#!/usr/bin/python # lsb_release command for Debian # (C) 2005-08 Chris Lawrence # This package is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; version 2 dated June, 1991. # This package is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this package; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA # 02111-1307, USA. from optparse import OptionParser import sys import commands import os import re # XXX: Update as needed # This should really be included in apt-cache policy output... it is already # in the Release file... RELEASE_CODENAME_LOOKUP = { '1.1' : 'buzz', '1.2' : 'rex', '1.3' : 'bo', '2.0' : 'hamm', '2.1' : 'slink', '2.2' : 'potato', '3.0' : 'woody', '3.1' : 'sarge', '4.0' : 'etch', '5.0' : 'lenny', } TESTING_CODENAME = 'lenny' def lookup_codename(release, unknown=None): m = re.match(r'(\d+)\.(\d+)(r(\d+))?', release) if not m: return unknown shortrelease = '%s.%s' % m.group(1,2) return RELEASE_CODENAME_LOOKUP.get(shortrelease, unknown) # LSB compliance packages... may grow eventually PACKAGES = 'lsb-core lsb-cxx lsb-graphics lsb-desktop lsb-qt4 lsb-languages lsb-multimedia lsb-printing' modnamere = re.compile(r'lsb-(?P[a-z0-9]+)-(?P[^ ]+)(?: \(= (?P[0-9.]+)\))?') def valid_lsb_versions(version, module): # If a module is ever released that only appears in >= version, deal # with that here if version == '3.0': return ['2.0', '3.0'] elif version == '3.1': if module in ('desktop', 'qt4'): return ['3.1'] else: return ['2.0', '3.0', '3.1'] elif version == '3.2': if module == 'desktop': return ['3.1', '3.2'] elif module == 'qt4': return ['3.1'] elif module in ('printing', 'languages', 'multimedia'): return ['3.2'] elif module == 'cxx': return ['3.0', '3.1', '3.2'] else: return ['2.0', '3.0', '3.1', '3.2'] return [version] import sets # This is Debian-specific at present def check_modules_installed(): # Find which LSB modules are installed on this system output = commands.getoutput("dpkg-query -f '${Version} ${Provides}\n' -W %s 2>/dev/null" % PACKAGES) if not output: return [] modules = sets.Set() for line in output.split(os.linesep): version, provides = line.split(' ', 1) version = version.split('-', 1)[0] for pkg in provides.split(','): mob = modnamere.search(pkg) if not mob: continue mgroups = mob.groupdict() # If no versioned provides... if mgroups.get('version'): module = '%(module)s-%(version)s-%(arch)s' % mgroups modules.add(module) else: module = mgroups['module'] for v in valid_lsb_versions(version, module): mgroups['version'] = v module = '%(module)s-%(version)s-%(arch)s' % mgroups modules.add(module) modules = list(modules) modules.sort() return modules longnames = {'v' : 'version', 'o': 'origin', 'a': 'suite', 'c' : 'component', 'l': 'label'} def parse_policy_line(data): retval = {} bits = data.split(',') for bit in bits: kv = bit.split('=', 1) if len(kv) > 1: k, v = kv[:2] if k in longnames: retval[longnames[k]] = v return retval def parse_apt_policy(): data = [] policy = commands.getoutput('apt-cache policy 2>/dev/null') for line in policy.split('\n'): line = line.strip() m = re.match(r'(\d+)', line) if m: priority = int(m.group(1)) if line.startswith('release'): bits = line.split(' ', 1) if len(bits) > 1: data.append( (priority, parse_policy_line(bits[1])) ) return data def guess_release_from_apt(origin='Debian', component='main', ignoresuites=('experimental'), label='Debian'): releases = parse_apt_policy() if not releases: return None # We only care about the specified origin, component, and label releases = [x for x in releases if ( x[1].get('origin', '') == origin and x[1].get('component', '') == component and x[1].get('label', '') == label)] # Check again to make sure we didn't wipe out all of the releases if not releases: return None releases.sort() releases.reverse() # We've sorted the list by descending priority, so the first entry should # be the "main" release in use on the system return releases[0][1] def guess_debian_release(): distinfo = {'ID' : 'Debian'} kern = os.uname()[0] if kern in ('Linux', 'Hurd', 'NetBSD'): distinfo['OS'] = 'GNU/'+kern elif kern == 'FreeBSD': distinfo['OS'] = 'GNU/k'+kern else: distinfo['OS'] = 'GNU' distinfo['DESCRIPTION'] = '%(ID)s %(OS)s' % distinfo if os.path.exists('/etc/debian_version'): release = open('/etc/debian_version').read().strip() if not release[0:1].isalpha(): # /etc/debian_version should be numeric codename = lookup_codename(release, 'n/a') distinfo.update({ 'RELEASE' : release, 'CODENAME' : codename }) elif release.endswith('/sid'): distinfo['RELEASE'] = 'testing/unstable' else: distinfo['RELEASE'] = release # Only use apt information if we did not get the proper information # from /etc/debian_version or if we don't have a codename # (which will happen if /etc/debian_version does not contain a # number but some text like 'testing/unstable' or 'lenny/sid') # # This is slightly faster and less error prone in case the user # has an entry in his /etc/apt/sources.list but has not actually # upgraded the system. rinfo = guess_release_from_apt() if rinfo and not distinfo.get('CODENAME'): release = rinfo.get('version') if release: codename = lookup_codename(release, 'n/a') else: release = rinfo.get('suite', 'unstable') if release == 'testing': # Would be nice if I didn't have to hardcode this. codename = TESTING_CODENAME else: codename = 'sid' distinfo.update({ 'RELEASE' : release, 'CODENAME' : codename }) if distinfo.get('RELEASE'): distinfo['DESCRIPTION'] += ' %(RELEASE)s' % distinfo if distinfo.get('CODENAME'): distinfo['DESCRIPTION'] += ' (%(CODENAME)s)' % distinfo return distinfo # Whatever is guessed above can be overridden in /etc/lsb-release def get_lsb_information(): distinfo = {} if os.path.exists('/etc/lsb-release'): for line in open('/etc/lsb-release'): line = line.strip() if not line: continue # Skip invalid lines if not '=' in line: continue var, arg = line.split('=', 1) if var.startswith('DISTRIB_'): var = var[8:] if arg.startswith('"') and arg.endswith('"'): arg = arg[1:-1] distinfo[var] = arg return distinfo def get_distro_information(): distinfo = guess_debian_release() distinfo.update(get_lsb_information()) return distinfo def main(): parser = OptionParser() parser.add_option('-v', '--version', dest='version', action='store_true', default=False, help="show LSB modules this system supports") parser.add_option('-i', '--id', dest='id', action='store_true', default=False, help="show distributor ID") parser.add_option('-d', '--description', dest='description', default=False, action='store_true', help="show description of this distribution") parser.add_option('-r', '--release', dest='release', default=False, action='store_true', help="show release number of this distribution") parser.add_option('-c', '--codename', dest='codename', default=False, action='store_true', help="show code name of this distribution") parser.add_option('-a', '--all', dest='all', default=False, action='store_true', help="show all of the above information") parser.add_option('-s', '--short', dest='short', action='store_true', default=False, help="show requested information in short format") (options, args) = parser.parse_args() if args: parser.error("No arguments are permitted") short = (options.short) all = (options.all) none = not (options.all or options.version or options.id or options.description or options.codename or options.release) distinfo = get_distro_information() if none or all or options.version: verinfo = check_modules_installed() if not verinfo: print >> sys.stderr, "No LSB modules are available." elif short: print ':'.join(verinfo) else: print 'LSB Version:\t' + ':'.join(verinfo) if options.id or all: if short: print distinfo.get('ID', 'n/a') else: print 'Distributor ID:\t%s' % distinfo.get('ID', 'n/a') if options.description or all: if short: print distinfo.get('DESCRIPTION', 'n/a') else: print 'Description:\t%s' % distinfo.get('DESCRIPTION', 'n/a') if options.release or all: if short: print distinfo.get('RELEASE', 'n/a') else: print 'Release:\t%s' % distinfo.get('RELEASE', 'n/a') if options.codename or all: if short: print distinfo.get('CODENAME', 'n/a') else: print 'Codename:\t%s' % distinfo.get('CODENAME', 'n/a') if __name__ == '__main__': main()