Skip to content

Allow setting of config file path without requiring environment variables. #234

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions include/lsl/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -227,3 +227,23 @@ extern LIBLSL_C_API double lsl_local_clock();
* no free() method is available (e.g., in some scripting languages).
*/
extern LIBLSL_C_API void lsl_destroy_string(char *s);

/**
* Set the name of the configuration file to be used.
*
* This is a global setting that will be used by all LSL
* after this function is called. If, and only if, this function
* is called before the first call to any other LSL function.
*/
extern LIBLSL_C_API void lsl_set_config_filename(const char *filename);

/**
* Set the content of the configuration file to be used.
*
* This is a global setting that will be used by all LSL
* after this function is called. If, and only if, this function
* is called before the first call to any other LSL function.
*
* @note the configuration content is wiped after LSL has initialized.
*/
extern LIBLSL_C_API void lsl_set_config_content(const char *content);
73 changes: 57 additions & 16 deletions src/api_config.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
#include "api_config.h"
#include "common.h"
#include "util/cast.hpp"
#include "util/inireader.hpp"
#include "util/strfuns.hpp"
#include <algorithm>
#include <cstdlib>
Expand All @@ -10,6 +9,7 @@
#include <loguru.hpp>
#include <mutex>
#include <stdexcept>
#include <sstream>

using namespace lsl;

Expand Down Expand Up @@ -52,9 +52,36 @@ bool file_is_readable(const std::string &filename) {
}

