windows/APIService.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 <aclapi.h>
40 #include "APIServer.hpp"
41 #include "stdio.h"
42 
43 #define SERVICENAME "RXAPI"
44 #define SERVICEDESCRIPTION "%s Service for Open Object Rexx version %d.%d.%d"
45 
46 #define SYNTAX_HELP "Syntax: rxapi opt [/s]\n\n" \
47  "Where opt is exactly one of:\n" \
48  "/i :\tinstall as Service.\n" \
49  "/u :\tuninstall as Service.\n" \
50  "/v :\tshow Version number\n\n" \
51  "The /s (silent) is optional and prevents any messages from displaying."
52 
53 #define APP_LOG_KEYNAME "SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\"
54 
57 
58 // Prototypes
59 void setServiceDACL(SC_HANDLE hService);
60 
61 // Globals for Service
62 static SERVICE_STATUS_HANDLE m_hServiceStatus;
63 static SERVICE_STATUS m_Status;
64 static SERVICE_DESCRIPTION Info;
65 
66 
67 /* - - - - Useful debugging help, if'd out under normal circumstances- - - - -*/
68 
69 #if 0
70 #include <shlobj.h>
71 #include <shlwapi.h>
72 
73 TCHAR fileBuf[MAX_PATH];
74 static const char *fileName = NULL;
75 
76 void __cdecl DebugMsg(const char* pszFormat, ...)
77 {
78  FILE *stream;
79  va_list arglist;
80  char buf[1024];
81  sprintf(buf, "[%s](%lu)(%lu){%d}: ", SERVICENAME, GetCurrentThreadId(),GetCurrentProcessId(),
82  m_Status.dwCurrentState);
83  va_start(arglist, pszFormat);
84  vsprintf(&buf[strlen(buf)], pszFormat, arglist);
85  va_end(arglist);
86  strcat(buf, "\n");
87 
88  if ( fileName == NULL )
89  {
90  SHGetFolderPath(NULL, CSIDL_COMMON_DOCUMENTS | CSIDL_FLAG_CREATE, NULL, 0, fileBuf);
91  PathAppend(fileBuf, "apiService.log");
92  fileName = fileBuf;
93  }
94 
95  stream = fopen(fileName, "a+");
96  if ( stream )
97  {
98  fwrite(buf, 1, strlen(buf), stream);
99  fclose(stream);
100  }
101 }
102 #endif
103 /* - - End debugging help - - - - - - - - - - - - - - - - - - - - - - - - - - */
104 
105 APIServer apiServer; // the real server instance
106 
107 // A couple of helper functions.
108 inline void showMessage(const char *msg, unsigned int icon)
109 {
110  MessageBox(NULL, msg, SERVICENAME, MB_OK | icon);
111 }
112 
113 #define WINDOWS_95_98_ME VER_PLATFORM_WIN32_WINDOWS
114 
116 {
117  OSVERSIONINFO version_info;
118  bool answer = false;
119 
120  version_info.dwOSVersionInfoSize = sizeof(version_info);
121  GetVersionEx(&version_info);
122 
123  switch ( type )
124  {
125  case Windows_NT :
126  answer = version_info.dwPlatformId != WINDOWS_95_98_ME;
127  break;
128 
129  case Windows_2K :
130  answer = version_info.dwMajorVersion >= 5;
131  break;
132 
133  default :
134  break;
135  }
136  return answer;
137 }
138 
139 /**
140  * Starts up the API server and has it listen for messages.
141  *
142  * Run() is either called from ServiceMain() in which case the API server will
143  * be running in a Service process. If rxapi is not installed as a service, or
144  * for some reason the service can not be started, then Run() is called from
145  * WinMain() and the API server runs in a standard process.
146  *
147  * @param asService If true, Run() was called from ServiceMain(), otherwise
148  * Run() was called from WinMain().
149  */
150 void Run (bool asService)
151 {
152  try
153  {
154  apiServer.initServer(); // start up the server
155  apiServer.listenForConnections(); // go into the message loop
156  }
157  catch (ServiceException *)
158  {
159  }
160  apiServer.terminateServer(); // shut everything down
161 }
162 
163 /**
164  * The following functions handle the Service control requests.
165  */
166 
167 // Called when the service is first initialized
168 bool OnInit()
169 {
170  return true;
171 }
172 
173 // Called when the service control manager wants to stop the service
174 void OnStop()
175 {
176  apiServer.terminateServer(); // terminate the server.
177 }
178 
179 // called when the service is interrogated
181 {
182 }
183 
184 // called when the service is paused
185 void OnPause()
186 {
187 }
188 
189 // called when the service is continued
191 {
192 }
193 
194 // called when the service is shut down
196 {
197 }
198 
199 // called when the service gets a user control message
200 BOOL OnUserControl(DWORD dwOpcode)
201 {
202  return FALSE; // say not handled
203 }
204 
205 /**
206  * Updates the Service Control Manager with our current state.
207  *
208  * @param dwState The current state.
209  */
210 void SetStatus(DWORD dwState)
211 {
212  m_Status.dwCurrentState = dwState;
213  SetServiceStatus(m_hServiceStatus, &m_Status);
214 }
215 
216 /**
217  * The handler function registered with the Service Control dispatcher. The
218  * dispatcher uses this function to send service control messages to the service
219  * process.
220  *
221  * @param dwOpcode The control message.
222  */
223 static void __cdecl Handler(DWORD dwOpcode)
224 {
225  switch (dwOpcode) {
226  case SERVICE_CONTROL_STOP: // 1
227  SetStatus(SERVICE_STOP_PENDING);
228  OnStop();
229  break;
230 
231  case SERVICE_CONTROL_PAUSE: // 2
232  OnPause();
233  break;
234 
235  case SERVICE_CONTROL_CONTINUE: // 3
236  OnContinue();
237  break;
238 
239  case SERVICE_CONTROL_INTERROGATE: // 4
240  OnInterrogate();
241  break;
242 
243  case SERVICE_CONTROL_SHUTDOWN: // 5
244  OnShutdown();
245  break;
246 
247  default:
248  break;
249  }
250 
251  // Report current status
252  SetServiceStatus(m_hServiceStatus, &m_Status);
253 }
254 
256 {
257  bool result;
258 
259  // Inform the Service Control Manager that we are about to start.
260  SetStatus(SERVICE_START_PENDING);
261 
262  // Perform the actual initialization
263  result = OnInit();
264 
265  // Inform the Service Control Manager of our final state.
266  m_Status.dwWin32ExitCode = GetLastError();
267  m_Status.dwCheckPoint = 0;
268  m_Status.dwWaitHint = 0;
269 
270  if ( ! result )
271  {
272  SetStatus(SERVICE_STOPPED);
273  return false;
274  }
275  SetStatus(SERVICE_RUNNING);
276  return true;
277 }
278 
279 /**
280  * After the service control dislpatcher receives a start request, (see
281  * startTheService(),) it creates a new thread and invokes this function on that
282  * thread to do the actual work of the service.
283  *
284  * @param dwArgc Count of args (not used by rxapi)
285  * @param lpszArgv Args (not used by rxapi)
286  */
287 static void WINAPI ServiceMain(DWORD dwArgc, LPTSTR* lpszArgv)
288 {
289  // Register the handler. The Service Control Manager will call back to this
290  // function to issue control requests.
291  m_Status.dwCurrentState = SERVICE_START_PENDING;
292  m_hServiceStatus = RegisterServiceCtrlHandler(SERVICENAME, (LPHANDLER_FUNCTION)Handler);
293  if ( m_hServiceStatus == (SERVICE_STATUS_HANDLE)NULL )
294  {
295  return;
296  }
297 
298  // Start the initialisation
299  if ( Initialize() )
300  {
301  // Do the actual work. When the Run function returns, the service has
302  // stopped.
303  m_Status.dwWin32ExitCode = 0;
304  m_Status.dwCheckPoint = 0;
305  m_Status.dwWaitHint = 0;
306 
307  Run(true);
308  }
309 
310  // Tell the service control manager we have stopped
311  SetStatus(SERVICE_STOPPED);
312 }
313 
314 /**
315  * Connects the main thread of this service process to the Service Control
316  * Manager. When this function succeeds, it will not return until the rxapi
317  * Service is stopped.
318  *
319  * @return A return of true indicates that rxapi was successfully started as a
320  * service process and has run to completion. A return of false
321  * indicates the rxapi service process was not started.
322  *
323  * @note When rxapi is installed as a service, it can not run as a service
324  * process unless it is started through the Service Control Manager. If
325  * rxapi is started through the command line or by CreateProcess(),
326  * StartServiceCtrlDispatcher() will return
327  * ERROR_FAILED_SERVICE_CONTROLLER_CONNECT and the service process will
328  * not have run.
329  */
330 bool startTheService(void)
331 {
332  SERVICE_TABLE_ENTRY st[] =
333  {
334  {
335  (LPTSTR)SERVICENAME,
337  },
338  {
339  NULL, NULL
340  }
341  };
342 
343  m_Status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
344  m_Status.dwCurrentState = SERVICE_STOPPED;
345  m_Status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
346  m_Status.dwWin32ExitCode = 0;
347  m_Status.dwServiceSpecificExitCode = 0;
348  m_Status.dwCheckPoint = 0;
349  m_Status.dwWaitHint = 0;
350 
351  return StartServiceCtrlDispatcher(st) != 0;
352 }
353 
354 /**
355  * If rxapi is currently running as a Service process, then stop it.
356  *
357  * @param hService Opened handle to the service. The handle must have been
358  * opened with SERVICE_QUERY_STATUS and SERVICE_STOP access
359  * rights.
360  *
361  * @param timeOut Time to wait, in miliseconds for a pending stop to clear.
362  *
363  * @return True if the service is stopped, false if not sure that the service
364  * is stopped.
365  */
366 bool stopTheService(SC_HANDLE hService, DWORD timeOut)
367 {
368  SERVICE_STATUS_PROCESS ssp;
369  DWORD startTicks = GetTickCount();
370  DWORD waitTime = timeOut / 20;
371  DWORD needed;
372  bool success = false;
373 
374  // See if the service is already stopped.
375  if ( ! QueryServiceStatusEx(hService, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp, sizeof(SERVICE_STATUS_PROCESS), &needed) )
376  {
377  goto finished;
378  }
379 
380  if ( ssp.dwCurrentState == SERVICE_STOPPED )
381  {
382  success = true;
383  goto finished;
384  }
385 
386  // When a service has a stop pending, we'll wait for it.
387  while ( ssp.dwCurrentState == SERVICE_STOP_PENDING )
388  {
389  Sleep(waitTime);
390  if ( ! QueryServiceStatusEx(hService, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp, sizeof(SERVICE_STATUS_PROCESS), &needed) )
391  {
392  goto finished;
393  }
394 
395  if ( ssp.dwCurrentState == SERVICE_STOPPED )
396  {
397  success = true;
398  goto finished;
399  }
400 
401  if ( GetTickCount() - startTicks > timeOut )
402  {
403  goto finished;
404  }
405  }
406 
407  // Send a stop code through the Sevice Control Manager to the service
408  SERVICE_STATUS ss;
409  if ( ! ControlService(hService, SERVICE_CONTROL_STOP, &ss) )
410  {
411  goto finished;
412  }
413 
414  // Wait for the service to stop
415  while ( ssp.dwCurrentState != SERVICE_STOPPED )
416  {
417  Sleep(waitTime);
418  if ( ! QueryServiceStatusEx(hService, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp, sizeof(SERVICE_STATUS_PROCESS), &needed) )
419  {
420  break;
421  }
422 
423  if ( ss.dwCurrentState == SERVICE_STOPPED )
424  {
425  success = true;
426  break;
427  }
428 
429  if ( GetTickCount() - startTicks > timeOut )
430  {
431  break;
432  }
433  }
434 
435 finished:
436  return success;
437 }
438 
439 void setServiceDACL(SC_HANDLE hService)
440 {
441  EXPLICIT_ACCESS ea;
442  SECURITY_DESCRIPTOR sd;
443  PSECURITY_DESCRIPTOR psd = NULL;
444  PACL pacl = NULL;
445  PACL pNewAcl = NULL;
446  BOOL bDaclPresent = FALSE;
447  BOOL bDaclDefaulted = FALSE;
448  DWORD dwError = 0;
449  DWORD needed = 0;
450 
451  // Get the current security descriptor for the service. First query with a
452  // size of 0 to get the needed size.
453  if ( ! QueryServiceObjectSecurity(hService, DACL_SECURITY_INFORMATION, &psd, 0, &needed) )
454  {
455  if ( GetLastError() == ERROR_INSUFFICIENT_BUFFER )
456  {
457  DWORD size = needed;
458  psd = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR, size);
459  if (psd == NULL)
460  {
461  goto cleanup;
462  }
463 
464  if ( ! QueryServiceObjectSecurity( hService, DACL_SECURITY_INFORMATION, psd, size, &needed) )
465  {
466  goto cleanup;
467  }
468  }
469  else
470  {
471  goto cleanup;
472  }
473  }
474 
475  // Get the current DACL from the security descriptor.
476  if ( ! GetSecurityDescriptorDacl(psd, &bDaclPresent, &pacl, &bDaclDefaulted) )
477  {
478  goto cleanup;
479  }
480 
481  // Build the ACE.
482  BuildExplicitAccessWithName(&ea, TEXT("Users"), SERVICE_START | SERVICE_STOP | GENERIC_READ, SET_ACCESS, NO_INHERITANCE);
483 
484  dwError = SetEntriesInAcl(1, (PEXPLICIT_ACCESS)&ea, pacl, &pNewAcl);
485  if ( dwError != ERROR_SUCCESS )
486  {
487  goto cleanup;
488  }
489 
490  // Initialize a new security descriptor.
491  if ( ! InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION) )
492  {
493  goto cleanup;
494  }
495 
496  // Set the new DACL in the security descriptor.
497  if ( ! SetSecurityDescriptorDacl(&sd, TRUE, pNewAcl, FALSE) )
498  {
499  goto cleanup;
500  }
501 
502  // Set the new security descriptor for the service object.
503  SetServiceObjectSecurity(hService, DACL_SECURITY_INFORMATION, &sd);
504 
505 cleanup:
506  if( pNewAcl != NULL )
507  {
508  LocalFree((HLOCAL)pNewAcl);
509  }
510  if( psd != NULL )
511  {
512  LocalFree((LPVOID)psd);
513  }
514 }
515 
516 /**
517  * Install rxapi as a Windows service and add the registry entries to allow
518  * logging through the Event Log service.
519  *
520  * @return True if the service was installed, otherwise false.
521  */
522 bool Install()
523 {
524  char szFilePath[_MAX_PATH];
525  SC_HANDLE hService = NULL;
526  char szKey[_MAX_PATH];
527  HKEY hKey = NULL;
528  DWORD dwData;
529  bool success = false;
530 
531  // Open the Service Control Manager
532  SC_HANDLE hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE | SC_MANAGER_CONNECT);
533  if ( hSCM == NULL )
534  {
535  goto cleanup;
536  }
537 
538  // Get the executable file path. The docs for Services say that if the path
539  // contains spaces it must be quoted.
540 
541  // Use szKey as a temp buffer.
542  GetModuleFileName(NULL, szKey, sizeof(szKey));
543  if ( strstr(szKey, " ") == 0 )
544  {
545  strcpy(szFilePath, szKey);
546  }
547  else
548  {
549  _snprintf(szFilePath, sizeof(szFilePath), "\"%s\"", szKey);
550  }
551 
552  // Create the service
553  hService = CreateService(hSCM, SERVICENAME, SERVICENAME, SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
554  SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, szFilePath, NULL, NULL, NULL, NULL,
555  NULL);
556 
557  if ( hService == NULL )
558  {
559  goto cleanup;
560  }
561 
562  // At this point, the service is installed, so success is set to true. The
563  // rest of the stuff is icing on the cake so to speak. If something fails,
564  // it might be nice to notify someone, but we shouldn't say the service
565  // install failed, because it hasn't.
566  success = true;
567 
568  // ChangeServiceConfig2() needs W2K or later.
570  {
571  // Use szKey as a temporary buffer
572  sprintf(szKey, SERVICEDESCRIPTION, SERVICENAME, ORX_VER, ORX_REL, ORX_MOD);
573  Info.lpDescription = szKey;
574 
575  ChangeServiceConfig2(hService, SERVICE_CONFIG_DESCRIPTION, &Info);
576  }
577 
578  // Set the discretionary ACL for the service.
579  setServiceDACL(hService);
580 
581  // Add the registry entries to support logging messages. The source name is
582  // added as a subkey under the Application key in the EventLog service
583  // portion of the registry.
584  strcpy(szKey, APP_LOG_KEYNAME);
585  strcat(szKey, SERVICENAME);
586 
587  if ( RegCreateKey(HKEY_LOCAL_MACHINE, szKey, &hKey) == ERROR_SUCCESS )
588  {
589  // Set the value of 'EventMessageFile' to the Event ID message-file name.
590  RegSetValueEx(hKey, "EventMessageFile", 0, REG_EXPAND_SZ, (CONST BYTE*)szFilePath,
591  (DWORD)strlen(szFilePath) + 1);
592 
593  // Set the value of 'TypesSupported' to the supported types.
594  dwData = EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE;
595  RegSetValueEx(hKey, "TypesSupported", 0, REG_DWORD, (CONST BYTE*)&dwData, sizeof(DWORD));
596 
597  RegCloseKey(hKey);
598  }
599 
600 cleanup:
601  if ( hService != NULL )
602  {
603  CloseServiceHandle(hService);
604  }
605  if ( hSCM != NULL )
606  {
607  CloseServiceHandle(hSCM);
608  }
609  return success;
610 }
611 
612 /**
613  * Deletes rxapi as a service and cleans up the registry entries that were made
614  * when rxapi was installed as a service.
615  *
616  * @return true on success, othewise false.
617  */
618 bool Uninstall()
619 {
620  bool success = false;
621 
622  // Open the Service Control Manager
623  SC_HANDLE hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
624  if ( hSCM != NULL )
625  {
626  SC_HANDLE hService = OpenService(hSCM, SERVICENAME, DELETE | SERVICE_QUERY_STATUS | SERVICE_STOP);
627  if ( hService != NULL )
628  {
629  // First stop the service if it is running, which allows the Service
630  // Control Manger to clean up the database immediately. If we fail
631  // to stop the service we just ignore it. The database will be
632  // cleaned up at the next reboot. (Or sooner if the rxapi process is
633  // killed.)
634  stopTheService(hService, 1000);
635 
636  if ( DeleteService(hService) )
637  {
638  success = true;
639  }
640  CloseServiceHandle(hService);
641  }
642  CloseServiceHandle(hSCM);
643  }
644 
645  // Remove the Event Log service entries in the registry for rxapi. These
646  // entries are added during the Service installation function. Any errors
647  // are just ignored.
648  if ( success )
649  {
650  HKEY hKey = NULL;
651  if ( RegOpenKeyEx(HKEY_LOCAL_MACHINE, APP_LOG_KEYNAME, 0, DELETE, &hKey) == ERROR_SUCCESS )
652  {
653  if ( RegDeleteKey(hKey, SERVICENAME) == ERROR_SUCCESS )
654  {
655  RegCloseKey(hKey);
656  }
657  }
658  }
659  return success;
660 }
661 
662 /**
663  * Determines if the service is currently running.
664  *
665  * If the current state is START PENDING will wait until the service is running
666  * or times out.
667  *
668  * @param hService Handle to the opened service. Note that the handle has to
669  * have been opened with SERVICE_QUERY_STATUS so that we can
670  * use QueryServiceStatusEx().
671  *
672  * @return True if the service is running, false otherwise.
673  */
674 bool serviceIsRunning(SC_HANDLE hService)
675 {
676  SERVICE_STATUS_PROCESS ssp;
677  DWORD needed;
678 
679  BOOL success = QueryServiceStatusEx(hService, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
680  sizeof(SERVICE_STATUS_PROCESS), &needed);
681 
682  if ( success )
683  {
684  if ( ssp.dwCurrentState == SERVICE_RUNNING || ssp.dwCurrentState == SERVICE_STOPPED ||
685  ssp.dwCurrentState == SERVICE_STOP_PENDING )
686  {
687  return ssp.dwCurrentState == SERVICE_RUNNING;
688  }
689 
690  if ( ssp.dwCurrentState == SERVICE_START_PENDING )
691  {
692  // Save the tick count and initial checkpoint.
693  uint32_t startTicks = GetTickCount();
694  uint32_t oldCheck = ssp.dwCheckPoint;
695  uint32_t waitTime;
696 
697  // Check the status until the service is no longer start pending.
698  // rxapi is not pausable, so PAUSED or PAUSED_PENDING should not be
699  // possible.
700  while ( ssp.dwCurrentState == SERVICE_START_PENDING )
701  {
702  // Do not wait longer than the wait hint, which for rxapi will
703  // be 2000 milliseconds.
704  //
705  // Microsoft suggests that a good interval is one tenth the wait
706  // hint, but not less than 1 second and no more than 10 seconds.
707  // rxapi usually starts in less than 200 milliseconds.
708  waitTime = ssp.dwWaitHint / 10;
709 
710  if( waitTime < 200 )
711  {
712  waitTime = 200;
713  }
714  else if ( waitTime > 10000 )
715  {
716  waitTime = 10000;
717  }
718 
719  Sleep(waitTime);
720 
721  BOOL success = QueryServiceStatusEx(hService, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
722  sizeof(SERVICE_STATUS_PROCESS), &needed);
723  if ( ! success || ssp.dwCurrentState == SERVICE_RUNNING )
724  {
725  break;
726  }
727 
728  if ( ssp.dwCheckPoint > oldCheck )
729  {
730  // The service is making progress, so continue.
731  startTicks = GetTickCount();
732  oldCheck = ssp.dwCheckPoint;
733  }
734  else
735  {
736  if( (GetTickCount() - startTicks) > ssp.dwWaitHint )
737  {
738  // The wait hint interval has expired and we are still
739  // not started, so quit.
740  break;
741  }
742  }
743  }
744 
745  return ssp.dwCurrentState == SERVICE_RUNNING;
746  }
747  }
748  return false;
749 }
750 
751 /**
752  * Determines if: rxapi is installed as a service and is already running, is
753  * installed as a service but not running, installed but the service is
754  * currently disabled, or not installed as a service.
755  *
756  * @return One of the 4 service state enums.
757  */
759 {
761 
762  // Open the Service Control Manager
763  SC_HANDLE hSCM = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_CONNECT);
764  if ( hSCM )
765  {
766  // Try to open the service, if this fails, rxapi is not installed as a
767  // service.
768  SC_HANDLE hService = OpenService(hSCM, SERVICENAME, SERVICE_QUERY_CONFIG | SERVICE_QUERY_STATUS);
769  if ( hService )
770  {
771  // The service is definitely installed, we'll mark it as disabled
772  // until we know for sure it is not disabled.
773  state = disabled_state;
774 
775  LPQUERY_SERVICE_CONFIG pqsc = (LPQUERY_SERVICE_CONFIG)LocalAlloc(LPTR, 4096);
776  if ( pqsc )
777  {
778  DWORD needed;
779  if ( QueryServiceConfig(hService, pqsc, 4096, &needed) )
780  {
781  if ( pqsc->dwStartType != SERVICE_DISABLED )
782  {
783  state = installed_state;
784  }
785  }
786  LocalFree(pqsc);
787  }
788 
789  if ( state == installed_state )
790  {
791  // See if rxapi is already running as a service.
792  if ( serviceIsRunning(hService) )
793  {
794  state = running_state;
795  }
796  }
797  CloseServiceHandle(hService);
798  }
799  CloseServiceHandle(hSCM);
800  }
801  return state;
802 }
803 
804 /**
805  * Start rxapi as a Windows Service, if possible. This function determines if
806  * rxapi is installed as a Service and if it is already running as a service.
807  *
808  * If it is installled as a service, but not running, an attempt is made to
809  * start it as a Service.
810  *
811  * @return True if now running as a Windows Service, otherwis false.
812  */
814 {
815  bool started;
816  DWORD errRC;
817  int iRet;
818 
820  if ( state == running_state )
821  {
822  return true;
823  }
824 
825  if ( state == installed_state )
826  {
827  // When the service is started successfully here, we do not return until
828  // the service has been stopped.
829  started = startTheService();
830 
831  if ( ! started )
832  {
833  // The service did not start, get the error code.
834  errRC = GetLastError();
835  if ( errRC == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT )
836  {
837  // This specific error happens when rxapi is not running and is
838  // started from the command line or by CreateProcess(). Service
839  // processes can not run when started as console programs.
840  // (Really we should educate the user not to do this.) We use
841  // NET START RXAPI, which will invoke the service controller to
842  // start rxapi correctly. When we return from system(), rxapi
843  // will be running as a service in another process, so it is
844  // important to exit this process.
845 
846  iRet = system("NET start rxapi");
847 
848  // If the return is not 0, rxapi was not started as a service.
849  return iRet == 0;
850  }
851  else
852  {
853  // The service could not be started for some un-anticipated
854  // reason. Return false so that rxapi will continue by running
855  // as a standard process.
856  return false;
857  }
858  }
859 
860  // Do not continue by running rxapi as a standard process.
861  return true;
862  }
863 
864  // Either rxapi is not installed as a Service, or the Service has been set
865  // to disabled. Continue by running rxapi as a standard process.
866  return false;
867 }
868 
869 /**
870  * We have command line args, process them. Most likely the args are to install
871  * or uninstall rxapi as a service.
872  *
873  * @param cmdLine The command line, must not be null.
874  */
875 void processCmdLine(const char *cmdLine)
876 {
877  char msg[256];
878  bool verbose = true;
879 
880  size_t len = strlen(cmdLine);
881 
882  if ( cmdLine[0] != '/' || len < 2 )
883  {
884  showMessage(SYNTAX_HELP, MB_ICONERROR);
885  return;
886  }
887 
888  if ( len >= 5 )
889  {
890  char opt = cmdLine[4];
891  if ( opt == 's' || opt == 'S' || opt == 'q' || opt == 'Q' )
892  {
893  verbose = false;
894  }
895  }
896 
897  bool isInstalled = (getServiceState() != uninstalled_state);
898 
899  switch ( cmdLine[1] )
900  {
901  case 'v' :
902  case 'V' :
903  sprintf(msg ,"%s Version %d.%d.%d\n\nThe service is %s installed", SERVICENAME,
904  ORX_VER, ORX_REL, ORX_MOD, isInstalled ? "currently" : "not");
905  showMessage(msg, MB_ICONINFORMATION);
906  break;
907 
908  case 'i' :
909  case 'I' :
910  if ( isInstalled )
911  {
912  if ( verbose )
913  {
914  sprintf(msg, "%s is already installed as a service.", SERVICENAME);
915  showMessage(msg, MB_ICONWARNING);
916  }
917  }
918  else
919  {
920  if ( Install() )
921  {
922  if ( verbose )
923  {
924  sprintf(msg, "The %s service was installed\n", SERVICENAME);
925  showMessage(msg, MB_ICONINFORMATION);
926  }
927  }
928  else
929  {
930  if ( verbose )
931  {
932  sprintf(msg, "The %s service failed to install.\n"
933  "Error %d\n", SERVICENAME, GetLastError());
934  showMessage(msg, MB_ICONERROR);
935  }
936  }
937  }
938  break;
939 
940  case 'u' :
941  case 'U' :
942  if ( ! isInstalled )
943  {
944  if ( verbose )
945  {
946  sprintf(msg, "%s is not installed as a service\n", SERVICENAME);
947  showMessage(msg, MB_ICONWARNING);
948  }
949  }
950  else
951  {
952  if ( Uninstall() )
953  {
954  if ( verbose )
955  {
956  sprintf(msg, "%s was uninstalled as a service.\n\n"
957  "(Open Object REXX is not uninstalled.)", SERVICENAME);
958  showMessage(msg, MB_ICONINFORMATION);
959  }
960  }
961  else
962  {
963  if ( verbose )
964  {
965  sprintf(msg, "The %s service could not be uninstalled.\n"
966  "Error %d\n", SERVICENAME, GetLastError());
967  showMessage(msg, MB_ICONERROR);
968  }
969  }
970  }
971  break;
972 
973  default :
974  showMessage(SYNTAX_HELP, MB_ICONERROR);
975  break;
976  }
977  return;
978 }
979 
980 /**
981  * The main entry point for rxapi.exe.
982  *
983  * When rxapi is installed as a service, the invoker might be the service
984  * control manager, or the interpreter could be starting rxapi as a service
985  * because rxapi was not running.
986  *
987  * When rxapi is not installed as a service, then the invoker is most likely the
988  * interpreter starting up rxapi. Although, the user could also be invoking
989  * rxapi from the command line.
990  *
991  * The third possibility is that rxapi is being invoked from the command line
992  * with arguments to either install, uninstall, or query rxapi as a service.
993  *
994  *
995  * @param hInstance
996  * @param hPrevInstance
997  * @param lpCmdLine The only arg we are interested in, the command line
998  * arguments.
999  * @param nCmdShow
1000  *
1001  * @return 0
1002  */
1003 int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
1004 {
1006  {
1007  // If there are args, we always process them and exit.
1008  if ( strlen(lpCmdLine) != 0 )
1009  {
1010  processCmdLine(lpCmdLine);
1011  exit(0);
1012  }
1013 
1014  // If rxapi is already running as a service then startAsWindowsService()
1015  // will return immediately and we will exit. Otherwise, the function
1016  // will attempt to strart rxapi as a service. If successful, we do not
1017  // return from startAsWindowService() until the service is stopped.
1018  if ( startAsWindowsService() )
1019  {
1020  // rxapi was already running as a service, or rxapi was started and
1021  // ran as a service, and is now ended / stopped.
1022  exit(0);
1023  }
1024  }
1025 
1026  // For some reason we are not running as an installed service, (either not
1027  // installed as service, the user does not have the authority to start a
1028  // stopped service, or some other reason.) In this case, run rxapi as a
1029  // normal process.
1030  Run(false);
1031 
1032  return 0;
1033 }
void initServer()
Definition: APIServer.cpp:53
void terminateServer()
Definition: APIServer.cpp:69
void listenForConnections()
Definition: APIServer.cpp:82
int type
Definition: cmdparse.cpp:1888
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
static SERVICE_STATUS m_Status
bool startAsWindowsService(void)
BOOL OnUserControl(DWORD dwOpcode)
void OnShutdown()
bool Initialize()
#define SERVICEDESCRIPTION
void setServiceDACL(SC_HANDLE hService)
static SERVICE_STATUS_HANDLE m_hServiceStatus
bool startTheService(void)
ServiceStateType getServiceState(void)
void OnContinue()
void SetStatus(DWORD dwState)
#define SERVICENAME
ServiceStateType
@ uninstalled_state
@ running_state
@ installed_state
@ disabled_state
#define APP_LOG_KEYNAME
void Run(bool asService)
void OnPause()
bool Uninstall()
bool serviceIsRunning(SC_HANDLE hService)
static void WINAPI ServiceMain(DWORD dwArgc, LPTSTR *lpszArgv)
bool OnInit()
void OnInterrogate()
bool stopTheService(SC_HANDLE hService, DWORD timeOut)
static SERVICE_DESCRIPTION Info
WindowsVersionType
@ Windows_2K
@ Windows_NT
void processCmdLine(const char *cmdLine)
#define WINDOWS_95_98_ME
static void __cdecl Handler(DWORD dwOpcode)
#define SYNTAX_HELP
void OnStop()
bool Install()
void showMessage(const char *msg, unsigned int icon)
bool isAtLeastVersion(WindowsVersionType type)
APIServer apiServer
unsigned int uint32_t