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