From 10908fdcf18b9831b19aadd8a4b926b654729fae Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 9 Nov 2023 14:05:38 +0100 Subject: [PATCH 1/6] server/upsd.c: setuptcp(): warn if requested to LISTEN on "localhost" by name (in case it misfires for IPv4 vs. IPv6) Signed-off-by: Jim Klimov --- server/upsd.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/server/upsd.c b/server/upsd.c index 5e4b37e7dd..abc7903c8b 100644 --- a/server/upsd.c +++ b/server/upsd.c @@ -276,7 +276,7 @@ static void setuptcp(stype_t *server) int v = 0, one = 1; if (VALID_FD_SOCK(server->sock_fd)) { - /* Alredy bound, e.g. thanks to 'LISTEN *' handling and injection + /* Already bound, e.g. thanks to 'LISTEN *' handling and injection * into the list we loop over */ upsdebugx(6, "setuptcp: SKIP bind to %s port %s: entry already initialized", server->addr, server->port); @@ -284,6 +284,13 @@ static void setuptcp(stype_t *server) } upsdebugx(3, "setuptcp: try to bind to %s port %s", server->addr, server->port); + if (!strcmp(server->addr, "localhost")) { + /* Warn about possible surprises with IPv4 vs. IPv6 */ + upsdebugx(1, + "setuptcp: WARNING: requested to LISTEN on 'localhost' " + "by name - will use the first system-resolved " + "IP address for that"); + } /* Special handling note for `LISTEN * ` directive with the * literal asterisk on systems with RFC-3493 (no relation!) support From da6d48167bde93925255274ad31e156ab972419f Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 9 Nov 2023 14:15:16 +0100 Subject: [PATCH 2/6] server/upsd.c: server_load(): keep track of listenersTotal vs. listenersValid, and base "no listening interface available" diagnosis on the latter, not on the firstaddr->sock_fd [#723] Signed-off-by: Jim Klimov --- NEWS.adoc | 4 ++++ server/upsd.c | 17 ++++++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/NEWS.adoc b/NEWS.adoc index f3452c2598..aaaa7afe4d 100644 --- a/NEWS.adoc +++ b/NEWS.adoc @@ -39,6 +39,10 @@ https://github.com/networkupstools/nut/milestone/10 - (expected) Bug fixes for fallout possible due to "fightwarn" effort in 2.8.0 + - upsd: fixed conditions for "no listening interface available" diagnosis + to check how many listeners we succeeded with, not whether the first one + succeeded or not [#723] + - nut-usbinfo.pl, nut-scanner and libnutscan: * USB VendorID:ProductID support list files generated by the script for different OS frameworks now include a comment with other possibly diff --git a/server/upsd.c b/server/upsd.c index abc7903c8b..30a52e8a82 100644 --- a/server/upsd.c +++ b/server/upsd.c @@ -884,6 +884,7 @@ static void client_readline(nut_ctype_t *client) void server_load(void) { stype_t *server; + size_t listenersTotal = 0, listenersValid = 0; /* default behaviour if no LISTEN address has been specified */ if (!firstaddr) { @@ -903,8 +904,22 @@ void server_load(void) setuptcp(server); } + /* Account separately from setuptcp() because it can edit the list, + * e.g. when handling `LISTEN *` lines. + */ + for (server = firstaddr; server; server = server->next) { + listenersTotal++; + if (VALID_FD_SOCK(server->sock_fd)) { + listenersValid++; + } + } + + upsdebugx(1, "%s: tried to set up %" PRIuSIZE + " listening sockets, succeeded with %" PRIuSIZE, + __func__, listenersTotal, listenersValid); + /* check if we have at least 1 valid LISTEN interface */ - if (INVALID_FD_SOCK(firstaddr->sock_fd)) { + if (!listenersValid) { fatalx(EXIT_FAILURE, "no listening interface available"); } } From c414cfb04fc3c146f12947972097e7f9cdb64ae9 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 9 Nov 2023 14:29:31 +0100 Subject: [PATCH 3/6] server/upsd.c: server_load(): keep track of listenersTotal vs. listenersValid for certain localhost-related string names and IP addresses [#723] Signed-off-by: Jim Klimov --- server/upsd.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/server/upsd.c b/server/upsd.c index 30a52e8a82..5e4ac31b0b 100644 --- a/server/upsd.c +++ b/server/upsd.c @@ -884,7 +884,15 @@ static void client_readline(nut_ctype_t *client) void server_load(void) { stype_t *server; - size_t listenersTotal = 0, listenersValid = 0; + size_t listenersTotal = 0, listenersValid = 0, + listenersLocalhostName = 0, + listenersLocalhostName6 = 0, + listenersLocalhostIPv4 = 0, + listenersLocalhostIPv6 = 0, + listenersValidLocalhostName = 0, + listenersValidLocalhostName6 = 0, + listenersValidLocalhostIPv4 = 0, + listenersValidLocalhostIPv6 = 0; /* default behaviour if no LISTEN address has been specified */ if (!firstaddr) { @@ -912,11 +920,50 @@ void server_load(void) if (VALID_FD_SOCK(server->sock_fd)) { listenersValid++; } + + if (!strcmp(server->addr, "localhost")) { + listenersLocalhostName++; + if (VALID_FD_SOCK(server->sock_fd)) { + listenersValidLocalhostName++; + } + } + + if (!strcmp(server->addr, "localhost6")) { + listenersLocalhostName6++; + if (VALID_FD_SOCK(server->sock_fd)) { + listenersValidLocalhostName6++; + } + } + + if (!strcmp(server->addr, "127.0.0.1")) { + listenersLocalhostIPv4++; + if (VALID_FD_SOCK(server->sock_fd)) { + listenersValidLocalhostIPv4++; + } + } + + if (!strcmp(server->addr, "::1")) { + listenersLocalhostIPv6++; + if (VALID_FD_SOCK(server->sock_fd)) { + listenersValidLocalhostIPv6++; + } + } } upsdebugx(1, "%s: tried to set up %" PRIuSIZE " listening sockets, succeeded with %" PRIuSIZE, __func__, listenersTotal, listenersValid); + upsdebugx(3, "%s: ...of those related to localhost: " + "by name: %" PRIuSIZE " tried, %" PRIuSIZE " succeeded; " + "by name(6): %" PRIuSIZE " tried, %" PRIuSIZE " succeeded; " + "by IPv4 addr: %" PRIuSIZE " tried, %" PRIuSIZE " succeeded; " + "by IPv6 addr: %" PRIuSIZE " tried, %" PRIuSIZE " succeeded", + __func__, + listenersLocalhostName, listenersValidLocalhostName, + listenersLocalhostName6, listenersValidLocalhostName6, + listenersLocalhostIPv4, listenersValidLocalhostIPv4, + listenersLocalhostIPv6, listenersValidLocalhostIPv6 + ); /* check if we have at least 1 valid LISTEN interface */ if (!listenersValid) { From f425e77bac57f7b6bf124f491378099817833f55 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 9 Nov 2023 14:41:22 +0100 Subject: [PATCH 4/6] server/upsd.c: server_load(): if discrepancy of listenersTotal vs. listenersValid amounts to localhost-related addresses, let it slide [#723] Signed-off-by: Jim Klimov --- server/upsd.c | 46 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 42 insertions(+), 4 deletions(-) diff --git a/server/upsd.c b/server/upsd.c index 5e4ac31b0b..588e5b4724 100644 --- a/server/upsd.c +++ b/server/upsd.c @@ -885,6 +885,7 @@ void server_load(void) { stype_t *server; size_t listenersTotal = 0, listenersValid = 0, + listenersTotalLocalhost = 0, listenersValidLocalhost = 0, listenersLocalhostName = 0, listenersLocalhostName6 = 0, listenersLocalhostIPv4 = 0, @@ -923,29 +924,37 @@ void server_load(void) if (!strcmp(server->addr, "localhost")) { listenersLocalhostName++; + listenersTotalLocalhost++; if (VALID_FD_SOCK(server->sock_fd)) { listenersValidLocalhostName++; + listenersValidLocalhost++; } } if (!strcmp(server->addr, "localhost6")) { listenersLocalhostName6++; + listenersTotalLocalhost++; if (VALID_FD_SOCK(server->sock_fd)) { listenersValidLocalhostName6++; + listenersValidLocalhost++; } } if (!strcmp(server->addr, "127.0.0.1")) { listenersLocalhostIPv4++; + listenersTotalLocalhost++; if (VALID_FD_SOCK(server->sock_fd)) { listenersValidLocalhostIPv4++; + listenersValidLocalhost++; } } if (!strcmp(server->addr, "::1")) { listenersLocalhostIPv6++; + listenersTotalLocalhost++; if (VALID_FD_SOCK(server->sock_fd)) { listenersValidLocalhostIPv6++; + listenersValidLocalhost++; } } } @@ -954,11 +963,13 @@ void server_load(void) " listening sockets, succeeded with %" PRIuSIZE, __func__, listenersTotal, listenersValid); upsdebugx(3, "%s: ...of those related to localhost: " - "by name: %" PRIuSIZE " tried, %" PRIuSIZE " succeeded; " - "by name(6): %" PRIuSIZE " tried, %" PRIuSIZE " succeeded; " - "by IPv4 addr: %" PRIuSIZE " tried, %" PRIuSIZE " succeeded; " - "by IPv6 addr: %" PRIuSIZE " tried, %" PRIuSIZE " succeeded", + "overall: %" PRIuSIZE " tried, %" PRIuSIZE " succeeded; " + "by name: %" PRIuSIZE "T/%" PRIuSIZE "S; " + "by name(6): %" PRIuSIZE "T/%" PRIuSIZE "S; " + "by IPv4 addr: %" PRIuSIZE "T/%" PRIuSIZE "S; " + "by IPv6 addr: %" PRIuSIZE "T/%" PRIuSIZE "S", __func__, + listenersTotalLocalhost, listenersValidLocalhost, listenersLocalhostName, listenersValidLocalhostName, listenersLocalhostName6, listenersValidLocalhostName6, listenersLocalhostIPv4, listenersValidLocalhostIPv4, @@ -969,6 +980,33 @@ void server_load(void) if (!listenersValid) { fatalx(EXIT_FAILURE, "no listening interface available"); } + + /* is everything requested - handled okay? */ + if (listenersTotal == listenersValid) + return; + + /* check for edge cases we can let slide */ + if ( (listenersTotal - listenersValid) == + (listenersTotalLocalhost - listenersValidLocalhost) + ) { + /* Note that we can also get into this situation + * when "dual-stack" IPv6 listener also handles + * IPv4 connections, and precludes successful + * setup of the IPv4 listener later. + * + * FIXME? Can we get into this situation the other + * way around - an IPv4 listener precluding the + * IPv6 one, so end-user actually lacks one of the + * requested connection types? + */ + upsdebugx(1, "%s: discrepancy corresponds to " + "addresses related to localhost; assuming " + "that it was attempted under several names " + "which resolved to same IP:PORT socket specs " + "(so only the first one of each succeeded)", + __func__); + return; + } } void server_free(void) From 9647892537e7d8f9503c2cf07ecdc7bffcb9edcc Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 9 Nov 2023 14:45:05 +0100 Subject: [PATCH 5/6] server/upsd.c: server_load(): if listenersTotal != listenersValid, default to fatal start-up failure [#723] Signed-off-by: Jim Klimov --- NEWS.adoc | 5 +++-- server/upsd.c | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/NEWS.adoc b/NEWS.adoc index aaaa7afe4d..51eeb86ab6 100644 --- a/NEWS.adoc +++ b/NEWS.adoc @@ -39,9 +39,10 @@ https://github.com/networkupstools/nut/milestone/10 - (expected) Bug fixes for fallout possible due to "fightwarn" effort in 2.8.0 - - upsd: fixed conditions for "no listening interface available" diagnosis + - upsd: Fixed conditions for "no listening interface available" diagnosis to check how many listeners we succeeded with, not whether the first one - succeeded or not [#723] + succeeded or not. If not all requested (non-localhost) listeners were + available, default to fail the daemon start-up attempt. [#723] - nut-usbinfo.pl, nut-scanner and libnutscan: * USB VendorID:ProductID support list files generated by the script for diff --git a/server/upsd.c b/server/upsd.c index 588e5b4724..86d46f8f7c 100644 --- a/server/upsd.c +++ b/server/upsd.c @@ -1007,6 +1007,8 @@ void server_load(void) __func__); return; } + + fatalx(EXIT_FAILURE, "some listening interfaces were not available"); } void server_free(void) From 4c1fc7ecdcf5b7368656550a12fb822c24214215 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 9 Nov 2023 15:26:09 +0100 Subject: [PATCH 6/6] upsd: introduce support for ALLOW_NOT_ALL_LISTENERS configuration toggle [#723] Signed-off-by: Jim Klimov --- NEWS.adoc | 3 ++- conf/nut.conf.sample | 22 ++++++++++----- conf/upsd.conf.sample | 19 +++++++++++++ docs/config-notes.txt | 10 +++++++ docs/man/nut.conf.txt | 14 ++++++++++ docs/man/upsd.conf.txt | 20 ++++++++++++++ scripts/augeas/nutupsdconf.aug.in | 4 ++- server/conf.c | 15 ++++++++++- server/upsd.c | 45 ++++++++++++++++++++++++++++++- server/upsd.h | 2 +- 10 files changed, 143 insertions(+), 11 deletions(-) diff --git a/NEWS.adoc b/NEWS.adoc index 51eeb86ab6..30fc86d557 100644 --- a/NEWS.adoc +++ b/NEWS.adoc @@ -42,7 +42,8 @@ https://github.com/networkupstools/nut/milestone/10 - upsd: Fixed conditions for "no listening interface available" diagnosis to check how many listeners we succeeded with, not whether the first one succeeded or not. If not all requested (non-localhost) listeners were - available, default to fail the daemon start-up attempt. [#723] + available, default to fail the daemon start-up attempt; support for an + `ALLOW_NOT_ALL_LISTENERS` setting was added to control this behavior. [#723] - nut-usbinfo.pl, nut-scanner and libnutscan: * USB VendorID:ProductID support list files generated by the script for diff --git a/conf/nut.conf.sample b/conf/nut.conf.sample index 4cd84dd054..71d5465d92 100644 --- a/conf/nut.conf.sample +++ b/conf/nut.conf.sample @@ -43,15 +43,25 @@ MODE=none -# Uncomment this to allow starting the service even if `ups.conf` has no device -# sections configured at the moment. This environment variable overrides the -# built-in "false" flag in `upsd`, and an optional same-named default flag that -# can be set in `upsd.conf`. If you want a data server always running, even if -# it initially has nothing to serve (may be live-reloaded later, when devices -# become configured), this option is for you. +# Uncomment this to allow starting the `upsd` data server even if `ups.conf` +# has no device sections configured at the moment. This environment variable +# overrides the built-in "false" flag in `upsd`, and an optional same-named +# default flag that can be set in `upsd.conf`. If you want a data server always +# running, even if it initially has nothing to serve (may be live-reloaded +# later, when devices become configured), this option is for you. #ALLOW_NO_DEVICE=true #export ALLOW_NO_DEVICE +# Uncomment this to allow starting the `upsd` data server even if not all +# `LISTEN` directives can be honoured at the moment. This environment variable +# overrides the built-in "false" flag in `upsd`, and an optional same-named +# default flag that can be set in `upsd.conf`. If you want a data server always +# running, even if it would potentially not serve all clients on every uptime, +# this option is for you (note you would have to restart `upsd` to pick up the +# `LISTEN`ed IP address if it appears later). Probably `LISTEN *` is better. +#ALLOW_NOT_ALL_LISTENERS=true +#export ALLOW_NOT_ALL_LISTENERS + # The optional 'UPSD_OPTIONS' allow to set upsd specific command-line options. # It is ignored when 'MODE' above indicates that no upsd should be running. # It may be redundant in comparison to options which can be set in `upsd.conf`. diff --git a/conf/upsd.conf.sample b/conf/upsd.conf.sample index 277e58417f..dfbdf9e5ba 100644 --- a/conf/upsd.conf.sample +++ b/conf/upsd.conf.sample @@ -44,6 +44,25 @@ # Boolean values 'false', 'no', 'off' and '0' mean that the server should refuse # to start if zero device sections were found in ups.conf. This is the default. +# ======================================================================= +# ALLOW_NOT_ALL_LISTENERS +# ALLOW_NOT_ALL_LISTENERS true +# +# Normally upsd requires that all `LISTEN` directives can be honoured at the +# moment the daemon starts. If your LAN IP address (or host name) used in one +# of the `LISTEN` directives may be not always accessible, and for some reason +# do not want to just `LISTEN *` on the wildcard interface, but e.g. you still +# want to use `upsmon` on `localhost`, this option can help. Note you would +# have to restart `upsd` to pick up the `LISTEN`ed IP address if it appears +# later. +# +# Boolean values 'true', 'yes', 'on' and '1' mean that the server would not +# refuse to start if it can listen on at least one interface. +# +# Boolean values 'false', 'no', 'off' and '0' mean that the server should +# refuse to start if it can not LISTEN on each and every (non-localhost) +# interface found in upsd.conf. This is the default. + # ======================================================================= # STATEPATH # STATEPATH /var/run/nut diff --git a/docs/config-notes.txt b/docs/config-notes.txt index 8ed3269fd9..249441fc8d 100644 --- a/docs/config-notes.txt +++ b/docs/config-notes.txt @@ -356,6 +356,16 @@ but still want the data server to run, respond and report zero devices (e.g. on an automatically managed monitoring deployment), you can enable the `ALLOW_NO_DEVICE true` option in the 'upsd.conf' file. +NOTE: Normally `upsd` requires that at all `LISTEN` directives defined +in the 'upsd.conf' file are honoured (except for mishaps possible with +many names of `localhost`), and refuses to start otherwise. If you want +to allow start-up in cases where at least one but possibly not all of +the `LISTEN` directives were honoured, you can enable the +`ALLOW_NOT_ALL_LISTENERS true` option in the 'upsd.conf' file. +Note you would have to restart `upsd` to pick up the `LISTEN`ed IP address +if it appears later, so probably configuring `LISTEN *` is a better choice +in such cases. + On operating systems with service management frameworks, the data server life-cycle is managed by `nut-server` service. diff --git a/docs/man/nut.conf.txt b/docs/man/nut.conf.txt index 182f04cd01..2df3552645 100644 --- a/docs/man/nut.conf.txt +++ b/docs/man/nut.conf.txt @@ -65,6 +65,20 @@ If you want a data server always running and responding on the network, even if it initially has nothing to serve (may be live-reloaded later, when devices become configured), this option is for you. +*ALLOW_NOT_ALL_LISTENERS*:: +Optional, defaults to `false`. Set this to `true` to allow starting the `upsd` +NUT data server even if not all `LISTEN` directives can be honoured at the +moment. This environment variable overrides the built-in "false" flag in the +`upsd` program, and an optional same-named default flag that can be set in +`upsd.conf`. ++ +If you want a data server always running, even if it would potentially not +serve all clients on every uptime, this option is for you (note you would +have to restart `upsd` to pick up the `LISTEN`ed IP address if it appears +later). ++ +Probably configuring `LISTEN *` is a better choice in such cases. + *UPSD_OPTIONS*:: Optional. Set upsd specific options. See linkman:upsd[8] for more details. It is ignored when 'MODE' above indicates that no upsd diff --git a/docs/man/upsd.conf.txt b/docs/man/upsd.conf.txt index af8582a700..bf269673a0 100644 --- a/docs/man/upsd.conf.txt +++ b/docs/man/upsd.conf.txt @@ -48,6 +48,26 @@ unless the calling environment sets a same-named variable to enforce a value for the current run. One way this can happen is somebody un-commenting it in the 'nut.conf' file used by init-scripts and service unit method scripts. +"ALLOW_NOT_ALL_LISTENERS 'Boolean'":: + +Normally upsd requires that all `LISTEN` directives can be honoured at the +moment the daemon starts. If your LAN IP address (or host name) used in one +of the `LISTEN` directives may be not always accessible, and for some reason +do not want to just `LISTEN *` on the wildcard interface, but e.g. you still +want to use `upsmon` on `localhost`, this option can help. Note you would +have to restart `upsd` to pick up the `LISTEN`ed IP address if it appears +later. ++ +Boolean values 'true', 'yes', 'on' and '1' mean that the server would not +refuse to start if it can listen on at least one interface. ++ +Boolean values 'false', 'no', 'off' and '0' mean that the server should +refuse to start if it can not LISTEN on each and every (non-localhost) +interface found in upsd.conf. This is the default, unless the calling +environment sets a same-named variable to enforce a value for the current run. +One way this can happen is somebody un-commenting it in the 'nut.conf' file +used by init-scripts and service unit method scripts. + "STATEPATH 'path'":: Tell upsd to look for the driver state sockets in 'path' rather diff --git a/scripts/augeas/nutupsdconf.aug.in b/scripts/augeas/nutupsdconf.aug.in index 1592048387..09924cfe94 100644 --- a/scripts/augeas/nutupsdconf.aug.in +++ b/scripts/augeas/nutupsdconf.aug.in @@ -41,6 +41,7 @@ let path = word let upsd_maxage = [ opt_spc . key "MAXAGE" . sep_spc . store num . eol ] let upsd_trackingdelay = [ opt_spc . key "TRACKINGDELAY" . sep_spc . store num . eol ] let upsd_allow_no_device = [ opt_spc . key "ALLOW_NO_DEVICE" . sep_spc . store num . eol ] +let upsd_allow_not_all_listeners = [ opt_spc . key "ALLOW_NOT_ALL_LISTENERS" . sep_spc . store num . eol ] let upsd_statepath = [ opt_spc . key "STATEPATH" . sep_spc . store path . eol ] let upsd_listen = [ opt_spc . key "LISTEN" . sep_spc . [ label "interface" . store ip ] @@ -53,6 +54,7 @@ let upsd_certfile = [ opt_spc . key "CERTFILE" . sep_spc . store path . eol ] * MAXAGE seconds * TRACKINGDELAY seconds * ALLOW_NO_DEVICE Boolean + * ALLOW_NOT_ALL_LISTENERS Boolean * STATEPATH path * LISTEN interface port * Multiple lines each with one LISTEN address (or host name) and an optional @@ -65,7 +67,7 @@ let upsd_certfile = [ opt_spc . key "CERTFILE" . sep_spc . store path . eol ] * LISTEN 2001:0db8:1234:08d3:1319:8a2e:0370:7344 * *************************************************************************) -let upsd_other = upsd_maxage | upsd_trackingdelay | upsd_allow_no_device | upsd_statepath | upsd_listen_list | upsd_maxconn | upsd_certfile +let upsd_other = upsd_maxage | upsd_trackingdelay | upsd_allow_no_device | upsd_allow_not_all_listeners | upsd_statepath | upsd_listen_list | upsd_maxconn | upsd_certfile let upsd_lns = (upsd_other|comment|empty)* diff --git a/server/conf.c b/server/conf.c index bddd0a69ca..0a7df01c64 100644 --- a/server/conf.c +++ b/server/conf.c @@ -202,7 +202,7 @@ static int parse_upsd_conf_args(size_t numargs, char **arg) } } - /* ALLOW_NO_DEVICE */ + /* ALLOW_NO_DEVICE */ if (!strcmp(arg[0], "ALLOW_NO_DEVICE")) { if (isdigit((size_t)arg[1][0])) { allow_no_device = (atoi(arg[1]) != 0); /* non-zero arg is true here */ @@ -215,6 +215,19 @@ static int parse_upsd_conf_args(size_t numargs, char **arg) return 0; } + /* ALLOW_NOT_ALL_LISTENERS */ + if (!strcmp(arg[0], "ALLOW_NOT_ALL_LISTENERS")) { + if (isdigit((size_t)arg[1][0])) { + allow_not_all_listeners = (atoi(arg[1]) != 0); /* non-zero arg is true here */ + return 1; + } + if (parse_boolean(arg[1], &allow_not_all_listeners)) + return 1; + + upslogx(LOG_ERR, "ALLOW_NOT_ALL_LISTENERS has non numeric and non boolean value (%s)!", arg[1]); + return 0; + } + /* MAXCONN */ if (!strcmp(arg[0], "MAXCONN")) { if (isdigit((size_t)arg[1][0])) { diff --git a/server/upsd.c b/server/upsd.c index 86d46f8f7c..bd3a1543f7 100644 --- a/server/upsd.c +++ b/server/upsd.c @@ -85,6 +85,12 @@ int tracking_delay = 3600; */ int allow_no_device = 0; +/* + * Preloaded to ALLOW_NOT_ALL_LISTENERS from upsd.conf or environment variable + * (with higher prio for envvar); defaults to disabled for legacy compat. + */ +int allow_not_all_listeners = 0; + /* preloaded to {OPEN_MAX} in main, can be overridden via upsd.conf */ nfds_t maxconn = 0; @@ -1008,7 +1014,20 @@ void server_load(void) return; } - fatalx(EXIT_FAILURE, "some listening interfaces were not available"); + if (allow_not_all_listeners) { + upslogx(LOG_WARNING, + "WARNING: some listening interfaces were " + "not available, but the ALLOW_NOT_ALL_LISTENERS " + "setting is active"); + } else { + upsdebugx(0, + "Reconcile available NUT server IP addresses " + "and LISTEN configuration, or consider the " + "ALLOW_NOT_ALL_LISTENERS setting!"); + fatalx(EXIT_FAILURE, + "Fatal error: some listening interfaces were " + "not available"); + } } void server_free(void) @@ -2183,6 +2202,30 @@ int main(int argc, char **argv) } } /* scope */ + { /* scope */ + /* As documented above, the ALLOW_NOT_ALL_LISTENERS can be provided via + * envvars and then has higher priority than an upsd.conf setting + */ + const char *envvar = getenv("ALLOW_NOT_ALL_LISTENERS"); + if ( envvar != NULL) { + if ( (!strncasecmp("TRUE", envvar, 4)) || (!strncasecmp("YES", envvar, 3)) || (!strncasecmp("ON", envvar, 2)) || (!strncasecmp("1", envvar, 1)) ) { + /* Admins of this server expressed a desire to serve + * NUT protocol if at least one configured listener + * works (some may be missing and clients using those + * addresses would not be served!) + */ + allow_not_all_listeners = 1; + } else if ( (!strncasecmp("FALSE", envvar, 5)) || (!strncasecmp("NO", envvar, 2)) || (!strncasecmp("OFF", envvar, 3)) || (!strncasecmp("0", envvar, 1)) ) { + /* Admins of this server expressed a desire to serve + * NUT protocol only if all configured listeners work + * (default for least surprise - admins must address + * any configuration inconsistencies!) + */ + allow_not_all_listeners = 0; + } + } + } /* scope */ + /* start server */ server_load(); diff --git a/server/upsd.h b/server/upsd.h index 9d2802cb24..dfe27140ae 100644 --- a/server/upsd.h +++ b/server/upsd.h @@ -97,7 +97,7 @@ int tracking_disable(void); int tracking_is_enabled(void); /* declarations from upsd.c */ -extern int maxage, tracking_delay, allow_no_device; +extern int maxage, tracking_delay, allow_no_device, allow_not_all_listeners; extern nfds_t maxconn; extern char *statepath, *datapath; extern upstype_t *firstups;