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
276 void set_service_environment(nssm_service_t *service) {
\r
277 if (! service) return;
\r
278 if (service->env) duplicate_environment(service->env);
\r
279 if (service->env_extra) set_environment_block(service->env_extra);
\r
282 void unset_service_environment(nssm_service_t *service) {
\r
283 if (! service) return;
\r
284 duplicate_environment_strings(service->initial_env);
\r
288 Wrapper to be called in a new thread so that we can acknowledge a STOP
\r
289 control immediately.
\r
291 static unsigned long WINAPI shutdown_service(void *arg) {
\r
292 return stop_service((nssm_service_t *) arg, 0, true, true);
\r
296 Wrapper to be called in a new thread so that we can acknowledge start
\r
299 static unsigned long WINAPI launch_service(void *arg) {
\r
300 return monitor_service((nssm_service_t *) arg);
\r
303 /* Connect to the service manager */
\r
304 SC_HANDLE open_service_manager(unsigned long access) {
\r
305 SC_HANDLE ret = OpenSCManager(0, SERVICES_ACTIVE_DATABASE, access);
\r
307 if (is_admin) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENSCMANAGER_FAILED, 0);
\r
314 /* Open a service by name or display name. */
\r
315 SC_HANDLE open_service(SC_HANDLE services, TCHAR *service_name, unsigned long access, TCHAR *canonical_name, unsigned long canonical_namelen) {
\r
316 SC_HANDLE service_handle = OpenService(services, service_name, access);
\r
317 if (service_handle) {
\r
318 if (canonical_name && canonical_name != service_name) {
\r
319 TCHAR displayname[SERVICE_NAME_LENGTH];
\r
320 unsigned long displayname_len = (unsigned long) _countof(displayname);
\r
321 GetServiceDisplayName(services, service_name, displayname, &displayname_len);
\r
322 unsigned long keyname_len = canonical_namelen;
\r
323 GetServiceKeyName(services, displayname, canonical_name, &keyname_len);
\r
325 return service_handle;
\r
328 unsigned long error = GetLastError();
\r
329 if (error != ERROR_SERVICE_DOES_NOT_EXIST) {
\r
330 print_message(stderr, NSSM_MESSAGE_OPENSERVICE_FAILED, error_string(GetLastError()));
\r
334 /* We can't look for a display name because there's no buffer to store it. */
\r
335 if (! canonical_name) {
\r
336 print_message(stderr, NSSM_MESSAGE_OPENSERVICE_FAILED, error_string(GetLastError()));
\r
340 unsigned long bufsize, required, count, i;
\r
341 unsigned long resume = 0;
\r
342 EnumServicesStatus(services, SERVICE_DRIVER | SERVICE_FILE_SYSTEM_DRIVER | SERVICE_KERNEL_DRIVER | SERVICE_WIN32, SERVICE_STATE_ALL, 0, 0, &required, &count, &resume);
\r
343 error = GetLastError();
\r
344 if (error != ERROR_MORE_DATA) {
\r
345 print_message(stderr, NSSM_MESSAGE_ENUMSERVICESSTATUS_FAILED, error_string(GetLastError()));
\r
349 ENUM_SERVICE_STATUS *status = (ENUM_SERVICE_STATUS *) HeapAlloc(GetProcessHeap(), 0, required);
\r
351 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("ENUM_SERVICE_STATUS"), _T("open_service()"));
\r
355 bufsize = required;
\r
358 EnumServicesStatus() returns:
\r
359 1 when it retrieved data and there's no more data to come.
\r
360 0 and sets last error to ERROR_MORE_DATA when it retrieved data and
\r
361 there's more data to come.
\r
362 0 and sets last error to something else on error.
\r
364 int ret = EnumServicesStatus(services, SERVICE_DRIVER | SERVICE_FILE_SYSTEM_DRIVER | SERVICE_KERNEL_DRIVER | SERVICE_WIN32, SERVICE_STATE_ALL, status, bufsize, &required, &count, &resume);
\r
366 error = GetLastError();
\r
367 if (error != ERROR_MORE_DATA) {
\r
368 HeapFree(GetProcessHeap(), 0, status);
\r
369 print_message(stderr, NSSM_MESSAGE_ENUMSERVICESSTATUS_FAILED, error_string(GetLastError()));
\r
374 for (i = 0; i < count; i++) {
\r
375 if (str_equiv(status[i].lpDisplayName, service_name)) {
\r
376 if (_sntprintf_s(canonical_name, canonical_namelen, _TRUNCATE, _T("%s"), status[i].lpServiceName) < 0) {
\r
377 HeapFree(GetProcessHeap(), 0, status);
\r
378 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("canonical_name"), _T("open_service()"));
\r
382 HeapFree(GetProcessHeap(), 0, status);
\r
383 return open_service(services, canonical_name, access, 0, 0);
\r
390 /* Recurse so we can get an error message. */
\r
391 return open_service(services, service_name, access, 0, 0);
\r
394 QUERY_SERVICE_CONFIG *query_service_config(const TCHAR *service_name, SC_HANDLE service_handle) {
\r
395 QUERY_SERVICE_CONFIG *qsc;
\r
396 unsigned long bufsize;
\r
397 unsigned long error;
\r
399 QueryServiceConfig(service_handle, 0, 0, &bufsize);
\r
400 error = GetLastError();
\r
401 if (error == ERROR_INSUFFICIENT_BUFFER) {
\r
402 qsc = (QUERY_SERVICE_CONFIG *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, bufsize);
\r
404 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("QUERY_SERVICE_CONFIG"), _T("query_service_config()"), 0);
\r
409 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG_FAILED, service_name, error_string(error), 0);
\r
413 if (! QueryServiceConfig(service_handle, qsc, bufsize, &bufsize)) {
\r
414 HeapFree(GetProcessHeap(), 0, qsc);
\r
415 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG_FAILED, service_name, error_string(GetLastError()), 0);
\r
422 int set_service_dependencies(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR *buffer) {
\r
423 TCHAR *dependencies = _T("");
\r
424 unsigned long num_dependencies = 0;
\r
426 if (buffer && buffer[0]) {
\r
427 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
\r
429 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
434 Count the dependencies then allocate a buffer big enough for their
\r
435 canonical names, ie n * SERVICE_NAME_LENGTH.
\r
439 for (s = buffer; *s; s++) {
\r
440 num_dependencies++;
\r
441 if (*s == SC_GROUP_IDENTIFIER) groups = s;
\r
445 /* At least one dependency is a group so we need to verify them. */
\r
448 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, NSSM_REGISTRY_GROUPS, 0, KEY_READ, &key)) {
\r
449 _ftprintf(stderr, _T("%s: %s\n"), NSSM_REGISTRY_GROUPS, error_string(GetLastError()));
\r
453 unsigned long type;
\r
454 unsigned long groupslen;
\r
455 unsigned long ret = RegQueryValueEx(key, NSSM_REG_GROUPS, 0, &type, NULL, &groupslen);
\r
456 if (ret == ERROR_SUCCESS) {
\r
457 groups = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, groupslen);
\r
459 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("groups"), _T("set_service_dependencies()"));
\r
463 ret = RegQueryValueEx(key, NSSM_REG_GROUPS, 0, &type, (unsigned char *) groups, &groupslen);
\r
464 if (ret != ERROR_SUCCESS) {
\r
465 _ftprintf(stderr, _T("%s\\%s: %s"), NSSM_REGISTRY_GROUPS, NSSM_REG_GROUPS, error_string(GetLastError()));
\r
466 HeapFree(GetProcessHeap(), 0, groups);
\r
471 else if (ret != ERROR_FILE_NOT_FOUND) {
\r
472 _ftprintf(stderr, _T("%s\\%s: %s"), NSSM_REGISTRY_GROUPS, NSSM_REG_GROUPS, error_string(GetLastError()));
\r
481 unsigned long dependencieslen = (num_dependencies * SERVICE_NAME_LENGTH) + 2;
\r
482 dependencies = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dependencieslen * sizeof(TCHAR));
\r
485 TCHAR dependency[SERVICE_NAME_LENGTH];
\r
486 for (s = buffer; *s; s++) {
\r
488 if (*s == SC_GROUP_IDENTIFIER) {
\r
489 TCHAR *group = s + 1;
\r
493 for (TCHAR *g = groups; *g; g++) {
\r
494 if (str_equiv(g, group)) {
\r
496 /* Set canonical name. */
\r
497 memmove(group, g, _tcslen(g) * sizeof(TCHAR));
\r
505 if (ok) _sntprintf_s(dependency, _countof(dependency), _TRUNCATE, _T("%s"), s);
\r
507 HeapFree(GetProcessHeap(), 0, dependencies);
\r
508 if (groups) HeapFree(GetProcessHeap(), 0, groups);
\r
509 _ftprintf(stderr, _T("%s: %s"), s, error_string(ERROR_SERVICE_DEPENDENCY_DELETED));
\r
514 SC_HANDLE dependency_handle = open_service(services, s, SERVICE_QUERY_STATUS, dependency, _countof(dependency));
\r
515 if (! dependency_handle) {
\r
516 HeapFree(GetProcessHeap(), 0, dependencies);
\r
517 if (groups) HeapFree(GetProcessHeap(), 0, groups);
\r
518 CloseServiceHandle(services);
\r
519 _ftprintf(stderr, _T("%s: %s"), s, error_string(ERROR_SERVICE_DEPENDENCY_DELETED));
\r
524 size_t len = _tcslen(dependency) + 1;
\r
525 memmove(dependencies + i, dependency, len * sizeof(TCHAR));
\r
531 if (groups) HeapFree(GetProcessHeap(), 0, groups);
\r
532 CloseServiceHandle(services);
\r
535 if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, dependencies, 0, 0, 0)) {
\r
536 if (num_dependencies) HeapFree(GetProcessHeap(), 0, dependencies);
\r
537 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
541 if (num_dependencies) HeapFree(GetProcessHeap(), 0, dependencies);
\r
545 int get_service_dependencies(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR **buffer, unsigned long *bufsize, int type) {
\r
546 if (! buffer) return 1;
\r
547 if (! bufsize) return 2;
\r
552 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
553 if (! qsc) return 3;
\r
555 if (! qsc->lpDependencies) return 0;
\r
556 if (! qsc->lpDependencies[0]) return 0;
\r
558 /* lpDependencies is doubly NULL terminated. */
\r
559 while (qsc->lpDependencies[*bufsize]) {
\r
560 while (qsc->lpDependencies[*bufsize]) ++*bufsize;
\r
566 *buffer = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *bufsize * sizeof(TCHAR));
\r
569 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("lpDependencies"), _T("get_service_dependencies()"));
\r
573 if (type == DEPENDENCY_ALL) memmove(*buffer, qsc->lpDependencies, *bufsize * sizeof(TCHAR));
\r
578 for (s = qsc->lpDependencies; *s; s++) {
\r
579 /* Only copy the appropriate type of dependency. */
\r
580 if ((*s == SC_GROUP_IDENTIFIER && type & DEPENDENCY_GROUPS) || (*s != SC_GROUP_IDENTIFIER && type & DEPENDENCY_SERVICES)) {
\r
581 size_t len = _tcslen(s) + 1;
\r
582 *bufsize += (unsigned long) len;
\r
583 memmove(*buffer + i, s, len * sizeof(TCHAR));
\r
592 HeapFree(GetProcessHeap(), 0, qsc);
\r
597 int get_service_dependencies(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR **buffer, unsigned long *bufsize) {
\r
598 return get_service_dependencies(service_name, service_handle, buffer, bufsize, DEPENDENCY_ALL);
\r
601 int set_service_description(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR *buffer) {
\r
602 SERVICE_DESCRIPTION description;
\r
603 ZeroMemory(&description, sizeof(description));
\r
605 lpDescription must be NULL if we aren't changing, the new description
\r
608 if (buffer && buffer[0]) description.lpDescription = buffer;
\r
609 else description.lpDescription = _T("");
\r
611 if (ChangeServiceConfig2(service_handle, SERVICE_CONFIG_DESCRIPTION, &description)) return 0;
\r
613 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_DESCRIPTION_FAILED, service_name, error_string(GetLastError()), 0);
\r
617 int get_service_description(const TCHAR *service_name, SC_HANDLE service_handle, unsigned long len, TCHAR *buffer) {
\r
618 if (! buffer) return 1;
\r
620 unsigned long bufsize;
\r
621 QueryServiceConfig2(service_handle, SERVICE_CONFIG_DESCRIPTION, 0, 0, &bufsize);
\r
622 unsigned long error = GetLastError();
\r
623 if (error == ERROR_INSUFFICIENT_BUFFER) {
\r
624 SERVICE_DESCRIPTION *description = (SERVICE_DESCRIPTION *) HeapAlloc(GetProcessHeap(), 0, bufsize);
\r
625 if (! description) {
\r
626 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("SERVICE_CONFIG_DESCRIPTION"), _T("get_service_description()"));
\r
630 if (QueryServiceConfig2(service_handle, SERVICE_CONFIG_DESCRIPTION, (unsigned char *) description, bufsize, &bufsize)) {
\r
631 if (description->lpDescription) _sntprintf_s(buffer, len, _TRUNCATE, _T("%s"), description->lpDescription);
\r
632 else ZeroMemory(buffer, len * sizeof(TCHAR));
\r
633 HeapFree(GetProcessHeap(), 0, description);
\r
637 HeapFree(GetProcessHeap(), 0, description);
\r
638 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_CONFIG_DESCRIPTION"), error_string(error));
\r
643 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_CONFIG_DESCRIPTION"), error_string(error));
\r
650 int get_service_startup(const TCHAR *service_name, SC_HANDLE service_handle, const QUERY_SERVICE_CONFIG *qsc, unsigned long *startup) {
\r
651 if (! qsc) return 1;
\r
653 switch (qsc->dwStartType) {
\r
654 case SERVICE_DEMAND_START: *startup = NSSM_STARTUP_MANUAL; break;
\r
655 case SERVICE_DISABLED: *startup = NSSM_STARTUP_DISABLED; break;
\r
656 default: *startup = NSSM_STARTUP_AUTOMATIC;
\r
659 if (*startup != NSSM_STARTUP_AUTOMATIC) return 0;
\r
661 /* Check for delayed start. */
\r
662 unsigned long bufsize;
\r
663 unsigned long error;
\r
664 QueryServiceConfig2(service_handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, 0, 0, &bufsize);
\r
665 error = GetLastError();
\r
666 if (error == ERROR_INSUFFICIENT_BUFFER) {
\r
667 SERVICE_DELAYED_AUTO_START_INFO *info = (SERVICE_DELAYED_AUTO_START_INFO *) HeapAlloc(GetProcessHeap(), 0, bufsize);
\r
669 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("SERVICE_DELAYED_AUTO_START_INFO"), _T("get_service_startup()"));
\r
673 if (QueryServiceConfig2(service_handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, (unsigned char *) info, bufsize, &bufsize)) {
\r
674 if (info->fDelayedAutostart) *startup = NSSM_STARTUP_DELAYED;
\r
675 HeapFree(GetProcessHeap(), 0, info);
\r
679 error = GetLastError();
\r
680 if (error != ERROR_INVALID_LEVEL) {
\r
681 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_CONFIG_DELAYED_AUTO_START_INFO"), error_string(error));
\r
686 else if (error != ERROR_INVALID_LEVEL) {
\r
687 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_DELAYED_AUTO_START_INFO"), error_string(error));
\r
694 int get_service_username(const TCHAR *service_name, const QUERY_SERVICE_CONFIG *qsc, TCHAR **username, size_t *usernamelen) {
\r
695 if (! username) return 1;
\r
696 if (! usernamelen) return 1;
\r
701 if (! qsc) return 1;
\r
703 if (qsc->lpServiceStartName[0]) {
\r
704 if (is_localsystem(qsc->lpServiceStartName)) return 0;
\r
706 size_t len = _tcslen(qsc->lpServiceStartName);
\r
707 *username = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(TCHAR));
\r
709 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("username"), _T("get_service_username()"));
\r
713 memmove(*username, qsc->lpServiceStartName, (len + 1) * sizeof(TCHAR));
\r
714 *usernamelen = len;
\r
720 /* Set default values which aren't zero. */
\r
721 void set_nssm_service_defaults(nssm_service_t *service) {
\r
722 if (! service) return;
\r
724 service->type = SERVICE_WIN32_OWN_PROCESS;
\r
725 service->priority = NORMAL_PRIORITY_CLASS;
\r
726 service->stdin_sharing = NSSM_STDIN_SHARING;
\r
727 service->stdin_disposition = NSSM_STDIN_DISPOSITION;
\r
728 service->stdin_flags = NSSM_STDIN_FLAGS;
\r
729 service->stdout_sharing = NSSM_STDOUT_SHARING;
\r
730 service->stdout_disposition = NSSM_STDOUT_DISPOSITION;
\r
731 service->stdout_flags = NSSM_STDOUT_FLAGS;
\r
732 service->stderr_sharing = NSSM_STDERR_SHARING;
\r
733 service->stderr_disposition = NSSM_STDERR_DISPOSITION;
\r
734 service->stderr_flags = NSSM_STDERR_FLAGS;
\r
735 service->throttle_delay = NSSM_RESET_THROTTLE_RESTART;
\r
736 service->stop_method = ~0;
\r
737 service->kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;
\r
738 service->kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;
\r
739 service->kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;
\r
740 service->kill_process_tree = 1;
\r
743 /* Allocate and zero memory for a service. */
\r
744 nssm_service_t *alloc_nssm_service() {
\r
745 nssm_service_t *service = (nssm_service_t *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(nssm_service_t));
\r
746 if (! service) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("service"), _T("alloc_nssm_service()"), 0);
\r
750 /* Free memory for a service. */
\r
751 void cleanup_nssm_service(nssm_service_t *service) {
\r
752 if (! service) return;
\r
753 if (service->username) HeapFree(GetProcessHeap(), 0, service->username);
\r
754 if (service->password) {
\r
755 SecureZeroMemory(service->password, service->passwordlen);
\r
756 HeapFree(GetProcessHeap(), 0, service->password);
\r
758 if (service->dependencies) HeapFree(GetProcessHeap(), 0, service->dependencies);
\r
759 if (service->env) HeapFree(GetProcessHeap(), 0, service->env);
\r
760 if (service->env_extra) HeapFree(GetProcessHeap(), 0, service->env_extra);
\r
761 if (service->handle) CloseServiceHandle(service->handle);
\r
762 if (service->process_handle) CloseHandle(service->process_handle);
\r
763 if (service->wait_handle) UnregisterWait(service->wait_handle);
\r
764 if (service->throttle_section_initialised) DeleteCriticalSection(&service->throttle_section);
\r
765 if (service->throttle_timer) CloseHandle(service->throttle_timer);
\r
766 if (service->hook_section_initialised) DeleteCriticalSection(&service->hook_section);
\r
767 if (service->initial_env) FreeEnvironmentStrings(service->initial_env);
\r
768 HeapFree(GetProcessHeap(), 0, service);
\r
771 /* About to install the service */
\r
772 int pre_install_service(int argc, TCHAR **argv) {
\r
773 nssm_service_t *service = alloc_nssm_service();
\r
774 set_nssm_service_defaults(service);
\r
775 if (argc) _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]);
\r
777 /* Show the dialogue box if we didn't give the service name and path */
\r
778 if (argc < 2) return nssm_gui(IDD_INSTALL, service);
\r
781 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("service"), _T("pre_install_service()"));
\r
784 _sntprintf_s(service->exe, _countof(service->exe), _TRUNCATE, _T("%s"), argv[1]);
\r
786 /* Arguments are optional */
\r
787 size_t flagslen = 0;
\r
790 for (i = 2; i < argc; i++) flagslen += _tcslen(argv[i]) + 1;
\r
791 if (! flagslen) flagslen = 1;
\r
792 if (flagslen > _countof(service->flags)) {
\r
793 print_message(stderr, NSSM_MESSAGE_FLAGS_TOO_LONG);
\r
797 for (i = 2; i < argc; i++) {
\r
798 size_t len = _tcslen(argv[i]);
\r
799 memmove(service->flags + s, argv[i], len * sizeof(TCHAR));
\r
801 if (i < argc - 1) service->flags[s++] = _T(' ');
\r
804 /* Work out directory name */
\r
805 _sntprintf_s(service->dir, _countof(service->dir), _TRUNCATE, _T("%s"), service->exe);
\r
806 strip_basename(service->dir);
\r
808 int ret = install_service(service);
\r
809 cleanup_nssm_service(service);
\r
813 /* About to edit the service. */
\r
814 int pre_edit_service(int argc, TCHAR **argv) {
\r
815 /* Require service name. */
\r
816 if (argc < 2) return usage(1);
\r
818 /* Are we editing on the command line? */
\r
819 enum { MODE_EDITING, MODE_GETTING, MODE_SETTING, MODE_RESETTING } mode = MODE_EDITING;
\r
820 const TCHAR *verb = argv[0];
\r
821 const TCHAR *service_name = argv[1];
\r
822 bool getting = false;
\r
823 bool unsetting = false;
\r
825 /* Minimum number of arguments. */
\r
827 /* Index of first value. */
\r
830 if (str_equiv(verb, _T("get"))) {
\r
832 mode = MODE_GETTING;
\r
834 else if (str_equiv(verb, _T("set"))) {
\r
836 mode = MODE_SETTING;
\r
838 else if (str_equiv(verb, _T("reset")) || str_equiv(verb, _T("unset"))) {
\r
840 mode = MODE_RESETTING;
\r
842 if (argc < mandatory) return usage(1);
\r
844 const TCHAR *parameter = 0;
\r
845 settings_t *setting = 0;
\r
848 /* Validate the parameter. */
\r
849 if (mandatory > 2) {
\r
850 bool additional_mandatory = false;
\r
852 parameter = argv[2];
\r
853 for (i = 0; settings[i].name; i++) {
\r
854 setting = &settings[i];
\r
855 if (! str_equiv(setting->name, parameter)) continue;
\r
856 if (((setting->additional & ADDITIONAL_GETTING) && mode == MODE_GETTING) || ((setting->additional & ADDITIONAL_SETTING) && mode == MODE_SETTING) || ((setting->additional & ADDITIONAL_RESETTING) && mode == MODE_RESETTING)) {
\r
857 additional_mandatory = true;
\r
862 if (! settings[i].name) {
\r
863 print_message(stderr, NSSM_MESSAGE_INVALID_PARAMETER, parameter);
\r
864 for (i = 0; settings[i].name; i++) _ftprintf(stderr, _T("%s\n"), settings[i].name);
\r
869 if (additional_mandatory) {
\r
870 if (argc < mandatory) {
\r
871 print_message(stderr, NSSM_MESSAGE_MISSING_SUBPARAMETER, parameter);
\r
874 additional = argv[3];
\r
877 else if (str_equiv(setting->name, NSSM_NATIVE_OBJECTNAME) && mode == MODE_SETTING) {
\r
878 additional = argv[3];
\r
882 additional = argv[remainder];
\r
883 if (argc < mandatory) return usage(1);
\r
887 nssm_service_t *service = alloc_nssm_service();
\r
888 _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), service_name);
\r
890 /* Open service manager */
\r
891 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
\r
893 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
897 /* Try to open the service */
\r
898 unsigned long access = SERVICE_QUERY_CONFIG;
\r
899 if (mode != MODE_GETTING) access |= SERVICE_CHANGE_CONFIG;
\r
900 service->handle = open_service(services, service->name, access, service->name, _countof(service->name));
\r
901 if (! service->handle) {
\r
902 CloseServiceHandle(services);
\r
906 /* Get system details. */
\r
907 QUERY_SERVICE_CONFIG *qsc = query_service_config(service->name, service->handle);
\r
909 CloseServiceHandle(service->handle);
\r
910 CloseServiceHandle(services);
\r
914 service->type = qsc->dwServiceType;
\r
915 if (! (service->type & SERVICE_WIN32_OWN_PROCESS)) {
\r
916 if (mode != MODE_GETTING) {
\r
917 HeapFree(GetProcessHeap(), 0, qsc);
\r
918 CloseServiceHandle(service->handle);
\r
919 CloseServiceHandle(services);
\r
920 print_message(stderr, NSSM_MESSAGE_CANNOT_EDIT, service->name, NSSM_WIN32_OWN_PROCESS, 0);
\r
925 if (get_service_startup(service->name, service->handle, qsc, &service->startup)) {
\r
926 if (mode != MODE_GETTING) {
\r
927 HeapFree(GetProcessHeap(), 0, qsc);
\r
928 CloseServiceHandle(service->handle);
\r
929 CloseServiceHandle(services);
\r
934 if (get_service_username(service->name, qsc, &service->username, &service->usernamelen)) {
\r
935 if (mode != MODE_GETTING) {
\r
936 HeapFree(GetProcessHeap(), 0, qsc);
\r
937 CloseServiceHandle(service->handle);
\r
938 CloseServiceHandle(services);
\r
943 _sntprintf_s(service->displayname, _countof(service->displayname), _TRUNCATE, _T("%s"), qsc->lpDisplayName);
\r
945 /* Get the canonical service name. We open it case insensitively. */
\r
946 unsigned long bufsize = _countof(service->name);
\r
947 GetServiceKeyName(services, service->displayname, service->name, &bufsize);
\r
949 /* Remember the executable in case it isn't NSSM. */
\r
950 _sntprintf_s(service->image, _countof(service->image), _TRUNCATE, _T("%s"), qsc->lpBinaryPathName);
\r
951 HeapFree(GetProcessHeap(), 0, qsc);
\r
953 /* Get extended system details. */
\r
954 if (get_service_description(service->name, service->handle, _countof(service->description), service->description)) {
\r
955 if (mode != MODE_GETTING) {
\r
956 CloseServiceHandle(service->handle);
\r
957 CloseServiceHandle(services);
\r
962 if (get_service_dependencies(service->name, service->handle, &service->dependencies, &service->dependencieslen)) {
\r
963 if (mode != MODE_GETTING) {
\r
964 CloseServiceHandle(service->handle);
\r
965 CloseServiceHandle(services);
\r
970 /* Get NSSM details. */
\r
971 get_parameters(service, 0);
\r
973 CloseServiceHandle(services);
\r
975 if (! service->exe[0]) {
\r
976 service->native = true;
\r
977 if (mode != MODE_GETTING) print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE, service->name, NSSM, service->image);
\r
980 /* Editing with the GUI. */
\r
981 if (mode == MODE_EDITING) {
\r
982 nssm_gui(IDD_EDIT, service);
\r
986 /* Trying to manage App* parameters for a non-NSSM service. */
\r
987 if (! setting->native && service->native) {
\r
988 CloseServiceHandle(service->handle);
\r
989 print_message(stderr, NSSM_MESSAGE_NATIVE_PARAMETER, setting->name, NSSM);
\r
997 if (mode == MODE_GETTING) {
\r
998 if (! service->native) {
\r
999 key = open_registry(service->name, KEY_READ);
\r
1000 if (! key) return 4;
\r
1003 if (setting->native) ret = get_setting(service->name, service->handle, setting, &value, additional);
\r
1004 else ret = get_setting(service->name, key, setting, &value, additional);
\r
1006 CloseServiceHandle(service->handle);
\r
1010 switch (setting->type) {
\r
1011 case REG_EXPAND_SZ:
\r
1012 case REG_MULTI_SZ:
\r
1014 _tprintf(_T("%s\n"), value.string ? value.string : _T(""));
\r
1015 HeapFree(GetProcessHeap(), 0, value.string);
\r
1019 _tprintf(_T("%u\n"), value.numeric);
\r
1023 if (! service->native) RegCloseKey(key);
\r
1024 CloseServiceHandle(service->handle);
\r
1028 /* Build the value. */
\r
1029 if (mode == MODE_RESETTING) {
\r
1030 /* Unset the parameter. */
\r
1033 else if (remainder == argc) {
\r
1037 /* Set the parameter. */
\r
1039 size_t delimiterlen = (setting->additional & ADDITIONAL_CRLF) ? 2 : 1;
\r
1040 for (i = remainder; i < argc; i++) len += _tcslen(argv[i]) + delimiterlen;
\r
1043 value.string = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, len * sizeof(TCHAR));
\r
1044 if (! value.string) {
\r
1045 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("value"), _T("edit_service()"));
\r
1046 CloseServiceHandle(service->handle);
\r
1051 for (i = remainder; i < argc; i++) {
\r
1052 size_t len = _tcslen(argv[i]);
\r
1053 memmove(value.string + s, argv[i], len * sizeof(TCHAR));
\r
1055 if (i < argc - 1) {
\r
1056 if (setting->additional & ADDITIONAL_CRLF) {
\r
1057 value.string[s++] = _T('\r');
\r
1058 value.string[s++] = _T('\n');
\r
1060 else value.string[s++] = _T(' ');
\r
1063 value.string[s] = _T('\0');
\r
1066 if (! service->native) {
\r
1067 key = open_registry(service->name, KEY_WRITE);
\r
1069 if (value.string) HeapFree(GetProcessHeap(), 0, value.string);
\r
1074 if (setting->native) ret = set_setting(service->name, service->handle, setting, &value, additional);
\r
1075 else ret = set_setting(service->name, key, setting, &value, additional);
\r
1076 if (value.string) HeapFree(GetProcessHeap(), 0, value.string);
\r
1078 if (! service->native) RegCloseKey(key);
\r
1079 CloseServiceHandle(service->handle);
\r
1083 if (! service->native) RegCloseKey(key);
\r
1084 CloseServiceHandle(service->handle);
\r
1089 /* About to remove the service */
\r
1090 int pre_remove_service(int argc, TCHAR **argv) {
\r
1091 nssm_service_t *service = alloc_nssm_service();
\r
1092 set_nssm_service_defaults(service);
\r
1093 if (argc) _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]);
\r
1095 /* Show dialogue box if we didn't pass service name and "confirm" */
\r
1096 if (argc < 2) return nssm_gui(IDD_REMOVE, service);
\r
1097 if (str_equiv(argv[1], _T("confirm"))) {
\r
1098 int ret = remove_service(service);
\r
1099 cleanup_nssm_service(service);
\r
1102 print_message(stderr, NSSM_MESSAGE_PRE_REMOVE_SERVICE);
\r
1106 /* Install the service */
\r
1107 int install_service(nssm_service_t *service) {
\r
1108 if (! service) return 1;
\r
1110 /* Open service manager */
\r
1111 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE);
\r
1113 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
1114 cleanup_nssm_service(service);
\r
1118 /* Get path of this program */
\r
1119 GetModuleFileName(0, service->image, _countof(service->image));
\r
1121 /* Create the service - settings will be changed in edit_service() */
\r
1122 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
1123 if (! service->handle) {
\r
1124 print_message(stderr, NSSM_MESSAGE_CREATESERVICE_FAILED, error_string(GetLastError()));
\r
1125 CloseServiceHandle(services);
\r
1129 if (edit_service(service, false)) {
\r
1130 DeleteService(service->handle);
\r
1131 CloseServiceHandle(services);
\r
1135 print_message(stdout, NSSM_MESSAGE_SERVICE_INSTALLED, service->name);
\r
1138 CloseServiceHandle(services);
\r
1143 /* Edit the service. */
\r
1144 int edit_service(nssm_service_t *service, bool editing) {
\r
1145 if (! service) return 1;
\r
1148 The only two valid flags for service type are SERVICE_WIN32_OWN_PROCESS
\r
1149 and SERVICE_INTERACTIVE_PROCESS.
\r
1151 service->type &= SERVICE_INTERACTIVE_PROCESS;
\r
1152 service->type |= SERVICE_WIN32_OWN_PROCESS;
\r
1154 /* Startup type. */
\r
1155 unsigned long startup;
\r
1156 switch (service->startup) {
\r
1157 case NSSM_STARTUP_MANUAL: startup = SERVICE_DEMAND_START; break;
\r
1158 case NSSM_STARTUP_DISABLED: startup = SERVICE_DISABLED; break;
\r
1159 default: startup = SERVICE_AUTO_START;
\r
1162 /* Display name. */
\r
1163 if (! service->displayname[0]) _sntprintf_s(service->displayname, _countof(service->displayname), _TRUNCATE, _T("%s"), service->name);
\r
1166 Username must be NULL if we aren't changing or an account name.
\r
1167 We must explicitly use LOCALSYSTEM to change it when we are editing.
\r
1168 Password must be NULL if we aren't changing, a password or "".
\r
1169 Empty passwords are valid but we won't allow them in the GUI.
\r
1171 TCHAR *username = 0;
\r
1173 TCHAR *password = 0;
\r
1174 if (service->usernamelen) {
\r
1175 username = service->username;
\r
1176 if (canonicalise_username(username, &canon)) return 5;
\r
1177 if (service->passwordlen) password = service->password;
\r
1179 else if (editing) username = canon = NSSM_LOCALSYSTEM_ACCOUNT;
\r
1181 if (well_known_username(canon)) password = _T("");
\r
1183 if (grant_logon_as_service(canon)) {
\r
1184 if (canon != username) HeapFree(GetProcessHeap(), 0, canon);
\r
1185 print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username);
\r
1190 TCHAR *dependencies = _T("");
\r
1191 if (service->dependencieslen) dependencies = 0; /* Change later. */
\r
1193 if (! ChangeServiceConfig(service->handle, service->type, startup, SERVICE_NO_CHANGE, 0, 0, 0, dependencies, canon, password, service->displayname)) {
\r
1194 if (canon != username) HeapFree(GetProcessHeap(), 0, canon);
\r
1195 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
1198 if (canon != username) HeapFree(GetProcessHeap(), 0, canon);
\r
1200 if (service->dependencieslen) {
\r
1201 if (set_service_dependencies(service->name, service->handle, service->dependencies)) return 5;
\r
1204 if (service->description[0] || editing) {
\r
1205 set_service_description(service->name, service->handle, service->description);
\r
1208 SERVICE_DELAYED_AUTO_START_INFO delayed;
\r
1209 ZeroMemory(&delayed, sizeof(delayed));
\r
1210 if (service->startup == NSSM_STARTUP_DELAYED) delayed.fDelayedAutostart = 1;
\r
1211 else delayed.fDelayedAutostart = 0;
\r
1212 /* Delayed startup isn't supported until Vista. */
\r
1213 if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, &delayed)) {
\r
1214 unsigned long error = GetLastError();
\r
1215 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
\r
1216 if (error != ERROR_INVALID_LEVEL) {
\r
1217 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_DELAYED_AUTO_START_INFO_FAILED, service->name, error_string(error), 0);
\r
1221 /* Don't mess with parameters which aren't ours. */
\r
1222 if (! service->native) {
\r
1223 /* Now we need to put the parameters into the registry */
\r
1224 if (create_parameters(service, editing)) {
\r
1225 print_message(stderr, NSSM_MESSAGE_CREATE_PARAMETERS_FAILED);
\r
1229 set_service_recovery(service);
\r
1235 /* Control a service. */
\r
1236 int control_service(unsigned long control, int argc, TCHAR **argv) {
\r
1237 if (argc < 1) return usage(1);
\r
1238 TCHAR *service_name = argv[0];
\r
1239 TCHAR canonical_name[SERVICE_NAME_LENGTH];
\r
1241 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
\r
1243 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
1247 unsigned long access = SERVICE_QUERY_STATUS;
\r
1248 switch (control) {
\r
1249 case NSSM_SERVICE_CONTROL_START:
\r
1250 access |= SERVICE_START;
\r
1253 case SERVICE_CONTROL_CONTINUE:
\r
1254 case SERVICE_CONTROL_PAUSE:
\r
1255 access |= SERVICE_PAUSE_CONTINUE;
\r
1258 case SERVICE_CONTROL_STOP:
\r
1259 access |= SERVICE_STOP;
\r
1262 case NSSM_SERVICE_CONTROL_ROTATE:
\r
1263 access |= SERVICE_USER_DEFINED_CONTROL;
\r
1267 SC_HANDLE service_handle = open_service(services, service_name, access, canonical_name, _countof(canonical_name));
\r
1268 if (! service_handle) {
\r
1269 CloseServiceHandle(services);
\r
1274 unsigned long error;
\r
1275 SERVICE_STATUS service_status;
\r
1276 if (control == NSSM_SERVICE_CONTROL_START) {
\r
1277 unsigned long initial_status = SERVICE_STOPPED;
\r
1278 ret = StartService(service_handle, (unsigned long) argc, (const TCHAR **) argv);
\r
1279 error = GetLastError();
\r
1280 CloseServiceHandle(services);
\r
1282 if (error == ERROR_IO_PENDING) {
\r
1284 Older versions of Windows return immediately with ERROR_IO_PENDING
\r
1285 indicate that the operation is still in progress. Newer versions
\r
1286 will return it if there really is a delay.
\r
1289 error = ERROR_SUCCESS;
\r
1293 int response = await_service_control_response(control, service_handle, &service_status, initial_status);
\r
1294 CloseServiceHandle(service_handle);
\r
1297 print_message(stderr, NSSM_MESSAGE_BAD_CONTROL_RESPONSE, canonical_name, service_status_text(service_status.dwCurrentState), service_control_text(control));
\r
1300 else _tprintf(_T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1304 CloseServiceHandle(service_handle);
\r
1305 _ftprintf(stderr, _T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1309 else if (control == SERVICE_CONTROL_INTERROGATE) {
\r
1311 We could actually send an INTERROGATE control but that won't return
\r
1312 any information if the service is stopped and we don't care about
\r
1313 the extra details it might give us in any case. So we'll fake it.
\r
1315 ret = QueryServiceStatus(service_handle, &service_status);
\r
1316 error = GetLastError();
\r
1319 _tprintf(_T("%s\n"), service_status_text(service_status.dwCurrentState));
\r
1323 _ftprintf(stderr, _T("%s: %s\n"), canonical_name, error_string(error));
\r
1328 ret = ControlService(service_handle, control, &service_status);
\r
1329 unsigned long initial_status = service_status.dwCurrentState;
\r
1330 error = GetLastError();
\r
1331 CloseServiceHandle(services);
\r
1333 if (error == ERROR_IO_PENDING) {
\r
1335 error = ERROR_SUCCESS;
\r
1339 int response = await_service_control_response(control, service_handle, &service_status, initial_status);
\r
1340 CloseServiceHandle(service_handle);
\r
1343 print_message(stderr, NSSM_MESSAGE_BAD_CONTROL_RESPONSE, canonical_name, service_status_text(service_status.dwCurrentState), service_control_text(control));
\r
1346 else _tprintf(_T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1350 CloseServiceHandle(service_handle);
\r
1351 _ftprintf(stderr, _T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1352 if (error == ERROR_SERVICE_NOT_ACTIVE) {
\r
1353 if (control == SERVICE_CONTROL_SHUTDOWN || control == SERVICE_CONTROL_STOP) return 0;
\r
1360 /* Remove the service */
\r
1361 int remove_service(nssm_service_t *service) {
\r
1362 if (! service) return 1;
\r
1364 /* Open service manager */
\r
1365 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
\r
1367 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
1371 /* Try to open the service */
\r
1372 service->handle = open_service(services, service->name, DELETE, service->name, _countof(service->name));
\r
1373 if (! service->handle) {
\r
1374 CloseServiceHandle(services);
\r
1378 /* Get the canonical service name. We open it case insensitively. */
\r
1379 unsigned long bufsize = _countof(service->displayname);
\r
1380 GetServiceDisplayName(services, service->name, service->displayname, &bufsize);
\r
1381 bufsize = _countof(service->name);
\r
1382 GetServiceKeyName(services, service->displayname, service->name, &bufsize);
\r
1384 /* Try to delete the service */
\r
1385 if (! DeleteService(service->handle)) {
\r
1386 print_message(stderr, NSSM_MESSAGE_DELETESERVICE_FAILED);
\r
1387 CloseServiceHandle(services);
\r
1392 CloseServiceHandle(services);
\r
1394 print_message(stdout, NSSM_MESSAGE_SERVICE_REMOVED, service->name);
\r
1398 /* Service initialisation */
\r
1399 void WINAPI service_main(unsigned long argc, TCHAR **argv) {
\r
1400 nssm_service_t *service = alloc_nssm_service();
\r
1401 if (! service) return;
\r
1403 if (_sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]) < 0) {
\r
1404 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("service->name"), _T("service_main()"), 0);
\r
1408 /* We can use a condition variable in a critical section on Vista or later. */
\r
1409 if (imports.SleepConditionVariableCS && imports.WakeConditionVariable) use_critical_section = true;
\r
1410 else use_critical_section = false;
\r
1412 /* Initialise status */
\r
1413 ZeroMemory(&service->status, sizeof(service->status));
\r
1414 service->status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;
\r
1415 service->status.dwControlsAccepted = 0;
\r
1416 service->status.dwWin32ExitCode = NO_ERROR;
\r
1417 service->status.dwServiceSpecificExitCode = 0;
\r
1418 service->status.dwCheckPoint = 0;
\r
1419 service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;
\r
1421 /* Signal we AREN'T running the server */
\r
1422 service->process_handle = 0;
\r
1425 /* Register control handler */
\r
1426 service->status_handle = RegisterServiceCtrlHandlerEx(NSSM, service_control_handler, (void *) service);
\r
1427 if (! service->status_handle) {
\r
1428 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_REGISTERSERVICECTRLHANDER_FAILED, error_string(GetLastError()), 0);
\r
1432 log_service_control(service->name, 0, true);
\r
1434 service->status.dwCurrentState = SERVICE_START_PENDING;
\r
1435 service->status.dwWaitHint = service->throttle_delay + NSSM_WAITHINT_MARGIN;
\r
1436 SetServiceStatus(service->status_handle, &service->status);
\r
1439 /* Try to create the exit action parameters; we don't care if it fails */
\r
1440 create_exit_action(service->name, exit_action_strings[0], false);
\r
1442 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT);
\r
1444 service->handle = open_service(services, service->name, SERVICE_CHANGE_CONFIG, 0, 0);
\r
1445 set_service_recovery(service);
\r
1447 /* Remember our display name. */
\r
1448 unsigned long displayname_len = _countof(service->displayname);
\r
1449 GetServiceDisplayName(services, service->name, service->displayname, &displayname_len);
\r
1451 CloseServiceHandle(services);
\r
1455 /* Used for signalling a resume if the service pauses when throttled. */
\r
1456 if (use_critical_section) {
\r
1457 InitializeCriticalSection(&service->throttle_section);
\r
1458 service->throttle_section_initialised = true;
\r
1461 service->throttle_timer = CreateWaitableTimer(0, 1, 0);
\r
1462 if (! service->throttle_timer) {
\r
1463 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_CREATEWAITABLETIMER_FAILED, service->name, error_string(GetLastError()), 0);
\r
1467 /* Critical section for hooks. */
\r
1468 InitializeCriticalSection(&service->hook_section);
\r
1469 service->hook_section_initialised = true;
\r
1471 /* Remember our initial environment. */
\r
1472 service->initial_env = GetEnvironmentStrings();
\r
1474 /* Remember our creation time. */
\r
1475 if (get_process_creation_time(GetCurrentProcess(), &service->nssm_creation_time)) ZeroMemory(&service->nssm_creation_time, sizeof(service->nssm_creation_time));
\r
1477 service->allow_restart = true;
\r
1478 if (! CreateThread(NULL, 0, launch_service, (void *) service, 0, NULL)) {
\r
1479 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETHREAD_FAILED, error_string(GetLastError()), 0);
\r
1480 stop_service(service, 0, true, true);
\r
1484 /* Make sure service recovery actions are taken where necessary */
\r
1485 void set_service_recovery(nssm_service_t *service) {
\r
1486 SERVICE_FAILURE_ACTIONS_FLAG flag;
\r
1487 ZeroMemory(&flag, sizeof(flag));
\r
1488 flag.fFailureActionsOnNonCrashFailures = true;
\r
1490 /* This functionality was added in Vista so the call may fail */
\r
1491 if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, &flag)) {
\r
1492 unsigned long error = GetLastError();
\r
1493 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
\r
1494 if (error != ERROR_INVALID_LEVEL) {
\r
1495 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_FAILURE_ACTIONS_FAILED, service->name, error_string(error), 0);
\r
1500 int monitor_service(nssm_service_t *service) {
\r
1501 /* Set service status to started */
\r
1502 int ret = start_service(service);
\r
1505 _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%d"), ret);
\r
1506 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_START_SERVICE_FAILED, service->exe, service->name, ret, 0);
\r
1509 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_STARTED_SERVICE, service->exe, service->flags, service->name, service->dir, 0);
\r
1511 /* Monitor service */
\r
1512 if (! RegisterWaitForSingleObject(&service->wait_handle, service->process_handle, end_service, (void *) service, INFINITE, WT_EXECUTEONLYONCE | WT_EXECUTELONGFUNCTION)) {
\r
1513 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_REGISTERWAITFORSINGLEOBJECT_FAILED, service->name, service->exe, error_string(GetLastError()), 0);
\r
1519 TCHAR *service_control_text(unsigned long control) {
\r
1520 switch (control) {
\r
1521 /* HACK: there is no SERVICE_CONTROL_START constant */
\r
1522 case NSSM_SERVICE_CONTROL_START: return _T("START");
\r
1523 case SERVICE_CONTROL_STOP: return _T("STOP");
\r
1524 case SERVICE_CONTROL_SHUTDOWN: return _T("SHUTDOWN");
\r
1525 case SERVICE_CONTROL_PAUSE: return _T("PAUSE");
\r
1526 case SERVICE_CONTROL_CONTINUE: return _T("CONTINUE");
\r
1527 case SERVICE_CONTROL_INTERROGATE: return _T("INTERROGATE");
\r
1528 case NSSM_SERVICE_CONTROL_ROTATE: return _T("ROTATE");
\r
1529 case SERVICE_CONTROL_POWEREVENT: return _T("POWEREVENT");
\r
1530 default: return 0;
\r
1534 TCHAR *service_status_text(unsigned long status) {
\r
1536 case SERVICE_STOPPED: return _T("SERVICE_STOPPED");
\r
1537 case SERVICE_START_PENDING: return _T("SERVICE_START_PENDING");
\r
1538 case SERVICE_STOP_PENDING: return _T("SERVICE_STOP_PENDING");
\r
1539 case SERVICE_RUNNING: return _T("SERVICE_RUNNING");
\r
1540 case SERVICE_CONTINUE_PENDING: return _T("SERVICE_CONTINUE_PENDING");
\r
1541 case SERVICE_PAUSE_PENDING: return _T("SERVICE_PAUSE_PENDING");
\r
1542 case SERVICE_PAUSED: return _T("SERVICE_PAUSED");
\r
1543 default: return 0;
\r
1547 void log_service_control(TCHAR *service_name, unsigned long control, bool handled) {
\r
1548 TCHAR *text = service_control_text(control);
\r
1549 unsigned long event;
\r
1552 /* "0x" + 8 x hex + NULL */
\r
1553 text = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, 11 * sizeof(TCHAR));
\r
1555 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);
\r
1558 if (_sntprintf_s(text, 11, _TRUNCATE, _T("0x%08x"), control) < 0) {
\r
1559 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);
\r
1560 HeapFree(GetProcessHeap(), 0, text);
\r
1564 event = NSSM_EVENT_SERVICE_CONTROL_UNKNOWN;
\r
1566 else if (handled) event = NSSM_EVENT_SERVICE_CONTROL_HANDLED;
\r
1567 else event = NSSM_EVENT_SERVICE_CONTROL_NOT_HANDLED;
\r
1569 log_event(EVENTLOG_INFORMATION_TYPE, event, service_name, text, 0);
\r
1571 if (event == NSSM_EVENT_SERVICE_CONTROL_UNKNOWN) {
\r
1572 HeapFree(GetProcessHeap(), 0, text);
\r
1576 /* Service control handler */
\r
1577 unsigned long WINAPI service_control_handler(unsigned long control, unsigned long event, void *data, void *context) {
\r
1578 nssm_service_t *service = (nssm_service_t *) context;
\r
1580 switch (control) {
\r
1581 case SERVICE_CONTROL_INTERROGATE:
\r
1582 /* We always keep the service status up-to-date so this is a no-op. */
\r
1585 case SERVICE_CONTROL_SHUTDOWN:
\r
1586 case SERVICE_CONTROL_STOP:
\r
1587 service->last_control = control;
\r
1588 log_service_control(service->name, control, true);
\r
1590 /* Immediately block further controls. */
\r
1591 service->allow_restart = false;
\r
1592 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
1593 service->status.dwControlsAccepted = 0;
\r
1594 SetServiceStatus(service->status_handle, &service->status);
\r
1596 /* Pre-stop hook. */
\r
1597 nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_STOP, NSSM_HOOK_ACTION_PRE, &control, NSSM_SERVICE_STATUS_DEADLINE, false);
\r
1600 We MUST acknowledge the stop request promptly but we're committed to
\r
1601 waiting for the application to exit. Spawn a new thread to wait
\r
1602 while we acknowledge the request.
\r
1604 if (! CreateThread(NULL, 0, shutdown_service, context, 0, NULL)) {
\r
1605 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETHREAD_FAILED, error_string(GetLastError()), 0);
\r
1608 We couldn't create a thread to tidy up so we'll have to force the tidyup
\r
1609 to complete in time in this thread.
\r
1611 service->kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;
\r
1612 service->kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;
\r
1613 service->kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;
\r
1615 stop_service(service, 0, true, true);
\r
1619 case SERVICE_CONTROL_CONTINUE:
\r
1620 service->last_control = control;
\r
1621 log_service_control(service->name, control, true);
\r
1622 service->throttle = 0;
\r
1623 if (use_critical_section) imports.WakeConditionVariable(&service->throttle_condition);
\r
1625 if (! service->throttle_timer) return ERROR_CALL_NOT_IMPLEMENTED;
\r
1626 ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));
\r
1627 SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);
\r
1629 /* We can't continue if the application is running! */
\r
1630 if (! service->process_handle) service->status.dwCurrentState = SERVICE_CONTINUE_PENDING;
\r
1631 service->status.dwWaitHint = throttle_milliseconds(service->throttle) + NSSM_WAITHINT_MARGIN;
\r
1632 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESET_THROTTLE, service->name, 0);
\r
1633 SetServiceStatus(service->status_handle, &service->status);
\r
1636 case SERVICE_CONTROL_PAUSE:
\r
1638 We don't accept pause messages but it isn't possible to register
\r
1639 only for continue messages so we have to handle this case.
\r
1641 log_service_control(service->name, control, false);
\r
1642 return ERROR_CALL_NOT_IMPLEMENTED;
\r
1644 case NSSM_SERVICE_CONTROL_ROTATE:
\r
1645 service->last_control = control;
\r
1646 log_service_control(service->name, control, true);
\r
1647 (void) nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_ROTATE, NSSM_HOOK_ACTION_PRE, &control, NSSM_HOOK_DEADLINE, false);
\r
1648 if (service->rotate_stdout_online == NSSM_ROTATE_ONLINE) service->rotate_stdout_online = NSSM_ROTATE_ONLINE_ASAP;
\r
1649 if (service->rotate_stderr_online == NSSM_ROTATE_ONLINE) service->rotate_stderr_online = NSSM_ROTATE_ONLINE_ASAP;
\r
1650 (void) nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_ROTATE, NSSM_HOOK_ACTION_POST, &control);
\r
1653 case SERVICE_CONTROL_POWEREVENT:
\r
1654 /* Resume from suspend. */
\r
1655 if (event == PBT_APMRESUMEAUTOMATIC) {
\r
1656 service->last_control = control;
\r
1657 log_service_control(service->name, control, true);
\r
1658 (void) nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_POWER, NSSM_HOOK_ACTION_RESUME, &control);
\r
1662 /* Battery low or changed to A/C power or something. */
\r
1663 if (event == PBT_APMPOWERSTATUSCHANGE) {
\r
1664 service->last_control = control;
\r
1665 log_service_control(service->name, control, true);
\r
1666 (void) nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_POWER, NSSM_HOOK_ACTION_CHANGE, &control);
\r
1669 log_service_control(service->name, control, false);
\r
1673 /* Unknown control */
\r
1674 log_service_control(service->name, control, false);
\r
1675 return ERROR_CALL_NOT_IMPLEMENTED;
\r
1678 /* Start the service */
\r
1679 int start_service(nssm_service_t *service) {
\r
1680 service->stopping = false;
\r
1682 if (service->process_handle) return 0;
\r
1683 service->start_requested_count++;
\r
1685 /* Allocate a STARTUPINFO structure for a new process */
\r
1687 ZeroMemory(&si, sizeof(si));
\r
1688 si.cb = sizeof(si);
\r
1690 /* Allocate a PROCESSINFO structure for the process */
\r
1691 PROCESS_INFORMATION pi;
\r
1692 ZeroMemory(&pi, sizeof(pi));
\r
1694 /* Get startup parameters */
\r
1695 int ret = get_parameters(service, &si);
\r
1697 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_PARAMETERS_FAILED, service->name, 0);
\r
1698 unset_service_environment(service);
\r
1699 return stop_service(service, 2, true, true);
\r
1702 /* Launch executable with arguments */
\r
1703 TCHAR cmd[CMD_LENGTH];
\r
1704 if (_sntprintf_s(cmd, _countof(cmd), _TRUNCATE, _T("\"%s\" %s"), service->exe, service->flags) < 0) {
\r
1705 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("command line"), _T("start_service"), 0);
\r
1706 unset_service_environment(service);
\r
1707 return stop_service(service, 2, true, true);
\r
1710 throttle_restart(service);
\r
1712 service->status.dwCurrentState = SERVICE_START_PENDING;
\r
1713 service->status.dwControlsAccepted = SERVICE_ACCEPT_POWEREVENT | SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP;
\r
1714 SetServiceStatus(service->status_handle, &service->status);
\r
1716 /* Pre-start hook. */
\r
1717 unsigned long control = NSSM_SERVICE_CONTROL_START;
\r
1718 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
1720 _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), NSSM_HOOK_STATUS_ABORT);
\r
1721 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_PRESTART_HOOK_ABORT, NSSM_HOOK_EVENT_START, NSSM_HOOK_ACTION_PRE, service->name, code, 0);
\r
1722 unset_service_environment(service);
\r
1723 return stop_service(service, 5, true, true);
\r
1726 /* Did another thread receive a stop control? */
\r
1727 if (service->allow_restart) {
\r
1728 /* Set up I/O redirection. */
\r
1729 if (get_output_handles(service, &si)) {
\r
1730 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_OUTPUT_HANDLES_FAILED, service->name, 0);
\r
1731 if (! service->no_console) FreeConsole();
\r
1732 close_output_handles(&si);
\r
1733 unset_service_environment(service);
\r
1734 return stop_service(service, 4, true, true);
\r
1737 bool inherit_handles = false;
\r
1738 if (si.dwFlags & STARTF_USESTDHANDLES) inherit_handles = true;
\r
1739 unsigned long flags = service->priority & priority_mask();
\r
1740 if (service->affinity) flags |= CREATE_SUSPENDED;
\r
1741 if (! CreateProcess(0, cmd, 0, 0, inherit_handles, flags, 0, service->dir, &si, &pi)) {
\r
1742 unsigned long exitcode = 3;
\r
1743 unsigned long error = GetLastError();
\r
1744 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service->name, service->exe, error_string(error), 0);
\r
1745 close_output_handles(&si);
\r
1746 unset_service_environment(service);
\r
1747 return stop_service(service, exitcode, true, true);
\r
1749 service->start_count++;
\r
1750 service->process_handle = pi.hProcess;
\r
1751 service->pid = pi.dwProcessId;
\r
1753 if (get_process_creation_time(service->process_handle, &service->creation_time)) ZeroMemory(&service->creation_time, sizeof(service->creation_time));
\r
1755 close_output_handles(&si);
\r
1757 if (! service->no_console) FreeConsole();
\r
1759 if (service->affinity) {
\r
1761 We are explicitly storing service->affinity as a 64-bit unsigned integer
\r
1762 so that we can parse it regardless of whether we're running in 32-bit
\r
1763 or 64-bit mode. The arguments to SetProcessAffinityMask(), however, are
\r
1764 defined as type DWORD_PTR and hence limited to 32 bits on a 32-bit system
\r
1765 (or when running the 32-bit NSSM).
\r
1767 The result is a lot of seemingly-unnecessary casting throughout the code
\r
1768 and potentially confusion when we actually try to start the service.
\r
1769 Having said that, however, it's unlikely that we're actually going to
\r
1770 run in 32-bit mode on a system which has more than 32 CPUs so the
\r
1771 likelihood of seeing a confusing situation is somewhat diminished.
\r
1773 DWORD_PTR affinity, system_affinity;
\r
1775 if (GetProcessAffinityMask(service->process_handle, &affinity, &system_affinity)) affinity = service->affinity & system_affinity;
\r
1777 affinity = (DWORD_PTR) service->affinity;
\r
1778 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GETPROCESSAFFINITYMASK_FAILED, service->name, error_string(GetLastError()), 0);
\r
1781 if (! SetProcessAffinityMask(service->process_handle, affinity)) {
\r
1782 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_SETPROCESSAFFINITYMASK_FAILED, service->name, error_string(GetLastError()), 0);
\r
1785 ResumeThread(pi.hThread);
\r
1789 /* Restore our environment. */
\r
1790 unset_service_environment(service);
\r
1793 Wait for a clean startup before changing the service status to RUNNING
\r
1794 but be mindful of the fact that we are blocking the service control manager
\r
1795 so abandon the wait before too much time has elapsed.
\r
1797 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
1799 /* Did another thread receive a stop control? */
\r
1800 if (! service->allow_restart) return 0;
\r
1802 /* Signal successful start */
\r
1803 service->status.dwCurrentState = SERVICE_RUNNING;
\r
1804 service->status.dwControlsAccepted &= ~SERVICE_ACCEPT_PAUSE_CONTINUE;
\r
1805 SetServiceStatus(service->status_handle, &service->status);
\r
1807 /* Post-start hook. */
\r
1808 if (! service->throttle) {
\r
1809 (void) nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_START, NSSM_HOOK_ACTION_POST, &control);
\r
1812 /* Ensure the restart delay is always applied. */
\r
1813 if (service->restart_delay && ! service->throttle) service->throttle++;
\r
1818 /* Stop the service */
\r
1819 int stop_service(nssm_service_t *service, unsigned long exitcode, bool graceful, bool default_action) {
\r
1820 service->allow_restart = false;
\r
1821 if (service->wait_handle) {
\r
1822 UnregisterWait(service->wait_handle);
\r
1823 service->wait_handle = 0;
\r
1826 service->rotate_stdout_online = service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;
\r
1828 if (default_action && ! exitcode && ! graceful) {
\r
1829 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
1833 /* Signal we are stopping */
\r
1835 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
1836 service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;
\r
1837 SetServiceStatus(service->status_handle, &service->status);
\r
1840 /* Nothing to do if service isn't running */
\r
1841 if (service->pid) {
\r
1842 /* Shut down service */
\r
1843 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_TERMINATEPROCESS, service->name, service->exe, 0);
\r
1845 service_kill_t(service, &k);
\r
1849 else log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_PROCESS_ALREADY_STOPPED, service->name, service->exe, 0);
\r
1851 end_service((void *) service, true);
\r
1853 /* Signal we stopped */
\r
1855 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
1856 wait_for_hooks(service, true);
\r
1857 service->status.dwCurrentState = SERVICE_STOPPED;
\r
1859 service->status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
\r
1860 service->status.dwServiceSpecificExitCode = exitcode;
\r
1863 service->status.dwWin32ExitCode = NO_ERROR;
\r
1864 service->status.dwServiceSpecificExitCode = 0;
\r
1866 SetServiceStatus(service->status_handle, &service->status);
\r
1872 /* Callback function triggered when the server exits */
\r
1873 void CALLBACK end_service(void *arg, unsigned char why) {
\r
1874 nssm_service_t *service = (nssm_service_t *) arg;
\r
1876 if (service->stopping) return;
\r
1878 service->stopping = true;
\r
1880 service->rotate_stdout_online = service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;
\r
1882 /* Use now as a dummy exit time. */
\r
1883 GetSystemTimeAsFileTime(&service->exit_time);
\r
1885 /* Check exit code */
\r
1886 unsigned long exitcode = 0;
\r
1888 if (service->process_handle) {
\r
1889 GetExitCodeProcess(service->process_handle, &exitcode);
\r
1890 service->exitcode = exitcode;
\r
1891 /* Check real exit time. */
\r
1892 if (exitcode != STILL_ACTIVE) get_process_exit_time(service->process_handle, &service->exit_time);
\r
1893 CloseHandle(service->process_handle);
\r
1896 service->process_handle = 0;
\r
1899 Log that the service ended BEFORE logging about killing the process
\r
1900 tree. See below for the possible values of the why argument.
\r
1903 _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), exitcode);
\r
1904 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ENDED_SERVICE, service->exe, service->name, code, 0);
\r
1908 if (exitcode == STILL_ACTIVE) exitcode = 0;
\r
1909 if (service->pid && service->kill_process_tree) {
\r
1911 service_kill_t(service, &k);
\r
1912 kill_process_tree(&k, service->pid);
\r
1917 service->exit_count++;
\r
1918 (void) nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_EXIT, NSSM_HOOK_ACTION_POST, NULL, NSSM_HOOK_DEADLINE, true);
\r
1921 The why argument is true if our wait timed out or false otherwise.
\r
1922 Our wait is infinite so why will never be true when called by the system.
\r
1923 If it is indeed true, assume we were called from stop_service() because
\r
1924 this is a controlled shutdown, and don't take any restart action.
\r
1927 if (! service->allow_restart) return;
\r
1929 /* What action should we take? */
\r
1930 int action = NSSM_EXIT_RESTART;
\r
1931 TCHAR action_string[ACTION_LEN];
\r
1932 bool default_action;
\r
1933 if (! get_exit_action(service->name, &exitcode, action_string, &default_action)) {
\r
1934 for (int i = 0; exit_action_strings[i]; i++) {
\r
1935 if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
1943 /* Try to restart the service or return failure code to service manager */
\r
1944 case NSSM_EXIT_RESTART:
\r
1945 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_RESTART, service->name, code, exit_action_strings[action], service->exe, 0);
\r
1946 while (monitor_service(service)) {
\r
1947 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_RESTART_SERVICE_FAILED, service->exe, service->name, 0);
\r
1952 /* Do nothing, just like srvany would */
\r
1953 case NSSM_EXIT_IGNORE:
\r
1954 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_IGNORE, service->name, code, exit_action_strings[action], service->exe, 0);
\r
1955 wait_for_hooks(service, false);
\r
1959 /* Tell the service manager we are finished */
\r
1960 case NSSM_EXIT_REALLY:
\r
1961 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_REALLY, service->name, code, exit_action_strings[action], 0);
\r
1962 stop_service(service, exitcode, true, default_action);
\r
1965 /* Fake a crash so pre-Vista service managers will run recovery actions. */
\r
1966 case NSSM_EXIT_UNCLEAN:
\r
1967 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_UNCLEAN, service->name, code, exit_action_strings[action], 0);
\r
1968 stop_service(service, exitcode, false, default_action);
\r
1969 wait_for_hooks(service, false);
\r
1976 void throttle_restart(nssm_service_t *service) {
\r
1977 /* This can't be a restart if the service is already running. */
\r
1978 if (! service->throttle++) return;
\r
1981 unsigned long throttle_ms = throttle_milliseconds(service->throttle);
\r
1982 TCHAR threshold[8], milliseconds[8];
\r
1984 if (service->restart_delay > throttle_ms) ms = service->restart_delay;
\r
1985 else ms = throttle_ms;
\r
1987 _sntprintf_s(milliseconds, _countof(milliseconds), _TRUNCATE, _T("%lu"), ms);
\r
1989 if (service->throttle == 1 && service->restart_delay > throttle_ms) log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESTART_DELAY, service->name, milliseconds, 0);
\r
1991 _sntprintf_s(threshold, _countof(threshold), _TRUNCATE, _T("%lu"), service->throttle_delay);
\r
1992 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_THROTTLED, service->name, threshold, milliseconds, 0);
\r
1995 if (use_critical_section) EnterCriticalSection(&service->throttle_section);
\r
1996 else if (service->throttle_timer) {
\r
1997 ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));
\r
1998 service->throttle_duetime.QuadPart = 0 - (ms * 10000LL);
\r
1999 SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);
\r
2002 service->status.dwCurrentState = SERVICE_PAUSED;
\r
2003 service->status.dwControlsAccepted |= SERVICE_ACCEPT_PAUSE_CONTINUE;
\r
2004 SetServiceStatus(service->status_handle, &service->status);
\r
2006 if (use_critical_section) {
\r
2007 imports.SleepConditionVariableCS(&service->throttle_condition, &service->throttle_section, ms);
\r
2008 LeaveCriticalSection(&service->throttle_section);
\r
2011 if (service->throttle_timer) WaitForSingleObject(service->throttle_timer, INFINITE);
\r
2017 When responding to a stop (or any other) request we need to set dwWaitHint to
\r
2018 the number of milliseconds we expect the operation to take, and optionally
\r
2019 increase dwCheckPoint. If dwWaitHint milliseconds elapses without the
\r
2020 operation completing or dwCheckPoint increasing, the system will consider the
\r
2021 service to be hung.
\r
2023 However the system will consider the service to be hung after 30000
\r
2024 milliseconds regardless of the value of dwWaitHint if dwCheckPoint has not
\r
2025 changed. Therefore if we want to wait longer than that we must periodically
\r
2026 increase dwCheckPoint.
\r
2028 Furthermore, it will consider the service to be hung after 60000 milliseconds
\r
2029 regardless of the value of dwCheckPoint unless dwWaitHint is increased every
\r
2030 time dwCheckPoint is also increased.
\r
2032 Our strategy then is to retrieve the initial dwWaitHint and wait for
\r
2033 NSSM_SERVICE_STATUS_DEADLINE milliseconds. If the process is still running
\r
2034 and we haven't finished waiting we increment dwCheckPoint and add whichever is
\r
2035 smaller of NSSM_SERVICE_STATUS_DEADLINE or the remaining timeout to
\r
2038 Only doing both these things will prevent the system from killing the service.
\r
2040 If the status_handle and service_status arguments are omitted, this function
\r
2041 will not try to update the service manager but it will still log to the
\r
2042 event log that it is waiting for a handle.
\r
2044 Returns: 1 if the wait timed out.
\r
2045 0 if the wait completed.
\r
2048 int await_single_handle(SERVICE_STATUS_HANDLE status_handle, SERVICE_STATUS *status, HANDLE handle, TCHAR *name, TCHAR *function_name, unsigned long timeout) {
\r
2049 unsigned long interval;
\r
2050 unsigned long ret;
\r
2051 unsigned long waited;
\r
2052 TCHAR interval_milliseconds[16];
\r
2053 TCHAR timeout_milliseconds[16];
\r
2054 TCHAR waited_milliseconds[16];
\r
2055 TCHAR *function = function_name;
\r
2057 /* Add brackets to function name. */
\r
2058 size_t funclen = _tcslen(function_name) + 3;
\r
2059 TCHAR *func = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, funclen * sizeof(TCHAR));
\r
2061 if (_sntprintf_s(func, funclen, _TRUNCATE, _T("%s()"), function_name) > -1) function = func;
\r
2064 _sntprintf_s(timeout_milliseconds, _countof(timeout_milliseconds), _TRUNCATE, _T("%lu"), timeout);
\r
2067 while (waited < timeout) {
\r
2068 interval = timeout - waited;
\r
2069 if (interval > NSSM_SERVICE_STATUS_DEADLINE) interval = NSSM_SERVICE_STATUS_DEADLINE;
\r
2072 status->dwWaitHint += interval;
\r
2073 status->dwCheckPoint++;
\r
2074 SetServiceStatus(status_handle, status);
\r
2078 _sntprintf_s(waited_milliseconds, _countof(waited_milliseconds), _TRUNCATE, _T("%lu"), waited);
\r
2079 _sntprintf_s(interval_milliseconds, _countof(interval_milliseconds), _TRUNCATE, _T("%lu"), interval);
\r
2080 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_AWAITING_SINGLE_HANDLE, function, name, waited_milliseconds, interval_milliseconds, timeout_milliseconds, 0);
\r
2083 switch (WaitForSingleObject(handle, interval)) {
\r
2084 case WAIT_OBJECT_0:
\r
2088 case WAIT_TIMEOUT:
\r
2097 waited += interval;
\r
2101 if (func) HeapFree(GetProcessHeap(), 0, func);
\r