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 == 0) {
\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 0: 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 void log_service_control(TCHAR *service_name, unsigned long control, bool handled) {
\r
1296 TCHAR *text = service_control_text(control);
\r
1297 unsigned long event;
\r
1300 /* "0x" + 8 x hex + NULL */
\r
1301 text = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, 11 * sizeof(TCHAR));
\r
1303 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);
\r
1306 if (_sntprintf_s(text, 11, _TRUNCATE, _T("0x%08x"), control) < 0) {
\r
1307 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);
\r
1308 HeapFree(GetProcessHeap(), 0, text);
\r
1312 event = NSSM_EVENT_SERVICE_CONTROL_UNKNOWN;
\r
1314 else if (handled) event = NSSM_EVENT_SERVICE_CONTROL_HANDLED;
\r
1315 else event = NSSM_EVENT_SERVICE_CONTROL_NOT_HANDLED;
\r
1317 log_event(EVENTLOG_INFORMATION_TYPE, event, service_name, text, 0);
\r
1319 if (event == NSSM_EVENT_SERVICE_CONTROL_UNKNOWN) {
\r
1320 HeapFree(GetProcessHeap(), 0, text);
\r
1324 /* Service control handler */
\r
1325 unsigned long WINAPI service_control_handler(unsigned long control, unsigned long event, void *data, void *context) {
\r
1326 nssm_service_t *service = (nssm_service_t *) context;
\r
1328 switch (control) {
\r
1329 case SERVICE_CONTROL_INTERROGATE:
\r
1330 /* We always keep the service status up-to-date so this is a no-op. */
\r
1333 case SERVICE_CONTROL_SHUTDOWN:
\r
1334 case SERVICE_CONTROL_STOP:
\r
1335 log_service_control(service->name, control, true);
\r
1337 We MUST acknowledge the stop request promptly but we're committed to
\r
1338 waiting for the application to exit. Spawn a new thread to wait
\r
1339 while we acknowledge the request.
\r
1341 if (! CreateThread(NULL, 0, shutdown_service, context, 0, NULL)) {
\r
1342 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETHREAD_FAILED, error_string(GetLastError()), 0);
\r
1345 We couldn't create a thread to tidy up so we'll have to force the tidyup
\r
1346 to complete in time in this thread.
\r
1348 service->kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;
\r
1349 service->kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;
\r
1350 service->kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;
\r
1352 stop_service(service, 0, true, true);
\r
1356 case SERVICE_CONTROL_CONTINUE:
\r
1357 log_service_control(service->name, control, true);
\r
1358 service->throttle = 0;
\r
1359 if (use_critical_section) imports.WakeConditionVariable(&service->throttle_condition);
\r
1361 if (! service->throttle_timer) return ERROR_CALL_NOT_IMPLEMENTED;
\r
1362 ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));
\r
1363 SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);
\r
1365 service->status.dwCurrentState = SERVICE_CONTINUE_PENDING;
\r
1366 service->status.dwWaitHint = throttle_milliseconds(service->throttle) + NSSM_WAITHINT_MARGIN;
\r
1367 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESET_THROTTLE, service->name, 0);
\r
1368 SetServiceStatus(service->status_handle, &service->status);
\r
1371 case SERVICE_CONTROL_PAUSE:
\r
1373 We don't accept pause messages but it isn't possible to register
\r
1374 only for continue messages so we have to handle this case.
\r
1376 log_service_control(service->name, control, false);
\r
1377 return ERROR_CALL_NOT_IMPLEMENTED;
\r
1379 case NSSM_SERVICE_CONTROL_ROTATE:
\r
1380 log_service_control(service->name, control, true);
\r
1381 if (service->rotate_stdout_online) service->rotate_stdout_online = NSSM_ROTATE_ONLINE_ASAP;
\r
1382 if (service->rotate_stdout_online) service->rotate_stderr_online = NSSM_ROTATE_ONLINE_ASAP;
\r
1386 /* Unknown control */
\r
1387 log_service_control(service->name, control, false);
\r
1388 return ERROR_CALL_NOT_IMPLEMENTED;
\r
1391 /* Start the service */
\r
1392 int start_service(nssm_service_t *service) {
\r
1393 service->stopping = false;
\r
1394 service->allow_restart = true;
\r
1396 if (service->process_handle) return 0;
\r
1398 /* Allocate a STARTUPINFO structure for a new process */
\r
1400 ZeroMemory(&si, sizeof(si));
\r
1401 si.cb = sizeof(si);
\r
1403 /* Allocate a PROCESSINFO structure for the process */
\r
1404 PROCESS_INFORMATION pi;
\r
1405 ZeroMemory(&pi, sizeof(pi));
\r
1407 /* Get startup parameters */
\r
1408 int ret = get_parameters(service, &si);
\r
1410 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_PARAMETERS_FAILED, service->name, 0);
\r
1411 return stop_service(service, 2, true, true);
\r
1414 /* Launch executable with arguments */
\r
1415 TCHAR cmd[CMD_LENGTH];
\r
1416 if (_sntprintf_s(cmd, _countof(cmd), _TRUNCATE, _T("\"%s\" %s"), service->exe, service->flags) < 0) {
\r
1417 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("command line"), _T("start_service"), 0);
\r
1418 close_output_handles(&si);
\r
1419 return stop_service(service, 2, true, true);
\r
1422 throttle_restart(service);
\r
1424 bool inherit_handles = false;
\r
1425 if (si.dwFlags & STARTF_USESTDHANDLES) inherit_handles = true;
\r
1426 unsigned long flags = service->priority & priority_mask();
\r
1427 if (service->affinity) flags |= CREATE_SUSPENDED;
\r
1429 flags |= CREATE_UNICODE_ENVIRONMENT;
\r
1431 if (! CreateProcess(0, cmd, 0, 0, inherit_handles, flags, service->env, service->dir, &si, &pi)) {
\r
1432 unsigned long exitcode = 3;
\r
1433 unsigned long error = GetLastError();
\r
1434 if (error == ERROR_INVALID_PARAMETER && service->env) {
\r
1435 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED_INVALID_ENVIRONMENT, service->name, service->exe, NSSM_REG_ENV, 0);
\r
1436 if (test_environment(service->env)) exitcode = 4;
\r
1438 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service->name, service->exe, error_string(error), 0);
\r
1439 close_output_handles(&si);
\r
1440 return stop_service(service, exitcode, true, true);
\r
1442 service->process_handle = pi.hProcess;
\r
1443 service->pid = pi.dwProcessId;
\r
1445 if (get_process_creation_time(service->process_handle, &service->creation_time)) ZeroMemory(&service->creation_time, sizeof(service->creation_time));
\r
1447 close_output_handles(&si, ! service->rotate_stdout_online, ! service->rotate_stderr_online);
\r
1449 if (service->affinity) {
\r
1451 We are explicitly storing service->affinity as a 64-bit unsigned integer
\r
1452 so that we can parse it regardless of whether we're running in 32-bit
\r
1453 or 64-bit mode. The arguments to SetProcessAffinityMask(), however, are
\r
1454 defined as type DWORD_PTR and hence limited to 32 bits on a 32-bit system
\r
1455 (or when running the 32-bit NSSM).
\r
1457 The result is a lot of seemingly-unnecessary casting throughout the code
\r
1458 and potentially confusion when we actually try to start the service.
\r
1459 Having said that, however, it's unlikely that we're actually going to
\r
1460 run in 32-bit mode on a system which has more than 32 CPUs so the
\r
1461 likelihood of seeing a confusing situation is somewhat diminished.
\r
1463 DWORD_PTR affinity, system_affinity;
\r
1465 if (GetProcessAffinityMask(service->process_handle, &affinity, &system_affinity)) affinity = service->affinity & system_affinity;
\r
1467 affinity = (DWORD_PTR) service->affinity;
\r
1468 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GETPROCESSAFFINITYMASK_FAILED, service->name, error_string(GetLastError()), 0);
\r
1471 if (! SetProcessAffinityMask(service->process_handle, affinity)) {
\r
1472 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_SETPROCESSAFFINITYMASK_FAILED, service->name, error_string(GetLastError()), 0);
\r
1475 ResumeThread(pi.hThread);
\r
1479 Wait for a clean startup before changing the service status to RUNNING
\r
1480 but be mindful of the fact that we are blocking the service control manager
\r
1481 so abandon the wait before too much time has elapsed.
\r
1483 unsigned long delay = service->throttle_delay;
\r
1484 if (delay > NSSM_SERVICE_STATUS_DEADLINE) {
\r
1485 TCHAR delay_milliseconds[16];
\r
1486 _sntprintf_s(delay_milliseconds, _countof(delay_milliseconds), _TRUNCATE, _T("%lu"), delay);
\r
1487 TCHAR deadline_milliseconds[16];
\r
1488 _sntprintf_s(deadline_milliseconds, _countof(deadline_milliseconds), _TRUNCATE, _T("%lu"), NSSM_SERVICE_STATUS_DEADLINE);
\r
1489 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_STARTUP_DELAY_TOO_LONG, service->name, delay_milliseconds, NSSM, deadline_milliseconds, 0);
\r
1490 delay = NSSM_SERVICE_STATUS_DEADLINE;
\r
1492 unsigned long deadline = WaitForSingleObject(service->process_handle, delay);
\r
1494 /* Signal successful start */
\r
1495 service->status.dwCurrentState = SERVICE_RUNNING;
\r
1496 SetServiceStatus(service->status_handle, &service->status);
\r
1498 /* Continue waiting for a clean startup. */
\r
1499 if (deadline == WAIT_TIMEOUT) {
\r
1500 if (service->throttle_delay > delay) {
\r
1501 if (WaitForSingleObject(service->process_handle, service->throttle_delay - delay) == WAIT_TIMEOUT) service->throttle = 0;
\r
1503 else service->throttle = 0;
\r
1506 /* Ensure the restart delay is always applied. */
\r
1507 if (service->restart_delay && ! service->throttle) service->throttle++;
\r
1512 /* Stop the service */
\r
1513 int stop_service(nssm_service_t *service, unsigned long exitcode, bool graceful, bool default_action) {
\r
1514 service->allow_restart = false;
\r
1515 if (service->wait_handle) {
\r
1516 UnregisterWait(service->wait_handle);
\r
1517 service->wait_handle = 0;
\r
1520 if (default_action && ! exitcode && ! graceful) {
\r
1521 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
1525 /* Signal we are stopping */
\r
1527 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
1528 service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;
\r
1529 SetServiceStatus(service->status_handle, &service->status);
\r
1532 /* Nothing to do if service isn't running */
\r
1533 if (service->pid) {
\r
1534 /* Shut down service */
\r
1535 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_TERMINATEPROCESS, service->name, service->exe, 0);
\r
1536 kill_process(service, service->process_handle, service->pid, 0);
\r
1538 else log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_PROCESS_ALREADY_STOPPED, service->name, service->exe, 0);
\r
1540 end_service((void *) service, true);
\r
1542 /* Signal we stopped */
\r
1544 service->status.dwCurrentState = SERVICE_STOPPED;
\r
1546 service->status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
\r
1547 service->status.dwServiceSpecificExitCode = exitcode;
\r
1550 service->status.dwWin32ExitCode = NO_ERROR;
\r
1551 service->status.dwServiceSpecificExitCode = 0;
\r
1553 SetServiceStatus(service->status_handle, &service->status);
\r
1559 /* Callback function triggered when the server exits */
\r
1560 void CALLBACK end_service(void *arg, unsigned char why) {
\r
1561 nssm_service_t *service = (nssm_service_t *) arg;
\r
1563 if (service->stopping) return;
\r
1565 service->stopping = true;
\r
1567 /* Check exit code */
\r
1568 unsigned long exitcode = 0;
\r
1570 if (service->process_handle) {
\r
1571 GetExitCodeProcess(service->process_handle, &exitcode);
\r
1572 if (exitcode == STILL_ACTIVE || get_process_exit_time(service->process_handle, &service->exit_time)) GetSystemTimeAsFileTime(&service->exit_time);
\r
1573 CloseHandle(service->process_handle);
\r
1575 else GetSystemTimeAsFileTime(&service->exit_time);
\r
1577 service->process_handle = 0;
\r
1580 Log that the service ended BEFORE logging about killing the process
\r
1581 tree. See below for the possible values of the why argument.
\r
1584 _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), exitcode);
\r
1585 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ENDED_SERVICE, service->exe, service->name, code, 0);
\r
1589 if (exitcode == STILL_ACTIVE) exitcode = 0;
\r
1590 if (service->pid) kill_process_tree(service, service->pid, exitcode, service->pid);
\r
1594 The why argument is true if our wait timed out or false otherwise.
\r
1595 Our wait is infinite so why will never be true when called by the system.
\r
1596 If it is indeed true, assume we were called from stop_service() because
\r
1597 this is a controlled shutdown, and don't take any restart action.
\r
1600 if (! service->allow_restart) return;
\r
1602 /* What action should we take? */
\r
1603 int action = NSSM_EXIT_RESTART;
\r
1604 TCHAR action_string[ACTION_LEN];
\r
1605 bool default_action;
\r
1606 if (! get_exit_action(service->name, &exitcode, action_string, &default_action)) {
\r
1607 for (int i = 0; exit_action_strings[i]; i++) {
\r
1608 if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
1616 /* Try to restart the service or return failure code to service manager */
\r
1617 case NSSM_EXIT_RESTART:
\r
1618 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_RESTART, service->name, code, exit_action_strings[action], service->exe, 0);
\r
1619 while (monitor_service(service)) {
\r
1620 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_RESTART_SERVICE_FAILED, service->exe, service->name, 0);
\r
1625 /* Do nothing, just like srvany would */
\r
1626 case NSSM_EXIT_IGNORE:
\r
1627 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_IGNORE, service->name, code, exit_action_strings[action], service->exe, 0);
\r
1631 /* Tell the service manager we are finished */
\r
1632 case NSSM_EXIT_REALLY:
\r
1633 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_REALLY, service->name, code, exit_action_strings[action], 0);
\r
1634 stop_service(service, exitcode, true, default_action);
\r
1637 /* Fake a crash so pre-Vista service managers will run recovery actions. */
\r
1638 case NSSM_EXIT_UNCLEAN:
\r
1639 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_UNCLEAN, service->name, code, exit_action_strings[action], 0);
\r
1640 stop_service(service, exitcode, false, default_action);
\r
1647 void throttle_restart(nssm_service_t *service) {
\r
1648 /* This can't be a restart if the service is already running. */
\r
1649 if (! service->throttle++) return;
\r
1652 unsigned long throttle_ms = throttle_milliseconds(service->throttle);
\r
1653 TCHAR threshold[8], milliseconds[8];
\r
1655 if (service->restart_delay > throttle_ms) ms = service->restart_delay;
\r
1656 else ms = throttle_ms;
\r
1658 if (service->throttle > 7) service->throttle = 8;
\r
1660 _sntprintf_s(milliseconds, _countof(milliseconds), _TRUNCATE, _T("%lu"), ms);
\r
1662 if (service->throttle == 1 && service->restart_delay > throttle_ms) log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESTART_DELAY, service->name, milliseconds, 0);
\r
1664 _sntprintf_s(threshold, _countof(threshold), _TRUNCATE, _T("%lu"), service->throttle_delay);
\r
1665 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_THROTTLED, service->name, threshold, milliseconds, 0);
\r
1668 if (use_critical_section) EnterCriticalSection(&service->throttle_section);
\r
1669 else if (service->throttle_timer) {
\r
1670 ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));
\r
1671 service->throttle_duetime.QuadPart = 0 - (ms * 10000LL);
\r
1672 SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);
\r
1675 service->status.dwCurrentState = SERVICE_PAUSED;
\r
1676 SetServiceStatus(service->status_handle, &service->status);
\r
1678 if (use_critical_section) {
\r
1679 imports.SleepConditionVariableCS(&service->throttle_condition, &service->throttle_section, ms);
\r
1680 LeaveCriticalSection(&service->throttle_section);
\r
1683 if (service->throttle_timer) WaitForSingleObject(service->throttle_timer, INFINITE);
\r
1689 When responding to a stop (or any other) request we need to set dwWaitHint to
\r
1690 the number of milliseconds we expect the operation to take, and optionally
\r
1691 increase dwCheckPoint. If dwWaitHint milliseconds elapses without the
\r
1692 operation completing or dwCheckPoint increasing, the system will consider the
\r
1693 service to be hung.
\r
1695 However the system will consider the service to be hung after 30000
\r
1696 milliseconds regardless of the value of dwWaitHint if dwCheckPoint has not
\r
1697 changed. Therefore if we want to wait longer than that we must periodically
\r
1698 increase dwCheckPoint.
\r
1700 Furthermore, it will consider the service to be hung after 60000 milliseconds
\r
1701 regardless of the value of dwCheckPoint unless dwWaitHint is increased every
\r
1702 time dwCheckPoint is also increased.
\r
1704 Our strategy then is to retrieve the initial dwWaitHint and wait for
\r
1705 NSSM_SERVICE_STATUS_DEADLINE milliseconds. If the process is still running
\r
1706 and we haven't finished waiting we increment dwCheckPoint and add whichever is
\r
1707 smaller of NSSM_SERVICE_STATUS_DEADLINE or the remaining timeout to
\r
1710 Only doing both these things will prevent the system from killing the service.
\r
1712 Returns: 1 if the wait timed out.
\r
1713 0 if the wait completed.
\r
1716 int await_shutdown(nssm_service_t *service, TCHAR *function_name, unsigned long timeout) {
\r
1717 unsigned long interval;
\r
1718 unsigned long waithint;
\r
1719 unsigned long ret;
\r
1720 unsigned long waited;
\r
1721 TCHAR interval_milliseconds[16];
\r
1722 TCHAR timeout_milliseconds[16];
\r
1723 TCHAR waited_milliseconds[16];
\r
1724 TCHAR *function = function_name;
\r
1726 /* Add brackets to function name. */
\r
1727 size_t funclen = _tcslen(function_name) + 3;
\r
1728 TCHAR *func = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, funclen * sizeof(TCHAR));
\r
1730 if (_sntprintf_s(func, funclen, _TRUNCATE, _T("%s()"), function_name) > -1) function = func;
\r
1733 _sntprintf_s(timeout_milliseconds, _countof(timeout_milliseconds), _TRUNCATE, _T("%lu"), timeout);
\r
1735 waithint = service->status.dwWaitHint;
\r
1737 while (waited < timeout) {
\r
1738 interval = timeout - waited;
\r
1739 if (interval > NSSM_SERVICE_STATUS_DEADLINE) interval = NSSM_SERVICE_STATUS_DEADLINE;
\r
1741 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
1742 service->status.dwWaitHint += interval;
\r
1743 service->status.dwCheckPoint++;
\r
1744 SetServiceStatus(service->status_handle, &service->status);
\r
1747 _sntprintf_s(waited_milliseconds, _countof(waited_milliseconds), _TRUNCATE, _T("%lu"), waited);
\r
1748 _sntprintf_s(interval_milliseconds, _countof(interval_milliseconds), _TRUNCATE, _T("%lu"), interval);
\r
1749 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_AWAITING_SHUTDOWN, function, service->name, waited_milliseconds, interval_milliseconds, timeout_milliseconds, 0);
\r
1752 switch (WaitForSingleObject(service->process_handle, interval)) {
\r
1753 case WAIT_OBJECT_0:
\r
1757 case WAIT_TIMEOUT:
\r
1766 waited += interval;
\r
1770 if (func) HeapFree(GetProcessHeap(), 0, func);
\r