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