]>
Commit | Line | Data |
---|---|---|
3bd189cb JR |
1 | /************************************************************************ |
2 | * IRC - Internet Relay Chat, ircd/ircd.c | |
3 | * Copyright (C) 1990 Jarkko Oikarinen and | |
4 | * University of Oulu, Computing Center | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License as published by | |
8 | * the Free Software Foundation; either version 1, or (at your option) | |
9 | * any later version. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License | |
17 | * along with this program; if not, write to the Free Software | |
18 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
19 | */ | |
20 | ||
21 | #ifndef lint | |
22 | static const volatile char rcsid[] = "@(#)$Id: ircd.c,v 1.165 2010/08/11 17:39:00 bif Exp $"; | |
23 | #endif | |
24 | ||
25 | #include "os.h" | |
26 | #include "s_defines.h" | |
27 | #define IRCD_C | |
28 | #include "s_externs.h" | |
29 | #undef IRCD_C | |
30 | ||
31 | aClient me; /* That's me */ | |
32 | aClient *client = &me; /* Pointer to beginning of Client list */ | |
33 | ||
34 | static void open_debugfile(void), setup_signals(void), io_loop(void); | |
35 | ||
36 | #if defined(USE_IAUTH) | |
37 | static RETSIGTYPE s_slave(int s); | |
38 | #endif | |
39 | ||
40 | istat_t istat; | |
41 | iconf_t iconf; | |
42 | char **myargv; | |
43 | int rehashed = 0; | |
44 | int portnum = -1; /* Server port number, listening this */ | |
45 | char *configfile = IRCDCONF_PATH; /* Server configuration file */ | |
46 | int debuglevel = -1; /* Server debug level */ | |
47 | int bootopt = BOOT_PROT|BOOT_STRICTPROT; /* Server boot option flags */ | |
48 | int serverbooting = 1; | |
49 | int firstrejoindone = 0; /* Server rejoined the network after | |
50 | start */ | |
51 | char *debugmode = ""; /* -"- -"- -"- -"- */ | |
52 | char *sbrk0; /* initial sbrk(0) */ | |
53 | char *tunefile = IRCDTUNE_PATH; | |
54 | volatile static int dorehash = 0, | |
55 | dorestart = 0, | |
56 | restart_iauth = 0; | |
57 | ||
58 | #ifdef DELAY_CLOSE | |
59 | time_t nextdelayclose = 0; /* time for next delayed close */ | |
60 | #endif | |
61 | time_t nextconnect = 1; /* time for next try_connections call */ | |
62 | time_t nextgarbage = 1; /* time for next collect_channel_garbage call*/ | |
63 | time_t nextping = 1; /* same as above for check_pings() */ | |
64 | time_t nextdnscheck = 0; /* next time to poll dns to force timeouts */ | |
65 | time_t nextexpire = 1; /* next expire run on the dns cache */ | |
66 | time_t nextiarestart = 1; /* next time to check if iauth is alive */ | |
67 | time_t nextpreference = 1; /* time for next calculate_preference call */ | |
68 | #ifdef TKLINE | |
69 | time_t nexttkexpire = 0; /* time for next tkline_expire call */ | |
70 | #endif | |
71 | ||
72 | aClient *ListenerLL = NULL; /* Listeners linked list */ | |
73 | ||
74 | RETSIGTYPE s_die(int s) | |
75 | { | |
76 | sendto_serv_v(NULL, SV_UID, ":%s SDIE", me.serv->sid); | |
77 | #ifdef USE_SYSLOG | |
78 | (void)syslog(LOG_CRIT, "Server Killed By SIGTERM"); | |
79 | (void)closelog(); | |
80 | #endif | |
81 | logfiles_close(); | |
82 | ircd_writetune(tunefile); | |
83 | flush_connections(me.fd); | |
84 | #ifdef UNIXPORT | |
85 | { | |
86 | aClient *acptr; | |
87 | char unixpath[256]; | |
88 | for (acptr = ListenerLL; acptr; acptr = acptr->next) | |
89 | { | |
90 | if (IsUnixSocket(acptr)) | |
91 | { | |
92 | sprintf(unixpath, "%s/%d", | |
93 | acptr->confs->value.aconf->host, | |
94 | acptr->confs->value.aconf->port); | |
95 | (void)unlink(unixpath); | |
96 | } | |
97 | } | |
98 | } | |
99 | #endif | |
100 | exit(-1); | |
101 | } | |
102 | ||
103 | #if defined(USE_IAUTH) | |
104 | static RETSIGTYPE s_slave(int s) | |
105 | { | |
106 | # if POSIX_SIGNALS | |
107 | struct sigaction act; | |
108 | ||
109 | act.sa_handler = s_slave; | |
110 | act.sa_flags = 0; | |
111 | (void)sigemptyset(&act.sa_mask); | |
112 | (void)sigaddset(&act.sa_mask, SIGUSR1); | |
113 | (void)sigaction(SIGUSR1, &act, NULL); | |
114 | # else | |
115 | (void)signal(SIGUSR1, s_slave); | |
116 | # endif | |
117 | restart_iauth = 1; | |
118 | } | |
119 | #endif | |
120 | ||
121 | static RETSIGTYPE s_rehash(int s) | |
122 | { | |
123 | #if POSIX_SIGNALS | |
124 | struct sigaction act; | |
125 | ||
126 | act.sa_handler = s_rehash; | |
127 | act.sa_flags = 0; | |
128 | (void)sigemptyset(&act.sa_mask); | |
129 | (void)sigaddset(&act.sa_mask, SIGHUP); | |
130 | (void)sigaction(SIGHUP, &act, NULL); | |
131 | #else | |
132 | (void)signal(SIGHUP, s_rehash); /* sysV -argv */ | |
133 | #endif | |
134 | if (dorehash >= 1) | |
135 | dorehash = 2; | |
136 | else | |
137 | dorehash = 1; | |
138 | } | |
139 | ||
140 | void restart(char *mesg) | |
141 | { | |
142 | #ifdef USE_SYSLOG | |
143 | (void)syslog(LOG_WARNING, "Restarting Server because: %s (%u)", mesg, | |
144 | (u_int)((char *)sbrk((size_t)0)-sbrk0)); | |
145 | #endif | |
146 | sendto_flag(SCH_NOTICE, "Restarting server because: %s (%u)", mesg, | |
147 | (u_int)((char *)sbrk((size_t)0)-sbrk0)); | |
148 | server_reboot(); | |
149 | } | |
150 | ||
151 | RETSIGTYPE s_restart(int s) | |
152 | { | |
153 | #if POSIX_SIGNALS | |
154 | struct sigaction act; | |
155 | ||
156 | act.sa_handler = s_restart; | |
157 | act.sa_flags = 0; | |
158 | (void)sigemptyset(&act.sa_mask); | |
159 | (void)sigaddset(&act.sa_mask, SIGINT); | |
160 | (void)sigaction(SIGINT, &act, NULL); | |
161 | #else | |
162 | ||
163 | (void)signal(SIGHUP, SIG_DFL); /* sysV -argv */ | |
164 | #endif | |
165 | if (bootopt & BOOT_TTY) | |
166 | { | |
167 | fprintf(stderr, "Caught SIGINT, terminating...\n"); | |
168 | exit(-1); | |
169 | } | |
170 | ||
171 | dorestart = 1; | |
172 | } | |
173 | ||
174 | void server_reboot(void) | |
175 | { | |
176 | Reg int i; | |
177 | ||
178 | sendto_flag(SCH_NOTICE, "Aieeeee!!! Restarting server... (%u)", | |
179 | (u_int)((char *)sbrk((size_t)0)-sbrk0)); | |
180 | ||
181 | Debug((DEBUG_NOTICE,"Restarting server...")); | |
182 | flush_connections(me.fd); | |
183 | /* | |
184 | ** fd 0 must be 'preserved' if either the -d or -i options have | |
185 | ** been passed to us before restarting. | |
186 | */ | |
187 | #ifdef USE_SYSLOG | |
188 | (void)closelog(); | |
189 | #endif | |
190 | logfiles_close(); | |
191 | for (i = 3; i < MAXCONNECTIONS; i++) | |
192 | (void)close(i); | |
193 | if (!(bootopt & (BOOT_TTY|BOOT_DEBUG))) | |
194 | { | |
195 | (void)close(2); | |
196 | (void)close(1); | |
197 | } | |
198 | if ((bootopt & BOOT_CONSOLE) || isatty(0)) | |
199 | (void)close(0); | |
200 | ircd_writetune(tunefile); | |
201 | if (!(bootopt & BOOT_INETD)) | |
202 | { | |
203 | (void)execv(IRCD_PATH, myargv); | |
204 | #ifdef USE_SYSLOG | |
205 | /* Have to reopen since it has been closed above */ | |
206 | ||
207 | openlog(mybasename(myargv[0]), LOG_PID|LOG_NDELAY, LOG_FACILITY); | |
208 | syslog(LOG_CRIT, "execv(%s,%s) failed: %m\n", IRCD_PATH, | |
209 | myargv[0]); | |
210 | closelog(); | |
211 | #endif | |
212 | Debug((DEBUG_FATAL,"Couldn't restart server: %s", | |
213 | strerror(errno))); | |
214 | } | |
215 | exit(-1); | |
216 | } | |
217 | ||
218 | ||
219 | /* | |
220 | ** try_connections | |
221 | ** | |
222 | ** Scan through configuration and try new connections. | |
223 | ** Returns the calendar time when the next call to this | |
224 | ** function should be made latest. (No harm done if this | |
225 | ** is called earlier or later...) | |
226 | */ | |
227 | static time_t try_connections(time_t currenttime) | |
228 | { | |
229 | Reg aConfItem *aconf; | |
230 | Reg aClient *cptr; | |
231 | aConfItem **pconf; | |
232 | int confrq; | |
233 | time_t next = 0; | |
234 | aClass *cltmp; | |
235 | aConfItem *con_conf = NULL; | |
236 | int allheld = 1; | |
237 | #ifdef DISABLE_DOUBLE_CONNECTS | |
238 | int i; | |
239 | #endif | |
240 | ||
241 | if ((bootopt & BOOT_STANDALONE)) | |
242 | return 0; | |
243 | ||
244 | Debug((DEBUG_NOTICE,"Connection check at : %s", | |
245 | myctime(currenttime))); | |
246 | for (aconf = conf; aconf; aconf = aconf->next ) | |
247 | { | |
248 | /* not a C-line */ | |
249 | if (!(aconf->status & (CONF_CONNECT_SERVER|CONF_ZCONNECT_SERVER))) | |
250 | continue; | |
251 | ||
252 | /* not a candidate for AC */ | |
253 | if (aconf->port <= 0) | |
254 | continue; | |
255 | ||
256 | cltmp = Class(aconf); | |
257 | /* not a candidate for AC */ | |
258 | if (MaxLinks(cltmp) == 0) | |
259 | continue; | |
260 | ||
261 | /* minimize next to lowest hold time of all AC-able C-lines */ | |
262 | if (next > aconf->hold || next == 0) | |
263 | next = aconf->hold; | |
264 | ||
265 | /* skip conf if the use of it is on hold until future. */ | |
266 | if (aconf->hold > currenttime) | |
267 | continue; | |
268 | ||
269 | /* at least one candidate not held for future, good */ | |
270 | allheld = 0; | |
271 | ||
272 | /* see if another link in this conf is allowed */ | |
273 | if (Links(cltmp) >= MaxLinks(cltmp)) | |
274 | continue; | |
275 | ||
276 | /* next possible check after connfreq secs for this C-line */ | |
277 | confrq = get_con_freq(cltmp); | |
278 | aconf->hold = currenttime + confrq; | |
279 | ||
280 | /* is this server already connected? */ | |
281 | cptr = find_name(aconf->name, (aClient *)NULL); | |
282 | if (!cptr) | |
283 | cptr = find_mask(aconf->name, (aClient *)NULL); | |
284 | ||
285 | /* matching client already exists, no AC to it */ | |
286 | if (cptr) | |
287 | continue; | |
288 | ||
289 | /* no such server, check D-lines */ | |
290 | if (find_denied(aconf->name, Class(cltmp))) | |
291 | continue; | |
292 | ||
293 | #ifdef DISABLE_DOUBLE_CONNECTS | |
294 | /* Much better would be traversing only unknown | |
295 | ** connections, but this requires another global | |
296 | ** variable, adding and removing from there in | |
297 | ** proper places etc. Some day. --B. */ | |
298 | for (i = highest_fd; i >= 0; i--) | |
299 | { | |
300 | if (!(cptr = local[i]) || | |
301 | cptr->status > STAT_UNKNOWN) | |
302 | { | |
303 | continue; | |
304 | } | |
305 | /* an unknown traveller we have */ | |
306 | if ( | |
307 | #ifndef INET6 | |
308 | cptr->ip.s_addr == aconf->ipnum.s_addr | |
309 | #else | |
310 | !memcmp(cptr->ip.s6_addr, | |
311 | aconf->ipnum.s6_addr, 16) | |
312 | #endif | |
313 | ) | |
314 | { | |
315 | /* IP the same. Coincidence? Maybe. | |
316 | ** Do not cause havoc with double connect. */ | |
317 | break; | |
318 | } | |
319 | cptr = NULL; | |
320 | } | |
321 | if (cptr) | |
322 | { | |
323 | sendto_flag(SCH_SERVER, "AC to %s postponed", aconf->name); | |
324 | continue; | |
325 | } | |
326 | #endif | |
327 | /* we have a candidate! */ | |
328 | ||
329 | /* choose the best. */ | |
330 | if (!con_conf || | |
331 | (con_conf->pref > aconf->pref && aconf->pref >= 0) || | |
332 | (con_conf->pref == -1 && | |
333 | Class(cltmp) > ConfClass(con_conf))) | |
334 | { | |
335 | con_conf = aconf; | |
336 | } | |
337 | /* above is my doubt: if we always choose best connection | |
338 | ** and it always fails connecting, we may never try another, | |
339 | ** even "worse"; what shall we do? --Beeth */ | |
340 | } | |
341 | if (con_conf) | |
342 | { | |
343 | if (con_conf->next) /* are we already last? */ | |
344 | { | |
345 | for (pconf = &conf; (aconf = *pconf); | |
346 | pconf = &(aconf->next)) | |
347 | /* put the current one at the end and | |
348 | * make sure we try all connections | |
349 | */ | |
350 | if (aconf == con_conf) | |
351 | *pconf = aconf->next; | |
352 | (*pconf = con_conf)->next = 0; | |
353 | } | |
354 | ||
355 | /* "Penalty" for being the best, so in next call of | |
356 | * try_connections() other servers have chance. --B. */ | |
357 | con_conf->hold += get_con_freq(Class(con_conf)); | |
358 | ||
359 | if (iconf.aconnect == 0 || iconf.aconnect == 2 && | |
360 | timeofday - iconf.split > DELAYCHASETIMELIMIT) | |
361 | { | |
362 | sendto_flag(SCH_NOTICE, | |
363 | "Connection to %s deferred. Autoconnect " | |
364 | "administratively disabled", con_conf->name); | |
365 | } | |
366 | else if (connect_server(con_conf, (aClient *)NULL, | |
367 | (struct hostent *)NULL) == 0) | |
368 | { | |
369 | sendto_flag(SCH_NOTICE, | |
370 | "Connection to %s[%s] activated.", | |
371 | con_conf->name, con_conf->host); | |
372 | } | |
373 | } | |
374 | else | |
375 | if (allheld == 0) /* disable AC only when some C: got checked */ | |
376 | { | |
377 | /* No suitable conf for AC was found, so why bother checking | |
378 | ** again? If some server quits, it'd get reenabled --B. */ | |
379 | next = 0; | |
380 | } | |
381 | Debug((DEBUG_NOTICE,"Next connection check : %s", myctime(next))); | |
382 | return (next); | |
383 | } | |
384 | ||
385 | /* | |
386 | * calculate preference value based on accumulated stats. | |
387 | */ | |
388 | time_t calculate_preference(time_t currenttime) | |
389 | { | |
390 | aConfItem *aconf; | |
391 | aCPing *cp; | |
392 | double f, f2; | |
393 | ||
394 | for (aconf = conf; aconf; aconf = aconf->next) | |
395 | { | |
396 | /* not a C-line */ | |
397 | if (!(aconf->status & (CONF_CONNECT_SERVER|CONF_ZCONNECT_SERVER))) | |
398 | continue; | |
399 | ||
400 | /* not a candidate for AC */ | |
401 | if (aconf->port <= 0) | |
402 | continue; | |
403 | ||
404 | /* send (udp) pings for all AC-able C-lines, we'll use it to | |
405 | ** calculate preferences */ | |
406 | send_ping(aconf); | |
407 | ||
408 | if (!(cp = aconf->ping) || !cp->seq || !cp->recvd) | |
409 | { | |
410 | aconf->pref = -1; | |
411 | } | |
412 | else | |
413 | { | |
414 | f = (double)cp->recvd / (double)cp->seq; | |
415 | f2 = pow(f, (double)20.0); | |
416 | if (f2 < (double)0.001) | |
417 | f = (double)0.001; | |
418 | else | |
419 | f = f2; | |
420 | f2 = (double)cp->ping / (double)cp->recvd; | |
421 | f = f2 / f; | |
422 | if (f > 100000.0) | |
423 | f = 100000.0; | |
424 | aconf->pref = (u_int) (f * (double)100.0); | |
425 | } | |
426 | } | |
427 | return currenttime + 60; | |
428 | } | |
429 | ||
430 | /* Checks all clients against KILL lines. (And remove them, if found.) | |
431 | ** Only MAXDELAYEDKILLS at a time or all, if not defined. | |
432 | ** Returns 1, if still work to do, 0 if finished. | |
433 | */ | |
434 | static int delayed_kills(time_t currenttime) | |
435 | { | |
436 | static time_t dk_rehashed = 0; /* time of last rehash we're processing */ | |
437 | static int dk_lastfd; /* fd we last checked */ | |
438 | static int dk_checked; /* # clients we checked */ | |
439 | static int dk_killed; /* # clients we killed */ | |
440 | Reg aClient *cptr; | |
441 | Reg int i, j; | |
442 | ||
443 | if (dk_rehashed == 0) | |
444 | { | |
445 | dk_rehashed = currenttime; | |
446 | dk_checked = 0; | |
447 | dk_killed = 0; | |
448 | dk_lastfd = highest_fd; | |
449 | } | |
450 | #ifdef MAXDELAYEDKILLS | |
451 | /* checking only this many clients each time */ | |
452 | j = dk_lastfd - MAXDELAYEDKILLS + 1; | |
453 | if (j < 0) | |
454 | #endif | |
455 | j = 0; | |
456 | ||
457 | for (i = dk_lastfd; i >= j; i--) | |
458 | { | |
459 | int kflag = 0; | |
460 | char *reason = NULL; | |
461 | ||
462 | if (!(cptr = local[i]) || !IsPerson(cptr)) | |
463 | { | |
464 | /* for K:lines we're interested only in local, | |
465 | ** fully registered clients */ | |
466 | if (j > 0) | |
467 | j--; | |
468 | continue; | |
469 | } | |
470 | ||
471 | dk_checked++; | |
472 | kflag = find_kill(cptr, 0, &reason); | |
473 | ||
474 | /* If the client is a user and a KILL line was found | |
475 | ** to be active, close this connection. */ | |
476 | if (kflag == -1) | |
477 | { | |
478 | char buf[100]; | |
479 | ||
480 | dk_killed++; | |
481 | sendto_flag(SCH_NOTICE, | |
482 | "Kill line active for %s", | |
483 | get_client_name(cptr, FALSE)); | |
484 | cptr->exitc = EXITC_KLINE; | |
485 | if (!BadPtr(reason)) | |
486 | sprintf(buf, "Kill line active: %.80s", | |
487 | reason); | |
488 | (void)exit_client(cptr, cptr, &me, (reason) ? | |
489 | buf : "Kill line active"); | |
490 | } | |
491 | } | |
492 | dk_lastfd = i; /* from which fd to start next time */ | |
493 | Debug((DEBUG_DEBUG, "DelayedKills killed %d and counting...", | |
494 | dk_killed)); | |
495 | ||
496 | if (dk_lastfd < 0) | |
497 | { | |
498 | sendto_flag(SCH_NOTICE, "DelayedKills checked %d killed %d " | |
499 | "in %d sec", dk_checked, dk_killed, | |
500 | currenttime - dk_rehashed); | |
501 | dk_rehashed = 0; | |
502 | if (rehashed == 2) | |
503 | { | |
504 | /* there was rehash queued, start again */ | |
505 | return 1; | |
506 | } | |
507 | return 0; | |
508 | } | |
509 | return rehashed; | |
510 | } | |
511 | ||
512 | static time_t check_pings(time_t currenttime) | |
513 | { | |
514 | #ifdef TIMEDKLINES | |
515 | static time_t lkill = 0; | |
516 | #endif | |
517 | Reg aClient *cptr; | |
518 | Reg int kflag = 0; | |
519 | aClient *bysptr = NULL; | |
520 | int ping = 0, i; | |
521 | time_t oldest = 0, timeout; | |
522 | char *reason = NULL; | |
523 | ||
524 | for (i = highest_fd; i >= 0; i--) | |
525 | { | |
526 | if (!(cptr = local[i]) || IsListener(cptr)) | |
527 | continue; | |
528 | ||
529 | #ifdef TIMEDKLINES | |
530 | kflag = 0; | |
531 | reason = NULL; | |
532 | /* | |
533 | ** Once per TIMEDKLINES seconds. | |
534 | ** (1 minute is minimum resolution in K-line field) | |
535 | */ | |
536 | if ((currenttime - lkill > TIMEDKLINES) | |
537 | && IsPerson(cptr) && !IsKlineExempt(cptr)) | |
538 | { | |
539 | kflag = find_kill(cptr, 1, &reason); | |
540 | } | |
541 | #endif | |
542 | ping = IsRegistered(cptr) ? cptr->ping : ACCEPTTIMEOUT; | |
543 | Debug((DEBUG_DEBUG, "c(%s) %d p %d k %d a %d", | |
544 | cptr->name, cptr->status, ping, kflag, | |
545 | currenttime - cptr->lasttime)); | |
546 | /* | |
547 | * Ok, so goto's are ugly and can be avoided here but this code | |
548 | * is already indented enough so I think its justified. -avalon | |
549 | */ | |
550 | if (!kflag && IsRegistered(cptr) && | |
551 | (ping >= currenttime - cptr->lasttime)) | |
552 | goto ping_timeout; | |
553 | /* | |
554 | * If the server hasnt talked to us in 2*ping seconds | |
555 | * and it has a ping time, then close its connection. | |
556 | * If the client is a user and a KILL line was found | |
557 | * to be active, close this connection too. | |
558 | */ | |
559 | if (kflag || | |
560 | ((currenttime - cptr->lasttime) >= (2 * ping) && | |
561 | (cptr->flags & FLAGS_PINGSENT)) || | |
562 | (!IsRegistered(cptr) && | |
563 | (currenttime - cptr->firsttime) >= ping)) | |
564 | { | |
565 | if (!IsRegistered(cptr) && | |
566 | (DoingDNS(cptr) || DoingAuth(cptr) || | |
567 | DoingXAuth(cptr))) | |
568 | { | |
569 | if (cptr->authfd >= 0) | |
570 | { | |
571 | (void)close(cptr->authfd); | |
572 | cptr->authfd = -1; | |
573 | cptr->count = 0; | |
574 | *cptr->buffer = '\0'; | |
575 | } | |
576 | Debug((DEBUG_NOTICE, "%s/%c%s timeout %s", | |
577 | (DoingDNS(cptr)) ? "DNS" : "dns", | |
578 | (DoingXAuth(cptr)) ? "X" : "x", | |
579 | (DoingAuth(cptr)) ? "AUTH" : "auth", | |
580 | get_client_name(cptr,TRUE))); | |
581 | del_queries((char *)cptr); | |
582 | ClearAuth(cptr); | |
583 | #if defined(USE_IAUTH) | |
584 | if (DoingDNS(cptr) || DoingXAuth(cptr)) | |
585 | { | |
586 | if (DoingDNS(cptr) && | |
587 | (iauth_options & XOPT_EXTWAIT)) | |
588 | { | |
589 | /* iauth wants more time */ | |
590 | sendto_iauth("%d d", cptr->fd); | |
591 | ClearDNS(cptr); | |
592 | cptr->lasttime = currenttime; | |
593 | continue; | |
594 | } | |
595 | if (DoingXAuth(cptr) && | |
596 | (iauth_options & XOPT_NOTIMEOUT)) | |
597 | { | |
598 | cptr->exitc = EXITC_AUTHTOUT; | |
599 | sendto_iauth("%d T", cptr->fd); | |
600 | exit_client(cptr, cptr, &me, | |
601 | "Authentication Timeout"); | |
602 | continue; | |
603 | } | |
604 | sendto_iauth("%d T", cptr->fd); | |
605 | SetDoneXAuth(cptr); | |
606 | } | |
607 | #endif | |
608 | ClearDNS(cptr); | |
609 | ClearXAuth(cptr); | |
610 | ClearWXAuth(cptr); | |
611 | cptr->firsttime = currenttime; | |
612 | cptr->lasttime = currenttime; | |
613 | continue; | |
614 | } | |
615 | if (IsServer(cptr) || IsConnecting(cptr) || | |
616 | IsHandshake(cptr)) | |
617 | { | |
618 | if (cptr->serv && cptr->serv->byuid[0]) | |
619 | { | |
620 | bysptr = find_uid(cptr->serv->byuid, | |
621 | NULL); | |
622 | } | |
623 | /* we are interested only in *remote* opers */ | |
624 | if (bysptr && !MyConnect(bysptr)) | |
625 | { | |
626 | sendto_one(bysptr, ":%s NOTICE %s :" | |
627 | "No response from %s, closing" | |
628 | " link", ME, bysptr->name, | |
629 | get_client_name(cptr, FALSE)); | |
630 | } | |
631 | sendto_flag(SCH_NOTICE, | |
632 | "No response from %s closing link", | |
633 | get_client_name(cptr, FALSE)); | |
634 | } | |
635 | /* | |
636 | * this is used for KILL lines with time restrictions | |
637 | * on them - send a message to the user being killed | |
638 | * first. | |
639 | */ | |
640 | if (kflag && IsPerson(cptr)) | |
641 | { | |
642 | char buf[100]; | |
643 | ||
644 | sendto_flag(SCH_NOTICE, | |
645 | "Kill line active for %s", | |
646 | get_client_name(cptr, FALSE)); | |
647 | cptr->exitc = EXITC_KLINE; | |
648 | if (!BadPtr(reason)) | |
649 | sprintf(buf, "Kill line active: %.80s", | |
650 | reason); | |
651 | (void)exit_client(cptr, cptr, &me, (reason) ? | |
652 | buf : "Kill line active"); | |
653 | } | |
654 | else | |
655 | { | |
656 | cptr->exitc = EXITC_PING; | |
657 | (void)exit_client(cptr, cptr, &me, | |
658 | "Ping timeout"); | |
659 | } | |
660 | continue; | |
661 | } | |
662 | else if (IsRegistered(cptr) && | |
663 | (cptr->flags & FLAGS_PINGSENT) == 0) | |
664 | { | |
665 | /* | |
666 | * if we havent PINGed the connection and we havent | |
667 | * heard from it in a while, PING it to make sure | |
668 | * it is still alive. | |
669 | */ | |
670 | cptr->flags |= FLAGS_PINGSENT; | |
671 | /* not nice but does the job */ | |
672 | cptr->lasttime = currenttime - ping; | |
673 | sendto_one(cptr, "PING :%s", me.name); | |
674 | } | |
675 | ping_timeout: | |
676 | timeout = cptr->lasttime + ping; | |
677 | while (timeout <= currenttime) | |
678 | timeout += ping; | |
679 | if (timeout < oldest || !oldest) | |
680 | oldest = timeout; | |
681 | } | |
682 | #ifdef TIMEDKLINES | |
683 | if (currenttime - lkill > 60) | |
684 | lkill = currenttime; | |
685 | #endif | |
686 | if (!oldest || oldest < currenttime) | |
687 | oldest = currenttime + PINGFREQUENCY; | |
688 | if (oldest < currenttime + 30) | |
689 | oldest += 30; | |
690 | Debug((DEBUG_NOTICE,"Next check_ping() call at: %s, %d %d %d", | |
691 | myctime(oldest), ping, oldest, currenttime)); | |
692 | return (oldest); | |
693 | } | |
694 | ||
695 | ||
696 | static void setup_me(aClient *mp) | |
697 | { | |
698 | struct passwd *p; | |
699 | ||
700 | p = getpwuid(getuid()); | |
701 | strncpyzt(mp->username, (p) ? p->pw_name : "unknown", | |
702 | sizeof(mp->username)); | |
703 | (void)get_my_name(mp, mp->sockhost, sizeof(mp->sockhost)-1); | |
704 | /* I think we need no hostp, especially fake one --B. */ | |
705 | mp->hostp = NULL; | |
706 | if (mp->serv->namebuf[0] == '\0') | |
707 | strncpyzt(mp->serv->namebuf, mp->sockhost, sizeof(mp->serv->namebuf)); | |
708 | if (me.info == DefInfo) | |
709 | me.info = mystrdup("IRCers United"); | |
710 | mp->lasttime = mp->since = mp->firsttime = time(NULL); | |
711 | mp->hopcount = 0; | |
712 | mp->authfd = -1; | |
713 | mp->auth = mp->username; | |
714 | mp->confs = NULL; | |
715 | mp->flags = 0; | |
716 | mp->acpt = mp->from = mp; | |
717 | mp->next = NULL; | |
718 | mp->user = NULL; | |
719 | mp->fd = -1; | |
720 | SetMe(mp); | |
721 | mp->serv->snum = find_server_num (ME); | |
722 | /* we don't fill our own IP -> 0 as ip lenght */ | |
723 | (void) make_user(mp,0); | |
724 | istat.is_users++; /* here, cptr->next is NULL, see make_user() */ | |
725 | mp->user->flags |= FLAGS_OPER; | |
726 | mp->serv->up = mp; | |
727 | mp->serv->maskedby = mp; | |
728 | mp->serv->version |= SV_UID; | |
729 | mp->user->server = find_server_string(mp->serv->snum); | |
730 | strncpyzt(mp->user->username, (p) ? p->pw_name : "unknown", | |
731 | sizeof(mp->user->username)); | |
732 | (void) strcpy(mp->user->host, mp->name); | |
733 | SetEOB(mp); | |
734 | istat.is_eobservers = 1; | |
735 | ||
736 | (void)add_to_client_hash_table(mp->name, mp); | |
737 | (void)add_to_sid_hash_table(mp->serv->sid, mp); | |
738 | strncpyzt(mp->serv->verstr, PATCHLEVEL, sizeof(mp->serv->verstr)); | |
739 | setup_server_channels(mp); | |
740 | } | |
741 | ||
742 | /* | |
743 | ** bad_command | |
744 | ** This is called when the commandline is not acceptable. | |
745 | ** Give error message and exit without starting anything. | |
746 | */ | |
747 | static void bad_command(void) | |
748 | { | |
749 | (void)printf( | |
750 | "Usage: ircd [-a] [-b] [-c]%s [-h servername] [-q] [-i]" | |
751 | "[-T [tunefile]] [-p (strict|on|off)] [-s] [-v] [-t] %s\n", | |
752 | #ifdef CMDLINE_CONFIG | |
753 | " [-f config]", | |
754 | #else | |
755 | "", | |
756 | #endif | |
757 | #ifdef DEBUGMODE | |
758 | " [-x loglevel]" | |
759 | #else | |
760 | "" | |
761 | #endif | |
762 | ); | |
763 | (void)printf("Server not started\n\n"); | |
764 | exit(-1); | |
765 | } | |
766 | ||
767 | int main(int argc, char *argv[]) | |
768 | { | |
769 | uid_t uid, euid; | |
770 | ||
771 | sbrk0 = (char *)sbrk((size_t)0); | |
772 | uid = getuid(); | |
773 | euid = geteuid(); | |
774 | ||
775 | #ifdef CHROOTDIR | |
776 | ircd_res_init(); | |
777 | if (chdir(ROOT_PATH)!=0) | |
778 | { | |
779 | perror("chdir"); | |
780 | (void)fprintf(stderr,"%s: Cannot chdir: %s.\n", IRCD_PATH, | |
781 | ROOT_PATH); | |
782 | exit(5); | |
783 | } | |
784 | if (chroot(ROOT_PATH)!=0) | |
785 | { | |
786 | perror("chroot"); | |
787 | (void)fprintf(stderr,"%s: Cannot chroot: %s.\n", IRCD_PATH, | |
788 | ROOT_PATH); | |
789 | exit(5); | |
790 | } | |
791 | #endif /*CHROOTDIR*/ | |
792 | ||
793 | #ifdef ZIP_LINKS | |
794 | if (zlib_version[0] == '0') | |
795 | { | |
796 | fprintf(stderr, "zlib version 1.0 or higher required\n"); | |
797 | exit(1); | |
798 | } | |
799 | if (zlib_version[0] != ZLIB_VERSION[0]) | |
800 | { | |
801 | fprintf(stderr, "incompatible zlib version\n"); | |
802 | exit(1); | |
803 | } | |
804 | if (strcmp(zlib_version, ZLIB_VERSION) != 0) | |
805 | { | |
806 | fprintf(stderr, "warning: different zlib version\n"); | |
807 | } | |
808 | #endif | |
809 | ||
810 | myargv = argv; | |
811 | (void)umask(077); /* better safe than sorry --SRB */ | |
812 | bzero((char *)&me, sizeof(me)); | |
813 | ||
814 | make_server(&me); | |
815 | register_server(&me); | |
816 | ||
817 | version = make_version(); /* Generate readable version string */ | |
818 | ||
819 | /* | |
820 | ** All command line parameters have the syntax "-fstring" | |
821 | ** or "-f string" (e.g. the space is optional). String may | |
822 | ** be empty. Flag characters cannot be concatenated (like | |
823 | ** "-fxyz"), it would conflict with the form "-fstring". | |
824 | */ | |
825 | while (--argc > 0 && (*++argv)[0] == '-') | |
826 | { | |
827 | char *p = argv[0]+1; | |
828 | int flag = *p++; | |
829 | ||
830 | if (flag == '\0' || *p == '\0') | |
831 | { | |
832 | if (argc > 1 && argv[1][0] != '-') | |
833 | { | |
834 | p = *++argv; | |
835 | argc -= 1; | |
836 | } | |
837 | else | |
838 | { | |
839 | p = ""; | |
840 | } | |
841 | } | |
842 | ||
843 | switch (flag) | |
844 | { | |
845 | case 'a': | |
846 | bootopt |= BOOT_AUTODIE; | |
847 | break; | |
848 | case 'b': | |
849 | bootopt |= BOOT_BADTUNE; | |
850 | break; | |
851 | case 'c': | |
852 | bootopt |= BOOT_CONSOLE; | |
853 | break; | |
854 | case 'q': | |
855 | bootopt |= BOOT_QUICK; | |
856 | break; | |
857 | #ifdef CMDLINE_CONFIG | |
858 | case 'f': | |
859 | (void)setuid((uid_t)uid); | |
860 | configfile = p; | |
861 | break; | |
862 | #endif | |
863 | case 'h': | |
864 | if (*p == '\0') | |
865 | bad_command(); | |
866 | strncpyzt(me.serv->namebuf, p, sizeof(me.serv->namebuf)); | |
867 | break; | |
868 | case 'i': | |
869 | bootopt |= BOOT_INETD|BOOT_AUTODIE; | |
870 | break; | |
871 | case 'p': | |
872 | if (!strcmp(p, "strict")) | |
873 | bootopt |= BOOT_PROT|BOOT_STRICTPROT; | |
874 | else if (!strcmp(p, "on")) | |
875 | bootopt |= BOOT_PROT; | |
876 | else if (!strcmp(p, "off")) | |
877 | bootopt &= ~(BOOT_PROT|BOOT_STRICTPROT); | |
878 | else if (!strcmp(p, "standalone")) | |
879 | bootopt |= BOOT_STANDALONE; | |
880 | else | |
881 | bad_command(); | |
882 | break; | |
883 | case 's': | |
884 | bootopt |= BOOT_NOIAUTH; | |
885 | break; | |
886 | case 't': | |
887 | #ifdef DEBUGMODE | |
888 | (void)setuid((uid_t)uid); | |
889 | #endif | |
890 | bootopt |= BOOT_TTY; | |
891 | break; | |
892 | case 'T': | |
893 | tunefile = p; | |
894 | break; | |
895 | case 'v': | |
896 | (void)printf("ircd %s %s\n\tzlib %s\n\tircd.conf delimiter %c\n\t%s #%s\n", | |
897 | version, serveropts, | |
898 | #ifndef ZIP_LINKS | |
899 | "not used", | |
900 | #else | |
901 | zlib_version, | |
902 | #endif | |
903 | IRCDCONF_DELIMITER, | |
904 | creation, generation); | |
905 | exit(0); | |
906 | case 'x': | |
907 | #ifdef DEBUGMODE | |
908 | (void)setuid((uid_t)uid); | |
909 | debuglevel = atoi(p); | |
910 | debugmode = *p ? p : "0"; | |
911 | bootopt |= BOOT_DEBUG; | |
912 | break; | |
913 | #else | |
914 | (void)fprintf(stderr, | |
915 | "%s: DEBUGMODE must be defined for -x y\n", | |
916 | myargv[0]); | |
917 | exit(0); | |
918 | #endif | |
919 | default: | |
920 | bad_command(); | |
921 | } | |
922 | } | |
923 | ||
924 | if (strlen(tunefile) > 1023 || strlen(mybasename(tunefile)) > 42) | |
925 | { | |
926 | fprintf(stderr, "Too long tune filename\n"); | |
927 | exit(-1); | |
928 | } | |
929 | if (argc > 0) | |
930 | bad_command(); /* This exits out */ | |
931 | ||
932 | #ifndef IRC_UID | |
933 | if ((uid != euid) && !euid) | |
934 | { | |
935 | (void)fprintf(stderr, | |
936 | "ERROR: do not run ircd setuid root. Make it setuid a\ | |
937 | normal user.\n"); | |
938 | exit(-1); | |
939 | } | |
940 | #endif | |
941 | ||
942 | #if !defined(CHROOTDIR) | |
943 | (void)setuid((uid_t)euid); | |
944 | # if defined(IRC_UID) && defined(IRC_GID) | |
945 | if ((int)getuid() == 0) | |
946 | { | |
947 | /* run as a specified user */ | |
948 | (void)fprintf(stderr,"WARNING: running ircd with uid = %d\n", | |
949 | IRC_UID); | |
950 | (void)fprintf(stderr," changing to gid %d.\n",IRC_GID); | |
951 | (void)setgid(IRC_GID); | |
952 | (void)setuid(IRC_UID); | |
953 | } | |
954 | # endif | |
955 | #endif /*CHROOTDIR/UID/GID*/ | |
956 | ||
957 | #if defined(USE_IAUTH) | |
958 | /* At this point, we just check whether iauth is there. Real start | |
959 | * is done in init_sys(). */ | |
960 | if ((bootopt & BOOT_NOIAUTH) == 0) | |
961 | switch (vfork()) | |
962 | { | |
963 | case -1: | |
964 | fprintf(stderr, "%s: Unable to fork!", myargv[0]); | |
965 | exit(-1); | |
966 | case 0: | |
967 | close(0); close(1); close(3); | |
968 | if (execl(IAUTH_PATH, IAUTH, "-X", NULL) < 0) | |
969 | _exit(-1); | |
970 | default: | |
971 | { | |
972 | int rc; | |
973 | ||
974 | (void)wait(&rc); | |
975 | if (rc != 0) | |
976 | { | |
977 | fprintf(stderr, | |
978 | "%s: error: unable to find \"%s\".\n", | |
979 | myargv[0], IAUTH_PATH); | |
980 | exit(-1); | |
981 | } | |
982 | } | |
983 | } | |
984 | #endif | |
985 | ||
986 | setup_signals(); | |
987 | ||
988 | /* didn't set debuglevel */ | |
989 | /* but asked for debugging output to tty */ | |
990 | #ifdef DEBUGMODE | |
991 | ||
992 | if ((debuglevel < 0) && (bootopt & BOOT_TTY)) | |
993 | { | |
994 | (void)fprintf(stderr, | |
995 | "you specified -t without -x. use -x <n>\n"); | |
996 | exit(-1); | |
997 | } | |
998 | ||
999 | #endif | |
1000 | timeofday = time(NULL); | |
1001 | initanonymous(); | |
1002 | initstats(); | |
1003 | initruntimeconf(); | |
1004 | ircd_readtune(tunefile); | |
1005 | motd = NULL; | |
1006 | read_motd(IRCDMOTD_PATH); | |
1007 | inithashtables(); | |
1008 | initlists(); | |
1009 | initclass(); | |
1010 | initwhowas(); | |
1011 | timeofday = time(NULL); | |
1012 | open_debugfile(); | |
1013 | timeofday = time(NULL); | |
1014 | (void)init_sys(); | |
1015 | ||
1016 | #ifdef USE_SYSLOG | |
1017 | openlog(mybasename(myargv[0]), LOG_PID|LOG_NDELAY, LOG_FACILITY); | |
1018 | #endif | |
1019 | timeofday = time(NULL); | |
1020 | if (initconf(bootopt) == -1) | |
1021 | { | |
1022 | Debug((DEBUG_FATAL, "Couldn't open configuration file %s", | |
1023 | configfile)); | |
1024 | (void)fprintf(stderr, | |
1025 | "Couldn't open configuration file %s (%s)\n", | |
1026 | configfile,strerror(errno)); | |
1027 | ||
1028 | exit(-1); | |
1029 | } | |
1030 | else | |
1031 | { | |
1032 | aClient *acptr = NULL; | |
1033 | int i; | |
1034 | ||
1035 | for (i = 0; i <= highest_fd; i++) | |
1036 | { | |
1037 | if (!(acptr = local[i])) | |
1038 | continue; | |
1039 | if (IsListener(acptr)) | |
1040 | break; | |
1041 | acptr = NULL; | |
1042 | } | |
1043 | /* exit if there is nothing to listen to */ | |
1044 | if (acptr == NULL && !(bootopt & BOOT_INETD)) | |
1045 | { | |
1046 | fprintf(stderr, | |
1047 | "Fatal Error: No working P-line in ircd.conf\n"); | |
1048 | exit(-1); | |
1049 | } | |
1050 | /* Is there an M-line? */ | |
1051 | if (!find_me()) | |
1052 | { | |
1053 | fprintf(stderr, | |
1054 | "Fatal Error: No M-line in ircd.conf.\n"); | |
1055 | exit(-1); | |
1056 | } | |
1057 | if ((i=check_servername(ME))) | |
1058 | { | |
1059 | fprintf(stderr, | |
1060 | "Fatal Error: %s.\n", check_servername_errors[i-1][1]); | |
1061 | exit(-1); | |
1062 | } | |
1063 | if (!me.serv->sid) | |
1064 | { | |
1065 | fprintf(stderr, | |
1066 | "Fatal Error: No SID specified in ircd.conf\n"); | |
1067 | exit(-1); | |
1068 | } | |
1069 | if (!sid_valid(me.serv->sid)) | |
1070 | { | |
1071 | fprintf(stderr, | |
1072 | "Fatal Error: Invalid sid %s specified in ircd.conf\n", | |
1073 | me.serv->sid); | |
1074 | exit(-1); | |
1075 | } | |
1076 | if (!networkname) | |
1077 | { | |
1078 | fprintf(stderr, | |
1079 | "Warning: Network name is not set in ircd.conf\n"); | |
1080 | } | |
1081 | isupport = make_isupport(); /* Generate RPL_ISUPPORT (005) numerics */ | |
1082 | } | |
1083 | ||
1084 | setup_me(&me); | |
1085 | check_class(); | |
1086 | ircd_writetune(tunefile); | |
1087 | if (bootopt & BOOT_INETD) | |
1088 | { | |
1089 | aClient *tmp; | |
1090 | aConfItem *aconf; | |
1091 | ||
1092 | tmp = make_client(NULL); | |
1093 | make_server(tmp); | |
1094 | register_server(tmp); | |
1095 | ||
1096 | tmp->fd = 0; | |
1097 | tmp->flags = FLAGS_LISTEN; | |
1098 | tmp->acpt = tmp; | |
1099 | tmp->from = tmp; | |
1100 | tmp->firsttime = time(NULL); | |
1101 | ||
1102 | SetMe(tmp); | |
1103 | ||
1104 | (void)strcpy(tmp->serv->namebuf, "*"); | |
1105 | ||
1106 | if (inetport(tmp, 0, "0.0.0.0", 0, 1)) | |
1107 | tmp->fd = -1; | |
1108 | if (tmp->fd == 0) | |
1109 | { | |
1110 | aconf = make_conf(); | |
1111 | aconf->status = CONF_LISTEN_PORT; | |
1112 | aconf->clients++; | |
1113 | aconf->next = conf; | |
1114 | conf = aconf; | |
1115 | ||
1116 | tmp->confs = make_link(); | |
1117 | tmp->confs->next = NULL; | |
1118 | tmp->confs->value.aconf = aconf; | |
1119 | add_fd(tmp->fd, &fdas); | |
1120 | add_fd(tmp->fd, &fdall); | |
1121 | set_non_blocking(tmp->fd, tmp); | |
1122 | } | |
1123 | else | |
1124 | exit(5); | |
1125 | } | |
1126 | ||
1127 | Debug((DEBUG_NOTICE,"Server ready...")); | |
1128 | #ifdef USE_SYSLOG | |
1129 | syslog(LOG_NOTICE, "Server Ready: v%s (%s #%s)", version, creation, | |
1130 | generation); | |
1131 | #endif | |
1132 | printf("Server %s (%s) version %s starting%s%s", ME, me.serv->sid, | |
1133 | version, (bootopt & BOOT_TTY) ? " in foreground mode." : ".", | |
1134 | #ifdef DEBUGMODE | |
1135 | "(DEBUGMODE)\n" | |
1136 | #else | |
1137 | "\n" | |
1138 | #endif | |
1139 | ); | |
1140 | ||
1141 | timeofday = time(NULL); | |
1142 | mysrand(timeofday); | |
1143 | ||
1144 | /* daemonize() closes 0,1,2 -- make sure you don't have any fd open */ | |
1145 | daemonize(); | |
1146 | logfiles_open(); | |
1147 | write_pidfile(); | |
1148 | dbuf_init(); | |
1149 | ||
1150 | serverbooting = 0; | |
1151 | ||
1152 | while (1) | |
1153 | io_loop(); | |
1154 | } | |
1155 | ||
1156 | ||
1157 | static void io_loop(void) | |
1158 | { | |
1159 | static time_t delay = 0; | |
1160 | int maxs = 4; | |
1161 | ||
1162 | if (timeofday >= nextpreference) | |
1163 | nextpreference = calculate_preference(timeofday); | |
1164 | /* | |
1165 | ** We only want to connect if a connection is due, | |
1166 | ** not every time through. Note, if there are no | |
1167 | ** active C lines, this call to Tryconnections is | |
1168 | ** made once only; it will return 0. - avalon | |
1169 | */ | |
1170 | if (nextconnect && timeofday >= nextconnect) | |
1171 | nextconnect = try_connections(timeofday); | |
1172 | #ifdef DELAY_CLOSE | |
1173 | /* close all overdue delayed fds */ | |
1174 | if (nextdelayclose && timeofday >= nextdelayclose) | |
1175 | nextdelayclose = delay_close(-1); | |
1176 | #endif | |
1177 | #ifdef TKLINE | |
1178 | /* expire tklines */ | |
1179 | if (nexttkexpire && timeofday >= nexttkexpire) | |
1180 | nexttkexpire = tkline_expire(0); | |
1181 | #endif | |
1182 | /* | |
1183 | ** Every once in a while, hunt channel structures that | |
1184 | ** can be freed. Reop channels while at it, too. | |
1185 | */ | |
1186 | if (timeofday >= nextgarbage) | |
1187 | nextgarbage = collect_channel_garbage(timeofday); | |
1188 | /* | |
1189 | ** DNS checks. One to timeout queries, one for cache expiries. | |
1190 | */ | |
1191 | if (timeofday >= nextdnscheck) | |
1192 | nextdnscheck = timeout_query_list(timeofday); | |
1193 | if (timeofday >= nextexpire) | |
1194 | nextexpire = expire_cache(timeofday); | |
1195 | /* | |
1196 | ** take the smaller of the two 'timed' event times as | |
1197 | ** the time of next event (stops us being late :) - avalon | |
1198 | ** WARNING - nextconnect can return 0! | |
1199 | */ | |
1200 | if (nextconnect) | |
1201 | delay = MIN(nextping, nextconnect); | |
1202 | else | |
1203 | delay = nextping; | |
1204 | #ifdef DELAY_CLOSE | |
1205 | if (nextdelayclose) | |
1206 | delay = MIN(nextdelayclose, delay); | |
1207 | #endif | |
1208 | delay = MIN(nextdnscheck, delay); | |
1209 | delay = MIN(nextexpire, delay); | |
1210 | delay = MIN(nextpreference, delay); | |
1211 | delay -= timeofday; | |
1212 | /* | |
1213 | ** Adjust delay to something reasonable [ad hoc values] | |
1214 | ** (one might think something more clever here... --msa) | |
1215 | ** We don't really need to check that often and as long | |
1216 | ** as we don't delay too long, everything should be ok. | |
1217 | ** waiting too long can cause things to timeout... | |
1218 | ** i.e. PINGS -> a disconnection :( | |
1219 | ** - avalon | |
1220 | */ | |
1221 | if (delay < 1) | |
1222 | delay = 1; | |
1223 | else | |
1224 | delay = MIN(delay, TIMESEC); | |
1225 | ||
1226 | /* | |
1227 | ** First, try to drain traffic from servers and listening sockets. | |
1228 | ** Give up either if there's no traffic or too many iterations. | |
1229 | */ | |
1230 | while (maxs--) | |
1231 | if (read_message(0, &fdas, 0)) | |
1232 | flush_fdary(&fdas); | |
1233 | else | |
1234 | break; | |
1235 | ||
1236 | Debug((DEBUG_DEBUG, "delay for %d", delay)); | |
1237 | /* | |
1238 | ** Second, deal with _all_ clients but only try to empty sendQ's for | |
1239 | ** servers. Other clients are dealt with below.. | |
1240 | */ | |
1241 | if (read_message(1, &fdall, 1) == 0 && delay > 1) | |
1242 | { | |
1243 | /* | |
1244 | ** Timed out (e.g. *NO* traffic at all). | |
1245 | ** Try again but also check to empty sendQ's for all clients. | |
1246 | */ | |
1247 | (void)read_message(delay - 1, &fdall, 0); | |
1248 | } | |
1249 | timeofday = time(NULL); | |
1250 | ||
1251 | Debug((DEBUG_DEBUG ,"Got message(s)")); | |
1252 | /* | |
1253 | ** ...perhaps should not do these loops every time, | |
1254 | ** but only if there is some chance of something | |
1255 | ** happening (but, note that conf->hold times may | |
1256 | ** be changed elsewhere--so precomputed next event | |
1257 | ** time might be too far away... (similarly with | |
1258 | ** ping times) --msa | |
1259 | */ | |
1260 | if (timeofday >= nextping) | |
1261 | { | |
1262 | nextping = check_pings(timeofday); | |
1263 | if (rehashed > 0) | |
1264 | { | |
1265 | rehashed = delayed_kills(timeofday); | |
1266 | } | |
1267 | } | |
1268 | ||
1269 | if (dorestart) | |
1270 | restart("Caught SIGINT"); | |
1271 | if (dorehash > 0) | |
1272 | { /* Only on signal, not on oper /rehash */ | |
1273 | ircd_writetune(tunefile); | |
1274 | (void)rehash(&me, &me, 1); | |
1275 | dorehash--; | |
1276 | } | |
1277 | if (restart_iauth || timeofday >= nextiarestart) | |
1278 | { | |
1279 | start_iauth(restart_iauth); | |
1280 | restart_iauth = 0; | |
1281 | nextiarestart = timeofday + 15; | |
1282 | } | |
1283 | /* | |
1284 | ** Flush output buffers on all connections now if they | |
1285 | ** have data in them (or at least try to flush) | |
1286 | ** -avalon | |
1287 | */ | |
1288 | flush_connections(me.fd); | |
1289 | ||
1290 | #ifdef DEBUGMODE | |
1291 | checklists(); | |
1292 | #endif | |
1293 | ||
1294 | } | |
1295 | ||
1296 | /* | |
1297 | * open_debugfile | |
1298 | * | |
1299 | * If the -t option is not given on the command line when the server is | |
1300 | * started, all debugging output is sent to the file set by IRCDDBG_PATH. | |
1301 | * Here we just open that file and make sure it is opened to fd 2 so that | |
1302 | * any fprintf's to stderr also goto the logfile. If the debuglevel is not | |
1303 | * set from the command line by -x, use /dev/null as the dummy logfile as long | |
1304 | * as DEBUGMODE has been defined, else don't waste the fd. | |
1305 | */ | |
1306 | static void open_debugfile(void) | |
1307 | { | |
1308 | #ifdef DEBUGMODE | |
1309 | int fd; | |
1310 | ||
1311 | if (debuglevel >= 0) | |
1312 | { | |
1313 | (void)printf("isatty = %d ttyname = %#x\n", | |
1314 | isatty(2), (u_int)ttyname(2)); | |
1315 | if (!(bootopt & BOOT_TTY)) /* leave debugging output on fd 2 */ | |
1316 | { | |
1317 | (void)truncate(IRCDDBG_PATH, 0); | |
1318 | if ((fd = open(IRCDDBG_PATH,O_WRONLY|O_CREAT,0600))<0) | |
1319 | if ((fd = open("/dev/null", O_WRONLY)) < 0) | |
1320 | exit(-1); | |
1321 | if (fd != 2) | |
1322 | { | |
1323 | (void)dup2(fd, 2); | |
1324 | (void)close(fd); | |
1325 | } | |
1326 | } | |
1327 | Debug((DEBUG_FATAL, "Debug: File <%s> Level: %d at %s", | |
1328 | ( (!(bootopt & BOOT_TTY)) ? IRCDDBG_PATH : | |
1329 | (isatty(2) && ttyname(2)) ? ttyname(2) : "FD2-Pipe"), | |
1330 | debuglevel, myctime(time(NULL)))); | |
1331 | } | |
1332 | #endif | |
1333 | return; | |
1334 | } | |
1335 | ||
1336 | static void setup_signals(void) | |
1337 | { | |
1338 | #if POSIX_SIGNALS | |
1339 | struct sigaction act; | |
1340 | ||
1341 | act.sa_handler = SIG_IGN; | |
1342 | act.sa_flags = 0; | |
1343 | (void)sigemptyset(&act.sa_mask); | |
1344 | (void)sigaddset(&act.sa_mask, SIGPIPE); | |
1345 | (void)sigaddset(&act.sa_mask, SIGALRM); | |
1346 | # ifdef SIGWINCH | |
1347 | (void)sigaddset(&act.sa_mask, SIGWINCH); | |
1348 | (void)sigaction(SIGWINCH, &act, NULL); | |
1349 | # endif | |
1350 | (void)sigaction(SIGPIPE, &act, NULL); | |
1351 | act.sa_handler = dummy; | |
1352 | (void)sigaction(SIGALRM, &act, NULL); | |
1353 | act.sa_handler = s_rehash; | |
1354 | (void)sigemptyset(&act.sa_mask); | |
1355 | (void)sigaddset(&act.sa_mask, SIGHUP); | |
1356 | (void)sigaction(SIGHUP, &act, NULL); | |
1357 | act.sa_handler = s_restart; | |
1358 | (void)sigaddset(&act.sa_mask, SIGINT); | |
1359 | (void)sigaction(SIGINT, &act, NULL); | |
1360 | act.sa_handler = s_die; | |
1361 | (void)sigaddset(&act.sa_mask, SIGTERM); | |
1362 | (void)sigaction(SIGTERM, &act, NULL); | |
1363 | # if defined(USE_IAUTH) | |
1364 | act.sa_handler = s_slave; | |
1365 | # else | |
1366 | act.sa_handler = SIG_IGN; | |
1367 | # endif | |
1368 | (void)sigaddset(&act.sa_mask, SIGUSR1); | |
1369 | (void)sigaction(SIGUSR1, &act, NULL); | |
1370 | # if defined(USE_IAUTH) | |
1371 | act.sa_handler = SIG_IGN; | |
1372 | # ifdef SA_NOCLDWAIT | |
1373 | act.sa_flags = SA_NOCLDWAIT; | |
1374 | # else | |
1375 | act.sa_flags = 0; | |
1376 | # endif | |
1377 | (void)sigaddset(&act.sa_mask, SIGCHLD); | |
1378 | (void)sigaction(SIGCHLD, &act, NULL); | |
1379 | # endif | |
1380 | ||
1381 | # if defined(__FreeBSD__) | |
1382 | /* Don't core after detaching from gdb on fbsd */ | |
1383 | ||
1384 | act.sa_handler = SIG_IGN; | |
1385 | act.sa_flags = 0; | |
1386 | (void)sigaddset(&act.sa_mask, SIGTRAP); | |
1387 | (void)sigaction(SIGTRAP,&act,NULL); | |
1388 | # endif /* __FreeBSD__ */ | |
1389 | ||
1390 | #else /* POSIX_SIGNALS */ | |
1391 | ||
1392 | # ifndef HAVE_RELIABLE_SIGNALS | |
1393 | (void)signal(SIGPIPE, dummy); | |
1394 | # ifdef SIGWINCH | |
1395 | (void)signal(SIGWINCH, dummy); | |
1396 | # endif | |
1397 | # else /* HAVE_RELIABLE_SIGNALS */ | |
1398 | # ifdef SIGWINCH | |
1399 | (void)signal(SIGWINCH, SIG_IGN); | |
1400 | # endif | |
1401 | (void)signal(SIGPIPE, SIG_IGN); | |
1402 | ||
1403 | # endif /* HAVE_RELIABLE_SIGNALS */ | |
1404 | (void)signal(SIGALRM, dummy); | |
1405 | (void)signal(SIGHUP, s_rehash); | |
1406 | (void)signal(SIGTERM, s_die); | |
1407 | (void)signal(SIGINT, s_restart); | |
1408 | # if defined(USE_IAUTH) | |
1409 | (void)signal(SIGUSR1, s_slave); | |
1410 | (void)signal(SIGCHLD, SIG_IGN); | |
1411 | # else | |
1412 | (void)signal(SIGUSR1, SIG_IGN); | |
1413 | # endif | |
1414 | ||
1415 | # if defined(__FreeBSD__) | |
1416 | /* don't core after detaching from gdb on fbsd */ | |
1417 | (void)signal(SIGTRAP, SIG_IGN); | |
1418 | # endif /* __FreeBSD__ */ | |
1419 | ||
1420 | #endif /* POSIX_SIGNAL */ | |
1421 | ||
1422 | #ifdef RESTARTING_SYSTEMCALLS | |
1423 | /* | |
1424 | ** At least on Apollo sr10.1 it seems continuing system calls | |
1425 | ** after signal is the default. The following 'siginterrupt' | |
1426 | ** should change that default to interrupting calls. | |
1427 | */ | |
1428 | (void)siginterrupt(SIGALRM, 1); | |
1429 | #endif | |
1430 | } | |
1431 | ||
1432 | /* | |
1433 | * Called from bigger_hash_table(), s_die(), server_reboot(), | |
1434 | * main(after initializations), grow_history(), rehash(io_loop) signal. | |
1435 | */ | |
1436 | void ircd_writetune(char *filename) | |
1437 | { | |
1438 | int fd; | |
1439 | char buf[100]; | |
1440 | ||
1441 | if (!filename || !*filename) | |
1442 | return; | |
1443 | ||
1444 | (void)truncate(filename, 0); | |
1445 | if ((fd = open(filename, O_CREAT|O_WRONLY, 0600)) >= 0) | |
1446 | { | |
1447 | (void)sprintf(buf, "%d\n%d\n%d\n%d\n%d\n%d\n%d\n", ww_size, | |
1448 | lk_size, _HASHSIZE, _CHANNELHASHSIZE, | |
1449 | _SIDSIZE, poolsize, _UIDSIZE); | |
1450 | if (write(fd, buf, strlen(buf)) == -1) | |
1451 | sendto_flag(SCH_ERROR, | |
1452 | "Failed (%d) to write tune file: %s.", | |
1453 | errno, mybasename(filename)); | |
1454 | else | |
1455 | sendto_flag(SCH_NOTICE, "Updated %s.", | |
1456 | mybasename(filename)); | |
1457 | close(fd); | |
1458 | } | |
1459 | else | |
1460 | sendto_flag(SCH_ERROR, "Failed (%d) to open tune file: %s.", | |
1461 | errno, mybasename(filename)); | |
1462 | } | |
1463 | ||
1464 | /* | |
1465 | * Called only from main() at startup. | |
1466 | */ | |
1467 | void ircd_readtune(char *filename) | |
1468 | { | |
1469 | int fd, t_data[7]; | |
1470 | char buf[100]; | |
1471 | ||
1472 | memset(buf, 0, sizeof(buf)); | |
1473 | if ((fd = open(filename, O_RDONLY)) != -1) | |
1474 | { | |
1475 | read(fd, buf, 100); /* no panic if this fails.. */ | |
1476 | if (sscanf(buf, "%d\n%d\n%d\n%d\n%d\n%d\n%d\n", &t_data[0], | |
1477 | &t_data[1], &t_data[2], &t_data[3], | |
1478 | &t_data[4], &t_data[5], &t_data[6]) != 7) | |
1479 | { | |
1480 | close(fd); | |
1481 | if (bootopt & BOOT_BADTUNE) | |
1482 | return; | |
1483 | else | |
1484 | { | |
1485 | fprintf(stderr, | |
1486 | "ircd tune file %s: bad format\n", | |
1487 | filename); | |
1488 | exit(1); | |
1489 | } | |
1490 | } | |
1491 | ||
1492 | /* | |
1493 | ** Initiate the tune-values after successfully | |
1494 | ** reading the tune-file. | |
1495 | */ | |
1496 | ww_size = t_data[0]; | |
1497 | lk_size = t_data[1]; | |
1498 | _HASHSIZE = t_data[2]; | |
1499 | #ifdef USE_HOSTHASH | |
1500 | _HOSTNAMEHASHSIZE = t_data[2]; /* hostname has always same size | |
1501 | as the client hash */ | |
1502 | #endif | |
1503 | #ifdef USE_IPHASH | |
1504 | _IPHASHSIZE = t_data[2]; | |
1505 | #endif | |
1506 | _CHANNELHASHSIZE = t_data[3]; | |
1507 | _SIDSIZE = t_data[4]; | |
1508 | poolsize = t_data[5]; | |
1509 | _UIDSIZE = t_data[6]; | |
1510 | ||
1511 | /* | |
1512 | ** the lock array only grows if the whowas array grows, | |
1513 | ** I don't think it should be initialized with a lower | |
1514 | ** size since it will never adjust unless whowas array does. | |
1515 | */ | |
1516 | if (lk_size < ww_size) | |
1517 | lk_size = ww_size; | |
1518 | close(fd); | |
1519 | } | |
1520 | } | |
1521 |