Skip to content

Commit 432c4ea

Browse files
authored
Merge d2b22f5 into 9bd31ff
2 parents 9bd31ff + d2b22f5 commit 432c4ea

4 files changed

Lines changed: 135 additions & 30 deletions

File tree

NEWS.adoc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ For a complete and more detailed list of changes, please refer to the
1212
ChangeLog file (generated for release archives), or to the Git version
1313
control history for "live" codebase.
1414

15-
1615
PLANNED: Release notes for NUT 2.8.5 - what's new since 2.8.4
1716
-------------------------------------------------------------
1817

@@ -287,6 +286,9 @@ https://github.com/networkupstools/nut/milestone/12
287286
comes into play and breaks things. [issue #661]
288287
* Fixed `LISTEN *` handling for `upsd.exe` in NUT for Windows builds.
289288
[PR #3237]
289+
* Extended processing of `MAXCONN` setting to allow larger values than the
290+
operating system allows, by only waiting for that amount of Unix sockets
291+
or Windows `HANDLE`'s at a time, and moving on to another chunk. [#3302]
290292

291293
- `upsdrvctl` tool updates:
292294
* Make use of `setproctag()` and `getproctag()` to report parent/child

conf/upsd.conf.sample

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,11 @@
121121
# LISTEN address and each client count as one connection. If the server
122122
# runs out of connections, it will no longer accept new incoming client
123123
# connections. Only set this if you know exactly what you're doing.
124+
# Note that on some platforms there may be a smaller amount of file descriptors
125+
# or handles that can be polled in one operation, the server would then poll
126+
# several smaller groups until it handles all the connections it tracks.
127+
# With a large amount of connections this may however impact the delays between
128+
# processing loops, and time before an incoming message is seen and processed.
124129

125130
# =======================================================================
126131
# CERTFILE <certificate file>

docs/man/upsd.conf.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,12 @@ This defaults to maximum number allowed on your system. Each UPS, each
150150
`LISTEN` address and each client count as one connection. If the server
151151
runs out of connections, it will no longer accept new incoming client
152152
connections. Only set this if you know exactly what you're doing.
153+
+
154+
Note that on some platforms there may be a smaller amount of file descriptors
155+
or handles that can be polled in one operation, the server would then poll
156+
several smaller groups until it handles all the connections it tracks.
157+
With a large amount of connections this may however impact the delays between
158+
processing loops, and time before an incoming message is seen and processed.
153159

154160
*CERTFILE 'certificate file'*::
155161

server/upsd.c

Lines changed: 121 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
2008 Arjen de Korte <adkorte-guest@alioth.debian.org>
66
2011 - 2012 Arnaud Quette <arnaud.quette.free.fr>
77
2019 Eaton (author: Arnaud Quette <ArnaudQuette@eaton.com>)
8-
2020 - 2025 Jim Klimov <jimklimov+nut@gmail.com>
8+
2020 - 2026 Jim Klimov <jimklimov+nut@gmail.com>
99
1010
This program is free software; you can redistribute it and/or modify
1111
it under the terms of the GNU General Public License as published by
@@ -94,8 +94,12 @@ int allow_no_device = 0;
9494
*/
9595
int allow_not_all_listeners = 0;
9696

97-
/* preloaded to {OPEN_MAX} in main, can be overridden via upsd.conf */
97+
/* preloaded to POSIX sysconf(_SC_OPEN_MAX) or WIN32 MAX_WAIT_OBJECTS in main
98+
* and elsewhere, the run-time value can be overridden via upsd.conf `MAXCONN`
99+
* option (may cause partial waits chunk by chunk, if sysmaxconn is smaller).
100+
*/
98101
nfds_t maxconn = 0;
102+
static nfds_t sysmaxconn = 0;
99103

100104
/* preloaded to STATEPATH in main, can be overridden via upsd.conf */
101105
char *statepath = NULL;
@@ -112,7 +116,7 @@ nut_ctype_t *firstclient = NULL;
112116
/* default is to listen on all local interfaces */
113117
static stype_t *firstaddr = NULL;
114118

115-
static int opt_af = AF_UNSPEC;
119+
static int opt_af = AF_UNSPEC;
116120

117121
typedef enum {
118122
DRIVER = 1,
@@ -1224,30 +1228,37 @@ static void poll_reload(void)
12241228
size_t maxalloc;
12251229

12261230
#ifndef WIN32
1231+
/* Not likely this would change, but refresh just in case */
12271232
ret = sysconf(_SC_OPEN_MAX);
12281233
#else /* WIN32 */
12291234
ret = (long)MAXIMUM_WAIT_OBJECTS;
12301235
#endif /* WIN32 */
12311236

1232-
if ((intmax_t)ret < (intmax_t)maxconn) {
1237+
if (ret < 1) {
1238+
/* TOTHINK: Not fail, but use a conservative fallback number? */
12331239
fatalx(EXIT_FAILURE,
1240+
"System reported an absurd value %ld as maximum number of connections.\n"
1241+
"The server won't start until this problem is resolved.\n", ret);
1242+
}
1243+
1244+
if ((intmax_t)ret < (intmax_t)maxconn) {
1245+
upslogx(LOG_WARNING,
12341246
"Your system limits the maximum number of connections to %ld\n"
1235-
"but you requested %" PRIdMAX ". The server won't start until this\n"
1236-
"problem is resolved.\n", ret, (intmax_t)maxconn);
1247+
"but you requested %" PRIdMAX ". The server may handle connections\n"
1248+
"in smaller groups, maybe affecting efficiency and response time.\n",
1249+
ret, (intmax_t)maxconn);
12371250
}
12381251

1252+
sysmaxconn = (nfds_t)ret;
1253+
12391254
if (1 > maxconn) {
12401255
fatalx(EXIT_FAILURE,
12411256
"You requested %" PRIdMAX " as maximum number of connections.\n"
12421257
"The server won't start until this problem is resolved.\n", (intmax_t)maxconn);
12431258
}
12441259

1245-
#ifndef WIN32
12461260
/* How many items can we stuff into the array? */
12471261
maxalloc = SIZE_MAX / sizeof(void *);
1248-
#else /* WIN32 */
1249-
maxalloc = MAXIMUM_WAIT_OBJECTS;
1250-
#endif /* WIN32 */
12511262
if ((uintmax_t)maxalloc < (uintmax_t)maxconn) {
12521263
fatalx(EXIT_FAILURE,
12531264
"You requested %" PRIdMAX " as maximum number of connections, but we can only allocate %" PRIuSIZE ".\n"
@@ -1497,14 +1508,14 @@ static void mainloop(void)
14971508
nfds_t i;
14981509
#else /* WIN32 */
14991510
DWORD ret;
1500-
pipe_conn_t * conn;
1511+
pipe_conn_t *conn;
15011512
#endif /* WIN32 */
15021513

15031514
size_t nfds_wanted = 0, /* Connections we looked at (some may be invalid) */
15041515
nfds_considered = 0; /* Connections we wanted to poll (but might be over maxconn limit) */
15051516
nfds_t nfds = 0;
15061517
upstype_t *ups;
1507-
nut_ctype_t *client, *cnext;
1518+
nut_ctype_t *client, *cnext;
15081519
stype_t *server;
15091520
time_t now;
15101521

@@ -1644,18 +1655,53 @@ static void mainloop(void)
16441655
upsdebugx(2, "%s: polling %" PRIdMAX " filedescriptors; some stats: "
16451656
"considered %" PRIdMAX " connections, "
16461657
"wanted to actually poll %" PRIdMAX
1647-
" and was constrained by maxconn=%" PRIdMAX,
1658+
" and was constrained by maxconn=%" PRIdMAX
1659+
" and chunked by sysmaxconn=%" PRIdMAX,
16481660
__func__, (intmax_t)nfds, (intmax_t)nfds_considered,
1649-
(intmax_t)nfds_wanted, (intmax_t)maxconn);
1661+
(intmax_t)nfds_wanted, (intmax_t)maxconn, (intmax_t)sysmaxconn);
16501662

16511663
if (nfds_wanted != nfds || nfds_wanted >= maxconn) {
16521664
upslogx(LOG_ERR, "upsd polling %" PRIdMAX " filedescriptors,"
16531665
" but wanted to poll %" PRIdMAX
1654-
" and was constrained by maxconn=%" PRIdMAX,
1666+
" and was constrained by maxconn=%" PRIdMAX
1667+
" (see upsd.conf MAXCONN setting to adjust)",
16551668
(intmax_t)nfds, (intmax_t)nfds_wanted, (intmax_t)maxconn);
16561669
}
16571670

1658-
ret = poll(fds, nfds, 2000);
1671+
if (nfds <= sysmaxconn) {
1672+
ret = poll(fds, nfds, 2000);
1673+
} else {
1674+
/* Chunk it all; try to fit into same 2 sec as above.
1675+
* Note that nfds at the moment may be smaller than
1676+
* maxconn (allocated array size).
1677+
*/
1678+
size_t last_chunk = nfds % sysmaxconn, chunk,
1679+
chunks = nfds / sysmaxconn + (last_chunk ? 1 : 0);
1680+
int poll_TO = 2000 / chunks, tmpret;
1681+
1682+
if (poll_TO < 10)
1683+
poll_TO = 10;
1684+
1685+
ret = 0;
1686+
for (chunk = 0; chunk < chunks; chunk++) {
1687+
upsdebugx(5,
1688+
"%s: chunked filedescriptor polling #%" PRIuSIZE
1689+
" of %" PRIuSIZE " chunks, with %d hits so far",
1690+
__func__, chunk, chunks, ret);
1691+
tmpret = poll(&fds[chunk * sysmaxconn],
1692+
(last_chunk && chunk == chunks - 1 ? last_chunk : sysmaxconn),
1693+
poll_TO);
1694+
if (tmpret < 0) {
1695+
upsdebug_with_errno(2,
1696+
"%s: failed during chunked polling, handled %" PRIuSIZE
1697+
" of %" PRIuSIZE " chunks so far, with %d hits",
1698+
__func__, chunk, chunks, ret);
1699+
ret = tmpret;
1700+
break;
1701+
}
1702+
ret += tmpret;
1703+
}
1704+
}
16591705

16601706
if (ret == 0) {
16611707
upsdebugx(2, "%s: no data available", __func__);
@@ -1942,19 +1988,55 @@ static void mainloop(void)
19421988
upsdebugx(2, "%s: wait for %" PRIdMAX " filedescriptors; some stats: "
19431989
"considered %" PRIdMAX " connections, "
19441990
"wanted to actually poll %" PRIdMAX
1945-
" and was constrained by maxconn=%" PRIdMAX,
1991+
" and was constrained by maxconn=%" PRIdMAX
1992+
" and chunked by sysmaxconn=%" PRIdMAX,
19461993
__func__, (intmax_t)nfds, (intmax_t)nfds_considered,
1947-
(intmax_t)nfds_wanted, (intmax_t)maxconn);
1994+
(intmax_t)nfds_wanted, (intmax_t)maxconn, (intmax_t)sysmaxconn);
19481995

19491996
if (nfds_wanted != nfds || nfds_wanted >= maxconn) {
19501997
upslogx(LOG_ERR, "upsd polling %" PRIuMAX " filedescriptors,"
19511998
" but wanted to poll %" PRIuMAX
1952-
" and was constrained by maxconn=%" PRIuMAX,
1999+
" and was constrained by maxconn=%" PRIuMAX
2000+
" (see upsd.conf MAXCONN setting to adjust)",
19532001
(uintmax_t)nfds, (uintmax_t)nfds_wanted, (uintmax_t)maxconn);
19542002
}
19552003

19562004
/* https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-waitformultipleobjects */
1957-
ret = WaitForMultipleObjects(nfds,fds,FALSE,2000);
2005+
if (nfds <= sysmaxconn) {
2006+
ret = WaitForMultipleObjects(nfds, fds, FALSE, 2000);
2007+
} else {
2008+
/* Chunk it all; try to fit into same 2 sec as above.
2009+
* Note that nfds at the moment may be smaller than
2010+
* maxconn (allocated array size).
2011+
*/
2012+
size_t last_chunk = nfds % sysmaxconn, chunk,
2013+
chunks = nfds / sysmaxconn + (last_chunk ? 1 : 0);
2014+
DWORD poll_TO = 2000 / chunks, tmpret;
2015+
2016+
if (poll_TO < 10)
2017+
poll_TO = 10;
2018+
2019+
ret = 0;
2020+
for (chunk = 0; chunk < chunks; chunk++) {
2021+
upsdebugx(5,
2022+
"%s: chunked filedescriptor polling #%" PRIuSIZE
2023+
" of %" PRIuSIZE " chunks, with %" PRIu64 " hits so far",
2024+
__func__, chunk, chunks, ret);
2025+
tmpret = WaitForMultipleObjects(
2026+
(last_chunk && chunk == chunks - 1 ? last_chunk : sysmaxconn),
2027+
&fds[chunk * sysmaxconn],
2028+
FALSE, poll_TO);
2029+
if (tmpret < 0) {
2030+
upsdebug_with_errno(2,
2031+
"%s: failed during chunked polling, handled %" PRIuSIZE
2032+
" of %" PRIuSIZE " chunks so far, with %" PRIu64 " hits",
2033+
__func__, chunk, chunks, ret);
2034+
ret = tmpret;
2035+
break;
2036+
}
2037+
ret += tmpret;
2038+
}
2039+
}
19582040

19592041
upsdebugx(6, "%s: wait for filedescriptors done: %" PRIu64, __func__, ret);
19602042

@@ -2150,6 +2232,7 @@ void check_perms(const char *fn)
21502232
int main(int argc, char **argv)
21512233
{
21522234
int i, cmdret = 0, foreground = -1;
2235+
long l;
21532236
#ifndef WIN32
21542237
int cmd = 0;
21552238
pid_t oldpid = -1;
@@ -2284,14 +2367,14 @@ int main(int argc, char **argv)
22842367
}
22852368

22862369
{ /* scoping */
2287-
char *s = getenv("NUT_DEBUG_LEVEL");
2288-
int l;
2289-
if (s && str_to_int(s, &l, 10)) {
2290-
if (l > 0 && nut_debug_level_args < 1) {
2370+
char *s = getenv("NUT_DEBUG_LEVEL");
2371+
int lvl;
2372+
if (s && str_to_int(s, &lvl, 10)) {
2373+
if (lvl > 0 && nut_debug_level_args < 1) {
22912374
upslogx(LOG_INFO, "Defaulting debug verbosity to NUT_DEBUG_LEVEL=%d "
2292-
"since none was requested by command-line options", l);
2293-
nut_debug_level = l;
2294-
nut_debug_level_args = l;
2375+
"since none was requested by command-line options", lvl);
2376+
nut_debug_level = lvl;
2377+
nut_debug_level_args = lvl;
22952378
} /* else follow -D settings */
22962379
} /* else nothing to bother about */
22972380
}
@@ -2447,12 +2530,21 @@ int main(int argc, char **argv)
24472530
#ifndef WIN32
24482531
/* default to system limit (may be overridden in upsd.conf) */
24492532
/* FIXME: Check for overflows (and int size of nfds_t vs. long) - see get_max_pid_t() for example */
2450-
maxconn = (nfds_t)sysconf(_SC_OPEN_MAX);
2533+
l = sysconf(_SC_OPEN_MAX);
24512534
#else /* WIN32 */
24522535
/* hard-coded 64 (from ddk/wdm.h or winnt.h) */
2453-
maxconn = MAXIMUM_WAIT_OBJECTS;
2536+
l = (long)MAXIMUM_WAIT_OBJECTS;
24542537
#endif /* WIN32 */
24552538

2539+
if (l < 1) {
2540+
/* TOTHINK: Not fail, but use a conservative fallback number? */
2541+
fatalx(EXIT_FAILURE,
2542+
"System reported an absurd value %ld as maximum number of connections.\n"
2543+
"The server won't start until this problem is resolved.\n", l);
2544+
}
2545+
2546+
maxconn = sysmaxconn = (nfds_t)l;
2547+
24562548
/* handle upsd.conf */
24572549
load_upsdconf(0); /* 0 = initial */
24582550

0 commit comments

Comments
 (0)