fix possible crash on user deletion
[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 #include "spamserv.h"
37
38 #ifdef HAVE_GETOPT_H
39 #include <getopt.h>
40 #else
41 #include "getopt.h"
42 #endif
43 #ifdef HAVE_SYS_RESOURCE_H
44 #include <sys/resource.h>
45 #endif
46 #ifdef HAVE_NETINET_IN_H
47 #include <netinet/in.h>
48 #endif
49 #ifdef HAVE_SYS_SOCKET_H
50 #include <sys/socket.h>
51 #endif
52 #ifdef HAVE_SYS_WAIT_H
53 #include <sys/wait.h>
54 #endif
55
56 #include "main-common.c"
57
58 void sigaction_writedb(int x)
59 {
60 #ifndef HAVE_STRSIGNAL
61     log_module(MAIN_LOG, LOG_INFO, "Signal %d -- writing databases.", x);
62 #else
63     log_module(MAIN_LOG, LOG_INFO, "%s -- writing databases.", strsignal(x));
64 #endif
65     do_write_dbs = 1;
66 }
67
68 void sigaction_exit(int x)
69 {
70 #ifndef HAVE_STRSIGNAL
71     log_module(MAIN_LOG, LOG_INFO, "Signal %d -- exiting.", x);
72 #else
73     log_module(MAIN_LOG, LOG_INFO, "%s -- exiting.", strsignal(x));
74 #endif
75     irc_squit(self, "Exiting on signal from console.", NULL);
76     quit_services = 1;
77 }
78
79 void sigaction_wait(UNUSED_ARG(int x))
80 {
81     int code;
82     wait4(-1, &code, WNOHANG, NULL);
83 }
84
85 void sigaction_rehash(int x)
86 {
87 #ifndef HAVE_STRSIGNAL
88     log_module(MAIN_LOG, LOG_INFO, "Signal %d -- rehashing.", x);
89 #else
90     log_module(MAIN_LOG, LOG_INFO, "%s -- rehashing.", strsignal(x));
91 #endif
92     do_reopen = 1;
93 }
94
95 #if WITH_MALLOC_BOEHM_GC
96 void
97 gc_warn_proc(char *msg, GC_word arg)
98 {
99     log_module(MAIN_LOG, LOG_ERROR, "GC(%p): %s", (void*)arg, msg);
100 }
101 #endif
102
103 int main(int argc, char *argv[])
104 {
105     int run_as_daemon;
106     int debug;
107     pid_t pid = 0;
108     FILE *file_out;
109     struct sigaction sv;
110
111 #if WITH_MALLOC_BOEHM_GC
112     GC_find_leak = 1;
113     GC_set_warn_proc(gc_warn_proc);
114     GC_enable_incremental();
115 #endif
116
117     run_as_daemon = 1;
118     debug = 0;
119     tools_init();
120
121     /* set up some signal handlers */
122     memset(&sv, 0, sizeof(sv));
123     sigemptyset(&sv.sa_mask);
124     sv.sa_handler = SIG_IGN;
125     sigaction(SIGPIPE, &sv, NULL);
126     sv.sa_handler = sigaction_rehash;
127     sigaction(SIGHUP, &sv, NULL);
128     sv.sa_handler = sigaction_writedb;
129     sigaction(SIGINT, &sv, NULL);
130     sv.sa_handler = sigaction_exit;
131     sigaction(SIGQUIT, &sv, NULL);
132     sv.sa_handler = sigaction_wait;
133     sigaction(SIGCHLD, &sv, NULL);
134
135     if (argc > 1) { /* parse command line, if any */
136         int c;
137         struct option options[] =
138         {
139             {"config", 1, 0, 'c'},
140             {"debug", 0, 0, 'd'},
141             {"foreground", 0, 0, 'f'},
142             {"help", 0, 0, 'h'},
143             {"check", 0, 0, 'k'},
144             {"replay", 1, 0, 'r'},
145             {"version", 0, 0, 'v'},
146             {0, 0, 0, 0}
147         };
148
149         while ((c = getopt_long(argc, argv, "c:dfhkr:v", options, NULL)) != -1) {
150             switch (c) {
151             case 'c':
152                 services_config = optarg;
153                 break;
154             case 'k':
155                 if (conf_read(services_config)) {
156                     printf("%s appears to be a valid configuration file.\n", services_config);
157                 } else {
158                     printf("%s is an invalid configuration file.\n", services_config);
159                 }
160                 exit(0);
161             case 'r':
162                 replay_file = fopen(optarg, "r");
163                 if (!replay_file) {
164                     fprintf(stderr, "Could not open %s for reading: %s (%d)\n",
165                             optarg, strerror(errno), errno);
166                     exit(0);
167                 }
168                 break;
169             case 'd':
170                 debug = 1;
171                 break;
172             case 'f':
173                 run_as_daemon = 0;
174                 break;
175             case 'v':
176                 version();
177                 license();
178                 exit(0);
179             case 'h':
180             default:
181                 usage(argv[0]);
182                 exit(0);
183             }
184         }
185     }
186
187     version();
188
189     if (replay_file) {
190         /* We read a line here to "prime" the replay file parser, but
191          * mostly to get the right value of "now" for when we do the
192          * irc_introduce. */
193         replay_read_line();
194     } else {
195         now = time(NULL);
196     }
197     boot_time = now;
198
199     fprintf(stdout, "Initializing daemon...\n");
200     if (!conf_read(services_config)) {
201         fprintf(stderr, "Unable to read %s.\n", services_config);
202         exit(1);
203     }
204
205     conf_register_reload(uplink_compile);
206
207     if (run_as_daemon) {
208         /* Attempt to fork into the background if daemon mode is on. */
209         pid = fork();
210         if (pid < 0) {
211             fprintf(stderr, "Unable to fork: %s\n", strerror(errno));
212         } else if (pid > 0) {
213             fprintf(stdout, "Forking into the background (pid: %d)...\n", pid);
214             exit(0);
215         }
216         setsid();
217     }
218
219     file_out = fopen(PID_FILE, "w");
220     if (file_out == NULL) {
221         /* Create the main process' pid file */
222         fprintf(stderr, "Unable to create PID file: %s", strerror(errno));
223     } else {
224         fprintf(file_out, "%i\n", (int)getpid());
225         fclose(file_out);
226     }
227
228     if (run_as_daemon) {
229         /* Close these since we should not use them from now on. */
230         fclose(stdin);
231         fclose(stdout);
232         fclose(stderr);
233     }
234
235     services_argc = argc;
236     services_argv = argv;
237
238     atexit(call_exit_funcs);
239     reg_exit_func(main_shutdown);
240
241     log_init();
242     MAIN_LOG = log_register_type("srvx", "file:main.log");
243     if (debug)
244         log_debug();
245     ioset_init();
246     init_structs();
247     init_parse();
248     modcmd_init();
249     saxdb_init();
250     sar_init();
251     gline_init();
252     mail_init();
253     helpfile_init();
254     conf_globals(); /* initializes the core services */
255     conf_rlimits();
256     modules_init();
257     message_register_table(msgtab);
258     modcmd_finalize();
259     saxdb_finalize();
260     helpfile_finalize();
261     modules_finalize();
262
263     /* The first exit func to be called *should* be saxdb_write_all(). */
264     reg_exit_func(saxdb_write_all);
265     if (replay_file) {
266         char *msg;
267         log_module(MAIN_LOG, LOG_INFO, "Beginning replay...");
268         srand(now);
269         replay_event_loop();
270         if ((msg = dict_sanity_check(clients))) {
271             log_module(MAIN_LOG, LOG_ERROR, "Clients insanity: %s", msg);
272             free(msg);
273         }
274         if ((msg = dict_sanity_check(channels))) {
275             log_module(MAIN_LOG, LOG_ERROR, "Channels insanity: %s", msg);
276             free(msg);
277         }
278         if ((msg = dict_sanity_check(servers))) {
279             log_module(MAIN_LOG, LOG_ERROR, "Servers insanity: %s", msg);
280             free(msg);
281         }
282     } else {
283         now = time(NULL);
284         srand(now);
285         ioset_run();
286     }
287     return 0;
288 }