net-snmp 5.7
winservice.c
00001 /*
00002  * Windows Service related function definitions
00003  * By Raju Krishnappa(raju_krishnappa@yahoo.com)
00004  *
00005  */
00006 
00007 #ifdef WIN32
00008 
00009 #include <net-snmp/net-snmp-config.h>
00010 
00011 #include <windows.h>
00012 #include <tchar.h>
00013 
00014 #include <stdio.h>              /* sprintf */
00015 #include <process.h>            /* beginthreadex  */
00016 
00017 #include <net-snmp/library/winservice.h>
00018 
00019 #ifdef mingw32 /* MinGW doesn't fully support exception handling. */
00020 
00021 #define TRY if(1)
00022 #define LEAVE goto labelFIN
00023 #define FINALLY do { \
00024 labelFIN: \
00025         ; \
00026 } while(0); if(1)
00027 
00028 #else
00029 
00030 #define TRY __try
00031 #define LEAVE __leave
00032 #define FINALLY __finally
00033 
00034 #endif /* mingw32 */
00035 
00036 
00037 #define CountOf(arr) ( sizeof(arr) / sizeof(arr[0]) )
00038 
00039 
00040 #if defined(WIN32) && defined(HAVE_WIN32_PLATFORM_SDK) && !defined(mingw32)
00041 #pragma comment(lib, "iphlpapi.lib")
00042 #ifdef USING_WINEXTDLL_MODULE
00043 #pragma comment(lib, "snmpapi.lib")
00044 #pragma comment(lib, "mgmtapi.lib")
00045 #endif
00046 #endif
00047 
00048  
00049     /*
00050      * External global variables used here
00051      */
00052 
00053     /*
00054      * Application Name 
00055      * This should be declared by the application, which wants to register as
00056      * windows service
00057      */
00058 extern LPTSTR app_name_long;
00059 
00060     /*
00061      * Declare global variable
00062      */
00063 
00064     /*
00065      * Flag to indicate whether process is running as Service 
00066      */
00067 BOOL g_fRunningAsService = FALSE;
00068 
00069     /*
00070      * Variable to maintain Current Service status 
00071      */
00072 static SERVICE_STATUS ServiceStatus;
00073 
00074     /*
00075      * Service Handle 
00076      */
00077 static SERVICE_STATUS_HANDLE hServiceStatus = 0L;
00078 
00079     /*
00080      * Service Table Entry 
00081      */
00082 SERVICE_TABLE_ENTRY ServiceTableEntry[] = {
00083   {NULL, ServiceMain},          /* Service Main function */
00084   {NULL, NULL}
00085 };
00086 
00087     /*
00088      * Handle to Thread, to implement Pause, Resume and Stop functions
00089      */
00090 static HANDLE hServiceThread = NULL;    /* Thread Handle */
00091 
00092     /*
00093      * Holds calling partys Function Entry point, that should start
00094      * when entering service mode
00095      */
00096 static INT (*ServiceEntryPoint) (INT Argc, LPTSTR Argv[]) = 0L;
00097 
00098     /*
00099      * To hold Stop Function address, to be called when STOP request
00100      * received from the SCM
00101      */
00102 static VOID (*StopFunction) (VOID) = 0L;
00103 
00104 
00105     /*
00106      * To update windows service status to SCM 
00107      */
00108 static BOOL UpdateServiceStatus (DWORD dwStatus, DWORD dwErrorCode,
00109                                  DWORD dwWaitHint);
00110 
00111     /*
00112      * To Report current service status to SCM 
00113      */
00114 static BOOL ReportCurrentServiceStatus (VOID);
00115 
00116 VOID
00117 ProcessError (WORD eventLogType, LPCTSTR pszMessage, int useGetLastError, int quiet);
00118 
00119     /*
00120      * To register as Windows Service with SCM(Service Control Manager)
00121      * Input - Service Name, Service Display Name,Service Description and
00122      * Service startup arguments
00123      */
00124 int
00125 RegisterService (LPCTSTR lpszServiceName, LPCTSTR lpszServiceDisplayName,
00126                  LPCTSTR lpszServiceDescription,
00127                  InputParams * StartUpArg, int quiet) /* Startup argument to the service */
00128 {
00129   TCHAR szServicePath[MAX_PATH];        /* To hold module File name */
00130   TCHAR MsgErrorString[MAX_STR_SIZE];   /* Message or Error string */
00131   TCHAR szServiceCommand[MAX_PATH + 9]; /* Command to execute */
00132   SC_HANDLE hSCManager = NULL;
00133   SC_HANDLE hService = NULL;
00134   TCHAR szRegAppLogKey[] =
00135     _T("SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\");
00136   TCHAR szRegKey[512];
00137   HKEY hKey = NULL;             /* Key to registry entry */
00138   HKEY hParamKey = NULL;        /* To store startup parameters */
00139   DWORD dwData;                 /* Type of logging supported */
00140   DWORD i, j;                   /* Loop variables */
00141   int exitStatus = 0;
00142   GetModuleFileName (NULL, szServicePath, MAX_PATH);
00143   TRY
00144   {
00145 
00146     /*
00147      * Open Service Control Manager handle 
00148      */
00149     hSCManager = OpenSCManager (NULL, NULL, SC_MANAGER_CREATE_SERVICE);
00150     if (hSCManager == NULL)
00151       {
00152         ProcessError (EVENTLOG_ERROR_TYPE, _T ("Can't open SCM (Service Control Manager)"), 1, quiet);
00153         exitStatus = SERVICE_ERROR_SCM_OPEN;
00154         LEAVE;
00155       }
00156 
00157     /*
00158      * Generate the command to be executed by the SCM 
00159      */
00160     _sntprintf (szServiceCommand, CountOf(szServiceCommand), _T("%s %s"), szServicePath, _T ("-service"));
00161 
00162     /*
00163      * Create the desired service 
00164      */
00165     hService = CreateService (hSCManager, lpszServiceName, lpszServiceDisplayName,
00166                         SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
00167                         SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, szServiceCommand,
00168                               NULL,     /* load-order group */
00169                               NULL,     /* group member tag */
00170                               NULL,     /* dependencies */
00171                               NULL,     /* account */
00172                               NULL);    /* password */
00173     if (hService == NULL)
00174       {
00175         _sntprintf (MsgErrorString, CountOf(MsgErrorString), _T("%s %s"),
00176                    _T ("Can't create service"), lpszServiceDisplayName);
00177         ProcessError (EVENTLOG_ERROR_TYPE, MsgErrorString, 1, quiet);
00178 
00179         exitStatus = SERVICE_ERROR_CREATE_SERVICE;
00180         LEAVE;
00181       }
00182 
00183     /*
00184      * Create registry entries for the event log 
00185      */
00186     /*
00187      * Create registry Application event log key 
00188      */
00189     _tcscpy (szRegKey, szRegAppLogKey);
00190     _tcscat (szRegKey, lpszServiceName);
00191 
00192     /*
00193      * Create registry key 
00194      */
00195     if (RegCreateKey (HKEY_LOCAL_MACHINE, szRegKey, &hKey) != ERROR_SUCCESS)
00196       {
00197         _sntprintf (MsgErrorString, CountOf(MsgErrorString), _T("%s %s"),
00198                    _T ("is unable to create registry entries"), lpszServiceDisplayName);
00199         ProcessError (EVENTLOG_ERROR_TYPE, MsgErrorString, 1, quiet);
00200         exitStatus = SERVICE_ERROR_CREATE_REGISTRY_ENTRIES;
00201         LEAVE;
00202       }
00203 
00204     /*
00205      * Add Event ID message file name to the 'EventMessageFile' subkey 
00206      */
00207     RegSetValueEx (hKey, _T("EventMessageFile"), 0, REG_EXPAND_SZ,
00208                    (CONST BYTE *) szServicePath,
00209                    _tcslen (szServicePath) + sizeof (TCHAR));
00210 
00211     /*
00212      * Set the supported types flags. 
00213      */
00214     dwData = EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE;
00215     RegSetValueEx (hKey, _T("TypesSupported"), 0, REG_DWORD,
00216                    (CONST BYTE *) & dwData, sizeof (DWORD));
00217 
00218     /*
00219      * Close Registry key 
00220      */
00221     RegCloseKey (hKey);
00222 
00223     /*
00224      * Set Service Description String  and save startup parameters if present
00225      */
00226     if (lpszServiceDescription != NULL || StartUpArg->Argc > 2)
00227       {
00228         /*
00229          * Create Registry Key path 
00230          */
00231         _tcscpy (szRegKey, _T ("SYSTEM\\CurrentControlSet\\Services\\"));
00232         _tcscat (szRegKey, app_name_long);
00233         hKey = NULL;
00234 
00235         /*
00236          * Open Registry key using Create and Set access. 
00237          */
00238         if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, szRegKey, 0, KEY_WRITE,
00239                           &hKey) != ERROR_SUCCESS)
00240           {
00241             _sntprintf (MsgErrorString, CountOf(MsgErrorString), _T("%s %s"),
00242                        _T ("is unable to create registry entries"),
00243                        lpszServiceDisplayName);
00244             ProcessError (EVENTLOG_ERROR_TYPE, MsgErrorString, 1, quiet);
00245             exitStatus = SERVICE_ERROR_CREATE_REGISTRY_ENTRIES;
00246             LEAVE;
00247           }
00248 
00249         /*
00250          * Create description subkey and the set value 
00251          */
00252         if (lpszServiceDescription != NULL)
00253           {
00254             if (RegSetValueEx (hKey, _T("Description"), 0, REG_SZ,
00255                                (CONST BYTE *) lpszServiceDescription,
00256                                _tcslen (lpszServiceDescription) +
00257                                sizeof (TCHAR)) != ERROR_SUCCESS)
00258               {
00259                 _sntprintf (MsgErrorString, CountOf(MsgErrorString), _T("%s %s"),
00260                            _T ("is unable to create registry entries"),
00261                            lpszServiceDisplayName);
00262                 ProcessError (EVENTLOG_ERROR_TYPE, MsgErrorString, 1, quiet);
00263                 exitStatus = SERVICE_ERROR_CREATE_REGISTRY_ENTRIES;
00264                 LEAVE;
00265               };
00266           }
00267 
00268         /*
00269          * Save startup arguments if they are present 
00270          */
00271         if (StartUpArg->Argc > 2)
00272           {
00273             /*
00274              * Create Subkey parameters 
00275              */
00276             if (RegCreateKeyEx
00277                 (hKey, _T("Parameters"), 0, NULL,
00278                  REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL,
00279                  &hParamKey, NULL) != ERROR_SUCCESS)
00280               {
00281                 _sntprintf (MsgErrorString, CountOf(MsgErrorString), _T("%s %s"),
00282                            _T ("is unable to create registry entries"),
00283                            lpszServiceDisplayName);
00284                 ProcessError (EVENTLOG_ERROR_TYPE, MsgErrorString, 1, quiet);
00285                 exitStatus = SERVICE_ERROR_CREATE_REGISTRY_ENTRIES;
00286                 LEAVE;
00287               }
00288 
00289             /*
00290              * Save parameters 
00291              */
00292 
00293             /*
00294              * Loop through arguments 
00295              */
00296             if (quiet) /* Make sure we don't store -quiet arg */
00297               i = 3;
00298             else
00299               i = 2;
00300 
00301             for (j = 1; i < StartUpArg->Argc; i++, j++)
00302               {
00303                 _sntprintf (szRegKey, CountOf(szRegKey), _T("%s%d"), _T ("Param"), j);
00304 
00305                 /*
00306                  * Create registry key 
00307                  */
00308                 if (RegSetValueEx
00309                     (hParamKey, szRegKey, 0, REG_SZ,
00310                      (CONST BYTE *) StartUpArg->Argv[i],
00311                      _tcslen (StartUpArg->Argv[i]) +
00312                      sizeof (TCHAR)) != ERROR_SUCCESS)
00313                   {
00314                     _sntprintf (MsgErrorString, CountOf(MsgErrorString), _T("%s %s"),
00315                                _T ("is unable to create registry entries"),
00316                                lpszServiceDisplayName);
00317                     ProcessError (EVENTLOG_ERROR_TYPE, MsgErrorString, 1, quiet);
00318                     exitStatus = SERVICE_ERROR_CREATE_REGISTRY_ENTRIES;
00319                     LEAVE;
00320                   };
00321               }
00322           }
00323 
00324         /*
00325          * Everything is set, delete hKey 
00326          */
00327         RegCloseKey (hParamKey);
00328         RegCloseKey (hKey);
00329       }
00330 
00331     /*
00332      * Ready to log messages 
00333      */
00334 
00335     /*
00336      * Successfully registered as service 
00337      */
00338     _sntprintf (MsgErrorString, CountOf(MsgErrorString), _T("%s %s"), lpszServiceName,
00339                _T ("successfully registered as a service"));
00340 
00341     /*
00342      * Log message to eventlog 
00343      */
00344     ProcessError (EVENTLOG_INFORMATION_TYPE, MsgErrorString, 0, quiet);
00345   }
00346 
00347   FINALLY
00348   {
00349     if (hSCManager)
00350       CloseServiceHandle (hSCManager);
00351     if (hService)
00352       CloseServiceHandle (hService);
00353     if (hKey)
00354       RegCloseKey (hKey);
00355     if (hParamKey)
00356       RegCloseKey (hParamKey);
00357   }
00358   return (exitStatus);
00359 }
00360 
00361     /*
00362      * Unregister the service with the  Windows SCM 
00363      * Input - ServiceName
00364      */
00365 int
00366 UnregisterService (LPCTSTR lpszServiceName, int quiet)
00367 {
00368   TCHAR MsgErrorString[MAX_STR_SIZE];   /* Message or Error string */
00369   SC_HANDLE hSCManager = NULL;  /* SCM handle */
00370   SC_HANDLE hService = NULL;    /* Service Handle */
00371   SERVICE_STATUS sStatus;
00372   TCHAR szRegAppLogKey[] =
00373     _T("SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\");
00374   TCHAR szRegKey[512];
00375   int exitStatus = 0;
00376 /*  HKEY hKey = NULL;           ?* Key to registry entry */
00377   TRY
00378   {
00379     /*
00380      * Open Service Control Manager 
00381      */
00382     hSCManager = OpenSCManager (NULL, NULL, SC_MANAGER_CREATE_SERVICE);
00383     if (hSCManager == NULL)
00384       {
00385         ProcessError (EVENTLOG_ERROR_TYPE, _T ("Can't open SCM (Service Control Manager)"), 1, quiet);
00386         exitStatus = SERVICE_ERROR_SCM_OPEN;       
00387         LEAVE;
00388       }
00389 
00390     /*
00391      * Open registered service 
00392      */
00393     hService = OpenService (hSCManager, lpszServiceName, SERVICE_ALL_ACCESS);
00394     if (hService == NULL)
00395       {
00396         _sntprintf (MsgErrorString, CountOf(MsgErrorString), _T("%s %s"), _T ("Can't open service"),
00397                    lpszServiceName);
00398         ProcessError (EVENTLOG_ERROR_TYPE, MsgErrorString, 1, quiet);
00399         exitStatus = SERVICE_ERROR_OPEN_SERVICE;       
00400         LEAVE;
00401       }
00402 
00403     /*
00404      * Query service status 
00405      * If running stop before deleting 
00406      */
00407     if (QueryServiceStatus (hService, &sStatus))
00408       {
00409         if (sStatus.dwCurrentState == SERVICE_RUNNING
00410             || sStatus.dwCurrentState == SERVICE_PAUSED)
00411           {
00412             ControlService (hService, SERVICE_CONTROL_STOP, &sStatus);
00413           }
00414       };
00415 
00416     /*
00417      * Delete the service  
00418      */
00419     if (DeleteService (hService) == FALSE)
00420       {
00421         _sntprintf (MsgErrorString, CountOf(MsgErrorString), _T("%s %s"), _T ("Can't delete service"),
00422                    lpszServiceName);
00423 
00424         /*
00425          * Log message to eventlog 
00426          */
00427         ProcessError (EVENTLOG_ERROR_TYPE, MsgErrorString, 0, quiet);
00428         LEAVE;
00429       }
00430 
00431     /*
00432      * Log "Service deleted successfully " message to eventlog
00433      */
00434     _sntprintf (MsgErrorString, CountOf(MsgErrorString), _T("%s %s"), lpszServiceName, _T ("service deleted"));
00435     ProcessError (EVENTLOG_INFORMATION_TYPE, MsgErrorString, 0, quiet);
00436 
00437     /*
00438      * Delete registry entries for EventLog 
00439      */
00440     _tcscpy (szRegKey, szRegAppLogKey);
00441     _tcscat (szRegKey, lpszServiceName);
00442     RegDeleteKey (HKEY_LOCAL_MACHINE, szRegKey);
00443   }
00444 
00445   /*
00446    * Delete the handles 
00447    */
00448   FINALLY
00449   {
00450     if (hService)
00451       CloseServiceHandle (hService);
00452     if (hSCManager)
00453       CloseServiceHandle (hSCManager);
00454   }
00455   return (exitStatus);
00456 }
00457 
00458     /*
00459      * Write a message to the Windows event log.
00460      */
00461 VOID
00462 WriteToEventLog (WORD wType, LPCTSTR pszFormat, ...)
00463 {
00464   TCHAR szMessage[512];
00465   LPCTSTR LogStr[1];
00466   va_list ArgList;
00467   HANDLE hEventSource = NULL;
00468 
00469   va_start (ArgList, pszFormat);
00470   _vsntprintf (szMessage, CountOf(szMessage), pszFormat, ArgList);
00471   va_end (ArgList);
00472   LogStr[0] = szMessage;
00473   hEventSource = RegisterEventSource (NULL, app_name_long);
00474   if (hEventSource == NULL)
00475     return;
00476   ReportEvent (hEventSource, wType, 0,
00477                DISPLAY_MSG,
00478                NULL, 1, 0, LogStr, NULL);
00479   DeregisterEventSource (hEventSource);
00480 }
00481 
00482     /*
00483      * Pre-process the second command-line argument from the user. 
00484      *     Service related options are:
00485      *     -register       - registers the service
00486      *     -unregister     - unregisters the service
00487      *     -service        - run as service
00488      *     other command-line arguments are ignored here.
00489      *
00490      * Return: Type indicating the option specified
00491      */
00492 INT
00493 ParseCmdLineForServiceOption (int argc, TCHAR * argv[], int *quiet)
00494 {
00495   int nReturn = RUN_AS_CONSOLE; /* default is to run as a console application */
00496 
00497   if (argc >= 2)
00498     {
00499 
00500       /*
00501        * second argument present 
00502        */
00503       if (lstrcmpi (_T ("-register"), argv[1]) == 0)
00504         {
00505           nReturn = REGISTER_SERVICE;
00506         }
00507 
00508       else if (lstrcmpi (_T ("-unregister"), argv[1]) == 0)
00509         {
00510           nReturn = UN_REGISTER_SERVICE;
00511         }
00512 
00513       else if (lstrcmpi (_T ("-service"), argv[1]) == 0)
00514         {
00515           nReturn = RUN_AS_SERVICE;
00516         }
00517     }
00518 
00519   if (argc >= 3)
00520   {
00521     /*
00522      * third argument present 
00523      */
00524     if (lstrcmpi (_T ("-quiet"), argv[2]) == 0)
00525     {
00526       *quiet = 1;       
00527     }
00528   }
00529   
00530   return nReturn;
00531 }
00532 
00533     /*
00534      * Write error message to event log, console or pop-up window.
00535      *
00536      * If useGetLastError is 1, the last error returned from GetLastError()
00537      * is appended to pszMessage, separated by a ": ".
00538      *
00539      * eventLogType:                 MessageBox equivalent:
00540      * 
00541      * EVENTLOG_INFORMATION_TYPE     MB_ICONASTERISK
00542      * EVENTLOG_WARNING_TYPE         MB_ICONEXCLAMATION
00543      * EVENTLOG_ERROR_TYPE           MB_ICONSTOP
00544      * 
00545      */
00546 VOID
00547 ProcessError (WORD eventLogType, LPCTSTR pszMessage, int useGetLastError, int quiet)
00548 {
00549   HANDLE hEventSource = NULL;
00550   TCHAR pszMessageFull[MAX_STR_SIZE]; /* Combined pszMessage and GetLastError */
00551 
00552   /*
00553    * If useGetLastError enabled, generate text from GetLastError() and append to
00554    * pszMessageFull
00555    */
00556   if (useGetLastError) {
00557   LPTSTR pErrorMsgTemp = NULL;
00558   FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER |
00559                  FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError (),
00560                  MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
00561         (LPTSTR) & pErrorMsgTemp, 0, NULL);
00562 
00563     _sntprintf (pszMessageFull, CountOf(pszMessageFull), _T("%s: %s"), pszMessage, pErrorMsgTemp);
00564     if (pErrorMsgTemp) {
00565       LocalFree (pErrorMsgTemp);
00566       pErrorMsgTemp = NULL;
00567     }
00568   }
00569   else {
00570     _sntprintf (pszMessageFull, CountOf(pszMessageFull), _T("%s"), pszMessage);
00571   }
00572   
00573   hEventSource = RegisterEventSource (NULL, app_name_long);
00574   if (hEventSource != NULL) {
00575     LPCTSTR LogStr[1];
00576     LogStr[0] = pszMessageFull;
00577     
00578     if (ReportEvent (hEventSource, 
00579           eventLogType, 
00580           0,
00581           DISPLAY_MSG,  /* just output the text to the event log */
00582           NULL, 
00583           1, 
00584           0, 
00585           LogStr, 
00586           NULL)) {
00587     }
00588     else {
00589       LPTSTR pErrorMsgTemp = NULL;
00590       FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER |
00591           FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError (),
00592           MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
00593           (LPTSTR) & pErrorMsgTemp, 0, NULL);
00594       _ftprintf(stderr,_T("Could NOT lot to Event Log.  Error returned from ReportEvent(): %s\n"),pErrorMsgTemp);
00595       if (pErrorMsgTemp) {
00596         LocalFree (pErrorMsgTemp);
00597         pErrorMsgTemp = NULL;
00598       }
00599     }
00600     DeregisterEventSource (hEventSource);
00601     }
00602 
00603       if (quiet) {
00604     _ftprintf(stderr,_T("%s\n"),pszMessageFull);
00605       }
00606       else {
00607     switch (eventLogType) {
00608       case EVENTLOG_INFORMATION_TYPE:
00609         MessageBox (NULL, pszMessageFull, app_name_long, MB_ICONASTERISK);
00610         break;
00611       case EVENTLOG_WARNING_TYPE:
00612         MessageBox (NULL, pszMessageFull, app_name_long, MB_ICONEXCLAMATION);
00613         break;
00614       case EVENTLOG_ERROR_TYPE:
00615         MessageBox (NULL, pszMessageFull, app_name_long, MB_ICONSTOP);
00616         break;
00617       default:
00618         MessageBox (NULL, pszMessageFull, app_name_long, EVENTLOG_WARNING_TYPE);
00619         break;
00620       }
00621     }
00622 }
00623 
00624     /*
00625      * Update current service status.
00626      * Sends the current service status to the SCM. Also updates
00627      * the global service status structure.
00628      */
00629 static BOOL
00630 UpdateServiceStatus (DWORD dwStatus, DWORD dwErrorCode, DWORD dwWaitHint)
00631 {
00632   DWORD static dwCheckpoint = 1;
00633   DWORD dwControls = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE;
00634   if (g_fRunningAsService == FALSE)
00635     return FALSE;
00636   ZeroMemory (&ServiceStatus, sizeof (ServiceStatus));
00637   ServiceStatus.dwServiceType = SERVICE_WIN32;
00638   ServiceStatus.dwCurrentState = dwStatus;
00639   ServiceStatus.dwWaitHint = dwWaitHint;
00640   if (dwErrorCode)
00641     {
00642       ServiceStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
00643       ServiceStatus.dwServiceSpecificExitCode = dwErrorCode;
00644     }
00645 
00646   /*
00647    * special cases that depend on the new state 
00648    */
00649   switch (dwStatus)
00650     {
00651     case SERVICE_START_PENDING:
00652       dwControls = 0;
00653       break;
00654     case SERVICE_RUNNING:
00655     case SERVICE_STOPPED:
00656       dwCheckpoint = 0;
00657       break;
00658     }
00659   ServiceStatus.dwCheckPoint = dwCheckpoint++;
00660   ServiceStatus.dwControlsAccepted = dwControls;
00661   return ReportCurrentServiceStatus ();
00662 }
00663 
00664     /*
00665      * Reports current service status to SCM
00666      */
00667 static BOOL
00668 ReportCurrentServiceStatus ()
00669 {
00670   return SetServiceStatus (hServiceStatus, &ServiceStatus);
00671 }
00672 
00673     /*
00674      * ServiceMain function.
00675      */
00676 VOID WINAPI
00677 ServiceMain (DWORD argc, LPTSTR argv[])
00678 {
00679   SECURITY_ATTRIBUTES SecurityAttributes;
00680   unsigned threadId;
00681 
00682   /*
00683    * Input arguments
00684    */
00685   DWORD ArgCount = 0;
00686   LPTSTR *ArgArray = NULL;
00687   TCHAR szRegKey[512];
00688   HKEY hParamKey = NULL;
00689   DWORD TotalParams = 0;
00690   DWORD i;
00691   InputParams ThreadInputParams;
00692 
00693   /*
00694    * Build the Input parameters to pass to worker thread 
00695    */
00696 
00697   /*
00698    * SCM sends Service Name as first arg, increment to point
00699    * arguments user specified while starting control agent
00700    */
00701 
00702   /*
00703    * Read registry parameter 
00704    */
00705   ArgCount = 1;
00706 
00707   /*
00708    * Create registry key path 
00709    */
00710   _sntprintf (szRegKey, CountOf(szRegKey), _T("%s%s\\%s"),
00711              _T ("SYSTEM\\CurrentControlSet\\Services\\"), app_name_long,
00712              _T("Parameters"));
00713   if (RegOpenKeyEx
00714       (HKEY_LOCAL_MACHINE, szRegKey, 0, KEY_ALL_ACCESS, &hParamKey) == ERROR_SUCCESS)
00715     {
00716 
00717       /*
00718        * Read startup configuration information 
00719        */
00720       /*
00721        * Find number of subkeys inside parameters 
00722        */
00723       if (RegQueryInfoKey (hParamKey, NULL, NULL, 0,
00724            NULL, NULL, NULL, &TotalParams,
00725            NULL, NULL, NULL, NULL) == ERROR_SUCCESS)
00726         {
00727           if (TotalParams != 0)
00728             {
00729               ArgCount += TotalParams;
00730 
00731               /*
00732                * Allocate memory to hold strings 
00733                */
00734               ArgArray = calloc(ArgCount, sizeof(ArgArray[0]));
00735               if (ArgArray == 0)
00736                 {
00737                   WriteToEventLog (EVENTLOG_ERROR_TYPE,
00738                        _T ("Resource failure"));
00739                   return;
00740                 }
00741 
00742               /*
00743                * Copy first argument 
00744                */
00745               ArgArray[0] = _tcsdup (argv[0]);
00746               for (i = 1; i <= TotalParams; i++)
00747                 {
00748                   DWORD dwErrorcode;
00749                   DWORD nSize;
00750                   DWORD nRegkeyType;
00751                   TCHAR *szValue;
00752 
00753                   /*
00754                    * Create Subkey value name 
00755                    */
00756                   _sntprintf (szRegKey, CountOf(szRegKey), _T("%s%d"), _T("Param"), i);
00757 
00758                   /*
00759                    * Query subkey.
00760                    */
00761                   nSize = 0;
00762                   dwErrorcode = RegQueryValueEx(hParamKey, szRegKey, NULL,
00763                                                 &nRegkeyType, NULL, &nSize);
00764                   if (dwErrorcode == ERROR_SUCCESS) {
00765                     if (nRegkeyType == REG_SZ || nRegkeyType == REG_EXPAND_SZ) {
00766                       szValue = malloc(nSize + sizeof(szValue[0]));
00767                       if (szValue) {
00768                         dwErrorcode = RegQueryValueEx(hParamKey, szRegKey, NULL,
00769                                                       &nRegkeyType, (LPBYTE)szValue, &nSize);
00770                         if (dwErrorcode == ERROR_SUCCESS) {
00771                           szValue[nSize] = 0;
00772                           ArgArray[i] = szValue;
00773                         } else {
00774                           free(szValue);
00775                           WriteToEventLog(EVENTLOG_ERROR_TYPE, _T("Querying registry key %s failed: error code %ld"), szRegKey, dwErrorcode);
00776                         }
00777                       } else
00778                         WriteToEventLog(EVENTLOG_ERROR_TYPE, _T("Querying registry key %s failed: out of memory"), szRegKey);
00779                     } else
00780                       WriteToEventLog(EVENTLOG_ERROR_TYPE, _T("Type %ld of registry key %s is incorrect"), nRegkeyType, szRegKey);
00781                   } else
00782                     WriteToEventLog(EVENTLOG_ERROR_TYPE, _T("Querying registry key %s failed: error code %ld"), szRegKey, dwErrorcode);
00783 
00784                   if (!ArgArray[i]) {
00785                     TotalParams = ArgCount = i;
00786                     break;
00787                   }
00788                 }
00789             }
00790         }
00791       RegCloseKey (hParamKey);
00792     }
00793   if (ArgCount == 1)
00794     {
00795 
00796       /*
00797        * No startup args are given 
00798        */
00799       ThreadInputParams.Argc = argc;
00800       ThreadInputParams.Argv = argv;
00801     }
00802 
00803   else
00804     {
00805       ThreadInputParams.Argc = ArgCount;
00806       ThreadInputParams.Argv = ArgArray;
00807     }
00808 
00809   /*
00810    * Register Service Control Handler 
00811    */
00812   hServiceStatus = RegisterServiceCtrlHandler (app_name_long, ControlHandler);
00813   if (hServiceStatus == 0)
00814     {
00815       WriteToEventLog (EVENTLOG_ERROR_TYPE,
00816                        _T ("RegisterServiceCtrlHandler failed"));
00817       return;
00818     }
00819 
00820   /*
00821    * Update the service status to START_PENDING.
00822    */
00823   UpdateServiceStatus (SERVICE_START_PENDING, NO_ERROR, SCM_WAIT_INTERVAL);
00824 
00825   /*
00826    * Start the worker thread, which does the majority of the work .
00827    */
00828   TRY
00829   {
00830     if (SetSimpleSecurityAttributes (&SecurityAttributes) == FALSE)
00831       {
00832         WriteToEventLog (EVENTLOG_ERROR_TYPE,
00833                          _T ("Couldn't init security attributes"));
00834         LEAVE;
00835       }
00836     hServiceThread =
00837       (void *) _beginthreadex (&SecurityAttributes, 0,
00838                                ThreadFunction,
00839                                (void *) &ThreadInputParams, 0, &threadId);
00840     if (hServiceThread == NULL)
00841       {
00842         WriteToEventLog (EVENTLOG_ERROR_TYPE, _T ("Couldn't start worker thread"));
00843         LEAVE;
00844       }
00845 
00846     /*
00847      * Set service status to SERVICE_RUNNING.
00848      */
00849     UpdateServiceStatus (SERVICE_RUNNING, NO_ERROR, SCM_WAIT_INTERVAL);
00850 
00851     /*
00852      * Wait until the worker thread finishes.
00853      */
00854     WaitForSingleObject (hServiceThread, INFINITE);
00855   }
00856   FINALLY
00857   {
00858     /*
00859      * Release resources 
00860      */
00861     UpdateServiceStatus (SERVICE_STOPPED, NO_ERROR, SCM_WAIT_INTERVAL);
00862     if (hServiceThread)
00863       CloseHandle (hServiceThread);
00864     FreeSecurityAttributes (&SecurityAttributes);
00865 
00866     /*
00867      * Free allocated argument list 
00868      */
00869     if (ArgCount > 1 && ArgArray != NULL)
00870       {
00871         /*
00872          * Free all strings 
00873          */
00874         for (i = 0; i < ArgCount; i++)
00875           {
00876             free (ArgArray[i]);
00877           }
00878         free (ArgArray);
00879       }
00880   }
00881 }
00882 
00883     /*
00884      * Function to start as Windows service
00885      * The calling party should specify their entry point as input parameter
00886      * Returns TRUE if the Service is started successfully
00887      */
00888 BOOL
00889 RunAsService (INT (*ServiceFunction) (INT, LPTSTR *))
00890 {
00891 
00892   /*
00893    * Set the ServiceEntryPoint 
00894    */
00895   ServiceEntryPoint = ServiceFunction;
00896 
00897   /*
00898    * By default, mark as Running as a service 
00899    */
00900   g_fRunningAsService = TRUE;
00901 
00902   /*
00903    * Initialize ServiceTableEntry table 
00904    */
00905   ServiceTableEntry[0].lpServiceName = app_name_long;   /* Application Name */
00906 
00907   /*
00908    * Call SCM via StartServiceCtrlDispatcher to run as Service 
00909    * * If the function returns TRUE we are running as Service, 
00910    */
00911   if (StartServiceCtrlDispatcher (ServiceTableEntry) == FALSE)
00912     {
00913       g_fRunningAsService = FALSE;
00914 
00915       /*
00916        * Some other error has occurred. 
00917        */
00918       WriteToEventLog (EVENTLOG_ERROR_TYPE,
00919                        _T ("Couldn't start service - %s"), app_name_long);
00920     }
00921   return g_fRunningAsService;
00922 }
00923 
00924     /*
00925      * Service control handler function
00926      * Responds to SCM commands/requests
00927      * This service handles 4 commands
00928      * - interrogate, pause, continue and stop.
00929      */
00930 VOID WINAPI
00931 ControlHandler (DWORD dwControl)
00932 {
00933   switch (dwControl)
00934     {
00935     case SERVICE_CONTROL_INTERROGATE:
00936       ProcessServiceInterrogate ();
00937       break;
00938 
00939     case SERVICE_CONTROL_PAUSE:
00940       ProcessServicePause ();
00941       break;
00942 
00943     case SERVICE_CONTROL_CONTINUE:
00944       ProcessServiceContinue ();
00945       break;
00946 
00947     case SERVICE_CONTROL_STOP:
00948       ProcessServiceStop ();
00949       break;
00950     }
00951 }
00952 
00953     /*
00954      * To stop the service.
00955      * If a stop function was registered, invoke it,
00956      * otherwise terminate the worker thread.
00957      * After stopping, Service status is set to STOP in 
00958      * main loop
00959      */
00960 VOID
00961 ProcessServiceStop (VOID)
00962 {
00963   UpdateServiceStatus (SERVICE_STOP_PENDING, NO_ERROR, SCM_WAIT_INTERVAL);
00964 
00965   if (StopFunction != NULL)
00966     {
00967       (*StopFunction) ();
00968     }
00969 
00970   else
00971     {
00972       TerminateThread (hServiceThread, 0);
00973     }
00974 }
00975 
00976     /*
00977      * Returns the current state of the service to the SCM.
00978      */
00979 VOID
00980 ProcessServiceInterrogate (VOID)
00981 {
00982   ReportCurrentServiceStatus ();
00983 }
00984 
00985     /*
00986      * To Create a security descriptor with a NULL ACL, which
00987      * allows unlimited access. Returns a SECURITY_ATTRIBUTES
00988      * structure that contains the security descriptor.
00989      * The structure contains a dynamically allocated security
00990      * descriptor that must be freed either manually, or by
00991      * calling FreeSecurityAttributes 
00992      */
00993 BOOL
00994 SetSimpleSecurityAttributes (SECURITY_ATTRIBUTES * pSecurityAttr)
00995 {
00996   BOOL fReturn = FALSE;
00997   SECURITY_DESCRIPTOR *pSecurityDesc = NULL;
00998 
00999   /*
01000    * If an invalid address is passed as a parameter, return
01001    * FALSE right away. 
01002    */
01003   if (!pSecurityAttr)
01004     return FALSE;
01005   pSecurityDesc =
01006     (SECURITY_DESCRIPTOR *) LocalAlloc (LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);
01007   if (!pSecurityDesc)
01008     return FALSE;
01009   fReturn =
01010     InitializeSecurityDescriptor (pSecurityDesc, SECURITY_DESCRIPTOR_REVISION);
01011   if (fReturn != FALSE)
01012     {
01013       fReturn = SetSecurityDescriptorDacl (pSecurityDesc, TRUE, NULL, FALSE);
01014     }
01015   if (fReturn != FALSE)
01016     {
01017       pSecurityAttr->nLength = sizeof (SECURITY_ATTRIBUTES);
01018       pSecurityAttr->lpSecurityDescriptor = pSecurityDesc;
01019       pSecurityAttr->bInheritHandle = TRUE;
01020     }
01021 
01022   else
01023     {
01024       /*
01025        * Couldn't initialize or set security descriptor. 
01026        */
01027       LocalFree (pSecurityDesc);
01028     }
01029   return fReturn;
01030 }
01031 
01032     /*
01033      * This function Frees the security descriptor, if any was created.
01034      */
01035 VOID
01036 FreeSecurityAttributes (SECURITY_ATTRIBUTES * pSecurityAttr)
01037 {
01038   if (pSecurityAttr && pSecurityAttr->lpSecurityDescriptor)
01039     LocalFree (pSecurityAttr->lpSecurityDescriptor);
01040 }
01041 
01042     /*
01043      * This function runs in the worker thread
01044      * until an exit is forced, or until the SCM issues the STOP command.
01045      * Invokes registered service function
01046      * Returns when called registered function returns
01047      *
01048      * Input:
01049      *   lpParam contains argc and argv, pass to service main function 
01050      */
01051 unsigned WINAPI
01052 ThreadFunction (LPVOID lpParam)
01053 {
01054   InputParams * pInputArg = (InputParams *) lpParam;
01055   return (*ServiceEntryPoint) (pInputArg->Argc, pInputArg->Argv);
01056 }
01057 
01058     /*
01059      * This function is called to register an application-specific function
01060      *   which is invoked when the SCM stops the worker thread.
01061      */
01062 VOID
01063 RegisterStopFunction (VOID (*StopFunc) (VOID))
01064 {
01065   StopFunction = StopFunc;
01066 }
01067 
01068     /*
01069      * SCM pause command invokes this function
01070      * If the service is not running, this function does nothing.
01071      * Otherwise, suspend the worker thread and update the status.
01072      */
01073 VOID
01074 ProcessServicePause (VOID)
01075 {
01076   if (ServiceStatus.dwCurrentState == SERVICE_RUNNING)
01077     {
01078       UpdateServiceStatus (SERVICE_PAUSE_PENDING, NO_ERROR, SCM_WAIT_INTERVAL);
01079 
01080       if (SuspendThread (hServiceThread) != -1)
01081         {
01082           UpdateServiceStatus (SERVICE_PAUSED, NO_ERROR, SCM_WAIT_INTERVAL);
01083         }
01084     }
01085 }
01086 
01087     /*
01088      * SCM resume command invokes this function
01089      * If the service is not paused, this function does nothing.
01090      * Otherwise, resume the worker thread and update the status.
01091      */
01092 VOID
01093 ProcessServiceContinue (VOID)
01094 {
01095   if (ServiceStatus.dwCurrentState == SERVICE_PAUSED)
01096     {
01097       UpdateServiceStatus (SERVICE_CONTINUE_PENDING, NO_ERROR, SCM_WAIT_INTERVAL);
01098 
01099       if (ResumeThread (hServiceThread) != -1)
01100         {
01101           UpdateServiceStatus (SERVICE_RUNNING, NO_ERROR, SCM_WAIT_INTERVAL);
01102         }
01103     }
01104 }
01105 
01106 #endif /* WIN32 */
01107 
01108