~ubuntu-core-dev/update-notifier/ubuntu

825 by Brian Murray
apt-check: port to python3, if it is available use update-manager's
1
#!/usr/bin/python3
96 by mvo
* debian/control:
2
3
888 by Brian Murray
releasing package update-notifier version 3.173
4
# nice apt-get -s -o Debug::NoLocking=true upgrade | grep ^Inst
96 by mvo
* debian/control:
5
708 by Michael Vogt
* data/apt_check.py:
6
import apt
96 by mvo
* debian/control:
7
import apt_pkg
8
import os
9
import sys
134 by mvo
* data/apt-check:
10
from optparse import OptionParser
227 by Michael Vogt
* data/apt-check: typo fix
11
import gettext
459 by Michael Vogt
* data/apt_check.py:
12
import subprocess
406 by Michael Vogt
- use dgettext() so that it can be used from a different
13
106 by mvo
* data/apt-check:
14
SYNAPTIC_PINFILE = "/var/lib/synaptic/preferences"
825 by Brian Murray
apt-check: port to python3, if it is available use update-manager's
15
DISTRO = subprocess.check_output(
16
    ["lsb_release", "-c", "-s"],
17
    universal_newlines=True).strip()
406 by Michael Vogt
- use dgettext() so that it can be used from a different
18
858 by Steve Langasek
Migrate fully from python2 to python3 now that python3-debian is
19
458 by Michael Vogt
apt_check.py: refactor the code to make it easier to follow
20
def _(msg):
21
    return gettext.dgettext("update-notifier", msg)
22
858 by Steve Langasek
Migrate fully from python2 to python3 now that python3-debian is
23
458 by Michael Vogt
apt_check.py: refactor the code to make it easier to follow
24
def _handleException(type, value, tb):
888 by Brian Murray
releasing package update-notifier version 3.173
25
    sys.stderr.write("E: " + _("Unknown Error: '%s' (%s)") % (type, value))
458 by Michael Vogt
apt_check.py: refactor the code to make it easier to follow
26
    sys.exit(-1)
27
858 by Steve Langasek
Migrate fully from python2 to python3 now that python3-debian is
28
29
def clean(cache, depcache):
458 by Michael Vogt
apt_check.py: refactor the code to make it easier to follow
30
    " unmark (clean) all changes from the given depcache "
247 by Michael Vogt
* data/apt-check:
31
    # mvo: looping is too inefficient with the new auto-mark code
888 by Brian Murray
releasing package update-notifier version 3.173
32
    # for pkg in cache.Packages:
33
    #     depcache.MarkKeep(pkg)
573 by Michael Vogt
merged from debian
34
    depcache.init()
201 by mvo
* data/apt-check:
35
858 by Steve Langasek
Migrate fully from python2 to python3 now that python3-debian is
36
37
def saveDistUpgrade(cache, depcache):
877 by Brian Murray
data/apt_check.py: use pkg.get_fullname() instead of pkg.name so that
38
    """ this function mimics a upgrade but will never remove anything """
573 by Michael Vogt
merged from debian
39
    depcache.upgrade(True)
40
    if depcache.del_count > 0:
858 by Steve Langasek
Migrate fully from python2 to python3 now that python3-debian is
41
        clean(cache, depcache)
573 by Michael Vogt
merged from debian
42
    depcache.upgrade()
238 by Michael Vogt
* data/apt-check:
43
858 by Steve Langasek
Migrate fully from python2 to python3 now that python3-debian is
44
372 by Michael Vogt
* data/apt-check:
45
def isSecurityUpgrade(ver):
46
    " check if the given version is a security update (or masks one) "
573 by Michael Vogt
merged from debian
47
    security_pockets = [("Ubuntu", "%s-security" % DISTRO),
957.1.1 by Julian Andres Klode
apt-check: Count UbuntuESM -security update as security updates
48
                        ("UbuntuESM", "%s-security" % DISTRO),
573 by Michael Vogt
merged from debian
49
                        ("gNewSense", "%s-security" % DISTRO),
50
                        ("Debian", "%s-updates" % DISTRO)]
