//-----------------------------------------------------------------------------
// Copyright © 2003 - Philip Howard - All rights reserved
//
// 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
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
//-----------------------------------------------------------------------------
// package	libh/daemon
// purpose	Support daemon startup
// homepage	http://libh.slashusr.org/
//-----------------------------------------------------------------------------
// author	Philip Howard
// email	libh at ipal dot org
// homepage	http://phil.ipal.org/
//-----------------------------------------------------------------------------
// This file is best viewed using a fixed spaced font such as Courier
// and in a display at least 120 columns wide.
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// file		daemon.c
//
// purpose	All the daemon functions are contained here so that they may
//		share non-exported state between them.  Think of it as a
//		non-instantiated class.
//-----------------------------------------------------------------------------
#include "daemon_lib.h"

//-----------------------------------------------------------------------------
// Define variables to be shared between functions including signal handlers.
//-----------------------------------------------------------------------------
static time_t		timeout_first			= 10;
static time_t		timeout_repeat			= 5;
static int		timeout_state			= 0;
static int		master_signaled			= 0;
static int		verbose_signals			= 0;
static int		pipes_active			= 0;
static pid_t		start_pid			= 0;
static pid_t		master_pid			= 0;
static volatile pid_t	server_pid			= 0;
static volatile int	master_status			= 0;
static volatile int	server_status			= 0;
static time_t		server_start_time		= 0;
static int		server_fast_count		= 0;
static time_t		server_fast_time		= 10; // less than 10 seconds is too fast
static int		server_fast_max			= 10; // more than 10 times too fast then die
static int		server_count			= 0;
static int		server_count_max		= 0;
static unsigned int	restart_count_max		= 0;
static volatile int	worker_seq_ended		= 0;
static volatile int	server_sigalrm			= 0;
static int		start_return_fatal		= 0;
static int		master_return_fatal		= 0;
static int		server_return_fatal		= 0;
static int		server_return_restart		= 0;
static int		server_return_shutdown		= 0;
static int		shutdown_slow			= 0;
static int		shutdown_exit			= 0;
static int		ignore_hup			= 0; // need to implement a set function
static int		signal_fd			= -1;
static int		exit_status			= -1;
static unsigned long	server_tick_interval		= 60;
static unsigned long	server_tick_offset		= 30;
static unsigned int	worker_count_max		= 8;
static const char *	program_name			= NULL;
static sigset_t		sigset_all			;
static sigset_t		sigset_none			;

const static char	octets				[4] = { 0, 1, 2, 3 };


//-----------------------------------------------------------------------------
// function	daemon_msg_printf
//
// purpose	Centralize error message formatting.
//
// arguments	1 (const char *) function name
//		2 (const char *) format string
//		3 (va_list) variable argument pointer
//
// returns	(int) 1 for use in _exit()
//-----------------------------------------------------------------------------
static
int
daemon_msg_printf (
    const char *	arg_function
    ,
    const char *	arg_format
    ,
    va_list		arg_vararg
    )
{
    char	message		[1024];
    char *	msg_ptr		;
    size_t	msg_size	;
    int		len		;
    int		save_errno	;


    //-------------------------------------------
    // If the format begins with '*', remember to
    // append the system error message string.
    //-------------------------------------------
    save_errno = 0;
    if ( arg_format[0] == '*' ) {
	++ arg_format;
	save_errno = errno;
    }

    //------------------------------------
    // Start with the full message buffer.
    // Reserve a spot to add a newline.
    //------------------------------------
    msg_ptr = message;
    msg_size = sizeof (message) - 1;

    //-----------------------------------
    // Prefix the program name, if given.
    //-----------------------------------
    if ( program_name ) {
	len = strlen( program_name );
	if ( len > 256 ) len = 256;
	memcpy( msg_ptr, program_name, len );
	msg_ptr += len;
	msg_size -= len;

	//--------------------------------------------------
	// If a function name is also given, separate these.
	//--------------------------------------------------
	if ( arg_function ) {
	    msg_ptr[0] = ':';
	    msg_ptr[1] = ':';
	    msg_ptr += 2;
	    msg_size -= 2;
	}
    }

    //------------------------------------
    // Add in the function name, if given.
    //------------------------------------
    if ( arg_function ) {
	len = strlen( arg_function );
	if ( len > 256 ) len = 256;
	memcpy( msg_ptr, arg_function, len );
	msg_ptr += len;
	msg_size -= len;
    }

    //------------------------------------------
    // Add in the process ID number and a space.
    //------------------------------------------
    len = snprintf( msg_ptr, msg_size, "[%u] ", getpid() );
    msg_ptr += len;
    msg_size -= len;

    //-----------------------------------------------
    // Format and append the actual message contents.
    //-----------------------------------------------
    len = vsnprintf( msg_ptr, msg_size, arg_format, arg_vararg );
    if ( len >= msg_size || len < 0 ) len = msg_size;
    msg_ptr += len;
    msg_size -= len;

    //------------------------------------------------------
    // If requested, append the system error message string.
    //------------------------------------------------------
    if ( save_errno && msg_size > 3 ) {
	len = snprintf( msg_ptr, msg_size, ": %s", strerror( save_errno ) );
	if ( len >= msg_size || len < 0 ) len = msg_size;
	msg_ptr += len;
	msg_size -= len;
    }

    //-----------------------------------------
    // Make sure there is a newline at the end.
    //-----------------------------------------
    if ( * -- msg_ptr != '\n' ) * ++ msg_ptr = '\n';
    * ++ msg_ptr = 0;

    //-------------------------------------
    // Write the whole message out at once.
    //-------------------------------------
    fputs( message, stderr );

    //--------------------------------------------------------------
    // Finish and always return a 1 to be used by return or _exit().
    //--------------------------------------------------------------
    return 1;
}

//-----------------------------------------------------------------------------
// function	daemon_msg
//
// purpose	Output a formatted message with optional function name
//
// arguments	1 (const char *) function name
//		2 (const char *) format string
//		3..N (...) variable arguments for format
//
// returns	(int) 1 for use in _exit()
//-----------------------------------------------------------------------------
static
int
daemon_msg (
    const char *	arg_function
    ,
    const char *	arg_format
    ,
    ...
    )
{
    va_list	var_arg		;

    va_start( var_arg, arg_format );
    daemon_msg_printf( arg_function, arg_format, var_arg );
    va_end( var_arg );
    return 1;
}

//-----------------------------------------------------------------------------
// function	daemon_fatal
//
// purpose	Output a formatted error message with optional function name
//		and system error string, and exit process if code is positive
//
// arguments	1 (int) negative: return, positive: exit
//		2 (const char *) function name
//		3 (const char *) format string
//		4..N (...) variable arguments for format
//
// returns	(int) caller return code if < 0
//		or exits instead
//-----------------------------------------------------------------------------
static
int
daemon_fatal (
    int			arg_code
    ,
    const char *	arg_function
    ,
    const char *	arg_format
    ,
    ...
    )
{
    va_list	var_arg		;

    va_start( var_arg, arg_format );
    daemon_msg_printf( arg_function, arg_format, var_arg );
    va_end( var_arg );
    if ( arg_code >= 0 ) _exit( 1 );
    return arg_code;
}

//-----------------------------------------------------------------------------
// function	daemon_sync_timer
//
// purpose	Start the next alarm/timer at a syncronized interval.
//		The setitimer() syscall does not guarantee syncronization.
//
// arguments	1 (long) time interval in seconds
//		2 (long) time offset in seconds, modulo interval
//
// returns	(int) == 0 : OK
//		(int)  < 0 : error
//-----------------------------------------------------------------------------
static
int
daemon_sync_timer (
    long		arg_interval
    ,
    long		arg_offset
    )
{
    struct itimerval	this_time	;
    long		next_time	;

    if ( gettimeofday( & this_time.it_interval, NULL ) < 0 )
	return daemon_msg( __func__, "*gettimeofday" );
    next_time = this_time.it_interval.tv_sec + arg_interval - arg_offset;
    next_time -= next_time % arg_interval;
    this_time.it_value.tv_sec = next_time + arg_offset - this_time.it_interval.tv_sec - 1;
    this_time.it_value.tv_usec = 999999 - this_time.it_interval.tv_usec;
    this_time.it_interval.tv_usec = this_time.it_interval.tv_sec = 0;
    if ( setitimer( ITIMER_REAL, & this_time, NULL ) < 0 )
	return daemon_msg( __func__, "*setitimer" );
    return 0;
}

__PROTO_BEGIN__
//-----------------------------------------------------------------------------
// function	daemon_set_start_return_fatal
//
// purpose	Set the return code for fatal errors from daemon_start().
//		The default value of zero means to exit.  A non-zero value
//		will be the negative return value.
//
// arguments	1 (int) return code value to use, or 0 to not return
//
// returns	(int) the given return code value
//-----------------------------------------------------------------------------
int
daemon_set_start_return_fatal (
    signed int		arg_code
    )
