NetBSD-5.0.2/usr.sbin/isdn/isdnd/main.c

Compare this file to the similar file:
Show the results in this format:

/*
 * Copyright (c) 1997, 1999 Hellmuth Michaelis. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *---------------------------------------------------------------------------
 *
 *	i4b daemon - main program entry
 *	-------------------------------
 *
 *	$Id: main.c,v 1.10 2008/07/15 17:51:38 perry Exp $ 
 *
 * $FreeBSD$
 *
 *      last edit-date: [Mon Jan  8 07:57:26 2001]
 *
 *---------------------------------------------------------------------------*/

#include <locale.h>

#ifdef I4B_EXTERNAL_MONITOR
#include "monitor.h"
#endif

#define MAIN
#include "isdnd.h"
#undef MAIN

#ifdef I4B_EXTERNAL_MONITOR

#ifdef I4B_NOTCPIP_MONITOR
/* monitor via local socket */
static void mloop(int sockfd);
#else /* I4B_NOTCPIP_MONITOR */
/* monitor via local and tcp/ip socket */
static void mloop(int localsock, int remotesock);
#endif /* I4B_NOTCPIP_MONITOR */

#else /* I4B_EXTERNAL_MONITOR */
/* no monitoring at all */
static void mloop();
#endif /* I4B_EXTERNAL_MONITOR */

#ifdef USE_CURSES
static void kbdrdhdl(void);
#endif

static void isdnrdhdl(void);
static void usage(void);

#define MSG_BUF_SIZ	1024	/* message buffer size */

/*---------------------------------------------------------------------------*
 *	usage display and exit
 *---------------------------------------------------------------------------*/
static void
usage(void)
{
	fprintf(stderr, "\n");
	fprintf(stderr, "isdnd - i4b ISDN manager daemon, version %02d.%02d.%d\n", VERSION, REL, STEP);
#ifdef DEBUG
	fprintf(stderr, "  usage: isdnd [-c file] [-d level] [-F] [-f [-r dev] [-t termtype]]\n");
#else
	fprintf(stderr, "  usage: isdnd [-c file] [-F] [-f [-r dev] [-t termtype]]\n");
#endif	
	fprintf(stderr, "               [-l] [-L file] [-m] [-s facility] [-u time]\n");
	fprintf(stderr, "    -c <filename> configuration file name (def: %s)\n", CONFIG_FILE_DEF);
#ifdef DEBUG
	fprintf(stderr, "    -d <level>    set debug flag bits:\n");
	fprintf(stderr, "                  general = 0x%04x, rates  = 0x%04x, timing   = 0x%04x\n", DL_MSG,   DL_RATES, DL_TIME);
	fprintf(stderr, "                  state   = 0x%04x, retry  = 0x%04x, dial     = 0x%04x\n", DL_STATE, DL_RCVRY, DL_DIAL);
	fprintf(stderr, "                  process = 0x%04x, kernio = 0x%04x  ctrlstat = 0x%04x\n", DL_PROC,  DL_DRVR,  DL_CNST);
	fprintf(stderr, "                  rc-file = 0x%04x, budget = 0x%04x\n", DL_RCCF, DL_BDGT);
	fprintf(stderr, "    -dn           no debug output on fullscreen display\n");
#endif
	fprintf(stderr, "    -f            fullscreen status display\n");
	fprintf(stderr, "    -F            do not become a daemon process\n");
	fprintf(stderr, "    -l            use a logfile instead of syslog\n");
	fprintf(stderr, "    -L <file>     use file instead of %s for logging\n", LOG_FILE_DEF);
	fprintf(stderr, "    -P            pretty print real config to stdout and exit\n");
	fprintf(stderr, "    -r <device>   redirect output to other device    (for -f)\n");
	fprintf(stderr, "    -s <facility> use facility instead of %d for syslog logging\n", LOG_LOCAL0 >> 3);
	fprintf(stderr, "    -t <termtype> terminal type of redirected screen (for -f)\n");
	fprintf(stderr, "    -u <time>     length of a charging unit in seconds\n");
#ifdef I4B_EXTERNAL_MONITOR
	fprintf(stderr, "    -m            inhibit network/local monitoring (protocol %02d.%02d)\n", MPROT_VERSION, MPROT_REL);
#endif	
	fprintf(stderr, "\n");
	exit(1);
}