51
    for (file, index) in ver.file_list:
52
        for origin, archive in security_pockets:
53
            if (file.archive == archive and file.origin == origin):
54
                return True
372 by Michael Vogt
* data/apt-check:
55
    return False
458 by Michael Vogt
apt_check.py: refactor the code to make it easier to follow
56
858 by Steve Langasek
Migrate fully from python2 to python3 now that python3-debian is
57
957.1.3 by Julian Andres Klode
apt-check: Count enabled ESM upgrades
58
def isESMUpgrade(ver):
59
    " check if the given version is a security update (or masks one) "
60
    for (file, index) in ver.file_list:
61
        if file.origin == "UbuntuESM" and file.archive.startswith(DISTRO):
62
            return True
63
    return False
64
65
458 by Michael Vogt
apt_check.py: refactor the code to make it easier to follow
66
def write_package_names(outstream, cache, depcache):
67
    " write out package names that change to outstream "
947 by Andrea Azzarone
* data/apt_check.py, data/package-data-downloader, tests/test_pep8.py:
68
    pkgs = [pkg for pkg in cache.packages if depcache.marked_install(pkg)
69
            or depcache.marked_upgrade(pkg)]
870 by Brian Murray
data/apt_check.py: resolve crash when running apt-check with
70
    outstream.write("\n".join([p.name for p in pkgs]))
858 by Steve Langasek
Migrate fully from python2 to python3 now that python3-debian is
71
458 by Michael Vogt
apt_check.py: refactor the code to make it easier to follow
72
957.1.3 by Julian Andres Klode
apt-check: Count enabled ESM upgrades
73
def write_human_readable_summary(outstream, upgrades, security_updates,
957.1.4 by Julian Andres Klode
apt-check: Tell people to enable ESM if ESM updates are available
74
                                 esm_updates, have_esm, disabled_esm_updates):
458 by Michael Vogt
apt_check.py: refactor the code to make it easier to follow
75
    " write out human summary summary to outstream "
957.1.3 by Julian Andres Klode
apt-check: Count enabled ESM upgrades
76
    if have_esm is not None:
77
        if have_esm:
78
            outstream.write(gettext.dgettext("update-notifier",
975 by Brian Murray
data/apt_check.py: modify wording and output regarding ESM support.
79
                                             "UA Infrastructure Extended "
80
                                             "Security Maintenance (ESM) is "
81
                                             "enabled."))
957.1.3 by Julian Andres Klode
apt-check: Count enabled ESM upgrades
82
        else:
83
            outstream.write(gettext.dgettext("update-notifier",
975 by Brian Murray
data/apt_check.py: modify wording and output regarding ESM support.
84
                                             "UA Infrastructure Extended "
85
                                             "Security Maintenance (ESM) is "
86
                                             "not enabled."))
957.1.3 by Julian Andres Klode
apt-check: Count enabled ESM upgrades
87
        outstream.write("\n\n")
88
458 by Michael Vogt
apt_check.py: refactor the code to make it easier to follow
89
    outstream.write(gettext.dngettext("update-notifier",
957.1.2 by Julian Andres Klode
apt-check: Update wording to match spec
90
                                      "%i update can be installed "
91
                                      "immediately.",
92
                                      "%i updates can be installed "
93
                                      "immediately.",
888 by Brian Murray
releasing package update-notifier version 3.173
94
                                      upgrades) % upgrades)
458 by Michael Vogt
apt_check.py: refactor the code to make it easier to follow
95
    outstream.write("\n")
957.1.3 by Julian Andres Klode
apt-check: Count enabled ESM upgrades
96
    if esm_updates > 0:
