Fixes to rpmlint and license related issues
[platform/upstream/lsb.git] / lsb_release.py
1 #!/usr/bin/python
2
3 # LSB release detection module for Debian
4 # (C) 2005-09 Chris Lawrence <lawrencc@debian.org>
5
6 #    This package is free software; you can redistribute it and/or modify
7 #    it under the terms of the GNU General Public License as published by
8 #    the Free Software Foundation; version 2 dated June, 1991.
9
10 #    This package is distributed in the hope that it will be useful,
11 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
12 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 #    GNU General Public License for more details.
14
15 #    You should have received a copy of the GNU General Public License
16 #    along with this package; if not, write to the Free Software
17 #    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
18 #    02111-1307, USA.
19
20 import sys
21 import commands
22 import os
23 import re
24
25 # XXX: Update as needed
26 # This should really be included in apt-cache policy output... it is already
27 # in the Release file...
28 RELEASE_CODENAME_LOOKUP = {
29     '1.1' : 'buzz',
30     '1.2' : 'rex',
31     '1.3' : 'bo',
32     '2.0' : 'hamm',
33     '2.1' : 'slink',
34     '2.2' : 'potato',
35     '3.0' : 'woody',
36     '3.1' : 'sarge',
37     '4.0' : 'etch',
38     '5.0' : 'lenny',
39     }
40
41 TESTING_CODENAME = 'unknown.new.testing'
42
43 def lookup_codename(release, unknown=None):
44     m = re.match(r'(\d+)\.(\d+)(r(\d+))?', release)
45     if not m:
46         return unknown
47
48     shortrelease = '%s.%s' % m.group(1,2)
49     return RELEASE_CODENAME_LOOKUP.get(shortrelease, unknown)
50
51 # LSB compliance packages... may grow eventually
52 PACKAGES = 'lsb-core lsb-cxx lsb-graphics lsb-desktop lsb-qt4 lsb-languages lsb-multimedia lsb-printing'
53
54 modnamere = re.compile(r'lsb-(?P<module>[a-z0-9]+)-(?P<arch>[^ ]+)(?: \(= (?P<version>[0-9.]+)\))?')
55
56 def valid_lsb_versions(version, module):
57     # If a module is ever released that only appears in >= version, deal
58     # with that here
59     if version == '3.0':
60         return ['2.0', '3.0']
61     elif version == '3.1':
62         if module in ('desktop', 'qt4'):
63             return ['3.1']
64         else:
65             return ['2.0', '3.0', '3.1']
66     elif version == '3.2':
67         if module == 'desktop':
68             return ['3.1', '3.2']
69         elif module == 'qt4':
70             return ['3.1']
71         elif module in ('printing', 'languages', 'multimedia'):
72             return ['3.2']
73         elif module == 'cxx':
74             return ['3.0', '3.1', '3.2']
75         else:
76             return ['2.0', '3.0', '3.1', '3.2']
77
78     return [version]
79
80 try:
81     set # introduced in 2.4
82 except NameError:
83     import sets
84     set = sets.Set
85
86 # This is Debian-specific at present
87 def check_modules_installed():
88     # Find which LSB modules are installed on this system
89     output = commands.getoutput("dpkg-query -f '${Version} ${Provides}\n' -W %s 2>/dev/null" % PACKAGES)
90     if not output:
91         return []
92
93     modules = set()
94     for line in output.split(os.linesep):
95         version, provides = line.split(' ', 1)
96         version = version.split('-', 1)[0]
97         for pkg in provides.split(','):
98             mob = modnamere.search(pkg)
99             if not mob:
100                 continue
101
102             mgroups = mob.groupdict()
103             # If no versioned provides...
104             if mgroups.get('version'):
105                 module = '%(module)s-%(version)s-%(arch)s' % mgroups
106                 modules.add(module)
107             else:
108                 module = mgroups['module']
109                 for v in valid_lsb_versions(version, module):
110                     mgroups['version'] = v
111                     module = '%(module)s-%(version)s-%(arch)s' % mgroups
112                     modules.add(module)
113
114     modules = list(modules)
115     modules.sort()
116     return modules
117
118 longnames = {'v' : 'version', 'o': 'origin', 'a': 'suite',
119              'c' : 'component', 'l': 'label'}
120
121 def parse_policy_line(data):
122     retval = {}
123     bits = data.split(',')
124     for bit in bits:
125         kv = bit.split('=', 1)
126         if len(kv) > 1:
127             k, v = kv[:2]
128             if k in longnames:
129                 retval[longnames[k]] = v
130     return retval
131
132 def parse_apt_policy():
133     data = []
134     
135     policy = commands.getoutput('LANG=C apt-cache policy 2>/dev/null')
136     for line in policy.split('\n'):
137         line = line.strip()
138         m = re.match(r'(\d+)', line)
139         if m:
140             priority = int(m.group(1))
141         if line.startswith('release'):
142             bits = line.split(' ', 1)
143             if len(bits) > 1:
144                 data.append( (priority, parse_policy_line(bits[1])) )
145
146     return data
147
148 def guess_release_from_apt(origin='Debian', component='main',
149                            ignoresuites=('experimental'),
150                            label='Debian'):
151     releases = parse_apt_policy()
152
153     if not releases:
154         return None
155
156     # We only care about the specified origin, component, and label
157     releases = [x for x in releases if (
158         x[1].get('origin', '') == origin and
159         x[1].get('component', '') == component and
160         x[1].get('label', '') == label)]
161
162     # Check again to make sure we didn't wipe out all of the releases
163     if not releases:
164         return None
165     
166     releases.sort()
167     releases.reverse()
168
169     # We've sorted the list by descending priority, so the first entry should
170     # be the "main" release in use on the system
171
172     return releases[0][1]
173
174 def guess_debian_release():
175     distinfo = {'ID' : 'Debian'}
176
177     kern = os.uname()[0]
178     if kern in ('Linux', 'Hurd', 'NetBSD'):
179         distinfo['OS'] = 'GNU/'+kern
180     elif kern == 'FreeBSD':
181         distinfo['OS'] = 'GNU/k'+kern
182     else:
183         distinfo['OS'] = 'GNU'
184
185     distinfo['DESCRIPTION'] = '%(ID)s %(OS)s' % distinfo
186
187     if os.path.exists('/etc/debian_version'):
188         try:
189             release = open('/etc/debian_version').read().strip()
190         except IOError, msg:
191             print >> sys.stderr, 'Unable to open /etc/debian_version:', str(msg)
192             release = 'unknown'
193             
194         if not release[0:1].isalpha():
195             # /etc/debian_version should be numeric
196             codename = lookup_codename(release, 'n/a')
197             distinfo.update({ 'RELEASE' : release, 'CODENAME' : codename })
198         elif release.endswith('/sid'):
199             if release.rstrip('/sid').lower().isalpha() != 'testing':
200                 global TESTING_CODENAME
201                 TESTING_CODENAME = release.rstrip('/sid')
202             distinfo['RELEASE'] = 'testing/unstable'
203         else:
204             distinfo['RELEASE'] = release
205
206     # Only use apt information if we did not get the proper information
207     # from /etc/debian_version or if we don't have a codename
208     # (which will happen if /etc/debian_version does not contain a
209     # number but some text like 'testing/unstable' or 'lenny/sid')
210     #
211     # This is slightly faster and less error prone in case the user
212     # has an entry in his /etc/apt/sources.list but has not actually
213     # upgraded the system.
214     rinfo = guess_release_from_apt()
215     if rinfo and not distinfo.get('CODENAME'):
216         release = rinfo.get('version')
217         if release:
218             codename = lookup_codename(release, 'n/a')
219         else:
220             release = rinfo.get('suite', 'unstable')
221             if release == 'testing':
222                 # Would be nice if I didn't have to hardcode this.
223                 codename = TESTING_CODENAME
224             else:
225                 codename = 'sid'
226         distinfo.update({ 'RELEASE' : release, 'CODENAME' : codename })
227
228     if distinfo.get('RELEASE'):
229         distinfo['DESCRIPTION'] += ' %(RELEASE)s' % distinfo
230     if distinfo.get('CODENAME'):
231         distinfo['DESCRIPTION'] += ' (%(CODENAME)s)' % distinfo
232
233     return distinfo
234
235 # Whatever is guessed above can be overridden in /etc/lsb-release
236 def get_lsb_information():
237     distinfo = {}
238     if os.path.exists('/etc/lsb-release'):
239         try:
240             for line in open('/etc/lsb-release'):
241                 line = line.strip()
242                 if not line:
243                     continue
244                 # Skip invalid lines
245                 if not '=' in line:
246                     continue
247                 var, arg = line.split('=', 1)
248                 if var.startswith('DISTRIB_'):
249                     var = var[8:]
250                     if arg.startswith('"') and arg.endswith('"'):
251                         arg = arg[1:-1]
252                     if arg: # Ignore empty arguments
253                         distinfo[var] = arg
254         except IOError, msg:
255             print >> sys.stderr, 'Unable to open /etc/lsb-release:', str(msg)
256             
257     return distinfo
258
259 def get_distro_information():
260     distinfo = guess_debian_release()
261     distinfo.update(get_lsb_information())
262     return distinfo
263
264 def test():
265     print get_distro_information()
266     print check_modules_installed()
267
268 if __name__ == '__main__':
269     test()