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_RUNNING:
\r
42 case SERVICE_STOP_PENDING:
\r
45 case SERVICE_STOPPED:
\r
52 case SERVICE_CONTROL_PAUSE:
\r
54 case SERVICE_PAUSE_PENDING:
\r
57 case SERVICE_PAUSED:
\r
64 case SERVICE_CONTROL_CONTINUE:
\r
66 case SERVICE_CONTINUE_PENDING:
\r
69 case SERVICE_RUNNING:
\r
76 case SERVICE_CONTROL_INTERROGATE:
\r
77 case NSSM_SERVICE_CONTROL_ROTATE:
\r
84 static inline int await_service_control_response(unsigned long control, SC_HANDLE service_handle, SERVICE_STATUS *service_status, unsigned long initial_status) {
\r
86 unsigned long checkpoint = 0;
\r
87 unsigned long waithint = 0;
\r
88 while (QueryServiceStatus(service_handle, service_status)) {
\r
89 int response = service_control_response(control, service_status->dwCurrentState);
\r
90 /* Alas we can't WaitForSingleObject() on an SC_HANDLE. */
\r
91 if (! response) return response;
\r
92 if (response > 0 || service_status->dwCurrentState == initial_status) {
\r
93 if (service_status->dwCheckPoint != checkpoint || service_status->dwWaitHint != waithint) tries = 0;
\r
94 checkpoint = service_status->dwCheckPoint;
\r
95 waithint = service_status->dwWaitHint;
\r
96 if (++tries > 10) tries = 10;
\r
99 else return response;
\r
104 int affinity_mask_to_string(__int64 mask, TCHAR **string) {
\r
105 if (! string) return 1;
\r
113 /* SetProcessAffinityMask() accepts a mask of up to 64 processors. */
\r
115 for (n = 0; n < _countof(set); n++) set[n].first = set[n].last = -1;
\r
117 for (i = 0, n = 0; i < _countof(set); i++) {
\r
118 if (mask & (1LL << i)) {
\r
119 if (set[n].first == -1) set[n].first = set[n].last = (int) i;
\r
120 else if (set[n].last == (int) i - 1) set[n].last = (int) i;
\r
123 set[n].first = set[n].last = (int) i;
\r
128 /* Worst case is 2x2 characters for first and last CPU plus - and/or , */
\r
129 size_t len = (size_t) (n + 1) * 6;
\r
130 *string = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len * sizeof(TCHAR));
\r
131 if (! string) return 2;
\r
135 for (i = 0; i <= n; i++) {
\r
136 if (i) (*string)[s++] = _T(',');
\r
137 ret = _sntprintf_s(*string + s, 3, _TRUNCATE, _T("%u"), set[i].first);
\r
139 HeapFree(GetProcessHeap(), 0, *string);
\r
144 if (set[i].last != set[i].first) {
\r
145 ret =_sntprintf_s(*string + s, 4, _TRUNCATE, _T("%c%u"), (set[i].last == set[i].first + 1) ? _T(',') : _T('-'), set[i].last);
\r
147 HeapFree(GetProcessHeap(), 0, *string);
\r
158 int affinity_string_to_mask(TCHAR *string, __int64 *mask) {
\r
159 if (! mask) return 1;
\r
162 if (! string) return 0;
\r
171 unsigned long number;
\r
173 for (n = 0; n < _countof(set); n++) set[n].first = set[n].last = -1;
\r
177 ret = str_number(s, &number, &end);
\r
179 if (ret == 0 || ret == 2) {
\r
180 if (number >= _countof(set)) return 2;
\r
181 set[n].first = set[n].last = (int) number;
\r
193 if (! *(++s)) return 3;
\r
194 ret = str_number(s, &number, &end);
\r
195 if (ret == 0 || ret == 2) {
\r
197 if (! *s || *s == _T(',')) {
\r
198 set[n].last = (int) number;
\r
215 for (i = 0; i <= n; i++) {
\r
216 for (int j = set[i].first; j <= set[i].last; j++) (__int64) *mask |= (1LL << (__int64) j);
\r
222 inline unsigned long priority_mask() {
\r
223 return REALTIME_PRIORITY_CLASS | HIGH_PRIORITY_CLASS | ABOVE_NORMAL_PRIORITY_CLASS | NORMAL_PRIORITY_CLASS | BELOW_NORMAL_PRIORITY_CLASS | IDLE_PRIORITY_CLASS;
\r
226 int priority_constant_to_index(unsigned long constant) {
\r
227 switch (constant & priority_mask()) {
\r
228 case REALTIME_PRIORITY_CLASS: return NSSM_REALTIME_PRIORITY;
\r
229 case HIGH_PRIORITY_CLASS: return NSSM_HIGH_PRIORITY;
\r
230 case ABOVE_NORMAL_PRIORITY_CLASS: return NSSM_ABOVE_NORMAL_PRIORITY;
\r
231 case BELOW_NORMAL_PRIORITY_CLASS: return NSSM_BELOW_NORMAL_PRIORITY;
\r
232 case IDLE_PRIORITY_CLASS: return NSSM_IDLE_PRIORITY;
\r
234 return NSSM_NORMAL_PRIORITY;
\r
237 unsigned long priority_index_to_constant(int index) {
\r
239 case NSSM_REALTIME_PRIORITY: return REALTIME_PRIORITY_CLASS;
\r
240 case NSSM_HIGH_PRIORITY: return HIGH_PRIORITY_CLASS;
\r
241 case NSSM_ABOVE_NORMAL_PRIORITY: return ABOVE_NORMAL_PRIORITY_CLASS;
\r
242 case NSSM_BELOW_NORMAL_PRIORITY: return BELOW_NORMAL_PRIORITY_CLASS;
\r
243 case NSSM_IDLE_PRIORITY: return IDLE_PRIORITY_CLASS;
\r
245 return NORMAL_PRIORITY_CLASS;
\r
248 static inline unsigned long throttle_milliseconds(unsigned long throttle) {
\r
249 /* pow() operates on doubles. */
\r
250 unsigned long ret = 1; for (unsigned long i = 1; i < throttle; i++) ret *= 2;
\r
255 Wrapper to be called in a new thread so that we can acknowledge a STOP
\r
256 control immediately.
\r
258 static unsigned long WINAPI shutdown_service(void *arg) {
\r
259 return stop_service((nssm_service_t *) arg, 0, true, true);
\r
262 /* Connect to the service manager */
\r
263 SC_HANDLE open_service_manager(unsigned long access) {
\r
264 SC_HANDLE ret = OpenSCManager(0, SERVICES_ACTIVE_DATABASE, access);
\r
266 if (is_admin) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENSCMANAGER_FAILED, 0);
\r
273 /* Open a service by name or display name. */
\r
274 SC_HANDLE open_service(SC_HANDLE services, TCHAR *service_name, unsigned long access, TCHAR *canonical_name, unsigned long canonical_namelen) {
\r
275 SC_HANDLE service_handle = OpenService(services, service_name, access);
\r
276 if (service_handle) {
\r
277 if (canonical_name && canonical_name != service_name) {
\r
278 TCHAR displayname[SERVICE_NAME_LENGTH];
\r
279 unsigned long displayname_len = (unsigned long) _countof(displayname);
\r
280 GetServiceDisplayName(services, service_name, displayname, &displayname_len);
\r
281 unsigned long keyname_len = canonical_namelen;
\r
282 GetServiceKeyName(services, displayname, canonical_name, &keyname_len);
\r
284 return service_handle;
\r
287 unsigned long error = GetLastError();
\r
288 if (error != ERROR_SERVICE_DOES_NOT_EXIST) {
\r
289 print_message(stderr, NSSM_MESSAGE_OPENSERVICE_FAILED, error_string(GetLastError()));
\r
293 /* We can't look for a display name because there's no buffer to store it. */
\r
294 if (! canonical_name) {
\r
295 print_message(stderr, NSSM_MESSAGE_OPENSERVICE_FAILED, error_string(GetLastError()));
\r
299 unsigned long bufsize, required, count, i;
\r
300 unsigned long resume = 0;
\r
301 EnumServicesStatus(services, SERVICE_DRIVER | SERVICE_FILE_SYSTEM_DRIVER | SERVICE_KERNEL_DRIVER | SERVICE_WIN32, SERVICE_STATE_ALL, 0, 0, &required, &count, &resume);
\r
302 error = GetLastError();
\r
303 if (error != ERROR_MORE_DATA) {
\r
304 print_message(stderr, NSSM_MESSAGE_ENUMSERVICESSTATUS_FAILED, error_string(GetLastError()));
\r
308 ENUM_SERVICE_STATUS *status = (ENUM_SERVICE_STATUS *) HeapAlloc(GetProcessHeap(), 0, required);
\r
310 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("ENUM_SERVICE_STATUS"), _T("open_service()"));
\r
314 bufsize = required;
\r
317 EnumServicesStatus() returns:
\r
318 1 when it retrieved data and there's no more data to come.
\r
319 0 and sets last error to ERROR_MORE_DATA when it retrieved data and
\r
320 there's more data to come.
\r
321 0 and sets last error to something else on error.
\r
323 int ret = EnumServicesStatus(services, SERVICE_DRIVER | SERVICE_FILE_SYSTEM_DRIVER | SERVICE_KERNEL_DRIVER | SERVICE_WIN32, SERVICE_STATE_ALL, status, bufsize, &required, &count, &resume);
\r
325 error = GetLastError();
\r
326 if (error != ERROR_MORE_DATA) {
\r
327 HeapFree(GetProcessHeap(), 0, status);
\r
328 print_message(stderr, NSSM_MESSAGE_ENUMSERVICESSTATUS_FAILED, error_string(GetLastError()));
\r
333 for (i = 0; i < count; i++) {
\r
334 if (str_equiv(status[i].lpDisplayName, service_name)) {
\r
335 if (_sntprintf_s(canonical_name, canonical_namelen, _TRUNCATE, _T("%s"), status[i].lpServiceName) < 0) {
\r
336 HeapFree(GetProcessHeap(), 0, status);
\r
337 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("canonical_name"), _T("open_service()"));
\r
341 HeapFree(GetProcessHeap(), 0, status);
\r
342 return open_service(services, canonical_name, access, 0, 0);
\r
349 /* Recurse so we can get an error message. */
\r
350 return open_service(services, service_name, access, 0, 0);
\r
353 QUERY_SERVICE_CONFIG *query_service_config(const TCHAR *service_name, SC_HANDLE service_handle) {
\r
354 QUERY_SERVICE_CONFIG *qsc;
\r
355 unsigned long bufsize;
\r
356 unsigned long error;
\r
358 QueryServiceConfig(service_handle, 0, 0, &bufsize);
\r
359 error = GetLastError();
\r
360 if (error == ERROR_INSUFFICIENT_BUFFER) {
\r
361 qsc = (QUERY_SERVICE_CONFIG *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, bufsize);
\r
363 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("QUERY_SERVICE_CONFIG"), _T("query_service_config()"), 0);
\r
368 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG_FAILED, service_name, error_string(error), 0);
\r
372 if (! QueryServiceConfig(service_handle, qsc, bufsize, &bufsize)) {
\r
373 HeapFree(GetProcessHeap(), 0, qsc);
\r
374 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG_FAILED, service_name, error_string(GetLastError()), 0);
\r
381 int set_service_dependencies(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR *buffer) {
\r
382 TCHAR *dependencies = _T("");
\r
383 unsigned long num_dependencies = 0;
\r
385 if (buffer && buffer[0]) {
\r
386 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
\r
388 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
393 Count the dependencies then allocate a buffer big enough for their
\r
394 canonical names, ie n * SERVICE_NAME_LENGTH.
\r
398 for (s = buffer; *s; s++) {
\r
399 num_dependencies++;
\r
400 if (*s == SC_GROUP_IDENTIFIER) groups = s;
\r
404 /* At least one dependency is a group so we need to verify them. */
\r
407 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, NSSM_REGISTRY_GROUPS, 0, KEY_READ, &key)) {
\r
408 _ftprintf(stderr, _T("%s: %s\n"), NSSM_REGISTRY_GROUPS, error_string(GetLastError()));
\r
412 unsigned long type;
\r
413 unsigned long groupslen;
\r
414 unsigned long ret = RegQueryValueEx(key, NSSM_REG_GROUPS, 0, &type, NULL, &groupslen);
\r
415 if (ret == ERROR_SUCCESS) {
\r
416 groups = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, groupslen);
\r
418 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("groups"), _T("set_service_dependencies()"));
\r
422 ret = RegQueryValueEx(key, NSSM_REG_GROUPS, 0, &type, (unsigned char *) groups, &groupslen);
\r
423 if (ret != ERROR_SUCCESS) {
\r
424 _ftprintf(stderr, _T("%s\\%s: %s"), NSSM_REGISTRY_GROUPS, NSSM_REG_GROUPS, error_string(GetLastError()));
\r
425 HeapFree(GetProcessHeap(), 0, groups);
\r
430 else if (ret != ERROR_FILE_NOT_FOUND) {
\r
431 _ftprintf(stderr, _T("%s\\%s: %s"), NSSM_REGISTRY_GROUPS, NSSM_REG_GROUPS, error_string(GetLastError()));
\r
440 unsigned long dependencieslen = (num_dependencies * SERVICE_NAME_LENGTH) + 2;
\r
441 dependencies = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dependencieslen * sizeof(TCHAR));
\r
444 TCHAR dependency[SERVICE_NAME_LENGTH];
\r
445 for (s = buffer; *s; s++) {
\r
447 if (*s == SC_GROUP_IDENTIFIER) {
\r
448 TCHAR *group = s + 1;
\r
452 for (TCHAR *g = groups; *g; g++) {
\r
453 if (str_equiv(g, group)) {
\r
455 /* Set canonical name. */
\r
456 memmove(group, g, _tcslen(g) * sizeof(TCHAR));
\r
464 if (ok) _sntprintf_s(dependency, _countof(dependency), _TRUNCATE, _T("%s"), s);
\r
466 HeapFree(GetProcessHeap(), 0, dependencies);
\r
467 if (groups) HeapFree(GetProcessHeap(), 0, groups);
\r
468 _ftprintf(stderr, _T("%s: %s"), s, error_string(ERROR_SERVICE_DEPENDENCY_DELETED));
\r
473 SC_HANDLE dependency_handle = open_service(services, s, SERVICE_QUERY_STATUS, dependency, _countof(dependency));
\r
474 if (! dependency_handle) {
\r
475 HeapFree(GetProcessHeap(), 0, dependencies);
\r
476 if (groups) HeapFree(GetProcessHeap(), 0, groups);
\r
477 CloseServiceHandle(services);
\r
478 _ftprintf(stderr, _T("%s: %s"), s, error_string(ERROR_SERVICE_DEPENDENCY_DELETED));
\r
483 size_t len = _tcslen(dependency) + 1;
\r
484 memmove(dependencies + i, dependency, len * sizeof(TCHAR));
\r
490 if (groups) HeapFree(GetProcessHeap(), 0, groups);
\r
491 CloseServiceHandle(services);
\r
494 if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, dependencies, 0, 0, 0)) {
\r
495 if (num_dependencies) HeapFree(GetProcessHeap(), 0, dependencies);
\r
496 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
500 if (num_dependencies) HeapFree(GetProcessHeap(), 0, dependencies);
\r
504 int get_service_dependencies(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR **buffer, unsigned long *bufsize, int type) {
\r
505 if (! buffer) return 1;
\r
506 if (! bufsize) return 2;
\r
511 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
512 if (! qsc) return 3;
\r
514 if (! qsc->lpDependencies) return 0;
\r
515 if (! qsc->lpDependencies[0]) return 0;
\r
517 /* lpDependencies is doubly NULL terminated. */
\r
518 while (qsc->lpDependencies[*bufsize]) {
\r
519 while (qsc->lpDependencies[*bufsize]) ++*bufsize;
\r
525 *buffer = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *bufsize * sizeof(TCHAR));
\r
528 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("lpDependencies"), _T("get_service_dependencies()"));
\r
532 if (type == DEPENDENCY_ALL) memmove(*buffer, qsc->lpDependencies, *bufsize * sizeof(TCHAR));
\r
537 for (s = qsc->lpDependencies; *s; s++) {
\r
538 /* Only copy the appropriate type of dependency. */
\r
539 if ((*s == SC_GROUP_IDENTIFIER && type & DEPENDENCY_GROUPS) || (*s != SC_GROUP_IDENTIFIER && type & DEPENDENCY_SERVICES)) {
\r
540 size_t len = _tcslen(s) + 1;
\r
541 *bufsize += (unsigned long) len;
\r
542 memmove(*buffer + i, s, len * sizeof(TCHAR));
\r
551 HeapFree(GetProcessHeap(), 0, qsc);
\r
556 int get_service_dependencies(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR **buffer, unsigned long *bufsize) {
\r
557 return get_service_dependencies(service_name, service_handle, buffer, bufsize, DEPENDENCY_ALL);
\r
560 int set_service_description(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR *buffer) {
\r
561 SERVICE_DESCRIPTION description;
\r
562 ZeroMemory(&description, sizeof(description));
\r
564 lpDescription must be NULL if we aren't changing, the new description
\r
567 if (buffer && buffer[0]) description.lpDescription = buffer;
\r
568 else description.lpDescription = _T("");
\r
570 if (ChangeServiceConfig2(service_handle, SERVICE_CONFIG_DESCRIPTION, &description)) return 0;
\r
572 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_DESCRIPTION_FAILED, service_name, error_string(GetLastError()), 0);
\r
576 int get_service_description(const TCHAR *service_name, SC_HANDLE service_handle, unsigned long len, TCHAR *buffer) {
\r
577 if (! buffer) return 1;
\r
579 unsigned long bufsize;
\r
580 QueryServiceConfig2(service_handle, SERVICE_CONFIG_DESCRIPTION, 0, 0, &bufsize);
\r
581 unsigned long error = GetLastError();
\r
582 if (error == ERROR_INSUFFICIENT_BUFFER) {
\r
583 SERVICE_DESCRIPTION *description = (SERVICE_DESCRIPTION *) HeapAlloc(GetProcessHeap(), 0, bufsize);
\r
584 if (! description) {
\r
585 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("SERVICE_CONFIG_DESCRIPTION"), _T("get_service_description()"));
\r
589 if (QueryServiceConfig2(service_handle, SERVICE_CONFIG_DESCRIPTION, (unsigned char *) description, bufsize, &bufsize)) {
\r
590 if (description->lpDescription) _sntprintf_s(buffer, len, _TRUNCATE, _T("%s"), description->lpDescription);
\r
591 else ZeroMemory(buffer, len * sizeof(TCHAR));
\r
592 HeapFree(GetProcessHeap(), 0, description);
\r
596 HeapFree(GetProcessHeap(), 0, description);
\r
597 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_CONFIG_DESCRIPTION"), error_string(error));
\r
602 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_CONFIG_DESCRIPTION"), error_string(error));
\r
609 int get_service_startup(const TCHAR *service_name, SC_HANDLE service_handle, const QUERY_SERVICE_CONFIG *qsc, unsigned long *startup) {
\r
610 if (! qsc) return 1;
\r
612 switch (qsc->dwStartType) {
\r
613 case SERVICE_DEMAND_START: *startup = NSSM_STARTUP_MANUAL; break;
\r
614 case SERVICE_DISABLED: *startup = NSSM_STARTUP_DISABLED; break;
\r
615 default: *startup = NSSM_STARTUP_AUTOMATIC;
\r
618 if (*startup != NSSM_STARTUP_AUTOMATIC) return 0;
\r
620 /* Check for delayed start. */
\r
621 unsigned long bufsize;
\r
622 unsigned long error;
\r
623 QueryServiceConfig2(service_handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, 0, 0, &bufsize);
\r
624 error = GetLastError();
\r
625 if (error == ERROR_INSUFFICIENT_BUFFER) {
\r
626 SERVICE_DELAYED_AUTO_START_INFO *info = (SERVICE_DELAYED_AUTO_START_INFO *) HeapAlloc(GetProcessHeap(), 0, bufsize);
\r
628 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("SERVICE_DELAYED_AUTO_START_INFO"), _T("get_service_startup()"));
\r
632 if (QueryServiceConfig2(service_handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, (unsigned char *) info, bufsize, &bufsize)) {
\r
633 if (info->fDelayedAutostart) *startup = NSSM_STARTUP_DELAYED;
\r
634 HeapFree(GetProcessHeap(), 0, info);
\r
638 error = GetLastError();
\r
639 if (error != ERROR_INVALID_LEVEL) {
\r
640 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_CONFIG_DELAYED_AUTO_START_INFO"), error_string(error));
\r
645 else if (error != ERROR_INVALID_LEVEL) {
\r
646 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_DELAYED_AUTO_START_INFO"), error_string(error));
\r
653 int get_service_username(const TCHAR *service_name, const QUERY_SERVICE_CONFIG *qsc, TCHAR **username, size_t *usernamelen) {
\r
654 if (! username) return 1;
\r
655 if (! usernamelen) return 1;
\r
660 if (! qsc) return 1;
\r
662 if (qsc->lpServiceStartName[0]) {
\r
663 if (is_localsystem(qsc->lpServiceStartName)) return 0;
\r
665 size_t len = _tcslen(qsc->lpServiceStartName);
\r
666 *username = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(TCHAR));
\r
668 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("username"), _T("get_service_username()"));
\r
672 memmove(*username, qsc->lpServiceStartName, (len + 1) * sizeof(TCHAR));
\r
673 *usernamelen = len;
\r
679 /* Set default values which aren't zero. */
\r
680 void set_nssm_service_defaults(nssm_service_t *service) {
\r
681 if (! service) return;
\r
683 service->type = SERVICE_WIN32_OWN_PROCESS;
\r
684 service->priority = NORMAL_PRIORITY_CLASS;
\r
685 service->stdin_sharing = NSSM_STDIN_SHARING;
\r
686 service->stdin_disposition = NSSM_STDIN_DISPOSITION;
\r
687 service->stdin_flags = NSSM_STDIN_FLAGS;
\r
688 service->stdout_sharing = NSSM_STDOUT_SHARING;
\r
689 service->stdout_disposition = NSSM_STDOUT_DISPOSITION;
\r
690 service->stdout_flags = NSSM_STDOUT_FLAGS;
\r
691 service->stderr_sharing = NSSM_STDERR_SHARING;
\r
692 service->stderr_disposition = NSSM_STDERR_DISPOSITION;
\r
693 service->stderr_flags = NSSM_STDERR_FLAGS;
\r
694 service->throttle_delay = NSSM_RESET_THROTTLE_RESTART;
\r
695 service->stop_method = ~0;
\r
696 service->kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;
\r
697 service->kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;
\r
698 service->kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;
\r
699 service->kill_process_tree = 1;
\r
702 /* Allocate and zero memory for a service. */
\r
703 nssm_service_t *alloc_nssm_service() {
\r
704 nssm_service_t *service = (nssm_service_t *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(nssm_service_t));
\r
705 if (! service) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("service"), _T("alloc_nssm_service()"), 0);
\r
709 /* Free memory for a service. */
\r
710 void cleanup_nssm_service(nssm_service_t *service) {
\r
711 if (! service) return;
\r
712 if (service->username) HeapFree(GetProcessHeap(), 0, service->username);
\r
713 if (service->password) {
\r
714 SecureZeroMemory(service->password, service->passwordlen);
\r
715 HeapFree(GetProcessHeap(), 0, service->password);
\r
717 if (service->dependencies) HeapFree(GetProcessHeap(), 0, service->dependencies);
\r
718 if (service->env) HeapFree(GetProcessHeap(), 0, service->env);
\r
719 if (service->env_extra) HeapFree(GetProcessHeap(), 0, service->env_extra);
\r
720 if (service->handle) CloseServiceHandle(service->handle);
\r
721 if (service->process_handle) CloseHandle(service->process_handle);
\r
722 if (service->wait_handle) UnregisterWait(service->wait_handle);
\r
723 if (service->throttle_section_initialised) DeleteCriticalSection(&service->throttle_section);
\r
724 if (service->throttle_timer) CloseHandle(service->throttle_timer);
\r
725 if (service->initial_env) FreeEnvironmentStrings(service->initial_env);
\r
726 HeapFree(GetProcessHeap(), 0, service);
\r
729 /* About to install the service */
\r
730 int pre_install_service(int argc, TCHAR **argv) {
\r
731 nssm_service_t *service = alloc_nssm_service();
\r
732 set_nssm_service_defaults(service);
\r
733 if (argc) _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]);
\r
735 /* Show the dialogue box if we didn't give the service name and path */
\r
736 if (argc < 2) return nssm_gui(IDD_INSTALL, service);
\r
739 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("service"), _T("pre_install_service()"));
\r
742 _sntprintf_s(service->exe, _countof(service->exe), _TRUNCATE, _T("%s"), argv[1]);
\r
744 /* Arguments are optional */
\r
745 size_t flagslen = 0;
\r
748 for (i = 2; i < argc; i++) flagslen += _tcslen(argv[i]) + 1;
\r
749 if (! flagslen) flagslen = 1;
\r
750 if (flagslen > _countof(service->flags)) {
\r
751 print_message(stderr, NSSM_MESSAGE_FLAGS_TOO_LONG);
\r
755 for (i = 2; i < argc; i++) {
\r
756 size_t len = _tcslen(argv[i]);
\r
757 memmove(service->flags + s, argv[i], len * sizeof(TCHAR));
\r
759 if (i < argc - 1) service->flags[s++] = _T(' ');
\r
762 /* Work out directory name */
\r
763 _sntprintf_s(service->dir, _countof(service->dir), _TRUNCATE, _T("%s"), service->exe);
\r
764 strip_basename(service->dir);
\r
766 int ret = install_service(service);
\r
767 cleanup_nssm_service(service);
\r
771 /* About to edit the service. */
\r
772 int pre_edit_service(int argc, TCHAR **argv) {
\r
773 /* Require service name. */
\r
774 if (argc < 2) return usage(1);
\r
776 /* Are we editing on the command line? */
\r
777 enum { MODE_EDITING, MODE_GETTING, MODE_SETTING, MODE_RESETTING } mode = MODE_EDITING;
\r
778 const TCHAR *verb = argv[0];
\r
779 const TCHAR *service_name = argv[1];
\r
780 bool getting = false;
\r
781 bool unsetting = false;
\r
783 /* Minimum number of arguments. */
\r
785 /* Index of first value. */
\r
788 if (str_equiv(verb, _T("get"))) {
\r
790 mode = MODE_GETTING;
\r
792 else if (str_equiv(verb, _T("set"))) {
\r
794 mode = MODE_SETTING;
\r
796 else if (str_equiv(verb, _T("reset")) || str_equiv(verb, _T("unset"))) {
\r
798 mode = MODE_RESETTING;
\r
800 if (argc < mandatory) return usage(1);
\r
802 const TCHAR *parameter = 0;
\r
803 settings_t *setting = 0;
\r
806 /* Validate the parameter. */
\r
807 if (mandatory > 2) {
\r
808 bool additional_mandatory = false;
\r
810 parameter = argv[2];
\r
811 for (i = 0; settings[i].name; i++) {
\r
812 setting = &settings[i];
\r
813 if (! str_equiv(setting->name, parameter)) continue;
\r
814 if (((setting->additional & ADDITIONAL_GETTING) && mode == MODE_GETTING) || ((setting->additional & ADDITIONAL_SETTING) && mode == MODE_SETTING) || ((setting->additional & ADDITIONAL_RESETTING) && mode == MODE_RESETTING)) {
\r
815 additional_mandatory = true;
\r
820 if (! settings[i].name) {
\r
821 print_message(stderr, NSSM_MESSAGE_INVALID_PARAMETER, parameter);
\r
822 for (i = 0; settings[i].name; i++) _ftprintf(stderr, _T("%s\n"), settings[i].name);
\r
827 if (additional_mandatory) {
\r
828 if (argc < mandatory) {
\r
829 print_message(stderr, NSSM_MESSAGE_MISSING_SUBPARAMETER, parameter);
\r
832 additional = argv[3];
\r
835 else if (str_equiv(setting->name, NSSM_NATIVE_OBJECTNAME) && mode == MODE_SETTING) {
\r
836 additional = argv[3];
\r
840 additional = argv[remainder];
\r
841 if (argc < mandatory) return usage(1);
\r
845 nssm_service_t *service = alloc_nssm_service();
\r
846 _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), service_name);
\r
848 /* Open service manager */
\r
849 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
\r
851 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
855 /* Try to open the service */
\r
856 unsigned long access = SERVICE_QUERY_CONFIG;
\r
857 if (mode != MODE_GETTING) access |= SERVICE_CHANGE_CONFIG;
\r
858 service->handle = open_service(services, service->name, access, service->name, _countof(service->name));
\r
859 if (! service->handle) {
\r
860 CloseServiceHandle(services);
\r
864 /* Get system details. */
\r
865 QUERY_SERVICE_CONFIG *qsc = query_service_config(service->name, service->handle);
\r
867 CloseServiceHandle(service->handle);
\r
868 CloseServiceHandle(services);
\r
872 service->type = qsc->dwServiceType;
\r
873 if (! (service->type & SERVICE_WIN32_OWN_PROCESS)) {
\r
874 if (mode != MODE_GETTING) {
\r
875 HeapFree(GetProcessHeap(), 0, qsc);
\r
876 CloseServiceHandle(service->handle);
\r
877 CloseServiceHandle(services);
\r
878 print_message(stderr, NSSM_MESSAGE_CANNOT_EDIT, service->name, NSSM_WIN32_OWN_PROCESS, 0);
\r
883 if (get_service_startup(service->name, service->handle, qsc, &service->startup)) {
\r
884 if (mode != MODE_GETTING) {
\r
885 HeapFree(GetProcessHeap(), 0, qsc);
\r
886 CloseServiceHandle(service->handle);
\r
887 CloseServiceHandle(services);
\r
892 if (get_service_username(service->name, qsc, &service->username, &service->usernamelen)) {
\r
893 if (mode != MODE_GETTING) {
\r
894 HeapFree(GetProcessHeap(), 0, qsc);
\r
895 CloseServiceHandle(service->handle);
\r
896 CloseServiceHandle(services);
\r
901 _sntprintf_s(service->displayname, _countof(service->displayname), _TRUNCATE, _T("%s"), qsc->lpDisplayName);
\r
903 /* Get the canonical service name. We open it case insensitively. */
\r
904 unsigned long bufsize = _countof(service->name);
\r
905 GetServiceKeyName(services, service->displayname, service->name, &bufsize);
\r
907 /* Remember the executable in case it isn't NSSM. */
\r
908 _sntprintf_s(service->image, _countof(service->image), _TRUNCATE, _T("%s"), qsc->lpBinaryPathName);
\r
909 HeapFree(GetProcessHeap(), 0, qsc);
\r
911 /* Get extended system details. */
\r
912 if (get_service_description(service->name, service->handle, _countof(service->description), service->description)) {
\r
913 if (mode != MODE_GETTING) {
\r
914 CloseServiceHandle(service->handle);
\r
915 CloseServiceHandle(services);
\r
920 if (get_service_dependencies(service->name, service->handle, &service->dependencies, &service->dependencieslen)) {
\r
921 if (mode != MODE_GETTING) {
\r
922 CloseServiceHandle(service->handle);
\r
923 CloseServiceHandle(services);
\r
928 /* Get NSSM details. */
\r
929 get_parameters(service, 0);
\r
931 CloseServiceHandle(services);
\r
933 if (! service->exe[0]) {
\r
934 service->native = true;
\r
935 if (mode != MODE_GETTING) print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE, service->name, NSSM, service->image);
\r
938 /* Editing with the GUI. */
\r
939 if (mode == MODE_EDITING) {
\r
940 nssm_gui(IDD_EDIT, service);
\r
944 /* Trying to manage App* parameters for a non-NSSM service. */
\r
945 if (! setting->native && service->native) {
\r
946 CloseServiceHandle(service->handle);
\r
947 print_message(stderr, NSSM_MESSAGE_NATIVE_PARAMETER, setting->name, NSSM);
\r
955 if (mode == MODE_GETTING) {
\r
956 if (! service->native) {
\r
957 key = open_registry(service->name, KEY_READ);
\r
958 if (! key) return 4;
\r
961 if (setting->native) ret = get_setting(service->name, service->handle, setting, &value, additional);
\r
962 else ret = get_setting(service->name, key, setting, &value, additional);
\r
964 CloseServiceHandle(service->handle);
\r
968 switch (setting->type) {
\r
969 case REG_EXPAND_SZ:
\r
972 _tprintf(_T("%s\n"), value.string ? value.string : _T(""));
\r
973 HeapFree(GetProcessHeap(), 0, value.string);
\r
977 _tprintf(_T("%u\n"), value.numeric);
\r
981 if (! service->native) RegCloseKey(key);
\r
982 CloseServiceHandle(service->handle);
\r
986 /* Build the value. */
\r
987 if (mode == MODE_RESETTING) {
\r
988 /* Unset the parameter. */
\r
991 else if (remainder == argc) {
\r
995 /* Set the parameter. */
\r
997 size_t delimiterlen = (setting->additional & ADDITIONAL_CRLF) ? 2 : 1;
\r
998 for (i = remainder; i < argc; i++) len += _tcslen(argv[i]) + delimiterlen;
\r
1001 value.string = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, len * sizeof(TCHAR));
\r
1002 if (! value.string) {
\r
1003 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("value"), _T("edit_service()"));
\r
1004 CloseServiceHandle(service->handle);
\r
1009 for (i = remainder; i < argc; i++) {
\r
1010 size_t len = _tcslen(argv[i]);
\r
1011 memmove(value.string + s, argv[i], len * sizeof(TCHAR));
\r
1013 if (i < argc - 1) {
\r
1014 if (setting->additional & ADDITIONAL_CRLF) {
\r
1015 value.string[s++] = _T('\r');
\r
1016 value.string[s++] = _T('\n');
\r
1018 else value.string[s++] = _T(' ');
\r
1021 value.string[s] = _T('\0');
\r
1024 if (! service->native) {
\r
1025 key = open_registry(service->name, KEY_WRITE);
\r
1027 if (value.string) HeapFree(GetProcessHeap(), 0, value.string);
\r
1032 if (setting->native) ret = set_setting(service->name, service->handle, setting, &value, additional);
\r
1033 else ret = set_setting(service->name, key, setting, &value, additional);
\r
1034 if (value.string) HeapFree(GetProcessHeap(), 0, value.string);
\r
1036 if (! service->native) RegCloseKey(key);
\r
1037 CloseServiceHandle(service->handle);
\r
1041 if (! service->native) RegCloseKey(key);
\r
1042 CloseServiceHandle(service->handle);
\r
1047 /* About to remove the service */
\r
1048 int pre_remove_service(int argc, TCHAR **argv) {
\r
1049 nssm_service_t *service = alloc_nssm_service();
\r
1050 set_nssm_service_defaults(service);
\r
1051 if (argc) _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]);
\r
1053 /* Show dialogue box if we didn't pass service name and "confirm" */
\r
1054 if (argc < 2) return nssm_gui(IDD_REMOVE, service);
\r
1055 if (str_equiv(argv[1], _T("confirm"))) {
\r
1056 int ret = remove_service(service);
\r
1057 cleanup_nssm_service(service);
\r
1060 print_message(stderr, NSSM_MESSAGE_PRE_REMOVE_SERVICE);
\r
1064 /* Install the service */
\r
1065 int install_service(nssm_service_t *service) {
\r
1066 if (! service) return 1;
\r
1068 /* Open service manager */
\r
1069 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE);
\r
1071 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
1072 cleanup_nssm_service(service);
\r
1076 /* Get path of this program */
\r
1077 GetModuleFileName(0, service->image, _countof(service->image));
\r
1079 /* Create the service - settings will be changed in edit_service() */
\r
1080 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
1081 if (! service->handle) {
\r
1082 print_message(stderr, NSSM_MESSAGE_CREATESERVICE_FAILED, error_string(GetLastError()));
\r
1083 CloseServiceHandle(services);
\r
1087 if (edit_service(service, false)) {
\r
1088 DeleteService(service->handle);
\r
1089 CloseServiceHandle(services);
\r
1093 print_message(stdout, NSSM_MESSAGE_SERVICE_INSTALLED, service->name);
\r
1096 CloseServiceHandle(services);
\r
1101 /* Edit the service. */
\r
1102 int edit_service(nssm_service_t *service, bool editing) {
\r
1103 if (! service) return 1;
\r
1106 The only two valid flags for service type are SERVICE_WIN32_OWN_PROCESS
\r
1107 and SERVICE_INTERACTIVE_PROCESS.
\r
1109 service->type &= SERVICE_INTERACTIVE_PROCESS;
\r
1110 service->type |= SERVICE_WIN32_OWN_PROCESS;
\r
1112 /* Startup type. */
\r
1113 unsigned long startup;
\r
1114 switch (service->startup) {
\r
1115 case NSSM_STARTUP_MANUAL: startup = SERVICE_DEMAND_START; break;
\r
1116 case NSSM_STARTUP_DISABLED: startup = SERVICE_DISABLED; break;
\r
1117 default: startup = SERVICE_AUTO_START;
\r
1120 /* Display name. */
\r
1121 if (! service->displayname[0]) _sntprintf_s(service->displayname, _countof(service->displayname), _TRUNCATE, _T("%s"), service->name);
\r
1124 Username must be NULL if we aren't changing or an account name.
\r
1125 We must explicitly use LOCALSYSTEM to change it when we are editing.
\r
1126 Password must be NULL if we aren't changing, a password or "".
\r
1127 Empty passwords are valid but we won't allow them in the GUI.
\r
1129 TCHAR *username = 0;
\r
1131 TCHAR *password = 0;
\r
1132 if (service->usernamelen) {
\r
1133 username = service->username;
\r
1134 if (canonicalise_username(username, &canon)) return 5;
\r
1135 if (service->passwordlen) password = service->password;
\r
1137 else if (editing) username = canon = NSSM_LOCALSYSTEM_ACCOUNT;
\r
1139 if (well_known_username(canon)) password = _T("");
\r
1141 if (grant_logon_as_service(canon)) {
\r
1142 if (canon != username) HeapFree(GetProcessHeap(), 0, canon);
\r
1143 print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username);
\r
1148 TCHAR *dependencies = _T("");
\r
1149 if (service->dependencieslen) dependencies = 0; /* Change later. */
\r
1151 if (! ChangeServiceConfig(service->handle, service->type, startup, SERVICE_NO_CHANGE, 0, 0, 0, dependencies, canon, password, service->displayname)) {
\r
1152 if (canon != username) HeapFree(GetProcessHeap(), 0, canon);
\r
1153 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
1156 if (canon != username) HeapFree(GetProcessHeap(), 0, canon);
\r
1158 if (service->dependencieslen) {
\r
1159 if (set_service_dependencies(service->name, service->handle, service->dependencies)) return 5;
\r
1162 if (service->description[0] || editing) {
\r
1163 set_service_description(service->name, service->handle, service->description);
\r
1166 SERVICE_DELAYED_AUTO_START_INFO delayed;
\r
1167 ZeroMemory(&delayed, sizeof(delayed));
\r
1168 if (service->startup == NSSM_STARTUP_DELAYED) delayed.fDelayedAutostart = 1;
\r
1169 else delayed.fDelayedAutostart = 0;
\r
1170 /* Delayed startup isn't supported until Vista. */
\r
1171 if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, &delayed)) {
\r
1172 unsigned long error = GetLastError();
\r
1173 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
\r
1174 if (error != ERROR_INVALID_LEVEL) {
\r
1175 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_DELAYED_AUTO_START_INFO_FAILED, service->name, error_string(error), 0);
\r
1179 /* Don't mess with parameters which aren't ours. */
\r
1180 if (! service->native) {
\r
1181 /* Now we need to put the parameters into the registry */
\r
1182 if (create_parameters(service, editing)) {
\r
1183 print_message(stderr, NSSM_MESSAGE_CREATE_PARAMETERS_FAILED);
\r
1187 set_service_recovery(service);
\r
1193 /* Control a service. */
\r
1194 int control_service(unsigned long control, int argc, TCHAR **argv) {
\r
1195 if (argc < 1) return usage(1);
\r
1196 TCHAR *service_name = argv[0];
\r
1197 TCHAR canonical_name[SERVICE_NAME_LENGTH];
\r
1199 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
\r
1201 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
1205 unsigned long access = SERVICE_QUERY_STATUS;
\r
1206 switch (control) {
\r
1207 case NSSM_SERVICE_CONTROL_START:
\r
1208 access |= SERVICE_START;
\r
1211 case SERVICE_CONTROL_CONTINUE:
\r
1212 case SERVICE_CONTROL_PAUSE:
\r
1213 access |= SERVICE_PAUSE_CONTINUE;
\r
1216 case SERVICE_CONTROL_STOP:
\r
1217 access |= SERVICE_STOP;
\r
1220 case NSSM_SERVICE_CONTROL_ROTATE:
\r
1221 access |= SERVICE_USER_DEFINED_CONTROL;
\r
1225 SC_HANDLE service_handle = open_service(services, service_name, access, canonical_name, _countof(canonical_name));
\r
1226 if (! service_handle) {
\r
1227 CloseServiceHandle(services);
\r
1232 unsigned long error;
\r
1233 SERVICE_STATUS service_status;
\r
1234 if (control == NSSM_SERVICE_CONTROL_START) {
\r
1235 unsigned long initial_status = SERVICE_STOPPED;
\r
1236 ret = StartService(service_handle, (unsigned long) argc, (const TCHAR **) argv);
\r
1237 error = GetLastError();
\r
1238 CloseServiceHandle(services);
\r
1240 if (error == ERROR_IO_PENDING) {
\r
1242 Older versions of Windows return immediately with ERROR_IO_PENDING
\r
1243 indicate that the operation is still in progress. Newer versions
\r
1244 will return it if there really is a delay.
\r
1247 error = ERROR_SUCCESS;
\r
1251 int response = await_service_control_response(control, service_handle, &service_status, initial_status);
\r
1252 CloseServiceHandle(service_handle);
\r
1255 print_message(stderr, NSSM_MESSAGE_BAD_CONTROL_RESPONSE, canonical_name, service_status_text(service_status.dwCurrentState), service_control_text(control));
\r
1258 else _tprintf(_T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1262 CloseServiceHandle(service_handle);
\r
1263 _ftprintf(stderr, _T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1267 else if (control == SERVICE_CONTROL_INTERROGATE) {
\r
1269 We could actually send an INTERROGATE control but that won't return
\r
1270 any information if the service is stopped and we don't care about
\r
1271 the extra details it might give us in any case. So we'll fake it.
\r
1273 ret = QueryServiceStatus(service_handle, &service_status);
\r
1274 error = GetLastError();
\r
1277 _tprintf(_T("%s\n"), service_status_text(service_status.dwCurrentState));
\r
1281 _ftprintf(stderr, _T("%s: %s\n"), canonical_name, error_string(error));
\r
1286 ret = ControlService(service_handle, control, &service_status);
\r
1287 unsigned long initial_status = service_status.dwCurrentState;
\r
1288 error = GetLastError();
\r
1289 CloseServiceHandle(services);
\r
1291 if (error == ERROR_IO_PENDING) {
\r
1293 error = ERROR_SUCCESS;
\r
1297 int response = await_service_control_response(control, service_handle, &service_status, initial_status);
\r
1298 CloseServiceHandle(service_handle);
\r
1301 print_message(stderr, NSSM_MESSAGE_BAD_CONTROL_RESPONSE, canonical_name, service_status_text(service_status.dwCurrentState), service_control_text(control));
\r
1304 else _tprintf(_T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1308 CloseServiceHandle(service_handle);
\r
1309 _ftprintf(stderr, _T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1310 if (error == ERROR_SERVICE_NOT_ACTIVE) {
\r
1311 if (control == SERVICE_CONTROL_SHUTDOWN || control == SERVICE_CONTROL_STOP) return 0;
\r
1318 /* Remove the service */
\r
1319 int remove_service(nssm_service_t *service) {
\r
1320 if (! service) return 1;
\r
1322 /* Open service manager */
\r
1323 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
\r
1325 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
1329 /* Try to open the service */
\r
1330 service->handle = open_service(services, service->name, DELETE, service->name, _countof(service->name));
\r
1331 if (! service->handle) {
\r
1332 CloseServiceHandle(services);
\r
1336 /* Get the canonical service name. We open it case insensitively. */
\r
1337 unsigned long bufsize = _countof(service->displayname);
\r
1338 GetServiceDisplayName(services, service->name, service->displayname, &bufsize);
\r
1339 bufsize = _countof(service->name);
\r
1340 GetServiceKeyName(services, service->displayname, service->name, &bufsize);
\r
1342 /* Try to delete the service */
\r
1343 if (! DeleteService(service->handle)) {
\r
1344 print_message(stderr, NSSM_MESSAGE_DELETESERVICE_FAILED);
\r
1345 CloseServiceHandle(services);
\r
1350 CloseServiceHandle(services);
\r
1352 print_message(stdout, NSSM_MESSAGE_SERVICE_REMOVED, service->name);
\r
1356 /* Service initialisation */
\r
1357 void WINAPI service_main(unsigned long argc, TCHAR **argv) {
\r
1358 nssm_service_t *service = alloc_nssm_service();
\r
1359 if (! service) return;
\r
1361 if (_sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]) < 0) {
\r
1362 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("service->name"), _T("service_main()"), 0);
\r
1366 /* We can use a condition variable in a critical section on Vista or later. */
\r
1367 if (imports.SleepConditionVariableCS && imports.WakeConditionVariable) use_critical_section = true;
\r
1368 else use_critical_section = false;
\r
1370 /* Initialise status */
\r
1371 ZeroMemory(&service->status, sizeof(service->status));
\r
1372 service->status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;
\r
1373 service->status.dwControlsAccepted = 0;
\r
1374 service->status.dwWin32ExitCode = NO_ERROR;
\r
1375 service->status.dwServiceSpecificExitCode = 0;
\r
1376 service->status.dwCheckPoint = 0;
\r
1377 service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;
\r
1379 /* Signal we AREN'T running the server */
\r
1380 service->process_handle = 0;
\r
1383 /* Register control handler */
\r
1384 service->status_handle = RegisterServiceCtrlHandlerEx(NSSM, service_control_handler, (void *) service);
\r
1385 if (! service->status_handle) {
\r
1386 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_REGISTERSERVICECTRLHANDER_FAILED, error_string(GetLastError()), 0);
\r
1390 log_service_control(service->name, 0, true);
\r
1392 service->status.dwCurrentState = SERVICE_START_PENDING;
\r
1393 service->status.dwWaitHint = service->throttle_delay + NSSM_WAITHINT_MARGIN;
\r
1394 SetServiceStatus(service->status_handle, &service->status);
\r
1397 /* Try to create the exit action parameters; we don't care if it fails */
\r
1398 create_exit_action(service->name, exit_action_strings[0], false);
\r
1400 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT);
\r
1402 service->handle = open_service(services, service->name, SERVICE_CHANGE_CONFIG, 0, 0);
\r
1403 set_service_recovery(service);
\r
1405 CloseServiceHandle(services);
\r
1409 /* Used for signalling a resume if the service pauses when throttled. */
\r
1410 if (use_critical_section) {
\r
1411 InitializeCriticalSection(&service->throttle_section);
\r
1412 service->throttle_section_initialised = true;
\r
1415 service->throttle_timer = CreateWaitableTimer(0, 1, 0);
\r
1416 if (! service->throttle_timer) {
\r
1417 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_CREATEWAITABLETIMER_FAILED, service->name, error_string(GetLastError()), 0);
\r
1421 /* Remember our initial environment. */
\r
1422 service->initial_env = GetEnvironmentStrings();
\r
1424 monitor_service(service);
\r
1427 /* Make sure service recovery actions are taken where necessary */
\r
1428 void set_service_recovery(nssm_service_t *service) {
\r
1429 SERVICE_FAILURE_ACTIONS_FLAG flag;
\r
1430 ZeroMemory(&flag, sizeof(flag));
\r
1431 flag.fFailureActionsOnNonCrashFailures = true;
\r
1433 /* This functionality was added in Vista so the call may fail */
\r
1434 if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, &flag)) {
\r
1435 unsigned long error = GetLastError();
\r
1436 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
\r
1437 if (error != ERROR_INVALID_LEVEL) {
\r
1438 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_FAILURE_ACTIONS_FAILED, service->name, error_string(error), 0);
\r
1443 int monitor_service(nssm_service_t *service) {
\r
1444 /* Set service status to started */
\r
1445 int ret = start_service(service);
\r
1448 _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%d"), ret);
\r
1449 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_START_SERVICE_FAILED, service->exe, service->name, ret, 0);
\r
1452 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_STARTED_SERVICE, service->exe, service->flags, service->name, service->dir, 0);
\r
1454 /* Monitor service */
\r
1455 if (! RegisterWaitForSingleObject(&service->wait_handle, service->process_handle, end_service, (void *) service, INFINITE, WT_EXECUTEONLYONCE | WT_EXECUTELONGFUNCTION)) {
\r
1456 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_REGISTERWAITFORSINGLEOBJECT_FAILED, service->name, service->exe, error_string(GetLastError()), 0);
\r
1462 TCHAR *service_control_text(unsigned long control) {
\r
1463 switch (control) {
\r
1464 /* HACK: there is no SERVICE_CONTROL_START constant */
\r
1465 case NSSM_SERVICE_CONTROL_START: return _T("START");
\r
1466 case SERVICE_CONTROL_STOP: return _T("STOP");
\r
1467 case SERVICE_CONTROL_SHUTDOWN: return _T("SHUTDOWN");
\r
1468 case SERVICE_CONTROL_PAUSE: return _T("PAUSE");
\r
1469 case SERVICE_CONTROL_CONTINUE: return _T("CONTINUE");
\r
1470 case SERVICE_CONTROL_INTERROGATE: return _T("INTERROGATE");
\r
1471 case NSSM_SERVICE_CONTROL_ROTATE: return _T("ROTATE");
\r
1472 case SERVICE_CONTROL_POWEREVENT: return _T("POWEREVENT");
\r
1473 default: return 0;
\r
1477 TCHAR *service_status_text(unsigned long status) {
\r
1479 case SERVICE_STOPPED: return _T("SERVICE_STOPPED");
\r
1480 case SERVICE_START_PENDING: return _T("SERVICE_START_PENDING");
\r
1481 case SERVICE_STOP_PENDING: return _T("SERVICE_STOP_PENDING");
\r
1482 case SERVICE_RUNNING: return _T("SERVICE_RUNNING");
\r
1483 case SERVICE_CONTINUE_PENDING: return _T("SERVICE_CONTINUE_PENDING");
\r
1484 case SERVICE_PAUSE_PENDING: return _T("SERVICE_PAUSE_PENDING");
\r
1485 case SERVICE_PAUSED: return _T("SERVICE_PAUSED");
\r
1486 default: return 0;
\r
1490 void log_service_control(TCHAR *service_name, unsigned long control, bool handled) {
\r
1491 TCHAR *text = service_control_text(control);
\r
1492 unsigned long event;
\r
1495 /* "0x" + 8 x hex + NULL */
\r
1496 text = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, 11 * sizeof(TCHAR));
\r
1498 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);
\r
1501 if (_sntprintf_s(text, 11, _TRUNCATE, _T("0x%08x"), control) < 0) {
\r
1502 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);
\r
1503 HeapFree(GetProcessHeap(), 0, text);
\r
1507 event = NSSM_EVENT_SERVICE_CONTROL_UNKNOWN;
\r
1509 else if (handled) event = NSSM_EVENT_SERVICE_CONTROL_HANDLED;
\r
1510 else event = NSSM_EVENT_SERVICE_CONTROL_NOT_HANDLED;
\r
1512 log_event(EVENTLOG_INFORMATION_TYPE, event, service_name, text, 0);
\r
1514 if (event == NSSM_EVENT_SERVICE_CONTROL_UNKNOWN) {
\r
1515 HeapFree(GetProcessHeap(), 0, text);
\r
1519 /* Service control handler */
\r
1520 unsigned long WINAPI service_control_handler(unsigned long control, unsigned long event, void *data, void *context) {
\r
1521 nssm_service_t *service = (nssm_service_t *) context;
\r
1523 switch (control) {
\r
1524 case SERVICE_CONTROL_INTERROGATE:
\r
1525 /* We always keep the service status up-to-date so this is a no-op. */
\r
1528 case SERVICE_CONTROL_SHUTDOWN:
\r
1529 case SERVICE_CONTROL_STOP:
\r
1530 log_service_control(service->name, control, true);
\r
1532 We MUST acknowledge the stop request promptly but we're committed to
\r
1533 waiting for the application to exit. Spawn a new thread to wait
\r
1534 while we acknowledge the request.
\r
1536 if (! CreateThread(NULL, 0, shutdown_service, context, 0, NULL)) {
\r
1537 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETHREAD_FAILED, error_string(GetLastError()), 0);
\r
1540 We couldn't create a thread to tidy up so we'll have to force the tidyup
\r
1541 to complete in time in this thread.
\r
1543 service->kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;
\r
1544 service->kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;
\r
1545 service->kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;
\r
1547 stop_service(service, 0, true, true);
\r
1551 case SERVICE_CONTROL_CONTINUE:
\r
1552 log_service_control(service->name, control, true);
\r
1553 service->throttle = 0;
\r
1554 if (use_critical_section) imports.WakeConditionVariable(&service->throttle_condition);
\r
1556 if (! service->throttle_timer) return ERROR_CALL_NOT_IMPLEMENTED;
\r
1557 ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));
\r
1558 SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);
\r
1560 /* We can't continue if the application is running! */
\r
1561 if (! service->process_handle) service->status.dwCurrentState = SERVICE_CONTINUE_PENDING;
\r
1562 service->status.dwWaitHint = throttle_milliseconds(service->throttle) + NSSM_WAITHINT_MARGIN;
\r
1563 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESET_THROTTLE, service->name, 0);
\r
1564 SetServiceStatus(service->status_handle, &service->status);
\r
1567 case SERVICE_CONTROL_PAUSE:
\r
1569 We don't accept pause messages but it isn't possible to register
\r
1570 only for continue messages so we have to handle this case.
\r
1572 log_service_control(service->name, control, false);
\r
1573 return ERROR_CALL_NOT_IMPLEMENTED;
\r
1575 case NSSM_SERVICE_CONTROL_ROTATE:
\r
1576 log_service_control(service->name, control, true);
\r
1577 if (service->rotate_stdout_online == NSSM_ROTATE_ONLINE) service->rotate_stdout_online = NSSM_ROTATE_ONLINE_ASAP;
\r
1578 if (service->rotate_stderr_online == NSSM_ROTATE_ONLINE) service->rotate_stderr_online = NSSM_ROTATE_ONLINE_ASAP;
\r
1581 case SERVICE_CONTROL_POWEREVENT:
\r
1582 if (event != PBT_APMRESUMEAUTOMATIC) {
\r
1583 log_service_control(service->name, control, false);
\r
1586 log_service_control(service->name, control, true);
\r
1587 end_service((void *) service, false);
\r
1591 /* Unknown control */
\r
1592 log_service_control(service->name, control, false);
\r
1593 return ERROR_CALL_NOT_IMPLEMENTED;
\r
1596 /* Start the service */
\r
1597 int start_service(nssm_service_t *service) {
\r
1598 service->stopping = false;
\r
1599 service->allow_restart = true;
\r
1601 if (service->process_handle) return 0;
\r
1603 /* Allocate a STARTUPINFO structure for a new process */
\r
1605 ZeroMemory(&si, sizeof(si));
\r
1606 si.cb = sizeof(si);
\r
1608 /* Allocate a PROCESSINFO structure for the process */
\r
1609 PROCESS_INFORMATION pi;
\r
1610 ZeroMemory(&pi, sizeof(pi));
\r
1612 /* Get startup parameters */
\r
1613 int ret = get_parameters(service, &si);
\r
1615 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_PARAMETERS_FAILED, service->name, 0);
\r
1616 return stop_service(service, 2, true, true);
\r
1619 /* Launch executable with arguments */
\r
1620 TCHAR cmd[CMD_LENGTH];
\r
1621 if (_sntprintf_s(cmd, _countof(cmd), _TRUNCATE, _T("\"%s\" %s"), service->exe, service->flags) < 0) {
\r
1622 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("command line"), _T("start_service"), 0);
\r
1623 return stop_service(service, 2, true, true);
\r
1626 throttle_restart(service);
\r
1628 /* Set the environment. */
\r
1629 if (service->env) duplicate_environment(service->env);
\r
1630 if (service->env_extra) set_environment_block(service->env_extra);
\r
1632 /* Set up I/O redirection. */
\r
1633 if (get_output_handles(service, &si)) {
\r
1634 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_OUTPUT_HANDLES_FAILED, service->name, 0);
\r
1635 if (! service->no_console) FreeConsole();
\r
1636 close_output_handles(&si);
\r
1637 return stop_service(service, 4, true, true);
\r
1640 bool inherit_handles = false;
\r
1641 if (si.dwFlags & STARTF_USESTDHANDLES) inherit_handles = true;
\r
1642 unsigned long flags = service->priority & priority_mask();
\r
1643 if (service->affinity) flags |= CREATE_SUSPENDED;
\r
1644 if (! CreateProcess(0, cmd, 0, 0, inherit_handles, flags, 0, service->dir, &si, &pi)) {
\r
1645 unsigned long exitcode = 3;
\r
1646 unsigned long error = GetLastError();
\r
1647 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service->name, service->exe, error_string(error), 0);
\r
1648 close_output_handles(&si);
\r
1649 duplicate_environment_strings(service->initial_env);
\r
1650 return stop_service(service, exitcode, true, true);
\r
1652 service->process_handle = pi.hProcess;
\r
1653 service->pid = pi.dwProcessId;
\r
1655 if (get_process_creation_time(service->process_handle, &service->creation_time)) ZeroMemory(&service->creation_time, sizeof(service->creation_time));
\r
1657 close_output_handles(&si);
\r
1659 if (! service->no_console) FreeConsole();
\r
1661 /* Restore our environment. */
\r
1662 duplicate_environment_strings(service->initial_env);
\r
1664 if (service->affinity) {
\r
1666 We are explicitly storing service->affinity as a 64-bit unsigned integer
\r
1667 so that we can parse it regardless of whether we're running in 32-bit
\r
1668 or 64-bit mode. The arguments to SetProcessAffinityMask(), however, are
\r
1669 defined as type DWORD_PTR and hence limited to 32 bits on a 32-bit system
\r
1670 (or when running the 32-bit NSSM).
\r
1672 The result is a lot of seemingly-unnecessary casting throughout the code
\r
1673 and potentially confusion when we actually try to start the service.
\r
1674 Having said that, however, it's unlikely that we're actually going to
\r
1675 run in 32-bit mode on a system which has more than 32 CPUs so the
\r
1676 likelihood of seeing a confusing situation is somewhat diminished.
\r
1678 DWORD_PTR affinity, system_affinity;
\r
1680 if (GetProcessAffinityMask(service->process_handle, &affinity, &system_affinity)) affinity = service->affinity & system_affinity;
\r
1682 affinity = (DWORD_PTR) service->affinity;
\r
1683 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GETPROCESSAFFINITYMASK_FAILED, service->name, error_string(GetLastError()), 0);
\r
1686 if (! SetProcessAffinityMask(service->process_handle, affinity)) {
\r
1687 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_SETPROCESSAFFINITYMASK_FAILED, service->name, error_string(GetLastError()), 0);
\r
1690 ResumeThread(pi.hThread);
\r
1694 Wait for a clean startup before changing the service status to RUNNING
\r
1695 but be mindful of the fact that we are blocking the service control manager
\r
1696 so abandon the wait before too much time has elapsed.
\r
1698 service->status.dwCurrentState = SERVICE_START_PENDING;
\r
1699 service->status.dwControlsAccepted = SERVICE_ACCEPT_POWEREVENT | SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP;
\r
1700 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
1702 /* Signal successful start */
\r
1703 service->status.dwCurrentState = SERVICE_RUNNING;
\r
1704 service->status.dwControlsAccepted &= ~SERVICE_ACCEPT_PAUSE_CONTINUE;
\r
1705 SetServiceStatus(service->status_handle, &service->status);
\r
1707 /* Continue waiting for a clean startup. */
\r
1708 if (deadline == WAIT_TIMEOUT) {
\r
1709 if (service->throttle_delay > delay) {
\r
1710 if (WaitForSingleObject(service->process_handle, service->throttle_delay - delay) == WAIT_TIMEOUT) service->throttle = 0;
\r
1712 else service->throttle = 0;
\r
1715 /* Ensure the restart delay is always applied. */
\r
1716 if (service->restart_delay && ! service->throttle) service->throttle++;
\r
1721 /* Stop the service */
\r
1722 int stop_service(nssm_service_t *service, unsigned long exitcode, bool graceful, bool default_action) {
\r
1723 service->allow_restart = false;
\r
1724 if (service->wait_handle) {
\r
1725 UnregisterWait(service->wait_handle);
\r
1726 service->wait_handle = 0;
\r
1729 service->rotate_stdout_online = service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;
\r
1731 if (default_action && ! exitcode && ! graceful) {
\r
1732 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
1736 /* Signal we are stopping */
\r
1738 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
1739 service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;
\r
1741 service->status.dwControlsAccepted = 0;
\r
1742 SetServiceStatus(service->status_handle, &service->status);
\r
1744 /* Nothing to do if service isn't running */
\r
1745 if (service->pid) {
\r
1746 /* Shut down service */
\r
1747 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_TERMINATEPROCESS, service->name, service->exe, 0);
\r
1749 service_kill_t(service, &k);
\r
1753 else log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_PROCESS_ALREADY_STOPPED, service->name, service->exe, 0);
\r
1755 end_service((void *) service, true);
\r
1757 /* Signal we stopped */
\r
1759 service->status.dwCurrentState = SERVICE_STOPPED;
\r
1761 service->status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
\r
1762 service->status.dwServiceSpecificExitCode = exitcode;
\r
1765 service->status.dwWin32ExitCode = NO_ERROR;
\r
1766 service->status.dwServiceSpecificExitCode = 0;
\r
1768 SetServiceStatus(service->status_handle, &service->status);
\r
1774 /* Callback function triggered when the server exits */
\r
1775 void CALLBACK end_service(void *arg, unsigned char why) {
\r
1776 nssm_service_t *service = (nssm_service_t *) arg;
\r
1778 if (service->stopping) return;
\r
1780 service->stopping = true;
\r
1782 service->rotate_stdout_online = service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;
\r
1784 /* Use now as a dummy exit time. */
\r
1785 GetSystemTimeAsFileTime(&service->exit_time);
\r
1787 /* Check exit code */
\r
1788 unsigned long exitcode = 0;
\r
1790 if (service->process_handle) {
\r
1791 GetExitCodeProcess(service->process_handle, &exitcode);
\r
1792 /* Check real exit time. */
\r
1793 if (exitcode != STILL_ACTIVE) get_process_exit_time(service->process_handle, &service->exit_time);
\r
1794 CloseHandle(service->process_handle);
\r
1797 service->process_handle = 0;
\r
1800 Log that the service ended BEFORE logging about killing the process
\r
1801 tree. See below for the possible values of the why argument.
\r
1804 _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), exitcode);
\r
1805 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ENDED_SERVICE, service->exe, service->name, code, 0);
\r
1809 if (exitcode == STILL_ACTIVE) exitcode = 0;
\r
1810 if (service->pid && service->kill_process_tree) {
\r
1812 service_kill_t(service, &k);
\r
1813 kill_process_tree(&k, service->pid);
\r
1818 The why argument is true if our wait timed out or false otherwise.
\r
1819 Our wait is infinite so why will never be true when called by the system.
\r
1820 If it is indeed true, assume we were called from stop_service() because
\r
1821 this is a controlled shutdown, and don't take any restart action.
\r
1824 if (! service->allow_restart) return;
\r
1826 /* What action should we take? */
\r
1827 int action = NSSM_EXIT_RESTART;
\r
1828 TCHAR action_string[ACTION_LEN];
\r
1829 bool default_action;
\r
1830 if (! get_exit_action(service->name, &exitcode, action_string, &default_action)) {
\r
1831 for (int i = 0; exit_action_strings[i]; i++) {
\r
1832 if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
1840 /* Try to restart the service or return failure code to service manager */
\r
1841 case NSSM_EXIT_RESTART:
\r
1842 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_RESTART, service->name, code, exit_action_strings[action], service->exe, 0);
\r
1843 while (monitor_service(service)) {
\r
1844 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_RESTART_SERVICE_FAILED, service->exe, service->name, 0);
\r
1849 /* Do nothing, just like srvany would */
\r
1850 case NSSM_EXIT_IGNORE:
\r
1851 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_IGNORE, service->name, code, exit_action_strings[action], service->exe, 0);
\r
1855 /* Tell the service manager we are finished */
\r
1856 case NSSM_EXIT_REALLY:
\r
1857 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_REALLY, service->name, code, exit_action_strings[action], 0);
\r
1858 stop_service(service, exitcode, true, default_action);
\r
1861 /* Fake a crash so pre-Vista service managers will run recovery actions. */
\r
1862 case NSSM_EXIT_UNCLEAN:
\r
1863 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_UNCLEAN, service->name, code, exit_action_strings[action], 0);
\r
1864 stop_service(service, exitcode, false, default_action);
\r
1871 void throttle_restart(nssm_service_t *service) {
\r
1872 /* This can't be a restart if the service is already running. */
\r
1873 if (! service->throttle++) return;
\r
1876 unsigned long throttle_ms = throttle_milliseconds(service->throttle);
\r
1877 TCHAR threshold[8], milliseconds[8];
\r
1879 if (service->restart_delay > throttle_ms) ms = service->restart_delay;
\r
1880 else ms = throttle_ms;
\r
1882 if (service->throttle > 7) service->throttle = 8;
\r
1884 _sntprintf_s(milliseconds, _countof(milliseconds), _TRUNCATE, _T("%lu"), ms);
\r
1886 if (service->throttle == 1 && service->restart_delay > throttle_ms) log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESTART_DELAY, service->name, milliseconds, 0);
\r
1888 _sntprintf_s(threshold, _countof(threshold), _TRUNCATE, _T("%lu"), service->throttle_delay);
\r
1889 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_THROTTLED, service->name, threshold, milliseconds, 0);
\r
1892 if (use_critical_section) EnterCriticalSection(&service->throttle_section);
\r
1893 else if (service->throttle_timer) {
\r
1894 ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));
\r
1895 service->throttle_duetime.QuadPart = 0 - (ms * 10000LL);
\r
1896 SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);
\r
1899 service->status.dwCurrentState = SERVICE_PAUSED;
\r
1900 service->status.dwControlsAccepted |= SERVICE_ACCEPT_PAUSE_CONTINUE;
\r
1901 SetServiceStatus(service->status_handle, &service->status);
\r
1903 if (use_critical_section) {
\r
1904 imports.SleepConditionVariableCS(&service->throttle_condition, &service->throttle_section, ms);
\r
1905 LeaveCriticalSection(&service->throttle_section);
\r
1908 if (service->throttle_timer) WaitForSingleObject(service->throttle_timer, INFINITE);
\r
1914 When responding to a stop (or any other) request we need to set dwWaitHint to
\r
1915 the number of milliseconds we expect the operation to take, and optionally
\r
1916 increase dwCheckPoint. If dwWaitHint milliseconds elapses without the
\r
1917 operation completing or dwCheckPoint increasing, the system will consider the
\r
1918 service to be hung.
\r
1920 However the system will consider the service to be hung after 30000
\r
1921 milliseconds regardless of the value of dwWaitHint if dwCheckPoint has not
\r
1922 changed. Therefore if we want to wait longer than that we must periodically
\r
1923 increase dwCheckPoint.
\r
1925 Furthermore, it will consider the service to be hung after 60000 milliseconds
\r
1926 regardless of the value of dwCheckPoint unless dwWaitHint is increased every
\r
1927 time dwCheckPoint is also increased.
\r
1929 Our strategy then is to retrieve the initial dwWaitHint and wait for
\r
1930 NSSM_SERVICE_STATUS_DEADLINE milliseconds. If the process is still running
\r
1931 and we haven't finished waiting we increment dwCheckPoint and add whichever is
\r
1932 smaller of NSSM_SERVICE_STATUS_DEADLINE or the remaining timeout to
\r
1935 Only doing both these things will prevent the system from killing the service.
\r
1937 If the status_handle and service_status arguments are omitted, this function
\r
1938 will not try to update the service manager but it will still log to the
\r
1939 event log that it is waiting for a handle.
\r
1941 Returns: 1 if the wait timed out.
\r
1942 0 if the wait completed.
\r
1945 int await_single_handle(SERVICE_STATUS_HANDLE status_handle, SERVICE_STATUS *status, HANDLE handle, TCHAR *name, TCHAR *function_name, unsigned long timeout) {
\r
1946 unsigned long interval;
\r
1947 unsigned long ret;
\r
1948 unsigned long waited;
\r
1949 TCHAR interval_milliseconds[16];
\r
1950 TCHAR timeout_milliseconds[16];
\r
1951 TCHAR waited_milliseconds[16];
\r
1952 TCHAR *function = function_name;
\r
1954 /* Add brackets to function name. */
\r
1955 size_t funclen = _tcslen(function_name) + 3;
\r
1956 TCHAR *func = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, funclen * sizeof(TCHAR));
\r
1958 if (_sntprintf_s(func, funclen, _TRUNCATE, _T("%s()"), function_name) > -1) function = func;
\r
1961 _sntprintf_s(timeout_milliseconds, _countof(timeout_milliseconds), _TRUNCATE, _T("%lu"), timeout);
\r
1964 while (waited < timeout) {
\r
1965 interval = timeout - waited;
\r
1966 if (interval > NSSM_SERVICE_STATUS_DEADLINE) interval = NSSM_SERVICE_STATUS_DEADLINE;
\r
1969 status->dwWaitHint += interval;
\r
1970 status->dwCheckPoint++;
\r
1971 SetServiceStatus(status_handle, status);
\r
1975 _sntprintf_s(waited_milliseconds, _countof(waited_milliseconds), _TRUNCATE, _T("%lu"), waited);
\r
1976 _sntprintf_s(interval_milliseconds, _countof(interval_milliseconds), _TRUNCATE, _T("%lu"), interval);
\r
1977 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_AWAITING_SINGLE_HANDLE, function, name, waited_milliseconds, interval_milliseconds, timeout_milliseconds, 0);
\r
1980 switch (WaitForSingleObject(handle, interval)) {
\r
1981 case WAIT_OBJECT_0:
\r
1985 case WAIT_TIMEOUT:
\r
1994 waited += interval;
\r
1998 if (func) HeapFree(GetProcessHeap(), 0, func);
\r