Skip to content
Snippets Groups Projects
Select Git revision
  • 89f200ed3f1300b10cd657eff3013865105f82e9
  • master default protected
  • Binary
  • add-version-information
  • os-path-join
  • develop-GA
  • timeFstatmap
  • add-higher-spindown-components
  • develop-DK
  • adds-header-to-grid-search
  • v1.3
  • v1.2
  • v1.1.2
  • v1.1.0
  • v1.0.1
15 results

computing_the_Bayes_factor.py

Blame
  • Forked from Gregory Ashton / PyFstat
    Source project has a limited visibility.
    daemon_win32.cpp 28.33 KiB
    /*
     * os_win32/daemon_win32.c
     *
     * Home page of code is: http://smartmontools.sourceforge.net
     *
     * Copyright (C) 2004 Christian Franke <smartmontools-support@lists.sourceforge.net>
     *
     * This program is free software; you can redistribute it and/or modify
     * it under the terms of the GNU General Public License as published by
     * the Free Software Foundation; either version 2, or (at your option)
     * any later version.
     *
     * You should have received a copy of the GNU General Public License
     * (for example COPYING); if not, write to the Free
     * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
     *
     */
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <signal.h>
    #include <io.h>
    
    #define WIN32_LEAN_AND_MEAN
    // Need MB_SERVICE_NOTIFICATION (NT4/2000/XP), IsDebuggerPresent() (Win98/ME/NT4/2000/XP)
    #define _WIN32_WINNT 0x0400 
    #include <windows.h>
    #ifdef _DEBUG
    #include <crtdbg.h>
    #endif
    
    #include "daemon_win32.h"
    
    const char *daemon_win32_c_cvsid = "$Id: daemon_win32.cpp,v 1.5 2004/08/09 14:35:59 chrfranke Exp $"
    DAEMON_WIN32_H_CVSID;
    
    
    /////////////////////////////////////////////////////////////////////////////
    
    #define ARGUSED(x) ((void)(x))
    
    // Prevent spawning of child process if debugging
    #ifdef _DEBUG
    #define debugging() IsDebuggerPresent()
    #else
    #define debugging() FALSE
    #endif
    
    
    #define EVT_NAME_LEN 260
    
    // Internal events (must be > SIGUSRn)
    #define EVT_RUNNING   100 // Exists when running, signaled on creation
    #define EVT_DETACHED  101 // Signaled when child detaches from console
    #define EVT_RESTART   102 // Signaled when child should restart
    
    static void make_name(char * name, int sig)
    {
    	int i;
    	if (!GetModuleFileNameA(NULL, name, EVT_NAME_LEN-10))
    		strcpy(name, "DaemonEvent");
    	for (i = 0; name[i]; i++) {
    		char c = name[i];
    		if (!(   ('0' <= c && c <= '9')
    		      || ('A' <= c && c <= 'Z')
    		      || ('a' <= c && c <= 'z')))
    			  name[i] = '_';
    	}
    	sprintf(name+strlen(name), "-%d", sig);
    }
    
    
    static HANDLE create_event(int sig, BOOL initial, BOOL errmsg, BOOL * exists)
    {
    	char name[EVT_NAME_LEN];
    	HANDLE h;
    	make_name(name, sig);
    	if (exists)
    		*exists = FALSE;
    	if (!(h = CreateEventA(NULL, FALSE, initial, name))) {
    		if (errmsg)
    			fprintf(stderr, "CreateEvent(.,\"%s\"): Error=%ld\n", name, GetLastError());
    		return 0;
    	}
    
    	if (GetLastError() == ERROR_ALREADY_EXISTS) {
    		if (!exists) {
    			if (errmsg)
    				fprintf(stderr, "CreateEvent(.,\"%s\"): Exists\n", name);
    			CloseHandle(h);
    			return 0;
    		}
    		*exists = TRUE;
    	}
    	return h;
    }
    
    
    static HANDLE open_event(int sig)
    {
    	char name[EVT_NAME_LEN];
    	make_name(name, sig);
    	return OpenEventA(EVENT_MODIFY_STATE, FALSE, name);
    }
    
    
    static int event_exists(int sig)
    {
    	char name[EVT_NAME_LEN];
    	HANDLE h;
    	make_name(name, sig);
    	if (!(h = OpenEventA(EVENT_MODIFY_STATE, FALSE, name)))
    		return 0;
    	CloseHandle(h);
    	return 1;
    }
    
    
    static int sig_event(int sig)
    {
    	char name[EVT_NAME_LEN];
    	HANDLE h;
    	make_name(name, sig);
    	if (!(h = OpenEventA(EVENT_MODIFY_STATE, FALSE, name))) {
    		make_name(name, EVT_RUNNING);
    		if (!(h = OpenEvent(EVENT_MODIFY_STATE, FALSE, name)))
    			return -1;
    		CloseHandle(h);
    		return 0;
    	}
    	SetEvent(h);
    	CloseHandle(h);
    	return 1;
    }
    
    
    static void daemon_help(FILE * f, const char * ident, const char * message)
    {
    	fprintf(f,
    		"%s: %s.\n"
    		"Use \"%s status|stop|reload|restart|sigusr1|sigusr2\" to control daemon.\n",
    		ident, message, ident);
    	fflush(f);
    }
    
    
    /////////////////////////////////////////////////////////////////////////////
    // Parent Process
    
    
    static BOOL WINAPI parent_console_handler(DWORD event)
    {
    	switch (event) {
    		case CTRL_C_EVENT:
    		case CTRL_BREAK_EVENT:
    			return TRUE; // Ignore
    	}
    	return FALSE; // continue with next handler ...
    }
    
    
    static int parent_main(HANDLE rev)
    {
    	HANDLE dev;
    	HANDLE ht[2];
    	char * cmdline;
    	STARTUPINFO si;
    	PROCESS_INFORMATION pi;
    	DWORD rc, exitcode;
    
    	// Ignore ^C, ^BREAK in parent
    	SetConsoleCtrlHandler(parent_console_handler, TRUE/*add*/);
    
    	// Create event used by child to signal daemon_detach()
    	if (!(dev = create_event(EVT_DETACHED, FALSE/*not signaled*/, TRUE, NULL/*must not exist*/))) {
    		CloseHandle(rev);
    		return 101;
    	}
    
    	// Restart process with same args
    	cmdline = GetCommandLineA();
    	memset(&si, 0, sizeof(si)); si.cb = sizeof(si);
    	
    	if (!CreateProcessA(
    		NULL, cmdline,
    		NULL, NULL, TRUE/*inherit*/,
    		0, NULL, NULL, &si, &pi)) {
    		fprintf(stderr, "CreateProcess(.,\"%s\",.) failed, Error=%ld\n", cmdline, GetLastError());
    		CloseHandle(rev); CloseHandle(dev);
    		return 101;
    	}
    	CloseHandle(pi.hThread);
    
    	// Wait for daemon_detach() or exit()
    	ht[0] = dev; ht[1] = pi.hProcess;
    	rc = WaitForMultipleObjects(2, ht, FALSE/*or*/, INFINITE);
    	if (!(/*WAIT_OBJECT_0(0) <= rc && */ rc < WAIT_OBJECT_0+2)) {
    		fprintf(stderr, "WaitForMultipleObjects returns %lX\n", rc);
    		TerminateProcess(pi.hProcess, 200);
    	}
    	CloseHandle(rev); CloseHandle(dev);
    
    	// Get exit code
    	if (!GetExitCodeProcess(pi.hProcess, &exitcode))
    		exitcode = 201;
    	else if (exitcode == STILL_ACTIVE) // detach()ed, assume OK
    		exitcode = 0;
    
    	CloseHandle(pi.hProcess);
    	return exitcode;
    }
    
    
    /////////////////////////////////////////////////////////////////////////////
    // Child Process
    
    
    static int svc_mode;   // Running as service?
    static int svc_paused; // Service paused?
    
    static void service_report_status(int state, int waithint);
    
    
    // Tables of signal handler and corresponding events
    typedef void (*sigfunc_t)(int);
    
    #define MAX_SIG_HANDLERS 8
    
    static int num_sig_handlers = 0;
    static sigfunc_t sig_handlers[MAX_SIG_HANDLERS];
    static int sig_numbers[MAX_SIG_HANDLERS];
    static HANDLE sig_events[MAX_SIG_HANDLERS];
    
    static HANDLE sigint_handle, sigbreak_handle, sigterm_handle;
    
    static HANDLE running_event;
    
    static int reopen_stdin, reopen_stdout, reopen_stderr;
    
    
    // Handler for windows console events
    
    static BOOL WINAPI child_console_handler(DWORD event)
    {
    	// Caution: runs in a new thread
    	// TODO: Guard with a mutex
    	HANDLE h = 0;
    	switch (event) {
    		case CTRL_C_EVENT: // <CONTROL-C> (SIGINT)
    			h = sigint_handle; break;
    		case CTRL_BREAK_EVENT: // <CONTROL-Break> (SIGBREAK/SIGQUIT)
    		case CTRL_CLOSE_EVENT: // User closed console or abort via task manager
    			h = sigbreak_handle; break;
    		case CTRL_LOGOFF_EVENT: // Logout/Shutdown (SIGTERM)
    		case CTRL_SHUTDOWN_EVENT:
    			h = sigterm_handle; break;
    	}
    	if (!h)
    		return FALSE; // continue with next handler
    	// Signal event
    	if (!SetEvent(h))
    		return FALSE;
    	return TRUE;
    }
    
    
    static void child_exit(void)
    {
    	int i;
    	char * cmdline;
    	HANDLE rst;
    	STARTUPINFO si;
    	PROCESS_INFORMATION pi;
    
    	for (i = 0; i < num_sig_handlers; i++)
    		CloseHandle(sig_events[i]);
    	num_sig_handlers = 0;
    	CloseHandle(running_event); running_event = 0;
    
    	// Restart?
    	if (!(rst = open_event(EVT_RESTART)))
    		return; // No => normal exit
    
    	// Yes => Signal exit and restart process
    	Sleep(500);
    	SetEvent(rst);
    	CloseHandle(rst);
    	Sleep(500);
    
    	cmdline = GetCommandLineA();
    	memset(&si, 0, sizeof(si)); si.cb = sizeof(si);
    	si.dwFlags = STARTF_USESHOWWINDOW; si.wShowWindow = SW_HIDE;
    
    	if (!CreateProcessA(
    		NULL, cmdline,
    		NULL, NULL, TRUE/*inherit*/,
    		0, NULL, NULL, &si, &pi)) {
    		fprintf(stderr, "CreateProcess(.,\"%s\",.) failed, Error=%ld\n", cmdline, GetLastError());
    	}
    	CloseHandle(pi.hThread); CloseHandle(pi.hProcess);
    }
    
    static int child_main(HANDLE hev,int (*main_func)(int, char **), int argc, char **argv)
    {
    	// Keep EVT_RUNNING open until exit
    	running_event = hev;
    
    	// Install console handler
    	SetConsoleCtrlHandler(child_console_handler, TRUE/*add*/);
    
    	// Install restart handler
    	atexit(child_exit);
    
    	// Continue in main_func() to do the real work
    	return main_func(argc, argv);
    }
    
    
    // Simulate signal()
    
    sigfunc_t daemon_signal(int sig, sigfunc_t func)
    {
    	int i;
    	HANDLE h;
    	if (func == SIG_DFL || func == SIG_IGN)
    		return func; // TODO
    	for (i = 0; i < num_sig_handlers; i++) {
    		if (sig_numbers[i] == sig) {
    			sigfunc_t old = sig_handlers[i];
    			sig_handlers[i] = func;
    			return old;
    		}
    	}
    	if (num_sig_handlers >= MAX_SIG_HANDLERS)
    		return SIG_ERR;
    	if (!(h = create_event(sig, FALSE, TRUE, NULL)))
    		return SIG_ERR;
    	sig_events[num_sig_handlers]   = h;
    	sig_numbers[num_sig_handlers]  = sig;
    	sig_handlers[num_sig_handlers] = func;
    	switch (sig) {
    		case SIGINT:   sigint_handle   = h; break;
    		case SIGTERM:  sigterm_handle  = h; break;
    		case SIGBREAK: sigbreak_handle = h; break;
    	}
    	num_sig_handlers++;
    	return SIG_DFL;
    }
    
    
    // strsignal()
    
    const char * daemon_strsignal(int sig)
    {
    	switch (sig) {
    		case SIGHUP:  return "SIGHUP";
    		case SIGINT:  return "SIGINT";
    		case SIGTERM: return "SIGTERM";
    		case SIGBREAK:return "SIGBREAK";
    		case SIGUSR1: return "SIGUSR1";
    		case SIGUSR2: return "SIGUSR2";
    		default:      return "*UNKNOWN*";
    	}
    }
    
    
    // Simulate sleep()
    
    void daemon_sleep(int seconds)
    {
    	do {
    		if (num_sig_handlers <= 0) {
    			Sleep(seconds*1000L);
    		}
    		else {
    			// Wait for any signal or timeout
    			DWORD rc = WaitForMultipleObjects(num_sig_handlers, sig_events,
    				FALSE/*OR*/, seconds*1000L);
    			if (rc != WAIT_TIMEOUT) {
    				if (!(/*WAIT_OBJECT_0(0) <= rc && */ rc < WAIT_OBJECT_0+(unsigned)num_sig_handlers)) {
    					fprintf(stderr,"WaitForMultipleObjects returns %lu\n", rc);
    					Sleep(seconds*1000L);
    					return;
    				}
    				// Call Handler
    				sig_handlers[rc-WAIT_OBJECT_0](sig_numbers[rc-WAIT_OBJECT_0]);
    				break;
    			}
    		}
    	} while (svc_paused);
    }
    
    
    // Disable/Enable console
    
    void daemon_disable_console()
    {
    	SetConsoleCtrlHandler(child_console_handler, FALSE/*remove*/);
    	reopen_stdin = reopen_stdout = reopen_stderr = 0;
    	if (isatty(fileno(stdin))) {
    		fclose(stdin); reopen_stdin = 1;
    	}
    	if (isatty(fileno(stdout))) {
    		fclose(stdout); reopen_stdout = 1;
    	}
    	if (isatty(fileno(stderr))) {
    		fclose(stderr); reopen_stderr = 1;
    	}
    	FreeConsole();
    	SetConsoleCtrlHandler(child_console_handler, TRUE/*add*/);
    }
    
    int daemon_enable_console(const char * title)
    {
    	BOOL ok;
    	SetConsoleCtrlHandler(child_console_handler, FALSE/*remove*/);
    	ok = AllocConsole();
    	SetConsoleCtrlHandler(child_console_handler, TRUE/*add*/);
    	if (!ok)
    		return -1;
    	if (title)
    		SetConsoleTitleA(title);
    	if (reopen_stdin)
    		freopen("conin$",  "r", stdin);
    	if (reopen_stdout)
    		freopen("conout$", "w", stdout);
    	if (reopen_stderr)
    		freopen("conout$", "w", stderr);
    	reopen_stdin = reopen_stdout = reopen_stderr = 0;
    	return 0;
    }
    
    
    // Detach daemon from console & parent
    
    int daemon_detach(const char * ident)
    {
    	if (!svc_mode) {
    		if (ident) {
    			// Print help
    			FILE * f = ( isatty(fileno(stdout)) ? stdout
    					   : isatty(fileno(stderr)) ? stderr : NULL);
    			if (f)
    				daemon_help(f, ident, "now detaches from console into background mode");
    		}
    		// Signal detach to parent
    		if (sig_event(EVT_DETACHED) != 1) {
    			if (!debugging())
    				return -1;
    		}
    		daemon_disable_console();
    	}
    	else {
    		// Signal end of initialization to service control manager
    		service_report_status(SERVICE_RUNNING, 0);
    	}
    
    	return 0;
    }
    
    
    /////////////////////////////////////////////////////////////////////////////
    // MessageBox
    
    #ifndef _MT
    //MT runtime not necessary, because mbox_thread uses no unsafe lib functions
    //#error Program must be linked with multithreaded runtime library
    #endif
    
    static LONG mbox_count; // # mbox_thread()s
    static HANDLE mbox_mutex; // Show 1 box at a time (not necessary for service)
    
    typedef struct mbox_args_s {
    	HANDLE taken; const char * title, * text; int mode;
    } mbox_args;
    
    
    // Thread to display one message box
    
    static ULONG WINAPI mbox_thread(LPVOID arg)
    {
    	// Take args
    	mbox_args * mb = (mbox_args *)arg;
    	char title[100]; char text[1000]; int mode;
    	strncpy(title, mb->title, sizeof(title)-1); title[sizeof(title)-1] = 0;
    	strncpy(text , mb->text , sizeof(text )-1); text [sizeof(text )-1] = 0;
    	mode = mb->mode;
    	SetEvent(mb->taken);
    
    	// Show only one box at a time
    	WaitForSingleObject(mbox_mutex, INFINITE);
    	MessageBoxA(NULL, text, title, mode);
    	ReleaseMutex(mbox_mutex);
    
    	InterlockedDecrement(&mbox_count);
    	return 0;
    }
    
    
    // Display a message box
    int daemon_messagebox(int system, const char * title, const char * text)
    {
    	mbox_args mb;
    	HANDLE ht; DWORD tid;
    
    	// Create mutex during first call
    	if (!mbox_mutex)
    		mbox_mutex = CreateMutex(NULL/*!inherit*/, FALSE/*!owned*/, NULL/*unnamed*/);
    
    	// Allow at most 10 threads
    	if (InterlockedIncrement(&mbox_count) > 10) {
    		InterlockedDecrement(&mbox_count);
    		return -1;
    	}
    
    	// Create thread
    	mb.taken = CreateEvent(NULL/*!inherit*/, FALSE, FALSE/*!signaled*/, NULL/*unnamed*/);
    	mb.mode = MB_OK|MB_ICONWARNING
    	         |(svc_mode?MB_SERVICE_NOTIFICATION:0)
    	         |(system?MB_SYSTEMMODAL:MB_APPLMODAL);
    	mb.title = title; mb.text = text;
    	mb.text = text;
    	if (!(ht = CreateThread(NULL, 0, mbox_thread, &mb, 0, &tid)))
    		return -1;
    
    	// Wait for args taken
    	if (WaitForSingleObject(mb.taken, 10000) != WAIT_OBJECT_0)
    		TerminateThread(ht, 0);
    	CloseHandle(mb.taken);
    	CloseHandle(ht);
    	return 0;
    }
    
    
    /////////////////////////////////////////////////////////////////////////////
    
    // Spawn a command and redirect <inpbuf >outbuf
    // return command's exitcode or -1 on error
    
    int daemon_spawn(const char * cmd,
                     const char * inpbuf, int inpsize,
                     char *       outbuf, int outsize )
    {
    	HANDLE pipe_inp_r, pipe_inp_w, pipe_out_r, pipe_out_w, pipe_err_w, h;
    	DWORD num_io, exitcode; int i;
    	SECURITY_ATTRIBUTES sa;
    	STARTUPINFO si; PROCESS_INFORMATION pi;
    	HANDLE self = GetCurrentProcess();
    
    	// Create stdin pipe with inheritable read side
    	memset(&sa, 0, sizeof(sa)); sa.nLength = sizeof(sa);
    	sa.bInheritHandle = TRUE;
    	if (!CreatePipe(&pipe_inp_r, &h, &sa, inpsize*2+13))
    		return -1;
    	if (!DuplicateHandle(self, h, self, &pipe_inp_w,
    		0, FALSE/*!inherit*/, DUPLICATE_SAME_ACCESS)) {
    		CloseHandle(pipe_inp_r); CloseHandle(pipe_inp_w);
    		return -1;
    	}
    	CloseHandle(h);
    
    	// Create stdout pipe with inheritable write side
    	memset(&sa, 0, sizeof(sa)); sa.nLength = sizeof(sa);
    	sa.bInheritHandle = TRUE;
    	if (!CreatePipe(&h, &pipe_out_w, &sa, outsize)) {
    		CloseHandle(pipe_inp_r); CloseHandle(pipe_inp_w);
    		return -1;
    	}
    	if (!DuplicateHandle(self, h, self, &pipe_out_r,
    		0, FALSE/*!inherit*/, DUPLICATE_SAME_ACCESS)) {
    		CloseHandle(h);          CloseHandle(pipe_out_w);
    		CloseHandle(pipe_inp_r); CloseHandle(pipe_inp_w);
    		return -1;
    	}
    	CloseHandle(h);
    
    	// Create stderr handle as dup of stdout write side
    	if (!DuplicateHandle(self, pipe_out_w, self, &pipe_err_w,
    		0, TRUE/*inherit*/, DUPLICATE_SAME_ACCESS)) {
    		CloseHandle(pipe_out_r); CloseHandle(pipe_out_w);
    		CloseHandle(pipe_inp_r); CloseHandle(pipe_inp_w);
    		return -1;
    	}
    
    	// Create process with pipes as stdio
    	memset(&si, 0, sizeof(si)); si.cb = sizeof(si);
    	si.hStdInput  = pipe_inp_r;
    	si.hStdOutput = pipe_out_w;
    	si.hStdError  = pipe_err_w;
    	si.dwFlags = STARTF_USESTDHANDLES;
    	if (!CreateProcessA(
    		NULL, (char*)cmd,
    		NULL, NULL, TRUE/*inherit*/,
    		DETACHED_PROCESS/*no new console*/,
    		NULL, NULL, &si, &pi)) {
    		CloseHandle(pipe_err_w);
    		CloseHandle(pipe_out_r); CloseHandle(pipe_out_w);
    		CloseHandle(pipe_inp_r); CloseHandle(pipe_inp_w);
    		return -1;
    	}
    	CloseHandle(pi.hThread);
    	// Close inherited handles
    	CloseHandle(pipe_inp_r);
    	CloseHandle(pipe_out_w);
    	CloseHandle(pipe_err_w);
    
    	// Copy inpbuf to stdin
    	// convert \n => \r\n 
    	for (i = 0; i < inpsize; ) {
    		int len = 0;
    		while (i+len < inpsize && inpbuf[i+len] != '\n')
    			len++;
    		if (len > 0)
    			WriteFile(pipe_inp_w, inpbuf+i, len, &num_io, NULL);
    		i += len;
    		if (i < inpsize) {
    			WriteFile(pipe_inp_w, "\r\n", 2, &num_io, NULL);
    			i++;
    		}
    	}
    	CloseHandle(pipe_inp_w);
    
    	// Copy stdout to output buffer until full, rest to /dev/null
    	// convert \r\n => \n
    	for (i = 0; ; ) {
    		char buf[256];
    		int j;
    		if (!ReadFile(pipe_out_r, buf, sizeof(buf), &num_io, NULL) || num_io == 0)
    			break;
    		for (j = 0; i < outsize-1 && j < (int)num_io; j++) {
    			if (buf[j] != '\r')
    				outbuf[i++] = buf[j];
    		}	
    	}
    	outbuf[i] = 0;
    	CloseHandle(pipe_out_r);
    
    	// Wait for process exitcode
    	WaitForSingleObject(pi.hProcess, INFINITE);
    	exitcode = 42;
    	GetExitCodeProcess(pi.hProcess, &exitcode);
    	CloseHandle(pi.hProcess);
    	return exitcode;
    }
    
    
    /////////////////////////////////////////////////////////////////////////////
    // Initd Functions
    
    static int wait_signaled(HANDLE h, int seconds)
    {
    	int i;
    	for (i = 0; ; ) {
    		if (WaitForSingleObject(h, 1000L) == WAIT_OBJECT_0)
    			return 0;
    		if (++i >= seconds)
    			return -1;
    		fputchar('.'); fflush(stdout);
    	}
    }
    
    
    static int wait_evt_running(int seconds, int exists)
    {
    	int i;
    	if (event_exists(EVT_RUNNING) == exists)
    		return 0;
    	for (i = 0; ; ) {
    		Sleep(1000);
    		if (event_exists(EVT_RUNNING) == exists)
    			return 0;
    		if (++i >= seconds)
    			return -1;
    		fputchar('.'); fflush(stdout);
    	}
    }
    
    
    static int is_initd_command(char * s)
    {
    	if (!strcmp(s, "status"))
    		return EVT_RUNNING;
    	if (!strcmp(s, "stop"))
    		return SIGTERM;
    	if (!strcmp(s, "reload"))
    		return SIGHUP;
    	if (!strcmp(s, "sigusr1"))
    		return SIGUSR1;
    	if (!strcmp(s, "sigusr2"))
    		return SIGUSR2;
    	if (!strcmp(s, "restart"))
    		return EVT_RESTART;
    	return -1;
    }
    
    
    static int initd_main(const char * ident, int argc, char **argv)
    {
    	int rc;
    	if (argc < 2)
    		return -1;
    	if ((rc = is_initd_command(argv[1])) < 0)
    		return -1;
    	if (argc != 2) {
    		printf("%s: no arguments allowed for command %s\n", ident, argv[1]);
    		return 1;
    	}
    
    	switch (rc) {
    		default:
    		case EVT_RUNNING:
    			printf("Checking for %s:", ident); fflush(stdout);
    			rc = event_exists(EVT_RUNNING);
    			puts(rc ? " running" : " not running");
    			return (rc ? 0 : 1);
    
    		case SIGTERM:
    			printf("Stopping %s:", ident); fflush(stdout);
    			rc = sig_event(SIGTERM);
    			if (rc <= 0) {
    				puts(rc < 0 ? " not running" : " error");
    				return (rc < 0 ? 0 : 1);
    			}
    			rc = wait_evt_running(10, 0);
    			puts(!rc ? " done" : " timeout");
    			return (!rc ? 0 : 1);
    
    		case SIGHUP:
    			printf("Reloading %s:", ident); fflush(stdout);
    			rc = sig_event(SIGHUP);
    			puts(rc > 0 ? " done" : rc == 0 ? " error" : " not running");
    			return (rc > 0 ? 0 : 1);
    
    		case SIGUSR1:
    		case SIGUSR2:
    			printf("Sending SIGUSR%d to %s:", (rc-SIGUSR1+1), ident); fflush(stdout);
    			rc = sig_event(rc);
    			puts(rc > 0 ? " done" : rc == 0 ? " error" : " not running");
    			return (rc > 0 ? 0 : 1);
    
    		case EVT_RESTART:
    			{
    				HANDLE rst;
    				printf("Stopping %s:", ident); fflush(stdout);
    				if (event_exists(EVT_DETACHED)) {
    					puts(" not detached, cannot restart");
    					return 1;
    				}
    				if (!(rst = create_event(EVT_RESTART, FALSE, FALSE, NULL))) {
    					puts(" error");
    					return 1;
    				}
    				rc = sig_event(SIGTERM);
    				if (rc <= 0) {
    					puts(rc < 0 ? " not running" : " error");
    					CloseHandle(rst);
    					return 1;
    				}
    				rc = wait_signaled(rst, 10);
    				CloseHandle(rst);
    				if (rc) {
    					puts(" timeout");
    					return 1;
    				}
    				puts(" done");
    				Sleep(100);
    
    				printf("Starting %s:", ident); fflush(stdout);
    				rc = wait_evt_running(10, 1);
    				puts(!rc ? " done" : " error");
    				return (!rc ? 0 : 1);
    			}
    	}
    }
    
    
    /////////////////////////////////////////////////////////////////////////////
    // Windows Service Functions
    
    int daemon_winsvc_exitcode; // Set by app to exit(code)
    
    static SERVICE_STATUS_HANDLE svc_handle;
    static SERVICE_STATUS svc_status;
    
    
    // Report status to SCM
    
    static void service_report_status(int state, int seconds)
    {
    	// TODO: Avoid race
    	static DWORD checkpoint = 1;
    	svc_status.dwCurrentState = state;
    	svc_status.dwWaitHint = seconds*1000;
    	switch (state) {
    		default:
    			svc_status.dwCheckPoint = checkpoint++;
    			break;
    		case SERVICE_RUNNING:
    		case SERVICE_STOPPED:
    			svc_status.dwCheckPoint = 0;
    	}
    	switch (state) {
    		case SERVICE_START_PENDING:
    		case SERVICE_STOP_PENDING:
    			svc_status.dwControlsAccepted = 0;
    			break;
    		default:
    			svc_status.dwControlsAccepted =
    				SERVICE_ACCEPT_STOP|SERVICE_ACCEPT_SHUTDOWN|
    				SERVICE_ACCEPT_PAUSE_CONTINUE|SERVICE_ACCEPT_PARAMCHANGE;
    			break;
    	}
    	SetServiceStatus(svc_handle, &svc_status);
    }
    
    
    // Control the service, called by SCM
    
    static void WINAPI service_control(DWORD ctrlcode)
    {
    	switch (ctrlcode) {
    		case SERVICE_CONTROL_STOP:
    		case SERVICE_CONTROL_SHUTDOWN:
    			service_report_status(SERVICE_STOP_PENDING, 30);
    			svc_paused = 0;
    			sig_event(SIGTERM);
    			break;
    		case SERVICE_CONTROL_PARAMCHANGE: // Win2000/XP
    			service_report_status(svc_status.dwCurrentState, 0);
    			svc_paused = 0;
    			sig_event(SIGHUP); // reload
    			break;
    		case SERVICE_CONTROL_PAUSE:
    			service_report_status(SERVICE_PAUSED, 0);
    			svc_paused = 1;
    			break;
    		case SERVICE_CONTROL_CONTINUE:
    			service_report_status(SERVICE_RUNNING, 0);
    			{
    				int was_paused = svc_paused;
    				svc_paused = 0;
    				sig_event(was_paused ? SIGHUP : SIGUSR1); // reload:recheck
    			}
    			break;
    		case SERVICE_CONTROL_INTERROGATE:
    		default: // unknown
    			service_report_status(svc_status.dwCurrentState, 0);
    			break;
    	}
    }
    
    
    // Exit handler for service
    
    static void service_exit(void)
    {
    	// Close signal events
    	int i;
    	for (i = 0; i < num_sig_handlers; i++)
    		CloseHandle(sig_events[i]);
    	num_sig_handlers = 0;
    
    	// Set exitcode
    	if (daemon_winsvc_exitcode) {
    		svc_status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
    		svc_status.dwServiceSpecificExitCode = daemon_winsvc_exitcode;
    	}
    	// Report stopped
    	service_report_status(SERVICE_STOPPED, 0);
    }
    
    
    // Variables for passing main(argc, argv) from daemon_main to service_main()
    static int (*svc_main_func)(int, char **);
    static int svc_main_argc;
    static char ** svc_main_argv;
    
    // Main function for service, called by service dispatcher
    
    static void WINAPI service_main(DWORD argc, LPSTR * argv)
    {
    	char path[MAX_PATH], *p;
    	ARGUSED(argc);
    
    	// Register control handler
    	svc_handle = RegisterServiceCtrlHandler(argv[0], service_control);
    
    	// Init service status
    	svc_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS;
    	service_report_status(SERVICE_START_PENDING, 10);
    
    	// Service started in \windows\system32, change to .exe directory
    	if (GetModuleFileNameA(NULL, path, sizeof(path)) && (p = strrchr(path, '\\'))) {
    		*p = 0;	SetCurrentDirectoryA(path);
    	}
    	
    	// Install exit handler
    	atexit(service_exit);
    
    	// Do the real work, service status later updated by daemon_detach()
    	daemon_winsvc_exitcode = svc_main_func(svc_main_argc, svc_main_argv);
    
    	exit(daemon_winsvc_exitcode);
    	// ... continued in service_exit()
    }
    
    
    /////////////////////////////////////////////////////////////////////////////
    // Windows Service Admin Functions
    
    // Set Service description (Win2000/XP)
    
    static int svcadm_setdesc(SC_HANDLE hs, const char * desc)
    {
    	HANDLE hdll;
    	BOOL (WINAPI * ChangeServiceConfig2A_p)(SC_HANDLE, DWORD, LPVOID);
    	BOOL ret;
    	if (!(hdll = LoadLibraryA("ADVAPI32.DLL")))
    		return FALSE;
    	if (!((ChangeServiceConfig2A_p = (BOOL (WINAPI *)(SC_HANDLE, DWORD, LPVOID))GetProcAddress(hdll, "ChangeServiceConfig2A"))))
    		ret = FALSE;
    	else {
    		SERVICE_DESCRIPTIONA sd = { (char *)desc };
    		ret = ChangeServiceConfig2A_p(hs, SERVICE_CONFIG_DESCRIPTION, &sd);
    	}
    	FreeLibrary(hdll);
    	return ret;
    }
    
    
    // Service install/remove commands
    
    static int svcadm_main(const char * ident, const daemon_winsvc_options * svc_opts,
                           int argc, char **argv                                      )
    {
    	int remove;
    	SC_HANDLE hm, hs;
    
    	if (argc < 2)
    		return -1;
    	if (!strcmp(argv[1], "install"))
    		remove = 0;
    	else if (!strcmp(argv[1], "remove")) {
    		if (argc != 2) {
    			printf("%s: no arguments allowed for command remove\n", ident);
    			return 1;
    		}
    		remove = 1;
    	}
    	else
    		return -1;
    
    	printf("%s service %s:", (!remove?"Installing":"Removing"), ident); fflush(stdout);
    
    	// Open SCM
    	if (!(hm = OpenSCManager(NULL/*local*/, NULL/*default*/, SC_MANAGER_ALL_ACCESS))) {
    		printf(" cannot open SCManager, Error=%ld\n", GetLastError());
    		return 1;
    	}
    
    	if (!remove) {
    		char path[MAX_PATH+100];
    		int i;
    		// Get program path
    		if (!GetModuleFileNameA(NULL, path, MAX_PATH)) {
    			printf(" unknown program path, Error=%ld\n", GetLastError());
    			CloseServiceHandle(hm);
    			return 1;
    		}
    		// Append options
    		strcat(path, " "); strcat(path, svc_opts->cmd_opt);
    		for (i = 2; i < argc; i++) {
    			const char * s = argv[i];
    			if (strlen(path)+strlen(s)+1 >= sizeof(path))
    				break;
    			strcat(path, " "); strcat(path, s);
    		}
    		// Create
    		if (!(hs = CreateService(hm,
    			svc_opts->svcname, svc_opts->dispname,
    			SERVICE_ALL_ACCESS,
    			SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS,
    			SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, path,
    			NULL/*no load ordering*/, NULL/*no tag id*/,
    			""/*no depedencies*/, NULL/*local system account*/, NULL/*no pw*/))) {
    			printf(" failed, Error=%ld\n", GetLastError());
    			CloseServiceHandle(hm);
    			return 1;
    		}
    		// Set optional description
    		if (svc_opts->descript)
    			svcadm_setdesc(hs, svc_opts->descript);
    	}
    	else {
    		// Open
    		if (!(hs = OpenService(hm, ident, SERVICE_ALL_ACCESS))) {
    			puts(" not found");
    			CloseServiceHandle(hm);
    			return 1;
    		}
    		// TODO: Stop service if running
    		// Remove
    		if (!DeleteService(hs)) {
    			printf(" failed, Error=%ld\n", GetLastError());
    			CloseServiceHandle(hs); CloseServiceHandle(hm);
    			return 1;
    		}
    	}
    	puts(" done");
    	CloseServiceHandle(hs); CloseServiceHandle(hm);
    	return 0;
    }
    
    
    /////////////////////////////////////////////////////////////////////////////
    // Main Function
    
    // This function must be called from main()
    // main_func is the function doing the real work
    
    int daemon_main(const char * ident, const daemon_winsvc_options * svc_opts,
                    int (*main_func)(int, char **), int argc, char **argv      )
    {
    	int rc;
    #ifdef _DEBUG
    	// Enable Debug heap checks
    	_CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG)
    		|_CRTDBG_ALLOC_MEM_DF|_CRTDBG_CHECK_ALWAYS_DF|_CRTDBG_LEAK_CHECK_DF);
    #endif
    
    	// Check for [status|stop|reload|restart|sigusr1|sigusr2] parameters
    	if ((rc = initd_main(ident, argc, argv)) >= 0)
    		return rc;
    	// Check for [install|remove] parameters
    	if (svc_opts && (rc = svcadm_main(ident, svc_opts, argc, argv)) >= 0)
    		return rc;
    
    	// Run as service if svc_opts.cmd_opt is given as first(!) argument
    	svc_mode = (svc_opts && argc >= 2 && !strcmp(argv[1], svc_opts->cmd_opt));
    
    	if (!svc_mode) {
    		// Daemon: Try to simulate a Unix-like daemon
    		HANDLE rev;
    		BOOL exists;
    
    		// Create main event to detect process type:
    		// 1. new: parent process => start child and wait for detach() or exit() of child.
    		// 2. exists && signaled: child process => do the real work, signal detach() to parent
    		// 3. exists && !signaled: already running => exit()
    		if (!(rev = create_event(EVT_RUNNING, TRUE/*signaled*/, TRUE, &exists)))
    			return 100;
    
    		if (!exists && !debugging()) {
    			// Event new => parent process
    			return parent_main(rev);
    		}
    
    		if (WaitForSingleObject(rev, 0) == WAIT_OBJECT_0) {
    			// Event was signaled => In child process
    			return child_main(rev, main_func, argc, argv);
    		}
    
    		// Event no longer signaled => Already running!
    		daemon_help(stdout, ident, "already running");
    		CloseHandle(rev);
    		return 1;
    	}
    	else {
    		// Service: Start service_main() via SCM
    		SERVICE_TABLE_ENTRY service_table[] = {
    			{ (char*)svc_opts->svcname, service_main }, { NULL, NULL }
    		};
    
    		svc_main_func = main_func;
    		svc_main_argc = argc;
    		svc_main_argv = argv;
    		if (!StartServiceCtrlDispatcher(service_table)) {
    			fprintf(stderr, "%s: cannot dispatch service, Error=%ld\n", ident, GetLastError());
    #ifdef _DEBUG
    			if (debugging())
    				service_main(argc, argv);
    #endif
    			return 100;
    		}
    		Sleep(1000);
    		ExitThread(0); // Do not redo exit() processing
    		/*NOTREACHED*/
    		return 0;
    	}
    }
    
    
    /////////////////////////////////////////////////////////////////////////////
    // Test Program
    
    #ifdef TEST
    
    static volatile sig_atomic_t caughtsig = 0;
    
    static void sig_handler(int sig)
    {
    	caughtsig = sig;
    }
    
    static void test_exit(void)
    {
    	printf("Main exit\n");
    }
    
    int test_main(int argc, char **argv)
    {
    	int i;
    	int debug = 0;
    
    	printf("PID=%ld\n", GetCurrentProcessId());
    	for (i = 0; i < argc; i++) {
    		printf("%d: \"%s\"\n", i, argv[i]);
    		if (!strcmp(argv[i],"-d"))
    			debug = 1;
    	}
    
    	daemon_signal(SIGINT, sig_handler);
    	daemon_signal(SIGBREAK, sig_handler);
    	daemon_signal(SIGTERM, sig_handler);
    	daemon_signal(SIGHUP, sig_handler);
    	daemon_signal(SIGUSR2, sig_handler);
    
    	atexit(test_exit);
    
    	if (!debug) {
    		printf("Preparing to detach...\n");
    		Sleep(2000);
    		daemon_detach("test");
    		printf("Detached!\n");
    	}
    
    	for (;;) {
    		daemon_sleep(1);
    		printf("."); fflush(stdout);
    		if (caughtsig) {
    			if (caughtsig == SIGUSR2) {
    				debug ^= 1;
    				if (debug)
    					daemon_enable_console("Daemon[Debug]");
    				else
    					daemon_disable_console();
    			}
    			printf("[PID=%ld: Signal=%d]", GetCurrentProcessId(), caughtsig); fflush(stdout);
    			if (caughtsig == SIGTERM || caughtsig == SIGBREAK)
    				break;
    			caughtsig = 0;
    		}
    	}
    	printf("\nExiting on signal %d\n", caughtsig);
    	return 0;
    }
    
    
    int main(int argc, char **argv)
    {
    	static const daemon_winsvc_options svc_opts = {
    	"-s", "test", "Test Service", "Service to test daemon_win32.c Module"
    	};
    
    	return daemon_main("testd", &svc_opts, test_main, argc, argv);
    }
    
    #endif