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 (is_localsystem(qsc->lpServiceStartName)) return 0;
\r
477 size_t len = _tcslen(qsc->lpServiceStartName);
\r
478 *username = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(TCHAR));
\r
480 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("username"), _T("get_service_username()"));
\r
484 memmove(*username, qsc->lpServiceStartName, (len + 1) * sizeof(TCHAR));
\r
485 *usernamelen = len;
\r
490 /* Set default values which aren't zero. */
\r
491 void set_nssm_service_defaults(nssm_service_t *service) {
\r
492 if (! service) return;
\r
494 service->type = SERVICE_WIN32_OWN_PROCESS;
\r
495 service->priority = NORMAL_PRIORITY_CLASS;
\r
496 service->stdin_sharing = NSSM_STDIN_SHARING;
\r
497 service->stdin_disposition = NSSM_STDIN_DISPOSITION;
\r
498 service->stdin_flags = NSSM_STDIN_FLAGS;
\r
499 service->stdout_sharing = NSSM_STDOUT_SHARING;
\r
500 service->stdout_disposition = NSSM_STDOUT_DISPOSITION;
\r
501 service->stdout_flags = NSSM_STDOUT_FLAGS;
\r
502 service->stderr_sharing = NSSM_STDERR_SHARING;
\r
503 service->stderr_disposition = NSSM_STDERR_DISPOSITION;
\r
504 service->stderr_flags = NSSM_STDERR_FLAGS;
\r
505 service->throttle_delay = NSSM_RESET_THROTTLE_RESTART;
\r
506 service->stop_method = ~0;
\r
507 service->kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;
\r
508 service->kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;
\r
509 service->kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;
\r
512 /* Allocate and zero memory for a service. */
\r
513 nssm_service_t *alloc_nssm_service() {
\r
514 nssm_service_t *service = (nssm_service_t *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(nssm_service_t));
\r
515 if (! service) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("service"), _T("alloc_nssm_service()"), 0);
\r
519 /* Free memory for a service. */
\r
520 void cleanup_nssm_service(nssm_service_t *service) {
\r
521 if (! service) return;
\r
522 if (service->username) HeapFree(GetProcessHeap(), 0, service->username);
\r
523 if (service->password) {
\r
524 SecureZeroMemory(service->password, service->passwordlen);
\r
525 HeapFree(GetProcessHeap(), 0, service->password);
\r
527 if (service->env) HeapFree(GetProcessHeap(), 0, service->env);
\r
528 if (service->env_extra) HeapFree(GetProcessHeap(), 0, service->env_extra);
\r
529 if (service->handle) CloseServiceHandle(service->handle);
\r
530 if (service->process_handle) CloseHandle(service->process_handle);
\r
531 if (service->wait_handle) UnregisterWait(service->process_handle);
\r
532 if (service->throttle_section_initialised) DeleteCriticalSection(&service->throttle_section);
\r
533 if (service->throttle_timer) CloseHandle(service->throttle_timer);
\r
534 if (service->initial_env) FreeEnvironmentStrings(service->initial_env);
\r
535 HeapFree(GetProcessHeap(), 0, service);
\r
538 /* About to install the service */
\r
539 int pre_install_service(int argc, TCHAR **argv) {
\r
540 nssm_service_t *service = alloc_nssm_service();
\r
541 set_nssm_service_defaults(service);
\r
542 if (argc) _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]);
\r
544 /* Show the dialogue box if we didn't give the service name and path */
\r
545 if (argc < 2) return nssm_gui(IDD_INSTALL, service);
\r
548 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("service"), _T("pre_install_service()"));
\r
551 _sntprintf_s(service->exe, _countof(service->exe), _TRUNCATE, _T("%s"), argv[1]);
\r
553 /* Arguments are optional */
\r
554 size_t flagslen = 0;
\r
557 for (i = 2; i < argc; i++) flagslen += _tcslen(argv[i]) + 1;
\r
558 if (! flagslen) flagslen = 1;
\r
559 if (flagslen > _countof(service->flags)) {
\r
560 print_message(stderr, NSSM_MESSAGE_FLAGS_TOO_LONG);
\r
564 for (i = 2; i < argc; i++) {
\r
565 size_t len = _tcslen(argv[i]);
\r
566 memmove(service->flags + s, argv[i], len * sizeof(TCHAR));
\r
568 if (i < argc - 1) service->flags[s++] = _T(' ');
\r
571 /* Work out directory name */
\r
572 _sntprintf_s(service->dir, _countof(service->dir), _TRUNCATE, _T("%s"), service->exe);
\r
573 strip_basename(service->dir);
\r
575 int ret = install_service(service);
\r
576 cleanup_nssm_service(service);
\r
580 /* About to edit the service. */
\r
581 int pre_edit_service(int argc, TCHAR **argv) {
\r
582 /* Require service name. */
\r
583 if (argc < 2) return usage(1);
\r
585 /* Are we editing on the command line? */
\r
586 enum { MODE_EDITING, MODE_GETTING, MODE_SETTING, MODE_RESETTING } mode = MODE_EDITING;
\r
587 const TCHAR *verb = argv[0];
\r
588 const TCHAR *service_name = argv[1];
\r
589 bool getting = false;
\r
590 bool unsetting = false;
\r
592 /* Minimum number of arguments. */
\r
594 /* Index of first value. */
\r
597 if (str_equiv(verb, _T("get"))) {
\r
599 mode = MODE_GETTING;
\r
601 else if (str_equiv(verb, _T("set"))) {
\r
603 mode = MODE_SETTING;
\r
605 else if (str_equiv(verb, _T("reset")) || str_equiv(verb, _T("unset"))) {
\r
607 mode = MODE_RESETTING;
\r
609 if (argc < mandatory) return usage(1);
\r
611 const TCHAR *parameter = 0;
\r
612 settings_t *setting = 0;
\r
615 /* Validate the parameter. */
\r
616 if (mandatory > 2) {
\r
617 bool additional_mandatory = false;
\r
619 parameter = argv[2];
\r
620 for (i = 0; settings[i].name; i++) {
\r
621 setting = &settings[i];
\r
622 if (! str_equiv(setting->name, parameter)) continue;
\r
623 if (((setting->additional & ADDITIONAL_GETTING) && mode == MODE_GETTING) || ((setting->additional & ADDITIONAL_SETTING) && mode == MODE_SETTING) || ((setting->additional & ADDITIONAL_RESETTING) && mode == MODE_RESETTING)) {
\r
624 additional_mandatory = true;
\r
629 if (! settings[i].name) {
\r
630 print_message(stderr, NSSM_MESSAGE_INVALID_PARAMETER, parameter);
\r
631 for (i = 0; settings[i].name; i++) _ftprintf(stderr, _T("%s\n"), settings[i].name);
\r
636 if (additional_mandatory) {
\r
637 if (argc < mandatory) {
\r
638 print_message(stderr, NSSM_MESSAGE_MISSING_SUBPARAMETER, parameter);
\r
641 additional = argv[3];
\r
644 else if (str_equiv(setting->name, NSSM_NATIVE_OBJECTNAME) && mode == MODE_SETTING) {
\r
645 additional = argv[3];
\r
649 additional = argv[remainder];
\r
650 if (argc < mandatory) return usage(1);
\r
654 nssm_service_t *service = alloc_nssm_service();
\r
655 _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), service_name);
\r
657 /* Open service manager */
\r
658 SC_HANDLE services = open_service_manager();
\r
660 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
664 /* Try to open the service */
\r
665 service->handle = open_service(services, service->name, service->name, _countof(service->name));
\r
666 if (! service->handle) {
\r
667 CloseServiceHandle(services);
\r
671 /* Get system details. */
\r
672 QUERY_SERVICE_CONFIG *qsc = query_service_config(service->name, service->handle);
\r
674 CloseHandle(service->handle);
\r
675 CloseServiceHandle(services);
\r
679 service->type = qsc->dwServiceType;
\r
680 if (! (service->type & SERVICE_WIN32_OWN_PROCESS)) {
\r
681 if (mode != MODE_GETTING) {
\r
682 HeapFree(GetProcessHeap(), 0, qsc);
\r
683 CloseHandle(service->handle);
\r
684 CloseServiceHandle(services);
\r
685 print_message(stderr, NSSM_MESSAGE_CANNOT_EDIT, service->name, NSSM_WIN32_OWN_PROCESS, 0);
\r
690 if (get_service_startup(service->name, service->handle, qsc, &service->startup)) {
\r
691 if (mode != MODE_GETTING) {
\r
692 HeapFree(GetProcessHeap(), 0, qsc);
\r
693 CloseHandle(service->handle);
\r
694 CloseServiceHandle(services);
\r
699 if (get_service_username(service->name, qsc, &service->username, &service->usernamelen)) {
\r
700 if (mode != MODE_GETTING) {
\r
701 HeapFree(GetProcessHeap(), 0, qsc);
\r
702 CloseHandle(service->handle);
\r
703 CloseServiceHandle(services);
\r
708 _sntprintf_s(service->displayname, _countof(service->displayname), _TRUNCATE, _T("%s"), qsc->lpDisplayName);
\r
710 /* Get the canonical service name. We open it case insensitively. */
\r
711 unsigned long bufsize = _countof(service->name);
\r
712 GetServiceKeyName(services, service->displayname, service->name, &bufsize);
\r
714 /* Remember the executable in case it isn't NSSM. */
\r
715 _sntprintf_s(service->image, _countof(service->image), _TRUNCATE, _T("%s"), qsc->lpBinaryPathName);
\r
716 HeapFree(GetProcessHeap(), 0, qsc);
\r
718 /* Get extended system details. */
\r
719 if (get_service_description(service->name, service->handle, _countof(service->description), service->description)) {
\r
720 if (mode != MODE_GETTING) {
\r
721 CloseHandle(service->handle);
\r
722 CloseServiceHandle(services);
\r
727 /* Get NSSM details. */
\r
728 get_parameters(service, 0);
\r
730 CloseServiceHandle(services);
\r
732 if (! service->exe[0]) {
\r
733 service->native = true;
\r
734 if (mode != MODE_GETTING) print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE, service->name, NSSM, service->image);
\r
737 /* Editing with the GUI. */
\r
738 if (mode == MODE_EDITING) {
\r
739 nssm_gui(IDD_EDIT, service);
\r
743 /* Trying to manage App* parameters for a non-NSSM service. */
\r
744 if (! setting->native && service->native) {
\r
745 CloseHandle(service->handle);
\r
746 print_message(stderr, NSSM_MESSAGE_NATIVE_PARAMETER, setting->name, NSSM);
\r
754 if (mode == MODE_GETTING) {
\r
755 if (! service->native) {
\r
756 key = open_registry(service->name, KEY_READ);
\r
757 if (! key) return 4;
\r
760 if (setting->native) ret = get_setting(service->name, service->handle, setting, &value, additional);
\r
761 else ret = get_setting(service->name, key, setting, &value, additional);
\r
763 CloseHandle(service->handle);
\r
767 switch (setting->type) {
\r
768 case REG_EXPAND_SZ:
\r
771 _tprintf(_T("%s\n"), value.string ? value.string : _T(""));
\r
772 HeapFree(GetProcessHeap(), 0, value.string);
\r
776 _tprintf(_T("%u\n"), value.numeric);
\r
780 if (! service->native) RegCloseKey(key);
\r
781 CloseHandle(service->handle);
\r
785 /* Build the value. */
\r
786 if (mode == MODE_RESETTING) {
\r
787 /* Unset the parameter. */
\r
790 else if (remainder == argc) {
\r
794 /* Set the parameter. */
\r
796 size_t delimiterlen = (setting->additional & ADDITIONAL_CRLF) ? 2 : 1;
\r
797 for (i = remainder; i < argc; i++) len += _tcslen(argv[i]) + delimiterlen;
\r
800 value.string = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, len * sizeof(TCHAR));
\r
801 if (! value.string) {
\r
802 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("value"), _T("edit_service()"));
\r
803 CloseHandle(service->handle);
\r
808 for (i = remainder; i < argc; i++) {
\r
809 size_t len = _tcslen(argv[i]);
\r
810 memmove(value.string + s, argv[i], len * sizeof(TCHAR));
\r
812 if (i < argc - 1) {
\r
813 if (setting->additional & ADDITIONAL_CRLF) {
\r
814 value.string[s++] = _T('\r');
\r
815 value.string[s++] = _T('\n');
\r
817 else value.string[s++] = _T(' ');
\r
820 value.string[s] = _T('\0');
\r
823 if (! service->native) {
\r
824 key = open_registry(service->name, KEY_WRITE);
\r
826 if (value.string) HeapFree(GetProcessHeap(), 0, value.string);
\r
831 if (setting->native) ret = set_setting(service->name, service->handle, setting, &value, additional);
\r
832 else ret = set_setting(service->name, key, setting, &value, additional);
\r
833 if (value.string) HeapFree(GetProcessHeap(), 0, value.string);
\r
835 if (! service->native) RegCloseKey(key);
\r
836 CloseHandle(service->handle);
\r
840 if (! service->native) RegCloseKey(key);
\r
841 CloseHandle(service->handle);
\r
846 /* About to remove the service */
\r
847 int pre_remove_service(int argc, TCHAR **argv) {
\r
848 nssm_service_t *service = alloc_nssm_service();
\r
849 set_nssm_service_defaults(service);
\r
850 if (argc) _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]);
\r
852 /* Show dialogue box if we didn't pass service name and "confirm" */
\r
853 if (argc < 2) return nssm_gui(IDD_REMOVE, service);
\r
854 if (str_equiv(argv[1], _T("confirm"))) {
\r
855 int ret = remove_service(service);
\r
856 cleanup_nssm_service(service);
\r
859 print_message(stderr, NSSM_MESSAGE_PRE_REMOVE_SERVICE);
\r
863 /* Install the service */
\r
864 int install_service(nssm_service_t *service) {
\r
865 if (! service) return 1;
\r
867 /* Open service manager */
\r
868 SC_HANDLE services = open_service_manager();
\r
870 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
871 cleanup_nssm_service(service);
\r
875 /* Get path of this program */
\r
876 GetModuleFileName(0, service->image, _countof(service->image));
\r
878 /* Create the service - settings will be changed in edit_service() */
\r
879 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
880 if (! service->handle) {
\r
881 print_message(stderr, NSSM_MESSAGE_CREATESERVICE_FAILED, error_string(GetLastError()));
\r
882 CloseServiceHandle(services);
\r
886 if (edit_service(service, false)) {
\r
887 DeleteService(service->handle);
\r
888 CloseServiceHandle(services);
\r
892 print_message(stdout, NSSM_MESSAGE_SERVICE_INSTALLED, service->name);
\r
895 CloseServiceHandle(services);
\r
900 /* Edit the service. */
\r
901 int edit_service(nssm_service_t *service, bool editing) {
\r
902 if (! service) return 1;
\r
905 The only two valid flags for service type are SERVICE_WIN32_OWN_PROCESS
\r
906 and SERVICE_INTERACTIVE_PROCESS.
\r
908 service->type &= SERVICE_INTERACTIVE_PROCESS;
\r
909 service->type |= SERVICE_WIN32_OWN_PROCESS;
\r
911 /* Startup type. */
\r
912 unsigned long startup;
\r
913 switch (service->startup) {
\r
914 case NSSM_STARTUP_MANUAL: startup = SERVICE_DEMAND_START; break;
\r
915 case NSSM_STARTUP_DISABLED: startup = SERVICE_DISABLED; break;
\r
916 default: startup = SERVICE_AUTO_START;
\r
919 /* Display name. */
\r
920 if (! service->displayname[0]) _sntprintf_s(service->displayname, _countof(service->displayname), _TRUNCATE, _T("%s"), service->name);
\r
923 Username must be NULL if we aren't changing or an account name.
\r
924 We must explicitly use LOCALSYSTEM to change it when we are editing.
\r
925 Password must be NULL if we aren't changing, a password or "".
\r
926 Empty passwords are valid but we won't allow them in the GUI.
\r
928 TCHAR *username = 0;
\r
929 TCHAR *password = 0;
\r
930 if (service->usernamelen) {
\r
931 username = service->username;
\r
932 if (service->passwordlen) password = service->password;
\r
933 else password = _T("");
\r
935 else if (editing) username = NSSM_LOCALSYSTEM_ACCOUNT;
\r
937 if (well_known_username(username)) password = _T("");
\r
939 if (grant_logon_as_service(username)) {
\r
940 print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username);
\r
945 if (! ChangeServiceConfig(service->handle, service->type, startup, SERVICE_NO_CHANGE, 0, 0, 0, 0, username, password, service->displayname)) {
\r
946 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
950 if (service->description[0] || editing) {
\r
951 set_service_description(service->name, service->handle, service->description);
\r
954 SERVICE_DELAYED_AUTO_START_INFO delayed;
\r
955 ZeroMemory(&delayed, sizeof(delayed));
\r
956 if (service->startup == NSSM_STARTUP_DELAYED) delayed.fDelayedAutostart = 1;
\r
957 else delayed.fDelayedAutostart = 0;
\r
958 /* Delayed startup isn't supported until Vista. */
\r
959 if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, &delayed)) {
\r
960 unsigned long error = GetLastError();
\r
961 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
\r
962 if (error != ERROR_INVALID_LEVEL) {
\r
963 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_DELAYED_AUTO_START_INFO_FAILED, service->name, error_string(error), 0);
\r
967 /* Don't mess with parameters which aren't ours. */
\r
968 if (! service->native) {
\r
969 /* Now we need to put the parameters into the registry */
\r
970 if (create_parameters(service, editing)) {
\r
971 print_message(stderr, NSSM_MESSAGE_CREATE_PARAMETERS_FAILED);
\r
975 set_service_recovery(service);
\r
981 /* Control a service. */
\r
982 int control_service(unsigned long control, int argc, TCHAR **argv) {
\r
983 if (argc < 1) return usage(1);
\r
984 TCHAR *service_name = argv[0];
\r
985 TCHAR canonical_name[SERVICE_NAME_LENGTH];
\r
987 SC_HANDLE services = open_service_manager();
\r
989 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
993 SC_HANDLE service_handle = open_service(services, service_name, canonical_name, _countof(canonical_name));
\r
994 if (! service_handle) {
\r
995 CloseServiceHandle(services);
\r
1000 unsigned long error;
\r
1001 SERVICE_STATUS service_status;
\r
1002 if (control == NSSM_SERVICE_CONTROL_START) {
\r
1003 unsigned long initial_status = SERVICE_STOPPED;
\r
1004 ret = StartService(service_handle, (unsigned long) argc, (const TCHAR **) argv);
\r
1005 error = GetLastError();
\r
1006 CloseServiceHandle(services);
\r
1008 if (error == ERROR_IO_PENDING) {
\r
1010 Older versions of Windows return immediately with ERROR_IO_PENDING
\r
1011 indicate that the operation is still in progress. Newer versions
\r
1012 will return it if there really is a delay.
\r
1015 error = ERROR_SUCCESS;
\r
1019 int response = await_service_control_response(control, service_handle, &service_status, initial_status);
\r
1020 CloseHandle(service_handle);
\r
1023 print_message(stderr, NSSM_MESSAGE_BAD_CONTROL_RESPONSE, canonical_name, service_status_text(service_status.dwCurrentState), service_control_text(control));
\r
1026 else _tprintf(_T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1030 CloseHandle(service_handle);
\r
1031 _ftprintf(stderr, _T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1035 else if (control == SERVICE_CONTROL_INTERROGATE) {
\r
1037 We could actually send an INTERROGATE control but that won't return
\r
1038 any information if the service is stopped and we don't care about
\r
1039 the extra details it might give us in any case. So we'll fake it.
\r
1041 ret = QueryServiceStatus(service_handle, &service_status);
\r
1042 error = GetLastError();
\r
1045 _tprintf(_T("%s\n"), service_status_text(service_status.dwCurrentState));
\r
1049 _ftprintf(stderr, _T("%s: %s\n"), canonical_name, error_string(error));
\r
1054 ret = ControlService(service_handle, control, &service_status);
\r
1055 unsigned long initial_status = service_status.dwCurrentState;
\r
1056 error = GetLastError();
\r
1057 CloseServiceHandle(services);
\r
1059 if (error == ERROR_IO_PENDING) {
\r
1061 error = ERROR_SUCCESS;
\r
1065 int response = await_service_control_response(control, service_handle, &service_status, initial_status);
\r
1066 CloseHandle(service_handle);
\r
1069 print_message(stderr, NSSM_MESSAGE_BAD_CONTROL_RESPONSE, canonical_name, service_status_text(service_status.dwCurrentState), service_control_text(control));
\r
1072 else _tprintf(_T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1076 CloseHandle(service_handle);
\r
1077 _ftprintf(stderr, _T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1078 if (error == ERROR_SERVICE_NOT_ACTIVE) {
\r
1079 if (control == SERVICE_CONTROL_SHUTDOWN || control == SERVICE_CONTROL_STOP) return 0;
\r
1086 /* Remove the service */
\r
1087 int remove_service(nssm_service_t *service) {
\r
1088 if (! service) return 1;
\r
1090 /* Open service manager */
\r
1091 SC_HANDLE services = open_service_manager();
\r
1093 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
1097 /* Try to open the service */
\r
1098 service->handle = open_service(services, service->name, service->name, _countof(service->name));
\r
1099 if (! service->handle) {
\r
1100 CloseServiceHandle(services);
\r
1104 /* Get the canonical service name. We open it case insensitively. */
\r
1105 unsigned long bufsize = _countof(service->displayname);
\r
1106 GetServiceDisplayName(services, service->name, service->displayname, &bufsize);
\r
1107 bufsize = _countof(service->name);
\r
1108 GetServiceKeyName(services, service->displayname, service->name, &bufsize);
\r
1110 /* Try to delete the service */
\r
1111 if (! DeleteService(service->handle)) {
\r
1112 print_message(stderr, NSSM_MESSAGE_DELETESERVICE_FAILED);
\r
1113 CloseServiceHandle(services);
\r
1118 CloseServiceHandle(services);
\r
1120 print_message(stdout, NSSM_MESSAGE_SERVICE_REMOVED, service->name);
\r
1124 /* Service initialisation */
\r
1125 void WINAPI service_main(unsigned long argc, TCHAR **argv) {
\r
1126 nssm_service_t *service = alloc_nssm_service();
\r
1127 if (! service) return;
\r
1129 if (_sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]) < 0) {
\r
1130 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("service->name"), _T("service_main()"), 0);
\r
1134 /* We can use a condition variable in a critical section on Vista or later. */
\r
1135 if (imports.SleepConditionVariableCS && imports.WakeConditionVariable) use_critical_section = true;
\r
1136 else use_critical_section = false;
\r
1138 /* Initialise status */
\r
1139 ZeroMemory(&service->status, sizeof(service->status));
\r
1140 service->status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;
\r
1141 service->status.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE;
\r
1142 service->status.dwWin32ExitCode = NO_ERROR;
\r
1143 service->status.dwServiceSpecificExitCode = 0;
\r
1144 service->status.dwCheckPoint = 0;
\r
1145 service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;
\r
1147 /* Signal we AREN'T running the server */
\r
1148 service->process_handle = 0;
\r
1151 /* Register control handler */
\r
1152 service->status_handle = RegisterServiceCtrlHandlerEx(NSSM, service_control_handler, (void *) service);
\r
1153 if (! service->status_handle) {
\r
1154 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_REGISTERSERVICECTRLHANDER_FAILED, error_string(GetLastError()), 0);
\r
1158 log_service_control(service->name, 0, true);
\r
1160 service->status.dwCurrentState = SERVICE_START_PENDING;
\r
1161 service->status.dwWaitHint = service->throttle_delay + NSSM_WAITHINT_MARGIN;
\r
1162 SetServiceStatus(service->status_handle, &service->status);
\r
1165 /* Try to create the exit action parameters; we don't care if it fails */
\r
1166 create_exit_action(service->name, exit_action_strings[0], false);
\r
1168 SC_HANDLE services = open_service_manager();
\r
1170 service->handle = OpenService(services, service->name, SC_MANAGER_ALL_ACCESS);
\r
1171 set_service_recovery(service);
\r
1172 CloseServiceHandle(services);
\r
1176 /* Used for signalling a resume if the service pauses when throttled. */
\r
1177 if (use_critical_section) {
\r
1178 InitializeCriticalSection(&service->throttle_section);
\r
1179 service->throttle_section_initialised = true;
\r
1182 service->throttle_timer = CreateWaitableTimer(0, 1, 0);
\r
1183 if (! service->throttle_timer) {
\r
1184 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_CREATEWAITABLETIMER_FAILED, service->name, error_string(GetLastError()), 0);
\r
1188 /* Remember our initial environment. */
\r
1189 service->initial_env = GetEnvironmentStrings();
\r
1191 monitor_service(service);
\r
1194 /* Make sure service recovery actions are taken where necessary */
\r
1195 void set_service_recovery(nssm_service_t *service) {
\r
1196 SERVICE_FAILURE_ACTIONS_FLAG flag;
\r
1197 ZeroMemory(&flag, sizeof(flag));
\r
1198 flag.fFailureActionsOnNonCrashFailures = true;
\r
1200 /* This functionality was added in Vista so the call may fail */
\r
1201 if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, &flag)) {
\r
1202 unsigned long error = GetLastError();
\r
1203 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
\r
1204 if (error != ERROR_INVALID_LEVEL) {
\r
1205 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_FAILURE_ACTIONS_FAILED, service->name, error_string(error), 0);
\r
1210 int monitor_service(nssm_service_t *service) {
\r
1211 /* Set service status to started */
\r
1212 int ret = start_service(service);
\r
1215 _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%d"), ret);
\r
1216 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_START_SERVICE_FAILED, service->exe, service->name, ret, 0);
\r
1219 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_STARTED_SERVICE, service->exe, service->flags, service->name, service->dir, 0);
\r
1221 /* Monitor service */
\r
1222 if (! RegisterWaitForSingleObject(&service->wait_handle, service->process_handle, end_service, (void *) service, INFINITE, WT_EXECUTEONLYONCE | WT_EXECUTELONGFUNCTION)) {
\r
1223 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_REGISTERWAITFORSINGLEOBJECT_FAILED, service->name, service->exe, error_string(GetLastError()), 0);
\r
1229 TCHAR *service_control_text(unsigned long control) {
\r
1230 switch (control) {
\r
1231 /* HACK: there is no SERVICE_CONTROL_START constant */
\r
1232 case NSSM_SERVICE_CONTROL_START: return _T("START");
\r
1233 case SERVICE_CONTROL_STOP: return _T("STOP");
\r
1234 case SERVICE_CONTROL_SHUTDOWN: return _T("SHUTDOWN");
\r
1235 case SERVICE_CONTROL_PAUSE: return _T("PAUSE");
\r
1236 case SERVICE_CONTROL_CONTINUE: return _T("CONTINUE");
\r
1237 case SERVICE_CONTROL_INTERROGATE: return _T("INTERROGATE");
\r
1238 case NSSM_SERVICE_CONTROL_ROTATE: return _T("ROTATE");
\r
1239 default: return 0;
\r
1243 TCHAR *service_status_text(unsigned long status) {
\r
1245 case SERVICE_STOPPED: return _T("SERVICE_STOPPED");
\r
1246 case SERVICE_START_PENDING: return _T("SERVICE_START_PENDING");
\r
1247 case SERVICE_STOP_PENDING: return _T("SERVICE_STOP_PENDING");
\r
1248 case SERVICE_RUNNING: return _T("SERVICE_RUNNING");
\r
1249 case SERVICE_CONTINUE_PENDING: return _T("SERVICE_CONTINUE_PENDING");
\r
1250 case SERVICE_PAUSE_PENDING: return _T("SERVICE_PAUSE_PENDING");
\r
1251 case SERVICE_PAUSED: return _T("SERVICE_PAUSED");
\r
1252 default: return 0;
\r
1256 void log_service_control(TCHAR *service_name, unsigned long control, bool handled) {
\r
1257 TCHAR *text = service_control_text(control);
\r
1258 unsigned long event;
\r
1261 /* "0x" + 8 x hex + NULL */
\r
1262 text = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, 11 * sizeof(TCHAR));
\r
1264 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);
\r
1267 if (_sntprintf_s(text, 11, _TRUNCATE, _T("0x%08x"), control) < 0) {
\r
1268 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);
\r
1269 HeapFree(GetProcessHeap(), 0, text);
\r
1273 event = NSSM_EVENT_SERVICE_CONTROL_UNKNOWN;
\r
1275 else if (handled) event = NSSM_EVENT_SERVICE_CONTROL_HANDLED;
\r
1276 else event = NSSM_EVENT_SERVICE_CONTROL_NOT_HANDLED;
\r
1278 log_event(EVENTLOG_INFORMATION_TYPE, event, service_name, text, 0);
\r
1280 if (event == NSSM_EVENT_SERVICE_CONTROL_UNKNOWN) {
\r
1281 HeapFree(GetProcessHeap(), 0, text);
\r
1285 /* Service control handler */
\r
1286 unsigned long WINAPI service_control_handler(unsigned long control, unsigned long event, void *data, void *context) {
\r
1287 nssm_service_t *service = (nssm_service_t *) context;
\r
1289 switch (control) {
\r
1290 case SERVICE_CONTROL_INTERROGATE:
\r
1291 /* We always keep the service status up-to-date so this is a no-op. */
\r
1294 case SERVICE_CONTROL_SHUTDOWN:
\r
1295 case SERVICE_CONTROL_STOP:
\r
1296 log_service_control(service->name, control, true);
\r
1298 We MUST acknowledge the stop request promptly but we're committed to
\r
1299 waiting for the application to exit. Spawn a new thread to wait
\r
1300 while we acknowledge the request.
\r
1302 if (! CreateThread(NULL, 0, shutdown_service, context, 0, NULL)) {
\r
1303 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETHREAD_FAILED, error_string(GetLastError()), 0);
\r
1306 We couldn't create a thread to tidy up so we'll have to force the tidyup
\r
1307 to complete in time in this thread.
\r
1309 service->kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;
\r
1310 service->kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;
\r
1311 service->kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;
\r
1313 stop_service(service, 0, true, true);
\r
1317 case SERVICE_CONTROL_CONTINUE:
\r
1318 log_service_control(service->name, control, true);
\r
1319 service->throttle = 0;
\r
1320 if (use_critical_section) imports.WakeConditionVariable(&service->throttle_condition);
\r
1322 if (! service->throttle_timer) return ERROR_CALL_NOT_IMPLEMENTED;
\r
1323 ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));
\r
1324 SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);
\r
1326 /* We can't continue if the application is running! */
\r
1327 if (! service->process_handle) service->status.dwCurrentState = SERVICE_CONTINUE_PENDING;
\r
1328 service->status.dwWaitHint = throttle_milliseconds(service->throttle) + NSSM_WAITHINT_MARGIN;
\r
1329 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESET_THROTTLE, service->name, 0);
\r
1330 SetServiceStatus(service->status_handle, &service->status);
\r
1333 case SERVICE_CONTROL_PAUSE:
\r
1335 We don't accept pause messages but it isn't possible to register
\r
1336 only for continue messages so we have to handle this case.
\r
1338 log_service_control(service->name, control, false);
\r
1339 return ERROR_CALL_NOT_IMPLEMENTED;
\r
1341 case NSSM_SERVICE_CONTROL_ROTATE:
\r
1342 log_service_control(service->name, control, true);
\r
1343 if (service->rotate_stdout_online == NSSM_ROTATE_ONLINE) service->rotate_stdout_online = NSSM_ROTATE_ONLINE_ASAP;
\r
1344 if (service->rotate_stderr_online == NSSM_ROTATE_ONLINE) service->rotate_stderr_online = NSSM_ROTATE_ONLINE_ASAP;
\r
1348 /* Unknown control */
\r
1349 log_service_control(service->name, control, false);
\r
1350 return ERROR_CALL_NOT_IMPLEMENTED;
\r
1353 /* Start the service */
\r
1354 int start_service(nssm_service_t *service) {
\r
1355 service->stopping = false;
\r
1356 service->allow_restart = true;
\r
1358 if (service->process_handle) return 0;
\r
1360 /* Allocate a STARTUPINFO structure for a new process */
\r
1362 ZeroMemory(&si, sizeof(si));
\r
1363 si.cb = sizeof(si);
\r
1365 /* Allocate a PROCESSINFO structure for the process */
\r
1366 PROCESS_INFORMATION pi;
\r
1367 ZeroMemory(&pi, sizeof(pi));
\r
1369 /* Get startup parameters */
\r
1370 int ret = get_parameters(service, &si);
\r
1372 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_PARAMETERS_FAILED, service->name, 0);
\r
1373 return stop_service(service, 2, true, true);
\r
1376 /* Launch executable with arguments */
\r
1377 TCHAR cmd[CMD_LENGTH];
\r
1378 if (_sntprintf_s(cmd, _countof(cmd), _TRUNCATE, _T("\"%s\" %s"), service->exe, service->flags) < 0) {
\r
1379 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("command line"), _T("start_service"), 0);
\r
1380 return stop_service(service, 2, true, true);
\r
1383 throttle_restart(service);
\r
1385 /* Set the environment. */
\r
1386 if (service->env) duplicate_environment(service->env);
\r
1387 if (service->env_extra) set_environment_block(service->env_extra);
\r
1389 /* Set up I/O redirection. */
\r
1390 if (get_output_handles(service, &si)) {
\r
1391 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_OUTPUT_HANDLES_FAILED, service->name, 0);
\r
1392 if (! service->no_console) FreeConsole();
\r
1393 close_output_handles(&si);
\r
1394 return stop_service(service, 4, true, true);
\r
1397 bool inherit_handles = false;
\r
1398 if (si.dwFlags & STARTF_USESTDHANDLES) inherit_handles = true;
\r
1399 unsigned long flags = service->priority & priority_mask();
\r
1400 if (service->affinity) flags |= CREATE_SUSPENDED;
\r
1401 if (! CreateProcess(0, cmd, 0, 0, inherit_handles, flags, 0, service->dir, &si, &pi)) {
\r
1402 unsigned long exitcode = 3;
\r
1403 unsigned long error = GetLastError();
\r
1404 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service->name, service->exe, error_string(error), 0);
\r
1405 close_output_handles(&si);
\r
1406 duplicate_environment(service->initial_env);
\r
1407 return stop_service(service, exitcode, true, true);
\r
1409 service->process_handle = pi.hProcess;
\r
1410 service->pid = pi.dwProcessId;
\r
1412 if (get_process_creation_time(service->process_handle, &service->creation_time)) ZeroMemory(&service->creation_time, sizeof(service->creation_time));
\r
1414 close_output_handles(&si);
\r
1416 if (! service->no_console) FreeConsole();
\r
1418 /* Restore our environment. */
\r
1419 duplicate_environment(service->initial_env);
\r
1421 if (service->affinity) {
\r
1423 We are explicitly storing service->affinity as a 64-bit unsigned integer
\r
1424 so that we can parse it regardless of whether we're running in 32-bit
\r
1425 or 64-bit mode. The arguments to SetProcessAffinityMask(), however, are
\r
1426 defined as type DWORD_PTR and hence limited to 32 bits on a 32-bit system
\r
1427 (or when running the 32-bit NSSM).
\r
1429 The result is a lot of seemingly-unnecessary casting throughout the code
\r
1430 and potentially confusion when we actually try to start the service.
\r
1431 Having said that, however, it's unlikely that we're actually going to
\r
1432 run in 32-bit mode on a system which has more than 32 CPUs so the
\r
1433 likelihood of seeing a confusing situation is somewhat diminished.
\r
1435 DWORD_PTR affinity, system_affinity;
\r
1437 if (GetProcessAffinityMask(service->process_handle, &affinity, &system_affinity)) affinity = service->affinity & system_affinity;
\r
1439 affinity = (DWORD_PTR) service->affinity;
\r
1440 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GETPROCESSAFFINITYMASK_FAILED, service->name, error_string(GetLastError()), 0);
\r
1443 if (! SetProcessAffinityMask(service->process_handle, affinity)) {
\r
1444 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_SETPROCESSAFFINITYMASK_FAILED, service->name, error_string(GetLastError()), 0);
\r
1447 ResumeThread(pi.hThread);
\r
1451 Wait for a clean startup before changing the service status to RUNNING
\r
1452 but be mindful of the fact that we are blocking the service control manager
\r
1453 so abandon the wait before too much time has elapsed.
\r
1455 unsigned long delay = service->throttle_delay;
\r
1456 if (delay > NSSM_SERVICE_STATUS_DEADLINE) {
\r
1457 TCHAR delay_milliseconds[16];
\r
1458 _sntprintf_s(delay_milliseconds, _countof(delay_milliseconds), _TRUNCATE, _T("%lu"), delay);
\r
1459 TCHAR deadline_milliseconds[16];
\r
1460 _sntprintf_s(deadline_milliseconds, _countof(deadline_milliseconds), _TRUNCATE, _T("%lu"), NSSM_SERVICE_STATUS_DEADLINE);
\r
1461 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_STARTUP_DELAY_TOO_LONG, service->name, delay_milliseconds, NSSM, deadline_milliseconds, 0);
\r
1462 delay = NSSM_SERVICE_STATUS_DEADLINE;
\r
1464 unsigned long deadline = WaitForSingleObject(service->process_handle, delay);
\r
1466 /* Signal successful start */
\r
1467 service->status.dwCurrentState = SERVICE_RUNNING;
\r
1468 SetServiceStatus(service->status_handle, &service->status);
\r
1470 /* Continue waiting for a clean startup. */
\r
1471 if (deadline == WAIT_TIMEOUT) {
\r
1472 if (service->throttle_delay > delay) {
\r
1473 if (WaitForSingleObject(service->process_handle, service->throttle_delay - delay) == WAIT_TIMEOUT) service->throttle = 0;
\r
1475 else service->throttle = 0;
\r
1478 /* Ensure the restart delay is always applied. */
\r
1479 if (service->restart_delay && ! service->throttle) service->throttle++;
\r
1484 /* Stop the service */
\r
1485 int stop_service(nssm_service_t *service, unsigned long exitcode, bool graceful, bool default_action) {
\r
1486 service->allow_restart = false;
\r
1487 if (service->wait_handle) {
\r
1488 UnregisterWait(service->wait_handle);
\r
1489 service->wait_handle = 0;
\r
1492 service->rotate_stdout_online = service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;
\r
1494 if (default_action && ! exitcode && ! graceful) {
\r
1495 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
1499 /* Signal we are stopping */
\r
1501 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
1502 service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;
\r
1503 SetServiceStatus(service->status_handle, &service->status);
\r
1506 /* Nothing to do if service isn't running */
\r
1507 if (service->pid) {
\r
1508 /* Shut down service */
\r
1509 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_TERMINATEPROCESS, service->name, service->exe, 0);
\r
1510 kill_process(service, service->process_handle, service->pid, 0);
\r
1512 else log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_PROCESS_ALREADY_STOPPED, service->name, service->exe, 0);
\r
1514 end_service((void *) service, true);
\r
1516 /* Signal we stopped */
\r
1518 service->status.dwCurrentState = SERVICE_STOPPED;
\r
1520 service->status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
\r
1521 service->status.dwServiceSpecificExitCode = exitcode;
\r
1524 service->status.dwWin32ExitCode = NO_ERROR;
\r
1525 service->status.dwServiceSpecificExitCode = 0;
\r
1527 SetServiceStatus(service->status_handle, &service->status);
\r
1533 /* Callback function triggered when the server exits */
\r
1534 void CALLBACK end_service(void *arg, unsigned char why) {
\r
1535 nssm_service_t *service = (nssm_service_t *) arg;
\r
1537 if (service->stopping) return;
\r
1539 service->stopping = true;
\r
1541 service->rotate_stdout_online = service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;
\r
1543 /* Use now as a dummy exit time. */
\r
1544 GetSystemTimeAsFileTime(&service->exit_time);
\r
1546 /* Check exit code */
\r
1547 unsigned long exitcode = 0;
\r
1549 if (service->process_handle) {
\r
1550 GetExitCodeProcess(service->process_handle, &exitcode);
\r
1551 /* Check real exit time. */
\r
1552 if (exitcode != STILL_ACTIVE) get_process_exit_time(service->process_handle, &service->exit_time);
\r
1553 CloseHandle(service->process_handle);
\r
1556 service->process_handle = 0;
\r
1559 Log that the service ended BEFORE logging about killing the process
\r
1560 tree. See below for the possible values of the why argument.
\r
1563 _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), exitcode);
\r
1564 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ENDED_SERVICE, service->exe, service->name, code, 0);
\r
1568 if (exitcode == STILL_ACTIVE) exitcode = 0;
\r
1569 if (service->pid) kill_process_tree(service, service->pid, exitcode, service->pid);
\r
1573 The why argument is true if our wait timed out or false otherwise.
\r
1574 Our wait is infinite so why will never be true when called by the system.
\r
1575 If it is indeed true, assume we were called from stop_service() because
\r
1576 this is a controlled shutdown, and don't take any restart action.
\r
1579 if (! service->allow_restart) return;
\r
1581 /* What action should we take? */
\r
1582 int action = NSSM_EXIT_RESTART;
\r
1583 TCHAR action_string[ACTION_LEN];
\r
1584 bool default_action;
\r
1585 if (! get_exit_action(service->name, &exitcode, action_string, &default_action)) {
\r
1586 for (int i = 0; exit_action_strings[i]; i++) {
\r
1587 if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
1595 /* Try to restart the service or return failure code to service manager */
\r
1596 case NSSM_EXIT_RESTART:
\r
1597 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_RESTART, service->name, code, exit_action_strings[action], service->exe, 0);
\r
1598 while (monitor_service(service)) {
\r
1599 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_RESTART_SERVICE_FAILED, service->exe, service->name, 0);
\r
1604 /* Do nothing, just like srvany would */
\r
1605 case NSSM_EXIT_IGNORE:
\r
1606 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_IGNORE, service->name, code, exit_action_strings[action], service->exe, 0);
\r
1610 /* Tell the service manager we are finished */
\r
1611 case NSSM_EXIT_REALLY:
\r
1612 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_REALLY, service->name, code, exit_action_strings[action], 0);
\r
1613 stop_service(service, exitcode, true, default_action);
\r
1616 /* Fake a crash so pre-Vista service managers will run recovery actions. */
\r
1617 case NSSM_EXIT_UNCLEAN:
\r
1618 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_UNCLEAN, service->name, code, exit_action_strings[action], 0);
\r
1619 stop_service(service, exitcode, false, default_action);
\r
1626 void throttle_restart(nssm_service_t *service) {
\r
1627 /* This can't be a restart if the service is already running. */
\r
1628 if (! service->throttle++) return;
\r
1631 unsigned long throttle_ms = throttle_milliseconds(service->throttle);
\r
1632 TCHAR threshold[8], milliseconds[8];
\r
1634 if (service->restart_delay > throttle_ms) ms = service->restart_delay;
\r
1635 else ms = throttle_ms;
\r
1637 if (service->throttle > 7) service->throttle = 8;
\r
1639 _sntprintf_s(milliseconds, _countof(milliseconds), _TRUNCATE, _T("%lu"), ms);
\r
1641 if (service->throttle == 1 && service->restart_delay > throttle_ms) log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESTART_DELAY, service->name, milliseconds, 0);
\r
1643 _sntprintf_s(threshold, _countof(threshold), _TRUNCATE, _T("%lu"), service->throttle_delay);
\r
1644 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_THROTTLED, service->name, threshold, milliseconds, 0);
\r
1647 if (use_critical_section) EnterCriticalSection(&service->throttle_section);
\r
1648 else if (service->throttle_timer) {
\r
1649 ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));
\r
1650 service->throttle_duetime.QuadPart = 0 - (ms * 10000LL);
\r
1651 SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);
\r
1654 service->status.dwCurrentState = SERVICE_PAUSED;
\r
1655 SetServiceStatus(service->status_handle, &service->status);
\r
1657 if (use_critical_section) {
\r
1658 imports.SleepConditionVariableCS(&service->throttle_condition, &service->throttle_section, ms);
\r
1659 LeaveCriticalSection(&service->throttle_section);
\r
1662 if (service->throttle_timer) WaitForSingleObject(service->throttle_timer, INFINITE);
\r
1668 When responding to a stop (or any other) request we need to set dwWaitHint to
\r
1669 the number of milliseconds we expect the operation to take, and optionally
\r
1670 increase dwCheckPoint. If dwWaitHint milliseconds elapses without the
\r
1671 operation completing or dwCheckPoint increasing, the system will consider the
\r
1672 service to be hung.
\r
1674 However the system will consider the service to be hung after 30000
\r
1675 milliseconds regardless of the value of dwWaitHint if dwCheckPoint has not
\r
1676 changed. Therefore if we want to wait longer than that we must periodically
\r
1677 increase dwCheckPoint.
\r
1679 Furthermore, it will consider the service to be hung after 60000 milliseconds
\r
1680 regardless of the value of dwCheckPoint unless dwWaitHint is increased every
\r
1681 time dwCheckPoint is also increased.
\r
1683 Our strategy then is to retrieve the initial dwWaitHint and wait for
\r
1684 NSSM_SERVICE_STATUS_DEADLINE milliseconds. If the process is still running
\r
1685 and we haven't finished waiting we increment dwCheckPoint and add whichever is
\r
1686 smaller of NSSM_SERVICE_STATUS_DEADLINE or the remaining timeout to
\r
1689 Only doing both these things will prevent the system from killing the service.
\r
1691 Returns: 1 if the wait timed out.
\r
1692 0 if the wait completed.
\r
1695 int await_shutdown(nssm_service_t *service, TCHAR *function_name, unsigned long timeout) {
\r
1696 unsigned long interval;
\r
1697 unsigned long waithint;
\r
1698 unsigned long ret;
\r
1699 unsigned long waited;
\r
1700 TCHAR interval_milliseconds[16];
\r
1701 TCHAR timeout_milliseconds[16];
\r
1702 TCHAR waited_milliseconds[16];
\r
1703 TCHAR *function = function_name;
\r
1705 /* Add brackets to function name. */
\r
1706 size_t funclen = _tcslen(function_name) + 3;
\r
1707 TCHAR *func = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, funclen * sizeof(TCHAR));
\r
1709 if (_sntprintf_s(func, funclen, _TRUNCATE, _T("%s()"), function_name) > -1) function = func;
\r
1712 _sntprintf_s(timeout_milliseconds, _countof(timeout_milliseconds), _TRUNCATE, _T("%lu"), timeout);
\r
1714 waithint = service->status.dwWaitHint;
\r
1716 while (waited < timeout) {
\r
1717 interval = timeout - waited;
\r
1718 if (interval > NSSM_SERVICE_STATUS_DEADLINE) interval = NSSM_SERVICE_STATUS_DEADLINE;
\r
1720 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
1721 service->status.dwWaitHint += interval;
\r
1722 service->status.dwCheckPoint++;
\r
1723 SetServiceStatus(service->status_handle, &service->status);
\r
1726 _sntprintf_s(waited_milliseconds, _countof(waited_milliseconds), _TRUNCATE, _T("%lu"), waited);
\r
1727 _sntprintf_s(interval_milliseconds, _countof(interval_milliseconds), _TRUNCATE, _T("%lu"), interval);
\r
1728 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_AWAITING_SHUTDOWN, function, service->name, waited_milliseconds, interval_milliseconds, timeout_milliseconds, 0);
\r
1731 switch (WaitForSingleObject(service->process_handle, interval)) {
\r
1732 case WAIT_OBJECT_0:
\r
1736 case WAIT_TIMEOUT:
\r
1745 waited += interval;
\r
1749 if (func) HeapFree(GetProcessHeap(), 0, func);
\r