__PROTO_END__
{
    __FUNC_NAME__( daemon_set_start_return_fatal );
    return start_return_fatal = arg_code > 0 ? - arg_code : arg_code;
}

__PROTO_BEGIN__
//-----------------------------------------------------------------------------
// function	daemon_set_master_return_fatal
//
// purpose	Set the return code for fatal errors from daemon_master().
//		The default value of zero means to exit.  A non-zero value
//		will be the negative return value.
//
// arguments	1 (int) return code value to use, or 0 to not return
//
// returns	(int) the given return code value
//-----------------------------------------------------------------------------
int
daemon_set_master_return_fatal (
    signed int		arg_code
    )
__PROTO_END__
{
    __FUNC_NAME__( daemon_set_master_return_fatal );
    return master_return_fatal = arg_code > 0 ? - arg_code : arg_code;
}

__PROTO_BEGIN__
//-----------------------------------------------------------------------------
// function	daemon_set_server_return_fatal
//
// purpose	Set the return code for fatal errors from daemon_server().
//		The default value of zero means to exit.  A non-zero value
//		will be the negative return value.
//
// arguments	1 (int) return code value to use, or 0 to not return
//
// returns	(int) the given return code value
//-----------------------------------------------------------------------------
int
daemon_set_server_return_fatal (
    signed int		arg_code
    )
__PROTO_END__
{
    __FUNC_NAME__( daemon_set_server_return_fatal );
    return server_return_fatal = arg_code > 0 ? - arg_code : arg_code;
}

__PROTO_BEGIN__
//-----------------------------------------------------------------------------
// function	daemon_set_server_return_restart
//
// purpose	Set the return code for restarts from daemon_server().
//		The default value of zero means to exit with a status of 0
//		to cause the master process to restart a new server process.
//		A non-zero value will be the negative return value.
//
// note		When the caller of daemon_server() gets a return value of
//		the specified code, it should eventually exit with a status
//		of 0 unless it decides the situation warrants a shutdown.
//
// arguments	1 (int) return code value to use, or 0 to not return
//
// returns	(int) the given return code value
//-----------------------------------------------------------------------------
int
daemon_set_server_return_restart (
    signed int		arg_code
    )
__PROTO_END__
{
    __FUNC_NAME__( daemon_set_server_return_restart );
    return server_return_restart = arg_code > 0 ? - arg_code : arg_code;
}

__PROTO_BEGIN__
//-----------------------------------------------------------------------------
// function	daemon_set_server_return_shutdown
//
// purpose	Set the return code for shutdown errors from daemon_server().
//		The default value of zero means to exit with a status of 1
//		to cause the master process to exit.  A non-zero value will be
//		the negative return value.
//
// note		When the caller of daemon_server() gets a return value of
//		the specified code, it should eventually exit with a status
//		of 1 unless it decides the situation warrants a restart.
//
// arguments	1 (int) return code value to use, or 0 to not return
//
// returns	(int) the given return code value
//-----------------------------------------------------------------------------
int
daemon_set_server_return_shutdown (
    signed int		arg_code
    )
__PROTO_END__
{
    __FUNC_NAME__( daemon_set_server_return_shutdown );
    return server_return_shutdown = arg_code > 0 ? - arg_code : arg_code;
}

__PROTO_BEGIN__
//-----------------------------------------------------------------------------
// function	daemon_set_program_name
//
// purpose	Set the program name for error messages.  This function picks
//		only the final part of the program name if it is a path.
//
// arguments	1 (const char *) program name.
//
// returns	1 (const char *) pointer to final part of program name used
//				 as a convenience for the caller.
//-----------------------------------------------------------------------------
const char *
daemon_set_program_name (
    const char *	arg_name
    )
__PROTO_END__
{
    program_name = arg_name;
    while ( * arg_name ) {
	if ( * arg_name ++ == '/' ) program_name = arg_name;
    }
    return program_name;
}

__PROTO_BEGIN__
//-----------------------------------------------------------------------------
// function	daemon_set_start_timeout
//
// purpose	Set the value of the start timeout for the 1st timeout cycle.
//
//		This timeout limits how long the startup process in the shell
//		foreground will wait for the background server to indicate
//		that it has successfully started running.
//
// arguments	1 (time_t) timeout value in seconds, or 0 to disable
//
// returns	(int)  < 0 : error
//		(int) == 0 : OK
//-----------------------------------------------------------------------------
int
daemon_set_start_timeout (
    time_t	arg_timeout
    )
__PROTO_END__
{
    __FUNC_NAME__( daemon_set_start_timeout );

    timeout_first = arg_timeout;
    return 0;
}

__PROTO_BEGIN__
//-----------------------------------------------------------------------------
// function	daemon_set_restart_count_max
//
// purpose	Set the value of the restart count maximum.  The restart
//		process will not restart an exiting server more than this
//		many times.
//
// arguments	1 (int) count of times to restart, 0 for disabled.
//
// returns	(int)  < 0 : error
//		(int) == 0 : OK
//-----------------------------------------------------------------------------
int
daemon_set_restart_count_max (
    signed int		arg_count
    )
__PROTO_END__
{
    __FUNC_NAME__( daemon_set_restart_count_max );

    restart_count_max = arg_count;
    return 0;
}

__PROTO_BEGIN__
//-----------------------------------------------------------------------------
// function	daemon_set_server_fast_time
//
// purpose	Set the value of the time difference in which server restart
//		times less than this amount are considered a fast restart.
//
// arguments	1 (time_t) minimum restart time in seconds
//
// returns	(int) 0
//-----------------------------------------------------------------------------
int
daemon_set_server_fast_time (
    time_t		arg_time
    )
__PROTO_END__
{
    __FUNC_NAME__( daemon_set_server_fast_time );

    server_fast_time = arg_time;
    return 0;
}

__PROTO_BEGIN__
//-----------------------------------------------------------------------------
// function	daemon_set_server_fast_max
//
// purpose	Set the maximum number of fast restarts in a row allowed for a
//		server process.  After this number is exceeded, output an error
//		message and return or abort.
//
// arguments	1 (int) maximum number of fast restarts
//
// returns	(int) 0
//-----------------------------------------------------------------------------
int
daemon_set_server_fast_max (
    int			arg_max
    )
__PROTO_END__
{
    __FUNC_NAME__( daemon_set_server_fast_max );

    server_fast_max = arg_max;
    return 0;
}

__PROTO_BEGIN__
//-----------------------------------------------------------------------------
// function	daemon_set_server_tick
//
// purpose	Set the value of the tick interval in seconds.
//
//		The server process will wake up periodically at intervals
//		specified by this value.  It can perform certain checks at
//		this time, such as making sure the master process is still
//		running.
//
// arguments	1 (unsigned long) interval
//		2 (unsigned long) interval offset
//
// returns	(int)  < 0 : error
//		(int) == 0 : OK
//-----------------------------------------------------------------------------
int
daemon_set_server_tick (
    unsigned long	arg_interval
    ,
    unsigned long	arg_offset
    )
__PROTO_END__
{
    __FUNC_NAME__( daemon_set_server_tick );

    server_tick_interval = ( arg_interval > 0 ) ? arg_interval : 1;
    if ( arg_offset < 0 ) {
	//-----------------------------------------------------------
	// Because division/modulus is "divide towards zero", which
	// is not what this needs, add enough to shift it by an exact
	// multiple of the interval into the positive range.
	//-----------------------------------------------------------
	arg_offset += ( ( - arg_offset ) / server_tick_interval ) * server_tick_interval;
	if ( arg_offset < 0 ) arg_offset += server_tick_interval;
    }
    server_tick_offset = arg_offset % server_tick_interval;
    return 0;
}

__PROTO_BEGIN__
//-----------------------------------------------------------------------------
// function	daemon_set_worker_count_max
//
// purpose	Set the value of the maximum number of concurrent worker
//		processes to be allowed to run.  The server process will
//		stop accepting connections when this number of worker
//		processes are running.  It will resume accepting connections
//		when the number of worker processes running drops below this
//		number.
//
// arguments	1 (unsigned int) maximum worker count
//
// returns	(unsigned int) the previous setting
//-----------------------------------------------------------------------------
unsigned int
daemon_set_worker_count_max (
    unsigned int	arg_workers
    )
__PROTO_END__
{
    __FUNC_NAME__( daemon_set_worker_count_max );

    unsigned int	prev_workers	;

    prev_workers = worker_count_max;
    worker_count_max = arg_workers;
    return prev_workers;
}

