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_DUMPING } 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 else if (str_equiv(verb, _T("dump"))) mode = MODE_DUMPING;
\r
910 if (argc < mandatory) return usage(1);
\r
912 const TCHAR *parameter = 0;
\r
913 settings_t *setting = 0;
\r
916 /* Validate the parameter. */
\r
917 if (mandatory > 2) {
\r
918 bool additional_mandatory = false;
\r
920 parameter = argv[2];
\r
921 for (i = 0; settings[i].name; i++) {
\r
922 setting = &settings[i];
\r
923 if (! str_equiv(setting->name, parameter)) continue;
\r
924 if (((setting->additional & ADDITIONAL_GETTING) && mode == MODE_GETTING) || ((setting->additional & ADDITIONAL_SETTING) && mode == MODE_SETTING) || ((setting->additional & ADDITIONAL_RESETTING) && mode == MODE_RESETTING)) {
\r
925 additional_mandatory = true;
\r
930 if (! settings[i].name) {
\r
931 print_message(stderr, NSSM_MESSAGE_INVALID_PARAMETER, parameter);
\r
932 for (i = 0; settings[i].name; i++) _ftprintf(stderr, _T("%s\n"), settings[i].name);
\r
937 if (additional_mandatory) {
\r
938 if (argc < mandatory) {
\r
939 print_message(stderr, NSSM_MESSAGE_MISSING_SUBPARAMETER, parameter);
\r
942 additional = argv[3];
\r
945 else if (str_equiv(setting->name, NSSM_NATIVE_OBJECTNAME) && mode == MODE_SETTING) {
\r
946 additional = argv[3];
\r
950 additional = argv[remainder];
\r
951 if (argc < mandatory) return usage(1);
\r
955 nssm_service_t *service = alloc_nssm_service();
\r
956 _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), service_name);
\r
958 /* Open service manager */
\r
959 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
\r
961 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
965 /* Try to open the service */
\r
966 unsigned long access = SERVICE_QUERY_CONFIG;
\r
967 if (mode != MODE_GETTING) access |= SERVICE_CHANGE_CONFIG;
\r
968 service->handle = open_service(services, service->name, access, service->name, _countof(service->name));
\r
969 if (! service->handle) {
\r
970 CloseServiceHandle(services);
\r
974 /* Get system details. */
\r
975 QUERY_SERVICE_CONFIG *qsc = query_service_config(service->name, service->handle);
\r
977 CloseServiceHandle(service->handle);
\r
978 CloseServiceHandle(services);
\r
982 service->type = qsc->dwServiceType;
\r
983 if (! (service->type & SERVICE_WIN32_OWN_PROCESS)) {
\r
984 if (mode != MODE_GETTING && mode != MODE_DUMPING) {
\r
985 HeapFree(GetProcessHeap(), 0, qsc);
\r
986 CloseServiceHandle(service->handle);
\r
987 CloseServiceHandle(services);
\r
988 print_message(stderr, NSSM_MESSAGE_CANNOT_EDIT, service->name, NSSM_WIN32_OWN_PROCESS, 0);
\r
993 if (get_service_startup(service->name, service->handle, qsc, &service->startup)) {
\r
994 if (mode != MODE_GETTING && mode != MODE_DUMPING) {
\r
995 HeapFree(GetProcessHeap(), 0, qsc);
\r
996 CloseServiceHandle(service->handle);
\r
997 CloseServiceHandle(services);
\r
1002 if (get_service_username(service->name, qsc, &service->username, &service->usernamelen)) {
\r
1003 if (mode != MODE_GETTING && mode != MODE_DUMPING) {
\r
1004 HeapFree(GetProcessHeap(), 0, qsc);
\r
1005 CloseServiceHandle(service->handle);
\r
1006 CloseServiceHandle(services);
\r
1011 _sntprintf_s(service->displayname, _countof(service->displayname), _TRUNCATE, _T("%s"), qsc->lpDisplayName);
\r
1013 /* Get the canonical service name. We open it case insensitively. */
\r
1014 unsigned long bufsize = _countof(service->name);
\r
1015 GetServiceKeyName(services, service->displayname, service->name, &bufsize);
\r
1017 /* Remember the executable in case it isn't NSSM. */
\r
1018 _sntprintf_s(service->image, _countof(service->image), _TRUNCATE, _T("%s"), qsc->lpBinaryPathName);
\r
1019 HeapFree(GetProcessHeap(), 0, qsc);
\r
1021 /* Get extended system details. */
\r
1022 if (get_service_description(service->name, service->handle, _countof(service->description), service->description)) {
\r
1023 if (mode != MODE_GETTING && mode != MODE_DUMPING) {
\r
1024 CloseServiceHandle(service->handle);
\r
1025 CloseServiceHandle(services);
\r
1030 if (get_service_dependencies(service->name, service->handle, &service->dependencies, &service->dependencieslen)) {
\r
1031 if (mode != MODE_GETTING && mode != MODE_DUMPING) {
\r
1032 CloseServiceHandle(service->handle);
\r
1033 CloseServiceHandle(services);
\r
1038 /* Get NSSM details. */
\r
1039 get_parameters(service, 0);
\r
1041 CloseServiceHandle(services);
\r
1043 if (! service->exe[0]) {
\r
1044 service->native = true;
\r
1045 if (mode != MODE_GETTING && mode != MODE_DUMPING) print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE, service->name, NSSM, service->image);
\r
1048 /* Editing with the GUI. */
\r
1049 if (mode == MODE_EDITING) {
\r
1050 nssm_gui(IDD_EDIT, service);
\r
1058 if (mode == MODE_DUMPING) {
\r
1059 if (service->native) key = 0;
\r
1061 key = open_registry(service->name, KEY_READ);
\r
1062 if (! key) return 4;
\r
1066 for (i = 0; settings[i].name; i++) {
\r
1067 setting = &settings[i];
\r
1068 if (! setting->native && service->native) continue;
\r
1069 if (dump_setting(service->name, key, service->handle, setting)) ret++;
\r
1072 if (! service->native) RegCloseKey(key);
\r
1073 CloseServiceHandle(service->handle);
\r
1075 if (ret) return 1;
\r
1079 /* Trying to manage App* parameters for a non-NSSM service. */
\r
1080 if (! setting->native && service->native) {
\r
1081 CloseServiceHandle(service->handle);
\r
1082 print_message(stderr, NSSM_MESSAGE_NATIVE_PARAMETER, setting->name, NSSM);
\r
1086 if (mode == MODE_GETTING) {
\r
1087 if (! service->native) {
\r
1088 key = open_registry(service->name, KEY_READ);
\r
1089 if (! key) return 4;
\r
1092 if (setting->native) ret = get_setting(service->name, service->handle, setting, &value, additional);
\r
1093 else ret = get_setting(service->name, key, setting, &value, additional);
\r
1095 CloseServiceHandle(service->handle);
\r
1099 switch (setting->type) {
\r
1100 case REG_EXPAND_SZ:
\r
1101 case REG_MULTI_SZ:
\r
1103 _tprintf(_T("%s\n"), value.string ? value.string : _T(""));
\r
1104 HeapFree(GetProcessHeap(), 0, value.string);
\r
1108 _tprintf(_T("%lu\n"), value.numeric);
\r
1112 if (! service->native) RegCloseKey(key);
\r
1113 CloseServiceHandle(service->handle);
\r
1117 /* Build the value. */
\r
1118 if (mode == MODE_RESETTING) {
\r
1119 /* Unset the parameter. */
\r
1122 else if (remainder == argc) {
\r
1126 /* Set the parameter. */
\r
1128 size_t delimiterlen = (setting->additional & ADDITIONAL_CRLF) ? 2 : 1;
\r
1129 for (i = remainder; i < argc; i++) len += _tcslen(argv[i]) + delimiterlen;
\r
1132 value.string = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, len * sizeof(TCHAR));
\r
1133 if (! value.string) {
\r
1134 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("value"), _T("edit_service()"));
\r
1135 CloseServiceHandle(service->handle);
\r
1140 for (i = remainder; i < argc; i++) {
\r
1141 size_t len = _tcslen(argv[i]);
\r
1142 memmove(value.string + s, argv[i], len * sizeof(TCHAR));
\r
1144 if (i < argc - 1) {
\r
1145 if (setting->additional & ADDITIONAL_CRLF) {
\r
1146 value.string[s++] = _T('\r');
\r
1147 value.string[s++] = _T('\n');
\r
1149 else value.string[s++] = _T(' ');
\r
1152 value.string[s] = _T('\0');
\r
1155 if (! service->native) {
\r
1156 key = open_registry(service->name, KEY_READ | KEY_WRITE);
\r
1158 if (value.string) HeapFree(GetProcessHeap(), 0, value.string);
\r
1163 if (setting->native) ret = set_setting(service->name, service->handle, setting, &value, additional);
\r
1164 else ret = set_setting(service->name, key, setting, &value, additional);
\r
1165 if (value.string) HeapFree(GetProcessHeap(), 0, value.string);
\r
1167 if (! service->native) RegCloseKey(key);
\r
1168 CloseServiceHandle(service->handle);
\r
1172 if (! service->native) RegCloseKey(key);
\r
1173 CloseServiceHandle(service->handle);
\r
1178 /* About to remove the service */
\r
1179 int pre_remove_service(int argc, TCHAR **argv) {
\r
1180 nssm_service_t *service = alloc_nssm_service();
\r
1181 set_nssm_service_defaults(service);
\r
1182 if (argc) _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]);
\r
1184 /* Show dialogue box if we didn't pass service name and "confirm" */
\r
1185 if (argc < 2) return nssm_gui(IDD_REMOVE, service);
\r
1186 if (str_equiv(argv[1], _T("confirm"))) {
\r
1187 int ret = remove_service(service);
\r
1188 cleanup_nssm_service(service);
\r
1191 print_message(stderr, NSSM_MESSAGE_PRE_REMOVE_SERVICE);
\r
1195 /* Install the service */
\r
1196 int install_service(nssm_service_t *service) {
\r
1197 if (! service) return 1;
\r
1199 /* Open service manager */
\r
1200 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE);
\r
1202 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
1203 cleanup_nssm_service(service);
\r
1207 /* Get path of this program */
\r
1208 _sntprintf_s(service->image, _countof(service->image), _TRUNCATE, _T("%s"), nssm_imagepath());
\r
1210 /* Create the service - settings will be changed in edit_service() */
\r
1211 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
1212 if (! service->handle) {
\r
1213 print_message(stderr, NSSM_MESSAGE_CREATESERVICE_FAILED, error_string(GetLastError()));
\r
1214 CloseServiceHandle(services);
\r
1218 if (edit_service(service, false)) {
\r
1219 DeleteService(service->handle);
\r
1220 CloseServiceHandle(services);
\r
1224 print_message(stdout, NSSM_MESSAGE_SERVICE_INSTALLED, service->name);
\r
1227 CloseServiceHandle(services);
\r
1232 /* Edit the service. */
\r
1233 int edit_service(nssm_service_t *service, bool editing) {
\r
1234 if (! service) return 1;
\r
1237 The only two valid flags for service type are SERVICE_WIN32_OWN_PROCESS
\r
1238 and SERVICE_INTERACTIVE_PROCESS.
\r
1240 service->type &= SERVICE_INTERACTIVE_PROCESS;
\r
1241 service->type |= SERVICE_WIN32_OWN_PROCESS;
\r
1243 /* Startup type. */
\r
1244 unsigned long startup;
\r
1245 switch (service->startup) {
\r
1246 case NSSM_STARTUP_MANUAL: startup = SERVICE_DEMAND_START; break;
\r
1247 case NSSM_STARTUP_DISABLED: startup = SERVICE_DISABLED; break;
\r
1248 default: startup = SERVICE_AUTO_START;
\r
1251 /* Display name. */
\r
1252 if (! service->displayname[0]) _sntprintf_s(service->displayname, _countof(service->displayname), _TRUNCATE, _T("%s"), service->name);
\r
1255 Username must be NULL if we aren't changing or an account name.
\r
1256 We must explicitly use LOCALSYSTEM to change it when we are editing.
\r
1257 Password must be NULL if we aren't changing, a password or "".
\r
1258 Empty passwords are valid but we won't allow them in the GUI.
\r
1260 TCHAR *username = 0;
\r
1262 TCHAR *password = 0;
\r
1263 if (service->usernamelen) {
\r
1264 username = service->username;
\r
1265 if (canonicalise_username(username, &canon)) return 5;
\r
1266 if (service->passwordlen) password = service->password;
\r
1268 else if (editing) username = canon = NSSM_LOCALSYSTEM_ACCOUNT;
\r
1270 if (well_known_username(canon)) password = _T("");
\r
1272 if (grant_logon_as_service(canon)) {
\r
1273 if (canon != username) HeapFree(GetProcessHeap(), 0, canon);
\r
1274 print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username);
\r
1279 TCHAR *dependencies = _T("");
\r
1280 if (service->dependencieslen) dependencies = 0; /* Change later. */
\r
1282 if (! ChangeServiceConfig(service->handle, service->type, startup, SERVICE_NO_CHANGE, 0, 0, 0, dependencies, canon, password, service->displayname)) {
\r
1283 if (canon != username) HeapFree(GetProcessHeap(), 0, canon);
\r
1284 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
\r
1287 if (canon != username) HeapFree(GetProcessHeap(), 0, canon);
\r
1289 if (service->dependencieslen) {
\r
1290 if (set_service_dependencies(service->name, service->handle, service->dependencies)) return 5;
\r
1293 if (service->description[0] || editing) {
\r
1294 set_service_description(service->name, service->handle, service->description);
\r
1297 SERVICE_DELAYED_AUTO_START_INFO delayed;
\r
1298 ZeroMemory(&delayed, sizeof(delayed));
\r
1299 if (service->startup == NSSM_STARTUP_DELAYED) delayed.fDelayedAutostart = 1;
\r
1300 else delayed.fDelayedAutostart = 0;
\r
1301 /* Delayed startup isn't supported until Vista. */
\r
1302 if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, &delayed)) {
\r
1303 unsigned long error = GetLastError();
\r
1304 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
\r
1305 if (error != ERROR_INVALID_LEVEL) {
\r
1306 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_DELAYED_AUTO_START_INFO_FAILED, service->name, error_string(error), 0);
\r
1310 /* Don't mess with parameters which aren't ours. */
\r
1311 if (! service->native) {
\r
1312 /* Now we need to put the parameters into the registry */
\r
1313 if (create_parameters(service, editing)) {
\r
1314 print_message(stderr, NSSM_MESSAGE_CREATE_PARAMETERS_FAILED);
\r
1318 set_service_recovery(service);
\r
1324 /* Control a service. */
\r
1325 int control_service(unsigned long control, int argc, TCHAR **argv) {
\r
1326 if (argc < 1) return usage(1);
\r
1327 TCHAR *service_name = argv[0];
\r
1328 TCHAR canonical_name[SERVICE_NAME_LENGTH];
\r
1330 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
\r
1332 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
1336 unsigned long access = SERVICE_QUERY_STATUS;
\r
1337 switch (control) {
\r
1338 case NSSM_SERVICE_CONTROL_START:
\r
1339 access |= SERVICE_START;
\r
1342 case SERVICE_CONTROL_CONTINUE:
\r
1343 case SERVICE_CONTROL_PAUSE:
\r
1344 access |= SERVICE_PAUSE_CONTINUE;
\r
1347 case SERVICE_CONTROL_STOP:
\r
1348 access |= SERVICE_STOP;
\r
1351 case NSSM_SERVICE_CONTROL_ROTATE:
\r
1352 access |= SERVICE_USER_DEFINED_CONTROL;
\r
1356 SC_HANDLE service_handle = open_service(services, service_name, access, canonical_name, _countof(canonical_name));
\r
1357 if (! service_handle) {
\r
1358 CloseServiceHandle(services);
\r
1363 unsigned long error;
\r
1364 SERVICE_STATUS service_status;
\r
1365 if (control == NSSM_SERVICE_CONTROL_START) {
\r
1366 unsigned long initial_status = SERVICE_STOPPED;
\r
1367 ret = StartService(service_handle, (unsigned long) argc, (const TCHAR **) argv);
\r
1368 error = GetLastError();
\r
1369 CloseServiceHandle(services);
\r
1371 if (error == ERROR_IO_PENDING) {
\r
1373 Older versions of Windows return immediately with ERROR_IO_PENDING
\r
1374 indicate that the operation is still in progress. Newer versions
\r
1375 will return it if there really is a delay.
\r
1378 error = ERROR_SUCCESS;
\r
1382 int response = await_service_control_response(control, service_handle, &service_status, initial_status);
\r
1383 CloseServiceHandle(service_handle);
\r
1386 print_message(stderr, NSSM_MESSAGE_BAD_CONTROL_RESPONSE, canonical_name, service_status_text(service_status.dwCurrentState), service_control_text(control));
\r
1389 else _tprintf(_T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1393 CloseServiceHandle(service_handle);
\r
1394 _ftprintf(stderr, _T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1398 else if (control == SERVICE_CONTROL_INTERROGATE) {
\r
1400 We could actually send an INTERROGATE control but that won't return
\r
1401 any information if the service is stopped and we don't care about
\r
1402 the extra details it might give us in any case. So we'll fake it.
\r
1404 ret = QueryServiceStatus(service_handle, &service_status);
\r
1405 error = GetLastError();
\r
1408 _tprintf(_T("%s\n"), service_status_text(service_status.dwCurrentState));
\r
1412 _ftprintf(stderr, _T("%s: %s\n"), canonical_name, error_string(error));
\r
1417 ret = ControlService(service_handle, control, &service_status);
\r
1418 unsigned long initial_status = service_status.dwCurrentState;
\r
1419 error = GetLastError();
\r
1420 CloseServiceHandle(services);
\r
1422 if (error == ERROR_IO_PENDING) {
\r
1424 error = ERROR_SUCCESS;
\r
1428 int response = await_service_control_response(control, service_handle, &service_status, initial_status);
\r
1429 CloseServiceHandle(service_handle);
\r
1432 print_message(stderr, NSSM_MESSAGE_BAD_CONTROL_RESPONSE, canonical_name, service_status_text(service_status.dwCurrentState), service_control_text(control));
\r
1435 else _tprintf(_T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1439 CloseServiceHandle(service_handle);
\r
1440 _ftprintf(stderr, _T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error));
\r
1441 if (error == ERROR_SERVICE_NOT_ACTIVE) {
\r
1442 if (control == SERVICE_CONTROL_SHUTDOWN || control == SERVICE_CONTROL_STOP) return 0;
\r
1449 /* Remove the service */
\r
1450 int remove_service(nssm_service_t *service) {
\r
1451 if (! service) return 1;
\r
1453 /* Open service manager */
\r
1454 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
\r
1456 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
1460 /* Try to open the service */
\r
1461 service->handle = open_service(services, service->name, DELETE, service->name, _countof(service->name));
\r
1462 if (! service->handle) {
\r
1463 CloseServiceHandle(services);
\r
1467 /* Get the canonical service name. We open it case insensitively. */
\r
1468 unsigned long bufsize = _countof(service->displayname);
\r
1469 GetServiceDisplayName(services, service->name, service->displayname, &bufsize);
\r
1470 bufsize = _countof(service->name);
\r
1471 GetServiceKeyName(services, service->displayname, service->name, &bufsize);
\r
1473 /* Try to delete the service */
\r
1474 if (! DeleteService(service->handle)) {
\r
1475 print_message(stderr, NSSM_MESSAGE_DELETESERVICE_FAILED);
\r
1476 CloseServiceHandle(services);
\r
1481 CloseServiceHandle(services);
\r
1483 print_message(stdout, NSSM_MESSAGE_SERVICE_REMOVED, service->name);
\r
1487 /* Service initialisation */
\r
1488 void WINAPI service_main(unsigned long argc, TCHAR **argv) {
\r
1489 nssm_service_t *service = alloc_nssm_service();
\r
1490 if (! service) return;
\r
1492 if (_sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]) < 0) {
\r
1493 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("service->name"), _T("service_main()"), 0);
\r
1497 /* We can use a condition variable in a critical section on Vista or later. */
\r
1498 if (imports.SleepConditionVariableCS && imports.WakeConditionVariable) use_critical_section = true;
\r
1499 else use_critical_section = false;
\r
1501 /* Initialise status */
\r
1502 ZeroMemory(&service->status, sizeof(service->status));
\r
1503 service->status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;
\r
1504 service->status.dwControlsAccepted = 0;
\r
1505 service->status.dwWin32ExitCode = NO_ERROR;
\r
1506 service->status.dwServiceSpecificExitCode = 0;
\r
1507 service->status.dwCheckPoint = 0;
\r
1508 service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;
\r
1510 /* Signal we AREN'T running the server */
\r
1511 service->process_handle = 0;
\r
1514 /* Register control handler */
\r
1515 service->status_handle = RegisterServiceCtrlHandlerEx(NSSM, service_control_handler, (void *) service);
\r
1516 if (! service->status_handle) {
\r
1517 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_REGISTERSERVICECTRLHANDER_FAILED, error_string(GetLastError()), 0);
\r
1521 log_service_control(service->name, 0, true);
\r
1523 service->status.dwCurrentState = SERVICE_START_PENDING;
\r
1524 service->status.dwWaitHint = service->throttle_delay + NSSM_WAITHINT_MARGIN;
\r
1525 SetServiceStatus(service->status_handle, &service->status);
\r
1528 /* Try to create the exit action parameters; we don't care if it fails */
\r
1529 create_exit_action(service->name, exit_action_strings[0], false);
\r
1531 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT);
\r
1533 service->handle = open_service(services, service->name, SERVICE_CHANGE_CONFIG, 0, 0);
\r
1534 set_service_recovery(service);
\r
1536 /* Remember our display name. */
\r
1537 unsigned long displayname_len = _countof(service->displayname);
\r
1538 GetServiceDisplayName(services, service->name, service->displayname, &displayname_len);
\r
1540 CloseServiceHandle(services);
\r
1544 /* Used for signalling a resume if the service pauses when throttled. */
\r
1545 if (use_critical_section) {
\r
1546 InitializeCriticalSection(&service->throttle_section);
\r
1547 service->throttle_section_initialised = true;
\r
1550 service->throttle_timer = CreateWaitableTimer(0, 1, 0);
\r
1551 if (! service->throttle_timer) {
\r
1552 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_CREATEWAITABLETIMER_FAILED, service->name, error_string(GetLastError()), 0);
\r
1556 /* Critical section for hooks. */
\r
1557 InitializeCriticalSection(&service->hook_section);
\r
1558 service->hook_section_initialised = true;
\r
1560 /* Remember our initial environment. */
\r
1561 service->initial_env = copy_environment();
\r
1563 /* Remember our creation time. */
\r
1564 if (get_process_creation_time(GetCurrentProcess(), &service->nssm_creation_time)) ZeroMemory(&service->nssm_creation_time, sizeof(service->nssm_creation_time));
\r
1566 service->allow_restart = true;
\r
1567 if (! CreateThread(NULL, 0, launch_service, (void *) service, 0, NULL)) {
\r
1568 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETHREAD_FAILED, error_string(GetLastError()), 0);
\r
1569 stop_service(service, 0, true, true);
\r
1573 /* Make sure service recovery actions are taken where necessary */
\r
1574 void set_service_recovery(nssm_service_t *service) {
\r
1575 SERVICE_FAILURE_ACTIONS_FLAG flag;
\r
1576 ZeroMemory(&flag, sizeof(flag));
\r
1577 flag.fFailureActionsOnNonCrashFailures = true;
\r
1579 /* This functionality was added in Vista so the call may fail */
\r
1580 if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, &flag)) {
\r
1581 unsigned long error = GetLastError();
\r
1582 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
\r
1583 if (error != ERROR_INVALID_LEVEL) {
\r
1584 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_FAILURE_ACTIONS_FAILED, service->name, error_string(error), 0);
\r
1589 int monitor_service(nssm_service_t *service) {
\r
1590 /* Set service status to started */
\r
1591 int ret = start_service(service);
\r
1594 _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%d"), ret);
\r
1595 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_START_SERVICE_FAILED, service->exe, service->name, ret, 0);
\r
1598 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_STARTED_SERVICE, service->exe, service->flags, service->name, service->dir, 0);
\r
1600 /* Monitor service */
\r
1601 if (! RegisterWaitForSingleObject(&service->wait_handle, service->process_handle, end_service, (void *) service, INFINITE, WT_EXECUTEONLYONCE | WT_EXECUTELONGFUNCTION)) {
\r
1602 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_REGISTERWAITFORSINGLEOBJECT_FAILED, service->name, service->exe, error_string(GetLastError()), 0);
\r
1608 TCHAR *service_control_text(unsigned long control) {
\r
1609 switch (control) {
\r
1610 /* HACK: there is no SERVICE_CONTROL_START constant */
\r
1611 case NSSM_SERVICE_CONTROL_START: return _T("START");
\r
1612 case SERVICE_CONTROL_STOP: return _T("STOP");
\r
1613 case SERVICE_CONTROL_SHUTDOWN: return _T("SHUTDOWN");
\r
1614 case SERVICE_CONTROL_PAUSE: return _T("PAUSE");
\r
1615 case SERVICE_CONTROL_CONTINUE: return _T("CONTINUE");
\r
1616 case SERVICE_CONTROL_INTERROGATE: return _T("INTERROGATE");
\r
1617 case NSSM_SERVICE_CONTROL_ROTATE: return _T("ROTATE");
\r
1618 case SERVICE_CONTROL_POWEREVENT: return _T("POWEREVENT");
\r
1619 default: return 0;
\r
1623 TCHAR *service_status_text(unsigned long status) {
\r
1625 case SERVICE_STOPPED: return _T("SERVICE_STOPPED");
\r
1626 case SERVICE_START_PENDING: return _T("SERVICE_START_PENDING");
\r
1627 case SERVICE_STOP_PENDING: return _T("SERVICE_STOP_PENDING");
\r
1628 case SERVICE_RUNNING: return _T("SERVICE_RUNNING");
\r
1629 case SERVICE_CONTINUE_PENDING: return _T("SERVICE_CONTINUE_PENDING");
\r
1630 case SERVICE_PAUSE_PENDING: return _T("SERVICE_PAUSE_PENDING");
\r
1631 case SERVICE_PAUSED: return _T("SERVICE_PAUSED");
\r
1632 default: return 0;
\r
1636 void log_service_control(TCHAR *service_name, unsigned long control, bool handled) {
\r
1637 TCHAR *text = service_control_text(control);
\r
1638 unsigned long event;
\r
1641 /* "0x" + 8 x hex + NULL */
\r
1642 text = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, 11 * sizeof(TCHAR));
\r
1644 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);
\r
1647 if (_sntprintf_s(text, 11, _TRUNCATE, _T("0x%08x"), control) < 0) {
\r
1648 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);
\r
1649 HeapFree(GetProcessHeap(), 0, text);
\r
1653 event = NSSM_EVENT_SERVICE_CONTROL_UNKNOWN;
\r
1655 else if (handled) event = NSSM_EVENT_SERVICE_CONTROL_HANDLED;
\r
1656 else event = NSSM_EVENT_SERVICE_CONTROL_NOT_HANDLED;
\r
1658 log_event(EVENTLOG_INFORMATION_TYPE, event, service_name, text, 0);
\r
1660 if (event == NSSM_EVENT_SERVICE_CONTROL_UNKNOWN) {
\r
1661 HeapFree(GetProcessHeap(), 0, text);
\r
1665 /* Service control handler */
\r
1666 unsigned long WINAPI service_control_handler(unsigned long control, unsigned long event, void *data, void *context) {
\r
1667 nssm_service_t *service = (nssm_service_t *) context;
\r
1669 switch (control) {
\r
1670 case SERVICE_CONTROL_INTERROGATE:
\r
1671 /* We always keep the service status up-to-date so this is a no-op. */
\r
1674 case SERVICE_CONTROL_SHUTDOWN:
\r
1675 case SERVICE_CONTROL_STOP:
\r
1676 service->last_control = control;
\r
1677 log_service_control(service->name, control, true);
\r
1679 /* Immediately block further controls. */
\r
1680 service->allow_restart = false;
\r
1681 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
1682 service->status.dwControlsAccepted = 0;
\r
1683 SetServiceStatus(service->status_handle, &service->status);
\r
1685 /* Pre-stop hook. */
\r
1686 nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_STOP, NSSM_HOOK_ACTION_PRE, &control, NSSM_SERVICE_STATUS_DEADLINE, false);
\r
1689 We MUST acknowledge the stop request promptly but we're committed to
\r
1690 waiting for the application to exit. Spawn a new thread to wait
\r
1691 while we acknowledge the request.
\r
1693 if (! CreateThread(NULL, 0, shutdown_service, context, 0, NULL)) {
\r
1694 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETHREAD_FAILED, error_string(GetLastError()), 0);
\r
1697 We couldn't create a thread to tidy up so we'll have to force the tidyup
\r
1698 to complete in time in this thread.
\r
1700 service->kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;
\r
1701 service->kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;
\r
1702 service->kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;
\r
1704 stop_service(service, 0, true, true);
\r
1708 case SERVICE_CONTROL_CONTINUE:
\r
1709 service->last_control = control;
\r
1710 log_service_control(service->name, control, true);
\r
1711 service->throttle = 0;
\r
1712 if (use_critical_section) imports.WakeConditionVariable(&service->throttle_condition);
\r
1714 if (! service->throttle_timer) return ERROR_CALL_NOT_IMPLEMENTED;
\r
1715 ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));
\r
1716 SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);
\r
1718 /* We can't continue if the application is running! */
\r
1719 if (! service->process_handle) service->status.dwCurrentState = SERVICE_CONTINUE_PENDING;
\r
1720 service->status.dwWaitHint = throttle_milliseconds(service->throttle) + NSSM_WAITHINT_MARGIN;
\r
1721 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESET_THROTTLE, service->name, 0);
\r
1722 SetServiceStatus(service->status_handle, &service->status);
\r
1725 case SERVICE_CONTROL_PAUSE:
\r
1727 We don't accept pause messages but it isn't possible to register
\r
1728 only for continue messages so we have to handle this case.
\r
1730 log_service_control(service->name, control, false);
\r
1731 return ERROR_CALL_NOT_IMPLEMENTED;
\r
1733 case NSSM_SERVICE_CONTROL_ROTATE:
\r
1734 service->last_control = control;
\r
1735 log_service_control(service->name, control, true);
\r
1736 (void) nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_ROTATE, NSSM_HOOK_ACTION_PRE, &control, NSSM_HOOK_DEADLINE, false);
\r
1737 if (service->rotate_stdout_online == NSSM_ROTATE_ONLINE) service->rotate_stdout_online = NSSM_ROTATE_ONLINE_ASAP;
\r
1738 if (service->rotate_stderr_online == NSSM_ROTATE_ONLINE) service->rotate_stderr_online = NSSM_ROTATE_ONLINE_ASAP;
\r
1739 (void) nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_ROTATE, NSSM_HOOK_ACTION_POST, &control);
\r
1742 case SERVICE_CONTROL_POWEREVENT:
\r
1743 /* Resume from suspend. */
\r
1744 if (event == PBT_APMRESUMEAUTOMATIC) {
\r
1745 service->last_control = control;
\r
1746 log_service_control(service->name, control, true);
\r
1747 (void) nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_POWER, NSSM_HOOK_ACTION_RESUME, &control);
\r
1751 /* Battery low or changed to A/C power or something. */
\r
1752 if (event == PBT_APMPOWERSTATUSCHANGE) {
\r
1753 service->last_control = control;
\r
1754 log_service_control(service->name, control, true);
\r
1755 (void) nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_POWER, NSSM_HOOK_ACTION_CHANGE, &control);
\r
1758 log_service_control(service->name, control, false);
\r
1762 /* Unknown control */
\r
1763 log_service_control(service->name, control, false);
\r
1764 return ERROR_CALL_NOT_IMPLEMENTED;
\r
1767 /* Start the service */
\r
1768 int start_service(nssm_service_t *service) {
\r
1769 service->stopping = false;
\r
1771 if (service->process_handle) return 0;
\r
1772 service->start_requested_count++;
\r
1774 /* Allocate a STARTUPINFO structure for a new process */
\r
1776 ZeroMemory(&si, sizeof(si));
\r
1777 si.cb = sizeof(si);
\r
1779 /* Allocate a PROCESSINFO structure for the process */
\r
1780 PROCESS_INFORMATION pi;
\r
1781 ZeroMemory(&pi, sizeof(pi));
\r
1783 /* Get startup parameters */
\r
1784 int ret = get_parameters(service, &si);
\r
1786 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_PARAMETERS_FAILED, service->name, 0);
\r
1787 unset_service_environment(service);
\r
1788 return stop_service(service, 2, true, true);
\r
1791 /* Launch executable with arguments */
\r
1792 TCHAR cmd[CMD_LENGTH];
\r
1793 if (_sntprintf_s(cmd, _countof(cmd), _TRUNCATE, _T("\"%s\" %s"), service->exe, service->flags) < 0) {
\r
1794 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("command line"), _T("start_service"), 0);
\r
1795 unset_service_environment(service);
\r
1796 return stop_service(service, 2, true, true);
\r
1799 throttle_restart(service);
\r
1801 service->status.dwCurrentState = SERVICE_START_PENDING;
\r
1802 service->status.dwControlsAccepted = SERVICE_ACCEPT_POWEREVENT | SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP;
\r
1803 SetServiceStatus(service->status_handle, &service->status);
\r
1805 unsigned long control = NSSM_SERVICE_CONTROL_START;
\r
1807 /* Did another thread receive a stop control? */
\r
1808 if (service->allow_restart) {
\r
1809 /* Set up I/O redirection. */
\r
1810 if (get_output_handles(service, &si)) {
\r
1811 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_OUTPUT_HANDLES_FAILED, service->name, 0);
\r
1812 if (! service->no_console) FreeConsole();
\r
1813 close_output_handles(&si);
\r
1814 unset_service_environment(service);
\r
1815 return stop_service(service, 4, true, true);
\r
1818 /* Pre-start hook. May need I/O to have been redirected already. */
\r
1819 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
1821 _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), NSSM_HOOK_STATUS_ABORT);
\r
1822 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_PRESTART_HOOK_ABORT, NSSM_HOOK_EVENT_START, NSSM_HOOK_ACTION_PRE, service->name, code, 0);
\r
1823 unset_service_environment(service);
\r
1824 return stop_service(service, 5, true, true);
\r
1827 /* The pre-start hook will have cleaned the environment. */
\r
1828 set_service_environment(service);
\r
1830 bool inherit_handles = false;
\r
1831 if (si.dwFlags & STARTF_USESTDHANDLES) inherit_handles = true;
\r
1832 unsigned long flags = service->priority & priority_mask();
\r
1833 if (service->affinity) flags |= CREATE_SUSPENDED;
\r
1834 if (! CreateProcess(0, cmd, 0, 0, inherit_handles, flags, 0, service->dir, &si, &pi)) {
\r
1835 unsigned long exitcode = 3;
\r
1836 unsigned long error = GetLastError();
\r
1837 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service->name, service->exe, error_string(error), 0);
\r
1838 close_output_handles(&si);
\r
1839 unset_service_environment(service);
\r
1840 return stop_service(service, exitcode, true, true);
\r
1842 service->start_count++;
\r
1843 service->process_handle = pi.hProcess;
\r
1844 service->pid = pi.dwProcessId;
\r
1846 if (get_process_creation_time(service->process_handle, &service->creation_time)) ZeroMemory(&service->creation_time, sizeof(service->creation_time));
\r
1848 close_output_handles(&si);
\r
1850 if (! service->no_console) FreeConsole();
\r
1852 if (service->affinity) {
\r
1854 We are explicitly storing service->affinity as a 64-bit unsigned integer
\r
1855 so that we can parse it regardless of whether we're running in 32-bit
\r
1856 or 64-bit mode. The arguments to SetProcessAffinityMask(), however, are
\r
1857 defined as type DWORD_PTR and hence limited to 32 bits on a 32-bit system
\r
1858 (or when running the 32-bit NSSM).
\r
1860 The result is a lot of seemingly-unnecessary casting throughout the code
\r
1861 and potentially confusion when we actually try to start the service.
\r
1862 Having said that, however, it's unlikely that we're actually going to
\r
1863 run in 32-bit mode on a system which has more than 32 CPUs so the
\r
1864 likelihood of seeing a confusing situation is somewhat diminished.
\r
1866 DWORD_PTR affinity, system_affinity;
\r
1868 if (GetProcessAffinityMask(service->process_handle, &affinity, &system_affinity)) affinity = service->affinity & system_affinity;
\r
1870 affinity = (DWORD_PTR) service->affinity;
\r
1871 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GETPROCESSAFFINITYMASK_FAILED, service->name, error_string(GetLastError()), 0);
\r
1874 if (! SetProcessAffinityMask(service->process_handle, affinity)) {
\r
1875 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_SETPROCESSAFFINITYMASK_FAILED, service->name, error_string(GetLastError()), 0);
\r
1878 ResumeThread(pi.hThread);
\r
1882 /* Restore our environment. */
\r
1883 unset_service_environment(service);
\r
1886 Wait for a clean startup before changing the service status to RUNNING
\r
1887 but be mindful of the fact that we are blocking the service control manager
\r
1888 so abandon the wait before too much time has elapsed.
\r
1890 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
1892 /* Did another thread receive a stop control? */
\r
1893 if (! service->allow_restart) return 0;
\r
1895 /* Signal successful start */
\r
1896 service->status.dwCurrentState = SERVICE_RUNNING;
\r
1897 service->status.dwControlsAccepted &= ~SERVICE_ACCEPT_PAUSE_CONTINUE;
\r
1898 SetServiceStatus(service->status_handle, &service->status);
\r
1900 /* Post-start hook. */
\r
1901 if (! service->throttle) {
\r
1902 (void) nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_START, NSSM_HOOK_ACTION_POST, &control);
\r
1905 /* Ensure the restart delay is always applied. */
\r
1906 if (service->restart_delay && ! service->throttle) service->throttle++;
\r
1911 /* Stop the service */
\r
1912 int stop_service(nssm_service_t *service, unsigned long exitcode, bool graceful, bool default_action) {
\r
1913 service->allow_restart = false;
\r
1914 if (service->wait_handle) {
\r
1915 UnregisterWait(service->wait_handle);
\r
1916 service->wait_handle = 0;
\r
1919 service->rotate_stdout_online = service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;
\r
1921 if (default_action && ! exitcode && ! graceful) {
\r
1922 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
1926 /* Signal we are stopping */
\r
1928 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
1929 service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;
\r
1930 SetServiceStatus(service->status_handle, &service->status);
\r
1933 /* Nothing to do if service isn't running */
\r
1934 if (service->pid) {
\r
1935 /* Shut down service */
\r
1936 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_TERMINATEPROCESS, service->name, service->exe, 0);
\r
1938 service_kill_t(service, &k);
\r
1942 else log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_PROCESS_ALREADY_STOPPED, service->name, service->exe, 0);
\r
1944 end_service((void *) service, true);
\r
1946 /* Signal we stopped */
\r
1948 service->status.dwCurrentState = SERVICE_STOP_PENDING;
\r
1949 wait_for_hooks(service, true);
\r
1950 service->status.dwCurrentState = SERVICE_STOPPED;
\r
1952 service->status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
\r
1953 service->status.dwServiceSpecificExitCode = exitcode;
\r
1956 service->status.dwWin32ExitCode = NO_ERROR;
\r
1957 service->status.dwServiceSpecificExitCode = 0;
\r
1959 SetServiceStatus(service->status_handle, &service->status);
\r
1965 /* Callback function triggered when the server exits */
\r
1966 void CALLBACK end_service(void *arg, unsigned char why) {
\r
1967 nssm_service_t *service = (nssm_service_t *) arg;
\r
1969 if (service->stopping) return;
\r
1971 service->stopping = true;
\r
1973 service->rotate_stdout_online = service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;
\r
1975 /* Use now as a dummy exit time. */
\r
1976 GetSystemTimeAsFileTime(&service->exit_time);
\r
1978 /* Check exit code */
\r
1979 unsigned long exitcode = 0;
\r
1981 if (service->process_handle) {
\r
1982 GetExitCodeProcess(service->process_handle, &exitcode);
\r
1983 service->exitcode = exitcode;
\r
1984 /* Check real exit time. */
\r
1985 if (exitcode != STILL_ACTIVE) get_process_exit_time(service->process_handle, &service->exit_time);
\r
1986 CloseHandle(service->process_handle);
\r
1989 service->process_handle = 0;
\r
1992 Log that the service ended BEFORE logging about killing the process
\r
1993 tree. See below for the possible values of the why argument.
\r
1996 _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), exitcode);
\r
1997 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ENDED_SERVICE, service->exe, service->name, code, 0);
\r
2001 if (exitcode == STILL_ACTIVE) exitcode = 0;
\r
2002 if (service->pid && service->kill_process_tree) {
\r
2004 service_kill_t(service, &k);
\r
2005 kill_process_tree(&k, service->pid);
\r
2010 service->exit_count++;
\r
2011 (void) nssm_hook(&hook_threads, service, NSSM_HOOK_EVENT_EXIT, NSSM_HOOK_ACTION_POST, NULL, NSSM_HOOK_DEADLINE, true);
\r
2014 The why argument is true if our wait timed out or false otherwise.
\r
2015 Our wait is infinite so why will never be true when called by the system.
\r
2016 If it is indeed true, assume we were called from stop_service() because
\r
2017 this is a controlled shutdown, and don't take any restart action.
\r
2020 if (! service->allow_restart) return;
\r
2022 /* What action should we take? */
\r
2023 int action = NSSM_EXIT_RESTART;
\r
2024 TCHAR action_string[ACTION_LEN];
\r
2025 bool default_action;
\r
2026 if (! get_exit_action(service->name, &exitcode, action_string, &default_action)) {
\r
2027 for (int i = 0; exit_action_strings[i]; i++) {
\r
2028 if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {
\r
2036 /* Try to restart the service or return failure code to service manager */
\r
2037 case NSSM_EXIT_RESTART:
\r
2038 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_RESTART, service->name, code, exit_action_strings[action], service->exe, 0);
\r
2039 while (monitor_service(service)) {
\r
2040 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_RESTART_SERVICE_FAILED, service->exe, service->name, 0);
\r
2045 /* Do nothing, just like srvany would */
\r
2046 case NSSM_EXIT_IGNORE:
\r
2047 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_IGNORE, service->name, code, exit_action_strings[action], service->exe, 0);
\r
2048 wait_for_hooks(service, false);
\r
2052 /* Tell the service manager we are finished */
\r
2053 case NSSM_EXIT_REALLY:
\r
2054 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_REALLY, service->name, code, exit_action_strings[action], 0);
\r
2055 stop_service(service, exitcode, true, default_action);
\r
2058 /* Fake a crash so pre-Vista service managers will run recovery actions. */
\r
2059 case NSSM_EXIT_UNCLEAN:
\r
2060 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_UNCLEAN, service->name, code, exit_action_strings[action], 0);
\r
2061 stop_service(service, exitcode, false, default_action);
\r
2062 wait_for_hooks(service, false);
\r
2068 void throttle_restart(nssm_service_t *service) {
\r
2069 /* This can't be a restart if the service is already running. */
\r
2070 if (! service->throttle++) return;
\r
2073 unsigned long throttle_ms = throttle_milliseconds(service->throttle);
\r
2074 TCHAR threshold[8], milliseconds[8];
\r
2076 if (service->restart_delay > throttle_ms) ms = service->restart_delay;
\r
2077 else ms = throttle_ms;
\r
2079 _sntprintf_s(milliseconds, _countof(milliseconds), _TRUNCATE, _T("%lu"), ms);
\r
2081 if (service->throttle == 1 && service->restart_delay > throttle_ms) log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESTART_DELAY, service->name, milliseconds, 0);
\r
2083 _sntprintf_s(threshold, _countof(threshold), _TRUNCATE, _T("%lu"), service->throttle_delay);
\r
2084 log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_THROTTLED, service->name, threshold, milliseconds, 0);
\r
2087 if (use_critical_section) EnterCriticalSection(&service->throttle_section);
\r
2088 else if (service->throttle_timer) {
\r
2089 ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));
\r
2090 service->throttle_duetime.QuadPart = 0 - (ms * 10000LL);
\r
2091 SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);
\r
2094 service->status.dwCurrentState = SERVICE_PAUSED;
\r
2095 service->status.dwControlsAccepted |= SERVICE_ACCEPT_PAUSE_CONTINUE;
\r
2096 SetServiceStatus(service->status_handle, &service->status);
\r
2098 if (use_critical_section) {
\r
2099 imports.SleepConditionVariableCS(&service->throttle_condition, &service->throttle_section, ms);
\r
2100 LeaveCriticalSection(&service->throttle_section);
\r
2103 if (service->throttle_timer) WaitForSingleObject(service->throttle_timer, INFINITE);
\r
2109 When responding to a stop (or any other) request we need to set dwWaitHint to
\r
2110 the number of milliseconds we expect the operation to take, and optionally
\r
2111 increase dwCheckPoint. If dwWaitHint milliseconds elapses without the
\r
2112 operation completing or dwCheckPoint increasing, the system will consider the
\r
2113 service to be hung.
\r
2115 However the system will consider the service to be hung after 30000
\r
2116 milliseconds regardless of the value of dwWaitHint if dwCheckPoint has not
\r
2117 changed. Therefore if we want to wait longer than that we must periodically
\r
2118 increase dwCheckPoint.
\r
2120 Furthermore, it will consider the service to be hung after 60000 milliseconds
\r
2121 regardless of the value of dwCheckPoint unless dwWaitHint is increased every
\r
2122 time dwCheckPoint is also increased.
\r
2124 Our strategy then is to retrieve the initial dwWaitHint and wait for
\r
2125 NSSM_SERVICE_STATUS_DEADLINE milliseconds. If the process is still running
\r
2126 and we haven't finished waiting we increment dwCheckPoint and add whichever is
\r
2127 smaller of NSSM_SERVICE_STATUS_DEADLINE or the remaining timeout to
\r
2130 Only doing both these things will prevent the system from killing the service.
\r
2132 If the status_handle and service_status arguments are omitted, this function
\r
2133 will not try to update the service manager but it will still log to the
\r
2134 event log that it is waiting for a handle.
\r
2136 Returns: 1 if the wait timed out.
\r
2137 0 if the wait completed.
\r
2140 int await_single_handle(SERVICE_STATUS_HANDLE status_handle, SERVICE_STATUS *status, HANDLE handle, TCHAR *name, TCHAR *function_name, unsigned long timeout) {
\r
2141 unsigned long interval;
\r
2142 unsigned long ret;
\r
2143 unsigned long waited;
\r
2144 TCHAR interval_milliseconds[16];
\r
2145 TCHAR timeout_milliseconds[16];
\r
2146 TCHAR waited_milliseconds[16];
\r
2147 TCHAR *function = function_name;
\r
2149 /* Add brackets to function name. */
\r
2150 size_t funclen = _tcslen(function_name) + 3;
\r
2151 TCHAR *func = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, funclen * sizeof(TCHAR));
\r
2153 if (_sntprintf_s(func, funclen, _TRUNCATE, _T("%s()"), function_name) > -1) function = func;
\r
2156 _sntprintf_s(timeout_milliseconds, _countof(timeout_milliseconds), _TRUNCATE, _T("%lu"), timeout);
\r
2159 while (waited < timeout) {
\r
2160 interval = timeout - waited;
\r
2161 if (interval > NSSM_SERVICE_STATUS_DEADLINE) interval = NSSM_SERVICE_STATUS_DEADLINE;
\r
2164 status->dwWaitHint += interval;
\r
2165 status->dwCheckPoint++;
\r
2166 SetServiceStatus(status_handle, status);
\r
2170 _sntprintf_s(waited_milliseconds, _countof(waited_milliseconds), _TRUNCATE, _T("%lu"), waited);
\r
2171 _sntprintf_s(interval_milliseconds, _countof(interval_milliseconds), _TRUNCATE, _T("%lu"), interval);
\r
2172 log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_AWAITING_SINGLE_HANDLE, function, name, waited_milliseconds, interval_milliseconds, timeout_milliseconds, 0);
\r
2175 switch (WaitForSingleObject(handle, interval)) {
\r
2176 case WAIT_OBJECT_0:
\r
2180 case WAIT_TIMEOUT:
\r
2189 waited += interval;
\r
2193 if (func) HeapFree(GetProcessHeap(), 0, func);
\r
2198 int list_nssm_services() {
\r
2199 /* Open service manager. */
\r
2200 SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
\r
2202 print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
\r
2206 unsigned long bufsize, required, count, i;
\r
2207 unsigned long resume = 0;
\r
2208 EnumServicesStatus(services, SERVICE_WIN32, SERVICE_STATE_ALL, 0, 0, &required, &count, &resume);
\r
2209 unsigned long error = GetLastError();
\r
2210 if (error != ERROR_MORE_DATA) {
\r
2211 print_message(stderr, NSSM_MESSAGE_ENUMSERVICESSTATUS_FAILED, error_string(GetLastError()));
\r
2215 ENUM_SERVICE_STATUS *status = (ENUM_SERVICE_STATUS *) HeapAlloc(GetProcessHeap(), 0, required);
\r
2217 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("ENUM_SERVICE_STATUS"), _T("list_nssm_services()"));
\r
2221 bufsize = required;
\r
2223 int ret = EnumServicesStatus(services, SERVICE_WIN32, SERVICE_STATE_ALL, status, bufsize, &required, &count, &resume);
\r
2225 error = GetLastError();
\r
2226 if (error != ERROR_MORE_DATA) {
\r
2227 HeapFree(GetProcessHeap(), 0, status);
\r
2228 print_message(stderr, NSSM_MESSAGE_ENUMSERVICESSTATUS_FAILED, error_string(GetLastError()));
\r
2233 for (i = 0; i < count; i++) {
\r
2234 /* Try to get the service parameters. */
\r
2235 nssm_service_t *service = alloc_nssm_service();
\r
2237 HeapFree(GetProcessHeap(), 0, status);
\r
2238 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("nssm_service_t"), _T("list_nssm_services()"));
\r
2241 _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), status[i].lpServiceName);
\r
2243 get_parameters(service, 0);
\r
2244 /* We manage the service if we have an Application. */
\r
2245 if (service->exe[0]) _tprintf(_T("%s\n"), service->name);
\r
2247 cleanup_nssm_service(service);
\r
2253 HeapFree(GetProcessHeap(), 0, status);
\r