/*---------------------------------------------------------------------------*
 *	program entry
 *---------------------------------------------------------------------------*/
int
main(int argc, char **argv)
{
	int i;
	msg_vr_req_t mvr;
	
#ifdef I4B_EXTERNAL_MONITOR
	int sockfd = -1;		/* local monitor socket */
#ifndef I4B_NOTCPIP_MONITOR
	int remotesockfd = -1;		/* tcp/ip monitor socket */
#endif
#endif

	setlocale (LC_ALL, "");
	
	while ((i = getopt(argc, argv, "mc:d:fFlL:Pr:s:t:u:")) != -1)
	{
		switch (i)
		{
#ifdef I4B_EXTERNAL_MONITOR
		case 'm':
			inhibit_monitor = 1;
			break;
#endif
			
		case 'c':
			configfile = optarg;
			break;

#ifdef DEBUG				
		case 'd':
			if (*optarg == 'n')
				debug_noscreen = 1;
			else if ((sscanf(optarg, "%i", &debug_flags)) == 1)
				do_debug = 1;
			else
				usage();				
			break;
#endif

		case 'f':
			do_fullscreen = 1;
			do_fork = 0;			
#ifndef USE_CURSES
			fprintf(stderr, "Sorry, no fullscreen mode available - daemon compiled without USE_CURSES\n");
			exit(1);
#endif
			break;

		case 'F':
			do_fork = 0;
			break;

		case 'l':
			uselogfile = 1;
			break;

		case 'L':
			strlcpy(logfile, optarg, sizeof(logfile));
			break;

		case 'P':
			do_print = 1;
			break;

		case 'r':
			rdev = optarg;
			do_rdev = 1;
			break;

		case 's':
			if (isdigit((unsigned char)*optarg))
			{
				int facility;
				logfacility = strtoul(optarg, NULL, 10);
				facility = logfacility << 3;

				if ((facility < LOG_KERN) ||
				   (facility > LOG_FTP && facility < LOG_LOCAL0) ||
				   (facility > LOG_LOCAL7))
				{
					fprintf(stderr, "Error, option -s has invalid logging facility %d", logfacility);
					usage();
				}
				logfacility = facility;
			}
			else
			{
				fprintf(stderr, "Error: option -s requires a numeric argument!\n");
				usage();
			}
			break;

		case 't':
			ttype = optarg;
			do_ttytype = 1;
			break;

		case 'u':
			if (isdigit((unsigned char)*optarg))
			{
				unit_length = strtoul(optarg, NULL, 10);
				if (unit_length < ULSRC_CMDLMIN)
					unit_length = ULSRC_CMDLMIN;
				else if (unit_length > ULSRC_CMDLMAX)
					unit_length = ULSRC_CMDLMAX;
				got_unitlen = 1;
			}
			else
			{
				fprintf(stderr, "Error: option -T requires a numeric argument!\n");
				usage();
			}
			break;

		case '?':
		default:
			usage();
			break;
		}
	}
#ifdef DEBUG
	if (!do_debug)
		debug_noscreen = 0;
#endif

	if (!do_print)
	{
		umask(UMASK);	/* set our umask ... */	
	
		init_log();	/* initialize the logging subsystem */
	}
	
	check_pid();	/* check if we are already running */

	if (!do_print)
	{
		if (do_fork || (do_fullscreen && do_rdev)) /* daemon mode ? */
			daemonize();
	
		write_pid();	/* write our pid to file */
			
		/* set signal handler(s) */
	
		signal(SIGCHLD, sigchild_handler); /* process handling	*/
		signal(SIGHUP,  rereadconfig);	/* reread configuration	*/
		signal(SIGUSR1, reopenfiles);	/* reopen acct/log files*/
		signal(SIGPIPE, SIG_IGN);	/* handled manually	*/
		signal(SIGINT,  do_exit);	/* clean up on SIGINT	*/
		signal(SIGTERM, do_exit);	/* clean up on SIGTERM	*/
		signal(SIGQUIT, do_exit);	/* clean up on SIGQUIT	*/	
	}

	/* open isdn device */
	
	if ((isdnfd = open(I4BDEVICE, O_RDWR)) < 0)
	{
		logit(LL_ERR, "main: cannot open %s: %s", I4BDEVICE, strerror(errno));
		exit(1);
	}

	/* check kernel and userland have same version/release numbers */
	
	if ((ioctl(isdnfd, I4B_VR_REQ, &mvr)) < 0)
	{
		logit(LL_ERR, "main: ioctl I4B_VR_REQ failed: %s", strerror(errno));
		do_exit(1);
	}

	if (mvr.version != VERSION)
	{
		logit(LL_ERR, "main: version mismatch, kernel %d, daemon %d", mvr.version, VERSION);
		do_exit(1);
	}

	if (mvr.release != REL)
	{
		logit(LL_ERR, "main: release mismatch, kernel %d, daemon %d", mvr.release, REL);
		do_exit(1);
	}

	if (mvr.step != STEP)
	{
		logit(LL_ERR, "main: step mismatch, kernel %d, daemon %d", mvr.step, STEP);
		do_exit(1);
	}

	/* init controller state array */

	init_controller();

	/* read runtime configuration file and configure ourselves */
	
	configure(configfile, 0);

	if (config_error_flag)
	{
		logit(LL_ERR, "there were %d error(s) in the configuration file, terminating!", config_error_flag);
		exit(1);
	}

	/* set controller ISDN protocol */
	
	init_controller_protocol();
	
	/* init active controllers, if any */
	
	signal(SIGCHLD, SIG_IGN);		/*XXX*/

	init_active_controller();

	signal(SIGCHLD, sigchild_handler);	/*XXX*/
	
	/* handle the rates stuff */
	
	if ((i = readrates(ratesfile)) == ERROR)
	{
		if (rate_error != NULL)
			logit(LL_ERR, "%s", rate_error);
		exit(1);
	}

	if (i == GOOD)
	{
		got_rate = 1;	/* flag, ratesfile read and ok */
		DBGL(DL_RCCF, (logit(LL_DBG, "ratesfile %s read successfully", ratesfile)));
	}
	else
	{
		if (rate_error != NULL)
			logit(LL_WRN, "%s", rate_error);
	}

	/* if writing accounting info, open file, set unbuffered */
	
	if (useacctfile)
	{
		if ((acctfp = fopen(acctfile, "a")) == NULL)
		{
			logit(LL_ERR, "ERROR, can't open acctfile %s for writing, terminating!", acctfile);
			exit(1);
		}
		setvbuf(acctfp, (char *)NULL, _IONBF, 0);		
	}

	/* initialize alias processing */

	if (aliasing)
		init_alias(aliasfile);

	/* init holidays */
	
	init_holidays(holidayfile);		

	/* init remote monitoring */
	
#ifdef I4B_EXTERNAL_MONITOR
	if (do_monitor)
	{
		monitor_init();
		sockfd = monitor_create_local_socket();
#ifndef I4B_NOTCPIP_MONITOR
		remotesockfd = monitor_create_remote_socket(monitorport);
#endif
	}
#endif
	
	/* in case fullscreendisplay, initialize */

#ifdef USE_CURSES
	if (do_fullscreen)
	{
		init_screen();
	}
#endif

	/* init realtime priority */
  		
#ifdef USE_RTPRIO
  	if (rt_prio != RTPRIO_NOTUSED)
  	{
  		struct rtprio rtp;

  		rtp.type = RTP_PRIO_REALTIME;
  		rtp.prio = rt_prio;

  		if ((rtprio(RTP_SET, getpid(), &rtp)) == -1)
  		{
			logit(LL_ERR, "rtprio failed: %s", strerror(errno));
			do_exit(1);
		}
	}
#endif

	starttime = time(NULL);	/* get starttime */
	
	srandom(580403);	/* init random number gen */
	
	mloop(		/* enter loop of no return .. */
#ifdef I4B_EXTERNAL_MONITOR
		sockfd
#ifndef I4B_NOTCPIP_MONITOR
		, remotesockfd
#endif
#endif
		);
	do_exit(0);
	return(0);
}

