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>