diff --git a/documentation/client.rst b/documentation/client.rst index 77008af..fd75287 100644 --- a/documentation/client.rst +++ b/documentation/client.rst @@ -12,7 +12,7 @@ Configuration ------------- The recommended starting point is creating new context configured from $PVA_* environment variables. -Use `pvxs::server::Config::from_env` and then `pvxs::server::Config::build`. +Use `pvxs::server::Config::fromEnv` and then `pvxs::server::Config::build`. EPICS_PVA_ADDR_LIST A list of destination addresses to which UDP search messages will be sent. @@ -29,7 +29,7 @@ EPICS_PVA_BROADCAST_PORT using namespace pvxs; // Context configured from process environment - client::Context ctxt = client::Config::from_env().build(); + client::Context ctxt = client::Config::fromEnv().build(); Programatic configuration can be accomplished by explicitly filling in a `pvxs::server::Config`. diff --git a/documentation/server.rst b/documentation/server.rst index 0cb36ac..26c2b59 100644 --- a/documentation/server.rst +++ b/documentation/server.rst @@ -19,7 +19,7 @@ The basic recipe to run a server using configuration from the process environmen .. code-block:: c++ - auto serv = server::Config::from_env() + auto serv = server::Config::fromEnv() .build() // call serv.addSource() at least once serv.run(); // run intil SIGINT or serv.interrupt() @@ -35,7 +35,7 @@ provide/claim a given PV name, the Source with the lowest "order" will win. Configuration ------------- -The recommended starting point when configuring a Server is `pvxs::server::Config::from_env` +The recommended starting point when configuring a Server is `pvxs::server::Config::fromEnv` which will use the following environment variables when set. Entries naming multiple environment variables will prefer the left most which is set. diff --git a/documentation/sharedpv.rst b/documentation/sharedpv.rst index b847c20..b74f88e 100644 --- a/documentation/sharedpv.rst +++ b/documentation/sharedpv.rst @@ -28,7 +28,7 @@ A simple usage is: src.add(argv[1], pv); - auto serv = server::Server::Config::from_env() + auto serv = server::Server::Config::fromEnv() .build() .addSource("box", src.source()); diff --git a/example/client.cpp b/example/client.cpp index cbf5e3d..391fdd3 100644 --- a/example/client.cpp +++ b/example/client.cpp @@ -34,7 +34,7 @@ int main(int argc, char* argv[]) logger_config_env(); // Create a client context - client::Context ctxt(client::Config::from_env() + client::Context ctxt(client::Config::fromEnv() .build()); // Fetch current value diff --git a/example/mailbox.cpp b/example/mailbox.cpp index 73814d4..c8c5a8e 100644 --- a/example/mailbox.cpp +++ b/example/mailbox.cpp @@ -85,7 +85,7 @@ int main(int argc, char* argv[]) // Build server which will server this PV // Configure using process environment. - server::Server serv = server::Config::from_env() + server::Server serv = server::Config::fromEnv() .build() .addPV(argv[1], pv); diff --git a/example/spam.cpp b/example/spam.cpp index 0fd4916..0868e8b 100644 --- a/example/spam.cpp +++ b/example/spam.cpp @@ -102,7 +102,7 @@ int main(int argc, char* argv[]) // Build server which will server this PV // Configure using process environment. - server::Server serv = server::Config::from_env() + server::Server serv = server::Config::fromEnv() .build() .addSource("spamsrc", src); diff --git a/example/ticker.cpp b/example/ticker.cpp index e0ba39c..d7cd663 100644 --- a/example/ticker.cpp +++ b/example/ticker.cpp @@ -72,7 +72,7 @@ int main(int argc, char* argv[]) // Build server which will server this PV // Configure using process environment. - server::Server serv = server::Config::from_env() + server::Server serv = server::Config::fromEnv() .build() .addPV(argv[1], pv); diff --git a/src/config.cpp b/src/config.cpp index 5c171cc..7f160d8 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -48,18 +49,41 @@ void split_addr_into(const char* name, std::vector& out, const std: } } -const char* pickenv(const char** picked, std::initializer_list names) +std::string join_addr(const std::vector& in) { - for(auto name : names) { - if(auto val = getenv(name)) { - if(picked) - *picked = name; - return val; - } - } - return nullptr; + std::ostringstream strm; + std::copy(in.begin(), in.end(), std::ostream_iterator(strm, " ")); + return strm.str(); } +struct PickOne { + const std::map& defs; + bool useenv; + + std::string name, val; + + bool operator()(std::initializer_list names) { + for(auto candidate : names) { + if(useenv) { + if(auto eval = getenv(candidate)) { + name = candidate; + val = eval; + return true; + } + + } else { + auto it = defs.find(candidate); + if(it!=defs.end()) { + name = candidate; + val = it->second; + return true; + } + } + } + return false; + } +}; + template struct cleaner { Fn fn; @@ -126,47 +150,50 @@ void removeDups(std::vector& addrs) namespace server { -Config Config::from_env() +static +void _fromDefs(Config& self, const std::map& defs, bool useenv) { - Config ret; + PickOne pickone{defs, useenv}; - const char* name; - - if(const char *env = pickenv(&name, {"EPICS_PVAS_SERVER_PORT", "EPICS_PVA_SERVER_PORT"})) { + if(pickone({"EPICS_PVAS_SERVER_PORT", "EPICS_PVA_SERVER_PORT"})) { try { - ret.tcp_port = parseTo(env); + self.tcp_port = parseTo(pickone.val); }catch(std::exception& e) { - log_err_printf(serversetup, "%s invalid integer : %s", name, e.what()); + log_err_printf(serversetup, "%s invalid integer : %s", pickone.name.c_str(), e.what()); } } - if(const char *env = pickenv(&name, {"EPICS_PVAS_BROADCAST_PORT", "EPICS_PVA_BROADCAST_PORT"})) { + if(pickone({"EPICS_PVAS_BROADCAST_PORT", "EPICS_PVA_BROADCAST_PORT"})) { try { - ret.udp_port = parseTo(env); + self.udp_port = parseTo(pickone.val); }catch(std::exception& e) { - log_err_printf(serversetup, "%s invalid integer : %s", name, e.what()); + log_err_printf(serversetup, "%s invalid integer : %s", pickone.name.c_str(), e.what()); } } - if(const char *env = pickenv(&name, {"EPICS_PVAS_INTF_ADDR_LIST"})) { - split_addr_into(name, ret.interfaces, env, ret.tcp_port); + if(pickone({"EPICS_PVAS_INTF_ADDR_LIST"})) { + split_addr_into(pickone.name.c_str(), self.interfaces, pickone.val, self.tcp_port); } - if(auto env = pickenv(&name, {"EPICS_PVAS_BEACON_ADDR_LIST", "EPICS_PVA_ADDR_LIST"})) { - split_addr_into(name, ret.beaconDestinations, env, ret.udp_port); + if(pickone({"EPICS_PVAS_BEACON_ADDR_LIST", "EPICS_PVA_ADDR_LIST"})) { + split_addr_into(pickone.name.c_str(), self.beaconDestinations, pickone.val, self.udp_port); } - if(const char *env = pickenv(&name, {"EPICS_PVAS_AUTO_BEACON_ADDR_LIST", "EPICS_PVA_AUTO_ADDR_LIST"})) { - if(epicsStrCaseCmp(env, "YES")==0) { - ret.auto_beacon = true; - } else if(epicsStrCaseCmp(env, "NO")==0) { - ret.auto_beacon = false; + if(pickone({"EPICS_PVAS_AUTO_BEACON_ADDR_LIST", "EPICS_PVA_AUTO_ADDR_LIST"})) { + if(epicsStrCaseCmp(pickone.val.c_str(), "YES")==0) { + self.auto_beacon = true; + } else if(epicsStrCaseCmp(pickone.val.c_str(), "NO")==0) { + self.auto_beacon = false; } else { - log_err_printf(serversetup, "%s invalid bool value (YES/NO)", name); + log_err_printf(serversetup, "%s invalid bool value (YES/NO)", pickone.name.c_str()); } } +} - return ret; +Config& Config::applyEnv() +{ + _fromDefs(*this, std::map(), true); + return *this; } Config Config::isolated() @@ -182,6 +209,21 @@ Config Config::isolated() return ret; } +Config& Config::applyDefs(const std::map& defs) +{ + _fromDefs(*this, defs, false); + return *this; +} + +void Config::updateDefs(defs_t& defs) const +{ + defs["EPICS_PVAS_BROADCAST_PORT"] = SB()<& defs, bool useenv) { - Config ret; + PickOne pickone{defs, useenv}; - const char* name; - - if(const char *env = pickenv(&name, {"EPICS_PVA_BROADCAST_PORT"})) { + if(pickone({"EPICS_PVA_BROADCAST_PORT"})) { try { - ret.udp_port = parseTo(env); + self.udp_port = parseTo(pickone.val); }catch(std::exception& e) { - log_err_printf(serversetup, "%s invalid integer : %s", name, e.what()); + log_err_printf(serversetup, "%s invalid integer : %s", pickone.name.c_str(), e.what()); } } - if(ret.udp_port==0u) { + if(self.udp_port==0u) { log_err_printf(serversetup, "ignoring EPICS_PVA_BROADCAST_PORT=%d", 0); - ret.udp_port = 5076; + self.udp_port = 5076; } - if(const char *env = pickenv(&name, {"EPICS_PVA_ADDR_LIST"})) { - split_addr_into(name, ret.addressList, env, ret.udp_port); + if(pickone({"EPICS_PVA_ADDR_LIST"})) { + split_addr_into(pickone.name.c_str(), self.addressList, pickone.val, self.udp_port); } - if(const char *env = pickenv(&name, {"EPICS_PVA_AUTO_ADDR_LIST"})) { - if(epicsStrCaseCmp(env, "YES")==0) { - ret.autoAddrList = true; - } else if(epicsStrCaseCmp(env, "NO")==0) { - ret.autoAddrList = false; + if(pickone({"EPICS_PVA_AUTO_ADDR_LIST"})) { + if(epicsStrCaseCmp(pickone.val.c_str(), "YES")==0) { + self.autoAddrList = true; + } else if(epicsStrCaseCmp(pickone.val.c_str(), "NO")==0) { + self.autoAddrList = false; } else { - log_err_printf(serversetup, "%s invalid bool value (YES/NO)", name); + log_err_printf(serversetup, "%s invalid bool value (YES/NO)", pickone.name.c_str()); } } +} - return ret; +Config& Config::applyEnv() +{ + _fromDefs(*this, std::map(), true); + return *this; +} + +Config& Config::applyDefs(const std::map& defs) +{ + _fromDefs(*this, defs, false); + return *this; +} + +void Config::updateDefs(defs_t& defs) const +{ + defs["EPICS_PVA_BROADCAST_PORT"] = SB()< #include +#include #include #include #include @@ -625,8 +626,23 @@ struct PVXS_API Config { //! Whether to extend the addressList with local interface broadcast addresses. (recommended) bool autoAddrList = true; + // compat + static inline Config from_env() { return Config{}.applyEnv(); } + //! Default configuration using process environment - static Config from_env(); + static inline Config fromEnv() { return Config{}.applyEnv(); } + + //! update using defined EPICS_PVA* environment variables + Config& applyEnv(); + + typedef std::map defs_t; + //! update with definitions as with EPICS_PVA* environment variables + //! Process environment is not changed. + Config& applyDefs(const defs_t& defs); + + //! extract definitions with environment variable names as keys. + //! Process environment is not changed. + void updateDefs(defs_t& defs) const; /** Apply rules to translate current requested configuration * into one which can actually be loaded based on current host network configuration. diff --git a/src/pvxs/server.h b/src/pvxs/server.h index 4a99c65..e603f71 100644 --- a/src/pvxs/server.h +++ b/src/pvxs/server.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -131,13 +132,28 @@ struct PVXS_API Config { //! Server unique ID. Only meaningful in readback via Server::config() std::array guid{}; + // compat + static inline Config from_env() { return Config{}.applyEnv(); } + //! Default configuration using process environment - static Config from_env(); + static inline Config fromEnv() { return Config{}.applyEnv(); } //! Configuration limited to the local loopback interface on a randomly chosen port. //! Suitable for use in self-contained unit-tests. static Config isolated(); + //! update using defined EPICS_PVA* environment variables + Config& applyEnv(); + + typedef std::map defs_t; + //! update with definitions as with EPICS_PVA* environment variables. + //! Process environment is not changed. + Config& applyDefs(const defs_t& def); + + //! extract definitions with environment variable names as keys. + //! Process environment is not changed. + void updateDefs(defs_t& defs) const; + /** Apply rules to translate current requested configuration * into one which can actually be loaded based on current host network configuration. * diff --git a/test/mcat.cpp b/test/mcat.cpp index 477a161..24b9ee1 100644 --- a/test/mcat.cpp +++ b/test/mcat.cpp @@ -152,7 +152,7 @@ int main(int argc, char* argv[]) src->name = argv[optind]; src->fname = argv[optind+1]; - auto serv = server::Config::from_env() + auto serv = server::Config::fromEnv() .build() .addSource("mcat", src); diff --git a/test/testconfig.cpp b/test/testconfig.cpp index d24d711..36e92f3 100644 --- a/test/testconfig.cpp +++ b/test/testconfig.cpp @@ -33,10 +33,10 @@ void testParse() client::Config conf; try { - conf = client::Config::from_env(); - testPass("client::Config::from_env()"); + conf = client::Config::fromEnv(); + testPass("client::Config::fromEnv()"); }catch(std::exception& e){ - testFail("client::Config::from_env() %s : %s", typeid (e).name(), e.what()); + testFail("client::Config::fromEnv() %s : %s", typeid (e).name(), e.what()); } if(testEq(conf.addressList.size(), 2u)) { diff --git a/tools/call.cpp b/tools/call.cpp index dd3ab5b..ce06e0b 100644 --- a/tools/call.cpp +++ b/tools/call.cpp @@ -119,7 +119,7 @@ int main(int argc, char *argv[]) query[pair.first] = pair.second; } - auto ctxt = client::Config::from_env().build(); + auto ctxt = client::Config::fromEnv().build(); if(verbose) std::cout<<"Effective config\n"<