diff --git a/apps/osvr_server.cpp b/apps/osvr_server.cpp index 87eea9627..f62f0f0fd 100644 --- a/apps/osvr_server.cpp +++ b/apps/osvr_server.cpp @@ -29,20 +29,16 @@ #include #include #include +#include // Library/third-party includes #include -#include -#include -#include // Standard includes -#include -#include +#include #include namespace opt = boost::program_options; -namespace fs = boost::filesystem; static osvr::server::ServerPtr server; using ::osvr::util::log::OSVR_SERVER_LOG; @@ -57,19 +53,15 @@ void handleShutdown() { int main(int argc, char *argv[]) { auto log = ::osvr::util::log::make_logger(OSVR_SERVER_LOG); - std::string configName; // server configuration filename + std::vector configPaths; 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() + ("config", "server configuration filename") ("help", "display this help message") ("verbose,v", "enable verbose logging") ("debug,d", "enable debug logging"); @@ -106,36 +98,29 @@ int main(int argc, char *argv[]) { log->debug("Verbose logging enabled."); } - configName = values["config"].as(); + if (values.count("config")) { + std::string configFileArgument = values["config"].as(); + log->info() << "Using config file " << configFileArgument << " from command line argument."; + configPaths = { configFileArgument }; + } else { + log->info() << "Using default config file - pass a filename on the command " + "line to use a different one."; + configPaths = osvr::server::getDefaultConfigFilePaths(); + } - boost::optional configPath(configName); - try { - if (!fs::exists(*configPath)) { - log->warn() << "File '" << configName - << "' not found. Using empty configuration."; - configPath = boost::none; + server = osvr::server::configureServerFromFirstFileInList(configPaths); + if (!server) { + // only attempt to load the empty config if no arguments are passed. + if (!values.count("config")) { + log->info() << "Could not find a valid config file in the default search paths. Using default config object."; + server = osvr::server::configureServerFromString("{ }"); } 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; - } + 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 { - server = osvr::server::configureServerFromString("{ }"); } if (!server) { + log->error() << "Unknown error while creating server."; return -1; } diff --git a/inc/osvr/Server/ConfigFilePaths.h b/inc/osvr/Server/ConfigFilePaths.h new file mode 100644 index 000000000..d0280f5a1 --- /dev/null +++ b/inc/osvr/Server/ConfigFilePaths.h @@ -0,0 +1,45 @@ +/** @file + @brief Platform specific search paths for osvr server config files. + + @date 2017 + + @author + Sensics, Inc. + +*/ + +// Copyright 2017 Sensics, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef INCLUDED_ConfigFilePaths_h_GUID_241E9C9C_0E0E_46B0_9DED_8F8059306192 +#define INCLUDED_ConfigFilePaths_h_GUID_241E9C9C_0E0E_46B0_9DED_8F8059306192 + +// Internal Includes +#include + +// Library/third-party includes +// - none + +// Standard includes +#include +#include + +namespace osvr { +namespace server { + + /// @brief this returns a vector of default server configuration file paths. + OSVR_SERVER_EXPORT std::vector getDefaultConfigFilePaths(); +} +} +#endif // INCLUDED_ConfigFilePaths_h_GUID_241E9C9C_0E0E_46B0_9DED_8F8059306192 diff --git a/inc/osvr/Server/ConfigureServerFromFile.h b/inc/osvr/Server/ConfigureServerFromFile.h index 9c75fbd17..b4cbdc653 100644 --- a/inc/osvr/Server/ConfigureServerFromFile.h +++ b/inc/osvr/Server/ConfigureServerFromFile.h @@ -35,133 +35,31 @@ // - none // Standard includes -#include -#include -#include -#include +#include +#include namespace osvr { namespace server { + inline const char *getDefaultConfigFilename() { return "osvr_server_config.json"; } - /// @brief This is the basic common code of a server app's setup, ripped out + /// @brief This uses a file name to attempt to configure the server with + /// that config file. + /// Pass an empty string to use the default config. + /// 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 configureServerFromString(std::string const &json) { - auto log = - ::osvr::util::log::make_logger(::osvr::util::log::OSVR_SERVER_LOG); - - ServerPtr ret; - osvr::server::ConfigureServer srvConfig; - log->info() << "Constructing server as configured..."; - try { - srvConfig.loadConfig(json); - ret = srvConfig.constructServer(); - } catch (std::exception &e) { - log->error() - << "Caught exception constructing server from JSON config " - "file: " - << e.what(); - return nullptr; - } - - { - log->info() << "Loading auto-loadable plugins..."; - srvConfig.loadAutoPlugins(); - } - - { - log->info() << "Loading plugins..."; - srvConfig.loadPlugins(); - if (!srvConfig.getSuccessfulPlugins().empty()) { - log->info() << "Successfully loaded the following plugins:"; - for (auto const &plugin : srvConfig.getSuccessfulPlugins()) { - log->info() << " - " << plugin; - } - } - if (!srvConfig.getFailedPlugins().empty()) { - log->warn() << "Failed to load the following plugins:"; - for (auto const &pluginError : srvConfig.getFailedPlugins()) { - log->warn() << " - " << pluginError.first << "\t" - << pluginError.second; - } - } - } - - { - log->info() << "Instantiating configured drivers..."; - bool success = srvConfig.instantiateDrivers(); - if (!srvConfig.getSuccessfulInstantiations().empty()) { - log->info() << "Successes:"; - for (auto const &driver : - srvConfig.getSuccessfulInstantiations()) { - log->info() << " - " << driver; - } - } - if (!srvConfig.getFailedInstantiations().empty()) { - log->error() << "Errors:"; - for (auto const &error : srvConfig.getFailedInstantiations()) { - log->error() << " - " << error.first << "\t" - << error.second; - } - } - } - - if (srvConfig.processExternalDevices()) { - log->info() - << "External devices found and parsed from config file."; - } - - if (srvConfig.processRoutes()) { - log->info() << "Routes found and parsed from config file."; - } - - if (srvConfig.processAliases()) { - log->info() << "Aliases found and parsed from config file."; - } + OSVR_SERVER_EXPORT ServerPtr configureServerFromFile(std::string const &configName); - if (srvConfig.processDisplay()) { - log->info() - << "Display descriptor found and parsed from config file."; - } else { - log->info() - << "Using OSVR HDK for display configuration. " - "Did not find an alternate valid 'display' object in config " - "file."; - } + OSVR_SERVER_EXPORT ServerPtr configureServerFromString(std::string const &json); - if (srvConfig.processRenderManagerParameters()) { - log->info() << "RenderManager config found and parsed from the " - "config file."; - } - - log->info() << "Triggering automatic hardware detection..."; - ret->triggerHardwareDetect(); - - 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()); - } + /// @brief This iterates over a vector that contains a list of potential + /// config files, and uses the first working one to create the server + /// instance. + OSVR_SERVER_EXPORT ServerPtr configureServerFromFirstFileInList( + std::vector const &configNames); } // namespace server } // namespace osvr diff --git a/src/osvr/Server/CMakeLists.txt b/src/osvr/Server/CMakeLists.txt index a3943eb08..9b0d6803f 100644 --- a/src/osvr/Server/CMakeLists.txt +++ b/src/osvr/Server/CMakeLists.txt @@ -2,6 +2,7 @@ osvr_setup_lib_vars(Server) set(API "${HEADER_LOCATION}/ConfigureServer.h" + "${HEADER_LOCATION}/ConfigFilePaths.h" "${HEADER_LOCATION}/ConfigureServerFromFile.h" "${HEADER_LOCATION}/Server.h" "${HEADER_LOCATION}/ServerPtr.h" @@ -11,6 +12,8 @@ set(API set(SOURCE ConfigureServer.cpp + ConfigFilePaths.cpp + ConfigureServerFromFile.cpp JSONResolvePossibleRef.h JSONResolvePossibleRef.cpp Server.cpp diff --git a/src/osvr/Server/ConfigFilePaths.cpp b/src/osvr/Server/ConfigFilePaths.cpp new file mode 100644 index 000000000..f5d245dd7 --- /dev/null +++ b/src/osvr/Server/ConfigFilePaths.cpp @@ -0,0 +1,97 @@ +/** @file + @brief Platform specific search paths for osvr server config files. + + @date 2017 + + @author + Sensics, Inc. + +*/ + +// Copyright 2017 Sensics, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Internal Includes +#include +#include +#include +#include + +// Library/third-party includes +#include + +// Standard includes +#include + +namespace osvr { + namespace server { + + inline std::string getUserConfigDirectory() { + namespace fs = boost::filesystem; + using osvr::util::getEnvironmentVariable; + + fs::path configDir; + std::string configSubpath = "config"; + +#if defined(OSVR_LINUX) + // $XDG_CONFIG_HOME defines the base directory relative to which user + // specific non-essential data files should be stored. If + // $XDG_CONFIG_HOME is either not set or empty, a default equal to + // $HOME/.config should be used. + auto xdg_cache_dir = getEnvironmentVariable("XDG_CONFIG_HOME"); + if (xdg_cache_dir) { + configDir = *xdg_cache_dir; + } + else { + auto home_dir = getEnvironmentVariable("HOME"); + configDir = fs::path(*home_dir) / ".config"; + } + configDir /= fs::path("osvr"); +#elif defined(OSVR_MACOSX) + auto home_dir = getEnvironmentVariable("HOME"); + if (home_dir) { + configDir = *home_dir; + } + configDir /= "Library" / fs::path("Application Support") / fs::path("OSVR") / configSubpath; +#elif defined(OSVR_WINDOWS) + /// @todo there's actually a win32 api call to get localappdata + /// that's preferred to the env var. + auto local_app_dir = getEnvironmentVariable("LocalAppData"); + if (local_app_dir) { + configDir = *local_app_dir; + } + else { + configDir = "c:/"; + } + configDir /= fs::path("OSVR") / configSubpath; +#endif + + return configDir.string(); + } + + std::vector getDefaultConfigFilePaths() { + namespace fs = boost::filesystem; + std::vector names; + std::string configFileName = getDefaultConfigFilename(); + + fs::path userConfigDirectory(getUserConfigDirectory()); + auto userConfig = userConfigDirectory / configFileName; + + names.push_back(userConfig.string()); + names.push_back(configFileName); + + return names; + } + } +} \ No newline at end of file diff --git a/src/osvr/Server/ConfigureServer.cpp b/src/osvr/Server/ConfigureServer.cpp index e6c37d920..d175a3cc3 100644 --- a/src/osvr/Server/ConfigureServer.cpp +++ b/src/osvr/Server/ConfigureServer.cpp @@ -47,23 +47,6 @@ namespace osvr { namespace server { - namespace detail { - class StreamPrefixer { - public: - StreamPrefixer(const char *prefix, std::ostream &os) - : m_prefix(prefix), m_os(&os) {} - template std::ostream &operator<<(T val) { - return (*m_os) << m_prefix << val; - } - - private: - const char *m_prefix; - std::ostream *m_os; - }; - - static detail::StreamPrefixer out("[OSVR Server] ", std::cout); - static detail::StreamPrefixer err("[OSVR Server] ", std::cerr); - } // namespace detail inline void printJsonReferenceResolutionAttempts(ResolveRefResult const &refReturn) { diff --git a/src/osvr/Server/ConfigureServerFromFile.cpp b/src/osvr/Server/ConfigureServerFromFile.cpp new file mode 100644 index 000000000..a5d40c872 --- /dev/null +++ b/src/osvr/Server/ConfigureServerFromFile.cpp @@ -0,0 +1,175 @@ +/** @file + @brief Header + + @date 2014 + + @author + Sensics, Inc. + +*/ + +// Copyright 2014 Sensics, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Internal Includes +#include +#include +#include +#include +#include +#include +#include + +// Library/third-party includes +// - none + +// Standard includes +#include +#include +#include +#include +#include + +namespace osvr { +namespace server { + + ServerPtr configureServerFromString(std::string const &json) { + auto log = + ::osvr::util::log::make_logger(::osvr::util::log::OSVR_SERVER_LOG); + + ServerPtr ret; + osvr::server::ConfigureServer srvConfig; + log->info() << "Constructing server as configured..."; + try { + srvConfig.loadConfig(json); + ret = srvConfig.constructServer(); + } + catch (std::exception &e) { + log->error() + << "Caught exception constructing server from JSON config " + "file: " + << e.what(); + return nullptr; + } + + { + log->info() << "Loading auto-loadable plugins..."; + srvConfig.loadAutoPlugins(); + } + + { + log->info() << "Loading plugins..."; + srvConfig.loadPlugins(); + if (!srvConfig.getSuccessfulPlugins().empty()) { + log->info() << "Successfully loaded the following plugins:"; + for (auto const &plugin : srvConfig.getSuccessfulPlugins()) { + log->info() << " - " << plugin; + } + } + if (!srvConfig.getFailedPlugins().empty()) { + log->warn() << "Failed to load the following plugins:"; + for (auto const &pluginError : srvConfig.getFailedPlugins()) { + log->warn() << " - " << pluginError.first << "\t" + << pluginError.second; + } + } + } + + { + log->info() << "Instantiating configured drivers..."; + bool success = srvConfig.instantiateDrivers(); + if (!srvConfig.getSuccessfulInstantiations().empty()) { + log->info() << "Successes:"; + for (auto const &driver : + srvConfig.getSuccessfulInstantiations()) { + log->info() << " - " << driver; + } + } + if (!srvConfig.getFailedInstantiations().empty()) { + log->error() << "Errors:"; + for (auto const &error : srvConfig.getFailedInstantiations()) { + log->error() << " - " << error.first << "\t" + << error.second; + } + } + } + + if (srvConfig.processExternalDevices()) { + log->info() + << "External devices found and parsed from config file."; + } + + if (srvConfig.processRoutes()) { + log->info() << "Routes found and parsed from config file."; + } + + if (srvConfig.processAliases()) { + log->info() << "Aliases found and parsed from config file."; + } + + if (srvConfig.processDisplay()) { + log->info() + << "Display descriptor found and parsed from config file."; + } else { + log->info() + << "Using OSVR HDK for display configuration. " + "Did not find an alternate valid 'display' object in config " + "file."; + } + + if (srvConfig.processRenderManagerParameters()) { + log->info() << "RenderManager config found and parsed from the " + "config file."; + } + + log->info() << "Triggering automatic hardware detection..."; + ret->triggerHardwareDetect(); + + return ret; + } + + ServerPtr configureServerFromFile(std::string const &configName) { + auto log = + ::osvr::util::log::make_logger(::osvr::util::log::OSVR_SERVER_LOG); + + ServerPtr ret; + log->info() << "Attempting to load config file '" << configName << "'."; + std::ifstream config(configName); + if (!config.good()) { + log->error() << "Config file '" << configName << "' not found"; + return nullptr; + } + + std::stringstream sstr; + sstr << config.rdbuf(); + return configureServerFromString(sstr.str()); + } + + ServerPtr configureServerFromFirstFileInList( + std::vector const &configNames) { + auto log = + ::osvr::util::log::make_logger(::osvr::util::log::OSVR_SERVER_LOG); + + for (const auto name : configNames) { + std::ifstream config(name); + if (config.good()) { + return configureServerFromFile(name); + } + } + log->error() << "Could not find a valid config file!"; + return nullptr; + } + +} // namespace server +} // namespace osvr