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