- Added some tools to detect memory leaks.
[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 "config.h"
23
24 #include "ircd.h"
25 #include "IPcheck.h"
26 #include "class.h"
27 #include "client.h"
28 #include "crule.h"
29 #include "destruct_event.h"
30 #include "hash.h"
31 #include "ircd_alloc.h"
32 #include "ircd_events.h"
33 #include "ircd_features.h"
34 #include "ircd_log.h"
35 #include "ircd_reply.h"
36 #include "ircd_signal.h"
37 #include "ircd_string.h"
38 #include "jupe.h"
39 #include "list.h"
40 #include "match.h"
41 #include "motd.h"
42 #include "msg.h"
43 #include "numeric.h"
44 #include "numnicks.h"
45 #include "parse.h"
46 #include "res.h"
47 #include "s_auth.h"
48 #include "s_bsd.h"
49 #include "s_conf.h"
50 #include "s_debug.h"
51 #include "s_misc.h"
52 #include "send.h"
53 #include "sys.h"
54 #include "uping.h"
55 #include "userload.h"
56 #include "version.h"
57 #include "whowas.h"
58
59 #include <assert.h>
60 #include <errno.h>
61 #include <fcntl.h>
62 #include <netdb.h>
63 #include <pwd.h>
64 #include <stdio.h>
65 #include <stdlib.h>
66 #include <string.h>
67 #include <sys/socket.h>
68 #include <sys/stat.h>
69 #include <sys/types.h>
70 #include <unistd.h>
71
72
73
74 /*----------------------------------------------------------------------------
75  * External stuff
76  *--------------------------------------------------------------------------*/
77 extern void init_counters(void);
78 extern void mem_dbg_initialise(void);
79
80 /*----------------------------------------------------------------------------
81  * Constants / Enums
82  *--------------------------------------------------------------------------*/
83 enum {
84   BOOT_DEBUG = 1,
85   BOOT_TTY   = 2
86 };
87
88
89 /*----------------------------------------------------------------------------
90  * Global data (YUCK!)
91  *--------------------------------------------------------------------------*/
92 struct Client  me;                      /* That's me */
93 struct Connection me_con;               /* That's me too */
94 struct Client *GlobalClientList  = &me; /* Pointer to beginning of
95                                            Client list */
96 time_t         TSoffset          = 0;/* Offset of timestamps to system clock */
97 int            GlobalRehashFlag  = 0;   /* do a rehash if set */
98 int            GlobalRestartFlag = 0;   /* do a restart if set */
99 time_t         CurrentTime;          /* Updated every time we leave select() */
100
101 char          *configfile        = CPATH; /* Server configuration file */
102 int            debuglevel        = -1;    /* Server debug level  */
103 char          *debugmode         = "";    /* Server debug level */
104 static char   *dpath             = DPATH;
105
106 static struct Timer connect_timer; /* timer structure for try_connections() */
107 static struct Timer ping_timer; /* timer structure for check_pings() */
108 static struct Timer destruct_event_timer; /* timer structure for exec_expired_destruct_events() */
109
110 static struct Daemon thisServer  = { 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0 };
111
112 int running = 1;
113
114
115 /*----------------------------------------------------------------------------
116  * API: server_die
117  *--------------------------------------------------------------------------*/
118 void server_die(const char* message) {
119   /* log_write will send out message to both log file and as server notice */
120   log_write(LS_SYSTEM, L_CRIT, 0, "Server terminating: %s", message);
121   flush_connections(0);
122   close_connections(1);
123   running = 0;
124 }
125
126
127 /*----------------------------------------------------------------------------
128  * API: server_restart
129  *--------------------------------------------------------------------------*/
130 void server_restart(const char* message) {
131   static int restarting = 0;
132
133   /* inhibit sending any server notices; we may be in a loop */
134   log_write(LS_SYSTEM, L_WARNING, LOG_NOSNOTICE, "Restarting Server: %s",
135             message);
136   if (restarting++) /* increment restarting to prevent looping */
137     return;
138
139   sendto_opmask_butone(0, SNO_OLDSNO, "Restarting server: %s", message);
140   Debug((DEBUG_NOTICE, "Restarting server..."));
141   flush_connections(0);
142
143   log_close();
144
145   close_connections(!(thisServer.bootopt & (BOOT_TTY | BOOT_DEBUG)));
146
147   execv(SPATH, thisServer.argv);
148
149   /* Have to reopen since it has been closed above */
150   log_reopen();
151
152   log_write(LS_SYSTEM, L_CRIT, 0, "execv(%s,%s) failed: %m", SPATH,
153             *thisServer.argv);
154
155   Debug((DEBUG_FATAL, "Couldn't restart server \"%s\": %s",
156          SPATH, (strerror(errno)) ? strerror(errno) : ""));
157   exit(8);
158 }
159
160
161 /*----------------------------------------------------------------------------
162  * outofmemory:  Handler for out of memory conditions...
163  *--------------------------------------------------------------------------*/
164 static void outofmemory(void) {
165   Debug((DEBUG_FATAL, "Out of memory: restarting server..."));
166   server_restart("Out of Memory");
167
168
169
170 /*----------------------------------------------------------------------------
171  * write_pidfile
172  *--------------------------------------------------------------------------*/
173 static void write_pidfile(void) {
174   char buff[20];
175
176   if (thisServer.pid_fd >= 0) {
177     memset(buff, 0, sizeof(buff));
178     sprintf(buff, "%5d\n", (int)getpid());
179     if (write(thisServer.pid_fd, buff, strlen(buff)) == -1)
180       Debug((DEBUG_NOTICE, "Error writing to pid file %s: %m",
181              feature_str(FEAT_PPATH)));
182     return;
183   }
184   Debug((DEBUG_NOTICE, "Error opening pid file %s: %m",
185          feature_str(FEAT_PPATH)));
186 }
187
188 /* check_pid
189  * 
190  * inputs: 
191  *   none
192  * returns:
193  *   true - if the pid file exists (and is readable), and the pid refered
194  *          to in the file is still running.
195  *   false - otherwise.
196  */
197 static int check_pid(void)
198 {
199   struct flock lock;
200
201   lock.l_type = F_WRLCK;
202   lock.l_start = 0;
203   lock.l_whence = SEEK_SET;
204   lock.l_len = 0;
205
206   if ((thisServer.pid_fd = open(feature_str(FEAT_PPATH), O_CREAT | O_RDWR,
207                                 0600)) >= 0)
208     return fcntl(thisServer.pid_fd, F_SETLK, &lock);
209
210   return 0;
211 }
212   
213
214 /*----------------------------------------------------------------------------
215  * try_connections
216  *
217  * Scan through configuration and try new connections.
218  *
219  * Returns the calendar time when the next call to this
220  * function should be made latest. (No harm done if this
221  * is called earlier or later...)
222  *--------------------------------------------------------------------------*/
223 static void try_connections(struct Event* ev) {
224   struct ConfItem*  aconf;
225   struct Client*    cptr;
226   struct ConfItem** pconf;
227   int               connecting;
228   int               confrq;
229   time_t            next        = 0;
230   struct ConnectionClass* cltmp;
231   struct ConfItem*  con_conf    = 0;
232   struct Jupe*      ajupe;
233   const char*       con_class   = NULL;
234
235   assert(ET_EXPIRE == ev_type(ev));
236   assert(0 != ev_timer(ev));
237
238   connecting = FALSE;
239   Debug((DEBUG_NOTICE, "Connection check at   : %s", myctime(CurrentTime)));
240   for (aconf = GlobalConfList; aconf; aconf = aconf->next) {
241     /* Also when already connecting! (update holdtimes) --SRB */
242     if (!(aconf->status & CONF_SERVER) || aconf->port == 0 || aconf->hold == 0)
243       continue;
244
245     /* Also skip juped servers */
246     if ((ajupe = jupe_find(aconf->name)) && JupeIsActive(ajupe))
247       continue;
248
249     /* Skip this entry if the use of it is still on hold until
250      * future. Otherwise handle this entry (and set it on hold until next
251      * time). Will reset only hold times, if already made one successfull
252      * connection... [this algorithm is a bit fuzzy... -- msa >;) ]
253      */
254     if (aconf->hold > CurrentTime && (next > aconf->hold || next == 0)) {
255       next = aconf->hold;
256       continue;
257     }
258
259     cltmp = aconf->conn_class;
260     confrq = get_con_freq(cltmp);
261     if(confrq == 0)
262       aconf->hold = next = 0;
263     else
264       aconf->hold = CurrentTime + confrq;
265
266     /* Found a CONNECT config with port specified, scan clients and see if
267      * this server is already connected?
268      */
269     cptr = FindServer(aconf->name);
270
271     if (!cptr && (Links(cltmp) < MaxLinks(cltmp)) &&
272         (!connecting /*|| (ConClass(cltmp) > con_class)*/)) {
273       /*
274        * Check connect rules to see if we're allowed to try
275        */
276       if (0 == conf_eval_crule(aconf->name, CRULE_MASK)) {
277         con_class = ConClass(cltmp);
278         con_conf = aconf;
279         /* We connect only one at time... */
280         connecting = TRUE;
281       }
282     }
283     if ((next > aconf->hold) || (next == 0))
284       next = aconf->hold;
285   }
286   if (connecting) {
287     if (con_conf->next) { /* are we already last? */
288       /* Put the current one at the end and make sure we try all connections */
289       for (pconf = &GlobalConfList; (aconf = *pconf); pconf = &(aconf->next))
290         if (aconf == con_conf)
291           *pconf = aconf->next;
292       (*pconf = con_conf)->next = 0;
293     }
294
295     if (connect_server(con_conf, 0, 0))
296       sendto_opmask_butone(0, SNO_OLDSNO, "Connection to %s activated.",
297                            con_conf->name);
298   }
299
300   if (next == 0)
301     next = CurrentTime + feature_int(FEAT_CONNECTFREQUENCY);
302
303   Debug((DEBUG_NOTICE, "Next connection check : %s", myctime(next)));
304
305   timer_add(&connect_timer, try_connections, 0, TT_ABSOLUTE, next);
306 }
307
308
309 /*----------------------------------------------------------------------------
310  * check_pings
311  *
312  * TODO: This should be moved out of ircd.c.  It's protocol-specific when you
313  *       get right down to it.  Can't really be done until the server is more
314  *       modular, however...
315  *--------------------------------------------------------------------------*/
316 static void check_pings(struct Event* ev) {
317   int expire     = 0;
318   int next_check = CurrentTime;
319   int max_ping   = 0;
320   int i;
321
322   assert(ET_EXPIRE == ev_type(ev));
323   assert(0 != ev_timer(ev));
324
325   next_check += feature_int(FEAT_PINGFREQUENCY);
326   
327   /* Scan through the client table */
328   for (i=0; i <= HighestFd; i++) {
329     struct Client *cptr = LocalClientArray[i];
330    
331     if (!cptr)
332       continue;
333      
334     assert(&me != cptr);  /* I should never be in the local client array! */
335    
336
337     /* Remove dead clients. */
338     if (IsDead(cptr)) {
339       exit_client(cptr, cptr, &me, cli_info(cptr));
340       continue;
341     }
342
343     max_ping = IsRegistered(cptr) ? client_get_ping(cptr) :
344       feature_int(FEAT_CONNECTTIMEOUT);
345    
346     Debug((DEBUG_DEBUG, "check_pings(%s)=status:%s limit: %d current: %d",
347            cli_name(cptr),
348            (cli_flags(cptr) & FLAGS_PINGSENT) ? "[Ping Sent]" : "[]", 
349            max_ping, (int)(CurrentTime - cli_lasttime(cptr))));
350           
351
352     /* Ok, the thing that will happen most frequently, is that someone will
353      * have sent something recently.  Cover this first for speed.
354      */
355     if (CurrentTime-cli_lasttime(cptr) < max_ping) {
356       expire = cli_lasttime(cptr) + max_ping;
357       if (expire < next_check) 
358         next_check = expire;
359       continue;
360     }
361
362     /* Unregistered clients pingout after max_ping seconds, they don't
363      * get given a second chance - if they were then people could not quite
364      * finish registration and hold resources without being subject to k/g
365      * lines
366      */
367     if (!IsRegistered(cptr)) {
368       /* Display message if they have sent a NICK and a USER but no
369        * nospoof PONG.
370        */
371       if (*(cli_name(cptr)) && cli_user(cptr) && *(cli_user(cptr))->username) {
372         send_reply(cptr, SND_EXPLICIT | ERR_BADPING,
373                    ":Your client may not be compatible with this server.");
374         send_reply(cptr, SND_EXPLICIT | ERR_BADPING,
375                    ":Compatible clients are available at "
376                    URL_CLIENTS);
377       }    
378       exit_client_msg(cptr,cptr,&me, "Ping Timeout");
379       continue;
380     }
381     
382     if (!(cli_flags(cptr) & FLAGS_PINGSENT)) {
383       /* If we havent PINGed the connection and we havent heard from it in a
384        * while, PING it to make sure it is still alive.
385        */
386       cli_flags(cptr) |= FLAGS_PINGSENT;
387
388       /* If we're late in noticing don't hold it against them :) */
389       cli_lasttime(cptr) = CurrentTime - max_ping;
390       
391       if (IsUser(cptr))
392         sendrawto_one(cptr, MSG_PING " :%s", cli_name(&me));
393       else
394         sendcmdto_one(&me, CMD_PING, cptr, ":%s", cli_name(&me));
395     }
396     
397     /* Quit the client after max_ping*2 - they should have answered by now */
398     if (CurrentTime-cli_lasttime(cptr) >= (max_ping*2) ) {
399       /* If it was a server, then tell ops about it. */
400       if (IsServer(cptr) || IsConnecting(cptr) || IsHandshake(cptr))
401         sendto_opmask_butone(0, SNO_OLDSNO,
402                              "No response from %s, closing link",
403                              cli_name(cptr));
404       exit_client_msg(cptr, cptr, &me, "Ping timeout");
405       continue;
406     }
407     
408     expire = cli_lasttime(cptr) + max_ping * 2;
409     if (expire < next_check)
410       next_check=expire;
411   }
412   
413   assert(next_check >= CurrentTime);
414   
415   Debug((DEBUG_DEBUG, "[%i] check_pings() again in %is",
416          CurrentTime, next_check-CurrentTime));
417   
418   timer_add(&ping_timer, check_pings, 0, TT_ABSOLUTE, next_check);
419 }
420
421
422 /*----------------------------------------------------------------------------
423  * parse_command_line
424  * Side Effects: changes GLOBALS me, thisServer, dpath, configfile, debuglevel
425  * debugmode
426  *--------------------------------------------------------------------------*/
427 static void parse_command_line(int argc, char** argv) {
428   const char *options = "d:f:h:ntvx:";
429   int opt;
430
431   if (thisServer.euid != thisServer.uid)
432     setuid(thisServer.uid);
433
434   /* Do we really need to santiy check the non-NULLness of optarg?  That's
435    * getopt()'s job...  Removing those... -zs
436    */
437   while ((opt = getopt(argc, argv, options)) != EOF)
438     switch (opt) {
439     case 'n':
440     case 't':  thisServer.bootopt |= BOOT_TTY;         break;
441     case 'd':  dpath      = optarg;                    break;
442     case 'f':  configfile = optarg;                    break;
443     case 'h':  ircd_strncpy(cli_name(&me), optarg, HOSTLEN); break;
444     case 'v':
445       printf("ircd %s\n", version);
446       printf("Event engines: ");
447 #ifdef USE_KQUEUE
448       printf("kqueue() ");
449 #endif
450 #ifdef USE_DEVPOLL
451       printf("/dev/poll ");
452 #endif
453 #ifdef USE_POLL
454       printf("poll()");
455 #else
456       printf("select()");
457 #endif
458       printf("\nCompiled for a maximum of %d connections.\n", MAXCONNECTIONS);
459
460
461       exit(0);
462       break;
463       
464     case 'x':
465       debuglevel = atoi(optarg);
466       if (debuglevel < 0)
467         debuglevel = 0;
468       debugmode = optarg;
469       thisServer.bootopt |= BOOT_DEBUG;
470       break;
471       
472     default:
473       printf("Usage: ircd [-f config] [-h servername] [-x loglevel] [-ntv]\n");
474       printf("\n -n -t\t Don't detach\n -v\t display version\n\n");
475       printf("Server not started.\n");
476       exit(1);
477     }
478 }
479
480
481 /*----------------------------------------------------------------------------
482  * daemon_init
483  *--------------------------------------------------------------------------*/
484 static void daemon_init(int no_fork) {
485   if (!init_connection_limits())
486     exit(9);
487
488   close_connections(!(thisServer.bootopt & (BOOT_DEBUG | BOOT_TTY)));
489
490   if (no_fork)
491     return;
492
493   if (fork())
494     exit(0);
495
496 #ifdef TIOCNOTTY
497   {
498     int fd;
499     if ((fd = open("/dev/tty", O_RDWR)) > -1) {
500       ioctl(fd, TIOCNOTTY, 0);
501       close(fd);
502     }
503   }
504 #endif
505
506   setsid();
507 }
508
509 /*----------------------------------------------------------------------------
510  * check_file_access:  random helper function to make sure that a file is
511  *                     accessible in a certain way, and complain if not.
512  *--------------------------------------------------------------------------*/
513 static char check_file_access(const char *path, char which, int mode) {
514   if (!access(path, mode))
515     return 1;
516
517   fprintf(stderr, 
518           "Check on %cPATH (%s) failed: %s\n"
519           "Please create this file and/or rerun `configure' "
520           "using --with-%cpath and recompile to correct this.\n",
521           which, path, strerror(errno), which);
522
523   return 0;
524 }
525
526
527 /*----------------------------------------------------------------------------
528  * set_core_limit
529  *--------------------------------------------------------------------------*/
530 #if defined(HAVE_SETRLIMIT) && defined(RLIMIT_CORE)
531 static void set_core_limit(void) {
532   struct rlimit corelim;
533
534   if (getrlimit(RLIMIT_CORE, &corelim)) {
535     fprintf(stderr, "Read of rlimit core size failed: %s\n", strerror(errno));
536     corelim.rlim_max = RLIM_INFINITY;   /* Try to recover */
537   }
538
539   corelim.rlim_cur = corelim.rlim_max;
540   if (setrlimit(RLIMIT_CORE, &corelim))
541     fprintf(stderr, "Setting rlimit core size failed: %s\n", strerror(errno));
542 }
543 #endif
544
545
546
547 /*----------------------------------------------------------------------------
548  * set_userid_if_needed()
549  *--------------------------------------------------------------------------*/
550 static int set_userid_if_needed(void) {
551   if (getuid() == 0 || geteuid() == 0 ||
552       getgid() == 0 || getegid() == 0) {
553     fprintf(stderr, "ERROR:  This server will not run as superuser.\n");
554     return 0;
555   }
556
557   return 1;
558 }
559
560
561 /*----------------------------------------------------------------------------
562  * main - entrypoint
563  *
564  * TODO:  This should set the basic environment up and start the main loop.
565  *        we're doing waaaaaaaaay too much server initialization here.  I hate
566  *        long and ugly control paths...  -smd
567  *--------------------------------------------------------------------------*/
568 int main(int argc, char **argv) {
569   CurrentTime = time(NULL);
570
571   thisServer.argc = argc;
572   thisServer.argv = argv;
573   thisServer.uid  = getuid();
574   thisServer.euid = geteuid();
575
576 #ifdef MDEBUG
577   mem_dbg_initialise();
578 #endif
579
580 #if defined(HAVE_SETRLIMIT) && defined(RLIMIT_CORE)
581   set_core_limit();
582 #endif
583
584   umask(077);                   /* better safe than sorry --SRB */
585   memset(&me, 0, sizeof(me));
586   memset(&me_con, 0, sizeof(me_con));
587   cli_connect(&me) = &me_con;
588   cli_fd(&me) = -1;
589
590   parse_command_line(argc, argv);
591
592   if (chdir(dpath)) {
593     fprintf(stderr, "Fail: Cannot chdir(%s): %s, check DPATH\n", dpath, strerror(errno));
594     return 2;
595   }
596
597   if (!set_userid_if_needed())
598     return 3;
599
600   /* Check paths for accessibility */
601   if (!check_file_access(SPATH, 'S', X_OK) ||
602       !check_file_access(configfile, 'C', R_OK))
603     return 4;
604
605   debug_init(thisServer.bootopt & BOOT_TTY);
606   daemon_init(thisServer.bootopt & BOOT_TTY);
607   event_init(MAXCONNECTIONS);
608
609   setup_signals();
610   feature_init(); /* initialize features... */
611   log_init(*argv);
612   if (check_pid()) {
613     Debug((DEBUG_FATAL, "Failed to acquire PID file lock after fork"));
614     exit(2);
615   }
616   set_nomem_handler(outofmemory);
617   
618   if (!init_string()) {
619     log_write(LS_SYSTEM, L_CRIT, 0, "Failed to initialize string module");
620     return 6;
621   }
622
623   initload();
624   init_list();
625   init_hash();
626   init_class();
627   initwhowas();
628   initmsgtree();
629   initstats();
630
631   init_resolver();
632
633   motd_init();
634
635   if (!init_conf()) {
636     log_write(LS_SYSTEM, L_CRIT, 0, "Failed to read configuration file %s",
637               configfile);
638     return 7;
639   }
640
641   init_server_identity();
642
643   uping_init();
644
645   IPcheck_init();
646   timer_add(timer_init(&connect_timer), try_connections, 0, TT_RELATIVE, 1);
647   timer_add(timer_init(&ping_timer), check_pings, 0, TT_RELATIVE, 1);
648   timer_add(timer_init(&destruct_event_timer), exec_expired_destruct_events, 0, TT_PERIODIC, 60);
649
650   CurrentTime = time(NULL);
651
652   SetMe(&me);
653   cli_magic(&me) = CLIENT_MAGIC;
654   cli_from(&me) = &me;
655   make_server(&me);
656
657   cli_serv(&me)->timestamp = TStime();  /* Abuse own link timestamp as start TS */
658   cli_serv(&me)->prot      = atoi(MAJOR_PROTOCOL);
659   cli_serv(&me)->up        = &me;
660   cli_serv(&me)->down      = NULL;
661   cli_handler(&me)         = SERVER_HANDLER;
662
663   SetYXXCapacity(&me, MAXCLIENTS);
664
665   cli_lasttime(&me) = cli_since(&me) = cli_firsttime(&me) = CurrentTime;
666
667   hAddClient(&me);
668
669   write_pidfile();
670   init_counters();
671
672   Debug((DEBUG_NOTICE, "Server ready..."));
673   log_write(LS_SYSTEM, L_NOTICE, 0, "Server Ready");
674
675   event_loop();
676
677   return 0;
678 }
679
680