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_POWEREVENT | 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 case SERVICE_CONTROL_POWEREVENT: return _T("POWEREVENT");
\r
1464 default: return 0;
\r
1468 TCHAR *service_status_text(unsigned long status) {
\r
1470 case SERVICE_STOPPED: return _T("SERVICE_STOPPED");
\r
1471 case SERVICE_START_PENDING: return _T("SERVICE_START_PENDING");
\r
1472 case SERVICE_STOP_PENDING: return _T("SERVICE_STOP_PENDING");
\r
1473 case SERVICE_RUNNING: return _T("SERVICE_RUNNING");
\r
1474 case SERVICE_CONTINUE_PENDING: return _T("SERVICE_CONTINUE_PENDING");
\r
1475 case SERVICE_PAUSE_PENDING: return _T("SERVICE_PAUSE_PENDING");
\r
1476 case SERVICE_PAUSED: return _T("SERVICE_PAUSED");
\r
1477 default: return 0;
\r
1481 void log_service_control(TCHAR *service_name, unsigned long control, bool handled) {
\r
1482 TCHAR *text = service_control_text(control);
\r
1483 unsigned long event;
\r
1486 /* "0x" + 8 x hex + NULL */
\r
1487 text = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, 11 * sizeof(TCHAR));
\r
1489 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);
\r
1492 if (_sntprintf_s(text, 11, _TRUNCATE, _T("0x%08x"), control) < 0) {
\r
1493 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);
\r
1494 HeapFree(GetProcessHeap(), 0, text);
\r
1498 event = NSSM_EVENT_SERVICE_CONTROL_UNKNOWN;
\r
1500 else if (handled) event = NSSM_EVENT_SERVICE_CONTROL_HANDLED;
\r
1501 else event = NSSM_EVENT_SERVICE_CONTROL_NOT_HANDLED;
\r
1503 log_event(EVENTLOG_INFORMATION_TYPE, event, service_name, text, 0);
\r
1505 if (event == NSSM_EVENT_SERVICE_CONTROL_UNKNOWN) {
\r
1506 HeapFree(GetProcessHeap(), 0, text);
\r
1510 /* Service control handler */
\r
1511 unsigned long WINAPI service_control_handler(unsigned long control, unsigned long event, void *data, void *context) {
\r
1512 nssm_service_t *service = (nssm_service_t *) context;
\r
1514 switch (control) {
\r
1515 case SERVICE_CONTROL_INTERROGATE:
\r
1516 /* We always keep the service status up-to-date so this is a no-op. */
\r
1519 case SERVICE_CONTROL_SHUTDOWN:
\r
1520 case SERVICE_CONTROL_STOP:
\r
1521 log_service_control(service->name, control, true);
\r
1523 We MUST acknowledge the stop request promptly but we're committed to
\r
1524 waiting for the application to exit. Spawn a new thread to wait
\r
1525 while we acknowledge the request.
\r
1527 if (! CreateThread(NULL, 0, shutdown_service, context, 0, NULL)) {
\r
1528 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETHREAD_FAILED, error_string(GetLastError()), 0);
\r
1531 We couldn't create a thread to tidy up so we'll have to force the tidyup
\r
1532 to complete in time in this thread.
\r
1534 service->kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;
\r
1535 service->kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;
\r
1536 service->kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;
\r
1538 stop_service(service, 0, true, true);
\r
1542 case SERVICE_CONTROL_CONTINUE:
\r
1543 log_service_control(service->name, control, true);
\r
1544 service->throttle = 0;
\r
1545 if (use_critical_section) imports.WakeConditionVariable(&service->throttle_condition);
\r
1547 if (! service->throttle_timer) return ERROR_CALL_NOT_IMPLEMENTED;
\r
1548 ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));
\r
1549 SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);
\r
1551 /* We can't continue if the application is running! */
\r
1552 if (! service->process_handle) service->status.dwCurrentState = SERVICE_CONTINUE_PENDING;
\r
1553 service->status.dwWaitHint = throttle_milliseconds(service->throttle) + NSSM_WAITHINT_MARGIN;
\r
1554 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESET_THROTTLE, service->name, 0);
\r
1555 SetServiceStatus(service->status_handle, &service->status);
\r
1558 case SERVICE_CONTROL_PAUSE:
\r
1560 We don't accept pause messages but it isn't possible to register
\r
1561 only for continue messages so we have to handle this case.
\r
1563 log_service_control(service->name, control, false);
\r
1564 return ERROR_CALL_NOT_IMPLEMENTED;
\r
1566 case NSSM_SERVICE_CONTROL_ROTATE:
\r
1567 log_service_control(service->name, control, true);
\r
1568 if (service->rotate_stdout_online == NSSM_ROTATE_ONLINE) service->rotate_stdout_online = NSSM_ROTATE_ONLINE_ASAP;
\r
1569 if (service->rotate_stderr_online == NSSM_ROTATE_ONLINE) service->rotate_stderr_online = NSSM_ROTATE_ONLINE_ASAP;
\r
1572 case SERVICE_CONTROL_POWEREVENT:
\r
1573 if (event != PBT_APMRESUMEAUTOMATIC) {
\r
1574 log_service_control(service->name, control, false);
\r
1577 log_service_control(service->name, control, true);
\r
1578 end_service((void *) service, false);
\r
1582 /* Unknown control */
\r
1583 log_service_control(service->name, control, false);
\r
1584 return ERROR_CALL_NOT_IMPLEMENTED;
\r
1587 /* Start the service */
\r
1588 int start_service(nssm_service_t *service) {
\r
1589 service->stopping = false;
\r
1590 service->allow_restart = true;
\r
1592 if (service->process_handle) return 0;
\r
1594 /* Allocate a STARTUPINFO structure for a new process */
\r
1596 ZeroMemory(&si, sizeof(si));
\r
1597 si.cb = sizeof(si);
\r
1599 /* Allocate a PROCESSINFO structure for the process */
\r
1600 PROCESS_INFORMATION pi;
\r
1601 ZeroMemory(&pi, sizeof(pi));
\r
1603 /* Get startup parameters */
\r
1604 int ret = get_parameters(service, &si);
\r
1606 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_PARAMETERS_FAILED, service->name, 0);
\r
1607 return stop_service(service, 2, true, true);
\r
1610 /* Launch executable with arguments */
\r
1611 TCHAR cmd[CMD_LENGTH];
\r
1612 if (_sntprintf_s(cmd, _countof(cmd), _TRUNCATE, _T("\"%s\" %s"), service->exe, service->flags) < 0) {
\r
1613 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("command line"), _T("start_service"), 0);
\r
1614 return stop_service(service, 2, true, true);
\r
1617 throttle_restart(service);
\r
1619 /* Set the environment. */
\r
1620 if (service->env) duplicate_environment(service->env);
\r
1621 if (service->env_extra) set_environment_block(service->env_extra);
\r
1623 /* Set up I/O redirection. */
\r
1624 if (get_output_handles(service, &si)) {
\r
1625 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_OUTPUT_HANDLES_FAILED, service->name, 0);
\r
1626 if (! service->no_console) FreeConsole();
\r
1627 close_output_handles(&si);
\r
1628 return stop_service(service, 4, true, true);
\r
1631 bool inherit_handles = false;
\r
1632 if (si.dwFlags & STARTF_USESTDHANDLES) inherit_handles = true;
\r
1633 unsigned long flags = service->priority & priority_mask();
\r
1634 if (service->affinity) flags |= CREATE_SUSPENDED;
\r
1635 if (! CreateProcess(0, cmd, 0, 0, inherit_handles, flags, 0, service->dir, &si, &pi)) {
\r
1636 unsigned long exitcode = 3;
\r
1637 unsigned long error = GetLastError();
\r
1638 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service->name, service->exe, error_string(error), 0);
\r
1639 close_output_handles(&si);
\r
1640 duplicate_environment_strings(service->initial_env);
\r
1641 return stop_service(service, exitcode, true, true);
\r
1643 service->process_handle = pi.hProcess;
\r
1644 service->pid = pi.dwProcessId;
\r
1646 if (get_process_creation_time(service->process_handle, &service->creation_time)) ZeroMemory(&service->creation_time, sizeof(service->creation_time));
\r
1648 close_output_handles(&si);
\r
1650 if (! service->no_console) FreeConsole();
\r
1652 /* Restore our environment. */
\r
1653 duplicate_environment_strings(service->initial_env);
\r
1655 if (service->affinity) {
\r
1657 We are explicitly storing service->affinity as a 64-bit unsigned integer
\r
1658 so that we can parse it regardless of whether we're running in 32-bit
\r
1659 or 64-bit mode. The arguments to SetProcessAffinityMask(), however, are
\r
1660 defined as type DWORD_PTR and hence limited to 32 bits on a 32-bit system
\r
1661 (or when running the 32-bit NSSM).
\r
1663 The result is a lot of seemingly-unnecessary casting throughout the code
\r
1664 and potentially confusion when we actually try to start the service.
\r
1665 Having said that, however, it's unlikely that we're actually going to
\r
1666 run in 32-bit mode on a system which has more than 32 CPUs so the
\r
1667 likelihood of seeing a confusing situation is somewhat diminished.
\r
1669 DWORD_PTR affinity, system_affinity;
\r
1671 if (GetProcessAffinityMask(service->process_handle, &affinity, &system_affinity)) affinity = service->affinity & system_affinity;
\r
1673 affinity = (DWORD_PTR) service->affinity;
\r
1674 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GETPROCESSAFFINITYMASK_FAILED, service->name, error_string(GetLastError()), 0);
\r
1677 if (! SetProcessAffinityMask(service->process_handle, affinity)) {
\r
1678 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_SETPROCESSAFFINITYMASK_FAILED, service->name, error_string(GetLastError()), 0);
\r
1681 ResumeThread(pi.hThread);
\r
1685 Wait for a clean startup before changing the service status to RUNNING
\r
1686 but be mindful of the fact that we are blocking the service control manager
\r
1687 so abandon the wait before too much time has elapsed.
\r
1689 unsigned long delay = service->throttle_delay;
\r
1690 if (delay > NSSM_SERVICE_STATUS_DEADLINE) {
\r
1691 TCHAR delay_milliseconds[16];
\r
1692 _sntprintf_s(delay_milliseconds, _countof(delay_milliseconds), _TRUNCATE, _T("%lu"), delay);
\r
1693 TCHAR deadline_milliseconds[16];
\r
1694 _sntprintf_s(deadline_milliseconds, _countof(deadline_milliseconds), _TRUNCATE, _T("%lu"), NSSM_SERVICE_STATUS_DEADLINE);
\r
1695 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_STARTUP_DELAY_TOO_LONG, service->name, delay_milliseconds, NSSM, deadline_milliseconds, 0);
\r
1696 delay = NSSM_SERVICE_STATUS_DEADLINE;
\r
1698 unsigned long deadline = WaitForSingleObject(service->process_handle, delay);
\r
1700 /* Signal successful start */
\r
1701 service->status.dwCurrentState = SERVICE_RUNNING;
\r
1702 SetServiceStatus(service->status_handle, &service->status);
\r
1704 /* Continue waiting for a clean startup. */
\r
1705 if (deadline == WAIT_TIMEOUT) {
\r
1706 if (service->throttle_delay > delay) {
\r
1707 if (WaitForSingleObject(service->process_handle, service->throttle_delay - delay) == WAIT_TIMEOUT) service->throttle = 0;
\r
1709 else service->throttle = 0;
\r
1712 /* Ensure the restart delay is always applied. */
\r
1713 if (service->restart_delay && ! service->throttle) service->throttle++;
\r
1718 /* Stop the service */
\r
1719 int stop_service(nssm_service_t *service, unsigned long exitcode, bool graceful, bool default_action) {
\r
1720 service->allow_restart = false;
\r
1721 if (service->wait_handle) {
\r
1722 UnregisterWait(service->wait_handle);
\r
1723 service->wait_handle = 0;
\r
1726 service->rotate_stdout_online = service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;
\r
1728 if (default_action && ! exitcode && ! graceful) {
\r
1729 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
1733 /* Signal we are stopping */
\r
1735 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
1736 service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;
\r
1737 SetServiceStatus(service->status_handle, &service->status);
\r
1740 /* Nothing to do if service isn't running */
\r
1741 if (service->pid) {
\r
1742 /* Shut down service */
\r
1743 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_TERMINATEPROCESS, service->name, service->exe, 0);
\r
1744 kill_process(service, service->process_handle, service->pid, 0);
\r
1746 else log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_PROCESS_ALREADY_STOPPED, service->name, service->exe, 0);
\r
1748 end_service((void *) service, true);
\r
1750 /* Signal we stopped */
\r
1752 service->status.dwCurrentState = SERVICE_STOPPED;
\r
1754 service->status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
\r
1755 service->status.dwServiceSpecificExitCode = exitcode;
\r
1758 service->status.dwWin32ExitCode = NO_ERROR;
\r
1759 service->status.dwServiceSpecificExitCode = 0;
\r
1761 SetServiceStatus(service->status_handle, &service->status);
\r
1767 /* Callback function triggered when the server exits */
\r
1768 void CALLBACK end_service(void *arg, unsigned char why) {
\r
1769 nssm_service_t *service = (nssm_service_t *) arg;
\r
1771 if (service->stopping) return;
\r
1773 service->stopping = true;
\r
1775 service->rotate_stdout_online = service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;
\r
1777 /* Use now as a dummy exit time. */
\r
1778 GetSystemTimeAsFileTime(&service->exit_time);
\r
1780 /* Check exit code */
\r
1781 unsigned long exitcode = 0;
\r
1783 if (service->process_handle) {
\r
1784 GetExitCodeProcess(service->process_handle, &exitcode);
\r
1785 /* Check real exit time. */
\r
1786 if (exitcode != STILL_ACTIVE) get_process_exit_time(service->process_handle, &service->exit_time);
\r
1787 CloseHandle(service->process_handle);
\r
1790 service->process_handle = 0;
\r
1793 Log that the service ended BEFORE logging about killing the process
\r
1794 tree. See below for the possible values of the why argument.
\r
1797 _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), exitcode);
\r
1798 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ENDED_SERVICE, service->exe, service->name, code, 0);
\r
1802 if (exitcode == STILL_ACTIVE) exitcode = 0;
\r
1803 if (service->pid) kill_process_tree(service, service->pid, exitcode, service->pid);
\r
1807 The why argument is true if our wait timed out or false otherwise.
\r
1808 Our wait is infinite so why will never be true when called by the system.
\r
1809 If it is indeed true, assume we were called from stop_service() because
\r
1810 this is a controlled shutdown, and don't take any restart action.
\r
1813 if (! service->allow_restart) return;
\r
1815 /* What action should we take? */
\r
1816 int action = NSSM_EXIT_RESTART;
\r
1817 TCHAR action_string[ACTION_LEN];
\r
1818 bool default_action;
\r
1819 if (! get_exit_action(service->name, &exitcode, action_string, &default_action)) {
\r
1820 for (int i = 0; exit_action_strings[i]; i++) {
\r
1821 if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
1829 /* Try to restart the service or return failure code to service manager */
\r
1830 case NSSM_EXIT_RESTART:
\r
1831 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_RESTART, service->name, code, exit_action_strings[action], service->exe, 0);
\r
1832 while (monitor_service(service)) {
\r
1833 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_RESTART_SERVICE_FAILED, service->exe, service->name, 0);
\r
1838 /* Do nothing, just like srvany would */
\r
1839 case NSSM_EXIT_IGNORE:
\r
1840 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_IGNORE, service->name, code, exit_action_strings[action], service->exe, 0);
\r
1844 /* Tell the service manager we are finished */
\r
1845 case NSSM_EXIT_REALLY:
\r
1846 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_REALLY, service->name, code, exit_action_strings[action], 0);
\r
1847 stop_service(service, exitcode, true, default_action);
\r
1850 /* Fake a crash so pre-Vista service managers will run recovery actions. */
\r
1851 case NSSM_EXIT_UNCLEAN:
\r
1852 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_UNCLEAN, service->name, code, exit_action_strings[action], 0);
\r
1853 stop_service(service, exitcode, false, default_action);
\r
1860 void throttle_restart(nssm_service_t *service) {
\r
1861 /* This can't be a restart if the service is already running. */
\r
1862 if (! service->throttle++) return;
\r
1865 unsigned long throttle_ms = throttle_milliseconds(service->throttle);
\r
1866 TCHAR threshold[8], milliseconds[8];
\r
1868 if (service->restart_delay > throttle_ms) ms = service->restart_delay;
\r
1869 else ms = throttle_ms;
\r
1871 if (service->throttle > 7) service->throttle = 8;
\r
1873 _sntprintf_s(milliseconds, _countof(milliseconds), _TRUNCATE, _T("%lu"), ms);
\r
1875 if (service->throttle == 1 && service->restart_delay > throttle_ms) log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESTART_DELAY, service->name, milliseconds, 0);
\r
1877 _sntprintf_s(threshold, _countof(threshold), _TRUNCATE, _T("%lu"), service->throttle_delay);
\r
1878 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_THROTTLED, service->name, threshold, milliseconds, 0);
\r
1881 if (use_critical_section) EnterCriticalSection(&service->throttle_section);
\r
1882 else if (service->throttle_timer) {
\r
1883 ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));
\r
1884 service->throttle_duetime.QuadPart = 0 - (ms * 10000LL);
\r
1885 SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);
\r
1888 service->status.dwCurrentState = SERVICE_PAUSED;
\r
1889 SetServiceStatus(service->status_handle, &service->status);
\r
1891 if (use_critical_section) {
\r
1892 imports.SleepConditionVariableCS(&service->throttle_condition, &service->throttle_section, ms);
\r
1893 LeaveCriticalSection(&service->throttle_section);
\r
1896 if (service->throttle_timer) WaitForSingleObject(service->throttle_timer, INFINITE);
\r
1902 When responding to a stop (or any other) request we need to set dwWaitHint to
\r
1903 the number of milliseconds we expect the operation to take, and optionally
\r
1904 increase dwCheckPoint. If dwWaitHint milliseconds elapses without the
\r
1905 operation completing or dwCheckPoint increasing, the system will consider the
\r
1906 service to be hung.
\r
1908 However the system will consider the service to be hung after 30000
\r
1909 milliseconds regardless of the value of dwWaitHint if dwCheckPoint has not
\r
1910 changed. Therefore if we want to wait longer than that we must periodically
\r
1911 increase dwCheckPoint.
\r
1913 Furthermore, it will consider the service to be hung after 60000 milliseconds
\r
1914 regardless of the value of dwCheckPoint unless dwWaitHint is increased every
\r
1915 time dwCheckPoint is also increased.
\r
1917 Our strategy then is to retrieve the initial dwWaitHint and wait for
\r
1918 NSSM_SERVICE_STATUS_DEADLINE milliseconds. If the process is still running
\r
1919 and we haven't finished waiting we increment dwCheckPoint and add whichever is
\r
1920 smaller of NSSM_SERVICE_STATUS_DEADLINE or the remaining timeout to
\r
1923 Only doing both these things will prevent the system from killing the service.
\r
1925 Returns: 1 if the wait timed out.
\r
1926 0 if the wait completed.
\r
1929 int await_shutdown(nssm_service_t *service, TCHAR *function_name, unsigned long timeout) {
\r
1930 unsigned long interval;
\r
1931 unsigned long waithint;
\r
1932 unsigned long ret;
\r
1933 unsigned long waited;
\r
1934 TCHAR interval_milliseconds[16];
\r
1935 TCHAR timeout_milliseconds[16];
\r
1936 TCHAR waited_milliseconds[16];
\r
1937 TCHAR *function = function_name;
\r
1939 /* Add brackets to function name. */
\r
1940 size_t funclen = _tcslen(function_name) + 3;
\r
1941 TCHAR *func = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, funclen * sizeof(TCHAR));
\r
1943 if (_sntprintf_s(func, funclen, _TRUNCATE, _T("%s()"), function_name) > -1) function = func;
\r
1946 _sntprintf_s(timeout_milliseconds, _countof(timeout_milliseconds), _TRUNCATE, _T("%lu"), timeout);
\r
1948 waithint = service->status.dwWaitHint;
\r
1950 while (waited < timeout) {
\r
1951 interval = timeout - waited;
\r
1952 if (interval > NSSM_SERVICE_STATUS_DEADLINE) interval = NSSM_SERVICE_STATUS_DEADLINE;
\r
1954 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
1955 service->status.dwWaitHint += interval;
\r
1956 service->status.dwCheckPoint++;
\r
1957 SetServiceStatus(service->status_handle, &service->status);
\r
1960 _sntprintf_s(waited_milliseconds, _countof(waited_milliseconds), _TRUNCATE, _T("%lu"), waited);
\r
1961 _sntprintf_s(interval_milliseconds, _countof(interval_milliseconds), _TRUNCATE, _T("%lu"), interval);
\r
1962 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_AWAITING_SHUTDOWN, function, service->name, waited_milliseconds, interval_milliseconds, timeout_milliseconds, 0);
\r
1965 switch (WaitForSingleObject(service->process_handle, interval)) {
\r
1966 case WAIT_OBJECT_0:
\r
1970 case WAIT_TIMEOUT:
\r
1979 waited += interval;
\r
1983 if (func) HeapFree(GetProcessHeap(), 0, func);
\r