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
706 if (additional_mandatory) {
\r
707 if (argc < mandatory) {
\r
708 print_message(stderr, NSSM_MESSAGE_MISSING_SUBPARAMETER, parameter);
\r
711 additional = argv[3];
\r
715 additional = argv[remainder];
\r
716 if (argc < mandatory) return usage(1);
\r
720 nssm_service_t *service = alloc_nssm_service();
\r
721 _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), service_name);
\r
723 /* Open service manager */
\r
724 SC_HANDLE services = open_service_manager();
\r
726 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
730 /* Try to open the service */
\r
731 service->handle = open_service(services, service->name, service->name, _countof(service->name));
\r
732 if (! service->handle) {
\r
733 CloseServiceHandle(services);
\r
737 /* Get system details. */
\r
738 QUERY_SERVICE_CONFIG *qsc = query_service_config(service->name, service->handle);
\r
740 CloseHandle(service->handle);
\r
741 CloseServiceHandle(services);
\r
745 service->type = qsc->dwServiceType;
\r
746 if (! (service->type & SERVICE_WIN32_OWN_PROCESS)) {
\r
747 if (mode != MODE_GETTING) {
\r
748 HeapFree(GetProcessHeap(), 0, qsc);
\r
749 CloseHandle(service->handle);
\r
750 CloseServiceHandle(services);
\r
751 print_message(stderr, NSSM_MESSAGE_CANNOT_EDIT, service->name, NSSM_WIN32_OWN_PROCESS, 0);
\r
756 if (get_service_startup(service->name, service->handle, qsc, &service->startup)) {
\r
757 if (mode != MODE_GETTING) {
\r
758 HeapFree(GetProcessHeap(), 0, qsc);
\r
759 CloseHandle(service->handle);
\r
760 CloseServiceHandle(services);
\r
765 if (get_service_username(service->name, qsc, &service->username, &service->usernamelen)) {
\r
766 if (mode != MODE_GETTING) {
\r
767 HeapFree(GetProcessHeap(), 0, qsc);
\r
768 CloseHandle(service->handle);
\r
769 CloseServiceHandle(services);
\r
774 _sntprintf_s(service->displayname, _countof(service->displayname), _TRUNCATE, _T("%s"), qsc->lpDisplayName);
\r
776 /* Get the canonical service name. We open it case insensitively. */
\r
777 unsigned long bufsize = _countof(service->name);
\r
778 GetServiceKeyName(services, service->displayname, service->name, &bufsize);
\r
780 /* Remember the executable in case it isn't NSSM. */
\r
781 _sntprintf_s(service->image, _countof(service->image), _TRUNCATE, _T("%s"), qsc->lpBinaryPathName);
\r
782 HeapFree(GetProcessHeap(), 0, qsc);
\r
784 /* Get extended system details. */
\r
785 if (get_service_description(service->name, service->handle, _countof(service->description), service->description)) {
\r
786 if (mode != MODE_GETTING) {
\r
787 CloseHandle(service->handle);
\r
788 CloseServiceHandle(services);
\r
793 /* Get NSSM details. */
\r
794 get_parameters(service, 0);
\r
796 CloseServiceHandle(services);
\r
798 if (! service->exe[0]) {
\r
799 service->native = true;
\r
800 if (mode != MODE_GETTING) print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE, service->name, NSSM, service->image);
\r
803 /* Editing with the GUI. */
\r
804 if (mode == MODE_EDITING) {
\r
805 nssm_gui(IDD_EDIT, service);
\r
809 /* Trying to manage App* parameters for a non-NSSM service. */
\r
810 if (! setting->native && service->native) {
\r
811 CloseHandle(service->handle);
\r
812 print_message(stderr, NSSM_MESSAGE_NATIVE_PARAMETER, setting->name, NSSM);
\r
820 if (mode == MODE_GETTING) {
\r
821 if (! service->native) {
\r
822 key = open_registry(service->name, KEY_READ);
\r
823 if (! key) return 4;
\r
826 if (setting->native) ret = get_setting(service->name, service->handle, setting, &value, additional);
\r
827 else ret = get_setting(service->name, key, setting, &value, additional);
\r
829 CloseHandle(service->handle);
\r
833 switch (setting->type) {
\r
834 case REG_EXPAND_SZ:
\r
837 _tprintf(_T("%s\n"), value.string ? value.string : _T(""));
\r
838 HeapFree(GetProcessHeap(), 0, value.string);
\r
842 _tprintf(_T("%u\n"), value.numeric);
\r
846 if (! service->native) RegCloseKey(key);
\r
847 CloseHandle(service->handle);
\r
851 /* Build the value. */
\r
852 if (mode == MODE_RESETTING) {
\r
853 /* Unset the parameter. */
\r
857 /* Set the parameter. */
\r
859 size_t delimiterlen = (setting->additional & ADDITIONAL_CRLF) ? 2 : 1;
\r
860 for (i = remainder; i < argc; i++) len += _tcslen(argv[i]) + delimiterlen;
\r
863 value.string = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, len * sizeof(TCHAR));
\r
864 if (! value.string) {
\r
865 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("value"), _T("edit_service()"));
\r
866 CloseHandle(service->handle);
\r
871 for (i = remainder; i < argc; i++) {
\r
872 size_t len = _tcslen(argv[i]);
\r
873 memmove(value.string + s, argv[i], len * sizeof(TCHAR));
\r
875 if (i < argc - 1) {
\r
876 if (setting->additional & ADDITIONAL_CRLF) {
\r
877 value.string[s++] = _T('\r');
\r
878 value.string[s++] = _T('\n');
\r
880 else value.string[s++] = _T(' ');
\r
883 value.string[s] = _T('\0');
\r
886 if (! service->native) {
\r
887 key = open_registry(service->name, KEY_WRITE);
\r
889 if (value.string) HeapFree(GetProcessHeap(), 0, value.string);
\r
894 if (setting->native) ret = set_setting(service->name, service->handle, setting, &value, additional);
\r
895 else ret = set_setting(service->name, key, setting, &value, additional);
\r
896 if (value.string) HeapFree(GetProcessHeap(), 0, value.string);
\r
898 if (! service->native) RegCloseKey(key);
\r
899 CloseHandle(service->handle);
\r
903 if (! service->native) RegCloseKey(key);
\r
904 CloseHandle(service->handle);
\r
909 /* About to remove the service */
\r
910 int pre_remove_service(int argc, TCHAR **argv) {
\r
911 nssm_service_t *service = alloc_nssm_service();
\r
912 set_nssm_service_defaults(service);
\r
913 if (argc) _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]);
\r
915 /* Show dialogue box if we didn't pass service name and "confirm" */
\r
916 if (argc < 2) return nssm_gui(IDD_REMOVE, service);
\r
917 if (str_equiv(argv[1], _T("confirm"))) {
\r
918 int ret = remove_service(service);
\r
919 cleanup_nssm_service(service);
\r
922 print_message(stderr, NSSM_MESSAGE_PRE_REMOVE_SERVICE);
\r
926 /* Install the service */
\r
927 int install_service(nssm_service_t *service) {
\r
928 if (! service) return 1;
\r
930 /* Open service manager */
\r
931 SC_HANDLE services = open_service_manager();
\r
933 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
934 cleanup_nssm_service(service);
\r
938 /* Get path of this program */
\r
939 GetModuleFileName(0, service->image, _countof(service->image));
\r
941 /* Create the service - settings will be changed in edit_service() */
\r
942 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
943 if (! service->handle) {
\r
944 print_message(stderr, NSSM_MESSAGE_CREATESERVICE_FAILED, error_string(GetLastError()));
\r
945 CloseServiceHandle(services);
\r
949 if (edit_service(service, false)) {
\r
950 DeleteService(service->handle);
\r
951 CloseServiceHandle(services);
\r
955 print_message(stdout, NSSM_MESSAGE_SERVICE_INSTALLED, service->name);
\r
958 CloseServiceHandle(services);
\r
963 /* Edit the service. */
\r
964 int edit_service(nssm_service_t *service, bool editing) {
\r
965 if (! service) return 1;
\r
968 The only two valid flags for service type are SERVICE_WIN32_OWN_PROCESS
\r
969 and SERVICE_INTERACTIVE_PROCESS.
\r
971 service->type &= SERVICE_INTERACTIVE_PROCESS;
\r
972 service->type |= SERVICE_WIN32_OWN_PROCESS;
\r
974 /* Startup type. */
\r
975 unsigned long startup;
\r
976 switch (service->startup) {
\r
977 case NSSM_STARTUP_MANUAL: startup = SERVICE_DEMAND_START; break;
\r
978 case NSSM_STARTUP_DISABLED: startup = SERVICE_DISABLED; break;
\r
979 default: startup = SERVICE_AUTO_START;
\r
982 /* Display name. */
\r
983 if (! service->displayname[0]) _sntprintf_s(service->displayname, _countof(service->displayname), _TRUNCATE, _T("%s"), service->name);
\r
986 Username must be NULL if we aren't changing or an account name.
\r
987 We must explicitly use LOCALSYSTEM to change it when we are editing.
\r
988 Password must be NULL if we aren't changing, a password or "".
\r
989 Empty passwords are valid but we won't allow them in the GUI.
\r
991 TCHAR *username = 0;
\r
992 TCHAR *password = 0;
\r
993 if (service->usernamelen) {
\r
994 username = service->username;
\r
995 if (service->passwordlen) password = service->password;
\r
996 else password = _T("");
\r
998 else if (editing) username = NSSM_LOCALSYSTEM_ACCOUNT;
\r
1000 if (grant_logon_as_service(username)) {
\r
1001 print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username);
\r
1005 if (! ChangeServiceConfig(service->handle, service->type, startup, SERVICE_NO_CHANGE, 0, 0, 0, 0, username, password, service->displayname)) {
\r
1006 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
1010 if (service->description[0] || editing) {
\r
1011 set_service_description(service->name, service->handle, service->description);
\r
1014 SERVICE_DELAYED_AUTO_START_INFO delayed;
\r
1015 ZeroMemory(&delayed, sizeof(delayed));
\r
1016 if (service->startup == NSSM_STARTUP_DELAYED) delayed.fDelayedAutostart = 1;
\r
1017 else delayed.fDelayedAutostart = 0;
\r
1018 /* Delayed startup isn't supported until Vista. */
\r
1019 if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, &delayed)) {
\r
1020 unsigned long error = GetLastError();
\r
1021 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
\r
1022 if (error != ERROR_INVALID_LEVEL) {
\r
1023 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_DELAYED_AUTO_START_INFO_FAILED, service->name, error_string(error), 0);
\r
1027 /* Don't mess with parameters which aren't ours. */
\r
1028 if (! service->native) {
\r
1029 /* Now we need to put the parameters into the registry */
\r
1030 if (create_parameters(service, editing)) {
\r
1031 print_message(stderr, NSSM_MESSAGE_CREATE_PARAMETERS_FAILED);
\r
1035 set_service_recovery(service);
\r
1041 /* Control a service. */
\r
1042 int control_service(unsigned long control, int argc, TCHAR **argv) {
\r
1043 if (argc < 1) return usage(1);
\r
1044 TCHAR *service_name = argv[0];
\r
1045 TCHAR canonical_name[SERVICE_NAME_LENGTH];
\r
1047 SC_HANDLE services = open_service_manager();
\r
1049 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
1053 SC_HANDLE service_handle = open_service(services, service_name, canonical_name, _countof(canonical_name));
\r
1054 if (! service_handle) {
\r
1055 CloseServiceHandle(services);
\r
1060 unsigned long error;
\r
1061 SERVICE_STATUS service_status;
\r
1062 if (control == NSSM_SERVICE_CONTROL_START) {
\r
1063 ret = StartService(service_handle, (unsigned long) argc, (const TCHAR **) argv);
\r
1064 error = GetLastError();
\r
1065 CloseHandle(service_handle);
\r
1066 CloseServiceHandle(services);
\r
1068 if (error == ERROR_IO_PENDING) {
\r
1070 Older versions of Windows return immediately with ERROR_IO_PENDING
\r
1071 indicate that the operation is still in progress. Newer versions
\r
1072 will return it if there really is a delay. As far as we're
\r
1073 concerned the operation is a success. We don't claim to offer a
\r
1074 fully-feature service control method; it's just a quick 'n' dirty
\r
1077 In the future we may identify and handle this situation properly.
\r
1080 error = ERROR_SUCCESS;
\r
1084 _tprintf(_T("%s: %s"), canonical_name, error_string(error));
\r
1088 _ftprintf(stderr, _T("%s: %s"), canonical_name, error_string(error));
\r
1092 else if (control == SERVICE_CONTROL_INTERROGATE) {
\r
1094 We could actually send an INTERROGATE control but that won't return
\r
1095 any information if the service is stopped and we don't care about
\r
1096 the extra details it might give us in any case. So we'll fake it.
\r
1098 ret = QueryServiceStatus(service_handle, &service_status);
\r
1099 error = GetLastError();
\r
1102 switch (service_status.dwCurrentState) {
\r
1103 case SERVICE_STOPPED: _tprintf(_T("SERVICE_STOPPED\n")); break;
\r
1104 case SERVICE_START_PENDING: _tprintf(_T("SERVICE_START_PENDING\n")); break;
\r
1105 case SERVICE_STOP_PENDING: _tprintf(_T("SERVICE_STOP_PENDING\n")); break;
\r
1106 case SERVICE_RUNNING: _tprintf(_T("SERVICE_RUNNING\n")); break;
\r
1107 case SERVICE_CONTINUE_PENDING: _tprintf(_T("SERVICE_CONTINUE_PENDING\n")); break;
\r
1108 case SERVICE_PAUSE_PENDING: _tprintf(_T("SERVICE_PAUSE_PENDING\n")); break;
\r
1109 case SERVICE_PAUSED: _tprintf(_T("SERVICE_PAUSED\n")); break;
\r
1110 default: _tprintf(_T("?\n")); return 1;
\r
1115 _ftprintf(stderr, _T("%s: %s\n"), canonical_name, error_string(error));
\r
1120 ret = ControlService(service_handle, control, &service_status);
\r
1121 error = GetLastError();
\r
1122 CloseHandle(service_handle);
\r
1123 CloseServiceHandle(services);
\r
1125 if (error == ERROR_IO_PENDING) {
\r
1127 error = ERROR_SUCCESS;
\r
1131 _tprintf(_T("%s: %s"), canonical_name, error_string(error));
\r
1135 _ftprintf(stderr, _T("%s: %s"), canonical_name, error_string(error));
\r
1141 /* Remove the service */
\r
1142 int remove_service(nssm_service_t *service) {
\r
1143 if (! service) return 1;
\r
1145 /* Open service manager */
\r
1146 SC_HANDLE services = open_service_manager();
\r
1148 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
1152 /* Try to open the service */
\r
1153 service->handle = open_service(services, service->name, service->name, _countof(service->name));
\r
1154 if (! service->handle) {
\r
1155 CloseServiceHandle(services);
\r
1159 /* Get the canonical service name. We open it case insensitively. */
\r
1160 unsigned long bufsize = _countof(service->displayname);
\r
1161 GetServiceDisplayName(services, service->name, service->displayname, &bufsize);
\r
1162 bufsize = _countof(service->name);
\r
1163 GetServiceKeyName(services, service->displayname, service->name, &bufsize);
\r
1165 /* Try to delete the service */
\r
1166 if (! DeleteService(service->handle)) {
\r
1167 print_message(stderr, NSSM_MESSAGE_DELETESERVICE_FAILED);
\r
1168 CloseServiceHandle(services);
\r
1173 CloseServiceHandle(services);
\r
1175 print_message(stdout, NSSM_MESSAGE_SERVICE_REMOVED, service->name);
\r
1179 /* Service initialisation */
\r
1180 void WINAPI service_main(unsigned long argc, TCHAR **argv) {
\r
1181 nssm_service_t *service = alloc_nssm_service();
\r
1182 if (! service) return;
\r
1184 if (_sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]) < 0) {
\r
1185 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("service->name"), _T("service_main()"), 0);
\r
1189 /* We can use a condition variable in a critical section on Vista or later. */
\r
1190 if (imports.SleepConditionVariableCS && imports.WakeConditionVariable) use_critical_section = true;
\r
1191 else use_critical_section = false;
\r
1193 /* Initialise status */
\r
1194 ZeroMemory(&service->status, sizeof(service->status));
\r
1195 service->status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;
\r
1196 service->status.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE;
\r
1197 service->status.dwWin32ExitCode = NO_ERROR;
\r
1198 service->status.dwServiceSpecificExitCode = 0;
\r
1199 service->status.dwCheckPoint = 0;
\r
1200 service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;
\r
1202 /* Signal we AREN'T running the server */
\r
1203 service->process_handle = 0;
\r
1206 /* Register control handler */
\r
1207 service->status_handle = RegisterServiceCtrlHandlerEx(NSSM, service_control_handler, (void *) service);
\r
1208 if (! service->status_handle) {
\r
1209 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_REGISTERSERVICECTRLHANDER_FAILED, error_string(GetLastError()), 0);
\r
1213 log_service_control(service->name, 0, true);
\r
1215 service->status.dwCurrentState = SERVICE_START_PENDING;
\r
1216 service->status.dwWaitHint = service->throttle_delay + NSSM_WAITHINT_MARGIN;
\r
1217 SetServiceStatus(service->status_handle, &service->status);
\r
1220 /* Try to create the exit action parameters; we don't care if it fails */
\r
1221 create_exit_action(service->name, exit_action_strings[0], false);
\r
1223 SC_HANDLE services = open_service_manager();
\r
1225 service->handle = OpenService(services, service->name, SC_MANAGER_ALL_ACCESS);
\r
1226 set_service_recovery(service);
\r
1227 CloseServiceHandle(services);
\r
1231 /* Used for signalling a resume if the service pauses when throttled. */
\r
1232 if (use_critical_section) {
\r
1233 InitializeCriticalSection(&service->throttle_section);
\r
1234 service->throttle_section_initialised = true;
\r
1237 service->throttle_timer = CreateWaitableTimer(0, 1, 0);
\r
1238 if (! service->throttle_timer) {
\r
1239 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_CREATEWAITABLETIMER_FAILED, service->name, error_string(GetLastError()), 0);
\r
1243 monitor_service(service);
\r
1246 /* Make sure service recovery actions are taken where necessary */
\r
1247 void set_service_recovery(nssm_service_t *service) {
\r
1248 SERVICE_FAILURE_ACTIONS_FLAG flag;
\r
1249 ZeroMemory(&flag, sizeof(flag));
\r
1250 flag.fFailureActionsOnNonCrashFailures = true;
\r
1252 /* This functionality was added in Vista so the call may fail */
\r
1253 if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, &flag)) {
\r
1254 unsigned long error = GetLastError();
\r
1255 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
\r
1256 if (error != ERROR_INVALID_LEVEL) {
\r
1257 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_FAILURE_ACTIONS_FAILED, service->name, error_string(error), 0);
\r
1262 int monitor_service(nssm_service_t *service) {
\r
1263 /* Set service status to started */
\r
1264 int ret = start_service(service);
\r
1267 _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%d"), ret);
\r
1268 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_START_SERVICE_FAILED, service->exe, service->name, ret, 0);
\r
1271 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_STARTED_SERVICE, service->exe, service->flags, service->name, service->dir, 0);
\r
1273 /* Monitor service */
\r
1274 if (! RegisterWaitForSingleObject(&service->wait_handle, service->process_handle, end_service, (void *) service, INFINITE, WT_EXECUTEONLYONCE | WT_EXECUTELONGFUNCTION)) {
\r
1275 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_REGISTERWAITFORSINGLEOBJECT_FAILED, service->name, service->exe, error_string(GetLastError()), 0);
\r
1281 TCHAR *service_control_text(unsigned long control) {
\r
1282 switch (control) {
\r
1283 /* HACK: there is no SERVICE_CONTROL_START constant */
\r
1284 case NSSM_SERVICE_CONTROL_START: return _T("START");
\r
1285 case SERVICE_CONTROL_STOP: return _T("STOP");
\r
1286 case SERVICE_CONTROL_SHUTDOWN: return _T("SHUTDOWN");
\r
1287 case SERVICE_CONTROL_PAUSE: return _T("PAUSE");
\r
1288 case SERVICE_CONTROL_CONTINUE: return _T("CONTINUE");
\r
1289 case SERVICE_CONTROL_INTERROGATE: return _T("INTERROGATE");
\r
1290 case NSSM_SERVICE_CONTROL_ROTATE: return _T("ROTATE");
\r
1291 default: return 0;
\r
1295 TCHAR *service_status_text(unsigned long status) {
\r
1297 case SERVICE_STOPPED: return _T("SERVICE_STOPPED");
\r
1298 case SERVICE_START_PENDING: return _T("SERVICE_START_PENDING");
\r
1299 case SERVICE_STOP_PENDING: return _T("SERVICE_STOP_PENDING");
\r
1300 case SERVICE_RUNNING: return _T("SERVICE_RUNNING");
\r
1301 case SERVICE_CONTINUE_PENDING: return _T("SERVICE_CONTINUE_PENDING");
\r
1302 case SERVICE_PAUSE_PENDING: return _T("SERVICE_PAUSE_PENDING");
\r
1303 case SERVICE_PAUSED: return _T("SERVICE_PAUSED");
\r
1304 default: return 0;
\r
1308 void log_service_control(TCHAR *service_name, unsigned long control, bool handled) {
\r
1309 TCHAR *text = service_control_text(control);
\r
1310 unsigned long event;
\r
1313 /* "0x" + 8 x hex + NULL */
\r
1314 text = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, 11 * sizeof(TCHAR));
\r
1316 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);
\r
1319 if (_sntprintf_s(text, 11, _TRUNCATE, _T("0x%08x"), control) < 0) {
\r
1320 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);
\r
1321 HeapFree(GetProcessHeap(), 0, text);
\r
1325 event = NSSM_EVENT_SERVICE_CONTROL_UNKNOWN;
\r
1327 else if (handled) event = NSSM_EVENT_SERVICE_CONTROL_HANDLED;
\r
1328 else event = NSSM_EVENT_SERVICE_CONTROL_NOT_HANDLED;
\r
1330 log_event(EVENTLOG_INFORMATION_TYPE, event, service_name, text, 0);
\r
1332 if (event == NSSM_EVENT_SERVICE_CONTROL_UNKNOWN) {
\r
1333 HeapFree(GetProcessHeap(), 0, text);
\r
1337 /* Service control handler */
\r
1338 unsigned long WINAPI service_control_handler(unsigned long control, unsigned long event, void *data, void *context) {
\r
1339 nssm_service_t *service = (nssm_service_t *) context;
\r
1341 switch (control) {
\r
1342 case SERVICE_CONTROL_INTERROGATE:
\r
1343 /* We always keep the service status up-to-date so this is a no-op. */
\r
1346 case SERVICE_CONTROL_SHUTDOWN:
\r
1347 case SERVICE_CONTROL_STOP:
\r
1348 log_service_control(service->name, control, true);
\r
1350 We MUST acknowledge the stop request promptly but we're committed to
\r
1351 waiting for the application to exit. Spawn a new thread to wait
\r
1352 while we acknowledge the request.
\r
1354 if (! CreateThread(NULL, 0, shutdown_service, context, 0, NULL)) {
\r
1355 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETHREAD_FAILED, error_string(GetLastError()), 0);
\r
1358 We couldn't create a thread to tidy up so we'll have to force the tidyup
\r
1359 to complete in time in this thread.
\r
1361 service->kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;
\r
1362 service->kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;
\r
1363 service->kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;
\r
1365 stop_service(service, 0, true, true);
\r
1369 case SERVICE_CONTROL_CONTINUE:
\r
1370 log_service_control(service->name, control, true);
\r
1371 service->throttle = 0;
\r
1372 if (use_critical_section) imports.WakeConditionVariable(&service->throttle_condition);
\r
1374 if (! service->throttle_timer) return ERROR_CALL_NOT_IMPLEMENTED;
\r
1375 ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));
\r
1376 SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);
\r
1378 service->status.dwCurrentState = SERVICE_CONTINUE_PENDING;
\r
1379 service->status.dwWaitHint = throttle_milliseconds(service->throttle) + NSSM_WAITHINT_MARGIN;
\r
1380 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESET_THROTTLE, service->name, 0);
\r
1381 SetServiceStatus(service->status_handle, &service->status);
\r
1384 case SERVICE_CONTROL_PAUSE:
\r
1386 We don't accept pause messages but it isn't possible to register
\r
1387 only for continue messages so we have to handle this case.
\r
1389 log_service_control(service->name, control, false);
\r
1390 return ERROR_CALL_NOT_IMPLEMENTED;
\r
1392 case NSSM_SERVICE_CONTROL_ROTATE:
\r
1393 log_service_control(service->name, control, true);
\r
1394 if (service->rotate_stdout_online) service->rotate_stdout_online = NSSM_ROTATE_ONLINE_ASAP;
\r
1395 if (service->rotate_stdout_online) service->rotate_stderr_online = NSSM_ROTATE_ONLINE_ASAP;
\r
1399 /* Unknown control */
\r
1400 log_service_control(service->name, control, false);
\r
1401 return ERROR_CALL_NOT_IMPLEMENTED;
\r
1404 /* Start the service */
\r
1405 int start_service(nssm_service_t *service) {
\r
1406 service->stopping = false;
\r
1407 service->allow_restart = true;
\r
1409 if (service->process_handle) return 0;
\r
1411 /* Allocate a STARTUPINFO structure for a new process */
\r
1413 ZeroMemory(&si, sizeof(si));
\r
1414 si.cb = sizeof(si);
\r
1416 /* Allocate a PROCESSINFO structure for the process */
\r
1417 PROCESS_INFORMATION pi;
\r
1418 ZeroMemory(&pi, sizeof(pi));
\r
1420 /* Get startup parameters */
\r
1421 int ret = get_parameters(service, &si);
\r
1423 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_PARAMETERS_FAILED, service->name, 0);
\r
1424 return stop_service(service, 2, true, true);
\r
1427 /* Launch executable with arguments */
\r
1428 TCHAR cmd[CMD_LENGTH];
\r
1429 if (_sntprintf_s(cmd, _countof(cmd), _TRUNCATE, _T("\"%s\" %s"), service->exe, service->flags) < 0) {
\r
1430 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("command line"), _T("start_service"), 0);
\r
1431 close_output_handles(&si);
\r
1432 return stop_service(service, 2, true, true);
\r
1435 throttle_restart(service);
\r
1437 bool inherit_handles = false;
\r
1438 if (si.dwFlags & STARTF_USESTDHANDLES) inherit_handles = true;
\r
1439 unsigned long flags = service->priority & priority_mask();
\r
1440 if (service->affinity) flags |= CREATE_SUSPENDED;
\r
1442 flags |= CREATE_UNICODE_ENVIRONMENT;
\r
1444 if (! CreateProcess(0, cmd, 0, 0, inherit_handles, flags, service->env, service->dir, &si, &pi)) {
\r
1445 unsigned long exitcode = 3;
\r
1446 unsigned long error = GetLastError();
\r
1447 if (error == ERROR_INVALID_PARAMETER && service->env) {
\r
1448 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED_INVALID_ENVIRONMENT, service->name, service->exe, NSSM_REG_ENV, 0);
\r
1449 if (test_environment(service->env)) exitcode = 4;
\r
1451 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service->name, service->exe, error_string(error), 0);
\r
1452 close_output_handles(&si);
\r
1453 return stop_service(service, exitcode, true, true);
\r
1455 service->process_handle = pi.hProcess;
\r
1456 service->pid = pi.dwProcessId;
\r
1458 if (get_process_creation_time(service->process_handle, &service->creation_time)) ZeroMemory(&service->creation_time, sizeof(service->creation_time));
\r
1460 close_output_handles(&si, ! service->rotate_stdout_online, ! service->rotate_stderr_online);
\r
1462 if (service->affinity) {
\r
1464 We are explicitly storing service->affinity as a 64-bit unsigned integer
\r
1465 so that we can parse it regardless of whether we're running in 32-bit
\r
1466 or 64-bit mode. The arguments to SetProcessAffinityMask(), however, are
\r
1467 defined as type DWORD_PTR and hence limited to 32 bits on a 32-bit system
\r
1468 (or when running the 32-bit NSSM).
\r
1470 The result is a lot of seemingly-unnecessary casting throughout the code
\r
1471 and potentially confusion when we actually try to start the service.
\r
1472 Having said that, however, it's unlikely that we're actually going to
\r
1473 run in 32-bit mode on a system which has more than 32 CPUs so the
\r
1474 likelihood of seeing a confusing situation is somewhat diminished.
\r
1476 DWORD_PTR affinity, system_affinity;
\r
1478 if (GetProcessAffinityMask(service->process_handle, &affinity, &system_affinity)) affinity = service->affinity & system_affinity;
\r
1480 affinity = (DWORD_PTR) service->affinity;
\r
1481 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GETPROCESSAFFINITYMASK_FAILED, service->name, error_string(GetLastError()), 0);
\r
1484 if (! SetProcessAffinityMask(service->process_handle, affinity)) {
\r
1485 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_SETPROCESSAFFINITYMASK_FAILED, service->name, error_string(GetLastError()), 0);
\r
1488 ResumeThread(pi.hThread);
\r
1492 Wait for a clean startup before changing the service status to RUNNING
\r
1493 but be mindful of the fact that we are blocking the service control manager
\r
1494 so abandon the wait before too much time has elapsed.
\r
1496 unsigned long delay = service->throttle_delay;
\r
1497 if (delay > NSSM_SERVICE_STATUS_DEADLINE) {
\r
1498 TCHAR delay_milliseconds[16];
\r
1499 _sntprintf_s(delay_milliseconds, _countof(delay_milliseconds), _TRUNCATE, _T("%lu"), delay);
\r
1500 TCHAR deadline_milliseconds[16];
\r
1501 _sntprintf_s(deadline_milliseconds, _countof(deadline_milliseconds), _TRUNCATE, _T("%lu"), NSSM_SERVICE_STATUS_DEADLINE);
\r
1502 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_STARTUP_DELAY_TOO_LONG, service->name, delay_milliseconds, NSSM, deadline_milliseconds, 0);
\r
1503 delay = NSSM_SERVICE_STATUS_DEADLINE;
\r
1505 unsigned long deadline = WaitForSingleObject(service->process_handle, delay);
\r
1507 /* Signal successful start */
\r
1508 service->status.dwCurrentState = SERVICE_RUNNING;
\r
1509 SetServiceStatus(service->status_handle, &service->status);
\r
1511 /* Continue waiting for a clean startup. */
\r
1512 if (deadline == WAIT_TIMEOUT) {
\r
1513 if (service->throttle_delay > delay) {
\r
1514 if (WaitForSingleObject(service->process_handle, service->throttle_delay - delay) == WAIT_TIMEOUT) service->throttle = 0;
\r
1516 else service->throttle = 0;
\r
1519 /* Ensure the restart delay is always applied. */
\r
1520 if (service->restart_delay && ! service->throttle) service->throttle++;
\r
1525 /* Stop the service */
\r
1526 int stop_service(nssm_service_t *service, unsigned long exitcode, bool graceful, bool default_action) {
\r
1527 service->allow_restart = false;
\r
1528 if (service->wait_handle) {
\r
1529 UnregisterWait(service->wait_handle);
\r
1530 service->wait_handle = 0;
\r
1533 if (default_action && ! exitcode && ! graceful) {
\r
1534 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
1538 /* Signal we are stopping */
\r
1540 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
1541 service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;
\r
1542 SetServiceStatus(service->status_handle, &service->status);
\r
1545 /* Nothing to do if service isn't running */
\r
1546 if (service->pid) {
\r
1547 /* Shut down service */
\r
1548 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_TERMINATEPROCESS, service->name, service->exe, 0);
\r
1549 kill_process(service, service->process_handle, service->pid, 0);
\r
1551 else log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_PROCESS_ALREADY_STOPPED, service->name, service->exe, 0);
\r
1553 end_service((void *) service, true);
\r
1555 /* Signal we stopped */
\r
1557 service->status.dwCurrentState = SERVICE_STOPPED;
\r
1559 service->status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
\r
1560 service->status.dwServiceSpecificExitCode = exitcode;
\r
1563 service->status.dwWin32ExitCode = NO_ERROR;
\r
1564 service->status.dwServiceSpecificExitCode = 0;
\r
1566 SetServiceStatus(service->status_handle, &service->status);
\r
1572 /* Callback function triggered when the server exits */
\r
1573 void CALLBACK end_service(void *arg, unsigned char why) {
\r
1574 nssm_service_t *service = (nssm_service_t *) arg;
\r
1576 if (service->stopping) return;
\r
1578 service->stopping = true;
\r
1580 /* Check exit code */
\r
1581 unsigned long exitcode = 0;
\r
1583 if (service->process_handle) {
\r
1584 GetExitCodeProcess(service->process_handle, &exitcode);
\r
1585 if (exitcode == STILL_ACTIVE || get_process_exit_time(service->process_handle, &service->exit_time)) GetSystemTimeAsFileTime(&service->exit_time);
\r
1586 CloseHandle(service->process_handle);
\r
1588 else GetSystemTimeAsFileTime(&service->exit_time);
\r
1590 service->process_handle = 0;
\r
1593 Log that the service ended BEFORE logging about killing the process
\r
1594 tree. See below for the possible values of the why argument.
\r
1597 _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), exitcode);
\r
1598 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ENDED_SERVICE, service->exe, service->name, code, 0);
\r
1602 if (exitcode == STILL_ACTIVE) exitcode = 0;
\r
1603 if (service->pid) kill_process_tree(service, service->pid, exitcode, service->pid);
\r
1607 The why argument is true if our wait timed out or false otherwise.
\r
1608 Our wait is infinite so why will never be true when called by the system.
\r
1609 If it is indeed true, assume we were called from stop_service() because
\r
1610 this is a controlled shutdown, and don't take any restart action.
\r
1613 if (! service->allow_restart) return;
\r
1615 /* What action should we take? */
\r
1616 int action = NSSM_EXIT_RESTART;
\r
1617 TCHAR action_string[ACTION_LEN];
\r
1618 bool default_action;
\r
1619 if (! get_exit_action(service->name, &exitcode, action_string, &default_action)) {
\r
1620 for (int i = 0; exit_action_strings[i]; i++) {
\r
1621 if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
1629 /* Try to restart the service or return failure code to service manager */
\r
1630 case NSSM_EXIT_RESTART:
\r
1631 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_RESTART, service->name, code, exit_action_strings[action], service->exe, 0);
\r
1632 while (monitor_service(service)) {
\r
1633 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_RESTART_SERVICE_FAILED, service->exe, service->name, 0);
\r
1638 /* Do nothing, just like srvany would */
\r
1639 case NSSM_EXIT_IGNORE:
\r
1640 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_IGNORE, service->name, code, exit_action_strings[action], service->exe, 0);
\r
1644 /* Tell the service manager we are finished */
\r
1645 case NSSM_EXIT_REALLY:
\r
1646 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_REALLY, service->name, code, exit_action_strings[action], 0);
\r
1647 stop_service(service, exitcode, true, default_action);
\r
1650 /* Fake a crash so pre-Vista service managers will run recovery actions. */
\r
1651 case NSSM_EXIT_UNCLEAN:
\r
1652 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_UNCLEAN, service->name, code, exit_action_strings[action], 0);
\r
1653 stop_service(service, exitcode, false, default_action);
\r
1660 void throttle_restart(nssm_service_t *service) {
\r
1661 /* This can't be a restart if the service is already running. */
\r
1662 if (! service->throttle++) return;
\r
1665 unsigned long throttle_ms = throttle_milliseconds(service->throttle);
\r
1666 TCHAR threshold[8], milliseconds[8];
\r
1668 if (service->restart_delay > throttle_ms) ms = service->restart_delay;
\r
1669 else ms = throttle_ms;
\r
1671 if (service->throttle > 7) service->throttle = 8;
\r
1673 _sntprintf_s(milliseconds, _countof(milliseconds), _TRUNCATE, _T("%lu"), ms);
\r
1675 if (service->throttle == 1 && service->restart_delay > throttle_ms) log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESTART_DELAY, service->name, milliseconds, 0);
\r
1677 _sntprintf_s(threshold, _countof(threshold), _TRUNCATE, _T("%lu"), service->throttle_delay);
\r
1678 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_THROTTLED, service->name, threshold, milliseconds, 0);
\r
1681 if (use_critical_section) EnterCriticalSection(&service->throttle_section);
\r
1682 else if (service->throttle_timer) {
\r
1683 ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));
\r
1684 service->throttle_duetime.QuadPart = 0 - (ms * 10000LL);
\r
1685 SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);
\r
1688 service->status.dwCurrentState = SERVICE_PAUSED;
\r
1689 SetServiceStatus(service->status_handle, &service->status);
\r
1691 if (use_critical_section) {
\r
1692 imports.SleepConditionVariableCS(&service->throttle_condition, &service->throttle_section, ms);
\r
1693 LeaveCriticalSection(&service->throttle_section);
\r
1696 if (service->throttle_timer) WaitForSingleObject(service->throttle_timer, INFINITE);
\r
1702 When responding to a stop (or any other) request we need to set dwWaitHint to
\r
1703 the number of milliseconds we expect the operation to take, and optionally
\r
1704 increase dwCheckPoint. If dwWaitHint milliseconds elapses without the
\r
1705 operation completing or dwCheckPoint increasing, the system will consider the
\r
1706 service to be hung.
\r
1708 However the system will consider the service to be hung after 30000
\r
1709 milliseconds regardless of the value of dwWaitHint if dwCheckPoint has not
\r
1710 changed. Therefore if we want to wait longer than that we must periodically
\r
1711 increase dwCheckPoint.
\r
1713 Furthermore, it will consider the service to be hung after 60000 milliseconds
\r
1714 regardless of the value of dwCheckPoint unless dwWaitHint is increased every
\r
1715 time dwCheckPoint is also increased.
\r
1717 Our strategy then is to retrieve the initial dwWaitHint and wait for
\r
1718 NSSM_SERVICE_STATUS_DEADLINE milliseconds. If the process is still running
\r
1719 and we haven't finished waiting we increment dwCheckPoint and add whichever is
\r
1720 smaller of NSSM_SERVICE_STATUS_DEADLINE or the remaining timeout to
\r
1723 Only doing both these things will prevent the system from killing the service.
\r
1725 Returns: 1 if the wait timed out.
\r
1726 0 if the wait completed.
\r
1729 int await_shutdown(nssm_service_t *service, TCHAR *function_name, unsigned long timeout) {
\r
1730 unsigned long interval;
\r
1731 unsigned long waithint;
\r
1732 unsigned long ret;
\r
1733 unsigned long waited;
\r
1734 TCHAR interval_milliseconds[16];
\r
1735 TCHAR timeout_milliseconds[16];
\r
1736 TCHAR waited_milliseconds[16];
\r
1737 TCHAR *function = function_name;
\r
1739 /* Add brackets to function name. */
\r
1740 size_t funclen = _tcslen(function_name) + 3;
\r
1741 TCHAR *func = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, funclen * sizeof(TCHAR));
\r
1743 if (_sntprintf_s(func, funclen, _TRUNCATE, _T("%s()"), function_name) > -1) function = func;
\r
1746 _sntprintf_s(timeout_milliseconds, _countof(timeout_milliseconds), _TRUNCATE, _T("%lu"), timeout);
\r
1748 waithint = service->status.dwWaitHint;
\r
1750 while (waited < timeout) {
\r
1751 interval = timeout - waited;
\r
1752 if (interval > NSSM_SERVICE_STATUS_DEADLINE) interval = NSSM_SERVICE_STATUS_DEADLINE;
\r
1754 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
1755 service->status.dwWaitHint += interval;
\r
1756 service->status.dwCheckPoint++;
\r
1757 SetServiceStatus(service->status_handle, &service->status);
\r
1760 _sntprintf_s(waited_milliseconds, _countof(waited_milliseconds), _TRUNCATE, _T("%lu"), waited);
\r
1761 _sntprintf_s(interval_milliseconds, _countof(interval_milliseconds), _TRUNCATE, _T("%lu"), interval);
\r
1762 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_AWAITING_SHUTDOWN, function, service->name, waited_milliseconds, interval_milliseconds, timeout_milliseconds, 0);
\r
1765 switch (WaitForSingleObject(service->process_handle, interval)) {
\r
1766 case WAIT_OBJECT_0:
\r
1770 case WAIT_TIMEOUT:
\r
1779 waited += interval;
\r
1783 if (func) HeapFree(GetProcessHeap(), 0, func);
\r