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