/************************************************************************ * 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 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"); 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); } }