From 9348ebc7f4aaee9a0d97a5ed1399846e09393a00 Mon Sep 17 00:00:00 2001 From: Sang Woo Kim Date: Thu, 16 Jul 2026 21:37:37 +0900 Subject: [PATCH] config: defer port-less server interfaces to bind-time TCP port $EPICS_PVAS_INTF_ADDR_LIST filled the configured TCP port into every port-less interface entry (split_addr_into with defaultPort=tcp_port). When the first interface's port was already in use, build() picked a fallback TCP port, but the remaining interfaces were now pinned to the original (unbindable) port and could not follow -- the multi-interface conflict left the server unusable. Pass defaultPort=0 here, matching the client EPICS_PVA_INTF_ADDR_LIST path. A port-less interface then stays at port 0, and the existing acceptor loop (if(addr.port()==0) addr.setPort(effective.tcp_port)) stamps every such interface with the port actually bound, fallback included. This removes the double application of the default the reviewer identified, and drops the server.cpp change from the earlier revision of this branch. testconfig: server INTF list now round-trips port-less, symmetric with the client assertion. testwild.testconflictifaces exercises the multi-interface fallback through the environment as a user configures it. --- src/config.cpp | 7 ++++++- test/testconfig.cpp | 4 +++- test/testwild.cpp | 34 ++++++++++++++++++++++++++++++++-- 3 files changed, 41 insertions(+), 4 deletions(-) diff --git a/src/config.cpp b/src/config.cpp index fc292ae7b..102581b2b 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -416,7 +416,12 @@ void _fromDefs(Config& self, const std::map& defs, boo } if(pickone({"EPICS_PVAS_INTF_ADDR_LIST"})) { - split_addr_into(pickone.name.c_str(), self.interfaces, pickone.val, self.tcp_port, true); + // defaultPort=0, matching the client EPICS_PVA_INTF_ADDR_LIST path below. + // A port-less interface stays at port 0 so the acceptor loop can follow the + // server onto the port actually bound -- including a conflict fallback chosen + // while binding the first interface. Filling in tcp_port here erases that + // "follow me" signal and strands the interface on the unbindable port. + split_addr_into(pickone.name.c_str(), self.interfaces, pickone.val, 0, true); } if(pickone({"EPICS_PVAS_IGNORE_ADDR_LIST"})) { diff --git a/test/testconfig.cpp b/test/testconfig.cpp index 1a3ef6f96..1cd9d253e 100644 --- a/test/testconfig.cpp +++ b/test/testconfig.cpp @@ -141,7 +141,9 @@ void testDefs() testEq(conf.tcp_port, 5678); testFalse(conf.auto_beacon); testEq(conf.beaconDestinations, std::vector({"1.2.1.2:1234", "4.3.2.1:1234"})); - testEq(conf.interfaces, std::vector({"1.1.1.1:5678", "1.2.3.4:5678"})); + // server INTF list round-trips port-less, matching the client path above + // (a port-less interface follows the server's bound TCP port at bind time). + testEq(conf.interfaces, std::vector({"1.1.1.1", "1.2.3.4"})); } } diff --git a/test/testwild.cpp b/test/testwild.cpp index dc11797a3..a1f2e7a0d 100644 --- a/test/testwild.cpp +++ b/test/testwild.cpp @@ -80,11 +80,39 @@ void testconflict(const char* addr) testNotEq(iconf.tcp_port, fconf.tcp_port)<<"w/ "<