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 (requires_password(username)) {
\r
938 if (grant_logon_as_service(username)) {
\r
939 print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username);
\r
944 if (! ChangeServiceConfig(service->handle, service->type, startup, SERVICE_NO_CHANGE, 0, 0, 0, 0, username, password, service->displayname)) {
\r
945 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
949 if (service->description[0] || editing) {
\r
950 set_service_description(service->name, service->handle, service->description);
\r
953 SERVICE_DELAYED_AUTO_START_INFO delayed;
\r
954 ZeroMemory(&delayed, sizeof(delayed));
\r
955 if (service->startup == NSSM_STARTUP_DELAYED) delayed.fDelayedAutostart = 1;
\r
956 else delayed.fDelayedAutostart = 0;
\r
957 /* Delayed startup isn't supported until Vista. */
\r
958 if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, &delayed)) {
\r
959 unsigned long error = GetLastError();
\r
960 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
\r
961 if (error != ERROR_INVALID_LEVEL) {
\r
962 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_DELAYED_AUTO_START_INFO_FAILED, service->name, error_string(error), 0);
\r
966 /* Don't mess with parameters which aren't ours. */
\r
967 if (! service->native) {
\r
968 /* Now we need to put the parameters into the registry */
\r
969 if (create_parameters(service, editing)) {
\r
970 print_message(stderr, NSSM_MESSAGE_CREATE_PARAMETERS_FAILED);
\r
974 set_service_recovery(service);
\r
980 /* Control a service. */
\r
981 int control_service(unsigned long control, int argc, TCHAR **argv) {
\r
982 if (argc < 1) return usage(1);
\r
983 TCHAR *service_name = argv[0];
\r
984 TCHAR canonical_name[SERVICE_NAME_LENGTH];
\r
986 SC_HANDLE services = open_service_manager();
\r
988 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
992 SC_HANDLE service_handle = open_service(services, service_name, canonical_name, _countof(canonical_name));
\r
993 if (! service_handle) {
\r
994 CloseServiceHandle(services);
\r
999 unsigned long error;
\r
1000 SERVICE_STATUS service_status;
\r
1001 if (control == NSSM_SERVICE_CONTROL_START) {
\r
1002 unsigned long initial_status = SERVICE_STOPPED;
\r
1003 ret = StartService(service_handle, (unsigned long) argc, (const TCHAR **) argv);
\r
1004 error = GetLastError();
\r
1005 CloseServiceHandle(services);
\r
1007 if (error == ERROR_IO_PENDING) {
\r
1009 Older versions of Windows return immediately with ERROR_IO_PENDING
\r
1010 indicate that the operation is still in progress. Newer versions
\r
1011 will return it if there really is a delay.
\r
1014 error = ERROR_SUCCESS;
\r
1018 int response = await_service_control_response(control, service_handle, &service_status, initial_status);
\r
1019 CloseHandle(service_handle);
\r
1022 print_message(stderr, NSSM_MESSAGE_BAD_CONTROL_RESPONSE, canonical_name, service_status_text(service_status.dwCurrentState), service_control_text(control));
\r
1025 else _tprintf(_T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1029 CloseHandle(service_handle);
\r
1030 _ftprintf(stderr, _T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1034 else if (control == SERVICE_CONTROL_INTERROGATE) {
\r
1036 We could actually send an INTERROGATE control but that won't return
\r
1037 any information if the service is stopped and we don't care about
\r
1038 the extra details it might give us in any case. So we'll fake it.
\r
1040 ret = QueryServiceStatus(service_handle, &service_status);
\r
1041 error = GetLastError();
\r
1044 _tprintf(_T("%s\n"), service_status_text(service_status.dwCurrentState));
\r
1048 _ftprintf(stderr, _T("%s: %s\n"), canonical_name, error_string(error));
\r
1053 ret = ControlService(service_handle, control, &service_status);
\r
1054 unsigned long initial_status = service_status.dwCurrentState;
\r
1055 error = GetLastError();
\r
1056 CloseServiceHandle(services);
\r
1058 if (error == ERROR_IO_PENDING) {
\r
1060 error = ERROR_SUCCESS;
\r
1064 int response = await_service_control_response(control, service_handle, &service_status, initial_status);
\r
1065 CloseHandle(service_handle);
\r
1068 print_message(stderr, NSSM_MESSAGE_BAD_CONTROL_RESPONSE, canonical_name, service_status_text(service_status.dwCurrentState), service_control_text(control));
\r
1071 else _tprintf(_T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1075 CloseHandle(service_handle);
\r
1076 _ftprintf(stderr, _T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1077 if (error == ERROR_SERVICE_NOT_ACTIVE) {
\r
1078 if (control == SERVICE_CONTROL_SHUTDOWN || control == SERVICE_CONTROL_STOP) return 0;
\r
1085 /* Remove the service */
\r
1086 int remove_service(nssm_service_t *service) {
\r
1087 if (! service) return 1;
\r
1089 /* Open service manager */
\r
1090 SC_HANDLE services = open_service_manager();
\r
1092 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
1096 /* Try to open the service */
\r
1097 service->handle = open_service(services, service->name, service->name, _countof(service->name));
\r
1098 if (! service->handle) {
\r
1099 CloseServiceHandle(services);
\r
1103 /* Get the canonical service name. We open it case insensitively. */
\r
1104 unsigned long bufsize = _countof(service->displayname);
\r
1105 GetServiceDisplayName(services, service->name, service->displayname, &bufsize);
\r
1106 bufsize = _countof(service->name);
\r
1107 GetServiceKeyName(services, service->displayname, service->name, &bufsize);
\r
1109 /* Try to delete the service */
\r
1110 if (! DeleteService(service->handle)) {
\r
1111 print_message(stderr, NSSM_MESSAGE_DELETESERVICE_FAILED);
\r
1112 CloseServiceHandle(services);
\r
1117 CloseServiceHandle(services);
\r
1119 print_message(stdout, NSSM_MESSAGE_SERVICE_REMOVED, service->name);
\r
1123 /* Service initialisation */
\r
1124 void WINAPI service_main(unsigned long argc, TCHAR **argv) {
\r
1125 nssm_service_t *service = alloc_nssm_service();
\r
1126 if (! service) return;
\r
1128 if (_sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]) < 0) {
\r
1129 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("service->name"), _T("service_main()"), 0);
\r
1133 /* We can use a condition variable in a critical section on Vista or later. */
\r
1134 if (imports.SleepConditionVariableCS && imports.WakeConditionVariable) use_critical_section = true;
\r
1135 else use_critical_section = false;
\r
1137 /* Initialise status */
\r
1138 ZeroMemory(&service->status, sizeof(service->status));
\r
1139 service->status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;
\r
1140 service->status.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE;
\r
1141 service->status.dwWin32ExitCode = NO_ERROR;
\r
1142 service->status.dwServiceSpecificExitCode = 0;
\r
1143 service->status.dwCheckPoint = 0;
\r
1144 service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;
\r
1146 /* Signal we AREN'T running the server */
\r
1147 service->process_handle = 0;
\r
1150 /* Register control handler */
\r
1151 service->status_handle = RegisterServiceCtrlHandlerEx(NSSM, service_control_handler, (void *) service);
\r
1152 if (! service->status_handle) {
\r
1153 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_REGISTERSERVICECTRLHANDER_FAILED, error_string(GetLastError()), 0);
\r
1157 log_service_control(service->name, 0, true);
\r
1159 service->status.dwCurrentState = SERVICE_START_PENDING;
\r
1160 service->status.dwWaitHint = service->throttle_delay + NSSM_WAITHINT_MARGIN;
\r
1161 SetServiceStatus(service->status_handle, &service->status);
\r
1164 /* Try to create the exit action parameters; we don't care if it fails */
\r
1165 create_exit_action(service->name, exit_action_strings[0], false);
\r
1167 SC_HANDLE services = open_service_manager();
\r
1169 service->handle = OpenService(services, service->name, SC_MANAGER_ALL_ACCESS);
\r
1170 set_service_recovery(service);
\r
1171 CloseServiceHandle(services);
\r
1175 /* Used for signalling a resume if the service pauses when throttled. */
\r
1176 if (use_critical_section) {
\r
1177 InitializeCriticalSection(&service->throttle_section);
\r
1178 service->throttle_section_initialised = true;
\r
1181 service->throttle_timer = CreateWaitableTimer(0, 1, 0);
\r
1182 if (! service->throttle_timer) {
\r
1183 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_CREATEWAITABLETIMER_FAILED, service->name, error_string(GetLastError()), 0);
\r
1187 /* Remember our initial environment. */
\r
1188 service->initial_env = GetEnvironmentStrings();
\r
1190 monitor_service(service);
\r
1193 /* Make sure service recovery actions are taken where necessary */
\r
1194 void set_service_recovery(nssm_service_t *service) {
\r
1195 SERVICE_FAILURE_ACTIONS_FLAG flag;
\r
1196 ZeroMemory(&flag, sizeof(flag));
\r
1197 flag.fFailureActionsOnNonCrashFailures = true;
\r
1199 /* This functionality was added in Vista so the call may fail */
\r
1200 if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, &flag)) {
\r
1201 unsigned long error = GetLastError();
\r
1202 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
\r
1203 if (error != ERROR_INVALID_LEVEL) {
\r
1204 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_FAILURE_ACTIONS_FAILED, service->name, error_string(error), 0);
\r
1209 int monitor_service(nssm_service_t *service) {
\r
1210 /* Set service status to started */
\r
1211 int ret = start_service(service);
\r
1214 _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%d"), ret);
\r
1215 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_START_SERVICE_FAILED, service->exe, service->name, ret, 0);
\r
1218 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_STARTED_SERVICE, service->exe, service->flags, service->name, service->dir, 0);
\r
1220 /* Monitor service */
\r
1221 if (! RegisterWaitForSingleObject(&service->wait_handle, service->process_handle, end_service, (void *) service, INFINITE, WT_EXECUTEONLYONCE | WT_EXECUTELONGFUNCTION)) {
\r
1222 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_REGISTERWAITFORSINGLEOBJECT_FAILED, service->name, service->exe, error_string(GetLastError()), 0);
\r
1228 TCHAR *service_control_text(unsigned long control) {
\r
1229 switch (control) {
\r
1230 /* HACK: there is no SERVICE_CONTROL_START constant */
\r
1231 case NSSM_SERVICE_CONTROL_START: return _T("START");
\r
1232 case SERVICE_CONTROL_STOP: return _T("STOP");
\r
1233 case SERVICE_CONTROL_SHUTDOWN: return _T("SHUTDOWN");
\r
1234 case SERVICE_CONTROL_PAUSE: return _T("PAUSE");
\r
1235 case SERVICE_CONTROL_CONTINUE: return _T("CONTINUE");
\r
1236 case SERVICE_CONTROL_INTERROGATE: return _T("INTERROGATE");
\r
1237 case NSSM_SERVICE_CONTROL_ROTATE: return _T("ROTATE");
\r
1238 default: return 0;
\r
1242 TCHAR *service_status_text(unsigned long status) {
\r
1244 case SERVICE_STOPPED: return _T("SERVICE_STOPPED");
\r
1245 case SERVICE_START_PENDING: return _T("SERVICE_START_PENDING");
\r
1246 case SERVICE_STOP_PENDING: return _T("SERVICE_STOP_PENDING");
\r
1247 case SERVICE_RUNNING: return _T("SERVICE_RUNNING");
\r
1248 case SERVICE_CONTINUE_PENDING: return _T("SERVICE_CONTINUE_PENDING");
\r
1249 case SERVICE_PAUSE_PENDING: return _T("SERVICE_PAUSE_PENDING");
\r
1250 case SERVICE_PAUSED: return _T("SERVICE_PAUSED");
\r
1251 default: return 0;
\r
1255 void log_service_control(TCHAR *service_name, unsigned long control, bool handled) {
\r
1256 TCHAR *text = service_control_text(control);
\r
1257 unsigned long event;
\r
1260 /* "0x" + 8 x hex + NULL */
\r
1261 text = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, 11 * sizeof(TCHAR));
\r
1263 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);
\r
1266 if (_sntprintf_s(text, 11, _TRUNCATE, _T("0x%08x"), control) < 0) {
\r
1267 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);
\r
1268 HeapFree(GetProcessHeap(), 0, text);
\r
1272 event = NSSM_EVENT_SERVICE_CONTROL_UNKNOWN;
\r
1274 else if (handled) event = NSSM_EVENT_SERVICE_CONTROL_HANDLED;
\r
1275 else event = NSSM_EVENT_SERVICE_CONTROL_NOT_HANDLED;
\r
1277 log_event(EVENTLOG_INFORMATION_TYPE, event, service_name, text, 0);
\r
1279 if (event == NSSM_EVENT_SERVICE_CONTROL_UNKNOWN) {
\r
1280 HeapFree(GetProcessHeap(), 0, text);
\r
1284 /* Service control handler */
\r
1285 unsigned long WINAPI service_control_handler(unsigned long control, unsigned long event, void *data, void *context) {
\r
1286 nssm_service_t *service = (nssm_service_t *) context;
\r
1288 switch (control) {
\r
1289 case SERVICE_CONTROL_INTERROGATE:
\r
1290 /* We always keep the service status up-to-date so this is a no-op. */
\r
1293 case SERVICE_CONTROL_SHUTDOWN:
\r
1294 case SERVICE_CONTROL_STOP:
\r
1295 log_service_control(service->name, control, true);
\r
1297 We MUST acknowledge the stop request promptly but we're committed to
\r
1298 waiting for the application to exit. Spawn a new thread to wait
\r
1299 while we acknowledge the request.
\r
1301 if (! CreateThread(NULL, 0, shutdown_service, context, 0, NULL)) {
\r
1302 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETHREAD_FAILED, error_string(GetLastError()), 0);
\r
1305 We couldn't create a thread to tidy up so we'll have to force the tidyup
\r
1306 to complete in time in this thread.
\r
1308 service->kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;
\r
1309 service->kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;
\r
1310 service->kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;
\r
1312 stop_service(service, 0, true, true);
\r
1316 case SERVICE_CONTROL_CONTINUE:
\r
1317 log_service_control(service->name, control, true);
\r
1318 service->throttle = 0;
\r
1319 if (use_critical_section) imports.WakeConditionVariable(&service->throttle_condition);
\r
1321 if (! service->throttle_timer) return ERROR_CALL_NOT_IMPLEMENTED;
\r
1322 ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));
\r
1323 SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);
\r
1325 /* We can't continue if the application is running! */
\r
1326 if (! service->process_handle) service->status.dwCurrentState = SERVICE_CONTINUE_PENDING;
\r
1327 service->status.dwWaitHint = throttle_milliseconds(service->throttle) + NSSM_WAITHINT_MARGIN;
\r
1328 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESET_THROTTLE, service->name, 0);
\r
1329 SetServiceStatus(service->status_handle, &service->status);
\r
1332 case SERVICE_CONTROL_PAUSE:
\r
1334 We don't accept pause messages but it isn't possible to register
\r
1335 only for continue messages so we have to handle this case.
\r
1337 log_service_control(service->name, control, false);
\r
1338 return ERROR_CALL_NOT_IMPLEMENTED;
\r
1340 case NSSM_SERVICE_CONTROL_ROTATE:
\r
1341 log_service_control(service->name, control, true);
\r
1342 if (service->rotate_stdout_online == NSSM_ROTATE_ONLINE) service->rotate_stdout_online = NSSM_ROTATE_ONLINE_ASAP;
\r
1343 if (service->rotate_stderr_online == NSSM_ROTATE_ONLINE) service->rotate_stderr_online = NSSM_ROTATE_ONLINE_ASAP;
\r
1347 /* Unknown control */
\r
1348 log_service_control(service->name, control, false);
\r
1349 return ERROR_CALL_NOT_IMPLEMENTED;
\r
1352 /* Start the service */
\r
1353 int start_service(nssm_service_t *service) {
\r
1354 service->stopping = false;
\r
1355 service->allow_restart = true;
\r
1357 if (service->process_handle) return 0;
\r
1359 /* Allocate a STARTUPINFO structure for a new process */
\r
1361 ZeroMemory(&si, sizeof(si));
\r
1362 si.cb = sizeof(si);
\r
1364 /* Allocate a PROCESSINFO structure for the process */
\r
1365 PROCESS_INFORMATION pi;
\r
1366 ZeroMemory(&pi, sizeof(pi));
\r
1368 /* Get startup parameters */
\r
1369 int ret = get_parameters(service, &si);
\r
1371 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_PARAMETERS_FAILED, service->name, 0);
\r
1372 return stop_service(service, 2, true, true);
\r
1375 /* Launch executable with arguments */
\r
1376 TCHAR cmd[CMD_LENGTH];
\r
1377 if (_sntprintf_s(cmd, _countof(cmd), _TRUNCATE, _T("\"%s\" %s"), service->exe, service->flags) < 0) {
\r
1378 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("command line"), _T("start_service"), 0);
\r
1379 return stop_service(service, 2, true, true);
\r
1382 throttle_restart(service);
\r
1384 /* Set the environment. */
\r
1385 if (service->env) duplicate_environment(service->env);
\r
1386 if (service->env_extra) set_environment_block(service->env_extra);
\r
1388 /* Set up I/O redirection. */
\r
1389 if (get_output_handles(service, &si)) {
\r
1390 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_OUTPUT_HANDLES_FAILED, service->name, 0);
\r
1391 if (! service->no_console) FreeConsole();
\r
1392 close_output_handles(&si);
\r
1393 return stop_service(service, 4, true, true);
\r
1396 bool inherit_handles = false;
\r
1397 if (si.dwFlags & STARTF_USESTDHANDLES) inherit_handles = true;
\r
1398 unsigned long flags = service->priority & priority_mask();
\r
1399 if (service->affinity) flags |= CREATE_SUSPENDED;
\r
1400 if (! CreateProcess(0, cmd, 0, 0, inherit_handles, flags, 0, service->dir, &si, &pi)) {
\r
1401 unsigned long exitcode = 3;
\r
1402 unsigned long error = GetLastError();
\r
1403 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service->name, service->exe, error_string(error), 0);
\r
1404 close_output_handles(&si);
\r
1405 duplicate_environment(service->initial_env);
\r
1406 return stop_service(service, exitcode, true, true);
\r
1408 service->process_handle = pi.hProcess;
\r
1409 service->pid = pi.dwProcessId;
\r
1411 if (get_process_creation_time(service->process_handle, &service->creation_time)) ZeroMemory(&service->creation_time, sizeof(service->creation_time));
\r
1413 close_output_handles(&si);
\r
1415 if (! service->no_console) FreeConsole();
\r
1417 /* Restore our environment. */
\r
1418 duplicate_environment(service->initial_env);
\r
1420 if (service->affinity) {
\r
1422 We are explicitly storing service->affinity as a 64-bit unsigned integer
\r
1423 so that we can parse it regardless of whether we're running in 32-bit
\r
1424 or 64-bit mode. The arguments to SetProcessAffinityMask(), however, are
\r
1425 defined as type DWORD_PTR and hence limited to 32 bits on a 32-bit system
\r
1426 (or when running the 32-bit NSSM).
\r
1428 The result is a lot of seemingly-unnecessary casting throughout the code
\r
1429 and potentially confusion when we actually try to start the service.
\r
1430 Having said that, however, it's unlikely that we're actually going to
\r
1431 run in 32-bit mode on a system which has more than 32 CPUs so the
\r
1432 likelihood of seeing a confusing situation is somewhat diminished.
\r
1434 DWORD_PTR affinity, system_affinity;
\r
1436 if (GetProcessAffinityMask(service->process_handle, &affinity, &system_affinity)) affinity = service->affinity & system_affinity;
\r
1438 affinity = (DWORD_PTR) service->affinity;
\r
1439 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GETPROCESSAFFINITYMASK_FAILED, service->name, error_string(GetLastError()), 0);
\r
1442 if (! SetProcessAffinityMask(service->process_handle, affinity)) {
\r
1443 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_SETPROCESSAFFINITYMASK_FAILED, service->name, error_string(GetLastError()), 0);
\r
1446 ResumeThread(pi.hThread);
\r
1450 Wait for a clean startup before changing the service status to RUNNING
\r
1451 but be mindful of the fact that we are blocking the service control manager
\r
1452 so abandon the wait before too much time has elapsed.
\r
1454 unsigned long delay = service->throttle_delay;
\r
1455 if (delay > NSSM_SERVICE_STATUS_DEADLINE) {
\r
1456 TCHAR delay_milliseconds[16];
\r
1457 _sntprintf_s(delay_milliseconds, _countof(delay_milliseconds), _TRUNCATE, _T("%lu"), delay);
\r
1458 TCHAR deadline_milliseconds[16];
\r
1459 _sntprintf_s(deadline_milliseconds, _countof(deadline_milliseconds), _TRUNCATE, _T("%lu"), NSSM_SERVICE_STATUS_DEADLINE);
\r
1460 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_STARTUP_DELAY_TOO_LONG, service->name, delay_milliseconds, NSSM, deadline_milliseconds, 0);
\r
1461 delay = NSSM_SERVICE_STATUS_DEADLINE;
\r
1463 unsigned long deadline = WaitForSingleObject(service->process_handle, delay);
\r
1465 /* Signal successful start */
\r
1466 service->status.dwCurrentState = SERVICE_RUNNING;
\r
1467 SetServiceStatus(service->status_handle, &service->status);
\r
1469 /* Continue waiting for a clean startup. */
\r
1470 if (deadline == WAIT_TIMEOUT) {
\r
1471 if (service->throttle_delay > delay) {
\r
1472 if (WaitForSingleObject(service->process_handle, service->throttle_delay - delay) == WAIT_TIMEOUT) service->throttle = 0;
\r
1474 else service->throttle = 0;
\r
1477 /* Ensure the restart delay is always applied. */
\r
1478 if (service->restart_delay && ! service->throttle) service->throttle++;
\r
1483 /* Stop the service */
\r
1484 int stop_service(nssm_service_t *service, unsigned long exitcode, bool graceful, bool default_action) {
\r
1485 service->allow_restart = false;
\r
1486 if (service->wait_handle) {
\r
1487 UnregisterWait(service->wait_handle);
\r
1488 service->wait_handle = 0;
\r
1491 service->rotate_stdout_online = service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;
\r
1493 if (default_action && ! exitcode && ! graceful) {
\r
1494 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
1498 /* Signal we are stopping */
\r
1500 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
1501 service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;
\r
1502 SetServiceStatus(service->status_handle, &service->status);
\r
1505 /* Nothing to do if service isn't running */
\r
1506 if (service->pid) {
\r
1507 /* Shut down service */
\r
1508 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_TERMINATEPROCESS, service->name, service->exe, 0);
\r
1509 kill_process(service, service->process_handle, service->pid, 0);
\r
1511 else log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_PROCESS_ALREADY_STOPPED, service->name, service->exe, 0);
\r
1513 end_service((void *) service, true);
\r
1515 /* Signal we stopped */
\r
1517 service->status.dwCurrentState = SERVICE_STOPPED;
\r
1519 service->status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
\r
1520 service->status.dwServiceSpecificExitCode = exitcode;
\r
1523 service->status.dwWin32ExitCode = NO_ERROR;
\r
1524 service->status.dwServiceSpecificExitCode = 0;
\r
1526 SetServiceStatus(service->status_handle, &service->status);
\r
1532 /* Callback function triggered when the server exits */
\r
1533 void CALLBACK end_service(void *arg, unsigned char why) {
\r
1534 nssm_service_t *service = (nssm_service_t *) arg;
\r
1536 if (service->stopping) return;
\r
1538 service->stopping = true;
\r
1540 service->rotate_stdout_online = service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;
\r
1542 /* Use now as a dummy exit time. */
\r
1543 GetSystemTimeAsFileTime(&service->exit_time);
\r
1545 /* Check exit code */
\r
1546 unsigned long exitcode = 0;
\r
1548 if (service->process_handle) {
\r
1549 GetExitCodeProcess(service->process_handle, &exitcode);
\r
1550 /* Check real exit time. */
\r
1551 if (exitcode != STILL_ACTIVE) get_process_exit_time(service->process_handle, &service->exit_time);
\r
1552 CloseHandle(service->process_handle);
\r
1555 service->process_handle = 0;
\r
1558 Log that the service ended BEFORE logging about killing the process
\r
1559 tree. See below for the possible values of the why argument.
\r
1562 _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), exitcode);
\r
1563 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ENDED_SERVICE, service->exe, service->name, code, 0);
\r
1567 if (exitcode == STILL_ACTIVE) exitcode = 0;
\r
1568 if (service->pid) kill_process_tree(service, service->pid, exitcode, service->pid);
\r
1572 The why argument is true if our wait timed out or false otherwise.
\r
1573 Our wait is infinite so why will never be true when called by the system.
\r
1574 If it is indeed true, assume we were called from stop_service() because
\r
1575 this is a controlled shutdown, and don't take any restart action.
\r
1578 if (! service->allow_restart) return;
\r
1580 /* What action should we take? */
\r
1581 int action = NSSM_EXIT_RESTART;
\r
1582 TCHAR action_string[ACTION_LEN];
\r
1583 bool default_action;
\r
1584 if (! get_exit_action(service->name, &exitcode, action_string, &default_action)) {
\r
1585 for (int i = 0; exit_action_strings[i]; i++) {
\r
1586 if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
1594 /* Try to restart the service or return failure code to service manager */
\r
1595 case NSSM_EXIT_RESTART:
\r
1596 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_RESTART, service->name, code, exit_action_strings[action], service->exe, 0);
\r
1597 while (monitor_service(service)) {
\r
1598 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_RESTART_SERVICE_FAILED, service->exe, service->name, 0);
\r
1603 /* Do nothing, just like srvany would */
\r
1604 case NSSM_EXIT_IGNORE:
\r
1605 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_IGNORE, service->name, code, exit_action_strings[action], service->exe, 0);
\r
1609 /* Tell the service manager we are finished */
\r
1610 case NSSM_EXIT_REALLY:
\r
1611 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_REALLY, service->name, code, exit_action_strings[action], 0);
\r
1612 stop_service(service, exitcode, true, default_action);
\r
1615 /* Fake a crash so pre-Vista service managers will run recovery actions. */
\r
1616 case NSSM_EXIT_UNCLEAN:
\r
1617 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_UNCLEAN, service->name, code, exit_action_strings[action], 0);
\r
1618 stop_service(service, exitcode, false, default_action);
\r
1625 void throttle_restart(nssm_service_t *service) {
\r
1626 /* This can't be a restart if the service is already running. */
\r
1627 if (! service->throttle++) return;
\r
1630 unsigned long throttle_ms = throttle_milliseconds(service->throttle);
\r
1631 TCHAR threshold[8], milliseconds[8];
\r
1633 if (service->restart_delay > throttle_ms) ms = service->restart_delay;
\r
1634 else ms = throttle_ms;
\r
1636 if (service->throttle > 7) service->throttle = 8;
\r
1638 _sntprintf_s(milliseconds, _countof(milliseconds), _TRUNCATE, _T("%lu"), ms);
\r
1640 if (service->throttle == 1 && service->restart_delay > throttle_ms) log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESTART_DELAY, service->name, milliseconds, 0);
\r
1642 _sntprintf_s(threshold, _countof(threshold), _TRUNCATE, _T("%lu"), service->throttle_delay);
\r
1643 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_THROTTLED, service->name, threshold, milliseconds, 0);
\r
1646 if (use_critical_section) EnterCriticalSection(&service->throttle_section);
\r
1647 else if (service->throttle_timer) {
\r
1648 ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));
\r
1649 service->throttle_duetime.QuadPart = 0 - (ms * 10000LL);
\r
1650 SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);
\r
1653 service->status.dwCurrentState = SERVICE_PAUSED;
\r
1654 SetServiceStatus(service->status_handle, &service->status);
\r
1656 if (use_critical_section) {
\r
1657 imports.SleepConditionVariableCS(&service->throttle_condition, &service->throttle_section, ms);
\r
1658 LeaveCriticalSection(&service->throttle_section);
\r
1661 if (service->throttle_timer) WaitForSingleObject(service->throttle_timer, INFINITE);
\r
1667 When responding to a stop (or any other) request we need to set dwWaitHint to
\r
1668 the number of milliseconds we expect the operation to take, and optionally
\r
1669 increase dwCheckPoint. If dwWaitHint milliseconds elapses without the
\r
1670 operation completing or dwCheckPoint increasing, the system will consider the
\r
1671 service to be hung.
\r
1673 However the system will consider the service to be hung after 30000
\r
1674 milliseconds regardless of the value of dwWaitHint if dwCheckPoint has not
\r
1675 changed. Therefore if we want to wait longer than that we must periodically
\r
1676 increase dwCheckPoint.
\r
1678 Furthermore, it will consider the service to be hung after 60000 milliseconds
\r
1679 regardless of the value of dwCheckPoint unless dwWaitHint is increased every
\r
1680 time dwCheckPoint is also increased.
\r
1682 Our strategy then is to retrieve the initial dwWaitHint and wait for
\r
1683 NSSM_SERVICE_STATUS_DEADLINE milliseconds. If the process is still running
\r
1684 and we haven't finished waiting we increment dwCheckPoint and add whichever is
\r
1685 smaller of NSSM_SERVICE_STATUS_DEADLINE or the remaining timeout to
\r
1688 Only doing both these things will prevent the system from killing the service.
\r
1690 Returns: 1 if the wait timed out.
\r
1691 0 if the wait completed.
\r
1694 int await_shutdown(nssm_service_t *service, TCHAR *function_name, unsigned long timeout) {
\r
1695 unsigned long interval;
\r
1696 unsigned long waithint;
\r
1697 unsigned long ret;
\r
1698 unsigned long waited;
\r
1699 TCHAR interval_milliseconds[16];
\r
1700 TCHAR timeout_milliseconds[16];
\r
1701 TCHAR waited_milliseconds[16];
\r
1702 TCHAR *function = function_name;
\r
1704 /* Add brackets to function name. */
\r
1705 size_t funclen = _tcslen(function_name) + 3;
\r
1706 TCHAR *func = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, funclen * sizeof(TCHAR));
\r
1708 if (_sntprintf_s(func, funclen, _TRUNCATE, _T("%s()"), function_name) > -1) function = func;
\r
1711 _sntprintf_s(timeout_milliseconds, _countof(timeout_milliseconds), _TRUNCATE, _T("%lu"), timeout);
\r
1713 waithint = service->status.dwWaitHint;
\r
1715 while (waited < timeout) {
\r
1716 interval = timeout - waited;
\r
1717 if (interval > NSSM_SERVICE_STATUS_DEADLINE) interval = NSSM_SERVICE_STATUS_DEADLINE;
\r
1719 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
1720 service->status.dwWaitHint += interval;
\r
1721 service->status.dwCheckPoint++;
\r
1722 SetServiceStatus(service->status_handle, &service->status);
\r
1725 _sntprintf_s(waited_milliseconds, _countof(waited_milliseconds), _TRUNCATE, _T("%lu"), waited);
\r
1726 _sntprintf_s(interval_milliseconds, _countof(interval_milliseconds), _TRUNCATE, _T("%lu"), interval);
\r
1727 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_AWAITING_SHUTDOWN, function, service->name, waited_milliseconds, interval_milliseconds, timeout_milliseconds, 0);
\r
1730 switch (WaitForSingleObject(service->process_handle, interval)) {
\r
1731 case WAIT_OBJECT_0:
\r
1735 case WAIT_TIMEOUT:
\r
1744 waited += interval;
\r
1748 if (func) HeapFree(GetProcessHeap(), 0, func);
\r