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
694 /* Allocate and zero memory for a service. */
\r
695 nssm_service_t *alloc_nssm_service() {
\r
696 nssm_service_t *service = (nssm_service_t *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(nssm_service_t));
\r
697 if (! service) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("service"), _T("alloc_nssm_service()"), 0);
\r
701 /* Free memory for a service. */
\r
702 void cleanup_nssm_service(nssm_service_t *service) {
\r
703 if (! service) return;
\r
704 if (service->username) HeapFree(GetProcessHeap(), 0, service->username);
\r
705 if (service->password) {
\r
706 SecureZeroMemory(service->password, service->passwordlen);
\r
707 HeapFree(GetProcessHeap(), 0, service->password);
\r
709 if (service->dependencies) HeapFree(GetProcessHeap(), 0, service->dependencies);
\r
710 if (service->env) HeapFree(GetProcessHeap(), 0, service->env);
\r
711 if (service->env_extra) HeapFree(GetProcessHeap(), 0, service->env_extra);
\r
712 if (service->handle) CloseServiceHandle(service->handle);
\r
713 if (service->process_handle) CloseHandle(service->process_handle);
\r
714 if (service->wait_handle) UnregisterWait(service->wait_handle);
\r
715 if (service->throttle_section_initialised) DeleteCriticalSection(&service->throttle_section);
\r
716 if (service->throttle_timer) CloseHandle(service->throttle_timer);
\r
717 if (service->initial_env) FreeEnvironmentStrings(service->initial_env);
\r
718 HeapFree(GetProcessHeap(), 0, service);
\r
721 /* About to install the service */
\r
722 int pre_install_service(int argc, TCHAR **argv) {
\r
723 nssm_service_t *service = alloc_nssm_service();
\r
724 set_nssm_service_defaults(service);
\r
725 if (argc) _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]);
\r
727 /* Show the dialogue box if we didn't give the service name and path */
\r
728 if (argc < 2) return nssm_gui(IDD_INSTALL, service);
\r
731 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("service"), _T("pre_install_service()"));
\r
734 _sntprintf_s(service->exe, _countof(service->exe), _TRUNCATE, _T("%s"), argv[1]);
\r
736 /* Arguments are optional */
\r
737 size_t flagslen = 0;
\r
740 for (i = 2; i < argc; i++) flagslen += _tcslen(argv[i]) + 1;
\r
741 if (! flagslen) flagslen = 1;
\r
742 if (flagslen > _countof(service->flags)) {
\r
743 print_message(stderr, NSSM_MESSAGE_FLAGS_TOO_LONG);
\r
747 for (i = 2; i < argc; i++) {
\r
748 size_t len = _tcslen(argv[i]);
\r
749 memmove(service->flags + s, argv[i], len * sizeof(TCHAR));
\r
751 if (i < argc - 1) service->flags[s++] = _T(' ');
\r
754 /* Work out directory name */
\r
755 _sntprintf_s(service->dir, _countof(service->dir), _TRUNCATE, _T("%s"), service->exe);
\r
756 strip_basename(service->dir);
\r
758 int ret = install_service(service);
\r
759 cleanup_nssm_service(service);
\r
763 /* About to edit the service. */
\r
764 int pre_edit_service(int argc, TCHAR **argv) {
\r
765 /* Require service name. */
\r
766 if (argc < 2) return usage(1);
\r
768 /* Are we editing on the command line? */
\r
769 enum { MODE_EDITING, MODE_GETTING, MODE_SETTING, MODE_RESETTING } mode = MODE_EDITING;
\r
770 const TCHAR *verb = argv[0];
\r
771 const TCHAR *service_name = argv[1];
\r
772 bool getting = false;
\r
773 bool unsetting = false;
\r
775 /* Minimum number of arguments. */
\r
777 /* Index of first value. */
\r
780 if (str_equiv(verb, _T("get"))) {
\r
782 mode = MODE_GETTING;
\r
784 else if (str_equiv(verb, _T("set"))) {
\r
786 mode = MODE_SETTING;
\r
788 else if (str_equiv(verb, _T("reset")) || str_equiv(verb, _T("unset"))) {
\r
790 mode = MODE_RESETTING;
\r
792 if (argc < mandatory) return usage(1);
\r
794 const TCHAR *parameter = 0;
\r
795 settings_t *setting = 0;
\r
798 /* Validate the parameter. */
\r
799 if (mandatory > 2) {
\r
800 bool additional_mandatory = false;
\r
802 parameter = argv[2];
\r
803 for (i = 0; settings[i].name; i++) {
\r
804 setting = &settings[i];
\r
805 if (! str_equiv(setting->name, parameter)) continue;
\r
806 if (((setting->additional & ADDITIONAL_GETTING) && mode == MODE_GETTING) || ((setting->additional & ADDITIONAL_SETTING) && mode == MODE_SETTING) || ((setting->additional & ADDITIONAL_RESETTING) && mode == MODE_RESETTING)) {
\r
807 additional_mandatory = true;
\r
812 if (! settings[i].name) {
\r
813 print_message(stderr, NSSM_MESSAGE_INVALID_PARAMETER, parameter);
\r
814 for (i = 0; settings[i].name; i++) _ftprintf(stderr, _T("%s\n"), settings[i].name);
\r
819 if (additional_mandatory) {
\r
820 if (argc < mandatory) {
\r
821 print_message(stderr, NSSM_MESSAGE_MISSING_SUBPARAMETER, parameter);
\r
824 additional = argv[3];
\r
827 else if (str_equiv(setting->name, NSSM_NATIVE_OBJECTNAME) && mode == MODE_SETTING) {
\r
828 additional = argv[3];
\r
832 additional = argv[remainder];
\r
833 if (argc < mandatory) return usage(1);
\r
837 nssm_service_t *service = alloc_nssm_service();
\r
838 _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), service_name);
\r
840 /* Open service manager */
\r
841 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
\r
843 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
847 /* Try to open the service */
\r
848 unsigned long access = SERVICE_QUERY_CONFIG;
\r
849 if (mode != MODE_GETTING) access |= SERVICE_CHANGE_CONFIG;
\r
850 service->handle = open_service(services, service->name, access, service->name, _countof(service->name));
\r
851 if (! service->handle) {
\r
852 CloseServiceHandle(services);
\r
856 /* Get system details. */
\r
857 QUERY_SERVICE_CONFIG *qsc = query_service_config(service->name, service->handle);
\r
859 CloseServiceHandle(service->handle);
\r
860 CloseServiceHandle(services);
\r
864 service->type = qsc->dwServiceType;
\r
865 if (! (service->type & SERVICE_WIN32_OWN_PROCESS)) {
\r
866 if (mode != MODE_GETTING) {
\r
867 HeapFree(GetProcessHeap(), 0, qsc);
\r
868 CloseServiceHandle(service->handle);
\r
869 CloseServiceHandle(services);
\r
870 print_message(stderr, NSSM_MESSAGE_CANNOT_EDIT, service->name, NSSM_WIN32_OWN_PROCESS, 0);
\r
875 if (get_service_startup(service->name, service->handle, qsc, &service->startup)) {
\r
876 if (mode != MODE_GETTING) {
\r
877 HeapFree(GetProcessHeap(), 0, qsc);
\r
878 CloseServiceHandle(service->handle);
\r
879 CloseServiceHandle(services);
\r
884 if (get_service_username(service->name, qsc, &service->username, &service->usernamelen)) {
\r
885 if (mode != MODE_GETTING) {
\r
886 HeapFree(GetProcessHeap(), 0, qsc);
\r
887 CloseServiceHandle(service->handle);
\r
888 CloseServiceHandle(services);
\r
893 _sntprintf_s(service->displayname, _countof(service->displayname), _TRUNCATE, _T("%s"), qsc->lpDisplayName);
\r
895 /* Get the canonical service name. We open it case insensitively. */
\r
896 unsigned long bufsize = _countof(service->name);
\r
897 GetServiceKeyName(services, service->displayname, service->name, &bufsize);
\r
899 /* Remember the executable in case it isn't NSSM. */
\r
900 _sntprintf_s(service->image, _countof(service->image), _TRUNCATE, _T("%s"), qsc->lpBinaryPathName);
\r
901 HeapFree(GetProcessHeap(), 0, qsc);
\r
903 /* Get extended system details. */
\r
904 if (get_service_description(service->name, service->handle, _countof(service->description), service->description)) {
\r
905 if (mode != MODE_GETTING) {
\r
906 CloseServiceHandle(service->handle);
\r
907 CloseServiceHandle(services);
\r
912 if (get_service_dependencies(service->name, service->handle, &service->dependencies, &service->dependencieslen)) {
\r
913 if (mode != MODE_GETTING) {
\r
914 CloseServiceHandle(service->handle);
\r
915 CloseServiceHandle(services);
\r
920 /* Get NSSM details. */
\r
921 get_parameters(service, 0);
\r
923 CloseServiceHandle(services);
\r
925 if (! service->exe[0]) {
\r
926 service->native = true;
\r
927 if (mode != MODE_GETTING) print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE, service->name, NSSM, service->image);
\r
930 /* Editing with the GUI. */
\r
931 if (mode == MODE_EDITING) {
\r
932 nssm_gui(IDD_EDIT, service);
\r
936 /* Trying to manage App* parameters for a non-NSSM service. */
\r
937 if (! setting->native && service->native) {
\r
938 CloseServiceHandle(service->handle);
\r
939 print_message(stderr, NSSM_MESSAGE_NATIVE_PARAMETER, setting->name, NSSM);
\r
947 if (mode == MODE_GETTING) {
\r
948 if (! service->native) {
\r
949 key = open_registry(service->name, KEY_READ);
\r
950 if (! key) return 4;
\r
953 if (setting->native) ret = get_setting(service->name, service->handle, setting, &value, additional);
\r
954 else ret = get_setting(service->name, key, setting, &value, additional);
\r
956 CloseServiceHandle(service->handle);
\r
960 switch (setting->type) {
\r
961 case REG_EXPAND_SZ:
\r
964 _tprintf(_T("%s\n"), value.string ? value.string : _T(""));
\r
965 HeapFree(GetProcessHeap(), 0, value.string);
\r
969 _tprintf(_T("%u\n"), value.numeric);
\r
973 if (! service->native) RegCloseKey(key);
\r
974 CloseServiceHandle(service->handle);
\r
978 /* Build the value. */
\r
979 if (mode == MODE_RESETTING) {
\r
980 /* Unset the parameter. */
\r
983 else if (remainder == argc) {
\r
987 /* Set the parameter. */
\r
989 size_t delimiterlen = (setting->additional & ADDITIONAL_CRLF) ? 2 : 1;
\r
990 for (i = remainder; i < argc; i++) len += _tcslen(argv[i]) + delimiterlen;
\r
993 value.string = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, len * sizeof(TCHAR));
\r
994 if (! value.string) {
\r
995 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("value"), _T("edit_service()"));
\r
996 CloseServiceHandle(service->handle);
\r
1001 for (i = remainder; i < argc; i++) {
\r
1002 size_t len = _tcslen(argv[i]);
\r
1003 memmove(value.string + s, argv[i], len * sizeof(TCHAR));
\r
1005 if (i < argc - 1) {
\r
1006 if (setting->additional & ADDITIONAL_CRLF) {
\r
1007 value.string[s++] = _T('\r');
\r
1008 value.string[s++] = _T('\n');
\r
1010 else value.string[s++] = _T(' ');
\r
1013 value.string[s] = _T('\0');
\r
1016 if (! service->native) {
\r
1017 key = open_registry(service->name, KEY_WRITE);
\r
1019 if (value.string) HeapFree(GetProcessHeap(), 0, value.string);
\r
1024 if (setting->native) ret = set_setting(service->name, service->handle, setting, &value, additional);
\r
1025 else ret = set_setting(service->name, key, setting, &value, additional);
\r
1026 if (value.string) HeapFree(GetProcessHeap(), 0, value.string);
\r
1028 if (! service->native) RegCloseKey(key);
\r
1029 CloseServiceHandle(service->handle);
\r
1033 if (! service->native) RegCloseKey(key);
\r
1034 CloseServiceHandle(service->handle);
\r
1039 /* About to remove the service */
\r
1040 int pre_remove_service(int argc, TCHAR **argv) {
\r
1041 nssm_service_t *service = alloc_nssm_service();
\r
1042 set_nssm_service_defaults(service);
\r
1043 if (argc) _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]);
\r
1045 /* Show dialogue box if we didn't pass service name and "confirm" */
\r
1046 if (argc < 2) return nssm_gui(IDD_REMOVE, service);
\r
1047 if (str_equiv(argv[1], _T("confirm"))) {
\r
1048 int ret = remove_service(service);
\r
1049 cleanup_nssm_service(service);
\r
1052 print_message(stderr, NSSM_MESSAGE_PRE_REMOVE_SERVICE);
\r
1056 /* Install the service */
\r
1057 int install_service(nssm_service_t *service) {
\r
1058 if (! service) return 1;
\r
1060 /* Open service manager */
\r
1061 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE);
\r
1063 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
1064 cleanup_nssm_service(service);
\r
1068 /* Get path of this program */
\r
1069 GetModuleFileName(0, service->image, _countof(service->image));
\r
1071 /* Create the service - settings will be changed in edit_service() */
\r
1072 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
1073 if (! service->handle) {
\r
1074 print_message(stderr, NSSM_MESSAGE_CREATESERVICE_FAILED, error_string(GetLastError()));
\r
1075 CloseServiceHandle(services);
\r
1079 if (edit_service(service, false)) {
\r
1080 DeleteService(service->handle);
\r
1081 CloseServiceHandle(services);
\r
1085 print_message(stdout, NSSM_MESSAGE_SERVICE_INSTALLED, service->name);
\r
1088 CloseServiceHandle(services);
\r
1093 /* Edit the service. */
\r
1094 int edit_service(nssm_service_t *service, bool editing) {
\r
1095 if (! service) return 1;
\r
1098 The only two valid flags for service type are SERVICE_WIN32_OWN_PROCESS
\r
1099 and SERVICE_INTERACTIVE_PROCESS.
\r
1101 service->type &= SERVICE_INTERACTIVE_PROCESS;
\r
1102 service->type |= SERVICE_WIN32_OWN_PROCESS;
\r
1104 /* Startup type. */
\r
1105 unsigned long startup;
\r
1106 switch (service->startup) {
\r
1107 case NSSM_STARTUP_MANUAL: startup = SERVICE_DEMAND_START; break;
\r
1108 case NSSM_STARTUP_DISABLED: startup = SERVICE_DISABLED; break;
\r
1109 default: startup = SERVICE_AUTO_START;
\r
1112 /* Display name. */
\r
1113 if (! service->displayname[0]) _sntprintf_s(service->displayname, _countof(service->displayname), _TRUNCATE, _T("%s"), service->name);
\r
1116 Username must be NULL if we aren't changing or an account name.
\r
1117 We must explicitly use LOCALSYSTEM to change it when we are editing.
\r
1118 Password must be NULL if we aren't changing, a password or "".
\r
1119 Empty passwords are valid but we won't allow them in the GUI.
\r
1121 TCHAR *username = 0;
\r
1123 TCHAR *password = 0;
\r
1124 if (service->usernamelen) {
\r
1125 username = service->username;
\r
1126 if (canonicalise_username(username, &canon)) return 5;
\r
1127 if (service->passwordlen) password = service->password;
\r
1129 else if (editing) username = canon = NSSM_LOCALSYSTEM_ACCOUNT;
\r
1131 if (well_known_username(canon)) password = _T("");
\r
1133 if (grant_logon_as_service(canon)) {
\r
1134 if (canon != username) HeapFree(GetProcessHeap(), 0, canon);
\r
1135 print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username);
\r
1140 TCHAR *dependencies = _T("");
\r
1141 if (service->dependencieslen) dependencies = 0; /* Change later. */
\r
1143 if (! ChangeServiceConfig(service->handle, service->type, startup, SERVICE_NO_CHANGE, 0, 0, 0, dependencies, canon, password, service->displayname)) {
\r
1144 if (canon != username) HeapFree(GetProcessHeap(), 0, canon);
\r
1145 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
1148 if (canon != username) HeapFree(GetProcessHeap(), 0, canon);
\r
1150 if (service->dependencieslen) {
\r
1151 if (set_service_dependencies(service->name, service->handle, service->dependencies)) return 5;
\r
1154 if (service->description[0] || editing) {
\r
1155 set_service_description(service->name, service->handle, service->description);
\r
1158 SERVICE_DELAYED_AUTO_START_INFO delayed;
\r
1159 ZeroMemory(&delayed, sizeof(delayed));
\r
1160 if (service->startup == NSSM_STARTUP_DELAYED) delayed.fDelayedAutostart = 1;
\r
1161 else delayed.fDelayedAutostart = 0;
\r
1162 /* Delayed startup isn't supported until Vista. */
\r
1163 if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, &delayed)) {
\r
1164 unsigned long error = GetLastError();
\r
1165 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
\r
1166 if (error != ERROR_INVALID_LEVEL) {
\r
1167 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_DELAYED_AUTO_START_INFO_FAILED, service->name, error_string(error), 0);
\r
1171 /* Don't mess with parameters which aren't ours. */
\r
1172 if (! service->native) {
\r
1173 /* Now we need to put the parameters into the registry */
\r
1174 if (create_parameters(service, editing)) {
\r
1175 print_message(stderr, NSSM_MESSAGE_CREATE_PARAMETERS_FAILED);
\r
1179 set_service_recovery(service);
\r
1185 /* Control a service. */
\r
1186 int control_service(unsigned long control, int argc, TCHAR **argv) {
\r
1187 if (argc < 1) return usage(1);
\r
1188 TCHAR *service_name = argv[0];
\r
1189 TCHAR canonical_name[SERVICE_NAME_LENGTH];
\r
1191 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
\r
1193 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
1197 unsigned long access = SERVICE_QUERY_STATUS;
\r
1198 switch (control) {
\r
1199 case NSSM_SERVICE_CONTROL_START:
\r
1200 access |= SERVICE_START;
\r
1203 case SERVICE_CONTROL_CONTINUE:
\r
1204 case SERVICE_CONTROL_PAUSE:
\r
1205 access |= SERVICE_PAUSE_CONTINUE;
\r
1208 case SERVICE_CONTROL_STOP:
\r
1209 access |= SERVICE_STOP;
\r
1212 case NSSM_SERVICE_CONTROL_ROTATE:
\r
1213 access |= SERVICE_USER_DEFINED_CONTROL;
\r
1217 SC_HANDLE service_handle = open_service(services, service_name, access, canonical_name, _countof(canonical_name));
\r
1218 if (! service_handle) {
\r
1219 CloseServiceHandle(services);
\r
1224 unsigned long error;
\r
1225 SERVICE_STATUS service_status;
\r
1226 if (control == NSSM_SERVICE_CONTROL_START) {
\r
1227 unsigned long initial_status = SERVICE_STOPPED;
\r
1228 ret = StartService(service_handle, (unsigned long) argc, (const TCHAR **) argv);
\r
1229 error = GetLastError();
\r
1230 CloseServiceHandle(services);
\r
1232 if (error == ERROR_IO_PENDING) {
\r
1234 Older versions of Windows return immediately with ERROR_IO_PENDING
\r
1235 indicate that the operation is still in progress. Newer versions
\r
1236 will return it if there really is a delay.
\r
1239 error = ERROR_SUCCESS;
\r
1243 int response = await_service_control_response(control, service_handle, &service_status, initial_status);
\r
1244 CloseServiceHandle(service_handle);
\r
1247 print_message(stderr, NSSM_MESSAGE_BAD_CONTROL_RESPONSE, canonical_name, service_status_text(service_status.dwCurrentState), service_control_text(control));
\r
1250 else _tprintf(_T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1254 CloseServiceHandle(service_handle);
\r
1255 _ftprintf(stderr, _T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1259 else if (control == SERVICE_CONTROL_INTERROGATE) {
\r
1261 We could actually send an INTERROGATE control but that won't return
\r
1262 any information if the service is stopped and we don't care about
\r
1263 the extra details it might give us in any case. So we'll fake it.
\r
1265 ret = QueryServiceStatus(service_handle, &service_status);
\r
1266 error = GetLastError();
\r
1269 _tprintf(_T("%s\n"), service_status_text(service_status.dwCurrentState));
\r
1273 _ftprintf(stderr, _T("%s: %s\n"), canonical_name, error_string(error));
\r
1278 ret = ControlService(service_handle, control, &service_status);
\r
1279 unsigned long initial_status = service_status.dwCurrentState;
\r
1280 error = GetLastError();
\r
1281 CloseServiceHandle(services);
\r
1283 if (error == ERROR_IO_PENDING) {
\r
1285 error = ERROR_SUCCESS;
\r
1289 int response = await_service_control_response(control, service_handle, &service_status, initial_status);
\r
1290 CloseServiceHandle(service_handle);
\r
1293 print_message(stderr, NSSM_MESSAGE_BAD_CONTROL_RESPONSE, canonical_name, service_status_text(service_status.dwCurrentState), service_control_text(control));
\r
1296 else _tprintf(_T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1300 CloseServiceHandle(service_handle);
\r
1301 _ftprintf(stderr, _T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1302 if (error == ERROR_SERVICE_NOT_ACTIVE) {
\r
1303 if (control == SERVICE_CONTROL_SHUTDOWN || control == SERVICE_CONTROL_STOP) return 0;
\r
1310 /* Remove the service */
\r
1311 int remove_service(nssm_service_t *service) {
\r
1312 if (! service) return 1;
\r
1314 /* Open service manager */
\r
1315 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
\r
1317 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
1321 /* Try to open the service */
\r
1322 service->handle = open_service(services, service->name, DELETE, service->name, _countof(service->name));
\r
1323 if (! service->handle) {
\r
1324 CloseServiceHandle(services);
\r
1328 /* Get the canonical service name. We open it case insensitively. */
\r
1329 unsigned long bufsize = _countof(service->displayname);
\r
1330 GetServiceDisplayName(services, service->name, service->displayname, &bufsize);
\r
1331 bufsize = _countof(service->name);
\r
1332 GetServiceKeyName(services, service->displayname, service->name, &bufsize);
\r
1334 /* Try to delete the service */
\r
1335 if (! DeleteService(service->handle)) {
\r
1336 print_message(stderr, NSSM_MESSAGE_DELETESERVICE_FAILED);
\r
1337 CloseServiceHandle(services);
\r
1342 CloseServiceHandle(services);
\r
1344 print_message(stdout, NSSM_MESSAGE_SERVICE_REMOVED, service->name);
\r
1348 /* Service initialisation */
\r
1349 void WINAPI service_main(unsigned long argc, TCHAR **argv) {
\r
1350 nssm_service_t *service = alloc_nssm_service();
\r
1351 if (! service) return;
\r
1353 if (_sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]) < 0) {
\r
1354 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("service->name"), _T("service_main()"), 0);
\r
1358 /* We can use a condition variable in a critical section on Vista or later. */
\r
1359 if (imports.SleepConditionVariableCS && imports.WakeConditionVariable) use_critical_section = true;
\r
1360 else use_critical_section = false;
\r
1362 /* Initialise status */
\r
1363 ZeroMemory(&service->status, sizeof(service->status));
\r
1364 service->status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;
\r
1365 service->status.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE;
\r
1366 service->status.dwWin32ExitCode = NO_ERROR;
\r
1367 service->status.dwServiceSpecificExitCode = 0;
\r
1368 service->status.dwCheckPoint = 0;
\r
1369 service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;
\r
1371 /* Signal we AREN'T running the server */
\r
1372 service->process_handle = 0;
\r
1375 /* Register control handler */
\r
1376 service->status_handle = RegisterServiceCtrlHandlerEx(NSSM, service_control_handler, (void *) service);
\r
1377 if (! service->status_handle) {
\r
1378 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_REGISTERSERVICECTRLHANDER_FAILED, error_string(GetLastError()), 0);
\r
1382 log_service_control(service->name, 0, true);
\r
1384 service->status.dwCurrentState = SERVICE_START_PENDING;
\r
1385 service->status.dwWaitHint = service->throttle_delay + NSSM_WAITHINT_MARGIN;
\r
1386 SetServiceStatus(service->status_handle, &service->status);
\r
1389 /* Try to create the exit action parameters; we don't care if it fails */
\r
1390 create_exit_action(service->name, exit_action_strings[0], false);
\r
1392 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT);
\r
1394 service->handle = open_service(services, service->name, SERVICE_CHANGE_CONFIG, 0, 0);
\r
1395 set_service_recovery(service);
\r
1396 CloseServiceHandle(services);
\r
1400 /* Used for signalling a resume if the service pauses when throttled. */
\r
1401 if (use_critical_section) {
\r
1402 InitializeCriticalSection(&service->throttle_section);
\r
1403 service->throttle_section_initialised = true;
\r
1406 service->throttle_timer = CreateWaitableTimer(0, 1, 0);
\r
1407 if (! service->throttle_timer) {
\r
1408 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_CREATEWAITABLETIMER_FAILED, service->name, error_string(GetLastError()), 0);
\r
1412 /* Remember our initial environment. */
\r
1413 service->initial_env = GetEnvironmentStrings();
\r
1415 monitor_service(service);
\r
1418 /* Make sure service recovery actions are taken where necessary */
\r
1419 void set_service_recovery(nssm_service_t *service) {
\r
1420 SERVICE_FAILURE_ACTIONS_FLAG flag;
\r
1421 ZeroMemory(&flag, sizeof(flag));
\r
1422 flag.fFailureActionsOnNonCrashFailures = true;
\r
1424 /* This functionality was added in Vista so the call may fail */
\r
1425 if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, &flag)) {
\r
1426 unsigned long error = GetLastError();
\r
1427 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
\r
1428 if (error != ERROR_INVALID_LEVEL) {
\r
1429 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_FAILURE_ACTIONS_FAILED, service->name, error_string(error), 0);
\r
1434 int monitor_service(nssm_service_t *service) {
\r
1435 /* Set service status to started */
\r
1436 int ret = start_service(service);
\r
1439 _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%d"), ret);
\r
1440 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_START_SERVICE_FAILED, service->exe, service->name, ret, 0);
\r
1443 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_STARTED_SERVICE, service->exe, service->flags, service->name, service->dir, 0);
\r
1445 /* Monitor service */
\r
1446 if (! RegisterWaitForSingleObject(&service->wait_handle, service->process_handle, end_service, (void *) service, INFINITE, WT_EXECUTEONLYONCE | WT_EXECUTELONGFUNCTION)) {
\r
1447 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_REGISTERWAITFORSINGLEOBJECT_FAILED, service->name, service->exe, error_string(GetLastError()), 0);
\r
1453 TCHAR *service_control_text(unsigned long control) {
\r
1454 switch (control) {
\r
1455 /* HACK: there is no SERVICE_CONTROL_START constant */
\r
1456 case NSSM_SERVICE_CONTROL_START: return _T("START");
\r
1457 case SERVICE_CONTROL_STOP: return _T("STOP");
\r
1458 case SERVICE_CONTROL_SHUTDOWN: return _T("SHUTDOWN");
\r
1459 case SERVICE_CONTROL_PAUSE: return _T("PAUSE");
\r
1460 case SERVICE_CONTROL_CONTINUE: return _T("CONTINUE");
\r
1461 case SERVICE_CONTROL_INTERROGATE: return _T("INTERROGATE");
\r
1462 case NSSM_SERVICE_CONTROL_ROTATE: return _T("ROTATE");
\r
1463 default: return 0;
\r
1467 TCHAR *service_status_text(unsigned long status) {
\r
1469 case SERVICE_STOPPED: return _T("SERVICE_STOPPED");
\r
1470 case SERVICE_START_PENDING: return _T("SERVICE_START_PENDING");
\r
1471 case SERVICE_STOP_PENDING: return _T("SERVICE_STOP_PENDING");
\r
1472 case SERVICE_RUNNING: return _T("SERVICE_RUNNING");
\r
1473 case SERVICE_CONTINUE_PENDING: return _T("SERVICE_CONTINUE_PENDING");
\r
1474 case SERVICE_PAUSE_PENDING: return _T("SERVICE_PAUSE_PENDING");
\r
1475 case SERVICE_PAUSED: return _T("SERVICE_PAUSED");
\r
1476 default: return 0;
\r
1480 void log_service_control(TCHAR *service_name, unsigned long control, bool handled) {
\r
1481 TCHAR *text = service_control_text(control);
\r
1482 unsigned long event;
\r
1485 /* "0x" + 8 x hex + NULL */
\r
1486 text = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, 11 * sizeof(TCHAR));
\r
1488 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);
\r
1491 if (_sntprintf_s(text, 11, _TRUNCATE, _T("0x%08x"), control) < 0) {
\r
1492 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);
\r
1493 HeapFree(GetProcessHeap(), 0, text);
\r
1497 event = NSSM_EVENT_SERVICE_CONTROL_UNKNOWN;
\r
1499 else if (handled) event = NSSM_EVENT_SERVICE_CONTROL_HANDLED;
\r
1500 else event = NSSM_EVENT_SERVICE_CONTROL_NOT_HANDLED;
\r
1502 log_event(EVENTLOG_INFORMATION_TYPE, event, service_name, text, 0);
\r
1504 if (event == NSSM_EVENT_SERVICE_CONTROL_UNKNOWN) {
\r
1505 HeapFree(GetProcessHeap(), 0, text);
\r
1509 /* Service control handler */
\r
1510 unsigned long WINAPI service_control_handler(unsigned long control, unsigned long event, void *data, void *context) {
\r
1511 nssm_service_t *service = (nssm_service_t *) context;
\r
1513 switch (control) {
\r
1514 case SERVICE_CONTROL_INTERROGATE:
\r
1515 /* We always keep the service status up-to-date so this is a no-op. */
\r
1518 case SERVICE_CONTROL_SHUTDOWN:
\r
1519 case SERVICE_CONTROL_STOP:
\r
1520 log_service_control(service->name, control, true);
\r
1522 We MUST acknowledge the stop request promptly but we're committed to
\r
1523 waiting for the application to exit. Spawn a new thread to wait
\r
1524 while we acknowledge the request.
\r
1526 if (! CreateThread(NULL, 0, shutdown_service, context, 0, NULL)) {
\r
1527 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETHREAD_FAILED, error_string(GetLastError()), 0);
\r
1530 We couldn't create a thread to tidy up so we'll have to force the tidyup
\r
1531 to complete in time in this thread.
\r
1533 service->kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;
\r
1534 service->kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;
\r
1535 service->kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;
\r
1537 stop_service(service, 0, true, true);
\r
1541 case SERVICE_CONTROL_CONTINUE:
\r
1542 log_service_control(service->name, control, true);
\r
1543 service->throttle = 0;
\r
1544 if (use_critical_section) imports.WakeConditionVariable(&service->throttle_condition);
\r
1546 if (! service->throttle_timer) return ERROR_CALL_NOT_IMPLEMENTED;
\r
1547 ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));
\r
1548 SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);
\r
1550 /* We can't continue if the application is running! */
\r
1551 if (! service->process_handle) service->status.dwCurrentState = SERVICE_CONTINUE_PENDING;
\r
1552 service->status.dwWaitHint = throttle_milliseconds(service->throttle) + NSSM_WAITHINT_MARGIN;
\r
1553 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESET_THROTTLE, service->name, 0);
\r
1554 SetServiceStatus(service->status_handle, &service->status);
\r
1557 case SERVICE_CONTROL_PAUSE:
\r
1559 We don't accept pause messages but it isn't possible to register
\r
1560 only for continue messages so we have to handle this case.
\r
1562 log_service_control(service->name, control, false);
\r
1563 return ERROR_CALL_NOT_IMPLEMENTED;
\r
1565 case NSSM_SERVICE_CONTROL_ROTATE:
\r
1566 log_service_control(service->name, control, true);
\r
1567 if (service->rotate_stdout_online == NSSM_ROTATE_ONLINE) service->rotate_stdout_online = NSSM_ROTATE_ONLINE_ASAP;
\r
1568 if (service->rotate_stderr_online == NSSM_ROTATE_ONLINE) service->rotate_stderr_online = NSSM_ROTATE_ONLINE_ASAP;
\r
1572 /* Unknown control */
\r
1573 log_service_control(service->name, control, false);
\r
1574 return ERROR_CALL_NOT_IMPLEMENTED;
\r
1577 /* Start the service */
\r
1578 int start_service(nssm_service_t *service) {
\r
1579 service->stopping = false;
\r
1580 service->allow_restart = true;
\r
1582 if (service->process_handle) return 0;
\r
1584 /* Allocate a STARTUPINFO structure for a new process */
\r
1586 ZeroMemory(&si, sizeof(si));
\r
1587 si.cb = sizeof(si);
\r
1589 /* Allocate a PROCESSINFO structure for the process */
\r
1590 PROCESS_INFORMATION pi;
\r
1591 ZeroMemory(&pi, sizeof(pi));
\r
1593 /* Get startup parameters */
\r
1594 int ret = get_parameters(service, &si);
\r
1596 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_PARAMETERS_FAILED, service->name, 0);
\r
1597 return stop_service(service, 2, true, true);
\r
1600 /* Launch executable with arguments */
\r
1601 TCHAR cmd[CMD_LENGTH];
\r
1602 if (_sntprintf_s(cmd, _countof(cmd), _TRUNCATE, _T("\"%s\" %s"), service->exe, service->flags) < 0) {
\r
1603 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("command line"), _T("start_service"), 0);
\r
1604 return stop_service(service, 2, true, true);
\r
1607 throttle_restart(service);
\r
1609 /* Set the environment. */
\r
1610 if (service->env) duplicate_environment(service->env);
\r
1611 if (service->env_extra) set_environment_block(service->env_extra);
\r
1613 /* Set up I/O redirection. */
\r
1614 if (get_output_handles(service, &si)) {
\r
1615 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_OUTPUT_HANDLES_FAILED, service->name, 0);
\r
1616 if (! service->no_console) FreeConsole();
\r
1617 close_output_handles(&si);
\r
1618 return stop_service(service, 4, true, true);
\r
1621 bool inherit_handles = false;
\r
1622 if (si.dwFlags & STARTF_USESTDHANDLES) inherit_handles = true;
\r
1623 unsigned long flags = service->priority & priority_mask();
\r
1624 if (service->affinity) flags |= CREATE_SUSPENDED;
\r
1625 if (! CreateProcess(0, cmd, 0, 0, inherit_handles, flags, 0, service->dir, &si, &pi)) {
\r
1626 unsigned long exitcode = 3;
\r
1627 unsigned long error = GetLastError();
\r
1628 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service->name, service->exe, error_string(error), 0);
\r
1629 close_output_handles(&si);
\r
1630 duplicate_environment_strings(service->initial_env);
\r
1631 return stop_service(service, exitcode, true, true);
\r
1633 service->process_handle = pi.hProcess;
\r
1634 service->pid = pi.dwProcessId;
\r
1636 if (get_process_creation_time(service->process_handle, &service->creation_time)) ZeroMemory(&service->creation_time, sizeof(service->creation_time));
\r
1638 close_output_handles(&si);
\r
1640 if (! service->no_console) FreeConsole();
\r
1642 /* Restore our environment. */
\r
1643 duplicate_environment_strings(service->initial_env);
\r
1645 if (service->affinity) {
\r
1647 We are explicitly storing service->affinity as a 64-bit unsigned integer
\r
1648 so that we can parse it regardless of whether we're running in 32-bit
\r
1649 or 64-bit mode. The arguments to SetProcessAffinityMask(), however, are
\r
1650 defined as type DWORD_PTR and hence limited to 32 bits on a 32-bit system
\r
1651 (or when running the 32-bit NSSM).
\r
1653 The result is a lot of seemingly-unnecessary casting throughout the code
\r
1654 and potentially confusion when we actually try to start the service.
\r
1655 Having said that, however, it's unlikely that we're actually going to
\r
1656 run in 32-bit mode on a system which has more than 32 CPUs so the
\r
1657 likelihood of seeing a confusing situation is somewhat diminished.
\r
1659 DWORD_PTR affinity, system_affinity;
\r
1661 if (GetProcessAffinityMask(service->process_handle, &affinity, &system_affinity)) affinity = service->affinity & system_affinity;
\r
1663 affinity = (DWORD_PTR) service->affinity;
\r
1664 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GETPROCESSAFFINITYMASK_FAILED, service->name, error_string(GetLastError()), 0);
\r
1667 if (! SetProcessAffinityMask(service->process_handle, affinity)) {
\r
1668 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_SETPROCESSAFFINITYMASK_FAILED, service->name, error_string(GetLastError()), 0);
\r
1671 ResumeThread(pi.hThread);
\r
1675 Wait for a clean startup before changing the service status to RUNNING
\r
1676 but be mindful of the fact that we are blocking the service control manager
\r
1677 so abandon the wait before too much time has elapsed.
\r
1679 unsigned long delay = service->throttle_delay;
\r
1680 if (delay > NSSM_SERVICE_STATUS_DEADLINE) {
\r
1681 TCHAR delay_milliseconds[16];
\r
1682 _sntprintf_s(delay_milliseconds, _countof(delay_milliseconds), _TRUNCATE, _T("%lu"), delay);
\r
1683 TCHAR deadline_milliseconds[16];
\r
1684 _sntprintf_s(deadline_milliseconds, _countof(deadline_milliseconds), _TRUNCATE, _T("%lu"), NSSM_SERVICE_STATUS_DEADLINE);
\r
1685 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_STARTUP_DELAY_TOO_LONG, service->name, delay_milliseconds, NSSM, deadline_milliseconds, 0);
\r
1686 delay = NSSM_SERVICE_STATUS_DEADLINE;
\r
1688 unsigned long deadline = WaitForSingleObject(service->process_handle, delay);
\r
1690 /* Signal successful start */
\r
1691 service->status.dwCurrentState = SERVICE_RUNNING;
\r
1692 SetServiceStatus(service->status_handle, &service->status);
\r
1694 /* Continue waiting for a clean startup. */
\r
1695 if (deadline == WAIT_TIMEOUT) {
\r
1696 if (service->throttle_delay > delay) {
\r
1697 if (WaitForSingleObject(service->process_handle, service->throttle_delay - delay) == WAIT_TIMEOUT) service->throttle = 0;
\r
1699 else service->throttle = 0;
\r
1702 /* Ensure the restart delay is always applied. */
\r
1703 if (service->restart_delay && ! service->throttle) service->throttle++;
\r
1708 /* Stop the service */
\r
1709 int stop_service(nssm_service_t *service, unsigned long exitcode, bool graceful, bool default_action) {
\r
1710 service->allow_restart = false;
\r
1711 if (service->wait_handle) {
\r
1712 UnregisterWait(service->wait_handle);
\r
1713 service->wait_handle = 0;
\r
1716 service->rotate_stdout_online = service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;
\r
1718 if (default_action && ! exitcode && ! graceful) {
\r
1719 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
1723 /* Signal we are stopping */
\r
1725 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
1726 service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;
\r
1727 SetServiceStatus(service->status_handle, &service->status);
\r
1730 /* Nothing to do if service isn't running */
\r
1731 if (service->pid) {
\r
1732 /* Shut down service */
\r
1733 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_TERMINATEPROCESS, service->name, service->exe, 0);
\r
1734 kill_process(service, service->process_handle, service->pid, 0);
\r
1736 else log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_PROCESS_ALREADY_STOPPED, service->name, service->exe, 0);
\r
1738 end_service((void *) service, true);
\r
1740 /* Signal we stopped */
\r
1742 service->status.dwCurrentState = SERVICE_STOPPED;
\r
1744 service->status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
\r
1745 service->status.dwServiceSpecificExitCode = exitcode;
\r
1748 service->status.dwWin32ExitCode = NO_ERROR;
\r
1749 service->status.dwServiceSpecificExitCode = 0;
\r
1751 SetServiceStatus(service->status_handle, &service->status);
\r
1757 /* Callback function triggered when the server exits */
\r
1758 void CALLBACK end_service(void *arg, unsigned char why) {
\r
1759 nssm_service_t *service = (nssm_service_t *) arg;
\r
1761 if (service->stopping) return;
\r
1763 service->stopping = true;
\r
1765 service->rotate_stdout_online = service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;
\r
1767 /* Use now as a dummy exit time. */
\r
1768 GetSystemTimeAsFileTime(&service->exit_time);
\r
1770 /* Check exit code */
\r
1771 unsigned long exitcode = 0;
\r
1773 if (service->process_handle) {
\r
1774 GetExitCodeProcess(service->process_handle, &exitcode);
\r
1775 /* Check real exit time. */
\r
1776 if (exitcode != STILL_ACTIVE) get_process_exit_time(service->process_handle, &service->exit_time);
\r
1777 CloseHandle(service->process_handle);
\r
1780 service->process_handle = 0;
\r
1783 Log that the service ended BEFORE logging about killing the process
\r
1784 tree. See below for the possible values of the why argument.
\r
1787 _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), exitcode);
\r
1788 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ENDED_SERVICE, service->exe, service->name, code, 0);
\r
1792 if (exitcode == STILL_ACTIVE) exitcode = 0;
\r
1793 if (service->pid) kill_process_tree(service, service->pid, exitcode, service->pid);
\r
1797 The why argument is true if our wait timed out or false otherwise.
\r
1798 Our wait is infinite so why will never be true when called by the system.
\r
1799 If it is indeed true, assume we were called from stop_service() because
\r
1800 this is a controlled shutdown, and don't take any restart action.
\r
1803 if (! service->allow_restart) return;
\r
1805 /* What action should we take? */
\r
1806 int action = NSSM_EXIT_RESTART;
\r
1807 TCHAR action_string[ACTION_LEN];
\r
1808 bool default_action;
\r
1809 if (! get_exit_action(service->name, &exitcode, action_string, &default_action)) {
\r
1810 for (int i = 0; exit_action_strings[i]; i++) {
\r
1811 if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
1819 /* Try to restart the service or return failure code to service manager */
\r
1820 case NSSM_EXIT_RESTART:
\r
1821 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_RESTART, service->name, code, exit_action_strings[action], service->exe, 0);
\r
1822 while (monitor_service(service)) {
\r
1823 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_RESTART_SERVICE_FAILED, service->exe, service->name, 0);
\r
1828 /* Do nothing, just like srvany would */
\r
1829 case NSSM_EXIT_IGNORE:
\r
1830 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_IGNORE, service->name, code, exit_action_strings[action], service->exe, 0);
\r
1834 /* Tell the service manager we are finished */
\r
1835 case NSSM_EXIT_REALLY:
\r
1836 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_REALLY, service->name, code, exit_action_strings[action], 0);
\r
1837 stop_service(service, exitcode, true, default_action);
\r
1840 /* Fake a crash so pre-Vista service managers will run recovery actions. */
\r
1841 case NSSM_EXIT_UNCLEAN:
\r
1842 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_UNCLEAN, service->name, code, exit_action_strings[action], 0);
\r
1843 stop_service(service, exitcode, false, default_action);
\r
1850 void throttle_restart(nssm_service_t *service) {
\r
1851 /* This can't be a restart if the service is already running. */
\r
1852 if (! service->throttle++) return;
\r
1855 unsigned long throttle_ms = throttle_milliseconds(service->throttle);
\r
1856 TCHAR threshold[8], milliseconds[8];
\r
1858 if (service->restart_delay > throttle_ms) ms = service->restart_delay;
\r
1859 else ms = throttle_ms;
\r
1861 if (service->throttle > 7) service->throttle = 8;
\r
1863 _sntprintf_s(milliseconds, _countof(milliseconds), _TRUNCATE, _T("%lu"), ms);
\r
1865 if (service->throttle == 1 && service->restart_delay > throttle_ms) log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESTART_DELAY, service->name, milliseconds, 0);
\r
1867 _sntprintf_s(threshold, _countof(threshold), _TRUNCATE, _T("%lu"), service->throttle_delay);
\r
1868 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_THROTTLED, service->name, threshold, milliseconds, 0);
\r
1871 if (use_critical_section) EnterCriticalSection(&service->throttle_section);
\r
1872 else if (service->throttle_timer) {
\r
1873 ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));
\r
1874 service->throttle_duetime.QuadPart = 0 - (ms * 10000LL);
\r
1875 SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);
\r
1878 service->status.dwCurrentState = SERVICE_PAUSED;
\r
1879 SetServiceStatus(service->status_handle, &service->status);
\r
1881 if (use_critical_section) {
\r
1882 imports.SleepConditionVariableCS(&service->throttle_condition, &service->throttle_section, ms);
\r
1883 LeaveCriticalSection(&service->throttle_section);
\r
1886 if (service->throttle_timer) WaitForSingleObject(service->throttle_timer, INFINITE);
\r
1892 When responding to a stop (or any other) request we need to set dwWaitHint to
\r
1893 the number of milliseconds we expect the operation to take, and optionally
\r
1894 increase dwCheckPoint. If dwWaitHint milliseconds elapses without the
\r
1895 operation completing or dwCheckPoint increasing, the system will consider the
\r
1896 service to be hung.
\r
1898 However the system will consider the service to be hung after 30000
\r
1899 milliseconds regardless of the value of dwWaitHint if dwCheckPoint has not
\r
1900 changed. Therefore if we want to wait longer than that we must periodically
\r
1901 increase dwCheckPoint.
\r
1903 Furthermore, it will consider the service to be hung after 60000 milliseconds
\r
1904 regardless of the value of dwCheckPoint unless dwWaitHint is increased every
\r
1905 time dwCheckPoint is also increased.
\r
1907 Our strategy then is to retrieve the initial dwWaitHint and wait for
\r
1908 NSSM_SERVICE_STATUS_DEADLINE milliseconds. If the process is still running
\r
1909 and we haven't finished waiting we increment dwCheckPoint and add whichever is
\r
1910 smaller of NSSM_SERVICE_STATUS_DEADLINE or the remaining timeout to
\r
1913 Only doing both these things will prevent the system from killing the service.
\r
1915 Returns: 1 if the wait timed out.
\r
1916 0 if the wait completed.
\r
1919 int await_shutdown(nssm_service_t *service, TCHAR *function_name, unsigned long timeout) {
\r
1920 unsigned long interval;
\r
1921 unsigned long waithint;
\r
1922 unsigned long ret;
\r
1923 unsigned long waited;
\r
1924 TCHAR interval_milliseconds[16];
\r
1925 TCHAR timeout_milliseconds[16];
\r
1926 TCHAR waited_milliseconds[16];
\r
1927 TCHAR *function = function_name;
\r
1929 /* Add brackets to function name. */
\r
1930 size_t funclen = _tcslen(function_name) + 3;
\r
1931 TCHAR *func = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, funclen * sizeof(TCHAR));
\r
1933 if (_sntprintf_s(func, funclen, _TRUNCATE, _T("%s()"), function_name) > -1) function = func;
\r
1936 _sntprintf_s(timeout_milliseconds, _countof(timeout_milliseconds), _TRUNCATE, _T("%lu"), timeout);
\r
1938 waithint = service->status.dwWaitHint;
\r
1940 while (waited < timeout) {
\r
1941 interval = timeout - waited;
\r
1942 if (interval > NSSM_SERVICE_STATUS_DEADLINE) interval = NSSM_SERVICE_STATUS_DEADLINE;
\r
1944 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
1945 service->status.dwWaitHint += interval;
\r
1946 service->status.dwCheckPoint++;
\r
1947 SetServiceStatus(service->status_handle, &service->status);
\r
1950 _sntprintf_s(waited_milliseconds, _countof(waited_milliseconds), _TRUNCATE, _T("%lu"), waited);
\r
1951 _sntprintf_s(interval_milliseconds, _countof(interval_milliseconds), _TRUNCATE, _T("%lu"), interval);
\r
1952 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_AWAITING_SHUTDOWN, function, service->name, waited_milliseconds, interval_milliseconds, timeout_milliseconds, 0);
\r
1955 switch (WaitForSingleObject(service->process_handle, interval)) {
\r
1956 case WAIT_OBJECT_0:
\r
1960 case WAIT_TIMEOUT:
\r
1969 waited += interval;
\r
1973 if (func) HeapFree(GetProcessHeap(), 0, func);
\r