Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add reboot option to DNF Automatic #1879

Merged
merged 4 commits into from
Jan 26, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ DNF CONTRIBUTORS
Dave Johansen <[email protected]>
Dylan Pindur <[email protected]>
Eduard Cuba <[email protected]>
Evan Goode <[email protected]>
Filipe Brandenburger <[email protected]>
Frank Dana <[email protected]>
George Machitidze <[email protected]>
Expand Down
9 changes: 9 additions & 0 deletions dnf/automatic/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

import argparse
import logging
import os
import random
import socket
import time
Expand Down Expand Up @@ -179,6 +180,9 @@ def __init__(self):
libdnf.conf.VectorString(['default', 'security'])))
self.add_option('random_sleep', libdnf.conf.OptionNumberInt32(300))
self.add_option('network_online_timeout', libdnf.conf.OptionNumberInt32(60))
self.add_option('reboot', libdnf.conf.OptionEnumString('never',
libdnf.conf.VectorString(['never', 'when-changed', 'when-needed'])))
self.add_option('reboot_command', libdnf.conf.OptionString('shutdown -r +5 \'Rebooting after applying package updates\''))

def imply(self):
if self.apply_updates:
Expand Down Expand Up @@ -340,6 +344,11 @@ def main(args):
base.do_transaction()
emitters.notify_applied()
emitters.commit()

if (conf.commands.reboot == 'when-changed' or
(conf.commands.reboot == 'when-needed' and base.reboot_needed())):
if os.waitstatus_to_exitcode(os.system(conf.commands.reboot_command)) != 0:
return 1
evan-goode marked this conversation as resolved.
Show resolved Hide resolved
except dnf.exceptions.Error as exc:
logger.error(_('Error: %s'), ucd(exc))
return 1
Expand Down
14 changes: 14 additions & 0 deletions dnf/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -2794,6 +2794,20 @@ def _nevra(item):

return skipped_conflicts, skipped_dependency

def reboot_needed(self):
"""Check whether a system reboot is recommended following the transaction

:return: bool
"""
if not self.transaction:
return False

# List taken from DNF needs-restarting
need_reboot = frozenset(('kernel', 'kernel-rt', 'glibc',
'linux-firmware', 'systemd', 'dbus',
'dbus-broker', 'dbus-daemon'))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice to share this set with needs-restarting plugin somehow.
One option would be a new config option. In this case it should be implemented also for dnf5.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm true, would doing this via a new config option require a change in libdnf?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, if we agree that it is useful, it will be spread across libdnf, dnf, dnf-plugins-core, and dnf5 :)
But I'm not gonna block this PR because of that. It could be done in subsequent patches.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I think we can hold off for now and err on the side of minimal changes. Hopefully this list doesn't change too much over the years. But it would be good for it to be configurable by the user.

changed_pkgs = self.transaction.install_set | self.transaction.remove_set
return any(pkg.name in need_reboot for pkg in changed_pkgs)

def _msg_installed(pkg):
name = ucd(pkg)
Expand Down
12 changes: 12 additions & 0 deletions doc/automatic.rst
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,18 @@ Setting the mode of operation of the program.

What kind of upgrades to look at. ``default`` signals looking for all available updates, ``security`` only those with an issued security advisory.

``reboot``
either one of ``never``, ``when-changed``, ``when-needed``, default: ``never``

When the system should reboot following upgrades. ``never`` does not reboot the system. ``when-changed`` triggers a reboot after any upgrade. ``when-needed`` triggers a reboot only when rebooting is necessary to apply changes, such as when systemd or the kernel is upgraded.

``reboot_command``
string, default: ``shutdown -r +5 'Rebooting after applying package updates'``

Specify the command to run to trigger a reboot of the system. For example, to skip the 5-minute delay and wall message, use ``shutdown -r``



----------------------
``[emitters]`` section
----------------------
Expand Down
9 changes: 9 additions & 0 deletions etc/dnf/automatic.conf
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,15 @@ download_updates = yes
# install.timer override this setting.
apply_updates = no

# When the system should reboot following upgrades:
# never = don't reboot after upgrades
# when-changed = reboot after any changes
# when-needed = reboot when necessary to apply changes
reboot = never

# The command that is run to trigger a system reboot.
reboot_command = "shutdown -r +5 'Rebooting after applying package updates'"


[emitters]
# Name to use for this system in messages that are emitted. Default is the
Expand Down
4 changes: 4 additions & 0 deletions tests/automatic/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,7 @@ def test_load(self):
conf = dnf.automatic.main.AutomaticConfig(FILE, downloadupdates=True, installupdates=False)
self.assertTrue(conf.commands.download_updates)
self.assertFalse(conf.commands.apply_updates)

# test that reboot is "never" by default
conf = dnf.automatic.main.AutomaticConfig(FILE)
self.assertEqual(conf.commands.reboot, 'never')