diff options
Diffstat (limited to 'ircd/ircd.c')
-rw-r--r-- | ircd/ircd.c | 1287 |
1 files changed, 1287 insertions, 0 deletions
diff --git a/ircd/ircd.c b/ircd/ircd.c new file mode 100644 index 0000000..72bbf95 --- /dev/null +++ b/ircd/ircd.c @@ -0,0 +1,1287 @@ +/************************************************************************ + * 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.62 1999/08/13 17:17:42 kalt Exp $"; +#endif + +#include "os.h" +#include "s_defines.h" +#define IRCD_C +#include "s_externs.h" +#undef IRCD_C + +aClient me; /* That's me */ +aClient *client = &me; /* Pointer to beginning of Client list */ + +static void open_debugfile(), setup_signals(), 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; +static 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 */ + +#ifdef PROFIL +extern etext(); + +RETSIGTYPE s_monitor(s) +int s; +{ + static int mon = 0; +#if POSIX_SIGNALS + struct sigaction act; +#endif + + (void)moncontrol(mon); + mon = 1 - mon; +#if POSIX_SIGNALS + act.sa_handler = s_rehash; + act.sa_flags = 0; + (void)sigemptyset(&act.sa_mask); + (void)sigaddset(&act.sa_mask, SIGUSR1); + (void)sigaction(SIGUSR1, &act, NULL); +#else + (void)signal(SIGUSR1, s_monitor); +#endif +} +#endif + +RETSIGTYPE s_die(s) +int s; +{ +#ifdef USE_SYSLOG + (void)syslog(LOG_CRIT, "Server Killed By SIGTERM"); +#endif + ircd_writetune(tunefile); + flush_connections(me.fd); + exit(-1); +} + +#if defined(USE_IAUTH) +RETSIGTYPE s_slave(s) +int s; +{ +# if POSIX_SIGNALS + struct sigaction act; + + act.sa_handler = s_slave; + act.sa_flags = 0; + (void)sigemptyset(&act.sa_mask); + (void)sigaddset(&act.sa_mask, SIGUSR1); + (void)sigaction(SIGUSR1, &act, NULL); +# else + (void)signal(SIGUSR1, s_slave); +# endif + restart_iauth = 1; +} +#endif + +static RETSIGTYPE s_rehash(s) +int s; +{ +#if POSIX_SIGNALS + struct sigaction act; + + 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); +#else + (void)signal(SIGHUP, s_rehash); /* sysV -argv */ +#endif + dorehash = 1; +} + +void restart(mesg) +char *mesg; +{ +#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)); + server_reboot(); +} + +RETSIGTYPE s_restart(s) +int s; +{ +#if POSIX_SIGNALS + struct sigaction act; + + act.sa_handler = s_restart; + act.sa_flags = 0; + (void)sigemptyset(&act.sa_mask); + (void)sigaddset(&act.sa_mask, SIGINT); + (void)sigaction(SIGINT, &act, NULL); +#else + (void)signal(SIGHUP, SIG_DFL); /* sysV -argv */ +#endif + dorestart = 1; +} + +void server_reboot() +{ + Reg int i; + + sendto_flag(SCH_NOTICE, "Aieeeee!!! Restarting server... (%u)", + (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 + 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))) + { + (void)execv(IRCD_PATH, 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", 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->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 ((currenttime - lkill > 60) || rehashed) + { + if (IsPerson(cptr)) + { + kflag = find_kill(cptr, rehashed, &reason); +#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); +} + + +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; + 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(); +#ifdef PROFIL + (void)monstartup(0, etext); + (void)moncontrol(1); + (void)signal(SIGUSR1, s_monitor); +#endif + +#ifdef CHROOTDIR + ircd_res_init(); + if (chroot(ROOT_PATH)) + { + 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 */ + + /* + ** 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) + 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 + + 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); + } + + 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(); + +#ifdef USE_SYSLOG + openlog(myargv[0], LOG_PID|LOG_NDELAY, LOG_FACILITY); +#endif + timeofday = time(NULL); + if (initconf(bootopt) == -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) + exit(-1); + } + + dbuf_init(); + setup_me(&me); + 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); + /* + ** 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. + */ + sendto_flag(SCH_DEBUG, "read_message(RO) -> 0 [%d]", delay); + (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) + restart("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) + { + 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; +} + +static void setup_signals() +{ +#if POSIX_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); +# if defined(USE_IAUTH) + act.sa_handler = s_slave; + (void)sigaddset(&act.sa_mask, SIGUSR1); + (void)sigaction(SIGUSR1, &act, NULL); + act.sa_handler = SIG_IGN; +# ifdef SA_NOCLDWAIT + act.sa_flags = SA_NOCLDWAIT; +# else + act.sa_flags = 0; +# endif + (void)sigaddset(&act.sa_mask, SIGCHLD); + (void)sigaction(SIGCHLD, &act, NULL); +# endif + +#else /* POSIX_SIGNALS */ + +# ifndef HAVE_RELIABLE_SIGNALS + (void)signal(SIGPIPE, dummy); +# ifdef SIGWINCH + (void)signal(SIGWINCH, dummy); +# endif +# else /* HAVE_RELIABLE_SIGNALS */ +# ifdef SIGWINCH + (void)signal(SIGWINCH, SIG_IGN); +# endif + (void)signal(SIGPIPE, SIG_IGN); +# endif /* HAVE_RELIABLE_SIGNALS */ + (void)signal(SIGALRM, dummy); + (void)signal(SIGHUP, s_rehash); + (void)signal(SIGTERM, s_die); + (void)signal(SIGINT, s_restart); +# if defined(USE_IAUTH) + (void)signal(SIGUSR1, s_slave); + (void)signal(SIGCHLD, SIG_IGN); +# endif +#endif /* POSIX_SIGNAL */ + +#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 +} + +/* + * 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]; + + buf[0] = '\0'; + 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. + */ + if (lk_size < ww_size) + lk_size = ww_size; + close(fd); + } +} |