__PROTO_BEGIN__
//-----------------------------------------------------------------------------
// function	daemon_clean_fd
//
// purpose	Clean up excess leftover file descriptors, usually inherited
//		from annoying shells.
//
// arguments	1 (int) highest fd to NOT close
//
// returns	(int) count of descriptors which successfully closed
//
// note		Some systems may have limits on the number of file descriptors
//		allowed that are so high that looping to attempt to close them
//		all is an unreasonable use of CPU time that may burden or slow
//		down the startup of the daemon.  This function places a ceiling
//		on that number to keep things from getting out of control.
//-----------------------------------------------------------------------------
int
daemon_clean_fd (
    int		arg_lo
    )
__PROTO_END__
{
    int		num_fd	;
    int		max_fd	;

    //-----------------------------------------------------------------
    // Limit the high descriptor to an implemented or reasonable value.
    //-----------------------------------------------------------------
    max_fd = 65535;

#ifdef OPEN_MAX
    if ( max_fd > OPEN_MAX ) max_fd = OPEN_MAX;
#endif

#ifdef _SC_OPEN_MAX
    num_fd = sysconf( _SC_OPEN_MAX );
    if ( max_fd > num_fd ) max_fd = num_fd;
#endif

    //----------------------------------------------------------
    // Close everything from the highest downward to the lowest
    // while counting the number actually closed to be returned.
    //----------------------------------------------------------
    num_fd = 0;
    while ( -- max_fd > arg_lo ) {
	if ( close( max_fd ) == 0 ) ++ num_fd;
    }
    return num_fd;
}

//-----------------------------------------------------------------------------
// function	daemon_sigaction_all
//
// purpose	Set up handling of all signals in one handler.
//
// arguments	1 (void (*)()) pointer to three argument signal handler
//
// returns	(int) 0
//-----------------------------------------------------------------------------
static
int
daemon_sigaction_all (
    void	( *	arg_action	)( int, siginfo_t *, void * )
    )
{
    __FUNC_NAME__( daemon_sigaction_all );

    //-------------------------------------------------
    // List all the signals the daemon library handles.
    // Other signals are not handled.
    //-------------------------------------------------
    static const int		signal_list[]	= {
	SIGALRM,
	SIGCHLD,
	SIGHUP, SIGUSR1, SIGUSR2,
	SIGINT, SIGTERM, SIGQUIT,
	SIGABRT,
	SIGBUS, SIGFPE, SIGILL, SIGSEGV,
	SIGPIPE, SIGTSTP, SIGTTIN, SIGTTOU,
	0
    };

    struct sigaction	sig_act		;

    const int *		signal_ptr	;

    int			return_code	;


    //---------------------------------------------------------
    // sa_handler and sa_sigaction may be joined in a union,
    // so fill in the NULL value first and the real value last.
    // A smart compiler will optimize away the lame NULL.
    //---------------------------------------------------------
    if ( arg_action ) {
	sig_act.sa_handler = NULL;
	sig_act.sa_sigaction = arg_action;
	sig_act.sa_flags = SA_SIGINFO;
    } else {
	sig_act.sa_sigaction = NULL;
	sig_act.sa_handler = SIG_DFL;
	sig_act.sa_flags = 0;
    }

    //------------------------------------------------
    // Always handle signals with all signals blocked.
    //------------------------------------------------
    sigfillset( & sig_act.sa_mask );

    //-------------------------------------------------------
    // Set up handling for all known signals by this handler.
    //-------------------------------------------------------
    return_code = 0;
    for ( signal_ptr = signal_list; * signal_ptr; ++ signal_ptr ) {
	if ( sigaction( * signal_ptr, & sig_act, NULL ) < 0 ) {
	    daemon_msg( __func__, "sigaction: %s", strerror( errno ) );
	    return_code = -1;
	}
    }

    return return_code;
}

__PROTO_BEGIN__
//-----------------------------------------------------------------------------
// function	daemon_success
//
// purpose	Send a signal to the startup process that the server has
//		successfully begun operations.  This informs the startup
//		process that the status it should exit with is 0.
//
//		Also close the write ends of the pipes now so the startup
//		process gets EOF.
//
// arguments	-none-
//
// returns	(int) 0
//-----------------------------------------------------------------------------
int
daemon_success ()
__PROTO_END__
{
    __FUNC_NAME__( daemon_success );

    //-------------------------------------------------------------
    // If the stdout/stderr pipes are not active, this cannot work.
    //-------------------------------------------------------------
    if ( ! pipes_active )
	_exit( daemon_msg( __func__, "stdout/stderr pipes are not available to send success status" ) );

    //-------------------------------------------------------------------
    // Write a success status octet to the pipes for the startup process.
    //-------------------------------------------------------------------
    write( 2, octets + 0, 1 );
    write( 1, octets + 0, 1 );

    //--------------------------------------------------------
    // If this is not running in the master process, then tell
    // the master process to close its stdout/stderr pipes.
    //--------------------------------------------------------
    if ( getpid() != master_pid ) {
	if ( kill( master_pid, SIGUSR1 ) < 0 )
	    _exit( daemon_msg( __func__, "*kill(master,SIGUSR1)" ) );
    }

    //-------------------------------------------------------
    // Close the pipes and replace with /dev/null from stdin.
    //-------------------------------------------------------
    fflush( stderr );
    fflush( stdout );
    if ( dup2( 0, 1 ) < 0 || dup2( 0, 2 ) < 0 )
	_exit( daemon_msg( __func__, "*dup2" ) );
    pipes_active = 0;

    //-----------------------------------------
    // Return value is irrelevant and not used.
    //-----------------------------------------
    return 0;
}

//-----------------------------------------------------------------------------
// function	daemon_signal_start
//
// purpose	Handle signals for the startup process.
//
// note		This handler assumes all signals are blocked when entered.
//-----------------------------------------------------------------------------
static
void
daemon_signal_start (
    int			arg_signal
    ,
    siginfo_t *		arg_siginfo
    ,
    void *		arg_context
    )
{
    __FUNC_NAME__( daemon_signal_start );

    int save_errno = errno;

    switch ( arg_signal ) {

    case SIGALRM:
	//-----------
	// Timed out.
	//-----------
	if ( timeout_state > 1 )
	    _exit( daemon_msg( NULL, "final timeout: status unknown", timeout_state ) );
	daemon_msg( NULL, "timeout: sending SIG%s", timeout_state ? "TERM" : "KILL" );
	if ( kill( master_pid, timeout_state ? SIGTERM : SIGKILL ) < 0 )
	    daemon_msg( __func__, "*kill(%d /*master pgid*/,SIG%s)", master_pid, timeout_state ? "TERM" : "KILL" );
	master_signaled = timeout_state ? SIGTERM : SIGKILL;
	++ timeout_state;
	break;

    case SIGCHLD:
	//-----------------------------------------------------
	// Reap all child processes.
	// For those that are known, update appropriate status.
	// For those that are unknown, output an error message.
	//-----------------------------------------------------
	{
	    pid_t		child_pid	;
	    int			child_status	;
	    int			child_seen	;

	    child_seen = 0;
	    while ( ( child_pid = waitpid( -1, & child_status, WNOHANG ) ) > 1 ) {
		if ( child_pid == arg_siginfo->si_pid ) child_seen = 1;

		if ( child_pid == master_pid ) {
		    master_status = child_status;
		    master_pid = -1;
		} else if ( child_pid == arg_siginfo->si_pid ) {
		    daemon_msg( __func__, "siginfo->si_pid: unknown PID [%d]", arg_siginfo->si_pid );
		    continue;
		} else {
		    daemon_msg( __func__, "waitpid(-1,,WNOHANG): unknown PID [%d]", child_pid );
		    continue;
		}
		if ( WIFSIGNALED( child_status ) ) {
		    daemon_msg( __func__, "master [%d] killed by signal %s",
				child_pid, strsignal( WTERMSIG( child_status ) ) );
		} else if ( ! WIFEXITED( child_status ) ) {
		    daemon_msg( __func__, "master [%d] exit reason unknown",
				child_pid );
		}
	    }
	    if ( ! child_seen ) {
		daemon_msg( __func__, "signaled child [%d] not reaped", arg_siginfo->si_pid );
	    }
	}
	break;

    case SIGUSR1:
	//-------------------------------------------------------
	// Handle signal indicating successful (SIGUSR1) startup.
	//-------------------------------------------------------
	if ( exit_status < 0 ) exit_status = 0;
	break;

    case SIGUSR2:
	//---------------------------------------------------
	// Handle signal indicating failed (SIGUSR2) startup.
	//---------------------------------------------------
	if ( exit_status < 1 ) exit_status = 1;
	break;

    case SIGHUP:
	//----------------------------------------------------
	// Check first for NOHUP before passing the signal on.
	// Otherwise handle it as a SIGINT signal.
	//----------------------------------------------------
	if ( ignore_hup ) break;
	if ( master_pid == 0 )
	    _exit( daemon_msg( NULL, "SIGHUP received, master not started" ) );
	if ( master_signaled )
	    _exit( daemon_msg( NULL, "SIGHUP received, master status unknown" ) );
	if ( kill( master_pid, SIGINT ) < 0 )
	    _exit( daemon_msg( __func__, "*SIGHUP received, kill(%d /*master*/,SIGINT)", master_pid ) );
	master_signaled = SIGINT;
	daemon_msg( __func__, "SIGHUP received, master aborting" );
	break;

    case SIGINT:
    case SIGTERM:
    case SIGQUIT:
	//--------------------------------------------------
	// Pass any quit class signal to the master process.
	// Wait for the master to exit on the first signal,
	// but exit now on the second signal.
	//--------------------------------------------------
	// This allows, for example, Ctrl-C to be used on
	// the foreground before the startup is complete.
	//--------------------------------------------------
	if ( master_pid == 0 )
	    _exit( daemon_msg( NULL, "%s received, master not started", strsignal( arg_signal ) ) );
	if ( master_signaled )
	    _exit( daemon_msg( NULL, "%s received, master status unknown", strsignal( arg_signal ) ) );
	if ( kill( master_pid, arg_signal ) < 0 )
	    _exit( daemon_msg( __func__, "*%s received, kill(%d /*master*/,%s)",
			       strsignal( arg_signal ), master_pid, strsignal( arg_signal ) ) );
	master_signaled = arg_signal;
	daemon_msg( NULL, "%s received, master aborting", arg_signal );
	break;

    case SIGABRT:
    case SIGBUS:
    case SIGFPE:
    case SIGILL:
    case SIGSEGV:
	//-----------------------------------------
	// Should not happen, but abort if it does.
	//-----------------------------------------
	daemon_msg( NULL, "%s at location %p in PID %d, aborting",
		    strsignal( arg_signal ), arg_siginfo->si_addr, getpid() );
	if ( master_pid > 0 && kill( master_pid, SIGTERM ) < 0 )
	    daemon_msg( __func__, "*kill(%d/*master*/,SIGTERM)", master_pid );
	_exit( 1 );

    case SIGPIPE:
    case SIGTSTP:
    case SIGTTIN:
    case SIGTTOU:
    default:
	//------------------------------------------
	// Should not happen, but ignore if it does.
	//------------------------------------------
	if ( verbose_signals )
	    daemon_msg( "unexpected %s received", strsignal( arg_signal ) );
	break;
    }


    //-----------------------------------------------
    // Write a byte to the signal pipe to be sure
    // poll wakes up and a race condition is avoided.
    //-----------------------------------------------
    write( signal_fd, octets + 0, 1 );

    //-------------------------------------
    // Return back to the previous context.
    //-------------------------------------
    errno = save_errno;
    return;
}

