From 8b26692eb2097a03d4198e80810999c47fea18bb Mon Sep 17 00:00:00 2001 From: Evan Goode Date: Mon, 13 Mar 2023 14:50:41 -0400 Subject: [PATCH 1/2] smtplib: catch OSError, not SMTPException Upstream commit: aab7defca4fd827ede02336e5a0cf95e8691fb74 Some, but not all, types of connection error are caught by smtplib and reraised as an smtplib.SMTPException. Notably, TimeoutError, socket.gaierror (name resolution failure), and ConnectionRefusedError and are not caught. The more generic OSError should be caught here instead. Resolves #1905 Resolves: https://issues.redhat.com/browse/RHEL-46030 --- dnf/automatic/emitter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnf/automatic/emitter.py b/dnf/automatic/emitter.py index 4aea4b02aa..648f1a1d41 100644 --- a/dnf/automatic/emitter.py +++ b/dnf/automatic/emitter.py @@ -106,7 +106,7 @@ def commit(self): smtp = smtplib.SMTP(self._conf.email_host, timeout=300) smtp.sendmail(email_from, email_to, message.as_string()) smtp.close() - except smtplib.SMTPException as exc: + except OSError as exc: msg = _("Failed to send an email via '%s': %s") % ( self._conf.email_host, exc) logger.error(msg) From 99942a33823f3e62b051a3d9868e1023f83121e6 Mon Sep 17 00:00:00 2001 From: Marek Blaha Date: Thu, 17 Oct 2024 13:30:21 +0200 Subject: [PATCH 2/2] automatic: Check availability of config file If a configuration file is explicitly specified on the command line, ensure that it exists and is readable. If the file is not found, notify the user immediately and terminate the process. This resolves issues where users may run dnf-automatic with unrecognized positional arguments, such as `dnf-automatic install`. The most natural approach to handle a non-existing config file would be by catching the exception thrown by the `read()` method of the `libdnf.conf.ConfigParser` class. Unfortunately, the Python bindings override the `read()` method at the SWIG level, causing it to suppress any potentially raised IOError. For details see this section of the commit https://github.com/rpm-software-management/libdnf/commit/8f1fedf8551b72d6bc24018f5980714b3a103aeb def ConfigParser__newRead(self, filenames): parsedFNames = [] try: if isinstance(filenames, str) or isinstance(filenames, unicode): filenames = [filenames] except NameError: pass for fname in filenames: try: self.readFileName(fname) parsedFNames.append(fname) except IOError: pass except Exception as e: raise RuntimeError("Parsing file '%s' failed: %s" % (fname, str(e))) return parsedFNames ConfigParser.read = ConfigParser__newRead Resolves: https://issues.redhat.com/browse/RHEL-46030 --- dnf/automatic/main.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/dnf/automatic/main.py b/dnf/automatic/main.py index ccd9ab645c..3d73ffce1b 100644 --- a/dnf/automatic/main.py +++ b/dnf/automatic/main.py @@ -73,7 +73,7 @@ def build_emitters(conf): def parse_arguments(args): parser = argparse.ArgumentParser() - parser.add_argument('conf_path', nargs='?', default=dnf.const.CONF_AUTOMATIC_FILENAME) + parser.add_argument('conf_path', nargs='?') parser.add_argument('--timer', action='store_true') parser.add_argument('--installupdates', dest='installupdates', action='store_true') parser.add_argument('--downloadupdates', dest='downloadupdates', action='store_true') @@ -88,7 +88,17 @@ def parse_arguments(args): class AutomaticConfig(object): def __init__(self, filename=None, downloadupdates=None, installupdates=None): - if not filename: + if filename: + # Specific config file was explicitely requested. Check that it exists + # and is readable. + if os.access(filename, os.F_OK): + if not os.access(filename, os.R_OK): + raise dnf.exceptions.Error( + "Configuration file \"{}\" is not readable.".format(filename)) + else: + raise dnf.exceptions.Error( + "Configuration file \"{}\" not found.".format(filename)) + else: filename = dnf.const.CONF_AUTOMATIC_FILENAME self.commands = CommandsConfig() self.email = EmailConfig() @@ -295,6 +305,8 @@ def remote_address(url_list): def main(args): (opts, parser) = parse_arguments(args) + conf = None + emitters = None try: conf = AutomaticConfig(opts.conf_path, opts.downloadupdates,