]> arthur.barton.de Git - ngircd-alex.git/blob - src/ngircd/ngircd.c
Improve documentation for --syslog
[ngircd-alex.git] / src / ngircd / ngircd.c
1 /*
2  * ngIRCd -- The Next Generation IRC Daemon
3  * Copyright (c)2001-2022 Alexander Barton (alex@barton.de) and Contributors.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  * Please read the file COPYING, README and AUTHORS for more information.
10  */
11
12 #define GLOBAL_INIT
13 #include "portab.h"
14
15 /**
16  * @file
17  * The main program, including the C function main() which is called
18  * by the loader of the operating system.
19  */
20
21 #include <assert.h>
22 #include <errno.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
27 #include <time.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <fcntl.h>
31 #include <pwd.h>
32 #include <grp.h>
33
34 #if defined(DEBUG) && defined(HAVE_MTRACE)
35 #include <mcheck.h>
36 #endif
37
38 #include "conn.h"
39 #include "class.h"
40 #include "channel.h"
41 #include "conf.h"
42 #include "log.h"
43 #include "sighandlers.h"
44 #include "io.h"
45
46 #include "ngircd.h"
47
48 static void Show_Version PARAMS(( void ));
49 static void Show_Help PARAMS(( void ));
50
51 static void Pidfile_Create PARAMS(( pid_t pid ));
52 static void Pidfile_Delete PARAMS(( void ));
53
54 static void Fill_Version PARAMS(( void ));
55
56 static void Random_Init PARAMS(( void ));
57
58 static void Setup_FDStreams PARAMS(( int fd ));
59
60 static bool NGIRCd_Init PARAMS(( bool ));
61
62
63 /**
64  * The main() function of ngIRCd.
65  *
66  * Here all starts: this function is called by the operating system loader,
67  * it is the first portion of code executed of ngIRCd.
68  *
69  * @param argc The number of arguments passed to ngIRCd on the command line.
70  * @param argv An array containing all the arguments passed to ngIRCd.
71  * @return Global exit code of ngIRCd, zero on success.
72  */
73 GLOBAL int
74 main(int argc, const char *argv[])
75 {
76         bool ok, configtest = false;
77         bool NGIRCd_NoDaemon = false, NGIRCd_NoSyslog = false;
78         int i;
79         size_t n;
80
81 #if defined(DEBUG) && defined(HAVE_MTRACE)
82         /* enable GNU libc memory tracing when running in debug mode
83          * and functionality available */
84         mtrace();
85 #endif
86
87         umask(0077);
88
89         NGIRCd_SignalQuit = NGIRCd_SignalRestart = false;
90         NGIRCd_Passive = false;
91 #ifdef DEBUG
92         NGIRCd_Debug = false;
93 #endif
94 #ifdef SNIFFER
95         NGIRCd_Sniffer = false;
96 #endif
97         strlcpy(NGIRCd_ConfFile, SYSCONFDIR, sizeof(NGIRCd_ConfFile));
98         strlcat(NGIRCd_ConfFile, CONFIG_FILE, sizeof(NGIRCd_ConfFile));
99
100         Fill_Version();
101
102         /* parse conmmand line */
103         for (i = 1; i < argc; i++) {
104                 ok = false;
105                 if (argv[i][0] == '-' && argv[i][1] == '-') {
106                         /* long option */
107                         if (strcmp(argv[i], "--config") == 0) {
108                                 if (i + 1 < argc) {
109                                         /* Ok, there's an parameter left */
110                                         strlcpy(NGIRCd_ConfFile, argv[i+1],
111                                                 sizeof(NGIRCd_ConfFile));
112                                         /* next parameter */
113                                         i++; ok = true;
114                                 }
115                         }
116                         if (strcmp(argv[i], "--configtest") == 0) {
117                                 configtest = true;
118                                 ok = true;
119                         }
120 #ifdef DEBUG
121                         if (strcmp(argv[i], "--debug") == 0) {
122                                 NGIRCd_Debug = true;
123                                 ok = true;
124                         }
125 #endif
126                         if (strcmp(argv[i], "--help") == 0) {
127                                 Show_Version();
128                                 puts(""); Show_Help( ); puts( "" );
129                                 exit(0);
130                         }
131                         if (strcmp(argv[i], "--nodaemon") == 0) {
132                                 NGIRCd_NoDaemon = true;
133                                 NGIRCd_NoSyslog = true;
134                                 ok = true;
135                         }
136                         if (strcmp(argv[i], "--passive") == 0) {
137                                 NGIRCd_Passive = true;
138                                 ok = true;
139                         }
140 #ifdef SNIFFER
141                         if (strcmp(argv[i], "--sniffer") == 0) {
142                                 NGIRCd_Sniffer = true;
143                                 ok = true;
144                         }
145 #endif
146 #ifdef SYSLOG
147                         if (strcmp(argv[i], "--syslog") == 0) {
148                                 NGIRCd_NoSyslog = false;
149                                 ok = true;
150                         }
151 #endif
152                         if (strcmp(argv[i], "--version") == 0) {
153                                 Show_Version();
154                                 exit(0);
155                         }
156                 }
157                 else if(argv[i][0] == '-' && argv[i][1] != '-') {
158                         /* short option */
159                         for (n = 1; n < strlen(argv[i]); n++) {
160                                 ok = false;
161 #ifdef DEBUG
162                                 if (argv[i][n] == 'd') {
163                                         NGIRCd_Debug = true;
164                                         ok = true;
165                                 }
166 #endif
167                                 if (argv[i][n] == 'f') {
168                                         if (!argv[i][n+1] && i+1 < argc) {
169                                                 /* Ok, next character is a blank */
170                                                 strlcpy(NGIRCd_ConfFile, argv[i+1],
171                                                         sizeof(NGIRCd_ConfFile));
172
173                                                 /* go to the following parameter */
174                                                 i++;
175                                                 n = strlen(argv[i]);
176                                                 ok = true;
177                                         }
178                                 }
179
180                                 if (argv[i][n] == 'h') {
181                                         Show_Version();
182                                         puts(""); Show_Help(); puts("");
183                                         exit(1);
184                                 }
185
186                                 if (argv[i][n] == 'n') {
187                                         NGIRCd_NoDaemon = true;
188                                         NGIRCd_NoSyslog = true;
189                                         ok = true;
190                                 }
191                                 if (argv[i][n] == 'p') {
192                                         NGIRCd_Passive = true;
193                                         ok = true;
194                                 }
195 #ifdef SNIFFER
196                                 if (argv[i][n] == 's') {
197                                         NGIRCd_Sniffer = true;
198                                         ok = true;
199                                 }
200 #endif
201                                 if (argv[i][n] == 't') {
202                                         configtest = true;
203                                         ok = true;
204                                 }
205
206                                 if (argv[i][n] == 'V') {
207                                         Show_Version();
208                                         exit(1);
209                                 }
210 #ifdef SYSLOG
211                                 if (argv[i][n] == 'y') {
212                                         NGIRCd_NoSyslog = false;
213                                         ok = true;
214                                 }
215 #endif
216
217                                 if (!ok) {
218                                         fprintf(stderr,
219                                                 "%s: invalid option \"-%c\"!\n",
220                                                 PACKAGE_NAME, argv[i][n]);
221                                         fprintf(stderr,
222                                                 "Try \"%s --help\" for more information.\n",
223                                                 PACKAGE_NAME);
224                                         exit(2);
225                                 }
226                         }
227
228                 }
229                 if (!ok) {
230                         fprintf(stderr, "%s: invalid option \"%s\"!\n",
231                                 PACKAGE_NAME, argv[i]);
232                         fprintf(stderr, "Try \"%s --help\" for more information.\n",
233                                 PACKAGE_NAME);
234                         exit(2);
235                 }
236         }
237
238         /* Debug level for "VERSION" command */
239         NGIRCd_DebugLevel[0] = '\0';
240 #ifdef DEBUG
241         if (NGIRCd_Debug)
242                 strcpy(NGIRCd_DebugLevel, "1");
243 #endif
244 #ifdef SNIFFER
245         if (NGIRCd_Sniffer) {
246                 NGIRCd_Debug = true;
247                 strcpy(NGIRCd_DebugLevel, "2");
248         }
249 #endif
250
251         if (configtest) {
252                 Show_Version(); puts("");
253                 exit(Conf_Test());
254         }
255
256         while (!NGIRCd_SignalQuit) {
257                 /* Initialize global variables */
258                 NGIRCd_Start = time(NULL);
259                 (void)strftime(NGIRCd_StartStr, 64,
260                                "%a %b %d %Y at %H:%M:%S (%Z)",
261                                localtime(&NGIRCd_Start));
262
263                 NGIRCd_SignalRestart = false;
264                 NGIRCd_SignalQuit = false;
265
266                 Log_Init(!NGIRCd_NoSyslog);
267                 Random_Init();
268                 Conf_Init();
269                 Log_ReInit();
270
271                 /* Initialize the "main program":
272                  * chroot environment, user and group ID, ... */
273                 if (!NGIRCd_Init(NGIRCd_NoDaemon)) {
274                         Log(LOG_ALERT, "Fatal: Initialization failed, exiting!");
275                         exit(1);
276                 }
277
278                 if (!io_library_init(CONNECTION_POOL)) {
279                         Log(LOG_ALERT,
280                             "Fatal: Could not initialize IO routines: %s",
281                             strerror(errno));
282                         exit(1);
283                 }
284
285                 if (!Signals_Init()) {
286                         Log(LOG_ALERT,
287                             "Fatal: Could not set up signal handlers: %s",
288                             strerror(errno));
289                         exit(1);
290                 }
291
292                 Channel_Init();
293                 Conn_Init();
294                 Class_Init();
295                 Client_Init();
296
297                 /* Create protocol and server identification. The syntax
298                  * used by ngIRCd in PASS commands and the known "extended
299                  * flags" are described in doc/Protocol.txt. */
300 #ifdef IRCPLUS
301                 snprintf(NGIRCd_ProtoID, sizeof NGIRCd_ProtoID, "%s%s %s|%s:%s",
302                          PROTOVER, PROTOIRCPLUS, PACKAGE_NAME, PACKAGE_VERSION,
303                          IRCPLUSFLAGS);
304 #ifdef ZLIB
305                 strlcat(NGIRCd_ProtoID, "Z", sizeof NGIRCd_ProtoID);
306 #endif
307                 if (Conf_OperCanMode)
308                         strlcat(NGIRCd_ProtoID, "o", sizeof NGIRCd_ProtoID);
309 #else /* IRCPLUS */
310                 snprintf(NGIRCd_ProtoID, sizeof NGIRCd_ProtoID, "%s%s %s|%s",
311                          PROTOVER, PROTOIRC, PACKAGE_NAME, PACKAGE_VERSION);
312 #endif /* IRCPLUS */
313                 strlcat(NGIRCd_ProtoID, " P", sizeof NGIRCd_ProtoID);
314 #ifdef ZLIB
315                 strlcat(NGIRCd_ProtoID, "Z", sizeof NGIRCd_ProtoID);
316 #endif
317                 LogDebug("Protocol and server ID is \"%s\".", NGIRCd_ProtoID);
318
319                 Channel_InitPredefined();
320
321                 if (Conn_InitListeners() < 1) {
322                         Log(LOG_ALERT,
323                             "Server isn't listening on a single port!" );
324                         Log(LOG_ALERT,
325                             "%s exiting due to fatal errors!", PACKAGE_NAME);
326                         Pidfile_Delete();
327                         exit(1);
328                 }
329
330                 /* Main Run Loop */
331                 Conn_Handler();
332
333                 Conn_Exit();
334                 Client_Exit();
335                 Channel_Exit();
336                 Class_Exit();
337                 Log_Exit();
338                 Signals_Exit();
339         }
340         Pidfile_Delete();
341
342         return 0;
343 } /* main */
344
345
346 /**
347  * Generate ngIRCd "version strings".
348  *
349  * The ngIRCd version information is generated once and then stored in the
350  * NGIRCd_Version and NGIRCd_VersionAddition string variables for further
351  * usage, for example by the IRC command "VERSION" and the --version command
352  * line switch.
353  */
354 static void
355 Fill_Version(void)
356 {
357         NGIRCd_VersionAddition[0] = '\0';
358
359 #ifdef ICONV
360         if (NGIRCd_VersionAddition[0])
361                 strlcat(NGIRCd_VersionAddition, "+",
362                         sizeof NGIRCd_VersionAddition);
363         strlcat(NGIRCd_VersionAddition, "CHARCONV",
364                 sizeof NGIRCd_VersionAddition);
365 #endif
366 #ifdef DEBUG
367         if (NGIRCd_VersionAddition[0])
368                 strlcat(NGIRCd_VersionAddition, "+",
369                         sizeof NGIRCd_VersionAddition);
370         strlcat(NGIRCd_VersionAddition, "DEBUG",
371                 sizeof NGIRCd_VersionAddition);
372 #endif
373 #ifdef IDENTAUTH
374         if (NGIRCd_VersionAddition[0])
375                 strlcat(NGIRCd_VersionAddition, "+",
376                         sizeof NGIRCd_VersionAddition);
377         strlcat(NGIRCd_VersionAddition, "IDENT",
378                 sizeof NGIRCd_VersionAddition);
379 #endif
380 #ifdef WANT_IPV6
381         if (NGIRCd_VersionAddition[0])
382                 strlcat(NGIRCd_VersionAddition, "+",
383                         sizeof(NGIRCd_VersionAddition));
384         strlcat(NGIRCd_VersionAddition, "IPv6",
385                 sizeof(NGIRCd_VersionAddition));
386 #endif
387 #ifdef IRCPLUS
388         if (NGIRCd_VersionAddition[0])
389                 strlcat(NGIRCd_VersionAddition, "+",
390                         sizeof NGIRCd_VersionAddition);
391         strlcat(NGIRCd_VersionAddition, "IRCPLUS",
392                 sizeof NGIRCd_VersionAddition);
393 #endif
394 #ifdef PAM
395         if (NGIRCd_VersionAddition[0])
396                 strlcat(NGIRCd_VersionAddition, "+",
397                         sizeof NGIRCd_VersionAddition);
398         strlcat(NGIRCd_VersionAddition, "PAM",
399                 sizeof NGIRCd_VersionAddition);
400 #endif
401 #ifdef STRICT_RFC
402         if (NGIRCd_VersionAddition[0])
403                 strlcat(NGIRCd_VersionAddition, "+",
404                         sizeof NGIRCd_VersionAddition);
405         strlcat(NGIRCd_VersionAddition, "RFC",
406                 sizeof NGIRCd_VersionAddition);
407 #endif
408 #ifdef SNIFFER
409         if (NGIRCd_VersionAddition[0])
410                 strlcat(NGIRCd_VersionAddition, "+",
411                         sizeof NGIRCd_VersionAddition);
412         strlcat(NGIRCd_VersionAddition, "SNIFFER",
413                 sizeof NGIRCd_VersionAddition);
414 #endif
415 #ifdef SSL_SUPPORT
416         if (NGIRCd_VersionAddition[0])
417                 strlcat(NGIRCd_VersionAddition, "+",
418                         sizeof NGIRCd_VersionAddition);
419         strlcat(NGIRCd_VersionAddition, "SSL",
420                 sizeof NGIRCd_VersionAddition);
421 #endif
422 #ifdef SYSLOG
423         if (NGIRCd_VersionAddition[0])
424                 strlcat(NGIRCd_VersionAddition, "+",
425                         sizeof NGIRCd_VersionAddition);
426         strlcat(NGIRCd_VersionAddition, "SYSLOG",
427                 sizeof NGIRCd_VersionAddition);
428 #endif
429 #ifdef TCPWRAP
430         if (NGIRCd_VersionAddition[0])
431                 strlcat(NGIRCd_VersionAddition, "+",
432                         sizeof NGIRCd_VersionAddition);
433         strlcat(NGIRCd_VersionAddition, "TCPWRAP",
434                 sizeof NGIRCd_VersionAddition);
435 #endif
436 #ifdef ZLIB
437         if (NGIRCd_VersionAddition[0])
438                 strlcat(NGIRCd_VersionAddition, "+",
439                         sizeof NGIRCd_VersionAddition);
440         strlcat(NGIRCd_VersionAddition, "ZLIB",
441                 sizeof NGIRCd_VersionAddition);
442 #endif
443         if (NGIRCd_VersionAddition[0])
444                 strlcat(NGIRCd_VersionAddition, "-",
445                         sizeof(NGIRCd_VersionAddition));
446
447         strlcat(NGIRCd_VersionAddition, HOST_CPU,
448                 sizeof(NGIRCd_VersionAddition));
449         strlcat(NGIRCd_VersionAddition, "/", sizeof(NGIRCd_VersionAddition));
450         strlcat(NGIRCd_VersionAddition, HOST_VENDOR,
451                 sizeof(NGIRCd_VersionAddition));
452         strlcat(NGIRCd_VersionAddition, "/", sizeof(NGIRCd_VersionAddition));
453         strlcat(NGIRCd_VersionAddition, HOST_OS,
454                 sizeof(NGIRCd_VersionAddition));
455
456         snprintf(NGIRCd_Version, sizeof NGIRCd_Version, "%s %s-%s",
457                  PACKAGE_NAME, PACKAGE_VERSION, NGIRCd_VersionAddition);
458 } /* Fill_Version */
459
460
461 /**
462  * Display copyright and version information of ngIRCd on the console.
463  */
464 static void
465 Show_Version( void )
466 {
467         puts( NGIRCd_Version );
468         puts( "Copyright (c)2001-2022 Alexander Barton (<alex@barton.de>) and Contributors." );
469         puts( "Homepage: <http://ngircd.barton.de/>\n" );
470         puts( "This is free software; see the source for copying conditions. There is NO" );
471         puts( "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." );
472 } /* Show_Version */
473
474
475 /**
476  * Display a short help text on the console.
477  * This help depends on the configuration of the executable and only shows
478  * options that are actually enabled.
479  */
480 static void
481 Show_Help( void )
482 {
483 #ifdef DEBUG
484         puts( "  -d, --debug        log extra debug messages" );
485 #endif
486         puts( "  -f, --config <f>   use file <f> as configuration file" );
487         puts( "  -n, --nodaemon     don't fork and don't detach from controlling terminal" );
488         puts( "  -p, --passive      disable automatic connections to other servers" );
489 #ifdef SNIFFER
490         puts( "  -s, --sniffer      enable network sniffer and display all IRC traffic" );
491 #endif
492         puts( "  -t, --configtest   read, validate and display configuration; then exit" );
493         puts( "  -V, --version      output version information and exit" );
494 #ifdef SYSLOG
495         puts( "  -y, --syslog       log to syslog even when running in the foreground (-n)" );
496 #endif
497         puts( "  -h, --help         display this help and exit" );
498 } /* Show_Help */
499
500
501 /**
502  * Delete the file containing the process ID (PID).
503  */
504 static void
505 Pidfile_Delete( void )
506 {
507         /* Pidfile configured? */
508         if( ! Conf_PidFile[0] ) return;
509
510 #ifdef DEBUG
511         Log( LOG_DEBUG, "Removing PID file (%s) ...", Conf_PidFile );
512 #endif
513
514         if( unlink( Conf_PidFile ))
515                 Log( LOG_ERR, "Error unlinking PID file (%s): %s", Conf_PidFile, strerror( errno ));
516 } /* Pidfile_Delete */
517
518
519 /**
520  * Create the file containing the process ID of ngIRCd ("PID file").
521  *
522  * @param pid   The process ID to be stored in this file.
523  */
524 static void
525 Pidfile_Create(pid_t pid)
526 {
527         int pidfd;
528         char pidbuf[64];
529         int len;
530
531         /* Pidfile configured? */
532         if( ! Conf_PidFile[0] ) return;
533
534 #ifdef DEBUG
535         Log( LOG_DEBUG, "Creating PID file (%s) ...", Conf_PidFile );
536 #endif
537
538         pidfd = open( Conf_PidFile, O_RDWR|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
539         if ( pidfd < 0 ) {
540                 Log( LOG_ERR, "Error writing PID file (%s): %s", Conf_PidFile, strerror( errno ));
541                 return;
542         }
543
544         len = snprintf(pidbuf, sizeof pidbuf, "%ld\n", (long)pid);
545         if (len < 0 || len >= (int)sizeof pidbuf) {
546                 Log(LOG_ERR, "Error converting process ID!");
547                 close(pidfd);
548                 return;
549         }
550
551         if (write(pidfd, pidbuf, (size_t)len) != (ssize_t)len)
552                 Log(LOG_ERR, "Can't write PID file (%s): %s!", Conf_PidFile,
553                     strerror(errno));
554
555         if (close(pidfd) != 0)
556                 Log(LOG_ERR, "Error closing PID file (%s): %s!", Conf_PidFile,
557                     strerror(errno));
558 } /* Pidfile_Create */
559
560
561 /**
562  * Redirect stdin, stdout and stderr to appropriate file handles.
563  *
564  * @param fd    The file handle stdin, stdout and stderr should be redirected to.
565  */
566 static void
567 Setup_FDStreams(int fd)
568 {
569         if (fd < 0)
570                 return;
571
572         fflush(stdout);
573         fflush(stderr);
574
575         /* Create new stdin(0), stdout(1) and stderr(2) descriptors */
576         dup2( fd, 0 ); dup2( fd, 1 ); dup2( fd, 2 );
577 } /* Setup_FDStreams */
578
579
580 #if !defined(SINGLE_USER_OS)
581
582 /**
583  * Get user and group ID of unprivileged "nobody" user.
584  *
585  * @param uid   User ID
586  * @param gid   Group ID
587  * @return      true on success.
588  */
589 static bool
590 NGIRCd_getNobodyID(uid_t *uid, gid_t *gid )
591 {
592         struct passwd *pwd;
593
594 #ifdef __CYGWIN__
595         /* Cygwin kludge.
596          * It can return EINVAL instead of EPERM
597          * so, if we are already unprivileged,
598          * use id of current user.
599          */
600         if (geteuid() && getuid()) {
601                 *uid = getuid();
602                 *gid = getgid();
603                 return true;
604         }
605 #endif
606
607         pwd = getpwnam("nobody");
608         if (!pwd)
609                 return false;
610
611         if (!pwd->pw_uid || !pwd->pw_gid)
612                 return false;
613
614         *uid = pwd->pw_uid;
615         *gid = pwd->pw_gid;
616         endpwent();
617
618         return true;
619 } /* NGIRCd_getNobodyID */
620
621 #endif
622
623
624 #ifdef HAVE_ARC4RANDOM
625 static void
626 Random_Init(void)
627 {
628
629 }
630 #else
631 static bool
632 Random_Init_Kern(const char *file)
633 {
634         unsigned int seed;
635         bool ret = false;
636         int fd = open(file, O_RDONLY);
637         if (fd >= 0) {
638                 if (read(fd, &seed, sizeof(seed)) == sizeof(seed))
639                         ret = true;
640                 close(fd);
641                 srand(seed);
642         }
643         return ret;
644 }
645
646 /**
647  * Initialize libc rand(3) number generator
648  */
649 static void
650 Random_Init(void)
651 {
652         if (Random_Init_Kern("/dev/urandom"))
653                 return;
654         if (Random_Init_Kern("/dev/random"))
655                 return;
656         if (Random_Init_Kern("/dev/arandom"))
657                 return;
658         srand(rand() ^ (unsigned)getpid() ^ (unsigned)time(NULL));
659 }
660 #endif
661
662
663 /**
664  * Initialize ngIRCd daemon.
665  *
666  * @param NGIRCd_NoDaemon Set to true if ngIRCd should run in the
667  *              foreground (and not as a daemon).
668  * @return true on success.
669  */
670 static bool
671 NGIRCd_Init(bool NGIRCd_NoDaemon)
672 {
673         static bool initialized;
674         bool chrooted = false;
675         struct passwd *pwd;
676         struct group *grp;
677         int real_errno, fd = -1;
678         pid_t pid;
679
680         if (initialized)
681                 return true;
682
683         if (!NGIRCd_NoDaemon) {
684                 /* open /dev/null before chroot() */
685                 fd = open( "/dev/null", O_RDWR);
686                 if (fd < 0)
687                         Log(LOG_WARNING, "Could not open /dev/null: %s",
688                             strerror(errno));
689         }
690
691         /* SSL initialization */
692         if (!ConnSSL_InitLibrary()) {
693                 Log(LOG_ERR, "Error during SSL initialization!");
694                 goto out;
695         }
696
697         /* Change root */
698         if (Conf_Chroot[0]) {
699                 if (chdir(Conf_Chroot) != 0) {
700                         Log(LOG_ERR, "Can't chdir() in ChrootDir (%s): %s!",
701                             Conf_Chroot, strerror(errno));
702                         goto out;
703                 }
704
705                 if (chroot(Conf_Chroot) != 0) {
706                         Log(LOG_ERR,
707                             "Can't change root directory to \"%s\": %s!",
708                             Conf_Chroot, strerror(errno));
709                         goto out;
710                 } else {
711                         chrooted = true;
712                         Log(LOG_INFO,
713                             "Changed root and working directory to \"%s\".",
714                             Conf_Chroot);
715                 }
716         }
717
718 #if !defined(SINGLE_USER_OS)
719         /* Check user ID */
720         if (Conf_UID == 0) {
721                 pwd = getpwuid(0);
722                 Log(LOG_INFO,
723                     "ServerUID must not be %s(0), using \"nobody\" instead.",
724                     pwd ? pwd->pw_name : "?");
725                 if (!NGIRCd_getNobodyID(&Conf_UID, &Conf_GID)) {
726                         Log(LOG_WARNING,
727                             "Could not get user/group ID of user \"nobody\": %s",
728                             errno ? strerror(errno) : "not found" );
729                         goto out;
730                 }
731         }
732
733         /* Change group ID */
734         if (getgid() != Conf_GID) {
735                 if (setgid(Conf_GID) != 0) {
736                         real_errno = errno;
737                         grp = getgrgid(Conf_GID);
738                         Log(LOG_ERR, "Can't change group ID to %s(%u): %s!",
739                             grp ? grp->gr_name : "?", Conf_GID,
740                             strerror(real_errno));
741                         if (real_errno != EPERM)
742                                 goto out;
743                 }
744 #ifdef HAVE_SETGROUPS
745                 if (setgroups(0, NULL) != 0) {
746                         real_errno = errno;
747                         Log(LOG_ERR, "Can't drop supplementary group IDs: %s!",
748                                         strerror(errno));
749                         if (real_errno != EPERM)
750                                 goto out;
751                 }
752 #else
753                 Log(LOG_WARNING,
754                     "Can't drop supplementary group IDs: setgroups(3) missing!");
755 #endif
756         }
757 #endif
758
759         /* Change user ID */
760         if (getuid() != Conf_UID) {
761                 if (setuid(Conf_UID) != 0) {
762                         real_errno = errno;
763                         pwd = getpwuid(Conf_UID);
764                         Log(LOG_ERR, "Can't change user ID to %s(%u): %s!",
765                             pwd ? pwd->pw_name : "?", Conf_UID,
766                             strerror(real_errno));
767                         if (real_errno != EPERM)
768                                 goto out;
769                 }
770         }
771
772         initialized = true;
773
774         /* Normally a child process is forked which isn't any longer
775          * connected to ther controlling terminal. Use "--nodaemon"
776          * to disable this "daemon mode" (useful for debugging). */
777         if (!NGIRCd_NoDaemon) {
778                 pid = fork();
779                 if (pid > 0) {
780                         /* "Old" process: exit. */
781                         exit(0);
782                 }
783                 if (pid < 0) {
784                         /* Error!? */
785                         fprintf(stderr,
786                                 "%s: Can't fork: %s!\nFatal error, exiting now ...\n",
787                                 PACKAGE_NAME, strerror(errno));
788                         exit(1);
789                 }
790
791                 /* New child process */
792 #ifdef HAVE_SETSID
793                 (void)setsid();
794 #else
795                 setpgrp(0, getpid());
796 #endif
797                 if (chdir("/") != 0)
798                         Log(LOG_ERR, "Can't change directory to '/': %s!",
799                                      strerror(errno));
800
801                 /* Detach stdin, stdout and stderr */
802                 Setup_FDStreams(fd);
803                 if (fd > 2)
804                         close(fd);
805         }
806         pid = getpid();
807
808         Pidfile_Create(pid);
809
810         /* Check UID/GID we are running as, can be different from values
811          * configured (e. g. if we were already started with a UID>0. */
812         Conf_UID = getuid();
813         Conf_GID = getgid();
814
815         pwd = getpwuid(Conf_UID);
816         grp = getgrgid(Conf_GID);
817
818         Log(LOG_INFO, "Running as user %s(%ld), group %s(%ld), with PID %ld.",
819             pwd ? pwd->pw_name : "unknown", (long)Conf_UID,
820             grp ? grp->gr_name : "unknown", (long)Conf_GID, (long)pid);
821
822         if (chrooted) {
823                 Log(LOG_INFO, "Running with root directory \"%s\".",
824                     Conf_Chroot );
825                 return true;
826         } else
827                 Log(LOG_INFO, "Not running with changed root directory.");
828
829         /* Change working directory to home directory of the user we are
830          * running as (only when running in daemon mode and not in chroot) */
831
832         if (NGIRCd_NoDaemon)
833                 return true;
834
835         if (pwd) {
836                 if (chdir(pwd->pw_dir) == 0)
837                         Log(LOG_DEBUG,
838                             "Changed working directory to \"%s\" ...",
839                             pwd->pw_dir);
840                 else
841                         Log(LOG_ERR,
842                             "Can't change working directory to \"%s\": %s!",
843                             pwd->pw_dir, strerror(errno));
844         } else
845                 Log(LOG_ERR, "Can't get user informaton for UID %d!?", Conf_UID);
846
847         return true;
848  out:
849         if (fd > 2)
850                 close(fd);
851         return false;
852 } /* NGIRCd_Init */
853
854
855 /* -eof- */