1 #ifdef RCS
2 static char rcsid[]="$Id: dancer.c,v 1.2 2000/11/13 18:16:33 holsta Exp $";
3 #endif
4 /******************************************************************************
5 * Internetting Cooperating Programmers
6 * ----------------------------------------------------------------------------
7 *
8 * ____ PROJECT
9 * | _ \ __ _ _ __ ___ ___ _ __
10 * | | | |/ _` | '_ \ / __/ _ \ '__|
11 * | |_| | (_| | | | | (_| __/ |
12 * |____/ \__,_|_| |_|\___\___|_| the IRC bot
13 *
14 * All files in this archive are subject to the GNU General Public License.
15 *
16 * $Source: /cvsroot/dancer/dancer/src/dancer.c,v $
17 * $Revision: 1.2 $
18 * $Date: 2000/11/13 18:16:33 $
19 * $Author: holsta $
20 * $State: Exp $
21 * $Locker: $
22 *
23 * ---------------------------------------------------------------------------
24 *****************************************************************************/
25
26 #include <stdlib.h>
27 #include <stdarg.h>
28 #include <signal.h>
29 #include <unistd.h>
30 #include <sys/types.h>
31 #include <fcntl.h> /* open() the log for StackTrace() */
32
33 #include "dancer.h"
34
35 #ifdef HAVE_PWD_H
36 # include <pwd.h>
37 #endif
38
39 #include "trio.h"
40 #include "strio.h"
41 #include "list.h"
42 #include "setup.h"
43 #include "server.h"
44 #include "netstuff.h"
45 #include "function.h"
46 #include "user.h"
47 #include "explain.h"
48 #include "seen.h"
49 #include "country.h"
50 #include "convert.h"
51 #include "bans.h"
52 #include "transfer.h"
53 #include "parse.h"
54 #include "link.h"
55 #include "servfunc.h"
56 #include "regex.h"
57 #include "tell.h"
58 #include "news.h"
59 #include "files.h"
60 #include "fplrun.h"
61
62 #ifdef PROFILING
63 extern etext();
64 #endif
65
66 /* --- Global ----------------------------------------------------- */
67
68 extern time_t now;
69
70 extern char channel[];
71 extern char nickname[], realname[], myhost[];
72 extern char userfile[], logfile[];
73 extern bool autoop;
74 extern bool talkative;
75 extern bool masterflood;
76 extern bool loopservers;
77 extern int numretry;
78 extern int netburp;
79 extern int pendingpings;
80 extern time_t lastpost;
81 extern time_t commontime;
82 extern struct timeval pongval;
83 extern itemclient *client;
84 extern itemserv *servHead;
85
86 char shutdownby[NICKLEN+1];
87 char shutdownreason[MIDBUFFER];
88 char servername[MIDBUFFER];
89 char serverpasswd[MINIBUFFER] = "";
90 int serverport;
91 int reload_configuration = 0;
92
93 bool connected = FALSE; /* Are we connected? */
94 bool cleanup = FALSE;
95 bool restart = TRUE; /* Restart or try next server? */
96 bool shuttingdown = FALSE;
97 bool nickflag = TRUE;
98
99 int serverSocket = -1;
100 int retry = 0;
101
102 time_t uptime = 0;
103 time_t lastevent = 0;
104 time_t mastertime = 0;
105 time_t shutdowntime = 0;
106
107 itemserv *currentserv;
108
109
110 /* --- AlarmCheck ------------------------------------------------- */
111
112 /*
113 * SIGALRM, StackTrace(), LogSnaphots() and _exit() in 10 minutes
114 * after last alarm() unless we manage to respond in time.
115 * Only to trace down the busy loops that still exist.
116 */
117
AlarmCheck(void)118 static void AlarmCheck(void)
119 {
120 #if defined(DEBUG) && defined(SIGALRM)
121 static time_t lastalarm = 0;
122 time_t current_time;
123
124 snapshot;
125 current_time = time(NULL);
126 if ((current_time - lastalarm) > 2*SECINMIN) {
127 lastalarm = current_time;
128 alarm(10*SECINMIN);
129 }
130 #endif
131 }
132
133 /* --- SloppyEvents ----------------------------------------------- */
134
135 time_t lastserverscan = 0;
136 time_t lastfilesave = 0;
137
SloppyEvents(void)138 void SloppyEvents(void) /* Events where timing isn't critical */
139 {
140 extern time_t lastservertime;
141
142 snapshot;
143 AlarmCheck();
144
145 if (masterflood && ((now - mastertime) > netburp))
146 masterflood = FALSE;
147
148 if ((now - lastserverscan) > 15*SECINMIN) {
149 ScanSplitServers(FALSE);
150 lastserverscan = now;
151 }
152
153 if (connected) {
154 if (!pendingpings && ((now - pongval.tv_sec) > PINGDELAY))
155 PingServer();
156
157 if (reload_configuration) {
158 extern char expfile[];
159 extern char funcfile[];
160
161 snapshot;
162 Log(LOG, "SIGHUP received, attempting to reload configuration.");
163 UserReload(userfile);
164 ExplainReload(expfile);
165 FuncReload(funcfile);
166 reload_configuration = 0;
167 }
168
169 if (((now - pongval.tv_sec) > 20*SECINMIN) &&
170 ((now - lastservertime) > 20*SECINMIN)) {
171 /* X minutes since last server-PONG and
172 Y minutes since last line was received */
173 Debug("Lost contact with server");
174 restart = cleanup = TRUE;
175 }
176
177 if (autoop)
178 CheckForAutoOp();
179 }
180 else {
181 /* Try to connect to server for two minutes max */
182 if ((now - lastevent) > 2*SECINMIN) {
183 if (retry < numretry) {
184 sleep(++retry);
185 cleanup = restart = TRUE;
186 }
187 else {
188 Debug("Timeout while trying to connect to server");
189 restart = FALSE;
190 cleanup = TRUE;
191 }
192 }
193 }
194
195 /* Periodically save important files */
196 if ((now - lastfilesave) > 2*SECINHOUR) {
197 SeenSave();
198 /*TellSave();*/
199 BanSave();
200 WarnSave();
201 UserSave();
202 lastfilesave = now;
203 }
204 }
205
206 /* --- TimeEvents ------------------------------------------------- */
207
208 time_t lastbantimeout = 0;
209
TimeEvents(void)210 void TimeEvents(void) /* Events where timing is critical */
211 {
212 static int count = 0;
213
214 snapshot;
215 CheckLinkQueue();
216 MessageQueue();
217
218 /* Shutdown */
219 if (shuttingdown && (now >= shutdowntime))
220 Quit(shutdownby, shutdownreason);
221
222 if ((now - lastbantimeout) > 5) {
223 BanTimeout();
224 lastbantimeout = now;
225 }
226
227 fTimerCheck();
228
229 if (0 == count)
230 SloppyEvents();
231
232 if (++count >= TIMERCHK)
233 count = 0;
234 }
235
236 /* --- Hello ------------------------------------------------------ */
237
Hello(itemserv * s)238 void Hello(itemserv *s) /* Introduce ourselves to the server */
239 {
240 char *uname;
241 #ifdef HAVE_PWD_H
242 struct passwd *pw;
243 #endif
244
245 snapshot;
246 connected = FALSE;
247
248 /* Recommended order is PASS, NICK, then USER */
249 if (serverpasswd[0])
250 WriteServer("PASS %s", serverpasswd);
251 NewNick();
252 WriteServer("NICK %s", nickname);
253 #ifdef HAVE_PWD_H
254 pw = getpwuid(getuid());
255 if (pw)
256 uname = pw->pw_name;
257 else /* getenv() below */
258 #endif
259 uname = getenv("LOGNAME");
260
261 #if defined(OVERRIDE_USER) || defined(__CYGWIN32__)
262 if (getenv("BOTUSER"))
263 uname = getenv("BOTUSER");
264 #endif
265
266 /* Client cannot provide host and server, but we're just being nice */
267 WriteServer("USER %s %s %s :%s", (uname ? uname : "bot"),
268 myhost, servername, (realname[0] ? realname : "An IRC bot"));
269
270 if (NULL == s)
271 s = FindServ(servername, serverport);
272
273 ConnectServ(s); /* Report "we're connected" */
274 }
275
276 /* --- Start ------------------------------------------------------ */
277
278 /* Returns whether the next server should be tried */
279
Start(itemserv * s)280 bool Start(itemserv *s)
281 {
282 snapshot;
283 StrCopyMax(servername, sizeof(servername), s->s.name);
284 StrCopyMax(serverpasswd, sizeof(serverpasswd),
285 s->s.passwd ? s->s.passwd : "");
286 serverport = s->s.port;
287
288 #if 0
289 /* Check valid chars in RFC952 */
290 switch (StrScan(server, "%[a-zA-Z0-9.-]%*[ :]%d%*[ :]%[^\n]",
291 servername, &serverport, serverpasswd)) {
292 case 0:
293 return TRUE; /* No server, let's try the next */
294 case 1:
295 serverport = IRCPORT;
296 *serverpasswd = NIL;
297 break;
298 case 2:
299 *serverpasswd = NIL;
300 break;
301 default:
302 break;
303 }
304 #endif
305
306 serverSocket = OpenServerConnection(servername, serverport);
307 if (serverSocket != -1) {
308 lastevent = time(NULL);
309 Logf(LOG, "Connected to %s on port %d", servername, serverport);
310 Logf(LOG, "Logfile for %s started", channel);
311 Hello(s);
312 EventLoop();
313
314 return !restart;
315 }
316
317 return TRUE;
318 }
319
320 /* --- SignalGetName ---------------------------------------------- */
321
SignalGetName(int sig)322 const char *SignalGetName(int sig)
323 {
324 switch (sig) {
325 #ifdef SIGALRM
326 case SIGALRM:
327 return "SIGALRM";
328 #endif
329 #ifdef SIGBUS
330 case SIGBUS:
331 return "SIGBUS";
332 #endif
333 #ifdef SIGFPE
334 case SIGFPE:
335 return "SIGFPE";
336 #endif
337 #ifdef SIGILL
338 case SIGILL:
339 return "SIGILL";
340 #endif
341 #ifdef SIGINT
342 case SIGINT:
343 return "SIGINT";
344 #endif
345 #ifdef SIGKILL
346 case SIGKILL:
347 return "SIGKILL";
348 #endif
349 #ifdef SIGPIPE
350 case SIGPIPE:
351 return "SIGPIPE";
352 #endif
353 #ifdef SIGQUIT
354 case SIGQUIT:
355 return "SIGQUIT";
356 #endif
357 #ifdef SIGSEGV
358 case SIGSEGV:
359 return "SIGSEGV";
360 #endif
361 #ifdef SIGTERM
362 case SIGTERM:
363 return "SIGTERM";
364 #endif
365 default:
366 return "UNKNOWN";
367 }
368 }
369
370 /* --- SignalCrashHandler ----------------------------------------- */
371
372 /* StackTrace() doesn't seem to work with my gdb -gr8ron */
373 #if defined(DEBUG) && !defined(AMIGA)
374 #include "stacktrace.c"
375 #endif
376
SignalCrashHandler(int sig)377 void SignalCrashHandler(int sig)
378 {
379 #if defined(DEBUG)
380 #if !defined(AMIGA)
381 int fd = -1;
382
383 if (logfile[0]) {
384 fd = open(logfile, O_WRONLY | O_APPEND | O_CREAT, 0600);
385 }
386
387 if (0 <= fd) {
388 globalOutFD = fd;
389 }
390
391 StackTrace();
392
393 if (0 <= fd) {
394 close(fd);
395 }
396 #endif /* !AMIGA */
397 LogSnapshots();
398 #endif /* DEBUG */
399
400 Debug("Received FATAL signal %d (%s), crashing", sig, SignalGetName(sig));
401
402 signal(sig, SIG_DFL);
403 _exit(1);
404 }
405
406 /* --- SignalTerminateHandler ------------------------------------- */
407
SignalTerminateHandler(int sig)408 void SignalTerminateHandler(int sig)
409 {
410 Debug("Received signal %d (%s), terminating", sig, SignalGetName(sig));
411
412 SeenInsertAll(SEENQUITED, NULL, NULL);
413 restart = FALSE;
414 cleanup = TRUE;
415
416 /* Install default signal handler to avoid race conditions */
417 signal(sig, SIG_DFL);
418 }
419
420 /* --- SignalPipeHandler ------------------------------------------ */
421
SignalPipeHandler(int sig)422 void SignalPipeHandler(int sig)
423 {
424 if (!Alive(serverSocket)) {
425 Debug("Received signal %d (SIGPIPE)", sig);
426 restart = FALSE;
427 cleanup = TRUE;
428 signal(sig, SIG_DFL);
429 }
430
431 if (client) {
432 Debug("Broken pipe from %s", client->ident->userdomain);
433 RemoveClient(client);
434 client = NULL;
435 signal(sig, SignalPipeHandler);
436 }
437 }
438
439 /* --- SignalQueReload ------------------------------------------- */
440
SignalQueReload(int sig)441 void SignalQueReload(int sig)
442 {
443 reload_configuration = 1;
444 }
445
446 /* --- SignalInit ------------------------------------------------ */
447
SignalInit(void)448 void SignalInit(void)
449 {
450 snapshot;
451
452 /* Catch SIGHUP and reload config */
453 #ifdef SIGHUP
454 signal(SIGHUP, SignalQueReload);
455 #endif
456
457 /* Crash */
458 #ifdef SIGALRM
459 signal(SIGALRM, SignalCrashHandler); /* alarm(2) */
460 #endif
461 #ifdef SIGBUS
462 signal(SIGBUS, SignalCrashHandler); /* Bus error */
463 #endif
464 #ifdef SIGFPE
465 signal(SIGFPE, SignalCrashHandler); /* Floating point exception */
466 #endif
467 #ifdef SIGILL
468 signal(SIGILL, SignalCrashHandler); /* Illegal instruction */
469 #endif
470 #ifdef SIGSEGV
471 signal(SIGSEGV, SignalCrashHandler); /* Segmentation violation */
472 #endif
473
474 /* Terminate */
475 #ifdef SIGINT
476 signal(SIGINT, SignalTerminateHandler); /* CTRL-C */
477 #endif
478 #ifdef SIGKILL
479 signal(SIGKILL, SignalTerminateHandler); /* Kill (cannot be ignored) */
480 #endif
481 #ifdef SIGQUIT
482 signal(SIGQUIT, SignalTerminateHandler); /* CTRL-| */
483 #endif
484 #ifdef SIGTERM
485 signal(SIGTERM, SignalTerminateHandler); /* Software termination signal from kill */
486 #endif
487
488 /* Pipe */
489 #ifdef SIGPIPE
490 signal(SIGPIPE, SignalPipeHandler); /* Write on a pipe with no one to read it */
491 #endif
492
493 /* Ignore the rest */
494 #ifdef SIGABRT
495 signal(SIGABRT, SIG_IGN); /* abort(3) */
496 #endif
497 #ifdef SIGSTOP
498 signal(SIGSTOP, SIG_IGN); /* Sendable stop signal not from tty */
499 #endif
500 #ifdef SIGTSTP
501 signal(SIGTSTP, SIG_IGN); /* CTRL-Z (stop signal from tty) */
502 #endif
503 }
504
505 /* --- Cleanup ---------------------------------------------------- */
506
Cleanup(void)507 void Cleanup(void)
508 {
509 snapshot;
510 Log(LOG, "Cleanup");
511 DeleteGuests();
512 FlushMessages();
513 BanDisable();
514 if (restart) {
515 cleanup = FALSE;
516 NetCleanup();
517 }
518 else {
519 NetCleanup();
520 LinkQuit();
521 TellCleanup();
522 SeenCleanup();
523 ExplainCleanup();
524 UserCleanup();
525 ConfigCleanup();
526 BanCleanup();
527 KickCleanup();
528 WarnCleanup();
529 ServCleanup();
530 NewsCleanup();
531
532 #ifdef HAVE_LIBFPL
533 FPLCleanup();
534 #endif
535
536 Log(LOG, "Exit");
537 exit(0);
538 }
539 }
540
541 /* --- TimeInit --------------------------------------------------- */
542
TimeInit(void)543 void TimeInit(void)
544 {
545 snapshot;
546 uptime = lastevent = lastpost = lastserverscan = lastfilesave = \
547 lastbantimeout = commontime = now = time(NULL);
548 }
549
550 /* --- main ------------------------------------------------------- */
551
main(int argc,char * argv[])552 int main(int argc, char *argv[])
553 {
554 char buffer[MINIBUFFER];
555 bool configok;
556 itemserv *s;
557
558 snapshot;
559 #if defined(DEBUG) && !defined(AMIGA)
560 globalProgName = argv[0];
561 #endif
562
563 TimeInit();
564 SignalInit();
565
566 StrFormatMax(buffer, sizeof(buffer), "echo %d > .pid", (int)getpid());
567 system(buffer);
568
569 /* Any background process should be made nice */
570 nice(NICE);
571
572 #if defined(PROFILING)
573 monstartup(2, etext);
574 #endif
575
576 re_syntax_options |= RE_NO_BK_VBAR | RE_NO_BK_PARENS;
577
578 CommandInit(); /* Must be done prior to new command levels / ConfigInit() */
579 configok = ConfigInit();
580
581 if ((argc > 1) && StrEqualMax(argv[1], 4, "conf")) {
582 MakeConfig();
583 exit(1);
584 }
585
586 if (configok) {
587 UserInit();
588 if (UserLoad(userfile)) {
589 LogInit();
590 RandomInit(uptime);
591 NetInit();
592 SeenInit();
593 ExplainInit();
594 TellInit();
595 CountryInit();
596 TimeZoneInit();
597 BanInit();
598 KickInit();
599 WarnInit();
600 FuncInit();
601 #if 0
602 PatternInit();
603 #endif
604 ServInit();
605 NewsInit();
606
607 #ifdef HAVE_LIBFPL
608 FPLInit();
609 #endif
610
611 Log(LOG, "Started " VERSIONMSG );
612
613 snapshot;
614 do {
615 for (s = First(servHead); s && restart; s = Next(s)) {
616 if (s->s.flags & SERV_DISABLED) {
617 /* Server made disabled, please continue */
618 Logf(LOG, "Server %s is disabled", s->s.name);
619 continue;
620 }
621
622 retry = 0; /* Try enough number of times */
623
624 do {
625 AlarmCheck();
626 cleanup = FALSE;
627 if (!Start(s)) {
628 /* This means it exited properly. */
629 break;
630 }
631 if (!restart)
632 break;
633 if (++retry >= numretry) {
634 Logf(LOG, "Unable to connect to %s on port %d (tried %d times)",
635 servername, serverport, retry);
636 break;
637 }
638 /* try again! */
639 sleep(retry);
640 } while(1);
641
642 if (restart) {
643 /* We should loop, cleanup! */
644 Cleanup();
645 }
646 } /* Now, try next server or exit */
647
648 if (loopservers && restart) {
649 /* Lets see if there are any enabled servers before looping */
650 for (s = First(servHead); s; s = Next(s)) {
651 if (0 == (s->s.flags & SERV_DISABLED))
652 break;
653 }
654
655 if (s) {
656 static time_t lastloop = 0;
657
658 /* Lets try to avoid abusing the server(s) with too quick reconnections */
659 if ((now - lastloop) < 5*SECINMIN) {
660 /*
661 * We will get rid of all the sleep() calls once we have event driven
662 * code implemented, but until then this will have to do.
663 */
664 Debug("Sleeping for one minute - patience");
665 sleep(SECINMIN);
666 }
667
668 lastloop = now;
669 }
670 else {
671 restart = FALSE;
672 }
673 }
674 } while (loopservers && restart);
675
676 restart = FALSE;
677 cleanup = TRUE;
678 Cleanup();
679 }
680 else
681 Logf(LOG, "Userfile \"%s\" not found!", userfile);
682 }
683 else
684 Logf(LOG, CONFIGFILE " not found!");
685
686 restart = FALSE;
687 Cleanup();
688
689 return 0;
690 }
691