3 # Copyright 2002 Neil Spring <nspring@cs.washington.edu>
5 # report bugs to wmbiff-devel@lists.sourceforge.net
6 # or (preferred) use the debian BTS via 'reportbug'
8 # Based on security-update-check.py by Rob Bradford
14 # re-fetch interval - only bug the server once every hour.
15 # allows wmbiff to ask us often how many packages have been
16 # updated so that the number goes back to cyan (old) from
17 # yellow (new) quickly on upgrade.
19 # this still doesn't mean we grab the whole file. we get
20 # if-modified-since. it just means we don't connect to the
21 # server more often than this.
22 # 6 hours * 60 min/hour * 60 sec/min
23 Refetch_Interval_Sec = 6 * 60 * 60
25 # as an ordinary user, we store Packages in the home directory.
26 Cachedir = ENV['HOME'] + '/.wmbiff-sdr'
28 # look for updates from this server. This script is designed around
29 # (and simplified greatly by) using just a single server.
30 Server = 'security.debian.org'
32 # extend the Array class with a max method.
35 each { |value| n = yield(n, value) }
39 inject(0) { |n, value| ((n > value) ? n : value) }
44 $stderr.puts str if($VERBOSE)
47 # to be reimplemented without execing touch.
49 debugmsg "touching #{filename}"
50 Kernel.system('/usr/bin/touch ' + filename)
53 # to be reimplemented without execing dpkg, though running
54 # dpkg excessively doesn't seem to be a bottleneck.
55 def version_a_gt_b(a, b)
56 cmd = "/usr/bin/dpkg --compare-versions %s le %s" % [ a, b ]
58 return (!Kernel.system(cmd))
61 # figure out which lists to check
62 # there can be many implementations of
63 # this behavior, this seemed simplest.
66 # we're going to make an array of arrays, for each package
67 # file, the url, the system's cache of the file, and a
68 # per-user cache of the file.
69 packagelists = Dir.glob("/var/lib/apt/lists/#{Server}*Packages").map { |pkgfile|
70 [ '/debian-security' + pkgfile.gsub(/.*#{Server}/, '').tr('_','/').gsub(/Packages/, ''), # the url path
71 pkgfile, # the system cache of the packages file. probably up-to-date.
72 # and finally, a user's cache of the page, if needed.
73 "%s/%s" % [ Cachedir, pkgfile.gsub(/.*#{Server}_/,'') ]
77 # update the user's cache if necessary.
78 packagelists.each { |urlpath, sc, uc|
79 sctime = File.stat(sc).mtime
82 uctime = File.stat(uc).mtime
83 if ( uctime < sctime ) then
84 # we have a user cache, but it is older than the system cache
85 File.unlink(uc) # delete the obsolete user cache.
93 if(Time.now > cached_time + Refetch_Interval_Sec) then
94 debugmsg "fetching #{urlpath} %s > %s + %d" % [Time.now, cached_time, Refetch_Interval_Sec]
96 test(?e, Cachedir) or Dir.mkdir(Cachedir)
98 ftp = Net::FTP.new(Server)
101 ftp.getbinaryfile('Packages.gz', uc + '.gz', 1024)
104 # need to unzip Packages.gz
105 cmd_gunzip = "gzip -df %s" % [ uc + '.gz' ]
106 Kernel.system(cmd_gunzip)
108 rescue SocketError => e
109 # if the net is down, we'll get this error; avoid printing a stack trace.
113 rescue Timeout::Error => e
114 # if the net is down, we might get this error instead.
115 # but there is no good reason to print the specific exception. (execution expired)
119 debugmsg "urlpath updated"
121 debugmsg "skipping #{urlpath}"
127 packagelists.each { |url, sc, uc|
128 File.open( (test(?e, uc)) ? uc : sc, 'r').each { |ln|
129 if(m = /^Package: (.*)/.match(ln)) then
131 elsif(m = /^Version: (.*)/.match(ln)) then
132 available[package] = m[1]
140 File.open('/var/lib/dpkg/status').each { |ln|
141 if(m = /^Package: (.*)$/.match(ln)) then
143 isinstalled = false # reset
144 elsif(m = /^Status: install ok installed/.match(ln)) then
146 elsif(m = /^Version: (.*)$/.match(ln)) then
147 isinstalled && installed[package] = m[1]
151 debugmsg "%d installed, %d available" % [ installed.length, available.length ]
155 ( installed.keys & available.keys ).each { |pkg|
156 if(version_a_gt_b(available[pkg], installed[pkg])) then
158 updated.push(pkg + ": #{available[pkg]} > #{installed[pkg]}")
162 # we're done. output a count in the format expected by wmbiff.
163 if(updatedcount > 0) then
164 puts "%d new" % [ updatedcount ]
166 puts "%d old" % [ installed.length ]
169 puts updated.join("\n")