/*---------------------------------------------------------------------------*
 *	program exit
 *---------------------------------------------------------------------------*/
void
do_exit(int exitval)
{
	close_allactive();

	unlink(PIDFILE);

	logit(LL_DMN, "daemon terminating, exitval = %d", exitval);
	
#ifdef USE_CURSES
	if (do_fullscreen && curses_ready)
		endwin();
#endif

#ifdef I4B_EXTERNAL_MONITOR
	monitor_exit();
#endif

	exit(exitval);
}

/*---------------------------------------------------------------------------*
 *	program exit
 *---------------------------------------------------------------------------*/
void
error_exit(int exitval, const char *fmt, ...)
{
	close_allactive();

	unlink(PIDFILE);

	logit(LL_DMN, "fatal error, daemon terminating, exitval = %d", exitval);
	
#ifdef USE_CURSES
	if (do_fullscreen && curses_ready)
		endwin();
#endif

#ifdef I4B_EXTERNAL_MONITOR
	monitor_exit();
#endif

	if (mailto[0] && mailer[0])
	{

#define EXITBL 2048

		char ebuffer[EXITBL];
		char sbuffer[EXITBL];
		va_list ap;

		va_start(ap, fmt);
		vsnprintf(ebuffer, EXITBL-1, fmt, ap);
		va_end(ap);

		signal(SIGCHLD, SIG_IGN);	/* remove handler */
		
		snprintf(sbuffer, sizeof(sbuffer), "%s%s%s%s%s%s%s%s",
			"cat << ENDOFDATA | ",
			mailer,
			" -s \"i4b isdnd: fatal error, terminating\" ",
			mailto,
			"\nThe isdnd terminated because of a fatal error:\n\n",
			ebuffer,
			"\n\nYours sincerely,\n   the isdnd\n",
			"\nENDOFDATA\n");
		system(sbuffer);
	}

	exit(exitval);
}

