3 SERVICE_STATUS service_status;
\r
4 SERVICE_STATUS_HANDLE service_handle;
\r
5 HANDLE process_handle;
\r
8 static char service_name[SERVICE_NAME_LENGTH];
\r
9 char exe[EXE_LENGTH];
\r
10 char flags[CMD_LENGTH];
\r
13 unsigned long throttle_delay;
\r
14 HANDLE throttle_timer;
\r
15 LARGE_INTEGER throttle_duetime;
\r
17 static enum { NSSM_EXIT_RESTART, NSSM_EXIT_IGNORE, NSSM_EXIT_REALLY, NSSM_EXIT_UNCLEAN } exit_actions;
\r
18 static const char *exit_action_strings[] = { "Restart", "Ignore", "Exit", "Suicide", 0 };
\r
20 static unsigned long throttle;
\r
22 static inline int throttle_milliseconds() {
\r
23 /* pow() operates on doubles. */
\r
24 int ret = 1; for (unsigned long i = 1; i < throttle; i++) ret *= 2;
\r
28 /* Connect to the service manager */
\r
29 SC_HANDLE open_service_manager() {
\r
30 SC_HANDLE ret = OpenSCManager(0, SERVICES_ACTIVE_DATABASE, SC_MANAGER_ALL_ACCESS);
\r
32 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENSCMANAGER_FAILED, 0);
\r
39 /* About to install the service */
\r
40 int pre_install_service(int argc, char **argv) {
\r
41 /* Show the dialogue box if we didn't give the */
\r
42 if (argc < 2) return nssm_gui(IDD_INSTALL, argv[0]);
\r
44 /* Arguments are optional */
\r
46 size_t flagslen = 0;
\r
49 for (i = 2; i < argc; i++) flagslen += strlen(argv[i]) + 1;
\r
50 if (! flagslen) flagslen = 1;
\r
52 flags = (char *) HeapAlloc(GetProcessHeap(), 0, flagslen);
\r
54 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "flags", "pre_install_service()", 0);
\r
57 ZeroMemory(flags, flagslen);
\r
60 This probably isn't UTF8-safe and should use std::string or something
\r
61 but it's been broken for the best part of a decade and due for a rewrite
\r
62 anyway so it'll do as a quick-'n'-dirty fix. Note that we don't free
\r
63 the flags buffer but as the program exits that isn't a big problem.
\r
65 for (i = 2; i < argc; i++) {
\r
66 size_t len = strlen(argv[i]);
\r
67 memmove(flags + s, argv[i], len);
\r
69 if (i < argc - 1) flags[s++] = ' ';
\r
72 return install_service(argv[0], argv[1], flags);
\r
75 /* About to remove the service */
\r
76 int pre_remove_service(int argc, char **argv) {
\r
77 /* Show dialogue box if we didn't pass service name and "confirm" */
\r
78 if (argc < 2) return nssm_gui(IDD_REMOVE, argv[0]);
\r
79 if (str_equiv(argv[1], "confirm")) return remove_service(argv[0]);
\r
80 fprintf(stderr, "To remove a service without confirmation: nssm remove <servicename> confirm\n");
\r
84 /* Install the service */
\r
85 int install_service(char *name, char *exe, char *flags) {
\r
86 /* Open service manager */
\r
87 SC_HANDLE services = open_service_manager();
\r
89 fprintf(stderr, "Error opening service manager!\n");
\r
93 /* Get path of this program */
\r
94 char path[MAX_PATH];
\r
95 GetModuleFileName(0, path, MAX_PATH);
\r
97 /* Construct command */
\r
98 char command[CMD_LENGTH];
\r
99 size_t pathlen = strlen(path);
\r
100 if (pathlen + 1 >= VALUE_LENGTH) {
\r
101 fprintf(stderr, "The full path to " NSSM " is too long!\n");
\r
104 if (_snprintf(command, sizeof(command), "\"%s\"", path) < 0) {
\r
105 fprintf(stderr, "Out of memory for ImagePath!\n");
\r
109 /* Work out directory name */
\r
110 size_t len = strlen(exe);
\r
112 for (i = len; i && exe[i] != '\\' && exe[i] != '/'; i--);
\r
113 char dir[MAX_PATH];
\r
114 memmove(dir, exe, i);
\r
117 /* Create the service */
\r
118 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
120 fprintf(stderr, "Error creating service!\n");
\r
121 CloseServiceHandle(services);
\r
125 /* Now we need to put the parameters into the registry */
\r
126 if (create_parameters(name, exe, flags, dir)) {
\r
127 fprintf(stderr, "Error setting startup parameters for the service!\n");
\r
128 DeleteService(service);
\r
129 CloseServiceHandle(services);
\r
134 CloseServiceHandle(service);
\r
135 CloseServiceHandle(services);
\r
137 printf("Service \"%s\" installed successfully!\n", name);
\r
141 /* Remove the service */
\r
142 int remove_service(char *name) {
\r
143 /* Open service manager */
\r
144 SC_HANDLE services = open_service_manager();
\r
146 fprintf(stderr, "Error opening service manager!\n");
\r
150 /* Try to open the service */
\r
151 SC_HANDLE service = OpenService(services, name, SC_MANAGER_ALL_ACCESS);
\r
153 fprintf(stderr, "Can't open service!");
\r
154 CloseServiceHandle(services);
\r
158 /* Try to delete the service */
\r
159 if (! DeleteService(service)) {
\r
160 fprintf(stderr, "Error deleting service!\n");
\r
161 CloseServiceHandle(service);
\r
162 CloseServiceHandle(services);
\r
167 CloseServiceHandle(service);
\r
168 CloseServiceHandle(services);
\r
170 printf("Service \"%s\" removed successfully!\n", name);
\r
174 /* Service initialisation */
\r
175 void WINAPI service_main(unsigned long argc, char **argv) {
\r
176 if (_snprintf(service_name, sizeof(service_name), "%s", argv[0]) < 0) {
\r
177 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "service_name", "service_main()", 0);
\r
181 /* Initialise status */
\r
182 ZeroMemory(&service_status, sizeof(service_status));
\r
183 service_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;
\r
184 service_status.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE;
\r
185 service_status.dwWin32ExitCode = NO_ERROR;
\r
186 service_status.dwServiceSpecificExitCode = 0;
\r
187 service_status.dwCheckPoint = 0;
\r
188 service_status.dwWaitHint = NSSM_WAITHINT_MARGIN;
\r
190 /* Signal we AREN'T running the server */
\r
191 process_handle = 0;
\r
194 /* Register control handler */
\r
195 service_handle = RegisterServiceCtrlHandlerEx(NSSM, service_control_handler, 0);
\r
196 if (! service_handle) {
\r
197 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_REGISTERSERVICECTRLHANDER_FAILED, error_string(GetLastError()), 0);
\r
201 log_service_control(service_name, 0, true);
\r
203 service_status.dwCurrentState = SERVICE_START_PENDING;
\r
204 service_status.dwWaitHint = throttle_delay + NSSM_WAITHINT_MARGIN;
\r
205 SetServiceStatus(service_handle, &service_status);
\r
207 /* Try to create the exit action parameters; we don't care if it fails */
\r
208 create_exit_action(argv[0], exit_action_strings[0]);
\r
210 set_service_recovery(service_name);
\r
212 /* Used for signalling a resume if the service pauses when throttled. */
\r
213 throttle_timer = CreateWaitableTimer(0, 1, 0);
\r
214 if (! throttle_timer) {
\r
215 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_CREATEWAITABLETIMER_FAILED, service_name, error_string(GetLastError()), 0);
\r
221 /* Make sure service recovery actions are taken where necessary */
\r
222 void set_service_recovery(char *service_name) {
\r
223 SC_HANDLE services = open_service_manager();
\r
224 if (! services) return;
\r
226 SC_HANDLE service = OpenService(services, service_name, SC_MANAGER_ALL_ACCESS);
\r
227 if (! service) return;
\r
230 SERVICE_FAILURE_ACTIONS_FLAG flag;
\r
231 ZeroMemory(&flag, sizeof(flag));
\r
232 flag.fFailureActionsOnNonCrashFailures = true;
\r
234 /* This functionality was added in Vista so the call may fail */
\r
235 ChangeServiceConfig2(service, SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, &flag);
\r
238 int monitor_service() {
\r
239 /* Set service status to started */
\r
240 int ret = start_service();
\r
243 _snprintf(code, sizeof(code), "%d", ret);
\r
244 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_START_SERVICE_FAILED, exe, service_name, ret, 0);
\r
247 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_STARTED_SERVICE, exe, flags, service_name, dir, 0);
\r
249 /* Monitor service service */
\r
250 if (! RegisterWaitForSingleObject(&wait_handle, process_handle, end_service, (void *) pid, INFINITE, WT_EXECUTEONLYONCE | WT_EXECUTELONGFUNCTION)) {
\r
251 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_REGISTERWAITFORSINGLEOBJECT_FAILED, service_name, exe, error_string(GetLastError()), 0);
\r
257 char *service_control_text(unsigned long control) {
\r
259 /* HACK: there is no SERVICE_CONTROL_START constant */
\r
260 case 0: return "START";
\r
261 case SERVICE_CONTROL_STOP: return "STOP";
\r
262 case SERVICE_CONTROL_SHUTDOWN: return "SHUTDOWN";
\r
263 case SERVICE_CONTROL_PAUSE: return "PAUSE";
\r
264 case SERVICE_CONTROL_CONTINUE: return "CONTINUE";
\r
265 case SERVICE_CONTROL_INTERROGATE: return "INTERROGATE";
\r
270 void log_service_control(char *service_name, unsigned long control, bool handled) {
\r
271 char *text = service_control_text(control);
\r
272 unsigned long event;
\r
275 /* "0x" + 8 x hex + NULL */
\r
276 text = (char *) HeapAlloc(GetProcessHeap(), 0, 11);
\r
278 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "control code", "log_service_control", 0);
\r
281 if (_snprintf(text, 11, "0x%08x", control) < 0) {
\r
282 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "control code", "log_service_control", 0);
\r
283 HeapFree(GetProcessHeap(), 0, text);
\r
287 event = NSSM_EVENT_SERVICE_CONTROL_UNKNOWN;
\r
289 else if (handled) event = NSSM_EVENT_SERVICE_CONTROL_HANDLED;
\r
290 else event = NSSM_EVENT_SERVICE_CONTROL_NOT_HANDLED;
\r
292 log_event(EVENTLOG_INFORMATION_TYPE, event, service_name, text, 0);
\r
294 if (event == NSSM_EVENT_SERVICE_CONTROL_UNKNOWN) {
\r
295 HeapFree(GetProcessHeap(), 0, text);
\r
299 /* Service control handler */
\r
300 unsigned long WINAPI service_control_handler(unsigned long control, unsigned long event, void *data, void *context) {
\r
302 case SERVICE_CONTROL_SHUTDOWN:
\r
303 case SERVICE_CONTROL_STOP:
\r
304 log_service_control(service_name, control, true);
\r
305 stop_service(0, true, true);
\r
308 case SERVICE_CONTROL_CONTINUE:
\r
309 log_service_control(service_name, control, true);
\r
310 if (! throttle_timer) return ERROR_CALL_NOT_IMPLEMENTED;
\r
312 ZeroMemory(&throttle_duetime, sizeof(throttle_duetime));
\r
313 SetWaitableTimer(throttle_timer, &throttle_duetime, 0, 0, 0, 0);
\r
314 service_status.dwCurrentState = SERVICE_CONTINUE_PENDING;
\r
315 service_status.dwWaitHint = throttle_milliseconds() + NSSM_WAITHINT_MARGIN;
\r
316 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESET_THROTTLE, service_name, 0);
\r
317 SetServiceStatus(service_handle, &service_status);
\r
320 case SERVICE_CONTROL_PAUSE:
\r
322 We don't accept pause messages but it isn't possible to register
\r
323 only for continue messages so we have to handle this case.
\r
325 log_service_control(service_name, control, false);
\r
326 return ERROR_CALL_NOT_IMPLEMENTED;
\r
329 /* Unknown control */
\r
330 log_service_control(service_name, control, false);
\r
331 return ERROR_CALL_NOT_IMPLEMENTED;
\r
334 /* Start the service */
\r
335 int start_service() {
\r
338 if (process_handle) return 0;
\r
340 /* Allocate a STARTUPINFO structure for a new process */
\r
342 ZeroMemory(&si, sizeof(si));
\r
343 si.cb = sizeof(si);
\r
345 /* Allocate a PROCESSINFO structure for the process */
\r
346 PROCESS_INFORMATION pi;
\r
347 ZeroMemory(&pi, sizeof(pi));
\r
349 /* Get startup parameters */
\r
351 int ret = get_parameters(service_name, exe, sizeof(exe), flags, sizeof(flags), dir, sizeof(dir), &env, &throttle_delay);
\r
353 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_PARAMETERS_FAILED, service_name, 0);
\r
354 return stop_service(2, true, true);
\r
357 /* Launch executable with arguments */
\r
358 char cmd[CMD_LENGTH];
\r
359 if (_snprintf(cmd, sizeof(cmd), "\"%s\" %s", exe, flags) < 0) {
\r
360 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "command line", "start_service", 0);
\r
361 return stop_service(2, true, true);
\r
364 throttle_restart();
\r
366 if (! CreateProcess(0, cmd, 0, 0, false, 0, env, dir, &si, &pi)) {
\r
367 unsigned long error = GetLastError();
\r
368 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
369 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service_name, exe, error_string(error), 0);
\r
370 return stop_service(3, true, true);
\r
372 process_handle = pi.hProcess;
\r
373 pid = pi.dwProcessId;
\r
375 /* Signal successful start */
\r
376 service_status.dwCurrentState = SERVICE_RUNNING;
\r
377 SetServiceStatus(service_handle, &service_status);
\r
379 /* Wait for a clean startup. */
\r
380 if (WaitForSingleObject(process_handle, throttle_delay) == WAIT_TIMEOUT) throttle = 0;
\r
385 /* Stop the service */
\r
386 int stop_service(unsigned long exitcode, bool graceful, bool default_action) {
\r
387 if (default_action && ! exitcode && ! graceful) {
\r
388 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
392 /* Signal we are stopping */
\r
394 service_status.dwCurrentState = SERVICE_STOP_PENDING;
\r
395 service_status.dwWaitHint = NSSM_KILL_WINDOW_GRACE_PERIOD + NSSM_KILL_THREADS_GRACE_PERIOD + NSSM_WAITHINT_MARGIN;
\r
396 SetServiceStatus(service_handle, &service_status);
\r
399 /* Nothing to do if server isn't running */
\r
401 /* Shut down server */
\r
402 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_TERMINATEPROCESS, service_name, exe, 0);
\r
403 kill_process(service_name, process_handle, pid, 0);
\r
404 process_handle = 0;
\r
406 else log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_PROCESS_ALREADY_STOPPED, service_name, exe, 0);
\r
408 end_service((void *) pid, true);
\r
410 /* Signal we stopped */
\r
412 service_status.dwCurrentState = SERVICE_STOPPED;
\r
414 service_status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
\r
415 service_status.dwServiceSpecificExitCode = exitcode;
\r
418 service_status.dwWin32ExitCode = NO_ERROR;
\r
419 service_status.dwServiceSpecificExitCode = 0;
\r
421 SetServiceStatus(service_handle, &service_status);
\r
427 /* Callback function triggered when the server exits */
\r
428 void CALLBACK end_service(void *arg, unsigned char why) {
\r
429 if (stopping) return;
\r
433 pid = (unsigned long) arg;
\r
435 /* Check exit code */
\r
436 unsigned long exitcode = 0;
\r
437 GetExitCodeProcess(process_handle, &exitcode);
\r
440 kill_process_tree(service_name, pid, exitcode, pid);
\r
443 The why argument is true if our wait timed out or false otherwise.
\r
444 Our wait is infinite so why will never be true when called by the system.
\r
445 If it is indeed true, assume we were called from stop_service() because
\r
446 this is a controlled shutdown, and don't take any restart action.
\r
451 _snprintf(code, sizeof(code), "%d", exitcode);
\r
452 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ENDED_SERVICE, exe, service_name, code, 0);
\r
454 /* What action should we take? */
\r
455 int action = NSSM_EXIT_RESTART;
\r
456 unsigned char action_string[ACTION_LEN];
\r
457 bool default_action;
\r
458 if (! get_exit_action(service_name, &exitcode, action_string, &default_action)) {
\r
459 for (int i = 0; exit_action_strings[i]; i++) {
\r
460 if (! _strnicmp((const char *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
467 process_handle = 0;
\r
470 /* Try to restart the service or return failure code to service manager */
\r
471 case NSSM_EXIT_RESTART:
\r
472 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_RESTART, service_name, code, exit_action_strings[action], exe, 0);
\r
473 while (monitor_service()) {
\r
474 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_RESTART_SERVICE_FAILED, exe, service_name, 0);
\r
479 /* Do nothing, just like srvany would */
\r
480 case NSSM_EXIT_IGNORE:
\r
481 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_IGNORE, service_name, code, exit_action_strings[action], exe, 0);
\r
485 /* Tell the service manager we are finished */
\r
486 case NSSM_EXIT_REALLY:
\r
487 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_REALLY, service_name, code, exit_action_strings[action], 0);
\r
488 stop_service(exitcode, true, default_action);
\r
491 /* Fake a crash so pre-Vista service managers will run recovery actions. */
\r
492 case NSSM_EXIT_UNCLEAN:
\r
493 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_UNCLEAN, service_name, code, exit_action_strings[action], 0);
\r
494 exit(stop_service(exitcode, false, default_action));
\r
499 void throttle_restart() {
\r
500 /* This can't be a restart if the service is already running. */
\r
501 if (! throttle++) return;
\r
503 int ms = throttle_milliseconds();
\r
505 if (throttle > 7) throttle = 8;
\r
507 char threshold[8], milliseconds[8];
\r
508 _snprintf(threshold, sizeof(threshold), "%d", throttle_delay);
\r
509 _snprintf(milliseconds, sizeof(milliseconds), "%d", ms);
\r
510 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_THROTTLED, service_name, threshold, milliseconds, 0);
\r
512 if (throttle_timer) {
\r
513 ZeroMemory(&throttle_duetime, sizeof(throttle_duetime));
\r
514 throttle_duetime.QuadPart = 0 - (ms * 10000LL);
\r
515 SetWaitableTimer(throttle_timer, &throttle_duetime, 0, 0, 0, 0);
\r
518 service_status.dwCurrentState = SERVICE_PAUSED;
\r
519 SetServiceStatus(service_handle, &service_status);
\r
521 if (throttle_timer) WaitForSingleObject(throttle_timer, INFINITE);
\r