/*************************************************************************** * Copyright (C) gempa GmbH * * All rights reserved. * * Contact: gempa GmbH (seiscomp-dev@gempa.de) * * * * GNU Affero General Public License Usage * * This file may be used under the terms of the GNU Affero * * Public License version 3.0 as published by the Free Software Foundation * * and appearing in the file LICENSE included in the packaging of this * * file. Please review the following information to ensure the GNU Affero * * Public License version 3.0 requirements will be met: * * https://www.gnu.org/licenses/agpl-3.0.html. * * * * Other Usage * * Alternatively, this file may be used in accordance with the terms and * * conditions contained in a signed written agreement between you and * * gempa GmbH. * ***************************************************************************/ #ifndef SEISCOMP_SYSTEM_APPLICATION_H #define SEISCOMP_SYSTEM_APPLICATION_H #include #include #include #include #include #include #include #include namespace Seiscomp { namespace Logging { class Output; } namespace System { class Application; namespace Detail { template T getConfig(const Application *app, const std::string &symbol, bool asPath); std::string join(const std::string &prefix, const char *relativeName); } /** * \brief Application class to write commandline applications easily using * \brief configuration files and commandline options. * * The class Application works as follows: * * \code * exec() * init() * run() * done() * \endcode * * All of the above methods are virtual. A derived class can reimplement * each method to fit its needs. * The Application class does all the administrative work: * - Reading from configuration files * - Handling commandline parameters * - Process forking when creating daemons * - Signal handling * * ### Detailed worflow * * In the simple call sequence above the most complex implementation is * hidden inside \ref init(). It deals with handling the * commandline, reading configuration files, setup logging and other various * tasks. Many of the steps can be intercepted and re-implemented if necessary. * Always good practice is to call the implemented method of the base class * first and check the result, e.g. when re-implementing \ref init(). * @code * bool MyApplication::init() { * if ( !Application::init() ) { * SEISCOMP_ERROR("Error on initializing the application"); * return false; * } * * // Do custom initializations * return true; * } * @endcode * * If any of the functions called from within @ref init() returns false then * initialization is cancelled and @ref init() returns false. * * * init() (virtual) * * createCommandLineDescription() (virtual) * * initConfiguration() (virtual) * * printUsage() (virtual) * * printVersion() (virtual) * * handleCommandLineOptions() (virtual) * * validateParameters() (virtual) * * handleInitializationError() (virtual) * * printConfigVariables() (virtual) * * validateSchemaParameters() (virtual) * * handlePreFork() (virtual) * * forkProcess() (virtual) * * run() (virtual) * * Is pure virtual and needs to be implemented. * * done() (virtual) * * Sets internal exist flag and causes isExitRequested() to return * false. * * ### Settings * * An important part of an application are settings. The term settings refers * to a collection of several options. Let's illusttrate it with an example. * An application introduces two parameters, param1 and param2 whereas the * first one is an integer and the second a string. The imperative approach * to read the two parameters is: * * @code * void MyApplication::createCommandLineDescription() { * Application::createCommandLineDescription(); * * commandline().addGroup("My params"); * commandline().addOption("My params", "param1", "Sets param1", &_param1); * commandline().addOption("My params", "param2", "Sets param2", &_param2); * } * * bool MyApplication::initConfiguration() { * if ( !Application::initConfiguration() ) * return false; * * try { _param1 = configGetInt("param1"); } * catch ( ... ) {} * * try { _param2 = configGetString("param2"); } * catch ( ... ) {} * } * @endcode * * This procedure has to be repeated over and over again for each new * parameter. When dealing with arrays of settings then one has to repeat * even more boilerplate code. * * The declarative approach is more efficient and easy to use. An example * to illustrate the concept: * * @code * class MyApplication : public Application { * public: * MyApplication(int argc, char **argv) * : Application(argc, argv) { * // Bind the below settings structure to the application. Commandline * // and configuration files are handled transparently. * bindSettings(&_settings); * } * * private: * struct Settings : AbstractSettings { * int param1; * string param2; * * virtual void accept(SettingsLinker &linker) { * linker & cfg(param1, "param1") & cli("My params", "param1", "Sets param1") * & cfg(param2, "param2") & cli("My params", "param2", "Sets param2"); * } * } _settings; * }; * @endcode * * Here a settings structure is declared (which can be nested and also * contain arrays of other structures) which is bound to the application. * The only requirement for the root settings type is that it inherits from * \ref AbstractSettings. All other composite types below must implement * an accept method which takes \ref SettingsLinker as the only argument passed * via a reference. * * @code * void accept(SettingsLinker &linker); * @endcode */ class SC_SYSTEM_CORE_API Application : public Core::InterruptibleObject { // ---------------------------------------------------------------------- // Public types // ---------------------------------------------------------------------- public: typedef std::vector Arguments; enum Stage { COMMANDLINE, CONFIGURATION, LOGGING, PLUGINS, ST_QUANTITY }; // ---------------------------------------------------------------------- // X'truction // ---------------------------------------------------------------------- public: Application(int argc, char** argv); ~Application(); // ---------------------------------------------------------------------- // Operators // ---------------------------------------------------------------------- public: int operator()(); // ---------------------------------------------------------------------- // Public functions // ---------------------------------------------------------------------- public: //! Returns a list of commandline parameters const Arguments &arguments() const; //! Returns the commandline interface to add groups and options CommandLine &commandline(); const CommandLine &commandline() const; //! Returns the local configuration object const Config::Config &configuration() const; //! Returns the path of the application (arg[0]) const char* path() const; /** * Returns the name of the application used for section lookup * in the configuration repository. * @return The name of the application */ const std::string& name() const; /** * Adds a pacakge search path to the pluginregistry. This call * is equal to * \code * Seiscomp::System::PluginRegistry::Instance()->addPackagePath(package); * \endcode * @param path */ void addPluginPackagePath(const std::string &package); //! Returns the version string const char *frameworkVersion() const; /** * Enters the mainloop and waits until exit() is called * or a appropriate signal has been fired (e.g. SIGTERM). * @return The value that was set with to exit() */ int exec(); /** * Exit the application and set the returnCode. * @param returnCode The value returned from exec() */ virtual void exit(int returnCode); /** * Exit the application and set the returnCode to 0. * This call is equivalent to exit(0). */ void quit(); /** * Returns whether exit has been requested or not. * This query is important within own run loops to * check for an abort criteria. */ bool isExitRequested() const; //! Prints the program usage. The default implementation //! prints the commandline options only virtual void printUsage() const; //! Returns the path to the crashhandler const std::string& crashHandler() const; // ---------------------------------------------------------------------- // Initialization configuration functions // This functions have to be called before the init() method // ---------------------------------------------------------------------- public: //! Enables the daemon mode to be selectable via commandline void setDaemonEnabled(bool enable); //! Enables/disables logging of context (source file + line number) void setLoggingContext(bool); //! Enables/disables logging of component void setLoggingComponent(bool); //! Enables/disables logging to stderr void setLoggingToStdErr(bool); //! Adds a certain component to the logging output void addLoggingComponentSubscription(const std::string&); //! Closes the logging backend void closeLogging(); //! Returns the applications version. The default implementation //! returns nullptr and uses the global framework version instead. virtual const char *version(); // ---------------------------------------------------------------------- // Static public members // ---------------------------------------------------------------------- public: //! Returns the pointer to the application's instance. static Application *Instance(); /** * Enabled/disables signal handling. * It is enabled by default. * NOTE: Call this method BEFORE construction when disabling signal * handling. * @param termination enables/disables SIGTERM, SIGINT * @param crash enables/disables SIGSEGV, SIGABRT */ static void HandleSignals(bool termination, bool crash); // ---------------------------------------------------------------------- // Protected functions // ---------------------------------------------------------------------- protected: //! Only reimplement this in derived classes that are supposed to //! be inherited again. This is not meant for user code. Use //! \ref createCommandLineDescription() instead. //! If this method is being implemented, the base implementation //! must be called. virtual void createBaseCommandLineDescription(); //! Reimplement this method to add additional commandline groups //! and/or options virtual void createCommandLineDescription(); //! This method is called right after the commandline has been parsed //! and before parameters are validated. This is useful to run //! commands such as --version or custom queries. //! Return true if an option has been handled and the application //! should quit. The default implementation just returns false, virtual bool handleCommandLineOptions(); //! This method is called just before the process would fork if //! daemon mode is requested. Even without daemon mode it will //! be called. All plugins have been loaded already and e.g. an //! additional commandline check can be performed which would //! require a particular plugin to be loaded. //! Return false if the application should be terminated. The default //! implementation just returns true. virtual bool handlePreFork(); //! This method can be used to verify custom configuration or //! commandline parameters virtual bool validateParameters(); //! Initialization method. virtual bool init(); /** * Starts the mainloop until exit() or quit() is called. * The default implementation waits for messages in blocking mode * and calls handleMessage() whenever a new message arrives. */ virtual bool run() = 0; //! Cleanup method called before exec() returns. virtual void done(); /** * Forks the process. * @return The result of forking */ virtual bool forkProcess(); //! Opens the configuration file and reads the state variables virtual bool initConfiguration(); //! Loads plugins virtual bool initPlugins(); /** * Prints the version information to stdout */ virtual void printVersion(); /** * Prints all available configuration variables */ virtual void printConfigVariables(); /** * Returns lists of schema modules and plugins to validate in * method validateSchemaParameters */ virtual void schemaValidationNames(std::vector &modules, std::vector &plugins) const; /** * Validates configuration variables use by application against * description xml file */ virtual bool validateSchemaParameters(); //! Handles the interrupt request from outside virtual void handleInterrupt(int) override; /** * Derived class can implement this method to react on * errors while initialization. The default implementation * does nothing. * @param stage The stage where the error occured */ virtual bool handleInitializationError(int stage); // ---------------------------------------------------------------------- // Verbosity handlers // ---------------------------------------------------------------------- protected: //! Callback method to display a message regarding the current //! initialization state virtual void showMessage(const char*); //! Callback method to display a warning regarding the current //! initialization state virtual void showWarning(const char*); // ---------------------------------------------------------------------- // Configuration query functions // ---------------------------------------------------------------------- public: /** * Read a single value from the application's configuration. * All configuration query methods throw exceptions when * the query could not be resolved or the requested format * did not match. * This documentation applies to all configGet* functions. * @param query The query * @return The requested value */ bool configGetBool(const std::string& query) const; int configGetInt(const std::string& query) const; double configGetDouble(const std::string& query) const; std::string configGetString(const std::string& query) const; /** * @brief Method that resolves a string variable and produces a * canonicalized absolute pathname. * @param query The query * @return The path */ std::string configGetPath(const std::string& query) const; std::vector configGetBools(const std::string& query) const; std::vector configGetInts(const std::string& query) const; std::vector configGetDoubles(const std::string& query) const; std::vector configGetStrings(const std::string& query) const; /** * Write a singel value to the local section of the clients * configuration file. */ void configSetBool(const std::string& query, bool v); void configSetInt(const std::string& query, int v); void configSetDouble(const std::string& query, double v); void configSetString(const std::string& query, const std::string &v); void configSetBools(const std::string& query, const std::vector&); void configSetInts(const std::string& query, const std::vector&); void configSetDoubles(const std::string& query, const std::vector&); void configSetStrings(const std::string& query, const std::vector&); void configUnset(const std::string& query); bool saveConfiguration(); template struct OptionBinding { enum Flags { NoFlags = 0x00, IsKey = 0x01, InterpretAsPath = 0x02, CLIPrintDefault = 0x04, CLIIsSwitch = 0x08 }; OptionBinding(T &value, int flags, const char *configFileRelativeSymbol, const char *cliGroup = nullptr, const char *cliAbsoluteSymbol = nullptr, const char *cliDesc = nullptr) : value(value) , flags(flags) , configFileRelativeSymbol(configFileRelativeSymbol) , cliGroup(cliGroup) , cliAbsoluteSymbol(cliAbsoluteSymbol) , cliDesc(cliDesc) {} bool isKey() { return flags & IsKey; } bool printDefault() { return flags & CLIPrintDefault; } bool isSwitch() { return flags & CLIIsSwitch; } T &value; int flags; const char *configFileRelativeSymbol; const char *cliGroup; const char *cliAbsoluteSymbol; const char *cliDesc; }; template struct OptionBinding< std::vector > { enum Flags { NoFlags = 0x00, IsKey = 0x01, InterpretAsPath = 0x02, CLIPrintDefault = 0x04, CLIIsSwitch = 0x08 }; typedef std::function InitFunction; OptionBinding(std::vector &value, int flags, const char *configFileRelativeSymbol, const char *cliGroup = nullptr, const char *cliAbsoluteSymbol = nullptr, const char *cliDesc = nullptr, InitFunction ctor = nullptr) : value(value) , ctor(ctor) , flags(flags) , configFileRelativeSymbol(configFileRelativeSymbol) , cliGroup(cliGroup) , cliAbsoluteSymbol(cliAbsoluteSymbol) , cliDesc(cliDesc) {} bool isKey() { return flags & IsKey; } bool printDefault() { return flags & CLIPrintDefault; } bool isSwitch() { return flags & CLIIsSwitch; } std::vector &value; InitFunction ctor; int flags; const char *configFileRelativeSymbol; const char *cliGroup; const char *cliAbsoluteSymbol; const char *cliDesc; }; class OptionLinker { public: OptionLinker() : _stage(None) {} public: void bind(CommandLine *cli) { setStage(BindCli); _external.cli = cli; } void get(const CommandLine *cli) { setStage(GetCli); _external.constCli = cli; } void get(const Application *app) { setStage(GetCfg); _external.constApp = app; } void dump(std::ostream &os) { setStage(Print); _external.os = &os; } public: // A single non-array option template void visitSingle(V &visitor, OptionBinding &visitedItem) { switch ( _stage ) { case None: break; case BindCli: if ( visitedItem.cliAbsoluteSymbol ) { if ( visitedItem.cliGroup ) _external.cli->addGroup(visitedItem.cliGroup); CliLinkHelper::value>::process(*this, visitedItem); } break; case GetCli: if ( !visitedItem.cliAbsoluteSymbol ) return; if ( !CliGetHelper::value>::process(*this, visitedItem) ) visitor.setError(std::string("Invalid commandline value for ") + visitedItem.cliAbsoluteSymbol); break; case GetCfg: if ( !visitedItem.isKey() && !visitedItem.configFileRelativeSymbol ) return; if ( !CfgLinkHelper::value>::process(*this, visitedItem, visitor.configPrefix) ) visitor.setError("Invalid configuration value for " + Detail::join(visitor.configPrefix, visitedItem.configFileRelativeSymbol)); break; case PutCfg: break; case Print: if ( visitedItem.configFileRelativeSymbol ) *_external.os << Detail::join(visitor.configPrefix, visitedItem.configFileRelativeSymbol); else if ( visitedItem.cliAbsoluteSymbol ) *_external.os << "--" << visitedItem.cliAbsoluteSymbol; else if ( visitedItem.isKey() ) *_external.os << "*KEY*"; else return; *_external.os << ": "; PrintHelper::value>::process(*_external.os, visitedItem.value); *_external.os << std::endl; break; } } // A single array option template void visitSingle(V &visitor, OptionBinding< std::vector > &visitedItem) { switch ( _stage ) { case None: break; case BindCli: if ( visitedItem.cliAbsoluteSymbol ) { if ( visitedItem.cliGroup ) _external.cli->addGroup(visitedItem.cliGroup); CliLinkHelper, IsNativelySupported::value>::process(*this, visitedItem); } break; case GetCli: if ( !visitedItem.cliAbsoluteSymbol ) return; if ( !CliGetHelper, IsNativelySupported::value>::process(*this, visitedItem) ) visitor.setError(std::string("Invalid commandline value for ") + visitedItem.cliAbsoluteSymbol); break; case GetCfg: if ( !visitedItem.configFileRelativeSymbol ) return; if ( !CfgLinkHelper, IsNativelySupported::value>::process(*this, visitedItem, visitor.configPrefix) ) visitor.setError("Invalid configuration value for " + Detail::join(visitor.configPrefix, visitedItem.configFileRelativeSymbol)); break; case PutCfg: break; case Print: if ( visitedItem.configFileRelativeSymbol ) { if ( *visitedItem.configFileRelativeSymbol ) { *_external.os << Detail::join(visitor.configPrefix, visitedItem.configFileRelativeSymbol); } else { *_external.os << visitor.configPrefix; } } else if ( visitedItem.cliAbsoluteSymbol ) *_external.os << "--" << visitedItem.cliAbsoluteSymbol; else if ( visitedItem.isKey() ) *_external.os << "*KEY*"; else return; *_external.os << ": "; PrintHelper, IsNativelySupported::value>::process(*_external.os, visitedItem.value); *_external.os << std::endl; break; } } // An array option consisting of composites template void visitMultiple(V &visitor, OptionBinding< std::vector > &visitedItem) { switch ( _stage ) { case None: break; case BindCli: case GetCli: visitor.push(visitedItem); for ( size_t i = 0; i < visitedItem.value.size(); ++i ) visitedItem.value[i].accept(visitor); visitor.pop(); break; case GetCfg: try { std::vector items; items = Detail::getConfig< std::vector >( _external.constApp, Detail::join( visitor.configPrefix, visitedItem.configFileRelativeSymbol ), false ); std::string oldKey = _key; visitor.push(visitedItem); for ( size_t i = 0; i < items.size(); ++i ) { T value; if ( visitedItem.ctor ) { visitedItem.ctor(value); } OptionBinding item(value, false, items[i].c_str()); std::string oldKey = _key; visitor.push(item); _key = items[i]; item.value.accept(visitor); if ( visitor.success() ) visitedItem.value.push_back(value); visitor.pop(); _key = oldKey; } visitor.pop(); _key = oldKey; } catch ( ... ) {} break; case PutCfg: break; case Print: if ( visitedItem.configFileRelativeSymbol ) { *_external.os << Detail::join(visitor.configPrefix, visitedItem.configFileRelativeSymbol); *_external.os << ": "; if ( visitedItem.value.empty() ) *_external.os << "[]" << std::endl; else { *_external.os << "[" << visitedItem.value.size() << "]" << std::endl; std::string oldKey = _key; visitor.push(visitedItem); for ( size_t i = 0; i < visitedItem.value.size(); ++i ) { *_external.os << "{" << std::endl; visitedItem.value[i].accept(visitor); *_external.os << "}" << std::endl; } visitor.pop(); _key = oldKey; } } break; } } // Helpers private: template T key() const { return Generic::Detail::MustMatch::get(_key); } template struct IsNativelySupported { enum { value = Generic::Detail::IsClassType::value ? ( boost::is_same::value ? 1 : 0 ) : 1 }; }; template struct CliLinkHelper {}; template struct CliLinkHelper { template static void process(P &proc, OptionBinding &visitedItem) { using namespace Seiscomp::Core; proc._proxyValueStore[&visitedItem.value] = toString(visitedItem.value); proc._external.cli->addOption( visitedItem.cliGroup?visitedItem.cliGroup:"Generic", visitedItem.cliAbsoluteSymbol, visitedItem.cliDesc, &proc._proxyValueStore[&visitedItem.value], visitedItem.printDefault() ); } }; template struct CliLinkHelper { template static void process(P &proc, OptionBinding &visitedItem) { if ( visitedItem.isSwitch() ) { proc._external.cli->addOption( visitedItem.cliGroup?visitedItem.cliGroup:"Generic", visitedItem.cliAbsoluteSymbol, visitedItem.cliDesc ); } else { proc._external.cli->addOption( visitedItem.cliGroup?visitedItem.cliGroup:"Generic", visitedItem.cliAbsoluteSymbol, visitedItem.cliDesc, &visitedItem.value, visitedItem.printDefault() ); } } }; template struct CliLinkHelper,1> { template static void process(P &proc, OptionBinding< std::vector > &visitedItem) { proc._external.cli->addOption( visitedItem.cliGroup?visitedItem.cliGroup:"Generic", visitedItem.cliAbsoluteSymbol, visitedItem.cliDesc, &visitedItem.value ); } }; template struct CliGetHelper {}; template struct CliGetHelper { template static bool process(P &proc, OptionBinding &visitedItem) { using namespace Seiscomp::Core; try { bool hasOption = false; const char *s = strchr(visitedItem.cliAbsoluteSymbol, ','); if ( s ) { size_t len = s - visitedItem.cliAbsoluteSymbol; s = visitedItem.cliAbsoluteSymbol; Core::trim(s, len); hasOption = proc._external.constCli->hasOption(std::string(s, len)); } else hasOption = proc._external.constCli->hasOption(visitedItem.cliAbsoluteSymbol); if ( hasOption ) return fromString(visitedItem.value, proc._proxyValueStore[&visitedItem.value]); else return true; } catch ( std::exception & ) { return true; } } }; template struct CliGetHelper { template static bool process(P &proc, OptionBinding &visitedItem) { if ( visitedItem.cliAbsoluteSymbol && visitedItem.isSwitch() ) { const char *s = strchr(visitedItem.cliAbsoluteSymbol, ','); if ( s ) { size_t len = static_cast(s - visitedItem.cliAbsoluteSymbol); s = visitedItem.cliAbsoluteSymbol; Core::trim(s, len); if ( proc._external.constCli->hasOption(std::string(s, len)) ) { visitedItem.value = true; } } else if ( proc._external.constCli->hasOption(visitedItem.cliAbsoluteSymbol) ) { visitedItem.value = true; } } return true; } }; template struct CliGetHelper,1> { template static bool process(P &, OptionBinding< std::vector > &) { return true; } }; template struct CfgLinkHelper {}; template struct CfgLinkHelper { template static bool process(P &proc, OptionBinding &visitedItem, const std::string &prefix) { try { std::string tmp; if ( visitedItem.isKey() ) tmp = proc.template key(); else tmp = Detail::getConfig( proc._external.constApp, Detail::join(prefix, visitedItem.configFileRelativeSymbol), visitedItem.flags & OptionBinding::InterpretAsPath ); return fromString(visitedItem.value, tmp); } catch ( ... ) {} return true; } }; template struct CfgLinkHelper,0> { template static bool process(P &proc, OptionBinding< std::vector > &visitedItem, const std::string &prefix) { try { std::vector tmp; if ( visitedItem.isKey() ) tmp = proc.template key >(); else tmp = Detail::getConfig >( proc._external.constApp, Detail::join(prefix, visitedItem.configFileRelativeSymbol), visitedItem.flags & OptionBinding< std::vector >::InterpretAsPath ); visitedItem.value.resize(tmp.size()); for ( size_t i = 0; i < tmp.size(); ++i ) { if ( !fromString(visitedItem.value[i], tmp[i]) ) return false; } } catch ( ... ) {} return true; } }; template struct CfgLinkHelper { template static bool process(P &proc, OptionBinding &visitedItem, const std::string &prefix) { if ( visitedItem.isKey() ) visitedItem.value = proc.template key(); else { try { visitedItem.value = Detail::getConfig( proc._external.constApp, Detail::join(prefix, visitedItem.configFileRelativeSymbol), visitedItem.flags & OptionBinding::InterpretAsPath ); } catch ( ... ) {} } return true; } }; template struct PrintHelper {}; template struct PrintHelper { static void process(std::ostream &os, const T &value) { os << toString(value); } }; template struct PrintHelper,0> { static void process(std::ostream &os, const std::vector &value) { if ( value.empty() ) { os << "[]"; return; } for ( size_t i = 0; i < value.size(); ++i ) { if ( i ) os << ", "; PrintHelper::process(os, value[i]); } } }; template struct PrintHelper { static void process(std::ostream &os, const T &value) { os << value; } }; template struct PrintHelper,1> { static void process(std::ostream &os, const std::vector &value) { if ( value.empty() ) os << "[]"; else os << Core::toString(value); } }; private: enum Stage { None, BindCli, GetCli, GetCfg, PutCfg, Print }; void setStage(Stage s) { _stage = s; } Stage _stage; std::string _key; //!< The current array item key value std::map _proxyValueStore; // Output structures depending on the stage union { std::ostream *os; CommandLine *cli; const CommandLine *constCli; Application *app; const Application *constApp; } _external; }; typedef Generic::SettingsVisitor< OptionBinding, OptionLinker > SettingsLinker; class AbstractSettings { public: virtual ~AbstractSettings() {} public: virtual void accept(SettingsLinker &linker) = 0; template static OptionBinding key(T &boundValue) { return OptionBinding(boundValue, OptionBinding::IsKey, nullptr); } template static OptionBinding cfg(T &boundValue, const char *name) { return OptionBinding(boundValue, 0, name); } template static OptionBinding< std::vector > cfg(std::vector &boundValue, const char *name, typename OptionBinding< std::vector >::InitFunction ctor = nullptr) { return OptionBinding< std::vector >(boundValue, 0, name, nullptr, nullptr, nullptr, ctor); } template static OptionBinding cfgAsPath(T &boundValue, const char *name) { return OptionBinding(boundValue, OptionBinding::InterpretAsPath, name); } template static OptionBinding cli(T &boundValue, const char *group, const char *option, const char *desc, bool default_ = false, bool switch_ = false) { int flags = 0; if ( default_ ) flags |= OptionBinding::CLIPrintDefault; if ( switch_ ) flags |= OptionBinding::CLIIsSwitch; return OptionBinding(boundValue, flags, nullptr, group, option, desc); } template static OptionBinding cliAsPath(T &boundValue, const char *group, const char *option, const char *desc, bool default_ = false, bool switch_ = false) { int flags = OptionBinding::InterpretAsPath; if ( default_ ) flags |= OptionBinding::CLIPrintDefault; if ( switch_ ) flags |= OptionBinding::CLIIsSwitch; return OptionBinding(boundValue, flags, nullptr, group, option, desc); } static OptionBinding cliSwitch(bool &boundValue, const char *group, const char *option, const char *desc) { return OptionBinding(boundValue, OptionBinding::CLIIsSwitch, nullptr, group, option, desc); } }; void bindSettings(AbstractSettings *settings); // ---------------------------------------------------------------------- // Private functions // ---------------------------------------------------------------------- private: std::string argumentStr(const std::string &query) const; bool parseCommandLine(); int acquireLockfile(const std::string &lockfile); void initCommandLine(); bool initLogging(); // ---------------------------------------------------------------------- // Implementation // ---------------------------------------------------------------------- protected: static Application *_instance; typedef std::vector ComponentList; SettingsLinker _settingsLinker; static bool _handleCrash; static bool _handleTermination; int _argc; char **_argv; std::string _name; Arguments _arguments; boost::shared_ptr _commandline; Logging::Output *_logger; // Initialization configuration Config::Config _configuration; int _returnCode; bool _exitRequested; std::string _version; std::list _settings; struct BaseSettings : AbstractSettings { BaseSettings(); std::string alternativeConfigFile; bool enableDaemon; std::string crashHandler; std::string lockfile; std::string plugins; std::string certificateStoreDirectory; struct Logging { unsigned int verbosity{2}; bool quiet{false}; bool trace{false}; bool debug{false}; #ifndef WIN32 bool syslog{false}; #endif bool context{false}; int component{-1}; bool toStdout{false}; bool UTC{false}; std::string alternativeLogFile; ComponentList components; struct File { struct Rotator { bool enable{true}; int timeSpan{60 * 60 * 24}; /* one day*/ int archiveSize{7}; /* one week archive */ int maxFileSize{100 * 1024 * 1024}; /* max 100MB per logfile */ void accept(SettingsLinker &linker) { linker & cfg(timeSpan, "timeSpan") & cfg(archiveSize, "archiveSize") & cfg(maxFileSize, "maxFileSize"); } } rotator; void accept(SettingsLinker &linker) { linker & cfg(rotator.enable, "rotator") & cfg(rotator, "rotator"); } } file; void accept(SettingsLinker &linker) { linker & cfg(verbosity, "level") & cfg(context, "context") & cfg(component, "component") & cfg(components, "components") & cfg(syslog, "syslog") & cfg(toStdout, "stderr") & cfg(UTC, "utc") & cfg(file, "file") & cli( quiet, "Verbose", "quiet,q", "Quiet mode: no logging output" ) & cli(verbosity, "Verbose", "verbosity", "Verbosity level [0..4]", false) & cli(context, "Verbose", "print-context", "Print source file and line number", false ) & cli(component, "Verbose", "print-component", "Print the log component (default: file:1, stdout:0)", false ) & cli( components, "Verbose", "component", "Limits the logging to a certain component. " "This option can be given more than once" ) & cli( toStdout, "Verbose", "console", "Send log output to stdout" ) & cli( debug, "Verbose", "debug", "debug mode: --verbosity=4 --console=1", false, true ) & cli( trace, "Verbose", "trace", "trace mode: --verbosity=4 --console=1 --print-component=1 --print-context=1", false, true ) & cli( alternativeLogFile, "Verbose", "log-file", "Use alternative log file" ) & cli( UTC, "Verbose", "log-utc", "Use UTC instead of local timezone" ) #ifndef WIN32 & cli( syslog, "Verbose", "syslog,s", "Use syslog", false, true ) #endif ; } } logging; virtual void accept(SettingsLinker &linker); } _baseSettings; }; inline CommandLine &Application::commandline() { return *_commandline; } inline const CommandLine &Application::commandline() const { return *_commandline; } } } #endif