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 */
9595int 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+ */
98101nfds_t maxconn = 0 ;
102+ static nfds_t sysmaxconn = 0 ;
99103
100104/* preloaded to STATEPATH in main, can be overridden via upsd.conf */
101105char * statepath = NULL ;
@@ -112,7 +116,7 @@ nut_ctype_t *firstclient = NULL;
112116/* default is to listen on all local interfaces */
113117static stype_t * firstaddr = NULL ;
114118
115- static int opt_af = AF_UNSPEC ;
119+ static int opt_af = AF_UNSPEC ;
116120
117121typedef 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)
21502232int 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