3 /* This is explicitly a wide string. */
\r
4 #define NSSM_LOGON_AS_SERVICE_RIGHT L"SeServiceLogonRight"
\r
7 bool use_critical_section;
\r
9 extern imports_t imports;
\r
10 extern settings_t settings[];
\r
12 const TCHAR *exit_action_strings[] = { _T("Restart"), _T("Ignore"), _T("Exit"), _T("Suicide"), 0 };
\r
13 const TCHAR *startup_strings[] = { _T("SERVICE_AUTO_START"), _T("SERVICE_DELAYED_AUTO_START"), _T("SERVICE_DEMAND_START"), _T("SERVICE_DISABLED"), 0 };
\r
14 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
22 Check the status in response to a control.
\r
23 Returns: 1 if the status is expected, eg STOP following CONTROL_STOP.
\r
24 0 if the status is desired, eg STOPPED following CONTROL_STOP.
\r
25 -1 if the status is undesired, eg STOPPED following CONTROL_START.
\r
27 static inline int service_control_response(unsigned long control, unsigned long status) {
\r
29 case NSSM_SERVICE_CONTROL_START:
\r
31 case SERVICE_START_PENDING:
\r
34 case SERVICE_RUNNING:
\r
41 case SERVICE_CONTROL_STOP:
\r
42 case SERVICE_CONTROL_SHUTDOWN:
\r
44 case SERVICE_STOP_PENDING:
\r
47 case SERVICE_STOPPED:
\r
54 case SERVICE_CONTROL_PAUSE:
\r
56 case SERVICE_PAUSE_PENDING:
\r
59 case SERVICE_PAUSED:
\r
66 case SERVICE_CONTROL_CONTINUE:
\r
68 case SERVICE_CONTINUE_PENDING:
\r
71 case SERVICE_RUNNING:
\r
78 case SERVICE_CONTROL_INTERROGATE:
\r
85 static inline int await_service_control_response(unsigned long control, SC_HANDLE service_handle, SERVICE_STATUS *service_status, unsigned long initial_status) {
\r
87 while (QueryServiceStatus(service_handle, service_status)) {
\r
88 int response = service_control_response(control, service_status->dwCurrentState);
\r
89 /* Alas we can't WaitForSingleObject() on an SC_HANDLE. */
\r
90 if (! response) return response;
\r
91 if (response > 0 || service_status->dwCurrentState == initial_status) {
\r
92 if (++tries > 10) return response;
\r
95 else return response;
\r
100 int affinity_mask_to_string(__int64 mask, TCHAR **string) {
\r
101 if (! string) return 1;
\r
109 /* SetProcessAffinityMask() accepts a mask of up to 64 processors. */
\r
111 for (n = 0; n < _countof(set); n++) set[n].first = set[n].last = -1;
\r
113 for (i = 0, n = 0; i < _countof(set); i++) {
\r
114 if (mask & (1LL << i)) {
\r
115 if (set[n].first == -1) set[n].first = set[n].last = (int) i;
\r
116 else if (set[n].last == (int) i - 1) set[n].last = (int) i;
\r
119 set[n].first = set[n].last = (int) i;
\r
124 /* Worst case is 2x2 characters for first and last CPU plus - and/or , */
\r
125 size_t len = (size_t) (n + 1) * 6;
\r
126 *string = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len * sizeof(TCHAR));
\r
127 if (! string) return 2;
\r
131 for (i = 0; i <= n; i++) {
\r
132 if (i) (*string)[s++] = _T(',');
\r
133 ret = _sntprintf_s(*string + s, 3, _TRUNCATE, _T("%u"), set[i].first);
\r
135 HeapFree(GetProcessHeap(), 0, *string);
\r
140 if (set[i].last != set[i].first) {
\r
141 ret =_sntprintf_s(*string + s, 4, _TRUNCATE, _T("%c%u"), (set[i].last == set[i].first + 1) ? _T(',') : _T('-'), set[i].last);
\r
143 HeapFree(GetProcessHeap(), 0, *string);
\r
154 int affinity_string_to_mask(TCHAR *string, __int64 *mask) {
\r
155 if (! mask) return 1;
\r
158 if (! string) return 0;
\r
167 unsigned long number;
\r
169 for (n = 0; n < _countof(set); n++) set[n].first = set[n].last = -1;
\r
173 ret = str_number(s, &number, &end);
\r
175 if (ret == 0 || ret == 2) {
\r
176 if (number >= _countof(set)) return 2;
\r
177 set[n].first = set[n].last = (int) number;
\r
189 if (! *(++s)) return 3;
\r
190 ret = str_number(s, &number, &end);
\r
191 if (ret == 0 || ret == 2) {
\r
193 if (! *s || *s == _T(',')) {
\r
194 set[n].last = (int) number;
\r
211 for (i = 0; i <= n; i++) {
\r
212 for (int j = set[i].first; j <= set[i].last; j++) (__int64) *mask |= (1LL << (__int64) j);
\r
218 inline unsigned long priority_mask() {
\r
219 return REALTIME_PRIORITY_CLASS | HIGH_PRIORITY_CLASS | ABOVE_NORMAL_PRIORITY_CLASS | NORMAL_PRIORITY_CLASS | BELOW_NORMAL_PRIORITY_CLASS | IDLE_PRIORITY_CLASS;
\r
222 int priority_constant_to_index(unsigned long constant) {
\r
223 switch (constant & priority_mask()) {
\r
224 case REALTIME_PRIORITY_CLASS: return NSSM_REALTIME_PRIORITY;
\r
225 case HIGH_PRIORITY_CLASS: return NSSM_HIGH_PRIORITY;
\r
226 case ABOVE_NORMAL_PRIORITY_CLASS: return NSSM_ABOVE_NORMAL_PRIORITY;
\r
227 case BELOW_NORMAL_PRIORITY_CLASS: return NSSM_BELOW_NORMAL_PRIORITY;
\r
228 case IDLE_PRIORITY_CLASS: return NSSM_IDLE_PRIORITY;
\r
230 return NSSM_NORMAL_PRIORITY;
\r
233 unsigned long priority_index_to_constant(int index) {
\r
235 case NSSM_REALTIME_PRIORITY: return REALTIME_PRIORITY_CLASS;
\r
236 case NSSM_HIGH_PRIORITY: return HIGH_PRIORITY_CLASS;
\r
237 case NSSM_ABOVE_NORMAL_PRIORITY: return ABOVE_NORMAL_PRIORITY_CLASS;
\r
238 case NSSM_BELOW_NORMAL_PRIORITY: return BELOW_NORMAL_PRIORITY_CLASS;
\r
239 case NSSM_IDLE_PRIORITY: return IDLE_PRIORITY_CLASS;
\r
241 return NORMAL_PRIORITY_CLASS;
\r
244 static inline unsigned long throttle_milliseconds(unsigned long throttle) {
\r
245 /* pow() operates on doubles. */
\r
246 unsigned long ret = 1; for (unsigned long i = 1; i < throttle; i++) ret *= 2;
\r
251 Wrapper to be called in a new thread so that we can acknowledge a STOP
\r
252 control immediately.
\r
254 static unsigned long WINAPI shutdown_service(void *arg) {
\r
255 return stop_service((nssm_service_t *) arg, 0, true, true);
\r
258 /* Connect to the service manager */
\r
259 SC_HANDLE open_service_manager() {
\r
260 SC_HANDLE ret = OpenSCManager(0, SERVICES_ACTIVE_DATABASE, SC_MANAGER_ALL_ACCESS);
\r
262 if (is_admin) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENSCMANAGER_FAILED, 0);
\r
269 /* Open a service by name or display name. */
\r
270 SC_HANDLE open_service(SC_HANDLE services, TCHAR *service_name, TCHAR *canonical_name, unsigned long canonical_namelen) {
\r
271 SC_HANDLE service_handle = OpenService(services, service_name, SERVICE_ALL_ACCESS);
\r
272 if (service_handle) {
\r
273 if (canonical_name && canonical_name != service_name) {
\r
274 if (_sntprintf_s(canonical_name, canonical_namelen, _TRUNCATE, _T("%s"), service_name) < 0) {
\r
275 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("canonical_name"), _T("open_service()"));
\r
279 return service_handle;
\r
282 unsigned long error = GetLastError();
\r
283 if (error != ERROR_SERVICE_DOES_NOT_EXIST) {
\r
284 print_message(stderr, NSSM_MESSAGE_OPENSERVICE_FAILED, error_string(GetLastError()));
\r
288 /* We can't look for a display name because there's no buffer to store it. */
\r
289 if (! canonical_name) {
\r
290 print_message(stderr, NSSM_MESSAGE_OPENSERVICE_FAILED, error_string(GetLastError()));
\r
294 unsigned long bufsize, required, count, i;
\r
295 unsigned long resume = 0;
\r
296 EnumServicesStatus(services, SERVICE_DRIVER | SERVICE_FILE_SYSTEM_DRIVER | SERVICE_KERNEL_DRIVER | SERVICE_WIN32, SERVICE_STATE_ALL, 0, 0, &required, &count, &resume);
\r
297 error = GetLastError();
\r
298 if (error != ERROR_MORE_DATA) {
\r
299 print_message(stderr, NSSM_MESSAGE_ENUMSERVICESSTATUS_FAILED, error_string(GetLastError()));
\r
303 ENUM_SERVICE_STATUS *status = (ENUM_SERVICE_STATUS *) HeapAlloc(GetProcessHeap(), 0, required);
\r
305 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("ENUM_SERVICE_STATUS"), _T("open_service()"));
\r
309 bufsize = required;
\r
312 EnumServicesStatus() returns:
\r
313 1 when it retrieved data and there's no more data to come.
\r
314 0 and sets last error to ERROR_MORE_DATA when it retrieved data and
\r
315 there's more data to come.
\r
316 0 and sets last error to something else on error.
\r
318 int ret = EnumServicesStatus(services, SERVICE_DRIVER | SERVICE_FILE_SYSTEM_DRIVER | SERVICE_KERNEL_DRIVER | SERVICE_WIN32, SERVICE_STATE_ALL, status, bufsize, &required, &count, &resume);
\r
320 error = GetLastError();
\r
321 if (error != ERROR_MORE_DATA) {
\r
322 HeapFree(GetProcessHeap(), 0, status);
\r
323 print_message(stderr, NSSM_MESSAGE_ENUMSERVICESSTATUS_FAILED, error_string(GetLastError()));
\r
328 for (i = 0; i < count; i++) {
\r
329 if (str_equiv(status[i].lpDisplayName, service_name)) {
\r
330 if (_sntprintf_s(canonical_name, canonical_namelen, _TRUNCATE, _T("%s"), status[i].lpServiceName) < 0) {
\r
331 HeapFree(GetProcessHeap(), 0, status);
\r
332 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("canonical_name"), _T("open_service()"));
\r
336 HeapFree(GetProcessHeap(), 0, status);
\r
337 return open_service(services, canonical_name, 0, 0);
\r
344 /* Recurse so we can get an error message. */
\r
345 return open_service(services, service_name, 0, 0);
\r
348 QUERY_SERVICE_CONFIG *query_service_config(const TCHAR *service_name, SC_HANDLE service_handle) {
\r
349 QUERY_SERVICE_CONFIG *qsc;
\r
350 unsigned long bufsize;
\r
351 unsigned long error;
\r
353 QueryServiceConfig(service_handle, 0, 0, &bufsize);
\r
354 error = GetLastError();
\r
355 if (error == ERROR_INSUFFICIENT_BUFFER) {
\r
356 qsc = (QUERY_SERVICE_CONFIG *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, bufsize);
\r
358 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("QUERY_SERVICE_CONFIG"), _T("query_service_config()"), 0);
\r
363 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG_FAILED, service_name, error_string(error), 0);
\r
367 if (! QueryServiceConfig(service_handle, qsc, bufsize, &bufsize)) {
\r
368 HeapFree(GetProcessHeap(), 0, qsc);
\r
369 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG_FAILED, service_name, error_string(GetLastError()), 0);
\r
376 int set_service_description(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR *buffer) {
\r
377 SERVICE_DESCRIPTION description;
\r
378 ZeroMemory(&description, sizeof(description));
\r
380 lpDescription must be NULL if we aren't changing, the new description
\r
383 if (buffer && buffer[0]) description.lpDescription = buffer;
\r
384 else description.lpDescription = _T("");
\r
386 if (ChangeServiceConfig2(service_handle, SERVICE_CONFIG_DESCRIPTION, &description)) return 0;
\r
388 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_DESCRIPTION_FAILED, service_name, error_string(GetLastError()), 0);
\r
392 int get_service_description(const TCHAR *service_name, SC_HANDLE service_handle, unsigned long len, TCHAR *buffer) {
\r
393 if (! buffer) return 1;
\r
395 unsigned long bufsize;
\r
396 QueryServiceConfig2(service_handle, SERVICE_CONFIG_DESCRIPTION, 0, 0, &bufsize);
\r
397 unsigned long error = GetLastError();
\r
398 if (error == ERROR_INSUFFICIENT_BUFFER) {
\r
399 SERVICE_DESCRIPTION *description = (SERVICE_DESCRIPTION *) HeapAlloc(GetProcessHeap(), 0, bufsize);
\r
400 if (! description) {
\r
401 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("SERVICE_CONFIG_DESCRIPTION"), _T("get_service_description()"));
\r
405 if (QueryServiceConfig2(service_handle, SERVICE_CONFIG_DESCRIPTION, (unsigned char *) description, bufsize, &bufsize)) {
\r
406 if (description->lpDescription) _sntprintf_s(buffer, len, _TRUNCATE, _T("%s"), description->lpDescription);
\r
407 else ZeroMemory(buffer, len * sizeof(TCHAR));
\r
408 HeapFree(GetProcessHeap(), 0, description);
\r
412 HeapFree(GetProcessHeap(), 0, description);
\r
413 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_CONFIG_DESCRIPTION"), error_string(error));
\r
418 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_CONFIG_DESCRIPTION"), error_string(error));
\r
425 int get_service_startup(const TCHAR *service_name, SC_HANDLE service_handle, const QUERY_SERVICE_CONFIG *qsc, unsigned long *startup) {
\r
426 if (! qsc) return 1;
\r
428 switch (qsc->dwStartType) {
\r
429 case SERVICE_DEMAND_START: *startup = NSSM_STARTUP_MANUAL; break;
\r
430 case SERVICE_DISABLED: *startup = NSSM_STARTUP_DISABLED; break;
\r
431 default: *startup = NSSM_STARTUP_AUTOMATIC;
\r
434 if (*startup != NSSM_STARTUP_AUTOMATIC) return 0;
\r
436 /* Check for delayed start. */
\r
437 unsigned long bufsize;
\r
438 unsigned long error;
\r
439 QueryServiceConfig2(service_handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, 0, 0, &bufsize);
\r
440 error = GetLastError();
\r
441 if (error == ERROR_INSUFFICIENT_BUFFER) {
\r
442 SERVICE_DELAYED_AUTO_START_INFO *info = (SERVICE_DELAYED_AUTO_START_INFO *) HeapAlloc(GetProcessHeap(), 0, bufsize);
\r
444 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("SERVICE_DELAYED_AUTO_START_INFO"), _T("get_service_startup()"));
\r
448 if (QueryServiceConfig2(service_handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, (unsigned char *) info, bufsize, &bufsize)) {
\r
449 if (info->fDelayedAutostart) *startup = NSSM_STARTUP_DELAYED;
\r
450 HeapFree(GetProcessHeap(), 0, info);
\r
454 error = GetLastError();
\r
455 if (error != ERROR_INVALID_LEVEL) {
\r
456 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_CONFIG_DELAYED_AUTO_START_INFO"), error_string(error));
\r
461 else if (error != ERROR_INVALID_LEVEL) {
\r
462 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_DELAYED_AUTO_START_INFO"), error_string(error));
\r
469 int get_service_username(const TCHAR *service_name, const QUERY_SERVICE_CONFIG *qsc, TCHAR **username, size_t *usernamelen) {
\r
470 if (! username) return 1;
\r
471 if (! usernamelen) return 1;
\r
476 if (! qsc) return 1;
\r
478 if (str_equiv(qsc->lpServiceStartName, NSSM_LOCALSYSTEM_ACCOUNT)) return 0;
\r
480 size_t len = _tcslen(qsc->lpServiceStartName);
\r
481 *username = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(TCHAR));
\r
483 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("username"), _T("get_service_username()"));
\r
487 memmove(*username, qsc->lpServiceStartName, (len + 1) * sizeof(TCHAR));
\r
488 *usernamelen = len;
\r
493 int grant_logon_as_service(const TCHAR *username) {
\r
494 if (! username) return 0;
\r
495 if (str_equiv(username, NSSM_LOCALSYSTEM_ACCOUNT)) return 0;
\r
497 /* Open Policy object. */
\r
498 LSA_OBJECT_ATTRIBUTES attributes;
\r
499 ZeroMemory(&attributes, sizeof(attributes));
\r
503 NTSTATUS status = LsaOpenPolicy(0, &attributes, POLICY_ALL_ACCESS, &policy);
\r
505 print_message(stderr, NSSM_MESSAGE_LSAOPENPOLICY_FAILED, error_string(LsaNtStatusToWinError(status)));
\r
509 /* Look up SID for the account. */
\r
510 LSA_UNICODE_STRING lsa_username;
\r
512 lsa_username.Buffer = (wchar_t *) username;
\r
513 lsa_username.Length = (unsigned short) _tcslen(username) * sizeof(TCHAR);
\r
514 lsa_username.MaximumLength = lsa_username.Length + sizeof(TCHAR);
\r
517 mbstowcs_s(&buflen, NULL, 0, username, _TRUNCATE);
\r
518 lsa_username.MaximumLength = (unsigned short) buflen * sizeof(wchar_t);
\r
519 lsa_username.Length = lsa_username.MaximumLength - sizeof(wchar_t);
\r
520 lsa_username.Buffer = (wchar_t *) HeapAlloc(GetProcessHeap(), 0, lsa_username.MaximumLength);
\r
521 if (lsa_username.Buffer) mbstowcs_s(&buflen, lsa_username.Buffer, lsa_username.MaximumLength, username, _TRUNCATE);
\r
524 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("LSA_UNICODE_STRING"), _T("grant_logon_as_service()"));
\r
529 LSA_REFERENCED_DOMAIN_LIST *translated_domains;
\r
530 LSA_TRANSLATED_SID *translated_sid;
\r
531 status = LsaLookupNames(policy, 1, &lsa_username, &translated_domains, &translated_sid);
\r
533 HeapFree(GetProcessHeap(), 0, lsa_username.Buffer);
\r
536 LsaFreeMemory(translated_domains);
\r
537 LsaFreeMemory(translated_sid);
\r
539 print_message(stderr, NSSM_MESSAGE_LSALOOKUPNAMES_FAILED, username, error_string(LsaNtStatusToWinError(status)));
\r
543 if (translated_sid->Use != SidTypeUser) {
\r
544 LsaFreeMemory(translated_domains);
\r
545 LsaFreeMemory(translated_sid);
\r
547 print_message(stderr, NSSM_GUI_INVALID_USERNAME, username);
\r
551 LSA_TRUST_INFORMATION *trust = &translated_domains->Domains[translated_sid->DomainIndex];
\r
552 if (! trust || ! IsValidSid(trust->Sid)) {
\r
553 LsaFreeMemory(translated_domains);
\r
554 LsaFreeMemory(translated_sid);
\r
556 print_message(stderr, NSSM_GUI_INVALID_USERNAME, username);
\r
560 /* GetSidSubAuthority*() return pointers! */
\r
561 unsigned char *n = GetSidSubAuthorityCount(trust->Sid);
\r
563 /* Convert translated SID to SID. */
\r
564 SID *sid = (SID *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, GetSidLengthRequired(*n + 1));
\r
566 LsaFreeMemory(translated_domains);
\r
567 LsaFreeMemory(translated_sid);
\r
569 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("SID"), _T("grant_logon_as_service"));
\r
573 unsigned long error;
\r
574 if (! InitializeSid(sid, GetSidIdentifierAuthority(trust->Sid), *n + 1)) {
\r
575 error = GetLastError();
\r
576 HeapFree(GetProcessHeap(), 0, sid);
\r
577 LsaFreeMemory(translated_domains);
\r
578 LsaFreeMemory(translated_sid);
\r
580 print_message(stderr, NSSM_MESSAGE_INITIALIZESID_FAILED, username, error_string(error));
\r
584 for (unsigned char i = 0; i <= *n; i++) {
\r
585 unsigned long *sub = GetSidSubAuthority(sid, i);
\r
586 if (i < *n) *sub = *GetSidSubAuthority(trust->Sid, i);
\r
587 else *sub = translated_sid->RelativeId;
\r
590 LsaFreeMemory(translated_domains);
\r
591 LsaFreeMemory(translated_sid);
\r
593 /* Check if the SID has the "Log on as a service" right. */
\r
594 LSA_UNICODE_STRING lsa_right;
\r
595 lsa_right.Buffer = NSSM_LOGON_AS_SERVICE_RIGHT;
\r
596 lsa_right.Length = (unsigned short) wcslen(lsa_right.Buffer) * sizeof(wchar_t);
\r
597 lsa_right.MaximumLength = lsa_right.Length + sizeof(wchar_t);
\r
599 LSA_UNICODE_STRING *rights;
\r
600 unsigned long count = ~0;
\r
601 status = LsaEnumerateAccountRights(policy, sid, &rights, &count);
\r
604 If the account has no rights set LsaEnumerateAccountRights() will return
\r
605 STATUS_OBJECT_NAME_NOT_FOUND and set count to 0.
\r
607 error = LsaNtStatusToWinError(status);
\r
608 if (error != ERROR_FILE_NOT_FOUND) {
\r
609 HeapFree(GetProcessHeap(), 0, sid);
\r
611 print_message(stderr, NSSM_MESSAGE_LSAENUMERATEACCOUNTRIGHTS_FAILED, username, error_string(error));
\r
616 for (unsigned long i = 0; i < count; i++) {
\r
617 if (rights[i].Length != lsa_right.Length) continue;
\r
618 if (_wcsnicmp(rights[i].Buffer, lsa_right.Buffer, lsa_right.MaximumLength)) continue;
\r
619 /* The SID has the right. */
\r
620 HeapFree(GetProcessHeap(), 0, sid);
\r
621 LsaFreeMemory(rights);
\r
625 LsaFreeMemory(rights);
\r
627 /* Add the right. */
\r
628 status = LsaAddAccountRights(policy, sid, &lsa_right, 1);
\r
629 HeapFree(GetProcessHeap(), 0, sid);
\r
632 print_message(stderr, NSSM_MESSAGE_LSAADDACCOUNTRIGHTS_FAILED, error_string(LsaNtStatusToWinError(status)));
\r
636 print_message(stdout, NSSM_MESSAGE_GRANTED_LOGON_AS_SERVICE, username);
\r
640 /* Set default values which aren't zero. */
\r
641 void set_nssm_service_defaults(nssm_service_t *service) {
\r
642 if (! service) return;
\r
644 service->type = SERVICE_WIN32_OWN_PROCESS;
\r
645 service->priority = NORMAL_PRIORITY_CLASS;
\r
646 service->stdin_sharing = NSSM_STDIN_SHARING;
\r
647 service->stdin_disposition = NSSM_STDIN_DISPOSITION;
\r
648 service->stdin_flags = NSSM_STDIN_FLAGS;
\r
649 service->stdout_sharing = NSSM_STDOUT_SHARING;
\r
650 service->stdout_disposition = NSSM_STDOUT_DISPOSITION;
\r
651 service->stdout_flags = NSSM_STDOUT_FLAGS;
\r
652 service->stderr_sharing = NSSM_STDERR_SHARING;
\r
653 service->stderr_disposition = NSSM_STDERR_DISPOSITION;
\r
654 service->stderr_flags = NSSM_STDERR_FLAGS;
\r
655 service->throttle_delay = NSSM_RESET_THROTTLE_RESTART;
\r
656 service->stop_method = ~0;
\r
657 service->kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;
\r
658 service->kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;
\r
659 service->kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;
\r
662 /* Allocate and zero memory for a service. */
\r
663 nssm_service_t *alloc_nssm_service() {
\r
664 nssm_service_t *service = (nssm_service_t *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(nssm_service_t));
\r
665 if (! service) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("service"), _T("alloc_nssm_service()"), 0);
\r
669 /* Free memory for a service. */
\r
670 void cleanup_nssm_service(nssm_service_t *service) {
\r
671 if (! service) return;
\r
672 if (service->username) HeapFree(GetProcessHeap(), 0, service->username);
\r
673 if (service->password) {
\r
674 SecureZeroMemory(service->password, service->passwordlen);
\r
675 HeapFree(GetProcessHeap(), 0, service->password);
\r
677 if (service->env) HeapFree(GetProcessHeap(), 0, service->env);
\r
678 if (service->env_extra) HeapFree(GetProcessHeap(), 0, service->env_extra);
\r
679 if (service->handle) CloseServiceHandle(service->handle);
\r
680 if (service->process_handle) CloseHandle(service->process_handle);
\r
681 if (service->wait_handle) UnregisterWait(service->process_handle);
\r
682 if (service->throttle_section_initialised) DeleteCriticalSection(&service->throttle_section);
\r
683 if (service->throttle_timer) CloseHandle(service->throttle_timer);
\r
684 if (service->initial_env) FreeEnvironmentStrings(service->initial_env);
\r
685 HeapFree(GetProcessHeap(), 0, service);
\r
688 /* About to install the service */
\r
689 int pre_install_service(int argc, TCHAR **argv) {
\r
690 nssm_service_t *service = alloc_nssm_service();
\r
691 set_nssm_service_defaults(service);
\r
692 if (argc) _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]);
\r
694 /* Show the dialogue box if we didn't give the service name and path */
\r
695 if (argc < 2) return nssm_gui(IDD_INSTALL, service);
\r
698 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("service"), _T("pre_install_service()"));
\r
701 _sntprintf_s(service->exe, _countof(service->exe), _TRUNCATE, _T("%s"), argv[1]);
\r
703 /* Arguments are optional */
\r
704 size_t flagslen = 0;
\r
707 for (i = 2; i < argc; i++) flagslen += _tcslen(argv[i]) + 1;
\r
708 if (! flagslen) flagslen = 1;
\r
709 if (flagslen > _countof(service->flags)) {
\r
710 print_message(stderr, NSSM_MESSAGE_FLAGS_TOO_LONG);
\r
714 for (i = 2; i < argc; i++) {
\r
715 size_t len = _tcslen(argv[i]);
\r
716 memmove(service->flags + s, argv[i], len * sizeof(TCHAR));
\r
718 if (i < argc - 1) service->flags[s++] = _T(' ');
\r
721 /* Work out directory name */
\r
722 _sntprintf_s(service->dir, _countof(service->dir), _TRUNCATE, _T("%s"), service->exe);
\r
723 strip_basename(service->dir);
\r
725 int ret = install_service(service);
\r
726 cleanup_nssm_service(service);
\r
730 /* About to edit the service. */
\r
731 int pre_edit_service(int argc, TCHAR **argv) {
\r
732 /* Require service name. */
\r
733 if (argc < 2) return usage(1);
\r
735 /* Are we editing on the command line? */
\r
736 enum { MODE_EDITING, MODE_GETTING, MODE_SETTING, MODE_RESETTING } mode = MODE_EDITING;
\r
737 const TCHAR *verb = argv[0];
\r
738 const TCHAR *service_name = argv[1];
\r
739 bool getting = false;
\r
740 bool unsetting = false;
\r
742 /* Minimum number of arguments. */
\r
744 /* Index of first value. */
\r
747 if (str_equiv(verb, _T("get"))) {
\r
749 mode = MODE_GETTING;
\r
751 else if (str_equiv(verb, _T("set"))) {
\r
753 mode = MODE_SETTING;
\r
755 else if (str_equiv(verb, _T("reset")) || str_equiv(verb, _T("unset"))) {
\r
757 mode = MODE_RESETTING;
\r
759 if (argc < mandatory) return usage(1);
\r
761 const TCHAR *parameter = 0;
\r
762 settings_t *setting = 0;
\r
765 /* Validate the parameter. */
\r
766 if (mandatory > 2) {
\r
767 bool additional_mandatory = false;
\r
769 parameter = argv[2];
\r
770 for (i = 0; settings[i].name; i++) {
\r
771 setting = &settings[i];
\r
772 if (! str_equiv(setting->name, parameter)) continue;
\r
773 if (((setting->additional & ADDITIONAL_GETTING) && mode == MODE_GETTING) || ((setting->additional & ADDITIONAL_SETTING) && mode == MODE_SETTING) || ((setting->additional & ADDITIONAL_RESETTING) && mode == MODE_RESETTING)) {
\r
774 additional_mandatory = true;
\r
779 if (! settings[i].name) {
\r
780 print_message(stderr, NSSM_MESSAGE_INVALID_PARAMETER, parameter);
\r
781 for (i = 0; settings[i].name; i++) _ftprintf(stderr, _T("%s\n"), settings[i].name);
\r
786 if (additional_mandatory) {
\r
787 if (argc < mandatory) {
\r
788 print_message(stderr, NSSM_MESSAGE_MISSING_SUBPARAMETER, parameter);
\r
791 additional = argv[3];
\r
794 else if (str_equiv(setting->name, NSSM_NATIVE_OBJECTNAME) && mode == MODE_SETTING) {
\r
795 additional = argv[3];
\r
799 additional = argv[remainder];
\r
800 if (argc < mandatory) return usage(1);
\r
804 nssm_service_t *service = alloc_nssm_service();
\r
805 _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), service_name);
\r
807 /* Open service manager */
\r
808 SC_HANDLE services = open_service_manager();
\r
810 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
814 /* Try to open the service */
\r
815 service->handle = open_service(services, service->name, service->name, _countof(service->name));
\r
816 if (! service->handle) {
\r
817 CloseServiceHandle(services);
\r
821 /* Get system details. */
\r
822 QUERY_SERVICE_CONFIG *qsc = query_service_config(service->name, service->handle);
\r
824 CloseHandle(service->handle);
\r
825 CloseServiceHandle(services);
\r
829 service->type = qsc->dwServiceType;
\r
830 if (! (service->type & SERVICE_WIN32_OWN_PROCESS)) {
\r
831 if (mode != MODE_GETTING) {
\r
832 HeapFree(GetProcessHeap(), 0, qsc);
\r
833 CloseHandle(service->handle);
\r
834 CloseServiceHandle(services);
\r
835 print_message(stderr, NSSM_MESSAGE_CANNOT_EDIT, service->name, NSSM_WIN32_OWN_PROCESS, 0);
\r
840 if (get_service_startup(service->name, service->handle, qsc, &service->startup)) {
\r
841 if (mode != MODE_GETTING) {
\r
842 HeapFree(GetProcessHeap(), 0, qsc);
\r
843 CloseHandle(service->handle);
\r
844 CloseServiceHandle(services);
\r
849 if (get_service_username(service->name, qsc, &service->username, &service->usernamelen)) {
\r
850 if (mode != MODE_GETTING) {
\r
851 HeapFree(GetProcessHeap(), 0, qsc);
\r
852 CloseHandle(service->handle);
\r
853 CloseServiceHandle(services);
\r
858 _sntprintf_s(service->displayname, _countof(service->displayname), _TRUNCATE, _T("%s"), qsc->lpDisplayName);
\r
860 /* Get the canonical service name. We open it case insensitively. */
\r
861 unsigned long bufsize = _countof(service->name);
\r
862 GetServiceKeyName(services, service->displayname, service->name, &bufsize);
\r
864 /* Remember the executable in case it isn't NSSM. */
\r
865 _sntprintf_s(service->image, _countof(service->image), _TRUNCATE, _T("%s"), qsc->lpBinaryPathName);
\r
866 HeapFree(GetProcessHeap(), 0, qsc);
\r
868 /* Get extended system details. */
\r
869 if (get_service_description(service->name, service->handle, _countof(service->description), service->description)) {
\r
870 if (mode != MODE_GETTING) {
\r
871 CloseHandle(service->handle);
\r
872 CloseServiceHandle(services);
\r
877 /* Get NSSM details. */
\r
878 get_parameters(service, 0);
\r
880 CloseServiceHandle(services);
\r
882 if (! service->exe[0]) {
\r
883 service->native = true;
\r
884 if (mode != MODE_GETTING) print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE, service->name, NSSM, service->image);
\r
887 /* Editing with the GUI. */
\r
888 if (mode == MODE_EDITING) {
\r
889 nssm_gui(IDD_EDIT, service);
\r
893 /* Trying to manage App* parameters for a non-NSSM service. */
\r
894 if (! setting->native && service->native) {
\r
895 CloseHandle(service->handle);
\r
896 print_message(stderr, NSSM_MESSAGE_NATIVE_PARAMETER, setting->name, NSSM);
\r
904 if (mode == MODE_GETTING) {
\r
905 if (! service->native) {
\r
906 key = open_registry(service->name, KEY_READ);
\r
907 if (! key) return 4;
\r
910 if (setting->native) ret = get_setting(service->name, service->handle, setting, &value, additional);
\r
911 else ret = get_setting(service->name, key, setting, &value, additional);
\r
913 CloseHandle(service->handle);
\r
917 switch (setting->type) {
\r
918 case REG_EXPAND_SZ:
\r
921 _tprintf(_T("%s\n"), value.string ? value.string : _T(""));
\r
922 HeapFree(GetProcessHeap(), 0, value.string);
\r
926 _tprintf(_T("%u\n"), value.numeric);
\r
930 if (! service->native) RegCloseKey(key);
\r
931 CloseHandle(service->handle);
\r
935 /* Build the value. */
\r
936 if (mode == MODE_RESETTING) {
\r
937 /* Unset the parameter. */
\r
940 else if (remainder == argc) {
\r
944 /* Set the parameter. */
\r
946 size_t delimiterlen = (setting->additional & ADDITIONAL_CRLF) ? 2 : 1;
\r
947 for (i = remainder; i < argc; i++) len += _tcslen(argv[i]) + delimiterlen;
\r
950 value.string = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, len * sizeof(TCHAR));
\r
951 if (! value.string) {
\r
952 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("value"), _T("edit_service()"));
\r
953 CloseHandle(service->handle);
\r
958 for (i = remainder; i < argc; i++) {
\r
959 size_t len = _tcslen(argv[i]);
\r
960 memmove(value.string + s, argv[i], len * sizeof(TCHAR));
\r
962 if (i < argc - 1) {
\r
963 if (setting->additional & ADDITIONAL_CRLF) {
\r
964 value.string[s++] = _T('\r');
\r
965 value.string[s++] = _T('\n');
\r
967 else value.string[s++] = _T(' ');
\r
970 value.string[s] = _T('\0');
\r
973 if (! service->native) {
\r
974 key = open_registry(service->name, KEY_WRITE);
\r
976 if (value.string) HeapFree(GetProcessHeap(), 0, value.string);
\r
981 if (setting->native) ret = set_setting(service->name, service->handle, setting, &value, additional);
\r
982 else ret = set_setting(service->name, key, setting, &value, additional);
\r
983 if (value.string) HeapFree(GetProcessHeap(), 0, value.string);
\r
985 if (! service->native) RegCloseKey(key);
\r
986 CloseHandle(service->handle);
\r
990 if (! service->native) RegCloseKey(key);
\r
991 CloseHandle(service->handle);
\r
996 /* About to remove the service */
\r
997 int pre_remove_service(int argc, TCHAR **argv) {
\r
998 nssm_service_t *service = alloc_nssm_service();
\r
999 set_nssm_service_defaults(service);
\r
1000 if (argc) _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]);
\r
1002 /* Show dialogue box if we didn't pass service name and "confirm" */
\r
1003 if (argc < 2) return nssm_gui(IDD_REMOVE, service);
\r
1004 if (str_equiv(argv[1], _T("confirm"))) {
\r
1005 int ret = remove_service(service);
\r
1006 cleanup_nssm_service(service);
\r
1009 print_message(stderr, NSSM_MESSAGE_PRE_REMOVE_SERVICE);
\r
1013 /* Install the service */
\r
1014 int install_service(nssm_service_t *service) {
\r
1015 if (! service) return 1;
\r
1017 /* Open service manager */
\r
1018 SC_HANDLE services = open_service_manager();
\r
1020 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
1021 cleanup_nssm_service(service);
\r
1025 /* Get path of this program */
\r
1026 GetModuleFileName(0, service->image, _countof(service->image));
\r
1028 /* Create the service - settings will be changed in edit_service() */
\r
1029 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
1030 if (! service->handle) {
\r
1031 print_message(stderr, NSSM_MESSAGE_CREATESERVICE_FAILED, error_string(GetLastError()));
\r
1032 CloseServiceHandle(services);
\r
1036 if (edit_service(service, false)) {
\r
1037 DeleteService(service->handle);
\r
1038 CloseServiceHandle(services);
\r
1042 print_message(stdout, NSSM_MESSAGE_SERVICE_INSTALLED, service->name);
\r
1045 CloseServiceHandle(services);
\r
1050 /* Edit the service. */
\r
1051 int edit_service(nssm_service_t *service, bool editing) {
\r
1052 if (! service) return 1;
\r
1055 The only two valid flags for service type are SERVICE_WIN32_OWN_PROCESS
\r
1056 and SERVICE_INTERACTIVE_PROCESS.
\r
1058 service->type &= SERVICE_INTERACTIVE_PROCESS;
\r
1059 service->type |= SERVICE_WIN32_OWN_PROCESS;
\r
1061 /* Startup type. */
\r
1062 unsigned long startup;
\r
1063 switch (service->startup) {
\r
1064 case NSSM_STARTUP_MANUAL: startup = SERVICE_DEMAND_START; break;
\r
1065 case NSSM_STARTUP_DISABLED: startup = SERVICE_DISABLED; break;
\r
1066 default: startup = SERVICE_AUTO_START;
\r
1069 /* Display name. */
\r
1070 if (! service->displayname[0]) _sntprintf_s(service->displayname, _countof(service->displayname), _TRUNCATE, _T("%s"), service->name);
\r
1073 Username must be NULL if we aren't changing or an account name.
\r
1074 We must explicitly use LOCALSYSTEM to change it when we are editing.
\r
1075 Password must be NULL if we aren't changing, a password or "".
\r
1076 Empty passwords are valid but we won't allow them in the GUI.
\r
1078 TCHAR *username = 0;
\r
1079 TCHAR *password = 0;
\r
1080 if (service->usernamelen) {
\r
1081 username = service->username;
\r
1082 if (service->passwordlen) password = service->password;
\r
1083 else password = _T("");
\r
1085 else if (editing) username = NSSM_LOCALSYSTEM_ACCOUNT;
\r
1087 if (grant_logon_as_service(username)) {
\r
1088 print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username);
\r
1092 if (! ChangeServiceConfig(service->handle, service->type, startup, SERVICE_NO_CHANGE, 0, 0, 0, 0, username, password, service->displayname)) {
\r
1093 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
1097 if (service->description[0] || editing) {
\r
1098 set_service_description(service->name, service->handle, service->description);
\r
1101 SERVICE_DELAYED_AUTO_START_INFO delayed;
\r
1102 ZeroMemory(&delayed, sizeof(delayed));
\r
1103 if (service->startup == NSSM_STARTUP_DELAYED) delayed.fDelayedAutostart = 1;
\r
1104 else delayed.fDelayedAutostart = 0;
\r
1105 /* Delayed startup isn't supported until Vista. */
\r
1106 if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, &delayed)) {
\r
1107 unsigned long error = GetLastError();
\r
1108 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
\r
1109 if (error != ERROR_INVALID_LEVEL) {
\r
1110 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_DELAYED_AUTO_START_INFO_FAILED, service->name, error_string(error), 0);
\r
1114 /* Don't mess with parameters which aren't ours. */
\r
1115 if (! service->native) {
\r
1116 /* Now we need to put the parameters into the registry */
\r
1117 if (create_parameters(service, editing)) {
\r
1118 print_message(stderr, NSSM_MESSAGE_CREATE_PARAMETERS_FAILED);
\r
1122 set_service_recovery(service);
\r
1128 /* Control a service. */
\r
1129 int control_service(unsigned long control, int argc, TCHAR **argv) {
\r
1130 if (argc < 1) return usage(1);
\r
1131 TCHAR *service_name = argv[0];
\r
1132 TCHAR canonical_name[SERVICE_NAME_LENGTH];
\r
1134 SC_HANDLE services = open_service_manager();
\r
1136 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
1140 SC_HANDLE service_handle = open_service(services, service_name, canonical_name, _countof(canonical_name));
\r
1141 if (! service_handle) {
\r
1142 CloseServiceHandle(services);
\r
1147 unsigned long error;
\r
1148 SERVICE_STATUS service_status;
\r
1149 if (control == NSSM_SERVICE_CONTROL_START) {
\r
1150 unsigned long initial_status = SERVICE_STOPPED;
\r
1151 ret = StartService(service_handle, (unsigned long) argc, (const TCHAR **) argv);
\r
1152 error = GetLastError();
\r
1153 CloseServiceHandle(services);
\r
1155 if (error == ERROR_IO_PENDING) {
\r
1157 Older versions of Windows return immediately with ERROR_IO_PENDING
\r
1158 indicate that the operation is still in progress. Newer versions
\r
1159 will return it if there really is a delay.
\r
1162 error = ERROR_SUCCESS;
\r
1166 int response = await_service_control_response(control, service_handle, &service_status, initial_status);
\r
1167 CloseHandle(service_handle);
\r
1170 print_message(stderr, NSSM_MESSAGE_BAD_CONTROL_RESPONSE, canonical_name, service_status_text(service_status.dwCurrentState), service_control_text(control));
\r
1173 else _tprintf(_T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1177 CloseHandle(service_handle);
\r
1178 _ftprintf(stderr, _T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1182 else if (control == SERVICE_CONTROL_INTERROGATE) {
\r
1184 We could actually send an INTERROGATE control but that won't return
\r
1185 any information if the service is stopped and we don't care about
\r
1186 the extra details it might give us in any case. So we'll fake it.
\r
1188 ret = QueryServiceStatus(service_handle, &service_status);
\r
1189 error = GetLastError();
\r
1192 _tprintf(_T("%s\n"), service_status_text(service_status.dwCurrentState));
\r
1196 _ftprintf(stderr, _T("%s: %s\n"), canonical_name, error_string(error));
\r
1201 ret = ControlService(service_handle, control, &service_status);
\r
1202 unsigned long initial_status = service_status.dwCurrentState;
\r
1203 error = GetLastError();
\r
1204 CloseServiceHandle(services);
\r
1206 if (error == ERROR_IO_PENDING) {
\r
1208 error = ERROR_SUCCESS;
\r
1212 int response = await_service_control_response(control, service_handle, &service_status, initial_status);
\r
1213 CloseHandle(service_handle);
\r
1216 print_message(stderr, NSSM_MESSAGE_BAD_CONTROL_RESPONSE, canonical_name, service_status_text(service_status.dwCurrentState), service_control_text(control));
\r
1219 else _tprintf(_T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1223 CloseHandle(service_handle);
\r
1224 _ftprintf(stderr, _T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1225 if (error == ERROR_SERVICE_NOT_ACTIVE) {
\r
1226 if (control == SERVICE_CONTROL_SHUTDOWN || control == SERVICE_CONTROL_STOP) return 0;
\r
1233 /* Remove the service */
\r
1234 int remove_service(nssm_service_t *service) {
\r
1235 if (! service) return 1;
\r
1237 /* Open service manager */
\r
1238 SC_HANDLE services = open_service_manager();
\r
1240 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
1244 /* Try to open the service */
\r
1245 service->handle = open_service(services, service->name, service->name, _countof(service->name));
\r
1246 if (! service->handle) {
\r
1247 CloseServiceHandle(services);
\r
1251 /* Get the canonical service name. We open it case insensitively. */
\r
1252 unsigned long bufsize = _countof(service->displayname);
\r
1253 GetServiceDisplayName(services, service->name, service->displayname, &bufsize);
\r
1254 bufsize = _countof(service->name);
\r
1255 GetServiceKeyName(services, service->displayname, service->name, &bufsize);
\r
1257 /* Try to delete the service */
\r
1258 if (! DeleteService(service->handle)) {
\r
1259 print_message(stderr, NSSM_MESSAGE_DELETESERVICE_FAILED);
\r
1260 CloseServiceHandle(services);
\r
1265 CloseServiceHandle(services);
\r
1267 print_message(stdout, NSSM_MESSAGE_SERVICE_REMOVED, service->name);
\r
1271 /* Service initialisation */
\r
1272 void WINAPI service_main(unsigned long argc, TCHAR **argv) {
\r
1273 nssm_service_t *service = alloc_nssm_service();
\r
1274 if (! service) return;
\r
1276 if (_sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]) < 0) {
\r
1277 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("service->name"), _T("service_main()"), 0);
\r
1281 /* We can use a condition variable in a critical section on Vista or later. */
\r
1282 if (imports.SleepConditionVariableCS && imports.WakeConditionVariable) use_critical_section = true;
\r
1283 else use_critical_section = false;
\r
1285 /* Initialise status */
\r
1286 ZeroMemory(&service->status, sizeof(service->status));
\r
1287 service->status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;
\r
1288 service->status.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE;
\r
1289 service->status.dwWin32ExitCode = NO_ERROR;
\r
1290 service->status.dwServiceSpecificExitCode = 0;
\r
1291 service->status.dwCheckPoint = 0;
\r
1292 service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;
\r
1294 /* Signal we AREN'T running the server */
\r
1295 service->process_handle = 0;
\r
1298 /* Register control handler */
\r
1299 service->status_handle = RegisterServiceCtrlHandlerEx(NSSM, service_control_handler, (void *) service);
\r
1300 if (! service->status_handle) {
\r
1301 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_REGISTERSERVICECTRLHANDER_FAILED, error_string(GetLastError()), 0);
\r
1305 log_service_control(service->name, 0, true);
\r
1307 service->status.dwCurrentState = SERVICE_START_PENDING;
\r
1308 service->status.dwWaitHint = service->throttle_delay + NSSM_WAITHINT_MARGIN;
\r
1309 SetServiceStatus(service->status_handle, &service->status);
\r
1312 /* Try to create the exit action parameters; we don't care if it fails */
\r
1313 create_exit_action(service->name, exit_action_strings[0], false);
\r
1315 SC_HANDLE services = open_service_manager();
\r
1317 service->handle = OpenService(services, service->name, SC_MANAGER_ALL_ACCESS);
\r
1318 set_service_recovery(service);
\r
1319 CloseServiceHandle(services);
\r
1323 /* Used for signalling a resume if the service pauses when throttled. */
\r
1324 if (use_critical_section) {
\r
1325 InitializeCriticalSection(&service->throttle_section);
\r
1326 service->throttle_section_initialised = true;
\r
1329 service->throttle_timer = CreateWaitableTimer(0, 1, 0);
\r
1330 if (! service->throttle_timer) {
\r
1331 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_CREATEWAITABLETIMER_FAILED, service->name, error_string(GetLastError()), 0);
\r
1335 /* Remember our initial environment. */
\r
1336 service->initial_env = GetEnvironmentStrings();
\r
1338 monitor_service(service);
\r
1341 /* Make sure service recovery actions are taken where necessary */
\r
1342 void set_service_recovery(nssm_service_t *service) {
\r
1343 SERVICE_FAILURE_ACTIONS_FLAG flag;
\r
1344 ZeroMemory(&flag, sizeof(flag));
\r
1345 flag.fFailureActionsOnNonCrashFailures = true;
\r
1347 /* This functionality was added in Vista so the call may fail */
\r
1348 if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, &flag)) {
\r
1349 unsigned long error = GetLastError();
\r
1350 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
\r
1351 if (error != ERROR_INVALID_LEVEL) {
\r
1352 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_FAILURE_ACTIONS_FAILED, service->name, error_string(error), 0);
\r
1357 int monitor_service(nssm_service_t *service) {
\r
1358 /* Set service status to started */
\r
1359 int ret = start_service(service);
\r
1362 _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%d"), ret);
\r
1363 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_START_SERVICE_FAILED, service->exe, service->name, ret, 0);
\r
1366 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_STARTED_SERVICE, service->exe, service->flags, service->name, service->dir, 0);
\r
1368 /* Monitor service */
\r
1369 if (! RegisterWaitForSingleObject(&service->wait_handle, service->process_handle, end_service, (void *) service, INFINITE, WT_EXECUTEONLYONCE | WT_EXECUTELONGFUNCTION)) {
\r
1370 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_REGISTERWAITFORSINGLEOBJECT_FAILED, service->name, service->exe, error_string(GetLastError()), 0);
\r
1376 TCHAR *service_control_text(unsigned long control) {
\r
1377 switch (control) {
\r
1378 /* HACK: there is no SERVICE_CONTROL_START constant */
\r
1379 case NSSM_SERVICE_CONTROL_START: return _T("START");
\r
1380 case SERVICE_CONTROL_STOP: return _T("STOP");
\r
1381 case SERVICE_CONTROL_SHUTDOWN: return _T("SHUTDOWN");
\r
1382 case SERVICE_CONTROL_PAUSE: return _T("PAUSE");
\r
1383 case SERVICE_CONTROL_CONTINUE: return _T("CONTINUE");
\r
1384 case SERVICE_CONTROL_INTERROGATE: return _T("INTERROGATE");
\r
1385 case NSSM_SERVICE_CONTROL_ROTATE: return _T("ROTATE");
\r
1386 default: return 0;
\r
1390 TCHAR *service_status_text(unsigned long status) {
\r
1392 case SERVICE_STOPPED: return _T("SERVICE_STOPPED");
\r
1393 case SERVICE_START_PENDING: return _T("SERVICE_START_PENDING");
\r
1394 case SERVICE_STOP_PENDING: return _T("SERVICE_STOP_PENDING");
\r
1395 case SERVICE_RUNNING: return _T("SERVICE_RUNNING");
\r
1396 case SERVICE_CONTINUE_PENDING: return _T("SERVICE_CONTINUE_PENDING");
\r
1397 case SERVICE_PAUSE_PENDING: return _T("SERVICE_PAUSE_PENDING");
\r
1398 case SERVICE_PAUSED: return _T("SERVICE_PAUSED");
\r
1399 default: return 0;
\r
1403 void log_service_control(TCHAR *service_name, unsigned long control, bool handled) {
\r
1404 TCHAR *text = service_control_text(control);
\r
1405 unsigned long event;
\r
1408 /* "0x" + 8 x hex + NULL */
\r
1409 text = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, 11 * sizeof(TCHAR));
\r
1411 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);
\r
1414 if (_sntprintf_s(text, 11, _TRUNCATE, _T("0x%08x"), control) < 0) {
\r
1415 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);
\r
1416 HeapFree(GetProcessHeap(), 0, text);
\r
1420 event = NSSM_EVENT_SERVICE_CONTROL_UNKNOWN;
\r
1422 else if (handled) event = NSSM_EVENT_SERVICE_CONTROL_HANDLED;
\r
1423 else event = NSSM_EVENT_SERVICE_CONTROL_NOT_HANDLED;
\r
1425 log_event(EVENTLOG_INFORMATION_TYPE, event, service_name, text, 0);
\r
1427 if (event == NSSM_EVENT_SERVICE_CONTROL_UNKNOWN) {
\r
1428 HeapFree(GetProcessHeap(), 0, text);
\r
1432 /* Service control handler */
\r
1433 unsigned long WINAPI service_control_handler(unsigned long control, unsigned long event, void *data, void *context) {
\r
1434 nssm_service_t *service = (nssm_service_t *) context;
\r
1436 switch (control) {
\r
1437 case SERVICE_CONTROL_INTERROGATE:
\r
1438 /* We always keep the service status up-to-date so this is a no-op. */
\r
1441 case SERVICE_CONTROL_SHUTDOWN:
\r
1442 case SERVICE_CONTROL_STOP:
\r
1443 log_service_control(service->name, control, true);
\r
1445 We MUST acknowledge the stop request promptly but we're committed to
\r
1446 waiting for the application to exit. Spawn a new thread to wait
\r
1447 while we acknowledge the request.
\r
1449 if (! CreateThread(NULL, 0, shutdown_service, context, 0, NULL)) {
\r
1450 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETHREAD_FAILED, error_string(GetLastError()), 0);
\r
1453 We couldn't create a thread to tidy up so we'll have to force the tidyup
\r
1454 to complete in time in this thread.
\r
1456 service->kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;
\r
1457 service->kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;
\r
1458 service->kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;
\r
1460 stop_service(service, 0, true, true);
\r
1464 case SERVICE_CONTROL_CONTINUE:
\r
1465 log_service_control(service->name, control, true);
\r
1466 service->throttle = 0;
\r
1467 if (use_critical_section) imports.WakeConditionVariable(&service->throttle_condition);
\r
1469 if (! service->throttle_timer) return ERROR_CALL_NOT_IMPLEMENTED;
\r
1470 ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));
\r
1471 SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);
\r
1473 /* We can't continue if the application is running! */
\r
1474 if (! service->process_handle) service->status.dwCurrentState = SERVICE_CONTINUE_PENDING;
\r
1475 service->status.dwWaitHint = throttle_milliseconds(service->throttle) + NSSM_WAITHINT_MARGIN;
\r
1476 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESET_THROTTLE, service->name, 0);
\r
1477 SetServiceStatus(service->status_handle, &service->status);
\r
1480 case SERVICE_CONTROL_PAUSE:
\r
1482 We don't accept pause messages but it isn't possible to register
\r
1483 only for continue messages so we have to handle this case.
\r
1485 log_service_control(service->name, control, false);
\r
1486 return ERROR_CALL_NOT_IMPLEMENTED;
\r
1488 case NSSM_SERVICE_CONTROL_ROTATE:
\r
1489 log_service_control(service->name, control, true);
\r
1490 if (service->rotate_stdout_online == NSSM_ROTATE_ONLINE) service->rotate_stdout_online = NSSM_ROTATE_ONLINE_ASAP;
\r
1491 if (service->rotate_stderr_online == NSSM_ROTATE_ONLINE) service->rotate_stderr_online = NSSM_ROTATE_ONLINE_ASAP;
\r
1495 /* Unknown control */
\r
1496 log_service_control(service->name, control, false);
\r
1497 return ERROR_CALL_NOT_IMPLEMENTED;
\r
1500 /* Start the service */
\r
1501 int start_service(nssm_service_t *service) {
\r
1502 service->stopping = false;
\r
1503 service->allow_restart = true;
\r
1505 if (service->process_handle) return 0;
\r
1507 /* Allocate a STARTUPINFO structure for a new process */
\r
1509 ZeroMemory(&si, sizeof(si));
\r
1510 si.cb = sizeof(si);
\r
1512 /* Allocate a PROCESSINFO structure for the process */
\r
1513 PROCESS_INFORMATION pi;
\r
1514 ZeroMemory(&pi, sizeof(pi));
\r
1516 /* Get startup parameters */
\r
1517 int ret = get_parameters(service, &si);
\r
1519 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_PARAMETERS_FAILED, service->name, 0);
\r
1520 return stop_service(service, 2, true, true);
\r
1523 /* Launch executable with arguments */
\r
1524 TCHAR cmd[CMD_LENGTH];
\r
1525 if (_sntprintf_s(cmd, _countof(cmd), _TRUNCATE, _T("\"%s\" %s"), service->exe, service->flags) < 0) {
\r
1526 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("command line"), _T("start_service"), 0);
\r
1527 return stop_service(service, 2, true, true);
\r
1530 throttle_restart(service);
\r
1532 /* Set the environment. */
\r
1533 if (service->env) duplicate_environment(service->env);
\r
1534 if (service->env_extra) set_environment_block(service->env_extra);
\r
1536 /* Set up I/O redirection. */
\r
1537 if (get_output_handles(service, &si)) {
\r
1538 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_OUTPUT_HANDLES_FAILED, service->name, 0);
\r
1539 if (! service->no_console) FreeConsole();
\r
1540 close_output_handles(&si);
\r
1541 return stop_service(service, 4, true, true);
\r
1544 bool inherit_handles = false;
\r
1545 if (si.dwFlags & STARTF_USESTDHANDLES) inherit_handles = true;
\r
1546 unsigned long flags = service->priority & priority_mask();
\r
1547 if (service->affinity) flags |= CREATE_SUSPENDED;
\r
1548 if (! CreateProcess(0, cmd, 0, 0, inherit_handles, flags, 0, service->dir, &si, &pi)) {
\r
1549 unsigned long exitcode = 3;
\r
1550 unsigned long error = GetLastError();
\r
1551 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service->name, service->exe, error_string(error), 0);
\r
1552 close_output_handles(&si);
\r
1553 duplicate_environment(service->initial_env);
\r
1554 return stop_service(service, exitcode, true, true);
\r
1556 service->process_handle = pi.hProcess;
\r
1557 service->pid = pi.dwProcessId;
\r
1559 if (get_process_creation_time(service->process_handle, &service->creation_time)) ZeroMemory(&service->creation_time, sizeof(service->creation_time));
\r
1561 close_output_handles(&si);
\r
1563 if (! service->no_console) FreeConsole();
\r
1565 /* Restore our environment. */
\r
1566 duplicate_environment(service->initial_env);
\r
1568 if (service->affinity) {
\r
1570 We are explicitly storing service->affinity as a 64-bit unsigned integer
\r
1571 so that we can parse it regardless of whether we're running in 32-bit
\r
1572 or 64-bit mode. The arguments to SetProcessAffinityMask(), however, are
\r
1573 defined as type DWORD_PTR and hence limited to 32 bits on a 32-bit system
\r
1574 (or when running the 32-bit NSSM).
\r
1576 The result is a lot of seemingly-unnecessary casting throughout the code
\r
1577 and potentially confusion when we actually try to start the service.
\r
1578 Having said that, however, it's unlikely that we're actually going to
\r
1579 run in 32-bit mode on a system which has more than 32 CPUs so the
\r
1580 likelihood of seeing a confusing situation is somewhat diminished.
\r
1582 DWORD_PTR affinity, system_affinity;
\r
1584 if (GetProcessAffinityMask(service->process_handle, &affinity, &system_affinity)) affinity = service->affinity & system_affinity;
\r
1586 affinity = (DWORD_PTR) service->affinity;
\r
1587 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GETPROCESSAFFINITYMASK_FAILED, service->name, error_string(GetLastError()), 0);
\r
1590 if (! SetProcessAffinityMask(service->process_handle, affinity)) {
\r
1591 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_SETPROCESSAFFINITYMASK_FAILED, service->name, error_string(GetLastError()), 0);
\r
1594 ResumeThread(pi.hThread);
\r
1598 Wait for a clean startup before changing the service status to RUNNING
\r
1599 but be mindful of the fact that we are blocking the service control manager
\r
1600 so abandon the wait before too much time has elapsed.
\r
1602 unsigned long delay = service->throttle_delay;
\r
1603 if (delay > NSSM_SERVICE_STATUS_DEADLINE) {
\r
1604 TCHAR delay_milliseconds[16];
\r
1605 _sntprintf_s(delay_milliseconds, _countof(delay_milliseconds), _TRUNCATE, _T("%lu"), delay);
\r
1606 TCHAR deadline_milliseconds[16];
\r
1607 _sntprintf_s(deadline_milliseconds, _countof(deadline_milliseconds), _TRUNCATE, _T("%lu"), NSSM_SERVICE_STATUS_DEADLINE);
\r
1608 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_STARTUP_DELAY_TOO_LONG, service->name, delay_milliseconds, NSSM, deadline_milliseconds, 0);
\r
1609 delay = NSSM_SERVICE_STATUS_DEADLINE;
\r
1611 unsigned long deadline = WaitForSingleObject(service->process_handle, delay);
\r
1613 /* Signal successful start */
\r
1614 service->status.dwCurrentState = SERVICE_RUNNING;
\r
1615 SetServiceStatus(service->status_handle, &service->status);
\r
1617 /* Continue waiting for a clean startup. */
\r
1618 if (deadline == WAIT_TIMEOUT) {
\r
1619 if (service->throttle_delay > delay) {
\r
1620 if (WaitForSingleObject(service->process_handle, service->throttle_delay - delay) == WAIT_TIMEOUT) service->throttle = 0;
\r
1622 else service->throttle = 0;
\r
1625 /* Ensure the restart delay is always applied. */
\r
1626 if (service->restart_delay && ! service->throttle) service->throttle++;
\r
1631 /* Stop the service */
\r
1632 int stop_service(nssm_service_t *service, unsigned long exitcode, bool graceful, bool default_action) {
\r
1633 service->allow_restart = false;
\r
1634 if (service->wait_handle) {
\r
1635 UnregisterWait(service->wait_handle);
\r
1636 service->wait_handle = 0;
\r
1639 service->rotate_stdout_online = service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;
\r
1641 if (default_action && ! exitcode && ! graceful) {
\r
1642 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
1646 /* Signal we are stopping */
\r
1648 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
1649 service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;
\r
1650 SetServiceStatus(service->status_handle, &service->status);
\r
1653 /* Nothing to do if service isn't running */
\r
1654 if (service->pid) {
\r
1655 /* Shut down service */
\r
1656 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_TERMINATEPROCESS, service->name, service->exe, 0);
\r
1657 kill_process(service, service->process_handle, service->pid, 0);
\r
1659 else log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_PROCESS_ALREADY_STOPPED, service->name, service->exe, 0);
\r
1661 end_service((void *) service, true);
\r
1663 /* Signal we stopped */
\r
1665 service->status.dwCurrentState = SERVICE_STOPPED;
\r
1667 service->status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
\r
1668 service->status.dwServiceSpecificExitCode = exitcode;
\r
1671 service->status.dwWin32ExitCode = NO_ERROR;
\r
1672 service->status.dwServiceSpecificExitCode = 0;
\r
1674 SetServiceStatus(service->status_handle, &service->status);
\r
1680 /* Callback function triggered when the server exits */
\r
1681 void CALLBACK end_service(void *arg, unsigned char why) {
\r
1682 nssm_service_t *service = (nssm_service_t *) arg;
\r
1684 if (service->stopping) return;
\r
1686 service->stopping = true;
\r
1688 service->rotate_stdout_online = service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;
\r
1690 /* Use now as a dummy exit time. */
\r
1691 GetSystemTimeAsFileTime(&service->exit_time);
\r
1693 /* Check exit code */
\r
1694 unsigned long exitcode = 0;
\r
1696 if (service->process_handle) {
\r
1697 GetExitCodeProcess(service->process_handle, &exitcode);
\r
1698 /* Check real exit time. */
\r
1699 if (exitcode != STILL_ACTIVE) get_process_exit_time(service->process_handle, &service->exit_time);
\r
1700 CloseHandle(service->process_handle);
\r
1703 service->process_handle = 0;
\r
1706 Log that the service ended BEFORE logging about killing the process
\r
1707 tree. See below for the possible values of the why argument.
\r
1710 _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), exitcode);
\r
1711 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ENDED_SERVICE, service->exe, service->name, code, 0);
\r
1715 if (exitcode == STILL_ACTIVE) exitcode = 0;
\r
1716 if (service->pid) kill_process_tree(service, service->pid, exitcode, service->pid);
\r
1720 The why argument is true if our wait timed out or false otherwise.
\r
1721 Our wait is infinite so why will never be true when called by the system.
\r
1722 If it is indeed true, assume we were called from stop_service() because
\r
1723 this is a controlled shutdown, and don't take any restart action.
\r
1726 if (! service->allow_restart) return;
\r
1728 /* What action should we take? */
\r
1729 int action = NSSM_EXIT_RESTART;
\r
1730 TCHAR action_string[ACTION_LEN];
\r
1731 bool default_action;
\r
1732 if (! get_exit_action(service->name, &exitcode, action_string, &default_action)) {
\r
1733 for (int i = 0; exit_action_strings[i]; i++) {
\r
1734 if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
1742 /* Try to restart the service or return failure code to service manager */
\r
1743 case NSSM_EXIT_RESTART:
\r
1744 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_RESTART, service->name, code, exit_action_strings[action], service->exe, 0);
\r
1745 while (monitor_service(service)) {
\r
1746 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_RESTART_SERVICE_FAILED, service->exe, service->name, 0);
\r
1751 /* Do nothing, just like srvany would */
\r
1752 case NSSM_EXIT_IGNORE:
\r
1753 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_IGNORE, service->name, code, exit_action_strings[action], service->exe, 0);
\r
1757 /* Tell the service manager we are finished */
\r
1758 case NSSM_EXIT_REALLY:
\r
1759 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_REALLY, service->name, code, exit_action_strings[action], 0);
\r
1760 stop_service(service, exitcode, true, default_action);
\r
1763 /* Fake a crash so pre-Vista service managers will run recovery actions. */
\r
1764 case NSSM_EXIT_UNCLEAN:
\r
1765 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_UNCLEAN, service->name, code, exit_action_strings[action], 0);
\r
1766 stop_service(service, exitcode, false, default_action);
\r
1773 void throttle_restart(nssm_service_t *service) {
\r
1774 /* This can't be a restart if the service is already running. */
\r
1775 if (! service->throttle++) return;
\r
1778 unsigned long throttle_ms = throttle_milliseconds(service->throttle);
\r
1779 TCHAR threshold[8], milliseconds[8];
\r
1781 if (service->restart_delay > throttle_ms) ms = service->restart_delay;
\r
1782 else ms = throttle_ms;
\r
1784 if (service->throttle > 7) service->throttle = 8;
\r
1786 _sntprintf_s(milliseconds, _countof(milliseconds), _TRUNCATE, _T("%lu"), ms);
\r
1788 if (service->throttle == 1 && service->restart_delay > throttle_ms) log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESTART_DELAY, service->name, milliseconds, 0);
\r
1790 _sntprintf_s(threshold, _countof(threshold), _TRUNCATE, _T("%lu"), service->throttle_delay);
\r
1791 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_THROTTLED, service->name, threshold, milliseconds, 0);
\r
1794 if (use_critical_section) EnterCriticalSection(&service->throttle_section);
\r
1795 else if (service->throttle_timer) {
\r
1796 ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));
\r
1797 service->throttle_duetime.QuadPart = 0 - (ms * 10000LL);
\r
1798 SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);
\r
1801 service->status.dwCurrentState = SERVICE_PAUSED;
\r
1802 SetServiceStatus(service->status_handle, &service->status);
\r
1804 if (use_critical_section) {
\r
1805 imports.SleepConditionVariableCS(&service->throttle_condition, &service->throttle_section, ms);
\r
1806 LeaveCriticalSection(&service->throttle_section);
\r
1809 if (service->throttle_timer) WaitForSingleObject(service->throttle_timer, INFINITE);
\r
1815 When responding to a stop (or any other) request we need to set dwWaitHint to
\r
1816 the number of milliseconds we expect the operation to take, and optionally
\r
1817 increase dwCheckPoint. If dwWaitHint milliseconds elapses without the
\r
1818 operation completing or dwCheckPoint increasing, the system will consider the
\r
1819 service to be hung.
\r
1821 However the system will consider the service to be hung after 30000
\r
1822 milliseconds regardless of the value of dwWaitHint if dwCheckPoint has not
\r
1823 changed. Therefore if we want to wait longer than that we must periodically
\r
1824 increase dwCheckPoint.
\r
1826 Furthermore, it will consider the service to be hung after 60000 milliseconds
\r
1827 regardless of the value of dwCheckPoint unless dwWaitHint is increased every
\r
1828 time dwCheckPoint is also increased.
\r
1830 Our strategy then is to retrieve the initial dwWaitHint and wait for
\r
1831 NSSM_SERVICE_STATUS_DEADLINE milliseconds. If the process is still running
\r
1832 and we haven't finished waiting we increment dwCheckPoint and add whichever is
\r
1833 smaller of NSSM_SERVICE_STATUS_DEADLINE or the remaining timeout to
\r
1836 Only doing both these things will prevent the system from killing the service.
\r
1838 Returns: 1 if the wait timed out.
\r
1839 0 if the wait completed.
\r
1842 int await_shutdown(nssm_service_t *service, TCHAR *function_name, unsigned long timeout) {
\r
1843 unsigned long interval;
\r
1844 unsigned long waithint;
\r
1845 unsigned long ret;
\r
1846 unsigned long waited;
\r
1847 TCHAR interval_milliseconds[16];
\r
1848 TCHAR timeout_milliseconds[16];
\r
1849 TCHAR waited_milliseconds[16];
\r
1850 TCHAR *function = function_name;
\r
1852 /* Add brackets to function name. */
\r
1853 size_t funclen = _tcslen(function_name) + 3;
\r
1854 TCHAR *func = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, funclen * sizeof(TCHAR));
\r
1856 if (_sntprintf_s(func, funclen, _TRUNCATE, _T("%s()"), function_name) > -1) function = func;
\r
1859 _sntprintf_s(timeout_milliseconds, _countof(timeout_milliseconds), _TRUNCATE, _T("%lu"), timeout);
\r
1861 waithint = service->status.dwWaitHint;
\r
1863 while (waited < timeout) {
\r
1864 interval = timeout - waited;
\r
1865 if (interval > NSSM_SERVICE_STATUS_DEADLINE) interval = NSSM_SERVICE_STATUS_DEADLINE;
\r
1867 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
1868 service->status.dwWaitHint += interval;
\r
1869 service->status.dwCheckPoint++;
\r
1870 SetServiceStatus(service->status_handle, &service->status);
\r
1873 _sntprintf_s(waited_milliseconds, _countof(waited_milliseconds), _TRUNCATE, _T("%lu"), waited);
\r
1874 _sntprintf_s(interval_milliseconds, _countof(interval_milliseconds), _TRUNCATE, _T("%lu"), interval);
\r
1875 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_AWAITING_SHUTDOWN, function, service->name, waited_milliseconds, interval_milliseconds, timeout_milliseconds, 0);
\r
1878 switch (WaitForSingleObject(service->process_handle, interval)) {
\r
1879 case WAIT_OBJECT_0:
\r
1883 case WAIT_TIMEOUT:
\r
1892 waited += interval;
\r
1896 if (func) HeapFree(GetProcessHeap(), 0, func);
\r