3 /* This is explicitly a wide string. */
\r
4 #define NSSM_LOGON_AS_SERVICE_RIGHT L"SeServiceLogonRight"
\r
6 extern const TCHAR *exit_action_strings[];
\r
9 bool use_critical_section;
\r
11 extern imports_t imports;
\r
13 const TCHAR *exit_action_strings[] = { _T("Restart"), _T("Ignore"), _T("Exit"), _T("Suicide"), 0 };
\r
15 static inline int throttle_milliseconds(unsigned long throttle) {
\r
16 /* pow() operates on doubles. */
\r
17 int ret = 1; for (unsigned long i = 1; i < throttle; i++) ret *= 2;
\r
22 Wrapper to be called in a new thread so that we can acknowledge a STOP
\r
23 control immediately.
\r
25 static unsigned long WINAPI shutdown_service(void *arg) {
\r
26 return stop_service((nssm_service_t *) arg, 0, true, true);
\r
29 /* Connect to the service manager */
\r
30 SC_HANDLE open_service_manager() {
\r
31 SC_HANDLE ret = OpenSCManager(0, SERVICES_ACTIVE_DATABASE, SC_MANAGER_ALL_ACCESS);
\r
33 if (is_admin) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENSCMANAGER_FAILED, 0);
\r
40 QUERY_SERVICE_CONFIG *query_service_config(const TCHAR *service_name, SC_HANDLE service_handle) {
\r
41 QUERY_SERVICE_CONFIG *qsc;
\r
42 unsigned long bufsize;
\r
43 unsigned long error;
\r
45 QueryServiceConfig(service_handle, 0, 0, &bufsize);
\r
46 error = GetLastError();
\r
47 if (error == ERROR_INSUFFICIENT_BUFFER) {
\r
48 qsc = (QUERY_SERVICE_CONFIG *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, bufsize);
\r
50 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("QUERY_SERVICE_CONFIG"), _T("query_service_config()"), 0);
\r
55 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG_FAILED, service_name, error_string(error), 0);
\r
59 if (! QueryServiceConfig(service_handle, qsc, bufsize, &bufsize)) {
\r
60 HeapFree(GetProcessHeap(), 0, qsc);
\r
61 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG_FAILED, service_name, error_string(GetLastError()), 0);
\r
68 int get_service_startup(const TCHAR *service_name, SC_HANDLE service_handle, const QUERY_SERVICE_CONFIG *qsc, unsigned long *startup) {
\r
69 if (! qsc) return 1;
\r
71 switch (qsc->dwStartType) {
\r
72 case SERVICE_DEMAND_START: *startup = NSSM_STARTUP_MANUAL; break;
\r
73 case SERVICE_DISABLED: *startup = NSSM_STARTUP_DISABLED; break;
\r
74 default: *startup = NSSM_STARTUP_AUTOMATIC;
\r
77 if (*startup != NSSM_STARTUP_AUTOMATIC) return 0;
\r
79 /* Check for delayed start. */
\r
80 unsigned long bufsize;
\r
81 unsigned long error;
\r
82 QueryServiceConfig2(service_handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, 0, 0, &bufsize);
\r
83 error = GetLastError();
\r
84 if (error == ERROR_INSUFFICIENT_BUFFER) {
\r
85 SERVICE_DELAYED_AUTO_START_INFO *info = (SERVICE_DELAYED_AUTO_START_INFO *) HeapAlloc(GetProcessHeap(), 0, bufsize);
\r
87 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("SERVICE_DELAYED_AUTO_START_INFO"), _T("get_service_startup()"));
\r
91 if (QueryServiceConfig2(service_handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, (unsigned char *) info, bufsize, &bufsize)) {
\r
92 if (info->fDelayedAutostart) *startup = NSSM_STARTUP_DELAYED;
\r
93 HeapFree(GetProcessHeap(), 0, info);
\r
97 error = GetLastError();
\r
98 if (error != ERROR_INVALID_LEVEL) {
\r
99 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_CONFIG_DELAYED_AUTO_START_INFO"), error_string(error));
\r
104 else if (error != ERROR_INVALID_LEVEL) {
\r
105 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_DELAYED_AUTO_START_INFO"), error_string(error));
\r
112 static int grant_logon_as_service(const TCHAR *username) {
\r
113 if (str_equiv(username, NSSM_LOCALSYSTEM_ACCOUNT)) return 0;
\r
115 /* Open Policy object. */
\r
116 LSA_OBJECT_ATTRIBUTES attributes;
\r
117 ZeroMemory(&attributes, sizeof(attributes));
\r
121 NTSTATUS status = LsaOpenPolicy(0, &attributes, POLICY_ALL_ACCESS, &policy);
\r
123 print_message(stderr, NSSM_MESSAGE_LSAOPENPOLICY_FAILED, error_string(LsaNtStatusToWinError(status)));
\r
127 /* Look up SID for the account. */
\r
128 LSA_UNICODE_STRING lsa_username;
\r
130 lsa_username.Buffer = (wchar_t *) username;
\r
131 lsa_username.Length = (unsigned short) _tcslen(username) * sizeof(TCHAR);
\r
132 lsa_username.MaximumLength = lsa_username.Length + sizeof(TCHAR);
\r
135 mbstowcs_s(&buflen, NULL, 0, username, _TRUNCATE);
\r
136 lsa_username.MaximumLength = buflen * sizeof(wchar_t);
\r
137 lsa_username.Length = lsa_username.MaximumLength - sizeof(wchar_t);
\r
138 lsa_username.Buffer = (wchar_t *) HeapAlloc(GetProcessHeap(), 0, lsa_username.MaximumLength);
\r
139 if (lsa_username.Buffer) mbstowcs_s(&buflen, lsa_username.Buffer, lsa_username.MaximumLength, username, _TRUNCATE);
\r
142 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("LSA_UNICODE_STRING"), _T("grant_logon_as_service()"));
\r
147 LSA_REFERENCED_DOMAIN_LIST *translated_domains;
\r
148 LSA_TRANSLATED_SID *translated_sid;
\r
149 status = LsaLookupNames(policy, 1, &lsa_username, &translated_domains, &translated_sid);
\r
151 HeapFree(GetProcessHeap(), 0, lsa_username.Buffer);
\r
154 LsaFreeMemory(translated_domains);
\r
155 LsaFreeMemory(translated_sid);
\r
157 print_message(stderr, NSSM_MESSAGE_LSALOOKUPNAMES_FAILED, username, error_string(LsaNtStatusToWinError(status)));
\r
161 if (translated_sid->Use != SidTypeUser) {
\r
162 LsaFreeMemory(translated_domains);
\r
163 LsaFreeMemory(translated_sid);
\r
165 print_message(stderr, NSSM_GUI_INVALID_USERNAME, username);
\r
169 LSA_TRUST_INFORMATION *trust = &translated_domains->Domains[translated_sid->DomainIndex];
\r
170 if (! trust || ! IsValidSid(trust->Sid)) {
\r
171 LsaFreeMemory(translated_domains);
\r
172 LsaFreeMemory(translated_sid);
\r
174 print_message(stderr, NSSM_GUI_INVALID_USERNAME, username);
\r
178 /* GetSidSubAuthority*() return pointers! */
\r
179 unsigned char *n = GetSidSubAuthorityCount(trust->Sid);
\r
181 /* Convert translated SID to SID. */
\r
182 SID *sid = (SID *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, GetSidLengthRequired(*n + 1));
\r
184 LsaFreeMemory(translated_domains);
\r
185 LsaFreeMemory(translated_sid);
\r
187 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("SID"), _T("grant_logon_as_service"));
\r
191 unsigned long error;
\r
192 if (! InitializeSid(sid, GetSidIdentifierAuthority(trust->Sid), *n + 1)) {
\r
193 error = GetLastError();
\r
194 HeapFree(GetProcessHeap(), 0, sid);
\r
195 LsaFreeMemory(translated_domains);
\r
196 LsaFreeMemory(translated_sid);
\r
198 print_message(stderr, NSSM_MESSAGE_INITIALIZESID_FAILED, username, error_string(error));
\r
202 for (unsigned char i = 0; i <= *n; i++) {
\r
203 unsigned long *sub = GetSidSubAuthority(sid, i);
\r
204 if (i < *n) *sub = *GetSidSubAuthority(trust->Sid, i);
\r
205 else *sub = translated_sid->RelativeId;
\r
208 LsaFreeMemory(translated_domains);
\r
209 LsaFreeMemory(translated_sid);
\r
211 /* Check if the SID has the "Log on as a service" right. */
\r
212 LSA_UNICODE_STRING lsa_right;
\r
213 lsa_right.Buffer = NSSM_LOGON_AS_SERVICE_RIGHT;
\r
214 lsa_right.Length = (unsigned short) wcslen(lsa_right.Buffer) * sizeof(wchar_t);
\r
215 lsa_right.MaximumLength = lsa_right.Length + sizeof(wchar_t);
\r
217 LSA_UNICODE_STRING *rights;
\r
218 unsigned long count = ~0;
\r
219 status = LsaEnumerateAccountRights(policy, sid, &rights, &count);
\r
222 If the account has no rights set LsaEnumerateAccountRights() will return
\r
223 STATUS_OBJECT_NAME_NOT_FOUND and set count to 0.
\r
225 error = LsaNtStatusToWinError(status);
\r
226 if (error != ERROR_FILE_NOT_FOUND) {
\r
227 HeapFree(GetProcessHeap(), 0, sid);
\r
229 print_message(stderr, NSSM_MESSAGE_LSAENUMERATEACCOUNTRIGHTS_FAILED, username, error_string(error));
\r
234 for (unsigned long i = 0; i < count; i++) {
\r
235 if (rights[i].Length != lsa_right.Length) continue;
\r
236 if (_wcsnicmp(rights[i].Buffer, lsa_right.Buffer, lsa_right.MaximumLength)) continue;
\r
237 /* The SID has the right. */
\r
238 HeapFree(GetProcessHeap(), 0, sid);
\r
239 LsaFreeMemory(rights);
\r
243 LsaFreeMemory(rights);
\r
245 /* Add the right. */
\r
246 status = LsaAddAccountRights(policy, sid, &lsa_right, 1);
\r
247 HeapFree(GetProcessHeap(), 0, sid);
\r
250 print_message(stderr, NSSM_MESSAGE_LSAADDACCOUNTRIGHTS_FAILED, error_string(LsaNtStatusToWinError(status)));
\r
254 print_message(stdout, NSSM_MESSAGE_GRANTED_LOGON_AS_SERVICE, username);
\r
258 /* Set default values which aren't zero. */
\r
259 void set_nssm_service_defaults(nssm_service_t *service) {
\r
260 if (! service) return;
\r
262 service->type = SERVICE_WIN32_OWN_PROCESS;
\r
263 service->stdin_sharing = NSSM_STDIN_SHARING;
\r
264 service->stdin_disposition = NSSM_STDIN_DISPOSITION;
\r
265 service->stdin_flags = NSSM_STDIN_FLAGS;
\r
266 service->stdout_sharing = NSSM_STDOUT_SHARING;
\r
267 service->stdout_disposition = NSSM_STDOUT_DISPOSITION;
\r
268 service->stdout_flags = NSSM_STDOUT_FLAGS;
\r
269 service->stderr_sharing = NSSM_STDERR_SHARING;
\r
270 service->stderr_disposition = NSSM_STDERR_DISPOSITION;
\r
271 service->stderr_flags = NSSM_STDERR_FLAGS;
\r
272 service->throttle_delay = NSSM_RESET_THROTTLE_RESTART;
\r
273 service->stop_method = ~0;
\r
274 service->kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;
\r
275 service->kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;
\r
276 service->kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;
\r
279 /* Allocate and zero memory for a service. */
\r
280 nssm_service_t *alloc_nssm_service() {
\r
281 nssm_service_t *service = (nssm_service_t *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(nssm_service_t));
\r
282 if (! service) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("service"), _T("alloc_nssm_service()"), 0);
\r
286 /* Free memory for a service. */
\r
287 void cleanup_nssm_service(nssm_service_t *service) {
\r
288 if (! service) return;
\r
289 if (service->username) HeapFree(GetProcessHeap(), 0, service->username);
\r
290 if (service->password) {
\r
291 SecureZeroMemory(service->password, service->passwordlen);
\r
292 HeapFree(GetProcessHeap(), 0, service->password);
\r
294 if (service->env) HeapFree(GetProcessHeap(), 0, service->env);
\r
295 if (service->env_extra) HeapFree(GetProcessHeap(), 0, service->env_extra);
\r
296 if (service->handle) CloseServiceHandle(service->handle);
\r
297 if (service->process_handle) CloseHandle(service->process_handle);
\r
298 if (service->wait_handle) UnregisterWait(service->process_handle);
\r
299 if (service->throttle_section_initialised) DeleteCriticalSection(&service->throttle_section);
\r
300 if (service->throttle_timer) CloseHandle(service->throttle_timer);
\r
301 HeapFree(GetProcessHeap(), 0, service);
\r
304 /* About to install the service */
\r
305 int pre_install_service(int argc, TCHAR **argv) {
\r
306 nssm_service_t *service = alloc_nssm_service();
\r
307 set_nssm_service_defaults(service);
\r
308 if (argc) _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]);
\r
310 /* Show the dialogue box if we didn't give the service name and path */
\r
311 if (argc < 2) return nssm_gui(IDD_INSTALL, service);
\r
314 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("service"), _T("pre_install_service()"));
\r
317 _sntprintf_s(service->exe, _countof(service->exe), _TRUNCATE, _T("%s"), argv[1]);
\r
319 /* Arguments are optional */
\r
320 size_t flagslen = 0;
\r
323 for (i = 2; i < argc; i++) flagslen += _tcslen(argv[i]) + 1;
\r
324 if (! flagslen) flagslen = 1;
\r
325 if (flagslen > _countof(service->flags)) {
\r
326 print_message(stderr, NSSM_MESSAGE_FLAGS_TOO_LONG);
\r
330 for (i = 2; i < argc; i++) {
\r
331 size_t len = _tcslen(argv[i]);
\r
332 memmove(service->flags + s, argv[i], len * sizeof(TCHAR));
\r
334 if (i < argc - 1) service->flags[s++] = _T(' ');
\r
337 /* Work out directory name */
\r
338 _sntprintf_s(service->dir, _countof(service->dir), _TRUNCATE, _T("%s"), service->exe);
\r
339 strip_basename(service->dir);
\r
341 int ret = install_service(service);
\r
342 cleanup_nssm_service(service);
\r
346 /* About to edit the service. */
\r
347 int pre_edit_service(int argc, TCHAR **argv) {
\r
348 /* Require service name. */
\r
349 if (argc < 1) return usage(1);
\r
351 nssm_service_t *service = alloc_nssm_service();
\r
352 _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]);
\r
354 /* Open service manager */
\r
355 SC_HANDLE services = open_service_manager();
\r
357 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
361 /* Try to open the service */
\r
362 service->handle = OpenService(services, service->name, SC_MANAGER_ALL_ACCESS);
\r
363 if (! service->handle) {
\r
364 CloseServiceHandle(services);
\r
365 print_message(stderr, NSSM_MESSAGE_OPENSERVICE_FAILED);
\r
369 /* Get system details. */
\r
370 unsigned long bufsize;
\r
371 unsigned long error;
\r
372 QUERY_SERVICE_CONFIG *qsc = query_service_config(service->name, service->handle);
\r
374 CloseHandle(service->handle);
\r
375 CloseServiceHandle(services);
\r
379 service->type = qsc->dwServiceType;
\r
380 if (! (service->type & SERVICE_WIN32_OWN_PROCESS)) {
\r
381 HeapFree(GetProcessHeap(), 0, qsc);
\r
382 CloseHandle(service->handle);
\r
383 CloseServiceHandle(services);
\r
384 print_message(stderr, NSSM_MESSAGE_CANNOT_EDIT, service->name, _T("SERVICE_WIN32_OWN_PROCESS"), 0);
\r
388 if (get_service_startup(service->name, service->handle, qsc, &service->startup)) {
\r
389 HeapFree(GetProcessHeap(), 0, qsc);
\r
390 CloseHandle(service->handle);
\r
391 CloseServiceHandle(services);
\r
395 if (! str_equiv(qsc->lpServiceStartName, NSSM_LOCALSYSTEM_ACCOUNT)) {
\r
396 size_t len = _tcslen(qsc->lpServiceStartName);
\r
397 service->username = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(TCHAR));
\r
398 if (service->username) {
\r
399 memmove(service->username, qsc->lpServiceStartName, (len + 1) * sizeof(TCHAR));
\r
400 service->usernamelen = (unsigned long) len;
\r
403 HeapFree(GetProcessHeap(), 0, qsc);
\r
404 CloseHandle(service->handle);
\r
405 CloseServiceHandle(services);
\r
406 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("username"), _T("pre_edit_service()"));
\r
410 _sntprintf_s(service->displayname, _countof(service->displayname), _TRUNCATE, _T("%s"), qsc->lpDisplayName);
\r
412 /* Get the canonical service name. We open it case insensitively. */
\r
413 bufsize = _countof(service->name);
\r
414 GetServiceKeyName(services, service->displayname, service->name, &bufsize);
\r
416 /* Remember the executable in case it isn't NSSM. */
\r
417 _sntprintf_s(service->image, _countof(service->image), _TRUNCATE, _T("%s"), qsc->lpBinaryPathName);
\r
418 HeapFree(GetProcessHeap(), 0, qsc);
\r
420 /* Get extended system details. */
\r
421 QueryServiceConfig2(service->handle, SERVICE_CONFIG_DESCRIPTION, 0, 0, &bufsize);
\r
422 error = GetLastError();
\r
423 if (error == ERROR_INSUFFICIENT_BUFFER) {
\r
424 SERVICE_DESCRIPTION *description = (SERVICE_DESCRIPTION *) HeapAlloc(GetProcessHeap(), 0, bufsize);
\r
425 if (! description) {
\r
426 CloseHandle(service->handle);
\r
427 CloseServiceHandle(services);
\r
428 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("SERVICE_CONFIG_DESCRIPTION"), _T("pre_edit_service()"));
\r
432 if (QueryServiceConfig2(service->handle, SERVICE_CONFIG_DESCRIPTION, (unsigned char *) description, bufsize, &bufsize)) {
\r
433 if (description->lpDescription) _sntprintf_s(service->description, _countof(service->description), _TRUNCATE, _T("%s"), description->lpDescription);
\r
434 HeapFree(GetProcessHeap(), 0, description);
\r
437 HeapFree(GetProcessHeap(), 0, description);
\r
438 CloseHandle(service->handle);
\r
439 CloseServiceHandle(services);
\r
440 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service->name, _T("SERVICE_CONFIG_DELAYED_AUTO_START_INFO"), error_string(error));
\r
445 CloseHandle(service->handle);
\r
446 CloseServiceHandle(services);
\r
447 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service->name, _T("SERVICE_CONFIG_DELAYED_AUTO_START_INFO"), error_string(error));
\r
451 /* Get NSSM details. */
\r
452 get_parameters(service, 0);
\r
454 CloseServiceHandle(services);
\r
456 if (! service->exe[0]) {
\r
457 print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE, service->name, NSSM, service->image);
\r
458 service->native = true;
\r
461 nssm_gui(IDD_EDIT, service);
\r
466 /* About to remove the service */
\r
467 int pre_remove_service(int argc, TCHAR **argv) {
\r
468 nssm_service_t *service = alloc_nssm_service();
\r
469 set_nssm_service_defaults(service);
\r
470 if (argc) _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]);
\r
472 /* Show dialogue box if we didn't pass service name and "confirm" */
\r
473 if (argc < 2) return nssm_gui(IDD_REMOVE, service);
\r
474 if (str_equiv(argv[1], _T("confirm"))) {
\r
475 int ret = remove_service(service);
\r
476 cleanup_nssm_service(service);
\r
479 print_message(stderr, NSSM_MESSAGE_PRE_REMOVE_SERVICE);
\r
483 /* Install the service */
\r
484 int install_service(nssm_service_t *service) {
\r
485 if (! service) return 1;
\r
487 /* Open service manager */
\r
488 SC_HANDLE services = open_service_manager();
\r
490 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
491 cleanup_nssm_service(service);
\r
495 /* Get path of this program */
\r
496 GetModuleFileName(0, service->image, _countof(service->image));
\r
498 /* Create the service - settings will be changed in edit_service() */
\r
499 service->handle = CreateService(services, service->name, service->name, SC_MANAGER_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, service->image, 0, 0, 0, 0, 0);
\r
500 if (! service->handle) {
\r
501 print_message(stderr, NSSM_MESSAGE_CREATESERVICE_FAILED);
\r
502 CloseServiceHandle(services);
\r
506 if (edit_service(service, false)) {
\r
507 DeleteService(service->handle);
\r
508 CloseServiceHandle(services);
\r
512 print_message(stdout, NSSM_MESSAGE_SERVICE_INSTALLED, service->name);
\r
515 CloseServiceHandle(services);
\r
520 /* Edit the service. */
\r
521 int edit_service(nssm_service_t *service, bool editing) {
\r
522 if (! service) return 1;
\r
525 The only two valid flags for service type are SERVICE_WIN32_OWN_PROCESS
\r
526 and SERVICE_INTERACTIVE_PROCESS.
\r
528 service->type &= SERVICE_INTERACTIVE_PROCESS;
\r
529 service->type |= SERVICE_WIN32_OWN_PROCESS;
\r
531 /* Startup type. */
\r
532 unsigned long startup;
\r
533 switch (service->startup) {
\r
534 case NSSM_STARTUP_MANUAL: startup = SERVICE_DEMAND_START; break;
\r
535 case NSSM_STARTUP_DISABLED: startup = SERVICE_DISABLED; break;
\r
536 default: startup = SERVICE_AUTO_START;
\r
539 /* Display name. */
\r
540 if (! service->displayname[0]) _sntprintf_s(service->displayname, _countof(service->displayname), _TRUNCATE, _T("%s"), service->name);
\r
543 Username must be NULL if we aren't changing or an account name.
\r
544 We must explicitly user LOCALSYSTEM to change it when we are editing.
\r
545 Password must be NULL if we aren't changing, a password or "".
\r
546 Empty passwords are valid but we won't allow them in the GUI.
\r
548 TCHAR *username = 0;
\r
549 TCHAR *password = 0;
\r
550 if (service->usernamelen) {
\r
551 username = service->username;
\r
552 if (service->passwordlen) password = service->password;
\r
553 else password = _T("");
\r
555 else if (editing) username = NSSM_LOCALSYSTEM_ACCOUNT;
\r
557 if (grant_logon_as_service(username)) {
\r
558 print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username);
\r
562 if (! ChangeServiceConfig(service->handle, service->type, startup, SERVICE_NO_CHANGE, 0, 0, 0, 0, username, password, service->displayname)) {
\r
563 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
567 if (service->description[0] || editing) {
\r
568 SERVICE_DESCRIPTION description;
\r
569 ZeroMemory(&description, sizeof(description));
\r
570 if (service->description[0]) description.lpDescription = service->description;
\r
571 else description.lpDescription = 0;
\r
572 if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_DESCRIPTION, &description)) {
\r
573 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_DESCRIPTION_FAILED, service->name, error_string(GetLastError()), 0);
\r
577 SERVICE_DELAYED_AUTO_START_INFO delayed;
\r
578 ZeroMemory(&delayed, sizeof(delayed));
\r
579 if (service->startup == NSSM_STARTUP_DELAYED) delayed.fDelayedAutostart = 1;
\r
580 else delayed.fDelayedAutostart = 0;
\r
581 /* Delayed startup isn't supported until Vista. */
\r
582 if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, &delayed)) {
\r
583 unsigned long error = GetLastError();
\r
584 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
\r
585 if (error != ERROR_INVALID_LEVEL) {
\r
586 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_DELAYED_AUTO_START_INFO_FAILED, service->name, error_string(error), 0);
\r
590 /* Don't mess with parameters which aren't ours. */
\r
591 if (! service->native) {
\r
592 /* Now we need to put the parameters into the registry */
\r
593 if (create_parameters(service, editing)) {
\r
594 print_message(stderr, NSSM_MESSAGE_CREATE_PARAMETERS_FAILED);
\r
598 set_service_recovery(service);
\r
604 /* Remove the service */
\r
605 int remove_service(nssm_service_t *service) {
\r
606 if (! service) return 1;
\r
608 /* Open service manager */
\r
609 SC_HANDLE services = open_service_manager();
\r
611 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
615 /* Try to open the service */
\r
616 service->handle = OpenService(services, service->name, SC_MANAGER_ALL_ACCESS);
\r
617 if (! service->handle) {
\r
618 print_message(stderr, NSSM_MESSAGE_OPENSERVICE_FAILED);
\r
619 CloseServiceHandle(services);
\r
623 /* Get the canonical service name. We open it case insensitively. */
\r
624 unsigned long bufsize = _countof(service->displayname);
\r
625 GetServiceDisplayName(services, service->name, service->displayname, &bufsize);
\r
626 bufsize = _countof(service->name);
\r
627 GetServiceKeyName(services, service->displayname, service->name, &bufsize);
\r
629 /* Try to delete the service */
\r
630 if (! DeleteService(service->handle)) {
\r
631 print_message(stderr, NSSM_MESSAGE_DELETESERVICE_FAILED);
\r
632 CloseServiceHandle(services);
\r
637 CloseServiceHandle(services);
\r
639 print_message(stdout, NSSM_MESSAGE_SERVICE_REMOVED, service->name);
\r
643 /* Service initialisation */
\r
644 void WINAPI service_main(unsigned long argc, TCHAR **argv) {
\r
645 nssm_service_t *service = alloc_nssm_service();
\r
646 if (! service) return;
\r
648 if (_sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]) < 0) {
\r
649 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("service->name"), _T("service_main()"), 0);
\r
653 /* We can use a condition variable in a critical section on Vista or later. */
\r
654 if (imports.SleepConditionVariableCS && imports.WakeConditionVariable) use_critical_section = true;
\r
655 else use_critical_section = false;
\r
657 /* Initialise status */
\r
658 ZeroMemory(&service->status, sizeof(service->status));
\r
659 service->status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;
\r
660 service->status.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE;
\r
661 service->status.dwWin32ExitCode = NO_ERROR;
\r
662 service->status.dwServiceSpecificExitCode = 0;
\r
663 service->status.dwCheckPoint = 0;
\r
664 service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;
\r
666 /* Signal we AREN'T running the server */
\r
667 service->process_handle = 0;
\r
670 /* Register control handler */
\r
671 service->status_handle = RegisterServiceCtrlHandlerEx(NSSM, service_control_handler, (void *) service);
\r
672 if (! service->status_handle) {
\r
673 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_REGISTERSERVICECTRLHANDER_FAILED, error_string(GetLastError()), 0);
\r
677 log_service_control(service->name, 0, true);
\r
679 service->status.dwCurrentState = SERVICE_START_PENDING;
\r
680 service->status.dwWaitHint = service->throttle_delay + NSSM_WAITHINT_MARGIN;
\r
681 SetServiceStatus(service->status_handle, &service->status);
\r
684 /* Try to create the exit action parameters; we don't care if it fails */
\r
685 create_exit_action(service->name, exit_action_strings[0], false);
\r
687 SC_HANDLE services = open_service_manager();
\r
689 service->handle = OpenService(services, service->name, SC_MANAGER_ALL_ACCESS);
\r
690 set_service_recovery(service);
\r
691 CloseServiceHandle(services);
\r
695 /* Used for signalling a resume if the service pauses when throttled. */
\r
696 if (use_critical_section) {
\r
697 InitializeCriticalSection(&service->throttle_section);
\r
698 service->throttle_section_initialised = true;
\r
701 service->throttle_timer = CreateWaitableTimer(0, 1, 0);
\r
702 if (! service->throttle_timer) {
\r
703 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_CREATEWAITABLETIMER_FAILED, service->name, error_string(GetLastError()), 0);
\r
707 monitor_service(service);
\r
710 /* Make sure service recovery actions are taken where necessary */
\r
711 void set_service_recovery(nssm_service_t *service) {
\r
712 SERVICE_FAILURE_ACTIONS_FLAG flag;
\r
713 ZeroMemory(&flag, sizeof(flag));
\r
714 flag.fFailureActionsOnNonCrashFailures = true;
\r
716 /* This functionality was added in Vista so the call may fail */
\r
717 if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, &flag)) {
\r
718 unsigned long error = GetLastError();
\r
719 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
\r
720 if (error != ERROR_INVALID_LEVEL) {
\r
721 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_FAILURE_ACTIONS_FAILED, service->name, error_string(error), 0);
\r
726 int monitor_service(nssm_service_t *service) {
\r
727 /* Set service status to started */
\r
728 int ret = start_service(service);
\r
731 _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%d"), ret);
\r
732 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_START_SERVICE_FAILED, service->exe, service->name, ret, 0);
\r
735 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_STARTED_SERVICE, service->exe, service->flags, service->name, service->dir, 0);
\r
737 /* Monitor service */
\r
738 if (! RegisterWaitForSingleObject(&service->wait_handle, service->process_handle, end_service, (void *) service, INFINITE, WT_EXECUTEONLYONCE | WT_EXECUTELONGFUNCTION)) {
\r
739 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_REGISTERWAITFORSINGLEOBJECT_FAILED, service->name, service->exe, error_string(GetLastError()), 0);
\r
745 TCHAR *service_control_text(unsigned long control) {
\r
747 /* HACK: there is no SERVICE_CONTROL_START constant */
\r
748 case 0: return _T("START");
\r
749 case SERVICE_CONTROL_STOP: return _T("STOP");
\r
750 case SERVICE_CONTROL_SHUTDOWN: return _T("SHUTDOWN");
\r
751 case SERVICE_CONTROL_PAUSE: return _T("PAUSE");
\r
752 case SERVICE_CONTROL_CONTINUE: return _T("CONTINUE");
\r
753 case SERVICE_CONTROL_INTERROGATE: return _T("INTERROGATE");
\r
758 void log_service_control(TCHAR *service_name, unsigned long control, bool handled) {
\r
759 TCHAR *text = service_control_text(control);
\r
760 unsigned long event;
\r
763 /* "0x" + 8 x hex + NULL */
\r
764 text = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, 11 * sizeof(TCHAR));
\r
766 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);
\r
769 if (_sntprintf_s(text, 11, _TRUNCATE, _T("0x%08x"), control) < 0) {
\r
770 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);
\r
771 HeapFree(GetProcessHeap(), 0, text);
\r
775 event = NSSM_EVENT_SERVICE_CONTROL_UNKNOWN;
\r
777 else if (handled) event = NSSM_EVENT_SERVICE_CONTROL_HANDLED;
\r
778 else event = NSSM_EVENT_SERVICE_CONTROL_NOT_HANDLED;
\r
780 log_event(EVENTLOG_INFORMATION_TYPE, event, service_name, text, 0);
\r
782 if (event == NSSM_EVENT_SERVICE_CONTROL_UNKNOWN) {
\r
783 HeapFree(GetProcessHeap(), 0, text);
\r
787 /* Service control handler */
\r
788 unsigned long WINAPI service_control_handler(unsigned long control, unsigned long event, void *data, void *context) {
\r
789 nssm_service_t *service = (nssm_service_t *) context;
\r
792 case SERVICE_CONTROL_INTERROGATE:
\r
793 /* We always keep the service status up-to-date so this is a no-op. */
\r
796 case SERVICE_CONTROL_SHUTDOWN:
\r
797 case SERVICE_CONTROL_STOP:
\r
798 log_service_control(service->name, control, true);
\r
800 We MUST acknowledge the stop request promptly but we're committed to
\r
801 waiting for the application to exit. Spawn a new thread to wait
\r
802 while we acknowledge the request.
\r
804 if (! CreateThread(NULL, 0, shutdown_service, context, 0, NULL)) {
\r
805 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETHREAD_FAILED, error_string(GetLastError()), 0);
\r
808 We couldn't create a thread to tidy up so we'll have to force the tidyup
\r
809 to complete in time in this thread.
\r
811 service->kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;
\r
812 service->kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;
\r
813 service->kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;
\r
815 stop_service(service, 0, true, true);
\r
819 case SERVICE_CONTROL_CONTINUE:
\r
820 log_service_control(service->name, control, true);
\r
821 service->throttle = 0;
\r
822 if (use_critical_section) imports.WakeConditionVariable(&service->throttle_condition);
\r
824 if (! service->throttle_timer) return ERROR_CALL_NOT_IMPLEMENTED;
\r
825 ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));
\r
826 SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);
\r
828 service->status.dwCurrentState = SERVICE_CONTINUE_PENDING;
\r
829 service->status.dwWaitHint = throttle_milliseconds(service->throttle) + NSSM_WAITHINT_MARGIN;
\r
830 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESET_THROTTLE, service->name, 0);
\r
831 SetServiceStatus(service->status_handle, &service->status);
\r
834 case SERVICE_CONTROL_PAUSE:
\r
836 We don't accept pause messages but it isn't possible to register
\r
837 only for continue messages so we have to handle this case.
\r
839 log_service_control(service->name, control, false);
\r
840 return ERROR_CALL_NOT_IMPLEMENTED;
\r
843 /* Unknown control */
\r
844 log_service_control(service->name, control, false);
\r
845 return ERROR_CALL_NOT_IMPLEMENTED;
\r
848 /* Start the service */
\r
849 int start_service(nssm_service_t *service) {
\r
850 service->stopping = false;
\r
851 service->allow_restart = true;
\r
853 if (service->process_handle) return 0;
\r
855 /* Allocate a STARTUPINFO structure for a new process */
\r
857 ZeroMemory(&si, sizeof(si));
\r
858 si.cb = sizeof(si);
\r
860 /* Allocate a PROCESSINFO structure for the process */
\r
861 PROCESS_INFORMATION pi;
\r
862 ZeroMemory(&pi, sizeof(pi));
\r
864 /* Get startup parameters */
\r
865 int ret = get_parameters(service, &si);
\r
867 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_PARAMETERS_FAILED, service->name, 0);
\r
868 return stop_service(service, 2, true, true);
\r
871 /* Launch executable with arguments */
\r
872 TCHAR cmd[CMD_LENGTH];
\r
873 if (_sntprintf_s(cmd, _countof(cmd), _TRUNCATE, _T("\"%s\" %s"), service->exe, service->flags) < 0) {
\r
874 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("command line"), _T("start_service"), 0);
\r
875 close_output_handles(&si);
\r
876 return stop_service(service, 2, true, true);
\r
879 throttle_restart(service);
\r
881 bool inherit_handles = false;
\r
882 if (si.dwFlags & STARTF_USESTDHANDLES) inherit_handles = true;
\r
883 unsigned long flags = 0;
\r
885 flags |= CREATE_UNICODE_ENVIRONMENT;
\r
887 if (! CreateProcess(0, cmd, 0, 0, inherit_handles, flags, service->env, service->dir, &si, &pi)) {
\r
888 unsigned long exitcode = 3;
\r
889 unsigned long error = GetLastError();
\r
890 if (error == ERROR_INVALID_PARAMETER && service->env) {
\r
891 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED_INVALID_ENVIRONMENT, service->name, service->exe, NSSM_REG_ENV, 0);
\r
892 if (test_environment(service->env)) exitcode = 4;
\r
894 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service->name, service->exe, error_string(error), 0);
\r
895 close_output_handles(&si);
\r
896 return stop_service(service, exitcode, true, true);
\r
898 service->process_handle = pi.hProcess;
\r
899 service->pid = pi.dwProcessId;
\r
901 if (get_process_creation_time(service->process_handle, &service->creation_time)) ZeroMemory(&service->creation_time, sizeof(service->creation_time));
\r
903 close_output_handles(&si);
\r
906 Wait for a clean startup before changing the service status to RUNNING
\r
907 but be mindful of the fact that we are blocking the service control manager
\r
908 so abandon the wait before too much time has elapsed.
\r
910 unsigned long delay = service->throttle_delay;
\r
911 if (delay > NSSM_SERVICE_STATUS_DEADLINE) {
\r
912 TCHAR delay_milliseconds[16];
\r
913 _sntprintf_s(delay_milliseconds, _countof(delay_milliseconds), _TRUNCATE, _T("%lu"), delay);
\r
914 TCHAR deadline_milliseconds[16];
\r
915 _sntprintf_s(deadline_milliseconds, _countof(deadline_milliseconds), _TRUNCATE, _T("%lu"), NSSM_SERVICE_STATUS_DEADLINE);
\r
916 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_STARTUP_DELAY_TOO_LONG, service->name, delay_milliseconds, NSSM, deadline_milliseconds, 0);
\r
917 delay = NSSM_SERVICE_STATUS_DEADLINE;
\r
919 unsigned long deadline = WaitForSingleObject(service->process_handle, delay);
\r
921 /* Signal successful start */
\r
922 service->status.dwCurrentState = SERVICE_RUNNING;
\r
923 SetServiceStatus(service->status_handle, &service->status);
\r
925 /* Continue waiting for a clean startup. */
\r
926 if (deadline == WAIT_TIMEOUT) {
\r
927 if (service->throttle_delay > delay) {
\r
928 if (WaitForSingleObject(service->process_handle, service->throttle_delay - delay) == WAIT_TIMEOUT) service->throttle = 0;
\r
930 else service->throttle = 0;
\r
936 /* Stop the service */
\r
937 int stop_service(nssm_service_t *service, unsigned long exitcode, bool graceful, bool default_action) {
\r
938 service->allow_restart = false;
\r
939 if (service->wait_handle) {
\r
940 UnregisterWait(service->wait_handle);
\r
941 service->wait_handle = 0;
\r
944 if (default_action && ! exitcode && ! graceful) {
\r
945 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
949 /* Signal we are stopping */
\r
951 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
952 service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;
\r
953 SetServiceStatus(service->status_handle, &service->status);
\r
956 /* Nothing to do if service isn't running */
\r
957 if (service->pid) {
\r
958 /* Shut down service */
\r
959 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_TERMINATEPROCESS, service->name, service->exe, 0);
\r
960 kill_process(service, service->process_handle, service->pid, 0);
\r
962 else log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_PROCESS_ALREADY_STOPPED, service->name, service->exe, 0);
\r
964 end_service((void *) service, true);
\r
966 /* Signal we stopped */
\r
968 service->status.dwCurrentState = SERVICE_STOPPED;
\r
970 service->status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
\r
971 service->status.dwServiceSpecificExitCode = exitcode;
\r
974 service->status.dwWin32ExitCode = NO_ERROR;
\r
975 service->status.dwServiceSpecificExitCode = 0;
\r
977 SetServiceStatus(service->status_handle, &service->status);
\r
983 /* Callback function triggered when the server exits */
\r
984 void CALLBACK end_service(void *arg, unsigned char why) {
\r
985 nssm_service_t *service = (nssm_service_t *) arg;
\r
987 if (service->stopping) return;
\r
989 service->stopping = true;
\r
991 /* Check exit code */
\r
992 unsigned long exitcode = 0;
\r
994 if (service->process_handle) {
\r
995 GetExitCodeProcess(service->process_handle, &exitcode);
\r
996 if (exitcode == STILL_ACTIVE || get_process_exit_time(service->process_handle, &service->exit_time)) GetSystemTimeAsFileTime(&service->exit_time);
\r
997 CloseHandle(service->process_handle);
\r
999 else GetSystemTimeAsFileTime(&service->exit_time);
\r
1001 service->process_handle = 0;
\r
1004 Log that the service ended BEFORE logging about killing the process
\r
1005 tree. See below for the possible values of the why argument.
\r
1008 _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), exitcode);
\r
1009 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ENDED_SERVICE, service->exe, service->name, code, 0);
\r
1013 if (exitcode == STILL_ACTIVE) exitcode = 0;
\r
1014 if (service->pid) kill_process_tree(service, service->pid, exitcode, service->pid);
\r
1018 The why argument is true if our wait timed out or false otherwise.
\r
1019 Our wait is infinite so why will never be true when called by the system.
\r
1020 If it is indeed true, assume we were called from stop_service() because
\r
1021 this is a controlled shutdown, and don't take any restart action.
\r
1024 if (! service->allow_restart) return;
\r
1026 /* What action should we take? */
\r
1027 int action = NSSM_EXIT_RESTART;
\r
1028 TCHAR action_string[ACTION_LEN];
\r
1029 bool default_action;
\r
1030 if (! get_exit_action(service->name, &exitcode, action_string, &default_action)) {
\r
1031 for (int i = 0; exit_action_strings[i]; i++) {
\r
1032 if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
1040 /* Try to restart the service or return failure code to service manager */
\r
1041 case NSSM_EXIT_RESTART:
\r
1042 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_RESTART, service->name, code, exit_action_strings[action], service->exe, 0);
\r
1043 while (monitor_service(service)) {
\r
1044 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_RESTART_SERVICE_FAILED, service->exe, service->name, 0);
\r
1049 /* Do nothing, just like srvany would */
\r
1050 case NSSM_EXIT_IGNORE:
\r
1051 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_IGNORE, service->name, code, exit_action_strings[action], service->exe, 0);
\r
1055 /* Tell the service manager we are finished */
\r
1056 case NSSM_EXIT_REALLY:
\r
1057 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_REALLY, service->name, code, exit_action_strings[action], 0);
\r
1058 stop_service(service, exitcode, true, default_action);
\r
1061 /* Fake a crash so pre-Vista service managers will run recovery actions. */
\r
1062 case NSSM_EXIT_UNCLEAN:
\r
1063 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_UNCLEAN, service->name, code, exit_action_strings[action], 0);
\r
1064 stop_service(service, exitcode, false, default_action);
\r
1071 void throttle_restart(nssm_service_t *service) {
\r
1072 /* This can't be a restart if the service is already running. */
\r
1073 if (! service->throttle++) return;
\r
1075 int ms = throttle_milliseconds(service->throttle);
\r
1077 if (service->throttle > 7) service->throttle = 8;
\r
1079 TCHAR threshold[8], milliseconds[8];
\r
1080 _sntprintf_s(threshold, _countof(threshold), _TRUNCATE, _T("%lu"), service->throttle_delay);
\r
1081 _sntprintf_s(milliseconds, _countof(milliseconds), _TRUNCATE, _T("%lu"), ms);
\r
1082 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_THROTTLED, service->name, threshold, milliseconds, 0);
\r
1084 if (use_critical_section) EnterCriticalSection(&service->throttle_section);
\r
1085 else if (service->throttle_timer) {
\r
1086 ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));
\r
1087 service->throttle_duetime.QuadPart = 0 - (ms * 10000LL);
\r
1088 SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);
\r
1091 service->status.dwCurrentState = SERVICE_PAUSED;
\r
1092 SetServiceStatus(service->status_handle, &service->status);
\r
1094 if (use_critical_section) {
\r
1095 imports.SleepConditionVariableCS(&service->throttle_condition, &service->throttle_section, ms);
\r
1096 LeaveCriticalSection(&service->throttle_section);
\r
1099 if (service->throttle_timer) WaitForSingleObject(service->throttle_timer, INFINITE);
\r
1105 When responding to a stop (or any other) request we need to set dwWaitHint to
\r
1106 the number of milliseconds we expect the operation to take, and optionally
\r
1107 increase dwCheckPoint. If dwWaitHint milliseconds elapses without the
\r
1108 operation completing or dwCheckPoint increasing, the system will consider the
\r
1109 service to be hung.
\r
1111 However the system will consider the service to be hung after 30000
\r
1112 milliseconds regardless of the value of dwWaitHint if dwCheckPoint has not
\r
1113 changed. Therefore if we want to wait longer than that we must periodically
\r
1114 increase dwCheckPoint.
\r
1116 Furthermore, it will consider the service to be hung after 60000 milliseconds
\r
1117 regardless of the value of dwCheckPoint unless dwWaitHint is increased every
\r
1118 time dwCheckPoint is also increased.
\r
1120 Our strategy then is to retrieve the initial dwWaitHint and wait for
\r
1121 NSSM_SERVICE_STATUS_DEADLINE milliseconds. If the process is still running
\r
1122 and we haven't finished waiting we increment dwCheckPoint and add whichever is
\r
1123 smaller of NSSM_SERVICE_STATUS_DEADLINE or the remaining timeout to
\r
1126 Only doing both these things will prevent the system from killing the service.
\r
1128 Returns: 1 if the wait timed out.
\r
1129 0 if the wait completed.
\r
1132 int await_shutdown(nssm_service_t *service, TCHAR *function_name, unsigned long timeout) {
\r
1133 unsigned long interval;
\r
1134 unsigned long waithint;
\r
1135 unsigned long ret;
\r
1136 unsigned long waited;
\r
1137 TCHAR interval_milliseconds[16];
\r
1138 TCHAR timeout_milliseconds[16];
\r
1139 TCHAR waited_milliseconds[16];
\r
1140 TCHAR *function = function_name;
\r
1142 /* Add brackets to function name. */
\r
1143 size_t funclen = _tcslen(function_name) + 3;
\r
1144 TCHAR *func = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, funclen * sizeof(TCHAR));
\r
1146 if (_sntprintf_s(func, funclen, _TRUNCATE, _T("%s()"), function_name) > -1) function = func;
\r
1149 _sntprintf_s(timeout_milliseconds, _countof(timeout_milliseconds), _TRUNCATE, _T("%lu"), timeout);
\r
1151 waithint = service->status.dwWaitHint;
\r
1153 while (waited < timeout) {
\r
1154 interval = timeout - waited;
\r
1155 if (interval > NSSM_SERVICE_STATUS_DEADLINE) interval = NSSM_SERVICE_STATUS_DEADLINE;
\r
1157 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
1158 service->status.dwWaitHint += interval;
\r
1159 service->status.dwCheckPoint++;
\r
1160 SetServiceStatus(service->status_handle, &service->status);
\r
1163 _sntprintf_s(waited_milliseconds, _countof(waited_milliseconds), _TRUNCATE, _T("%lu"), waited);
\r
1164 _sntprintf_s(interval_milliseconds, _countof(interval_milliseconds), _TRUNCATE, _T("%lu"), interval);
\r
1165 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_AWAITING_SHUTDOWN, function, service->name, waited_milliseconds, interval_milliseconds, timeout_milliseconds, 0);
\r
1168 switch (WaitForSingleObject(service->process_handle, interval)) {
\r
1169 case WAIT_OBJECT_0:
\r
1173 case WAIT_TIMEOUT:
\r
1182 waited += interval;
\r
1186 if (func) HeapFree(GetProcessHeap(), 0, func);
\r