3f8ddadd0cfad7cb0515e4cbeb352c265d787a51
[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 activated.", con_conf->name);
288   }
289   Debug((DEBUG_NOTICE, "Next connection check : %s", myctime(next)));
290   return (next);
291 }
292
293 static time_t check_pings(void)
294 {
295   Reg1 aClient *cptr;
296   int ping = 0, i, rflag = 0;
297   time_t oldest = 0, timeout;
298
299   for (i = 0; i <= highest_fd; i++)
300   {
301     if (!(cptr = loc_clients[i]) || IsMe(cptr) || IsLog(cptr) || IsPing(cptr))
302       continue;
303
304     /*
305      * Note: No need to notify opers here.
306      * It's already done when "FLAGS_DEADSOCKET" is set.
307      */
308     if (IsDead(cptr))
309     {
310       exit_client(cptr, cptr, &me, LastDeadComment(cptr));
311       continue;
312     }
313
314 #if defined(R_LINES) && defined(R_LINES_OFTEN)
315     rflag = IsUser(cptr) ? find_restrict(cptr) : 0;
316 #endif
317     ping = IsRegistered(cptr) ? get_client_ping(cptr) : CONNECTTIMEOUT;
318     Debug((DEBUG_DEBUG, "c(%s)=%d p %d r %d a %d",
319         cptr->name, cptr->status, ping, rflag, (int)(now - cptr->lasttime)));
320     /*
321      * Ok, so goto's are ugly and can be avoided here but this code
322      * is already indented enough so I think its justified. -avalon
323      */
324     if (!rflag && IsRegistered(cptr) && (ping >= now - cptr->lasttime))
325       goto ping_timeout;
326     /*
327      * If the server hasnt talked to us in 2*ping seconds
328      * and it has a ping time, then close its connection.
329      * If the client is a user and a KILL line was found
330      * to be active, close this connection too.
331      */
332     if (rflag ||
333         ((now - cptr->lasttime) >= (2 * ping) &&
334         (cptr->flags & FLAGS_PINGSENT)) ||
335         (!IsRegistered(cptr) && !IsHandshake(cptr) &&
336         (now - cptr->firsttime) >= ping))
337     {
338       if (!IsRegistered(cptr) && (DoingDNS(cptr) || DoingAuth(cptr)))
339       {
340         Debug((DEBUG_NOTICE, "%s/%s timeout %s", DoingDNS(cptr) ? "DNS" : "",
341             DoingAuth(cptr) ? "AUTH" : "", get_client_name(cptr, FALSE)));
342         if (cptr->authfd >= 0)
343         {
344           close(cptr->authfd);
345           cptr->authfd = -1;
346           cptr->count = 0;
347           *cptr->buffer = '\0';
348         }
349         del_queries((char *)cptr);
350         ClearAuth(cptr);
351         ClearDNS(cptr);
352         SetAccess(cptr);
353         cptr->firsttime = now;
354         cptr->lasttime = now;
355         continue;
356       }
357       if (IsServer(cptr) || IsConnecting(cptr) || IsHandshake(cptr))
358       {
359         sendto_ops("No response from %s, closing link", cptr->name);
360         exit_client(cptr, cptr, &me, "Ping timeout");
361         continue;
362       }
363       /*
364        * This is used for KILL lines with time restrictions
365        * on them - send a messgae to the user being killed first.
366        */
367 #if defined(R_LINES) && defined(R_LINES_OFTEN)
368       else if (IsUser(cptr) && rflag)
369       {
370         sendto_ops("Restricting %s, closing link.",
371             get_client_name(cptr, FALSE));
372         exit_client(cptr, cptr, &me, "R-lined");
373       }
374 #endif
375       else
376       {
377         if (!IsRegistered(cptr) && *cptr->name && *cptr->user->username)
378         {
379           sendto_one(cptr,
380               ":%s %d %s :Your client may not be compatible with this server.",
381               me.name, ERR_BADPING, cptr->name);
382           sendto_one(cptr,
383               ":%s %d %s :Compatible clients are available at "
384               "ftp://ftp.undernet.org/pub/irc/clients",
385               me.name, ERR_BADPING, cptr->name);
386         }
387         exit_client_msg(cptr, cptr, &me, "Ping timeout for %s",
388             IsServer(cptr) ? cptr->name : get_client_name(cptr, FALSE));
389       }
390       continue;
391     }
392     else if (IsRegistered(cptr) && (cptr->flags & FLAGS_PINGSENT) == 0)
393     {
394       /*
395        * If we havent PINGed the connection and we havent
396        * heard from it in a while, PING it to make sure
397        * it is still alive.
398        */
399       cptr->flags |= FLAGS_PINGSENT;
400       /* not nice but does the job */
401       cptr->lasttime = now - ping;
402       if (IsUser(cptr))
403         sendto_one(cptr, "PING :%s", me.name);
404       else
405         sendto_one(cptr, ":%s PING :%s", me.name, me.name);
406     }
407   ping_timeout:
408     timeout = cptr->lasttime + ping;
409     while (timeout <= now)
410       timeout += ping;
411     if (timeout < oldest || !oldest)
412       oldest = timeout;
413   }
414   if (!oldest || oldest < now)
415     oldest = now + PINGFREQUENCY;
416   Debug((DEBUG_NOTICE,
417       "Next check_ping() call at: %s, %d " TIME_T_FMT " " TIME_T_FMT,
418       myctime(oldest), ping, oldest, now));
419
420   return (oldest);
421 }
422
423 /*
424  * bad_command
425  *
426  * This is called when the commandline is not acceptable.
427  * Give error message and exit without starting anything.
428  */
429 static int bad_command(void)
430 {
431   printf("Usage: ircd %s[-h servername] [-p portnumber] [-x loglevel] [-t]\n",
432 #ifdef CMDLINE_CONFIG
433       "[-f config] "
434 #else
435       ""
436 #endif
437       );
438   printf("Server not started\n\n");
439   return (-1);
440 }
441
442 static void setup_signals(void)
443 {
444 #ifdef  POSIX_SIGNALS
445   struct sigaction act;
446
447   act.sa_handler = SIG_IGN;
448   act.sa_flags = 0;
449   sigemptyset(&act.sa_mask);
450   sigaddset(&act.sa_mask, SIGPIPE);
451   sigaddset(&act.sa_mask, SIGALRM);
452 #ifdef  SIGWINCH
453   sigaddset(&act.sa_mask, SIGWINCH);
454   sigaction(SIGWINCH, &act, NULL);
455 #endif
456   sigaction(SIGPIPE, &act, NULL);
457   act.sa_handler = dummy;
458   sigaction(SIGALRM, &act, NULL);
459   act.sa_handler = s_rehash;
460   sigemptyset(&act.sa_mask);
461   sigaddset(&act.sa_mask, SIGHUP);
462   sigaction(SIGHUP, &act, NULL);
463   act.sa_handler = s_restart;
464   sigaddset(&act.sa_mask, SIGINT);
465   sigaction(SIGINT, &act, NULL);
466   act.sa_handler = s_die;
467   sigaddset(&act.sa_mask, SIGTERM);
468   sigaction(SIGTERM, &act, NULL);
469
470 #else
471 #ifndef HAVE_RELIABLE_SIGNALS
472   signal(SIGPIPE, dummy);
473 #ifdef  SIGWINCH
474   signal(SIGWINCH, dummy);
475 #endif
476 #else
477 #ifdef  SIGWINCH
478   signal(SIGWINCH, SIG_IGN);
479 #endif
480   signal(SIGPIPE, SIG_IGN);
481 #endif
482   signal(SIGALRM, dummy);
483   signal(SIGHUP, s_rehash);
484   signal(SIGTERM, s_die);
485   signal(SIGINT, s_restart);
486 #endif
487
488 #ifdef HAVE_RESTARTABLE_SYSCALLS
489   /*
490    * At least on Apollo sr10.1 it seems continuing system calls
491    * after signal is the default. The following 'siginterrupt'
492    * should change that default to interrupting calls.
493    */
494   siginterrupt(SIGALRM, 1);
495 #endif
496 }
497
498 /*
499  * open_debugfile
500  *
501  * If the -t option is not given on the command line when the server is
502  * started, all debugging output is sent to the file set by LPATH in config.h
503  * Here we just open that file and make sure it is opened to fd 2 so that
504  * any fprintf's to stderr also goto the logfile.  If the debuglevel is not
505  * set from the command line by -x, use /dev/null as the dummy logfile as long
506  * as DEBUGMODE has been defined, else dont waste the fd.
507  */
508 static void open_debugfile(void)
509 {
510 #ifdef  DEBUGMODE
511   int fd;
512   aClient *cptr;
513
514   if (debuglevel >= 0)
515   {
516     cptr = make_client(NULL, STAT_LOG);
517     cptr->fd = 2;
518     cptr->port = debuglevel;
519     cptr->flags = 0;
520     cptr->acpt = cptr;
521     loc_clients[2] = cptr;
522     strcpy(cptr->sockhost, me.name);
523
524     printf("isatty = %d ttyname = %#x\n", isatty(2), (unsigned int)ttyname(2));
525     if (!(bootopt & BOOT_TTY))  /* leave debugging output on fd 2 */
526     {
527       if ((fd = creat(LOGFILE, 0600)) < 0)
528         if ((fd = open("/dev/null", O_WRONLY)) < 0)
529           exit(-1);
530       if (fd != 2)
531       {
532         dup2(fd, 2);
533         close(fd);
534       }
535       strncpy(cptr->name, LOGFILE, sizeof(cptr->name));
536       cptr->name[sizeof(cptr->name) - 1] = 0;
537     }
538     else if (isatty(2) && ttyname(2))
539     {
540       strncpy(cptr->name, ttyname(2), sizeof(cptr->name));
541       cptr->name[sizeof(cptr->name) - 1] = 0;
542     }
543     else
544       strcpy(cptr->name, "FD2-Pipe");
545     Debug((DEBUG_FATAL, "Debug: File <%s> Level: %u at %s",
546         cptr->name, cptr->port, myctime(now)));
547   }
548   else
549     loc_clients[2] = NULL;
550 #endif
551   return;
552 }
553
554 int main(int argc, char *argv[])
555 {
556   unsigned short int portarg = 0;
557   uid_t uid;
558   uid_t euid;
559   time_t delay = 0;
560 #if defined(HAVE_SETRLIMIT) && defined(RLIMIT_CORE)
561   struct rlimit corelim;
562 #endif
563
564   uid = getuid();
565   euid = geteuid();
566   now = time(NULL);
567 #ifdef PROFIL
568   monstartup(0, etext);
569   moncontrol(1);
570   signal(SIGUSR1, s_monitor);
571 #endif
572
573 #ifdef CHROOTDIR
574   if (chdir(DPATH))
575   {
576     fprintf(stderr, "Fail: Cannot chdir(%s): %s\n", DPATH, strerror(errno));
577     exit(-1);
578   }
579   res_init();
580   if (chroot(DPATH))
581   {
582     fprintf(stderr, "Fail: Cannot chroot(%s): %s\n", DPATH, strerror(errno));
583     exit(5);
584   }
585   dpath = "/";
586 #endif /*CHROOTDIR */
587
588   myargv = argv;
589   umask(077);                   /* better safe than sorry --SRB */
590   memset(&me, 0, sizeof(me));
591 #ifdef VIRTUAL_HOST
592   memset(&vserv, 0, sizeof(vserv));
593 #endif
594
595   setup_signals();
596   initload();
597
598 #if defined(HAVE_SETRLIMIT) && defined(RLIMIT_CORE)
599   if (getrlimit(RLIMIT_CORE, &corelim))
600   {
601     fprintf(stderr, "Read of rlimit core size failed: %s\n", strerror(errno));
602     corelim.rlim_max = RLIM_INFINITY;   /* Try to recover */
603   }
604   corelim.rlim_cur = corelim.rlim_max;
605   if (setrlimit(RLIMIT_CORE, &corelim))
606     fprintf(stderr, "Setting rlimit core size failed: %s\n", strerror(errno));
607 #endif
608
609   /*
610    * All command line parameters have the syntax "-fstring"
611    * or "-f string" (e.g. the space is optional). String may
612    * be empty. Flag characters cannot be concatenated (like
613    * "-fxyz"), it would conflict with the form "-fstring".
614    */
615   while (--argc > 0 && (*++argv)[0] == '-')
616   {
617     char *p = argv[0] + 1;
618     int flag = *p++;
619
620     if (flag == '\0' || *p == '\0')
621     {
622       if (argc > 1 && argv[1][0] != '-')
623       {
624         p = *++argv;
625         argc -= 1;
626       }
627       else
628         p = "";
629     }
630
631     switch (flag)
632     {
633       case 'a':
634         bootopt |= BOOT_AUTODIE;
635         break;
636       case 'c':
637         bootopt |= BOOT_CONSOLE;
638         break;
639       case 'q':
640         bootopt |= BOOT_QUICK;
641         break;
642       case 'd':
643         if (euid != uid)
644           setuid((uid_t) uid);
645         dpath = p;
646         break;
647 #ifdef CMDLINE_CONFIG
648       case 'f':
649         if (euid != uid)
650           setuid((uid_t) uid);
651         configfile = p;
652         break;
653 #endif
654       case 'h':
655         strncpy(me.name, p, sizeof(me.name));
656         me.name[sizeof(me.name) - 1] = 0;
657         break;
658       case 'i':
659         bootopt |= BOOT_INETD | BOOT_AUTODIE;
660         break;
661       case 'p':
662         if ((portarg = atoi(p)) > 0)
663           portnum = portarg;
664         break;
665       case 't':
666         if (euid != uid)
667           setuid((uid_t) uid);
668         bootopt |= BOOT_TTY;
669         break;
670       case 'v':
671         printf("ircd %s\n", version);
672         exit(0);
673 #ifdef VIRTUAL_HOST
674       case 'w':
675       {
676         struct hostent *hep;
677         if (!(hep = gethostbyname(p)))
678         {
679           fprintf(stderr, "%s: Error creating virtual host \"%s\": %d",
680               argv[0], p, h_errno);
681           return -1;
682         }
683         if (hep->h_addrtype == AF_INET && hep->h_addr_list[0] &&
684             !hep->h_addr_list[1])
685         {
686           memcpy(&vserv.sin_addr, hep->h_addr_list[0], sizeof(struct in_addr));
687           vserv.sin_family = AF_INET;
688         }
689         else
690         {
691           fprintf(stderr, "%s: Error creating virtual host \"%s\": "
692               "Use -w <IP-number of interface>\n", argv[0], p);
693           return -1;
694         }
695         break;
696       }
697 #endif
698       case 'x':
699 #ifdef  DEBUGMODE
700         if (euid != uid)
701           setuid((uid_t) uid);
702         debuglevel = atoi(p);
703         debugmode = *p ? p : "0";
704         bootopt |= BOOT_DEBUG;
705         break;
706 #else
707         fprintf(stderr, "%s: DEBUGMODE must be defined for -x y\n", myargv[0]);
708         exit(0);
709 #endif
710       default:
711         bad_command();
712         break;
713     }
714   }
715
716   if (chdir(dpath))
717   {
718     fprintf(stderr, "Fail: Cannot chdir(%s): %s\n", dpath, strerror(errno));
719     exit(-1);
720   }
721
722 #ifndef IRC_UID
723   if ((uid != euid) && !euid)
724   {
725     fprintf(stderr,
726         "ERROR: do not run ircd setuid root. Make it setuid a normal user.\n");
727     exit(-1);
728   }
729 #endif
730
731 #if !defined(CHROOTDIR) || (defined(IRC_UID) && defined(IRC_GID))
732 #ifndef _AIX
733   if (euid != uid)
734   {
735     setuid((uid_t) uid);
736     setuid((uid_t) euid);
737   }
738 #endif
739
740   if ((int)getuid() == 0)
741   {
742 #if defined(IRC_UID) && defined(IRC_GID)
743
744     /* run as a specified user */
745     fprintf(stderr, "WARNING: running ircd with uid = %d\n", IRC_UID);
746     fprintf(stderr, "         changing to gid %d.\n", IRC_GID);
747     setuid(IRC_UID);
748     setgid(IRC_GID);
749 #else
750     /* check for setuid root as usual */
751     fprintf(stderr,
752         "ERROR: do not run ircd setuid root. Make it setuid a normal user.\n");
753     exit(-1);
754 #endif
755   }
756 #endif /*CHROOTDIR/UID/GID */
757
758   if (argc > 0)
759     return bad_command();       /* This should exit out */
760
761 #if HAVE_UNISTD_H
762   /* Sanity checks */
763   {
764     char c;
765     char *path;
766
767     c = 'S';
768     path = SPATH;
769     if (access(path, X_OK) == 0)
770     {
771       c = 'C';
772       path = CPATH;
773       if (access(path, R_OK) == 0)
774       {
775         c = 'M';
776         path = MPATH;
777         if (access(path, R_OK) == 0)
778         {
779           c = 'R';
780           path = RPATH;
781           if (access(path, R_OK) == 0)
782           {
783 #ifndef DEBUG
784             c = 0;
785 #else
786             c = 'L';
787             path = LPATH;
788             if (access(path, W_OK) == 0)
789               c = 0;
790 #endif
791           }
792         }
793       }
794     }
795     if (c)
796     {
797       fprintf(stderr, "Check on %cPATH (%s) failed: %s\n",
798           c, path, strerror(errno));
799       fprintf(stderr,
800           "Please create file and/or rerun `make config' and recompile to correct this.\n");
801 #ifdef CHROOTDIR
802       fprintf(stderr,
803           "Keep in mind that all paths are relative to CHROOTDIR.\n");
804 #endif
805       exit(-1);
806     }
807   }
808 #endif
809
810   hash_init();
811 #ifdef DEBUGMODE
812   initlists();
813 #endif
814   initclass();
815   initwhowas();
816   initmsgtree();
817   initstats();
818   open_debugfile();
819   if (portnum == 0)
820     portnum = PORTNUM;
821   me.port = portnum;
822   init_sys();
823   me.flags = FLAGS_LISTEN;
824   if ((bootopt & BOOT_INETD))
825   {
826     me.fd = 0;
827     loc_clients[0] = &me;
828     me.flags = FLAGS_LISTEN;
829   }
830   else
831     me.fd = -1;
832
833 #ifdef USE_SYSLOG
834   openlog(myargv[0], LOG_PID | LOG_NDELAY, LOG_FACILITY);
835 #endif
836   if (initconf(bootopt) == -1)
837   {
838     Debug((DEBUG_FATAL, "Failed in reading configuration file %s", configfile));
839     printf("Couldn't open configuration file %s\n", configfile);
840     exit(-1);
841   }
842   get_my_name(&me);
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   now = time(NULL);
863   me.hopcount = 0;
864   me.authfd = -1;
865   me.confs = NULL;
866   me.next = NULL;
867   me.user = NULL;
868   me.from = &me;
869   SetMe(&me);
870   make_server(&me);
871   /* Abuse own link timestamp as start timestamp: */
872   me.serv->timestamp = TStime();
873   me.serv->prot = atoi(MAJOR_PROTOCOL);
874   me.serv->up = &me;
875   me.serv->down = NULL;
876
877   SetYXXCapacity(&me, MAXCLIENTS);
878
879   me.lasttime = me.since = me.firsttime = now;
880   hAddClient(&me);
881
882   check_class();
883   write_pidfile();
884
885   init_counters();
886
887   Debug((DEBUG_NOTICE, "Server ready..."));
888 #ifdef USE_SYSLOG
889   syslog(LOG_NOTICE, "Server Ready");
890 #endif
891
892   for (;;)
893   {
894     /*
895      * We only want to connect if a connection is due,
896      * not every time through.   Note, if there are no
897      * active C lines, this call to Tryconnections is
898      * made once only; it will return 0. - avalon
899      */
900     if (nextconnect && now >= nextconnect)
901       nextconnect = try_connections();
902     /*
903      * DNS checks. One to timeout queries, one for cache expiries.
904      */
905     if (now >= nextdnscheck)
906       nextdnscheck = timeout_query_list();
907     if (now >= nextexpire)
908       nextexpire = expire_cache();
909     /*
910      * Take the smaller of the two 'timed' event times as
911      * the time of next event (stops us being late :) - avalon
912      * WARNING - nextconnect can return 0!
913      */
914     if (nextconnect)
915       delay = MIN(nextping, nextconnect);
916     else
917       delay = nextping;
918     delay = MIN(nextdnscheck, delay);
919     delay = MIN(nextexpire, delay);
920     delay -= now;
921     /*
922      * Adjust delay to something reasonable [ad hoc values]
923      * (one might think something more clever here... --msa)
924      * We don't really need to check that often and as long
925      * as we don't delay too long, everything should be ok.
926      * waiting too long can cause things to timeout...
927      * i.e. PINGS -> a disconnection :(
928      * - avalon
929      */
930     if (delay < 1)
931       delay = 1;
932     else
933       delay = MIN(delay, TIMESEC);
934     read_message(delay);
935
936     Debug((DEBUG_DEBUG, "Got message(s)"));
937
938     /*
939      * ...perhaps should not do these loops every time,
940      * but only if there is some chance of something
941      * happening (but, note that conf->hold times may
942      * be changed elsewhere--so precomputed next event
943      * time might be too far away... (similarly with
944      * ping times) --msa
945      */
946     if (now >= nextping)
947       nextping = check_pings();
948
949     if (dorehash)
950     {
951       rehash(&me, 1);
952       dorehash = 0;
953     }
954     if (restartFlag)
955       server_reboot();
956   }
957 }