You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1042 lines
24 KiB
C
1042 lines
24 KiB
C
/*
|
|
* binkleyforce -- unix FTN mailer project
|
|
*
|
|
* Copyright (c) 1998-2000 Alexander Belkin, 2:5020/1398.11
|
|
*
|
|
* 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.
|
|
*
|
|
* $Id$
|
|
*/
|
|
|
|
#include "includes.h"
|
|
#include "confread.h"
|
|
#include "logger.h"
|
|
#include "util.h"
|
|
#include "nodelist.h"
|
|
#include "io.h"
|
|
#include "session.h"
|
|
#include "outbound.h"
|
|
#include "daemon.h"
|
|
|
|
/*
|
|
* Maxumum number of simultaneously running clients
|
|
*/
|
|
int max_tcpip = 0;
|
|
int max_modem = 0;
|
|
|
|
/*
|
|
* Current number of running clients
|
|
*/
|
|
int tcpip_clients = 0;
|
|
int modem_clients = 0;
|
|
|
|
/*
|
|
* Write queue/demon status to the log every XX seconds
|
|
*/
|
|
#define DAEMON_ALIVE_TIMER 1200
|
|
|
|
/*
|
|
* Time to sleep than all queues are in "DQ_Idle" state
|
|
*/
|
|
#define DAEMON_IDLE_SLEEP 5
|
|
|
|
/*
|
|
* Table of handled signals
|
|
*/
|
|
struct {
|
|
int signum;
|
|
DM_State state;
|
|
bool wait;
|
|
} daemon_signals[] = {
|
|
{ SIGHUP, DM_Restart, FALSE },
|
|
{ SIGINT, DM_Shutdown, FALSE },
|
|
{ SIGTERM, DM_Shutdown, FALSE },
|
|
{ SIGUSR1, DM_Usr1, FALSE },
|
|
{ SIGUSR2, DM_Usr2, FALSE },
|
|
{ 0, 0, FALSE }
|
|
};
|
|
|
|
/*
|
|
* Systems queue (with all adresses, flavors, etc.)
|
|
*/
|
|
s_sysqueue daemon_sys_queue;
|
|
|
|
/*
|
|
* Outgoing calls queues
|
|
*/
|
|
s_daemon_queue daemon_queues[] = {
|
|
{ &daemon_sys_queue, -1, 0, FALSE, DQ_Idle }, /* Modem */
|
|
{ &daemon_sys_queue, -1, 0, TRUE, DQ_Idle }, /* IP */
|
|
{ NULL, -1, 0, FALSE, DQ_Idle }
|
|
};
|
|
|
|
/*
|
|
* Positions of the certain queues in the 'daemon_queues' array
|
|
*/
|
|
#define MODEM_QUEUE 0
|
|
#define TCPIP_QUEUE 1
|
|
|
|
static RETSIGTYPE daemon_sighandler_chld(int sig)
|
|
{
|
|
int old_errno = errno;
|
|
int rc, status;
|
|
pid_t pid;
|
|
|
|
while( (pid = waitpid(-1, &status, WNOHANG)) > 0 )
|
|
{
|
|
rc = WIFEXITED(status) ? WEXITSTATUS(status) : -1;
|
|
if( daemon_branch_exit(pid, rc) == -1 )
|
|
log("wait got unexpected pid %d", (int)pid);
|
|
}
|
|
|
|
signal(SIGCHLD, daemon_sighandler_chld);
|
|
errno = old_errno;
|
|
}
|
|
|
|
static RETSIGTYPE daemon_sighandler(int signum)
|
|
{
|
|
int i;
|
|
|
|
for( i = 0; daemon_signals[i].signum; i++ )
|
|
if( daemon_signals[i].signum == signum )
|
|
{
|
|
daemon_signals[i].wait = TRUE;
|
|
return;
|
|
}
|
|
|
|
log("daemon got unexpected signal %d", signum);
|
|
}
|
|
|
|
static int daemon_sysentry_init(s_sysentry *sysent)
|
|
{
|
|
s_override *p;
|
|
char msg[64] = "";
|
|
char abuf[BF_MAXADDRSTR+1];
|
|
|
|
/* Read calls/sessions statistic from sts file */
|
|
(void)session_stat_get(&sysent->stat, &sysent->node.addr);
|
|
|
|
/* Lookup node in nodelist */
|
|
(void)nodelist_lookup(&sysent->node, sysent->node.addr);
|
|
|
|
/* Set overrides */
|
|
if( (p = conf_override(cf_override, sysent->node.addr)) )
|
|
{
|
|
sysent->overrides = p;
|
|
sysent->lineptr = NULL;
|
|
sysent->line = 0;
|
|
}
|
|
|
|
if( sysent->node.keyword == KEYWORD_HOLD )
|
|
{
|
|
strcpy(msg, "hold by nodelist");
|
|
sysent->stat.undialable = TRUE;
|
|
}
|
|
else if( sysent->node.keyword == KEYWORD_DOWN )
|
|
{
|
|
strcpy(msg, "down by nodelist");
|
|
sysent->stat.undialable = TRUE;
|
|
}
|
|
else if( sysent->stat.undialable )
|
|
strcpy(msg, "undialable");
|
|
else if( sysent->stat.hold_until )
|
|
{
|
|
int holdtime = sysent->stat.hold_until - time(0);
|
|
|
|
if( holdtime > 0 )
|
|
sprintf(msg, "calls holded for %d seconds", holdtime);
|
|
else
|
|
strcpy(msg, "calls holded");
|
|
}
|
|
else if( sysent->stat.hold_freqs )
|
|
{
|
|
int holdtime = sysent->stat.hold_freqs - time(0);
|
|
|
|
if( holdtime > 0 )
|
|
sprintf(msg, "freqs holded for %d seconds", holdtime);
|
|
else
|
|
strcpy(msg, "freqs holded");
|
|
}
|
|
|
|
if( msg && *msg )
|
|
log("QUEUE: add %s (%s)", ftn_addrstr(abuf, sysent->node.addr), msg);
|
|
else
|
|
log("QUEUE: add %s", ftn_addrstr(abuf, sysent->node.addr));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int daemon_sysentry_deinit(s_sysentry *sysent)
|
|
{
|
|
char abuf[BF_MAXADDRSTR+1];
|
|
|
|
log("QUEUE: remove %s", ftn_addrstr(abuf, sysent->node.addr));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static bool daemon_node_cancall_line(const s_node *node, const s_override *ovrd)
|
|
{
|
|
bool good_phone = FALSE;
|
|
bool good_host = FALSE;
|
|
bool good_time = FALSE;
|
|
time_t unixtime = time(NULL);
|
|
struct tm *now = localtime(&unixtime);
|
|
|
|
/*
|
|
* Check phone number
|
|
*/
|
|
if( ovrd && ovrd->sPhone && *ovrd->sPhone )
|
|
{
|
|
if( modem_isgood_phone(ovrd->sPhone) )
|
|
good_phone = TRUE;
|
|
}
|
|
else if( node && node->phone && *node->phone )
|
|
{
|
|
if( modem_isgood_phone(node->phone) )
|
|
good_phone = TRUE;
|
|
}
|
|
|
|
/*
|
|
* Check host name (IP address)
|
|
*/
|
|
if( ovrd && ovrd->sIpaddr && *ovrd->sIpaddr )
|
|
{
|
|
if( tcpip_isgood_host(ovrd->sIpaddr) )
|
|
good_host = TRUE;
|
|
}
|
|
|
|
/*
|
|
* Check work time
|
|
*/
|
|
if( ovrd && timevec_isdefined(&ovrd->worktime) )
|
|
{
|
|
if( timevec_isnow(&ovrd->worktime, now) )
|
|
good_time = TRUE;
|
|
}
|
|
else if( ovrd && ovrd->sFlags && !nodelist_checkflag(ovrd->sFlags, "CM") )
|
|
{
|
|
/*
|
|
* It is not an error. We check CM flag only
|
|
* if the work time was not specified in the override
|
|
*/
|
|
good_time = TRUE;
|
|
}
|
|
else if( node && timevec_isdefined(&node->worktime) )
|
|
{
|
|
if( timevec_isnow(&node->worktime, now) )
|
|
good_time = TRUE;
|
|
}
|
|
|
|
if( good_time )
|
|
{
|
|
if( good_host )
|
|
return 2;
|
|
else if( good_phone )
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
enum { UNHOLD_CALLS, UNHOLD_FREQS };
|
|
|
|
static void daemon_unhold_system(s_sysentry *syst, int mode)
|
|
{
|
|
char abuf[BF_MAXADDRSTR+1];
|
|
s_sess_stat stat_diff;
|
|
|
|
memset(&stat_diff, '\0', sizeof(s_sess_stat));
|
|
|
|
/*
|
|
* Reset try counters to the TRIES_RESET value
|
|
*/
|
|
session_stat_reset_counters(&stat_diff);
|
|
|
|
if( mode == UNHOLD_FREQS )
|
|
stat_diff.hold_freqs = HOLD_RESET;
|
|
else
|
|
stat_diff.hold_until = HOLD_RESET;
|
|
|
|
/*
|
|
* Write new statistic to the sts file
|
|
*/
|
|
if( session_stat_apply_diff(syst->node.addr, stat_diff) == -1 )
|
|
{
|
|
log("cannot write statistic for address %s",
|
|
ftn_addrstr(abuf, syst->node.addr));
|
|
}
|
|
|
|
/*
|
|
* Read new statistic from sts file, ignore errors
|
|
*/
|
|
(void)session_stat_get(&syst->stat, &syst->node.addr);
|
|
}
|
|
|
|
static bool daemon_node_cancall(s_sysentry *syst, bool ign_wtime, bool tcpip)
|
|
{
|
|
char abuf[BF_MAXADDRSTR+1];
|
|
const s_override *ptrl, *lastptr;
|
|
int line;
|
|
int rc;
|
|
|
|
/* Is this system allready active in an another queue? Skip. */
|
|
if( syst->busy )
|
|
return FALSE;
|
|
|
|
/* Check for undialable flag */
|
|
if( syst->stat.undialable )
|
|
return FALSE;
|
|
|
|
if( daemon_branch_exist(syst->node.addr) )
|
|
return FALSE;
|
|
|
|
/* Check for netmail presence, when MAILONLY */
|
|
if( conf_options(cf_options) & OPTIONS_MAILONLY )
|
|
if ( !( syst->types & TYPE_NETMAIL ) )
|
|
return FALSE;
|
|
|
|
/* Check system hold status */
|
|
if( syst->stat.hold_until > 0 )
|
|
{
|
|
if( time(0) > syst->stat.hold_until )
|
|
{
|
|
log("unholding %s", ftn_addrstr(abuf, syst->node.addr));
|
|
daemon_unhold_system(syst, UNHOLD_CALLS);
|
|
}
|
|
else
|
|
return FALSE;
|
|
}
|
|
|
|
/* Check freq hold status */
|
|
if( syst->stat.hold_freqs > 0 )
|
|
{
|
|
if( time(0) > syst->stat.hold_freqs )
|
|
{
|
|
log("unholding freqs for %s", ftn_addrstr(abuf, syst->node.addr));
|
|
daemon_unhold_system(syst, UNHOLD_FREQS);
|
|
}
|
|
else if( !syst->flavors && (syst->types & TYPE_REQUEST) )
|
|
return FALSE;
|
|
}
|
|
|
|
/* Make checks for all lines */
|
|
if( syst->overrides )
|
|
{
|
|
if( syst->lineptr && syst->lineptr->hidden )
|
|
{
|
|
line = syst->line + 1;
|
|
/* Check lines after current */
|
|
for( ptrl = syst->lineptr->hidden; ptrl; ptrl = ptrl->hidden )
|
|
{
|
|
if( (rc = daemon_node_cancall_line(NULL, ptrl))
|
|
&& (rc == (tcpip ? 2 : 1)) )
|
|
{
|
|
syst->line = line;
|
|
syst->lineptr = ptrl;
|
|
syst->tcpip = (rc == 2) ? TRUE : FALSE;
|
|
return TRUE;
|
|
}
|
|
line++;
|
|
}
|
|
}
|
|
|
|
if( syst->lineptr == NULL )
|
|
{ lastptr = NULL; syst->line = 0; }
|
|
else
|
|
{ lastptr = syst->lineptr->hidden; }
|
|
|
|
line = 0;
|
|
/* Check lines before current */
|
|
for( ptrl = syst->overrides; ptrl && ptrl != lastptr; ptrl = ptrl->hidden )
|
|
{
|
|
if( (rc = daemon_node_cancall_line(line ? NULL : &syst->node, ptrl))
|
|
&& (rc == (tcpip ? 2 : 1)) )
|
|
{
|
|
syst->line = line;
|
|
syst->lineptr = ptrl;
|
|
syst->tcpip = (rc == 2) ? TRUE : FALSE;
|
|
return TRUE;
|
|
}
|
|
line++;
|
|
}
|
|
}
|
|
else /* Node without overriden parameters */
|
|
{
|
|
syst->line = line = 0;
|
|
return daemon_node_cancall_line(&syst->node, NULL);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static int daemon_getnext(const s_sysqueue *q, int current, bool tcpip)
|
|
{
|
|
int i;
|
|
int normal = -1;
|
|
|
|
if( current < 0 )
|
|
current = 0;
|
|
|
|
/* Check systems AFTER the current */
|
|
for( i = current + 1; i < q->sysnum; i++ )
|
|
{
|
|
if( q->systab[i].flavors & FLAVOR_IMMED ) {
|
|
if( daemon_node_cancall(&q->systab[i], TRUE, tcpip) )
|
|
return i;
|
|
} else if( q->systab[i].flavors & FLAVOR_CRASH ) {
|
|
if( daemon_node_cancall(&q->systab[i], FALSE, tcpip) )
|
|
return i;
|
|
} else if( q->systab[i].flavors & FLAVOR_DIRECT
|
|
|| q->systab[i].flavors & FLAVOR_NORMAL ) {
|
|
if( normal == -1 && daemon_node_cancall(&q->systab[i], FALSE, tcpip) )
|
|
normal = i;
|
|
}
|
|
}
|
|
|
|
/* Check systems BEFORE the current */
|
|
for( i = 0; (i < current + 1) && (i < q->sysnum); i++ )
|
|
{
|
|
if( q->systab[i].flavors & FLAVOR_IMMED ) {
|
|
if( daemon_node_cancall(&q->systab[i], TRUE, tcpip) )
|
|
return i;
|
|
} else if( q->systab[i].flavors & FLAVOR_CRASH ) {
|
|
if( daemon_node_cancall(&q->systab[i], FALSE, tcpip) )
|
|
return i;
|
|
} else if( q->systab[i].flavors & FLAVOR_DIRECT
|
|
|| q->systab[i].flavors & FLAVOR_NORMAL ) {
|
|
if( normal == -1 && daemon_node_cancall(&q->systab[i], FALSE, tcpip) )
|
|
normal = i;
|
|
}
|
|
}
|
|
|
|
return normal;
|
|
}
|
|
|
|
static int daemon_getnext2(const s_sysqueue *q, int current, bool tcpip)
|
|
{
|
|
int next;
|
|
|
|
/* Unlock previous system if available */
|
|
if( current >= 0 )
|
|
q->systab[current].busy = FALSE;
|
|
|
|
next = daemon_getnext(q, current, tcpip);
|
|
|
|
/* Lock new system */
|
|
if( next >= 0 )
|
|
q->systab[next].busy = TRUE;
|
|
|
|
return next;
|
|
}
|
|
|
|
static void daemon_do_tries_action(s_sysqueue *q, int pos, s_tries *act, const char *msg)
|
|
{
|
|
s_sess_stat stat_diff;
|
|
char abuf[BF_MAXADDRSTR+1];
|
|
|
|
memset(&stat_diff, '\0', sizeof(s_sess_stat));
|
|
|
|
if( act->action == TRIES_ACTION_UNDIALABLE )
|
|
{
|
|
log("set %s undialable: %s",
|
|
ftn_addrstr(abuf, q->systab[pos].node.addr), msg);
|
|
stat_diff.undialable = TRUE;
|
|
}
|
|
else if( act->action == TRIES_ACTION_HOLDSYSTEM )
|
|
{
|
|
log("hold %s for %d seconds: %s",
|
|
ftn_addrstr(abuf, q->systab[pos].node.addr), act->arg, msg);
|
|
stat_diff.hold_until = time(NULL) + act->arg;
|
|
}
|
|
else if( act->action == TRIES_ACTION_HOLDALL )
|
|
{
|
|
log("hold all systems for %d seconds: %s", act->arg, msg);
|
|
q->holduntil = time(NULL) + act->arg;
|
|
}
|
|
|
|
/*
|
|
* Write new statistic to the sts file
|
|
*/
|
|
if( session_stat_apply_diff(q->systab[pos].node.addr, stat_diff) == -1 )
|
|
{
|
|
log("cannot write statistic for address %s",
|
|
ftn_addrstr(abuf, q->systab[pos].node.addr));
|
|
}
|
|
|
|
/*
|
|
* Read new statistic from sts file, ignore errors
|
|
*/
|
|
(void)session_stat_get(&q->systab[pos].stat,
|
|
&q->systab[pos].node.addr);
|
|
}
|
|
|
|
static void daemon_process_rc(s_sysqueue *q, s_faddr addr, int rc)
|
|
{
|
|
int i;
|
|
s_tries *maxtries = NULL;
|
|
s_tries *maxtries_noansw = NULL;
|
|
s_tries *maxtries_noconn = NULL;
|
|
s_tries *maxtries_nodial = NULL;
|
|
s_tries *maxtries_hshake = NULL;
|
|
s_tries *maxtries_sessns = NULL;
|
|
char abuf[BF_MAXADDRSTR+1];
|
|
|
|
for( i = 0; i < q->sysnum; i++ )
|
|
if( ftn_addrcomp(q->systab[i].node.addr, addr) == 0 )
|
|
break;
|
|
|
|
if( i < q->sysnum )
|
|
{
|
|
/* Set last call finish time */
|
|
q->systab[i].lastcall = time(0);
|
|
|
|
/* Temporary set `state' structure to
|
|
make dynamical configuration work! */
|
|
state.valid = TRUE;
|
|
state.node = q->systab[i].node;
|
|
state.listed = q->systab[i].node.listed;
|
|
|
|
maxtries = conf_tries(cf_maxtries);
|
|
maxtries_noansw = conf_tries(cf_maxtries_noansw);
|
|
maxtries_noconn = conf_tries(cf_maxtries_noconn);
|
|
maxtries_nodial = conf_tries(cf_maxtries_nodial);
|
|
maxtries_hshake = conf_tries(cf_maxtries_hshake);
|
|
maxtries_sessns = conf_tries(cf_maxtries_sessions);
|
|
|
|
/* Reset `state' structure */
|
|
init_state(&state);
|
|
|
|
/* Update statistic, ignore errors */
|
|
session_stat_get(&q->systab[i].stat, &addr);
|
|
|
|
log("%s: rc = %d [%d/%d/%d/%d/%d/%d]",
|
|
ftn_addrstr(abuf, addr), rc,
|
|
q->systab[i].stat.tries,
|
|
q->systab[i].stat.tries_noansw,
|
|
q->systab[i].stat.tries_noconn,
|
|
q->systab[i].stat.tries_nodial,
|
|
q->systab[i].stat.tries_hshake,
|
|
q->systab[i].stat.tries_sessns);
|
|
|
|
DEB((D_DAEMON, "daemon_process_rc: address %s: [%d/%d/%d/%d/%d/%d]",
|
|
ftn_addrstr(abuf, addr),
|
|
q->systab[i].stat.tries,
|
|
q->systab[i].stat.tries_noansw,
|
|
q->systab[i].stat.tries_noconn,
|
|
q->systab[i].stat.tries_nodial,
|
|
q->systab[i].stat.tries_hshake,
|
|
q->systab[i].stat.tries_sessns));
|
|
|
|
if( maxtries && q->systab[i].stat.tries >= maxtries->tries )
|
|
{
|
|
daemon_do_tries_action(q, i, maxtries, "reached maximal tries count");
|
|
}
|
|
else if( maxtries_noansw && q->systab[i].stat.tries_noansw >= maxtries_noansw->tries )
|
|
{
|
|
daemon_do_tries_action(q, i, maxtries_noansw, "reached maximal no answers count");
|
|
}
|
|
else if( maxtries_noconn && q->systab[i].stat.tries_noconn >= maxtries_noconn->tries )
|
|
{
|
|
daemon_do_tries_action(q, i, maxtries_noconn, "reached maximal connect failures count");
|
|
}
|
|
else if( maxtries_nodial && q->systab[i].stat.tries_nodial >= maxtries_nodial->tries )
|
|
{
|
|
daemon_do_tries_action(q, i, maxtries_nodial, "reached maximal no dialtone count");
|
|
}
|
|
else if( maxtries_hshake && q->systab[i].stat.tries_hshake >= maxtries_hshake->tries )
|
|
{
|
|
daemon_do_tries_action(q, i, maxtries_hshake, "reached maximal handshake failures count");
|
|
}
|
|
else if( maxtries_sessns && q->systab[i].stat.tries_sessns >= maxtries_sessns->tries )
|
|
{
|
|
daemon_do_tries_action(q, i, maxtries_sessns, "reached maximal session failures count");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* No entry in the systems queue
|
|
* TODO: load statistic and check tries? */
|
|
log("%s: rc = %d",
|
|
ftn_addrstr(abuf, addr), rc);
|
|
DEB((D_DAEMON, "daemon_process_rc: address %s: [%d/%d/%d/%d/%d]",
|
|
ftn_addrstr(abuf, addr)));
|
|
}
|
|
}
|
|
|
|
static void daemon_alive_message(s_sysqueue *q)
|
|
{
|
|
int i, holded_num = 0;
|
|
|
|
for( i = 0; i < q->sysnum; i++ )
|
|
if( q->systab[i].stat.hold_until
|
|
|| q->systab[i].stat.hold_freqs )
|
|
holded_num++;
|
|
|
|
log("still alive (%d systems, %d holded, %d branches)",
|
|
q->sysnum, holded_num, daemon_branch_number());
|
|
}
|
|
|
|
static void daemon_queue_do(s_daemon_queue *dq)
|
|
{
|
|
bool select_next_address = FALSE;
|
|
s_sysqueue *q = dq->q;
|
|
#ifdef DEBUG
|
|
char abuf[BF_MAXADDRSTR+1];
|
|
#endif
|
|
|
|
if( dq->current >= 0 )
|
|
{
|
|
if( (q->systab[dq->current].lastcall == 0)
|
|
|| (q->systab[dq->current].lastcall + dq->circle < time(0)) )
|
|
{
|
|
if( !daemon_branch_exist(q->systab[dq->current].node.addr) )
|
|
{
|
|
DEB((D_DAEMON, "daemon_queue: calling %s",
|
|
ftn_addrstr(abuf, q->systab[dq->current].node.addr)));
|
|
if( !daemon_call(&q->systab[dq->current]) )
|
|
select_next_address = TRUE;
|
|
}
|
|
else
|
|
select_next_address = TRUE;
|
|
}
|
|
}
|
|
else
|
|
select_next_address = TRUE;
|
|
|
|
if( select_next_address )
|
|
{
|
|
int next = daemon_getnext2(q, dq->current, dq->tcpip);
|
|
DEB((D_DAEMON, "daemon_queue: next = %d, current = %d",
|
|
next, dq->current));
|
|
|
|
if( next >= 0 )
|
|
{
|
|
dq->current = next;
|
|
|
|
/*
|
|
* Temporary set `state' structure to
|
|
* make dynamical configuration work!
|
|
*/
|
|
state.valid = TRUE;
|
|
state.node = q->systab[dq->current].node;
|
|
state.listed = q->systab[dq->current].node.listed;
|
|
state.inet = dq->tcpip;
|
|
|
|
if( (q->systab[dq->current].flavors & FLAVOR_IMMED) )
|
|
dq->circle = conf_number(cf_daemon_circle_immed);
|
|
else if( (q->systab[dq->current].flavors & FLAVOR_CRASH) )
|
|
dq->circle = conf_number(cf_daemon_circle_crash);
|
|
else if( (q->systab[dq->current].flavors & FLAVOR_DIRECT) )
|
|
dq->circle = conf_number(cf_daemon_circle_direct);
|
|
else
|
|
dq->circle = conf_number(cf_daemon_circle_normal);
|
|
|
|
init_state(&state);
|
|
}
|
|
else
|
|
dq->current = -1;
|
|
}
|
|
}
|
|
|
|
void daemon_queues_update_current(s_daemon_queue dqs[], int pos)
|
|
{
|
|
int i;
|
|
|
|
for( i = 0; dqs[i].q; i++ )
|
|
{
|
|
if( dqs[i].current < 0 )
|
|
continue;
|
|
|
|
if( dqs[i].current == pos )
|
|
dqs[i].current = -1;
|
|
else if( dqs[i].current > pos )
|
|
dqs[i].current--;
|
|
}
|
|
}
|
|
|
|
int daemon_rescan_sysqueue(s_sysqueue *q, s_daemon_queue dqs[])
|
|
{
|
|
int i;
|
|
s_outbound_callback_data ocb;
|
|
|
|
out_reset_sysqueue(q);
|
|
|
|
memset(&ocb, '\0', sizeof(s_outbound_callback_data));
|
|
ocb.callback = out_handle_sysqueue;
|
|
ocb.dest = (void *)q;
|
|
if( out_scan(&ocb, NULL) )
|
|
{
|
|
log("error scanning outbound");
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* Remove empty entries from sysqueue (e.g. the mail was
|
|
* removed by the sysop or by an another program)
|
|
*/
|
|
for( i = 0; i < q->sysnum; i++ )
|
|
if( !q->systab[i].types && !q->systab[i].flavors )
|
|
{
|
|
out_remove_from_sysqueue(q, i);
|
|
daemon_queues_update_current(dqs, i);
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
log_sysqueue(q);
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
int daemon_remove_waiting_branches(s_sysqueue *q, s_daemon_queue dqs[])
|
|
{
|
|
int i;
|
|
int j;
|
|
s_daemon_branch *bptr;
|
|
#ifdef DEBUG
|
|
char abuf[BF_MAXADDRSTR+1];
|
|
#endif
|
|
|
|
while( (i = daemon_branch_get_first_waiting()) >= 0 )
|
|
{
|
|
bptr = daemon_branch_get_pointer(i);
|
|
if( !bptr )
|
|
{
|
|
log("internal error: cannot get branch pointer!");
|
|
return -1;
|
|
}
|
|
|
|
DEB((D_DAEMON, "daemon: process branch information for %s, rc = %d",
|
|
ftn_addrstr(abuf, bptr->addr), bptr->rc));
|
|
|
|
if( bptr->tcpip )
|
|
--tcpip_clients;
|
|
else
|
|
--modem_clients;
|
|
|
|
daemon_process_rc(q, bptr->addr, bptr->rc);
|
|
|
|
if( bptr->rc == BFERR_NOERROR )
|
|
{
|
|
/* Remove system from queue */
|
|
for( j = 0; j < q->sysnum; j++ )
|
|
if( !ftn_addrcomp(bptr->addr, q->systab[j].node.addr) )
|
|
{
|
|
out_remove_from_sysqueue(q, j);
|
|
daemon_queues_update_current(dqs, j);
|
|
}
|
|
}
|
|
|
|
/* Hold modem line for some time */
|
|
if( !bptr->tcpip && bptr->portname )
|
|
{
|
|
daemon_line_hold(bptr->portname,
|
|
conf_number(cf_daemon_circle_modem));
|
|
}
|
|
|
|
/* Remove branch entry */
|
|
daemon_branch_remove(i);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#define PIDFILE_CREATE 1
|
|
#define PIDFILE_DELETE 2
|
|
#define PIDFILE_TERMINATE 3
|
|
|
|
int daemon_pidfile(int cmd)
|
|
{
|
|
char *pidfile = conf_string(cf_daemon_pid_file);
|
|
pid_t hispid, mypid = getpid();
|
|
FILE *pf;
|
|
struct stat sb;
|
|
|
|
if( !pidfile )
|
|
return 0;
|
|
|
|
switch (cmd) {
|
|
case PIDFILE_CREATE:
|
|
if( !stat(pidfile, &sb) ) {
|
|
pf = fopen(pidfile, "r");
|
|
if ( !pf ) {
|
|
log("daemon_pidfile: cannot open %s for reading", pidfile);
|
|
return -1;
|
|
}
|
|
|
|
fscanf(pf, "%d", &hispid);
|
|
fclose(pf);
|
|
|
|
if( hispid ) {
|
|
if( hispid == mypid )
|
|
return 0;
|
|
if( !kill(hispid, 0) ) {
|
|
log("daemon_pidfile: another daemon exist. pid=%d", hispid);
|
|
return -1;
|
|
}
|
|
if( errno != ESRCH ) {
|
|
log("daemon_pidfile: error sending signal. pid=%d, errno=%d", hispid, errno);
|
|
return -1;
|
|
}
|
|
}
|
|
} else if( errno != ENOENT )
|
|
{
|
|
log("daemon_pidfile: error stat(%s). errno=%d", pidfile, errno);
|
|
return -1;
|
|
}
|
|
|
|
pf = fopen(pidfile, "w");
|
|
if( !pf ) {
|
|
log("daemon_pidfile: cannot open %s for writing", pidfile);
|
|
return -1;
|
|
}
|
|
|
|
fprintf(pf, "%d", mypid);
|
|
fclose(pf);
|
|
break;
|
|
|
|
case PIDFILE_DELETE:
|
|
if( !stat(pidfile, &sb) ) {
|
|
pf = fopen(pidfile, "r");
|
|
if( !pf ) {
|
|
log("daemon_pidfile: cannot open %s for reading", pidfile);
|
|
return -1;
|
|
}
|
|
|
|
fscanf(pf, "%d", &hispid);
|
|
fclose(pf);
|
|
|
|
if( !hispid || (hispid == mypid) ) {
|
|
unlink(pidfile);
|
|
return 0;
|
|
} else {
|
|
log("daemon_pidfile: oops! mypid=%d, hispid=%d", mypid, hispid);
|
|
return -1;
|
|
}
|
|
} else {
|
|
log("daemon_pidfile: error stat(%s). errno=%d", pidfile, errno);
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case PIDFILE_TERMINATE:
|
|
if( !stat(pidfile, &sb) ) {
|
|
pf = fopen(pidfile, "r");
|
|
if( !pf ) {
|
|
log("daemon_pidfile: cannot open %s for reading", pidfile);
|
|
return -1;
|
|
}
|
|
|
|
fscanf(pf, "%d", &hispid);
|
|
fclose(pf);
|
|
unlink(pidfile);
|
|
|
|
if( hispid )
|
|
kill(hispid, 15);
|
|
|
|
} else {
|
|
log("daemon_pidfile: error stat(%s). errno=%d", pidfile, errno);
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
log("daemon_pidfile: undefined cmd = %d", cmd);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int daemon_run(const char *confname, const char *incname, bool quit)
|
|
{
|
|
DM_State dmstate = DM_Start;
|
|
time_t timer_rescan = 0;
|
|
time_t timer_alive = 0;
|
|
bool started = FALSE;
|
|
int circle_rescan = 0;
|
|
int rc = 0;
|
|
int i;
|
|
|
|
if ( quit ) {
|
|
daemon_pidfile(PIDFILE_TERMINATE);
|
|
exit(0);
|
|
}
|
|
|
|
/* Initialisation */
|
|
memset(&daemon_sys_queue, '\0', sizeof(s_sysqueue));
|
|
|
|
/* Install signal handlers */
|
|
signal(SIGCHLD, daemon_sighandler_chld);
|
|
signal(SIGINT, daemon_sighandler);
|
|
signal(SIGTERM, daemon_sighandler);
|
|
signal(SIGHUP, daemon_sighandler);
|
|
signal(SIGUSR1, daemon_sighandler);
|
|
signal(SIGUSR2, daemon_sighandler);
|
|
|
|
while(1)
|
|
{
|
|
switch(dmstate) {
|
|
case DM_Start:
|
|
DEB((D_DAEMON, "daemon: entering state DM_Start"));
|
|
|
|
if ( daemon_pidfile(PIDFILE_CREATE) == -1 )
|
|
exit(0);
|
|
|
|
circle_rescan = conf_number(cf_daemon_circle_rescan);
|
|
max_tcpip = conf_number(cf_daemon_maxclients_tcpip);
|
|
max_modem = conf_number(cf_daemon_maxclients_modem);
|
|
|
|
/* Set default values */
|
|
if( !circle_rescan ) circle_rescan = 60;
|
|
|
|
/* Disable counting of files/mail size */
|
|
sysqueue_dont_count_sizes = TRUE;
|
|
|
|
/* Setup sysqueue scanner callbacks */
|
|
sysqueue_add_callback = daemon_sysentry_init;
|
|
sysqueue_rem_callback = daemon_sysentry_deinit;
|
|
|
|
/* Reopen log and debug files */
|
|
if( log_isopened() )
|
|
log_close();
|
|
#ifdef DEBUG
|
|
if( debug_isopened() )
|
|
debug_close();
|
|
#endif
|
|
if( log_open(log_getfilename(LOG_FILE_DAEMON), NULL, NULL) )
|
|
{
|
|
log("can't continue without logging");
|
|
return BFERR_FATALERROR;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
(void)debug_setfilename(log_getfilename(LOG_FILE_DEBUG));
|
|
#endif
|
|
|
|
log("%sstarting daemon (%s)",
|
|
started ? "re" : "", BF_VERSION);
|
|
|
|
started = TRUE;
|
|
dmstate = DM_ProcessQueues;
|
|
break;
|
|
|
|
case DM_Restart:
|
|
DEB((D_DAEMON, "daemon: entering state DM_Restart"));
|
|
|
|
/* Release memory */
|
|
deinit_sysqueue(&daemon_sys_queue);
|
|
deinit_conf();
|
|
|
|
/* Reset queues */
|
|
daemon_queues[MODEM_QUEUE].current =
|
|
daemon_queues[TCPIP_QUEUE].current = -1;
|
|
daemon_queues[MODEM_QUEUE].circle =
|
|
daemon_queues[TCPIP_QUEUE].circle = 0;
|
|
daemon_queues[MODEM_QUEUE].state =
|
|
daemon_queues[TCPIP_QUEUE].state = DQ_Idle;
|
|
|
|
/* Read primary config file */
|
|
if( confname && *confname )
|
|
rc = conf_readconf(confname, 0);
|
|
else
|
|
rc = conf_readconf(conf_getconfname(), 0);
|
|
|
|
if( rc )
|
|
return(BFERR_FATALERROR);
|
|
|
|
/* Read additional config file (manual include) */
|
|
if( incname && *incname && conf_readconf(incname, 1) )
|
|
log("cannot read additional config (ignore)");
|
|
|
|
dmstate = DM_Start;
|
|
break;
|
|
|
|
case DM_ProcessQueues:
|
|
DEB((D_DAEMON, "daemon: entering state DM_ProcessQueues"));
|
|
|
|
/*
|
|
* Handle received signals
|
|
*/
|
|
for( i = 0; daemon_signals[i].signum; i++ )
|
|
if( daemon_signals[i].wait )
|
|
break;
|
|
|
|
if( daemon_signals[i].signum )
|
|
{
|
|
daemon_signals[i].wait = FALSE;
|
|
dmstate = daemon_signals[i].state;
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Process finished branches
|
|
*/
|
|
(void)daemon_remove_waiting_branches(&daemon_sys_queue,
|
|
daemon_queues);
|
|
|
|
/*
|
|
* Check rescan timer
|
|
*/
|
|
if( !timer_running(timer_rescan) || timer_expired(timer_rescan) )
|
|
{
|
|
(void)daemon_rescan_sysqueue(&daemon_sys_queue,
|
|
daemon_queues);
|
|
timer_set(&timer_rescan, circle_rescan);
|
|
}
|
|
|
|
/*
|
|
* Check alive timer
|
|
*/
|
|
if( !timer_running(timer_alive) || timer_expired(timer_alive) )
|
|
{
|
|
daemon_alive_message(&daemon_sys_queue);
|
|
timer_set(&timer_alive, DAEMON_ALIVE_TIMER);
|
|
}
|
|
|
|
if( max_modem > 0 )
|
|
daemon_queue_do(&daemon_queues[MODEM_QUEUE]);
|
|
if( max_tcpip > 0 )
|
|
daemon_queue_do(&daemon_queues[TCPIP_QUEUE]);
|
|
|
|
(void)sleep(DAEMON_IDLE_SLEEP);
|
|
break;
|
|
|
|
case DM_Usr1:
|
|
DEB((D_DAEMON, "daemon: entering state DM_Usr1"));
|
|
|
|
log("signal USR1 has no effect now. Have any ideas?");
|
|
dmstate = DM_ProcessQueues;
|
|
|
|
break;
|
|
|
|
case DM_Usr2:
|
|
DEB((D_DAEMON, "daemon: entering state DM_Usr2"));
|
|
|
|
log("signal USR2 has no effect now. Have any ideas?");
|
|
dmstate = DM_ProcessQueues;
|
|
|
|
break;
|
|
|
|
case DM_Shutdown:
|
|
DEB((D_DAEMON, "daemon: entering state DM_Shutdown"));
|
|
|
|
log("stopping daemon");
|
|
daemon_pidfile(PIDFILE_DELETE);
|
|
|
|
/* Release memory */
|
|
deinit_sysqueue(&daemon_sys_queue);
|
|
daemon_branch_deinit();
|
|
daemon_lines_deinit();
|
|
deinit_conf();
|
|
|
|
exit(0);
|
|
}
|
|
}
|
|
}
|
|
|