mirror of
https://github.com/freedombox/FreedomBox.git
synced 2026-01-28 08:03:36 +00:00
It is incorrect to check for backports availability, FreedomBox systems got added buster-backports sources prematurely. This will lead to apt update failures resulting in FreedomBox becoming unable to install new apps. Fix this by removing old sources and adding new sources only after performing (this time correct) backports URL check. Closes: #1496. Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org> Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
216 lines
6.9 KiB
Python
Executable File
216 lines
6.9 KiB
Python
Executable File
#!/usr/bin/python3
|
|
#
|
|
# This file is part of FreedomBox.
|
|
#
|
|
# This program is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU Affero General Public License as
|
|
# published by the Free Software Foundation, either version 3 of the
|
|
# License, or (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU Affero General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU Affero General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
#
|
|
"""
|
|
Configures or runs unattended-upgrades
|
|
"""
|
|
|
|
import argparse
|
|
import os
|
|
import re
|
|
import subprocess
|
|
import sys
|
|
|
|
from plinth import action_utils
|
|
|
|
AUTO_CONF_FILE = '/etc/apt/apt.conf.d/20auto-upgrades'
|
|
LOG_FILE = '/var/log/unattended-upgrades/unattended-upgrades.log'
|
|
BUSTER_BACKPORTS_RELEASE_FILE_URL = 'https://deb.debian.org/debian/dists/buster-backports/Release'
|
|
|
|
|
|
def parse_arguments():
|
|
"""Return parsed command line arguments as dictionary"""
|
|
parser = argparse.ArgumentParser()
|
|
subparsers = parser.add_subparsers(dest='subcommand', help='Sub command')
|
|
|
|
subparsers.add_parser('run', help='Upgrade packages on the system')
|
|
subparsers.add_parser('check-auto',
|
|
help='Check if automatic upgrades are enabled')
|
|
subparsers.add_parser('enable-auto', help='Enable automatic upgrades')
|
|
subparsers.add_parser('disable-auto', help='Disable automatic upgrades.')
|
|
subparsers.add_parser('get-log', help='Print the automatic upgrades log')
|
|
|
|
subparsers.add_parser('setup-repositories',
|
|
help='Setup software repositories for FreedomBox')
|
|
|
|
subparsers.required = True
|
|
return parser.parse_args()
|
|
|
|
|
|
def subcommand_run(_):
|
|
"""Run unattended-upgrades"""
|
|
try:
|
|
subprocess.Popen(['unattended-upgrades', '-v'],
|
|
stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL,
|
|
stderr=subprocess.DEVNULL, close_fds=True,
|
|
start_new_session=True)
|
|
except FileNotFoundError:
|
|
print('Error: unattended-upgrades is not available.', file=sys.stderr)
|
|
sys.exit(2)
|
|
except Exception as error:
|
|
print('Error: {0}'.format(error), file=sys.stderr)
|
|
sys.exit(3)
|
|
|
|
|
|
def subcommand_check_auto(_):
|
|
"""Check if automatic upgrades are enabled"""
|
|
arguments = [
|
|
'apt-config', 'shell', 'UpdateInterval',
|
|
'APT::Periodic::Update-Package-Lists'
|
|
]
|
|
try:
|
|
output = subprocess.check_output(arguments).decode()
|
|
except subprocess.CalledProcessError as error:
|
|
print('Error: {0}'.format(error), file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
update_interval = 0
|
|
match = re.match(r"UpdateInterval='(.*)'", output)
|
|
if match:
|
|
update_interval = int(match.group(1))
|
|
|
|
print(bool(update_interval))
|
|
|
|
|
|
def subcommand_enable_auto(_):
|
|
"""Enable automatic upgrades"""
|
|
with open(AUTO_CONF_FILE, 'w') as conffile:
|
|
conffile.write('APT::Periodic::Update-Package-Lists "1";\n')
|
|
conffile.write('APT::Periodic::Unattended-Upgrade "1";\n')
|
|
|
|
|
|
def subcommand_disable_auto(_):
|
|
"""Disable automatic upgrades"""
|
|
with open(AUTO_CONF_FILE, 'w') as conffile:
|
|
conffile.write('APT::Periodic::Update-Package-Lists "0";\n')
|
|
conffile.write('APT::Periodic::Unattended-Upgrade "0";\n')
|
|
|
|
|
|
def subcommand_get_log(_):
|
|
"""Print the automatic upgrades log."""
|
|
try:
|
|
with open(LOG_FILE, 'r') as file_handle:
|
|
print(file_handle.read())
|
|
except IOError:
|
|
pass
|
|
|
|
|
|
def _get_protocol():
|
|
"""Return the protocol to use for newly added repository sources."""
|
|
try:
|
|
from plinth.modules.tor import utils
|
|
if utils.is_apt_transport_tor_enabled():
|
|
return 'tor+http'
|
|
except Exception:
|
|
pass
|
|
|
|
return 'http'
|
|
|
|
|
|
def _is_release_file_available(protocol):
|
|
"""Return whether the release for backports is available."""
|
|
wrapper = None
|
|
if protocol == 'tor+http':
|
|
wrapper = 'torsocks'
|
|
|
|
result = action_utils.check_url(BUSTER_BACKPORTS_RELEASE_FILE_URL,
|
|
wrapper=wrapper)
|
|
return result == 'passed'
|
|
|
|
|
|
def _add_buster_backports_sources(sources_list, protocol):
|
|
"""Add buster backports sources to freedombox repositories list."""
|
|
sources = '''# This file is managed by FreedomBox, do not edit.
|
|
# Allow carefully selected updates to 'freedombox' from backports.
|
|
|
|
deb {protocol}://deb.debian.org/debian buster-backports main
|
|
deb-src {protocol}://deb.debian.org/debian buster-backports main
|
|
'''
|
|
sources = sources.format(protocol=protocol)
|
|
with open(sources_list, 'w') as file_handle:
|
|
file_handle.write(sources)
|
|
|
|
|
|
def _check_and_backports_sources():
|
|
"""Add buster backports sources after checking if it is available."""
|
|
old_sources_list = '/etc/apt/sources.list.d/freedombox.list'
|
|
if os.path.exists(old_sources_list):
|
|
os.remove(old_sources_list)
|
|
|
|
sources_list = '/etc/apt/sources.list.d/freedombox2.list'
|
|
if os.path.exists(sources_list):
|
|
print('Repositories list up-to-date. Skipping update.')
|
|
return
|
|
|
|
protocol = _get_protocol()
|
|
if protocol == 'tor+http':
|
|
print('Package download over Tor is enabled.')
|
|
|
|
if not _is_release_file_available(protocol):
|
|
print('Release file for Buster backports is not available yet.')
|
|
return
|
|
|
|
print('Buster backports is now available. Adding to sources.')
|
|
_add_buster_backports_sources(sources_list, protocol)
|
|
|
|
|
|
def _add_apt_preferences():
|
|
"""Setup APT preferences to upgrade selected packages from backports."""
|
|
old_preferences_file = '/etc/apt/preferences.d/50freedombox.pref'
|
|
if os.path.exists(old_preferences_file):
|
|
os.remove(old_preferences_file)
|
|
|
|
preferences_file = '/etc/apt/preferences.d/50freedombox2.pref'
|
|
if os.path.exists(preferences_file):
|
|
print('Preferences up-to-date. Skipping update')
|
|
return
|
|
|
|
preferences = '''Explanation: This file is managed by FreedomBox, do not edit.
|
|
Explanation: Allow carefully selected updates to 'freedombox' from backports.
|
|
Package: freedombox
|
|
Pin: release a=buster-backports
|
|
Pin-Priority: 500
|
|
'''
|
|
|
|
print('Updating APT preferences.')
|
|
with open(preferences_file, 'w') as file_handle:
|
|
file_handle.write(preferences)
|
|
|
|
|
|
def subcommand_setup_repositories(_):
|
|
"""Setup software repositories needed for FreedomBox.
|
|
|
|
Repositories list for now only contains the backports. If the file exists,
|
|
assume that it contains backports.
|
|
|
|
"""
|
|
_check_and_backports_sources()
|
|
_add_apt_preferences()
|
|
|
|
|
|
def main():
|
|
"""Parse arguments and perform all duties"""
|
|
arguments = parse_arguments()
|
|
|
|
subcommand = arguments.subcommand.replace('-', '_')
|
|
subcommand_method = globals()['subcommand_' + subcommand]
|
|
subcommand_method(arguments)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|