Author: Bleep <tomh@inxpress.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  * $Id$
21  */
22 #include "ircd.h"
23 #include "class.h"
24 #include "client.h"
25 #include "crule.h"
26 #include "hash.h"
27 #include "ircd_alloc.h" /* set_nomem_handler */
28 #include "ircd_log.h"
29 #include "ircd_signal.h"
30 #include "ircd_string.h"
31 #include "list.h"
32 #include "listener.h"
33 #include "match.h"
34 #include "numeric.h"
35 #include "numnicks.h"
36 #include "parse.h"
37 #include "res.h"
38 #include "s_auth.h"
39 #include "s_bsd.h"
40 #include "s_conf.h"
41 #include "s_debug.h"
42 #include "s_misc.h"
43 #include "send.h"
44 #include "struct.h"
45 #include "sys.h"
46 #include "userload.h"
47 #include "version.h"
48 #include "whowas.h"
49 #include "msg.h"
50
51 #include <assert.h>
52 #include <errno.h>
53 #include <fcntl.h>
54 #include <pwd.h>
55 #include <stdio.h>
56 #include <stdlib.h>
57 #include <string.h>
58 #include <sys/socket.h>
59 #include <sys/stat.h>
60 #include <unistd.h>
61 #include <netdb.h>
62
63 extern void init_counters(void);
64
65 struct Client  me;                      /* That's me */
66 struct Client* GlobalClientList = &me;  /* Pointer to beginning of Client list */
67 time_t         TSoffset = 0;      /* Offset of timestamps to system clock */
68 int            GlobalRehashFlag = 0;    /* do a rehash if set */
69 int            GlobalRestartFlag = 0;   /* do a restart if set */
70 time_t         CurrentTime;       /* Updated every time we leave select() */
71
72 char **myargv;
73 char *configfile = CPATH;       /* Server configuration file */
74 int debuglevel = -1;            /* Server debug level */
75 unsigned int bootopt = 0;       /* Server boot option flags */
76 char *debugmode = "";           /*  -"-    -"-   -"-  */
77 static char *dpath = DPATH;
78
79 time_t nextconnect = 1;         /* time for next try_connections call */
80 time_t nextping = 1;            /* same as above for check_pings() */
81 time_t nextdnscheck = 0;        /* next time to poll dns to force timeouts */
82 time_t nextexpire = 1;          /* next expire run on the dns cache */
83
84 #ifdef PROFIL
85 extern etext(void);
86 #endif
87
88 static void server_reboot(const char* message)
89 {
90   int i;
91
92   sendto_ops("Restarting server: %s", message);
93   Debug((DEBUG_NOTICE, "Restarting server..."));
94   flush_connections(0);
95   /*
96    * fd 0 must be 'preserved' if either the -d or -i options have
97    * been passed to us before restarting.
98    */
99   close_log();
100
101   for (i = 3; i < MAXCONNECTIONS; i++)
102     close(i);
103   if (!(bootopt & (BOOT_TTY | BOOT_DEBUG)))
104     close(2);
105   close(1);
106   close(0);
107
108   execv(SPATH, myargv);
109
110   /* Have to reopen since it has been closed above */
111   open_log(myargv[0]);
112   ircd_log(L_CRIT, "execv(%s,%s) failed: %m\n", SPATH, myargv[0]);
113
114   Debug((DEBUG_FATAL, "Couldn't restart server \"%s\": %s",
115          SPATH, strerror(errno)));
116   exit(-1);
117 }
118
119 void server_die(const char* message)
120 {
121   ircd_log(L_CRIT, "Server terminating: %s", message);
122   sendto_ops("Server terminating: %s", message);
123   flush_connections(0);
124   exit(-1);
125 }
126
127 void server_restart(const char* message)
128 {
129   static int restarting = 0;
130
131   ircd_log(L_WARNING, "Restarting Server: %s", message);
132   if (restarting == 0) {
133     restarting = 1;
134     server_reboot(message);
135   }
136 }
137
138 static void outofmemory(void)
139 {
140   Debug((DEBUG_FATAL, "Out of memory: restarting server..."));
141   server_restart("Out of Memory");
142
143
144 static void write_pidfile(void)
145 {
146 #ifdef PPATH
147   int fd;
148   char buff[20];
149   if ((fd = open(PPATH, O_CREAT | O_WRONLY, 0600)) >= 0) {
150     memset(buff, 0, sizeof(buff));
151     sprintf(buff, "%5d\n", (int)getpid());
152     if (write(fd, buff, strlen(buff)) == -1)
153       Debug((DEBUG_NOTICE, "Error writing to pid file %s", PPATH));
154     close(fd);
155     return;
156   }
157   Debug((DEBUG_NOTICE, "Error opening pid file \"%s\": %s",
158         PPATH, strerror(errno)));
159 #endif
160 }
161
162 /*
163  * try_connections
164  *
165  * Scan through configuration and try new connections.
166  *
167  * Returns the calendar time when the next call to this
168  * function should be made latest. (No harm done if this
169  * is called earlier or later...)
170  */
171 static time_t try_connections(void)
172 {
173   struct ConfItem *aconf;
174   struct Client *cptr;
175   struct ConfItem **pconf;
176   int connecting, confrq;
177   time_t next = 0;
178   struct ConfClass *cltmp;
179   struct ConfItem *cconf, *con_conf = NULL;
180   unsigned int con_class = 0;
181
182   connecting = FALSE;
183   Debug((DEBUG_NOTICE, "Connection check at   : %s", myctime(CurrentTime)));
184   for (aconf = GlobalConfList; aconf; aconf = aconf->next)
185   {
186     /* Also when already connecting! (update holdtimes) --SRB */
187     if (!(aconf->status & CONF_SERVER) || aconf->port == 0)
188       continue;
189     cltmp = aconf->confClass;
190     /*
191      * Skip this entry if the use of it is still on hold until
192      * future. Otherwise handle this entry (and set it on hold
193      * until next time). Will reset only hold times, if already
194      * made one successfull connection... [this algorithm is
195      * a bit fuzzy... -- msa >;) ]
196      */
197
198     if ((aconf->hold > CurrentTime))
199     {
200       if ((next > aconf->hold) || (next == 0))
201         next = aconf->hold;
202       continue;
203     }
204
205     confrq = get_con_freq(cltmp);
206     aconf->hold = CurrentTime + confrq;
207     /*
208      * Found a CONNECT config with port specified, scan clients
209      * and see if this server is already connected?
210      */
211     cptr = FindServer(aconf->name);
212
213     if (!cptr && (Links(cltmp) < MaxLinks(cltmp)) &&
214         (!connecting || (ConClass(cltmp) > con_class)))
215     {
216       /* Check connect rules to see if we're allowed to try */
217       for (cconf = GlobalConfList; cconf; cconf = cconf->next)
218         if ((cconf->status & CONF_CRULE) &&
219             (match(cconf->host, aconf->name) == 0))
220           if (crule_eval(cconf->passwd))
221             break;
222       if (!cconf)
223       {
224         con_class = ConClass(cltmp);
225         con_conf = aconf;
226         /* We connect only one at time... */
227         connecting = TRUE;
228       }
229     }
230     if ((next > aconf->hold) || (next == 0))
231       next = aconf->hold;
232   }
233   if (connecting)
234   {
235     if (con_conf->next)         /* are we already last? */
236     {
237       /* Put the current one at the end and make sure we try all connections */
238       for (pconf = &GlobalConfList; (aconf = *pconf); pconf = &(aconf->next))
239         if (aconf == con_conf)
240           *pconf = aconf->next;
241       (*pconf = con_conf)->next = 0;
242     }
243     if (connect_server(con_conf, 0, 0))
244       sendto_ops("Connection to %s activated.", con_conf->name);
245   }
246   Debug((DEBUG_NOTICE, "Next connection check : %s", myctime(next)));
247   return (next);
248 }
249
250 static time_t check_pings(void)
251 {
252   struct Client *cptr;
253   int ping = 0;
254   int i;
255   time_t oldest = CurrentTime + PINGFREQUENCY;
256   time_t timeout;
257
258   for (i = 0; i <= HighestFd; i++) {
259     if (!(cptr = LocalClientArray[i]))
260       continue;
261     /*
262      * me is never in the local client array
263      */
264     assert(cptr != &me);
265     /*
266      * Note: No need to notify opers here.
267      * It's already done when "FLAGS_DEADSOCKET" is set.
268      */
269     if (IsDead(cptr)) {
270       exit_client(cptr, cptr, &me, LastDeadComment(cptr));
271       continue;
272     }
273
274     ping = IsRegistered(cptr) ? get_client_ping(cptr) : CONNECTTIMEOUT;
275     Debug((DEBUG_DEBUG, "c(%s)=%d p %d a %d",
276           cptr->name, cptr->status, ping, 
277           (int)(CurrentTime - cptr->lasttime)));
278     /*
279      * Ok, so goto's are ugly and can be avoided here but this code
280      * is already indented enough so I think its justified. -avalon
281      */
282     if (IsRegistered(cptr) && (ping >= CurrentTime - cptr->lasttime))
283       goto ping_timeout;
284     /*
285      * If the server hasnt talked to us in 2 * ping seconds
286      * and it has a ping time, then close its connection.
287      * If the client is a user and a KILL line was found
288      * to be active, close this connection too.
289      */
290     if (((CurrentTime - cptr->lasttime) >= (2 * ping) && (cptr->flags & FLAGS_PINGSENT)) ||
291         (!IsRegistered(cptr) && !IsHandshake(cptr) && (CurrentTime - cptr->firsttime) >= ping))
292     {
293       if (IsServer(cptr) || IsConnecting(cptr) || IsHandshake(cptr))
294       {
295         sendto_ops("No response from %s, closing link", cptr->name);
296         exit_client(cptr, cptr, &me, "Ping timeout");
297         continue;
298       }
299       else {
300         if (!IsRegistered(cptr) && *cptr->name && *cptr->user->username) {
301           sendto_one(cptr,
302               ":%s %d %s :Your client may not be compatible with this server.",
303               me.name, ERR_BADPING, cptr->name);
304           sendto_one(cptr,
305               ":%s %d %s :Compatible clients are available at "
306               "ftp://ftp.undernet.org/pub/irc/clients",
307               me.name, ERR_BADPING, cptr->name);
308         }
309         exit_client_msg(cptr, cptr, &me, "Ping timeout for %s",
310                         get_client_name(cptr, HIDE_IP));
311       }
312       continue;
313     }
314     else if (IsRegistered(cptr) && 0 == (cptr->flags & FLAGS_PINGSENT)) {
315       /*
316        * If we havent PINGed the connection and we havent
317        * heard from it in a while, PING it to make sure
318        * it is still alive.
319        */
320       cptr->flags |= FLAGS_PINGSENT;
321       /*
322        * not nice but does the job
323        */
324       cptr->lasttime = CurrentTime - ping;
325       if (IsUser(cptr))
326         sendto_one(cptr, "PING :%s", me.name);
327       else
328         sendto_one(cptr, "%s " TOK_PING " :%s", NumServ(&me), me.name);
329     }
330 ping_timeout:
331     timeout = cptr->lasttime + ping;
332     while (timeout <= CurrentTime)
333       timeout += ping;
334     if (timeout < oldest)
335       oldest = timeout;
336   }
337   if (oldest < CurrentTime)
338     oldest = CurrentTime + PINGFREQUENCY;
339   Debug((DEBUG_NOTICE,
340         "Next check_ping() call at: %s, %d " TIME_T_FMT " " TIME_T_FMT,
341         myctime(oldest), ping, oldest, CurrentTime));
342
343   return (oldest);
344 }
345
346 /*
347  * bad_command
348  *
349  * This is called when the commandline is not acceptable.
350  * Give error message and exit without starting anything.
351  */
352 static int bad_command(void)
353 {
354   printf("Usage: ircd %s[-h servername] [-x loglevel] [-t]\n",
355 #ifdef CMDLINE_CONFIG
356       "[-f config] "
357 #else
358       ""
359 #endif
360       );
361   printf("Server not started\n\n");
362   return (-1);
363 }
364
365 int main(int argc, char *argv[])
366 {
367   uid_t uid;
368   uid_t euid;
369   time_t delay = 0;
370 #if defined(HAVE_SETRLIMIT) && defined(RLIMIT_CORE)
371   struct rlimit corelim;
372 #endif
373
374   CurrentTime = time(NULL);
375
376   /*
377    * sanity check
378    */
379   if (MAXCONNECTIONS < 64 || MAXCONNECTIONS > 256000) {
380     fprintf(stderr, "%s: MAXCONNECTIONS insane: %d\n", *argv, MAXCONNECTIONS);
381     return 2;
382   }
383     
384   uid = getuid();
385   euid = geteuid();
386 #ifdef PROFIL
387   monstartup(0, etext);
388   moncontrol(1);
389   signal(SIGUSR1, s_monitor);
390 #endif
391
392 #ifdef CHROOTDIR
393   if (chdir(DPATH))
394   {
395     fprintf(stderr, "Fail: Cannot chdir(%s): %s\n", DPATH, strerror(errno));
396     exit(2);
397   }
398   if (chroot(DPATH))
399   {
400     fprintf(stderr, "Fail: Cannot chroot(%s): %s\n", DPATH, strerror(errno));
401     exit(5);
402   }
403   dpath = "/";
404 #endif /*CHROOTDIR */
405
406   myargv = argv;
407   umask(077);                   /* better safe than sorry --SRB */
408   memset(&me, 0, sizeof(me));
409   me.fd = -1;
410
411 #if 0
412 #ifdef VIRTUAL_HOST
413   memset(&vserv, 0, sizeof(vserv));
414 #endif
415 #endif
416
417   setup_signals();
418   initload();
419
420 #if defined(HAVE_SETRLIMIT) && defined(RLIMIT_CORE)
421   if (getrlimit(RLIMIT_CORE, &corelim))
422   {
423     fprintf(stderr, "Read of rlimit core size failed: %s\n", strerror(errno));
424     corelim.rlim_max = RLIM_INFINITY;   /* Try to recover */
425   }
426   corelim.rlim_cur = corelim.rlim_max;
427   if (setrlimit(RLIMIT_CORE, &corelim))
428     fprintf(stderr, "Setting rlimit core size failed: %s\n", strerror(errno));
429 #endif
430
431   /*
432    * All command line parameters have the syntax "-fstring"
433    * or "-f string" (e.g. the space is optional). String may
434    * be empty. Flag characters cannot be concatenated (like
435    * "-fxyz"), it would conflict with the form "-fstring".
436    */
437   while (--argc > 0 && (*++argv)[0] == '-')
438   {
439     char *p = argv[0] + 1;
440     int flag = *p++;
441
442     if (flag == '\0' || *p == '\0')
443     {
444       if (argc > 1 && argv[1][0] != '-')
445       {
446         p = *++argv;
447         argc -= 1;
448       }
449       else
450         p = "";
451     }
452
453     switch (flag)
454     {
455       case 'q':
456         bootopt |= BOOT_QUICK;
457         break;
458       case 'd':
459         if (euid != uid)
460           setuid((uid_t) uid);
461         dpath = p;
462         break;
463 #ifdef CMDLINE_CONFIG
464       case 'f':
465         if (euid != uid)
466           setuid((uid_t) uid);
467         configfile = p;
468         break;
469 #endif
470       case 'h':
471         ircd_strncpy(me.name, p, HOSTLEN);
472         break;
473       case 't':
474         if (euid != uid)
475           setuid((uid_t) uid);
476         bootopt |= BOOT_TTY;
477         break;
478       case 'v':
479         printf("ircd %s\n", version);
480         exit(0);
481 #if 0
482 #ifdef VIRTUAL_HOST
483       case 'w':
484       {
485         struct hostent *hep;
486         if (!(hep = gethostbyname(p)))
487         {
488           fprintf(stderr, "%s: Error creating virtual host \"%s\": %d",
489               argv[0], p, h_errno);
490           return 2;
491         }
492         if (hep->h_addrtype == AF_INET && hep->h_addr_list[0] &&
493             !hep->h_addr_list[1])
494         {
495           memcpy(&vserv.sin_addr, hep->h_addr_list[0], sizeof(struct in_addr));
496           vserv.sin_family = AF_INET;
497         }
498         else
499         {
500           fprintf(stderr, "%s: Error creating virtual host \"%s\": "
501               "Use -w <IP-number of interface>\n", argv[0], p);
502           return 2;
503         }
504         break;
505       }
506 #endif
507 #endif
508       case 'x':
509 #ifdef  DEBUGMODE
510         if (euid != uid)
511           setuid((uid_t) uid);
512         debuglevel = atoi(p);
513         debugmode = *p ? p : "0";
514         bootopt |= BOOT_DEBUG;
515         break;
516 #else
517         fprintf(stderr, "%s: DEBUGMODE must be defined for -x y\n", myargv[0]);
518         exit(0);
519 #endif
520       default:
521         bad_command();
522         break;
523     }
524   }
525
526   if (chdir(dpath))
527   {
528     fprintf(stderr, "Fail: Cannot chdir(%s): %s\n", dpath, strerror(errno));
529     exit(2);
530   }
531
532 #ifndef IRC_UID
533   if ((uid != euid) && !euid)
534   {
535     fprintf(stderr,
536         "ERROR: do not run ircd setuid root. Make it setuid a normal user.\n");
537     exit(2);
538   }
539 #endif
540
541 #if !defined(CHROOTDIR) || (defined(IRC_UID) && defined(IRC_GID))
542   if (euid != uid)
543   {
544     setuid(uid);
545     setuid(euid);
546   }
547
548   if (0 == getuid())
549   {
550 #if defined(IRC_UID) && defined(IRC_GID)
551
552     /* run as a specified user */
553     fprintf(stderr, "WARNING: running ircd with uid = %d\n", IRC_UID);
554     fprintf(stderr, "         changing to gid %d.\n", IRC_GID);
555     setuid(IRC_UID);
556     setgid(IRC_GID);
557 #else
558     /* check for setuid root as usual */
559     fprintf(stderr,
560         "ERROR: do not run ircd setuid root. Make it setuid a normal user.\n");
561     exit(2);
562 #endif
563   }
564 #endif /*CHROOTDIR/UID/GID */
565
566   if (argc > 0)
567     return bad_command();       /* This should exit out */
568
569   /* Sanity checks */
570   {
571     char c;
572     char *path;
573
574     c = 'S';
575     path = SPATH;
576     if (access(path, X_OK) == 0) {
577       c = 'C';
578       path = CPATH;
579       if (access(path, R_OK) == 0) {
580         c = 'M';
581         path = MPATH;
582         if (access(path, R_OK) == 0) {
583           c = 'R';
584           path = RPATH;
585           if (access(path, R_OK) == 0) {
586 #ifndef DEBUG
587             c = 0;
588 #else
589             c = 'L';
590             path = LPATH;
591             if (access(path, W_OK) == 0)
592               c = 0;
593 #endif
594           }
595         }
596       }
597     }
598     if (c)
599     {
600       fprintf(stderr, "Check on %cPATH (%s) failed: %s\n", 
601               c, path, strerror(errno));
602       fprintf(stderr,
603           "Please create file and/or rerun `make config' and recompile to correct this.\n");
604 #ifdef CHROOTDIR
605       fprintf(stderr,
606           "Keep in mind that all paths are relative to CHROOTDIR.\n");
607 #endif
608       exit(2);
609     }
610   }
611
612   init_list();
613   hash_init();
614   initclass();
615   initwhowas();
616   initmsgtree();
617   initstats();
618   open_debugfile();
619   init_sys();
620   set_nomem_handler(outofmemory);
621
622   me.fd = -1;
623
624   open_log(myargv[0]);
625
626   if (initconf(bootopt) == -1) {
627     Debug((DEBUG_FATAL, "Failed in reading configuration file %s", configfile));
628     printf("Couldn't open configuration file %s\n", configfile);
629     exit(2);
630   }
631   if (!init_server_identity()) {
632     Debug((DEBUG_FATAL, "Failed to initialize server identity"));
633     exit(2);
634   }
635   read_tlines();
636   rmotd = read_motd(RPATH);
637   motd = read_motd(MPATH);
638   CurrentTime = time(NULL);
639   me.from = &me;
640   SetMe(&me);
641   make_server(&me);
642   /*
643    * Abuse own link timestamp as start timestamp:
644    */
645   me.serv->timestamp = TStime();
646   me.serv->prot = atoi(MAJOR_PROTOCOL);
647   me.serv->up = &me;
648   me.serv->down = NULL;
649   me.handler = SERVER_HANDLER;
650
651   SetYXXCapacity(&me, MAXCLIENTS);
652
653   me.lasttime = me.since = me.firsttime = CurrentTime;
654   hAddClient(&me);
655
656   check_class();
657   write_pidfile();
658
659   init_counters();
660
661   Debug((DEBUG_NOTICE, "Server ready..."));
662   ircd_log(L_NOTICE, "Server Ready");
663
664   for (;;)
665   {
666     /*
667      * We only want to connect if a connection is due,
668      * not every time through.   Note, if there are no
669      * active C lines, this call to Tryconnections is
670      * made once only; it will return 0. - avalon
671      */
672     if (nextconnect && CurrentTime >= nextconnect)
673       nextconnect = try_connections();
674     /*
675      * DNS checks. One to timeout queries, one for cache expiries.
676      */
677     nextdnscheck = timeout_resolver(CurrentTime);
678     /*
679      * Take the smaller of the two 'timed' event times as
680      * the time of next event (stops us being late :) - avalon
681      * WARNING - nextconnect can return 0!
682      */
683     if (nextconnect)
684       delay = IRCD_MIN(nextping, nextconnect);
685     else
686       delay = nextping;
687     delay = IRCD_MIN(nextdnscheck, delay);
688     delay -= CurrentTime;
689     /*
690      * Adjust delay to something reasonable [ad hoc values]
691      * (one might think something more clever here... --msa)
692      * We don't really need to check that often and as long
693      * as we don't delay too long, everything should be ok.
694      * waiting too long can cause things to timeout...
695      * i.e. PINGS -> a disconnection :(
696      * - avalon
697      */
698     if (delay < 1)
699       delay = 1;
700     else
701       delay = IRCD_MIN(delay, TIMESEC);
702     read_message(delay);
703
704     Debug((DEBUG_DEBUG, "Got message(s)"));
705
706     /*
707      * ...perhaps should not do these loops every time,
708      * but only if there is some chance of something
709      * happening (but, note that conf->hold times may
710      * be changed elsewhere--so precomputed next event
711      * time might be too far away... (similarly with
712      * ping times) --msa
713      */
714     if (CurrentTime >= nextping)
715       nextping = check_pings();
716     
717     /*
718      * timeout pending queries that haven't been responded to
719      */
720     timeout_auth_queries(CurrentTime);
721
722     if (GlobalRehashFlag) {
723       rehash(&me, 1);
724       GlobalRehashFlag = 0;
725     }
726     if (GlobalRestartFlag)
727       server_restart("caught signal: SIGINT");
728   }
729 }