Author: Run <carlo@alinoe.com>
[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 #include <sys/socket.h>         /* Needed for AF_INET on some OS */
52 #include "h.h"
53 #include "res.h"
54 #include "struct.h"
55 #include "s_serv.h"
56 #include "send.h"
57 #include "ircd.h"
58 #include "s_conf.h"
59 #include "class.h"
60 #include "s_misc.h"
61 #include "parse.h"
62 #include "match.h"
63 #include "s_bsd.h"
64 #include "crule.h"
65 #include "userload.h"
66 #include "numeric.h"
67 #include "hash.h"
68 #include "bsd.h"
69 #include "version.h"
70 #include "whowas.h"
71 #include "numnicks.h"
72
73 RCSTAG_CC("$Id$");
74
75 extern void init_counters(void);
76
77 aClient me;                     /* That's me */
78 aClient *client = &me;          /* Pointer to beginning of Client list */
79 time_t TSoffset = 0;            /* Global variable; Offset of timestamps to
80                                    system clock */
81
82 char **myargv;
83 unsigned short int portnum = 0; /* Server port number, listening this */
84 char *configfile = CPATH;       /* Server configuration file */
85 int debuglevel = -1;            /* Server debug level */
86 unsigned int bootopt = 0;       /* Server boot option flags */
87 char *debugmode = "";           /*  -"-    -"-   -"-  */
88 int dorehash = 0;
89 int restartFlag = 0;
90 static char *dpath = DPATH;
91
92 time_t nextconnect = 1;         /* time for next try_connections call */
93 time_t nextping = 1;            /* same as above for check_pings() */
94 time_t nextdnscheck = 0;        /* next time to poll dns to force timeouts */
95 time_t nextexpire = 1;          /* next expire run on the dns cache */
96
97 time_t now;                     /* Updated every time we leave select(),
98                                    and used everywhere else */
99
100 #ifdef PROFIL
101 extern etext(void);
102
103 RETSIGTYPE s_monitor(HANDLER_ARG(int UNUSED(sig)))
104 {
105   static int mon = 0;
106 #ifdef POSIX_SIGNALS
107   struct sigaction act;
108 #endif
109
110   moncontrol(mon);
111   mon = 1 - mon;
112 #ifdef POSIX_SIGNALS
113   act.sa_handler = s_rehash;
114   act.sa_flags = 0;
115   sigemptyset(&act.sa_mask);
116   sigaddset(&act.sa_mask, SIGUSR1);
117   sigaction(SIGUSR1, &act, NULL);
118 #else
119   signal(SIGUSR1, s_monitor);
120 #endif
121 }
122
123 #endif
124
125 RETSIGTYPE s_die(HANDLER_ARG(int UNUSED(sig)))
126 {
127 #ifdef  USE_SYSLOG
128   syslog(LOG_CRIT, "Server Killed By SIGTERM");
129 #endif
130   flush_connections(me.fd);
131   exit(-1);
132 }
133
134 static RETSIGTYPE s_rehash(HANDLER_ARG(int UNUSED(sig)))
135 {
136 #ifdef  POSIX_SIGNALS
137   struct sigaction act;
138 #endif
139   dorehash = 1;
140 #ifdef  POSIX_SIGNALS
141   act.sa_handler = s_rehash;
142   act.sa_flags = 0;
143   sigemptyset(&act.sa_mask);
144   sigaddset(&act.sa_mask, SIGHUP);
145   sigaction(SIGHUP, &act, NULL);
146 #else
147   signal(SIGHUP, s_rehash);     /* sysV -argv */
148 #endif
149 }
150
151 #ifdef  USE_SYSLOG
152 void restart(char *mesg)
153 #else
154 void restart(char *UNUSED(mesg))
155 #endif
156 {
157 #ifdef  USE_SYSLOG
158   syslog(LOG_WARNING, "Restarting Server because: %s", mesg);
159 #endif
160   server_reboot();
161 }
162
163 RETSIGTYPE s_restart(HANDLER_ARG(int UNUSED(sig)))
164 {
165   restartFlag = 1;
166 }
167
168 void server_reboot(void)
169 {
170   Reg1 int i;
171
172   sendto_ops("Aieeeee!!!  Restarting server...");
173   Debug((DEBUG_NOTICE, "Restarting server..."));
174   flush_connections(me.fd);
175   /*
176    * fd 0 must be 'preserved' if either the -d or -i options have
177    * been passed to us before restarting.
178    */
179 #ifdef USE_SYSLOG
180   closelog();
181 #endif
182   for (i = 3; i < MAXCONNECTIONS; i++)
183     close(i);
184   if (!(bootopt & (BOOT_TTY | BOOT_DEBUG)))
185     close(2);
186   close(1);
187   if ((bootopt & BOOT_CONSOLE) || isatty(0))
188     close(0);
189   if (!(bootopt & BOOT_INETD))
190     execv(SPATH, myargv);
191 #ifdef USE_SYSLOG
192   /* Have to reopen since it has been closed above */
193
194   openlog(myargv[0], LOG_PID | LOG_NDELAY, LOG_FACILITY);
195   syslog(LOG_CRIT, "execv(%s,%s) failed: %m\n", SPATH, myargv[0]);
196   closelog();
197 #endif
198   Debug((DEBUG_FATAL, "Couldn't restart server \"%s\": %s",
199       SPATH, strerror(errno)));
200   exit(-1);
201 }
202
203 /*
204  * try_connections
205  *
206  * Scan through configuration and try new connections.
207  *
208  * Returns the calendar time when the next call to this
209  * function should be made latest. (No harm done if this
210  * is called earlier or later...)
211  */
212 static time_t try_connections(void)
213 {
214   Reg1 aConfItem *aconf;
215   Reg2 aClient *cptr;
216   aConfItem **pconf;
217   int connecting, confrq;
218   time_t next = 0;
219   aConfClass *cltmp;
220   aConfItem *cconf, *con_conf = NULL;
221   unsigned int con_class = 0;
222
223   connecting = FALSE;
224   Debug((DEBUG_NOTICE, "Connection check at   : %s", myctime(now)));
225   for (aconf = conf; aconf; aconf = aconf->next)
226   {
227     /* Also when already connecting! (update holdtimes) --SRB */
228     if (!(aconf->status & CONF_CONNECT_SERVER) || aconf->port == 0)
229       continue;
230     cltmp = aconf->confClass;
231     /*
232      * Skip this entry if the use of it is still on hold until
233      * future. Otherwise handle this entry (and set it on hold
234      * until next time). Will reset only hold times, if already
235      * made one successfull connection... [this algorithm is
236      * a bit fuzzy... -- msa >;) ]
237      */
238
239     if ((aconf->hold > now))
240     {
241       if ((next > aconf->hold) || (next == 0))
242         next = aconf->hold;
243       continue;
244     }
245
246     confrq = get_con_freq(cltmp);
247     aconf->hold = now + confrq;
248     /*
249      * Found a CONNECT config with port specified, scan clients
250      * and see if this server is already connected?
251      */
252     cptr = FindServer(aconf->name);
253
254     if (!cptr && (Links(cltmp) < MaxLinks(cltmp)) &&
255         (!connecting || (ConClass(cltmp) > con_class)))
256     {
257       /* Check connect rules to see if we're allowed to try */
258       for (cconf = conf; cconf; cconf = cconf->next)
259         if ((cconf->status & CONF_CRULE) &&
260             (match(cconf->host, aconf->name) == 0))
261           if (crule_eval(cconf->passwd))
262             break;
263       if (!cconf)
264       {
265         con_class = ConClass(cltmp);
266         con_conf = aconf;
267         /* We connect only one at time... */
268         connecting = TRUE;
269       }
270     }
271     if ((next > aconf->hold) || (next == 0))
272       next = aconf->hold;
273   }
274   if (connecting)
275   {
276     if (con_conf->next)         /* are we already last? */
277     {
278       /* Put the current one at the end and make sure we try all connections */
279       for (pconf = &conf; (aconf = *pconf); pconf = &(aconf->next))
280         if (aconf == con_conf)
281           *pconf = aconf->next;
282       (*pconf = con_conf)->next = 0;
283     }
284     if (connect_server(con_conf, (aClient *)NULL, (struct hostent *)NULL) == 0)
285       sendto_ops("Connection to %s[%s] activated.",
286           con_conf->name, con_conf->host);
287   }
288   Debug((DEBUG_NOTICE, "Next connection check : %s", myctime(next)));
289   return (next);
290 }
291
292 static time_t check_pings(void)
293 {
294   Reg1 aClient *cptr;
295   int ping = 0, i, rflag = 0;
296   time_t oldest = 0, timeout;
297
298   for (i = 0; i <= highest_fd; i++)
299   {
300     if (!(cptr = loc_clients[i]) || IsMe(cptr) || IsLog(cptr) || IsPing(cptr))
301       continue;
302
303     /*
304      * Note: No need to notify opers here.
305      * It's already done when "FLAGS_DEADSOCKET" is set.
306      */
307     if (IsDead(cptr))
308     {
309       exit_client(cptr, cptr, &me, LastDeadComment(cptr));
310       continue;
311     }
312
313 #if defined(R_LINES) && defined(R_LINES_OFTEN)
314     rflag = IsUser(cptr) ? find_restrict(cptr) : 0;
315 #endif
316     ping = IsRegistered(cptr) ? get_client_ping(cptr) : CONNECTTIMEOUT;
317     Debug((DEBUG_DEBUG, "c(%s)=%d p %d r %d a %d",
318         cptr->name, cptr->status, ping, rflag, (int)(now - cptr->lasttime)));
319     /*
320      * Ok, so goto's are ugly and can be avoided here but this code
321      * is already indented enough so I think its justified. -avalon
322      */
323     if (!rflag && IsRegistered(cptr) && (ping >= now - cptr->lasttime))
324       goto ping_timeout;
325     /*
326      * If the server hasnt talked to us in 2*ping seconds
327      * and it has a ping time, then close its connection.
328      * If the client is a user and a KILL line was found
329      * to be active, close this connection too.
330      */
331     if (rflag ||
332         ((now - cptr->lasttime) >= (2 * ping) &&
333         (cptr->flags & FLAGS_PINGSENT)) ||
334         (!IsRegistered(cptr) && !IsHandshake(cptr) &&
335         (now - cptr->firsttime) >= ping))
336     {
337       if (!IsRegistered(cptr) && (DoingDNS(cptr) || DoingAuth(cptr)))
338       {
339         Debug((DEBUG_NOTICE, "%s/%s timeout %s", DoingDNS(cptr) ? "DNS" : "",
340             DoingAuth(cptr) ? "AUTH" : "", get_client_name(cptr, TRUE)));
341         if (cptr->authfd >= 0)
342         {
343           close(cptr->authfd);
344           cptr->authfd = -1;
345           cptr->count = 0;
346           *cptr->buffer = '\0';
347         }
348         del_queries((char *)cptr);
349         ClearAuth(cptr);
350         ClearDNS(cptr);
351         SetAccess(cptr);
352         cptr->firsttime = now;
353         cptr->lasttime = now;
354         continue;
355       }
356       if (IsServer(cptr) || IsConnecting(cptr) || IsHandshake(cptr))
357       {
358         sendto_ops("No response from %s, closing link",
359             get_client_name(cptr, FALSE));
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             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.sockhost);
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 have_server_port;
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   memset(&vserv, 0, sizeof(vserv));
594   vserv.sin_family = AF_INET;
595   vserv.sin_addr.s_addr = htonl(INADDR_ANY);
596   memset(&cserv, 0, sizeof(cserv));
597   cserv.sin_addr.s_addr = htonl(INADDR_ANY);
598   cserv.sin_family = AF_INET;
599
600   setup_signals();
601   initload();
602
603 #if defined(HAVE_SETRLIMIT) && defined(RLIMIT_CORE)
604   if (getrlimit(RLIMIT_CORE, &corelim))
605   {
606     fprintf(stderr, "Read of rlimit core size failed: %s\n", strerror(errno));
607     corelim.rlim_max = RLIM_INFINITY;   /* Try to recover */
608   }
609   corelim.rlim_cur = corelim.rlim_max;
610   if (setrlimit(RLIMIT_CORE, &corelim))
611     fprintf(stderr, "Setting rlimit core size failed: %s\n", strerror(errno));
612 #endif
613
614   /*
615    * All command line parameters have the syntax "-fstring"
616    * or "-f string" (e.g. the space is optional). String may
617    * be empty. Flag characters cannot be concatenated (like
618    * "-fxyz"), it would conflict with the form "-fstring".
619    */
620   while (--argc > 0 && (*++argv)[0] == '-')
621   {
622     char *p = argv[0] + 1;
623     int flag = *p++;
624
625     if (flag == '\0' || *p == '\0')
626     {
627       if (argc > 1 && argv[1][0] != '-')
628       {
629         p = *++argv;
630         argc -= 1;
631       }
632       else
633         p = "";
634     }
635
636     switch (flag)
637     {
638       case 'a':
639         bootopt |= BOOT_AUTODIE;
640         break;
641       case 'c':
642         bootopt |= BOOT_CONSOLE;
643         break;
644       case 'q':
645         bootopt |= BOOT_QUICK;
646         break;
647       case 'd':
648         if (euid != uid)
649           setuid((uid_t) uid);
650         dpath = p;
651         break;
652 #ifdef CMDLINE_CONFIG
653       case 'f':
654         if (euid != uid)
655           setuid((uid_t) uid);
656         configfile = p;
657         break;
658 #endif
659       case 'h':
660         strncpy(me.name, p, sizeof(me.name));
661         me.name[sizeof(me.name) - 1] = 0;
662         break;
663       case 'i':
664         bootopt |= BOOT_INETD | BOOT_AUTODIE;
665         break;
666       case 'p':
667         if ((portarg = atoi(p)) > 0)
668           portnum = portarg;
669         break;
670       case 't':
671         if (euid != uid)
672           setuid((uid_t) uid);
673         bootopt |= BOOT_TTY;
674         break;
675       case 'v':
676         printf("ircd %s\n", version);
677         exit(0);
678       case 'w':
679       {
680         struct hostent *hep;
681         if (!(hep = gethostbyname(p)))
682         {
683           fprintf(stderr, "%s: Error resolving \"%s\" (h_errno == %d).\n",
684               argv[-1], p, h_errno);
685           return -1;
686         }
687         if (hep->h_addrtype == AF_INET && hep->h_addr_list[0] &&
688             !hep->h_addr_list[1])
689         {
690           int fd;
691           memcpy(&vserv.sin_addr, hep->h_addr_list[0], sizeof(struct in_addr));
692           memcpy(&cserv.sin_addr, hep->h_addr_list[0], sizeof(struct in_addr));
693           /* Test if we can bind to this address */
694           fd = socket(AF_INET, SOCK_STREAM, 0);
695           if (bind(fd, (struct sockaddr *)&vserv, sizeof(vserv)) == 0)
696           {
697             close(fd);
698             break;
699           }
700         }
701         fprintf(stderr, "%s:\tError binding to interface \"%s\".\n"
702             "   \tUse `ifconfig -a' to check your interfaces.\n", argv[-1], p);
703         return -1;
704       }
705       case 'x':
706 #ifdef  DEBUGMODE
707         if (euid != uid)
708           setuid((uid_t) uid);
709         debuglevel = atoi(p);
710         debugmode = *p ? p : "0";
711         bootopt |= BOOT_DEBUG;
712         break;
713 #else
714         fprintf(stderr, "%s: DEBUGMODE must be defined for -x y\n", myargv[0]);
715         exit(0);
716 #endif
717       default:
718         bad_command();
719         break;
720     }
721   }
722
723   if (chdir(dpath))
724   {
725     fprintf(stderr, "Fail: Cannot chdir(%s): %s\n", dpath, strerror(errno));
726     exit(-1);
727   }
728
729 #ifndef IRC_UID
730   if ((uid != euid) && !euid)
731   {
732     fprintf(stderr,
733         "ERROR: do not run ircd setuid root. Make it setuid a normal user.\n");
734     exit(-1);
735   }
736 #endif
737
738 #if !defined(CHROOTDIR) || (defined(IRC_UID) && defined(IRC_GID))
739 #ifndef _AIX
740   if (euid != uid)
741   {
742     setuid((uid_t) uid);
743     setuid((uid_t) euid);
744   }
745 #endif
746
747   if ((int)getuid() == 0)
748   {
749 #if defined(IRC_UID) && defined(IRC_GID)
750
751     /* run as a specified user */
752     fprintf(stderr, "WARNING: running ircd with uid = %d\n", IRC_UID);
753     fprintf(stderr, "         changing to gid %d.\n", IRC_GID);
754     setuid(IRC_UID);
755     setgid(IRC_GID);
756 #else
757     /* check for setuid root as usual */
758     fprintf(stderr,
759         "ERROR: do not run ircd setuid root. Make it setuid a normal user.\n");
760     exit(-1);
761 #endif
762   }
763 #endif /*CHROOTDIR/UID/GID */
764
765   if (argc > 0)
766     return bad_command();       /* This should exit out */
767
768 #if HAVE_UNISTD_H
769   /* Sanity checks */
770   {
771     char c;
772     char *path;
773
774     c = 'S';
775     path = SPATH;
776     if (access(path, X_OK) == 0)
777     {
778       c = 'C';
779       path = CPATH;
780       if (access(path, R_OK) == 0)
781       {
782         c = 'M';
783         path = MPATH;
784         if (access(path, R_OK) == 0)
785         {
786           c = 'R';
787           path = RPATH;
788           if (access(path, R_OK) == 0)
789           {
790 #ifndef DEBUG
791             c = 0;
792 #else
793             c = 'L';
794             path = LPATH;
795             if (access(path, W_OK) == 0)
796               c = 0;
797 #endif
798           }
799         }
800       }
801     }
802     if (c)
803     {
804       fprintf(stderr, "Check on %cPATH (%s) failed: %s\n",
805           c, path, strerror(errno));
806       fprintf(stderr,
807           "Please create file and/or rerun `make config' and recompile to correct this.\n");
808 #ifdef CHROOTDIR
809       fprintf(stderr,
810           "Keep in mind that all paths are relative to CHROOTDIR.\n");
811 #endif
812       exit(-1);
813     }
814   }
815 #endif
816
817   hash_init();
818 #ifdef DEBUGMODE
819   initlists();
820 #endif
821   initclass();
822   initwhowas();
823   initmsgtree();
824   initstats();
825   open_debugfile();
826   init_sys();
827   me.flags = FLAGS_LISTEN;
828   if ((bootopt & BOOT_INETD))
829   {
830     me.fd = 0;
831     loc_clients[0] = &me;
832     me.flags = FLAGS_LISTEN;
833   }
834   else
835     me.fd = -1;
836
837 #ifdef USE_SYSLOG
838   openlog(myargv[0], LOG_PID | LOG_NDELAY, LOG_FACILITY);
839 #endif
840   if (initconf(bootopt) == -1)
841   {
842     Debug((DEBUG_FATAL, "Failed in reading configuration file %s", configfile));
843     printf("Couldn't open configuration file %s\n", configfile);
844     exit(-1);
845   }
846   if (!(bootopt & BOOT_INETD))
847   {
848     Debug((DEBUG_ERROR, "Port = %u", portnum));
849     if (!have_server_port && inetport(&me, "*", "", portnum))
850       exit(1);
851   }
852   else if (inetport(&me, "*", "*", 0))
853     exit(1);
854
855   read_tlines();
856   rmotd = read_motd(RPATH);
857   motd = read_motd(MPATH);
858   setup_ping();
859   get_my_name(&me, me.sockhost, sizeof(me.sockhost) - 1);
860   now = time(NULL);
861   me.hopcount = 0;
862   me.authfd = -1;
863   me.confs = NULL;
864   me.next = NULL;
865   me.user = NULL;
866   me.from = &me;
867   SetMe(&me);
868   make_server(&me);
869   /* Abuse own link timestamp as start timestamp: */
870   me.serv->timestamp = TStime();
871   me.serv->prot = atoi(MAJOR_PROTOCOL);
872   me.serv->up = &me;
873   me.serv->down = NULL;
874
875   SetYXXCapacity(&me, MAXCLIENTS);
876
877   me.lasttime = me.since = me.firsttime = now;
878   hAddClient(&me);
879
880   check_class();
881   write_pidfile();
882
883   init_counters();
884
885   Debug((DEBUG_NOTICE, "Server ready..."));
886 #ifdef USE_SYSLOG
887   syslog(LOG_NOTICE, "Server Ready");
888 #endif
889
890   for (;;)
891   {
892     /*
893      * We only want to connect if a connection is due,
894      * not every time through.   Note, if there are no
895      * active C lines, this call to Tryconnections is
896      * made once only; it will return 0. - avalon
897      */
898     if (nextconnect && now >= nextconnect)
899       nextconnect = try_connections();
900     /*
901      * DNS checks. One to timeout queries, one for cache expiries.
902      */
903     if (now >= nextdnscheck)
904       nextdnscheck = timeout_query_list();
905     if (now >= nextexpire)
906       nextexpire = expire_cache();
907     /*
908      * Take the smaller of the two 'timed' event times as
909      * the time of next event (stops us being late :) - avalon
910      * WARNING - nextconnect can return 0!
911      */
912     if (nextconnect)
913       delay = MIN(nextping, nextconnect);
914     else
915       delay = nextping;
916     delay = MIN(nextdnscheck, delay);
917     delay = MIN(nextexpire, delay);
918     delay -= now;
919     /*
920      * Adjust delay to something reasonable [ad hoc values]
921      * (one might think something more clever here... --msa)
922      * We don't really need to check that often and as long
923      * as we don't delay too long, everything should be ok.
924      * waiting too long can cause things to timeout...
925      * i.e. PINGS -> a disconnection :(
926      * - avalon
927      */
928     if (delay < 1)
929       delay = 1;
930     else
931       delay = MIN(delay, TIMESEC);
932     read_message(delay);
933
934     Debug((DEBUG_DEBUG, "Got message(s)"));
935
936     /*
937      * ...perhaps should not do these loops every time,
938      * but only if there is some chance of something
939      * happening (but, note that conf->hold times may
940      * be changed elsewhere--so precomputed next event
941      * time might be too far away... (similarly with
942      * ping times) --msa
943      */
944     if (now >= nextping)
945       nextping = check_pings();
946
947     if (dorehash)
948     {
949       rehash(&me, 1);
950       dorehash = 0;
951     }
952     if (restartFlag)
953       server_reboot();
954   }
955 }