4 bool use_critical_section;
\r
6 extern imports_t imports;
\r
7 extern settings_t settings[];
\r
9 const TCHAR *exit_action_strings[] = { _T("Restart"), _T("Ignore"), _T("Exit"), _T("Suicide"), 0 };
\r
10 const TCHAR *startup_strings[] = { _T("SERVICE_AUTO_START"), _T("SERVICE_DELAYED_AUTO_START"), _T("SERVICE_DEMAND_START"), _T("SERVICE_DISABLED"), 0 };
\r
11 const TCHAR *priority_strings[] = { _T("REALTIME_PRIORITY_CLASS"), _T("HIGH_PRIORITY_CLASS"), _T("ABOVE_NORMAL_PRIORITY_CLASS"), _T("NORMAL_PRIORITY_CLASS"), _T("BELOW_NORMAL_PRIORITY_CLASS"), _T("IDLE_PRIORITY_CLASS"), 0 };
\r
19 Check the status in response to a control.
\r
20 Returns: 1 if the status is expected, eg STOP following CONTROL_STOP.
\r
21 0 if the status is desired, eg STOPPED following CONTROL_STOP.
\r
22 -1 if the status is undesired, eg STOPPED following CONTROL_START.
\r
24 static inline int service_control_response(unsigned long control, unsigned long status) {
\r
26 case NSSM_SERVICE_CONTROL_START:
\r
28 case SERVICE_START_PENDING:
\r
31 case SERVICE_RUNNING:
\r
38 case SERVICE_CONTROL_STOP:
\r
39 case SERVICE_CONTROL_SHUTDOWN:
\r
41 case SERVICE_STOP_PENDING:
\r
44 case SERVICE_STOPPED:
\r
51 case SERVICE_CONTROL_PAUSE:
\r
53 case SERVICE_PAUSE_PENDING:
\r
56 case SERVICE_PAUSED:
\r
63 case SERVICE_CONTROL_CONTINUE:
\r
65 case SERVICE_CONTINUE_PENDING:
\r
68 case SERVICE_RUNNING:
\r
75 case SERVICE_CONTROL_INTERROGATE:
\r
82 static inline int await_service_control_response(unsigned long control, SC_HANDLE service_handle, SERVICE_STATUS *service_status, unsigned long initial_status) {
\r
84 while (QueryServiceStatus(service_handle, service_status)) {
\r
85 int response = service_control_response(control, service_status->dwCurrentState);
\r
86 /* Alas we can't WaitForSingleObject() on an SC_HANDLE. */
\r
87 if (! response) return response;
\r
88 if (response > 0 || service_status->dwCurrentState == initial_status) {
\r
89 if (++tries > 10) return response;
\r
92 else return response;
\r
97 int affinity_mask_to_string(__int64 mask, TCHAR **string) {
\r
98 if (! string) return 1;
\r
106 /* SetProcessAffinityMask() accepts a mask of up to 64 processors. */
\r
108 for (n = 0; n < _countof(set); n++) set[n].first = set[n].last = -1;
\r
110 for (i = 0, n = 0; i < _countof(set); i++) {
\r
111 if (mask & (1LL << i)) {
\r
112 if (set[n].first == -1) set[n].first = set[n].last = (int) i;
\r
113 else if (set[n].last == (int) i - 1) set[n].last = (int) i;
\r
116 set[n].first = set[n].last = (int) i;
\r
121 /* Worst case is 2x2 characters for first and last CPU plus - and/or , */
\r
122 size_t len = (size_t) (n + 1) * 6;
\r
123 *string = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len * sizeof(TCHAR));
\r
124 if (! string) return 2;
\r
128 for (i = 0; i <= n; i++) {
\r
129 if (i) (*string)[s++] = _T(',');
\r
130 ret = _sntprintf_s(*string + s, 3, _TRUNCATE, _T("%u"), set[i].first);
\r
132 HeapFree(GetProcessHeap(), 0, *string);
\r
137 if (set[i].last != set[i].first) {
\r
138 ret =_sntprintf_s(*string + s, 4, _TRUNCATE, _T("%c%u"), (set[i].last == set[i].first + 1) ? _T(',') : _T('-'), set[i].last);
\r
140 HeapFree(GetProcessHeap(), 0, *string);
\r
151 int affinity_string_to_mask(TCHAR *string, __int64 *mask) {
\r
152 if (! mask) return 1;
\r
155 if (! string) return 0;
\r
164 unsigned long number;
\r
166 for (n = 0; n < _countof(set); n++) set[n].first = set[n].last = -1;
\r
170 ret = str_number(s, &number, &end);
\r
172 if (ret == 0 || ret == 2) {
\r
173 if (number >= _countof(set)) return 2;
\r
174 set[n].first = set[n].last = (int) number;
\r
186 if (! *(++s)) return 3;
\r
187 ret = str_number(s, &number, &end);
\r
188 if (ret == 0 || ret == 2) {
\r
190 if (! *s || *s == _T(',')) {
\r
191 set[n].last = (int) number;
\r
208 for (i = 0; i <= n; i++) {
\r
209 for (int j = set[i].first; j <= set[i].last; j++) (__int64) *mask |= (1LL << (__int64) j);
\r
215 inline unsigned long priority_mask() {
\r
216 return REALTIME_PRIORITY_CLASS | HIGH_PRIORITY_CLASS | ABOVE_NORMAL_PRIORITY_CLASS | NORMAL_PRIORITY_CLASS | BELOW_NORMAL_PRIORITY_CLASS | IDLE_PRIORITY_CLASS;
\r
219 int priority_constant_to_index(unsigned long constant) {
\r
220 switch (constant & priority_mask()) {
\r
221 case REALTIME_PRIORITY_CLASS: return NSSM_REALTIME_PRIORITY;
\r
222 case HIGH_PRIORITY_CLASS: return NSSM_HIGH_PRIORITY;
\r
223 case ABOVE_NORMAL_PRIORITY_CLASS: return NSSM_ABOVE_NORMAL_PRIORITY;
\r
224 case BELOW_NORMAL_PRIORITY_CLASS: return NSSM_BELOW_NORMAL_PRIORITY;
\r
225 case IDLE_PRIORITY_CLASS: return NSSM_IDLE_PRIORITY;
\r
227 return NSSM_NORMAL_PRIORITY;
\r
230 unsigned long priority_index_to_constant(int index) {
\r
232 case NSSM_REALTIME_PRIORITY: return REALTIME_PRIORITY_CLASS;
\r
233 case NSSM_HIGH_PRIORITY: return HIGH_PRIORITY_CLASS;
\r
234 case NSSM_ABOVE_NORMAL_PRIORITY: return ABOVE_NORMAL_PRIORITY_CLASS;
\r
235 case NSSM_BELOW_NORMAL_PRIORITY: return BELOW_NORMAL_PRIORITY_CLASS;
\r
236 case NSSM_IDLE_PRIORITY: return IDLE_PRIORITY_CLASS;
\r
238 return NORMAL_PRIORITY_CLASS;
\r
241 static inline unsigned long throttle_milliseconds(unsigned long throttle) {
\r
242 /* pow() operates on doubles. */
\r
243 unsigned long ret = 1; for (unsigned long i = 1; i < throttle; i++) ret *= 2;
\r
248 Wrapper to be called in a new thread so that we can acknowledge a STOP
\r
249 control immediately.
\r
251 static unsigned long WINAPI shutdown_service(void *arg) {
\r
252 return stop_service((nssm_service_t *) arg, 0, true, true);
\r
255 /* Connect to the service manager */
\r
256 SC_HANDLE open_service_manager(unsigned long access) {
\r
257 SC_HANDLE ret = OpenSCManager(0, SERVICES_ACTIVE_DATABASE, access);
\r
259 if (is_admin) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENSCMANAGER_FAILED, 0);
\r
266 /* Open a service by name or display name. */
\r
267 SC_HANDLE open_service(SC_HANDLE services, TCHAR *service_name, unsigned long access, TCHAR *canonical_name, unsigned long canonical_namelen) {
\r
268 SC_HANDLE service_handle = OpenService(services, service_name, access);
\r
269 if (service_handle) {
\r
270 if (canonical_name && canonical_name != service_name) {
\r
271 TCHAR displayname[SERVICE_NAME_LENGTH];
\r
272 unsigned long displayname_len = (unsigned long) _countof(displayname);
\r
273 GetServiceDisplayName(services, service_name, displayname, &displayname_len);
\r
274 unsigned long keyname_len = canonical_namelen;
\r
275 GetServiceKeyName(services, displayname, canonical_name, &keyname_len);
\r
277 return service_handle;
\r
280 unsigned long error = GetLastError();
\r
281 if (error != ERROR_SERVICE_DOES_NOT_EXIST) {
\r
282 print_message(stderr, NSSM_MESSAGE_OPENSERVICE_FAILED, error_string(GetLastError()));
\r
286 /* We can't look for a display name because there's no buffer to store it. */
\r
287 if (! canonical_name) {
\r
288 print_message(stderr, NSSM_MESSAGE_OPENSERVICE_FAILED, error_string(GetLastError()));
\r
292 unsigned long bufsize, required, count, i;
\r
293 unsigned long resume = 0;
\r
294 EnumServicesStatus(services, SERVICE_DRIVER | SERVICE_FILE_SYSTEM_DRIVER | SERVICE_KERNEL_DRIVER | SERVICE_WIN32, SERVICE_STATE_ALL, 0, 0, &required, &count, &resume);
\r
295 error = GetLastError();
\r
296 if (error != ERROR_MORE_DATA) {
\r
297 print_message(stderr, NSSM_MESSAGE_ENUMSERVICESSTATUS_FAILED, error_string(GetLastError()));
\r
301 ENUM_SERVICE_STATUS *status = (ENUM_SERVICE_STATUS *) HeapAlloc(GetProcessHeap(), 0, required);
\r
303 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("ENUM_SERVICE_STATUS"), _T("open_service()"));
\r
307 bufsize = required;
\r
310 EnumServicesStatus() returns:
\r
311 1 when it retrieved data and there's no more data to come.
\r
312 0 and sets last error to ERROR_MORE_DATA when it retrieved data and
\r
313 there's more data to come.
\r
314 0 and sets last error to something else on error.
\r
316 int ret = EnumServicesStatus(services, SERVICE_DRIVER | SERVICE_FILE_SYSTEM_DRIVER | SERVICE_KERNEL_DRIVER | SERVICE_WIN32, SERVICE_STATE_ALL, status, bufsize, &required, &count, &resume);
\r
318 error = GetLastError();
\r
319 if (error != ERROR_MORE_DATA) {
\r
320 HeapFree(GetProcessHeap(), 0, status);
\r
321 print_message(stderr, NSSM_MESSAGE_ENUMSERVICESSTATUS_FAILED, error_string(GetLastError()));
\r
326 for (i = 0; i < count; i++) {
\r
327 if (str_equiv(status[i].lpDisplayName, service_name)) {
\r
328 if (_sntprintf_s(canonical_name, canonical_namelen, _TRUNCATE, _T("%s"), status[i].lpServiceName) < 0) {
\r
329 HeapFree(GetProcessHeap(), 0, status);
\r
330 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("canonical_name"), _T("open_service()"));
\r
334 HeapFree(GetProcessHeap(), 0, status);
\r
335 return open_service(services, canonical_name, access, 0, 0);
\r
342 /* Recurse so we can get an error message. */
\r
343 return open_service(services, service_name, access, 0, 0);
\r
346 QUERY_SERVICE_CONFIG *query_service_config(const TCHAR *service_name, SC_HANDLE service_handle) {
\r
347 QUERY_SERVICE_CONFIG *qsc;
\r
348 unsigned long bufsize;
\r
349 unsigned long error;
\r
351 QueryServiceConfig(service_handle, 0, 0, &bufsize);
\r
352 error = GetLastError();
\r
353 if (error == ERROR_INSUFFICIENT_BUFFER) {
\r
354 qsc = (QUERY_SERVICE_CONFIG *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, bufsize);
\r
356 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("QUERY_SERVICE_CONFIG"), _T("query_service_config()"), 0);
\r
361 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG_FAILED, service_name, error_string(error), 0);
\r
365 if (! QueryServiceConfig(service_handle, qsc, bufsize, &bufsize)) {
\r
366 HeapFree(GetProcessHeap(), 0, qsc);
\r
367 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG_FAILED, service_name, error_string(GetLastError()), 0);
\r
374 int set_service_dependencies(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR *buffer) {
\r
375 TCHAR *dependencies = _T("");
\r
376 unsigned long num_dependencies = 0;
\r
378 if (buffer && buffer[0]) {
\r
379 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
\r
381 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
386 Count the dependencies then allocate a buffer big enough for their
\r
387 canonical names, ie n * SERVICE_NAME_LENGTH.
\r
391 for (s = buffer; *s; s++) {
\r
392 num_dependencies++;
\r
393 if (*s == SC_GROUP_IDENTIFIER) groups = s;
\r
397 /* At least one dependency is a group so we need to verify them. */
\r
400 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, NSSM_REGISTRY_GROUPS, 0, KEY_READ, &key)) {
\r
401 _ftprintf(stderr, _T("%s: %s\n"), NSSM_REGISTRY_GROUPS, error_string(GetLastError()));
\r
405 unsigned long type;
\r
406 unsigned long groupslen;
\r
407 unsigned long ret = RegQueryValueEx(key, NSSM_REG_GROUPS, 0, &type, NULL, &groupslen);
\r
408 if (ret == ERROR_SUCCESS) {
\r
409 groups = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, groupslen);
\r
411 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("groups"), _T("set_service_dependencies()"));
\r
415 ret = RegQueryValueEx(key, NSSM_REG_GROUPS, 0, &type, (unsigned char *) groups, &groupslen);
\r
416 if (ret != ERROR_SUCCESS) {
\r
417 _ftprintf(stderr, _T("%s\\%s: %s"), NSSM_REGISTRY_GROUPS, NSSM_REG_GROUPS, error_string(GetLastError()));
\r
418 HeapFree(GetProcessHeap(), 0, groups);
\r
423 else if (ret != ERROR_FILE_NOT_FOUND) {
\r
424 _ftprintf(stderr, _T("%s\\%s: %s"), NSSM_REGISTRY_GROUPS, NSSM_REG_GROUPS, error_string(GetLastError()));
\r
433 unsigned long dependencieslen = (num_dependencies * SERVICE_NAME_LENGTH) + 2;
\r
434 dependencies = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dependencieslen * sizeof(TCHAR));
\r
437 TCHAR dependency[SERVICE_NAME_LENGTH];
\r
438 for (s = buffer; *s; s++) {
\r
440 if (*s == SC_GROUP_IDENTIFIER) {
\r
441 TCHAR *group = s + 1;
\r
445 for (TCHAR *g = groups; *g; g++) {
\r
446 if (str_equiv(g, group)) {
\r
448 /* Set canonical name. */
\r
449 memmove(group, g, _tcslen(g) * sizeof(TCHAR));
\r
457 if (ok) _sntprintf_s(dependency, _countof(dependency), _TRUNCATE, _T("%s"), s);
\r
459 HeapFree(GetProcessHeap(), 0, dependencies);
\r
460 if (groups) HeapFree(GetProcessHeap(), 0, groups);
\r
461 _ftprintf(stderr, _T("%s: %s"), s, error_string(ERROR_SERVICE_DEPENDENCY_DELETED));
\r
466 SC_HANDLE dependency_handle = open_service(services, s, SERVICE_QUERY_STATUS, dependency, _countof(dependency));
\r
467 if (! dependency_handle) {
\r
468 HeapFree(GetProcessHeap(), 0, dependencies);
\r
469 if (groups) HeapFree(GetProcessHeap(), 0, groups);
\r
470 CloseServiceHandle(services);
\r
471 _ftprintf(stderr, _T("%s: %s"), s, error_string(ERROR_SERVICE_DEPENDENCY_DELETED));
\r
476 size_t len = _tcslen(dependency) + 1;
\r
477 memmove(dependencies + i, dependency, len * sizeof(TCHAR));
\r
483 if (groups) HeapFree(GetProcessHeap(), 0, groups);
\r
484 CloseServiceHandle(services);
\r
487 if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, dependencies, 0, 0, 0)) {
\r
488 if (num_dependencies) HeapFree(GetProcessHeap(), 0, dependencies);
\r
489 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
493 if (num_dependencies) HeapFree(GetProcessHeap(), 0, dependencies);
\r
497 int get_service_dependencies(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR **buffer, unsigned long *bufsize, int type) {
\r
498 if (! buffer) return 1;
\r
499 if (! bufsize) return 2;
\r
504 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
505 if (! qsc) return 3;
\r
507 if (! qsc->lpDependencies) return 0;
\r
508 if (! qsc->lpDependencies[0]) return 0;
\r
510 /* lpDependencies is doubly NULL terminated. */
\r
511 while (qsc->lpDependencies[*bufsize]) {
\r
512 while (qsc->lpDependencies[*bufsize]) ++*bufsize;
\r
518 *buffer = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *bufsize * sizeof(TCHAR));
\r
521 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("lpDependencies"), _T("get_service_dependencies()"));
\r
525 if (type == DEPENDENCY_ALL) memmove(*buffer, qsc->lpDependencies, *bufsize * sizeof(TCHAR));
\r
530 for (s = qsc->lpDependencies; *s; s++) {
\r
531 /* Only copy the appropriate type of dependency. */
\r
532 if ((*s == SC_GROUP_IDENTIFIER && type & DEPENDENCY_GROUPS) || (*s != SC_GROUP_IDENTIFIER && type & DEPENDENCY_SERVICES)) {
\r
533 size_t len = _tcslen(s) + 1;
\r
534 *bufsize += (unsigned long) len;
\r
535 memmove(*buffer + i, s, len * sizeof(TCHAR));
\r
544 HeapFree(GetProcessHeap(), 0, qsc);
\r
549 int get_service_dependencies(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR **buffer, unsigned long *bufsize) {
\r
550 return get_service_dependencies(service_name, service_handle, buffer, bufsize, DEPENDENCY_ALL);
\r
553 int set_service_description(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR *buffer) {
\r
554 SERVICE_DESCRIPTION description;
\r
555 ZeroMemory(&description, sizeof(description));
\r
557 lpDescription must be NULL if we aren't changing, the new description
\r
560 if (buffer && buffer[0]) description.lpDescription = buffer;
\r
561 else description.lpDescription = _T("");
\r
563 if (ChangeServiceConfig2(service_handle, SERVICE_CONFIG_DESCRIPTION, &description)) return 0;
\r
565 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_DESCRIPTION_FAILED, service_name, error_string(GetLastError()), 0);
\r
569 int get_service_description(const TCHAR *service_name, SC_HANDLE service_handle, unsigned long len, TCHAR *buffer) {
\r
570 if (! buffer) return 1;
\r
572 unsigned long bufsize;
\r
573 QueryServiceConfig2(service_handle, SERVICE_CONFIG_DESCRIPTION, 0, 0, &bufsize);
\r
574 unsigned long error = GetLastError();
\r
575 if (error == ERROR_INSUFFICIENT_BUFFER) {
\r
576 SERVICE_DESCRIPTION *description = (SERVICE_DESCRIPTION *) HeapAlloc(GetProcessHeap(), 0, bufsize);
\r
577 if (! description) {
\r
578 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("SERVICE_CONFIG_DESCRIPTION"), _T("get_service_description()"));
\r
582 if (QueryServiceConfig2(service_handle, SERVICE_CONFIG_DESCRIPTION, (unsigned char *) description, bufsize, &bufsize)) {
\r
583 if (description->lpDescription) _sntprintf_s(buffer, len, _TRUNCATE, _T("%s"), description->lpDescription);
\r
584 else ZeroMemory(buffer, len * sizeof(TCHAR));
\r
585 HeapFree(GetProcessHeap(), 0, description);
\r
589 HeapFree(GetProcessHeap(), 0, description);
\r
590 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_CONFIG_DESCRIPTION"), error_string(error));
\r
595 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_CONFIG_DESCRIPTION"), error_string(error));
\r
602 int get_service_startup(const TCHAR *service_name, SC_HANDLE service_handle, const QUERY_SERVICE_CONFIG *qsc, unsigned long *startup) {
\r
603 if (! qsc) return 1;
\r
605 switch (qsc->dwStartType) {
\r
606 case SERVICE_DEMAND_START: *startup = NSSM_STARTUP_MANUAL; break;
\r
607 case SERVICE_DISABLED: *startup = NSSM_STARTUP_DISABLED; break;
\r
608 default: *startup = NSSM_STARTUP_AUTOMATIC;
\r
611 if (*startup != NSSM_STARTUP_AUTOMATIC) return 0;
\r
613 /* Check for delayed start. */
\r
614 unsigned long bufsize;
\r
615 unsigned long error;
\r
616 QueryServiceConfig2(service_handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, 0, 0, &bufsize);
\r
617 error = GetLastError();
\r
618 if (error == ERROR_INSUFFICIENT_BUFFER) {
\r
619 SERVICE_DELAYED_AUTO_START_INFO *info = (SERVICE_DELAYED_AUTO_START_INFO *) HeapAlloc(GetProcessHeap(), 0, bufsize);
\r
621 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("SERVICE_DELAYED_AUTO_START_INFO"), _T("get_service_startup()"));
\r
625 if (QueryServiceConfig2(service_handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, (unsigned char *) info, bufsize, &bufsize)) {
\r
626 if (info->fDelayedAutostart) *startup = NSSM_STARTUP_DELAYED;
\r
627 HeapFree(GetProcessHeap(), 0, info);
\r
631 error = GetLastError();
\r
632 if (error != ERROR_INVALID_LEVEL) {
\r
633 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_CONFIG_DELAYED_AUTO_START_INFO"), error_string(error));
\r
638 else if (error != ERROR_INVALID_LEVEL) {
\r
639 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_DELAYED_AUTO_START_INFO"), error_string(error));
\r
646 int get_service_username(const TCHAR *service_name, const QUERY_SERVICE_CONFIG *qsc, TCHAR **username, size_t *usernamelen) {
\r
647 if (! username) return 1;
\r
648 if (! usernamelen) return 1;
\r
653 if (! qsc) return 1;
\r
655 if (qsc->lpServiceStartName[0]) {
\r
656 if (is_localsystem(qsc->lpServiceStartName)) return 0;
\r
658 size_t len = _tcslen(qsc->lpServiceStartName);
\r
659 *username = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(TCHAR));
\r
661 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("username"), _T("get_service_username()"));
\r
665 memmove(*username, qsc->lpServiceStartName, (len + 1) * sizeof(TCHAR));
\r
666 *usernamelen = len;
\r
672 /* Set default values which aren't zero. */
\r
673 void set_nssm_service_defaults(nssm_service_t *service) {
\r
674 if (! service) return;
\r
676 service->type = SERVICE_WIN32_OWN_PROCESS;
\r
677 service->priority = NORMAL_PRIORITY_CLASS;
\r
678 service->stdin_sharing = NSSM_STDIN_SHARING;
\r
679 service->stdin_disposition = NSSM_STDIN_DISPOSITION;
\r
680 service->stdin_flags = NSSM_STDIN_FLAGS;
\r
681 service->stdout_sharing = NSSM_STDOUT_SHARING;
\r
682 service->stdout_disposition = NSSM_STDOUT_DISPOSITION;
\r
683 service->stdout_flags = NSSM_STDOUT_FLAGS;
\r
684 service->stderr_sharing = NSSM_STDERR_SHARING;
\r
685 service->stderr_disposition = NSSM_STDERR_DISPOSITION;
\r
686 service->stderr_flags = NSSM_STDERR_FLAGS;
\r
687 service->throttle_delay = NSSM_RESET_THROTTLE_RESTART;
\r
688 service->stop_method = ~0;
\r
689 service->kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;
\r
690 service->kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;
\r
691 service->kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;
\r
692 service->kill_process_tree = 1;
\r
695 /* Allocate and zero memory for a service. */
\r
696 nssm_service_t *alloc_nssm_service() {
\r
697 nssm_service_t *service = (nssm_service_t *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(nssm_service_t));
\r
698 if (! service) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("service"), _T("alloc_nssm_service()"), 0);
\r
702 /* Free memory for a service. */
\r
703 void cleanup_nssm_service(nssm_service_t *service) {
\r
704 if (! service) return;
\r
705 if (service->username) HeapFree(GetProcessHeap(), 0, service->username);
\r
706 if (service->password) {
\r
707 SecureZeroMemory(service->password, service->passwordlen);
\r
708 HeapFree(GetProcessHeap(), 0, service->password);
\r
710 if (service->dependencies) HeapFree(GetProcessHeap(), 0, service->dependencies);
\r
711 if (service->env) HeapFree(GetProcessHeap(), 0, service->env);
\r
712 if (service->env_extra) HeapFree(GetProcessHeap(), 0, service->env_extra);
\r
713 if (service->handle) CloseServiceHandle(service->handle);
\r
714 if (service->process_handle) CloseHandle(service->process_handle);
\r
715 if (service->wait_handle) UnregisterWait(service->wait_handle);
\r
716 if (service->throttle_section_initialised) DeleteCriticalSection(&service->throttle_section);
\r
717 if (service->throttle_timer) CloseHandle(service->throttle_timer);
\r
718 if (service->initial_env) FreeEnvironmentStrings(service->initial_env);
\r
719 HeapFree(GetProcessHeap(), 0, service);
\r
722 /* About to install the service */
\r
723 int pre_install_service(int argc, TCHAR **argv) {
\r
724 nssm_service_t *service = alloc_nssm_service();
\r
725 set_nssm_service_defaults(service);
\r
726 if (argc) _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]);
\r
728 /* Show the dialogue box if we didn't give the service name and path */
\r
729 if (argc < 2) return nssm_gui(IDD_INSTALL, service);
\r
732 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("service"), _T("pre_install_service()"));
\r
735 _sntprintf_s(service->exe, _countof(service->exe), _TRUNCATE, _T("%s"), argv[1]);
\r
737 /* Arguments are optional */
\r
738 size_t flagslen = 0;
\r
741 for (i = 2; i < argc; i++) flagslen += _tcslen(argv[i]) + 1;
\r
742 if (! flagslen) flagslen = 1;
\r
743 if (flagslen > _countof(service->flags)) {
\r
744 print_message(stderr, NSSM_MESSAGE_FLAGS_TOO_LONG);
\r
748 for (i = 2; i < argc; i++) {
\r
749 size_t len = _tcslen(argv[i]);
\r
750 memmove(service->flags + s, argv[i], len * sizeof(TCHAR));
\r
752 if (i < argc - 1) service->flags[s++] = _T(' ');
\r
755 /* Work out directory name */
\r
756 _sntprintf_s(service->dir, _countof(service->dir), _TRUNCATE, _T("%s"), service->exe);
\r
757 strip_basename(service->dir);
\r
759 int ret = install_service(service);
\r
760 cleanup_nssm_service(service);
\r
764 /* About to edit the service. */
\r
765 int pre_edit_service(int argc, TCHAR **argv) {
\r
766 /* Require service name. */
\r
767 if (argc < 2) return usage(1);
\r
769 /* Are we editing on the command line? */
\r
770 enum { MODE_EDITING, MODE_GETTING, MODE_SETTING, MODE_RESETTING } mode = MODE_EDITING;
\r
771 const TCHAR *verb = argv[0];
\r
772 const TCHAR *service_name = argv[1];
\r
773 bool getting = false;
\r
774 bool unsetting = false;
\r
776 /* Minimum number of arguments. */
\r
778 /* Index of first value. */
\r
781 if (str_equiv(verb, _T("get"))) {
\r
783 mode = MODE_GETTING;
\r
785 else if (str_equiv(verb, _T("set"))) {
\r
787 mode = MODE_SETTING;
\r
789 else if (str_equiv(verb, _T("reset")) || str_equiv(verb, _T("unset"))) {
\r
791 mode = MODE_RESETTING;
\r
793 if (argc < mandatory) return usage(1);
\r
795 const TCHAR *parameter = 0;
\r
796 settings_t *setting = 0;
\r
799 /* Validate the parameter. */
\r
800 if (mandatory > 2) {
\r
801 bool additional_mandatory = false;
\r
803 parameter = argv[2];
\r
804 for (i = 0; settings[i].name; i++) {
\r
805 setting = &settings[i];
\r
806 if (! str_equiv(setting->name, parameter)) continue;
\r
807 if (((setting->additional & ADDITIONAL_GETTING) && mode == MODE_GETTING) || ((setting->additional & ADDITIONAL_SETTING) && mode == MODE_SETTING) || ((setting->additional & ADDITIONAL_RESETTING) && mode == MODE_RESETTING)) {
\r
808 additional_mandatory = true;
\r
813 if (! settings[i].name) {
\r
814 print_message(stderr, NSSM_MESSAGE_INVALID_PARAMETER, parameter);
\r
815 for (i = 0; settings[i].name; i++) _ftprintf(stderr, _T("%s\n"), settings[i].name);
\r
820 if (additional_mandatory) {
\r
821 if (argc < mandatory) {
\r
822 print_message(stderr, NSSM_MESSAGE_MISSING_SUBPARAMETER, parameter);
\r
825 additional = argv[3];
\r
828 else if (str_equiv(setting->name, NSSM_NATIVE_OBJECTNAME) && mode == MODE_SETTING) {
\r
829 additional = argv[3];
\r
833 additional = argv[remainder];
\r
834 if (argc < mandatory) return usage(1);
\r
838 nssm_service_t *service = alloc_nssm_service();
\r
839 _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), service_name);
\r
841 /* Open service manager */
\r
842 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
\r
844 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
848 /* Try to open the service */
\r
849 unsigned long access = SERVICE_QUERY_CONFIG;
\r
850 if (mode != MODE_GETTING) access |= SERVICE_CHANGE_CONFIG;
\r
851 service->handle = open_service(services, service->name, access, service->name, _countof(service->name));
\r
852 if (! service->handle) {
\r
853 CloseServiceHandle(services);
\r
857 /* Get system details. */
\r
858 QUERY_SERVICE_CONFIG *qsc = query_service_config(service->name, service->handle);
\r
860 CloseServiceHandle(service->handle);
\r
861 CloseServiceHandle(services);
\r
865 service->type = qsc->dwServiceType;
\r
866 if (! (service->type & SERVICE_WIN32_OWN_PROCESS)) {
\r
867 if (mode != MODE_GETTING) {
\r
868 HeapFree(GetProcessHeap(), 0, qsc);
\r
869 CloseServiceHandle(service->handle);
\r
870 CloseServiceHandle(services);
\r
871 print_message(stderr, NSSM_MESSAGE_CANNOT_EDIT, service->name, NSSM_WIN32_OWN_PROCESS, 0);
\r
876 if (get_service_startup(service->name, service->handle, qsc, &service->startup)) {
\r
877 if (mode != MODE_GETTING) {
\r
878 HeapFree(GetProcessHeap(), 0, qsc);
\r
879 CloseServiceHandle(service->handle);
\r
880 CloseServiceHandle(services);
\r
885 if (get_service_username(service->name, qsc, &service->username, &service->usernamelen)) {
\r
886 if (mode != MODE_GETTING) {
\r
887 HeapFree(GetProcessHeap(), 0, qsc);
\r
888 CloseServiceHandle(service->handle);
\r
889 CloseServiceHandle(services);
\r
894 _sntprintf_s(service->displayname, _countof(service->displayname), _TRUNCATE, _T("%s"), qsc->lpDisplayName);
\r
896 /* Get the canonical service name. We open it case insensitively. */
\r
897 unsigned long bufsize = _countof(service->name);
\r
898 GetServiceKeyName(services, service->displayname, service->name, &bufsize);
\r
900 /* Remember the executable in case it isn't NSSM. */
\r
901 _sntprintf_s(service->image, _countof(service->image), _TRUNCATE, _T("%s"), qsc->lpBinaryPathName);
\r
902 HeapFree(GetProcessHeap(), 0, qsc);
\r
904 /* Get extended system details. */
\r
905 if (get_service_description(service->name, service->handle, _countof(service->description), service->description)) {
\r
906 if (mode != MODE_GETTING) {
\r
907 CloseServiceHandle(service->handle);
\r
908 CloseServiceHandle(services);
\r
913 if (get_service_dependencies(service->name, service->handle, &service->dependencies, &service->dependencieslen)) {
\r
914 if (mode != MODE_GETTING) {
\r
915 CloseServiceHandle(service->handle);
\r
916 CloseServiceHandle(services);
\r
921 /* Get NSSM details. */
\r
922 get_parameters(service, 0);
\r
924 CloseServiceHandle(services);
\r
926 if (! service->exe[0]) {
\r
927 service->native = true;
\r
928 if (mode != MODE_GETTING) print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE, service->name, NSSM, service->image);
\r
931 /* Editing with the GUI. */
\r
932 if (mode == MODE_EDITING) {
\r
933 nssm_gui(IDD_EDIT, service);
\r
937 /* Trying to manage App* parameters for a non-NSSM service. */
\r
938 if (! setting->native && service->native) {
\r
939 CloseServiceHandle(service->handle);
\r
940 print_message(stderr, NSSM_MESSAGE_NATIVE_PARAMETER, setting->name, NSSM);
\r
948 if (mode == MODE_GETTING) {
\r
949 if (! service->native) {
\r
950 key = open_registry(service->name, KEY_READ);
\r
951 if (! key) return 4;
\r
954 if (setting->native) ret = get_setting(service->name, service->handle, setting, &value, additional);
\r
955 else ret = get_setting(service->name, key, setting, &value, additional);
\r
957 CloseServiceHandle(service->handle);
\r
961 switch (setting->type) {
\r
962 case REG_EXPAND_SZ:
\r
965 _tprintf(_T("%s\n"), value.string ? value.string : _T(""));
\r
966 HeapFree(GetProcessHeap(), 0, value.string);
\r
970 _tprintf(_T("%u\n"), value.numeric);
\r
974 if (! service->native) RegCloseKey(key);
\r
975 CloseServiceHandle(service->handle);
\r
979 /* Build the value. */
\r
980 if (mode == MODE_RESETTING) {
\r
981 /* Unset the parameter. */
\r
984 else if (remainder == argc) {
\r
988 /* Set the parameter. */
\r
990 size_t delimiterlen = (setting->additional & ADDITIONAL_CRLF) ? 2 : 1;
\r
991 for (i = remainder; i < argc; i++) len += _tcslen(argv[i]) + delimiterlen;
\r
994 value.string = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, len * sizeof(TCHAR));
\r
995 if (! value.string) {
\r
996 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("value"), _T("edit_service()"));
\r
997 CloseServiceHandle(service->handle);
\r
1002 for (i = remainder; i < argc; i++) {
\r
1003 size_t len = _tcslen(argv[i]);
\r
1004 memmove(value.string + s, argv[i], len * sizeof(TCHAR));
\r
1006 if (i < argc - 1) {
\r
1007 if (setting->additional & ADDITIONAL_CRLF) {
\r
1008 value.string[s++] = _T('\r');
\r
1009 value.string[s++] = _T('\n');
\r
1011 else value.string[s++] = _T(' ');
\r
1014 value.string[s] = _T('\0');
\r
1017 if (! service->native) {
\r
1018 key = open_registry(service->name, KEY_WRITE);
\r
1020 if (value.string) HeapFree(GetProcessHeap(), 0, value.string);
\r
1025 if (setting->native) ret = set_setting(service->name, service->handle, setting, &value, additional);
\r
1026 else ret = set_setting(service->name, key, setting, &value, additional);
\r
1027 if (value.string) HeapFree(GetProcessHeap(), 0, value.string);
\r
1029 if (! service->native) RegCloseKey(key);
\r
1030 CloseServiceHandle(service->handle);
\r
1034 if (! service->native) RegCloseKey(key);
\r
1035 CloseServiceHandle(service->handle);
\r
1040 /* About to remove the service */
\r
1041 int pre_remove_service(int argc, TCHAR **argv) {
\r
1042 nssm_service_t *service = alloc_nssm_service();
\r
1043 set_nssm_service_defaults(service);
\r
1044 if (argc) _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]);
\r
1046 /* Show dialogue box if we didn't pass service name and "confirm" */
\r
1047 if (argc < 2) return nssm_gui(IDD_REMOVE, service);
\r
1048 if (str_equiv(argv[1], _T("confirm"))) {
\r
1049 int ret = remove_service(service);
\r
1050 cleanup_nssm_service(service);
\r
1053 print_message(stderr, NSSM_MESSAGE_PRE_REMOVE_SERVICE);
\r
1057 /* Install the service */
\r
1058 int install_service(nssm_service_t *service) {
\r
1059 if (! service) return 1;
\r
1061 /* Open service manager */
\r
1062 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE);
\r
1064 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
1065 cleanup_nssm_service(service);
\r
1069 /* Get path of this program */
\r
1070 GetModuleFileName(0, service->image, _countof(service->image));
\r
1072 /* Create the service - settings will be changed in edit_service() */
\r
1073 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
1074 if (! service->handle) {
\r
1075 print_message(stderr, NSSM_MESSAGE_CREATESERVICE_FAILED, error_string(GetLastError()));
\r
1076 CloseServiceHandle(services);
\r
1080 if (edit_service(service, false)) {
\r
1081 DeleteService(service->handle);
\r
1082 CloseServiceHandle(services);
\r
1086 print_message(stdout, NSSM_MESSAGE_SERVICE_INSTALLED, service->name);
\r
1089 CloseServiceHandle(services);
\r
1094 /* Edit the service. */
\r
1095 int edit_service(nssm_service_t *service, bool editing) {
\r
1096 if (! service) return 1;
\r
1099 The only two valid flags for service type are SERVICE_WIN32_OWN_PROCESS
\r
1100 and SERVICE_INTERACTIVE_PROCESS.
\r
1102 service->type &= SERVICE_INTERACTIVE_PROCESS;
\r
1103 service->type |= SERVICE_WIN32_OWN_PROCESS;
\r
1105 /* Startup type. */
\r
1106 unsigned long startup;
\r
1107 switch (service->startup) {
\r
1108 case NSSM_STARTUP_MANUAL: startup = SERVICE_DEMAND_START; break;
\r
1109 case NSSM_STARTUP_DISABLED: startup = SERVICE_DISABLED; break;
\r
1110 default: startup = SERVICE_AUTO_START;
\r
1113 /* Display name. */
\r
1114 if (! service->displayname[0]) _sntprintf_s(service->displayname, _countof(service->displayname), _TRUNCATE, _T("%s"), service->name);
\r
1117 Username must be NULL if we aren't changing or an account name.
\r
1118 We must explicitly use LOCALSYSTEM to change it when we are editing.
\r
1119 Password must be NULL if we aren't changing, a password or "".
\r
1120 Empty passwords are valid but we won't allow them in the GUI.
\r
1122 TCHAR *username = 0;
\r
1124 TCHAR *password = 0;
\r
1125 if (service->usernamelen) {
\r
1126 username = service->username;
\r
1127 if (canonicalise_username(username, &canon)) return 5;
\r
1128 if (service->passwordlen) password = service->password;
\r
1130 else if (editing) username = canon = NSSM_LOCALSYSTEM_ACCOUNT;
\r
1132 if (well_known_username(canon)) password = _T("");
\r
1134 if (grant_logon_as_service(canon)) {
\r
1135 if (canon != username) HeapFree(GetProcessHeap(), 0, canon);
\r
1136 print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username);
\r
1141 TCHAR *dependencies = _T("");
\r
1142 if (service->dependencieslen) dependencies = 0; /* Change later. */
\r
1144 if (! ChangeServiceConfig(service->handle, service->type, startup, SERVICE_NO_CHANGE, 0, 0, 0, dependencies, canon, password, service->displayname)) {
\r
1145 if (canon != username) HeapFree(GetProcessHeap(), 0, canon);
\r
1146 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
1149 if (canon != username) HeapFree(GetProcessHeap(), 0, canon);
\r
1151 if (service->dependencieslen) {
\r
1152 if (set_service_dependencies(service->name, service->handle, service->dependencies)) return 5;
\r
1155 if (service->description[0] || editing) {
\r
1156 set_service_description(service->name, service->handle, service->description);
\r
1159 SERVICE_DELAYED_AUTO_START_INFO delayed;
\r
1160 ZeroMemory(&delayed, sizeof(delayed));
\r
1161 if (service->startup == NSSM_STARTUP_DELAYED) delayed.fDelayedAutostart = 1;
\r
1162 else delayed.fDelayedAutostart = 0;
\r
1163 /* Delayed startup isn't supported until Vista. */
\r
1164 if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, &delayed)) {
\r
1165 unsigned long error = GetLastError();
\r
1166 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
\r
1167 if (error != ERROR_INVALID_LEVEL) {
\r
1168 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_DELAYED_AUTO_START_INFO_FAILED, service->name, error_string(error), 0);
\r
1172 /* Don't mess with parameters which aren't ours. */
\r
1173 if (! service->native) {
\r
1174 /* Now we need to put the parameters into the registry */
\r
1175 if (create_parameters(service, editing)) {
\r
1176 print_message(stderr, NSSM_MESSAGE_CREATE_PARAMETERS_FAILED);
\r
1180 set_service_recovery(service);
\r
1186 /* Control a service. */
\r
1187 int control_service(unsigned long control, int argc, TCHAR **argv) {
\r
1188 if (argc < 1) return usage(1);
\r
1189 TCHAR *service_name = argv[0];
\r
1190 TCHAR canonical_name[SERVICE_NAME_LENGTH];
\r
1192 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
\r
1194 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
1198 unsigned long access = SERVICE_QUERY_STATUS;
\r
1199 switch (control) {
\r
1200 case NSSM_SERVICE_CONTROL_START:
\r
1201 access |= SERVICE_START;
\r
1204 case SERVICE_CONTROL_CONTINUE:
\r
1205 case SERVICE_CONTROL_PAUSE:
\r
1206 access |= SERVICE_PAUSE_CONTINUE;
\r
1209 case SERVICE_CONTROL_STOP:
\r
1210 access |= SERVICE_STOP;
\r
1213 case NSSM_SERVICE_CONTROL_ROTATE:
\r
1214 access |= SERVICE_USER_DEFINED_CONTROL;
\r
1218 SC_HANDLE service_handle = open_service(services, service_name, access, canonical_name, _countof(canonical_name));
\r
1219 if (! service_handle) {
\r
1220 CloseServiceHandle(services);
\r
1225 unsigned long error;
\r
1226 SERVICE_STATUS service_status;
\r
1227 if (control == NSSM_SERVICE_CONTROL_START) {
\r
1228 unsigned long initial_status = SERVICE_STOPPED;
\r
1229 ret = StartService(service_handle, (unsigned long) argc, (const TCHAR **) argv);
\r
1230 error = GetLastError();
\r
1231 CloseServiceHandle(services);
\r
1233 if (error == ERROR_IO_PENDING) {
\r
1235 Older versions of Windows return immediately with ERROR_IO_PENDING
\r
1236 indicate that the operation is still in progress. Newer versions
\r
1237 will return it if there really is a delay.
\r
1240 error = ERROR_SUCCESS;
\r
1244 int response = await_service_control_response(control, service_handle, &service_status, initial_status);
\r
1245 CloseServiceHandle(service_handle);
\r
1248 print_message(stderr, NSSM_MESSAGE_BAD_CONTROL_RESPONSE, canonical_name, service_status_text(service_status.dwCurrentState), service_control_text(control));
\r
1251 else _tprintf(_T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1255 CloseServiceHandle(service_handle);
\r
1256 _ftprintf(stderr, _T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1260 else if (control == SERVICE_CONTROL_INTERROGATE) {
\r
1262 We could actually send an INTERROGATE control but that won't return
\r
1263 any information if the service is stopped and we don't care about
\r
1264 the extra details it might give us in any case. So we'll fake it.
\r
1266 ret = QueryServiceStatus(service_handle, &service_status);
\r
1267 error = GetLastError();
\r
1270 _tprintf(_T("%s\n"), service_status_text(service_status.dwCurrentState));
\r
1274 _ftprintf(stderr, _T("%s: %s\n"), canonical_name, error_string(error));
\r
1279 ret = ControlService(service_handle, control, &service_status);
\r
1280 unsigned long initial_status = service_status.dwCurrentState;
\r
1281 error = GetLastError();
\r
1282 CloseServiceHandle(services);
\r
1284 if (error == ERROR_IO_PENDING) {
\r
1286 error = ERROR_SUCCESS;
\r
1290 int response = await_service_control_response(control, service_handle, &service_status, initial_status);
\r
1291 CloseServiceHandle(service_handle);
\r
1294 print_message(stderr, NSSM_MESSAGE_BAD_CONTROL_RESPONSE, canonical_name, service_status_text(service_status.dwCurrentState), service_control_text(control));
\r
1297 else _tprintf(_T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1301 CloseServiceHandle(service_handle);
\r
1302 _ftprintf(stderr, _T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1303 if (error == ERROR_SERVICE_NOT_ACTIVE) {
\r
1304 if (control == SERVICE_CONTROL_SHUTDOWN || control == SERVICE_CONTROL_STOP) return 0;
\r
1311 /* Remove the service */
\r
1312 int remove_service(nssm_service_t *service) {
\r
1313 if (! service) return 1;
\r
1315 /* Open service manager */
\r
1316 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
\r
1318 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
1322 /* Try to open the service */
\r
1323 service->handle = open_service(services, service->name, DELETE, service->name, _countof(service->name));
\r
1324 if (! service->handle) {
\r
1325 CloseServiceHandle(services);
\r
1329 /* Get the canonical service name. We open it case insensitively. */
\r
1330 unsigned long bufsize = _countof(service->displayname);
\r
1331 GetServiceDisplayName(services, service->name, service->displayname, &bufsize);
\r
1332 bufsize = _countof(service->name);
\r
1333 GetServiceKeyName(services, service->displayname, service->name, &bufsize);
\r
1335 /* Try to delete the service */
\r
1336 if (! DeleteService(service->handle)) {
\r
1337 print_message(stderr, NSSM_MESSAGE_DELETESERVICE_FAILED);
\r
1338 CloseServiceHandle(services);
\r
1343 CloseServiceHandle(services);
\r
1345 print_message(stdout, NSSM_MESSAGE_SERVICE_REMOVED, service->name);
\r
1349 /* Service initialisation */
\r
1350 void WINAPI service_main(unsigned long argc, TCHAR **argv) {
\r
1351 nssm_service_t *service = alloc_nssm_service();
\r
1352 if (! service) return;
\r
1354 if (_sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]) < 0) {
\r
1355 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("service->name"), _T("service_main()"), 0);
\r
1359 /* We can use a condition variable in a critical section on Vista or later. */
\r
1360 if (imports.SleepConditionVariableCS && imports.WakeConditionVariable) use_critical_section = true;
\r
1361 else use_critical_section = false;
\r
1363 /* Initialise status */
\r
1364 ZeroMemory(&service->status, sizeof(service->status));
\r
1365 service->status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;
\r
1366 service->status.dwControlsAccepted = SERVICE_ACCEPT_POWEREVENT | SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE;
\r
1367 service->status.dwWin32ExitCode = NO_ERROR;
\r
1368 service->status.dwServiceSpecificExitCode = 0;
\r
1369 service->status.dwCheckPoint = 0;
\r
1370 service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;
\r
1372 /* Signal we AREN'T running the server */
\r
1373 service->process_handle = 0;
\r
1376 /* Register control handler */
\r
1377 service->status_handle = RegisterServiceCtrlHandlerEx(NSSM, service_control_handler, (void *) service);
\r
1378 if (! service->status_handle) {
\r
1379 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_REGISTERSERVICECTRLHANDER_FAILED, error_string(GetLastError()), 0);
\r
1383 log_service_control(service->name, 0, true);
\r
1385 service->status.dwCurrentState = SERVICE_START_PENDING;
\r
1386 service->status.dwWaitHint = service->throttle_delay + NSSM_WAITHINT_MARGIN;
\r
1387 SetServiceStatus(service->status_handle, &service->status);
\r
1390 /* Try to create the exit action parameters; we don't care if it fails */
\r
1391 create_exit_action(service->name, exit_action_strings[0], false);
\r
1393 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT);
\r
1395 service->handle = open_service(services, service->name, SERVICE_CHANGE_CONFIG, 0, 0);
\r
1396 set_service_recovery(service);
\r
1398 CloseServiceHandle(services);
\r
1402 /* Used for signalling a resume if the service pauses when throttled. */
\r
1403 if (use_critical_section) {
\r
1404 InitializeCriticalSection(&service->throttle_section);
\r
1405 service->throttle_section_initialised = true;
\r
1408 service->throttle_timer = CreateWaitableTimer(0, 1, 0);
\r
1409 if (! service->throttle_timer) {
\r
1410 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_CREATEWAITABLETIMER_FAILED, service->name, error_string(GetLastError()), 0);
\r
1414 /* Remember our initial environment. */
\r
1415 service->initial_env = GetEnvironmentStrings();
\r
1417 monitor_service(service);
\r
1420 /* Make sure service recovery actions are taken where necessary */
\r
1421 void set_service_recovery(nssm_service_t *service) {
\r
1422 SERVICE_FAILURE_ACTIONS_FLAG flag;
\r
1423 ZeroMemory(&flag, sizeof(flag));
\r
1424 flag.fFailureActionsOnNonCrashFailures = true;
\r
1426 /* This functionality was added in Vista so the call may fail */
\r
1427 if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, &flag)) {
\r
1428 unsigned long error = GetLastError();
\r
1429 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
\r
1430 if (error != ERROR_INVALID_LEVEL) {
\r
1431 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_FAILURE_ACTIONS_FAILED, service->name, error_string(error), 0);
\r
1436 int monitor_service(nssm_service_t *service) {
\r
1437 /* Set service status to started */
\r
1438 int ret = start_service(service);
\r
1441 _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%d"), ret);
\r
1442 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_START_SERVICE_FAILED, service->exe, service->name, ret, 0);
\r
1445 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_STARTED_SERVICE, service->exe, service->flags, service->name, service->dir, 0);
\r
1447 /* Monitor service */
\r
1448 if (! RegisterWaitForSingleObject(&service->wait_handle, service->process_handle, end_service, (void *) service, INFINITE, WT_EXECUTEONLYONCE | WT_EXECUTELONGFUNCTION)) {
\r
1449 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_REGISTERWAITFORSINGLEOBJECT_FAILED, service->name, service->exe, error_string(GetLastError()), 0);
\r
1455 TCHAR *service_control_text(unsigned long control) {
\r
1456 switch (control) {
\r
1457 /* HACK: there is no SERVICE_CONTROL_START constant */
\r
1458 case NSSM_SERVICE_CONTROL_START: return _T("START");
\r
1459 case SERVICE_CONTROL_STOP: return _T("STOP");
\r
1460 case SERVICE_CONTROL_SHUTDOWN: return _T("SHUTDOWN");
\r
1461 case SERVICE_CONTROL_PAUSE: return _T("PAUSE");
\r
1462 case SERVICE_CONTROL_CONTINUE: return _T("CONTINUE");
\r
1463 case SERVICE_CONTROL_INTERROGATE: return _T("INTERROGATE");
\r
1464 case NSSM_SERVICE_CONTROL_ROTATE: return _T("ROTATE");
\r
1465 case SERVICE_CONTROL_POWEREVENT: return _T("POWEREVENT");
\r
1466 default: return 0;
\r
1470 TCHAR *service_status_text(unsigned long status) {
\r
1472 case SERVICE_STOPPED: return _T("SERVICE_STOPPED");
\r
1473 case SERVICE_START_PENDING: return _T("SERVICE_START_PENDING");
\r
1474 case SERVICE_STOP_PENDING: return _T("SERVICE_STOP_PENDING");
\r
1475 case SERVICE_RUNNING: return _T("SERVICE_RUNNING");
\r
1476 case SERVICE_CONTINUE_PENDING: return _T("SERVICE_CONTINUE_PENDING");
\r
1477 case SERVICE_PAUSE_PENDING: return _T("SERVICE_PAUSE_PENDING");
\r
1478 case SERVICE_PAUSED: return _T("SERVICE_PAUSED");
\r
1479 default: return 0;
\r
1483 void log_service_control(TCHAR *service_name, unsigned long control, bool handled) {
\r
1484 TCHAR *text = service_control_text(control);
\r
1485 unsigned long event;
\r
1488 /* "0x" + 8 x hex + NULL */
\r
1489 text = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, 11 * sizeof(TCHAR));
\r
1491 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);
\r
1494 if (_sntprintf_s(text, 11, _TRUNCATE, _T("0x%08x"), control) < 0) {
\r
1495 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);
\r
1496 HeapFree(GetProcessHeap(), 0, text);
\r
1500 event = NSSM_EVENT_SERVICE_CONTROL_UNKNOWN;
\r
1502 else if (handled) event = NSSM_EVENT_SERVICE_CONTROL_HANDLED;
\r
1503 else event = NSSM_EVENT_SERVICE_CONTROL_NOT_HANDLED;
\r
1505 log_event(EVENTLOG_INFORMATION_TYPE, event, service_name, text, 0);
\r
1507 if (event == NSSM_EVENT_SERVICE_CONTROL_UNKNOWN) {
\r
1508 HeapFree(GetProcessHeap(), 0, text);
\r
1512 /* Service control handler */
\r
1513 unsigned long WINAPI service_control_handler(unsigned long control, unsigned long event, void *data, void *context) {
\r
1514 nssm_service_t *service = (nssm_service_t *) context;
\r
1516 switch (control) {
\r
1517 case SERVICE_CONTROL_INTERROGATE:
\r
1518 /* We always keep the service status up-to-date so this is a no-op. */
\r
1521 case SERVICE_CONTROL_SHUTDOWN:
\r
1522 case SERVICE_CONTROL_STOP:
\r
1523 log_service_control(service->name, control, true);
\r
1525 We MUST acknowledge the stop request promptly but we're committed to
\r
1526 waiting for the application to exit. Spawn a new thread to wait
\r
1527 while we acknowledge the request.
\r
1529 if (! CreateThread(NULL, 0, shutdown_service, context, 0, NULL)) {
\r
1530 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETHREAD_FAILED, error_string(GetLastError()), 0);
\r
1533 We couldn't create a thread to tidy up so we'll have to force the tidyup
\r
1534 to complete in time in this thread.
\r
1536 service->kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;
\r
1537 service->kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;
\r
1538 service->kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;
\r
1540 stop_service(service, 0, true, true);
\r
1544 case SERVICE_CONTROL_CONTINUE:
\r
1545 log_service_control(service->name, control, true);
\r
1546 service->throttle = 0;
\r
1547 if (use_critical_section) imports.WakeConditionVariable(&service->throttle_condition);
\r
1549 if (! service->throttle_timer) return ERROR_CALL_NOT_IMPLEMENTED;
\r
1550 ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));
\r
1551 SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);
\r
1553 /* We can't continue if the application is running! */
\r
1554 if (! service->process_handle) service->status.dwCurrentState = SERVICE_CONTINUE_PENDING;
\r
1555 service->status.dwWaitHint = throttle_milliseconds(service->throttle) + NSSM_WAITHINT_MARGIN;
\r
1556 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESET_THROTTLE, service->name, 0);
\r
1557 SetServiceStatus(service->status_handle, &service->status);
\r
1560 case SERVICE_CONTROL_PAUSE:
\r
1562 We don't accept pause messages but it isn't possible to register
\r
1563 only for continue messages so we have to handle this case.
\r
1565 log_service_control(service->name, control, false);
\r
1566 return ERROR_CALL_NOT_IMPLEMENTED;
\r
1568 case NSSM_SERVICE_CONTROL_ROTATE:
\r
1569 log_service_control(service->name, control, true);
\r
1570 if (service->rotate_stdout_online == NSSM_ROTATE_ONLINE) service->rotate_stdout_online = NSSM_ROTATE_ONLINE_ASAP;
\r
1571 if (service->rotate_stderr_online == NSSM_ROTATE_ONLINE) service->rotate_stderr_online = NSSM_ROTATE_ONLINE_ASAP;
\r
1574 case SERVICE_CONTROL_POWEREVENT:
\r
1575 if (event != PBT_APMRESUMEAUTOMATIC) {
\r
1576 log_service_control(service->name, control, false);
\r
1579 log_service_control(service->name, control, true);
\r
1580 end_service((void *) service, false);
\r
1584 /* Unknown control */
\r
1585 log_service_control(service->name, control, false);
\r
1586 return ERROR_CALL_NOT_IMPLEMENTED;
\r
1589 /* Start the service */
\r
1590 int start_service(nssm_service_t *service) {
\r
1591 service->stopping = false;
\r
1592 service->allow_restart = true;
\r
1594 if (service->process_handle) return 0;
\r
1596 /* Allocate a STARTUPINFO structure for a new process */
\r
1598 ZeroMemory(&si, sizeof(si));
\r
1599 si.cb = sizeof(si);
\r
1601 /* Allocate a PROCESSINFO structure for the process */
\r
1602 PROCESS_INFORMATION pi;
\r
1603 ZeroMemory(&pi, sizeof(pi));
\r
1605 /* Get startup parameters */
\r
1606 int ret = get_parameters(service, &si);
\r
1608 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_PARAMETERS_FAILED, service->name, 0);
\r
1609 return stop_service(service, 2, true, true);
\r
1612 /* Launch executable with arguments */
\r
1613 TCHAR cmd[CMD_LENGTH];
\r
1614 if (_sntprintf_s(cmd, _countof(cmd), _TRUNCATE, _T("\"%s\" %s"), service->exe, service->flags) < 0) {
\r
1615 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("command line"), _T("start_service"), 0);
\r
1616 return stop_service(service, 2, true, true);
\r
1619 throttle_restart(service);
\r
1621 /* Set the environment. */
\r
1622 if (service->env) duplicate_environment(service->env);
\r
1623 if (service->env_extra) set_environment_block(service->env_extra);
\r
1625 /* Set up I/O redirection. */
\r
1626 if (get_output_handles(service, &si)) {
\r
1627 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_OUTPUT_HANDLES_FAILED, service->name, 0);
\r
1628 if (! service->no_console) FreeConsole();
\r
1629 close_output_handles(&si);
\r
1630 return stop_service(service, 4, true, true);
\r
1633 bool inherit_handles = false;
\r
1634 if (si.dwFlags & STARTF_USESTDHANDLES) inherit_handles = true;
\r
1635 unsigned long flags = service->priority & priority_mask();
\r
1636 if (service->affinity) flags |= CREATE_SUSPENDED;
\r
1637 if (! CreateProcess(0, cmd, 0, 0, inherit_handles, flags, 0, service->dir, &si, &pi)) {
\r
1638 unsigned long exitcode = 3;
\r
1639 unsigned long error = GetLastError();
\r
1640 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service->name, service->exe, error_string(error), 0);
\r
1641 close_output_handles(&si);
\r
1642 duplicate_environment_strings(service->initial_env);
\r
1643 return stop_service(service, exitcode, true, true);
\r
1645 service->process_handle = pi.hProcess;
\r
1646 service->pid = pi.dwProcessId;
\r
1648 if (get_process_creation_time(service->process_handle, &service->creation_time)) ZeroMemory(&service->creation_time, sizeof(service->creation_time));
\r
1650 close_output_handles(&si);
\r
1652 if (! service->no_console) FreeConsole();
\r
1654 /* Restore our environment. */
\r
1655 duplicate_environment_strings(service->initial_env);
\r
1657 if (service->affinity) {
\r
1659 We are explicitly storing service->affinity as a 64-bit unsigned integer
\r
1660 so that we can parse it regardless of whether we're running in 32-bit
\r
1661 or 64-bit mode. The arguments to SetProcessAffinityMask(), however, are
\r
1662 defined as type DWORD_PTR and hence limited to 32 bits on a 32-bit system
\r
1663 (or when running the 32-bit NSSM).
\r
1665 The result is a lot of seemingly-unnecessary casting throughout the code
\r
1666 and potentially confusion when we actually try to start the service.
\r
1667 Having said that, however, it's unlikely that we're actually going to
\r
1668 run in 32-bit mode on a system which has more than 32 CPUs so the
\r
1669 likelihood of seeing a confusing situation is somewhat diminished.
\r
1671 DWORD_PTR affinity, system_affinity;
\r
1673 if (GetProcessAffinityMask(service->process_handle, &affinity, &system_affinity)) affinity = service->affinity & system_affinity;
\r
1675 affinity = (DWORD_PTR) service->affinity;
\r
1676 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GETPROCESSAFFINITYMASK_FAILED, service->name, error_string(GetLastError()), 0);
\r
1679 if (! SetProcessAffinityMask(service->process_handle, affinity)) {
\r
1680 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_SETPROCESSAFFINITYMASK_FAILED, service->name, error_string(GetLastError()), 0);
\r
1683 ResumeThread(pi.hThread);
\r
1687 Wait for a clean startup before changing the service status to RUNNING
\r
1688 but be mindful of the fact that we are blocking the service control manager
\r
1689 so abandon the wait before too much time has elapsed.
\r
1691 service->status.dwCurrentState = SERVICE_START_PENDING;
\r
1692 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
1694 /* Signal successful start */
\r
1695 service->status.dwCurrentState = SERVICE_RUNNING;
\r
1696 SetServiceStatus(service->status_handle, &service->status);
\r
1698 /* Continue waiting for a clean startup. */
\r
1699 if (deadline == WAIT_TIMEOUT) {
\r
1700 if (service->throttle_delay > delay) {
\r
1701 if (WaitForSingleObject(service->process_handle, service->throttle_delay - delay) == WAIT_TIMEOUT) service->throttle = 0;
\r
1703 else service->throttle = 0;
\r
1706 /* Ensure the restart delay is always applied. */
\r
1707 if (service->restart_delay && ! service->throttle) service->throttle++;
\r
1712 /* Stop the service */
\r
1713 int stop_service(nssm_service_t *service, unsigned long exitcode, bool graceful, bool default_action) {
\r
1714 service->allow_restart = false;
\r
1715 if (service->wait_handle) {
\r
1716 UnregisterWait(service->wait_handle);
\r
1717 service->wait_handle = 0;
\r
1720 service->rotate_stdout_online = service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;
\r
1722 if (default_action && ! exitcode && ! graceful) {
\r
1723 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
1727 /* Signal we are stopping */
\r
1729 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
1730 service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;
\r
1731 SetServiceStatus(service->status_handle, &service->status);
\r
1734 /* Nothing to do if service isn't running */
\r
1735 if (service->pid) {
\r
1736 /* Shut down service */
\r
1737 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_TERMINATEPROCESS, service->name, service->exe, 0);
\r
1739 service_kill_t(service, &k);
\r
1743 else log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_PROCESS_ALREADY_STOPPED, service->name, service->exe, 0);
\r
1745 end_service((void *) service, true);
\r
1747 /* Signal we stopped */
\r
1749 service->status.dwCurrentState = SERVICE_STOPPED;
\r
1751 service->status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
\r
1752 service->status.dwServiceSpecificExitCode = exitcode;
\r
1755 service->status.dwWin32ExitCode = NO_ERROR;
\r
1756 service->status.dwServiceSpecificExitCode = 0;
\r
1758 SetServiceStatus(service->status_handle, &service->status);
\r
1764 /* Callback function triggered when the server exits */
\r
1765 void CALLBACK end_service(void *arg, unsigned char why) {
\r
1766 nssm_service_t *service = (nssm_service_t *) arg;
\r
1768 if (service->stopping) return;
\r
1770 service->stopping = true;
\r
1772 service->rotate_stdout_online = service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;
\r
1774 /* Use now as a dummy exit time. */
\r
1775 GetSystemTimeAsFileTime(&service->exit_time);
\r
1777 /* Check exit code */
\r
1778 unsigned long exitcode = 0;
\r
1780 if (service->process_handle) {
\r
1781 GetExitCodeProcess(service->process_handle, &exitcode);
\r
1782 /* Check real exit time. */
\r
1783 if (exitcode != STILL_ACTIVE) get_process_exit_time(service->process_handle, &service->exit_time);
\r
1784 CloseHandle(service->process_handle);
\r
1787 service->process_handle = 0;
\r
1790 Log that the service ended BEFORE logging about killing the process
\r
1791 tree. See below for the possible values of the why argument.
\r
1794 _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), exitcode);
\r
1795 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ENDED_SERVICE, service->exe, service->name, code, 0);
\r
1799 if (exitcode == STILL_ACTIVE) exitcode = 0;
\r
1800 if (service->pid && service->kill_process_tree) {
\r
1802 service_kill_t(service, &k);
\r
1803 kill_process_tree(&k, service->pid);
\r
1808 The why argument is true if our wait timed out or false otherwise.
\r
1809 Our wait is infinite so why will never be true when called by the system.
\r
1810 If it is indeed true, assume we were called from stop_service() because
\r
1811 this is a controlled shutdown, and don't take any restart action.
\r
1814 if (! service->allow_restart) return;
\r
1816 /* What action should we take? */
\r
1817 int action = NSSM_EXIT_RESTART;
\r
1818 TCHAR action_string[ACTION_LEN];
\r
1819 bool default_action;
\r
1820 if (! get_exit_action(service->name, &exitcode, action_string, &default_action)) {
\r
1821 for (int i = 0; exit_action_strings[i]; i++) {
\r
1822 if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
1830 /* Try to restart the service or return failure code to service manager */
\r
1831 case NSSM_EXIT_RESTART:
\r
1832 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_RESTART, service->name, code, exit_action_strings[action], service->exe, 0);
\r
1833 while (monitor_service(service)) {
\r
1834 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_RESTART_SERVICE_FAILED, service->exe, service->name, 0);
\r
1839 /* Do nothing, just like srvany would */
\r
1840 case NSSM_EXIT_IGNORE:
\r
1841 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_IGNORE, service->name, code, exit_action_strings[action], service->exe, 0);
\r
1845 /* Tell the service manager we are finished */
\r
1846 case NSSM_EXIT_REALLY:
\r
1847 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_REALLY, service->name, code, exit_action_strings[action], 0);
\r
1848 stop_service(service, exitcode, true, default_action);
\r
1851 /* Fake a crash so pre-Vista service managers will run recovery actions. */
\r
1852 case NSSM_EXIT_UNCLEAN:
\r
1853 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_UNCLEAN, service->name, code, exit_action_strings[action], 0);
\r
1854 stop_service(service, exitcode, false, default_action);
\r
1861 void throttle_restart(nssm_service_t *service) {
\r
1862 /* This can't be a restart if the service is already running. */
\r
1863 if (! service->throttle++) return;
\r
1866 unsigned long throttle_ms = throttle_milliseconds(service->throttle);
\r
1867 TCHAR threshold[8], milliseconds[8];
\r
1869 if (service->restart_delay > throttle_ms) ms = service->restart_delay;
\r
1870 else ms = throttle_ms;
\r
1872 if (service->throttle > 7) service->throttle = 8;
\r
1874 _sntprintf_s(milliseconds, _countof(milliseconds), _TRUNCATE, _T("%lu"), ms);
\r
1876 if (service->throttle == 1 && service->restart_delay > throttle_ms) log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESTART_DELAY, service->name, milliseconds, 0);
\r
1878 _sntprintf_s(threshold, _countof(threshold), _TRUNCATE, _T("%lu"), service->throttle_delay);
\r
1879 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_THROTTLED, service->name, threshold, milliseconds, 0);
\r
1882 if (use_critical_section) EnterCriticalSection(&service->throttle_section);
\r
1883 else if (service->throttle_timer) {
\r
1884 ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));
\r
1885 service->throttle_duetime.QuadPart = 0 - (ms * 10000LL);
\r
1886 SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);
\r
1889 service->status.dwCurrentState = SERVICE_PAUSED;
\r
1890 SetServiceStatus(service->status_handle, &service->status);
\r
1892 if (use_critical_section) {
\r
1893 imports.SleepConditionVariableCS(&service->throttle_condition, &service->throttle_section, ms);
\r
1894 LeaveCriticalSection(&service->throttle_section);
\r
1897 if (service->throttle_timer) WaitForSingleObject(service->throttle_timer, INFINITE);
\r
1903 When responding to a stop (or any other) request we need to set dwWaitHint to
\r
1904 the number of milliseconds we expect the operation to take, and optionally
\r
1905 increase dwCheckPoint. If dwWaitHint milliseconds elapses without the
\r
1906 operation completing or dwCheckPoint increasing, the system will consider the
\r
1907 service to be hung.
\r
1909 However the system will consider the service to be hung after 30000
\r
1910 milliseconds regardless of the value of dwWaitHint if dwCheckPoint has not
\r
1911 changed. Therefore if we want to wait longer than that we must periodically
\r
1912 increase dwCheckPoint.
\r
1914 Furthermore, it will consider the service to be hung after 60000 milliseconds
\r
1915 regardless of the value of dwCheckPoint unless dwWaitHint is increased every
\r
1916 time dwCheckPoint is also increased.
\r
1918 Our strategy then is to retrieve the initial dwWaitHint and wait for
\r
1919 NSSM_SERVICE_STATUS_DEADLINE milliseconds. If the process is still running
\r
1920 and we haven't finished waiting we increment dwCheckPoint and add whichever is
\r
1921 smaller of NSSM_SERVICE_STATUS_DEADLINE or the remaining timeout to
\r
1924 Only doing both these things will prevent the system from killing the service.
\r
1926 If the status_handle and service_status arguments are omitted, this function
\r
1927 will not try to update the service manager but it will still log to the
\r
1928 event log that it is waiting for a handle.
\r
1930 Returns: 1 if the wait timed out.
\r
1931 0 if the wait completed.
\r
1934 int await_single_handle(SERVICE_STATUS_HANDLE status_handle, SERVICE_STATUS *status, HANDLE handle, TCHAR *name, TCHAR *function_name, unsigned long timeout) {
\r
1935 unsigned long interval;
\r
1936 unsigned long ret;
\r
1937 unsigned long waited;
\r
1938 TCHAR interval_milliseconds[16];
\r
1939 TCHAR timeout_milliseconds[16];
\r
1940 TCHAR waited_milliseconds[16];
\r
1941 TCHAR *function = function_name;
\r
1943 /* Add brackets to function name. */
\r
1944 size_t funclen = _tcslen(function_name) + 3;
\r
1945 TCHAR *func = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, funclen * sizeof(TCHAR));
\r
1947 if (_sntprintf_s(func, funclen, _TRUNCATE, _T("%s()"), function_name) > -1) function = func;
\r
1950 _sntprintf_s(timeout_milliseconds, _countof(timeout_milliseconds), _TRUNCATE, _T("%lu"), timeout);
\r
1953 while (waited < timeout) {
\r
1954 interval = timeout - waited;
\r
1955 if (interval > NSSM_SERVICE_STATUS_DEADLINE) interval = NSSM_SERVICE_STATUS_DEADLINE;
\r
1958 status->dwWaitHint += interval;
\r
1959 status->dwCheckPoint++;
\r
1960 SetServiceStatus(status_handle, status);
\r
1964 _sntprintf_s(waited_milliseconds, _countof(waited_milliseconds), _TRUNCATE, _T("%lu"), waited);
\r
1965 _sntprintf_s(interval_milliseconds, _countof(interval_milliseconds), _TRUNCATE, _T("%lu"), interval);
\r
1966 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_AWAITING_SINGLE_HANDLE, function, name, waited_milliseconds, interval_milliseconds, timeout_milliseconds, 0);
\r
1969 switch (WaitForSingleObject(handle, interval)) {
\r
1970 case WAIT_OBJECT_0:
\r
1974 case WAIT_TIMEOUT:
\r
1983 waited += interval;
\r
1987 if (func) HeapFree(GetProcessHeap(), 0, func);
\r