]> git.proxmox.com Git - mirror_smartmontools-debian.git/blob - os_win32/daemon_win32.cpp
Imported Upstream version 6.1+svn3812
[mirror_smartmontools-debian.git] / os_win32 / daemon_win32.cpp
1 /*
2 * os_win32/daemon_win32.cpp
3 *
4 * Home page of code is: http://smartmontools.sourceforge.net
5 *
6 * Copyright (C) 2004-13 Christian Franke <smartmontools-support@lists.sourceforge.net>
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, see <http://www.gnu.org/licenses/>.
15 *
16 */
17
18 #define WINVER 0x0600
19 #define _WIN32_WINNT WINVER
20
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
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <signal.h>
29 #include <io.h>
30
31 #define WIN32_LEAN_AND_MEAN
32 #include <windows.h>
33 #ifdef _DEBUG
34 #include <crtdbg.h>
35 #endif
36
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
41
42
43 /////////////////////////////////////////////////////////////////////////////
44
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 {
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);
73 }
74
75
76 static HANDLE create_event(int sig, BOOL initial, BOOL errmsg, BOOL * exists)
77 {
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;
102 }
103
104
105 static HANDLE open_event(int sig)
106 {
107 char name[EVT_NAME_LEN];
108 make_name(name, sig);
109 return OpenEventA(EVENT_MODIFY_STATE, FALSE, name);
110 }
111
112
113 static int event_exists(int sig)
114 {
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;
122 }
123
124
125 static int sig_event(int sig)
126 {
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;
140 }
141
142
143 static void daemon_help(FILE * f, const char * ident, const char * message)
144 {
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);
150 }
151
152
153 /////////////////////////////////////////////////////////////////////////////
154 // Parent Process
155
156
157 static BOOL WINAPI parent_console_handler(DWORD event)
158 {
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 ...
165 }
166
167
168 static int parent_main(HANDLE rev)
169 {
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;
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 {
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;
271 }
272
273
274 static void child_exit(void)
275 {
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);
308 }
309
310 static int child_main(HANDLE hev,int (*main_func)(int, char **), int argc, char **argv)
311 {
312 // Keep EVT_RUNNING open until exit
313 running_event = hev;
314
315 // Install console handler
316 SetConsoleCtrlHandler(child_console_handler, TRUE/*add*/);
317
318 // Install restart handler
319 atexit(child_exit);
320
321 // Continue in main_func() to do the real work
322 return main_func(argc, argv);
323 }
324
325
326 // Simulate signal()
327
328 sigfunc_t daemon_signal(int sig, sigfunc_t func)
329 {
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;
357 }
358
359
360 // strsignal()
361
362 const char * daemon_strsignal(int sig)
363 {
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 }
373 }
374
375
376 // Simulate sleep()
377
378 void daemon_sleep(int seconds)
379 {
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);
400 }
401
402
403 // Disable/Enable console
404
405 void daemon_disable_console()
406 {
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*/);
420 }
421
422 int daemon_enable_console(const char * title)
423 {
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;
440 }
441
442
443 // Detach daemon from console & parent
444
445 int daemon_detach(const char * ident)
446 {
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;
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 {
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;
583 }
584
585
586 /////////////////////////////////////////////////////////////////////////////
587 // Initd Functions
588
589 static int wait_signaled(HANDLE h, int seconds)
590 {
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 }
599 }
600
601
602 static int wait_evt_running(int seconds, int exists)
603 {
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 }
615 }
616
617
618 static int is_initd_command(char * s)
619 {
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;
633 }
634
635
636 static int initd_main(const char * ident, int argc, char **argv)
637 {
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 }
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 {
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);
753 }
754
755
756 // Control the service, called by SCM
757
758 static void WINAPI service_control(DWORD ctrlcode)
759 {
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 }
789 }
790
791
792 // Exit handler for service
793
794 static void service_exit(void)
795 {
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);
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
819 static void WINAPI service_main(DWORD /*argc*/, LPSTR * argv)
820 {
821 char path[MAX_PATH], *p;
822
823 // Register control handler
824 svc_handle = RegisterServiceCtrlHandler(argv[0], service_control);
825
826 // Init service status
827 svc_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
828 service_report_status(SERVICE_START_PENDING, 10);
829
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 }
834
835 // Install exit handler
836 atexit(service_exit);
837
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);
840
841 exit(daemon_winsvc_exitcode);
842 // ... continued in service_exit()
843 }
844
845
846 /////////////////////////////////////////////////////////////////////////////
847 // Windows Service Admin Functions
848
849
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)
906 {
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");
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 {
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;
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 {
1061 int rc;
1062 #ifdef _DEBUG
1063 // Enable Debug heap checks
1064 _CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG)
1065 |_CRTDBG_ALLOC_MEM_DF|_CRTDBG_CHECK_ALWAYS_DF|_CRTDBG_LEAK_CHECK_DF);
1066 #endif
1067
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);
1120
1121 #ifdef _DEBUG
1122 if (debugging())
1123 service_main(argc, argv);
1124 #endif
1125 return 100;
1126 }
1127 Sleep(1000);
1128 ExitThread(0); // Do not redo exit() processing
1129 /*NOTREACHED*/
1130 return 0;
1131 }
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 {
1144 caughtsig = sig;
1145 }
1146
1147 static void test_exit(void)
1148 {
1149 printf("Main exit\n");
1150 }
1151
1152 int test_main(int argc, char **argv)
1153 {
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;
1216 }
1217
1218
1219 int main(int argc, char **argv)
1220 {
1221 static const daemon_winsvc_options svc_opts = {
1222 "-s", "test", "Test Service", "Service to test daemon_win32.c Module"
1223 };
1224
1225 return daemon_main("testd", &svc_opts, test_main, argc, argv);
1226 }
1227
1228 #endif