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
910 if (error == ERROR_IO_PENDING) {
\r
912 Older versions of Windows return immediately with ERROR_IO_PENDING
\r
913 indicate that the operation is still in progress. Newer versions
\r
914 will return it if there really is a delay. As far as we're
\r
915 concerned the operation is a success. We don't claim to offer a
\r
916 fully-feature service control method; it's just a quick 'n' dirty
\r
919 In the future we may identify and handle this situation properly.
\r
922 error = ERROR_SUCCESS;
\r
926 _tprintf(_T("%s: %s"), canonical_name, error_string(error));
\r
930 _ftprintf(stderr, _T("%s: %s"), canonical_name, error_string(error));
\r
934 else if (control == SERVICE_CONTROL_INTERROGATE) {
\r
936 We could actually send an INTERROGATE control but that won't return
\r
937 any information if the service is stopped and we don't care about
\r
938 the extra details it might give us in any case. So we'll fake it.
\r
940 ret = QueryServiceStatus(service_handle, &service_status);
\r
941 error = GetLastError();
\r
944 switch (service_status.dwCurrentState) {
\r
945 case SERVICE_STOPPED: _tprintf(_T("SERVICE_STOPPED\n")); break;
\r
946 case SERVICE_START_PENDING: _tprintf(_T("SERVICE_START_PENDING\n")); break;
\r
947 case SERVICE_STOP_PENDING: _tprintf(_T("SERVICE_STOP_PENDING\n")); break;
\r
948 case SERVICE_RUNNING: _tprintf(_T("SERVICE_RUNNING\n")); break;
\r
949 case SERVICE_CONTINUE_PENDING: _tprintf(_T("SERVICE_CONTINUE_PENDING\n")); break;
\r
950 case SERVICE_PAUSE_PENDING: _tprintf(_T("SERVICE_PAUSE_PENDING\n")); break;
\r
951 case SERVICE_PAUSED: _tprintf(_T("SERVICE_PAUSED\n")); break;
\r
952 default: _tprintf(_T("?\n")); return 1;
\r
957 _ftprintf(stderr, _T("%s: %s\n"), canonical_name, error_string(error));
\r
962 ret = ControlService(service_handle, control, &service_status);
\r
963 error = GetLastError();
\r
964 CloseHandle(service_handle);
\r
965 CloseServiceHandle(services);
\r
967 if (error == ERROR_IO_PENDING) {
\r
969 error = ERROR_SUCCESS;
\r
973 _tprintf(_T("%s: %s"), canonical_name, error_string(error));
\r
977 _ftprintf(stderr, _T("%s: %s"), canonical_name, error_string(error));
\r
983 /* Remove the service */
\r
984 int remove_service(nssm_service_t *service) {
\r
985 if (! service) return 1;
\r
987 /* Open service manager */
\r
988 SC_HANDLE services = open_service_manager();
\r
990 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
994 /* Try to open the service */
\r
995 service->handle = open_service(services, service->name, service->name, _countof(service->name));
\r
996 if (! service->handle) {
\r
997 CloseServiceHandle(services);
\r
1001 /* Get the canonical service name. We open it case insensitively. */
\r
1002 unsigned long bufsize = _countof(service->displayname);
\r
1003 GetServiceDisplayName(services, service->name, service->displayname, &bufsize);
\r
1004 bufsize = _countof(service->name);
\r
1005 GetServiceKeyName(services, service->displayname, service->name, &bufsize);
\r
1007 /* Try to delete the service */
\r
1008 if (! DeleteService(service->handle)) {
\r
1009 print_message(stderr, NSSM_MESSAGE_DELETESERVICE_FAILED);
\r
1010 CloseServiceHandle(services);
\r
1015 CloseServiceHandle(services);
\r
1017 print_message(stdout, NSSM_MESSAGE_SERVICE_REMOVED, service->name);
\r
1021 /* Service initialisation */
\r
1022 void WINAPI service_main(unsigned long argc, TCHAR **argv) {
\r
1023 nssm_service_t *service = alloc_nssm_service();
\r
1024 if (! service) return;
\r
1026 if (_sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]) < 0) {
\r
1027 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("service->name"), _T("service_main()"), 0);
\r
1031 /* We can use a condition variable in a critical section on Vista or later. */
\r
1032 if (imports.SleepConditionVariableCS && imports.WakeConditionVariable) use_critical_section = true;
\r
1033 else use_critical_section = false;
\r
1035 /* Initialise status */
\r
1036 ZeroMemory(&service->status, sizeof(service->status));
\r
1037 service->status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;
\r
1038 service->status.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE;
\r
1039 service->status.dwWin32ExitCode = NO_ERROR;
\r
1040 service->status.dwServiceSpecificExitCode = 0;
\r
1041 service->status.dwCheckPoint = 0;
\r
1042 service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;
\r
1044 /* Signal we AREN'T running the server */
\r
1045 service->process_handle = 0;
\r
1048 /* Register control handler */
\r
1049 service->status_handle = RegisterServiceCtrlHandlerEx(NSSM, service_control_handler, (void *) service);
\r
1050 if (! service->status_handle) {
\r
1051 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_REGISTERSERVICECTRLHANDER_FAILED, error_string(GetLastError()), 0);
\r
1055 log_service_control(service->name, 0, true);
\r
1057 service->status.dwCurrentState = SERVICE_START_PENDING;
\r
1058 service->status.dwWaitHint = service->throttle_delay + NSSM_WAITHINT_MARGIN;
\r
1059 SetServiceStatus(service->status_handle, &service->status);
\r
1062 /* Try to create the exit action parameters; we don't care if it fails */
\r
1063 create_exit_action(service->name, exit_action_strings[0], false);
\r
1065 SC_HANDLE services = open_service_manager();
\r
1067 service->handle = OpenService(services, service->name, SC_MANAGER_ALL_ACCESS);
\r
1068 set_service_recovery(service);
\r
1069 CloseServiceHandle(services);
\r
1073 /* Used for signalling a resume if the service pauses when throttled. */
\r
1074 if (use_critical_section) {
\r
1075 InitializeCriticalSection(&service->throttle_section);
\r
1076 service->throttle_section_initialised = true;
\r
1079 service->throttle_timer = CreateWaitableTimer(0, 1, 0);
\r
1080 if (! service->throttle_timer) {
\r
1081 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_CREATEWAITABLETIMER_FAILED, service->name, error_string(GetLastError()), 0);
\r
1085 monitor_service(service);
\r
1088 /* Make sure service recovery actions are taken where necessary */
\r
1089 void set_service_recovery(nssm_service_t *service) {
\r
1090 SERVICE_FAILURE_ACTIONS_FLAG flag;
\r
1091 ZeroMemory(&flag, sizeof(flag));
\r
1092 flag.fFailureActionsOnNonCrashFailures = true;
\r
1094 /* This functionality was added in Vista so the call may fail */
\r
1095 if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, &flag)) {
\r
1096 unsigned long error = GetLastError();
\r
1097 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
\r
1098 if (error != ERROR_INVALID_LEVEL) {
\r
1099 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_FAILURE_ACTIONS_FAILED, service->name, error_string(error), 0);
\r
1104 int monitor_service(nssm_service_t *service) {
\r
1105 /* Set service status to started */
\r
1106 int ret = start_service(service);
\r
1109 _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%d"), ret);
\r
1110 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_START_SERVICE_FAILED, service->exe, service->name, ret, 0);
\r
1113 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_STARTED_SERVICE, service->exe, service->flags, service->name, service->dir, 0);
\r
1115 /* Monitor service */
\r
1116 if (! RegisterWaitForSingleObject(&service->wait_handle, service->process_handle, end_service, (void *) service, INFINITE, WT_EXECUTEONLYONCE | WT_EXECUTELONGFUNCTION)) {
\r
1117 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_REGISTERWAITFORSINGLEOBJECT_FAILED, service->name, service->exe, error_string(GetLastError()), 0);
\r
1123 TCHAR *service_control_text(unsigned long control) {
\r
1124 switch (control) {
\r
1125 /* HACK: there is no SERVICE_CONTROL_START constant */
\r
1126 case 0: return _T("START");
\r
1127 case SERVICE_CONTROL_STOP: return _T("STOP");
\r
1128 case SERVICE_CONTROL_SHUTDOWN: return _T("SHUTDOWN");
\r
1129 case SERVICE_CONTROL_PAUSE: return _T("PAUSE");
\r
1130 case SERVICE_CONTROL_CONTINUE: return _T("CONTINUE");
\r
1131 case SERVICE_CONTROL_INTERROGATE: return _T("INTERROGATE");
\r
1132 default: return 0;
\r
1136 void log_service_control(TCHAR *service_name, unsigned long control, bool handled) {
\r
1137 TCHAR *text = service_control_text(control);
\r
1138 unsigned long event;
\r
1141 /* "0x" + 8 x hex + NULL */
\r
1142 text = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, 11 * sizeof(TCHAR));
\r
1144 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);
\r
1147 if (_sntprintf_s(text, 11, _TRUNCATE, _T("0x%08x"), control) < 0) {
\r
1148 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);
\r
1149 HeapFree(GetProcessHeap(), 0, text);
\r
1153 event = NSSM_EVENT_SERVICE_CONTROL_UNKNOWN;
\r
1155 else if (handled) event = NSSM_EVENT_SERVICE_CONTROL_HANDLED;
\r
1156 else event = NSSM_EVENT_SERVICE_CONTROL_NOT_HANDLED;
\r
1158 log_event(EVENTLOG_INFORMATION_TYPE, event, service_name, text, 0);
\r
1160 if (event == NSSM_EVENT_SERVICE_CONTROL_UNKNOWN) {
\r
1161 HeapFree(GetProcessHeap(), 0, text);
\r
1165 /* Service control handler */
\r
1166 unsigned long WINAPI service_control_handler(unsigned long control, unsigned long event, void *data, void *context) {
\r
1167 nssm_service_t *service = (nssm_service_t *) context;
\r
1169 switch (control) {
\r
1170 case SERVICE_CONTROL_INTERROGATE:
\r
1171 /* We always keep the service status up-to-date so this is a no-op. */
\r
1174 case SERVICE_CONTROL_SHUTDOWN:
\r
1175 case SERVICE_CONTROL_STOP:
\r
1176 log_service_control(service->name, control, true);
\r
1178 We MUST acknowledge the stop request promptly but we're committed to
\r
1179 waiting for the application to exit. Spawn a new thread to wait
\r
1180 while we acknowledge the request.
\r
1182 if (! CreateThread(NULL, 0, shutdown_service, context, 0, NULL)) {
\r
1183 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETHREAD_FAILED, error_string(GetLastError()), 0);
\r
1186 We couldn't create a thread to tidy up so we'll have to force the tidyup
\r
1187 to complete in time in this thread.
\r
1189 service->kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;
\r
1190 service->kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;
\r
1191 service->kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;
\r
1193 stop_service(service, 0, true, true);
\r
1197 case SERVICE_CONTROL_CONTINUE:
\r
1198 log_service_control(service->name, control, true);
\r
1199 service->throttle = 0;
\r
1200 if (use_critical_section) imports.WakeConditionVariable(&service->throttle_condition);
\r
1202 if (! service->throttle_timer) return ERROR_CALL_NOT_IMPLEMENTED;
\r
1203 ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));
\r
1204 SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);
\r
1206 service->status.dwCurrentState = SERVICE_CONTINUE_PENDING;
\r
1207 service->status.dwWaitHint = throttle_milliseconds(service->throttle) + NSSM_WAITHINT_MARGIN;
\r
1208 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESET_THROTTLE, service->name, 0);
\r
1209 SetServiceStatus(service->status_handle, &service->status);
\r
1212 case SERVICE_CONTROL_PAUSE:
\r
1214 We don't accept pause messages but it isn't possible to register
\r
1215 only for continue messages so we have to handle this case.
\r
1217 log_service_control(service->name, control, false);
\r
1218 return ERROR_CALL_NOT_IMPLEMENTED;
\r
1221 /* Unknown control */
\r
1222 log_service_control(service->name, control, false);
\r
1223 return ERROR_CALL_NOT_IMPLEMENTED;
\r
1226 /* Start the service */
\r
1227 int start_service(nssm_service_t *service) {
\r
1228 service->stopping = false;
\r
1229 service->allow_restart = true;
\r
1231 if (service->process_handle) return 0;
\r
1233 /* Allocate a STARTUPINFO structure for a new process */
\r
1235 ZeroMemory(&si, sizeof(si));
\r
1236 si.cb = sizeof(si);
\r
1238 /* Allocate a PROCESSINFO structure for the process */
\r
1239 PROCESS_INFORMATION pi;
\r
1240 ZeroMemory(&pi, sizeof(pi));
\r
1242 /* Get startup parameters */
\r
1243 int ret = get_parameters(service, &si);
\r
1245 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_PARAMETERS_FAILED, service->name, 0);
\r
1246 return stop_service(service, 2, true, true);
\r
1249 /* Launch executable with arguments */
\r
1250 TCHAR cmd[CMD_LENGTH];
\r
1251 if (_sntprintf_s(cmd, _countof(cmd), _TRUNCATE, _T("\"%s\" %s"), service->exe, service->flags) < 0) {
\r
1252 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("command line"), _T("start_service"), 0);
\r
1253 close_output_handles(&si);
\r
1254 return stop_service(service, 2, true, true);
\r
1257 throttle_restart(service);
\r
1259 bool inherit_handles = false;
\r
1260 if (si.dwFlags & STARTF_USESTDHANDLES) inherit_handles = true;
\r
1261 unsigned long flags = 0;
\r
1263 flags |= CREATE_UNICODE_ENVIRONMENT;
\r
1265 if (! CreateProcess(0, cmd, 0, 0, inherit_handles, flags, service->env, service->dir, &si, &pi)) {
\r
1266 unsigned long exitcode = 3;
\r
1267 unsigned long error = GetLastError();
\r
1268 if (error == ERROR_INVALID_PARAMETER && service->env) {
\r
1269 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED_INVALID_ENVIRONMENT, service->name, service->exe, NSSM_REG_ENV, 0);
\r
1270 if (test_environment(service->env)) exitcode = 4;
\r
1272 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service->name, service->exe, error_string(error), 0);
\r
1273 close_output_handles(&si);
\r
1274 return stop_service(service, exitcode, true, true);
\r
1276 service->process_handle = pi.hProcess;
\r
1277 service->pid = pi.dwProcessId;
\r
1279 if (get_process_creation_time(service->process_handle, &service->creation_time)) ZeroMemory(&service->creation_time, sizeof(service->creation_time));
\r
1281 close_output_handles(&si);
\r
1284 Wait for a clean startup before changing the service status to RUNNING
\r
1285 but be mindful of the fact that we are blocking the service control manager
\r
1286 so abandon the wait before too much time has elapsed.
\r
1288 unsigned long delay = service->throttle_delay;
\r
1289 if (delay > NSSM_SERVICE_STATUS_DEADLINE) {
\r
1290 TCHAR delay_milliseconds[16];
\r
1291 _sntprintf_s(delay_milliseconds, _countof(delay_milliseconds), _TRUNCATE, _T("%lu"), delay);
\r
1292 TCHAR deadline_milliseconds[16];
\r
1293 _sntprintf_s(deadline_milliseconds, _countof(deadline_milliseconds), _TRUNCATE, _T("%lu"), NSSM_SERVICE_STATUS_DEADLINE);
\r
1294 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_STARTUP_DELAY_TOO_LONG, service->name, delay_milliseconds, NSSM, deadline_milliseconds, 0);
\r
1295 delay = NSSM_SERVICE_STATUS_DEADLINE;
\r
1297 unsigned long deadline = WaitForSingleObject(service->process_handle, delay);
\r
1299 /* Signal successful start */
\r
1300 service->status.dwCurrentState = SERVICE_RUNNING;
\r
1301 SetServiceStatus(service->status_handle, &service->status);
\r
1303 /* Continue waiting for a clean startup. */
\r
1304 if (deadline == WAIT_TIMEOUT) {
\r
1305 if (service->throttle_delay > delay) {
\r
1306 if (WaitForSingleObject(service->process_handle, service->throttle_delay - delay) == WAIT_TIMEOUT) service->throttle = 0;
\r
1308 else service->throttle = 0;
\r
1314 /* Stop the service */
\r
1315 int stop_service(nssm_service_t *service, unsigned long exitcode, bool graceful, bool default_action) {
\r
1316 service->allow_restart = false;
\r
1317 if (service->wait_handle) {
\r
1318 UnregisterWait(service->wait_handle);
\r
1319 service->wait_handle = 0;
\r
1322 if (default_action && ! exitcode && ! graceful) {
\r
1323 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
1327 /* Signal we are stopping */
\r
1329 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
1330 service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;
\r
1331 SetServiceStatus(service->status_handle, &service->status);
\r
1334 /* Nothing to do if service isn't running */
\r
1335 if (service->pid) {
\r
1336 /* Shut down service */
\r
1337 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_TERMINATEPROCESS, service->name, service->exe, 0);
\r
1338 kill_process(service, service->process_handle, service->pid, 0);
\r
1340 else log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_PROCESS_ALREADY_STOPPED, service->name, service->exe, 0);
\r
1342 end_service((void *) service, true);
\r
1344 /* Signal we stopped */
\r
1346 service->status.dwCurrentState = SERVICE_STOPPED;
\r
1348 service->status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
\r
1349 service->status.dwServiceSpecificExitCode = exitcode;
\r
1352 service->status.dwWin32ExitCode = NO_ERROR;
\r
1353 service->status.dwServiceSpecificExitCode = 0;
\r
1355 SetServiceStatus(service->status_handle, &service->status);
\r
1361 /* Callback function triggered when the server exits */
\r
1362 void CALLBACK end_service(void *arg, unsigned char why) {
\r
1363 nssm_service_t *service = (nssm_service_t *) arg;
\r
1365 if (service->stopping) return;
\r
1367 service->stopping = true;
\r
1369 /* Check exit code */
\r
1370 unsigned long exitcode = 0;
\r
1372 if (service->process_handle) {
\r
1373 GetExitCodeProcess(service->process_handle, &exitcode);
\r
1374 if (exitcode == STILL_ACTIVE || get_process_exit_time(service->process_handle, &service->exit_time)) GetSystemTimeAsFileTime(&service->exit_time);
\r
1375 CloseHandle(service->process_handle);
\r
1377 else GetSystemTimeAsFileTime(&service->exit_time);
\r
1379 service->process_handle = 0;
\r
1382 Log that the service ended BEFORE logging about killing the process
\r
1383 tree. See below for the possible values of the why argument.
\r
1386 _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), exitcode);
\r
1387 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ENDED_SERVICE, service->exe, service->name, code, 0);
\r
1391 if (exitcode == STILL_ACTIVE) exitcode = 0;
\r
1392 if (service->pid) kill_process_tree(service, service->pid, exitcode, service->pid);
\r
1396 The why argument is true if our wait timed out or false otherwise.
\r
1397 Our wait is infinite so why will never be true when called by the system.
\r
1398 If it is indeed true, assume we were called from stop_service() because
\r
1399 this is a controlled shutdown, and don't take any restart action.
\r
1402 if (! service->allow_restart) return;
\r
1404 /* What action should we take? */
\r
1405 int action = NSSM_EXIT_RESTART;
\r
1406 TCHAR action_string[ACTION_LEN];
\r
1407 bool default_action;
\r
1408 if (! get_exit_action(service->name, &exitcode, action_string, &default_action)) {
\r
1409 for (int i = 0; exit_action_strings[i]; i++) {
\r
1410 if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
1418 /* Try to restart the service or return failure code to service manager */
\r
1419 case NSSM_EXIT_RESTART:
\r
1420 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_RESTART, service->name, code, exit_action_strings[action], service->exe, 0);
\r
1421 while (monitor_service(service)) {
\r
1422 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_RESTART_SERVICE_FAILED, service->exe, service->name, 0);
\r
1427 /* Do nothing, just like srvany would */
\r
1428 case NSSM_EXIT_IGNORE:
\r
1429 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_IGNORE, service->name, code, exit_action_strings[action], service->exe, 0);
\r
1433 /* Tell the service manager we are finished */
\r
1434 case NSSM_EXIT_REALLY:
\r
1435 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_REALLY, service->name, code, exit_action_strings[action], 0);
\r
1436 stop_service(service, exitcode, true, default_action);
\r
1439 /* Fake a crash so pre-Vista service managers will run recovery actions. */
\r
1440 case NSSM_EXIT_UNCLEAN:
\r
1441 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_UNCLEAN, service->name, code, exit_action_strings[action], 0);
\r
1442 stop_service(service, exitcode, false, default_action);
\r
1449 void throttle_restart(nssm_service_t *service) {
\r
1450 /* This can't be a restart if the service is already running. */
\r
1451 if (! service->throttle++) return;
\r
1453 int ms = throttle_milliseconds(service->throttle);
\r
1455 if (service->throttle > 7) service->throttle = 8;
\r
1457 TCHAR threshold[8], milliseconds[8];
\r
1458 _sntprintf_s(threshold, _countof(threshold), _TRUNCATE, _T("%lu"), service->throttle_delay);
\r
1459 _sntprintf_s(milliseconds, _countof(milliseconds), _TRUNCATE, _T("%lu"), ms);
\r
1460 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_THROTTLED, service->name, threshold, milliseconds, 0);
\r
1462 if (use_critical_section) EnterCriticalSection(&service->throttle_section);
\r
1463 else if (service->throttle_timer) {
\r
1464 ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));
\r
1465 service->throttle_duetime.QuadPart = 0 - (ms * 10000LL);
\r
1466 SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);
\r
1469 service->status.dwCurrentState = SERVICE_PAUSED;
\r
1470 SetServiceStatus(service->status_handle, &service->status);
\r
1472 if (use_critical_section) {
\r
1473 imports.SleepConditionVariableCS(&service->throttle_condition, &service->throttle_section, ms);
\r
1474 LeaveCriticalSection(&service->throttle_section);
\r
1477 if (service->throttle_timer) WaitForSingleObject(service->throttle_timer, INFINITE);
\r
1483 When responding to a stop (or any other) request we need to set dwWaitHint to
\r
1484 the number of milliseconds we expect the operation to take, and optionally
\r
1485 increase dwCheckPoint. If dwWaitHint milliseconds elapses without the
\r
1486 operation completing or dwCheckPoint increasing, the system will consider the
\r
1487 service to be hung.
\r
1489 However the system will consider the service to be hung after 30000
\r
1490 milliseconds regardless of the value of dwWaitHint if dwCheckPoint has not
\r
1491 changed. Therefore if we want to wait longer than that we must periodically
\r
1492 increase dwCheckPoint.
\r
1494 Furthermore, it will consider the service to be hung after 60000 milliseconds
\r
1495 regardless of the value of dwCheckPoint unless dwWaitHint is increased every
\r
1496 time dwCheckPoint is also increased.
\r
1498 Our strategy then is to retrieve the initial dwWaitHint and wait for
\r
1499 NSSM_SERVICE_STATUS_DEADLINE milliseconds. If the process is still running
\r
1500 and we haven't finished waiting we increment dwCheckPoint and add whichever is
\r
1501 smaller of NSSM_SERVICE_STATUS_DEADLINE or the remaining timeout to
\r
1504 Only doing both these things will prevent the system from killing the service.
\r
1506 Returns: 1 if the wait timed out.
\r
1507 0 if the wait completed.
\r
1510 int await_shutdown(nssm_service_t *service, TCHAR *function_name, unsigned long timeout) {
\r
1511 unsigned long interval;
\r
1512 unsigned long waithint;
\r
1513 unsigned long ret;
\r
1514 unsigned long waited;
\r
1515 TCHAR interval_milliseconds[16];
\r
1516 TCHAR timeout_milliseconds[16];
\r
1517 TCHAR waited_milliseconds[16];
\r
1518 TCHAR *function = function_name;
\r
1520 /* Add brackets to function name. */
\r
1521 size_t funclen = _tcslen(function_name) + 3;
\r
1522 TCHAR *func = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, funclen * sizeof(TCHAR));
\r
1524 if (_sntprintf_s(func, funclen, _TRUNCATE, _T("%s()"), function_name) > -1) function = func;
\r
1527 _sntprintf_s(timeout_milliseconds, _countof(timeout_milliseconds), _TRUNCATE, _T("%lu"), timeout);
\r
1529 waithint = service->status.dwWaitHint;
\r
1531 while (waited < timeout) {
\r
1532 interval = timeout - waited;
\r
1533 if (interval > NSSM_SERVICE_STATUS_DEADLINE) interval = NSSM_SERVICE_STATUS_DEADLINE;
\r
1535 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
1536 service->status.dwWaitHint += interval;
\r
1537 service->status.dwCheckPoint++;
\r
1538 SetServiceStatus(service->status_handle, &service->status);
\r
1541 _sntprintf_s(waited_milliseconds, _countof(waited_milliseconds), _TRUNCATE, _T("%lu"), waited);
\r
1542 _sntprintf_s(interval_milliseconds, _countof(interval_milliseconds), _TRUNCATE, _T("%lu"), interval);
\r
1543 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_AWAITING_SHUTDOWN, function, service->name, waited_milliseconds, interval_milliseconds, timeout_milliseconds, 0);
\r
1546 switch (WaitForSingleObject(service->process_handle, interval)) {
\r
1547 case WAIT_OBJECT_0:
\r
1551 case WAIT_TIMEOUT:
\r
1560 waited += interval;
\r
1564 if (func) HeapFree(GetProcessHeap(), 0, func);
\r