__PROTO_BEGIN__
//-----------------------------------------------------------------------------
// function	daemon_start
//
// purpose	Run the startup loop in the foreground process and return to
//		the calling program in a child process, which will later become
//		the server process or the master process.
//
//		The inherited stdin, stdout, and stderr descriptors and the
//		controlling tty are isolated from the background processes.
//		Pipes are established in place of stdout and stderr and copied
//		by the startup loop to the real stdout and stderr so that the
//		startup or failure messages can be presented to the startup
//		session tty.
//
// arguments	-none-
//
// returns	(int) -1 : in caller: fork failed or other error before fork
//		(int)  0 : in child:  child should now run server
//
// note		When caller gets a return value of -1 it should do any
//		cleanups and additional error messages then exit.
//
// note		When caller gets a return value of 0 it should do any needed
//		preparations to run a daemon then start running it, perhaps
//		by calling daemon_master() to have automatic restarts or
//		daemon_server() to run a server once.
//-----------------------------------------------------------------------------
#define DAEMON_START_RETURN_ERROR(v)	((v)<0)
#define DAEMON_START_RETURN_IN_MASTER	0
int
daemon_start ()
__PROTO_END__
{
    __FUNC_NAME__( daemon_start );

    struct pollfd	poll_list	[3];
    sigset_t		sigset_prev	;
    int			pipes		[3][2];
    int			n		;


    //---------------------------------------------------
    // Make sure the process startup sequence is correct.
    //---------------------------------------------------
    if ( master_pid != 0 || server_pid != 0 )
	return daemon_fatal( start_return_fatal,
			     __func__,
			     master_pid == getpid()
			     ? "this is already master process [%d]"
			     : "master process [%d] already running",
			     master_pid );

    //-------------------------------------------------------------
    // Initialize commonly used signal masks to be used everywhere.
    //-------------------------------------------------------------
    sigfillset( & sigset_all );
    sigemptyset( & sigset_none );

    //---------------------------------------------
    // Block signals, but keep the old signal mask.
    //---------------------------------------------
    if ( sigprocmask( SIG_SETMASK, & sigset_all, & sigset_prev ) < 0 )
	return daemon_fatal( start_return_fatal, __func__, "*sigprocmask" );

    //---------------------------------------------------------------------
    // Set up pipes for passing stdout and stderr messages from descendants
    // to the startup process, to isolate from the controlling tty.
    //---------------------------------------------------------------------
    if ( pipe( pipes[1] ) < 0 || pipe( pipes[2] ) < 0 )
	return daemon_fatal( start_return_fatal, __func__, "*pipe" );
    pipes_active = 1;

    //------------------------------------------------------
    // Remember which PID is the startup foreground process.
    //------------------------------------------------------
    start_pid = getpid();

    //----------------------------------------------------------------
    // Launch the daemon leader process to get a new process group ID.
    //----------------------------------------------------------------
    fflush( stderr );
    fflush( stdout );
    if ( ( master_pid = fork() ) < 0 )
	return daemon_fatal( start_return_fatal, __func__, "*fork" );

    //------------------
    // In child process:
    //------------------
    if ( master_pid == 0 ) {
	master_pid = getpid();

	//------------------------------------------------------------------
	// Close pipe read ends and move write ends to stdout and stderr.
	// This closes the inherited stdout and stderr.
	//---------------------------------------------------------------
	for ( n = 1; n <= 2; ++ n ) {
	    if ( close( pipes[n][0] ) < 0 )
		return daemon_fatal( start_return_fatal, __func__, "*close" );
	    if ( dup2( pipes[n][1], n ) < 0 )
		return daemon_fatal( start_return_fatal, __func__, "*dup2" );
	    if ( close( pipes[n][1] ) < 0 )
		return daemon_fatal( start_return_fatal, __func__, "*close" );
	}

	//------------------------------------------------------------
	// Now replace stdin with /dev/null.  Make it O_RDWR so it can
	// be duplicated as stdout and stderr when the pipes are to be
	// closed.  Nothing should need to read this in the server.
	//------------------------------------------------------------
	close( 0 );
	if ( ( n = open( "/dev/null", O_RDWR ) ) < 0 )
	    return daemon_fatal( start_return_fatal, __func__, "*open(\"/dev/null\",O_RDWR)" );
	if ( n != 0 ) {
	    if ( dup2( n, 0 ) < 0 )
		return daemon_fatal( start_return_fatal, __func__, "*dup2" );
	    if ( close( n ) < 0 )
		return daemon_fatal( start_return_fatal, __func__, "*close" );
	}

	//---------------------------------------------------
	// Create a new session separate from the foreground.
	//---------------------------------------------------
	setsid();

	//-------------------------------------
	// Restore original caller signal mask.
	//-------------------------------------
	if ( sigprocmask( SIG_SETMASK, & sigset_prev, NULL ) < 0 )
	    return daemon_fatal( start_return_fatal, __func__, "*sigprocmask" );

	//----------------------------------------
	// Return to caller in grandchild process.
	//----------------------------------------
	return 0;
    }

    //------------------------------------------------------------------------
    // In parent, run startup process logic (copy output and wait for signal).
    //------------------------------------------------------------------------
    exit_status = -1;

    //------------------------------------------------------------------
    // Set up a pipe to be used to syncronize signal handling to a poll
    // loop without a race condition.  The signal handler will write one
    // byte to this pipe to ensure that poll() wakes up in case a signal
    // arrives asyncronously between calling sigprocmask() and calling
    // poll().  Both ends of the pipe need to be non-blocking.
    //------------------------------------------------------------------
    if ( pipe( pipes[0] ) < 0 )
	return daemon_fatal( start_return_fatal, __func__, "*pipe" );
    if ( fcntl( pipes[0][0], F_SETFL, O_NONBLOCK ) < 0 )
	return daemon_fatal( start_return_fatal, __func__, "*fcntl(%d,F_SETFL,O_NONBLOCK)", pipes[0][0] );
    if ( fcntl( pipes[0][1], F_SETFL, O_NONBLOCK ) < 0 )
	return daemon_fatal( start_return_fatal, __func__, "*fcntl(%d,F_SETFL,O_NONBLOCK)", pipes[0][1] );
    signal_fd = pipes[0][1];

    //-----------------------------------------------------------
    // Handle signals known to the daemon library in one handler.
    //-----------------------------------------------------------
    daemon_sigaction_all( daemon_signal_start );

    //-------------------------------
    // Start repeating timeout timer.
    //-------------------------------
    timeout_state = 0;
    if ( timeout_first ) {
	struct itimerval timeout;
	timeout.it_value.tv_sec = timeout_first;
	timeout.it_interval.tv_sec = timeout_repeat;
	timeout.it_value.tv_usec = timeout.it_interval.tv_usec = 0;
	if ( setitimer( ITIMER_REAL, & timeout, NULL ) < 0 )
	    return daemon_fatal( start_return_fatal, __func__, "*setitimer" );
    }

    //------------------------------------------------------
    // Close the write side of the pipes, so EOF can happen.
    //------------------------------------------------------
    if ( close( pipes[1][1] ) < 0 || close( pipes[2][1] ) < 0 )
	return daemon_fatal( start_return_fatal, __func__, "*close" );

    //---------------------------------------------
    // Set up pollfd array for various descriptors.
    //---------------------------------------------
    poll_list[0].fd = pipes[0][0];
    poll_list[0].events = POLLIN;
    poll_list[1].fd = pipes[1][0];
    poll_list[1].events = POLLIN;
    poll_list[2].fd = pipes[2][0];
    poll_list[2].events = POLLIN;

    //-----------------------------------------------------
    // Run startup loop to copy stdout/stderr from descendants
    // and wait for success signal or child exit.
    //-----------------------------------------------------
    while ( poll_list[1].fd >= 0 || poll_list[2].fd >= 0 ) {
	char	buffer	[4096];

	//-----------------------------------------------------------------
	// Unblock signals and poll (then block them back again).
	//-----------------------------------------------------------------
	// A race condition with signal handling is resolved by the signal
	// handler writing a byte to the signal pipe.  This ensures that
	// poll() wakes up even if a signal happened between calling
	// sigprocmask() to unblock signals and calling poll() to wait
	// until something happens.  If the signal happens after poll()
	// returns, but before the next call to sigprocmask() to block
	// signals, then poll() will wake up the next time around the loop.
	// If multiple signals happen each time, it is not necessary for
	// an equal number of bytes to be written.  One byte is enough.
	//-----------------------------------------------------------------
	if ( sigprocmask( SIG_SETMASK, & sigset_none, NULL ) < 0 )
	    _exit( daemon_msg( __func__, "*sigprocmask" ) );
	if ( poll( poll_list, 3, -1 ) < 0 && errno != EINTR )
	    _exit( daemon_msg( __func__, "*poll" ) );
	if ( sigprocmask( SIG_SETMASK, & sigset_all, NULL ) < 0 )
	    _exit( daemon_msg( __func__, "*sigprocmask" ) );

	//--------------------------------------------------
	// If there was a signal, clean out the signal pipe.
	//--------------------------------------------------
	if ( poll_list[0].revents & ( POLLIN | POLLERR | POLLHUP | POLLNVAL ) ) {
	    read( pipes[0][0], buffer, sizeof (buffer) );
	}

	//----------------------------------------------------
	// If there is data in the pipes, read them now, check
	// for status bytes, and copy the rest to the output.
	//----------------------------------------------------
	for ( n = 2; n > 0; -- n ) {
	    if ( poll_list[n].revents & ( POLLIN | POLLERR | POLLHUP | POLLNVAL ) ) {
		char *pa, *pb, *pz;
		ssize_t len;
		len = read( pipes[n][0], buffer, sizeof (buffer) );
		if ( len == 0 ) {
		    close( poll_list[n].fd );
		    poll_list[n].fd = -1;
		    continue;
		}
		if ( len < 0 ) {
		    if ( errno == EAGAIN ) continue; // should not happen
		    _exit( daemon_msg( __func__, "*read" ) );
		}
		pz = ( pa = buffer ) + len;
		while ( pa < pz && * pa > 2 ) ++ pa;

		//-------------------------------------------------------------
		// Check for, record, and remove, any status code bytes (0..3).
		//-------------------------------------------------------------
		if ( * pa <= 3 ) {
		    pb = pa;
		    while ( pb < pz ) {
			if ( * pb <= 3 ) {
			    if ( exit_status < * pb ) exit_status = * pb;
			} else {
			    * pa ++ = * pb;
			}
			++ pb;
		    }
		}

		//--------------------------------------------------
		// If there is any data in the buffer now, write it.
		//--------------------------------------------------
		if ( ( len = pa - buffer ) > 0 ) {
		    fwrite( buffer, 1, len, n == 1 ? stdout : stderr );
		    fflush( n == 1 ? stdout : stderr );
		}
	    }
	}
    }
    
    //------------------------------------------------------------------------
    // The server process has exited or signaled, so exit the startup process.
    //------------------------------------------------------------------------
    _exit( exit_status < 0 ? 1 : exit_status );
}