/*---------------------------------------------------------------------------*
 *	main loop
 *---------------------------------------------------------------------------*/
static void
mloop(
#ifdef I4B_EXTERNAL_MONITOR
	int localmonitor
#ifndef I4B_NOTCPIP_MONITOR
	, int remotemonitor
#endif
#endif
)
{
	fd_set set;
	struct timeval timeout;
	int ret;
	int high_selfd;

 	/* go into loop */
	
 	logit(LL_DMN, "i4b isdn daemon started (pid = %d)", getpid());
 
	for (;;)
	{
		FD_ZERO(&set);

#ifdef USE_CURSES
		if (do_fullscreen)
			FD_SET(STDIN_FILENO, &set);
#endif

		FD_SET(isdnfd, &set);

		high_selfd = isdnfd;
		
#ifdef I4B_EXTERNAL_MONITOR
		if (do_monitor)
		{
			if (localmonitor != -1) {
				/* always watch for new connections */
				FD_SET(localmonitor, &set);
				if (localmonitor > high_selfd)
					high_selfd = localmonitor;
			}
#ifndef I4B_NOTCPIP_MONITOR
			if (remotemonitor != -1) {
				FD_SET(remotemonitor, &set);
				if (remotemonitor > high_selfd)
					high_selfd = remotemonitor;
			}
#endif

			/* if there are client connections, let monitor module
			 * enter them into the fdset */
			if (accepted)
			{
				monitor_prepselect(&set, &high_selfd);
			}
		}
#endif
		
		timeout.tv_sec = 1;
		timeout.tv_usec = 0;

		ret = select(high_selfd + 1, &set, NULL, NULL, &timeout);

		if (ret > 0)
		{	
			if (FD_ISSET(isdnfd, &set))
				isdnrdhdl();

#ifdef USE_CURSES
			if (FD_ISSET(STDIN_FILENO, &set))
				kbdrdhdl();
#endif

#ifdef I4B_EXTERNAL_MONITOR
			if (do_monitor)
			{
				if (localmonitor != -1 && FD_ISSET(localmonitor, &set))
					monitor_handle_connect(localmonitor, 1);

#ifndef I4B_NOTCPIP_MONITOR
				if (remotemonitor != -1 && FD_ISSET(remotemonitor, &set))
					monitor_handle_connect(remotemonitor, 0);
#endif
				if (accepted)
					monitor_handle_input(&set);
			}
#endif
		}
		else if (ret == -1)
		{
			if (errno != EINTR)
			{
				logit(LL_ERR, "mloop: ERROR, select error on isdn device, errno = %d!", errno);
				error_exit(1, "mloop: ERROR, select error on isdn device, errno = %d!", errno);
			}
		}			

		/* handle timeout and recovery */		

		handle_recovery();
	}
}

