/************************************************************************
* IRC - Internet Relay Chat, ircd/ircd.c
* Copyright (C) 1990 Jarkko Oikarinen and
* University of Oulu, Computing Center
*
* 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef lint
static char rcsid[] = "@(#)$Id: ircd.c,v 1.13 2004/07/04 10:57:31 skold Exp $";
#endif
#include "os.h"
#include "s_defines.h"
#define IRCD_C
#include "s_externs.h"
#undef IRCD_C
#ifdef RUSNET_IRCD
#include <locale.h>
char *rusnetfile = RUSNETCONF_PATH;
#endif
aClient me; /* That's me */
aClient *client = &me; /* Pointer to beginning of Client list */
static void open_debugfile(), io_loop();
istat_t istat;
char **myargv;
int rehashed = 0;
int portnum = -1; /* Server port number, listening this */
char *configfile = IRCDCONF_PATH; /* Server configuration file */
int debuglevel = -1; /* Server debug level */
int bootopt = BOOT_PROT|BOOT_STRICTPROT; /* Server boot option flags */
char *debugmode = ""; /* -"- -"- -"- -"- */
char *sbrk0; /* initial sbrk(0) */
char *tunefile = IRCDTUNE_PATH;
int dorehash = 0,
dorestart = 0,
restart_iauth = 0;
time_t nextconnect = 1; /* time for next try_connections call */
time_t nextgarbage = 1; /* time for next collect_channel_garbage call*/
time_t nextping = 1; /* same as above for check_pings() */
time_t nextdnscheck = 0; /* next time to poll dns to force timeouts */
time_t nextexpire = 1; /* next expire run on the dns cache */
time_t nextiarestart = 1; /* next time to check if iauth is alive */
time_t nextlockscheck = 1; /* next time to expire NDELAY locks -kmale */
time_t nextcmapscheck = 1; /* next time to expire collision maps -erra */
#if defined(USE_SERVICES) && defined(RUSNET_IRCD)
unsigned long invincible; /* prepare crc for SERVICES_SERV */
#endif
void server_reboot(mesg)
char *mesg;
{
Reg int i;
int time_slave;
#ifdef USE_SYSLOG
(void)syslog(LOG_WARNING, "Restarting Server because: %s (%u)", mesg,
(u_int)((char *)sbrk((size_t)0)-sbrk0));
#endif
sendto_flag(SCH_NOTICE, "Restarting server because: %s (%u)", mesg,
(u_int)((char *)sbrk((size_t)0)-sbrk0));
Debug((DEBUG_NOTICE,"Restarting server..."));
flush_connections(me.fd);
/*
** fd 0 must be 'preserved' if either the -d or -i options have
** been passed to us before restarting.
*/
#ifdef USE_SYSLOG
(void)closelog();
#endif
logfiles_close();
for (i = 3; i < MAXCONNECTIONS; i++)
(void)close(i);
if (!(bootopt & (BOOT_TTY|BOOT_DEBUG)))
(void)close(2);
(void)close(1);
if ((bootopt & BOOT_CONSOLE) || isatty(0))
(void)close(0);
ircd_writetune(tunefile);
if (!(bootopt & (BOOT_INETD|BOOT_OPER)))
{
/* Have to wait for our zombies --skold */
Debug((DEBUG_DEBUG, "Waiting for slaves:"));
time_slave = timeofday;
if (wait(NULL) > -1)
Debug((DEBUG_DEBUG,
"Some slaves were alive... But since we are here, they are already dead"));
Debug((DEBUG_DEBUG, "Time waited: %d secs", timeofday - time_slave));
(void)execv(IRCD_PATH, myargv);
#ifdef USE_SYSLOG
/* Have to reopen since it has been closed above */
openlog(mybasename(myargv[0]), LOG_PID|LOG_NDELAY, LOG_FACILITY);
syslog(LOG_CRIT, "execv(%s,%s) failed: %m\n", IRCD_PATH,
myargv[0]);
closelog();
#endif
Debug((DEBUG_FATAL,"Couldn't restart server: %s",
strerror(errno)));
}
exit(-1);
}
/*
** try_connections
**
** Scan through configuration and try new connections.
** Returns the calendar time when the next call to this
** function should be made latest. (No harm done if this
** is called earlier or later...)
*/
static time_t try_connections(currenttime)
time_t currenttime;
{
static time_t lastsort = 0;
Reg aConfItem *aconf;
Reg aClient *cptr;
aConfItem **pconf;
int confrq;
time_t next = 0;
aClass *cltmp;
aConfItem *con_conf = NULL;
double f, f2;
aCPing *cp;
Debug((DEBUG_NOTICE,"Connection check at : %s",
myctime(currenttime)));
for (aconf = conf; aconf; aconf = aconf->next )
{
/* Also when already connecting! (update holdtimes) --SRB */
if (!(aconf->status & (CONF_CONNECT_SERVER|CONF_ZCONNECT_SERVER)))
continue;
/*
** Skip this entry if the use of it is still on hold until
** future. Otherwise handle this entry (and set it on hold
** until next time). Will reset only hold times, if already
** made one successfull connection... [this algorithm is
** a bit fuzzy... -- msa >;) ]
*/
if ((aconf->hold > currenttime))
{
if ((next > aconf->hold) || (next == 0))
next = aconf->hold;
continue;
}
send_ping(aconf);
if (aconf->port <= 0)
continue;
cltmp = Class(aconf);
confrq = get_con_freq(cltmp);
aconf->hold = currenttime + confrq;
/*
** Found a CONNECT config with port specified, scan clients
** and see if this server is already connected?
*/
cptr = find_name(aconf->name, (aClient *)NULL);
if (!cptr)
cptr = find_mask(aconf->name, (aClient *)NULL);
/*
** It is not connected, scan clients and see if any matches
** a D(eny) line.
*/
if (find_denied(aconf->name, Class(cltmp)))
continue;
/* We have a candidate, let's see if it could be the best. */
if (!cptr && (Links(cltmp) < MaxLinks(cltmp)) &&
(!con_conf || con_conf->localpref < aconf->localpref ||
(con_conf->pref > aconf->pref && aconf->pref >= 0) ||
(con_conf->pref == -1 &&
Class(cltmp) > ConfClass(con_conf))))
con_conf = aconf;
if ((next > aconf->hold) || (next == 0))
next = aconf->hold;
}
if (con_conf)
{
if (con_conf->next) /* are we already last? */
{
for (pconf = &conf; (aconf = *pconf);
pconf = &(aconf->next))
/* put the current one at the end and
* make sure we try all connections
*/
if (aconf == con_conf)
*pconf = aconf->next;
(*pconf = con_conf)->next = 0;
}
if (connect_server(con_conf, (aClient *)NULL,
(struct hostent *)NULL) == 0)
sendto_flag(SCH_NOTICE,
"Connection to %s[%s] activated.",
con_conf->name, con_conf->host);
}
Debug((DEBUG_NOTICE,"Next connection check : %s", myctime(next)));
/*
* calculate preference value based on accumulated stats.
*/
if (!lastsort || lastsort < currenttime)
{
for (aconf = conf; aconf; aconf = aconf->next)
if (!(cp = aconf->ping) || !cp->seq || !cp->recvd)
aconf->pref = -1;
else
{
f = (double)cp->recvd / (double)cp->seq;
f2 = pow(f, (double)20.0);
if (f2 < (double)0.001)
f = (double)0.001;
else
f = f2;
f2 = (double)cp->ping / (double)cp->recvd;
f = f2 / f;
if (f > 100000.0)
f = 100000.0;
aconf->pref = (u_int) (f * (double)100.0);
}
lastsort = currenttime + 60;
}
return (next);
}
static void close_held(cptr)
aClient *cptr;
{
Reg aClient *acptr;
int i;
for (i = highest_fd; i >= 0; i--)
if ((acptr = local[i]) && (cptr->port == acptr->port) &&
(acptr != cptr) && IsHeld(acptr) &&
!bcmp((char *)&cptr->ip, (char *)&acptr->ip,
sizeof(acptr->ip)))
{
(void) exit_client(acptr, acptr, &me,
"Reconnect Timeout");
return;
}
}
static time_t check_pings(currenttime)
time_t currenttime;
{
static time_t lkill = 0;
Reg aClient *cptr;
Reg int kflag = 0;
int ping = 0, i, rflag = 0;
time_t oldest = 0, timeout;
char *reason;
for (i = highest_fd; i >= 0; i--)
{
if (!(cptr = local[i]) || IsListening(cptr) || IsLog(cptr) ||
IsHeld(cptr))
continue;
/*
* K and R lines once per minute, max. This is the max.
* granularity in K-lines anyway (with time field).
*/
if (
#if defined(TIMEDKLINES) || ( defined(R_LINES) && defined(R_LINES_OFTEN) )
(currenttime - lkill > TIMEDKLINES) ||
#endif /* TIMEDKLINES */
rehashed)
{
if (IsPerson(cptr))
{
kflag = find_kill(cptr, rehashed, &reason
#ifdef RUSNET_IRCD
, cptr->name
#endif
);
#ifdef R_LINES_OFTEN
rflag = find_restrict(cptr);
#endif
}
else
{
kflag = rflag = 0;
reason = NULL;
}
}
ping = IsRegistered(cptr) ? get_client_ping(cptr) :
ACCEPTTIMEOUT;
Debug((DEBUG_DEBUG, "c(%s) %d p %d k %d r %d a %d",
cptr->name, cptr->status, ping, kflag, rflag,
currenttime - cptr->lasttime));
/*
* Ok, so goto's are ugly and can be avoided here but this code
* is already indented enough so I think its justified. -avalon
*/
if (!kflag && !rflag && IsRegistered(cptr) &&
(ping >= currenttime - cptr->lasttime))
goto ping_timeout;
/*
* If the server hasnt talked to us in 2*ping seconds
* and it has a ping time, then close its connection.
* If the client is a user and a KILL line was found
* to be active, close this connection too.
*/
if (kflag || rflag ||
((currenttime - cptr->lasttime) >= (2 * ping) &&
(cptr->flags & FLAGS_PINGSENT)) ||
(!IsRegistered(cptr) &&
(currenttime - cptr->firsttime) >= ping))
{
if (IsReconnect(cptr))
{
sendto_flag(SCH_ERROR,
"Reconnect timeout to %s",
get_client_name(cptr, TRUE));
close_held(cptr);
(void)exit_client(cptr, cptr, &me,
"Ping timeout");
}
if (!IsRegistered(cptr) &&
(DoingDNS(cptr) || DoingAuth(cptr) ||
DoingXAuth(cptr)))
{
if (cptr->authfd >= 0)
{
(void)close(cptr->authfd);
cptr->authfd = -1;
cptr->count = 0;
*cptr->buffer = '\0';
}
Debug((DEBUG_NOTICE, "%s/%c%s timeout %s",
(DoingDNS(cptr)) ? "DNS" : "dns",
(DoingXAuth(cptr)) ? "X" : "x",
(DoingAuth(cptr)) ? "AUTH" : "auth",
get_client_name(cptr,TRUE)));
del_queries((char *)cptr);
ClearAuth(cptr);
#if defined(USE_IAUTH)
if (DoingDNS(cptr) || DoingXAuth(cptr))
{
if (DoingDNS(cptr) &&
(iauth_options & XOPT_EXTWAIT))
{
/* iauth wants more time */
sendto_iauth("%d d", cptr->fd);
ClearDNS(cptr);
cptr->lasttime = currenttime;
continue;
}
if (DoingXAuth(cptr) &&
(iauth_options & XOPT_NOTIMEOUT))
{
cptr->exitc = EXITC_AUTHTOUT;
sendto_iauth("%d T", cptr->fd);
ereject_user(cptr, " Timeout ",
"Authentication Timeout");
continue;
}
sendto_iauth("%d T", cptr->fd);
SetDoneXAuth(cptr);
}
#endif
ClearDNS(cptr);
ClearXAuth(cptr);
ClearWXAuth(cptr);
cptr->firsttime = currenttime;
cptr->lasttime = currenttime;
continue;
}
if (IsServer(cptr) || IsConnecting(cptr) ||
IsHandshake(cptr))
sendto_flag(SCH_NOTICE,
"No response from %s closing link",
get_client_name(cptr, FALSE));
/*
* this is used for KILL lines with time restrictions
* on them - send a messgae to the user being killed
* first.
*/
if (kflag && IsPerson(cptr))
{
char buf[100];
sendto_flag(SCH_NOTICE,
"Kill line active for %s",
get_client_name(cptr, FALSE));
cptr->exitc = EXITC_KLINE;
if (!BadPtr(reason))
sprintf(buf, "Kill line active: %.80s",
reason);
(void)exit_client(cptr, cptr, &me, (reason) ?
buf : "Kill line active");
}
#if defined(R_LINES) && defined(R_LINES_OFTEN)
else if (IsPerson(cptr) && rflag)
{
sendto_flag(SCH_NOTICE,
"Restricting %s, closing link.",
get_client_name(cptr,FALSE));
cptr->exitc = EXITC_RLINE;
(void)exit_client(cptr, cptr, &me,
"Restricting");
}
#endif
else
{
cptr->exitc = EXITC_PING;
(void)exit_client(cptr, cptr, &me,
"Ping timeout");
}
continue;
}
else if (IsRegistered(cptr) &&
(cptr->flags & FLAGS_PINGSENT) == 0)
{
/*
* if we havent PINGed the connection and we havent
* heard from it in a while, PING it to make sure
* it is still alive.
*/
cptr->flags |= FLAGS_PINGSENT;
/* not nice but does the job */
cptr->lasttime = currenttime - ping;
sendto_one(cptr, "PING :%s", me.name);
}
ping_timeout:
timeout = cptr->lasttime + ping;
while (timeout <= currenttime)
timeout += ping;
if (timeout < oldest || !oldest)
oldest = timeout;
}
if (currenttime - lkill > 60)
lkill = currenttime;
if (!oldest || oldest < currenttime)
oldest = currenttime + PINGFREQUENCY;
if (oldest < currenttime + 2)
oldest += 2;
Debug((DEBUG_NOTICE,"Next check_ping() call at: %s, %d %d %d",
myctime(oldest), ping, oldest, currenttime));
return (oldest);
}
/*
** Check expired NDELAY locks -kmale
*/
static time_t check_locks(time)
time_t time;
{
remove_locks(time-DELAYCHASETIMELIMIT);
return(time+KILLCHASETIMELIMIT);
}
/*
** Check expired collision maps -erra
*/
static time_t check_cmaps(time)
time_t time;
{
expire_collision_map(time);
return (time + CMAPCHECKEVERY);
}
static void setup_me(mp)
aClient *mp;
{
struct passwd *p;
p = getpwuid(getuid());
strncpyzt(mp->username, (p) ? p->pw_name : "unknown",
sizeof(mp->username));
(void)get_my_name(mp, mp->sockhost, sizeof(mp->sockhost)-1);
/* Setup hostp - fake record to resolve localhost. -Toor */
mp->hostp = (struct hostent *)MyMalloc(sizeof(struct hostent));
mp->hostp->h_name = MyMalloc(strlen(me.sockhost)+1);
strcpy(mp->hostp->h_name, mp->sockhost);
mp->hostp->h_aliases = (char **)MyMalloc(sizeof(char *));
*mp->hostp->h_aliases = NULL;
mp->hostp->h_addrtype = AFINET;
mp->hostp->h_length =
#ifdef INET6
IN6ADDRSZ;
#else
sizeof(long);
#endif
mp->hostp->h_addr_list = (char **)MyMalloc(2*sizeof(char *));
#ifdef INET6
mp->hostp->h_addr_list[0] = (char *)&in6addr_loopback;
#else
mp->hostp->h_addr_list[0] = (void *)MyMalloc(mp->hostp->h_length);
*(long *)(mp->hostp->h_addr_list[0]) = IN_LOOPBACKNET;
#endif
mp->hostp->h_addr_list[1] = NULL ;
if (mp->name[0] == '\0')
strncpyzt(mp->name, mp->sockhost, sizeof(mp->name));
if (me.info == DefInfo)
me.info = mystrdup("IRCers United");
mp->lasttime = mp->since = mp->firsttime = time(NULL);
mp->hopcount = 0;
mp->authfd = -1;
mp->auth = mp->username;
mp->confs = NULL;
mp->flags = 0;
mp->acpt = mp->from = mp;
mp->next = NULL;
mp->user = NULL;
mp->fd = -1;
SetMe(mp);
(void) make_server(mp);
mp->serv->snum = find_server_num (ME);
(void) make_user(mp);
istat.is_users++; /* here, cptr->next is NULL, see make_user() */
mp->user->flags |= FLAGS_OPER;
mp->serv->up = mp->name;
#ifdef RUSNET_IRCD
mp->serv->crc = gen_crc(mp->name);
#endif
mp->user->server = find_server_string(mp->serv->snum);
strncpyzt(mp->user->username, (p) ? p->pw_name : "unknown",
sizeof(mp->user->username));
(void) strcpy(mp->user->host, mp->name);
(void)add_to_client_hash_table(mp->name, mp);
setup_server_channels(mp);
}
/*
** bad_command
** This is called when the commandline is not acceptable.
** Give error message and exit without starting anything.
*/
static int bad_command()
{
(void)printf(
"Usage: ircd [-a] [-b] [-c]%s [-h servername] [-q] [-o] [-i] [-T tunefile] [-p (strict|on|off)] [-s] [-v] %s\n",
#ifdef CMDLINE_CONFIG
" [-f config]",
#else
"",
#endif
#ifdef DEBUGMODE
" [-x loglevel] [-t]"
#else
""
#endif
);
(void)printf("Server not started\n\n");
exit(-1);
}
int main(argc, argv)
int argc;
char *argv[];
{
uid_t uid, euid;
(void) myctime(time(NULL)); /* Don't ask, just *don't* ask */
sbrk0 = (char *)sbrk((size_t)0);
uid = getuid();
euid = geteuid();
/* If by any chance PROFIL is defined (xBSD) you will not compile --skold
#ifdef PROFIL
(void)monstartup(0, etext);
(void)moncontrol(1);
(void)signal(SIGUSR1, s_monitor);
#endif
*/
#ifdef CHROOTDIR
ircd_res_init();
if (chdir(ROOT_PATH)!=0)
{
perror("chdir");
(void)fprintf(stderr,"%s: Cannot chdir: %s.\n", IRCD_PATH,
ROOT_PATH);
exit(5);
}
if (chroot(ROOT_PATH)!=0)
{
perror("chroot");
(void)fprintf(stderr,"%s: Cannot chroot: %s.\n", IRCD_PATH,
ROOT_PATH);
exit(5);
}
#endif /*CHROOTDIR*/
#ifdef ZIP_LINKS
if (zlib_version[0] == '0')
{
fprintf(stderr, "zlib version 1.0 or higher required\n");
exit(1);
}
if (zlib_version[0] != ZLIB_VERSION[0])
{
fprintf(stderr, "incompatible zlib version\n");
exit(1);
}
if (strcmp(zlib_version, ZLIB_VERSION) != 0)
{
fprintf(stderr, "warning: different zlib version\n");
}
#endif
myargv = argv;
(void)umask(077); /* better safe than sorry --SRB */
bzero((char *)&me, sizeof(me));
version = make_version(); /* Generate readable version string */
#ifdef SEND_ISUPPORT
isupport = make_isupport(); /* generate 005 numeric */
#endif
/*
** All command line parameters have the syntax "-fstring"
** or "-f string" (e.g. the space is optional). String may
** be empty. Flag characters cannot be concatenated (like
** "-fxyz"), it would conflict with the form "-fstring".
*/
while (--argc > 0 && (*++argv)[0] == '-')
{
char *p = argv[0]+1;
int flag = *p++;
if (flag == '\0' || *p == '\0')
if (argc > 1 && argv[1][0] != '-')
{
p = *++argv;
argc -= 1;
}
else
p = "";
switch (flag)
{
case 'a':
bootopt |= BOOT_AUTODIE;
break;
case 'b':
bootopt |= BOOT_BADTUNE;
break;
case 'c':
bootopt |= BOOT_CONSOLE;
break;
case 'q':
bootopt |= BOOT_QUICK;
break;
case 'o': /* Per user local daemon... */
(void)setuid((uid_t)uid);
bootopt |= BOOT_OPER;
break;
#ifdef CMDLINE_CONFIG
case 'f':
(void)setuid((uid_t)uid);
configfile = p;
break;
#endif
case 'h':
if (*p == '\0')
bad_command();
strncpyzt(me.name, p, sizeof(me.name));
break;
case 'i':
bootopt |= BOOT_INETD|BOOT_AUTODIE;
break;
case 'p':
if (!strcmp(p, "strict"))
bootopt |= BOOT_PROT|BOOT_STRICTPROT;
else if (!strcmp(p, "on"))
bootopt |= BOOT_PROT;
else if (!strcmp(p, "off"))
bootopt &= ~(BOOT_PROT|BOOT_STRICTPROT);
else
bad_command();
break;
case 's':
bootopt |= BOOT_NOIAUTH;
break;
case 't':
(void)setuid((uid_t)uid);
bootopt |= BOOT_TTY;
break;
case 'T':
if (*p == '\0')
bad_command();
tunefile = p;
break;
case 'v':
(void)printf("ircd %s %s\n\tzlib %s\n\t%s #%s\n",
version, serveropts,
#ifndef ZIP_LINKS
"not used",
#else
zlib_version,
#endif
creation, generation);
exit(0);
case 'x':
#ifdef DEBUGMODE
(void)setuid((uid_t)uid);
debuglevel = atoi(p);
debugmode = *p ? p : "0";
bootopt |= BOOT_DEBUG;
break;
#else
(void)fprintf(stderr,
"%s: DEBUGMODE must be defined for -x y\n",
myargv[0]);
exit(0);
#endif
default:
bad_command();
}
}
if (argc > 0)
bad_command(); /* This exits out */
#if defined(USE_IAUTH) && defined(__CYGWIN32__)
if ((bootopt & BOOT_NOIAUTH) == 0)
{
bootopt |= BOOT_NOIAUTH;
(void)fprintf(stderr, "WARNING: Assuming -s option.\n");
}
#endif
#ifndef IRC_UID
if ((uid != euid) && !euid)
{
(void)fprintf(stderr,
"ERROR: do not run ircd setuid root. Make it setuid a\
normal user.\n");
exit(-1);
}
#endif
#if !defined(CHROOTDIR)
(void)setuid((uid_t)euid);
# if defined(IRC_UID) && defined(IRC_GID)
if ((int)getuid() == 0)
{
/* run as a specified user */
(void)fprintf(stderr,"WARNING: running ircd with uid = %d\n",
IRC_UID);
(void)fprintf(stderr," changing to gid %d.\n",IRC_GID);
(void)setgid(IRC_GID);
(void)setuid(IRC_UID);
}
# endif
#endif /*CHROOTDIR/UID/GID*/
#if defined(USE_IAUTH)
if ((bootopt & BOOT_NOIAUTH) == 0)
restore_sigchld(); /* We need this if we expect valid response from wait()
* about iauth -X test results. (If in doubt - we do ;)) --skold
*/
switch (vfork())
{
case -1:
fprintf(stderr, "%s: Unable to fork!", myargv[0]);
exit(-1);
case 0:
close(0); close(1); close(3);
if (execl(IAUTH_PATH, IAUTH, "-X", NULL) < 0)
_exit(-1);
default:
{
int rc;
(void)wait(&rc);
if (rc != 0)
{
fprintf(stderr,
"%s: error: unable to find \"%s\".\n",
myargv[0], IAUTH_PATH);
exit(-1);
}
}
}
#endif
/*
** This way to set locale is incorrect
** but we do not need this stuff anyway -skold
**
#if defined(RUSNET_IRCD)
{
char *locale = getenv ("LC_ALL");
if (!locale || !*locale)
locale = getenv ("LANG");
setlocale(LC_ALL, (!locale || !*locale) ? "C" : locale);
}
#endif
*/
setup_signals();
/* didn't set debuglevel */
/* but asked for debugging output to tty */
if ((debuglevel < 0) && (bootopt & BOOT_TTY))
{
(void)fprintf(stderr,
"you specified -t without -x. use -x <n>\n");
exit(-1);
}
#ifdef RUSNET_IRCD
initialize_rusnet(rusnetfile);
#endif
initstats();
ircd_readtune(tunefile);
timeofday = time(NULL);
#ifdef CACHED_MOTD
motd = NULL;
read_motd(IRCDMOTD_PATH);
#endif
inithashtables();
initlists();
initclass();
initwhowas();
timeofday = time(NULL);
open_debugfile();
timeofday = time(NULL);
(void)init_sys();
logfiles_open();
#ifdef USE_SYSLOG
openlog(mybasename(myargv[0]), LOG_PID|LOG_NDELAY, LOG_FACILITY);
#endif
timeofday = time(NULL);
if (initconf(bootopt, configfile) == -1)
{
Debug((DEBUG_FATAL, "Failed in reading configuration file %s",
configfile));
/* no can do.
(void)printf("Couldn't open configuration file %s\n",
configfile);
*/
exit(-1);
}
else
{
aClient *acptr = NULL;
int i;
for (i = 0; i <= highest_fd; i++)
{
if (!(acptr = local[i]))
continue;
if (IsListening(acptr))
break;
acptr = NULL;
}
/* exit if there is nothing to listen to */
if (acptr == NULL && !(bootopt & BOOT_INETD))
exit(-1);
/* Is there an M-line? */
if (!find_me())
exit(-1);
}
dbuf_init();
#ifdef RUSNET_IRCD
gen_crc32table();
#endif /* RUSNET_IRCD */
setup_me(&me);
#ifdef RUSNET_IRCD
srand(timeofday ^ me.serv->crc);
# ifdef USE_SERVICES
invincible = gen_crc(SERVICES_SERV); /* prepare crc for SERVICES_SERV */
# endif /* USE_SERVICES */
#endif /* RUSNET_IRCD */
check_class();
ircd_writetune(tunefile);
if (bootopt & BOOT_INETD)
{
aClient *tmp;
aConfItem *aconf;
tmp = make_client(NULL);
tmp->fd = 0;
tmp->flags = FLAGS_LISTEN;
tmp->acpt = tmp;
tmp->from = tmp;
tmp->firsttime = time(NULL);
SetMe(tmp);
(void)strcpy(tmp->name, "*");
if (inetport(tmp, 0, "0.0.0.0", 0))
tmp->fd = -1;
if (tmp->fd == 0)
{
aconf = make_conf();
aconf->status = CONF_LISTEN_PORT;
aconf->clients++;
aconf->next = conf;
conf = aconf;
tmp->confs = make_link();
tmp->confs->next = NULL;
tmp->confs->value.aconf = aconf;
add_fd(tmp->fd, &fdas);
add_fd(tmp->fd, &fdall);
set_non_blocking(tmp->fd, tmp);
}
else
exit(5);
}
if (bootopt & BOOT_OPER)
{
aClient *tmp = add_connection(&me, 0);
if (!tmp)
exit(1);
SetMaster(tmp);
local[0] = tmp;
}
else
write_pidfile();
Debug((DEBUG_NOTICE,"Server ready..."));
#ifdef USE_SYSLOG
syslog(LOG_NOTICE, "Server Ready: v%s (%s #%s)", version, creation,
generation);
#endif
timeofday = time(NULL);
while (1)
io_loop();
}
void io_loop()
{
static time_t delay = 0;
int maxs = 4;
/*
** We only want to connect if a connection is due,
** not every time through. Note, if there are no
** active C lines, this call to Tryconnections is
** made once only; it will return 0. - avalon
*/
if (nextconnect && timeofday >= nextconnect)
nextconnect = try_connections(timeofday);
/*
** Every once in a while, hunt channel structures that
** can be freed.
*/
if (timeofday >= nextgarbage)
nextgarbage = collect_channel_garbage(timeofday);
/*
** DNS checks. One to timeout queries, one for cache expiries.
*/
if (timeofday >= nextdnscheck)
nextdnscheck = timeout_query_list(timeofday);
if (timeofday >= nextexpire)
nextexpire = expire_cache(timeofday);
/*
** Expired NDELAY locks check. -kmale
*/
if (timeofday >= nextlockscheck)
nextlockscheck = check_locks(timeofday);
/*
** Expired collision maps check. -erra
*/
if (timeofday >= nextcmapscheck)
nextcmapscheck = check_cmaps(timeofday);
/*
** take the smaller of the two 'timed' event times as
** the time of next event (stops us being late :) - avalon
** WARNING - nextconnect can return 0!
*/
if (nextconnect)
delay = MIN(nextping, nextconnect);
else
delay = nextping;
delay = MIN(nextdnscheck, delay);
delay = MIN(nextexpire, delay);
delay -= timeofday;
/*
** Adjust delay to something reasonable [ad hoc values]
** (one might think something more clever here... --msa)
** We don't really need to check that often and as long
** as we don't delay too long, everything should be ok.
** waiting too long can cause things to timeout...
** i.e. PINGS -> a disconnection :(
** - avalon
*/
if (delay < 1)
delay = 1;
else
delay = MIN(delay, TIMESEC);
/*
** First, try to drain traffic from servers (this includes listening
** ports). Give up, either if there's no traffic, or too many
** iterations.
*/
while (maxs--)
if (read_message(0, &fdas, 0))
flush_fdary(&fdas);
else
break;
Debug((DEBUG_DEBUG, "delay for %d", delay));
/*
** Second, deal with _all_ clients but only try to empty sendQ's for
** servers. Other clients are dealt with below..
*/
if (read_message(1, &fdall, 1) == 0 && delay > 1)
{
/*
** Timed out (e.g. *NO* traffic at all).
** Try again but also check to empty sendQ's for all clients.
*/
(void)read_message(delay - 1, &fdall, 0);
}
timeofday = time(NULL);
Debug((DEBUG_DEBUG ,"Got message(s)"));
/*
** ...perhaps should not do these loops every time,
** but only if there is some chance of something
** happening (but, note that conf->hold times may
** be changed elsewhere--so precomputed next event
** time might be too far away... (similarly with
** ping times) --msa
*/
if (timeofday >= nextping)
{
nextping = check_pings(timeofday);
rehashed = 0;
}
if (dorestart)
server_reboot("Caught SIGINT");
if (dorehash)
{ /* Only on signal, not on oper /rehash */
ircd_writetune(tunefile);
(void)rehash(&me, &me, 1);
dorehash = 0;
}
if (restart_iauth || timeofday >= nextiarestart)
{
Debug((DEBUG_DEBUG ,"restart_iauth=%d", restart_iauth ));
start_iauth(restart_iauth);
restart_iauth = 0;
nextiarestart = timeofday + 15;
}
/*
** Flush output buffers on all connections now if they
** have data in them (or at least try to flush)
** -avalon
*/
flush_connections(me.fd);
#ifdef DEBUGMODE
checklists();
#endif
}
/*
* open_debugfile
*
* If the -t option is not given on the command line when the server is
* started, all debugging output is sent to the file set by IRCDDBG_PATH.
* Here we just open that file and make sure it is opened to fd 2 so that
* any fprintf's to stderr also goto the logfile. If the debuglevel is not
* set from the command line by -x, use /dev/null as the dummy logfile as long
* as DEBUGMODE has been defined, else don't waste the fd.
*/
static void open_debugfile()
{
#ifdef DEBUGMODE
int fd;
aClient *cptr;
if (debuglevel >= 0)
{
cptr = make_client(NULL);
cptr->fd = 2;
SetLog(cptr);
cptr->port = debuglevel;
cptr->flags = 0;
cptr->acpt = cptr;
local[2] = cptr;
(void)strcpy(cptr->sockhost, me.sockhost);
(void)printf("isatty = %d ttyname = %#x\n",
isatty(2), (u_int)ttyname(2));
if (!(bootopt & BOOT_TTY)) /* leave debugging output on fd 2 */
{
(void)truncate(IRCDDBG_PATH, 0);
if ((fd = open(IRCDDBG_PATH,O_WRONLY|O_CREAT,0600))<0)
if ((fd = open("/dev/null", O_WRONLY)) < 0)
exit(-1);
if (fd != 2)
{
(void)dup2(fd, 2);
(void)close(fd);
}
strncpyzt(cptr->name, IRCDDBG_PATH,sizeof(cptr->name));
}
else if (isatty(2) && ttyname(2))
strncpyzt(cptr->name, ttyname(2), sizeof(cptr->name));
else
(void)strcpy(cptr->name, "FD2-Pipe");
Debug((DEBUG_FATAL, "Debug: File <%s> Level: %d at %s",
cptr->name, cptr->port, myctime(time(NULL))));
}
else
local[2] = NULL;
#endif
return;
}
/*
* Called from bigger_hash_table(), s_die(), server_reboot(),
* main(after initializations), grow_history(), rehash(io_loop) signal.
*/
void ircd_writetune(filename)
char *filename;
{
int fd;
char buf[100];
(void)truncate(filename, 0);
if ((fd = open(filename, O_CREAT|O_WRONLY, 0600)) >= 0)
{
(void)sprintf(buf, "%d\n%d\n%d\n%d\n%d\n%d\n", ww_size,
lk_size, _HASHSIZE, _CHANNELHASHSIZE,
_SERVERSIZE, poolsize);
if (write(fd, buf, strlen(buf)) == -1)
sendto_flag(SCH_ERROR,
"Failed (%d) to write tune file: %s.",
errno, mybasename(filename));
else
sendto_flag(SCH_NOTICE, "Updated %s.",
mybasename(filename));
close(fd);
}
else
sendto_flag(SCH_ERROR, "Failed (%d) to open tune file: %s.",
errno, mybasename(filename));
}
/*
* Called only from main() at startup.
*/
void ircd_readtune(filename)
char *filename;
{
int fd, t_data[6];
char buf[100];
memset(buf, 0, sizeof(buf));
if ((fd = open(filename, O_RDONLY)) != -1)
{
read(fd, buf, 100); /* no panic if this fails.. */
if (sscanf(buf, "%d\n%d\n%d\n%d\n%d\n%d\n", &t_data[0],
&t_data[1], &t_data[2], &t_data[3],
&t_data[4], &t_data[5]) != 6)
{
close(fd);
if (bootopt & BOOT_BADTUNE)
return;
else
{
fprintf(stderr,
"ircd tune file %s: bad format\n",
filename);
exit(1);
}
}
/*
** Initiate the tune-values after successfully
** reading the tune-file.
*/
ww_size = t_data[0];
lk_size = t_data[1];
_HASHSIZE = t_data[2];
_CHANNELHASHSIZE = t_data[3];
_SERVERSIZE = t_data[4];
poolsize = t_data[5];
/*
** the lock array only grows if the whowas array grows,
** I don't think it should be initialized with a lower
** size since it will never adjust unless whowas array does.
*/
/*
** For big and medium networks old lock management is
** not so good, I've changed it. -kmale
*/
#ifdef CONSERVATIVE_NDELAY_MALLOC
if (lk_size < ww_size)
lk_size = ww_size;
#endif
close(fd);
}
}
syntax highlighted by Code2HTML, v. 0.9.1