97
        outstream.write(gettext.dngettext("update-notifier",
975 by Brian Murray
data/apt_check.py: modify wording and output regarding ESM support.
98
                                          "%i of these updates is provided "
99
                                          "through UA Infrastructure ESM.",
957.1.3 by Julian Andres Klode
apt-check: Count enabled ESM upgrades
100
                                          "%i of these updates are "
975 by Brian Murray
data/apt_check.py: modify wording and output regarding ESM support.
101
                                          "provided through UA "
102
                                          "Infrastructure ESM.",
957.1.3 by Julian Andres Klode
apt-check: Count enabled ESM upgrades
103
                                          esm_updates) %
104
                        esm_updates)
105
        outstream.write("\n")
458 by Michael Vogt
apt_check.py: refactor the code to make it easier to follow
106
    outstream.write(gettext.dngettext("update-notifier",
957.1.3 by Julian Andres Klode
apt-check: Count enabled ESM upgrades
107
                                      "%i of these updates is a "
108
                                      "security update.",
109
                                      "%i of these updates are "
110
                                      "security updates.",
111
                                      security_updates) %
112
                    security_updates)
975 by Brian Murray
data/apt_check.py: modify wording and output regarding ESM support.
113
    if upgrades > 0 or security_updates > 0 or esm_updates > 0:
114
        outstream.write("\n")
115
        outstream.write(gettext.dgettext("update-notifier",
116
                                         "To see these additional updates "
117
                                         "run: apt list --upgradable"))
963 by Julian Andres Klode
We told people ESM is not enabled, but not how to enable it.
118
    if have_esm is not None and not have_esm:
957.1.4 by Julian Andres Klode
apt-check: Tell people to enable ESM if ESM updates are available
119
        outstream.write("\n")
963 by Julian Andres Klode
We told people ESM is not enabled, but not how to enable it.
120
        if disabled_esm_updates > 0:
975 by Brian Murray
data/apt_check.py: modify wording and output regarding ESM support.
121
            outstream.write("\n")
963 by Julian Andres Klode
We told people ESM is not enabled, but not how to enable it.
122
            outstream.write(gettext.dngettext("update-notifier",
975 by Brian Murray
data/apt_check.py: modify wording and output regarding ESM support.
123
                                              "Enable UA Infrastructure ESM "
124
                                              "to receive %i additional "
125
                                              "security update.",
126
                                              "Enable UA Infrastructure ESM "
127
                                              "to receive %i additional "
128
                                              "security updates.",
963 by Julian Andres Klode
We told people ESM is not enabled, but not how to enable it.
129
                                              disabled_esm_updates) %
130
                            disabled_esm_updates)
131
        else:
975 by Brian Murray
data/apt_check.py: modify wording and output regarding ESM support.
132
            outstream.write("\n")
963 by Julian Andres Klode
We told people ESM is not enabled, but not how to enable it.
133
            outstream.write(gettext.dgettext("update-notifier",
975 by Brian Murray
data/apt_check.py: modify wording and output regarding ESM support.
134
                                             "Enable UA Infrastructure ESM to "
135
                                             "receive additional future "
136
                                             "security updates."))
957.1.4 by Julian Andres Klode
apt-check: Tell people to enable ESM if ESM updates are available
137
        outstream.write("\n")
138
        outstream.write(gettext.dgettext("update-notifier",
975 by Brian Murray
data/apt_check.py: modify wording and output regarding ESM support.
139
                                         "See https://ubuntu.com/esm "
140
                                         "or run: sudo ua status"))
141
    outstream.write("\n")
957.1.4 by Julian Andres Klode
apt-check: Tell people to enable ESM if ESM updates are available
142
143
144
def has_disabled_esm_security_update(depcache, pkg):
145
    " check if we have a disabled ESM security update "
146
    inst_ver = pkg.current_ver
147
    if not inst_ver:
148
        return False
149
150
    for ver in pkg.version_list:
151
        if ver == inst_ver:
152
            break
153
154
        for (file, index) in ver.file_list:
155
            if (file.origin == "UbuntuESM" and file.archive.startswith(DISTRO)
156
                    and depcache.policy.get_priority(file) == -32768):