//-----------------------------------------------------------------------------
// function	daemon_signal_master
//
// purpose	Handle signals for master process.
//
// note		This handler assumes all signals are blocked when entered.
//-----------------------------------------------------------------------------
static
void
daemon_signal_master (
    int			arg_signal
    ,
    siginfo_t *		arg_siginfo
    ,
    void *		arg_context
    )
{
    __FUNC_NAME__( daemon_signal_master );

    int			save_errno	;

    save_errno = errno;

    switch ( arg_signal ) {

    case SIGALRM:
	//--------
	// Ignore.
	//--------
        break;

    case SIGCHLD:
	//-----------------------------------------------------
	// Reap all child processes.
	// For those that are known, update appropriate status.
	// For those that are unknown, output an error message.
	//-----------------------------------------------------
	{
	    const char *	child_name	;
	    pid_t		child_pid	;
	    int			child_status	;
	    int			child_seen	;

	    child_seen = 0;
	    while ( ( child_pid = waitpid( -1, & child_status, WNOHANG ) ) > 1 ) {
		if ( child_pid == arg_siginfo->si_pid ) child_seen = 1;

		if ( child_pid == server_pid ) {
		    server_status = child_status;
		    server_pid = -1;
		    child_name = "server";
		} else if ( child_pid == arg_siginfo->si_pid ) {
		    daemon_msg( __func__, "siginfo->si_pid: unknown PID [%d]", arg_siginfo->si_pid );
		    continue;
		} else {
		    daemon_msg( __func__, "waitpid(-1,,WNOHANG): unknown PID [%d]", child_pid );
		    continue;
		}

		if ( WIFEXITED( child_status ) ) {
		    child_status = WEXITSTATUS( child_status );
		    if ( child_status != 0 ) {
			daemon_msg( __func__, "%s [%d] exited with status %d",
				    child_name, child_pid, child_status );
		    }
		} else if ( WIFSIGNALED( child_status ) ) {
		    daemon_msg( __func__, "%s [%d] killed by signal %s",
				child_name, child_pid, strsignal( WTERMSIG( child_status ) ) );
		} else {
		    daemon_msg( __func__, "%s [%d] exit reason unknown",
				child_name, child_pid );
		}
	    }
	    if ( ! child_seen ) {
		daemon_msg( __func__, "signaled child [%d] not reaped", arg_siginfo->si_pid );
	    }
	}
	break;

    case SIGHUP:
    case SIGUSR1:
    case SIGUSR2:
	//---------------------------------------------------------------
	// If the signal comes from the server process this means it is
	// signalling a successful or failed startup.  Close stdout and
	// stderr (replacing it with /dev/null) which the startup process
	// is reading, so it gets EOF and exits.
	//---------------------------------------------------------------
	if ( arg_siginfo->si_pid == server_pid ) {
	    if ( pipes_active ) {
		if ( dup2( 0, 1 ) < 0 || dup2( 0, 2 ) < 0 )
		    _exit( daemon_msg( __func__, "*%s: dup", strsignal( arg_signal ) ) );
		pipes_active = 0;
	    }
	    break;
	}

	//----------------------------------------------------
	// If the signal did not come from the server process,
	// then pass it on to the server process.
	//----------------------------------------------------
	if ( server_pid > 0 ) {
	    if ( kill( server_pid, arg_signal ) < 0 )
		_exit( daemon_msg( __func__, "*%s received, server status unknown due to: kill",
				   strsignal( arg_signal ) ) );
	}
	break;

    case SIGINT:
    case SIGTERM:
    case SIGQUIT:
	//--------------------------------------
	// Pass fast shutdown signals then exit.
	//--------------------------------------
	if ( arg_siginfo->si_pid != server_pid && server_pid > 0 ) {
	    if ( kill( server_pid, arg_signal ) < 0 )
		_exit( daemon_msg( __func__, "*%s: server status unknown due to: kill",
				   strsignal( arg_signal ) ) );
	}
	_exit( 1 );

    case SIGABRT:
	//-----------------------------------------
	// Should not happen, but abort if it does.
	//-----------------------------------------
	if ( arg_siginfo->si_pid == server_pid ) {
	    daemon_msg( __func__, "%s received from server [%d], aborting",
			strsignal( arg_signal ), server_pid );
	} else if ( server_pid > 0 ) {
	    if ( kill( server_pid, SIGABRT ) < 0 ) {
		daemon_msg( __func__, "*%s: failed to pass to server [%d] due to: kill",
			    strsignal( arg_signal ), server_pid );
	    } else {
		daemon_msg( __func__, "%s: passed to server [%d]",
			    strsignal( arg_signal ), server_pid );
	    }
	}
	_exit( 1 );

    case SIGBUS:
    case SIGFPE:
    case SIGILL:
    case SIGSEGV:
	//-----------------------------------------
	// Should not happen, but abort if it does.
	//-----------------------------------------
	daemon_msg( __func__, "%s at location %p in PID %d, aborting",
		    strsignal( arg_signal ), arg_siginfo->si_addr, getpid() );
	if ( server_pid > 0 ) {
	    if ( kill( server_pid, SIGTERM ) < 0 ) {
		daemon_msg( __func__, "*%s: failed to pass SIGTERM to server [%d] due to: kill",
			    strsignal( arg_signal ), server_pid );
	    } else {
		daemon_msg( __func__, "%s: passed SIGTERM to server [%d]",
			    strsignal( arg_signal ), server_pid );
	    }
	}
	_exit( 1 );

    case SIGPIPE:
    case SIGTSTP:
    case SIGTTIN:
    case SIGTTOU:
    default:
	//------------------------------------------
	// Should not happen, but ignore if it does.
	//------------------------------------------
	if ( verbose_signals ) {
	    daemon_msg( __func__, "unexpected %s", strsignal( arg_signal ) );
	}
	break;
    }

    //-------------------------------------
    // Return back to the previous context.
    //-------------------------------------
    errno = save_errno;
    return;
}

