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,v 1.1.1.2 2013/07/22 01:17:36 misho Exp $"
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
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>