4 bool use_critical_section;
\r
6 extern imports_t imports;
\r
7 extern settings_t settings[];
\r
9 const TCHAR *exit_action_strings[] = { _T("Restart"), _T("Ignore"), _T("Exit"), _T("Suicide"), 0 };
\r
10 const TCHAR *startup_strings[] = { _T("SERVICE_AUTO_START"), _T("SERVICE_DELAYED_AUTO_START"), _T("SERVICE_DEMAND_START"), _T("SERVICE_DISABLED"), 0 };
\r
11 const TCHAR *priority_strings[] = { _T("REALTIME_PRIORITY_CLASS"), _T("HIGH_PRIORITY_CLASS"), _T("ABOVE_NORMAL_PRIORITY_CLASS"), _T("NORMAL_PRIORITY_CLASS"), _T("BELOW_NORMAL_PRIORITY_CLASS"), _T("IDLE_PRIORITY_CLASS"), 0 };
\r
13 static hook_thread_t hook_threads = { NULL, 0 };
\r
21 Check the status in response to a control.
\r
22 Returns: 1 if the status is expected, eg STOP following CONTROL_STOP.
\r
23 0 if the status is desired, eg STOPPED following CONTROL_STOP.
\r
24 -1 if the status is undesired, eg STOPPED following CONTROL_START.
\r
26 static inline int service_control_response(unsigned long control, unsigned long status) {
\r
28 case NSSM_SERVICE_CONTROL_START:
\r
30 case SERVICE_START_PENDING:
\r
33 case SERVICE_RUNNING:
\r
40 case SERVICE_CONTROL_STOP:
\r
41 case SERVICE_CONTROL_SHUTDOWN:
\r
43 case SERVICE_RUNNING:
\r
44 case SERVICE_STOP_PENDING:
\r
47 case SERVICE_STOPPED:
\r
54 case SERVICE_CONTROL_PAUSE:
\r
56 case SERVICE_PAUSE_PENDING:
\r
59 case SERVICE_PAUSED:
\r
66 case SERVICE_CONTROL_CONTINUE:
\r
68 case SERVICE_CONTINUE_PENDING:
\r
71 case SERVICE_RUNNING:
\r
78 case SERVICE_CONTROL_INTERROGATE:
\r
79 case NSSM_SERVICE_CONTROL_ROTATE:
\r
86 static inline int await_service_control_response(unsigned long control, SC_HANDLE service_handle, SERVICE_STATUS *service_status, unsigned long initial_status) {
\r
88 unsigned long checkpoint = 0;
\r
89 unsigned long waithint = 0;
\r
90 while (QueryServiceStatus(service_handle, service_status)) {
\r
91 int response = service_control_response(control, service_status->dwCurrentState);
\r
92 /* Alas we can't WaitForSingleObject() on an SC_HANDLE. */
\r
93 if (! response) return response;
\r
94 if (response > 0 || service_status->dwCurrentState == initial_status) {
\r
95 if (service_status->dwCheckPoint != checkpoint || service_status->dwWaitHint != waithint) tries = 0;
\r
96 checkpoint = service_status->dwCheckPoint;
\r
97 waithint = service_status->dwWaitHint;
\r
98 if (++tries > 10) tries = 10;
\r
101 else return response;
\r
106 static inline void wait_for_hooks(nssm_service_t *service, bool notify) {
\r
107 SERVICE_STATUS_HANDLE status_handle;
\r
108 SERVICE_STATUS *status;
\r
110 /* On a clean shutdown we need to keep the service's status up-to-date. */
\r
112 status_handle = service->status_handle;
\r
113 status = &service->status;
\r
116 status_handle = NULL;
\r
120 EnterCriticalSection(&service->hook_section);
\r
121 await_hook_threads(&hook_threads, status_handle, status, NSSM_HOOK_THREAD_DEADLINE);
\r
122 LeaveCriticalSection(&service->hook_section);
\r
125 int affinity_mask_to_string(__int64 mask, TCHAR **string) {
\r
126 if (! string) return 1;
\r
134 /* SetProcessAffinityMask() accepts a mask of up to 64 processors. */
\r
136 for (n = 0; n < _countof(set); n++) set[n].first = set[n].last = -1;
\r
138 for (i = 0, n = 0; i < _countof(set); i++) {
\r
139 if (mask & (1LL << i)) {
\r
140 if (set[n].first == -1) set[n].first = set[n].last = (int) i;
\r
141 else if (set[n].last == (int) i - 1) set[n].last = (int) i;
\r
144 set[n].first = set[n].last = (int) i;
\r
149 /* Worst case is 2x2 characters for first and last CPU plus - and/or , */
\r
150 size_t len = (size_t) (n + 1) * 6;
\r
151 *string = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len * sizeof(TCHAR));
\r
152 if (! string) return 2;
\r
156 for (i = 0; i <= n; i++) {
\r
157 if (i) (*string)[s++] = _T(',');
\r
158 ret = _sntprintf_s(*string + s, 3, _TRUNCATE, _T("%u"), set[i].first);
\r
160 HeapFree(GetProcessHeap(), 0, *string);
\r
165 if (set[i].last != set[i].first) {
\r
166 ret =_sntprintf_s(*string + s, 4, _TRUNCATE, _T("%c%u"), (set[i].last == set[i].first + 1) ? _T(',') : _T('-'), set[i].last);
\r
168 HeapFree(GetProcessHeap(), 0, *string);
\r
179 int affinity_string_to_mask(TCHAR *string, __int64 *mask) {
\r
180 if (! mask) return 1;
\r
183 if (! string) return 0;
\r
192 unsigned long number;
\r
194 for (n = 0; n < _countof(set); n++) set[n].first = set[n].last = -1;
\r
198 ret = str_number(s, &number, &end);
\r
200 if (ret == 0 || ret == 2) {
\r
201 if (number >= _countof(set)) return 2;
\r
202 set[n].first = set[n].last = (int) number;
\r
214 if (! *(++s)) return 3;
\r
215 ret = str_number(s, &number, &end);
\r
216 if (ret == 0 || ret == 2) {
\r
218 if (! *s || *s == _T(',')) {
\r
219 set[n].last = (int) number;
\r
236 for (i = 0; i <= n; i++) {
\r
237 for (int j = set[i].first; j <= set[i].last; j++) (__int64) *mask |= (1LL << (__int64) j);
\r
243 inline unsigned long priority_mask() {
\r
244 return REALTIME_PRIORITY_CLASS | HIGH_PRIORITY_CLASS | ABOVE_NORMAL_PRIORITY_CLASS | NORMAL_PRIORITY_CLASS | BELOW_NORMAL_PRIORITY_CLASS | IDLE_PRIORITY_CLASS;
\r
247 int priority_constant_to_index(unsigned long constant) {
\r
248 switch (constant & priority_mask()) {
\r
249 case REALTIME_PRIORITY_CLASS: return NSSM_REALTIME_PRIORITY;
\r
250 case HIGH_PRIORITY_CLASS: return NSSM_HIGH_PRIORITY;
\r
251 case ABOVE_NORMAL_PRIORITY_CLASS: return NSSM_ABOVE_NORMAL_PRIORITY;
\r
252 case BELOW_NORMAL_PRIORITY_CLASS: return NSSM_BELOW_NORMAL_PRIORITY;
\r
253 case IDLE_PRIORITY_CLASS: return NSSM_IDLE_PRIORITY;
\r
255 return NSSM_NORMAL_PRIORITY;
\r
258 unsigned long priority_index_to_constant(int index) {
\r
260 case NSSM_REALTIME_PRIORITY: return REALTIME_PRIORITY_CLASS;
\r
261 case NSSM_HIGH_PRIORITY: return HIGH_PRIORITY_CLASS;
\r
262 case NSSM_ABOVE_NORMAL_PRIORITY: return ABOVE_NORMAL_PRIORITY_CLASS;
\r
263 case NSSM_BELOW_NORMAL_PRIORITY: return BELOW_NORMAL_PRIORITY_CLASS;
\r
264 case NSSM_IDLE_PRIORITY: return IDLE_PRIORITY_CLASS;
\r
266 return NORMAL_PRIORITY_CLASS;
\r
269 static inline unsigned long throttle_milliseconds(unsigned long throttle) {
\r
270 if (throttle > 7) throttle = 8;
\r
271 /* pow() operates on doubles. */
\r
272 unsigned long ret = 1; for (unsigned long i = 1; i < throttle; i++) ret *= 2;
\r
277 Wrapper to be called in a new thread so that we can acknowledge a STOP
\r
278 control immediately.
\r
280 static unsigned long WINAPI shutdown_service(void *arg) {
\r
281 return stop_service((nssm_service_t *) arg, 0, true, true);
\r
285 Wrapper to be called in a new thread so that we can acknowledge start
\r
288 static unsigned long WINAPI launch_service(void *arg) {
\r
289 return monitor_service((nssm_service_t *) arg);
\r
292 /* Connect to the service manager */
\r
293 SC_HANDLE open_service_manager(unsigned long access) {
\r
294 SC_HANDLE ret = OpenSCManager(0, SERVICES_ACTIVE_DATABASE, access);
\r
296 if (is_admin) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENSCMANAGER_FAILED, 0);
\r
303 /* Open a service by name or display name. */
\r
304 SC_HANDLE open_service(SC_HANDLE services, TCHAR *service_name, unsigned long access, TCHAR *canonical_name, unsigned long canonical_namelen) {
\r
305 SC_HANDLE service_handle = OpenService(services, service_name, access);
\r
306 if (service_handle) {
\r
307 if (canonical_name && canonical_name != service_name) {
\r
308 TCHAR displayname[SERVICE_NAME_LENGTH];
\r
309 unsigned long displayname_len = (unsigned long) _countof(displayname);
\r
310 GetServiceDisplayName(services, service_name, displayname, &displayname_len);
\r
311 unsigned long keyname_len = canonical_namelen;
\r
312 GetServiceKeyName(services, displayname, canonical_name, &keyname_len);
\r
314 return service_handle;
\r
317 unsigned long error = GetLastError();
\r
318 if (error != ERROR_SERVICE_DOES_NOT_EXIST) {
\r
319 print_message(stderr, NSSM_MESSAGE_OPENSERVICE_FAILED, error_string(GetLastError()));
\r
323 /* We can't look for a display name because there's no buffer to store it. */
\r
324 if (! canonical_name) {
\r
325 print_message(stderr, NSSM_MESSAGE_OPENSERVICE_FAILED, error_string(GetLastError()));
\r
329 unsigned long bufsize, required, count, i;
\r
330 unsigned long resume = 0;
\r
331 EnumServicesStatus(services, SERVICE_DRIVER | SERVICE_FILE_SYSTEM_DRIVER | SERVICE_KERNEL_DRIVER | SERVICE_WIN32, SERVICE_STATE_ALL, 0, 0, &required, &count, &resume);
\r
332 error = GetLastError();
\r
333 if (error != ERROR_MORE_DATA) {
\r
334 print_message(stderr, NSSM_MESSAGE_ENUMSERVICESSTATUS_FAILED, error_string(GetLastError()));
\r
338 ENUM_SERVICE_STATUS *status = (ENUM_SERVICE_STATUS *) HeapAlloc(GetProcessHeap(), 0, required);
\r
340 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("ENUM_SERVICE_STATUS"), _T("open_service()"));
\r
344 bufsize = required;
\r
347 EnumServicesStatus() returns:
\r
348 1 when it retrieved data and there's no more data to come.
\r
349 0 and sets last error to ERROR_MORE_DATA when it retrieved data and
\r
350 there's more data to come.
\r
351 0 and sets last error to something else on error.
\r
353 int ret = EnumServicesStatus(services, SERVICE_DRIVER | SERVICE_FILE_SYSTEM_DRIVER | SERVICE_KERNEL_DRIVER | SERVICE_WIN32, SERVICE_STATE_ALL, status, bufsize, &required, &count, &resume);
\r
355 error = GetLastError();
\r
356 if (error != ERROR_MORE_DATA) {
\r
357 HeapFree(GetProcessHeap(), 0, status);
\r
358 print_message(stderr, NSSM_MESSAGE_ENUMSERVICESSTATUS_FAILED, error_string(GetLastError()));
\r
363 for (i = 0; i < count; i++) {
\r
364 if (str_equiv(status[i].lpDisplayName, service_name)) {
\r
365 if (_sntprintf_s(canonical_name, canonical_namelen, _TRUNCATE, _T("%s"), status[i].lpServiceName) < 0) {
\r
366 HeapFree(GetProcessHeap(), 0, status);
\r
367 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("canonical_name"), _T("open_service()"));
\r
371 HeapFree(GetProcessHeap(), 0, status);
\r
372 return open_service(services, canonical_name, access, 0, 0);
\r
379 /* Recurse so we can get an error message. */
\r
380 return open_service(services, service_name, access, 0, 0);
\r
383 QUERY_SERVICE_CONFIG *query_service_config(const TCHAR *service_name, SC_HANDLE service_handle) {
\r
384 QUERY_SERVICE_CONFIG *qsc;
\r
385 unsigned long bufsize;
\r
386 unsigned long error;
\r
388 QueryServiceConfig(service_handle, 0, 0, &bufsize);
\r
389 error = GetLastError();
\r
390 if (error == ERROR_INSUFFICIENT_BUFFER) {
\r
391 qsc = (QUERY_SERVICE_CONFIG *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, bufsize);
\r
393 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("QUERY_SERVICE_CONFIG"), _T("query_service_config()"), 0);
\r
398 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG_FAILED, service_name, error_string(error), 0);
\r
402 if (! QueryServiceConfig(service_handle, qsc, bufsize, &bufsize)) {
\r
403 HeapFree(GetProcessHeap(), 0, qsc);
\r
404 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG_FAILED, service_name, error_string(GetLastError()), 0);
\r
411 int set_service_dependencies(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR *buffer) {
\r
412 TCHAR *dependencies = _T("");
\r
413 unsigned long num_dependencies = 0;
\r
415 if (buffer && buffer[0]) {
\r
416 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
\r
418 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
423 Count the dependencies then allocate a buffer big enough for their
\r
424 canonical names, ie n * SERVICE_NAME_LENGTH.
\r
428 for (s = buffer; *s; s++) {
\r
429 num_dependencies++;
\r
430 if (*s == SC_GROUP_IDENTIFIER) groups = s;
\r
434 /* At least one dependency is a group so we need to verify them. */
\r
437 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, NSSM_REGISTRY_GROUPS, 0, KEY_READ, &key)) {
\r
438 _ftprintf(stderr, _T("%s: %s\n"), NSSM_REGISTRY_GROUPS, error_string(GetLastError()));
\r
442 unsigned long type;
\r
443 unsigned long groupslen;
\r
444 unsigned long ret = RegQueryValueEx(key, NSSM_REG_GROUPS, 0, &type, NULL, &groupslen);
\r
445 if (ret == ERROR_SUCCESS) {
\r
446 groups = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, groupslen);
\r
448 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("groups"), _T("set_service_dependencies()"));
\r
452 ret = RegQueryValueEx(key, NSSM_REG_GROUPS, 0, &type, (unsigned char *) groups, &groupslen);
\r
453 if (ret != ERROR_SUCCESS) {
\r
454 _ftprintf(stderr, _T("%s\\%s: %s"), NSSM_REGISTRY_GROUPS, NSSM_REG_GROUPS, error_string(GetLastError()));
\r
455 HeapFree(GetProcessHeap(), 0, groups);
\r
460 else if (ret != ERROR_FILE_NOT_FOUND) {
\r
461 _ftprintf(stderr, _T("%s\\%s: %s"), NSSM_REGISTRY_GROUPS, NSSM_REG_GROUPS, error_string(GetLastError()));
\r
470 unsigned long dependencieslen = (num_dependencies * SERVICE_NAME_LENGTH) + 2;
\r
471 dependencies = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dependencieslen * sizeof(TCHAR));
\r
474 TCHAR dependency[SERVICE_NAME_LENGTH];
\r
475 for (s = buffer; *s; s++) {
\r
477 if (*s == SC_GROUP_IDENTIFIER) {
\r
478 TCHAR *group = s + 1;
\r
482 for (TCHAR *g = groups; *g; g++) {
\r
483 if (str_equiv(g, group)) {
\r
485 /* Set canonical name. */
\r
486 memmove(group, g, _tcslen(g) * sizeof(TCHAR));
\r
494 if (ok) _sntprintf_s(dependency, _countof(dependency), _TRUNCATE, _T("%s"), s);
\r
496 HeapFree(GetProcessHeap(), 0, dependencies);
\r
497 if (groups) HeapFree(GetProcessHeap(), 0, groups);
\r
498 _ftprintf(stderr, _T("%s: %s"), s, error_string(ERROR_SERVICE_DEPENDENCY_DELETED));
\r
503 SC_HANDLE dependency_handle = open_service(services, s, SERVICE_QUERY_STATUS, dependency, _countof(dependency));
\r
504 if (! dependency_handle) {
\r
505 HeapFree(GetProcessHeap(), 0, dependencies);
\r
506 if (groups) HeapFree(GetProcessHeap(), 0, groups);
\r
507 CloseServiceHandle(services);
\r
508 _ftprintf(stderr, _T("%s: %s"), s, error_string(ERROR_SERVICE_DEPENDENCY_DELETED));
\r
513 size_t len = _tcslen(dependency) + 1;
\r
514 memmove(dependencies + i, dependency, len * sizeof(TCHAR));
\r
520 if (groups) HeapFree(GetProcessHeap(), 0, groups);
\r
521 CloseServiceHandle(services);
\r
524 if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, dependencies, 0, 0, 0)) {
\r
525 if (num_dependencies) HeapFree(GetProcessHeap(), 0, dependencies);
\r
526 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
530 if (num_dependencies) HeapFree(GetProcessHeap(), 0, dependencies);
\r
534 int get_service_dependencies(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR **buffer, unsigned long *bufsize, int type) {
\r
535 if (! buffer) return 1;
\r
536 if (! bufsize) return 2;
\r
541 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
542 if (! qsc) return 3;
\r
544 if (! qsc->lpDependencies) return 0;
\r
545 if (! qsc->lpDependencies[0]) return 0;
\r
547 /* lpDependencies is doubly NULL terminated. */
\r
548 while (qsc->lpDependencies[*bufsize]) {
\r
549 while (qsc->lpDependencies[*bufsize]) ++*bufsize;
\r
555 *buffer = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *bufsize * sizeof(TCHAR));
\r
558 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("lpDependencies"), _T("get_service_dependencies()"));
\r
562 if (type == DEPENDENCY_ALL) memmove(*buffer, qsc->lpDependencies, *bufsize * sizeof(TCHAR));
\r
567 for (s = qsc->lpDependencies; *s; s++) {
\r
568 /* Only copy the appropriate type of dependency. */
\r
569 if ((*s == SC_GROUP_IDENTIFIER && type & DEPENDENCY_GROUPS) || (*s != SC_GROUP_IDENTIFIER && type & DEPENDENCY_SERVICES)) {
\r
570 size_t len = _tcslen(s) + 1;
\r
571 *bufsize += (unsigned long) len;
\r
572 memmove(*buffer + i, s, len * sizeof(TCHAR));
\r
581 HeapFree(GetProcessHeap(), 0, qsc);
\r
586 int get_service_dependencies(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR **buffer, unsigned long *bufsize) {
\r
587 return get_service_dependencies(service_name, service_handle, buffer, bufsize, DEPENDENCY_ALL);
\r
590 int set_service_description(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR *buffer) {
\r
591 SERVICE_DESCRIPTION description;
\r
592 ZeroMemory(&description, sizeof(description));
\r
594 lpDescription must be NULL if we aren't changing, the new description
\r
597 if (buffer && buffer[0]) description.lpDescription = buffer;
\r
598 else description.lpDescription = _T("");
\r
600 if (ChangeServiceConfig2(service_handle, SERVICE_CONFIG_DESCRIPTION, &description)) return 0;
\r
602 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_DESCRIPTION_FAILED, service_name, error_string(GetLastError()), 0);
\r
606 int get_service_description(const TCHAR *service_name, SC_HANDLE service_handle, unsigned long len, TCHAR *buffer) {
\r
607 if (! buffer) return 1;
\r
609 unsigned long bufsize;
\r
610 QueryServiceConfig2(service_handle, SERVICE_CONFIG_DESCRIPTION, 0, 0, &bufsize);
\r
611 unsigned long error = GetLastError();
\r
612 if (error == ERROR_INSUFFICIENT_BUFFER) {
\r
613 SERVICE_DESCRIPTION *description = (SERVICE_DESCRIPTION *) HeapAlloc(GetProcessHeap(), 0, bufsize);
\r
614 if (! description) {
\r
615 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("SERVICE_CONFIG_DESCRIPTION"), _T("get_service_description()"));
\r
619 if (QueryServiceConfig2(service_handle, SERVICE_CONFIG_DESCRIPTION, (unsigned char *) description, bufsize, &bufsize)) {
\r
620 if (description->lpDescription) _sntprintf_s(buffer, len, _TRUNCATE, _T("%s"), description->lpDescription);
\r
621 else ZeroMemory(buffer, len * sizeof(TCHAR));
\r
622 HeapFree(GetProcessHeap(), 0, description);
\r
626 HeapFree(GetProcessHeap(), 0, description);
\r
627 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_CONFIG_DESCRIPTION"), error_string(error));
\r
632 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_CONFIG_DESCRIPTION"), error_string(error));
\r
639 int get_service_startup(const TCHAR *service_name, SC_HANDLE service_handle, const QUERY_SERVICE_CONFIG *qsc, unsigned long *startup) {
\r
640 if (! qsc) return 1;
\r
642 switch (qsc->dwStartType) {
\r
643 case SERVICE_DEMAND_START: *startup = NSSM_STARTUP_MANUAL; break;
\r
644 case SERVICE_DISABLED: *startup = NSSM_STARTUP_DISABLED; break;
\r
645 default: *startup = NSSM_STARTUP_AUTOMATIC;
\r
648 if (*startup != NSSM_STARTUP_AUTOMATIC) return 0;
\r
650 /* Check for delayed start. */
\r
651 unsigned long bufsize;
\r
652 unsigned long error;
\r
653 QueryServiceConfig2(service_handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, 0, 0, &bufsize);
\r
654 error = GetLastError();
\r
655 if (error == ERROR_INSUFFICIENT_BUFFER) {
\r
656 SERVICE_DELAYED_AUTO_START_INFO *info = (SERVICE_DELAYED_AUTO_START_INFO *) HeapAlloc(GetProcessHeap(), 0, bufsize);
\r
658 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("SERVICE_DELAYED_AUTO_START_INFO"), _T("get_service_startup()"));
\r
662 if (QueryServiceConfig2(service_handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, (unsigned char *) info, bufsize, &bufsize)) {
\r
663 if (info->fDelayedAutostart) *startup = NSSM_STARTUP_DELAYED;
\r
664 HeapFree(GetProcessHeap(), 0, info);
\r
668 error = GetLastError();
\r
669 if (error != ERROR_INVALID_LEVEL) {
\r
670 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_CONFIG_DELAYED_AUTO_START_INFO"), error_string(error));
\r
675 else if (error != ERROR_INVALID_LEVEL) {
\r
676 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_DELAYED_AUTO_START_INFO"), error_string(error));
\r
683 int get_service_username(const TCHAR *service_name, const QUERY_SERVICE_CONFIG *qsc, TCHAR **username, size_t *usernamelen) {
\r
684 if (! username) return 1;
\r
685 if (! usernamelen) return 1;
\r
690 if (! qsc) return 1;
\r
692 if (qsc->lpServiceStartName[0]) {
\r
693 if (is_localsystem(qsc->lpServiceStartName)) return 0;
\r
695 size_t len = _tcslen(qsc->lpServiceStartName);
\r
696 *username = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(TCHAR));
\r
698 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("username"), _T("get_service_username()"));
\r
702 memmove(*username, qsc->lpServiceStartName, (len + 1) * sizeof(TCHAR));
\r
703 *usernamelen = len;
\r
709 /* Set default values which aren't zero. */
\r
710 void set_nssm_service_defaults(nssm_service_t *service) {
\r
711 if (! service) return;
\r
713 service->type = SERVICE_WIN32_OWN_PROCESS;
\r
714 service->priority = NORMAL_PRIORITY_CLASS;
\r
715 service->stdin_sharing = NSSM_STDIN_SHARING;
\r
716 service->stdin_disposition = NSSM_STDIN_DISPOSITION;
\r
717 service->stdin_flags = NSSM_STDIN_FLAGS;
\r
718 service->stdout_sharing = NSSM_STDOUT_SHARING;
\r
719 service->stdout_disposition = NSSM_STDOUT_DISPOSITION;
\r
720 service->stdout_flags = NSSM_STDOUT_FLAGS;
\r
721 service->stderr_sharing = NSSM_STDERR_SHARING;
\r
722 service->stderr_disposition = NSSM_STDERR_DISPOSITION;
\r
723 service->stderr_flags = NSSM_STDERR_FLAGS;
\r
724 service->throttle_delay = NSSM_RESET_THROTTLE_RESTART;
\r
725 service->stop_method = ~0;
\r
726 service->kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;
\r
727 service->kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;
\r
728 service->kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;
\r
729 service->kill_process_tree = 1;
\r
732 /* Allocate and zero memory for a service. */
\r
733 nssm_service_t *alloc_nssm_service() {
\r
734 nssm_service_t *service = (nssm_service_t *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(nssm_service_t));
\r
735 if (! service) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("service"), _T("alloc_nssm_service()"), 0);
\r
739 /* Free memory for a service. */
\r
740 void cleanup_nssm_service(nssm_service_t *service) {
\r
741 if (! service) return;
\r
742 if (service->username) HeapFree(GetProcessHeap(), 0, service->username);
\r
743 if (service->password) {
\r
744 SecureZeroMemory(service->password, service->passwordlen);
\r
745 HeapFree(GetProcessHeap(), 0, service->password);
\r
747 if (service->dependencies) HeapFree(GetProcessHeap(), 0, service->dependencies);
\r
748 if (service->env) HeapFree(GetProcessHeap(), 0, service->env);
\r
749 if (service->env_extra) HeapFree(GetProcessHeap(), 0, service->env_extra);
\r
750 if (service->handle) CloseServiceHandle(service->handle);
\r
751 if (service->process_handle) CloseHandle(service->process_handle);
\r
752 if (service->wait_handle) UnregisterWait(service->wait_handle);
\r
753 if (service->throttle_section_initialised) DeleteCriticalSection(&service->throttle_section);
\r
754 if (service->throttle_timer) CloseHandle(service->throttle_timer);
\r
755 if (service->hook_section_initialised) DeleteCriticalSection(&service->hook_section);
\r
756 if (service->initial_env) FreeEnvironmentStrings(service->initial_env);
\r
757 HeapFree(GetProcessHeap(), 0, service);
\r
760 /* About to install the service */
\r
761 int pre_install_service(int argc, TCHAR **argv) {
\r
762 nssm_service_t *service = alloc_nssm_service();
\r
763 set_nssm_service_defaults(service);
\r
764 if (argc) _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]);
\r
766 /* Show the dialogue box if we didn't give the service name and path */
\r
767 if (argc < 2) return nssm_gui(IDD_INSTALL, service);
\r
770 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("service"), _T("pre_install_service()"));
\r
773 _sntprintf_s(service->exe, _countof(service->exe), _TRUNCATE, _T("%s"), argv[1]);
\r
775 /* Arguments are optional */
\r
776 size_t flagslen = 0;
\r
779 for (i = 2; i < argc; i++) flagslen += _tcslen(argv[i]) + 1;
\r
780 if (! flagslen) flagslen = 1;
\r
781 if (flagslen > _countof(service->flags)) {
\r
782 print_message(stderr, NSSM_MESSAGE_FLAGS_TOO_LONG);
\r
786 for (i = 2; i < argc; i++) {
\r
787 size_t len = _tcslen(argv[i]);
\r
788 memmove(service->flags + s, argv[i], len * sizeof(TCHAR));
\r
790 if (i < argc - 1) service->flags[s++] = _T(' ');
\r
793 /* Work out directory name */
\r
794 _sntprintf_s(service->dir, _countof(service->dir), _TRUNCATE, _T("%s"), service->exe);
\r
795 strip_basename(service->dir);
\r
797 int ret = install_service(service);
\r
798 cleanup_nssm_service(service);
\r
802 /* About to edit the service. */
\r
803 int pre_edit_service(int argc, TCHAR **argv) {
\r
804 /* Require service name. */
\r
805 if (argc < 2) return usage(1);
\r
807 /* Are we editing on the command line? */
\r
808 enum { MODE_EDITING, MODE_GETTING, MODE_SETTING, MODE_RESETTING } mode = MODE_EDITING;
\r
809 const TCHAR *verb = argv[0];
\r
810 const TCHAR *service_name = argv[1];
\r
811 bool getting = false;
\r
812 bool unsetting = false;
\r
814 /* Minimum number of arguments. */
\r
816 /* Index of first value. */
\r
819 if (str_equiv(verb, _T("get"))) {
\r
821 mode = MODE_GETTING;
\r
823 else if (str_equiv(verb, _T("set"))) {
\r
825 mode = MODE_SETTING;
\r
827 else if (str_equiv(verb, _T("reset")) || str_equiv(verb, _T("unset"))) {
\r
829 mode = MODE_RESETTING;
\r
831 if (argc < mandatory) return usage(1);
\r
833 const TCHAR *parameter = 0;
\r
834 settings_t *setting = 0;
\r
837 /* Validate the parameter. */
\r
838 if (mandatory > 2) {
\r
839 bool additional_mandatory = false;
\r
841 parameter = argv[2];
\r
842 for (i = 0; settings[i].name; i++) {
\r
843 setting = &settings[i];
\r
844 if (! str_equiv(setting->name, parameter)) continue;
\r
845 if (((setting->additional & ADDITIONAL_GETTING) && mode == MODE_GETTING) || ((setting->additional & ADDITIONAL_SETTING) && mode == MODE_SETTING) || ((setting->additional & ADDITIONAL_RESETTING) && mode == MODE_RESETTING)) {
\r
846 additional_mandatory = true;
\r
851 if (! settings[i].name) {
\r
852 print_message(stderr, NSSM_MESSAGE_INVALID_PARAMETER, parameter);
\r
853 for (i = 0; settings[i].name; i++) _ftprintf(stderr, _T("%s\n"), settings[i].name);
\r
858 if (additional_mandatory) {
\r
859 if (argc < mandatory) {
\r
860 print_message(stderr, NSSM_MESSAGE_MISSING_SUBPARAMETER, parameter);
\r
863 additional = argv[3];
\r
866 else if (str_equiv(setting->name, NSSM_NATIVE_OBJECTNAME) && mode == MODE_SETTING) {
\r
867 additional = argv[3];
\r
871 additional = argv[remainder];
\r
872 if (argc < mandatory) return usage(1);
\r
876 nssm_service_t *service = alloc_nssm_service();
\r
877 _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), service_name);
\r
879 /* Open service manager */
\r
880 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
\r
882 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
886 /* Try to open the service */
\r
887 unsigned long access = SERVICE_QUERY_CONFIG;
\r
888 if (mode != MODE_GETTING) access |= SERVICE_CHANGE_CONFIG;
\r
889 service->handle = open_service(services, service->name, access, service->name, _countof(service->name));
\r
890 if (! service->handle) {
\r
891 CloseServiceHandle(services);
\r
895 /* Get system details. */
\r
896 QUERY_SERVICE_CONFIG *qsc = query_service_config(service->name, service->handle);
\r
898 CloseServiceHandle(service->handle);
\r
899 CloseServiceHandle(services);
\r
903 service->type = qsc->dwServiceType;
\r
904 if (! (service->type & SERVICE_WIN32_OWN_PROCESS)) {
\r
905 if (mode != MODE_GETTING) {
\r
906 HeapFree(GetProcessHeap(), 0, qsc);
\r
907 CloseServiceHandle(service->handle);
\r
908 CloseServiceHandle(services);
\r
909 print_message(stderr, NSSM_MESSAGE_CANNOT_EDIT, service->name, NSSM_WIN32_OWN_PROCESS, 0);
\r
914 if (get_service_startup(service->name, service->handle, qsc, &service->startup)) {
\r
915 if (mode != MODE_GETTING) {
\r
916 HeapFree(GetProcessHeap(), 0, qsc);
\r
917 CloseServiceHandle(service->handle);
\r
918 CloseServiceHandle(services);
\r
923 if (get_service_username(service->name, qsc, &service->username, &service->usernamelen)) {
\r
924 if (mode != MODE_GETTING) {
\r
925 HeapFree(GetProcessHeap(), 0, qsc);
\r
926 CloseServiceHandle(service->handle);
\r
927 CloseServiceHandle(services);
\r
932 _sntprintf_s(service->displayname, _countof(service->displayname), _TRUNCATE, _T("%s"), qsc->lpDisplayName);
\r
934 /* Get the canonical service name. We open it case insensitively. */
\r
935 unsigned long bufsize = _countof(service->name);
\r
936 GetServiceKeyName(services, service->displayname, service->name, &bufsize);
\r
938 /* Remember the executable in case it isn't NSSM. */
\r
939 _sntprintf_s(service->image, _countof(service->image), _TRUNCATE, _T("%s"), qsc->lpBinaryPathName);
\r
940 HeapFree(GetProcessHeap(), 0, qsc);
\r
942 /* Get extended system details. */
\r
943 if (get_service_description(service->name, service->handle, _countof(service->description), service->description)) {
\r
944 if (mode != MODE_GETTING) {
\r
945 CloseServiceHandle(service->handle);
\r
946 CloseServiceHandle(services);
\r
951 if (get_service_dependencies(service->name, service->handle, &service->dependencies, &service->dependencieslen)) {
\r
952 if (mode != MODE_GETTING) {
\r
953 CloseServiceHandle(service->handle);
\r
954 CloseServiceHandle(services);
\r
959 /* Get NSSM details. */
\r
960 get_parameters(service, 0);
\r
962 CloseServiceHandle(services);
\r
964 if (! service->exe[0]) {
\r
965 service->native = true;
\r
966 if (mode != MODE_GETTING) print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE, service->name, NSSM, service->image);
\r
969 /* Editing with the GUI. */
\r
970 if (mode == MODE_EDITING) {
\r
971 nssm_gui(IDD_EDIT, service);
\r
975 /* Trying to manage App* parameters for a non-NSSM service. */
\r
976 if (! setting->native && service->native) {
\r
977 CloseServiceHandle(service->handle);
\r
978 print_message(stderr, NSSM_MESSAGE_NATIVE_PARAMETER, setting->name, NSSM);
\r
986 if (mode == MODE_GETTING) {
\r
987 if (! service->native) {
\r
988 key = open_registry(service->name, KEY_READ);
\r
989 if (! key) return 4;
\r
992 if (setting->native) ret = get_setting(service->name, service->handle, setting, &value, additional);
\r
993 else ret = get_setting(service->name, key, setting, &value, additional);
\r
995 CloseServiceHandle(service->handle);
\r
999 switch (setting->type) {
\r
1000 case REG_EXPAND_SZ:
\r
1001 case REG_MULTI_SZ:
\r
1003 _tprintf(_T("%s\n"), value.string ? value.string : _T(""));
\r
1004 HeapFree(GetProcessHeap(), 0, value.string);
\r
1008 _tprintf(_T("%u\n"), value.numeric);
\r
1012 if (! service->native) RegCloseKey(key);
\r
1013 CloseServiceHandle(service->handle);
\r
1017 /* Build the value. */
\r
1018 if (mode == MODE_RESETTING) {
\r
1019 /* Unset the parameter. */
\r
1022 else if (remainder == argc) {
\r
1026 /* Set the parameter. */
\r
1028 size_t delimiterlen = (setting->additional & ADDITIONAL_CRLF) ? 2 : 1;
\r
1029 for (i = remainder; i < argc; i++) len += _tcslen(argv[i]) + delimiterlen;
\r
1032 value.string = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, len * sizeof(TCHAR));
\r
1033 if (! value.string) {
\r
1034 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("value"), _T("edit_service()"));
\r
1035 CloseServiceHandle(service->handle);
\r
1040 for (i = remainder; i < argc; i++) {
\r
1041 size_t len = _tcslen(argv[i]);
\r
1042 memmove(value.string + s, argv[i], len * sizeof(TCHAR));
\r
1044 if (i < argc - 1) {
\r
1045 if (setting->additional & ADDITIONAL_CRLF) {
\r
1046 value.string[s++] = _T('\r');
\r
1047 value.string[s++] = _T('\n');
\r
1049 else value.string[s++] = _T(' ');
\r
1052 value.string[s] = _T('\0');
\r
1055 if (! service->native) {
\r
1056 key = open_registry(service->name, KEY_WRITE);
\r
1058 if (value.string) HeapFree(GetProcessHeap(), 0, value.string);
\r
1063 if (setting->native) ret = set_setting(service->name, service->handle, setting, &value, additional);
\r
1064 else ret = set_setting(service->name, key, setting, &value, additional);
\r
1065 if (value.string) HeapFree(GetProcessHeap(), 0, value.string);
\r
1067 if (! service->native) RegCloseKey(key);
\r
1068 CloseServiceHandle(service->handle);
\r
1072 if (! service->native) RegCloseKey(key);
\r
1073 CloseServiceHandle(service->handle);
\r
1078 /* About to remove the service */
\r
1079 int pre_remove_service(int argc, TCHAR **argv) {
\r
1080 nssm_service_t *service = alloc_nssm_service();
\r
1081 set_nssm_service_defaults(service);
\r
1082 if (argc) _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]);
\r
1084 /* Show dialogue box if we didn't pass service name and "confirm" */
\r
1085 if (argc < 2) return nssm_gui(IDD_REMOVE, service);
\r
1086 if (str_equiv(argv[1], _T("confirm"))) {
\r
1087 int ret = remove_service(service);
\r
1088 cleanup_nssm_service(service);
\r
1091 print_message(stderr, NSSM_MESSAGE_PRE_REMOVE_SERVICE);
\r
1095 /* Install the service */
\r
1096 int install_service(nssm_service_t *service) {
\r
1097 if (! service) return 1;
\r
1099 /* Open service manager */
\r
1100 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE);
\r
1102 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
1103 cleanup_nssm_service(service);
\r
1107 /* Get path of this program */
\r
1108 GetModuleFileName(0, service->image, _countof(service->image));
\r
1110 /* Create the service - settings will be changed in edit_service() */
\r
1111 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
1112 if (! service->handle) {
\r
1113 print_message(stderr, NSSM_MESSAGE_CREATESERVICE_FAILED, error_string(GetLastError()));
\r
1114 CloseServiceHandle(services);
\r
1118 if (edit_service(service, false)) {
\r
1119 DeleteService(service->handle);
\r
1120 CloseServiceHandle(services);
\r
1124 print_message(stdout, NSSM_MESSAGE_SERVICE_INSTALLED, service->name);
\r
1127 CloseServiceHandle(services);
\r
1132 /* Edit the service. */
\r
1133 int edit_service(nssm_service_t *service, bool editing) {
\r
1134 if (! service) return 1;
\r
1137 The only two valid flags for service type are SERVICE_WIN32_OWN_PROCESS
\r
1138 and SERVICE_INTERACTIVE_PROCESS.
\r
1140 service->type &= SERVICE_INTERACTIVE_PROCESS;
\r
1141 service->type |= SERVICE_WIN32_OWN_PROCESS;
\r
1143 /* Startup type. */
\r
1144 unsigned long startup;
\r
1145 switch (service->startup) {
\r
1146 case NSSM_STARTUP_MANUAL: startup = SERVICE_DEMAND_START; break;
\r
1147 case NSSM_STARTUP_DISABLED: startup = SERVICE_DISABLED; break;
\r
1148 default: startup = SERVICE_AUTO_START;
\r
1151 /* Display name. */
\r
1152 if (! service->displayname[0]) _sntprintf_s(service->displayname, _countof(service->displayname), _TRUNCATE, _T("%s"), service->name);
\r
1155 Username must be NULL if we aren't changing or an account name.
\r
1156 We must explicitly use LOCALSYSTEM to change it when we are editing.
\r
1157 Password must be NULL if we aren't changing, a password or "".
\r
1158 Empty passwords are valid but we won't allow them in the GUI.
\r
1160 TCHAR *username = 0;
\r
1162 TCHAR *password = 0;
\r
1163 if (service->usernamelen) {
\r
1164 username = service->username;
\r
1165 if (canonicalise_username(username, &canon)) return 5;
\r
1166 if (service->passwordlen) password = service->password;
\r
1168 else if (editing) username = canon = NSSM_LOCALSYSTEM_ACCOUNT;
\r
1170 if (well_known_username(canon)) password = _T("");
\r
1172 if (grant_logon_as_service(canon)) {
\r
1173 if (canon != username) HeapFree(GetProcessHeap(), 0, canon);
\r
1174 print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username);
\r
1179 TCHAR *dependencies = _T("");
\r
1180 if (service->dependencieslen) dependencies = 0; /* Change later. */
\r
1182 if (! ChangeServiceConfig(service->handle, service->type, startup, SERVICE_NO_CHANGE, 0, 0, 0, dependencies, canon, password, service->displayname)) {
\r
1183 if (canon != username) HeapFree(GetProcessHeap(), 0, canon);
\r
1184 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
1187 if (canon != username) HeapFree(GetProcessHeap(), 0, canon);
\r
1189 if (service->dependencieslen) {
\r
1190 if (set_service_dependencies(service->name, service->handle, service->dependencies)) return 5;
\r
1193 if (service->description[0] || editing) {
\r
1194 set_service_description(service->name, service->handle, service->description);
\r
1197 SERVICE_DELAYED_AUTO_START_INFO delayed;
\r
1198 ZeroMemory(&delayed, sizeof(delayed));
\r
1199 if (service->startup == NSSM_STARTUP_DELAYED) delayed.fDelayedAutostart = 1;
\r
1200 else delayed.fDelayedAutostart = 0;
\r
1201 /* Delayed startup isn't supported until Vista. */
\r
1202 if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, &delayed)) {
\r
1203 unsigned long error = GetLastError();
\r
1204 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
\r
1205 if (error != ERROR_INVALID_LEVEL) {
\r
1206 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_DELAYED_AUTO_START_INFO_FAILED, service->name, error_string(error), 0);
\r
1210 /* Don't mess with parameters which aren't ours. */
\r
1211 if (! service->native) {
\r
1212 /* Now we need to put the parameters into the registry */
\r
1213 if (create_parameters(service, editing)) {
\r
1214 print_message(stderr, NSSM_MESSAGE_CREATE_PARAMETERS_FAILED);
\r
1218 set_service_recovery(service);
\r
1224 /* Control a service. */
\r
1225 int control_service(unsigned long control, int argc, TCHAR **argv) {
\r
1226 if (argc < 1) return usage(1);
\r
1227 TCHAR *service_name = argv[0];
\r
1228 TCHAR canonical_name[SERVICE_NAME_LENGTH];
\r
1230 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
\r
1232 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
1236 unsigned long access = SERVICE_QUERY_STATUS;
\r
1237 switch (control) {
\r
1238 case NSSM_SERVICE_CONTROL_START:
\r
1239 access |= SERVICE_START;
\r
1242 case SERVICE_CONTROL_CONTINUE:
\r
1243 case SERVICE_CONTROL_PAUSE:
\r
1244 access |= SERVICE_PAUSE_CONTINUE;
\r
1247 case SERVICE_CONTROL_STOP:
\r
1248 access |= SERVICE_STOP;
\r
1251 case NSSM_SERVICE_CONTROL_ROTATE:
\r
1252 access |= SERVICE_USER_DEFINED_CONTROL;
\r
1256 SC_HANDLE service_handle = open_service(services, service_name, access, canonical_name, _countof(canonical_name));
\r
1257 if (! service_handle) {
\r
1258 CloseServiceHandle(services);
\r
1263 unsigned long error;
\r
1264 SERVICE_STATUS service_status;
\r
1265 if (control == NSSM_SERVICE_CONTROL_START) {
\r
1266 unsigned long initial_status = SERVICE_STOPPED;
\r
1267 ret = StartService(service_handle, (unsigned long) argc, (const TCHAR **) argv);
\r
1268 error = GetLastError();
\r
1269 CloseServiceHandle(services);
\r
1271 if (error == ERROR_IO_PENDING) {
\r
1273 Older versions of Windows return immediately with ERROR_IO_PENDING
\r
1274 indicate that the operation is still in progress. Newer versions
\r
1275 will return it if there really is a delay.
\r
1278 error = ERROR_SUCCESS;
\r
1282 int response = await_service_control_response(control, service_handle, &service_status, initial_status);
\r
1283 CloseServiceHandle(service_handle);
\r
1286 print_message(stderr, NSSM_MESSAGE_BAD_CONTROL_RESPONSE, canonical_name, service_status_text(service_status.dwCurrentState), service_control_text(control));
\r
1289 else _tprintf(_T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1293 CloseServiceHandle(service_handle);
\r
1294 _ftprintf(stderr, _T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1298 else if (control == SERVICE_CONTROL_INTERROGATE) {
\r
1300 We could actually send an INTERROGATE control but that won't return
\r
1301 any information if the service is stopped and we don't care about
\r
1302 the extra details it might give us in any case. So we'll fake it.
\r
1304 ret = QueryServiceStatus(service_handle, &service_status);
\r
1305 error = GetLastError();
\r
1308 _tprintf(_T("%s\n"), service_status_text(service_status.dwCurrentState));
\r
1312 _ftprintf(stderr, _T("%s: %s\n"), canonical_name, error_string(error));
\r
1317 ret = ControlService(service_handle, control, &service_status);
\r
1318 unsigned long initial_status = service_status.dwCurrentState;
\r
1319 error = GetLastError();
\r
1320 CloseServiceHandle(services);
\r
1322 if (error == ERROR_IO_PENDING) {
\r
1324 error = ERROR_SUCCESS;
\r
1328 int response = await_service_control_response(control, service_handle, &service_status, initial_status);
\r
1329 CloseServiceHandle(service_handle);
\r
1332 print_message(stderr, NSSM_MESSAGE_BAD_CONTROL_RESPONSE, canonical_name, service_status_text(service_status.dwCurrentState), service_control_text(control));
\r
1335 else _tprintf(_T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1339 CloseServiceHandle(service_handle);
\r
1340 _ftprintf(stderr, _T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1341 if (error == ERROR_SERVICE_NOT_ACTIVE) {
\r
1342 if (control == SERVICE_CONTROL_SHUTDOWN || control == SERVICE_CONTROL_STOP) return 0;
\r
1349 /* Remove the service */
\r
1350 int remove_service(nssm_service_t *service) {
\r
1351 if (! service) return 1;
\r
1353 /* Open service manager */
\r
1354 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
\r
1356 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
1360 /* Try to open the service */
\r
1361 service->handle = open_service(services, service->name, DELETE, service->name, _countof(service->name));
\r
1362 if (! service->handle) {
\r
1363 CloseServiceHandle(services);
\r
1367 /* Get the canonical service name. We open it case insensitively. */
\r
1368 unsigned long bufsize = _countof(service->displayname);
\r
1369 GetServiceDisplayName(services, service->name, service->displayname, &bufsize);
\r
1370 bufsize = _countof(service->name);
\r
1371 GetServiceKeyName(services, service->displayname, service->name, &bufsize);
\r
1373 /* Try to delete the service */
\r
1374 if (! DeleteService(service->handle)) {
\r
1375 print_message(stderr, NSSM_MESSAGE_DELETESERVICE_FAILED);
\r
1376 CloseServiceHandle(services);
\r
1381 CloseServiceHandle(services);
\r
1383 print_message(stdout, NSSM_MESSAGE_SERVICE_REMOVED, service->name);
\r
1387 /* Service initialisation */
\r
1388 void WINAPI service_main(unsigned long argc, TCHAR **argv) {
\r
1389 nssm_service_t *service = alloc_nssm_service();
\r
1390 if (! service) return;
\r
1392 if (_sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]) < 0) {
\r
1393 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("service->name"), _T("service_main()"), 0);
\r
1397 /* We can use a condition variable in a critical section on Vista or later. */
\r
1398 if (imports.SleepConditionVariableCS && imports.WakeConditionVariable) use_critical_section = true;
\r
1399 else use_critical_section = false;
\r
1401 /* Initialise status */
\r
1402 ZeroMemory(&service->status, sizeof(service->status));
\r
1403 service->status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;
\r
1404 service->status.dwControlsAccepted = 0;
\r
1405 service->status.dwWin32ExitCode = NO_ERROR;
\r
1406 service->status.dwServiceSpecificExitCode = 0;
\r
1407 service->status.dwCheckPoint = 0;
\r
1408 service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;
\r
1410 /* Signal we AREN'T running the server */
\r
1411 service->process_handle = 0;
\r
1414 /* Register control handler */
\r
1415 service->status_handle = RegisterServiceCtrlHandlerEx(NSSM, service_control_handler, (void *) service);
\r
1416 if (! service->status_handle) {
\r
1417 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_REGISTERSERVICECTRLHANDER_FAILED, error_string(GetLastError()), 0);
\r
1421 log_service_control(service->name, 0, true);
\r
1423 service->status.dwCurrentState = SERVICE_START_PENDING;
\r
1424 service->status.dwWaitHint = service->throttle_delay + NSSM_WAITHINT_MARGIN;
\r
1425 SetServiceStatus(service->status_handle, &service->status);
\r
1428 /* Try to create the exit action parameters; we don't care if it fails */
\r
1429 create_exit_action(service->name, exit_action_strings[0], false);
\r
1431 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT);
\r
1433 service->handle = open_service(services, service->name, SERVICE_CHANGE_CONFIG, 0, 0);
\r
1434 set_service_recovery(service);
\r
1436 /* Remember our display name. */
\r
1437 unsigned long displayname_len = _countof(service->displayname);
\r
1438 GetServiceDisplayName(services, service->name, service->displayname, &displayname_len);
\r
1440 CloseServiceHandle(services);
\r
1444 /* Used for signalling a resume if the service pauses when throttled. */
\r
1445 if (use_critical_section) {
\r
1446 InitializeCriticalSection(&service->throttle_section);
\r
1447 service->throttle_section_initialised = true;
\r
1450 service->throttle_timer = CreateWaitableTimer(0, 1, 0);
\r
1451 if (! service->throttle_timer) {
\r
1452 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_CREATEWAITABLETIMER_FAILED, service->name, error_string(GetLastError()), 0);
\r
1456 /* Critical section for hooks. */
\r
1457 InitializeCriticalSection(&service->hook_section);
\r
1458 service->hook_section_initialised = true;
\r
1460 /* Remember our initial environment. */
\r
1461 service->initial_env = GetEnvironmentStrings();
\r
1463 /* Remember our creation time. */
\r
1464 if (get_process_creation_time(GetCurrentProcess(), &service->nssm_creation_time)) ZeroMemory(&service->nssm_creation_time, sizeof(service->nssm_creation_time));
\r
1466 service->allow_restart = true;
\r
1467 if (! CreateThread(NULL, 0, launch_service, (void *) service, 0, NULL)) {
\r
1468 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETHREAD_FAILED, error_string(GetLastError()), 0);
\r
1469 stop_service(service, 0, true, true);
\r
1473 /* Make sure service recovery actions are taken where necessary */
\r
1474 void set_service_recovery(nssm_service_t *service) {
\r
1475 SERVICE_FAILURE_ACTIONS_FLAG flag;
\r
1476 ZeroMemory(&flag, sizeof(flag));
\r
1477 flag.fFailureActionsOnNonCrashFailures = true;
\r
1479 /* This functionality was added in Vista so the call may fail */
\r
1480 if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, &flag)) {
\r
1481 unsigned long error = GetLastError();
\r
1482 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
\r
1483 if (error != ERROR_INVALID_LEVEL) {
\r
1484 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_FAILURE_ACTIONS_FAILED, service->name, error_string(error), 0);
\r
1489 int monitor_service(nssm_service_t *service) {
\r
1490 /* Set service status to started */
\r
1491 int ret = start_service(service);
\r
1494 _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%d"), ret);
\r
1495 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_START_SERVICE_FAILED, service->exe, service->name, ret, 0);
\r
1498 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_STARTED_SERVICE, service->exe, service->flags, service->name, service->dir, 0);
\r
1500 /* Monitor service */
\r
1501 if (! RegisterWaitForSingleObject(&service->wait_handle, service->process_handle, end_service, (void *) service, INFINITE, WT_EXECUTEONLYONCE | WT_EXECUTELONGFUNCTION)) {
\r
1502 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_REGISTERWAITFORSINGLEOBJECT_FAILED, service->name, service->exe, error_string(GetLastError()), 0);
\r
1508 TCHAR *service_control_text(unsigned long control) {
\r
1509 switch (control) {
\r
1510 /* HACK: there is no SERVICE_CONTROL_START constant */
\r
1511 case NSSM_SERVICE_CONTROL_START: return _T("START");
\r
1512 case SERVICE_CONTROL_STOP: return _T("STOP");
\r
1513 case SERVICE_CONTROL_SHUTDOWN: return _T("SHUTDOWN");
\r
1514 case SERVICE_CONTROL_PAUSE: return _T("PAUSE");
\r
1515 case SERVICE_CONTROL_CONTINUE: return _T("CONTINUE");
\r
1516 case SERVICE_CONTROL_INTERROGATE: return _T("INTERROGATE");
\r
1517 case NSSM_SERVICE_CONTROL_ROTATE: return _T("ROTATE");
\r
1518 case SERVICE_CONTROL_POWEREVENT: return _T("POWEREVENT");
\r
1519 default: return 0;
\r
1523 TCHAR *service_status_text(unsigned long status) {
\r
1525 case SERVICE_STOPPED: return _T("SERVICE_STOPPED");
\r
1526 case SERVICE_START_PENDING: return _T("SERVICE_START_PENDING");
\r
1527 case SERVICE_STOP_PENDING: return _T("SERVICE_STOP_PENDING");
\r
1528 case SERVICE_RUNNING: return _T("SERVICE_RUNNING");
\r
1529 case SERVICE_CONTINUE_PENDING: return _T("SERVICE_CONTINUE_PENDING");
\r
1530 case SERVICE_PAUSE_PENDING: return _T("SERVICE_PAUSE_PENDING");
\r
1531 case SERVICE_PAUSED: return _T("SERVICE_PAUSED");
\r
1532 default: return 0;
\r
1536 void log_service_control(TCHAR *service_name, unsigned long control, bool handled) {
\r
1537 TCHAR *text = service_control_text(control);
\r
1538 unsigned long event;
\r
1541 /* "0x" + 8 x hex + NULL */
\r
1542 text = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, 11 * sizeof(TCHAR));
\r
1544 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);
\r
1547 if (_sntprintf_s(text, 11, _TRUNCATE, _T("0x%08x"), control) < 0) {
\r
1548 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);
\r
1549 HeapFree(GetProcessHeap(), 0, text);
\r
1553 event = NSSM_EVENT_SERVICE_CONTROL_UNKNOWN;
\r
1555 else if (handled) event = NSSM_EVENT_SERVICE_CONTROL_HANDLED;
\r
1556 else event = NSSM_EVENT_SERVICE_CONTROL_NOT_HANDLED;
\r
1558 log_event(EVENTLOG_INFORMATION_TYPE, event, service_name, text, 0);
\r
1560 if (event == NSSM_EVENT_SERVICE_CONTROL_UNKNOWN) {
\r
1561 HeapFree(GetProcessHeap(), 0, text);
\r
1565 /* Service control handler */
\r
1566 unsigned long WINAPI service_control_handler(unsigned long control, unsigned long event, void *data, void *context) {
\r
1567 nssm_service_t *service = (nssm_service_t *) context;
\r
1569 switch (control) {
\r
1570 case SERVICE_CONTROL_INTERROGATE:
\r
1571 /* We always keep the service status up-to-date so this is a no-op. */
\r
1574 case SERVICE_CONTROL_SHUTDOWN:
\r
1575 case SERVICE_CONTROL_STOP:
\r
1576 service->last_control = control;
\r
1577 log_service_control(service->name, control, true);
\r
1579 /* Immediately block further controls. */
\r
1580 service->allow_restart = false;
\r
1581 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
1582 service->status.dwControlsAccepted = 0;
\r
1583 SetServiceStatus(service->status_handle, &service->status);
\r
1585 /* Pre-stop hook. */
\r
1586 nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_STOP, NSSM_HOOK_ACTION_PRE, &control, NSSM_SERVICE_STATUS_DEADLINE, false);
\r
1589 We MUST acknowledge the stop request promptly but we're committed to
\r
1590 waiting for the application to exit. Spawn a new thread to wait
\r
1591 while we acknowledge the request.
\r
1593 if (! CreateThread(NULL, 0, shutdown_service, context, 0, NULL)) {
\r
1594 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETHREAD_FAILED, error_string(GetLastError()), 0);
\r
1597 We couldn't create a thread to tidy up so we'll have to force the tidyup
\r
1598 to complete in time in this thread.
\r
1600 service->kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;
\r
1601 service->kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;
\r
1602 service->kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;
\r
1604 stop_service(service, 0, true, true);
\r
1608 case SERVICE_CONTROL_CONTINUE:
\r
1609 service->last_control = control;
\r
1610 log_service_control(service->name, control, true);
\r
1611 service->throttle = 0;
\r
1612 if (use_critical_section) imports.WakeConditionVariable(&service->throttle_condition);
\r
1614 if (! service->throttle_timer) return ERROR_CALL_NOT_IMPLEMENTED;
\r
1615 ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));
\r
1616 SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);
\r
1618 /* We can't continue if the application is running! */
\r
1619 if (! service->process_handle) service->status.dwCurrentState = SERVICE_CONTINUE_PENDING;
\r
1620 service->status.dwWaitHint = throttle_milliseconds(service->throttle) + NSSM_WAITHINT_MARGIN;
\r
1621 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESET_THROTTLE, service->name, 0);
\r
1622 SetServiceStatus(service->status_handle, &service->status);
\r
1625 case SERVICE_CONTROL_PAUSE:
\r
1627 We don't accept pause messages but it isn't possible to register
\r
1628 only for continue messages so we have to handle this case.
\r
1630 log_service_control(service->name, control, false);
\r
1631 return ERROR_CALL_NOT_IMPLEMENTED;
\r
1633 case NSSM_SERVICE_CONTROL_ROTATE:
\r
1634 service->last_control = control;
\r
1635 log_service_control(service->name, control, true);
\r
1636 (void) nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_ROTATE, NSSM_HOOK_ACTION_PRE, &control, NSSM_HOOK_DEADLINE, false);
\r
1637 if (service->rotate_stdout_online == NSSM_ROTATE_ONLINE) service->rotate_stdout_online = NSSM_ROTATE_ONLINE_ASAP;
\r
1638 if (service->rotate_stderr_online == NSSM_ROTATE_ONLINE) service->rotate_stderr_online = NSSM_ROTATE_ONLINE_ASAP;
\r
1639 (void) nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_ROTATE, NSSM_HOOK_ACTION_POST, &control);
\r
1642 case SERVICE_CONTROL_POWEREVENT:
\r
1643 /* Resume from suspend. */
\r
1644 if (event == PBT_APMRESUMEAUTOMATIC) {
\r
1645 service->last_control = control;
\r
1646 log_service_control(service->name, control, true);
\r
1647 (void) nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_POWER, NSSM_HOOK_ACTION_RESUME, &control);
\r
1651 /* Battery low or changed to A/C power or something. */
\r
1652 if (event == PBT_APMPOWERSTATUSCHANGE) {
\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_POWER, NSSM_HOOK_ACTION_CHANGE, &control);
\r
1658 log_service_control(service->name, control, false);
\r
1662 /* Unknown control */
\r
1663 log_service_control(service->name, control, false);
\r
1664 return ERROR_CALL_NOT_IMPLEMENTED;
\r
1667 /* Start the service */
\r
1668 int start_service(nssm_service_t *service) {
\r
1669 service->stopping = false;
\r
1671 if (service->process_handle) return 0;
\r
1672 service->start_requested_count++;
\r
1674 /* Allocate a STARTUPINFO structure for a new process */
\r
1676 ZeroMemory(&si, sizeof(si));
\r
1677 si.cb = sizeof(si);
\r
1679 /* Allocate a PROCESSINFO structure for the process */
\r
1680 PROCESS_INFORMATION pi;
\r
1681 ZeroMemory(&pi, sizeof(pi));
\r
1683 /* Get startup parameters */
\r
1684 int ret = get_parameters(service, &si);
\r
1686 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_PARAMETERS_FAILED, service->name, 0);
\r
1687 return stop_service(service, 2, true, true);
\r
1690 /* Launch executable with arguments */
\r
1691 TCHAR cmd[CMD_LENGTH];
\r
1692 if (_sntprintf_s(cmd, _countof(cmd), _TRUNCATE, _T("\"%s\" %s"), service->exe, service->flags) < 0) {
\r
1693 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("command line"), _T("start_service"), 0);
\r
1694 return stop_service(service, 2, true, true);
\r
1697 throttle_restart(service);
\r
1699 /* Set the environment. */
\r
1700 if (service->env) duplicate_environment(service->env);
\r
1701 if (service->env_extra) set_environment_block(service->env_extra);
\r
1703 service->status.dwCurrentState = SERVICE_START_PENDING;
\r
1704 service->status.dwControlsAccepted = SERVICE_ACCEPT_POWEREVENT | SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP;
\r
1705 SetServiceStatus(service->status_handle, &service->status);
\r
1707 /* Pre-start hook. */
\r
1708 unsigned long control = NSSM_SERVICE_CONTROL_START;
\r
1709 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
1711 _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), NSSM_HOOK_STATUS_ABORT);
\r
1712 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_PRESTART_HOOK_ABORT, NSSM_HOOK_EVENT_START, NSSM_HOOK_ACTION_PRE, service->name, code, 0);
\r
1713 duplicate_environment_strings(service->initial_env);
\r
1714 return stop_service(service, 5, true, true);
\r
1717 /* Did another thread receive a stop control? */
\r
1718 if (service->allow_restart) {
\r
1719 /* Set up I/O redirection. */
\r
1720 if (get_output_handles(service, &si)) {
\r
1721 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_OUTPUT_HANDLES_FAILED, service->name, 0);
\r
1722 if (! service->no_console) FreeConsole();
\r
1723 close_output_handles(&si);
\r
1724 duplicate_environment_strings(service->initial_env);
\r
1725 return stop_service(service, 4, true, true);
\r
1728 bool inherit_handles = false;
\r
1729 if (si.dwFlags & STARTF_USESTDHANDLES) inherit_handles = true;
\r
1730 unsigned long flags = service->priority & priority_mask();
\r
1731 if (service->affinity) flags |= CREATE_SUSPENDED;
\r
1732 if (! CreateProcess(0, cmd, 0, 0, inherit_handles, flags, 0, service->dir, &si, &pi)) {
\r
1733 unsigned long exitcode = 3;
\r
1734 unsigned long error = GetLastError();
\r
1735 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service->name, service->exe, error_string(error), 0);
\r
1736 close_output_handles(&si);
\r
1737 duplicate_environment_strings(service->initial_env);
\r
1738 return stop_service(service, exitcode, true, true);
\r
1740 service->start_count++;
\r
1741 service->process_handle = pi.hProcess;
\r
1742 service->pid = pi.dwProcessId;
\r
1744 if (get_process_creation_time(service->process_handle, &service->creation_time)) ZeroMemory(&service->creation_time, sizeof(service->creation_time));
\r
1746 close_output_handles(&si);
\r
1748 if (! service->no_console) FreeConsole();
\r
1750 if (service->affinity) {
\r
1752 We are explicitly storing service->affinity as a 64-bit unsigned integer
\r
1753 so that we can parse it regardless of whether we're running in 32-bit
\r
1754 or 64-bit mode. The arguments to SetProcessAffinityMask(), however, are
\r
1755 defined as type DWORD_PTR and hence limited to 32 bits on a 32-bit system
\r
1756 (or when running the 32-bit NSSM).
\r
1758 The result is a lot of seemingly-unnecessary casting throughout the code
\r
1759 and potentially confusion when we actually try to start the service.
\r
1760 Having said that, however, it's unlikely that we're actually going to
\r
1761 run in 32-bit mode on a system which has more than 32 CPUs so the
\r
1762 likelihood of seeing a confusing situation is somewhat diminished.
\r
1764 DWORD_PTR affinity, system_affinity;
\r
1766 if (GetProcessAffinityMask(service->process_handle, &affinity, &system_affinity)) affinity = service->affinity & system_affinity;
\r
1768 affinity = (DWORD_PTR) service->affinity;
\r
1769 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GETPROCESSAFFINITYMASK_FAILED, service->name, error_string(GetLastError()), 0);
\r
1772 if (! SetProcessAffinityMask(service->process_handle, affinity)) {
\r
1773 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_SETPROCESSAFFINITYMASK_FAILED, service->name, error_string(GetLastError()), 0);
\r
1776 ResumeThread(pi.hThread);
\r
1780 /* Restore our environment. */
\r
1781 duplicate_environment_strings(service->initial_env);
\r
1784 Wait for a clean startup before changing the service status to RUNNING
\r
1785 but be mindful of the fact that we are blocking the service control manager
\r
1786 so abandon the wait before too much time has elapsed.
\r
1788 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
1790 /* Did another thread receive a stop control? */
\r
1791 if (! service->allow_restart) return 0;
\r
1793 /* Signal successful start */
\r
1794 service->status.dwCurrentState = SERVICE_RUNNING;
\r
1795 service->status.dwControlsAccepted &= ~SERVICE_ACCEPT_PAUSE_CONTINUE;
\r
1796 SetServiceStatus(service->status_handle, &service->status);
\r
1798 /* Post-start hook. */
\r
1799 if (! service->throttle) {
\r
1800 (void) nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_START, NSSM_HOOK_ACTION_POST, &control);
\r
1803 /* Ensure the restart delay is always applied. */
\r
1804 if (service->restart_delay && ! service->throttle) service->throttle++;
\r
1809 /* Stop the service */
\r
1810 int stop_service(nssm_service_t *service, unsigned long exitcode, bool graceful, bool default_action) {
\r
1811 service->allow_restart = false;
\r
1812 if (service->wait_handle) {
\r
1813 UnregisterWait(service->wait_handle);
\r
1814 service->wait_handle = 0;
\r
1817 service->rotate_stdout_online = service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;
\r
1819 if (default_action && ! exitcode && ! graceful) {
\r
1820 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
1824 /* Signal we are stopping */
\r
1826 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
1827 service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;
\r
1828 SetServiceStatus(service->status_handle, &service->status);
\r
1831 /* Nothing to do if service isn't running */
\r
1832 if (service->pid) {
\r
1833 /* Shut down service */
\r
1834 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_TERMINATEPROCESS, service->name, service->exe, 0);
\r
1836 service_kill_t(service, &k);
\r
1840 else log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_PROCESS_ALREADY_STOPPED, service->name, service->exe, 0);
\r
1842 end_service((void *) service, true);
\r
1844 /* Signal we stopped */
\r
1846 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
1847 wait_for_hooks(service, true);
\r
1848 service->status.dwCurrentState = SERVICE_STOPPED;
\r
1850 service->status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
\r
1851 service->status.dwServiceSpecificExitCode = exitcode;
\r
1854 service->status.dwWin32ExitCode = NO_ERROR;
\r
1855 service->status.dwServiceSpecificExitCode = 0;
\r
1857 SetServiceStatus(service->status_handle, &service->status);
\r
1863 /* Callback function triggered when the server exits */
\r
1864 void CALLBACK end_service(void *arg, unsigned char why) {
\r
1865 nssm_service_t *service = (nssm_service_t *) arg;
\r
1867 if (service->stopping) return;
\r
1869 service->stopping = true;
\r
1871 service->rotate_stdout_online = service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;
\r
1873 /* Use now as a dummy exit time. */
\r
1874 GetSystemTimeAsFileTime(&service->exit_time);
\r
1876 /* Check exit code */
\r
1877 unsigned long exitcode = 0;
\r
1879 if (service->process_handle) {
\r
1880 GetExitCodeProcess(service->process_handle, &exitcode);
\r
1881 service->exitcode = exitcode;
\r
1882 /* Check real exit time. */
\r
1883 if (exitcode != STILL_ACTIVE) get_process_exit_time(service->process_handle, &service->exit_time);
\r
1884 CloseHandle(service->process_handle);
\r
1887 service->process_handle = 0;
\r
1890 Log that the service ended BEFORE logging about killing the process
\r
1891 tree. See below for the possible values of the why argument.
\r
1894 _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), exitcode);
\r
1895 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ENDED_SERVICE, service->exe, service->name, code, 0);
\r
1899 if (exitcode == STILL_ACTIVE) exitcode = 0;
\r
1900 if (service->pid && service->kill_process_tree) {
\r
1902 service_kill_t(service, &k);
\r
1903 kill_process_tree(&k, service->pid);
\r
1908 service->exit_count++;
\r
1909 (void) nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_EXIT, NSSM_HOOK_ACTION_POST, NULL, NSSM_HOOK_DEADLINE, true);
\r
1912 The why argument is true if our wait timed out or false otherwise.
\r
1913 Our wait is infinite so why will never be true when called by the system.
\r
1914 If it is indeed true, assume we were called from stop_service() because
\r
1915 this is a controlled shutdown, and don't take any restart action.
\r
1918 if (! service->allow_restart) return;
\r
1920 /* What action should we take? */
\r
1921 int action = NSSM_EXIT_RESTART;
\r
1922 TCHAR action_string[ACTION_LEN];
\r
1923 bool default_action;
\r
1924 if (! get_exit_action(service->name, &exitcode, action_string, &default_action)) {
\r
1925 for (int i = 0; exit_action_strings[i]; i++) {
\r
1926 if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
1934 /* Try to restart the service or return failure code to service manager */
\r
1935 case NSSM_EXIT_RESTART:
\r
1936 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_RESTART, service->name, code, exit_action_strings[action], service->exe, 0);
\r
1937 while (monitor_service(service)) {
\r
1938 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_RESTART_SERVICE_FAILED, service->exe, service->name, 0);
\r
1943 /* Do nothing, just like srvany would */
\r
1944 case NSSM_EXIT_IGNORE:
\r
1945 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_IGNORE, service->name, code, exit_action_strings[action], service->exe, 0);
\r
1946 wait_for_hooks(service, false);
\r
1950 /* Tell the service manager we are finished */
\r
1951 case NSSM_EXIT_REALLY:
\r
1952 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_REALLY, service->name, code, exit_action_strings[action], 0);
\r
1953 stop_service(service, exitcode, true, default_action);
\r
1956 /* Fake a crash so pre-Vista service managers will run recovery actions. */
\r
1957 case NSSM_EXIT_UNCLEAN:
\r
1958 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_UNCLEAN, service->name, code, exit_action_strings[action], 0);
\r
1959 stop_service(service, exitcode, false, default_action);
\r
1960 wait_for_hooks(service, false);
\r
1967 void throttle_restart(nssm_service_t *service) {
\r
1968 /* This can't be a restart if the service is already running. */
\r
1969 if (! service->throttle++) return;
\r
1972 unsigned long throttle_ms = throttle_milliseconds(service->throttle);
\r
1973 TCHAR threshold[8], milliseconds[8];
\r
1975 if (service->restart_delay > throttle_ms) ms = service->restart_delay;
\r
1976 else ms = throttle_ms;
\r
1978 _sntprintf_s(milliseconds, _countof(milliseconds), _TRUNCATE, _T("%lu"), ms);
\r
1980 if (service->throttle == 1 && service->restart_delay > throttle_ms) log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESTART_DELAY, service->name, milliseconds, 0);
\r
1982 _sntprintf_s(threshold, _countof(threshold), _TRUNCATE, _T("%lu"), service->throttle_delay);
\r
1983 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_THROTTLED, service->name, threshold, milliseconds, 0);
\r
1986 if (use_critical_section) EnterCriticalSection(&service->throttle_section);
\r
1987 else if (service->throttle_timer) {
\r
1988 ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));
\r
1989 service->throttle_duetime.QuadPart = 0 - (ms * 10000LL);
\r
1990 SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);
\r
1993 service->status.dwCurrentState = SERVICE_PAUSED;
\r
1994 service->status.dwControlsAccepted |= SERVICE_ACCEPT_PAUSE_CONTINUE;
\r
1995 SetServiceStatus(service->status_handle, &service->status);
\r
1997 if (use_critical_section) {
\r
1998 imports.SleepConditionVariableCS(&service->throttle_condition, &service->throttle_section, ms);
\r
1999 LeaveCriticalSection(&service->throttle_section);
\r
2002 if (service->throttle_timer) WaitForSingleObject(service->throttle_timer, INFINITE);
\r
2008 When responding to a stop (or any other) request we need to set dwWaitHint to
\r
2009 the number of milliseconds we expect the operation to take, and optionally
\r
2010 increase dwCheckPoint. If dwWaitHint milliseconds elapses without the
\r
2011 operation completing or dwCheckPoint increasing, the system will consider the
\r
2012 service to be hung.
\r
2014 However the system will consider the service to be hung after 30000
\r
2015 milliseconds regardless of the value of dwWaitHint if dwCheckPoint has not
\r
2016 changed. Therefore if we want to wait longer than that we must periodically
\r
2017 increase dwCheckPoint.
\r
2019 Furthermore, it will consider the service to be hung after 60000 milliseconds
\r
2020 regardless of the value of dwCheckPoint unless dwWaitHint is increased every
\r
2021 time dwCheckPoint is also increased.
\r
2023 Our strategy then is to retrieve the initial dwWaitHint and wait for
\r
2024 NSSM_SERVICE_STATUS_DEADLINE milliseconds. If the process is still running
\r
2025 and we haven't finished waiting we increment dwCheckPoint and add whichever is
\r
2026 smaller of NSSM_SERVICE_STATUS_DEADLINE or the remaining timeout to
\r
2029 Only doing both these things will prevent the system from killing the service.
\r
2031 If the status_handle and service_status arguments are omitted, this function
\r
2032 will not try to update the service manager but it will still log to the
\r
2033 event log that it is waiting for a handle.
\r
2035 Returns: 1 if the wait timed out.
\r
2036 0 if the wait completed.
\r
2039 int await_single_handle(SERVICE_STATUS_HANDLE status_handle, SERVICE_STATUS *status, HANDLE handle, TCHAR *name, TCHAR *function_name, unsigned long timeout) {
\r
2040 unsigned long interval;
\r
2041 unsigned long ret;
\r
2042 unsigned long waited;
\r
2043 TCHAR interval_milliseconds[16];
\r
2044 TCHAR timeout_milliseconds[16];
\r
2045 TCHAR waited_milliseconds[16];
\r
2046 TCHAR *function = function_name;
\r
2048 /* Add brackets to function name. */
\r
2049 size_t funclen = _tcslen(function_name) + 3;
\r
2050 TCHAR *func = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, funclen * sizeof(TCHAR));
\r
2052 if (_sntprintf_s(func, funclen, _TRUNCATE, _T("%s()"), function_name) > -1) function = func;
\r
2055 _sntprintf_s(timeout_milliseconds, _countof(timeout_milliseconds), _TRUNCATE, _T("%lu"), timeout);
\r
2058 while (waited < timeout) {
\r
2059 interval = timeout - waited;
\r
2060 if (interval > NSSM_SERVICE_STATUS_DEADLINE) interval = NSSM_SERVICE_STATUS_DEADLINE;
\r
2063 status->dwWaitHint += interval;
\r
2064 status->dwCheckPoint++;
\r
2065 SetServiceStatus(status_handle, status);
\r
2069 _sntprintf_s(waited_milliseconds, _countof(waited_milliseconds), _TRUNCATE, _T("%lu"), waited);
\r
2070 _sntprintf_s(interval_milliseconds, _countof(interval_milliseconds), _TRUNCATE, _T("%lu"), interval);
\r
2071 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_AWAITING_SINGLE_HANDLE, function, name, waited_milliseconds, interval_milliseconds, timeout_milliseconds, 0);
\r
2074 switch (WaitForSingleObject(handle, interval)) {
\r
2075 case WAIT_OBJECT_0:
\r
2079 case WAIT_TIMEOUT:
\r
2088 waited += interval;
\r
2092 if (func) HeapFree(GetProcessHeap(), 0, func);
\r