"Fossies" - the Fresh Open Source Software Archive

Member "bahamut-2.1.5/src/ircd.c" (28 May 2020, 38872 Bytes) of package /linux/privat/bahamut-2.1.5.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "ircd.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 2.1.4_vs_2.1.5.

    1 /************************************************************************
    2  *   IRC - Internet Relay Chat, src/ircd.c
    3  *   Copyright (C) 1990 Jarkko Oikarinen and
    4  *                      University of Oulu, Computing Center
    5  *
    6  *   This program is free software; you can redistribute it and/or modify
    7  *   it under the terms of the GNU General Public License as published by
    8  *   the Free Software Foundation; either version 1, or (at your option)
    9  *   any later version.
   10  *
   11  *   This program is distributed in the hope that it will be useful,
   12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
   13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   14  *   GNU General Public License for more details.
   15  *
   16  *   You should have received a copy of the GNU General Public License
   17  *   along with this program; if not, write to the Free Software
   18  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
   19  */
   20 
   21 #include "struct.h"
   22 #include "common.h"
   23 #include "sys.h"
   24 #include "numeric.h"
   25 #include "msg.h"
   26 #include "sbuf.h"
   27 #include <sys/file.h>
   28 #include <sys/stat.h>
   29 #include <sys/types.h>
   30 #include <sys/resource.h>
   31 #include <sys/socket.h>
   32 #include <pwd.h>
   33 #include <signal.h>
   34 #include <fcntl.h>
   35 #if defined PROFILING && defined __GLIBC__ && (__GLIBC__ >= 2)
   36 #include <sys/gmon.h>
   37 #define monstartup __monstartup
   38 #endif
   39 #include "inet.h"
   40 #include "h.h"
   41 #include "patchlevel.h"
   42 #include "dh.h"
   43 
   44 #include "throttle.h"
   45 #include "userban.h"
   46 #include "clones.h"
   47 #include "hooks.h"
   48 #include "fds.h"
   49 #include "memcount.h"
   50 #include "spamfilter.h"
   51 
   52 aMotd      *motd;
   53 aMotd      *helpfile;           /* misnomer, aMotd could be generalized */
   54 aMotd      *shortmotd;          /* short motd */
   55 
   56 /* global conf options (from option block) */
   57 char ProxyMonURL[TOPICLEN+1];
   58 char ProxyMonHost[HOSTLEN+1];
   59 char Network_Name[HOSTLEN+1];
   60 char Services_Name[HOSTLEN+1];
   61 char Stats_Name[HOSTLEN+1];
   62 char NS_Register_URL[TOPICLEN+1];
   63 char SpamFilter_URL[TOPICLEN+1];
   64 char Network_Kline_Address[HOSTLEN+1];
   65 char Local_Kline_Address[HOSTLEN+1];
   66 char Staff_Address[HOSTLEN+1];
   67 int  maxchannelsperuser, tsmaxdelta, tswarndelta;
   68 int  confopts, new_confopts;
   69 int  local_ip_limit, local_ip24_limit, global_ip_limit, global_ip24_limit;
   70 
   71 /* this stuff by mnystrom@mit.edu */
   72 #include "fdlist.h"
   73 
   74 fdlist      default_fdlist;     /* just the number of the entry */
   75 
   76 int         MAXCLIENTS = MAX_ACTIVECONN;   /* runtime configurable by m_set */
   77 
   78 struct Counter Count;
   79 int         R_do_dns, R_fin_dns, R_fin_dnsc, R_fail_dns, R_do_id,
   80             R_fin_id, R_fail_id;
   81 
   82 time_t           NOW;
   83 time_t           last_stat_save;
   84 aClient          me;            /* That's me */
   85 aClient         *client = &me;  /* Pointer to beginning of Client list */
   86 
   87 int     forked = 0;
   88 
   89 float curSendK = 0, curRecvK = 0;
   90 
   91 extern void      engine_read_message(int);
   92 
   93 void            server_reboot();
   94 void            restart(char *);
   95 static void     open_debugfile(), setup_signals();
   96 static void     io_loop();
   97 
   98 /* externally needed functions */
   99 
  100 extern void     init_fdlist(fdlist *);      /* defined in fdlist.c */
  101 extern void     read_motd(char *);          /* defined in s_serv.c */
  102 extern void     read_shortmotd(char *);     /* defined in s_serv.c */
  103 extern void     read_help(char *);          /* defined in s_serv.c */
  104 extern void     init_globals();
  105 extern int      klinestore_init(int);    /* defined in klines.c */
  106 
  107 char        **myargv;
  108 char        configfile[PATH_MAX] = {0};     /* Server configuration file */
  109 
  110 int         debuglevel = -1;       /* Server debug level */
  111 int         bootopt = 0;           /* Server boot option flags */
  112 char        *debugmode = "";        /* -"-    -"-   -"-  */
  113 char        *sbrk0;                 /* initial sbrk(0) */
  114 static int  dorehash = 0;
  115 char        dpath[PATH_MAX] = {0};  /* our configure files live in here */
  116 char        spath[PATH_MAX] = {0};  /* the path to our binary */
  117 int         rehashed = 1;
  118 int         zline_in_progress = 0; /* killing off matching D lines */
  119 time_t      nextconnect = 1;       /* time for next try_connections call */
  120 time_t      nextping = 1;          /* same as above for check_pings() */
  121 time_t      nextdnscheck = 0;      /* next time to poll dns to force timeout */
  122 time_t      nextexpire = 1;        /* next expire run on the dns cache */
  123 time_t      nextbanexpire = 1;     /* next time to expire the throttles/userbans */
  124 
  125 #ifdef PROFILING
  126 extern void _start, etext;
  127 
  128 static int profiling_state = 1;
  129 static int profiling_newmsg = 0;
  130 static char profiling_msg[512];
  131 
  132 void s_dumpprof()
  133 {
  134     char buf[32];
  135 
  136     sprintf(buf, "gmon.%d", (int)time(NULL));
  137     setenv("GMON_OUT_PREFIX", buf, 1);
  138     _mcleanup();
  139     monstartup ((u_long) &_start, (u_long) &etext);
  140     setenv("GMON_OUT_PREFIX", "gmon.auto", 1);
  141     sprintf(profiling_msg, "Reset profile, saved past profile data to %s", buf);
  142     profiling_newmsg = 1;
  143 }
  144 
  145 void s_toggleprof()
  146 {
  147     char buf[32];
  148 
  149     if(profiling_state == 1)
  150     {
  151        sprintf(buf, "gmon.%d", (int)time(NULL));
  152        setenv("GMON_OUT_PREFIX", buf, 1);
  153        _mcleanup();
  154        sprintf(profiling_msg, "Turned profiling OFF, saved profile data to %s", buf);
  155        profiling_state = 0;
  156     }
  157     else
  158     {
  159        monstartup ((u_long) &_start, (u_long) &etext);
  160        setenv("GMON_OUT_PREFIX", "gmon.auto", 1);
  161        profiling_state = 1;
  162        sprintf(profiling_msg, "Turned profiling ON");
  163     } 
  164     profiling_newmsg = 1;
  165 }
  166 
  167 #endif
  168 
  169 static void build_version(void)
  170 {
  171     char *s=PATCHES;
  172     if(*s != 0)
  173         sprintf(version, "%s-%d.%d.%d-%s", BASENAME, MAJOR, MINOR, PATCH, PATCHES);
  174     else
  175         sprintf(version, "%s-%d.%d.%d", BASENAME, MAJOR, MINOR, PATCH);
  176 }
  177 
  178 void s_die() 
  179 {
  180     FILE *fp;
  181     char tmp[PATH_MAX];
  182     dump_connections(me.fd);
  183 #ifdef  USE_SYSLOG
  184     (void) syslog(LOG_CRIT, "Server killed By SIGTERM");
  185 #endif
  186     ircsprintf(tmp, "%s/.maxclients", dpath);
  187     fp=fopen(tmp, "w");
  188     if(fp!=NULL) 
  189     {
  190         fprintf(fp, "%d %d %li %li %li %ld %ld %ld %ld", Count.max_loc, 
  191                 Count.max_tot, Count.weekly, Count.monthly, 
  192                 Count.yearly, Count.start, Count.week, Count.month, 
  193                 Count.year);
  194         fclose(fp);
  195     }
  196     exit(0);
  197 }
  198 
  199 static  void s_rehash() 
  200 {
  201     struct sigaction act;
  202     dorehash = 1;
  203     act.sa_handler = s_rehash;
  204     act.sa_flags = 0;
  205     (void) sigemptyset(&act.sa_mask);
  206     (void) sigaddset(&act.sa_mask, SIGHUP);
  207     (void) sigaction(SIGHUP, &act, NULL);
  208 }
  209 
  210 void restart(char *mesg) 
  211 {
  212     static int  was_here = NO;  /* redundant due to restarting flag below */
  213     if (was_here)
  214         abort();
  215     was_here = YES;
  216         
  217 #ifdef  USE_SYSLOG
  218     (void) syslog(LOG_WARNING, "Restarting Server because: %s, sbrk(0)-etext: %lu",
  219                   mesg, (u_long) sbrk((size_t) 0) - (u_long) sbrk0);
  220 #endif
  221     server_reboot();
  222 }
  223 
  224 void s_restart() 
  225 {
  226     static int  restarting = 0;
  227         
  228 #ifdef  USE_SYSLOG
  229     (void) syslog(LOG_WARNING, "Server Restarting on SIGINT");
  230 #endif
  231     if (restarting == 0) 
  232     {
  233         /* Send (or attempt to) a dying scream to oper if present */
  234         restarting = 1;
  235         server_reboot();
  236     }
  237 }
  238 
  239 void server_reboot() 
  240 {
  241     int     i;
  242     sendto_ops("Aieeeee!!!  Restarting server... sbrk(0)-etext: %lu",
  243                (u_long) sbrk((size_t) 0) - (u_long) sbrk0);
  244         
  245     Debug((DEBUG_NOTICE, "Restarting server..."));
  246     dump_connections(me.fd);
  247     /*
  248      * fd 0 must be 'preserved' if either the -d or -i options have
  249      * been passed to us before restarting.
  250      */
  251 #ifdef USE_SYSLOG
  252     (void) closelog();
  253 #endif
  254     for (i = 3; i < MAXCONNECTIONS; i++)
  255         (void) close(i);
  256 
  257     if (!(bootopt & (BOOT_TTY | BOOT_DEBUG)))
  258         (void) close(2);
  259 
  260     (void) close(1);
  261 
  262     if (!(bootopt & BOOT_OPER))
  263         (void) execv(spath, myargv);
  264 
  265 #ifdef USE_SYSLOG
  266     /* Have to reopen since it has been closed above */
  267     openlog(myargv[0], LOG_PID | LOG_NDELAY, LOG_FACILITY);
  268     syslog(LOG_CRIT, "execv(%s,%s) failed: %m\n", spath, myargv[0]);
  269     closelog();
  270 #endif
  271 
  272     Debug((DEBUG_FATAL, "Couldn't restart server: %s", strerror(errno)));
  273     exit(-1);
  274 }
  275 
  276 /*
  277  * try_connections 
  278  * 
  279  *      Scan through configuration and try new connections. 
  280  *   Returns  the calendar time when the next call to this 
  281  *      function should be made latest. (No harm done if this 
  282  *      is called earlier or later...)
  283  */
  284 static time_t try_connections(time_t currenttime)
  285 {
  286     aConnect  *aconn, **pconn, *con_conn = (aConnect *) NULL;
  287     aClient   *cptr;
  288     aClass    *cltmp;
  289     int        connecting, confrq;
  290     time_t      next = 0;
  291 
  292     connecting = FALSE;
  293 
  294     Debug((DEBUG_NOTICE, "Connection check at   : %s",
  295            myctime(currenttime)));
  296 
  297     for (aconn = connects; aconn; aconn = aconn->next) 
  298     {
  299         /* Also when already connecting! (update holdtimes) --SRB */
  300         if (aconn->port <= 0 || aconn->class->connfreq == 0)
  301             continue;
  302         if (aconn->legal == -1)
  303             continue;
  304         cltmp = aconn->class;
  305 
  306         /*
  307          * * Skip this entry if the use of it is still on hold until 
  308          * future. Otherwise handle this entry (and set it on hold 
  309          * until next time). Will reset only hold times, if already 
  310          * made one successfull connection... [this algorithm is a bit
  311          * fuzzy... -- msa >;) ]
  312          */
  313 
  314         if ((aconn->hold > currenttime)) 
  315         {
  316             if ((next > aconn->hold) || (next == 0))
  317                 next = aconn->hold;
  318             continue;
  319         }
  320 
  321         confrq = cltmp->connfreq;
  322         aconn->hold = currenttime + confrq;
  323 
  324         /* Found a CONNECT config with port specified, scan clients 
  325          * and see if this server is already connected?
  326          */
  327 
  328         cptr = find_name(aconn->name, (aClient *) NULL);
  329 
  330         if (!cptr && (cltmp->links < cltmp->maxlinks) && !connecting) 
  331         {
  332             con_conn = aconn;
  333             /* We connect only one at time... */
  334             connecting = TRUE;
  335         }
  336 
  337         if ((next > aconn->hold) || (next == 0))
  338             next = aconn->hold;
  339     }
  340 
  341     if (connecting && (!server_list || confopts & FLAGS_HUB)) 
  342     {
  343         if (con_conn->next)     /* are we already last? */
  344         {
  345             for (pconn = &connects; (aconn = *pconn);
  346                  pconn = &(aconn->next))
  347                 /*
  348                  * put the current one at the end and make sure we try all
  349                  * connections
  350                  */
  351                 if (aconn == con_conn)
  352                     *pconn = aconn->next;
  353             (*pconn = con_conn)->next = 0;
  354         }
  355         if (connect_server(con_conn, (aClient *) NULL,
  356                            (struct hostent *) NULL) == 0)
  357             sendto_gnotice("from %s: Connection to %s activated.", me.name,
  358                            con_conn->name);
  359     }
  360     Debug((DEBUG_NOTICE, "Next connection check : %s", myctime(next)));
  361     return (next);
  362 }
  363 
  364 /* dianora's code in the new checkpings is slightly wasteful.
  365  * however, upon inspection (thanks seddy), when we close a connection,
  366  * the ordering of local[i] is NOT reordered; simply local[highest_fd] becomes
  367  * local[i], so we can just i--;  - lucas
  368  */
  369 
  370 static time_t check_pings(time_t currenttime)
  371 {
  372     aClient     *cptr;
  373     int          ping = 0, i;
  374     time_t       oldest = 0; /* timeout removed, see EXPLANATION below */
  375     char         fbuf[512], *errtxt = "No response from %s, closing link";
  376 
  377 
  378     for (i = 0; i <= highest_fd; i++) 
  379     {
  380         if (!(cptr = local[i]) || IsMe(cptr) || IsLog(cptr))
  381             continue;
  382 
  383         /* Note: No need to notify opers here. It's 
  384          * already done when "FLAGS_DEADSOCKET" is set.
  385          */
  386 
  387         if (cptr->flags & FLAGS_DEADSOCKET) 
  388         {
  389             exit_client(cptr, cptr, &me, (cptr->flags & FLAGS_SENDQEX) ?
  390                         "SendQ exceeded" : "Dead socket");
  391             continue;
  392         }
  393 
  394         if (IsRegistered(cptr))
  395             ping = cptr->class->pingfreq;
  396         else
  397             ping = CONNECTTIMEOUT;
  398 
  399         /*
  400          * Ok, so goto's are ugly and can be avoided here but this code
  401          * is already indented enough so I think its justified. -avalon
  402          *
  403          * justified by what? laziness? <g>
  404          * If the client pingtime is fine (ie, not larger than the client ping) 
  405          * skip over all the checks below. - lucas
  406          */
  407         
  408         if (ping < (currenttime - cptr->lasttime))
  409         {
  410             /*
  411              * If the server hasnt talked to us in 2*ping seconds and it has
  412              * a ping time, then close its connection. If the client is a
  413              * user and a KILL line was found to be active, close this
  414              * connection too.
  415              */
  416             if (((cptr->flags & FLAGS_PINGSENT) &&
  417                  ((currenttime - cptr->lasttime) >= (2 * ping))) ||
  418                 ((!IsRegistered(cptr) && 
  419                   (currenttime - cptr->since) >= ping))) 
  420             {
  421                 if (!IsRegistered(cptr) && (DoingDNS(cptr) || 
  422                                             DoingAuth(cptr))) 
  423                 {
  424                     if (cptr->authfd >= 0) 
  425                     {
  426                         del_fd(cptr->authfd);
  427                         close(cptr->authfd);
  428                         cptr->authfd = -1;
  429                         cptr->count = 0;
  430                         *cptr->buffer = '\0';
  431                     }
  432 #ifdef SHOW_HEADERS
  433                     if (DoingDNS(cptr))
  434                         sendto_one(cptr, "%s", REPORT_FAIL_DNS);
  435                     if (DoingAuth(cptr))
  436                         sendto_one(cptr, "%s", REPORT_FAIL_ID);
  437 #endif
  438                     Debug((DEBUG_NOTICE, "DNS/AUTH timeout %s",
  439                            get_client_name(cptr, TRUE)));
  440                     del_queries((char *) cptr);
  441                     ClearAuth(cptr);
  442                     ClearDNS(cptr);
  443                     cptr->since = currenttime;
  444                     check_client_fd(cptr);
  445                     continue;
  446                 }
  447                 
  448                 if (IsServer(cptr) || IsConnecting(cptr) || IsHandshake(cptr)) 
  449                 {
  450                     ircsprintf(fbuf, "from %s: %s", me.name, errtxt);
  451                     sendto_gnotice(fbuf, get_client_name(cptr, HIDEME));
  452                     ircsprintf(fbuf, ":%s GNOTICE :%s", me.name, errtxt);
  453                     sendto_serv_butone(cptr, fbuf, 
  454                                        get_client_name(cptr, HIDEME));
  455                 }
  456                 
  457                 exit_client(cptr, cptr, &me, "Ping timeout");
  458                 continue;
  459             } /* don't send pings during a burst, as we send them already. */
  460             else if (!(cptr->flags & (FLAGS_PINGSENT|FLAGS_BURST)) && 
  461                      !(IsConnecting(cptr) || IsHandshake(cptr))) 
  462             {
  463                 /*
  464                  * if we havent PINGed the connection and we havent heard from
  465                  * it in a while, PING it to make sure it is still alive.
  466                  */
  467                 cptr->flags |= FLAGS_PINGSENT;
  468                 /* not nice but does the job */
  469                 cptr->lasttime = currenttime - ping;
  470                 sendto_one(cptr, "PING :%s", me.name);
  471             }
  472         }
  473         
  474         /* see EXPLANATION below
  475          *
  476          * timeout = cptr->lasttime + ping;
  477          * while (timeout <= currenttime)
  478          *  timeout += ping;
  479          * if (timeout < oldest || !oldest)
  480          *   oldest = timeout;
  481          */
  482 
  483         /*
  484          * Check UNKNOWN connections - if they have been in this state
  485          * for > 100s, close them.
  486          */
  487         if (IsUnknown(cptr))
  488             if (cptr->firsttime ? ((timeofday - cptr->firsttime) > 100) : 0) 
  489                 (void) exit_client(cptr, cptr, &me, "Connection Timed Out");
  490     }
  491     
  492     rehashed = 0;
  493     zline_in_progress = 0;
  494     
  495     /* EXPLANATION
  496      * on a server with a large volume of clients, at any given point
  497      * there may be a client which needs to be pinged the next second,
  498      * or even right away (a second may have passed while running
  499      * check_pings). Preserving CPU time is more important than
  500      * pinging clients out at exact times, IMO. Therefore, I am going to make
  501      * check_pings always return currenttime + 9. This means that it may take
  502      * a user up to 9 seconds more than pingfreq to timeout. Oh well.
  503      * Plus, the number is 9 to 'stagger' our check_pings calls out over
  504      * time, to avoid doing it and the other tasks ircd does at the same time
  505      * all the time (which are usually done on intervals of 5 seconds or so). 
  506      * - lucas
  507      *
  508      *  if (!oldest || oldest < currenttime)
  509      *     oldest = currenttime + PINGFREQUENCY;
  510      */
  511 
  512     oldest = currenttime + 9;
  513 
  514     Debug((DEBUG_NOTICE, "Next check_ping() call at: %s, %d %d %d",
  515            myctime(oldest), ping, oldest, currenttime));
  516 
  517     return oldest;
  518 }
  519 
  520 /* get_paths()
  521  * setup our file paths
  522  */
  523 
  524 void get_paths(char *argv)
  525 {
  526     char        t_dpath[PATH_MAX], t_d2path[PATH_MAX], tmp[PATH_MAX],
  527                 tmp2[PATH_MAX];
  528     int len, fd;
  529     
  530     *t_dpath = 0;
  531     *t_d2path = 0;
  532     *tmp = 0;
  533     *tmp2 = 0;
  534 
  535     if(!*configfile)
  536     {
  537         getcwd(t_dpath, PATH_MAX);  /* directory we're called from */
  538         if(argv[0] == '/')       /* absolute filename used to call */
  539             strcat(spath, argv);
  540         else
  541         {
  542             strcat(spath, t_dpath);
  543             strcat(spath, "/");
  544             strcat(spath, argv);
  545         }
  546         strcat(tmp, t_dpath);
  547         strcat(tmp, "/ircd.conf");
  548         if((fd = open(tmp, O_RDONLY)) > 0)
  549         {
  550             /* found our ircd.conf in the directory
  551              * where we were called from */
  552             strcpy(configfile, tmp);
  553             close(fd);
  554             strcpy(dpath, t_dpath);
  555             return;
  556         }
  557         len = strlen(spath);
  558         while(spath[len] != '/')
  559             len--;
  560         strncat(t_d2path, spath, len);
  561         strcat(tmp2, t_d2path);
  562         strcat(tmp2, "/ircd.conf");
  563         if((fd = open(tmp2, O_RDONLY)) > 0)
  564         {
  565             /* found the ircd.conf in the directory local
  566              * to our binary itself */
  567             strcpy(configfile, tmp2);
  568             close(fd);
  569             strcpy(dpath, t_d2path);
  570             return;
  571         }
  572     }
  573     else
  574     {
  575         getcwd(t_dpath, PATH_MAX);  /* directory we're called from */
  576         if(argv[0] == '/')       /* absolute filename used to call */
  577             strcat(spath, argv);
  578         else
  579         {
  580             strcat(spath, t_dpath);
  581             strcat(spath, "/");
  582             strcat(spath, argv);
  583         }
  584         if(configfile[0] == '/')     /* absolute filename in configfile */
  585         {
  586             len = strlen(configfile);
  587             while(configfile[len] != '/')
  588                 len--;
  589             strncat(dpath, configfile, len);
  590         }
  591         else
  592         {
  593             strcat(dpath, t_dpath);
  594             strcat(dpath, "/");
  595             if(strchr(configfile, '/'))
  596             {
  597                 len = strlen(configfile);
  598                 while(configfile[len] != '/')
  599                     len--;
  600                 strncat(dpath, configfile, len);
  601             }
  602         }
  603     }
  604     printf("CONFIGFILE: %s\n", configfile);
  605 }
  606 
  607 
  608 /*
  609  * bad_command 
  610  *    This is called when the commandline is not acceptable. 
  611  *    Give error message and exit without starting anything.
  612  */
  613 static int bad_command()
  614 {
  615     printf("Usage: ircd ");
  616 #ifdef CMDLINE_CONFIG
  617     printf("[-f configfile] ");
  618 #endif
  619     printf("[-t] [-v]\n");
  620     printf("-t will cause ircd not to fork (mostly for debugging)\n");
  621     printf("-v will cause ircd to print its version and quit\n");
  622     printf("Server Not Started\n");
  623     return (-1);
  624 }
  625 #ifndef TRUE
  626 #define TRUE 1
  627 #endif
  628 
  629 /* ripped this out of hybrid7 out of lazyness. */
  630 static void
  631 setup_corefile()
  632 {
  633 #ifdef HAVE_SYS_RESOURCE_H
  634   struct rlimit rlim; /* resource limits */
  635 
  636   /* Set corefilesize to maximum */
  637   if (!getrlimit(RLIMIT_CORE, &rlim))
  638     {
  639       rlim.rlim_cur = rlim.rlim_max;
  640       setrlimit(RLIMIT_CORE, &rlim);
  641     }
  642 #endif
  643 }
  644 
  645 char REPORT_DO_DNS[256], REPORT_FIN_DNS[256], REPORT_FIN_DNSC[256], 
  646     REPORT_FAIL_DNS[256], REPORT_DO_ID[256], REPORT_FIN_ID[256], 
  647     REPORT_FAIL_ID[256], REPORT_REJECT_ID[256];
  648 
  649 FILE *dumpfp=NULL;
  650 
  651 int
  652 main(int argc, char *argv[])
  653 {
  654     uid_t         uid, euid;
  655     char        tmp[PATH_MAX];
  656     FILE        *mcsfp;
  657     char        *conferr;
  658 #ifdef USE_SSL
  659     extern int  ssl_capable;
  660 #endif
  661         
  662     if ((timeofday = time(NULL)) == -1) 
  663     {
  664         (void) fprintf(stderr, "ERROR: Clock Failure (%d)\n", errno);
  665         exit(errno);
  666     }
  667         
  668     build_version();
  669 
  670     printf("\n%s booting...\n", version);
  671     printf("Security related issues should be sent to coders@dal.net\n");
  672     printf("All other issues should be sent to dalnet-src@dal.net\n\n");
  673 
  674     setup_corefile();
  675 
  676     Count.server = 1;           /* us */
  677     Count.oper = 0;
  678     Count.chan = 0;
  679     Count.local = 0;
  680     Count.total = 0;
  681     Count.invisi = 0;
  682     Count.unknown = 0;
  683     Count.max_loc = 0;
  684     Count.max_tot = 0;
  685     Count.today = 0;
  686     Count.weekly = 0;
  687     Count.monthly = 0;
  688     Count.yearly = 0;
  689     Count.start = NOW;
  690     Count.day = NOW;
  691     Count.week = NOW;
  692     Count.month = NOW;
  693     Count.year = NOW;
  694 
  695     /*
  696      * this code by mika@cs.caltech.edu 
  697      * it is intended to keep the ircd from being swapped out. BSD
  698      * swapping criteria do not match the requirements of ircd
  699      */
  700 
  701 #if defined(INITIAL_SBUFS_LARGE) && defined(INITIAL_SBUFS_SMALL)        
  702     sbuf_init();        
  703 #endif
  704     
  705     sbrk0 = (char *) sbrk((size_t) 0);
  706     uid = getuid();
  707     euid = geteuid();
  708 
  709 #ifdef PROFILING
  710     setenv("GMON_OUT_PREFIX", "gmon.out", 1);
  711     (void) signal(SIGUSR1, s_dumpprof);
  712     (void) signal(SIGUSR2, s_toggleprof);
  713 #endif
  714         
  715     myargv = argv;
  716     (void) umask(077);          /* better safe than sorry --SRB  */
  717     memset((char *) &me, '\0', sizeof(me));
  718     
  719     setup_signals();
  720     /*
  721      * * All command line parameters have the syntax "-fstring"  or "-f
  722      * string" (e.g. the space is optional). String may  be empty. Flag
  723      * characters cannot be concatenated (like "-fxyz"), it would
  724      * conflict with the form "-fstring".
  725      */
  726     while (--argc > 0 && (*++argv)[0] == '-') 
  727     {
  728         char       *p = argv[0] + 1;
  729         int         flag = *p++;
  730         
  731         if (flag == '\0' || *p == '\0') 
  732         {
  733             if (argc > 1 && argv[1][0] != '-') 
  734             {
  735                 p = *++argv;
  736                 argc -= 1;
  737             }
  738             else
  739                 p = "";
  740         }
  741                 
  742         switch (flag) 
  743         {
  744 #ifdef CMDLINE_CONFIG
  745         case 'f':
  746             (void) setuid((uid_t) uid);
  747             strcpy(configfile, p);
  748             break;
  749 #endif
  750         case 's':
  751             bootopt |= BOOT_STDERR;
  752             break;
  753         case 't':
  754             (void) setuid((uid_t) uid);
  755             bootopt |= BOOT_TTY;
  756             break;
  757         case 'v':
  758             (void) printf("%s\n", version);
  759             exit(0);
  760         case 'x':
  761 #ifdef  DEBUGMODE
  762             (void) setuid((uid_t) uid);
  763             debuglevel = atoi(p);
  764             debugmode = *p ? p : "0";
  765             bootopt |= BOOT_DEBUG;
  766             break;
  767 #else
  768             bad_command();
  769             break;
  770 #endif
  771         default:
  772             bad_command();
  773             break;
  774         }
  775     }
  776 
  777     get_paths(myargv[0]);
  778 
  779     if(chdir(dpath))
  780     {
  781         printf("Error changing directory to ircd.conf location\n");
  782         printf("Server not started\n");
  783         exit(0);
  784     }
  785 
  786     ircsprintf(tmp, "%s/.maxclients", dpath);
  787     mcsfp = fopen(tmp, "r");
  788     if(mcsfp != NULL)
  789     {
  790         fscanf(mcsfp, "%d %d %li %li %li %ld %ld %ld %ld", &Count.max_loc,
  791                &Count.max_tot, &Count.weekly, &Count.monthly, &Count.yearly,
  792                &Count.start, &Count.week, &Count.month, &Count.year);
  793         fclose(mcsfp);
  794     }
  795 
  796     if ((uid != euid) && !euid) 
  797     {
  798         printf("Do not run ircd as root.\nAborting...\n");
  799         exit(-1);
  800     }
  801         
  802     if (argc > 0)
  803         return bad_command();   /* This should exit out  */
  804 
  805     init_globals();
  806 
  807 #ifdef HAVE_ENCRYPTION_ON
  808     printf("Initializing Encryption...");
  809     if(dh_init() == -1)
  810     {
  811         printf("\n\nEncryption Init failed!\n\n");
  812         return 0;
  813     }
  814 #endif
  815     
  816     motd = (aMotd *) NULL;
  817     helpfile = (aMotd *) NULL;
  818     shortmotd = NULL;
  819         
  820     clear_client_hash_table();
  821     clear_channel_hash_table();
  822     clear_scache_hash_table();  /* server cache name table */
  823 
  824     /* init the throttle system -wd */
  825     throttle_init();
  826 
  827     /* clone tracking and limiting */
  828     clones_init();
  829 
  830     /* init the file descriptor tracking system */
  831     init_fds();
  832 
  833     /* init the kline/akill system */
  834     init_userban();
  835 
  836     initlists();
  837     initwhowas();
  838     initstats();
  839     init_tree_parse(msgtab);
  840     init_send();
  841     open_debugfile();
  842     NOW = time(NULL);
  843 
  844     initclass();
  845 
  846     if(initconf(configfile) == -1)
  847     {
  848         printf("Server not started\n");
  849         exit(-1);
  850     }
  851     conferr = finishconf();
  852     if (conferr)
  853     {
  854         printf("ERROR: %s in config file\nServer not started\n", conferr);
  855         exit(-1);
  856     }
  857     merge_confs();
  858     build_rplcache();
  859     read_motd(MOTD);
  860     read_help(HELPFILE);
  861     if(confopts & FLAGS_SMOTD)
  862         read_shortmotd(SHORTMOTD);
  863     printf("Configuration Loaded.\n");
  864 
  865     init_fdlist(&default_fdlist);
  866     {
  867         int i;
  868                   
  869         for (i = MAXCONNECTIONS + 1; i > 0; i--) 
  870         {
  871             default_fdlist.entry[i] = i - 1;
  872         }
  873     }
  874 
  875     /* init the modules, load default modules! */
  876     init_modules();
  877 
  878     me.flags = FLAGS_LISTEN;
  879     me.fd = -1;
  880         
  881     /* We don't want to calculate these every time they are used :) */
  882         
  883     sprintf(REPORT_DO_DNS, REPORT_DO_DNS_, me.name);
  884     sprintf(REPORT_FIN_DNS, REPORT_FIN_DNS_, me.name);
  885     sprintf(REPORT_FIN_DNSC, REPORT_FIN_DNSC_, me.name);
  886     sprintf(REPORT_FAIL_DNS, REPORT_FAIL_DNS_, me.name);
  887     sprintf(REPORT_DO_ID, REPORT_DO_ID_, me.name);
  888     sprintf(REPORT_FIN_ID, REPORT_FIN_ID_, me.name);
  889     sprintf(REPORT_FAIL_ID, REPORT_FAIL_ID_, me.name);
  890     sprintf(REPORT_REJECT_ID, REPORT_REJECT_ID_, me.name);
  891     R_do_dns = strlen(REPORT_DO_DNS);
  892     R_fin_dns = strlen(REPORT_FIN_DNS);
  893     R_fin_dnsc = strlen(REPORT_FIN_DNSC);
  894     R_fail_dns = strlen(REPORT_FAIL_DNS);
  895     R_do_id = strlen(REPORT_DO_ID);
  896     R_fin_id = strlen(REPORT_FIN_ID);
  897     R_fail_id = strlen(REPORT_FAIL_ID);
  898         
  899     NOW = time(NULL);
  900         
  901 #ifdef SPAMFILTER
  902     load_spamfilter();
  903 #endif
  904 
  905 #ifdef USE_SSL
  906     printf("Trying to initialize ssl...\n");
  907     if(!(ssl_capable = ssl_init()))
  908     {
  909         fprintf(stderr, "ssl failed!\n");
  910         exit(-1);
  911     }
  912     printf("ssl has been loaded.\n");
  913 #endif
  914 
  915     init_sys();
  916     forked = 1;
  917 
  918 #ifdef USE_SYSLOG
  919 # define SYSLOG_ME     "ircd"
  920     openlog(SYSLOG_ME, LOG_PID | LOG_NDELAY, LOG_FACILITY);
  921 #endif
  922 
  923     /* the pid file must be written *AFTER* the fork */
  924     write_pidfile();
  925 
  926     /* this should be sooner, but the fork/detach stuff is so brain-dead... */
  927     klinestore_init(0);
  928     
  929     /* moved this to here such that we allow more verbose error
  930      * checking on startup.  -epi
  931      */
  932     open_listeners();
  933 
  934     get_my_name(&me, me.sockhost, sizeof(me.sockhost) - 1);
  935     if (me.name[0] == '\0')
  936         strncpyzt(me.name, me.sockhost, sizeof(me.name));
  937     me.hopcount = 0;
  938     me.authfd = -1;
  939     me.next = NULL;
  940     me.user = NULL;
  941     me.from = &me;
  942     SetMe(&me);
  943     make_server(&me);
  944     me.serv->up = me.name;
  945     me.lasttime = me.since = me.firsttime = NOW;
  946     (void) add_to_client_hash_table(me.name, &me);
  947 
  948 
  949 #ifdef DUMP_DEBUG
  950     dumpfp=fopen("dump.log", "w");
  951 #endif
  952 #ifdef USE_SYSLOG
  953     syslog(LOG_NOTICE, "Server Ready");
  954 #endif
  955     
  956     io_loop();
  957     return 0;
  958 }
  959 
  960 void do_recvqs()
  961 {
  962    DLink *lp, *lpn;
  963    aClient *cptr;
  964 
  965    for(lp = recvq_clients; lp; lp = lpn)
  966    {
  967       lpn = lp->next;
  968       cptr = lp->value.cptr;
  969 
  970       /* dlink is tagged for deletion, cptr is already gone */
  971       if (lp->flags == -1)
  972       {
  973           remove_from_list(&recvq_clients, NULL, lp);
  974           continue;
  975       }
  976 
  977       if(SBufLength(&cptr->recvQ) && !NoNewLine(cptr))
  978       {
  979          if(do_client_queue(cptr) == FLUSH_BUFFER)
  980          {
  981              remove_from_list(&recvq_clients, NULL, lp);
  982              continue;
  983          }
  984       }
  985 
  986       if(!(SBufLength(&cptr->recvQ) && !NoNewLine(cptr)))
  987       {
  988          remove_from_list(&recvq_clients, cptr, lp);
  989          cptr->flags &= ~(FLAGS_HAVERECVQ);
  990       }
  991    }
  992 }
  993 
  994 void send_safelists()
  995 {
  996    DLink *lp, *lpn;
  997    aClient *cptr;
  998 
  999    for(lp = listing_clients; lp; lp = lpn)
 1000    {
 1001       lpn = lp->next;
 1002 
 1003       cptr = lp->value.cptr;
 1004       while(DoList(cptr) && IsSendable(cptr))
 1005          send_list(cptr, 64);
 1006    }
 1007 }
 1008 
 1009 void io_loop()
 1010 {
 1011     char to_send[200];
 1012     int lastexp=0;
 1013 
 1014     time_t      next10sec = 0; /* For events we do every 10 seconds */
 1015 
 1016     time_t      lastbwcalc = 0;
 1017     long        lastbwSK = 0, lastbwRK = 0;
 1018     time_t      lasttimeofday;
 1019     int delay = 0;
 1020 
 1021     while(1)
 1022     {
 1023         lasttimeofday = timeofday;
 1024 
 1025         if ((timeofday = time(NULL)) == -1) 
 1026         {
 1027 #ifdef USE_SYSLOG
 1028             syslog(LOG_WARNING, "Clock Failure (%d), TS can be corrupted", 
 1029                    errno);
 1030 #endif
 1031             sendto_ops("Clock Failure (%d), TS can be corrupted", errno);
 1032         }
 1033 
 1034         if (timeofday < lasttimeofday) 
 1035         {
 1036             ircsprintf(to_send, "System clock running backwards - (%ld < %ld)",
 1037                        (long)timeofday, (long)lasttimeofday);
 1038             report_error(to_send, &me);
 1039         }
 1040 
 1041         NOW = timeofday;
 1042 
 1043         /*
 1044          * Calculate a moving average of our total traffic.     
 1045          * Traffic is a 4 second average, 'sampled' every 2 seconds.
 1046          */
 1047 
 1048         if((timeofday - lastbwcalc) >= 2)
 1049         {
 1050             long ilength = timeofday - lastbwcalc;
 1051 
 1052             curSendK += (float) (me.sendK - lastbwSK) / (float) ilength;
 1053             curRecvK += (float) (me.receiveK - lastbwRK) / (float) ilength;
 1054             curSendK /= 2;
 1055             curRecvK /= 2;
 1056 
 1057             lastbwSK = me.sendK;
 1058             lastbwRK = me.receiveK;
 1059             lastbwcalc = timeofday;
 1060         }
 1061 
 1062         /*
 1063          * We only want to connect if a connection is due, not every
 1064          * time through.  Note, if there are no active C lines, this call
 1065          * to Tryconnections is made once only; it will return 0. - avalon
 1066          */
 1067 
 1068         if (nextconnect && timeofday >= nextconnect)
 1069             nextconnect = try_connections(timeofday);
 1070 
 1071         /* DNS checks. One to timeout queries, one for cache expiries.*/
 1072 
 1073         if (timeofday >= nextdnscheck)
 1074             nextdnscheck = timeout_query_list(timeofday);
 1075         if (timeofday >= nextexpire)
 1076             nextexpire = expire_cache(timeofday);
 1077 
 1078         if (timeofday >= nextbanexpire)
 1079         {
 1080             /*
 1081              * magic number: 13 seconds
 1082              * space out these heavy tasks at semi-random intervals, so as not to coincide
 1083              * with anything else ircd does regularly 
 1084              */
 1085             nextbanexpire = NOW + 13;
 1086             
 1087             if(lastexp == 0)
 1088             {
 1089                 expire_userbans();
 1090                 lastexp++;
 1091             }
 1092             else if(lastexp == 1)
 1093             {
 1094                 expire_simbans();
 1095                 lastexp++;
 1096             }
 1097             else
 1098             {
 1099                 throttle_timer(NOW);
 1100                 lastexp = 0;
 1101             }
 1102         }
 1103 
 1104         if (timeofday >= next10sec)
 1105         {
 1106             next10sec = timeofday + 10;
 1107             call_hooks(CHOOK_10SEC);
 1108         }
 1109 
 1110         /*
 1111          * take the smaller of the two 'timed' event times as the time
 1112          * of next event (stops us being late :) - avalon WARNING -
 1113          * nextconnect can return 0!
 1114          */
 1115 
 1116         if (nextconnect)
 1117             delay = MIN(nextping, nextconnect);
 1118         else
 1119             delay = nextping;
 1120         delay = MIN(nextdnscheck, delay);
 1121         delay = MIN(nextexpire, delay);
 1122         delay -= timeofday;
 1123 
 1124         /*
 1125          * Parse people who have blocked recvqs
 1126          */
 1127         do_recvqs();
 1128 
 1129         /*
 1130          * Send people their /list replies, being careful
 1131          * not to fill their sendQ
 1132          */
 1133         send_safelists();
 1134 
 1135         /*
 1136          * Adjust delay to something reasonable [ad hoc values] (one
 1137          * might think something more clever here... --msa) 
 1138          * We don't really need to check that often and as long 
 1139          * as we don't delay too long, everything should be ok. 
 1140          * waiting too long can cause things to timeout... 
 1141          * i.e. PINGS -> a disconnection :( 
 1142          * - avalon
 1143          */
 1144         if (delay < 1)
 1145             delay = 1;
 1146         else
 1147         {
 1148             /* We need to get back here to do that recvq thing */
 1149             if(recvq_clients != NULL)
 1150                 delay = 1;
 1151             else
 1152                 delay = MIN(delay, TIMESEC);
 1153         }
 1154 
 1155         engine_read_message(delay);     /* check everything! */
 1156 
 1157         /*
 1158          * * ...perhaps should not do these loops every time, but only if
 1159          * there is some chance of something happening (but, note that
 1160          * conf->hold times may be changed elsewhere--so precomputed next
 1161          * event time might be too far away... (similarly with ping
 1162          * times) --msa
 1163          */
 1164         
 1165         if ((timeofday >= nextping))
 1166             nextping = check_pings(timeofday);
 1167 
 1168 #ifdef PROFILING
 1169         if (profiling_newmsg)
 1170         {
 1171             sendto_realops("PROFILING: %s", profiling_msg);
 1172             profiling_newmsg = 0;
 1173         }
 1174 #endif
 1175         
 1176         if (dorehash) 
 1177         {
 1178             (void) rehash(&me, &me, 1);
 1179         (void) read_motd(MOTD);
 1180             dorehash = 0;
 1181         }
 1182         /*
 1183          * 
 1184          * Flush output buffers on all connections now if they 
 1185          * have data in them (or at least try to flush)  -avalon
 1186          *
 1187          * flush_connections(me.fd);
 1188          *
 1189          * avalon, what kind of crack have you been smoking? why
 1190          * on earth would we flush_connections blindly when
 1191          * we already check to see if we can write (and do)
 1192          * in read_message? There is no point, as this causes
 1193          * lots and lots of unnecessary sendto's which 
 1194          * 99% of the time will fail because if we couldn't
 1195          * empty them in read_message we can't empty them here.
 1196          * one effect: during htm, output to normal lusers
 1197          * will lag.
 1198      * htm doesnt exist anymore, but this comment was funny, so i
 1199      * left it in. -epi
 1200          */
 1201         
 1202         /* Now we've made this call a bit smarter. */
 1203         /* Only flush non-blocked sockets. */
 1204         
 1205         flush_connections(me.fd);
 1206     }
 1207 }
 1208 
 1209 /*
 1210  * open_debugfile
 1211  * 
 1212  * If the -t option is not given on the command line when the server is
 1213  * started, all debugging output is sent to the file set by LPATH in
 1214  * config.h Here we just open that file and make sure it is opened to
 1215  * fd 2 so that any fprintf's to stderr also goto the logfile.  If the
 1216  * debuglevel is not set from the command line by -x, use /dev/null as
 1217  * the dummy logfile as long as DEBUGMODE has been defined, else dont
 1218  * waste the fd.
 1219  */
 1220 static void open_debugfile()
 1221 {
 1222 #ifdef  DEBUGMODE
 1223     int         fd;
 1224     aClient    *cptr;
 1225 
 1226     if (debuglevel >= 0) 
 1227     {
 1228         cptr = make_client(NULL, NULL);
 1229         cptr->fd = 2;
 1230         SetLog(cptr);
 1231         cptr->port = debuglevel;
 1232         cptr->flags = 0;
 1233         /*XXX cptr->acpt = cptr; */
 1234         local[2] = cptr;
 1235         (void) strcpy(cptr->sockhost, me.sockhost);
 1236 
 1237         (void) printf("isatty = %d ttyname = %#x\n",
 1238                       isatty(2), (u_int) ttyname(2));
 1239         if (!(bootopt & BOOT_TTY))      /* leave debugging output on fd */ 
 1240         {
 1241             (void) truncate(LOGFILE, 0);
 1242             if ((fd = open(LOGFILE, O_WRONLY | O_CREAT, 0600)) < 0)
 1243                 if ((fd = open("/dev/null", O_WRONLY)) < 0)
 1244                     exit(-1);
 1245             if (fd != 2) 
 1246             {
 1247                 (void) dup2(fd, 2);
 1248                 (void) close(fd);
 1249             }
 1250             strncpyzt(cptr->name, LOGFILE, sizeof(cptr->name));
 1251         }
 1252         else if (isatty(2) && ttyname(2))
 1253             strncpyzt(cptr->name, ttyname(2), sizeof(cptr->name));
 1254         else
 1255             (void) strcpy(cptr->name, "FD2-Pipe");
 1256         Debug((DEBUG_FATAL, "Debug: File <%s> Level: %d at %s",
 1257                cptr->name, cptr->port, myctime(time(NULL))));
 1258     }
 1259     else
 1260         local[2] = NULL;
 1261 #endif
 1262     return;
 1263 }
 1264 
 1265 static void setup_signals()
 1266 {
 1267     struct sigaction act;
 1268 
 1269     act.sa_handler = SIG_IGN;
 1270     act.sa_flags = 0;
 1271     (void) sigemptyset(&act.sa_mask);
 1272     (void) sigaddset(&act.sa_mask, SIGPIPE);
 1273     (void) sigaddset(&act.sa_mask, SIGALRM);
 1274 # ifdef SIGWINCH
 1275     (void) sigaddset(&act.sa_mask, SIGWINCH);
 1276     (void) sigaction(SIGWINCH, &act, NULL);
 1277 # endif
 1278     (void) sigaction(SIGPIPE, &act, NULL);
 1279     act.sa_handler = dummy;
 1280     (void) sigaction(SIGALRM, &act, NULL);
 1281     act.sa_handler = s_rehash;
 1282     (void) sigemptyset(&act.sa_mask);
 1283     (void) sigaddset(&act.sa_mask, SIGHUP);
 1284     (void) sigaction(SIGHUP, &act, NULL);
 1285     act.sa_handler = s_restart;
 1286     (void) sigaddset(&act.sa_mask, SIGINT);
 1287     (void) sigaction(SIGINT, &act, NULL);
 1288     act.sa_handler = s_die;
 1289     (void) sigaddset(&act.sa_mask, SIGTERM);
 1290     (void) sigaction(SIGTERM, &act, NULL);
 1291 
 1292 #ifdef RESTARTING_SYSTEMCALLS
 1293     /*
 1294      * * At least on Apollo sr10.1 it seems continuing system calls 
 1295      * after signal is the default. The following 'siginterrupt' 
 1296      * should change that default to interrupting calls.
 1297      */
 1298     (void) siginterrupt(SIGALRM, 1);
 1299 #endif
 1300 }
 1301 
 1302 u_long
 1303 memcount_ircd(MCircd *mc)
 1304 {
 1305     mc->file = __FILE__;
 1306 
 1307     mc->s_confbuf.c++;
 1308     mc->s_confbuf.m += sizeof(ProxyMonURL);
 1309     mc->s_confbuf.c++;
 1310     mc->s_confbuf.m += sizeof(ProxyMonHost);
 1311     mc->s_confbuf.c++;
 1312     mc->s_confbuf.m += sizeof(Network_Name);
 1313     mc->s_confbuf.c++;
 1314     mc->s_confbuf.m += sizeof(Services_Name);
 1315     mc->s_confbuf.c++;
 1316     mc->s_confbuf.m += sizeof(Stats_Name);
 1317     mc->s_confbuf.c++;
 1318     mc->s_confbuf.m += sizeof(NS_Register_URL);
 1319     mc->s_confbuf.c++;
 1320     mc->s_confbuf.m += sizeof(SpamFilter_URL);
 1321     mc->s_confbuf.c++;
 1322     mc->s_confbuf.m += sizeof(Network_Kline_Address);
 1323     mc->s_confbuf.c++;
 1324     mc->s_confbuf.m += sizeof(Local_Kline_Address);
 1325     mc->s_confbuf.c++;
 1326     mc->s_confbuf.m += sizeof(Staff_Address);
 1327     mc->s_confbuf.c++;
 1328     mc->s_confbuf.m += sizeof(configfile);
 1329     mc->s_confbuf.c++;
 1330     mc->s_confbuf.m += sizeof(dpath);
 1331     mc->s_confbuf.c++;
 1332     mc->s_confbuf.m += sizeof(spath);
 1333 
 1334     return 0;
 1335 }
 1336