Allow a subset of checks to be run.
[rcheck] / check.d / 50.updates
diff --git a/check.d/50.updates b/check.d/50.updates
new file mode 100755 (executable)
index 0000000..9eb27a4
--- /dev/null
@@ -0,0 +1,68 @@
+#! /usr/bin/python
+###
+### Report on available package updates, including security updates.
+
+import time as T
+import os as OS
+import errno as E
+
+import apt_pkg as APT
+import apt.cache as AC
+
+###--------------------------------------------------------------------------
+### Some utility functions.
+
+def cache_up_to_date_p():
+  """Answer whether the cache is up-to-date."""
+
+  ## I use the same stamp file as APT's standard periodic update service.
+  ## This needs prodding via a hook in the APT configuration.
+  now = T.time()
+  try:
+    last = OS.path.getmtime('/var/lib/apt/periodic/update-stamp')
+  except OSError, err:
+    if err.errno == E.ENOENT:
+      return False
+  return now - last < 86400
+
+def upgradable_packages():
+  """Return a list of packages for which updates are available."""
+  cache = AC.Cache()
+  return [pkg for pkg in cache if pkg.is_upgradable]
+
+def security_updates_p(pkg):
+  """Answer whether any update for PKG is security-relevant."""
+
+  ## There doesn't seem to be a good way of doing this.  For distributions
+  ## like `testing', security updates are folded into the main distribution
+  ## after the usual triage process, so they stop looking like security
+  ## updates.  Worse, for `unstable' there aren't distinct security updates
+  ## anyway: they're all just thrown into the mixer.  The good way to tell
+  ## would be to fetch the changelog and look for urgent changes.  Debian's
+  ## Aptitude checks explicitly for `security.debian.org'.  This check at
+  ## least also captures Ubuntu.
+  for v in pkg.versions:
+    if v < pkg.installed:
+      continue
+    for o in v.origins:
+      if o.site.startswith('security.'):
+        return True
+  return False
+
+###--------------------------------------------------------------------------
+### Main program.
+
+if not cache_up_to_date_p():
+  print 'W: package cache is more than 24 hours of date'
+updates = upgradable_packages()
+if updates:
+  plural = len(updates) != 1
+  print 'I: updates available for %d %s' % \
+        (len(updates), plural and 'packages' or 'package')
+sec = [pkg for pkg in updates if security_updates_p(pkg)]
+if sec:
+  plural = len(sec) != 1
+  print 'W: security updates available for %d %s' % \
+        (len(sec), plural and 'packages' or 'package')
+
+###----- That's all, folks --------------------------------------------------