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

830 lines
20 KiB
C

/*
* 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.13 2011/08/31 16:24:59 plunky 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 */
__dead static void mloop(int sockfd);
#else /* I4B_NOTCPIP_MONITOR */
/* monitor via local and tcp/ip socket */
__dead 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);
__dead 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, 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 timeo;
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
timeo.tv_sec = 1;
timeo.tv_usec = 0;
ret = select(high_selfd + 1, &set, NULL, NULL, &timeo);
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, 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, NULL, _IONBF, 0);
}
}
/* EOF */