2438868d2789da61376b9f9cb440acedfa2da8f8
[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 #if WITH_MALLOC_BOEHM_GC
99 void
100 gc_warn_proc(char *msg, GC_word arg)
101 {
102     log_module(MAIN_LOG, LOG_ERROR, "GC(%p): %s", (void*)arg, msg);
103 }
104 #endif
105
106 int main(int argc, char *argv[])
107 {
108     int daemon, debug;
109     pid_t pid = 0;
110     FILE *file_out;
111     struct sigaction sv;
112
113 #if WITH_MALLOC_BOEHM_GC
114     GC_find_leak = 1;
115     GC_set_warn_proc(gc_warn_proc);
116     GC_enable_incremental();
117 #endif
118
119     daemon = 1;
120     debug = 0;
121     tools_init();
122
123     /* set up some signal handlers */
124     memset(&sv, 0, sizeof(sv));
125     sigemptyset(&sv.sa_mask);
126     sv.sa_handler = SIG_IGN;
127     sigaction(SIGPIPE, &sv, NULL);
128     sv.sa_handler = sigaction_rehash;
129     sigaction(SIGHUP, &sv, NULL);
130     sv.sa_handler = sigaction_writedb;
131     sigaction(SIGINT, &sv, NULL);
132     sv.sa_handler = sigaction_exit;
133     sigaction(SIGQUIT, &sv, NULL);
134     sv.sa_handler = sigaction_wait;
135     sigaction(SIGCHLD, &sv, NULL);
136
137     if (argc > 1) { /* parse command line, if any */
138         int c;
139         struct option options[] =
140         {
141             {"config", 1, 0, 'c'},
142             {"debug", 0, 0, 'd'},
143             {"foreground", 0, 0, 'f'},
144             {"help", 0, 0, 'h'},
145             {"check", 0, 0, 'k'},
146             {"replay", 1, 0, 'r'},
147             {"version", 0, 0, 'v'},
148             {0, 0, 0, 0}
149         };
150
151         while ((c = getopt_long(argc, argv, "c:dfhkr:v", options, NULL)) != -1) {
152             switch (c) {
153             case 'c':
154                 services_config = optarg;
155                 break;
156             case 'k':
157                 if (conf_read(services_config)) {
158                     printf("%s appears to be a valid configuration file.\n", services_config);
159                 } else {
160                     printf("%s is an invalid configuration file.\n", services_config);
161                 }
162                 exit(0);
163             case 'r':
164                 replay_file = fopen(optarg, "r");
165                 if (!replay_file) {
166                     fprintf(stderr, "Could not open %s for reading: %s (%d)\n",
167                             optarg, strerror(errno), errno);
168                     exit(0);
169                 }
170                 break;
171             case 'd':
172                 debug = 1;
173                 break;
174             case 'f':
175                 daemon = 0;
176                 break;
177             case 'v':
178                 version();
179                 license();
180                 exit(0);
181             case 'h':
182             default:
183                 usage(argv[0]);
184                 exit(0);
185             }
186         }
187     }
188
189     version();
190
191     if (replay_file) {
192         /* We read a line here to "prime" the replay file parser, but
193          * mostly to get the right value of "now" for when we do the
194          * irc_introduce. */
195         replay_read_line();
196     } else {
197         now = time(NULL);
198     }
199     boot_time = now;
200
201     fprintf(stdout, "Initializing daemon...\n");
202     if (!conf_read(services_config)) {
203         fprintf(stderr, "Unable to read %s.\n", services_config);
204         exit(1);
205     }
206
207     conf_register_reload(uplink_compile);
208
209     if (daemon) {
210         /* Attempt to fork into the background if daemon mode is on. */
211         pid = fork();
212         if (pid < 0) {
213             fprintf(stderr, "Unable to fork: %s\n", strerror(errno));
214         } else if (pid > 0) {
215             fprintf(stdout, "Forking into the background (pid: %d)...\n", pid);
216             exit(0);
217         }
218         setsid();
219     }
220
221     file_out = fopen(PID_FILE, "w");
222     if (file_out == NULL) {
223         /* Create the main process' pid file */
224         fprintf(stderr, "Unable to create PID file: %s", strerror(errno));
225     } else {
226         fprintf(file_out, "%i\n", (int)getpid());
227         fclose(file_out);
228     }
229
230     if (daemon) {
231         /* Close these since we should not use them from now on. */
232         fclose(stdin);
233         fclose(stdout);
234         fclose(stderr);
235     }
236
237     services_argc = argc;
238     services_argv = argv;
239
240     atexit(call_exit_funcs);
241     reg_exit_func(main_shutdown);
242
243     log_init();
244     MAIN_LOG = log_register_type("srvx", "file:main.log");
245     if (debug)
246         log_debug();
247     ioset_init();
248     init_structs();
249     init_parse();
250     modcmd_init();
251     saxdb_init();
252     sar_init();
253     gline_init();
254     mail_init();
255     helpfile_init();
256     conf_globals(); /* initializes the core services */
257     conf_rlimits();
258     modules_init();
259     message_register_table(msgtab);
260     modcmd_finalize();
261     saxdb_finalize();
262     helpfile_finalize();
263     modules_finalize();
264
265     /* The first exit func to be called *should* be saxdb_write_all(). */
266     reg_exit_func(saxdb_write_all);
267     if (replay_file) {
268         char *msg;
269         log_module(MAIN_LOG, LOG_INFO, "Beginning replay...");
270         srand(now);
271         replay_event_loop();
272         if ((msg = dict_sanity_check(clients))) {
273             log_module(MAIN_LOG, LOG_ERROR, "Clients insanity: %s", msg);
274             free(msg);
275         }
276         if ((msg = dict_sanity_check(channels))) {
277             log_module(MAIN_LOG, LOG_ERROR, "Channels insanity: %s", msg);
278             free(msg);
279         }
280         if ((msg = dict_sanity_check(servers))) {
281             log_module(MAIN_LOG, LOG_ERROR, "Servers insanity: %s", msg);
282             free(msg);
283         }
284     } else {
285         now = time(NULL);
286         srand(now);
287         ioset_run();
288     }
289     return 0;
290 }