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 HeapFree(GetProcessHeap(), 0, service);
\r
687 /* About to install the service */
\r
688 int pre_install_service(int argc, TCHAR **argv) {
\r
689 nssm_service_t *service = alloc_nssm_service();
\r
690 set_nssm_service_defaults(service);
\r
691 if (argc) _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]);
\r
693 /* Show the dialogue box if we didn't give the service name and path */
\r
694 if (argc < 2) return nssm_gui(IDD_INSTALL, service);
\r
697 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("service"), _T("pre_install_service()"));
\r
700 _sntprintf_s(service->exe, _countof(service->exe), _TRUNCATE, _T("%s"), argv[1]);
\r
702 /* Arguments are optional */
\r
703 size_t flagslen = 0;
\r
706 for (i = 2; i < argc; i++) flagslen += _tcslen(argv[i]) + 1;
\r
707 if (! flagslen) flagslen = 1;
\r
708 if (flagslen > _countof(service->flags)) {
\r
709 print_message(stderr, NSSM_MESSAGE_FLAGS_TOO_LONG);
\r
713 for (i = 2; i < argc; i++) {
\r
714 size_t len = _tcslen(argv[i]);
\r
715 memmove(service->flags + s, argv[i], len * sizeof(TCHAR));
\r
717 if (i < argc - 1) service->flags[s++] = _T(' ');
\r
720 /* Work out directory name */
\r
721 _sntprintf_s(service->dir, _countof(service->dir), _TRUNCATE, _T("%s"), service->exe);
\r
722 strip_basename(service->dir);
\r
724 int ret = install_service(service);
\r
725 cleanup_nssm_service(service);
\r
729 /* About to edit the service. */
\r
730 int pre_edit_service(int argc, TCHAR **argv) {
\r
731 /* Require service name. */
\r
732 if (argc < 2) return usage(1);
\r
734 /* Are we editing on the command line? */
\r
735 enum { MODE_EDITING, MODE_GETTING, MODE_SETTING, MODE_RESETTING } mode = MODE_EDITING;
\r
736 const TCHAR *verb = argv[0];
\r
737 const TCHAR *service_name = argv[1];
\r
738 bool getting = false;
\r
739 bool unsetting = false;
\r
741 /* Minimum number of arguments. */
\r
743 /* Index of first value. */
\r
746 if (str_equiv(verb, _T("get"))) {
\r
748 mode = MODE_GETTING;
\r
750 else if (str_equiv(verb, _T("set"))) {
\r
752 mode = MODE_SETTING;
\r
754 else if (str_equiv(verb, _T("reset")) || str_equiv(verb, _T("unset"))) {
\r
756 mode = MODE_RESETTING;
\r
758 if (argc < mandatory) return usage(1);
\r
760 const TCHAR *parameter = 0;
\r
761 settings_t *setting = 0;
\r
764 /* Validate the parameter. */
\r
765 if (mandatory > 2) {
\r
766 bool additional_mandatory = false;
\r
768 parameter = argv[2];
\r
769 for (i = 0; settings[i].name; i++) {
\r
770 setting = &settings[i];
\r
771 if (! str_equiv(setting->name, parameter)) continue;
\r
772 if (((setting->additional & ADDITIONAL_GETTING) && mode == MODE_GETTING) || ((setting->additional & ADDITIONAL_SETTING) && mode == MODE_SETTING) || ((setting->additional & ADDITIONAL_RESETTING) && mode == MODE_RESETTING)) {
\r
773 additional_mandatory = true;
\r
778 if (! settings[i].name) {
\r
779 print_message(stderr, NSSM_MESSAGE_INVALID_PARAMETER, parameter);
\r
780 for (i = 0; settings[i].name; i++) _ftprintf(stderr, _T("%s\n"), settings[i].name);
\r
785 if (additional_mandatory) {
\r
786 if (argc < mandatory) {
\r
787 print_message(stderr, NSSM_MESSAGE_MISSING_SUBPARAMETER, parameter);
\r
790 additional = argv[3];
\r
794 additional = argv[remainder];
\r
795 if (argc < mandatory) return usage(1);
\r
799 nssm_service_t *service = alloc_nssm_service();
\r
800 _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), service_name);
\r
802 /* Open service manager */
\r
803 SC_HANDLE services = open_service_manager();
\r
805 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
809 /* Try to open the service */
\r
810 service->handle = open_service(services, service->name, service->name, _countof(service->name));
\r
811 if (! service->handle) {
\r
812 CloseServiceHandle(services);
\r
816 /* Get system details. */
\r
817 QUERY_SERVICE_CONFIG *qsc = query_service_config(service->name, service->handle);
\r
819 CloseHandle(service->handle);
\r
820 CloseServiceHandle(services);
\r
824 service->type = qsc->dwServiceType;
\r
825 if (! (service->type & SERVICE_WIN32_OWN_PROCESS)) {
\r
826 if (mode != MODE_GETTING) {
\r
827 HeapFree(GetProcessHeap(), 0, qsc);
\r
828 CloseHandle(service->handle);
\r
829 CloseServiceHandle(services);
\r
830 print_message(stderr, NSSM_MESSAGE_CANNOT_EDIT, service->name, NSSM_WIN32_OWN_PROCESS, 0);
\r
835 if (get_service_startup(service->name, service->handle, qsc, &service->startup)) {
\r
836 if (mode != MODE_GETTING) {
\r
837 HeapFree(GetProcessHeap(), 0, qsc);
\r
838 CloseHandle(service->handle);
\r
839 CloseServiceHandle(services);
\r
844 if (get_service_username(service->name, qsc, &service->username, &service->usernamelen)) {
\r
845 if (mode != MODE_GETTING) {
\r
846 HeapFree(GetProcessHeap(), 0, qsc);
\r
847 CloseHandle(service->handle);
\r
848 CloseServiceHandle(services);
\r
853 _sntprintf_s(service->displayname, _countof(service->displayname), _TRUNCATE, _T("%s"), qsc->lpDisplayName);
\r
855 /* Get the canonical service name. We open it case insensitively. */
\r
856 unsigned long bufsize = _countof(service->name);
\r
857 GetServiceKeyName(services, service->displayname, service->name, &bufsize);
\r
859 /* Remember the executable in case it isn't NSSM. */
\r
860 _sntprintf_s(service->image, _countof(service->image), _TRUNCATE, _T("%s"), qsc->lpBinaryPathName);
\r
861 HeapFree(GetProcessHeap(), 0, qsc);
\r
863 /* Get extended system details. */
\r
864 if (get_service_description(service->name, service->handle, _countof(service->description), service->description)) {
\r
865 if (mode != MODE_GETTING) {
\r
866 CloseHandle(service->handle);
\r
867 CloseServiceHandle(services);
\r
872 /* Get NSSM details. */
\r
873 get_parameters(service, 0);
\r
875 CloseServiceHandle(services);
\r
877 if (! service->exe[0]) {
\r
878 service->native = true;
\r
879 if (mode != MODE_GETTING) print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE, service->name, NSSM, service->image);
\r
882 /* Editing with the GUI. */
\r
883 if (mode == MODE_EDITING) {
\r
884 nssm_gui(IDD_EDIT, service);
\r
888 /* Trying to manage App* parameters for a non-NSSM service. */
\r
889 if (! setting->native && service->native) {
\r
890 CloseHandle(service->handle);
\r
891 print_message(stderr, NSSM_MESSAGE_NATIVE_PARAMETER, setting->name, NSSM);
\r
899 if (mode == MODE_GETTING) {
\r
900 if (! service->native) {
\r
901 key = open_registry(service->name, KEY_READ);
\r
902 if (! key) return 4;
\r
905 if (setting->native) ret = get_setting(service->name, service->handle, setting, &value, additional);
\r
906 else ret = get_setting(service->name, key, setting, &value, additional);
\r
908 CloseHandle(service->handle);
\r
912 switch (setting->type) {
\r
913 case REG_EXPAND_SZ:
\r
916 _tprintf(_T("%s\n"), value.string ? value.string : _T(""));
\r
917 HeapFree(GetProcessHeap(), 0, value.string);
\r
921 _tprintf(_T("%u\n"), value.numeric);
\r
925 if (! service->native) RegCloseKey(key);
\r
926 CloseHandle(service->handle);
\r
930 /* Build the value. */
\r
931 if (mode == MODE_RESETTING) {
\r
932 /* Unset the parameter. */
\r
936 /* Set the parameter. */
\r
938 size_t delimiterlen = (setting->additional & ADDITIONAL_CRLF) ? 2 : 1;
\r
939 for (i = remainder; i < argc; i++) len += _tcslen(argv[i]) + delimiterlen;
\r
942 value.string = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, len * sizeof(TCHAR));
\r
943 if (! value.string) {
\r
944 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("value"), _T("edit_service()"));
\r
945 CloseHandle(service->handle);
\r
950 for (i = remainder; i < argc; i++) {
\r
951 size_t len = _tcslen(argv[i]);
\r
952 memmove(value.string + s, argv[i], len * sizeof(TCHAR));
\r
954 if (i < argc - 1) {
\r
955 if (setting->additional & ADDITIONAL_CRLF) {
\r
956 value.string[s++] = _T('\r');
\r
957 value.string[s++] = _T('\n');
\r
959 else value.string[s++] = _T(' ');
\r
962 value.string[s] = _T('\0');
\r
965 if (! service->native) {
\r
966 key = open_registry(service->name, KEY_WRITE);
\r
968 if (value.string) HeapFree(GetProcessHeap(), 0, value.string);
\r
973 if (setting->native) ret = set_setting(service->name, service->handle, setting, &value, additional);
\r
974 else ret = set_setting(service->name, key, setting, &value, additional);
\r
975 if (value.string) HeapFree(GetProcessHeap(), 0, value.string);
\r
977 if (! service->native) RegCloseKey(key);
\r
978 CloseHandle(service->handle);
\r
982 if (! service->native) RegCloseKey(key);
\r
983 CloseHandle(service->handle);
\r
988 /* About to remove the service */
\r
989 int pre_remove_service(int argc, TCHAR **argv) {
\r
990 nssm_service_t *service = alloc_nssm_service();
\r
991 set_nssm_service_defaults(service);
\r
992 if (argc) _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]);
\r
994 /* Show dialogue box if we didn't pass service name and "confirm" */
\r
995 if (argc < 2) return nssm_gui(IDD_REMOVE, service);
\r
996 if (str_equiv(argv[1], _T("confirm"))) {
\r
997 int ret = remove_service(service);
\r
998 cleanup_nssm_service(service);
\r
1001 print_message(stderr, NSSM_MESSAGE_PRE_REMOVE_SERVICE);
\r
1005 /* Install the service */
\r
1006 int install_service(nssm_service_t *service) {
\r
1007 if (! service) return 1;
\r
1009 /* Open service manager */
\r
1010 SC_HANDLE services = open_service_manager();
\r
1012 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
1013 cleanup_nssm_service(service);
\r
1017 /* Get path of this program */
\r
1018 GetModuleFileName(0, service->image, _countof(service->image));
\r
1020 /* Create the service - settings will be changed in edit_service() */
\r
1021 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
1022 if (! service->handle) {
\r
1023 print_message(stderr, NSSM_MESSAGE_CREATESERVICE_FAILED, error_string(GetLastError()));
\r
1024 CloseServiceHandle(services);
\r
1028 if (edit_service(service, false)) {
\r
1029 DeleteService(service->handle);
\r
1030 CloseServiceHandle(services);
\r
1034 print_message(stdout, NSSM_MESSAGE_SERVICE_INSTALLED, service->name);
\r
1037 CloseServiceHandle(services);
\r
1042 /* Edit the service. */
\r
1043 int edit_service(nssm_service_t *service, bool editing) {
\r
1044 if (! service) return 1;
\r
1047 The only two valid flags for service type are SERVICE_WIN32_OWN_PROCESS
\r
1048 and SERVICE_INTERACTIVE_PROCESS.
\r
1050 service->type &= SERVICE_INTERACTIVE_PROCESS;
\r
1051 service->type |= SERVICE_WIN32_OWN_PROCESS;
\r
1053 /* Startup type. */
\r
1054 unsigned long startup;
\r
1055 switch (service->startup) {
\r
1056 case NSSM_STARTUP_MANUAL: startup = SERVICE_DEMAND_START; break;
\r
1057 case NSSM_STARTUP_DISABLED: startup = SERVICE_DISABLED; break;
\r
1058 default: startup = SERVICE_AUTO_START;
\r
1061 /* Display name. */
\r
1062 if (! service->displayname[0]) _sntprintf_s(service->displayname, _countof(service->displayname), _TRUNCATE, _T("%s"), service->name);
\r
1065 Username must be NULL if we aren't changing or an account name.
\r
1066 We must explicitly use LOCALSYSTEM to change it when we are editing.
\r
1067 Password must be NULL if we aren't changing, a password or "".
\r
1068 Empty passwords are valid but we won't allow them in the GUI.
\r
1070 TCHAR *username = 0;
\r
1071 TCHAR *password = 0;
\r
1072 if (service->usernamelen) {
\r
1073 username = service->username;
\r
1074 if (service->passwordlen) password = service->password;
\r
1075 else password = _T("");
\r
1077 else if (editing) username = NSSM_LOCALSYSTEM_ACCOUNT;
\r
1079 if (grant_logon_as_service(username)) {
\r
1080 print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username);
\r
1084 if (! ChangeServiceConfig(service->handle, service->type, startup, SERVICE_NO_CHANGE, 0, 0, 0, 0, username, password, service->displayname)) {
\r
1085 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
1089 if (service->description[0] || editing) {
\r
1090 set_service_description(service->name, service->handle, service->description);
\r
1093 SERVICE_DELAYED_AUTO_START_INFO delayed;
\r
1094 ZeroMemory(&delayed, sizeof(delayed));
\r
1095 if (service->startup == NSSM_STARTUP_DELAYED) delayed.fDelayedAutostart = 1;
\r
1096 else delayed.fDelayedAutostart = 0;
\r
1097 /* Delayed startup isn't supported until Vista. */
\r
1098 if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, &delayed)) {
\r
1099 unsigned long error = GetLastError();
\r
1100 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
\r
1101 if (error != ERROR_INVALID_LEVEL) {
\r
1102 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_DELAYED_AUTO_START_INFO_FAILED, service->name, error_string(error), 0);
\r
1106 /* Don't mess with parameters which aren't ours. */
\r
1107 if (! service->native) {
\r
1108 /* Now we need to put the parameters into the registry */
\r
1109 if (create_parameters(service, editing)) {
\r
1110 print_message(stderr, NSSM_MESSAGE_CREATE_PARAMETERS_FAILED);
\r
1114 set_service_recovery(service);
\r
1120 /* Control a service. */
\r
1121 int control_service(unsigned long control, int argc, TCHAR **argv) {
\r
1122 if (argc < 1) return usage(1);
\r
1123 TCHAR *service_name = argv[0];
\r
1124 TCHAR canonical_name[SERVICE_NAME_LENGTH];
\r
1126 SC_HANDLE services = open_service_manager();
\r
1128 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
1132 SC_HANDLE service_handle = open_service(services, service_name, canonical_name, _countof(canonical_name));
\r
1133 if (! service_handle) {
\r
1134 CloseServiceHandle(services);
\r
1139 unsigned long error;
\r
1140 SERVICE_STATUS service_status;
\r
1141 if (control == NSSM_SERVICE_CONTROL_START) {
\r
1142 unsigned long initial_status = SERVICE_STOPPED;
\r
1143 ret = StartService(service_handle, (unsigned long) argc, (const TCHAR **) argv);
\r
1144 error = GetLastError();
\r
1145 CloseServiceHandle(services);
\r
1147 if (error == ERROR_IO_PENDING) {
\r
1149 Older versions of Windows return immediately with ERROR_IO_PENDING
\r
1150 indicate that the operation is still in progress. Newer versions
\r
1151 will return it if there really is a delay.
\r
1154 error = ERROR_SUCCESS;
\r
1158 int response = await_service_control_response(control, service_handle, &service_status, initial_status);
\r
1159 CloseHandle(service_handle);
\r
1162 print_message(stderr, NSSM_MESSAGE_BAD_CONTROL_RESPONSE, canonical_name, service_status_text(service_status.dwCurrentState), service_control_text(control));
\r
1165 else _tprintf(_T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1169 CloseHandle(service_handle);
\r
1170 _ftprintf(stderr, _T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1174 else if (control == SERVICE_CONTROL_INTERROGATE) {
\r
1176 We could actually send an INTERROGATE control but that won't return
\r
1177 any information if the service is stopped and we don't care about
\r
1178 the extra details it might give us in any case. So we'll fake it.
\r
1180 ret = QueryServiceStatus(service_handle, &service_status);
\r
1181 error = GetLastError();
\r
1184 _tprintf(_T("%s\n"), service_status_text(service_status.dwCurrentState));
\r
1188 _ftprintf(stderr, _T("%s: %s\n"), canonical_name, error_string(error));
\r
1193 ret = ControlService(service_handle, control, &service_status);
\r
1194 unsigned long initial_status = service_status.dwCurrentState;
\r
1195 error = GetLastError();
\r
1196 CloseServiceHandle(services);
\r
1198 if (error == ERROR_IO_PENDING) {
\r
1200 error = ERROR_SUCCESS;
\r
1204 int response = await_service_control_response(control, service_handle, &service_status, initial_status);
\r
1205 CloseHandle(service_handle);
\r
1208 print_message(stderr, NSSM_MESSAGE_BAD_CONTROL_RESPONSE, canonical_name, service_status_text(service_status.dwCurrentState), service_control_text(control));
\r
1211 else _tprintf(_T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1215 CloseHandle(service_handle);
\r
1216 _ftprintf(stderr, _T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1222 /* Remove the service */
\r
1223 int remove_service(nssm_service_t *service) {
\r
1224 if (! service) return 1;
\r
1226 /* Open service manager */
\r
1227 SC_HANDLE services = open_service_manager();
\r
1229 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
1233 /* Try to open the service */
\r
1234 service->handle = open_service(services, service->name, service->name, _countof(service->name));
\r
1235 if (! service->handle) {
\r
1236 CloseServiceHandle(services);
\r
1240 /* Get the canonical service name. We open it case insensitively. */
\r
1241 unsigned long bufsize = _countof(service->displayname);
\r
1242 GetServiceDisplayName(services, service->name, service->displayname, &bufsize);
\r
1243 bufsize = _countof(service->name);
\r
1244 GetServiceKeyName(services, service->displayname, service->name, &bufsize);
\r
1246 /* Try to delete the service */
\r
1247 if (! DeleteService(service->handle)) {
\r
1248 print_message(stderr, NSSM_MESSAGE_DELETESERVICE_FAILED);
\r
1249 CloseServiceHandle(services);
\r
1254 CloseServiceHandle(services);
\r
1256 print_message(stdout, NSSM_MESSAGE_SERVICE_REMOVED, service->name);
\r
1260 /* Service initialisation */
\r
1261 void WINAPI service_main(unsigned long argc, TCHAR **argv) {
\r
1262 nssm_service_t *service = alloc_nssm_service();
\r
1263 if (! service) return;
\r
1265 if (_sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]) < 0) {
\r
1266 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("service->name"), _T("service_main()"), 0);
\r
1270 /* We can use a condition variable in a critical section on Vista or later. */
\r
1271 if (imports.SleepConditionVariableCS && imports.WakeConditionVariable) use_critical_section = true;
\r
1272 else use_critical_section = false;
\r
1274 /* Initialise status */
\r
1275 ZeroMemory(&service->status, sizeof(service->status));
\r
1276 service->status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;
\r
1277 service->status.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE;
\r
1278 service->status.dwWin32ExitCode = NO_ERROR;
\r
1279 service->status.dwServiceSpecificExitCode = 0;
\r
1280 service->status.dwCheckPoint = 0;
\r
1281 service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;
\r
1283 /* Signal we AREN'T running the server */
\r
1284 service->process_handle = 0;
\r
1287 /* Register control handler */
\r
1288 service->status_handle = RegisterServiceCtrlHandlerEx(NSSM, service_control_handler, (void *) service);
\r
1289 if (! service->status_handle) {
\r
1290 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_REGISTERSERVICECTRLHANDER_FAILED, error_string(GetLastError()), 0);
\r
1294 log_service_control(service->name, 0, true);
\r
1296 service->status.dwCurrentState = SERVICE_START_PENDING;
\r
1297 service->status.dwWaitHint = service->throttle_delay + NSSM_WAITHINT_MARGIN;
\r
1298 SetServiceStatus(service->status_handle, &service->status);
\r
1301 /* Try to create the exit action parameters; we don't care if it fails */
\r
1302 create_exit_action(service->name, exit_action_strings[0], false);
\r
1304 SC_HANDLE services = open_service_manager();
\r
1306 service->handle = OpenService(services, service->name, SC_MANAGER_ALL_ACCESS);
\r
1307 set_service_recovery(service);
\r
1308 CloseServiceHandle(services);
\r
1312 /* Used for signalling a resume if the service pauses when throttled. */
\r
1313 if (use_critical_section) {
\r
1314 InitializeCriticalSection(&service->throttle_section);
\r
1315 service->throttle_section_initialised = true;
\r
1318 service->throttle_timer = CreateWaitableTimer(0, 1, 0);
\r
1319 if (! service->throttle_timer) {
\r
1320 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_CREATEWAITABLETIMER_FAILED, service->name, error_string(GetLastError()), 0);
\r
1324 monitor_service(service);
\r
1327 /* Make sure service recovery actions are taken where necessary */
\r
1328 void set_service_recovery(nssm_service_t *service) {
\r
1329 SERVICE_FAILURE_ACTIONS_FLAG flag;
\r
1330 ZeroMemory(&flag, sizeof(flag));
\r
1331 flag.fFailureActionsOnNonCrashFailures = true;
\r
1333 /* This functionality was added in Vista so the call may fail */
\r
1334 if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, &flag)) {
\r
1335 unsigned long error = GetLastError();
\r
1336 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
\r
1337 if (error != ERROR_INVALID_LEVEL) {
\r
1338 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_FAILURE_ACTIONS_FAILED, service->name, error_string(error), 0);
\r
1343 int monitor_service(nssm_service_t *service) {
\r
1344 /* Set service status to started */
\r
1345 int ret = start_service(service);
\r
1348 _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%d"), ret);
\r
1349 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_START_SERVICE_FAILED, service->exe, service->name, ret, 0);
\r
1352 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_STARTED_SERVICE, service->exe, service->flags, service->name, service->dir, 0);
\r
1354 /* Monitor service */
\r
1355 if (! RegisterWaitForSingleObject(&service->wait_handle, service->process_handle, end_service, (void *) service, INFINITE, WT_EXECUTEONLYONCE | WT_EXECUTELONGFUNCTION)) {
\r
1356 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_REGISTERWAITFORSINGLEOBJECT_FAILED, service->name, service->exe, error_string(GetLastError()), 0);
\r
1362 TCHAR *service_control_text(unsigned long control) {
\r
1363 switch (control) {
\r
1364 /* HACK: there is no SERVICE_CONTROL_START constant */
\r
1365 case NSSM_SERVICE_CONTROL_START: return _T("START");
\r
1366 case SERVICE_CONTROL_STOP: return _T("STOP");
\r
1367 case SERVICE_CONTROL_SHUTDOWN: return _T("SHUTDOWN");
\r
1368 case SERVICE_CONTROL_PAUSE: return _T("PAUSE");
\r
1369 case SERVICE_CONTROL_CONTINUE: return _T("CONTINUE");
\r
1370 case SERVICE_CONTROL_INTERROGATE: return _T("INTERROGATE");
\r
1371 case NSSM_SERVICE_CONTROL_ROTATE: return _T("ROTATE");
\r
1372 default: return 0;
\r
1376 TCHAR *service_status_text(unsigned long status) {
\r
1378 case SERVICE_STOPPED: return _T("SERVICE_STOPPED");
\r
1379 case SERVICE_START_PENDING: return _T("SERVICE_START_PENDING");
\r
1380 case SERVICE_STOP_PENDING: return _T("SERVICE_STOP_PENDING");
\r
1381 case SERVICE_RUNNING: return _T("SERVICE_RUNNING");
\r
1382 case SERVICE_CONTINUE_PENDING: return _T("SERVICE_CONTINUE_PENDING");
\r
1383 case SERVICE_PAUSE_PENDING: return _T("SERVICE_PAUSE_PENDING");
\r
1384 case SERVICE_PAUSED: return _T("SERVICE_PAUSED");
\r
1385 default: return 0;
\r
1389 void log_service_control(TCHAR *service_name, unsigned long control, bool handled) {
\r
1390 TCHAR *text = service_control_text(control);
\r
1391 unsigned long event;
\r
1394 /* "0x" + 8 x hex + NULL */
\r
1395 text = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, 11 * sizeof(TCHAR));
\r
1397 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);
\r
1400 if (_sntprintf_s(text, 11, _TRUNCATE, _T("0x%08x"), control) < 0) {
\r
1401 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);
\r
1402 HeapFree(GetProcessHeap(), 0, text);
\r
1406 event = NSSM_EVENT_SERVICE_CONTROL_UNKNOWN;
\r
1408 else if (handled) event = NSSM_EVENT_SERVICE_CONTROL_HANDLED;
\r
1409 else event = NSSM_EVENT_SERVICE_CONTROL_NOT_HANDLED;
\r
1411 log_event(EVENTLOG_INFORMATION_TYPE, event, service_name, text, 0);
\r
1413 if (event == NSSM_EVENT_SERVICE_CONTROL_UNKNOWN) {
\r
1414 HeapFree(GetProcessHeap(), 0, text);
\r
1418 /* Service control handler */
\r
1419 unsigned long WINAPI service_control_handler(unsigned long control, unsigned long event, void *data, void *context) {
\r
1420 nssm_service_t *service = (nssm_service_t *) context;
\r
1422 switch (control) {
\r
1423 case SERVICE_CONTROL_INTERROGATE:
\r
1424 /* We always keep the service status up-to-date so this is a no-op. */
\r
1427 case SERVICE_CONTROL_SHUTDOWN:
\r
1428 case SERVICE_CONTROL_STOP:
\r
1429 log_service_control(service->name, control, true);
\r
1431 We MUST acknowledge the stop request promptly but we're committed to
\r
1432 waiting for the application to exit. Spawn a new thread to wait
\r
1433 while we acknowledge the request.
\r
1435 if (! CreateThread(NULL, 0, shutdown_service, context, 0, NULL)) {
\r
1436 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETHREAD_FAILED, error_string(GetLastError()), 0);
\r
1439 We couldn't create a thread to tidy up so we'll have to force the tidyup
\r
1440 to complete in time in this thread.
\r
1442 service->kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;
\r
1443 service->kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;
\r
1444 service->kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;
\r
1446 stop_service(service, 0, true, true);
\r
1450 case SERVICE_CONTROL_CONTINUE:
\r
1451 log_service_control(service->name, control, true);
\r
1452 service->throttle = 0;
\r
1453 if (use_critical_section) imports.WakeConditionVariable(&service->throttle_condition);
\r
1455 if (! service->throttle_timer) return ERROR_CALL_NOT_IMPLEMENTED;
\r
1456 ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));
\r
1457 SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);
\r
1459 /* We can't continue if the application is running! */
\r
1460 if (! service->process_handle) service->status.dwCurrentState = SERVICE_CONTINUE_PENDING;
\r
1461 service->status.dwWaitHint = throttle_milliseconds(service->throttle) + NSSM_WAITHINT_MARGIN;
\r
1462 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESET_THROTTLE, service->name, 0);
\r
1463 SetServiceStatus(service->status_handle, &service->status);
\r
1466 case SERVICE_CONTROL_PAUSE:
\r
1468 We don't accept pause messages but it isn't possible to register
\r
1469 only for continue messages so we have to handle this case.
\r
1471 log_service_control(service->name, control, false);
\r
1472 return ERROR_CALL_NOT_IMPLEMENTED;
\r
1474 case NSSM_SERVICE_CONTROL_ROTATE:
\r
1475 log_service_control(service->name, control, true);
\r
1476 if (service->rotate_stdout_online == NSSM_ROTATE_ONLINE) service->rotate_stdout_online = NSSM_ROTATE_ONLINE_ASAP;
\r
1477 if (service->rotate_stderr_online == NSSM_ROTATE_ONLINE) service->rotate_stderr_online = NSSM_ROTATE_ONLINE_ASAP;
\r
1481 /* Unknown control */
\r
1482 log_service_control(service->name, control, false);
\r
1483 return ERROR_CALL_NOT_IMPLEMENTED;
\r
1486 /* Start the service */
\r
1487 int start_service(nssm_service_t *service) {
\r
1488 service->stopping = false;
\r
1489 service->allow_restart = true;
\r
1491 if (service->process_handle) return 0;
\r
1493 /* Allocate a STARTUPINFO structure for a new process */
\r
1495 ZeroMemory(&si, sizeof(si));
\r
1496 si.cb = sizeof(si);
\r
1498 /* Allocate a PROCESSINFO structure for the process */
\r
1499 PROCESS_INFORMATION pi;
\r
1500 ZeroMemory(&pi, sizeof(pi));
\r
1502 /* Get startup parameters */
\r
1503 int ret = get_parameters(service, &si);
\r
1505 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_PARAMETERS_FAILED, service->name, 0);
\r
1506 return stop_service(service, 2, true, true);
\r
1509 /* Launch executable with arguments */
\r
1510 TCHAR cmd[CMD_LENGTH];
\r
1511 if (_sntprintf_s(cmd, _countof(cmd), _TRUNCATE, _T("\"%s\" %s"), service->exe, service->flags) < 0) {
\r
1512 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("command line"), _T("start_service"), 0);
\r
1513 close_output_handles(&si);
\r
1514 return stop_service(service, 2, true, true);
\r
1517 throttle_restart(service);
\r
1519 bool inherit_handles = false;
\r
1520 if (si.dwFlags & STARTF_USESTDHANDLES) inherit_handles = true;
\r
1521 unsigned long flags = service->priority & priority_mask();
\r
1522 if (service->affinity) flags |= CREATE_SUSPENDED;
\r
1524 flags |= CREATE_UNICODE_ENVIRONMENT;
\r
1526 if (! CreateProcess(0, cmd, 0, 0, inherit_handles, flags, service->env, service->dir, &si, &pi)) {
\r
1527 unsigned long exitcode = 3;
\r
1528 unsigned long error = GetLastError();
\r
1529 if (error == ERROR_INVALID_PARAMETER && service->env) {
\r
1530 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED_INVALID_ENVIRONMENT, service->name, service->exe, NSSM_REG_ENV, 0);
\r
1531 if (test_environment(service->env)) exitcode = 4;
\r
1533 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service->name, service->exe, error_string(error), 0);
\r
1534 close_output_handles(&si);
\r
1535 return stop_service(service, exitcode, true, true);
\r
1537 service->process_handle = pi.hProcess;
\r
1538 service->pid = pi.dwProcessId;
\r
1540 if (get_process_creation_time(service->process_handle, &service->creation_time)) ZeroMemory(&service->creation_time, sizeof(service->creation_time));
\r
1542 close_output_handles(&si);
\r
1544 if (service->affinity) {
\r
1546 We are explicitly storing service->affinity as a 64-bit unsigned integer
\r
1547 so that we can parse it regardless of whether we're running in 32-bit
\r
1548 or 64-bit mode. The arguments to SetProcessAffinityMask(), however, are
\r
1549 defined as type DWORD_PTR and hence limited to 32 bits on a 32-bit system
\r
1550 (or when running the 32-bit NSSM).
\r
1552 The result is a lot of seemingly-unnecessary casting throughout the code
\r
1553 and potentially confusion when we actually try to start the service.
\r
1554 Having said that, however, it's unlikely that we're actually going to
\r
1555 run in 32-bit mode on a system which has more than 32 CPUs so the
\r
1556 likelihood of seeing a confusing situation is somewhat diminished.
\r
1558 DWORD_PTR affinity, system_affinity;
\r
1560 if (GetProcessAffinityMask(service->process_handle, &affinity, &system_affinity)) affinity = service->affinity & system_affinity;
\r
1562 affinity = (DWORD_PTR) service->affinity;
\r
1563 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GETPROCESSAFFINITYMASK_FAILED, service->name, error_string(GetLastError()), 0);
\r
1566 if (! SetProcessAffinityMask(service->process_handle, affinity)) {
\r
1567 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_SETPROCESSAFFINITYMASK_FAILED, service->name, error_string(GetLastError()), 0);
\r
1570 ResumeThread(pi.hThread);
\r
1574 Wait for a clean startup before changing the service status to RUNNING
\r
1575 but be mindful of the fact that we are blocking the service control manager
\r
1576 so abandon the wait before too much time has elapsed.
\r
1578 unsigned long delay = service->throttle_delay;
\r
1579 if (delay > NSSM_SERVICE_STATUS_DEADLINE) {
\r
1580 TCHAR delay_milliseconds[16];
\r
1581 _sntprintf_s(delay_milliseconds, _countof(delay_milliseconds), _TRUNCATE, _T("%lu"), delay);
\r
1582 TCHAR deadline_milliseconds[16];
\r
1583 _sntprintf_s(deadline_milliseconds, _countof(deadline_milliseconds), _TRUNCATE, _T("%lu"), NSSM_SERVICE_STATUS_DEADLINE);
\r
1584 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_STARTUP_DELAY_TOO_LONG, service->name, delay_milliseconds, NSSM, deadline_milliseconds, 0);
\r
1585 delay = NSSM_SERVICE_STATUS_DEADLINE;
\r
1587 unsigned long deadline = WaitForSingleObject(service->process_handle, delay);
\r
1589 /* Signal successful start */
\r
1590 service->status.dwCurrentState = SERVICE_RUNNING;
\r
1591 SetServiceStatus(service->status_handle, &service->status);
\r
1593 /* Continue waiting for a clean startup. */
\r
1594 if (deadline == WAIT_TIMEOUT) {
\r
1595 if (service->throttle_delay > delay) {
\r
1596 if (WaitForSingleObject(service->process_handle, service->throttle_delay - delay) == WAIT_TIMEOUT) service->throttle = 0;
\r
1598 else service->throttle = 0;
\r
1601 /* Ensure the restart delay is always applied. */
\r
1602 if (service->restart_delay && ! service->throttle) service->throttle++;
\r
1607 /* Stop the service */
\r
1608 int stop_service(nssm_service_t *service, unsigned long exitcode, bool graceful, bool default_action) {
\r
1609 service->allow_restart = false;
\r
1610 if (service->wait_handle) {
\r
1611 UnregisterWait(service->wait_handle);
\r
1612 service->wait_handle = 0;
\r
1615 service->rotate_stdout_online = service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;
\r
1617 if (default_action && ! exitcode && ! graceful) {
\r
1618 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
1622 /* Signal we are stopping */
\r
1624 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
1625 service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;
\r
1626 SetServiceStatus(service->status_handle, &service->status);
\r
1629 /* Nothing to do if service isn't running */
\r
1630 if (service->pid) {
\r
1631 /* Shut down service */
\r
1632 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_TERMINATEPROCESS, service->name, service->exe, 0);
\r
1633 kill_process(service, service->process_handle, service->pid, 0);
\r
1635 else log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_PROCESS_ALREADY_STOPPED, service->name, service->exe, 0);
\r
1637 end_service((void *) service, true);
\r
1639 /* Signal we stopped */
\r
1641 service->status.dwCurrentState = SERVICE_STOPPED;
\r
1643 service->status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
\r
1644 service->status.dwServiceSpecificExitCode = exitcode;
\r
1647 service->status.dwWin32ExitCode = NO_ERROR;
\r
1648 service->status.dwServiceSpecificExitCode = 0;
\r
1650 SetServiceStatus(service->status_handle, &service->status);
\r
1656 /* Callback function triggered when the server exits */
\r
1657 void CALLBACK end_service(void *arg, unsigned char why) {
\r
1658 nssm_service_t *service = (nssm_service_t *) arg;
\r
1660 if (service->stopping) return;
\r
1662 service->stopping = true;
\r
1664 service->rotate_stdout_online = service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;
\r
1666 /* Check exit code */
\r
1667 unsigned long exitcode = 0;
\r
1669 if (service->process_handle) {
\r
1670 GetExitCodeProcess(service->process_handle, &exitcode);
\r
1671 if (exitcode == STILL_ACTIVE || get_process_exit_time(service->process_handle, &service->exit_time)) GetSystemTimeAsFileTime(&service->exit_time);
\r
1672 CloseHandle(service->process_handle);
\r
1674 else GetSystemTimeAsFileTime(&service->exit_time);
\r
1676 service->process_handle = 0;
\r
1679 Log that the service ended BEFORE logging about killing the process
\r
1680 tree. See below for the possible values of the why argument.
\r
1683 _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), exitcode);
\r
1684 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ENDED_SERVICE, service->exe, service->name, code, 0);
\r
1688 if (exitcode == STILL_ACTIVE) exitcode = 0;
\r
1689 if (service->pid) kill_process_tree(service, service->pid, exitcode, service->pid);
\r
1693 The why argument is true if our wait timed out or false otherwise.
\r
1694 Our wait is infinite so why will never be true when called by the system.
\r
1695 If it is indeed true, assume we were called from stop_service() because
\r
1696 this is a controlled shutdown, and don't take any restart action.
\r
1699 if (! service->allow_restart) return;
\r
1701 /* What action should we take? */
\r
1702 int action = NSSM_EXIT_RESTART;
\r
1703 TCHAR action_string[ACTION_LEN];
\r
1704 bool default_action;
\r
1705 if (! get_exit_action(service->name, &exitcode, action_string, &default_action)) {
\r
1706 for (int i = 0; exit_action_strings[i]; i++) {
\r
1707 if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
1715 /* Try to restart the service or return failure code to service manager */
\r
1716 case NSSM_EXIT_RESTART:
\r
1717 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_RESTART, service->name, code, exit_action_strings[action], service->exe, 0);
\r
1718 while (monitor_service(service)) {
\r
1719 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_RESTART_SERVICE_FAILED, service->exe, service->name, 0);
\r
1724 /* Do nothing, just like srvany would */
\r
1725 case NSSM_EXIT_IGNORE:
\r
1726 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_IGNORE, service->name, code, exit_action_strings[action], service->exe, 0);
\r
1730 /* Tell the service manager we are finished */
\r
1731 case NSSM_EXIT_REALLY:
\r
1732 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_REALLY, service->name, code, exit_action_strings[action], 0);
\r
1733 stop_service(service, exitcode, true, default_action);
\r
1736 /* Fake a crash so pre-Vista service managers will run recovery actions. */
\r
1737 case NSSM_EXIT_UNCLEAN:
\r
1738 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_UNCLEAN, service->name, code, exit_action_strings[action], 0);
\r
1739 stop_service(service, exitcode, false, default_action);
\r
1746 void throttle_restart(nssm_service_t *service) {
\r
1747 /* This can't be a restart if the service is already running. */
\r
1748 if (! service->throttle++) return;
\r
1751 unsigned long throttle_ms = throttle_milliseconds(service->throttle);
\r
1752 TCHAR threshold[8], milliseconds[8];
\r
1754 if (service->restart_delay > throttle_ms) ms = service->restart_delay;
\r
1755 else ms = throttle_ms;
\r
1757 if (service->throttle > 7) service->throttle = 8;
\r
1759 _sntprintf_s(milliseconds, _countof(milliseconds), _TRUNCATE, _T("%lu"), ms);
\r
1761 if (service->throttle == 1 && service->restart_delay > throttle_ms) log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESTART_DELAY, service->name, milliseconds, 0);
\r
1763 _sntprintf_s(threshold, _countof(threshold), _TRUNCATE, _T("%lu"), service->throttle_delay);
\r
1764 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_THROTTLED, service->name, threshold, milliseconds, 0);
\r
1767 if (use_critical_section) EnterCriticalSection(&service->throttle_section);
\r
1768 else if (service->throttle_timer) {
\r
1769 ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));
\r
1770 service->throttle_duetime.QuadPart = 0 - (ms * 10000LL);
\r
1771 SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);
\r
1774 service->status.dwCurrentState = SERVICE_PAUSED;
\r
1775 SetServiceStatus(service->status_handle, &service->status);
\r
1777 if (use_critical_section) {
\r
1778 imports.SleepConditionVariableCS(&service->throttle_condition, &service->throttle_section, ms);
\r
1779 LeaveCriticalSection(&service->throttle_section);
\r
1782 if (service->throttle_timer) WaitForSingleObject(service->throttle_timer, INFINITE);
\r
1788 When responding to a stop (or any other) request we need to set dwWaitHint to
\r
1789 the number of milliseconds we expect the operation to take, and optionally
\r
1790 increase dwCheckPoint. If dwWaitHint milliseconds elapses without the
\r
1791 operation completing or dwCheckPoint increasing, the system will consider the
\r
1792 service to be hung.
\r
1794 However the system will consider the service to be hung after 30000
\r
1795 milliseconds regardless of the value of dwWaitHint if dwCheckPoint has not
\r
1796 changed. Therefore if we want to wait longer than that we must periodically
\r
1797 increase dwCheckPoint.
\r
1799 Furthermore, it will consider the service to be hung after 60000 milliseconds
\r
1800 regardless of the value of dwCheckPoint unless dwWaitHint is increased every
\r
1801 time dwCheckPoint is also increased.
\r
1803 Our strategy then is to retrieve the initial dwWaitHint and wait for
\r
1804 NSSM_SERVICE_STATUS_DEADLINE milliseconds. If the process is still running
\r
1805 and we haven't finished waiting we increment dwCheckPoint and add whichever is
\r
1806 smaller of NSSM_SERVICE_STATUS_DEADLINE or the remaining timeout to
\r
1809 Only doing both these things will prevent the system from killing the service.
\r
1811 Returns: 1 if the wait timed out.
\r
1812 0 if the wait completed.
\r
1815 int await_shutdown(nssm_service_t *service, TCHAR *function_name, unsigned long timeout) {
\r
1816 unsigned long interval;
\r
1817 unsigned long waithint;
\r
1818 unsigned long ret;
\r
1819 unsigned long waited;
\r
1820 TCHAR interval_milliseconds[16];
\r
1821 TCHAR timeout_milliseconds[16];
\r
1822 TCHAR waited_milliseconds[16];
\r
1823 TCHAR *function = function_name;
\r
1825 /* Add brackets to function name. */
\r
1826 size_t funclen = _tcslen(function_name) + 3;
\r
1827 TCHAR *func = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, funclen * sizeof(TCHAR));
\r
1829 if (_sntprintf_s(func, funclen, _TRUNCATE, _T("%s()"), function_name) > -1) function = func;
\r
1832 _sntprintf_s(timeout_milliseconds, _countof(timeout_milliseconds), _TRUNCATE, _T("%lu"), timeout);
\r
1834 waithint = service->status.dwWaitHint;
\r
1836 while (waited < timeout) {
\r
1837 interval = timeout - waited;
\r
1838 if (interval > NSSM_SERVICE_STATUS_DEADLINE) interval = NSSM_SERVICE_STATUS_DEADLINE;
\r
1840 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
1841 service->status.dwWaitHint += interval;
\r
1842 service->status.dwCheckPoint++;
\r
1843 SetServiceStatus(service->status_handle, &service->status);
\r
1846 _sntprintf_s(waited_milliseconds, _countof(waited_milliseconds), _TRUNCATE, _T("%lu"), waited);
\r
1847 _sntprintf_s(interval_milliseconds, _countof(interval_milliseconds), _TRUNCATE, _T("%lu"), interval);
\r
1848 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_AWAITING_SHUTDOWN, function, service->name, waited_milliseconds, interval_milliseconds, timeout_milliseconds, 0);
\r
1851 switch (WaitForSingleObject(service->process_handle, interval)) {
\r
1852 case WAIT_OBJECT_0:
\r
1856 case WAIT_TIMEOUT:
\r
1865 waited += interval;
\r
1869 if (func) HeapFree(GetProcessHeap(), 0, func);
\r