/[svn]/vendor/ircservices-5.1.24/init.c
ViewVC logotype

Contents of /vendor/ircservices-5.1.24/init.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1171 - (show annotations)
Fri Aug 12 20:00:46 2011 UTC (10 years, 3 months ago) by michael
File MIME type: text/x-chdr
File size: 46932 byte(s)
- Import ircservices-5.1.24. Don't ever think about modifying anything in this
  folder!
  Since Andrew Church has discontinued his services project in April 2011, the
  ircd-hybrid team has been given permissions to officially continue and
  maintain the already mentioned project.
  The name of this project will be changed for the reason being that the current
  name "IRC Services" is way too generic these days.

  Remember: Don't ever modify anything in here. This folder is kept for reference.

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 */

svnadmin@ircd-hybrid.org
ViewVC Help
Powered by ViewVC 1.1.28