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