- wenn ein System sigaction() nicht kennt, so wird nun signal() verwendet.
[ngircd-alex.git] / src / ngircd / ngircd.c
1 /*
2  * ngIRCd -- The Next Generation IRC Daemon
3  * Copyright (c)2001,2002 by Alexander Barton (alex@barton.de)
4  *
5  * Dieses Programm ist freie Software. Sie koennen es unter den Bedingungen
6  * der GNU General Public License (GPL), wie von der Free Software Foundation
7  * herausgegeben, weitergeben und/oder modifizieren, entweder unter Version 2
8  * der Lizenz oder (wenn Sie es wuenschen) jeder spaeteren Version.
9  * Naehere Informationen entnehmen Sie bitter der Datei COPYING. Eine Liste
10  * der an ngIRCd beteiligten Autoren finden Sie in der Datei AUTHORS.
11  *
12  * $Id: ngircd.c,v 1.27 2002/02/25 11:42:47 alex Exp $
13  *
14  * ngircd.c: Hier beginnt alles ;-)
15  *
16  * $Log: ngircd.c,v $
17  * Revision 1.27  2002/02/25 11:42:47  alex
18  * - wenn ein System sigaction() nicht kennt, so wird nun signal() verwendet.
19  *
20  * Revision 1.26  2002/02/23 19:06:47  alex
21  * - fuer SIGCHLD wird nun auch SA_NOCLDWAIT gesetzt, wenn vorhanden.
22  *
23  * Revision 1.25  2002/02/19 20:30:47  alex
24  * - SA_RESTART wird fuer Signale nur noch gesetzt, wenn es definiert ist.
25  *
26  * Revision 1.24  2002/02/19 20:08:24  alex
27  * - "Passive-Mode" implementiert: kein Auto-Conect zu anderen Servern.
28  * - NGIRCd_DebugLevel wird (fuer VERSION-Befehl) ermittelt.
29  *
30  * Revision 1.23  2002/02/17 23:40:21  alex
31  * - neue Funktion NGIRCd_VersionAddition(). NGIRCd_Version() aufgespaltet.
32  *
33  * Revision 1.22  2002/01/22 17:15:39  alex
34  * - die Fehlermeldung "interrupted system call" sollte nicht mehr auftreten.
35  *
36  * Revision 1.21  2002/01/21 00:02:11  alex
37  * - Hilfetexte korrigiert und ergaenzt (Sniffer).
38  *
39  * Revision 1.20  2002/01/18 11:12:11  alex
40  * - der Sniffer wird nun nur noch aktiviert, wenn auf Kommandozeile angegeben.
41  *
42  * Revision 1.19  2002/01/12 00:17:28  alex
43  * - ngIRCd wandelt sich nun selber in einen Daemon (Hintergrundprozess) um.
44  *
45  * Revision 1.18  2002/01/11 14:45:18  alex
46  * - Kommandozeilen-Parser implementiert: Debug- und No-Daemon-Modus, Hilfe.
47  *
48  * Revision 1.17  2002/01/02 02:51:16  alex
49  * - Signal-Handler fuer SIGCHLD: so sollten Zombies nicht mehr vorkommen.
50  *
51  * Revision 1.15  2001/12/31 02:18:51  alex
52  * - viele neue Befehle (WHOIS, ISON, OPER, DIE, RESTART),
53  * - neuen Header "defines.h" mit (fast) allen Konstanten.
54  * - Code Cleanups und viele "kleine" Aenderungen & Bugfixes.
55  *
56  * Revision 1.14  2001/12/30 19:26:12  alex
57  * - Unterstuetzung fuer die Konfigurationsdatei eingebaut.
58  *
59  * Revision 1.13  2001/12/30 11:42:00  alex
60  * - der Server meldet nun eine ordentliche "Start-Zeit".
61  *
62  * Revision 1.12  2001/12/29 03:07:36  alex
63  * - einige Loglevel geaendert.
64  *
65  * Revision 1.11  2001/12/26 14:45:37  alex
66  * - "Code Cleanups".
67  *
68  * Revision 1.10  2001/12/24 01:34:38  alex
69  * - Signal-Handler aufgeraeumt; u.a. SIGPIPE wird nun korrekt ignoriert.
70  *
71  * Revision 1.9  2001/12/21 22:24:50  alex
72  * - neues Modul "parse" wird initialisiert und abgemeldet.
73  *
74  * Revision 1.8  2001/12/14 08:15:26  alex
75  * - neue Module (irc, client, channel) werden an- und abgemeldet.
76  * - zweiter Listen-Socket wird zu Testzwecken konfiguriert.
77  *
78  * Revision 1.7  2001/12/13 01:31:46  alex
79  * - Conn_Handler() wird nun mit einem Timeout aufgerufen.
80  *
81  * Revision 1.6  2001/12/12 23:30:42  alex
82  * - Log-Meldungen an syslog angepasst.
83  * - NGIRCd_Quit ist nun das Flag zum Beenden des ngircd.
84  *
85  * Revision 1.5  2001/12/12 17:21:21  alex
86  * - mehr Unterfunktionen eingebaut, Modul besser strukturiert & dokumentiert.
87  * - Anpassungen an neue Module.
88  *
89  * Revision 1.4  2001/12/12 01:58:53  alex
90  * - Test auf socklen_t verbessert.
91  *
92  * Revision 1.3  2001/12/12 01:40:39  alex
93  * - ein paar mehr Kommentare; Variablennamen verstaendlicher gemacht.
94  * - fehlenden Header <arpa/inet.h> ergaenz.
95  * - SIGINT und SIGQUIT werden nun ebenfalls behandelt.
96  *
97  * Revision 1.2  2001/12/11 22:04:21  alex
98  * - Test auf stdint.h (HAVE_STDINT_H) hinzugefuegt.
99  *
100  * Revision 1.1.1.1  2001/12/11 21:53:04  alex
101  * - Imported sources to CVS.
102  */
103
104
105 #define PORTAB_CHECK_TYPES              /* Prueffunktion einbinden, s.u. */
106
107
108 #include <portab.h>
109 #include "global.h"
110
111 #include <imp.h>
112
113 #include <assert.h>
114 #include <errno.h>
115 #include <stdio.h>
116 #include <signal.h>
117 #include <string.h>
118 #include <unistd.h>
119 #include <sys/types.h>
120 #include <sys/wait.h>
121 #include <time.h>
122
123 #include "channel.h"
124 #include "client.h"
125 #include "conf.h"
126 #include "conn.h"
127 #include "irc.h"
128 #include "log.h"
129 #include "parse.h"
130
131 #include <exp.h>
132 #include "ngircd.h"
133
134
135 LOCAL VOID Initialize_Signal_Handler( VOID );
136 LOCAL VOID Signal_Handler( INT Signal );
137
138 LOCAL VOID Initialize_Listen_Ports( VOID );
139
140 LOCAL VOID Show_Version( VOID );
141 LOCAL VOID Show_Help( VOID );
142
143
144 GLOBAL INT main( INT argc, CONST CHAR *argv[] )
145 {
146         BOOLEAN ok;
147         INT pid, i, n;
148
149         /* Datentypen der portab-Library ueberpruefen */
150         portab_check_types( );
151
152         NGIRCd_Restart = FALSE;
153         NGIRCd_Quit = FALSE;
154         NGIRCd_NoDaemon = FALSE;
155         NGIRCd_Passive = FALSE;
156 #ifdef DEBUG
157         NGIRCd_Debug = FALSE;
158 #endif
159 #ifdef SNIFFER
160         NGIRCd_Sniffer = FALSE;
161 #endif
162
163         /* Kommandozeile parsen */
164         for( i = 1; i < argc; i++ )
165         {
166                 ok = FALSE;
167                 if(( argv[i][0] == '-' ) && ( argv[i][1] == '-' ))
168                 {
169                         /* Lange Option */
170
171 #ifdef DEBUG
172                         if( strcmp( argv[i], "--debug" ) == 0 )
173                         {
174                                 NGIRCd_Debug = TRUE;
175                                 ok = TRUE;
176                         }
177 #endif
178                         if( strcmp( argv[i], "--help" ) == 0 )
179                         {
180                                 Show_Version( ); puts( "" );
181                                 Show_Help( ); puts( "" );
182                                 exit( 1 );
183                         }
184                         if( strcmp( argv[i], "--nodaemon" ) == 0 )
185                         {
186                                 NGIRCd_NoDaemon = TRUE;
187                                 ok = TRUE;
188                         }
189                         if( strcmp( argv[i], "--passive" ) == 0 )
190                         {
191                                 NGIRCd_Passive = TRUE;
192                                 ok = TRUE;
193                         }
194 #ifdef SNIFFER
195                         if( strcmp( argv[i], "--sniffer" ) == 0 )
196                         {
197                                 NGIRCd_Sniffer = TRUE;
198                                 ok = TRUE;
199                         }
200 #endif
201                         if( strcmp( argv[i], "--version" ) == 0 )
202                         {
203                                 Show_Version( );
204                                 exit( 1 );
205                         }
206                 }
207                 else if(( argv[i][0] == '-' ) && ( argv[i][1] != '-' ))
208                 {
209                         /* Kurze Option */
210                         
211                         for( n = 1; n < strlen( argv[i] ); n++ )
212                         {
213                                 ok = FALSE;
214 #ifdef DEBUG
215                                 if( argv[i][n] == 'd' )
216                                 {
217                                         NGIRCd_Debug = TRUE;
218                                         ok = TRUE;
219                                 }
220 #endif
221                                 if( argv[i][n] == 'n' )
222                                 {
223                                         NGIRCd_NoDaemon = TRUE;
224                                         ok = TRUE;
225                                 }
226                                 if( argv[i][n] == 'p' )
227                                 {
228                                         NGIRCd_Passive = TRUE;
229                                         ok = TRUE;
230                                 }
231 #ifdef SNIFFER
232                                 if( argv[i][n] == 's' )
233                                 {
234                                         NGIRCd_Sniffer = TRUE;
235                                         ok = TRUE;
236                                 }
237 #endif
238
239                                 if( ! ok )
240                                 {
241                                         printf( PACKAGE": invalid option \"-%c\"!\n", argv[i][n] );
242                                         puts( "Try \""PACKAGE" --help\" for more information." );
243                                         exit( 1 );
244                                 }
245                         }
246
247                 }
248                 if( ! ok )
249                 {
250                         printf( PACKAGE": invalid option \"%s\"!\n", argv[i] );
251                         puts( "Try \""PACKAGE" --help\" for more information." );
252                         exit( 1 );
253                 }
254         }
255
256         /* Debug-Level (fuer IRC-Befehl "VERSION") ermitteln */
257         strcpy( NGIRCd_DebugLevel, "" );
258 #ifdef DEBUG
259         if( NGIRCd_Debug ) strcpy( NGIRCd_DebugLevel, "1" );
260 #endif
261 #ifdef SNIFFER
262         if( NGIRCd_Sniffer ) strcpy( NGIRCd_DebugLevel, "2" );
263 #endif
264         
265         while( ! NGIRCd_Quit )
266         {
267                 /* In der Regel wird ein Sub-Prozess ge-fork()'t, der
268                  * nicht mehr mit dem Terminal verbunden ist. Mit der
269                  * Option "--nodaemon" kann dies (z.B. zum Debuggen)
270                  * verhindert werden. */
271                 if( ! NGIRCd_NoDaemon )
272                 {
273                         /* Daemon im Hintergrund erzeugen */
274                         pid = fork( );
275                         if( pid > 0 )
276                         {
277                                 /* "alter" Prozess */
278                                 exit( 0 );
279                         }
280                         if( pid < 0 )
281                         {
282                                 /* Fehler */
283                                 printf( PACKAGE": Can't fork: %s!\nFatal error, exiting now ...", strerror( errno ));
284                                 exit( 1 );
285                         }
286                         setsid( );
287                         chdir( "/" );
288                 }
289         
290                 /* Globale Variablen initialisieren */
291                 NGIRCd_Start = time( NULL );
292                 strftime( NGIRCd_StartStr, 64, "%a %b %d %Y at %H:%M:%S (%Z)", localtime( &NGIRCd_Start ));
293                 NGIRCd_Restart = FALSE;
294                 NGIRCd_Quit = FALSE;
295
296                 /* Module initialisieren */
297                 Log_Init( );
298                 Conf_Init( );
299                 Parse_Init( );
300                 IRC_Init( );
301                 Channel_Init( );
302                 Client_Init( );
303                 Conn_Init( );
304
305                 /* Signal-Handler initialisieren */
306                 Initialize_Signal_Handler( );
307
308                 /* Listen-Ports initialisieren */
309                 Initialize_Listen_Ports( );
310
311                 /* Hauptschleife */
312                 while( TRUE )
313                 {
314                         if( NGIRCd_Quit || NGIRCd_Restart ) break;
315                         Conn_Handler( 5 );
316                 }
317
318                 /* Alles abmelden */
319                 Conn_Exit( );
320                 Client_Exit( );
321                 Channel_Exit( );
322                 IRC_Exit( );
323                 Parse_Exit( );
324                 Conf_Exit( );
325                 Log_Exit( );
326         }
327         return 0;
328 } /* main */
329
330
331 GLOBAL CHAR *NGIRCd_Version( VOID )
332 {
333         STATIC CHAR version[126];
334
335         sprintf( version, PACKAGE" version "VERSION"-%s", NGIRCd_VersionAddition( ));
336         return version;
337 } /* NGIRCd_Version */
338
339
340 GLOBAL CHAR *NGIRCd_VersionAddition( VOID )
341 {
342         STATIC CHAR txt[64];
343
344         strcpy( txt, "" );
345
346 #ifdef USE_SYSLOG
347         if( txt[0] ) strcat( txt, "+" );
348         strcat( txt, "SYSLOG" );
349 #endif
350 #ifdef STRICT_RFC
351         if( txt[0] ) strcat( txt, "+" );
352         strcat( txt, "RFC" );
353 #endif
354 #ifdef DEBUG
355         if( txt[0] ) strcat( txt, "+" );
356         strcat( txt, "DEBUG" );
357 #endif
358 #ifdef SNIFFER
359         if( txt[0] ) strcat( txt, "+" );
360         strcat( txt, "SNIFFER" );
361 #endif
362
363         if( txt[0] ) strcat( txt, "-" );
364         strcat( txt, P_OSNAME"/"P_ARCHNAME );
365
366         return txt;
367 } /* NGIRCd_VersionAddition */
368
369
370 LOCAL VOID Initialize_Signal_Handler( VOID )
371 {
372         /* Signal-Handler initialisieren: einige Signale
373          * werden ignoriert, andere speziell behandelt. */
374
375 #ifdef HAVE_SIGACTION
376         /* sigaction() ist vorhanden */
377
378         struct sigaction saction;
379
380         /* Signal-Struktur initialisieren */
381         memset( &saction, 0, sizeof( saction ));
382         saction.sa_handler = Signal_Handler;
383 #ifdef SA_RESTART
384         saction.sa_flags |= SA_RESTART;
385 #endif
386 #ifdef SA_NOCLDWAIT
387         saction.sa_flags |= SA_NOCLDWAIT;
388 #endif
389
390         /* Signal-Handler einhaengen */
391         sigaction( SIGINT, &saction, NULL );
392         sigaction( SIGQUIT, &saction, NULL );
393         sigaction( SIGTERM, &saction, NULL);
394         sigaction( SIGHUP, &saction, NULL);
395         sigaction( SIGCHLD, &saction, NULL);
396
397         /* einige Signale ignorieren */
398         saction.sa_handler = SIG_IGN;
399         sigaction( SIGPIPE, &saction, NULL );
400 #else
401         /* kein sigaction() vorhanden */
402
403         /* Signal-Handler einhaengen */
404         signal( SIGINT, Signal_Handler );
405         signal( SIGQUIT, Signal_Handler );
406         signal( SIGTERM, Signal_Handler );
407         signal( SIGHUP, Signal_Handler );
408         signal( SIGCHLD, Signal_Handler );
409
410         /* einige Signale ignorieren */
411         signal( SIGPIPE, SIG_IGN );
412 #endif
413 } /* Initialize_Signal_Handler */
414
415
416 LOCAL VOID Signal_Handler( INT Signal )
417 {
418         /* Signal-Handler. Dieser wird aufgerufen, wenn eines der Signale eintrifft,
419          * fuer das wir uns registriert haben (vgl. Initialize_Signal_Handler). Die
420          * Nummer des eingetroffenen Signals wird der Funktion uebergeben. */
421
422         switch( Signal )
423         {
424                 case SIGTERM:
425                 case SIGINT:
426                 case SIGQUIT:
427                         /* wir soll(t)en uns wohl beenden ... */
428                         if( Signal == SIGTERM ) Log( LOG_WARNING, "Got TERM signal, terminating now ..." );
429                         else if( Signal == SIGINT ) Log( LOG_WARNING, "Got INT signal, terminating now ..." );
430                         else if( Signal == SIGQUIT ) Log( LOG_WARNING, "Got QUIT signal, terminating now ..." );
431                         NGIRCd_Quit = TRUE;
432                         break;
433                 case SIGHUP:
434                         /* neu starten */
435                         Log( LOG_WARNING, "Got HUP signal, restarting now ..." );
436                         NGIRCd_Restart = TRUE;
437                         break;
438                 case SIGCHLD:
439                         /* Child-Prozess wurde beendet. Zombies vermeiden: */
440                         while( waitpid( -1, NULL, WNOHANG ) > 0);
441                         break;
442                 default:
443                         /* unbekanntes bzw. unbehandeltes Signal */
444                         Log( LOG_NOTICE, "Got signal %d! Ignored.", Signal );
445         }
446 } /* Signal_Handler */
447
448
449 LOCAL VOID Initialize_Listen_Ports( VOID )
450 {
451         /* Ports, auf denen der Server Verbindungen entgegennehmen
452          * soll, initialisieren */
453         
454         INT created, i;
455
456         created = 0;
457         for( i = 0; i < Conf_ListenPorts_Count; i++ )
458         {
459                 if( Conn_NewListener( Conf_ListenPorts[i] )) created++;
460                 else Log( LOG_ERR, "Can't listen on port %d!", Conf_ListenPorts[i] );
461         }
462
463         if( created < 1 )
464         {
465                 Log( LOG_ALERT, "Server isn't listening on a single port!" );
466                 Log( LOG_ALERT, PACKAGE" exiting due to fatal errors!" );
467                 exit( 1 );
468         }
469 } /* Initialize_Listen_Ports */
470
471
472 LOCAL VOID Show_Version( VOID )
473 {
474         puts( NGIRCd_Version( ));
475         puts( "Copyright (c)2001,2002 by Alexander Barton (alex@barton.de).\n" );
476         puts( "This is free software; see the source for copying conditions. There is NO" );
477         puts( "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." );
478 } /* Show_Version */
479
480
481 LOCAL VOID Show_Help( VOID )
482 {
483 #ifdef DEBUG
484         puts( "  -d, --debug       log extra debug messages" );
485 #endif
486         puts( "  -n, --nodaemon    don't fork and don't detatch from controlling terminal" );
487         puts( "  -p, --passive     disable automatic connections to other servers" );
488 #ifdef SNIFFER
489         puts( "  -s, --sniffer     enable network sniffer and display all IRC traffic" );
490 #endif
491         puts( "      --version     output version information and exit" );
492         puts( "      --help        display this help and exit" );
493 } /* Show_Help */
494
495
496 /* -eof- */