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 || ! qsc->lpDependencies[0]) {
\r
566 HeapFree(GetProcessHeap(), 0, qsc);
\r
570 /* lpDependencies is doubly NULL terminated. */
\r
571 while (qsc->lpDependencies[*bufsize]) {
\r
572 while (qsc->lpDependencies[*bufsize]) ++*bufsize;
\r
578 *buffer = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *bufsize * sizeof(TCHAR));
\r
581 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("lpDependencies"), _T("get_service_dependencies()"));
\r
582 HeapFree(GetProcessHeap(), 0, qsc);
\r
586 if (type == DEPENDENCY_ALL) memmove(*buffer, qsc->lpDependencies, *bufsize * sizeof(TCHAR));
\r
591 for (s = qsc->lpDependencies; *s; s++) {
\r
592 /* Only copy the appropriate type of dependency. */
\r
593 if ((*s == SC_GROUP_IDENTIFIER && type & DEPENDENCY_GROUPS) || (*s != SC_GROUP_IDENTIFIER && type & DEPENDENCY_SERVICES)) {
\r
594 size_t len = _tcslen(s) + 1;
\r
595 *bufsize += (unsigned long) len;
\r
596 memmove(*buffer + i, s, len * sizeof(TCHAR));
\r
605 HeapFree(GetProcessHeap(), 0, qsc);
\r
607 if (! *buffer[0]) {
\r
608 HeapFree(GetProcessHeap(), 0, *buffer);
\r
616 int get_service_dependencies(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR **buffer, unsigned long *bufsize) {
\r
617 return get_service_dependencies(service_name, service_handle, buffer, bufsize, DEPENDENCY_ALL);
\r
620 int set_service_description(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR *buffer) {
\r
621 SERVICE_DESCRIPTION description;
\r
622 ZeroMemory(&description, sizeof(description));
\r
624 lpDescription must be NULL if we aren't changing, the new description
\r
627 if (buffer && buffer[0]) description.lpDescription = buffer;
\r
628 else description.lpDescription = _T("");
\r
630 if (ChangeServiceConfig2(service_handle, SERVICE_CONFIG_DESCRIPTION, &description)) return 0;
\r
632 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_DESCRIPTION_FAILED, service_name, error_string(GetLastError()), 0);
\r
636 int get_service_description(const TCHAR *service_name, SC_HANDLE service_handle, unsigned long len, TCHAR *buffer) {
\r
637 if (! buffer) return 1;
\r
639 unsigned long bufsize;
\r
640 QueryServiceConfig2(service_handle, SERVICE_CONFIG_DESCRIPTION, 0, 0, &bufsize);
\r
641 unsigned long error = GetLastError();
\r
642 if (error == ERROR_INSUFFICIENT_BUFFER) {
\r
643 SERVICE_DESCRIPTION *description = (SERVICE_DESCRIPTION *) HeapAlloc(GetProcessHeap(), 0, bufsize);
\r
644 if (! description) {
\r
645 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("SERVICE_CONFIG_DESCRIPTION"), _T("get_service_description()"));
\r
649 if (QueryServiceConfig2(service_handle, SERVICE_CONFIG_DESCRIPTION, (unsigned char *) description, bufsize, &bufsize)) {
\r
650 if (description->lpDescription) _sntprintf_s(buffer, len, _TRUNCATE, _T("%s"), description->lpDescription);
\r
651 else ZeroMemory(buffer, len * sizeof(TCHAR));
\r
652 HeapFree(GetProcessHeap(), 0, description);
\r
656 HeapFree(GetProcessHeap(), 0, description);
\r
657 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_CONFIG_DESCRIPTION"), error_string(error));
\r
662 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_CONFIG_DESCRIPTION"), error_string(error));
\r
667 int get_service_startup(const TCHAR *service_name, SC_HANDLE service_handle, const QUERY_SERVICE_CONFIG *qsc, unsigned long *startup) {
\r
668 if (! qsc) return 1;
\r
670 switch (qsc->dwStartType) {
\r
671 case SERVICE_DEMAND_START: *startup = NSSM_STARTUP_MANUAL; break;
\r
672 case SERVICE_DISABLED: *startup = NSSM_STARTUP_DISABLED; break;
\r
673 default: *startup = NSSM_STARTUP_AUTOMATIC;
\r
676 if (*startup != NSSM_STARTUP_AUTOMATIC) return 0;
\r
678 /* Check for delayed start. */
\r
679 unsigned long bufsize;
\r
680 unsigned long error;
\r
681 QueryServiceConfig2(service_handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, 0, 0, &bufsize);
\r
682 error = GetLastError();
\r
683 if (error == ERROR_INSUFFICIENT_BUFFER) {
\r
684 SERVICE_DELAYED_AUTO_START_INFO *info = (SERVICE_DELAYED_AUTO_START_INFO *) HeapAlloc(GetProcessHeap(), 0, bufsize);
\r
686 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("SERVICE_DELAYED_AUTO_START_INFO"), _T("get_service_startup()"));
\r
690 if (QueryServiceConfig2(service_handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, (unsigned char *) info, bufsize, &bufsize)) {
\r
691 if (info->fDelayedAutostart) *startup = NSSM_STARTUP_DELAYED;
\r
692 HeapFree(GetProcessHeap(), 0, info);
\r
696 error = GetLastError();
\r
697 if (error != ERROR_INVALID_LEVEL) {
\r
698 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_CONFIG_DELAYED_AUTO_START_INFO"), error_string(error));
\r
703 else if (error != ERROR_INVALID_LEVEL) {
\r
704 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_DELAYED_AUTO_START_INFO"), error_string(error));
\r
711 int get_service_username(const TCHAR *service_name, const QUERY_SERVICE_CONFIG *qsc, TCHAR **username, size_t *usernamelen) {
\r
712 if (! username) return 1;
\r
713 if (! usernamelen) return 1;
\r
718 if (! qsc) return 1;
\r
720 if (qsc->lpServiceStartName[0]) {
\r
721 if (is_localsystem(qsc->lpServiceStartName)) return 0;
\r
723 size_t len = _tcslen(qsc->lpServiceStartName);
\r
724 *username = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(TCHAR));
\r
726 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("username"), _T("get_service_username()"));
\r
730 memmove(*username, qsc->lpServiceStartName, (len + 1) * sizeof(TCHAR));
\r
731 *usernamelen = len;
\r
737 /* Set default values which aren't zero. */
\r
738 void set_nssm_service_defaults(nssm_service_t *service) {
\r
739 if (! service) return;
\r
741 service->type = SERVICE_WIN32_OWN_PROCESS;
\r
742 service->priority = NORMAL_PRIORITY_CLASS;
\r
743 service->stdin_sharing = NSSM_STDIN_SHARING;
\r
744 service->stdin_disposition = NSSM_STDIN_DISPOSITION;
\r
745 service->stdin_flags = NSSM_STDIN_FLAGS;
\r
746 service->stdout_sharing = NSSM_STDOUT_SHARING;
\r
747 service->stdout_disposition = NSSM_STDOUT_DISPOSITION;
\r
748 service->stdout_flags = NSSM_STDOUT_FLAGS;
\r
749 service->stderr_sharing = NSSM_STDERR_SHARING;
\r
750 service->stderr_disposition = NSSM_STDERR_DISPOSITION;
\r
751 service->stderr_flags = NSSM_STDERR_FLAGS;
\r
752 service->throttle_delay = NSSM_RESET_THROTTLE_RESTART;
\r
753 service->stop_method = ~0;
\r
754 service->kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;
\r
755 service->kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;
\r
756 service->kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;
\r
757 service->kill_process_tree = 1;
\r
760 /* Allocate and zero memory for a service. */
\r
761 nssm_service_t *alloc_nssm_service() {
\r
762 nssm_service_t *service = (nssm_service_t *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(nssm_service_t));
\r
763 if (! service) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("service"), _T("alloc_nssm_service()"), 0);
\r
767 /* Free memory for a service. */
\r
768 void cleanup_nssm_service(nssm_service_t *service) {
\r
769 if (! service) return;
\r
770 if (service->username) HeapFree(GetProcessHeap(), 0, service->username);
\r
771 if (service->password) {
\r
772 SecureZeroMemory(service->password, service->passwordlen * sizeof(TCHAR));
\r
773 HeapFree(GetProcessHeap(), 0, service->password);
\r
775 if (service->dependencies) HeapFree(GetProcessHeap(), 0, service->dependencies);
\r
776 if (service->env) HeapFree(GetProcessHeap(), 0, service->env);
\r
777 if (service->env_extra) HeapFree(GetProcessHeap(), 0, service->env_extra);
\r
778 if (service->handle) CloseServiceHandle(service->handle);
\r
779 if (service->process_handle) CloseHandle(service->process_handle);
\r
780 if (service->wait_handle) UnregisterWait(service->wait_handle);
\r
781 if (service->throttle_section_initialised) DeleteCriticalSection(&service->throttle_section);
\r
782 if (service->throttle_timer) CloseHandle(service->throttle_timer);
\r
783 if (service->hook_section_initialised) DeleteCriticalSection(&service->hook_section);
\r
784 if (service->initial_env) HeapFree(GetProcessHeap(), 0, service->initial_env);
\r
785 HeapFree(GetProcessHeap(), 0, service);
\r
788 /* About to install the service */
\r
789 int pre_install_service(int argc, TCHAR **argv) {
\r
790 nssm_service_t *service = alloc_nssm_service();
\r
791 set_nssm_service_defaults(service);
\r
792 if (argc) _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]);
\r
794 /* Show the dialogue box if we didn't give the service name and path */
\r
795 if (argc < 2) return nssm_gui(IDD_INSTALL, service);
\r
798 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("service"), _T("pre_install_service()"));
\r
801 _sntprintf_s(service->exe, _countof(service->exe), _TRUNCATE, _T("%s"), argv[1]);
\r
803 /* Arguments are optional */
\r
804 size_t flagslen = 0;
\r
807 for (i = 2; i < argc; i++) flagslen += _tcslen(argv[i]) + 1;
\r
808 if (! flagslen) flagslen = 1;
\r
809 if (flagslen > _countof(service->flags)) {
\r
810 print_message(stderr, NSSM_MESSAGE_FLAGS_TOO_LONG);
\r
814 for (i = 2; i < argc; i++) {
\r
815 size_t len = _tcslen(argv[i]);
\r
816 memmove(service->flags + s, argv[i], len * sizeof(TCHAR));
\r
818 if (i < argc - 1) service->flags[s++] = _T(' ');
\r
821 /* Work out directory name */
\r
822 _sntprintf_s(service->dir, _countof(service->dir), _TRUNCATE, _T("%s"), service->exe);
\r
823 strip_basename(service->dir);
\r
825 int ret = install_service(service);
\r
826 cleanup_nssm_service(service);
\r
830 /* About to edit the service. */
\r
831 int pre_edit_service(int argc, TCHAR **argv) {
\r
832 /* Require service name. */
\r
833 if (argc < 2) return usage(1);
\r
835 /* Are we editing on the command line? */
\r
836 enum { MODE_EDITING, MODE_GETTING, MODE_SETTING, MODE_RESETTING } mode = MODE_EDITING;
\r
837 const TCHAR *verb = argv[0];
\r
838 const TCHAR *service_name = argv[1];
\r
839 bool getting = false;
\r
840 bool unsetting = false;
\r
842 /* Minimum number of arguments. */
\r
844 /* Index of first value. */
\r
847 if (str_equiv(verb, _T("get"))) {
\r
849 mode = MODE_GETTING;
\r
851 else if (str_equiv(verb, _T("set"))) {
\r
853 mode = MODE_SETTING;
\r
855 else if (str_equiv(verb, _T("reset")) || str_equiv(verb, _T("unset"))) {
\r
857 mode = MODE_RESETTING;
\r
859 if (argc < mandatory) return usage(1);
\r
861 const TCHAR *parameter = 0;
\r
862 settings_t *setting = 0;
\r
865 /* Validate the parameter. */
\r
866 if (mandatory > 2) {
\r
867 bool additional_mandatory = false;
\r
869 parameter = argv[2];
\r
870 for (i = 0; settings[i].name; i++) {
\r
871 setting = &settings[i];
\r
872 if (! str_equiv(setting->name, parameter)) continue;
\r
873 if (((setting->additional & ADDITIONAL_GETTING) && mode == MODE_GETTING) || ((setting->additional & ADDITIONAL_SETTING) && mode == MODE_SETTING) || ((setting->additional & ADDITIONAL_RESETTING) && mode == MODE_RESETTING)) {
\r
874 additional_mandatory = true;
\r
879 if (! settings[i].name) {
\r
880 print_message(stderr, NSSM_MESSAGE_INVALID_PARAMETER, parameter);
\r
881 for (i = 0; settings[i].name; i++) _ftprintf(stderr, _T("%s\n"), settings[i].name);
\r
886 if (additional_mandatory) {
\r
887 if (argc < mandatory) {
\r
888 print_message(stderr, NSSM_MESSAGE_MISSING_SUBPARAMETER, parameter);
\r
891 additional = argv[3];
\r
894 else if (str_equiv(setting->name, NSSM_NATIVE_OBJECTNAME) && mode == MODE_SETTING) {
\r
895 additional = argv[3];
\r
899 additional = argv[remainder];
\r
900 if (argc < mandatory) return usage(1);
\r
904 nssm_service_t *service = alloc_nssm_service();
\r
905 _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), service_name);
\r
907 /* Open service manager */
\r
908 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
\r
910 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
914 /* Try to open the service */
\r
915 unsigned long access = SERVICE_QUERY_CONFIG;
\r
916 if (mode != MODE_GETTING) access |= SERVICE_CHANGE_CONFIG;
\r
917 service->handle = open_service(services, service->name, access, service->name, _countof(service->name));
\r
918 if (! service->handle) {
\r
919 CloseServiceHandle(services);
\r
923 /* Get system details. */
\r
924 QUERY_SERVICE_CONFIG *qsc = query_service_config(service->name, service->handle);
\r
926 CloseServiceHandle(service->handle);
\r
927 CloseServiceHandle(services);
\r
931 service->type = qsc->dwServiceType;
\r
932 if (! (service->type & SERVICE_WIN32_OWN_PROCESS)) {
\r
933 if (mode != MODE_GETTING) {
\r
934 HeapFree(GetProcessHeap(), 0, qsc);
\r
935 CloseServiceHandle(service->handle);
\r
936 CloseServiceHandle(services);
\r
937 print_message(stderr, NSSM_MESSAGE_CANNOT_EDIT, service->name, NSSM_WIN32_OWN_PROCESS, 0);
\r
942 if (get_service_startup(service->name, service->handle, qsc, &service->startup)) {
\r
943 if (mode != MODE_GETTING) {
\r
944 HeapFree(GetProcessHeap(), 0, qsc);
\r
945 CloseServiceHandle(service->handle);
\r
946 CloseServiceHandle(services);
\r
951 if (get_service_username(service->name, qsc, &service->username, &service->usernamelen)) {
\r
952 if (mode != MODE_GETTING) {
\r
953 HeapFree(GetProcessHeap(), 0, qsc);
\r
954 CloseServiceHandle(service->handle);
\r
955 CloseServiceHandle(services);
\r
960 _sntprintf_s(service->displayname, _countof(service->displayname), _TRUNCATE, _T("%s"), qsc->lpDisplayName);
\r
962 /* Get the canonical service name. We open it case insensitively. */
\r
963 unsigned long bufsize = _countof(service->name);
\r
964 GetServiceKeyName(services, service->displayname, service->name, &bufsize);
\r
966 /* Remember the executable in case it isn't NSSM. */
\r
967 _sntprintf_s(service->image, _countof(service->image), _TRUNCATE, _T("%s"), qsc->lpBinaryPathName);
\r
968 HeapFree(GetProcessHeap(), 0, qsc);
\r
970 /* Get extended system details. */
\r
971 if (get_service_description(service->name, service->handle, _countof(service->description), service->description)) {
\r
972 if (mode != MODE_GETTING) {
\r
973 CloseServiceHandle(service->handle);
\r
974 CloseServiceHandle(services);
\r
979 if (get_service_dependencies(service->name, service->handle, &service->dependencies, &service->dependencieslen)) {
\r
980 if (mode != MODE_GETTING) {
\r
981 CloseServiceHandle(service->handle);
\r
982 CloseServiceHandle(services);
\r
987 /* Get NSSM details. */
\r
988 get_parameters(service, 0);
\r
990 CloseServiceHandle(services);
\r
992 if (! service->exe[0]) {
\r
993 service->native = true;
\r
994 if (mode != MODE_GETTING) print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE, service->name, NSSM, service->image);
\r
997 /* Editing with the GUI. */
\r
998 if (mode == MODE_EDITING) {
\r
999 nssm_gui(IDD_EDIT, service);
\r
1003 /* Trying to manage App* parameters for a non-NSSM service. */
\r
1004 if (! setting->native && service->native) {
\r
1005 CloseServiceHandle(service->handle);
\r
1006 print_message(stderr, NSSM_MESSAGE_NATIVE_PARAMETER, setting->name, NSSM);
\r
1014 if (mode == MODE_GETTING) {
\r
1015 if (! service->native) {
\r
1016 key = open_registry(service->name, KEY_READ);
\r
1017 if (! key) return 4;
\r
1020 if (setting->native) ret = get_setting(service->name, service->handle, setting, &value, additional);
\r
1021 else ret = get_setting(service->name, key, setting, &value, additional);
\r
1023 CloseServiceHandle(service->handle);
\r
1027 switch (setting->type) {
\r
1028 case REG_EXPAND_SZ:
\r
1029 case REG_MULTI_SZ:
\r
1031 _tprintf(_T("%s\n"), value.string ? value.string : _T(""));
\r
1032 HeapFree(GetProcessHeap(), 0, value.string);
\r
1036 _tprintf(_T("%u\n"), value.numeric);
\r
1040 if (! service->native) RegCloseKey(key);
\r
1041 CloseServiceHandle(service->handle);
\r
1045 /* Build the value. */
\r
1046 if (mode == MODE_RESETTING) {
\r
1047 /* Unset the parameter. */
\r
1050 else if (remainder == argc) {
\r
1054 /* Set the parameter. */
\r
1056 size_t delimiterlen = (setting->additional & ADDITIONAL_CRLF) ? 2 : 1;
\r
1057 for (i = remainder; i < argc; i++) len += _tcslen(argv[i]) + delimiterlen;
\r
1060 value.string = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, len * sizeof(TCHAR));
\r
1061 if (! value.string) {
\r
1062 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("value"), _T("edit_service()"));
\r
1063 CloseServiceHandle(service->handle);
\r
1068 for (i = remainder; i < argc; i++) {
\r
1069 size_t len = _tcslen(argv[i]);
\r
1070 memmove(value.string + s, argv[i], len * sizeof(TCHAR));
\r
1072 if (i < argc - 1) {
\r
1073 if (setting->additional & ADDITIONAL_CRLF) {
\r
1074 value.string[s++] = _T('\r');
\r
1075 value.string[s++] = _T('\n');
\r
1077 else value.string[s++] = _T(' ');
\r
1080 value.string[s] = _T('\0');
\r
1083 if (! service->native) {
\r
1084 key = open_registry(service->name, KEY_READ | KEY_WRITE);
\r
1086 if (value.string) HeapFree(GetProcessHeap(), 0, value.string);
\r
1091 if (setting->native) ret = set_setting(service->name, service->handle, setting, &value, additional);
\r
1092 else ret = set_setting(service->name, key, setting, &value, additional);
\r
1093 if (value.string) HeapFree(GetProcessHeap(), 0, value.string);
\r
1095 if (! service->native) RegCloseKey(key);
\r
1096 CloseServiceHandle(service->handle);
\r
1100 if (! service->native) RegCloseKey(key);
\r
1101 CloseServiceHandle(service->handle);
\r
1106 /* About to remove the service */
\r
1107 int pre_remove_service(int argc, TCHAR **argv) {
\r
1108 nssm_service_t *service = alloc_nssm_service();
\r
1109 set_nssm_service_defaults(service);
\r
1110 if (argc) _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]);
\r
1112 /* Show dialogue box if we didn't pass service name and "confirm" */
\r
1113 if (argc < 2) return nssm_gui(IDD_REMOVE, service);
\r
1114 if (str_equiv(argv[1], _T("confirm"))) {
\r
1115 int ret = remove_service(service);
\r
1116 cleanup_nssm_service(service);
\r
1119 print_message(stderr, NSSM_MESSAGE_PRE_REMOVE_SERVICE);
\r
1123 /* Install the service */
\r
1124 int install_service(nssm_service_t *service) {
\r
1125 if (! service) return 1;
\r
1127 /* Open service manager */
\r
1128 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE);
\r
1130 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
1131 cleanup_nssm_service(service);
\r
1135 /* Get path of this program */
\r
1136 _sntprintf_s(service->image, _countof(service->image), _TRUNCATE, _T("%s"), nssm_imagepath());
\r
1138 /* Create the service - settings will be changed in edit_service() */
\r
1139 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
1140 if (! service->handle) {
\r
1141 print_message(stderr, NSSM_MESSAGE_CREATESERVICE_FAILED, error_string(GetLastError()));
\r
1142 CloseServiceHandle(services);
\r
1146 if (edit_service(service, false)) {
\r
1147 DeleteService(service->handle);
\r
1148 CloseServiceHandle(services);
\r
1152 print_message(stdout, NSSM_MESSAGE_SERVICE_INSTALLED, service->name);
\r
1155 CloseServiceHandle(services);
\r
1160 /* Edit the service. */
\r
1161 int edit_service(nssm_service_t *service, bool editing) {
\r
1162 if (! service) return 1;
\r
1165 The only two valid flags for service type are SERVICE_WIN32_OWN_PROCESS
\r
1166 and SERVICE_INTERACTIVE_PROCESS.
\r
1168 service->type &= SERVICE_INTERACTIVE_PROCESS;
\r
1169 service->type |= SERVICE_WIN32_OWN_PROCESS;
\r
1171 /* Startup type. */
\r
1172 unsigned long startup;
\r
1173 switch (service->startup) {
\r
1174 case NSSM_STARTUP_MANUAL: startup = SERVICE_DEMAND_START; break;
\r
1175 case NSSM_STARTUP_DISABLED: startup = SERVICE_DISABLED; break;
\r
1176 default: startup = SERVICE_AUTO_START;
\r
1179 /* Display name. */
\r
1180 if (! service->displayname[0]) _sntprintf_s(service->displayname, _countof(service->displayname), _TRUNCATE, _T("%s"), service->name);
\r
1183 Username must be NULL if we aren't changing or an account name.
\r
1184 We must explicitly use LOCALSYSTEM to change it when we are editing.
\r
1185 Password must be NULL if we aren't changing, a password or "".
\r
1186 Empty passwords are valid but we won't allow them in the GUI.
\r
1188 TCHAR *username = 0;
\r
1190 TCHAR *password = 0;
\r
1191 if (service->usernamelen) {
\r
1192 username = service->username;
\r
1193 if (canonicalise_username(username, &canon)) return 5;
\r
1194 if (service->passwordlen) password = service->password;
\r
1196 else if (editing) username = canon = NSSM_LOCALSYSTEM_ACCOUNT;
\r
1198 if (well_known_username(canon)) password = _T("");
\r
1200 if (grant_logon_as_service(canon)) {
\r
1201 if (canon != username) HeapFree(GetProcessHeap(), 0, canon);
\r
1202 print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username);
\r
1207 TCHAR *dependencies = _T("");
\r
1208 if (service->dependencieslen) dependencies = 0; /* Change later. */
\r
1210 if (! ChangeServiceConfig(service->handle, service->type, startup, SERVICE_NO_CHANGE, 0, 0, 0, dependencies, canon, password, service->displayname)) {
\r
1211 if (canon != username) HeapFree(GetProcessHeap(), 0, canon);
\r
1212 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
1215 if (canon != username) HeapFree(GetProcessHeap(), 0, canon);
\r
1217 if (service->dependencieslen) {
\r
1218 if (set_service_dependencies(service->name, service->handle, service->dependencies)) return 5;
\r
1221 if (service->description[0] || editing) {
\r
1222 set_service_description(service->name, service->handle, service->description);
\r
1225 SERVICE_DELAYED_AUTO_START_INFO delayed;
\r
1226 ZeroMemory(&delayed, sizeof(delayed));
\r
1227 if (service->startup == NSSM_STARTUP_DELAYED) delayed.fDelayedAutostart = 1;
\r
1228 else delayed.fDelayedAutostart = 0;
\r
1229 /* Delayed startup isn't supported until Vista. */
\r
1230 if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, &delayed)) {
\r
1231 unsigned long error = GetLastError();
\r
1232 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
\r
1233 if (error != ERROR_INVALID_LEVEL) {
\r
1234 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_DELAYED_AUTO_START_INFO_FAILED, service->name, error_string(error), 0);
\r
1238 /* Don't mess with parameters which aren't ours. */
\r
1239 if (! service->native) {
\r
1240 /* Now we need to put the parameters into the registry */
\r
1241 if (create_parameters(service, editing)) {
\r
1242 print_message(stderr, NSSM_MESSAGE_CREATE_PARAMETERS_FAILED);
\r
1246 set_service_recovery(service);
\r
1252 /* Control a service. */
\r
1253 int control_service(unsigned long control, int argc, TCHAR **argv) {
\r
1254 if (argc < 1) return usage(1);
\r
1255 TCHAR *service_name = argv[0];
\r
1256 TCHAR canonical_name[SERVICE_NAME_LENGTH];
\r
1258 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
\r
1260 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
1264 unsigned long access = SERVICE_QUERY_STATUS;
\r
1265 switch (control) {
\r
1266 case NSSM_SERVICE_CONTROL_START:
\r
1267 access |= SERVICE_START;
\r
1270 case SERVICE_CONTROL_CONTINUE:
\r
1271 case SERVICE_CONTROL_PAUSE:
\r
1272 access |= SERVICE_PAUSE_CONTINUE;
\r
1275 case SERVICE_CONTROL_STOP:
\r
1276 access |= SERVICE_STOP;
\r
1279 case NSSM_SERVICE_CONTROL_ROTATE:
\r
1280 access |= SERVICE_USER_DEFINED_CONTROL;
\r
1284 SC_HANDLE service_handle = open_service(services, service_name, access, canonical_name, _countof(canonical_name));
\r
1285 if (! service_handle) {
\r
1286 CloseServiceHandle(services);
\r
1291 unsigned long error;
\r
1292 SERVICE_STATUS service_status;
\r
1293 if (control == NSSM_SERVICE_CONTROL_START) {
\r
1294 unsigned long initial_status = SERVICE_STOPPED;
\r
1295 ret = StartService(service_handle, (unsigned long) argc, (const TCHAR **) argv);
\r
1296 error = GetLastError();
\r
1297 CloseServiceHandle(services);
\r
1299 if (error == ERROR_IO_PENDING) {
\r
1301 Older versions of Windows return immediately with ERROR_IO_PENDING
\r
1302 indicate that the operation is still in progress. Newer versions
\r
1303 will return it if there really is a delay.
\r
1306 error = ERROR_SUCCESS;
\r
1310 int response = await_service_control_response(control, service_handle, &service_status, initial_status);
\r
1311 CloseServiceHandle(service_handle);
\r
1314 print_message(stderr, NSSM_MESSAGE_BAD_CONTROL_RESPONSE, canonical_name, service_status_text(service_status.dwCurrentState), service_control_text(control));
\r
1317 else _tprintf(_T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1321 CloseServiceHandle(service_handle);
\r
1322 _ftprintf(stderr, _T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1326 else if (control == SERVICE_CONTROL_INTERROGATE) {
\r
1328 We could actually send an INTERROGATE control but that won't return
\r
1329 any information if the service is stopped and we don't care about
\r
1330 the extra details it might give us in any case. So we'll fake it.
\r
1332 ret = QueryServiceStatus(service_handle, &service_status);
\r
1333 error = GetLastError();
\r
1336 _tprintf(_T("%s\n"), service_status_text(service_status.dwCurrentState));
\r
1340 _ftprintf(stderr, _T("%s: %s\n"), canonical_name, error_string(error));
\r
1345 ret = ControlService(service_handle, control, &service_status);
\r
1346 unsigned long initial_status = service_status.dwCurrentState;
\r
1347 error = GetLastError();
\r
1348 CloseServiceHandle(services);
\r
1350 if (error == ERROR_IO_PENDING) {
\r
1352 error = ERROR_SUCCESS;
\r
1356 int response = await_service_control_response(control, service_handle, &service_status, initial_status);
\r
1357 CloseServiceHandle(service_handle);
\r
1360 print_message(stderr, NSSM_MESSAGE_BAD_CONTROL_RESPONSE, canonical_name, service_status_text(service_status.dwCurrentState), service_control_text(control));
\r
1363 else _tprintf(_T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1367 CloseServiceHandle(service_handle);
\r
1368 _ftprintf(stderr, _T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1369 if (error == ERROR_SERVICE_NOT_ACTIVE) {
\r
1370 if (control == SERVICE_CONTROL_SHUTDOWN || control == SERVICE_CONTROL_STOP) return 0;
\r
1377 /* Remove the service */
\r
1378 int remove_service(nssm_service_t *service) {
\r
1379 if (! service) return 1;
\r
1381 /* Open service manager */
\r
1382 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
\r
1384 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
1388 /* Try to open the service */
\r
1389 service->handle = open_service(services, service->name, DELETE, service->name, _countof(service->name));
\r
1390 if (! service->handle) {
\r
1391 CloseServiceHandle(services);
\r
1395 /* Get the canonical service name. We open it case insensitively. */
\r
1396 unsigned long bufsize = _countof(service->displayname);
\r
1397 GetServiceDisplayName(services, service->name, service->displayname, &bufsize);
\r
1398 bufsize = _countof(service->name);
\r
1399 GetServiceKeyName(services, service->displayname, service->name, &bufsize);
\r
1401 /* Try to delete the service */
\r
1402 if (! DeleteService(service->handle)) {
\r
1403 print_message(stderr, NSSM_MESSAGE_DELETESERVICE_FAILED);
\r
1404 CloseServiceHandle(services);
\r
1409 CloseServiceHandle(services);
\r
1411 print_message(stdout, NSSM_MESSAGE_SERVICE_REMOVED, service->name);
\r
1415 /* Service initialisation */
\r
1416 void WINAPI service_main(unsigned long argc, TCHAR **argv) {
\r
1417 nssm_service_t *service = alloc_nssm_service();
\r
1418 if (! service) return;
\r
1420 if (_sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]) < 0) {
\r
1421 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("service->name"), _T("service_main()"), 0);
\r
1425 /* We can use a condition variable in a critical section on Vista or later. */
\r
1426 if (imports.SleepConditionVariableCS && imports.WakeConditionVariable) use_critical_section = true;
\r
1427 else use_critical_section = false;
\r
1429 /* Initialise status */
\r
1430 ZeroMemory(&service->status, sizeof(service->status));
\r
1431 service->status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;
\r
1432 service->status.dwControlsAccepted = 0;
\r
1433 service->status.dwWin32ExitCode = NO_ERROR;
\r
1434 service->status.dwServiceSpecificExitCode = 0;
\r
1435 service->status.dwCheckPoint = 0;
\r
1436 service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;
\r
1438 /* Signal we AREN'T running the server */
\r
1439 service->process_handle = 0;
\r
1442 /* Register control handler */
\r
1443 service->status_handle = RegisterServiceCtrlHandlerEx(NSSM, service_control_handler, (void *) service);
\r
1444 if (! service->status_handle) {
\r
1445 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_REGISTERSERVICECTRLHANDER_FAILED, error_string(GetLastError()), 0);
\r
1449 log_service_control(service->name, 0, true);
\r
1451 service->status.dwCurrentState = SERVICE_START_PENDING;
\r
1452 service->status.dwWaitHint = service->throttle_delay + NSSM_WAITHINT_MARGIN;
\r
1453 SetServiceStatus(service->status_handle, &service->status);
\r
1456 /* Try to create the exit action parameters; we don't care if it fails */
\r
1457 create_exit_action(service->name, exit_action_strings[0], false);
\r
1459 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT);
\r
1461 service->handle = open_service(services, service->name, SERVICE_CHANGE_CONFIG, 0, 0);
\r
1462 set_service_recovery(service);
\r
1464 /* Remember our display name. */
\r
1465 unsigned long displayname_len = _countof(service->displayname);
\r
1466 GetServiceDisplayName(services, service->name, service->displayname, &displayname_len);
\r
1468 CloseServiceHandle(services);
\r
1472 /* Used for signalling a resume if the service pauses when throttled. */
\r
1473 if (use_critical_section) {
\r
1474 InitializeCriticalSection(&service->throttle_section);
\r
1475 service->throttle_section_initialised = true;
\r
1478 service->throttle_timer = CreateWaitableTimer(0, 1, 0);
\r
1479 if (! service->throttle_timer) {
\r
1480 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_CREATEWAITABLETIMER_FAILED, service->name, error_string(GetLastError()), 0);
\r
1484 /* Critical section for hooks. */
\r
1485 InitializeCriticalSection(&service->hook_section);
\r
1486 service->hook_section_initialised = true;
\r
1488 /* Remember our initial environment. */
\r
1489 service->initial_env = copy_environment();
\r
1491 /* Remember our creation time. */
\r
1492 if (get_process_creation_time(GetCurrentProcess(), &service->nssm_creation_time)) ZeroMemory(&service->nssm_creation_time, sizeof(service->nssm_creation_time));
\r
1494 service->allow_restart = true;
\r
1495 if (! CreateThread(NULL, 0, launch_service, (void *) service, 0, NULL)) {
\r
1496 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETHREAD_FAILED, error_string(GetLastError()), 0);
\r
1497 stop_service(service, 0, true, true);
\r
1501 /* Make sure service recovery actions are taken where necessary */
\r
1502 void set_service_recovery(nssm_service_t *service) {
\r
1503 SERVICE_FAILURE_ACTIONS_FLAG flag;
\r
1504 ZeroMemory(&flag, sizeof(flag));
\r
1505 flag.fFailureActionsOnNonCrashFailures = true;
\r
1507 /* This functionality was added in Vista so the call may fail */
\r
1508 if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, &flag)) {
\r
1509 unsigned long error = GetLastError();
\r
1510 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
\r
1511 if (error != ERROR_INVALID_LEVEL) {
\r
1512 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_FAILURE_ACTIONS_FAILED, service->name, error_string(error), 0);
\r
1517 int monitor_service(nssm_service_t *service) {
\r
1518 /* Set service status to started */
\r
1519 int ret = start_service(service);
\r
1522 _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%d"), ret);
\r
1523 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_START_SERVICE_FAILED, service->exe, service->name, ret, 0);
\r
1526 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_STARTED_SERVICE, service->exe, service->flags, service->name, service->dir, 0);
\r
1528 /* Monitor service */
\r
1529 if (! RegisterWaitForSingleObject(&service->wait_handle, service->process_handle, end_service, (void *) service, INFINITE, WT_EXECUTEONLYONCE | WT_EXECUTELONGFUNCTION)) {
\r
1530 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_REGISTERWAITFORSINGLEOBJECT_FAILED, service->name, service->exe, error_string(GetLastError()), 0);
\r
1536 TCHAR *service_control_text(unsigned long control) {
\r
1537 switch (control) {
\r
1538 /* HACK: there is no SERVICE_CONTROL_START constant */
\r
1539 case NSSM_SERVICE_CONTROL_START: return _T("START");
\r
1540 case SERVICE_CONTROL_STOP: return _T("STOP");
\r
1541 case SERVICE_CONTROL_SHUTDOWN: return _T("SHUTDOWN");
\r
1542 case SERVICE_CONTROL_PAUSE: return _T("PAUSE");
\r
1543 case SERVICE_CONTROL_CONTINUE: return _T("CONTINUE");
\r
1544 case SERVICE_CONTROL_INTERROGATE: return _T("INTERROGATE");
\r
1545 case NSSM_SERVICE_CONTROL_ROTATE: return _T("ROTATE");
\r
1546 case SERVICE_CONTROL_POWEREVENT: return _T("POWEREVENT");
\r
1547 default: return 0;
\r
1551 TCHAR *service_status_text(unsigned long status) {
\r
1553 case SERVICE_STOPPED: return _T("SERVICE_STOPPED");
\r
1554 case SERVICE_START_PENDING: return _T("SERVICE_START_PENDING");
\r
1555 case SERVICE_STOP_PENDING: return _T("SERVICE_STOP_PENDING");
\r
1556 case SERVICE_RUNNING: return _T("SERVICE_RUNNING");
\r
1557 case SERVICE_CONTINUE_PENDING: return _T("SERVICE_CONTINUE_PENDING");
\r
1558 case SERVICE_PAUSE_PENDING: return _T("SERVICE_PAUSE_PENDING");
\r
1559 case SERVICE_PAUSED: return _T("SERVICE_PAUSED");
\r
1560 default: return 0;
\r
1564 void log_service_control(TCHAR *service_name, unsigned long control, bool handled) {
\r
1565 TCHAR *text = service_control_text(control);
\r
1566 unsigned long event;
\r
1569 /* "0x" + 8 x hex + NULL */
\r
1570 text = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, 11 * sizeof(TCHAR));
\r
1572 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);
\r
1575 if (_sntprintf_s(text, 11, _TRUNCATE, _T("0x%08x"), control) < 0) {
\r
1576 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);
\r
1577 HeapFree(GetProcessHeap(), 0, text);
\r
1581 event = NSSM_EVENT_SERVICE_CONTROL_UNKNOWN;
\r
1583 else if (handled) event = NSSM_EVENT_SERVICE_CONTROL_HANDLED;
\r
1584 else event = NSSM_EVENT_SERVICE_CONTROL_NOT_HANDLED;
\r
1586 log_event(EVENTLOG_INFORMATION_TYPE, event, service_name, text, 0);
\r
1588 if (event == NSSM_EVENT_SERVICE_CONTROL_UNKNOWN) {
\r
1589 HeapFree(GetProcessHeap(), 0, text);
\r
1593 /* Service control handler */
\r
1594 unsigned long WINAPI service_control_handler(unsigned long control, unsigned long event, void *data, void *context) {
\r
1595 nssm_service_t *service = (nssm_service_t *) context;
\r
1597 switch (control) {
\r
1598 case SERVICE_CONTROL_INTERROGATE:
\r
1599 /* We always keep the service status up-to-date so this is a no-op. */
\r
1602 case SERVICE_CONTROL_SHUTDOWN:
\r
1603 case SERVICE_CONTROL_STOP:
\r
1604 service->last_control = control;
\r
1605 log_service_control(service->name, control, true);
\r
1607 /* Immediately block further controls. */
\r
1608 service->allow_restart = false;
\r
1609 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
1610 service->status.dwControlsAccepted = 0;
\r
1611 SetServiceStatus(service->status_handle, &service->status);
\r
1613 /* Pre-stop hook. */
\r
1614 nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_STOP, NSSM_HOOK_ACTION_PRE, &control, NSSM_SERVICE_STATUS_DEADLINE, false);
\r
1617 We MUST acknowledge the stop request promptly but we're committed to
\r
1618 waiting for the application to exit. Spawn a new thread to wait
\r
1619 while we acknowledge the request.
\r
1621 if (! CreateThread(NULL, 0, shutdown_service, context, 0, NULL)) {
\r
1622 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETHREAD_FAILED, error_string(GetLastError()), 0);
\r
1625 We couldn't create a thread to tidy up so we'll have to force the tidyup
\r
1626 to complete in time in this thread.
\r
1628 service->kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;
\r
1629 service->kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;
\r
1630 service->kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;
\r
1632 stop_service(service, 0, true, true);
\r
1636 case SERVICE_CONTROL_CONTINUE:
\r
1637 service->last_control = control;
\r
1638 log_service_control(service->name, control, true);
\r
1639 service->throttle = 0;
\r
1640 if (use_critical_section) imports.WakeConditionVariable(&service->throttle_condition);
\r
1642 if (! service->throttle_timer) return ERROR_CALL_NOT_IMPLEMENTED;
\r
1643 ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));
\r
1644 SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);
\r
1646 /* We can't continue if the application is running! */
\r
1647 if (! service->process_handle) service->status.dwCurrentState = SERVICE_CONTINUE_PENDING;
\r
1648 service->status.dwWaitHint = throttle_milliseconds(service->throttle) + NSSM_WAITHINT_MARGIN;
\r
1649 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESET_THROTTLE, service->name, 0);
\r
1650 SetServiceStatus(service->status_handle, &service->status);
\r
1653 case SERVICE_CONTROL_PAUSE:
\r
1655 We don't accept pause messages but it isn't possible to register
\r
1656 only for continue messages so we have to handle this case.
\r
1658 log_service_control(service->name, control, false);
\r
1659 return ERROR_CALL_NOT_IMPLEMENTED;
\r
1661 case NSSM_SERVICE_CONTROL_ROTATE:
\r
1662 service->last_control = control;
\r
1663 log_service_control(service->name, control, true);
\r
1664 (void) nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_ROTATE, NSSM_HOOK_ACTION_PRE, &control, NSSM_HOOK_DEADLINE, false);
\r
1665 if (service->rotate_stdout_online == NSSM_ROTATE_ONLINE) service->rotate_stdout_online = NSSM_ROTATE_ONLINE_ASAP;
\r
1666 if (service->rotate_stderr_online == NSSM_ROTATE_ONLINE) service->rotate_stderr_online = NSSM_ROTATE_ONLINE_ASAP;
\r
1667 (void) nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_ROTATE, NSSM_HOOK_ACTION_POST, &control);
\r
1670 case SERVICE_CONTROL_POWEREVENT:
\r
1671 /* Resume from suspend. */
\r
1672 if (event == PBT_APMRESUMEAUTOMATIC) {
\r
1673 service->last_control = control;
\r
1674 log_service_control(service->name, control, true);
\r
1675 (void) nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_POWER, NSSM_HOOK_ACTION_RESUME, &control);
\r
1679 /* Battery low or changed to A/C power or something. */
\r
1680 if (event == PBT_APMPOWERSTATUSCHANGE) {
\r
1681 service->last_control = control;
\r
1682 log_service_control(service->name, control, true);
\r
1683 (void) nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_POWER, NSSM_HOOK_ACTION_CHANGE, &control);
\r
1686 log_service_control(service->name, control, false);
\r
1690 /* Unknown control */
\r
1691 log_service_control(service->name, control, false);
\r
1692 return ERROR_CALL_NOT_IMPLEMENTED;
\r
1695 /* Start the service */
\r
1696 int start_service(nssm_service_t *service) {
\r
1697 service->stopping = false;
\r
1699 if (service->process_handle) return 0;
\r
1700 service->start_requested_count++;
\r
1702 /* Allocate a STARTUPINFO structure for a new process */
\r
1704 ZeroMemory(&si, sizeof(si));
\r
1705 si.cb = sizeof(si);
\r
1707 /* Allocate a PROCESSINFO structure for the process */
\r
1708 PROCESS_INFORMATION pi;
\r
1709 ZeroMemory(&pi, sizeof(pi));
\r
1711 /* Get startup parameters */
\r
1712 int ret = get_parameters(service, &si);
\r
1714 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_PARAMETERS_FAILED, service->name, 0);
\r
1715 unset_service_environment(service);
\r
1716 return stop_service(service, 2, true, true);
\r
1719 /* Launch executable with arguments */
\r
1720 TCHAR cmd[CMD_LENGTH];
\r
1721 if (_sntprintf_s(cmd, _countof(cmd), _TRUNCATE, _T("\"%s\" %s"), service->exe, service->flags) < 0) {
\r
1722 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("command line"), _T("start_service"), 0);
\r
1723 unset_service_environment(service);
\r
1724 return stop_service(service, 2, true, true);
\r
1727 throttle_restart(service);
\r
1729 service->status.dwCurrentState = SERVICE_START_PENDING;
\r
1730 service->status.dwControlsAccepted = SERVICE_ACCEPT_POWEREVENT | SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP;
\r
1731 SetServiceStatus(service->status_handle, &service->status);
\r
1733 unsigned long control = NSSM_SERVICE_CONTROL_START;
\r
1735 /* Did another thread receive a stop control? */
\r
1736 if (service->allow_restart) {
\r
1737 /* Set up I/O redirection. */
\r
1738 if (get_output_handles(service, &si)) {
\r
1739 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_OUTPUT_HANDLES_FAILED, service->name, 0);
\r
1740 if (! service->no_console) FreeConsole();
\r
1741 close_output_handles(&si);
\r
1742 unset_service_environment(service);
\r
1743 return stop_service(service, 4, true, true);
\r
1746 /* Pre-start hook. May need I/O to have been redirected already. */
\r
1747 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
1749 _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), NSSM_HOOK_STATUS_ABORT);
\r
1750 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_PRESTART_HOOK_ABORT, NSSM_HOOK_EVENT_START, NSSM_HOOK_ACTION_PRE, service->name, code, 0);
\r
1751 unset_service_environment(service);
\r
1752 return stop_service(service, 5, true, true);
\r
1755 /* The pre-start hook will have cleaned the environment. */
\r
1756 set_service_environment(service);
\r
1758 bool inherit_handles = false;
\r
1759 if (si.dwFlags & STARTF_USESTDHANDLES) inherit_handles = true;
\r
1760 unsigned long flags = service->priority & priority_mask();
\r
1761 if (service->affinity) flags |= CREATE_SUSPENDED;
\r
1762 if (! CreateProcess(0, cmd, 0, 0, inherit_handles, flags, 0, service->dir, &si, &pi)) {
\r
1763 unsigned long exitcode = 3;
\r
1764 unsigned long error = GetLastError();
\r
1765 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service->name, service->exe, error_string(error), 0);
\r
1766 close_output_handles(&si);
\r
1767 unset_service_environment(service);
\r
1768 return stop_service(service, exitcode, true, true);
\r
1770 service->start_count++;
\r
1771 service->process_handle = pi.hProcess;
\r
1772 service->pid = pi.dwProcessId;
\r
1774 if (get_process_creation_time(service->process_handle, &service->creation_time)) ZeroMemory(&service->creation_time, sizeof(service->creation_time));
\r
1776 close_output_handles(&si);
\r
1778 if (! service->no_console) FreeConsole();
\r
1780 if (service->affinity) {
\r
1782 We are explicitly storing service->affinity as a 64-bit unsigned integer
\r
1783 so that we can parse it regardless of whether we're running in 32-bit
\r
1784 or 64-bit mode. The arguments to SetProcessAffinityMask(), however, are
\r
1785 defined as type DWORD_PTR and hence limited to 32 bits on a 32-bit system
\r
1786 (or when running the 32-bit NSSM).
\r
1788 The result is a lot of seemingly-unnecessary casting throughout the code
\r
1789 and potentially confusion when we actually try to start the service.
\r
1790 Having said that, however, it's unlikely that we're actually going to
\r
1791 run in 32-bit mode on a system which has more than 32 CPUs so the
\r
1792 likelihood of seeing a confusing situation is somewhat diminished.
\r
1794 DWORD_PTR affinity, system_affinity;
\r
1796 if (GetProcessAffinityMask(service->process_handle, &affinity, &system_affinity)) affinity = service->affinity & system_affinity;
\r
1798 affinity = (DWORD_PTR) service->affinity;
\r
1799 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GETPROCESSAFFINITYMASK_FAILED, service->name, error_string(GetLastError()), 0);
\r
1802 if (! SetProcessAffinityMask(service->process_handle, affinity)) {
\r
1803 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_SETPROCESSAFFINITYMASK_FAILED, service->name, error_string(GetLastError()), 0);
\r
1806 ResumeThread(pi.hThread);
\r
1810 /* Restore our environment. */
\r
1811 unset_service_environment(service);
\r
1814 Wait for a clean startup before changing the service status to RUNNING
\r
1815 but be mindful of the fact that we are blocking the service control manager
\r
1816 so abandon the wait before too much time has elapsed.
\r
1818 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
1820 /* Did another thread receive a stop control? */
\r
1821 if (! service->allow_restart) return 0;
\r
1823 /* Signal successful start */
\r
1824 service->status.dwCurrentState = SERVICE_RUNNING;
\r
1825 service->status.dwControlsAccepted &= ~SERVICE_ACCEPT_PAUSE_CONTINUE;
\r
1826 SetServiceStatus(service->status_handle, &service->status);
\r
1828 /* Post-start hook. */
\r
1829 if (! service->throttle) {
\r
1830 (void) nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_START, NSSM_HOOK_ACTION_POST, &control);
\r
1833 /* Ensure the restart delay is always applied. */
\r
1834 if (service->restart_delay && ! service->throttle) service->throttle++;
\r
1839 /* Stop the service */
\r
1840 int stop_service(nssm_service_t *service, unsigned long exitcode, bool graceful, bool default_action) {
\r
1841 service->allow_restart = false;
\r
1842 if (service->wait_handle) {
\r
1843 UnregisterWait(service->wait_handle);
\r
1844 service->wait_handle = 0;
\r
1847 service->rotate_stdout_online = service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;
\r
1849 if (default_action && ! exitcode && ! graceful) {
\r
1850 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
1854 /* Signal we are stopping */
\r
1856 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
1857 service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;
\r
1858 SetServiceStatus(service->status_handle, &service->status);
\r
1861 /* Nothing to do if service isn't running */
\r
1862 if (service->pid) {
\r
1863 /* Shut down service */
\r
1864 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_TERMINATEPROCESS, service->name, service->exe, 0);
\r
1866 service_kill_t(service, &k);
\r
1870 else log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_PROCESS_ALREADY_STOPPED, service->name, service->exe, 0);
\r
1872 end_service((void *) service, true);
\r
1874 /* Signal we stopped */
\r
1876 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
1877 wait_for_hooks(service, true);
\r
1878 service->status.dwCurrentState = SERVICE_STOPPED;
\r
1880 service->status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
\r
1881 service->status.dwServiceSpecificExitCode = exitcode;
\r
1884 service->status.dwWin32ExitCode = NO_ERROR;
\r
1885 service->status.dwServiceSpecificExitCode = 0;
\r
1887 SetServiceStatus(service->status_handle, &service->status);
\r
1893 /* Callback function triggered when the server exits */
\r
1894 void CALLBACK end_service(void *arg, unsigned char why) {
\r
1895 nssm_service_t *service = (nssm_service_t *) arg;
\r
1897 if (service->stopping) return;
\r
1899 service->stopping = true;
\r
1901 service->rotate_stdout_online = service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;
\r
1903 /* Use now as a dummy exit time. */
\r
1904 GetSystemTimeAsFileTime(&service->exit_time);
\r
1906 /* Check exit code */
\r
1907 unsigned long exitcode = 0;
\r
1909 if (service->process_handle) {
\r
1910 GetExitCodeProcess(service->process_handle, &exitcode);
\r
1911 service->exitcode = exitcode;
\r
1912 /* Check real exit time. */
\r
1913 if (exitcode != STILL_ACTIVE) get_process_exit_time(service->process_handle, &service->exit_time);
\r
1914 CloseHandle(service->process_handle);
\r
1917 service->process_handle = 0;
\r
1920 Log that the service ended BEFORE logging about killing the process
\r
1921 tree. See below for the possible values of the why argument.
\r
1924 _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), exitcode);
\r
1925 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ENDED_SERVICE, service->exe, service->name, code, 0);
\r
1929 if (exitcode == STILL_ACTIVE) exitcode = 0;
\r
1930 if (service->pid && service->kill_process_tree) {
\r
1932 service_kill_t(service, &k);
\r
1933 kill_process_tree(&k, service->pid);
\r
1938 service->exit_count++;
\r
1939 (void) nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_EXIT, NSSM_HOOK_ACTION_POST, NULL, NSSM_HOOK_DEADLINE, true);
\r
1942 The why argument is true if our wait timed out or false otherwise.
\r
1943 Our wait is infinite so why will never be true when called by the system.
\r
1944 If it is indeed true, assume we were called from stop_service() because
\r
1945 this is a controlled shutdown, and don't take any restart action.
\r
1948 if (! service->allow_restart) return;
\r
1950 /* What action should we take? */
\r
1951 int action = NSSM_EXIT_RESTART;
\r
1952 TCHAR action_string[ACTION_LEN];
\r
1953 bool default_action;
\r
1954 if (! get_exit_action(service->name, &exitcode, action_string, &default_action)) {
\r
1955 for (int i = 0; exit_action_strings[i]; i++) {
\r
1956 if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
1964 /* Try to restart the service or return failure code to service manager */
\r
1965 case NSSM_EXIT_RESTART:
\r
1966 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_RESTART, service->name, code, exit_action_strings[action], service->exe, 0);
\r
1967 while (monitor_service(service)) {
\r
1968 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_RESTART_SERVICE_FAILED, service->exe, service->name, 0);
\r
1973 /* Do nothing, just like srvany would */
\r
1974 case NSSM_EXIT_IGNORE:
\r
1975 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_IGNORE, service->name, code, exit_action_strings[action], service->exe, 0);
\r
1976 wait_for_hooks(service, false);
\r
1980 /* Tell the service manager we are finished */
\r
1981 case NSSM_EXIT_REALLY:
\r
1982 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_REALLY, service->name, code, exit_action_strings[action], 0);
\r
1983 stop_service(service, exitcode, true, default_action);
\r
1986 /* Fake a crash so pre-Vista service managers will run recovery actions. */
\r
1987 case NSSM_EXIT_UNCLEAN:
\r
1988 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_UNCLEAN, service->name, code, exit_action_strings[action], 0);
\r
1989 stop_service(service, exitcode, false, default_action);
\r
1990 wait_for_hooks(service, false);
\r
1996 void throttle_restart(nssm_service_t *service) {
\r
1997 /* This can't be a restart if the service is already running. */
\r
1998 if (! service->throttle++) return;
\r
2001 unsigned long throttle_ms = throttle_milliseconds(service->throttle);
\r
2002 TCHAR threshold[8], milliseconds[8];
\r
2004 if (service->restart_delay > throttle_ms) ms = service->restart_delay;
\r
2005 else ms = throttle_ms;
\r
2007 _sntprintf_s(milliseconds, _countof(milliseconds), _TRUNCATE, _T("%lu"), ms);
\r
2009 if (service->throttle == 1 && service->restart_delay > throttle_ms) log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESTART_DELAY, service->name, milliseconds, 0);
\r
2011 _sntprintf_s(threshold, _countof(threshold), _TRUNCATE, _T("%lu"), service->throttle_delay);
\r
2012 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_THROTTLED, service->name, threshold, milliseconds, 0);
\r
2015 if (use_critical_section) EnterCriticalSection(&service->throttle_section);
\r
2016 else if (service->throttle_timer) {
\r
2017 ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));
\r
2018 service->throttle_duetime.QuadPart = 0 - (ms * 10000LL);
\r
2019 SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);
\r
2022 service->status.dwCurrentState = SERVICE_PAUSED;
\r
2023 service->status.dwControlsAccepted |= SERVICE_ACCEPT_PAUSE_CONTINUE;
\r
2024 SetServiceStatus(service->status_handle, &service->status);
\r
2026 if (use_critical_section) {
\r
2027 imports.SleepConditionVariableCS(&service->throttle_condition, &service->throttle_section, ms);
\r
2028 LeaveCriticalSection(&service->throttle_section);
\r
2031 if (service->throttle_timer) WaitForSingleObject(service->throttle_timer, INFINITE);
\r
2037 When responding to a stop (or any other) request we need to set dwWaitHint to
\r
2038 the number of milliseconds we expect the operation to take, and optionally
\r
2039 increase dwCheckPoint. If dwWaitHint milliseconds elapses without the
\r
2040 operation completing or dwCheckPoint increasing, the system will consider the
\r
2041 service to be hung.
\r
2043 However the system will consider the service to be hung after 30000
\r
2044 milliseconds regardless of the value of dwWaitHint if dwCheckPoint has not
\r
2045 changed. Therefore if we want to wait longer than that we must periodically
\r
2046 increase dwCheckPoint.
\r
2048 Furthermore, it will consider the service to be hung after 60000 milliseconds
\r
2049 regardless of the value of dwCheckPoint unless dwWaitHint is increased every
\r
2050 time dwCheckPoint is also increased.
\r
2052 Our strategy then is to retrieve the initial dwWaitHint and wait for
\r
2053 NSSM_SERVICE_STATUS_DEADLINE milliseconds. If the process is still running
\r
2054 and we haven't finished waiting we increment dwCheckPoint and add whichever is
\r
2055 smaller of NSSM_SERVICE_STATUS_DEADLINE or the remaining timeout to
\r
2058 Only doing both these things will prevent the system from killing the service.
\r
2060 If the status_handle and service_status arguments are omitted, this function
\r
2061 will not try to update the service manager but it will still log to the
\r
2062 event log that it is waiting for a handle.
\r
2064 Returns: 1 if the wait timed out.
\r
2065 0 if the wait completed.
\r
2068 int await_single_handle(SERVICE_STATUS_HANDLE status_handle, SERVICE_STATUS *status, HANDLE handle, TCHAR *name, TCHAR *function_name, unsigned long timeout) {
\r
2069 unsigned long interval;
\r
2070 unsigned long ret;
\r
2071 unsigned long waited;
\r
2072 TCHAR interval_milliseconds[16];
\r
2073 TCHAR timeout_milliseconds[16];
\r
2074 TCHAR waited_milliseconds[16];
\r
2075 TCHAR *function = function_name;
\r
2077 /* Add brackets to function name. */
\r
2078 size_t funclen = _tcslen(function_name) + 3;
\r
2079 TCHAR *func = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, funclen * sizeof(TCHAR));
\r
2081 if (_sntprintf_s(func, funclen, _TRUNCATE, _T("%s()"), function_name) > -1) function = func;
\r
2084 _sntprintf_s(timeout_milliseconds, _countof(timeout_milliseconds), _TRUNCATE, _T("%lu"), timeout);
\r
2087 while (waited < timeout) {
\r
2088 interval = timeout - waited;
\r
2089 if (interval > NSSM_SERVICE_STATUS_DEADLINE) interval = NSSM_SERVICE_STATUS_DEADLINE;
\r
2092 status->dwWaitHint += interval;
\r
2093 status->dwCheckPoint++;
\r
2094 SetServiceStatus(status_handle, status);
\r
2098 _sntprintf_s(waited_milliseconds, _countof(waited_milliseconds), _TRUNCATE, _T("%lu"), waited);
\r
2099 _sntprintf_s(interval_milliseconds, _countof(interval_milliseconds), _TRUNCATE, _T("%lu"), interval);
\r
2100 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_AWAITING_SINGLE_HANDLE, function, name, waited_milliseconds, interval_milliseconds, timeout_milliseconds, 0);
\r
2103 switch (WaitForSingleObject(handle, interval)) {
\r
2104 case WAIT_OBJECT_0:
\r
2108 case WAIT_TIMEOUT:
\r
2117 waited += interval;
\r
2121 if (func) HeapFree(GetProcessHeap(), 0, func);
\r
2126 int list_nssm_services() {
\r
2127 /* Open service manager. */
\r
2128 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
\r
2130 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
2134 unsigned long bufsize, required, count, i;
\r
2135 unsigned long resume = 0;
\r
2136 EnumServicesStatus(services, SERVICE_WIN32, SERVICE_STATE_ALL, 0, 0, &required, &count, &resume);
\r
2137 unsigned long error = GetLastError();
\r
2138 if (error != ERROR_MORE_DATA) {
\r
2139 print_message(stderr, NSSM_MESSAGE_ENUMSERVICESSTATUS_FAILED, error_string(GetLastError()));
\r
2143 ENUM_SERVICE_STATUS *status = (ENUM_SERVICE_STATUS *) HeapAlloc(GetProcessHeap(), 0, required);
\r
2145 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("ENUM_SERVICE_STATUS"), _T("list_nssm_services()"));
\r
2149 bufsize = required;
\r
2151 int ret = EnumServicesStatus(services, SERVICE_WIN32, SERVICE_STATE_ALL, status, bufsize, &required, &count, &resume);
\r
2153 error = GetLastError();
\r
2154 if (error != ERROR_MORE_DATA) {
\r
2155 HeapFree(GetProcessHeap(), 0, status);
\r
2156 print_message(stderr, NSSM_MESSAGE_ENUMSERVICESSTATUS_FAILED, error_string(GetLastError()));
\r
2161 for (i = 0; i < count; i++) {
\r
2162 /* Try to get the service parameters. */
\r
2163 nssm_service_t *service = alloc_nssm_service();
\r
2165 HeapFree(GetProcessHeap(), 0, status);
\r
2166 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("nssm_service_t"), _T("list_nssm_services()"));
\r
2169 _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), status[i].lpServiceName);
\r
2171 get_parameters(service, 0);
\r
2172 /* We manage the service if we have an Application. */
\r
2173 if (service->exe[0]) _tprintf(_T("%s\n"), service->name);
\r
2175 cleanup_nssm_service(service);
\r
2181 HeapFree(GetProcessHeap(), 0, status);
\r