Add missing newline to "Forking into the background" display.
[srvx.git] / src / main.c
1 /* main.c - srvx
2  * Copyright 2000-2006 srvx Development Team
3  *
4  * This file is part of srvx.
5  *
6  * srvx 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 2 of the License, or
9  * (at your option) 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 srvx; if not, write to the Free Software Foundation,
18  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.
19  */
20
21 #define PID_FILE "srvx.pid"
22
23 #include "conf.h"
24 #include "gline.h"
25 #include "ioset.h"
26 #include "modcmd.h"
27 #include "saxdb.h"
28 #include "mail.h"
29 #include "timeq.h"
30 #include "sar.h"
31
32 #include "chanserv.h"
33 #include "global.h"
34 #include "modules.h"
35 #include "opserv.h"
36
37 #ifdef HAVE_GETOPT_H
38 #include <getopt.h>
39 #else
40 #include "getopt.h"
41 #endif
42 #ifdef HAVE_SYS_RESOURCE_H
43 #include <sys/resource.h>
44 #endif
45 #ifdef HAVE_NETINET_IN_H
46 #include <netinet/in.h>
47 #endif
48 #ifdef HAVE_SYS_SOCKET_H
49 #include <sys/socket.h>
50 #endif
51 #ifdef HAVE_SYS_WAIT_H
52 #include <sys/wait.h>
53 #endif
54
55 #ifndef SIGCHLD
56 #define SIGCHLD SIGCLD
57 #endif
58
59 #include "main-common.c"
60
61 void sigaction_writedb(int x)
62 {
63 #ifndef HAVE_STRSIGNAL
64     log_module(MAIN_LOG, LOG_INFO, "Signal %d -- writing databases.", x);
65 #else
66     log_module(MAIN_LOG, LOG_INFO, "%s -- writing databases.", strsignal(x));
67 #endif
68     do_write_dbs = 1;
69 }
70
71 void sigaction_exit(int x)
72 {
73 #ifndef HAVE_STRSIGNAL
74     log_module(MAIN_LOG, LOG_INFO, "Signal %d -- exiting.", x);
75 #else
76     log_module(MAIN_LOG, LOG_INFO, "%s -- exiting.", strsignal(x));
77 #endif
78     irc_squit(self, "Exiting on signal from console.", NULL);
79     quit_services = 1;
80 }
81
82 void sigaction_wait(UNUSED_ARG(int x))
83 {
84     int code;
85     wait4(-1, &code, WNOHANG, NULL);
86 }
87
88 void sigaction_rehash(int x)
89 {
90 #ifndef HAVE_STRSIGNAL
91     log_module(MAIN_LOG, LOG_INFO, "Signal %d -- rehashing.", x);
92 #else
93     log_module(MAIN_LOG, LOG_INFO, "%s -- rehashing.", strsignal(x));
94 #endif
95     do_reopen = 1;
96 }
97
98 void usage(char *self) {
99     /* We can assume we have getopt_long(). */
100     printf("Usage: %s [-c config] [-r log] [-d] [-f] [-v|-h]\n"
101            "-c, --config                    selects a different configuration file.\n"
102            "-d, --debug                     enables debug mode.\n"
103            "-f, --foreground                run srvx in the foreground.\n"
104            "-h, --help                      prints this usage message.\n"
105            "-k, --check                     checks the configuration file's syntax.\n"
106            "-r, --replay                    replay a log file (for debugging)\n"
107            "-v, --version                   prints this program's version.\n"
108            , self);
109 }
110
111 void version() {
112     printf("    --------------------------------------------------\n"
113            "    - "PACKAGE_STRING" ("CODENAME"), Built: " __DATE__ ", " __TIME__".\n"
114            "    - Copyright (C) 2000 - 2005, srvx Development Team\n"
115            "    --------------------------------------------------\n");
116 }
117
118 void license() {
119     printf("\n"
120            "This program is free software; you can redistribute it and/or modify\n"
121            "it under the terms of the GNU General Public License as published by\n"
122            "the Free Software Foundation; either version 2 of the License, or\n"
123            "(at your option) any later version.\n"
124            "\n"
125            "This program is distributed in the hope that it will be useful,\n"
126            "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
127            "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
128            "GNU General Public License for more details.\n"
129            "\n"
130            "You should have received a copy of the GNU General Public License\n"
131            "along with this program; if not, write to the Free Software\n"
132            "Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n\n");
133 }
134
135 #if WITH_MALLOC_BOEHM_GC
136 void
137 gc_warn_proc(char *msg, GC_word arg)
138 {
139     log_module(MAIN_LOG, LOG_ERROR, "GC(%p): %s", (void*)arg, msg);
140 }
141 #endif
142
143 int main(int argc, char *argv[])
144 {
145     int daemon, debug;
146     pid_t pid = 0;
147     FILE *file_out;
148     struct sigaction sv;
149
150 #if WITH_MALLOC_BOEHM_GC
151     GC_find_leak = 1;
152     GC_set_warn_proc(gc_warn_proc);
153     GC_enable_incremental();
154 #endif
155
156     daemon = 1;
157     debug = 0;
158     tools_init();
159
160     /* set up some signal handlers */
161     memset(&sv, 0, sizeof(sv));
162     sigemptyset(&sv.sa_mask);
163     sv.sa_handler = SIG_IGN;
164     sigaction(SIGPIPE, &sv, NULL);
165     sv.sa_handler = sigaction_rehash;
166     sigaction(SIGHUP, &sv, NULL);
167     sv.sa_handler = sigaction_writedb;
168     sigaction(SIGINT, &sv, NULL);
169     sv.sa_handler = sigaction_exit;
170     sigaction(SIGQUIT, &sv, NULL);
171     sv.sa_handler = sigaction_wait;
172     sigaction(SIGCHLD, &sv, NULL);
173
174     if (argc > 1) { /* parse command line, if any */
175         int c;
176         struct option options[] =
177         {
178             {"config", 1, 0, 'c'},
179             {"debug", 0, 0, 'd'},
180             {"foreground", 0, 0, 'f'},
181             {"help", 0, 0, 'h'},
182             {"check", 0, 0, 'k'},
183             {"replay", 1, 0, 'r'},
184             {"version", 0, 0, 'v'},
185             {"verbose", 0, 0, 'V'},
186             {0, 0, 0, 0}
187         };
188
189         while ((c = getopt_long(argc, argv, "c:kr:dfvVh", options, NULL)) != -1) {
190             switch(c) {
191             case 'c':
192                 services_config = optarg;
193                 break;
194             case 'k':
195                 if (conf_read(services_config)) {
196                     printf("%s appears to be a valid configuration file.\n", services_config);
197                 } else {
198                     printf("%s is an invalid configuration file.\n", services_config);
199                 }
200                 exit(0);
201             case 'r':
202                 replay_file = fopen(optarg, "r");
203                 if (!replay_file) {
204                     fprintf(stderr, "Could not open %s for reading: %s (%d)\n",
205                             optarg, strerror(errno), errno);
206                     exit(0);
207                 }
208                 break;
209             case 'd':
210                 debug = 1;
211                 break;
212             case 'f':
213                 daemon = 0;
214                 break;
215             case 'v':
216                 version();
217                 license();
218                 exit(0);
219             case 'h':
220             default:
221                 usage(argv[0]);
222                 exit(0);
223             }
224         }
225     }
226
227     version();
228
229     if (replay_file) {
230         /* We read a line here to "prime" the replay file parser, but
231          * mostly to get the right value of "now" for when we do the
232          * irc_introduce. */
233         replay_read_line();
234         boot_time = now;
235     } else {
236         boot_time = time(&now);
237     }
238
239     fprintf(stdout, "Initializing daemon...\n");
240     if (!conf_read(services_config)) {
241         fprintf(stderr, "Unable to read %s.\n", services_config);
242         exit(1);
243     }
244
245     conf_register_reload(uplink_compile);
246
247     if (daemon) {
248         /* Attempt to fork into the background if daemon mode is on. */
249         pid = fork();
250         if (pid < 0) {
251             fprintf(stderr, "Unable to fork: %s\n", strerror(errno));
252         } else if (pid > 0) {
253             fprintf(stdout, "Forking into the background (pid: %d)...\n", pid);
254             exit(0);
255         }
256         setsid();
257     }
258
259     file_out = fopen(PID_FILE, "w");
260     if (file_out == NULL) {
261         /* Create the main process' pid file */
262         fprintf(stderr, "Unable to create PID file: %s", strerror(errno));
263     } else {
264         fprintf(file_out, "%i\n", (int)getpid());
265         fclose(file_out);
266     }
267
268     if (daemon) {
269         /* Close these since we should not use them from now on. */
270         fclose(stdin);
271         fclose(stdout);
272         fclose(stderr);
273     }
274
275     services_argc = argc;
276     services_argv = argv;
277
278     atexit(call_exit_funcs);
279     reg_exit_func(main_shutdown);
280
281     log_init();
282     MAIN_LOG = log_register_type("srvx", "file:main.log");
283     if (debug)
284         log_debug();
285     ioset_init();
286     init_structs();
287     init_parse();
288     modcmd_init();
289     saxdb_init();
290     sar_init();
291     gline_init();
292     mail_init();
293     helpfile_init();
294     conf_globals(); /* initializes the core services */
295     conf_rlimits();
296     modules_init();
297     message_register_table(msgtab);
298     modcmd_finalize();
299     saxdb_finalize();
300     helpfile_finalize();
301     modules_finalize();
302
303     /* The first exit func to be called *should* be saxdb_write_all(). */
304     reg_exit_func(saxdb_write_all);
305     if (replay_file) {
306         char *msg;
307         log_module(MAIN_LOG, LOG_INFO, "Beginning replay...");
308         srand(now);
309         replay_event_loop();
310         if ((msg = dict_sanity_check(clients))) {
311             log_module(MAIN_LOG, LOG_ERROR, "Clients insanity: %s", msg);
312             free(msg);
313         }
314         if ((msg = dict_sanity_check(channels))) {
315             log_module(MAIN_LOG, LOG_ERROR, "Channels insanity: %s", msg);
316             free(msg);
317         }
318         if ((msg = dict_sanity_check(servers))) {
319             log_module(MAIN_LOG, LOG_ERROR, "Servers insanity: %s", msg);
320             free(msg);
321         }
322     } else {
323         srand(time(&now));
324         ioset_run();
325     }
326     return 0;
327 }