1 |
/* |
2 |
* main.c -- handles: |
3 |
* core event handling |
4 |
* signal handling |
5 |
* command line arguments |
6 |
* context and assert debugging |
7 |
* |
8 |
* $Id: main.c,v 1.86 2001/10/15 09:27:08 stdarg Exp $ |
9 |
*/ |
10 |
/* |
11 |
* Copyright (C) 1997 Robey Pointer |
12 |
* Copyright (C) 1999, 2000, 2001 Eggheads Development Team |
13 |
* |
14 |
* This program is free software; you can redistribute it and/or |
15 |
* modify it under the terms of the GNU General Public License |
16 |
* as published by the Free Software Foundation; either version 2 |
17 |
* of the License, or (at your option) any later version. |
18 |
* |
19 |
* This program is distributed in the hope that it will be useful, |
20 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
21 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
22 |
* GNU General Public License for more details. |
23 |
* |
24 |
* You should have received a copy of the GNU General Public License |
25 |
* along with this program; if not, write to the Free Software |
26 |
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
27 |
*/ |
28 |
/* |
29 |
* The author (Robey Pointer) can be reached at: robey@netcom.com |
30 |
* NOTE: Robey is no long working on this code, there is a discussion |
31 |
* list available at eggheads@eggheads.org. |
32 |
*/ |
33 |
|
34 |
#include "main.h" |
35 |
#include <fcntl.h> |
36 |
#include <time.h> |
37 |
#include <errno.h> |
38 |
#include <signal.h> |
39 |
#include <netdb.h> |
40 |
#include <setjmp.h> |
41 |
|
42 |
#include <locale.h> |
43 |
|
44 |
#ifdef STOP_UAC /* osf/1 complains a lot */ |
45 |
#include <sys/sysinfo.h> |
46 |
#define UAC_NOPRINT 0x00000001 /* Don't report unaligned fixups */ |
47 |
#endif |
48 |
/* Some systems have a working sys/wait.h even though configure will |
49 |
* decide it's not bsd compatable. Oh well. |
50 |
*/ |
51 |
|
52 |
#include "chan.h" |
53 |
#include "modules.h" |
54 |
#include "tandem.h" |
55 |
#include "bg.h" |
56 |
#include "egg_timer.h" |
57 |
#include "core_binds.h" |
58 |
|
59 |
#include "adns/adns.h" |
60 |
|
61 |
#ifdef CYGWIN_HACKS |
62 |
#include <windows.h> |
63 |
#endif |
64 |
|
65 |
#ifndef _POSIX_SOURCE |
66 |
/* Solaris needs this */ |
67 |
#define _POSIX_SOURCE 1 |
68 |
#endif |
69 |
|
70 |
extern char origbotname[], userfile[], botnetnick[]; |
71 |
extern int dcc_total, conmask, cache_hit, cache_miss, |
72 |
max_logs, quick_logs; |
73 |
extern struct dcc_t *dcc; |
74 |
extern struct userrec *userlist; |
75 |
extern struct chanset_t *chanset; |
76 |
extern log_t *logs; |
77 |
extern Tcl_Interp *interp; |
78 |
extern tcl_timer_t *timer, |
79 |
*utimer; |
80 |
extern jmp_buf alarmret; |
81 |
|
82 |
|
83 |
/* |
84 |
* Please use the PATCH macro instead of directly altering the version |
85 |
* string from now on (it makes it much easier to maintain patches). |
86 |
* Also please read the README file regarding your rights to distribute |
87 |
* modified versions of this bot. |
88 |
*/ |
89 |
|
90 |
char egg_version[1024] = VERSION; |
91 |
int egg_numver = VERSION_NUM; |
92 |
|
93 |
char notify_new[121] = ""; /* Person to send a note to for new users */ |
94 |
int default_flags = 0; /* Default user flags and */ |
95 |
int default_uflags = 0; /* Default userdefinied flags for people |
96 |
who say 'hello' or for .adduser */ |
97 |
|
98 |
int backgrd = 1; /* Run in the background? */ |
99 |
int con_chan = 0; /* Foreground: constantly display channel |
100 |
stats? */ |
101 |
int term_z = 0; /* Foreground: use the terminal as a party |
102 |
line? */ |
103 |
char configfile[121] = "eggdrop.conf"; /* Name of the config file */ |
104 |
char helpdir[121]; /* Directory of help files (if used) */ |
105 |
char textdir[121] = ""; /* Directory for text files that get dumped */ |
106 |
int keep_all_logs = 0; /* Never erase logfiles, no matter how old |
107 |
they are? */ |
108 |
char logfile_suffix[21] = ".%d%b%Y"; /* Format of logfile suffix. */ |
109 |
time_t online_since; /* Unix-time that the bot loaded up */ |
110 |
int make_userfile = 0; /* Using bot in make-userfile mode? (first |
111 |
user to 'hello' becomes master) */ |
112 |
char owner[121] = ""; /* Permanent owner(s) of the bot */ |
113 |
char pid_file[HANDLEN + 5]; /* Name of the file for the pid to be |
114 |
stored in */ |
115 |
int save_users_at = 0; /* How many minutes past the hour to |
116 |
save the userfile? */ |
117 |
int notify_users_at = 0; /* How many minutes past the hour to |
118 |
notify users of notes? */ |
119 |
int switch_logfiles_at = 300; /* When (military time) to switch logfiles */ |
120 |
char version[81]; /* Version info (long form) */ |
121 |
char ver[41]; /* Version info (short form) */ |
122 |
char egg_xtra[2048]; /* Patch info */ |
123 |
int use_stderr = 1; /* Send stuff to stderr instead of logfiles? */ |
124 |
int do_restart = 0; /* .restart has been called, restart asap */ |
125 |
int die_on_sighup = 0; /* die if bot receives SIGHUP */ |
126 |
int die_on_sigterm = 1; /* die if bot receives SIGTERM */ |
127 |
int resolve_timeout = 15; /* hostname/address lookup timeout */ |
128 |
char quit_msg[1024]; /* quit message */ |
129 |
time_t now; /* duh, now :) */ |
130 |
|
131 |
/* Traffic stats |
132 |
*/ |
133 |
unsigned long otraffic_irc = 0; |
134 |
unsigned long otraffic_irc_today = 0; |
135 |
unsigned long otraffic_bn = 0; |
136 |
unsigned long otraffic_bn_today = 0; |
137 |
unsigned long otraffic_dcc = 0; |
138 |
unsigned long otraffic_dcc_today = 0; |
139 |
unsigned long otraffic_filesys = 0; |
140 |
unsigned long otraffic_filesys_today = 0; |
141 |
unsigned long otraffic_trans = 0; |
142 |
unsigned long otraffic_trans_today = 0; |
143 |
unsigned long otraffic_unknown = 0; |
144 |
unsigned long otraffic_unknown_today = 0; |
145 |
unsigned long itraffic_irc = 0; |
146 |
unsigned long itraffic_irc_today = 0; |
147 |
unsigned long itraffic_bn = 0; |
148 |
unsigned long itraffic_bn_today = 0; |
149 |
unsigned long itraffic_dcc = 0; |
150 |
unsigned long itraffic_dcc_today = 0; |
151 |
unsigned long itraffic_trans = 0; |
152 |
unsigned long itraffic_trans_today = 0; |
153 |
unsigned long itraffic_unknown = 0; |
154 |
unsigned long itraffic_unknown_today = 0; |
155 |
|
156 |
#ifdef DEBUG_CONTEXT |
157 |
/* Context storage for fatal crashes */ |
158 |
char cx_file[16][30]; |
159 |
char cx_note[16][256]; |
160 |
int cx_line[16]; |
161 |
int cx_ptr = 0; |
162 |
#endif |
163 |
|
164 |
|
165 |
void fatal(const char *s, int recoverable) |
166 |
{ |
167 |
int i; |
168 |
|
169 |
putlog(LOG_MISC, "*", "* %s", s); |
170 |
flushlogs(); |
171 |
for (i = 0; i < dcc_total; i++) |
172 |
if (dcc[i].sock >= 0) |
173 |
killsock(dcc[i].sock); |
174 |
unlink(pid_file); |
175 |
if (!recoverable) { |
176 |
bg_send_quit(BG_ABORT); |
177 |
exit(1); |
178 |
} |
179 |
} |
180 |
|
181 |
static void check_expired_dcc() |
182 |
{ |
183 |
int i; |
184 |
|
185 |
for (i = 0; i < dcc_total; i++) |
186 |
if (dcc[i].type && dcc[i].type->timeout_val && |
187 |
((now - dcc[i].timeval) > *(dcc[i].type->timeout_val))) { |
188 |
if (dcc[i].type->timeout) |
189 |
dcc[i].type->timeout(i); |
190 |
else if (dcc[i].type->eof) |
191 |
dcc[i].type->eof(i); |
192 |
else |
193 |
continue; |
194 |
/* Only timeout 1 socket per cycle, too risky for more */ |
195 |
return; |
196 |
} |
197 |
} |
198 |
|
199 |
#ifdef DEBUG_CONTEXT |
200 |
static int nested_debug = 0; |
201 |
|
202 |
void write_debug() |
203 |
{ |
204 |
int x; |
205 |
char s[25]; |
206 |
int y; |
207 |
|
208 |
if (nested_debug) { |
209 |
/* Yoicks, if we have this there's serious trouble! |
210 |
* All of these are pretty reliable, so we'll try these. |
211 |
* |
212 |
* NOTE: dont try and display context-notes in here, it's |
213 |
* _not_ safe <cybah> |
214 |
*/ |
215 |
x = creat("DEBUG.DEBUG", 0644); |
216 |
setsock(x, SOCK_NONSOCK); |
217 |
if (x >= 0) { |
218 |
strncpyz(s, ctime(&now), sizeof s); |
219 |
dprintf(-x, "Debug (%s) written %s\n", ver, s); |
220 |
dprintf(-x, "Please report problem to bugs@eggheads.org\n"); |
221 |
dprintf(-x, "after a visit to http://www.eggheads.org/bugzilla/\n"); |
222 |
dprintf(-x, "Full Patch List: %s\n", egg_xtra); |
223 |
dprintf(-x, "Context: "); |
224 |
cx_ptr = cx_ptr & 15; |
225 |
for (y = ((cx_ptr + 1) & 15); y != cx_ptr; y = ((y + 1) & 15)) |
226 |
dprintf(-x, "%s/%d,\n ", cx_file[y], cx_line[y]); |
227 |
dprintf(-x, "%s/%d\n\n", cx_file[y], cx_line[y]); |
228 |
killsock(x); |
229 |
close(x); |
230 |
} |
231 |
bg_send_quit(BG_ABORT); |
232 |
exit(1); /* Dont even try & tell people about, that may |
233 |
have caused the fault last time. */ |
234 |
} else |
235 |
nested_debug = 1; |
236 |
putlog(LOG_MISC, "*", "* Last context: %s/%d [%s]", cx_file[cx_ptr], |
237 |
cx_line[cx_ptr], cx_note[cx_ptr][0] ? cx_note[cx_ptr] : ""); |
238 |
putlog(LOG_MISC, "*", "* Please REPORT this BUG!"); |
239 |
putlog(LOG_MISC, "*", "* Check doc/BUG-REPORT on how to do so."); |
240 |
x = creat("DEBUG", 0644); |
241 |
setsock(x, SOCK_NONSOCK); |
242 |
if (x < 0) { |
243 |
putlog(LOG_MISC, "*", "* Failed to write DEBUG"); |
244 |
} else { |
245 |
strncpyz(s, ctime(&now), sizeof s); |
246 |
dprintf(-x, "Debug (%s) written %s\n", ver, s); |
247 |
dprintf(-x, "Full Patch List: %s\n", egg_xtra); |
248 |
#ifdef STATIC |
249 |
dprintf(-x, "STATICALLY LINKED\n"); |
250 |
#endif |
251 |
|
252 |
/* info library */ |
253 |
dprintf(-x, "TCL library: %s\n", |
254 |
((interp) && (Tcl_Eval(interp, "info library") == TCL_OK)) ? |
255 |
interp->result : "*unknown*"); |
256 |
|
257 |
/* info tclversion/patchlevel */ |
258 |
dprintf(-x, "TCL version: %s (header version %s)\n", |
259 |
((interp) && (Tcl_Eval(interp, "info patchlevel") == TCL_OK)) ? |
260 |
interp->result : (Tcl_Eval(interp, "info tclversion") == TCL_OK) ? |
261 |
interp->result : "*unknown*", TCL_PATCH_LEVEL ? TCL_PATCH_LEVEL : |
262 |
"*unknown*"); |
263 |
|
264 |
#if HAVE_TCL_THREADS |
265 |
dprintf(-x, "TCL is threaded\n"); |
266 |
#endif |
267 |
|
268 |
#ifdef CCFLAGS |
269 |
dprintf(-x, "Compile flags: %s\n", CCFLAGS); |
270 |
#endif |
271 |
#ifdef LDFLAGS |
272 |
dprintf(-x, "Link flags: %s\n", LDFLAGS); |
273 |
#endif |
274 |
#ifdef STRIPFLAGS |
275 |
dprintf(-x, "Strip flags: %s\n", STRIPFLAGS); |
276 |
#endif |
277 |
|
278 |
dprintf(-x, "Context: "); |
279 |
cx_ptr = cx_ptr & 15; |
280 |
for (y = ((cx_ptr + 1) & 15); y != cx_ptr; y = ((y + 1) & 15)) |
281 |
dprintf(-x, "%s/%d, [%s]\n ", cx_file[y], cx_line[y], |
282 |
(cx_note[y][0]) ? cx_note[y] : ""); |
283 |
dprintf(-x, "%s/%d [%s]\n\n", cx_file[cx_ptr], cx_line[cx_ptr], |
284 |
(cx_note[cx_ptr][0]) ? cx_note[cx_ptr] : ""); |
285 |
tell_dcc(-x); |
286 |
dprintf(-x, "\n"); |
287 |
tell_netdebug(-x); |
288 |
killsock(x); |
289 |
close(x); |
290 |
putlog(LOG_MISC, "*", "* Wrote DEBUG"); |
291 |
} |
292 |
} |
293 |
#endif |
294 |
|
295 |
static void got_bus(int z) |
296 |
{ |
297 |
#ifdef DEBUG_CONTEXT |
298 |
write_debug(); |
299 |
#endif |
300 |
fatal("BUS ERROR -- CRASHING!", 1); |
301 |
#ifdef SA_RESETHAND |
302 |
kill(getpid(), SIGBUS); |
303 |
#else |
304 |
bg_send_quit(BG_ABORT); |
305 |
exit(1); |
306 |
#endif |
307 |
} |
308 |
|
309 |
static void got_segv(int z) |
310 |
{ |
311 |
#ifdef DEBUG_CONTEXT |
312 |
write_debug(); |
313 |
#endif |
314 |
fatal("SEGMENT VIOLATION -- CRASHING!", 1); |
315 |
#ifdef SA_RESETHAND |
316 |
kill(getpid(), SIGSEGV); |
317 |
#else |
318 |
bg_send_quit(BG_ABORT); |
319 |
exit(1); |
320 |
#endif |
321 |
} |
322 |
|
323 |
static void got_fpe(int z) |
324 |
{ |
325 |
#ifdef DEBUG_CONTEXT |
326 |
write_debug(); |
327 |
#endif |
328 |
fatal("FLOATING POINT ERROR -- CRASHING!", 0); |
329 |
} |
330 |
|
331 |
static void got_term(int z) |
332 |
{ |
333 |
write_userfile(-1); |
334 |
check_bind_event("sigterm"); |
335 |
if (die_on_sigterm) { |
336 |
fatal("TERMINATE SIGNAL -- SIGNING OFF", 0); |
337 |
} else { |
338 |
putlog(LOG_MISC, "*", "RECEIVED TERMINATE SIGNAL (IGNORING)"); |
339 |
} |
340 |
} |
341 |
|
342 |
static void got_quit(int z) |
343 |
{ |
344 |
check_bind_event("sigquit"); |
345 |
putlog(LOG_MISC, "*", "RECEIVED QUIT SIGNAL (IGNORING)"); |
346 |
return; |
347 |
} |
348 |
|
349 |
static void got_hup(int z) |
350 |
{ |
351 |
write_userfile(-1); |
352 |
check_bind_event("sighup"); |
353 |
if (die_on_sighup) { |
354 |
fatal("HANGUP SIGNAL -- SIGNING OFF", 0); |
355 |
} else |
356 |
putlog(LOG_MISC, "*", "Received HUP signal: rehashing..."); |
357 |
do_restart = -2; |
358 |
return; |
359 |
} |
360 |
|
361 |
/* A call to resolver (gethostbyname, etc) timed out |
362 |
*/ |
363 |
static void got_alarm(int z) |
364 |
{ |
365 |
longjmp(alarmret, 1); |
366 |
|
367 |
/* -Never reached- */ |
368 |
} |
369 |
|
370 |
/* Got ILL signal -- log context and continue |
371 |
*/ |
372 |
static void got_ill(int z) |
373 |
{ |
374 |
check_bind_event("sigill"); |
375 |
#ifdef DEBUG_CONTEXT |
376 |
putlog(LOG_MISC, "*", "* Context: %s/%d [%s]", cx_file[cx_ptr], |
377 |
cx_line[cx_ptr], (cx_note[cx_ptr][0]) ? cx_note[cx_ptr] : ""); |
378 |
#endif |
379 |
} |
380 |
|
381 |
#ifdef DEBUG_CONTEXT |
382 |
/* Context */ |
383 |
void eggContext(const char *file, int line, const char *module) |
384 |
{ |
385 |
char x[31], *p; |
386 |
|
387 |
p = strrchr(file, '/'); |
388 |
if (!module) { |
389 |
strncpyz(x, p ? p + 1 : file, sizeof x); |
390 |
} else |
391 |
egg_snprintf(x, 31, "%s:%s", module, p ? p + 1 : file); |
392 |
cx_ptr = ((cx_ptr + 1) & 15); |
393 |
strcpy(cx_file[cx_ptr], x); |
394 |
cx_line[cx_ptr] = line; |
395 |
cx_note[cx_ptr][0] = 0; |
396 |
} |
397 |
|
398 |
/* Called from the ContextNote macro. |
399 |
*/ |
400 |
void eggContextNote(const char *file, int line, const char *module, |
401 |
const char *note) |
402 |
{ |
403 |
char x[31], *p; |
404 |
|
405 |
p = strrchr(file, '/'); |
406 |
if (!module) { |
407 |
strncpyz(x, p ? p + 1 : file, sizeof x); |
408 |
} else |
409 |
egg_snprintf(x, 31, "%s:%s", module, p ? p + 1 : file); |
410 |
cx_ptr = ((cx_ptr + 1) & 15); |
411 |
strcpy(cx_file[cx_ptr], x); |
412 |
cx_line[cx_ptr] = line; |
413 |
strncpyz(cx_note[cx_ptr], note, sizeof cx_note[cx_ptr]); |
414 |
} |
415 |
#endif |
416 |
|
417 |
static void do_arg(char *s) |
418 |
{ |
419 |
char x[1024], *z = x; |
420 |
int i; |
421 |
|
422 |
if (s[0] == '-') |
423 |
for (i = 1; i < strlen(s); i++) { |
424 |
switch (s[i]) { |
425 |
case 'n': |
426 |
backgrd = 0; |
427 |
break; |
428 |
case 'c': |
429 |
con_chan = 1; |
430 |
term_z = 0; |
431 |
break; |
432 |
case 't': |
433 |
con_chan = 0; |
434 |
term_z = 1; |
435 |
break; |
436 |
case 'm': |
437 |
make_userfile = 1; |
438 |
break; |
439 |
case 'v': |
440 |
strncpyz(x, egg_version, sizeof x); |
441 |
newsplit(&z); |
442 |
newsplit(&z); |
443 |
printf("%s\n", version); |
444 |
if (z[0]) |
445 |
printf(" (patches: %s)\n", z); |
446 |
bg_send_quit(BG_ABORT); |
447 |
exit(0); |
448 |
break; /* this should never be reached */ |
449 |
case 'h': |
450 |
printf("\n%s\n\n", version); |
451 |
printf(_("Command line arguments:\n\ |
452 |
-h help\n\ |
453 |
-v print version and exit\n\ |
454 |
-n don't go into the background\n\ |
455 |
-c (with -n) display channel stats every 10 seconds\n\ |
456 |
-t (with -n) use terminal to simulate dcc-chat\n\ |
457 |
-m userfile creation mode\n\ |
458 |
optional config filename (default 'eggdrop.conf')\n")); |
459 |
printf("\n"); |
460 |
bg_send_quit(BG_ABORT); |
461 |
exit(0); |
462 |
break; /* this should never be reached */ |
463 |
} |
464 |
} else |
465 |
strncpyz(configfile, s, sizeof configfile); |
466 |
} |
467 |
|
468 |
void backup_userfile(void) |
469 |
{ |
470 |
char s[125]; |
471 |
|
472 |
putlog(LOG_MISC, "*", _("Backing up user file...")); |
473 |
egg_snprintf(s, sizeof s, "%s~bak", userfile); |
474 |
copyfile(userfile, s); |
475 |
} |
476 |
|
477 |
/* Timer info */ |
478 |
static int lastmin = 99; |
479 |
static time_t then; |
480 |
static struct tm nowtm; |
481 |
|
482 |
/* Called once a second. |
483 |
* |
484 |
* Note: Try to not put any Context lines in here (guppy 21Mar2000). |
485 |
*/ |
486 |
static void core_secondly() |
487 |
{ |
488 |
static int cnt = 0; |
489 |
int miltime; |
490 |
|
491 |
call_hook(HOOK_SECONDLY); /* Will be removed later */ |
492 |
do_check_timers(&utimer); /* Secondly timers */ |
493 |
cnt++; |
494 |
if (cnt >= 10) { /* Every 10 seconds */ |
495 |
cnt = 0; |
496 |
check_expired_dcc(); |
497 |
if (con_chan && !backgrd) { |
498 |
dprintf(DP_STDOUT, "\033[2J\033[1;1H"); |
499 |
tell_verbose_status(DP_STDOUT); |
500 |
do_module_report(DP_STDOUT, 0, "server"); |
501 |
do_module_report(DP_STDOUT, 0, "channels"); |
502 |
} |
503 |
} |
504 |
memcpy(&nowtm, localtime(&now), sizeof(struct tm)); |
505 |
if (nowtm.tm_min != lastmin) { |
506 |
int i = 0; |
507 |
|
508 |
/* Once a minute */ |
509 |
lastmin = (lastmin + 1) % 60; |
510 |
call_hook(HOOK_MINUTELY); |
511 |
check_expired_ignores(); |
512 |
autolink_cycle(NULL); /* Attempt autolinks */ |
513 |
/* In case for some reason more than 1 min has passed: */ |
514 |
while (nowtm.tm_min != lastmin) { |
515 |
/* Timer drift, dammit */ |
516 |
debug2("timer: drift (lastmin=%d, now=%d)", lastmin, nowtm.tm_min); |
517 |
i++; |
518 |
lastmin = (lastmin + 1) % 60; |
519 |
call_hook(HOOK_MINUTELY); |
520 |
} |
521 |
if (i > 1) |
522 |
putlog(LOG_MISC, "*", "(!) timer drift -- spun %d minutes", i); |
523 |
miltime = (nowtm.tm_hour * 100) + (nowtm.tm_min); |
524 |
if (((int) (nowtm.tm_min / 5) * 5) == (nowtm.tm_min)) { /* 5 min */ |
525 |
call_hook(HOOK_5MINUTELY); |
526 |
if (!quick_logs) { |
527 |
flushlogs(); |
528 |
check_logsize(); |
529 |
} |
530 |
if (!miltime) { /* At midnight */ |
531 |
char s[25]; |
532 |
int j; |
533 |
|
534 |
strncpyz(s, ctime(&now), sizeof s); |
535 |
putlog(LOG_ALL, "*", "--- %.11s%s", s, s + 20); |
536 |
call_hook(HOOK_BACKUP); |
537 |
for (j = 0; j < max_logs; j++) { |
538 |
if (logs[j].filename != NULL && logs[j].f != NULL) { |
539 |
fclose(logs[j].f); |
540 |
logs[j].f = NULL; |
541 |
} |
542 |
} |
543 |
} |
544 |
} |
545 |
if (nowtm.tm_min == notify_users_at) |
546 |
call_hook(HOOK_HOURLY); |
547 |
/* These no longer need checking since they are all check vs minutely |
548 |
* settings and we only get this far on the minute. |
549 |
*/ |
550 |
if (miltime == switch_logfiles_at) { |
551 |
call_hook(HOOK_DAILY); |
552 |
if (!keep_all_logs) { |
553 |
putlog(LOG_MISC, "*", _("Switching logfiles...")); |
554 |
for (i = 0; i < max_logs; i++) |
555 |
if (logs[i].filename) { |
556 |
char s[1024]; |
557 |
|
558 |
if (logs[i].f) { |
559 |
fclose(logs[i].f); |
560 |
logs[i].f = NULL; |
561 |
} |
562 |
egg_snprintf(s, sizeof s, "%s.yesterday", logs[i].filename); |
563 |
unlink(s); |
564 |
movefile(logs[i].filename, s); |
565 |
} |
566 |
} |
567 |
} |
568 |
} |
569 |
} |
570 |
|
571 |
static void core_minutely() |
572 |
{ |
573 |
check_bind_time(&nowtm); |
574 |
if (quick_logs != 0) { |
575 |
flushlogs(); |
576 |
check_logsize(); |
577 |
} |
578 |
} |
579 |
|
580 |
static void core_hourly() |
581 |
{ |
582 |
write_userfile(-1); |
583 |
} |
584 |
|
585 |
static void event_rehash() |
586 |
{ |
587 |
check_bind_event("rehash"); |
588 |
} |
589 |
|
590 |
static void event_prerehash() |
591 |
{ |
592 |
check_bind_event("prerehash"); |
593 |
} |
594 |
|
595 |
static void event_save() |
596 |
{ |
597 |
check_bind_event("save"); |
598 |
} |
599 |
|
600 |
static void event_logfile() |
601 |
{ |
602 |
check_bind_event("logfile"); |
603 |
} |
604 |
|
605 |
static void event_resettraffic() |
606 |
{ |
607 |
otraffic_irc += otraffic_irc_today; |
608 |
itraffic_irc += itraffic_irc_today; |
609 |
otraffic_bn += otraffic_bn_today; |
610 |
itraffic_bn += itraffic_bn_today; |
611 |
otraffic_dcc += otraffic_dcc_today; |
612 |
itraffic_dcc += itraffic_dcc_today; |
613 |
otraffic_unknown += otraffic_unknown_today; |
614 |
itraffic_unknown += itraffic_unknown_today; |
615 |
otraffic_trans += otraffic_trans_today; |
616 |
itraffic_trans += itraffic_trans_today; |
617 |
otraffic_irc_today = otraffic_bn_today = 0; |
618 |
otraffic_dcc_today = otraffic_unknown_today = 0; |
619 |
itraffic_irc_today = itraffic_bn_today = 0; |
620 |
itraffic_dcc_today = itraffic_unknown_today = 0; |
621 |
itraffic_trans_today = otraffic_trans_today = 0; |
622 |
} |
623 |
|
624 |
static void event_loaded() |
625 |
{ |
626 |
check_bind_event("loaded"); |
627 |
} |
628 |
|
629 |
void kill_tcl(); |
630 |
extern module_entry *module_list; |
631 |
void restart_chons(); |
632 |
|
633 |
int init_dcc_max(), init_userent(), init_misc(), init_net(), |
634 |
init_modules(), init_tcl(int, char **); |
635 |
void botnet_init(); |
636 |
void script_init(); |
637 |
void binds_init(); |
638 |
|
639 |
void patch(const char *str) |
640 |
{ |
641 |
char *p = strchr(egg_version, '+'); |
642 |
|
643 |
if (!p) |
644 |
p = &egg_version[strlen(egg_version)]; |
645 |
sprintf(p, "+%s", str); |
646 |
egg_numver++; |
647 |
sprintf(&egg_xtra[strlen(egg_xtra)], " %s", str); |
648 |
} |
649 |
|
650 |
static inline void garbage_collect(void) |
651 |
{ |
652 |
static u_8bit_t run_cnt = 0; |
653 |
|
654 |
if (run_cnt == 3) |
655 |
garbage_collect_tclhash(); |
656 |
else |
657 |
run_cnt++; |
658 |
} |
659 |
|
660 |
int main(int argc, char **argv) |
661 |
{ |
662 |
int xx, i; |
663 |
char buf[520], s[25]; |
664 |
FILE *f; |
665 |
struct sigaction sv; |
666 |
struct chanset_t *chan; |
667 |
egg_timeval_t howlong; |
668 |
|
669 |
#ifdef DEBUG |
670 |
/* Make sure it can write core, if you make debug. Else it's pretty |
671 |
* useless (dw) |
672 |
*/ |
673 |
{ |
674 |
#include <sys/resource.h> |
675 |
struct rlimit cdlim; |
676 |
|
677 |
cdlim.rlim_cur = RLIM_INFINITY; |
678 |
cdlim.rlim_max = RLIM_INFINITY; |
679 |
setrlimit(RLIMIT_CORE, &cdlim); |
680 |
} |
681 |
#endif |
682 |
|
683 |
/* Initialise context list */ |
684 |
for (i = 0; i < 16; i++) |
685 |
Context; |
686 |
|
687 |
setlocale(LC_MESSAGES, ""); |
688 |
bindtextdomain(PACKAGE, LOCALEDIR); |
689 |
textdomain(PACKAGE); |
690 |
|
691 |
#include "patch.h" |
692 |
/* Version info! */ |
693 |
egg_snprintf(ver, sizeof ver, "eggdrop v%s", egg_version); |
694 |
egg_snprintf(version, sizeof version, "Eggdrop v%s (C)1997 Robey Pointer (C)2001 Eggheads", |
695 |
egg_version); |
696 |
/* Now add on the patchlevel (for Tcl) */ |
697 |
sprintf(&egg_version[strlen(egg_version)], " %u", egg_numver); |
698 |
strcat(egg_version, egg_xtra); |
699 |
#ifdef STOP_UAC |
700 |
{ |
701 |
int nvpair[2]; |
702 |
|
703 |
nvpair[0] = SSIN_UACPROC; |
704 |
nvpair[1] = UAC_NOPRINT; |
705 |
setsysinfo(SSI_NVPAIRS, (char *) nvpair, 1, NULL, 0); |
706 |
} |
707 |
#endif |
708 |
|
709 |
/* Set up error traps: */ |
710 |
sv.sa_handler = got_bus; |
711 |
sigemptyset(&sv.sa_mask); |
712 |
#ifdef SA_RESETHAND |
713 |
sv.sa_flags = SA_RESETHAND; |
714 |
#else |
715 |
sv.sa_flags = 0; |
716 |
#endif |
717 |
sigaction(SIGBUS, &sv, NULL); |
718 |
sv.sa_handler = got_segv; |
719 |
sigaction(SIGSEGV, &sv, NULL); |
720 |
#ifdef SA_RESETHAND |
721 |
sv.sa_flags = 0; |
722 |
#endif |
723 |
sv.sa_handler = got_fpe; |
724 |
sigaction(SIGFPE, &sv, NULL); |
725 |
sv.sa_handler = got_term; |
726 |
sigaction(SIGTERM, &sv, NULL); |
727 |
sv.sa_handler = got_hup; |
728 |
sigaction(SIGHUP, &sv, NULL); |
729 |
sv.sa_handler = got_quit; |
730 |
sigaction(SIGQUIT, &sv, NULL); |
731 |
sv.sa_handler = SIG_IGN; |
732 |
sigaction(SIGPIPE, &sv, NULL); |
733 |
sv.sa_handler = got_ill; |
734 |
sigaction(SIGILL, &sv, NULL); |
735 |
sv.sa_handler = got_alarm; |
736 |
sigaction(SIGALRM, &sv, NULL); |
737 |
|
738 |
/* Initialize variables and stuff */ |
739 |
now = time(NULL); |
740 |
chanset = NULL; |
741 |
memcpy(&nowtm, localtime(&now), sizeof(struct tm)); |
742 |
lastmin = nowtm.tm_min; |
743 |
srandom(now % (getpid() + getppid())); |
744 |
if (argc > 1) |
745 |
for (i = 1; i < argc; i++) |
746 |
do_arg(argv[i]); |
747 |
printf("\n%s\n", version); |
748 |
|
749 |
/* Don't allow eggdrop to run as root */ |
750 |
if (((int) getuid() == 0) || ((int) geteuid() == 0)) |
751 |
fatal(_("ERROR: Eggdrop will not run as root!"), 0); |
752 |
|
753 |
script_init(); |
754 |
binds_init(); |
755 |
core_binds_init(); |
756 |
init_dcc_max(); |
757 |
init_userent(); |
758 |
init_misc(); |
759 |
botnet_init(); |
760 |
init_net(); |
761 |
init_modules(); |
762 |
|
763 |
if (backgrd) |
764 |
bg_prepare_split(); |
765 |
|
766 |
init_tcl(argc, argv); |
767 |
|
768 |
strncpyz(s, ctime(&now), sizeof s); |
769 |
strcpy(&s[11], &s[20]); |
770 |
putlog(LOG_ALL, "*", "--- Loading %s (%s)", ver, s); |
771 |
chanprog(); |
772 |
if (!encrypt_pass) { |
773 |
printf(_("You have installed modules but have not selected an encryption\n\ |
774 |
module, please consult the default config file for info.\n")); |
775 |
bg_send_quit(BG_ABORT); |
776 |
exit(1); |
777 |
} |
778 |
i = 0; |
779 |
for (chan = chanset; chan; chan = chan->next) |
780 |
i++; |
781 |
putlog(LOG_MISC, "*", "=== %s: %d channels, %d users.", |
782 |
botnetnick, i, count_users(userlist)); |
783 |
cache_miss = 0; |
784 |
cache_hit = 0; |
785 |
egg_snprintf(pid_file, sizeof pid_file, "pid.%s", botnetnick); |
786 |
|
787 |
/* Check for pre-existing eggdrop! */ |
788 |
f = fopen(pid_file, "r"); |
789 |
if (f != NULL) { |
790 |
fgets(s, 10, f); |
791 |
xx = atoi(s); |
792 |
kill(xx, SIGCHLD); /* Meaningless kill to determine if pid |
793 |
is used */ |
794 |
if (errno != ESRCH) { |
795 |
printf(_("I detect %s already running from this directory.\n"), origbotname); |
796 |
printf(_("If this is incorrect, erase the %s\n"), pid_file); |
797 |
bg_send_quit(BG_ABORT); |
798 |
exit(1); |
799 |
} |
800 |
} |
801 |
|
802 |
/* Move into background? */ |
803 |
if (backgrd) { |
804 |
#ifndef CYGWIN_HACKS |
805 |
bg_do_split(); |
806 |
} else { /* !backgrd */ |
807 |
#endif |
808 |
xx = getpid(); |
809 |
if (xx != 0) { |
810 |
FILE *fp; |
811 |
|
812 |
/* Write pid to file */ |
813 |
unlink(pid_file); |
814 |
fp = fopen(pid_file, "w"); |
815 |
if (fp != NULL) { |
816 |
fprintf(fp, "%u\n", xx); |
817 |
if (fflush(fp)) { |
818 |
/* Let the bot live since this doesn't appear to be a botchk */ |
819 |
printf(_("* Warning! Could not write %s file!\n"), pid_file); |
820 |
fclose(fp); |
821 |
unlink(pid_file); |
822 |
} else |
823 |
fclose(fp); |
824 |
} else |
825 |
printf(_("* Warning! Could not write %s file!\n"), pid_file); |
826 |
#ifdef CYGWIN_HACKS |
827 |
printf("Launched into the background (pid: %d)\n\n", xx); |
828 |
#endif |
829 |
} |
830 |
} |
831 |
|
832 |
use_stderr = 0; /* Stop writing to stderr now */ |
833 |
if (backgrd) { |
834 |
/* Ok, try to disassociate from controlling terminal (finger cross) */ |
835 |
#if HAVE_SETPGID && !defined(CYGWIN_HACKS) |
836 |
setpgid(0, 0); |
837 |
#endif |
838 |
/* Tcl wants the stdin, stdout and stderr file handles kept open. */ |
839 |
freopen("/dev/null", "r", stdin); |
840 |
freopen("/dev/null", "w", stdout); |
841 |
freopen("/dev/null", "w", stderr); |
842 |
#ifdef CYGWIN_HACKS |
843 |
FreeConsole(); |
844 |
#endif |
845 |
} |
846 |
|
847 |
/* Terminal emulating dcc chat */ |
848 |
if (!backgrd && term_z) { |
849 |
int n = new_dcc(&DCC_CHAT, sizeof(struct chat_info)); |
850 |
|
851 |
dcc[n].addr[0] = '\0'; |
852 |
dcc[n].sock = STDOUT; |
853 |
dcc[n].timeval = now; |
854 |
dcc[n].u.chat->con_flags = conmask; |
855 |
dcc[n].u.chat->strip_flags = STRIP_ALL; |
856 |
dcc[n].status = STAT_ECHO; |
857 |
strcpy(dcc[n].nick, "HQ"); |
858 |
strcpy(dcc[n].host, "llama@console"); |
859 |
dcc[n].user = get_user_by_handle(userlist, "HQ"); |
860 |
/* Make sure there's an innocuous HQ user if needed */ |
861 |
if (!dcc[n].user) { |
862 |
userlist = adduser(userlist, "HQ", "none", "-", USER_PARTY); |
863 |
dcc[n].user = get_user_by_handle(userlist, "HQ"); |
864 |
} |
865 |
setsock(STDOUT, 0); /* Entry in net table */ |
866 |
dprintf(n, "\n### ENTERING DCC CHAT SIMULATION ###\n\n"); |
867 |
dcc_chatter(n); |
868 |
} |
869 |
|
870 |
then = now; |
871 |
online_since = now; |
872 |
autolink_cycle(NULL); /* Hurry and connect to tandem bots */ |
873 |
add_help_reference("cmds1.help"); |
874 |
add_help_reference("cmds2.help"); |
875 |
add_help_reference("core.help"); |
876 |
howlong.sec = 1; |
877 |
howlong.usec = 0; |
878 |
timer_create_repeater(&howlong, (Function) core_secondly); |
879 |
add_hook(HOOK_MINUTELY, (Function) core_minutely); |
880 |
add_hook(HOOK_HOURLY, (Function) core_hourly); |
881 |
add_hook(HOOK_REHASH, (Function) event_rehash); |
882 |
add_hook(HOOK_PRE_REHASH, (Function) event_prerehash); |
883 |
add_hook(HOOK_USERFILE, (Function) event_save); |
884 |
add_hook(HOOK_BACKUP, (Function) backup_userfile); |
885 |
add_hook(HOOK_DAILY, (Function) event_logfile); |
886 |
add_hook(HOOK_DAILY, (Function) event_resettraffic); |
887 |
add_hook(HOOK_LOADED, (Function) event_loaded); |
888 |
|
889 |
call_hook(HOOK_LOADED); |
890 |
|
891 |
debug0("main: entering loop"); |
892 |
while (1) { |
893 |
int socket_cleanup = 0; |
894 |
|
895 |
#if !defined(HAVE_PRE7_5_TCL) |
896 |
/* Process a single tcl event */ |
897 |
Tcl_DoOneEvent(TCL_ALL_EVENTS | TCL_DONT_WAIT); |
898 |
#endif |
899 |
|
900 |
/* Lets move some of this here, reducing the numer of actual |
901 |
* calls to periodic_timers |
902 |
*/ |
903 |
now = time(NULL); |
904 |
random(); /* Woop, lets really jumble things */ |
905 |
timer_run(); |
906 |
if (now != then) { /* Once a second */ |
907 |
/* call_hook(HOOK_SECONDLY); */ |
908 |
then = now; |
909 |
} |
910 |
|
911 |
/* Only do this every so often. */ |
912 |
if (!socket_cleanup) { |
913 |
socket_cleanup = 5; |
914 |
|
915 |
/* Remove dead dcc entries. */ |
916 |
dcc_remove_lost(); |
917 |
|
918 |
/* Check for server or dcc activity. */ |
919 |
dequeue_sockets(); |
920 |
} else |
921 |
socket_cleanup--; |
922 |
|
923 |
/* Free unused structures. */ |
924 |
garbage_collect(); |
925 |
|
926 |
xx = sockgets(buf, &i); |
927 |
if (xx >= 0) { /* Non-error */ |
928 |
int idx; |
929 |
|
930 |
for (idx = 0; idx < dcc_total; idx++) |
931 |
if (dcc[idx].sock == xx) { |
932 |
if (dcc[idx].type && dcc[idx].type->activity) { |
933 |
/* Traffic stats */ |
934 |
if (dcc[idx].type->name) { |
935 |
if (!strncmp(dcc[idx].type->name, "BOT", 3)) |
936 |
itraffic_bn_today += strlen(buf) + 1; |
937 |
else if (!strcmp(dcc[idx].type->name, "SERVER")) |
938 |
itraffic_irc_today += strlen(buf) + 1; |
939 |
else if (!strncmp(dcc[idx].type->name, "CHAT", 4)) |
940 |
itraffic_dcc_today += strlen(buf) + 1; |
941 |
else if (!strncmp(dcc[idx].type->name, "FILES", 5)) |
942 |
itraffic_dcc_today += strlen(buf) + 1; |
943 |
else if (!strcmp(dcc[idx].type->name, "SEND")) |
944 |
itraffic_trans_today += strlen(buf) + 1; |
945 |
else if (!strncmp(dcc[idx].type->name, "GET", 3)) |
946 |
itraffic_trans_today += strlen(buf) + 1; |
947 |
else |
948 |
itraffic_unknown_today += strlen(buf) + 1; |
949 |
} |
950 |
dcc[idx].type->activity(idx, buf, i); |
951 |
} else |
952 |
putlog(LOG_MISC, "*", |
953 |
"!!! untrapped dcc activity: type %s, sock %d", |
954 |
dcc[idx].type->name, dcc[idx].sock); |
955 |
break; |
956 |
} |
957 |
} else if (xx == -1) { /* EOF from someone */ |
958 |
int idx; |
959 |
|
960 |
if (i == STDOUT && !backgrd) |
961 |
fatal("END OF FILE ON TERMINAL", 0); |
962 |
for (idx = 0; idx < dcc_total; idx++) |
963 |
if (dcc[idx].sock == i) { |
964 |
if (dcc[idx].type && dcc[idx].type->eof) |
965 |
dcc[idx].type->eof(idx); |
966 |
else { |
967 |
putlog(LOG_MISC, "*", |
968 |
"*** ATTENTION: DEAD SOCKET (%d) OF TYPE %s UNTRAPPED", |
969 |
i, dcc[idx].type ? dcc[idx].type->name : "*UNKNOWN*"); |
970 |
killsock(i); |
971 |
lostdcc(idx); |
972 |
} |
973 |
idx = dcc_total + 1; |
974 |
} |
975 |
if (idx == dcc_total) { |
976 |
putlog(LOG_MISC, "*", |
977 |
"(@) EOF socket %d, not a dcc socket, not anything.", i); |
978 |
close(i); |
979 |
killsock(i); |
980 |
} |
981 |
} else if (xx == -2 && errno != EINTR) { /* select() error */ |
982 |
putlog(LOG_MISC, "*", "* Socket error #%d; recovering.", errno); |
983 |
for (i = 0; i < dcc_total; i++) { |
984 |
if ((fcntl(dcc[i].sock, F_GETFD, 0) == -1) && (errno = EBADF)) { |
985 |
putlog(LOG_MISC, "*", |
986 |
"DCC socket %d (type %d, name '%s') expired -- pfft", |
987 |
dcc[i].sock, dcc[i].type, dcc[i].nick); |
988 |
killsock(dcc[i].sock); |
989 |
lostdcc(i); |
990 |
i--; |
991 |
} |
992 |
} |
993 |
} else if (xx == -3) { |
994 |
call_hook(HOOK_IDLE); |
995 |
socket_cleanup = 0; /* If we've been idle, cleanup & flush */ |
996 |
} |
997 |
|
998 |
if (do_restart) { |
999 |
if (do_restart == -2) |
1000 |
rehash(); |
1001 |
else { |
1002 |
/* Unload as many modules as possible */ |
1003 |
int f = 1; |
1004 |
module_entry *p; |
1005 |
Function x; |
1006 |
char xx[256]; |
1007 |
|
1008 |
/* oops, I guess we should call this event before tcl is restarted */ |
1009 |
check_bind_event("prerestart"); |
1010 |
|
1011 |
while (f) { |
1012 |
f = 0; |
1013 |
for (p = module_list; p != NULL; p = p->next) { |
1014 |
dependancy *d = dependancy_list; |
1015 |
int ok = 1; |
1016 |
|
1017 |
while (ok && d) { |
1018 |
if (d->needed == p) |
1019 |
ok = 0; |
1020 |
d = d->next; |
1021 |
} |
1022 |
if (ok) { |
1023 |
strcpy(xx, p->name); |
1024 |
if (module_unload(xx, origbotname) == NULL) { |
1025 |
f = 1; |
1026 |
break; |
1027 |
} |
1028 |
} |
1029 |
} |
1030 |
} |
1031 |
p = module_list; |
1032 |
if (p && p->next && p->next->next) |
1033 |
/* Should be only 2 modules now - blowfish (or some other |
1034 |
encryption module) and eggdrop. */ |
1035 |
putlog(LOG_MISC, "*", _("Stagnant module; there WILL be memory leaks!")); |
1036 |
flushlogs(); |
1037 |
kill_tcl(); |
1038 |
timer_destroy_all(); /* Destroy all timers. */ |
1039 |
init_tcl(argc, argv); |
1040 |
/* We expect the encryption module as the current module pointed |
1041 |
* to by `module_list'. |
1042 |
*/ |
1043 |
x = p->funcs[MODCALL_START]; |
1044 |
/* `NULL' indicates that we just recently restarted. The module |
1045 |
* is expected to re-initialise as needed. |
1046 |
*/ |
1047 |
x(NULL); |
1048 |
rehash(); |
1049 |
restart_chons(); |
1050 |
call_hook(HOOK_LOADED); |
1051 |
} |
1052 |
do_restart = 0; |
1053 |
} |
1054 |
} |
1055 |
} |