1 /*
2 * main.c -- handles:
3 * core event handling
4 * signal handling
5 * command line arguments
6 * context and assert debugging
7 */
8 /*
9 * Copyright (C) 1997 Robey Pointer
10 * Copyright (C) 1999 - 2021 Eggheads Development Team
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25 */
26 /*
27 * The author (Robey Pointer) can be reached at: robey@netcom.com
28 * NOTE: Robey is no long working on this code, there is a discussion
29 * list available at eggheads@eggheads.org.
30 */
31
32 /* We need config.h for CYGWIN_HACKS, but windows.h must be included before
33 * eggdrop headers, because the malloc/free/Context macros break the inclusion.
34 * The SSL undefs are a workaround for bug #2182 in openssl with msys/mingw.
35 */
36 #include <config.h>
37 #ifdef CYGWIN_HACKS
38 # include <windows.h>
39 # undef X509_NAME
40 # undef X509_EXTENSIONS
41 # undef X509_CERT_PAIR
42 # undef PKCS7_ISSUER_AND_SERIAL
43 # undef PKCS7_SIGNER_INFO
44 # undef OCSP_REQUEST
45 # undef OCSP_RESPONSE
46 #endif
47
48 #include "main.h"
49
50 #include <errno.h>
51 #include <fcntl.h>
52 #include <resolv.h>
53 #include <setjmp.h>
54 #include <signal.h>
55
56 #ifdef TIME_WITH_SYS_TIME
57 # include <sys/time.h>
58 # include <time.h>
59 #else
60 # ifdef HAVE_SYS_TIME_H
61 # include <sys/time.h>
62 # else
63 # include <time.h>
64 # endif
65 #endif
66
67 #ifdef STOP_UAC /* OSF/1 complains a lot */
68 # include <sys/sysinfo.h>
69 # define UAC_NOPRINT 0x00000001 /* Don't report unaligned fixups */
70 #endif
71
72 #include "version.h"
73 #include "chan.h"
74 #include "modules.h"
75 #include "tandem.h"
76 #include "bg.h"
77
78 #ifdef DEBUG /* For debug compile */
79 # include <sys/resource.h> /* setrlimit() */
80 #endif
81
82 #ifdef HAVE_GETRANDOM
83 # include <sys/random.h>
84 #endif
85
86 #ifndef _POSIX_SOURCE
87 # define _POSIX_SOURCE 1 /* Solaris needs this */
88 #endif
89
90 extern char origbotname[], botnetnick[];
91 extern int dcc_total, conmask, cache_hit, cache_miss, max_logs, quick_logs,
92 quiet_save;
93 extern struct dcc_t *dcc;
94 extern struct userrec *userlist;
95 extern struct chanset_t *chanset;
96 extern log_t *logs;
97 extern Tcl_Interp *interp;
98 extern tcl_timer_t *timer, *utimer;
99 extern sigjmp_buf alarmret;
100 time_t now;
101 static int argc;
102 static char **argv;
103
104 /*
105 * Please use the PATCH macro instead of directly altering the version
106 * string from now on (it makes it much easier to maintain patches).
107 * Also please read the README file regarding your rights to distribute
108 * modified versions of this bot.
109 */
110
111 char egg_version[1024];
112 int egg_numver = EGG_NUMVER;
113
114 char notify_new[121] = ""; /* Person to send a note to for new users */
115 int default_flags = 0; /* Default user flags */
116 int default_uflags = 0; /* Default user-definied flags */
117
118 int backgrd = 1; /* Run in the background? */
119 int con_chan = 0; /* Foreground: constantly display channel stats? */
120 int term_z = -1; /* Foreground: use the terminal as a partyline? */
121 int use_stderr = 1; /* Send stuff to stderr instead of logfiles? */
122
123 char configfile[121] = "eggdrop.conf"; /* Default config file name */
124 char pid_file[121]; /* Name of the pid file */
125 char helpdir[121] = "help/"; /* Directory of help files */
126 char textdir[121] = "text/"; /* Directory for text files */
127
128 int keep_all_logs = 0; /* Never erase logfiles? */
129 int switch_logfiles_at = 300; /* When to switch logfiles */
130
131 time_t online_since; /* time that the bot was started */
132
133 int make_userfile = 0; /* Using bot in userfile-creation mode? */
134
135 int save_users_at = 0; /* Minutes past the hour to save the userfile? */
136 int notify_users_at = 0; /* Minutes past the hour to notify users of notes? */
137
138 char version[81]; /* Version info (long form) */
139 char ver[41]; /* Version info (short form) */
140
141 volatile sig_atomic_t do_restart = 0; /* .restart has been called, restart ASAP */
142 int resolve_timeout = RES_TIMEOUT; /* Hostname/address lookup timeout */
143 char quit_msg[1024]; /* Quit message */
144
145 /* Moved here for n flag warning, put back in do_arg if removed */
146 unsigned char cliflags = 0;
147
148
149 /* Traffic stats */
150 unsigned long otraffic_irc = 0;
151 unsigned long otraffic_irc_today = 0;
152 unsigned long otraffic_bn = 0;
153 unsigned long otraffic_bn_today = 0;
154 unsigned long otraffic_dcc = 0;
155 unsigned long otraffic_dcc_today = 0;
156 unsigned long otraffic_filesys = 0;
157 unsigned long otraffic_filesys_today = 0;
158 unsigned long otraffic_trans = 0;
159 unsigned long otraffic_trans_today = 0;
160 unsigned long otraffic_unknown = 0;
161 unsigned long otraffic_unknown_today = 0;
162 unsigned long itraffic_irc = 0;
163 unsigned long itraffic_irc_today = 0;
164 unsigned long itraffic_bn = 0;
165 unsigned long itraffic_bn_today = 0;
166 unsigned long itraffic_dcc = 0;
167 unsigned long itraffic_dcc_today = 0;
168 unsigned long itraffic_trans = 0;
169 unsigned long itraffic_trans_today = 0;
170 unsigned long itraffic_unknown = 0;
171 unsigned long itraffic_unknown_today = 0;
172
173 #ifdef DEBUG_CONTEXT
174 /* Context storage for fatal crashes */
175 char cx_file[16][32];
176 char cx_note[16][256];
177 int cx_line[16];
178 int cx_ptr = 0;
179 #endif
180
181 #ifdef TLS
182 int ssl_cleanup();
183 #endif
184
fatal(const char * s,int recoverable)185 void fatal(const char *s, int recoverable)
186 {
187 int i;
188
189 putlog(LOG_MISC, "*", "* %s", s);
190 flushlogs();
191 for (i = 0; i < dcc_total; i++)
192 if (dcc[i].sock >= 0)
193 killsock(dcc[i].sock);
194 #ifdef TLS
195 ssl_cleanup();
196 #endif
197 unlink(pid_file);
198 if (recoverable != 1) {
199 bg_send_quit(BG_ABORT);
200 exit(!recoverable);
201 }
202 }
203
204 int expmem_chanprog();
205 int expmem_users();
206 int expmem_misc();
207 int expmem_dccutil();
208 int expmem_botnet();
209 int expmem_tcl();
210 int expmem_tclhash();
211 int expmem_net();
212 int expmem_modules(int);
213 int expmem_language();
214 int expmem_tcldcc();
215 int expmem_tclmisc();
216 int expmem_dns();
217 #ifdef TLS
218 int expmem_tls();
219 #endif
220
221 /* For mem.c : calculate memory we SHOULD be using
222 */
expected_memory(void)223 int expected_memory(void)
224 {
225 int tot;
226
227 tot = expmem_chanprog() + expmem_users() + expmem_misc() + expmem_dccutil() +
228 expmem_botnet() + expmem_tcl() + expmem_tclhash() + expmem_net() +
229 expmem_modules(0) + expmem_language() + expmem_tcldcc() +
230 expmem_tclmisc() + expmem_dns();
231 #ifdef TLS
232 tot += expmem_tls();
233 #endif
234 return tot;
235 }
236
check_expired_dcc()237 static void check_expired_dcc()
238 {
239 int i;
240
241 for (i = 0; i < dcc_total; i++)
242 if (dcc[i].type && dcc[i].type->timeout_val &&
243 ((now - dcc[i].timeval) > *(dcc[i].type->timeout_val))) {
244 if (dcc[i].type->timeout)
245 dcc[i].type->timeout(i);
246 else if (dcc[i].type->eof)
247 dcc[i].type->eof(i);
248 else
249 continue;
250 /* Only timeout 1 socket per cycle, too risky for more */
251 return;
252 }
253 }
254
255 #ifndef DEBUG_CONTEXT
256 #define write_debug() do {} while (0)
257 #else
258 static int nested_debug = 0;
259
write_debug()260 static void write_debug()
261 {
262 int x;
263 char s[25];
264 int y;
265
266 if (nested_debug) {
267 /* Yoicks, if we have this there's serious trouble!
268 * All of these are pretty reliable, so we'll try these.
269 *
270 * NOTE: don't try and display context-notes in here, it's
271 * _not_ safe <cybah>
272 */
273 x = creat("DEBUG.DEBUG", 0644);
274 if (x >= 0) {
275 setsock(x, SOCK_NONSOCK);
276 strlcpy(s, ctime(&now), sizeof s);
277 dprintf(-x, "Debug (%s) written %s\n", ver, s);
278 dprintf(-x, "Please report problem to bugs@eggheads.org\n");
279 dprintf(-x, "after a visit to http://www.eggheads.org/bugzilla/\n");
280 #ifdef EGG_PATCH
281 dprintf(-x, "Patch level: %s\n", EGG_PATCH);
282 #else
283 dprintf(-x, "Patch level: %s\n", "stable");
284 #endif
285 dprintf(-x, "Context: ");
286 cx_ptr = cx_ptr & 15;
287 for (y = ((cx_ptr + 1) & 15); y != cx_ptr; y = ((y + 1) & 15))
288 dprintf(-x, "%s/%d,\n ", cx_file[y], cx_line[y]);
289 dprintf(-x, "%s/%d\n\n", cx_file[y], cx_line[y]);
290 killsock(x);
291 close(x);
292 }
293 bg_send_quit(BG_ABORT);
294 exit(1); /* Dont even try & tell people about, that may
295 * have caused the fault last time. */
296 } else
297 nested_debug = 1;
298 putlog(LOG_MISC, "*", "* Last context: %s/%d [%s]", cx_file[cx_ptr],
299 cx_line[cx_ptr], cx_note[cx_ptr][0] ? cx_note[cx_ptr] : "");
300 putlog(LOG_MISC, "*", "* Please REPORT this BUG!");
301 putlog(LOG_MISC, "*", "* Check doc/BUG-REPORT on how to do so.");
302 x = creat("DEBUG", 0644);
303 setsock(x, SOCK_NONSOCK);
304 if (x < 0) {
305 putlog(LOG_MISC, "*", "* Failed to write DEBUG");
306 } else {
307 strlcpy(s, ctime(&now), sizeof s);
308 dprintf(-x, "Debug (%s) written %s\n", ver, s);
309 #ifdef EGG_PATCH
310 dprintf(-x, "Patch level: %s\n", EGG_PATCH);
311 #else
312 dprintf(-x, "Patch level: %s\n", "stable");
313 #endif
314 #ifdef STATIC
315 dprintf(-x, "STATICALLY LINKED\n");
316 #endif
317
318 /* info library */
319 dprintf(-x, "Tcl library: %s\n",
320 ((interp) && (Tcl_Eval(interp, "info library") == TCL_OK)) ?
321 tcl_resultstring() : "*unknown*");
322
323 /* info tclversion/patchlevel */
324 dprintf(-x, "Tcl version: %s (header version %s)\n",
325 ((interp) && (Tcl_Eval(interp, "info patchlevel") == TCL_OK)) ?
326 tcl_resultstring() : (Tcl_Eval(interp, "info tclversion") == TCL_OK) ?
327 tcl_resultstring() : "*unknown*", TCL_PATCH_LEVEL);
328
329 if (tcl_threaded())
330 dprintf(-x, "Tcl is threaded\n");
331 #ifdef IPV6
332 dprintf(-x, "Compiled with IPv6 support\n");
333 #else
334 dprintf(-x, "Compiled without IPv6 support\n");
335 #endif
336 #ifdef TLS
337 dprintf(-x, "Compiled with TLS support\n");
338 #else
339 dprintf(-x, "Compiled without TLS support\n");
340 #endif
341 if (!strcmp(EGG_AC_ARGS, "")) {
342 dprintf(-x, "Configure flags: none\n");
343 } else {
344 dprintf(-x, "Configure flags: %s\n", EGG_AC_ARGS);
345 }
346 #ifdef CCFLAGS
347 dprintf(-x, "Compile flags: %s\n", CCFLAGS);
348 #endif
349
350 #ifdef LDFLAGS
351 dprintf(-x, "Link flags: %s\n", LDFLAGS);
352 #endif
353
354 #ifdef STRIPFLAGS
355 dprintf(-x, "Strip flags: %s\n", STRIPFLAGS);
356 #endif
357
358 dprintf(-x, "Context: ");
359 cx_ptr = cx_ptr & 15;
360 for (y = ((cx_ptr + 1) & 15); y != cx_ptr; y = ((y + 1) & 15))
361 dprintf(-x, "%s/%d, [%s]\n ", cx_file[y], cx_line[y],
362 (cx_note[y][0]) ? cx_note[y] : "");
363 dprintf(-x, "%s/%d [%s]\n\n", cx_file[cx_ptr], cx_line[cx_ptr],
364 (cx_note[cx_ptr][0]) ? cx_note[cx_ptr] : "");
365 tell_dcc(-x);
366 dprintf(-x, "\n");
367 debug_mem_to_dcc(-x);
368 killsock(x);
369 close(x);
370 putlog(LOG_MISC, "*", "* Wrote DEBUG");
371 }
372 }
373 #endif /* DEBUG_CONTEXT */
374
got_bus(int z)375 static void got_bus(int z)
376 {
377 write_debug();
378 fatal("BUS ERROR -- CRASHING!", 1);
379 #ifdef SA_RESETHAND
380 kill(getpid(), SIGBUS);
381 #else
382 bg_send_quit(BG_ABORT);
383 exit(1);
384 #endif
385 }
386
got_segv(int z)387 static void got_segv(int z)
388 {
389 write_debug();
390 fatal("SEGMENT VIOLATION -- CRASHING!", 1);
391 #ifdef SA_RESETHAND
392 kill(getpid(), SIGSEGV);
393 #else
394 bg_send_quit(BG_ABORT);
395 exit(1);
396 #endif
397 }
398
got_fpe(int z)399 static void got_fpe(int z)
400 {
401 write_debug();
402 fatal("FLOATING POINT ERROR -- CRASHING!", 0);
403 }
404
got_term(int z)405 static void got_term(int z)
406 {
407 /* Now we die by default on sigterm, but scripts have the chance to
408 * catch the event themselves and cancel shutdown by returning 1
409 */
410 if (check_tcl_signal("sigterm"))
411 return;
412 kill_bot("ACK, I've been terminated!", "TERMINATE SIGNAL -- SIGNING OFF");
413 }
414
got_quit(int z)415 static void got_quit(int z)
416 {
417 if (check_tcl_signal("sigquit"))
418 return;
419 putlog(LOG_MISC, "*", "Received QUIT signal: restarting...");
420 do_restart = -1;
421 return;
422 }
423
got_hup(int z)424 static void got_hup(int z)
425 {
426 write_userfile(-1);
427 if (check_tcl_signal("sighup"))
428 return;
429 putlog(LOG_MISC, "*", "Received HUP signal: rehashing...");
430 do_restart = -2;
431 return;
432 }
433
434 /* A call to resolver (gethostbyname, etc) timed out
435 */
got_alarm(int z)436 static void got_alarm(int z)
437 {
438 siglongjmp(alarmret, 1);
439
440 /* -Never reached- */
441 }
442
443 /* Got ILL signal -- log context and continue
444 */
got_ill(int z)445 static void got_ill(int z)
446 {
447 check_tcl_signal("sigill");
448 #ifdef DEBUG_CONTEXT
449 putlog(LOG_MISC, "*", "* Context: %s/%d [%s]", cx_file[cx_ptr],
450 cx_line[cx_ptr], (cx_note[cx_ptr][0]) ? cx_note[cx_ptr] : "");
451 #endif
452 }
453
454 #ifdef DEBUG_CONTEXT
455 /* Called from the Context macro.
456 */
eggContext(const char * file,int line,const char * module)457 void eggContext(const char *file, int line, const char *module)
458 {
459 eggContextNote(file, line, module, NULL);
460 }
461
462 /* Called from the ContextNote macro.
463 */
eggContextNote(const char * file,int line,const char * module,const char * note)464 void eggContextNote(const char *file, int line, const char *module,
465 const char *note)
466 {
467 char *p;
468
469 p = strrchr(file, '/');
470 cx_ptr = ((cx_ptr + 1) & 15);
471 if (!module)
472 strlcpy(cx_file[cx_ptr], p ? p + 1 : file, sizeof cx_file[cx_ptr]);
473 else
474 snprintf(cx_file[cx_ptr], sizeof cx_file[cx_ptr], "%s:%s", module, p ? p + 1 : file);
475 cx_line[cx_ptr] = line;
476 if (!note)
477 cx_note[cx_ptr][0] = 0;
478 else
479 strlcpy(cx_note[cx_ptr], note, sizeof cx_note[cx_ptr]);
480 }
481 #endif /* DEBUG_CONTEXT */
482
483 #ifdef DEBUG_ASSERT
484 /* Called from the Assert macro.
485 */
eggAssert(const char * file,int line,const char * module)486 void eggAssert(const char *file, int line, const char *module)
487 {
488 write_debug();
489 if (!module)
490 putlog(LOG_MISC, "*", "* In file %s, line %u", file, line);
491 else
492 putlog(LOG_MISC, "*", "* In file %s:%s, line %u", module, file, line);
493 fatal("ASSERT FAILED -- CRASHING!", 1);
494 }
495 #endif
496
show_ver()497 static void show_ver() {
498 char x[512], *z = x;
499
500 strlcpy(x, egg_version, sizeof x);
501 newsplit(&z);
502 newsplit(&z);
503 printf("%s\n", version);
504 if (z[0]) {
505 printf(" (patches: %s)\n", z);
506 }
507 if (!strcmp(EGG_AC_ARGS, "")) {
508 printf("Configure flags: none\n");
509 } else {
510 printf("Configure flags: %s\n", EGG_AC_ARGS);
511 }
512 printf("Compiled with: ");
513 #ifdef IPV6
514 printf("IPv6, ");
515 #endif
516 #ifdef TLS
517 printf("TLS, ");
518 #endif
519 #ifdef EGG_TDNS
520 printf("Threaded DNS core (beta), ");
521 #endif
522 printf("handlen=%d\n", HANDLEN);
523 bg_send_quit(BG_ABORT);
524 }
525
526 /* Hard coded text because config file isn't loaded yet,
527 meaning other languages can't be loaded yet.
528 English (or an error) is the only possible option.
529 */
show_help()530 static void show_help() {
531 printf("\n%s\n\n", version);
532 printf("Usage: %s [options] [config-file]\n\n"
533 "Options:\n"
534 "-n Don't background; send all log entries to console.\n"
535 "-c Don't background; display channel stats every 10 seconds.\n"
536 "-t Don't background; use terminal to simulate DCC chat.\n"
537 "-m Create userfile.\n"
538 "-h Show this help and exit.\n"
539 "-v Show version info and exit.\n\n", argv[0]);
540 bg_send_quit(BG_ABORT);
541 }
542
do_arg()543 static void do_arg()
544 {
545 int option = 0;
546 /* Put this back if removing n flag warning
547 unsigned char cliflags = 0;
548 */
549 #define CLI_V 1 << 0
550 #define CLI_M 1 << 1
551 #define CLI_T 1 << 2
552 #define CLI_C 1 << 3
553 #define CLI_N 1 << 4
554 #define CLI_H 1 << 5
555 #define CLI_BAD_FLAG 1 << 6
556
557 while ((option = getopt(argc, argv, "hnctmv")) != -1) {
558 switch (option) {
559 case 'n':
560 cliflags |= CLI_N;
561 backgrd = 0;
562 break;
563 case 'c':
564 cliflags |= CLI_C;
565 con_chan = 1;
566 term_z = -1;
567 backgrd = 0;
568 break;
569 case 't':
570 cliflags |= CLI_T;
571 con_chan = 0;
572 term_z = 0;
573 backgrd = 0;
574 break;
575 case 'm':
576 cliflags |= CLI_M;
577 make_userfile = 1;
578 break;
579 case 'v':
580 cliflags |= CLI_V;
581 break;
582 case 'h':
583 cliflags |= CLI_H;
584 break;
585 default:
586 cliflags |= CLI_BAD_FLAG;
587 break;
588 }
589 }
590 if (cliflags & CLI_H) {
591 show_help();
592 exit(0);
593 } else if (cliflags & CLI_BAD_FLAG) {
594 show_help();
595 exit(1);
596 } else if (cliflags & CLI_V) {
597 show_ver();
598 exit(0);
599 } else if (argc > (optind + 1)) {
600 printf("\n");
601 printf("WARNING: More than one config file value detected\n");
602 printf(" Using %s as config file\n", argv[optind]);
603 }
604 if (argc > optind) {
605 strlcpy(configfile, argv[optind], sizeof configfile);
606 }
607 }
608
609 /* Timer info */
610 static time_t lastmin;
611 static time_t then;
612 static struct tm nowtm;
613
614 /* Called once a second.
615 *
616 * Note: Try to not put any Context lines in here (guppy 21Mar2000).
617 */
core_secondly()618 static void core_secondly()
619 {
620 static int cnt = 10; /* Don't wait the first 10 seconds to display */
621 int miltime;
622 time_t nowmins;
623 int i;
624
625 do_check_timers(&utimer); /* Secondly timers */
626 cnt++;
627 if (cnt >= 10) { /* Every 10 seconds */
628 cnt = 0;
629 check_expired_dcc();
630 if (con_chan && !backgrd) {
631 dprintf(DP_STDOUT, "\033[2J\033[1;1H");
632 if ((cliflags & CLI_N) && (cliflags & CLI_C)) {
633 printf("NOTE: You don't need to use the -n flag with the\n");
634 printf(" -t or -c flag anymore.\n");
635 }
636 tell_verbose_status(DP_STDOUT);
637 do_module_report(DP_STDOUT, 0, "server");
638 do_module_report(DP_STDOUT, 0, "channels");
639 tell_mem_status_dcc(DP_STDOUT);
640 }
641 }
642 nowmins = time(NULL) / 60;
643 if (nowmins > lastmin) {
644 memcpy(&nowtm, localtime(&now), sizeof(struct tm));
645 i = 0;
646
647 /* Once a minute */
648 ++lastmin;
649 call_hook(HOOK_MINUTELY);
650 check_expired_ignores();
651 autolink_cycle(NULL); /* Attempt autolinks */
652 /* In case for some reason more than 1 min has passed: */
653 while (nowmins != lastmin) {
654 /* Timer drift, dammit */
655 debug2("timer: drift (lastmin=%d, nowmins=%d)", lastmin, nowmins);
656 i++;
657 ++lastmin;
658 call_hook(HOOK_MINUTELY);
659 }
660 if (i > 1)
661 putlog(LOG_MISC, "*", "(!) timer drift -- spun %d minutes", i);
662 miltime = (nowtm.tm_hour * 100) + (nowtm.tm_min);
663 if (((int) (nowtm.tm_min / 5) * 5) == (nowtm.tm_min)) { /* 5 min */
664 call_hook(HOOK_5MINUTELY);
665 check_botnet_pings();
666 if (!quick_logs) {
667 flushlogs();
668 check_logsize();
669 }
670 if (!miltime) { /* At midnight */
671 char s[25];
672 int j;
673
674 strlcpy(s, ctime(&now), sizeof s);
675 if (quiet_save < 3)
676 putlog(LOG_ALL, "*", "--- %.11s%s", s, s + 20);
677 call_hook(HOOK_BACKUP);
678 for (j = 0; j < max_logs; j++) {
679 if (logs[j].filename != NULL && logs[j].f != NULL) {
680 fclose(logs[j].f);
681 logs[j].f = NULL;
682 }
683 }
684 }
685 }
686 if (nowtm.tm_min == notify_users_at)
687 call_hook(HOOK_HOURLY);
688 /* These no longer need checking since they are all check vs minutely
689 * settings and we only get this far on the minute.
690 */
691 if (miltime == switch_logfiles_at) {
692 call_hook(HOOK_DAILY);
693 if (!keep_all_logs) {
694 if (quiet_save < 3)
695 putlog(LOG_MISC, "*", MISC_LOGSWITCH);
696 for (i = 0; i < max_logs; i++)
697 if (logs[i].filename) {
698 char s[1024];
699
700 if (logs[i].f) {
701 fclose(logs[i].f);
702 logs[i].f = NULL;
703 }
704 egg_snprintf(s, sizeof s, "%s.yesterday", logs[i].filename);
705 unlink(s);
706 movefile(logs[i].filename, s);
707 }
708 }
709 }
710 }
711 }
712
core_minutely()713 static void core_minutely()
714 {
715 check_tcl_time_and_cron(&nowtm);
716 do_check_timers(&timer);
717 if (quick_logs != 0) {
718 flushlogs();
719 check_logsize();
720 }
721 }
722
core_hourly()723 static void core_hourly()
724 {
725 write_userfile(-1);
726 }
727
event_rehash()728 static void event_rehash()
729 {
730 check_tcl_event("rehash");
731 }
732
event_prerehash()733 static void event_prerehash()
734 {
735 check_tcl_event("prerehash");
736 }
737
event_save()738 static void event_save()
739 {
740 check_tcl_event("save");
741 }
742
event_logfile()743 static void event_logfile()
744 {
745 check_tcl_event("logfile");
746 }
747
event_resettraffic()748 static void event_resettraffic()
749 {
750 otraffic_irc += otraffic_irc_today;
751 itraffic_irc += itraffic_irc_today;
752 otraffic_bn += otraffic_bn_today;
753 itraffic_bn += itraffic_bn_today;
754 otraffic_dcc += otraffic_dcc_today;
755 itraffic_dcc += itraffic_dcc_today;
756 otraffic_unknown += otraffic_unknown_today;
757 itraffic_unknown += itraffic_unknown_today;
758 otraffic_trans += otraffic_trans_today;
759 itraffic_trans += itraffic_trans_today;
760 otraffic_irc_today = otraffic_bn_today = 0;
761 otraffic_dcc_today = otraffic_unknown_today = 0;
762 itraffic_irc_today = itraffic_bn_today = 0;
763 itraffic_dcc_today = itraffic_unknown_today = 0;
764 itraffic_trans_today = otraffic_trans_today = 0;
765 }
766
event_loaded()767 static void event_loaded()
768 {
769 check_tcl_event("loaded");
770 }
771
772 void kill_tcl();
773 extern module_entry *module_list;
774 void restart_chons();
775
776 #ifdef STATIC
777 void check_static(char *, char *(*)());
778
779 #include "mod/static.h"
780 #endif
781 int init_threaddata(int);
782 int init_mem();
783 int init_userent();
784 int init_misc();
785 int init_bots();
786 int init_modules();
787 int init_tcl(int, char **);
788 int init_language(int);
789 #ifdef TLS
790 int ssl_init();
791 #endif
792
garbage_collect(void)793 static void garbage_collect(void)
794 {
795 static uint8_t run_cnt = 0;
796
797 if (run_cnt == 3)
798 garbage_collect_tclhash();
799 else
800 run_cnt++;
801 }
802
mainloop(int toplevel)803 int mainloop(int toplevel)
804 {
805 static int socket_cleanup = 0;
806 int xx, i, eggbusy = 1, tclbusy = 0;
807 char buf[8702];
808
809 /* Lets move some of this here, reducing the number of actual
810 * calls to periodic_timers
811 */
812 now = time(NULL);
813
814 /* If we want to restart, we have to unwind to the toplevel.
815 * Tcl will Panic if we kill the interp with Tcl_Eval in progress.
816 * This is done by returning -1 in tickle_WaitForEvent.
817 */
818 if (do_restart && do_restart != -2 && !toplevel)
819 return -1;
820
821 /* Once a second */
822 if (now != then) {
823 call_hook(HOOK_SECONDLY);
824 then = now;
825 }
826
827 /* Only do this every so often. */
828 if (!socket_cleanup) {
829 socket_cleanup = 5;
830
831 /* Remove dead dcc entries. */
832 dcc_remove_lost();
833
834 /* Check for server or dcc activity. */
835 dequeue_sockets();
836 } else
837 socket_cleanup--;
838
839 /* Free unused structures. */
840 garbage_collect();
841
842 xx = sockgets(buf, &i);
843 if (xx >= 0) { /* Non-error */
844 int idx;
845
846 for (idx = 0; idx < dcc_total; idx++)
847 if (dcc[idx].sock == xx) {
848 if (dcc[idx].type && dcc[idx].type->activity) {
849 /* Traffic stats */
850 if (dcc[idx].type->name) {
851 if (!strncmp(dcc[idx].type->name, "BOT", 3))
852 itraffic_bn_today += strlen(buf) + 1;
853 else if (!strcmp(dcc[idx].type->name, "SERVER"))
854 itraffic_irc_today += strlen(buf) + 1;
855 else if (!strncmp(dcc[idx].type->name, "CHAT", 4))
856 itraffic_dcc_today += strlen(buf) + 1;
857 else if (!strncmp(dcc[idx].type->name, "FILES", 5))
858 itraffic_dcc_today += strlen(buf) + 1;
859 else if (!strcmp(dcc[idx].type->name, "SEND"))
860 itraffic_trans_today += strlen(buf) + 1;
861 else if (!strcmp(dcc[idx].type->name, "FORK_SEND"))
862 itraffic_trans_today += strlen(buf) + 1;
863 else if (!strncmp(dcc[idx].type->name, "GET", 3))
864 itraffic_trans_today += strlen(buf) + 1;
865 else
866 itraffic_unknown_today += strlen(buf) + 1;
867 }
868 dcc[idx].type->activity(idx, buf, i);
869 } else
870 putlog(LOG_MISC, "*", "ERROR: untrapped dcc activity: type %s, sock %d",
871 dcc[idx].type ? dcc[idx].type->name : "UNKNOWN", dcc[idx].sock);
872 break;
873 }
874 } else if (xx == -1) { /* EOF from someone */
875 int idx;
876
877 if (i == STDOUT && !backgrd)
878 fatal("END OF FILE ON TERMINAL", 0);
879 for (idx = 0; idx < dcc_total; idx++)
880 if (dcc[idx].sock == i) {
881 if (dcc[idx].type && dcc[idx].type->eof)
882 dcc[idx].type->eof(idx);
883 else {
884 putlog(LOG_MISC, "*",
885 "*** ATTENTION: DEAD SOCKET (%d) OF TYPE %s UNTRAPPED",
886 i, dcc[idx].type ? dcc[idx].type->name : "*UNKNOWN*");
887 killsock(i);
888 lostdcc(idx);
889 }
890 idx = dcc_total + 1;
891 }
892 if (idx == dcc_total) {
893 putlog(LOG_MISC, "*",
894 "(@) EOF socket %d, not a dcc socket, not anything.", i);
895 close(i);
896 killsock(i);
897 }
898 } else if (xx == -2 && errno != EINTR) { /* select() error */
899 putlog(LOG_MISC, "*", "* Socket error #%d; recovering.", errno);
900 for (i = 0; i < dcc_total; i++) {
901 if ((fcntl(dcc[i].sock, F_GETFD, 0) == -1) && (errno == EBADF)) {
902 putlog(LOG_MISC, "*",
903 "DCC socket %d (type %d, name '%s') expired -- pfft",
904 dcc[i].sock, dcc[i].type, dcc[i].nick);
905 killsock(dcc[i].sock);
906 lostdcc(i);
907 i--;
908 }
909 }
910 } else if (xx == -3) {
911 call_hook(HOOK_IDLE);
912 socket_cleanup = 0; /* If we've been idle, cleanup & flush */
913 eggbusy = 0;
914 } else if (xx == -5) {
915 eggbusy = 0;
916 tclbusy = 1;
917 }
918
919 if (do_restart) {
920 if (do_restart == -2)
921 rehash();
922 else if (!toplevel)
923 return -1; /* Unwind to toplevel before restarting */
924 else {
925 /* Unload as many modules as possible */
926 int f = 1;
927 module_entry *p;
928 Function startfunc;
929 char name[256];
930
931 /* oops, I guess we should call this event before tcl is restarted */
932 check_tcl_event("prerestart");
933
934 while (f) {
935 f = 0;
936 for (p = module_list; p != NULL; p = p->next) {
937 dependancy *d = dependancy_list;
938 int ok = 1;
939
940 while (ok && d) {
941 if (d->needed == p)
942 ok = 0;
943 d = d->next;
944 }
945 if (ok) {
946 strlcpy(name, p->name, sizeof name);
947 if (module_unload(name, botnetnick) == NULL) {
948 f = 1;
949 break;
950 }
951 }
952 }
953 }
954
955 /* Make sure we don't have any modules left hanging around other than
956 * "eggdrop" and the 3 that are supposed to be.
957 */
958 for (f = 0, p = module_list; p; p = p->next) {
959 if (strcmp(p->name, "eggdrop") && strcmp(p->name, "encryption") &&
960 strcmp(p->name, "encryption2") && strcmp(p->name, "uptime")) {
961 f++;
962 debug1("stagnant module %s", p->name);
963 }
964 }
965 if (f != 0) {
966 putlog(LOG_MISC, "*", MOD_STAGNANT);
967 }
968
969 flushlogs();
970 kill_tcl();
971 init_tcl(argc, argv);
972 init_language(0);
973
974 /* this resets our modules which we didn't unload (encryption and uptime) */
975 for (p = module_list; p; p = p->next) {
976 if (p->funcs) {
977 startfunc = p->funcs[MODCALL_START];
978 startfunc(NULL);
979 }
980 }
981
982 rehash();
983 #ifdef TLS
984 ssl_cleanup();
985 ssl_init();
986 #endif
987 restart_chons();
988 call_hook(HOOK_LOADED);
989 }
990 eggbusy = 1;
991 do_restart = 0;
992 }
993
994 if (!eggbusy) {
995 /* Process all pending tcl events */
996 # ifdef REPLACE_NOTIFIER
997 if (Tcl_ServiceAll())
998 tclbusy = 1;
999 # else
1000 while (Tcl_DoOneEvent(TCL_DONT_WAIT | TCL_ALL_EVENTS))
1001 tclbusy = 1;
1002 # endif /* REPLACE_NOTIFIER */
1003 }
1004
1005 return (eggbusy || tclbusy);
1006 }
1007
init_random(void)1008 static void init_random(void) {
1009 unsigned int seed;
1010 #ifdef HAVE_GETRANDOM
1011 if (getrandom(&seed, sizeof(seed), 0) != sizeof(seed)) {
1012 if (errno != ENOSYS) {
1013 fatal("ERROR: getrandom()\n", 0);
1014 } else {
1015 /* getrandom() is available in header but syscall is not!
1016 * This can happen with glibc>=2.25 and linux<3.17
1017 */
1018 #endif
1019 struct timeval tp;
1020 gettimeofday(&tp, NULL);
1021 seed = (((int64_t) tp.tv_sec * tp.tv_usec)) ^ getpid();
1022 #ifdef HAVE_GETRANDOM
1023 }
1024 }
1025 #endif
1026 srandom(seed);
1027 }
1028
main(int arg_c,char ** arg_v)1029 int main(int arg_c, char **arg_v)
1030 {
1031 int i, xx;
1032 char s[25];
1033 FILE *f;
1034 struct sigaction sv;
1035 struct chanset_t *chan;
1036 #ifdef DEBUG
1037 struct rlimit cdlim;
1038 #endif
1039 #ifdef STOP_UAC
1040 int nvpair[2];
1041 #endif
1042
1043 /* Make sure it can write core, if you make debug. Else it's pretty
1044 * useless (dw)
1045 *
1046 * Only allow unlimited size core files when compiled with DEBUG defined.
1047 * This is not a good idea for normal builds -- in these cases, use the
1048 * default system resource limits instead.
1049 */
1050 #ifdef DEBUG
1051 cdlim.rlim_cur = RLIM_INFINITY;
1052 cdlim.rlim_max = RLIM_INFINITY;
1053 setrlimit(RLIMIT_CORE, &cdlim);
1054 #endif
1055
1056 #ifdef DEBUG_CONTEXT
1057 /* Initialise context list */
1058 for (i = 0; i < 16; i++)
1059 Context;
1060 #endif
1061
1062 argc = arg_c;
1063 argv = arg_v;
1064
1065 /* Version info! */
1066 #ifdef EGG_PATCH
1067 egg_snprintf(egg_version, sizeof egg_version, "%s+%s %u", EGG_STRINGVER, EGG_PATCH, egg_numver);
1068 egg_snprintf(ver, sizeof ver, "eggdrop v%s+%s", EGG_STRINGVER, EGG_PATCH);
1069 egg_snprintf(version, sizeof version,
1070 "Eggdrop v%s+%s (C) 1997 Robey Pointer (C) 1999-2021 Eggheads",
1071 EGG_STRINGVER, EGG_PATCH);
1072 #else
1073 egg_snprintf(egg_version, sizeof egg_version, "%s %u", EGG_STRINGVER, egg_numver);
1074 egg_snprintf(ver, sizeof ver, "eggdrop v%s", EGG_STRINGVER);
1075 egg_snprintf(version, sizeof version,
1076 "Eggdrop v%s (C) 1997 Robey Pointer (C) 1999-2021 Eggheads",
1077 EGG_STRINGVER);
1078 #endif
1079
1080 /* For OSF/1 */
1081 #ifdef STOP_UAC
1082 /* Don't print "unaligned access fixup" warning to the user */
1083 nvpair[0] = SSIN_UACPROC;
1084 nvpair[1] = UAC_NOPRINT;
1085 setsysinfo(SSI_NVPAIRS, (char *) nvpair, 1, NULL, 0);
1086 #endif
1087
1088 /* Set up error traps: */
1089 sv.sa_handler = got_bus;
1090 sigemptyset(&sv.sa_mask);
1091 #ifdef SA_RESETHAND
1092 sv.sa_flags = SA_RESETHAND;
1093 #else
1094 sv.sa_flags = 0;
1095 #endif
1096 sigaction(SIGBUS, &sv, NULL);
1097 sv.sa_handler = got_segv;
1098 sigaction(SIGSEGV, &sv, NULL);
1099 #ifdef SA_RESETHAND
1100 sv.sa_flags = 0;
1101 #endif
1102 sv.sa_handler = got_fpe;
1103 sigaction(SIGFPE, &sv, NULL);
1104 sv.sa_handler = got_term;
1105 sigaction(SIGTERM, &sv, NULL);
1106 sv.sa_handler = got_hup;
1107 sigaction(SIGHUP, &sv, NULL);
1108 sv.sa_handler = got_quit;
1109 sigaction(SIGQUIT, &sv, NULL);
1110 sv.sa_handler = SIG_IGN;
1111 sigaction(SIGPIPE, &sv, NULL);
1112 sv.sa_handler = got_ill;
1113 sigaction(SIGILL, &sv, NULL);
1114 sv.sa_handler = got_alarm;
1115 sigaction(SIGALRM, &sv, NULL);
1116
1117 /* Initialize variables and stuff */
1118 now = time(NULL);
1119 chanset = NULL;
1120 lastmin = now / 60;
1121 init_random();
1122 init_mem();
1123 if (argc > 1)
1124 do_arg();
1125 init_language(1);
1126
1127 printf("\n%s\n", version);
1128
1129 #ifndef CYGWIN_HACKS
1130 /* Don't allow eggdrop to run as root
1131 * This check isn't useful under cygwin and has been
1132 * reported to cause trouble in some situations.
1133 */
1134 if (((int) getuid() == 0) || ((int) geteuid() == 0))
1135 fatal("ERROR: Eggdrop will not run as root!", 0);
1136 #endif
1137
1138 #ifndef REPLACE_NOTIFIER
1139 init_threaddata(1);
1140 #endif
1141 init_userent();
1142 init_misc();
1143 init_bots();
1144 init_modules();
1145 if (backgrd)
1146 bg_prepare_split();
1147 init_tcl(argc, argv);
1148 init_language(0);
1149 #ifdef STATIC
1150 link_statics();
1151 #endif
1152 strlcpy(s, ctime(&now), sizeof s);
1153 memmove(&s[11], &s[20], strlen(&s[20]) + 1);
1154 putlog(LOG_ALL, "*", "--- Loading %s (%s)", ver, s);
1155 chanprog();
1156 if (!encrypt_pass2 && !encrypt_pass) {
1157 printf("%s", MOD_NOCRYPT);
1158 bg_send_quit(BG_ABORT);
1159 exit(1);
1160 }
1161 i = 0;
1162 for (chan = chanset; chan; chan = chan->next)
1163 i++;
1164 putlog(LOG_MISC, "*", "=== %s: %d channels, %d users.",
1165 botnetnick, i, count_users(userlist));
1166 if ((cliflags & CLI_N) && (cliflags & CLI_T)) {
1167 printf("\n");
1168 printf("NOTE: The -n flag is no longer used, it is as effective as Han\n");
1169 printf(" without Chewie\n");
1170 }
1171 #ifdef TLS
1172 ssl_init();
1173 #endif
1174 cache_miss = 0;
1175 cache_hit = 0;
1176 if (!pid_file[0])
1177 egg_snprintf(pid_file, sizeof pid_file, "pid.%s", botnetnick);
1178
1179 /* Check for pre-existing eggdrop! */
1180 f = fopen(pid_file, "r");
1181 if (f != NULL) {
1182 if (fgets(s, 10, f) != NULL) {
1183 xx = atoi(s);
1184 i = kill(xx, SIGCHLD); /* Meaningless kill to determine if pid
1185 * is used */
1186 if (i == 0 || errno != ESRCH) {
1187 printf(EGG_RUNNING1, botnetnick);
1188 printf(EGG_RUNNING2, pid_file);
1189 bg_send_quit(BG_ABORT);
1190 exit(1);
1191 }
1192 } else {
1193 printf("Error checking for existing Eggdrop process.\n");
1194 }
1195 fclose(f);
1196 }
1197
1198 /* Move into background? */
1199 if (backgrd) {
1200 bg_do_split();
1201 } else { /* !backgrd */
1202 xx = getpid();
1203 if (xx != 0) {
1204 FILE *fp;
1205
1206 /* Write pid to file */
1207 unlink(pid_file);
1208 fp = fopen(pid_file, "w");
1209 if (fp != NULL) {
1210 fprintf(fp, "%u\n", xx);
1211 if (fflush(fp)) {
1212 /* Let the bot live since this doesn't appear to be a botchk */
1213 printf(EGG_NOWRITE, pid_file);
1214 fclose(fp);
1215 unlink(pid_file);
1216 } else
1217 fclose(fp);
1218 } else
1219 printf(EGG_NOWRITE, pid_file);
1220 }
1221 }
1222
1223 use_stderr = 0; /* Stop writing to stderr now */
1224 if (backgrd) {
1225 /* Ok, try to disassociate from controlling terminal (finger cross) */
1226 setpgid(0, 0);
1227 /* Tcl wants the stdin, stdout and stderr file handles kept open. */
1228 if (freopen("/dev/null", "r", stdin) == NULL) {
1229 putlog(LOG_MISC, "*", "Error renaming stdin file handle: %s", strerror(errno));
1230 }
1231 if (freopen("/dev/null", "w", stdout) == NULL) {
1232 putlog(LOG_MISC, "*", "Error renaming stdout file handle: %s", strerror(errno));
1233 }
1234 if (freopen("/dev/null", "w", stderr) == NULL) {
1235 putlog(LOG_MISC, "*", "Error renaming stderr file handle: %s", strerror(errno));
1236 }
1237 #ifdef CYGWIN_HACKS
1238 FreeConsole();
1239 #endif
1240 }
1241
1242 /* Terminal emulating dcc chat */
1243 if (!backgrd && term_z >= 0) {
1244 /* reuse term_z as glob var to pass it's index in the dcc table around */
1245 term_z = new_dcc(&DCC_CHAT, sizeof(struct chat_info));
1246
1247 /* new_dcc returns -1 on error */
1248 if (term_z < 0)
1249 fatal("ERROR: Failed to initialize foreground chat.", 0);
1250
1251 getvhost(&dcc[term_z].sockname, AF_INET);
1252 dcc[term_z].sock = STDOUT;
1253 dcc[term_z].timeval = now;
1254 dcc[term_z].u.chat->con_flags = conmask | EGG_BG_CONMASK;
1255 dcc[term_z].u.chat->strip_flags = STRIP_ALL;
1256 dcc[term_z].status = STAT_ECHO;
1257 strcpy(dcc[term_z].nick, EGG_BG_HANDLE);
1258 strcpy(dcc[term_z].host, "llama@console");
1259 add_hq_user();
1260 setsock(STDOUT, 0); /* Entry in net table */
1261 dprintf(term_z, "\n### ENTERING DCC CHAT SIMULATION ###\n");
1262 dprintf(term_z, "You can use the .su command to log into your Eggdrop account.\n\n");
1263 dcc_chatter(term_z);
1264 }
1265
1266 /* -1 to make mainloop() call
1267 * call_hook(HOOK_SECONDLY)->server_secondly()->connect_server() before first
1268 * sockgets()->sockread()->select() to avoid an unnecessary select timeout of
1269 * up to 1 sec while starting up
1270 */
1271 then = now - 1;
1272
1273 online_since = now;
1274 #ifdef EGG_TDNS
1275 dns_thread_head = nmalloc(sizeof(struct dns_thread_node));
1276 dns_thread_head->next = NULL;
1277 #endif
1278 autolink_cycle(NULL); /* Hurry and connect to tandem bots */
1279 add_help_reference("cmds1.help");
1280 add_help_reference("cmds2.help");
1281 add_help_reference("core.help");
1282 add_hook(HOOK_SECONDLY, (Function) core_secondly);
1283 add_hook(HOOK_MINUTELY, (Function) core_minutely);
1284 add_hook(HOOK_HOURLY, (Function) core_hourly);
1285 add_hook(HOOK_REHASH, (Function) event_rehash);
1286 add_hook(HOOK_PRE_REHASH, (Function) event_prerehash);
1287 add_hook(HOOK_USERFILE, (Function) event_save);
1288 add_hook(HOOK_BACKUP, (Function) backup_userfile);
1289 add_hook(HOOK_DAILY, (Function) event_logfile);
1290 add_hook(HOOK_DAILY, (Function) event_resettraffic);
1291 add_hook(HOOK_LOADED, (Function) event_loaded);
1292
1293 call_hook(HOOK_LOADED);
1294
1295 debug0("main: entering loop");
1296 while (1) {
1297 mainloop(1);
1298 }
1299 }
1300