Author: Isomer <perry@coders.net>
[ircu2.10.12-pk.git] / ircd / ircd.c
1 /*
2  * IRC - Internet Relay Chat, ircd/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 "sys.h"
22 #if HAVE_SYS_FILE_H
23 #include <sys/file.h>
24 #endif
25 #include <sys/stat.h>
26 #include <pwd.h>
27 #include <signal.h>
28 #if HAVE_FCNTL_H
29 #include <fcntl.h>
30 #endif
31 #ifdef HPUX
32 #define _KERNEL
33 #endif
34 #include <sys/resource.h>
35 #ifdef HPUX
36 #undef _KERNEL
37 #endif
38 #if HAVE_UNISTD_H
39 #include <unistd.h>
40 #endif
41 #include <stdlib.h>
42 #include <stdio.h>
43 #ifdef USE_SYSLOG
44 #include <syslog.h>
45 #endif
46 #ifdef  CHROOTDIR
47 #include <netinet/in.h>
48 #include <arpa/nameser.h>
49 #include <resolv.h>
50 #endif
51 #ifdef VIRTUAL_HOST
52 #include <sys/socket.h>         /* Needed for AF_INET on some OS */
53 #endif
54 #include "h.h"
55 #include "res.h"
56 #include "struct.h"
57 #include "s_serv.h"
58 #include "send.h"
59 #include "ircd.h"
60 #include "s_conf.h"
61 #include "class.h"
62 #include "s_misc.h"
63 #include "parse.h"
64 #include "match.h"
65 #include "s_bsd.h"
66 #include "crule.h"
67 #include "userload.h"
68 #include "numeric.h"
69 #include "hash.h"
70 #include "bsd.h"
71 #include "version.h"
72 #include "whowas.h"
73 #include "numnicks.h"
74
75 RCSTAG_CC("$Id$");
76
77 extern void init_counters(void);
78
79 aClient me;                     /* That's me */
80 aClient *client = &me;          /* Pointer to beginning of Client list */
81 time_t TSoffset = 0;            /* Global variable; Offset of timestamps to
82                                    system clock */
83
84 char **myargv;
85 unsigned short int portnum = 0; /* Server port number, listening this */
86 char *configfile = CPATH;       /* Server configuration file */
87 int debuglevel = -1;            /* Server debug level */
88 unsigned int bootopt = 0;       /* Server boot option flags */
89 char *debugmode = "";           /*  -"-    -"-   -"-  */
90 int dorehash = 0;
91 int restartFlag = 0;
92 static char *dpath = DPATH;
93
94 time_t nextconnect = 1;         /* time for next try_connections call */
95 time_t nextping = 1;            /* same as above for check_pings() */
96 time_t nextdnscheck = 0;        /* next time to poll dns to force timeouts */
97 time_t nextexpire = 1;          /* next expire run on the dns cache */
98
99 time_t now;                     /* Updated every time we leave select(),
100                                    and used everywhere else */
101
102 #ifdef PROFIL
103 extern etext(void);
104
105 RETSIGTYPE s_monitor(HANDLER_ARG(int UNUSED(sig)))
106 {
107   static int mon = 0;
108 #ifdef POSIX_SIGNALS
109   struct sigaction act;
110 #endif
111
112   moncontrol(mon);
113   mon = 1 - mon;
114 #ifdef POSIX_SIGNALS
115   act.sa_handler = s_rehash;
116   act.sa_flags = 0;
117   sigemptyset(&act.sa_mask);
118   sigaddset(&act.sa_mask, SIGUSR1);
119   sigaction(SIGUSR1, &act, NULL);
120 #else
121   signal(SIGUSR1, s_monitor);
122 #endif
123 }
124
125 #endif
126
127 RETSIGTYPE s_die(HANDLER_ARG(int UNUSED(sig)))
128 {
129 #ifdef  USE_SYSLOG
130   syslog(LOG_CRIT, "Server Killed By SIGTERM");
131 #endif
132   flush_connections(me.fd);
133   exit(-1);
134 }
135
136 static RETSIGTYPE s_rehash(HANDLER_ARG(int UNUSED(sig)))
137 {
138 #ifdef  POSIX_SIGNALS
139   struct sigaction act;
140 #endif
141   dorehash = 1;
142 #ifdef  POSIX_SIGNALS
143   act.sa_handler = s_rehash;
144   act.sa_flags = 0;
145   sigemptyset(&act.sa_mask);
146   sigaddset(&act.sa_mask, SIGHUP);
147   sigaction(SIGHUP, &act, NULL);
148 #else
149   signal(SIGHUP, s_rehash);     /* sysV -argv */
150 #endif
151 }
152
153 #ifdef  USE_SYSLOG
154 void restart(char *mesg)
155 #else
156 void restart(char *UNUSED(mesg))
157 #endif
158 {
159 #ifdef  USE_SYSLOG
160   syslog(LOG_WARNING, "Restarting Server because: %s", mesg);
161 #endif
162   server_reboot();
163 }
164
165 RETSIGTYPE s_restart(HANDLER_ARG(int UNUSED(sig)))
166 {
167   restartFlag = 1;
168 }
169
170 void server_reboot(void)
171 {
172   Reg1 int i;
173
174   sendto_ops("Aieeeee!!!  Restarting server...");
175   Debug((DEBUG_NOTICE, "Restarting server..."));
176   flush_connections(me.fd);
177   /*
178    * fd 0 must be 'preserved' if either the -d or -i options have
179    * been passed to us before restarting.
180    */
181 #ifdef USE_SYSLOG
182   closelog();
183 #endif
184   for (i = 3; i < MAXCONNECTIONS; i++)
185     close(i);
186   if (!(bootopt & (BOOT_TTY | BOOT_DEBUG)))
187     close(2);
188   close(1);
189   if ((bootopt & BOOT_CONSOLE) || isatty(0))
190     close(0);
191   if (!(bootopt & BOOT_INETD))
192     execv(SPATH, myargv);
193 #ifdef USE_SYSLOG
194   /* Have to reopen since it has been closed above */
195
196   openlog(myargv[0], LOG_PID | LOG_NDELAY, LOG_FACILITY);
197   syslog(LOG_CRIT, "execv(%s,%s) failed: %m\n", SPATH, myargv[0]);
198   closelog();
199 #endif
200   Debug((DEBUG_FATAL, "Couldn't restart server \"%s\": %s",
201       SPATH, strerror(errno)));
202   exit(-1);
203 }
204
205 /*
206  * try_connections
207  *
208  * Scan through configuration and try new connections.
209  *
210  * Returns the calendar time when the next call to this
211  * function should be made latest. (No harm done if this
212  * is called earlier or later...)
213  */
214 static time_t try_connections(void)
215 {
216   Reg1 aConfItem *aconf;
217   Reg2 aClient *cptr;
218   aConfItem **pconf;
219   int connecting, confrq;
220   time_t next = 0;
221   aConfClass *cltmp;
222   aConfItem *cconf, *con_conf = NULL;
223   unsigned int con_class = 0;
224
225   connecting = FALSE;
226   Debug((DEBUG_NOTICE, "Connection check at   : %s", myctime(now)));
227   for (aconf = conf; aconf; aconf = aconf->next)
228   {
229     /* Also when already connecting! (update holdtimes) --SRB */
230     if (!(aconf->status & CONF_CONNECT_SERVER) || aconf->port == 0)
231       continue;
232     cltmp = aconf->confClass;
233     /*
234      * Skip this entry if the use of it is still on hold until
235      * future. Otherwise handle this entry (and set it on hold
236      * until next time). Will reset only hold times, if already
237      * made one successfull connection... [this algorithm is
238      * a bit fuzzy... -- msa >;) ]
239      */
240
241     if ((aconf->hold > now))
242     {
243       if ((next > aconf->hold) || (next == 0))
244         next = aconf->hold;
245       continue;
246     }
247
248     confrq = get_con_freq(cltmp);
249     aconf->hold = now + confrq;
250     /*
251      * Found a CONNECT config with port specified, scan clients
252      * and see if this server is already connected?
253      */
254     cptr = FindServer(aconf->name);
255
256     if (!cptr && (Links(cltmp) < MaxLinks(cltmp)) &&
257         (!connecting || (ConClass(cltmp) > con_class)))
258     {
259       /* Check connect rules to see if we're allowed to try */
260       for (cconf = conf; cconf; cconf = cconf->next)
261         if ((cconf->status & CONF_CRULE) &&
262             (match(cconf->host, aconf->name) == 0))
263           if (crule_eval(cconf->passwd))
264             break;
265       if (!cconf)
266       {
267         con_class = ConClass(cltmp);
268         con_conf = aconf;
269         /* We connect only one at time... */
270         connecting = TRUE;
271       }
272     }
273     if ((next > aconf->hold) || (next == 0))
274       next = aconf->hold;
275   }
276   if (connecting)
277   {
278     if (con_conf->next)         /* are we already last? */
279     {
280       /* Put the current one at the end and make sure we try all connections */
281       for (pconf = &conf; (aconf = *pconf); pconf = &(aconf->next))
282         if (aconf == con_conf)
283           *pconf = aconf->next;
284       (*pconf = con_conf)->next = 0;
285     }
286     if (connect_server(con_conf, (aClient *)NULL, (struct hostent *)NULL) == 0)
287       sendto_ops("Connection to %s[%s] activated.",
288           con_conf->name, con_conf->host);
289   }
290   Debug((DEBUG_NOTICE, "Next connection check : %s", myctime(next)));
291   return (next);
292 }
293
294 static time_t check_pings(void)
295 {
296   Reg1 aClient *cptr;
297   int ping = 0, i, rflag = 0;
298   time_t oldest = 0, timeout;
299
300   for (i = 0; i <= highest_fd; i++)
301   {
302     if (!(cptr = loc_clients[i]) || IsMe(cptr) || IsLog(cptr) || IsPing(cptr))
303       continue;
304
305     /*
306      * Note: No need to notify opers here.
307      * It's already done when "FLAGS_DEADSOCKET" is set.
308      */
309     if (IsDead(cptr))
310     {
311       exit_client(cptr, cptr, &me, LastDeadComment(cptr));
312       continue;
313     }
314
315 #if defined(R_LINES) && defined(R_LINES_OFTEN)
316     rflag = IsUser(cptr) ? find_restrict(cptr) : 0;
317 #endif
318     ping = IsRegistered(cptr) ? get_client_ping(cptr) : CONNECTTIMEOUT;
319     Debug((DEBUG_DEBUG, "c(%s)=%d p %d r %d a %d",
320         cptr->name, cptr->status, ping, rflag, (int)(now - cptr->lasttime)));
321     /*
322      * Ok, so goto's are ugly and can be avoided here but this code
323      * is already indented enough so I think its justified. -avalon
324      */
325     if (!rflag && IsRegistered(cptr) && (ping >= now - cptr->lasttime))
326       goto ping_timeout;
327     /*
328      * If the server hasnt talked to us in 2*ping seconds
329      * and it has a ping time, then close its connection.
330      * If the client is a user and a KILL line was found
331      * to be active, close this connection too.
332      */
333     if (rflag ||
334         ((now - cptr->lasttime) >= (2 * ping) &&
335         (cptr->flags & FLAGS_PINGSENT)) ||
336         (!IsRegistered(cptr) && !IsHandshake(cptr) &&
337         (now - cptr->firsttime) >= ping))
338     {
339       if (!IsRegistered(cptr) && (DoingDNS(cptr) || DoingAuth(cptr)))
340       {
341         Debug((DEBUG_NOTICE, "%s/%s timeout %s", DoingDNS(cptr) ? "DNS" : "",
342             DoingAuth(cptr) ? "AUTH" : "", get_client_name(cptr, TRUE)));
343         if (cptr->authfd >= 0)
344         {
345           close(cptr->authfd);
346           cptr->authfd = -1;
347           cptr->count = 0;
348           *cptr->buffer = '\0';
349         }
350         del_queries((char *)cptr);
351         ClearAuth(cptr);
352         ClearDNS(cptr);
353         SetAccess(cptr);
354         cptr->firsttime = now;
355         cptr->lasttime = now;
356         continue;
357       }
358       if (IsServer(cptr) || IsConnecting(cptr) || IsHandshake(cptr))
359       {
360         sendto_ops("No response from %s, closing link",
361             get_client_name(cptr, FALSE));
362         exit_client(cptr, cptr, &me, "Ping timeout");
363         continue;
364       }
365       /*
366        * This is used for KILL lines with time restrictions
367        * on them - send a messgae to the user being killed first.
368        */
369 #if defined(R_LINES) && defined(R_LINES_OFTEN)
370       else if (IsUser(cptr) && rflag)
371       {
372         sendto_ops("Restricting %s, closing link.",
373             get_client_name(cptr, FALSE));
374         exit_client(cptr, cptr, &me, "R-lined");
375       }
376 #endif
377       else
378       {
379         if (!IsRegistered(cptr) && *cptr->name && *cptr->user->username)
380         {
381           sendto_one(cptr,
382               ":%s %d %s :Your client may not be compatible with this server.",
383               me.name, ERR_BADPING, cptr->name);
384           sendto_one(cptr,
385               ":%s %d %s :Compatible clients are available at "
386               "ftp://ftp.undernet.org/pub/irc/clients",
387               me.name, ERR_BADPING, cptr->name);
388         }
389         exit_client_msg(cptr, cptr, &me, "Ping timeout for %s",
390             get_client_name(cptr, FALSE));
391       }
392       continue;
393     }
394     else if (IsRegistered(cptr) && (cptr->flags & FLAGS_PINGSENT) == 0)
395     {
396       /*
397        * If we havent PINGed the connection and we havent
398        * heard from it in a while, PING it to make sure
399        * it is still alive.
400        */
401       cptr->flags |= FLAGS_PINGSENT;
402       /* not nice but does the job */
403       cptr->lasttime = now - ping;
404       if (IsUser(cptr))
405         sendto_one(cptr, "PING :%s", me.name);
406       else
407         sendto_one(cptr, ":%s PING :%s", me.name, me.name);
408     }
409   ping_timeout:
410     timeout = cptr->lasttime + ping;
411     while (timeout <= now)
412       timeout += ping;
413     if (timeout < oldest || !oldest)
414       oldest = timeout;
415   }
416   if (!oldest || oldest < now)
417     oldest = now + PINGFREQUENCY;
418   Debug((DEBUG_NOTICE,
419       "Next check_ping() call at: %s, %d " TIME_T_FMT " " TIME_T_FMT,
420       myctime(oldest), ping, oldest, now));
421
422   return (oldest);
423 }
424
425 /*
426  * bad_command
427  *
428  * This is called when the commandline is not acceptable.
429  * Give error message and exit without starting anything.
430  */
431 static int bad_command(void)
432 {
433   printf("Usage: ircd %s[-h servername] [-p portnumber] [-x loglevel] [-t]\n",
434 #ifdef CMDLINE_CONFIG
435       "[-f config] "
436 #else
437       ""
438 #endif
439       );
440   printf("Server not started\n\n");
441   return (-1);
442 }
443
444 static void setup_signals(void)
445 {
446 #ifdef  POSIX_SIGNALS
447   struct sigaction act;
448
449   act.sa_handler = SIG_IGN;
450   act.sa_flags = 0;
451   sigemptyset(&act.sa_mask);
452   sigaddset(&act.sa_mask, SIGPIPE);
453   sigaddset(&act.sa_mask, SIGALRM);
454 #ifdef  SIGWINCH
455   sigaddset(&act.sa_mask, SIGWINCH);
456   sigaction(SIGWINCH, &act, NULL);
457 #endif
458   sigaction(SIGPIPE, &act, NULL);
459   act.sa_handler = dummy;
460   sigaction(SIGALRM, &act, NULL);
461   act.sa_handler = s_rehash;
462   sigemptyset(&act.sa_mask);
463   sigaddset(&act.sa_mask, SIGHUP);
464   sigaction(SIGHUP, &act, NULL);
465   act.sa_handler = s_restart;
466   sigaddset(&act.sa_mask, SIGINT);
467   sigaction(SIGINT, &act, NULL);
468   act.sa_handler = s_die;
469   sigaddset(&act.sa_mask, SIGTERM);
470   sigaction(SIGTERM, &act, NULL);
471
472 #else
473 #ifndef HAVE_RELIABLE_SIGNALS
474   signal(SIGPIPE, dummy);
475 #ifdef  SIGWINCH
476   signal(SIGWINCH, dummy);
477 #endif
478 #else
479 #ifdef  SIGWINCH
480   signal(SIGWINCH, SIG_IGN);
481 #endif
482   signal(SIGPIPE, SIG_IGN);
483 #endif
484   signal(SIGALRM, dummy);
485   signal(SIGHUP, s_rehash);
486   signal(SIGTERM, s_die);
487   signal(SIGINT, s_restart);
488 #endif
489
490 #ifdef HAVE_RESTARTABLE_SYSCALLS
491   /*
492    * At least on Apollo sr10.1 it seems continuing system calls
493    * after signal is the default. The following 'siginterrupt'
494    * should change that default to interrupting calls.
495    */
496   siginterrupt(SIGALRM, 1);
497 #endif
498 }
499
500 /*
501  * open_debugfile
502  *
503  * If the -t option is not given on the command line when the server is
504  * started, all debugging output is sent to the file set by LPATH in config.h
505  * Here we just open that file and make sure it is opened to fd 2 so that
506  * any fprintf's to stderr also goto the logfile.  If the debuglevel is not
507  * set from the command line by -x, use /dev/null as the dummy logfile as long
508  * as DEBUGMODE has been defined, else dont waste the fd.
509  */
510 static void open_debugfile(void)
511 {
512 #ifdef  DEBUGMODE
513   int fd;
514   aClient *cptr;
515
516   if (debuglevel >= 0)
517   {
518     cptr = make_client(NULL, STAT_LOG);
519     cptr->fd = 2;
520     cptr->port = debuglevel;
521     cptr->flags = 0;
522     cptr->acpt = cptr;
523     loc_clients[2] = cptr;
524     strcpy(cptr->sockhost, me.sockhost);
525
526     printf("isatty = %d ttyname = %#x\n", isatty(2), (unsigned int)ttyname(2));
527     if (!(bootopt & BOOT_TTY))  /* leave debugging output on fd 2 */
528     {
529       if ((fd = creat(LOGFILE, 0600)) < 0)
530         if ((fd = open("/dev/null", O_WRONLY)) < 0)
531           exit(-1);
532       if (fd != 2)
533       {
534         dup2(fd, 2);
535         close(fd);
536       }
537       strncpy(cptr->name, LOGFILE, sizeof(cptr->name));
538       cptr->name[sizeof(cptr->name) - 1] = 0;
539     }
540     else if (isatty(2) && ttyname(2))
541     {
542       strncpy(cptr->name, ttyname(2), sizeof(cptr->name));
543       cptr->name[sizeof(cptr->name) - 1] = 0;
544     }
545     else
546       strcpy(cptr->name, "FD2-Pipe");
547     Debug((DEBUG_FATAL, "Debug: File <%s> Level: %u at %s",
548         cptr->name, cptr->port, myctime(now)));
549   }
550   else
551     loc_clients[2] = NULL;
552 #endif
553   return;
554 }
555
556 int main(int argc, char *argv[])
557 {
558   unsigned short int portarg = 0;
559   uid_t uid;
560   uid_t euid;
561   time_t delay = 0;
562 #if defined(HAVE_SETRLIMIT) && defined(RLIMIT_CORE)
563   struct rlimit corelim;
564 #endif
565
566   uid = getuid();
567   euid = geteuid();
568   now = time(NULL);
569 #ifdef PROFIL
570   monstartup(0, etext);
571   moncontrol(1);
572   signal(SIGUSR1, s_monitor);
573 #endif
574
575 #ifdef CHROOTDIR
576   if (chdir(DPATH))
577   {
578     fprintf(stderr, "Fail: Cannot chdir(%s): %s\n", DPATH, strerror(errno));
579     exit(-1);
580   }
581   res_init();
582   if (chroot(DPATH))
583   {
584     fprintf(stderr, "Fail: Cannot chroot(%s): %s\n", DPATH, strerror(errno));
585     exit(5);
586   }
587   dpath = "/";
588 #endif /*CHROOTDIR */
589
590   myargv = argv;
591   umask(077);                   /* better safe than sorry --SRB */
592   memset(&me, 0, sizeof(me));
593 #ifdef VIRTUAL_HOST
594   memset(&vserv, 0, sizeof(vserv));
595 #endif
596
597   setup_signals();
598   initload();
599
600 #if defined(HAVE_SETRLIMIT) && defined(RLIMIT_CORE)
601   if (getrlimit(RLIMIT_CORE, &corelim))
602   {
603     fprintf(stderr, "Read of rlimit core size failed: %s\n", strerror(errno));
604     corelim.rlim_max = RLIM_INFINITY;   /* Try to recover */
605   }
606   corelim.rlim_cur = corelim.rlim_max;
607   if (setrlimit(RLIMIT_CORE, &corelim))
608     fprintf(stderr, "Setting rlimit core size failed: %s\n", strerror(errno));
609 #endif
610
611   /*
612    * All command line parameters have the syntax "-fstring"
613    * or "-f string" (e.g. the space is optional). String may
614    * be empty. Flag characters cannot be concatenated (like
615    * "-fxyz"), it would conflict with the form "-fstring".
616    */
617   while (--argc > 0 && (*++argv)[0] == '-')
618   {
619     char *p = argv[0] + 1;
620     int flag = *p++;
621
622     if (flag == '\0' || *p == '\0')
623     {
624       if (argc > 1 && argv[1][0] != '-')
625       {
626         p = *++argv;
627         argc -= 1;
628       }
629       else
630         p = "";
631     }
632
633     switch (flag)
634     {
635       case 'a':
636         bootopt |= BOOT_AUTODIE;
637         break;
638       case 'c':
639         bootopt |= BOOT_CONSOLE;
640         break;
641       case 'q':
642         bootopt |= BOOT_QUICK;
643         break;
644       case 'd':
645         if (euid != uid)
646           setuid((uid_t) uid);
647         dpath = p;
648         break;
649 #ifdef CMDLINE_CONFIG
650       case 'f':
651         if (euid != uid)
652           setuid((uid_t) uid);
653         configfile = p;
654         break;
655 #endif
656       case 'h':
657         strncpy(me.name, p, sizeof(me.name));
658         me.name[sizeof(me.name) - 1] = 0;
659         break;
660       case 'i':
661         bootopt |= BOOT_INETD | BOOT_AUTODIE;
662         break;
663       case 'p':
664         if ((portarg = atoi(p)) > 0)
665           portnum = portarg;
666         break;
667       case 't':
668         if (euid != uid)
669           setuid((uid_t) uid);
670         bootopt |= BOOT_TTY;
671         break;
672       case 'v':
673         printf("ircd %s\n", version);
674         exit(0);
675 #ifdef VIRTUAL_HOST
676       case 'w':
677       {
678         struct hostent *hep;
679         if (!(hep = gethostbyname(p)))
680         {
681           fprintf(stderr, "%s: Error creating virtual host \"%s\": %d",
682               argv[0], p, h_errno);
683           return -1;
684         }
685         if (hep->h_addrtype == AF_INET && hep->h_addr_list[0] &&
686             !hep->h_addr_list[1])
687         {
688           memcpy(&vserv.sin_addr, hep->h_addr_list[0], sizeof(struct in_addr));
689           vserv.sin_family = AF_INET;
690         }
691         else
692         {
693           fprintf(stderr, "%s: Error creating virtual host \"%s\": "
694               "Use -w <IP-number of interface>\n", argv[0], p);
695           return -1;
696         }
697         break;
698       }
699 #endif
700       case 'x':
701 #ifdef  DEBUGMODE
702         if (euid != uid)
703           setuid((uid_t) uid);
704         debuglevel = atoi(p);
705         debugmode = *p ? p : "0";
706         bootopt |= BOOT_DEBUG;
707         break;
708 #else
709         fprintf(stderr, "%s: DEBUGMODE must be defined for -x y\n", myargv[0]);
710         exit(0);
711 #endif
712       default:
713         bad_command();
714         break;
715     }
716   }
717
718   if (chdir(dpath))
719   {
720     fprintf(stderr, "Fail: Cannot chdir(%s): %s\n", dpath, strerror(errno));
721     exit(-1);
722   }
723
724 #ifndef IRC_UID
725   if ((uid != euid) && !euid)
726   {
727     fprintf(stderr,
728         "ERROR: do not run ircd setuid root. Make it setuid a normal user.\n");
729     exit(-1);
730   }
731 #endif
732
733 #if !defined(CHROOTDIR) || (defined(IRC_UID) && defined(IRC_GID))
734 #ifndef _AIX
735   if (euid != uid)
736   {
737     setuid((uid_t) uid);
738     setuid((uid_t) euid);
739   }
740 #endif
741
742   if ((int)getuid() == 0)
743   {
744 #if defined(IRC_UID) && defined(IRC_GID)
745
746     /* run as a specified user */
747     fprintf(stderr, "WARNING: running ircd with uid = %d\n", IRC_UID);
748     fprintf(stderr, "         changing to gid %d.\n", IRC_GID);
749     setuid(IRC_UID);
750     setgid(IRC_GID);
751 #else
752     /* check for setuid root as usual */
753     fprintf(stderr,
754         "ERROR: do not run ircd setuid root. Make it setuid a normal user.\n");
755     exit(-1);
756 #endif
757   }
758 #endif /*CHROOTDIR/UID/GID */
759
760   if (argc > 0)
761     return bad_command();       /* This should exit out */
762
763 #if HAVE_UNISTD_H
764   /* Sanity checks */
765   {
766     char c;
767     char *path;
768
769     c = 'S';
770     path = SPATH;
771     if (access(path, X_OK) == 0)
772     {
773       c = 'C';
774       path = CPATH;
775       if (access(path, R_OK) == 0)
776       {
777         c = 'M';
778         path = MPATH;
779         if (access(path, R_OK) == 0)
780         {
781           c = 'R';
782           path = RPATH;
783           if (access(path, R_OK) == 0)
784           {
785 #ifndef DEBUG
786             c = 0;
787 #else
788             c = 'L';
789             path = LPATH;
790             if (access(path, W_OK) == 0)
791               c = 0;
792 #endif
793           }
794         }
795       }
796     }
797     if (c)
798     {
799       fprintf(stderr, "Check on %cPATH (%s) failed: %s\n",
800           c, path, strerror(errno));
801       fprintf(stderr,
802           "Please create file and/or rerun `make config' and recompile to correct this.\n");
803 #ifdef CHROOTDIR
804       fprintf(stderr,
805           "Keep in mind that all paths are relative to CHROOTDIR.\n");
806 #endif
807       exit(-1);
808     }
809   }
810 #endif
811
812   hash_init();
813 #ifdef DEBUGMODE
814   initlists();
815 #endif
816   initclass();
817   initwhowas();
818   initmsgtree();
819   initstats();
820   open_debugfile();
821   if (portnum == 0)
822     portnum = PORTNUM;
823   me.port = portnum;
824   init_sys();
825   me.flags = FLAGS_LISTEN;
826   if ((bootopt & BOOT_INETD))
827   {
828     me.fd = 0;
829     loc_clients[0] = &me;
830     me.flags = FLAGS_LISTEN;
831   }
832   else
833     me.fd = -1;
834
835 #ifdef USE_SYSLOG
836   openlog(myargv[0], LOG_PID | LOG_NDELAY, LOG_FACILITY);
837 #endif
838   if (initconf(bootopt) == -1)
839   {
840     Debug((DEBUG_FATAL, "Failed in reading configuration file %s", configfile));
841     printf("Couldn't open configuration file %s\n", configfile);
842     exit(-1);
843   }
844   if (!(bootopt & BOOT_INETD))
845   {
846     static char star[] = "*";
847     aConfItem *aconf;
848
849     if ((aconf = find_me()) && portarg == 0 && aconf->port != 0)
850       portnum = aconf->port;
851     Debug((DEBUG_ERROR, "Port = %u", portnum));
852     if (inetport(&me, star, portnum))
853       exit(1);
854   }
855   else if (inetport(&me, "*", 0))
856     exit(1);
857
858   read_tlines();
859   rmotd = read_motd(RPATH);
860   motd = read_motd(MPATH);
861   setup_ping();
862   get_my_name(&me, me.sockhost, sizeof(me.sockhost) - 1);
863   now = time(NULL);
864   me.hopcount = 0;
865   me.authfd = -1;
866   me.confs = NULL;
867   me.next = NULL;
868   me.user = NULL;
869   me.from = &me;
870   SetMe(&me);
871   make_server(&me);
872   /* Abuse own link timestamp as start timestamp: */
873   me.serv->timestamp = TStime();
874   me.serv->prot = atoi(MAJOR_PROTOCOL);
875   me.serv->up = &me;
876   me.serv->down = NULL;
877
878   SetYXXCapacity(&me, MAXCLIENTS);
879
880   me.lasttime = me.since = me.firsttime = now;
881   hAddClient(&me);
882
883   check_class();
884   write_pidfile();
885
886   init_counters();
887
888   Debug((DEBUG_NOTICE, "Server ready..."));
889 #ifdef USE_SYSLOG
890   syslog(LOG_NOTICE, "Server Ready");
891 #endif
892
893   for (;;)
894   {
895     /*
896      * We only want to connect if a connection is due,
897      * not every time through.   Note, if there are no
898      * active C lines, this call to Tryconnections is
899      * made once only; it will return 0. - avalon
900      */
901     if (nextconnect && now >= nextconnect)
902       nextconnect = try_connections();
903     /*
904      * DNS checks. One to timeout queries, one for cache expiries.
905      */
906     if (now >= nextdnscheck)
907       nextdnscheck = timeout_query_list();
908     if (now >= nextexpire)
909       nextexpire = expire_cache();
910     /*
911      * Take the smaller of the two 'timed' event times as
912      * the time of next event (stops us being late :) - avalon
913      * WARNING - nextconnect can return 0!
914      */
915     if (nextconnect)
916       delay = MIN(nextping, nextconnect);
917     else
918       delay = nextping;
919     delay = MIN(nextdnscheck, delay);
920     delay = MIN(nextexpire, delay);
921     delay -= now;
922     /*
923      * Adjust delay to something reasonable [ad hoc values]
924      * (one might think something more clever here... --msa)
925      * We don't really need to check that often and as long
926      * as we don't delay too long, everything should be ok.
927      * waiting too long can cause things to timeout...
928      * i.e. PINGS -> a disconnection :(
929      * - avalon
930      */
931     if (delay < 1)
932       delay = 1;
933     else
934       delay = MIN(delay, TIMESEC);
935     read_message(delay);
936
937     Debug((DEBUG_DEBUG, "Got message(s)"));
938
939     /*
940      * ...perhaps should not do these loops every time,
941      * but only if there is some chance of something
942      * happening (but, note that conf->hold times may
943      * be changed elsewhere--so precomputed next event
944      * time might be too far away... (similarly with
945      * ping times) --msa
946      */
947     if (now >= nextping)
948       nextping = check_pings();
949
950     if (dorehash)
951     {
952       rehash(&me, 1);
953       dorehash = 0;
954     }
955     if (restartFlag)
956       server_reboot();
957   }
958 }