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