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

net-snmpに対してSat Sep 5 13:14:28 2009に生成されました。  doxygen 1.4.7