api_config::api_config() {
// first check to see if a config content was provided
if (!api_config_content_.empty()) {
try {
// if so, load it from the content
load_from_content(api_config_content_);
// free the content this can only be called once
api_config_content_.clear();
// config loaded successfully, so return
return;
} catch (std::exception &e) {
LOG_F(ERROR, "Error parsing config content: '%s', rolling back to defaults", e.what());
// clear the content, it was invalid anyway
api_config_content_.clear();
}
}
// otherwise, load the config from a file

// for each config file location under consideration...
std::vector<std::string> filenames;

// NOLINTNEXTLINE(concurrency-mt-unsafe)
if (!api_config_filename_.empty()) {
// if a config file name was set, use it if it is readable
if (file_is_readable(api_config_filename_)) {
filenames.insert(filenames.begin(), api_config_filename_);
} else {
LOG_F(ERROR, "Config file %s not found", api_config_filename_.c_str());
}
}

// NOLINTNEXTLINE(concurrency-mt-unsafe)
if (auto *cfgpath = getenv("LSLAPICFG")) {
std::string envcfg(cfgpath);
Expand Down Expand Up @@ -82,7 +109,6 @@ api_config::api_config() {
load_from_file();
}


void api_config::load_from_file(const std::string &filename) {
try {
INI pt;
Expand All @@ -92,7 +118,35 @@ void api_config::load_from_file(const std::string &filename) {
pt.load(infile);
}
}
api_config::load(pt);
// log config filename only after setting the verbosity level and all config has been read
if (!filename.empty())
LOG_F(INFO, "Configuration loaded from %s", filename.c_str());
else
LOG_F(INFO, "Loaded default config");

} catch (std::exception &e) {
LOG_F(ERROR, "Error parsing config file '%s': '%s', rolling back to defaults",
filename.c_str(), e.what());
// any error: assign defaults
load_from_file();
// and rethrow
throw e;
}
}

void api_config::load_from_content(const std::string &content) {
// load the content into an INI object
INI pt;
if (!content.empty()) {
std::istringstream content_stream(content);
pt.load(content_stream);
}
api_config::load(pt);
LOG_F(INFO, "Configuration loaded from content");
}

void api_config::load(INI &pt) {
// read the [log] settings
int log_level = pt.get("log.level", (int)loguru::Verbosity_INFO);
if (log_level < -3 || log_level > 9)
Expand Down Expand Up @@ -264,20 +318,7 @@ void api_config::load_from_file(const std::string &filename) {
smoothing_halftime_ = pt.get("tuning.SmoothingHalftime", 90.0F);
force_default_timestamps_ = pt.get("tuning.ForceDefaultTimestamps", false);

// log config filename only after setting the verbosity level and all config has been read
if (!filename.empty())
LOG_F(INFO, "Configuration loaded from %s", filename.c_str());
else
LOG_F(INFO, "Loaded default config");

} catch (std::exception &e) {
LOG_F(ERROR, "Error parsing config file '%s': '%s', rolling back to defaults",
filename.c_str(), e.what());
// any error: assign defaults
load_from_file();
// and rethrow
throw e;
}

}

static std::once_flag api_config_once_flag;
Expand Down
57 changes: 54 additions & 3 deletions src/api_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#define API_CONFIG_H

#include "netinterfaces.h"
#include "util/inireader.hpp"
#include <cstdint>
#include <loguru.hpp>
#include <string>
Expand All @@ -14,9 +15,11 @@ namespace lsl {
* A configuration object: holds all the configurable settings of liblsl.
* These settings can be set via a configuration file that is automatically searched
* by stream providers and recipients in a series of locations:
* - lsl_api.cfg
* - ~/lsl_api/lsl_api.cfg
* - /etc/lsl_api/lsl_api.cfg
* - First, the content set via `lsl_set_config_content()`
* - Second, the file set via `lsl_set_config_filename()`
* - Third, the file `lsl_api.cfg` in the current working directory
* - Fourth, the file `lsl_api.cfg` in the home directory (e.g., `~/lsl_api/lsl_api.cfg`)
* - Fifth, the file `lsl_api.cfg` in the system configuration directory (e.g., `/etc/lsl_api/lsl_api.cfg`)
*
* Note that, while in some cases it might seem sufficient to override configurations
* only for a subset of machines involved in a recording session (e.g., the servers),
Expand Down Expand Up @@ -76,6 +79,33 @@ class api_config {
bool allow_ipv6() const { return allow_ipv6_; }
bool allow_ipv4() const { return allow_ipv4_; }



/**
* @brief Set the configuration directly from a string.
*
* This allows passing in configuration content directly rather than from a file.
* This MUST be called before the first call to get_instance() to have any effect.
*/
static void set_api_config_content(const std::string &content) {
api_config_content_ = content;
}

/**
* @brief An additional settings path to load configuration from.
*/
const std::string &api_config_filename() const { return api_config_filename_; }

/**
* @brief Set the config file name used to load the settings.
*
* This MUST be called before the first call to get_instance() to have any effect.
*/
static void set_api_config_filename(const std::string &filename) {
api_config_filename_ = filename;
}


/**
* @brief The range or scope of stream lookup when using multicast-based discovery
*
Expand Down Expand Up @@ -221,6 +251,22 @@ class api_config {
*/
void load_from_file(const std::string &filename = std::string());

/**
* @brief Load a configuration from a string.
* @param content The configuration content to parse
*/
void load_from_content(const std::string &content);

/**
* @brief Load the configuration from an INI object.
* @param pt The INI object to load the configuration from
*/
void load(INI &pt);

// config overrides
static std::string api_config_filename_;
static std::string api_config_content_;

// core parameters
bool allow_ipv6_, allow_ipv4_;
uint16_t base_port_;
Expand Down Expand Up @@ -258,6 +304,11 @@ class api_config {
float smoothing_halftime_;
bool force_default_timestamps_;
};

// initialize configuration file name
inline std::string api_config::api_config_filename_ = "";
inline std::string api_config::api_config_content_ = "";

} // namespace lsl

#endif
12 changes: 12 additions & 0 deletions src/common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,18 @@ LIBLSL_C_API int32_t lsl_protocol_version() {
return lsl::api_config::get_instance()->use_protocol_version();
}

LIBLSL_C_API void lsl_set_config_filename(const char *filename) {
if (filename) {
lsl::api_config::set_api_config_filename(filename);
}
}

LIBLSL_C_API void lsl_set_config_content(const char *content) {
if (content) {
lsl::api_config::set_api_config_content(content);
}
}

LIBLSL_C_API int32_t lsl_library_version() { return LSL_LIBRARY_VERSION; }

LIBLSL_C_API double lsl_local_clock() {
Expand Down