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
14 const TCHAR *priority_strings[] = { _T("REALTIME_PRIORITY_CLASS"), _T("HIGH_PRIORITY_CLASS"), _T("ABOVE_NORMAL_PRIORITY_CLASS"), _T("NORMAL_PRIORITY_CLASS"), _T("BELOW_NORMAL_PRIORITY_CLASS"), _T("IDLE_PRIORITY_CLASS"), 0 };
\r
16 inline unsigned long priority_mask() {
\r
17 return REALTIME_PRIORITY_CLASS | HIGH_PRIORITY_CLASS | ABOVE_NORMAL_PRIORITY_CLASS | NORMAL_PRIORITY_CLASS | BELOW_NORMAL_PRIORITY_CLASS | IDLE_PRIORITY_CLASS;
\r
20 int priority_constant_to_index(unsigned long constant) {
\r
21 switch (constant & priority_mask()) {
\r
22 case REALTIME_PRIORITY_CLASS: return NSSM_REALTIME_PRIORITY;
\r
23 case HIGH_PRIORITY_CLASS: return NSSM_HIGH_PRIORITY;
\r
24 case ABOVE_NORMAL_PRIORITY_CLASS: return NSSM_ABOVE_NORMAL_PRIORITY;
\r
25 case BELOW_NORMAL_PRIORITY_CLASS: return NSSM_BELOW_NORMAL_PRIORITY;
\r
26 case IDLE_PRIORITY_CLASS: return NSSM_IDLE_PRIORITY;
\r
28 return NSSM_NORMAL_PRIORITY;
\r
31 unsigned long priority_index_to_constant(int index) {
\r
33 case NSSM_REALTIME_PRIORITY: return REALTIME_PRIORITY_CLASS;
\r
34 case NSSM_HIGH_PRIORITY: return HIGH_PRIORITY_CLASS;
\r
35 case NSSM_ABOVE_NORMAL_PRIORITY: return ABOVE_NORMAL_PRIORITY_CLASS;
\r
36 case NSSM_BELOW_NORMAL_PRIORITY: return BELOW_NORMAL_PRIORITY_CLASS;
\r
37 case NSSM_IDLE_PRIORITY: return IDLE_PRIORITY_CLASS;
\r
39 return NORMAL_PRIORITY_CLASS;
\r
42 static inline int throttle_milliseconds(unsigned long throttle) {
\r
43 /* pow() operates on doubles. */
\r
44 int ret = 1; for (unsigned long i = 1; i < throttle; i++) ret *= 2;
\r
49 Wrapper to be called in a new thread so that we can acknowledge a STOP
\r
50 control immediately.
\r
52 static unsigned long WINAPI shutdown_service(void *arg) {
\r
53 return stop_service((nssm_service_t *) arg, 0, true, true);
\r
56 /* Connect to the service manager */
\r
57 SC_HANDLE open_service_manager() {
\r
58 SC_HANDLE ret = OpenSCManager(0, SERVICES_ACTIVE_DATABASE, SC_MANAGER_ALL_ACCESS);
\r
60 if (is_admin) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENSCMANAGER_FAILED, 0);
\r
67 /* Open a service by name or display name. */
\r
68 SC_HANDLE open_service(SC_HANDLE services, TCHAR *service_name, TCHAR *canonical_name, unsigned long canonical_namelen) {
\r
69 SC_HANDLE service_handle = OpenService(services, service_name, SERVICE_ALL_ACCESS);
\r
70 if (service_handle) {
\r
71 if (canonical_name && canonical_name != service_name) {
\r
72 if (_sntprintf_s(canonical_name, canonical_namelen, _TRUNCATE, _T("%s"), service_name) < 0) {
\r
73 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("canonical_name"), _T("open_service()"));
\r
77 return service_handle;
\r
80 unsigned long error = GetLastError();
\r
81 if (error != ERROR_SERVICE_DOES_NOT_EXIST) {
\r
82 print_message(stderr, NSSM_MESSAGE_OPENSERVICE_FAILED, error_string(GetLastError()));
\r
86 /* We can't look for a display name because there's no buffer to store it. */
\r
87 if (! canonical_name) {
\r
88 print_message(stderr, NSSM_MESSAGE_OPENSERVICE_FAILED, error_string(GetLastError()));
\r
92 unsigned long bufsize, required, count, i;
\r
93 unsigned long resume = 0;
\r
94 EnumServicesStatus(services, SERVICE_DRIVER | SERVICE_FILE_SYSTEM_DRIVER | SERVICE_KERNEL_DRIVER | SERVICE_WIN32, SERVICE_STATE_ALL, 0, 0, &required, &count, &resume);
\r
95 error = GetLastError();
\r
96 if (error != ERROR_MORE_DATA) {
\r
97 print_message(stderr, NSSM_MESSAGE_ENUMSERVICESSTATUS_FAILED, error_string(GetLastError()));
\r
101 ENUM_SERVICE_STATUS *status = (ENUM_SERVICE_STATUS *) HeapAlloc(GetProcessHeap(), 0, required);
\r
103 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("ENUM_SERVICE_STATUS"), _T("open_service()"));
\r
107 bufsize = required;
\r
110 EnumServicesStatus() returns:
\r
111 1 when it retrieved data and there's no more data to come.
\r
112 0 and sets last error to ERROR_MORE_DATA when it retrieved data and
\r
113 there's more data to come.
\r
114 0 and sets last error to something else on error.
\r
116 int ret = EnumServicesStatus(services, SERVICE_DRIVER | SERVICE_FILE_SYSTEM_DRIVER | SERVICE_KERNEL_DRIVER | SERVICE_WIN32, SERVICE_STATE_ALL, status, bufsize, &required, &count, &resume);
\r
118 error = GetLastError();
\r
119 if (error != ERROR_MORE_DATA) {
\r
120 HeapFree(GetProcessHeap(), 0, status);
\r
121 print_message(stderr, NSSM_MESSAGE_ENUMSERVICESSTATUS_FAILED, error_string(GetLastError()));
\r
126 for (i = 0; i < count; i++) {
\r
127 if (str_equiv(status[i].lpDisplayName, service_name)) {
\r
128 if (_sntprintf_s(canonical_name, canonical_namelen, _TRUNCATE, _T("%s"), status[i].lpServiceName) < 0) {
\r
129 HeapFree(GetProcessHeap(), 0, status);
\r
130 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("canonical_name"), _T("open_service()"));
\r
134 HeapFree(GetProcessHeap(), 0, status);
\r
135 return open_service(services, canonical_name, 0, 0);
\r
142 /* Recurse so we can get an error message. */
\r
143 return open_service(services, service_name, 0, 0);
\r
146 QUERY_SERVICE_CONFIG *query_service_config(const TCHAR *service_name, SC_HANDLE service_handle) {
\r
147 QUERY_SERVICE_CONFIG *qsc;
\r
148 unsigned long bufsize;
\r
149 unsigned long error;
\r
151 QueryServiceConfig(service_handle, 0, 0, &bufsize);
\r
152 error = GetLastError();
\r
153 if (error == ERROR_INSUFFICIENT_BUFFER) {
\r
154 qsc = (QUERY_SERVICE_CONFIG *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, bufsize);
\r
156 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("QUERY_SERVICE_CONFIG"), _T("query_service_config()"), 0);
\r
161 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG_FAILED, service_name, error_string(error), 0);
\r
165 if (! QueryServiceConfig(service_handle, qsc, bufsize, &bufsize)) {
\r
166 HeapFree(GetProcessHeap(), 0, qsc);
\r
167 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG_FAILED, service_name, error_string(GetLastError()), 0);
\r
174 int set_service_description(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR *buffer) {
\r
175 SERVICE_DESCRIPTION description;
\r
176 ZeroMemory(&description, sizeof(description));
\r
178 lpDescription must be NULL if we aren't changing, the new description
\r
181 if (buffer && buffer[0]) description.lpDescription = buffer;
\r
182 else description.lpDescription = _T("");
\r
184 if (ChangeServiceConfig2(service_handle, SERVICE_CONFIG_DESCRIPTION, &description)) return 0;
\r
186 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_DESCRIPTION_FAILED, service_name, error_string(GetLastError()), 0);
\r
190 int get_service_description(const TCHAR *service_name, SC_HANDLE service_handle, unsigned long len, TCHAR *buffer) {
\r
191 if (! buffer) return 1;
\r
193 unsigned long bufsize;
\r
194 QueryServiceConfig2(service_handle, SERVICE_CONFIG_DESCRIPTION, 0, 0, &bufsize);
\r
195 unsigned long error = GetLastError();
\r
196 if (error == ERROR_INSUFFICIENT_BUFFER) {
\r
197 SERVICE_DESCRIPTION *description = (SERVICE_DESCRIPTION *) HeapAlloc(GetProcessHeap(), 0, bufsize);
\r
198 if (! description) {
\r
199 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("SERVICE_CONFIG_DESCRIPTION"), _T("get_service_description()"));
\r
203 if (QueryServiceConfig2(service_handle, SERVICE_CONFIG_DESCRIPTION, (unsigned char *) description, bufsize, &bufsize)) {
\r
204 if (description->lpDescription) _sntprintf_s(buffer, len, _TRUNCATE, _T("%s"), description->lpDescription);
\r
205 else ZeroMemory(buffer, len * sizeof(TCHAR));
\r
206 HeapFree(GetProcessHeap(), 0, description);
\r
210 HeapFree(GetProcessHeap(), 0, description);
\r
211 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_CONFIG_DESCRIPTION"), error_string(error));
\r
216 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_CONFIG_DESCRIPTION"), error_string(error));
\r
223 int get_service_startup(const TCHAR *service_name, SC_HANDLE service_handle, const QUERY_SERVICE_CONFIG *qsc, unsigned long *startup) {
\r
224 if (! qsc) return 1;
\r
226 switch (qsc->dwStartType) {
\r
227 case SERVICE_DEMAND_START: *startup = NSSM_STARTUP_MANUAL; break;
\r
228 case SERVICE_DISABLED: *startup = NSSM_STARTUP_DISABLED; break;
\r
229 default: *startup = NSSM_STARTUP_AUTOMATIC;
\r
232 if (*startup != NSSM_STARTUP_AUTOMATIC) return 0;
\r
234 /* Check for delayed start. */
\r
235 unsigned long bufsize;
\r
236 unsigned long error;
\r
237 QueryServiceConfig2(service_handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, 0, 0, &bufsize);
\r
238 error = GetLastError();
\r
239 if (error == ERROR_INSUFFICIENT_BUFFER) {
\r
240 SERVICE_DELAYED_AUTO_START_INFO *info = (SERVICE_DELAYED_AUTO_START_INFO *) HeapAlloc(GetProcessHeap(), 0, bufsize);
\r
242 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("SERVICE_DELAYED_AUTO_START_INFO"), _T("get_service_startup()"));
\r
246 if (QueryServiceConfig2(service_handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, (unsigned char *) info, bufsize, &bufsize)) {
\r
247 if (info->fDelayedAutostart) *startup = NSSM_STARTUP_DELAYED;
\r
248 HeapFree(GetProcessHeap(), 0, info);
\r
252 error = GetLastError();
\r
253 if (error != ERROR_INVALID_LEVEL) {
\r
254 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_CONFIG_DELAYED_AUTO_START_INFO"), error_string(error));
\r
259 else if (error != ERROR_INVALID_LEVEL) {
\r
260 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_DELAYED_AUTO_START_INFO"), error_string(error));
\r
267 int get_service_username(const TCHAR *service_name, const QUERY_SERVICE_CONFIG *qsc, TCHAR **username, size_t *usernamelen) {
\r
268 if (! username) return 1;
\r
269 if (! usernamelen) return 1;
\r
274 if (! qsc) return 1;
\r
276 if (str_equiv(qsc->lpServiceStartName, NSSM_LOCALSYSTEM_ACCOUNT)) return 0;
\r
278 size_t len = _tcslen(qsc->lpServiceStartName);
\r
279 *username = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(TCHAR));
\r
281 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("username"), _T("get_service_username()"));
\r
285 memmove(*username, qsc->lpServiceStartName, (len + 1) * sizeof(TCHAR));
\r
286 *usernamelen = len;
\r
291 int grant_logon_as_service(const TCHAR *username) {
\r
292 if (! username) return 0;
\r
293 if (str_equiv(username, NSSM_LOCALSYSTEM_ACCOUNT)) return 0;
\r
295 /* Open Policy object. */
\r
296 LSA_OBJECT_ATTRIBUTES attributes;
\r
297 ZeroMemory(&attributes, sizeof(attributes));
\r
301 NTSTATUS status = LsaOpenPolicy(0, &attributes, POLICY_ALL_ACCESS, &policy);
\r
303 print_message(stderr, NSSM_MESSAGE_LSAOPENPOLICY_FAILED, error_string(LsaNtStatusToWinError(status)));
\r
307 /* Look up SID for the account. */
\r
308 LSA_UNICODE_STRING lsa_username;
\r
310 lsa_username.Buffer = (wchar_t *) username;
\r
311 lsa_username.Length = (unsigned short) _tcslen(username) * sizeof(TCHAR);
\r
312 lsa_username.MaximumLength = lsa_username.Length + sizeof(TCHAR);
\r
315 mbstowcs_s(&buflen, NULL, 0, username, _TRUNCATE);
\r
316 lsa_username.MaximumLength = (unsigned short) buflen * sizeof(wchar_t);
\r
317 lsa_username.Length = lsa_username.MaximumLength - sizeof(wchar_t);
\r
318 lsa_username.Buffer = (wchar_t *) HeapAlloc(GetProcessHeap(), 0, lsa_username.MaximumLength);
\r
319 if (lsa_username.Buffer) mbstowcs_s(&buflen, lsa_username.Buffer, lsa_username.MaximumLength, username, _TRUNCATE);
\r
322 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("LSA_UNICODE_STRING"), _T("grant_logon_as_service()"));
\r
327 LSA_REFERENCED_DOMAIN_LIST *translated_domains;
\r
328 LSA_TRANSLATED_SID *translated_sid;
\r
329 status = LsaLookupNames(policy, 1, &lsa_username, &translated_domains, &translated_sid);
\r
331 HeapFree(GetProcessHeap(), 0, lsa_username.Buffer);
\r
334 LsaFreeMemory(translated_domains);
\r
335 LsaFreeMemory(translated_sid);
\r
337 print_message(stderr, NSSM_MESSAGE_LSALOOKUPNAMES_FAILED, username, error_string(LsaNtStatusToWinError(status)));
\r
341 if (translated_sid->Use != SidTypeUser) {
\r
342 LsaFreeMemory(translated_domains);
\r
343 LsaFreeMemory(translated_sid);
\r
345 print_message(stderr, NSSM_GUI_INVALID_USERNAME, username);
\r
349 LSA_TRUST_INFORMATION *trust = &translated_domains->Domains[translated_sid->DomainIndex];
\r
350 if (! trust || ! IsValidSid(trust->Sid)) {
\r
351 LsaFreeMemory(translated_domains);
\r
352 LsaFreeMemory(translated_sid);
\r
354 print_message(stderr, NSSM_GUI_INVALID_USERNAME, username);
\r
358 /* GetSidSubAuthority*() return pointers! */
\r
359 unsigned char *n = GetSidSubAuthorityCount(trust->Sid);
\r
361 /* Convert translated SID to SID. */
\r
362 SID *sid = (SID *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, GetSidLengthRequired(*n + 1));
\r
364 LsaFreeMemory(translated_domains);
\r
365 LsaFreeMemory(translated_sid);
\r
367 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("SID"), _T("grant_logon_as_service"));
\r
371 unsigned long error;
\r
372 if (! InitializeSid(sid, GetSidIdentifierAuthority(trust->Sid), *n + 1)) {
\r
373 error = GetLastError();
\r
374 HeapFree(GetProcessHeap(), 0, sid);
\r
375 LsaFreeMemory(translated_domains);
\r
376 LsaFreeMemory(translated_sid);
\r
378 print_message(stderr, NSSM_MESSAGE_INITIALIZESID_FAILED, username, error_string(error));
\r
382 for (unsigned char i = 0; i <= *n; i++) {
\r
383 unsigned long *sub = GetSidSubAuthority(sid, i);
\r
384 if (i < *n) *sub = *GetSidSubAuthority(trust->Sid, i);
\r
385 else *sub = translated_sid->RelativeId;
\r
388 LsaFreeMemory(translated_domains);
\r
389 LsaFreeMemory(translated_sid);
\r
391 /* Check if the SID has the "Log on as a service" right. */
\r
392 LSA_UNICODE_STRING lsa_right;
\r
393 lsa_right.Buffer = NSSM_LOGON_AS_SERVICE_RIGHT;
\r
394 lsa_right.Length = (unsigned short) wcslen(lsa_right.Buffer) * sizeof(wchar_t);
\r
395 lsa_right.MaximumLength = lsa_right.Length + sizeof(wchar_t);
\r
397 LSA_UNICODE_STRING *rights;
\r
398 unsigned long count = ~0;
\r
399 status = LsaEnumerateAccountRights(policy, sid, &rights, &count);
\r
402 If the account has no rights set LsaEnumerateAccountRights() will return
\r
403 STATUS_OBJECT_NAME_NOT_FOUND and set count to 0.
\r
405 error = LsaNtStatusToWinError(status);
\r
406 if (error != ERROR_FILE_NOT_FOUND) {
\r
407 HeapFree(GetProcessHeap(), 0, sid);
\r
409 print_message(stderr, NSSM_MESSAGE_LSAENUMERATEACCOUNTRIGHTS_FAILED, username, error_string(error));
\r
414 for (unsigned long i = 0; i < count; i++) {
\r
415 if (rights[i].Length != lsa_right.Length) continue;
\r
416 if (_wcsnicmp(rights[i].Buffer, lsa_right.Buffer, lsa_right.MaximumLength)) continue;
\r
417 /* The SID has the right. */
\r
418 HeapFree(GetProcessHeap(), 0, sid);
\r
419 LsaFreeMemory(rights);
\r
423 LsaFreeMemory(rights);
\r
425 /* Add the right. */
\r
426 status = LsaAddAccountRights(policy, sid, &lsa_right, 1);
\r
427 HeapFree(GetProcessHeap(), 0, sid);
\r
430 print_message(stderr, NSSM_MESSAGE_LSAADDACCOUNTRIGHTS_FAILED, error_string(LsaNtStatusToWinError(status)));
\r
434 print_message(stdout, NSSM_MESSAGE_GRANTED_LOGON_AS_SERVICE, username);
\r
438 /* Set default values which aren't zero. */
\r
439 void set_nssm_service_defaults(nssm_service_t *service) {
\r
440 if (! service) return;
\r
442 service->type = SERVICE_WIN32_OWN_PROCESS;
\r
443 service->priority = NORMAL_PRIORITY_CLASS;
\r
444 service->stdin_sharing = NSSM_STDIN_SHARING;
\r
445 service->stdin_disposition = NSSM_STDIN_DISPOSITION;
\r
446 service->stdin_flags = NSSM_STDIN_FLAGS;
\r
447 service->stdout_sharing = NSSM_STDOUT_SHARING;
\r
448 service->stdout_disposition = NSSM_STDOUT_DISPOSITION;
\r
449 service->stdout_flags = NSSM_STDOUT_FLAGS;
\r
450 service->stderr_sharing = NSSM_STDERR_SHARING;
\r
451 service->stderr_disposition = NSSM_STDERR_DISPOSITION;
\r
452 service->stderr_flags = NSSM_STDERR_FLAGS;
\r
453 service->throttle_delay = NSSM_RESET_THROTTLE_RESTART;
\r
454 service->stop_method = ~0;
\r
455 service->kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;
\r
456 service->kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;
\r
457 service->kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;
\r
460 /* Allocate and zero memory for a service. */
\r
461 nssm_service_t *alloc_nssm_service() {
\r
462 nssm_service_t *service = (nssm_service_t *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(nssm_service_t));
\r
463 if (! service) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("service"), _T("alloc_nssm_service()"), 0);
\r
467 /* Free memory for a service. */
\r
468 void cleanup_nssm_service(nssm_service_t *service) {
\r
469 if (! service) return;
\r
470 if (service->username) HeapFree(GetProcessHeap(), 0, service->username);
\r
471 if (service->password) {
\r
472 SecureZeroMemory(service->password, service->passwordlen);
\r
473 HeapFree(GetProcessHeap(), 0, service->password);
\r
475 if (service->env) HeapFree(GetProcessHeap(), 0, service->env);
\r
476 if (service->env_extra) HeapFree(GetProcessHeap(), 0, service->env_extra);
\r
477 if (service->handle) CloseServiceHandle(service->handle);
\r
478 if (service->process_handle) CloseHandle(service->process_handle);
\r
479 if (service->wait_handle) UnregisterWait(service->process_handle);
\r
480 if (service->throttle_section_initialised) DeleteCriticalSection(&service->throttle_section);
\r
481 if (service->throttle_timer) CloseHandle(service->throttle_timer);
\r
482 HeapFree(GetProcessHeap(), 0, service);
\r
485 /* About to install the service */
\r
486 int pre_install_service(int argc, TCHAR **argv) {
\r
487 nssm_service_t *service = alloc_nssm_service();
\r
488 set_nssm_service_defaults(service);
\r
489 if (argc) _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]);
\r
491 /* Show the dialogue box if we didn't give the service name and path */
\r
492 if (argc < 2) return nssm_gui(IDD_INSTALL, service);
\r
495 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("service"), _T("pre_install_service()"));
\r
498 _sntprintf_s(service->exe, _countof(service->exe), _TRUNCATE, _T("%s"), argv[1]);
\r
500 /* Arguments are optional */
\r
501 size_t flagslen = 0;
\r
504 for (i = 2; i < argc; i++) flagslen += _tcslen(argv[i]) + 1;
\r
505 if (! flagslen) flagslen = 1;
\r
506 if (flagslen > _countof(service->flags)) {
\r
507 print_message(stderr, NSSM_MESSAGE_FLAGS_TOO_LONG);
\r
511 for (i = 2; i < argc; i++) {
\r
512 size_t len = _tcslen(argv[i]);
\r
513 memmove(service->flags + s, argv[i], len * sizeof(TCHAR));
\r
515 if (i < argc - 1) service->flags[s++] = _T(' ');
\r
518 /* Work out directory name */
\r
519 _sntprintf_s(service->dir, _countof(service->dir), _TRUNCATE, _T("%s"), service->exe);
\r
520 strip_basename(service->dir);
\r
522 int ret = install_service(service);
\r
523 cleanup_nssm_service(service);
\r
527 /* About to edit the service. */
\r
528 int pre_edit_service(int argc, TCHAR **argv) {
\r
529 /* Require service name. */
\r
530 if (argc < 2) return usage(1);
\r
532 /* Are we editing on the command line? */
\r
533 enum { MODE_EDITING, MODE_GETTING, MODE_SETTING, MODE_RESETTING } mode = MODE_EDITING;
\r
534 const TCHAR *verb = argv[0];
\r
535 const TCHAR *service_name = argv[1];
\r
536 bool getting = false;
\r
537 bool unsetting = false;
\r
539 /* Minimum number of arguments. */
\r
541 /* Index of first value. */
\r
544 if (str_equiv(verb, _T("get"))) {
\r
546 mode = MODE_GETTING;
\r
548 else if (str_equiv(verb, _T("set"))) {
\r
550 mode = MODE_SETTING;
\r
552 else if (str_equiv(verb, _T("reset")) || str_equiv(verb, _T("unset"))) {
\r
554 mode = MODE_RESETTING;
\r
556 if (argc < mandatory) return usage(1);
\r
558 const TCHAR *parameter = 0;
\r
559 settings_t *setting = 0;
\r
562 /* Validate the parameter. */
\r
563 if (mandatory > 2) {
\r
564 bool additional_mandatory = false;
\r
566 parameter = argv[2];
\r
567 for (i = 0; settings[i].name; i++) {
\r
568 setting = &settings[i];
\r
569 if (! str_equiv(setting->name, parameter)) continue;
\r
570 if (((setting->additional & ADDITIONAL_GETTING) && mode == MODE_GETTING) || ((setting->additional & ADDITIONAL_SETTING) && mode == MODE_SETTING) || ((setting->additional & ADDITIONAL_RESETTING) && mode == MODE_RESETTING)) {
\r
571 additional_mandatory = true;
\r
576 if (! settings[i].name) {
\r
577 print_message(stderr, NSSM_MESSAGE_INVALID_PARAMETER, parameter);
\r
578 for (i = 0; settings[i].name; i++) _ftprintf(stderr, _T("%s\n"), settings[i].name);
\r
581 if (argc < mandatory) return usage(1);
\r
584 if (additional_mandatory) {
\r
585 additional = argv[3];
\r
588 else additional = argv[remainder];
\r
591 nssm_service_t *service = alloc_nssm_service();
\r
592 _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), service_name);
\r
594 /* Open service manager */
\r
595 SC_HANDLE services = open_service_manager();
\r
597 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
601 /* Try to open the service */
\r
602 service->handle = open_service(services, service->name, service->name, _countof(service->name));
\r
603 if (! service->handle) {
\r
604 CloseServiceHandle(services);
\r
608 /* Get system details. */
\r
609 QUERY_SERVICE_CONFIG *qsc = query_service_config(service->name, service->handle);
\r
611 CloseHandle(service->handle);
\r
612 CloseServiceHandle(services);
\r
616 service->type = qsc->dwServiceType;
\r
617 if (! (service->type & SERVICE_WIN32_OWN_PROCESS)) {
\r
618 if (mode != MODE_GETTING) {
\r
619 HeapFree(GetProcessHeap(), 0, qsc);
\r
620 CloseHandle(service->handle);
\r
621 CloseServiceHandle(services);
\r
622 print_message(stderr, NSSM_MESSAGE_CANNOT_EDIT, service->name, NSSM_WIN32_OWN_PROCESS, 0);
\r
627 if (get_service_startup(service->name, service->handle, qsc, &service->startup)) {
\r
628 if (mode != MODE_GETTING) {
\r
629 HeapFree(GetProcessHeap(), 0, qsc);
\r
630 CloseHandle(service->handle);
\r
631 CloseServiceHandle(services);
\r
636 if (get_service_username(service->name, qsc, &service->username, &service->usernamelen)) {
\r
637 if (mode != MODE_GETTING) {
\r
638 HeapFree(GetProcessHeap(), 0, qsc);
\r
639 CloseHandle(service->handle);
\r
640 CloseServiceHandle(services);
\r
645 _sntprintf_s(service->displayname, _countof(service->displayname), _TRUNCATE, _T("%s"), qsc->lpDisplayName);
\r
647 /* Get the canonical service name. We open it case insensitively. */
\r
648 unsigned long bufsize = _countof(service->name);
\r
649 GetServiceKeyName(services, service->displayname, service->name, &bufsize);
\r
651 /* Remember the executable in case it isn't NSSM. */
\r
652 _sntprintf_s(service->image, _countof(service->image), _TRUNCATE, _T("%s"), qsc->lpBinaryPathName);
\r
653 HeapFree(GetProcessHeap(), 0, qsc);
\r
655 /* Get extended system details. */
\r
656 if (get_service_description(service->name, service->handle, _countof(service->description), service->description)) {
\r
657 if (mode != MODE_GETTING) {
\r
658 CloseHandle(service->handle);
\r
659 CloseServiceHandle(services);
\r
664 /* Get NSSM details. */
\r
665 get_parameters(service, 0);
\r
667 CloseServiceHandle(services);
\r
669 if (! service->exe[0]) {
\r
670 service->native = true;
\r
671 if (mode != MODE_GETTING) print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE, service->name, NSSM, service->image);
\r
674 /* Editing with the GUI. */
\r
675 if (mode == MODE_EDITING) {
\r
676 nssm_gui(IDD_EDIT, service);
\r
680 /* Trying to manage App* parameters for a non-NSSM service. */
\r
681 if (! setting->native && service->native) {
\r
682 CloseHandle(service->handle);
\r
683 print_message(stderr, NSSM_MESSAGE_NATIVE_PARAMETER, setting->name, NSSM);
\r
691 if (mode == MODE_GETTING) {
\r
692 if (! service->native) {
\r
693 key = open_registry(service->name, KEY_READ);
\r
694 if (! key) return 4;
\r
697 if (setting->native) ret = get_setting(service->name, service->handle, setting, &value, additional);
\r
698 else ret = get_setting(service->name, key, setting, &value, additional);
\r
700 CloseHandle(service->handle);
\r
704 switch (setting->type) {
\r
705 case REG_EXPAND_SZ:
\r
708 _tprintf(_T("%s\n"), value.string ? value.string : _T(""));
\r
709 HeapFree(GetProcessHeap(), 0, value.string);
\r
713 _tprintf(_T("%u\n"), value.numeric);
\r
717 if (! service->native) RegCloseKey(key);
\r
718 CloseHandle(service->handle);
\r
722 /* Build the value. */
\r
723 if (mode == MODE_RESETTING) {
\r
724 /* Unset the parameter. */
\r
728 /* Set the parameter. */
\r
730 size_t delimiterlen = (setting->additional & ADDITIONAL_CRLF) ? 2 : 1;
\r
731 for (i = remainder; i < argc; i++) len += _tcslen(argv[i]) + delimiterlen;
\r
734 value.string = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, len * sizeof(TCHAR));
\r
735 if (! value.string) {
\r
736 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("value"), _T("edit_service()"));
\r
737 CloseHandle(service->handle);
\r
742 for (i = remainder; i < argc; i++) {
\r
743 size_t len = _tcslen(argv[i]);
\r
744 memmove(value.string + s, argv[i], len * sizeof(TCHAR));
\r
746 if (i < argc - 1) {
\r
747 if (setting->additional & ADDITIONAL_CRLF) {
\r
748 value.string[s++] = _T('\r');
\r
749 value.string[s++] = _T('\n');
\r
751 else value.string[s++] = _T(' ');
\r
754 value.string[s] = _T('\0');
\r
757 if (! service->native) {
\r
758 key = open_registry(service->name, KEY_WRITE);
\r
760 if (value.string) HeapFree(GetProcessHeap(), 0, value.string);
\r
765 if (setting->native) ret = set_setting(service->name, service->handle, setting, &value, additional);
\r
766 else ret = set_setting(service->name, key, setting, &value, additional);
\r
767 if (value.string) HeapFree(GetProcessHeap(), 0, value.string);
\r
769 if (! service->native) RegCloseKey(key);
\r
770 CloseHandle(service->handle);
\r
774 if (! service->native) RegCloseKey(key);
\r
775 CloseHandle(service->handle);
\r
780 /* About to remove the service */
\r
781 int pre_remove_service(int argc, TCHAR **argv) {
\r
782 nssm_service_t *service = alloc_nssm_service();
\r
783 set_nssm_service_defaults(service);
\r
784 if (argc) _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]);
\r
786 /* Show dialogue box if we didn't pass service name and "confirm" */
\r
787 if (argc < 2) return nssm_gui(IDD_REMOVE, service);
\r
788 if (str_equiv(argv[1], _T("confirm"))) {
\r
789 int ret = remove_service(service);
\r
790 cleanup_nssm_service(service);
\r
793 print_message(stderr, NSSM_MESSAGE_PRE_REMOVE_SERVICE);
\r
797 /* Install the service */
\r
798 int install_service(nssm_service_t *service) {
\r
799 if (! service) return 1;
\r
801 /* Open service manager */
\r
802 SC_HANDLE services = open_service_manager();
\r
804 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
805 cleanup_nssm_service(service);
\r
809 /* Get path of this program */
\r
810 GetModuleFileName(0, service->image, _countof(service->image));
\r
812 /* Create the service - settings will be changed in edit_service() */
\r
813 service->handle = CreateService(services, service->name, service->name, SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, service->image, 0, 0, 0, 0, 0);
\r
814 if (! service->handle) {
\r
815 print_message(stderr, NSSM_MESSAGE_CREATESERVICE_FAILED, error_string(GetLastError()));
\r
816 CloseServiceHandle(services);
\r
820 if (edit_service(service, false)) {
\r
821 DeleteService(service->handle);
\r
822 CloseServiceHandle(services);
\r
826 print_message(stdout, NSSM_MESSAGE_SERVICE_INSTALLED, service->name);
\r
829 CloseServiceHandle(services);
\r
834 /* Edit the service. */
\r
835 int edit_service(nssm_service_t *service, bool editing) {
\r
836 if (! service) return 1;
\r
839 The only two valid flags for service type are SERVICE_WIN32_OWN_PROCESS
\r
840 and SERVICE_INTERACTIVE_PROCESS.
\r
842 service->type &= SERVICE_INTERACTIVE_PROCESS;
\r
843 service->type |= SERVICE_WIN32_OWN_PROCESS;
\r
845 /* Startup type. */
\r
846 unsigned long startup;
\r
847 switch (service->startup) {
\r
848 case NSSM_STARTUP_MANUAL: startup = SERVICE_DEMAND_START; break;
\r
849 case NSSM_STARTUP_DISABLED: startup = SERVICE_DISABLED; break;
\r
850 default: startup = SERVICE_AUTO_START;
\r
853 /* Display name. */
\r
854 if (! service->displayname[0]) _sntprintf_s(service->displayname, _countof(service->displayname), _TRUNCATE, _T("%s"), service->name);
\r
857 Username must be NULL if we aren't changing or an account name.
\r
858 We must explicitly use LOCALSYSTEM to change it when we are editing.
\r
859 Password must be NULL if we aren't changing, a password or "".
\r
860 Empty passwords are valid but we won't allow them in the GUI.
\r
862 TCHAR *username = 0;
\r
863 TCHAR *password = 0;
\r
864 if (service->usernamelen) {
\r
865 username = service->username;
\r
866 if (service->passwordlen) password = service->password;
\r
867 else password = _T("");
\r
869 else if (editing) username = NSSM_LOCALSYSTEM_ACCOUNT;
\r
871 if (grant_logon_as_service(username)) {
\r
872 print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username);
\r
876 if (! ChangeServiceConfig(service->handle, service->type, startup, SERVICE_NO_CHANGE, 0, 0, 0, 0, username, password, service->displayname)) {
\r
877 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
881 if (service->description[0] || editing) {
\r
882 set_service_description(service->name, service->handle, service->description);
\r
885 SERVICE_DELAYED_AUTO_START_INFO delayed;
\r
886 ZeroMemory(&delayed, sizeof(delayed));
\r
887 if (service->startup == NSSM_STARTUP_DELAYED) delayed.fDelayedAutostart = 1;
\r
888 else delayed.fDelayedAutostart = 0;
\r
889 /* Delayed startup isn't supported until Vista. */
\r
890 if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, &delayed)) {
\r
891 unsigned long error = GetLastError();
\r
892 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
\r
893 if (error != ERROR_INVALID_LEVEL) {
\r
894 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_DELAYED_AUTO_START_INFO_FAILED, service->name, error_string(error), 0);
\r
898 /* Don't mess with parameters which aren't ours. */
\r
899 if (! service->native) {
\r
900 /* Now we need to put the parameters into the registry */
\r
901 if (create_parameters(service, editing)) {
\r
902 print_message(stderr, NSSM_MESSAGE_CREATE_PARAMETERS_FAILED);
\r
906 set_service_recovery(service);
\r
912 /* Control a service. */
\r
913 int control_service(unsigned long control, int argc, TCHAR **argv) {
\r
914 if (argc < 1) return usage(1);
\r
915 TCHAR *service_name = argv[0];
\r
916 TCHAR canonical_name[SERVICE_NAME_LENGTH];
\r
918 SC_HANDLE services = open_service_manager();
\r
920 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
924 SC_HANDLE service_handle = open_service(services, service_name, canonical_name, _countof(canonical_name));
\r
925 if (! service_handle) {
\r
926 CloseServiceHandle(services);
\r
931 unsigned long error;
\r
932 SERVICE_STATUS service_status;
\r
933 if (control == 0) {
\r
934 ret = StartService(service_handle, (unsigned long) argc, (const TCHAR **) argv);
\r
935 error = GetLastError();
\r
936 CloseHandle(service_handle);
\r
937 CloseServiceHandle(services);
\r
939 if (error == ERROR_IO_PENDING) {
\r
941 Older versions of Windows return immediately with ERROR_IO_PENDING
\r
942 indicate that the operation is still in progress. Newer versions
\r
943 will return it if there really is a delay. As far as we're
\r
944 concerned the operation is a success. We don't claim to offer a
\r
945 fully-feature service control method; it's just a quick 'n' dirty
\r
948 In the future we may identify and handle this situation properly.
\r
951 error = ERROR_SUCCESS;
\r
955 _tprintf(_T("%s: %s"), canonical_name, error_string(error));
\r
959 _ftprintf(stderr, _T("%s: %s"), canonical_name, error_string(error));
\r
963 else if (control == SERVICE_CONTROL_INTERROGATE) {
\r
965 We could actually send an INTERROGATE control but that won't return
\r
966 any information if the service is stopped and we don't care about
\r
967 the extra details it might give us in any case. So we'll fake it.
\r
969 ret = QueryServiceStatus(service_handle, &service_status);
\r
970 error = GetLastError();
\r
973 switch (service_status.dwCurrentState) {
\r
974 case SERVICE_STOPPED: _tprintf(_T("SERVICE_STOPPED\n")); break;
\r
975 case SERVICE_START_PENDING: _tprintf(_T("SERVICE_START_PENDING\n")); break;
\r
976 case SERVICE_STOP_PENDING: _tprintf(_T("SERVICE_STOP_PENDING\n")); break;
\r
977 case SERVICE_RUNNING: _tprintf(_T("SERVICE_RUNNING\n")); break;
\r
978 case SERVICE_CONTINUE_PENDING: _tprintf(_T("SERVICE_CONTINUE_PENDING\n")); break;
\r
979 case SERVICE_PAUSE_PENDING: _tprintf(_T("SERVICE_PAUSE_PENDING\n")); break;
\r
980 case SERVICE_PAUSED: _tprintf(_T("SERVICE_PAUSED\n")); break;
\r
981 default: _tprintf(_T("?\n")); return 1;
\r
986 _ftprintf(stderr, _T("%s: %s\n"), canonical_name, error_string(error));
\r
991 ret = ControlService(service_handle, control, &service_status);
\r
992 error = GetLastError();
\r
993 CloseHandle(service_handle);
\r
994 CloseServiceHandle(services);
\r
996 if (error == ERROR_IO_PENDING) {
\r
998 error = ERROR_SUCCESS;
\r
1002 _tprintf(_T("%s: %s"), canonical_name, error_string(error));
\r
1006 _ftprintf(stderr, _T("%s: %s"), canonical_name, error_string(error));
\r
1012 /* Remove the service */
\r
1013 int remove_service(nssm_service_t *service) {
\r
1014 if (! service) return 1;
\r
1016 /* Open service manager */
\r
1017 SC_HANDLE services = open_service_manager();
\r
1019 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
1023 /* Try to open the service */
\r
1024 service->handle = open_service(services, service->name, service->name, _countof(service->name));
\r
1025 if (! service->handle) {
\r
1026 CloseServiceHandle(services);
\r
1030 /* Get the canonical service name. We open it case insensitively. */
\r
1031 unsigned long bufsize = _countof(service->displayname);
\r
1032 GetServiceDisplayName(services, service->name, service->displayname, &bufsize);
\r
1033 bufsize = _countof(service->name);
\r
1034 GetServiceKeyName(services, service->displayname, service->name, &bufsize);
\r
1036 /* Try to delete the service */
\r
1037 if (! DeleteService(service->handle)) {
\r
1038 print_message(stderr, NSSM_MESSAGE_DELETESERVICE_FAILED);
\r
1039 CloseServiceHandle(services);
\r
1044 CloseServiceHandle(services);
\r
1046 print_message(stdout, NSSM_MESSAGE_SERVICE_REMOVED, service->name);
\r
1050 /* Service initialisation */
\r
1051 void WINAPI service_main(unsigned long argc, TCHAR **argv) {
\r
1052 nssm_service_t *service = alloc_nssm_service();
\r
1053 if (! service) return;
\r
1055 if (_sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]) < 0) {
\r
1056 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("service->name"), _T("service_main()"), 0);
\r
1060 /* We can use a condition variable in a critical section on Vista or later. */
\r
1061 if (imports.SleepConditionVariableCS && imports.WakeConditionVariable) use_critical_section = true;
\r
1062 else use_critical_section = false;
\r
1064 /* Initialise status */
\r
1065 ZeroMemory(&service->status, sizeof(service->status));
\r
1066 service->status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;
\r
1067 service->status.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE;
\r
1068 service->status.dwWin32ExitCode = NO_ERROR;
\r
1069 service->status.dwServiceSpecificExitCode = 0;
\r
1070 service->status.dwCheckPoint = 0;
\r
1071 service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;
\r
1073 /* Signal we AREN'T running the server */
\r
1074 service->process_handle = 0;
\r
1077 /* Register control handler */
\r
1078 service->status_handle = RegisterServiceCtrlHandlerEx(NSSM, service_control_handler, (void *) service);
\r
1079 if (! service->status_handle) {
\r
1080 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_REGISTERSERVICECTRLHANDER_FAILED, error_string(GetLastError()), 0);
\r
1084 log_service_control(service->name, 0, true);
\r
1086 service->status.dwCurrentState = SERVICE_START_PENDING;
\r
1087 service->status.dwWaitHint = service->throttle_delay + NSSM_WAITHINT_MARGIN;
\r
1088 SetServiceStatus(service->status_handle, &service->status);
\r
1091 /* Try to create the exit action parameters; we don't care if it fails */
\r
1092 create_exit_action(service->name, exit_action_strings[0], false);
\r
1094 SC_HANDLE services = open_service_manager();
\r
1096 service->handle = OpenService(services, service->name, SC_MANAGER_ALL_ACCESS);
\r
1097 set_service_recovery(service);
\r
1098 CloseServiceHandle(services);
\r
1102 /* Used for signalling a resume if the service pauses when throttled. */
\r
1103 if (use_critical_section) {
\r
1104 InitializeCriticalSection(&service->throttle_section);
\r
1105 service->throttle_section_initialised = true;
\r
1108 service->throttle_timer = CreateWaitableTimer(0, 1, 0);
\r
1109 if (! service->throttle_timer) {
\r
1110 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_CREATEWAITABLETIMER_FAILED, service->name, error_string(GetLastError()), 0);
\r
1114 monitor_service(service);
\r
1117 /* Make sure service recovery actions are taken where necessary */
\r
1118 void set_service_recovery(nssm_service_t *service) {
\r
1119 SERVICE_FAILURE_ACTIONS_FLAG flag;
\r
1120 ZeroMemory(&flag, sizeof(flag));
\r
1121 flag.fFailureActionsOnNonCrashFailures = true;
\r
1123 /* This functionality was added in Vista so the call may fail */
\r
1124 if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, &flag)) {
\r
1125 unsigned long error = GetLastError();
\r
1126 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
\r
1127 if (error != ERROR_INVALID_LEVEL) {
\r
1128 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_FAILURE_ACTIONS_FAILED, service->name, error_string(error), 0);
\r
1133 int monitor_service(nssm_service_t *service) {
\r
1134 /* Set service status to started */
\r
1135 int ret = start_service(service);
\r
1138 _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%d"), ret);
\r
1139 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_START_SERVICE_FAILED, service->exe, service->name, ret, 0);
\r
1142 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_STARTED_SERVICE, service->exe, service->flags, service->name, service->dir, 0);
\r
1144 /* Monitor service */
\r
1145 if (! RegisterWaitForSingleObject(&service->wait_handle, service->process_handle, end_service, (void *) service, INFINITE, WT_EXECUTEONLYONCE | WT_EXECUTELONGFUNCTION)) {
\r
1146 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_REGISTERWAITFORSINGLEOBJECT_FAILED, service->name, service->exe, error_string(GetLastError()), 0);
\r
1152 TCHAR *service_control_text(unsigned long control) {
\r
1153 switch (control) {
\r
1154 /* HACK: there is no SERVICE_CONTROL_START constant */
\r
1155 case 0: return _T("START");
\r
1156 case SERVICE_CONTROL_STOP: return _T("STOP");
\r
1157 case SERVICE_CONTROL_SHUTDOWN: return _T("SHUTDOWN");
\r
1158 case SERVICE_CONTROL_PAUSE: return _T("PAUSE");
\r
1159 case SERVICE_CONTROL_CONTINUE: return _T("CONTINUE");
\r
1160 case SERVICE_CONTROL_INTERROGATE: return _T("INTERROGATE");
\r
1161 default: return 0;
\r
1165 void log_service_control(TCHAR *service_name, unsigned long control, bool handled) {
\r
1166 TCHAR *text = service_control_text(control);
\r
1167 unsigned long event;
\r
1170 /* "0x" + 8 x hex + NULL */
\r
1171 text = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, 11 * sizeof(TCHAR));
\r
1173 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);
\r
1176 if (_sntprintf_s(text, 11, _TRUNCATE, _T("0x%08x"), control) < 0) {
\r
1177 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);
\r
1178 HeapFree(GetProcessHeap(), 0, text);
\r
1182 event = NSSM_EVENT_SERVICE_CONTROL_UNKNOWN;
\r
1184 else if (handled) event = NSSM_EVENT_SERVICE_CONTROL_HANDLED;
\r
1185 else event = NSSM_EVENT_SERVICE_CONTROL_NOT_HANDLED;
\r
1187 log_event(EVENTLOG_INFORMATION_TYPE, event, service_name, text, 0);
\r
1189 if (event == NSSM_EVENT_SERVICE_CONTROL_UNKNOWN) {
\r
1190 HeapFree(GetProcessHeap(), 0, text);
\r
1194 /* Service control handler */
\r
1195 unsigned long WINAPI service_control_handler(unsigned long control, unsigned long event, void *data, void *context) {
\r
1196 nssm_service_t *service = (nssm_service_t *) context;
\r
1198 switch (control) {
\r
1199 case SERVICE_CONTROL_INTERROGATE:
\r
1200 /* We always keep the service status up-to-date so this is a no-op. */
\r
1203 case SERVICE_CONTROL_SHUTDOWN:
\r
1204 case SERVICE_CONTROL_STOP:
\r
1205 log_service_control(service->name, control, true);
\r
1207 We MUST acknowledge the stop request promptly but we're committed to
\r
1208 waiting for the application to exit. Spawn a new thread to wait
\r
1209 while we acknowledge the request.
\r
1211 if (! CreateThread(NULL, 0, shutdown_service, context, 0, NULL)) {
\r
1212 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETHREAD_FAILED, error_string(GetLastError()), 0);
\r
1215 We couldn't create a thread to tidy up so we'll have to force the tidyup
\r
1216 to complete in time in this thread.
\r
1218 service->kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;
\r
1219 service->kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;
\r
1220 service->kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;
\r
1222 stop_service(service, 0, true, true);
\r
1226 case SERVICE_CONTROL_CONTINUE:
\r
1227 log_service_control(service->name, control, true);
\r
1228 service->throttle = 0;
\r
1229 if (use_critical_section) imports.WakeConditionVariable(&service->throttle_condition);
\r
1231 if (! service->throttle_timer) return ERROR_CALL_NOT_IMPLEMENTED;
\r
1232 ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));
\r
1233 SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);
\r
1235 service->status.dwCurrentState = SERVICE_CONTINUE_PENDING;
\r
1236 service->status.dwWaitHint = throttle_milliseconds(service->throttle) + NSSM_WAITHINT_MARGIN;
\r
1237 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESET_THROTTLE, service->name, 0);
\r
1238 SetServiceStatus(service->status_handle, &service->status);
\r
1241 case SERVICE_CONTROL_PAUSE:
\r
1243 We don't accept pause messages but it isn't possible to register
\r
1244 only for continue messages so we have to handle this case.
\r
1246 log_service_control(service->name, control, false);
\r
1247 return ERROR_CALL_NOT_IMPLEMENTED;
\r
1250 /* Unknown control */
\r
1251 log_service_control(service->name, control, false);
\r
1252 return ERROR_CALL_NOT_IMPLEMENTED;
\r
1255 /* Start the service */
\r
1256 int start_service(nssm_service_t *service) {
\r
1257 service->stopping = false;
\r
1258 service->allow_restart = true;
\r
1260 if (service->process_handle) return 0;
\r
1262 /* Allocate a STARTUPINFO structure for a new process */
\r
1264 ZeroMemory(&si, sizeof(si));
\r
1265 si.cb = sizeof(si);
\r
1267 /* Allocate a PROCESSINFO structure for the process */
\r
1268 PROCESS_INFORMATION pi;
\r
1269 ZeroMemory(&pi, sizeof(pi));
\r
1271 /* Get startup parameters */
\r
1272 int ret = get_parameters(service, &si);
\r
1274 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_PARAMETERS_FAILED, service->name, 0);
\r
1275 return stop_service(service, 2, true, true);
\r
1278 /* Launch executable with arguments */
\r
1279 TCHAR cmd[CMD_LENGTH];
\r
1280 if (_sntprintf_s(cmd, _countof(cmd), _TRUNCATE, _T("\"%s\" %s"), service->exe, service->flags) < 0) {
\r
1281 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("command line"), _T("start_service"), 0);
\r
1282 close_output_handles(&si);
\r
1283 return stop_service(service, 2, true, true);
\r
1286 throttle_restart(service);
\r
1288 bool inherit_handles = false;
\r
1289 if (si.dwFlags & STARTF_USESTDHANDLES) inherit_handles = true;
\r
1290 unsigned long flags = service->priority & priority_mask();
\r
1292 flags |= CREATE_UNICODE_ENVIRONMENT;
\r
1294 if (! CreateProcess(0, cmd, 0, 0, inherit_handles, flags, service->env, service->dir, &si, &pi)) {
\r
1295 unsigned long exitcode = 3;
\r
1296 unsigned long error = GetLastError();
\r
1297 if (error == ERROR_INVALID_PARAMETER && service->env) {
\r
1298 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED_INVALID_ENVIRONMENT, service->name, service->exe, NSSM_REG_ENV, 0);
\r
1299 if (test_environment(service->env)) exitcode = 4;
\r
1301 else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service->name, service->exe, error_string(error), 0);
\r
1302 close_output_handles(&si);
\r
1303 return stop_service(service, exitcode, true, true);
\r
1305 service->process_handle = pi.hProcess;
\r
1306 service->pid = pi.dwProcessId;
\r
1308 if (get_process_creation_time(service->process_handle, &service->creation_time)) ZeroMemory(&service->creation_time, sizeof(service->creation_time));
\r
1310 close_output_handles(&si);
\r
1313 Wait for a clean startup before changing the service status to RUNNING
\r
1314 but be mindful of the fact that we are blocking the service control manager
\r
1315 so abandon the wait before too much time has elapsed.
\r
1317 unsigned long delay = service->throttle_delay;
\r
1318 if (delay > NSSM_SERVICE_STATUS_DEADLINE) {
\r
1319 TCHAR delay_milliseconds[16];
\r
1320 _sntprintf_s(delay_milliseconds, _countof(delay_milliseconds), _TRUNCATE, _T("%lu"), delay);
\r
1321 TCHAR deadline_milliseconds[16];
\r
1322 _sntprintf_s(deadline_milliseconds, _countof(deadline_milliseconds), _TRUNCATE, _T("%lu"), NSSM_SERVICE_STATUS_DEADLINE);
\r
1323 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_STARTUP_DELAY_TOO_LONG, service->name, delay_milliseconds, NSSM, deadline_milliseconds, 0);
\r
1324 delay = NSSM_SERVICE_STATUS_DEADLINE;
\r
1326 unsigned long deadline = WaitForSingleObject(service->process_handle, delay);
\r
1328 /* Signal successful start */
\r
1329 service->status.dwCurrentState = SERVICE_RUNNING;
\r
1330 SetServiceStatus(service->status_handle, &service->status);
\r
1332 /* Continue waiting for a clean startup. */
\r
1333 if (deadline == WAIT_TIMEOUT) {
\r
1334 if (service->throttle_delay > delay) {
\r
1335 if (WaitForSingleObject(service->process_handle, service->throttle_delay - delay) == WAIT_TIMEOUT) service->throttle = 0;
\r
1337 else service->throttle = 0;
\r
1343 /* Stop the service */
\r
1344 int stop_service(nssm_service_t *service, unsigned long exitcode, bool graceful, bool default_action) {
\r
1345 service->allow_restart = false;
\r
1346 if (service->wait_handle) {
\r
1347 UnregisterWait(service->wait_handle);
\r
1348 service->wait_handle = 0;
\r
1351 if (default_action && ! exitcode && ! graceful) {
\r
1352 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
1356 /* Signal we are stopping */
\r
1358 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
1359 service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;
\r
1360 SetServiceStatus(service->status_handle, &service->status);
\r
1363 /* Nothing to do if service isn't running */
\r
1364 if (service->pid) {
\r
1365 /* Shut down service */
\r
1366 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_TERMINATEPROCESS, service->name, service->exe, 0);
\r
1367 kill_process(service, service->process_handle, service->pid, 0);
\r
1369 else log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_PROCESS_ALREADY_STOPPED, service->name, service->exe, 0);
\r
1371 end_service((void *) service, true);
\r
1373 /* Signal we stopped */
\r
1375 service->status.dwCurrentState = SERVICE_STOPPED;
\r
1377 service->status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
\r
1378 service->status.dwServiceSpecificExitCode = exitcode;
\r
1381 service->status.dwWin32ExitCode = NO_ERROR;
\r
1382 service->status.dwServiceSpecificExitCode = 0;
\r
1384 SetServiceStatus(service->status_handle, &service->status);
\r
1390 /* Callback function triggered when the server exits */
\r
1391 void CALLBACK end_service(void *arg, unsigned char why) {
\r
1392 nssm_service_t *service = (nssm_service_t *) arg;
\r
1394 if (service->stopping) return;
\r
1396 service->stopping = true;
\r
1398 /* Check exit code */
\r
1399 unsigned long exitcode = 0;
\r
1401 if (service->process_handle) {
\r
1402 GetExitCodeProcess(service->process_handle, &exitcode);
\r
1403 if (exitcode == STILL_ACTIVE || get_process_exit_time(service->process_handle, &service->exit_time)) GetSystemTimeAsFileTime(&service->exit_time);
\r
1404 CloseHandle(service->process_handle);
\r
1406 else GetSystemTimeAsFileTime(&service->exit_time);
\r
1408 service->process_handle = 0;
\r
1411 Log that the service ended BEFORE logging about killing the process
\r
1412 tree. See below for the possible values of the why argument.
\r
1415 _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), exitcode);
\r
1416 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ENDED_SERVICE, service->exe, service->name, code, 0);
\r
1420 if (exitcode == STILL_ACTIVE) exitcode = 0;
\r
1421 if (service->pid) kill_process_tree(service, service->pid, exitcode, service->pid);
\r
1425 The why argument is true if our wait timed out or false otherwise.
\r
1426 Our wait is infinite so why will never be true when called by the system.
\r
1427 If it is indeed true, assume we were called from stop_service() because
\r
1428 this is a controlled shutdown, and don't take any restart action.
\r
1431 if (! service->allow_restart) return;
\r
1433 /* What action should we take? */
\r
1434 int action = NSSM_EXIT_RESTART;
\r
1435 TCHAR action_string[ACTION_LEN];
\r
1436 bool default_action;
\r
1437 if (! get_exit_action(service->name, &exitcode, action_string, &default_action)) {
\r
1438 for (int i = 0; exit_action_strings[i]; i++) {
\r
1439 if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
1447 /* Try to restart the service or return failure code to service manager */
\r
1448 case NSSM_EXIT_RESTART:
\r
1449 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_RESTART, service->name, code, exit_action_strings[action], service->exe, 0);
\r
1450 while (monitor_service(service)) {
\r
1451 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_RESTART_SERVICE_FAILED, service->exe, service->name, 0);
\r
1456 /* Do nothing, just like srvany would */
\r
1457 case NSSM_EXIT_IGNORE:
\r
1458 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_IGNORE, service->name, code, exit_action_strings[action], service->exe, 0);
\r
1462 /* Tell the service manager we are finished */
\r
1463 case NSSM_EXIT_REALLY:
\r
1464 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_REALLY, service->name, code, exit_action_strings[action], 0);
\r
1465 stop_service(service, exitcode, true, default_action);
\r
1468 /* Fake a crash so pre-Vista service managers will run recovery actions. */
\r
1469 case NSSM_EXIT_UNCLEAN:
\r
1470 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_UNCLEAN, service->name, code, exit_action_strings[action], 0);
\r
1471 stop_service(service, exitcode, false, default_action);
\r
1478 void throttle_restart(nssm_service_t *service) {
\r
1479 /* This can't be a restart if the service is already running. */
\r
1480 if (! service->throttle++) return;
\r
1482 int ms = throttle_milliseconds(service->throttle);
\r
1484 if (service->throttle > 7) service->throttle = 8;
\r
1486 TCHAR threshold[8], milliseconds[8];
\r
1487 _sntprintf_s(threshold, _countof(threshold), _TRUNCATE, _T("%lu"), service->throttle_delay);
\r
1488 _sntprintf_s(milliseconds, _countof(milliseconds), _TRUNCATE, _T("%lu"), ms);
\r
1489 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_THROTTLED, service->name, threshold, milliseconds, 0);
\r
1491 if (use_critical_section) EnterCriticalSection(&service->throttle_section);
\r
1492 else if (service->throttle_timer) {
\r
1493 ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));
\r
1494 service->throttle_duetime.QuadPart = 0 - (ms * 10000LL);
\r
1495 SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);
\r
1498 service->status.dwCurrentState = SERVICE_PAUSED;
\r
1499 SetServiceStatus(service->status_handle, &service->status);
\r
1501 if (use_critical_section) {
\r
1502 imports.SleepConditionVariableCS(&service->throttle_condition, &service->throttle_section, ms);
\r
1503 LeaveCriticalSection(&service->throttle_section);
\r
1506 if (service->throttle_timer) WaitForSingleObject(service->throttle_timer, INFINITE);
\r
1512 When responding to a stop (or any other) request we need to set dwWaitHint to
\r
1513 the number of milliseconds we expect the operation to take, and optionally
\r
1514 increase dwCheckPoint. If dwWaitHint milliseconds elapses without the
\r
1515 operation completing or dwCheckPoint increasing, the system will consider the
\r
1516 service to be hung.
\r
1518 However the system will consider the service to be hung after 30000
\r
1519 milliseconds regardless of the value of dwWaitHint if dwCheckPoint has not
\r
1520 changed. Therefore if we want to wait longer than that we must periodically
\r
1521 increase dwCheckPoint.
\r
1523 Furthermore, it will consider the service to be hung after 60000 milliseconds
\r
1524 regardless of the value of dwCheckPoint unless dwWaitHint is increased every
\r
1525 time dwCheckPoint is also increased.
\r
1527 Our strategy then is to retrieve the initial dwWaitHint and wait for
\r
1528 NSSM_SERVICE_STATUS_DEADLINE milliseconds. If the process is still running
\r
1529 and we haven't finished waiting we increment dwCheckPoint and add whichever is
\r
1530 smaller of NSSM_SERVICE_STATUS_DEADLINE or the remaining timeout to
\r
1533 Only doing both these things will prevent the system from killing the service.
\r
1535 Returns: 1 if the wait timed out.
\r
1536 0 if the wait completed.
\r
1539 int await_shutdown(nssm_service_t *service, TCHAR *function_name, unsigned long timeout) {
\r
1540 unsigned long interval;
\r
1541 unsigned long waithint;
\r
1542 unsigned long ret;
\r
1543 unsigned long waited;
\r
1544 TCHAR interval_milliseconds[16];
\r
1545 TCHAR timeout_milliseconds[16];
\r
1546 TCHAR waited_milliseconds[16];
\r
1547 TCHAR *function = function_name;
\r
1549 /* Add brackets to function name. */
\r
1550 size_t funclen = _tcslen(function_name) + 3;
\r
1551 TCHAR *func = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, funclen * sizeof(TCHAR));
\r
1553 if (_sntprintf_s(func, funclen, _TRUNCATE, _T("%s()"), function_name) > -1) function = func;
\r
1556 _sntprintf_s(timeout_milliseconds, _countof(timeout_milliseconds), _TRUNCATE, _T("%lu"), timeout);
\r
1558 waithint = service->status.dwWaitHint;
\r
1560 while (waited < timeout) {
\r
1561 interval = timeout - waited;
\r
1562 if (interval > NSSM_SERVICE_STATUS_DEADLINE) interval = NSSM_SERVICE_STATUS_DEADLINE;
\r
1564 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
1565 service->status.dwWaitHint += interval;
\r
1566 service->status.dwCheckPoint++;
\r
1567 SetServiceStatus(service->status_handle, &service->status);
\r
1570 _sntprintf_s(waited_milliseconds, _countof(waited_milliseconds), _TRUNCATE, _T("%lu"), waited);
\r
1571 _sntprintf_s(interval_milliseconds, _countof(interval_milliseconds), _TRUNCATE, _T("%lu"), interval);
\r
1572 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_AWAITING_SHUTDOWN, function, service->name, waited_milliseconds, interval_milliseconds, timeout_milliseconds, 0);
\r
1575 switch (WaitForSingleObject(service->process_handle, interval)) {
\r
1576 case WAIT_OBJECT_0:
\r
1580 case WAIT_TIMEOUT:
\r
1589 waited += interval;
\r
1593 if (func) HeapFree(GetProcessHeap(), 0, func);
\r