Convert time-related variables to consistently use "unsigned long".
[srvx.git] / src / main-common.c
1 extern FILE *replay_file;
2
3 unsigned long boot_time;
4 unsigned long burst_begin;
5 unsigned long now;
6 unsigned long burst_length;
7 struct log_type *MAIN_LOG;
8
9 int quit_services;
10 int max_cycles;
11
12 char *services_config = "srvx.conf";
13
14 char **services_argv;
15 int services_argc;
16
17 struct cManagerNode cManager;
18
19 struct policer_params *oper_policer_params, *luser_policer_params, *god_policer_params;
20
21 static const struct message_entry msgtab[] = {
22     { "MSG_NONE", "None" },
23     { "MSG_ON", "On" },
24     { "MSG_OFF", "Off" },
25     { "MSG_NEVER", "Never" },
26     { "MSG_SERVICE_IMMUNE", "$b%s$b may not be kicked, killed, banned, or deopped." },
27     { "MSG_SERVICE_PRIVILEGED", "$b%s$b is a privileged service." },
28     { "MSG_NOT_A_SERVICE", "$b%s$b is not a service bot." },
29     { "MSG_COMMAND_UNKNOWN", "$b%s$b is an unknown command." },
30     { "MSG_COMMAND_PRIVILEGED", "$b%s$b is a privileged command." },
31     { "MSG_COMMAND_DISABLED", "$b%s$b is a disabled command." },
32     { "MSG_SETTING_PRIVILEGED", "$b%s$b is a privileged setting." },
33     { "MSG_AUTHENTICATE", "You must first authenticate with $b$N$b." },
34     { "MSG_USER_AUTHENTICATE", "%s must first authenticate with $b$N$b." },
35     { "MSG_SET_EMAIL_ADDR", "You must first set your account's email address.  (Contact network staff if you cannot auth to your account.)" },
36     { "MSG_HANDLE_UNKNOWN", "Account $b%s$b has not been registered." },
37     { "MSG_NICK_UNKNOWN", "User with nick $b%s$b does not exist." },
38     { "MSG_CHANNEL_UNKNOWN", "Channel with name $b%s$b does not exist." },
39     { "MSG_SERVER_UNKNOWN", "Server with name $b%s$b does not exist or is not linked." },
40     { "MSG_MODULE_UNKNOWN", "No module has been registered with name $b%s$b." },
41     { "MSG_INVALID_MODES", "$b%s$b is an invalid set of channel modes." },
42     { "MSG_INVALID_GLINE", "Invalid G-line '%s'." },
43     { "MSG_INVALID_DURATION", "Invalid time span '%s'." },
44     { "MSG_NOT_TARGET_NAME", "You must provide the name of a channel or user." },
45     { "MSG_NOT_CHANNEL_NAME", "You must provide a valid channel name." },
46     { "MSG_INVALID_CHANNEL", "You must provide the name of a channel that exists." },
47     { "MSG_CHANNEL_ABSENT", "You aren't currently in $b%s$b." },
48     { "MSG_CHANNEL_USER_ABSENT", "$b%s$b isn't currently in $b%s$b." },
49     { "MSG_MISSING_PARAMS", "$b%s$b requires more parameters." },
50     { "MSG_DEPRECATED_COMMAND", "The $b%s$b command has been deprecated, and will be removed in the future; please use $b%s$b instead." },
51     { "MSG_OPER_SUSPENDED", "Your $b$O$b access has been suspended." },
52     { "MSG_USER_OUTRANKED", "$b%s$b outranks you (command has no effect)." },
53     { "MSG_STUPID_ACCESS_CHANGE", "Please ask someone $belse$b to demote you." },
54     { "MSG_NO_SEARCH_ACCESS", "You do not have enough access to search based on $b%s$b." },
55     { "MSG_INVALID_CRITERIA", "$b%s$b is an invalid search criteria." },
56     { "MSG_MATCH_COUNT", "Found $b%u$b matches." },
57     { "MSG_NO_MATCHES", "Nothing matched the criteria of your search." },
58     { "MSG_TOPIC_UNKNOWN", "No help on that topic." },
59     { "MSG_INVALID_BINARY", "$b%s$b is an invalid binary value." },
60     { "MSG_INTERNAL_FAILURE", "Your command could not be processed due to an internal failure." },
61     { "MSG_DB_UNKNOWN", "I do not know of a database named %s." },
62     { "MSG_DB_IS_MONDO", "Database %s is in the \"mondo\" database and cannot be written separately." },
63     { "MSG_DB_WRITE_ERROR", "Error while writing database %s." },
64     { "MSG_DB_WROTE_DB", "Wrote database %s (in %lu.%06lu seconds)." },
65     { "MSG_DB_WROTE_ALL", "Wrote all databases (in %lu.%06lu seconds)." },
66     { "MSG_AND", "and" },
67     { "MSG_0_SECONDS", "0 seconds" },
68     { "MSG_YEAR", "year" },
69     { "MSG_YEARS", "years" },
70     { "MSG_WEEK", "week" },
71     { "MSG_WEEKS", "weeks" },
72     { "MSG_DAY", "day" },
73     { "MSG_DAYS", "days" },
74     { "MSG_HOUR", "hour" },
75     { "MSG_HOURS", "hours" },
76     { "MSG_MINUTE", "minute" },
77     { "MSG_MINUTES", "minutes" },
78     { "MSG_SECOND", "second" },
79     { "MSG_SECONDS", "seconds" },
80     { NULL, NULL }
81 };
82
83 void uplink_select(char *name);
84
85 static int
86 uplink_insert(const char *key, void *data, UNUSED_ARG(void *extra))
87 {
88     struct uplinkNode *uplink = malloc(sizeof(struct uplinkNode));
89     struct record_data *rd = data;
90     struct addrinfo hints, *ai;
91     int enabled = 1;
92     char *str;
93
94     if(!uplink)
95     {
96         return 0;
97     }
98
99     uplink->name = (char *)key;
100     uplink->host = database_get_data(rd->d.object, "address", RECDB_QSTRING);
101
102     str = database_get_data(rd->d.object, "port", RECDB_QSTRING);
103     uplink->port = str ? atoi(str) : 6667;
104     uplink->password = database_get_data(rd->d.object, "password", RECDB_QSTRING);
105     uplink->their_password = database_get_data(rd->d.object, "uplink_password", RECDB_QSTRING);
106
107     str = database_get_data(rd->d.object, "enabled", RECDB_QSTRING);
108     if(str)
109     {
110         enabled = atoi(str) ? 1 : 0;
111     }
112
113     cManager.enabled += enabled;
114
115     str = database_get_data(rd->d.object, "max_tries", RECDB_QSTRING);
116     uplink->max_tries = str ? atoi(str) : 3;
117     uplink->flags = enabled ? 0 : UPLINK_UNAVAILABLE;
118     uplink->state = DISCONNECTED;
119     uplink->tries = 0;
120
121     str = database_get_data(rd->d.object, "bind_address", RECDB_QSTRING);
122     memset(&hints, 0, sizeof(hints));
123     hints.ai_flags = AI_PASSIVE;
124     hints.ai_socktype = SOCK_STREAM;
125     if (!getaddrinfo(str, NULL, &hints, &ai))
126     {
127         uplink->bind_addr_len = ai->ai_addrlen;
128         uplink->bind_addr = calloc(1, ai->ai_addrlen);
129         memcpy(uplink->bind_addr, ai->ai_addr, ai->ai_addrlen);
130         freeaddrinfo(ai);
131     }
132     else
133     {
134         uplink->bind_addr = NULL;
135         uplink->bind_addr_len = 0;
136     }
137
138     uplink->next = cManager.uplinks;
139     uplink->prev = NULL;
140
141     if(cManager.uplinks)
142     {
143         cManager.uplinks->prev = uplink;
144     }
145
146     cManager.uplinks = uplink;
147
148     /* If the configuration is being reloaded, set the current uplink
149        to the reloaded equivalent, if possible. */
150     if(cManager.uplink
151        && enabled
152        && !irccasecmp(uplink->host, cManager.uplink->host)
153        && uplink->port == cManager.uplink->port)
154     {
155         uplink->state = cManager.uplink->state;
156         uplink->tries = cManager.uplink->tries;
157         cManager.uplink = uplink;
158     }
159
160     return 0;
161 }
162
163 void
164 uplink_compile(void)
165 {
166     const char *cycles;
167     dict_t conf_node;
168     struct uplinkNode *oldUplinks = NULL, *oldUplink = NULL;
169
170     /* Save the old uplinks, we'll remove them later. */
171     oldUplink = cManager.uplink;
172     oldUplinks = cManager.uplinks;
173
174     cycles = conf_get_data("server/max_cycles", RECDB_QSTRING);
175     max_cycles = cycles ? atoi(cycles) : 30;
176     if(!(conf_node = conf_get_data("uplinks", RECDB_OBJECT)))
177     {
178         log_module(MAIN_LOG, LOG_FATAL, "No uplinks configured; giving up.");
179         exit(1);
180     }
181
182     cManager.enabled = 0;
183     dict_foreach(conf_node, uplink_insert, NULL);
184
185     /* Remove the old uplinks, if any. It doesn't matter if oldUplink (below)
186        is a reference to one of these, because it won't get dereferenced. */
187     if(oldUplinks)
188     {
189         struct uplinkNode *uplink, *next;
190
191         oldUplinks->prev->next = NULL;
192
193         for(uplink = oldUplinks; uplink; uplink = next)
194         {
195             next = uplink->next;
196             free(uplink->bind_addr);
197             free(uplink);
198         }
199     }
200
201     /* If the uplink hasn't changed, it's either NULL or pointing at
202        an uplink that was just deleted, select a new one. */
203     if(cManager.uplink == oldUplink)
204     {
205         if(oldUplink)
206         {
207             irc_squit(self, "Uplinks updated; selecting new uplink.", NULL);
208         }
209
210         cManager.uplink = NULL;
211         uplink_select(NULL);
212     }
213 }
214
215 struct uplinkNode *
216 uplink_find(char *name)
217 {
218     struct uplinkNode *uplink;
219
220     if(!cManager.enabled || !cManager.uplinks)
221     {
222         return NULL;
223     }
224
225     for(uplink = cManager.uplinks; uplink; uplink = uplink->next)
226     {
227         if(!strcasecmp(uplink->name, name))
228         {
229             return uplink;
230         }
231     }
232
233     return NULL;
234 }
235
236 void
237 uplink_select(char *name)
238 {
239     struct uplinkNode *start, *uplink, *next;
240     int stop;
241
242     if(!cManager.enabled || !cManager.uplinks)
243     {
244         log_module(MAIN_LOG, LOG_FATAL, "No uplinks enabled; giving up.");
245         exit(1);
246     }
247
248     if(!cManager.uplink)
249     {
250         start = cManager.uplinks;
251     }
252     else
253     {
254         start = cManager.uplink->next;
255         if(!start)
256         {
257             start = cManager.uplinks;
258         }
259     }
260
261     stop = 0;
262     for(uplink = start; uplink; uplink = next)
263     {
264         next = uplink->next ? uplink->next : cManager.uplinks;
265
266         if(stop)
267         {
268             uplink = NULL;
269             break;
270         }
271
272         /* We've wrapped around the list. */
273         if(next == start)
274         {
275             sleep((cManager.cycles >> 1) * 5);
276             cManager.cycles++;
277
278             if(max_cycles && (cManager.cycles >= max_cycles))
279             {
280                 log_module(MAIN_LOG, LOG_ERROR, "Maximum uplink list cycles exceeded; giving up.");
281                 exit(1);
282             }
283
284             /* Give the uplink currently in 'uplink' consideration,
285                and if not selected, break on the next iteration. */
286             stop = 1;
287         }
288
289         /* Skip bad uplinks. */
290         if(uplink->flags & UPLINK_UNAVAILABLE)
291         {
292             continue;
293         }
294
295         if(name && irccasecmp(uplink->name, name))
296         {
297             /* If we were told to connect to a specific uplink, don't stop
298                until we find it.
299             */
300             continue;
301         }
302
303         /* It would be possible to track uplink health through a variety
304            of statistics and only break on the best uplink. For now, break
305            on the first available one.
306         */
307
308         break;
309     }
310
311     if(!uplink)
312     {
313         /* We are shit outta luck if every single uplink has been passed
314            over. Use the current uplink if possible. */
315         if(!cManager.uplink || cManager.uplink->flags & UPLINK_UNAVAILABLE)
316         {
317             log_module(MAIN_LOG, LOG_ERROR, "All available uplinks exhausted; giving up.");
318             exit(1);
319         }
320
321         return;
322     }
323
324     cManager.uplink = uplink;
325 }
326
327 int
328 uplink_connect(void)
329 {
330     struct uplinkNode *uplink = cManager.uplink;
331
332     if(uplink->state != DISCONNECTED)
333     {
334         return 0;
335     }
336
337     if(uplink->flags & UPLINK_UNAVAILABLE)
338     {
339         uplink_select(NULL);
340         uplink = cManager.uplink;
341     }
342
343     if(uplink->tries)
344     {
345         /* This delay could scale with the number of tries. */
346         sleep(2);
347     }
348
349     if(!create_socket_client(uplink))
350     {
351         if(uplink->max_tries && (uplink->tries >= uplink->max_tries))
352         {
353             /* This is a bad uplink, move on. */
354             uplink->flags |= UPLINK_UNAVAILABLE;
355             uplink_select(NULL);
356         }
357
358         return 0;
359     }
360     else
361     {
362         uplink->state = AUTHENTICATING;
363         irc_introduce(uplink->password);
364     }
365
366     return 1;
367 }
368
369 void
370 received_ping(void)
371 {
372     /* This function is called when a ping is received. Take it as
373        a sign of link health and reset the connection manager
374        information. */
375
376     cManager.cycles = 0;
377 }
378
379 static exit_func_t *ef_list;
380 static unsigned int ef_size = 0, ef_used = 0;
381
382 void reg_exit_func(exit_func_t handler)
383 {
384     if (ef_used == ef_size) {
385         if (ef_size) {
386             ef_size <<= 1;
387             ef_list = realloc(ef_list, ef_size*sizeof(exit_func_t));
388         } else {
389             ef_size = 8;
390             ef_list = malloc(ef_size*sizeof(exit_func_t));
391         }
392     }
393     ef_list[ef_used++] = handler;
394 }
395
396 void call_exit_funcs(void)
397 {
398     unsigned int n = ef_used;
399
400     /* Call them in reverse order because we initialize logs, then
401      * nickserv, then chanserv, etc., and they register their exit
402      * funcs in that order, and there are some dependencies (for
403      * example, ChanServ requires NickServ to not have cleaned up).
404      */
405
406     while (n > 0) {
407         ef_list[--n]();
408     }
409     free(ef_list);
410     ef_used = ef_size = 0;
411 }
412
413 int
414 set_policer_param(const char *param, void *data, void *extra)
415 {
416     struct record_data *rd = data;
417     const char *str = GET_RECORD_QSTRING(rd);
418     if (str) {
419         policer_params_set(extra, param, str);
420     }
421     return 0;
422 }
423
424 static void
425 conf_globals(void)
426 {
427     const char *info;
428     dict_t dict;
429
430     info = conf_get_data("services/global/nick", RECDB_QSTRING);
431     if (info && (info[0] == '.'))
432         info = NULL;
433     init_global(info);
434
435     info = conf_get_data("services/nickserv/nick", RECDB_QSTRING);
436     if (info && (info[0] == '.'))
437         info = NULL;
438     init_nickserv(info);
439
440     info = conf_get_data("services/chanserv/nick", RECDB_QSTRING);
441     if (info && (info[0] == '.'))
442         info = NULL;
443     init_chanserv(info);
444
445     god_policer_params = policer_params_new();
446     if ((dict = conf_get_data("policers/commands-god", RECDB_OBJECT))) {
447         dict_foreach(dict, set_policer_param, god_policer_params);
448     } else {
449         policer_params_set(god_policer_params, "size", "30");
450         policer_params_set(god_policer_params, "drain-rate", "1");
451     }
452     oper_policer_params = policer_params_new();
453     if ((dict = conf_get_data("policers/commands-oper", RECDB_OBJECT))) {
454         dict_foreach(dict, set_policer_param, oper_policer_params);
455     } else {
456         policer_params_set(oper_policer_params, "size", "10");
457         policer_params_set(oper_policer_params, "drain-rate", "1");
458     }
459     luser_policer_params = policer_params_new();
460     if ((dict = conf_get_data("policers/commands-luser", RECDB_OBJECT))) {
461         dict_foreach(dict, set_policer_param, luser_policer_params);
462     } else {
463         policer_params_set(luser_policer_params, "size", "5");
464         policer_params_set(luser_policer_params, "drain-rate", "0.50");
465     }
466
467     info = conf_get_data("services/opserv/nick", RECDB_QSTRING);
468     if (info && (info[0] == '.'))
469         info = NULL;
470     init_opserv(info);
471 }
472
473 #ifdef HAVE_SYS_RESOURCE_H
474
475 static int
476 set_item_rlimit(const char *name, void *data, void *extra)
477 {
478     long rsrc;
479     int found;
480     struct record_data *rd = data;
481     struct rlimit rlim;
482     const char *str;
483
484     rsrc = (long)dict_find(extra, name, &found);
485     if (!found) {
486         log_module(MAIN_LOG, LOG_ERROR, "Invalid rlimit \"%s\" in rlimits section.", name);
487         return 0;
488     }
489     if (!(str = GET_RECORD_QSTRING(rd))) {
490         log_module(MAIN_LOG, LOG_ERROR, "Missing or invalid parameter type for rlimit \"%s\".", name);
491         return 0;
492     }
493     if (getrlimit(rsrc, &rlim) < 0) {
494         log_module(MAIN_LOG, LOG_ERROR, "Couldn't get rlimit \"%s\": errno %d: %s", name, errno, strerror(errno));
495         return 0;
496     }
497     rlim.rlim_cur = ParseVolume(str);
498     if (setrlimit(rsrc, &rlim) < 0) {
499         log_module(MAIN_LOG, LOG_ERROR, "Couldn't set rlimit \"%s\": errno %d: %s", name, errno, strerror(errno));
500     }
501     return 0;
502 }
503
504 static void
505 conf_rlimits(void)
506 {
507     dict_t dict, values;
508
509     values = dict_new();
510     dict_insert(values, "data", (void*)RLIMIT_DATA);
511     dict_insert(values, "stack", (void*)RLIMIT_STACK);
512 #ifdef RLIMIT_VMEM
513     dict_insert(values, "vmem", (void*)RLIMIT_VMEM);
514 #else
515 #ifdef RLIMIT_AS
516     dict_insert(values, "vmem", (void*)RLIMIT_AS);
517 #endif
518 #endif
519     if ((dict = conf_get_data("rlimits", RECDB_OBJECT))) {
520         dict_foreach(dict, set_item_rlimit, values);
521     }
522     dict_delete(values);
523 }
524
525 #else
526
527 static void
528 conf_rlimits(void)
529 {
530 }
531
532 #endif
533
534 static void
535 usage(char *self)
536 {
537     /* We can assume we have getopt_long(). */
538     printf("Usage: %s [-c config] [-r log] [-d] [-f] [-v|-h]\n"
539            " -c, --config         selects a different configuration file.\n"
540            " -d, --debug          enables debug mode.\n"
541            " -f, --foreground     run srvx in the foreground.\n"
542            " -h, --help           prints this usage message.\n"
543            " -k, --check          checks the configuration file's syntax.\n"
544            " -r, --replay         replay a log file (for debugging).\n"
545            " -v, --version        prints this program's version.\n"
546            , self);
547 }
548
549 static void
550 version()
551 {
552     printf("    --------------------------------------------------\n"
553            "    - "PACKAGE_STRING" ("CODENAME"), Built: " __DATE__ ", " __TIME__".\n"
554            "    - Copyright (C) 2000 - 2007, srvx Development Team\n"
555            "    - Version tag %s\n"
556            "    --------------------------------------------------\n",
557            git_version);
558 }
559
560 static void
561 license()
562 {
563     printf("\n"
564            "This program is free software; you can redistribute it and/or modify\n"
565            "it under the terms of the GNU General Public License as published by\n"
566            "the Free Software Foundation; either version 2 of the License, or\n"
567            "(at your option) any later version.\n"
568            "\n"
569            "This program is distributed in the hope that it will be useful,\n"
570            "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
571            "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
572            "GNU General Public License for more details.\n"
573            "\n"
574            "You should have received a copy of the GNU General Public License\n"
575            "along with this program; if not, write to the Free Software\n"
576            "Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n\n");
577 }
578
579 void main_shutdown(void)
580 {
581     struct uplinkNode *ul, *ul_next;
582     ioset_cleanup();
583     for (ul = cManager.uplinks; ul; ul = ul_next) {
584         ul_next = ul->next;
585         free(ul->bind_addr);
586         free(ul);
587     }
588     tools_cleanup();
589     conf_close();
590 #if defined(PID_FILE)
591     remove(PID_FILE);
592 #endif
593     policer_params_delete(god_policer_params);
594     policer_params_delete(oper_policer_params);
595     policer_params_delete(luser_policer_params);
596     if (replay_file)
597         fclose(replay_file);
598 }