#include #include // Cyphesis Online RPG Server and AI Engine // Copyright (C) 2000-2004 Alistair Riddoch // // 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; if not, write to the Free Software Foundation, // Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #ifdef HAVE_CONFIG_H #endif #include "globals.h" #include "const.h" #include "log.h" #include "compose.hpp" #include "system.h" #include #include #include #include #include #ifdef HAVE_IO_H #include #endif #include #include const char * const CYPHESIS = "cyphesis"; const char * const SLAVE = "slave"; static const char * const DEFAULT_RULESET = "deeds"; static const char * const DEFAULT_INSTANCE = "cyphesis"; varconf::Config * global_conf = nullptr; std::string instance(DEFAULT_INSTANCE); std::string bin_directory(BINDIR); std::string share_directory(DATADIR); std::string etc_directory(SYSCONFDIR); std::string var_directory(LOCALSTATEDIR); std::string assets_directory(""); std::string ruleset_name(DEFAULT_RULESET); std::string server_uuid(""); std::string server_key(""); bool exit_flag = false; bool exit_flag_soft = false; bool exit_soft_enabled = false; bool daemon_flag = false; bool database_flag = true; int timeoffset = SPM * MPH * 9; // Morning int client_port_num = 6767; int dynamic_port_start = 6800; int dynamic_port_end = 6899; static const char * const FALLBACK_LOCALSTATEDIR = "/var"; static const int S = USAGE_SERVER; static const int C = USAGE_CLIENT; static const int M = USAGE_CYCMD; static const int D = USAGE_DBASE; static const int P = USAGE_CYPYTHON; static const int A = USAGE_AICLIENT; /// \brief Structure for holding data about varconf options typedef struct { const char * section; const char * option; const char * value; const char * dflt; const char * description; int flags; } usage_data; static const usage_data usage[] = { { "", "help", "", "", "Display usage information and exit", S|C|M|D|A }, { "", "version", "", "", "Display the version information and exit", S|C|M|D|A }, { "", "instance", "", "\"cyphesis\"", "Unique short name for the server instance", S|C|M|D }, { "", "interactive", "", "", "Run in interactive mode, giving a Python prompt", C }, { CYPHESIS, "dynamicpaths", "true|false", "false", "Enables dynamic paths, calculated from the current executable", S|D|C|M|A }, { CYPHESIS, "directory", "", "", "Directory where server data and scripts can be found", S|C }, { CYPHESIS, "confdir", "", "", "Directory where server config can be found", S|C|M|D }, { CYPHESIS, "bindir", "", "", "Directory where Cyphesis binaries can be found", S|C|M|D }, { CYPHESIS, "vardir", "", "", "Directory where temporary files can be stored", S|C|M }, { CYPHESIS, "assetsdir", "", "", "Directory where media assets are stored, if nothing is specified Cyphesis will look under the shared directory", S }, { CYPHESIS, "ruleset", "", DEFAULT_RULESET, "Ruleset name", S|C|D }, { CYPHESIS, "servername", "", "", "Published name of the server", S|C }, { CYPHESIS, "tcpport", "", "6767", "Network listen port for client connections", S|C|M }, { CYPHESIS, "dynamic_port_start", "", "6800", "Lowest port to try and used for dyanmic ports", S }, { CYPHESIS, "dynamic_port_end", "", "6899", "Highest port to try and used for dyanmic ports", S }, { CYPHESIS, "usedatabase", "true|false", "true", "Flag to control whether to use a database for persistent storage", S }, { CYPHESIS, "daemon", "true|false", "false", "Flag to control running the server in daemon mode", S }, { CYPHESIS, "nice", "", "1", "Reduce the priority level of the server", S }, { CYPHESIS, "useaiclient", "true|false", "false", "Flag to control whether AI is to be driven by a client", S }, { CYPHESIS, "dbserver", "", "", "Hostname for the PostgreSQL RDBMS", S|D }, { CYPHESIS, "dbname", "", "\"cyphesis\"", "Name of the database to use", S|D }, { CYPHESIS, "dbuser", "", "", "Database user name for access", S|D }, { CYPHESIS, "dbpasswd", "", "", "Database password for access", S|D }, { SLAVE, "tcpport", "", "6768", "Network listen port for client connections to the AI slave server", M }, { SLAVE, "server", "", "localhost", "Master server to connect the slave to", M }, {nullptr, nullptr, nullptr, nullptr } }; static int check_tmp_path(const std::string & dir) { std::string tmp_directory = dir + "/tmp"; struct stat tmp_stat{}; if (::stat(tmp_directory.c_str(), &tmp_stat) != 0) { return -1; } if (!S_ISDIR(tmp_stat.st_mode)) { return -1; } if (::access(tmp_directory.c_str(), W_OK) != 0) { return -1; } return 0; } static int force_simple_name(const std::string & in, std::string & out) { out = std::string(in.size(), ' '); for (unsigned int i = 0; i < in.size(); ++i) { int c = in[i]; if (islower(c) || isdigit(c)) { out[i] = c; } else if (isalpha(c)) { out[i] = ::tolower(c); } else if (isspace(c) || c == '_') { out[i] = '_'; } else { return -1; } } return 0; } template int readConfigItem(const std::string & section, const std::string & key, T & storage) { if (global_conf->findItem(section, key)) { storage = global_conf->getItem(section, key); return 0; } return -1; } template<> int readConfigItem(const std::string & section, const std::string & key, std::string & storage) { if (global_conf->findItem(section, key)) { storage = global_conf->getItem(section, key).as_string(); return 0; } return -1; } template int readConfigItem(const std::string & section, const std::string & key, bool & storage); template int readConfigItem(const std::string & section, const std::string & key, int & storage); /// \brief Base class for handling varconf options declared inline. class Option { protected: const std::string m_value; const std::string m_description; Option(std::string val, std::string descr); public: virtual ~Option() = 0; const std::string & value() const { return m_value; } const std::string & description() const { return m_description; } virtual void read(varconf::Variable var) = 0; virtual const std::string repr() const = 0; virtual size_t size() const = 0; virtual void missing() { } virtual void postProcess() { } }; /// \brief Basic varconf option which does not require any processing class DumbOption : public Option { protected: const std::string m_default; public: DumbOption(const std::string & val, const std::string & descr, std::string deflt); ~DumbOption() override = default; void read(varconf::Variable var) override { } const std::string repr() const override; size_t size() const override; }; /// \brief Basic varconf option declared as a variable inline template class StaticOption : public Option { protected: ValueT & m_data; const ValueT m_default; public: StaticOption(const std::string & val, const std::string & descr, ValueT & data) : Option(val, descr), m_data(data), m_default(data) { } void read(varconf::Variable var) override; const std::string repr() const override; size_t size() const override; }; Option::Option(std::string val, std::string descr) : m_value(std::move(val)), m_description(std::move(descr)) { } Option::~Option() = default; DumbOption::DumbOption(const std::string & val, const std::string & descr, std::string deflt) : Option(val, descr), m_default(std::move(deflt)) { } const std::string DumbOption::repr() const { return m_default; } size_t DumbOption::size() const { return m_default.size(); } template<> void StaticOption::read(varconf::Variable var) { m_data = var.as_string(); } template void StaticOption::read(varconf::Variable var) { m_data = var; } template const std::string StaticOption::repr() const { return String::compose("%1", m_default); } template size_t StaticOption::size() const { return String::compose("%1", m_default).size(); } template class StaticOption; /// \brief Handle the processing required for a unix socket option class UnixSockOption : public StaticOption { protected: const char * const m_format; public: UnixSockOption(const std::string & val, const std::string & descr, std::string & data, const char * format) : StaticOption(val, descr, data), m_format(format) { } void missing() override; void postProcess() override; }; void UnixSockOption::missing() { m_data = String::compose(m_format, instance); } void UnixSockOption::postProcess() { if (m_data.find('/') != 0) { m_data = String::compose("%1/tmp/%2", var_directory, m_data); } } typedef std::map > OptionMap; typedef std::map SectionMap; /// \brief Singleton to manage all information about varconf options. class Options { protected: SectionMap m_sectionMap; static Options * m_instance; explicit Options(); public: static Options * instance() { if (m_instance == nullptr) { m_instance = new Options; } return m_instance; } const SectionMap & sectionMap() const { return m_sectionMap; } int check_config(varconf::Config &, int usage_groups = USAGE_SERVER| USAGE_CLIENT| USAGE_CYCMD| USAGE_DBASE) const; void addOption(const std::string & section, const std::string & setting, Option *); }; Options * Options::m_instance = nullptr; Options::Options() { const usage_data * ud = &usage[0]; for (; ud->section != nullptr; ++ud) { m_sectionMap[ud->section].insert(std::make_pair(ud->option, std::unique_ptr