1
0
mirror of https://github.com/anope/anope.git synced 2026-06-25 13:46:38 +02:00
Files
anope/src/main.c
T
rob rob@31f1291d-b8d6-0310-a050-a5561fc1590b 6690784bff BUILD : 1.7.14 (1068) BUGS : N/A NOTES : Added -nothird and -support command line options
git-svn-id: svn://svn.anope.org/anope/trunk@1068 31f1291d-b8d6-0310-a050-a5561fc1590b


git-svn-id: http://anope.svn.sourceforge.net/svnroot/anope/trunk@792 5417fbe8-f217-4b02-8779-1006273d7864
2006-06-22 07:48:45 +00:00

659 lines
18 KiB
C

/* Services -- main source file.
*
* (C) 2003-2005 Anope Team
* Contact us at info@anope.org
*
* Please read COPYING and README for further 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$
*
*/
#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 nothird = 0; /* -nothrid */
int noexpire = 0; /* -noexpire */
#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;
/* Moved here from version.h */
const char version_number[] = VERSION_STRING;
const char version_number_dotted[] = VERSION_STRING_DOTTED;
const char version_build[] =
"build #" BUILD ", compiled " __DATE__ " " __TIME__;
/* the space is needed cause if you build with nothing it will complain */
const char version_flags[] =
" " VER_DEBUG VER_ENCRYPTION VER_OS VER_MYSQL VER_MODULE;
extern char *mod_current_buffer;
/******** 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 */
extern void expire_all(void)
{
send_event(EVENT_DB_EXPIRE, 1, EVENT_START);
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();
if (ircd->sgline) {
waiting = -26;
expire_sglines();
}
if (ircd->sqline) {
waiting = -28;
expire_sqlines();
}
if (ircd->szline) {
waiting = -27;
expire_szlines();
}
expire_exceptions();
send_event(EVENT_DB_EXPIRE, 1, EVENT_STOP);
}
/*************************************************************************/
void save_databases(void)
{
send_event(EVENT_DB_SAVING, 1, EVENT_START);
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();
anope_cmd_pong(ServerName, ServerName);
waiting = -12;
save_cs_rdb_dbase();
anope_cmd_pong(ServerName, ServerName);
if (PreNickDBName) {
save_ns_req_rdb_dbase();
anope_cmd_pong(ServerName, ServerName);
waiting = -13;
}
if (s_BotServ) {
waiting = -14;
save_bs_rdb_dbase();
anope_cmd_pong(ServerName, ServerName);
}
if (s_HostServ) {
waiting = -15;
save_hs_rdb_dbase();
anope_cmd_pong(ServerName, ServerName);
}
waiting = -16;
save_os_rdb_dbase();
anope_cmd_pong(ServerName, ServerName);
waiting = -17;
save_rdb_news();
anope_cmd_pong(ServerName, ServerName);
waiting = -18;
save_rdb_exceptions();
anope_cmd_pong(ServerName, ServerName);
}
}
#endif
send_event(EVENT_DB_SAVING, 1, EVENT_STOP);
}
/*************************************************************************/
/* Restarts services */
static void services_restart(void)
{
alog("Restarting");
send_event(EVENT_RESTART, 1, EVENT_START);
if (!quitmsg)
quitmsg = "Restarting";
anope_cmd_squit(ServerName, quitmsg);
disconn(servsock);
close_log();
modules_unload_all(true);
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)
{
if (!readonly) {
expire_all();
save_databases();
}
services_restart();
exit(1);
}
/*************************************************************************/
/* Terminates services */
static void services_shutdown(void)
{
User *u, *next;
send_event(EVENT_SHUTDOWN, 1, EVENT_START);
if (!quitmsg)
quitmsg = "Terminating, reason unknown";
alog("%s", quitmsg);
if (started) {
anope_cmd_squit(ServerName, quitmsg);
Anope_Free(uplink);
Anope_Free(mod_current_buffer);
if (ircd->chanmodes) {
Anope_Free(ircd->chanmodes);
}
u = firstuser();
while (u) {
next = nextuser();
delete_user(u);
u = next;
}
}
send_event(EVENT_SHUTDOWN, 1, EVENT_STOP);
disconn(servsock);
modules_unload_all(true); /* Only legitimate use of this function */
}
/*************************************************************************/
/* If we get a weird signal, come here. */
void sighandler(int signum)
{
/* We set the quit message to something default, just to be sure it is
* always set when we need it. It seems some signals slip through to the
* QUIT code without having a valid quitmsg. -GD
*/
quitmsg = sstrdup("Signal Received");
if (started) {
#ifndef _WIN32
if (signum == SIGHUP) { /* SIGHUP = save databases and restart */
signal(SIGHUP, SIG_IGN);
signal(SIGUSR2, 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 == SIGQUIT) {
/* had to move it to here to make win32 happy */
} else if (signum == SIGUSR2) {
alog("Received SIGUSR2: Saving Databases & Rehash Configuration");
expire_all();
save_databases();
if (!read_config(1)) {
sprintf(quitmsg,
"Error Reading Configuration File (Received SIGUSR2)");
quitting = 1;
}
send_event(EVENT_RELOAD, 1, EVENT_START);
return;
} else
#endif
if (signum == SIGTERM) {
signal(SIGTERM, SIG_IGN);
#ifndef _WIN32
signal(SIGHUP, SIG_IGN);
#endif
alog("Received SIGTERM, exiting.");
expire_all();
save_databases();
quitmsg = "Shutting down on SIGTERM";
services_shutdown();
exit(0);
} else if (signum == SIGINT) {
if (nofork) {
signal(SIGINT, SIG_IGN);
alog("Received SIGINT, exiting.");
expire_all();
save_databases();
quitmsg = "Shutting down on SIGINT";
services_shutdown();
exit(0);
}
} 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);
modules_unload_all(false);
} 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;
case -26:
snprintf(buf, sizeof(buf), "expiring SGLINEs");
break;
case -27:
snprintf(buf, sizeof(buf), "expiring SZLINEs");
break;
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));
modules_unload_all(false);
}
}
if (
#ifndef _WIN32
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 (signum == SIGSEGV) {
do_backtrace(1);
modules_unload_all(false); /* probably cant do this, but might as well try, we have nothing left to loose */
}
/* Should we send the signum here as well? -GD */
send_event(EVENT_SIGNAL, 1, quitmsg);
if (started) {
services_shutdown();
exit(0);
} else {
if (isatty(2)) {
fprintf(stderr, "%s\n", quitmsg);
} else {
alog("%s", 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;
#ifndef _WIN32
/* If we're root, issue a warning now */
if ((getuid() == 0) && (getgid() == 0)) {
fprintf(stderr,
"WARNING: You are currently running Anope as the root superuser. Anope does not\n");
fprintf(stderr,
" require root privileges to run, and it is discouraged that you run Anope\n");
fprintf(stderr, " as the root superuser.\n");
}
#endif
/* General initialization first */
if ((i = init_primary(ac, av)) != 0)
return i;
/* Find program name. */
if ((progname = strrchr(av[0], '/')) != NULL)
progname++;
else
progname = av[0];
#ifdef _WIN32
if (strcmp(progname, "listnicks.exe") == 0)
#else
if (strcmp(progname, "listnicks") == 0)
#endif
{
do_listnicks(ac, av);
return 0;
}
#ifdef _WIN32
else if (strcmp(progname, "listchans.exe") == 0)
#else
else if (strcmp(progname, "listchans") == 0)
#endif
{
do_listchans(ac, av);
return 0;
}
/* Initialization stuff. */
if ((i = init_secondary(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)
anope_cmd_global(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 (error num: %d)",
strerror(errno_save), 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";
anope_cmd_squit(ServerName, quitmsg);
disconn(servsock);
close_log();
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;
}
/*************************************************************************/
void do_backtrace(int show_segheader)
{
#ifndef _WIN32
#ifdef HAVE_BACKTRACE
void *array[50];
size_t size;
char **strings;
int i;
if (show_segheader) {
alog("Backtrace: Segmentation fault detected");
alog("Backtrace: report the following lines");
}
alog("Backtrace: Anope version %s %s %s", version_number,
version_build, version_flags);
size = backtrace(array, 10);
strings = backtrace_symbols(array, size);
for (i = 0; i < size; i++) {
alog("Backtrace(%d): %s", i, strings[i]);
}
free(strings);
alog("Backtrace: complete");
#else
alog("Backtrace: not available on this platform");
#endif
#else
char *winver;
winver = GetWindowsVersion();
alog("Backtrace: not available on Windows");
alog("Running %S", winver);
free(winver);
#endif
}