157
                return True
158
    return False
159
858 by Steve Langasek
Migrate fully from python2 to python3 now that python3-debian is
160
458 by Michael Vogt
apt_check.py: refactor the code to make it easier to follow
161
def init():
162
    " init the system, be nice "
163
    # FIXME: do a ionice here too?
404 by Michael Vogt
* data/apt-check:
164
    os.nice(19)
165
    apt_pkg.init()
870 by Brian Murray
data/apt_check.py: resolve crash when running apt-check with
166
888 by Brian Murray
releasing package update-notifier version 3.173
167
458 by Michael Vogt
apt_check.py: refactor the code to make it easier to follow
168
def run(options=None):
471 by Michael Vogt
when running in auto-launch mode, check if security
169
170
    # we are run in "are security updates installed automatically?"
171
    # question mode
172
    if options.security_updates_unattended:
573 by Michael Vogt
merged from debian
173
        res = apt_pkg.config.find_i("APT::Periodic::Unattended-Upgrade", 0)
888 by Brian Murray
releasing package update-notifier version 3.173
174
        # print(res)
471 by Michael Vogt
when running in auto-launch mode, check if security
175
        sys.exit(res)
176
404 by Michael Vogt
* data/apt-check:
177
    # get caches
178
    try:
708 by Michael Vogt
* data/apt_check.py:
179
        cache = apt_pkg.Cache(apt.progress.base.OpProgress())
825 by Brian Murray
apt-check: port to python3, if it is available use update-manager's
180
    except SystemError as e:
888 by Brian Murray
releasing package update-notifier version 3.173
181
        sys.stderr.write("E: " + _("Error: Opening the cache (%s)") % e)
404 by Michael Vogt
* data/apt-check:
182
        sys.exit(-1)
721 by Michael Terry
Undo the Python 3 conversion until python3-debian is ready
183
    depcache = apt_pkg.DepCache(cache)
404 by Michael Vogt
* data/apt-check:
184
185
    # read the synaptic pins too
186
    if os.path.exists(SYNAPTIC_PINFILE):
573 by Michael Vogt
merged from debian
187
        depcache.read_pinfile(SYNAPTIC_PINFILE)
820.1.2 by Anders Kaseorg
apt-check: Only reinitialize the depcache if an extra pinfile was read
188
        depcache.init()
404 by Michael Vogt
* data/apt-check:
189
573 by Michael Vogt
merged from debian
190
    if depcache.broken_count > 0:
888 by Brian Murray
releasing package update-notifier version 3.173
191
        sys.stderr.write("E: " + _("Error: BrokenCount > 0"))
404 by Michael Vogt
* data/apt-check:
192
        sys.exit(-1)
130 by mvo
* report cache errors to the user
193
458 by Michael Vogt
apt_check.py: refactor the code to make it easier to follow
194
    # do the upgrade (not dist-upgrade!)
404 by Michael Vogt
* data/apt-check:
195
    try:
888 by Brian Murray
releasing package update-notifier version 3.173
196
        saveDistUpgrade(cache, depcache)
825 by Brian Murray
apt-check: port to python3, if it is available use update-manager's
197
    except SystemError as e:
888 by Brian Murray
releasing package update-notifier version 3.173
198
        sys.stderr.write("E: " + _("Error: Marking the upgrade (%s)") % e)
404 by Michael Vogt
* data/apt-check:
199
        sys.exit(-1)
96 by mvo
* debian/control:
200
957.1.3 by Julian Andres Klode
apt-check: Count enabled ESM upgrades
201
    # Check if we have ESM enabled or disabled; and if it exists in the
202
    # first place.
203
    have_esm = None        # None == does not exist
204
    for file in cache.file_list:
205
        if file.origin == "UbuntuESM" and file.archive.startswith(DISTRO):
206
            # In case of multiple ESM repos, one enabled is sufficient.
964 by Julian Andres Klode
Fix multiple disabled ESM repositories being counted as enabled ones.
207
            if depcache.policy.get_priority(file) == -32768:
208
                # We found a disabled ESM repository, but we'll only count
209
                # ESM as disabled here if we have not found any other ESM
210
                # repo, so one ESM repo being enabled means ESM is enabled.
211
                if have_esm is None:
212
                    have_esm = False
957.1.3 by Julian Andres Klode
apt-check: Count enabled ESM upgrades
213
            else:
214
                have_esm = True
215
                break
216
458 by Michael Vogt
apt_check.py: refactor the code to make it easier to follow
217
    # analyze the ugprade
404 by Michael Vogt
* data/apt-check:
218
    upgrades = 0
219
    security_updates = 0
957.1.3 by Julian Andres Klode
apt-check: Count enabled ESM upgrades
220
    esm_updates = 0
957.1.4 by Julian Andres Klode
apt-check: Tell people to enable ESM if ESM updates are available
221
    disabled_esm_updates = 0
825 by Brian Murray
apt-check: port to python3, if it is available use update-manager's
222
223
    # we need another cache that has more pkg details
224
    with apt.Cache() as aptcache:
225
        for pkg in cache.packages:
957.1.4 by Julian Andres Klode
apt-check: Tell people to enable ESM if ESM updates are available
226
            if has_disabled_esm_security_update(depcache, pkg):
227
                disabled_esm_updates += 1
228
825 by Brian Murray
apt-check: port to python3, if it is available use update-manager's
229
            # skip packages that are not marked upgraded/installed
947 by Andrea Azzarone
* data/apt_check.py, data/package-data-downloader, tests/test_pep8.py:
230
            if not (depcache.marked_install(pkg)
231
                    or depcache.marked_upgrade(pkg)):
825 by Brian Murray
apt-check: port to python3, if it is available use update-manager's
232
                continue
233
            # check if this is really a upgrade or a false positive
234
            # (workaround for ubuntu #7907)
235
            inst_ver = pkg.current_ver
236
            cand_ver = depcache.get_candidate_ver(pkg)
237
            if cand_ver == inst_ver:
238
                continue
239
            # check for security upgrades
240
            if isSecurityUpgrade(cand_ver):
957.1.3 by Julian Andres Klode
apt-check: Count enabled ESM upgrades
241
                if isESMUpgrade(cand_ver):
242
                    esm_updates += 1
825 by Brian Murray
apt-check: port to python3, if it is available use update-manager's
243
                upgrades += 1
484 by Michael Vogt
* src/apt_check.py:
244
                security_updates += 1
825 by Brian Murray
apt-check: port to python3, if it is available use update-manager's
245
                continue
246
247
            # check to see if the update is a phased one
248
            try:
249
                from UpdateManager.Core.UpdateList import UpdateList
250
                ul = UpdateList(None)
888 by Brian Murray
releasing package update-notifier version 3.173
251
                ignored = ul._is_ignored_phased_update(
252
                    aptcache[pkg.get_fullname()])
825 by Brian Murray
apt-check: port to python3, if it is available use update-manager's
253
                if ignored:
254
                    depcache.mark_keep(pkg)
255
                    continue
256
            except ImportError:
257
                pass
258
858 by Steve Langasek
Migrate fully from python2 to python3 now that python3-debian is
259
            upgrades = upgrades + 1
825 by Brian Murray
apt-check: port to python3, if it is available use update-manager's
260
858 by Steve Langasek
Migrate fully from python2 to python3 now that python3-debian is
261
            # now check for security updates that are masked by a
877 by Brian Murray
data/apt_check.py: use pkg.get_fullname() instead of pkg.name so that
262
            # candidate version from another repo (-proposed or -updates)
825 by Brian Murray
apt-check: port to python3, if it is available use update-manager's
263
            for ver in pkg.version_list:
947 by Andrea Azzarone
* data/apt_check.py, data/package-data-downloader, tests/test_pep8.py:
264
                if (inst_ver
265
                        and apt_pkg.version_compare(ver.ver_str,
266
                                                    inst_ver.ver_str) <= 0):
