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 (str_equiv(username, NSSM_LOCALSYSTEM_ACCOUNT)) return 0;
\r
267 /* Open Policy object. */
\r
268 LSA_OBJECT_ATTRIBUTES attributes;
\r
269 ZeroMemory(&attributes, sizeof(attributes));
\r
273 NTSTATUS status = LsaOpenPolicy(0, &attributes, POLICY_ALL_ACCESS, &policy);
\r
275 print_message(stderr, NSSM_MESSAGE_LSAOPENPOLICY_FAILED, error_string(LsaNtStatusToWinError(status)));
\r
279 /* Look up SID for the account. */
\r
280 LSA_UNICODE_STRING lsa_username;
\r
282 lsa_username.Buffer = (wchar_t *) username;
\r
283 lsa_username.Length = (unsigned short) _tcslen(username) * sizeof(TCHAR);
\r
284 lsa_username.MaximumLength = lsa_username.Length + sizeof(TCHAR);
\r
287 mbstowcs_s(&buflen, NULL, 0, username, _TRUNCATE);
\r
288 lsa_username.MaximumLength = buflen * sizeof(wchar_t);
\r
289 lsa_username.Length = lsa_username.MaximumLength - sizeof(wchar_t);
\r
290 lsa_username.Buffer = (wchar_t *) HeapAlloc(GetProcessHeap(), 0, lsa_username.MaximumLength);
\r
291 if (lsa_username.Buffer) mbstowcs_s(&buflen, lsa_username.Buffer, lsa_username.MaximumLength, username, _TRUNCATE);
\r
294 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("LSA_UNICODE_STRING"), _T("grant_logon_as_service()"));
\r
299 LSA_REFERENCED_DOMAIN_LIST *translated_domains;
\r
300 LSA_TRANSLATED_SID *translated_sid;
\r
301 status = LsaLookupNames(policy, 1, &lsa_username, &translated_domains, &translated_sid);
\r
303 HeapFree(GetProcessHeap(), 0, lsa_username.Buffer);
\r
306 LsaFreeMemory(translated_domains);
\r
307 LsaFreeMemory(translated_sid);
\r
309 print_message(stderr, NSSM_MESSAGE_LSALOOKUPNAMES_FAILED, username, error_string(LsaNtStatusToWinError(status)));
\r
313 if (translated_sid->Use != SidTypeUser) {
\r
314 LsaFreeMemory(translated_domains);
\r
315 LsaFreeMemory(translated_sid);
\r
317 print_message(stderr, NSSM_GUI_INVALID_USERNAME, username);
\r
321 LSA_TRUST_INFORMATION *trust = &translated_domains->Domains[translated_sid->DomainIndex];
\r
322 if (! trust || ! IsValidSid(trust->Sid)) {
\r
323 LsaFreeMemory(translated_domains);
\r
324 LsaFreeMemory(translated_sid);
\r
326 print_message(stderr, NSSM_GUI_INVALID_USERNAME, username);
\r
330 /* GetSidSubAuthority*() return pointers! */
\r
331 unsigned char *n = GetSidSubAuthorityCount(trust->Sid);
\r
333 /* Convert translated SID to SID. */
\r
334 SID *sid = (SID *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, GetSidLengthRequired(*n + 1));
\r
336 LsaFreeMemory(translated_domains);
\r
337 LsaFreeMemory(translated_sid);
\r
339 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("SID"), _T("grant_logon_as_service"));
\r
343 unsigned long error;
\r
344 if (! InitializeSid(sid, GetSidIdentifierAuthority(trust->Sid), *n + 1)) {
\r
345 error = GetLastError();
\r
346 HeapFree(GetProcessHeap(), 0, sid);
\r
347 LsaFreeMemory(translated_domains);
\r
348 LsaFreeMemory(translated_sid);
\r
350 print_message(stderr, NSSM_MESSAGE_INITIALIZESID_FAILED, username, error_string(error));
\r
354 for (unsigned char i = 0; i <= *n; i++) {
\r
355 unsigned long *sub = GetSidSubAuthority(sid, i);
\r
356 if (i < *n) *sub = *GetSidSubAuthority(trust->Sid, i);
\r
357 else *sub = translated_sid->RelativeId;
\r
360 LsaFreeMemory(translated_domains);
\r
361 LsaFreeMemory(translated_sid);
\r
363 /* Check if the SID has the "Log on as a service" right. */
\r
364 LSA_UNICODE_STRING lsa_right;
\r
365 lsa_right.Buffer = NSSM_LOGON_AS_SERVICE_RIGHT;
\r
366 lsa_right.Length = (unsigned short) wcslen(lsa_right.Buffer) * sizeof(wchar_t);
\r
367 lsa_right.MaximumLength = lsa_right.Length + sizeof(wchar_t);
\r
369 LSA_UNICODE_STRING *rights;
\r
370 unsigned long count = ~0;
\r
371 status = LsaEnumerateAccountRights(policy, sid, &rights, &count);
\r
374 If the account has no rights set LsaEnumerateAccountRights() will return
\r
375 STATUS_OBJECT_NAME_NOT_FOUND and set count to 0.
\r
377 error = LsaNtStatusToWinError(status);
\r
378 if (error != ERROR_FILE_NOT_FOUND) {
\r
379 HeapFree(GetProcessHeap(), 0, sid);
\r
381 print_message(stderr, NSSM_MESSAGE_LSAENUMERATEACCOUNTRIGHTS_FAILED, username, error_string(error));
\r
386 for (unsigned long i = 0; i < count; i++) {
\r
387 if (rights[i].Length != lsa_right.Length) continue;
\r
388 if (_wcsnicmp(rights[i].Buffer, lsa_right.Buffer, lsa_right.MaximumLength)) continue;
\r
389 /* The SID has the right. */
\r
390 HeapFree(GetProcessHeap(), 0, sid);
\r
391 LsaFreeMemory(rights);
\r
395 LsaFreeMemory(rights);
\r
397 /* Add the right. */
\r
398 status = LsaAddAccountRights(policy, sid, &lsa_right, 1);
\r
399 HeapFree(GetProcessHeap(), 0, sid);
\r
402 print_message(stderr, NSSM_MESSAGE_LSAADDACCOUNTRIGHTS_FAILED, error_string(LsaNtStatusToWinError(status)));
\r
406 print_message(stdout, NSSM_MESSAGE_GRANTED_LOGON_AS_SERVICE, username);
\r
410 /* Set default values which aren't zero. */
\r
411 void set_nssm_service_defaults(nssm_service_t *service) {
\r
412 if (! service) return;
\r
414 service->type = SERVICE_WIN32_OWN_PROCESS;
\r
415 service->stdin_sharing = NSSM_STDIN_SHARING;
\r
416 service->stdin_disposition = NSSM_STDIN_DISPOSITION;
\r
417 service->stdin_flags = NSSM_STDIN_FLAGS;
\r
418 service->stdout_sharing = NSSM_STDOUT_SHARING;
\r
419 service->stdout_disposition = NSSM_STDOUT_DISPOSITION;
\r
420 service->stdout_flags = NSSM_STDOUT_FLAGS;
\r
421 service->stderr_sharing = NSSM_STDERR_SHARING;
\r
422 service->stderr_disposition = NSSM_STDERR_DISPOSITION;
\r
423 service->stderr_flags = NSSM_STDERR_FLAGS;
\r
424 service->throttle_delay = NSSM_RESET_THROTTLE_RESTART;
\r
425 service->stop_method = ~0;
\r
426 service->kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;
\r
427 service->kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;
\r
428 service->kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;
\r
431 /* Allocate and zero memory for a service. */
\r
432 nssm_service_t *alloc_nssm_service() {
\r
433 nssm_service_t *service = (nssm_service_t *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(nssm_service_t));
\r
434 if (! service) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("service"), _T("alloc_nssm_service()"), 0);
\r
438 /* Free memory for a service. */
\r
439 void cleanup_nssm_service(nssm_service_t *service) {
\r
440 if (! service) return;
\r
441 if (service->username) HeapFree(GetProcessHeap(), 0, service->username);
\r
442 if (service->password) {
\r
443 SecureZeroMemory(service->password, service->passwordlen);
\r
444 HeapFree(GetProcessHeap(), 0, service->password);
\r
446 if (service->env) HeapFree(GetProcessHeap(), 0, service->env);
\r
447 if (service->env_extra) HeapFree(GetProcessHeap(), 0, service->env_extra);
\r
448 if (service->handle) CloseServiceHandle(service->handle);
\r
449 if (service->process_handle) CloseHandle(service->process_handle);
\r
450 if (service->wait_handle) UnregisterWait(service->process_handle);
\r
451 if (service->throttle_section_initialised) DeleteCriticalSection(&service->throttle_section);
\r
452 if (service->throttle_timer) CloseHandle(service->throttle_timer);
\r
453 HeapFree(GetProcessHeap(), 0, service);
\r
456 /* About to install the service */
\r
457 int pre_install_service(int argc, TCHAR **argv) {
\r
458 nssm_service_t *service = alloc_nssm_service();
\r
459 set_nssm_service_defaults(service);
\r
460 if (argc) _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]);
\r
462 /* Show the dialogue box if we didn't give the service name and path */
\r
463 if (argc < 2) return nssm_gui(IDD_INSTALL, service);
\r
466 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("service"), _T("pre_install_service()"));
\r
469 _sntprintf_s(service->exe, _countof(service->exe), _TRUNCATE, _T("%s"), argv[1]);
\r
471 /* Arguments are optional */
\r
472 size_t flagslen = 0;
\r
475 for (i = 2; i < argc; i++) flagslen += _tcslen(argv[i]) + 1;
\r
476 if (! flagslen) flagslen = 1;
\r
477 if (flagslen > _countof(service->flags)) {
\r
478 print_message(stderr, NSSM_MESSAGE_FLAGS_TOO_LONG);
\r
482 for (i = 2; i < argc; i++) {
\r
483 size_t len = _tcslen(argv[i]);
\r
484 memmove(service->flags + s, argv[i], len * sizeof(TCHAR));
\r
486 if (i < argc - 1) service->flags[s++] = _T(' ');
\r
489 /* Work out directory name */
\r
490 _sntprintf_s(service->dir, _countof(service->dir), _TRUNCATE, _T("%s"), service->exe);
\r
491 strip_basename(service->dir);
\r
493 int ret = install_service(service);
\r
494 cleanup_nssm_service(service);
\r
498 /* About to edit the service. */
\r
499 int pre_edit_service(int argc, TCHAR **argv) {
\r
500 /* Require service name. */
\r
501 if (argc < 2) return usage(1);
\r
503 /* Are we editing on the command line? */
\r
504 enum { MODE_EDITING, MODE_GETTING, MODE_SETTING, MODE_RESETTING } mode = MODE_EDITING;
\r
505 const TCHAR *verb = argv[0];
\r
506 const TCHAR *service_name = argv[1];
\r
507 bool getting = false;
\r
508 bool unsetting = false;
\r
510 /* Minimum number of arguments. */
\r
512 /* Index of first value. */
\r
515 if (str_equiv(verb, _T("get"))) {
\r
517 mode = MODE_GETTING;
\r
519 else if (str_equiv(verb, _T("set"))) {
\r
521 mode = MODE_SETTING;
\r
523 else if (str_equiv(verb, _T("reset")) || str_equiv(verb, _T("unset"))) {
\r
525 mode = MODE_RESETTING;
\r
527 if (argc < mandatory) return usage(1);
\r
529 const TCHAR *parameter = 0;
\r
530 settings_t *setting = 0;
\r
533 /* Validate the parameter. */
\r
534 if (mandatory > 2) {
\r
535 bool additional_mandatory = false;
\r
537 parameter = argv[2];
\r
538 for (i = 0; settings[i].name; i++) {
\r
539 setting = &settings[i];
\r
540 if (! str_equiv(setting->name, parameter)) continue;
\r
541 if (((setting->additional & ADDITIONAL_GETTING) && mode == MODE_GETTING) || ((setting->additional & ADDITIONAL_SETTING) && mode == MODE_SETTING) || ((setting->additional & ADDITIONAL_RESETTING) && mode == MODE_RESETTING)) {
\r
542 additional_mandatory = true;
\r
547 if (! settings[i].name) {
\r
548 print_message(stderr, NSSM_MESSAGE_INVALID_PARAMETER, parameter);
\r
549 for (i = 0; settings[i].name; i++) _ftprintf(stderr, _T("%s\n"), settings[i].name);
\r
552 if (argc < mandatory) return usage(1);
\r
555 if (additional_mandatory) {
\r
556 additional = argv[3];
\r
559 else additional = argv[remainder];
\r
562 nssm_service_t *service = alloc_nssm_service();
\r
563 _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), service_name);
\r
565 /* Open service manager */
\r
566 SC_HANDLE services = open_service_manager();
\r
568 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
572 /* Try to open the service */
\r
573 service->handle = open_service(services, service->name, service->name, _countof(service->name));
\r
574 if (! service->handle) {
\r
575 CloseServiceHandle(services);
\r
579 /* Get system details. */
\r
580 QUERY_SERVICE_CONFIG *qsc = query_service_config(service->name, service->handle);
\r
582 CloseHandle(service->handle);
\r
583 CloseServiceHandle(services);
\r
587 service->type = qsc->dwServiceType;
\r
588 if (! (service->type & SERVICE_WIN32_OWN_PROCESS)) {
\r
589 if (mode != MODE_GETTING) {
\r
590 HeapFree(GetProcessHeap(), 0, qsc);
\r
591 CloseHandle(service->handle);
\r
592 CloseServiceHandle(services);
\r
593 print_message(stderr, NSSM_MESSAGE_CANNOT_EDIT, service->name, NSSM_WIN32_OWN_PROCESS, 0);
\r
598 if (get_service_startup(service->name, service->handle, qsc, &service->startup)) {
\r
599 if (mode != MODE_GETTING) {
\r
600 HeapFree(GetProcessHeap(), 0, qsc);
\r
601 CloseHandle(service->handle);
\r
602 CloseServiceHandle(services);
\r
607 if (get_service_username(service->name, qsc, &service->username, &service->usernamelen)) {
\r
608 if (mode != MODE_GETTING) {
\r
609 HeapFree(GetProcessHeap(), 0, qsc);
\r
610 CloseHandle(service->handle);
\r
611 CloseServiceHandle(services);
\r
616 _sntprintf_s(service->displayname, _countof(service->displayname), _TRUNCATE, _T("%s"), qsc->lpDisplayName);
\r
618 /* Get the canonical service name. We open it case insensitively. */
\r
619 unsigned long bufsize = _countof(service->name);
\r
620 GetServiceKeyName(services, service->displayname, service->name, &bufsize);
\r
622 /* Remember the executable in case it isn't NSSM. */
\r
623 _sntprintf_s(service->image, _countof(service->image), _TRUNCATE, _T("%s"), qsc->lpBinaryPathName);
\r
624 HeapFree(GetProcessHeap(), 0, qsc);
\r
626 /* Get extended system details. */
\r
627 if (get_service_description(service->name, service->handle, _countof(service->description), service->description)) {
\r
628 if (mode != MODE_GETTING) {
\r
629 CloseHandle(service->handle);
\r
630 CloseServiceHandle(services);
\r
635 /* Get NSSM details. */
\r
636 get_parameters(service, 0);
\r
638 CloseServiceHandle(services);
\r
640 if (! service->exe[0]) {
\r
641 service->native = true;
\r
642 if (mode != MODE_GETTING) print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE, service->name, NSSM, service->image);
\r
645 /* Editing with the GUI. */
\r
646 if (mode == MODE_EDITING) {
\r
647 nssm_gui(IDD_EDIT, service);
\r
651 /* Trying to manage App* parameters for a non-NSSM service. */
\r
652 if (! setting->native && service->native) {
\r
653 CloseHandle(service->handle);
\r
654 print_message(stderr, NSSM_MESSAGE_NATIVE_PARAMETER, setting->name, NSSM);
\r
662 if (mode == MODE_GETTING) {
\r
663 if (! service->native) {
\r
664 key = open_registry(service->name, KEY_READ);
\r
665 if (! key) return 4;
\r
668 if (setting->native) ret = get_setting(service->name, service->handle, setting, &value, additional);
\r
669 else ret = get_setting(service->name, key, setting, &value, additional);
\r
671 CloseHandle(service->handle);
\r
675 switch (setting->type) {
\r
676 case REG_EXPAND_SZ:
\r
679 _tprintf(_T("%s\n"), value.string ? value.string : _T(""));
\r
680 HeapFree(GetProcessHeap(), 0, value.string);
\r
684 _tprintf(_T("%u\n"), value.numeric);
\r
688 if (! service->native) RegCloseKey(key);
\r
689 CloseHandle(service->handle);
\r
693 /* Build the value. */
\r
694 if (mode == MODE_RESETTING) {
\r
695 /* Unset the parameter. */
\r
699 /* Set the parameter. */
\r
701 size_t delimiterlen = (setting->additional & ADDITIONAL_CRLF) ? 2 : 1;
\r
702 for (i = remainder; i < argc; i++) len += _tcslen(argv[i]) + delimiterlen;
\r
705 value.string = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, len * sizeof(TCHAR));
\r
706 if (! value.string) {
\r
707 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("value"), _T("edit_service()"));
\r
708 CloseHandle(service->handle);
\r
713 for (i = remainder; i < argc; i++) {
\r
714 size_t len = _tcslen(argv[i]);
\r
715 memmove(value.string + s, argv[i], len * sizeof(TCHAR));
\r
717 if (i < argc - 1) {
\r
718 if (setting->additional & ADDITIONAL_CRLF) {
\r
719 value.string[s++] = _T('\r');
\r
720 value.string[s++] = _T('\n');
\r
722 else value.string[s++] = _T(' ');
\r
725 value.string[s] = _T('\0');
\r
728 if (! service->native) {
\r
729 key = open_registry(service->name, KEY_WRITE);
\r
731 if (value.string) HeapFree(GetProcessHeap(), 0, value.string);
\r
736 if (setting->native) ret = set_setting(service->name, service->handle, setting, &value, additional);
\r
737 else ret = set_setting(service->name, key, setting, &value, additional);
\r
738 if (value.string) HeapFree(GetProcessHeap(), 0, value.string);
\r
740 if (! service->native) RegCloseKey(key);
\r
741 CloseHandle(service->handle);
\r
745 if (! service->native) RegCloseKey(key);
\r
746 CloseHandle(service->handle);
\r
751 /* About to remove the service */
\r
752 int pre_remove_service(int argc, TCHAR **argv) {
\r
753 nssm_service_t *service = alloc_nssm_service();
\r
754 set_nssm_service_defaults(service);
\r
755 if (argc) _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]);
\r
757 /* Show dialogue box if we didn't pass service name and "confirm" */
\r
758 if (argc < 2) return nssm_gui(IDD_REMOVE, service);
\r
759 if (str_equiv(argv[1], _T("confirm"))) {
\r
760 int ret = remove_service(service);
\r
761 cleanup_nssm_service(service);
\r
764 print_message(stderr, NSSM_MESSAGE_PRE_REMOVE_SERVICE);
\r
768 /* Install the service */
\r
769 int install_service(nssm_service_t *service) {
\r
770 if (! service) return 1;
\r
772 /* Open service manager */
\r
773 SC_HANDLE services = open_service_manager();
\r
775 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
776 cleanup_nssm_service(service);
\r
780 /* Get path of this program */
\r
781 GetModuleFileName(0, service->image, _countof(service->image));
\r
783 /* Create the service - settings will be changed in edit_service() */
\r
784 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
785 if (! service->handle) {
\r
786 print_message(stderr, NSSM_MESSAGE_CREATESERVICE_FAILED);
\r
787 CloseServiceHandle(services);
\r
791 if (edit_service(service, false)) {
\r
792 DeleteService(service->handle);
\r
793 CloseServiceHandle(services);
\r
797 print_message(stdout, NSSM_MESSAGE_SERVICE_INSTALLED, service->name);
\r
800 CloseServiceHandle(services);
\r
805 /* Edit the service. */
\r
806 int edit_service(nssm_service_t *service, bool editing) {
\r
807 if (! service) return 1;
\r
810 The only two valid flags for service type are SERVICE_WIN32_OWN_PROCESS
\r
811 and SERVICE_INTERACTIVE_PROCESS.
\r
813 service->type &= SERVICE_INTERACTIVE_PROCESS;
\r
814 service->type |= SERVICE_WIN32_OWN_PROCESS;
\r
816 /* Startup type. */
\r
817 unsigned long startup;
\r
818 switch (service->startup) {
\r
819 case NSSM_STARTUP_MANUAL: startup = SERVICE_DEMAND_START; break;
\r
820 case NSSM_STARTUP_DISABLED: startup = SERVICE_DISABLED; break;
\r
821 default: startup = SERVICE_AUTO_START;
\r
824 /* Display name. */
\r
825 if (! service->displayname[0]) _sntprintf_s(service->displayname, _countof(service->displayname), _TRUNCATE, _T("%s"), service->name);
\r
828 Username must be NULL if we aren't changing or an account name.
\r
829 We must explicitly use LOCALSYSTEM to change it when we are editing.
\r
830 Password must be NULL if we aren't changing, a password or "".
\r
831 Empty passwords are valid but we won't allow them in the GUI.
\r
833 TCHAR *username = 0;
\r
834 TCHAR *password = 0;
\r
835 if (service->usernamelen) {
\r
836 username = service->username;
\r
837 if (service->passwordlen) password = service->password;
\r
838 else password = _T("");
\r
840 else if (editing) username = NSSM_LOCALSYSTEM_ACCOUNT;
\r
842 if (grant_logon_as_service(username)) {
\r
843 print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username);
\r
847 if (! ChangeServiceConfig(service->handle, service->type, startup, SERVICE_NO_CHANGE, 0, 0, 0, 0, username, password, service->displayname)) {
\r
848 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
852 if (service->description[0] || editing) {
\r
853 set_service_description(service->name, service->handle, service->description);
\r
856 SERVICE_DELAYED_AUTO_START_INFO delayed;
\r
857 ZeroMemory(&delayed, sizeof(delayed));
\r
858 if (service->startup == NSSM_STARTUP_DELAYED) delayed.fDelayedAutostart = 1;
\r
859 else delayed.fDelayedAutostart = 0;
\r
860 /* Delayed startup isn't supported until Vista. */
\r
861 if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, &delayed)) {
\r
862 unsigned long error = GetLastError();
\r
863 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
\r
864 if (error != ERROR_INVALID_LEVEL) {
\r
865 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_DELAYED_AUTO_START_INFO_FAILED, service->name, error_string(error), 0);
\r
869 /* Don't mess with parameters which aren't ours. */
\r
870 if (! service->native) {
\r
871 /* Now we need to put the parameters into the registry */
\r
872 if (create_parameters(service, editing)) {
\r
873 print_message(stderr, NSSM_MESSAGE_CREATE_PARAMETERS_FAILED);
\r
877 set_service_recovery(service);
\r
883 /* Control a service. */
\r
884 int control_service(unsigned long control, int argc, TCHAR **argv) {
\r
885 if (argc < 1) return usage(1);
\r
886 TCHAR *service_name = argv[0];
\r
887 TCHAR canonical_name[SERVICE_NAME_LENGTH];
\r
889 SC_HANDLE services = open_service_manager();
\r
891 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
895 SC_HANDLE service_handle = open_service(services, service_name, canonical_name, _countof(canonical_name));
\r
896 if (! service_handle) {
\r
897 CloseServiceHandle(services);
\r
902 unsigned long error;
\r
903 SERVICE_STATUS service_status;
\r
904 if (control == 0) {
\r
905 ret = StartService(service_handle, (unsigned long) argc, (const TCHAR **) argv);
\r
906 error = GetLastError();
\r
907 CloseHandle(service_handle);
\r
908 CloseServiceHandle(services);
\r
911 _tprintf(_T("%s: %s"), canonical_name, error_string(error));
\r
915 _ftprintf(stderr, _T("%s: %s"), canonical_name, error_string(error));
\r
919 else if (control == SERVICE_CONTROL_INTERROGATE) {
\r
921 We could actually send an INTERROGATE control but that won't return
\r
922 any information if the service is stopped and we don't care about
\r
923 the extra details it might give us in any case. So we'll fake it.
\r
925 ret = QueryServiceStatus(service_handle, &service_status);
\r
926 error = GetLastError();
\r
929 switch (service_status.dwCurrentState) {
\r
930 case SERVICE_STOPPED: _tprintf(_T("SERVICE_STOPPED\n")); break;
\r
931 case SERVICE_START_PENDING: _tprintf(_T("SERVICE_START_PENDING\n")); break;
\r
932 case SERVICE_STOP_PENDING: _tprintf(_T("SERVICE_STOP_PENDING\n")); break;
\r
933 case SERVICE_RUNNING: _tprintf(_T("SERVICE_RUNNING\n")); break;
\r
934 case SERVICE_CONTINUE_PENDING: _tprintf(_T("SERVICE_CONTINUE_PENDING\n")); break;
\r
935 case SERVICE_PAUSE_PENDING: _tprintf(_T("SERVICE_PAUSE_PENDING\n")); break;
\r
936 case SERVICE_PAUSED: _tprintf(_T("SERVICE_PAUSED\n")); break;
\r
937 default: _tprintf(_T("?\n")); return 1;
\r
942 _ftprintf(stderr, _T("%s: %s\n"), canonical_name, error_string(error));
\r
947 ret = ControlService(service_handle, control, &service_status);
\r
948 error = GetLastError();
\r
949 CloseHandle(service_handle);
\r
950 CloseServiceHandle(services);
\r
953 _tprintf(_T("%s: %s"), canonical_name, error_string(error));
\r
957 _ftprintf(stderr, _T("%s: %s"), canonical_name, error_string(error));
\r
963 /* Remove the service */
\r
964 int remove_service(nssm_service_t *service) {
\r
965 if (! service) return 1;
\r
967 /* Open service manager */
\r
968 SC_HANDLE services = open_service_manager();
\r
970 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
974 /* Try to open the service */
\r
975 service->handle = open_service(services, service->name, service->name, _countof(service->name));
\r
976 if (! service->handle) {
\r
977 CloseServiceHandle(services);
\r
981 /* Get the canonical service name. We open it case insensitively. */
\r
982 unsigned long bufsize = _countof(service->displayname);
\r
983 GetServiceDisplayName(services, service->name, service->displayname, &bufsize);
\r
984 bufsize = _countof(service->name);
\r
985 GetServiceKeyName(services, service->displayname, service->name, &bufsize);
\r
987 /* Try to delete the service */
\r
988 if (! DeleteService(service->handle)) {
\r
989 print_message(stderr, NSSM_MESSAGE_DELETESERVICE_FAILED);
\r
990 CloseServiceHandle(services);
\r
995 CloseServiceHandle(services);
\r
997 print_message(stdout, NSSM_MESSAGE_SERVICE_REMOVED, service->name);
\r
1001 /* Service initialisation */
\r
1002 void WINAPI service_main(unsigned long argc, TCHAR **argv) {
\r
1003 nssm_service_t *service = alloc_nssm_service();
\r
1004 if (! service) return;
\r
1006 if (_sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]) < 0) {
\r
1007 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("service->name"), _T("service_main()"), 0);
\r
1011 /* We can use a condition variable in a critical section on Vista or later. */
\r
1012 if (imports.SleepConditionVariableCS && imports.WakeConditionVariable) use_critical_section = true;
\r
1013 else use_critical_section = false;
\r
1015 /* Initialise status */
\r
1016 ZeroMemory(&service->status, sizeof(service->status));
\r
1017 service->status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;
\r
1018 service->status.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE;
\r
1019 service->status.dwWin32ExitCode = NO_ERROR;
\r
1020 service->status.dwServiceSpecificExitCode = 0;
\r
1021 service->status.dwCheckPoint = 0;
\r
1022 service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;
\r
1024 /* Signal we AREN'T running the server */
\r
1025 service->process_handle = 0;
\r
1028 /* Register control handler */
\r
1029 service->status_handle = RegisterServiceCtrlHandlerEx(NSSM, service_control_handler, (void *) service);
\r
1030 if (! service->status_handle) {
\r
1031 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_REGISTERSERVICECTRLHANDER_FAILED, error_string(GetLastError()), 0);
\r
1035 log_service_control(service->name, 0, true);
\r
1037 service->status.dwCurrentState = SERVICE_START_PENDING;
\r
1038 service->status.dwWaitHint = service->throttle_delay + NSSM_WAITHINT_MARGIN;
\r
1039 SetServiceStatus(service->status_handle, &service->status);
\r
1042 /* Try to create the exit action parameters; we don't care if it fails */
\r
1043 create_exit_action(service->name, exit_action_strings[0], false);
\r
1045 SC_HANDLE services = open_service_manager();
\r
1047 service->handle = OpenService(services, service->name, SC_MANAGER_ALL_ACCESS);
\r
1048 set_service_recovery(service);
\r
1049 CloseServiceHandle(services);
\r
1053 /* Used for signalling a resume if the service pauses when throttled. */
\r
1054 if (use_critical_section) {
\r
1055 InitializeCriticalSection(&service->throttle_section);
\r
1056 service->throttle_section_initialised = true;
\r
1059 service->throttle_timer = CreateWaitableTimer(0, 1, 0);
\r
1060 if (! service->throttle_timer) {
\r
1061 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_CREATEWAITABLETIMER_FAILED, service->name, error_string(GetLastError()), 0);
\r
1065 monitor_service(service);
\r
1068 /* Make sure service recovery actions are taken where necessary */
\r
1069 void set_service_recovery(nssm_service_t *service) {
\r
1070 SERVICE_FAILURE_ACTIONS_FLAG flag;
\r
1071 ZeroMemory(&flag, sizeof(flag));
\r
1072 flag.fFailureActionsOnNonCrashFailures = true;
\r
1074 /* This functionality was added in Vista so the call may fail */
\r
1075 if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, &flag)) {
\r
1076 unsigned long error = GetLastError();
\r
1077 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
\r
1078 if (error != ERROR_INVALID_LEVEL) {
\r
1079 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_FAILURE_ACTIONS_FAILED, service->name, error_string(error), 0);
\r
1084 int monitor_service(nssm_service_t *service) {
\r
1085 /* Set service status to started */
\r
1086 int ret = start_service(service);
\r
1089 _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%d"), ret);
\r
1090 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_START_SERVICE_FAILED, service->exe, service->name, ret, 0);
\r
1093 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_STARTED_SERVICE, service->exe, service->flags, service->name, service->dir, 0);
\r
1095 /* Monitor service */
\r
1096 if (! RegisterWaitForSingleObject(&service->wait_handle, service->process_handle, end_service, (void *) service, INFINITE, WT_EXECUTEONLYONCE | WT_EXECUTELONGFUNCTION)) {
\r
1097 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_REGISTERWAITFORSINGLEOBJECT_FAILED, service->name, service->exe, error_string(GetLastError()), 0);
\r
1103 TCHAR *service_control_text(unsigned long control) {
\r
1104 switch (control) {
\r
1105 /* HACK: there is no SERVICE_CONTROL_START constant */
\r
1106 case 0: return _T("START");
\r
1107 case SERVICE_CONTROL_STOP: return _T("STOP");
\r
1108 case SERVICE_CONTROL_SHUTDOWN: return _T("SHUTDOWN");
\r
1109 case SERVICE_CONTROL_PAUSE: return _T("PAUSE");
\r
1110 case SERVICE_CONTROL_CONTINUE: return _T("CONTINUE");
\r
1111 case SERVICE_CONTROL_INTERROGATE: return _T("INTERROGATE");
\r
1112 default: return 0;
\r
1116 void log_service_control(TCHAR *service_name, unsigned long control, bool handled) {
\r
1117 TCHAR *text = service_control_text(control);
\r
1118 unsigned long event;
\r
1121 /* "0x" + 8 x hex + NULL */
\r
1122 text = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, 11 * sizeof(TCHAR));
\r
1124 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);
\r
1127 if (_sntprintf_s(text, 11, _TRUNCATE, _T("0x%08x"), control) < 0) {
\r
1128 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);
\r
1129 HeapFree(GetProcessHeap(), 0, text);
\r
1133 event = NSSM_EVENT_SERVICE_CONTROL_UNKNOWN;
\r
1135 else if (handled) event = NSSM_EVENT_SERVICE_CONTROL_HANDLED;
\r
1136 else event = NSSM_EVENT_SERVICE_CONTROL_NOT_HANDLED;
\r
1138 log_event(EVENTLOG_INFORMATION_TYPE, event, service_name, text, 0);
\r
1140 if (event == NSSM_EVENT_SERVICE_CONTROL_UNKNOWN) {
\r
1141 HeapFree(GetProcessHeap(), 0, text);
\r
1145 /* Service control handler */
\r
1146 unsigned long WINAPI service_control_handler(unsigned long control, unsigned long event, void *data, void *context) {
\r
1147 nssm_service_t *service = (nssm_service_t *) context;
\r
1149 switch (control) {
\r
1150 case SERVICE_CONTROL_INTERROGATE:
\r
1151 /* We always keep the service status up-to-date so this is a no-op. */
\r
1154 case SERVICE_CONTROL_SHUTDOWN:
\r
1155 case SERVICE_CONTROL_STOP:
\r
1156 log_service_control(service->name, control, true);
\r
1158 We MUST acknowledge the stop request promptly but we're committed to
\r
1159 waiting for the application to exit. Spawn a new thread to wait
\r
1160 while we acknowledge the request.
\r
1162 if (! CreateThread(NULL, 0, shutdown_service, context, 0, NULL)) {
\r
1163 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETHREAD_FAILED, error_string(GetLastError()), 0);
\r
1166 We couldn't create a thread to tidy up so we'll have to force the tidyup
\r
1167 to complete in time in this thread.
\r
1169 service->kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;
\r
1170 service->kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;
\r
1171 service->kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;
\r
1173 stop_service(service, 0, true, true);
\r
1177 case SERVICE_CONTROL_CONTINUE:
\r
1178 log_service_control(service->name, control, true);
\r
1179 service->throttle = 0;
\r
1180 if (use_critical_section) imports.WakeConditionVariable(&service->throttle_condition);
\r
1182 if (! service->throttle_timer) return ERROR_CALL_NOT_IMPLEMENTED;
\r
1183 ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));
\r
1184 SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);
\r
1186 service->status.dwCurrentState = SERVICE_CONTINUE_PENDING;
\r
1187 service->status.dwWaitHint = throttle_milliseconds(service->throttle) + NSSM_WAITHINT_MARGIN;
\r
1188 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESET_THROTTLE, service->name, 0);
\r
1189 SetServiceStatus(service->status_handle, &service->status);
\r
1192 case SERVICE_CONTROL_PAUSE:
\r
1194 We don't accept pause messages but it isn't possible to register
\r
1195 only for continue messages so we have to handle this case.
\r
1197 log_service_control(service->name, control, false);
\r
1198 return ERROR_CALL_NOT_IMPLEMENTED;
\r
1201 /* Unknown control */
\r
1202 log_service_control(service->name, control, false);
\r
1203 return ERROR_CALL_NOT_IMPLEMENTED;
\r
1206 /* Start the service */
\r
1207 int start_service(nssm_service_t *service) {
\r
1208 service->stopping = false;
\r
1209 service->allow_restart = true;
\r
1211 if (service->process_handle) return 0;
\r
1213 /* Allocate a STARTUPINFO structure for a new process */
\r
1215 ZeroMemory(&si, sizeof(si));
\r
1216 si.cb = sizeof(si);
\r
1218 /* Allocate a PROCESSINFO structure for the process */
\r
1219 PROCESS_INFORMATION pi;
\r
1220 ZeroMemory(&pi, sizeof(pi));
\r
1222 /* Get startup parameters */
\r
1223 int ret = get_parameters(service, &si);
\r
1225 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_PARAMETERS_FAILED, service->name, 0);
\r
1226 return stop_service(service, 2, true, true);
\r
1229 /* Launch executable with arguments */
\r
1230 TCHAR cmd[CMD_LENGTH];
\r
1231 if (_sntprintf_s(cmd, _countof(cmd), _TRUNCATE, _T("\"%s\" %s"), service->exe, service->flags) < 0) {
\r
1232 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("command line"), _T("start_service"), 0);
\r
1233 close_output_handles(&si);
\r
1234 return stop_service(service, 2, true, true);
\r
1237 throttle_restart(service);
\r
1239 bool inherit_handles = false;
\r
1240 if (si.dwFlags & STARTF_USESTDHANDLES) inherit_handles = true;
\r
1241 unsigned long flags = 0;
\r
1243 flags |= CREATE_UNICODE_ENVIRONMENT;
\r
1245 if (! CreateProcess(0, cmd, 0, 0, inherit_handles, flags, service->env, service->dir, &si, &pi)) {
\r
1246 unsigned long exitcode = 3;
\r
1247 unsigned long error = GetLastError();
\r
1248 if (error == ERROR_INVALID_PARAMETER && service->env) {
\r
1249 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED_INVALID_ENVIRONMENT, service->name, service->exe, NSSM_REG_ENV, 0);
\r
1250 if (test_environment(service->env)) exitcode = 4;
\r
1252 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service->name, service->exe, error_string(error), 0);
\r
1253 close_output_handles(&si);
\r
1254 return stop_service(service, exitcode, true, true);
\r
1256 service->process_handle = pi.hProcess;
\r
1257 service->pid = pi.dwProcessId;
\r
1259 if (get_process_creation_time(service->process_handle, &service->creation_time)) ZeroMemory(&service->creation_time, sizeof(service->creation_time));
\r
1261 close_output_handles(&si);
\r
1264 Wait for a clean startup before changing the service status to RUNNING
\r
1265 but be mindful of the fact that we are blocking the service control manager
\r
1266 so abandon the wait before too much time has elapsed.
\r
1268 unsigned long delay = service->throttle_delay;
\r
1269 if (delay > NSSM_SERVICE_STATUS_DEADLINE) {
\r
1270 TCHAR delay_milliseconds[16];
\r
1271 _sntprintf_s(delay_milliseconds, _countof(delay_milliseconds), _TRUNCATE, _T("%lu"), delay);
\r
1272 TCHAR deadline_milliseconds[16];
\r
1273 _sntprintf_s(deadline_milliseconds, _countof(deadline_milliseconds), _TRUNCATE, _T("%lu"), NSSM_SERVICE_STATUS_DEADLINE);
\r
1274 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_STARTUP_DELAY_TOO_LONG, service->name, delay_milliseconds, NSSM, deadline_milliseconds, 0);
\r
1275 delay = NSSM_SERVICE_STATUS_DEADLINE;
\r
1277 unsigned long deadline = WaitForSingleObject(service->process_handle, delay);
\r
1279 /* Signal successful start */
\r
1280 service->status.dwCurrentState = SERVICE_RUNNING;
\r
1281 SetServiceStatus(service->status_handle, &service->status);
\r
1283 /* Continue waiting for a clean startup. */
\r
1284 if (deadline == WAIT_TIMEOUT) {
\r
1285 if (service->throttle_delay > delay) {
\r
1286 if (WaitForSingleObject(service->process_handle, service->throttle_delay - delay) == WAIT_TIMEOUT) service->throttle = 0;
\r
1288 else service->throttle = 0;
\r
1294 /* Stop the service */
\r
1295 int stop_service(nssm_service_t *service, unsigned long exitcode, bool graceful, bool default_action) {
\r
1296 service->allow_restart = false;
\r
1297 if (service->wait_handle) {
\r
1298 UnregisterWait(service->wait_handle);
\r
1299 service->wait_handle = 0;
\r
1302 if (default_action && ! exitcode && ! graceful) {
\r
1303 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
1307 /* Signal we are stopping */
\r
1309 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
1310 service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;
\r
1311 SetServiceStatus(service->status_handle, &service->status);
\r
1314 /* Nothing to do if service isn't running */
\r
1315 if (service->pid) {
\r
1316 /* Shut down service */
\r
1317 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_TERMINATEPROCESS, service->name, service->exe, 0);
\r
1318 kill_process(service, service->process_handle, service->pid, 0);
\r
1320 else log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_PROCESS_ALREADY_STOPPED, service->name, service->exe, 0);
\r
1322 end_service((void *) service, true);
\r
1324 /* Signal we stopped */
\r
1326 service->status.dwCurrentState = SERVICE_STOPPED;
\r
1328 service->status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
\r
1329 service->status.dwServiceSpecificExitCode = exitcode;
\r
1332 service->status.dwWin32ExitCode = NO_ERROR;
\r
1333 service->status.dwServiceSpecificExitCode = 0;
\r
1335 SetServiceStatus(service->status_handle, &service->status);
\r
1341 /* Callback function triggered when the server exits */
\r
1342 void CALLBACK end_service(void *arg, unsigned char why) {
\r
1343 nssm_service_t *service = (nssm_service_t *) arg;
\r
1345 if (service->stopping) return;
\r
1347 service->stopping = true;
\r
1349 /* Check exit code */
\r
1350 unsigned long exitcode = 0;
\r
1352 if (service->process_handle) {
\r
1353 GetExitCodeProcess(service->process_handle, &exitcode);
\r
1354 if (exitcode == STILL_ACTIVE || get_process_exit_time(service->process_handle, &service->exit_time)) GetSystemTimeAsFileTime(&service->exit_time);
\r
1355 CloseHandle(service->process_handle);
\r
1357 else GetSystemTimeAsFileTime(&service->exit_time);
\r
1359 service->process_handle = 0;
\r
1362 Log that the service ended BEFORE logging about killing the process
\r
1363 tree. See below for the possible values of the why argument.
\r
1366 _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), exitcode);
\r
1367 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ENDED_SERVICE, service->exe, service->name, code, 0);
\r
1371 if (exitcode == STILL_ACTIVE) exitcode = 0;
\r
1372 if (service->pid) kill_process_tree(service, service->pid, exitcode, service->pid);
\r
1376 The why argument is true if our wait timed out or false otherwise.
\r
1377 Our wait is infinite so why will never be true when called by the system.
\r
1378 If it is indeed true, assume we were called from stop_service() because
\r
1379 this is a controlled shutdown, and don't take any restart action.
\r
1382 if (! service->allow_restart) return;
\r
1384 /* What action should we take? */
\r
1385 int action = NSSM_EXIT_RESTART;
\r
1386 TCHAR action_string[ACTION_LEN];
\r
1387 bool default_action;
\r
1388 if (! get_exit_action(service->name, &exitcode, action_string, &default_action)) {
\r
1389 for (int i = 0; exit_action_strings[i]; i++) {
\r
1390 if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
1398 /* Try to restart the service or return failure code to service manager */
\r
1399 case NSSM_EXIT_RESTART:
\r
1400 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_RESTART, service->name, code, exit_action_strings[action], service->exe, 0);
\r
1401 while (monitor_service(service)) {
\r
1402 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_RESTART_SERVICE_FAILED, service->exe, service->name, 0);
\r
1407 /* Do nothing, just like srvany would */
\r
1408 case NSSM_EXIT_IGNORE:
\r
1409 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_IGNORE, service->name, code, exit_action_strings[action], service->exe, 0);
\r
1413 /* Tell the service manager we are finished */
\r
1414 case NSSM_EXIT_REALLY:
\r
1415 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_REALLY, service->name, code, exit_action_strings[action], 0);
\r
1416 stop_service(service, exitcode, true, default_action);
\r
1419 /* Fake a crash so pre-Vista service managers will run recovery actions. */
\r
1420 case NSSM_EXIT_UNCLEAN:
\r
1421 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_UNCLEAN, service->name, code, exit_action_strings[action], 0);
\r
1422 stop_service(service, exitcode, false, default_action);
\r
1429 void throttle_restart(nssm_service_t *service) {
\r
1430 /* This can't be a restart if the service is already running. */
\r
1431 if (! service->throttle++) return;
\r
1433 int ms = throttle_milliseconds(service->throttle);
\r
1435 if (service->throttle > 7) service->throttle = 8;
\r
1437 TCHAR threshold[8], milliseconds[8];
\r
1438 _sntprintf_s(threshold, _countof(threshold), _TRUNCATE, _T("%lu"), service->throttle_delay);
\r
1439 _sntprintf_s(milliseconds, _countof(milliseconds), _TRUNCATE, _T("%lu"), ms);
\r
1440 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_THROTTLED, service->name, threshold, milliseconds, 0);
\r
1442 if (use_critical_section) EnterCriticalSection(&service->throttle_section);
\r
1443 else if (service->throttle_timer) {
\r
1444 ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));
\r
1445 service->throttle_duetime.QuadPart = 0 - (ms * 10000LL);
\r
1446 SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);
\r
1449 service->status.dwCurrentState = SERVICE_PAUSED;
\r
1450 SetServiceStatus(service->status_handle, &service->status);
\r
1452 if (use_critical_section) {
\r
1453 imports.SleepConditionVariableCS(&service->throttle_condition, &service->throttle_section, ms);
\r
1454 LeaveCriticalSection(&service->throttle_section);
\r
1457 if (service->throttle_timer) WaitForSingleObject(service->throttle_timer, INFINITE);
\r
1463 When responding to a stop (or any other) request we need to set dwWaitHint to
\r
1464 the number of milliseconds we expect the operation to take, and optionally
\r
1465 increase dwCheckPoint. If dwWaitHint milliseconds elapses without the
\r
1466 operation completing or dwCheckPoint increasing, the system will consider the
\r
1467 service to be hung.
\r
1469 However the system will consider the service to be hung after 30000
\r
1470 milliseconds regardless of the value of dwWaitHint if dwCheckPoint has not
\r
1471 changed. Therefore if we want to wait longer than that we must periodically
\r
1472 increase dwCheckPoint.
\r
1474 Furthermore, it will consider the service to be hung after 60000 milliseconds
\r
1475 regardless of the value of dwCheckPoint unless dwWaitHint is increased every
\r
1476 time dwCheckPoint is also increased.
\r
1478 Our strategy then is to retrieve the initial dwWaitHint and wait for
\r
1479 NSSM_SERVICE_STATUS_DEADLINE milliseconds. If the process is still running
\r
1480 and we haven't finished waiting we increment dwCheckPoint and add whichever is
\r
1481 smaller of NSSM_SERVICE_STATUS_DEADLINE or the remaining timeout to
\r
1484 Only doing both these things will prevent the system from killing the service.
\r
1486 Returns: 1 if the wait timed out.
\r
1487 0 if the wait completed.
\r
1490 int await_shutdown(nssm_service_t *service, TCHAR *function_name, unsigned long timeout) {
\r
1491 unsigned long interval;
\r
1492 unsigned long waithint;
\r
1493 unsigned long ret;
\r
1494 unsigned long waited;
\r
1495 TCHAR interval_milliseconds[16];
\r
1496 TCHAR timeout_milliseconds[16];
\r
1497 TCHAR waited_milliseconds[16];
\r
1498 TCHAR *function = function_name;
\r
1500 /* Add brackets to function name. */
\r
1501 size_t funclen = _tcslen(function_name) + 3;
\r
1502 TCHAR *func = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, funclen * sizeof(TCHAR));
\r
1504 if (_sntprintf_s(func, funclen, _TRUNCATE, _T("%s()"), function_name) > -1) function = func;
\r
1507 _sntprintf_s(timeout_milliseconds, _countof(timeout_milliseconds), _TRUNCATE, _T("%lu"), timeout);
\r
1509 waithint = service->status.dwWaitHint;
\r
1511 while (waited < timeout) {
\r
1512 interval = timeout - waited;
\r
1513 if (interval > NSSM_SERVICE_STATUS_DEADLINE) interval = NSSM_SERVICE_STATUS_DEADLINE;
\r
1515 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
1516 service->status.dwWaitHint += interval;
\r
1517 service->status.dwCheckPoint++;
\r
1518 SetServiceStatus(service->status_handle, &service->status);
\r
1521 _sntprintf_s(waited_milliseconds, _countof(waited_milliseconds), _TRUNCATE, _T("%lu"), waited);
\r
1522 _sntprintf_s(interval_milliseconds, _countof(interval_milliseconds), _TRUNCATE, _T("%lu"), interval);
\r
1523 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_AWAITING_SHUTDOWN, function, service->name, waited_milliseconds, interval_milliseconds, timeout_milliseconds, 0);
\r
1526 switch (WaitForSingleObject(service->process_handle, interval)) {
\r
1527 case WAIT_OBJECT_0:
\r
1531 case WAIT_TIMEOUT:
\r
1540 waited += interval;
\r
1544 if (func) HeapFree(GetProcessHeap(), 0, func);
\r