summaryrefslogtreecommitdiff
path: root/src/main.cpp
diff options
context:
space:
mode:
authorAdam <Adam@Anope.org>2010-05-25 00:57:25 -0500
committerAdam <Adam@anope.org>2010-06-18 21:04:07 -0400
commit4a2b9ebcf38d6c0a2860b966421b3af125438488 (patch)
treee4af4e59fd29352138db0fb0ff614d50233850b8 /src/main.cpp
parent2fba686904e6f78ebab35df171c5757afeebf05d (diff)
Renamed all of source files from .c to .cpp
Diffstat (limited to 'src/main.cpp')
-rw-r--r--src/main.cpp587
1 files changed, 587 insertions, 0 deletions
diff --git a/src/main.cpp b/src/main.cpp
new file mode 100644
index 000000000..d4746cf93
--- /dev/null
+++ b/src/main.cpp
@@ -0,0 +1,587 @@
+/* Services -- main source file.
+ *
+ * (C) 2003-2010 Anope Team
+ * Contact us at team@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.
+ *
+ *
+ */
+
+#include "services.h"
+#include "timers.h"
+#include "version.h"
+#include "modules.h"
+
+// getrlimit.
+#ifndef _WIN32
+# include <sys/time.h>
+# include <sys/resource.h>
+#endif
+
+
+/******** Global variables! ********/
+
+/* Command-line options: (note that configuration variables are in config.c) */
+std::string services_dir; /* -dir dirname */
+std::string services_bin; /* Binary as specified by the user */
+std::string orig_cwd; /* Original current working directory */
+std::string log_filename = "services.log"; /* -log filename */
+int debug = 0; /* -debug */
+int readonly = 0; /* -readonly */
+bool LogChan = false; /* -logchan */
+int nofork = 0; /* -nofork */
+int forceload = 0; /* -forceload */
+int nothird = 0; /* -nothrid */
+int noexpire = 0; /* -noexpire */
+int protocoldebug = 0; /* -protocoldebug */
+
+std::string binary_dir; /* Used to store base path for Anope */
+#ifdef _WIN32
+#include <process.h>
+#define execve _execve
+#endif
+
+/* Set to 1 if we are to quit */
+int quitting = 0;
+
+/* Set to 1 if we are to quit after saving databases */
+int shutting_down = 0;
+
+/* Contains a message as to why services is terminating */
+const char *quitmsg = NULL;
+
+/* 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 */
+
+/******** Local variables! ********/
+
+/* Set to 1 after we've set everything up */
+static int started = 0;
+
+/*************************************************************************/
+
+class ExpireTimer : public Timer
+{
+ public:
+ ExpireTimer(time_t timeout, time_t now) : Timer(timeout, now, true) { }
+
+ void Tick(time_t)
+ {
+ if (!readonly && !noexpire)
+ expire_all();
+ }
+};
+
+class UpdateTimer : public Timer
+{
+ public:
+ UpdateTimer(time_t timeout, time_t now) : Timer(timeout, now, true) { }
+
+ void Tick(time_t)
+ {
+ if (!readonly)
+ save_databases();
+ }
+};
+
+Socket *UplinkSock = NULL;
+
+class UplinkSocket : public Socket
+{
+ public:
+ UplinkSocket(const std::string &nTargetHost, int nPort, const std::string &nBindHost = "", bool nIPv6 = false) : Socket(nTargetHost, nPort, nBindHost, nIPv6)
+ {
+ UplinkSock = this;
+ }
+
+ ~UplinkSocket()
+ {
+ UplinkSock = NULL;
+ }
+
+ bool Read(const std::string &buf)
+ {
+ process(buf);
+ return true;
+ }
+};
+
+/*************************************************************************/
+
+/* Run expiration routines */
+
+extern void expire_all()
+{
+ if (noexpire || readonly)
+ {
+ // Definitely *do not* want.
+ return;
+ }
+
+ FOREACH_MOD(I_OnPreDatabaseExpire, OnPreDatabaseExpire());
+
+ Alog(LOG_DEBUG) << "Running expire routines";
+ expire_nicks();
+ expire_chans();
+ expire_requests();
+ expire_exceptions();
+
+ FOREACH_MOD(I_OnDatabaseExpire, OnDatabaseExpire());
+}
+
+/*************************************************************************/
+
+void save_databases()
+{
+ if (readonly)
+ return;
+
+ EventReturn MOD_RESULT;
+ FOREACH_RESULT(I_OnSaveDatabase, OnSaveDatabase());
+ Alog(LOG_DEBUG) << "Saving FFF databases";
+}
+
+/*************************************************************************/
+
+/* Restarts services */
+void do_restart_services()
+{
+ if (!readonly) {
+ expire_all();
+ save_databases();
+ }
+ Alog() << "Restarting";
+
+ FOREACH_MOD(I_OnPreRestart, OnPreRestart());
+
+ if (!quitmsg)
+ quitmsg = "Restarting";
+ /* Send a quit for all of our bots */
+ for (botinfo_map::const_iterator it = BotList.begin(); it != BotList.end(); ++it)
+ {
+ /* Don't use quitmsg here, it may contain information you don't want people to see */
+ ircdproto->SendQuit(it->second, "Restarting");
+ }
+ ircdproto->SendSquit(Config.ServerName, quitmsg);
+ /* Process to send the last bits of information before disconnecting */
+ socketEngine.Process();
+ delete UplinkSock;
+ close_log();
+ /* First don't unload protocol module, then do so */
+ ModuleManager::UnloadAll(false);
+ ModuleManager::UnloadAll(true);
+ chdir(binary_dir.c_str());
+ execve(services_bin.c_str(), my_av, my_envp);
+ if (!readonly) {
+ open_log();
+ log_perror("Restart failed");
+ close_log();
+ }
+
+ FOREACH_MOD(I_OnRestart, OnRestart());
+
+ exit(1);
+}
+
+/*************************************************************************/
+
+/* Terminates services */
+
+static void services_shutdown()
+{
+ FOREACH_MOD(I_OnPreShutdown, OnPreShutdown());
+
+ if (!quitmsg)
+ quitmsg = "Terminating, reason unknown";
+ Alog() << quitmsg;
+ if (started && UplinkSock)
+ {
+ /* Send a quit for all of our bots */
+ for (botinfo_map::const_iterator it = BotList.begin(); it != BotList.end(); ++it)
+ {
+ /* Don't use quitmsg here, it may contain information you don't want people to see */
+ ircdproto->SendQuit(it->second, "Shutting down");
+ }
+
+ ircdproto->SendSquit(Config.ServerName, quitmsg);
+
+ while (!UserListByNick.empty())
+ {
+ delete UserListByNick.begin()->second;
+ }
+ }
+ /* Process to send the last bits of information before disconnecting */
+ socketEngine.Process();
+ delete UplinkSock;
+ FOREACH_MOD(I_OnShutdown, OnShutdown());
+ /* First don't unload protocol module, then do so */
+ ModuleManager::UnloadAll(false);
+ ModuleManager::UnloadAll(true);
+ /* just in case they weren't all removed at least run once */
+ ModuleRunTimeDirCleanUp();
+}
+
+/*************************************************************************/
+
+/* 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
+ */
+ if (!quitmsg)
+ quitmsg = "Services terminating via a signal.";
+
+ if (started)
+ {
+#ifndef _WIN32
+ if (signum == SIGHUP)
+ {
+ Alog() << "Received SIGHUP: Saving Databases & Rehash Configuration";
+
+ expire_all();
+ save_databases();
+
+ if (!read_config(1))
+ {
+ quitmsg = "Error Reading Configuration File (Received SIGHUP)";
+ quitting = 1;
+ }
+
+ FOREACH_MOD(I_OnReload, OnReload(true));
+ 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);
+ }
+ }
+ }
+
+
+ /* Should we send the signum here as well? -GD */
+ FOREACH_MOD(I_OnSignal, OnSignal(quitmsg));
+
+ if (started)
+ {
+ services_shutdown();
+ exit(0);
+ }
+ else
+ {
+ if (isatty(2))
+ {
+ fprintf(stderr, "%s\n", quitmsg);
+ }
+ else
+ {
+ Alog() << quitmsg;
+ }
+
+ exit(1);
+ }
+}
+
+/*************************************************************************/
+
+/** The following comes from InspIRCd to get the full path of the Anope executable
+ */
+std::string GetFullProgDir(char *argv0)
+{
+ char buffer[PATH_MAX];
+#ifdef _WIN32
+ /* Windows has specific API calls to get the EXE path that never fail.
+ * For once, Windows has something of use, compared to the POSIX code
+ * for this, this is positively neato.
+ */
+ if (GetModuleFileName(NULL, buffer, PATH_MAX))
+ {
+ std::string fullpath = buffer;
+ std::string::size_type n = fullpath.rfind("\\" SERVICES_BIN);
+ services_bin = fullpath.substr(n + 1, fullpath.size());
+ return std::string(fullpath, 0, n);
+ }
+#else
+ // Get the current working directory
+ if (getcwd(buffer, PATH_MAX))
+ {
+ std::string remainder = argv0;
+
+ /* Does argv[0] start with /? If so, it's a full path, use it */
+ if (remainder[0] == '/')
+ {
+ std::string::size_type n = remainder.rfind("/" SERVICES_BIN);
+ services_bin = remainder.substr(n + 1, remainder.size());
+ return std::string(remainder, 0, n);
+ }
+
+ services_bin = remainder;
+ if (services_bin.substr(0, 2) == "./")
+ services_bin = services_bin.substr(2);
+ std::string fullpath = std::string(buffer) + "/" + remainder;
+ std::string::size_type n = fullpath.rfind("/" SERVICES_BIN);
+ return std::string(fullpath, 0, n);
+ }
+#endif
+ return "/";
+}
+
+/*************************************************************************/
+
+static bool Connect()
+{
+ /* Connect to the remote server */
+ int servernum = 1;
+ for (std::list<Uplink *>::iterator curr_uplink = Config.Uplinks.begin(); curr_uplink != Config.Uplinks.end(); ++curr_uplink, ++servernum)
+ {
+ uplink_server = *curr_uplink;
+
+ EventReturn MOD_RESULT;
+ FOREACH_RESULT(I_OnPreServerConnect, OnPreServerConnect(*curr_uplink, servernum));
+ if (MOD_RESULT != EVENT_CONTINUE)
+ {
+ if (MOD_RESULT == EVENT_STOP)
+ break;
+ return true;
+ }
+
+ try
+ {
+ new UplinkSocket(uplink_server->host, uplink_server->port, Config.LocalHost ? Config.LocalHost : "", uplink_server->ipv6);
+ }
+ catch (SocketException& ex)
+ {
+ Alog() << "Unable to connect to server" << servernum << " (" << uplink_server->host << ":" << uplink_server->port << "), " << ex.GetReason();
+ continue;
+ }
+
+ Alog() << "Connected to Server " << servernum << " (" << uplink_server->host << ":" << uplink_server->port << ")";
+ return true;
+ }
+
+ uplink_server = NULL;
+
+ return false;
+}
+
+/*************************************************************************/
+
+/* Main routine. (What does it look like? :-) ) */
+
+int main(int ac, char **av, char **envp)
+{
+ int i;
+
+ my_av = av;
+ my_envp = envp;
+
+ char cwd[PATH_MAX] = "";
+#ifdef _WIN32
+ GetCurrentDirectory(PATH_MAX, cwd);
+#else
+ getcwd(cwd, PATH_MAX);
+#endif
+ orig_cwd = cwd;
+
+#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
+
+ binary_dir = GetFullProgDir(av[0]);
+ if (binary_dir[binary_dir.size() - 1] == '.')
+ binary_dir = binary_dir.substr(0, binary_dir.size() - 2);
+#ifdef _WIN32
+ std::string::size_type n = binary_dir.rfind("\\");
+ services_dir = binary_dir.substr(0, n) + "\\data";
+#else
+ std::string::size_type n = binary_dir.rfind("/");
+ services_dir = binary_dir.substr(0, n) + "/data";
+#endif
+
+ /* Clean out the module runtime directory prior to running, just in case files were left behind during a previous run */
+ ModuleRunTimeDirCleanUp();
+
+ /* General initialization first */
+ if ((i = init_primary(ac, av)) != 0)
+ return i;
+
+ Alog(LOG_TERMINAL) << "Anope " << version_number << ", " << version_build;
+#ifdef _WIN32
+ Alog(LOG_TERMINAL) << "Using configuration file " << services_dir << "\\" << services_conf;
+#else
+ Alog(LOG_TERMINAL) << "Using configuration file " << services_dir << "/" << services_conf;
+#endif
+
+ /* Initialization stuff. */
+ if ((i = init_secondary(ac, av)) != 0)
+ return i;
+
+ /* If the first connect fails give up, don't sit endlessly trying to reconnect */
+ if (!Connect())
+ fatal_perror("Can't connect to any servers");
+
+ ircdproto->SendConnect();
+ FOREACH_MOD(I_OnServerConnect, OnServerConnect());
+
+ started = 1;
+
+#ifndef _WIN32
+ if (Config.DumpCore)
+ {
+ rlimit rl;
+ if (getrlimit(RLIMIT_CORE, &rl) == -1)
+ {
+ Alog() << "Failed to getrlimit()!";
+ }
+ else
+ {
+ rl.rlim_cur = rl.rlim_max;
+ if (setrlimit(RLIMIT_CORE, &rl) == -1)
+ {
+ Alog() << "setrlimit() failed, cannot increase coredump size";
+ }
+ }
+ }
+#endif
+
+ /* Set up timers */
+ time_t last_check = time(NULL);
+ ExpireTimer expireTimer(Config.ExpireTimeout, last_check);
+ UpdateTimer updateTimer(Config.UpdateTimeout, last_check);
+
+ /*** Main loop. ***/
+ while (!quitting)
+ {
+ while (!quitting && UplinkSock)
+ {
+ time_t t = time(NULL);
+
+ Alog(LOG_DEBUG_2) << "Top of main loop";
+
+ if (!readonly && (save_data || shutting_down))
+ {
+ if (!noexpire)
+ expire_all();
+ if (shutting_down)
+ ircdproto->SendGlobops(NULL, "Updating databases on shutdown, please wait.");
+ save_databases();
+ save_data = 0;
+ }
+
+ if (shutting_down)
+ {
+ quitting = 1;
+ break;
+ }
+
+ if (t - last_check >= Config.TimeoutCheck)
+ {
+ TimerManager::TickTimers(t);
+ last_check = t;
+ }
+
+ /* Process any modes that need to be (un)set */
+ ModeManager::ProcessModes();
+
+ /* Process the socket engine */
+ socketEngine.Process();
+ }
+
+ if (quitting)
+ {
+ /* Disconnect and exit */
+ services_shutdown();
+ }
+ else
+ {
+ FOREACH_MOD(I_OnServerDisconnect, OnServerDisconnect());
+
+ unsigned j = 0;
+ for (; j < (Config.MaxRetries ? Config.MaxRetries : j + 1); ++j)
+ {
+ Alog() << "Disconnected from the server, retrying in " << Config.RetryWait << " seconds";
+
+ sleep(Config.RetryWait);
+ if (Connect())
+ {
+ ircdproto->SendConnect();
+ FOREACH_MOD(I_OnServerConnect, OnServerConnect());
+ break;
+ }
+ }
+ if (Config.MaxRetries && j == Config.MaxRetries)
+ {
+ Alog() << "Max connection retry limit exceeded";
+ quitting = 1;
+ }
+ }
+ }
+
+ return 0;
+}
+