825 by Brian Murray
apt-check: port to python3, if it is available use update-manager's
267
                    continue
957.1.3 by Julian Andres Klode
apt-check: Count enabled ESM upgrades
268
                if isESMUpgrade(ver):
269
                    esm_updates += 1
825 by Brian Murray
apt-check: port to python3, if it is available use update-manager's
270
                if isSecurityUpgrade(ver):
271
                    security_updates += 1
272
                    break
404 by Michael Vogt
* data/apt-check:
273
274
    # print the number of upgrades
406 by Michael Vogt
- use dgettext() so that it can be used from a different
275
    if options and options.show_package_names:
458 by Michael Vogt
apt_check.py: refactor the code to make it easier to follow
276
        write_package_names(sys.stderr, cache, depcache)
407 by Michael Vogt
* data/apt_check.py:
277
    elif options and options.readable_output:
957.1.3 by Julian Andres Klode
apt-check: Count enabled ESM upgrades
278
        write_human_readable_summary(sys.stdout, upgrades, security_updates,
957.1.4 by Julian Andres Klode
apt-check: Tell people to enable ESM if ESM updates are available
279
                                     esm_updates, have_esm,
280
                                     disabled_esm_updates)
404 by Michael Vogt
* data/apt-check:
281
    else:
858 by Steve Langasek
Migrate fully from python2 to python3 now that python3-debian is
282
        # print the number of regular upgrades and the number of
404 by Michael Vogt
* data/apt-check:
283
        # security upgrades
858 by Steve Langasek
Migrate fully from python2 to python3 now that python3-debian is
284
        sys.stderr.write("%s;%s" % (upgrades, security_updates))
404 by Michael Vogt
* data/apt-check:
285
286
    # return the number of upgrades (if its used as a module)
858 by Steve Langasek
Migrate fully from python2 to python3 now that python3-debian is
287
    return(upgrades, security_updates)
288
289
290
if __name__ == "__main__":
406 by Michael Vogt
- use dgettext() so that it can be used from a different
291
    # setup a exception handler to make sure that uncaught stuff goes
292
    # to the notifier
293
    sys.excepthook = _handleException
858 by Steve Langasek
Migrate fully from python2 to python3 now that python3-debian is
294
406 by Michael Vogt
- use dgettext() so that it can be used from a different
295
    # gettext
888 by Brian Murray
releasing package update-notifier version 3.173
296
    APP = "update-notifier"
297
    DIR = "/usr/share/locale"
406 by Michael Vogt
- use dgettext() so that it can be used from a different
298
    gettext.bindtextdomain(APP, DIR)
299
    gettext.textdomain(APP)
300
301
    # check arguments
302
    parser = OptionParser()
303
    parser.add_option("-p",
304
                      "--package-names",
305
                      action="store_true",
306
                      dest="show_package_names",
888 by Brian Murray
releasing package update-notifier version 3.173
307
                      help=_("Show the packages that are "
308
                             "going to be installed/upgraded"))
484 by Michael Vogt
* src/apt_check.py:
309
    parser.add_option("",
407 by Michael Vogt
* data/apt_check.py:
310
                      "--human-readable",
311
                      action="store_true",
312
                      dest="readable_output",
313
                      help=_("Show human readable output on stdout"))
484 by Michael Vogt
* src/apt_check.py:
314
    parser.add_option("",
471 by Michael Vogt
when running in auto-launch mode, check if security
315
                      "--security-updates-unattended",
316
                      action="store_true",
317
                      help=_("Return the time in days when security updates "
318
                             "are installed unattended (0 means disabled)"))
406 by Michael Vogt
- use dgettext() so that it can be used from a different
319
    (options, args) = parser.parse_args()
320
321
    # run it
458 by Michael Vogt
apt_check.py: refactor the code to make it easier to follow
322
    init()
406 by Michael Vogt
- use dgettext() so that it can be used from a different
323
    run(options)