__PROTO_BEGIN__
//-----------------------------------------------------------------------------
// function	daemon_master
//
// purpose	1.  Start and restart as needed a process to run a server
//		    which will serve incoming network connections.
//
//		2.  Run the master loop which monitors and restarts the
//		    server process.
//
//		The daemon_master() function returns to the caller in the
//		same process it was called in if there is an error getting
//		started, or if there is an error in the master loop which
//		has been directed to return an error code.
//
//		The daemon_master() function returns to the caller in new
//		processes for each startup of the server process.  The main
//		caller should then call daemon_server() to actually run the
//		server after any configuration or other setup.
//
// arguments	1 (long *) where to store process restart sequence count
//
// returns	(int) == -1: in master process: fatal error
//		(int) ==  0: in server process: started
//
// note		Some errors in master loop will exit instead of return.
//
// note		When caller gets a return value of 0, it should proceed to
//		run the server, generally by calling daemon_server() to serve
//		network connections.
//-----------------------------------------------------------------------------
#define DAEMON_MASTER_RETURN_ERROR(v)	((v)<0)
#define DAEMON_MASTER_RETURN_IN_SERVER	0
long
daemon_master (
    long *	arg_count_ptr
    )
__PROTO_END__
{
    __FUNC_NAME__( daemon_master );

    sigset_t		sigset_prev	;
    long		restart_num	;


    //------------------------------------------------------------
    // Make sure this is the real master process as daemon_start()
    // set up, and that the server has not yet been started.
    //------------------------------------------------------------
    if ( ! master_pid )
	return daemon_fatal( master_return_fatal, __func__,
			     "master process not started by daemon_start()" );
    if ( master_pid != getpid() )
	return daemon_fatal( master_return_fatal, __func__,
			     "this process [%d] is not the master process [%d]",
			     getpid(), master_pid );
    if ( server_pid )
	return daemon_fatal( master_return_fatal, __func__,
			     "server process [%d] already started", server_pid );

    //--------------------------------------------------
    // Block signals but keep the old signal mask for
    // later restore when returning in the server child.
    //--------------------------------------------------
    if ( sigprocmask( SIG_SETMASK, & sigset_all, & sigset_prev ) < 0 )
	return daemon_fatal( master_return_fatal, __func__, "*sigprocmask" );

    //-----------------------------------------------------------
    // Handle signals known to the daemon library in one handler.
    //-----------------------------------------------------------
    daemon_sigaction_all( daemon_signal_master );

    //---------------------------------------------------
    // Run main loop to restart processes when they exit.
    //---------------------------------------------------
    restart_num = 0;
    for (;;) {

	//----------------------------
	// Check for failed processes.
	//----------------------------
	if ( server_pid < 0 ) {
	    if ( WIFEXITED( server_status ) && WEXITSTATUS( server_status ) != 0 ) return -1;
	    if ( WIFSIGNALED( server_status ) && WTERMSIG( server_status ) != 0 ) return -1;
	}

	//-------------------------------------------
	// Flush any standard output just to be sure.
	//-------------------------------------------
	fflush( stderr );
	fflush( stdout );

	//-------------------------------------------------
	// If the server process is not running, launch it.
	//-------------------------------------------------
	if ( server_pid <= 0 ) {

	    //----------------------------------
	    // Check for restart count exceeded.
	    //----------------------------------
	    ++ server_count;
	    if ( server_count_max ) {
		if ( server_count >= server_count_max ) {
		    return daemon_fatal( master_return_fatal, __func__,
					 "%s maximum restart count (%d) exhausted",
					 "server", server_count_max );
		}
	    } else {
		if ( server_count < 0 ) server_count = 1;
	    }

	    //---------------------------------------------------
	    // Check for restart too fast.  If the time since the
	    // last start is less than the required time, it is a
	    // fast restart.  If the number of fast restarts in a
	    // row exceeds a specified maximum, then output and
	    // error message and possibly return to the caller.
	    //---------------------------------------------------
	    if ( ( time( NULL ) - server_start_time ) < server_fast_time ) {
		++ server_fast_count;
		if ( server_fast_count > server_fast_max ) {
		    return daemon_fatal( master_return_fatal, __func__, "server restarting too fast" );
		}
	    } else {
		server_fast_count = 0;
	    }

	    //---------------------------
	    // Fork a new server process.
	    //---------------------------
	    if ( ( server_pid = fork() ) < 0 )
		return daemon_fatal( master_return_fatal, __func__, "*fork(server)" );

	    //------------------------------------------------------------
	    // In the child process, the server will be run by the caller.
	    // The caller will usually call daemon_server() to do this.
	    //------------------------------------------------------------
	    if ( server_pid == 0 ) {
		server_pid = getpid();

		//-------------------------------------------------------------
		// Reset signal handling back to the default and original mask.
		//-------------------------------------------------------------
		daemon_sigaction_all( NULL );
		if ( sigprocmask( SIG_SETMASK, & sigset_prev, NULL ) < 0 )
		    _exit( daemon_fatal( master_return_fatal, __func__, "*sigprocmask" ) );

		//-----------------------------------------------
		// Start yet another process group to exclude the
		// master.  This is to allow a group kill of the
		// server and workers without killing the master.
		//-----------------------------------------------
		setpgrp();

		//--------------------------------------------
		// Return to caller to run the server process.
		//--------------------------------------------
		if ( arg_count_ptr ) * arg_count_ptr = restart_num;
		return 0;
	    }
	    ++ restart_num;

	    //------------------------------------------------------------------------
	    // Remember when the server process is started to check for exit too fast.
	    //------------------------------------------------------------------------
	    server_start_time = time( NULL );
	}

	//-------------------------------------------------------------
	// Wait for a child process to exit (master does nothing else).
	//-------------------------------------------------------------
	if ( sigsuspend( & sigset_none ) < 0 && errno != EINTR )
	    return daemon_fatal( master_return_fatal, __func__, "*sigsuspend" );
    }
}

