diff --git a/apps/CMakeLists.txt b/apps/CMakeLists.txt index 100d8fa3e..965907a6d 100644 --- a/apps/CMakeLists.txt +++ b/apps/CMakeLists.txt @@ -149,7 +149,11 @@ if(BUILD_SERVER_APP) add_executable(osvr_server osvr_server.cpp ${OSVR_SERVER_RESOURCE}) - target_link_libraries(osvr_server osvrServer JsonCpp::JsonCpp) + target_link_libraries(osvr_server + osvrServer + boost_program_options + boost_filesystem + JsonCpp::JsonCpp) set_target_properties(osvr_server PROPERTIES FOLDER "OSVR Stock Applications") install(TARGETS osvr_server diff --git a/apps/osvr_server.cpp b/apps/osvr_server.cpp index 1aab193ca..87eea9627 100644 --- a/apps/osvr_server.cpp +++ b/apps/osvr_server.cpp @@ -28,15 +28,22 @@ #include #include #include +#include // Library/third-party includes -// - none +#include +#include +#include +#include // Standard includes #include #include #include +namespace opt = boost::program_options; +namespace fs = boost::filesystem; + static osvr::server::ServerPtr server; using ::osvr::util::log::OSVR_SERVER_LOG; @@ -50,16 +57,84 @@ void handleShutdown() { int main(int argc, char *argv[]) { auto log = ::osvr::util::log::make_logger(OSVR_SERVER_LOG); - std::string configName(osvr::server::getDefaultConfigFilename()); - if (argc > 1) { - configName = argv[1]; + std::string configName; // server configuration filename + + opt::options_description optionsAll("All Options"); + opt::options_description optionsVisible("Command Line Options"); + opt::positional_options_description optionsPositional; + + optionsPositional.add("config", -1); + optionsAll.add_options()( + "config", + opt::value(&configName) + ->default_value(osvr::server::getDefaultConfigFilename()), + "server configuration filename"); + optionsVisible.add_options() + ("help", "display this help message") + ("verbose,v", "enable verbose logging") + ("debug,d", "enable debug logging"); + optionsAll.add(optionsVisible); + + opt::variables_map values; + try { + opt::store(opt::command_line_parser(argc, argv) + .options(optionsAll) + .positional(optionsPositional) + .run(), + values); + opt::notify(values); + } catch (opt::invalid_command_line_syntax &e) { + log->error() << e.what(); + return 1; + } catch (opt::unknown_option &e) { + log->error() << e.what(); // may want to replace boost msg + return 1; + } + + if (values.count("help")) { + std::cout << optionsVisible << std::endl; + return 0; + } + + if (values.count("debug")) { + osvr::util::log::LogRegistry::instance().setLevel(osvr::util::log::LogLevel::trace); + osvr::util::log::LogRegistry::instance().setConsoleLevel(osvr::util::log::LogLevel::trace); + log->trace("Debug logging enabled."); + } else if (values.count("verbose")) { + osvr::util::log::LogRegistry::instance().setLevel(osvr::util::log::LogLevel::debug); + osvr::util::log::LogRegistry::instance().setConsoleLevel(osvr::util::log::LogLevel::debug); + log->debug("Verbose logging enabled."); + } + + configName = values["config"].as(); + + boost::optional configPath(configName); + try { + if (!fs::exists(*configPath)) { + log->warn() << "File '" << configName + << "' not found. Using empty configuration."; + configPath = boost::none; + } else { + if (fs::is_directory(*configPath)) { + log->error() << "'" << configName << "' is a directory."; + return -1; + } else if (!fs::is_regular_file(*configPath)) { + log->error() << "'" << configName << "' is special file."; + return -1; + } + } + } catch (fs::filesystem_error &e) { + log->error() << "Could not open config file at '" << configName << "'."; + log->error() << "Reason " << e.what() << "."; + configPath = boost::none; + } + + if (configPath) { + server = osvr::server::configureServerFromFile(configName); } else { - log->info() - << "Using default config file - pass a filename on the command " - "line to use a different one."; + server = osvr::server::configureServerFromString("{ }"); } - server = osvr::server::configureServerFromFile(configName); if (!server) { return -1; } diff --git a/inc/osvr/Server/ConfigureServerFromFile.h b/inc/osvr/Server/ConfigureServerFromFile.h index 8d31deb9d..9c75fbd17 100644 --- a/inc/osvr/Server/ConfigureServerFromFile.h +++ b/inc/osvr/Server/ConfigureServerFromFile.h @@ -38,6 +38,7 @@ #include #include #include +#include namespace osvr { namespace server { @@ -48,24 +49,15 @@ namespace server { /// @brief This is the basic common code of a server app's setup, ripped out /// of the main server app to make alternate server-acting apps simpler to /// develop. - inline ServerPtr configureServerFromFile(std::string const &configName) { + inline ServerPtr configureServerFromString(std::string const &json) { auto log = ::osvr::util::log::make_logger(::osvr::util::log::OSVR_SERVER_LOG); ServerPtr ret; - log->info() << "Using config file '" << configName << "'."; - std::ifstream config(configName); - if (!config.good()) { - log->error() << "Could not open config file!"; - log->error() << "Searched in the current directory; file may be " - "misspelled, missing, or in a different directory."; - return nullptr; - } - osvr::server::ConfigureServer srvConfig; log->info() << "Constructing server as configured..."; try { - srvConfig.loadConfig(config); + srvConfig.loadConfig(json); ret = srvConfig.constructServer(); } catch (std::exception &e) { log->error() @@ -151,6 +143,26 @@ namespace server { return ret; } + /// @Brief Convenience wrapper for configureServerFromString(). + inline ServerPtr configureServerFromFile(std::string const &configName) { + auto log = + ::osvr::util::log::make_logger(::osvr::util::log::OSVR_SERVER_LOG); + + ServerPtr ret; + log->info() << "Using config file '" << configName << "'."; + std::ifstream config(configName); + if (!config.good()) { + log->error() << "Could not open config file!"; + log->error() << "Searched in the current directory; file may be " + "misspelled, missing, or in a different directory."; + return nullptr; + } + + std::stringstream sstr; + sstr << config.rdbuf(); + return configureServerFromString(sstr.str()); + } + } // namespace server } // namespace osvr diff --git a/inc/osvr/Util/LogRegistry.h b/inc/osvr/Util/LogRegistry.h index 98085b5c6..3d8d98f7c 100644 --- a/inc/osvr/Util/LogRegistry.h +++ b/inc/osvr/Util/LogRegistry.h @@ -29,6 +29,7 @@ // Internal Includes #include // for LoggerPtr forward declaration #include +#include // Library/third-party includes // - none @@ -57,12 +58,12 @@ namespace util { class LogRegistry { public: - LogRegistry(LogRegistry const &) = delete; // copy construct - LogRegistry(LogRegistry &&) = delete; // move construct - LogRegistry &operator=(LogRegistry const &) = delete; // copy assign - LogRegistry &operator=(LogRegistry &&) = delete; // move assign + OSVR_UTIL_EXPORT LogRegistry(LogRegistry const &) = delete; // copy construct + OSVR_UTIL_EXPORT LogRegistry(LogRegistry &&) = delete; // move construct + OSVR_UTIL_EXPORT LogRegistry &operator=(LogRegistry const &) = delete; // copy assign + OSVR_UTIL_EXPORT LogRegistry &operator=(LogRegistry &&) = delete; // move assign - static LogRegistry &instance(std::string const * = nullptr); + OSVR_UTIL_EXPORT static LogRegistry &instance(std::string const * = nullptr); /** * @brief Gets or creates a logger named @c logger_name. @@ -73,7 +74,7 @@ namespace util { * @param logger_name The name of the logger. * */ - LoggerPtr getOrCreateLogger(const std::string &logger_name); + OSVR_UTIL_EXPORT LoggerPtr getOrCreateLogger(const std::string &logger_name); /** * @brief Drops a logger from the registry. @@ -82,7 +83,7 @@ namespace util { * (e.g., goes out of scope). This function is useful if you want to * destroy a logger before the program terminates. */ - void drop(const std::string &name); + OSVR_UTIL_EXPORT void drop(const std::string &name); /** * @brief Removes all the registered loggers from the registry. @@ -90,39 +91,39 @@ namespace util { * Each logger will survive until the last copy of it is destroyed * (e.g., goes out of scope). */ - void dropAll(); + OSVR_UTIL_EXPORT void dropAll(); /** * @brief Flush all sinks manually. */ - void flush(); + OSVR_UTIL_EXPORT void flush(); /** * @brief Sets the output pattern on all registered loggers. */ - void setPattern(const std::string &pattern); + OSVR_UTIL_EXPORT void setPattern(const std::string &pattern); /** * @brief Sets the minimum level of messages to be logged on all * registered loggers. */ - void setLevel(LogLevel severity); + OSVR_UTIL_EXPORT void setLevel(LogLevel severity); /** * @brief Sets the minimum level of messages to be logged to the * console. */ - void setConsoleLevel(LogLevel severity); + OSVR_UTIL_EXPORT void setConsoleLevel(LogLevel severity); - std::string const &getLogFileBaseName() const { + OSVR_UTIL_EXPORT std::string const &getLogFileBaseName() const { return logFileBaseName_; } - bool couldOpenLogFile() const { return sinks_.size() > 1; } + OSVR_UTIL_EXPORT bool couldOpenLogFile() const { return sinks_.size() > 1; } protected: - LogRegistry(std::string const &logFileBaseName); - ~LogRegistry(); + OSVR_UTIL_EXPORT LogRegistry(std::string const &logFileBaseName); + OSVR_UTIL_EXPORT ~LogRegistry(); private: void setLevelImpl(LogLevel severity);