diff mbox series

[1/1] package/nut: bump version to 2.8.3

Message ID 20250511161547.3195183-1-bernd@kuhls.net
State Accepted
Delegated to: Julien Olivain
Headers show
Series [1/1] package/nut: bump version to 2.8.3 | expand

Commit Message

Bernd Kuhls May 11, 2025, 4:15 p.m. UTC
Release notes:
https://github.com/networkupstools/nut/releases/tag/v2.8.3

Remove all patches which are included in this release.

Updated license hash due to upstream commits:
https://github.com/networkupstools/nut/commit/1cd56ce019a0005d2c9083c3eba26af13a9b2581
https://github.com/networkupstools/nut/commit/91585bf708fd5128ca9e23c82800a0c50f39db14
https://github.com/networkupstools/nut/commit/f9a41e7225a472443c34c008fbf4c168bd94b904

Upstream commit https://github.com/networkupstools/nut/commit/4ddc5aedf322a6ff2803aa7cb7509057ac7edbbf
sets PKG_CONFIG_LIBDIR=/dev/null when cross-compiling and the pkg-config
binary lacks the host triplet, but:

"If pkg-config libdir is already defined, we suppose that callers know
 what they're doing and leave it alone ..."

Add PKG_CONFIG_LIBDIR to NUT_CONF_ENV to fix library detections.

Signed-off-by: Bernd Kuhls <bernd@kuhls.net>
---
 ...te-copy-of-telnetlib-for-PyNUTClient.patch | 878 ------------------
 ...est-with-nut_telnetlib-Python-module.patch |  37 -
 ...nut_telnetlib-module-from-source-dir.patch |  39 -
 package/nut/nut.hash                          |   6 +-
 package/nut/nut.mk                            |   7 +-
 5 files changed, 5 insertions(+), 962 deletions(-)
 delete mode 100644 package/nut/0001-Provide-a-private-copy-of-telnetlib-for-PyNUTClient.patch
 delete mode 100644 package/nut/0002-configure-ac-typo-in-path-to-test-with-nut_telnetlib-Python-module.patch
 delete mode 100644 package/nut/0003-configure.ac-be-sure-to-probe-nut_telnetlib-module-from-source-dir.patch

Comments

Julien Olivain May 12, 2025, 7:54 p.m. UTC | #1
On 11/05/2025 18:15, Bernd Kuhls wrote:
> Release notes:
> https://github.com/networkupstools/nut/releases/tag/v2.8.3
> 
> Remove all patches which are included in this release.
> 
> Updated license hash due to upstream commits:
> https://github.com/networkupstools/nut/commit/1cd56ce019a0005d2c9083c3eba26af13a9b2581
> https://github.com/networkupstools/nut/commit/91585bf708fd5128ca9e23c82800a0c50f39db14
> https://github.com/networkupstools/nut/commit/f9a41e7225a472443c34c008fbf4c168bd94b904
> 
> Upstream commit 
> https://github.com/networkupstools/nut/commit/4ddc5aedf322a6ff2803aa7cb7509057ac7edbbf
> sets PKG_CONFIG_LIBDIR=/dev/null when cross-compiling and the 
> pkg-config
> binary lacks the host triplet, but:
> 
> "If pkg-config libdir is already defined, we suppose that callers know
>  what they're doing and leave it alone ..."
> 
> Add PKG_CONFIG_LIBDIR to NUT_CONF_ENV to fix library detections.
> 
> Signed-off-by: Bernd Kuhls <bernd@kuhls.net>

Applied to master, thanks.
diff mbox series

Patch

