File: utils.py

package info (click to toggle)
freedombox 26.3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 83,092 kB
  • sloc: python: 48,542; javascript: 1,730; xml: 481; makefile: 290; sh: 137; php: 32
file content (126 lines) | stat: -rw-r--r-- 3,777 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# SPDX-License-Identifier: AGPL-3.0-or-later
"""Utilities for regular updates and dist-upgrades."""

import pathlib
import re

import augeas

from plinth import action_utils
from plinth.modules.apache.components import check_url

RELEASE_FILE_URL = \
    'https://deb.debian.org/debian/dists/{}/Release'

DIST_UPGRADE_REQUIRED_FREE_SPACE = 5000000

sources_list = pathlib.Path('/etc/apt/sources.list')


def check_auto() -> bool:
    """Return whether automatic updates are enabled."""
    arguments = [
        'apt-config', 'shell', 'UpdateInterval',
        'APT::Periodic::Update-Package-Lists'
    ]
    output = action_utils.run(arguments, check=True).stdout.decode()
    update_interval = 0
    match = re.match(r"UpdateInterval='(.*)'", output)
    if match:
        update_interval = int(match.group(1))

    return bool(update_interval)


def get_http_protocol() -> str:
    """Return the protocol to use for newly added repository sources."""
    try:
        from plinth.modules.torproxy import utils
        if utils.is_apt_transport_tor_enabled():
            return 'tor+http'
    except Exception:
        pass

    return 'http'


def is_release_file_available(protocol: str, dist: str,
                              backports=False) -> bool:
    """Return whether the release for dist[-backports] is available."""
    wrapper = None
    if protocol == 'tor+http':
        wrapper = 'torsocks'

    if backports:
        dist += '-backports'

    try:
        return check_url(RELEASE_FILE_URL.format(dist), wrapper=wrapper)
    except FileNotFoundError:
        return False


def is_sufficient_free_space() -> bool:
    """Return whether there is sufficient free space for dist upgrade."""
    output = action_utils.run(['df', '--output=avail', '/'], check=True).stdout
    free_space = int(output.decode().split('\n')[1])
    return free_space >= DIST_UPGRADE_REQUIRED_FREE_SPACE


def get_sources_list_codename() -> str | None:
    """Return the codename set in the /etc/apt/sources.list file."""
    aug = augeas.Augeas(flags=augeas.Augeas.NO_LOAD +
                        augeas.Augeas.NO_MODL_AUTOLOAD)
    aug.transform('aptsources', str(sources_list))
    aug.set('/augeas/context', '/files' + str(sources_list))
    aug.load()

    dists = set()
    for match_ in aug.match('*'):
        dist = aug.get(match_ + '/distribution')
        if not dist:
            continue

        dist = dist.removesuffix('-updates')
        dist = dist.removesuffix('-security')
        dists.add(dist)

    if 'unstable' in dists or 'sid' in dists:
        return 'unstable'

    if 'testing' in dists:
        return 'testing'

    # Multiple distributions are not understood.
    if len(dists) != 1:
        return None

    return dists.pop()


def get_current_release():
    """Return current release and codename as a tuple."""
    output = action_utils.run(
        ['lsb_release', '--release', '--codename', '--short'],
        check=True).stdout.decode().strip()
    lines = output.split('\n')
    return lines[0], lines[1]


def is_distribution_unstable() -> bool:
    """Return whether the current distribution is unstable.

    There is no way to distinguish between 'testing' and 'unstable'
    distributions in Debian using commands like lsb_release (powered by
    /etc/os-release). See: https://lwn.net/Articles/984635/ . So, use the value
    set in /etc/apt/sources.list.
    """
    codename = get_sources_list_codename()
    return codename in ['unstable', 'sid']


def is_distribution_rolling() -> bool:
    """Return whether the current distribution is testing or unstable."""
    # Release will be 'n/a' in latest unstable and testing distributions.
    release, _ = get_current_release()
    return release in ['unstable', 'testing', 'n/a']