4 SERVICE_STATUS service_status;
\r
5 SERVICE_STATUS_HANDLE service_handle;
\r
6 HANDLE process_handle;
\r
9 static char service_name[SERVICE_NAME_LENGTH];
\r
10 char exe[EXE_LENGTH];
\r
11 char flags[CMD_LENGTH];
\r
14 unsigned long throttle_delay;
\r
15 HANDLE throttle_timer;
\r
16 LARGE_INTEGER throttle_duetime;
\r
18 static enum { NSSM_EXIT_RESTART, NSSM_EXIT_IGNORE, NSSM_EXIT_REALLY, NSSM_EXIT_UNCLEAN } exit_actions;
\r
19 static const char *exit_action_strings[] = { "Restart", "Ignore", "Exit", "Suicide", 0 };
\r
21 static unsigned long throttle;
\r
23 static inline int throttle_milliseconds() {
\r
24 /* pow() operates on doubles. */
\r
25 int ret = 1; for (unsigned long i = 1; i < throttle; i++) ret *= 2;
\r
29 /* Connect to the service manager */
\r
30 SC_HANDLE open_service_manager() {
\r
31 SC_HANDLE ret = OpenSCManager(0, SERVICES_ACTIVE_DATABASE, SC_MANAGER_ALL_ACCESS);
\r
33 if (is_admin) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENSCMANAGER_FAILED, 0);
\r
40 /* About to install the service */
\r
41 int pre_install_service(int argc, char **argv) {
\r
42 /* Show the dialogue box if we didn't give the service name and path */
\r
43 if (argc < 2) return nssm_gui(IDD_INSTALL, argv[0]);
\r
45 /* Arguments are optional */
\r
47 size_t flagslen = 0;
\r
50 for (i = 2; i < argc; i++) flagslen += strlen(argv[i]) + 1;
\r
51 if (! flagslen) flagslen = 1;
\r
53 flags = (char *) HeapAlloc(GetProcessHeap(), 0, flagslen);
\r
55 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "flags", "pre_install_service()", 0);
\r
58 ZeroMemory(flags, flagslen);
\r
61 This probably isn't UTF8-safe and should use std::string or something
\r
62 but it's been broken for the best part of a decade and due for a rewrite
\r
63 anyway so it'll do as a quick-'n'-dirty fix. Note that we don't free
\r
64 the flags buffer but as the program exits that isn't a big problem.
\r
66 for (i = 2; i < argc; i++) {
\r
67 size_t len = strlen(argv[i]);
\r
68 memmove(flags + s, argv[i], len);
\r
70 if (i < argc - 1) flags[s++] = ' ';
\r
73 return install_service(argv[0], argv[1], flags);
\r
76 /* About to remove the service */
\r
77 int pre_remove_service(int argc, char **argv) {
\r
78 /* Show dialogue box if we didn't pass service name and "confirm" */
\r
79 if (argc < 2) return nssm_gui(IDD_REMOVE, argv[0]);
\r
80 if (str_equiv(argv[1], "confirm")) return remove_service(argv[0]);
\r
81 print_message(stderr, NSSM_MESSAGE_PRE_REMOVE_SERVICE);
\r
85 /* Install the service */
\r
86 int install_service(char *name, char *exe, char *flags) {
\r
87 /* Open service manager */
\r
88 SC_HANDLE services = open_service_manager();
\r
90 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
94 /* Get path of this program */
\r
95 char path[MAX_PATH];
\r
96 GetModuleFileName(0, path, MAX_PATH);
\r
98 /* Construct command */
\r
99 char command[CMD_LENGTH];
\r
100 size_t pathlen = strlen(path);
\r
101 if (pathlen + 1 >= VALUE_LENGTH) {
\r
102 print_message(stderr, NSSM_MESSAGE_PATH_TOO_LONG, NSSM);
\r
105 if (_snprintf(command, sizeof(command), "\"%s\"", path) < 0) {
\r
106 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY_FOR_IMAGEPATH);
\r
110 /* Work out directory name */
\r
111 size_t len = strlen(exe);
\r
113 for (i = len; i && exe[i] != '\\' && exe[i] != '/'; i--);
\r
114 char dir[MAX_PATH];
\r
115 memmove(dir, exe, i);
\r
118 /* Create the service */
\r
119 SC_HANDLE service = CreateService(services, name, name, SC_MANAGER_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, command, 0, 0, 0, 0, 0);
\r
121 print_message(stderr, NSSM_MESSAGE_CREATESERVICE_FAILED);
\r
122 CloseServiceHandle(services);
\r
126 /* Now we need to put the parameters into the registry */
\r
127 if (create_parameters(name, exe, flags, dir)) {
\r
128 print_message(stderr, NSSM_MESSAGE_CREATE_PARAMETERS_FAILED);
\r
129 DeleteService(service);
\r
130 CloseServiceHandle(services);
\r
134 set_service_recovery(service, name);
\r
137 CloseServiceHandle(service);
\r
138 CloseServiceHandle(services);
\r
140 print_message(stdout, NSSM_MESSAGE_SERVICE_INSTALLED, name);
\r
144 /* Remove the service */
\r
145 int remove_service(char *name) {
\r
146 /* Open service manager */
\r
147 SC_HANDLE services = open_service_manager();
\r
149 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
153 /* Try to open the service */
\r
154 SC_HANDLE service = OpenService(services, name, SC_MANAGER_ALL_ACCESS);
\r
156 print_message(stderr, NSSM_MESSAGE_OPENSERVICE_FAILED);
\r
157 CloseServiceHandle(services);
\r
161 /* Try to delete the service */
\r
162 if (! DeleteService(service)) {
\r
163 print_message(stderr, NSSM_MESSAGE_DELETESERVICE_FAILED);
\r
164 CloseServiceHandle(service);
\r
165 CloseServiceHandle(services);
\r
170 CloseServiceHandle(service);
\r
171 CloseServiceHandle(services);
\r
173 print_message(stdout, NSSM_MESSAGE_SERVICE_REMOVED, name);
\r
177 /* Service initialisation */
\r
178 void WINAPI service_main(unsigned long argc, char **argv) {
\r
179 if (_snprintf(service_name, sizeof(service_name), "%s", argv[0]) < 0) {
\r
180 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "service_name", "service_main()", 0);
\r
184 /* Initialise status */
\r
185 ZeroMemory(&service_status, sizeof(service_status));
\r
186 service_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;
\r
187 service_status.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE;
\r
188 service_status.dwWin32ExitCode = NO_ERROR;
\r
189 service_status.dwServiceSpecificExitCode = 0;
\r
190 service_status.dwCheckPoint = 0;
\r
191 service_status.dwWaitHint = NSSM_WAITHINT_MARGIN;
\r
193 /* Signal we AREN'T running the server */
\r
194 process_handle = 0;
\r
197 /* Register control handler */
\r
198 service_handle = RegisterServiceCtrlHandlerEx(NSSM, service_control_handler, 0);
\r
199 if (! service_handle) {
\r
200 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_REGISTERSERVICECTRLHANDER_FAILED, error_string(GetLastError()), 0);
\r
204 log_service_control(service_name, 0, true);
\r
206 service_status.dwCurrentState = SERVICE_START_PENDING;
\r
207 service_status.dwWaitHint = throttle_delay + NSSM_WAITHINT_MARGIN;
\r
208 SetServiceStatus(service_handle, &service_status);
\r
211 /* Try to create the exit action parameters; we don't care if it fails */
\r
212 create_exit_action(argv[0], exit_action_strings[0]);
\r
214 set_service_recovery(0, service_name);
\r
217 /* Used for signalling a resume if the service pauses when throttled. */
\r
218 throttle_timer = CreateWaitableTimer(0, 1, 0);
\r
219 if (! throttle_timer) {
\r
220 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_CREATEWAITABLETIMER_FAILED, service_name, error_string(GetLastError()), 0);
\r
226 /* Make sure service recovery actions are taken where necessary */
\r
227 void set_service_recovery(SC_HANDLE service, char *service_name) {
\r
228 SC_HANDLE services = 0;
\r
231 services = open_service_manager();
\r
232 if (! services) return;
\r
234 service = OpenService(services, service_name, SC_MANAGER_ALL_ACCESS);
\r
235 if (! service) return;
\r
238 SERVICE_FAILURE_ACTIONS_FLAG flag;
\r
239 ZeroMemory(&flag, sizeof(flag));
\r
240 flag.fFailureActionsOnNonCrashFailures = true;
\r
242 /* This functionality was added in Vista so the call may fail */
\r
243 if (! ChangeServiceConfig2(service, SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, &flag)) {
\r
244 unsigned long error = GetLastError();
\r
245 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
\r
246 if (error != ERROR_INVALID_LEVEL) {
\r
247 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CHANGESERVICECONFIG2_FAILED, service_name, error_string(error), 0);
\r
252 CloseServiceHandle(service);
\r
253 CloseServiceHandle(services);
\r
257 int monitor_service() {
\r
258 /* Set service status to started */
\r
259 int ret = start_service();
\r
262 _snprintf(code, sizeof(code), "%d", ret);
\r
263 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_START_SERVICE_FAILED, exe, service_name, ret, 0);
\r
266 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_STARTED_SERVICE, exe, flags, service_name, dir, 0);
\r
268 /* Monitor service service */
\r
269 if (! RegisterWaitForSingleObject(&wait_handle, process_handle, end_service, (void *) pid, INFINITE, WT_EXECUTEONLYONCE | WT_EXECUTELONGFUNCTION)) {
\r
270 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_REGISTERWAITFORSINGLEOBJECT_FAILED, service_name, exe, error_string(GetLastError()), 0);
\r
276 char *service_control_text(unsigned long control) {
\r
278 /* HACK: there is no SERVICE_CONTROL_START constant */
\r
279 case 0: return "START";
\r
280 case SERVICE_CONTROL_STOP: return "STOP";
\r
281 case SERVICE_CONTROL_SHUTDOWN: return "SHUTDOWN";
\r
282 case SERVICE_CONTROL_PAUSE: return "PAUSE";
\r
283 case SERVICE_CONTROL_CONTINUE: return "CONTINUE";
\r
284 case SERVICE_CONTROL_INTERROGATE: return "INTERROGATE";
\r
289 void log_service_control(char *service_name, unsigned long control, bool handled) {
\r
290 char *text = service_control_text(control);
\r
291 unsigned long event;
\r
294 /* "0x" + 8 x hex + NULL */
\r
295 text = (char *) HeapAlloc(GetProcessHeap(), 0, 11);
\r
297 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "control code", "log_service_control", 0);
\r
300 if (_snprintf(text, 11, "0x%08x", control) < 0) {
\r
301 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "control code", "log_service_control", 0);
\r
302 HeapFree(GetProcessHeap(), 0, text);
\r
306 event = NSSM_EVENT_SERVICE_CONTROL_UNKNOWN;
\r
308 else if (handled) event = NSSM_EVENT_SERVICE_CONTROL_HANDLED;
\r
309 else event = NSSM_EVENT_SERVICE_CONTROL_NOT_HANDLED;
\r
311 log_event(EVENTLOG_INFORMATION_TYPE, event, service_name, text, 0);
\r
313 if (event == NSSM_EVENT_SERVICE_CONTROL_UNKNOWN) {
\r
314 HeapFree(GetProcessHeap(), 0, text);
\r
318 /* Service control handler */
\r
319 unsigned long WINAPI service_control_handler(unsigned long control, unsigned long event, void *data, void *context) {
\r
321 case SERVICE_CONTROL_SHUTDOWN:
\r
322 case SERVICE_CONTROL_STOP:
\r
323 log_service_control(service_name, control, true);
\r
324 stop_service(0, true, true);
\r
327 case SERVICE_CONTROL_CONTINUE:
\r
328 log_service_control(service_name, control, true);
\r
329 if (! throttle_timer) return ERROR_CALL_NOT_IMPLEMENTED;
\r
331 ZeroMemory(&throttle_duetime, sizeof(throttle_duetime));
\r
332 SetWaitableTimer(throttle_timer, &throttle_duetime, 0, 0, 0, 0);
\r
333 service_status.dwCurrentState = SERVICE_CONTINUE_PENDING;
\r
334 service_status.dwWaitHint = throttle_milliseconds() + NSSM_WAITHINT_MARGIN;
\r
335 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESET_THROTTLE, service_name, 0);
\r
336 SetServiceStatus(service_handle, &service_status);
\r
339 case SERVICE_CONTROL_PAUSE:
\r
341 We don't accept pause messages but it isn't possible to register
\r
342 only for continue messages so we have to handle this case.
\r
344 log_service_control(service_name, control, false);
\r
345 return ERROR_CALL_NOT_IMPLEMENTED;
\r
348 /* Unknown control */
\r
349 log_service_control(service_name, control, false);
\r
350 return ERROR_CALL_NOT_IMPLEMENTED;
\r
353 /* Start the service */
\r
354 int start_service() {
\r
357 if (process_handle) return 0;
\r
359 /* Allocate a STARTUPINFO structure for a new process */
\r
361 ZeroMemory(&si, sizeof(si));
\r
362 si.cb = sizeof(si);
\r
364 /* Allocate a PROCESSINFO structure for the process */
\r
365 PROCESS_INFORMATION pi;
\r
366 ZeroMemory(&pi, sizeof(pi));
\r
368 /* Get startup parameters */
\r
370 int ret = get_parameters(service_name, exe, sizeof(exe), flags, sizeof(flags), dir, sizeof(dir), &env, &throttle_delay);
\r
372 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_PARAMETERS_FAILED, service_name, 0);
\r
373 return stop_service(2, true, true);
\r
376 /* Launch executable with arguments */
\r
377 char cmd[CMD_LENGTH];
\r
378 if (_snprintf(cmd, sizeof(cmd), "\"%s\" %s", exe, flags) < 0) {
\r
379 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "command line", "start_service", 0);
\r
380 return stop_service(2, true, true);
\r
383 throttle_restart();
\r
385 if (! CreateProcess(0, cmd, 0, 0, false, 0, env, dir, &si, &pi)) {
\r
386 unsigned long error = GetLastError();
\r
387 if (error == ERROR_INVALID_PARAMETER && env) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED_INVALID_ENVIRONMENT, service_name, exe, NSSM_REG_ENV, 0);
\r
388 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service_name, exe, error_string(error), 0);
\r
389 return stop_service(3, true, true);
\r
391 process_handle = pi.hProcess;
\r
392 pid = pi.dwProcessId;
\r
394 /* Signal successful start */
\r
395 service_status.dwCurrentState = SERVICE_RUNNING;
\r
396 SetServiceStatus(service_handle, &service_status);
\r
398 /* Wait for a clean startup. */
\r
399 if (WaitForSingleObject(process_handle, throttle_delay) == WAIT_TIMEOUT) throttle = 0;
\r
404 /* Stop the service */
\r
405 int stop_service(unsigned long exitcode, bool graceful, bool default_action) {
\r
406 if (default_action && ! exitcode && ! graceful) {
\r
407 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_GRACEFUL_SUICIDE, service_name, exe, exit_action_strings[NSSM_EXIT_UNCLEAN], exit_action_strings[NSSM_EXIT_UNCLEAN], exit_action_strings[NSSM_EXIT_UNCLEAN], exit_action_strings[NSSM_EXIT_REALLY] ,0);
\r
411 /* Signal we are stopping */
\r
413 service_status.dwCurrentState = SERVICE_STOP_PENDING;
\r
414 service_status.dwWaitHint = NSSM_KILL_WINDOW_GRACE_PERIOD + NSSM_KILL_THREADS_GRACE_PERIOD + NSSM_WAITHINT_MARGIN;
\r
415 SetServiceStatus(service_handle, &service_status);
\r
418 /* Nothing to do if server isn't running */
\r
420 /* Shut down server */
\r
421 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_TERMINATEPROCESS, service_name, exe, 0);
\r
422 kill_process(service_name, process_handle, pid, 0);
\r
423 process_handle = 0;
\r
425 else log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_PROCESS_ALREADY_STOPPED, service_name, exe, 0);
\r
427 end_service((void *) pid, true);
\r
429 /* Signal we stopped */
\r
431 service_status.dwCurrentState = SERVICE_STOPPED;
\r
433 service_status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
\r
434 service_status.dwServiceSpecificExitCode = exitcode;
\r
437 service_status.dwWin32ExitCode = NO_ERROR;
\r
438 service_status.dwServiceSpecificExitCode = 0;
\r
440 SetServiceStatus(service_handle, &service_status);
\r
446 /* Callback function triggered when the server exits */
\r
447 void CALLBACK end_service(void *arg, unsigned char why) {
\r
448 if (stopping) return;
\r
452 pid = (unsigned long) arg;
\r
454 /* Check exit code */
\r
455 unsigned long exitcode = 0;
\r
457 GetExitCodeProcess(process_handle, &exitcode);
\r
460 Log that the service ended BEFORE logging about killing the process
\r
461 tree. See below for the possible values of the why argument.
\r
464 _snprintf(code, sizeof(code), "%d", exitcode);
\r
465 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ENDED_SERVICE, exe, service_name, code, 0);
\r
469 kill_process_tree(service_name, pid, exitcode, pid);
\r
472 The why argument is true if our wait timed out or false otherwise.
\r
473 Our wait is infinite so why will never be true when called by the system.
\r
474 If it is indeed true, assume we were called from stop_service() because
\r
475 this is a controlled shutdown, and don't take any restart action.
\r
479 /* What action should we take? */
\r
480 int action = NSSM_EXIT_RESTART;
\r
481 unsigned char action_string[ACTION_LEN];
\r
482 bool default_action;
\r
483 if (! get_exit_action(service_name, &exitcode, action_string, &default_action)) {
\r
484 for (int i = 0; exit_action_strings[i]; i++) {
\r
485 if (! _strnicmp((const char *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
492 process_handle = 0;
\r
495 /* Try to restart the service or return failure code to service manager */
\r
496 case NSSM_EXIT_RESTART:
\r
497 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_RESTART, service_name, code, exit_action_strings[action], exe, 0);
\r
498 while (monitor_service()) {
\r
499 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_RESTART_SERVICE_FAILED, exe, service_name, 0);
\r
504 /* Do nothing, just like srvany would */
\r
505 case NSSM_EXIT_IGNORE:
\r
506 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_IGNORE, service_name, code, exit_action_strings[action], exe, 0);
\r
510 /* Tell the service manager we are finished */
\r
511 case NSSM_EXIT_REALLY:
\r
512 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_REALLY, service_name, code, exit_action_strings[action], 0);
\r
513 stop_service(exitcode, true, default_action);
\r
516 /* Fake a crash so pre-Vista service managers will run recovery actions. */
\r
517 case NSSM_EXIT_UNCLEAN:
\r
518 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_UNCLEAN, service_name, code, exit_action_strings[action], 0);
\r
519 exit(stop_service(exitcode, false, default_action));
\r
524 void throttle_restart() {
\r
525 /* This can't be a restart if the service is already running. */
\r
526 if (! throttle++) return;
\r
528 int ms = throttle_milliseconds();
\r
530 if (throttle > 7) throttle = 8;
\r
532 char threshold[8], milliseconds[8];
\r
533 _snprintf(threshold, sizeof(threshold), "%d", throttle_delay);
\r
534 _snprintf(milliseconds, sizeof(milliseconds), "%d", ms);
\r
535 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_THROTTLED, service_name, threshold, milliseconds, 0);
\r
537 if (throttle_timer) {
\r
538 ZeroMemory(&throttle_duetime, sizeof(throttle_duetime));
\r
539 throttle_duetime.QuadPart = 0 - (ms * 10000LL);
\r
540 SetWaitableTimer(throttle_timer, &throttle_duetime, 0, 0, 0, 0);
\r
543 service_status.dwCurrentState = SERVICE_PAUSED;
\r
544 SetServiceStatus(service_handle, &service_status);
\r
546 if (throttle_timer) WaitForSingleObject(throttle_timer, INFINITE);
\r