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
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 set_service_description(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR *buffer) {
\r
69 SERVICE_DESCRIPTION description;
\r
70 ZeroMemory(&description, sizeof(description));
\r
72 lpDescription must be NULL if we aren't changing, the new description
\r
75 if (buffer && buffer[0]) description.lpDescription = buffer;
\r
76 else description.lpDescription = _T("");
\r
78 if (ChangeServiceConfig2(service_handle, SERVICE_CONFIG_DESCRIPTION, &description)) return 0;
\r
80 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_DESCRIPTION_FAILED, service_name, error_string(GetLastError()), 0);
\r
84 int get_service_description(const TCHAR *service_name, SC_HANDLE service_handle, unsigned long len, TCHAR *buffer) {
\r
85 if (! buffer) return 1;
\r
87 unsigned long bufsize;
\r
88 QueryServiceConfig2(service_handle, SERVICE_CONFIG_DESCRIPTION, 0, 0, &bufsize);
\r
89 unsigned long error = GetLastError();
\r
90 if (error == ERROR_INSUFFICIENT_BUFFER) {
\r
91 SERVICE_DESCRIPTION *description = (SERVICE_DESCRIPTION *) HeapAlloc(GetProcessHeap(), 0, bufsize);
\r
92 if (! description) {
\r
93 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("SERVICE_CONFIG_DESCRIPTION"), _T("get_service_description()"));
\r
97 if (QueryServiceConfig2(service_handle, SERVICE_CONFIG_DESCRIPTION, (unsigned char *) description, bufsize, &bufsize)) {
\r
98 if (description->lpDescription) _sntprintf_s(buffer, len, _TRUNCATE, _T("%s"), description->lpDescription);
\r
99 else ZeroMemory(buffer, len * sizeof(TCHAR));
\r
100 HeapFree(GetProcessHeap(), 0, description);
\r
104 HeapFree(GetProcessHeap(), 0, description);
\r
105 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_CONFIG_DESCRIPTION"), error_string(error));
\r
110 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_CONFIG_DESCRIPTION"), error_string(error));
\r
117 int get_service_startup(const TCHAR *service_name, SC_HANDLE service_handle, const QUERY_SERVICE_CONFIG *qsc, unsigned long *startup) {
\r
118 if (! qsc) return 1;
\r
120 switch (qsc->dwStartType) {
\r
121 case SERVICE_DEMAND_START: *startup = NSSM_STARTUP_MANUAL; break;
\r
122 case SERVICE_DISABLED: *startup = NSSM_STARTUP_DISABLED; break;
\r
123 default: *startup = NSSM_STARTUP_AUTOMATIC;
\r
126 if (*startup != NSSM_STARTUP_AUTOMATIC) return 0;
\r
128 /* Check for delayed start. */
\r
129 unsigned long bufsize;
\r
130 unsigned long error;
\r
131 QueryServiceConfig2(service_handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, 0, 0, &bufsize);
\r
132 error = GetLastError();
\r
133 if (error == ERROR_INSUFFICIENT_BUFFER) {
\r
134 SERVICE_DELAYED_AUTO_START_INFO *info = (SERVICE_DELAYED_AUTO_START_INFO *) HeapAlloc(GetProcessHeap(), 0, bufsize);
\r
136 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("SERVICE_DELAYED_AUTO_START_INFO"), _T("get_service_startup()"));
\r
140 if (QueryServiceConfig2(service_handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, (unsigned char *) info, bufsize, &bufsize)) {
\r
141 if (info->fDelayedAutostart) *startup = NSSM_STARTUP_DELAYED;
\r
142 HeapFree(GetProcessHeap(), 0, info);
\r
146 error = GetLastError();
\r
147 if (error != ERROR_INVALID_LEVEL) {
\r
148 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_CONFIG_DELAYED_AUTO_START_INFO"), error_string(error));
\r
153 else if (error != ERROR_INVALID_LEVEL) {
\r
154 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_DELAYED_AUTO_START_INFO"), error_string(error));
\r
161 int get_service_username(const TCHAR *service_name, const QUERY_SERVICE_CONFIG *qsc, TCHAR **username, size_t *usernamelen) {
\r
162 if (! username) return 1;
\r
163 if (! usernamelen) return 1;
\r
168 if (! qsc) return 1;
\r
170 if (str_equiv(qsc->lpServiceStartName, NSSM_LOCALSYSTEM_ACCOUNT)) return 0;
\r
172 size_t len = _tcslen(qsc->lpServiceStartName);
\r
173 *username = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(TCHAR));
\r
175 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("username"), _T("get_service_username()"));
\r
179 memmove(*username, qsc->lpServiceStartName, (len + 1) * sizeof(TCHAR));
\r
180 *usernamelen = len;
\r
185 int grant_logon_as_service(const TCHAR *username) {
\r
186 if (str_equiv(username, NSSM_LOCALSYSTEM_ACCOUNT)) return 0;
\r
188 /* Open Policy object. */
\r
189 LSA_OBJECT_ATTRIBUTES attributes;
\r
190 ZeroMemory(&attributes, sizeof(attributes));
\r
194 NTSTATUS status = LsaOpenPolicy(0, &attributes, POLICY_ALL_ACCESS, &policy);
\r
196 print_message(stderr, NSSM_MESSAGE_LSAOPENPOLICY_FAILED, error_string(LsaNtStatusToWinError(status)));
\r
200 /* Look up SID for the account. */
\r
201 LSA_UNICODE_STRING lsa_username;
\r
203 lsa_username.Buffer = (wchar_t *) username;
\r
204 lsa_username.Length = (unsigned short) _tcslen(username) * sizeof(TCHAR);
\r
205 lsa_username.MaximumLength = lsa_username.Length + sizeof(TCHAR);
\r
208 mbstowcs_s(&buflen, NULL, 0, username, _TRUNCATE);
\r
209 lsa_username.MaximumLength = buflen * sizeof(wchar_t);
\r
210 lsa_username.Length = lsa_username.MaximumLength - sizeof(wchar_t);
\r
211 lsa_username.Buffer = (wchar_t *) HeapAlloc(GetProcessHeap(), 0, lsa_username.MaximumLength);
\r
212 if (lsa_username.Buffer) mbstowcs_s(&buflen, lsa_username.Buffer, lsa_username.MaximumLength, username, _TRUNCATE);
\r
215 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("LSA_UNICODE_STRING"), _T("grant_logon_as_service()"));
\r
220 LSA_REFERENCED_DOMAIN_LIST *translated_domains;
\r
221 LSA_TRANSLATED_SID *translated_sid;
\r
222 status = LsaLookupNames(policy, 1, &lsa_username, &translated_domains, &translated_sid);
\r
224 HeapFree(GetProcessHeap(), 0, lsa_username.Buffer);
\r
227 LsaFreeMemory(translated_domains);
\r
228 LsaFreeMemory(translated_sid);
\r
230 print_message(stderr, NSSM_MESSAGE_LSALOOKUPNAMES_FAILED, username, error_string(LsaNtStatusToWinError(status)));
\r
234 if (translated_sid->Use != SidTypeUser) {
\r
235 LsaFreeMemory(translated_domains);
\r
236 LsaFreeMemory(translated_sid);
\r
238 print_message(stderr, NSSM_GUI_INVALID_USERNAME, username);
\r
242 LSA_TRUST_INFORMATION *trust = &translated_domains->Domains[translated_sid->DomainIndex];
\r
243 if (! trust || ! IsValidSid(trust->Sid)) {
\r
244 LsaFreeMemory(translated_domains);
\r
245 LsaFreeMemory(translated_sid);
\r
247 print_message(stderr, NSSM_GUI_INVALID_USERNAME, username);
\r
251 /* GetSidSubAuthority*() return pointers! */
\r
252 unsigned char *n = GetSidSubAuthorityCount(trust->Sid);
\r
254 /* Convert translated SID to SID. */
\r
255 SID *sid = (SID *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, GetSidLengthRequired(*n + 1));
\r
257 LsaFreeMemory(translated_domains);
\r
258 LsaFreeMemory(translated_sid);
\r
260 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("SID"), _T("grant_logon_as_service"));
\r
264 unsigned long error;
\r
265 if (! InitializeSid(sid, GetSidIdentifierAuthority(trust->Sid), *n + 1)) {
\r
266 error = GetLastError();
\r
267 HeapFree(GetProcessHeap(), 0, sid);
\r
268 LsaFreeMemory(translated_domains);
\r
269 LsaFreeMemory(translated_sid);
\r
271 print_message(stderr, NSSM_MESSAGE_INITIALIZESID_FAILED, username, error_string(error));
\r
275 for (unsigned char i = 0; i <= *n; i++) {
\r
276 unsigned long *sub = GetSidSubAuthority(sid, i);
\r
277 if (i < *n) *sub = *GetSidSubAuthority(trust->Sid, i);
\r
278 else *sub = translated_sid->RelativeId;
\r
281 LsaFreeMemory(translated_domains);
\r
282 LsaFreeMemory(translated_sid);
\r
284 /* Check if the SID has the "Log on as a service" right. */
\r
285 LSA_UNICODE_STRING lsa_right;
\r
286 lsa_right.Buffer = NSSM_LOGON_AS_SERVICE_RIGHT;
\r
287 lsa_right.Length = (unsigned short) wcslen(lsa_right.Buffer) * sizeof(wchar_t);
\r
288 lsa_right.MaximumLength = lsa_right.Length + sizeof(wchar_t);
\r
290 LSA_UNICODE_STRING *rights;
\r
291 unsigned long count = ~0;
\r
292 status = LsaEnumerateAccountRights(policy, sid, &rights, &count);
\r
295 If the account has no rights set LsaEnumerateAccountRights() will return
\r
296 STATUS_OBJECT_NAME_NOT_FOUND and set count to 0.
\r
298 error = LsaNtStatusToWinError(status);
\r
299 if (error != ERROR_FILE_NOT_FOUND) {
\r
300 HeapFree(GetProcessHeap(), 0, sid);
\r
302 print_message(stderr, NSSM_MESSAGE_LSAENUMERATEACCOUNTRIGHTS_FAILED, username, error_string(error));
\r
307 for (unsigned long i = 0; i < count; i++) {
\r
308 if (rights[i].Length != lsa_right.Length) continue;
\r
309 if (_wcsnicmp(rights[i].Buffer, lsa_right.Buffer, lsa_right.MaximumLength)) continue;
\r
310 /* The SID has the right. */
\r
311 HeapFree(GetProcessHeap(), 0, sid);
\r
312 LsaFreeMemory(rights);
\r
316 LsaFreeMemory(rights);
\r
318 /* Add the right. */
\r
319 status = LsaAddAccountRights(policy, sid, &lsa_right, 1);
\r
320 HeapFree(GetProcessHeap(), 0, sid);
\r
323 print_message(stderr, NSSM_MESSAGE_LSAADDACCOUNTRIGHTS_FAILED, error_string(LsaNtStatusToWinError(status)));
\r
327 print_message(stdout, NSSM_MESSAGE_GRANTED_LOGON_AS_SERVICE, username);
\r
331 /* Set default values which aren't zero. */
\r
332 void set_nssm_service_defaults(nssm_service_t *service) {
\r
333 if (! service) return;
\r
335 service->type = SERVICE_WIN32_OWN_PROCESS;
\r
336 service->stdin_sharing = NSSM_STDIN_SHARING;
\r
337 service->stdin_disposition = NSSM_STDIN_DISPOSITION;
\r
338 service->stdin_flags = NSSM_STDIN_FLAGS;
\r
339 service->stdout_sharing = NSSM_STDOUT_SHARING;
\r
340 service->stdout_disposition = NSSM_STDOUT_DISPOSITION;
\r
341 service->stdout_flags = NSSM_STDOUT_FLAGS;
\r
342 service->stderr_sharing = NSSM_STDERR_SHARING;
\r
343 service->stderr_disposition = NSSM_STDERR_DISPOSITION;
\r
344 service->stderr_flags = NSSM_STDERR_FLAGS;
\r
345 service->throttle_delay = NSSM_RESET_THROTTLE_RESTART;
\r
346 service->stop_method = ~0;
\r
347 service->kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;
\r
348 service->kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;
\r
349 service->kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;
\r
352 /* Allocate and zero memory for a service. */
\r
353 nssm_service_t *alloc_nssm_service() {
\r
354 nssm_service_t *service = (nssm_service_t *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(nssm_service_t));
\r
355 if (! service) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("service"), _T("alloc_nssm_service()"), 0);
\r
359 /* Free memory for a service. */
\r
360 void cleanup_nssm_service(nssm_service_t *service) {
\r
361 if (! service) return;
\r
362 if (service->username) HeapFree(GetProcessHeap(), 0, service->username);
\r
363 if (service->password) {
\r
364 SecureZeroMemory(service->password, service->passwordlen);
\r
365 HeapFree(GetProcessHeap(), 0, service->password);
\r
367 if (service->env) HeapFree(GetProcessHeap(), 0, service->env);
\r
368 if (service->env_extra) HeapFree(GetProcessHeap(), 0, service->env_extra);
\r
369 if (service->handle) CloseServiceHandle(service->handle);
\r
370 if (service->process_handle) CloseHandle(service->process_handle);
\r
371 if (service->wait_handle) UnregisterWait(service->process_handle);
\r
372 if (service->throttle_section_initialised) DeleteCriticalSection(&service->throttle_section);
\r
373 if (service->throttle_timer) CloseHandle(service->throttle_timer);
\r
374 HeapFree(GetProcessHeap(), 0, service);
\r
377 /* About to install the service */
\r
378 int pre_install_service(int argc, TCHAR **argv) {
\r
379 nssm_service_t *service = alloc_nssm_service();
\r
380 set_nssm_service_defaults(service);
\r
381 if (argc) _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]);
\r
383 /* Show the dialogue box if we didn't give the service name and path */
\r
384 if (argc < 2) return nssm_gui(IDD_INSTALL, service);
\r
387 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("service"), _T("pre_install_service()"));
\r
390 _sntprintf_s(service->exe, _countof(service->exe), _TRUNCATE, _T("%s"), argv[1]);
\r
392 /* Arguments are optional */
\r
393 size_t flagslen = 0;
\r
396 for (i = 2; i < argc; i++) flagslen += _tcslen(argv[i]) + 1;
\r
397 if (! flagslen) flagslen = 1;
\r
398 if (flagslen > _countof(service->flags)) {
\r
399 print_message(stderr, NSSM_MESSAGE_FLAGS_TOO_LONG);
\r
403 for (i = 2; i < argc; i++) {
\r
404 size_t len = _tcslen(argv[i]);
\r
405 memmove(service->flags + s, argv[i], len * sizeof(TCHAR));
\r
407 if (i < argc - 1) service->flags[s++] = _T(' ');
\r
410 /* Work out directory name */
\r
411 _sntprintf_s(service->dir, _countof(service->dir), _TRUNCATE, _T("%s"), service->exe);
\r
412 strip_basename(service->dir);
\r
414 int ret = install_service(service);
\r
415 cleanup_nssm_service(service);
\r
419 /* About to edit the service. */
\r
420 int pre_edit_service(int argc, TCHAR **argv) {
\r
421 /* Require service name. */
\r
422 if (argc < 2) return usage(1);
\r
424 /* Are we editing on the command line? */
\r
425 enum { MODE_EDITING, MODE_GETTING, MODE_SETTING, MODE_RESETTING } mode = MODE_EDITING;
\r
426 const TCHAR *verb = argv[0];
\r
427 const TCHAR *service_name = argv[1];
\r
428 bool getting = false;
\r
429 bool unsetting = false;
\r
431 /* Minimum number of arguments. */
\r
433 /* Index of first value. */
\r
436 if (str_equiv(verb, _T("get"))) {
\r
438 mode = MODE_GETTING;
\r
440 else if (str_equiv(verb, _T("set"))) {
\r
442 mode = MODE_SETTING;
\r
444 else if (str_equiv(verb, _T("reset")) || str_equiv(verb, _T("unset"))) {
\r
446 mode = MODE_RESETTING;
\r
448 if (argc < mandatory) return usage(1);
\r
450 const TCHAR *parameter = 0;
\r
451 settings_t *setting = 0;
\r
454 /* Validate the parameter. */
\r
455 if (mandatory > 2) {
\r
456 bool additional_mandatory = false;
\r
458 parameter = argv[2];
\r
459 for (i = 0; settings[i].name; i++) {
\r
460 setting = &settings[i];
\r
461 if (! str_equiv(setting->name, parameter)) continue;
\r
462 if (((setting->additional & ADDITIONAL_GETTING) && mode == MODE_GETTING) || ((setting->additional & ADDITIONAL_SETTING) && mode == MODE_SETTING) || ((setting->additional & ADDITIONAL_RESETTING) && mode == MODE_RESETTING)) {
\r
463 additional_mandatory = true;
\r
468 if (! settings[i].name) {
\r
469 print_message(stderr, NSSM_MESSAGE_INVALID_PARAMETER, parameter);
\r
470 for (i = 0; settings[i].name; i++) _ftprintf(stderr, _T("%s\n"), settings[i].name);
\r
473 if (argc < mandatory) return usage(1);
\r
476 if (additional_mandatory) {
\r
477 additional = argv[3];
\r
480 else additional = argv[remainder];
\r
483 nssm_service_t *service = alloc_nssm_service();
\r
484 _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), service_name);
\r
486 /* Open service manager */
\r
487 SC_HANDLE services = open_service_manager();
\r
489 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
493 /* Try to open the service */
\r
494 service->handle = OpenService(services, service->name, SC_MANAGER_ALL_ACCESS);
\r
495 if (! service->handle) {
\r
496 CloseServiceHandle(services);
\r
497 print_message(stderr, NSSM_MESSAGE_OPENSERVICE_FAILED);
\r
501 /* Get system details. */
\r
502 QUERY_SERVICE_CONFIG *qsc = query_service_config(service->name, service->handle);
\r
504 CloseHandle(service->handle);
\r
505 CloseServiceHandle(services);
\r
509 service->type = qsc->dwServiceType;
\r
510 if (! (service->type & SERVICE_WIN32_OWN_PROCESS)) {
\r
511 if (mode != MODE_GETTING) {
\r
512 HeapFree(GetProcessHeap(), 0, qsc);
\r
513 CloseHandle(service->handle);
\r
514 CloseServiceHandle(services);
\r
515 print_message(stderr, NSSM_MESSAGE_CANNOT_EDIT, service->name, NSSM_WIN32_OWN_PROCESS, 0);
\r
520 if (get_service_startup(service->name, service->handle, qsc, &service->startup)) {
\r
521 if (mode != MODE_GETTING) {
\r
522 HeapFree(GetProcessHeap(), 0, qsc);
\r
523 CloseHandle(service->handle);
\r
524 CloseServiceHandle(services);
\r
529 if (get_service_username(service->name, qsc, &service->username, &service->usernamelen)) {
\r
530 if (mode != MODE_GETTING) {
\r
531 HeapFree(GetProcessHeap(), 0, qsc);
\r
532 CloseHandle(service->handle);
\r
533 CloseServiceHandle(services);
\r
538 _sntprintf_s(service->displayname, _countof(service->displayname), _TRUNCATE, _T("%s"), qsc->lpDisplayName);
\r
540 /* Get the canonical service name. We open it case insensitively. */
\r
541 unsigned long bufsize = _countof(service->name);
\r
542 GetServiceKeyName(services, service->displayname, service->name, &bufsize);
\r
544 /* Remember the executable in case it isn't NSSM. */
\r
545 _sntprintf_s(service->image, _countof(service->image), _TRUNCATE, _T("%s"), qsc->lpBinaryPathName);
\r
546 HeapFree(GetProcessHeap(), 0, qsc);
\r
548 /* Get extended system details. */
\r
549 if (get_service_description(service->name, service->handle, _countof(service->description), service->description)) {
\r
550 if (mode != MODE_GETTING) {
\r
551 CloseHandle(service->handle);
\r
552 CloseServiceHandle(services);
\r
557 /* Get NSSM details. */
\r
558 get_parameters(service, 0);
\r
560 CloseServiceHandle(services);
\r
562 if (! service->exe[0]) {
\r
563 service->native = true;
\r
564 if (mode != MODE_GETTING) print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE, service->name, NSSM, service->image);
\r
567 /* Editing with the GUI. */
\r
568 if (mode == MODE_EDITING) {
\r
569 nssm_gui(IDD_EDIT, service);
\r
573 /* Trying to manage App* parameters for a non-NSSM service. */
\r
574 if (! setting->native && service->native) {
\r
575 CloseHandle(service->handle);
\r
576 print_message(stderr, NSSM_MESSAGE_NATIVE_PARAMETER, setting->name, NSSM);
\r
584 if (mode == MODE_GETTING) {
\r
585 if (! service->native) {
\r
586 key = open_registry(service->name, KEY_READ);
\r
587 if (! key) return 4;
\r
590 if (setting->native) ret = get_setting(service->name, service->handle, setting, &value, additional);
\r
591 else ret = get_setting(service->name, key, setting, &value, additional);
\r
593 CloseHandle(service->handle);
\r
597 switch (setting->type) {
\r
598 case REG_EXPAND_SZ:
\r
601 _tprintf(_T("%s\n"), value.string ? value.string : _T(""));
\r
602 HeapFree(GetProcessHeap(), 0, value.string);
\r
606 _tprintf(_T("%u\n"), value.numeric);
\r
610 if (! service->native) RegCloseKey(key);
\r
611 CloseHandle(service->handle);
\r
615 /* Build the value. */
\r
616 if (mode == MODE_RESETTING) {
\r
617 /* Unset the parameter. */
\r
621 /* Set the parameter. */
\r
623 size_t delimiterlen = (setting->additional & ADDITIONAL_CRLF) ? 2 : 1;
\r
624 for (i = remainder; i < argc; i++) len += _tcslen(argv[i]) + delimiterlen;
\r
627 value.string = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, len * sizeof(TCHAR));
\r
628 if (! value.string) {
\r
629 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("value"), _T("edit_service()"));
\r
630 CloseHandle(service->handle);
\r
635 for (i = remainder; i < argc; i++) {
\r
636 size_t len = _tcslen(argv[i]);
\r
637 memmove(value.string + s, argv[i], len * sizeof(TCHAR));
\r
639 if (i < argc - 1) {
\r
640 if (setting->additional & ADDITIONAL_CRLF) {
\r
641 value.string[s++] = _T('\r');
\r
642 value.string[s++] = _T('\n');
\r
644 else value.string[s++] = _T(' ');
\r
647 value.string[s] = _T('\0');
\r
650 if (! service->native) {
\r
651 key = open_registry(service->name, KEY_WRITE);
\r
653 if (value.string) HeapFree(GetProcessHeap(), 0, value.string);
\r
658 if (setting->native) ret = set_setting(service->name, service->handle, setting, &value, additional);
\r
659 else ret = set_setting(service->name, key, setting, &value, additional);
\r
660 if (value.string) HeapFree(GetProcessHeap(), 0, value.string);
\r
662 if (! service->native) RegCloseKey(key);
\r
663 CloseHandle(service->handle);
\r
667 if (! service->native) RegCloseKey(key);
\r
668 CloseHandle(service->handle);
\r
673 /* About to remove the service */
\r
674 int pre_remove_service(int argc, TCHAR **argv) {
\r
675 nssm_service_t *service = alloc_nssm_service();
\r
676 set_nssm_service_defaults(service);
\r
677 if (argc) _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]);
\r
679 /* Show dialogue box if we didn't pass service name and "confirm" */
\r
680 if (argc < 2) return nssm_gui(IDD_REMOVE, service);
\r
681 if (str_equiv(argv[1], _T("confirm"))) {
\r
682 int ret = remove_service(service);
\r
683 cleanup_nssm_service(service);
\r
686 print_message(stderr, NSSM_MESSAGE_PRE_REMOVE_SERVICE);
\r
690 /* Install the service */
\r
691 int install_service(nssm_service_t *service) {
\r
692 if (! service) return 1;
\r
694 /* Open service manager */
\r
695 SC_HANDLE services = open_service_manager();
\r
697 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
698 cleanup_nssm_service(service);
\r
702 /* Get path of this program */
\r
703 GetModuleFileName(0, service->image, _countof(service->image));
\r
705 /* Create the service - settings will be changed in edit_service() */
\r
706 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
707 if (! service->handle) {
\r
708 print_message(stderr, NSSM_MESSAGE_CREATESERVICE_FAILED);
\r
709 CloseServiceHandle(services);
\r
713 if (edit_service(service, false)) {
\r
714 DeleteService(service->handle);
\r
715 CloseServiceHandle(services);
\r
719 print_message(stdout, NSSM_MESSAGE_SERVICE_INSTALLED, service->name);
\r
722 CloseServiceHandle(services);
\r
727 /* Edit the service. */
\r
728 int edit_service(nssm_service_t *service, bool editing) {
\r
729 if (! service) return 1;
\r
732 The only two valid flags for service type are SERVICE_WIN32_OWN_PROCESS
\r
733 and SERVICE_INTERACTIVE_PROCESS.
\r
735 service->type &= SERVICE_INTERACTIVE_PROCESS;
\r
736 service->type |= SERVICE_WIN32_OWN_PROCESS;
\r
738 /* Startup type. */
\r
739 unsigned long startup;
\r
740 switch (service->startup) {
\r
741 case NSSM_STARTUP_MANUAL: startup = SERVICE_DEMAND_START; break;
\r
742 case NSSM_STARTUP_DISABLED: startup = SERVICE_DISABLED; break;
\r
743 default: startup = SERVICE_AUTO_START;
\r
746 /* Display name. */
\r
747 if (! service->displayname[0]) _sntprintf_s(service->displayname, _countof(service->displayname), _TRUNCATE, _T("%s"), service->name);
\r
750 Username must be NULL if we aren't changing or an account name.
\r
751 We must explicitly use LOCALSYSTEM to change it when we are editing.
\r
752 Password must be NULL if we aren't changing, a password or "".
\r
753 Empty passwords are valid but we won't allow them in the GUI.
\r
755 TCHAR *username = 0;
\r
756 TCHAR *password = 0;
\r
757 if (service->usernamelen) {
\r
758 username = service->username;
\r
759 if (service->passwordlen) password = service->password;
\r
760 else password = _T("");
\r
762 else if (editing) username = NSSM_LOCALSYSTEM_ACCOUNT;
\r
764 if (grant_logon_as_service(username)) {
\r
765 print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username);
\r
769 if (! ChangeServiceConfig(service->handle, service->type, startup, SERVICE_NO_CHANGE, 0, 0, 0, 0, username, password, service->displayname)) {
\r
770 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
774 if (service->description[0] || editing) {
\r
775 set_service_description(service->name, service->handle, service->description);
\r
778 SERVICE_DELAYED_AUTO_START_INFO delayed;
\r
779 ZeroMemory(&delayed, sizeof(delayed));
\r
780 if (service->startup == NSSM_STARTUP_DELAYED) delayed.fDelayedAutostart = 1;
\r
781 else delayed.fDelayedAutostart = 0;
\r
782 /* Delayed startup isn't supported until Vista. */
\r
783 if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, &delayed)) {
\r
784 unsigned long error = GetLastError();
\r
785 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
\r
786 if (error != ERROR_INVALID_LEVEL) {
\r
787 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_DELAYED_AUTO_START_INFO_FAILED, service->name, error_string(error), 0);
\r
791 /* Don't mess with parameters which aren't ours. */
\r
792 if (! service->native) {
\r
793 /* Now we need to put the parameters into the registry */
\r
794 if (create_parameters(service, editing)) {
\r
795 print_message(stderr, NSSM_MESSAGE_CREATE_PARAMETERS_FAILED);
\r
799 set_service_recovery(service);
\r
805 /* Control a service. */
\r
806 int control_service(unsigned long control, int argc, TCHAR **argv) {
\r
807 if (argc < 1) return usage(1);
\r
808 TCHAR *service_name = argv[0];
\r
810 SC_HANDLE services = open_service_manager();
\r
812 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
816 SC_HANDLE service_handle = OpenService(services, service_name, SC_MANAGER_ALL_ACCESS);
\r
817 if (! service_handle) {
\r
818 print_message(stderr, NSSM_MESSAGE_OPENSERVICE_FAILED);
\r
819 CloseServiceHandle(services);
\r
824 unsigned long error;
\r
825 SERVICE_STATUS service_status;
\r
826 if (control == 0) {
\r
827 ret = StartService(service_handle, (unsigned long) argc, (const TCHAR **) argv);
\r
828 error = GetLastError();
\r
829 CloseHandle(service_handle);
\r
830 CloseServiceHandle(services);
\r
833 _tprintf(_T("%s: %s"), canonical_name, error_string(error));
\r
837 _ftprintf(stderr, _T("%s: %s"), canonical_name, error_string(error));
\r
841 else if (control == SERVICE_CONTROL_INTERROGATE) {
\r
843 We could actually send an INTERROGATE control but that won't return
\r
844 any information if the service is stopped and we don't care about
\r
845 the extra details it might give us in any case. So we'll fake it.
\r
847 ret = QueryServiceStatus(service_handle, &service_status);
\r
848 error = GetLastError();
\r
851 switch (service_status.dwCurrentState) {
\r
852 case SERVICE_STOPPED: _tprintf(_T("SERVICE_STOPPED\n")); break;
\r
853 case SERVICE_START_PENDING: _tprintf(_T("SERVICE_START_PENDING\n")); break;
\r
854 case SERVICE_STOP_PENDING: _tprintf(_T("SERVICE_STOP_PENDING\n")); break;
\r
855 case SERVICE_RUNNING: _tprintf(_T("SERVICE_RUNNING\n")); break;
\r
856 case SERVICE_CONTINUE_PENDING: _tprintf(_T("SERVICE_CONTINUE_PENDING\n")); break;
\r
857 case SERVICE_PAUSE_PENDING: _tprintf(_T("SERVICE_PAUSE_PENDING\n")); break;
\r
858 case SERVICE_PAUSED: _tprintf(_T("SERVICE_PAUSED\n")); break;
\r
859 default: _tprintf(_T("?\n")); return 1;
\r
864 _ftprintf(stderr, _T("%s: %s\n"), service_name, error_string(error));
\r
869 ret = ControlService(service_handle, control, &service_status);
\r
870 error = GetLastError();
\r
871 CloseHandle(service_handle);
\r
872 CloseServiceHandle(services);
\r
875 _tprintf(_T("%s: %s"), canonical_name, error_string(error));
\r
879 _ftprintf(stderr, _T("%s: %s"), canonical_name, error_string(error));
\r
885 /* Remove the service */
\r
886 int remove_service(nssm_service_t *service) {
\r
887 if (! service) return 1;
\r
889 /* Open service manager */
\r
890 SC_HANDLE services = open_service_manager();
\r
892 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
896 /* Try to open the service */
\r
897 service->handle = OpenService(services, service->name, SC_MANAGER_ALL_ACCESS);
\r
898 if (! service->handle) {
\r
899 print_message(stderr, NSSM_MESSAGE_OPENSERVICE_FAILED);
\r
900 CloseServiceHandle(services);
\r
904 /* Get the canonical service name. We open it case insensitively. */
\r
905 unsigned long bufsize = _countof(service->displayname);
\r
906 GetServiceDisplayName(services, service->name, service->displayname, &bufsize);
\r
907 bufsize = _countof(service->name);
\r
908 GetServiceKeyName(services, service->displayname, service->name, &bufsize);
\r
910 /* Try to delete the service */
\r
911 if (! DeleteService(service->handle)) {
\r
912 print_message(stderr, NSSM_MESSAGE_DELETESERVICE_FAILED);
\r
913 CloseServiceHandle(services);
\r
918 CloseServiceHandle(services);
\r
920 print_message(stdout, NSSM_MESSAGE_SERVICE_REMOVED, service->name);
\r
924 /* Service initialisation */
\r
925 void WINAPI service_main(unsigned long argc, TCHAR **argv) {
\r
926 nssm_service_t *service = alloc_nssm_service();
\r
927 if (! service) return;
\r
929 if (_sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]) < 0) {
\r
930 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("service->name"), _T("service_main()"), 0);
\r
934 /* We can use a condition variable in a critical section on Vista or later. */
\r
935 if (imports.SleepConditionVariableCS && imports.WakeConditionVariable) use_critical_section = true;
\r
936 else use_critical_section = false;
\r
938 /* Initialise status */
\r
939 ZeroMemory(&service->status, sizeof(service->status));
\r
940 service->status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;
\r
941 service->status.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE;
\r
942 service->status.dwWin32ExitCode = NO_ERROR;
\r
943 service->status.dwServiceSpecificExitCode = 0;
\r
944 service->status.dwCheckPoint = 0;
\r
945 service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;
\r
947 /* Signal we AREN'T running the server */
\r
948 service->process_handle = 0;
\r
951 /* Register control handler */
\r
952 service->status_handle = RegisterServiceCtrlHandlerEx(NSSM, service_control_handler, (void *) service);
\r
953 if (! service->status_handle) {
\r
954 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_REGISTERSERVICECTRLHANDER_FAILED, error_string(GetLastError()), 0);
\r
958 log_service_control(service->name, 0, true);
\r
960 service->status.dwCurrentState = SERVICE_START_PENDING;
\r
961 service->status.dwWaitHint = service->throttle_delay + NSSM_WAITHINT_MARGIN;
\r
962 SetServiceStatus(service->status_handle, &service->status);
\r
965 /* Try to create the exit action parameters; we don't care if it fails */
\r
966 create_exit_action(service->name, exit_action_strings[0], false);
\r
968 SC_HANDLE services = open_service_manager();
\r
970 service->handle = OpenService(services, service->name, SC_MANAGER_ALL_ACCESS);
\r
971 set_service_recovery(service);
\r
972 CloseServiceHandle(services);
\r
976 /* Used for signalling a resume if the service pauses when throttled. */
\r
977 if (use_critical_section) {
\r
978 InitializeCriticalSection(&service->throttle_section);
\r
979 service->throttle_section_initialised = true;
\r
982 service->throttle_timer = CreateWaitableTimer(0, 1, 0);
\r
983 if (! service->throttle_timer) {
\r
984 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_CREATEWAITABLETIMER_FAILED, service->name, error_string(GetLastError()), 0);
\r
988 monitor_service(service);
\r
991 /* Make sure service recovery actions are taken where necessary */
\r
992 void set_service_recovery(nssm_service_t *service) {
\r
993 SERVICE_FAILURE_ACTIONS_FLAG flag;
\r
994 ZeroMemory(&flag, sizeof(flag));
\r
995 flag.fFailureActionsOnNonCrashFailures = true;
\r
997 /* This functionality was added in Vista so the call may fail */
\r
998 if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, &flag)) {
\r
999 unsigned long error = GetLastError();
\r
1000 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
\r
1001 if (error != ERROR_INVALID_LEVEL) {
\r
1002 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_FAILURE_ACTIONS_FAILED, service->name, error_string(error), 0);
\r
1007 int monitor_service(nssm_service_t *service) {
\r
1008 /* Set service status to started */
\r
1009 int ret = start_service(service);
\r
1012 _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%d"), ret);
\r
1013 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_START_SERVICE_FAILED, service->exe, service->name, ret, 0);
\r
1016 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_STARTED_SERVICE, service->exe, service->flags, service->name, service->dir, 0);
\r
1018 /* Monitor service */
\r
1019 if (! RegisterWaitForSingleObject(&service->wait_handle, service->process_handle, end_service, (void *) service, INFINITE, WT_EXECUTEONLYONCE | WT_EXECUTELONGFUNCTION)) {
\r
1020 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_REGISTERWAITFORSINGLEOBJECT_FAILED, service->name, service->exe, error_string(GetLastError()), 0);
\r
1026 TCHAR *service_control_text(unsigned long control) {
\r
1027 switch (control) {
\r
1028 /* HACK: there is no SERVICE_CONTROL_START constant */
\r
1029 case 0: return _T("START");
\r
1030 case SERVICE_CONTROL_STOP: return _T("STOP");
\r
1031 case SERVICE_CONTROL_SHUTDOWN: return _T("SHUTDOWN");
\r
1032 case SERVICE_CONTROL_PAUSE: return _T("PAUSE");
\r
1033 case SERVICE_CONTROL_CONTINUE: return _T("CONTINUE");
\r
1034 case SERVICE_CONTROL_INTERROGATE: return _T("INTERROGATE");
\r
1035 default: return 0;
\r
1039 void log_service_control(TCHAR *service_name, unsigned long control, bool handled) {
\r
1040 TCHAR *text = service_control_text(control);
\r
1041 unsigned long event;
\r
1044 /* "0x" + 8 x hex + NULL */
\r
1045 text = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, 11 * sizeof(TCHAR));
\r
1047 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);
\r
1050 if (_sntprintf_s(text, 11, _TRUNCATE, _T("0x%08x"), control) < 0) {
\r
1051 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);
\r
1052 HeapFree(GetProcessHeap(), 0, text);
\r
1056 event = NSSM_EVENT_SERVICE_CONTROL_UNKNOWN;
\r
1058 else if (handled) event = NSSM_EVENT_SERVICE_CONTROL_HANDLED;
\r
1059 else event = NSSM_EVENT_SERVICE_CONTROL_NOT_HANDLED;
\r
1061 log_event(EVENTLOG_INFORMATION_TYPE, event, service_name, text, 0);
\r
1063 if (event == NSSM_EVENT_SERVICE_CONTROL_UNKNOWN) {
\r
1064 HeapFree(GetProcessHeap(), 0, text);
\r
1068 /* Service control handler */
\r
1069 unsigned long WINAPI service_control_handler(unsigned long control, unsigned long event, void *data, void *context) {
\r
1070 nssm_service_t *service = (nssm_service_t *) context;
\r
1072 switch (control) {
\r
1073 case SERVICE_CONTROL_INTERROGATE:
\r
1074 /* We always keep the service status up-to-date so this is a no-op. */
\r
1077 case SERVICE_CONTROL_SHUTDOWN:
\r
1078 case SERVICE_CONTROL_STOP:
\r
1079 log_service_control(service->name, control, true);
\r
1081 We MUST acknowledge the stop request promptly but we're committed to
\r
1082 waiting for the application to exit. Spawn a new thread to wait
\r
1083 while we acknowledge the request.
\r
1085 if (! CreateThread(NULL, 0, shutdown_service, context, 0, NULL)) {
\r
1086 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETHREAD_FAILED, error_string(GetLastError()), 0);
\r
1089 We couldn't create a thread to tidy up so we'll have to force the tidyup
\r
1090 to complete in time in this thread.
\r
1092 service->kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;
\r
1093 service->kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;
\r
1094 service->kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;
\r
1096 stop_service(service, 0, true, true);
\r
1100 case SERVICE_CONTROL_CONTINUE:
\r
1101 log_service_control(service->name, control, true);
\r
1102 service->throttle = 0;
\r
1103 if (use_critical_section) imports.WakeConditionVariable(&service->throttle_condition);
\r
1105 if (! service->throttle_timer) return ERROR_CALL_NOT_IMPLEMENTED;
\r
1106 ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));
\r
1107 SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);
\r
1109 service->status.dwCurrentState = SERVICE_CONTINUE_PENDING;
\r
1110 service->status.dwWaitHint = throttle_milliseconds(service->throttle) + NSSM_WAITHINT_MARGIN;
\r
1111 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESET_THROTTLE, service->name, 0);
\r
1112 SetServiceStatus(service->status_handle, &service->status);
\r
1115 case SERVICE_CONTROL_PAUSE:
\r
1117 We don't accept pause messages but it isn't possible to register
\r
1118 only for continue messages so we have to handle this case.
\r
1120 log_service_control(service->name, control, false);
\r
1121 return ERROR_CALL_NOT_IMPLEMENTED;
\r
1124 /* Unknown control */
\r
1125 log_service_control(service->name, control, false);
\r
1126 return ERROR_CALL_NOT_IMPLEMENTED;
\r
1129 /* Start the service */
\r
1130 int start_service(nssm_service_t *service) {
\r
1131 service->stopping = false;
\r
1132 service->allow_restart = true;
\r
1134 if (service->process_handle) return 0;
\r
1136 /* Allocate a STARTUPINFO structure for a new process */
\r
1138 ZeroMemory(&si, sizeof(si));
\r
1139 si.cb = sizeof(si);
\r
1141 /* Allocate a PROCESSINFO structure for the process */
\r
1142 PROCESS_INFORMATION pi;
\r
1143 ZeroMemory(&pi, sizeof(pi));
\r
1145 /* Get startup parameters */
\r
1146 int ret = get_parameters(service, &si);
\r
1148 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_PARAMETERS_FAILED, service->name, 0);
\r
1149 return stop_service(service, 2, true, true);
\r
1152 /* Launch executable with arguments */
\r
1153 TCHAR cmd[CMD_LENGTH];
\r
1154 if (_sntprintf_s(cmd, _countof(cmd), _TRUNCATE, _T("\"%s\" %s"), service->exe, service->flags) < 0) {
\r
1155 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("command line"), _T("start_service"), 0);
\r
1156 close_output_handles(&si);
\r
1157 return stop_service(service, 2, true, true);
\r
1160 throttle_restart(service);
\r
1162 bool inherit_handles = false;
\r
1163 if (si.dwFlags & STARTF_USESTDHANDLES) inherit_handles = true;
\r
1164 unsigned long flags = 0;
\r
1166 flags |= CREATE_UNICODE_ENVIRONMENT;
\r
1168 if (! CreateProcess(0, cmd, 0, 0, inherit_handles, flags, service->env, service->dir, &si, &pi)) {
\r
1169 unsigned long exitcode = 3;
\r
1170 unsigned long error = GetLastError();
\r
1171 if (error == ERROR_INVALID_PARAMETER && service->env) {
\r
1172 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED_INVALID_ENVIRONMENT, service->name, service->exe, NSSM_REG_ENV, 0);
\r
1173 if (test_environment(service->env)) exitcode = 4;
\r
1175 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service->name, service->exe, error_string(error), 0);
\r
1176 close_output_handles(&si);
\r
1177 return stop_service(service, exitcode, true, true);
\r
1179 service->process_handle = pi.hProcess;
\r
1180 service->pid = pi.dwProcessId;
\r
1182 if (get_process_creation_time(service->process_handle, &service->creation_time)) ZeroMemory(&service->creation_time, sizeof(service->creation_time));
\r
1184 close_output_handles(&si);
\r
1187 Wait for a clean startup before changing the service status to RUNNING
\r
1188 but be mindful of the fact that we are blocking the service control manager
\r
1189 so abandon the wait before too much time has elapsed.
\r
1191 unsigned long delay = service->throttle_delay;
\r
1192 if (delay > NSSM_SERVICE_STATUS_DEADLINE) {
\r
1193 TCHAR delay_milliseconds[16];
\r
1194 _sntprintf_s(delay_milliseconds, _countof(delay_milliseconds), _TRUNCATE, _T("%lu"), delay);
\r
1195 TCHAR deadline_milliseconds[16];
\r
1196 _sntprintf_s(deadline_milliseconds, _countof(deadline_milliseconds), _TRUNCATE, _T("%lu"), NSSM_SERVICE_STATUS_DEADLINE);
\r
1197 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_STARTUP_DELAY_TOO_LONG, service->name, delay_milliseconds, NSSM, deadline_milliseconds, 0);
\r
1198 delay = NSSM_SERVICE_STATUS_DEADLINE;
\r
1200 unsigned long deadline = WaitForSingleObject(service->process_handle, delay);
\r
1202 /* Signal successful start */
\r
1203 service->status.dwCurrentState = SERVICE_RUNNING;
\r
1204 SetServiceStatus(service->status_handle, &service->status);
\r
1206 /* Continue waiting for a clean startup. */
\r
1207 if (deadline == WAIT_TIMEOUT) {
\r
1208 if (service->throttle_delay > delay) {
\r
1209 if (WaitForSingleObject(service->process_handle, service->throttle_delay - delay) == WAIT_TIMEOUT) service->throttle = 0;
\r
1211 else service->throttle = 0;
\r
1217 /* Stop the service */
\r
1218 int stop_service(nssm_service_t *service, unsigned long exitcode, bool graceful, bool default_action) {
\r
1219 service->allow_restart = false;
\r
1220 if (service->wait_handle) {
\r
1221 UnregisterWait(service->wait_handle);
\r
1222 service->wait_handle = 0;
\r
1225 if (default_action && ! exitcode && ! graceful) {
\r
1226 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
1230 /* Signal we are stopping */
\r
1232 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
1233 service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;
\r
1234 SetServiceStatus(service->status_handle, &service->status);
\r
1237 /* Nothing to do if service isn't running */
\r
1238 if (service->pid) {
\r
1239 /* Shut down service */
\r
1240 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_TERMINATEPROCESS, service->name, service->exe, 0);
\r
1241 kill_process(service, service->process_handle, service->pid, 0);
\r
1243 else log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_PROCESS_ALREADY_STOPPED, service->name, service->exe, 0);
\r
1245 end_service((void *) service, true);
\r
1247 /* Signal we stopped */
\r
1249 service->status.dwCurrentState = SERVICE_STOPPED;
\r
1251 service->status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
\r
1252 service->status.dwServiceSpecificExitCode = exitcode;
\r
1255 service->status.dwWin32ExitCode = NO_ERROR;
\r
1256 service->status.dwServiceSpecificExitCode = 0;
\r
1258 SetServiceStatus(service->status_handle, &service->status);
\r
1264 /* Callback function triggered when the server exits */
\r
1265 void CALLBACK end_service(void *arg, unsigned char why) {
\r
1266 nssm_service_t *service = (nssm_service_t *) arg;
\r
1268 if (service->stopping) return;
\r
1270 service->stopping = true;
\r
1272 /* Check exit code */
\r
1273 unsigned long exitcode = 0;
\r
1275 if (service->process_handle) {
\r
1276 GetExitCodeProcess(service->process_handle, &exitcode);
\r
1277 if (exitcode == STILL_ACTIVE || get_process_exit_time(service->process_handle, &service->exit_time)) GetSystemTimeAsFileTime(&service->exit_time);
\r
1278 CloseHandle(service->process_handle);
\r
1280 else GetSystemTimeAsFileTime(&service->exit_time);
\r
1282 service->process_handle = 0;
\r
1285 Log that the service ended BEFORE logging about killing the process
\r
1286 tree. See below for the possible values of the why argument.
\r
1289 _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), exitcode);
\r
1290 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ENDED_SERVICE, service->exe, service->name, code, 0);
\r
1294 if (exitcode == STILL_ACTIVE) exitcode = 0;
\r
1295 if (service->pid) kill_process_tree(service, service->pid, exitcode, service->pid);
\r
1299 The why argument is true if our wait timed out or false otherwise.
\r
1300 Our wait is infinite so why will never be true when called by the system.
\r
1301 If it is indeed true, assume we were called from stop_service() because
\r
1302 this is a controlled shutdown, and don't take any restart action.
\r
1305 if (! service->allow_restart) return;
\r
1307 /* What action should we take? */
\r
1308 int action = NSSM_EXIT_RESTART;
\r
1309 TCHAR action_string[ACTION_LEN];
\r
1310 bool default_action;
\r
1311 if (! get_exit_action(service->name, &exitcode, action_string, &default_action)) {
\r
1312 for (int i = 0; exit_action_strings[i]; i++) {
\r
1313 if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
1321 /* Try to restart the service or return failure code to service manager */
\r
1322 case NSSM_EXIT_RESTART:
\r
1323 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_RESTART, service->name, code, exit_action_strings[action], service->exe, 0);
\r
1324 while (monitor_service(service)) {
\r
1325 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_RESTART_SERVICE_FAILED, service->exe, service->name, 0);
\r
1330 /* Do nothing, just like srvany would */
\r
1331 case NSSM_EXIT_IGNORE:
\r
1332 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_IGNORE, service->name, code, exit_action_strings[action], service->exe, 0);
\r
1336 /* Tell the service manager we are finished */
\r
1337 case NSSM_EXIT_REALLY:
\r
1338 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_REALLY, service->name, code, exit_action_strings[action], 0);
\r
1339 stop_service(service, exitcode, true, default_action);
\r
1342 /* Fake a crash so pre-Vista service managers will run recovery actions. */
\r
1343 case NSSM_EXIT_UNCLEAN:
\r
1344 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_UNCLEAN, service->name, code, exit_action_strings[action], 0);
\r
1345 stop_service(service, exitcode, false, default_action);
\r
1352 void throttle_restart(nssm_service_t *service) {
\r
1353 /* This can't be a restart if the service is already running. */
\r
1354 if (! service->throttle++) return;
\r
1356 int ms = throttle_milliseconds(service->throttle);
\r
1358 if (service->throttle > 7) service->throttle = 8;
\r
1360 TCHAR threshold[8], milliseconds[8];
\r
1361 _sntprintf_s(threshold, _countof(threshold), _TRUNCATE, _T("%lu"), service->throttle_delay);
\r
1362 _sntprintf_s(milliseconds, _countof(milliseconds), _TRUNCATE, _T("%lu"), ms);
\r
1363 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_THROTTLED, service->name, threshold, milliseconds, 0);
\r
1365 if (use_critical_section) EnterCriticalSection(&service->throttle_section);
\r
1366 else if (service->throttle_timer) {
\r
1367 ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));
\r
1368 service->throttle_duetime.QuadPart = 0 - (ms * 10000LL);
\r
1369 SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);
\r
1372 service->status.dwCurrentState = SERVICE_PAUSED;
\r
1373 SetServiceStatus(service->status_handle, &service->status);
\r
1375 if (use_critical_section) {
\r
1376 imports.SleepConditionVariableCS(&service->throttle_condition, &service->throttle_section, ms);
\r
1377 LeaveCriticalSection(&service->throttle_section);
\r
1380 if (service->throttle_timer) WaitForSingleObject(service->throttle_timer, INFINITE);
\r
1386 When responding to a stop (or any other) request we need to set dwWaitHint to
\r
1387 the number of milliseconds we expect the operation to take, and optionally
\r
1388 increase dwCheckPoint. If dwWaitHint milliseconds elapses without the
\r
1389 operation completing or dwCheckPoint increasing, the system will consider the
\r
1390 service to be hung.
\r
1392 However the system will consider the service to be hung after 30000
\r
1393 milliseconds regardless of the value of dwWaitHint if dwCheckPoint has not
\r
1394 changed. Therefore if we want to wait longer than that we must periodically
\r
1395 increase dwCheckPoint.
\r
1397 Furthermore, it will consider the service to be hung after 60000 milliseconds
\r
1398 regardless of the value of dwCheckPoint unless dwWaitHint is increased every
\r
1399 time dwCheckPoint is also increased.
\r
1401 Our strategy then is to retrieve the initial dwWaitHint and wait for
\r
1402 NSSM_SERVICE_STATUS_DEADLINE milliseconds. If the process is still running
\r
1403 and we haven't finished waiting we increment dwCheckPoint and add whichever is
\r
1404 smaller of NSSM_SERVICE_STATUS_DEADLINE or the remaining timeout to
\r
1407 Only doing both these things will prevent the system from killing the service.
\r
1409 Returns: 1 if the wait timed out.
\r
1410 0 if the wait completed.
\r
1413 int await_shutdown(nssm_service_t *service, TCHAR *function_name, unsigned long timeout) {
\r
1414 unsigned long interval;
\r
1415 unsigned long waithint;
\r
1416 unsigned long ret;
\r
1417 unsigned long waited;
\r
1418 TCHAR interval_milliseconds[16];
\r
1419 TCHAR timeout_milliseconds[16];
\r
1420 TCHAR waited_milliseconds[16];
\r
1421 TCHAR *function = function_name;
\r
1423 /* Add brackets to function name. */
\r
1424 size_t funclen = _tcslen(function_name) + 3;
\r
1425 TCHAR *func = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, funclen * sizeof(TCHAR));
\r
1427 if (_sntprintf_s(func, funclen, _TRUNCATE, _T("%s()"), function_name) > -1) function = func;
\r
1430 _sntprintf_s(timeout_milliseconds, _countof(timeout_milliseconds), _TRUNCATE, _T("%lu"), timeout);
\r
1432 waithint = service->status.dwWaitHint;
\r
1434 while (waited < timeout) {
\r
1435 interval = timeout - waited;
\r
1436 if (interval > NSSM_SERVICE_STATUS_DEADLINE) interval = NSSM_SERVICE_STATUS_DEADLINE;
\r
1438 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
1439 service->status.dwWaitHint += interval;
\r
1440 service->status.dwCheckPoint++;
\r
1441 SetServiceStatus(service->status_handle, &service->status);
\r
1444 _sntprintf_s(waited_milliseconds, _countof(waited_milliseconds), _TRUNCATE, _T("%lu"), waited);
\r
1445 _sntprintf_s(interval_milliseconds, _countof(interval_milliseconds), _TRUNCATE, _T("%lu"), interval);
\r
1446 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_AWAITING_SHUTDOWN, function, service->name, waited_milliseconds, interval_milliseconds, timeout_milliseconds, 0);
\r
1449 switch (WaitForSingleObject(service->process_handle, interval)) {
\r
1450 case WAIT_OBJECT_0:
\r
1454 case WAIT_TIMEOUT:
\r
1463 waited += interval;
\r
1467 if (func) HeapFree(GetProcessHeap(), 0, func);
\r