/* * THIS FILE IS UNDER CVS - DO NOT MODIFY UNLESS YOU HAVE * CHECKED IT OUT USING THE COMMAND CHECKOUT. * * $Id: service_ew.c,v 1.4 2005/06/08 17:39:03 mark Exp $ * * Revision history: * $Log: service_ew.c,v $ * Revision 1.4 2005/06/08 17:39:03 mark * Added wait_for_dependencies * * Revision 1.3 2004/12/06 22:15:00 mark * Added get_service_status * * Revision 1.2 2004/08/03 18:48:45 mark * Added dependencies param to install_service * * Revision 1.1 2004/07/13 16:47:12 mark * Initial checkin * */ #include #include #include #include "service_ew.h" #include "earthworm_simple_funcs.h" static SERVICE_STATUS serv_status; static SERVICE_STATUS_HANDLE serv_handle = 0; /******************* * install_service * * Installs this app as a service. This only needs to be called once, period; * not once per time you run. After this is called, the service will be set * to start automatically at every reboot. * * Params: * szServiceName - internal name given to this service * szDisplayName - name displayed in the Service Manager ******************/ void install_service(const char *szServiceName, const char *szDisplayName, const char *szDependencies) { SC_HANDLE schSCManager, schService; char szPath[512]; int err; schSCManager = OpenSCManager( NULL, NULL, SC_MANAGER_CREATE_SERVICE); if (schSCManager == NULL) { fprintf(stderr, "OpenSCManager failed; error code: %d", GetLastError()); return; } if (GetModuleFileName(NULL, szPath, 512) == 0) { fprintf(stderr, "Unable to get binary path; error code: %d", GetLastError()); CloseServiceHandle(schSCManager); return; } schService = CreateService( schSCManager, /* SCManager database */ szServiceName, /* name of service */ szDisplayName, /* service name to display */ SERVICE_ALL_ACCESS, /* desired access */ SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS, /* service type */ SERVICE_AUTO_START, /* start type */ SERVICE_ERROR_NORMAL, /* error control type */ szPath, /* service's binary */ NULL, /* no load ordering group */ NULL, /* no tag identifier */ szDependencies, /* any dependencies */ NULL, /* LocalSystem account */ NULL /* no password */ ); if (schService == NULL) { err = GetLastError(); if (err == ERROR_SERVICE_EXISTS) { fprintf(stderr, "This service has already been installed."); } else { fprintf(stderr, "Unable to install service; error code: %d", GetLastError()); } } else { fprintf(stderr, "%s service successfully installed.", szServiceName); CloseServiceHandle(schService); } CloseServiceHandle(schSCManager); } /******************* * uninstall_service * * Uninstalls this app as a service. * * Params: * szServiceName - internal name given to this service, used during install_service ******************/ void uninstall_service(const char *szServiceName) { SC_HANDLE schSCManager, schService; int err; schSCManager = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS); if (schSCManager == NULL) { fprintf(stderr, "OpenSCManager failed; error code: %d", GetLastError()); return; } schService = OpenService( schSCManager, szServiceName, SERVICE_ALL_ACCESS); if (schService == NULL) { err = GetLastError(); if (err == ERROR_SERVICE_DOES_NOT_EXIST) { fprintf(stderr, "The service has already been uninstalled! That was easy..."); } else { fprintf(stderr, "Unable to open service; error code: %d", err); } CloseServiceHandle(schSCManager); return; } if(!DeleteService(schService)) { fprintf(stderr, "Unable to delete service; error code: %d", GetLastError()); } else { fprintf(stderr, "%s service successfully removed", szServiceName); } CloseServiceHandle(schService); CloseServiceHandle(schSCManager); } /****************** * start_service * * Starts running the service. This function should be called within the main() function. * Its purpose it to register this app as running with the Windows Service Manager, then * call another function which will act as a second main() function. * * Params; * szServiceName - internal name given to this service, used during install_service * mainfunc - new "main" function. * Returns: * 0 if all is well, otherwise an error value. This function will not return until the * "mainfunc" function has completed and returned. *****************/ int start_service(char *szServiceName, LPSERVICE_MAIN_FUNCTION mainfunc) { SERVICE_TABLE_ENTRY serviceTable[] = { { szServiceName, mainfunc }, { NULL, NULL } }; if (!StartServiceCtrlDispatcher(serviceTable)) { fprintf(stderr, "Error %d from StartServiceCtrlDispatcher\n", GetLastError() ); return 1; } return 0; } /****************** * init_service * * Initializes the service. This function should be called towards the beginning of * the mainfunc() function passed to start_service. * * Params: * szServiceName - internal name given to this service, used during install_service * and start_service * allow_pause - non-zero if this service can pause/continue; zero if it can't * handler - function that is called when the Windows Service Manager needs to issue * STOP, PAUSE, or INTERROGATE commands * Returns: * 0 if all is well, otherwise an error value. ******************/ int init_service(char *szServiceName, int allow_pause, LPHANDLER_FUNCTION handler) { serv_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; if (allow_pause == 0) serv_status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN; else serv_status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_PAUSE_CONTINUE; serv_status.dwServiceSpecificExitCode = 0; serv_handle = RegisterServiceCtrlHandler(szServiceName, handler); if (serv_handle == 0) { fprintf(stderr, "Unable to register service control handler; error code: %d", GetLastError()); return -1; } return 0; } /****************** * set_service_status * * Informs the Windows Service Manager (and anybody else who's listening via the WSM) * the current state of the service. See the Win32 SetServiceStatus() function for * more details. * * Params: * state - current state of the service (e.g. SERVICE_RUNNING), or 0 to re-send the * last known state. * error - Win32 error to report, or 0 if everything is fine * checkpoint - current checkpoint for _PENDING states. Start with a 0 value when you * enter one of these states, then increment by 1 with subsequent set_service_status * calls until you're no longer pending. * hint - how long the Service Manager can expect to wait until you're no longer pending, * or until the next "checkpoint" status is sent (in ms). * Returns: * 0 if all is well, otherwise an error value. ******************/ int set_service_status(int state, int error, int checkpoint, int hint) { if (state != 0) { serv_status.dwCurrentState = state; serv_status.dwWin32ExitCode = error; serv_status.dwCheckPoint = checkpoint; serv_status.dwWaitHint = hint; } if (SetServiceStatus(serv_handle, &serv_status)) return -1; return 0; } /****************** * get_service_status * * Determines the status of a given service. This can be used to determine if all service * dependencies are running. * * Params: * szServiceName - internal name given to this service, used during install_service * and start_service (does not have to have registered as an earthworm/hydra * service, but can refer to any service) * pState - return value referring to current service state (e.g. SERVICE_RUNNING) * pError - return value referring to current service error value, or 0 if all is well. * * Returns: * 0 if all is well, otherwise an error value. ******************/ int get_service_status(char *szServiceName, int *pState, int *pError) { SC_HANDLE schSCManager, schService; SERVICE_STATUS status; int retval = 0; schSCManager = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS); if (schSCManager == NULL) { retval = GetLastError(); return retval; } schService = OpenService(schSCManager, szServiceName, GENERIC_READ); if (schService == NULL) { retval = GetLastError(); CloseServiceHandle(schSCManager); return retval; } if (!QueryServiceStatus(schService, &status)) { retval = GetLastError(); CloseServiceHandle(schService); CloseServiceHandle(schSCManager); return retval; } *pState = status.dwCurrentState; *pError = status.dwWin32ExitCode; CloseServiceHandle(schService); CloseServiceHandle(schSCManager); return 0; } /****************** * wait_for_dependencies * * Waits for all of the given dependencies to start running. This is used when a service has * more than one dependency, since Windows only waits for one service to start running rather * than all of them. * * Params: * szDependencyList - List of dependant services given to install_service; each service name * is NULL terminated, and the entire array is double-NULL terminated. * pCheckpoint - On entry, the next checkpoint to pass to set_service_status. * On exit, the next checkpoint for the calling function to pass to * set_service_status. * * Returns: * 0 if all is well, otherwise an error value. ******************/ int wait_for_dependencies(char *szDependencyList, int *pCheckpoint) { char *szDeps = szDependencyList; int i, state, err, retval; while (*szDeps != 0) // step through all dependencies { while (TRUE) // wait for each dependency { set_service_status(SERVICE_START_PENDING, 0, *pCheckpoint, 1500); *pCheckpoint++; // Get the status of this dependency. retval = get_service_status(szDeps, &state, &err); if (retval != 0) { printf("Error %d trying to find status of %s!\n", retval, szDeps); return -1; } // If this is running properly, continue to the next dependency. if (state == SERVICE_RUNNING) break; // Wait for another second for this dependency to come up... sleep_ew(1000); } // while we wait for a single dependency // Skip to just past the next null character. i = strlen(szDeps); szDeps += (i + 1); } // while we step through all dependencies return 0; }