diff --git a/documentation/server.rst b/documentation/server.rst index a412f66..8e3166f 100644 --- a/documentation/server.rst +++ b/documentation/server.rst @@ -23,13 +23,47 @@ The basic recipe to run a server using configuration from the process environmen serv.run(); // run intil SIGINT or serv.interrupt() // could also call serv.start() and later serv.stop() -A useful server will have one or more `pvxs::server::Source` instances added to it with -addSource() method. +A useful server will have one or more `pvxs::server::Source` instances added to it with addSource() method. +Common usage will be with `pvxs::server::StaticSource` and one or more `pvxs::server::SharedPV`. If more than one Source is added, then an order of precedence is established through the "order" argument of addSource(). In the event that more than one Source could 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()` +which will use the following environment variables when set. + +Entries naming multiple environment variables will use prefer the left most which is set. + +EPICS_PVAS_INTF_ADDR_LIST + Space seperated list of local interface addresses to which the server will bind. + Port numbers are parsed and ignore. + Sets `pvxs::server::Config::interfaces` + +EPICS_PVAS_BEACON_ADDR_LIST or EPICS_PVA_ADDR_LIST + Space seperated list of unicast or broadcast addresses. + This list is supplimented all local broadcast addresses if auto-beacon is YES. + Sets `pvxs::server::Config::beaconDestinations` + +EPICS_PVAS_AUTO_BEACON_ADDR_LIST or EPICS_PVA_AUTO_ADDR_LIST + YES or NO. + Sets `pvxs::server::Config::auto_beacon` + +EPICS_PVAS_SERVER_PORT or EPIC_PVAS_SERVER_PORT + Single integer. + Prefered TCP port to bind. + If already in use then a random port will be choosen. + Sets `pvxs::server::Config::tcp_port` + +EPICS_PVAS_BROADCAST_PORT or EPICS_PVA_BROADCAST_PORT + Single integer. + UDP port to bind. + If already in use, then an exception is thrown. + Sets `pvxs::server::Config::udp_port` + .. doxygenstruct:: pvxs::server::Config :members: diff --git a/src/pvxs/server.h b/src/pvxs/server.h index 6e48389..f20e841 100644 --- a/src/pvxs/server.h +++ b/src/pvxs/server.h @@ -28,17 +28,17 @@ class Server; //! Configuration for a Server struct Config { - //! List of network interface addresses to which this server will bind. + //! List of network interface addresses (**not** host names) to which this server will bind. //! interfaces.empty() treated as an alias for "0.0.0.0", which may also be given explicitly. //! Port numbers are optional and unused (parsed and ignored) std::vector interfaces; - //! Addresses to which (UDP) beacons message will be sent. + //! Addresses (**not** host names) to which (UDP) beacons message will be sent. //! May include broadcast and/or unicast addresses. //! Supplimented iif auto_beacon==true std::vector beaconDestinations; //! TCP port to bind. Default is 5075. May be zero. unsigned short tcp_port; - //! UDP port to bind. Default is 5076. May not be zero, cf. Server::config() to find allocated port. + //! UDP port to bind. Default is 5076. May be zero, cf. Server::config() to find allocated port. unsigned short udp_port; //! Whether to populate the beacon address list automatically. (recommended) bool auto_beacon; diff --git a/src/server.cpp b/src/server.cpp index 31ac641..2538384 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -42,47 +43,77 @@ DEFINE_LOGGER(serversetup, "pvxs.server.setup"); DEFINE_LOGGER(serverio, "pvxs.server.io"); namespace { -void split_into(std::vector& out, const char *inp) +void split_addr_into(const char* name, std::vector& out, const char *inp) { std::regex word("\\s*(\\S+)(.*)"); std::cmatch M; while(*inp && std::regex_match(inp, M, word)) { - out.push_back(M[1].str()); + sockaddr_in addr = {}; + if(aToIPAddr(M[1].str().c_str(), 0, &addr)) { + log_err_printf(serversetup, "%s ignoring invalid '%s'\n", name, M[1].str().c_str()); + continue; + } + char buf[24]; + ipAddrToDottedIP(&addr, buf, sizeof(buf)); + out.emplace_back(buf); inp = M[2].first; } } + +const char* pickenv(const char** picked, std::initializer_list names) +{ + for(auto name : names) { + if(auto val = getenv(name)) { + if(picked) + *picked = name; + return val; + } + } + return nullptr; } +} // namespace + Config Config::from_env() { Config ret; ret.udp_port = 5076; - if(const char *env = getenv("EPICS_PVAS_INTF_ADDR_LIST")) { - split_into(ret.interfaces, env); + const char* name; + + if(const char *env = pickenv(&name, {"EPICS_PVAS_INTF_ADDR_LIST"})) { + split_addr_into(name, ret.interfaces, env); } - if(const char *env = getenv("EPICS_PVAS_BEACON_ADDR_LIST")) { - split_into(ret.beaconDestinations, env); - } else if(const char *env = getenv("EPICS_PVA_ADDR_LIST")) { - split_into(ret.beaconDestinations, env); + if(auto env = pickenv(&name, {"EPICS_PVAS_BEACON_ADDR_LIST", "EPICS_PVA_ADDR_LIST"})) { + split_addr_into(name, ret.beaconDestinations, env); } - // TODO resolve host->IP in interfaces and beaconDestinations - - ret.tcp_port = 5075; - if(const char *env = getenv("EPICS_PVAS_SERVER_PORT")) { - ret.tcp_port = lexical_cast(env); - } else if(const char *env = getenv("EPICS_PVA_SERVER_PORT")) { - ret.tcp_port = lexical_cast(env); + 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; + } else { + log_err_printf(serversetup, "%s invalid bool value (YES/NO)", name); + } } - ret.udp_port = 5076; - if(const char *env = getenv("EPICS_PVAS_BROADCAST_PORT")) { - ret.udp_port = lexical_cast(env); - } else if(const char *env = getenv("EPICS_PVA_BROADCAST_PORT")) { - ret.udp_port = lexical_cast(env); + if(const char *env = pickenv(&name, {"EPICS_PVAS_SERVER_PORT", "EPICS_PVA_SERVER_PORT"})) { + try { + ret.tcp_port = lexical_cast(env); + }catch(std::exception& e) { + log_err_printf(serversetup, "%s invalid integer : %s", name, e.what()); + } + } + + if(const char *env = pickenv(&name, {"EPICS_PVAS_BROADCAST_PORT", "EPICS_PVA_BROADCAST_PORT"})) { + try { + ret.udp_port = lexical_cast(env); + }catch(std::exception& e) { + log_err_printf(serversetup, "%s invalid integer : %s", name, e.what()); + } } return ret; @@ -286,7 +317,7 @@ Server::Pvt::Pvt(Config&& conf) // empty interface address list implies the wildcard // (because no addresses isn't interesting...) if(effective.interfaces.empty()) { - effective.interfaces.push_back("0.0.0.0"); + effective.interfaces.emplace_back("0.0.0.0"); } auto manager = UDPManager::instance();