]> jfr.im git - irc/evilnet/x3.git/blame - src/main.c
Upgrading to GPL v3
[irc/evilnet/x3.git] / src / main.c
CommitLineData
7637f48f 1/* main.c - X3
d76ed9a9 2 * Copyright 2000-2004 srvx Development Team
3 *
83ff05c3 4 * This file is part of x3.
d76ed9a9 5 *
d0f04f71 6 * x3 is free software; you can redistribute it and/or modify
d76ed9a9 7 * it under the terms of the GNU General Public License as published by
348683aa 8 * the Free Software Foundation; either version 3 of the License, or
d76ed9a9 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
ceafd592 21#define PID_FILE "x3.pid"
d76ed9a9 22
23#include "conf.h"
24#include "gline.h"
25#include "ioset.h"
26#include "modcmd.h"
27#include "saxdb.h"
28#include "sendmail.h"
63c95a47 29#include "spamserv.h"
d914d1cb 30#include "shun.h"
d76ed9a9 31#include "timeq.h"
32
33#include "chanserv.h"
34#include "global.h"
35#include "modules.h"
36#include "opserv.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#ifndef SIGCHLD
57#define SIGCHLD SIGCLD
58#endif
59
60extern FILE *replay_file;
61
62time_t boot_time, burst_begin, now;
63unsigned long burst_length;
64struct log_type *MAIN_LOG;
65
66int quit_services, max_cycles;
67
ceafd592 68char *services_config = "x3.conf";
d76ed9a9 69
70char **services_argv;
71int services_argc;
72
73struct cManagerNode cManager;
74
75struct policer_params *oper_policer_params, *luser_policer_params, *god_policer_params;
76
77static const struct message_entry msgtab[] = {
78 { "MSG_NONE", "None" },
79 { "MSG_ON", "On" },
80 { "MSG_OFF", "Off" },
81 { "MSG_NEVER", "Never" },
de9510bc 82 { "MSG_BAR", "----------------------------------------" },
d76ed9a9 83 { "MSG_SERVICE_IMMUNE", "$b%s$b may not be kicked, killed, banned, or deopped." },
84 { "MSG_SERVICE_PRIVILEGED", "$b%s$b is a privileged service." },
85 { "MSG_NOT_A_SERVICE", "$b%s$b is not a service bot." },
86 { "MSG_COMMAND_UNKNOWN", "$b%s$b is an unknown command." },
87 { "MSG_COMMAND_PRIVILEGED", "$b%s$b is a privileged command." },
88 { "MSG_COMMAND_DISABLED", "$b%s$b is a disabled command." },
89 { "MSG_SETTING_PRIVILEGED", "$b%s$b is a privileged setting." },
90 { "MSG_AUTHENTICATE", "You must first authenticate with $b$N$b." },
91 { "MSG_USER_AUTHENTICATE", "%s must first authenticate with $b$N$b." },
92 { "MSG_SET_EMAIL_ADDR", "You must first set your account's email address. (Contact network staff if you cannot auth to your account.)" },
93 { "MSG_HANDLE_UNKNOWN", "Account $b%s$b has not been registered." },
94 { "MSG_NICK_UNKNOWN", "User with nick $b%s$b does not exist." },
95 { "MSG_CHANNEL_UNKNOWN", "Channel with name $b%s$b does not exist." },
96 { "MSG_SERVER_UNKNOWN", "Server with name $b%s$b does not exist or is not linked." },
97 { "MSG_MODULE_UNKNOWN", "No module has been registered with name $b%s$b." },
98 { "MSG_INVALID_MODES", "$b%s$b is an invalid set of channel modes." },
99 { "MSG_INVALID_GLINE", "Invalid G-line '%s'." },
d914d1cb 100 { "MSG_INVALID_SHUN", "Invalid Shun '%s'." },
d76ed9a9 101 { "MSG_INVALID_DURATION", "Invalid time span '%s'." },
102 { "MSG_NOT_TARGET_NAME", "You must provide the name of a channel or user." },
fc8798ec 103 { "MSG_NOT_CHANNEL_NAME", "The channel name you specified is not a valid channel name." },
104 { "MSG_INVALID_CHANNEL", "The channel name you specified does not exist." },
d76ed9a9 105 { "MSG_CHANNEL_ABSENT", "You aren't currently in $b%s$b." },
106 { "MSG_CHANNEL_USER_ABSENT", "$b%s$b isn't currently in $b%s$b." },
107 { "MSG_MISSING_PARAMS", "$b%s$b requires more parameters." },
108 { "MSG_DEPRECATED_COMMAND", "The $b%s$b command has been deprecated, and will be removed in the future; please use $b%s$b instead." },
109 { "MSG_OPER_SUSPENDED", "Your $b$O$b access has been suspended." },
110 { "MSG_USER_OUTRANKED", "$b%s$b outranks you (command has no effect)." },
111 { "MSG_STUPID_ACCESS_CHANGE", "Please ask someone $belse$b to demote you." },
112 { "MSG_NO_SEARCH_ACCESS", "You do not have enough access to search based on $b%s$b." },
113 { "MSG_INVALID_CRITERIA", "$b%s$b is an invalid search criteria." },
de9510bc 114 { "MSG_MATCH_COUNT", "-----------Found $b%3u$b Matches------------" },
d76ed9a9 115 { "MSG_NO_MATCHES", "Nothing matched the criteria of your search." },
116 { "MSG_TOPIC_UNKNOWN", "No help on that topic." },
117 { "MSG_INVALID_BINARY", "$b%s$b is an invalid binary value." },
118 { "MSG_INTERNAL_FAILURE", "Your command could not be processed due to an internal failure." },
119 { "MSG_DB_UNKNOWN", "I do not know of a database named %s." },
120 { "MSG_DB_IS_MONDO", "Database %s is in the \"mondo\" database and cannot be written separately." },
121 { "MSG_DB_WRITE_ERROR", "Error while writing database %s." },
122 { "MSG_DB_WROTE_DB", "Wrote database %s (in "FMT_TIME_T".%06lu seconds)." },
123 { "MSG_DB_WROTE_ALL", "Wrote all databases (in "FMT_TIME_T".%06lu seconds)." },
de9510bc 124 { "MSG_AND", "," },
d76ed9a9 125 { "MSG_0_SECONDS", "0 seconds" },
de9510bc 126 { "MSG_YEAR", "y" },
127 { "MSG_YEARS", "y" },
128 { "MSG_WEEK", "w" },
129 { "MSG_WEEKS", "w" },
130 { "MSG_DAY", "d" },
131 { "MSG_DAYS", "d" },
132 { "MSG_HOUR", "h" },
133 { "MSG_HOURS", "h" },
134 { "MSG_MINUTE", "m" },
135 { "MSG_MINUTES", "m" },
136 { "MSG_SECOND", "s" },
137 { "MSG_SECONDS", "s" },
d76ed9a9 138 { NULL, NULL }
139};
140
141void uplink_select(char *name);
142
143static int
144uplink_insert(const char *key, void *data, UNUSED_ARG(void *extra))
145{
146 struct uplinkNode *uplink = malloc(sizeof(struct uplinkNode));
147 struct record_data *rd = data;
2f61d1d7 148 struct addrinfo hints, *ai;
d76ed9a9 149 int enabled = 1;
150 char *str;
d76ed9a9 151
152 if(!uplink)
153 {
154 return 0;
155 }
156
157 uplink->name = (char *)key;
158 uplink->host = database_get_data(rd->d.object, "address", RECDB_QSTRING);
159
160 str = database_get_data(rd->d.object, "port", RECDB_QSTRING);
161 uplink->port = str ? atoi(str) : 6667;
162 uplink->password = database_get_data(rd->d.object, "password", RECDB_QSTRING);
163 uplink->their_password = database_get_data(rd->d.object, "uplink_password", RECDB_QSTRING);
164
165 str = database_get_data(rd->d.object, "enabled", RECDB_QSTRING);
166 if(str)
167 {
168 enabled = atoi(str) ? 1 : 0;
169 }
170
171 cManager.enabled += enabled;
172
173 str = database_get_data(rd->d.object, "max_tries", RECDB_QSTRING);
174 uplink->max_tries = str ? atoi(str) : 3;
175 uplink->flags = enabled ? 0 : UPLINK_UNAVAILABLE;
176 uplink->state = DISCONNECTED;
177 uplink->tries = 0;
178
179 str = database_get_data(rd->d.object, "bind_address", RECDB_QSTRING);
2f61d1d7 180 memset(&hints, 0, sizeof(hints));
181 hints.ai_flags = AI_PASSIVE;
182 hints.ai_socktype = SOCK_STREAM;
183 if (!getaddrinfo(str, NULL, &hints, &ai))
d76ed9a9 184 {
2f61d1d7 185 uplink->bind_addr_len = ai->ai_addrlen;
186 uplink->bind_addr = calloc(1, ai->ai_addrlen);
187 memcpy(uplink->bind_addr, ai->ai_addr, ai->ai_addrlen);
188 freeaddrinfo(ai);
189 }
190 else
d76ed9a9 191 {
192 uplink->bind_addr = NULL;
193 uplink->bind_addr_len = 0;
194 }
195
196 uplink->next = cManager.uplinks;
197 uplink->prev = NULL;
198
199 if(cManager.uplinks)
200 {
201 cManager.uplinks->prev = uplink;
202 }
203
204 cManager.uplinks = uplink;
205
206 /* If the configuration is being reloaded, set the current uplink
207 to the reloaded equivalent, if possible. */
208 if(cManager.uplink
209 && enabled
210 && !irccasecmp(uplink->host, cManager.uplink->host)
211 && uplink->port == cManager.uplink->port)
212 {
213 uplink->state = cManager.uplink->state;
214 uplink->tries = cManager.uplink->tries;
215 cManager.uplink = uplink;
216 }
217
218 return 0;
219}
220
221void
222uplink_compile(void)
223{
224 const char *cycles;
225 dict_t conf_node;
226 struct uplinkNode *oldUplinks = NULL, *oldUplink = NULL;
227
228 /* Save the old uplinks, we'll remove them later. */
229 oldUplink = cManager.uplink;
230 oldUplinks = cManager.uplinks;
231
232 cycles = conf_get_data("server/max_cycles", RECDB_QSTRING);
233 max_cycles = cycles ? atoi(cycles) : 30;
234 if(!(conf_node = conf_get_data("uplinks", RECDB_OBJECT)))
235 {
236 log_module(MAIN_LOG, LOG_FATAL, "No uplinks configured; giving up.");
237 exit(1);
238 }
239
240 cManager.enabled = 0;
241 dict_foreach(conf_node, uplink_insert, NULL);
242
243 /* Remove the old uplinks, if any. It doesn't matter if oldUplink (below)
244 is a reference to one of these, because it won't get dereferenced. */
245 if(oldUplinks)
246 {
247 struct uplinkNode *uplink, *next;
248
249 oldUplinks->prev->next = NULL;
250
251 for(uplink = oldUplinks; uplink; uplink = next)
252 {
253 next = uplink->next;
254 free(uplink->bind_addr);
255 free(uplink);
256 }
257 }
258
259 /* If the uplink hasn't changed, it's either NULL or pointing at
260 an uplink that was just deleted, select a new one. */
261 if(cManager.uplink == oldUplink)
262 {
263 if(oldUplink)
264 {
265 irc_squit(self, "Uplinks updated; selecting new uplink.", NULL);
266 }
267
268 cManager.uplink = NULL;
269 uplink_select(NULL);
270 }
271}
272
273struct uplinkNode *
274uplink_find(char *name)
275{
276 struct uplinkNode *uplink;
277
278 if(!cManager.enabled || !cManager.uplinks)
279 {
280 return NULL;
281 }
282
283 for(uplink = cManager.uplinks; uplink; uplink = uplink->next)
284 {
285 if(!strcasecmp(uplink->name, name))
286 {
287 return uplink;
288 }
289 }
290
291 return NULL;
292}
293
294void
295uplink_select(char *name)
296{
297 struct uplinkNode *start, *uplink, *next;
298 int stop;
299
300 if(!cManager.enabled || !cManager.uplinks)
301 {
302 log_module(MAIN_LOG, LOG_FATAL, "No uplinks enabled; giving up.");
303 exit(1);
304 }
305
306 if(!cManager.uplink)
307 {
308 start = cManager.uplinks;
309 }
310 else
311 {
312 start = cManager.uplink->next;
313 if(!start)
314 {
315 start = cManager.uplinks;
316 }
317 }
318
319 stop = 0;
320 for(uplink = start; uplink; uplink = next)
321 {
322 next = uplink->next ? uplink->next : cManager.uplinks;
323
324 if(stop)
325 {
326 uplink = NULL;
327 break;
328 }
329
330 /* We've wrapped around the list. */
331 if(next == start)
332 {
333 sleep((cManager.cycles >> 1) * 5);
334 cManager.cycles++;
335
336 if(max_cycles && (cManager.cycles >= max_cycles))
337 {
338 log_module(MAIN_LOG, LOG_FATAL, "Maximum uplink list cycles exceeded; giving up.");
339 exit(1);
340 }
341
342 /* Give the uplink currently in 'uplink' consideration,
343 and if not selected, break on the next iteration. */
344 stop = 1;
345 }
346
347 /* Skip bad uplinks. */
348 if(uplink->flags & UPLINK_UNAVAILABLE)
349 {
350 continue;
351 }
352
353 if(name && irccasecmp(uplink->name, name))
354 {
355 /* If we were told to connect to a specific uplink, don't stop
356 until we find it.
357 */
358 continue;
359 }
360
361 /* It would be possible to track uplink health through a variety
362 of statistics and only break on the best uplink. For now, break
363 on the first available one.
364 */
365
366 break;
367 }
368
369 if(!uplink)
370 {
371 /* We are shit outta luck if every single uplink has been passed
372 over. Use the current uplink if possible. */
373 if(!cManager.uplink || cManager.uplink->flags & UPLINK_UNAVAILABLE)
374 {
375 log_module(MAIN_LOG, LOG_FATAL, "All available uplinks exhausted; giving up.");
376 exit(1);
377 }
378
379 return;
380 }
381
382 cManager.uplink = uplink;
383}
384
385int
386uplink_connect(void)
387{
388 struct uplinkNode *uplink = cManager.uplink;
389
390 if(uplink->state != DISCONNECTED)
391 {
392 return 0;
393 }
394
395 if(uplink->flags & UPLINK_UNAVAILABLE)
396 {
397 uplink_select(NULL);
398 uplink = cManager.uplink;
399 }
400
401 if(uplink->tries)
402 {
403 /* This delay could scale with the number of tries. */
404 sleep(2);
405 }
406
407 if(!create_socket_client(uplink))
408 {
409 if(uplink->max_tries && (uplink->tries >= uplink->max_tries))
410 {
411 /* This is a bad uplink, move on. */
412 uplink->flags |= UPLINK_UNAVAILABLE;
413 uplink_select(NULL);
414 }
415
416 return 0;
417 }
418 else
419 {
420 uplink->state = AUTHENTICATING;
421 irc_introduce(uplink->password);
422 }
423
424 return 1;
425}
426
427void
428received_ping(void)
429{
430 /* This function is called when a ping is received. Take it as
431 a sign of link health and reset the connection manager
432 information. */
433
434 cManager.cycles = 0;
435}
436
437void sigaction_writedb(int x)
438{
439#ifndef HAVE_STRSIGNAL
440 log_module(MAIN_LOG, LOG_INFO, "Signal %d -- writing databases.", x);
441#else
442 log_module(MAIN_LOG, LOG_INFO, "%s -- writing databases.", strsignal(x));
443#endif
444 do_write_dbs = 1;
445}
446
447void sigaction_exit(int x)
448{
449#ifndef HAVE_STRSIGNAL
450 log_module(MAIN_LOG, LOG_INFO, "Signal %d -- exiting.", x);
451#else
452 log_module(MAIN_LOG, LOG_INFO, "%s -- exiting.", strsignal(x));
453#endif
454 irc_squit(self, "Exiting on signal from console.", NULL);
455 quit_services = 1;
456}
457
458void sigaction_wait(UNUSED_ARG(int x))
459{
460 int code;
461 wait4(-1, &code, WNOHANG, NULL);
462}
463
464void sigaction_rehash(int x)
465{
466#ifndef HAVE_STRSIGNAL
467 log_module(MAIN_LOG, LOG_INFO, "Signal %d -- rehashing.", x);
468#else
469 log_module(MAIN_LOG, LOG_INFO, "%s -- rehashing.", strsignal(x));
470#endif
471 do_reopen = 1;
472}
473
474static exit_func_t *ef_list;
475static unsigned int ef_size = 0, ef_used = 0;
476
477void reg_exit_func(exit_func_t handler)
478{
479 if (ef_used == ef_size) {
480 if (ef_size) {
481 ef_size <<= 1;
482 ef_list = realloc(ef_list, ef_size*sizeof(exit_func_t));
483 } else {
484 ef_size = 8;
485 ef_list = malloc(ef_size*sizeof(exit_func_t));
486 }
487 }
488 ef_list[ef_used++] = handler;
489}
490
491void call_exit_funcs(void)
492{
493 unsigned int n = ef_used;
494
495 /* Call them in reverse order because we initialize logs, then
496 * nickserv, then chanserv, etc., and they register their exit
497 * funcs in that order, and there are some dependencies (for
498 * example, ChanServ requires NickServ to not have cleaned up).
499 */
500
501 while (n > 0) {
502 ef_list[--n]();
503 }
504 free(ef_list);
505 ef_used = ef_size = 0;
506}
507
508int
509set_policer_param(const char *param, void *data, void *extra)
510{
511 struct record_data *rd = data;
512 const char *str = GET_RECORD_QSTRING(rd);
513 if (str) {
514 policer_params_set(extra, param, str);
515 }
516 return 0;
517}
518
519static void
520conf_globals(void)
521{
522 const char *info;
523 dict_t dict;
524
d76ed9a9 525 god_policer_params = policer_params_new();
526 if ((dict = conf_get_data("policers/commands-god", RECDB_OBJECT))) {
527 dict_foreach(dict, set_policer_param, god_policer_params);
528 } else {
529 policer_params_set(god_policer_params, "size", "30");
530 policer_params_set(god_policer_params, "drain-rate", "1");
531 }
532 oper_policer_params = policer_params_new();
533 if ((dict = conf_get_data("policers/commands-oper", RECDB_OBJECT))) {
534 dict_foreach(dict, set_policer_param, oper_policer_params);
535 } else {
536 policer_params_set(oper_policer_params, "size", "10");
537 policer_params_set(oper_policer_params, "drain-rate", "1");
538 }
539 luser_policer_params = policer_params_new();
540 if ((dict = conf_get_data("policers/commands-luser", RECDB_OBJECT))) {
541 dict_foreach(dict, set_policer_param, luser_policer_params);
542 } else {
543 policer_params_set(luser_policer_params, "size", "5");
544 policer_params_set(luser_policer_params, "drain-rate", "0.50");
545 }
546
547 info = conf_get_data("services/opserv/nick", RECDB_QSTRING);
548 if (info && (info[0] == '.'))
549 info = NULL;
550 init_opserv(info);
7637f48f 551
552 info = conf_get_data("services/global/nick", RECDB_QSTRING);
553 if (info && (info[0] == '.'))
554 info = NULL;
555 init_global(info);
556
557 info = conf_get_data("services/nickserv/nick", RECDB_QSTRING);
558 if (info && (info[0] == '.'))
559 info = NULL;
560 init_nickserv(info);
561
562 info = conf_get_data("services/chanserv/nick", RECDB_QSTRING);
563 if (info && (info[0] == '.'))
564 info = NULL;
565 init_chanserv(info);
3ec42e29 566
567 info = conf_get_data("services/spamserv/nick", RECDB_QSTRING);
568 if (info && (info[0] == '.'))
569 info = NULL;
570 init_spamserv(info);
d76ed9a9 571}
572
573#ifdef HAVE_SYS_RESOURCE_H
574
575static int
576set_item_rlimit(const char *name, void *data, void *extra)
577{
2f61d1d7 578 long rsrc;
579 int found;
d76ed9a9 580 struct record_data *rd = data;
581 struct rlimit rlim;
582 const char *str;
583
2f61d1d7 584 rsrc = (long)dict_find(extra, name, &found);
d76ed9a9 585 if (!found) {
586 log_module(MAIN_LOG, LOG_ERROR, "Invalid rlimit \"%s\" in rlimits section.", name);
587 return 0;
588 }
589 if (!(str = GET_RECORD_QSTRING(rd))) {
590 log_module(MAIN_LOG, LOG_ERROR, "Missing or invalid parameter type for rlimit \"%s\".", name);
591 return 0;
592 }
593 if (getrlimit(rsrc, &rlim) < 0) {
594 log_module(MAIN_LOG, LOG_ERROR, "Couldn't get rlimit \"%s\": errno %d: %s", name, errno, strerror(errno));
595 return 0;
596 }
597 rlim.rlim_cur = ParseVolume(str);
598 if (setrlimit(rsrc, &rlim) < 0) {
599 log_module(MAIN_LOG, LOG_ERROR, "Couldn't set rlimit \"%s\": errno %d: %s", name, errno, strerror(errno));
600 }
601 return 0;
602}
603
604static void
605conf_rlimits(void)
606{
607 dict_t dict, values;
608
609 values = dict_new();
610 dict_insert(values, "data", (void*)RLIMIT_DATA);
611 dict_insert(values, "stack", (void*)RLIMIT_STACK);
612#ifdef RLIMIT_VMEM
613 dict_insert(values, "vmem", (void*)RLIMIT_VMEM);
614#else
615#ifdef RLIMIT_AS
616 dict_insert(values, "vmem", (void*)RLIMIT_AS);
617#endif
618#endif
619 if ((dict = conf_get_data("rlimits", RECDB_OBJECT))) {
620 dict_foreach(dict, set_item_rlimit, values);
621 }
622 dict_delete(values);
623}
624
625#else
626
627static void
628conf_rlimits(void)
629{
630}
631
632#endif
633
634void main_shutdown(void)
635{
636 struct uplinkNode *ul, *ul_next;
637 ioset_cleanup();
638 for (ul = cManager.uplinks; ul; ul = ul_next) {
639 ul_next = ul->next;
640 free(ul->bind_addr);
641 free(ul);
642 }
643 tools_cleanup();
644 conf_close();
645 remove(PID_FILE);
646 policer_params_delete(god_policer_params);
647 policer_params_delete(oper_policer_params);
648 policer_params_delete(luser_policer_params);
649 if (replay_file)
650 fclose(replay_file);
651}
652
653void usage(char *self) {
654 /* We can assume we have getopt_long(). */
655 printf("Usage: %s [-c config] [-r log] [-d] [-f] [-v|-h]\n"
656 "-c, --config selects a different configuration file.\n"
657 "-d, --debug enables debug mode.\n"
ceafd592 658 "-f, --foreground run X3 in the foreground.\n"
d76ed9a9 659 "-h, --help prints this usage message.\n"
660 "-k, --check checks the configuration file's syntax.\n"
661 "-r, --replay replay a log file (for debugging)\n"
662 "-v, --version prints this program's version.\n"
663 , self);
664}
665
666void version() {
667 printf(" --------------------------------------------------\n"
ceafd592 668 " - "PACKAGE_STRING", Built: " __DATE__ ", " __TIME__".\n"
0d16e639 669 " - Copyright (C) 2000 - 2005, srvx Development Team\n"
ceafd592 670 " - Copyright (C) 2004 - 2005, X3 Development Team\n"
d76ed9a9 671 " --------------------------------------------------\n");
672}
673
674void license() {
675 printf("\n"
676 "This program is free software; you can redistribute it and/or modify\n"
677 "it under the terms of the GNU General Public License as published by\n"
348683aa 678 "the Free Software Foundation; either version 3 of the License, or\n"
d76ed9a9 679 "(at your option) any later version.\n"
680 "\n"
681 "This program is distributed in the hope that it will be useful,\n"
682 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
683 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
684 "GNU General Public License for more details.\n"
685 "\n"
686 "You should have received a copy of the GNU General Public License\n"
687 "along with this program; if not, write to the Free Software\n"
688 "Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n\n");
689}
690
691#if WITH_MALLOC_BOEHM_GC
692void
693gc_warn_proc(char *msg, GC_word arg)
694{
695 log_module(MAIN_LOG, LOG_ERROR, "GC(%p): %s", (void*)arg, msg);
696}
697#endif
698
699int main(int argc, char *argv[])
700{
701 int daemon, debug;
702 pid_t pid = 0;
703 FILE *file_out;
704 struct sigaction sv;
705
0d16e639 706#if WITH_MALLOC_BOEHM_GC
707 GC_find_leak = 1;
708 GC_set_warn_proc(gc_warn_proc);
709 GC_enable_incremental();
710#endif
711
d76ed9a9 712 daemon = 1;
713 debug = 0;
714 tools_init();
715
716 /* set up some signal handlers */
717 memset(&sv, 0, sizeof(sv));
718 sigemptyset(&sv.sa_mask);
719 sv.sa_handler = SIG_IGN;
720 sigaction(SIGPIPE, &sv, NULL);
721 sv.sa_handler = sigaction_rehash;
722 sigaction(SIGHUP, &sv, NULL);
723 sv.sa_handler = sigaction_writedb;
724 sigaction(SIGINT, &sv, NULL);
725 sv.sa_handler = sigaction_exit;
726 sigaction(SIGQUIT, &sv, NULL);
727 sv.sa_handler = sigaction_wait;
728 sigaction(SIGCHLD, &sv, NULL);
729
730 if (argc > 1) { /* parse command line, if any */
731 int c;
732 struct option options[] =
733 {
734 {"config", 1, 0, 'c'},
735 {"debug", 0, 0, 'd'},
736 {"foreground", 0, 0, 'f'},
737 {"help", 0, 0, 'h'},
738 {"check", 0, 0, 'k'},
739 {"replay", 1, 0, 'r'},
740 {"version", 0, 0, 'v'},
741 {"verbose", 0, 0, 'V'},
742 {0, 0, 0, 0}
743 };
744
745 while ((c = getopt_long(argc, argv, "c:kr:dfvVh", options, NULL)) != -1) {
746 switch(c) {
747 case 'c':
748 services_config = optarg;
749 break;
750 case 'k':
751 if (conf_read(services_config)) {
752 printf("%s appears to be a valid configuration file.\n", services_config);
753 } else {
754 printf("%s is an invalid configuration file.\n", services_config);
755 }
756 exit(0);
757 case 'r':
758 replay_file = fopen(optarg, "r");
759 if (!replay_file) {
760 fprintf(stderr, "Could not open %s for reading: %s (%d)\n",
761 optarg, strerror(errno), errno);
762 exit(0);
763 }
764 break;
765 case 'd':
766 debug = 1;
767 break;
768 case 'f':
769 daemon = 0;
770 break;
771 case 'v':
772 version();
773 license();
774 exit(0);
775 case 'h':
776 default:
777 usage(argv[0]);
778 exit(0);
779 }
780 }
781 }
782
783 version();
784
785 if (replay_file) {
786 /* We read a line here to "prime" the replay file parser, but
787 * mostly to get the right value of "now" for when we do the
788 * irc_introduce. */
789 replay_read_line();
790 boot_time = now;
791 } else {
792 boot_time = time(&now);
793 }
794
795 log_module(MAIN_LOG, LOG_INFO, "Initializing daemon...");
796 if (!conf_read(services_config)) {
797 log_module(MAIN_LOG, LOG_FATAL, "Unable to read %s.", services_config);
798 exit(0);
799 }
800
801 conf_register_reload(uplink_compile);
802
803 if (daemon) {
804 /* Attempt to fork into the background if daemon mode is on. */
805 pid = fork();
806 if (pid < 0) {
807 log_module(MAIN_LOG, LOG_FATAL, "Unable to fork: %s", strerror(errno));
808 } else if (pid > 0) {
809 log_module(MAIN_LOG, LOG_INFO, "Forking into the background (pid: %i)...", pid);
810 exit(0);
811 }
812 setsid();
813 /* Close these since we should not use them from now on. */
814 fclose(stdin);
815 fclose(stdout);
816 fclose(stderr);
817 }
818
819 if ((file_out = fopen(PID_FILE, "w")) == NULL) {
820 /* Create the main process' pid file */
821 log_module(MAIN_LOG, LOG_ERROR, "Unable to create PID file: %s", strerror(errno));
822 } else {
823 fprintf(file_out, "%i\n", (int)getpid());
824 fclose(file_out);
825 }
826
827 services_argc = argc;
828 services_argv = argv;
829
830 atexit(call_exit_funcs);
831 reg_exit_func(main_shutdown);
832
833 log_init();
ceafd592 834 MAIN_LOG = log_register_type("x3", "file:main.log");
d76ed9a9 835 if (debug)
836 log_debug();
d76ed9a9 837 timeq_init();
838 init_structs();
839 init_parse();
840 modcmd_init();
841 saxdb_init();
842 gline_init();
d914d1cb 843 shun_init();
d76ed9a9 844 sendmail_init();
845 helpfile_init();
846 conf_globals(); /* initializes the core services */
847 conf_rlimits();
848 modules_init();
849 message_register_table(msgtab);
850 modcmd_finalize();
851 saxdb_finalize();
852 helpfile_finalize();
853 modules_finalize();
854
855 /* The first exit func to be called *should* be saxdb_write_all(). */
856 reg_exit_func(saxdb_write_all);
857 if (replay_file) {
858 char *msg;
859 log_module(MAIN_LOG, LOG_INFO, "Beginning replay...");
860 srand(now);
861 replay_event_loop();
862 if ((msg = dict_sanity_check(clients))) {
863 log_module(MAIN_LOG, LOG_ERROR, "Clients insanity: %s", msg);
864 free(msg);
865 }
866 if ((msg = dict_sanity_check(channels))) {
867 log_module(MAIN_LOG, LOG_ERROR, "Channels insanity: %s", msg);
868 free(msg);
869 }
870 if ((msg = dict_sanity_check(servers))) {
871 log_module(MAIN_LOG, LOG_ERROR, "Servers insanity: %s", msg);
872 free(msg);
873 }
874 } else {
875 srand(time(&now));
876 ioset_run();
877 }
878 return 0;
879}