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 /* WILL NOT allocate a new string if the identifier is already present. */
\r
433 int prepend_service_group_identifier(TCHAR *group, TCHAR **canon) {
\r
434 if (! group || ! group[0] || group[0] == SC_GROUP_IDENTIFIER) {
\r
439 size_t len = _tcslen(group) + 1;
\r
440 *canon = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(TCHAR));
\r
442 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("canon"), _T("prepend_service_group_identifier()"));
\r
447 *s++ = SC_GROUP_IDENTIFIER;
\r
448 memmove(s, group, len * sizeof(TCHAR));
\r
449 (*canon)[len] = _T('\0');
\r
454 int append_to_dependencies(TCHAR *dependencies, unsigned long dependencieslen, TCHAR *string, TCHAR **newdependencies, unsigned long *newlen, int type) {
\r
458 if (type == DEPENDENCY_GROUPS) {
\r
459 if (prepend_service_group_identifier(string, &canon)) return 1;
\r
461 else canon = string;
\r
462 int ret = append_to_double_null(dependencies, dependencieslen, newdependencies, newlen, canon, 0, false);
\r
463 if (canon && canon != string) HeapFree(GetProcessHeap(), 0, canon);
\r
468 int remove_from_dependencies(TCHAR *dependencies, unsigned long dependencieslen, TCHAR *string, TCHAR **newdependencies, unsigned long *newlen, int type) {
\r
472 if (type == DEPENDENCY_GROUPS) {
\r
473 if (prepend_service_group_identifier(string, &canon)) return 1;
\r
475 else canon = string;
\r
476 int ret = remove_from_double_null(dependencies, dependencieslen, newdependencies, newlen, canon, 0, false);
\r
477 if (canon && canon != string) HeapFree(GetProcessHeap(), 0, canon);
\r
482 int set_service_dependencies(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR *buffer) {
\r
483 TCHAR *dependencies = _T("");
\r
484 unsigned long num_dependencies = 0;
\r
486 if (buffer && buffer[0]) {
\r
487 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
\r
489 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
494 Count the dependencies then allocate a buffer big enough for their
\r
495 canonical names, ie n * SERVICE_NAME_LENGTH.
\r
499 for (s = buffer; *s; s++) {
\r
500 num_dependencies++;
\r
501 if (*s == SC_GROUP_IDENTIFIER) groups = s;
\r
505 /* At least one dependency is a group so we need to verify them. */
\r
508 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, NSSM_REGISTRY_GROUPS, 0, KEY_READ, &key)) {
\r
509 _ftprintf(stderr, _T("%s: %s\n"), NSSM_REGISTRY_GROUPS, error_string(GetLastError()));
\r
513 unsigned long type;
\r
514 unsigned long groupslen;
\r
515 unsigned long ret = RegQueryValueEx(key, NSSM_REG_GROUPS, 0, &type, NULL, &groupslen);
\r
516 if (ret == ERROR_SUCCESS) {
\r
517 groups = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, groupslen);
\r
519 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("groups"), _T("set_service_dependencies()"));
\r
523 ret = RegQueryValueEx(key, NSSM_REG_GROUPS, 0, &type, (unsigned char *) groups, &groupslen);
\r
524 if (ret != ERROR_SUCCESS) {
\r
525 _ftprintf(stderr, _T("%s\\%s: %s"), NSSM_REGISTRY_GROUPS, NSSM_REG_GROUPS, error_string(GetLastError()));
\r
526 HeapFree(GetProcessHeap(), 0, groups);
\r
531 else if (ret != ERROR_FILE_NOT_FOUND) {
\r
532 _ftprintf(stderr, _T("%s\\%s: %s"), NSSM_REGISTRY_GROUPS, NSSM_REG_GROUPS, error_string(GetLastError()));
\r
541 unsigned long dependencieslen = (num_dependencies * SERVICE_NAME_LENGTH) + 2;
\r
542 dependencies = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dependencieslen * sizeof(TCHAR));
\r
545 TCHAR dependency[SERVICE_NAME_LENGTH];
\r
546 for (s = buffer; *s; s++) {
\r
548 if (*s == SC_GROUP_IDENTIFIER) {
\r
549 TCHAR *group = s + 1;
\r
553 for (TCHAR *g = groups; *g; g++) {
\r
554 if (str_equiv(g, group)) {
\r
556 /* Set canonical name. */
\r
557 memmove(group, g, _tcslen(g) * sizeof(TCHAR));
\r
565 if (ok) _sntprintf_s(dependency, _countof(dependency), _TRUNCATE, _T("%s"), s);
\r
567 HeapFree(GetProcessHeap(), 0, dependencies);
\r
568 if (groups) HeapFree(GetProcessHeap(), 0, groups);
\r
569 _ftprintf(stderr, _T("%s: %s"), s, error_string(ERROR_SERVICE_DEPENDENCY_DELETED));
\r
574 SC_HANDLE dependency_handle = open_service(services, s, SERVICE_QUERY_STATUS, dependency, _countof(dependency));
\r
575 if (! dependency_handle) {
\r
576 HeapFree(GetProcessHeap(), 0, dependencies);
\r
577 if (groups) HeapFree(GetProcessHeap(), 0, groups);
\r
578 CloseServiceHandle(services);
\r
579 _ftprintf(stderr, _T("%s: %s"), s, error_string(ERROR_SERVICE_DEPENDENCY_DELETED));
\r
584 size_t len = _tcslen(dependency) + 1;
\r
585 memmove(dependencies + i, dependency, len * sizeof(TCHAR));
\r
591 if (groups) HeapFree(GetProcessHeap(), 0, groups);
\r
592 CloseServiceHandle(services);
\r
595 if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, dependencies, 0, 0, 0)) {
\r
596 if (num_dependencies) HeapFree(GetProcessHeap(), 0, dependencies);
\r
597 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
601 if (num_dependencies) HeapFree(GetProcessHeap(), 0, dependencies);
\r
605 int get_service_dependencies(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR **buffer, unsigned long *bufsize, int type) {
\r
606 if (! buffer) return 1;
\r
607 if (! bufsize) return 2;
\r
612 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
\r
613 if (! qsc) return 3;
\r
615 if (! qsc->lpDependencies || ! qsc->lpDependencies[0]) {
\r
616 HeapFree(GetProcessHeap(), 0, qsc);
\r
620 /* lpDependencies is doubly NULL terminated. */
\r
621 while (qsc->lpDependencies[*bufsize]) {
\r
622 while (qsc->lpDependencies[*bufsize]) ++*bufsize;
\r
628 *buffer = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *bufsize * sizeof(TCHAR));
\r
631 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("lpDependencies"), _T("get_service_dependencies()"));
\r
632 HeapFree(GetProcessHeap(), 0, qsc);
\r
636 if (type == DEPENDENCY_ALL) memmove(*buffer, qsc->lpDependencies, *bufsize * sizeof(TCHAR));
\r
641 for (s = qsc->lpDependencies; *s; s++) {
\r
642 /* Only copy the appropriate type of dependency. */
\r
643 if ((*s == SC_GROUP_IDENTIFIER && type & DEPENDENCY_GROUPS) || (*s != SC_GROUP_IDENTIFIER && type & DEPENDENCY_SERVICES)) {
\r
644 size_t len = _tcslen(s) + 1;
\r
645 *bufsize += (unsigned long) len;
\r
646 memmove(*buffer + i, s, len * sizeof(TCHAR));
\r
655 HeapFree(GetProcessHeap(), 0, qsc);
\r
657 if (! *buffer[0]) {
\r
658 HeapFree(GetProcessHeap(), 0, *buffer);
\r
666 int get_service_dependencies(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR **buffer, unsigned long *bufsize) {
\r
667 return get_service_dependencies(service_name, service_handle, buffer, bufsize, DEPENDENCY_ALL);
\r
670 int set_service_description(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR *buffer) {
\r
671 SERVICE_DESCRIPTION description;
\r
672 ZeroMemory(&description, sizeof(description));
\r
674 lpDescription must be NULL if we aren't changing, the new description
\r
677 if (buffer && buffer[0]) description.lpDescription = buffer;
\r
678 else description.lpDescription = _T("");
\r
680 if (ChangeServiceConfig2(service_handle, SERVICE_CONFIG_DESCRIPTION, &description)) return 0;
\r
682 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_DESCRIPTION_FAILED, service_name, error_string(GetLastError()), 0);
\r
686 int get_service_description(const TCHAR *service_name, SC_HANDLE service_handle, unsigned long len, TCHAR *buffer) {
\r
687 if (! buffer) return 1;
\r
689 unsigned long bufsize;
\r
690 QueryServiceConfig2(service_handle, SERVICE_CONFIG_DESCRIPTION, 0, 0, &bufsize);
\r
691 unsigned long error = GetLastError();
\r
692 if (error == ERROR_INSUFFICIENT_BUFFER) {
\r
693 SERVICE_DESCRIPTION *description = (SERVICE_DESCRIPTION *) HeapAlloc(GetProcessHeap(), 0, bufsize);
\r
694 if (! description) {
\r
695 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("SERVICE_CONFIG_DESCRIPTION"), _T("get_service_description()"));
\r
699 if (QueryServiceConfig2(service_handle, SERVICE_CONFIG_DESCRIPTION, (unsigned char *) description, bufsize, &bufsize)) {
\r
700 if (description->lpDescription) _sntprintf_s(buffer, len, _TRUNCATE, _T("%s"), description->lpDescription);
\r
701 else ZeroMemory(buffer, len * sizeof(TCHAR));
\r
702 HeapFree(GetProcessHeap(), 0, description);
\r
706 HeapFree(GetProcessHeap(), 0, description);
\r
707 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_CONFIG_DESCRIPTION"), error_string(error));
\r
712 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_CONFIG_DESCRIPTION"), error_string(error));
\r
717 int get_service_startup(const TCHAR *service_name, SC_HANDLE service_handle, const QUERY_SERVICE_CONFIG *qsc, unsigned long *startup) {
\r
718 if (! qsc) return 1;
\r
720 switch (qsc->dwStartType) {
\r
721 case SERVICE_DEMAND_START: *startup = NSSM_STARTUP_MANUAL; break;
\r
722 case SERVICE_DISABLED: *startup = NSSM_STARTUP_DISABLED; break;
\r
723 default: *startup = NSSM_STARTUP_AUTOMATIC;
\r
726 if (*startup != NSSM_STARTUP_AUTOMATIC) return 0;
\r
728 /* Check for delayed start. */
\r
729 unsigned long bufsize;
\r
730 unsigned long error;
\r
731 QueryServiceConfig2(service_handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, 0, 0, &bufsize);
\r
732 error = GetLastError();
\r
733 if (error == ERROR_INSUFFICIENT_BUFFER) {
\r
734 SERVICE_DELAYED_AUTO_START_INFO *info = (SERVICE_DELAYED_AUTO_START_INFO *) HeapAlloc(GetProcessHeap(), 0, bufsize);
\r
736 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("SERVICE_DELAYED_AUTO_START_INFO"), _T("get_service_startup()"));
\r
740 if (QueryServiceConfig2(service_handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, (unsigned char *) info, bufsize, &bufsize)) {
\r
741 if (info->fDelayedAutostart) *startup = NSSM_STARTUP_DELAYED;
\r
742 HeapFree(GetProcessHeap(), 0, info);
\r
746 error = GetLastError();
\r
747 if (error != ERROR_INVALID_LEVEL) {
\r
748 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_CONFIG_DELAYED_AUTO_START_INFO"), error_string(error));
\r
753 else if (error != ERROR_INVALID_LEVEL) {
\r
754 print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_DELAYED_AUTO_START_INFO"), error_string(error));
\r
761 int get_service_username(const TCHAR *service_name, const QUERY_SERVICE_CONFIG *qsc, TCHAR **username, size_t *usernamelen) {
\r
762 if (! username) return 1;
\r
763 if (! usernamelen) return 1;
\r
768 if (! qsc) return 1;
\r
770 if (qsc->lpServiceStartName[0]) {
\r
771 if (is_localsystem(qsc->lpServiceStartName)) return 0;
\r
773 size_t len = _tcslen(qsc->lpServiceStartName);
\r
774 *username = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(TCHAR));
\r
776 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("username"), _T("get_service_username()"));
\r
780 memmove(*username, qsc->lpServiceStartName, (len + 1) * sizeof(TCHAR));
\r
781 *usernamelen = len;
\r
787 /* Set default values which aren't zero. */
\r
788 void set_nssm_service_defaults(nssm_service_t *service) {
\r
789 if (! service) return;
\r
791 service->type = SERVICE_WIN32_OWN_PROCESS;
\r
792 service->priority = NORMAL_PRIORITY_CLASS;
\r
793 service->stdin_sharing = NSSM_STDIN_SHARING;
\r
794 service->stdin_disposition = NSSM_STDIN_DISPOSITION;
\r
795 service->stdin_flags = NSSM_STDIN_FLAGS;
\r
796 service->stdout_sharing = NSSM_STDOUT_SHARING;
\r
797 service->stdout_disposition = NSSM_STDOUT_DISPOSITION;
\r
798 service->stdout_flags = NSSM_STDOUT_FLAGS;
\r
799 service->stderr_sharing = NSSM_STDERR_SHARING;
\r
800 service->stderr_disposition = NSSM_STDERR_DISPOSITION;
\r
801 service->stderr_flags = NSSM_STDERR_FLAGS;
\r
802 service->throttle_delay = NSSM_RESET_THROTTLE_RESTART;
\r
803 service->stop_method = ~0;
\r
804 service->kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;
\r
805 service->kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;
\r
806 service->kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;
\r
807 service->kill_process_tree = 1;
\r
810 /* Allocate and zero memory for a service. */
\r
811 nssm_service_t *alloc_nssm_service() {
\r
812 nssm_service_t *service = (nssm_service_t *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(nssm_service_t));
\r
813 if (! service) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("service"), _T("alloc_nssm_service()"), 0);
\r
817 /* Free memory for a service. */
\r
818 void cleanup_nssm_service(nssm_service_t *service) {
\r
819 if (! service) return;
\r
820 if (service->username) HeapFree(GetProcessHeap(), 0, service->username);
\r
821 if (service->password) {
\r
822 SecureZeroMemory(service->password, service->passwordlen * sizeof(TCHAR));
\r
823 HeapFree(GetProcessHeap(), 0, service->password);
\r
825 if (service->dependencies) HeapFree(GetProcessHeap(), 0, service->dependencies);
\r
826 if (service->env) HeapFree(GetProcessHeap(), 0, service->env);
\r
827 if (service->env_extra) HeapFree(GetProcessHeap(), 0, service->env_extra);
\r
828 if (service->handle) CloseServiceHandle(service->handle);
\r
829 if (service->process_handle) CloseHandle(service->process_handle);
\r
830 if (service->wait_handle) UnregisterWait(service->wait_handle);
\r
831 if (service->throttle_section_initialised) DeleteCriticalSection(&service->throttle_section);
\r
832 if (service->throttle_timer) CloseHandle(service->throttle_timer);
\r
833 if (service->hook_section_initialised) DeleteCriticalSection(&service->hook_section);
\r
834 if (service->initial_env) HeapFree(GetProcessHeap(), 0, service->initial_env);
\r
835 HeapFree(GetProcessHeap(), 0, service);
\r
838 /* About to install the service */
\r
839 int pre_install_service(int argc, TCHAR **argv) {
\r
840 nssm_service_t *service = alloc_nssm_service();
\r
841 set_nssm_service_defaults(service);
\r
842 if (argc) _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]);
\r
844 /* Show the dialogue box if we didn't give the service name and path */
\r
845 if (argc < 2) return nssm_gui(IDD_INSTALL, service);
\r
848 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("service"), _T("pre_install_service()"));
\r
851 _sntprintf_s(service->exe, _countof(service->exe), _TRUNCATE, _T("%s"), argv[1]);
\r
853 /* Arguments are optional */
\r
854 size_t flagslen = 0;
\r
857 for (i = 2; i < argc; i++) flagslen += _tcslen(argv[i]) + 1;
\r
858 if (! flagslen) flagslen = 1;
\r
859 if (flagslen > _countof(service->flags)) {
\r
860 print_message(stderr, NSSM_MESSAGE_FLAGS_TOO_LONG);
\r
864 for (i = 2; i < argc; i++) {
\r
865 size_t len = _tcslen(argv[i]);
\r
866 memmove(service->flags + s, argv[i], len * sizeof(TCHAR));
\r
868 if (i < argc - 1) service->flags[s++] = _T(' ');
\r
871 /* Work out directory name */
\r
872 _sntprintf_s(service->dir, _countof(service->dir), _TRUNCATE, _T("%s"), service->exe);
\r
873 strip_basename(service->dir);
\r
875 int ret = install_service(service);
\r
876 cleanup_nssm_service(service);
\r
880 /* About to edit the service. */
\r
881 int pre_edit_service(int argc, TCHAR **argv) {
\r
882 /* Require service name. */
\r
883 if (argc < 2) return usage(1);
\r
885 /* Are we editing on the command line? */
\r
886 enum { MODE_EDITING, MODE_GETTING, MODE_SETTING, MODE_RESETTING } mode = MODE_EDITING;
\r
887 const TCHAR *verb = argv[0];
\r
888 const TCHAR *service_name = argv[1];
\r
889 bool getting = false;
\r
890 bool unsetting = false;
\r
892 /* Minimum number of arguments. */
\r
894 /* Index of first value. */
\r
897 if (str_equiv(verb, _T("get"))) {
\r
899 mode = MODE_GETTING;
\r
901 else if (str_equiv(verb, _T("set"))) {
\r
903 mode = MODE_SETTING;
\r
905 else if (str_equiv(verb, _T("reset")) || str_equiv(verb, _T("unset"))) {
\r
907 mode = MODE_RESETTING;
\r
909 if (argc < mandatory) return usage(1);
\r
911 const TCHAR *parameter = 0;
\r
912 settings_t *setting = 0;
\r
915 /* Validate the parameter. */
\r
916 if (mandatory > 2) {
\r
917 bool additional_mandatory = false;
\r
919 parameter = argv[2];
\r
920 for (i = 0; settings[i].name; i++) {
\r
921 setting = &settings[i];
\r
922 if (! str_equiv(setting->name, parameter)) continue;
\r
923 if (((setting->additional & ADDITIONAL_GETTING) && mode == MODE_GETTING) || ((setting->additional & ADDITIONAL_SETTING) && mode == MODE_SETTING) || ((setting->additional & ADDITIONAL_RESETTING) && mode == MODE_RESETTING)) {
\r
924 additional_mandatory = true;
\r
929 if (! settings[i].name) {
\r
930 print_message(stderr, NSSM_MESSAGE_INVALID_PARAMETER, parameter);
\r
931 for (i = 0; settings[i].name; i++) _ftprintf(stderr, _T("%s\n"), settings[i].name);
\r
936 if (additional_mandatory) {
\r
937 if (argc < mandatory) {
\r
938 print_message(stderr, NSSM_MESSAGE_MISSING_SUBPARAMETER, parameter);
\r
941 additional = argv[3];
\r
944 else if (str_equiv(setting->name, NSSM_NATIVE_OBJECTNAME) && mode == MODE_SETTING) {
\r
945 additional = argv[3];
\r
949 additional = argv[remainder];
\r
950 if (argc < mandatory) return usage(1);
\r
954 nssm_service_t *service = alloc_nssm_service();
\r
955 _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), service_name);
\r
957 /* Open service manager */
\r
958 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
\r
960 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
964 /* Try to open the service */
\r
965 unsigned long access = SERVICE_QUERY_CONFIG;
\r
966 if (mode != MODE_GETTING) access |= SERVICE_CHANGE_CONFIG;
\r
967 service->handle = open_service(services, service->name, access, service->name, _countof(service->name));
\r
968 if (! service->handle) {
\r
969 CloseServiceHandle(services);
\r
973 /* Get system details. */
\r
974 QUERY_SERVICE_CONFIG *qsc = query_service_config(service->name, service->handle);
\r
976 CloseServiceHandle(service->handle);
\r
977 CloseServiceHandle(services);
\r
981 service->type = qsc->dwServiceType;
\r
982 if (! (service->type & SERVICE_WIN32_OWN_PROCESS)) {
\r
983 if (mode != MODE_GETTING) {
\r
984 HeapFree(GetProcessHeap(), 0, qsc);
\r
985 CloseServiceHandle(service->handle);
\r
986 CloseServiceHandle(services);
\r
987 print_message(stderr, NSSM_MESSAGE_CANNOT_EDIT, service->name, NSSM_WIN32_OWN_PROCESS, 0);
\r
992 if (get_service_startup(service->name, service->handle, qsc, &service->startup)) {
\r
993 if (mode != MODE_GETTING) {
\r
994 HeapFree(GetProcessHeap(), 0, qsc);
\r
995 CloseServiceHandle(service->handle);
\r
996 CloseServiceHandle(services);
\r
1001 if (get_service_username(service->name, qsc, &service->username, &service->usernamelen)) {
\r
1002 if (mode != MODE_GETTING) {
\r
1003 HeapFree(GetProcessHeap(), 0, qsc);
\r
1004 CloseServiceHandle(service->handle);
\r
1005 CloseServiceHandle(services);
\r
1010 _sntprintf_s(service->displayname, _countof(service->displayname), _TRUNCATE, _T("%s"), qsc->lpDisplayName);
\r
1012 /* Get the canonical service name. We open it case insensitively. */
\r
1013 unsigned long bufsize = _countof(service->name);
\r
1014 GetServiceKeyName(services, service->displayname, service->name, &bufsize);
\r
1016 /* Remember the executable in case it isn't NSSM. */
\r
1017 _sntprintf_s(service->image, _countof(service->image), _TRUNCATE, _T("%s"), qsc->lpBinaryPathName);
\r
1018 HeapFree(GetProcessHeap(), 0, qsc);
\r
1020 /* Get extended system details. */
\r
1021 if (get_service_description(service->name, service->handle, _countof(service->description), service->description)) {
\r
1022 if (mode != MODE_GETTING) {
\r
1023 CloseServiceHandle(service->handle);
\r
1024 CloseServiceHandle(services);
\r
1029 if (get_service_dependencies(service->name, service->handle, &service->dependencies, &service->dependencieslen)) {
\r
1030 if (mode != MODE_GETTING) {
\r
1031 CloseServiceHandle(service->handle);
\r
1032 CloseServiceHandle(services);
\r
1037 /* Get NSSM details. */
\r
1038 get_parameters(service, 0);
\r
1040 CloseServiceHandle(services);
\r
1042 if (! service->exe[0]) {
\r
1043 service->native = true;
\r
1044 if (mode != MODE_GETTING) print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE, service->name, NSSM, service->image);
\r
1047 /* Editing with the GUI. */
\r
1048 if (mode == MODE_EDITING) {
\r
1049 nssm_gui(IDD_EDIT, service);
\r
1053 /* Trying to manage App* parameters for a non-NSSM service. */
\r
1054 if (! setting->native && service->native) {
\r
1055 CloseServiceHandle(service->handle);
\r
1056 print_message(stderr, NSSM_MESSAGE_NATIVE_PARAMETER, setting->name, NSSM);
\r
1064 if (mode == MODE_GETTING) {
\r
1065 if (! service->native) {
\r
1066 key = open_registry(service->name, KEY_READ);
\r
1067 if (! key) return 4;
\r
1070 if (setting->native) ret = get_setting(service->name, service->handle, setting, &value, additional);
\r
1071 else ret = get_setting(service->name, key, setting, &value, additional);
\r
1073 CloseServiceHandle(service->handle);
\r
1077 switch (setting->type) {
\r
1078 case REG_EXPAND_SZ:
\r
1079 case REG_MULTI_SZ:
\r
1081 _tprintf(_T("%s\n"), value.string ? value.string : _T(""));
\r
1082 HeapFree(GetProcessHeap(), 0, value.string);
\r
1086 _tprintf(_T("%u\n"), value.numeric);
\r
1090 if (! service->native) RegCloseKey(key);
\r
1091 CloseServiceHandle(service->handle);
\r
1095 /* Build the value. */
\r
1096 if (mode == MODE_RESETTING) {
\r
1097 /* Unset the parameter. */
\r
1100 else if (remainder == argc) {
\r
1104 /* Set the parameter. */
\r
1106 size_t delimiterlen = (setting->additional & ADDITIONAL_CRLF) ? 2 : 1;
\r
1107 for (i = remainder; i < argc; i++) len += _tcslen(argv[i]) + delimiterlen;
\r
1110 value.string = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, len * sizeof(TCHAR));
\r
1111 if (! value.string) {
\r
1112 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("value"), _T("edit_service()"));
\r
1113 CloseServiceHandle(service->handle);
\r
1118 for (i = remainder; i < argc; i++) {
\r
1119 size_t len = _tcslen(argv[i]);
\r
1120 memmove(value.string + s, argv[i], len * sizeof(TCHAR));
\r
1122 if (i < argc - 1) {
\r
1123 if (setting->additional & ADDITIONAL_CRLF) {
\r
1124 value.string[s++] = _T('\r');
\r
1125 value.string[s++] = _T('\n');
\r
1127 else value.string[s++] = _T(' ');
\r
1130 value.string[s] = _T('\0');
\r
1133 if (! service->native) {
\r
1134 key = open_registry(service->name, KEY_READ | KEY_WRITE);
\r
1136 if (value.string) HeapFree(GetProcessHeap(), 0, value.string);
\r
1141 if (setting->native) ret = set_setting(service->name, service->handle, setting, &value, additional);
\r
1142 else ret = set_setting(service->name, key, setting, &value, additional);
\r
1143 if (value.string) HeapFree(GetProcessHeap(), 0, value.string);
\r
1145 if (! service->native) RegCloseKey(key);
\r
1146 CloseServiceHandle(service->handle);
\r
1150 if (! service->native) RegCloseKey(key);
\r
1151 CloseServiceHandle(service->handle);
\r
1156 /* About to remove the service */
\r
1157 int pre_remove_service(int argc, TCHAR **argv) {
\r
1158 nssm_service_t *service = alloc_nssm_service();
\r
1159 set_nssm_service_defaults(service);
\r
1160 if (argc) _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]);
\r
1162 /* Show dialogue box if we didn't pass service name and "confirm" */
\r
1163 if (argc < 2) return nssm_gui(IDD_REMOVE, service);
\r
1164 if (str_equiv(argv[1], _T("confirm"))) {
\r
1165 int ret = remove_service(service);
\r
1166 cleanup_nssm_service(service);
\r
1169 print_message(stderr, NSSM_MESSAGE_PRE_REMOVE_SERVICE);
\r
1173 /* Install the service */
\r
1174 int install_service(nssm_service_t *service) {
\r
1175 if (! service) return 1;
\r
1177 /* Open service manager */
\r
1178 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE);
\r
1180 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
1181 cleanup_nssm_service(service);
\r
1185 /* Get path of this program */
\r
1186 _sntprintf_s(service->image, _countof(service->image), _TRUNCATE, _T("%s"), nssm_imagepath());
\r
1188 /* Create the service - settings will be changed in edit_service() */
\r
1189 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
1190 if (! service->handle) {
\r
1191 print_message(stderr, NSSM_MESSAGE_CREATESERVICE_FAILED, error_string(GetLastError()));
\r
1192 CloseServiceHandle(services);
\r
1196 if (edit_service(service, false)) {
\r
1197 DeleteService(service->handle);
\r
1198 CloseServiceHandle(services);
\r
1202 print_message(stdout, NSSM_MESSAGE_SERVICE_INSTALLED, service->name);
\r
1205 CloseServiceHandle(services);
\r
1210 /* Edit the service. */
\r
1211 int edit_service(nssm_service_t *service, bool editing) {
\r
1212 if (! service) return 1;
\r
1215 The only two valid flags for service type are SERVICE_WIN32_OWN_PROCESS
\r
1216 and SERVICE_INTERACTIVE_PROCESS.
\r
1218 service->type &= SERVICE_INTERACTIVE_PROCESS;
\r
1219 service->type |= SERVICE_WIN32_OWN_PROCESS;
\r
1221 /* Startup type. */
\r
1222 unsigned long startup;
\r
1223 switch (service->startup) {
\r
1224 case NSSM_STARTUP_MANUAL: startup = SERVICE_DEMAND_START; break;
\r
1225 case NSSM_STARTUP_DISABLED: startup = SERVICE_DISABLED; break;
\r
1226 default: startup = SERVICE_AUTO_START;
\r
1229 /* Display name. */
\r
1230 if (! service->displayname[0]) _sntprintf_s(service->displayname, _countof(service->displayname), _TRUNCATE, _T("%s"), service->name);
\r
1233 Username must be NULL if we aren't changing or an account name.
\r
1234 We must explicitly use LOCALSYSTEM to change it when we are editing.
\r
1235 Password must be NULL if we aren't changing, a password or "".
\r
1236 Empty passwords are valid but we won't allow them in the GUI.
\r
1238 TCHAR *username = 0;
\r
1240 TCHAR *password = 0;
\r
1241 if (service->usernamelen) {
\r
1242 username = service->username;
\r
1243 if (canonicalise_username(username, &canon)) return 5;
\r
1244 if (service->passwordlen) password = service->password;
\r
1246 else if (editing) username = canon = NSSM_LOCALSYSTEM_ACCOUNT;
\r
1248 if (well_known_username(canon)) password = _T("");
\r
1250 if (grant_logon_as_service(canon)) {
\r
1251 if (canon != username) HeapFree(GetProcessHeap(), 0, canon);
\r
1252 print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username);
\r
1257 TCHAR *dependencies = _T("");
\r
1258 if (service->dependencieslen) dependencies = 0; /* Change later. */
\r
1260 if (! ChangeServiceConfig(service->handle, service->type, startup, SERVICE_NO_CHANGE, 0, 0, 0, dependencies, canon, password, service->displayname)) {
\r
1261 if (canon != username) HeapFree(GetProcessHeap(), 0, canon);
\r
1262 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
1265 if (canon != username) HeapFree(GetProcessHeap(), 0, canon);
\r
1267 if (service->dependencieslen) {
\r
1268 if (set_service_dependencies(service->name, service->handle, service->dependencies)) return 5;
\r
1271 if (service->description[0] || editing) {
\r
1272 set_service_description(service->name, service->handle, service->description);
\r
1275 SERVICE_DELAYED_AUTO_START_INFO delayed;
\r
1276 ZeroMemory(&delayed, sizeof(delayed));
\r
1277 if (service->startup == NSSM_STARTUP_DELAYED) delayed.fDelayedAutostart = 1;
\r
1278 else delayed.fDelayedAutostart = 0;
\r
1279 /* Delayed startup isn't supported until Vista. */
\r
1280 if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, &delayed)) {
\r
1281 unsigned long error = GetLastError();
\r
1282 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
\r
1283 if (error != ERROR_INVALID_LEVEL) {
\r
1284 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_DELAYED_AUTO_START_INFO_FAILED, service->name, error_string(error), 0);
\r
1288 /* Don't mess with parameters which aren't ours. */
\r
1289 if (! service->native) {
\r
1290 /* Now we need to put the parameters into the registry */
\r
1291 if (create_parameters(service, editing)) {
\r
1292 print_message(stderr, NSSM_MESSAGE_CREATE_PARAMETERS_FAILED);
\r
1296 set_service_recovery(service);
\r
1302 /* Control a service. */
\r
1303 int control_service(unsigned long control, int argc, TCHAR **argv) {
\r
1304 if (argc < 1) return usage(1);
\r
1305 TCHAR *service_name = argv[0];
\r
1306 TCHAR canonical_name[SERVICE_NAME_LENGTH];
\r
1308 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
\r
1310 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
1314 unsigned long access = SERVICE_QUERY_STATUS;
\r
1315 switch (control) {
\r
1316 case NSSM_SERVICE_CONTROL_START:
\r
1317 access |= SERVICE_START;
\r
1320 case SERVICE_CONTROL_CONTINUE:
\r
1321 case SERVICE_CONTROL_PAUSE:
\r
1322 access |= SERVICE_PAUSE_CONTINUE;
\r
1325 case SERVICE_CONTROL_STOP:
\r
1326 access |= SERVICE_STOP;
\r
1329 case NSSM_SERVICE_CONTROL_ROTATE:
\r
1330 access |= SERVICE_USER_DEFINED_CONTROL;
\r
1334 SC_HANDLE service_handle = open_service(services, service_name, access, canonical_name, _countof(canonical_name));
\r
1335 if (! service_handle) {
\r
1336 CloseServiceHandle(services);
\r
1341 unsigned long error;
\r
1342 SERVICE_STATUS service_status;
\r
1343 if (control == NSSM_SERVICE_CONTROL_START) {
\r
1344 unsigned long initial_status = SERVICE_STOPPED;
\r
1345 ret = StartService(service_handle, (unsigned long) argc, (const TCHAR **) argv);
\r
1346 error = GetLastError();
\r
1347 CloseServiceHandle(services);
\r
1349 if (error == ERROR_IO_PENDING) {
\r
1351 Older versions of Windows return immediately with ERROR_IO_PENDING
\r
1352 indicate that the operation is still in progress. Newer versions
\r
1353 will return it if there really is a delay.
\r
1356 error = ERROR_SUCCESS;
\r
1360 int response = await_service_control_response(control, service_handle, &service_status, initial_status);
\r
1361 CloseServiceHandle(service_handle);
\r
1364 print_message(stderr, NSSM_MESSAGE_BAD_CONTROL_RESPONSE, canonical_name, service_status_text(service_status.dwCurrentState), service_control_text(control));
\r
1367 else _tprintf(_T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1371 CloseServiceHandle(service_handle);
\r
1372 _ftprintf(stderr, _T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1376 else if (control == SERVICE_CONTROL_INTERROGATE) {
\r
1378 We could actually send an INTERROGATE control but that won't return
\r
1379 any information if the service is stopped and we don't care about
\r
1380 the extra details it might give us in any case. So we'll fake it.
\r
1382 ret = QueryServiceStatus(service_handle, &service_status);
\r
1383 error = GetLastError();
\r
1386 _tprintf(_T("%s\n"), service_status_text(service_status.dwCurrentState));
\r
1390 _ftprintf(stderr, _T("%s: %s\n"), canonical_name, error_string(error));
\r
1395 ret = ControlService(service_handle, control, &service_status);
\r
1396 unsigned long initial_status = service_status.dwCurrentState;
\r
1397 error = GetLastError();
\r
1398 CloseServiceHandle(services);
\r
1400 if (error == ERROR_IO_PENDING) {
\r
1402 error = ERROR_SUCCESS;
\r
1406 int response = await_service_control_response(control, service_handle, &service_status, initial_status);
\r
1407 CloseServiceHandle(service_handle);
\r
1410 print_message(stderr, NSSM_MESSAGE_BAD_CONTROL_RESPONSE, canonical_name, service_status_text(service_status.dwCurrentState), service_control_text(control));
\r
1413 else _tprintf(_T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1417 CloseServiceHandle(service_handle);
\r
1418 _ftprintf(stderr, _T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1419 if (error == ERROR_SERVICE_NOT_ACTIVE) {
\r
1420 if (control == SERVICE_CONTROL_SHUTDOWN || control == SERVICE_CONTROL_STOP) return 0;
\r
1427 /* Remove the service */
\r
1428 int remove_service(nssm_service_t *service) {
\r
1429 if (! service) return 1;
\r
1431 /* Open service manager */
\r
1432 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
\r
1434 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
1438 /* Try to open the service */
\r
1439 service->handle = open_service(services, service->name, DELETE, service->name, _countof(service->name));
\r
1440 if (! service->handle) {
\r
1441 CloseServiceHandle(services);
\r
1445 /* Get the canonical service name. We open it case insensitively. */
\r
1446 unsigned long bufsize = _countof(service->displayname);
\r
1447 GetServiceDisplayName(services, service->name, service->displayname, &bufsize);
\r
1448 bufsize = _countof(service->name);
\r
1449 GetServiceKeyName(services, service->displayname, service->name, &bufsize);
\r
1451 /* Try to delete the service */
\r
1452 if (! DeleteService(service->handle)) {
\r
1453 print_message(stderr, NSSM_MESSAGE_DELETESERVICE_FAILED);
\r
1454 CloseServiceHandle(services);
\r
1459 CloseServiceHandle(services);
\r
1461 print_message(stdout, NSSM_MESSAGE_SERVICE_REMOVED, service->name);
\r
1465 /* Service initialisation */
\r
1466 void WINAPI service_main(unsigned long argc, TCHAR **argv) {
\r
1467 nssm_service_t *service = alloc_nssm_service();
\r
1468 if (! service) return;
\r
1470 if (_sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]) < 0) {
\r
1471 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("service->name"), _T("service_main()"), 0);
\r
1475 /* We can use a condition variable in a critical section on Vista or later. */
\r
1476 if (imports.SleepConditionVariableCS && imports.WakeConditionVariable) use_critical_section = true;
\r
1477 else use_critical_section = false;
\r
1479 /* Initialise status */
\r
1480 ZeroMemory(&service->status, sizeof(service->status));
\r
1481 service->status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;
\r
1482 service->status.dwControlsAccepted = 0;
\r
1483 service->status.dwWin32ExitCode = NO_ERROR;
\r
1484 service->status.dwServiceSpecificExitCode = 0;
\r
1485 service->status.dwCheckPoint = 0;
\r
1486 service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;
\r
1488 /* Signal we AREN'T running the server */
\r
1489 service->process_handle = 0;
\r
1492 /* Register control handler */
\r
1493 service->status_handle = RegisterServiceCtrlHandlerEx(NSSM, service_control_handler, (void *) service);
\r
1494 if (! service->status_handle) {
\r
1495 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_REGISTERSERVICECTRLHANDER_FAILED, error_string(GetLastError()), 0);
\r
1499 log_service_control(service->name, 0, true);
\r
1501 service->status.dwCurrentState = SERVICE_START_PENDING;
\r
1502 service->status.dwWaitHint = service->throttle_delay + NSSM_WAITHINT_MARGIN;
\r
1503 SetServiceStatus(service->status_handle, &service->status);
\r
1506 /* Try to create the exit action parameters; we don't care if it fails */
\r
1507 create_exit_action(service->name, exit_action_strings[0], false);
\r
1509 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT);
\r
1511 service->handle = open_service(services, service->name, SERVICE_CHANGE_CONFIG, 0, 0);
\r
1512 set_service_recovery(service);
\r
1514 /* Remember our display name. */
\r
1515 unsigned long displayname_len = _countof(service->displayname);
\r
1516 GetServiceDisplayName(services, service->name, service->displayname, &displayname_len);
\r
1518 CloseServiceHandle(services);
\r
1522 /* Used for signalling a resume if the service pauses when throttled. */
\r
1523 if (use_critical_section) {
\r
1524 InitializeCriticalSection(&service->throttle_section);
\r
1525 service->throttle_section_initialised = true;
\r
1528 service->throttle_timer = CreateWaitableTimer(0, 1, 0);
\r
1529 if (! service->throttle_timer) {
\r
1530 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_CREATEWAITABLETIMER_FAILED, service->name, error_string(GetLastError()), 0);
\r
1534 /* Critical section for hooks. */
\r
1535 InitializeCriticalSection(&service->hook_section);
\r
1536 service->hook_section_initialised = true;
\r
1538 /* Remember our initial environment. */
\r
1539 service->initial_env = copy_environment();
\r
1541 /* Remember our creation time. */
\r
1542 if (get_process_creation_time(GetCurrentProcess(), &service->nssm_creation_time)) ZeroMemory(&service->nssm_creation_time, sizeof(service->nssm_creation_time));
\r
1544 service->allow_restart = true;
\r
1545 if (! CreateThread(NULL, 0, launch_service, (void *) service, 0, NULL)) {
\r
1546 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETHREAD_FAILED, error_string(GetLastError()), 0);
\r
1547 stop_service(service, 0, true, true);
\r
1551 /* Make sure service recovery actions are taken where necessary */
\r
1552 void set_service_recovery(nssm_service_t *service) {
\r
1553 SERVICE_FAILURE_ACTIONS_FLAG flag;
\r
1554 ZeroMemory(&flag, sizeof(flag));
\r
1555 flag.fFailureActionsOnNonCrashFailures = true;
\r
1557 /* This functionality was added in Vista so the call may fail */
\r
1558 if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, &flag)) {
\r
1559 unsigned long error = GetLastError();
\r
1560 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
\r
1561 if (error != ERROR_INVALID_LEVEL) {
\r
1562 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_FAILURE_ACTIONS_FAILED, service->name, error_string(error), 0);
\r
1567 int monitor_service(nssm_service_t *service) {
\r
1568 /* Set service status to started */
\r
1569 int ret = start_service(service);
\r
1572 _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%d"), ret);
\r
1573 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_START_SERVICE_FAILED, service->exe, service->name, ret, 0);
\r
1576 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_STARTED_SERVICE, service->exe, service->flags, service->name, service->dir, 0);
\r
1578 /* Monitor service */
\r
1579 if (! RegisterWaitForSingleObject(&service->wait_handle, service->process_handle, end_service, (void *) service, INFINITE, WT_EXECUTEONLYONCE | WT_EXECUTELONGFUNCTION)) {
\r
1580 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_REGISTERWAITFORSINGLEOBJECT_FAILED, service->name, service->exe, error_string(GetLastError()), 0);
\r
1586 TCHAR *service_control_text(unsigned long control) {
\r
1587 switch (control) {
\r
1588 /* HACK: there is no SERVICE_CONTROL_START constant */
\r
1589 case NSSM_SERVICE_CONTROL_START: return _T("START");
\r
1590 case SERVICE_CONTROL_STOP: return _T("STOP");
\r
1591 case SERVICE_CONTROL_SHUTDOWN: return _T("SHUTDOWN");
\r
1592 case SERVICE_CONTROL_PAUSE: return _T("PAUSE");
\r
1593 case SERVICE_CONTROL_CONTINUE: return _T("CONTINUE");
\r
1594 case SERVICE_CONTROL_INTERROGATE: return _T("INTERROGATE");
\r
1595 case NSSM_SERVICE_CONTROL_ROTATE: return _T("ROTATE");
\r
1596 case SERVICE_CONTROL_POWEREVENT: return _T("POWEREVENT");
\r
1597 default: return 0;
\r
1601 TCHAR *service_status_text(unsigned long status) {
\r
1603 case SERVICE_STOPPED: return _T("SERVICE_STOPPED");
\r
1604 case SERVICE_START_PENDING: return _T("SERVICE_START_PENDING");
\r
1605 case SERVICE_STOP_PENDING: return _T("SERVICE_STOP_PENDING");
\r
1606 case SERVICE_RUNNING: return _T("SERVICE_RUNNING");
\r
1607 case SERVICE_CONTINUE_PENDING: return _T("SERVICE_CONTINUE_PENDING");
\r
1608 case SERVICE_PAUSE_PENDING: return _T("SERVICE_PAUSE_PENDING");
\r
1609 case SERVICE_PAUSED: return _T("SERVICE_PAUSED");
\r
1610 default: return 0;
\r
1614 void log_service_control(TCHAR *service_name, unsigned long control, bool handled) {
\r
1615 TCHAR *text = service_control_text(control);
\r
1616 unsigned long event;
\r
1619 /* "0x" + 8 x hex + NULL */
\r
1620 text = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, 11 * sizeof(TCHAR));
\r
1622 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);
\r
1625 if (_sntprintf_s(text, 11, _TRUNCATE, _T("0x%08x"), control) < 0) {
\r
1626 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);
\r
1627 HeapFree(GetProcessHeap(), 0, text);
\r
1631 event = NSSM_EVENT_SERVICE_CONTROL_UNKNOWN;
\r
1633 else if (handled) event = NSSM_EVENT_SERVICE_CONTROL_HANDLED;
\r
1634 else event = NSSM_EVENT_SERVICE_CONTROL_NOT_HANDLED;
\r
1636 log_event(EVENTLOG_INFORMATION_TYPE, event, service_name, text, 0);
\r
1638 if (event == NSSM_EVENT_SERVICE_CONTROL_UNKNOWN) {
\r
1639 HeapFree(GetProcessHeap(), 0, text);
\r
1643 /* Service control handler */
\r
1644 unsigned long WINAPI service_control_handler(unsigned long control, unsigned long event, void *data, void *context) {
\r
1645 nssm_service_t *service = (nssm_service_t *) context;
\r
1647 switch (control) {
\r
1648 case SERVICE_CONTROL_INTERROGATE:
\r
1649 /* We always keep the service status up-to-date so this is a no-op. */
\r
1652 case SERVICE_CONTROL_SHUTDOWN:
\r
1653 case SERVICE_CONTROL_STOP:
\r
1654 service->last_control = control;
\r
1655 log_service_control(service->name, control, true);
\r
1657 /* Immediately block further controls. */
\r
1658 service->allow_restart = false;
\r
1659 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
1660 service->status.dwControlsAccepted = 0;
\r
1661 SetServiceStatus(service->status_handle, &service->status);
\r
1663 /* Pre-stop hook. */
\r
1664 nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_STOP, NSSM_HOOK_ACTION_PRE, &control, NSSM_SERVICE_STATUS_DEADLINE, false);
\r
1667 We MUST acknowledge the stop request promptly but we're committed to
\r
1668 waiting for the application to exit. Spawn a new thread to wait
\r
1669 while we acknowledge the request.
\r
1671 if (! CreateThread(NULL, 0, shutdown_service, context, 0, NULL)) {
\r
1672 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETHREAD_FAILED, error_string(GetLastError()), 0);
\r
1675 We couldn't create a thread to tidy up so we'll have to force the tidyup
\r
1676 to complete in time in this thread.
\r
1678 service->kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;
\r
1679 service->kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;
\r
1680 service->kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;
\r
1682 stop_service(service, 0, true, true);
\r
1686 case SERVICE_CONTROL_CONTINUE:
\r
1687 service->last_control = control;
\r
1688 log_service_control(service->name, control, true);
\r
1689 service->throttle = 0;
\r
1690 if (use_critical_section) imports.WakeConditionVariable(&service->throttle_condition);
\r
1692 if (! service->throttle_timer) return ERROR_CALL_NOT_IMPLEMENTED;
\r
1693 ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));
\r
1694 SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);
\r
1696 /* We can't continue if the application is running! */
\r
1697 if (! service->process_handle) service->status.dwCurrentState = SERVICE_CONTINUE_PENDING;
\r
1698 service->status.dwWaitHint = throttle_milliseconds(service->throttle) + NSSM_WAITHINT_MARGIN;
\r
1699 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESET_THROTTLE, service->name, 0);
\r
1700 SetServiceStatus(service->status_handle, &service->status);
\r
1703 case SERVICE_CONTROL_PAUSE:
\r
1705 We don't accept pause messages but it isn't possible to register
\r
1706 only for continue messages so we have to handle this case.
\r
1708 log_service_control(service->name, control, false);
\r
1709 return ERROR_CALL_NOT_IMPLEMENTED;
\r
1711 case NSSM_SERVICE_CONTROL_ROTATE:
\r
1712 service->last_control = control;
\r
1713 log_service_control(service->name, control, true);
\r
1714 (void) nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_ROTATE, NSSM_HOOK_ACTION_PRE, &control, NSSM_HOOK_DEADLINE, false);
\r
1715 if (service->rotate_stdout_online == NSSM_ROTATE_ONLINE) service->rotate_stdout_online = NSSM_ROTATE_ONLINE_ASAP;
\r
1716 if (service->rotate_stderr_online == NSSM_ROTATE_ONLINE) service->rotate_stderr_online = NSSM_ROTATE_ONLINE_ASAP;
\r
1717 (void) nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_ROTATE, NSSM_HOOK_ACTION_POST, &control);
\r
1720 case SERVICE_CONTROL_POWEREVENT:
\r
1721 /* Resume from suspend. */
\r
1722 if (event == PBT_APMRESUMEAUTOMATIC) {
\r
1723 service->last_control = control;
\r
1724 log_service_control(service->name, control, true);
\r
1725 (void) nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_POWER, NSSM_HOOK_ACTION_RESUME, &control);
\r
1729 /* Battery low or changed to A/C power or something. */
\r
1730 if (event == PBT_APMPOWERSTATUSCHANGE) {
\r
1731 service->last_control = control;
\r
1732 log_service_control(service->name, control, true);
\r
1733 (void) nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_POWER, NSSM_HOOK_ACTION_CHANGE, &control);
\r
1736 log_service_control(service->name, control, false);
\r
1740 /* Unknown control */
\r
1741 log_service_control(service->name, control, false);
\r
1742 return ERROR_CALL_NOT_IMPLEMENTED;
\r
1745 /* Start the service */
\r
1746 int start_service(nssm_service_t *service) {
\r
1747 service->stopping = false;
\r
1749 if (service->process_handle) return 0;
\r
1750 service->start_requested_count++;
\r
1752 /* Allocate a STARTUPINFO structure for a new process */
\r
1754 ZeroMemory(&si, sizeof(si));
\r
1755 si.cb = sizeof(si);
\r
1757 /* Allocate a PROCESSINFO structure for the process */
\r
1758 PROCESS_INFORMATION pi;
\r
1759 ZeroMemory(&pi, sizeof(pi));
\r
1761 /* Get startup parameters */
\r
1762 int ret = get_parameters(service, &si);
\r
1764 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_PARAMETERS_FAILED, service->name, 0);
\r
1765 unset_service_environment(service);
\r
1766 return stop_service(service, 2, true, true);
\r
1769 /* Launch executable with arguments */
\r
1770 TCHAR cmd[CMD_LENGTH];
\r
1771 if (_sntprintf_s(cmd, _countof(cmd), _TRUNCATE, _T("\"%s\" %s"), service->exe, service->flags) < 0) {
\r
1772 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("command line"), _T("start_service"), 0);
\r
1773 unset_service_environment(service);
\r
1774 return stop_service(service, 2, true, true);
\r
1777 throttle_restart(service);
\r
1779 service->status.dwCurrentState = SERVICE_START_PENDING;
\r
1780 service->status.dwControlsAccepted = SERVICE_ACCEPT_POWEREVENT | SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP;
\r
1781 SetServiceStatus(service->status_handle, &service->status);
\r
1783 unsigned long control = NSSM_SERVICE_CONTROL_START;
\r
1785 /* Did another thread receive a stop control? */
\r
1786 if (service->allow_restart) {
\r
1787 /* Set up I/O redirection. */
\r
1788 if (get_output_handles(service, &si)) {
\r
1789 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_OUTPUT_HANDLES_FAILED, service->name, 0);
\r
1790 if (! service->no_console) FreeConsole();
\r
1791 close_output_handles(&si);
\r
1792 unset_service_environment(service);
\r
1793 return stop_service(service, 4, true, true);
\r
1796 /* Pre-start hook. May need I/O to have been redirected already. */
\r
1797 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
1799 _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), NSSM_HOOK_STATUS_ABORT);
\r
1800 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_PRESTART_HOOK_ABORT, NSSM_HOOK_EVENT_START, NSSM_HOOK_ACTION_PRE, service->name, code, 0);
\r
1801 unset_service_environment(service);
\r
1802 return stop_service(service, 5, true, true);
\r
1805 /* The pre-start hook will have cleaned the environment. */
\r
1806 set_service_environment(service);
\r
1808 bool inherit_handles = false;
\r
1809 if (si.dwFlags & STARTF_USESTDHANDLES) inherit_handles = true;
\r
1810 unsigned long flags = service->priority & priority_mask();
\r
1811 if (service->affinity) flags |= CREATE_SUSPENDED;
\r
1812 if (! CreateProcess(0, cmd, 0, 0, inherit_handles, flags, 0, service->dir, &si, &pi)) {
\r
1813 unsigned long exitcode = 3;
\r
1814 unsigned long error = GetLastError();
\r
1815 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service->name, service->exe, error_string(error), 0);
\r
1816 close_output_handles(&si);
\r
1817 unset_service_environment(service);
\r
1818 return stop_service(service, exitcode, true, true);
\r
1820 service->start_count++;
\r
1821 service->process_handle = pi.hProcess;
\r
1822 service->pid = pi.dwProcessId;
\r
1824 if (get_process_creation_time(service->process_handle, &service->creation_time)) ZeroMemory(&service->creation_time, sizeof(service->creation_time));
\r
1826 close_output_handles(&si);
\r
1828 if (! service->no_console) FreeConsole();
\r
1830 if (service->affinity) {
\r
1832 We are explicitly storing service->affinity as a 64-bit unsigned integer
\r
1833 so that we can parse it regardless of whether we're running in 32-bit
\r
1834 or 64-bit mode. The arguments to SetProcessAffinityMask(), however, are
\r
1835 defined as type DWORD_PTR and hence limited to 32 bits on a 32-bit system
\r
1836 (or when running the 32-bit NSSM).
\r
1838 The result is a lot of seemingly-unnecessary casting throughout the code
\r
1839 and potentially confusion when we actually try to start the service.
\r
1840 Having said that, however, it's unlikely that we're actually going to
\r
1841 run in 32-bit mode on a system which has more than 32 CPUs so the
\r
1842 likelihood of seeing a confusing situation is somewhat diminished.
\r
1844 DWORD_PTR affinity, system_affinity;
\r
1846 if (GetProcessAffinityMask(service->process_handle, &affinity, &system_affinity)) affinity = service->affinity & system_affinity;
\r
1848 affinity = (DWORD_PTR) service->affinity;
\r
1849 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GETPROCESSAFFINITYMASK_FAILED, service->name, error_string(GetLastError()), 0);
\r
1852 if (! SetProcessAffinityMask(service->process_handle, affinity)) {
\r
1853 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_SETPROCESSAFFINITYMASK_FAILED, service->name, error_string(GetLastError()), 0);
\r
1856 ResumeThread(pi.hThread);
\r
1860 /* Restore our environment. */
\r
1861 unset_service_environment(service);
\r
1864 Wait for a clean startup before changing the service status to RUNNING
\r
1865 but be mindful of the fact that we are blocking the service control manager
\r
1866 so abandon the wait before too much time has elapsed.
\r
1868 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
1870 /* Did another thread receive a stop control? */
\r
1871 if (! service->allow_restart) return 0;
\r
1873 /* Signal successful start */
\r
1874 service->status.dwCurrentState = SERVICE_RUNNING;
\r
1875 service->status.dwControlsAccepted &= ~SERVICE_ACCEPT_PAUSE_CONTINUE;
\r
1876 SetServiceStatus(service->status_handle, &service->status);
\r
1878 /* Post-start hook. */
\r
1879 if (! service->throttle) {
\r
1880 (void) nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_START, NSSM_HOOK_ACTION_POST, &control);
\r
1883 /* Ensure the restart delay is always applied. */
\r
1884 if (service->restart_delay && ! service->throttle) service->throttle++;
\r
1889 /* Stop the service */
\r
1890 int stop_service(nssm_service_t *service, unsigned long exitcode, bool graceful, bool default_action) {
\r
1891 service->allow_restart = false;
\r
1892 if (service->wait_handle) {
\r
1893 UnregisterWait(service->wait_handle);
\r
1894 service->wait_handle = 0;
\r
1897 service->rotate_stdout_online = service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;
\r
1899 if (default_action && ! exitcode && ! graceful) {
\r
1900 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
1904 /* Signal we are stopping */
\r
1906 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
1907 service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;
\r
1908 SetServiceStatus(service->status_handle, &service->status);
\r
1911 /* Nothing to do if service isn't running */
\r
1912 if (service->pid) {
\r
1913 /* Shut down service */
\r
1914 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_TERMINATEPROCESS, service->name, service->exe, 0);
\r
1916 service_kill_t(service, &k);
\r
1920 else log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_PROCESS_ALREADY_STOPPED, service->name, service->exe, 0);
\r
1922 end_service((void *) service, true);
\r
1924 /* Signal we stopped */
\r
1926 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
1927 wait_for_hooks(service, true);
\r
1928 service->status.dwCurrentState = SERVICE_STOPPED;
\r
1930 service->status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
\r
1931 service->status.dwServiceSpecificExitCode = exitcode;
\r
1934 service->status.dwWin32ExitCode = NO_ERROR;
\r
1935 service->status.dwServiceSpecificExitCode = 0;
\r
1937 SetServiceStatus(service->status_handle, &service->status);
\r
1943 /* Callback function triggered when the server exits */
\r
1944 void CALLBACK end_service(void *arg, unsigned char why) {
\r
1945 nssm_service_t *service = (nssm_service_t *) arg;
\r
1947 if (service->stopping) return;
\r
1949 service->stopping = true;
\r
1951 service->rotate_stdout_online = service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;
\r
1953 /* Use now as a dummy exit time. */
\r
1954 GetSystemTimeAsFileTime(&service->exit_time);
\r
1956 /* Check exit code */
\r
1957 unsigned long exitcode = 0;
\r
1959 if (service->process_handle) {
\r
1960 GetExitCodeProcess(service->process_handle, &exitcode);
\r
1961 service->exitcode = exitcode;
\r
1962 /* Check real exit time. */
\r
1963 if (exitcode != STILL_ACTIVE) get_process_exit_time(service->process_handle, &service->exit_time);
\r
1964 CloseHandle(service->process_handle);
\r
1967 service->process_handle = 0;
\r
1970 Log that the service ended BEFORE logging about killing the process
\r
1971 tree. See below for the possible values of the why argument.
\r
1974 _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), exitcode);
\r
1975 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ENDED_SERVICE, service->exe, service->name, code, 0);
\r
1979 if (exitcode == STILL_ACTIVE) exitcode = 0;
\r
1980 if (service->pid && service->kill_process_tree) {
\r
1982 service_kill_t(service, &k);
\r
1983 kill_process_tree(&k, service->pid);
\r
1988 service->exit_count++;
\r
1989 (void) nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_EXIT, NSSM_HOOK_ACTION_POST, NULL, NSSM_HOOK_DEADLINE, true);
\r
1992 The why argument is true if our wait timed out or false otherwise.
\r
1993 Our wait is infinite so why will never be true when called by the system.
\r
1994 If it is indeed true, assume we were called from stop_service() because
\r
1995 this is a controlled shutdown, and don't take any restart action.
\r
1998 if (! service->allow_restart) return;
\r
2000 /* What action should we take? */
\r
2001 int action = NSSM_EXIT_RESTART;
\r
2002 TCHAR action_string[ACTION_LEN];
\r
2003 bool default_action;
\r
2004 if (! get_exit_action(service->name, &exitcode, action_string, &default_action)) {
\r
2005 for (int i = 0; exit_action_strings[i]; i++) {
\r
2006 if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
2014 /* Try to restart the service or return failure code to service manager */
\r
2015 case NSSM_EXIT_RESTART:
\r
2016 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_RESTART, service->name, code, exit_action_strings[action], service->exe, 0);
\r
2017 while (monitor_service(service)) {
\r
2018 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_RESTART_SERVICE_FAILED, service->exe, service->name, 0);
\r
2023 /* Do nothing, just like srvany would */
\r
2024 case NSSM_EXIT_IGNORE:
\r
2025 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_IGNORE, service->name, code, exit_action_strings[action], service->exe, 0);
\r
2026 wait_for_hooks(service, false);
\r
2030 /* Tell the service manager we are finished */
\r
2031 case NSSM_EXIT_REALLY:
\r
2032 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_REALLY, service->name, code, exit_action_strings[action], 0);
\r
2033 stop_service(service, exitcode, true, default_action);
\r
2036 /* Fake a crash so pre-Vista service managers will run recovery actions. */
\r
2037 case NSSM_EXIT_UNCLEAN:
\r
2038 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_UNCLEAN, service->name, code, exit_action_strings[action], 0);
\r
2039 stop_service(service, exitcode, false, default_action);
\r
2040 wait_for_hooks(service, false);
\r
2046 void throttle_restart(nssm_service_t *service) {
\r
2047 /* This can't be a restart if the service is already running. */
\r
2048 if (! service->throttle++) return;
\r
2051 unsigned long throttle_ms = throttle_milliseconds(service->throttle);
\r
2052 TCHAR threshold[8], milliseconds[8];
\r
2054 if (service->restart_delay > throttle_ms) ms = service->restart_delay;
\r
2055 else ms = throttle_ms;
\r
2057 _sntprintf_s(milliseconds, _countof(milliseconds), _TRUNCATE, _T("%lu"), ms);
\r
2059 if (service->throttle == 1 && service->restart_delay > throttle_ms) log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESTART_DELAY, service->name, milliseconds, 0);
\r
2061 _sntprintf_s(threshold, _countof(threshold), _TRUNCATE, _T("%lu"), service->throttle_delay);
\r
2062 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_THROTTLED, service->name, threshold, milliseconds, 0);
\r
2065 if (use_critical_section) EnterCriticalSection(&service->throttle_section);
\r
2066 else if (service->throttle_timer) {
\r
2067 ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));
\r
2068 service->throttle_duetime.QuadPart = 0 - (ms * 10000LL);
\r
2069 SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);
\r
2072 service->status.dwCurrentState = SERVICE_PAUSED;
\r
2073 service->status.dwControlsAccepted |= SERVICE_ACCEPT_PAUSE_CONTINUE;
\r
2074 SetServiceStatus(service->status_handle, &service->status);
\r
2076 if (use_critical_section) {
\r
2077 imports.SleepConditionVariableCS(&service->throttle_condition, &service->throttle_section, ms);
\r
2078 LeaveCriticalSection(&service->throttle_section);
\r
2081 if (service->throttle_timer) WaitForSingleObject(service->throttle_timer, INFINITE);
\r
2087 When responding to a stop (or any other) request we need to set dwWaitHint to
\r
2088 the number of milliseconds we expect the operation to take, and optionally
\r
2089 increase dwCheckPoint. If dwWaitHint milliseconds elapses without the
\r
2090 operation completing or dwCheckPoint increasing, the system will consider the
\r
2091 service to be hung.
\r
2093 However the system will consider the service to be hung after 30000
\r
2094 milliseconds regardless of the value of dwWaitHint if dwCheckPoint has not
\r
2095 changed. Therefore if we want to wait longer than that we must periodically
\r
2096 increase dwCheckPoint.
\r
2098 Furthermore, it will consider the service to be hung after 60000 milliseconds
\r
2099 regardless of the value of dwCheckPoint unless dwWaitHint is increased every
\r
2100 time dwCheckPoint is also increased.
\r
2102 Our strategy then is to retrieve the initial dwWaitHint and wait for
\r
2103 NSSM_SERVICE_STATUS_DEADLINE milliseconds. If the process is still running
\r
2104 and we haven't finished waiting we increment dwCheckPoint and add whichever is
\r
2105 smaller of NSSM_SERVICE_STATUS_DEADLINE or the remaining timeout to
\r
2108 Only doing both these things will prevent the system from killing the service.
\r
2110 If the status_handle and service_status arguments are omitted, this function
\r
2111 will not try to update the service manager but it will still log to the
\r
2112 event log that it is waiting for a handle.
\r
2114 Returns: 1 if the wait timed out.
\r
2115 0 if the wait completed.
\r
2118 int await_single_handle(SERVICE_STATUS_HANDLE status_handle, SERVICE_STATUS *status, HANDLE handle, TCHAR *name, TCHAR *function_name, unsigned long timeout) {
\r
2119 unsigned long interval;
\r
2120 unsigned long ret;
\r
2121 unsigned long waited;
\r
2122 TCHAR interval_milliseconds[16];
\r
2123 TCHAR timeout_milliseconds[16];
\r
2124 TCHAR waited_milliseconds[16];
\r
2125 TCHAR *function = function_name;
\r
2127 /* Add brackets to function name. */
\r
2128 size_t funclen = _tcslen(function_name) + 3;
\r
2129 TCHAR *func = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, funclen * sizeof(TCHAR));
\r
2131 if (_sntprintf_s(func, funclen, _TRUNCATE, _T("%s()"), function_name) > -1) function = func;
\r
2134 _sntprintf_s(timeout_milliseconds, _countof(timeout_milliseconds), _TRUNCATE, _T("%lu"), timeout);
\r
2137 while (waited < timeout) {
\r
2138 interval = timeout - waited;
\r
2139 if (interval > NSSM_SERVICE_STATUS_DEADLINE) interval = NSSM_SERVICE_STATUS_DEADLINE;
\r
2142 status->dwWaitHint += interval;
\r
2143 status->dwCheckPoint++;
\r
2144 SetServiceStatus(status_handle, status);
\r
2148 _sntprintf_s(waited_milliseconds, _countof(waited_milliseconds), _TRUNCATE, _T("%lu"), waited);
\r
2149 _sntprintf_s(interval_milliseconds, _countof(interval_milliseconds), _TRUNCATE, _T("%lu"), interval);
\r
2150 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_AWAITING_SINGLE_HANDLE, function, name, waited_milliseconds, interval_milliseconds, timeout_milliseconds, 0);
\r
2153 switch (WaitForSingleObject(handle, interval)) {
\r
2154 case WAIT_OBJECT_0:
\r
2158 case WAIT_TIMEOUT:
\r
2167 waited += interval;
\r
2171 if (func) HeapFree(GetProcessHeap(), 0, func);
\r
2176 int list_nssm_services() {
\r
2177 /* Open service manager. */
\r
2178 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
\r
2180 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
2184 unsigned long bufsize, required, count, i;
\r
2185 unsigned long resume = 0;
\r
2186 EnumServicesStatus(services, SERVICE_WIN32, SERVICE_STATE_ALL, 0, 0, &required, &count, &resume);
\r
2187 unsigned long error = GetLastError();
\r
2188 if (error != ERROR_MORE_DATA) {
\r
2189 print_message(stderr, NSSM_MESSAGE_ENUMSERVICESSTATUS_FAILED, error_string(GetLastError()));
\r
2193 ENUM_SERVICE_STATUS *status = (ENUM_SERVICE_STATUS *) HeapAlloc(GetProcessHeap(), 0, required);
\r
2195 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("ENUM_SERVICE_STATUS"), _T("list_nssm_services()"));
\r
2199 bufsize = required;
\r
2201 int ret = EnumServicesStatus(services, SERVICE_WIN32, SERVICE_STATE_ALL, status, bufsize, &required, &count, &resume);
\r
2203 error = GetLastError();
\r
2204 if (error != ERROR_MORE_DATA) {
\r
2205 HeapFree(GetProcessHeap(), 0, status);
\r
2206 print_message(stderr, NSSM_MESSAGE_ENUMSERVICESSTATUS_FAILED, error_string(GetLastError()));
\r
2211 for (i = 0; i < count; i++) {
\r
2212 /* Try to get the service parameters. */
\r
2213 nssm_service_t *service = alloc_nssm_service();
\r
2215 HeapFree(GetProcessHeap(), 0, status);
\r
2216 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("nssm_service_t"), _T("list_nssm_services()"));
\r
2219 _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), status[i].lpServiceName);
\r
2221 get_parameters(service, 0);
\r
2222 /* We manage the service if we have an Application. */
\r
2223 if (service->exe[0]) _tprintf(_T("%s\n"), service->name);
\r
2225 cleanup_nssm_service(service);
\r
2231 HeapFree(GetProcessHeap(), 0, status);
\r