ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/vendor/ircservices-5.1.24/init.c
Revision: 3389
Committed: Fri Apr 25 14:12:15 2014 UTC (9 years, 10 months ago) by michael
Content type: text/x-csrc
File size: 46932 byte(s)
Log Message:
- Imported ircservices-5.1.24

File Contents

# Content
1 /* Initalization and related routines.
2 *
3 * IRC Services is copyright (c) 1996-2009 Andrew Church.
4 * E-mail: <achurch@achurch.org>
5 * Parts written by Andrew Kempe and others.
6 * This program is free but copyrighted software; see the file GPL.txt for
7 * details.
8 */
9
10 #include "services.h"
11 #include "conffile.h"
12 #include "databases.h"
13 #include "messages.h"
14 #include "modules.h"
15 #include "language.h"
16 #include "version.h"
17
18 #if HAVE_SETGRENT
19 # include <grp.h>
20 #endif
21 #if HAVE_UMASK
22 # include <sys/stat.h> /* for umask() on some systems */
23 #endif
24 #if HAVE_GETSETRLIMIT
25 # include <sys/resource.h>
26 #endif
27
28 /*************************************************************************/
29
30 /* Callbacks used in this file: */
31 static int cb_introduce_user = -1;
32 static int cb_cmdline = -1;
33
34 /*************************************************************************/
35 /************************* Configuration options *************************/
36 /*************************************************************************/
37
38 /* Configurable variables: */
39
40 char **LoadModules;
41 int LoadModules_count;
42 char **LoadLanguageText;
43 int LoadLanguageText_count;
44
45 char * RemoteServer;
46 int32 RemotePort;
47 char * RemotePassword;
48 char * LocalHost;
49 int32 LocalPort;
50
51 char * ServerName;
52 char * ServerDesc;
53 char * ServiceUser;
54 char * ServiceHost;
55
56 char * LogFilename;
57 char PIDFilename[PATH_MAX+1];
58 char * MOTDFilename;
59 char * LockFilename;
60
61 int16 DefTimeZone;
62
63 int NoBouncyModes;
64 int NoSplitRecovery;
65 int StrictPasswords;
66 int NoAdminPasswordCheck;
67 int32 BadPassLimit;
68 time_t BadPassTimeout;
69 int32 BadPassWarning;
70 int32 IgnoreDecay;
71 double IgnoreThreshold;
72 time_t UpdateTimeout;
73 time_t WarningTimeout;
74 int32 ReadTimeout;
75 int32 TimeoutCheck;
76 time_t PingFrequency;
77 int32 MergeChannelModes;
78 int32 TotalNetBufferSize;
79 int32 NetBufferSize;
80 int32 NetBufferLimitInactive;
81 int32 NetBufferLimitIgnore;
82
83 char * EncryptionType;
84 char * GuestNickPrefix;
85 char **RejectEmail;
86 int RejectEmail_count;
87 int32 ListMax;
88 int LogMaxUsers;
89 int EnableGetpass;
90 int WallAdminPrivs;
91
92 /* Routines to handle special directives: */
93 static int do_DefTimeZone(const char *filename, int linenum, char *param);
94 static int do_IgnoreThreshold(const char *filename, int linenum, char *param);
95 static int do_LoadLanguageText(const char *filename, int linenum, char *param);
96 static int do_LoadModule(const char *filename, int linenum, char *param);
97 static int do_PIDFilename(const char *filename, int linenum, char *param);
98 static int do_RejectEmail(const char *filename, int linenum, char *param);
99 static int do_RunGroup(const char *filename, int linenum, char *param);
100 static int do_ServiceUser(const char *filename, int linenum, char *param);
101 static int do_Umask(const char *filename, int linenum, char *param);
102
103 /*************************************************************************/
104
105 /* List of directives for ircservices.conf (main configuration file): */
106
107 static ConfigDirective main_directives[] = {
108 { "BadPassLimit", { { CD_POSINT, 0, &BadPassLimit } } },
109 { "BadPassTimeout", { { CD_TIME, 0, &BadPassTimeout } } },
110 { "BadPassWarning", { { CD_POSINT, 0, &BadPassWarning } } },
111 { "DefTimeZone", { { CD_FUNC, 0, do_DefTimeZone } } },
112 { "EnableGetpass", { { CD_SET, 0, &EnableGetpass } } },
113 { "EncryptionType", { { CD_STRING, 0, &EncryptionType } } },
114 { "ExpireTimeout", { { CD_DEPRECATED, 0 } } },
115 { "GuestNickPrefix", { { CD_STRING, CF_DIRREQ, &GuestNickPrefix } } },
116 { "IgnoreDecay", { { CD_TIMEMSEC, 0, &IgnoreDecay } } },
117 { "IgnoreThreshold", { { CD_FUNC, 0, do_IgnoreThreshold } } },
118 { "ListMax", { { CD_POSINT, CF_DIRREQ, &ListMax } } },
119 { "LoadLanguageText", { { CD_FUNC, 0, do_LoadLanguageText } } },
120 { "LoadModule", { { CD_FUNC, 0, do_LoadModule } } },
121 { "LocalAddress", { { CD_STRING, 0, &LocalHost },
122 { CD_PORT, CF_OPTIONAL, &LocalPort } } },
123 { "LockFilename", { { CD_STRING, CF_DIRREQ, &LockFilename } } },
124 { "LogFilename", { { CD_STRING, CF_DIRREQ, &LogFilename } } },
125 { "LogMaxUsers", { { CD_SET, 0, &LogMaxUsers } } },
126 { "MergeChannelModes",{ { CD_TIMEMSEC, 0, &MergeChannelModes } } },
127 { "MOTDFilename", { { CD_STRING, CF_DIRREQ, &MOTDFilename } } },
128 { "NetBufferLimit", { { CD_POSINT, 0, &NetBufferLimitInactive },
129 { CD_POSINT, CF_OPTIONAL, &NetBufferLimitIgnore}}},
130 { "NetBufferSize", { { CD_POSINT, 0, &TotalNetBufferSize },
131 { CD_POSINT, CF_OPTIONAL, &NetBufferSize } } },
132 { "NoAdminPasswordCheck",{{CD_SET, 0, &NoAdminPasswordCheck } } },
133 { "NoBouncyModes", { { CD_SET, 0, &NoBouncyModes } } },
134 { "NoSplitRecovery", { { CD_SET, 0, &NoSplitRecovery } } },
135 { "PIDFilename", { { CD_FUNC, 0, do_PIDFilename } } },
136 { "PingFrequency", { { CD_TIME, 0, &PingFrequency } } },
137 { "ReadTimeout", { { CD_TIMEMSEC, 0, &ReadTimeout } } },
138 { "RejectEmail", { { CD_FUNC, 0, do_RejectEmail } } },
139 { "RemoteServer", { { CD_STRING, CF_DIRREQ, &RemoteServer },
140 { CD_PORT, 0, &RemotePort },
141 { CD_STRING, 0, &RemotePassword } } },
142 { "RunGroup", { { CD_FUNC, 0, do_RunGroup } } },
143 { "ServerDesc", { { CD_STRING, CF_DIRREQ, &ServerDesc } } },
144 { "ServerName", { { CD_STRING, CF_DIRREQ, &ServerName } } },
145 { "ServiceUser", { { CD_FUNC, CF_DIRREQ, do_ServiceUser } } },
146 { "StrictPasswords", { { CD_SET, 0, &StrictPasswords } } },
147 { "TimeoutCheck", { { CD_TIMEMSEC, CF_DIRREQ, &TimeoutCheck } } },
148 { "Umask", { { CD_FUNC, 0, do_Umask } } },
149 { "UpdateTimeout", { { CD_TIME, CF_DIRREQ, &UpdateTimeout } } },
150 { "WallAdminPrivs", { { CD_SET, 0, &WallAdminPrivs } } },
151 { "WarningTimeout", { { CD_TIME, CF_DIRREQ, &WarningTimeout } } },
152 { NULL }
153 };
154
155 /*************************************************************************/
156
157 /* read_config(): Read the main configuration file. If an error occurs
158 * while reading the file or a required directive is not
159 * found, print and log an appropriate error message and
160 * return 0; otherwise, return 1.
161 */
162
163 static int read_config(void)
164 {
165 int retval = 1;
166
167 if (!configure(NULL, main_directives, CONFIGURE_READ | CONFIGURE_SET))
168 return 0;
169
170 if (TotalNetBufferSize) {
171 if (TotalNetBufferSize < SOCK_MIN_BUFSIZE*2) {
172 config_error(IRCSERVICES_CONF, 0,
173 "Buffer size limit for NetBufferSize must be at"
174 " least %d", SOCK_MIN_BUFSIZE*2);
175 retval = 0;
176 } else {
177 /* Make sure it's a multiple of SOCK_MIN_BUFSIZE */
178 TotalNetBufferSize /= SOCK_MIN_BUFSIZE;
179 TotalNetBufferSize *= SOCK_MIN_BUFSIZE;
180 if (NetBufferSize) {
181 if (NetBufferSize < SOCK_MIN_BUFSIZE*2) {
182 config_error(IRCSERVICES_CONF, 0,
183 "Per-connection buffer size limit for"
184 " NetBufferSize must be at least %d",
185 SOCK_MIN_BUFSIZE*2);
186 retval = 0;
187 } else if (NetBufferSize > TotalNetBufferSize) {
188 config_error(IRCSERVICES_CONF, 0,
189 "Per-connection buffer size limit for"
190 " NetBufferSize must be no more than total"
191 " limit");
192 retval = 0;
193 } else {
194 NetBufferSize /= SOCK_MIN_BUFSIZE;
195 NetBufferSize *= SOCK_MIN_BUFSIZE;
196 }
197 }
198 }
199 } /* if (TotalNetBufferSize) */
200
201 if (NetBufferLimitInactive) {
202 if (!TotalNetBufferSize) {
203 NetBufferLimitInactive = NetBufferLimitIgnore = 0;
204 } else {
205 if (NetBufferLimitInactive > 99 || NetBufferLimitIgnore > 99) {
206 config_error(IRCSERVICES_CONF, 0,
207 "Thresholds for NetBufferLimit must be between"
208 " 1 and 99 inclusive");
209 retval = 0;
210 }
211 if (NetBufferLimitIgnore
212 && NetBufferLimitIgnore < NetBufferLimitInactive
213 ) {
214 config_error(IRCSERVICES_CONF, 0,
215 "Ignore threshold for NetBufferLimit must be"
216 " greater than or equal to inactive threshold");
217 retval = 0;
218 }
219 }
220 }
221
222 return retval;
223 }
224
225 /*************************************************************************/
226 /*************************************************************************/
227
228 /* Configuration directive callback functions: */
229
230 /*************************************************************************/
231
232 #define TZ_MAXLEN 16
233
234 static int do_DefTimeZone(const char *filename, int linenum, char *param)
235 {
236 static const char *origTZ = NULL;
237 static char newTZ[TZ_MAXLEN+1];
238 static char tzbuf[TZ_MAXLEN+4]; /* for setting TZ */
239
240 if (!filename) {
241 switch (linenum) {
242 case CDFUNC_INIT:
243 /* Prepare for reading config file */
244 *newTZ = 0;
245 if (!origTZ) {
246 /* Obtain current value of TZ environment variable (once
247 * only, at start of program), and truncate to TZ_MAXLEN */
248 origTZ = getenv("TZ");
249 if (!origTZ)
250 origTZ = "";
251 if (strlen(origTZ) > TZ_MAXLEN) {
252 static char new_origTZ[TZ_MAXLEN+1];
253 memcpy(new_origTZ, origTZ, TZ_MAXLEN);
254 new_origTZ[TZ_MAXLEN] = 0;
255 origTZ = new_origTZ;
256 }
257 }
258 break;
259 case CDFUNC_SET:
260 /* Copy data to config variables */
261 if (!origTZ) {
262 log("BUG: origTZ not set in do_DefTimeZone/CDFUNC_SET");
263 break;
264 }
265 snprintf(tzbuf, sizeof(tzbuf), "TZ=%s", *newTZ ? newTZ : origTZ);
266 if (putenv(tzbuf) < 0) {
267 log("Warning: putenv(%s) failed, time zone may be incorrect",
268 tzbuf);
269 }
270 break;
271 case CDFUNC_DECONFIG:
272 /* Reset to initial values */
273 if (!origTZ) {
274 log("BUG: origTZ not set in do_DefTimeZone/CDFUNC_DECONFIG");
275 break;
276 }
277 snprintf(tzbuf, sizeof(tzbuf), "TZ=%s", origTZ);
278 if (putenv(tzbuf) < 0) {
279 log("Warning: putenv(%s) failed, time zone may be incorrect",
280 tzbuf);
281 }
282 break;
283 } /* switch (linenum) */
284 } else { /* filename != NULL, process parameter */
285 if (strlen(param) > TZ_MAXLEN) {
286 config_error(filename, linenum, "DefTimeZone parameter must not"
287 " be longer than %d characters", TZ_MAXLEN);
288 return 0;
289 } else {
290 strbcpy(newTZ, param);
291 }
292 }
293 return 1;
294 }
295
296 /*************************************************************************/
297
298 static int do_IgnoreThreshold(const char *filename, int linenum, char *param)
299 {
300 static double new_IgnoreThreshold = 0;
301
302 if (filename) {
303 new_IgnoreThreshold = strtod(param, &param);
304 if (*param || new_IgnoreThreshold <= 0) {
305 config_error(filename, linenum, "Parameter for IgnoreThreshold"
306 " must be a positive number");
307 return 0;
308 }
309 } else if (linenum == CDFUNC_INIT) {
310 new_IgnoreThreshold = 0;
311 } else if (linenum == CDFUNC_SET) {
312 IgnoreThreshold = new_IgnoreThreshold;
313 } else if (linenum == CDFUNC_DECONFIG) {
314 IgnoreThreshold = 0;
315 }
316 return 1;
317 }
318
319 /*************************************************************************/
320
321 static int do_LoadLanguageText(const char *filename, int linenum, char *param)
322 {
323 static char **new_LoadLanguageText = NULL;
324 static int new_LoadLanguageText_count = 0;
325 int i;
326
327 if (!filename) {
328 switch (linenum) {
329 case CDFUNC_INIT:
330 /* Prepare for reading config file: clear out "new" array */
331 ARRAY_FOREACH (i, new_LoadLanguageText)
332 free(new_LoadLanguageText[i]);
333 free(new_LoadLanguageText);
334 new_LoadLanguageText = NULL;
335 new_LoadLanguageText_count = 0;
336 break;
337 case CDFUNC_SET:
338 /* Copy data to config variables */
339 ARRAY_FOREACH (i, LoadLanguageText)
340 free(LoadLanguageText[i]);
341 free(LoadLanguageText);
342 LoadLanguageText = new_LoadLanguageText;
343 LoadLanguageText_count = new_LoadLanguageText_count;
344 new_LoadLanguageText = NULL;
345 new_LoadLanguageText_count = 0;
346 break;
347 case CDFUNC_DECONFIG:
348 /* Clear out config variables */
349 ARRAY_FOREACH (i, LoadLanguageText)
350 free(LoadLanguageText[i]);
351 free(LoadLanguageText);
352 LoadLanguageText = NULL;
353 LoadLanguageText_count = 0;
354 break;
355 }
356 return 1;
357 } /* if (!filename) */
358
359 /* We can't use ARRAY_EXTEND because SIGUSR1 (for srealloc()) may not
360 * be ready yet */
361 if (new_LoadLanguageText_count+1 < new_LoadLanguageText_count) {
362 config_error(filename, linenum, "LoadLanguageText: too many files!");
363 return 0;
364 }
365 new_LoadLanguageText = realloc(new_LoadLanguageText,
366 sizeof(char *) * (new_LoadLanguageText_count+1));
367 param = strdup(param);
368 if (!new_LoadLanguageText || !param) {
369 config_error(filename, linenum, "LoadLanguageText: out of memory!");
370 return 0;
371 }
372 new_LoadLanguageText[new_LoadLanguageText_count++] = param;
373 return 1;
374 }
375
376 /*************************************************************************/
377
378 static int do_LoadModule(const char *filename, int linenum, char *param)
379 {
380 static char **new_LoadModules = NULL;
381 static int new_LoadModules_count = 0;
382 int i;
383
384 if (!filename) {
385 switch (linenum) {
386 case CDFUNC_INIT:
387 /* Prepare for reading config file: clear out "new" array */
388 ARRAY_FOREACH (i, new_LoadModules)
389 free(new_LoadModules[i]);
390 free(new_LoadModules);
391 new_LoadModules = NULL;
392 new_LoadModules_count = 0;
393 break;
394 case CDFUNC_SET:
395 /* Copy data to config variables */
396 ARRAY_FOREACH (i, LoadModules)
397 free(LoadModules[i]);
398 free(LoadModules);
399 LoadModules = new_LoadModules;
400 LoadModules_count = new_LoadModules_count;
401 new_LoadModules = NULL;
402 new_LoadModules_count = 0;
403 break;
404 case CDFUNC_DECONFIG:
405 /* Clear out config variables */
406 ARRAY_FOREACH (i, LoadModules)
407 free(LoadModules[i]);
408 free(LoadModules);
409 LoadModules = NULL;
410 LoadModules_count = 0;
411 break;
412 }
413 return 1;
414 } /* if (!filename) */
415
416 /* We can't use ARRAY_EXTEND because SIGUSR1 (for srealloc()) may not
417 * be ready yet */
418 if (new_LoadModules_count+1 < new_LoadModules_count) {
419 config_error(filename, linenum, "LoadModule: too many modules!");
420 return 0;
421 }
422 new_LoadModules = realloc(new_LoadModules,
423 sizeof(char *) * (new_LoadModules_count+1));
424 param = strdup(param);
425 if (!new_LoadModules || !param) {
426 config_error(filename, linenum, "LoadModule: out of memory!");
427 return 0;
428 }
429 new_LoadModules[new_LoadModules_count++] = param;
430 return 1;
431 }
432
433 /*************************************************************************/
434
435 static int do_PIDFilename(const char *filename, int linenum, char *param)
436 {
437 static char *new_PIDFilename = NULL;
438
439 if (!filename) {
440 switch (linenum) {
441 case CDFUNC_INIT:
442 /* Prepare for reading config file */
443 free(new_PIDFilename);
444 new_PIDFilename = NULL;
445 break;
446 case CDFUNC_SET:
447 /* Copy data to config variables */
448 if (new_PIDFilename) {
449 strbcpy(PIDFilename, new_PIDFilename);
450 } else {
451 *PIDFilename = 0;
452 }
453 free(new_PIDFilename);
454 new_PIDFilename = NULL;
455 break;
456 case CDFUNC_DECONFIG:
457 /* Clear out config variables. For PIDFilename, however, we
458 * leave the value in place so the file can be removed on exit
459 * properly. */
460 break;
461 }
462 return 1;
463 } /* if (!filename) */
464
465 if (strlen(param) >= sizeof(PIDFilename)) {
466 config_error(filename, linenum, "PIDFilename: Path too long");
467 return 0;
468 }
469 new_PIDFilename = strdup(param);
470 if (!new_PIDFilename) {
471 config_error(filename, linenum, "PIDFilename: Out of memory!");
472 return 0;
473 }
474 return 1;
475 }
476
477 /*************************************************************************/
478
479 static int do_RejectEmail(const char *filename, int linenum, char *param)
480 {
481 static char **new_RejectEmail = NULL;
482 static int new_RejectEmail_count = 0;
483 int i;
484
485 if (!filename) {
486 switch (linenum) {
487 case CDFUNC_INIT:
488 /* Prepare for reading config file: clear out "new" array */
489 ARRAY_FOREACH (i, new_RejectEmail)
490 free(new_RejectEmail[i]);
491 free(new_RejectEmail);
492 new_RejectEmail = NULL;
493 new_RejectEmail_count = 0;
494 break;
495 case CDFUNC_SET:
496 /* Copy data to config variables */
497 ARRAY_FOREACH (i, RejectEmail)
498 free(RejectEmail[i]);
499 free(RejectEmail);
500 RejectEmail = new_RejectEmail;
501 RejectEmail_count = new_RejectEmail_count;
502 new_RejectEmail = NULL;
503 new_RejectEmail_count = 0;
504 break;
505 case CDFUNC_DECONFIG:
506 /* Clear out config variables */
507 ARRAY_FOREACH (i, RejectEmail)
508 free(RejectEmail[i]);
509 free(RejectEmail);
510 RejectEmail = NULL;
511 RejectEmail_count = 0;
512 break;
513 }
514 return 1;
515 } /* if (!filename) */
516
517 ARRAY_EXTEND(new_RejectEmail);
518 new_RejectEmail[new_RejectEmail_count-1] = sstrdup(param);
519 return 1;
520 }
521
522 /*************************************************************************/
523
524 static int do_RunGroup(const char *filename, int linenum, char *param)
525 {
526 #ifndef SIZEOF_GID_T
527 config_error(filename, linenum,
528 "RunGroup: groups not supported on this system");
529 return 0;
530 #else
531 static gid_t groupnum = -1;
532 long tmp;
533
534 if (filename) {
535
536 if (*param == '=') {
537 if (!param[1]) {
538 config_error(filename, linenum,
539 "RunGroup: group number required after `='");
540 return 0;
541 }
542 errno = 0;
543 tmp = strtol(param+1, &param, 0);
544 # if SIZEOF_GID_T >= 4
545 if (errno == ERANGE)
546 # else
547 if (tmp < -32768 || tmp > 65535)
548 # endif
549 {
550 config_error(filename, linenum,
551 "RunGroup: group number out of range (0..%ld)",
552 # if SIZEOF_GID_T >= 4
553 0x7FFFFFFFL
554 # else
555 65535L
556 # endif
557 );
558 return 0;
559 }
560 groupnum = tmp;
561 } else { /* *param != '=' */
562 # if !HAVE_SETGRENT
563 config_error(filename, linenum,
564 "RunGroup: group names not supported on this system");
565 return 0;
566 # else
567 struct group *gr;
568 setgrent();
569 while ((gr = getgrent()) != NULL) {
570 if (strcmp(gr->gr_name, param) == 0)
571 break;
572 }
573 endgrent();
574 if (!gr) {
575 config_error(filename, linenum,
576 "RunGroup: unknown group `%s'", param);
577 return 0;
578 }
579 groupnum = gr->gr_gid;
580 # endif
581 }
582
583 } else { /* !filename */
584
585 # if HAVE_SETREGID
586 if (setregid(groupnum, groupnum) < 0) {
587 # else
588 if (setegid(groupnum) < 0 || setgid(groupnum) < 0) {
589 # endif
590 config_error(filename, linenum,
591 "RunGroup: unable to set group: %s", strerror(errno));
592 return 0;
593 }
594
595 } /* if (filename) */
596
597 return 1;
598
599 #endif /* have gid_t? */
600 }
601
602 /*************************************************************************/
603
604 static int do_ServiceUser(const char *filename, int linenum, char *param)
605 {
606 char *s;
607 static char *new_ServiceUser = NULL, *new_ServiceHost = NULL;
608
609 if (filename) {
610 /* Make sure the string has an @ and there's at least one character
611 * on each side */
612 s = strchr(param, '@');
613 if (!s || s == param || s[1] == 0) {
614 config_error(filename, linenum,
615 "`user@host' string required for ServiceUser");
616 return 0;
617 }
618 /* Store user and hostname parts (after freeing any old ones) */
619 *s++ = 0;
620 free(new_ServiceUser);
621 free(new_ServiceHost);
622 new_ServiceUser = strdup(param);
623 new_ServiceHost = strdup(s);
624 if (!new_ServiceUser || !new_ServiceHost) {
625 free(new_ServiceUser); /* still alloc'ed if ServiceHost failed */
626 new_ServiceUser = NULL;
627 config_error(filename, linenum, "Out of memory");
628 return 0;
629 }
630 } else if (linenum == CDFUNC_SET) {
631 /* Copy new values to config variables and clear */
632 if (new_ServiceUser && new_ServiceHost) { /* paranoia */
633 free(ServiceUser);
634 free(ServiceHost);
635 ServiceUser = new_ServiceUser;
636 ServiceHost = new_ServiceHost;
637 } else {
638 free(new_ServiceUser);
639 free(new_ServiceHost);
640 }
641 new_ServiceUser = new_ServiceHost = NULL;
642 } else if (linenum == CDFUNC_DECONFIG) {
643 /* Reset to defaults */
644 free(ServiceUser);
645 free(ServiceHost);
646 ServiceUser = NULL;
647 ServiceHost = NULL;
648 }
649 return 1;
650 }
651
652 /*************************************************************************/
653
654 static int do_Umask(const char *filename, int linenum, char *param)
655 {
656 #if !HAVE_UMASK
657 config_error(filename, linenum, "Umask is not supported on this system");
658 return 0;
659 #else
660 char *s;
661 static int umask_val = -1;
662
663 if (filename) {
664 umask_val = strtol(param, &s, 8);
665 if (*s || umask_val < 0 || umask_val > 0777) {
666 config_error(filename, linenum,
667 "Expected an octal value between 000 and 777");
668 return 0;
669 }
670 } else {
671 if (umask_val >= 0)
672 umask(umask_val);
673 umask_val = -1;
674 }
675 return 1;
676 #endif /* HAVE_UMASK */
677 }
678
679 /*************************************************************************/
680 /*********************** Introducing pseudoclients ***********************/
681 /*************************************************************************/
682
683 /* If `user' is the name of a Services pseudo-client, send a NICK command
684 * for that given pseudo-client. If `user' is NULL, send NICK commands for
685 * all the pseudo-clients. Return 1 if we sent a NICK command, else 0.
686 */
687
688 int introduce_user(const char *user)
689 {
690 int retval;
691
692 retval = call_callback_1(cb_introduce_user, user);
693 if (user == NULL) {
694 if (retval > 0)
695 log("introduce_user(): callback returned nonzero for user==NULL");
696 retval = 1;
697 }
698
699 /* Watch out for infinite loops... */
700 if (retval) {
701 #define LTSIZE 20
702 static int lasttimes[LTSIZE];
703 if (lasttimes[0] >= time(NULL)-3)
704 fatal("introduce_user() loop detected");
705 memmove(lasttimes, lasttimes+1, sizeof(lasttimes)-sizeof(*lasttimes));
706 lasttimes[LTSIZE-1] = time(NULL);
707 #undef LTSIZE
708 }
709
710 return retval;
711 }
712
713 /*************************************************************************/
714 /************************* Command-line parsing **************************/
715 /*************************************************************************/
716
717 /* Parse command-line options. If `call_modules' is zero, parse main
718 * options and ignore unrecognized ones; if nonzero, ignore main options,
719 * call "command line" callback to check module options, and fail on
720 * unrecognized ones. Return 0 if all went well, -1 for an error with an
721 * option, or 1 if Services should exit successfully. Calls exit(0) if
722 * "-help", "--help", or "-h" is present.
723 */
724
725 static int parse_options(int ac, char **av, int call_modules)
726 {
727 int i;
728 char *s, *t;
729
730 if (!call_modules) /* only initialize it once */
731 debug = 0;
732
733 for (i = 1; i < ac; i++) {
734 s = av[i];
735 if (*s == '-') {
736 s++;
737 if (*s == '-')
738 s++;
739 if (strncmp(s, "dir", 3) == 0 && (!s[3] || s[3] == '=')) {
740 if (!call_modules) {
741 if (!s[3]) {
742 fprintf(stderr, "-dir requires a parameter\n");
743 return -1;
744 }
745 services_dir = s+4;
746 }
747 } else if (strncmp(s, "remote", 6) == 0 && (!s[6] || s[6]=='=')) {
748 if (!call_modules) {
749 if (!s[6]) {
750 fprintf(stderr, "-remote requires hostname[:port]\n");
751 return -1;
752 }
753 s += 7;
754 t = strchr(s, ':');
755 if (t) {
756 *t = 0;
757 if (atoi(t+1) > 0)
758 RemotePort = atoi(t+1);
759 else {
760 fprintf(stderr, "-remote: port number must be a"
761 " positive integer. Using default.\n");
762 return -1;
763 }
764 }
765 free(RemoteServer);
766 RemoteServer = strdup(s);
767 if (!RemoteServer) {
768 fprintf(stderr, "Out of memory\n");
769 exit(-1);
770 }
771 if (t) /* put the colon back for next time around */
772 *t = ':';
773 }
774 } else if (strncmp(s, "log", 3) == 0 && (!s[3] || s[3] == '=')) {
775 if (!call_modules) {
776 if (!s[3]) {
777 fprintf(stderr, "-log requires a parameter\n");
778 return -1;
779 }
780 free(LogFilename);
781 LogFilename = sstrdup(s+4);
782 }
783 } else if (strcmp(s, "debug") == 0) {
784 if (!call_modules)
785 debug++;
786 } else if (strcmp(s, "readonly") == 0) {
787 if (!call_modules)
788 readonly = 1;
789 } else if (strcmp(s, "nofork") == 0) {
790 if (!call_modules)
791 nofork = 1;
792 } else if (strcmp(s, "noexpire") == 0) {
793 if (!call_modules)
794 noexpire = 1;
795 } else if (strcmp(s, "noakill") == 0) {
796 if (!call_modules)
797 noakill = 1;
798 } else if (strcmp(s, "forceload") == 0) {
799 if (!call_modules)
800 forceload = 1;
801 } else if (strcmp(s, "encrypt-all") == 0) {
802 if (!call_modules)
803 encrypt_all = 1;
804 } else if (strcmp(s, "h") == 0 || strcmp(s, "help") == 0
805 || strcmp(s, "-help") == 0) {
806 fputs(
807 "The following options are recognized:\n"
808 " -dir=directory Directory containing Services' data files\n"
809 " (e.g. /usr/local/lib/ircservices)\n"
810 " -remote=server[:port] Remote server to connect to\n"
811 " -log=filename Services log filename (e.g. services.log)\n"
812 " -debug Enable debugging mode--more info sent to log\n"
813 " (give option more times for more info)\n"
814 " -readonly Enable read-only mode--no changes to\n"
815 " databases allowed, .db files and log\n"
816 " not written\n"
817 " -nofork Do not fork after startup; log messages will\n"
818 " be written to terminal (as well as to\n"
819 " the log file if not in read-only mode)\n"
820 " -noexpire Prevents all expirations (nicknames, channels,\n"
821 " akills, session limit exceptions, etc.)\n"
822 " -noakill Disables autokill checking\n"
823 " -forceload Try to load as much of the databases as\n"
824 " possible, even if errors are encountered\n"
825 " -encrypt-all Re-encrypt all passwords on startup\n"
826 "Other options may be available depending on loaded modules; see the manual\n"
827 "for details.\n"
828 , stdout);
829 exit(0);
830 } else if (call_modules) {
831 int res;
832 t = strchr(s, '=');
833 if (t)
834 *t++ = 0;
835 res = call_callback_2(cb_cmdline, s, t);
836 switch (res) {
837 case 0:
838 fprintf(stderr, "Unknown option -%s. Use \"-help\" for"
839 " help.\n", s);
840 return -1;
841 case 1:
842 break;
843 case 2:
844 return -1;
845 case 3:
846 return 1;
847 default:
848 log("init: bad return value (%d) from command line"
849 " callback for `-%s%s%s'", res, s, t ? "=" : "", t);
850 return -1;
851 }
852 }
853 } else {
854 fprintf(stderr, "Non-option arguments not allowed\n");
855 return -1;
856 }
857 }
858 return 0;
859 }
860
861 /*************************************************************************/
862 /*************************** PID file handling ***************************/
863 /*************************************************************************/
864
865 /* Remove our PID file. Done at exit. */
866
867 static void remove_pidfile(void)
868 {
869 if (*PIDFilename)
870 remove(PIDFilename);
871 }
872
873 /*************************************************************************/
874
875 /* Create our PID file and write the PID to it. Returns nonzero on
876 * success, zero on failure.
877 */
878
879 static int write_pidfile(void)
880 {
881 FILE *pidfile;
882
883 pidfile = fopen(PIDFilename, "w");
884 if (!pidfile)
885 return 0;
886 fprintf(pidfile, "%d\n", (int)getpid());
887 fclose(pidfile);
888 atexit(remove_pidfile);
889 return 1;
890 }
891
892 /*************************************************************************/
893 /********************** Main initialization routine **********************/
894 /*************************************************************************/
895
896 /* Overall initialization routine. Returns 0 on success, -1 on failure.
897 * Never returns failure after forking / closing standard file descriptors.
898 */
899
900 int init(int ac, char **av)
901 {
902 int i;
903 int openlog_failed = 0, openlog_errno = 0;
904 int started_from_term = isatty(0) && isatty(1) && isatty(2);
905
906
907 /* Initialize memory log, to catch messages written before the log file
908 is opened (if any). */
909 open_memory_log();
910
911 /* Account for runtime memory. */
912 init_memory();
913
914 /* Parse command-line options; exit if an error occurs. */
915 if (parse_options(ac, av, 0) < 0)
916 return -1;
917
918 /* Chdir to Services data directory. */
919 if (chdir(services_dir) < 0) {
920 fprintf(stderr, "chdir(%s): %s\n", services_dir, strerror(errno));
921 return -1;
922 }
923
924 /* Read configuration file; exit if there are problems. */
925 if (!read_config())
926 return -1;
927
928 /* Re-parse command-line options (to override configuration file). */
929 parse_options(ac, av, 0);
930
931 /* Open logfile, and complain if we couldn't. */
932 set_logfile(LogFilename);
933 if (!open_log()) {
934 openlog_errno = errno;
935 if (started_from_term) {
936 fprintf(stderr, "Warning: unable to open log file %s: %s\n",
937 LogFilename, strerror(errno));
938 } else {
939 openlog_failed = 1;
940 }
941 }
942
943 /* Announce ourselves to the logfile. */
944 if (debug || readonly || noexpire) {
945 log("IRC Services %s starting up (options:%s%s%s)",
946 version_number,
947 debug ? " debug" : "",
948 readonly ? " readonly" : "",
949 noexpire ? " noexpire" : "");
950 } else {
951 log("IRC Services %s starting up", version_number);
952 }
953 start_time = time(NULL);
954
955 /* If in read-only mode, close the logfile again. */
956 if (readonly)
957 close_log();
958
959
960 /* If DUMPCORE is set and the OS supports get/setrlimit(), then attempt
961 * to remove, or at least raise to maximum, the core file size limit. */
962 #if DUMPCORE && HAVE_GETSETRLIMIT
963 {
964 struct rlimit rl = {RLIM_INFINITY,RLIM_INFINITY};
965 if (setrlimit(RLIMIT_CORE, &rl) < 0) {
966 log_perror("setrlimit(RLIMIT_CORE, RLIM_INFINITY)");
967 if ((i = getrlimit(RLIMIT_CORE, &rl)) < 0
968 || rl.rlim_cur >= rl.rlim_max
969 ) {
970 if (i < 0)
971 log_perror("getrlimit(RLIMIT_CORE)");
972 log("Unable to set core file size limit; core files %s.",
973 i < 0 || rl.rlim_cur == 0
974 ? "will not be generated" : "may be truncated");
975 } else {
976 rl.rlim_cur = rl.rlim_max;
977 if (setrlimit(RLIMIT_CORE, &rl) < 0) {
978 log_perror("setrlimit(RLIMIT_CORE, %ld)",
979 (long)rl.rlim_cur);
980 log("Unable to set core file size limit; core files may"
981 " be truncated.");
982 } else {
983 log("Core file size limited to %ldkB; core files may be"
984 " truncated.",
985 (long)(rl.rlim_cur<1024 ? 1 : rl.rlim_cur/1024));
986 }
987 }
988 } /* if (setrlimit(...) < 0) */
989 } /* setrlimit() block */
990 #endif
991
992
993 /* Initialize pseudo-random number generator. */
994 srand(time(NULL) ^ getppid() ^ getpid()<<16);
995
996 /* Initialize socket system. */
997 sock_set_buflimits(NetBufferSize, TotalNetBufferSize);
998 sock_set_rto(ReadTimeout);
999
1000 /* Initialize module system. This should be called before any
1001 * callbacks are registered. */
1002 if (!modules_init(ac, av))
1003 return -1;
1004
1005 /* Register our (and main.c's) callbacks. */
1006 cb_cmdline = register_callback("command line");
1007 cb_introduce_user = register_callback("introduce_user");
1008 cb_connect = register_callback("connect");
1009 cb_save_complete = register_callback("save data complete");
1010 if (cb_cmdline < 0 || cb_introduce_user < 0 || cb_connect < 0
1011 || cb_save_complete < 0
1012 ) {
1013 log("init(): Unable to register callbacks");
1014 return -1;
1015 }
1016
1017 /* Call other initialization routines. These are mainly (right now
1018 * only) for adding callbacks. */
1019 if (!user_init(ac,av) || !channel_init(ac,av) || !server_init(ac,av)
1020 || !process_init(ac,av) || !messages_init(ac,av)
1021 || !actions_init(ac,av) || !send_init(ac,av) || !database_init(ac,av)
1022 ) {
1023 return -1;
1024 }
1025
1026 /* Initialize mode handling routines. */
1027 mode_setup();
1028
1029 /* Initialize multi-language support. */
1030 if (!lang_init())
1031 return -1;
1032 log_debug(1, "Loaded languages");
1033
1034 /* Load modules. */
1035 ARRAY_FOREACH (i, LoadModules) {
1036 if (!load_module(LoadModules[i])) {
1037 log("Error loading modules, aborting");
1038 return -1;
1039 }
1040 }
1041 log_debug(1, "Loaded modules");
1042
1043 /* Load external language files (now that modules have had a chance to
1044 * add their own strings). */
1045 ARRAY_FOREACH (i, LoadLanguageText)
1046 load_ext_lang(LoadLanguageText[i]);
1047
1048 /* Check command-line arguments in modules, and exit if a module directs
1049 * us to. */
1050 i = parse_options(ac, av, 1);
1051 if (i != 0) {
1052 if (i < 0) {
1053 cleanup();
1054 return -1;
1055 } else {
1056 save_all_dbtables();
1057 cleanup();
1058 exit(0);
1059 }
1060 }
1061
1062 /* Make sure a protocol module was loaded. */
1063 if (protocol_features & PF_UNSET) {
1064 fprintf(stderr,
1065 "No protocol module has been loaded! Make sure to include a LoadModule\n"
1066 "directive for the appropriate module in the `%s' file.\n",
1067 IRCSERVICES_CONF);
1068 cleanup();
1069 return -1;
1070 }
1071
1072
1073 /* So far so good; let the user know everything is okay. */
1074 if (!nofork)
1075 fprintf(stderr, "Initialization successful, starting IRC Services.\n");
1076
1077 /* Detach ourselves if requested. */
1078 if (!nofork) {
1079 if ((i = fork()) < 0) {
1080 perror("fork()");
1081 return -1;
1082 } else if (i != 0) {
1083 #if MEMCHECKS
1084 /* Avoid a bogus "XXX bytes leaked on exit" message for the
1085 * parent. */
1086 uninit_memory();
1087 #endif
1088 exit(0);
1089 }
1090 if (started_from_term) {
1091 close(0);
1092 close(1);
1093 close(2);
1094 }
1095 if (setpgid(0, 0) < 0) {
1096 perror("setpgid()");
1097 return -1;
1098 }
1099 }
1100
1101 /*
1102 * Everything from here down needs to be done in the child process
1103 * (when forking).
1104 */
1105
1106 /* Write our PID to the PID file. */
1107 if (!write_pidfile())
1108 log_perror("Warning: cannot write to PID file %s", PIDFilename);
1109
1110 /* Set up signal handlers. */
1111 init_signals();
1112
1113 /* Connect to the remote server. */
1114 servsock = sock_new();
1115 if (!servsock)
1116 fatal_perror("Can't create server socket");
1117 sock_setcb(servsock, SCB_CONNECT, connect_callback);
1118 sock_setcb(servsock, SCB_DISCONNECT, disconnect_callback);
1119 if (conn(servsock, RemoteServer, RemotePort, LocalHost, LocalPort) < 0)
1120 fatal_perror("Can't connect to server (%s:%d)",
1121 RemoteServer, RemotePort);
1122 log_debug(1, "Initiated connection to %s:%d", RemoteServer, RemotePort);
1123
1124 /* Return success (connect_callback() will handle the rest). */
1125 return 0;
1126 }
1127
1128 /*************************************************************************/
1129 /**************************** Reconfiguration ****************************/
1130 /*************************************************************************/
1131
1132 int reconfigure(void)
1133 {
1134 char *old_RemoteServer, *old_RemotePassword, *old_LocalHost;
1135 int old_RemotePort, old_LocalPort;
1136 char *old_ServerName, *old_ServerDesc, *old_ServiceUser, *old_ServiceHost;
1137 char *old_LogFilename, *old_PIDFilename;
1138 char **old_LoadModules;
1139 int old_LoadModules_count;
1140 int LoadModules_insert; /* where to insert unloadable modules */
1141 int i, j;
1142 int retval = 1;
1143
1144 /* First save any data that we need after re-reading the conf file */
1145 old_RemoteServer = sstrdup(RemoteServer);
1146 old_RemotePort = RemotePort;
1147 old_RemotePassword = sstrdup(RemotePassword);
1148 old_LocalHost = LocalHost ? sstrdup(LocalHost) : NULL;
1149 old_LocalPort = LocalPort;
1150 old_ServerName = sstrdup(ServerName);
1151 old_ServerDesc = sstrdup(ServerDesc);
1152 old_ServiceUser = sstrdup(ServiceUser);
1153 old_ServiceHost = sstrdup(ServiceHost);
1154 old_LogFilename = sstrdup(LogFilename);
1155 old_PIDFilename = sstrdup(PIDFilename);
1156 old_LoadModules = LoadModules;
1157 old_LoadModules_count = LoadModules_count;
1158
1159 /* Re-read the configuration */
1160 if (!configure(NULL, main_directives, CONFIGURE_READ))
1161 return 0;
1162 /* Prevent current LoadModules (now old_LoadModules) from being freed */
1163 LoadModules = NULL;
1164 LoadModules_count = 0;
1165 /* Copy new values to configuration variables */
1166 configure(NULL, main_directives, CONFIGURE_SET);
1167
1168 /* Deal with configuration changes */
1169 if (stricmp(RemoteServer, old_RemoteServer) != 0
1170 || RemotePort != old_RemotePort
1171 || strcmp(RemotePassword, old_RemotePassword) != 0)
1172 log("warning: reconfigure: new RemoteServer value will not take"
1173 " effect until restart");
1174 if ((!old_LocalHost && LocalHost) || (old_LocalHost && !LocalHost)
1175 || (LocalHost && stricmp(LocalHost, old_LocalHost) != 0)
1176 || LocalPort != old_LocalPort)
1177 log("warning: reconfigure: new LocalHost value will not take"
1178 " effect until restart");
1179 if (strcmp(ServerName, old_ServerName) != 0)
1180 log("warning: reconfigure: new ServerName value will not take"
1181 " effect until restart");
1182 if (strcmp(ServerDesc, old_ServerDesc) != 0)
1183 log("warning: reconfigure: new ServerDesc value will not take"
1184 " effect until restart");
1185 if ((!old_ServiceUser && ServiceUser) || (!old_ServiceHost && ServiceHost)
1186 || (ServiceUser && strcmp(ServiceUser, old_ServiceUser) != 0)
1187 || (ServiceHost && strcmp(ServiceHost, old_ServiceHost) != 0))
1188 log("warning: reconfigure: new ServiceUser value will not take"
1189 " effect until restart");
1190 if (strcmp(LogFilename, old_LogFilename) != 0) {
1191 log("reconfigure: LogFilename changed, closing log file");
1192 set_logfile(LogFilename);
1193 if (reopen_log()) {
1194 log("reconfigure: LogFilename changed, writing to new log file");
1195 } else {
1196 log("warning: reconfigure: unable to open new log file `%s',"
1197 " reverting to old file `%s'", LogFilename, old_LogFilename);
1198 free(LogFilename);
1199 LogFilename = old_LogFilename;
1200 old_LogFilename = NULL; /* don't free it below */
1201 }
1202 }
1203 if (strcmp(PIDFilename, old_PIDFilename) != 0) {
1204 if (write_pidfile()) {
1205 /* Successfully wrote the new PID file, so delete the old one */
1206 remove(old_PIDFilename);
1207 } else {
1208 log("warning: reconfigure: unable to write new PID file `%s',"
1209 " reverting to old file `%s'", PIDFilename, old_PIDFilename);
1210 strbcpy(PIDFilename, old_PIDFilename);
1211 }
1212 }
1213
1214 /* Reset language data, then reload any new language files */
1215 reset_ext_lang();
1216 ARRAY_FOREACH (i, LoadLanguageText)
1217 load_ext_lang(LoadLanguageText[i]);
1218
1219 /* For modules, we need to:
1220 * - first unload any modules which don't have LoadModule lines
1221 * anymore--this has to be done in reverse order to avoid
1222 * dependency problems;
1223 * - next reconfigure any still-loaded modules (because newly-loaded
1224 * modules in the next step may depend on the new settings);
1225 * - finally load any modules which weren't loaded before but have
1226 * LoadModule lines now.
1227 */
1228 LoadModules_insert = LoadModules_count;
1229 for (i = old_LoadModules_count - 1; i >= 0; i--) {
1230 ARRAY_SEARCH_PLAIN(LoadModules, old_LoadModules[i], strcmp, j);
1231 if (j >= LoadModules_count) {
1232 Module *mod = find_module(old_LoadModules[i]);
1233 if (!mod) {
1234 log("BUG: reconfigure: module `%s' not available",
1235 old_LoadModules[i]);
1236 retval = 0;
1237 } else if (!unload_module(mod)) {
1238 log("warning: reconfigure: module `%s' could not be unloaded",
1239 old_LoadModules[i]);
1240 ARRAY_INSERT(LoadModules, LoadModules_insert);
1241 LoadModules[LoadModules_insert] = sstrdup(old_LoadModules[i]);
1242 retval = 0;
1243 }
1244 }
1245 }
1246 if (retval && !reconfigure_modules()) {
1247 log("warning: reconfigure: module reconfiguration failed");
1248 retval = 0;
1249 }
1250 if (retval) {
1251 ARRAY_FOREACH (i, LoadModules) {
1252 ARRAY_SEARCH_PLAIN(old_LoadModules, LoadModules[i], strcmp, j);
1253 if (j >= old_LoadModules_count) {
1254 if (!load_module(LoadModules[i])) {
1255 log("warning: reconfigure: new module `%s' could not"
1256 " be loaded", LoadModules[i]);
1257 ARRAY_REMOVE(LoadModules, i);
1258 i--;
1259 retval = 0;
1260 }
1261 }
1262 }
1263 }
1264
1265 /* Free old configuration data and return */
1266 free(old_RemoteServer);
1267 free(old_RemotePassword);
1268 free(old_LocalHost);
1269 free(old_ServerName);
1270 free(old_ServerDesc);
1271 free(old_ServiceUser);
1272 free(old_ServiceHost);
1273 free(old_LogFilename);
1274 free(old_PIDFilename);
1275 ARRAY_FOREACH (i, old_LoadModules)
1276 free(old_LoadModules[i]);
1277 free(old_LoadModules);
1278 return retval;
1279 }
1280
1281 /*************************************************************************/
1282 /******************************** Cleanup ********************************/
1283 /*************************************************************************/
1284
1285 void cleanup(void)
1286 {
1287 if (!*quitmsg)
1288 strbcpy(quitmsg, "Terminating, reason unknown");
1289 log("%s", quitmsg);
1290 set_cmode(NULL, NULL);
1291 unload_all_modules();
1292 if (servsock) {
1293 if (sock_isconn(servsock)) {
1294 send_cmd(ServerName, "SQUIT %s :%s", ServerName, quitmsg);
1295 disconn(servsock);
1296 }
1297 sock_free(servsock);
1298 }
1299 lang_cleanup();
1300 database_cleanup();
1301 send_cleanup();
1302 actions_cleanup();
1303 messages_cleanup();
1304 process_cleanup();
1305 server_cleanup();
1306 channel_cleanup();
1307 user_cleanup();
1308 unregister_callback(cb_save_complete);
1309 unregister_callback(cb_connect);
1310 unregister_callback(cb_introduce_user);
1311 unregister_callback(cb_cmdline);
1312 modules_cleanup();
1313 close_log();
1314 }
1315
1316 /*************************************************************************/
1317
1318 /*
1319 * Local variables:
1320 * c-file-style: "stroustrup"
1321 * c-file-offsets: ((case-label . *) (statement-case-intro . *))
1322 * indent-tabs-mode: nil
1323 * End:
1324 *
1325 * vim: expandtab shiftwidth=4:
1326 */