/* Services -- main source file.
*
* (C) 2003 Anope Team
* Contact us at info@anope.org
*
* Please read COPYING and README for furhter details.
*
* Based on the original code of Epona by Lara.
* Based on the original code of Services by Andy Church.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program (see the file COPYING); if not, write to the
* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: main.c 5 2004-03-29 01:29:50Z dane $
*
*/
#include "services.h"
#include "timeout.h"
#include "version.h"
#include "datafiles.h"
/******** Global variables! ********/
/* Command-line options: (note that configuration variables are in config.c) */
char *services_dir = SERVICES_DIR; /* -dir dirname */
char *log_filename = LOG_FILENAME; /* -log filename */
int debug = 0; /* -debug */
int readonly = 0; /* -readonly */
int logchan = 0; /* -logchan */
int skeleton = 0; /* -skeleton */
int nofork = 0; /* -nofork */
int forceload = 0; /* -forceload */
int noexpire = 0; /* -noexpire */
#ifdef IS44_CONVERTER
int is44 = 0; /* -is44 */
#endif
#ifdef USE_RDB
int do_mysql = 0; /* use mysql ? */
#endif
/* Set to 1 if we are to quit */
int quitting = 0;
/* Set to 1 if we are to quit after saving databases */
int delayed_quit = 0;
/* Contains a message as to why services is terminating */
char *quitmsg = NULL;
/* Input buffer - global, so we can dump it if something goes wrong */
char inbuf[BUFSIZE];
/* Socket for talking to server */
int servsock = -1;
/* Should we update the databases now? */
int save_data = 0;
/* At what time were we started? */
time_t start_time;
/* Parameters and environment */
char **my_av, **my_envp;
/******** Local variables! ********/
/* Set to 1 if we are waiting for input */
static int waiting = 0;
/* Set to 1 after we've set everything up */
static int started = 0;
/*************************************************************************/
/* Run expiration routines */
static void expire_all(void)
{
waiting = -3;
if (debug)
alog("debug: Running expire routines");
if (!skeleton) {
waiting = -21;
expire_nicks();
waiting = -22;
expire_chans();
waiting = -23;
expire_requests();
}
waiting = -25;
expire_akills();
#ifdef IRC_BAHAMUT
waiting = -26;
expire_sglines();
#endif
waiting = -28;
expire_sqlines();
#ifdef IRC_BAHAMUT
waiting = -27;
expire_szlines();
#endif
#ifndef STREAMLINED
expire_exceptions();
#endif
#ifdef USE_THREADS
if (ProxyDetect)
proxy_expire();
#endif
}
/*************************************************************************/
void save_databases(void)
{
waiting = -2;
if (debug)
alog("debug: Saving FFF databases");
waiting = -10;
backup_databases();
if (!skeleton) {
waiting = -11;
save_ns_dbase();
waiting = -12;
if (PreNickDBName) {
save_ns_req_dbase();
waiting = -13;
}
save_cs_dbase();
if (s_BotServ) {
waiting = -14;
save_bs_dbase();
}
if (s_HostServ) {
waiting = -15;
save_hs_dbase();
}
}
waiting = -16;
save_os_dbase();
waiting = -17;
save_news();
waiting = -18;
save_exceptions();
#ifdef USE_RDB
if (do_mysql) {
if (debug)
alog("debug: Saving RDB databases");
waiting = -10;
if (!skeleton) {
waiting = -11;
save_ns_rdb_dbase();
waiting = -12;
save_cs_rdb_dbase();
if (PreNickDBName) {
save_ns_req_rdb_dbase();
waiting = -13;
}
/* Temporary fix to avoid unwanted timeouts... */
send_cmd(ServerName, "PONG %s", ServerName);
if (s_BotServ) {
waiting = -14;
save_bs_rdb_dbase();
}
if (s_HostServ) {
waiting = -15;
save_hs_rdb_dbase();
}
waiting = -16;
save_os_rdb_dbase();
waiting = -17;
save_rdb_news();
waiting = -18;
save_rdb_exceptions();
}
}
#endif
}
/*************************************************************************/
/* Restarts services */
static void services_restart(void)
{
alog("Restarting");
if (!quitmsg)
quitmsg = "Restarting";
send_cmd(ServerName, "SQUIT %s :%s", ServerName, quitmsg);
disconn(servsock);
close_log();
#if defined(LINUX20) || defined(LINUX22)
pthread_kill_other_threads_np();
#endif
execve(SERVICES_BIN, my_av, my_envp);
if (!readonly) {
open_log();
log_perror("Restart failed");
close_log();
}
}
/*************************************************************************/
/**
* Added to allow do_restart from operserv access to the static functions without making them
* fair game to every other function - not exactly ideal :|
**/
void do_restart_services(void)
{
expire_all();
save_databases();
services_restart();
exit(1);
}
/*************************************************************************/
/* Terminates services */
static void services_shutdown(void)
{
if (!quitmsg)
quitmsg = "Terminating, reason unknown";
alog("%s", quitmsg);
if (started)
send_cmd(ServerName, "SQUIT %s :%s", ServerName, quitmsg);
disconn(servsock);
}
/*************************************************************************/
/* If we get a weird signal, come here. */
void sighandler(int signum)
{
if (started) {
if (signum == SIGHUP) { /* SIGHUP = save databases and restart */
signal(SIGHUP, SIG_IGN);
alog("Received SIGHUP, restarting.");
expire_all();
save_databases();
if (!quitmsg)
quitmsg = "Restarting on SIGHUP";
#ifdef SERVICES_BIN
services_restart();
exit(1);
#else
quitmsg =
"Restart attempt failed--SERVICES_BIN not defined (rerun configure)";
#endif
} else if (signum == SIGTERM) {
signal(SIGTERM, SIG_IGN);
signal(SIGHUP, SIG_IGN);
alog("Received SIGTERM, exiting.");
expire_all();
save_databases();
quitmsg = "Shutting down on SIGTERM";
services_shutdown();
exit(0);
} else if (signum == SIGINT || signum == SIGQUIT) {
/* nothing -- terminate below */
} else if (!waiting) {
alog("PANIC! buffer = %s", inbuf);
/* Cut off if this would make IRC command >510 characters. */
if (strlen(inbuf) > 448) {
inbuf[446] = '>';
inbuf[447] = '>';
inbuf[448] = 0;
}
wallops(NULL, "PANIC! buffer = %s\r\n", inbuf);
} else if (waiting < 0) {
/* This is static on the off-chance we run low on stack */
static char buf[BUFSIZE];
switch (waiting) {
case -1:
snprintf(buf, sizeof(buf), "in timed_update");
break;
case -10:
snprintf(buf, sizeof(buf), "backing up databases");
break;
case -11:
snprintf(buf, sizeof(buf), "saving %s", NickDBName);
break;
case -12:
snprintf(buf, sizeof(buf), "saving %s", ChanDBName);
break;
case -13:
snprintf(buf, sizeof(buf), "saving %s", PreNickDBName);
break;
case -14:
snprintf(buf, sizeof(buf), "saving %s", BotDBName);
break;
case -15:
snprintf(buf, sizeof(buf), "saving %s", HostDBName);
break;
case -16:
snprintf(buf, sizeof(buf), "saving %s", OperDBName);
break;
case -17:
snprintf(buf, sizeof(buf), "saving %s", NewsDBName);
break;
case -18:
snprintf(buf, sizeof(buf), "saving %s", ExceptionDBName);
break;
case -21:
snprintf(buf, sizeof(buf), "expiring nicknames");
break;
case -22:
snprintf(buf, sizeof(buf), "expiring channels");
break;
case -25:
snprintf(buf, sizeof(buf), "expiring autokills");
break;
#ifdef IRC_BAHAMUT
case -26:
snprintf(buf, sizeof(buf), "expiring SGLINEs");
break;
case -27:
snprintf(buf, sizeof(buf), "expiring SZLINEs");
break;
#endif
case -28:
snprintf(buf, sizeof(buf), "expiring SQLINEs");
break;
default:
snprintf(buf, sizeof(buf), "waiting=%d", waiting);
}
wallops(NULL, "PANIC! %s (%s)", buf, strsignal(signum));
alog("PANIC! %s (%s)", buf, strsignal(signum));
}
}
if (
#if !defined(USE_THREADS) || !defined(LINUX20)
signum == SIGUSR1 ||
#endif
!(quitmsg = calloc(BUFSIZE, 1))) {
quitmsg = "Out of memory!";
} else {
#if HAVE_STRSIGNAL
snprintf(quitmsg, BUFSIZE, "Services terminating: %s",
strsignal(signum));
#else
snprintf(quitmsg, BUFSIZE, "Services terminating on signal %d",
signum);
#endif
}
if (started) {
services_shutdown();
exit(0);
} else {
alog("%s", quitmsg);
if (isatty(2))
fprintf(stderr, "%s\n", quitmsg);
exit(1);
}
}
/*************************************************************************/
/* Main routine. (What does it look like? :-) ) */
int main(int ac, char **av, char **envp)
{
volatile time_t last_update; /* When did we last update the databases? */
volatile time_t last_expire; /* When did we last expire nicks/channels? */
volatile time_t last_check; /* When did we last check timeouts? */
volatile time_t last_DefCon; /* When was DefCon last checked? */
int i;
char *progname;
my_av = av;
my_envp = envp;
/* Find program name. */
if ((progname = strrchr(av[0], '/')) != NULL)
progname++;
else
progname = av[0];
/* Were we run under "listnicks" or "listchans"? Do appropriate stuff
* if so. */
if (strcmp(progname, "listnicks") == 0) {
do_listnicks(ac, av);
return 0;
} else if (strcmp(progname, "listchans") == 0) {
do_listchans(ac, av);
return 0;
}
/* Initialization stuff. */
if ((i = init(ac, av)) != 0)
return i;
/* We have a line left over from earlier, so process it first. */
process();
/* Set up timers. */
last_update = time(NULL);
last_expire = time(NULL);
last_check = time(NULL);
last_DefCon = time(NULL);
started = 1;
/*** Main loop. ***/
while (!quitting) {
time_t t = time(NULL);
if (debug >= 2)
alog("debug: Top of main loop");
if (!noexpire && !readonly
&& (save_data || t - last_expire >= ExpireTimeout)) {
expire_all();
last_expire = t;
}
if (!readonly && (save_data || t - last_update >= UpdateTimeout)) {
if (delayed_quit)
wallops(NULL,
"Updating databases on shutdown, please wait.");
save_databases();
if (save_data < 0)
break; /* out of main loop */
save_data = 0;
last_update = t;
}
if ((DefConTimeOut) && (t - last_DefCon >= dotime(DefConTimeOut))) {
resetDefCon(5);
last_DefCon = t;
}
if (delayed_quit)
break;
moduleCallBackRun();
waiting = -1;
if (t - last_check >= TimeoutCheck) {
check_timeouts();
last_check = t;
}
waiting = 1;
i = (int) (long) sgets2(inbuf, sizeof(inbuf), servsock);
waiting = 0;
if (i > 0) {
process();
} else if (i == 0) {
int errno_save = errno;
quitmsg = scalloc(BUFSIZE, 1);
if (quitmsg) {
snprintf(quitmsg, BUFSIZE, "Read error from server: %s",
strerror(errno_save));
} else {
quitmsg = "Read error from server";
}
quitting = 1;
}
waiting = -4;
}
/* Check for restart instead of exit */
if (save_data == -2) {
#ifdef SERVICES_BIN
alog("Restarting");
if (!quitmsg)
quitmsg = "Restarting";
send_cmd(ServerName, "SQUIT %s :%s", ServerName, quitmsg);
disconn(servsock);
close_log();
#if defined(LINUX20) || defined(LINUX22)
pthread_kill_other_threads_np();
#endif
execve(SERVICES_BIN, av, envp);
if (!readonly) {
open_log();
log_perror("Restart failed");
close_log();
}
return 1;
#else
quitmsg =
"Restart attempt failed--SERVICES_BIN not defined (rerun configure)";
#endif
}
/* Disconnect and exit */
services_shutdown();
return 0;
}
/*************************************************************************/
syntax highlighted by Code2HTML, v. 0.9.1