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 service_status.dwCurrentState = SERVICE_START_PENDING;
\r
202 service_status.dwWaitHint = NSSM_RESET_THROTTLE_RESTART + 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 InitializeCriticalSection(&throttle_section);
\r
216 /* Make sure service recovery actions are taken where necessary */
\r
217 void set_service_recovery(char *service_name) {
\r
218 SC_HANDLE services = open_service_manager();
\r
219 if (! services) return;
\r
221 SC_HANDLE service = OpenService(services, service_name, SC_MANAGER_ALL_ACCESS);
\r
222 if (! service) return;
\r
225 SERVICE_FAILURE_ACTIONS_FLAG flag;
\r
226 ZeroMemory(&flag, sizeof(flag));
\r
227 flag.fFailureActionsOnNonCrashFailures = true;
\r
229 /* This functionality was added in Vista so the call may fail */
\r
230 ChangeServiceConfig2(service, SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, &flag);
\r
233 int monitor_service() {
\r
234 /* Set service status to started */
\r
235 int ret = start_service();
\r
238 _snprintf(code, sizeof(code), "%d", ret);
\r
239 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_START_SERVICE_FAILED, exe, service_name, ret, 0);
\r
242 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_STARTED_SERVICE, exe, flags, service_name, dir, 0);
\r
244 /* Monitor service service */
\r
245 if (! RegisterWaitForSingleObject(&wait_handle, process_handle, end_service, (void *) pid, INFINITE, WT_EXECUTEONLYONCE | WT_EXECUTELONGFUNCTION)) {
\r
246 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_REGISTERWAITFORSINGLEOBJECT_FAILED, service_name, exe, error_string(GetLastError()), 0);
\r
252 /* Service control handler */
\r
253 unsigned long WINAPI service_control_handler(unsigned long control, unsigned long event, void *data, void *context) {
\r
255 case SERVICE_CONTROL_SHUTDOWN:
\r
256 case SERVICE_CONTROL_STOP:
\r
257 stop_service(0, true, true);
\r
260 case SERVICE_CONTROL_CONTINUE:
\r
262 WakeConditionVariable(&throttle_condition);
\r
263 service_status.dwCurrentState = SERVICE_CONTINUE_PENDING;
\r
264 service_status.dwWaitHint = throttle_milliseconds() + NSSM_WAITHINT_MARGIN;
\r
265 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESET_THROTTLE, service_name, 0);
\r
266 SetServiceStatus(service_handle, &service_status);
\r
269 case SERVICE_CONTROL_PAUSE:
\r
271 We don't accept pause messages but it isn't possible to register
\r
272 only for continue messages so we have to handle this case.
\r
274 return ERROR_CALL_NOT_IMPLEMENTED;
\r
277 /* Unknown control */
\r
278 return ERROR_CALL_NOT_IMPLEMENTED;
\r
281 /* Start the service */
\r
282 int start_service() {
\r
285 if (process_handle) return 0;
\r
287 /* Allocate a STARTUPINFO structure for a new process */
\r
289 ZeroMemory(&si, sizeof(si));
\r
290 si.cb = sizeof(si);
\r
292 /* Allocate a PROCESSINFO structure for the process */
\r
293 PROCESS_INFORMATION pi;
\r
294 ZeroMemory(&pi, sizeof(pi));
\r
296 /* Get startup parameters */
\r
297 int ret = get_parameters(service_name, exe, sizeof(exe), flags, sizeof(flags), dir, sizeof(dir));
\r
299 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_PARAMETERS_FAILED, service_name, 0);
\r
300 return stop_service(2, true, true);
\r
303 /* Launch executable with arguments */
\r
304 char cmd[CMD_LENGTH];
\r
305 if (_snprintf(cmd, sizeof(cmd), "%s %s", exe, flags) < 0) {
\r
306 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "command line", "start_service", 0);
\r
307 return stop_service(2, true, true);
\r
310 throttle_restart();
\r
312 if (! CreateProcess(0, cmd, 0, 0, false, 0, 0, dir, &si, &pi)) {
\r
313 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service_name, exe, error_string(GetLastError()), 0);
\r
314 return stop_service(3, true, true);
\r
316 process_handle = pi.hProcess;
\r
317 pid = pi.dwProcessId;
\r
319 /* Signal successful start */
\r
320 service_status.dwCurrentState = SERVICE_RUNNING;
\r
321 SetServiceStatus(service_handle, &service_status);
\r
323 /* Wait for a clean startup. */
\r
324 if (WaitForSingleObject(process_handle, NSSM_RESET_THROTTLE_RESTART) == WAIT_TIMEOUT) throttle = 0;
\r
329 /* Stop the service */
\r
330 int stop_service(unsigned long exitcode, bool graceful, bool default_action) {
\r
331 if (default_action && ! exitcode && ! graceful) {
\r
332 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
336 /* Signal we are stopping */
\r
338 service_status.dwCurrentState = SERVICE_STOP_PENDING;
\r
339 service_status.dwWaitHint = NSSM_KILL_WINDOW_GRACE_PERIOD + NSSM_KILL_THREADS_GRACE_PERIOD + NSSM_WAITHINT_MARGIN;
\r
340 SetServiceStatus(service_handle, &service_status);
\r
343 /* Nothing to do if server isn't running */
\r
345 /* Shut down server */
\r
346 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_TERMINATEPROCESS, service_name, exe, 0);
\r
347 kill_process(service_name, process_handle, pid, 0);
\r
348 process_handle = 0;
\r
350 else log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_PROCESS_ALREADY_STOPPED, service_name, exe, 0);
\r
352 end_service((void *) pid, true);
\r
354 /* Signal we stopped */
\r
356 service_status.dwCurrentState = SERVICE_STOPPED;
\r
358 service_status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
\r
359 service_status.dwServiceSpecificExitCode = exitcode;
\r
362 service_status.dwWin32ExitCode = NO_ERROR;
\r
363 service_status.dwServiceSpecificExitCode = 0;
\r
365 SetServiceStatus(service_handle, &service_status);
\r
371 /* Callback function triggered when the server exits */
\r
372 void CALLBACK end_service(void *arg, unsigned char why) {
\r
373 if (stopping) return;
\r
377 pid = (unsigned long) arg;
\r
379 /* Check exit code */
\r
380 unsigned long exitcode = 0;
\r
381 GetExitCodeProcess(process_handle, &exitcode);
\r
384 kill_process_tree(service_name, pid, exitcode, pid);
\r
387 The why argument is true if our wait timed out or false otherwise.
\r
388 Our wait is infinite so why will never be true when called by the system.
\r
389 If it is indeed true, assume we were called from stop_service() because
\r
390 this is a controlled shutdown, and don't take any restart action.
\r
395 _snprintf(code, sizeof(code), "%d", exitcode);
\r
396 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ENDED_SERVICE, exe, service_name, code, 0);
\r
398 /* What action should we take? */
\r
399 int action = NSSM_EXIT_RESTART;
\r
400 unsigned char action_string[ACTION_LEN];
\r
401 bool default_action;
\r
402 if (! get_exit_action(service_name, &exitcode, action_string, &default_action)) {
\r
403 for (int i = 0; exit_action_strings[i]; i++) {
\r
404 if (! _strnicmp((const char *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
411 process_handle = 0;
\r
414 /* Try to restart the service or return failure code to service manager */
\r
415 case NSSM_EXIT_RESTART:
\r
416 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_RESTART, service_name, code, exit_action_strings[action], exe, 0);
\r
417 while (monitor_service()) {
\r
418 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_RESTART_SERVICE_FAILED, exe, service_name, 0);
\r
423 /* Do nothing, just like srvany would */
\r
424 case NSSM_EXIT_IGNORE:
\r
425 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_IGNORE, service_name, code, exit_action_strings[action], exe, 0);
\r
429 /* Tell the service manager we are finished */
\r
430 case NSSM_EXIT_REALLY:
\r
431 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_REALLY, service_name, code, exit_action_strings[action], 0);
\r
432 stop_service(exitcode, true, default_action);
\r
435 /* Fake a crash so pre-Vista service managers will run recovery actions. */
\r
436 case NSSM_EXIT_UNCLEAN:
\r
437 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_UNCLEAN, service_name, code, exit_action_strings[action], 0);
\r
438 exit(stop_service(exitcode, false, default_action));
\r
443 void throttle_restart() {
\r
444 /* This can't be a restart if the service is already running. */
\r
445 if (! throttle++) return;
\r
447 int ms = throttle_milliseconds();
\r
449 if (throttle > 7) throttle = 8;
\r
451 char threshold[8], milliseconds[8];
\r
452 _snprintf(threshold, sizeof(threshold), "%d", NSSM_RESET_THROTTLE_RESTART);
\r
453 _snprintf(milliseconds, sizeof(milliseconds), "%d", ms);
\r
454 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_THROTTLED, service_name, threshold, milliseconds, 0);
\r
456 EnterCriticalSection(&throttle_section);
\r
458 service_status.dwCurrentState = SERVICE_PAUSED;
\r
459 SetServiceStatus(service_handle, &service_status);
\r
461 SleepConditionVariableCS(&throttle_condition, &throttle_section, ms);
\r
463 LeaveCriticalSection(&throttle_section);
\r