4 bool use_critical_section;
\r
6 extern imports_t imports;
\r
7 extern settings_t settings[];
\r
9 const TCHAR *exit_action_strings[] = { _T("Restart"), _T("Ignore"), _T("Exit"), _T("Suicide"), 0 };
\r
10 const TCHAR *startup_strings[] = { _T("SERVICE_AUTO_START"), _T("SERVICE_DELAYED_AUTO_START"), _T("SERVICE_DEMAND_START"), _T("SERVICE_DISABLED"), 0 };
\r
11 const TCHAR *priority_strings[] = { _T("REALTIME_PRIORITY_CLASS"), _T("HIGH_PRIORITY_CLASS"), _T("ABOVE_NORMAL_PRIORITY_CLASS"), _T("NORMAL_PRIORITY_CLASS"), _T("BELOW_NORMAL_PRIORITY_CLASS"), _T("IDLE_PRIORITY_CLASS"), 0 };
\r
19 Check the status in response to a control.
\r
20 Returns: 1 if the status is expected, eg STOP following CONTROL_STOP.
\r
21 0 if the status is desired, eg STOPPED following CONTROL_STOP.
\r
22 -1 if the status is undesired, eg STOPPED following CONTROL_START.
\r
24 static inline int service_control_response(unsigned long control, unsigned long status) {
\r
26 case NSSM_SERVICE_CONTROL_START:
\r
28 case SERVICE_START_PENDING:
\r
31 case SERVICE_RUNNING:
\r
38 case SERVICE_CONTROL_STOP:
\r
39 case SERVICE_CONTROL_SHUTDOWN:
\r
41 case SERVICE_STOP_PENDING:
\r
44 case SERVICE_STOPPED:
\r
51 case SERVICE_CONTROL_PAUSE:
\r
53 case SERVICE_PAUSE_PENDING:
\r
56 case SERVICE_PAUSED:
\r
63 case SERVICE_CONTROL_CONTINUE:
\r
65 case SERVICE_CONTINUE_PENDING:
\r
68 case SERVICE_RUNNING:
\r
75 case SERVICE_CONTROL_INTERROGATE:
\r
82 static inline int await_service_control_response(unsigned long control, SC_HANDLE service_handle, SERVICE_STATUS *service_status, unsigned long initial_status) {
\r
84 while (QueryServiceStatus(service_handle, service_status)) {
\r
85 int response = service_control_response(control, service_status->dwCurrentState);
\r
86 /* Alas we can't WaitForSingleObject() on an SC_HANDLE. */
\r
87 if (! response) return response;
\r
88 if (response > 0 || service_status->dwCurrentState == initial_status) {
\r
89 if (++tries > 10) return response;
\r
92 else return response;
\r
97 int affinity_mask_to_string(__int64 mask, TCHAR **string) {
\r
98 if (! string) return 1;
\r
106 /* SetProcessAffinityMask() accepts a mask of up to 64 processors. */
\r
108 for (n = 0; n < _countof(set); n++) set[n].first = set[n].last = -1;
\r
110 for (i = 0, n = 0; i < _countof(set); i++) {
\r
111 if (mask & (1LL << i)) {
\r
112 if (set[n].first == -1) set[n].first = set[n].last = (int) i;
\r
113 else if (set[n].last == (int) i - 1) set[n].last = (int) i;
\r
116 set[n].first = set[n].last = (int) i;
\r
121 /* Worst case is 2x2 characters for first and last CPU plus - and/or , */
\r
122 size_t len = (size_t) (n + 1) * 6;
\r
123 *string = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len * sizeof(TCHAR));
\r
124 if (! string) return 2;
\r
128 for (i = 0; i <= n; i++) {
\r
129 if (i) (*string)[s++] = _T(',');
\r
130 ret = _sntprintf_s(*string + s, 3, _TRUNCATE, _T("%u"), set[i].first);
\r
132 HeapFree(GetProcessHeap(), 0, *string);
\r
137 if (set[i].last != set[i].first) {
\r
138 ret =_sntprintf_s(*string + s, 4, _TRUNCATE, _T("%c%u"), (set[i].last == set[i].first + 1) ? _T(',') : _T('-'), set[i].last);
\r
140 HeapFree(GetProcessHeap(), 0, *string);
\r
151 int affinity_string_to_mask(TCHAR *string, __int64 *mask) {
\r
152 if (! mask) return 1;
\r
155 if (! string) return 0;
\r
164 unsigned long number;
\r
166 for (n = 0; n < _countof(set); n++) set[n].first = set[n].last = -1;
\r
170 ret = str_number(s, &number, &end);
\r
172 if (ret == 0 || ret == 2) {
\r
173 if (number >= _countof(set)) return 2;
\r
174 set[n].first = set[n].last = (int) number;
\r
186 if (! *(++s)) return 3;
\r
187 ret = str_number(s, &number, &end);
\r
188 if (ret == 0 || ret == 2) {
\r
190 if (! *s || *s == _T(',')) {
\r
191 set[n].last = (int) number;
\r
208 for (i = 0; i <= n; i++) {
\r
209 for (int j = set[i].first; j <= set[i].last; j++) (__int64) *mask |= (1LL << (__int64) j);
\r
215 inline unsigned long priority_mask() {
\r
216 return REALTIME_PRIORITY_CLASS | HIGH_PRIORITY_CLASS | ABOVE_NORMAL_PRIORITY_CLASS | NORMAL_PRIORITY_CLASS | BELOW_NORMAL_PRIORITY_CLASS | IDLE_PRIORITY_CLASS;
\r
219 int priority_constant_to_index(unsigned long constant) {
\r
220 switch (constant & priority_mask()) {
\r
221 case REALTIME_PRIORITY_CLASS: return NSSM_REALTIME_PRIORITY;
\r
222 case HIGH_PRIORITY_CLASS: return NSSM_HIGH_PRIORITY;
\r
223 case ABOVE_NORMAL_PRIORITY_CLASS: return NSSM_ABOVE_NORMAL_PRIORITY;
\r
224 case BELOW_NORMAL_PRIORITY_CLASS: return NSSM_BELOW_NORMAL_PRIORITY;
\r
225 case IDLE_PRIORITY_CLASS: return NSSM_IDLE_PRIORITY;
\r
227 return NSSM_NORMAL_PRIORITY;
\r
230 unsigned long priority_index_to_constant(int index) {
\r
232 case NSSM_REALTIME_PRIORITY: return REALTIME_PRIORITY_CLASS;
\r
233 case NSSM_HIGH_PRIORITY: return HIGH_PRIORITY_CLASS;
\r
234 case NSSM_ABOVE_NORMAL_PRIORITY: return ABOVE_NORMAL_PRIORITY_CLASS;
\r
235 case NSSM_BELOW_NORMAL_PRIORITY: return BELOW_NORMAL_PRIORITY_CLASS;
\r
236 case NSSM_IDLE_PRIORITY: return IDLE_PRIORITY_CLASS;
\r
238 return NORMAL_PRIORITY_CLASS;
\r
241 static inline unsigned long throttle_milliseconds(unsigned long throttle) {
\r
242 /* pow() operates on doubles. */
\r
243 unsigned long ret = 1; for (unsigned long i = 1; i < throttle; i++) ret *= 2;
\r
248 Wrapper to be called in a new thread so that we can acknowledge a STOP
\r
249 control immediately.
\r
251 static unsigned long WINAPI shutdown_service(void *arg) {
\r
252 return stop_service((nssm_service_t *) arg, 0, true, true);
\r
255 /* Connect to the service manager */
\r
256 SC_HANDLE open_service_manager(unsigned long access) {
\r
257 SC_HANDLE ret = OpenSCManager(0, SERVICES_ACTIVE_DATABASE, access);
\r
259 if (is_admin) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENSCMANAGER_FAILED, 0);
\r
266 /* Open a service by name or display name. */
\r
267 SC_HANDLE open_service(SC_HANDLE services, TCHAR *service_name, unsigned long access, TCHAR *canonical_name, unsigned long canonical_namelen) {
\r
268 SC_HANDLE service_handle = OpenService(services, service_name, access);
\r
269 if (service_handle) {
\r
270 if (canonical_name && canonical_name != service_name) {
\r
271 if (_sntprintf_s(canonical_name, canonical_namelen, _TRUNCATE, _T("%s"), service_name) < 0) {
\r
272 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("canonical_name"), _T("open_service()"));
\r
276 return service_handle;
\r
279 unsigned long error = GetLastError();
\r
280 if (error != ERROR_SERVICE_DOES_NOT_EXIST) {
\r
281 print_message(stderr, NSSM_MESSAGE_OPENSERVICE_FAILED, error_string(GetLastError()));
\r
285 /* We can't look for a display name because there's no buffer to store it. */
\r
286 if (! canonical_name) {
\r
287 print_message(stderr, NSSM_MESSAGE_OPENSERVICE_FAILED, error_string(GetLastError()));
\r
291 unsigned long bufsize, required, count, i;
\r
292 unsigned long resume = 0;
\r
293 EnumServicesStatus(services, SERVICE_DRIVER | SERVICE_FILE_SYSTEM_DRIVER | SERVICE_KERNEL_DRIVER | SERVICE_WIN32, SERVICE_STATE_ALL, 0, 0, &required, &count, &resume);
\r
294 error = GetLastError();
\r
295 if (error != ERROR_MORE_DATA) {
\r
296 print_message(stderr, NSSM_MESSAGE_ENUMSERVICESSTATUS_FAILED, error_string(GetLastError()));
\r
300 ENUM_SERVICE_STATUS *status = (ENUM_SERVICE_STATUS *) HeapAlloc(GetProcessHeap(), 0, required);
\r
302 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("ENUM_SERVICE_STATUS"), _T("open_service()"));
\r
306 bufsize = required;
\r
309 EnumServicesStatus() returns:
\r
310 1 when it retrieved data and there's no more data to come.
\r
311 0 and sets last error to ERROR_MORE_DATA when it retrieved data and
\r
312 there's more data to come.
\r
313 0 and sets last error to something else on error.
\r
315 int ret = EnumServicesStatus(services, SERVICE_DRIVER | SERVICE_FILE_SYSTEM_DRIVER | SERVICE_KERNEL_DRIVER | SERVICE_WIN32, SERVICE_STATE_ALL, status, bufsize, &required, &count, &resume);
\r
317 error = GetLastError();
\r
318 if (error != ERROR_MORE_DATA) {
\r
319 HeapFree(GetProcessHeap(), 0, status);
\r
320 print_message(stderr, NSSM_MESSAGE_ENUMSERVICESSTATUS_FAILED, error_string(GetLastError()));
\r
325 for (i = 0; i < count; i++) {
\r
326 if (str_equiv(status[i].lpDisplayName, service_name)) {
\r
327 if (_sntprintf_s(canonical_name, canonical_namelen, _TRUNCATE, _T("%s"), status[i].lpServiceName) < 0) {
\r
328 HeapFree(GetProcessHeap(), 0, status);
\r
329 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("canonical_name"), _T("open_service()"));
\r
333 HeapFree(GetProcessHeap(), 0, status);
\r
334 return open_service(services, canonical_name, access, 0, 0);
\r
341 /* Recurse so we can get an error message. */
\r
342 return open_service(services, service_name, access, 0, 0);
\r
345 QUERY_SERVICE_CONFIG *query_service_config(const TCHAR *service_name, SC_HANDLE service_handle) {
\r
346 QUERY_SERVICE_CONFIG *qsc;
\r
347 unsigned long bufsize;
\r
348 unsigned long error;
\r
350 QueryServiceConfig(service_handle, 0, 0, &bufsize);
\r
351 error = GetLastError();
\r
352 if (error == ERROR_INSUFFICIENT_BUFFER) {
\r
353 qsc = (QUERY_SERVICE_CONFIG *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, bufsize);
\r
355 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("QUERY_SERVICE_CONFIG"), _T("query_service_config()"), 0);
\r
360 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG_FAILED, service_name, error_string(error), 0);
\r
364 if (! QueryServiceConfig(service_handle, qsc, bufsize, &bufsize)) {
\r
365 HeapFree(GetProcessHeap(), 0, qsc);
\r
366 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG_FAILED, service_name, error_string(GetLastError()), 0);
\r
373 int set_service_description(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR *buffer) {
\r
374 SERVICE_DESCRIPTION description;
\r
375 ZeroMemory(&description, sizeof(description));
\r
377 lpDescription must be NULL if we aren't changing, the new description
\r
380 if (buffer && buffer[0]) description.lpDescription = buffer;
\r
381 else description.lpDescription = _T("");
\r
383 if (ChangeServiceConfig2(service_handle, SERVICE_CONFIG_DESCRIPTION, &description)) return 0;
\r
385 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_DESCRIPTION_FAILED, service_name, error_string(GetLastError()), 0);
\r
389 int get_service_description(const TCHAR *service_name, SC_HANDLE service_handle, unsigned long len, TCHAR *buffer) {
\r
390 if (! buffer) return 1;
\r
392 unsigned long bufsize;
\r
393 QueryServiceConfig2(service_handle, SERVICE_CONFIG_DESCRIPTION, 0, 0, &bufsize);
\r
394 unsigned long error = GetLastError();
\r
395 if (error == ERROR_INSUFFICIENT_BUFFER) {
\r
396 SERVICE_DESCRIPTION *description = (SERVICE_DESCRIPTION *) HeapAlloc(GetProcessHeap(), 0, bufsize);
\r
397 if (! description) {
\r
398 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("SERVICE_CONFIG_DESCRIPTION"), _T("get_service_description()"));
\r
402 if (QueryServiceConfig2(service_handle, SERVICE_CONFIG_DESCRIPTION, (unsigned char *) description, bufsize, &bufsize)) {
\r
403 if (description->lpDescription) _sntprintf_s(buffer, len, _TRUNCATE, _T("%s"), description->lpDescription);
\r
404 else ZeroMemory(buffer, len * sizeof(TCHAR));
\r
405 HeapFree(GetProcessHeap(), 0, description);
\r
409 HeapFree(GetProcessHeap(), 0, description);
\r
410 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_CONFIG_DESCRIPTION"), error_string(error));
\r
415 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_CONFIG_DESCRIPTION"), error_string(error));
\r
422 int get_service_startup(const TCHAR *service_name, SC_HANDLE service_handle, const QUERY_SERVICE_CONFIG *qsc, unsigned long *startup) {
\r
423 if (! qsc) return 1;
\r
425 switch (qsc->dwStartType) {
\r
426 case SERVICE_DEMAND_START: *startup = NSSM_STARTUP_MANUAL; break;
\r
427 case SERVICE_DISABLED: *startup = NSSM_STARTUP_DISABLED; break;
\r
428 default: *startup = NSSM_STARTUP_AUTOMATIC;
\r
431 if (*startup != NSSM_STARTUP_AUTOMATIC) return 0;
\r
433 /* Check for delayed start. */
\r
434 unsigned long bufsize;
\r
435 unsigned long error;
\r
436 QueryServiceConfig2(service_handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, 0, 0, &bufsize);
\r
437 error = GetLastError();
\r
438 if (error == ERROR_INSUFFICIENT_BUFFER) {
\r
439 SERVICE_DELAYED_AUTO_START_INFO *info = (SERVICE_DELAYED_AUTO_START_INFO *) HeapAlloc(GetProcessHeap(), 0, bufsize);
\r
441 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("SERVICE_DELAYED_AUTO_START_INFO"), _T("get_service_startup()"));
\r
445 if (QueryServiceConfig2(service_handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, (unsigned char *) info, bufsize, &bufsize)) {
\r
446 if (info->fDelayedAutostart) *startup = NSSM_STARTUP_DELAYED;
\r
447 HeapFree(GetProcessHeap(), 0, info);
\r
451 error = GetLastError();
\r
452 if (error != ERROR_INVALID_LEVEL) {
\r
453 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_CONFIG_DELAYED_AUTO_START_INFO"), error_string(error));
\r
458 else if (error != ERROR_INVALID_LEVEL) {
\r
459 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_DELAYED_AUTO_START_INFO"), error_string(error));
\r
466 int get_service_username(const TCHAR *service_name, const QUERY_SERVICE_CONFIG *qsc, TCHAR **username, size_t *usernamelen) {
\r
467 if (! username) return 1;
\r
468 if (! usernamelen) return 1;
\r
473 if (! qsc) return 1;
\r
475 if (qsc->lpServiceStartName[0]) {
\r
476 if (is_localsystem(qsc->lpServiceStartName)) return 0;
\r
478 size_t len = _tcslen(qsc->lpServiceStartName);
\r
479 *username = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(TCHAR));
\r
481 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("username"), _T("get_service_username()"));
\r
485 memmove(*username, qsc->lpServiceStartName, (len + 1) * sizeof(TCHAR));
\r
486 *usernamelen = len;
\r
492 /* Set default values which aren't zero. */
\r
493 void set_nssm_service_defaults(nssm_service_t *service) {
\r
494 if (! service) return;
\r
496 service->type = SERVICE_WIN32_OWN_PROCESS;
\r
497 service->priority = NORMAL_PRIORITY_CLASS;
\r
498 service->stdin_sharing = NSSM_STDIN_SHARING;
\r
499 service->stdin_disposition = NSSM_STDIN_DISPOSITION;
\r
500 service->stdin_flags = NSSM_STDIN_FLAGS;
\r
501 service->stdout_sharing = NSSM_STDOUT_SHARING;
\r
502 service->stdout_disposition = NSSM_STDOUT_DISPOSITION;
\r
503 service->stdout_flags = NSSM_STDOUT_FLAGS;
\r
504 service->stderr_sharing = NSSM_STDERR_SHARING;
\r
505 service->stderr_disposition = NSSM_STDERR_DISPOSITION;
\r
506 service->stderr_flags = NSSM_STDERR_FLAGS;
\r
507 service->throttle_delay = NSSM_RESET_THROTTLE_RESTART;
\r
508 service->stop_method = ~0;
\r
509 service->kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;
\r
510 service->kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;
\r
511 service->kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;
\r
514 /* Allocate and zero memory for a service. */
\r
515 nssm_service_t *alloc_nssm_service() {
\r
516 nssm_service_t *service = (nssm_service_t *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(nssm_service_t));
\r
517 if (! service) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("service"), _T("alloc_nssm_service()"), 0);
\r
521 /* Free memory for a service. */
\r
522 void cleanup_nssm_service(nssm_service_t *service) {
\r
523 if (! service) return;
\r
524 if (service->username) HeapFree(GetProcessHeap(), 0, service->username);
\r
525 if (service->password) {
\r
526 SecureZeroMemory(service->password, service->passwordlen);
\r
527 HeapFree(GetProcessHeap(), 0, service->password);
\r
529 if (service->env) HeapFree(GetProcessHeap(), 0, service->env);
\r
530 if (service->env_extra) HeapFree(GetProcessHeap(), 0, service->env_extra);
\r
531 if (service->handle) CloseHandle(service->handle);
\r
532 if (service->process_handle) CloseHandle(service->process_handle);
\r
533 if (service->wait_handle) UnregisterWait(service->process_handle);
\r
534 if (service->throttle_section_initialised) DeleteCriticalSection(&service->throttle_section);
\r
535 if (service->throttle_timer) CloseHandle(service->throttle_timer);
\r
536 if (service->initial_env) FreeEnvironmentStrings(service->initial_env);
\r
537 HeapFree(GetProcessHeap(), 0, service);
\r
540 /* About to install the service */
\r
541 int pre_install_service(int argc, TCHAR **argv) {
\r
542 nssm_service_t *service = alloc_nssm_service();
\r
543 set_nssm_service_defaults(service);
\r
544 if (argc) _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]);
\r
546 /* Show the dialogue box if we didn't give the service name and path */
\r
547 if (argc < 2) return nssm_gui(IDD_INSTALL, service);
\r
550 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("service"), _T("pre_install_service()"));
\r
553 _sntprintf_s(service->exe, _countof(service->exe), _TRUNCATE, _T("%s"), argv[1]);
\r
555 /* Arguments are optional */
\r
556 size_t flagslen = 0;
\r
559 for (i = 2; i < argc; i++) flagslen += _tcslen(argv[i]) + 1;
\r
560 if (! flagslen) flagslen = 1;
\r
561 if (flagslen > _countof(service->flags)) {
\r
562 print_message(stderr, NSSM_MESSAGE_FLAGS_TOO_LONG);
\r
566 for (i = 2; i < argc; i++) {
\r
567 size_t len = _tcslen(argv[i]);
\r
568 memmove(service->flags + s, argv[i], len * sizeof(TCHAR));
\r
570 if (i < argc - 1) service->flags[s++] = _T(' ');
\r
573 /* Work out directory name */
\r
574 _sntprintf_s(service->dir, _countof(service->dir), _TRUNCATE, _T("%s"), service->exe);
\r
575 strip_basename(service->dir);
\r
577 int ret = install_service(service);
\r
578 cleanup_nssm_service(service);
\r
582 /* About to edit the service. */
\r
583 int pre_edit_service(int argc, TCHAR **argv) {
\r
584 /* Require service name. */
\r
585 if (argc < 2) return usage(1);
\r
587 /* Are we editing on the command line? */
\r
588 enum { MODE_EDITING, MODE_GETTING, MODE_SETTING, MODE_RESETTING } mode = MODE_EDITING;
\r
589 const TCHAR *verb = argv[0];
\r
590 const TCHAR *service_name = argv[1];
\r
591 bool getting = false;
\r
592 bool unsetting = false;
\r
594 /* Minimum number of arguments. */
\r
596 /* Index of first value. */
\r
599 if (str_equiv(verb, _T("get"))) {
\r
601 mode = MODE_GETTING;
\r
603 else if (str_equiv(verb, _T("set"))) {
\r
605 mode = MODE_SETTING;
\r
607 else if (str_equiv(verb, _T("reset")) || str_equiv(verb, _T("unset"))) {
\r
609 mode = MODE_RESETTING;
\r
611 if (argc < mandatory) return usage(1);
\r
613 const TCHAR *parameter = 0;
\r
614 settings_t *setting = 0;
\r
617 /* Validate the parameter. */
\r
618 if (mandatory > 2) {
\r
619 bool additional_mandatory = false;
\r
621 parameter = argv[2];
\r
622 for (i = 0; settings[i].name; i++) {
\r
623 setting = &settings[i];
\r
624 if (! str_equiv(setting->name, parameter)) continue;
\r
625 if (((setting->additional & ADDITIONAL_GETTING) && mode == MODE_GETTING) || ((setting->additional & ADDITIONAL_SETTING) && mode == MODE_SETTING) || ((setting->additional & ADDITIONAL_RESETTING) && mode == MODE_RESETTING)) {
\r
626 additional_mandatory = true;
\r
631 if (! settings[i].name) {
\r
632 print_message(stderr, NSSM_MESSAGE_INVALID_PARAMETER, parameter);
\r
633 for (i = 0; settings[i].name; i++) _ftprintf(stderr, _T("%s\n"), settings[i].name);
\r
638 if (additional_mandatory) {
\r
639 if (argc < mandatory) {
\r
640 print_message(stderr, NSSM_MESSAGE_MISSING_SUBPARAMETER, parameter);
\r
643 additional = argv[3];
\r
646 else if (str_equiv(setting->name, NSSM_NATIVE_OBJECTNAME) && mode == MODE_SETTING) {
\r
647 additional = argv[3];
\r
651 additional = argv[remainder];
\r
652 if (argc < mandatory) return usage(1);
\r
656 nssm_service_t *service = alloc_nssm_service();
\r
657 _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), service_name);
\r
659 /* Open service manager */
\r
660 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
\r
662 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
666 /* Try to open the service */
\r
667 unsigned long access = SERVICE_QUERY_CONFIG;
\r
668 if (mode != MODE_GETTING) access |= SERVICE_CHANGE_CONFIG;
\r
669 service->handle = open_service(services, service->name, access, service->name, _countof(service->name));
\r
670 if (! service->handle) {
\r
671 CloseServiceHandle(services);
\r
675 /* Get system details. */
\r
676 QUERY_SERVICE_CONFIG *qsc = query_service_config(service->name, service->handle);
\r
678 CloseHandle(service->handle);
\r
679 CloseServiceHandle(services);
\r
683 service->type = qsc->dwServiceType;
\r
684 if (! (service->type & SERVICE_WIN32_OWN_PROCESS)) {
\r
685 if (mode != MODE_GETTING) {
\r
686 HeapFree(GetProcessHeap(), 0, qsc);
\r
687 CloseHandle(service->handle);
\r
688 CloseServiceHandle(services);
\r
689 print_message(stderr, NSSM_MESSAGE_CANNOT_EDIT, service->name, NSSM_WIN32_OWN_PROCESS, 0);
\r
694 if (get_service_startup(service->name, service->handle, qsc, &service->startup)) {
\r
695 if (mode != MODE_GETTING) {
\r
696 HeapFree(GetProcessHeap(), 0, qsc);
\r
697 CloseHandle(service->handle);
\r
698 CloseServiceHandle(services);
\r
703 if (get_service_username(service->name, qsc, &service->username, &service->usernamelen)) {
\r
704 if (mode != MODE_GETTING) {
\r
705 HeapFree(GetProcessHeap(), 0, qsc);
\r
706 CloseHandle(service->handle);
\r
707 CloseServiceHandle(services);
\r
712 _sntprintf_s(service->displayname, _countof(service->displayname), _TRUNCATE, _T("%s"), qsc->lpDisplayName);
\r
714 /* Get the canonical service name. We open it case insensitively. */
\r
715 unsigned long bufsize = _countof(service->name);
\r
716 GetServiceKeyName(services, service->displayname, service->name, &bufsize);
\r
718 /* Remember the executable in case it isn't NSSM. */
\r
719 _sntprintf_s(service->image, _countof(service->image), _TRUNCATE, _T("%s"), qsc->lpBinaryPathName);
\r
720 HeapFree(GetProcessHeap(), 0, qsc);
\r
722 /* Get extended system details. */
\r
723 if (get_service_description(service->name, service->handle, _countof(service->description), service->description)) {
\r
724 if (mode != MODE_GETTING) {
\r
725 CloseHandle(service->handle);
\r
726 CloseServiceHandle(services);
\r
731 /* Get NSSM details. */
\r
732 get_parameters(service, 0);
\r
734 CloseServiceHandle(services);
\r
736 if (! service->exe[0]) {
\r
737 service->native = true;
\r
738 if (mode != MODE_GETTING) print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE, service->name, NSSM, service->image);
\r
741 /* Editing with the GUI. */
\r
742 if (mode == MODE_EDITING) {
\r
743 nssm_gui(IDD_EDIT, service);
\r
747 /* Trying to manage App* parameters for a non-NSSM service. */
\r
748 if (! setting->native && service->native) {
\r
749 CloseHandle(service->handle);
\r
750 print_message(stderr, NSSM_MESSAGE_NATIVE_PARAMETER, setting->name, NSSM);
\r
758 if (mode == MODE_GETTING) {
\r
759 if (! service->native) {
\r
760 key = open_registry(service->name, KEY_READ);
\r
761 if (! key) return 4;
\r
764 if (setting->native) ret = get_setting(service->name, service->handle, setting, &value, additional);
\r
765 else ret = get_setting(service->name, key, setting, &value, additional);
\r
767 CloseHandle(service->handle);
\r
771 switch (setting->type) {
\r
772 case REG_EXPAND_SZ:
\r
775 _tprintf(_T("%s\n"), value.string ? value.string : _T(""));
\r
776 HeapFree(GetProcessHeap(), 0, value.string);
\r
780 _tprintf(_T("%u\n"), value.numeric);
\r
784 if (! service->native) RegCloseKey(key);
\r
785 CloseHandle(service->handle);
\r
789 /* Build the value. */
\r
790 if (mode == MODE_RESETTING) {
\r
791 /* Unset the parameter. */
\r
794 else if (remainder == argc) {
\r
798 /* Set the parameter. */
\r
800 size_t delimiterlen = (setting->additional & ADDITIONAL_CRLF) ? 2 : 1;
\r
801 for (i = remainder; i < argc; i++) len += _tcslen(argv[i]) + delimiterlen;
\r
804 value.string = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, len * sizeof(TCHAR));
\r
805 if (! value.string) {
\r
806 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("value"), _T("edit_service()"));
\r
807 CloseHandle(service->handle);
\r
812 for (i = remainder; i < argc; i++) {
\r
813 size_t len = _tcslen(argv[i]);
\r
814 memmove(value.string + s, argv[i], len * sizeof(TCHAR));
\r
816 if (i < argc - 1) {
\r
817 if (setting->additional & ADDITIONAL_CRLF) {
\r
818 value.string[s++] = _T('\r');
\r
819 value.string[s++] = _T('\n');
\r
821 else value.string[s++] = _T(' ');
\r
824 value.string[s] = _T('\0');
\r
827 if (! service->native) {
\r
828 key = open_registry(service->name, KEY_WRITE);
\r
830 if (value.string) HeapFree(GetProcessHeap(), 0, value.string);
\r
835 if (setting->native) ret = set_setting(service->name, service->handle, setting, &value, additional);
\r
836 else ret = set_setting(service->name, key, setting, &value, additional);
\r
837 if (value.string) HeapFree(GetProcessHeap(), 0, value.string);
\r
839 if (! service->native) RegCloseKey(key);
\r
840 CloseHandle(service->handle);
\r
844 if (! service->native) RegCloseKey(key);
\r
845 CloseHandle(service->handle);
\r
850 /* About to remove the service */
\r
851 int pre_remove_service(int argc, TCHAR **argv) {
\r
852 nssm_service_t *service = alloc_nssm_service();
\r
853 set_nssm_service_defaults(service);
\r
854 if (argc) _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]);
\r
856 /* Show dialogue box if we didn't pass service name and "confirm" */
\r
857 if (argc < 2) return nssm_gui(IDD_REMOVE, service);
\r
858 if (str_equiv(argv[1], _T("confirm"))) {
\r
859 int ret = remove_service(service);
\r
860 cleanup_nssm_service(service);
\r
863 print_message(stderr, NSSM_MESSAGE_PRE_REMOVE_SERVICE);
\r
867 /* Install the service */
\r
868 int install_service(nssm_service_t *service) {
\r
869 if (! service) return 1;
\r
871 /* Open service manager */
\r
872 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE);
\r
874 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
875 cleanup_nssm_service(service);
\r
879 /* Get path of this program */
\r
880 GetModuleFileName(0, service->image, _countof(service->image));
\r
882 /* Create the service - settings will be changed in edit_service() */
\r
883 service->handle = CreateService(services, service->name, service->name, SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, service->image, 0, 0, 0, 0, 0);
\r
884 if (! service->handle) {
\r
885 print_message(stderr, NSSM_MESSAGE_CREATESERVICE_FAILED, error_string(GetLastError()));
\r
886 CloseServiceHandle(services);
\r
890 if (edit_service(service, false)) {
\r
891 DeleteService(service->handle);
\r
892 CloseServiceHandle(services);
\r
896 print_message(stdout, NSSM_MESSAGE_SERVICE_INSTALLED, service->name);
\r
899 CloseServiceHandle(services);
\r
904 /* Edit the service. */
\r
905 int edit_service(nssm_service_t *service, bool editing) {
\r
906 if (! service) return 1;
\r
909 The only two valid flags for service type are SERVICE_WIN32_OWN_PROCESS
\r
910 and SERVICE_INTERACTIVE_PROCESS.
\r
912 service->type &= SERVICE_INTERACTIVE_PROCESS;
\r
913 service->type |= SERVICE_WIN32_OWN_PROCESS;
\r
915 /* Startup type. */
\r
916 unsigned long startup;
\r
917 switch (service->startup) {
\r
918 case NSSM_STARTUP_MANUAL: startup = SERVICE_DEMAND_START; break;
\r
919 case NSSM_STARTUP_DISABLED: startup = SERVICE_DISABLED; break;
\r
920 default: startup = SERVICE_AUTO_START;
\r
923 /* Display name. */
\r
924 if (! service->displayname[0]) _sntprintf_s(service->displayname, _countof(service->displayname), _TRUNCATE, _T("%s"), service->name);
\r
927 Username must be NULL if we aren't changing or an account name.
\r
928 We must explicitly use LOCALSYSTEM to change it when we are editing.
\r
929 Password must be NULL if we aren't changing, a password or "".
\r
930 Empty passwords are valid but we won't allow them in the GUI.
\r
932 TCHAR *username = 0;
\r
933 TCHAR *password = 0;
\r
934 if (service->usernamelen) {
\r
935 username = service->username;
\r
936 if (service->passwordlen) password = service->password;
\r
937 else password = _T("");
\r
939 else if (editing) username = NSSM_LOCALSYSTEM_ACCOUNT;
\r
941 if (well_known_username(username)) password = _T("");
\r
943 if (grant_logon_as_service(username)) {
\r
944 print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username);
\r
949 if (! ChangeServiceConfig(service->handle, service->type, startup, SERVICE_NO_CHANGE, 0, 0, 0, 0, username, password, service->displayname)) {
\r
950 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
954 if (service->description[0] || editing) {
\r
955 set_service_description(service->name, service->handle, service->description);
\r
958 SERVICE_DELAYED_AUTO_START_INFO delayed;
\r
959 ZeroMemory(&delayed, sizeof(delayed));
\r
960 if (service->startup == NSSM_STARTUP_DELAYED) delayed.fDelayedAutostart = 1;
\r
961 else delayed.fDelayedAutostart = 0;
\r
962 /* Delayed startup isn't supported until Vista. */
\r
963 if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, &delayed)) {
\r
964 unsigned long error = GetLastError();
\r
965 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
\r
966 if (error != ERROR_INVALID_LEVEL) {
\r
967 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_DELAYED_AUTO_START_INFO_FAILED, service->name, error_string(error), 0);
\r
971 /* Don't mess with parameters which aren't ours. */
\r
972 if (! service->native) {
\r
973 /* Now we need to put the parameters into the registry */
\r
974 if (create_parameters(service, editing)) {
\r
975 print_message(stderr, NSSM_MESSAGE_CREATE_PARAMETERS_FAILED);
\r
979 set_service_recovery(service);
\r
985 /* Control a service. */
\r
986 int control_service(unsigned long control, int argc, TCHAR **argv) {
\r
987 if (argc < 1) return usage(1);
\r
988 TCHAR *service_name = argv[0];
\r
989 TCHAR canonical_name[SERVICE_NAME_LENGTH];
\r
991 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
\r
993 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
997 unsigned long access = SERVICE_QUERY_STATUS;
\r
999 case NSSM_SERVICE_CONTROL_START:
\r
1000 access |= SERVICE_START;
\r
1003 case SERVICE_CONTROL_CONTINUE:
\r
1004 case SERVICE_CONTROL_PAUSE:
\r
1005 access |= SERVICE_PAUSE_CONTINUE;
\r
1008 case SERVICE_CONTROL_STOP:
\r
1009 access |= SERVICE_STOP;
\r
1012 case NSSM_SERVICE_CONTROL_ROTATE:
\r
1013 access |= SERVICE_USER_DEFINED_CONTROL;
\r
1017 SC_HANDLE service_handle = open_service(services, service_name, access, canonical_name, _countof(canonical_name));
\r
1018 if (! service_handle) {
\r
1019 CloseServiceHandle(services);
\r
1024 unsigned long error;
\r
1025 SERVICE_STATUS service_status;
\r
1026 if (control == NSSM_SERVICE_CONTROL_START) {
\r
1027 unsigned long initial_status = SERVICE_STOPPED;
\r
1028 ret = StartService(service_handle, (unsigned long) argc, (const TCHAR **) argv);
\r
1029 error = GetLastError();
\r
1030 CloseServiceHandle(services);
\r
1032 if (error == ERROR_IO_PENDING) {
\r
1034 Older versions of Windows return immediately with ERROR_IO_PENDING
\r
1035 indicate that the operation is still in progress. Newer versions
\r
1036 will return it if there really is a delay.
\r
1039 error = ERROR_SUCCESS;
\r
1043 int response = await_service_control_response(control, service_handle, &service_status, initial_status);
\r
1044 CloseHandle(service_handle);
\r
1047 print_message(stderr, NSSM_MESSAGE_BAD_CONTROL_RESPONSE, canonical_name, service_status_text(service_status.dwCurrentState), service_control_text(control));
\r
1050 else _tprintf(_T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1054 CloseHandle(service_handle);
\r
1055 _ftprintf(stderr, _T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1059 else if (control == SERVICE_CONTROL_INTERROGATE) {
\r
1061 We could actually send an INTERROGATE control but that won't return
\r
1062 any information if the service is stopped and we don't care about
\r
1063 the extra details it might give us in any case. So we'll fake it.
\r
1065 ret = QueryServiceStatus(service_handle, &service_status);
\r
1066 error = GetLastError();
\r
1069 _tprintf(_T("%s\n"), service_status_text(service_status.dwCurrentState));
\r
1073 _ftprintf(stderr, _T("%s: %s\n"), canonical_name, error_string(error));
\r
1078 ret = ControlService(service_handle, control, &service_status);
\r
1079 unsigned long initial_status = service_status.dwCurrentState;
\r
1080 error = GetLastError();
\r
1081 CloseServiceHandle(services);
\r
1083 if (error == ERROR_IO_PENDING) {
\r
1085 error = ERROR_SUCCESS;
\r
1089 int response = await_service_control_response(control, service_handle, &service_status, initial_status);
\r
1090 CloseHandle(service_handle);
\r
1093 print_message(stderr, NSSM_MESSAGE_BAD_CONTROL_RESPONSE, canonical_name, service_status_text(service_status.dwCurrentState), service_control_text(control));
\r
1096 else _tprintf(_T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1100 CloseHandle(service_handle);
\r
1101 _ftprintf(stderr, _T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1102 if (error == ERROR_SERVICE_NOT_ACTIVE) {
\r
1103 if (control == SERVICE_CONTROL_SHUTDOWN || control == SERVICE_CONTROL_STOP) return 0;
\r
1110 /* Remove the service */
\r
1111 int remove_service(nssm_service_t *service) {
\r
1112 if (! service) return 1;
\r
1114 /* Open service manager */
\r
1115 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
\r
1117 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
1121 /* Try to open the service */
\r
1122 service->handle = open_service(services, service->name, DELETE, service->name, _countof(service->name));
\r
1123 if (! service->handle) {
\r
1124 CloseServiceHandle(services);
\r
1128 /* Get the canonical service name. We open it case insensitively. */
\r
1129 unsigned long bufsize = _countof(service->displayname);
\r
1130 GetServiceDisplayName(services, service->name, service->displayname, &bufsize);
\r
1131 bufsize = _countof(service->name);
\r
1132 GetServiceKeyName(services, service->displayname, service->name, &bufsize);
\r
1134 /* Try to delete the service */
\r
1135 if (! DeleteService(service->handle)) {
\r
1136 print_message(stderr, NSSM_MESSAGE_DELETESERVICE_FAILED);
\r
1137 CloseServiceHandle(services);
\r
1142 CloseServiceHandle(services);
\r
1144 print_message(stdout, NSSM_MESSAGE_SERVICE_REMOVED, service->name);
\r
1148 /* Service initialisation */
\r
1149 void WINAPI service_main(unsigned long argc, TCHAR **argv) {
\r
1150 nssm_service_t *service = alloc_nssm_service();
\r
1151 if (! service) return;
\r
1153 if (_sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]) < 0) {
\r
1154 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("service->name"), _T("service_main()"), 0);
\r
1158 /* We can use a condition variable in a critical section on Vista or later. */
\r
1159 if (imports.SleepConditionVariableCS && imports.WakeConditionVariable) use_critical_section = true;
\r
1160 else use_critical_section = false;
\r
1162 /* Initialise status */
\r
1163 ZeroMemory(&service->status, sizeof(service->status));
\r
1164 service->status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;
\r
1165 service->status.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE;
\r
1166 service->status.dwWin32ExitCode = NO_ERROR;
\r
1167 service->status.dwServiceSpecificExitCode = 0;
\r
1168 service->status.dwCheckPoint = 0;
\r
1169 service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;
\r
1171 /* Signal we AREN'T running the server */
\r
1172 service->process_handle = 0;
\r
1175 /* Register control handler */
\r
1176 service->status_handle = RegisterServiceCtrlHandlerEx(NSSM, service_control_handler, (void *) service);
\r
1177 if (! service->status_handle) {
\r
1178 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_REGISTERSERVICECTRLHANDER_FAILED, error_string(GetLastError()), 0);
\r
1182 log_service_control(service->name, 0, true);
\r
1184 service->status.dwCurrentState = SERVICE_START_PENDING;
\r
1185 service->status.dwWaitHint = service->throttle_delay + NSSM_WAITHINT_MARGIN;
\r
1186 SetServiceStatus(service->status_handle, &service->status);
\r
1189 /* Try to create the exit action parameters; we don't care if it fails */
\r
1190 create_exit_action(service->name, exit_action_strings[0], false);
\r
1192 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT);
\r
1194 service->handle = open_service(services, service->name, SERVICE_CHANGE_CONFIG, 0, 0);
\r
1195 set_service_recovery(service);
\r
1196 CloseServiceHandle(services);
\r
1200 /* Used for signalling a resume if the service pauses when throttled. */
\r
1201 if (use_critical_section) {
\r
1202 InitializeCriticalSection(&service->throttle_section);
\r
1203 service->throttle_section_initialised = true;
\r
1206 service->throttle_timer = CreateWaitableTimer(0, 1, 0);
\r
1207 if (! service->throttle_timer) {
\r
1208 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_CREATEWAITABLETIMER_FAILED, service->name, error_string(GetLastError()), 0);
\r
1212 /* Remember our initial environment. */
\r
1213 service->initial_env = GetEnvironmentStrings();
\r
1215 monitor_service(service);
\r
1218 /* Make sure service recovery actions are taken where necessary */
\r
1219 void set_service_recovery(nssm_service_t *service) {
\r
1220 SERVICE_FAILURE_ACTIONS_FLAG flag;
\r
1221 ZeroMemory(&flag, sizeof(flag));
\r
1222 flag.fFailureActionsOnNonCrashFailures = true;
\r
1224 /* This functionality was added in Vista so the call may fail */
\r
1225 if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, &flag)) {
\r
1226 unsigned long error = GetLastError();
\r
1227 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
\r
1228 if (error != ERROR_INVALID_LEVEL) {
\r
1229 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_FAILURE_ACTIONS_FAILED, service->name, error_string(error), 0);
\r
1234 int monitor_service(nssm_service_t *service) {
\r
1235 /* Set service status to started */
\r
1236 int ret = start_service(service);
\r
1239 _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%d"), ret);
\r
1240 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_START_SERVICE_FAILED, service->exe, service->name, ret, 0);
\r
1243 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_STARTED_SERVICE, service->exe, service->flags, service->name, service->dir, 0);
\r
1245 /* Monitor service */
\r
1246 if (! RegisterWaitForSingleObject(&service->wait_handle, service->process_handle, end_service, (void *) service, INFINITE, WT_EXECUTEONLYONCE | WT_EXECUTELONGFUNCTION)) {
\r
1247 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_REGISTERWAITFORSINGLEOBJECT_FAILED, service->name, service->exe, error_string(GetLastError()), 0);
\r
1253 TCHAR *service_control_text(unsigned long control) {
\r
1254 switch (control) {
\r
1255 /* HACK: there is no SERVICE_CONTROL_START constant */
\r
1256 case NSSM_SERVICE_CONTROL_START: return _T("START");
\r
1257 case SERVICE_CONTROL_STOP: return _T("STOP");
\r
1258 case SERVICE_CONTROL_SHUTDOWN: return _T("SHUTDOWN");
\r
1259 case SERVICE_CONTROL_PAUSE: return _T("PAUSE");
\r
1260 case SERVICE_CONTROL_CONTINUE: return _T("CONTINUE");
\r
1261 case SERVICE_CONTROL_INTERROGATE: return _T("INTERROGATE");
\r
1262 case NSSM_SERVICE_CONTROL_ROTATE: return _T("ROTATE");
\r
1263 default: return 0;
\r
1267 TCHAR *service_status_text(unsigned long status) {
\r
1269 case SERVICE_STOPPED: return _T("SERVICE_STOPPED");
\r
1270 case SERVICE_START_PENDING: return _T("SERVICE_START_PENDING");
\r
1271 case SERVICE_STOP_PENDING: return _T("SERVICE_STOP_PENDING");
\r
1272 case SERVICE_RUNNING: return _T("SERVICE_RUNNING");
\r
1273 case SERVICE_CONTINUE_PENDING: return _T("SERVICE_CONTINUE_PENDING");
\r
1274 case SERVICE_PAUSE_PENDING: return _T("SERVICE_PAUSE_PENDING");
\r
1275 case SERVICE_PAUSED: return _T("SERVICE_PAUSED");
\r
1276 default: return 0;
\r
1280 void log_service_control(TCHAR *service_name, unsigned long control, bool handled) {
\r
1281 TCHAR *text = service_control_text(control);
\r
1282 unsigned long event;
\r
1285 /* "0x" + 8 x hex + NULL */
\r
1286 text = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, 11 * sizeof(TCHAR));
\r
1288 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);
\r
1291 if (_sntprintf_s(text, 11, _TRUNCATE, _T("0x%08x"), control) < 0) {
\r
1292 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);
\r
1293 HeapFree(GetProcessHeap(), 0, text);
\r
1297 event = NSSM_EVENT_SERVICE_CONTROL_UNKNOWN;
\r
1299 else if (handled) event = NSSM_EVENT_SERVICE_CONTROL_HANDLED;
\r
1300 else event = NSSM_EVENT_SERVICE_CONTROL_NOT_HANDLED;
\r
1302 log_event(EVENTLOG_INFORMATION_TYPE, event, service_name, text, 0);
\r
1304 if (event == NSSM_EVENT_SERVICE_CONTROL_UNKNOWN) {
\r
1305 HeapFree(GetProcessHeap(), 0, text);
\r
1309 /* Service control handler */
\r
1310 unsigned long WINAPI service_control_handler(unsigned long control, unsigned long event, void *data, void *context) {
\r
1311 nssm_service_t *service = (nssm_service_t *) context;
\r
1313 switch (control) {
\r
1314 case SERVICE_CONTROL_INTERROGATE:
\r
1315 /* We always keep the service status up-to-date so this is a no-op. */
\r
1318 case SERVICE_CONTROL_SHUTDOWN:
\r
1319 case SERVICE_CONTROL_STOP:
\r
1320 log_service_control(service->name, control, true);
\r
1322 We MUST acknowledge the stop request promptly but we're committed to
\r
1323 waiting for the application to exit. Spawn a new thread to wait
\r
1324 while we acknowledge the request.
\r
1326 if (! CreateThread(NULL, 0, shutdown_service, context, 0, NULL)) {
\r
1327 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETHREAD_FAILED, error_string(GetLastError()), 0);
\r
1330 We couldn't create a thread to tidy up so we'll have to force the tidyup
\r
1331 to complete in time in this thread.
\r
1333 service->kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;
\r
1334 service->kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;
\r
1335 service->kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;
\r
1337 stop_service(service, 0, true, true);
\r
1341 case SERVICE_CONTROL_CONTINUE:
\r
1342 log_service_control(service->name, control, true);
\r
1343 service->throttle = 0;
\r
1344 if (use_critical_section) imports.WakeConditionVariable(&service->throttle_condition);
\r
1346 if (! service->throttle_timer) return ERROR_CALL_NOT_IMPLEMENTED;
\r
1347 ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));
\r
1348 SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);
\r
1350 /* We can't continue if the application is running! */
\r
1351 if (! service->process_handle) service->status.dwCurrentState = SERVICE_CONTINUE_PENDING;
\r
1352 service->status.dwWaitHint = throttle_milliseconds(service->throttle) + NSSM_WAITHINT_MARGIN;
\r
1353 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESET_THROTTLE, service->name, 0);
\r
1354 SetServiceStatus(service->status_handle, &service->status);
\r
1357 case SERVICE_CONTROL_PAUSE:
\r
1359 We don't accept pause messages but it isn't possible to register
\r
1360 only for continue messages so we have to handle this case.
\r
1362 log_service_control(service->name, control, false);
\r
1363 return ERROR_CALL_NOT_IMPLEMENTED;
\r
1365 case NSSM_SERVICE_CONTROL_ROTATE:
\r
1366 log_service_control(service->name, control, true);
\r
1367 if (service->rotate_stdout_online == NSSM_ROTATE_ONLINE) service->rotate_stdout_online = NSSM_ROTATE_ONLINE_ASAP;
\r
1368 if (service->rotate_stderr_online == NSSM_ROTATE_ONLINE) service->rotate_stderr_online = NSSM_ROTATE_ONLINE_ASAP;
\r
1372 /* Unknown control */
\r
1373 log_service_control(service->name, control, false);
\r
1374 return ERROR_CALL_NOT_IMPLEMENTED;
\r
1377 /* Start the service */
\r
1378 int start_service(nssm_service_t *service) {
\r
1379 service->stopping = false;
\r
1380 service->allow_restart = true;
\r
1382 if (service->process_handle) return 0;
\r
1384 /* Allocate a STARTUPINFO structure for a new process */
\r
1386 ZeroMemory(&si, sizeof(si));
\r
1387 si.cb = sizeof(si);
\r
1389 /* Allocate a PROCESSINFO structure for the process */
\r
1390 PROCESS_INFORMATION pi;
\r
1391 ZeroMemory(&pi, sizeof(pi));
\r
1393 /* Get startup parameters */
\r
1394 int ret = get_parameters(service, &si);
\r
1396 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_PARAMETERS_FAILED, service->name, 0);
\r
1397 return stop_service(service, 2, true, true);
\r
1400 /* Launch executable with arguments */
\r
1401 TCHAR cmd[CMD_LENGTH];
\r
1402 if (_sntprintf_s(cmd, _countof(cmd), _TRUNCATE, _T("\"%s\" %s"), service->exe, service->flags) < 0) {
\r
1403 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("command line"), _T("start_service"), 0);
\r
1404 return stop_service(service, 2, true, true);
\r
1407 throttle_restart(service);
\r
1409 /* Set the environment. */
\r
1410 if (service->env) duplicate_environment(service->env);
\r
1411 if (service->env_extra) set_environment_block(service->env_extra);
\r
1413 /* Set up I/O redirection. */
\r
1414 if (get_output_handles(service, &si)) {
\r
1415 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_OUTPUT_HANDLES_FAILED, service->name, 0);
\r
1416 if (! service->no_console) FreeConsole();
\r
1417 close_output_handles(&si);
\r
1418 return stop_service(service, 4, true, true);
\r
1421 bool inherit_handles = false;
\r
1422 if (si.dwFlags & STARTF_USESTDHANDLES) inherit_handles = true;
\r
1423 unsigned long flags = service->priority & priority_mask();
\r
1424 if (service->affinity) flags |= CREATE_SUSPENDED;
\r
1425 if (! CreateProcess(0, cmd, 0, 0, inherit_handles, flags, 0, service->dir, &si, &pi)) {
\r
1426 unsigned long exitcode = 3;
\r
1427 unsigned long error = GetLastError();
\r
1428 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service->name, service->exe, error_string(error), 0);
\r
1429 close_output_handles(&si);
\r
1430 duplicate_environment(service->initial_env);
\r
1431 return stop_service(service, exitcode, true, true);
\r
1433 service->process_handle = pi.hProcess;
\r
1434 service->pid = pi.dwProcessId;
\r
1436 if (get_process_creation_time(service->process_handle, &service->creation_time)) ZeroMemory(&service->creation_time, sizeof(service->creation_time));
\r
1438 close_output_handles(&si);
\r
1440 if (! service->no_console) FreeConsole();
\r
1442 /* Restore our environment. */
\r
1443 duplicate_environment(service->initial_env);
\r
1445 if (service->affinity) {
\r
1447 We are explicitly storing service->affinity as a 64-bit unsigned integer
\r
1448 so that we can parse it regardless of whether we're running in 32-bit
\r
1449 or 64-bit mode. The arguments to SetProcessAffinityMask(), however, are
\r
1450 defined as type DWORD_PTR and hence limited to 32 bits on a 32-bit system
\r
1451 (or when running the 32-bit NSSM).
\r
1453 The result is a lot of seemingly-unnecessary casting throughout the code
\r
1454 and potentially confusion when we actually try to start the service.
\r
1455 Having said that, however, it's unlikely that we're actually going to
\r
1456 run in 32-bit mode on a system which has more than 32 CPUs so the
\r
1457 likelihood of seeing a confusing situation is somewhat diminished.
\r
1459 DWORD_PTR affinity, system_affinity;
\r
1461 if (GetProcessAffinityMask(service->process_handle, &affinity, &system_affinity)) affinity = service->affinity & system_affinity;
\r
1463 affinity = (DWORD_PTR) service->affinity;
\r
1464 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GETPROCESSAFFINITYMASK_FAILED, service->name, error_string(GetLastError()), 0);
\r
1467 if (! SetProcessAffinityMask(service->process_handle, affinity)) {
\r
1468 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_SETPROCESSAFFINITYMASK_FAILED, service->name, error_string(GetLastError()), 0);
\r
1471 ResumeThread(pi.hThread);
\r
1475 Wait for a clean startup before changing the service status to RUNNING
\r
1476 but be mindful of the fact that we are blocking the service control manager
\r
1477 so abandon the wait before too much time has elapsed.
\r
1479 unsigned long delay = service->throttle_delay;
\r
1480 if (delay > NSSM_SERVICE_STATUS_DEADLINE) {
\r
1481 TCHAR delay_milliseconds[16];
\r
1482 _sntprintf_s(delay_milliseconds, _countof(delay_milliseconds), _TRUNCATE, _T("%lu"), delay);
\r
1483 TCHAR deadline_milliseconds[16];
\r
1484 _sntprintf_s(deadline_milliseconds, _countof(deadline_milliseconds), _TRUNCATE, _T("%lu"), NSSM_SERVICE_STATUS_DEADLINE);
\r
1485 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_STARTUP_DELAY_TOO_LONG, service->name, delay_milliseconds, NSSM, deadline_milliseconds, 0);
\r
1486 delay = NSSM_SERVICE_STATUS_DEADLINE;
\r
1488 unsigned long deadline = WaitForSingleObject(service->process_handle, delay);
\r
1490 /* Signal successful start */
\r
1491 service->status.dwCurrentState = SERVICE_RUNNING;
\r
1492 SetServiceStatus(service->status_handle, &service->status);
\r
1494 /* Continue waiting for a clean startup. */
\r
1495 if (deadline == WAIT_TIMEOUT) {
\r
1496 if (service->throttle_delay > delay) {
\r
1497 if (WaitForSingleObject(service->process_handle, service->throttle_delay - delay) == WAIT_TIMEOUT) service->throttle = 0;
\r
1499 else service->throttle = 0;
\r
1502 /* Ensure the restart delay is always applied. */
\r
1503 if (service->restart_delay && ! service->throttle) service->throttle++;
\r
1508 /* Stop the service */
\r
1509 int stop_service(nssm_service_t *service, unsigned long exitcode, bool graceful, bool default_action) {
\r
1510 service->allow_restart = false;
\r
1511 if (service->wait_handle) {
\r
1512 UnregisterWait(service->wait_handle);
\r
1513 service->wait_handle = 0;
\r
1516 service->rotate_stdout_online = service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;
\r
1518 if (default_action && ! exitcode && ! graceful) {
\r
1519 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_GRACEFUL_SUICIDE, service->name, service->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
1523 /* Signal we are stopping */
\r
1525 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
1526 service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;
\r
1527 SetServiceStatus(service->status_handle, &service->status);
\r
1530 /* Nothing to do if service isn't running */
\r
1531 if (service->pid) {
\r
1532 /* Shut down service */
\r
1533 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_TERMINATEPROCESS, service->name, service->exe, 0);
\r
1534 kill_process(service, service->process_handle, service->pid, 0);
\r
1536 else log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_PROCESS_ALREADY_STOPPED, service->name, service->exe, 0);
\r
1538 end_service((void *) service, true);
\r
1540 /* Signal we stopped */
\r
1542 service->status.dwCurrentState = SERVICE_STOPPED;
\r
1544 service->status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
\r
1545 service->status.dwServiceSpecificExitCode = exitcode;
\r
1548 service->status.dwWin32ExitCode = NO_ERROR;
\r
1549 service->status.dwServiceSpecificExitCode = 0;
\r
1551 SetServiceStatus(service->status_handle, &service->status);
\r
1557 /* Callback function triggered when the server exits */
\r
1558 void CALLBACK end_service(void *arg, unsigned char why) {
\r
1559 nssm_service_t *service = (nssm_service_t *) arg;
\r
1561 if (service->stopping) return;
\r
1563 service->stopping = true;
\r
1565 service->rotate_stdout_online = service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;
\r
1567 /* Use now as a dummy exit time. */
\r
1568 GetSystemTimeAsFileTime(&service->exit_time);
\r
1570 /* Check exit code */
\r
1571 unsigned long exitcode = 0;
\r
1573 if (service->process_handle) {
\r
1574 GetExitCodeProcess(service->process_handle, &exitcode);
\r
1575 /* Check real exit time. */
\r
1576 if (exitcode != STILL_ACTIVE) get_process_exit_time(service->process_handle, &service->exit_time);
\r
1577 CloseHandle(service->process_handle);
\r
1580 service->process_handle = 0;
\r
1583 Log that the service ended BEFORE logging about killing the process
\r
1584 tree. See below for the possible values of the why argument.
\r
1587 _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), exitcode);
\r
1588 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ENDED_SERVICE, service->exe, service->name, code, 0);
\r
1592 if (exitcode == STILL_ACTIVE) exitcode = 0;
\r
1593 if (service->pid) kill_process_tree(service, service->pid, exitcode, service->pid);
\r
1597 The why argument is true if our wait timed out or false otherwise.
\r
1598 Our wait is infinite so why will never be true when called by the system.
\r
1599 If it is indeed true, assume we were called from stop_service() because
\r
1600 this is a controlled shutdown, and don't take any restart action.
\r
1603 if (! service->allow_restart) return;
\r
1605 /* What action should we take? */
\r
1606 int action = NSSM_EXIT_RESTART;
\r
1607 TCHAR action_string[ACTION_LEN];
\r
1608 bool default_action;
\r
1609 if (! get_exit_action(service->name, &exitcode, action_string, &default_action)) {
\r
1610 for (int i = 0; exit_action_strings[i]; i++) {
\r
1611 if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
1619 /* Try to restart the service or return failure code to service manager */
\r
1620 case NSSM_EXIT_RESTART:
\r
1621 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_RESTART, service->name, code, exit_action_strings[action], service->exe, 0);
\r
1622 while (monitor_service(service)) {
\r
1623 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_RESTART_SERVICE_FAILED, service->exe, service->name, 0);
\r
1628 /* Do nothing, just like srvany would */
\r
1629 case NSSM_EXIT_IGNORE:
\r
1630 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_IGNORE, service->name, code, exit_action_strings[action], service->exe, 0);
\r
1634 /* Tell the service manager we are finished */
\r
1635 case NSSM_EXIT_REALLY:
\r
1636 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_REALLY, service->name, code, exit_action_strings[action], 0);
\r
1637 stop_service(service, exitcode, true, default_action);
\r
1640 /* Fake a crash so pre-Vista service managers will run recovery actions. */
\r
1641 case NSSM_EXIT_UNCLEAN:
\r
1642 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_UNCLEAN, service->name, code, exit_action_strings[action], 0);
\r
1643 stop_service(service, exitcode, false, default_action);
\r
1650 void throttle_restart(nssm_service_t *service) {
\r
1651 /* This can't be a restart if the service is already running. */
\r
1652 if (! service->throttle++) return;
\r
1655 unsigned long throttle_ms = throttle_milliseconds(service->throttle);
\r
1656 TCHAR threshold[8], milliseconds[8];
\r
1658 if (service->restart_delay > throttle_ms) ms = service->restart_delay;
\r
1659 else ms = throttle_ms;
\r
1661 if (service->throttle > 7) service->throttle = 8;
\r
1663 _sntprintf_s(milliseconds, _countof(milliseconds), _TRUNCATE, _T("%lu"), ms);
\r
1665 if (service->throttle == 1 && service->restart_delay > throttle_ms) log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESTART_DELAY, service->name, milliseconds, 0);
\r
1667 _sntprintf_s(threshold, _countof(threshold), _TRUNCATE, _T("%lu"), service->throttle_delay);
\r
1668 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_THROTTLED, service->name, threshold, milliseconds, 0);
\r
1671 if (use_critical_section) EnterCriticalSection(&service->throttle_section);
\r
1672 else if (service->throttle_timer) {
\r
1673 ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));
\r
1674 service->throttle_duetime.QuadPart = 0 - (ms * 10000LL);
\r
1675 SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);
\r
1678 service->status.dwCurrentState = SERVICE_PAUSED;
\r
1679 SetServiceStatus(service->status_handle, &service->status);
\r
1681 if (use_critical_section) {
\r
1682 imports.SleepConditionVariableCS(&service->throttle_condition, &service->throttle_section, ms);
\r
1683 LeaveCriticalSection(&service->throttle_section);
\r
1686 if (service->throttle_timer) WaitForSingleObject(service->throttle_timer, INFINITE);
\r
1692 When responding to a stop (or any other) request we need to set dwWaitHint to
\r
1693 the number of milliseconds we expect the operation to take, and optionally
\r
1694 increase dwCheckPoint. If dwWaitHint milliseconds elapses without the
\r
1695 operation completing or dwCheckPoint increasing, the system will consider the
\r
1696 service to be hung.
\r
1698 However the system will consider the service to be hung after 30000
\r
1699 milliseconds regardless of the value of dwWaitHint if dwCheckPoint has not
\r
1700 changed. Therefore if we want to wait longer than that we must periodically
\r
1701 increase dwCheckPoint.
\r
1703 Furthermore, it will consider the service to be hung after 60000 milliseconds
\r
1704 regardless of the value of dwCheckPoint unless dwWaitHint is increased every
\r
1705 time dwCheckPoint is also increased.
\r
1707 Our strategy then is to retrieve the initial dwWaitHint and wait for
\r
1708 NSSM_SERVICE_STATUS_DEADLINE milliseconds. If the process is still running
\r
1709 and we haven't finished waiting we increment dwCheckPoint and add whichever is
\r
1710 smaller of NSSM_SERVICE_STATUS_DEADLINE or the remaining timeout to
\r
1713 Only doing both these things will prevent the system from killing the service.
\r
1715 Returns: 1 if the wait timed out.
\r
1716 0 if the wait completed.
\r
1719 int await_shutdown(nssm_service_t *service, TCHAR *function_name, unsigned long timeout) {
\r
1720 unsigned long interval;
\r
1721 unsigned long waithint;
\r
1722 unsigned long ret;
\r
1723 unsigned long waited;
\r
1724 TCHAR interval_milliseconds[16];
\r
1725 TCHAR timeout_milliseconds[16];
\r
1726 TCHAR waited_milliseconds[16];
\r
1727 TCHAR *function = function_name;
\r
1729 /* Add brackets to function name. */
\r
1730 size_t funclen = _tcslen(function_name) + 3;
\r
1731 TCHAR *func = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, funclen * sizeof(TCHAR));
\r
1733 if (_sntprintf_s(func, funclen, _TRUNCATE, _T("%s()"), function_name) > -1) function = func;
\r
1736 _sntprintf_s(timeout_milliseconds, _countof(timeout_milliseconds), _TRUNCATE, _T("%lu"), timeout);
\r
1738 waithint = service->status.dwWaitHint;
\r
1740 while (waited < timeout) {
\r
1741 interval = timeout - waited;
\r
1742 if (interval > NSSM_SERVICE_STATUS_DEADLINE) interval = NSSM_SERVICE_STATUS_DEADLINE;
\r
1744 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
1745 service->status.dwWaitHint += interval;
\r
1746 service->status.dwCheckPoint++;
\r
1747 SetServiceStatus(service->status_handle, &service->status);
\r
1750 _sntprintf_s(waited_milliseconds, _countof(waited_milliseconds), _TRUNCATE, _T("%lu"), waited);
\r
1751 _sntprintf_s(interval_milliseconds, _countof(interval_milliseconds), _TRUNCATE, _T("%lu"), interval);
\r
1752 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_AWAITING_SHUTDOWN, function, service->name, waited_milliseconds, interval_milliseconds, timeout_milliseconds, 0);
\r
1755 switch (WaitForSingleObject(service->process_handle, interval)) {
\r
1756 case WAIT_OBJECT_0:
\r
1760 case WAIT_TIMEOUT:
\r
1769 waited += interval;
\r
1773 if (func) HeapFree(GetProcessHeap(), 0, func);
\r