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 case NSSM_SERVICE_CONTROL_ROTATE: return _T("ROTATE");
\r
1285 default: return 0;
\r
1289 void log_service_control(TCHAR *service_name, unsigned long control, bool handled) {
\r
1290 TCHAR *text = service_control_text(control);
\r
1291 unsigned long event;
\r
1294 /* "0x" + 8 x hex + NULL */
\r
1295 text = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, 11 * sizeof(TCHAR));
\r
1297 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);
\r
1300 if (_sntprintf_s(text, 11, _TRUNCATE, _T("0x%08x"), control) < 0) {
\r
1301 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);
\r
1302 HeapFree(GetProcessHeap(), 0, text);
\r
1306 event = NSSM_EVENT_SERVICE_CONTROL_UNKNOWN;
\r
1308 else if (handled) event = NSSM_EVENT_SERVICE_CONTROL_HANDLED;
\r
1309 else event = NSSM_EVENT_SERVICE_CONTROL_NOT_HANDLED;
\r
1311 log_event(EVENTLOG_INFORMATION_TYPE, event, service_name, text, 0);
\r
1313 if (event == NSSM_EVENT_SERVICE_CONTROL_UNKNOWN) {
\r
1314 HeapFree(GetProcessHeap(), 0, text);
\r
1318 /* Service control handler */
\r
1319 unsigned long WINAPI service_control_handler(unsigned long control, unsigned long event, void *data, void *context) {
\r
1320 nssm_service_t *service = (nssm_service_t *) context;
\r
1322 switch (control) {
\r
1323 case SERVICE_CONTROL_INTERROGATE:
\r
1324 /* We always keep the service status up-to-date so this is a no-op. */
\r
1327 case SERVICE_CONTROL_SHUTDOWN:
\r
1328 case SERVICE_CONTROL_STOP:
\r
1329 log_service_control(service->name, control, true);
\r
1331 We MUST acknowledge the stop request promptly but we're committed to
\r
1332 waiting for the application to exit. Spawn a new thread to wait
\r
1333 while we acknowledge the request.
\r
1335 if (! CreateThread(NULL, 0, shutdown_service, context, 0, NULL)) {
\r
1336 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETHREAD_FAILED, error_string(GetLastError()), 0);
\r
1339 We couldn't create a thread to tidy up so we'll have to force the tidyup
\r
1340 to complete in time in this thread.
\r
1342 service->kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;
\r
1343 service->kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;
\r
1344 service->kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;
\r
1346 stop_service(service, 0, true, true);
\r
1350 case SERVICE_CONTROL_CONTINUE:
\r
1351 log_service_control(service->name, control, true);
\r
1352 service->throttle = 0;
\r
1353 if (use_critical_section) imports.WakeConditionVariable(&service->throttle_condition);
\r
1355 if (! service->throttle_timer) return ERROR_CALL_NOT_IMPLEMENTED;
\r
1356 ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));
\r
1357 SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);
\r
1359 service->status.dwCurrentState = SERVICE_CONTINUE_PENDING;
\r
1360 service->status.dwWaitHint = throttle_milliseconds(service->throttle) + NSSM_WAITHINT_MARGIN;
\r
1361 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESET_THROTTLE, service->name, 0);
\r
1362 SetServiceStatus(service->status_handle, &service->status);
\r
1365 case SERVICE_CONTROL_PAUSE:
\r
1367 We don't accept pause messages but it isn't possible to register
\r
1368 only for continue messages so we have to handle this case.
\r
1370 log_service_control(service->name, control, false);
\r
1371 return ERROR_CALL_NOT_IMPLEMENTED;
\r
1373 case NSSM_SERVICE_CONTROL_ROTATE:
\r
1374 log_service_control(service->name, control, true);
\r
1375 if (service->rotate_stdout_online) service->rotate_stdout_online = NSSM_ROTATE_ONLINE_ASAP;
\r
1376 if (service->rotate_stdout_online) service->rotate_stderr_online = NSSM_ROTATE_ONLINE_ASAP;
\r
1380 /* Unknown control */
\r
1381 log_service_control(service->name, control, false);
\r
1382 return ERROR_CALL_NOT_IMPLEMENTED;
\r
1385 /* Start the service */
\r
1386 int start_service(nssm_service_t *service) {
\r
1387 service->stopping = false;
\r
1388 service->allow_restart = true;
\r
1390 if (service->process_handle) return 0;
\r
1392 /* Allocate a STARTUPINFO structure for a new process */
\r
1394 ZeroMemory(&si, sizeof(si));
\r
1395 si.cb = sizeof(si);
\r
1397 /* Allocate a PROCESSINFO structure for the process */
\r
1398 PROCESS_INFORMATION pi;
\r
1399 ZeroMemory(&pi, sizeof(pi));
\r
1401 /* Get startup parameters */
\r
1402 int ret = get_parameters(service, &si);
\r
1404 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_PARAMETERS_FAILED, service->name, 0);
\r
1405 return stop_service(service, 2, true, true);
\r
1408 /* Launch executable with arguments */
\r
1409 TCHAR cmd[CMD_LENGTH];
\r
1410 if (_sntprintf_s(cmd, _countof(cmd), _TRUNCATE, _T("\"%s\" %s"), service->exe, service->flags) < 0) {
\r
1411 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("command line"), _T("start_service"), 0);
\r
1412 close_output_handles(&si);
\r
1413 return stop_service(service, 2, true, true);
\r
1416 throttle_restart(service);
\r
1418 bool inherit_handles = false;
\r
1419 if (si.dwFlags & STARTF_USESTDHANDLES) inherit_handles = true;
\r
1420 unsigned long flags = service->priority & priority_mask();
\r
1421 if (service->affinity) flags |= CREATE_SUSPENDED;
\r
1423 flags |= CREATE_UNICODE_ENVIRONMENT;
\r
1425 if (! CreateProcess(0, cmd, 0, 0, inherit_handles, flags, service->env, service->dir, &si, &pi)) {
\r
1426 unsigned long exitcode = 3;
\r
1427 unsigned long error = GetLastError();
\r
1428 if (error == ERROR_INVALID_PARAMETER && service->env) {
\r
1429 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED_INVALID_ENVIRONMENT, service->name, service->exe, NSSM_REG_ENV, 0);
\r
1430 if (test_environment(service->env)) exitcode = 4;
\r
1432 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service->name, service->exe, error_string(error), 0);
\r
1433 close_output_handles(&si);
\r
1434 return stop_service(service, exitcode, true, true);
\r
1436 service->process_handle = pi.hProcess;
\r
1437 service->pid = pi.dwProcessId;
\r
1439 if (get_process_creation_time(service->process_handle, &service->creation_time)) ZeroMemory(&service->creation_time, sizeof(service->creation_time));
\r
1441 close_output_handles(&si, ! service->rotate_stdout_online, ! service->rotate_stderr_online);
\r
1443 if (service->affinity) {
\r
1445 We are explicitly storing service->affinity as a 64-bit unsigned integer
\r
1446 so that we can parse it regardless of whether we're running in 32-bit
\r
1447 or 64-bit mode. The arguments to SetProcessAffinityMask(), however, are
\r
1448 defined as type DWORD_PTR and hence limited to 32 bits on a 32-bit system
\r
1449 (or when running the 32-bit NSSM).
\r
1451 The result is a lot of seemingly-unnecessary casting throughout the code
\r
1452 and potentially confusion when we actually try to start the service.
\r
1453 Having said that, however, it's unlikely that we're actually going to
\r
1454 run in 32-bit mode on a system which has more than 32 CPUs so the
\r
1455 likelihood of seeing a confusing situation is somewhat diminished.
\r
1457 DWORD_PTR affinity, system_affinity;
\r
1459 if (GetProcessAffinityMask(service->process_handle, &affinity, &system_affinity)) affinity = service->affinity & system_affinity;
\r
1461 affinity = (DWORD_PTR) service->affinity;
\r
1462 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GETPROCESSAFFINITYMASK_FAILED, service->name, error_string(GetLastError()), 0);
\r
1465 if (! SetProcessAffinityMask(service->process_handle, affinity)) {
\r
1466 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_SETPROCESSAFFINITYMASK_FAILED, service->name, error_string(GetLastError()), 0);
\r
1469 ResumeThread(pi.hThread);
\r
1473 Wait for a clean startup before changing the service status to RUNNING
\r
1474 but be mindful of the fact that we are blocking the service control manager
\r
1475 so abandon the wait before too much time has elapsed.
\r
1477 unsigned long delay = service->throttle_delay;
\r
1478 if (delay > NSSM_SERVICE_STATUS_DEADLINE) {
\r
1479 TCHAR delay_milliseconds[16];
\r
1480 _sntprintf_s(delay_milliseconds, _countof(delay_milliseconds), _TRUNCATE, _T("%lu"), delay);
\r
1481 TCHAR deadline_milliseconds[16];
\r
1482 _sntprintf_s(deadline_milliseconds, _countof(deadline_milliseconds), _TRUNCATE, _T("%lu"), NSSM_SERVICE_STATUS_DEADLINE);
\r
1483 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_STARTUP_DELAY_TOO_LONG, service->name, delay_milliseconds, NSSM, deadline_milliseconds, 0);
\r
1484 delay = NSSM_SERVICE_STATUS_DEADLINE;
\r
1486 unsigned long deadline = WaitForSingleObject(service->process_handle, delay);
\r
1488 /* Signal successful start */
\r
1489 service->status.dwCurrentState = SERVICE_RUNNING;
\r
1490 SetServiceStatus(service->status_handle, &service->status);
\r
1492 /* Continue waiting for a clean startup. */
\r
1493 if (deadline == WAIT_TIMEOUT) {
\r
1494 if (service->throttle_delay > delay) {
\r
1495 if (WaitForSingleObject(service->process_handle, service->throttle_delay - delay) == WAIT_TIMEOUT) service->throttle = 0;
\r
1497 else service->throttle = 0;
\r
1500 /* Ensure the restart delay is always applied. */
\r
1501 if (service->restart_delay && ! service->throttle) service->throttle++;
\r
1506 /* Stop the service */
\r
1507 int stop_service(nssm_service_t *service, unsigned long exitcode, bool graceful, bool default_action) {
\r
1508 service->allow_restart = false;
\r
1509 if (service->wait_handle) {
\r
1510 UnregisterWait(service->wait_handle);
\r
1511 service->wait_handle = 0;
\r
1514 if (default_action && ! exitcode && ! graceful) {
\r
1515 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
1519 /* Signal we are stopping */
\r
1521 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
1522 service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;
\r
1523 SetServiceStatus(service->status_handle, &service->status);
\r
1526 /* Nothing to do if service isn't running */
\r
1527 if (service->pid) {
\r
1528 /* Shut down service */
\r
1529 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_TERMINATEPROCESS, service->name, service->exe, 0);
\r
1530 kill_process(service, service->process_handle, service->pid, 0);
\r
1532 else log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_PROCESS_ALREADY_STOPPED, service->name, service->exe, 0);
\r
1534 end_service((void *) service, true);
\r
1536 /* Signal we stopped */
\r
1538 service->status.dwCurrentState = SERVICE_STOPPED;
\r
1540 service->status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
\r
1541 service->status.dwServiceSpecificExitCode = exitcode;
\r
1544 service->status.dwWin32ExitCode = NO_ERROR;
\r
1545 service->status.dwServiceSpecificExitCode = 0;
\r
1547 SetServiceStatus(service->status_handle, &service->status);
\r
1553 /* Callback function triggered when the server exits */
\r
1554 void CALLBACK end_service(void *arg, unsigned char why) {
\r
1555 nssm_service_t *service = (nssm_service_t *) arg;
\r
1557 if (service->stopping) return;
\r
1559 service->stopping = true;
\r
1561 /* Check exit code */
\r
1562 unsigned long exitcode = 0;
\r
1564 if (service->process_handle) {
\r
1565 GetExitCodeProcess(service->process_handle, &exitcode);
\r
1566 if (exitcode == STILL_ACTIVE || get_process_exit_time(service->process_handle, &service->exit_time)) GetSystemTimeAsFileTime(&service->exit_time);
\r
1567 CloseHandle(service->process_handle);
\r
1569 else GetSystemTimeAsFileTime(&service->exit_time);
\r
1571 service->process_handle = 0;
\r
1574 Log that the service ended BEFORE logging about killing the process
\r
1575 tree. See below for the possible values of the why argument.
\r
1578 _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), exitcode);
\r
1579 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ENDED_SERVICE, service->exe, service->name, code, 0);
\r
1583 if (exitcode == STILL_ACTIVE) exitcode = 0;
\r
1584 if (service->pid) kill_process_tree(service, service->pid, exitcode, service->pid);
\r
1588 The why argument is true if our wait timed out or false otherwise.
\r
1589 Our wait is infinite so why will never be true when called by the system.
\r
1590 If it is indeed true, assume we were called from stop_service() because
\r
1591 this is a controlled shutdown, and don't take any restart action.
\r
1594 if (! service->allow_restart) return;
\r
1596 /* What action should we take? */
\r
1597 int action = NSSM_EXIT_RESTART;
\r
1598 TCHAR action_string[ACTION_LEN];
\r
1599 bool default_action;
\r
1600 if (! get_exit_action(service->name, &exitcode, action_string, &default_action)) {
\r
1601 for (int i = 0; exit_action_strings[i]; i++) {
\r
1602 if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
1610 /* Try to restart the service or return failure code to service manager */
\r
1611 case NSSM_EXIT_RESTART:
\r
1612 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_RESTART, service->name, code, exit_action_strings[action], service->exe, 0);
\r
1613 while (monitor_service(service)) {
\r
1614 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_RESTART_SERVICE_FAILED, service->exe, service->name, 0);
\r
1619 /* Do nothing, just like srvany would */
\r
1620 case NSSM_EXIT_IGNORE:
\r
1621 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_IGNORE, service->name, code, exit_action_strings[action], service->exe, 0);
\r
1625 /* Tell the service manager we are finished */
\r
1626 case NSSM_EXIT_REALLY:
\r
1627 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_REALLY, service->name, code, exit_action_strings[action], 0);
\r
1628 stop_service(service, exitcode, true, default_action);
\r
1631 /* Fake a crash so pre-Vista service managers will run recovery actions. */
\r
1632 case NSSM_EXIT_UNCLEAN:
\r
1633 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_UNCLEAN, service->name, code, exit_action_strings[action], 0);
\r
1634 stop_service(service, exitcode, false, default_action);
\r
1641 void throttle_restart(nssm_service_t *service) {
\r
1642 /* This can't be a restart if the service is already running. */
\r
1643 if (! service->throttle++) return;
\r
1646 unsigned long throttle_ms = throttle_milliseconds(service->throttle);
\r
1647 TCHAR threshold[8], milliseconds[8];
\r
1649 if (service->restart_delay > throttle_ms) ms = service->restart_delay;
\r
1650 else ms = throttle_ms;
\r
1652 if (service->throttle > 7) service->throttle = 8;
\r
1654 _sntprintf_s(milliseconds, _countof(milliseconds), _TRUNCATE, _T("%lu"), ms);
\r
1656 if (service->throttle == 1 && service->restart_delay > throttle_ms) log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESTART_DELAY, service->name, milliseconds, 0);
\r
1658 _sntprintf_s(threshold, _countof(threshold), _TRUNCATE, _T("%lu"), service->throttle_delay);
\r
1659 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_THROTTLED, service->name, threshold, milliseconds, 0);
\r
1662 if (use_critical_section) EnterCriticalSection(&service->throttle_section);
\r
1663 else if (service->throttle_timer) {
\r
1664 ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));
\r
1665 service->throttle_duetime.QuadPart = 0 - (ms * 10000LL);
\r
1666 SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);
\r
1669 service->status.dwCurrentState = SERVICE_PAUSED;
\r
1670 SetServiceStatus(service->status_handle, &service->status);
\r
1672 if (use_critical_section) {
\r
1673 imports.SleepConditionVariableCS(&service->throttle_condition, &service->throttle_section, ms);
\r
1674 LeaveCriticalSection(&service->throttle_section);
\r
1677 if (service->throttle_timer) WaitForSingleObject(service->throttle_timer, INFINITE);
\r
1683 When responding to a stop (or any other) request we need to set dwWaitHint to
\r
1684 the number of milliseconds we expect the operation to take, and optionally
\r
1685 increase dwCheckPoint. If dwWaitHint milliseconds elapses without the
\r
1686 operation completing or dwCheckPoint increasing, the system will consider the
\r
1687 service to be hung.
\r
1689 However the system will consider the service to be hung after 30000
\r
1690 milliseconds regardless of the value of dwWaitHint if dwCheckPoint has not
\r
1691 changed. Therefore if we want to wait longer than that we must periodically
\r
1692 increase dwCheckPoint.
\r
1694 Furthermore, it will consider the service to be hung after 60000 milliseconds
\r
1695 regardless of the value of dwCheckPoint unless dwWaitHint is increased every
\r
1696 time dwCheckPoint is also increased.
\r
1698 Our strategy then is to retrieve the initial dwWaitHint and wait for
\r
1699 NSSM_SERVICE_STATUS_DEADLINE milliseconds. If the process is still running
\r
1700 and we haven't finished waiting we increment dwCheckPoint and add whichever is
\r
1701 smaller of NSSM_SERVICE_STATUS_DEADLINE or the remaining timeout to
\r
1704 Only doing both these things will prevent the system from killing the service.
\r
1706 Returns: 1 if the wait timed out.
\r
1707 0 if the wait completed.
\r
1710 int await_shutdown(nssm_service_t *service, TCHAR *function_name, unsigned long timeout) {
\r
1711 unsigned long interval;
\r
1712 unsigned long waithint;
\r
1713 unsigned long ret;
\r
1714 unsigned long waited;
\r
1715 TCHAR interval_milliseconds[16];
\r
1716 TCHAR timeout_milliseconds[16];
\r
1717 TCHAR waited_milliseconds[16];
\r
1718 TCHAR *function = function_name;
\r
1720 /* Add brackets to function name. */
\r
1721 size_t funclen = _tcslen(function_name) + 3;
\r
1722 TCHAR *func = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, funclen * sizeof(TCHAR));
\r
1724 if (_sntprintf_s(func, funclen, _TRUNCATE, _T("%s()"), function_name) > -1) function = func;
\r
1727 _sntprintf_s(timeout_milliseconds, _countof(timeout_milliseconds), _TRUNCATE, _T("%lu"), timeout);
\r
1729 waithint = service->status.dwWaitHint;
\r
1731 while (waited < timeout) {
\r
1732 interval = timeout - waited;
\r
1733 if (interval > NSSM_SERVICE_STATUS_DEADLINE) interval = NSSM_SERVICE_STATUS_DEADLINE;
\r
1735 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
1736 service->status.dwWaitHint += interval;
\r
1737 service->status.dwCheckPoint++;
\r
1738 SetServiceStatus(service->status_handle, &service->status);
\r
1741 _sntprintf_s(waited_milliseconds, _countof(waited_milliseconds), _TRUNCATE, _T("%lu"), waited);
\r
1742 _sntprintf_s(interval_milliseconds, _countof(interval_milliseconds), _TRUNCATE, _T("%lu"), interval);
\r
1743 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_AWAITING_SHUTDOWN, function, service->name, waited_milliseconds, interval_milliseconds, timeout_milliseconds, 0);
\r
1746 switch (WaitForSingleObject(service->process_handle, interval)) {
\r
1747 case WAIT_OBJECT_0:
\r
1751 case WAIT_TIMEOUT:
\r
1760 waited += interval;
\r
1764 if (func) HeapFree(GetProcessHeap(), 0, func);
\r