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