#ifdef RCS
static char rcsid[]="$Id: dancer.c,v 1.2 2000/11/13 18:16:33 holsta Exp $";
#endif
/******************************************************************************
* Internetting Cooperating Programmers
* ----------------------------------------------------------------------------
*
* ____ PROJECT
* | _ \ __ _ _ __ ___ ___ _ __
* | | | |/ _` | '_ \ / __/ _ \ '__|
* | |_| | (_| | | | | (_| __/ |
* |____/ \__,_|_| |_|\___\___|_| the IRC bot
*
* All files in this archive are subject to the GNU General Public License.
*
* $Source: /cvsroot/dancer/dancer/src/dancer.c,v $
* $Revision: 1.2 $
* $Date: 2000/11/13 18:16:33 $
* $Author: holsta $
* $State: Exp $
* $Locker: $
*
* ---------------------------------------------------------------------------
*****************************************************************************/
#include <stdlib.h>
#include <stdarg.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h> /* open() the log for StackTrace() */
#include "dancer.h"
#ifdef HAVE_PWD_H
# include <pwd.h>
#endif
#include "trio.h"
#include "strio.h"
#include "list.h"
#include "setup.h"
#include "server.h"
#include "netstuff.h"
#include "function.h"
#include "user.h"
#include "explain.h"
#include "seen.h"
#include "country.h"
#include "convert.h"
#include "bans.h"
#include "transfer.h"
#include "parse.h"
#include "link.h"
#include "servfunc.h"
#include "regex.h"
#include "tell.h"
#include "news.h"
#include "files.h"
#include "fplrun.h"
#ifdef PROFILING
extern etext();
#endif
/* --- Global ----------------------------------------------------- */
extern time_t now;
extern char channel[];
extern char nickname[], realname[], myhost[];
extern char userfile[], logfile[];
extern bool autoop;
extern bool talkative;
extern bool masterflood;
extern bool loopservers;
extern int numretry;
extern int netburp;
extern int pendingpings;
extern time_t lastpost;
extern time_t commontime;
extern struct timeval pongval;
extern itemclient *client;
extern itemserv *servHead;
char shutdownby[NICKLEN+1];
char shutdownreason[MIDBUFFER];
char servername[MIDBUFFER];
char serverpasswd[MINIBUFFER] = "";
int serverport;
int reload_configuration = 0;
bool connected = FALSE; /* Are we connected? */
bool cleanup = FALSE;
bool restart = TRUE; /* Restart or try next server? */
bool shuttingdown = FALSE;
bool nickflag = TRUE;
int serverSocket = -1;
int retry = 0;
time_t uptime = 0;
time_t lastevent = 0;
time_t mastertime = 0;
time_t shutdowntime = 0;
itemserv *currentserv;
/* --- AlarmCheck ------------------------------------------------- */
/*
* SIGALRM, StackTrace(), LogSnaphots() and _exit() in 10 minutes
* after last alarm() unless we manage to respond in time.
* Only to trace down the busy loops that still exist.
*/
static void AlarmCheck(void)
{
#if defined(DEBUG) && defined(SIGALRM)
static time_t lastalarm = 0;
time_t current_time;
snapshot;
current_time = time(NULL);
if ((current_time - lastalarm) > 2*SECINMIN) {
lastalarm = current_time;
alarm(10*SECINMIN);
}
#endif
}
/* --- SloppyEvents ----------------------------------------------- */
time_t lastserverscan = 0;
time_t lastfilesave = 0;
void SloppyEvents(void) /* Events where timing isn't critical */
{
extern time_t lastservertime;
snapshot;
AlarmCheck();
if (masterflood && ((now - mastertime) > netburp))
masterflood = FALSE;
if ((now - lastserverscan) > 15*SECINMIN) {
ScanSplitServers(FALSE);
lastserverscan = now;
}
if (connected) {
if (!pendingpings && ((now - pongval.tv_sec) > PINGDELAY))
PingServer();
if (reload_configuration) {
extern char expfile[];
extern char funcfile[];
snapshot;
Log(LOG, "SIGHUP received, attempting to reload configuration.");
UserReload(userfile);
ExplainReload(expfile);
FuncReload(funcfile);
reload_configuration = 0;
}
if (((now - pongval.tv_sec) > 20*SECINMIN) &&
((now - lastservertime) > 20*SECINMIN)) {
/* X minutes since last server-PONG and
Y minutes since last line was received */
Debug("Lost contact with server");
restart = cleanup = TRUE;
}
if (autoop)
CheckForAutoOp();
}
else {
/* Try to connect to server for two minutes max */
if ((now - lastevent) > 2*SECINMIN) {
if (retry < numretry) {
sleep(++retry);
cleanup = restart = TRUE;
}
else {
Debug("Timeout while trying to connect to server");
restart = FALSE;
cleanup = TRUE;
}
}
}
/* Periodically save important files */
if ((now - lastfilesave) > 2*SECINHOUR) {
SeenSave();
/*TellSave();*/
BanSave();
WarnSave();
UserSave();
lastfilesave = now;
}
}
/* --- TimeEvents ------------------------------------------------- */
time_t lastbantimeout = 0;
void TimeEvents(void) /* Events where timing is critical */
{
static int count = 0;
snapshot;
CheckLinkQueue();
MessageQueue();
/* Shutdown */
if (shuttingdown && (now >= shutdowntime))
Quit(shutdownby, shutdownreason);
if ((now - lastbantimeout) > 5) {
BanTimeout();
lastbantimeout = now;
}
fTimerCheck();
if (0 == count)
SloppyEvents();
if (++count >= TIMERCHK)
count = 0;
}
/* --- Hello ------------------------------------------------------ */
void Hello(itemserv *s) /* Introduce ourselves to the server */
{
char *uname;
#ifdef HAVE_PWD_H
struct passwd *pw;
#endif
snapshot;
connected = FALSE;
/* Recommended order is PASS, NICK, then USER */
if (serverpasswd[0])
WriteServer("PASS %s", serverpasswd);
NewNick();
WriteServer("NICK %s", nickname);
#ifdef HAVE_PWD_H
pw = getpwuid(getuid());
if (pw)
uname = pw->pw_name;
else /* getenv() below */
#endif
uname = getenv("LOGNAME");
#if defined(OVERRIDE_USER) || defined(__CYGWIN32__)
if (getenv("BOTUSER"))
uname = getenv("BOTUSER");
#endif
/* Client cannot provide host and server, but we're just being nice */
WriteServer("USER %s %s %s :%s", (uname ? uname : "bot"),
myhost, servername, (realname[0] ? realname : "An IRC bot"));
if (NULL == s)
s = FindServ(servername, serverport);
ConnectServ(s); /* Report "we're connected" */
}
/* --- Start ------------------------------------------------------ */
/* Returns whether the next server should be tried */
bool Start(itemserv *s)
{
snapshot;
StrCopyMax(servername, sizeof(servername), s->s.name);
StrCopyMax(serverpasswd, sizeof(serverpasswd),
s->s.passwd ? s->s.passwd : "");
serverport = s->s.port;
#if 0
/* Check valid chars in RFC952 */
switch (StrScan(server, "%[a-zA-Z0-9.-]%*[ :]%d%*[ :]%[^\n]",
servername, &serverport, serverpasswd)) {
case 0:
return TRUE; /* No server, let's try the next */
case 1:
serverport = IRCPORT;
*serverpasswd = NIL;
break;
case 2:
*serverpasswd = NIL;
break;
default:
break;
}
#endif
serverSocket = OpenServerConnection(servername, serverport);
if (serverSocket != -1) {
lastevent = time(NULL);
Logf(LOG, "Connected to %s on port %d", servername, serverport);
Logf(LOG, "Logfile for %s started", channel);
Hello(s);
EventLoop();
return !restart;
}
return TRUE;
}
/* --- SignalGetName ---------------------------------------------- */
const char *SignalGetName(int sig)
{
switch (sig) {
#ifdef SIGALRM
case SIGALRM:
return "SIGALRM";
#endif
#ifdef SIGBUS
case SIGBUS:
return "SIGBUS";
#endif
#ifdef SIGFPE
case SIGFPE:
return "SIGFPE";
#endif
#ifdef SIGILL
case SIGILL:
return "SIGILL";
#endif
#ifdef SIGINT
case SIGINT:
return "SIGINT";
#endif
#ifdef SIGKILL
case SIGKILL:
return "SIGKILL";
#endif
#ifdef SIGPIPE
case SIGPIPE:
return "SIGPIPE";
#endif
#ifdef SIGQUIT
case SIGQUIT:
return "SIGQUIT";
#endif
#ifdef SIGSEGV
case SIGSEGV:
return "SIGSEGV";
#endif
#ifdef SIGTERM
case SIGTERM:
return "SIGTERM";
#endif
default:
return "UNKNOWN";
}
}
/* --- SignalCrashHandler ----------------------------------------- */
/* StackTrace() doesn't seem to work with my gdb -gr8ron */
#if defined(DEBUG) && !defined(AMIGA)
#include "stacktrace.c"
#endif
void SignalCrashHandler(int sig)
{
#if defined(DEBUG)
#if !defined(AMIGA)
int fd = -1;
if (logfile[0]) {
fd = open(logfile, O_WRONLY | O_APPEND | O_CREAT, 0600);
}
if (0 <= fd) {
globalOutFD = fd;
}
StackTrace();
if (0 <= fd) {
close(fd);
}
#endif /* !AMIGA */
LogSnapshots();
#endif /* DEBUG */
Debug("Received FATAL signal %d (%s), crashing", sig, SignalGetName(sig));
signal(sig, SIG_DFL);
_exit(1);
}
/* --- SignalTerminateHandler ------------------------------------- */
void SignalTerminateHandler(int sig)
{
Debug("Received signal %d (%s), terminating", sig, SignalGetName(sig));
SeenInsertAll(SEENQUITED, NULL, NULL);
restart = FALSE;
cleanup = TRUE;
/* Install default signal handler to avoid race conditions */
signal(sig, SIG_DFL);
}
/* --- SignalPipeHandler ------------------------------------------ */
void SignalPipeHandler(int sig)
{
if (!Alive(serverSocket)) {
Debug("Received signal %d (SIGPIPE)", sig);
restart = FALSE;
cleanup = TRUE;
signal(sig, SIG_DFL);
}
if (client) {
Debug("Broken pipe from %s", client->ident->userdomain);
RemoveClient(client);
client = NULL;
signal(sig, SignalPipeHandler);
}
}
/* --- SignalQueReload ------------------------------------------- */
void SignalQueReload(int sig)
{
reload_configuration = 1;
}
/* --- SignalInit ------------------------------------------------ */
void SignalInit(void)
{
snapshot;
/* Catch SIGHUP and reload config */
#ifdef SIGHUP
signal(SIGHUP, SignalQueReload);
#endif
/* Crash */
#ifdef SIGALRM
signal(SIGALRM, SignalCrashHandler); /* alarm(2) */
#endif
#ifdef SIGBUS
signal(SIGBUS, SignalCrashHandler); /* Bus error */
#endif
#ifdef SIGFPE
signal(SIGFPE, SignalCrashHandler); /* Floating point exception */
#endif
#ifdef SIGILL
signal(SIGILL, SignalCrashHandler); /* Illegal instruction */
#endif
#ifdef SIGSEGV
signal(SIGSEGV, SignalCrashHandler); /* Segmentation violation */
#endif
/* Terminate */
#ifdef SIGINT
signal(SIGINT, SignalTerminateHandler); /* CTRL-C */
#endif
#ifdef SIGKILL
signal(SIGKILL, SignalTerminateHandler); /* Kill (cannot be ignored) */
#endif
#ifdef SIGQUIT
signal(SIGQUIT, SignalTerminateHandler); /* CTRL-| */
#endif
#ifdef SIGTERM
signal(SIGTERM, SignalTerminateHandler); /* Software termination signal from kill */
#endif
/* Pipe */
#ifdef SIGPIPE
signal(SIGPIPE, SignalPipeHandler); /* Write on a pipe with no one to read it */
#endif
/* Ignore the rest */
#ifdef SIGABRT
signal(SIGABRT, SIG_IGN); /* abort(3) */
#endif
#ifdef SIGSTOP
signal(SIGSTOP, SIG_IGN); /* Sendable stop signal not from tty */
#endif
#ifdef SIGTSTP
signal(SIGTSTP, SIG_IGN); /* CTRL-Z (stop signal from tty) */
#endif
}
/* --- Cleanup ---------------------------------------------------- */
void Cleanup(void)
{
snapshot;
Log(LOG, "Cleanup");
DeleteGuests();
FlushMessages();
BanDisable();
if (restart) {
cleanup = FALSE;
NetCleanup();
}
else {
NetCleanup();
LinkQuit();
TellCleanup();
SeenCleanup();
ExplainCleanup();
UserCleanup();
ConfigCleanup();
BanCleanup();
KickCleanup();
WarnCleanup();
ServCleanup();
NewsCleanup();
#ifdef HAVE_LIBFPL
FPLCleanup();
#endif
Log(LOG, "Exit");
exit(0);
}
}
/* --- TimeInit --------------------------------------------------- */
void TimeInit(void)
{
snapshot;
uptime = lastevent = lastpost = lastserverscan = lastfilesave = \
lastbantimeout = commontime = now = time(NULL);
}
/* --- main ------------------------------------------------------- */
int main(int argc, char *argv[])
{
char buffer[MINIBUFFER];
bool configok;
itemserv *s;
snapshot;
#if defined(DEBUG) && !defined(AMIGA)
globalProgName = argv[0];
#endif
TimeInit();
SignalInit();
StrFormatMax(buffer, sizeof(buffer), "echo %d > .pid", (int)getpid());
system(buffer);
/* Any background process should be made nice */
nice(NICE);
#if defined(PROFILING)
monstartup(2, etext);
#endif
re_syntax_options |= RE_NO_BK_VBAR | RE_NO_BK_PARENS;
CommandInit(); /* Must be done prior to new command levels / ConfigInit() */
configok = ConfigInit();
if ((argc > 1) && StrEqualMax(argv[1], 4, "conf")) {
MakeConfig();
exit(1);
}
if (configok) {
UserInit();
if (UserLoad(userfile)) {
LogInit();
RandomInit(uptime);
NetInit();
SeenInit();
ExplainInit();
TellInit();
CountryInit();
TimeZoneInit();
BanInit();
KickInit();
WarnInit();
FuncInit();
#if 0
PatternInit();
#endif
ServInit();
NewsInit();
#ifdef HAVE_LIBFPL
FPLInit();
#endif
Log(LOG, "Started " VERSIONMSG );
snapshot;
do {
for (s = First(servHead); s && restart; s = Next(s)) {
if (s->s.flags & SERV_DISABLED) {
/* Server made disabled, please continue */
Logf(LOG, "Server %s is disabled", s->s.name);
continue;
}
retry = 0; /* Try enough number of times */
do {
AlarmCheck();
cleanup = FALSE;
if (!Start(s)) {
/* This means it exited properly. */
break;
}
if (!restart)
break;
if (++retry >= numretry) {
Logf(LOG, "Unable to connect to %s on port %d (tried %d times)",
servername, serverport, retry);
break;
}
/* try again! */
sleep(retry);
} while(1);
if (restart) {
/* We should loop, cleanup! */
Cleanup();
}
} /* Now, try next server or exit */
if (loopservers && restart) {
/* Lets see if there are any enabled servers before looping */
for (s = First(servHead); s; s = Next(s)) {
if (0 == (s->s.flags & SERV_DISABLED))
break;
}
if (s) {
static time_t lastloop = 0;
/* Lets try to avoid abusing the server(s) with too quick reconnections */
if ((now - lastloop) < 5*SECINMIN) {
/*
* We will get rid of all the sleep() calls once we have event driven
* code implemented, but until then this will have to do.
*/
Debug("Sleeping for one minute - patience");
sleep(SECINMIN);
}
lastloop = now;
}
else {
restart = FALSE;
}
}
} while (loopservers && restart);
restart = FALSE;
cleanup = TRUE;
Cleanup();
}
else
Logf(LOG, "Userfile \"%s\" not found!", userfile);
}
else
Logf(LOG, CONFIGFILE " not found!");
restart = FALSE;
Cleanup();
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1