service.c revision 96f84a891cde1541b06ccea6fc9865d7e6d05840
NULL,
// no user security identifier // --------------------------------------------------------------- // 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 // scm the handler to the SCM // The function returns SERVICE_RETURN_OK if we could get the SCM // and SERVICE_RETURN_ERROR otherwise. // --------------------------------------------------------------- // open Service Control Manager NULL,
// ServicesActive database debug(
"Successfully opened the Service Control Manager.");
// --------------------------------------------------- // 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. // --------------------------------------------------- // true if the server is already registered // false as soon as an error occurs // Create the event source subkey (or open it if it already exists) // 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. // Check whether the Registry Key is already created, // If so don't create a new one. NULL,
// key object class NULL,
// hkey cannot be inherited "EventMessageFile",
// value name *
sizeof(
TCHAR)
// length of value data // Set the supported event types "TypesSupported",
// value name sizeof(
DWORD)
// length of value data // Set the category message file "CategoryMessageFile",
// value name *
sizeof(
TCHAR)
// length of value data // Set the number of categories: 1 (OPENDS) "CategoryCount",
// value name sizeof(
DWORD)
// length of value data // close the key before leaving // --------------------------------------------------- // 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. // --------------------------------------------------- // Create the event source subkey (or open it if it already exists) // Check whether the Registry Key is already created, // If so don't create a new one. debug(
"The registry key for service '%s' does not exist, so we do not need to remove it.",
serviceName);
// Assume that the registry key does not exist. // --------------------------------------------------- // Register the source of event and returns the handle // serviceName the serviceName. // --------------------------------------------------- // subkey under Eventlog registry key subkey // subkey under Eventlog registry key // --------------------------------------------------- // Deregister the source of event. // --------------------------------------------------- debug(
"Deregistering the Event Log.");
// ---------------------------------------------------- // 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. // ---------------------------------------------------- debug(
"Determining if the server is running.");
debug(
"When determining whether the server is running, the lock file name is '%s'.",
lockFile);
// Test if there is a lock /* Lock some bytes and read them. Then unlock. */ debug(
"Able to lock '%s', so the server is not running.",
lockFile);
debug(
"Unable to lock '%s', so the server is running.",
lockFile);
debug(
"Could not open lock file '%s', which means the server is not running.",
lockFile);
debug(
"Lock file path is too long.");
// ---------------------------------------------------- // Start the application using start-ds.bat // The functions returns SERVICE_RETURN_OK if we could start the server // and SERVICE_RETURN_ERROR otherwise. // ---------------------------------------------------- debug(
"doStartApplication called.");
debug(
"doStartApplication attempting to spawn '%s'",
command);
// Try to see if server is really running debug(
"doStartApplication: the spawn of the process worked. Command: '%s'",
command);
// Wait to be able to launch the java process in order it to free the lock debug(
"Sleeping for 3 seconds to allow the process to free the lock.");
debug(
"Sleeping for 2 seconds to allow the process to free the lock. %d tries remaining.",
nTries);
debug(
"doStartApplication: server running.");
debug(
"doStartApplication: server not running.");
debug(
"doStartApplication: spawn failed. Sent command: '%s'",
command);
debug(
"doStartApplication: the command path name is too long.");
// ---------------------------------------------------- // Start the application using stop-ds.bat // The functions returns SERVICE_RETURN_OK if we could stop the server // and SERVICE_RETURN_ERROR otherwise. // ---------------------------------------------------- // Try to see if server is really stopped debug(
"doStopApplication: the spawn of the process worked.");
// Wait to be able to launch the java process in order it to free the lock debug(
"doStopApplication: server stopped.");
debug(
"doStopApplication: server NOT stopped.");
debug(
"doStopApplication: spawn failed. Sent command: %s",
command);
debug(
"doStopApplication: the command path name is too long.");
// --------------------------------------------------------------- // 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 // It is up to the caller of the function to allocate // at least COMMAND_SIZE bytes in serviceBinPath. // The function returns SERVICE_RETURN_OK if we could create the binary // path name and SERVICE_RETURN_ERROR otherwise. // --------------------------------------------------------------- // get the full path to the current executable file NULL,
// get the path to the current executable file // failed to get the path of the executable file debug(
"Could not get the path of the executable file.");
debug(
"When determining the service bin path, the module file name is '%s'.",
fileName);
// buffer was too small, executable name is probably not valid debug(
"The name of the module file is too long.");
char *
msg =
"The name of the resulting windows service command is too long.\n";
// buffer was too small, executable name is probably not valid }
// 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 get 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). // ---------------------------------------------------- // retrieve list of services debug(
"Attempting to get the service name assuming command to run is '%s'.",
cmdToRun);
// go through the list of services and search for the service name // whose display name is [displayName] // This function assumes that there are at least // MAX_SERVICE_NAME (256) characters reserved in debug(
"getServiceName: could not get service list.");
// ---------------------------------------------------- // Set the current status for the service. // statusToSet current service status to set // win32ExitCode determine which exit code to use // serviceExitCode service code to return in case win32ExitCode says so // checkPoint incremental value use to report progress during a lenghty // operation (start, stop...). // waitHint estimated time required for a pending operation (in ms); if // the service has not updated the checkpoint or change the state then // the service controler thinks the service should be stopped! // serviceStatusHandle the handle used to set the service status // The functions returns SERVICE_RETURN_OK if we could start the service // and SERVICE_RETURN_ERROR otherwise. // ---------------------------------------------------- // elaborate service type: // SERVICE_WIN32_OWN_PROCESS means this is not a driver and there is // only one service in the process // elaborate the commands supported by the service: // - STOP customer has performed a stop-ds (or NET STOP) // - SHUTDOWN the system is rebooting // - INTERROGATE service controler can interogate the service // Note: INTERROGATE *must* be supported by the service handler debug(
"Updating the service status. statusToSet=%d win32ExitCode=%d serviceExitCode=%d checkPoint=%d waitHint=%d",
debug(
"Service start pending.");
// do not accept any command when the service is starting up... // fill in the status structure // set the service status // ---------------------------------------------------- // This function is the "main" of the service. It has been registered // to the SCM by the main right after the service has been started through // The job of the serviceMain is // 1- to register a handler to manage the commands STOP, PAUSE, CONTINUE, // SHUTDOWN and INTERROGATE sent by the SCM // 2- to start the main application using "start-ds" // The serviceMain will return only when the service is terminated. // ---------------------------------------------------- // a checkpoint value indicate the progress of an operation debug(
"serviceMain called.");
debug(
"serviceMain: could not get service name.");
debug(
"serviceMain: failed to create service bin path.");
// first register the service control handler to the SCM debug(
"serviceMain: failed to register service handler.");
// update the service status to START_PENDING // create an event to signal the application termination NULL,
// handle is not inherited by the child process TRUE,
// event has to be reset manually after a signal FALSE,
// initial state is "non signaled" NULL // the event has no name // update the service status to START_PENDING // if all is ok wait for the application to die before we leave debug(
"Waiting indefinitely for the application to exit.");
debug(
"The application has exited.");
// update the service status to STOPPED if it's not already done debug(
"serviceMain() returning.");
// ---------------------------------------------------- // Notify the serviceMain that service is now terminated. // terminationEvent the event upon which serviceMain is blocked // ---------------------------------------------------- debug(
"Faking a service termination so serviceMain can return.");
// ---------------------------------------------------- // This function is the handler of the service. It is processing the // commands send by the SCM. Commands can be: STOP, PAUSE, CONTINUE, // SHUTDOWN and INTERROGATE. // controlCode the code of the command // ---------------------------------------------------- // If system is shuting down then stop the service debug(
"serviceHandler: shutdown");
// update service status to STOP_PENDING debug(
"serviceHandler: stop");
// let's try to stop the application whatever may be the status above // again, let's ignore the above status and // notify serviceMain that service has stopped debug(
"The server could not be stopped.");
// We could not stop the server // Request to pause the service // ---------------------------- debug(
"serviceHandler: pause.");
// Request to resume the service // ----------------------------- debug(
"serviceHandler: continue.");
// Interrogate the service status // ------------------------------ debug(
"serviceHandler: interrogate.");
debug(
"serviceHandler: error interrogating.");
debug(
"serviceHandler: service running.");
debug(
"serviceHandler: service stopped.");
// Other codes are ignored // --------------------------------------------------------------- // 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 COMMAND_SIZE bytes // The function returns SERVICE_RETURN_OK if we could create the binary // path name and SERVICE_RETURN_ERROR otherwise. // --------------------------------------------------------------- // if SCM exists then retrieve the config info of the service // buffer not big enough... debug(
"getBinaryPath: error calling QueryServiceConfig. Code [%d]",
errCode);
// --------------------------------------------------------------- // 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. // --------------------------------------------------------------- // open Service Control Manager // get the list of services being configured in the SCM database // 1- first try with a single data structure ENUM_SERVICE_STATUS scm,
// handle to the SCM &
nbSvc,
// number of services // buffer is not big enough: try again with a proper size scm,
// handle to the SCM &
nbSvc,
// number of services debug(
"getServiceList: second try generic error. Code [%d]",
lastError);
// Data buffer is not large enough. This case should // never happen as proper buffer size has been debug(
"getServiceList: buffer error");
debug(
"getServiceList: error opening scm.");
// now elaborate the list of service to return... for (i = 0; i <
aux; i++)
// close the handle to the SCM // free the result buffer // --------------------------------------------------------------- // 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. // --------------------------------------------------------------- // retrieve list of services // go through the list of services and search for the service name debug(
"The service name is NULL.\n");
debugError(
"Could not determine if the service name '%s' is in use because listing the services failed.",
serviceName);
// --------------------------------------------------------------- // 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 <baseName> for the first OpenDS and // <baseName>-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). // --------------------------------------------------------------- // this service name is already in use: try another one... // this service name is not used so it's a good candidate // an error occurred checking the service name debug(
"createServiceName returning serviceName='%s' and returnValue=%d",
// --------------------------------------------------------------- // 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. // --------------------------------------------------------------- // - serviceName is the service name // elaborate the service name based on the displayName provided debug(
"createServiceInScm: openScm did not work.");
debug(
"createServiceInScm: createServiceName did not work.");
NULL,
// no load ordering group NULL,
// no tag identifier NULL,
// LocalSystem account // --------------------------------------------------------------- // 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. // --------------------------------------------------------------- // stop the service if necessary }
// 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. // --------------------------------------------------------------- // There is a valid serviceName for the command to run, so // OpenDS is registered as a service. // We could not find a serviceName for the command to run, so // try to create the service. debug(
"Could not get a service name for command to run.");
debug(
"createService could not create bin path.");
debug(
"Service successfully created.");
debug(
"Service already exists.");
debug(
"Duplicated service name.");
debug(
"Unexpected error creating service.");
// --------------------------------------------------------------- // 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. // --------------------------------------------------------------- debug(
"Getting service state.");
// There is a valid serviceName for the command to run, so // OpenDS is registered as a service. debug(
"An error occurred getting the service status.");
// --------------------------------------------------------------- // Function called to remove the service associated with a given // 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 // Returns 3 if an error occurred. // --------------------------------------------------------------- debug(
"Removing service.");
debug(
"Service does not exist.");
debug(
"Service successfully removed.");
debug(
"Service marked for deletion.");
debug(
"Unexpected error removing service.");
}
// 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 // Returns 3 if an error occurred. // --------------------------------------------------------------- debug(
"removeService()");
// --------------------------------------------------------------- // Function called to start the service where this executable is installed. // Returns 0 if the service runs. // Returns 1 if an error occurred. // --------------------------------------------------------------- // register the service to the SCM. The function will return once the // service is terminated. "startService: StartServiceCtrlDispatcher did not work: \ ERROR_FAILED_SERVICE_CONTROLLER_CONNECT.";
"startService: StartServiceCtrlDispatcher did not work: \ "startService: StartServiceCtrlDispatcher did not work: \ ERROR_SERVICE_ALREADY_RUNNING.";
"startService: StartServiceCtrlDispatcher did not work.";
debug(
"startService: Could not get service name.");
for (i = 0; i <
argc; i++) {
"Subcommand required: create, state, remove, start or cleanup.\n");
"Subcommand create requires instance dir, service name and description.\n");
"Subcommand state requires instance dir.\n");
"Subcommand remove requires instance dir.\n");
"Subcommand start requires instance dir.\n");
"Subcommand isrunning requires instance dir.\n");
"Subcommand cleanup requires service name.\n");