//-----------------------------------------------------------------------------
// function	daemon_signal_server
//
// purpose	Handle signals for server process doing accept loop.
//
// note		This handler assumes all signals are blocked when entered.
//-----------------------------------------------------------------------------
static
void
daemon_signal_server (
    int			arg_signal
    ,
    siginfo_t *		arg_siginfo
    ,
    void *		arg_context
    )
{
    __FUNC_NAME__( daemon_signal_server );

    int		save_errno	;

    save_errno = errno;

    switch ( arg_signal ) {

    case SIGALRM:
	//------------------------------------------------------------
	// Remember the alarm happened so the main loop can handle it.
	// The main loop will reschedule the alarm.
	//------------------------------------------------------------
	server_sigalrm = 1;
	break;

    case SIGCHLD:
	//--------------------------------------------------------
	// Reap all worker processes that have exited, and adjust
	// the sequence limit up to allow more processes to start.
	//--------------------------------------------------------
	{
	    int child_pid;
	    int child_status;

	    while ( ( child_pid = waitpid( -1, & child_status, WNOHANG ) ) > 0 ) {
		if ( WIFEXITED( child_status ) ) {
		    if ( WEXITSTATUS( child_status ) != 0 ) {
			daemon_msg( __func__, "[%d] worker process exit status = %d",
				    child_pid, WEXITSTATUS( child_status ) );
		    }
		} else if ( WIFSIGNALED( child_status ) ) {
		    daemon_msg( __func__, "[%d] worker process killed by %s",
				child_pid, strsignal( WTERMSIG( child_status ) ) );
		} else {
		    daemon_msg( __func__, "[%d] worker process exit, reason unknown", child_pid );
		}
		++ worker_seq_ended;
	    }
	}
	break;

    case SIGHUP:
	//-------------------------------------
	// Handle request for graceful restart.
	//-------------------------------------
	if ( arg_siginfo->si_pid != server_pid ) {
	    shutdown_slow = 1;
	    shutdown_exit = 0;
	    if ( kill( 0, arg_signal ) < 0 ) daemon_msg( __func__, "*kill" );
	}
	break;

    case SIGUSR1:
	//---------------------------------
	// Handle request for fast restart.
	//---------------------------------
	if ( arg_siginfo->si_pid != server_pid ) {
	    if ( kill( 0, SIGTERM ) < 0 ) daemon_msg( __func__, "*kill" );
	    _exit( 0 );
	}
	break;

    case SIGUSR2:
	//--------------------------------------
	// Handle request for graceful shutdown.
	//--------------------------------------
	if ( arg_siginfo->si_pid != server_pid ) {
	    shutdown_slow = 1;
	    shutdown_exit = 1;
	    if ( kill( 0, arg_signal ) < 0 ) daemon_msg( __func__, "*kill" );
	}
	break;

    case SIGINT:
    case SIGTERM:
    case SIGQUIT:
	//----------------------------------
	// Handle request for fast shutdown.
	//----------------------------------
	if ( arg_siginfo->si_pid != server_pid ) {
	    if ( kill( 0, arg_signal ) < 0 ) daemon_msg( __func__, "*kill" );
	}
	_exit( 1 );

    case SIGABRT:
	//-----------------------------------------
	// Should not happen, but abort if it does.
	//-----------------------------------------
	daemon_msg( __func__, "%s received, aborting", strsignal( arg_signal ) );
	if ( kill( 0, SIGTERM ) < 0 ) daemon_msg( __func__, "*kill" );
	_exit( 1 );

    case SIGBUS:
    case SIGFPE:
    case SIGILL:
    case SIGSEGV:
	//-----------------------------------------
	// Should not happen, but abort if it does.
	//-----------------------------------------
	daemon_msg( __func__, "%s at location %p in PID %d, aborting",
		    strsignal( arg_signal ), arg_siginfo->si_addr, getpid() );
	if ( kill( - server_pid, SIGTERM ) == 0 )
	    daemon_msg( __func__, "SIGTERM sent to server group [%d]", server_pid );
	else
	    daemon_msg( __func__, "*SIGTERM to server group [%d] failed: kill", server_pid );
	_exit( 1 );

    case SIGPIPE:
    case SIGTSTP:
    case SIGTTIN:
    case SIGTTOU:
    default:
	//------------------------------------------
	// Should not happen, but ignore if it does.
	//------------------------------------------
	if ( verbose_signals )
	    daemon_msg( "unexpected %s received", strsignal( arg_signal ) );
	break;
    }

    //-------------------------------------
    // Return back to the previous context.
    //-------------------------------------
    errno = save_errno;
    return;
}

__PROTO_BEGIN__
//-----------------------------------------------------------------------------
// function	daemon_server
//
// purpose	Run the server process accepting (via poll) incoming
//		connections, forking a new worker for each.
//
// details	The worker process is started as a child process which returns
//		to the calling program to run the worker program code.  The
//		new connection will be descriptors 0 and 1.  When the worker
//		is done, it can shutdown(), close() and exit().
//
// arguments	1 (int *) list of descriptors to accept connections on
//		2 (struct sockaddr *) socket address to be filled by accept
//		3 (socklen_t *) socket address length to be filled by accept
//
// returns	(int) <  0 : in caller: error
//		(int) >= 0 : index into list of descriptors for the specific
//				descriptor on which this connection arrived
//
// note		The list of descriptors to accept connections on should be
//		terminated by a negative value, but a value of 2 or less
//		will also terminate it since standard descriptors should not
//		be passed to the daemon library in this list.
//
// note		The 2nd and 3rd arguments are like the 2nd and 3rd arguments
//		of the accept() call.  The variable pointed to by the 3rd
//		argument must be filled in with the length of space available
//		in the 2nd argument.  Upon return, the 2nd argument will have
//		the connecting socket address filled in, and the 3rd argument
//		will have the actual address length filled in the same way
//		that accept() does.
//
// note		In order to accept IPv6 connections, the size of the socket
//		address area, and the value of the given length, must be at
//		least that of sizeof(sockaddr_in6).  See "man ipv6".
//
// note		The returned value is an index to the list of descriptors,
//		not the descriptor value itself.  This allows the caller to
//		keep another array of information related to each descriptor,
//		such as the type of service or protocol to carry out, which
//		can be easily indexed by this returned value.
//-----------------------------------------------------------------------------
#define DAEMON_SERVER_RETURN_ERROR(v)	((v)<0)
int
daemon_server (
    int *		arg_sockfd_list
    ,
    struct sockaddr *	arg_sock_addr
    ,
    socklen_t *		arg_sock_addrlen
    )
