]>
Commit | Line | Data |
---|---|---|
832b75ed | 1 | /* |
4d59bff9 | 2 | * os_win32/daemon_win32.cpp |
832b75ed GG |
3 | * |
4 | * Home page of code is: http://smartmontools.sourceforge.net | |
5 | * | |
d2e702cf | 6 | * Copyright (C) 2004-14 Christian Franke <smartmontools-support@lists.sourceforge.net> |
832b75ed GG |
7 | * |
8 | * This program is free software; you can redistribute it and/or modify | |
9 | * it under the terms of the GNU General Public License as published by | |
10 | * the Free Software Foundation; either version 2, or (at your option) | |
11 | * any later version. | |
12 | * | |
13 | * You should have received a copy of the GNU General Public License | |
ee38a438 | 14 | * (for example COPYING); If not, see <http://www.gnu.org/licenses/>. |
832b75ed GG |
15 | * |
16 | */ | |
17 | ||
ee38a438 | 18 | #define WINVER 0x0600 |
cfbba5b9 GI |
19 | #define _WIN32_WINNT WINVER |
20 | ||
ee38a438 GI |
21 | #include "daemon_win32.h" |
22 | ||
d2e702cf | 23 | const char * daemon_win32_cpp_cvsid = "$Id: daemon_win32.cpp 3959 2014-07-18 19:22:18Z chrfranke $" |
ee38a438 GI |
24 | DAEMON_WIN32_H_CVSID; |
25 | ||
832b75ed GG |
26 | #include <stdio.h> |
27 | #include <stdlib.h> | |
28 | #include <signal.h> | |
29 | #include <io.h> | |
30 | ||
31 | #define WIN32_LEAN_AND_MEAN | |
832b75ed GG |
32 | #include <windows.h> |
33 | #ifdef _DEBUG | |
34 | #include <crtdbg.h> | |
35 | #endif | |
36 | ||
832b75ed GG |
37 | |
38 | ///////////////////////////////////////////////////////////////////////////// | |
39 | ||
832b75ed GG |
40 | // Prevent spawning of child process if debugging |
41 | #ifdef _DEBUG | |
42 | #define debugging() IsDebuggerPresent() | |
43 | #else | |
44 | #define debugging() FALSE | |
45 | #endif | |
46 | ||
47 | ||
48 | #define EVT_NAME_LEN 260 | |
49 | ||
50 | // Internal events (must be > SIGUSRn) | |
51 | #define EVT_RUNNING 100 // Exists when running, signaled on creation | |
52 | #define EVT_DETACHED 101 // Signaled when child detaches from console | |
53 | #define EVT_RESTART 102 // Signaled when child should restart | |
54 | ||
55 | static void make_name(char * name, int sig) | |
56 | { | |
ee38a438 GI |
57 | int i; |
58 | if (!GetModuleFileNameA(NULL, name, EVT_NAME_LEN-10)) | |
59 | strcpy(name, "DaemonEvent"); | |
60 | for (i = 0; name[i]; i++) { | |
61 | char c = name[i]; | |
62 | if (!( ('0' <= c && c <= '9') | |
63 | || ('A' <= c && c <= 'Z') | |
64 | || ('a' <= c && c <= 'z'))) | |
65 | name[i] = '_'; | |
66 | } | |
67 | sprintf(name+strlen(name), "-%d", sig); | |
832b75ed GG |
68 | } |
69 | ||
70 | ||
71 | static HANDLE create_event(int sig, BOOL initial, BOOL errmsg, BOOL * exists) | |
72 | { | |
ee38a438 GI |
73 | char name[EVT_NAME_LEN]; |
74 | HANDLE h; | |
75 | if (sig >= 0) | |
76 | make_name(name, sig); | |
77 | else | |
78 | name[0] = 0; | |
79 | if (exists) | |
80 | *exists = FALSE; | |
81 | if (!(h = CreateEventA(NULL, FALSE, initial, (name[0] ? name : NULL)))) { | |
82 | if (errmsg) | |
83 | fprintf(stderr, "CreateEvent(.,\"%s\"): Error=%ld\n", name, GetLastError()); | |
84 | return 0; | |
85 | } | |
86 | ||
87 | if (GetLastError() == ERROR_ALREADY_EXISTS) { | |
88 | if (!exists) { | |
89 | if (errmsg) | |
90 | fprintf(stderr, "CreateEvent(.,\"%s\"): Exists\n", name); | |
91 | CloseHandle(h); | |
92 | return 0; | |
93 | } | |
94 | *exists = TRUE; | |
95 | } | |
96 | return h; | |
832b75ed GG |
97 | } |
98 | ||
99 | ||
100 | static HANDLE open_event(int sig) | |
101 | { | |
ee38a438 GI |
102 | char name[EVT_NAME_LEN]; |
103 | make_name(name, sig); | |
104 | return OpenEventA(EVENT_MODIFY_STATE, FALSE, name); | |
832b75ed GG |
105 | } |
106 | ||
107 | ||
108 | static int event_exists(int sig) | |
109 | { | |
ee38a438 GI |
110 | char name[EVT_NAME_LEN]; |
111 | HANDLE h; | |
112 | make_name(name, sig); | |
113 | if (!(h = OpenEventA(EVENT_MODIFY_STATE, FALSE, name))) | |
114 | return 0; | |
115 | CloseHandle(h); | |
116 | return 1; | |
832b75ed GG |
117 | } |
118 | ||
119 | ||
120 | static int sig_event(int sig) | |
121 | { | |
ee38a438 GI |
122 | char name[EVT_NAME_LEN]; |
123 | HANDLE h; | |
124 | make_name(name, sig); | |
125 | if (!(h = OpenEventA(EVENT_MODIFY_STATE, FALSE, name))) { | |
126 | make_name(name, EVT_RUNNING); | |
127 | if (!(h = OpenEvent(EVENT_MODIFY_STATE, FALSE, name))) | |
128 | return -1; | |
129 | CloseHandle(h); | |
130 | return 0; | |
131 | } | |
132 | SetEvent(h); | |
133 | CloseHandle(h); | |
134 | return 1; | |
832b75ed GG |
135 | } |
136 | ||
137 | ||
138 | static void daemon_help(FILE * f, const char * ident, const char * message) | |
139 | { | |
ee38a438 GI |
140 | fprintf(f, |
141 | "%s: %s.\n" | |
142 | "Use \"%s status|stop|reload|restart|sigusr1|sigusr2\" to control daemon.\n", | |
143 | ident, message, ident); | |
144 | fflush(f); | |
832b75ed GG |
145 | } |
146 | ||
147 | ||
148 | ///////////////////////////////////////////////////////////////////////////// | |
149 | // Parent Process | |
150 | ||
151 | ||
152 | static BOOL WINAPI parent_console_handler(DWORD event) | |
153 | { | |
ee38a438 GI |
154 | switch (event) { |
155 | case CTRL_C_EVENT: | |
156 | case CTRL_BREAK_EVENT: | |
157 | return TRUE; // Ignore | |
158 | } | |
159 | return FALSE; // continue with next handler ... | |
832b75ed GG |
160 | } |
161 | ||
162 | ||
163 | static int parent_main(HANDLE rev) | |
164 | { | |
ee38a438 GI |
165 | HANDLE dev; |
166 | HANDLE ht[2]; | |
167 | char * cmdline; | |
168 | STARTUPINFO si; | |
169 | PROCESS_INFORMATION pi; | |
170 | DWORD rc, exitcode; | |
171 | ||
172 | // Ignore ^C, ^BREAK in parent | |
173 | SetConsoleCtrlHandler(parent_console_handler, TRUE/*add*/); | |
174 | ||
175 | // Create event used by child to signal daemon_detach() | |
176 | if (!(dev = create_event(EVT_DETACHED, FALSE/*not signaled*/, TRUE, NULL/*must not exist*/))) { | |
177 | CloseHandle(rev); | |
178 | return 101; | |
179 | } | |
180 | ||
181 | // Restart process with same args | |
182 | cmdline = GetCommandLineA(); | |
183 | memset(&si, 0, sizeof(si)); si.cb = sizeof(si); | |
184 | ||
185 | if (!CreateProcessA( | |
186 | NULL, cmdline, | |
187 | NULL, NULL, TRUE/*inherit*/, | |
188 | 0, NULL, NULL, &si, &pi)) { | |
189 | fprintf(stderr, "CreateProcess(.,\"%s\",.) failed, Error=%ld\n", cmdline, GetLastError()); | |
190 | CloseHandle(rev); CloseHandle(dev); | |
191 | return 101; | |
192 | } | |
193 | CloseHandle(pi.hThread); | |
194 | ||
195 | // Wait for daemon_detach() or exit() | |
196 | ht[0] = dev; ht[1] = pi.hProcess; | |
197 | rc = WaitForMultipleObjects(2, ht, FALSE/*or*/, INFINITE); | |
198 | if (!(/*WAIT_OBJECT_0(0) <= rc && */ rc < WAIT_OBJECT_0+2)) { | |
199 | fprintf(stderr, "WaitForMultipleObjects returns %lX\n", rc); | |
200 | TerminateProcess(pi.hProcess, 200); | |
201 | } | |
202 | CloseHandle(rev); CloseHandle(dev); | |
203 | ||
204 | // Get exit code | |
205 | if (!GetExitCodeProcess(pi.hProcess, &exitcode)) | |
206 | exitcode = 201; | |
207 | else if (exitcode == STILL_ACTIVE) // detach()ed, assume OK | |
208 | exitcode = 0; | |
209 | ||
210 | CloseHandle(pi.hProcess); | |
211 | return exitcode; | |
832b75ed GG |
212 | } |
213 | ||
214 | ||
215 | ///////////////////////////////////////////////////////////////////////////// | |
216 | // Child Process | |
217 | ||
218 | ||
219 | static int svc_mode; // Running as service? | |
220 | static int svc_paused; // Service paused? | |
221 | ||
222 | static void service_report_status(int state, int waithint); | |
223 | ||
224 | ||
225 | // Tables of signal handler and corresponding events | |
226 | typedef void (*sigfunc_t)(int); | |
227 | ||
228 | #define MAX_SIG_HANDLERS 8 | |
229 | ||
230 | static int num_sig_handlers = 0; | |
231 | static sigfunc_t sig_handlers[MAX_SIG_HANDLERS]; | |
232 | static int sig_numbers[MAX_SIG_HANDLERS]; | |
233 | static HANDLE sig_events[MAX_SIG_HANDLERS]; | |
234 | ||
235 | static HANDLE sighup_handle, sigint_handle, sigbreak_handle; | |
236 | static HANDLE sigterm_handle, sigusr1_handle; | |
237 | ||
238 | static HANDLE running_event; | |
239 | ||
240 | static int reopen_stdin, reopen_stdout, reopen_stderr; | |
241 | ||
242 | ||
243 | // Handler for windows console events | |
244 | ||
245 | static BOOL WINAPI child_console_handler(DWORD event) | |
246 | { | |
ee38a438 GI |
247 | // Caution: runs in a new thread |
248 | // TODO: Guard with a mutex | |
249 | HANDLE h = 0; | |
250 | switch (event) { | |
251 | case CTRL_C_EVENT: // <CONTROL-C> (SIGINT) | |
252 | h = sigint_handle; break; | |
253 | case CTRL_BREAK_EVENT: // <CONTROL-Break> (SIGBREAK/SIGQUIT) | |
254 | case CTRL_CLOSE_EVENT: // User closed console or abort via task manager | |
255 | h = sigbreak_handle; break; | |
256 | case CTRL_LOGOFF_EVENT: // Logout/Shutdown (SIGTERM) | |
257 | case CTRL_SHUTDOWN_EVENT: | |
258 | h = sigterm_handle; break; | |
259 | } | |
260 | if (!h) | |
261 | return FALSE; // continue with next handler | |
262 | // Signal event | |
263 | if (!SetEvent(h)) | |
264 | return FALSE; | |
265 | return TRUE; | |
832b75ed GG |
266 | } |
267 | ||
268 | ||
269 | static void child_exit(void) | |
270 | { | |
ee38a438 GI |
271 | int i; |
272 | char * cmdline; | |
273 | HANDLE rst; | |
274 | STARTUPINFO si; | |
275 | PROCESS_INFORMATION pi; | |
276 | ||
277 | for (i = 0; i < num_sig_handlers; i++) | |
278 | CloseHandle(sig_events[i]); | |
279 | num_sig_handlers = 0; | |
280 | CloseHandle(running_event); running_event = 0; | |
281 | ||
282 | // Restart? | |
283 | if (!(rst = open_event(EVT_RESTART))) | |
284 | return; // No => normal exit | |
285 | ||
286 | // Yes => Signal exit and restart process | |
287 | Sleep(500); | |
288 | SetEvent(rst); | |
289 | CloseHandle(rst); | |
290 | Sleep(500); | |
291 | ||
292 | cmdline = GetCommandLineA(); | |
293 | memset(&si, 0, sizeof(si)); si.cb = sizeof(si); | |
294 | si.dwFlags = STARTF_USESHOWWINDOW; si.wShowWindow = SW_HIDE; | |
295 | ||
296 | if (!CreateProcessA( | |
297 | NULL, cmdline, | |
298 | NULL, NULL, TRUE/*inherit*/, | |
299 | 0, NULL, NULL, &si, &pi)) { | |
300 | fprintf(stderr, "CreateProcess(.,\"%s\",.) failed, Error=%ld\n", cmdline, GetLastError()); | |
301 | } | |
302 | CloseHandle(pi.hThread); CloseHandle(pi.hProcess); | |
832b75ed GG |
303 | } |
304 | ||
305 | static int child_main(HANDLE hev,int (*main_func)(int, char **), int argc, char **argv) | |
306 | { | |
ee38a438 GI |
307 | // Keep EVT_RUNNING open until exit |
308 | running_event = hev; | |
832b75ed | 309 | |
ee38a438 GI |
310 | // Install console handler |
311 | SetConsoleCtrlHandler(child_console_handler, TRUE/*add*/); | |
832b75ed | 312 | |
ee38a438 GI |
313 | // Install restart handler |
314 | atexit(child_exit); | |
832b75ed | 315 | |
ee38a438 GI |
316 | // Continue in main_func() to do the real work |
317 | return main_func(argc, argv); | |
832b75ed GG |
318 | } |
319 | ||
320 | ||
321 | // Simulate signal() | |
322 | ||
323 | sigfunc_t daemon_signal(int sig, sigfunc_t func) | |
324 | { | |
ee38a438 GI |
325 | int i; |
326 | HANDLE h; | |
327 | if (func == SIG_DFL || func == SIG_IGN) | |
328 | return func; // TODO | |
329 | for (i = 0; i < num_sig_handlers; i++) { | |
330 | if (sig_numbers[i] == sig) { | |
331 | sigfunc_t old = sig_handlers[i]; | |
332 | sig_handlers[i] = func; | |
333 | return old; | |
334 | } | |
335 | } | |
336 | if (num_sig_handlers >= MAX_SIG_HANDLERS) | |
337 | return SIG_ERR; | |
338 | if (!(h = create_event((!svc_mode ? sig : -1), FALSE, TRUE, NULL))) | |
339 | return SIG_ERR; | |
340 | sig_events[num_sig_handlers] = h; | |
341 | sig_numbers[num_sig_handlers] = sig; | |
342 | sig_handlers[num_sig_handlers] = func; | |
343 | switch (sig) { | |
344 | case SIGHUP: sighup_handle = h; break; | |
345 | case SIGINT: sigint_handle = h; break; | |
346 | case SIGTERM: sigterm_handle = h; break; | |
347 | case SIGBREAK: sigbreak_handle = h; break; | |
348 | case SIGUSR1: sigusr1_handle = h; break; | |
349 | } | |
350 | num_sig_handlers++; | |
351 | return SIG_DFL; | |
832b75ed GG |
352 | } |
353 | ||
354 | ||
355 | // strsignal() | |
356 | ||
357 | const char * daemon_strsignal(int sig) | |
358 | { | |
ee38a438 GI |
359 | switch (sig) { |
360 | case SIGHUP: return "SIGHUP"; | |
361 | case SIGINT: return "SIGINT"; | |
362 | case SIGTERM: return "SIGTERM"; | |
363 | case SIGBREAK:return "SIGBREAK"; | |
364 | case SIGUSR1: return "SIGUSR1"; | |
365 | case SIGUSR2: return "SIGUSR2"; | |
366 | default: return "*UNKNOWN*"; | |
367 | } | |
832b75ed GG |
368 | } |
369 | ||
370 | ||
371 | // Simulate sleep() | |
372 | ||
373 | void daemon_sleep(int seconds) | |
374 | { | |
ee38a438 GI |
375 | do { |
376 | if (num_sig_handlers <= 0) { | |
377 | Sleep(seconds*1000L); | |
378 | } | |
379 | else { | |
380 | // Wait for any signal or timeout | |
381 | DWORD rc = WaitForMultipleObjects(num_sig_handlers, sig_events, | |
382 | FALSE/*OR*/, seconds*1000L); | |
383 | if (rc != WAIT_TIMEOUT) { | |
384 | if (!(/*WAIT_OBJECT_0(0) <= rc && */ rc < WAIT_OBJECT_0+(unsigned)num_sig_handlers)) { | |
385 | fprintf(stderr,"WaitForMultipleObjects returns %lu\n", rc); | |
386 | Sleep(seconds*1000L); | |
387 | return; | |
388 | } | |
389 | // Call Handler | |
390 | sig_handlers[rc-WAIT_OBJECT_0](sig_numbers[rc-WAIT_OBJECT_0]); | |
391 | break; | |
392 | } | |
393 | } | |
394 | } while (svc_paused); | |
832b75ed GG |
395 | } |
396 | ||
397 | ||
398 | // Disable/Enable console | |
399 | ||
400 | void daemon_disable_console() | |
401 | { | |
ee38a438 GI |
402 | SetConsoleCtrlHandler(child_console_handler, FALSE/*remove*/); |
403 | reopen_stdin = reopen_stdout = reopen_stderr = 0; | |
404 | if (isatty(fileno(stdin))) { | |
405 | fclose(stdin); reopen_stdin = 1; | |
406 | } | |
407 | if (isatty(fileno(stdout))) { | |
408 | fclose(stdout); reopen_stdout = 1; | |
409 | } | |
410 | if (isatty(fileno(stderr))) { | |
411 | fclose(stderr); reopen_stderr = 1; | |
412 | } | |
413 | FreeConsole(); | |
414 | SetConsoleCtrlHandler(child_console_handler, TRUE/*add*/); | |
832b75ed GG |
415 | } |
416 | ||
417 | int daemon_enable_console(const char * title) | |
418 | { | |
ee38a438 GI |
419 | BOOL ok; |
420 | SetConsoleCtrlHandler(child_console_handler, FALSE/*remove*/); | |
421 | ok = AllocConsole(); | |
422 | SetConsoleCtrlHandler(child_console_handler, TRUE/*add*/); | |
423 | if (!ok) | |
424 | return -1; | |
425 | if (title) | |
426 | SetConsoleTitleA(title); | |
427 | if (reopen_stdin) | |
428 | freopen("conin$", "r", stdin); | |
429 | if (reopen_stdout) | |
430 | freopen("conout$", "w", stdout); | |
431 | if (reopen_stderr) | |
432 | freopen("conout$", "w", stderr); | |
433 | reopen_stdin = reopen_stdout = reopen_stderr = 0; | |
434 | return 0; | |
832b75ed GG |
435 | } |
436 | ||
437 | ||
438 | // Detach daemon from console & parent | |
439 | ||
440 | int daemon_detach(const char * ident) | |
441 | { | |
ee38a438 GI |
442 | if (!svc_mode) { |
443 | if (ident) { | |
444 | // Print help | |
445 | FILE * f = ( isatty(fileno(stdout)) ? stdout | |
446 | : isatty(fileno(stderr)) ? stderr : NULL); | |
447 | if (f) | |
448 | daemon_help(f, ident, "now detaches from console into background mode"); | |
449 | } | |
450 | // Signal detach to parent | |
451 | if (sig_event(EVT_DETACHED) != 1) { | |
452 | if (!debugging()) | |
453 | return -1; | |
454 | } | |
455 | daemon_disable_console(); | |
456 | } | |
457 | else { | |
458 | // Signal end of initialization to service control manager | |
459 | service_report_status(SERVICE_RUNNING, 0); | |
460 | reopen_stdin = reopen_stdout = reopen_stderr = 1; | |
461 | } | |
462 | ||
463 | return 0; | |
832b75ed GG |
464 | } |
465 | ||
466 | ||
467 | ///////////////////////////////////////////////////////////////////////////// | |
468 | ||
469 | // Spawn a command and redirect <inpbuf >outbuf | |
470 | // return command's exitcode or -1 on error | |
471 | ||
472 | int daemon_spawn(const char * cmd, | |
473 | const char * inpbuf, int inpsize, | |
474 | char * outbuf, int outsize ) | |
475 | { | |
ee38a438 GI |
476 | HANDLE self = GetCurrentProcess(); |
477 | ||
478 | // Create stdin pipe with inheritable read side | |
479 | SECURITY_ATTRIBUTES sa; | |
480 | memset(&sa, 0, sizeof(sa)); sa.nLength = sizeof(sa); | |
481 | sa.bInheritHandle = TRUE; | |
482 | HANDLE pipe_inp_r, pipe_inp_w, h; | |
483 | if (!CreatePipe(&pipe_inp_r, &h, &sa/*inherit*/, inpsize*2+13)) | |
484 | return -1; | |
485 | if (!DuplicateHandle(self, h, self, &pipe_inp_w, | |
486 | 0, FALSE/*!inherit*/, DUPLICATE_SAME_ACCESS|DUPLICATE_CLOSE_SOURCE)) { | |
487 | CloseHandle(pipe_inp_r); | |
488 | return -1; | |
489 | } | |
490 | ||
491 | // Create stdout pipe with inheritable write side | |
492 | memset(&sa, 0, sizeof(sa)); sa.nLength = sizeof(sa); | |
493 | sa.bInheritHandle = TRUE; | |
494 | HANDLE pipe_out_w; | |
495 | if (!CreatePipe(&h, &pipe_out_w, &sa/*inherit*/, outsize)) { | |
496 | CloseHandle(pipe_inp_r); CloseHandle(pipe_inp_w); | |
497 | return -1; | |
498 | } | |
499 | ||
500 | HANDLE pipe_out_r; | |
501 | if (!DuplicateHandle(self, h, self, &pipe_out_r, | |
502 | GENERIC_READ, FALSE/*!inherit*/, DUPLICATE_CLOSE_SOURCE)) { | |
503 | CloseHandle(pipe_out_w); CloseHandle(pipe_inp_r); CloseHandle(pipe_inp_w); | |
504 | return -1; | |
505 | } | |
506 | ||
507 | // Create stderr handle as dup of stdout write side | |
508 | HANDLE pipe_err_w; | |
509 | if (!DuplicateHandle(self, pipe_out_w, self, &pipe_err_w, | |
510 | 0, TRUE/*inherit*/, DUPLICATE_SAME_ACCESS)) { | |
511 | CloseHandle(pipe_out_r); CloseHandle(pipe_out_w); | |
512 | CloseHandle(pipe_inp_r); CloseHandle(pipe_inp_w); | |
513 | return -1; | |
514 | } | |
515 | ||
516 | // Create process with pipes as stdio | |
517 | STARTUPINFO si; | |
518 | memset(&si, 0, sizeof(si)); si.cb = sizeof(si); | |
519 | si.hStdInput = pipe_inp_r; | |
520 | si.hStdOutput = pipe_out_w; | |
521 | si.hStdError = pipe_err_w; | |
522 | si.dwFlags = STARTF_USESTDHANDLES; | |
523 | PROCESS_INFORMATION pi; | |
524 | if (!CreateProcessA( | |
525 | NULL, (char*)cmd, | |
526 | NULL, NULL, TRUE/*inherit*/, | |
527 | CREATE_NO_WINDOW, // DETACHED_PROCESS does not work | |
528 | NULL, NULL, &si, &pi)) { | |
529 | CloseHandle(pipe_err_w); | |
530 | CloseHandle(pipe_out_r); CloseHandle(pipe_out_w); | |
531 | CloseHandle(pipe_inp_r); CloseHandle(pipe_inp_w); | |
532 | return -1; | |
533 | } | |
534 | CloseHandle(pi.hThread); | |
535 | // Close inherited handles | |
536 | CloseHandle(pipe_inp_r); | |
537 | CloseHandle(pipe_out_w); | |
538 | CloseHandle(pipe_err_w); | |
539 | ||
540 | // Copy inpbuf to stdin | |
541 | // convert \n => \r\n | |
542 | DWORD num_io; | |
543 | int i; | |
544 | for (i = 0; i < inpsize; ) { | |
545 | int len = 0; | |
546 | while (i+len < inpsize && inpbuf[i+len] != '\n') | |
547 | len++; | |
548 | if (len > 0) | |
549 | WriteFile(pipe_inp_w, inpbuf+i, len, &num_io, NULL); | |
550 | i += len; | |
551 | if (i < inpsize) { | |
552 | WriteFile(pipe_inp_w, "\r\n", 2, &num_io, NULL); | |
553 | i++; | |
554 | } | |
555 | } | |
556 | CloseHandle(pipe_inp_w); | |
557 | ||
558 | // Copy stdout to output buffer until full, rest to /dev/null | |
559 | // convert \r\n => \n | |
560 | for (i = 0; ; ) { | |
561 | char buf[256]; | |
562 | if (!ReadFile(pipe_out_r, buf, sizeof(buf), &num_io, NULL) || num_io == 0) | |
563 | break; | |
564 | for (int j = 0; i < outsize-1 && j < (int)num_io; j++) { | |
565 | if (buf[j] != '\r') | |
566 | outbuf[i++] = buf[j]; | |
567 | } | |
568 | } | |
569 | outbuf[i] = 0; | |
570 | CloseHandle(pipe_out_r); | |
571 | ||
572 | // Wait for process exitcode | |
573 | DWORD exitcode = 42; | |
574 | WaitForSingleObject(pi.hProcess, INFINITE); | |
575 | GetExitCodeProcess(pi.hProcess, &exitcode); | |
576 | CloseHandle(pi.hProcess); | |
577 | return exitcode; | |
832b75ed GG |
578 | } |
579 | ||
580 | ||
581 | ///////////////////////////////////////////////////////////////////////////// | |
582 | // Initd Functions | |
583 | ||
584 | static int wait_signaled(HANDLE h, int seconds) | |
585 | { | |
ee38a438 GI |
586 | int i; |
587 | for (i = 0; ; ) { | |
588 | if (WaitForSingleObject(h, 1000L) == WAIT_OBJECT_0) | |
589 | return 0; | |
590 | if (++i >= seconds) | |
591 | return -1; | |
592 | fputchar('.'); fflush(stdout); | |
593 | } | |
832b75ed GG |
594 | } |
595 | ||
596 | ||
597 | static int wait_evt_running(int seconds, int exists) | |
598 | { | |
ee38a438 GI |
599 | int i; |
600 | if (event_exists(EVT_RUNNING) == exists) | |
601 | return 0; | |
602 | for (i = 0; ; ) { | |
603 | Sleep(1000); | |
604 | if (event_exists(EVT_RUNNING) == exists) | |
605 | return 0; | |
606 | if (++i >= seconds) | |
607 | return -1; | |
608 | fputchar('.'); fflush(stdout); | |
609 | } | |
832b75ed GG |
610 | } |
611 | ||
612 | ||
613 | static int is_initd_command(char * s) | |
614 | { | |
ee38a438 GI |
615 | if (!strcmp(s, "status")) |
616 | return EVT_RUNNING; | |
617 | if (!strcmp(s, "stop")) | |
618 | return SIGTERM; | |
619 | if (!strcmp(s, "reload")) | |
620 | return SIGHUP; | |
621 | if (!strcmp(s, "sigusr1")) | |
622 | return SIGUSR1; | |
623 | if (!strcmp(s, "sigusr2")) | |
624 | return SIGUSR2; | |
625 | if (!strcmp(s, "restart")) | |
626 | return EVT_RESTART; | |
627 | return -1; | |
832b75ed GG |
628 | } |
629 | ||
630 | ||
631 | static int initd_main(const char * ident, int argc, char **argv) | |
632 | { | |
ee38a438 GI |
633 | int rc; |
634 | if (argc < 2) | |
635 | return -1; | |
636 | if ((rc = is_initd_command(argv[1])) < 0) | |
637 | return -1; | |
638 | if (argc != 2) { | |
639 | printf("%s: no arguments allowed for command %s\n", ident, argv[1]); | |
640 | return 1; | |
641 | } | |
642 | ||
643 | switch (rc) { | |
644 | default: | |
645 | case EVT_RUNNING: | |
646 | printf("Checking for %s:", ident); fflush(stdout); | |
647 | rc = event_exists(EVT_RUNNING); | |
648 | puts(rc ? " running" : " not running"); | |
649 | return (rc ? 0 : 1); | |
650 | ||
651 | case SIGTERM: | |
652 | printf("Stopping %s:", ident); fflush(stdout); | |
653 | rc = sig_event(SIGTERM); | |
654 | if (rc <= 0) { | |
655 | puts(rc < 0 ? " not running" : " error"); | |
656 | return (rc < 0 ? 0 : 1); | |
657 | } | |
658 | rc = wait_evt_running(10, 0); | |
659 | puts(!rc ? " done" : " timeout"); | |
660 | return (!rc ? 0 : 1); | |
661 | ||
662 | case SIGHUP: | |
663 | printf("Reloading %s:", ident); fflush(stdout); | |
664 | rc = sig_event(SIGHUP); | |
665 | puts(rc > 0 ? " done" : rc == 0 ? " error" : " not running"); | |
666 | return (rc > 0 ? 0 : 1); | |
667 | ||
668 | case SIGUSR1: | |
669 | case SIGUSR2: | |
670 | printf("Sending SIGUSR%d to %s:", (rc-SIGUSR1+1), ident); fflush(stdout); | |
671 | rc = sig_event(rc); | |
672 | puts(rc > 0 ? " done" : rc == 0 ? " error" : " not running"); | |
673 | return (rc > 0 ? 0 : 1); | |
674 | ||
675 | case EVT_RESTART: | |
676 | { | |
677 | HANDLE rst; | |
678 | printf("Stopping %s:", ident); fflush(stdout); | |
679 | if (event_exists(EVT_DETACHED)) { | |
680 | puts(" not detached, cannot restart"); | |
681 | return 1; | |
682 | } | |
683 | if (!(rst = create_event(EVT_RESTART, FALSE, FALSE, NULL))) { | |
684 | puts(" error"); | |
685 | return 1; | |
686 | } | |
687 | rc = sig_event(SIGTERM); | |
688 | if (rc <= 0) { | |
689 | puts(rc < 0 ? " not running" : " error"); | |
690 | CloseHandle(rst); | |
691 | return 1; | |
692 | } | |
693 | rc = wait_signaled(rst, 10); | |
694 | CloseHandle(rst); | |
695 | if (rc) { | |
696 | puts(" timeout"); | |
697 | return 1; | |
698 | } | |
699 | puts(" done"); | |
700 | Sleep(100); | |
701 | ||
702 | printf("Starting %s:", ident); fflush(stdout); | |
703 | rc = wait_evt_running(10, 1); | |
704 | puts(!rc ? " done" : " error"); | |
705 | return (!rc ? 0 : 1); | |
706 | } | |
707 | } | |
832b75ed GG |
708 | } |
709 | ||
710 | ||
711 | ///////////////////////////////////////////////////////////////////////////// | |
712 | // Windows Service Functions | |
713 | ||
714 | int daemon_winsvc_exitcode; // Set by app to exit(code) | |
715 | ||
716 | static SERVICE_STATUS_HANDLE svc_handle; | |
717 | static SERVICE_STATUS svc_status; | |
718 | ||
719 | ||
720 | // Report status to SCM | |
721 | ||
722 | static void service_report_status(int state, int seconds) | |
723 | { | |
ee38a438 GI |
724 | // TODO: Avoid race |
725 | static DWORD checkpoint = 1; | |
726 | svc_status.dwCurrentState = state; | |
727 | svc_status.dwWaitHint = seconds*1000; | |
728 | switch (state) { | |
729 | default: | |
730 | svc_status.dwCheckPoint = checkpoint++; | |
731 | break; | |
732 | case SERVICE_RUNNING: | |
733 | case SERVICE_STOPPED: | |
734 | svc_status.dwCheckPoint = 0; | |
735 | } | |
736 | switch (state) { | |
737 | case SERVICE_START_PENDING: | |
738 | case SERVICE_STOP_PENDING: | |
739 | svc_status.dwControlsAccepted = 0; | |
740 | break; | |
741 | default: | |
742 | svc_status.dwControlsAccepted = | |
743 | SERVICE_ACCEPT_STOP|SERVICE_ACCEPT_SHUTDOWN| | |
744 | SERVICE_ACCEPT_PAUSE_CONTINUE|SERVICE_ACCEPT_PARAMCHANGE; | |
745 | break; | |
746 | } | |
747 | SetServiceStatus(svc_handle, &svc_status); | |
832b75ed GG |
748 | } |
749 | ||
750 | ||
751 | // Control the service, called by SCM | |
752 | ||
753 | static void WINAPI service_control(DWORD ctrlcode) | |
754 | { | |
ee38a438 GI |
755 | switch (ctrlcode) { |
756 | case SERVICE_CONTROL_STOP: | |
757 | case SERVICE_CONTROL_SHUTDOWN: | |
758 | service_report_status(SERVICE_STOP_PENDING, 30); | |
759 | svc_paused = 0; | |
760 | SetEvent(sigterm_handle); | |
761 | break; | |
762 | case SERVICE_CONTROL_PARAMCHANGE: // Win2000/XP | |
763 | service_report_status(svc_status.dwCurrentState, 0); | |
764 | svc_paused = 0; | |
765 | SetEvent(sighup_handle); // reload | |
766 | break; | |
767 | case SERVICE_CONTROL_PAUSE: | |
768 | service_report_status(SERVICE_PAUSED, 0); | |
769 | svc_paused = 1; | |
770 | break; | |
771 | case SERVICE_CONTROL_CONTINUE: | |
772 | service_report_status(SERVICE_RUNNING, 0); | |
773 | { | |
774 | int was_paused = svc_paused; | |
775 | svc_paused = 0; | |
776 | SetEvent(was_paused ? sighup_handle : sigusr1_handle); // reload:recheck | |
777 | } | |
778 | break; | |
779 | case SERVICE_CONTROL_INTERROGATE: | |
780 | default: // unknown | |
781 | service_report_status(svc_status.dwCurrentState, 0); | |
782 | break; | |
783 | } | |
832b75ed GG |
784 | } |
785 | ||
786 | ||
787 | // Exit handler for service | |
788 | ||
789 | static void service_exit(void) | |
790 | { | |
ee38a438 GI |
791 | // Close signal events |
792 | int i; | |
793 | for (i = 0; i < num_sig_handlers; i++) | |
794 | CloseHandle(sig_events[i]); | |
795 | num_sig_handlers = 0; | |
796 | ||
797 | // Set exitcode | |
798 | if (daemon_winsvc_exitcode) { | |
799 | svc_status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR; | |
800 | svc_status.dwServiceSpecificExitCode = daemon_winsvc_exitcode; | |
801 | } | |
802 | // Report stopped | |
803 | service_report_status(SERVICE_STOPPED, 0); | |
832b75ed GG |
804 | } |
805 | ||
806 | ||
807 | // Variables for passing main(argc, argv) from daemon_main to service_main() | |
808 | static int (*svc_main_func)(int, char **); | |
809 | static int svc_main_argc; | |
810 | static char ** svc_main_argv; | |
811 | ||
812 | // Main function for service, called by service dispatcher | |
813 | ||
ee38a438 | 814 | static void WINAPI service_main(DWORD /*argc*/, LPSTR * argv) |
832b75ed | 815 | { |
ee38a438 GI |
816 | char path[MAX_PATH], *p; |
817 | ||
818 | // Register control handler | |
819 | svc_handle = RegisterServiceCtrlHandler(argv[0], service_control); | |
832b75ed | 820 | |
ee38a438 GI |
821 | // Init service status |
822 | svc_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; | |
823 | service_report_status(SERVICE_START_PENDING, 10); | |
832b75ed | 824 | |
ee38a438 GI |
825 | // Service started in \windows\system32, change to .exe directory |
826 | if (GetModuleFileNameA(NULL, path, sizeof(path)) && (p = strrchr(path, '\\'))) { | |
827 | *p = 0; SetCurrentDirectoryA(path); | |
828 | } | |
832b75ed | 829 | |
ee38a438 GI |
830 | // Install exit handler |
831 | atexit(service_exit); | |
832b75ed | 832 | |
ee38a438 GI |
833 | // Do the real work, service status later updated by daemon_detach() |
834 | daemon_winsvc_exitcode = svc_main_func(svc_main_argc, svc_main_argv); | |
832b75ed | 835 | |
ee38a438 GI |
836 | exit(daemon_winsvc_exitcode); |
837 | // ... continued in service_exit() | |
832b75ed GG |
838 | } |
839 | ||
840 | ||
841 | ///////////////////////////////////////////////////////////////////////////// | |
842 | // Windows Service Admin Functions | |
843 | ||
832b75ed | 844 | |
ee38a438 GI |
845 | // Make registry key name for event message file |
846 | static bool make_evtkey(char * buf, unsigned size, const char * ident) | |
847 | { | |
848 | static const char prefix[] = "SYSTEM\\CurrentControlSet\\Services\\Eventlog\\Application\\"; | |
849 | const unsigned pfxlen = sizeof(prefix)-1; | |
850 | unsigned idlen = strlen(ident); | |
851 | if (pfxlen + idlen >= size) { | |
852 | printf(" Buffer overflow\n"); | |
853 | return false; | |
854 | } | |
855 | memcpy(buf, prefix, pfxlen); | |
856 | memcpy(buf+pfxlen, ident, idlen+1); | |
857 | return true; | |
858 | } | |
859 | ||
860 | // Install this exe as event message file | |
861 | static void inst_evtmsg(const char * ident) | |
862 | { | |
863 | printf("Installing event message file for %s:", ident); fflush(stdout); | |
864 | ||
865 | char mypath[MAX_PATH]; | |
866 | if (!GetModuleFileNameA((HMODULE)0, mypath, sizeof(mypath))) { | |
867 | printf(" unknown program path, Error=%ld\n", GetLastError()); | |
868 | return; | |
869 | } | |
870 | ||
871 | char subkey[MAX_PATH]; | |
872 | if (!make_evtkey(subkey, sizeof(subkey), ident)) | |
873 | return; | |
874 | ||
875 | HKEY hk; | |
876 | LONG err = RegCreateKeyExA(HKEY_LOCAL_MACHINE, subkey, 0, (char *)0, 0, KEY_ALL_ACCESS, | |
877 | (SECURITY_ATTRIBUTES *)0, &hk, (DWORD *)0); | |
878 | if (err != ERROR_SUCCESS) { | |
879 | printf(" RegCreateKeyEx failed, error=%ld\n", err); | |
880 | return; | |
881 | } | |
882 | ||
883 | err = RegSetValueExA(hk, "EventMessageFile", 0, REG_SZ, | |
884 | (const BYTE *)mypath, strlen(mypath)+1); | |
885 | if (err == ERROR_SUCCESS) { | |
886 | DWORD val = EVENTLOG_INFORMATION_TYPE | |
887 | |EVENTLOG_WARNING_TYPE | |
888 | |EVENTLOG_ERROR_TYPE; | |
889 | err = RegSetValueExA(hk, "TypesSupported", 0, REG_DWORD, | |
890 | (const BYTE *)&val, sizeof(val)); | |
891 | } | |
892 | if (err != ERROR_SUCCESS) | |
893 | printf(" RegSetValueEx failed, error=%ld\n", err); | |
894 | ||
895 | RegCloseKey(hk); | |
896 | puts(" done"); | |
897 | } | |
898 | ||
899 | // Uninstall event message file | |
900 | static void uninst_evtmsg(const char * ident) | |
832b75ed | 901 | { |
ee38a438 GI |
902 | printf("Removing event message file for %s:", ident); fflush(stdout); |
903 | ||
904 | char subkey[MAX_PATH]; | |
905 | if (!make_evtkey(subkey, sizeof(subkey), ident)) | |
906 | return; | |
907 | ||
908 | LONG err = RegDeleteKeyA(HKEY_LOCAL_MACHINE, subkey); | |
909 | if (err != ERROR_SUCCESS && err != ERROR_FILE_NOT_FOUND) { | |
910 | printf(" RegDeleteKey failed, error=%ld\n", err); | |
911 | return; | |
912 | } | |
913 | puts(" done"); | |
832b75ed GG |
914 | } |
915 | ||
916 | ||
917 | // Service install/remove commands | |
918 | ||
919 | static int svcadm_main(const char * ident, const daemon_winsvc_options * svc_opts, | |
920 | int argc, char **argv ) | |
921 | { | |
ee38a438 GI |
922 | int remove; long err; |
923 | SC_HANDLE hm, hs; | |
924 | ||
925 | if (argc < 2) | |
926 | return -1; | |
927 | if (!strcmp(argv[1], "install")) | |
928 | remove = 0; | |
929 | else if (!strcmp(argv[1], "remove")) { | |
930 | if (argc != 2) { | |
931 | printf("%s: no arguments allowed for command remove\n", ident); | |
932 | return 1; | |
933 | } | |
934 | remove = 1; | |
935 | } | |
936 | else | |
937 | return -1; | |
938 | ||
939 | printf("%s service %s:", (!remove?"Installing":"Removing"), ident); fflush(stdout); | |
940 | ||
941 | // Open SCM | |
942 | if (!(hm = OpenSCManager(NULL/*local*/, NULL/*default*/, SC_MANAGER_ALL_ACCESS))) { | |
943 | if ((err = GetLastError()) == ERROR_ACCESS_DENIED) | |
944 | puts(" access to SCManager denied"); | |
945 | else | |
946 | printf(" cannot open SCManager, Error=%ld\n", err); | |
947 | return 1; | |
948 | } | |
949 | ||
950 | if (!remove) { | |
951 | char path[MAX_PATH+100]; | |
952 | int i; | |
953 | // Get program path | |
954 | if (!GetModuleFileNameA(NULL, path, MAX_PATH)) { | |
955 | printf(" unknown program path, Error=%ld\n", GetLastError()); | |
956 | CloseServiceHandle(hm); | |
957 | return 1; | |
958 | } | |
959 | // Add quotes if necessary | |
960 | if (strchr(path, ' ')) { | |
961 | i = strlen(path); | |
962 | path[i+1] = '"'; path[i+2] = 0; | |
963 | while (--i >= 0) | |
964 | path[i+1] = path[i]; | |
965 | path[0] = '"'; | |
966 | } | |
967 | // Append options | |
968 | strcat(path, " "); strcat(path, svc_opts->cmd_opt); | |
969 | for (i = 2; i < argc; i++) { | |
970 | const char * s = argv[i]; | |
971 | if (strlen(path)+1+1+strlen(s)+1 >= sizeof(path)) | |
972 | break; | |
973 | // Add quotes if necessary | |
974 | if (strchr(s, ' ') && !strchr(s, '"')) { | |
975 | strcat(path, " \""); strcat(path, s); strcat(path, "\""); | |
976 | } | |
977 | else { | |
978 | strcat(path, " "); strcat(path, s); | |
979 | } | |
980 | } | |
981 | // Create | |
982 | if (!(hs = CreateService(hm, | |
983 | svc_opts->svcname, svc_opts->dispname, | |
984 | SERVICE_ALL_ACCESS, | |
3d17a85c | 985 | SERVICE_WIN32_OWN_PROCESS, |
ee38a438 GI |
986 | SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, path, |
987 | NULL/*no load ordering*/, NULL/*no tag id*/, | |
988 | ""/*no depedencies*/, NULL/*local system account*/, NULL/*no pw*/))) { | |
989 | if ((err = GetLastError()) == ERROR_SERVICE_EXISTS) | |
990 | puts(" the service is already installed"); | |
991 | else if (err == ERROR_SERVICE_MARKED_FOR_DELETE) | |
992 | puts(" service is still running and marked for deletion\n" | |
993 | "Stop the service and retry install"); | |
994 | else | |
995 | printf(" failed, Error=%ld\n", err); | |
996 | CloseServiceHandle(hm); | |
997 | return 1; | |
998 | } | |
999 | // Set optional description | |
1000 | if (svc_opts->descript) { | |
1001 | SERVICE_DESCRIPTIONA sd = { const_cast<char *>(svc_opts->descript) }; | |
1002 | ChangeServiceConfig2A(hs, SERVICE_CONFIG_DESCRIPTION, &sd); | |
1003 | } | |
1004 | // Enable delayed auto start if supported | |
1005 | OSVERSIONINFOA ver; ver.dwOSVersionInfoSize = sizeof(ver); | |
1006 | if ( GetVersionExA(&ver) | |
1007 | && ver.dwPlatformId == VER_PLATFORM_WIN32_NT | |
1008 | && ver.dwMajorVersion >= 6 /* Vista */ ) { | |
d2e702cf GI |
1009 | // SERVICE_{,CONFIG_}DELAYED_AUTO_START_INFO are missing in older MinGW headers |
1010 | struct /* SERVICE_DELAYED_AUTO_START_INFO */ { | |
1011 | BOOL fDelayedAutostart; | |
1012 | } sdasi = { TRUE }; | |
1013 | // typedef char ASSERT_sizeof_sdasi[sizeof(sdasi) == sizeof(SERVICE_DELAYED_AUTO_START_INFO) ? 1 : -1]; | |
1014 | // typedef char ASSERT_const_scdasi[SERVICE_CONFIG_DELAYED_AUTO_START_INFO == 3 ? 1 : -1]; | |
1015 | ChangeServiceConfig2A(hs, 3 /* SERVICE_CONFIG_DELAYED_AUTO_START_INFO */, &sdasi); | |
ee38a438 GI |
1016 | } |
1017 | } | |
1018 | else { | |
1019 | // Open | |
1020 | if (!(hs = OpenService(hm, svc_opts->svcname, SERVICE_ALL_ACCESS))) { | |
1021 | puts(" not found"); | |
1022 | CloseServiceHandle(hm); | |
1023 | return 1; | |
1024 | } | |
1025 | // TODO: Stop service if running | |
1026 | // Remove | |
1027 | if (!DeleteService(hs)) { | |
1028 | if ((err = GetLastError()) == ERROR_SERVICE_MARKED_FOR_DELETE) | |
1029 | puts(" service is still running and marked for deletion\n" | |
1030 | "Stop the service to remove it"); | |
1031 | else | |
1032 | printf(" failed, Error=%ld\n", err); | |
1033 | CloseServiceHandle(hs); CloseServiceHandle(hm); | |
1034 | return 1; | |
1035 | } | |
1036 | } | |
1037 | puts(" done"); | |
1038 | CloseServiceHandle(hs); CloseServiceHandle(hm); | |
1039 | ||
1040 | // Install/Remove event message file registry entry | |
1041 | if (!remove) { | |
1042 | inst_evtmsg(ident); | |
1043 | } | |
1044 | else { | |
1045 | uninst_evtmsg(ident); | |
1046 | } | |
1047 | ||
1048 | return 0; | |
832b75ed GG |
1049 | } |
1050 | ||
1051 | ||
1052 | ///////////////////////////////////////////////////////////////////////////// | |
1053 | // Main Function | |
1054 | ||
1055 | // This function must be called from main() | |
1056 | // main_func is the function doing the real work | |
1057 | ||
1058 | int daemon_main(const char * ident, const daemon_winsvc_options * svc_opts, | |
1059 | int (*main_func)(int, char **), int argc, char **argv ) | |
1060 | { | |
ee38a438 | 1061 | int rc; |
832b75ed | 1062 | #ifdef _DEBUG |
ee38a438 GI |
1063 | // Enable Debug heap checks |
1064 | _CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | |
1065 | |_CRTDBG_ALLOC_MEM_DF|_CRTDBG_CHECK_ALWAYS_DF|_CRTDBG_LEAK_CHECK_DF); | |
832b75ed GG |
1066 | #endif |
1067 | ||
ee38a438 GI |
1068 | // Check for [status|stop|reload|restart|sigusr1|sigusr2] parameters |
1069 | if ((rc = initd_main(ident, argc, argv)) >= 0) | |
1070 | return rc; | |
1071 | // Check for [install|remove] parameters | |
1072 | if (svc_opts && (rc = svcadm_main(ident, svc_opts, argc, argv)) >= 0) | |
1073 | return rc; | |
1074 | ||
1075 | // Run as service if svc_opts.cmd_opt is given as first(!) argument | |
1076 | svc_mode = (svc_opts && argc >= 2 && !strcmp(argv[1], svc_opts->cmd_opt)); | |
1077 | ||
1078 | if (!svc_mode) { | |
1079 | // Daemon: Try to simulate a Unix-like daemon | |
1080 | HANDLE rev; | |
1081 | BOOL exists; | |
1082 | ||
1083 | // Create main event to detect process type: | |
1084 | // 1. new: parent process => start child and wait for detach() or exit() of child. | |
1085 | // 2. exists && signaled: child process => do the real work, signal detach() to parent | |
1086 | // 3. exists && !signaled: already running => exit() | |
1087 | if (!(rev = create_event(EVT_RUNNING, TRUE/*signaled*/, TRUE, &exists))) | |
1088 | return 100; | |
1089 | ||
1090 | if (!exists && !debugging()) { | |
1091 | // Event new => parent process | |
1092 | return parent_main(rev); | |
1093 | } | |
1094 | ||
1095 | if (WaitForSingleObject(rev, 0) == WAIT_OBJECT_0) { | |
1096 | // Event was signaled => In child process | |
1097 | return child_main(rev, main_func, argc, argv); | |
1098 | } | |
1099 | ||
1100 | // Event no longer signaled => Already running! | |
1101 | daemon_help(stdout, ident, "already running"); | |
1102 | CloseHandle(rev); | |
1103 | return 1; | |
1104 | } | |
1105 | else { | |
1106 | // Service: Start service_main() via SCM | |
1107 | SERVICE_TABLE_ENTRY service_table[] = { | |
1108 | { (char*)svc_opts->svcname, service_main }, { NULL, NULL } | |
1109 | }; | |
1110 | ||
1111 | svc_main_func = main_func; | |
1112 | svc_main_argc = argc; | |
1113 | svc_main_argv = argv; | |
1114 | if (!StartServiceCtrlDispatcher(service_table)) { | |
1115 | printf("%s: cannot dispatch service, Error=%ld\n" | |
1116 | "Option \"%s\" cannot be used to start %s as a service from console.\n" | |
1117 | "Use \"%s install ...\" to install the service\n" | |
1118 | "and \"net start %s\" to start it.\n", | |
1119 | ident, GetLastError(), svc_opts->cmd_opt, ident, ident, ident); | |
832b75ed GG |
1120 | |
1121 | #ifdef _DEBUG | |
ee38a438 GI |
1122 | if (debugging()) |
1123 | service_main(argc, argv); | |
832b75ed | 1124 | #endif |
ee38a438 GI |
1125 | return 100; |
1126 | } | |
1127 | Sleep(1000); | |
1128 | ExitThread(0); // Do not redo exit() processing | |
1129 | /*NOTREACHED*/ | |
1130 | return 0; | |
1131 | } | |
832b75ed GG |
1132 | } |
1133 | ||
1134 | ||
1135 | ///////////////////////////////////////////////////////////////////////////// | |
1136 | // Test Program | |
1137 | ||
1138 | #ifdef TEST | |
1139 | ||
1140 | static volatile sig_atomic_t caughtsig = 0; | |
1141 | ||
1142 | static void sig_handler(int sig) | |
1143 | { | |
ee38a438 | 1144 | caughtsig = sig; |
832b75ed GG |
1145 | } |
1146 | ||
1147 | static void test_exit(void) | |
1148 | { | |
ee38a438 | 1149 | printf("Main exit\n"); |
832b75ed GG |
1150 | } |
1151 | ||
1152 | int test_main(int argc, char **argv) | |
1153 | { | |
ee38a438 GI |
1154 | int i; |
1155 | int debug = 0; | |
1156 | char * cmd = 0; | |
1157 | ||
1158 | printf("PID=%ld\n", GetCurrentProcessId()); | |
1159 | for (i = 0; i < argc; i++) { | |
1160 | printf("%d: \"%s\"\n", i, argv[i]); | |
1161 | if (!strcmp(argv[i],"-d")) | |
1162 | debug = 1; | |
1163 | } | |
1164 | if (argc > 1 && argv[argc-1][0] != '-') | |
1165 | cmd = argv[argc-1]; | |
1166 | ||
1167 | daemon_signal(SIGINT, sig_handler); | |
1168 | daemon_signal(SIGBREAK, sig_handler); | |
1169 | daemon_signal(SIGTERM, sig_handler); | |
1170 | daemon_signal(SIGHUP, sig_handler); | |
1171 | daemon_signal(SIGUSR1, sig_handler); | |
1172 | daemon_signal(SIGUSR2, sig_handler); | |
1173 | ||
1174 | atexit(test_exit); | |
1175 | ||
1176 | if (!debug) { | |
1177 | printf("Preparing to detach...\n"); | |
1178 | Sleep(2000); | |
1179 | daemon_detach("test"); | |
1180 | printf("Detached!\n"); | |
1181 | } | |
1182 | ||
1183 | for (;;) { | |
1184 | daemon_sleep(1); | |
1185 | printf("."); fflush(stdout); | |
1186 | if (caughtsig) { | |
1187 | if (caughtsig == SIGUSR2) { | |
1188 | debug ^= 1; | |
1189 | if (debug) | |
1190 | daemon_enable_console("Daemon[Debug]"); | |
1191 | else | |
1192 | daemon_disable_console(); | |
1193 | } | |
1194 | else if (caughtsig == SIGUSR1 && cmd) { | |
1195 | char inpbuf[200], outbuf[1000]; int rc; | |
1196 | strcpy(inpbuf, "Hello\nWorld!\n"); | |
1197 | rc = daemon_spawn(cmd, inpbuf, strlen(inpbuf), outbuf, sizeof(outbuf)); | |
1198 | if (!debug) | |
1199 | daemon_enable_console("Command output"); | |
1200 | printf("\"%s\" returns %d\n", cmd, rc); | |
1201 | if (rc >= 0) | |
1202 | printf("output:\n%s.\n", outbuf); | |
1203 | fflush(stdout); | |
1204 | if (!debug) { | |
1205 | Sleep(10000); daemon_disable_console(); | |
1206 | } | |
1207 | } | |
1208 | printf("[PID=%ld: Signal=%d]", GetCurrentProcessId(), caughtsig); fflush(stdout); | |
1209 | if (caughtsig == SIGTERM || caughtsig == SIGBREAK) | |
1210 | break; | |
1211 | caughtsig = 0; | |
1212 | } | |
1213 | } | |
1214 | printf("\nExiting on signal %d\n", caughtsig); | |
1215 | return 0; | |
832b75ed GG |
1216 | } |
1217 | ||
1218 | ||
1219 | int main(int argc, char **argv) | |
1220 | { | |
ee38a438 GI |
1221 | static const daemon_winsvc_options svc_opts = { |
1222 | "-s", "test", "Test Service", "Service to test daemon_win32.c Module" | |
1223 | }; | |
832b75ed | 1224 | |
ee38a438 | 1225 | return daemon_main("testd", &svc_opts, test_main, argc, argv); |
832b75ed GG |
1226 | } |
1227 | ||
1228 | #endif |