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() {
\r
257 SC_HANDLE ret = OpenSCManager(0, SERVICES_ACTIVE_DATABASE, SC_MANAGER_ALL_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, TCHAR *canonical_name, unsigned long canonical_namelen) {
\r
268 SC_HANDLE service_handle = OpenService(services, service_name, SERVICE_ALL_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, 0, 0);
\r
341 /* Recurse so we can get an error message. */
\r
342 return open_service(services, service_name, 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) CloseServiceHandle(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();
\r
662 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
666 /* Try to open the service */
\r
667 service->handle = open_service(services, service->name, service->name, _countof(service->name));
\r
668 if (! service->handle) {
\r
669 CloseServiceHandle(services);
\r
673 /* Get system details. */
\r
674 QUERY_SERVICE_CONFIG *qsc = query_service_config(service->name, service->handle);
\r
676 CloseHandle(service->handle);
\r
677 CloseServiceHandle(services);
\r
681 service->type = qsc->dwServiceType;
\r
682 if (! (service->type & SERVICE_WIN32_OWN_PROCESS)) {
\r
683 if (mode != MODE_GETTING) {
\r
684 HeapFree(GetProcessHeap(), 0, qsc);
\r
685 CloseHandle(service->handle);
\r
686 CloseServiceHandle(services);
\r
687 print_message(stderr, NSSM_MESSAGE_CANNOT_EDIT, service->name, NSSM_WIN32_OWN_PROCESS, 0);
\r
692 if (get_service_startup(service->name, service->handle, qsc, &service->startup)) {
\r
693 if (mode != MODE_GETTING) {
\r
694 HeapFree(GetProcessHeap(), 0, qsc);
\r
695 CloseHandle(service->handle);
\r
696 CloseServiceHandle(services);
\r
701 if (get_service_username(service->name, qsc, &service->username, &service->usernamelen)) {
\r
702 if (mode != MODE_GETTING) {
\r
703 HeapFree(GetProcessHeap(), 0, qsc);
\r
704 CloseHandle(service->handle);
\r
705 CloseServiceHandle(services);
\r
710 _sntprintf_s(service->displayname, _countof(service->displayname), _TRUNCATE, _T("%s"), qsc->lpDisplayName);
\r
712 /* Get the canonical service name. We open it case insensitively. */
\r
713 unsigned long bufsize = _countof(service->name);
\r
714 GetServiceKeyName(services, service->displayname, service->name, &bufsize);
\r
716 /* Remember the executable in case it isn't NSSM. */
\r
717 _sntprintf_s(service->image, _countof(service->image), _TRUNCATE, _T("%s"), qsc->lpBinaryPathName);
\r
718 HeapFree(GetProcessHeap(), 0, qsc);
\r
720 /* Get extended system details. */
\r
721 if (get_service_description(service->name, service->handle, _countof(service->description), service->description)) {
\r
722 if (mode != MODE_GETTING) {
\r
723 CloseHandle(service->handle);
\r
724 CloseServiceHandle(services);
\r
729 /* Get NSSM details. */
\r
730 get_parameters(service, 0);
\r
732 CloseServiceHandle(services);
\r
734 if (! service->exe[0]) {
\r
735 service->native = true;
\r
736 if (mode != MODE_GETTING) print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE, service->name, NSSM, service->image);
\r
739 /* Editing with the GUI. */
\r
740 if (mode == MODE_EDITING) {
\r
741 nssm_gui(IDD_EDIT, service);
\r
745 /* Trying to manage App* parameters for a non-NSSM service. */
\r
746 if (! setting->native && service->native) {
\r
747 CloseHandle(service->handle);
\r
748 print_message(stderr, NSSM_MESSAGE_NATIVE_PARAMETER, setting->name, NSSM);
\r
756 if (mode == MODE_GETTING) {
\r
757 if (! service->native) {
\r
758 key = open_registry(service->name, KEY_READ);
\r
759 if (! key) return 4;
\r
762 if (setting->native) ret = get_setting(service->name, service->handle, setting, &value, additional);
\r
763 else ret = get_setting(service->name, key, setting, &value, additional);
\r
765 CloseHandle(service->handle);
\r
769 switch (setting->type) {
\r
770 case REG_EXPAND_SZ:
\r
773 _tprintf(_T("%s\n"), value.string ? value.string : _T(""));
\r
774 HeapFree(GetProcessHeap(), 0, value.string);
\r
778 _tprintf(_T("%u\n"), value.numeric);
\r
782 if (! service->native) RegCloseKey(key);
\r
783 CloseHandle(service->handle);
\r
787 /* Build the value. */
\r
788 if (mode == MODE_RESETTING) {
\r
789 /* Unset the parameter. */
\r
792 else if (remainder == argc) {
\r
796 /* Set the parameter. */
\r
798 size_t delimiterlen = (setting->additional & ADDITIONAL_CRLF) ? 2 : 1;
\r
799 for (i = remainder; i < argc; i++) len += _tcslen(argv[i]) + delimiterlen;
\r
802 value.string = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, len * sizeof(TCHAR));
\r
803 if (! value.string) {
\r
804 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("value"), _T("edit_service()"));
\r
805 CloseHandle(service->handle);
\r
810 for (i = remainder; i < argc; i++) {
\r
811 size_t len = _tcslen(argv[i]);
\r
812 memmove(value.string + s, argv[i], len * sizeof(TCHAR));
\r
814 if (i < argc - 1) {
\r
815 if (setting->additional & ADDITIONAL_CRLF) {
\r
816 value.string[s++] = _T('\r');
\r
817 value.string[s++] = _T('\n');
\r
819 else value.string[s++] = _T(' ');
\r
822 value.string[s] = _T('\0');
\r
825 if (! service->native) {
\r
826 key = open_registry(service->name, KEY_WRITE);
\r
828 if (value.string) HeapFree(GetProcessHeap(), 0, value.string);
\r
833 if (setting->native) ret = set_setting(service->name, service->handle, setting, &value, additional);
\r
834 else ret = set_setting(service->name, key, setting, &value, additional);
\r
835 if (value.string) HeapFree(GetProcessHeap(), 0, value.string);
\r
837 if (! service->native) RegCloseKey(key);
\r
838 CloseHandle(service->handle);
\r
842 if (! service->native) RegCloseKey(key);
\r
843 CloseHandle(service->handle);
\r
848 /* About to remove the service */
\r
849 int pre_remove_service(int argc, TCHAR **argv) {
\r
850 nssm_service_t *service = alloc_nssm_service();
\r
851 set_nssm_service_defaults(service);
\r
852 if (argc) _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]);
\r
854 /* Show dialogue box if we didn't pass service name and "confirm" */
\r
855 if (argc < 2) return nssm_gui(IDD_REMOVE, service);
\r
856 if (str_equiv(argv[1], _T("confirm"))) {
\r
857 int ret = remove_service(service);
\r
858 cleanup_nssm_service(service);
\r
861 print_message(stderr, NSSM_MESSAGE_PRE_REMOVE_SERVICE);
\r
865 /* Install the service */
\r
866 int install_service(nssm_service_t *service) {
\r
867 if (! service) return 1;
\r
869 /* Open service manager */
\r
870 SC_HANDLE services = open_service_manager();
\r
872 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
873 cleanup_nssm_service(service);
\r
877 /* Get path of this program */
\r
878 GetModuleFileName(0, service->image, _countof(service->image));
\r
880 /* Create the service - settings will be changed in edit_service() */
\r
881 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
882 if (! service->handle) {
\r
883 print_message(stderr, NSSM_MESSAGE_CREATESERVICE_FAILED, error_string(GetLastError()));
\r
884 CloseServiceHandle(services);
\r
888 if (edit_service(service, false)) {
\r
889 DeleteService(service->handle);
\r
890 CloseServiceHandle(services);
\r
894 print_message(stdout, NSSM_MESSAGE_SERVICE_INSTALLED, service->name);
\r
897 CloseServiceHandle(services);
\r
902 /* Edit the service. */
\r
903 int edit_service(nssm_service_t *service, bool editing) {
\r
904 if (! service) return 1;
\r
907 The only two valid flags for service type are SERVICE_WIN32_OWN_PROCESS
\r
908 and SERVICE_INTERACTIVE_PROCESS.
\r
910 service->type &= SERVICE_INTERACTIVE_PROCESS;
\r
911 service->type |= SERVICE_WIN32_OWN_PROCESS;
\r
913 /* Startup type. */
\r
914 unsigned long startup;
\r
915 switch (service->startup) {
\r
916 case NSSM_STARTUP_MANUAL: startup = SERVICE_DEMAND_START; break;
\r
917 case NSSM_STARTUP_DISABLED: startup = SERVICE_DISABLED; break;
\r
918 default: startup = SERVICE_AUTO_START;
\r
921 /* Display name. */
\r
922 if (! service->displayname[0]) _sntprintf_s(service->displayname, _countof(service->displayname), _TRUNCATE, _T("%s"), service->name);
\r
925 Username must be NULL if we aren't changing or an account name.
\r
926 We must explicitly use LOCALSYSTEM to change it when we are editing.
\r
927 Password must be NULL if we aren't changing, a password or "".
\r
928 Empty passwords are valid but we won't allow them in the GUI.
\r
930 TCHAR *username = 0;
\r
931 TCHAR *password = 0;
\r
932 if (service->usernamelen) {
\r
933 username = service->username;
\r
934 if (service->passwordlen) password = service->password;
\r
935 else password = _T("");
\r
937 else if (editing) username = NSSM_LOCALSYSTEM_ACCOUNT;
\r
939 if (well_known_username(username)) password = _T("");
\r
941 if (grant_logon_as_service(username)) {
\r
942 print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username);
\r
947 if (! ChangeServiceConfig(service->handle, service->type, startup, SERVICE_NO_CHANGE, 0, 0, 0, 0, username, password, service->displayname)) {
\r
948 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
952 if (service->description[0] || editing) {
\r
953 set_service_description(service->name, service->handle, service->description);
\r
956 SERVICE_DELAYED_AUTO_START_INFO delayed;
\r
957 ZeroMemory(&delayed, sizeof(delayed));
\r
958 if (service->startup == NSSM_STARTUP_DELAYED) delayed.fDelayedAutostart = 1;
\r
959 else delayed.fDelayedAutostart = 0;
\r
960 /* Delayed startup isn't supported until Vista. */
\r
961 if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, &delayed)) {
\r
962 unsigned long error = GetLastError();
\r
963 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
\r
964 if (error != ERROR_INVALID_LEVEL) {
\r
965 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_DELAYED_AUTO_START_INFO_FAILED, service->name, error_string(error), 0);
\r
969 /* Don't mess with parameters which aren't ours. */
\r
970 if (! service->native) {
\r
971 /* Now we need to put the parameters into the registry */
\r
972 if (create_parameters(service, editing)) {
\r
973 print_message(stderr, NSSM_MESSAGE_CREATE_PARAMETERS_FAILED);
\r
977 set_service_recovery(service);
\r
983 /* Control a service. */
\r
984 int control_service(unsigned long control, int argc, TCHAR **argv) {
\r
985 if (argc < 1) return usage(1);
\r
986 TCHAR *service_name = argv[0];
\r
987 TCHAR canonical_name[SERVICE_NAME_LENGTH];
\r
989 SC_HANDLE services = open_service_manager();
\r
991 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
995 SC_HANDLE service_handle = open_service(services, service_name, canonical_name, _countof(canonical_name));
\r
996 if (! service_handle) {
\r
997 CloseServiceHandle(services);
\r
1002 unsigned long error;
\r
1003 SERVICE_STATUS service_status;
\r
1004 if (control == NSSM_SERVICE_CONTROL_START) {
\r
1005 unsigned long initial_status = SERVICE_STOPPED;
\r
1006 ret = StartService(service_handle, (unsigned long) argc, (const TCHAR **) argv);
\r
1007 error = GetLastError();
\r
1008 CloseServiceHandle(services);
\r
1010 if (error == ERROR_IO_PENDING) {
\r
1012 Older versions of Windows return immediately with ERROR_IO_PENDING
\r
1013 indicate that the operation is still in progress. Newer versions
\r
1014 will return it if there really is a delay.
\r
1017 error = ERROR_SUCCESS;
\r
1021 int response = await_service_control_response(control, service_handle, &service_status, initial_status);
\r
1022 CloseHandle(service_handle);
\r
1025 print_message(stderr, NSSM_MESSAGE_BAD_CONTROL_RESPONSE, canonical_name, service_status_text(service_status.dwCurrentState), service_control_text(control));
\r
1028 else _tprintf(_T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1032 CloseHandle(service_handle);
\r
1033 _ftprintf(stderr, _T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1037 else if (control == SERVICE_CONTROL_INTERROGATE) {
\r
1039 We could actually send an INTERROGATE control but that won't return
\r
1040 any information if the service is stopped and we don't care about
\r
1041 the extra details it might give us in any case. So we'll fake it.
\r
1043 ret = QueryServiceStatus(service_handle, &service_status);
\r
1044 error = GetLastError();
\r
1047 _tprintf(_T("%s\n"), service_status_text(service_status.dwCurrentState));
\r
1051 _ftprintf(stderr, _T("%s: %s\n"), canonical_name, error_string(error));
\r
1056 ret = ControlService(service_handle, control, &service_status);
\r
1057 unsigned long initial_status = service_status.dwCurrentState;
\r
1058 error = GetLastError();
\r
1059 CloseServiceHandle(services);
\r
1061 if (error == ERROR_IO_PENDING) {
\r
1063 error = ERROR_SUCCESS;
\r
1067 int response = await_service_control_response(control, service_handle, &service_status, initial_status);
\r
1068 CloseHandle(service_handle);
\r
1071 print_message(stderr, NSSM_MESSAGE_BAD_CONTROL_RESPONSE, canonical_name, service_status_text(service_status.dwCurrentState), service_control_text(control));
\r
1074 else _tprintf(_T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1078 CloseHandle(service_handle);
\r
1079 _ftprintf(stderr, _T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1080 if (error == ERROR_SERVICE_NOT_ACTIVE) {
\r
1081 if (control == SERVICE_CONTROL_SHUTDOWN || control == SERVICE_CONTROL_STOP) return 0;
\r
1088 /* Remove the service */
\r
1089 int remove_service(nssm_service_t *service) {
\r
1090 if (! service) return 1;
\r
1092 /* Open service manager */
\r
1093 SC_HANDLE services = open_service_manager();
\r
1095 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
1099 /* Try to open the service */
\r
1100 service->handle = open_service(services, service->name, service->name, _countof(service->name));
\r
1101 if (! service->handle) {
\r
1102 CloseServiceHandle(services);
\r
1106 /* Get the canonical service name. We open it case insensitively. */
\r
1107 unsigned long bufsize = _countof(service->displayname);
\r
1108 GetServiceDisplayName(services, service->name, service->displayname, &bufsize);
\r
1109 bufsize = _countof(service->name);
\r
1110 GetServiceKeyName(services, service->displayname, service->name, &bufsize);
\r
1112 /* Try to delete the service */
\r
1113 if (! DeleteService(service->handle)) {
\r
1114 print_message(stderr, NSSM_MESSAGE_DELETESERVICE_FAILED);
\r
1115 CloseServiceHandle(services);
\r
1120 CloseServiceHandle(services);
\r
1122 print_message(stdout, NSSM_MESSAGE_SERVICE_REMOVED, service->name);
\r
1126 /* Service initialisation */
\r
1127 void WINAPI service_main(unsigned long argc, TCHAR **argv) {
\r
1128 nssm_service_t *service = alloc_nssm_service();
\r
1129 if (! service) return;
\r
1131 if (_sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]) < 0) {
\r
1132 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("service->name"), _T("service_main()"), 0);
\r
1136 /* We can use a condition variable in a critical section on Vista or later. */
\r
1137 if (imports.SleepConditionVariableCS && imports.WakeConditionVariable) use_critical_section = true;
\r
1138 else use_critical_section = false;
\r
1140 /* Initialise status */
\r
1141 ZeroMemory(&service->status, sizeof(service->status));
\r
1142 service->status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;
\r
1143 service->status.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE;
\r
1144 service->status.dwWin32ExitCode = NO_ERROR;
\r
1145 service->status.dwServiceSpecificExitCode = 0;
\r
1146 service->status.dwCheckPoint = 0;
\r
1147 service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;
\r
1149 /* Signal we AREN'T running the server */
\r
1150 service->process_handle = 0;
\r
1153 /* Register control handler */
\r
1154 service->status_handle = RegisterServiceCtrlHandlerEx(NSSM, service_control_handler, (void *) service);
\r
1155 if (! service->status_handle) {
\r
1156 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_REGISTERSERVICECTRLHANDER_FAILED, error_string(GetLastError()), 0);
\r
1160 log_service_control(service->name, 0, true);
\r
1162 service->status.dwCurrentState = SERVICE_START_PENDING;
\r
1163 service->status.dwWaitHint = service->throttle_delay + NSSM_WAITHINT_MARGIN;
\r
1164 SetServiceStatus(service->status_handle, &service->status);
\r
1167 /* Try to create the exit action parameters; we don't care if it fails */
\r
1168 create_exit_action(service->name, exit_action_strings[0], false);
\r
1170 SC_HANDLE services = open_service_manager();
\r
1172 service->handle = OpenService(services, service->name, SC_MANAGER_ALL_ACCESS);
\r
1173 set_service_recovery(service);
\r
1174 CloseServiceHandle(services);
\r
1178 /* Used for signalling a resume if the service pauses when throttled. */
\r
1179 if (use_critical_section) {
\r
1180 InitializeCriticalSection(&service->throttle_section);
\r
1181 service->throttle_section_initialised = true;
\r
1184 service->throttle_timer = CreateWaitableTimer(0, 1, 0);
\r
1185 if (! service->throttle_timer) {
\r
1186 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_CREATEWAITABLETIMER_FAILED, service->name, error_string(GetLastError()), 0);
\r
1190 /* Remember our initial environment. */
\r
1191 service->initial_env = GetEnvironmentStrings();
\r
1193 monitor_service(service);
\r
1196 /* Make sure service recovery actions are taken where necessary */
\r
1197 void set_service_recovery(nssm_service_t *service) {
\r
1198 SERVICE_FAILURE_ACTIONS_FLAG flag;
\r
1199 ZeroMemory(&flag, sizeof(flag));
\r
1200 flag.fFailureActionsOnNonCrashFailures = true;
\r
1202 /* This functionality was added in Vista so the call may fail */
\r
1203 if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, &flag)) {
\r
1204 unsigned long error = GetLastError();
\r
1205 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
\r
1206 if (error != ERROR_INVALID_LEVEL) {
\r
1207 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_FAILURE_ACTIONS_FAILED, service->name, error_string(error), 0);
\r
1212 int monitor_service(nssm_service_t *service) {
\r
1213 /* Set service status to started */
\r
1214 int ret = start_service(service);
\r
1217 _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%d"), ret);
\r
1218 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_START_SERVICE_FAILED, service->exe, service->name, ret, 0);
\r
1221 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_STARTED_SERVICE, service->exe, service->flags, service->name, service->dir, 0);
\r
1223 /* Monitor service */
\r
1224 if (! RegisterWaitForSingleObject(&service->wait_handle, service->process_handle, end_service, (void *) service, INFINITE, WT_EXECUTEONLYONCE | WT_EXECUTELONGFUNCTION)) {
\r
1225 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_REGISTERWAITFORSINGLEOBJECT_FAILED, service->name, service->exe, error_string(GetLastError()), 0);
\r
1231 TCHAR *service_control_text(unsigned long control) {
\r
1232 switch (control) {
\r
1233 /* HACK: there is no SERVICE_CONTROL_START constant */
\r
1234 case NSSM_SERVICE_CONTROL_START: return _T("START");
\r
1235 case SERVICE_CONTROL_STOP: return _T("STOP");
\r
1236 case SERVICE_CONTROL_SHUTDOWN: return _T("SHUTDOWN");
\r
1237 case SERVICE_CONTROL_PAUSE: return _T("PAUSE");
\r
1238 case SERVICE_CONTROL_CONTINUE: return _T("CONTINUE");
\r
1239 case SERVICE_CONTROL_INTERROGATE: return _T("INTERROGATE");
\r
1240 case NSSM_SERVICE_CONTROL_ROTATE: return _T("ROTATE");
\r
1241 default: return 0;
\r
1245 TCHAR *service_status_text(unsigned long status) {
\r
1247 case SERVICE_STOPPED: return _T("SERVICE_STOPPED");
\r
1248 case SERVICE_START_PENDING: return _T("SERVICE_START_PENDING");
\r
1249 case SERVICE_STOP_PENDING: return _T("SERVICE_STOP_PENDING");
\r
1250 case SERVICE_RUNNING: return _T("SERVICE_RUNNING");
\r
1251 case SERVICE_CONTINUE_PENDING: return _T("SERVICE_CONTINUE_PENDING");
\r
1252 case SERVICE_PAUSE_PENDING: return _T("SERVICE_PAUSE_PENDING");
\r
1253 case SERVICE_PAUSED: return _T("SERVICE_PAUSED");
\r
1254 default: return 0;
\r
1258 void log_service_control(TCHAR *service_name, unsigned long control, bool handled) {
\r
1259 TCHAR *text = service_control_text(control);
\r
1260 unsigned long event;
\r
1263 /* "0x" + 8 x hex + NULL */
\r
1264 text = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, 11 * sizeof(TCHAR));
\r
1266 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);
\r
1269 if (_sntprintf_s(text, 11, _TRUNCATE, _T("0x%08x"), control) < 0) {
\r
1270 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);
\r
1271 HeapFree(GetProcessHeap(), 0, text);
\r
1275 event = NSSM_EVENT_SERVICE_CONTROL_UNKNOWN;
\r
1277 else if (handled) event = NSSM_EVENT_SERVICE_CONTROL_HANDLED;
\r
1278 else event = NSSM_EVENT_SERVICE_CONTROL_NOT_HANDLED;
\r
1280 log_event(EVENTLOG_INFORMATION_TYPE, event, service_name, text, 0);
\r
1282 if (event == NSSM_EVENT_SERVICE_CONTROL_UNKNOWN) {
\r
1283 HeapFree(GetProcessHeap(), 0, text);
\r
1287 /* Service control handler */
\r
1288 unsigned long WINAPI service_control_handler(unsigned long control, unsigned long event, void *data, void *context) {
\r
1289 nssm_service_t *service = (nssm_service_t *) context;
\r
1291 switch (control) {
\r
1292 case SERVICE_CONTROL_INTERROGATE:
\r
1293 /* We always keep the service status up-to-date so this is a no-op. */
\r
1296 case SERVICE_CONTROL_SHUTDOWN:
\r
1297 case SERVICE_CONTROL_STOP:
\r
1298 log_service_control(service->name, control, true);
\r
1300 We MUST acknowledge the stop request promptly but we're committed to
\r
1301 waiting for the application to exit. Spawn a new thread to wait
\r
1302 while we acknowledge the request.
\r
1304 if (! CreateThread(NULL, 0, shutdown_service, context, 0, NULL)) {
\r
1305 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETHREAD_FAILED, error_string(GetLastError()), 0);
\r
1308 We couldn't create a thread to tidy up so we'll have to force the tidyup
\r
1309 to complete in time in this thread.
\r
1311 service->kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;
\r
1312 service->kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;
\r
1313 service->kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;
\r
1315 stop_service(service, 0, true, true);
\r
1319 case SERVICE_CONTROL_CONTINUE:
\r
1320 log_service_control(service->name, control, true);
\r
1321 service->throttle = 0;
\r
1322 if (use_critical_section) imports.WakeConditionVariable(&service->throttle_condition);
\r
1324 if (! service->throttle_timer) return ERROR_CALL_NOT_IMPLEMENTED;
\r
1325 ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));
\r
1326 SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);
\r
1328 /* We can't continue if the application is running! */
\r
1329 if (! service->process_handle) service->status.dwCurrentState = SERVICE_CONTINUE_PENDING;
\r
1330 service->status.dwWaitHint = throttle_milliseconds(service->throttle) + NSSM_WAITHINT_MARGIN;
\r
1331 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESET_THROTTLE, service->name, 0);
\r
1332 SetServiceStatus(service->status_handle, &service->status);
\r
1335 case SERVICE_CONTROL_PAUSE:
\r
1337 We don't accept pause messages but it isn't possible to register
\r
1338 only for continue messages so we have to handle this case.
\r
1340 log_service_control(service->name, control, false);
\r
1341 return ERROR_CALL_NOT_IMPLEMENTED;
\r
1343 case NSSM_SERVICE_CONTROL_ROTATE:
\r
1344 log_service_control(service->name, control, true);
\r
1345 if (service->rotate_stdout_online == NSSM_ROTATE_ONLINE) service->rotate_stdout_online = NSSM_ROTATE_ONLINE_ASAP;
\r
1346 if (service->rotate_stderr_online == NSSM_ROTATE_ONLINE) service->rotate_stderr_online = NSSM_ROTATE_ONLINE_ASAP;
\r
1350 /* Unknown control */
\r
1351 log_service_control(service->name, control, false);
\r
1352 return ERROR_CALL_NOT_IMPLEMENTED;
\r
1355 /* Start the service */
\r
1356 int start_service(nssm_service_t *service) {
\r
1357 service->stopping = false;
\r
1358 service->allow_restart = true;
\r
1360 if (service->process_handle) return 0;
\r
1362 /* Allocate a STARTUPINFO structure for a new process */
\r
1364 ZeroMemory(&si, sizeof(si));
\r
1365 si.cb = sizeof(si);
\r
1367 /* Allocate a PROCESSINFO structure for the process */
\r
1368 PROCESS_INFORMATION pi;
\r
1369 ZeroMemory(&pi, sizeof(pi));
\r
1371 /* Get startup parameters */
\r
1372 int ret = get_parameters(service, &si);
\r
1374 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_PARAMETERS_FAILED, service->name, 0);
\r
1375 return stop_service(service, 2, true, true);
\r
1378 /* Launch executable with arguments */
\r
1379 TCHAR cmd[CMD_LENGTH];
\r
1380 if (_sntprintf_s(cmd, _countof(cmd), _TRUNCATE, _T("\"%s\" %s"), service->exe, service->flags) < 0) {
\r
1381 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("command line"), _T("start_service"), 0);
\r
1382 return stop_service(service, 2, true, true);
\r
1385 throttle_restart(service);
\r
1387 /* Set the environment. */
\r
1388 if (service->env) duplicate_environment(service->env);
\r
1389 if (service->env_extra) set_environment_block(service->env_extra);
\r
1391 /* Set up I/O redirection. */
\r
1392 if (get_output_handles(service, &si)) {
\r
1393 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_OUTPUT_HANDLES_FAILED, service->name, 0);
\r
1394 if (! service->no_console) FreeConsole();
\r
1395 close_output_handles(&si);
\r
1396 return stop_service(service, 4, true, true);
\r
1399 bool inherit_handles = false;
\r
1400 if (si.dwFlags & STARTF_USESTDHANDLES) inherit_handles = true;
\r
1401 unsigned long flags = service->priority & priority_mask();
\r
1402 if (service->affinity) flags |= CREATE_SUSPENDED;
\r
1403 if (! CreateProcess(0, cmd, 0, 0, inherit_handles, flags, 0, service->dir, &si, &pi)) {
\r
1404 unsigned long exitcode = 3;
\r
1405 unsigned long error = GetLastError();
\r
1406 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service->name, service->exe, error_string(error), 0);
\r
1407 close_output_handles(&si);
\r
1408 duplicate_environment(service->initial_env);
\r
1409 return stop_service(service, exitcode, true, true);
\r
1411 service->process_handle = pi.hProcess;
\r
1412 service->pid = pi.dwProcessId;
\r
1414 if (get_process_creation_time(service->process_handle, &service->creation_time)) ZeroMemory(&service->creation_time, sizeof(service->creation_time));
\r
1416 close_output_handles(&si);
\r
1418 if (! service->no_console) FreeConsole();
\r
1420 /* Restore our environment. */
\r
1421 duplicate_environment(service->initial_env);
\r
1423 if (service->affinity) {
\r
1425 We are explicitly storing service->affinity as a 64-bit unsigned integer
\r
1426 so that we can parse it regardless of whether we're running in 32-bit
\r
1427 or 64-bit mode. The arguments to SetProcessAffinityMask(), however, are
\r
1428 defined as type DWORD_PTR and hence limited to 32 bits on a 32-bit system
\r
1429 (or when running the 32-bit NSSM).
\r
1431 The result is a lot of seemingly-unnecessary casting throughout the code
\r
1432 and potentially confusion when we actually try to start the service.
\r
1433 Having said that, however, it's unlikely that we're actually going to
\r
1434 run in 32-bit mode on a system which has more than 32 CPUs so the
\r
1435 likelihood of seeing a confusing situation is somewhat diminished.
\r
1437 DWORD_PTR affinity, system_affinity;
\r
1439 if (GetProcessAffinityMask(service->process_handle, &affinity, &system_affinity)) affinity = service->affinity & system_affinity;
\r
1441 affinity = (DWORD_PTR) service->affinity;
\r
1442 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GETPROCESSAFFINITYMASK_FAILED, service->name, error_string(GetLastError()), 0);
\r
1445 if (! SetProcessAffinityMask(service->process_handle, affinity)) {
\r
1446 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_SETPROCESSAFFINITYMASK_FAILED, service->name, error_string(GetLastError()), 0);
\r
1449 ResumeThread(pi.hThread);
\r
1453 Wait for a clean startup before changing the service status to RUNNING
\r
1454 but be mindful of the fact that we are blocking the service control manager
\r
1455 so abandon the wait before too much time has elapsed.
\r
1457 unsigned long delay = service->throttle_delay;
\r
1458 if (delay > NSSM_SERVICE_STATUS_DEADLINE) {
\r
1459 TCHAR delay_milliseconds[16];
\r
1460 _sntprintf_s(delay_milliseconds, _countof(delay_milliseconds), _TRUNCATE, _T("%lu"), delay);
\r
1461 TCHAR deadline_milliseconds[16];
\r
1462 _sntprintf_s(deadline_milliseconds, _countof(deadline_milliseconds), _TRUNCATE, _T("%lu"), NSSM_SERVICE_STATUS_DEADLINE);
\r
1463 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_STARTUP_DELAY_TOO_LONG, service->name, delay_milliseconds, NSSM, deadline_milliseconds, 0);
\r
1464 delay = NSSM_SERVICE_STATUS_DEADLINE;
\r
1466 unsigned long deadline = WaitForSingleObject(service->process_handle, delay);
\r
1468 /* Signal successful start */
\r
1469 service->status.dwCurrentState = SERVICE_RUNNING;
\r
1470 SetServiceStatus(service->status_handle, &service->status);
\r
1472 /* Continue waiting for a clean startup. */
\r
1473 if (deadline == WAIT_TIMEOUT) {
\r
1474 if (service->throttle_delay > delay) {
\r
1475 if (WaitForSingleObject(service->process_handle, service->throttle_delay - delay) == WAIT_TIMEOUT) service->throttle = 0;
\r
1477 else service->throttle = 0;
\r
1480 /* Ensure the restart delay is always applied. */
\r
1481 if (service->restart_delay && ! service->throttle) service->throttle++;
\r
1486 /* Stop the service */
\r
1487 int stop_service(nssm_service_t *service, unsigned long exitcode, bool graceful, bool default_action) {
\r
1488 service->allow_restart = false;
\r
1489 if (service->wait_handle) {
\r
1490 UnregisterWait(service->wait_handle);
\r
1491 service->wait_handle = 0;
\r
1494 service->rotate_stdout_online = service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;
\r
1496 if (default_action && ! exitcode && ! graceful) {
\r
1497 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
1501 /* Signal we are stopping */
\r
1503 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
1504 service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;
\r
1505 SetServiceStatus(service->status_handle, &service->status);
\r
1508 /* Nothing to do if service isn't running */
\r
1509 if (service->pid) {
\r
1510 /* Shut down service */
\r
1511 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_TERMINATEPROCESS, service->name, service->exe, 0);
\r
1512 kill_process(service, service->process_handle, service->pid, 0);
\r
1514 else log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_PROCESS_ALREADY_STOPPED, service->name, service->exe, 0);
\r
1516 end_service((void *) service, true);
\r
1518 /* Signal we stopped */
\r
1520 service->status.dwCurrentState = SERVICE_STOPPED;
\r
1522 service->status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
\r
1523 service->status.dwServiceSpecificExitCode = exitcode;
\r
1526 service->status.dwWin32ExitCode = NO_ERROR;
\r
1527 service->status.dwServiceSpecificExitCode = 0;
\r
1529 SetServiceStatus(service->status_handle, &service->status);
\r
1535 /* Callback function triggered when the server exits */
\r
1536 void CALLBACK end_service(void *arg, unsigned char why) {
\r
1537 nssm_service_t *service = (nssm_service_t *) arg;
\r
1539 if (service->stopping) return;
\r
1541 service->stopping = true;
\r
1543 service->rotate_stdout_online = service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;
\r
1545 /* Use now as a dummy exit time. */
\r
1546 GetSystemTimeAsFileTime(&service->exit_time);
\r
1548 /* Check exit code */
\r
1549 unsigned long exitcode = 0;
\r
1551 if (service->process_handle) {
\r
1552 GetExitCodeProcess(service->process_handle, &exitcode);
\r
1553 /* Check real exit time. */
\r
1554 if (exitcode != STILL_ACTIVE) get_process_exit_time(service->process_handle, &service->exit_time);
\r
1555 CloseHandle(service->process_handle);
\r
1558 service->process_handle = 0;
\r
1561 Log that the service ended BEFORE logging about killing the process
\r
1562 tree. See below for the possible values of the why argument.
\r
1565 _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), exitcode);
\r
1566 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ENDED_SERVICE, service->exe, service->name, code, 0);
\r
1570 if (exitcode == STILL_ACTIVE) exitcode = 0;
\r
1571 if (service->pid) kill_process_tree(service, service->pid, exitcode, service->pid);
\r
1575 The why argument is true if our wait timed out or false otherwise.
\r
1576 Our wait is infinite so why will never be true when called by the system.
\r
1577 If it is indeed true, assume we were called from stop_service() because
\r
1578 this is a controlled shutdown, and don't take any restart action.
\r
1581 if (! service->allow_restart) return;
\r
1583 /* What action should we take? */
\r
1584 int action = NSSM_EXIT_RESTART;
\r
1585 TCHAR action_string[ACTION_LEN];
\r
1586 bool default_action;
\r
1587 if (! get_exit_action(service->name, &exitcode, action_string, &default_action)) {
\r
1588 for (int i = 0; exit_action_strings[i]; i++) {
\r
1589 if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
1597 /* Try to restart the service or return failure code to service manager */
\r
1598 case NSSM_EXIT_RESTART:
\r
1599 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_RESTART, service->name, code, exit_action_strings[action], service->exe, 0);
\r
1600 while (monitor_service(service)) {
\r
1601 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_RESTART_SERVICE_FAILED, service->exe, service->name, 0);
\r
1606 /* Do nothing, just like srvany would */
\r
1607 case NSSM_EXIT_IGNORE:
\r
1608 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_IGNORE, service->name, code, exit_action_strings[action], service->exe, 0);
\r
1612 /* Tell the service manager we are finished */
\r
1613 case NSSM_EXIT_REALLY:
\r
1614 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_REALLY, service->name, code, exit_action_strings[action], 0);
\r
1615 stop_service(service, exitcode, true, default_action);
\r
1618 /* Fake a crash so pre-Vista service managers will run recovery actions. */
\r
1619 case NSSM_EXIT_UNCLEAN:
\r
1620 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_UNCLEAN, service->name, code, exit_action_strings[action], 0);
\r
1621 stop_service(service, exitcode, false, default_action);
\r
1628 void throttle_restart(nssm_service_t *service) {
\r
1629 /* This can't be a restart if the service is already running. */
\r
1630 if (! service->throttle++) return;
\r
1633 unsigned long throttle_ms = throttle_milliseconds(service->throttle);
\r
1634 TCHAR threshold[8], milliseconds[8];
\r
1636 if (service->restart_delay > throttle_ms) ms = service->restart_delay;
\r
1637 else ms = throttle_ms;
\r
1639 if (service->throttle > 7) service->throttle = 8;
\r
1641 _sntprintf_s(milliseconds, _countof(milliseconds), _TRUNCATE, _T("%lu"), ms);
\r
1643 if (service->throttle == 1 && service->restart_delay > throttle_ms) log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESTART_DELAY, service->name, milliseconds, 0);
\r
1645 _sntprintf_s(threshold, _countof(threshold), _TRUNCATE, _T("%lu"), service->throttle_delay);
\r
1646 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_THROTTLED, service->name, threshold, milliseconds, 0);
\r
1649 if (use_critical_section) EnterCriticalSection(&service->throttle_section);
\r
1650 else if (service->throttle_timer) {
\r
1651 ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));
\r
1652 service->throttle_duetime.QuadPart = 0 - (ms * 10000LL);
\r
1653 SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);
\r
1656 service->status.dwCurrentState = SERVICE_PAUSED;
\r
1657 SetServiceStatus(service->status_handle, &service->status);
\r
1659 if (use_critical_section) {
\r
1660 imports.SleepConditionVariableCS(&service->throttle_condition, &service->throttle_section, ms);
\r
1661 LeaveCriticalSection(&service->throttle_section);
\r
1664 if (service->throttle_timer) WaitForSingleObject(service->throttle_timer, INFINITE);
\r
1670 When responding to a stop (or any other) request we need to set dwWaitHint to
\r
1671 the number of milliseconds we expect the operation to take, and optionally
\r
1672 increase dwCheckPoint. If dwWaitHint milliseconds elapses without the
\r
1673 operation completing or dwCheckPoint increasing, the system will consider the
\r
1674 service to be hung.
\r
1676 However the system will consider the service to be hung after 30000
\r
1677 milliseconds regardless of the value of dwWaitHint if dwCheckPoint has not
\r
1678 changed. Therefore if we want to wait longer than that we must periodically
\r
1679 increase dwCheckPoint.
\r
1681 Furthermore, it will consider the service to be hung after 60000 milliseconds
\r
1682 regardless of the value of dwCheckPoint unless dwWaitHint is increased every
\r
1683 time dwCheckPoint is also increased.
\r
1685 Our strategy then is to retrieve the initial dwWaitHint and wait for
\r
1686 NSSM_SERVICE_STATUS_DEADLINE milliseconds. If the process is still running
\r
1687 and we haven't finished waiting we increment dwCheckPoint and add whichever is
\r
1688 smaller of NSSM_SERVICE_STATUS_DEADLINE or the remaining timeout to
\r
1691 Only doing both these things will prevent the system from killing the service.
\r
1693 Returns: 1 if the wait timed out.
\r
1694 0 if the wait completed.
\r
1697 int await_shutdown(nssm_service_t *service, TCHAR *function_name, unsigned long timeout) {
\r
1698 unsigned long interval;
\r
1699 unsigned long waithint;
\r
1700 unsigned long ret;
\r
1701 unsigned long waited;
\r
1702 TCHAR interval_milliseconds[16];
\r
1703 TCHAR timeout_milliseconds[16];
\r
1704 TCHAR waited_milliseconds[16];
\r
1705 TCHAR *function = function_name;
\r
1707 /* Add brackets to function name. */
\r
1708 size_t funclen = _tcslen(function_name) + 3;
\r
1709 TCHAR *func = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, funclen * sizeof(TCHAR));
\r
1711 if (_sntprintf_s(func, funclen, _TRUNCATE, _T("%s()"), function_name) > -1) function = func;
\r
1714 _sntprintf_s(timeout_milliseconds, _countof(timeout_milliseconds), _TRUNCATE, _T("%lu"), timeout);
\r
1716 waithint = service->status.dwWaitHint;
\r
1718 while (waited < timeout) {
\r
1719 interval = timeout - waited;
\r
1720 if (interval > NSSM_SERVICE_STATUS_DEADLINE) interval = NSSM_SERVICE_STATUS_DEADLINE;
\r
1722 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
1723 service->status.dwWaitHint += interval;
\r
1724 service->status.dwCheckPoint++;
\r
1725 SetServiceStatus(service->status_handle, &service->status);
\r
1728 _sntprintf_s(waited_milliseconds, _countof(waited_milliseconds), _TRUNCATE, _T("%lu"), waited);
\r
1729 _sntprintf_s(interval_milliseconds, _countof(interval_milliseconds), _TRUNCATE, _T("%lu"), interval);
\r
1730 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_AWAITING_SHUTDOWN, function, service->name, waited_milliseconds, interval_milliseconds, timeout_milliseconds, 0);
\r
1733 switch (WaitForSingleObject(service->process_handle, interval)) {
\r
1734 case WAIT_OBJECT_0:
\r
1738 case WAIT_TIMEOUT:
\r
1747 waited += interval;
\r
1751 if (func) HeapFree(GetProcessHeap(), 0, func);
\r