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 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
280 We have to duplicate the block because this function will be called
\r
281 multiple times between registry reads.
\r
283 if (service->env) duplicate_environment_strings(service->env);
\r
284 if (! service->env_extra) return;
\r
285 TCHAR *env_extra = copy_environment_block(service->env_extra);
\r
286 if (! env_extra) return;
\r
288 set_environment_block(env_extra);
\r
289 HeapFree(GetProcessHeap(), 0, env_extra);
\r
292 void unset_service_environment(nssm_service_t *service) {
\r
293 if (! service) return;
\r
294 duplicate_environment_strings(service->initial_env);
\r
298 Wrapper to be called in a new thread so that we can acknowledge a STOP
\r
299 control immediately.
\r
301 static unsigned long WINAPI shutdown_service(void *arg) {
\r
302 return stop_service((nssm_service_t *) arg, 0, true, true);
\r
306 Wrapper to be called in a new thread so that we can acknowledge start
\r
309 static unsigned long WINAPI launch_service(void *arg) {
\r
310 return monitor_service((nssm_service_t *) arg);
\r
313 /* Connect to the service manager */
\r
314 SC_HANDLE open_service_manager(unsigned long access) {
\r
315 SC_HANDLE ret = OpenSCManager(0, SERVICES_ACTIVE_DATABASE, access);
\r
317 if (is_admin) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENSCMANAGER_FAILED, 0);
\r
324 /* Open a service by name or display name. */
\r
325 SC_HANDLE open_service(SC_HANDLE services, TCHAR *service_name, unsigned long access, TCHAR *canonical_name, unsigned long canonical_namelen) {
\r
326 SC_HANDLE service_handle = OpenService(services, service_name, access);
\r
327 if (service_handle) {
\r
328 if (canonical_name && canonical_name != service_name) {
\r
329 TCHAR displayname[SERVICE_NAME_LENGTH];
\r
330 unsigned long displayname_len = (unsigned long) _countof(displayname);
\r
331 GetServiceDisplayName(services, service_name, displayname, &displayname_len);
\r
332 unsigned long keyname_len = canonical_namelen;
\r
333 GetServiceKeyName(services, displayname, canonical_name, &keyname_len);
\r
335 return service_handle;
\r
338 unsigned long error = GetLastError();
\r
339 if (error != ERROR_SERVICE_DOES_NOT_EXIST) {
\r
340 print_message(stderr, NSSM_MESSAGE_OPENSERVICE_FAILED, error_string(GetLastError()));
\r
344 /* We can't look for a display name because there's no buffer to store it. */
\r
345 if (! canonical_name) {
\r
346 print_message(stderr, NSSM_MESSAGE_OPENSERVICE_FAILED, error_string(GetLastError()));
\r
350 unsigned long bufsize, required, count, i;
\r
351 unsigned long resume = 0;
\r
352 EnumServicesStatus(services, SERVICE_DRIVER | SERVICE_FILE_SYSTEM_DRIVER | SERVICE_KERNEL_DRIVER | SERVICE_WIN32, SERVICE_STATE_ALL, 0, 0, &required, &count, &resume);
\r
353 error = GetLastError();
\r
354 if (error != ERROR_MORE_DATA) {
\r
355 print_message(stderr, NSSM_MESSAGE_ENUMSERVICESSTATUS_FAILED, error_string(GetLastError()));
\r
359 ENUM_SERVICE_STATUS *status = (ENUM_SERVICE_STATUS *) HeapAlloc(GetProcessHeap(), 0, required);
\r
361 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("ENUM_SERVICE_STATUS"), _T("open_service()"));
\r
365 bufsize = required;
\r
368 EnumServicesStatus() returns:
\r
369 1 when it retrieved data and there's no more data to come.
\r
370 0 and sets last error to ERROR_MORE_DATA when it retrieved data and
\r
371 there's more data to come.
\r
372 0 and sets last error to something else on error.
\r
374 int ret = EnumServicesStatus(services, SERVICE_DRIVER | SERVICE_FILE_SYSTEM_DRIVER | SERVICE_KERNEL_DRIVER | SERVICE_WIN32, SERVICE_STATE_ALL, status, bufsize, &required, &count, &resume);
\r
376 error = GetLastError();
\r
377 if (error != ERROR_MORE_DATA) {
\r
378 HeapFree(GetProcessHeap(), 0, status);
\r
379 print_message(stderr, NSSM_MESSAGE_ENUMSERVICESSTATUS_FAILED, error_string(GetLastError()));
\r
384 for (i = 0; i < count; i++) {
\r
385 if (str_equiv(status[i].lpDisplayName, service_name)) {
\r
386 if (_sntprintf_s(canonical_name, canonical_namelen, _TRUNCATE, _T("%s"), status[i].lpServiceName) < 0) {
\r
387 HeapFree(GetProcessHeap(), 0, status);
\r
388 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("canonical_name"), _T("open_service()"));
\r
392 HeapFree(GetProcessHeap(), 0, status);
\r
393 return open_service(services, canonical_name, access, 0, 0);
\r
400 /* Recurse so we can get an error message. */
\r
401 return open_service(services, service_name, access, 0, 0);
\r
404 QUERY_SERVICE_CONFIG *query_service_config(const TCHAR *service_name, SC_HANDLE service_handle) {
\r
405 QUERY_SERVICE_CONFIG *qsc;
\r
406 unsigned long bufsize;
\r
407 unsigned long error;
\r
409 QueryServiceConfig(service_handle, 0, 0, &bufsize);
\r
410 error = GetLastError();
\r
411 if (error == ERROR_INSUFFICIENT_BUFFER) {
\r
412 qsc = (QUERY_SERVICE_CONFIG *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, bufsize);
\r
414 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("QUERY_SERVICE_CONFIG"), _T("query_service_config()"), 0);
\r
419 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG_FAILED, service_name, error_string(error), 0);
\r
423 if (! QueryServiceConfig(service_handle, qsc, bufsize, &bufsize)) {
\r
424 HeapFree(GetProcessHeap(), 0, qsc);
\r
425 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG_FAILED, service_name, error_string(GetLastError()), 0);
\r
432 int set_service_dependencies(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR *buffer) {
\r
433 TCHAR *dependencies = _T("");
\r
434 unsigned long num_dependencies = 0;
\r
436 if (buffer && buffer[0]) {
\r
437 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
\r
439 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
444 Count the dependencies then allocate a buffer big enough for their
\r
445 canonical names, ie n * SERVICE_NAME_LENGTH.
\r
449 for (s = buffer; *s; s++) {
\r
450 num_dependencies++;
\r
451 if (*s == SC_GROUP_IDENTIFIER) groups = s;
\r
455 /* At least one dependency is a group so we need to verify them. */
\r
458 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, NSSM_REGISTRY_GROUPS, 0, KEY_READ, &key)) {
\r
459 _ftprintf(stderr, _T("%s: %s\n"), NSSM_REGISTRY_GROUPS, error_string(GetLastError()));
\r
463 unsigned long type;
\r
464 unsigned long groupslen;
\r
465 unsigned long ret = RegQueryValueEx(key, NSSM_REG_GROUPS, 0, &type, NULL, &groupslen);
\r
466 if (ret == ERROR_SUCCESS) {
\r
467 groups = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, groupslen);
\r
469 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("groups"), _T("set_service_dependencies()"));
\r
473 ret = RegQueryValueEx(key, NSSM_REG_GROUPS, 0, &type, (unsigned char *) groups, &groupslen);
\r
474 if (ret != ERROR_SUCCESS) {
\r
475 _ftprintf(stderr, _T("%s\\%s: %s"), NSSM_REGISTRY_GROUPS, NSSM_REG_GROUPS, error_string(GetLastError()));
\r
476 HeapFree(GetProcessHeap(), 0, groups);
\r
481 else if (ret != ERROR_FILE_NOT_FOUND) {
\r
482 _ftprintf(stderr, _T("%s\\%s: %s"), NSSM_REGISTRY_GROUPS, NSSM_REG_GROUPS, error_string(GetLastError()));
\r
491 unsigned long dependencieslen = (num_dependencies * SERVICE_NAME_LENGTH) + 2;
\r
492 dependencies = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dependencieslen * sizeof(TCHAR));
\r
495 TCHAR dependency[SERVICE_NAME_LENGTH];
\r
496 for (s = buffer; *s; s++) {
\r
498 if (*s == SC_GROUP_IDENTIFIER) {
\r
499 TCHAR *group = s + 1;
\r
503 for (TCHAR *g = groups; *g; g++) {
\r
504 if (str_equiv(g, group)) {
\r
506 /* Set canonical name. */
\r
507 memmove(group, g, _tcslen(g) * sizeof(TCHAR));
\r
515 if (ok) _sntprintf_s(dependency, _countof(dependency), _TRUNCATE, _T("%s"), s);
\r
517 HeapFree(GetProcessHeap(), 0, dependencies);
\r
518 if (groups) HeapFree(GetProcessHeap(), 0, groups);
\r
519 _ftprintf(stderr, _T("%s: %s"), s, error_string(ERROR_SERVICE_DEPENDENCY_DELETED));
\r
524 SC_HANDLE dependency_handle = open_service(services, s, SERVICE_QUERY_STATUS, dependency, _countof(dependency));
\r
525 if (! dependency_handle) {
\r
526 HeapFree(GetProcessHeap(), 0, dependencies);
\r
527 if (groups) HeapFree(GetProcessHeap(), 0, groups);
\r
528 CloseServiceHandle(services);
\r
529 _ftprintf(stderr, _T("%s: %s"), s, error_string(ERROR_SERVICE_DEPENDENCY_DELETED));
\r
534 size_t len = _tcslen(dependency) + 1;
\r
535 memmove(dependencies + i, dependency, len * sizeof(TCHAR));
\r
541 if (groups) HeapFree(GetProcessHeap(), 0, groups);
\r
542 CloseServiceHandle(services);
\r
545 if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, dependencies, 0, 0, 0)) {
\r
546 if (num_dependencies) HeapFree(GetProcessHeap(), 0, dependencies);
\r
547 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
551 if (num_dependencies) HeapFree(GetProcessHeap(), 0, dependencies);
\r
555 int get_service_dependencies(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR **buffer, unsigned long *bufsize, int type) {
\r
556 if (! buffer) return 1;
\r
557 if (! bufsize) return 2;
\r
562 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
563 if (! qsc) return 3;
\r
565 if (! qsc->lpDependencies) return 0;
\r
566 if (! qsc->lpDependencies[0]) return 0;
\r
568 /* lpDependencies is doubly NULL terminated. */
\r
569 while (qsc->lpDependencies[*bufsize]) {
\r
570 while (qsc->lpDependencies[*bufsize]) ++*bufsize;
\r
576 *buffer = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *bufsize * sizeof(TCHAR));
\r
579 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("lpDependencies"), _T("get_service_dependencies()"));
\r
583 if (type == DEPENDENCY_ALL) memmove(*buffer, qsc->lpDependencies, *bufsize * sizeof(TCHAR));
\r
588 for (s = qsc->lpDependencies; *s; s++) {
\r
589 /* Only copy the appropriate type of dependency. */
\r
590 if ((*s == SC_GROUP_IDENTIFIER && type & DEPENDENCY_GROUPS) || (*s != SC_GROUP_IDENTIFIER && type & DEPENDENCY_SERVICES)) {
\r
591 size_t len = _tcslen(s) + 1;
\r
592 *bufsize += (unsigned long) len;
\r
593 memmove(*buffer + i, s, len * sizeof(TCHAR));
\r
602 HeapFree(GetProcessHeap(), 0, qsc);
\r
607 int get_service_dependencies(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR **buffer, unsigned long *bufsize) {
\r
608 return get_service_dependencies(service_name, service_handle, buffer, bufsize, DEPENDENCY_ALL);
\r
611 int set_service_description(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR *buffer) {
\r
612 SERVICE_DESCRIPTION description;
\r
613 ZeroMemory(&description, sizeof(description));
\r
615 lpDescription must be NULL if we aren't changing, the new description
\r
618 if (buffer && buffer[0]) description.lpDescription = buffer;
\r
619 else description.lpDescription = _T("");
\r
621 if (ChangeServiceConfig2(service_handle, SERVICE_CONFIG_DESCRIPTION, &description)) return 0;
\r
623 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_DESCRIPTION_FAILED, service_name, error_string(GetLastError()), 0);
\r
627 int get_service_description(const TCHAR *service_name, SC_HANDLE service_handle, unsigned long len, TCHAR *buffer) {
\r
628 if (! buffer) return 1;
\r
630 unsigned long bufsize;
\r
631 QueryServiceConfig2(service_handle, SERVICE_CONFIG_DESCRIPTION, 0, 0, &bufsize);
\r
632 unsigned long error = GetLastError();
\r
633 if (error == ERROR_INSUFFICIENT_BUFFER) {
\r
634 SERVICE_DESCRIPTION *description = (SERVICE_DESCRIPTION *) HeapAlloc(GetProcessHeap(), 0, bufsize);
\r
635 if (! description) {
\r
636 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("SERVICE_CONFIG_DESCRIPTION"), _T("get_service_description()"));
\r
640 if (QueryServiceConfig2(service_handle, SERVICE_CONFIG_DESCRIPTION, (unsigned char *) description, bufsize, &bufsize)) {
\r
641 if (description->lpDescription) _sntprintf_s(buffer, len, _TRUNCATE, _T("%s"), description->lpDescription);
\r
642 else ZeroMemory(buffer, len * sizeof(TCHAR));
\r
643 HeapFree(GetProcessHeap(), 0, description);
\r
647 HeapFree(GetProcessHeap(), 0, description);
\r
648 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_CONFIG_DESCRIPTION"), error_string(error));
\r
653 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_CONFIG_DESCRIPTION"), error_string(error));
\r
658 int get_service_startup(const TCHAR *service_name, SC_HANDLE service_handle, const QUERY_SERVICE_CONFIG *qsc, unsigned long *startup) {
\r
659 if (! qsc) return 1;
\r
661 switch (qsc->dwStartType) {
\r
662 case SERVICE_DEMAND_START: *startup = NSSM_STARTUP_MANUAL; break;
\r
663 case SERVICE_DISABLED: *startup = NSSM_STARTUP_DISABLED; break;
\r
664 default: *startup = NSSM_STARTUP_AUTOMATIC;
\r
667 if (*startup != NSSM_STARTUP_AUTOMATIC) return 0;
\r
669 /* Check for delayed start. */
\r
670 unsigned long bufsize;
\r
671 unsigned long error;
\r
672 QueryServiceConfig2(service_handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, 0, 0, &bufsize);
\r
673 error = GetLastError();
\r
674 if (error == ERROR_INSUFFICIENT_BUFFER) {
\r
675 SERVICE_DELAYED_AUTO_START_INFO *info = (SERVICE_DELAYED_AUTO_START_INFO *) HeapAlloc(GetProcessHeap(), 0, bufsize);
\r
677 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("SERVICE_DELAYED_AUTO_START_INFO"), _T("get_service_startup()"));
\r
681 if (QueryServiceConfig2(service_handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, (unsigned char *) info, bufsize, &bufsize)) {
\r
682 if (info->fDelayedAutostart) *startup = NSSM_STARTUP_DELAYED;
\r
683 HeapFree(GetProcessHeap(), 0, info);
\r
687 error = GetLastError();
\r
688 if (error != ERROR_INVALID_LEVEL) {
\r
689 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_CONFIG_DELAYED_AUTO_START_INFO"), error_string(error));
\r
694 else if (error != ERROR_INVALID_LEVEL) {
\r
695 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_DELAYED_AUTO_START_INFO"), error_string(error));
\r
702 int get_service_username(const TCHAR *service_name, const QUERY_SERVICE_CONFIG *qsc, TCHAR **username, size_t *usernamelen) {
\r
703 if (! username) return 1;
\r
704 if (! usernamelen) return 1;
\r
709 if (! qsc) return 1;
\r
711 if (qsc->lpServiceStartName[0]) {
\r
712 if (is_localsystem(qsc->lpServiceStartName)) return 0;
\r
714 size_t len = _tcslen(qsc->lpServiceStartName);
\r
715 *username = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(TCHAR));
\r
717 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("username"), _T("get_service_username()"));
\r
721 memmove(*username, qsc->lpServiceStartName, (len + 1) * sizeof(TCHAR));
\r
722 *usernamelen = len;
\r
728 /* Set default values which aren't zero. */
\r
729 void set_nssm_service_defaults(nssm_service_t *service) {
\r
730 if (! service) return;
\r
732 service->type = SERVICE_WIN32_OWN_PROCESS;
\r
733 service->priority = NORMAL_PRIORITY_CLASS;
\r
734 service->stdin_sharing = NSSM_STDIN_SHARING;
\r
735 service->stdin_disposition = NSSM_STDIN_DISPOSITION;
\r
736 service->stdin_flags = NSSM_STDIN_FLAGS;
\r
737 service->stdout_sharing = NSSM_STDOUT_SHARING;
\r
738 service->stdout_disposition = NSSM_STDOUT_DISPOSITION;
\r
739 service->stdout_flags = NSSM_STDOUT_FLAGS;
\r
740 service->stderr_sharing = NSSM_STDERR_SHARING;
\r
741 service->stderr_disposition = NSSM_STDERR_DISPOSITION;
\r
742 service->stderr_flags = NSSM_STDERR_FLAGS;
\r
743 service->throttle_delay = NSSM_RESET_THROTTLE_RESTART;
\r
744 service->stop_method = ~0;
\r
745 service->kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;
\r
746 service->kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;
\r
747 service->kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;
\r
748 service->kill_process_tree = 1;
\r
751 /* Allocate and zero memory for a service. */
\r
752 nssm_service_t *alloc_nssm_service() {
\r
753 nssm_service_t *service = (nssm_service_t *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(nssm_service_t));
\r
754 if (! service) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("service"), _T("alloc_nssm_service()"), 0);
\r
758 /* Free memory for a service. */
\r
759 void cleanup_nssm_service(nssm_service_t *service) {
\r
760 if (! service) return;
\r
761 if (service->username) HeapFree(GetProcessHeap(), 0, service->username);
\r
762 if (service->password) {
\r
763 SecureZeroMemory(service->password, service->passwordlen * sizeof(TCHAR));
\r
764 HeapFree(GetProcessHeap(), 0, service->password);
\r
766 if (service->dependencies) HeapFree(GetProcessHeap(), 0, service->dependencies);
\r
767 if (service->env) HeapFree(GetProcessHeap(), 0, service->env);
\r
768 if (service->env_extra) HeapFree(GetProcessHeap(), 0, service->env_extra);
\r
769 if (service->handle) CloseServiceHandle(service->handle);
\r
770 if (service->process_handle) CloseHandle(service->process_handle);
\r
771 if (service->wait_handle) UnregisterWait(service->wait_handle);
\r
772 if (service->throttle_section_initialised) DeleteCriticalSection(&service->throttle_section);
\r
773 if (service->throttle_timer) CloseHandle(service->throttle_timer);
\r
774 if (service->hook_section_initialised) DeleteCriticalSection(&service->hook_section);
\r
775 if (service->initial_env) HeapFree(GetProcessHeap(), 0, service->initial_env);
\r
776 HeapFree(GetProcessHeap(), 0, service);
\r
779 /* About to install the service */
\r
780 int pre_install_service(int argc, TCHAR **argv) {
\r
781 nssm_service_t *service = alloc_nssm_service();
\r
782 set_nssm_service_defaults(service);
\r
783 if (argc) _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]);
\r
785 /* Show the dialogue box if we didn't give the service name and path */
\r
786 if (argc < 2) return nssm_gui(IDD_INSTALL, service);
\r
789 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("service"), _T("pre_install_service()"));
\r
792 _sntprintf_s(service->exe, _countof(service->exe), _TRUNCATE, _T("%s"), argv[1]);
\r
794 /* Arguments are optional */
\r
795 size_t flagslen = 0;
\r
798 for (i = 2; i < argc; i++) flagslen += _tcslen(argv[i]) + 1;
\r
799 if (! flagslen) flagslen = 1;
\r
800 if (flagslen > _countof(service->flags)) {
\r
801 print_message(stderr, NSSM_MESSAGE_FLAGS_TOO_LONG);
\r
805 for (i = 2; i < argc; i++) {
\r
806 size_t len = _tcslen(argv[i]);
\r
807 memmove(service->flags + s, argv[i], len * sizeof(TCHAR));
\r
809 if (i < argc - 1) service->flags[s++] = _T(' ');
\r
812 /* Work out directory name */
\r
813 _sntprintf_s(service->dir, _countof(service->dir), _TRUNCATE, _T("%s"), service->exe);
\r
814 strip_basename(service->dir);
\r
816 int ret = install_service(service);
\r
817 cleanup_nssm_service(service);
\r
821 /* About to edit the service. */
\r
822 int pre_edit_service(int argc, TCHAR **argv) {
\r
823 /* Require service name. */
\r
824 if (argc < 2) return usage(1);
\r
826 /* Are we editing on the command line? */
\r
827 enum { MODE_EDITING, MODE_GETTING, MODE_SETTING, MODE_RESETTING } mode = MODE_EDITING;
\r
828 const TCHAR *verb = argv[0];
\r
829 const TCHAR *service_name = argv[1];
\r
830 bool getting = false;
\r
831 bool unsetting = false;
\r
833 /* Minimum number of arguments. */
\r
835 /* Index of first value. */
\r
838 if (str_equiv(verb, _T("get"))) {
\r
840 mode = MODE_GETTING;
\r
842 else if (str_equiv(verb, _T("set"))) {
\r
844 mode = MODE_SETTING;
\r
846 else if (str_equiv(verb, _T("reset")) || str_equiv(verb, _T("unset"))) {
\r
848 mode = MODE_RESETTING;
\r
850 if (argc < mandatory) return usage(1);
\r
852 const TCHAR *parameter = 0;
\r
853 settings_t *setting = 0;
\r
856 /* Validate the parameter. */
\r
857 if (mandatory > 2) {
\r
858 bool additional_mandatory = false;
\r
860 parameter = argv[2];
\r
861 for (i = 0; settings[i].name; i++) {
\r
862 setting = &settings[i];
\r
863 if (! str_equiv(setting->name, parameter)) continue;
\r
864 if (((setting->additional & ADDITIONAL_GETTING) && mode == MODE_GETTING) || ((setting->additional & ADDITIONAL_SETTING) && mode == MODE_SETTING) || ((setting->additional & ADDITIONAL_RESETTING) && mode == MODE_RESETTING)) {
\r
865 additional_mandatory = true;
\r
870 if (! settings[i].name) {
\r
871 print_message(stderr, NSSM_MESSAGE_INVALID_PARAMETER, parameter);
\r
872 for (i = 0; settings[i].name; i++) _ftprintf(stderr, _T("%s\n"), settings[i].name);
\r
877 if (additional_mandatory) {
\r
878 if (argc < mandatory) {
\r
879 print_message(stderr, NSSM_MESSAGE_MISSING_SUBPARAMETER, parameter);
\r
882 additional = argv[3];
\r
885 else if (str_equiv(setting->name, NSSM_NATIVE_OBJECTNAME) && mode == MODE_SETTING) {
\r
886 additional = argv[3];
\r
890 additional = argv[remainder];
\r
891 if (argc < mandatory) return usage(1);
\r
895 nssm_service_t *service = alloc_nssm_service();
\r
896 _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), service_name);
\r
898 /* Open service manager */
\r
899 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
\r
901 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
905 /* Try to open the service */
\r
906 unsigned long access = SERVICE_QUERY_CONFIG;
\r
907 if (mode != MODE_GETTING) access |= SERVICE_CHANGE_CONFIG;
\r
908 service->handle = open_service(services, service->name, access, service->name, _countof(service->name));
\r
909 if (! service->handle) {
\r
910 CloseServiceHandle(services);
\r
914 /* Get system details. */
\r
915 QUERY_SERVICE_CONFIG *qsc = query_service_config(service->name, service->handle);
\r
917 CloseServiceHandle(service->handle);
\r
918 CloseServiceHandle(services);
\r
922 service->type = qsc->dwServiceType;
\r
923 if (! (service->type & SERVICE_WIN32_OWN_PROCESS)) {
\r
924 if (mode != MODE_GETTING) {
\r
925 HeapFree(GetProcessHeap(), 0, qsc);
\r
926 CloseServiceHandle(service->handle);
\r
927 CloseServiceHandle(services);
\r
928 print_message(stderr, NSSM_MESSAGE_CANNOT_EDIT, service->name, NSSM_WIN32_OWN_PROCESS, 0);
\r
933 if (get_service_startup(service->name, service->handle, qsc, &service->startup)) {
\r
934 if (mode != MODE_GETTING) {
\r
935 HeapFree(GetProcessHeap(), 0, qsc);
\r
936 CloseServiceHandle(service->handle);
\r
937 CloseServiceHandle(services);
\r
942 if (get_service_username(service->name, qsc, &service->username, &service->usernamelen)) {
\r
943 if (mode != MODE_GETTING) {
\r
944 HeapFree(GetProcessHeap(), 0, qsc);
\r
945 CloseServiceHandle(service->handle);
\r
946 CloseServiceHandle(services);
\r
951 _sntprintf_s(service->displayname, _countof(service->displayname), _TRUNCATE, _T("%s"), qsc->lpDisplayName);
\r
953 /* Get the canonical service name. We open it case insensitively. */
\r
954 unsigned long bufsize = _countof(service->name);
\r
955 GetServiceKeyName(services, service->displayname, service->name, &bufsize);
\r
957 /* Remember the executable in case it isn't NSSM. */
\r
958 _sntprintf_s(service->image, _countof(service->image), _TRUNCATE, _T("%s"), qsc->lpBinaryPathName);
\r
959 HeapFree(GetProcessHeap(), 0, qsc);
\r
961 /* Get extended system details. */
\r
962 if (get_service_description(service->name, service->handle, _countof(service->description), service->description)) {
\r
963 if (mode != MODE_GETTING) {
\r
964 CloseServiceHandle(service->handle);
\r
965 CloseServiceHandle(services);
\r
970 if (get_service_dependencies(service->name, service->handle, &service->dependencies, &service->dependencieslen)) {
\r
971 if (mode != MODE_GETTING) {
\r
972 CloseServiceHandle(service->handle);
\r
973 CloseServiceHandle(services);
\r
978 /* Get NSSM details. */
\r
979 get_parameters(service, 0);
\r
981 CloseServiceHandle(services);
\r
983 if (! service->exe[0]) {
\r
984 service->native = true;
\r
985 if (mode != MODE_GETTING) print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE, service->name, NSSM, service->image);
\r
988 /* Editing with the GUI. */
\r
989 if (mode == MODE_EDITING) {
\r
990 nssm_gui(IDD_EDIT, service);
\r
994 /* Trying to manage App* parameters for a non-NSSM service. */
\r
995 if (! setting->native && service->native) {
\r
996 CloseServiceHandle(service->handle);
\r
997 print_message(stderr, NSSM_MESSAGE_NATIVE_PARAMETER, setting->name, NSSM);
\r
1005 if (mode == MODE_GETTING) {
\r
1006 if (! service->native) {
\r
1007 key = open_registry(service->name, KEY_READ);
\r
1008 if (! key) return 4;
\r
1011 if (setting->native) ret = get_setting(service->name, service->handle, setting, &value, additional);
\r
1012 else ret = get_setting(service->name, key, setting, &value, additional);
\r
1014 CloseServiceHandle(service->handle);
\r
1018 switch (setting->type) {
\r
1019 case REG_EXPAND_SZ:
\r
1020 case REG_MULTI_SZ:
\r
1022 _tprintf(_T("%s\n"), value.string ? value.string : _T(""));
\r
1023 HeapFree(GetProcessHeap(), 0, value.string);
\r
1027 _tprintf(_T("%u\n"), value.numeric);
\r
1031 if (! service->native) RegCloseKey(key);
\r
1032 CloseServiceHandle(service->handle);
\r
1036 /* Build the value. */
\r
1037 if (mode == MODE_RESETTING) {
\r
1038 /* Unset the parameter. */
\r
1041 else if (remainder == argc) {
\r
1045 /* Set the parameter. */
\r
1047 size_t delimiterlen = (setting->additional & ADDITIONAL_CRLF) ? 2 : 1;
\r
1048 for (i = remainder; i < argc; i++) len += _tcslen(argv[i]) + delimiterlen;
\r
1051 value.string = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, len * sizeof(TCHAR));
\r
1052 if (! value.string) {
\r
1053 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("value"), _T("edit_service()"));
\r
1054 CloseServiceHandle(service->handle);
\r
1059 for (i = remainder; i < argc; i++) {
\r
1060 size_t len = _tcslen(argv[i]);
\r
1061 memmove(value.string + s, argv[i], len * sizeof(TCHAR));
\r
1063 if (i < argc - 1) {
\r
1064 if (setting->additional & ADDITIONAL_CRLF) {
\r
1065 value.string[s++] = _T('\r');
\r
1066 value.string[s++] = _T('\n');
\r
1068 else value.string[s++] = _T(' ');
\r
1071 value.string[s] = _T('\0');
\r
1074 if (! service->native) {
\r
1075 key = open_registry(service->name, KEY_WRITE);
\r
1077 if (value.string) HeapFree(GetProcessHeap(), 0, value.string);
\r
1082 if (setting->native) ret = set_setting(service->name, service->handle, setting, &value, additional);
\r
1083 else ret = set_setting(service->name, key, setting, &value, additional);
\r
1084 if (value.string) HeapFree(GetProcessHeap(), 0, value.string);
\r
1086 if (! service->native) RegCloseKey(key);
\r
1087 CloseServiceHandle(service->handle);
\r
1091 if (! service->native) RegCloseKey(key);
\r
1092 CloseServiceHandle(service->handle);
\r
1097 /* About to remove the service */
\r
1098 int pre_remove_service(int argc, TCHAR **argv) {
\r
1099 nssm_service_t *service = alloc_nssm_service();
\r
1100 set_nssm_service_defaults(service);
\r
1101 if (argc) _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]);
\r
1103 /* Show dialogue box if we didn't pass service name and "confirm" */
\r
1104 if (argc < 2) return nssm_gui(IDD_REMOVE, service);
\r
1105 if (str_equiv(argv[1], _T("confirm"))) {
\r
1106 int ret = remove_service(service);
\r
1107 cleanup_nssm_service(service);
\r
1110 print_message(stderr, NSSM_MESSAGE_PRE_REMOVE_SERVICE);
\r
1114 /* Install the service */
\r
1115 int install_service(nssm_service_t *service) {
\r
1116 if (! service) return 1;
\r
1118 /* Open service manager */
\r
1119 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE);
\r
1121 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
1122 cleanup_nssm_service(service);
\r
1126 /* Get path of this program */
\r
1127 _sntprintf_s(service->image, _countof(service->image), _TRUNCATE, _T("%s"), nssm_imagepath());
\r
1129 /* Create the service - settings will be changed in edit_service() */
\r
1130 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
1131 if (! service->handle) {
\r
1132 print_message(stderr, NSSM_MESSAGE_CREATESERVICE_FAILED, error_string(GetLastError()));
\r
1133 CloseServiceHandle(services);
\r
1137 if (edit_service(service, false)) {
\r
1138 DeleteService(service->handle);
\r
1139 CloseServiceHandle(services);
\r
1143 print_message(stdout, NSSM_MESSAGE_SERVICE_INSTALLED, service->name);
\r
1146 CloseServiceHandle(services);
\r
1151 /* Edit the service. */
\r
1152 int edit_service(nssm_service_t *service, bool editing) {
\r
1153 if (! service) return 1;
\r
1156 The only two valid flags for service type are SERVICE_WIN32_OWN_PROCESS
\r
1157 and SERVICE_INTERACTIVE_PROCESS.
\r
1159 service->type &= SERVICE_INTERACTIVE_PROCESS;
\r
1160 service->type |= SERVICE_WIN32_OWN_PROCESS;
\r
1162 /* Startup type. */
\r
1163 unsigned long startup;
\r
1164 switch (service->startup) {
\r
1165 case NSSM_STARTUP_MANUAL: startup = SERVICE_DEMAND_START; break;
\r
1166 case NSSM_STARTUP_DISABLED: startup = SERVICE_DISABLED; break;
\r
1167 default: startup = SERVICE_AUTO_START;
\r
1170 /* Display name. */
\r
1171 if (! service->displayname[0]) _sntprintf_s(service->displayname, _countof(service->displayname), _TRUNCATE, _T("%s"), service->name);
\r
1174 Username must be NULL if we aren't changing or an account name.
\r
1175 We must explicitly use LOCALSYSTEM to change it when we are editing.
\r
1176 Password must be NULL if we aren't changing, a password or "".
\r
1177 Empty passwords are valid but we won't allow them in the GUI.
\r
1179 TCHAR *username = 0;
\r
1181 TCHAR *password = 0;
\r
1182 if (service->usernamelen) {
\r
1183 username = service->username;
\r
1184 if (canonicalise_username(username, &canon)) return 5;
\r
1185 if (service->passwordlen) password = service->password;
\r
1187 else if (editing) username = canon = NSSM_LOCALSYSTEM_ACCOUNT;
\r
1189 if (well_known_username(canon)) password = _T("");
\r
1191 if (grant_logon_as_service(canon)) {
\r
1192 if (canon != username) HeapFree(GetProcessHeap(), 0, canon);
\r
1193 print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username);
\r
1198 TCHAR *dependencies = _T("");
\r
1199 if (service->dependencieslen) dependencies = 0; /* Change later. */
\r
1201 if (! ChangeServiceConfig(service->handle, service->type, startup, SERVICE_NO_CHANGE, 0, 0, 0, dependencies, canon, password, service->displayname)) {
\r
1202 if (canon != username) HeapFree(GetProcessHeap(), 0, canon);
\r
1203 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
1206 if (canon != username) HeapFree(GetProcessHeap(), 0, canon);
\r
1208 if (service->dependencieslen) {
\r
1209 if (set_service_dependencies(service->name, service->handle, service->dependencies)) return 5;
\r
1212 if (service->description[0] || editing) {
\r
1213 set_service_description(service->name, service->handle, service->description);
\r
1216 SERVICE_DELAYED_AUTO_START_INFO delayed;
\r
1217 ZeroMemory(&delayed, sizeof(delayed));
\r
1218 if (service->startup == NSSM_STARTUP_DELAYED) delayed.fDelayedAutostart = 1;
\r
1219 else delayed.fDelayedAutostart = 0;
\r
1220 /* Delayed startup isn't supported until Vista. */
\r
1221 if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, &delayed)) {
\r
1222 unsigned long error = GetLastError();
\r
1223 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
\r
1224 if (error != ERROR_INVALID_LEVEL) {
\r
1225 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_DELAYED_AUTO_START_INFO_FAILED, service->name, error_string(error), 0);
\r
1229 /* Don't mess with parameters which aren't ours. */
\r
1230 if (! service->native) {
\r
1231 /* Now we need to put the parameters into the registry */
\r
1232 if (create_parameters(service, editing)) {
\r
1233 print_message(stderr, NSSM_MESSAGE_CREATE_PARAMETERS_FAILED);
\r
1237 set_service_recovery(service);
\r
1243 /* Control a service. */
\r
1244 int control_service(unsigned long control, int argc, TCHAR **argv) {
\r
1245 if (argc < 1) return usage(1);
\r
1246 TCHAR *service_name = argv[0];
\r
1247 TCHAR canonical_name[SERVICE_NAME_LENGTH];
\r
1249 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
\r
1251 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
1255 unsigned long access = SERVICE_QUERY_STATUS;
\r
1256 switch (control) {
\r
1257 case NSSM_SERVICE_CONTROL_START:
\r
1258 access |= SERVICE_START;
\r
1261 case SERVICE_CONTROL_CONTINUE:
\r
1262 case SERVICE_CONTROL_PAUSE:
\r
1263 access |= SERVICE_PAUSE_CONTINUE;
\r
1266 case SERVICE_CONTROL_STOP:
\r
1267 access |= SERVICE_STOP;
\r
1270 case NSSM_SERVICE_CONTROL_ROTATE:
\r
1271 access |= SERVICE_USER_DEFINED_CONTROL;
\r
1275 SC_HANDLE service_handle = open_service(services, service_name, access, canonical_name, _countof(canonical_name));
\r
1276 if (! service_handle) {
\r
1277 CloseServiceHandle(services);
\r
1282 unsigned long error;
\r
1283 SERVICE_STATUS service_status;
\r
1284 if (control == NSSM_SERVICE_CONTROL_START) {
\r
1285 unsigned long initial_status = SERVICE_STOPPED;
\r
1286 ret = StartService(service_handle, (unsigned long) argc, (const TCHAR **) argv);
\r
1287 error = GetLastError();
\r
1288 CloseServiceHandle(services);
\r
1290 if (error == ERROR_IO_PENDING) {
\r
1292 Older versions of Windows return immediately with ERROR_IO_PENDING
\r
1293 indicate that the operation is still in progress. Newer versions
\r
1294 will return it if there really is a delay.
\r
1297 error = ERROR_SUCCESS;
\r
1301 int response = await_service_control_response(control, service_handle, &service_status, initial_status);
\r
1302 CloseServiceHandle(service_handle);
\r
1305 print_message(stderr, NSSM_MESSAGE_BAD_CONTROL_RESPONSE, canonical_name, service_status_text(service_status.dwCurrentState), service_control_text(control));
\r
1308 else _tprintf(_T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1312 CloseServiceHandle(service_handle);
\r
1313 _ftprintf(stderr, _T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1317 else if (control == SERVICE_CONTROL_INTERROGATE) {
\r
1319 We could actually send an INTERROGATE control but that won't return
\r
1320 any information if the service is stopped and we don't care about
\r
1321 the extra details it might give us in any case. So we'll fake it.
\r
1323 ret = QueryServiceStatus(service_handle, &service_status);
\r
1324 error = GetLastError();
\r
1327 _tprintf(_T("%s\n"), service_status_text(service_status.dwCurrentState));
\r
1331 _ftprintf(stderr, _T("%s: %s\n"), canonical_name, error_string(error));
\r
1336 ret = ControlService(service_handle, control, &service_status);
\r
1337 unsigned long initial_status = service_status.dwCurrentState;
\r
1338 error = GetLastError();
\r
1339 CloseServiceHandle(services);
\r
1341 if (error == ERROR_IO_PENDING) {
\r
1343 error = ERROR_SUCCESS;
\r
1347 int response = await_service_control_response(control, service_handle, &service_status, initial_status);
\r
1348 CloseServiceHandle(service_handle);
\r
1351 print_message(stderr, NSSM_MESSAGE_BAD_CONTROL_RESPONSE, canonical_name, service_status_text(service_status.dwCurrentState), service_control_text(control));
\r
1354 else _tprintf(_T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1358 CloseServiceHandle(service_handle);
\r
1359 _ftprintf(stderr, _T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1360 if (error == ERROR_SERVICE_NOT_ACTIVE) {
\r
1361 if (control == SERVICE_CONTROL_SHUTDOWN || control == SERVICE_CONTROL_STOP) return 0;
\r
1368 /* Remove the service */
\r
1369 int remove_service(nssm_service_t *service) {
\r
1370 if (! service) return 1;
\r
1372 /* Open service manager */
\r
1373 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
\r
1375 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
1379 /* Try to open the service */
\r
1380 service->handle = open_service(services, service->name, DELETE, service->name, _countof(service->name));
\r
1381 if (! service->handle) {
\r
1382 CloseServiceHandle(services);
\r
1386 /* Get the canonical service name. We open it case insensitively. */
\r
1387 unsigned long bufsize = _countof(service->displayname);
\r
1388 GetServiceDisplayName(services, service->name, service->displayname, &bufsize);
\r
1389 bufsize = _countof(service->name);
\r
1390 GetServiceKeyName(services, service->displayname, service->name, &bufsize);
\r
1392 /* Try to delete the service */
\r
1393 if (! DeleteService(service->handle)) {
\r
1394 print_message(stderr, NSSM_MESSAGE_DELETESERVICE_FAILED);
\r
1395 CloseServiceHandle(services);
\r
1400 CloseServiceHandle(services);
\r
1402 print_message(stdout, NSSM_MESSAGE_SERVICE_REMOVED, service->name);
\r
1406 /* Service initialisation */
\r
1407 void WINAPI service_main(unsigned long argc, TCHAR **argv) {
\r
1408 nssm_service_t *service = alloc_nssm_service();
\r
1409 if (! service) return;
\r
1411 if (_sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]) < 0) {
\r
1412 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("service->name"), _T("service_main()"), 0);
\r
1416 /* We can use a condition variable in a critical section on Vista or later. */
\r
1417 if (imports.SleepConditionVariableCS && imports.WakeConditionVariable) use_critical_section = true;
\r
1418 else use_critical_section = false;
\r
1420 /* Initialise status */
\r
1421 ZeroMemory(&service->status, sizeof(service->status));
\r
1422 service->status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;
\r
1423 service->status.dwControlsAccepted = 0;
\r
1424 service->status.dwWin32ExitCode = NO_ERROR;
\r
1425 service->status.dwServiceSpecificExitCode = 0;
\r
1426 service->status.dwCheckPoint = 0;
\r
1427 service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;
\r
1429 /* Signal we AREN'T running the server */
\r
1430 service->process_handle = 0;
\r
1433 /* Register control handler */
\r
1434 service->status_handle = RegisterServiceCtrlHandlerEx(NSSM, service_control_handler, (void *) service);
\r
1435 if (! service->status_handle) {
\r
1436 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_REGISTERSERVICECTRLHANDER_FAILED, error_string(GetLastError()), 0);
\r
1440 log_service_control(service->name, 0, true);
\r
1442 service->status.dwCurrentState = SERVICE_START_PENDING;
\r
1443 service->status.dwWaitHint = service->throttle_delay + NSSM_WAITHINT_MARGIN;
\r
1444 SetServiceStatus(service->status_handle, &service->status);
\r
1447 /* Try to create the exit action parameters; we don't care if it fails */
\r
1448 create_exit_action(service->name, exit_action_strings[0], false);
\r
1450 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT);
\r
1452 service->handle = open_service(services, service->name, SERVICE_CHANGE_CONFIG, 0, 0);
\r
1453 set_service_recovery(service);
\r
1455 /* Remember our display name. */
\r
1456 unsigned long displayname_len = _countof(service->displayname);
\r
1457 GetServiceDisplayName(services, service->name, service->displayname, &displayname_len);
\r
1459 CloseServiceHandle(services);
\r
1463 /* Used for signalling a resume if the service pauses when throttled. */
\r
1464 if (use_critical_section) {
\r
1465 InitializeCriticalSection(&service->throttle_section);
\r
1466 service->throttle_section_initialised = true;
\r
1469 service->throttle_timer = CreateWaitableTimer(0, 1, 0);
\r
1470 if (! service->throttle_timer) {
\r
1471 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_CREATEWAITABLETIMER_FAILED, service->name, error_string(GetLastError()), 0);
\r
1475 /* Critical section for hooks. */
\r
1476 InitializeCriticalSection(&service->hook_section);
\r
1477 service->hook_section_initialised = true;
\r
1479 /* Remember our initial environment. */
\r
1480 service->initial_env = copy_environment();
\r
1482 /* Remember our creation time. */
\r
1483 if (get_process_creation_time(GetCurrentProcess(), &service->nssm_creation_time)) ZeroMemory(&service->nssm_creation_time, sizeof(service->nssm_creation_time));
\r
1485 service->allow_restart = true;
\r
1486 if (! CreateThread(NULL, 0, launch_service, (void *) service, 0, NULL)) {
\r
1487 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETHREAD_FAILED, error_string(GetLastError()), 0);
\r
1488 stop_service(service, 0, true, true);
\r
1492 /* Make sure service recovery actions are taken where necessary */
\r
1493 void set_service_recovery(nssm_service_t *service) {
\r
1494 SERVICE_FAILURE_ACTIONS_FLAG flag;
\r
1495 ZeroMemory(&flag, sizeof(flag));
\r
1496 flag.fFailureActionsOnNonCrashFailures = true;
\r
1498 /* This functionality was added in Vista so the call may fail */
\r
1499 if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, &flag)) {
\r
1500 unsigned long error = GetLastError();
\r
1501 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
\r
1502 if (error != ERROR_INVALID_LEVEL) {
\r
1503 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_FAILURE_ACTIONS_FAILED, service->name, error_string(error), 0);
\r
1508 int monitor_service(nssm_service_t *service) {
\r
1509 /* Set service status to started */
\r
1510 int ret = start_service(service);
\r
1513 _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%d"), ret);
\r
1514 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_START_SERVICE_FAILED, service->exe, service->name, ret, 0);
\r
1517 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_STARTED_SERVICE, service->exe, service->flags, service->name, service->dir, 0);
\r
1519 /* Monitor service */
\r
1520 if (! RegisterWaitForSingleObject(&service->wait_handle, service->process_handle, end_service, (void *) service, INFINITE, WT_EXECUTEONLYONCE | WT_EXECUTELONGFUNCTION)) {
\r
1521 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_REGISTERWAITFORSINGLEOBJECT_FAILED, service->name, service->exe, error_string(GetLastError()), 0);
\r
1527 TCHAR *service_control_text(unsigned long control) {
\r
1528 switch (control) {
\r
1529 /* HACK: there is no SERVICE_CONTROL_START constant */
\r
1530 case NSSM_SERVICE_CONTROL_START: return _T("START");
\r
1531 case SERVICE_CONTROL_STOP: return _T("STOP");
\r
1532 case SERVICE_CONTROL_SHUTDOWN: return _T("SHUTDOWN");
\r
1533 case SERVICE_CONTROL_PAUSE: return _T("PAUSE");
\r
1534 case SERVICE_CONTROL_CONTINUE: return _T("CONTINUE");
\r
1535 case SERVICE_CONTROL_INTERROGATE: return _T("INTERROGATE");
\r
1536 case NSSM_SERVICE_CONTROL_ROTATE: return _T("ROTATE");
\r
1537 case SERVICE_CONTROL_POWEREVENT: return _T("POWEREVENT");
\r
1538 default: return 0;
\r
1542 TCHAR *service_status_text(unsigned long status) {
\r
1544 case SERVICE_STOPPED: return _T("SERVICE_STOPPED");
\r
1545 case SERVICE_START_PENDING: return _T("SERVICE_START_PENDING");
\r
1546 case SERVICE_STOP_PENDING: return _T("SERVICE_STOP_PENDING");
\r
1547 case SERVICE_RUNNING: return _T("SERVICE_RUNNING");
\r
1548 case SERVICE_CONTINUE_PENDING: return _T("SERVICE_CONTINUE_PENDING");
\r
1549 case SERVICE_PAUSE_PENDING: return _T("SERVICE_PAUSE_PENDING");
\r
1550 case SERVICE_PAUSED: return _T("SERVICE_PAUSED");
\r
1551 default: return 0;
\r
1555 void log_service_control(TCHAR *service_name, unsigned long control, bool handled) {
\r
1556 TCHAR *text = service_control_text(control);
\r
1557 unsigned long event;
\r
1560 /* "0x" + 8 x hex + NULL */
\r
1561 text = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, 11 * sizeof(TCHAR));
\r
1563 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);
\r
1566 if (_sntprintf_s(text, 11, _TRUNCATE, _T("0x%08x"), control) < 0) {
\r
1567 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);
\r
1568 HeapFree(GetProcessHeap(), 0, text);
\r
1572 event = NSSM_EVENT_SERVICE_CONTROL_UNKNOWN;
\r
1574 else if (handled) event = NSSM_EVENT_SERVICE_CONTROL_HANDLED;
\r
1575 else event = NSSM_EVENT_SERVICE_CONTROL_NOT_HANDLED;
\r
1577 log_event(EVENTLOG_INFORMATION_TYPE, event, service_name, text, 0);
\r
1579 if (event == NSSM_EVENT_SERVICE_CONTROL_UNKNOWN) {
\r
1580 HeapFree(GetProcessHeap(), 0, text);
\r
1584 /* Service control handler */
\r
1585 unsigned long WINAPI service_control_handler(unsigned long control, unsigned long event, void *data, void *context) {
\r
1586 nssm_service_t *service = (nssm_service_t *) context;
\r
1588 switch (control) {
\r
1589 case SERVICE_CONTROL_INTERROGATE:
\r
1590 /* We always keep the service status up-to-date so this is a no-op. */
\r
1593 case SERVICE_CONTROL_SHUTDOWN:
\r
1594 case SERVICE_CONTROL_STOP:
\r
1595 service->last_control = control;
\r
1596 log_service_control(service->name, control, true);
\r
1598 /* Immediately block further controls. */
\r
1599 service->allow_restart = false;
\r
1600 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
1601 service->status.dwControlsAccepted = 0;
\r
1602 SetServiceStatus(service->status_handle, &service->status);
\r
1604 /* Pre-stop hook. */
\r
1605 nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_STOP, NSSM_HOOK_ACTION_PRE, &control, NSSM_SERVICE_STATUS_DEADLINE, false);
\r
1608 We MUST acknowledge the stop request promptly but we're committed to
\r
1609 waiting for the application to exit. Spawn a new thread to wait
\r
1610 while we acknowledge the request.
\r
1612 if (! CreateThread(NULL, 0, shutdown_service, context, 0, NULL)) {
\r
1613 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETHREAD_FAILED, error_string(GetLastError()), 0);
\r
1616 We couldn't create a thread to tidy up so we'll have to force the tidyup
\r
1617 to complete in time in this thread.
\r
1619 service->kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;
\r
1620 service->kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;
\r
1621 service->kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;
\r
1623 stop_service(service, 0, true, true);
\r
1627 case SERVICE_CONTROL_CONTINUE:
\r
1628 service->last_control = control;
\r
1629 log_service_control(service->name, control, true);
\r
1630 service->throttle = 0;
\r
1631 if (use_critical_section) imports.WakeConditionVariable(&service->throttle_condition);
\r
1633 if (! service->throttle_timer) return ERROR_CALL_NOT_IMPLEMENTED;
\r
1634 ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));
\r
1635 SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);
\r
1637 /* We can't continue if the application is running! */
\r
1638 if (! service->process_handle) service->status.dwCurrentState = SERVICE_CONTINUE_PENDING;
\r
1639 service->status.dwWaitHint = throttle_milliseconds(service->throttle) + NSSM_WAITHINT_MARGIN;
\r
1640 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESET_THROTTLE, service->name, 0);
\r
1641 SetServiceStatus(service->status_handle, &service->status);
\r
1644 case SERVICE_CONTROL_PAUSE:
\r
1646 We don't accept pause messages but it isn't possible to register
\r
1647 only for continue messages so we have to handle this case.
\r
1649 log_service_control(service->name, control, false);
\r
1650 return ERROR_CALL_NOT_IMPLEMENTED;
\r
1652 case NSSM_SERVICE_CONTROL_ROTATE:
\r
1653 service->last_control = control;
\r
1654 log_service_control(service->name, control, true);
\r
1655 (void) nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_ROTATE, NSSM_HOOK_ACTION_PRE, &control, NSSM_HOOK_DEADLINE, false);
\r
1656 if (service->rotate_stdout_online == NSSM_ROTATE_ONLINE) service->rotate_stdout_online = NSSM_ROTATE_ONLINE_ASAP;
\r
1657 if (service->rotate_stderr_online == NSSM_ROTATE_ONLINE) service->rotate_stderr_online = NSSM_ROTATE_ONLINE_ASAP;
\r
1658 (void) nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_ROTATE, NSSM_HOOK_ACTION_POST, &control);
\r
1661 case SERVICE_CONTROL_POWEREVENT:
\r
1662 /* Resume from suspend. */
\r
1663 if (event == PBT_APMRESUMEAUTOMATIC) {
\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_RESUME, &control);
\r
1670 /* Battery low or changed to A/C power or something. */
\r
1671 if (event == PBT_APMPOWERSTATUSCHANGE) {
\r
1672 service->last_control = control;
\r
1673 log_service_control(service->name, control, true);
\r
1674 (void) nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_POWER, NSSM_HOOK_ACTION_CHANGE, &control);
\r
1677 log_service_control(service->name, control, false);
\r
1681 /* Unknown control */
\r
1682 log_service_control(service->name, control, false);
\r
1683 return ERROR_CALL_NOT_IMPLEMENTED;
\r
1686 /* Start the service */
\r
1687 int start_service(nssm_service_t *service) {
\r
1688 service->stopping = false;
\r
1690 if (service->process_handle) return 0;
\r
1691 service->start_requested_count++;
\r
1693 /* Allocate a STARTUPINFO structure for a new process */
\r
1695 ZeroMemory(&si, sizeof(si));
\r
1696 si.cb = sizeof(si);
\r
1698 /* Allocate a PROCESSINFO structure for the process */
\r
1699 PROCESS_INFORMATION pi;
\r
1700 ZeroMemory(&pi, sizeof(pi));
\r
1702 /* Get startup parameters */
\r
1703 int ret = get_parameters(service, &si);
\r
1705 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_PARAMETERS_FAILED, service->name, 0);
\r
1706 unset_service_environment(service);
\r
1707 return stop_service(service, 2, true, true);
\r
1710 /* Launch executable with arguments */
\r
1711 TCHAR cmd[CMD_LENGTH];
\r
1712 if (_sntprintf_s(cmd, _countof(cmd), _TRUNCATE, _T("\"%s\" %s"), service->exe, service->flags) < 0) {
\r
1713 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("command line"), _T("start_service"), 0);
\r
1714 unset_service_environment(service);
\r
1715 return stop_service(service, 2, true, true);
\r
1718 throttle_restart(service);
\r
1720 service->status.dwCurrentState = SERVICE_START_PENDING;
\r
1721 service->status.dwControlsAccepted = SERVICE_ACCEPT_POWEREVENT | SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP;
\r
1722 SetServiceStatus(service->status_handle, &service->status);
\r
1724 unsigned long control = NSSM_SERVICE_CONTROL_START;
\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 /* Pre-start hook. May need I/O to have been redirected already. */
\r
1738 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
1740 _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), NSSM_HOOK_STATUS_ABORT);
\r
1741 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_PRESTART_HOOK_ABORT, NSSM_HOOK_EVENT_START, NSSM_HOOK_ACTION_PRE, service->name, code, 0);
\r
1742 unset_service_environment(service);
\r
1743 return stop_service(service, 5, true, true);
\r
1746 /* The pre-start hook will have cleaned the environment. */
\r
1747 set_service_environment(service);
\r
1749 bool inherit_handles = false;
\r
1750 if (si.dwFlags & STARTF_USESTDHANDLES) inherit_handles = true;
\r
1751 unsigned long flags = service->priority & priority_mask();
\r
1752 if (service->affinity) flags |= CREATE_SUSPENDED;
\r
1753 if (! CreateProcess(0, cmd, 0, 0, inherit_handles, flags, 0, service->dir, &si, &pi)) {
\r
1754 unsigned long exitcode = 3;
\r
1755 unsigned long error = GetLastError();
\r
1756 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service->name, service->exe, error_string(error), 0);
\r
1757 close_output_handles(&si);
\r
1758 unset_service_environment(service);
\r
1759 return stop_service(service, exitcode, true, true);
\r
1761 service->start_count++;
\r
1762 service->process_handle = pi.hProcess;
\r
1763 service->pid = pi.dwProcessId;
\r
1765 if (get_process_creation_time(service->process_handle, &service->creation_time)) ZeroMemory(&service->creation_time, sizeof(service->creation_time));
\r
1767 close_output_handles(&si);
\r
1769 if (! service->no_console) FreeConsole();
\r
1771 if (service->affinity) {
\r
1773 We are explicitly storing service->affinity as a 64-bit unsigned integer
\r
1774 so that we can parse it regardless of whether we're running in 32-bit
\r
1775 or 64-bit mode. The arguments to SetProcessAffinityMask(), however, are
\r
1776 defined as type DWORD_PTR and hence limited to 32 bits on a 32-bit system
\r
1777 (or when running the 32-bit NSSM).
\r
1779 The result is a lot of seemingly-unnecessary casting throughout the code
\r
1780 and potentially confusion when we actually try to start the service.
\r
1781 Having said that, however, it's unlikely that we're actually going to
\r
1782 run in 32-bit mode on a system which has more than 32 CPUs so the
\r
1783 likelihood of seeing a confusing situation is somewhat diminished.
\r
1785 DWORD_PTR affinity, system_affinity;
\r
1787 if (GetProcessAffinityMask(service->process_handle, &affinity, &system_affinity)) affinity = service->affinity & system_affinity;
\r
1789 affinity = (DWORD_PTR) service->affinity;
\r
1790 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GETPROCESSAFFINITYMASK_FAILED, service->name, error_string(GetLastError()), 0);
\r
1793 if (! SetProcessAffinityMask(service->process_handle, affinity)) {
\r
1794 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_SETPROCESSAFFINITYMASK_FAILED, service->name, error_string(GetLastError()), 0);
\r
1797 ResumeThread(pi.hThread);
\r
1801 /* Restore our environment. */
\r
1802 unset_service_environment(service);
\r
1805 Wait for a clean startup before changing the service status to RUNNING
\r
1806 but be mindful of the fact that we are blocking the service control manager
\r
1807 so abandon the wait before too much time has elapsed.
\r
1809 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
1811 /* Did another thread receive a stop control? */
\r
1812 if (! service->allow_restart) return 0;
\r
1814 /* Signal successful start */
\r
1815 service->status.dwCurrentState = SERVICE_RUNNING;
\r
1816 service->status.dwControlsAccepted &= ~SERVICE_ACCEPT_PAUSE_CONTINUE;
\r
1817 SetServiceStatus(service->status_handle, &service->status);
\r
1819 /* Post-start hook. */
\r
1820 if (! service->throttle) {
\r
1821 (void) nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_START, NSSM_HOOK_ACTION_POST, &control);
\r
1824 /* Ensure the restart delay is always applied. */
\r
1825 if (service->restart_delay && ! service->throttle) service->throttle++;
\r
1830 /* Stop the service */
\r
1831 int stop_service(nssm_service_t *service, unsigned long exitcode, bool graceful, bool default_action) {
\r
1832 service->allow_restart = false;
\r
1833 if (service->wait_handle) {
\r
1834 UnregisterWait(service->wait_handle);
\r
1835 service->wait_handle = 0;
\r
1838 service->rotate_stdout_online = service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;
\r
1840 if (default_action && ! exitcode && ! graceful) {
\r
1841 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
1845 /* Signal we are stopping */
\r
1847 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
1848 service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;
\r
1849 SetServiceStatus(service->status_handle, &service->status);
\r
1852 /* Nothing to do if service isn't running */
\r
1853 if (service->pid) {
\r
1854 /* Shut down service */
\r
1855 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_TERMINATEPROCESS, service->name, service->exe, 0);
\r
1857 service_kill_t(service, &k);
\r
1861 else log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_PROCESS_ALREADY_STOPPED, service->name, service->exe, 0);
\r
1863 end_service((void *) service, true);
\r
1865 /* Signal we stopped */
\r
1867 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
1868 wait_for_hooks(service, true);
\r
1869 service->status.dwCurrentState = SERVICE_STOPPED;
\r
1871 service->status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
\r
1872 service->status.dwServiceSpecificExitCode = exitcode;
\r
1875 service->status.dwWin32ExitCode = NO_ERROR;
\r
1876 service->status.dwServiceSpecificExitCode = 0;
\r
1878 SetServiceStatus(service->status_handle, &service->status);
\r
1884 /* Callback function triggered when the server exits */
\r
1885 void CALLBACK end_service(void *arg, unsigned char why) {
\r
1886 nssm_service_t *service = (nssm_service_t *) arg;
\r
1888 if (service->stopping) return;
\r
1890 service->stopping = true;
\r
1892 service->rotate_stdout_online = service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;
\r
1894 /* Use now as a dummy exit time. */
\r
1895 GetSystemTimeAsFileTime(&service->exit_time);
\r
1897 /* Check exit code */
\r
1898 unsigned long exitcode = 0;
\r
1900 if (service->process_handle) {
\r
1901 GetExitCodeProcess(service->process_handle, &exitcode);
\r
1902 service->exitcode = exitcode;
\r
1903 /* Check real exit time. */
\r
1904 if (exitcode != STILL_ACTIVE) get_process_exit_time(service->process_handle, &service->exit_time);
\r
1905 CloseHandle(service->process_handle);
\r
1908 service->process_handle = 0;
\r
1911 Log that the service ended BEFORE logging about killing the process
\r
1912 tree. See below for the possible values of the why argument.
\r
1915 _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), exitcode);
\r
1916 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ENDED_SERVICE, service->exe, service->name, code, 0);
\r
1920 if (exitcode == STILL_ACTIVE) exitcode = 0;
\r
1921 if (service->pid && service->kill_process_tree) {
\r
1923 service_kill_t(service, &k);
\r
1924 kill_process_tree(&k, service->pid);
\r
1929 service->exit_count++;
\r
1930 (void) nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_EXIT, NSSM_HOOK_ACTION_POST, NULL, NSSM_HOOK_DEADLINE, true);
\r
1933 The why argument is true if our wait timed out or false otherwise.
\r
1934 Our wait is infinite so why will never be true when called by the system.
\r
1935 If it is indeed true, assume we were called from stop_service() because
\r
1936 this is a controlled shutdown, and don't take any restart action.
\r
1939 if (! service->allow_restart) return;
\r
1941 /* What action should we take? */
\r
1942 int action = NSSM_EXIT_RESTART;
\r
1943 TCHAR action_string[ACTION_LEN];
\r
1944 bool default_action;
\r
1945 if (! get_exit_action(service->name, &exitcode, action_string, &default_action)) {
\r
1946 for (int i = 0; exit_action_strings[i]; i++) {
\r
1947 if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
1955 /* Try to restart the service or return failure code to service manager */
\r
1956 case NSSM_EXIT_RESTART:
\r
1957 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_RESTART, service->name, code, exit_action_strings[action], service->exe, 0);
\r
1958 while (monitor_service(service)) {
\r
1959 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_RESTART_SERVICE_FAILED, service->exe, service->name, 0);
\r
1964 /* Do nothing, just like srvany would */
\r
1965 case NSSM_EXIT_IGNORE:
\r
1966 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_IGNORE, service->name, code, exit_action_strings[action], service->exe, 0);
\r
1967 wait_for_hooks(service, false);
\r
1971 /* Tell the service manager we are finished */
\r
1972 case NSSM_EXIT_REALLY:
\r
1973 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_REALLY, service->name, code, exit_action_strings[action], 0);
\r
1974 stop_service(service, exitcode, true, default_action);
\r
1977 /* Fake a crash so pre-Vista service managers will run recovery actions. */
\r
1978 case NSSM_EXIT_UNCLEAN:
\r
1979 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_UNCLEAN, service->name, code, exit_action_strings[action], 0);
\r
1980 stop_service(service, exitcode, false, default_action);
\r
1981 wait_for_hooks(service, false);
\r
1987 void throttle_restart(nssm_service_t *service) {
\r
1988 /* This can't be a restart if the service is already running. */
\r
1989 if (! service->throttle++) return;
\r
1992 unsigned long throttle_ms = throttle_milliseconds(service->throttle);
\r
1993 TCHAR threshold[8], milliseconds[8];
\r
1995 if (service->restart_delay > throttle_ms) ms = service->restart_delay;
\r
1996 else ms = throttle_ms;
\r
1998 _sntprintf_s(milliseconds, _countof(milliseconds), _TRUNCATE, _T("%lu"), ms);
\r
2000 if (service->throttle == 1 && service->restart_delay > throttle_ms) log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESTART_DELAY, service->name, milliseconds, 0);
\r
2002 _sntprintf_s(threshold, _countof(threshold), _TRUNCATE, _T("%lu"), service->throttle_delay);
\r
2003 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_THROTTLED, service->name, threshold, milliseconds, 0);
\r
2006 if (use_critical_section) EnterCriticalSection(&service->throttle_section);
\r
2007 else if (service->throttle_timer) {
\r
2008 ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));
\r
2009 service->throttle_duetime.QuadPart = 0 - (ms * 10000LL);
\r
2010 SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);
\r
2013 service->status.dwCurrentState = SERVICE_PAUSED;
\r
2014 service->status.dwControlsAccepted |= SERVICE_ACCEPT_PAUSE_CONTINUE;
\r
2015 SetServiceStatus(service->status_handle, &service->status);
\r
2017 if (use_critical_section) {
\r
2018 imports.SleepConditionVariableCS(&service->throttle_condition, &service->throttle_section, ms);
\r
2019 LeaveCriticalSection(&service->throttle_section);
\r
2022 if (service->throttle_timer) WaitForSingleObject(service->throttle_timer, INFINITE);
\r
2028 When responding to a stop (or any other) request we need to set dwWaitHint to
\r
2029 the number of milliseconds we expect the operation to take, and optionally
\r
2030 increase dwCheckPoint. If dwWaitHint milliseconds elapses without the
\r
2031 operation completing or dwCheckPoint increasing, the system will consider the
\r
2032 service to be hung.
\r
2034 However the system will consider the service to be hung after 30000
\r
2035 milliseconds regardless of the value of dwWaitHint if dwCheckPoint has not
\r
2036 changed. Therefore if we want to wait longer than that we must periodically
\r
2037 increase dwCheckPoint.
\r
2039 Furthermore, it will consider the service to be hung after 60000 milliseconds
\r
2040 regardless of the value of dwCheckPoint unless dwWaitHint is increased every
\r
2041 time dwCheckPoint is also increased.
\r
2043 Our strategy then is to retrieve the initial dwWaitHint and wait for
\r
2044 NSSM_SERVICE_STATUS_DEADLINE milliseconds. If the process is still running
\r
2045 and we haven't finished waiting we increment dwCheckPoint and add whichever is
\r
2046 smaller of NSSM_SERVICE_STATUS_DEADLINE or the remaining timeout to
\r
2049 Only doing both these things will prevent the system from killing the service.
\r
2051 If the status_handle and service_status arguments are omitted, this function
\r
2052 will not try to update the service manager but it will still log to the
\r
2053 event log that it is waiting for a handle.
\r
2055 Returns: 1 if the wait timed out.
\r
2056 0 if the wait completed.
\r
2059 int await_single_handle(SERVICE_STATUS_HANDLE status_handle, SERVICE_STATUS *status, HANDLE handle, TCHAR *name, TCHAR *function_name, unsigned long timeout) {
\r
2060 unsigned long interval;
\r
2061 unsigned long ret;
\r
2062 unsigned long waited;
\r
2063 TCHAR interval_milliseconds[16];
\r
2064 TCHAR timeout_milliseconds[16];
\r
2065 TCHAR waited_milliseconds[16];
\r
2066 TCHAR *function = function_name;
\r
2068 /* Add brackets to function name. */
\r
2069 size_t funclen = _tcslen(function_name) + 3;
\r
2070 TCHAR *func = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, funclen * sizeof(TCHAR));
\r
2072 if (_sntprintf_s(func, funclen, _TRUNCATE, _T("%s()"), function_name) > -1) function = func;
\r
2075 _sntprintf_s(timeout_milliseconds, _countof(timeout_milliseconds), _TRUNCATE, _T("%lu"), timeout);
\r
2078 while (waited < timeout) {
\r
2079 interval = timeout - waited;
\r
2080 if (interval > NSSM_SERVICE_STATUS_DEADLINE) interval = NSSM_SERVICE_STATUS_DEADLINE;
\r
2083 status->dwWaitHint += interval;
\r
2084 status->dwCheckPoint++;
\r
2085 SetServiceStatus(status_handle, status);
\r
2089 _sntprintf_s(waited_milliseconds, _countof(waited_milliseconds), _TRUNCATE, _T("%lu"), waited);
\r
2090 _sntprintf_s(interval_milliseconds, _countof(interval_milliseconds), _TRUNCATE, _T("%lu"), interval);
\r
2091 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_AWAITING_SINGLE_HANDLE, function, name, waited_milliseconds, interval_milliseconds, timeout_milliseconds, 0);
\r
2094 switch (WaitForSingleObject(handle, interval)) {
\r
2095 case WAIT_OBJECT_0:
\r
2099 case WAIT_TIMEOUT:
\r
2108 waited += interval;
\r
2112 if (func) HeapFree(GetProcessHeap(), 0, func);
\r
2117 int list_nssm_services() {
\r
2118 /* Open service manager. */
\r
2119 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
\r
2121 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
2125 unsigned long bufsize, required, count, i;
\r
2126 unsigned long resume = 0;
\r
2127 EnumServicesStatus(services, SERVICE_WIN32, SERVICE_STATE_ALL, 0, 0, &required, &count, &resume);
\r
2128 unsigned long error = GetLastError();
\r
2129 if (error != ERROR_MORE_DATA) {
\r
2130 print_message(stderr, NSSM_MESSAGE_ENUMSERVICESSTATUS_FAILED, error_string(GetLastError()));
\r
2134 ENUM_SERVICE_STATUS *status = (ENUM_SERVICE_STATUS *) HeapAlloc(GetProcessHeap(), 0, required);
\r
2136 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("ENUM_SERVICE_STATUS"), _T("list_nssm_services()"));
\r
2140 bufsize = required;
\r
2142 int ret = EnumServicesStatus(services, SERVICE_WIN32, SERVICE_STATE_ALL, status, bufsize, &required, &count, &resume);
\r
2144 error = GetLastError();
\r
2145 if (error != ERROR_MORE_DATA) {
\r
2146 HeapFree(GetProcessHeap(), 0, status);
\r
2147 print_message(stderr, NSSM_MESSAGE_ENUMSERVICESSTATUS_FAILED, error_string(GetLastError()));
\r
2152 for (i = 0; i < count; i++) {
\r
2153 /* Try to get the service parameters. */
\r
2154 nssm_service_t *service = alloc_nssm_service();
\r
2156 HeapFree(GetProcessHeap(), 0, status);
\r
2157 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("nssm_service_t"), _T("list_nssm_services()"));
\r
2160 _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), status[i].lpServiceName);
\r
2162 get_parameters(service, 0);
\r
2163 /* We manage the service if we have an Application. */
\r
2164 if (service->exe[0]) _tprintf(_T("%s\n"), service->name);
\r
2166 cleanup_nssm_service(service);
\r
2172 HeapFree(GetProcessHeap(), 0, status);
\r