__PROTO_END__
{
    __FUNC_NAME__( daemon_server );

    struct pollfd *	pollfd_list		;
    int *		fd_ptr			;
    unsigned int	worker_seq_started	;
    unsigned int	workers_running		;
    int			fd_count		;
    int			fd_index		;
    int			worker_pid		;
    int			new_fd			;
    static const int	value_1			= 1;

    //----------------------------------------------------------------------
    // Make sure this is the real server process started by daemon_master().
    //----------------------------------------------------------------
    if ( ! server_pid )
	return daemon_fatal( server_return_fatal, __func__,
			     "server process not started by daemon_master()" );
    if ( server_pid != getpid() )
	return daemon_fatal( server_return_fatal, __func__,
			     "this process [%d] is not the server process [%d]",
			     getpid(), server_pid );

    //-------------------
    // Block all signals.
    //-------------------
    if ( sigprocmask( SIG_SETMASK, & sigset_all, NULL ) < 0 )
	return daemon_fatal( server_return_fatal, __func__, "*sigprocmask" );

    //-----------------------------------------------------------
    // Handle signals known to the daemon library in one handler.
    //-----------------------------------------------------------
    daemon_sigaction_all( daemon_signal_server );

    //---------------------------------
    // Count the number of descriptors.
    //---------------------------------
    fd_ptr = arg_sockfd_list;
    while ( * fd_ptr > 2 ) ++ fd_ptr;
    fd_count = fd_ptr - arg_sockfd_list;

    //---------------------------------------------------------------
    // If there is more than one listen socket descriptor, then build
    // a poll list and set all the descriptors to non-blocking mode.
    // Else for just one descriptor, poll() is not needed, so set the
    // descriptor to blocking mode so it just blocks in accept().
    //---------------------------------------------------------------
    if ( fd_count > 1 ) {

	//------------------------------------
	// The alloca() function is preferred.
	//------------------------------------
#ifdef NOALLOCA
	pollfd_list = malloc( sizeof (struct pollfd) * fd_count );
	if ( ! pollfd_list ) _exit( daemon_msg( __func__, "*malloc" ) );
#else
	pollfd_list = alloca( sizeof (struct pollfd) * fd_count );
	if ( ! pollfd_list ) _exit( daemon_msg( __func__, "*alloca" ) );
#endif /* NOALLOCA */

	//--------------------------------------------------------
	// Fill in the poll list array while setting non-blocking.
	//--------------------------------------------------------
	for ( fd_index = 0; fd_index < fd_count; ++ fd_index ) {
	    if ( fcntl( arg_sockfd_list[fd_index], F_SETFL, O_NONBLOCK ) < 0 )
		return daemon_fatal( server_return_fatal, __func__, "*fcntl(%d,F_SETFL,O_NONBLOCK)",
				     arg_sockfd_list[fd_index] );
	    pollfd_list[fd_index].fd = arg_sockfd_list[fd_index];
	    pollfd_list[fd_index].events = POLLIN;
	}

    } else {

	//-------------------------------------------------
	// One descriptor alone can just block in accept().
	//-------------------------------------------------
	if ( fcntl( arg_sockfd_list[0], F_SETFL, 0 ) < 0 )
	    return daemon_fatal( server_return_fatal, __func__, "*fcntl(%d,F_SETFL,0)", arg_sockfd_list[0] );
	pollfd_list = NULL;

    }

    //------------------------------------------------------------------------
    // Pretend that the alarm has already signaled to schedule it in the loop.
    //------------------------------------------------------------------------
    server_sigalrm = 1;

    //---------------------------------------------------------
    // Run master loop to accept connections and spawn workers.
    //---------------------------------------------------------
    worker_seq_started = 0;
    worker_seq_ended = 0;
    for (;;) {

	//-------------------------------------------------------------------
	// Block signals to safely test variables changed in signal handlers.
	//-------------------------------------------------------------------
	if ( sigprocmask( SIG_SETMASK, & sigset_all, NULL ) < 0 )
	    return daemon_fatal( server_return_fatal, __func__, "*sigprocmask" );

	//--------------------------------------------------
	// If the alarm signal happened, handle things here.
	//--------------------------------------------------
	if ( server_sigalrm ) {

	    //-----------------------------------------------
	    // If a master process is supposed to be running,
	    // check to be sure it is still there as parent.
	    //-----------------------------------------------
	    if ( server_pid != master_pid && getppid() <= 1 ) {
		daemon_msg( __func__, "no parent" );
		if ( kill( 0, SIGTERM ) < 0 )
		    return daemon_fatal( server_return_fatal, __func__, "*kill" );
	    }

	    //-----------------------------------------------------------------
	    // Reschedule the next alarm syncronized to the specified interval.
	    //-----------------------------------------------------------------
	    if ( daemon_sync_timer( server_tick_interval, server_tick_offset ) ) return 1;
	    server_sigalrm = 0;
	}

	//------------------------------------------------------
	// Check for how many workers are running.
	// This depends on integer wrap-around to be consistent.
	//------------------------------------------------------
	workers_running = worker_seq_started - worker_seq_ended;

	//-----------------------------------------------------------
	// If shutting down slowly, check if all workers have exited.
	// If so, exit now.  If not, just wait for things to happen.
	//-----------------------------------------------------------
	if ( shutdown_slow ) {
	    if ( workers_running == 0 ) {
		if ( shutdown_exit == 0 ) {
		    if ( server_return_restart < 0 ) return server_return_restart;
		} else {
		    if ( server_return_shutdown < 0 ) return server_return_shutdown;
		}
		_exit( shutdown_exit );
	    }
	    if ( sigsuspend( & sigset_none ) < 0 && errno != EINTR )
		return daemon_fatal( server_return_fatal, __func__, "*sigsuspend" );
	    continue;
	}

	//-----------------------------------------------------------------------
	// If more workers cannot be started, then don't accept more connections.
	//-----------------------------------------------------------------------
	if ( workers_running >= worker_count_max ) {
	    if ( sigsuspend( & sigset_none ) < 0 && errno != EINTR )
		return daemon_fatal( server_return_fatal, __func__, "*sigsuspend" );
	    continue;
	}

	//-------------------------------------------
	// Unblock signals during poll() or accept().
	//-------------------------------------------
	if ( sigprocmask( SIG_SETMASK, & sigset_none, NULL ) < 0 )
	    return daemon_fatal( server_return_fatal, __func__, "*sigprocmask" );

	//---------------------------------------------------------
	// If a poll list exists, do the poll.  Else there is only
	// one descriptor and it will just block in accept() below.
	//---------------------------------------------------------
	if ( pollfd_list ) {
	    int n;
	    n = poll( pollfd_list, fd_count, -1 );
	    if ( n < 0 ) {
		if ( errno == EINTR ) continue;
		return daemon_fatal( server_return_fatal, __func__, "*poll" );
	    }
	}
	    
	//-------------------------------------------------
	// Scan list of descriptors for all that are ready.
	//-------------------------------------------------
	fflush( stdout );
	fflush( stderr );
	for ( fd_index = 0; fd_index < fd_count; ++ fd_index ) {

	    //---------------------------------------------------------------------------
	    // If a poll list exists, check this descriptor's element for ready-to-accept
	    // status. If it is not ready, continue the loop to the next descriptor.  For
	    // unexpected events, report it as an error and delete it.  If there is no
	    // poll list, then just skip the check and let accept() block until ready.
	    //---------------------------------------------------------------------------
	    if ( pollfd_list && pollfd_list[fd_index].revents != POLLIN ) {
		if ( pollfd_list[fd_index].revents ) {
		    fprintf( stderr, "poll: pollfd[%d].revents = %d\n", fd_index, pollfd_list[fd_index].revents );
		    pollfd_list[fd_index].fd = -1;
		}
		continue;
	    }

	    //---------------------------------------------------------
	    // Must be a new connection since these are listen sockets.
	    //---------------------------------------------------------
	    memset( arg_sock_addr, 0, * arg_sock_addrlen );
	    errno=0;
	    new_fd = accept( arg_sockfd_list[fd_index], arg_sock_addr, arg_sock_addrlen );

	    //------------------------------------------
	    // If accept() returned an error, check why.
	    //------------------------------------------
	    if ( new_fd < 0 ) {

		//-------------------------------------------------------
		// If the call is interrupted or incomplete, just go on.
		// This descriptor will be accepted() again soon, anyway.
		// ERESTARTSYS should not happen, but I have seen it
		// happen in Linux, so it is here just in case.
		//-------------------------------------------------------
		if ( errno == EINTR ) continue;
		if ( errno == ECONNABORTED ) continue;
#ifdef ERESTARTSYS
		if ( errno == ERESTARTSYS ) continue;
#endif

		//-----------------------------------
		// For real errors, just bail out.
		// This should be made more graceful.
		//-----------------------------------
		return daemon_fatal( server_return_fatal, __func__, "*accept" );
	    }

	    //----------------------------------------------------------
	    // If we got a STD descriptor, something is wrong somewhere.
	    // This should be made more graceful.
	    //----------------------------------------------------------
	    if ( new_fd <= 2 ) {
		if ( close( new_fd ) < 0 )
		    daemon_msg( __func__, "*close(%d)", new_fd );
		return daemon_fatal( server_return_fatal, __func__,
				     "accept should not return standard fd: %d", new_fd );
	    }

	    //--------------------------------------------
	    // Fork a new worker to handle the connection.
	    //--------------------------------------------
	    if ( ( worker_pid = fork() ) < 0 )
		return daemon_fatal( server_return_fatal, __func__, "*fork" );

	    //---------------
	    // Parent/server:
	    //---------------
	    if ( worker_pid > 0 ) {

		//------------------------------------------------
		// Close the connection so only the worker has it.
		// Track the count of workers.
		//------------------------------------------------
		if ( close( new_fd ) < 0 )
		    return daemon_fatal( server_return_fatal, __func__, "*close" );
		++ worker_seq_started;
		continue;
	    }

	    //--------------
	    // Child/worker:
	    //--------------

	    //---------------------------------------------------------
	    // Make sure we can rebind the port/address if we need to.
	    // Apparently some systems do not inherit all socket flags.
	    //---------------------------------------------------------
	    if ( setsockopt( new_fd,
			     SOL_SOCKET,
			     SO_REUSEADDR,
			     (void const *) & value_1,
			     (socklen_t) sizeof (value_1) ) < 0 )
		return daemon_fatal( server_return_fatal, __func__,
				     "*setsockopt(%d,SOL_SOCKET,SO_REUSEADDR,&1,)", new_fd );

	    //----------------------------------------------------------
	    // Close all listen sockets so worker cannot mess with them.
	    // Move the connection to stdin and stdout.
	    // Keep stderr; it might be a log file.
	    //-----------------------------------------
	    for ( fd_ptr = arg_sockfd_list; * fd_ptr > 0; ++ fd_ptr )
		if ( close( * fd_ptr ) < 0 )
		    return daemon_fatal( server_return_fatal, __func__, "*close" );
	    if ( dup2( new_fd, 0 ) < 0 || dup2( new_fd, 1 ) < 0 )
		return daemon_fatal( server_return_fatal, __func__, "*dup2" );
	    if ( close( new_fd ) < 0 )
		return daemon_fatal( server_return_fatal, __func__, "*close" );

	    //-------------------------------------------------------
	    // Return to caller in worker process with the INDEX to
	    // which listen/accept descriptor got the new connection.
	    // The index, rather that the descriptor, is returned so
	    // the caller can use it to look up other information.
	    //-------------------------------------------------------
	    return fd_index;
	}

	//------------------------
	// Go back and poll again.
	//------------------------
	// continue;
    }
}