diff --git a/package/nut/0001-Provide-a-private-copy-of-telnetlib-for-PyNUTClient.patch b/package/nut/0001-Provide-a-private-copy-of-telnetlib-for-PyNUTClient.patch
deleted file mode 100644
index 9273ee517b..0000000000
--- a/package/nut/0001-Provide-a-private-copy-of-telnetlib-for-PyNUTClient.patch
+++ /dev/null
@@ -1,878 +0,0 @@ 
-From 84c8ddef0b70ae14a8867639366501e2988a1092 Mon Sep 17 00:00:00 2001
-From: Jim Klimov <jimklimov+nut@gmail.com>
-Date: Mon, 1 Jul 2024 15:47:46 +0200
-Subject: [PATCH] Provide a fallback copy of telnetlib module for PyNUTClient
- [#2183]
-
-Signed-off-by: Jim Klimov <jimklimov+nut@gmail.com>
-
-Upstream: https://github.com/networkupstools/nut/commit/84c8ddef0b70ae14a8867639366501e2988a1092
-
-Signed-off-by: Bernd Kuhls <bernd@kuhls.net>
----
- configure.ac                           |  17 +-
- docs/nut.dict                          |   3 +-
- scripts/python/Makefile.am             |   1 +
- scripts/python/README.adoc             |  15 +
- scripts/python/module/Makefile.am      |   7 +-
- scripts/python/module/PyNUT.py.in      |  18 +-
- scripts/python/module/nut_telnetlib.py | 683 +++++++++++++++++++++++++
- scripts/python/module/setup.py.in      |   6 +-
- 9 files changed, 746 insertions(+), 8 deletions(-)
- create mode 100644 scripts/python/module/nut_telnetlib.py
-
-diff --git a/configure.ac b/configure.ac
-index d369ea82b8..6f90e50a5e 100644
---- a/configure.ac
-+++ b/configure.ac
-@@ -2447,7 +2447,7 @@ if test x"${nut_with_nut_monitor}" != xno ; then
- fi
- 
- dnl ${nut_with_pynut}: TODO: arg values to request python 2, 3 or both
--AC_MSG_CHECKING([if we can and should install PyNUT module])
-+AC_MSG_CHECKING([if we can and should install PyNUT module (note for warnings from python 3.11 and beyond: we have a fallback nut_telnetlib module just in case)])
- nut_with_pynut_py=""
- nut_with_pynut_py2=""
- nut_with_pynut_py3=""
-@@ -2469,6 +2469,15 @@ if test x"${nut_with_pynut}" != xno \
-         if ${PYTHON3} -c "import telnetlib" \
-         ; then
-             nut_with_pynut_py3="yes"
-+        else
-+            dnl We have a stashed copy from Python 3.10, so
-+            dnl this line essentially checks for presence of
-+            dnl a usable interpreter implementation compatible
-+            dnl with Python 3.x syntax.
-+            if (cd script/python/module && ${PYTHON3} -c "import nut_telnetlib as telnetlib") \
-+            ; then
-+                nut_with_pynut_py3="yes"
-+            fi
-         fi
-     fi
- 
-@@ -2480,6 +2489,12 @@ if test x"${nut_with_pynut}" != xno \
-         if ${PYTHON} -c "import telnetlib" \
-         ; then
-             nut_with_pynut_py="yes"
-+        else
-+            dnl See comments above
-+            if (cd script/python/module && ${PYTHON} -c "import nut_telnetlib as telnetlib") \
-+            ; then
-+                nut_with_pynut_py="yes"
-+            fi
-         fi
-     fi
- fi
-diff --git a/scripts/python/Makefile.am b/scripts/python/Makefile.am
-index 0b83249cd5..a737f50c91 100644
---- a/scripts/python/Makefile.am
-+++ b/scripts/python/Makefile.am
-@@ -70,6 +70,7 @@ NUT_MONITOR_COMMON = \
- 	app/locale/ru/LC_MESSAGES/NUT-Monitor.mo
- 
- PYNUT_COMMON = \
-+	module/nut_telnetlib.py \
- 	module/README.adoc
- 
- # Note: we both distribute and install the generated *.mo translation files
-diff --git a/scripts/python/README.adoc b/scripts/python/README.adoc
-index 72e268063b..8770d4ca06 100644
---- a/scripts/python/README.adoc
-+++ b/scripts/python/README.adoc
-@@ -35,6 +35,21 @@ and protocol support.
- 
- For one practical example, you can research `tests/NIT/nit.sh` in NUT sources.
- 
-+telnetlib
-+^^^^^^^^^
-+
-+Historically, the `PyNUTClient` class relied on `telnetlib` module for socket
-+communications with the NUT data server, which was provided as part of Python
-+core installation with tested 2.6+ and 3.* versions. The module was, however,
-+marked deprecated since Python 3.11 and not provided since 3.13. Due to this,
-+as a quick stop-gap solution, NUT sources provide `nut_telnetlib.py` - a copy
-+of the module from Python 3.10 installation, and no longer should require its
-+presence in the Python deployment.
-+
-+A better solution would be to find or create a new layer for the constrained
-+use-case of NUT client interactions with a data server, which do not need the
-+fully fledged Telnet-like capabilities.
-+
- app
- ~~~
- 
-diff --git a/scripts/python/module/Makefile.am b/scripts/python/module/Makefile.am
-index fc46eab0ad..5eb650969c 100644
---- a/scripts/python/module/Makefile.am
-+++ b/scripts/python/module/Makefile.am
-@@ -16,7 +16,7 @@ check-local:
- tox: dist .pypi-tools-tox
- 	tox
- 
--EXTRA_DIST = tox.ini MANIFEST.in
-+EXTRA_DIST = tox.ini MANIFEST.in nut_telnetlib.py
- 
- NUT_SOURCE_GITREV_NUMERIC = @NUT_SOURCE_GITREV_NUMERIC@
- PYTHON = @PYTHON@
-@@ -64,7 +64,9 @@ upload publish:
- 	 esac
- 
- # README.txt is also a part of module standard expectations
--.pypi-src: test_nutclient.py.in PyNUT.py.in setup.py.in README.adoc Makefile $(top_srcdir)/LICENSE-GPL3
-+# nut_telnetlib.py is not converted from a .in template, it
-+# does not even have a shebang line.
-+.pypi-src: test_nutclient.py.in PyNUT.py.in setup.py.in nut_telnetlib.py README.adoc Makefile $(top_srcdir)/LICENSE-GPL3
- 	@echo "  PYPI  Generate PyPI module source"
- 	@rm -rf $(GENERATED_SRC) "$@"
- 	@mkdir -p PyNUTClient
-@@ -83,6 +85,7 @@ upload publish:
- 		sed -e "s,[@]PYTHON[@],@PYTHON@," < "$(srcdir)/$${B}.in" > "PyNUTClient/$${B}" || exit ; \
- 		if test -x "$(srcdir)/$${B}.in" ; then chmod +x "PyNUTClient/$${B}"; fi ; \
- 	 done ; \
-+	 cp -pf "$(srcdir)/nut_telnetlib.py" PyNUTClient/ || exit ; \
- 	 cp -pf "$(srcdir)/README.adoc" README.txt || exit ; \
- 	 cp -pf "$(top_srcdir)/LICENSE-GPL3" . || exit ; \
- 	 echo "from . import PyNUT" > PyNUTClient/__init__.py || exit
-diff --git a/scripts/python/module/PyNUT.py.in b/scripts/python/module/PyNUT.py.in
-index 6f3c4ff510..6838fc373a 100644
---- a/scripts/python/module/PyNUT.py.in
-+++ b/scripts/python/module/PyNUT.py.in
-@@ -55,8 +55,20 @@
- # 2023-01-18 Jim Klimov <jimklimov+nut@gmail.com> - Version 1.6.0
- #            Added CheckUPSAvailable() method originally by Michal Hlavinka
- #            from 2013-01-07 RedHat/Fedora packaging
-+#
-+# 2024-07-01 Jim Klimov <jimklimov+nut@gmail.com> - Version 1.7.0
-+#            Re-arranged dependency on telnetlib module (deprecated/removed
-+#            since Python 3.11/3.13), so we can fall back on a privately
-+#            stashed copy until a better solution is developed.
-+#
- 
--import telnetlib
-+try:
-+    import telnetlib
-+except ModuleNotFoundError:
-+    import os
-+    if "true" == os.getenv('DEBUG', 'false'):
-+        print( "[DEBUG] Fall back to private copy of telnetlib for PyNUTClient\n" )
-+    import nut_telnetlib as telnetlib
- 
- class PyNUTError( Exception ) :
-     """ Base class for custom exceptions """
-@@ -73,8 +85,8 @@ class PyNUTClient :
-     __timeout     = None
-     __srv_handler = None
- 
--    __version     = "1.6.0"
--    __release     = "2023-01-18"
-+    __version     = "1.7.0"
-+    __release     = "2024-07-01"
- 
- 
-     def __init__( self, host="127.0.0.1", port=3493, login=None, password=None, debug=False, timeout=5 ) :
-diff --git a/scripts/python/module/nut_telnetlib.py b/scripts/python/module/nut_telnetlib.py
-new file mode 100644
-index 0000000000..a9c444f10a
---- /dev/null
-+++ b/scripts/python/module/nut_telnetlib.py
-@@ -0,0 +1,683 @@
-+r"""TELNET client class.
-+
-+------
-+This file has been copied for PyNUTClient fallback needs from Python 3.10
-+(newer Python releases obsoleted the module) and not modified except for
-+this notice.
-+------
-+
-+Based on RFC 854: TELNET Protocol Specification, by J. Postel and
-+J. Reynolds
-+
-+Example:
-+
-+>>> from telnetlib import Telnet
-+>>> tn = Telnet('www.python.org', 79)   # connect to finger port
-+>>> tn.write(b'guido\r\n')
-+>>> print(tn.read_all())
-+Login       Name               TTY         Idle    When    Where
-+guido    Guido van Rossum      pts/2        <Dec  2 11:10> snag.cnri.reston..
-+
-+>>>
-+
-+Note that read_all() won't read until eof -- it just reads some data
-+-- but it guarantees to read at least one byte unless EOF is hit.
-+
-+It is possible to pass a Telnet object to a selector in order to wait until
-+more data is available.  Note that in this case, read_eager() may return b''
-+even if there was data on the socket, because the protocol negotiation may have
-+eaten the data.  This is why EOFError is needed in some cases to distinguish
-+between "no data" and "connection closed" (since the socket also appears ready
-+for reading when it is closed).
-+
-+To do:
-+- option negotiation
-+- timeout should be intrinsic to the connection object instead of an
-+  option on one of the read calls only
-+
-+"""
-+
-+
-+# Imported modules
-+import sys
-+import socket
-+import selectors
-+from time import monotonic as _time
-+
-+__all__ = ["Telnet"]
-+
-+# Tunable parameters
-+DEBUGLEVEL = 0
-+
-+# Telnet protocol defaults
-+TELNET_PORT = 23
-+
-+# Telnet protocol characters (don't change)
-+IAC  = bytes([255]) # "Interpret As Command"
-+DONT = bytes([254])
-+DO   = bytes([253])
-+WONT = bytes([252])
-+WILL = bytes([251])
-+theNULL = bytes([0])
-+
-+SE  = bytes([240])  # Subnegotiation End
-+NOP = bytes([241])  # No Operation
-+DM  = bytes([242])  # Data Mark
-+BRK = bytes([243])  # Break
-+IP  = bytes([244])  # Interrupt process
-+AO  = bytes([245])  # Abort output
-+AYT = bytes([246])  # Are You There
-+EC  = bytes([247])  # Erase Character
-+EL  = bytes([248])  # Erase Line
-+GA  = bytes([249])  # Go Ahead
-+SB =  bytes([250])  # Subnegotiation Begin
-+
-+
-+# Telnet protocol options code (don't change)
-+# These ones all come from arpa/telnet.h
-+BINARY = bytes([0]) # 8-bit data path
-+ECHO = bytes([1]) # echo
-+RCP = bytes([2]) # prepare to reconnect
-+SGA = bytes([3]) # suppress go ahead
-+NAMS = bytes([4]) # approximate message size
-+STATUS = bytes([5]) # give status
-+TM = bytes([6]) # timing mark
-+RCTE = bytes([7]) # remote controlled transmission and echo
-+NAOL = bytes([8]) # negotiate about output line width
-+NAOP = bytes([9]) # negotiate about output page size
-+NAOCRD = bytes([10]) # negotiate about CR disposition
-+NAOHTS = bytes([11]) # negotiate about horizontal tabstops
-+NAOHTD = bytes([12]) # negotiate about horizontal tab disposition
-+NAOFFD = bytes([13]) # negotiate about formfeed disposition
-+NAOVTS = bytes([14]) # negotiate about vertical tab stops
-+NAOVTD = bytes([15]) # negotiate about vertical tab disposition
-+NAOLFD = bytes([16]) # negotiate about output LF disposition
-+XASCII = bytes([17]) # extended ascii character set
-+LOGOUT = bytes([18]) # force logout
-+BM = bytes([19]) # byte macro
-+DET = bytes([20]) # data entry terminal
-+SUPDUP = bytes([21]) # supdup protocol
-+SUPDUPOUTPUT = bytes([22]) # supdup output
-+SNDLOC = bytes([23]) # send location
-+TTYPE = bytes([24]) # terminal type
-+EOR = bytes([25]) # end or record
-+TUID = bytes([26]) # TACACS user identification
-+OUTMRK = bytes([27]) # output marking
-+TTYLOC = bytes([28]) # terminal location number
-+VT3270REGIME = bytes([29]) # 3270 regime
-+X3PAD = bytes([30]) # X.3 PAD
-+NAWS = bytes([31]) # window size
-+TSPEED = bytes([32]) # terminal speed
-+LFLOW = bytes([33]) # remote flow control
-+LINEMODE = bytes([34]) # Linemode option
-+XDISPLOC = bytes([35]) # X Display Location
-+OLD_ENVIRON = bytes([36]) # Old - Environment variables
-+AUTHENTICATION = bytes([37]) # Authenticate
-+ENCRYPT = bytes([38]) # Encryption option
-+NEW_ENVIRON = bytes([39]) # New - Environment variables
-+# the following ones come from
-+# http://www.iana.org/assignments/telnet-options
-+# Unfortunately, that document does not assign identifiers
-+# to all of them, so we are making them up
-+TN3270E = bytes([40]) # TN3270E
-+XAUTH = bytes([41]) # XAUTH
-+CHARSET = bytes([42]) # CHARSET
-+RSP = bytes([43]) # Telnet Remote Serial Port
-+COM_PORT_OPTION = bytes([44]) # Com Port Control Option
-+SUPPRESS_LOCAL_ECHO = bytes([45]) # Telnet Suppress Local Echo
-+TLS = bytes([46]) # Telnet Start TLS
-+KERMIT = bytes([47]) # KERMIT
-+SEND_URL = bytes([48]) # SEND-URL
-+FORWARD_X = bytes([49]) # FORWARD_X
-+PRAGMA_LOGON = bytes([138]) # TELOPT PRAGMA LOGON
-+SSPI_LOGON = bytes([139]) # TELOPT SSPI LOGON
-+PRAGMA_HEARTBEAT = bytes([140]) # TELOPT PRAGMA HEARTBEAT
-+EXOPL = bytes([255]) # Extended-Options-List
-+NOOPT = bytes([0])
-+
-+
-+# poll/select have the advantage of not requiring any extra file descriptor,
-+# contrarily to epoll/kqueue (also, they require a single syscall).
-+if hasattr(selectors, 'PollSelector'):
-+    _TelnetSelector = selectors.PollSelector
-+else:
-+    _TelnetSelector = selectors.SelectSelector
-+
-+
-+class Telnet:
-+
-+    """Telnet interface class.
-+
-+    An instance of this class represents a connection to a telnet
-+    server.  The instance is initially not connected; the open()
-+    method must be used to establish a connection.  Alternatively, the
-+    host name and optional port number can be passed to the
-+    constructor, too.
-+
-+    Don't try to reopen an already connected instance.
-+
-+    This class has many read_*() methods.  Note that some of them
-+    raise EOFError when the end of the connection is read, because
-+    they can return an empty string for other reasons.  See the
-+    individual doc strings.
-+
-+    read_until(expected, [timeout])
-+        Read until the expected string has been seen, or a timeout is
-+        hit (default is no timeout); may block.
-+
-+    read_all()
-+        Read all data until EOF; may block.
-+
-+    read_some()
-+        Read at least one byte or EOF; may block.
-+
-+    read_very_eager()
-+        Read all data available already queued or on the socket,
-+        without blocking.
-+
-+    read_eager()
-+        Read either data already queued or some data available on the
-+        socket, without blocking.
-+
-+    read_lazy()
-+        Read all data in the raw queue (processing it first), without
-+        doing any socket I/O.
-+
-+    read_very_lazy()
-+        Reads all data in the cooked queue, without doing any socket
-+        I/O.
-+
-+    read_sb_data()
-+        Reads available data between SB ... SE sequence. Don't block.
-+
-+    set_option_negotiation_callback(callback)
-+        Each time a telnet option is read on the input flow, this callback
-+        (if set) is called with the following parameters :
-+        callback(telnet socket, command, option)
-+            option will be chr(0) when there is no option.
-+        No other action is done afterwards by telnetlib.
-+
-+    """
-+
-+    def __init__(self, host=None, port=0,
-+                 timeout=socket._GLOBAL_DEFAULT_TIMEOUT):
-+        """Constructor.
-+
-+        When called without arguments, create an unconnected instance.
-+        With a hostname argument, it connects the instance; port number
-+        and timeout are optional.
-+        """
-+        self.debuglevel = DEBUGLEVEL
-+        self.host = host
-+        self.port = port
-+        self.timeout = timeout
-+        self.sock = None
-+        self.rawq = b''
-+        self.irawq = 0
-+        self.cookedq = b''
-+        self.eof = 0
-+        self.iacseq = b'' # Buffer for IAC sequence.
-+        self.sb = 0 # flag for SB and SE sequence.
-+        self.sbdataq = b''
-+        self.option_callback = None
-+        if host is not None:
-+            self.open(host, port, timeout)
-+
-+    def open(self, host, port=0, timeout=socket._GLOBAL_DEFAULT_TIMEOUT):
-+        """Connect to a host.
-+
-+        The optional second argument is the port number, which
-+        defaults to the standard telnet port (23).
-+
-+        Don't try to reopen an already connected instance.
-+        """
-+        self.eof = 0
-+        if not port:
-+            port = TELNET_PORT
-+        self.host = host
-+        self.port = port
-+        self.timeout = timeout
-+        sys.audit("telnetlib.Telnet.open", self, host, port)
-+        self.sock = socket.create_connection((host, port), timeout)
-+
-+    def __del__(self):
-+        """Destructor -- close the connection."""
-+        self.close()
-+
-+    def msg(self, msg, *args):
-+        """Print a debug message, when the debug level is > 0.
-+
-+        If extra arguments are present, they are substituted in the
-+        message using the standard string formatting operator.
-+
-+        """
-+        if self.debuglevel > 0:
-+            print('Telnet(%s,%s):' % (self.host, self.port), end=' ')
-+            if args:
-+                print(msg % args)
-+            else:
-+                print(msg)
-+
-+    def set_debuglevel(self, debuglevel):
-+        """Set the debug level.
-+
-+        The higher it is, the more debug output you get (on sys.stdout).
-+
-+        """
-+        self.debuglevel = debuglevel
-+
-+    def close(self):
-+        """Close the connection."""
-+        sock = self.sock
-+        self.sock = None
-+        self.eof = True
-+        self.iacseq = b''
-+        self.sb = 0
-+        if sock:
-+            sock.close()
-+
-+    def get_socket(self):
-+        """Return the socket object used internally."""
-+        return self.sock
-+
-+    def fileno(self):
-+        """Return the fileno() of the socket object used internally."""
-+        return self.sock.fileno()
-+
-+    def write(self, buffer):
-+        """Write a string to the socket, doubling any IAC characters.
-+
-+        Can block if the connection is blocked.  May raise
-+        OSError if the connection is closed.
-+
-+        """
-+        if IAC in buffer:
-+            buffer = buffer.replace(IAC, IAC+IAC)
-+        sys.audit("telnetlib.Telnet.write", self, buffer)
-+        self.msg("send %r", buffer)
-+        self.sock.sendall(buffer)
-+
-+    def read_until(self, match, timeout=None):
-+        """Read until a given string is encountered or until timeout.
-+
-+        When no match is found, return whatever is available instead,
-+        possibly the empty string.  Raise EOFError if the connection
-+        is closed and no cooked data is available.
-+
-+        """
-+        n = len(match)
-+        self.process_rawq()
-+        i = self.cookedq.find(match)
-+        if i >= 0:
-+            i = i+n
-+            buf = self.cookedq[:i]
-+            self.cookedq = self.cookedq[i:]
-+            return buf
-+        if timeout is not None:
-+            deadline = _time() + timeout
-+        with _TelnetSelector() as selector:
-+            selector.register(self, selectors.EVENT_READ)
-+            while not self.eof:
-+                if selector.select(timeout):
-+                    i = max(0, len(self.cookedq)-n)
-+                    self.fill_rawq()
-+                    self.process_rawq()
-+                    i = self.cookedq.find(match, i)
-+                    if i >= 0:
-+                        i = i+n
-+                        buf = self.cookedq[:i]
-+                        self.cookedq = self.cookedq[i:]
-+                        return buf
-+                if timeout is not None:
-+                    timeout = deadline - _time()
-+                    if timeout < 0:
-+                        break
-+        return self.read_very_lazy()
-+
-+    def read_all(self):
-+        """Read all data until EOF; block until connection closed."""
-+        self.process_rawq()
-+        while not self.eof:
-+            self.fill_rawq()
-+            self.process_rawq()
-+        buf = self.cookedq
-+        self.cookedq = b''
-+        return buf
-+
-+    def read_some(self):
-+        """Read at least one byte of cooked data unless EOF is hit.
-+
-+        Return b'' if EOF is hit.  Block if no data is immediately
-+        available.
-+
-+        """
-+        self.process_rawq()
-+        while not self.cookedq and not self.eof:
-+            self.fill_rawq()
-+            self.process_rawq()
-+        buf = self.cookedq
-+        self.cookedq = b''
-+        return buf
-+
-+    def read_very_eager(self):
-+        """Read everything that's possible without blocking in I/O (eager).
-+
-+        Raise EOFError if connection closed and no cooked data
-+        available.  Return b'' if no cooked data available otherwise.
-+        Don't block unless in the midst of an IAC sequence.
-+
-+        """
-+        self.process_rawq()
-+        while not self.eof and self.sock_avail():
-+            self.fill_rawq()
-+            self.process_rawq()
-+        return self.read_very_lazy()
-+
-+    def read_eager(self):
-+        """Read readily available data.
-+
-+        Raise EOFError if connection closed and no cooked data
-+        available.  Return b'' if no cooked data available otherwise.
-+        Don't block unless in the midst of an IAC sequence.
-+
-+        """
-+        self.process_rawq()
-+        while not self.cookedq and not self.eof and self.sock_avail():
-+            self.fill_rawq()
-+            self.process_rawq()
-+        return self.read_very_lazy()
-+
-+    def read_lazy(self):
-+        """Process and return data that's already in the queues (lazy).
-+
-+        Raise EOFError if connection closed and no data available.
-+        Return b'' if no cooked data available otherwise.  Don't block
-+        unless in the midst of an IAC sequence.
-+
-+        """
-+        self.process_rawq()
-+        return self.read_very_lazy()
-+
-+    def read_very_lazy(self):
-+        """Return any data available in the cooked queue (very lazy).
-+
-+        Raise EOFError if connection closed and no data available.
-+        Return b'' if no cooked data available otherwise.  Don't block.
-+
-+        """
-+        buf = self.cookedq
-+        self.cookedq = b''
-+        if not buf and self.eof and not self.rawq:
-+            raise EOFError('telnet connection closed')
-+        return buf
-+
-+    def read_sb_data(self):
-+        """Return any data available in the SB ... SE queue.
-+
-+        Return b'' if no SB ... SE available. Should only be called
-+        after seeing a SB or SE command. When a new SB command is
-+        found, old unread SB data will be discarded. Don't block.
-+
-+        """
-+        buf = self.sbdataq
-+        self.sbdataq = b''
-+        return buf
-+
-+    def set_option_negotiation_callback(self, callback):
-+        """Provide a callback function called after each receipt of a telnet option."""
-+        self.option_callback = callback
-+
-+    def process_rawq(self):
-+        """Transfer from raw queue to cooked queue.
-+
-+        Set self.eof when connection is closed.  Don't block unless in
-+        the midst of an IAC sequence.
-+
-+        """
-+        buf = [b'', b'']
-+        try:
-+            while self.rawq:
-+                c = self.rawq_getchar()
-+                if not self.iacseq:
-+                    if c == theNULL:
-+                        continue
-+                    if c == b"\021":
-+                        continue
-+                    if c != IAC:
-+                        buf[self.sb] = buf[self.sb] + c
-+                        continue
-+                    else:
-+                        self.iacseq += c
-+                elif len(self.iacseq) == 1:
-+                    # 'IAC: IAC CMD [OPTION only for WILL/WONT/DO/DONT]'
-+                    if c in (DO, DONT, WILL, WONT):
-+                        self.iacseq += c
-+                        continue
-+
-+                    self.iacseq = b''
-+                    if c == IAC:
-+                        buf[self.sb] = buf[self.sb] + c
-+                    else:
-+                        if c == SB: # SB ... SE start.
-+                            self.sb = 1
-+                            self.sbdataq = b''
-+                        elif c == SE:
-+                            self.sb = 0
-+                            self.sbdataq = self.sbdataq + buf[1]
-+                            buf[1] = b''
-+                        if self.option_callback:
-+                            # Callback is supposed to look into
-+                            # the sbdataq
-+                            self.option_callback(self.sock, c, NOOPT)
-+                        else:
-+                            # We can't offer automatic processing of
-+                            # suboptions. Alas, we should not get any
-+                            # unless we did a WILL/DO before.
-+                            self.msg('IAC %d not recognized' % ord(c))
-+                elif len(self.iacseq) == 2:
-+                    cmd = self.iacseq[1:2]
-+                    self.iacseq = b''
-+                    opt = c
-+                    if cmd in (DO, DONT):
-+                        self.msg('IAC %s %d',
-+                            cmd == DO and 'DO' or 'DONT', ord(opt))
-+                        if self.option_callback:
-+                            self.option_callback(self.sock, cmd, opt)
-+                        else:
-+                            self.sock.sendall(IAC + WONT + opt)
-+                    elif cmd in (WILL, WONT):
-+                        self.msg('IAC %s %d',
-+                            cmd == WILL and 'WILL' or 'WONT', ord(opt))
-+                        if self.option_callback:
-+                            self.option_callback(self.sock, cmd, opt)
-+                        else:
-+                            self.sock.sendall(IAC + DONT + opt)
-+        except EOFError: # raised by self.rawq_getchar()
-+            self.iacseq = b'' # Reset on EOF
-+            self.sb = 0
-+            pass
-+        self.cookedq = self.cookedq + buf[0]
-+        self.sbdataq = self.sbdataq + buf[1]
-+
-+    def rawq_getchar(self):
-+        """Get next char from raw queue.
-+
-+        Block if no data is immediately available.  Raise EOFError
-+        when connection is closed.
-+
-+        """
-+        if not self.rawq:
-+            self.fill_rawq()
-+            if self.eof:
-+                raise EOFError
-+        c = self.rawq[self.irawq:self.irawq+1]
-+        self.irawq = self.irawq + 1
-+        if self.irawq >= len(self.rawq):
-+            self.rawq = b''
-+            self.irawq = 0
-+        return c
-+
-+    def fill_rawq(self):
-+        """Fill raw queue from exactly one recv() system call.
-+
-+        Block if no data is immediately available.  Set self.eof when
-+        connection is closed.
-+
-+        """
-+        if self.irawq >= len(self.rawq):
-+            self.rawq = b''
-+            self.irawq = 0
-+        # The buffer size should be fairly small so as to avoid quadratic
-+        # behavior in process_rawq() above
-+        buf = self.sock.recv(50)
-+        self.msg("recv %r", buf)
-+        self.eof = (not buf)
-+        self.rawq = self.rawq + buf
-+
-+    def sock_avail(self):
-+        """Test whether data is available on the socket."""
-+        with _TelnetSelector() as selector:
-+            selector.register(self, selectors.EVENT_READ)
-+            return bool(selector.select(0))
-+
-+    def interact(self):
-+        """Interaction function, emulates a very dumb telnet client."""
-+        if sys.platform == "win32":
-+            self.mt_interact()
-+            return
-+        with _TelnetSelector() as selector:
-+            selector.register(self, selectors.EVENT_READ)
-+            selector.register(sys.stdin, selectors.EVENT_READ)
-+
-+            while True:
-+                for key, events in selector.select():
-+                    if key.fileobj is self:
-+                        try:
-+                            text = self.read_eager()
-+                        except EOFError:
-+                            print('*** Connection closed by remote host ***')
-+                            return
-+                        if text:
-+                            sys.stdout.write(text.decode('ascii'))
-+                            sys.stdout.flush()
-+                    elif key.fileobj is sys.stdin:
-+                        line = sys.stdin.readline().encode('ascii')
-+                        if not line:
-+                            return
-+                        self.write(line)
-+
-+    def mt_interact(self):
-+        """Multithreaded version of interact()."""
-+        import _thread
-+        _thread.start_new_thread(self.listener, ())
-+        while 1:
-+            line = sys.stdin.readline()
-+            if not line:
-+                break
-+            self.write(line.encode('ascii'))
-+
-+    def listener(self):
-+        """Helper for mt_interact() -- this executes in the other thread."""
-+        while 1:
-+            try:
-+                data = self.read_eager()
-+            except EOFError:
-+                print('*** Connection closed by remote host ***')
-+                return
-+            if data:
-+                sys.stdout.write(data.decode('ascii'))
-+            else:
-+                sys.stdout.flush()
-+
-+    def expect(self, list, timeout=None):
-+        """Read until one from a list of a regular expressions matches.
-+
-+        The first argument is a list of regular expressions, either
-+        compiled (re.Pattern instances) or uncompiled (strings).
-+        The optional second argument is a timeout, in seconds; default
-+        is no timeout.
-+
-+        Return a tuple of three items: the index in the list of the
-+        first regular expression that matches; the re.Match object
-+        returned; and the text read up till and including the match.
-+
-+        If EOF is read and no text was read, raise EOFError.
-+        Otherwise, when nothing matches, return (-1, None, text) where
-+        text is the text received so far (may be the empty string if a
-+        timeout happened).
-+
-+        If a regular expression ends with a greedy match (e.g. '.*')
-+        or if more than one expression can match the same input, the
-+        results are undeterministic, and may depend on the I/O timing.
-+
-+        """
-+        re = None
-+        list = list[:]
-+        indices = range(len(list))
-+        for i in indices:
-+            if not hasattr(list[i], "search"):
-+                if not re: import re
-+                list[i] = re.compile(list[i])
-+        if timeout is not None:
-+            deadline = _time() + timeout
-+        with _TelnetSelector() as selector:
-+            selector.register(self, selectors.EVENT_READ)
-+            while not self.eof:
-+                self.process_rawq()
-+                for i in indices:
-+                    m = list[i].search(self.cookedq)
-+                    if m:
-+                        e = m.end()
-+                        text = self.cookedq[:e]
-+                        self.cookedq = self.cookedq[e:]
-+                        return (i, m, text)
-+                if timeout is not None:
-+                    ready = selector.select(timeout)
-+                    timeout = deadline - _time()
-+                    if not ready:
-+                        if timeout < 0:
-+                            break
-+                        else:
-+                            continue
-+                self.fill_rawq()
-+        text = self.read_very_lazy()
-+        if not text and self.eof:
-+            raise EOFError
-+        return (-1, None, text)
-+
-+    def __enter__(self):
-+        return self
-+
-+    def __exit__(self, type, value, traceback):
-+        self.close()
-+
-+
-+def test():
-+    """Test program for telnetlib.
-+
-+    Usage: python telnetlib.py [-d] ... [host [port]]
-+
-+    Default host is localhost; default port is 23.
-+
-+    """
-+    debuglevel = 0
-+    while sys.argv[1:] and sys.argv[1] == '-d':
-+        debuglevel = debuglevel+1
-+        del sys.argv[1]
-+    host = 'localhost'
-+    if sys.argv[1:]:
-+        host = sys.argv[1]
-+    port = 0
-+    if sys.argv[2:]:
-+        portstr = sys.argv[2]
-+        try:
-+            port = int(portstr)
-+        except ValueError:
-+            port = socket.getservbyname(portstr, 'tcp')
-+    with Telnet() as tn:
-+        tn.set_debuglevel(debuglevel)
-+        tn.open(host, port, timeout=0.5)
-+        tn.interact()
-+
-+if __name__ == '__main__':
-+    test()
-diff --git a/scripts/python/module/setup.py.in b/scripts/python/module/setup.py.in
-index 18f956d20c..c6276fd2f2 100644
---- a/scripts/python/module/setup.py.in
-+++ b/scripts/python/module/setup.py.in
-@@ -30,7 +30,11 @@ setup(
-     #data_files =	[('', ['tox.ini'])],
-     #scripts    =	['PyNUTClient/test_nutclient.py', 'PyNUTClient/__init__.py'],
-     python_requires =	'>=2.6',
--    # install_requires =	['telnetlib'],	# NOTE: telnetlib.py is part of Python core for tested 2.x and 3.x versions, not something 'pip' can download
-+    # NOTE: telnetlib.py is part of Python core for tested 2.x and 3.x versions,
-+    # not something 'pip' can download as a package. It is deprecated since 3.11
-+    # and removed since 3.13, so as a hotfix, NUT provides a copy nut_telnetlib.py
-+    # (taken from Python 3.10 - the latest one without a deprecation warning).
-+    # install_requires =	['telnetlib'],
-     keywords =	['pypi', 'cicd', 'python', 'nut', 'Network UPS Tools'],
-     classifiers = [
-         "Development Status :: 5 - Production/Stable",
diff --git a/package/nut/0002-configure-ac-typo-in-path-to-test-with-nut_telnetlib-Python-module.patch b/package/nut/0002-configure-ac-typo-in-path-to-test-with-nut_telnetlib-Python-module.patch
deleted file mode 100644
index 14fa74902b..0000000000
--- a/package/nut/0002-configure-ac-typo-in-path-to-test-with-nut_telnetlib-Python-module.patch
+++ /dev/null
@@ -1,37 +0,0 @@ 
-From d0a234ac4fe9304c4178256584712e4359541060 Mon Sep 17 00:00:00 2001
-From: Jim Klimov <jimklimov+nut@gmail.com>
-Date: Wed, 3 Jul 2024 09:51:07 +0200
-Subject: [PATCH] configure.ac: typo in path to test with nut_telnetlib Python
- module [#2183]
-
-Signed-off-by: Jim Klimov <jimklimov+nut@gmail.com>
-
-Upstream: https://github.com/networkupstools/nut/commit/d0a234ac4fe9304c4178256584712e4359541060
-
-Signed-off-by: Bernd Kuhls <bernd@kuhls.net>
----
- configure.ac | 4 ++--
- 1 file changed, 2 insertions(+), 2 deletions(-)
-
-diff --git a/configure.ac b/configure.ac
-index 935e73d368..e542baacd2 100644
---- a/configure.ac
-+++ b/configure.ac
-@@ -2474,7 +2474,7 @@ if test x"${nut_with_pynut}" != xno \
-             dnl this line essentially checks for presence of
-             dnl a usable interpreter implementation compatible
-             dnl with Python 3.x syntax.
--            if (cd script/python/module && ${PYTHON3} -c "import nut_telnetlib as telnetlib") \
-+            if (cd scripts/python/module && ${PYTHON3} -c "import nut_telnetlib as telnetlib") \
-             ; then
-                 nut_with_pynut_py3="yes"
-             fi
-@@ -2491,7 +2491,7 @@ if test x"${nut_with_pynut}" != xno \
-             nut_with_pynut_py="yes"
-         else
-             dnl See comments above
--            if (cd script/python/module && ${PYTHON} -c "import nut_telnetlib as telnetlib") \
-+            if (cd scripts/python/module && ${PYTHON} -c "import nut_telnetlib as telnetlib") \
-             ; then
-                 nut_with_pynut_py="yes"
-             fi
diff --git a/package/nut/0003-configure.ac-be-sure-to-probe-nut_telnetlib-module-from-source-dir.patch b/package/nut/0003-configure.ac-be-sure-to-probe-nut_telnetlib-module-from-source-dir.patch
deleted file mode 100644
index 8410bceecc..0000000000
--- a/package/nut/0003-configure.ac-be-sure-to-probe-nut_telnetlib-module-from-source-dir.patch
+++ /dev/null
@@ -1,39 +0,0 @@ 
-From 2898a45a83d7f9af62fe0844fcb5358337d905d3 Mon Sep 17 00:00:00 2001
-From: Jim Klimov <jimklimov+nut@gmail.com>
-Date: Wed, 3 Jul 2024 10:22:24 +0200
-Subject: [PATCH] configure.ac: be sure to probe nut_telnetlib module from
- source dir [#2183]
-
-Avoid confusion for out-of-tree builds
-
-Signed-off-by: Jim Klimov <jimklimov+nut@gmail.com>
-
-Upstream: https://github.com/networkupstools/nut/commit/2898a45a83d7f9af62fe0844fcb5358337d905d3
-
-Signed-off-by: Bernd Kuhls <bernd@kuhls.net>
----
- configure.ac | 4 ++--
- 1 file changed, 2 insertions(+), 2 deletions(-)
-
-diff --git a/configure.ac b/configure.ac
-index 5865ad5fe3..9d622c34fc 100644
---- a/configure.ac
-+++ b/configure.ac
-@@ -2474,7 +2474,7 @@ if test x"${nut_with_pynut}" != xno \
-             dnl this line essentially checks for presence of
-             dnl a usable interpreter implementation compatible
-             dnl with Python 3.x syntax.
--            if (cd scripts/python/module && ${PYTHON3} -c "import nut_telnetlib as telnetlib") \
-+            if (cd "${srcdir}"/scripts/python/module && ${PYTHON3} -c "import nut_telnetlib as telnetlib") \
-             ; then
-                 nut_with_pynut_py3="yes"
-             fi
-@@ -2491,7 +2491,7 @@ if test x"${nut_with_pynut}" != xno \
-             nut_with_pynut_py="yes"
-         else
-             dnl See comments above
--            if (cd scripts/python/module && ${PYTHON} -c "import nut_telnetlib as telnetlib") \
-+            if (cd "${srcdir}"/scripts/python/module && ${PYTHON} -c "import nut_telnetlib as telnetlib") \
-             ; then
-                 nut_with_pynut_py="yes"
-             fi
diff --git a/package/nut/nut.hash b/package/nut/nut.hash
index 4c962ee9d8..7346070f77 100644
--- a/package/nut/nut.hash
+++ b/package/nut/nut.hash
@@ -1,7 +1,7 @@ 
-# From https://github.com/networkupstools/nut/releases/download/v2.8.2/nut-2.8.2.tar.gz.sha256
-sha256  e4b4b0cbe7dd39ba9097be7f7d787bb2fffbe35df64dff53b5fe393d659c597d  nut-2.8.2.tar.gz
+# From https://github.com/networkupstools/nut/releases/download/v2.8.3/nut-2.8.3.tar.gz.sha256
+sha256  d6ca17f0b39003bac7649eb17ab4a713e4d5fcaa8fd1aedca28357d59df095ed  nut-2.8.3.tar.gz
 
 # Locally computed
-sha256  8a3de48b86daffcbe822dd94e514c8a77fe70f77c9005ffd9dcf1709dbdc80c7  COPYING
+sha256  40885775f43463c404d74bbd00a59fac6c0fe73f838d4520472d721917412577  COPYING
 sha256  ab15fd526bd8dd18a9e77ebc139656bf4d33e97fc7238cd11bf60e2b9b8666c6  LICENSE-GPL2
 sha256  fc82ca8b6fdb18d4e3e85cfd8ab58d1bcd3f1b29abe782895abd91d64763f8e7  LICENSE-GPL3
diff --git a/package/nut/nut.mk b/package/nut/nut.mk
index 55937f3818..379f31bef2 100644
--- a/package/nut/nut.mk
+++ b/package/nut/nut.mk
@@ -4,17 +4,13 @@ 
 #
 ################################################################################
 
-NUT_VERSION = 2.8.2
+NUT_VERSION = 2.8.3
 NUT_SITE = https://github.com/networkupstools/nut/releases/download/v$(NUT_VERSION)
 NUT_LICENSE = GPL-2.0+, GPL-3.0+ (python scripts), GPL/Artistic (perl client)
 NUT_LICENSE_FILES = COPYING LICENSE-GPL2 LICENSE-GPL3
 NUT_SELINUX_MODULES = apache nut
 NUT_INSTALL_STAGING = YES
 NUT_DEPENDENCIES = host-pkgconf
-# 0001-Provide-a-private-copy-of-telnetlib-for-PyNUTClient.patch
-# 0002-configure-ac-typo-in-path-to-test-with-nut_telnetlib-Python-module.patch
-# 0003-configure.ac-be-sure-to-probe-nut_telnetlib-module-from-source-dir.patch
-NUT_AUTORECONF = YES
 
 # Put the PID files in a read-write place (/var/run is a tmpfs)
 # since the default location (/var/state/ups) maybe readonly.
@@ -28,6 +24,7 @@  NUT_CONF_OPTS = \
 	--with-group=nut
 
 NUT_CONF_ENV = \
+	PKG_CONFIG_LIBDIR=$(STAGING_DIR)/usr/lib/pkgconfig:$(STAGING_DIR)/usr/share/pkgconfig \
 	ax_cv_check_cflags__Werror__Wno_unknown_warning_option=no \
 	ax_cv_check_cxxflags__Werror__Wno_unknown_warning_option=no \
 	ac_cv_func_strcasecmp=yes \