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_description(const TCHAR *service_name, SC_HANDLE service_handle, unsigned long len, TCHAR *buffer) {
\r
69 if (! buffer) return 1;
\r
71 unsigned long bufsize;
\r
72 QueryServiceConfig2(service_handle, SERVICE_CONFIG_DESCRIPTION, 0, 0, &bufsize);
\r
73 unsigned long error = GetLastError();
\r
74 if (error == ERROR_INSUFFICIENT_BUFFER) {
\r
75 SERVICE_DESCRIPTION *description = (SERVICE_DESCRIPTION *) HeapAlloc(GetProcessHeap(), 0, bufsize);
\r
76 if (! description) {
\r
77 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("SERVICE_CONFIG_DESCRIPTION"), _T("get_service_description()"));
\r
81 if (QueryServiceConfig2(service_handle, SERVICE_CONFIG_DESCRIPTION, (unsigned char *) description, bufsize, &bufsize)) {
\r
82 if (description->lpDescription) _sntprintf_s(buffer, len, _TRUNCATE, _T("%s"), description->lpDescription);
\r
83 else ZeroMemory(buffer, len * sizeof(TCHAR));
\r
84 HeapFree(GetProcessHeap(), 0, description);
\r
88 HeapFree(GetProcessHeap(), 0, description);
\r
89 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_CONFIG_DESCRIPTION"), error_string(error));
\r
94 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_CONFIG_DESCRIPTION"), error_string(error));
\r
101 int get_service_startup(const TCHAR *service_name, SC_HANDLE service_handle, const QUERY_SERVICE_CONFIG *qsc, unsigned long *startup) {
\r
102 if (! qsc) return 1;
\r
104 switch (qsc->dwStartType) {
\r
105 case SERVICE_DEMAND_START: *startup = NSSM_STARTUP_MANUAL; break;
\r
106 case SERVICE_DISABLED: *startup = NSSM_STARTUP_DISABLED; break;
\r
107 default: *startup = NSSM_STARTUP_AUTOMATIC;
\r
110 if (*startup != NSSM_STARTUP_AUTOMATIC) return 0;
\r
112 /* Check for delayed start. */
\r
113 unsigned long bufsize;
\r
114 unsigned long error;
\r
115 QueryServiceConfig2(service_handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, 0, 0, &bufsize);
\r
116 error = GetLastError();
\r
117 if (error == ERROR_INSUFFICIENT_BUFFER) {
\r
118 SERVICE_DELAYED_AUTO_START_INFO *info = (SERVICE_DELAYED_AUTO_START_INFO *) HeapAlloc(GetProcessHeap(), 0, bufsize);
\r
120 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("SERVICE_DELAYED_AUTO_START_INFO"), _T("get_service_startup()"));
\r
124 if (QueryServiceConfig2(service_handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, (unsigned char *) info, bufsize, &bufsize)) {
\r
125 if (info->fDelayedAutostart) *startup = NSSM_STARTUP_DELAYED;
\r
126 HeapFree(GetProcessHeap(), 0, info);
\r
130 error = GetLastError();
\r
131 if (error != ERROR_INVALID_LEVEL) {
\r
132 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_CONFIG_DELAYED_AUTO_START_INFO"), error_string(error));
\r
137 else if (error != ERROR_INVALID_LEVEL) {
\r
138 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_DELAYED_AUTO_START_INFO"), error_string(error));
\r
145 int get_service_username(const TCHAR *service_name, const QUERY_SERVICE_CONFIG *qsc, TCHAR **username, size_t *usernamelen) {
\r
146 if (! username) return 1;
\r
147 if (! usernamelen) return 1;
\r
152 if (! qsc) return 1;
\r
154 if (str_equiv(qsc->lpServiceStartName, NSSM_LOCALSYSTEM_ACCOUNT)) return 0;
\r
156 size_t len = _tcslen(qsc->lpServiceStartName);
\r
157 *username = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(TCHAR));
\r
159 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("username"), _T("get_service_username()"));
\r
163 memmove(*username, qsc->lpServiceStartName, (len + 1) * sizeof(TCHAR));
\r
164 *usernamelen = len;
\r
169 static int grant_logon_as_service(const TCHAR *username) {
\r
170 if (str_equiv(username, NSSM_LOCALSYSTEM_ACCOUNT)) return 0;
\r
172 /* Open Policy object. */
\r
173 LSA_OBJECT_ATTRIBUTES attributes;
\r
174 ZeroMemory(&attributes, sizeof(attributes));
\r
178 NTSTATUS status = LsaOpenPolicy(0, &attributes, POLICY_ALL_ACCESS, &policy);
\r
180 print_message(stderr, NSSM_MESSAGE_LSAOPENPOLICY_FAILED, error_string(LsaNtStatusToWinError(status)));
\r
184 /* Look up SID for the account. */
\r
185 LSA_UNICODE_STRING lsa_username;
\r
187 lsa_username.Buffer = (wchar_t *) username;
\r
188 lsa_username.Length = (unsigned short) _tcslen(username) * sizeof(TCHAR);
\r
189 lsa_username.MaximumLength = lsa_username.Length + sizeof(TCHAR);
\r
192 mbstowcs_s(&buflen, NULL, 0, username, _TRUNCATE);
\r
193 lsa_username.MaximumLength = buflen * sizeof(wchar_t);
\r
194 lsa_username.Length = lsa_username.MaximumLength - sizeof(wchar_t);
\r
195 lsa_username.Buffer = (wchar_t *) HeapAlloc(GetProcessHeap(), 0, lsa_username.MaximumLength);
\r
196 if (lsa_username.Buffer) mbstowcs_s(&buflen, lsa_username.Buffer, lsa_username.MaximumLength, username, _TRUNCATE);
\r
199 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("LSA_UNICODE_STRING"), _T("grant_logon_as_service()"));
\r
204 LSA_REFERENCED_DOMAIN_LIST *translated_domains;
\r
205 LSA_TRANSLATED_SID *translated_sid;
\r
206 status = LsaLookupNames(policy, 1, &lsa_username, &translated_domains, &translated_sid);
\r
208 HeapFree(GetProcessHeap(), 0, lsa_username.Buffer);
\r
211 LsaFreeMemory(translated_domains);
\r
212 LsaFreeMemory(translated_sid);
\r
214 print_message(stderr, NSSM_MESSAGE_LSALOOKUPNAMES_FAILED, username, error_string(LsaNtStatusToWinError(status)));
\r
218 if (translated_sid->Use != SidTypeUser) {
\r
219 LsaFreeMemory(translated_domains);
\r
220 LsaFreeMemory(translated_sid);
\r
222 print_message(stderr, NSSM_GUI_INVALID_USERNAME, username);
\r
226 LSA_TRUST_INFORMATION *trust = &translated_domains->Domains[translated_sid->DomainIndex];
\r
227 if (! trust || ! IsValidSid(trust->Sid)) {
\r
228 LsaFreeMemory(translated_domains);
\r
229 LsaFreeMemory(translated_sid);
\r
231 print_message(stderr, NSSM_GUI_INVALID_USERNAME, username);
\r
235 /* GetSidSubAuthority*() return pointers! */
\r
236 unsigned char *n = GetSidSubAuthorityCount(trust->Sid);
\r
238 /* Convert translated SID to SID. */
\r
239 SID *sid = (SID *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, GetSidLengthRequired(*n + 1));
\r
241 LsaFreeMemory(translated_domains);
\r
242 LsaFreeMemory(translated_sid);
\r
244 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("SID"), _T("grant_logon_as_service"));
\r
248 unsigned long error;
\r
249 if (! InitializeSid(sid, GetSidIdentifierAuthority(trust->Sid), *n + 1)) {
\r
250 error = GetLastError();
\r
251 HeapFree(GetProcessHeap(), 0, sid);
\r
252 LsaFreeMemory(translated_domains);
\r
253 LsaFreeMemory(translated_sid);
\r
255 print_message(stderr, NSSM_MESSAGE_INITIALIZESID_FAILED, username, error_string(error));
\r
259 for (unsigned char i = 0; i <= *n; i++) {
\r
260 unsigned long *sub = GetSidSubAuthority(sid, i);
\r
261 if (i < *n) *sub = *GetSidSubAuthority(trust->Sid, i);
\r
262 else *sub = translated_sid->RelativeId;
\r
265 LsaFreeMemory(translated_domains);
\r
266 LsaFreeMemory(translated_sid);
\r
268 /* Check if the SID has the "Log on as a service" right. */
\r
269 LSA_UNICODE_STRING lsa_right;
\r
270 lsa_right.Buffer = NSSM_LOGON_AS_SERVICE_RIGHT;
\r
271 lsa_right.Length = (unsigned short) wcslen(lsa_right.Buffer) * sizeof(wchar_t);
\r
272 lsa_right.MaximumLength = lsa_right.Length + sizeof(wchar_t);
\r
274 LSA_UNICODE_STRING *rights;
\r
275 unsigned long count = ~0;
\r
276 status = LsaEnumerateAccountRights(policy, sid, &rights, &count);
\r
279 If the account has no rights set LsaEnumerateAccountRights() will return
\r
280 STATUS_OBJECT_NAME_NOT_FOUND and set count to 0.
\r
282 error = LsaNtStatusToWinError(status);
\r
283 if (error != ERROR_FILE_NOT_FOUND) {
\r
284 HeapFree(GetProcessHeap(), 0, sid);
\r
286 print_message(stderr, NSSM_MESSAGE_LSAENUMERATEACCOUNTRIGHTS_FAILED, username, error_string(error));
\r
291 for (unsigned long i = 0; i < count; i++) {
\r
292 if (rights[i].Length != lsa_right.Length) continue;
\r
293 if (_wcsnicmp(rights[i].Buffer, lsa_right.Buffer, lsa_right.MaximumLength)) continue;
\r
294 /* The SID has the right. */
\r
295 HeapFree(GetProcessHeap(), 0, sid);
\r
296 LsaFreeMemory(rights);
\r
300 LsaFreeMemory(rights);
\r
302 /* Add the right. */
\r
303 status = LsaAddAccountRights(policy, sid, &lsa_right, 1);
\r
304 HeapFree(GetProcessHeap(), 0, sid);
\r
307 print_message(stderr, NSSM_MESSAGE_LSAADDACCOUNTRIGHTS_FAILED, error_string(LsaNtStatusToWinError(status)));
\r
311 print_message(stdout, NSSM_MESSAGE_GRANTED_LOGON_AS_SERVICE, username);
\r
315 /* Set default values which aren't zero. */
\r
316 void set_nssm_service_defaults(nssm_service_t *service) {
\r
317 if (! service) return;
\r
319 service->type = SERVICE_WIN32_OWN_PROCESS;
\r
320 service->stdin_sharing = NSSM_STDIN_SHARING;
\r
321 service->stdin_disposition = NSSM_STDIN_DISPOSITION;
\r
322 service->stdin_flags = NSSM_STDIN_FLAGS;
\r
323 service->stdout_sharing = NSSM_STDOUT_SHARING;
\r
324 service->stdout_disposition = NSSM_STDOUT_DISPOSITION;
\r
325 service->stdout_flags = NSSM_STDOUT_FLAGS;
\r
326 service->stderr_sharing = NSSM_STDERR_SHARING;
\r
327 service->stderr_disposition = NSSM_STDERR_DISPOSITION;
\r
328 service->stderr_flags = NSSM_STDERR_FLAGS;
\r
329 service->throttle_delay = NSSM_RESET_THROTTLE_RESTART;
\r
330 service->stop_method = ~0;
\r
331 service->kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;
\r
332 service->kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;
\r
333 service->kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;
\r
336 /* Allocate and zero memory for a service. */
\r
337 nssm_service_t *alloc_nssm_service() {
\r
338 nssm_service_t *service = (nssm_service_t *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(nssm_service_t));
\r
339 if (! service) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("service"), _T("alloc_nssm_service()"), 0);
\r
343 /* Free memory for a service. */
\r
344 void cleanup_nssm_service(nssm_service_t *service) {
\r
345 if (! service) return;
\r
346 if (service->username) HeapFree(GetProcessHeap(), 0, service->username);
\r
347 if (service->password) {
\r
348 SecureZeroMemory(service->password, service->passwordlen);
\r
349 HeapFree(GetProcessHeap(), 0, service->password);
\r
351 if (service->env) HeapFree(GetProcessHeap(), 0, service->env);
\r
352 if (service->env_extra) HeapFree(GetProcessHeap(), 0, service->env_extra);
\r
353 if (service->handle) CloseServiceHandle(service->handle);
\r
354 if (service->process_handle) CloseHandle(service->process_handle);
\r
355 if (service->wait_handle) UnregisterWait(service->process_handle);
\r
356 if (service->throttle_section_initialised) DeleteCriticalSection(&service->throttle_section);
\r
357 if (service->throttle_timer) CloseHandle(service->throttle_timer);
\r
358 HeapFree(GetProcessHeap(), 0, service);
\r
361 /* About to install the service */
\r
362 int pre_install_service(int argc, TCHAR **argv) {
\r
363 nssm_service_t *service = alloc_nssm_service();
\r
364 set_nssm_service_defaults(service);
\r
365 if (argc) _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]);
\r
367 /* Show the dialogue box if we didn't give the service name and path */
\r
368 if (argc < 2) return nssm_gui(IDD_INSTALL, service);
\r
371 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("service"), _T("pre_install_service()"));
\r
374 _sntprintf_s(service->exe, _countof(service->exe), _TRUNCATE, _T("%s"), argv[1]);
\r
376 /* Arguments are optional */
\r
377 size_t flagslen = 0;
\r
380 for (i = 2; i < argc; i++) flagslen += _tcslen(argv[i]) + 1;
\r
381 if (! flagslen) flagslen = 1;
\r
382 if (flagslen > _countof(service->flags)) {
\r
383 print_message(stderr, NSSM_MESSAGE_FLAGS_TOO_LONG);
\r
387 for (i = 2; i < argc; i++) {
\r
388 size_t len = _tcslen(argv[i]);
\r
389 memmove(service->flags + s, argv[i], len * sizeof(TCHAR));
\r
391 if (i < argc - 1) service->flags[s++] = _T(' ');
\r
394 /* Work out directory name */
\r
395 _sntprintf_s(service->dir, _countof(service->dir), _TRUNCATE, _T("%s"), service->exe);
\r
396 strip_basename(service->dir);
\r
398 int ret = install_service(service);
\r
399 cleanup_nssm_service(service);
\r
403 /* About to edit the service. */
\r
404 int pre_edit_service(int argc, TCHAR **argv) {
\r
405 /* Require service name. */
\r
406 if (argc < 1) return usage(1);
\r
408 nssm_service_t *service = alloc_nssm_service();
\r
409 _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]);
\r
411 /* Open service manager */
\r
412 SC_HANDLE services = open_service_manager();
\r
414 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
418 /* Try to open the service */
\r
419 service->handle = OpenService(services, service->name, SC_MANAGER_ALL_ACCESS);
\r
420 if (! service->handle) {
\r
421 CloseServiceHandle(services);
\r
422 print_message(stderr, NSSM_MESSAGE_OPENSERVICE_FAILED);
\r
426 /* Get system details. */
\r
427 unsigned long bufsize;
\r
428 QUERY_SERVICE_CONFIG *qsc = query_service_config(service->name, service->handle);
\r
430 CloseHandle(service->handle);
\r
431 CloseServiceHandle(services);
\r
435 service->type = qsc->dwServiceType;
\r
436 if (! (service->type & SERVICE_WIN32_OWN_PROCESS)) {
\r
437 HeapFree(GetProcessHeap(), 0, qsc);
\r
438 CloseHandle(service->handle);
\r
439 CloseServiceHandle(services);
\r
440 print_message(stderr, NSSM_MESSAGE_CANNOT_EDIT, service->name, _T("SERVICE_WIN32_OWN_PROCESS"), 0);
\r
444 if (get_service_startup(service->name, service->handle, qsc, &service->startup)) {
\r
445 HeapFree(GetProcessHeap(), 0, qsc);
\r
446 CloseHandle(service->handle);
\r
447 CloseServiceHandle(services);
\r
451 if (get_service_username(service->name, qsc, &service->username, &service->usernamelen)) {
\r
452 HeapFree(GetProcessHeap(), 0, qsc);
\r
453 CloseHandle(service->handle);
\r
454 CloseServiceHandle(services);
\r
458 _sntprintf_s(service->displayname, _countof(service->displayname), _TRUNCATE, _T("%s"), qsc->lpDisplayName);
\r
460 /* Get the canonical service name. We open it case insensitively. */
\r
461 bufsize = _countof(service->name);
\r
462 GetServiceKeyName(services, service->displayname, service->name, &bufsize);
\r
464 /* Remember the executable in case it isn't NSSM. */
\r
465 _sntprintf_s(service->image, _countof(service->image), _TRUNCATE, _T("%s"), qsc->lpBinaryPathName);
\r
466 HeapFree(GetProcessHeap(), 0, qsc);
\r
468 /* Get extended system details. */
\r
469 if (get_service_description(service->name, service->handle, _countof(service->description), service->description)) {
\r
470 CloseHandle(service->handle);
\r
471 CloseServiceHandle(services);
\r
475 /* Get NSSM details. */
\r
476 get_parameters(service, 0);
\r
478 CloseServiceHandle(services);
\r
480 if (! service->exe[0]) {
\r
481 print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE, service->name, NSSM, service->image);
\r
482 service->native = true;
\r
485 nssm_gui(IDD_EDIT, service);
\r
490 /* About to remove the service */
\r
491 int pre_remove_service(int argc, TCHAR **argv) {
\r
492 nssm_service_t *service = alloc_nssm_service();
\r
493 set_nssm_service_defaults(service);
\r
494 if (argc) _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]);
\r
496 /* Show dialogue box if we didn't pass service name and "confirm" */
\r
497 if (argc < 2) return nssm_gui(IDD_REMOVE, service);
\r
498 if (str_equiv(argv[1], _T("confirm"))) {
\r
499 int ret = remove_service(service);
\r
500 cleanup_nssm_service(service);
\r
503 print_message(stderr, NSSM_MESSAGE_PRE_REMOVE_SERVICE);
\r
507 /* Install the service */
\r
508 int install_service(nssm_service_t *service) {
\r
509 if (! service) return 1;
\r
511 /* Open service manager */
\r
512 SC_HANDLE services = open_service_manager();
\r
514 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
515 cleanup_nssm_service(service);
\r
519 /* Get path of this program */
\r
520 GetModuleFileName(0, service->image, _countof(service->image));
\r
522 /* Create the service - settings will be changed in edit_service() */
\r
523 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
524 if (! service->handle) {
\r
525 print_message(stderr, NSSM_MESSAGE_CREATESERVICE_FAILED);
\r
526 CloseServiceHandle(services);
\r
530 if (edit_service(service, false)) {
\r
531 DeleteService(service->handle);
\r
532 CloseServiceHandle(services);
\r
536 print_message(stdout, NSSM_MESSAGE_SERVICE_INSTALLED, service->name);
\r
539 CloseServiceHandle(services);
\r
544 /* Edit the service. */
\r
545 int edit_service(nssm_service_t *service, bool editing) {
\r
546 if (! service) return 1;
\r
549 The only two valid flags for service type are SERVICE_WIN32_OWN_PROCESS
\r
550 and SERVICE_INTERACTIVE_PROCESS.
\r
552 service->type &= SERVICE_INTERACTIVE_PROCESS;
\r
553 service->type |= SERVICE_WIN32_OWN_PROCESS;
\r
555 /* Startup type. */
\r
556 unsigned long startup;
\r
557 switch (service->startup) {
\r
558 case NSSM_STARTUP_MANUAL: startup = SERVICE_DEMAND_START; break;
\r
559 case NSSM_STARTUP_DISABLED: startup = SERVICE_DISABLED; break;
\r
560 default: startup = SERVICE_AUTO_START;
\r
563 /* Display name. */
\r
564 if (! service->displayname[0]) _sntprintf_s(service->displayname, _countof(service->displayname), _TRUNCATE, _T("%s"), service->name);
\r
567 Username must be NULL if we aren't changing or an account name.
\r
568 We must explicitly user LOCALSYSTEM to change it when we are editing.
\r
569 Password must be NULL if we aren't changing, a password or "".
\r
570 Empty passwords are valid but we won't allow them in the GUI.
\r
572 TCHAR *username = 0;
\r
573 TCHAR *password = 0;
\r
574 if (service->usernamelen) {
\r
575 username = service->username;
\r
576 if (service->passwordlen) password = service->password;
\r
577 else password = _T("");
\r
579 else if (editing) username = NSSM_LOCALSYSTEM_ACCOUNT;
\r
581 if (grant_logon_as_service(username)) {
\r
582 print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username);
\r
586 if (! ChangeServiceConfig(service->handle, service->type, startup, SERVICE_NO_CHANGE, 0, 0, 0, 0, username, password, service->displayname)) {
\r
587 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
591 if (service->description[0] || editing) {
\r
592 SERVICE_DESCRIPTION description;
\r
593 ZeroMemory(&description, sizeof(description));
\r
594 if (service->description[0]) description.lpDescription = service->description;
\r
595 else description.lpDescription = 0;
\r
596 if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_DESCRIPTION, &description)) {
\r
597 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_DESCRIPTION_FAILED, service->name, error_string(GetLastError()), 0);
\r
601 SERVICE_DELAYED_AUTO_START_INFO delayed;
\r
602 ZeroMemory(&delayed, sizeof(delayed));
\r
603 if (service->startup == NSSM_STARTUP_DELAYED) delayed.fDelayedAutostart = 1;
\r
604 else delayed.fDelayedAutostart = 0;
\r
605 /* Delayed startup isn't supported until Vista. */
\r
606 if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, &delayed)) {
\r
607 unsigned long error = GetLastError();
\r
608 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
\r
609 if (error != ERROR_INVALID_LEVEL) {
\r
610 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_DELAYED_AUTO_START_INFO_FAILED, service->name, error_string(error), 0);
\r
614 /* Don't mess with parameters which aren't ours. */
\r
615 if (! service->native) {
\r
616 /* Now we need to put the parameters into the registry */
\r
617 if (create_parameters(service, editing)) {
\r
618 print_message(stderr, NSSM_MESSAGE_CREATE_PARAMETERS_FAILED);
\r
622 set_service_recovery(service);
\r
628 /* Remove the service */
\r
629 int remove_service(nssm_service_t *service) {
\r
630 if (! service) return 1;
\r
632 /* Open service manager */
\r
633 SC_HANDLE services = open_service_manager();
\r
635 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
639 /* Try to open the service */
\r
640 service->handle = OpenService(services, service->name, SC_MANAGER_ALL_ACCESS);
\r
641 if (! service->handle) {
\r
642 print_message(stderr, NSSM_MESSAGE_OPENSERVICE_FAILED);
\r
643 CloseServiceHandle(services);
\r
647 /* Get the canonical service name. We open it case insensitively. */
\r
648 unsigned long bufsize = _countof(service->displayname);
\r
649 GetServiceDisplayName(services, service->name, service->displayname, &bufsize);
\r
650 bufsize = _countof(service->name);
\r
651 GetServiceKeyName(services, service->displayname, service->name, &bufsize);
\r
653 /* Try to delete the service */
\r
654 if (! DeleteService(service->handle)) {
\r
655 print_message(stderr, NSSM_MESSAGE_DELETESERVICE_FAILED);
\r
656 CloseServiceHandle(services);
\r
661 CloseServiceHandle(services);
\r
663 print_message(stdout, NSSM_MESSAGE_SERVICE_REMOVED, service->name);
\r
667 /* Service initialisation */
\r
668 void WINAPI service_main(unsigned long argc, TCHAR **argv) {
\r
669 nssm_service_t *service = alloc_nssm_service();
\r
670 if (! service) return;
\r
672 if (_sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]) < 0) {
\r
673 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("service->name"), _T("service_main()"), 0);
\r
677 /* We can use a condition variable in a critical section on Vista or later. */
\r
678 if (imports.SleepConditionVariableCS && imports.WakeConditionVariable) use_critical_section = true;
\r
679 else use_critical_section = false;
\r
681 /* Initialise status */
\r
682 ZeroMemory(&service->status, sizeof(service->status));
\r
683 service->status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;
\r
684 service->status.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE;
\r
685 service->status.dwWin32ExitCode = NO_ERROR;
\r
686 service->status.dwServiceSpecificExitCode = 0;
\r
687 service->status.dwCheckPoint = 0;
\r
688 service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;
\r
690 /* Signal we AREN'T running the server */
\r
691 service->process_handle = 0;
\r
694 /* Register control handler */
\r
695 service->status_handle = RegisterServiceCtrlHandlerEx(NSSM, service_control_handler, (void *) service);
\r
696 if (! service->status_handle) {
\r
697 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_REGISTERSERVICECTRLHANDER_FAILED, error_string(GetLastError()), 0);
\r
701 log_service_control(service->name, 0, true);
\r
703 service->status.dwCurrentState = SERVICE_START_PENDING;
\r
704 service->status.dwWaitHint = service->throttle_delay + NSSM_WAITHINT_MARGIN;
\r
705 SetServiceStatus(service->status_handle, &service->status);
\r
708 /* Try to create the exit action parameters; we don't care if it fails */
\r
709 create_exit_action(service->name, exit_action_strings[0], false);
\r
711 SC_HANDLE services = open_service_manager();
\r
713 service->handle = OpenService(services, service->name, SC_MANAGER_ALL_ACCESS);
\r
714 set_service_recovery(service);
\r
715 CloseServiceHandle(services);
\r
719 /* Used for signalling a resume if the service pauses when throttled. */
\r
720 if (use_critical_section) {
\r
721 InitializeCriticalSection(&service->throttle_section);
\r
722 service->throttle_section_initialised = true;
\r
725 service->throttle_timer = CreateWaitableTimer(0, 1, 0);
\r
726 if (! service->throttle_timer) {
\r
727 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_CREATEWAITABLETIMER_FAILED, service->name, error_string(GetLastError()), 0);
\r
731 monitor_service(service);
\r
734 /* Make sure service recovery actions are taken where necessary */
\r
735 void set_service_recovery(nssm_service_t *service) {
\r
736 SERVICE_FAILURE_ACTIONS_FLAG flag;
\r
737 ZeroMemory(&flag, sizeof(flag));
\r
738 flag.fFailureActionsOnNonCrashFailures = true;
\r
740 /* This functionality was added in Vista so the call may fail */
\r
741 if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, &flag)) {
\r
742 unsigned long error = GetLastError();
\r
743 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
\r
744 if (error != ERROR_INVALID_LEVEL) {
\r
745 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_FAILURE_ACTIONS_FAILED, service->name, error_string(error), 0);
\r
750 int monitor_service(nssm_service_t *service) {
\r
751 /* Set service status to started */
\r
752 int ret = start_service(service);
\r
755 _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%d"), ret);
\r
756 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_START_SERVICE_FAILED, service->exe, service->name, ret, 0);
\r
759 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_STARTED_SERVICE, service->exe, service->flags, service->name, service->dir, 0);
\r
761 /* Monitor service */
\r
762 if (! RegisterWaitForSingleObject(&service->wait_handle, service->process_handle, end_service, (void *) service, INFINITE, WT_EXECUTEONLYONCE | WT_EXECUTELONGFUNCTION)) {
\r
763 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_REGISTERWAITFORSINGLEOBJECT_FAILED, service->name, service->exe, error_string(GetLastError()), 0);
\r
769 TCHAR *service_control_text(unsigned long control) {
\r
771 /* HACK: there is no SERVICE_CONTROL_START constant */
\r
772 case 0: return _T("START");
\r
773 case SERVICE_CONTROL_STOP: return _T("STOP");
\r
774 case SERVICE_CONTROL_SHUTDOWN: return _T("SHUTDOWN");
\r
775 case SERVICE_CONTROL_PAUSE: return _T("PAUSE");
\r
776 case SERVICE_CONTROL_CONTINUE: return _T("CONTINUE");
\r
777 case SERVICE_CONTROL_INTERROGATE: return _T("INTERROGATE");
\r
782 void log_service_control(TCHAR *service_name, unsigned long control, bool handled) {
\r
783 TCHAR *text = service_control_text(control);
\r
784 unsigned long event;
\r
787 /* "0x" + 8 x hex + NULL */
\r
788 text = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, 11 * sizeof(TCHAR));
\r
790 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);
\r
793 if (_sntprintf_s(text, 11, _TRUNCATE, _T("0x%08x"), control) < 0) {
\r
794 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);
\r
795 HeapFree(GetProcessHeap(), 0, text);
\r
799 event = NSSM_EVENT_SERVICE_CONTROL_UNKNOWN;
\r
801 else if (handled) event = NSSM_EVENT_SERVICE_CONTROL_HANDLED;
\r
802 else event = NSSM_EVENT_SERVICE_CONTROL_NOT_HANDLED;
\r
804 log_event(EVENTLOG_INFORMATION_TYPE, event, service_name, text, 0);
\r
806 if (event == NSSM_EVENT_SERVICE_CONTROL_UNKNOWN) {
\r
807 HeapFree(GetProcessHeap(), 0, text);
\r
811 /* Service control handler */
\r
812 unsigned long WINAPI service_control_handler(unsigned long control, unsigned long event, void *data, void *context) {
\r
813 nssm_service_t *service = (nssm_service_t *) context;
\r
816 case SERVICE_CONTROL_INTERROGATE:
\r
817 /* We always keep the service status up-to-date so this is a no-op. */
\r
820 case SERVICE_CONTROL_SHUTDOWN:
\r
821 case SERVICE_CONTROL_STOP:
\r
822 log_service_control(service->name, control, true);
\r
824 We MUST acknowledge the stop request promptly but we're committed to
\r
825 waiting for the application to exit. Spawn a new thread to wait
\r
826 while we acknowledge the request.
\r
828 if (! CreateThread(NULL, 0, shutdown_service, context, 0, NULL)) {
\r
829 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETHREAD_FAILED, error_string(GetLastError()), 0);
\r
832 We couldn't create a thread to tidy up so we'll have to force the tidyup
\r
833 to complete in time in this thread.
\r
835 service->kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;
\r
836 service->kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;
\r
837 service->kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;
\r
839 stop_service(service, 0, true, true);
\r
843 case SERVICE_CONTROL_CONTINUE:
\r
844 log_service_control(service->name, control, true);
\r
845 service->throttle = 0;
\r
846 if (use_critical_section) imports.WakeConditionVariable(&service->throttle_condition);
\r
848 if (! service->throttle_timer) return ERROR_CALL_NOT_IMPLEMENTED;
\r
849 ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));
\r
850 SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);
\r
852 service->status.dwCurrentState = SERVICE_CONTINUE_PENDING;
\r
853 service->status.dwWaitHint = throttle_milliseconds(service->throttle) + NSSM_WAITHINT_MARGIN;
\r
854 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESET_THROTTLE, service->name, 0);
\r
855 SetServiceStatus(service->status_handle, &service->status);
\r
858 case SERVICE_CONTROL_PAUSE:
\r
860 We don't accept pause messages but it isn't possible to register
\r
861 only for continue messages so we have to handle this case.
\r
863 log_service_control(service->name, control, false);
\r
864 return ERROR_CALL_NOT_IMPLEMENTED;
\r
867 /* Unknown control */
\r
868 log_service_control(service->name, control, false);
\r
869 return ERROR_CALL_NOT_IMPLEMENTED;
\r
872 /* Start the service */
\r
873 int start_service(nssm_service_t *service) {
\r
874 service->stopping = false;
\r
875 service->allow_restart = true;
\r
877 if (service->process_handle) return 0;
\r
879 /* Allocate a STARTUPINFO structure for a new process */
\r
881 ZeroMemory(&si, sizeof(si));
\r
882 si.cb = sizeof(si);
\r
884 /* Allocate a PROCESSINFO structure for the process */
\r
885 PROCESS_INFORMATION pi;
\r
886 ZeroMemory(&pi, sizeof(pi));
\r
888 /* Get startup parameters */
\r
889 int ret = get_parameters(service, &si);
\r
891 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_PARAMETERS_FAILED, service->name, 0);
\r
892 return stop_service(service, 2, true, true);
\r
895 /* Launch executable with arguments */
\r
896 TCHAR cmd[CMD_LENGTH];
\r
897 if (_sntprintf_s(cmd, _countof(cmd), _TRUNCATE, _T("\"%s\" %s"), service->exe, service->flags) < 0) {
\r
898 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("command line"), _T("start_service"), 0);
\r
899 close_output_handles(&si);
\r
900 return stop_service(service, 2, true, true);
\r
903 throttle_restart(service);
\r
905 bool inherit_handles = false;
\r
906 if (si.dwFlags & STARTF_USESTDHANDLES) inherit_handles = true;
\r
907 unsigned long flags = 0;
\r
909 flags |= CREATE_UNICODE_ENVIRONMENT;
\r
911 if (! CreateProcess(0, cmd, 0, 0, inherit_handles, flags, service->env, service->dir, &si, &pi)) {
\r
912 unsigned long exitcode = 3;
\r
913 unsigned long error = GetLastError();
\r
914 if (error == ERROR_INVALID_PARAMETER && service->env) {
\r
915 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED_INVALID_ENVIRONMENT, service->name, service->exe, NSSM_REG_ENV, 0);
\r
916 if (test_environment(service->env)) exitcode = 4;
\r
918 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service->name, service->exe, error_string(error), 0);
\r
919 close_output_handles(&si);
\r
920 return stop_service(service, exitcode, true, true);
\r
922 service->process_handle = pi.hProcess;
\r
923 service->pid = pi.dwProcessId;
\r
925 if (get_process_creation_time(service->process_handle, &service->creation_time)) ZeroMemory(&service->creation_time, sizeof(service->creation_time));
\r
927 close_output_handles(&si);
\r
930 Wait for a clean startup before changing the service status to RUNNING
\r
931 but be mindful of the fact that we are blocking the service control manager
\r
932 so abandon the wait before too much time has elapsed.
\r
934 unsigned long delay = service->throttle_delay;
\r
935 if (delay > NSSM_SERVICE_STATUS_DEADLINE) {
\r
936 TCHAR delay_milliseconds[16];
\r
937 _sntprintf_s(delay_milliseconds, _countof(delay_milliseconds), _TRUNCATE, _T("%lu"), delay);
\r
938 TCHAR deadline_milliseconds[16];
\r
939 _sntprintf_s(deadline_milliseconds, _countof(deadline_milliseconds), _TRUNCATE, _T("%lu"), NSSM_SERVICE_STATUS_DEADLINE);
\r
940 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_STARTUP_DELAY_TOO_LONG, service->name, delay_milliseconds, NSSM, deadline_milliseconds, 0);
\r
941 delay = NSSM_SERVICE_STATUS_DEADLINE;
\r
943 unsigned long deadline = WaitForSingleObject(service->process_handle, delay);
\r
945 /* Signal successful start */
\r
946 service->status.dwCurrentState = SERVICE_RUNNING;
\r
947 SetServiceStatus(service->status_handle, &service->status);
\r
949 /* Continue waiting for a clean startup. */
\r
950 if (deadline == WAIT_TIMEOUT) {
\r
951 if (service->throttle_delay > delay) {
\r
952 if (WaitForSingleObject(service->process_handle, service->throttle_delay - delay) == WAIT_TIMEOUT) service->throttle = 0;
\r
954 else service->throttle = 0;
\r
960 /* Stop the service */
\r
961 int stop_service(nssm_service_t *service, unsigned long exitcode, bool graceful, bool default_action) {
\r
962 service->allow_restart = false;
\r
963 if (service->wait_handle) {
\r
964 UnregisterWait(service->wait_handle);
\r
965 service->wait_handle = 0;
\r
968 if (default_action && ! exitcode && ! graceful) {
\r
969 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
973 /* Signal we are stopping */
\r
975 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
976 service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;
\r
977 SetServiceStatus(service->status_handle, &service->status);
\r
980 /* Nothing to do if service isn't running */
\r
981 if (service->pid) {
\r
982 /* Shut down service */
\r
983 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_TERMINATEPROCESS, service->name, service->exe, 0);
\r
984 kill_process(service, service->process_handle, service->pid, 0);
\r
986 else log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_PROCESS_ALREADY_STOPPED, service->name, service->exe, 0);
\r
988 end_service((void *) service, true);
\r
990 /* Signal we stopped */
\r
992 service->status.dwCurrentState = SERVICE_STOPPED;
\r
994 service->status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
\r
995 service->status.dwServiceSpecificExitCode = exitcode;
\r
998 service->status.dwWin32ExitCode = NO_ERROR;
\r
999 service->status.dwServiceSpecificExitCode = 0;
\r
1001 SetServiceStatus(service->status_handle, &service->status);
\r
1007 /* Callback function triggered when the server exits */
\r
1008 void CALLBACK end_service(void *arg, unsigned char why) {
\r
1009 nssm_service_t *service = (nssm_service_t *) arg;
\r
1011 if (service->stopping) return;
\r
1013 service->stopping = true;
\r
1015 /* Check exit code */
\r
1016 unsigned long exitcode = 0;
\r
1018 if (service->process_handle) {
\r
1019 GetExitCodeProcess(service->process_handle, &exitcode);
\r
1020 if (exitcode == STILL_ACTIVE || get_process_exit_time(service->process_handle, &service->exit_time)) GetSystemTimeAsFileTime(&service->exit_time);
\r
1021 CloseHandle(service->process_handle);
\r
1023 else GetSystemTimeAsFileTime(&service->exit_time);
\r
1025 service->process_handle = 0;
\r
1028 Log that the service ended BEFORE logging about killing the process
\r
1029 tree. See below for the possible values of the why argument.
\r
1032 _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), exitcode);
\r
1033 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ENDED_SERVICE, service->exe, service->name, code, 0);
\r
1037 if (exitcode == STILL_ACTIVE) exitcode = 0;
\r
1038 if (service->pid) kill_process_tree(service, service->pid, exitcode, service->pid);
\r
1042 The why argument is true if our wait timed out or false otherwise.
\r
1043 Our wait is infinite so why will never be true when called by the system.
\r
1044 If it is indeed true, assume we were called from stop_service() because
\r
1045 this is a controlled shutdown, and don't take any restart action.
\r
1048 if (! service->allow_restart) return;
\r
1050 /* What action should we take? */
\r
1051 int action = NSSM_EXIT_RESTART;
\r
1052 TCHAR action_string[ACTION_LEN];
\r
1053 bool default_action;
\r
1054 if (! get_exit_action(service->name, &exitcode, action_string, &default_action)) {
\r
1055 for (int i = 0; exit_action_strings[i]; i++) {
\r
1056 if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
1064 /* Try to restart the service or return failure code to service manager */
\r
1065 case NSSM_EXIT_RESTART:
\r
1066 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_RESTART, service->name, code, exit_action_strings[action], service->exe, 0);
\r
1067 while (monitor_service(service)) {
\r
1068 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_RESTART_SERVICE_FAILED, service->exe, service->name, 0);
\r
1073 /* Do nothing, just like srvany would */
\r
1074 case NSSM_EXIT_IGNORE:
\r
1075 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_IGNORE, service->name, code, exit_action_strings[action], service->exe, 0);
\r
1079 /* Tell the service manager we are finished */
\r
1080 case NSSM_EXIT_REALLY:
\r
1081 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_REALLY, service->name, code, exit_action_strings[action], 0);
\r
1082 stop_service(service, exitcode, true, default_action);
\r
1085 /* Fake a crash so pre-Vista service managers will run recovery actions. */
\r
1086 case NSSM_EXIT_UNCLEAN:
\r
1087 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_UNCLEAN, service->name, code, exit_action_strings[action], 0);
\r
1088 stop_service(service, exitcode, false, default_action);
\r
1095 void throttle_restart(nssm_service_t *service) {
\r
1096 /* This can't be a restart if the service is already running. */
\r
1097 if (! service->throttle++) return;
\r
1099 int ms = throttle_milliseconds(service->throttle);
\r
1101 if (service->throttle > 7) service->throttle = 8;
\r
1103 TCHAR threshold[8], milliseconds[8];
\r
1104 _sntprintf_s(threshold, _countof(threshold), _TRUNCATE, _T("%lu"), service->throttle_delay);
\r
1105 _sntprintf_s(milliseconds, _countof(milliseconds), _TRUNCATE, _T("%lu"), ms);
\r
1106 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_THROTTLED, service->name, threshold, milliseconds, 0);
\r
1108 if (use_critical_section) EnterCriticalSection(&service->throttle_section);
\r
1109 else if (service->throttle_timer) {
\r
1110 ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));
\r
1111 service->throttle_duetime.QuadPart = 0 - (ms * 10000LL);
\r
1112 SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);
\r
1115 service->status.dwCurrentState = SERVICE_PAUSED;
\r
1116 SetServiceStatus(service->status_handle, &service->status);
\r
1118 if (use_critical_section) {
\r
1119 imports.SleepConditionVariableCS(&service->throttle_condition, &service->throttle_section, ms);
\r
1120 LeaveCriticalSection(&service->throttle_section);
\r
1123 if (service->throttle_timer) WaitForSingleObject(service->throttle_timer, INFINITE);
\r
1129 When responding to a stop (or any other) request we need to set dwWaitHint to
\r
1130 the number of milliseconds we expect the operation to take, and optionally
\r
1131 increase dwCheckPoint. If dwWaitHint milliseconds elapses without the
\r
1132 operation completing or dwCheckPoint increasing, the system will consider the
\r
1133 service to be hung.
\r
1135 However the system will consider the service to be hung after 30000
\r
1136 milliseconds regardless of the value of dwWaitHint if dwCheckPoint has not
\r
1137 changed. Therefore if we want to wait longer than that we must periodically
\r
1138 increase dwCheckPoint.
\r
1140 Furthermore, it will consider the service to be hung after 60000 milliseconds
\r
1141 regardless of the value of dwCheckPoint unless dwWaitHint is increased every
\r
1142 time dwCheckPoint is also increased.
\r
1144 Our strategy then is to retrieve the initial dwWaitHint and wait for
\r
1145 NSSM_SERVICE_STATUS_DEADLINE milliseconds. If the process is still running
\r
1146 and we haven't finished waiting we increment dwCheckPoint and add whichever is
\r
1147 smaller of NSSM_SERVICE_STATUS_DEADLINE or the remaining timeout to
\r
1150 Only doing both these things will prevent the system from killing the service.
\r
1152 Returns: 1 if the wait timed out.
\r
1153 0 if the wait completed.
\r
1156 int await_shutdown(nssm_service_t *service, TCHAR *function_name, unsigned long timeout) {
\r
1157 unsigned long interval;
\r
1158 unsigned long waithint;
\r
1159 unsigned long ret;
\r
1160 unsigned long waited;
\r
1161 TCHAR interval_milliseconds[16];
\r
1162 TCHAR timeout_milliseconds[16];
\r
1163 TCHAR waited_milliseconds[16];
\r
1164 TCHAR *function = function_name;
\r
1166 /* Add brackets to function name. */
\r
1167 size_t funclen = _tcslen(function_name) + 3;
\r
1168 TCHAR *func = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, funclen * sizeof(TCHAR));
\r
1170 if (_sntprintf_s(func, funclen, _TRUNCATE, _T("%s()"), function_name) > -1) function = func;
\r
1173 _sntprintf_s(timeout_milliseconds, _countof(timeout_milliseconds), _TRUNCATE, _T("%lu"), timeout);
\r
1175 waithint = service->status.dwWaitHint;
\r
1177 while (waited < timeout) {
\r
1178 interval = timeout - waited;
\r
1179 if (interval > NSSM_SERVICE_STATUS_DEADLINE) interval = NSSM_SERVICE_STATUS_DEADLINE;
\r
1181 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
1182 service->status.dwWaitHint += interval;
\r
1183 service->status.dwCheckPoint++;
\r
1184 SetServiceStatus(service->status_handle, &service->status);
\r
1187 _sntprintf_s(waited_milliseconds, _countof(waited_milliseconds), _TRUNCATE, _T("%lu"), waited);
\r
1188 _sntprintf_s(interval_milliseconds, _countof(interval_milliseconds), _TRUNCATE, _T("%lu"), interval);
\r
1189 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_AWAITING_SHUTDOWN, function, service->name, waited_milliseconds, interval_milliseconds, timeout_milliseconds, 0);
\r
1192 switch (WaitForSingleObject(service->process_handle, interval)) {
\r
1193 case WAIT_OBJECT_0:
\r
1197 case WAIT_TIMEOUT:
\r
1206 waited += interval;
\r
1210 if (func) HeapFree(GetProcessHeap(), 0, func);
\r