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