/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at * trunk/opends/resource/legal-notices/OpenDS.LICENSE * or https://OpenDS.dev.java.net/OpenDS.LICENSE. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, * add the following below this CDDL HEADER, with the fields enclosed * by brackets "[]" replaced with your own identifying * information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Portions Copyright 2007 Sun Microsystems, Inc. */ #include "service.h" int _serviceCurStatus; SERVICE_STATUS_HANDLE *_serviceStatusHandle; HANDLE _terminationEvent = NULL; char *_instanceDir = NULL; HANDLE _eventLog = NULL; BOOL DEBUG = FALSE; // ---------------------------------------------------- // Register a service handler to the service control dispatcher. // A service handler will manage the control function such as: // stop, pause, continue, shutdown, interrogate. The control functions // are sent by the service control dispatcher upon user request // (ie. NET STOP) // // serviceName the internal name of the service (unique in the system) // serviceHandler the handler of the service // serviceStatusHandle the service status handle returned by the SCM // The functions returns SERVICE_RETURN_OK if we could start the service // and SERVICE_RETURN_ERROR otherwise. // ---------------------------------------------------- ServiceReturnCode registerServiceHandler ( char* serviceName, LPHANDLER_FUNCTION serviceHandler, SERVICE_STATUS_HANDLE* serviceStatusHandle ) { ServiceReturnCode returnValue; // register the service to the service control dispatcher (SCM) *serviceStatusHandle = RegisterServiceCtrlHandler ( serviceName, (LPHANDLER_FUNCTION) serviceHandler ); if (serviceStatusHandle == NULL) { returnValue = SERVICE_RETURN_ERROR; } else { returnValue = SERVICE_RETURN_OK; } return returnValue; } // registerServiceHandler // --------------------------------------------------- // Debug utility. If the _eventLog is not NULL and the DEBUG variable is // TRUE send the message to the event log. // If the _eventLog is NULL and the DEBUG variable is TRUE send the message // to the standard output. // --------------------------------------------------- void debug(char* msg) { if (DEBUG == TRUE) { if (_eventLog != NULL) { const char* args[1]; args[0] = msg; // report the event ReportEvent( _eventLog, // event log handle EVENTLOG_INFORMATION_TYPE, // info, warning, error WIN_FACILITY_NAME_OPENDS, // unique category for OPENDS WIN_EVENT_ID_DEBUG, NULL, // no user security identifier 1, // number of args 0, // raw data size (const char**)args, // args NULL // no war data ); } else { fprintf(stdout, "%s\n", msg); } } } // --------------------------------------------------- // Reports a log event of a given type, id and arguments // serviceBinPath the binary associated with the service. // The function returns TRUE if the event could be logged and FALSE otherwise. // --------------------------------------------------- BOOL reportLogEvent(WORD eventType, DWORD eventId, WORD argCount, const char** args) { BOOL reportOk; if (argCount > 0) { // report the event reportOk = ReportEvent( _eventLog, // event log handle eventType, // info, warning, error WIN_FACILITY_NAME_OPENDS, // unique category for OPENDS eventId, NULL, // no user security identifier argCount, // number of args 0, // raw data size args, // args NULL // no raw data ); } else { // report the event reportOk = ReportEvent( _eventLog, // event log handle eventType, // info, warning, error WIN_FACILITY_NAME_OPENDS, // unique category for OPENDS eventId, NULL, // no user security identifier argCount, // number of args 0, // raw data size NULL, // args NULL // no raw data ); } return reportOk; } // --------------------------------------------------------------- // Get a handle to the Service Control Manager (SCM). // accessRights the desired access rights; generic access are: // - GENERIC_READ use to get the list of services // - GENERIC_WRITE use to create & remove a service // - GENERIC_EXECUTE // - GENERIC_ALL // scm the handler to the SCM // The function returns SERVICE_RETURN_OK if we could get the SCM // and SERVICE_RETURN_ERROR otherwise. // --------------------------------------------------------------- ServiceReturnCode openScm(DWORD accessRights, SC_HANDLE *scm) { ServiceReturnCode returnValue; // open Service Control Manager *scm = (SC_HANDLE)OpenSCManager ( NULL, // local machine NULL, // ServicesActive database accessRights // desired rights ); if (scm == NULL) { debug("scm is NULL."); returnValue = SERVICE_RETURN_ERROR; } else { returnValue = SERVICE_RETURN_OK; } return returnValue; } // openScm // --------------------------------------------------- // Creates the registry key to send events based on the name of the service. // serviceName the serviceName. // serviceBinPath the binary associated with the service. // The function returns TRUE if the key could be registered (or was already // registered) and FALSE otherwise. // --------------------------------------------------- BOOLEAN createRegistryKey(char* serviceName) { // true if the server is already registered BOOL alreadyRegistered = FALSE; // false as soon as an error occurs BOOL success = TRUE; // handle to the created/opened key HKEY hkey = NULL; // Create the event source subkey (or open it if it already exists) char subkey [MAX_REGISTRY_KEY]; long result; DWORD nbCategories = 1; // get the full path to the current executable file: is safe to do it // here because we already required it to figure out to get the service // name based on the command to run associated with it. char execName [MAX_PATH]; GetModuleFileName ( NULL, execName, MAX_PATH ); // Check whether the Registry Key is already created, // If so don't create a new one. sprintf (subkey, EVENT_LOG_KEY, serviceName); result = RegOpenKeyEx( HKEY_LOCAL_MACHINE, subkey, 0, KEY_QUERY_VALUE, // to query the values of a registry key. &hkey // OUT ); if (result == ERROR_SUCCESS) { alreadyRegistered = TRUE; success = FALSE; } if (success) { DWORD disposition; result = RegCreateKeyEx( HKEY_LOCAL_MACHINE, // subkey, // 0, // reserved NULL, // key object class REG_OPTION_NON_VOLATILE, // option KEY_WRITE, // desired access NULL, // hkey cannot be inherited &hkey, // OUT &disposition // OUT new key / existing key ); if (result != ERROR_SUCCESS) { debug("RegCreateKeyEx failed."); success = FALSE; } } if (success) { result = RegSetValueEx( hkey, // subkey handle "EventMessageFile", // value name 0, // must be zero REG_EXPAND_SZ, // value type (LPBYTE)execName, // pointer to value data (DWORD) (lstrlen(execName) + 1) * sizeof(TCHAR) // length of value data ); if (result != ERROR_SUCCESS) { success = FALSE; } } // Set the supported event types if (success) { DWORD supportedTypes = EVENTLOG_SUCCESS | EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE; result = RegSetValueEx( hkey, // subkey handle "TypesSupported", // value name 0, // must be zero REG_DWORD, // value type (LPBYTE) &supportedTypes, // pointer to value data sizeof(DWORD) // length of value data ); if (result != ERROR_SUCCESS) { success = FALSE; } } // Set the category message file if (success) { result = RegSetValueEx( hkey, // subkey handle "CategoryMessageFile", // value name 0, // must be zero REG_EXPAND_SZ, // value type (LPBYTE)execName, // pointer to value data (DWORD) (lstrlen(execName) + 1) *sizeof(TCHAR) // length of value data ); if (result != ERROR_SUCCESS) { success = FALSE; } } // Set the number of categories: 1 (OPENDS) if (success) { long result = RegSetValueEx( hkey, // subkey handle "CategoryCount", // value name 0, // must be zero REG_DWORD, // value type (LPBYTE) &nbCategories, // pointer to value data sizeof(DWORD) // length of value data ); if (result != ERROR_SUCCESS) { success = FALSE; } } // close the key before leaving if (hkey != NULL) { RegCloseKey (hkey); } if (alreadyRegistered || success) { return TRUE; } else { debug("Could not create a registry key."); return FALSE; } } // createRegistryKey // --------------------------------------------------- // Removes the registry key to send events based on the name of the service. // serviceName the serviceName. // The function returns TRUE if the key could be unregistered (or it was not // registered) and FALSE otherwise. // --------------------------------------------------- BOOLEAN removeRegistryKey(char* serviceName) { BOOL returnValue; // Create the event source subkey (or open it if it already exists) char subkey [MAX_REGISTRY_KEY]; long result; HKEY hkey = NULL; // Check whether the Registry Key is already created, // If so don't create a new one. sprintf (subkey, EVENT_LOG_KEY, serviceName); result = RegOpenKeyEx( HKEY_LOCAL_MACHINE, subkey, 0, KEY_QUERY_VALUE, // to query the values of a registry key. &hkey // OUT ); if (result != ERROR_SUCCESS) { // Assume that the registry key does not exist. returnValue = TRUE; } else { result = RegDeleteKey (HKEY_LOCAL_MACHINE, subkey); if (result == ERROR_SUCCESS) { returnValue = TRUE; } } return returnValue; } // removeRegistryKey // --------------------------------------------------- // Register the source of event and returns the handle // for the event log. // serviceName the serviceName. // --------------------------------------------------- HANDLE registerEventLog(char *serviceName) { HANDLE eventLog = NULL; // subkey under Eventlog registry key char subkey [MAX_SERVICE_NAME]; sprintf (subkey, serviceName); eventLog = RegisterEventSource( NULL, // local host subkey // subkey under Eventlog registry key ); return eventLog; } // registerEventLog // --------------------------------------------------- // Deregister the source of event. // --------------------------------------------------- void deregisterEventLog() { if (_eventLog != NULL) { DeregisterEventSource(_eventLog); } } // ---------------------------------------------------- // Check if the server is running or not. // The functions returns SERVICE_RETURN_OK if we could determine if // the server is running or not and false otherwise. // ---------------------------------------------------- ServiceReturnCode isServerRunning(BOOL *running) { ServiceReturnCode returnValue; char* relativePath = "\\locks\\server.lock"; char lockFile[MAX_PATH]; if (strlen(relativePath)+strlen(_instanceDir)+1 < MAX_PATH) { int fd; sprintf(lockFile, "%s%s", _instanceDir, relativePath); fd = _open(lockFile, _O_RDWR); if (fd != -1) { returnValue = SERVICE_RETURN_OK; // Test if there is a lock /* Lock some bytes and read them. Then unlock. */ if(_locking(fd, LK_NBLCK, 1) != -1) { *running = FALSE; } else { if (errno == EACCES) { *running = TRUE; } else { *running = FALSE; returnValue = SERVICE_RETURN_ERROR; debug("Unexpected error locking"); } } _close(fd); } else { *running = FALSE; returnValue = SERVICE_RETURN_ERROR; } } else { *running = FALSE; returnValue = SERVICE_RETURN_ERROR; } return returnValue; } // isServerRunning // ---------------------------------------------------- // Start the application using start-ds.bat // The functions returns SERVICE_RETURN_OK if we could start the server // and SERVICE_RETURN_ERROR otherwise. // ---------------------------------------------------- ServiceReturnCode doStartApplication() { ServiceReturnCode returnValue; // init out params char* relativePath = "\\bin\\start-ds.bat"; char command[COMMAND_SIZE]; if (strlen(relativePath)+strlen(_instanceDir)+1 < COMMAND_SIZE) { sprintf(command, "\"%s%s\" --windowsNetStart", _instanceDir, relativePath); // launch the command if (spawn(command, FALSE) != 0) { // Try to see if server is really running int nTries = 10; BOOL running = FALSE; // Wait to be able to launch the java process in order it to free the lock // on the file. Sleep(3000); while ((nTries > 0) && !running) { if (isServerRunning(&running) != SERVICE_RETURN_OK) { break; } if (!running) { Sleep(2000); } } if (running) { returnValue = SERVICE_RETURN_OK; } else { returnValue = SERVICE_RETURN_ERROR; } } else { returnValue = SERVICE_RETURN_ERROR; } } else { returnValue = SERVICE_RETURN_ERROR; } return returnValue; } // doStartApplication // ---------------------------------------------------- // Start the application using stop-ds.bat // The functions returns SERVICE_RETURN_OK if we could stop the server // and SERVICE_RETURN_ERROR otherwise. // ---------------------------------------------------- ServiceReturnCode doStopApplication() { ServiceReturnCode returnValue; // init out params char* relativePath = "\\bin\\stop-ds.bat"; char command[COMMAND_SIZE]; if (strlen(relativePath)+strlen(_instanceDir)+1 < COMMAND_SIZE) { sprintf(command, "\"%s%s\" --windowsNetStop", _instanceDir, relativePath); // launch the command if (spawn(command, FALSE) != 0) { // Try to see if server is really stopped int nTries = 10; BOOL running = TRUE; // Wait to be able to launch the java process in order it to free the lock // on the file. Sleep(3000); while ((nTries > 0) && running) { if (isServerRunning(&running) != SERVICE_RETURN_OK) { break; } if (running) { Sleep(2000); } } if (!running) { returnValue = SERVICE_RETURN_OK; } else { returnValue = SERVICE_RETURN_ERROR; } } else { returnValue = SERVICE_RETURN_ERROR; } } else { returnValue = SERVICE_RETURN_ERROR; } return returnValue; } // doStopApplication // --------------------------------------------------------------- // Build the path to the binary that contains serviceMain. // Actually, the binary is the current executable file... // serviceBinPath the path to the service binary. // instanceDir the instanceDirectory. // The string stored in serviceBinPath looks like // /lib/service.exe start <_instanceDir> // It is up to the caller of the function to allocate // at least MAX_PATH bytes in serviceBinPath. // The function returns SERVICE_RETURN_OK if we could create the binary // path name and SERVICE_RETURN_ERROR otherwise. // --------------------------------------------------------------- ServiceReturnCode createServiceBinPath(char* serviceBinPath) { ServiceReturnCode returnValue = SERVICE_RETURN_OK; // get the full path to the current executable file char fileName [MAX_PATH]; DWORD result = GetModuleFileName ( NULL, // get the path to the current executable file fileName, MAX_PATH ); if (result == 0) { // failed to get the path of the executable file returnValue = SERVICE_RETURN_ERROR; } else { if (result == MAX_PATH) { // buffer was too small, executable name is probably not valid returnValue = SERVICE_RETURN_ERROR; } else { if (strlen(fileName) + strlen(" start ") + strlen(_instanceDir) < MAX_PATH) { sprintf(serviceBinPath, "%s start \"%s\"", fileName, _instanceDir); } else { // buffer was too small, executable name is probably not valid returnValue = SERVICE_RETURN_ERROR; } } } return returnValue; } // createServiceBinPath // ---------------------------------------------------- // Returns the service name that maps the command used to start the // product. All commands are supposed to be unique because they have // the instance dir as parameter. // // The functions returns SERVICE_RETURN_OK if we could create a service name // and SERVICE_RETURN_ERROR otherwise. // The serviceName buffer must be allocated OUTSIDE the function and its // minimum size must be of 256 (the maximum string length of a Service Name). // ---------------------------------------------------- ServiceReturnCode getServiceName(char* cmdToRun, char* serviceName) { // returned status code ServiceReturnCode returnValue = SERVICE_RETURN_OK; // retrieve list of services ServiceDescriptor* serviceList = NULL; int nbServices = -1; returnValue = getServiceList(&serviceList, &nbServices); // go through the list of services and search for the service name // whose display name is [displayName] if (returnValue == SERVICE_RETURN_OK) { int i; returnValue = SERVICE_RETURN_ERROR; if (nbServices > 0) { for (i = 0; i no break here case SERVICE_CONTROL_STOP: { // update service status to STOP_PENDING debug("Stop called"); _serviceCurStatus = SERVICE_STOP_PENDING; checkpoint = CHECKPOINT_FIRST_VALUE; updateServiceStatus ( _serviceCurStatus, NO_ERROR, 0, checkpoint++, TIMEOUT_STOP_SERVICE, _serviceStatusHandle ); // let's try to stop the application whatever may be the status above // (best effort mode) code = doStopApplication(); if (code == SERVICE_RETURN_OK) { WORD argCount = 1; const char *argc[] = {_instanceDir}; _serviceCurStatus = SERVICE_STOPPED; updateServiceStatus ( _serviceCurStatus, NO_ERROR, 0, CHECKPOINT_NO_ONGOING_OPERATION, TIMEOUT_NONE, _serviceStatusHandle ); // again, let's ignore the above status and // notify serviceMain that service has stopped doTerminateService (_terminationEvent); reportLogEvent( EVENTLOG_SUCCESS, WIN_EVENT_ID_SERVER_STOP, argCount, argc ); } else { WORD argCount = 1; const char *argc[] = {_instanceDir}; // We could not stop the server reportLogEvent( EVENTLOG_ERROR_TYPE, WIN_EVENT_ID_SERVER_STOP_FAILED, argCount, argc ); } break; } // Request to pause the service // ---------------------------- case SERVICE_CONTROL_PAUSE: // not supported break; // Request to resume the service // ----------------------------- case SERVICE_CONTROL_CONTINUE: // not supported break; // Interrogate the service status // ------------------------------ case SERVICE_CONTROL_INTERROGATE: code = isServerRunning(&running); if (code != SERVICE_RETURN_OK) { } else if (running) { _serviceCurStatus = SERVICE_RUNNING; } else { _serviceCurStatus = SERVICE_STOPPED; } updateServiceStatus ( _serviceCurStatus, NO_ERROR, 0, CHECKPOINT_NO_ONGOING_OPERATION, TIMEOUT_NONE, _serviceStatusHandle ); break; // Other codes are ignored default: break; } } // serviceHandler // --------------------------------------------------------------- // Retrieve the binaryPathName from the SCM database for a given service. // // scm is the SCM handler (must not be NULL) // serviceName the name of the service. // It is up to the caller of the function to allocate at least MAX_PATH bytes // in binPathName. // The function returns SERVICE_RETURN_OK if we could create the binary // path name and SERVICE_RETURN_ERROR otherwise. // --------------------------------------------------------------- ServiceReturnCode getBinaryPathName(HANDLE scm, char* serviceName, char* binPathName) { ServiceReturnCode returnValue; // pathtname to return char* binPathname = NULL; // handle to the service SC_HANDLE myService = NULL; BOOL getConfigOk = FALSE; DWORD configSize = 4096; LPQUERY_SERVICE_CONFIG serviceConfig = (LPQUERY_SERVICE_CONFIG)malloc(configSize); returnValue = SERVICE_RETURN_ERROR; // if SCM exists then retrieve the config info of the service if (scm != NULL) { myService = OpenService( scm, serviceName, SERVICE_QUERY_CONFIG ); } if (myService != NULL) { while (!getConfigOk) { DWORD bytesNeeded; getConfigOk = QueryServiceConfig( myService, serviceConfig, configSize, &bytesNeeded ); if (!getConfigOk) { DWORD errCode = GetLastError(); if (errCode == ERROR_INSUFFICIENT_BUFFER) { // buffer nor big enough... configSize += bytesNeeded; serviceConfig = (LPQUERY_SERVICE_CONFIG)realloc(serviceConfig, configSize); continue; } else { break; } } else { if (strlen(serviceConfig->lpBinaryPathName) < MAX_PATH) { sprintf(binPathName, serviceConfig->lpBinaryPathName); returnValue = SERVICE_RETURN_OK; } } } } // free buffers if (serviceConfig != NULL) { free(serviceConfig); } return returnValue; } // getBinaryPathName // --------------------------------------------------------------- // Returns the list of NT services being created on the current host. // The function allocates the memory for the returned buffer. // serviceList contains the list of services. // nbServices the number of services returned in the list. // The functions returns SERVICE_RETURN_OK if we could create the service // list and SERVICE_RETURN_ERROR otherwise. // --------------------------------------------------------------- ServiceReturnCode getServiceList(ServiceDescriptor** serviceList, int *nbServices) { ServiceReturnCode returnValue; // open Service Control Manager SC_HANDLE scm = NULL; // get the list of services being configured in the SCM database // 1- first try with a single data structure ENUM_SERVICE_STATUS ENUM_SERVICE_STATUS serviceData; ENUM_SERVICE_STATUS* lpServiceData = &serviceData; DWORD dataSize = sizeof (serviceData); DWORD neededSize; DWORD resumeHandle = 0; unsigned long nbSvc = 0; if (openScm(SC_MANAGER_ENUMERATE_SERVICE, &scm) == SERVICE_RETURN_OK) { BOOL svcStatusOk = EnumServicesStatus( scm, // handle to the SCM SERVICE_WIN32, // for OWN_PROCESS | SHARE_PROCESS SERVICE_STATE_ALL, // all services (runing & stopped) &serviceData, // output buffer dataSize, // output buffer size &neededSize, // sized needed to get the entries &nbSvc, // number of services &resumeHandle // next service entry to read ); if (! svcStatusOk) { DWORD lastError = GetLastError(); if (lastError != ERROR_MORE_DATA) { char msg[200]; sprintf(msg, "getServiceList: generic error. Code [%d]", lastError); // error debug(msg); returnValue = SERVICE_RETURN_ERROR; } else { debug("getServiceList: error More Data."); // buffer is not big enough: try again with a proper size dataSize += neededSize; lpServiceData = (ENUM_SERVICE_STATUS*)calloc( dataSize, sizeof(ENUM_SERVICE_STATUS)); svcStatusOk = EnumServicesStatus( scm, // handle to the SCM SERVICE_WIN32, // for OWN_PROCESS | SHARE_PROCESS SERVICE_STATE_ALL, // all services (running & stopped) lpServiceData, // output buffer dataSize, // output buffer size &neededSize, // sized needed to get the entries &nbSvc, // number of services &resumeHandle // next service entry to read ); if (! svcStatusOk) { DWORD lastError = GetLastError(); if (lastError != ERROR_MORE_DATA) { returnValue = SERVICE_RETURN_ERROR; } else { // Data buffer is not large enough. This case should // never happen as proper buffer size has been // provided!... debug("getServiceList: buffer error"); returnValue = SERVICE_RETURN_ERROR; } } else { returnValue = SERVICE_RETURN_OK; } } } else { returnValue = SERVICE_RETURN_OK; } } else { returnValue = SERVICE_RETURN_ERROR; } // now elaborate the list of service to return... if (returnValue == SERVICE_RETURN_OK) { int i; int aux = (int)nbSvc; ServiceDescriptor* l; ENUM_SERVICE_STATUS* curService = lpServiceData; *nbServices = aux; if (aux > 0) { char binPath[MAX_PATH]; l = (ServiceDescriptor*)calloc(sizeof(ServiceDescriptor), aux); for (i = 0; i < aux; i++) { l[i].serviceName = strdup(curService->lpServiceName); l[i].displayName = strdup(curService->lpDisplayName); if (getBinaryPathName(scm, l[i].serviceName, binPath) == SERVICE_RETURN_OK) { l[i].cmdToRun = strdup(binPath); } curService++; } *serviceList = l; } } // close the handle to the SCM if (scm != NULL) { CloseServiceHandle (scm); // free the result buffer if (lpServiceData != NULL) { free (lpServiceData); } } return returnValue; } // getServiceList // --------------------------------------------------------------- // Function used to know if a given service name is in use or not. // Returns SERVICE_IN_USE if the provided service name is in use. // Returns NOT_SERVICE_IN_USE if the provided service name is not in use. // Returns SERVICE_RETURN_ERROR if the function could not determine if the // service name is in use or not. // --------------------------------------------------------------- ServiceReturnCode serviceNameInUse(char* serviceName) { ServiceReturnCode returnValue; // retrieve list of services ServiceDescriptor* serviceList = NULL; ServiceDescriptor curService; int nbServices = -1; int i; // go through the list of services and search for the service name if (getServiceList(&serviceList, &nbServices) == SERVICE_RETURN_OK) { returnValue = SERVICE_NOT_IN_USE; if (nbServices > 0) { for (i = 0; i < nbServices && (returnValue == SERVICE_NOT_IN_USE); i++) { curService = serviceList[i]; if (curService.serviceName == NULL) { debug("The service name is NULL.\n"); } else { if (strcmp (serviceName, curService.serviceName) == 0) { // found the service! returnValue = SERVICE_IN_USE; } } } free(serviceList); } } else { returnValue = SERVICE_RETURN_ERROR; } return returnValue; } // serviceNameInUse // --------------------------------------------------------------- // Build a service name for OpenDS and make sure // the service name is unique on the system. To achieve this requirement // the service name looks like for the first OpenDS and // -n if there are more than one. // // The functions returns SERVICE_RETURN_OK if we could create a service // name and SERVICE_RETURN_ERROR otherwise. // The serviceName buffer must be allocated OUTSIDE the function and its // minimum size must be of 256 (the maximum string length of a Service Name). // --------------------------------------------------------------- ServiceReturnCode createServiceName(char* serviceName, char* baseName) { ServiceReturnCode returnValue = SERVICE_RETURN_OK; int i = 1; BOOL ended = FALSE; ServiceReturnCode nameInUseResult; while (!ended) { if (i == 1) { sprintf(serviceName, baseName); } else { sprintf(serviceName, "%s-%d", baseName, i); } nameInUseResult = serviceNameInUse(serviceName); if (nameInUseResult == SERVICE_IN_USE) { // this service name is already in use: try another one... i++; } else if (nameInUseResult == SERVICE_NOT_IN_USE) { // this service name is not used so it's a good candidate ended = TRUE; } else { // an error occurred checking the service name returnValue = SERVICE_RETURN_ERROR; ended = TRUE; } } return returnValue; } // createServiceName // --------------------------------------------------------------- // Create a service in the SCM database. Once the service is created, // we can view it with "service list". // displayName is the display name of the service // description is the description of the service // cmdToRun is the command to be run by the SCM upon NET START // // The function returns SERVICE_RETURN_OK if we could create the service and // SERVICE_RETURN_ERROR otherwise. // --------------------------------------------------------------- ServiceReturnCode createServiceInScm(char* displayName, char* description, char* cmdToRun) { ServiceReturnCode returnValue; SC_HANDLE scm = NULL; SC_HANDLE myService = NULL; // local vars // - serviceName is the service name char* serviceName = (char*) calloc(1, MAX_SERVICE_NAME); // elaborate the service name based on the displayName provided returnValue = createServiceName(serviceName, displayName); // create the service if (returnValue == SERVICE_RETURN_OK) { if (openScm(GENERIC_WRITE, &scm) != SERVICE_RETURN_OK) { returnValue = SERVICE_RETURN_ERROR; debug("createServiceInScm: openScm did not work."); } } else { returnValue = SERVICE_RETURN_ERROR; debug("createServiceInScm: createServiceName did not work."); } if (returnValue == SERVICE_RETURN_OK) { myService = CreateService( scm, serviceName, // name of service serviceName, // service name to display SERVICE_ALL_ACCESS, // desired access SERVICE_WIN32_OWN_PROCESS, // service type SERVICE_AUTO_START, // start service during // system startup SERVICE_ERROR_NORMAL, // error control type cmdToRun, // path to service's binary NULL, // no load ordering group NULL, // no tag identifier NULL, // no dependencies NULL, // LocalSystem account NULL // no password ); } if ((returnValue == SERVICE_RETURN_OK) && (myService == NULL)) { DWORD errCode = GetLastError(); if (errCode == ERROR_DUPLICATE_SERVICE_NAME) { returnValue = DUPLICATED_SERVICE_NAME; } else if (errCode == ERROR_SERVICE_EXISTS) { returnValue = SERVICE_ALREADY_EXISTS; } else { returnValue = SERVICE_RETURN_ERROR; } } // add description field if (returnValue == SERVICE_RETURN_OK) { BOOL success; SERVICE_DESCRIPTION serviceDescription; serviceDescription.lpDescription = description; success = ChangeServiceConfig2( myService, SERVICE_CONFIG_DESCRIPTION, (LPVOID) &serviceDescription ); if (!success) { returnValue = SERVICE_RETURN_ERROR; } } // close handles if (myService != NULL) { CloseServiceHandle (myService); } if (scm != NULL) { CloseServiceHandle (scm); } // free names if (serviceName != NULL) { free (serviceName); } return returnValue; } // createServiceInScm // --------------------------------------------------------------- // Remove a service with the name serviceName from SCM. // If the service could be removed returns SERVICE_RETURN_OK. // If the service cannot be removed because still in use by any process // then returned status is SERVICE_MARKED_FOR_DELETION. // If an error occurs returns SERVICE_RETURN_ERROR. // --------------------------------------------------------------- ServiceReturnCode removeServiceFromScm(char* serviceName) { // local vars ServiceReturnCode returnValue = SERVICE_RETURN_OK; SC_HANDLE scm = NULL; SC_HANDLE myService = NULL; SERVICE_STATUS serviceStatus; returnValue = openScm(GENERIC_WRITE, &scm); // open the service if (returnValue == SERVICE_RETURN_OK) { myService = OpenService( scm, serviceName, SERVICE_ALL_ACCESS | DELETE ); if (myService == NULL) { returnValue = SERVICE_RETURN_ERROR; } } if (returnValue == SERVICE_RETURN_OK) { BOOL success = QueryServiceStatus( myService, &serviceStatus ); if (!success) { returnValue = SERVICE_RETURN_ERROR; } } // stop the service if necessary if (returnValue == SERVICE_RETURN_OK) { if (serviceStatus.dwCurrentState != SERVICE_STOPPED) { BOOL success = ControlService ( myService, SERVICE_CONTROL_STOP, &serviceStatus ); if (!success) { DWORD errCode = GetLastError(); if (errCode == ERROR_SERVICE_MARKED_FOR_DELETE) { returnValue = SERVICE_MARKED_FOR_DELETION; } else { returnValue = SERVICE_RETURN_ERROR; } } else { Sleep (500); } } } // remove the service if (returnValue == SERVICE_RETURN_OK) { BOOL success = DeleteService (myService); if (!success) { DWORD errCode = GetLastError(); if (errCode == ERROR_SERVICE_MARKED_FOR_DELETE) { returnValue = SERVICE_MARKED_FOR_DELETION; } else { returnValue = SERVICE_RETURN_ERROR; } } } // close handles if (myService != NULL) { CloseServiceHandle (myService); } if (scm != NULL) { CloseServiceHandle (scm); } return returnValue; } // removeServiceFromScm // --------------------------------------------------------------- // Function called to create a service for the OpenDS instance // where this executable is installed. // The first argument that is passed is the displayName of the service // and the second the description, // // Returns 0 if the service was successfully created. // Returns 1 if the service already existed for this instance. // Returns 2 if the service name we created already exists. // Returns 3 if an error occurred. // --------------------------------------------------------------- int createService(char* displayName, char* description) { int returnCode = 0; char cmdToRun[MAX_PATH]; ServiceReturnCode code; code = createServiceBinPath(cmdToRun); if (code == SERVICE_RETURN_OK) { char serviceName[MAX_SERVICE_NAME]; code = getServiceName(cmdToRun, serviceName); if (code == SERVICE_RETURN_OK) { // There is a valid serviceName for the command to run, so // OpenDS is registered as a service. code = SERVICE_ALREADY_EXISTS; createRegistryKey(serviceName); debug("createService: service already exists for this instance."); } else { // We could not find a serviceName for the command to run, so // try to create the service. code = createServiceInScm(displayName, description, cmdToRun); if (code == SERVICE_RETURN_OK) { code = getServiceName(cmdToRun, serviceName); if (code == SERVICE_RETURN_OK) { createRegistryKey(serviceName); } } } } switch (code) { case SERVICE_RETURN_OK: returnCode = 0; break; case SERVICE_ALREADY_EXISTS: returnCode = 1; break; case DUPLICATED_SERVICE_NAME: returnCode = 2; break; default: returnCode = 3; } return returnCode; } // createService // --------------------------------------------------------------- // Function called to know if the OpenDS instance where this // executable is installed is running as a service or not. // Returns 0 if the instance is running as a service and print the // serviceName in the standard output. // Returns 1 if the instance is not running as a service. // Returns 2 if an error occurred or we cannot determine if Open DS // is running as a service or not. // --------------------------------------------------------------- int serviceState() { int returnCode = 0; char cmdToRun[MAX_PATH]; char serviceName[MAX_SERVICE_NAME]; ServiceReturnCode code; code = createServiceBinPath(cmdToRun); if (code == SERVICE_RETURN_OK) { code = getServiceName(cmdToRun, serviceName); if (code == SERVICE_RETURN_OK) { // There is a valid serviceName for the command to run, so // OpenDS is registered as a service. fprintf(stdout, serviceName); returnCode = 0; } else { returnCode = 1; } } else { returnCode = 2; } return returnCode; } // serviceState // --------------------------------------------------------------- // Function called to remove the service associated with a given // service name. // Returns 0 if the service was successfully removed. // Returns 1 if the service does not exist. // Returns 2 if the service was marked for deletion but is still in // use. // Returns 3 if an error occurred. // --------------------------------------------------------------- int removeServiceWithServiceName(char *serviceName) { int returnCode = 0; ServiceReturnCode code = serviceNameInUse(serviceName); if (code != SERVICE_IN_USE) { returnCode = 1; } else { code = removeServiceFromScm(serviceName); switch (code) { case SERVICE_RETURN_OK: removeRegistryKey(serviceName); returnCode = 0; break; case SERVICE_MARKED_FOR_DELETION: removeRegistryKey(serviceName); returnCode = 2; break; default: returnCode = 3; } } return returnCode; } // removeServiceWithServiceName // --------------------------------------------------------------- // Function called to remove the service for the OpenDS instance // where this executable is installed. // Returns 0 if the service was successfully removed. // Returns 1 if the service does not exist. // Returns 2 if the service was marked for deletion but is still in // use. // Returns 3 if an error occurred. // --------------------------------------------------------------- int removeService() { int returnCode = 0; char cmdToRun[MAX_PATH]; char serviceName[MAX_SERVICE_NAME]; ServiceReturnCode code; code = createServiceBinPath(cmdToRun); if (code == SERVICE_RETURN_OK) { code = getServiceName(cmdToRun, serviceName); if (code == SERVICE_RETURN_OK) { returnCode = removeServiceWithServiceName(serviceName); } else { returnCode = 1; } } else { returnCode = 2; } return returnCode; } // removeService // --------------------------------------------------------------- // Function called to start the service where this executable is installed. // Returns 0 if the service runs. // Returns 1 if an error occurred. // --------------------------------------------------------------- int startService() { int returnCode; char serviceName[MAX_SERVICE_NAME]; char cmdToRun[MAX_PATH]; ServiceReturnCode code; code = createServiceBinPath(cmdToRun); if (code == SERVICE_RETURN_OK) { code = getServiceName(cmdToRun, serviceName); } if (code == SERVICE_RETURN_OK) { BOOL success; SERVICE_TABLE_ENTRY serviceTable[] = { {serviceName, (LPSERVICE_MAIN_FUNCTION) serviceMain}, {NULL, NULL} }; _eventLog = registerEventLog(serviceName); // register the service to the SCM. The function will return once the // service is terminated. success = StartServiceCtrlDispatcher(serviceTable); if (!success) { WORD argCount = 2; DWORD lastError = GetLastError(); const char *argc[2]; argc[0] = _instanceDir; if (lastError == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) { argc[1] = "startService: StartServiceCtrlDispatcher did not work: \ ERROR_FAILED_SERVICE_CONTROLLER_CONNECT."; } else if (lastError == ERROR_INVALID_DATA) { argc[1] = "startService: StartServiceCtrlDispatcher did not work: \ ERROR_INVALID_DATA."; } else if (lastError == ERROR_SERVICE_ALREADY_RUNNING) { argc[1] = "startService: StartServiceCtrlDispatcher did not work: \ ERROR_SERVICE_ALREADY_RUNNING."; } else { argc[1] = "startService: StartServiceCtrlDispatcher did not work."; } code = SERVICE_RETURN_ERROR; reportLogEvent( EVENTLOG_ERROR_TYPE, WIN_EVENT_ID_SERVER_START_FAILED, argCount, argc ); } deregisterEventLog(); } else { debug("startService: Could not get service name."); } if (code == SERVICE_RETURN_OK) { returnCode = 0; } else { returnCode = 1; } return returnCode; } // startService // --------------------------------------------------------------- // Function called to know if the --debug option was passed // when calling this executable or not. The DEBUG variable is // updated accordingly. // --------------------------------------------------------------- void updateDebugFlag(char* argv[], int argc, int startIndex) { int i; DEBUG = FALSE; for (i=startIndex; (i