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