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