-
-
Save yumminhuang/8b1502a49d8b20a6ae70 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python | |
#coding=utf-8 | |
import apt | |
import apt_pkg | |
from time import strftime | |
import os | |
import subprocess | |
import sys | |
""" | |
Following functions are used to return package info of available updates. | |
See: /usr/lib/update-notifier/apt_check.py | |
""" | |
SYNAPTIC_PINFILE = "/var/lib/synaptic/preferences" | |
DISTRO = subprocess.check_output(["lsb_release", "-c", "-s"], | |
universal_newlines=True).strip() | |
def clean(cache,depcache): | |
""" unmark (clean) all changes from the given depcache """ | |
# mvo: looping is too inefficient with the new auto-mark code | |
# for pkg in cache.Packages: | |
# depcache.MarkKeep(pkg) | |
depcache.init() | |
def saveDistUpgrade(cache,depcache): | |
""" this functions mimics a upgrade but will never remove anything """ | |
depcache.upgrade(True) | |
if depcache.del_count > 0: | |
clean(cache,depcache) | |
depcache.upgrade() | |
def get_update_packages(): | |
""" | |
Return a list of dict about package updates | |
""" | |
pkgs = [] | |
apt_pkg.init() | |
# force apt to build its caches in memory for now to make sure | |
# that there is no race when the pkgcache file gets re-generated | |
apt_pkg.config.set("Dir::Cache::pkgcache","") | |
try: | |
cache = apt_pkg.Cache(apt.progress.base.OpProgress()) | |
except SystemError as e: | |
sys.stderr.write("Error: Opening the cache (%s)" % e) | |
sys.exit(-1) | |
depcache = apt_pkg.DepCache(cache) | |
# read the pin files | |
depcache.read_pinfile() | |
# read the synaptic pins too | |
if os.path.exists(SYNAPTIC_PINFILE): | |
depcache.read_pinfile(SYNAPTIC_PINFILE) | |
# init the depcache | |
depcache.init() | |
try: | |
saveDistUpgrade(cache,depcache) | |
except SystemError as e: | |
sys.stderr.write("Error: Marking the upgrade (%s)" % e) | |
sys.exit(-1) | |
# use assignment here since apt.Cache() doesn't provide a __exit__ method | |
# on Ubuntu 12.04 it looks like | |
# aptcache = apt.Cache() | |
for pkg in cache.packages: | |
if not (depcache.marked_install(pkg) or depcache.marked_upgrade(pkg)): | |
continue | |
inst_ver = pkg.current_ver | |
cand_ver = depcache.get_candidate_ver(pkg) | |
if cand_ver == inst_ver: | |
continue | |
record = {"name": pkg.name, | |
"security": isSecurityUpgrade(pkg, depcache), | |
"section": pkg.section, | |
"current_version": inst_ver.ver_str if inst_ver else '-', | |
"candidate_version": cand_ver.ver_str if cand_ver else '-', | |
"priority": cand_ver.priority_str} | |
pkgs.append(record) | |
return pkgs | |
def isSecurityUpgrade(pkg, depcache): | |
def isSecurityUpgrade_helper(ver): | |
""" check if the given version is a security update (or masks one) """ | |
security_pockets = [("Ubuntu", "%s-security" % DISTRO), | |
("gNewSense", "%s-security" % DISTRO), | |
("Debian", "%s-updates" % DISTRO)] | |
for (file, index) in ver.file_list: | |
for origin, archive in security_pockets: | |
if (file.archive == archive and file.origin == origin): | |
return True | |
return False | |
inst_ver = pkg.current_ver | |
cand_ver = depcache.get_candidate_ver(pkg) | |
if isSecurityUpgrade_helper(cand_ver): | |
return True | |
# now check for security updates that are masked by a | |
# canidate version from another repo (-proposed or -updates) | |
for ver in pkg.version_list: | |
if (inst_ver and | |
apt_pkg.version_compare(ver.ver_str, inst_ver.ver_str) <= 0): | |
#print "skipping '%s' " % ver.VerStr | |
continue | |
if isSecurityUpgrade_helper(ver): | |
return True | |
return False | |
def print_result(pkgs): | |
""" | |
Print package updates in a table | |
""" | |
security_updates = filter(lambda x: x.get('security'), pkgs) | |
text = list() | |
text.append('Check Time: %s' % strftime('%m/%d/%Y %H:%M:%S')) | |
if not pkgs: | |
text.append('No available updates on this machine.') | |
else: | |
# Updates are available, build a table | |
text.append('%d packages can be updated.' % len(pkgs)) | |
text.append('%d updates are security updates.' % len(security_updates)) | |
text.append('-' * 100) | |
# List available security updates | |
text.append('Package Name'.ljust(30) + | |
'Current Version'.ljust(30) + | |
'Latest Version'.ljust(30) + | |
'Security'.ljust(10)) | |
text.append('-' * 100) | |
for pkg in pkgs: | |
text.append('{:<30}{:<30}{:<30}{:<10}'.format(pkg.get('name'), | |
pkg.get('current_version'), | |
pkg.get('candidate_version'), | |
'*' if pkg.get('security') else '')) | |
text.append('=' * 100) | |
return '\n'.join(text) | |
if __name__ == '__main__': | |
pkgs = get_update_packages() | |
print print_result(pkgs) |
Hi @FolkertsHeiko
I am not quite sure what are the restrictions on my code if it is used in a project under GPLv3 license. Since my code is based on /usr/lib/update-notifier/apt-check
, you can refer to its license.
You code is a complete monitoring solution. Looks good! 👍
Great, Thank you @yumminhuang !
Hi, thank you very much for the contribution, I am trying to run on kali linux 2021.1 or ubuntu 20.4 and it gives me a SyntaxError: invalid syntax error
File "/home/username/.config/polybar/scripts/package_updates_check.py", line 152
print print_result (pkgs)
can it be corrected?
Hi, thank you very much for the contribution, I am trying to run on kali linux 2021.1 or ubuntu 20.4 and it gives me a SyntaxError: invalid syntax error
File "/home/username/.config/polybar/scripts/package_updates_check.py", line 152
print print_result (pkgs)can it be corrected?
@panxos Which version of Python do you have?
@panxos Change print print_result(pkgs) to print(print_result(pkgs))
If there is nothing to do, the script works fine.
However, if there are some updates to do, I get this error:
Traceback (most recent call last):
File "package_updates_check.py", line 151, in
pkgs = get_update_packages()
File "package_updates_check.py", line 81, in get_update_packages
"section": pkg.section,
AttributeError: 'apt_pkg.Package' object has no attribute 'section'
Using 3.8.10 on Ubuntu 20.04
@tmatthews5511
No guarantee, but:
The issue is that the "section" attribute has been removed from apt_pkg.Package in version 1.6. The code should be updated to either use a newer version of apt_pkg or find an alternative way to get the package info.
An alternative for the "section" attribute could be the "candidate" attribute of the apt_pkg.Package object, which returns an apt_pkg.Version object which contains a "section" attribute. For example:
for pkg in cache.packages:
if not (depcache.marked_install(pkg) or depcache.marked_upgrade(pkg)):
continue
inst_ver = pkg.current_ver
cand_ver = depcache.get_candidate_ver(pkg)
if cand_ver == inst_ver:
continue
record = {"name": pkg.name,
"security": isSecurityUpgrade(pkg, depcache),
"section": cand_ver.section,
"current_version": inst_ver.ver_str if inst_ver else '-',
"candidate_version": cand_ver.ver_str if cand_ver else '-',
"priority": cand_ver.priority_str}
pkgs.append(record)
Ubuntu: 22.04.1 LTS
Anyone running into this TypeError:
Traceback (most recent call last):
File "<path>", line 152, in <module>
print (print_result(pkgs))
File "<path>", line 134, in print_result
text.append('%d updates are security updates.' % len(security_updates))
TypeError: object of type 'filter' has no len()
can update line 126
from:
security_updates = filter(lambda x: x.get('security'), pkgs)
to:
security_updates = list(filter(lambda x: x.get('security'), pkgs))
this should resolve that TypeError
I'm getting this error when executing the script and I cannot get what it's missing. Any ideas?
Traceback (most recent call last): File "package_updates_check.py", line 149, in <module> print (print_result(pkgs)) File "package_updates_check.py", line 123, in print_result security_updates = list(filter(lambda x: x.get('security'), pkgs)) TypeError: 'NoneType' object is not iterable
I fixed all errors I faced. But it's with Discord notification. So you have to edit the last lines if you don't want to use Discord.
https://gist.github.com/kankadev/05ee806815cfb059af69e7e56c1ef637
And you have to use this directory structure:
Copy config.temp.json
to ~/discord-config.json
and paste your webhook URL if you want to use Discord or comment / delete related lines.
I'm getting this error when executing the script and I cannot get what it's missing. Any ideas?
Traceback (most recent call last): File "package_updates_check.py", line 149, in <module> print (print_result(pkgs)) File "package_updates_check.py", line 123, in print_result security_updates = list(filter(lambda x: x.get('security'), pkgs)) TypeError: 'NoneType' object is not iterable
Nevermind, already fix it :).
I'm thinking on making a fork of this script. I've been able to adapt it to be used as a monitoring script for Nagios/Icinga or similars.
@yumminhuang
Really great work and would save me a lot of time. Is it ok to use your code in a project under GPLv3 license?
Happy Coding!
Heiko.