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 /* Open a service by name or display name. */
\r
41 SC_HANDLE open_service(SC_HANDLE services, TCHAR *service_name, TCHAR *canonical_name, unsigned long canonical_namelen) {
\r
42 SC_HANDLE service_handle = OpenService(services, service_name, SERVICE_ALL_ACCESS);
\r
43 if (service_handle) {
\r
44 if (canonical_name && canonical_name != service_name) {
\r
45 if (_sntprintf_s(canonical_name, canonical_namelen, _TRUNCATE, _T("%s"), service_name) < 0) {
\r
46 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("canonical_name"), _T("open_service()"));
\r
50 return service_handle;
\r
53 unsigned long error = GetLastError();
\r
54 if (error != ERROR_SERVICE_DOES_NOT_EXIST) {
\r
55 print_message(stderr, NSSM_MESSAGE_OPENSERVICE_FAILED, error_string(GetLastError()));
\r
59 /* We can't look for a display name because there's no buffer to store it. */
\r
60 if (! canonical_name) {
\r
61 print_message(stderr, NSSM_MESSAGE_OPENSERVICE_FAILED, error_string(GetLastError()));
\r
65 unsigned long bufsize, required, count, i;
\r
66 unsigned long resume = 0;
\r
67 EnumServicesStatus(services, SERVICE_DRIVER | SERVICE_FILE_SYSTEM_DRIVER | SERVICE_KERNEL_DRIVER | SERVICE_WIN32, SERVICE_STATE_ALL, 0, 0, &required, &count, &resume);
\r
68 error = GetLastError();
\r
69 if (error != ERROR_MORE_DATA) {
\r
70 print_message(stderr, NSSM_MESSAGE_ENUMSERVICESSTATUS_FAILED, error_string(GetLastError()));
\r
74 ENUM_SERVICE_STATUS *status = (ENUM_SERVICE_STATUS *) HeapAlloc(GetProcessHeap(), 0, required);
\r
76 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("ENUM_SERVICE_STATUS"), _T("open_service()"));
\r
83 EnumServicesStatus() returns:
\r
84 1 when it retrieved data and there's no more data to come.
\r
85 0 and sets last error to ERROR_MORE_DATA when it retrieved data and
\r
86 there's more data to come.
\r
87 0 and sets last error to something else on error.
\r
89 int ret = EnumServicesStatus(services, SERVICE_DRIVER | SERVICE_FILE_SYSTEM_DRIVER | SERVICE_KERNEL_DRIVER | SERVICE_WIN32, SERVICE_STATE_ALL, status, bufsize, &required, &count, &resume);
\r
91 error = GetLastError();
\r
92 if (error != ERROR_MORE_DATA) {
\r
93 HeapFree(GetProcessHeap(), 0, status);
\r
94 print_message(stderr, NSSM_MESSAGE_ENUMSERVICESSTATUS_FAILED, error_string(GetLastError()));
\r
99 for (i = 0; i < count; i++) {
\r
100 if (str_equiv(status[i].lpDisplayName, service_name)) {
\r
101 if (_sntprintf_s(canonical_name, canonical_namelen, _TRUNCATE, _T("%s"), status[i].lpServiceName) < 0) {
\r
102 HeapFree(GetProcessHeap(), 0, status);
\r
103 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("canonical_name"), _T("open_service()"));
\r
107 HeapFree(GetProcessHeap(), 0, status);
\r
108 return open_service(services, canonical_name, 0, 0);
\r
115 /* Recurse so we can get an error message. */
\r
116 return open_service(services, service_name, 0, 0);
\r
119 QUERY_SERVICE_CONFIG *query_service_config(const TCHAR *service_name, SC_HANDLE service_handle) {
\r
120 QUERY_SERVICE_CONFIG *qsc;
\r
121 unsigned long bufsize;
\r
122 unsigned long error;
\r
124 QueryServiceConfig(service_handle, 0, 0, &bufsize);
\r
125 error = GetLastError();
\r
126 if (error == ERROR_INSUFFICIENT_BUFFER) {
\r
127 qsc = (QUERY_SERVICE_CONFIG *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, bufsize);
\r
129 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("QUERY_SERVICE_CONFIG"), _T("query_service_config()"), 0);
\r
134 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG_FAILED, service_name, error_string(error), 0);
\r
138 if (! QueryServiceConfig(service_handle, qsc, bufsize, &bufsize)) {
\r
139 HeapFree(GetProcessHeap(), 0, qsc);
\r
140 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG_FAILED, service_name, error_string(GetLastError()), 0);
\r
147 int set_service_description(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR *buffer) {
\r
148 SERVICE_DESCRIPTION description;
\r
149 ZeroMemory(&description, sizeof(description));
\r
151 lpDescription must be NULL if we aren't changing, the new description
\r
154 if (buffer && buffer[0]) description.lpDescription = buffer;
\r
155 else description.lpDescription = _T("");
\r
157 if (ChangeServiceConfig2(service_handle, SERVICE_CONFIG_DESCRIPTION, &description)) return 0;
\r
159 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_DESCRIPTION_FAILED, service_name, error_string(GetLastError()), 0);
\r
163 int get_service_description(const TCHAR *service_name, SC_HANDLE service_handle, unsigned long len, TCHAR *buffer) {
\r
164 if (! buffer) return 1;
\r
166 unsigned long bufsize;
\r
167 QueryServiceConfig2(service_handle, SERVICE_CONFIG_DESCRIPTION, 0, 0, &bufsize);
\r
168 unsigned long error = GetLastError();
\r
169 if (error == ERROR_INSUFFICIENT_BUFFER) {
\r
170 SERVICE_DESCRIPTION *description = (SERVICE_DESCRIPTION *) HeapAlloc(GetProcessHeap(), 0, bufsize);
\r
171 if (! description) {
\r
172 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("SERVICE_CONFIG_DESCRIPTION"), _T("get_service_description()"));
\r
176 if (QueryServiceConfig2(service_handle, SERVICE_CONFIG_DESCRIPTION, (unsigned char *) description, bufsize, &bufsize)) {
\r
177 if (description->lpDescription) _sntprintf_s(buffer, len, _TRUNCATE, _T("%s"), description->lpDescription);
\r
178 else ZeroMemory(buffer, len * sizeof(TCHAR));
\r
179 HeapFree(GetProcessHeap(), 0, description);
\r
183 HeapFree(GetProcessHeap(), 0, description);
\r
184 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_CONFIG_DESCRIPTION"), error_string(error));
\r
189 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_CONFIG_DESCRIPTION"), error_string(error));
\r
196 int get_service_startup(const TCHAR *service_name, SC_HANDLE service_handle, const QUERY_SERVICE_CONFIG *qsc, unsigned long *startup) {
\r
197 if (! qsc) return 1;
\r
199 switch (qsc->dwStartType) {
\r
200 case SERVICE_DEMAND_START: *startup = NSSM_STARTUP_MANUAL; break;
\r
201 case SERVICE_DISABLED: *startup = NSSM_STARTUP_DISABLED; break;
\r
202 default: *startup = NSSM_STARTUP_AUTOMATIC;
\r
205 if (*startup != NSSM_STARTUP_AUTOMATIC) return 0;
\r
207 /* Check for delayed start. */
\r
208 unsigned long bufsize;
\r
209 unsigned long error;
\r
210 QueryServiceConfig2(service_handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, 0, 0, &bufsize);
\r
211 error = GetLastError();
\r
212 if (error == ERROR_INSUFFICIENT_BUFFER) {
\r
213 SERVICE_DELAYED_AUTO_START_INFO *info = (SERVICE_DELAYED_AUTO_START_INFO *) HeapAlloc(GetProcessHeap(), 0, bufsize);
\r
215 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("SERVICE_DELAYED_AUTO_START_INFO"), _T("get_service_startup()"));
\r
219 if (QueryServiceConfig2(service_handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, (unsigned char *) info, bufsize, &bufsize)) {
\r
220 if (info->fDelayedAutostart) *startup = NSSM_STARTUP_DELAYED;
\r
221 HeapFree(GetProcessHeap(), 0, info);
\r
225 error = GetLastError();
\r
226 if (error != ERROR_INVALID_LEVEL) {
\r
227 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_CONFIG_DELAYED_AUTO_START_INFO"), error_string(error));
\r
232 else if (error != ERROR_INVALID_LEVEL) {
\r
233 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_DELAYED_AUTO_START_INFO"), error_string(error));
\r
240 int get_service_username(const TCHAR *service_name, const QUERY_SERVICE_CONFIG *qsc, TCHAR **username, size_t *usernamelen) {
\r
241 if (! username) return 1;
\r
242 if (! usernamelen) return 1;
\r
247 if (! qsc) return 1;
\r
249 if (str_equiv(qsc->lpServiceStartName, NSSM_LOCALSYSTEM_ACCOUNT)) return 0;
\r
251 size_t len = _tcslen(qsc->lpServiceStartName);
\r
252 *username = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(TCHAR));
\r
254 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("username"), _T("get_service_username()"));
\r
258 memmove(*username, qsc->lpServiceStartName, (len + 1) * sizeof(TCHAR));
\r
259 *usernamelen = len;
\r
264 int grant_logon_as_service(const TCHAR *username) {
\r
265 if (! username) return 0;
\r
266 if (str_equiv(username, NSSM_LOCALSYSTEM_ACCOUNT)) return 0;
\r
268 /* Open Policy object. */
\r
269 LSA_OBJECT_ATTRIBUTES attributes;
\r
270 ZeroMemory(&attributes, sizeof(attributes));
\r
274 NTSTATUS status = LsaOpenPolicy(0, &attributes, POLICY_ALL_ACCESS, &policy);
\r
276 print_message(stderr, NSSM_MESSAGE_LSAOPENPOLICY_FAILED, error_string(LsaNtStatusToWinError(status)));
\r
280 /* Look up SID for the account. */
\r
281 LSA_UNICODE_STRING lsa_username;
\r
283 lsa_username.Buffer = (wchar_t *) username;
\r
284 lsa_username.Length = (unsigned short) _tcslen(username) * sizeof(TCHAR);
\r
285 lsa_username.MaximumLength = lsa_username.Length + sizeof(TCHAR);
\r
288 mbstowcs_s(&buflen, NULL, 0, username, _TRUNCATE);
\r
289 lsa_username.MaximumLength = buflen * sizeof(wchar_t);
\r
290 lsa_username.Length = lsa_username.MaximumLength - sizeof(wchar_t);
\r
291 lsa_username.Buffer = (wchar_t *) HeapAlloc(GetProcessHeap(), 0, lsa_username.MaximumLength);
\r
292 if (lsa_username.Buffer) mbstowcs_s(&buflen, lsa_username.Buffer, lsa_username.MaximumLength, username, _TRUNCATE);
\r
295 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("LSA_UNICODE_STRING"), _T("grant_logon_as_service()"));
\r
300 LSA_REFERENCED_DOMAIN_LIST *translated_domains;
\r
301 LSA_TRANSLATED_SID *translated_sid;
\r
302 status = LsaLookupNames(policy, 1, &lsa_username, &translated_domains, &translated_sid);
\r
304 HeapFree(GetProcessHeap(), 0, lsa_username.Buffer);
\r
307 LsaFreeMemory(translated_domains);
\r
308 LsaFreeMemory(translated_sid);
\r
310 print_message(stderr, NSSM_MESSAGE_LSALOOKUPNAMES_FAILED, username, error_string(LsaNtStatusToWinError(status)));
\r
314 if (translated_sid->Use != SidTypeUser) {
\r
315 LsaFreeMemory(translated_domains);
\r
316 LsaFreeMemory(translated_sid);
\r
318 print_message(stderr, NSSM_GUI_INVALID_USERNAME, username);
\r
322 LSA_TRUST_INFORMATION *trust = &translated_domains->Domains[translated_sid->DomainIndex];
\r
323 if (! trust || ! IsValidSid(trust->Sid)) {
\r
324 LsaFreeMemory(translated_domains);
\r
325 LsaFreeMemory(translated_sid);
\r
327 print_message(stderr, NSSM_GUI_INVALID_USERNAME, username);
\r
331 /* GetSidSubAuthority*() return pointers! */
\r
332 unsigned char *n = GetSidSubAuthorityCount(trust->Sid);
\r
334 /* Convert translated SID to SID. */
\r
335 SID *sid = (SID *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, GetSidLengthRequired(*n + 1));
\r
337 LsaFreeMemory(translated_domains);
\r
338 LsaFreeMemory(translated_sid);
\r
340 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("SID"), _T("grant_logon_as_service"));
\r
344 unsigned long error;
\r
345 if (! InitializeSid(sid, GetSidIdentifierAuthority(trust->Sid), *n + 1)) {
\r
346 error = GetLastError();
\r
347 HeapFree(GetProcessHeap(), 0, sid);
\r
348 LsaFreeMemory(translated_domains);
\r
349 LsaFreeMemory(translated_sid);
\r
351 print_message(stderr, NSSM_MESSAGE_INITIALIZESID_FAILED, username, error_string(error));
\r
355 for (unsigned char i = 0; i <= *n; i++) {
\r
356 unsigned long *sub = GetSidSubAuthority(sid, i);
\r
357 if (i < *n) *sub = *GetSidSubAuthority(trust->Sid, i);
\r
358 else *sub = translated_sid->RelativeId;
\r
361 LsaFreeMemory(translated_domains);
\r
362 LsaFreeMemory(translated_sid);
\r
364 /* Check if the SID has the "Log on as a service" right. */
\r
365 LSA_UNICODE_STRING lsa_right;
\r
366 lsa_right.Buffer = NSSM_LOGON_AS_SERVICE_RIGHT;
\r
367 lsa_right.Length = (unsigned short) wcslen(lsa_right.Buffer) * sizeof(wchar_t);
\r
368 lsa_right.MaximumLength = lsa_right.Length + sizeof(wchar_t);
\r
370 LSA_UNICODE_STRING *rights;
\r
371 unsigned long count = ~0;
\r
372 status = LsaEnumerateAccountRights(policy, sid, &rights, &count);
\r
375 If the account has no rights set LsaEnumerateAccountRights() will return
\r
376 STATUS_OBJECT_NAME_NOT_FOUND and set count to 0.
\r
378 error = LsaNtStatusToWinError(status);
\r
379 if (error != ERROR_FILE_NOT_FOUND) {
\r
380 HeapFree(GetProcessHeap(), 0, sid);
\r
382 print_message(stderr, NSSM_MESSAGE_LSAENUMERATEACCOUNTRIGHTS_FAILED, username, error_string(error));
\r
387 for (unsigned long i = 0; i < count; i++) {
\r
388 if (rights[i].Length != lsa_right.Length) continue;
\r
389 if (_wcsnicmp(rights[i].Buffer, lsa_right.Buffer, lsa_right.MaximumLength)) continue;
\r
390 /* The SID has the right. */
\r
391 HeapFree(GetProcessHeap(), 0, sid);
\r
392 LsaFreeMemory(rights);
\r
396 LsaFreeMemory(rights);
\r
398 /* Add the right. */
\r
399 status = LsaAddAccountRights(policy, sid, &lsa_right, 1);
\r
400 HeapFree(GetProcessHeap(), 0, sid);
\r
403 print_message(stderr, NSSM_MESSAGE_LSAADDACCOUNTRIGHTS_FAILED, error_string(LsaNtStatusToWinError(status)));
\r
407 print_message(stdout, NSSM_MESSAGE_GRANTED_LOGON_AS_SERVICE, username);
\r
411 /* Set default values which aren't zero. */
\r
412 void set_nssm_service_defaults(nssm_service_t *service) {
\r
413 if (! service) return;
\r
415 service->type = SERVICE_WIN32_OWN_PROCESS;
\r
416 service->stdin_sharing = NSSM_STDIN_SHARING;
\r
417 service->stdin_disposition = NSSM_STDIN_DISPOSITION;
\r
418 service->stdin_flags = NSSM_STDIN_FLAGS;
\r
419 service->stdout_sharing = NSSM_STDOUT_SHARING;
\r
420 service->stdout_disposition = NSSM_STDOUT_DISPOSITION;
\r
421 service->stdout_flags = NSSM_STDOUT_FLAGS;
\r
422 service->stderr_sharing = NSSM_STDERR_SHARING;
\r
423 service->stderr_disposition = NSSM_STDERR_DISPOSITION;
\r
424 service->stderr_flags = NSSM_STDERR_FLAGS;
\r
425 service->throttle_delay = NSSM_RESET_THROTTLE_RESTART;
\r
426 service->stop_method = ~0;
\r
427 service->kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;
\r
428 service->kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;
\r
429 service->kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;
\r
432 /* Allocate and zero memory for a service. */
\r
433 nssm_service_t *alloc_nssm_service() {
\r
434 nssm_service_t *service = (nssm_service_t *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(nssm_service_t));
\r
435 if (! service) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("service"), _T("alloc_nssm_service()"), 0);
\r
439 /* Free memory for a service. */
\r
440 void cleanup_nssm_service(nssm_service_t *service) {
\r
441 if (! service) return;
\r
442 if (service->username) HeapFree(GetProcessHeap(), 0, service->username);
\r
443 if (service->password) {
\r
444 SecureZeroMemory(service->password, service->passwordlen);
\r
445 HeapFree(GetProcessHeap(), 0, service->password);
\r
447 if (service->env) HeapFree(GetProcessHeap(), 0, service->env);
\r
448 if (service->env_extra) HeapFree(GetProcessHeap(), 0, service->env_extra);
\r
449 if (service->handle) CloseServiceHandle(service->handle);
\r
450 if (service->process_handle) CloseHandle(service->process_handle);
\r
451 if (service->wait_handle) UnregisterWait(service->process_handle);
\r
452 if (service->throttle_section_initialised) DeleteCriticalSection(&service->throttle_section);
\r
453 if (service->throttle_timer) CloseHandle(service->throttle_timer);
\r
454 HeapFree(GetProcessHeap(), 0, service);
\r
457 /* About to install the service */
\r
458 int pre_install_service(int argc, TCHAR **argv) {
\r
459 nssm_service_t *service = alloc_nssm_service();
\r
460 set_nssm_service_defaults(service);
\r
461 if (argc) _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]);
\r
463 /* Show the dialogue box if we didn't give the service name and path */
\r
464 if (argc < 2) return nssm_gui(IDD_INSTALL, service);
\r
467 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("service"), _T("pre_install_service()"));
\r
470 _sntprintf_s(service->exe, _countof(service->exe), _TRUNCATE, _T("%s"), argv[1]);
\r
472 /* Arguments are optional */
\r
473 size_t flagslen = 0;
\r
476 for (i = 2; i < argc; i++) flagslen += _tcslen(argv[i]) + 1;
\r
477 if (! flagslen) flagslen = 1;
\r
478 if (flagslen > _countof(service->flags)) {
\r
479 print_message(stderr, NSSM_MESSAGE_FLAGS_TOO_LONG);
\r
483 for (i = 2; i < argc; i++) {
\r
484 size_t len = _tcslen(argv[i]);
\r
485 memmove(service->flags + s, argv[i], len * sizeof(TCHAR));
\r
487 if (i < argc - 1) service->flags[s++] = _T(' ');
\r
490 /* Work out directory name */
\r
491 _sntprintf_s(service->dir, _countof(service->dir), _TRUNCATE, _T("%s"), service->exe);
\r
492 strip_basename(service->dir);
\r
494 int ret = install_service(service);
\r
495 cleanup_nssm_service(service);
\r
499 /* About to edit the service. */
\r
500 int pre_edit_service(int argc, TCHAR **argv) {
\r
501 /* Require service name. */
\r
502 if (argc < 2) return usage(1);
\r
504 /* Are we editing on the command line? */
\r
505 enum { MODE_EDITING, MODE_GETTING, MODE_SETTING, MODE_RESETTING } mode = MODE_EDITING;
\r
506 const TCHAR *verb = argv[0];
\r
507 const TCHAR *service_name = argv[1];
\r
508 bool getting = false;
\r
509 bool unsetting = false;
\r
511 /* Minimum number of arguments. */
\r
513 /* Index of first value. */
\r
516 if (str_equiv(verb, _T("get"))) {
\r
518 mode = MODE_GETTING;
\r
520 else if (str_equiv(verb, _T("set"))) {
\r
522 mode = MODE_SETTING;
\r
524 else if (str_equiv(verb, _T("reset")) || str_equiv(verb, _T("unset"))) {
\r
526 mode = MODE_RESETTING;
\r
528 if (argc < mandatory) return usage(1);
\r
530 const TCHAR *parameter = 0;
\r
531 settings_t *setting = 0;
\r
534 /* Validate the parameter. */
\r
535 if (mandatory > 2) {
\r
536 bool additional_mandatory = false;
\r
538 parameter = argv[2];
\r
539 for (i = 0; settings[i].name; i++) {
\r
540 setting = &settings[i];
\r
541 if (! str_equiv(setting->name, parameter)) continue;
\r
542 if (((setting->additional & ADDITIONAL_GETTING) && mode == MODE_GETTING) || ((setting->additional & ADDITIONAL_SETTING) && mode == MODE_SETTING) || ((setting->additional & ADDITIONAL_RESETTING) && mode == MODE_RESETTING)) {
\r
543 additional_mandatory = true;
\r
548 if (! settings[i].name) {
\r
549 print_message(stderr, NSSM_MESSAGE_INVALID_PARAMETER, parameter);
\r
550 for (i = 0; settings[i].name; i++) _ftprintf(stderr, _T("%s\n"), settings[i].name);
\r
553 if (argc < mandatory) return usage(1);
\r
556 if (additional_mandatory) {
\r
557 additional = argv[3];
\r
560 else additional = argv[remainder];
\r
563 nssm_service_t *service = alloc_nssm_service();
\r
564 _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), service_name);
\r
566 /* Open service manager */
\r
567 SC_HANDLE services = open_service_manager();
\r
569 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
573 /* Try to open the service */
\r
574 service->handle = open_service(services, service->name, service->name, _countof(service->name));
\r
575 if (! service->handle) {
\r
576 CloseServiceHandle(services);
\r
580 /* Get system details. */
\r
581 QUERY_SERVICE_CONFIG *qsc = query_service_config(service->name, service->handle);
\r
583 CloseHandle(service->handle);
\r
584 CloseServiceHandle(services);
\r
588 service->type = qsc->dwServiceType;
\r
589 if (! (service->type & SERVICE_WIN32_OWN_PROCESS)) {
\r
590 if (mode != MODE_GETTING) {
\r
591 HeapFree(GetProcessHeap(), 0, qsc);
\r
592 CloseHandle(service->handle);
\r
593 CloseServiceHandle(services);
\r
594 print_message(stderr, NSSM_MESSAGE_CANNOT_EDIT, service->name, NSSM_WIN32_OWN_PROCESS, 0);
\r
599 if (get_service_startup(service->name, service->handle, qsc, &service->startup)) {
\r
600 if (mode != MODE_GETTING) {
\r
601 HeapFree(GetProcessHeap(), 0, qsc);
\r
602 CloseHandle(service->handle);
\r
603 CloseServiceHandle(services);
\r
608 if (get_service_username(service->name, qsc, &service->username, &service->usernamelen)) {
\r
609 if (mode != MODE_GETTING) {
\r
610 HeapFree(GetProcessHeap(), 0, qsc);
\r
611 CloseHandle(service->handle);
\r
612 CloseServiceHandle(services);
\r
617 _sntprintf_s(service->displayname, _countof(service->displayname), _TRUNCATE, _T("%s"), qsc->lpDisplayName);
\r
619 /* Get the canonical service name. We open it case insensitively. */
\r
620 unsigned long bufsize = _countof(service->name);
\r
621 GetServiceKeyName(services, service->displayname, service->name, &bufsize);
\r
623 /* Remember the executable in case it isn't NSSM. */
\r
624 _sntprintf_s(service->image, _countof(service->image), _TRUNCATE, _T("%s"), qsc->lpBinaryPathName);
\r
625 HeapFree(GetProcessHeap(), 0, qsc);
\r
627 /* Get extended system details. */
\r
628 if (get_service_description(service->name, service->handle, _countof(service->description), service->description)) {
\r
629 if (mode != MODE_GETTING) {
\r
630 CloseHandle(service->handle);
\r
631 CloseServiceHandle(services);
\r
636 /* Get NSSM details. */
\r
637 get_parameters(service, 0);
\r
639 CloseServiceHandle(services);
\r
641 if (! service->exe[0]) {
\r
642 service->native = true;
\r
643 if (mode != MODE_GETTING) print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE, service->name, NSSM, service->image);
\r
646 /* Editing with the GUI. */
\r
647 if (mode == MODE_EDITING) {
\r
648 nssm_gui(IDD_EDIT, service);
\r
652 /* Trying to manage App* parameters for a non-NSSM service. */
\r
653 if (! setting->native && service->native) {
\r
654 CloseHandle(service->handle);
\r
655 print_message(stderr, NSSM_MESSAGE_NATIVE_PARAMETER, setting->name, NSSM);
\r
663 if (mode == MODE_GETTING) {
\r
664 if (! service->native) {
\r
665 key = open_registry(service->name, KEY_READ);
\r
666 if (! key) return 4;
\r
669 if (setting->native) ret = get_setting(service->name, service->handle, setting, &value, additional);
\r
670 else ret = get_setting(service->name, key, setting, &value, additional);
\r
672 CloseHandle(service->handle);
\r
676 switch (setting->type) {
\r
677 case REG_EXPAND_SZ:
\r
680 _tprintf(_T("%s\n"), value.string ? value.string : _T(""));
\r
681 HeapFree(GetProcessHeap(), 0, value.string);
\r
685 _tprintf(_T("%u\n"), value.numeric);
\r
689 if (! service->native) RegCloseKey(key);
\r
690 CloseHandle(service->handle);
\r
694 /* Build the value. */
\r
695 if (mode == MODE_RESETTING) {
\r
696 /* Unset the parameter. */
\r
700 /* Set the parameter. */
\r
702 size_t delimiterlen = (setting->additional & ADDITIONAL_CRLF) ? 2 : 1;
\r
703 for (i = remainder; i < argc; i++) len += _tcslen(argv[i]) + delimiterlen;
\r
706 value.string = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, len * sizeof(TCHAR));
\r
707 if (! value.string) {
\r
708 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("value"), _T("edit_service()"));
\r
709 CloseHandle(service->handle);
\r
714 for (i = remainder; i < argc; i++) {
\r
715 size_t len = _tcslen(argv[i]);
\r
716 memmove(value.string + s, argv[i], len * sizeof(TCHAR));
\r
718 if (i < argc - 1) {
\r
719 if (setting->additional & ADDITIONAL_CRLF) {
\r
720 value.string[s++] = _T('\r');
\r
721 value.string[s++] = _T('\n');
\r
723 else value.string[s++] = _T(' ');
\r
726 value.string[s] = _T('\0');
\r
729 if (! service->native) {
\r
730 key = open_registry(service->name, KEY_WRITE);
\r
732 if (value.string) HeapFree(GetProcessHeap(), 0, value.string);
\r
737 if (setting->native) ret = set_setting(service->name, service->handle, setting, &value, additional);
\r
738 else ret = set_setting(service->name, key, setting, &value, additional);
\r
739 if (value.string) HeapFree(GetProcessHeap(), 0, value.string);
\r
741 if (! service->native) RegCloseKey(key);
\r
742 CloseHandle(service->handle);
\r
746 if (! service->native) RegCloseKey(key);
\r
747 CloseHandle(service->handle);
\r
752 /* About to remove the service */
\r
753 int pre_remove_service(int argc, TCHAR **argv) {
\r
754 nssm_service_t *service = alloc_nssm_service();
\r
755 set_nssm_service_defaults(service);
\r
756 if (argc) _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]);
\r
758 /* Show dialogue box if we didn't pass service name and "confirm" */
\r
759 if (argc < 2) return nssm_gui(IDD_REMOVE, service);
\r
760 if (str_equiv(argv[1], _T("confirm"))) {
\r
761 int ret = remove_service(service);
\r
762 cleanup_nssm_service(service);
\r
765 print_message(stderr, NSSM_MESSAGE_PRE_REMOVE_SERVICE);
\r
769 /* Install the service */
\r
770 int install_service(nssm_service_t *service) {
\r
771 if (! service) return 1;
\r
773 /* Open service manager */
\r
774 SC_HANDLE services = open_service_manager();
\r
776 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
777 cleanup_nssm_service(service);
\r
781 /* Get path of this program */
\r
782 GetModuleFileName(0, service->image, _countof(service->image));
\r
784 /* Create the service - settings will be changed in edit_service() */
\r
785 service->handle = CreateService(services, service->name, service->name, SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, service->image, 0, 0, 0, 0, 0);
\r
786 if (! service->handle) {
\r
787 print_message(stderr, NSSM_MESSAGE_CREATESERVICE_FAILED, error_string(GetLastError()));
\r
788 CloseServiceHandle(services);
\r
792 if (edit_service(service, false)) {
\r
793 DeleteService(service->handle);
\r
794 CloseServiceHandle(services);
\r
798 print_message(stdout, NSSM_MESSAGE_SERVICE_INSTALLED, service->name);
\r
801 CloseServiceHandle(services);
\r
806 /* Edit the service. */
\r
807 int edit_service(nssm_service_t *service, bool editing) {
\r
808 if (! service) return 1;
\r
811 The only two valid flags for service type are SERVICE_WIN32_OWN_PROCESS
\r
812 and SERVICE_INTERACTIVE_PROCESS.
\r
814 service->type &= SERVICE_INTERACTIVE_PROCESS;
\r
815 service->type |= SERVICE_WIN32_OWN_PROCESS;
\r
817 /* Startup type. */
\r
818 unsigned long startup;
\r
819 switch (service->startup) {
\r
820 case NSSM_STARTUP_MANUAL: startup = SERVICE_DEMAND_START; break;
\r
821 case NSSM_STARTUP_DISABLED: startup = SERVICE_DISABLED; break;
\r
822 default: startup = SERVICE_AUTO_START;
\r
825 /* Display name. */
\r
826 if (! service->displayname[0]) _sntprintf_s(service->displayname, _countof(service->displayname), _TRUNCATE, _T("%s"), service->name);
\r
829 Username must be NULL if we aren't changing or an account name.
\r
830 We must explicitly use LOCALSYSTEM to change it when we are editing.
\r
831 Password must be NULL if we aren't changing, a password or "".
\r
832 Empty passwords are valid but we won't allow them in the GUI.
\r
834 TCHAR *username = 0;
\r
835 TCHAR *password = 0;
\r
836 if (service->usernamelen) {
\r
837 username = service->username;
\r
838 if (service->passwordlen) password = service->password;
\r
839 else password = _T("");
\r
841 else if (editing) username = NSSM_LOCALSYSTEM_ACCOUNT;
\r
843 if (grant_logon_as_service(username)) {
\r
844 print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username);
\r
848 if (! ChangeServiceConfig(service->handle, service->type, startup, SERVICE_NO_CHANGE, 0, 0, 0, 0, username, password, service->displayname)) {
\r
849 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
853 if (service->description[0] || editing) {
\r
854 set_service_description(service->name, service->handle, service->description);
\r
857 SERVICE_DELAYED_AUTO_START_INFO delayed;
\r
858 ZeroMemory(&delayed, sizeof(delayed));
\r
859 if (service->startup == NSSM_STARTUP_DELAYED) delayed.fDelayedAutostart = 1;
\r
860 else delayed.fDelayedAutostart = 0;
\r
861 /* Delayed startup isn't supported until Vista. */
\r
862 if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, &delayed)) {
\r
863 unsigned long error = GetLastError();
\r
864 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
\r
865 if (error != ERROR_INVALID_LEVEL) {
\r
866 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_DELAYED_AUTO_START_INFO_FAILED, service->name, error_string(error), 0);
\r
870 /* Don't mess with parameters which aren't ours. */
\r
871 if (! service->native) {
\r
872 /* Now we need to put the parameters into the registry */
\r
873 if (create_parameters(service, editing)) {
\r
874 print_message(stderr, NSSM_MESSAGE_CREATE_PARAMETERS_FAILED);
\r
878 set_service_recovery(service);
\r
884 /* Control a service. */
\r
885 int control_service(unsigned long control, int argc, TCHAR **argv) {
\r
886 if (argc < 1) return usage(1);
\r
887 TCHAR *service_name = argv[0];
\r
888 TCHAR canonical_name[SERVICE_NAME_LENGTH];
\r
890 SC_HANDLE services = open_service_manager();
\r
892 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
896 SC_HANDLE service_handle = open_service(services, service_name, canonical_name, _countof(canonical_name));
\r
897 if (! service_handle) {
\r
898 CloseServiceHandle(services);
\r
903 unsigned long error;
\r
904 SERVICE_STATUS service_status;
\r
905 if (control == 0) {
\r
906 ret = StartService(service_handle, (unsigned long) argc, (const TCHAR **) argv);
\r
907 error = GetLastError();
\r
908 CloseHandle(service_handle);
\r
909 CloseServiceHandle(services);
\r
911 if (error == ERROR_IO_PENDING) {
\r
913 Older versions of Windows return immediately with ERROR_IO_PENDING
\r
914 indicate that the operation is still in progress. Newer versions
\r
915 will return it if there really is a delay. As far as we're
\r
916 concerned the operation is a success. We don't claim to offer a
\r
917 fully-feature service control method; it's just a quick 'n' dirty
\r
920 In the future we may identify and handle this situation properly.
\r
923 error = ERROR_SUCCESS;
\r
927 _tprintf(_T("%s: %s"), canonical_name, error_string(error));
\r
931 _ftprintf(stderr, _T("%s: %s"), canonical_name, error_string(error));
\r
935 else if (control == SERVICE_CONTROL_INTERROGATE) {
\r
937 We could actually send an INTERROGATE control but that won't return
\r
938 any information if the service is stopped and we don't care about
\r
939 the extra details it might give us in any case. So we'll fake it.
\r
941 ret = QueryServiceStatus(service_handle, &service_status);
\r
942 error = GetLastError();
\r
945 switch (service_status.dwCurrentState) {
\r
946 case SERVICE_STOPPED: _tprintf(_T("SERVICE_STOPPED\n")); break;
\r
947 case SERVICE_START_PENDING: _tprintf(_T("SERVICE_START_PENDING\n")); break;
\r
948 case SERVICE_STOP_PENDING: _tprintf(_T("SERVICE_STOP_PENDING\n")); break;
\r
949 case SERVICE_RUNNING: _tprintf(_T("SERVICE_RUNNING\n")); break;
\r
950 case SERVICE_CONTINUE_PENDING: _tprintf(_T("SERVICE_CONTINUE_PENDING\n")); break;
\r
951 case SERVICE_PAUSE_PENDING: _tprintf(_T("SERVICE_PAUSE_PENDING\n")); break;
\r
952 case SERVICE_PAUSED: _tprintf(_T("SERVICE_PAUSED\n")); break;
\r
953 default: _tprintf(_T("?\n")); return 1;
\r
958 _ftprintf(stderr, _T("%s: %s\n"), canonical_name, error_string(error));
\r
963 ret = ControlService(service_handle, control, &service_status);
\r
964 error = GetLastError();
\r
965 CloseHandle(service_handle);
\r
966 CloseServiceHandle(services);
\r
968 if (error == ERROR_IO_PENDING) {
\r
970 error = ERROR_SUCCESS;
\r
974 _tprintf(_T("%s: %s"), canonical_name, error_string(error));
\r
978 _ftprintf(stderr, _T("%s: %s"), canonical_name, error_string(error));
\r
984 /* Remove the service */
\r
985 int remove_service(nssm_service_t *service) {
\r
986 if (! service) return 1;
\r
988 /* Open service manager */
\r
989 SC_HANDLE services = open_service_manager();
\r
991 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
995 /* Try to open the service */
\r
996 service->handle = open_service(services, service->name, service->name, _countof(service->name));
\r
997 if (! service->handle) {
\r
998 CloseServiceHandle(services);
\r
1002 /* Get the canonical service name. We open it case insensitively. */
\r
1003 unsigned long bufsize = _countof(service->displayname);
\r
1004 GetServiceDisplayName(services, service->name, service->displayname, &bufsize);
\r
1005 bufsize = _countof(service->name);
\r
1006 GetServiceKeyName(services, service->displayname, service->name, &bufsize);
\r
1008 /* Try to delete the service */
\r
1009 if (! DeleteService(service->handle)) {
\r
1010 print_message(stderr, NSSM_MESSAGE_DELETESERVICE_FAILED);
\r
1011 CloseServiceHandle(services);
\r
1016 CloseServiceHandle(services);
\r
1018 print_message(stdout, NSSM_MESSAGE_SERVICE_REMOVED, service->name);
\r
1022 /* Service initialisation */
\r
1023 void WINAPI service_main(unsigned long argc, TCHAR **argv) {
\r
1024 nssm_service_t *service = alloc_nssm_service();
\r
1025 if (! service) return;
\r
1027 if (_sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]) < 0) {
\r
1028 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("service->name"), _T("service_main()"), 0);
\r
1032 /* We can use a condition variable in a critical section on Vista or later. */
\r
1033 if (imports.SleepConditionVariableCS && imports.WakeConditionVariable) use_critical_section = true;
\r
1034 else use_critical_section = false;
\r
1036 /* Initialise status */
\r
1037 ZeroMemory(&service->status, sizeof(service->status));
\r
1038 service->status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;
\r
1039 service->status.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE;
\r
1040 service->status.dwWin32ExitCode = NO_ERROR;
\r
1041 service->status.dwServiceSpecificExitCode = 0;
\r
1042 service->status.dwCheckPoint = 0;
\r
1043 service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;
\r
1045 /* Signal we AREN'T running the server */
\r
1046 service->process_handle = 0;
\r
1049 /* Register control handler */
\r
1050 service->status_handle = RegisterServiceCtrlHandlerEx(NSSM, service_control_handler, (void *) service);
\r
1051 if (! service->status_handle) {
\r
1052 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_REGISTERSERVICECTRLHANDER_FAILED, error_string(GetLastError()), 0);
\r
1056 log_service_control(service->name, 0, true);
\r
1058 service->status.dwCurrentState = SERVICE_START_PENDING;
\r
1059 service->status.dwWaitHint = service->throttle_delay + NSSM_WAITHINT_MARGIN;
\r
1060 SetServiceStatus(service->status_handle, &service->status);
\r
1063 /* Try to create the exit action parameters; we don't care if it fails */
\r
1064 create_exit_action(service->name, exit_action_strings[0], false);
\r
1066 SC_HANDLE services = open_service_manager();
\r
1068 service->handle = OpenService(services, service->name, SC_MANAGER_ALL_ACCESS);
\r
1069 set_service_recovery(service);
\r
1070 CloseServiceHandle(services);
\r
1074 /* Used for signalling a resume if the service pauses when throttled. */
\r
1075 if (use_critical_section) {
\r
1076 InitializeCriticalSection(&service->throttle_section);
\r
1077 service->throttle_section_initialised = true;
\r
1080 service->throttle_timer = CreateWaitableTimer(0, 1, 0);
\r
1081 if (! service->throttle_timer) {
\r
1082 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_CREATEWAITABLETIMER_FAILED, service->name, error_string(GetLastError()), 0);
\r
1086 monitor_service(service);
\r
1089 /* Make sure service recovery actions are taken where necessary */
\r
1090 void set_service_recovery(nssm_service_t *service) {
\r
1091 SERVICE_FAILURE_ACTIONS_FLAG flag;
\r
1092 ZeroMemory(&flag, sizeof(flag));
\r
1093 flag.fFailureActionsOnNonCrashFailures = true;
\r
1095 /* This functionality was added in Vista so the call may fail */
\r
1096 if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, &flag)) {
\r
1097 unsigned long error = GetLastError();
\r
1098 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
\r
1099 if (error != ERROR_INVALID_LEVEL) {
\r
1100 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_FAILURE_ACTIONS_FAILED, service->name, error_string(error), 0);
\r
1105 int monitor_service(nssm_service_t *service) {
\r
1106 /* Set service status to started */
\r
1107 int ret = start_service(service);
\r
1110 _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%d"), ret);
\r
1111 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_START_SERVICE_FAILED, service->exe, service->name, ret, 0);
\r
1114 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_STARTED_SERVICE, service->exe, service->flags, service->name, service->dir, 0);
\r
1116 /* Monitor service */
\r
1117 if (! RegisterWaitForSingleObject(&service->wait_handle, service->process_handle, end_service, (void *) service, INFINITE, WT_EXECUTEONLYONCE | WT_EXECUTELONGFUNCTION)) {
\r
1118 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_REGISTERWAITFORSINGLEOBJECT_FAILED, service->name, service->exe, error_string(GetLastError()), 0);
\r
1124 TCHAR *service_control_text(unsigned long control) {
\r
1125 switch (control) {
\r
1126 /* HACK: there is no SERVICE_CONTROL_START constant */
\r
1127 case 0: return _T("START");
\r
1128 case SERVICE_CONTROL_STOP: return _T("STOP");
\r
1129 case SERVICE_CONTROL_SHUTDOWN: return _T("SHUTDOWN");
\r
1130 case SERVICE_CONTROL_PAUSE: return _T("PAUSE");
\r
1131 case SERVICE_CONTROL_CONTINUE: return _T("CONTINUE");
\r
1132 case SERVICE_CONTROL_INTERROGATE: return _T("INTERROGATE");
\r
1133 default: return 0;
\r
1137 void log_service_control(TCHAR *service_name, unsigned long control, bool handled) {
\r
1138 TCHAR *text = service_control_text(control);
\r
1139 unsigned long event;
\r
1142 /* "0x" + 8 x hex + NULL */
\r
1143 text = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, 11 * sizeof(TCHAR));
\r
1145 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);
\r
1148 if (_sntprintf_s(text, 11, _TRUNCATE, _T("0x%08x"), control) < 0) {
\r
1149 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);
\r
1150 HeapFree(GetProcessHeap(), 0, text);
\r
1154 event = NSSM_EVENT_SERVICE_CONTROL_UNKNOWN;
\r
1156 else if (handled) event = NSSM_EVENT_SERVICE_CONTROL_HANDLED;
\r
1157 else event = NSSM_EVENT_SERVICE_CONTROL_NOT_HANDLED;
\r
1159 log_event(EVENTLOG_INFORMATION_TYPE, event, service_name, text, 0);
\r
1161 if (event == NSSM_EVENT_SERVICE_CONTROL_UNKNOWN) {
\r
1162 HeapFree(GetProcessHeap(), 0, text);
\r
1166 /* Service control handler */
\r
1167 unsigned long WINAPI service_control_handler(unsigned long control, unsigned long event, void *data, void *context) {
\r
1168 nssm_service_t *service = (nssm_service_t *) context;
\r
1170 switch (control) {
\r
1171 case SERVICE_CONTROL_INTERROGATE:
\r
1172 /* We always keep the service status up-to-date so this is a no-op. */
\r
1175 case SERVICE_CONTROL_SHUTDOWN:
\r
1176 case SERVICE_CONTROL_STOP:
\r
1177 log_service_control(service->name, control, true);
\r
1179 We MUST acknowledge the stop request promptly but we're committed to
\r
1180 waiting for the application to exit. Spawn a new thread to wait
\r
1181 while we acknowledge the request.
\r
1183 if (! CreateThread(NULL, 0, shutdown_service, context, 0, NULL)) {
\r
1184 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETHREAD_FAILED, error_string(GetLastError()), 0);
\r
1187 We couldn't create a thread to tidy up so we'll have to force the tidyup
\r
1188 to complete in time in this thread.
\r
1190 service->kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;
\r
1191 service->kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;
\r
1192 service->kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;
\r
1194 stop_service(service, 0, true, true);
\r
1198 case SERVICE_CONTROL_CONTINUE:
\r
1199 log_service_control(service->name, control, true);
\r
1200 service->throttle = 0;
\r
1201 if (use_critical_section) imports.WakeConditionVariable(&service->throttle_condition);
\r
1203 if (! service->throttle_timer) return ERROR_CALL_NOT_IMPLEMENTED;
\r
1204 ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));
\r
1205 SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);
\r
1207 service->status.dwCurrentState = SERVICE_CONTINUE_PENDING;
\r
1208 service->status.dwWaitHint = throttle_milliseconds(service->throttle) + NSSM_WAITHINT_MARGIN;
\r
1209 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESET_THROTTLE, service->name, 0);
\r
1210 SetServiceStatus(service->status_handle, &service->status);
\r
1213 case SERVICE_CONTROL_PAUSE:
\r
1215 We don't accept pause messages but it isn't possible to register
\r
1216 only for continue messages so we have to handle this case.
\r
1218 log_service_control(service->name, control, false);
\r
1219 return ERROR_CALL_NOT_IMPLEMENTED;
\r
1222 /* Unknown control */
\r
1223 log_service_control(service->name, control, false);
\r
1224 return ERROR_CALL_NOT_IMPLEMENTED;
\r
1227 /* Start the service */
\r
1228 int start_service(nssm_service_t *service) {
\r
1229 service->stopping = false;
\r
1230 service->allow_restart = true;
\r
1232 if (service->process_handle) return 0;
\r
1234 /* Allocate a STARTUPINFO structure for a new process */
\r
1236 ZeroMemory(&si, sizeof(si));
\r
1237 si.cb = sizeof(si);
\r
1239 /* Allocate a PROCESSINFO structure for the process */
\r
1240 PROCESS_INFORMATION pi;
\r
1241 ZeroMemory(&pi, sizeof(pi));
\r
1243 /* Get startup parameters */
\r
1244 int ret = get_parameters(service, &si);
\r
1246 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_PARAMETERS_FAILED, service->name, 0);
\r
1247 return stop_service(service, 2, true, true);
\r
1250 /* Launch executable with arguments */
\r
1251 TCHAR cmd[CMD_LENGTH];
\r
1252 if (_sntprintf_s(cmd, _countof(cmd), _TRUNCATE, _T("\"%s\" %s"), service->exe, service->flags) < 0) {
\r
1253 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("command line"), _T("start_service"), 0);
\r
1254 close_output_handles(&si);
\r
1255 return stop_service(service, 2, true, true);
\r
1258 throttle_restart(service);
\r
1260 bool inherit_handles = false;
\r
1261 if (si.dwFlags & STARTF_USESTDHANDLES) inherit_handles = true;
\r
1262 unsigned long flags = 0;
\r
1264 flags |= CREATE_UNICODE_ENVIRONMENT;
\r
1266 if (! CreateProcess(0, cmd, 0, 0, inherit_handles, flags, service->env, service->dir, &si, &pi)) {
\r
1267 unsigned long exitcode = 3;
\r
1268 unsigned long error = GetLastError();
\r
1269 if (error == ERROR_INVALID_PARAMETER && service->env) {
\r
1270 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED_INVALID_ENVIRONMENT, service->name, service->exe, NSSM_REG_ENV, 0);
\r
1271 if (test_environment(service->env)) exitcode = 4;
\r
1273 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service->name, service->exe, error_string(error), 0);
\r
1274 close_output_handles(&si);
\r
1275 return stop_service(service, exitcode, true, true);
\r
1277 service->process_handle = pi.hProcess;
\r
1278 service->pid = pi.dwProcessId;
\r
1280 if (get_process_creation_time(service->process_handle, &service->creation_time)) ZeroMemory(&service->creation_time, sizeof(service->creation_time));
\r
1282 close_output_handles(&si);
\r
1285 Wait for a clean startup before changing the service status to RUNNING
\r
1286 but be mindful of the fact that we are blocking the service control manager
\r
1287 so abandon the wait before too much time has elapsed.
\r
1289 unsigned long delay = service->throttle_delay;
\r
1290 if (delay > NSSM_SERVICE_STATUS_DEADLINE) {
\r
1291 TCHAR delay_milliseconds[16];
\r
1292 _sntprintf_s(delay_milliseconds, _countof(delay_milliseconds), _TRUNCATE, _T("%lu"), delay);
\r
1293 TCHAR deadline_milliseconds[16];
\r
1294 _sntprintf_s(deadline_milliseconds, _countof(deadline_milliseconds), _TRUNCATE, _T("%lu"), NSSM_SERVICE_STATUS_DEADLINE);
\r
1295 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_STARTUP_DELAY_TOO_LONG, service->name, delay_milliseconds, NSSM, deadline_milliseconds, 0);
\r
1296 delay = NSSM_SERVICE_STATUS_DEADLINE;
\r
1298 unsigned long deadline = WaitForSingleObject(service->process_handle, delay);
\r
1300 /* Signal successful start */
\r
1301 service->status.dwCurrentState = SERVICE_RUNNING;
\r
1302 SetServiceStatus(service->status_handle, &service->status);
\r
1304 /* Continue waiting for a clean startup. */
\r
1305 if (deadline == WAIT_TIMEOUT) {
\r
1306 if (service->throttle_delay > delay) {
\r
1307 if (WaitForSingleObject(service->process_handle, service->throttle_delay - delay) == WAIT_TIMEOUT) service->throttle = 0;
\r
1309 else service->throttle = 0;
\r
1315 /* Stop the service */
\r
1316 int stop_service(nssm_service_t *service, unsigned long exitcode, bool graceful, bool default_action) {
\r
1317 service->allow_restart = false;
\r
1318 if (service->wait_handle) {
\r
1319 UnregisterWait(service->wait_handle);
\r
1320 service->wait_handle = 0;
\r
1323 if (default_action && ! exitcode && ! graceful) {
\r
1324 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
1328 /* Signal we are stopping */
\r
1330 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
1331 service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;
\r
1332 SetServiceStatus(service->status_handle, &service->status);
\r
1335 /* Nothing to do if service isn't running */
\r
1336 if (service->pid) {
\r
1337 /* Shut down service */
\r
1338 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_TERMINATEPROCESS, service->name, service->exe, 0);
\r
1339 kill_process(service, service->process_handle, service->pid, 0);
\r
1341 else log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_PROCESS_ALREADY_STOPPED, service->name, service->exe, 0);
\r
1343 end_service((void *) service, true);
\r
1345 /* Signal we stopped */
\r
1347 service->status.dwCurrentState = SERVICE_STOPPED;
\r
1349 service->status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
\r
1350 service->status.dwServiceSpecificExitCode = exitcode;
\r
1353 service->status.dwWin32ExitCode = NO_ERROR;
\r
1354 service->status.dwServiceSpecificExitCode = 0;
\r
1356 SetServiceStatus(service->status_handle, &service->status);
\r
1362 /* Callback function triggered when the server exits */
\r
1363 void CALLBACK end_service(void *arg, unsigned char why) {
\r
1364 nssm_service_t *service = (nssm_service_t *) arg;
\r
1366 if (service->stopping) return;
\r
1368 service->stopping = true;
\r
1370 /* Check exit code */
\r
1371 unsigned long exitcode = 0;
\r
1373 if (service->process_handle) {
\r
1374 GetExitCodeProcess(service->process_handle, &exitcode);
\r
1375 if (exitcode == STILL_ACTIVE || get_process_exit_time(service->process_handle, &service->exit_time)) GetSystemTimeAsFileTime(&service->exit_time);
\r
1376 CloseHandle(service->process_handle);
\r
1378 else GetSystemTimeAsFileTime(&service->exit_time);
\r
1380 service->process_handle = 0;
\r
1383 Log that the service ended BEFORE logging about killing the process
\r
1384 tree. See below for the possible values of the why argument.
\r
1387 _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), exitcode);
\r
1388 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ENDED_SERVICE, service->exe, service->name, code, 0);
\r
1392 if (exitcode == STILL_ACTIVE) exitcode = 0;
\r
1393 if (service->pid) kill_process_tree(service, service->pid, exitcode, service->pid);
\r
1397 The why argument is true if our wait timed out or false otherwise.
\r
1398 Our wait is infinite so why will never be true when called by the system.
\r
1399 If it is indeed true, assume we were called from stop_service() because
\r
1400 this is a controlled shutdown, and don't take any restart action.
\r
1403 if (! service->allow_restart) return;
\r
1405 /* What action should we take? */
\r
1406 int action = NSSM_EXIT_RESTART;
\r
1407 TCHAR action_string[ACTION_LEN];
\r
1408 bool default_action;
\r
1409 if (! get_exit_action(service->name, &exitcode, action_string, &default_action)) {
\r
1410 for (int i = 0; exit_action_strings[i]; i++) {
\r
1411 if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
1419 /* Try to restart the service or return failure code to service manager */
\r
1420 case NSSM_EXIT_RESTART:
\r
1421 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_RESTART, service->name, code, exit_action_strings[action], service->exe, 0);
\r
1422 while (monitor_service(service)) {
\r
1423 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_RESTART_SERVICE_FAILED, service->exe, service->name, 0);
\r
1428 /* Do nothing, just like srvany would */
\r
1429 case NSSM_EXIT_IGNORE:
\r
1430 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_IGNORE, service->name, code, exit_action_strings[action], service->exe, 0);
\r
1434 /* Tell the service manager we are finished */
\r
1435 case NSSM_EXIT_REALLY:
\r
1436 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_REALLY, service->name, code, exit_action_strings[action], 0);
\r
1437 stop_service(service, exitcode, true, default_action);
\r
1440 /* Fake a crash so pre-Vista service managers will run recovery actions. */
\r
1441 case NSSM_EXIT_UNCLEAN:
\r
1442 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_UNCLEAN, service->name, code, exit_action_strings[action], 0);
\r
1443 stop_service(service, exitcode, false, default_action);
\r
1450 void throttle_restart(nssm_service_t *service) {
\r
1451 /* This can't be a restart if the service is already running. */
\r
1452 if (! service->throttle++) return;
\r
1454 int ms = throttle_milliseconds(service->throttle);
\r
1456 if (service->throttle > 7) service->throttle = 8;
\r
1458 TCHAR threshold[8], milliseconds[8];
\r
1459 _sntprintf_s(threshold, _countof(threshold), _TRUNCATE, _T("%lu"), service->throttle_delay);
\r
1460 _sntprintf_s(milliseconds, _countof(milliseconds), _TRUNCATE, _T("%lu"), ms);
\r
1461 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_THROTTLED, service->name, threshold, milliseconds, 0);
\r
1463 if (use_critical_section) EnterCriticalSection(&service->throttle_section);
\r
1464 else if (service->throttle_timer) {
\r
1465 ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));
\r
1466 service->throttle_duetime.QuadPart = 0 - (ms * 10000LL);
\r
1467 SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);
\r
1470 service->status.dwCurrentState = SERVICE_PAUSED;
\r
1471 SetServiceStatus(service->status_handle, &service->status);
\r
1473 if (use_critical_section) {
\r
1474 imports.SleepConditionVariableCS(&service->throttle_condition, &service->throttle_section, ms);
\r
1475 LeaveCriticalSection(&service->throttle_section);
\r
1478 if (service->throttle_timer) WaitForSingleObject(service->throttle_timer, INFINITE);
\r
1484 When responding to a stop (or any other) request we need to set dwWaitHint to
\r
1485 the number of milliseconds we expect the operation to take, and optionally
\r
1486 increase dwCheckPoint. If dwWaitHint milliseconds elapses without the
\r
1487 operation completing or dwCheckPoint increasing, the system will consider the
\r
1488 service to be hung.
\r
1490 However the system will consider the service to be hung after 30000
\r
1491 milliseconds regardless of the value of dwWaitHint if dwCheckPoint has not
\r
1492 changed. Therefore if we want to wait longer than that we must periodically
\r
1493 increase dwCheckPoint.
\r
1495 Furthermore, it will consider the service to be hung after 60000 milliseconds
\r
1496 regardless of the value of dwCheckPoint unless dwWaitHint is increased every
\r
1497 time dwCheckPoint is also increased.
\r
1499 Our strategy then is to retrieve the initial dwWaitHint and wait for
\r
1500 NSSM_SERVICE_STATUS_DEADLINE milliseconds. If the process is still running
\r
1501 and we haven't finished waiting we increment dwCheckPoint and add whichever is
\r
1502 smaller of NSSM_SERVICE_STATUS_DEADLINE or the remaining timeout to
\r
1505 Only doing both these things will prevent the system from killing the service.
\r
1507 Returns: 1 if the wait timed out.
\r
1508 0 if the wait completed.
\r
1511 int await_shutdown(nssm_service_t *service, TCHAR *function_name, unsigned long timeout) {
\r
1512 unsigned long interval;
\r
1513 unsigned long waithint;
\r
1514 unsigned long ret;
\r
1515 unsigned long waited;
\r
1516 TCHAR interval_milliseconds[16];
\r
1517 TCHAR timeout_milliseconds[16];
\r
1518 TCHAR waited_milliseconds[16];
\r
1519 TCHAR *function = function_name;
\r
1521 /* Add brackets to function name. */
\r
1522 size_t funclen = _tcslen(function_name) + 3;
\r
1523 TCHAR *func = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, funclen * sizeof(TCHAR));
\r
1525 if (_sntprintf_s(func, funclen, _TRUNCATE, _T("%s()"), function_name) > -1) function = func;
\r
1528 _sntprintf_s(timeout_milliseconds, _countof(timeout_milliseconds), _TRUNCATE, _T("%lu"), timeout);
\r
1530 waithint = service->status.dwWaitHint;
\r
1532 while (waited < timeout) {
\r
1533 interval = timeout - waited;
\r
1534 if (interval > NSSM_SERVICE_STATUS_DEADLINE) interval = NSSM_SERVICE_STATUS_DEADLINE;
\r
1536 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
1537 service->status.dwWaitHint += interval;
\r
1538 service->status.dwCheckPoint++;
\r
1539 SetServiceStatus(service->status_handle, &service->status);
\r
1542 _sntprintf_s(waited_milliseconds, _countof(waited_milliseconds), _TRUNCATE, _T("%lu"), waited);
\r
1543 _sntprintf_s(interval_milliseconds, _countof(interval_milliseconds), _TRUNCATE, _T("%lu"), interval);
\r
1544 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_AWAITING_SHUTDOWN, function, service->name, waited_milliseconds, interval_milliseconds, timeout_milliseconds, 0);
\r
1547 switch (WaitForSingleObject(service->process_handle, interval)) {
\r
1548 case WAIT_OBJECT_0:
\r
1552 case WAIT_TIMEOUT:
\r
1561 waited += interval;
\r
1565 if (func) HeapFree(GetProcessHeap(), 0, func);
\r