4 bool use_critical_section;
\r
6 extern imports_t imports;
\r
7 extern settings_t settings[];
\r
9 const TCHAR *exit_action_strings[] = { _T("Restart"), _T("Ignore"), _T("Exit"), _T("Suicide"), 0 };
\r
10 const TCHAR *startup_strings[] = { _T("SERVICE_AUTO_START"), _T("SERVICE_DELAYED_AUTO_START"), _T("SERVICE_DEMAND_START"), _T("SERVICE_DISABLED"), 0 };
\r
11 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
19 Check the status in response to a control.
\r
20 Returns: 1 if the status is expected, eg STOP following CONTROL_STOP.
\r
21 0 if the status is desired, eg STOPPED following CONTROL_STOP.
\r
22 -1 if the status is undesired, eg STOPPED following CONTROL_START.
\r
24 static inline int service_control_response(unsigned long control, unsigned long status) {
\r
26 case NSSM_SERVICE_CONTROL_START:
\r
28 case SERVICE_START_PENDING:
\r
31 case SERVICE_RUNNING:
\r
38 case SERVICE_CONTROL_STOP:
\r
39 case SERVICE_CONTROL_SHUTDOWN:
\r
41 case SERVICE_STOP_PENDING:
\r
44 case SERVICE_STOPPED:
\r
51 case SERVICE_CONTROL_PAUSE:
\r
53 case SERVICE_PAUSE_PENDING:
\r
56 case SERVICE_PAUSED:
\r
63 case SERVICE_CONTROL_CONTINUE:
\r
65 case SERVICE_CONTINUE_PENDING:
\r
68 case SERVICE_RUNNING:
\r
75 case SERVICE_CONTROL_INTERROGATE:
\r
82 static inline int await_service_control_response(unsigned long control, SC_HANDLE service_handle, SERVICE_STATUS *service_status, unsigned long initial_status) {
\r
84 while (QueryServiceStatus(service_handle, service_status)) {
\r
85 int response = service_control_response(control, service_status->dwCurrentState);
\r
86 /* Alas we can't WaitForSingleObject() on an SC_HANDLE. */
\r
87 if (! response) return response;
\r
88 if (response > 0 || service_status->dwCurrentState == initial_status) {
\r
89 if (++tries > 10) return response;
\r
92 else return response;
\r
97 int affinity_mask_to_string(__int64 mask, TCHAR **string) {
\r
98 if (! string) return 1;
\r
106 /* SetProcessAffinityMask() accepts a mask of up to 64 processors. */
\r
108 for (n = 0; n < _countof(set); n++) set[n].first = set[n].last = -1;
\r
110 for (i = 0, n = 0; i < _countof(set); i++) {
\r
111 if (mask & (1LL << i)) {
\r
112 if (set[n].first == -1) set[n].first = set[n].last = (int) i;
\r
113 else if (set[n].last == (int) i - 1) set[n].last = (int) i;
\r
116 set[n].first = set[n].last = (int) i;
\r
121 /* Worst case is 2x2 characters for first and last CPU plus - and/or , */
\r
122 size_t len = (size_t) (n + 1) * 6;
\r
123 *string = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len * sizeof(TCHAR));
\r
124 if (! string) return 2;
\r
128 for (i = 0; i <= n; i++) {
\r
129 if (i) (*string)[s++] = _T(',');
\r
130 ret = _sntprintf_s(*string + s, 3, _TRUNCATE, _T("%u"), set[i].first);
\r
132 HeapFree(GetProcessHeap(), 0, *string);
\r
137 if (set[i].last != set[i].first) {
\r
138 ret =_sntprintf_s(*string + s, 4, _TRUNCATE, _T("%c%u"), (set[i].last == set[i].first + 1) ? _T(',') : _T('-'), set[i].last);
\r
140 HeapFree(GetProcessHeap(), 0, *string);
\r
151 int affinity_string_to_mask(TCHAR *string, __int64 *mask) {
\r
152 if (! mask) return 1;
\r
155 if (! string) return 0;
\r
164 unsigned long number;
\r
166 for (n = 0; n < _countof(set); n++) set[n].first = set[n].last = -1;
\r
170 ret = str_number(s, &number, &end);
\r
172 if (ret == 0 || ret == 2) {
\r
173 if (number >= _countof(set)) return 2;
\r
174 set[n].first = set[n].last = (int) number;
\r
186 if (! *(++s)) return 3;
\r
187 ret = str_number(s, &number, &end);
\r
188 if (ret == 0 || ret == 2) {
\r
190 if (! *s || *s == _T(',')) {
\r
191 set[n].last = (int) number;
\r
208 for (i = 0; i <= n; i++) {
\r
209 for (int j = set[i].first; j <= set[i].last; j++) (__int64) *mask |= (1LL << (__int64) j);
\r
215 inline unsigned long priority_mask() {
\r
216 return REALTIME_PRIORITY_CLASS | HIGH_PRIORITY_CLASS | ABOVE_NORMAL_PRIORITY_CLASS | NORMAL_PRIORITY_CLASS | BELOW_NORMAL_PRIORITY_CLASS | IDLE_PRIORITY_CLASS;
\r
219 int priority_constant_to_index(unsigned long constant) {
\r
220 switch (constant & priority_mask()) {
\r
221 case REALTIME_PRIORITY_CLASS: return NSSM_REALTIME_PRIORITY;
\r
222 case HIGH_PRIORITY_CLASS: return NSSM_HIGH_PRIORITY;
\r
223 case ABOVE_NORMAL_PRIORITY_CLASS: return NSSM_ABOVE_NORMAL_PRIORITY;
\r
224 case BELOW_NORMAL_PRIORITY_CLASS: return NSSM_BELOW_NORMAL_PRIORITY;
\r
225 case IDLE_PRIORITY_CLASS: return NSSM_IDLE_PRIORITY;
\r
227 return NSSM_NORMAL_PRIORITY;
\r
230 unsigned long priority_index_to_constant(int index) {
\r
232 case NSSM_REALTIME_PRIORITY: return REALTIME_PRIORITY_CLASS;
\r
233 case NSSM_HIGH_PRIORITY: return HIGH_PRIORITY_CLASS;
\r
234 case NSSM_ABOVE_NORMAL_PRIORITY: return ABOVE_NORMAL_PRIORITY_CLASS;
\r
235 case NSSM_BELOW_NORMAL_PRIORITY: return BELOW_NORMAL_PRIORITY_CLASS;
\r
236 case NSSM_IDLE_PRIORITY: return IDLE_PRIORITY_CLASS;
\r
238 return NORMAL_PRIORITY_CLASS;
\r
241 static inline unsigned long throttle_milliseconds(unsigned long throttle) {
\r
242 /* pow() operates on doubles. */
\r
243 unsigned long ret = 1; for (unsigned long i = 1; i < throttle; i++) ret *= 2;
\r
248 Wrapper to be called in a new thread so that we can acknowledge a STOP
\r
249 control immediately.
\r
251 static unsigned long WINAPI shutdown_service(void *arg) {
\r
252 return stop_service((nssm_service_t *) arg, 0, true, true);
\r
255 /* Connect to the service manager */
\r
256 SC_HANDLE open_service_manager(unsigned long access) {
\r
257 SC_HANDLE ret = OpenSCManager(0, SERVICES_ACTIVE_DATABASE, access);
\r
259 if (is_admin) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENSCMANAGER_FAILED, 0);
\r
266 /* Open a service by name or display name. */
\r
267 SC_HANDLE open_service(SC_HANDLE services, TCHAR *service_name, unsigned long access, TCHAR *canonical_name, unsigned long canonical_namelen) {
\r
268 SC_HANDLE service_handle = OpenService(services, service_name, access);
\r
269 if (service_handle) {
\r
270 if (canonical_name && canonical_name != service_name) {
\r
271 TCHAR displayname[SERVICE_NAME_LENGTH];
\r
272 unsigned long displayname_len = (unsigned long) _countof(displayname);
\r
273 GetServiceDisplayName(services, service_name, displayname, &displayname_len);
\r
274 unsigned long keyname_len = canonical_namelen;
\r
275 GetServiceKeyName(services, displayname, canonical_name, &keyname_len);
\r
277 return service_handle;
\r
280 unsigned long error = GetLastError();
\r
281 if (error != ERROR_SERVICE_DOES_NOT_EXIST) {
\r
282 print_message(stderr, NSSM_MESSAGE_OPENSERVICE_FAILED, error_string(GetLastError()));
\r
286 /* We can't look for a display name because there's no buffer to store it. */
\r
287 if (! canonical_name) {
\r
288 print_message(stderr, NSSM_MESSAGE_OPENSERVICE_FAILED, error_string(GetLastError()));
\r
292 unsigned long bufsize, required, count, i;
\r
293 unsigned long resume = 0;
\r
294 EnumServicesStatus(services, SERVICE_DRIVER | SERVICE_FILE_SYSTEM_DRIVER | SERVICE_KERNEL_DRIVER | SERVICE_WIN32, SERVICE_STATE_ALL, 0, 0, &required, &count, &resume);
\r
295 error = GetLastError();
\r
296 if (error != ERROR_MORE_DATA) {
\r
297 print_message(stderr, NSSM_MESSAGE_ENUMSERVICESSTATUS_FAILED, error_string(GetLastError()));
\r
301 ENUM_SERVICE_STATUS *status = (ENUM_SERVICE_STATUS *) HeapAlloc(GetProcessHeap(), 0, required);
\r
303 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("ENUM_SERVICE_STATUS"), _T("open_service()"));
\r
307 bufsize = required;
\r
310 EnumServicesStatus() returns:
\r
311 1 when it retrieved data and there's no more data to come.
\r
312 0 and sets last error to ERROR_MORE_DATA when it retrieved data and
\r
313 there's more data to come.
\r
314 0 and sets last error to something else on error.
\r
316 int ret = EnumServicesStatus(services, SERVICE_DRIVER | SERVICE_FILE_SYSTEM_DRIVER | SERVICE_KERNEL_DRIVER | SERVICE_WIN32, SERVICE_STATE_ALL, status, bufsize, &required, &count, &resume);
\r
318 error = GetLastError();
\r
319 if (error != ERROR_MORE_DATA) {
\r
320 HeapFree(GetProcessHeap(), 0, status);
\r
321 print_message(stderr, NSSM_MESSAGE_ENUMSERVICESSTATUS_FAILED, error_string(GetLastError()));
\r
326 for (i = 0; i < count; i++) {
\r
327 if (str_equiv(status[i].lpDisplayName, service_name)) {
\r
328 if (_sntprintf_s(canonical_name, canonical_namelen, _TRUNCATE, _T("%s"), status[i].lpServiceName) < 0) {
\r
329 HeapFree(GetProcessHeap(), 0, status);
\r
330 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("canonical_name"), _T("open_service()"));
\r
334 HeapFree(GetProcessHeap(), 0, status);
\r
335 return open_service(services, canonical_name, access, 0, 0);
\r
342 /* Recurse so we can get an error message. */
\r
343 return open_service(services, service_name, access, 0, 0);
\r
346 QUERY_SERVICE_CONFIG *query_service_config(const TCHAR *service_name, SC_HANDLE service_handle) {
\r
347 QUERY_SERVICE_CONFIG *qsc;
\r
348 unsigned long bufsize;
\r
349 unsigned long error;
\r
351 QueryServiceConfig(service_handle, 0, 0, &bufsize);
\r
352 error = GetLastError();
\r
353 if (error == ERROR_INSUFFICIENT_BUFFER) {
\r
354 qsc = (QUERY_SERVICE_CONFIG *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, bufsize);
\r
356 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("QUERY_SERVICE_CONFIG"), _T("query_service_config()"), 0);
\r
361 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG_FAILED, service_name, error_string(error), 0);
\r
365 if (! QueryServiceConfig(service_handle, qsc, bufsize, &bufsize)) {
\r
366 HeapFree(GetProcessHeap(), 0, qsc);
\r
367 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG_FAILED, service_name, error_string(GetLastError()), 0);
\r
374 int set_service_dependencies(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR *buffer) {
\r
375 TCHAR *dependencies = _T("");
\r
376 unsigned long num_dependencies = 0;
\r
378 if (buffer && buffer[0]) {
\r
379 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
\r
381 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
386 Count the dependencies then allocate a buffer big enough for their
\r
387 canonical names, ie n * SERVICE_NAME_LENGTH.
\r
391 for (s = buffer; *s; s++) {
\r
392 num_dependencies++;
\r
393 if (*s == SC_GROUP_IDENTIFIER) groups = s;
\r
397 /* At least one dependency is a group so we need to verify them. */
\r
400 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, NSSM_REGISTRY_GROUPS, 0, KEY_READ, &key)) {
\r
401 _ftprintf(stderr, _T("%s: %s\n"), NSSM_REGISTRY_GROUPS, error_string(GetLastError()));
\r
405 unsigned long type;
\r
406 unsigned long groupslen;
\r
407 unsigned long ret = RegQueryValueEx(key, NSSM_REG_GROUPS, 0, &type, NULL, &groupslen);
\r
408 if (ret == ERROR_SUCCESS) {
\r
409 groups = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, groupslen);
\r
411 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("groups"), _T("set_service_dependencies()"));
\r
415 ret = RegQueryValueEx(key, NSSM_REG_GROUPS, 0, &type, (unsigned char *) groups, &groupslen);
\r
416 if (ret != ERROR_SUCCESS) {
\r
417 _ftprintf(stderr, _T("%s\\%s: %s"), NSSM_REGISTRY_GROUPS, NSSM_REG_GROUPS, error_string(GetLastError()));
\r
418 HeapFree(GetProcessHeap(), 0, groups);
\r
423 else if (ret != ERROR_FILE_NOT_FOUND) {
\r
424 _ftprintf(stderr, _T("%s\\%s: %s"), NSSM_REGISTRY_GROUPS, NSSM_REG_GROUPS, error_string(GetLastError()));
\r
433 unsigned long dependencieslen = (num_dependencies * SERVICE_NAME_LENGTH) + 2;
\r
434 dependencies = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dependencieslen * sizeof(TCHAR));
\r
437 TCHAR dependency[SERVICE_NAME_LENGTH];
\r
438 for (s = buffer; *s; s++) {
\r
440 if (*s == SC_GROUP_IDENTIFIER) {
\r
441 TCHAR *group = s + 1;
\r
445 for (TCHAR *g = groups; *g; g++) {
\r
446 if (str_equiv(g, group)) {
\r
448 /* Set canonical name. */
\r
449 memmove(group, g, _tcslen(g) * sizeof(TCHAR));
\r
457 if (ok) _sntprintf_s(dependency, _countof(dependency), _TRUNCATE, _T("%s"), s);
\r
459 HeapFree(GetProcessHeap(), 0, dependencies);
\r
460 if (groups) HeapFree(GetProcessHeap(), 0, groups);
\r
461 _ftprintf(stderr, _T("%s: %s"), s, error_string(ERROR_SERVICE_DEPENDENCY_DELETED));
\r
466 SC_HANDLE dependency_handle = open_service(services, s, SERVICE_QUERY_STATUS, dependency, _countof(dependency));
\r
467 if (! dependency_handle) {
\r
468 HeapFree(GetProcessHeap(), 0, dependencies);
\r
469 if (groups) HeapFree(GetProcessHeap(), 0, groups);
\r
470 CloseServiceHandle(services);
\r
471 _ftprintf(stderr, _T("%s: %s"), s, error_string(ERROR_SERVICE_DEPENDENCY_DELETED));
\r
476 size_t len = _tcslen(dependency) + 1;
\r
477 memmove(dependencies + i, dependency, len * sizeof(TCHAR));
\r
483 if (groups) HeapFree(GetProcessHeap(), 0, groups);
\r
484 CloseServiceHandle(services);
\r
487 if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, dependencies, 0, 0, 0)) {
\r
488 if (num_dependencies) HeapFree(GetProcessHeap(), 0, dependencies);
\r
489 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
493 if (num_dependencies) HeapFree(GetProcessHeap(), 0, dependencies);
\r
497 int get_service_dependencies(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR **buffer, unsigned long *bufsize, int type) {
\r
498 if (! buffer) return 1;
\r
499 if (! bufsize) return 2;
\r
504 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
505 if (! qsc) return 3;
\r
507 if (! qsc->lpDependencies) return 0;
\r
508 if (! qsc->lpDependencies[0]) return 0;
\r
510 /* lpDependencies is doubly NULL terminated. */
\r
511 while (qsc->lpDependencies[*bufsize]) {
\r
512 while (qsc->lpDependencies[*bufsize]) ++*bufsize;
\r
518 *buffer = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *bufsize * sizeof(TCHAR));
\r
521 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("lpDependencies"), _T("get_service_dependencies()"));
\r
525 if (type == DEPENDENCY_ALL) memmove(*buffer, qsc->lpDependencies, *bufsize * sizeof(TCHAR));
\r
530 for (s = qsc->lpDependencies; *s; s++) {
\r
531 /* Only copy the appropriate type of dependency. */
\r
532 if ((*s == SC_GROUP_IDENTIFIER && type & DEPENDENCY_GROUPS) || (*s != SC_GROUP_IDENTIFIER && type & DEPENDENCY_SERVICES)) {
\r
533 size_t len = _tcslen(s) + 1;
\r
534 *bufsize += (unsigned long) len;
\r
535 memmove(*buffer + i, s, len * sizeof(TCHAR));
\r
544 HeapFree(GetProcessHeap(), 0, qsc);
\r
549 int get_service_dependencies(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR **buffer, unsigned long *bufsize) {
\r
550 return get_service_dependencies(service_name, service_handle, buffer, bufsize, DEPENDENCY_ALL);
\r
553 int set_service_description(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR *buffer) {
\r
554 SERVICE_DESCRIPTION description;
\r
555 ZeroMemory(&description, sizeof(description));
\r
557 lpDescription must be NULL if we aren't changing, the new description
\r
560 if (buffer && buffer[0]) description.lpDescription = buffer;
\r
561 else description.lpDescription = _T("");
\r
563 if (ChangeServiceConfig2(service_handle, SERVICE_CONFIG_DESCRIPTION, &description)) return 0;
\r
565 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_DESCRIPTION_FAILED, service_name, error_string(GetLastError()), 0);
\r
569 int get_service_description(const TCHAR *service_name, SC_HANDLE service_handle, unsigned long len, TCHAR *buffer) {
\r
570 if (! buffer) return 1;
\r
572 unsigned long bufsize;
\r
573 QueryServiceConfig2(service_handle, SERVICE_CONFIG_DESCRIPTION, 0, 0, &bufsize);
\r
574 unsigned long error = GetLastError();
\r
575 if (error == ERROR_INSUFFICIENT_BUFFER) {
\r
576 SERVICE_DESCRIPTION *description = (SERVICE_DESCRIPTION *) HeapAlloc(GetProcessHeap(), 0, bufsize);
\r
577 if (! description) {
\r
578 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("SERVICE_CONFIG_DESCRIPTION"), _T("get_service_description()"));
\r
582 if (QueryServiceConfig2(service_handle, SERVICE_CONFIG_DESCRIPTION, (unsigned char *) description, bufsize, &bufsize)) {
\r
583 if (description->lpDescription) _sntprintf_s(buffer, len, _TRUNCATE, _T("%s"), description->lpDescription);
\r
584 else ZeroMemory(buffer, len * sizeof(TCHAR));
\r
585 HeapFree(GetProcessHeap(), 0, description);
\r
589 HeapFree(GetProcessHeap(), 0, description);
\r
590 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_CONFIG_DESCRIPTION"), error_string(error));
\r
595 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_CONFIG_DESCRIPTION"), error_string(error));
\r
602 int get_service_startup(const TCHAR *service_name, SC_HANDLE service_handle, const QUERY_SERVICE_CONFIG *qsc, unsigned long *startup) {
\r
603 if (! qsc) return 1;
\r
605 switch (qsc->dwStartType) {
\r
606 case SERVICE_DEMAND_START: *startup = NSSM_STARTUP_MANUAL; break;
\r
607 case SERVICE_DISABLED: *startup = NSSM_STARTUP_DISABLED; break;
\r
608 default: *startup = NSSM_STARTUP_AUTOMATIC;
\r
611 if (*startup != NSSM_STARTUP_AUTOMATIC) return 0;
\r
613 /* Check for delayed start. */
\r
614 unsigned long bufsize;
\r
615 unsigned long error;
\r
616 QueryServiceConfig2(service_handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, 0, 0, &bufsize);
\r
617 error = GetLastError();
\r
618 if (error == ERROR_INSUFFICIENT_BUFFER) {
\r
619 SERVICE_DELAYED_AUTO_START_INFO *info = (SERVICE_DELAYED_AUTO_START_INFO *) HeapAlloc(GetProcessHeap(), 0, bufsize);
\r
621 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("SERVICE_DELAYED_AUTO_START_INFO"), _T("get_service_startup()"));
\r
625 if (QueryServiceConfig2(service_handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, (unsigned char *) info, bufsize, &bufsize)) {
\r
626 if (info->fDelayedAutostart) *startup = NSSM_STARTUP_DELAYED;
\r
627 HeapFree(GetProcessHeap(), 0, info);
\r
631 error = GetLastError();
\r
632 if (error != ERROR_INVALID_LEVEL) {
\r
633 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_CONFIG_DELAYED_AUTO_START_INFO"), error_string(error));
\r
638 else if (error != ERROR_INVALID_LEVEL) {
\r
639 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_DELAYED_AUTO_START_INFO"), error_string(error));
\r
646 int get_service_username(const TCHAR *service_name, const QUERY_SERVICE_CONFIG *qsc, TCHAR **username, size_t *usernamelen) {
\r
647 if (! username) return 1;
\r
648 if (! usernamelen) return 1;
\r
653 if (! qsc) return 1;
\r
655 if (qsc->lpServiceStartName[0]) {
\r
656 if (is_localsystem(qsc->lpServiceStartName)) return 0;
\r
658 size_t len = _tcslen(qsc->lpServiceStartName);
\r
659 *username = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(TCHAR));
\r
661 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("username"), _T("get_service_username()"));
\r
665 memmove(*username, qsc->lpServiceStartName, (len + 1) * sizeof(TCHAR));
\r
666 *usernamelen = len;
\r
672 /* Set default values which aren't zero. */
\r
673 void set_nssm_service_defaults(nssm_service_t *service) {
\r
674 if (! service) return;
\r
676 service->type = SERVICE_WIN32_OWN_PROCESS;
\r
677 service->priority = NORMAL_PRIORITY_CLASS;
\r
678 service->stdin_sharing = NSSM_STDIN_SHARING;
\r
679 service->stdin_disposition = NSSM_STDIN_DISPOSITION;
\r
680 service->stdin_flags = NSSM_STDIN_FLAGS;
\r
681 service->stdout_sharing = NSSM_STDOUT_SHARING;
\r
682 service->stdout_disposition = NSSM_STDOUT_DISPOSITION;
\r
683 service->stdout_flags = NSSM_STDOUT_FLAGS;
\r
684 service->stderr_sharing = NSSM_STDERR_SHARING;
\r
685 service->stderr_disposition = NSSM_STDERR_DISPOSITION;
\r
686 service->stderr_flags = NSSM_STDERR_FLAGS;
\r
687 service->throttle_delay = NSSM_RESET_THROTTLE_RESTART;
\r
688 service->stop_method = ~0;
\r
689 service->kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;
\r
690 service->kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;
\r
691 service->kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;
\r
694 /* Allocate and zero memory for a service. */
\r
695 nssm_service_t *alloc_nssm_service() {
\r
696 nssm_service_t *service = (nssm_service_t *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(nssm_service_t));
\r
697 if (! service) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("service"), _T("alloc_nssm_service()"), 0);
\r
701 /* Free memory for a service. */
\r
702 void cleanup_nssm_service(nssm_service_t *service) {
\r
703 if (! service) return;
\r
704 if (service->username) HeapFree(GetProcessHeap(), 0, service->username);
\r
705 if (service->password) {
\r
706 SecureZeroMemory(service->password, service->passwordlen);
\r
707 HeapFree(GetProcessHeap(), 0, service->password);
\r
709 if (service->dependencies) HeapFree(GetProcessHeap(), 0, service->dependencies);
\r
710 if (service->env) HeapFree(GetProcessHeap(), 0, service->env);
\r
711 if (service->env_extra) HeapFree(GetProcessHeap(), 0, service->env_extra);
\r
712 if (service->handle) CloseHandle(service->handle);
\r
713 if (service->process_handle) CloseHandle(service->process_handle);
\r
714 if (service->wait_handle) UnregisterWait(service->process_handle);
\r
715 if (service->throttle_section_initialised) DeleteCriticalSection(&service->throttle_section);
\r
716 if (service->throttle_timer) CloseHandle(service->throttle_timer);
\r
717 if (service->initial_env) FreeEnvironmentStrings(service->initial_env);
\r
718 HeapFree(GetProcessHeap(), 0, service);
\r
721 /* About to install the service */
\r
722 int pre_install_service(int argc, TCHAR **argv) {
\r
723 nssm_service_t *service = alloc_nssm_service();
\r
724 set_nssm_service_defaults(service);
\r
725 if (argc) _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]);
\r
727 /* Show the dialogue box if we didn't give the service name and path */
\r
728 if (argc < 2) return nssm_gui(IDD_INSTALL, service);
\r
731 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("service"), _T("pre_install_service()"));
\r
734 _sntprintf_s(service->exe, _countof(service->exe), _TRUNCATE, _T("%s"), argv[1]);
\r
736 /* Arguments are optional */
\r
737 size_t flagslen = 0;
\r
740 for (i = 2; i < argc; i++) flagslen += _tcslen(argv[i]) + 1;
\r
741 if (! flagslen) flagslen = 1;
\r
742 if (flagslen > _countof(service->flags)) {
\r
743 print_message(stderr, NSSM_MESSAGE_FLAGS_TOO_LONG);
\r
747 for (i = 2; i < argc; i++) {
\r
748 size_t len = _tcslen(argv[i]);
\r
749 memmove(service->flags + s, argv[i], len * sizeof(TCHAR));
\r
751 if (i < argc - 1) service->flags[s++] = _T(' ');
\r
754 /* Work out directory name */
\r
755 _sntprintf_s(service->dir, _countof(service->dir), _TRUNCATE, _T("%s"), service->exe);
\r
756 strip_basename(service->dir);
\r
758 int ret = install_service(service);
\r
759 cleanup_nssm_service(service);
\r
763 /* About to edit the service. */
\r
764 int pre_edit_service(int argc, TCHAR **argv) {
\r
765 /* Require service name. */
\r
766 if (argc < 2) return usage(1);
\r
768 /* Are we editing on the command line? */
\r
769 enum { MODE_EDITING, MODE_GETTING, MODE_SETTING, MODE_RESETTING } mode = MODE_EDITING;
\r
770 const TCHAR *verb = argv[0];
\r
771 const TCHAR *service_name = argv[1];
\r
772 bool getting = false;
\r
773 bool unsetting = false;
\r
775 /* Minimum number of arguments. */
\r
777 /* Index of first value. */
\r
780 if (str_equiv(verb, _T("get"))) {
\r
782 mode = MODE_GETTING;
\r
784 else if (str_equiv(verb, _T("set"))) {
\r
786 mode = MODE_SETTING;
\r
788 else if (str_equiv(verb, _T("reset")) || str_equiv(verb, _T("unset"))) {
\r
790 mode = MODE_RESETTING;
\r
792 if (argc < mandatory) return usage(1);
\r
794 const TCHAR *parameter = 0;
\r
795 settings_t *setting = 0;
\r
798 /* Validate the parameter. */
\r
799 if (mandatory > 2) {
\r
800 bool additional_mandatory = false;
\r
802 parameter = argv[2];
\r
803 for (i = 0; settings[i].name; i++) {
\r
804 setting = &settings[i];
\r
805 if (! str_equiv(setting->name, parameter)) continue;
\r
806 if (((setting->additional & ADDITIONAL_GETTING) && mode == MODE_GETTING) || ((setting->additional & ADDITIONAL_SETTING) && mode == MODE_SETTING) || ((setting->additional & ADDITIONAL_RESETTING) && mode == MODE_RESETTING)) {
\r
807 additional_mandatory = true;
\r
812 if (! settings[i].name) {
\r
813 print_message(stderr, NSSM_MESSAGE_INVALID_PARAMETER, parameter);
\r
814 for (i = 0; settings[i].name; i++) _ftprintf(stderr, _T("%s\n"), settings[i].name);
\r
819 if (additional_mandatory) {
\r
820 if (argc < mandatory) {
\r
821 print_message(stderr, NSSM_MESSAGE_MISSING_SUBPARAMETER, parameter);
\r
824 additional = argv[3];
\r
827 else if (str_equiv(setting->name, NSSM_NATIVE_OBJECTNAME) && mode == MODE_SETTING) {
\r
828 additional = argv[3];
\r
832 additional = argv[remainder];
\r
833 if (argc < mandatory) return usage(1);
\r
837 nssm_service_t *service = alloc_nssm_service();
\r
838 _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), service_name);
\r
840 /* Open service manager */
\r
841 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
\r
843 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
847 /* Try to open the service */
\r
848 unsigned long access = SERVICE_QUERY_CONFIG;
\r
849 if (mode != MODE_GETTING) access |= SERVICE_CHANGE_CONFIG;
\r
850 service->handle = open_service(services, service->name, access, service->name, _countof(service->name));
\r
851 if (! service->handle) {
\r
852 CloseServiceHandle(services);
\r
856 /* Get system details. */
\r
857 QUERY_SERVICE_CONFIG *qsc = query_service_config(service->name, service->handle);
\r
859 CloseHandle(service->handle);
\r
860 CloseServiceHandle(services);
\r
864 service->type = qsc->dwServiceType;
\r
865 if (! (service->type & SERVICE_WIN32_OWN_PROCESS)) {
\r
866 if (mode != MODE_GETTING) {
\r
867 HeapFree(GetProcessHeap(), 0, qsc);
\r
868 CloseHandle(service->handle);
\r
869 CloseServiceHandle(services);
\r
870 print_message(stderr, NSSM_MESSAGE_CANNOT_EDIT, service->name, NSSM_WIN32_OWN_PROCESS, 0);
\r
875 if (get_service_startup(service->name, service->handle, qsc, &service->startup)) {
\r
876 if (mode != MODE_GETTING) {
\r
877 HeapFree(GetProcessHeap(), 0, qsc);
\r
878 CloseHandle(service->handle);
\r
879 CloseServiceHandle(services);
\r
884 if (get_service_username(service->name, qsc, &service->username, &service->usernamelen)) {
\r
885 if (mode != MODE_GETTING) {
\r
886 HeapFree(GetProcessHeap(), 0, qsc);
\r
887 CloseHandle(service->handle);
\r
888 CloseServiceHandle(services);
\r
893 _sntprintf_s(service->displayname, _countof(service->displayname), _TRUNCATE, _T("%s"), qsc->lpDisplayName);
\r
895 /* Get the canonical service name. We open it case insensitively. */
\r
896 unsigned long bufsize = _countof(service->name);
\r
897 GetServiceKeyName(services, service->displayname, service->name, &bufsize);
\r
899 /* Remember the executable in case it isn't NSSM. */
\r
900 _sntprintf_s(service->image, _countof(service->image), _TRUNCATE, _T("%s"), qsc->lpBinaryPathName);
\r
901 HeapFree(GetProcessHeap(), 0, qsc);
\r
903 /* Get extended system details. */
\r
904 if (get_service_description(service->name, service->handle, _countof(service->description), service->description)) {
\r
905 if (mode != MODE_GETTING) {
\r
906 CloseHandle(service->handle);
\r
907 CloseServiceHandle(services);
\r
912 if (get_service_dependencies(service->name, service->handle, &service->dependencies, &service->dependencieslen)) {
\r
913 if (mode != MODE_GETTING) {
\r
914 CloseHandle(service->handle);
\r
915 CloseServiceHandle(services);
\r
920 /* Get NSSM details. */
\r
921 get_parameters(service, 0);
\r
923 CloseServiceHandle(services);
\r
925 if (! service->exe[0]) {
\r
926 service->native = true;
\r
927 if (mode != MODE_GETTING) print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE, service->name, NSSM, service->image);
\r
930 /* Editing with the GUI. */
\r
931 if (mode == MODE_EDITING) {
\r
932 nssm_gui(IDD_EDIT, service);
\r
936 /* Trying to manage App* parameters for a non-NSSM service. */
\r
937 if (! setting->native && service->native) {
\r
938 CloseHandle(service->handle);
\r
939 print_message(stderr, NSSM_MESSAGE_NATIVE_PARAMETER, setting->name, NSSM);
\r
947 if (mode == MODE_GETTING) {
\r
948 if (! service->native) {
\r
949 key = open_registry(service->name, KEY_READ);
\r
950 if (! key) return 4;
\r
953 if (setting->native) ret = get_setting(service->name, service->handle, setting, &value, additional);
\r
954 else ret = get_setting(service->name, key, setting, &value, additional);
\r
956 CloseHandle(service->handle);
\r
960 switch (setting->type) {
\r
961 case REG_EXPAND_SZ:
\r
964 _tprintf(_T("%s\n"), value.string ? value.string : _T(""));
\r
965 HeapFree(GetProcessHeap(), 0, value.string);
\r
969 _tprintf(_T("%u\n"), value.numeric);
\r
973 if (! service->native) RegCloseKey(key);
\r
974 CloseHandle(service->handle);
\r
978 /* Build the value. */
\r
979 if (mode == MODE_RESETTING) {
\r
980 /* Unset the parameter. */
\r
983 else if (remainder == argc) {
\r
987 /* Set the parameter. */
\r
989 size_t delimiterlen = (setting->additional & ADDITIONAL_CRLF) ? 2 : 1;
\r
990 for (i = remainder; i < argc; i++) len += _tcslen(argv[i]) + delimiterlen;
\r
993 value.string = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, len * sizeof(TCHAR));
\r
994 if (! value.string) {
\r
995 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("value"), _T("edit_service()"));
\r
996 CloseHandle(service->handle);
\r
1001 for (i = remainder; i < argc; i++) {
\r
1002 size_t len = _tcslen(argv[i]);
\r
1003 memmove(value.string + s, argv[i], len * sizeof(TCHAR));
\r
1005 if (i < argc - 1) {
\r
1006 if (setting->additional & ADDITIONAL_CRLF) {
\r
1007 value.string[s++] = _T('\r');
\r
1008 value.string[s++] = _T('\n');
\r
1010 else value.string[s++] = _T(' ');
\r
1013 value.string[s] = _T('\0');
\r
1016 if (! service->native) {
\r
1017 key = open_registry(service->name, KEY_WRITE);
\r
1019 if (value.string) HeapFree(GetProcessHeap(), 0, value.string);
\r
1024 if (setting->native) ret = set_setting(service->name, service->handle, setting, &value, additional);
\r
1025 else ret = set_setting(service->name, key, setting, &value, additional);
\r
1026 if (value.string) HeapFree(GetProcessHeap(), 0, value.string);
\r
1028 if (! service->native) RegCloseKey(key);
\r
1029 CloseHandle(service->handle);
\r
1033 if (! service->native) RegCloseKey(key);
\r
1034 CloseHandle(service->handle);
\r
1039 /* About to remove the service */
\r
1040 int pre_remove_service(int argc, TCHAR **argv) {
\r
1041 nssm_service_t *service = alloc_nssm_service();
\r
1042 set_nssm_service_defaults(service);
\r
1043 if (argc) _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]);
\r
1045 /* Show dialogue box if we didn't pass service name and "confirm" */
\r
1046 if (argc < 2) return nssm_gui(IDD_REMOVE, service);
\r
1047 if (str_equiv(argv[1], _T("confirm"))) {
\r
1048 int ret = remove_service(service);
\r
1049 cleanup_nssm_service(service);
\r
1052 print_message(stderr, NSSM_MESSAGE_PRE_REMOVE_SERVICE);
\r
1056 /* Install the service */
\r
1057 int install_service(nssm_service_t *service) {
\r
1058 if (! service) return 1;
\r
1060 /* Open service manager */
\r
1061 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE);
\r
1063 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
1064 cleanup_nssm_service(service);
\r
1068 /* Get path of this program */
\r
1069 GetModuleFileName(0, service->image, _countof(service->image));
\r
1071 /* Create the service - settings will be changed in edit_service() */
\r
1072 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
1073 if (! service->handle) {
\r
1074 print_message(stderr, NSSM_MESSAGE_CREATESERVICE_FAILED, error_string(GetLastError()));
\r
1075 CloseServiceHandle(services);
\r
1079 if (edit_service(service, false)) {
\r
1080 DeleteService(service->handle);
\r
1081 CloseServiceHandle(services);
\r
1085 print_message(stdout, NSSM_MESSAGE_SERVICE_INSTALLED, service->name);
\r
1088 CloseServiceHandle(services);
\r
1093 /* Edit the service. */
\r
1094 int edit_service(nssm_service_t *service, bool editing) {
\r
1095 if (! service) return 1;
\r
1098 The only two valid flags for service type are SERVICE_WIN32_OWN_PROCESS
\r
1099 and SERVICE_INTERACTIVE_PROCESS.
\r
1101 service->type &= SERVICE_INTERACTIVE_PROCESS;
\r
1102 service->type |= SERVICE_WIN32_OWN_PROCESS;
\r
1104 /* Startup type. */
\r
1105 unsigned long startup;
\r
1106 switch (service->startup) {
\r
1107 case NSSM_STARTUP_MANUAL: startup = SERVICE_DEMAND_START; break;
\r
1108 case NSSM_STARTUP_DISABLED: startup = SERVICE_DISABLED; break;
\r
1109 default: startup = SERVICE_AUTO_START;
\r
1112 /* Display name. */
\r
1113 if (! service->displayname[0]) _sntprintf_s(service->displayname, _countof(service->displayname), _TRUNCATE, _T("%s"), service->name);
\r
1116 Username must be NULL if we aren't changing or an account name.
\r
1117 We must explicitly use LOCALSYSTEM to change it when we are editing.
\r
1118 Password must be NULL if we aren't changing, a password or "".
\r
1119 Empty passwords are valid but we won't allow them in the GUI.
\r
1121 TCHAR *username = 0;
\r
1122 TCHAR *password = 0;
\r
1123 if (service->usernamelen) {
\r
1124 username = service->username;
\r
1125 if (service->passwordlen) password = service->password;
\r
1126 else password = _T("");
\r
1128 else if (editing) username = NSSM_LOCALSYSTEM_ACCOUNT;
\r
1130 if (well_known_username(username)) password = _T("");
\r
1132 if (grant_logon_as_service(username)) {
\r
1133 print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username);
\r
1138 TCHAR *dependencies = _T("");
\r
1139 if (service->dependencieslen) dependencies = 0; /* Change later. */
\r
1141 if (! ChangeServiceConfig(service->handle, service->type, startup, SERVICE_NO_CHANGE, 0, 0, 0, dependencies, username, password, service->displayname)) {
\r
1142 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
1146 if (service->dependencieslen) {
\r
1147 if (set_service_dependencies(service->name, service->handle, service->dependencies)) return 5;
\r
1150 if (service->description[0] || editing) {
\r
1151 set_service_description(service->name, service->handle, service->description);
\r
1154 SERVICE_DELAYED_AUTO_START_INFO delayed;
\r
1155 ZeroMemory(&delayed, sizeof(delayed));
\r
1156 if (service->startup == NSSM_STARTUP_DELAYED) delayed.fDelayedAutostart = 1;
\r
1157 else delayed.fDelayedAutostart = 0;
\r
1158 /* Delayed startup isn't supported until Vista. */
\r
1159 if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, &delayed)) {
\r
1160 unsigned long error = GetLastError();
\r
1161 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
\r
1162 if (error != ERROR_INVALID_LEVEL) {
\r
1163 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_DELAYED_AUTO_START_INFO_FAILED, service->name, error_string(error), 0);
\r
1167 /* Don't mess with parameters which aren't ours. */
\r
1168 if (! service->native) {
\r
1169 /* Now we need to put the parameters into the registry */
\r
1170 if (create_parameters(service, editing)) {
\r
1171 print_message(stderr, NSSM_MESSAGE_CREATE_PARAMETERS_FAILED);
\r
1175 set_service_recovery(service);
\r
1181 /* Control a service. */
\r
1182 int control_service(unsigned long control, int argc, TCHAR **argv) {
\r
1183 if (argc < 1) return usage(1);
\r
1184 TCHAR *service_name = argv[0];
\r
1185 TCHAR canonical_name[SERVICE_NAME_LENGTH];
\r
1187 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
\r
1189 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
1193 unsigned long access = SERVICE_QUERY_STATUS;
\r
1194 switch (control) {
\r
1195 case NSSM_SERVICE_CONTROL_START:
\r
1196 access |= SERVICE_START;
\r
1199 case SERVICE_CONTROL_CONTINUE:
\r
1200 case SERVICE_CONTROL_PAUSE:
\r
1201 access |= SERVICE_PAUSE_CONTINUE;
\r
1204 case SERVICE_CONTROL_STOP:
\r
1205 access |= SERVICE_STOP;
\r
1208 case NSSM_SERVICE_CONTROL_ROTATE:
\r
1209 access |= SERVICE_USER_DEFINED_CONTROL;
\r
1213 SC_HANDLE service_handle = open_service(services, service_name, access, canonical_name, _countof(canonical_name));
\r
1214 if (! service_handle) {
\r
1215 CloseServiceHandle(services);
\r
1220 unsigned long error;
\r
1221 SERVICE_STATUS service_status;
\r
1222 if (control == NSSM_SERVICE_CONTROL_START) {
\r
1223 unsigned long initial_status = SERVICE_STOPPED;
\r
1224 ret = StartService(service_handle, (unsigned long) argc, (const TCHAR **) argv);
\r
1225 error = GetLastError();
\r
1226 CloseServiceHandle(services);
\r
1228 if (error == ERROR_IO_PENDING) {
\r
1230 Older versions of Windows return immediately with ERROR_IO_PENDING
\r
1231 indicate that the operation is still in progress. Newer versions
\r
1232 will return it if there really is a delay.
\r
1235 error = ERROR_SUCCESS;
\r
1239 int response = await_service_control_response(control, service_handle, &service_status, initial_status);
\r
1240 CloseHandle(service_handle);
\r
1243 print_message(stderr, NSSM_MESSAGE_BAD_CONTROL_RESPONSE, canonical_name, service_status_text(service_status.dwCurrentState), service_control_text(control));
\r
1246 else _tprintf(_T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1250 CloseHandle(service_handle);
\r
1251 _ftprintf(stderr, _T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1255 else if (control == SERVICE_CONTROL_INTERROGATE) {
\r
1257 We could actually send an INTERROGATE control but that won't return
\r
1258 any information if the service is stopped and we don't care about
\r
1259 the extra details it might give us in any case. So we'll fake it.
\r
1261 ret = QueryServiceStatus(service_handle, &service_status);
\r
1262 error = GetLastError();
\r
1265 _tprintf(_T("%s\n"), service_status_text(service_status.dwCurrentState));
\r
1269 _ftprintf(stderr, _T("%s: %s\n"), canonical_name, error_string(error));
\r
1274 ret = ControlService(service_handle, control, &service_status);
\r
1275 unsigned long initial_status = service_status.dwCurrentState;
\r
1276 error = GetLastError();
\r
1277 CloseServiceHandle(services);
\r
1279 if (error == ERROR_IO_PENDING) {
\r
1281 error = ERROR_SUCCESS;
\r
1285 int response = await_service_control_response(control, service_handle, &service_status, initial_status);
\r
1286 CloseHandle(service_handle);
\r
1289 print_message(stderr, NSSM_MESSAGE_BAD_CONTROL_RESPONSE, canonical_name, service_status_text(service_status.dwCurrentState), service_control_text(control));
\r
1292 else _tprintf(_T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1296 CloseHandle(service_handle);
\r
1297 _ftprintf(stderr, _T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1298 if (error == ERROR_SERVICE_NOT_ACTIVE) {
\r
1299 if (control == SERVICE_CONTROL_SHUTDOWN || control == SERVICE_CONTROL_STOP) return 0;
\r
1306 /* Remove the service */
\r
1307 int remove_service(nssm_service_t *service) {
\r
1308 if (! service) return 1;
\r
1310 /* Open service manager */
\r
1311 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
\r
1313 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
1317 /* Try to open the service */
\r
1318 service->handle = open_service(services, service->name, DELETE, service->name, _countof(service->name));
\r
1319 if (! service->handle) {
\r
1320 CloseServiceHandle(services);
\r
1324 /* Get the canonical service name. We open it case insensitively. */
\r
1325 unsigned long bufsize = _countof(service->displayname);
\r
1326 GetServiceDisplayName(services, service->name, service->displayname, &bufsize);
\r
1327 bufsize = _countof(service->name);
\r
1328 GetServiceKeyName(services, service->displayname, service->name, &bufsize);
\r
1330 /* Try to delete the service */
\r
1331 if (! DeleteService(service->handle)) {
\r
1332 print_message(stderr, NSSM_MESSAGE_DELETESERVICE_FAILED);
\r
1333 CloseServiceHandle(services);
\r
1338 CloseServiceHandle(services);
\r
1340 print_message(stdout, NSSM_MESSAGE_SERVICE_REMOVED, service->name);
\r
1344 /* Service initialisation */
\r
1345 void WINAPI service_main(unsigned long argc, TCHAR **argv) {
\r
1346 nssm_service_t *service = alloc_nssm_service();
\r
1347 if (! service) return;
\r
1349 if (_sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]) < 0) {
\r
1350 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("service->name"), _T("service_main()"), 0);
\r
1354 /* We can use a condition variable in a critical section on Vista or later. */
\r
1355 if (imports.SleepConditionVariableCS && imports.WakeConditionVariable) use_critical_section = true;
\r
1356 else use_critical_section = false;
\r
1358 /* Initialise status */
\r
1359 ZeroMemory(&service->status, sizeof(service->status));
\r
1360 service->status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;
\r
1361 service->status.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE;
\r
1362 service->status.dwWin32ExitCode = NO_ERROR;
\r
1363 service->status.dwServiceSpecificExitCode = 0;
\r
1364 service->status.dwCheckPoint = 0;
\r
1365 service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;
\r
1367 /* Signal we AREN'T running the server */
\r
1368 service->process_handle = 0;
\r
1371 /* Register control handler */
\r
1372 service->status_handle = RegisterServiceCtrlHandlerEx(NSSM, service_control_handler, (void *) service);
\r
1373 if (! service->status_handle) {
\r
1374 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_REGISTERSERVICECTRLHANDER_FAILED, error_string(GetLastError()), 0);
\r
1378 log_service_control(service->name, 0, true);
\r
1380 service->status.dwCurrentState = SERVICE_START_PENDING;
\r
1381 service->status.dwWaitHint = service->throttle_delay + NSSM_WAITHINT_MARGIN;
\r
1382 SetServiceStatus(service->status_handle, &service->status);
\r
1385 /* Try to create the exit action parameters; we don't care if it fails */
\r
1386 create_exit_action(service->name, exit_action_strings[0], false);
\r
1388 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT);
\r
1390 service->handle = open_service(services, service->name, SERVICE_CHANGE_CONFIG, 0, 0);
\r
1391 set_service_recovery(service);
\r
1392 CloseServiceHandle(services);
\r
1396 /* Used for signalling a resume if the service pauses when throttled. */
\r
1397 if (use_critical_section) {
\r
1398 InitializeCriticalSection(&service->throttle_section);
\r
1399 service->throttle_section_initialised = true;
\r
1402 service->throttle_timer = CreateWaitableTimer(0, 1, 0);
\r
1403 if (! service->throttle_timer) {
\r
1404 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_CREATEWAITABLETIMER_FAILED, service->name, error_string(GetLastError()), 0);
\r
1408 /* Remember our initial environment. */
\r
1409 service->initial_env = GetEnvironmentStrings();
\r
1411 monitor_service(service);
\r
1414 /* Make sure service recovery actions are taken where necessary */
\r
1415 void set_service_recovery(nssm_service_t *service) {
\r
1416 SERVICE_FAILURE_ACTIONS_FLAG flag;
\r
1417 ZeroMemory(&flag, sizeof(flag));
\r
1418 flag.fFailureActionsOnNonCrashFailures = true;
\r
1420 /* This functionality was added in Vista so the call may fail */
\r
1421 if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, &flag)) {
\r
1422 unsigned long error = GetLastError();
\r
1423 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
\r
1424 if (error != ERROR_INVALID_LEVEL) {
\r
1425 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_FAILURE_ACTIONS_FAILED, service->name, error_string(error), 0);
\r
1430 int monitor_service(nssm_service_t *service) {
\r
1431 /* Set service status to started */
\r
1432 int ret = start_service(service);
\r
1435 _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%d"), ret);
\r
1436 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_START_SERVICE_FAILED, service->exe, service->name, ret, 0);
\r
1439 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_STARTED_SERVICE, service->exe, service->flags, service->name, service->dir, 0);
\r
1441 /* Monitor service */
\r
1442 if (! RegisterWaitForSingleObject(&service->wait_handle, service->process_handle, end_service, (void *) service, INFINITE, WT_EXECUTEONLYONCE | WT_EXECUTELONGFUNCTION)) {
\r
1443 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_REGISTERWAITFORSINGLEOBJECT_FAILED, service->name, service->exe, error_string(GetLastError()), 0);
\r
1449 TCHAR *service_control_text(unsigned long control) {
\r
1450 switch (control) {
\r
1451 /* HACK: there is no SERVICE_CONTROL_START constant */
\r
1452 case NSSM_SERVICE_CONTROL_START: return _T("START");
\r
1453 case SERVICE_CONTROL_STOP: return _T("STOP");
\r
1454 case SERVICE_CONTROL_SHUTDOWN: return _T("SHUTDOWN");
\r
1455 case SERVICE_CONTROL_PAUSE: return _T("PAUSE");
\r
1456 case SERVICE_CONTROL_CONTINUE: return _T("CONTINUE");
\r
1457 case SERVICE_CONTROL_INTERROGATE: return _T("INTERROGATE");
\r
1458 case NSSM_SERVICE_CONTROL_ROTATE: return _T("ROTATE");
\r
1459 default: return 0;
\r
1463 TCHAR *service_status_text(unsigned long status) {
\r
1465 case SERVICE_STOPPED: return _T("SERVICE_STOPPED");
\r
1466 case SERVICE_START_PENDING: return _T("SERVICE_START_PENDING");
\r
1467 case SERVICE_STOP_PENDING: return _T("SERVICE_STOP_PENDING");
\r
1468 case SERVICE_RUNNING: return _T("SERVICE_RUNNING");
\r
1469 case SERVICE_CONTINUE_PENDING: return _T("SERVICE_CONTINUE_PENDING");
\r
1470 case SERVICE_PAUSE_PENDING: return _T("SERVICE_PAUSE_PENDING");
\r
1471 case SERVICE_PAUSED: return _T("SERVICE_PAUSED");
\r
1472 default: return 0;
\r
1476 void log_service_control(TCHAR *service_name, unsigned long control, bool handled) {
\r
1477 TCHAR *text = service_control_text(control);
\r
1478 unsigned long event;
\r
1481 /* "0x" + 8 x hex + NULL */
\r
1482 text = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, 11 * sizeof(TCHAR));
\r
1484 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);
\r
1487 if (_sntprintf_s(text, 11, _TRUNCATE, _T("0x%08x"), control) < 0) {
\r
1488 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);
\r
1489 HeapFree(GetProcessHeap(), 0, text);
\r
1493 event = NSSM_EVENT_SERVICE_CONTROL_UNKNOWN;
\r
1495 else if (handled) event = NSSM_EVENT_SERVICE_CONTROL_HANDLED;
\r
1496 else event = NSSM_EVENT_SERVICE_CONTROL_NOT_HANDLED;
\r
1498 log_event(EVENTLOG_INFORMATION_TYPE, event, service_name, text, 0);
\r
1500 if (event == NSSM_EVENT_SERVICE_CONTROL_UNKNOWN) {
\r
1501 HeapFree(GetProcessHeap(), 0, text);
\r
1505 /* Service control handler */
\r
1506 unsigned long WINAPI service_control_handler(unsigned long control, unsigned long event, void *data, void *context) {
\r
1507 nssm_service_t *service = (nssm_service_t *) context;
\r
1509 switch (control) {
\r
1510 case SERVICE_CONTROL_INTERROGATE:
\r
1511 /* We always keep the service status up-to-date so this is a no-op. */
\r
1514 case SERVICE_CONTROL_SHUTDOWN:
\r
1515 case SERVICE_CONTROL_STOP:
\r
1516 log_service_control(service->name, control, true);
\r
1518 We MUST acknowledge the stop request promptly but we're committed to
\r
1519 waiting for the application to exit. Spawn a new thread to wait
\r
1520 while we acknowledge the request.
\r
1522 if (! CreateThread(NULL, 0, shutdown_service, context, 0, NULL)) {
\r
1523 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETHREAD_FAILED, error_string(GetLastError()), 0);
\r
1526 We couldn't create a thread to tidy up so we'll have to force the tidyup
\r
1527 to complete in time in this thread.
\r
1529 service->kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;
\r
1530 service->kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;
\r
1531 service->kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;
\r
1533 stop_service(service, 0, true, true);
\r
1537 case SERVICE_CONTROL_CONTINUE:
\r
1538 log_service_control(service->name, control, true);
\r
1539 service->throttle = 0;
\r
1540 if (use_critical_section) imports.WakeConditionVariable(&service->throttle_condition);
\r
1542 if (! service->throttle_timer) return ERROR_CALL_NOT_IMPLEMENTED;
\r
1543 ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));
\r
1544 SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);
\r
1546 /* We can't continue if the application is running! */
\r
1547 if (! service->process_handle) service->status.dwCurrentState = SERVICE_CONTINUE_PENDING;
\r
1548 service->status.dwWaitHint = throttle_milliseconds(service->throttle) + NSSM_WAITHINT_MARGIN;
\r
1549 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESET_THROTTLE, service->name, 0);
\r
1550 SetServiceStatus(service->status_handle, &service->status);
\r
1553 case SERVICE_CONTROL_PAUSE:
\r
1555 We don't accept pause messages but it isn't possible to register
\r
1556 only for continue messages so we have to handle this case.
\r
1558 log_service_control(service->name, control, false);
\r
1559 return ERROR_CALL_NOT_IMPLEMENTED;
\r
1561 case NSSM_SERVICE_CONTROL_ROTATE:
\r
1562 log_service_control(service->name, control, true);
\r
1563 if (service->rotate_stdout_online == NSSM_ROTATE_ONLINE) service->rotate_stdout_online = NSSM_ROTATE_ONLINE_ASAP;
\r
1564 if (service->rotate_stderr_online == NSSM_ROTATE_ONLINE) service->rotate_stderr_online = NSSM_ROTATE_ONLINE_ASAP;
\r
1568 /* Unknown control */
\r
1569 log_service_control(service->name, control, false);
\r
1570 return ERROR_CALL_NOT_IMPLEMENTED;
\r
1573 /* Start the service */
\r
1574 int start_service(nssm_service_t *service) {
\r
1575 service->stopping = false;
\r
1576 service->allow_restart = true;
\r
1578 if (service->process_handle) return 0;
\r
1580 /* Allocate a STARTUPINFO structure for a new process */
\r
1582 ZeroMemory(&si, sizeof(si));
\r
1583 si.cb = sizeof(si);
\r
1585 /* Allocate a PROCESSINFO structure for the process */
\r
1586 PROCESS_INFORMATION pi;
\r
1587 ZeroMemory(&pi, sizeof(pi));
\r
1589 /* Get startup parameters */
\r
1590 int ret = get_parameters(service, &si);
\r
1592 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_PARAMETERS_FAILED, service->name, 0);
\r
1593 return stop_service(service, 2, true, true);
\r
1596 /* Launch executable with arguments */
\r
1597 TCHAR cmd[CMD_LENGTH];
\r
1598 if (_sntprintf_s(cmd, _countof(cmd), _TRUNCATE, _T("\"%s\" %s"), service->exe, service->flags) < 0) {
\r
1599 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("command line"), _T("start_service"), 0);
\r
1600 return stop_service(service, 2, true, true);
\r
1603 throttle_restart(service);
\r
1605 /* Set the environment. */
\r
1606 if (service->env) duplicate_environment(service->env);
\r
1607 if (service->env_extra) set_environment_block(service->env_extra);
\r
1609 /* Set up I/O redirection. */
\r
1610 if (get_output_handles(service, &si)) {
\r
1611 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_OUTPUT_HANDLES_FAILED, service->name, 0);
\r
1612 if (! service->no_console) FreeConsole();
\r
1613 close_output_handles(&si);
\r
1614 return stop_service(service, 4, true, true);
\r
1617 bool inherit_handles = false;
\r
1618 if (si.dwFlags & STARTF_USESTDHANDLES) inherit_handles = true;
\r
1619 unsigned long flags = service->priority & priority_mask();
\r
1620 if (service->affinity) flags |= CREATE_SUSPENDED;
\r
1621 if (! CreateProcess(0, cmd, 0, 0, inherit_handles, flags, 0, service->dir, &si, &pi)) {
\r
1622 unsigned long exitcode = 3;
\r
1623 unsigned long error = GetLastError();
\r
1624 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service->name, service->exe, error_string(error), 0);
\r
1625 close_output_handles(&si);
\r
1626 duplicate_environment_strings(service->initial_env);
\r
1627 return stop_service(service, exitcode, true, true);
\r
1629 service->process_handle = pi.hProcess;
\r
1630 service->pid = pi.dwProcessId;
\r
1632 if (get_process_creation_time(service->process_handle, &service->creation_time)) ZeroMemory(&service->creation_time, sizeof(service->creation_time));
\r
1634 close_output_handles(&si);
\r
1636 if (! service->no_console) FreeConsole();
\r
1638 /* Restore our environment. */
\r
1639 duplicate_environment_strings(service->initial_env);
\r
1641 if (service->affinity) {
\r
1643 We are explicitly storing service->affinity as a 64-bit unsigned integer
\r
1644 so that we can parse it regardless of whether we're running in 32-bit
\r
1645 or 64-bit mode. The arguments to SetProcessAffinityMask(), however, are
\r
1646 defined as type DWORD_PTR and hence limited to 32 bits on a 32-bit system
\r
1647 (or when running the 32-bit NSSM).
\r
1649 The result is a lot of seemingly-unnecessary casting throughout the code
\r
1650 and potentially confusion when we actually try to start the service.
\r
1651 Having said that, however, it's unlikely that we're actually going to
\r
1652 run in 32-bit mode on a system which has more than 32 CPUs so the
\r
1653 likelihood of seeing a confusing situation is somewhat diminished.
\r
1655 DWORD_PTR affinity, system_affinity;
\r
1657 if (GetProcessAffinityMask(service->process_handle, &affinity, &system_affinity)) affinity = service->affinity & system_affinity;
\r
1659 affinity = (DWORD_PTR) service->affinity;
\r
1660 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GETPROCESSAFFINITYMASK_FAILED, service->name, error_string(GetLastError()), 0);
\r
1663 if (! SetProcessAffinityMask(service->process_handle, affinity)) {
\r
1664 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_SETPROCESSAFFINITYMASK_FAILED, service->name, error_string(GetLastError()), 0);
\r
1667 ResumeThread(pi.hThread);
\r
1671 Wait for a clean startup before changing the service status to RUNNING
\r
1672 but be mindful of the fact that we are blocking the service control manager
\r
1673 so abandon the wait before too much time has elapsed.
\r
1675 unsigned long delay = service->throttle_delay;
\r
1676 if (delay > NSSM_SERVICE_STATUS_DEADLINE) {
\r
1677 TCHAR delay_milliseconds[16];
\r
1678 _sntprintf_s(delay_milliseconds, _countof(delay_milliseconds), _TRUNCATE, _T("%lu"), delay);
\r
1679 TCHAR deadline_milliseconds[16];
\r
1680 _sntprintf_s(deadline_milliseconds, _countof(deadline_milliseconds), _TRUNCATE, _T("%lu"), NSSM_SERVICE_STATUS_DEADLINE);
\r
1681 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_STARTUP_DELAY_TOO_LONG, service->name, delay_milliseconds, NSSM, deadline_milliseconds, 0);
\r
1682 delay = NSSM_SERVICE_STATUS_DEADLINE;
\r
1684 unsigned long deadline = WaitForSingleObject(service->process_handle, delay);
\r
1686 /* Signal successful start */
\r
1687 service->status.dwCurrentState = SERVICE_RUNNING;
\r
1688 SetServiceStatus(service->status_handle, &service->status);
\r
1690 /* Continue waiting for a clean startup. */
\r
1691 if (deadline == WAIT_TIMEOUT) {
\r
1692 if (service->throttle_delay > delay) {
\r
1693 if (WaitForSingleObject(service->process_handle, service->throttle_delay - delay) == WAIT_TIMEOUT) service->throttle = 0;
\r
1695 else service->throttle = 0;
\r
1698 /* Ensure the restart delay is always applied. */
\r
1699 if (service->restart_delay && ! service->throttle) service->throttle++;
\r
1704 /* Stop the service */
\r
1705 int stop_service(nssm_service_t *service, unsigned long exitcode, bool graceful, bool default_action) {
\r
1706 service->allow_restart = false;
\r
1707 if (service->wait_handle) {
\r
1708 UnregisterWait(service->wait_handle);
\r
1709 service->wait_handle = 0;
\r
1712 service->rotate_stdout_online = service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;
\r
1714 if (default_action && ! exitcode && ! graceful) {
\r
1715 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
1719 /* Signal we are stopping */
\r
1721 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
1722 service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;
\r
1723 SetServiceStatus(service->status_handle, &service->status);
\r
1726 /* Nothing to do if service isn't running */
\r
1727 if (service->pid) {
\r
1728 /* Shut down service */
\r
1729 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_TERMINATEPROCESS, service->name, service->exe, 0);
\r
1730 kill_process(service, service->process_handle, service->pid, 0);
\r
1732 else log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_PROCESS_ALREADY_STOPPED, service->name, service->exe, 0);
\r
1734 end_service((void *) service, true);
\r
1736 /* Signal we stopped */
\r
1738 service->status.dwCurrentState = SERVICE_STOPPED;
\r
1740 service->status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
\r
1741 service->status.dwServiceSpecificExitCode = exitcode;
\r
1744 service->status.dwWin32ExitCode = NO_ERROR;
\r
1745 service->status.dwServiceSpecificExitCode = 0;
\r
1747 SetServiceStatus(service->status_handle, &service->status);
\r
1753 /* Callback function triggered when the server exits */
\r
1754 void CALLBACK end_service(void *arg, unsigned char why) {
\r
1755 nssm_service_t *service = (nssm_service_t *) arg;
\r
1757 if (service->stopping) return;
\r
1759 service->stopping = true;
\r
1761 service->rotate_stdout_online = service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;
\r
1763 /* Use now as a dummy exit time. */
\r
1764 GetSystemTimeAsFileTime(&service->exit_time);
\r
1766 /* Check exit code */
\r
1767 unsigned long exitcode = 0;
\r
1769 if (service->process_handle) {
\r
1770 GetExitCodeProcess(service->process_handle, &exitcode);
\r
1771 /* Check real exit time. */
\r
1772 if (exitcode != STILL_ACTIVE) get_process_exit_time(service->process_handle, &service->exit_time);
\r
1773 CloseHandle(service->process_handle);
\r
1776 service->process_handle = 0;
\r
1779 Log that the service ended BEFORE logging about killing the process
\r
1780 tree. See below for the possible values of the why argument.
\r
1783 _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), exitcode);
\r
1784 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ENDED_SERVICE, service->exe, service->name, code, 0);
\r
1788 if (exitcode == STILL_ACTIVE) exitcode = 0;
\r
1789 if (service->pid) kill_process_tree(service, service->pid, exitcode, service->pid);
\r
1793 The why argument is true if our wait timed out or false otherwise.
\r
1794 Our wait is infinite so why will never be true when called by the system.
\r
1795 If it is indeed true, assume we were called from stop_service() because
\r
1796 this is a controlled shutdown, and don't take any restart action.
\r
1799 if (! service->allow_restart) return;
\r
1801 /* What action should we take? */
\r
1802 int action = NSSM_EXIT_RESTART;
\r
1803 TCHAR action_string[ACTION_LEN];
\r
1804 bool default_action;
\r
1805 if (! get_exit_action(service->name, &exitcode, action_string, &default_action)) {
\r
1806 for (int i = 0; exit_action_strings[i]; i++) {
\r
1807 if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
1815 /* Try to restart the service or return failure code to service manager */
\r
1816 case NSSM_EXIT_RESTART:
\r
1817 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_RESTART, service->name, code, exit_action_strings[action], service->exe, 0);
\r
1818 while (monitor_service(service)) {
\r
1819 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_RESTART_SERVICE_FAILED, service->exe, service->name, 0);
\r
1824 /* Do nothing, just like srvany would */
\r
1825 case NSSM_EXIT_IGNORE:
\r
1826 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_IGNORE, service->name, code, exit_action_strings[action], service->exe, 0);
\r
1830 /* Tell the service manager we are finished */
\r
1831 case NSSM_EXIT_REALLY:
\r
1832 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_REALLY, service->name, code, exit_action_strings[action], 0);
\r
1833 stop_service(service, exitcode, true, default_action);
\r
1836 /* Fake a crash so pre-Vista service managers will run recovery actions. */
\r
1837 case NSSM_EXIT_UNCLEAN:
\r
1838 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_UNCLEAN, service->name, code, exit_action_strings[action], 0);
\r
1839 stop_service(service, exitcode, false, default_action);
\r
1846 void throttle_restart(nssm_service_t *service) {
\r
1847 /* This can't be a restart if the service is already running. */
\r
1848 if (! service->throttle++) return;
\r
1851 unsigned long throttle_ms = throttle_milliseconds(service->throttle);
\r
1852 TCHAR threshold[8], milliseconds[8];
\r
1854 if (service->restart_delay > throttle_ms) ms = service->restart_delay;
\r
1855 else ms = throttle_ms;
\r
1857 if (service->throttle > 7) service->throttle = 8;
\r
1859 _sntprintf_s(milliseconds, _countof(milliseconds), _TRUNCATE, _T("%lu"), ms);
\r
1861 if (service->throttle == 1 && service->restart_delay > throttle_ms) log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESTART_DELAY, service->name, milliseconds, 0);
\r
1863 _sntprintf_s(threshold, _countof(threshold), _TRUNCATE, _T("%lu"), service->throttle_delay);
\r
1864 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_THROTTLED, service->name, threshold, milliseconds, 0);
\r
1867 if (use_critical_section) EnterCriticalSection(&service->throttle_section);
\r
1868 else if (service->throttle_timer) {
\r
1869 ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));
\r
1870 service->throttle_duetime.QuadPart = 0 - (ms * 10000LL);
\r
1871 SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);
\r
1874 service->status.dwCurrentState = SERVICE_PAUSED;
\r
1875 SetServiceStatus(service->status_handle, &service->status);
\r
1877 if (use_critical_section) {
\r
1878 imports.SleepConditionVariableCS(&service->throttle_condition, &service->throttle_section, ms);
\r
1879 LeaveCriticalSection(&service->throttle_section);
\r
1882 if (service->throttle_timer) WaitForSingleObject(service->throttle_timer, INFINITE);
\r
1888 When responding to a stop (or any other) request we need to set dwWaitHint to
\r
1889 the number of milliseconds we expect the operation to take, and optionally
\r
1890 increase dwCheckPoint. If dwWaitHint milliseconds elapses without the
\r
1891 operation completing or dwCheckPoint increasing, the system will consider the
\r
1892 service to be hung.
\r
1894 However the system will consider the service to be hung after 30000
\r
1895 milliseconds regardless of the value of dwWaitHint if dwCheckPoint has not
\r
1896 changed. Therefore if we want to wait longer than that we must periodically
\r
1897 increase dwCheckPoint.
\r
1899 Furthermore, it will consider the service to be hung after 60000 milliseconds
\r
1900 regardless of the value of dwCheckPoint unless dwWaitHint is increased every
\r
1901 time dwCheckPoint is also increased.
\r
1903 Our strategy then is to retrieve the initial dwWaitHint and wait for
\r
1904 NSSM_SERVICE_STATUS_DEADLINE milliseconds. If the process is still running
\r
1905 and we haven't finished waiting we increment dwCheckPoint and add whichever is
\r
1906 smaller of NSSM_SERVICE_STATUS_DEADLINE or the remaining timeout to
\r
1909 Only doing both these things will prevent the system from killing the service.
\r
1911 Returns: 1 if the wait timed out.
\r
1912 0 if the wait completed.
\r
1915 int await_shutdown(nssm_service_t *service, TCHAR *function_name, unsigned long timeout) {
\r
1916 unsigned long interval;
\r
1917 unsigned long waithint;
\r
1918 unsigned long ret;
\r
1919 unsigned long waited;
\r
1920 TCHAR interval_milliseconds[16];
\r
1921 TCHAR timeout_milliseconds[16];
\r
1922 TCHAR waited_milliseconds[16];
\r
1923 TCHAR *function = function_name;
\r
1925 /* Add brackets to function name. */
\r
1926 size_t funclen = _tcslen(function_name) + 3;
\r
1927 TCHAR *func = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, funclen * sizeof(TCHAR));
\r
1929 if (_sntprintf_s(func, funclen, _TRUNCATE, _T("%s()"), function_name) > -1) function = func;
\r
1932 _sntprintf_s(timeout_milliseconds, _countof(timeout_milliseconds), _TRUNCATE, _T("%lu"), timeout);
\r
1934 waithint = service->status.dwWaitHint;
\r
1936 while (waited < timeout) {
\r
1937 interval = timeout - waited;
\r
1938 if (interval > NSSM_SERVICE_STATUS_DEADLINE) interval = NSSM_SERVICE_STATUS_DEADLINE;
\r
1940 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
1941 service->status.dwWaitHint += interval;
\r
1942 service->status.dwCheckPoint++;
\r
1943 SetServiceStatus(service->status_handle, &service->status);
\r
1946 _sntprintf_s(waited_milliseconds, _countof(waited_milliseconds), _TRUNCATE, _T("%lu"), waited);
\r
1947 _sntprintf_s(interval_milliseconds, _countof(interval_milliseconds), _TRUNCATE, _T("%lu"), interval);
\r
1948 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_AWAITING_SHUTDOWN, function, service->name, waited_milliseconds, interval_milliseconds, timeout_milliseconds, 0);
\r
1951 switch (WaitForSingleObject(service->process_handle, interval)) {
\r
1952 case WAIT_OBJECT_0:
\r
1956 case WAIT_TIMEOUT:
\r
1965 waited += interval;
\r
1969 if (func) HeapFree(GetProcessHeap(), 0, func);
\r