#ifdef USE_CURSES
/*---------------------------------------------------------------------------*
 *	data from keyboard available, read and process it 
 *---------------------------------------------------------------------------*/
static void
kbdrdhdl(void)
{
	int ch = getch();
		
	switch (ch)
	{
	case 0x0c:	/* control L */
		wrefresh(curscr);
		break;
	
	case '\n':
	case '\r':
		do_menu();
		break;
	}
}
#endif

/*---------------------------------------------------------------------------*
 *	data from /dev/isdn available, read and process them
 *---------------------------------------------------------------------------*/
static void
isdnrdhdl(void)
{
	static unsigned char msg_rd_buf[MSG_BUF_SIZ];
	msg_hdr_t *hp = (msg_hdr_t *)&msg_rd_buf[0];
	
	register int len;

	if ((len = read(isdnfd, msg_rd_buf, MSG_BUF_SIZ)) > 0)
	{
		switch (hp->type)
		{
		case MSG_CONNECT_IND:				
			msg_connect_ind((msg_connect_ind_t *)msg_rd_buf, len);
			break;
			
		case MSG_CONNECT_ACTIVE_IND:
			msg_connect_active_ind((msg_connect_active_ind_t *)msg_rd_buf);
			break;

		case MSG_DISCONNECT_IND:
			msg_disconnect_ind((msg_disconnect_ind_t *)msg_rd_buf);
			break;
			
		case MSG_DIALOUT_IND:
			msg_dialout((msg_dialout_ind_t *)msg_rd_buf);
			break;

		case MSG_ACCT_IND:
			msg_accounting((msg_accounting_ind_t *)msg_rd_buf);
			break;

		case MSG_IDLE_TIMEOUT_IND:
			msg_idle_timeout_ind((msg_idle_timeout_ind_t *)msg_rd_buf);
			break;

		case MSG_CHARGING_IND:
			msg_charging_ind((msg_charging_ind_t *)msg_rd_buf);
			break;

		case MSG_PROCEEDING_IND:
			msg_proceeding_ind((msg_proceeding_ind_t *)msg_rd_buf);
			break;

		case MSG_ALERT_IND:
			msg_alert_ind((msg_alert_ind_t *)msg_rd_buf);
			break;

		case MSG_DRVRDISC_REQ:
			msg_drvrdisc_req((msg_drvrdisc_req_t *)msg_rd_buf);
			break;

		case MSG_L12STAT_IND:
			msg_l12stat_ind((msg_l12stat_ind_t *)msg_rd_buf);
			break;

		case MSG_TEIASG_IND:
			msg_teiasg_ind((msg_teiasg_ind_t *)msg_rd_buf);
			break;

		case MSG_PDEACT_IND:
			msg_pdeact_ind((msg_pdeact_ind_t *)msg_rd_buf);
			break;

		case MSG_NEGCOMP_IND:
			msg_negcomplete_ind((msg_negcomplete_ind_t *)msg_rd_buf);
			break;

		case MSG_IFSTATE_CHANGED_IND:
			msg_ifstatechg_ind((msg_ifstatechg_ind_t *)msg_rd_buf);
			break;

		case MSG_DIALOUTNUMBER_IND:
			msg_dialoutnumber((msg_dialoutnumber_ind_t *)msg_rd_buf);
			break;

		case MSG_PACKET_IND:
			msg_packet_ind((msg_packet_ind_t *)msg_rd_buf);
			break;

		case MSG_CONTR_EV_IND:
			msg_ctrl_ev_ind((msg_ctrl_ev_ind_t *)msg_rd_buf);
			break;

		default:
			logit(LL_WRN, "ERROR, unknown message received from /dev/isdn (0x%x)", msg_rd_buf[0]);
			break;
		}
	}
	else
	{
		logit(LL_WRN, "ERROR, read error on isdn device, errno = %d, length = %d", errno, len);
	}
}

