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
13 static hook_thread_t hook_threads = { NULL, 0 };
\r
21 Check the status in response to a control.
\r
22 Returns: 1 if the status is expected, eg STOP following CONTROL_STOP.
\r
23 0 if the status is desired, eg STOPPED following CONTROL_STOP.
\r
24 -1 if the status is undesired, eg STOPPED following CONTROL_START.
\r
26 static inline int service_control_response(unsigned long control, unsigned long status) {
\r
28 case NSSM_SERVICE_CONTROL_START:
\r
30 case SERVICE_START_PENDING:
\r
33 case SERVICE_RUNNING:
\r
40 case SERVICE_CONTROL_STOP:
\r
41 case SERVICE_CONTROL_SHUTDOWN:
\r
43 case SERVICE_RUNNING:
\r
44 case SERVICE_STOP_PENDING:
\r
47 case SERVICE_STOPPED:
\r
54 case SERVICE_CONTROL_PAUSE:
\r
56 case SERVICE_PAUSE_PENDING:
\r
59 case SERVICE_PAUSED:
\r
66 case SERVICE_CONTROL_CONTINUE:
\r
68 case SERVICE_CONTINUE_PENDING:
\r
71 case SERVICE_RUNNING:
\r
78 case SERVICE_CONTROL_INTERROGATE:
\r
79 case NSSM_SERVICE_CONTROL_ROTATE:
\r
86 static inline int await_service_control_response(unsigned long control, SC_HANDLE service_handle, SERVICE_STATUS *service_status, unsigned long initial_status) {
\r
88 unsigned long checkpoint = 0;
\r
89 unsigned long waithint = 0;
\r
90 while (QueryServiceStatus(service_handle, service_status)) {
\r
91 int response = service_control_response(control, service_status->dwCurrentState);
\r
92 /* Alas we can't WaitForSingleObject() on an SC_HANDLE. */
\r
93 if (! response) return response;
\r
94 if (response > 0 || service_status->dwCurrentState == initial_status) {
\r
95 if (service_status->dwCheckPoint != checkpoint || service_status->dwWaitHint != waithint) tries = 0;
\r
96 checkpoint = service_status->dwCheckPoint;
\r
97 waithint = service_status->dwWaitHint;
\r
98 if (++tries > 10) tries = 10;
\r
101 else return response;
\r
106 static inline void wait_for_hooks(nssm_service_t *service, bool notify) {
\r
107 SERVICE_STATUS_HANDLE status_handle;
\r
108 SERVICE_STATUS *status;
\r
110 /* On a clean shutdown we need to keep the service's status up-to-date. */
\r
112 status_handle = service->status_handle;
\r
113 status = &service->status;
\r
116 status_handle = NULL;
\r
120 EnterCriticalSection(&service->hook_section);
\r
121 await_hook_threads(&hook_threads, status_handle, status, NSSM_HOOK_THREAD_DEADLINE);
\r
122 LeaveCriticalSection(&service->hook_section);
\r
125 int affinity_mask_to_string(__int64 mask, TCHAR **string) {
\r
126 if (! string) return 1;
\r
134 /* SetProcessAffinityMask() accepts a mask of up to 64 processors. */
\r
136 for (n = 0; n < _countof(set); n++) set[n].first = set[n].last = -1;
\r
138 for (i = 0, n = 0; i < _countof(set); i++) {
\r
139 if (mask & (1LL << i)) {
\r
140 if (set[n].first == -1) set[n].first = set[n].last = (int) i;
\r
141 else if (set[n].last == (int) i - 1) set[n].last = (int) i;
\r
144 set[n].first = set[n].last = (int) i;
\r
149 /* Worst case is 2x2 characters for first and last CPU plus - and/or , */
\r
150 size_t len = (size_t) (n + 1) * 6;
\r
151 *string = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len * sizeof(TCHAR));
\r
152 if (! string) return 2;
\r
156 for (i = 0; i <= n; i++) {
\r
157 if (i) (*string)[s++] = _T(',');
\r
158 ret = _sntprintf_s(*string + s, 3, _TRUNCATE, _T("%u"), set[i].first);
\r
160 HeapFree(GetProcessHeap(), 0, *string);
\r
165 if (set[i].last != set[i].first) {
\r
166 ret =_sntprintf_s(*string + s, 4, _TRUNCATE, _T("%c%u"), (set[i].last == set[i].first + 1) ? _T(',') : _T('-'), set[i].last);
\r
168 HeapFree(GetProcessHeap(), 0, *string);
\r
179 int affinity_string_to_mask(TCHAR *string, __int64 *mask) {
\r
180 if (! mask) return 1;
\r
183 if (! string) return 0;
\r
192 unsigned long number;
\r
194 for (n = 0; n < _countof(set); n++) set[n].first = set[n].last = -1;
\r
198 ret = str_number(s, &number, &end);
\r
200 if (ret == 0 || ret == 2) {
\r
201 if (number >= _countof(set)) return 2;
\r
202 set[n].first = set[n].last = (int) number;
\r
214 if (! *(++s)) return 3;
\r
215 ret = str_number(s, &number, &end);
\r
216 if (ret == 0 || ret == 2) {
\r
218 if (! *s || *s == _T(',')) {
\r
219 set[n].last = (int) number;
\r
236 for (i = 0; i <= n; i++) {
\r
237 for (int j = set[i].first; j <= set[i].last; j++) (__int64) *mask |= (1LL << (__int64) j);
\r
243 inline unsigned long priority_mask() {
\r
244 return REALTIME_PRIORITY_CLASS | HIGH_PRIORITY_CLASS | ABOVE_NORMAL_PRIORITY_CLASS | NORMAL_PRIORITY_CLASS | BELOW_NORMAL_PRIORITY_CLASS | IDLE_PRIORITY_CLASS;
\r
247 int priority_constant_to_index(unsigned long constant) {
\r
248 switch (constant & priority_mask()) {
\r
249 case REALTIME_PRIORITY_CLASS: return NSSM_REALTIME_PRIORITY;
\r
250 case HIGH_PRIORITY_CLASS: return NSSM_HIGH_PRIORITY;
\r
251 case ABOVE_NORMAL_PRIORITY_CLASS: return NSSM_ABOVE_NORMAL_PRIORITY;
\r
252 case BELOW_NORMAL_PRIORITY_CLASS: return NSSM_BELOW_NORMAL_PRIORITY;
\r
253 case IDLE_PRIORITY_CLASS: return NSSM_IDLE_PRIORITY;
\r
255 return NSSM_NORMAL_PRIORITY;
\r
258 unsigned long priority_index_to_constant(int index) {
\r
260 case NSSM_REALTIME_PRIORITY: return REALTIME_PRIORITY_CLASS;
\r
261 case NSSM_HIGH_PRIORITY: return HIGH_PRIORITY_CLASS;
\r
262 case NSSM_ABOVE_NORMAL_PRIORITY: return ABOVE_NORMAL_PRIORITY_CLASS;
\r
263 case NSSM_BELOW_NORMAL_PRIORITY: return BELOW_NORMAL_PRIORITY_CLASS;
\r
264 case NSSM_IDLE_PRIORITY: return IDLE_PRIORITY_CLASS;
\r
266 return NORMAL_PRIORITY_CLASS;
\r
269 static inline unsigned long throttle_milliseconds(unsigned long throttle) {
\r
270 if (throttle > 7) throttle = 8;
\r
271 /* pow() operates on doubles. */
\r
272 unsigned long ret = 1; for (unsigned long i = 1; i < throttle; i++) ret *= 2;
\r
277 Wrapper to be called in a new thread so that we can acknowledge a STOP
\r
278 control immediately.
\r
280 static unsigned long WINAPI shutdown_service(void *arg) {
\r
281 return stop_service((nssm_service_t *) arg, 0, true, true);
\r
284 /* Connect to the service manager */
\r
285 SC_HANDLE open_service_manager(unsigned long access) {
\r
286 SC_HANDLE ret = OpenSCManager(0, SERVICES_ACTIVE_DATABASE, access);
\r
288 if (is_admin) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENSCMANAGER_FAILED, 0);
\r
295 /* Open a service by name or display name. */
\r
296 SC_HANDLE open_service(SC_HANDLE services, TCHAR *service_name, unsigned long access, TCHAR *canonical_name, unsigned long canonical_namelen) {
\r
297 SC_HANDLE service_handle = OpenService(services, service_name, access);
\r
298 if (service_handle) {
\r
299 if (canonical_name && canonical_name != service_name) {
\r
300 TCHAR displayname[SERVICE_NAME_LENGTH];
\r
301 unsigned long displayname_len = (unsigned long) _countof(displayname);
\r
302 GetServiceDisplayName(services, service_name, displayname, &displayname_len);
\r
303 unsigned long keyname_len = canonical_namelen;
\r
304 GetServiceKeyName(services, displayname, canonical_name, &keyname_len);
\r
306 return service_handle;
\r
309 unsigned long error = GetLastError();
\r
310 if (error != ERROR_SERVICE_DOES_NOT_EXIST) {
\r
311 print_message(stderr, NSSM_MESSAGE_OPENSERVICE_FAILED, error_string(GetLastError()));
\r
315 /* We can't look for a display name because there's no buffer to store it. */
\r
316 if (! canonical_name) {
\r
317 print_message(stderr, NSSM_MESSAGE_OPENSERVICE_FAILED, error_string(GetLastError()));
\r
321 unsigned long bufsize, required, count, i;
\r
322 unsigned long resume = 0;
\r
323 EnumServicesStatus(services, SERVICE_DRIVER | SERVICE_FILE_SYSTEM_DRIVER | SERVICE_KERNEL_DRIVER | SERVICE_WIN32, SERVICE_STATE_ALL, 0, 0, &required, &count, &resume);
\r
324 error = GetLastError();
\r
325 if (error != ERROR_MORE_DATA) {
\r
326 print_message(stderr, NSSM_MESSAGE_ENUMSERVICESSTATUS_FAILED, error_string(GetLastError()));
\r
330 ENUM_SERVICE_STATUS *status = (ENUM_SERVICE_STATUS *) HeapAlloc(GetProcessHeap(), 0, required);
\r
332 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("ENUM_SERVICE_STATUS"), _T("open_service()"));
\r
336 bufsize = required;
\r
339 EnumServicesStatus() returns:
\r
340 1 when it retrieved data and there's no more data to come.
\r
341 0 and sets last error to ERROR_MORE_DATA when it retrieved data and
\r
342 there's more data to come.
\r
343 0 and sets last error to something else on error.
\r
345 int ret = EnumServicesStatus(services, SERVICE_DRIVER | SERVICE_FILE_SYSTEM_DRIVER | SERVICE_KERNEL_DRIVER | SERVICE_WIN32, SERVICE_STATE_ALL, status, bufsize, &required, &count, &resume);
\r
347 error = GetLastError();
\r
348 if (error != ERROR_MORE_DATA) {
\r
349 HeapFree(GetProcessHeap(), 0, status);
\r
350 print_message(stderr, NSSM_MESSAGE_ENUMSERVICESSTATUS_FAILED, error_string(GetLastError()));
\r
355 for (i = 0; i < count; i++) {
\r
356 if (str_equiv(status[i].lpDisplayName, service_name)) {
\r
357 if (_sntprintf_s(canonical_name, canonical_namelen, _TRUNCATE, _T("%s"), status[i].lpServiceName) < 0) {
\r
358 HeapFree(GetProcessHeap(), 0, status);
\r
359 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("canonical_name"), _T("open_service()"));
\r
363 HeapFree(GetProcessHeap(), 0, status);
\r
364 return open_service(services, canonical_name, access, 0, 0);
\r
371 /* Recurse so we can get an error message. */
\r
372 return open_service(services, service_name, access, 0, 0);
\r
375 QUERY_SERVICE_CONFIG *query_service_config(const TCHAR *service_name, SC_HANDLE service_handle) {
\r
376 QUERY_SERVICE_CONFIG *qsc;
\r
377 unsigned long bufsize;
\r
378 unsigned long error;
\r
380 QueryServiceConfig(service_handle, 0, 0, &bufsize);
\r
381 error = GetLastError();
\r
382 if (error == ERROR_INSUFFICIENT_BUFFER) {
\r
383 qsc = (QUERY_SERVICE_CONFIG *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, bufsize);
\r
385 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("QUERY_SERVICE_CONFIG"), _T("query_service_config()"), 0);
\r
390 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG_FAILED, service_name, error_string(error), 0);
\r
394 if (! QueryServiceConfig(service_handle, qsc, bufsize, &bufsize)) {
\r
395 HeapFree(GetProcessHeap(), 0, qsc);
\r
396 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG_FAILED, service_name, error_string(GetLastError()), 0);
\r
403 int set_service_dependencies(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR *buffer) {
\r
404 TCHAR *dependencies = _T("");
\r
405 unsigned long num_dependencies = 0;
\r
407 if (buffer && buffer[0]) {
\r
408 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
\r
410 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
415 Count the dependencies then allocate a buffer big enough for their
\r
416 canonical names, ie n * SERVICE_NAME_LENGTH.
\r
420 for (s = buffer; *s; s++) {
\r
421 num_dependencies++;
\r
422 if (*s == SC_GROUP_IDENTIFIER) groups = s;
\r
426 /* At least one dependency is a group so we need to verify them. */
\r
429 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, NSSM_REGISTRY_GROUPS, 0, KEY_READ, &key)) {
\r
430 _ftprintf(stderr, _T("%s: %s\n"), NSSM_REGISTRY_GROUPS, error_string(GetLastError()));
\r
434 unsigned long type;
\r
435 unsigned long groupslen;
\r
436 unsigned long ret = RegQueryValueEx(key, NSSM_REG_GROUPS, 0, &type, NULL, &groupslen);
\r
437 if (ret == ERROR_SUCCESS) {
\r
438 groups = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, groupslen);
\r
440 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("groups"), _T("set_service_dependencies()"));
\r
444 ret = RegQueryValueEx(key, NSSM_REG_GROUPS, 0, &type, (unsigned char *) groups, &groupslen);
\r
445 if (ret != ERROR_SUCCESS) {
\r
446 _ftprintf(stderr, _T("%s\\%s: %s"), NSSM_REGISTRY_GROUPS, NSSM_REG_GROUPS, error_string(GetLastError()));
\r
447 HeapFree(GetProcessHeap(), 0, groups);
\r
452 else if (ret != ERROR_FILE_NOT_FOUND) {
\r
453 _ftprintf(stderr, _T("%s\\%s: %s"), NSSM_REGISTRY_GROUPS, NSSM_REG_GROUPS, error_string(GetLastError()));
\r
462 unsigned long dependencieslen = (num_dependencies * SERVICE_NAME_LENGTH) + 2;
\r
463 dependencies = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dependencieslen * sizeof(TCHAR));
\r
466 TCHAR dependency[SERVICE_NAME_LENGTH];
\r
467 for (s = buffer; *s; s++) {
\r
469 if (*s == SC_GROUP_IDENTIFIER) {
\r
470 TCHAR *group = s + 1;
\r
474 for (TCHAR *g = groups; *g; g++) {
\r
475 if (str_equiv(g, group)) {
\r
477 /* Set canonical name. */
\r
478 memmove(group, g, _tcslen(g) * sizeof(TCHAR));
\r
486 if (ok) _sntprintf_s(dependency, _countof(dependency), _TRUNCATE, _T("%s"), s);
\r
488 HeapFree(GetProcessHeap(), 0, dependencies);
\r
489 if (groups) HeapFree(GetProcessHeap(), 0, groups);
\r
490 _ftprintf(stderr, _T("%s: %s"), s, error_string(ERROR_SERVICE_DEPENDENCY_DELETED));
\r
495 SC_HANDLE dependency_handle = open_service(services, s, SERVICE_QUERY_STATUS, dependency, _countof(dependency));
\r
496 if (! dependency_handle) {
\r
497 HeapFree(GetProcessHeap(), 0, dependencies);
\r
498 if (groups) HeapFree(GetProcessHeap(), 0, groups);
\r
499 CloseServiceHandle(services);
\r
500 _ftprintf(stderr, _T("%s: %s"), s, error_string(ERROR_SERVICE_DEPENDENCY_DELETED));
\r
505 size_t len = _tcslen(dependency) + 1;
\r
506 memmove(dependencies + i, dependency, len * sizeof(TCHAR));
\r
512 if (groups) HeapFree(GetProcessHeap(), 0, groups);
\r
513 CloseServiceHandle(services);
\r
516 if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, dependencies, 0, 0, 0)) {
\r
517 if (num_dependencies) HeapFree(GetProcessHeap(), 0, dependencies);
\r
518 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
522 if (num_dependencies) HeapFree(GetProcessHeap(), 0, dependencies);
\r
526 int get_service_dependencies(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR **buffer, unsigned long *bufsize, int type) {
\r
527 if (! buffer) return 1;
\r
528 if (! bufsize) return 2;
\r
533 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
534 if (! qsc) return 3;
\r
536 if (! qsc->lpDependencies) return 0;
\r
537 if (! qsc->lpDependencies[0]) return 0;
\r
539 /* lpDependencies is doubly NULL terminated. */
\r
540 while (qsc->lpDependencies[*bufsize]) {
\r
541 while (qsc->lpDependencies[*bufsize]) ++*bufsize;
\r
547 *buffer = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *bufsize * sizeof(TCHAR));
\r
550 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("lpDependencies"), _T("get_service_dependencies()"));
\r
554 if (type == DEPENDENCY_ALL) memmove(*buffer, qsc->lpDependencies, *bufsize * sizeof(TCHAR));
\r
559 for (s = qsc->lpDependencies; *s; s++) {
\r
560 /* Only copy the appropriate type of dependency. */
\r
561 if ((*s == SC_GROUP_IDENTIFIER && type & DEPENDENCY_GROUPS) || (*s != SC_GROUP_IDENTIFIER && type & DEPENDENCY_SERVICES)) {
\r
562 size_t len = _tcslen(s) + 1;
\r
563 *bufsize += (unsigned long) len;
\r
564 memmove(*buffer + i, s, len * sizeof(TCHAR));
\r
573 HeapFree(GetProcessHeap(), 0, qsc);
\r
578 int get_service_dependencies(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR **buffer, unsigned long *bufsize) {
\r
579 return get_service_dependencies(service_name, service_handle, buffer, bufsize, DEPENDENCY_ALL);
\r
582 int set_service_description(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR *buffer) {
\r
583 SERVICE_DESCRIPTION description;
\r
584 ZeroMemory(&description, sizeof(description));
\r
586 lpDescription must be NULL if we aren't changing, the new description
\r
589 if (buffer && buffer[0]) description.lpDescription = buffer;
\r
590 else description.lpDescription = _T("");
\r
592 if (ChangeServiceConfig2(service_handle, SERVICE_CONFIG_DESCRIPTION, &description)) return 0;
\r
594 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_DESCRIPTION_FAILED, service_name, error_string(GetLastError()), 0);
\r
598 int get_service_description(const TCHAR *service_name, SC_HANDLE service_handle, unsigned long len, TCHAR *buffer) {
\r
599 if (! buffer) return 1;
\r
601 unsigned long bufsize;
\r
602 QueryServiceConfig2(service_handle, SERVICE_CONFIG_DESCRIPTION, 0, 0, &bufsize);
\r
603 unsigned long error = GetLastError();
\r
604 if (error == ERROR_INSUFFICIENT_BUFFER) {
\r
605 SERVICE_DESCRIPTION *description = (SERVICE_DESCRIPTION *) HeapAlloc(GetProcessHeap(), 0, bufsize);
\r
606 if (! description) {
\r
607 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("SERVICE_CONFIG_DESCRIPTION"), _T("get_service_description()"));
\r
611 if (QueryServiceConfig2(service_handle, SERVICE_CONFIG_DESCRIPTION, (unsigned char *) description, bufsize, &bufsize)) {
\r
612 if (description->lpDescription) _sntprintf_s(buffer, len, _TRUNCATE, _T("%s"), description->lpDescription);
\r
613 else ZeroMemory(buffer, len * sizeof(TCHAR));
\r
614 HeapFree(GetProcessHeap(), 0, description);
\r
618 HeapFree(GetProcessHeap(), 0, description);
\r
619 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_CONFIG_DESCRIPTION"), error_string(error));
\r
624 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_CONFIG_DESCRIPTION"), error_string(error));
\r
631 int get_service_startup(const TCHAR *service_name, SC_HANDLE service_handle, const QUERY_SERVICE_CONFIG *qsc, unsigned long *startup) {
\r
632 if (! qsc) return 1;
\r
634 switch (qsc->dwStartType) {
\r
635 case SERVICE_DEMAND_START: *startup = NSSM_STARTUP_MANUAL; break;
\r
636 case SERVICE_DISABLED: *startup = NSSM_STARTUP_DISABLED; break;
\r
637 default: *startup = NSSM_STARTUP_AUTOMATIC;
\r
640 if (*startup != NSSM_STARTUP_AUTOMATIC) return 0;
\r
642 /* Check for delayed start. */
\r
643 unsigned long bufsize;
\r
644 unsigned long error;
\r
645 QueryServiceConfig2(service_handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, 0, 0, &bufsize);
\r
646 error = GetLastError();
\r
647 if (error == ERROR_INSUFFICIENT_BUFFER) {
\r
648 SERVICE_DELAYED_AUTO_START_INFO *info = (SERVICE_DELAYED_AUTO_START_INFO *) HeapAlloc(GetProcessHeap(), 0, bufsize);
\r
650 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("SERVICE_DELAYED_AUTO_START_INFO"), _T("get_service_startup()"));
\r
654 if (QueryServiceConfig2(service_handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, (unsigned char *) info, bufsize, &bufsize)) {
\r
655 if (info->fDelayedAutostart) *startup = NSSM_STARTUP_DELAYED;
\r
656 HeapFree(GetProcessHeap(), 0, info);
\r
660 error = GetLastError();
\r
661 if (error != ERROR_INVALID_LEVEL) {
\r
662 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_CONFIG_DELAYED_AUTO_START_INFO"), error_string(error));
\r
667 else if (error != ERROR_INVALID_LEVEL) {
\r
668 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_DELAYED_AUTO_START_INFO"), error_string(error));
\r
675 int get_service_username(const TCHAR *service_name, const QUERY_SERVICE_CONFIG *qsc, TCHAR **username, size_t *usernamelen) {
\r
676 if (! username) return 1;
\r
677 if (! usernamelen) return 1;
\r
682 if (! qsc) return 1;
\r
684 if (qsc->lpServiceStartName[0]) {
\r
685 if (is_localsystem(qsc->lpServiceStartName)) return 0;
\r
687 size_t len = _tcslen(qsc->lpServiceStartName);
\r
688 *username = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(TCHAR));
\r
690 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("username"), _T("get_service_username()"));
\r
694 memmove(*username, qsc->lpServiceStartName, (len + 1) * sizeof(TCHAR));
\r
695 *usernamelen = len;
\r
701 /* Set default values which aren't zero. */
\r
702 void set_nssm_service_defaults(nssm_service_t *service) {
\r
703 if (! service) return;
\r
705 service->type = SERVICE_WIN32_OWN_PROCESS;
\r
706 service->priority = NORMAL_PRIORITY_CLASS;
\r
707 service->stdin_sharing = NSSM_STDIN_SHARING;
\r
708 service->stdin_disposition = NSSM_STDIN_DISPOSITION;
\r
709 service->stdin_flags = NSSM_STDIN_FLAGS;
\r
710 service->stdout_sharing = NSSM_STDOUT_SHARING;
\r
711 service->stdout_disposition = NSSM_STDOUT_DISPOSITION;
\r
712 service->stdout_flags = NSSM_STDOUT_FLAGS;
\r
713 service->stderr_sharing = NSSM_STDERR_SHARING;
\r
714 service->stderr_disposition = NSSM_STDERR_DISPOSITION;
\r
715 service->stderr_flags = NSSM_STDERR_FLAGS;
\r
716 service->throttle_delay = NSSM_RESET_THROTTLE_RESTART;
\r
717 service->stop_method = ~0;
\r
718 service->kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;
\r
719 service->kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;
\r
720 service->kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;
\r
721 service->kill_process_tree = 1;
\r
724 /* Allocate and zero memory for a service. */
\r
725 nssm_service_t *alloc_nssm_service() {
\r
726 nssm_service_t *service = (nssm_service_t *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(nssm_service_t));
\r
727 if (! service) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("service"), _T("alloc_nssm_service()"), 0);
\r
731 /* Free memory for a service. */
\r
732 void cleanup_nssm_service(nssm_service_t *service) {
\r
733 if (! service) return;
\r
734 if (service->username) HeapFree(GetProcessHeap(), 0, service->username);
\r
735 if (service->password) {
\r
736 SecureZeroMemory(service->password, service->passwordlen);
\r
737 HeapFree(GetProcessHeap(), 0, service->password);
\r
739 if (service->dependencies) HeapFree(GetProcessHeap(), 0, service->dependencies);
\r
740 if (service->env) HeapFree(GetProcessHeap(), 0, service->env);
\r
741 if (service->env_extra) HeapFree(GetProcessHeap(), 0, service->env_extra);
\r
742 if (service->handle) CloseServiceHandle(service->handle);
\r
743 if (service->process_handle) CloseHandle(service->process_handle);
\r
744 if (service->wait_handle) UnregisterWait(service->wait_handle);
\r
745 if (service->throttle_section_initialised) DeleteCriticalSection(&service->throttle_section);
\r
746 if (service->throttle_timer) CloseHandle(service->throttle_timer);
\r
747 if (service->hook_section_initialised) DeleteCriticalSection(&service->hook_section);
\r
748 if (service->initial_env) FreeEnvironmentStrings(service->initial_env);
\r
749 HeapFree(GetProcessHeap(), 0, service);
\r
752 /* About to install the service */
\r
753 int pre_install_service(int argc, TCHAR **argv) {
\r
754 nssm_service_t *service = alloc_nssm_service();
\r
755 set_nssm_service_defaults(service);
\r
756 if (argc) _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]);
\r
758 /* Show the dialogue box if we didn't give the service name and path */
\r
759 if (argc < 2) return nssm_gui(IDD_INSTALL, service);
\r
762 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("service"), _T("pre_install_service()"));
\r
765 _sntprintf_s(service->exe, _countof(service->exe), _TRUNCATE, _T("%s"), argv[1]);
\r
767 /* Arguments are optional */
\r
768 size_t flagslen = 0;
\r
771 for (i = 2; i < argc; i++) flagslen += _tcslen(argv[i]) + 1;
\r
772 if (! flagslen) flagslen = 1;
\r
773 if (flagslen > _countof(service->flags)) {
\r
774 print_message(stderr, NSSM_MESSAGE_FLAGS_TOO_LONG);
\r
778 for (i = 2; i < argc; i++) {
\r
779 size_t len = _tcslen(argv[i]);
\r
780 memmove(service->flags + s, argv[i], len * sizeof(TCHAR));
\r
782 if (i < argc - 1) service->flags[s++] = _T(' ');
\r
785 /* Work out directory name */
\r
786 _sntprintf_s(service->dir, _countof(service->dir), _TRUNCATE, _T("%s"), service->exe);
\r
787 strip_basename(service->dir);
\r
789 int ret = install_service(service);
\r
790 cleanup_nssm_service(service);
\r
794 /* About to edit the service. */
\r
795 int pre_edit_service(int argc, TCHAR **argv) {
\r
796 /* Require service name. */
\r
797 if (argc < 2) return usage(1);
\r
799 /* Are we editing on the command line? */
\r
800 enum { MODE_EDITING, MODE_GETTING, MODE_SETTING, MODE_RESETTING } mode = MODE_EDITING;
\r
801 const TCHAR *verb = argv[0];
\r
802 const TCHAR *service_name = argv[1];
\r
803 bool getting = false;
\r
804 bool unsetting = false;
\r
806 /* Minimum number of arguments. */
\r
808 /* Index of first value. */
\r
811 if (str_equiv(verb, _T("get"))) {
\r
813 mode = MODE_GETTING;
\r
815 else if (str_equiv(verb, _T("set"))) {
\r
817 mode = MODE_SETTING;
\r
819 else if (str_equiv(verb, _T("reset")) || str_equiv(verb, _T("unset"))) {
\r
821 mode = MODE_RESETTING;
\r
823 if (argc < mandatory) return usage(1);
\r
825 const TCHAR *parameter = 0;
\r
826 settings_t *setting = 0;
\r
829 /* Validate the parameter. */
\r
830 if (mandatory > 2) {
\r
831 bool additional_mandatory = false;
\r
833 parameter = argv[2];
\r
834 for (i = 0; settings[i].name; i++) {
\r
835 setting = &settings[i];
\r
836 if (! str_equiv(setting->name, parameter)) continue;
\r
837 if (((setting->additional & ADDITIONAL_GETTING) && mode == MODE_GETTING) || ((setting->additional & ADDITIONAL_SETTING) && mode == MODE_SETTING) || ((setting->additional & ADDITIONAL_RESETTING) && mode == MODE_RESETTING)) {
\r
838 additional_mandatory = true;
\r
843 if (! settings[i].name) {
\r
844 print_message(stderr, NSSM_MESSAGE_INVALID_PARAMETER, parameter);
\r
845 for (i = 0; settings[i].name; i++) _ftprintf(stderr, _T("%s\n"), settings[i].name);
\r
850 if (additional_mandatory) {
\r
851 if (argc < mandatory) {
\r
852 print_message(stderr, NSSM_MESSAGE_MISSING_SUBPARAMETER, parameter);
\r
855 additional = argv[3];
\r
858 else if (str_equiv(setting->name, NSSM_NATIVE_OBJECTNAME) && mode == MODE_SETTING) {
\r
859 additional = argv[3];
\r
863 additional = argv[remainder];
\r
864 if (argc < mandatory) return usage(1);
\r
868 nssm_service_t *service = alloc_nssm_service();
\r
869 _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), service_name);
\r
871 /* Open service manager */
\r
872 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
\r
874 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
878 /* Try to open the service */
\r
879 unsigned long access = SERVICE_QUERY_CONFIG;
\r
880 if (mode != MODE_GETTING) access |= SERVICE_CHANGE_CONFIG;
\r
881 service->handle = open_service(services, service->name, access, service->name, _countof(service->name));
\r
882 if (! service->handle) {
\r
883 CloseServiceHandle(services);
\r
887 /* Get system details. */
\r
888 QUERY_SERVICE_CONFIG *qsc = query_service_config(service->name, service->handle);
\r
890 CloseServiceHandle(service->handle);
\r
891 CloseServiceHandle(services);
\r
895 service->type = qsc->dwServiceType;
\r
896 if (! (service->type & SERVICE_WIN32_OWN_PROCESS)) {
\r
897 if (mode != MODE_GETTING) {
\r
898 HeapFree(GetProcessHeap(), 0, qsc);
\r
899 CloseServiceHandle(service->handle);
\r
900 CloseServiceHandle(services);
\r
901 print_message(stderr, NSSM_MESSAGE_CANNOT_EDIT, service->name, NSSM_WIN32_OWN_PROCESS, 0);
\r
906 if (get_service_startup(service->name, service->handle, qsc, &service->startup)) {
\r
907 if (mode != MODE_GETTING) {
\r
908 HeapFree(GetProcessHeap(), 0, qsc);
\r
909 CloseServiceHandle(service->handle);
\r
910 CloseServiceHandle(services);
\r
915 if (get_service_username(service->name, qsc, &service->username, &service->usernamelen)) {
\r
916 if (mode != MODE_GETTING) {
\r
917 HeapFree(GetProcessHeap(), 0, qsc);
\r
918 CloseServiceHandle(service->handle);
\r
919 CloseServiceHandle(services);
\r
924 _sntprintf_s(service->displayname, _countof(service->displayname), _TRUNCATE, _T("%s"), qsc->lpDisplayName);
\r
926 /* Get the canonical service name. We open it case insensitively. */
\r
927 unsigned long bufsize = _countof(service->name);
\r
928 GetServiceKeyName(services, service->displayname, service->name, &bufsize);
\r
930 /* Remember the executable in case it isn't NSSM. */
\r
931 _sntprintf_s(service->image, _countof(service->image), _TRUNCATE, _T("%s"), qsc->lpBinaryPathName);
\r
932 HeapFree(GetProcessHeap(), 0, qsc);
\r
934 /* Get extended system details. */
\r
935 if (get_service_description(service->name, service->handle, _countof(service->description), service->description)) {
\r
936 if (mode != MODE_GETTING) {
\r
937 CloseServiceHandle(service->handle);
\r
938 CloseServiceHandle(services);
\r
943 if (get_service_dependencies(service->name, service->handle, &service->dependencies, &service->dependencieslen)) {
\r
944 if (mode != MODE_GETTING) {
\r
945 CloseServiceHandle(service->handle);
\r
946 CloseServiceHandle(services);
\r
951 /* Get NSSM details. */
\r
952 get_parameters(service, 0);
\r
954 CloseServiceHandle(services);
\r
956 if (! service->exe[0]) {
\r
957 service->native = true;
\r
958 if (mode != MODE_GETTING) print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE, service->name, NSSM, service->image);
\r
961 /* Editing with the GUI. */
\r
962 if (mode == MODE_EDITING) {
\r
963 nssm_gui(IDD_EDIT, service);
\r
967 /* Trying to manage App* parameters for a non-NSSM service. */
\r
968 if (! setting->native && service->native) {
\r
969 CloseServiceHandle(service->handle);
\r
970 print_message(stderr, NSSM_MESSAGE_NATIVE_PARAMETER, setting->name, NSSM);
\r
978 if (mode == MODE_GETTING) {
\r
979 if (! service->native) {
\r
980 key = open_registry(service->name, KEY_READ);
\r
981 if (! key) return 4;
\r
984 if (setting->native) ret = get_setting(service->name, service->handle, setting, &value, additional);
\r
985 else ret = get_setting(service->name, key, setting, &value, additional);
\r
987 CloseServiceHandle(service->handle);
\r
991 switch (setting->type) {
\r
992 case REG_EXPAND_SZ:
\r
995 _tprintf(_T("%s\n"), value.string ? value.string : _T(""));
\r
996 HeapFree(GetProcessHeap(), 0, value.string);
\r
1000 _tprintf(_T("%u\n"), value.numeric);
\r
1004 if (! service->native) RegCloseKey(key);
\r
1005 CloseServiceHandle(service->handle);
\r
1009 /* Build the value. */
\r
1010 if (mode == MODE_RESETTING) {
\r
1011 /* Unset the parameter. */
\r
1014 else if (remainder == argc) {
\r
1018 /* Set the parameter. */
\r
1020 size_t delimiterlen = (setting->additional & ADDITIONAL_CRLF) ? 2 : 1;
\r
1021 for (i = remainder; i < argc; i++) len += _tcslen(argv[i]) + delimiterlen;
\r
1024 value.string = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, len * sizeof(TCHAR));
\r
1025 if (! value.string) {
\r
1026 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("value"), _T("edit_service()"));
\r
1027 CloseServiceHandle(service->handle);
\r
1032 for (i = remainder; i < argc; i++) {
\r
1033 size_t len = _tcslen(argv[i]);
\r
1034 memmove(value.string + s, argv[i], len * sizeof(TCHAR));
\r
1036 if (i < argc - 1) {
\r
1037 if (setting->additional & ADDITIONAL_CRLF) {
\r
1038 value.string[s++] = _T('\r');
\r
1039 value.string[s++] = _T('\n');
\r
1041 else value.string[s++] = _T(' ');
\r
1044 value.string[s] = _T('\0');
\r
1047 if (! service->native) {
\r
1048 key = open_registry(service->name, KEY_WRITE);
\r
1050 if (value.string) HeapFree(GetProcessHeap(), 0, value.string);
\r
1055 if (setting->native) ret = set_setting(service->name, service->handle, setting, &value, additional);
\r
1056 else ret = set_setting(service->name, key, setting, &value, additional);
\r
1057 if (value.string) HeapFree(GetProcessHeap(), 0, value.string);
\r
1059 if (! service->native) RegCloseKey(key);
\r
1060 CloseServiceHandle(service->handle);
\r
1064 if (! service->native) RegCloseKey(key);
\r
1065 CloseServiceHandle(service->handle);
\r
1070 /* About to remove the service */
\r
1071 int pre_remove_service(int argc, TCHAR **argv) {
\r
1072 nssm_service_t *service = alloc_nssm_service();
\r
1073 set_nssm_service_defaults(service);
\r
1074 if (argc) _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]);
\r
1076 /* Show dialogue box if we didn't pass service name and "confirm" */
\r
1077 if (argc < 2) return nssm_gui(IDD_REMOVE, service);
\r
1078 if (str_equiv(argv[1], _T("confirm"))) {
\r
1079 int ret = remove_service(service);
\r
1080 cleanup_nssm_service(service);
\r
1083 print_message(stderr, NSSM_MESSAGE_PRE_REMOVE_SERVICE);
\r
1087 /* Install the service */
\r
1088 int install_service(nssm_service_t *service) {
\r
1089 if (! service) return 1;
\r
1091 /* Open service manager */
\r
1092 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE);
\r
1094 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
1095 cleanup_nssm_service(service);
\r
1099 /* Get path of this program */
\r
1100 GetModuleFileName(0, service->image, _countof(service->image));
\r
1102 /* Create the service - settings will be changed in edit_service() */
\r
1103 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
1104 if (! service->handle) {
\r
1105 print_message(stderr, NSSM_MESSAGE_CREATESERVICE_FAILED, error_string(GetLastError()));
\r
1106 CloseServiceHandle(services);
\r
1110 if (edit_service(service, false)) {
\r
1111 DeleteService(service->handle);
\r
1112 CloseServiceHandle(services);
\r
1116 print_message(stdout, NSSM_MESSAGE_SERVICE_INSTALLED, service->name);
\r
1119 CloseServiceHandle(services);
\r
1124 /* Edit the service. */
\r
1125 int edit_service(nssm_service_t *service, bool editing) {
\r
1126 if (! service) return 1;
\r
1129 The only two valid flags for service type are SERVICE_WIN32_OWN_PROCESS
\r
1130 and SERVICE_INTERACTIVE_PROCESS.
\r
1132 service->type &= SERVICE_INTERACTIVE_PROCESS;
\r
1133 service->type |= SERVICE_WIN32_OWN_PROCESS;
\r
1135 /* Startup type. */
\r
1136 unsigned long startup;
\r
1137 switch (service->startup) {
\r
1138 case NSSM_STARTUP_MANUAL: startup = SERVICE_DEMAND_START; break;
\r
1139 case NSSM_STARTUP_DISABLED: startup = SERVICE_DISABLED; break;
\r
1140 default: startup = SERVICE_AUTO_START;
\r
1143 /* Display name. */
\r
1144 if (! service->displayname[0]) _sntprintf_s(service->displayname, _countof(service->displayname), _TRUNCATE, _T("%s"), service->name);
\r
1147 Username must be NULL if we aren't changing or an account name.
\r
1148 We must explicitly use LOCALSYSTEM to change it when we are editing.
\r
1149 Password must be NULL if we aren't changing, a password or "".
\r
1150 Empty passwords are valid but we won't allow them in the GUI.
\r
1152 TCHAR *username = 0;
\r
1154 TCHAR *password = 0;
\r
1155 if (service->usernamelen) {
\r
1156 username = service->username;
\r
1157 if (canonicalise_username(username, &canon)) return 5;
\r
1158 if (service->passwordlen) password = service->password;
\r
1160 else if (editing) username = canon = NSSM_LOCALSYSTEM_ACCOUNT;
\r
1162 if (well_known_username(canon)) password = _T("");
\r
1164 if (grant_logon_as_service(canon)) {
\r
1165 if (canon != username) HeapFree(GetProcessHeap(), 0, canon);
\r
1166 print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username);
\r
1171 TCHAR *dependencies = _T("");
\r
1172 if (service->dependencieslen) dependencies = 0; /* Change later. */
\r
1174 if (! ChangeServiceConfig(service->handle, service->type, startup, SERVICE_NO_CHANGE, 0, 0, 0, dependencies, canon, password, service->displayname)) {
\r
1175 if (canon != username) HeapFree(GetProcessHeap(), 0, canon);
\r
1176 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
1179 if (canon != username) HeapFree(GetProcessHeap(), 0, canon);
\r
1181 if (service->dependencieslen) {
\r
1182 if (set_service_dependencies(service->name, service->handle, service->dependencies)) return 5;
\r
1185 if (service->description[0] || editing) {
\r
1186 set_service_description(service->name, service->handle, service->description);
\r
1189 SERVICE_DELAYED_AUTO_START_INFO delayed;
\r
1190 ZeroMemory(&delayed, sizeof(delayed));
\r
1191 if (service->startup == NSSM_STARTUP_DELAYED) delayed.fDelayedAutostart = 1;
\r
1192 else delayed.fDelayedAutostart = 0;
\r
1193 /* Delayed startup isn't supported until Vista. */
\r
1194 if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, &delayed)) {
\r
1195 unsigned long error = GetLastError();
\r
1196 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
\r
1197 if (error != ERROR_INVALID_LEVEL) {
\r
1198 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_DELAYED_AUTO_START_INFO_FAILED, service->name, error_string(error), 0);
\r
1202 /* Don't mess with parameters which aren't ours. */
\r
1203 if (! service->native) {
\r
1204 /* Now we need to put the parameters into the registry */
\r
1205 if (create_parameters(service, editing)) {
\r
1206 print_message(stderr, NSSM_MESSAGE_CREATE_PARAMETERS_FAILED);
\r
1210 set_service_recovery(service);
\r
1216 /* Control a service. */
\r
1217 int control_service(unsigned long control, int argc, TCHAR **argv) {
\r
1218 if (argc < 1) return usage(1);
\r
1219 TCHAR *service_name = argv[0];
\r
1220 TCHAR canonical_name[SERVICE_NAME_LENGTH];
\r
1222 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
\r
1224 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
1228 unsigned long access = SERVICE_QUERY_STATUS;
\r
1229 switch (control) {
\r
1230 case NSSM_SERVICE_CONTROL_START:
\r
1231 access |= SERVICE_START;
\r
1234 case SERVICE_CONTROL_CONTINUE:
\r
1235 case SERVICE_CONTROL_PAUSE:
\r
1236 access |= SERVICE_PAUSE_CONTINUE;
\r
1239 case SERVICE_CONTROL_STOP:
\r
1240 access |= SERVICE_STOP;
\r
1243 case NSSM_SERVICE_CONTROL_ROTATE:
\r
1244 access |= SERVICE_USER_DEFINED_CONTROL;
\r
1248 SC_HANDLE service_handle = open_service(services, service_name, access, canonical_name, _countof(canonical_name));
\r
1249 if (! service_handle) {
\r
1250 CloseServiceHandle(services);
\r
1255 unsigned long error;
\r
1256 SERVICE_STATUS service_status;
\r
1257 if (control == NSSM_SERVICE_CONTROL_START) {
\r
1258 unsigned long initial_status = SERVICE_STOPPED;
\r
1259 ret = StartService(service_handle, (unsigned long) argc, (const TCHAR **) argv);
\r
1260 error = GetLastError();
\r
1261 CloseServiceHandle(services);
\r
1263 if (error == ERROR_IO_PENDING) {
\r
1265 Older versions of Windows return immediately with ERROR_IO_PENDING
\r
1266 indicate that the operation is still in progress. Newer versions
\r
1267 will return it if there really is a delay.
\r
1270 error = ERROR_SUCCESS;
\r
1274 int response = await_service_control_response(control, service_handle, &service_status, initial_status);
\r
1275 CloseServiceHandle(service_handle);
\r
1278 print_message(stderr, NSSM_MESSAGE_BAD_CONTROL_RESPONSE, canonical_name, service_status_text(service_status.dwCurrentState), service_control_text(control));
\r
1281 else _tprintf(_T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1285 CloseServiceHandle(service_handle);
\r
1286 _ftprintf(stderr, _T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1290 else if (control == SERVICE_CONTROL_INTERROGATE) {
\r
1292 We could actually send an INTERROGATE control but that won't return
\r
1293 any information if the service is stopped and we don't care about
\r
1294 the extra details it might give us in any case. So we'll fake it.
\r
1296 ret = QueryServiceStatus(service_handle, &service_status);
\r
1297 error = GetLastError();
\r
1300 _tprintf(_T("%s\n"), service_status_text(service_status.dwCurrentState));
\r
1304 _ftprintf(stderr, _T("%s: %s\n"), canonical_name, error_string(error));
\r
1309 ret = ControlService(service_handle, control, &service_status);
\r
1310 unsigned long initial_status = service_status.dwCurrentState;
\r
1311 error = GetLastError();
\r
1312 CloseServiceHandle(services);
\r
1314 if (error == ERROR_IO_PENDING) {
\r
1316 error = ERROR_SUCCESS;
\r
1320 int response = await_service_control_response(control, service_handle, &service_status, initial_status);
\r
1321 CloseServiceHandle(service_handle);
\r
1324 print_message(stderr, NSSM_MESSAGE_BAD_CONTROL_RESPONSE, canonical_name, service_status_text(service_status.dwCurrentState), service_control_text(control));
\r
1327 else _tprintf(_T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1331 CloseServiceHandle(service_handle);
\r
1332 _ftprintf(stderr, _T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1333 if (error == ERROR_SERVICE_NOT_ACTIVE) {
\r
1334 if (control == SERVICE_CONTROL_SHUTDOWN || control == SERVICE_CONTROL_STOP) return 0;
\r
1341 /* Remove the service */
\r
1342 int remove_service(nssm_service_t *service) {
\r
1343 if (! service) return 1;
\r
1345 /* Open service manager */
\r
1346 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
\r
1348 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
1352 /* Try to open the service */
\r
1353 service->handle = open_service(services, service->name, DELETE, service->name, _countof(service->name));
\r
1354 if (! service->handle) {
\r
1355 CloseServiceHandle(services);
\r
1359 /* Get the canonical service name. We open it case insensitively. */
\r
1360 unsigned long bufsize = _countof(service->displayname);
\r
1361 GetServiceDisplayName(services, service->name, service->displayname, &bufsize);
\r
1362 bufsize = _countof(service->name);
\r
1363 GetServiceKeyName(services, service->displayname, service->name, &bufsize);
\r
1365 /* Try to delete the service */
\r
1366 if (! DeleteService(service->handle)) {
\r
1367 print_message(stderr, NSSM_MESSAGE_DELETESERVICE_FAILED);
\r
1368 CloseServiceHandle(services);
\r
1373 CloseServiceHandle(services);
\r
1375 print_message(stdout, NSSM_MESSAGE_SERVICE_REMOVED, service->name);
\r
1379 /* Service initialisation */
\r
1380 void WINAPI service_main(unsigned long argc, TCHAR **argv) {
\r
1381 nssm_service_t *service = alloc_nssm_service();
\r
1382 if (! service) return;
\r
1384 if (_sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]) < 0) {
\r
1385 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("service->name"), _T("service_main()"), 0);
\r
1389 /* We can use a condition variable in a critical section on Vista or later. */
\r
1390 if (imports.SleepConditionVariableCS && imports.WakeConditionVariable) use_critical_section = true;
\r
1391 else use_critical_section = false;
\r
1393 /* Initialise status */
\r
1394 ZeroMemory(&service->status, sizeof(service->status));
\r
1395 service->status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;
\r
1396 service->status.dwControlsAccepted = 0;
\r
1397 service->status.dwWin32ExitCode = NO_ERROR;
\r
1398 service->status.dwServiceSpecificExitCode = 0;
\r
1399 service->status.dwCheckPoint = 0;
\r
1400 service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;
\r
1402 /* Signal we AREN'T running the server */
\r
1403 service->process_handle = 0;
\r
1406 /* Register control handler */
\r
1407 service->status_handle = RegisterServiceCtrlHandlerEx(NSSM, service_control_handler, (void *) service);
\r
1408 if (! service->status_handle) {
\r
1409 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_REGISTERSERVICECTRLHANDER_FAILED, error_string(GetLastError()), 0);
\r
1413 log_service_control(service->name, 0, true);
\r
1415 service->status.dwCurrentState = SERVICE_START_PENDING;
\r
1416 service->status.dwWaitHint = service->throttle_delay + NSSM_WAITHINT_MARGIN;
\r
1417 SetServiceStatus(service->status_handle, &service->status);
\r
1420 /* Try to create the exit action parameters; we don't care if it fails */
\r
1421 create_exit_action(service->name, exit_action_strings[0], false);
\r
1423 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT);
\r
1425 service->handle = open_service(services, service->name, SERVICE_CHANGE_CONFIG, 0, 0);
\r
1426 set_service_recovery(service);
\r
1428 /* Remember our display name. */
\r
1429 unsigned long displayname_len = _countof(service->displayname);
\r
1430 GetServiceDisplayName(services, service->name, service->displayname, &displayname_len);
\r
1432 CloseServiceHandle(services);
\r
1436 /* Used for signalling a resume if the service pauses when throttled. */
\r
1437 if (use_critical_section) {
\r
1438 InitializeCriticalSection(&service->throttle_section);
\r
1439 service->throttle_section_initialised = true;
\r
1442 service->throttle_timer = CreateWaitableTimer(0, 1, 0);
\r
1443 if (! service->throttle_timer) {
\r
1444 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_CREATEWAITABLETIMER_FAILED, service->name, error_string(GetLastError()), 0);
\r
1448 /* Critical section for hooks. */
\r
1449 InitializeCriticalSection(&service->hook_section);
\r
1450 service->hook_section_initialised = true;
\r
1452 /* Remember our initial environment. */
\r
1453 service->initial_env = GetEnvironmentStrings();
\r
1455 /* Remember our creation time. */
\r
1456 if (get_process_creation_time(GetCurrentProcess(), &service->nssm_creation_time)) ZeroMemory(&service->nssm_creation_time, sizeof(service->nssm_creation_time));
\r
1458 monitor_service(service);
\r
1461 /* Make sure service recovery actions are taken where necessary */
\r
1462 void set_service_recovery(nssm_service_t *service) {
\r
1463 SERVICE_FAILURE_ACTIONS_FLAG flag;
\r
1464 ZeroMemory(&flag, sizeof(flag));
\r
1465 flag.fFailureActionsOnNonCrashFailures = true;
\r
1467 /* This functionality was added in Vista so the call may fail */
\r
1468 if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, &flag)) {
\r
1469 unsigned long error = GetLastError();
\r
1470 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
\r
1471 if (error != ERROR_INVALID_LEVEL) {
\r
1472 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_FAILURE_ACTIONS_FAILED, service->name, error_string(error), 0);
\r
1477 int monitor_service(nssm_service_t *service) {
\r
1478 /* Set service status to started */
\r
1479 int ret = start_service(service);
\r
1482 _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%d"), ret);
\r
1483 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_START_SERVICE_FAILED, service->exe, service->name, ret, 0);
\r
1486 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_STARTED_SERVICE, service->exe, service->flags, service->name, service->dir, 0);
\r
1488 /* Monitor service */
\r
1489 if (! RegisterWaitForSingleObject(&service->wait_handle, service->process_handle, end_service, (void *) service, INFINITE, WT_EXECUTEONLYONCE | WT_EXECUTELONGFUNCTION)) {
\r
1490 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_REGISTERWAITFORSINGLEOBJECT_FAILED, service->name, service->exe, error_string(GetLastError()), 0);
\r
1496 TCHAR *service_control_text(unsigned long control) {
\r
1497 switch (control) {
\r
1498 /* HACK: there is no SERVICE_CONTROL_START constant */
\r
1499 case NSSM_SERVICE_CONTROL_START: return _T("START");
\r
1500 case SERVICE_CONTROL_STOP: return _T("STOP");
\r
1501 case SERVICE_CONTROL_SHUTDOWN: return _T("SHUTDOWN");
\r
1502 case SERVICE_CONTROL_PAUSE: return _T("PAUSE");
\r
1503 case SERVICE_CONTROL_CONTINUE: return _T("CONTINUE");
\r
1504 case SERVICE_CONTROL_INTERROGATE: return _T("INTERROGATE");
\r
1505 case NSSM_SERVICE_CONTROL_ROTATE: return _T("ROTATE");
\r
1506 case SERVICE_CONTROL_POWEREVENT: return _T("POWEREVENT");
\r
1507 default: return 0;
\r
1511 TCHAR *service_status_text(unsigned long status) {
\r
1513 case SERVICE_STOPPED: return _T("SERVICE_STOPPED");
\r
1514 case SERVICE_START_PENDING: return _T("SERVICE_START_PENDING");
\r
1515 case SERVICE_STOP_PENDING: return _T("SERVICE_STOP_PENDING");
\r
1516 case SERVICE_RUNNING: return _T("SERVICE_RUNNING");
\r
1517 case SERVICE_CONTINUE_PENDING: return _T("SERVICE_CONTINUE_PENDING");
\r
1518 case SERVICE_PAUSE_PENDING: return _T("SERVICE_PAUSE_PENDING");
\r
1519 case SERVICE_PAUSED: return _T("SERVICE_PAUSED");
\r
1520 default: return 0;
\r
1524 void log_service_control(TCHAR *service_name, unsigned long control, bool handled) {
\r
1525 TCHAR *text = service_control_text(control);
\r
1526 unsigned long event;
\r
1529 /* "0x" + 8 x hex + NULL */
\r
1530 text = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, 11 * sizeof(TCHAR));
\r
1532 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);
\r
1535 if (_sntprintf_s(text, 11, _TRUNCATE, _T("0x%08x"), control) < 0) {
\r
1536 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);
\r
1537 HeapFree(GetProcessHeap(), 0, text);
\r
1541 event = NSSM_EVENT_SERVICE_CONTROL_UNKNOWN;
\r
1543 else if (handled) event = NSSM_EVENT_SERVICE_CONTROL_HANDLED;
\r
1544 else event = NSSM_EVENT_SERVICE_CONTROL_NOT_HANDLED;
\r
1546 log_event(EVENTLOG_INFORMATION_TYPE, event, service_name, text, 0);
\r
1548 if (event == NSSM_EVENT_SERVICE_CONTROL_UNKNOWN) {
\r
1549 HeapFree(GetProcessHeap(), 0, text);
\r
1553 /* Service control handler */
\r
1554 unsigned long WINAPI service_control_handler(unsigned long control, unsigned long event, void *data, void *context) {
\r
1555 nssm_service_t *service = (nssm_service_t *) context;
\r
1557 switch (control) {
\r
1558 case SERVICE_CONTROL_INTERROGATE:
\r
1559 /* We always keep the service status up-to-date so this is a no-op. */
\r
1562 case SERVICE_CONTROL_SHUTDOWN:
\r
1563 case SERVICE_CONTROL_STOP:
\r
1564 service->last_control = control;
\r
1565 log_service_control(service->name, control, true);
\r
1567 /* Pre-stop hook. */
\r
1568 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
1569 SetServiceStatus(service->status_handle, &service->status);
\r
1570 nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_STOP, NSSM_HOOK_ACTION_PRE, &control, NSSM_SERVICE_STATUS_DEADLINE, false);
\r
1573 We MUST acknowledge the stop request promptly but we're committed to
\r
1574 waiting for the application to exit. Spawn a new thread to wait
\r
1575 while we acknowledge the request.
\r
1577 if (! CreateThread(NULL, 0, shutdown_service, context, 0, NULL)) {
\r
1578 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETHREAD_FAILED, error_string(GetLastError()), 0);
\r
1581 We couldn't create a thread to tidy up so we'll have to force the tidyup
\r
1582 to complete in time in this thread.
\r
1584 service->kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;
\r
1585 service->kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;
\r
1586 service->kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;
\r
1588 stop_service(service, 0, true, true);
\r
1592 case SERVICE_CONTROL_CONTINUE:
\r
1593 service->last_control = control;
\r
1594 log_service_control(service->name, control, true);
\r
1595 service->throttle = 0;
\r
1596 if (use_critical_section) imports.WakeConditionVariable(&service->throttle_condition);
\r
1598 if (! service->throttle_timer) return ERROR_CALL_NOT_IMPLEMENTED;
\r
1599 ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));
\r
1600 SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);
\r
1602 /* We can't continue if the application is running! */
\r
1603 if (! service->process_handle) service->status.dwCurrentState = SERVICE_CONTINUE_PENDING;
\r
1604 service->status.dwWaitHint = throttle_milliseconds(service->throttle) + NSSM_WAITHINT_MARGIN;
\r
1605 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESET_THROTTLE, service->name, 0);
\r
1606 SetServiceStatus(service->status_handle, &service->status);
\r
1609 case SERVICE_CONTROL_PAUSE:
\r
1611 We don't accept pause messages but it isn't possible to register
\r
1612 only for continue messages so we have to handle this case.
\r
1614 log_service_control(service->name, control, false);
\r
1615 return ERROR_CALL_NOT_IMPLEMENTED;
\r
1617 case NSSM_SERVICE_CONTROL_ROTATE:
\r
1618 service->last_control = control;
\r
1619 log_service_control(service->name, control, true);
\r
1620 (void) nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_ROTATE, NSSM_HOOK_ACTION_PRE, &control, NSSM_HOOK_DEADLINE, false);
\r
1621 if (service->rotate_stdout_online == NSSM_ROTATE_ONLINE) service->rotate_stdout_online = NSSM_ROTATE_ONLINE_ASAP;
\r
1622 if (service->rotate_stderr_online == NSSM_ROTATE_ONLINE) service->rotate_stderr_online = NSSM_ROTATE_ONLINE_ASAP;
\r
1623 (void) nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_ROTATE, NSSM_HOOK_ACTION_POST, &control);
\r
1626 case SERVICE_CONTROL_POWEREVENT:
\r
1627 /* Resume from suspend. */
\r
1628 if (event == PBT_APMRESUMEAUTOMATIC) {
\r
1629 service->last_control = control;
\r
1630 log_service_control(service->name, control, true);
\r
1631 (void) nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_POWER, NSSM_HOOK_ACTION_RESUME, &control);
\r
1635 /* Battery low or changed to A/C power or something. */
\r
1636 if (event == PBT_APMPOWERSTATUSCHANGE) {
\r
1637 service->last_control = control;
\r
1638 log_service_control(service->name, control, true);
\r
1639 (void) nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_POWER, NSSM_HOOK_ACTION_CHANGE, &control);
\r
1642 log_service_control(service->name, control, false);
\r
1646 /* Unknown control */
\r
1647 log_service_control(service->name, control, false);
\r
1648 return ERROR_CALL_NOT_IMPLEMENTED;
\r
1651 /* Start the service */
\r
1652 int start_service(nssm_service_t *service) {
\r
1653 service->stopping = false;
\r
1654 service->allow_restart = true;
\r
1656 if (service->process_handle) return 0;
\r
1657 service->start_requested_count++;
\r
1659 /* Allocate a STARTUPINFO structure for a new process */
\r
1661 ZeroMemory(&si, sizeof(si));
\r
1662 si.cb = sizeof(si);
\r
1664 /* Allocate a PROCESSINFO structure for the process */
\r
1665 PROCESS_INFORMATION pi;
\r
1666 ZeroMemory(&pi, sizeof(pi));
\r
1668 /* Get startup parameters */
\r
1669 int ret = get_parameters(service, &si);
\r
1671 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_PARAMETERS_FAILED, service->name, 0);
\r
1672 return stop_service(service, 2, true, true);
\r
1675 /* Launch executable with arguments */
\r
1676 TCHAR cmd[CMD_LENGTH];
\r
1677 if (_sntprintf_s(cmd, _countof(cmd), _TRUNCATE, _T("\"%s\" %s"), service->exe, service->flags) < 0) {
\r
1678 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("command line"), _T("start_service"), 0);
\r
1679 return stop_service(service, 2, true, true);
\r
1682 throttle_restart(service);
\r
1684 /* Set the environment. */
\r
1685 if (service->env) duplicate_environment(service->env);
\r
1686 if (service->env_extra) set_environment_block(service->env_extra);
\r
1688 /* Pre-start hook. */
\r
1689 unsigned long control = NSSM_SERVICE_CONTROL_START;
\r
1690 service->status.dwCurrentState = SERVICE_START_PENDING;
\r
1691 service->status.dwControlsAccepted = SERVICE_ACCEPT_POWEREVENT | SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP;
\r
1692 if (nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_START, NSSM_HOOK_ACTION_PRE, &control, NSSM_SERVICE_STATUS_DEADLINE, false) == NSSM_HOOK_STATUS_ABORT) {
\r
1694 _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), NSSM_HOOK_STATUS_ABORT);
\r
1695 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_PRESTART_HOOK_ABORT, NSSM_HOOK_EVENT_START, NSSM_HOOK_ACTION_PRE, service->name, code, 0);
\r
1696 return stop_service(service, 5, true, true);
\r
1699 /* Set up I/O redirection. */
\r
1700 if (get_output_handles(service, &si)) {
\r
1701 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_OUTPUT_HANDLES_FAILED, service->name, 0);
\r
1702 if (! service->no_console) FreeConsole();
\r
1703 close_output_handles(&si);
\r
1704 return stop_service(service, 4, true, true);
\r
1707 bool inherit_handles = false;
\r
1708 if (si.dwFlags & STARTF_USESTDHANDLES) inherit_handles = true;
\r
1709 unsigned long flags = service->priority & priority_mask();
\r
1710 if (service->affinity) flags |= CREATE_SUSPENDED;
\r
1711 if (! CreateProcess(0, cmd, 0, 0, inherit_handles, flags, 0, service->dir, &si, &pi)) {
\r
1712 unsigned long exitcode = 3;
\r
1713 unsigned long error = GetLastError();
\r
1714 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service->name, service->exe, error_string(error), 0);
\r
1715 close_output_handles(&si);
\r
1716 duplicate_environment_strings(service->initial_env);
\r
1717 return stop_service(service, exitcode, true, true);
\r
1719 service->start_count++;
\r
1720 service->process_handle = pi.hProcess;
\r
1721 service->pid = pi.dwProcessId;
\r
1723 if (get_process_creation_time(service->process_handle, &service->creation_time)) ZeroMemory(&service->creation_time, sizeof(service->creation_time));
\r
1725 close_output_handles(&si);
\r
1727 if (! service->no_console) FreeConsole();
\r
1729 /* Restore our environment. */
\r
1730 duplicate_environment_strings(service->initial_env);
\r
1732 if (service->affinity) {
\r
1734 We are explicitly storing service->affinity as a 64-bit unsigned integer
\r
1735 so that we can parse it regardless of whether we're running in 32-bit
\r
1736 or 64-bit mode. The arguments to SetProcessAffinityMask(), however, are
\r
1737 defined as type DWORD_PTR and hence limited to 32 bits on a 32-bit system
\r
1738 (or when running the 32-bit NSSM).
\r
1740 The result is a lot of seemingly-unnecessary casting throughout the code
\r
1741 and potentially confusion when we actually try to start the service.
\r
1742 Having said that, however, it's unlikely that we're actually going to
\r
1743 run in 32-bit mode on a system which has more than 32 CPUs so the
\r
1744 likelihood of seeing a confusing situation is somewhat diminished.
\r
1746 DWORD_PTR affinity, system_affinity;
\r
1748 if (GetProcessAffinityMask(service->process_handle, &affinity, &system_affinity)) affinity = service->affinity & system_affinity;
\r
1750 affinity = (DWORD_PTR) service->affinity;
\r
1751 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GETPROCESSAFFINITYMASK_FAILED, service->name, error_string(GetLastError()), 0);
\r
1754 if (! SetProcessAffinityMask(service->process_handle, affinity)) {
\r
1755 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_SETPROCESSAFFINITYMASK_FAILED, service->name, error_string(GetLastError()), 0);
\r
1758 ResumeThread(pi.hThread);
\r
1762 Wait for a clean startup before changing the service status to RUNNING
\r
1763 but be mindful of the fact that we are blocking the service control manager
\r
1764 so abandon the wait before too much time has elapsed.
\r
1766 service->status.dwCurrentState = SERVICE_START_PENDING;
\r
1767 if (await_single_handle(service->status_handle, &service->status, service->process_handle, service->name, _T("start_service"), service->throttle_delay) == 1) service->throttle = 0;
\r
1769 /* Signal successful start */
\r
1770 service->status.dwCurrentState = SERVICE_RUNNING;
\r
1771 service->status.dwControlsAccepted &= ~SERVICE_ACCEPT_PAUSE_CONTINUE;
\r
1772 SetServiceStatus(service->status_handle, &service->status);
\r
1774 /* Post-start hook. */
\r
1775 if (! service->throttle) {
\r
1776 (void) nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_START, NSSM_HOOK_ACTION_POST, &control);
\r
1779 /* Ensure the restart delay is always applied. */
\r
1780 if (service->restart_delay && ! service->throttle) service->throttle++;
\r
1785 /* Stop the service */
\r
1786 int stop_service(nssm_service_t *service, unsigned long exitcode, bool graceful, bool default_action) {
\r
1787 service->allow_restart = false;
\r
1788 if (service->wait_handle) {
\r
1789 UnregisterWait(service->wait_handle);
\r
1790 service->wait_handle = 0;
\r
1793 service->rotate_stdout_online = service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;
\r
1795 if (default_action && ! exitcode && ! graceful) {
\r
1796 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
1800 /* Signal we are stopping */
\r
1802 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
1803 service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;
\r
1805 service->status.dwControlsAccepted = 0;
\r
1806 SetServiceStatus(service->status_handle, &service->status);
\r
1808 /* Nothing to do if service isn't running */
\r
1809 if (service->pid) {
\r
1810 /* Shut down service */
\r
1811 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_TERMINATEPROCESS, service->name, service->exe, 0);
\r
1813 service_kill_t(service, &k);
\r
1817 else log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_PROCESS_ALREADY_STOPPED, service->name, service->exe, 0);
\r
1819 end_service((void *) service, true);
\r
1821 /* Signal we stopped */
\r
1823 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
1824 wait_for_hooks(service, true);
\r
1825 service->status.dwCurrentState = SERVICE_STOPPED;
\r
1827 service->status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
\r
1828 service->status.dwServiceSpecificExitCode = exitcode;
\r
1831 service->status.dwWin32ExitCode = NO_ERROR;
\r
1832 service->status.dwServiceSpecificExitCode = 0;
\r
1834 SetServiceStatus(service->status_handle, &service->status);
\r
1840 /* Callback function triggered when the server exits */
\r
1841 void CALLBACK end_service(void *arg, unsigned char why) {
\r
1842 nssm_service_t *service = (nssm_service_t *) arg;
\r
1844 if (service->stopping) return;
\r
1846 service->stopping = true;
\r
1848 service->rotate_stdout_online = service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;
\r
1850 /* Use now as a dummy exit time. */
\r
1851 GetSystemTimeAsFileTime(&service->exit_time);
\r
1853 /* Check exit code */
\r
1854 unsigned long exitcode = 0;
\r
1856 if (service->process_handle) {
\r
1857 GetExitCodeProcess(service->process_handle, &exitcode);
\r
1858 service->exitcode = exitcode;
\r
1859 /* Check real exit time. */
\r
1860 if (exitcode != STILL_ACTIVE) get_process_exit_time(service->process_handle, &service->exit_time);
\r
1861 CloseHandle(service->process_handle);
\r
1864 service->process_handle = 0;
\r
1867 Log that the service ended BEFORE logging about killing the process
\r
1868 tree. See below for the possible values of the why argument.
\r
1871 _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), exitcode);
\r
1872 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ENDED_SERVICE, service->exe, service->name, code, 0);
\r
1876 if (exitcode == STILL_ACTIVE) exitcode = 0;
\r
1877 if (service->pid && service->kill_process_tree) {
\r
1879 service_kill_t(service, &k);
\r
1880 kill_process_tree(&k, service->pid);
\r
1885 service->exit_count++;
\r
1886 (void) nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_EXIT, NSSM_HOOK_ACTION_POST, NULL, NSSM_HOOK_DEADLINE, true);
\r
1889 The why argument is true if our wait timed out or false otherwise.
\r
1890 Our wait is infinite so why will never be true when called by the system.
\r
1891 If it is indeed true, assume we were called from stop_service() because
\r
1892 this is a controlled shutdown, and don't take any restart action.
\r
1895 if (! service->allow_restart) return;
\r
1897 /* What action should we take? */
\r
1898 int action = NSSM_EXIT_RESTART;
\r
1899 TCHAR action_string[ACTION_LEN];
\r
1900 bool default_action;
\r
1901 if (! get_exit_action(service->name, &exitcode, action_string, &default_action)) {
\r
1902 for (int i = 0; exit_action_strings[i]; i++) {
\r
1903 if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
1911 /* Try to restart the service or return failure code to service manager */
\r
1912 case NSSM_EXIT_RESTART:
\r
1913 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_RESTART, service->name, code, exit_action_strings[action], service->exe, 0);
\r
1914 while (monitor_service(service)) {
\r
1915 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_RESTART_SERVICE_FAILED, service->exe, service->name, 0);
\r
1920 /* Do nothing, just like srvany would */
\r
1921 case NSSM_EXIT_IGNORE:
\r
1922 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_IGNORE, service->name, code, exit_action_strings[action], service->exe, 0);
\r
1923 wait_for_hooks(service, false);
\r
1927 /* Tell the service manager we are finished */
\r
1928 case NSSM_EXIT_REALLY:
\r
1929 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_REALLY, service->name, code, exit_action_strings[action], 0);
\r
1930 stop_service(service, exitcode, true, default_action);
\r
1933 /* Fake a crash so pre-Vista service managers will run recovery actions. */
\r
1934 case NSSM_EXIT_UNCLEAN:
\r
1935 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_UNCLEAN, service->name, code, exit_action_strings[action], 0);
\r
1936 stop_service(service, exitcode, false, default_action);
\r
1937 wait_for_hooks(service, false);
\r
1944 void throttle_restart(nssm_service_t *service) {
\r
1945 /* This can't be a restart if the service is already running. */
\r
1946 if (! service->throttle++) return;
\r
1949 unsigned long throttle_ms = throttle_milliseconds(service->throttle);
\r
1950 TCHAR threshold[8], milliseconds[8];
\r
1952 if (service->restart_delay > throttle_ms) ms = service->restart_delay;
\r
1953 else ms = throttle_ms;
\r
1955 _sntprintf_s(milliseconds, _countof(milliseconds), _TRUNCATE, _T("%lu"), ms);
\r
1957 if (service->throttle == 1 && service->restart_delay > throttle_ms) log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESTART_DELAY, service->name, milliseconds, 0);
\r
1959 _sntprintf_s(threshold, _countof(threshold), _TRUNCATE, _T("%lu"), service->throttle_delay);
\r
1960 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_THROTTLED, service->name, threshold, milliseconds, 0);
\r
1963 if (use_critical_section) EnterCriticalSection(&service->throttle_section);
\r
1964 else if (service->throttle_timer) {
\r
1965 ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));
\r
1966 service->throttle_duetime.QuadPart = 0 - (ms * 10000LL);
\r
1967 SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);
\r
1970 service->status.dwCurrentState = SERVICE_PAUSED;
\r
1971 service->status.dwControlsAccepted |= SERVICE_ACCEPT_PAUSE_CONTINUE;
\r
1972 SetServiceStatus(service->status_handle, &service->status);
\r
1974 if (use_critical_section) {
\r
1975 imports.SleepConditionVariableCS(&service->throttle_condition, &service->throttle_section, ms);
\r
1976 LeaveCriticalSection(&service->throttle_section);
\r
1979 if (service->throttle_timer) WaitForSingleObject(service->throttle_timer, INFINITE);
\r
1985 When responding to a stop (or any other) request we need to set dwWaitHint to
\r
1986 the number of milliseconds we expect the operation to take, and optionally
\r
1987 increase dwCheckPoint. If dwWaitHint milliseconds elapses without the
\r
1988 operation completing or dwCheckPoint increasing, the system will consider the
\r
1989 service to be hung.
\r
1991 However the system will consider the service to be hung after 30000
\r
1992 milliseconds regardless of the value of dwWaitHint if dwCheckPoint has not
\r
1993 changed. Therefore if we want to wait longer than that we must periodically
\r
1994 increase dwCheckPoint.
\r
1996 Furthermore, it will consider the service to be hung after 60000 milliseconds
\r
1997 regardless of the value of dwCheckPoint unless dwWaitHint is increased every
\r
1998 time dwCheckPoint is also increased.
\r
2000 Our strategy then is to retrieve the initial dwWaitHint and wait for
\r
2001 NSSM_SERVICE_STATUS_DEADLINE milliseconds. If the process is still running
\r
2002 and we haven't finished waiting we increment dwCheckPoint and add whichever is
\r
2003 smaller of NSSM_SERVICE_STATUS_DEADLINE or the remaining timeout to
\r
2006 Only doing both these things will prevent the system from killing the service.
\r
2008 If the status_handle and service_status arguments are omitted, this function
\r
2009 will not try to update the service manager but it will still log to the
\r
2010 event log that it is waiting for a handle.
\r
2012 Returns: 1 if the wait timed out.
\r
2013 0 if the wait completed.
\r
2016 int await_single_handle(SERVICE_STATUS_HANDLE status_handle, SERVICE_STATUS *status, HANDLE handle, TCHAR *name, TCHAR *function_name, unsigned long timeout) {
\r
2017 unsigned long interval;
\r
2018 unsigned long ret;
\r
2019 unsigned long waited;
\r
2020 TCHAR interval_milliseconds[16];
\r
2021 TCHAR timeout_milliseconds[16];
\r
2022 TCHAR waited_milliseconds[16];
\r
2023 TCHAR *function = function_name;
\r
2025 /* Add brackets to function name. */
\r
2026 size_t funclen = _tcslen(function_name) + 3;
\r
2027 TCHAR *func = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, funclen * sizeof(TCHAR));
\r
2029 if (_sntprintf_s(func, funclen, _TRUNCATE, _T("%s()"), function_name) > -1) function = func;
\r
2032 _sntprintf_s(timeout_milliseconds, _countof(timeout_milliseconds), _TRUNCATE, _T("%lu"), timeout);
\r
2035 while (waited < timeout) {
\r
2036 interval = timeout - waited;
\r
2037 if (interval > NSSM_SERVICE_STATUS_DEADLINE) interval = NSSM_SERVICE_STATUS_DEADLINE;
\r
2040 status->dwWaitHint += interval;
\r
2041 status->dwCheckPoint++;
\r
2042 SetServiceStatus(status_handle, status);
\r
2046 _sntprintf_s(waited_milliseconds, _countof(waited_milliseconds), _TRUNCATE, _T("%lu"), waited);
\r
2047 _sntprintf_s(interval_milliseconds, _countof(interval_milliseconds), _TRUNCATE, _T("%lu"), interval);
\r
2048 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_AWAITING_SINGLE_HANDLE, function, name, waited_milliseconds, interval_milliseconds, timeout_milliseconds, 0);
\r
2051 switch (WaitForSingleObject(handle, interval)) {
\r
2052 case WAIT_OBJECT_0:
\r
2056 case WAIT_TIMEOUT:
\r
2065 waited += interval;
\r
2069 if (func) HeapFree(GetProcessHeap(), 0, func);
\r