File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / smartmontools / os_win32 / daemon_win32.cpp
Revision 1.1: download - view: text, annotated - select for diffs - revision graph
Tue Feb 21 16:32:16 2012 UTC (12 years, 6 months ago) by misho
CVS tags: MAIN, HEAD
Initial revision

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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>