winservice.c
上传用户:wxp200602
上传日期:2007-10-30
资源大小:4028k
文件大小:26k
源码类别:

SNMP编程

开发平台:

Unix_Linux

  1. /*
  2.  * Windows Service related function definitions
  3.  * By Raju Krishnappa(raju_krishnappa@yahoo.com)
  4.  *
  5.  */
  6. #ifdef WIN32
  7. #include <windows.h>
  8. #include <tchar.h>
  9. #include <stdio.h> /* sprintf */
  10. #include <process.h> /* beginthreadex  */
  11. #include <net-snmp/library/winservice.h>
  12. #ifdef mingw32 /* MinGW doesn't fully support exception handling. */
  13. #define TRY if(1)
  14. #define LEAVE goto labelFIN
  15. #define FINALLY do { 
  16. labelFIN: 
  17. } while(0); if(1)
  18. #else
  19. #define TRY __try
  20. #define LEAVE __leave
  21. #define FINALLY __finally
  22. #endif /* mingw32 */
  23.     /*
  24.      * External global variables used here
  25.      */
  26.     /*
  27.      * Application Name 
  28.      * This should be declared by the application, which wants to register as
  29.      * windows service
  30.      */
  31. extern LPTSTR app_name;
  32.     /*
  33.      * Declare global variable
  34.      */
  35.     /*
  36.      * Flag to indicate whether process is running as Service 
  37.      */
  38. BOOL g_fRunningAsService = FALSE;
  39.     /*
  40.      * Variable to maintain Current Service status 
  41.      */
  42. static SERVICE_STATUS ServiceStatus;
  43.     /*
  44.      * Service Handle 
  45.      */
  46. static SERVICE_STATUS_HANDLE hServiceStatus = 0L;
  47.     /*
  48.      * Service Table Entry 
  49.      */
  50. SERVICE_TABLE_ENTRY ServiceTableEntry[] = {
  51.   {NULL, ServiceMain}, /* Service Main function */
  52.   {NULL, NULL}
  53. };
  54.     /*
  55.      * Handle to Thread, to implement Pause, Resume and Stop functions
  56.      */
  57. static HANDLE hServiceThread = NULL; /* Thread Handle */
  58.     /*
  59.      * Holds calling partys Function Entry point, that should start
  60.      * when entering service mode
  61.      */
  62. static INT (*ServiceEntryPoint) (INT Argc, LPTSTR Argv[]) = 0L;
  63.     /*
  64.      * To hold Stop Function address, to be called when STOP request
  65.      * received from the SCM
  66.      */
  67. static VOID (*StopFunction) (VOID) = 0L;
  68. VOID
  69. ProcessError (WORD eventLogType, LPCTSTR pszMessage, int useGetLastError, int quiet);
  70.     /*
  71.      * To register as Windows Service with SCM(Service Control Manager)
  72.      * Input - Service Name, Service Display Name,Service Description and
  73.      * Service startup arguments
  74.      */
  75. int
  76. RegisterService (LPCTSTR lpszServiceName, LPCTSTR lpszServiceDisplayName,
  77.  LPCTSTR lpszServiceDescription,
  78.  InputParams * StartUpArg, int quiet) /* Startup argument to the service */
  79. {
  80.   TCHAR szServicePath[MAX_PATH]; /* To hold module File name */
  81.   TCHAR MsgErrorString[MAX_STR_SIZE]; /* Message or Error string */
  82.   TCHAR szServiceCommand[MAX_PATH + 9]; /* Command to execute */
  83.   SC_HANDLE hSCManager = NULL;
  84.   SC_HANDLE hService = NULL;
  85.   TCHAR szRegAppLogKey[] =
  86.     "SYSTEM\CurrentControlSet\Services\EventLog\Application\";
  87.   TCHAR szRegKey[512];
  88.   HKEY hKey = NULL; /* Key to registry entry */
  89.   HKEY hParamKey = NULL; /* To store startup parameters */
  90.   DWORD dwData; /* Type of logging supported */
  91.   DWORD i, j; /* Loop variables */
  92.   int exitStatus = 0;
  93.   GetModuleFileName (NULL, szServicePath, MAX_PATH);
  94.   TRY
  95.   {
  96.     /*
  97.      * Open Service Control Manager handle 
  98.      */
  99.     hSCManager = OpenSCManager (NULL, NULL, SC_MANAGER_CREATE_SERVICE);
  100.     if (hSCManager == NULL)
  101.       {
  102.         ProcessError (EVENTLOG_ERROR_TYPE, _T ("Can't open SCM (Service Control Manager)"), 1, quiet);
  103.         exitStatus = SERVICE_ERROR_SCM_OPEN;
  104. LEAVE;
  105.       }
  106.     /*
  107.      * Generate the Command to be executed by SCM 
  108.      */
  109.     _snprintf (szServiceCommand, sizeof(szServiceCommand), "%s %s", szServicePath, _T ("-service"));
  110.     /*
  111.      * Create the Desired service 
  112.      */
  113.     hService = CreateService (hSCManager, lpszServiceName, lpszServiceDisplayName,
  114. SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
  115. SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, szServiceCommand,
  116.       NULL, /* load-order group */
  117.       NULL, /* group member tag */
  118.       NULL, /* dependencies */
  119.       NULL, /* account */
  120.       NULL); /* password */
  121.     if (hService == NULL)
  122.       {
  123. _snprintf (MsgErrorString, sizeof(MsgErrorString), "%s %s",
  124.    _T ("Can't create service"), lpszServiceDisplayName);
  125.         ProcessError (EVENTLOG_ERROR_TYPE, MsgErrorString, 1, quiet);
  126.         exitStatus = SERVICE_ERROR_CREATE_SERVICE;
  127. LEAVE;
  128.       }
  129.     /*
  130.      * Create registry entries for EventLog 
  131.      */
  132.     /*
  133.      * Create registry Application event log key 
  134.      */
  135.     _tcscpy (szRegKey, szRegAppLogKey);
  136.     _tcscat (szRegKey, lpszServiceName);
  137.     /*
  138.      * Create registry key 
  139.      */
  140.     if (RegCreateKey (HKEY_LOCAL_MACHINE, szRegKey, &hKey) != ERROR_SUCCESS)
  141.       {
  142. _snprintf (MsgErrorString, sizeof(MsgErrorString), "%s %s",
  143.    _T ("is unable to create registry entries"), lpszServiceDisplayName);
  144.         ProcessError (EVENTLOG_ERROR_TYPE, MsgErrorString, 1, quiet);
  145.         exitStatus = SERVICE_ERROR_CREATE_REGISTRY_ENTRIES;
  146. LEAVE;
  147.       }
  148.     /*
  149.      * Add Event ID message file name to the 'EventMessageFile' subkey 
  150.      */
  151.     RegSetValueEx (hKey, "EventMessageFile", 0, REG_EXPAND_SZ,
  152.    (CONST BYTE *) szServicePath,
  153.    _tcslen (szServicePath) + sizeof (TCHAR));
  154.     /*
  155.      * Set the supported types flags. 
  156.      */
  157.     dwData = EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE;
  158.     RegSetValueEx (hKey, "TypesSupported", 0, REG_DWORD,
  159.    (CONST BYTE *) & dwData, sizeof (DWORD));
  160.     /*
  161.      * Close Registry key 
  162.      */
  163.     RegCloseKey (hKey);
  164.     /*
  165.      * Set Service Description String  and save startup parameters if present
  166.      */
  167.     if (lpszServiceDescription != NULL || StartUpArg->Argc > 2)
  168.       {
  169. /*
  170.  * Create Registry Key path 
  171.  */
  172. _tcscpy (szRegKey, _T ("SYSTEM\CurrentControlSet\Services\"));
  173. _tcscat (szRegKey, app_name);
  174. hKey = NULL;
  175. /*
  176.  * Open Registry key using Create and Set access. 
  177.  */
  178. if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, szRegKey, 0, KEY_WRITE,
  179.   &hKey) != ERROR_SUCCESS)
  180.   {
  181.     _snprintf (MsgErrorString, sizeof(MsgErrorString), "%s %s",
  182.        _T ("is unable to create registry entries"),
  183.        lpszServiceDisplayName);
  184.             ProcessError (EVENTLOG_ERROR_TYPE, MsgErrorString, 1, quiet);
  185.             exitStatus = SERVICE_ERROR_CREATE_REGISTRY_ENTRIES;
  186.     LEAVE;
  187.   }
  188. /*
  189.  * Create description subkey and the set value 
  190.  */
  191. if (lpszServiceDescription != NULL)
  192.   {
  193.     if (RegSetValueEx (hKey, "Description", 0, REG_SZ,
  194.        (CONST BYTE *) lpszServiceDescription,
  195.        _tcslen (lpszServiceDescription) +
  196.        sizeof (TCHAR)) != ERROR_SUCCESS)
  197.       {
  198. _snprintf (MsgErrorString, sizeof(MsgErrorString), "%s %s",
  199.    _T ("is unable to create registry entries"),
  200.    lpszServiceDisplayName);
  201.                 ProcessError (EVENTLOG_ERROR_TYPE, MsgErrorString, 1, quiet);
  202.                 exitStatus = SERVICE_ERROR_CREATE_REGISTRY_ENTRIES;
  203. LEAVE;
  204.       };
  205.   }
  206. /*
  207.  * Save startup arguments if they are present 
  208.  */
  209. if (StartUpArg->Argc > 2)
  210.   {
  211.     /*
  212.      * Create Subkey parameters 
  213.      */
  214.     if (RegCreateKeyEx
  215. (hKey, "Parameters", 0, NULL,
  216.  REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL,
  217.  &hParamKey, NULL) != ERROR_SUCCESS)
  218.       {
  219. _snprintf (MsgErrorString, sizeof(MsgErrorString), "%s %s",
  220.    _T ("is unable to create registry entries"),
  221.    lpszServiceDisplayName);
  222.                 ProcessError (EVENTLOG_ERROR_TYPE, MsgErrorString, 1, quiet);
  223.                 exitStatus = SERVICE_ERROR_CREATE_REGISTRY_ENTRIES;
  224.                 LEAVE;
  225.       }
  226.     /*
  227.      * Save parameters 
  228.      */
  229.     /*
  230.      * Loop through arguments 
  231.      */
  232.             if (quiet) /* Make sure we don't store -quiet arg */
  233.               i = 3;
  234.             else
  235.               i = 2;
  236.     for (j = 1; i < StartUpArg->Argc; i++, j++)
  237.       {
  238. _snprintf (szRegKey, sizeof(szRegKey), "%s%d", _T ("Param"), j);
  239. /*
  240.  * Create registry key 
  241.  */
  242. if (RegSetValueEx
  243.     (hParamKey, szRegKey, 0, REG_SZ,
  244.      (CONST BYTE *) StartUpArg->Argv[i],
  245.      _tcslen (StartUpArg->Argv[i]) +
  246.      sizeof (TCHAR)) != ERROR_SUCCESS)
  247.   {
  248.     _snprintf (MsgErrorString, sizeof(MsgErrorString), "%s %s",
  249.        _T ("is unable to create registry entries"),
  250.        lpszServiceDisplayName);
  251.                     ProcessError (EVENTLOG_ERROR_TYPE, MsgErrorString, 1, quiet);
  252.                     exitStatus = SERVICE_ERROR_CREATE_REGISTRY_ENTRIES;
  253.     LEAVE;
  254.   };
  255.       }
  256.   }
  257. /*
  258.  * Everything is set, delete hKey 
  259.  */
  260. RegCloseKey (hParamKey);
  261. RegCloseKey (hKey);
  262.       }
  263.     /*
  264.      * Ready to Log messages 
  265.      */
  266.     /*
  267.      * Successfully registered as service 
  268.      */
  269.     _snprintf (MsgErrorString, sizeof(MsgErrorString), "%s %s", lpszServiceName,
  270.        _T ("successfully registered as a service"));
  271.     /*
  272.      * Log message to eventlog 
  273.      */
  274.     ProcessError (EVENTLOG_INFORMATION_TYPE, MsgErrorString, 0, quiet);
  275.   }
  276.   FINALLY
  277.   {
  278.     if (hSCManager)
  279.       CloseServiceHandle (hSCManager);
  280.     if (hService)
  281.       CloseServiceHandle (hService);
  282.     if (hKey)
  283.       RegCloseKey (hKey);
  284.     if (hParamKey)
  285.       RegCloseKey (hParamKey);
  286.   }
  287.   return (exitStatus);
  288. }
  289.     /*
  290.      * Unregister the service with the  Windows SCM 
  291.      * Input - ServiceName
  292.      */
  293. int
  294. UnregisterService (LPCSTR lpszServiceName, int quiet)
  295. {
  296.   TCHAR MsgErrorString[MAX_STR_SIZE]; /* Message or Error string */
  297.   SC_HANDLE hSCManager = NULL; /* SCM handle */
  298.   SC_HANDLE hService = NULL; /* Service Handle */
  299.   SERVICE_STATUS sStatus;
  300.   TCHAR szRegAppLogKey[] =
  301.     "SYSTEM\CurrentControlSet\Services\EventLog\Application\";
  302.   TCHAR szRegKey[512];
  303.   int exitStatus = 0;
  304. /*  HKEY hKey = NULL; ?* Key to registry entry */
  305.   TRY
  306.   {
  307.     /*
  308.      * Open Service Control Manager 
  309.      */
  310.     hSCManager = OpenSCManager (NULL, NULL, SC_MANAGER_CREATE_SERVICE);
  311.     if (hSCManager == NULL)
  312.       {
  313.         ProcessError (EVENTLOG_ERROR_TYPE, _T ("Can't open SCM (Service Control Manager)"), 1, quiet);
  314.         exitStatus = SERVICE_ERROR_SCM_OPEN;       
  315. LEAVE;
  316.       }
  317.     /*
  318.      * Open registered service 
  319.      */
  320.     hService = OpenService (hSCManager, lpszServiceName, SERVICE_ALL_ACCESS);
  321.     if (hService == NULL)
  322.       {
  323. _snprintf (MsgErrorString, sizeof(MsgErrorString), "%s %s", _T ("Can't open service"),
  324.    lpszServiceName);
  325.         ProcessError (EVENTLOG_ERROR_TYPE, MsgErrorString, 1, quiet);
  326.         exitStatus = SERVICE_ERROR_OPEN_SERVICE;       
  327. LEAVE;
  328.       }
  329.     /*
  330.      * Query service status 
  331.      * If running stop before deleting 
  332.      */
  333.     if (QueryServiceStatus (hService, &sStatus))
  334.       {
  335. if (sStatus.dwCurrentState == SERVICE_RUNNING
  336.     || sStatus.dwCurrentState == SERVICE_PAUSED)
  337.   {
  338.     ControlService (hService, SERVICE_CONTROL_STOP, &sStatus);
  339.   }
  340.       };
  341.     /*
  342.      * Delete the service  
  343.      */
  344.     if (DeleteService (hService) == FALSE)
  345.       {
  346. _snprintf (MsgErrorString, sizeof(MsgErrorString), "%s %s", _T ("Can't delete service"),
  347.    lpszServiceName);
  348. /*
  349.  * Log message to eventlog 
  350.  */
  351.         ProcessError (EVENTLOG_ERROR_TYPE, MsgErrorString, 0, quiet);
  352. LEAVE;
  353.       }
  354.     /*
  355.      * Log "Service deleted successfully " message to eventlog
  356.      */
  357.     _snprintf (MsgErrorString, sizeof(MsgErrorString), "%s %s", lpszServiceName, _T ("service deleted"));
  358.     ProcessError (EVENTLOG_INFORMATION_TYPE, MsgErrorString, 0, quiet);
  359.     /*
  360.      * Delete registry entries for EventLog 
  361.      */
  362.     _tcscpy (szRegKey, szRegAppLogKey);
  363.     _tcscat (szRegKey, lpszServiceName);
  364.     RegDeleteKey (HKEY_LOCAL_MACHINE, szRegKey);
  365.   }
  366.   /*
  367.    * Delete the handles 
  368.    */
  369.   FINALLY
  370.   {
  371.     if (hService)
  372.       CloseServiceHandle (hService);
  373.     if (hSCManager)
  374.       CloseServiceHandle (hSCManager);
  375.   }
  376.   return (exitStatus);
  377. }
  378.     /*
  379.      * To write message to Windows Event log
  380.      * Input - Event Type, Message string
  381.      */
  382. VOID
  383. WriteToEventLog (WORD wType, LPCTSTR pszFormat, ...)
  384. {
  385.   TCHAR szMessage[512];
  386.   LPTSTR LogStr[1];
  387.   va_list ArgList;
  388.   HANDLE hEventSource = NULL;
  389.   va_start (ArgList, pszFormat);
  390.   _vsnprintf (szMessage, sizeof(szMessage), pszFormat, ArgList);
  391.   va_end (ArgList);
  392.   LogStr[0] = szMessage;
  393.   hEventSource = RegisterEventSource (NULL, app_name);
  394.   if (hEventSource == NULL)
  395.     return;
  396.   ReportEvent (hEventSource, wType, 0,
  397.        DISPLAY_MSG, /* To Just output the text to event log */
  398.        NULL, 1, 0, LogStr, NULL);
  399.   DeregisterEventSource (hEventSource);
  400. }
  401.     /*
  402.      * Pre-process the second command-line argument from the user. 
  403.      *     Service related options are:
  404.      *     -register       - registers the service
  405.      *     -unregister     - unregisters the service
  406.      *     -service        - run as service
  407.      *     other command-line arguments are ignored here.
  408.      *
  409.      * Return: Type indicating the option specified
  410.      */
  411. INT
  412. ParseCmdLineForServiceOption (int argc, TCHAR * argv[], int *quiet)
  413. {
  414.   int nReturn = RUN_AS_CONSOLE; /* Defualted to run as console */
  415.   if (argc >= 2)
  416.     {
  417.       /*
  418.        * second argument present 
  419.        */
  420.       if (lstrcmpi (_T ("-register"), argv[1]) == 0)
  421. {
  422.   nReturn = REGISTER_SERVICE;
  423. }
  424.       else if (lstrcmpi (_T ("-unregister"), argv[1]) == 0)
  425. {
  426.   nReturn = UN_REGISTER_SERVICE;
  427. }
  428.       else if (lstrcmpi (_T ("-service"), argv[1]) == 0)
  429. {
  430.   nReturn = RUN_AS_SERVICE;
  431. }
  432.     }
  433.   if (argc >= 3)
  434.   {
  435.     /*
  436.      * third argument present 
  437.      */
  438.     if (lstrcmpi (_T ("-quiet"), argv[2]) == 0)
  439.     {
  440.       *quiet = 1;
  441.     }
  442.   }
  443.   
  444.   return nReturn;
  445. }
  446.     /*
  447.      * Write error message to Event Log, console or pop-up window
  448.      *
  449.      * If useGetLastError is 1, the last error returned from GetLastError()
  450.      * is appended to pszMessage, separated by a ": ".
  451.      *
  452.      * eventLogType:                 MessageBox equivalent:
  453.      * 
  454.      * EVENTLOG_INFORMATION_TYPE     MB_ICONASTERISK
  455.      * EVENTLOG_WARNING_TYPE         MB_ICONEXCLAMATION
  456.      * EVENTLOG_ERROR_TYPE           MB_ICONSTOP
  457.      * 
  458.      */
  459. VOID
  460. ProcessError (WORD eventLogType, LPCTSTR pszMessage, int useGetLastError, int quiet)
  461. {
  462.   LPTSTR pErrorMsgTemp = NULL;
  463.   HANDLE hEventSource = NULL;
  464.   TCHAR pszMessageFull[MAX_STR_SIZE]; /* Combined pszMessage and GetLastError */
  465.   /*
  466.    * If useGetLastError enabled, generate text from GetLastError() and append to
  467.    * pszMessageFull
  468.    */
  469.   if (useGetLastError) {
  470.   FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER |
  471.  FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError (),
  472.  MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
  473.         (LPTSTR) & pErrorMsgTemp, 0, NULL);
  474.     _snprintf (pszMessageFull, sizeof(pszMessageFull), "%s: %s", pszMessage, pErrorMsgTemp);
  475.     if (pErrorMsgTemp) {
  476.       LocalFree (pErrorMsgTemp);
  477.       pErrorMsgTemp = NULL;
  478.     }
  479.   }
  480.   else {
  481.     _snprintf (pszMessageFull, sizeof(pszMessageFull), "%s", pszMessage);
  482.   }
  483.   
  484.   hEventSource = RegisterEventSource (NULL, app_name);
  485.   if (hEventSource != NULL) {
  486.     pErrorMsgTemp = pszMessageFull;
  487.     
  488.     if (ReportEvent (hEventSource, 
  489.           eventLogType, 
  490.           0,
  491.           DISPLAY_MSG, /* To Just output the text to event log */
  492.           NULL, 
  493.           1, 
  494.           0, 
  495.           &pErrorMsgTemp, 
  496.           NULL)) {
  497.     }
  498.     else {
  499.       FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER |
  500.           FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError (),
  501.           MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
  502.           (LPTSTR) & pErrorMsgTemp, 0, NULL);
  503.       
  504.       fprintf(stderr,"Could NOT lot to Event Log.  Error returned from ReportEvent(): %sn",pErrorMsgTemp);
  505.       if (pErrorMsgTemp) {
  506.         LocalFree (pErrorMsgTemp);
  507.         pErrorMsgTemp = NULL;
  508.       }
  509.     }
  510.     DeregisterEventSource (hEventSource);
  511.     }
  512.       if (quiet) {
  513.     fprintf(stderr,"%sn",pszMessageFull);
  514.       }
  515.       else {
  516.     switch (eventLogType) {
  517.       case EVENTLOG_INFORMATION_TYPE:
  518.         MessageBox (NULL, pszMessageFull, app_name, MB_ICONASTERISK);
  519.         break;
  520.       case EVENTLOG_WARNING_TYPE:
  521.         MessageBox (NULL, pszMessageFull, app_name, MB_ICONEXCLAMATION);
  522.         break;
  523.       case EVENTLOG_ERROR_TYPE:
  524.         MessageBox (NULL, pszMessageFull, app_name, MB_ICONSTOP);
  525.         break;
  526.       default:
  527.         MessageBox (NULL, pszMessageFull, app_name, EVENTLOG_WARNING_TYPE);
  528.         break;
  529.       }
  530.     }
  531.   
  532.   LocalFree (pErrorMsgTemp);  
  533. }
  534.     /*
  535.      *  To update current service status 
  536.      *  Sends the current service status to the SCM. Also updates
  537.      *  the global service status structure.
  538.      */
  539. static BOOL
  540. UpdateServiceStatus (DWORD dwStatus, DWORD dwErrorCode, DWORD dwWaitHint)
  541. {
  542.   DWORD static dwCheckpoint = 1;
  543.   DWORD dwControls = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE;
  544.   if (g_fRunningAsService == FALSE)
  545.     return FALSE;
  546.   ZeroMemory (&ServiceStatus, sizeof (ServiceStatus));
  547.   ServiceStatus.dwServiceType = SERVICE_WIN32;
  548.   ServiceStatus.dwCurrentState = dwStatus;
  549.   ServiceStatus.dwWaitHint = dwWaitHint;
  550.   if (dwErrorCode)
  551.     {
  552.       ServiceStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
  553.       ServiceStatus.dwServiceSpecificExitCode = dwErrorCode;
  554.     }
  555.   /*
  556.    * special cases that depend on the new state 
  557.    */
  558.   switch (dwStatus)
  559.     {
  560.     case SERVICE_START_PENDING:
  561.       dwControls = 0;
  562.       break;
  563.     case SERVICE_RUNNING:
  564.     case SERVICE_STOPPED:
  565.       dwCheckpoint = 0;
  566.       break;
  567.     }
  568.   ServiceStatus.dwCheckPoint = dwCheckpoint++;
  569.   ServiceStatus.dwControlsAccepted = dwControls;
  570.   return ReportCurrentServiceStatus ();
  571. }
  572.     /*
  573.      * Reports current Service status to SCM
  574.      */
  575. static BOOL
  576. ReportCurrentServiceStatus ()
  577. {
  578.   return SetServiceStatus (hServiceStatus, &ServiceStatus);
  579. }
  580.     /*
  581.      * The ServiceMain function to start service.
  582.      */
  583. VOID WINAPI
  584. ServiceMain (DWORD argc, LPTSTR argv[])
  585. {
  586.   SECURITY_ATTRIBUTES SecurityAttributes;
  587.   DWORD dwThreadId;
  588.   /*
  589.    * Input Arguments to function startup 
  590.    */
  591.   DWORD ArgCount = 0;
  592.   LPTSTR *ArgArray = NULL;
  593.   TCHAR szRegKey[512];
  594.   TCHAR szValue[128];
  595.   DWORD nSize;
  596.   HKEY hParamKey = NULL; /* To read startup parameters */
  597.   DWORD TotalParams = 0;
  598.   DWORD i;
  599.   InputParams ThreadInputParams;
  600.   /*
  601.    * Build the Input parameters to pass to worker thread 
  602.    */
  603.   /*
  604.    * SCM sends Service Name as first arg, increment to point
  605.    * arguments user specified while starting contorl agent
  606.    */
  607.   /*
  608.    * Read registry parameter 
  609.    */
  610.   ArgCount = 1;
  611.   /*
  612.    * Create Registry Key path 
  613.    */
  614.   _snprintf (szRegKey, sizeof(szRegKey), "%s%s\%s",
  615.      _T ("SYSTEM\CurrentControlSet\Services\"), app_name,
  616.      "Parameters");
  617.   if (RegOpenKeyEx
  618.       (HKEY_LOCAL_MACHINE, szRegKey, 0, KEY_ALL_ACCESS, &hParamKey) == ERROR_SUCCESS)
  619.     {
  620.       /*
  621.        * Read startup Configuration information 
  622.        */
  623.       /*
  624.        * Find number of subkeys inside parameters 
  625.        */
  626.       if (RegQueryInfoKey (hParamKey, NULL, NULL, 0,
  627.    NULL, NULL, NULL, &TotalParams,
  628.    NULL, NULL, NULL, NULL) == ERROR_SUCCESS)
  629. {
  630.   if (TotalParams != 0)
  631.     {
  632.       ArgCount += TotalParams;
  633.       /*
  634.        * Allocate memory to hold strings 
  635.        */
  636.       ArgArray = (LPTSTR *) malloc (sizeof (LPTSTR) * ArgCount);
  637.       if (ArgArray == 0)
  638.                 {
  639.                   WriteToEventLog (EVENTLOG_ERROR_TYPE,
  640.        _T ("Resource failure"));
  641.                   return;
  642.                 }
  643.       /*
  644.        * Copy first argument 
  645.        */
  646.       ArgArray[0] = _tcsdup (argv[0]);
  647.       for (i = 1; i <= TotalParams; i++)
  648. {
  649.   /*
  650.    * Create Subkey value name 
  651.    */
  652.   _snprintf (szRegKey, sizeof(szRegKey), "%s%d", "Param", i);
  653.   /*
  654.    * Set size 
  655.    */
  656.   nSize = 128;
  657.   RegQueryValueEx (hParamKey, szRegKey, 0, NULL,
  658.    (LPBYTE) & szValue, &nSize);
  659.   ArgArray[i] = _tcsdup (szValue);
  660. }
  661.     }
  662. }
  663.       RegCloseKey (hParamKey);
  664.     }
  665.   if (ArgCount == 1)
  666.     {
  667.       /*
  668.        * No statup agrs are given 
  669.        */
  670.       ThreadInputParams.Argc = argc;
  671.       ThreadInputParams.Argv = argv;
  672.     }
  673.   else
  674.     {
  675.       ThreadInputParams.Argc = ArgCount;
  676.       ThreadInputParams.Argv = ArgArray;
  677.     }
  678.   /*
  679.    * Register Service Control Handler 
  680.    */
  681.   hServiceStatus = RegisterServiceCtrlHandler (app_name, ControlHandler);
  682.   if (hServiceStatus == 0)
  683.     {
  684.       WriteToEventLog (EVENTLOG_ERROR_TYPE,
  685.        _T ("RegisterServiceCtrlHandler failed"));
  686.       return;
  687.     }
  688.   /*
  689.    * Update the service status to START_PENDING 
  690.    */
  691.   UpdateServiceStatus (SERVICE_START_PENDING, NO_ERROR, SCM_WAIT_INTERVAL);
  692.   /*
  693.    * Spin of worker thread, which does majority of the work 
  694.    */
  695.   TRY
  696.   {
  697.     if (SetSimpleSecurityAttributes (&SecurityAttributes) == FALSE)
  698.       {
  699. WriteToEventLog (EVENTLOG_ERROR_TYPE,
  700.  _T ("Couldn't init security attributes"));
  701. LEAVE;
  702.       }
  703.     hServiceThread =
  704.       (void *) _beginthreadex (&SecurityAttributes, 0,
  705.        ThreadFunction,
  706.        (void *) &ThreadInputParams, 0, &dwThreadId);
  707.     if (hServiceThread == NULL)
  708.       {
  709. WriteToEventLog (EVENTLOG_ERROR_TYPE, _T ("Couldn't start worker thread"));
  710. LEAVE;
  711.       }
  712.     /*
  713.      * Set Service Status to Running 
  714.      */
  715.     UpdateServiceStatus (SERVICE_RUNNING, NO_ERROR, SCM_WAIT_INTERVAL);
  716.     /*
  717.      * Wait for termination event and worker thread to
  718.      * * spin down.
  719.      */
  720.     WaitForSingleObject (hServiceThread, INFINITE);
  721.   }
  722.   FINALLY
  723.   {
  724.     /*
  725.      * Release resources 
  726.      */
  727.     UpdateServiceStatus (SERVICE_STOPPED, NO_ERROR, SCM_WAIT_INTERVAL);
  728.     if (hServiceThread)
  729.       CloseHandle (hServiceThread);
  730.     FreeSecurityAttributes (&SecurityAttributes);
  731.     /*
  732.      * Delete allocated argument list 
  733.      */
  734.     if (ArgCount > 1 && ArgArray != NULL)
  735.       {
  736. /*
  737.  * Delete all strings 
  738.  */
  739. for (i = 0; i < ArgCount; i++)
  740.   {
  741.     free (ArgArray[i]);
  742.   }
  743. free (ArgArray);
  744.       }
  745.   }
  746. }
  747.     /*
  748.      * Function to start as Windows service
  749.      * The calling party should specify their entry point as input parameter
  750.      * Returns TRUE if the Service is started successfully
  751.      */
  752. BOOL
  753. RunAsService (INT (*ServiceFunction) (INT, LPTSTR *))
  754. {
  755.   /*
  756.    * Set the ServiceEntryPoint 
  757.    */
  758.   ServiceEntryPoint = ServiceFunction;
  759.   /*
  760.    * By default, mark as Running as a service 
  761.    */
  762.   g_fRunningAsService = TRUE;
  763.   /*
  764.    * Initialize ServiceTableEntry table 
  765.    */
  766.   ServiceTableEntry[0].lpServiceName = app_name; /* Application Name */
  767.   /*
  768.    * Call SCM via StartServiceCtrlDispatcher to run as Service 
  769.    * * If the function returns TRUE we are running as Service, 
  770.    */
  771.   if (StartServiceCtrlDispatcher (ServiceTableEntry) == FALSE)
  772.     {
  773.       g_fRunningAsService = FALSE;
  774.       /*
  775.        * Some other error has occurred. 
  776.        */
  777.       WriteToEventLog (EVENTLOG_ERROR_TYPE,
  778.        _T ("Couldn't start service - %s"), app_name);
  779.     }
  780.   return g_fRunningAsService;
  781. }
  782.     /*
  783.      * Service control handler function
  784.      * Responds to SCM commands/requests
  785.      * This service handles 4 commands
  786.      * - interrogate, pause, continue and stop.
  787.      */
  788. VOID WINAPI
  789. ControlHandler (DWORD dwControl)
  790. {
  791.   switch (dwControl)
  792.     {
  793.     case SERVICE_CONTROL_INTERROGATE:
  794.       ProcessServiceInterrogate ();
  795.       break;
  796.     case SERVICE_CONTROL_PAUSE:
  797.       ProcessServicePause ();
  798.       break;
  799.     case SERVICE_CONTROL_CONTINUE:
  800.       ProcessServiceContinue ();
  801.       break;
  802.     case SERVICE_CONTROL_STOP:
  803.       ProcessServiceStop ();
  804.       break;
  805.     }
  806. }
  807.     /*
  808.      * To stop the service.
  809.      * If a stop function was registered, invoke it,
  810.      * otherwise terminate the worker thread.
  811.      * After stopping, Service status is set to STOP in 
  812.      * main loop
  813.      */
  814. VOID
  815. ProcessServiceStop (VOID)
  816. {
  817.   UpdateServiceStatus (SERVICE_STOP_PENDING, NO_ERROR, SCM_WAIT_INTERVAL);
  818.   if (StopFunction != NULL)
  819.     {
  820.       (*StopFunction) ();
  821.     }
  822.   else
  823.     {
  824.       TerminateThread (hServiceThread, 0);
  825.     }
  826. }
  827.     /*
  828.      * Returns the current state of the service to the SCM.
  829.      */
  830. VOID
  831. ProcessServiceInterrogate (VOID)
  832. {
  833.   ReportCurrentServiceStatus ();
  834. }
  835.     /*
  836.      * To Create a security descriptor with a NULL ACL, which
  837.      * allows unlimited access. Returns a SECURITY_ATTRIBUTES
  838.      * structure that contains the security descriptor.
  839.      * The structure contains a dynamically allocated security
  840.      * descriptor that must be freed either manually, or by
  841.      * calling FreeSecurityAttributes 
  842.      */
  843. BOOL
  844. SetSimpleSecurityAttributes (SECURITY_ATTRIBUTES * pSecurityAttr)
  845. {
  846.   BOOL fReturn = FALSE;
  847.   SECURITY_DESCRIPTOR *pSecurityDesc = NULL;
  848.   /*
  849.    * If an invalid address is passed as a parameter, return
  850.    * FALSE right away. 
  851.    */
  852.   if (!pSecurityAttr)
  853.     return FALSE;
  854.   pSecurityDesc =
  855.     (SECURITY_DESCRIPTOR *) LocalAlloc (LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);
  856.   if (!pSecurityDesc)
  857.     return FALSE;
  858.   fReturn =
  859.     InitializeSecurityDescriptor (pSecurityDesc, SECURITY_DESCRIPTOR_REVISION);
  860.   if (fReturn != FALSE)
  861.     {
  862.       fReturn = SetSecurityDescriptorDacl (pSecurityDesc, TRUE, NULL, FALSE);
  863.     }
  864.   if (fReturn != FALSE)
  865.     {
  866.       pSecurityAttr->nLength = sizeof (SECURITY_ATTRIBUTES);
  867.       pSecurityAttr->lpSecurityDescriptor = pSecurityDesc;
  868.       pSecurityAttr->bInheritHandle = TRUE;
  869.     }
  870.   else
  871.     {
  872.       /*
  873.        * Couldn't initialize or set security descriptor. 
  874.        */
  875.       LocalFree (pSecurityDesc);
  876.     }
  877.   return fReturn;
  878. }
  879.     /*
  880.      * This function Frees the security descriptor, if any was created.
  881.      */
  882. VOID
  883. FreeSecurityAttributes (SECURITY_ATTRIBUTES * pSecurityAttr)
  884. {
  885.   if (pSecurityAttr && pSecurityAttr->lpSecurityDescriptor)
  886.     LocalFree (pSecurityAttr->lpSecurityDescriptor);
  887. }
  888.     /*
  889.      * This function runs in the worker thread
  890.      * until an exit is forced, or until the SCM issues the STOP command.
  891.      * Invokes registered service function
  892.      * Returns when called registered function returns
  893.      *
  894.      * Input:
  895.      *   lpParam contains argc and argv, pass to service main function 
  896.      */
  897. DWORD WINAPI
  898. ThreadFunction (LPVOID lpParam)
  899. {
  900.   InputParams * pInputArg = (InputParams *) lpParam;
  901.   return (*ServiceEntryPoint) (pInputArg->Argc, pInputArg->Argv);
  902. }
  903.     /*
  904.      * This function is called to register an application-specific function
  905.      *   which is invoked when the SCM stops the worker thread.
  906.      */
  907. VOID
  908. RegisterStopFunction (VOID (*StopFunc) (VOID))
  909. {
  910.   StopFunction = StopFunc;
  911. }
  912.     /*
  913.      * SCM pause command invokes this function
  914.      * If the service is not running, this function does nothing.
  915.      * Otherwise, suspend the worker thread and update the status.
  916.      */
  917. VOID
  918. ProcessServicePause (VOID)
  919. {
  920.   if (ServiceStatus.dwCurrentState == SERVICE_RUNNING)
  921.     {
  922.       UpdateServiceStatus (SERVICE_PAUSE_PENDING, NO_ERROR, SCM_WAIT_INTERVAL);
  923.       if (SuspendThread (hServiceThread) != -1)
  924. {
  925.   UpdateServiceStatus (SERVICE_PAUSED, NO_ERROR, SCM_WAIT_INTERVAL);
  926. }
  927.     }
  928. }
  929.     /*
  930.      * SCM resume command invokes this function
  931.      * If the service is not paused, this function does nothing.
  932.      * Otherwise, resume the worker thread and update the status.
  933.      */
  934. VOID
  935. ProcessServiceContinue (VOID)
  936. {
  937.   if (ServiceStatus.dwCurrentState == SERVICE_PAUSED)
  938.     {
  939.       UpdateServiceStatus (SERVICE_CONTINUE_PENDING, NO_ERROR, SCM_WAIT_INTERVAL);
  940.       if (ResumeThread (hServiceThread) != -1)
  941. {
  942.   UpdateServiceStatus (SERVICE_RUNNING, NO_ERROR, SCM_WAIT_INTERVAL);
  943. }
  944.     }
  945. }
  946. #endif /* WIN32 */