5 HANDLE process_handle;
\r
7 unsigned long deadline;
\r
8 FILETIME creation_time;
\r
12 static unsigned long WINAPI await_hook(void *arg) {
\r
13 hook_t *hook = (hook_t *) arg;
\r
14 if (! hook) return NSSM_HOOK_STATUS_ERROR;
\r
17 if (WaitForSingleObject(hook->process_handle, hook->deadline) == WAIT_TIMEOUT) ret = NSSM_HOOK_STATUS_TIMEOUT;
\r
19 /* Tidy up hook process tree. */
\r
20 if (hook->name) hook->k.name = hook->name;
\r
21 else hook->k.name = _T("hook");
\r
22 hook->k.process_handle = hook->process_handle;
\r
23 hook->k.pid = hook->pid;
\r
24 hook->k.stop_method = ~0;
\r
25 hook->k.kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;
\r
26 hook->k.kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;
\r
27 hook->k.kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;
\r
28 hook->k.creation_time = hook->creation_time;
\r
29 GetSystemTimeAsFileTime(&hook->k.exit_time);
\r
30 kill_process_tree(&hook->k, hook->pid);
\r
33 CloseHandle(hook->process_handle);
\r
34 if (hook->name) HeapFree(GetProcessHeap(), 0, hook->name);
\r
35 HeapFree(GetProcessHeap(), 0, hook);
\r
39 unsigned long exitcode;
\r
40 GetExitCodeProcess(hook->process_handle, &exitcode);
\r
41 CloseHandle(hook->process_handle);
\r
43 if (hook->name) HeapFree(GetProcessHeap(), 0, hook->name);
\r
44 HeapFree(GetProcessHeap(), 0, hook);
\r
46 if (exitcode == NSSM_HOOK_STATUS_ABORT) return NSSM_HOOK_STATUS_ABORT;
\r
47 if (exitcode) return NSSM_HOOK_STATUS_FAILED;
\r
49 return NSSM_HOOK_STATUS_SUCCESS;
\r
52 static void set_hook_runtime(TCHAR *v, FILETIME *start, FILETIME *now) {
\r
55 s.LowPart = start->dwLowDateTime;
\r
56 s.HighPart = start->dwHighDateTime;
\r
59 t.LowPart = now->dwLowDateTime;
\r
60 t.HighPart = now->dwHighDateTime;
\r
61 if (t.QuadPart && t.QuadPart >= s.QuadPart) {
\r
62 t.QuadPart -= s.QuadPart;
\r
63 t.QuadPart /= 10000LL;
\r
65 _sntprintf_s(number, _countof(number), _TRUNCATE, _T("%llu"), t.QuadPart);
\r
66 SetEnvironmentVariable(v, number);
\r
71 SetEnvironmentVariable(v, _T(""));
\r
74 static void add_thread_handle(hook_thread_t *hook_threads, HANDLE thread_handle, TCHAR *name) {
\r
75 if (! hook_threads) return;
\r
77 int num_threads = hook_threads->num_threads + 1;
\r
78 hook_thread_data_t *data = (hook_thread_data_t *) HeapAlloc(GetProcessHeap(), 0, num_threads * sizeof(hook_thread_data_t));
\r
80 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("hook_thread_t"), _T("add_thread_handle()"), 0);
\r
85 for (i = 0; i < hook_threads->num_threads; i++) memmove(&data[i], &hook_threads->data[i], sizeof(data[i]));
\r
86 memmove(data[i].name, name, sizeof(data[i].name));
\r
87 data[i].thread_handle = thread_handle;
\r
89 if (hook_threads->data) HeapFree(GetProcessHeap(), 0, hook_threads->data);
\r
90 hook_threads->data = data;
\r
91 hook_threads->num_threads = num_threads;
\r
94 bool valid_hook_name(const TCHAR *hook_event, const TCHAR *hook_action, bool quiet) {
\r
95 bool valid_event = false;
\r
96 bool valid_action = false;
\r
99 if (str_equiv(hook_event, NSSM_HOOK_EVENT_EXIT)) {
\r
100 if (str_equiv(hook_action, NSSM_HOOK_ACTION_POST)) return true;
\r
101 if (quiet) return false;
\r
102 print_message(stderr, NSSM_MESSAGE_INVALID_HOOK_ACTION, hook_event);
\r
103 _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_ACTION_POST);
\r
107 /* Power/{Change,Resume} */
\r
108 if (str_equiv(hook_event, NSSM_HOOK_EVENT_POWER)) {
\r
109 if (str_equiv(hook_action, NSSM_HOOK_ACTION_CHANGE)) return true;
\r
110 if (str_equiv(hook_action, NSSM_HOOK_ACTION_RESUME)) return true;
\r
111 if (quiet) return false;
\r
112 print_message(stderr, NSSM_MESSAGE_INVALID_HOOK_ACTION, hook_event);
\r
113 _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_ACTION_CHANGE);
\r
114 _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_ACTION_RESUME);
\r
118 /* Rotate/{Pre,Post} */
\r
119 if (str_equiv(hook_event, NSSM_HOOK_EVENT_ROTATE)) {
\r
120 if (str_equiv(hook_action, NSSM_HOOK_ACTION_PRE)) return true;
\r
121 if (str_equiv(hook_action, NSSM_HOOK_ACTION_POST)) return true;
\r
122 if (quiet) return false;
\r
123 print_message(stderr, NSSM_MESSAGE_INVALID_HOOK_ACTION, hook_event);
\r
124 _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_ACTION_PRE);
\r
125 _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_ACTION_POST);
\r
129 /* Start/{Pre,Post} */
\r
130 if (str_equiv(hook_event, NSSM_HOOK_EVENT_START)) {
\r
131 if (str_equiv(hook_action, NSSM_HOOK_ACTION_PRE)) return true;
\r
132 if (str_equiv(hook_action, NSSM_HOOK_ACTION_POST)) return true;
\r
133 if (quiet) return false;
\r
134 print_message(stderr, NSSM_MESSAGE_INVALID_HOOK_ACTION, hook_event);
\r
135 _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_ACTION_PRE);
\r
136 _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_ACTION_POST);
\r
141 if (str_equiv(hook_event, NSSM_HOOK_EVENT_STOP)) {
\r
142 if (str_equiv(hook_action, NSSM_HOOK_ACTION_PRE)) return true;
\r
143 if (quiet) return false;
\r
144 print_message(stderr, NSSM_MESSAGE_INVALID_HOOK_ACTION, hook_event);
\r
145 _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_ACTION_PRE);
\r
149 if (quiet) return false;
\r
150 print_message(stderr, NSSM_MESSAGE_INVALID_HOOK_EVENT);
\r
151 _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_EVENT_EXIT);
\r
152 _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_EVENT_POWER);
\r
153 _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_EVENT_ROTATE);
\r
154 _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_EVENT_START);
\r
155 _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_EVENT_STOP);
\r
159 void await_hook_threads(hook_thread_t *hook_threads, SERVICE_STATUS_HANDLE status_handle, SERVICE_STATUS *status, unsigned long deadline) {
\r
160 if (! hook_threads) return;
\r
161 if (! hook_threads->num_threads) return;
\r
163 int *retain = (int *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, hook_threads->num_threads * sizeof(int));
\r
165 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("retain"), _T("await_hook_threads()"), 0);
\r
170 We could use WaitForMultipleObjects() but await_single_object() can update
\r
171 the service status as well.
\r
173 int num_threads = 0;
\r
175 for (i = 0; i < hook_threads->num_threads; i++) {
\r
177 if (await_single_handle(status_handle, status, hook_threads->data[i].thread_handle, hook_threads->data[i].name, _T(__FUNCTION__), deadline) != 1) {
\r
178 CloseHandle(hook_threads->data[i].thread_handle);
\r
182 else if (WaitForSingleObject(hook_threads->data[i].thread_handle, 0) != WAIT_TIMEOUT) {
\r
183 CloseHandle(hook_threads->data[i].thread_handle);
\r
187 retain[num_threads++]= i;
\r
191 hook_thread_data_t *data = (hook_thread_data_t *) HeapAlloc(GetProcessHeap(), 0, num_threads * sizeof(hook_thread_data_t));
\r
193 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("data"), _T("await_hook_threads()"), 0);
\r
194 HeapFree(GetProcessHeap(), 0, retain);
\r
198 for (i = 0; i < num_threads; i++) memmove(&data[i], &hook_threads->data[retain[i]], sizeof(data[i]));
\r
200 HeapFree(GetProcessHeap(), 0, hook_threads->data);
\r
201 hook_threads->data = data;
\r
202 hook_threads->num_threads = num_threads;
\r
205 HeapFree(GetProcessHeap(), 0, hook_threads->data);
\r
206 ZeroMemory(hook_threads, sizeof(*hook_threads));
\r
209 HeapFree(GetProcessHeap(), 0, retain);
\r
214 NSSM_HOOK_STATUS_SUCCESS if the hook ran successfully.
\r
215 NSSM_HOOK_STATUS_NOTFOUND if no hook was found.
\r
216 NSSM_HOOK_STATUS_ABORT if the hook failed and we should cancel service start.
\r
217 NSSM_HOOK_STATUS_ERROR on error.
\r
218 NSSM_HOOK_STATUS_NOTRUN if the hook didn't run.
\r
219 NSSM_HOOK_STATUS_TIMEOUT if the hook timed out.
\r
220 NSSM_HOOK_STATUS_FAILED if the hook failed.
\r
222 int nssm_hook(hook_thread_t *hook_threads, nssm_service_t *service, TCHAR *hook_event, TCHAR *hook_action, unsigned long *hook_control, unsigned long deadline, bool async) {
\r
225 hook_t *hook = (hook_t *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(hook_t));
\r
227 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("hook"), _T("nssm_hook()"), 0);
\r
228 return NSSM_HOOK_STATUS_ERROR;
\r
232 GetSystemTimeAsFileTime(&now);
\r
234 EnterCriticalSection(&service->hook_section);
\r
236 /* Set the environment. */
\r
237 set_service_environment(service);
\r
241 _sntprintf_s(number, _countof(number), _TRUNCATE, _T("%lu"), NSSM_HOOK_VERSION);
\r
242 SetEnvironmentVariable(NSSM_HOOK_ENV_VERSION, number);
\r
244 /* Event triggering this action. */
\r
245 SetEnvironmentVariable(NSSM_HOOK_ENV_EVENT, hook_event);
\r
248 SetEnvironmentVariable(NSSM_HOOK_ENV_ACTION, hook_action);
\r
250 /* Control triggering this action. May be empty. */
\r
251 if (hook_control) SetEnvironmentVariable(NSSM_HOOK_ENV_TRIGGER, service_control_text(*hook_control));
\r
252 else SetEnvironmentVariable(NSSM_HOOK_ENV_TRIGGER, _T(""));
\r
254 /* Last control handled. */
\r
255 SetEnvironmentVariable(NSSM_HOOK_ENV_LAST_CONTROL, service_control_text(service->last_control));
\r
257 /* Path to NSSM, unquoted for the environment. */
\r
258 SetEnvironmentVariable(NSSM_HOOK_ENV_IMAGE_PATH, nssm_unquoted_imagepath());
\r
260 /* NSSM version. */
\r
261 SetEnvironmentVariable(NSSM_HOOK_ENV_NSSM_CONFIGURATION, NSSM_CONFIGURATION);
\r
262 SetEnvironmentVariable(NSSM_HOOK_ENV_NSSM_VERSION, NSSM_VERSION);
\r
263 SetEnvironmentVariable(NSSM_HOOK_ENV_BUILD_DATE, NSSM_DATE);
\r
266 _sntprintf_s(number, _countof(number), _TRUNCATE, _T("%lu"), GetCurrentProcessId());
\r
267 SetEnvironmentVariable(NSSM_HOOK_ENV_PID, number);
\r
269 /* NSSM runtime. */
\r
270 set_hook_runtime(NSSM_HOOK_ENV_RUNTIME, &service->nssm_creation_time, &now);
\r
272 /* Application PID. */
\r
273 if (service->pid) {
\r
274 _sntprintf_s(number, _countof(number), _TRUNCATE, _T("%lu"), service->pid);
\r
275 SetEnvironmentVariable(NSSM_HOOK_ENV_APPLICATION_PID, number);
\r
276 /* Application runtime. */
\r
277 set_hook_runtime(NSSM_HOOK_ENV_APPLICATION_RUNTIME, &service->creation_time, &now);
\r
279 SetEnvironmentVariable(NSSM_HOOK_ENV_EXITCODE, _T(""));
\r
282 SetEnvironmentVariable(NSSM_HOOK_ENV_APPLICATION_PID, _T(""));
\r
283 if (str_equiv(hook_event, NSSM_HOOK_EVENT_START) && str_equiv(hook_action, NSSM_HOOK_ACTION_PRE)) {
\r
284 SetEnvironmentVariable(NSSM_HOOK_ENV_APPLICATION_RUNTIME, _T(""));
\r
285 SetEnvironmentVariable(NSSM_HOOK_ENV_EXITCODE, _T(""));
\r
288 set_hook_runtime(NSSM_HOOK_ENV_APPLICATION_RUNTIME, &service->creation_time, &service->exit_time);
\r
290 _sntprintf_s(number, _countof(number), _TRUNCATE, _T("%lu"), service->exitcode);
\r
291 SetEnvironmentVariable(NSSM_HOOK_ENV_EXITCODE, number);
\r
295 /* Deadline for this script. */
\r
296 _sntprintf_s(number, _countof(number), _TRUNCATE, _T("%lu"), deadline);
\r
297 SetEnvironmentVariable(NSSM_HOOK_ENV_DEADLINE, number);
\r
299 /* Service name. */
\r
300 SetEnvironmentVariable(NSSM_HOOK_ENV_SERVICE_NAME, service->name);
\r
301 SetEnvironmentVariable(NSSM_HOOK_ENV_SERVICE_DISPLAYNAME, service->displayname);
\r
303 /* Times the service was asked to start. */
\r
304 _sntprintf_s(number, _countof(number), _TRUNCATE, _T("%lu"), service->start_requested_count);
\r
305 SetEnvironmentVariable(NSSM_HOOK_ENV_START_REQUESTED_COUNT, number);
\r
307 /* Times the service actually did start. */
\r
308 _sntprintf_s(number, _countof(number), _TRUNCATE, _T("%lu"), service->start_count);
\r
309 SetEnvironmentVariable(NSSM_HOOK_ENV_START_COUNT, number);
\r
311 /* Times the service exited. */
\r
312 _sntprintf_s(number, _countof(number), _TRUNCATE, _T("%lu"), service->exit_count);
\r
313 SetEnvironmentVariable(NSSM_HOOK_ENV_EXIT_COUNT, number);
\r
315 /* Throttled count. */
\r
316 _sntprintf_s(number, _countof(number), _TRUNCATE, _T("%lu"), service->throttle);
\r
317 SetEnvironmentVariable(NSSM_HOOK_ENV_THROTTLE_COUNT, number);
\r
319 /* Command line. */
\r
320 TCHAR app[CMD_LENGTH];
\r
321 _sntprintf_s(app, _countof(app), _TRUNCATE, _T("\"%s\" %s"), service->exe, service->flags);
\r
322 SetEnvironmentVariable(NSSM_HOOK_ENV_COMMAND_LINE, app);
\r
324 TCHAR cmd[CMD_LENGTH];
\r
325 if (get_hook(service->name, hook_event, hook_action, cmd, sizeof(cmd))) {
\r
326 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_HOOK_FAILED, hook_event, hook_action, service->name, 0);
\r
327 unset_service_environment(service);
\r
328 LeaveCriticalSection(&service->hook_section);
\r
329 HeapFree(GetProcessHeap(), 0, hook);
\r
330 return NSSM_HOOK_STATUS_ERROR;
\r
334 if (! _tcslen(cmd)) {
\r
335 unset_service_environment(service);
\r
336 LeaveCriticalSection(&service->hook_section);
\r
337 HeapFree(GetProcessHeap(), 0, hook);
\r
338 return NSSM_HOOK_STATUS_NOTFOUND;
\r
341 /* Run the command. */
\r
343 ZeroMemory(&si, sizeof(si));
\r
344 si.cb = sizeof(si);
\r
345 PROCESS_INFORMATION pi;
\r
346 ZeroMemory(&pi, sizeof(pi));
\r
347 if (service->hook_share_output_handles) (void) use_output_handles(service, &si);
\r
348 bool inherit_handles = false;
\r
349 if (si.dwFlags & STARTF_USESTDHANDLES) inherit_handles = true;
\r
350 unsigned long flags = 0;
\r
352 flags |= CREATE_UNICODE_ENVIRONMENT;
\r
354 ret = NSSM_HOOK_STATUS_NOTRUN;
\r
355 if (CreateProcess(0, cmd, 0, 0, inherit_handles, flags, 0, service->dir, &si, &pi)) {
\r
356 hook->name = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, HOOK_NAME_LENGTH * sizeof(TCHAR));
\r
357 if (hook->name) _sntprintf_s(hook->name, HOOK_NAME_LENGTH, _TRUNCATE, _T("%s (%s/%s)"), service->name, hook_event, hook_action);
\r
358 hook->process_handle = pi.hProcess;
\r
359 hook->pid = pi.dwProcessId;
\r
360 hook->deadline = deadline;
\r
361 if (get_process_creation_time(hook->process_handle, &hook->creation_time)) GetSystemTimeAsFileTime(&hook->creation_time);
\r
364 HANDLE thread_handle = CreateThread(NULL, 0, await_hook, (void *) hook, 0, &tid);
\r
365 if (thread_handle) {
\r
368 await_hook_threads(hook_threads, service->status_handle, &service->status, 0);
\r
369 add_thread_handle(hook_threads, thread_handle, hook->name);
\r
372 await_single_handle(service->status_handle, &service->status, thread_handle, hook->name, _T(__FUNCTION__), deadline + NSSM_SERVICE_STATUS_DEADLINE);
\r
373 unsigned long exitcode;
\r
374 GetExitCodeThread(thread_handle, &exitcode);
\r
375 ret = (int) exitcode;
\r
376 CloseHandle(thread_handle);
\r
380 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETHREAD_FAILED, error_string(GetLastError()), 0);
\r
382 if (hook->name) HeapFree(GetProcessHeap(), 0, hook->name);
\r
383 HeapFree(GetProcessHeap(), 0, hook);
\r
387 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_HOOK_CREATEPROCESS_FAILED, hook_event, hook_action, service->name, cmd, error_string(GetLastError()), 0);
\r
388 HeapFree(GetProcessHeap(), 0, hook);
\r
389 close_output_handles(&si);
\r
392 /* Restore our environment. */
\r
393 unset_service_environment(service);
\r
395 LeaveCriticalSection(&service->hook_section);
\r
400 int nssm_hook(hook_thread_t *hook_threads, nssm_service_t *service, TCHAR *hook_event, TCHAR *hook_action, unsigned long *hook_control, unsigned long deadline) {
\r
401 return nssm_hook(hook_threads, service, hook_event, hook_action, hook_control, deadline, true);
\r
404 int nssm_hook(hook_thread_t *hook_threads, nssm_service_t *service, TCHAR *hook_event, TCHAR *hook_action, unsigned long *hook_control) {
\r
405 return nssm_hook(hook_threads, service, hook_event, hook_action, hook_control, NSSM_HOOK_DEADLINE);
\r