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 service_status.dwCurrentState = SERVICE_START_PENDING;
\r
202 service_status.dwWaitHint = throttle_delay + NSSM_WAITHINT_MARGIN;
\r
203 SetServiceStatus(service_handle, &service_status);
\r
205 /* Try to create the exit action parameters; we don't care if it fails */
\r
206 create_exit_action(argv[0], exit_action_strings[0]);
\r
208 set_service_recovery(service_name);
\r
210 /* Used for signalling a resume if the service pauses when throttled. */
\r
211 throttle_timer = CreateWaitableTimer(0, 1, 0);
\r
212 if (! throttle_timer) {
\r
213 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_CREATEWAITABLETIMER_FAILED, service_name, error_string(GetLastError()), 0);
\r
219 /* Make sure service recovery actions are taken where necessary */
\r
220 void set_service_recovery(char *service_name) {
\r
221 SC_HANDLE services = open_service_manager();
\r
222 if (! services) return;
\r
224 SC_HANDLE service = OpenService(services, service_name, SC_MANAGER_ALL_ACCESS);
\r
225 if (! service) return;
\r
228 SERVICE_FAILURE_ACTIONS_FLAG flag;
\r
229 ZeroMemory(&flag, sizeof(flag));
\r
230 flag.fFailureActionsOnNonCrashFailures = true;
\r
232 /* This functionality was added in Vista so the call may fail */
\r
233 ChangeServiceConfig2(service, SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, &flag);
\r
236 int monitor_service() {
\r
237 /* Set service status to started */
\r
238 int ret = start_service();
\r
241 _snprintf(code, sizeof(code), "%d", ret);
\r
242 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_START_SERVICE_FAILED, exe, service_name, ret, 0);
\r
245 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_STARTED_SERVICE, exe, flags, service_name, dir, 0);
\r
247 /* Monitor service service */
\r
248 if (! RegisterWaitForSingleObject(&wait_handle, process_handle, end_service, (void *) pid, INFINITE, WT_EXECUTEONLYONCE | WT_EXECUTELONGFUNCTION)) {
\r
249 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_REGISTERWAITFORSINGLEOBJECT_FAILED, service_name, exe, error_string(GetLastError()), 0);
\r
255 /* Service control handler */
\r
256 unsigned long WINAPI service_control_handler(unsigned long control, unsigned long event, void *data, void *context) {
\r
258 case SERVICE_CONTROL_SHUTDOWN:
\r
259 case SERVICE_CONTROL_STOP:
\r
260 stop_service(0, true, true);
\r
263 case SERVICE_CONTROL_CONTINUE:
\r
264 if (! throttle_timer) return ERROR_CALL_NOT_IMPLEMENTED;
\r
266 ZeroMemory(&throttle_duetime, sizeof(throttle_duetime));
\r
267 SetWaitableTimer(throttle_timer, &throttle_duetime, 0, 0, 0, 0);
\r
268 service_status.dwCurrentState = SERVICE_CONTINUE_PENDING;
\r
269 service_status.dwWaitHint = throttle_milliseconds() + NSSM_WAITHINT_MARGIN;
\r
270 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESET_THROTTLE, service_name, 0);
\r
271 SetServiceStatus(service_handle, &service_status);
\r
274 case SERVICE_CONTROL_PAUSE:
\r
276 We don't accept pause messages but it isn't possible to register
\r
277 only for continue messages so we have to handle this case.
\r
279 return ERROR_CALL_NOT_IMPLEMENTED;
\r
282 /* Unknown control */
\r
283 return ERROR_CALL_NOT_IMPLEMENTED;
\r
286 /* Start the service */
\r
287 int start_service() {
\r
290 if (process_handle) return 0;
\r
292 /* Allocate a STARTUPINFO structure for a new process */
\r
294 ZeroMemory(&si, sizeof(si));
\r
295 si.cb = sizeof(si);
\r
297 /* Allocate a PROCESSINFO structure for the process */
\r
298 PROCESS_INFORMATION pi;
\r
299 ZeroMemory(&pi, sizeof(pi));
\r
301 /* Get startup parameters */
\r
303 int ret = get_parameters(service_name, exe, sizeof(exe), flags, sizeof(flags), dir, sizeof(dir), &env, &throttle_delay);
\r
305 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_PARAMETERS_FAILED, service_name, 0);
\r
306 return stop_service(2, true, true);
\r
309 /* Launch executable with arguments */
\r
310 char cmd[CMD_LENGTH];
\r
311 if (_snprintf(cmd, sizeof(cmd), "\"%s\" %s", exe, flags) < 0) {
\r
312 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "command line", "start_service", 0);
\r
313 return stop_service(2, true, true);
\r
316 throttle_restart();
\r
318 if (! CreateProcess(0, cmd, 0, 0, false, 0, env, dir, &si, &pi)) {
\r
319 unsigned long error = GetLastError();
\r
320 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
321 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service_name, exe, error_string(error), 0);
\r
322 return stop_service(3, true, true);
\r
324 process_handle = pi.hProcess;
\r
325 pid = pi.dwProcessId;
\r
327 /* Signal successful start */
\r
328 service_status.dwCurrentState = SERVICE_RUNNING;
\r
329 SetServiceStatus(service_handle, &service_status);
\r
331 /* Wait for a clean startup. */
\r
332 if (WaitForSingleObject(process_handle, throttle_delay) == WAIT_TIMEOUT) throttle = 0;
\r
337 /* Stop the service */
\r
338 int stop_service(unsigned long exitcode, bool graceful, bool default_action) {
\r
339 if (default_action && ! exitcode && ! graceful) {
\r
340 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
344 /* Signal we are stopping */
\r
346 service_status.dwCurrentState = SERVICE_STOP_PENDING;
\r
347 service_status.dwWaitHint = NSSM_KILL_WINDOW_GRACE_PERIOD + NSSM_KILL_THREADS_GRACE_PERIOD + NSSM_WAITHINT_MARGIN;
\r
348 SetServiceStatus(service_handle, &service_status);
\r
351 /* Nothing to do if server isn't running */
\r
353 /* Shut down server */
\r
354 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_TERMINATEPROCESS, service_name, exe, 0);
\r
355 kill_process(service_name, process_handle, pid, 0);
\r
356 process_handle = 0;
\r
358 else log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_PROCESS_ALREADY_STOPPED, service_name, exe, 0);
\r
360 end_service((void *) pid, true);
\r
362 /* Signal we stopped */
\r
364 service_status.dwCurrentState = SERVICE_STOPPED;
\r
366 service_status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
\r
367 service_status.dwServiceSpecificExitCode = exitcode;
\r
370 service_status.dwWin32ExitCode = NO_ERROR;
\r
371 service_status.dwServiceSpecificExitCode = 0;
\r
373 SetServiceStatus(service_handle, &service_status);
\r
379 /* Callback function triggered when the server exits */
\r
380 void CALLBACK end_service(void *arg, unsigned char why) {
\r
381 if (stopping) return;
\r
385 pid = (unsigned long) arg;
\r
387 /* Check exit code */
\r
388 unsigned long exitcode = 0;
\r
389 GetExitCodeProcess(process_handle, &exitcode);
\r
392 kill_process_tree(service_name, pid, exitcode, pid);
\r
395 The why argument is true if our wait timed out or false otherwise.
\r
396 Our wait is infinite so why will never be true when called by the system.
\r
397 If it is indeed true, assume we were called from stop_service() because
\r
398 this is a controlled shutdown, and don't take any restart action.
\r
403 _snprintf(code, sizeof(code), "%d", exitcode);
\r
404 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ENDED_SERVICE, exe, service_name, code, 0);
\r
406 /* What action should we take? */
\r
407 int action = NSSM_EXIT_RESTART;
\r
408 unsigned char action_string[ACTION_LEN];
\r
409 bool default_action;
\r
410 if (! get_exit_action(service_name, &exitcode, action_string, &default_action)) {
\r
411 for (int i = 0; exit_action_strings[i]; i++) {
\r
412 if (! _strnicmp((const char *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
419 process_handle = 0;
\r
422 /* Try to restart the service or return failure code to service manager */
\r
423 case NSSM_EXIT_RESTART:
\r
424 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_RESTART, service_name, code, exit_action_strings[action], exe, 0);
\r
425 while (monitor_service()) {
\r
426 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_RESTART_SERVICE_FAILED, exe, service_name, 0);
\r
431 /* Do nothing, just like srvany would */
\r
432 case NSSM_EXIT_IGNORE:
\r
433 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_IGNORE, service_name, code, exit_action_strings[action], exe, 0);
\r
437 /* Tell the service manager we are finished */
\r
438 case NSSM_EXIT_REALLY:
\r
439 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_REALLY, service_name, code, exit_action_strings[action], 0);
\r
440 stop_service(exitcode, true, default_action);
\r
443 /* Fake a crash so pre-Vista service managers will run recovery actions. */
\r
444 case NSSM_EXIT_UNCLEAN:
\r
445 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_UNCLEAN, service_name, code, exit_action_strings[action], 0);
\r
446 exit(stop_service(exitcode, false, default_action));
\r
451 void throttle_restart() {
\r
452 /* This can't be a restart if the service is already running. */
\r
453 if (! throttle++) return;
\r
455 int ms = throttle_milliseconds();
\r
457 if (throttle > 7) throttle = 8;
\r
459 char threshold[8], milliseconds[8];
\r
460 _snprintf(threshold, sizeof(threshold), "%d", throttle_delay);
\r
461 _snprintf(milliseconds, sizeof(milliseconds), "%d", ms);
\r
462 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_THROTTLED, service_name, threshold, milliseconds, 0);
\r
464 if (throttle_timer) {
\r
465 ZeroMemory(&throttle_duetime, sizeof(throttle_duetime));
\r
466 throttle_duetime.QuadPart = 0 - (ms * 10000LL);
\r
467 SetWaitableTimer(throttle_timer, &throttle_duetime, 0, 0, 0, 0);
\r
470 service_status.dwCurrentState = SERVICE_PAUSED;
\r
471 SetServiceStatus(service_handle, &service_status);
\r
473 if (throttle_timer) WaitForSingleObject(throttle_timer, INFINITE);
\r