4
# nice apt-get -s -o Debug::NoLocking=true upgrade | grep ^Inst
10
from optparse import OptionParser
14
SYNAPTIC_PINFILE = "/var/lib/synaptic/preferences"
15
DISTRO = subprocess.check_output(
16
["lsb_release", "-c", "-s"],
17
universal_newlines=True).strip()
21
return gettext.dgettext("update-notifier", msg)
24
def _handleException(type, value, tb):
25
sys.stderr.write("E: " + _("Unknown Error: '%s' (%s)") % (type, value))
29
def clean(cache, depcache):
30
" unmark (clean) all changes from the given depcache "
31
# mvo: looping is too inefficient with the new auto-mark code
32
# for pkg in cache.Packages:
33
# depcache.MarkKeep(pkg)
37
def saveDistUpgrade(cache, depcache):
38
""" this function mimics a upgrade but will never remove anything """
39
depcache.upgrade(True)
40
if depcache.del_count > 0:
41
clean(cache, depcache)
45
def isSecurityUpgrade(ver):
46
" check if the given version is a security update (or masks one) "
47
security_pockets = [("Ubuntu", "%s-security" % DISTRO),
48
("UbuntuESM", "%s-security" % DISTRO),
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):
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):
66
def write_package_names(outstream, cache, depcache):
67
" write out package names that change to outstream "
68
pkgs = [pkg for pkg in cache.packages if depcache.marked_install(pkg)
69
or depcache.marked_upgrade(pkg)]
70
outstream.write("\n".join([p.name for p in pkgs]))
73
def write_human_readable_summary(outstream, upgrades, security_updates,
74
esm_updates, have_esm, disabled_esm_updates):
75
" write out human summary summary to outstream "
76
if have_esm is not None:
78
outstream.write(gettext.dgettext("update-notifier",
79
"UA Infrastructure Extended "
80
"Security Maintenance (ESM) is "
83
outstream.write(gettext.dgettext("update-notifier",
84
"UA Infrastructure Extended "
85
"Security Maintenance (ESM) is "
87
outstream.write("\n\n")
89
outstream.write(gettext.dngettext("update-notifier",
90
"%i update can be installed "
92
"%i updates can be installed "
97
outstream.write(gettext.dngettext("update-notifier",
98
"%i of these updates is provided "
99
"through UA Infrastructure ESM.",
100
"%i of these updates are "
101
"provided through UA "
102
"Infrastructure ESM.",
105
outstream.write("\n")
106
outstream.write(gettext.dngettext("update-notifier",
107
"%i of these updates is a "
109
"%i of these updates are "
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"))
118
if have_esm is not None and not have_esm:
119
outstream.write("\n")
120
if disabled_esm_updates > 0:
121
outstream.write("\n")
122
outstream.write(gettext.dngettext("update-notifier",
123
"Enable UA Infrastructure ESM "
124
"to receive %i additional "
126
"Enable UA Infrastructure ESM "
127
"to receive %i additional "
129
disabled_esm_updates) %
130
disabled_esm_updates)
132
outstream.write("\n")
133
outstream.write(gettext.dgettext("update-notifier",
134
"Enable UA Infrastructure ESM to "
135
"receive additional future "
136
"security updates."))
137
outstream.write("\n")
138
outstream.write(gettext.dgettext("update-notifier",
139
"See https://ubuntu.com/esm "
140
"or run: sudo ua status"))
141
outstream.write("\n")
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
150
for ver in pkg.version_list:
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):
162
" init the system, be nice "
163
# FIXME: do a ionice here too?
168
def run(options=None):
170
# we are run in "are security updates installed automatically?"
172
if options.security_updates_unattended:
173
res = apt_pkg.config.find_i("APT::Periodic::Unattended-Upgrade", 0)
179
cache = apt_pkg.Cache(apt.progress.base.OpProgress())
180
except SystemError as e:
181
sys.stderr.write("E: " + _("Error: Opening the cache (%s)") % e)
183
depcache = apt_pkg.DepCache(cache)
185
# read the synaptic pins too
186
if os.path.exists(SYNAPTIC_PINFILE):
187
depcache.read_pinfile(SYNAPTIC_PINFILE)
190
if depcache.broken_count > 0:
191
sys.stderr.write("E: " + _("Error: BrokenCount > 0"))
194
# do the upgrade (not dist-upgrade!)
196
saveDistUpgrade(cache, depcache)
197
except SystemError as e:
198
sys.stderr.write("E: " + _("Error: Marking the upgrade (%s)") % e)
201
# Check if we have ESM enabled or disabled; and if it exists in the
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.
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.
217
# analyze the ugprade
221
disabled_esm_updates = 0
223
# we need another cache that has more pkg details
224
with apt.Cache() as aptcache:
225
for pkg in cache.packages:
226
if has_disabled_esm_security_update(depcache, pkg):
227
disabled_esm_updates += 1
229
# skip packages that are not marked upgraded/installed
230
if not (depcache.marked_install(pkg)
231
or depcache.marked_upgrade(pkg)):
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:
239
# check for security upgrades
240
if isSecurityUpgrade(cand_ver):
241
if isESMUpgrade(cand_ver):
244
security_updates += 1
247
# check to see if the update is a phased one
249
from UpdateManager.Core.UpdateList import UpdateList
250
ul = UpdateList(None)
251
ignored = ul._is_ignored_phased_update(
252
aptcache[pkg.get_fullname()])
254
depcache.mark_keep(pkg)
259
upgrades = upgrades + 1
261
# now check for security updates that are masked by a
262
# candidate version from another repo (-proposed or -updates)
263
for ver in pkg.version_list:
265
and apt_pkg.version_compare(ver.ver_str,
266
inst_ver.ver_str) <= 0):
268
if isESMUpgrade(ver):
270
if isSecurityUpgrade(ver):
271
security_updates += 1
274
# print the number of upgrades
275
if options and options.show_package_names:
276
write_package_names(sys.stderr, cache, depcache)
277
elif options and options.readable_output:
278
write_human_readable_summary(sys.stdout, upgrades, security_updates,
279
esm_updates, have_esm,
280
disabled_esm_updates)
282
# print the number of regular upgrades and the number of
284
sys.stderr.write("%s;%s" % (upgrades, security_updates))
286
# return the number of upgrades (if its used as a module)
287
return(upgrades, security_updates)
290
if __name__ == "__main__":
291
# setup a exception handler to make sure that uncaught stuff goes
293
sys.excepthook = _handleException
296
APP = "update-notifier"
297
DIR = "/usr/share/locale"
298
gettext.bindtextdomain(APP, DIR)
299
gettext.textdomain(APP)
302
parser = OptionParser()
303
parser.add_option("-p",
306
dest="show_package_names",
307
help=_("Show the packages that are "
308
"going to be installed/upgraded"))
309
parser.add_option("",
312
dest="readable_output",
313
help=_("Show human readable output on stdout"))
314
parser.add_option("",
315
"--security-updates-unattended",
317
help=_("Return the time in days when security updates "
318
"are installed unattended (0 means disabled)"))
319
(options, args) = parser.parse_args()