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 CRITICAL_SECTION throttle_section;
\r
14 CONDITION_VARIABLE throttle_condition;
\r
16 static enum { NSSM_EXIT_RESTART, NSSM_EXIT_IGNORE, NSSM_EXIT_REALLY, NSSM_EXIT_UNCLEAN } exit_actions;
\r
17 static const char *exit_action_strings[] = { "Restart", "Ignore", "Exit", "Suicide", 0 };
\r
19 static unsigned long throttle;
\r
21 static inline int throttle_milliseconds() {
\r
22 /* pow() operates on doubles. */
\r
23 int ret = 1; for (unsigned long i = 1; i < throttle; i++) ret *= 2;
\r
27 /* Connect to the service manager */
\r
28 SC_HANDLE open_service_manager() {
\r
29 SC_HANDLE ret = OpenSCManager(0, SERVICES_ACTIVE_DATABASE, SC_MANAGER_ALL_ACCESS);
\r
31 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENSCMANAGER_FAILED, 0);
\r
38 /* About to install the service */
\r
39 int pre_install_service(int argc, char **argv) {
\r
40 /* Show the dialogue box if we didn't give the */
\r
41 if (argc < 2) return nssm_gui(IDD_INSTALL, argv[0]);
\r
43 /* Arguments are optional */
\r
45 size_t flagslen = 0;
\r
48 for (i = 2; i < argc; i++) flagslen += strlen(argv[i]) + 1;
\r
49 if (! flagslen) flagslen = 1;
\r
51 flags = (char *) HeapAlloc(GetProcessHeap(), 0, flagslen);
\r
53 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "flags", "pre_install_service()", 0);
\r
56 ZeroMemory(flags, flagslen);
\r
59 This probably isn't UTF8-safe and should use std::string or something
\r
60 but it's been broken for the best part of a decade and due for a rewrite
\r
61 anyway so it'll do as a quick-'n'-dirty fix. Note that we don't free
\r
62 the flags buffer but as the program exits that isn't a big problem.
\r
64 for (i = 2; i < argc; i++) {
\r
65 size_t len = strlen(argv[i]);
\r
66 memmove(flags + s, argv[i], len);
\r
68 if (i < argc - 1) flags[s++] = ' ';
\r
71 return install_service(argv[0], argv[1], flags);
\r
74 /* About to remove the service */
\r
75 int pre_remove_service(int argc, char **argv) {
\r
76 /* Show dialogue box if we didn't pass service name and "confirm" */
\r
77 if (argc < 2) return nssm_gui(IDD_REMOVE, argv[0]);
\r
78 if (str_equiv(argv[1], "confirm")) return remove_service(argv[0]);
\r
79 fprintf(stderr, "To remove a service without confirmation: nssm remove <servicename> confirm\n");
\r
83 /* Install the service */
\r
84 int install_service(char *name, char *exe, char *flags) {
\r
85 /* Open service manager */
\r
86 SC_HANDLE services = open_service_manager();
\r
88 fprintf(stderr, "Error opening service manager!\n");
\r
92 /* Get path of this program */
\r
93 char path[MAX_PATH];
\r
94 GetModuleFileName(0, path, MAX_PATH);
\r
96 /* Construct command */
\r
97 char command[CMD_LENGTH];
\r
98 size_t runlen = strlen(NSSM_RUN);
\r
99 size_t pathlen = strlen(path);
\r
100 if (pathlen + runlen + 2 >= VALUE_LENGTH) {
\r
101 fprintf(stderr, "The full path to " NSSM " is too long!\n");
\r
104 if (snprintf(command, sizeof(command), "\"%s\" %s", path, NSSM_RUN) < 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 /* Get startup parameters */
\r
202 int ret = get_parameters(argv[0], exe, sizeof(exe), flags, sizeof(flags), dir, sizeof(dir));
\r
204 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_PARAMETERS_FAILED, argv[0], 0);
\r
205 service_status.dwCurrentState = SERVICE_STOPPED;
\r
206 /* An accurate, if not particularly helpful, status */
\r
207 service_status.dwWin32ExitCode = ERROR_SERVICE_NOT_ACTIVE;
\r
208 SetServiceStatus(service_handle, &service_status);
\r
212 service_status.dwCurrentState = SERVICE_START_PENDING;
\r
213 service_status.dwWaitHint = NSSM_RESET_THROTTLE_RESTART + NSSM_WAITHINT_MARGIN;
\r
214 SetServiceStatus(service_handle, &service_status);
\r
216 /* Try to create the exit action parameters; we don't care if it fails */
\r
217 create_exit_action(argv[0], exit_action_strings[0]);
\r
219 set_service_recovery(service_name);
\r
221 /* Used for signalling a resume if the service pauses when throttled. */
\r
222 InitializeCriticalSection(&throttle_section);
\r
227 /* Make sure service recovery actions are taken where necessary */
\r
228 void set_service_recovery(char *service_name) {
\r
229 SC_HANDLE services = open_service_manager();
\r
230 if (! services) return;
\r
232 SC_HANDLE service = OpenService(services, service_name, SC_MANAGER_ALL_ACCESS);
\r
233 if (! service) return;
\r
236 SERVICE_FAILURE_ACTIONS_FLAG flag;
\r
237 ZeroMemory(&flag, sizeof(flag));
\r
238 flag.fFailureActionsOnNonCrashFailures = true;
\r
240 /* This functionality was added in Vista so the call may fail */
\r
241 ChangeServiceConfig2(service, SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, &flag);
\r
244 int monitor_service() {
\r
245 /* Set service status to started */
\r
246 int ret = start_service();
\r
249 snprintf(code, sizeof(code), "%d", ret);
\r
250 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_START_SERVICE_FAILED, exe, service_name, ret, 0);
\r
253 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_STARTED_SERVICE, exe, flags, service_name, dir, 0);
\r
255 /* Monitor service service */
\r
256 if (! RegisterWaitForSingleObject(&wait_handle, process_handle, end_service, (void *) pid, INFINITE, WT_EXECUTEONLYONCE | WT_EXECUTELONGFUNCTION)) {
\r
257 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_REGISTERWAITFORSINGLEOBJECT_FAILED, service_name, exe, error_string(GetLastError()), 0);
\r
263 /* Service control handler */
\r
264 unsigned long WINAPI service_control_handler(unsigned long control, unsigned long event, void *data, void *context) {
\r
266 case SERVICE_CONTROL_SHUTDOWN:
\r
267 case SERVICE_CONTROL_STOP:
\r
268 stop_service(0, true, true);
\r
271 case SERVICE_CONTROL_CONTINUE:
\r
273 WakeConditionVariable(&throttle_condition);
\r
274 service_status.dwCurrentState = SERVICE_CONTINUE_PENDING;
\r
275 service_status.dwWaitHint = throttle_milliseconds() + NSSM_WAITHINT_MARGIN;
\r
276 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESET_THROTTLE, service_name, 0);
\r
277 SetServiceStatus(service_handle, &service_status);
\r
280 case SERVICE_CONTROL_PAUSE:
\r
282 We don't accept pause messages but it isn't possible to register
\r
283 only for continue messages so we have to handle this case.
\r
285 return ERROR_CALL_NOT_IMPLEMENTED;
\r
288 /* Unknown control */
\r
289 return ERROR_CALL_NOT_IMPLEMENTED;
\r
292 /* Start the service */
\r
293 int start_service() {
\r
296 if (process_handle) return 0;
\r
298 /* Allocate a STARTUPINFO structure for a new process */
\r
300 ZeroMemory(&si, sizeof(si));
\r
301 si.cb = sizeof(si);
\r
303 /* Allocate a PROCESSINFO structure for the process */
\r
304 PROCESS_INFORMATION pi;
\r
305 ZeroMemory(&pi, sizeof(pi));
\r
307 /* Launch executable with arguments */
\r
308 char cmd[CMD_LENGTH];
\r
309 if (_snprintf(cmd, sizeof(cmd), "%s %s", exe, flags) < 0) {
\r
310 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "command line", "start_service", 0);
\r
311 return stop_service(2, true, true);
\r
314 throttle_restart();
\r
316 if (! CreateProcess(0, cmd, 0, 0, false, 0, 0, dir, &si, &pi)) {
\r
317 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service_name, exe, error_string(GetLastError()), 0);
\r
318 return stop_service(3, true, true);
\r
320 process_handle = pi.hProcess;
\r
321 pid = pi.dwProcessId;
\r
323 /* Signal successful start */
\r
324 service_status.dwCurrentState = SERVICE_RUNNING;
\r
325 SetServiceStatus(service_handle, &service_status);
\r
327 /* Wait for a clean startup. */
\r
328 if (WaitForSingleObject(process_handle, NSSM_RESET_THROTTLE_RESTART) == WAIT_TIMEOUT) throttle = 0;
\r
333 /* Stop the service */
\r
334 int stop_service(unsigned long exitcode, bool graceful, bool default_action) {
\r
335 if (default_action && ! exitcode && ! graceful) {
\r
336 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
340 /* Signal we are stopping */
\r
342 service_status.dwCurrentState = SERVICE_STOP_PENDING;
\r
343 service_status.dwWaitHint = NSSM_KILL_WINDOW_GRACE_PERIOD + NSSM_KILL_THREADS_GRACE_PERIOD + NSSM_WAITHINT_MARGIN;
\r
344 SetServiceStatus(service_handle, &service_status);
\r
347 /* Nothing to do if server isn't running */
\r
349 /* Shut down server */
\r
350 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_TERMINATEPROCESS, service_name, exe, 0);
\r
351 kill_process(service_name, process_handle, pid, 0);
\r
352 process_handle = 0;
\r
354 else log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_PROCESS_ALREADY_STOPPED, service_name, exe, 0);
\r
356 end_service((void *) pid, true);
\r
358 /* Signal we stopped */
\r
360 service_status.dwCurrentState = SERVICE_STOPPED;
\r
362 service_status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
\r
363 service_status.dwServiceSpecificExitCode = exitcode;
\r
366 service_status.dwWin32ExitCode = NO_ERROR;
\r
367 service_status.dwServiceSpecificExitCode = 0;
\r
369 SetServiceStatus(service_handle, &service_status);
\r
375 /* Callback function triggered when the server exits */
\r
376 void CALLBACK end_service(void *arg, unsigned char why) {
\r
377 if (stopping) return;
\r
381 pid = (unsigned long) arg;
\r
383 /* Check exit code */
\r
384 unsigned long exitcode = 0;
\r
385 GetExitCodeProcess(process_handle, &exitcode);
\r
388 kill_process_tree(service_name, pid, exitcode, pid);
\r
391 The why argument is true if our wait timed out or false otherwise.
\r
392 Our wait is infinite so why will never be true when called by the system.
\r
393 If it is indeed true, assume we were called from stop_service() because
\r
394 this is a controlled shutdown, and don't take any restart action.
\r
399 _snprintf(code, sizeof(code), "%d", exitcode);
\r
400 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ENDED_SERVICE, exe, service_name, code, 0);
\r
402 /* What action should we take? */
\r
403 int action = NSSM_EXIT_RESTART;
\r
404 unsigned char action_string[ACTION_LEN];
\r
405 bool default_action;
\r
406 if (! get_exit_action(service_name, &exitcode, action_string, &default_action)) {
\r
407 for (int i = 0; exit_action_strings[i]; i++) {
\r
408 if (! _strnicmp((const char *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
415 process_handle = 0;
\r
418 /* Try to restart the service or return failure code to service manager */
\r
419 case NSSM_EXIT_RESTART:
\r
420 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_RESTART, service_name, code, exit_action_strings[action], exe, 0);
\r
421 while (monitor_service()) {
\r
422 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_RESTART_SERVICE_FAILED, exe, service_name, 0);
\r
427 /* Do nothing, just like srvany would */
\r
428 case NSSM_EXIT_IGNORE:
\r
429 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_IGNORE, service_name, code, exit_action_strings[action], exe, 0);
\r
433 /* Tell the service manager we are finished */
\r
434 case NSSM_EXIT_REALLY:
\r
435 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_REALLY, service_name, code, exit_action_strings[action], 0);
\r
436 stop_service(exitcode, true, default_action);
\r
439 /* Fake a crash so pre-Vista service managers will run recovery actions. */
\r
440 case NSSM_EXIT_UNCLEAN:
\r
441 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_UNCLEAN, service_name, code, exit_action_strings[action], 0);
\r
442 exit(stop_service(exitcode, false, default_action));
\r
447 void throttle_restart() {
\r
448 /* This can't be a restart if the service is already running. */
\r
449 if (! throttle++) return;
\r
451 int ms = throttle_milliseconds();
\r
453 if (throttle > 7) throttle = 8;
\r
455 char threshold[8], milliseconds[8];
\r
456 _snprintf(threshold, sizeof(threshold), "%d", NSSM_RESET_THROTTLE_RESTART);
\r
457 _snprintf(milliseconds, sizeof(milliseconds), "%d", ms);
\r
458 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_THROTTLED, service_name, threshold, milliseconds, 0);
\r
460 EnterCriticalSection(&throttle_section);
\r
462 service_status.dwCurrentState = SERVICE_PAUSED;
\r
463 SetServiceStatus(service_handle, &service_status);
\r
465 SleepConditionVariableCS(&throttle_condition, &throttle_section, ms);
\r
467 LeaveCriticalSection(&throttle_section);
\r