/************************************************************************ * 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 #include #include #include #include #include #include #include #if defined PROFILING && defined __GLIBC__ && (__GLIBC__ >= 2) #include #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? * 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; }