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
21 int affinity_mask_to_string(__int64 mask, TCHAR **string) {
\r
22 if (! string) return 1;
\r
30 /* SetProcessAffinityMask() accepts a mask of up to 64 processors. */
\r
32 for (n = 0; n < _countof(set); n++) set[n].first = set[n].last = -1;
\r
34 for (i = 0, n = 0; i < _countof(set); i++) {
\r
35 if (mask & (1LL << i)) {
\r
36 if (set[n].first == -1) set[n].first = set[n].last = (int) i;
\r
37 else if (set[n].last == (int) i - 1) set[n].last = (int) i;
\r
40 set[n].first = set[n].last = (int) i;
\r
45 /* Worst case is 2x2 characters for first and last CPU plus - and/or , */
\r
46 size_t len = (size_t) (n + 1) * 6;
\r
47 *string = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len * sizeof(TCHAR));
\r
48 if (! string) return 2;
\r
52 for (i = 0; i <= n; i++) {
\r
53 if (i) (*string)[s++] = _T(',');
\r
54 ret = _sntprintf_s(*string + s, 3, _TRUNCATE, _T("%u"), set[i].first);
\r
56 HeapFree(GetProcessHeap(), 0, *string);
\r
61 if (set[i].last != set[i].first) {
\r
62 ret =_sntprintf_s(*string + s, 4, _TRUNCATE, _T("%c%u"), (set[i].last == set[i].first + 1) ? _T(',') : _T('-'), set[i].last);
\r
64 HeapFree(GetProcessHeap(), 0, *string);
\r
75 int affinity_string_to_mask(TCHAR *string, __int64 *mask) {
\r
76 if (! mask) return 1;
\r
79 if (! string) return 0;
\r
88 unsigned long number;
\r
90 for (n = 0; n < _countof(set); n++) set[n].first = set[n].last = -1;
\r
94 ret = str_number(s, &number, &end);
\r
96 if (ret == 0 || ret == 2) {
\r
97 if (number >= _countof(set)) return 2;
\r
98 set[n].first = set[n].last = (int) number;
\r
110 if (! *(++s)) return 3;
\r
111 ret = str_number(s, &number, &end);
\r
112 if (ret == 0 || ret == 2) {
\r
114 if (! *s || *s == _T(',')) {
\r
115 set[n].last = (int) number;
\r
132 for (i = 0; i <= n; i++) {
\r
133 for (int j = set[i].first; j <= set[i].last; j++) (__int64) *mask |= (1LL << (__int64) j);
\r
139 inline unsigned long priority_mask() {
\r
140 return REALTIME_PRIORITY_CLASS | HIGH_PRIORITY_CLASS | ABOVE_NORMAL_PRIORITY_CLASS | NORMAL_PRIORITY_CLASS | BELOW_NORMAL_PRIORITY_CLASS | IDLE_PRIORITY_CLASS;
\r
143 int priority_constant_to_index(unsigned long constant) {
\r
144 switch (constant & priority_mask()) {
\r
145 case REALTIME_PRIORITY_CLASS: return NSSM_REALTIME_PRIORITY;
\r
146 case HIGH_PRIORITY_CLASS: return NSSM_HIGH_PRIORITY;
\r
147 case ABOVE_NORMAL_PRIORITY_CLASS: return NSSM_ABOVE_NORMAL_PRIORITY;
\r
148 case BELOW_NORMAL_PRIORITY_CLASS: return NSSM_BELOW_NORMAL_PRIORITY;
\r
149 case IDLE_PRIORITY_CLASS: return NSSM_IDLE_PRIORITY;
\r
151 return NSSM_NORMAL_PRIORITY;
\r
154 unsigned long priority_index_to_constant(int index) {
\r
156 case NSSM_REALTIME_PRIORITY: return REALTIME_PRIORITY_CLASS;
\r
157 case NSSM_HIGH_PRIORITY: return HIGH_PRIORITY_CLASS;
\r
158 case NSSM_ABOVE_NORMAL_PRIORITY: return ABOVE_NORMAL_PRIORITY_CLASS;
\r
159 case NSSM_BELOW_NORMAL_PRIORITY: return BELOW_NORMAL_PRIORITY_CLASS;
\r
160 case NSSM_IDLE_PRIORITY: return IDLE_PRIORITY_CLASS;
\r
162 return NORMAL_PRIORITY_CLASS;
\r
165 static inline unsigned long throttle_milliseconds(unsigned long throttle) {
\r
166 /* pow() operates on doubles. */
\r
167 unsigned long ret = 1; for (unsigned long i = 1; i < throttle; i++) ret *= 2;
\r
172 Wrapper to be called in a new thread so that we can acknowledge a STOP
\r
173 control immediately.
\r
175 static unsigned long WINAPI shutdown_service(void *arg) {
\r
176 return stop_service((nssm_service_t *) arg, 0, true, true);
\r
179 /* Connect to the service manager */
\r
180 SC_HANDLE open_service_manager() {
\r
181 SC_HANDLE ret = OpenSCManager(0, SERVICES_ACTIVE_DATABASE, SC_MANAGER_ALL_ACCESS);
\r
183 if (is_admin) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENSCMANAGER_FAILED, 0);
\r
190 /* Open a service by name or display name. */
\r
191 SC_HANDLE open_service(SC_HANDLE services, TCHAR *service_name, TCHAR *canonical_name, unsigned long canonical_namelen) {
\r
192 SC_HANDLE service_handle = OpenService(services, service_name, SERVICE_ALL_ACCESS);
\r
193 if (service_handle) {
\r
194 if (canonical_name && canonical_name != service_name) {
\r
195 if (_sntprintf_s(canonical_name, canonical_namelen, _TRUNCATE, _T("%s"), service_name) < 0) {
\r
196 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("canonical_name"), _T("open_service()"));
\r
200 return service_handle;
\r
203 unsigned long error = GetLastError();
\r
204 if (error != ERROR_SERVICE_DOES_NOT_EXIST) {
\r
205 print_message(stderr, NSSM_MESSAGE_OPENSERVICE_FAILED, error_string(GetLastError()));
\r
209 /* We can't look for a display name because there's no buffer to store it. */
\r
210 if (! canonical_name) {
\r
211 print_message(stderr, NSSM_MESSAGE_OPENSERVICE_FAILED, error_string(GetLastError()));
\r
215 unsigned long bufsize, required, count, i;
\r
216 unsigned long resume = 0;
\r
217 EnumServicesStatus(services, SERVICE_DRIVER | SERVICE_FILE_SYSTEM_DRIVER | SERVICE_KERNEL_DRIVER | SERVICE_WIN32, SERVICE_STATE_ALL, 0, 0, &required, &count, &resume);
\r
218 error = GetLastError();
\r
219 if (error != ERROR_MORE_DATA) {
\r
220 print_message(stderr, NSSM_MESSAGE_ENUMSERVICESSTATUS_FAILED, error_string(GetLastError()));
\r
224 ENUM_SERVICE_STATUS *status = (ENUM_SERVICE_STATUS *) HeapAlloc(GetProcessHeap(), 0, required);
\r
226 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("ENUM_SERVICE_STATUS"), _T("open_service()"));
\r
230 bufsize = required;
\r
233 EnumServicesStatus() returns:
\r
234 1 when it retrieved data and there's no more data to come.
\r
235 0 and sets last error to ERROR_MORE_DATA when it retrieved data and
\r
236 there's more data to come.
\r
237 0 and sets last error to something else on error.
\r
239 int ret = EnumServicesStatus(services, SERVICE_DRIVER | SERVICE_FILE_SYSTEM_DRIVER | SERVICE_KERNEL_DRIVER | SERVICE_WIN32, SERVICE_STATE_ALL, status, bufsize, &required, &count, &resume);
\r
241 error = GetLastError();
\r
242 if (error != ERROR_MORE_DATA) {
\r
243 HeapFree(GetProcessHeap(), 0, status);
\r
244 print_message(stderr, NSSM_MESSAGE_ENUMSERVICESSTATUS_FAILED, error_string(GetLastError()));
\r
249 for (i = 0; i < count; i++) {
\r
250 if (str_equiv(status[i].lpDisplayName, service_name)) {
\r
251 if (_sntprintf_s(canonical_name, canonical_namelen, _TRUNCATE, _T("%s"), status[i].lpServiceName) < 0) {
\r
252 HeapFree(GetProcessHeap(), 0, status);
\r
253 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("canonical_name"), _T("open_service()"));
\r
257 HeapFree(GetProcessHeap(), 0, status);
\r
258 return open_service(services, canonical_name, 0, 0);
\r
265 /* Recurse so we can get an error message. */
\r
266 return open_service(services, service_name, 0, 0);
\r
269 QUERY_SERVICE_CONFIG *query_service_config(const TCHAR *service_name, SC_HANDLE service_handle) {
\r
270 QUERY_SERVICE_CONFIG *qsc;
\r
271 unsigned long bufsize;
\r
272 unsigned long error;
\r
274 QueryServiceConfig(service_handle, 0, 0, &bufsize);
\r
275 error = GetLastError();
\r
276 if (error == ERROR_INSUFFICIENT_BUFFER) {
\r
277 qsc = (QUERY_SERVICE_CONFIG *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, bufsize);
\r
279 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("QUERY_SERVICE_CONFIG"), _T("query_service_config()"), 0);
\r
284 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG_FAILED, service_name, error_string(error), 0);
\r
288 if (! QueryServiceConfig(service_handle, qsc, bufsize, &bufsize)) {
\r
289 HeapFree(GetProcessHeap(), 0, qsc);
\r
290 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG_FAILED, service_name, error_string(GetLastError()), 0);
\r
297 int set_service_description(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR *buffer) {
\r
298 SERVICE_DESCRIPTION description;
\r
299 ZeroMemory(&description, sizeof(description));
\r
301 lpDescription must be NULL if we aren't changing, the new description
\r
304 if (buffer && buffer[0]) description.lpDescription = buffer;
\r
305 else description.lpDescription = _T("");
\r
307 if (ChangeServiceConfig2(service_handle, SERVICE_CONFIG_DESCRIPTION, &description)) return 0;
\r
309 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_DESCRIPTION_FAILED, service_name, error_string(GetLastError()), 0);
\r
313 int get_service_description(const TCHAR *service_name, SC_HANDLE service_handle, unsigned long len, TCHAR *buffer) {
\r
314 if (! buffer) return 1;
\r
316 unsigned long bufsize;
\r
317 QueryServiceConfig2(service_handle, SERVICE_CONFIG_DESCRIPTION, 0, 0, &bufsize);
\r
318 unsigned long error = GetLastError();
\r
319 if (error == ERROR_INSUFFICIENT_BUFFER) {
\r
320 SERVICE_DESCRIPTION *description = (SERVICE_DESCRIPTION *) HeapAlloc(GetProcessHeap(), 0, bufsize);
\r
321 if (! description) {
\r
322 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("SERVICE_CONFIG_DESCRIPTION"), _T("get_service_description()"));
\r
326 if (QueryServiceConfig2(service_handle, SERVICE_CONFIG_DESCRIPTION, (unsigned char *) description, bufsize, &bufsize)) {
\r
327 if (description->lpDescription) _sntprintf_s(buffer, len, _TRUNCATE, _T("%s"), description->lpDescription);
\r
328 else ZeroMemory(buffer, len * sizeof(TCHAR));
\r
329 HeapFree(GetProcessHeap(), 0, description);
\r
333 HeapFree(GetProcessHeap(), 0, description);
\r
334 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_CONFIG_DESCRIPTION"), error_string(error));
\r
339 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_CONFIG_DESCRIPTION"), error_string(error));
\r
346 int get_service_startup(const TCHAR *service_name, SC_HANDLE service_handle, const QUERY_SERVICE_CONFIG *qsc, unsigned long *startup) {
\r
347 if (! qsc) return 1;
\r
349 switch (qsc->dwStartType) {
\r
350 case SERVICE_DEMAND_START: *startup = NSSM_STARTUP_MANUAL; break;
\r
351 case SERVICE_DISABLED: *startup = NSSM_STARTUP_DISABLED; break;
\r
352 default: *startup = NSSM_STARTUP_AUTOMATIC;
\r
355 if (*startup != NSSM_STARTUP_AUTOMATIC) return 0;
\r
357 /* Check for delayed start. */
\r
358 unsigned long bufsize;
\r
359 unsigned long error;
\r
360 QueryServiceConfig2(service_handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, 0, 0, &bufsize);
\r
361 error = GetLastError();
\r
362 if (error == ERROR_INSUFFICIENT_BUFFER) {
\r
363 SERVICE_DELAYED_AUTO_START_INFO *info = (SERVICE_DELAYED_AUTO_START_INFO *) HeapAlloc(GetProcessHeap(), 0, bufsize);
\r
365 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("SERVICE_DELAYED_AUTO_START_INFO"), _T("get_service_startup()"));
\r
369 if (QueryServiceConfig2(service_handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, (unsigned char *) info, bufsize, &bufsize)) {
\r
370 if (info->fDelayedAutostart) *startup = NSSM_STARTUP_DELAYED;
\r
371 HeapFree(GetProcessHeap(), 0, info);
\r
375 error = GetLastError();
\r
376 if (error != ERROR_INVALID_LEVEL) {
\r
377 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_CONFIG_DELAYED_AUTO_START_INFO"), error_string(error));
\r
382 else if (error != ERROR_INVALID_LEVEL) {
\r
383 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_DELAYED_AUTO_START_INFO"), error_string(error));
\r
390 int get_service_username(const TCHAR *service_name, const QUERY_SERVICE_CONFIG *qsc, TCHAR **username, size_t *usernamelen) {
\r
391 if (! username) return 1;
\r
392 if (! usernamelen) return 1;
\r
397 if (! qsc) return 1;
\r
399 if (str_equiv(qsc->lpServiceStartName, NSSM_LOCALSYSTEM_ACCOUNT)) return 0;
\r
401 size_t len = _tcslen(qsc->lpServiceStartName);
\r
402 *username = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(TCHAR));
\r
404 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("username"), _T("get_service_username()"));
\r
408 memmove(*username, qsc->lpServiceStartName, (len + 1) * sizeof(TCHAR));
\r
409 *usernamelen = len;
\r
414 int grant_logon_as_service(const TCHAR *username) {
\r
415 if (! username) return 0;
\r
416 if (str_equiv(username, NSSM_LOCALSYSTEM_ACCOUNT)) return 0;
\r
418 /* Open Policy object. */
\r
419 LSA_OBJECT_ATTRIBUTES attributes;
\r
420 ZeroMemory(&attributes, sizeof(attributes));
\r
424 NTSTATUS status = LsaOpenPolicy(0, &attributes, POLICY_ALL_ACCESS, &policy);
\r
426 print_message(stderr, NSSM_MESSAGE_LSAOPENPOLICY_FAILED, error_string(LsaNtStatusToWinError(status)));
\r
430 /* Look up SID for the account. */
\r
431 LSA_UNICODE_STRING lsa_username;
\r
433 lsa_username.Buffer = (wchar_t *) username;
\r
434 lsa_username.Length = (unsigned short) _tcslen(username) * sizeof(TCHAR);
\r
435 lsa_username.MaximumLength = lsa_username.Length + sizeof(TCHAR);
\r
438 mbstowcs_s(&buflen, NULL, 0, username, _TRUNCATE);
\r
439 lsa_username.MaximumLength = (unsigned short) buflen * sizeof(wchar_t);
\r
440 lsa_username.Length = lsa_username.MaximumLength - sizeof(wchar_t);
\r
441 lsa_username.Buffer = (wchar_t *) HeapAlloc(GetProcessHeap(), 0, lsa_username.MaximumLength);
\r
442 if (lsa_username.Buffer) mbstowcs_s(&buflen, lsa_username.Buffer, lsa_username.MaximumLength, username, _TRUNCATE);
\r
445 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("LSA_UNICODE_STRING"), _T("grant_logon_as_service()"));
\r
450 LSA_REFERENCED_DOMAIN_LIST *translated_domains;
\r
451 LSA_TRANSLATED_SID *translated_sid;
\r
452 status = LsaLookupNames(policy, 1, &lsa_username, &translated_domains, &translated_sid);
\r
454 HeapFree(GetProcessHeap(), 0, lsa_username.Buffer);
\r
457 LsaFreeMemory(translated_domains);
\r
458 LsaFreeMemory(translated_sid);
\r
460 print_message(stderr, NSSM_MESSAGE_LSALOOKUPNAMES_FAILED, username, error_string(LsaNtStatusToWinError(status)));
\r
464 if (translated_sid->Use != SidTypeUser) {
\r
465 LsaFreeMemory(translated_domains);
\r
466 LsaFreeMemory(translated_sid);
\r
468 print_message(stderr, NSSM_GUI_INVALID_USERNAME, username);
\r
472 LSA_TRUST_INFORMATION *trust = &translated_domains->Domains[translated_sid->DomainIndex];
\r
473 if (! trust || ! IsValidSid(trust->Sid)) {
\r
474 LsaFreeMemory(translated_domains);
\r
475 LsaFreeMemory(translated_sid);
\r
477 print_message(stderr, NSSM_GUI_INVALID_USERNAME, username);
\r
481 /* GetSidSubAuthority*() return pointers! */
\r
482 unsigned char *n = GetSidSubAuthorityCount(trust->Sid);
\r
484 /* Convert translated SID to SID. */
\r
485 SID *sid = (SID *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, GetSidLengthRequired(*n + 1));
\r
487 LsaFreeMemory(translated_domains);
\r
488 LsaFreeMemory(translated_sid);
\r
490 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("SID"), _T("grant_logon_as_service"));
\r
494 unsigned long error;
\r
495 if (! InitializeSid(sid, GetSidIdentifierAuthority(trust->Sid), *n + 1)) {
\r
496 error = GetLastError();
\r
497 HeapFree(GetProcessHeap(), 0, sid);
\r
498 LsaFreeMemory(translated_domains);
\r
499 LsaFreeMemory(translated_sid);
\r
501 print_message(stderr, NSSM_MESSAGE_INITIALIZESID_FAILED, username, error_string(error));
\r
505 for (unsigned char i = 0; i <= *n; i++) {
\r
506 unsigned long *sub = GetSidSubAuthority(sid, i);
\r
507 if (i < *n) *sub = *GetSidSubAuthority(trust->Sid, i);
\r
508 else *sub = translated_sid->RelativeId;
\r
511 LsaFreeMemory(translated_domains);
\r
512 LsaFreeMemory(translated_sid);
\r
514 /* Check if the SID has the "Log on as a service" right. */
\r
515 LSA_UNICODE_STRING lsa_right;
\r
516 lsa_right.Buffer = NSSM_LOGON_AS_SERVICE_RIGHT;
\r
517 lsa_right.Length = (unsigned short) wcslen(lsa_right.Buffer) * sizeof(wchar_t);
\r
518 lsa_right.MaximumLength = lsa_right.Length + sizeof(wchar_t);
\r
520 LSA_UNICODE_STRING *rights;
\r
521 unsigned long count = ~0;
\r
522 status = LsaEnumerateAccountRights(policy, sid, &rights, &count);
\r
525 If the account has no rights set LsaEnumerateAccountRights() will return
\r
526 STATUS_OBJECT_NAME_NOT_FOUND and set count to 0.
\r
528 error = LsaNtStatusToWinError(status);
\r
529 if (error != ERROR_FILE_NOT_FOUND) {
\r
530 HeapFree(GetProcessHeap(), 0, sid);
\r
532 print_message(stderr, NSSM_MESSAGE_LSAENUMERATEACCOUNTRIGHTS_FAILED, username, error_string(error));
\r
537 for (unsigned long i = 0; i < count; i++) {
\r
538 if (rights[i].Length != lsa_right.Length) continue;
\r
539 if (_wcsnicmp(rights[i].Buffer, lsa_right.Buffer, lsa_right.MaximumLength)) continue;
\r
540 /* The SID has the right. */
\r
541 HeapFree(GetProcessHeap(), 0, sid);
\r
542 LsaFreeMemory(rights);
\r
546 LsaFreeMemory(rights);
\r
548 /* Add the right. */
\r
549 status = LsaAddAccountRights(policy, sid, &lsa_right, 1);
\r
550 HeapFree(GetProcessHeap(), 0, sid);
\r
553 print_message(stderr, NSSM_MESSAGE_LSAADDACCOUNTRIGHTS_FAILED, error_string(LsaNtStatusToWinError(status)));
\r
557 print_message(stdout, NSSM_MESSAGE_GRANTED_LOGON_AS_SERVICE, username);
\r
561 /* Set default values which aren't zero. */
\r
562 void set_nssm_service_defaults(nssm_service_t *service) {
\r
563 if (! service) return;
\r
565 service->type = SERVICE_WIN32_OWN_PROCESS;
\r
566 service->priority = NORMAL_PRIORITY_CLASS;
\r
567 service->stdin_sharing = NSSM_STDIN_SHARING;
\r
568 service->stdin_disposition = NSSM_STDIN_DISPOSITION;
\r
569 service->stdin_flags = NSSM_STDIN_FLAGS;
\r
570 service->stdout_sharing = NSSM_STDOUT_SHARING;
\r
571 service->stdout_disposition = NSSM_STDOUT_DISPOSITION;
\r
572 service->stdout_flags = NSSM_STDOUT_FLAGS;
\r
573 service->stderr_sharing = NSSM_STDERR_SHARING;
\r
574 service->stderr_disposition = NSSM_STDERR_DISPOSITION;
\r
575 service->stderr_flags = NSSM_STDERR_FLAGS;
\r
576 service->throttle_delay = NSSM_RESET_THROTTLE_RESTART;
\r
577 service->stop_method = ~0;
\r
578 service->kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;
\r
579 service->kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;
\r
580 service->kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;
\r
583 /* Allocate and zero memory for a service. */
\r
584 nssm_service_t *alloc_nssm_service() {
\r
585 nssm_service_t *service = (nssm_service_t *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(nssm_service_t));
\r
586 if (! service) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("service"), _T("alloc_nssm_service()"), 0);
\r
590 /* Free memory for a service. */
\r
591 void cleanup_nssm_service(nssm_service_t *service) {
\r
592 if (! service) return;
\r
593 if (service->username) HeapFree(GetProcessHeap(), 0, service->username);
\r
594 if (service->password) {
\r
595 SecureZeroMemory(service->password, service->passwordlen);
\r
596 HeapFree(GetProcessHeap(), 0, service->password);
\r
598 if (service->env) HeapFree(GetProcessHeap(), 0, service->env);
\r
599 if (service->env_extra) HeapFree(GetProcessHeap(), 0, service->env_extra);
\r
600 if (service->handle) CloseServiceHandle(service->handle);
\r
601 if (service->process_handle) CloseHandle(service->process_handle);
\r
602 if (service->wait_handle) UnregisterWait(service->process_handle);
\r
603 if (service->throttle_section_initialised) DeleteCriticalSection(&service->throttle_section);
\r
604 if (service->throttle_timer) CloseHandle(service->throttle_timer);
\r
605 HeapFree(GetProcessHeap(), 0, service);
\r
608 /* About to install the service */
\r
609 int pre_install_service(int argc, TCHAR **argv) {
\r
610 nssm_service_t *service = alloc_nssm_service();
\r
611 set_nssm_service_defaults(service);
\r
612 if (argc) _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]);
\r
614 /* Show the dialogue box if we didn't give the service name and path */
\r
615 if (argc < 2) return nssm_gui(IDD_INSTALL, service);
\r
618 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("service"), _T("pre_install_service()"));
\r
621 _sntprintf_s(service->exe, _countof(service->exe), _TRUNCATE, _T("%s"), argv[1]);
\r
623 /* Arguments are optional */
\r
624 size_t flagslen = 0;
\r
627 for (i = 2; i < argc; i++) flagslen += _tcslen(argv[i]) + 1;
\r
628 if (! flagslen) flagslen = 1;
\r
629 if (flagslen > _countof(service->flags)) {
\r
630 print_message(stderr, NSSM_MESSAGE_FLAGS_TOO_LONG);
\r
634 for (i = 2; i < argc; i++) {
\r
635 size_t len = _tcslen(argv[i]);
\r
636 memmove(service->flags + s, argv[i], len * sizeof(TCHAR));
\r
638 if (i < argc - 1) service->flags[s++] = _T(' ');
\r
641 /* Work out directory name */
\r
642 _sntprintf_s(service->dir, _countof(service->dir), _TRUNCATE, _T("%s"), service->exe);
\r
643 strip_basename(service->dir);
\r
645 int ret = install_service(service);
\r
646 cleanup_nssm_service(service);
\r
650 /* About to edit the service. */
\r
651 int pre_edit_service(int argc, TCHAR **argv) {
\r
652 /* Require service name. */
\r
653 if (argc < 2) return usage(1);
\r
655 /* Are we editing on the command line? */
\r
656 enum { MODE_EDITING, MODE_GETTING, MODE_SETTING, MODE_RESETTING } mode = MODE_EDITING;
\r
657 const TCHAR *verb = argv[0];
\r
658 const TCHAR *service_name = argv[1];
\r
659 bool getting = false;
\r
660 bool unsetting = false;
\r
662 /* Minimum number of arguments. */
\r
664 /* Index of first value. */
\r
667 if (str_equiv(verb, _T("get"))) {
\r
669 mode = MODE_GETTING;
\r
671 else if (str_equiv(verb, _T("set"))) {
\r
673 mode = MODE_SETTING;
\r
675 else if (str_equiv(verb, _T("reset")) || str_equiv(verb, _T("unset"))) {
\r
677 mode = MODE_RESETTING;
\r
679 if (argc < mandatory) return usage(1);
\r
681 const TCHAR *parameter = 0;
\r
682 settings_t *setting = 0;
\r
685 /* Validate the parameter. */
\r
686 if (mandatory > 2) {
\r
687 bool additional_mandatory = false;
\r
689 parameter = argv[2];
\r
690 for (i = 0; settings[i].name; i++) {
\r
691 setting = &settings[i];
\r
692 if (! str_equiv(setting->name, parameter)) continue;
\r
693 if (((setting->additional & ADDITIONAL_GETTING) && mode == MODE_GETTING) || ((setting->additional & ADDITIONAL_SETTING) && mode == MODE_SETTING) || ((setting->additional & ADDITIONAL_RESETTING) && mode == MODE_RESETTING)) {
\r
694 additional_mandatory = true;
\r
699 if (! settings[i].name) {
\r
700 print_message(stderr, NSSM_MESSAGE_INVALID_PARAMETER, parameter);
\r
701 for (i = 0; settings[i].name; i++) _ftprintf(stderr, _T("%s\n"), settings[i].name);
\r
704 if (argc < mandatory) return usage(1);
\r
707 if (additional_mandatory) {
\r
708 additional = argv[3];
\r
711 else additional = argv[remainder];
\r
714 nssm_service_t *service = alloc_nssm_service();
\r
715 _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), service_name);
\r
717 /* Open service manager */
\r
718 SC_HANDLE services = open_service_manager();
\r
720 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
724 /* Try to open the service */
\r
725 service->handle = open_service(services, service->name, service->name, _countof(service->name));
\r
726 if (! service->handle) {
\r
727 CloseServiceHandle(services);
\r
731 /* Get system details. */
\r
732 QUERY_SERVICE_CONFIG *qsc = query_service_config(service->name, service->handle);
\r
734 CloseHandle(service->handle);
\r
735 CloseServiceHandle(services);
\r
739 service->type = qsc->dwServiceType;
\r
740 if (! (service->type & SERVICE_WIN32_OWN_PROCESS)) {
\r
741 if (mode != MODE_GETTING) {
\r
742 HeapFree(GetProcessHeap(), 0, qsc);
\r
743 CloseHandle(service->handle);
\r
744 CloseServiceHandle(services);
\r
745 print_message(stderr, NSSM_MESSAGE_CANNOT_EDIT, service->name, NSSM_WIN32_OWN_PROCESS, 0);
\r
750 if (get_service_startup(service->name, service->handle, qsc, &service->startup)) {
\r
751 if (mode != MODE_GETTING) {
\r
752 HeapFree(GetProcessHeap(), 0, qsc);
\r
753 CloseHandle(service->handle);
\r
754 CloseServiceHandle(services);
\r
759 if (get_service_username(service->name, qsc, &service->username, &service->usernamelen)) {
\r
760 if (mode != MODE_GETTING) {
\r
761 HeapFree(GetProcessHeap(), 0, qsc);
\r
762 CloseHandle(service->handle);
\r
763 CloseServiceHandle(services);
\r
768 _sntprintf_s(service->displayname, _countof(service->displayname), _TRUNCATE, _T("%s"), qsc->lpDisplayName);
\r
770 /* Get the canonical service name. We open it case insensitively. */
\r
771 unsigned long bufsize = _countof(service->name);
\r
772 GetServiceKeyName(services, service->displayname, service->name, &bufsize);
\r
774 /* Remember the executable in case it isn't NSSM. */
\r
775 _sntprintf_s(service->image, _countof(service->image), _TRUNCATE, _T("%s"), qsc->lpBinaryPathName);
\r
776 HeapFree(GetProcessHeap(), 0, qsc);
\r
778 /* Get extended system details. */
\r
779 if (get_service_description(service->name, service->handle, _countof(service->description), service->description)) {
\r
780 if (mode != MODE_GETTING) {
\r
781 CloseHandle(service->handle);
\r
782 CloseServiceHandle(services);
\r
787 /* Get NSSM details. */
\r
788 get_parameters(service, 0);
\r
790 CloseServiceHandle(services);
\r
792 if (! service->exe[0]) {
\r
793 service->native = true;
\r
794 if (mode != MODE_GETTING) print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE, service->name, NSSM, service->image);
\r
797 /* Editing with the GUI. */
\r
798 if (mode == MODE_EDITING) {
\r
799 nssm_gui(IDD_EDIT, service);
\r
803 /* Trying to manage App* parameters for a non-NSSM service. */
\r
804 if (! setting->native && service->native) {
\r
805 CloseHandle(service->handle);
\r
806 print_message(stderr, NSSM_MESSAGE_NATIVE_PARAMETER, setting->name, NSSM);
\r
814 if (mode == MODE_GETTING) {
\r
815 if (! service->native) {
\r
816 key = open_registry(service->name, KEY_READ);
\r
817 if (! key) return 4;
\r
820 if (setting->native) ret = get_setting(service->name, service->handle, setting, &value, additional);
\r
821 else ret = get_setting(service->name, key, setting, &value, additional);
\r
823 CloseHandle(service->handle);
\r
827 switch (setting->type) {
\r
828 case REG_EXPAND_SZ:
\r
831 _tprintf(_T("%s\n"), value.string ? value.string : _T(""));
\r
832 HeapFree(GetProcessHeap(), 0, value.string);
\r
836 _tprintf(_T("%u\n"), value.numeric);
\r
840 if (! service->native) RegCloseKey(key);
\r
841 CloseHandle(service->handle);
\r
845 /* Build the value. */
\r
846 if (mode == MODE_RESETTING) {
\r
847 /* Unset the parameter. */
\r
851 /* Set the parameter. */
\r
853 size_t delimiterlen = (setting->additional & ADDITIONAL_CRLF) ? 2 : 1;
\r
854 for (i = remainder; i < argc; i++) len += _tcslen(argv[i]) + delimiterlen;
\r
857 value.string = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, len * sizeof(TCHAR));
\r
858 if (! value.string) {
\r
859 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("value"), _T("edit_service()"));
\r
860 CloseHandle(service->handle);
\r
865 for (i = remainder; i < argc; i++) {
\r
866 size_t len = _tcslen(argv[i]);
\r
867 memmove(value.string + s, argv[i], len * sizeof(TCHAR));
\r
869 if (i < argc - 1) {
\r
870 if (setting->additional & ADDITIONAL_CRLF) {
\r
871 value.string[s++] = _T('\r');
\r
872 value.string[s++] = _T('\n');
\r
874 else value.string[s++] = _T(' ');
\r
877 value.string[s] = _T('\0');
\r
880 if (! service->native) {
\r
881 key = open_registry(service->name, KEY_WRITE);
\r
883 if (value.string) HeapFree(GetProcessHeap(), 0, value.string);
\r
888 if (setting->native) ret = set_setting(service->name, service->handle, setting, &value, additional);
\r
889 else ret = set_setting(service->name, key, setting, &value, additional);
\r
890 if (value.string) HeapFree(GetProcessHeap(), 0, value.string);
\r
892 if (! service->native) RegCloseKey(key);
\r
893 CloseHandle(service->handle);
\r
897 if (! service->native) RegCloseKey(key);
\r
898 CloseHandle(service->handle);
\r
903 /* About to remove the service */
\r
904 int pre_remove_service(int argc, TCHAR **argv) {
\r
905 nssm_service_t *service = alloc_nssm_service();
\r
906 set_nssm_service_defaults(service);
\r
907 if (argc) _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]);
\r
909 /* Show dialogue box if we didn't pass service name and "confirm" */
\r
910 if (argc < 2) return nssm_gui(IDD_REMOVE, service);
\r
911 if (str_equiv(argv[1], _T("confirm"))) {
\r
912 int ret = remove_service(service);
\r
913 cleanup_nssm_service(service);
\r
916 print_message(stderr, NSSM_MESSAGE_PRE_REMOVE_SERVICE);
\r
920 /* Install the service */
\r
921 int install_service(nssm_service_t *service) {
\r
922 if (! service) return 1;
\r
924 /* Open service manager */
\r
925 SC_HANDLE services = open_service_manager();
\r
927 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
928 cleanup_nssm_service(service);
\r
932 /* Get path of this program */
\r
933 GetModuleFileName(0, service->image, _countof(service->image));
\r
935 /* Create the service - settings will be changed in edit_service() */
\r
936 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
937 if (! service->handle) {
\r
938 print_message(stderr, NSSM_MESSAGE_CREATESERVICE_FAILED, error_string(GetLastError()));
\r
939 CloseServiceHandle(services);
\r
943 if (edit_service(service, false)) {
\r
944 DeleteService(service->handle);
\r
945 CloseServiceHandle(services);
\r
949 print_message(stdout, NSSM_MESSAGE_SERVICE_INSTALLED, service->name);
\r
952 CloseServiceHandle(services);
\r
957 /* Edit the service. */
\r
958 int edit_service(nssm_service_t *service, bool editing) {
\r
959 if (! service) return 1;
\r
962 The only two valid flags for service type are SERVICE_WIN32_OWN_PROCESS
\r
963 and SERVICE_INTERACTIVE_PROCESS.
\r
965 service->type &= SERVICE_INTERACTIVE_PROCESS;
\r
966 service->type |= SERVICE_WIN32_OWN_PROCESS;
\r
968 /* Startup type. */
\r
969 unsigned long startup;
\r
970 switch (service->startup) {
\r
971 case NSSM_STARTUP_MANUAL: startup = SERVICE_DEMAND_START; break;
\r
972 case NSSM_STARTUP_DISABLED: startup = SERVICE_DISABLED; break;
\r
973 default: startup = SERVICE_AUTO_START;
\r
976 /* Display name. */
\r
977 if (! service->displayname[0]) _sntprintf_s(service->displayname, _countof(service->displayname), _TRUNCATE, _T("%s"), service->name);
\r
980 Username must be NULL if we aren't changing or an account name.
\r
981 We must explicitly use LOCALSYSTEM to change it when we are editing.
\r
982 Password must be NULL if we aren't changing, a password or "".
\r
983 Empty passwords are valid but we won't allow them in the GUI.
\r
985 TCHAR *username = 0;
\r
986 TCHAR *password = 0;
\r
987 if (service->usernamelen) {
\r
988 username = service->username;
\r
989 if (service->passwordlen) password = service->password;
\r
990 else password = _T("");
\r
992 else if (editing) username = NSSM_LOCALSYSTEM_ACCOUNT;
\r
994 if (grant_logon_as_service(username)) {
\r
995 print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username);
\r
999 if (! ChangeServiceConfig(service->handle, service->type, startup, SERVICE_NO_CHANGE, 0, 0, 0, 0, username, password, service->displayname)) {
\r
1000 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
1004 if (service->description[0] || editing) {
\r
1005 set_service_description(service->name, service->handle, service->description);
\r
1008 SERVICE_DELAYED_AUTO_START_INFO delayed;
\r
1009 ZeroMemory(&delayed, sizeof(delayed));
\r
1010 if (service->startup == NSSM_STARTUP_DELAYED) delayed.fDelayedAutostart = 1;
\r
1011 else delayed.fDelayedAutostart = 0;
\r
1012 /* Delayed startup isn't supported until Vista. */
\r
1013 if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, &delayed)) {
\r
1014 unsigned long error = GetLastError();
\r
1015 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
\r
1016 if (error != ERROR_INVALID_LEVEL) {
\r
1017 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_DELAYED_AUTO_START_INFO_FAILED, service->name, error_string(error), 0);
\r
1021 /* Don't mess with parameters which aren't ours. */
\r
1022 if (! service->native) {
\r
1023 /* Now we need to put the parameters into the registry */
\r
1024 if (create_parameters(service, editing)) {
\r
1025 print_message(stderr, NSSM_MESSAGE_CREATE_PARAMETERS_FAILED);
\r
1029 set_service_recovery(service);
\r
1035 /* Control a service. */
\r
1036 int control_service(unsigned long control, int argc, TCHAR **argv) {
\r
1037 if (argc < 1) return usage(1);
\r
1038 TCHAR *service_name = argv[0];
\r
1039 TCHAR canonical_name[SERVICE_NAME_LENGTH];
\r
1041 SC_HANDLE services = open_service_manager();
\r
1043 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
1047 SC_HANDLE service_handle = open_service(services, service_name, canonical_name, _countof(canonical_name));
\r
1048 if (! service_handle) {
\r
1049 CloseServiceHandle(services);
\r
1054 unsigned long error;
\r
1055 SERVICE_STATUS service_status;
\r
1056 if (control == 0) {
\r
1057 ret = StartService(service_handle, (unsigned long) argc, (const TCHAR **) argv);
\r
1058 error = GetLastError();
\r
1059 CloseHandle(service_handle);
\r
1060 CloseServiceHandle(services);
\r
1062 if (error == ERROR_IO_PENDING) {
\r
1064 Older versions of Windows return immediately with ERROR_IO_PENDING
\r
1065 indicate that the operation is still in progress. Newer versions
\r
1066 will return it if there really is a delay. As far as we're
\r
1067 concerned the operation is a success. We don't claim to offer a
\r
1068 fully-feature service control method; it's just a quick 'n' dirty
\r
1071 In the future we may identify and handle this situation properly.
\r
1074 error = ERROR_SUCCESS;
\r
1078 _tprintf(_T("%s: %s"), canonical_name, error_string(error));
\r
1082 _ftprintf(stderr, _T("%s: %s"), canonical_name, error_string(error));
\r
1086 else if (control == SERVICE_CONTROL_INTERROGATE) {
\r
1088 We could actually send an INTERROGATE control but that won't return
\r
1089 any information if the service is stopped and we don't care about
\r
1090 the extra details it might give us in any case. So we'll fake it.
\r
1092 ret = QueryServiceStatus(service_handle, &service_status);
\r
1093 error = GetLastError();
\r
1096 switch (service_status.dwCurrentState) {
\r
1097 case SERVICE_STOPPED: _tprintf(_T("SERVICE_STOPPED\n")); break;
\r
1098 case SERVICE_START_PENDING: _tprintf(_T("SERVICE_START_PENDING\n")); break;
\r
1099 case SERVICE_STOP_PENDING: _tprintf(_T("SERVICE_STOP_PENDING\n")); break;
\r
1100 case SERVICE_RUNNING: _tprintf(_T("SERVICE_RUNNING\n")); break;
\r
1101 case SERVICE_CONTINUE_PENDING: _tprintf(_T("SERVICE_CONTINUE_PENDING\n")); break;
\r
1102 case SERVICE_PAUSE_PENDING: _tprintf(_T("SERVICE_PAUSE_PENDING\n")); break;
\r
1103 case SERVICE_PAUSED: _tprintf(_T("SERVICE_PAUSED\n")); break;
\r
1104 default: _tprintf(_T("?\n")); return 1;
\r
1109 _ftprintf(stderr, _T("%s: %s\n"), canonical_name, error_string(error));
\r
1114 ret = ControlService(service_handle, control, &service_status);
\r
1115 error = GetLastError();
\r
1116 CloseHandle(service_handle);
\r
1117 CloseServiceHandle(services);
\r
1119 if (error == ERROR_IO_PENDING) {
\r
1121 error = ERROR_SUCCESS;
\r
1125 _tprintf(_T("%s: %s"), canonical_name, error_string(error));
\r
1129 _ftprintf(stderr, _T("%s: %s"), canonical_name, error_string(error));
\r
1135 /* Remove the service */
\r
1136 int remove_service(nssm_service_t *service) {
\r
1137 if (! service) return 1;
\r
1139 /* Open service manager */
\r
1140 SC_HANDLE services = open_service_manager();
\r
1142 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
1146 /* Try to open the service */
\r
1147 service->handle = open_service(services, service->name, service->name, _countof(service->name));
\r
1148 if (! service->handle) {
\r
1149 CloseServiceHandle(services);
\r
1153 /* Get the canonical service name. We open it case insensitively. */
\r
1154 unsigned long bufsize = _countof(service->displayname);
\r
1155 GetServiceDisplayName(services, service->name, service->displayname, &bufsize);
\r
1156 bufsize = _countof(service->name);
\r
1157 GetServiceKeyName(services, service->displayname, service->name, &bufsize);
\r
1159 /* Try to delete the service */
\r
1160 if (! DeleteService(service->handle)) {
\r
1161 print_message(stderr, NSSM_MESSAGE_DELETESERVICE_FAILED);
\r
1162 CloseServiceHandle(services);
\r
1167 CloseServiceHandle(services);
\r
1169 print_message(stdout, NSSM_MESSAGE_SERVICE_REMOVED, service->name);
\r
1173 /* Service initialisation */
\r
1174 void WINAPI service_main(unsigned long argc, TCHAR **argv) {
\r
1175 nssm_service_t *service = alloc_nssm_service();
\r
1176 if (! service) return;
\r
1178 if (_sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]) < 0) {
\r
1179 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("service->name"), _T("service_main()"), 0);
\r
1183 /* We can use a condition variable in a critical section on Vista or later. */
\r
1184 if (imports.SleepConditionVariableCS && imports.WakeConditionVariable) use_critical_section = true;
\r
1185 else use_critical_section = false;
\r
1187 /* Initialise status */
\r
1188 ZeroMemory(&service->status, sizeof(service->status));
\r
1189 service->status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;
\r
1190 service->status.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE;
\r
1191 service->status.dwWin32ExitCode = NO_ERROR;
\r
1192 service->status.dwServiceSpecificExitCode = 0;
\r
1193 service->status.dwCheckPoint = 0;
\r
1194 service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;
\r
1196 /* Signal we AREN'T running the server */
\r
1197 service->process_handle = 0;
\r
1200 /* Register control handler */
\r
1201 service->status_handle = RegisterServiceCtrlHandlerEx(NSSM, service_control_handler, (void *) service);
\r
1202 if (! service->status_handle) {
\r
1203 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_REGISTERSERVICECTRLHANDER_FAILED, error_string(GetLastError()), 0);
\r
1207 log_service_control(service->name, 0, true);
\r
1209 service->status.dwCurrentState = SERVICE_START_PENDING;
\r
1210 service->status.dwWaitHint = service->throttle_delay + NSSM_WAITHINT_MARGIN;
\r
1211 SetServiceStatus(service->status_handle, &service->status);
\r
1214 /* Try to create the exit action parameters; we don't care if it fails */
\r
1215 create_exit_action(service->name, exit_action_strings[0], false);
\r
1217 SC_HANDLE services = open_service_manager();
\r
1219 service->handle = OpenService(services, service->name, SC_MANAGER_ALL_ACCESS);
\r
1220 set_service_recovery(service);
\r
1221 CloseServiceHandle(services);
\r
1225 /* Used for signalling a resume if the service pauses when throttled. */
\r
1226 if (use_critical_section) {
\r
1227 InitializeCriticalSection(&service->throttle_section);
\r
1228 service->throttle_section_initialised = true;
\r
1231 service->throttle_timer = CreateWaitableTimer(0, 1, 0);
\r
1232 if (! service->throttle_timer) {
\r
1233 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_CREATEWAITABLETIMER_FAILED, service->name, error_string(GetLastError()), 0);
\r
1237 monitor_service(service);
\r
1240 /* Make sure service recovery actions are taken where necessary */
\r
1241 void set_service_recovery(nssm_service_t *service) {
\r
1242 SERVICE_FAILURE_ACTIONS_FLAG flag;
\r
1243 ZeroMemory(&flag, sizeof(flag));
\r
1244 flag.fFailureActionsOnNonCrashFailures = true;
\r
1246 /* This functionality was added in Vista so the call may fail */
\r
1247 if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, &flag)) {
\r
1248 unsigned long error = GetLastError();
\r
1249 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
\r
1250 if (error != ERROR_INVALID_LEVEL) {
\r
1251 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_FAILURE_ACTIONS_FAILED, service->name, error_string(error), 0);
\r
1256 int monitor_service(nssm_service_t *service) {
\r
1257 /* Set service status to started */
\r
1258 int ret = start_service(service);
\r
1261 _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%d"), ret);
\r
1262 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_START_SERVICE_FAILED, service->exe, service->name, ret, 0);
\r
1265 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_STARTED_SERVICE, service->exe, service->flags, service->name, service->dir, 0);
\r
1267 /* Monitor service */
\r
1268 if (! RegisterWaitForSingleObject(&service->wait_handle, service->process_handle, end_service, (void *) service, INFINITE, WT_EXECUTEONLYONCE | WT_EXECUTELONGFUNCTION)) {
\r
1269 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_REGISTERWAITFORSINGLEOBJECT_FAILED, service->name, service->exe, error_string(GetLastError()), 0);
\r
1275 TCHAR *service_control_text(unsigned long control) {
\r
1276 switch (control) {
\r
1277 /* HACK: there is no SERVICE_CONTROL_START constant */
\r
1278 case 0: return _T("START");
\r
1279 case SERVICE_CONTROL_STOP: return _T("STOP");
\r
1280 case SERVICE_CONTROL_SHUTDOWN: return _T("SHUTDOWN");
\r
1281 case SERVICE_CONTROL_PAUSE: return _T("PAUSE");
\r
1282 case SERVICE_CONTROL_CONTINUE: return _T("CONTINUE");
\r
1283 case SERVICE_CONTROL_INTERROGATE: return _T("INTERROGATE");
\r
1284 default: return 0;
\r
1288 void log_service_control(TCHAR *service_name, unsigned long control, bool handled) {
\r
1289 TCHAR *text = service_control_text(control);
\r
1290 unsigned long event;
\r
1293 /* "0x" + 8 x hex + NULL */
\r
1294 text = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, 11 * sizeof(TCHAR));
\r
1296 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);
\r
1299 if (_sntprintf_s(text, 11, _TRUNCATE, _T("0x%08x"), control) < 0) {
\r
1300 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);
\r
1301 HeapFree(GetProcessHeap(), 0, text);
\r
1305 event = NSSM_EVENT_SERVICE_CONTROL_UNKNOWN;
\r
1307 else if (handled) event = NSSM_EVENT_SERVICE_CONTROL_HANDLED;
\r
1308 else event = NSSM_EVENT_SERVICE_CONTROL_NOT_HANDLED;
\r
1310 log_event(EVENTLOG_INFORMATION_TYPE, event, service_name, text, 0);
\r
1312 if (event == NSSM_EVENT_SERVICE_CONTROL_UNKNOWN) {
\r
1313 HeapFree(GetProcessHeap(), 0, text);
\r
1317 /* Service control handler */
\r
1318 unsigned long WINAPI service_control_handler(unsigned long control, unsigned long event, void *data, void *context) {
\r
1319 nssm_service_t *service = (nssm_service_t *) context;
\r
1321 switch (control) {
\r
1322 case SERVICE_CONTROL_INTERROGATE:
\r
1323 /* We always keep the service status up-to-date so this is a no-op. */
\r
1326 case SERVICE_CONTROL_SHUTDOWN:
\r
1327 case SERVICE_CONTROL_STOP:
\r
1328 log_service_control(service->name, control, true);
\r
1330 We MUST acknowledge the stop request promptly but we're committed to
\r
1331 waiting for the application to exit. Spawn a new thread to wait
\r
1332 while we acknowledge the request.
\r
1334 if (! CreateThread(NULL, 0, shutdown_service, context, 0, NULL)) {
\r
1335 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETHREAD_FAILED, error_string(GetLastError()), 0);
\r
1338 We couldn't create a thread to tidy up so we'll have to force the tidyup
\r
1339 to complete in time in this thread.
\r
1341 service->kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;
\r
1342 service->kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;
\r
1343 service->kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;
\r
1345 stop_service(service, 0, true, true);
\r
1349 case SERVICE_CONTROL_CONTINUE:
\r
1350 log_service_control(service->name, control, true);
\r
1351 service->throttle = 0;
\r
1352 if (use_critical_section) imports.WakeConditionVariable(&service->throttle_condition);
\r
1354 if (! service->throttle_timer) return ERROR_CALL_NOT_IMPLEMENTED;
\r
1355 ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));
\r
1356 SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);
\r
1358 service->status.dwCurrentState = SERVICE_CONTINUE_PENDING;
\r
1359 service->status.dwWaitHint = throttle_milliseconds(service->throttle) + NSSM_WAITHINT_MARGIN;
\r
1360 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESET_THROTTLE, service->name, 0);
\r
1361 SetServiceStatus(service->status_handle, &service->status);
\r
1364 case SERVICE_CONTROL_PAUSE:
\r
1366 We don't accept pause messages but it isn't possible to register
\r
1367 only for continue messages so we have to handle this case.
\r
1369 log_service_control(service->name, control, false);
\r
1370 return ERROR_CALL_NOT_IMPLEMENTED;
\r
1373 /* Unknown control */
\r
1374 log_service_control(service->name, control, false);
\r
1375 return ERROR_CALL_NOT_IMPLEMENTED;
\r
1378 /* Start the service */
\r
1379 int start_service(nssm_service_t *service) {
\r
1380 service->stopping = false;
\r
1381 service->allow_restart = true;
\r
1383 if (service->process_handle) return 0;
\r
1385 /* Allocate a STARTUPINFO structure for a new process */
\r
1387 ZeroMemory(&si, sizeof(si));
\r
1388 si.cb = sizeof(si);
\r
1390 /* Allocate a PROCESSINFO structure for the process */
\r
1391 PROCESS_INFORMATION pi;
\r
1392 ZeroMemory(&pi, sizeof(pi));
\r
1394 /* Get startup parameters */
\r
1395 int ret = get_parameters(service, &si);
\r
1397 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_PARAMETERS_FAILED, service->name, 0);
\r
1398 return stop_service(service, 2, true, true);
\r
1401 /* Launch executable with arguments */
\r
1402 TCHAR cmd[CMD_LENGTH];
\r
1403 if (_sntprintf_s(cmd, _countof(cmd), _TRUNCATE, _T("\"%s\" %s"), service->exe, service->flags) < 0) {
\r
1404 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("command line"), _T("start_service"), 0);
\r
1405 close_output_handles(&si);
\r
1406 return stop_service(service, 2, true, true);
\r
1409 throttle_restart(service);
\r
1411 bool inherit_handles = false;
\r
1412 if (si.dwFlags & STARTF_USESTDHANDLES) inherit_handles = true;
\r
1413 unsigned long flags = service->priority & priority_mask();
\r
1414 if (service->affinity) flags |= CREATE_SUSPENDED;
\r
1416 flags |= CREATE_UNICODE_ENVIRONMENT;
\r
1418 if (! CreateProcess(0, cmd, 0, 0, inherit_handles, flags, service->env, service->dir, &si, &pi)) {
\r
1419 unsigned long exitcode = 3;
\r
1420 unsigned long error = GetLastError();
\r
1421 if (error == ERROR_INVALID_PARAMETER && service->env) {
\r
1422 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED_INVALID_ENVIRONMENT, service->name, service->exe, NSSM_REG_ENV, 0);
\r
1423 if (test_environment(service->env)) exitcode = 4;
\r
1425 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service->name, service->exe, error_string(error), 0);
\r
1426 close_output_handles(&si);
\r
1427 return stop_service(service, exitcode, true, true);
\r
1429 service->process_handle = pi.hProcess;
\r
1430 service->pid = pi.dwProcessId;
\r
1432 if (get_process_creation_time(service->process_handle, &service->creation_time)) ZeroMemory(&service->creation_time, sizeof(service->creation_time));
\r
1434 close_output_handles(&si);
\r
1436 if (service->affinity) {
\r
1438 We are explicitly storing service->affinity as a 64-bit unsigned integer
\r
1439 so that we can parse it regardless of whether we're running in 32-bit
\r
1440 or 64-bit mode. The arguments to SetProcessAffinityMask(), however, are
\r
1441 defined as type DWORD_PTR and hence limited to 32 bits on a 32-bit system
\r
1442 (or when running the 32-bit NSSM).
\r
1444 The result is a lot of seemingly-unnecessary casting throughout the code
\r
1445 and potentially confusion when we actually try to start the service.
\r
1446 Having said that, however, it's unlikely that we're actually going to
\r
1447 run in 32-bit mode on a system which has more than 32 CPUs so the
\r
1448 likelihood of seeing a confusing situation is somewhat diminished.
\r
1450 DWORD_PTR affinity, system_affinity;
\r
1452 if (GetProcessAffinityMask(service->process_handle, &affinity, &system_affinity)) affinity = service->affinity & system_affinity;
\r
1454 affinity = (DWORD_PTR) service->affinity;
\r
1455 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GETPROCESSAFFINITYMASK_FAILED, service->name, error_string(GetLastError()), 0);
\r
1458 if (! SetProcessAffinityMask(service->process_handle, affinity)) {
\r
1459 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_SETPROCESSAFFINITYMASK_FAILED, service->name, error_string(GetLastError()), 0);
\r
1462 ResumeThread(pi.hThread);
\r
1466 Wait for a clean startup before changing the service status to RUNNING
\r
1467 but be mindful of the fact that we are blocking the service control manager
\r
1468 so abandon the wait before too much time has elapsed.
\r
1470 unsigned long delay = service->throttle_delay;
\r
1471 if (delay > NSSM_SERVICE_STATUS_DEADLINE) {
\r
1472 TCHAR delay_milliseconds[16];
\r
1473 _sntprintf_s(delay_milliseconds, _countof(delay_milliseconds), _TRUNCATE, _T("%lu"), delay);
\r
1474 TCHAR deadline_milliseconds[16];
\r
1475 _sntprintf_s(deadline_milliseconds, _countof(deadline_milliseconds), _TRUNCATE, _T("%lu"), NSSM_SERVICE_STATUS_DEADLINE);
\r
1476 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_STARTUP_DELAY_TOO_LONG, service->name, delay_milliseconds, NSSM, deadline_milliseconds, 0);
\r
1477 delay = NSSM_SERVICE_STATUS_DEADLINE;
\r
1479 unsigned long deadline = WaitForSingleObject(service->process_handle, delay);
\r
1481 /* Signal successful start */
\r
1482 service->status.dwCurrentState = SERVICE_RUNNING;
\r
1483 SetServiceStatus(service->status_handle, &service->status);
\r
1485 /* Continue waiting for a clean startup. */
\r
1486 if (deadline == WAIT_TIMEOUT) {
\r
1487 if (service->throttle_delay > delay) {
\r
1488 if (WaitForSingleObject(service->process_handle, service->throttle_delay - delay) == WAIT_TIMEOUT) service->throttle = 0;
\r
1490 else service->throttle = 0;
\r
1493 /* Ensure the restart delay is always applied. */
\r
1494 if (service->restart_delay && ! service->throttle) service->throttle++;
\r
1499 /* Stop the service */
\r
1500 int stop_service(nssm_service_t *service, unsigned long exitcode, bool graceful, bool default_action) {
\r
1501 service->allow_restart = false;
\r
1502 if (service->wait_handle) {
\r
1503 UnregisterWait(service->wait_handle);
\r
1504 service->wait_handle = 0;
\r
1507 if (default_action && ! exitcode && ! graceful) {
\r
1508 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
1512 /* Signal we are stopping */
\r
1514 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
1515 service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;
\r
1516 SetServiceStatus(service->status_handle, &service->status);
\r
1519 /* Nothing to do if service isn't running */
\r
1520 if (service->pid) {
\r
1521 /* Shut down service */
\r
1522 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_TERMINATEPROCESS, service->name, service->exe, 0);
\r
1523 kill_process(service, service->process_handle, service->pid, 0);
\r
1525 else log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_PROCESS_ALREADY_STOPPED, service->name, service->exe, 0);
\r
1527 end_service((void *) service, true);
\r
1529 /* Signal we stopped */
\r
1531 service->status.dwCurrentState = SERVICE_STOPPED;
\r
1533 service->status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
\r
1534 service->status.dwServiceSpecificExitCode = exitcode;
\r
1537 service->status.dwWin32ExitCode = NO_ERROR;
\r
1538 service->status.dwServiceSpecificExitCode = 0;
\r
1540 SetServiceStatus(service->status_handle, &service->status);
\r
1546 /* Callback function triggered when the server exits */
\r
1547 void CALLBACK end_service(void *arg, unsigned char why) {
\r
1548 nssm_service_t *service = (nssm_service_t *) arg;
\r
1550 if (service->stopping) return;
\r
1552 service->stopping = true;
\r
1554 /* Check exit code */
\r
1555 unsigned long exitcode = 0;
\r
1557 if (service->process_handle) {
\r
1558 GetExitCodeProcess(service->process_handle, &exitcode);
\r
1559 if (exitcode == STILL_ACTIVE || get_process_exit_time(service->process_handle, &service->exit_time)) GetSystemTimeAsFileTime(&service->exit_time);
\r
1560 CloseHandle(service->process_handle);
\r
1562 else GetSystemTimeAsFileTime(&service->exit_time);
\r
1564 service->process_handle = 0;
\r
1567 Log that the service ended BEFORE logging about killing the process
\r
1568 tree. See below for the possible values of the why argument.
\r
1571 _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), exitcode);
\r
1572 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ENDED_SERVICE, service->exe, service->name, code, 0);
\r
1576 if (exitcode == STILL_ACTIVE) exitcode = 0;
\r
1577 if (service->pid) kill_process_tree(service, service->pid, exitcode, service->pid);
\r
1581 The why argument is true if our wait timed out or false otherwise.
\r
1582 Our wait is infinite so why will never be true when called by the system.
\r
1583 If it is indeed true, assume we were called from stop_service() because
\r
1584 this is a controlled shutdown, and don't take any restart action.
\r
1587 if (! service->allow_restart) return;
\r
1589 /* What action should we take? */
\r
1590 int action = NSSM_EXIT_RESTART;
\r
1591 TCHAR action_string[ACTION_LEN];
\r
1592 bool default_action;
\r
1593 if (! get_exit_action(service->name, &exitcode, action_string, &default_action)) {
\r
1594 for (int i = 0; exit_action_strings[i]; i++) {
\r
1595 if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
1603 /* Try to restart the service or return failure code to service manager */
\r
1604 case NSSM_EXIT_RESTART:
\r
1605 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_RESTART, service->name, code, exit_action_strings[action], service->exe, 0);
\r
1606 while (monitor_service(service)) {
\r
1607 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_RESTART_SERVICE_FAILED, service->exe, service->name, 0);
\r
1612 /* Do nothing, just like srvany would */
\r
1613 case NSSM_EXIT_IGNORE:
\r
1614 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_IGNORE, service->name, code, exit_action_strings[action], service->exe, 0);
\r
1618 /* Tell the service manager we are finished */
\r
1619 case NSSM_EXIT_REALLY:
\r
1620 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_REALLY, service->name, code, exit_action_strings[action], 0);
\r
1621 stop_service(service, exitcode, true, default_action);
\r
1624 /* Fake a crash so pre-Vista service managers will run recovery actions. */
\r
1625 case NSSM_EXIT_UNCLEAN:
\r
1626 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_UNCLEAN, service->name, code, exit_action_strings[action], 0);
\r
1627 stop_service(service, exitcode, false, default_action);
\r
1634 void throttle_restart(nssm_service_t *service) {
\r
1635 /* This can't be a restart if the service is already running. */
\r
1636 if (! service->throttle++) return;
\r
1639 unsigned long throttle_ms = throttle_milliseconds(service->throttle);
\r
1640 TCHAR threshold[8], milliseconds[8];
\r
1642 if (service->restart_delay > throttle_ms) ms = service->restart_delay;
\r
1643 else ms = throttle_ms;
\r
1645 if (service->throttle > 7) service->throttle = 8;
\r
1647 _sntprintf_s(milliseconds, _countof(milliseconds), _TRUNCATE, _T("%lu"), ms);
\r
1649 if (service->throttle == 1 && service->restart_delay > throttle_ms) log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESTART_DELAY, service->name, milliseconds, 0);
\r
1651 _sntprintf_s(threshold, _countof(threshold), _TRUNCATE, _T("%lu"), service->throttle_delay);
\r
1652 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_THROTTLED, service->name, threshold, milliseconds, 0);
\r
1655 if (use_critical_section) EnterCriticalSection(&service->throttle_section);
\r
1656 else if (service->throttle_timer) {
\r
1657 ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));
\r
1658 service->throttle_duetime.QuadPart = 0 - (ms * 10000LL);
\r
1659 SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);
\r
1662 service->status.dwCurrentState = SERVICE_PAUSED;
\r
1663 SetServiceStatus(service->status_handle, &service->status);
\r
1665 if (use_critical_section) {
\r
1666 imports.SleepConditionVariableCS(&service->throttle_condition, &service->throttle_section, ms);
\r
1667 LeaveCriticalSection(&service->throttle_section);
\r
1670 if (service->throttle_timer) WaitForSingleObject(service->throttle_timer, INFINITE);
\r
1676 When responding to a stop (or any other) request we need to set dwWaitHint to
\r
1677 the number of milliseconds we expect the operation to take, and optionally
\r
1678 increase dwCheckPoint. If dwWaitHint milliseconds elapses without the
\r
1679 operation completing or dwCheckPoint increasing, the system will consider the
\r
1680 service to be hung.
\r
1682 However the system will consider the service to be hung after 30000
\r
1683 milliseconds regardless of the value of dwWaitHint if dwCheckPoint has not
\r
1684 changed. Therefore if we want to wait longer than that we must periodically
\r
1685 increase dwCheckPoint.
\r
1687 Furthermore, it will consider the service to be hung after 60000 milliseconds
\r
1688 regardless of the value of dwCheckPoint unless dwWaitHint is increased every
\r
1689 time dwCheckPoint is also increased.
\r
1691 Our strategy then is to retrieve the initial dwWaitHint and wait for
\r
1692 NSSM_SERVICE_STATUS_DEADLINE milliseconds. If the process is still running
\r
1693 and we haven't finished waiting we increment dwCheckPoint and add whichever is
\r
1694 smaller of NSSM_SERVICE_STATUS_DEADLINE or the remaining timeout to
\r
1697 Only doing both these things will prevent the system from killing the service.
\r
1699 Returns: 1 if the wait timed out.
\r
1700 0 if the wait completed.
\r
1703 int await_shutdown(nssm_service_t *service, TCHAR *function_name, unsigned long timeout) {
\r
1704 unsigned long interval;
\r
1705 unsigned long waithint;
\r
1706 unsigned long ret;
\r
1707 unsigned long waited;
\r
1708 TCHAR interval_milliseconds[16];
\r
1709 TCHAR timeout_milliseconds[16];
\r
1710 TCHAR waited_milliseconds[16];
\r
1711 TCHAR *function = function_name;
\r
1713 /* Add brackets to function name. */
\r
1714 size_t funclen = _tcslen(function_name) + 3;
\r
1715 TCHAR *func = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, funclen * sizeof(TCHAR));
\r
1717 if (_sntprintf_s(func, funclen, _TRUNCATE, _T("%s()"), function_name) > -1) function = func;
\r
1720 _sntprintf_s(timeout_milliseconds, _countof(timeout_milliseconds), _TRUNCATE, _T("%lu"), timeout);
\r
1722 waithint = service->status.dwWaitHint;
\r
1724 while (waited < timeout) {
\r
1725 interval = timeout - waited;
\r
1726 if (interval > NSSM_SERVICE_STATUS_DEADLINE) interval = NSSM_SERVICE_STATUS_DEADLINE;
\r
1728 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
1729 service->status.dwWaitHint += interval;
\r
1730 service->status.dwCheckPoint++;
\r
1731 SetServiceStatus(service->status_handle, &service->status);
\r
1734 _sntprintf_s(waited_milliseconds, _countof(waited_milliseconds), _TRUNCATE, _T("%lu"), waited);
\r
1735 _sntprintf_s(interval_milliseconds, _countof(interval_milliseconds), _TRUNCATE, _T("%lu"), interval);
\r
1736 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_AWAITING_SHUTDOWN, function, service->name, waited_milliseconds, interval_milliseconds, timeout_milliseconds, 0);
\r
1739 switch (WaitForSingleObject(service->process_handle, interval)) {
\r
1740 case WAIT_OBJECT_0:
\r
1744 case WAIT_TIMEOUT:
\r
1753 waited += interval;
\r
1757 if (func) HeapFree(GetProcessHeap(), 0, func);
\r