4 bool use_critical_section;
\r
6 extern imports_t imports;
\r
7 extern settings_t settings[];
\r
9 const TCHAR *exit_action_strings[] = { _T("Restart"), _T("Ignore"), _T("Exit"), _T("Suicide"), 0 };
\r
10 const TCHAR *startup_strings[] = { _T("SERVICE_AUTO_START"), _T("SERVICE_DELAYED_AUTO_START"), _T("SERVICE_DEMAND_START"), _T("SERVICE_DISABLED"), 0 };
\r
11 const TCHAR *priority_strings[] = { _T("REALTIME_PRIORITY_CLASS"), _T("HIGH_PRIORITY_CLASS"), _T("ABOVE_NORMAL_PRIORITY_CLASS"), _T("NORMAL_PRIORITY_CLASS"), _T("BELOW_NORMAL_PRIORITY_CLASS"), _T("IDLE_PRIORITY_CLASS"), 0 };
\r
13 static hook_thread_t hook_threads = { NULL, 0 };
\r
21 Check the status in response to a control.
\r
22 Returns: 1 if the status is expected, eg STOP following CONTROL_STOP.
\r
23 0 if the status is desired, eg STOPPED following CONTROL_STOP.
\r
24 -1 if the status is undesired, eg STOPPED following CONTROL_START.
\r
26 static inline int service_control_response(unsigned long control, unsigned long status) {
\r
28 case NSSM_SERVICE_CONTROL_START:
\r
30 case SERVICE_START_PENDING:
\r
33 case SERVICE_RUNNING:
\r
40 case SERVICE_CONTROL_STOP:
\r
41 case SERVICE_CONTROL_SHUTDOWN:
\r
43 case SERVICE_RUNNING:
\r
44 case SERVICE_STOP_PENDING:
\r
47 case SERVICE_STOPPED:
\r
54 case SERVICE_CONTROL_PAUSE:
\r
56 case SERVICE_PAUSE_PENDING:
\r
59 case SERVICE_PAUSED:
\r
66 case SERVICE_CONTROL_CONTINUE:
\r
68 case SERVICE_CONTINUE_PENDING:
\r
71 case SERVICE_RUNNING:
\r
78 case SERVICE_CONTROL_INTERROGATE:
\r
79 case NSSM_SERVICE_CONTROL_ROTATE:
\r
86 static inline int await_service_control_response(unsigned long control, SC_HANDLE service_handle, SERVICE_STATUS *service_status, unsigned long initial_status) {
\r
88 unsigned long checkpoint = 0;
\r
89 unsigned long waithint = 0;
\r
90 while (QueryServiceStatus(service_handle, service_status)) {
\r
91 int response = service_control_response(control, service_status->dwCurrentState);
\r
92 /* Alas we can't WaitForSingleObject() on an SC_HANDLE. */
\r
93 if (! response) return response;
\r
94 if (response > 0 || service_status->dwCurrentState == initial_status) {
\r
95 if (service_status->dwCheckPoint != checkpoint || service_status->dwWaitHint != waithint) tries = 0;
\r
96 checkpoint = service_status->dwCheckPoint;
\r
97 waithint = service_status->dwWaitHint;
\r
98 if (++tries > 10) tries = 10;
\r
101 else return response;
\r
106 static inline void wait_for_hooks(nssm_service_t *service, bool notify) {
\r
107 SERVICE_STATUS_HANDLE status_handle;
\r
108 SERVICE_STATUS *status;
\r
110 /* On a clean shutdown we need to keep the service's status up-to-date. */
\r
112 status_handle = service->status_handle;
\r
113 status = &service->status;
\r
116 status_handle = NULL;
\r
120 EnterCriticalSection(&service->hook_section);
\r
121 await_hook_threads(&hook_threads, status_handle, status, NSSM_HOOK_THREAD_DEADLINE);
\r
122 LeaveCriticalSection(&service->hook_section);
\r
125 int affinity_mask_to_string(__int64 mask, TCHAR **string) {
\r
126 if (! string) return 1;
\r
134 /* SetProcessAffinityMask() accepts a mask of up to 64 processors. */
\r
136 for (n = 0; n < _countof(set); n++) set[n].first = set[n].last = -1;
\r
138 for (i = 0, n = 0; i < _countof(set); i++) {
\r
139 if (mask & (1LL << i)) {
\r
140 if (set[n].first == -1) set[n].first = set[n].last = (int) i;
\r
141 else if (set[n].last == (int) i - 1) set[n].last = (int) i;
\r
144 set[n].first = set[n].last = (int) i;
\r
149 /* Worst case is 2x2 characters for first and last CPU plus - and/or , */
\r
150 size_t len = (size_t) (n + 1) * 6;
\r
151 *string = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len * sizeof(TCHAR));
\r
152 if (! string) return 2;
\r
156 for (i = 0; i <= n; i++) {
\r
157 if (i) (*string)[s++] = _T(',');
\r
158 ret = _sntprintf_s(*string + s, 3, _TRUNCATE, _T("%u"), set[i].first);
\r
160 HeapFree(GetProcessHeap(), 0, *string);
\r
165 if (set[i].last != set[i].first) {
\r
166 ret =_sntprintf_s(*string + s, 4, _TRUNCATE, _T("%c%u"), (set[i].last == set[i].first + 1) ? _T(',') : _T('-'), set[i].last);
\r
168 HeapFree(GetProcessHeap(), 0, *string);
\r
179 int affinity_string_to_mask(TCHAR *string, __int64 *mask) {
\r
180 if (! mask) return 1;
\r
183 if (! string) return 0;
\r
192 unsigned long number;
\r
194 for (n = 0; n < _countof(set); n++) set[n].first = set[n].last = -1;
\r
198 ret = str_number(s, &number, &end);
\r
200 if (ret == 0 || ret == 2) {
\r
201 if (number >= _countof(set)) return 2;
\r
202 set[n].first = set[n].last = (int) number;
\r
214 if (! *(++s)) return 3;
\r
215 ret = str_number(s, &number, &end);
\r
216 if (ret == 0 || ret == 2) {
\r
218 if (! *s || *s == _T(',')) {
\r
219 set[n].last = (int) number;
\r
236 for (i = 0; i <= n; i++) {
\r
237 for (int j = set[i].first; j <= set[i].last; j++) (__int64) *mask |= (1LL << (__int64) j);
\r
243 unsigned long priority_mask() {
\r
244 return REALTIME_PRIORITY_CLASS | HIGH_PRIORITY_CLASS | ABOVE_NORMAL_PRIORITY_CLASS | NORMAL_PRIORITY_CLASS | BELOW_NORMAL_PRIORITY_CLASS | IDLE_PRIORITY_CLASS;
\r
247 int priority_constant_to_index(unsigned long constant) {
\r
248 switch (constant & priority_mask()) {
\r
249 case REALTIME_PRIORITY_CLASS: return NSSM_REALTIME_PRIORITY;
\r
250 case HIGH_PRIORITY_CLASS: return NSSM_HIGH_PRIORITY;
\r
251 case ABOVE_NORMAL_PRIORITY_CLASS: return NSSM_ABOVE_NORMAL_PRIORITY;
\r
252 case BELOW_NORMAL_PRIORITY_CLASS: return NSSM_BELOW_NORMAL_PRIORITY;
\r
253 case IDLE_PRIORITY_CLASS: return NSSM_IDLE_PRIORITY;
\r
255 return NSSM_NORMAL_PRIORITY;
\r
258 unsigned long priority_index_to_constant(int index) {
\r
260 case NSSM_REALTIME_PRIORITY: return REALTIME_PRIORITY_CLASS;
\r
261 case NSSM_HIGH_PRIORITY: return HIGH_PRIORITY_CLASS;
\r
262 case NSSM_ABOVE_NORMAL_PRIORITY: return ABOVE_NORMAL_PRIORITY_CLASS;
\r
263 case NSSM_BELOW_NORMAL_PRIORITY: return BELOW_NORMAL_PRIORITY_CLASS;
\r
264 case NSSM_IDLE_PRIORITY: return IDLE_PRIORITY_CLASS;
\r
266 return NORMAL_PRIORITY_CLASS;
\r
269 static inline unsigned long throttle_milliseconds(unsigned long throttle) {
\r
270 if (throttle > 7) throttle = 8;
\r
271 /* pow() operates on doubles. */
\r
272 unsigned long ret = 1; for (unsigned long i = 1; i < throttle; i++) ret *= 2;
\r
276 void set_service_environment(nssm_service_t *service) {
\r
277 if (! service) return;
\r
280 We have to duplicate the block because this function will be called
\r
281 multiple times between registry reads.
\r
283 if (service->env) duplicate_environment_strings(service->env);
\r
284 if (! service->env_extra) return;
\r
285 TCHAR *env_extra = copy_environment_block(service->env_extra);
\r
286 if (! env_extra) return;
\r
288 set_environment_block(env_extra);
\r
289 HeapFree(GetProcessHeap(), 0, env_extra);
\r
292 void unset_service_environment(nssm_service_t *service) {
\r
293 if (! service) return;
\r
294 duplicate_environment_strings(service->initial_env);
\r
298 Wrapper to be called in a new thread so that we can acknowledge a STOP
\r
299 control immediately.
\r
301 static unsigned long WINAPI shutdown_service(void *arg) {
\r
302 return stop_service((nssm_service_t *) arg, 0, true, true);
\r
306 Wrapper to be called in a new thread so that we can acknowledge start
\r
309 static unsigned long WINAPI launch_service(void *arg) {
\r
310 return monitor_service((nssm_service_t *) arg);
\r
313 /* Connect to the service manager */
\r
314 SC_HANDLE open_service_manager(unsigned long access) {
\r
315 SC_HANDLE ret = OpenSCManager(0, SERVICES_ACTIVE_DATABASE, access);
\r
317 if (is_admin) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENSCMANAGER_FAILED, 0);
\r
324 /* Open a service by name or display name. */
\r
325 SC_HANDLE open_service(SC_HANDLE services, TCHAR *service_name, unsigned long access, TCHAR *canonical_name, unsigned long canonical_namelen) {
\r
326 SC_HANDLE service_handle = OpenService(services, service_name, access);
\r
327 if (service_handle) {
\r
328 if (canonical_name && canonical_name != service_name) {
\r
329 TCHAR displayname[SERVICE_NAME_LENGTH];
\r
330 unsigned long displayname_len = (unsigned long) _countof(displayname);
\r
331 GetServiceDisplayName(services, service_name, displayname, &displayname_len);
\r
332 unsigned long keyname_len = canonical_namelen;
\r
333 GetServiceKeyName(services, displayname, canonical_name, &keyname_len);
\r
335 return service_handle;
\r
338 unsigned long error = GetLastError();
\r
339 if (error != ERROR_SERVICE_DOES_NOT_EXIST) {
\r
340 print_message(stderr, NSSM_MESSAGE_OPENSERVICE_FAILED, error_string(GetLastError()));
\r
344 /* We can't look for a display name because there's no buffer to store it. */
\r
345 if (! canonical_name) {
\r
346 print_message(stderr, NSSM_MESSAGE_OPENSERVICE_FAILED, error_string(GetLastError()));
\r
350 unsigned long bufsize, required, count, i;
\r
351 unsigned long resume = 0;
\r
352 EnumServicesStatus(services, SERVICE_DRIVER | SERVICE_FILE_SYSTEM_DRIVER | SERVICE_KERNEL_DRIVER | SERVICE_WIN32, SERVICE_STATE_ALL, 0, 0, &required, &count, &resume);
\r
353 error = GetLastError();
\r
354 if (error != ERROR_MORE_DATA) {
\r
355 print_message(stderr, NSSM_MESSAGE_ENUMSERVICESSTATUS_FAILED, error_string(GetLastError()));
\r
359 ENUM_SERVICE_STATUS *status = (ENUM_SERVICE_STATUS *) HeapAlloc(GetProcessHeap(), 0, required);
\r
361 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("ENUM_SERVICE_STATUS"), _T("open_service()"));
\r
365 bufsize = required;
\r
368 EnumServicesStatus() returns:
\r
369 1 when it retrieved data and there's no more data to come.
\r
370 0 and sets last error to ERROR_MORE_DATA when it retrieved data and
\r
371 there's more data to come.
\r
372 0 and sets last error to something else on error.
\r
374 int ret = EnumServicesStatus(services, SERVICE_DRIVER | SERVICE_FILE_SYSTEM_DRIVER | SERVICE_KERNEL_DRIVER | SERVICE_WIN32, SERVICE_STATE_ALL, status, bufsize, &required, &count, &resume);
\r
376 error = GetLastError();
\r
377 if (error != ERROR_MORE_DATA) {
\r
378 HeapFree(GetProcessHeap(), 0, status);
\r
379 print_message(stderr, NSSM_MESSAGE_ENUMSERVICESSTATUS_FAILED, error_string(GetLastError()));
\r
384 for (i = 0; i < count; i++) {
\r
385 if (str_equiv(status[i].lpDisplayName, service_name)) {
\r
386 if (_sntprintf_s(canonical_name, canonical_namelen, _TRUNCATE, _T("%s"), status[i].lpServiceName) < 0) {
\r
387 HeapFree(GetProcessHeap(), 0, status);
\r
388 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("canonical_name"), _T("open_service()"));
\r
392 HeapFree(GetProcessHeap(), 0, status);
\r
393 return open_service(services, canonical_name, access, 0, 0);
\r
400 /* Recurse so we can get an error message. */
\r
401 return open_service(services, service_name, access, 0, 0);
\r
404 QUERY_SERVICE_CONFIG *query_service_config(const TCHAR *service_name, SC_HANDLE service_handle) {
\r
405 QUERY_SERVICE_CONFIG *qsc;
\r
406 unsigned long bufsize;
\r
407 unsigned long error;
\r
409 QueryServiceConfig(service_handle, 0, 0, &bufsize);
\r
410 error = GetLastError();
\r
411 if (error == ERROR_INSUFFICIENT_BUFFER) {
\r
412 qsc = (QUERY_SERVICE_CONFIG *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, bufsize);
\r
414 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("QUERY_SERVICE_CONFIG"), _T("query_service_config()"), 0);
\r
419 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG_FAILED, service_name, error_string(error), 0);
\r
423 if (! QueryServiceConfig(service_handle, qsc, bufsize, &bufsize)) {
\r
424 HeapFree(GetProcessHeap(), 0, qsc);
\r
425 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG_FAILED, service_name, error_string(GetLastError()), 0);
\r
432 int set_service_dependencies(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR *buffer) {
\r
433 TCHAR *dependencies = _T("");
\r
434 unsigned long num_dependencies = 0;
\r
436 if (buffer && buffer[0]) {
\r
437 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
\r
439 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
444 Count the dependencies then allocate a buffer big enough for their
\r
445 canonical names, ie n * SERVICE_NAME_LENGTH.
\r
449 for (s = buffer; *s; s++) {
\r
450 num_dependencies++;
\r
451 if (*s == SC_GROUP_IDENTIFIER) groups = s;
\r
455 /* At least one dependency is a group so we need to verify them. */
\r
458 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, NSSM_REGISTRY_GROUPS, 0, KEY_READ, &key)) {
\r
459 _ftprintf(stderr, _T("%s: %s\n"), NSSM_REGISTRY_GROUPS, error_string(GetLastError()));
\r
463 unsigned long type;
\r
464 unsigned long groupslen;
\r
465 unsigned long ret = RegQueryValueEx(key, NSSM_REG_GROUPS, 0, &type, NULL, &groupslen);
\r
466 if (ret == ERROR_SUCCESS) {
\r
467 groups = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, groupslen);
\r
469 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("groups"), _T("set_service_dependencies()"));
\r
473 ret = RegQueryValueEx(key, NSSM_REG_GROUPS, 0, &type, (unsigned char *) groups, &groupslen);
\r
474 if (ret != ERROR_SUCCESS) {
\r
475 _ftprintf(stderr, _T("%s\\%s: %s"), NSSM_REGISTRY_GROUPS, NSSM_REG_GROUPS, error_string(GetLastError()));
\r
476 HeapFree(GetProcessHeap(), 0, groups);
\r
481 else if (ret != ERROR_FILE_NOT_FOUND) {
\r
482 _ftprintf(stderr, _T("%s\\%s: %s"), NSSM_REGISTRY_GROUPS, NSSM_REG_GROUPS, error_string(GetLastError()));
\r
491 unsigned long dependencieslen = (num_dependencies * SERVICE_NAME_LENGTH) + 2;
\r
492 dependencies = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dependencieslen * sizeof(TCHAR));
\r
495 TCHAR dependency[SERVICE_NAME_LENGTH];
\r
496 for (s = buffer; *s; s++) {
\r
498 if (*s == SC_GROUP_IDENTIFIER) {
\r
499 TCHAR *group = s + 1;
\r
503 for (TCHAR *g = groups; *g; g++) {
\r
504 if (str_equiv(g, group)) {
\r
506 /* Set canonical name. */
\r
507 memmove(group, g, _tcslen(g) * sizeof(TCHAR));
\r
515 if (ok) _sntprintf_s(dependency, _countof(dependency), _TRUNCATE, _T("%s"), s);
\r
517 HeapFree(GetProcessHeap(), 0, dependencies);
\r
518 if (groups) HeapFree(GetProcessHeap(), 0, groups);
\r
519 _ftprintf(stderr, _T("%s: %s"), s, error_string(ERROR_SERVICE_DEPENDENCY_DELETED));
\r
524 SC_HANDLE dependency_handle = open_service(services, s, SERVICE_QUERY_STATUS, dependency, _countof(dependency));
\r
525 if (! dependency_handle) {
\r
526 HeapFree(GetProcessHeap(), 0, dependencies);
\r
527 if (groups) HeapFree(GetProcessHeap(), 0, groups);
\r
528 CloseServiceHandle(services);
\r
529 _ftprintf(stderr, _T("%s: %s"), s, error_string(ERROR_SERVICE_DEPENDENCY_DELETED));
\r
534 size_t len = _tcslen(dependency) + 1;
\r
535 memmove(dependencies + i, dependency, len * sizeof(TCHAR));
\r
541 if (groups) HeapFree(GetProcessHeap(), 0, groups);
\r
542 CloseServiceHandle(services);
\r
545 if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, dependencies, 0, 0, 0)) {
\r
546 if (num_dependencies) HeapFree(GetProcessHeap(), 0, dependencies);
\r
547 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
551 if (num_dependencies) HeapFree(GetProcessHeap(), 0, dependencies);
\r
555 int get_service_dependencies(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR **buffer, unsigned long *bufsize, int type) {
\r
556 if (! buffer) return 1;
\r
557 if (! bufsize) return 2;
\r
562 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
563 if (! qsc) return 3;
\r
565 if (! qsc->lpDependencies) return 0;
\r
566 if (! qsc->lpDependencies[0]) return 0;
\r
568 /* lpDependencies is doubly NULL terminated. */
\r
569 while (qsc->lpDependencies[*bufsize]) {
\r
570 while (qsc->lpDependencies[*bufsize]) ++*bufsize;
\r
576 *buffer = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *bufsize * sizeof(TCHAR));
\r
579 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("lpDependencies"), _T("get_service_dependencies()"));
\r
583 if (type == DEPENDENCY_ALL) memmove(*buffer, qsc->lpDependencies, *bufsize * sizeof(TCHAR));
\r
588 for (s = qsc->lpDependencies; *s; s++) {
\r
589 /* Only copy the appropriate type of dependency. */
\r
590 if ((*s == SC_GROUP_IDENTIFIER && type & DEPENDENCY_GROUPS) || (*s != SC_GROUP_IDENTIFIER && type & DEPENDENCY_SERVICES)) {
\r
591 size_t len = _tcslen(s) + 1;
\r
592 *bufsize += (unsigned long) len;
\r
593 memmove(*buffer + i, s, len * sizeof(TCHAR));
\r
602 HeapFree(GetProcessHeap(), 0, qsc);
\r
607 int get_service_dependencies(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR **buffer, unsigned long *bufsize) {
\r
608 return get_service_dependencies(service_name, service_handle, buffer, bufsize, DEPENDENCY_ALL);
\r
611 int set_service_description(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR *buffer) {
\r
612 SERVICE_DESCRIPTION description;
\r
613 ZeroMemory(&description, sizeof(description));
\r
615 lpDescription must be NULL if we aren't changing, the new description
\r
618 if (buffer && buffer[0]) description.lpDescription = buffer;
\r
619 else description.lpDescription = _T("");
\r
621 if (ChangeServiceConfig2(service_handle, SERVICE_CONFIG_DESCRIPTION, &description)) return 0;
\r
623 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_DESCRIPTION_FAILED, service_name, error_string(GetLastError()), 0);
\r
627 int get_service_description(const TCHAR *service_name, SC_HANDLE service_handle, unsigned long len, TCHAR *buffer) {
\r
628 if (! buffer) return 1;
\r
630 unsigned long bufsize;
\r
631 QueryServiceConfig2(service_handle, SERVICE_CONFIG_DESCRIPTION, 0, 0, &bufsize);
\r
632 unsigned long error = GetLastError();
\r
633 if (error == ERROR_INSUFFICIENT_BUFFER) {
\r
634 SERVICE_DESCRIPTION *description = (SERVICE_DESCRIPTION *) HeapAlloc(GetProcessHeap(), 0, bufsize);
\r
635 if (! description) {
\r
636 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("SERVICE_CONFIG_DESCRIPTION"), _T("get_service_description()"));
\r
640 if (QueryServiceConfig2(service_handle, SERVICE_CONFIG_DESCRIPTION, (unsigned char *) description, bufsize, &bufsize)) {
\r
641 if (description->lpDescription) _sntprintf_s(buffer, len, _TRUNCATE, _T("%s"), description->lpDescription);
\r
642 else ZeroMemory(buffer, len * sizeof(TCHAR));
\r
643 HeapFree(GetProcessHeap(), 0, description);
\r
647 HeapFree(GetProcessHeap(), 0, description);
\r
648 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_CONFIG_DESCRIPTION"), error_string(error));
\r
653 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_CONFIG_DESCRIPTION"), error_string(error));
\r
660 int get_service_startup(const TCHAR *service_name, SC_HANDLE service_handle, const QUERY_SERVICE_CONFIG *qsc, unsigned long *startup) {
\r
661 if (! qsc) return 1;
\r
663 switch (qsc->dwStartType) {
\r
664 case SERVICE_DEMAND_START: *startup = NSSM_STARTUP_MANUAL; break;
\r
665 case SERVICE_DISABLED: *startup = NSSM_STARTUP_DISABLED; break;
\r
666 default: *startup = NSSM_STARTUP_AUTOMATIC;
\r
669 if (*startup != NSSM_STARTUP_AUTOMATIC) return 0;
\r
671 /* Check for delayed start. */
\r
672 unsigned long bufsize;
\r
673 unsigned long error;
\r
674 QueryServiceConfig2(service_handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, 0, 0, &bufsize);
\r
675 error = GetLastError();
\r
676 if (error == ERROR_INSUFFICIENT_BUFFER) {
\r
677 SERVICE_DELAYED_AUTO_START_INFO *info = (SERVICE_DELAYED_AUTO_START_INFO *) HeapAlloc(GetProcessHeap(), 0, bufsize);
\r
679 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("SERVICE_DELAYED_AUTO_START_INFO"), _T("get_service_startup()"));
\r
683 if (QueryServiceConfig2(service_handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, (unsigned char *) info, bufsize, &bufsize)) {
\r
684 if (info->fDelayedAutostart) *startup = NSSM_STARTUP_DELAYED;
\r
685 HeapFree(GetProcessHeap(), 0, info);
\r
689 error = GetLastError();
\r
690 if (error != ERROR_INVALID_LEVEL) {
\r
691 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_CONFIG_DELAYED_AUTO_START_INFO"), error_string(error));
\r
696 else if (error != ERROR_INVALID_LEVEL) {
\r
697 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_DELAYED_AUTO_START_INFO"), error_string(error));
\r
704 int get_service_username(const TCHAR *service_name, const QUERY_SERVICE_CONFIG *qsc, TCHAR **username, size_t *usernamelen) {
\r
705 if (! username) return 1;
\r
706 if (! usernamelen) return 1;
\r
711 if (! qsc) return 1;
\r
713 if (qsc->lpServiceStartName[0]) {
\r
714 if (is_localsystem(qsc->lpServiceStartName)) return 0;
\r
716 size_t len = _tcslen(qsc->lpServiceStartName);
\r
717 *username = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(TCHAR));
\r
719 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("username"), _T("get_service_username()"));
\r
723 memmove(*username, qsc->lpServiceStartName, (len + 1) * sizeof(TCHAR));
\r
724 *usernamelen = len;
\r
730 /* Set default values which aren't zero. */
\r
731 void set_nssm_service_defaults(nssm_service_t *service) {
\r
732 if (! service) return;
\r
734 service->type = SERVICE_WIN32_OWN_PROCESS;
\r
735 service->priority = NORMAL_PRIORITY_CLASS;
\r
736 service->stdin_sharing = NSSM_STDIN_SHARING;
\r
737 service->stdin_disposition = NSSM_STDIN_DISPOSITION;
\r
738 service->stdin_flags = NSSM_STDIN_FLAGS;
\r
739 service->stdout_sharing = NSSM_STDOUT_SHARING;
\r
740 service->stdout_disposition = NSSM_STDOUT_DISPOSITION;
\r
741 service->stdout_flags = NSSM_STDOUT_FLAGS;
\r
742 service->stderr_sharing = NSSM_STDERR_SHARING;
\r
743 service->stderr_disposition = NSSM_STDERR_DISPOSITION;
\r
744 service->stderr_flags = NSSM_STDERR_FLAGS;
\r
745 service->throttle_delay = NSSM_RESET_THROTTLE_RESTART;
\r
746 service->stop_method = ~0;
\r
747 service->kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;
\r
748 service->kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;
\r
749 service->kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;
\r
750 service->kill_process_tree = 1;
\r
753 /* Allocate and zero memory for a service. */
\r
754 nssm_service_t *alloc_nssm_service() {
\r
755 nssm_service_t *service = (nssm_service_t *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(nssm_service_t));
\r
756 if (! service) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("service"), _T("alloc_nssm_service()"), 0);
\r
760 /* Free memory for a service. */
\r
761 void cleanup_nssm_service(nssm_service_t *service) {
\r
762 if (! service) return;
\r
763 if (service->username) HeapFree(GetProcessHeap(), 0, service->username);
\r
764 if (service->password) {
\r
765 SecureZeroMemory(service->password, service->passwordlen);
\r
766 HeapFree(GetProcessHeap(), 0, service->password);
\r
768 if (service->dependencies) HeapFree(GetProcessHeap(), 0, service->dependencies);
\r
769 if (service->env) HeapFree(GetProcessHeap(), 0, service->env);
\r
770 if (service->env_extra) HeapFree(GetProcessHeap(), 0, service->env_extra);
\r
771 if (service->handle) CloseServiceHandle(service->handle);
\r
772 if (service->process_handle) CloseHandle(service->process_handle);
\r
773 if (service->wait_handle) UnregisterWait(service->wait_handle);
\r
774 if (service->throttle_section_initialised) DeleteCriticalSection(&service->throttle_section);
\r
775 if (service->throttle_timer) CloseHandle(service->throttle_timer);
\r
776 if (service->hook_section_initialised) DeleteCriticalSection(&service->hook_section);
\r
777 if (service->initial_env) HeapFree(GetProcessHeap(), 0, service->initial_env);
\r
778 HeapFree(GetProcessHeap(), 0, service);
\r
781 /* About to install the service */
\r
782 int pre_install_service(int argc, TCHAR **argv) {
\r
783 nssm_service_t *service = alloc_nssm_service();
\r
784 set_nssm_service_defaults(service);
\r
785 if (argc) _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]);
\r
787 /* Show the dialogue box if we didn't give the service name and path */
\r
788 if (argc < 2) return nssm_gui(IDD_INSTALL, service);
\r
791 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("service"), _T("pre_install_service()"));
\r
794 _sntprintf_s(service->exe, _countof(service->exe), _TRUNCATE, _T("%s"), argv[1]);
\r
796 /* Arguments are optional */
\r
797 size_t flagslen = 0;
\r
800 for (i = 2; i < argc; i++) flagslen += _tcslen(argv[i]) + 1;
\r
801 if (! flagslen) flagslen = 1;
\r
802 if (flagslen > _countof(service->flags)) {
\r
803 print_message(stderr, NSSM_MESSAGE_FLAGS_TOO_LONG);
\r
807 for (i = 2; i < argc; i++) {
\r
808 size_t len = _tcslen(argv[i]);
\r
809 memmove(service->flags + s, argv[i], len * sizeof(TCHAR));
\r
811 if (i < argc - 1) service->flags[s++] = _T(' ');
\r
814 /* Work out directory name */
\r
815 _sntprintf_s(service->dir, _countof(service->dir), _TRUNCATE, _T("%s"), service->exe);
\r
816 strip_basename(service->dir);
\r
818 int ret = install_service(service);
\r
819 cleanup_nssm_service(service);
\r
823 /* About to edit the service. */
\r
824 int pre_edit_service(int argc, TCHAR **argv) {
\r
825 /* Require service name. */
\r
826 if (argc < 2) return usage(1);
\r
828 /* Are we editing on the command line? */
\r
829 enum { MODE_EDITING, MODE_GETTING, MODE_SETTING, MODE_RESETTING } mode = MODE_EDITING;
\r
830 const TCHAR *verb = argv[0];
\r
831 const TCHAR *service_name = argv[1];
\r
832 bool getting = false;
\r
833 bool unsetting = false;
\r
835 /* Minimum number of arguments. */
\r
837 /* Index of first value. */
\r
840 if (str_equiv(verb, _T("get"))) {
\r
842 mode = MODE_GETTING;
\r
844 else if (str_equiv(verb, _T("set"))) {
\r
846 mode = MODE_SETTING;
\r
848 else if (str_equiv(verb, _T("reset")) || str_equiv(verb, _T("unset"))) {
\r
850 mode = MODE_RESETTING;
\r
852 if (argc < mandatory) return usage(1);
\r
854 const TCHAR *parameter = 0;
\r
855 settings_t *setting = 0;
\r
858 /* Validate the parameter. */
\r
859 if (mandatory > 2) {
\r
860 bool additional_mandatory = false;
\r
862 parameter = argv[2];
\r
863 for (i = 0; settings[i].name; i++) {
\r
864 setting = &settings[i];
\r
865 if (! str_equiv(setting->name, parameter)) continue;
\r
866 if (((setting->additional & ADDITIONAL_GETTING) && mode == MODE_GETTING) || ((setting->additional & ADDITIONAL_SETTING) && mode == MODE_SETTING) || ((setting->additional & ADDITIONAL_RESETTING) && mode == MODE_RESETTING)) {
\r
867 additional_mandatory = true;
\r
872 if (! settings[i].name) {
\r
873 print_message(stderr, NSSM_MESSAGE_INVALID_PARAMETER, parameter);
\r
874 for (i = 0; settings[i].name; i++) _ftprintf(stderr, _T("%s\n"), settings[i].name);
\r
879 if (additional_mandatory) {
\r
880 if (argc < mandatory) {
\r
881 print_message(stderr, NSSM_MESSAGE_MISSING_SUBPARAMETER, parameter);
\r
884 additional = argv[3];
\r
887 else if (str_equiv(setting->name, NSSM_NATIVE_OBJECTNAME) && mode == MODE_SETTING) {
\r
888 additional = argv[3];
\r
892 additional = argv[remainder];
\r
893 if (argc < mandatory) return usage(1);
\r
897 nssm_service_t *service = alloc_nssm_service();
\r
898 _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), service_name);
\r
900 /* Open service manager */
\r
901 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
\r
903 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
907 /* Try to open the service */
\r
908 unsigned long access = SERVICE_QUERY_CONFIG;
\r
909 if (mode != MODE_GETTING) access |= SERVICE_CHANGE_CONFIG;
\r
910 service->handle = open_service(services, service->name, access, service->name, _countof(service->name));
\r
911 if (! service->handle) {
\r
912 CloseServiceHandle(services);
\r
916 /* Get system details. */
\r
917 QUERY_SERVICE_CONFIG *qsc = query_service_config(service->name, service->handle);
\r
919 CloseServiceHandle(service->handle);
\r
920 CloseServiceHandle(services);
\r
924 service->type = qsc->dwServiceType;
\r
925 if (! (service->type & SERVICE_WIN32_OWN_PROCESS)) {
\r
926 if (mode != MODE_GETTING) {
\r
927 HeapFree(GetProcessHeap(), 0, qsc);
\r
928 CloseServiceHandle(service->handle);
\r
929 CloseServiceHandle(services);
\r
930 print_message(stderr, NSSM_MESSAGE_CANNOT_EDIT, service->name, NSSM_WIN32_OWN_PROCESS, 0);
\r
935 if (get_service_startup(service->name, service->handle, qsc, &service->startup)) {
\r
936 if (mode != MODE_GETTING) {
\r
937 HeapFree(GetProcessHeap(), 0, qsc);
\r
938 CloseServiceHandle(service->handle);
\r
939 CloseServiceHandle(services);
\r
944 if (get_service_username(service->name, qsc, &service->username, &service->usernamelen)) {
\r
945 if (mode != MODE_GETTING) {
\r
946 HeapFree(GetProcessHeap(), 0, qsc);
\r
947 CloseServiceHandle(service->handle);
\r
948 CloseServiceHandle(services);
\r
953 _sntprintf_s(service->displayname, _countof(service->displayname), _TRUNCATE, _T("%s"), qsc->lpDisplayName);
\r
955 /* Get the canonical service name. We open it case insensitively. */
\r
956 unsigned long bufsize = _countof(service->name);
\r
957 GetServiceKeyName(services, service->displayname, service->name, &bufsize);
\r
959 /* Remember the executable in case it isn't NSSM. */
\r
960 _sntprintf_s(service->image, _countof(service->image), _TRUNCATE, _T("%s"), qsc->lpBinaryPathName);
\r
961 HeapFree(GetProcessHeap(), 0, qsc);
\r
963 /* Get extended system details. */
\r
964 if (get_service_description(service->name, service->handle, _countof(service->description), service->description)) {
\r
965 if (mode != MODE_GETTING) {
\r
966 CloseServiceHandle(service->handle);
\r
967 CloseServiceHandle(services);
\r
972 if (get_service_dependencies(service->name, service->handle, &service->dependencies, &service->dependencieslen)) {
\r
973 if (mode != MODE_GETTING) {
\r
974 CloseServiceHandle(service->handle);
\r
975 CloseServiceHandle(services);
\r
980 /* Get NSSM details. */
\r
981 get_parameters(service, 0);
\r
983 CloseServiceHandle(services);
\r
985 if (! service->exe[0]) {
\r
986 service->native = true;
\r
987 if (mode != MODE_GETTING) print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE, service->name, NSSM, service->image);
\r
990 /* Editing with the GUI. */
\r
991 if (mode == MODE_EDITING) {
\r
992 nssm_gui(IDD_EDIT, service);
\r
996 /* Trying to manage App* parameters for a non-NSSM service. */
\r
997 if (! setting->native && service->native) {
\r
998 CloseServiceHandle(service->handle);
\r
999 print_message(stderr, NSSM_MESSAGE_NATIVE_PARAMETER, setting->name, NSSM);
\r
1007 if (mode == MODE_GETTING) {
\r
1008 if (! service->native) {
\r
1009 key = open_registry(service->name, KEY_READ);
\r
1010 if (! key) return 4;
\r
1013 if (setting->native) ret = get_setting(service->name, service->handle, setting, &value, additional);
\r
1014 else ret = get_setting(service->name, key, setting, &value, additional);
\r
1016 CloseServiceHandle(service->handle);
\r
1020 switch (setting->type) {
\r
1021 case REG_EXPAND_SZ:
\r
1022 case REG_MULTI_SZ:
\r
1024 _tprintf(_T("%s\n"), value.string ? value.string : _T(""));
\r
1025 HeapFree(GetProcessHeap(), 0, value.string);
\r
1029 _tprintf(_T("%u\n"), value.numeric);
\r
1033 if (! service->native) RegCloseKey(key);
\r
1034 CloseServiceHandle(service->handle);
\r
1038 /* Build the value. */
\r
1039 if (mode == MODE_RESETTING) {
\r
1040 /* Unset the parameter. */
\r
1043 else if (remainder == argc) {
\r
1047 /* Set the parameter. */
\r
1049 size_t delimiterlen = (setting->additional & ADDITIONAL_CRLF) ? 2 : 1;
\r
1050 for (i = remainder; i < argc; i++) len += _tcslen(argv[i]) + delimiterlen;
\r
1053 value.string = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, len * sizeof(TCHAR));
\r
1054 if (! value.string) {
\r
1055 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("value"), _T("edit_service()"));
\r
1056 CloseServiceHandle(service->handle);
\r
1061 for (i = remainder; i < argc; i++) {
\r
1062 size_t len = _tcslen(argv[i]);
\r
1063 memmove(value.string + s, argv[i], len * sizeof(TCHAR));
\r
1065 if (i < argc - 1) {
\r
1066 if (setting->additional & ADDITIONAL_CRLF) {
\r
1067 value.string[s++] = _T('\r');
\r
1068 value.string[s++] = _T('\n');
\r
1070 else value.string[s++] = _T(' ');
\r
1073 value.string[s] = _T('\0');
\r
1076 if (! service->native) {
\r
1077 key = open_registry(service->name, KEY_WRITE);
\r
1079 if (value.string) HeapFree(GetProcessHeap(), 0, value.string);
\r
1084 if (setting->native) ret = set_setting(service->name, service->handle, setting, &value, additional);
\r
1085 else ret = set_setting(service->name, key, setting, &value, additional);
\r
1086 if (value.string) HeapFree(GetProcessHeap(), 0, value.string);
\r
1088 if (! service->native) RegCloseKey(key);
\r
1089 CloseServiceHandle(service->handle);
\r
1093 if (! service->native) RegCloseKey(key);
\r
1094 CloseServiceHandle(service->handle);
\r
1099 /* About to remove the service */
\r
1100 int pre_remove_service(int argc, TCHAR **argv) {
\r
1101 nssm_service_t *service = alloc_nssm_service();
\r
1102 set_nssm_service_defaults(service);
\r
1103 if (argc) _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]);
\r
1105 /* Show dialogue box if we didn't pass service name and "confirm" */
\r
1106 if (argc < 2) return nssm_gui(IDD_REMOVE, service);
\r
1107 if (str_equiv(argv[1], _T("confirm"))) {
\r
1108 int ret = remove_service(service);
\r
1109 cleanup_nssm_service(service);
\r
1112 print_message(stderr, NSSM_MESSAGE_PRE_REMOVE_SERVICE);
\r
1116 /* Install the service */
\r
1117 int install_service(nssm_service_t *service) {
\r
1118 if (! service) return 1;
\r
1120 /* Open service manager */
\r
1121 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE);
\r
1123 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
1124 cleanup_nssm_service(service);
\r
1128 /* Get path of this program */
\r
1129 _sntprintf_s(service->image, _countof(service->image), _TRUNCATE, _T("%s"), nssm_imagepath());
\r
1131 /* Create the service - settings will be changed in edit_service() */
\r
1132 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
1133 if (! service->handle) {
\r
1134 print_message(stderr, NSSM_MESSAGE_CREATESERVICE_FAILED, error_string(GetLastError()));
\r
1135 CloseServiceHandle(services);
\r
1139 if (edit_service(service, false)) {
\r
1140 DeleteService(service->handle);
\r
1141 CloseServiceHandle(services);
\r
1145 print_message(stdout, NSSM_MESSAGE_SERVICE_INSTALLED, service->name);
\r
1148 CloseServiceHandle(services);
\r
1153 /* Edit the service. */
\r
1154 int edit_service(nssm_service_t *service, bool editing) {
\r
1155 if (! service) return 1;
\r
1158 The only two valid flags for service type are SERVICE_WIN32_OWN_PROCESS
\r
1159 and SERVICE_INTERACTIVE_PROCESS.
\r
1161 service->type &= SERVICE_INTERACTIVE_PROCESS;
\r
1162 service->type |= SERVICE_WIN32_OWN_PROCESS;
\r
1164 /* Startup type. */
\r
1165 unsigned long startup;
\r
1166 switch (service->startup) {
\r
1167 case NSSM_STARTUP_MANUAL: startup = SERVICE_DEMAND_START; break;
\r
1168 case NSSM_STARTUP_DISABLED: startup = SERVICE_DISABLED; break;
\r
1169 default: startup = SERVICE_AUTO_START;
\r
1172 /* Display name. */
\r
1173 if (! service->displayname[0]) _sntprintf_s(service->displayname, _countof(service->displayname), _TRUNCATE, _T("%s"), service->name);
\r
1176 Username must be NULL if we aren't changing or an account name.
\r
1177 We must explicitly use LOCALSYSTEM to change it when we are editing.
\r
1178 Password must be NULL if we aren't changing, a password or "".
\r
1179 Empty passwords are valid but we won't allow them in the GUI.
\r
1181 TCHAR *username = 0;
\r
1183 TCHAR *password = 0;
\r
1184 if (service->usernamelen) {
\r
1185 username = service->username;
\r
1186 if (canonicalise_username(username, &canon)) return 5;
\r
1187 if (service->passwordlen) password = service->password;
\r
1189 else if (editing) username = canon = NSSM_LOCALSYSTEM_ACCOUNT;
\r
1191 if (well_known_username(canon)) password = _T("");
\r
1193 if (grant_logon_as_service(canon)) {
\r
1194 if (canon != username) HeapFree(GetProcessHeap(), 0, canon);
\r
1195 print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username);
\r
1200 TCHAR *dependencies = _T("");
\r
1201 if (service->dependencieslen) dependencies = 0; /* Change later. */
\r
1203 if (! ChangeServiceConfig(service->handle, service->type, startup, SERVICE_NO_CHANGE, 0, 0, 0, dependencies, canon, password, service->displayname)) {
\r
1204 if (canon != username) HeapFree(GetProcessHeap(), 0, canon);
\r
1205 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
1208 if (canon != username) HeapFree(GetProcessHeap(), 0, canon);
\r
1210 if (service->dependencieslen) {
\r
1211 if (set_service_dependencies(service->name, service->handle, service->dependencies)) return 5;
\r
1214 if (service->description[0] || editing) {
\r
1215 set_service_description(service->name, service->handle, service->description);
\r
1218 SERVICE_DELAYED_AUTO_START_INFO delayed;
\r
1219 ZeroMemory(&delayed, sizeof(delayed));
\r
1220 if (service->startup == NSSM_STARTUP_DELAYED) delayed.fDelayedAutostart = 1;
\r
1221 else delayed.fDelayedAutostart = 0;
\r
1222 /* Delayed startup isn't supported until Vista. */
\r
1223 if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, &delayed)) {
\r
1224 unsigned long error = GetLastError();
\r
1225 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
\r
1226 if (error != ERROR_INVALID_LEVEL) {
\r
1227 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_DELAYED_AUTO_START_INFO_FAILED, service->name, error_string(error), 0);
\r
1231 /* Don't mess with parameters which aren't ours. */
\r
1232 if (! service->native) {
\r
1233 /* Now we need to put the parameters into the registry */
\r
1234 if (create_parameters(service, editing)) {
\r
1235 print_message(stderr, NSSM_MESSAGE_CREATE_PARAMETERS_FAILED);
\r
1239 set_service_recovery(service);
\r
1245 /* Control a service. */
\r
1246 int control_service(unsigned long control, int argc, TCHAR **argv) {
\r
1247 if (argc < 1) return usage(1);
\r
1248 TCHAR *service_name = argv[0];
\r
1249 TCHAR canonical_name[SERVICE_NAME_LENGTH];
\r
1251 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
\r
1253 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
1257 unsigned long access = SERVICE_QUERY_STATUS;
\r
1258 switch (control) {
\r
1259 case NSSM_SERVICE_CONTROL_START:
\r
1260 access |= SERVICE_START;
\r
1263 case SERVICE_CONTROL_CONTINUE:
\r
1264 case SERVICE_CONTROL_PAUSE:
\r
1265 access |= SERVICE_PAUSE_CONTINUE;
\r
1268 case SERVICE_CONTROL_STOP:
\r
1269 access |= SERVICE_STOP;
\r
1272 case NSSM_SERVICE_CONTROL_ROTATE:
\r
1273 access |= SERVICE_USER_DEFINED_CONTROL;
\r
1277 SC_HANDLE service_handle = open_service(services, service_name, access, canonical_name, _countof(canonical_name));
\r
1278 if (! service_handle) {
\r
1279 CloseServiceHandle(services);
\r
1284 unsigned long error;
\r
1285 SERVICE_STATUS service_status;
\r
1286 if (control == NSSM_SERVICE_CONTROL_START) {
\r
1287 unsigned long initial_status = SERVICE_STOPPED;
\r
1288 ret = StartService(service_handle, (unsigned long) argc, (const TCHAR **) argv);
\r
1289 error = GetLastError();
\r
1290 CloseServiceHandle(services);
\r
1292 if (error == ERROR_IO_PENDING) {
\r
1294 Older versions of Windows return immediately with ERROR_IO_PENDING
\r
1295 indicate that the operation is still in progress. Newer versions
\r
1296 will return it if there really is a delay.
\r
1299 error = ERROR_SUCCESS;
\r
1303 int response = await_service_control_response(control, service_handle, &service_status, initial_status);
\r
1304 CloseServiceHandle(service_handle);
\r
1307 print_message(stderr, NSSM_MESSAGE_BAD_CONTROL_RESPONSE, canonical_name, service_status_text(service_status.dwCurrentState), service_control_text(control));
\r
1310 else _tprintf(_T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1314 CloseServiceHandle(service_handle);
\r
1315 _ftprintf(stderr, _T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1319 else if (control == SERVICE_CONTROL_INTERROGATE) {
\r
1321 We could actually send an INTERROGATE control but that won't return
\r
1322 any information if the service is stopped and we don't care about
\r
1323 the extra details it might give us in any case. So we'll fake it.
\r
1325 ret = QueryServiceStatus(service_handle, &service_status);
\r
1326 error = GetLastError();
\r
1329 _tprintf(_T("%s\n"), service_status_text(service_status.dwCurrentState));
\r
1333 _ftprintf(stderr, _T("%s: %s\n"), canonical_name, error_string(error));
\r
1338 ret = ControlService(service_handle, control, &service_status);
\r
1339 unsigned long initial_status = service_status.dwCurrentState;
\r
1340 error = GetLastError();
\r
1341 CloseServiceHandle(services);
\r
1343 if (error == ERROR_IO_PENDING) {
\r
1345 error = ERROR_SUCCESS;
\r
1349 int response = await_service_control_response(control, service_handle, &service_status, initial_status);
\r
1350 CloseServiceHandle(service_handle);
\r
1353 print_message(stderr, NSSM_MESSAGE_BAD_CONTROL_RESPONSE, canonical_name, service_status_text(service_status.dwCurrentState), service_control_text(control));
\r
1356 else _tprintf(_T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1360 CloseServiceHandle(service_handle);
\r
1361 _ftprintf(stderr, _T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1362 if (error == ERROR_SERVICE_NOT_ACTIVE) {
\r
1363 if (control == SERVICE_CONTROL_SHUTDOWN || control == SERVICE_CONTROL_STOP) return 0;
\r
1370 /* Remove the service */
\r
1371 int remove_service(nssm_service_t *service) {
\r
1372 if (! service) return 1;
\r
1374 /* Open service manager */
\r
1375 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
\r
1377 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
1381 /* Try to open the service */
\r
1382 service->handle = open_service(services, service->name, DELETE, service->name, _countof(service->name));
\r
1383 if (! service->handle) {
\r
1384 CloseServiceHandle(services);
\r
1388 /* Get the canonical service name. We open it case insensitively. */
\r
1389 unsigned long bufsize = _countof(service->displayname);
\r
1390 GetServiceDisplayName(services, service->name, service->displayname, &bufsize);
\r
1391 bufsize = _countof(service->name);
\r
1392 GetServiceKeyName(services, service->displayname, service->name, &bufsize);
\r
1394 /* Try to delete the service */
\r
1395 if (! DeleteService(service->handle)) {
\r
1396 print_message(stderr, NSSM_MESSAGE_DELETESERVICE_FAILED);
\r
1397 CloseServiceHandle(services);
\r
1402 CloseServiceHandle(services);
\r
1404 print_message(stdout, NSSM_MESSAGE_SERVICE_REMOVED, service->name);
\r
1408 /* Service initialisation */
\r
1409 void WINAPI service_main(unsigned long argc, TCHAR **argv) {
\r
1410 nssm_service_t *service = alloc_nssm_service();
\r
1411 if (! service) return;
\r
1413 if (_sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]) < 0) {
\r
1414 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("service->name"), _T("service_main()"), 0);
\r
1418 /* We can use a condition variable in a critical section on Vista or later. */
\r
1419 if (imports.SleepConditionVariableCS && imports.WakeConditionVariable) use_critical_section = true;
\r
1420 else use_critical_section = false;
\r
1422 /* Initialise status */
\r
1423 ZeroMemory(&service->status, sizeof(service->status));
\r
1424 service->status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;
\r
1425 service->status.dwControlsAccepted = 0;
\r
1426 service->status.dwWin32ExitCode = NO_ERROR;
\r
1427 service->status.dwServiceSpecificExitCode = 0;
\r
1428 service->status.dwCheckPoint = 0;
\r
1429 service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;
\r
1431 /* Signal we AREN'T running the server */
\r
1432 service->process_handle = 0;
\r
1435 /* Register control handler */
\r
1436 service->status_handle = RegisterServiceCtrlHandlerEx(NSSM, service_control_handler, (void *) service);
\r
1437 if (! service->status_handle) {
\r
1438 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_REGISTERSERVICECTRLHANDER_FAILED, error_string(GetLastError()), 0);
\r
1442 log_service_control(service->name, 0, true);
\r
1444 service->status.dwCurrentState = SERVICE_START_PENDING;
\r
1445 service->status.dwWaitHint = service->throttle_delay + NSSM_WAITHINT_MARGIN;
\r
1446 SetServiceStatus(service->status_handle, &service->status);
\r
1449 /* Try to create the exit action parameters; we don't care if it fails */
\r
1450 create_exit_action(service->name, exit_action_strings[0], false);
\r
1452 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT);
\r
1454 service->handle = open_service(services, service->name, SERVICE_CHANGE_CONFIG, 0, 0);
\r
1455 set_service_recovery(service);
\r
1457 /* Remember our display name. */
\r
1458 unsigned long displayname_len = _countof(service->displayname);
\r
1459 GetServiceDisplayName(services, service->name, service->displayname, &displayname_len);
\r
1461 CloseServiceHandle(services);
\r
1465 /* Used for signalling a resume if the service pauses when throttled. */
\r
1466 if (use_critical_section) {
\r
1467 InitializeCriticalSection(&service->throttle_section);
\r
1468 service->throttle_section_initialised = true;
\r
1471 service->throttle_timer = CreateWaitableTimer(0, 1, 0);
\r
1472 if (! service->throttle_timer) {
\r
1473 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_CREATEWAITABLETIMER_FAILED, service->name, error_string(GetLastError()), 0);
\r
1477 /* Critical section for hooks. */
\r
1478 InitializeCriticalSection(&service->hook_section);
\r
1479 service->hook_section_initialised = true;
\r
1481 /* Remember our initial environment. */
\r
1482 service->initial_env = copy_environment();
\r
1484 /* Remember our creation time. */
\r
1485 if (get_process_creation_time(GetCurrentProcess(), &service->nssm_creation_time)) ZeroMemory(&service->nssm_creation_time, sizeof(service->nssm_creation_time));
\r
1487 service->allow_restart = true;
\r
1488 if (! CreateThread(NULL, 0, launch_service, (void *) service, 0, NULL)) {
\r
1489 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETHREAD_FAILED, error_string(GetLastError()), 0);
\r
1490 stop_service(service, 0, true, true);
\r
1494 /* Make sure service recovery actions are taken where necessary */
\r
1495 void set_service_recovery(nssm_service_t *service) {
\r
1496 SERVICE_FAILURE_ACTIONS_FLAG flag;
\r
1497 ZeroMemory(&flag, sizeof(flag));
\r
1498 flag.fFailureActionsOnNonCrashFailures = true;
\r
1500 /* This functionality was added in Vista so the call may fail */
\r
1501 if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, &flag)) {
\r
1502 unsigned long error = GetLastError();
\r
1503 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
\r
1504 if (error != ERROR_INVALID_LEVEL) {
\r
1505 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_FAILURE_ACTIONS_FAILED, service->name, error_string(error), 0);
\r
1510 int monitor_service(nssm_service_t *service) {
\r
1511 /* Set service status to started */
\r
1512 int ret = start_service(service);
\r
1515 _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%d"), ret);
\r
1516 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_START_SERVICE_FAILED, service->exe, service->name, ret, 0);
\r
1519 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_STARTED_SERVICE, service->exe, service->flags, service->name, service->dir, 0);
\r
1521 /* Monitor service */
\r
1522 if (! RegisterWaitForSingleObject(&service->wait_handle, service->process_handle, end_service, (void *) service, INFINITE, WT_EXECUTEONLYONCE | WT_EXECUTELONGFUNCTION)) {
\r
1523 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_REGISTERWAITFORSINGLEOBJECT_FAILED, service->name, service->exe, error_string(GetLastError()), 0);
\r
1529 TCHAR *service_control_text(unsigned long control) {
\r
1530 switch (control) {
\r
1531 /* HACK: there is no SERVICE_CONTROL_START constant */
\r
1532 case NSSM_SERVICE_CONTROL_START: return _T("START");
\r
1533 case SERVICE_CONTROL_STOP: return _T("STOP");
\r
1534 case SERVICE_CONTROL_SHUTDOWN: return _T("SHUTDOWN");
\r
1535 case SERVICE_CONTROL_PAUSE: return _T("PAUSE");
\r
1536 case SERVICE_CONTROL_CONTINUE: return _T("CONTINUE");
\r
1537 case SERVICE_CONTROL_INTERROGATE: return _T("INTERROGATE");
\r
1538 case NSSM_SERVICE_CONTROL_ROTATE: return _T("ROTATE");
\r
1539 case SERVICE_CONTROL_POWEREVENT: return _T("POWEREVENT");
\r
1540 default: return 0;
\r
1544 TCHAR *service_status_text(unsigned long status) {
\r
1546 case SERVICE_STOPPED: return _T("SERVICE_STOPPED");
\r
1547 case SERVICE_START_PENDING: return _T("SERVICE_START_PENDING");
\r
1548 case SERVICE_STOP_PENDING: return _T("SERVICE_STOP_PENDING");
\r
1549 case SERVICE_RUNNING: return _T("SERVICE_RUNNING");
\r
1550 case SERVICE_CONTINUE_PENDING: return _T("SERVICE_CONTINUE_PENDING");
\r
1551 case SERVICE_PAUSE_PENDING: return _T("SERVICE_PAUSE_PENDING");
\r
1552 case SERVICE_PAUSED: return _T("SERVICE_PAUSED");
\r
1553 default: return 0;
\r
1557 void log_service_control(TCHAR *service_name, unsigned long control, bool handled) {
\r
1558 TCHAR *text = service_control_text(control);
\r
1559 unsigned long event;
\r
1562 /* "0x" + 8 x hex + NULL */
\r
1563 text = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, 11 * sizeof(TCHAR));
\r
1565 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);
\r
1568 if (_sntprintf_s(text, 11, _TRUNCATE, _T("0x%08x"), control) < 0) {
\r
1569 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);
\r
1570 HeapFree(GetProcessHeap(), 0, text);
\r
1574 event = NSSM_EVENT_SERVICE_CONTROL_UNKNOWN;
\r
1576 else if (handled) event = NSSM_EVENT_SERVICE_CONTROL_HANDLED;
\r
1577 else event = NSSM_EVENT_SERVICE_CONTROL_NOT_HANDLED;
\r
1579 log_event(EVENTLOG_INFORMATION_TYPE, event, service_name, text, 0);
\r
1581 if (event == NSSM_EVENT_SERVICE_CONTROL_UNKNOWN) {
\r
1582 HeapFree(GetProcessHeap(), 0, text);
\r
1586 /* Service control handler */
\r
1587 unsigned long WINAPI service_control_handler(unsigned long control, unsigned long event, void *data, void *context) {
\r
1588 nssm_service_t *service = (nssm_service_t *) context;
\r
1590 switch (control) {
\r
1591 case SERVICE_CONTROL_INTERROGATE:
\r
1592 /* We always keep the service status up-to-date so this is a no-op. */
\r
1595 case SERVICE_CONTROL_SHUTDOWN:
\r
1596 case SERVICE_CONTROL_STOP:
\r
1597 service->last_control = control;
\r
1598 log_service_control(service->name, control, true);
\r
1600 /* Immediately block further controls. */
\r
1601 service->allow_restart = false;
\r
1602 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
1603 service->status.dwControlsAccepted = 0;
\r
1604 SetServiceStatus(service->status_handle, &service->status);
\r
1606 /* Pre-stop hook. */
\r
1607 nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_STOP, NSSM_HOOK_ACTION_PRE, &control, NSSM_SERVICE_STATUS_DEADLINE, false);
\r
1610 We MUST acknowledge the stop request promptly but we're committed to
\r
1611 waiting for the application to exit. Spawn a new thread to wait
\r
1612 while we acknowledge the request.
\r
1614 if (! CreateThread(NULL, 0, shutdown_service, context, 0, NULL)) {
\r
1615 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETHREAD_FAILED, error_string(GetLastError()), 0);
\r
1618 We couldn't create a thread to tidy up so we'll have to force the tidyup
\r
1619 to complete in time in this thread.
\r
1621 service->kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;
\r
1622 service->kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;
\r
1623 service->kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;
\r
1625 stop_service(service, 0, true, true);
\r
1629 case SERVICE_CONTROL_CONTINUE:
\r
1630 service->last_control = control;
\r
1631 log_service_control(service->name, control, true);
\r
1632 service->throttle = 0;
\r
1633 if (use_critical_section) imports.WakeConditionVariable(&service->throttle_condition);
\r
1635 if (! service->throttle_timer) return ERROR_CALL_NOT_IMPLEMENTED;
\r
1636 ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));
\r
1637 SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);
\r
1639 /* We can't continue if the application is running! */
\r
1640 if (! service->process_handle) service->status.dwCurrentState = SERVICE_CONTINUE_PENDING;
\r
1641 service->status.dwWaitHint = throttle_milliseconds(service->throttle) + NSSM_WAITHINT_MARGIN;
\r
1642 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESET_THROTTLE, service->name, 0);
\r
1643 SetServiceStatus(service->status_handle, &service->status);
\r
1646 case SERVICE_CONTROL_PAUSE:
\r
1648 We don't accept pause messages but it isn't possible to register
\r
1649 only for continue messages so we have to handle this case.
\r
1651 log_service_control(service->name, control, false);
\r
1652 return ERROR_CALL_NOT_IMPLEMENTED;
\r
1654 case NSSM_SERVICE_CONTROL_ROTATE:
\r
1655 service->last_control = control;
\r
1656 log_service_control(service->name, control, true);
\r
1657 (void) nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_ROTATE, NSSM_HOOK_ACTION_PRE, &control, NSSM_HOOK_DEADLINE, false);
\r
1658 if (service->rotate_stdout_online == NSSM_ROTATE_ONLINE) service->rotate_stdout_online = NSSM_ROTATE_ONLINE_ASAP;
\r
1659 if (service->rotate_stderr_online == NSSM_ROTATE_ONLINE) service->rotate_stderr_online = NSSM_ROTATE_ONLINE_ASAP;
\r
1660 (void) nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_ROTATE, NSSM_HOOK_ACTION_POST, &control);
\r
1663 case SERVICE_CONTROL_POWEREVENT:
\r
1664 /* Resume from suspend. */
\r
1665 if (event == PBT_APMRESUMEAUTOMATIC) {
\r
1666 service->last_control = control;
\r
1667 log_service_control(service->name, control, true);
\r
1668 (void) nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_POWER, NSSM_HOOK_ACTION_RESUME, &control);
\r
1672 /* Battery low or changed to A/C power or something. */
\r
1673 if (event == PBT_APMPOWERSTATUSCHANGE) {
\r
1674 service->last_control = control;
\r
1675 log_service_control(service->name, control, true);
\r
1676 (void) nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_POWER, NSSM_HOOK_ACTION_CHANGE, &control);
\r
1679 log_service_control(service->name, control, false);
\r
1683 /* Unknown control */
\r
1684 log_service_control(service->name, control, false);
\r
1685 return ERROR_CALL_NOT_IMPLEMENTED;
\r
1688 /* Start the service */
\r
1689 int start_service(nssm_service_t *service) {
\r
1690 service->stopping = false;
\r
1692 if (service->process_handle) return 0;
\r
1693 service->start_requested_count++;
\r
1695 /* Allocate a STARTUPINFO structure for a new process */
\r
1697 ZeroMemory(&si, sizeof(si));
\r
1698 si.cb = sizeof(si);
\r
1700 /* Allocate a PROCESSINFO structure for the process */
\r
1701 PROCESS_INFORMATION pi;
\r
1702 ZeroMemory(&pi, sizeof(pi));
\r
1704 /* Get startup parameters */
\r
1705 int ret = get_parameters(service, &si);
\r
1707 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_PARAMETERS_FAILED, service->name, 0);
\r
1708 unset_service_environment(service);
\r
1709 return stop_service(service, 2, true, true);
\r
1712 /* Launch executable with arguments */
\r
1713 TCHAR cmd[CMD_LENGTH];
\r
1714 if (_sntprintf_s(cmd, _countof(cmd), _TRUNCATE, _T("\"%s\" %s"), service->exe, service->flags) < 0) {
\r
1715 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("command line"), _T("start_service"), 0);
\r
1716 unset_service_environment(service);
\r
1717 return stop_service(service, 2, true, true);
\r
1720 throttle_restart(service);
\r
1722 service->status.dwCurrentState = SERVICE_START_PENDING;
\r
1723 service->status.dwControlsAccepted = SERVICE_ACCEPT_POWEREVENT | SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP;
\r
1724 SetServiceStatus(service->status_handle, &service->status);
\r
1726 /* Pre-start hook. */
\r
1727 unsigned long control = NSSM_SERVICE_CONTROL_START;
\r
1728 if (nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_START, NSSM_HOOK_ACTION_PRE, &control, NSSM_SERVICE_STATUS_DEADLINE, false) == NSSM_HOOK_STATUS_ABORT) {
\r
1730 _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), NSSM_HOOK_STATUS_ABORT);
\r
1731 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_PRESTART_HOOK_ABORT, NSSM_HOOK_EVENT_START, NSSM_HOOK_ACTION_PRE, service->name, code, 0);
\r
1732 unset_service_environment(service);
\r
1733 return stop_service(service, 5, true, true);
\r
1736 /* Did another thread receive a stop control? */
\r
1737 if (service->allow_restart) {
\r
1738 /* Set up I/O redirection. */
\r
1739 if (get_output_handles(service, &si)) {
\r
1740 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_OUTPUT_HANDLES_FAILED, service->name, 0);
\r
1741 if (! service->no_console) FreeConsole();
\r
1742 close_output_handles(&si);
\r
1743 unset_service_environment(service);
\r
1744 return stop_service(service, 4, true, true);
\r
1747 /* The pre-start hook will have cleaned the environment. */
\r
1748 set_service_environment(service);
\r
1750 bool inherit_handles = false;
\r
1751 if (si.dwFlags & STARTF_USESTDHANDLES) inherit_handles = true;
\r
1752 unsigned long flags = service->priority & priority_mask();
\r
1753 if (service->affinity) flags |= CREATE_SUSPENDED;
\r
1754 if (! CreateProcess(0, cmd, 0, 0, inherit_handles, flags, 0, service->dir, &si, &pi)) {
\r
1755 unsigned long exitcode = 3;
\r
1756 unsigned long error = GetLastError();
\r
1757 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service->name, service->exe, error_string(error), 0);
\r
1758 close_output_handles(&si);
\r
1759 unset_service_environment(service);
\r
1760 return stop_service(service, exitcode, true, true);
\r
1762 service->start_count++;
\r
1763 service->process_handle = pi.hProcess;
\r
1764 service->pid = pi.dwProcessId;
\r
1766 if (get_process_creation_time(service->process_handle, &service->creation_time)) ZeroMemory(&service->creation_time, sizeof(service->creation_time));
\r
1768 close_output_handles(&si);
\r
1770 if (! service->no_console) FreeConsole();
\r
1772 if (service->affinity) {
\r
1774 We are explicitly storing service->affinity as a 64-bit unsigned integer
\r
1775 so that we can parse it regardless of whether we're running in 32-bit
\r
1776 or 64-bit mode. The arguments to SetProcessAffinityMask(), however, are
\r
1777 defined as type DWORD_PTR and hence limited to 32 bits on a 32-bit system
\r
1778 (or when running the 32-bit NSSM).
\r
1780 The result is a lot of seemingly-unnecessary casting throughout the code
\r
1781 and potentially confusion when we actually try to start the service.
\r
1782 Having said that, however, it's unlikely that we're actually going to
\r
1783 run in 32-bit mode on a system which has more than 32 CPUs so the
\r
1784 likelihood of seeing a confusing situation is somewhat diminished.
\r
1786 DWORD_PTR affinity, system_affinity;
\r
1788 if (GetProcessAffinityMask(service->process_handle, &affinity, &system_affinity)) affinity = service->affinity & system_affinity;
\r
1790 affinity = (DWORD_PTR) service->affinity;
\r
1791 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GETPROCESSAFFINITYMASK_FAILED, service->name, error_string(GetLastError()), 0);
\r
1794 if (! SetProcessAffinityMask(service->process_handle, affinity)) {
\r
1795 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_SETPROCESSAFFINITYMASK_FAILED, service->name, error_string(GetLastError()), 0);
\r
1798 ResumeThread(pi.hThread);
\r
1802 /* Restore our environment. */
\r
1803 unset_service_environment(service);
\r
1806 Wait for a clean startup before changing the service status to RUNNING
\r
1807 but be mindful of the fact that we are blocking the service control manager
\r
1808 so abandon the wait before too much time has elapsed.
\r
1810 if (await_single_handle(service->status_handle, &service->status, service->process_handle, service->name, _T("start_service"), service->throttle_delay) == 1) service->throttle = 0;
\r
1812 /* Did another thread receive a stop control? */
\r
1813 if (! service->allow_restart) return 0;
\r
1815 /* Signal successful start */
\r
1816 service->status.dwCurrentState = SERVICE_RUNNING;
\r
1817 service->status.dwControlsAccepted &= ~SERVICE_ACCEPT_PAUSE_CONTINUE;
\r
1818 SetServiceStatus(service->status_handle, &service->status);
\r
1820 /* Post-start hook. */
\r
1821 if (! service->throttle) {
\r
1822 (void) nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_START, NSSM_HOOK_ACTION_POST, &control);
\r
1825 /* Ensure the restart delay is always applied. */
\r
1826 if (service->restart_delay && ! service->throttle) service->throttle++;
\r
1831 /* Stop the service */
\r
1832 int stop_service(nssm_service_t *service, unsigned long exitcode, bool graceful, bool default_action) {
\r
1833 service->allow_restart = false;
\r
1834 if (service->wait_handle) {
\r
1835 UnregisterWait(service->wait_handle);
\r
1836 service->wait_handle = 0;
\r
1839 service->rotate_stdout_online = service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;
\r
1841 if (default_action && ! exitcode && ! graceful) {
\r
1842 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
1846 /* Signal we are stopping */
\r
1848 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
1849 service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;
\r
1850 SetServiceStatus(service->status_handle, &service->status);
\r
1853 /* Nothing to do if service isn't running */
\r
1854 if (service->pid) {
\r
1855 /* Shut down service */
\r
1856 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_TERMINATEPROCESS, service->name, service->exe, 0);
\r
1858 service_kill_t(service, &k);
\r
1862 else log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_PROCESS_ALREADY_STOPPED, service->name, service->exe, 0);
\r
1864 end_service((void *) service, true);
\r
1866 /* Signal we stopped */
\r
1868 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
1869 wait_for_hooks(service, true);
\r
1870 service->status.dwCurrentState = SERVICE_STOPPED;
\r
1872 service->status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
\r
1873 service->status.dwServiceSpecificExitCode = exitcode;
\r
1876 service->status.dwWin32ExitCode = NO_ERROR;
\r
1877 service->status.dwServiceSpecificExitCode = 0;
\r
1879 SetServiceStatus(service->status_handle, &service->status);
\r
1885 /* Callback function triggered when the server exits */
\r
1886 void CALLBACK end_service(void *arg, unsigned char why) {
\r
1887 nssm_service_t *service = (nssm_service_t *) arg;
\r
1889 if (service->stopping) return;
\r
1891 service->stopping = true;
\r
1893 service->rotate_stdout_online = service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;
\r
1895 /* Use now as a dummy exit time. */
\r
1896 GetSystemTimeAsFileTime(&service->exit_time);
\r
1898 /* Check exit code */
\r
1899 unsigned long exitcode = 0;
\r
1901 if (service->process_handle) {
\r
1902 GetExitCodeProcess(service->process_handle, &exitcode);
\r
1903 service->exitcode = exitcode;
\r
1904 /* Check real exit time. */
\r
1905 if (exitcode != STILL_ACTIVE) get_process_exit_time(service->process_handle, &service->exit_time);
\r
1906 CloseHandle(service->process_handle);
\r
1909 service->process_handle = 0;
\r
1912 Log that the service ended BEFORE logging about killing the process
\r
1913 tree. See below for the possible values of the why argument.
\r
1916 _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), exitcode);
\r
1917 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ENDED_SERVICE, service->exe, service->name, code, 0);
\r
1921 if (exitcode == STILL_ACTIVE) exitcode = 0;
\r
1922 if (service->pid && service->kill_process_tree) {
\r
1924 service_kill_t(service, &k);
\r
1925 kill_process_tree(&k, service->pid);
\r
1930 service->exit_count++;
\r
1931 (void) nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_EXIT, NSSM_HOOK_ACTION_POST, NULL, NSSM_HOOK_DEADLINE, true);
\r
1934 The why argument is true if our wait timed out or false otherwise.
\r
1935 Our wait is infinite so why will never be true when called by the system.
\r
1936 If it is indeed true, assume we were called from stop_service() because
\r
1937 this is a controlled shutdown, and don't take any restart action.
\r
1940 if (! service->allow_restart) return;
\r
1942 /* What action should we take? */
\r
1943 int action = NSSM_EXIT_RESTART;
\r
1944 TCHAR action_string[ACTION_LEN];
\r
1945 bool default_action;
\r
1946 if (! get_exit_action(service->name, &exitcode, action_string, &default_action)) {
\r
1947 for (int i = 0; exit_action_strings[i]; i++) {
\r
1948 if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
1956 /* Try to restart the service or return failure code to service manager */
\r
1957 case NSSM_EXIT_RESTART:
\r
1958 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_RESTART, service->name, code, exit_action_strings[action], service->exe, 0);
\r
1959 while (monitor_service(service)) {
\r
1960 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_RESTART_SERVICE_FAILED, service->exe, service->name, 0);
\r
1965 /* Do nothing, just like srvany would */
\r
1966 case NSSM_EXIT_IGNORE:
\r
1967 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_IGNORE, service->name, code, exit_action_strings[action], service->exe, 0);
\r
1968 wait_for_hooks(service, false);
\r
1972 /* Tell the service manager we are finished */
\r
1973 case NSSM_EXIT_REALLY:
\r
1974 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_REALLY, service->name, code, exit_action_strings[action], 0);
\r
1975 stop_service(service, exitcode, true, default_action);
\r
1978 /* Fake a crash so pre-Vista service managers will run recovery actions. */
\r
1979 case NSSM_EXIT_UNCLEAN:
\r
1980 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_UNCLEAN, service->name, code, exit_action_strings[action], 0);
\r
1981 stop_service(service, exitcode, false, default_action);
\r
1982 wait_for_hooks(service, false);
\r
1989 void throttle_restart(nssm_service_t *service) {
\r
1990 /* This can't be a restart if the service is already running. */
\r
1991 if (! service->throttle++) return;
\r
1994 unsigned long throttle_ms = throttle_milliseconds(service->throttle);
\r
1995 TCHAR threshold[8], milliseconds[8];
\r
1997 if (service->restart_delay > throttle_ms) ms = service->restart_delay;
\r
1998 else ms = throttle_ms;
\r
2000 _sntprintf_s(milliseconds, _countof(milliseconds), _TRUNCATE, _T("%lu"), ms);
\r
2002 if (service->throttle == 1 && service->restart_delay > throttle_ms) log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESTART_DELAY, service->name, milliseconds, 0);
\r
2004 _sntprintf_s(threshold, _countof(threshold), _TRUNCATE, _T("%lu"), service->throttle_delay);
\r
2005 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_THROTTLED, service->name, threshold, milliseconds, 0);
\r
2008 if (use_critical_section) EnterCriticalSection(&service->throttle_section);
\r
2009 else if (service->throttle_timer) {
\r
2010 ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));
\r
2011 service->throttle_duetime.QuadPart = 0 - (ms * 10000LL);
\r
2012 SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);
\r
2015 service->status.dwCurrentState = SERVICE_PAUSED;
\r
2016 service->status.dwControlsAccepted |= SERVICE_ACCEPT_PAUSE_CONTINUE;
\r
2017 SetServiceStatus(service->status_handle, &service->status);
\r
2019 if (use_critical_section) {
\r
2020 imports.SleepConditionVariableCS(&service->throttle_condition, &service->throttle_section, ms);
\r
2021 LeaveCriticalSection(&service->throttle_section);
\r
2024 if (service->throttle_timer) WaitForSingleObject(service->throttle_timer, INFINITE);
\r
2030 When responding to a stop (or any other) request we need to set dwWaitHint to
\r
2031 the number of milliseconds we expect the operation to take, and optionally
\r
2032 increase dwCheckPoint. If dwWaitHint milliseconds elapses without the
\r
2033 operation completing or dwCheckPoint increasing, the system will consider the
\r
2034 service to be hung.
\r
2036 However the system will consider the service to be hung after 30000
\r
2037 milliseconds regardless of the value of dwWaitHint if dwCheckPoint has not
\r
2038 changed. Therefore if we want to wait longer than that we must periodically
\r
2039 increase dwCheckPoint.
\r
2041 Furthermore, it will consider the service to be hung after 60000 milliseconds
\r
2042 regardless of the value of dwCheckPoint unless dwWaitHint is increased every
\r
2043 time dwCheckPoint is also increased.
\r
2045 Our strategy then is to retrieve the initial dwWaitHint and wait for
\r
2046 NSSM_SERVICE_STATUS_DEADLINE milliseconds. If the process is still running
\r
2047 and we haven't finished waiting we increment dwCheckPoint and add whichever is
\r
2048 smaller of NSSM_SERVICE_STATUS_DEADLINE or the remaining timeout to
\r
2051 Only doing both these things will prevent the system from killing the service.
\r
2053 If the status_handle and service_status arguments are omitted, this function
\r
2054 will not try to update the service manager but it will still log to the
\r
2055 event log that it is waiting for a handle.
\r
2057 Returns: 1 if the wait timed out.
\r
2058 0 if the wait completed.
\r
2061 int await_single_handle(SERVICE_STATUS_HANDLE status_handle, SERVICE_STATUS *status, HANDLE handle, TCHAR *name, TCHAR *function_name, unsigned long timeout) {
\r
2062 unsigned long interval;
\r
2063 unsigned long ret;
\r
2064 unsigned long waited;
\r
2065 TCHAR interval_milliseconds[16];
\r
2066 TCHAR timeout_milliseconds[16];
\r
2067 TCHAR waited_milliseconds[16];
\r
2068 TCHAR *function = function_name;
\r
2070 /* Add brackets to function name. */
\r
2071 size_t funclen = _tcslen(function_name) + 3;
\r
2072 TCHAR *func = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, funclen * sizeof(TCHAR));
\r
2074 if (_sntprintf_s(func, funclen, _TRUNCATE, _T("%s()"), function_name) > -1) function = func;
\r
2077 _sntprintf_s(timeout_milliseconds, _countof(timeout_milliseconds), _TRUNCATE, _T("%lu"), timeout);
\r
2080 while (waited < timeout) {
\r
2081 interval = timeout - waited;
\r
2082 if (interval > NSSM_SERVICE_STATUS_DEADLINE) interval = NSSM_SERVICE_STATUS_DEADLINE;
\r
2085 status->dwWaitHint += interval;
\r
2086 status->dwCheckPoint++;
\r
2087 SetServiceStatus(status_handle, status);
\r
2091 _sntprintf_s(waited_milliseconds, _countof(waited_milliseconds), _TRUNCATE, _T("%lu"), waited);
\r
2092 _sntprintf_s(interval_milliseconds, _countof(interval_milliseconds), _TRUNCATE, _T("%lu"), interval);
\r
2093 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_AWAITING_SINGLE_HANDLE, function, name, waited_milliseconds, interval_milliseconds, timeout_milliseconds, 0);
\r
2096 switch (WaitForSingleObject(handle, interval)) {
\r
2097 case WAIT_OBJECT_0:
\r
2101 case WAIT_TIMEOUT:
\r
2110 waited += interval;
\r
2114 if (func) HeapFree(GetProcessHeap(), 0, func);
\r