/************************************************************************
* IRC - Internet Relay Chat, src/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.
*/
/* $Id: ircd.c,v 1.7 2005/12/26 05:21:38 tux316 Exp $ */
#include "struct.h"
#include "common.h"
#include "sys.h"
#include "numeric.h"
#include "msg.h"
#include "sbuf.h"
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/resource.h>
#include <sys/socket.h>
#include <pwd.h>
#include <signal.h>
#include <fcntl.h>
#if defined PROFILING && defined __GLIBC__ && (__GLIBC__ >= 2)
#include <sys/gmon.h>
#define monstartup __monstartup
#endif
#include "inet.h"
#include "h.h"
#include "patchlevel.h"
#include "dh.h"
#include "throttle.h"
#include "userban.h"
#include "clones.h"
#include "hooks.h"
#include "fds.h"
#include "memcount.h"
aMotd *motd;
aMotd *helpfile; /* misnomer, aMotd could be generalized */
aMotd *shortmotd; /* short motd */
/* global conf options (from option block) */
char ProxyMonURL[TOPICLEN+1];
char ProxyMonHost[HOSTLEN+1];
char Network_Name[HOSTLEN+1];
char Services_Name[HOSTLEN+1];
char Stats_Name[HOSTLEN+1];
char NS_Register_URL[TOPICLEN+1];
char Network_Kline_Address[HOSTLEN+1];
char Local_Kline_Address[HOSTLEN+1];
char Staff_Address[HOSTLEN+1];
char HiddenServName[HOSTLEN+9];
char HiddenServDesc[TOPICLEN+1];
char HELPCHAN[HOSTLEN+1];
char WEBSITE[HOSTLEN+1];
char AUP[HOSTLEN+1];
int maxchannelsperuser, tsmaxdelta, tswarndelta;
int confopts, new_confopts;
int local_ip_limit, local_ip24_limit, global_ip_limit, global_ip24_limit;
/* hostmasking */
char HostPrefix[HOSTLEN+1]; /* FIXME: A little too big? */
char HostDomain[HOSTLEN+1];
/* SSL */
char SSL_Certificate[HOSTLEN+1];
char SSL_Keyfile[HOSTLEN+1];
/* User & Bot list options class */
char *bot_class;
/* this stuff by mnystrom@mit.edu */
#include "fdlist.h"
fdlist default_fdlist; /* just the number of the entry */
int MAXCLIENTS = MAX_ACTIVECONN; /* runtime configurable by m_set */
struct Counter Count;
int R_do_dns, R_fin_dns, R_fin_dnsc, R_fail_dns, R_do_id,
R_fin_id, R_fail_id;
time_t NOW;
time_t last_stat_save;
aClient me; /* That's me */
aClient *client = &me; /* Pointer to beginning of Client list */
int forked = 0;
float curSendK = 0, curRecvK = 0;
#ifdef LOCKFILE
extern time_t pending_kline_time;
extern struct pkl *pending_klines;
extern void do_pending_klines(void);
#endif
extern void engine_read_message(int);
void server_reboot();
void restart(char *);
static void open_debugfile(), setup_signals();
static void io_loop();
/* externally needed functions */
extern void init_fdlist(fdlist *); /* defined in fdlist.c */
extern void read_motd(char *); /* defined in s_serv.c */
extern void read_shortmotd(char *); /* defined in s_serv.c */
extern void read_help(char *); /* defined in s_serv.c */
extern void init_globals();
extern int klinestore_init(int); /* defined in klines.c */
char **myargv;
char configfile[PATH_MAX] = {0}; /* Server configuration file */
int debuglevel = -1; /* Server debug level */
int bootopt = 0; /* Server boot option flags */
char *debugmode = ""; /* -"- -"- -"- */
char *sbrk0; /* initial sbrk(0) */
static int dorehash = 0;
char dpath[PATH_MAX] = {0}; /* our configure files live in here */
char spath[PATH_MAX] = {0}; /* the path to our binary */
int rehashed = 1;
int zline_in_progress = 0; /* killing off matching D lines */
time_t nextconnect = 1; /* time for next try_connections call */
time_t nextping = 1; /* same as above for check_pings() */
time_t nextdnscheck = 0; /* next time to poll dns to force timeout */
time_t nextexpire = 1; /* next expire run on the dns cache */
time_t nextbanexpire = 1; /* next time to expire the throttles/userbans */
#ifdef TOYS
extern void init_chef();
#endif
#ifdef PROFILING
extern void _start, etext;
static int profiling_state = 1;
static int profiling_newmsg = 0;
static char profiling_msg[512];
void s_dumpprof()
{
char buf[32];
sprintf(buf, "gmon.%d", (int)time(NULL));
setenv("GMON_OUT_PREFIX", buf, 1);
_mcleanup();
monstartup ((u_long) &_start, (u_long) &etext);
setenv("GMON_OUT_PREFIX", "gmon.auto", 1);
sprintf(profiling_msg, "Reset profile, saved past profile data to %s", buf);
profiling_newmsg = 1;
}
void s_toggleprof()
{
char buf[32];
if(profiling_state == 1)
{
sprintf(buf, "gmon.%d", (int)time(NULL));
setenv("GMON_OUT_PREFIX", buf, 1);
_mcleanup();
sprintf(profiling_msg, "Turned profiling OFF, saved profile data to %s", buf);
profiling_state = 0;
}
else
{
monstartup ((u_long) &_start, (u_long) &etext);
setenv("GMON_OUT_PREFIX", "gmon.auto", 1);
profiling_state = 1;
sprintf(profiling_msg, "Turned profiling ON");
}
profiling_newmsg = 1;
}
#endif
void s_die()
{
FILE *fp;
char tmp[PATH_MAX];
dump_connections(me.fd);
#ifdef USE_SYSLOG
(void) syslog(LOG_CRIT, "Server killed By SIGTERM");
#endif
ircsprintf(tmp, "%s/.maxclients", dpath);
fp=fopen(tmp, "w");
if(fp!=NULL)
{
fprintf(fp, "%d %d %li %li %li %ld %ld %ld %ld", Count.max_loc,
Count.max_tot, Count.weekly, Count.monthly,
Count.yearly, Count.start, Count.week, Count.month,
Count.year);
fclose(fp);
}
exit(0);
}
static void s_rehash()
{
struct sigaction act;
dorehash = 1;
act.sa_handler = s_rehash;
act.sa_flags = 0;
(void) sigemptyset(&act.sa_mask);
(void) sigaddset(&act.sa_mask, SIGHUP);
(void) sigaction(SIGHUP, &act, NULL);
}
void restart(char *mesg)
{
static int was_here = NO; /* redundant due to restarting flag below */
if (was_here)
abort();
was_here = YES;
#ifdef USE_SYSLOG
(void) syslog(LOG_WARNING, "Restarting Server because: %s, sbrk(0)-etext: %d",
mesg, (u_int) sbrk((size_t) 0) - (u_int) sbrk0);
#endif
server_reboot();
}
void s_restart()
{
static int restarting = 0;
#ifdef USE_SYSLOG
(void) syslog(LOG_WARNING, "Server Restarting on SIGINT");
#endif
if (restarting == 0)
{
/* Send (or attempt to) a dying scream to oper if present */
restarting = 1;
server_reboot();
}
}
void server_reboot()
{
int i;
sendto_ops("Aieeeee!!! Restarting server... sbrk(0)-etext: %d",
(u_int) sbrk((size_t) 0) - (u_int) sbrk0);
Debug((DEBUG_NOTICE, "Restarting server..."));
dump_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
for (i = 3; i < MAXCONNECTIONS; i++)
(void) close(i);
if (!(bootopt & (BOOT_TTY | BOOT_DEBUG)))
(void) close(2);
(void) close(1);
if (!(bootopt & BOOT_OPER))
(void) execv(spath, myargv);
#ifdef USE_SYSLOG
/* Have to reopen since it has been closed above */
openlog(myargv[0], LOG_PID | LOG_NDELAY, LOG_FACILITY);
syslog(LOG_CRIT, "execv(%s,%s) failed: %m\n", spath, 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(time_t currenttime)
{
aConnect *aconn, **pconn, *con_conn = (aConnect *) NULL;
aClient *cptr;
aClass *cltmp;
int connecting, confrq;
time_t next = 0;
connecting = FALSE;
Debug((DEBUG_NOTICE, "Connection check at : %s",
myctime(currenttime)));
for (aconn = connects; aconn; aconn = aconn->next)
{
/* Also when already connecting! (update holdtimes) --SRB */
if (aconn->port <= 0 || aconn->class->connfreq == 0)
continue;
cltmp = aconn->class;
/*
* * 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 ((aconn->hold > currenttime))
{
if ((next > aconn->hold) || (next == 0))
next = aconn->hold;
continue;
}
confrq = cltmp->connfreq;
aconn->hold = currenttime + confrq;
/* Found a CONNECT config with port specified, scan clients
* and see if this server is already connected?
*/
cptr = find_name(aconn->name, (aClient *) NULL);
if (!cptr && (cltmp->links < cltmp->maxlinks) && !connecting)
{
con_conn = aconn;
/* We connect only one at time... */
connecting = TRUE;
}
if ((next > aconn->hold) || (next == 0))
next = aconn->hold;
}
if (connecting && (!server_list || confopts & FLAGS_HUB))
{
if (con_conn->next) /* are we already last? */
{
for (pconn = &connects; (aconn = *pconn);
pconn = &(aconn->next))
/*
* put the current one at the end and make sure we try all
* connections
*/
if (aconn == con_conn)
*pconn = aconn->next;
(*pconn = con_conn)->next = 0;
}
if (connect_server(con_conn, (aClient *) NULL,
(struct hostent *) NULL) == 0)
sendto_gnotice("from %s: Connection to %s activated.", me.name,
con_conn->name);
}
Debug((DEBUG_NOTICE, "Next connection check : %s", myctime(next)));
return (next);
}
/* dianora's code in the new checkpings is slightly wasteful.
* however, upon inspection (thanks seddy), when we close a connection,
* the ordering of local[i] is NOT reordered; simply local[highest_fd] becomes
* local[i], so we can just i--; - lucas
*/
static time_t check_pings(time_t currenttime)
{
aClient *cptr;
int ping = 0, i;
time_t oldest = 0; /* timeout removed, see EXPLANATION below */
char fbuf[512], *errtxt = "No response from %s, closing link";
for (i = 0; i <= highest_fd; i++)
{
if (!(cptr = local[i]) || IsMe(cptr) || IsLog(cptr))
continue;
/* Note: No need to notify opers here. It's
* already done when "FLAGS_DEADSOCKET" is set.
*/
if (cptr->flags & FLAGS_DEADSOCKET)
{
exit_client(cptr, cptr, &me, (cptr->flags & FLAGS_SENDQEX) ?
"SendQ exceeded" : "Dead socket");
continue;
}
if (IsRegistered(cptr))
ping = cptr->class->pingfreq;
else
ping = CONNECTTIMEOUT;
/*
* Ok, so goto's are ugly and can be avoided here but this code
* is already indented enough so I think its justified. -avalon
*
* justified by what? laziness? <g>
* If the client pingtime is fine (ie, not larger than the client ping)
* skip over all the checks below. - lucas
*/
if (ping < (currenttime - cptr->lasttime))
{
/*
* 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 (((cptr->flags & FLAGS_PINGSENT) &&
((currenttime - cptr->lasttime) >= (2 * ping))) ||
((!IsRegistered(cptr) &&
(currenttime - cptr->since) >= ping)))
{
if (!IsRegistered(cptr) && (DoingDNS(cptr) ||
DoingAuth(cptr)))
{
if (cptr->authfd >= 0)
{
del_fd(cptr->authfd);
close(cptr->authfd);
cptr->authfd = -1;
cptr->count = 0;
*cptr->buffer = '\0';
}
#ifdef SHOW_HEADERS
if (DoingDNS(cptr))
sendto_one(cptr, REPORT_FAIL_DNS);
if (DoingAuth(cptr))
sendto_one(cptr, REPORT_FAIL_ID);
#endif
Debug((DEBUG_NOTICE, "DNS/AUTH timeout %s",
get_client_name(cptr, TRUE)));
del_queries((char *) cptr);
ClearAuth(cptr);
ClearDNS(cptr);
cptr->since = currenttime;
check_client_fd(cptr);
continue;
}
if (IsServer(cptr) || IsConnecting(cptr) || IsHandshake(cptr))
{
ircsprintf(fbuf, "from %s: %s", me.name, errtxt);
sendto_gnotice(fbuf, get_client_name(cptr, HIDEME));
ircsprintf(fbuf, ":%s GNOTICE :%s", me.name, errtxt);
sendto_serv_butone(cptr, fbuf,
get_client_name(cptr, HIDEME));
}
exit_client(cptr, cptr, &me, "Ping timeout");
continue;
} /* don't send pings during a burst, as we send them already. */
else if (!(cptr->flags & (FLAGS_PINGSENT|FLAGS_BURST)) &&
!(IsConnecting(cptr) || IsHandshake(cptr)))
{
/*
* 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);
}
}
/* see EXPLANATION below
*
* timeout = cptr->lasttime + ping;
* while (timeout <= currenttime)
* timeout += ping;
* if (timeout < oldest || !oldest)
* oldest = timeout;
*/
/*
* Check UNKNOWN connections - if they have been in this state
* for > 100s, close them.
*/
if (IsUnknown(cptr))
if (cptr->firsttime ? ((timeofday - cptr->firsttime) > 100) : 0)
(void) exit_client(cptr, cptr, &me, "Connection Timed Out");
}
rehashed = 0;
zline_in_progress = 0;
/* EXPLANATION
* on a server with a large volume of clients, at any given point
* there may be a client which needs to be pinged the next second,
* or even right away (a second may have passed while running
* check_pings). Preserving CPU time is more important than
* pinging clients out at exact times, IMO. Therefore, I am going to make
* check_pings always return currenttime + 9. This means that it may take
* a user up to 9 seconds more than pingfreq to timeout. Oh well.
* Plus, the number is 9 to 'stagger' our check_pings calls out over
* time, to avoid doing it and the other tasks ircd does at the same time
* all the time (which are usually done on intervals of 5 seconds or so).
* - lucas
*
* if (!oldest || oldest < currenttime)
* oldest = currenttime + PINGFREQUENCY;
*/
oldest = currenttime + 9;
Debug((DEBUG_NOTICE, "Next check_ping() call at: %s, %d %d %d",
myctime(oldest), ping, oldest, currenttime));
return oldest;
}
/* get_paths()
* setup our file paths
*/
void get_paths(char *argv)
{
char t_dpath[PATH_MAX], t_d2path[PATH_MAX], tmp[PATH_MAX],
tmp2[PATH_MAX];
int len, fd;
*t_dpath = 0;
*t_d2path = 0;
*tmp = 0;
*tmp2 = 0;
if(!*configfile)
{
getcwd(t_dpath, PATH_MAX); /* directory we're called from */
if(argv[0] == '/') /* absolute filename used to call */
strcat(spath, argv);
else
{
strcat(spath, t_dpath);
strcat(spath, "/");
strcat(spath, argv);
}
strcat(tmp, t_dpath);
strcat(tmp, "/ircd.conf");
if((fd = open(tmp, O_RDONLY)) > 0)
{
/* found our ircd.conf in the directory
* where we were called from */
strcpy(configfile, tmp);
close(fd);
strcpy(dpath, t_dpath);
return;
}
len = strlen(spath);
while(spath[len] != '/')
len--;
strncat(t_d2path, spath, len);
strcat(tmp2, t_d2path);
strcat(tmp2, "/ircd.conf");
if((fd = open(tmp2, O_RDONLY)) > 0)
{
/* found the ircd.conf in the directory local
* to our binary itself */
strcpy(configfile, tmp);
close(fd);
strcpy(dpath, t_d2path);
return;
}
}
else
{
getcwd(t_dpath, PATH_MAX); /* directory we're called from */
if(argv[0] == '/') /* absolute filename used to call */
strcat(spath, argv);
else
{
strcat(spath, t_dpath);
strcat(spath, "/");
strcat(spath, argv);
}
if(configfile[0] == '/') /* absolute filename in configfile */
{
len = strlen(configfile);
while(configfile[len] != '/')
len--;
strncat(dpath, configfile, len);
}
else
{
strcat(dpath, t_dpath);
strcat(dpath, "/");
if(strchr(configfile, '/'))
{
len = strlen(configfile);
while(configfile[len] != '/')
len--;
strncat(dpath, configfile, len);
}
}
}
printf("CONFIGFILE: %s\n", configfile);
}
/*
* bad_command
* This is called when the commandline is not acceptable.
* Give error message and exit without starting anything.
*/
static int bad_command()
{
printf("Usage: ircd ");
#ifdef CMDLINE_CONFIG
printf("[-f configfile] ");
#endif
printf("[-t] [-v]\n");
printf("-t will cause ircd not to fork (mostly for debugging)\n");
printf("-v will cause ircd to print its version and quit\n");
printf("Server Not Started\n");
return (-1);
}
#ifndef TRUE
#define TRUE 1
#endif
/* ripped this out of hybrid7 out of lazyness. */
static void
setup_corefile()
{
#ifdef HAVE_SYS_RESOURCE_H
struct rlimit rlim; /* resource limits */
/* Set corefilesize to maximum */
if (!getrlimit(RLIMIT_CORE, &rlim))
{
rlim.rlim_cur = rlim.rlim_max;
setrlimit(RLIMIT_CORE, &rlim);
}
#endif
}
char REPORT_DO_DNS[256], REPORT_FIN_DNS[256], REPORT_FIN_DNSC[256],
REPORT_FAIL_DNS[256], REPORT_DO_ID[256], REPORT_FIN_ID[256],
REPORT_FAIL_ID[256];
FILE *dumpfp=NULL;
int
main(int argc, char *argv[])
{
uid_t uid, euid;
char tmp[PATH_MAX];
FILE *mcsfp;
char *conferr;
if ((timeofday = time(NULL)) == -1)
{
(void) fprintf(stderr, "ERROR: Clock Failure (%d)\n", errno);
exit(errno);
}
build_version();
printf("\n%s booting...\n", version);
printf("Security related issues should be sent to ircd@solid-ircd.com\n");
printf("All other issues should be sent to bugtracker\n\n");
setup_corefile();
Count.server = 1; /* us */
Count.oper = 0;
Count.chan = 0;
Count.local = 0;
Count.total = 0;
Count.invisi = 0;
Count.unknown = 0;
Count.max_loc = 0;
Count.max_tot = 0;
Count.today = 0;
Count.weekly = 0;
Count.monthly = 0;
Count.yearly = 0;
Count.start = NOW;
Count.day = NOW;
Count.week = NOW;
Count.month = NOW;
Count.year = NOW;
/*
* this code by mika@cs.caltech.edu
* it is intended to keep the ircd from being swapped out. BSD
* swapping criteria do not match the requirements of ircd
*/
#if defined(INITIAL_SBUFS_LARGE) && defined(INITIAL_SBUFS_SMALL)
sbuf_init();
#endif
sbrk0 = (char *) sbrk((size_t) 0);
uid = getuid();
euid = geteuid();
#ifdef PROFILING
setenv("GMON_OUT_PREFIX", "gmon.out", 1);
(void) signal(SIGUSR1, s_dumpprof);
(void) signal(SIGUSR2, s_toggleprof);
#endif
myargv = argv;
(void) umask(077); /* better safe than sorry --SRB */
memset((char *) &me, '\0', sizeof(me));
setup_signals();
/*
* * 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)
{
#ifdef CMDLINE_CONFIG
case 'f':
(void) setuid((uid_t) uid);
strcpy(configfile, p);
break;
#endif
case 's':
bootopt |= BOOT_STDERR;
break;
case 't':
(void) setuid((uid_t) uid);
bootopt |= BOOT_TTY;
break;
case 'v':
(void) printf("%s\n", version);
exit(0);
case 'x':
#ifdef DEBUGMODE
(void) setuid((uid_t) uid);
debuglevel = atoi(p);
debugmode = *p ? p : "0";
bootopt |= BOOT_DEBUG;
break;
#else
bad_command();
break;
#endif
default:
bad_command();
break;
}
}
get_paths(myargv[0]);
if(chdir(dpath))
{
printf("Error changing directory to ircd.conf location\n");
printf("Server not started\n");
exit(0);
}
ircsprintf(tmp, "%s/.maxclients", dpath);
mcsfp = fopen(tmp, "r");
if(mcsfp != NULL)
{
fscanf(mcsfp, "%d %d %li %li %li %ld %ld %ld %ld", &Count.max_loc,
&Count.max_tot, &Count.weekly, &Count.monthly, &Count.yearly,
&Count.start, &Count.week, &Count.month, &Count.year);
fclose(mcsfp);
}
if ((uid != euid) && !euid)
{
printf("Do not run ircd as root.\nAborting...\n");
exit(-1);
}
if (argc > 0)
return bad_command(); /* This should exit out */
init_globals();
#ifdef HAVE_ENCRYPTION_ON
printf("Initializing Encryption...");
if(dh_init() == -1)
{
printf("\n\nEncryption Init failed!\n\n");
return 0;
}
#endif
motd = (aMotd *) NULL;
helpfile = (aMotd *) NULL;
shortmotd = NULL;
clear_client_hash_table();
clear_channel_hash_table();
clear_scache_hash_table(); /* server cache name table */
/* init the throttle system -wd */
throttle_init();
/* clone tracking and limiting */
clones_init();
/* init the file descriptor tracking system */
init_fds();
/* init the kline/akill system */
init_userban();
initlists();
initwhowas();
initstats();
init_tree_parse(msgtab);
init_send();
open_debugfile();
NOW = time(NULL);
initclass();
#ifdef TOYS
init_chef(); /* siwwy wabbit, ewmew wont wowk widout initiawizing it fiwst! - tux */
#endif
if(initconf(configfile) == -1)
{
printf("Server not started\n");
exit(-1);
}
conferr = finishconf();
if (conferr)
{
printf("ERROR: %s in config file\nServer not started\n", conferr);
exit(-1);
}
merge_confs();
build_rplcache();
read_motd(MOTD);
read_help(HELPFILE);
if(confopts & FLAGS_SMOTD)
read_shortmotd(SHORTMOTD);
printf("Configuration Loaded.\n");
init_fdlist(&default_fdlist);
{
int i;
for (i = MAXCONNECTIONS + 1; i > 0; i--)
{
default_fdlist.entry[i] = i - 1;
}
}
/* init the modules, load default modules! */
init_modules();
#ifdef HAVE_SSL
init_ssl();
#endif
me.flags = FLAGS_LISTEN;
me.fd = -1;
/* We don't want to calculate these every time they are used :) */
sprintf(REPORT_DO_DNS, REPORT_DO_DNS_, me.name);
sprintf(REPORT_FIN_DNS, REPORT_FIN_DNS_, me.name);
sprintf(REPORT_FIN_DNSC, REPORT_FIN_DNSC_, me.name);
sprintf(REPORT_FAIL_DNS, REPORT_FAIL_DNS_, me.name);
sprintf(REPORT_DO_ID, REPORT_DO_ID_, me.name);
sprintf(REPORT_FIN_ID, REPORT_FIN_ID_, me.name);
sprintf(REPORT_FAIL_ID, REPORT_FAIL_ID_, me.name);
R_do_dns = strlen(REPORT_DO_DNS);
R_fin_dns = strlen(REPORT_FIN_DNS);
R_fin_dnsc = strlen(REPORT_FIN_DNSC);
R_fail_dns = strlen(REPORT_FAIL_DNS);
R_do_id = strlen(REPORT_DO_ID);
R_fin_id = strlen(REPORT_FIN_ID);
R_fail_id = strlen(REPORT_FAIL_ID);
NOW = time(NULL);
init_sys();
forked = 1;
#ifdef USE_SYSLOG
# define SYSLOG_ME "ircd"
openlog(SYSLOG_ME, LOG_PID | LOG_NDELAY, LOG_FACILITY);
#endif
/* the pid file must be written *AFTER* the fork */
write_pidfile();
/* this should be sooner, but the fork/detach stuff is so brain-dead... */
klinestore_init(0);
/* moved this to here such that we allow more verbose error
* checking on startup. -epi
*/
open_listeners();
get_my_name(&me, me.sockhost, sizeof(me.sockhost) - 1);
if (me.name[0] == '\0')
strncpyzt(me.name, me.sockhost, sizeof(me.name));
me.hopcount = 0;
me.authfd = -1;
me.next = NULL;
me.user = NULL;
me.from = &me;
SetMe(&me);
make_server(&me);
me.serv->up = me.name;
me.lasttime = me.since = me.firsttime = NOW;
(void) add_to_client_hash_table(me.name, &me);
#ifdef DUMP_DEBUG
dumpfp=fopen("dump.log", "w");
#endif
#ifdef USE_SYSLOG
syslog(LOG_NOTICE, "Server Ready");
#endif
io_loop();
return 0;
}
void do_recvqs()
{
DLink *lp, *lpn;
aClient *cptr;
for(lp = recvq_clients; lp; lp = lpn)
{
lpn = lp->next;
cptr = lp->value.cptr;
/* dlink is tagged for deletion, cptr is already gone */
if (lp->flags == -1)
{
remove_from_list(&recvq_clients, NULL, lp);
continue;
}
if(SBufLength(&cptr->recvQ) && !NoNewLine(cptr))
{
if(do_client_queue(cptr) == FLUSH_BUFFER)
{
remove_from_list(&recvq_clients, NULL, lp);
continue;
}
}
if(!(SBufLength(&cptr->recvQ) && !NoNewLine(cptr)))
{
remove_from_list(&recvq_clients, cptr, lp);
cptr->flags &= ~(FLAGS_HAVERECVQ);
}
}
}
void send_safelists()
{
DLink *lp, *lpn;
aClient *cptr;
for(lp = listing_clients; lp; lp = lpn)
{
lpn = lp->next;
cptr = lp->value.cptr;
while(DoList(cptr) && IsSendable(cptr))
send_list(cptr, 64);
}
}
void io_loop()
{
char to_send[200];
int lastexp=0;
time_t next10sec = 0; /* For events we do every 10 seconds */
time_t lastbwcalc = 0;
long lastbwSK = 0, lastbwRK = 0;
time_t lasttimeofday;
int delay = 0;
while(1)
{
lasttimeofday = timeofday;
if ((timeofday = time(NULL)) == -1)
{
#ifdef USE_SYSLOG
syslog(LOG_WARNING, "Clock Failure (%d), TS can be corrupted",
errno);
#endif
sendto_ops("Clock Failure (%d), TS can be corrupted", errno);
}
if (timeofday < lasttimeofday)
{
ircsprintf(to_send, "System clock running backwards - (%d < %d)",
timeofday, lasttimeofday);
report_error(to_send, &me);
}
NOW = timeofday;
/*
* Calculate a moving average of our total traffic.
* Traffic is a 4 second average, 'sampled' every 2 seconds.
*/
if((timeofday - lastbwcalc) >= 2)
{
long ilength = timeofday - lastbwcalc;
curSendK += (float) (me.sendK - lastbwSK) / (float) ilength;
curRecvK += (float) (me.receiveK - lastbwRK) / (float) ilength;
curSendK /= 2;
curRecvK /= 2;
lastbwSK = me.sendK;
lastbwRK = me.receiveK;
lastbwcalc = timeofday;
}
/*
* 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);
/* 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);
if (timeofday >= nextbanexpire)
{
/*
* magic number: 13 seconds
* space out these heavy tasks at semi-random intervals, so as not to coincide
* with anything else ircd does regularly
*/
nextbanexpire = NOW + 13;
if(lastexp == 0)
{
expire_userbans();
lastexp++;
}
else if(lastexp == 1)
{
expire_simbans();
lastexp++;
}
else
{
throttle_timer(NOW);
lastexp = 0;
}
}
if (timeofday >= next10sec)
{
next10sec = timeofday + 10;
call_hooks(CHOOK_10SEC);
}
/*
* 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;
/*
* Parse people who have blocked recvqs
*/
do_recvqs();
/*
* Send people their /list replies, being careful
* not to fill their sendQ
*/
send_safelists();
/*
* 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
{
/* We need to get back here to do that recvq thing */
if(recvq_clients != NULL)
delay = 1;
else
delay = MIN(delay, TIMESEC);
}
engine_read_message(delay); /* check everything! */
/*
* * ...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);
#ifdef PROFILING
if (profiling_newmsg)
{
sendto_realops("PROFILING: %s", profiling_msg);
profiling_newmsg = 0;
}
#endif
if (dorehash)
{
(void) rehash(&me, &me, 1);
(void) read_motd(MOTD);
dorehash = 0;
}
/*
*
* Flush output buffers on all connections now if they
* have data in them (or at least try to flush) -avalon
*
* flush_connections(me.fd);
*
* avalon, what kind of crack have you been smoking? why
* on earth would we flush_connections blindly when
* we already check to see if we can write (and do)
* in read_message? There is no point, as this causes
* lots and lots of unnecessary sendto's which
* 99% of the time will fail because if we couldn't
* empty them in read_message we can't empty them here.
* one effect: during htm, output to normal lusers
* will lag.
* htm doesnt exist anymore, but this comment was funny, so i
* left it in. -epi
*/
/* Now we've made this call a bit smarter. */
/* Only flush non-blocked sockets. */
flush_connections(me.fd);
#ifdef LOCKFILE
/*
* * If we have pending klines and CHECK_PENDING_KLINES minutes
* have passed, try writing them out. -ThemBones
*/
if ((pending_klines) && ((timeofday - pending_kline_time)
>= (CHECK_PENDING_KLINES * 60)))
do_pending_klines();
#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 LPATH in
* config.h 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 dont
* waste the fd.
*/
static void open_debugfile()
{
#ifdef DEBUGMODE
int fd;
aClient *cptr;
if (debuglevel >= 0)
{
cptr = make_client(NULL, NULL);
cptr->fd = 2;
SetLog(cptr);
cptr->port = debuglevel;
cptr->flags = 0;
/*XXX 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 */
{
(void) truncate(LOGFILE, 0);
if ((fd = open(LOGFILE, 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, LOGFILE, 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;
}
static void setup_signals()
{
struct sigaction act;
act.sa_handler = SIG_IGN;
act.sa_flags = 0;
(void) sigemptyset(&act.sa_mask);
(void) sigaddset(&act.sa_mask, SIGPIPE);
(void) sigaddset(&act.sa_mask, SIGALRM);
# ifdef SIGWINCH
(void) sigaddset(&act.sa_mask, SIGWINCH);
(void) sigaction(SIGWINCH, &act, NULL);
# endif
(void) sigaction(SIGPIPE, &act, NULL);
act.sa_handler = dummy;
(void) sigaction(SIGALRM, &act, NULL);
act.sa_handler = s_rehash;
(void) sigemptyset(&act.sa_mask);
(void) sigaddset(&act.sa_mask, SIGHUP);
(void) sigaction(SIGHUP, &act, NULL);
act.sa_handler = s_restart;
(void) sigaddset(&act.sa_mask, SIGINT);
(void) sigaction(SIGINT, &act, NULL);
act.sa_handler = s_die;
(void) sigaddset(&act.sa_mask, SIGTERM);
(void) sigaction(SIGTERM, &act, NULL);
#ifdef RESTARTING_SYSTEMCALLS
/*
* * At least on Apollo sr10.1 it seems continuing system calls
* after signal is the default. The following 'siginterrupt'
* should change that default to interrupting calls.
*/
(void) siginterrupt(SIGALRM, 1);
#endif
}
void build_version(void)
{
char *s=PATCHES;
ircsprintf(version, "%s-%.1d.%.1d(%.2d)%s", BASENAME,
MAJOR, MINOR, PATCH, (*s != 0 ? PATCHES : ""));
}
u_long
memcount_ircd(MCircd *mc)
{
mc->file = __FILE__;
mc->s_confbuf.c++;
mc->s_confbuf.m += sizeof(ProxyMonURL);
mc->s_confbuf.c++;
mc->s_confbuf.m += sizeof(ProxyMonHost);
mc->s_confbuf.c++;
mc->s_confbuf.m += sizeof(Network_Name);
mc->s_confbuf.c++;
mc->s_confbuf.m += sizeof(Services_Name);
mc->s_confbuf.c++;
mc->s_confbuf.m += sizeof(Stats_Name);
mc->s_confbuf.c++;
mc->s_confbuf.m += sizeof(NS_Register_URL);
mc->s_confbuf.c++;
mc->s_confbuf.m += sizeof(Network_Kline_Address);
mc->s_confbuf.c++;
mc->s_confbuf.m += sizeof(Local_Kline_Address);
mc->s_confbuf.c++;
mc->s_confbuf.m += sizeof(Staff_Address);
mc->s_confbuf.c++;
mc->s_confbuf.m += sizeof(configfile);
mc->s_confbuf.c++;
mc->s_confbuf.m += sizeof(dpath);
mc->s_confbuf.c++;
mc->s_confbuf.m += sizeof(spath);
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1