/*---------------------------------------------------------------------------*
 *	re-read the config file on SIGHUP or menu command
 *---------------------------------------------------------------------------*/
void
rereadconfig(int dummy)
{
	logit(LL_DMN, "re-reading configuration file");
	
	close_allactive();

#if I4B_EXTERNAL_MONITOR
	monitor_clear_rights();
#endif

	remove_all_cfg_entries();

	/* read runtime configuration file and configure ourselves */
	
	configure(configfile, 1);

	if (config_error_flag)
	{
		logit(LL_ERR, "rereadconfig: there were %d error(s) in the configuration file, terminating!", config_error_flag);
		error_exit(1, "rereadconfig: there were %d error(s) in the configuration file, terminating!", config_error_flag);
	}

	if (aliasing)
	{
		/* reread alias database */
		free_aliases();
		init_alias(aliasfile);
	}
}

/*---------------------------------------------------------------------------*
 *	re-open the log/acct files on SIGUSR1
 *---------------------------------------------------------------------------*/
void
reopenfiles(int dummy)
{
        if (useacctfile)
	{
		/* close file */
		
	        fflush(acctfp);
	        fclose(acctfp);

	        /* if user specified a suffix, rename the old file */
	        
	        if (rotatesuffix[0] != '\0')
	        {
	        	char filename[MAXPATHLEN];

	        	snprintf(filename, sizeof(filename), "%s%s", acctfile, rotatesuffix);

			if ((rename(acctfile, filename)) != 0)
			{
				logit(LL_ERR, "reopenfiles: acct rename failed, cause = %s", strerror(errno));
				error_exit(1, "reopenfiles: acct rename failed, cause = %s", strerror(errno));
			}
		}

		if ((acctfp = fopen(acctfile, "a")) == NULL)
		{
			logit(LL_ERR, "ERROR, can't open acctfile %s for writing, terminating!", acctfile);
			error_exit(1, "ERROR, can't open acctfile %s for writing, terminating!", acctfile);
		}
		setvbuf(acctfp, (char *)NULL, _IONBF, 0);
	}

	if (uselogfile)
	{
	        finish_log();

	        /* if user specified a suffix, rename the old file */
	        
	        if (rotatesuffix[0] != '\0')
	        {
	        	char filename[MAXPATHLEN];

	        	snprintf(filename, sizeof(filename), "%s%s", logfile, rotatesuffix);

			if ((rename(logfile, filename)) != 0)
			{
				logit(LL_ERR, "reopenfiles: log rename failed, cause = %s", strerror(errno));
				error_exit(1, "reopenfiles: log rename failed, cause = %s", strerror(errno));
			}
		}

	        if ((logfp = fopen(logfile, "a")) == NULL)
		{
			fprintf(stderr, "ERROR, cannot open logfile %s: %s\n",
				logfile, strerror(errno));
			error_exit(1, "reopenfiles: ERROR, cannot open logfile %s: %s\n",
				logfile, strerror(errno));
		}

		/* set unbuffered operation */

		setvbuf(logfp, (char *)NULL, _IONBF, 0);
	}
}

/* EOF */