windows/SysLocalAPIManager.cpp
Go to the documentation of this file.
1 /*----------------------------------------------------------------------------*/
2 /* */
3 /* Copyright (c) 1995, 2004 IBM Corporation. All rights reserved. */
4 /* Copyright (c) 2005-2009 Rexx Language Association. All rights reserved. */
5 /* */
6 /* This program and the accompanying materials are made available under */
7 /* the terms of the Common Public License v1.0 which accompanies this */
8 /* distribution. A copy is also available at the following address: */
9 /* http://www.ibm.com/developerworks/oss/CPLv1.0.htm */
10 /* */
11 /* Redistribution and use in source and binary forms, with or */
12 /* without modification, are permitted provided that the following */
13 /* conditions are met: */
14 /* */
15 /* Redistributions of source code must retain the above copyright */
16 /* notice, this list of conditions and the following disclaimer. */
17 /* Redistributions in binary form must reproduce the above copyright */
18 /* notice, this list of conditions and the following disclaimer in */
19 /* the documentation and/or other materials provided with the distribution. */
20 /* */
21 /* Neither the name of Rexx Language Association nor the names */
22 /* of its contributors may be used to endorse or promote products */
23 /* derived from this software without specific prior written permission. */
24 /* */
25 /* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS */
26 /* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT */
27 /* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS */
28 /* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT */
29 /* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, */
30 /* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED */
31 /* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, */
32 /* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY */
33 /* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING */
34 /* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS */
35 /* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
36 /* */
37 /*----------------------------------------------------------------------------*/
38 
39 #include "SysLocalAPIManager.hpp"
40 #include "LocalAPIManager.hpp"
41 #include <stdio.h>
42 
43 
44 /* - - - - Temporary stuff for debugging help, will be removed - - - - - - - */
45 #if 0
46 #include <shlobj.h>
47 #include <shlwapi.h>
48 TCHAR fileBuf[MAX_PATH];
49 static const char *fileName = NULL;
50 
51 void __cdecl DebugMsg(const char* pszFormat, ...)
52 {
53  FILE *stream;
54  va_list arglist;
55  char buf[1024];
56  sprintf(buf, "[%s](%lu): ", SERVICENAME, GetCurrentThreadId());
57  va_start(arglist, pszFormat);
58  vsprintf(&buf[strlen(buf)], pszFormat, arglist);
59  va_end(arglist);
60  strcat(buf, "\n");
61 
62  if ( fileName == NULL )
63  {
64  SHGetFolderPath(NULL, CSIDL_COMMON_DOCUMENTS | CSIDL_FLAG_CREATE, NULL, 0, fileBuf);
65  PathAppend(fileBuf, "clientApi.log");
66  fileName = fileBuf;
67  }
68 
69  stream = fopen(fileName, "a+");
70  if ( stream )
71  {
72  fwrite(buf, 1, strlen(buf), stream);
73  fclose(stream);
74  }
75 }
76 #endif
77 /* - - End Temporary stuff for debugging help - - - - - - - - - - - - - - - - */
78 
79 #define SERVICENAME "RXAPI"
80 
81 // Windows Service status codes
82 typedef enum
83 {
91 
92 
93 /**
94  * Reports if the rxapi service is in the running state at the time this
95  * function returns. If the service is in the START PENDING state the function
96  * waits untils the service is running or has timed out.
97  *
98  * @param hService Opened service handle, must have the SERVICE_QUERY_STATUS
99  * privilege.
100  *
101  * @return True the service is not running, otherwise false.
102  */
103 static bool hasServiceStarted(SC_HANDLE hService)
104 {
105  SERVICE_STATUS_PROCESS ssp;
106  DWORD needed;
107 
108  if ( QueryServiceStatusEx(hService, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp, sizeof(SERVICE_STATUS_PROCESS), &needed) == 0 )
109  {
110  return false;
111  }
112 
113  if ( ssp.dwCurrentState == SERVICE_RUNNING || ssp.dwCurrentState == SERVICE_STOPPED ||
114  ssp.dwCurrentState == SERVICE_STOP_PENDING )
115  {
116  return ssp.dwCurrentState == SERVICE_RUNNING;
117  }
118 
119  // Save the tick count and initial checkpoint.
120  uint32_t startTicks = GetTickCount();
121  uint32_t oldCheck = ssp.dwCheckPoint;
122  uint32_t waitTime;
123 
124  // Check the status until the service is no longer start pending. rxapi is
125  // not pausable, so PAUSED or PAUSED_PENDING should not be possible.
126  while ( ssp.dwCurrentState == SERVICE_START_PENDING )
127  {
128  // Do not wait longer than the wait hint, which for rxapi will be 2000
129  // milliseconds.
130  //
131  // Microsoft suggests that a good interval is one tenth the wait hint,
132  // but not less than 1 second and no more than 10 seconds. rxapi usually
133  // starts in less than 200 milliseconds.
134  waitTime = ssp.dwWaitHint / 10;
135 
136  if( waitTime < 200 )
137  {
138  waitTime = 200;
139  }
140  else if ( waitTime > 10000 )
141  {
142  waitTime = 10000;
143  }
144 
145  Sleep(waitTime);
146 
147  BOOL success = QueryServiceStatusEx(hService, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp, sizeof(SERVICE_STATUS_PROCESS), &needed);
148  if ( ! success || ssp.dwCurrentState == SERVICE_RUNNING )
149  {
150  break;
151  }
152 
153  if ( ssp.dwCheckPoint > oldCheck )
154  {
155  // The service is making progress, so continue.
156  startTicks = GetTickCount();
157  oldCheck = ssp.dwCheckPoint;
158  }
159  else
160  {
161  if( (GetTickCount() - startTicks) > ssp.dwWaitHint )
162  {
163  // The wait hint interval has expired and we are still not
164  // started, so quit.
165  break;
166  }
167  }
168  }
169 
170  return ssp.dwCurrentState == SERVICE_RUNNING ? true : false;
171 }
172 
173 
174 /**
175  * Get the status of the rxapi service, which could be not installed as a
176  * service.
177  *
178  * @param phSCM Pointer to a handle for the Service Control Manager. If rxapi
179  * is installed as a service, and not disabled, an open handle is
180  * returned here. Otherwise the handle is set to NULL.
181  *
182  * @return A WinServiceStatusT enum indicating the status of rxapi as a Windows
183  * service.
184  */
185 static WinServiceStatusT getServiceStatus(SC_HANDLE *phSCM)
186 {
187  WinServiceStatusT status = WS_UNKNOWN;
188  SC_HANDLE hService = NULL;
189 
190  // Open the Service Control Manager
191  SC_HANDLE hSCM = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_CONNECT);
192  if ( hSCM )
193  {
194  // Open the service with the query access rights.
195  hService = OpenService(hSCM, SERVICENAME, SERVICE_QUERY_CONFIG | SERVICE_QUERY_STATUS);
196  if ( hService )
197  {
198  // The service is installed, make sure it is not currently disabled.
199  LPQUERY_SERVICE_CONFIG serviceCfg = (LPQUERY_SERVICE_CONFIG)LocalAlloc(LPTR, 4096);
200  if ( serviceCfg )
201  {
202  DWORD needed;
203 
204  if ( QueryServiceConfig(hService, serviceCfg, 4096, &needed) )
205  {
206  if ( serviceCfg->dwStartType == SERVICE_DISABLED )
207  {
208  status = WS_IS_DISABLED;
209  }
210  }
211  LocalFree(serviceCfg);
212  }
213  else
214  {
215  status = WS_NOT_PRIVILEGED;
216  }
217 
218  if ( status != WS_UNKNOWN )
219  {
220  CloseServiceHandle(hService);
221  hService = NULL;
222  }
223  }
224  }
225  else
226  {
227  status = WS_NOT_PRIVILEGED;
228  }
229 
230  if ( status != WS_UNKNOWN )
231  {
232  if ( hSCM != NULL )
233  {
234  CloseServiceHandle(hSCM);
235  }
236  return status;
237  }
238 
239  *phSCM = hSCM;
240 
241  if ( hasServiceStarted(hService) )
242  {
243  status = WS_IS_RUNNING;
244  }
245  else
246  {
247  status = WS_IS_STOPPED;
248  }
249 
250  CloseServiceHandle(hService);
251  return status;
252 }
253 
254 /**
255  * Determines if rxapi is installed and running as a service.
256  *
257  * If rxapi is installed as a service, but not running an attempt is made to
258  * start the service. When the service is not disabled, and the user (owner of
259  * this running process) has the authority to start the rxapi service, then the
260  * attempt should succeed.
261  *
262  * If the function attempts to start the service, it will wait to see that the
263  * service actually starts. If the above conditions are met, then the service
264  * will start.
265  *
266  * @return True if the service is started and running, otherwise false.
267  *
268  * @note Both the service control manager and the service itself are opened
269  * with the minimum access rights needed to accomplish the task. On
270  * Vista this is especially important because it prevents unnecessary
271  * failures.
272  */
273 bool startAsService(void)
274 {
275  SC_HANDLE hSCM = NULL;
276 
277  // First see if rxapi is already running as a service, or if not running, if
278  // there is at least a possibility of starting it.
279  WinServiceStatusT status = getServiceStatus(&hSCM);
280  if ( status == WS_NOT_INSTALLED || status == WS_IS_DISABLED || status == WS_NOT_PRIVILEGED )
281  {
282  return false;
283  }
284  else if ( status == WS_IS_RUNNING )
285  {
286  CloseServiceHandle(hSCM);
287  return true;
288  }
289 
290  // Okay, the service is not running, but it is installed and should be
291  // startable.
292  SC_HANDLE hService = OpenService(hSCM, SERVICENAME, SERVICE_QUERY_CONFIG | SERVICE_START | SERVICE_QUERY_STATUS);
293  if ( hService == NULL )
294  {
295  // Seems the user has sufficient privileges to query the service, but
296  // not to start it.
297  if ( hSCM != NULL )
298  {
299  CloseServiceHandle(hSCM);
300  }
301  return false;
302  }
303 
304  bool hasStarted = false;
305 
306  if ( ! StartService(hService, 0, NULL) )
307  {
308  goto done_out;
309  }
310 
311  hasStarted = hasServiceStarted(hService);
312 
313 done_out:
314  CloseServiceHandle(hSCM);
315  CloseServiceHandle(hService);
316  return hasStarted;
317 }
318 
320 {
321  // First try to start rxapi as a Windows service. If that fails, then start
322  // it as a normal process.
323  if ( startAsService() )
324  {
325  return;
326  }
327 
328  char apiExeName[] = "RXAPI.EXE";
329  LPCTSTR lpszImageName = NULL; // address of module name
330  LPTSTR lpszCommandLine = NULL; // address of command line
331  LPSECURITY_ATTRIBUTES
332  lpsaProcess = NULL; // address of process security attrs
333  LPSECURITY_ATTRIBUTES
334  lpsaThread = NULL; // address of thread security attrs */
335  /* don't inherit handles, because otherwise files are inherited
336  and inaccessible until RXAPI.EXE is stopped again */
337  BOOL fInheritHandles = FALSE; // new process doesn't inherit handles
338  DWORD fdwCreate = DETACHED_PROCESS; // creation flags
339  LPVOID lpvEnvironment = NULL; // address of new environment block
340  char szSysDir[256]; // retrieve system directory
341  LPCTSTR lpszCurDir = szSysDir; // address of command line
342  LPSTARTUPINFO lpsiStartInfo; // address of STARTUPINFO
343  STARTUPINFO siStartInfo;
344  LPPROCESS_INFORMATION lppiProcInfo; // address of PROCESS_INFORMATION
345  PROCESS_INFORMATION MemMgrProcessInfo;
346  lppiProcInfo = &MemMgrProcessInfo;
347 
348  siStartInfo.cb = sizeof(siStartInfo);
349  siStartInfo.lpReserved = NULL;
350  siStartInfo.lpDesktop = NULL;
351  siStartInfo.lpTitle = NULL;
352  siStartInfo.dwX = 0;
353  siStartInfo.dwY = 0;
354  siStartInfo.dwXSize = 0;
355  siStartInfo.dwYSize = 0;
356  siStartInfo.dwXCountChars = 0;
357  siStartInfo.dwYCountChars = 0;
358  siStartInfo.dwFillAttribute = 0;
359  siStartInfo.dwFlags = 0;
360  siStartInfo.wShowWindow = 0;
361  siStartInfo.cbReserved2 = 0;
362  siStartInfo.lpReserved2 = NULL;
363  siStartInfo.hStdInput = NULL;
364  siStartInfo.hStdOutput = NULL;
365  siStartInfo.hStdError = NULL;
366  lpsiStartInfo = &siStartInfo;
367  lpszCommandLine = apiExeName;
368 
369  /* start RXAPI process out of system directory */
370  if (!GetSystemDirectory(szSysDir, 255))
371  {
372  lpszCurDir = NULL;
373  }
374 
375  if(!CreateProcess(lpszImageName, lpszCommandLine, lpsaProcess,
376  lpsaThread, fInheritHandles, fdwCreate, lpvEnvironment,
377  lpszCurDir, lpsiStartInfo, lppiProcInfo))
378  {
379  throw new ServiceException(API_FAILURE, "Unable to start API server");
380  }
381 }
382 
383 
384 /**
385  * Check to see if we've inherited a session queue from a calling process. This shows
386  * up as an environment variable value.
387  *
388  * @param sessionQueue
389  * The returned session queue handle, if it exists.
390  *
391  * @return true if the session queue is inherited, false if a new once needs to be created.
392  */
394 {
395  // check to see if we have an env variable set...if we do we
396  // inherit from our parent session
397  char envbuffer[MAX_QUEUE_NAME_LENGTH+1];
398  DWORD envchars = GetEnvironmentVariable("RXQUEUESESSION", (LPTSTR) envbuffer, MAX_QUEUE_NAME_LENGTH);
399  if (envchars != 0)
400  {
401  sscanf(envbuffer, "%p", &sessionQueue);
402  return true;
403  }
404  return false;
405 }
406 
407 /**
408  * Set the active session queue as an environment variable.
409  *
410  * @param sessionQueue
411  * The session queue handle.
412  */
414 {
415  char envbuffer[MAX_QUEUE_NAME_LENGTH+1];
416  // and set this as an environment variable for programs we call
417  sprintf(envbuffer, "%p", sessionQueue);
418  SetEnvironmentVariable("RXQUEUESESSION", (LPTSTR) envbuffer);
419 }
uintptr_t QueueHandle
@ API_FAILURE
static void setActiveSessionQueue(QueueHandle sessionQueue)
static bool getActiveSessionQueue(QueueHandle &sessionQueue)
#define MAX_QUEUE_NAME_LENGTH
Definition: rexx.h:957
bool startAsService(void)
#define SERVICENAME
static WinServiceStatusT getServiceStatus(SC_HANDLE *phSCM)
static bool hasServiceStarted(SC_HANDLE hService)
unsigned int uint32_t