MythTV master
os_detect.py
Go to the documentation of this file.
1# -*- coding: utf-8 -*-
2
3# smolt - Fedora hardware profiler
4#
5# Copyright (C) 2008 James Meyer <james.meyer@operamail.com>
6# Copyright (C) 2008 Yaakov M. Nemoy <loupgaroublond@gmail.com>
7# Copyright (C) 2009 Carlos Goncalves <mail@cgoncalves.info>
8# Copyright (C) 2009 Francois Cami <fcami@fedoraproject.org>
9# Copyright (C) 2010 Mike McGrath <mmcgrath@redhat.com>
10# Copyright (C) 2012 Raymond Wagner <rwagner@mythtv.org>
11#
12# This program is free software; you can redistribute it and/or modify
13# it under the terms of the GNU General Public License as published by
14# the Free Software Foundation; either version 2 of the License, or
15# (at your option) any later version.
16#
17# This program is distributed in the hope that it will be useful,
18# but WITHOUT ANY WARRANTY; without even the implied warranty of
19# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20# GNU General Public License for more details.
21#
22# You should have received a copy of the GNU General Public License
23# along with this program; if not, write to the Free Software
24# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
25
26from builtins import object
27import os
28
30 # provide global sequencing for OS class and subclasses to ensure
31 # the be tested in proper order
32 nextorder = 0
33 def __new__(mcs, name, bases, attrs):
34 attrs['_order'] = mcs.nextorder
35 mcs.nextorder += 1
36 return type.__new__(mcs, name, bases, attrs)
37
38class OS(metaclass=OrderedType):
39 _requires_func = True
40 def __init__(self, ostype=-1, func=None, inst=None):
41 if callable(ostype):
42 # assume function being supplied directly
43 func = ostype
44 ostype = None
45
46 # use None to ignore os.type() check, or -1 to default to 'posix'
47 self.ostype = ostype if ostype != -1 else 'posix'
48
49 if (func is not None) and not callable(func):
50 raise TypeError((self.__class__.__name__ + "() requires a " \
51 "provided function be callable."))
52
53 self.func = func
54 self.inst = inst
55
56 if func:
57 self.__doc__ = func.__doc__
58 self.__name__ = func.__name__
59 self.__module__ = func.__module__
60
61 def __get__(self, inst, owner):
62 if inst is None:
63 # class attribute, return self
64 return inst
65 func = self.func.__get__(inst, owner) if self.func else None
66 return self.__class__(self.ostype, func, inst)
67
68 def __call__(self, func=None):
69 if self.inst is None:
70 # this is being called as a classmethod, prior to the class
71 # being initialized into an object. we want to operate as a
72 # descriptor, and receive a function as an argument
73 if self.func is not None:
74 raise TypeError((self.__class__.__name__ + "() has already " \
75 "been given a processing function"))
76 if (func is None) or not callable(func):
77 raise TypeError((self.__class__.__name__ + "() takes exactly " \
78 "one callable as a function, (0 given)"))
79 self.func = func
80 else:
81 if self._requires_func and (self.func is None):
82 raise RuntimeError((self.__class__.__name__ + "() has not " \
83 "been given a callable function"))
84
85 # return boolean as to whether match was successful
86
87 if (self.ostype is not None) and (os.name != self.ostype):
88 # os.type checking is enabled, and does not match
89 return False
90
91 return self.do_test()
92
93 def do_test(self, *args, **kwargs):
94 try:
95 # wrap function to handle failure for whatever reason
96 res = self.func(*args, **kwargs)
97 except:
98 return False
99 else:
100 if res:
101 # function returned positive, store it and return True
102 self.inst.name = res
103 return True
104 # function returned negative, return False so loop proceeds
105 return False
106
107class OSWithFile( OS ):
108 _requires_func = False
109 def __init__(self, filename, ostype='posix', func=None, inst=None):
110 self.filename = filename
111 super(OSWithFile, self).__init__(ostype, func, inst)
112
113 def __get__(self, inst, owner):
114 if inst is None:
115 return inst
116 func = self.func.__get__(inst, owner) if self.func else None
117 return self.__class__(self.filename, self.ostype, func, inst)
118
119 def do_test(self, *args, **kwargs):
120 if not os.path.exists(self.filename):
121 # filename does not exist, so assume no match
122 return False
123
124 text = open(self.filename).read().strip()
125 if self.func:
126 # pass text into function for further processing
127 return super(OSWithFile, self).do_test(text)
128 else:
129 # store text as version string, and report success
130 self.inst.name = text
131 return True
132
134 @property
135 def uname(self):
136 # only bother running this once, store the result
137 try: return self._uname
138 except AttributeError: pass
139
140 try:
141 # requires subprocess to operate
142 from subprocess import Popen, PIPE
143 except ImportError:
144 return {}
145
146 # requires uname. if need be, this can be modified to try PATH
147 # searching in the environment.
148 path = '/usr/bin/uname'
149 if not os.path.exists(path):
150 return {}
151
152 self._uname = {}
153 # pull OS name and release from uname. more can be added if needed
154 for k,v in (('OS', '-s'),
155 ('version', '-r')):
156 p = Popen([path, v], stdout=PIPE)
157 p.wait()
158 self._uname[k] = p.stdout.read().strip()
159
160 return self._uname
161
162 def do_test(self, *args, **kwargs):
163 if len(self.uname) == 0:
164 return False
165 return super(OSFromUname, self).do_test(**self.uname)
166
168 def __new__(mcs, name, bases, attrs):
169 OSs = []
170 for k,v in list(attrs.items()):
171 if isinstance(v, OS):
172 # build list of stored OS types
173 OSs.append((v._order, k))
174 # sort by ordering value inserted by OrderedType metaclass
175 attrs['_oslist'] = [i[1] for i in sorted(OSs)]
176 return type.__new__(mcs, name, bases, attrs)
177
178 def __call__(cls):
179 obj = cls.__new__(cls)
180 obj.__init__()
181 for attr in obj._oslist:
182 # loop through all availble OS types in specified order
183 if getattr(obj, attr)():
184 # if type returns success, return value
185 return obj.name
186 else:
187 # fall through to Unknown
188 return 'Unknown'
189
190class get_os_info(metaclass=OSInfoType):
191 @OS('nt')
192 def windows(self):
193 win_version = {
194 (1, 4, 0): '95',
195 (1, 4, 10): '98',
196 (1, 4, 90): 'ME',
197 (2, 4, 0): 'NT',
198 (2, 5, 0): '2000',
199 (2, 5, 1): 'XP'
200 }[os.sys.getwindowsversion()[3],
201 os.sys.getwindowsversion()[0],
202 os.sys.getwindowsversion()[1] ]
203 return "Windows " + win_version
204
205 blag_linux = OSWithFile('/etc/blag-release')
206 mythvantage = OSWithFile('/etc/mythvantage-release')
207 knoppmyth = OSWithFile('/etc/KnoppMyth-version')
208 linhes = OSWithFile('/etc/LinHES-release')
209 mythdora = OSWithFile('/etc/mythdora-release')
210
211 @OSWithFile('/etc/arch-release')
212 def archlinux(self, text):
213 return 'Arch Linux'
214
215 @OSWithFile('/etc/aurox-release')
216 def auroxlinux(self, text):
217 return 'Aurox Linux'
218
219 conectiva = OSWithFile('/etc/conectiva-release')
220 debian = OSWithFile('/etc/debian_release')
221
222 @OSWithFile('/etc/debian_version')
223 def ubuntu(self, text):
224 text = open('/etc/issue.net').read().strip()
225 if text.find('Ubuntu'):
226 try:
227 mtext = open('/var/log/installer/media-info').read().strip()
228 except:
229 pass
230 else:
231 if 'Mythbuntu' in mtext:
232 text.replace('Ubuntu', 'Mythbuntu')
233 return text
234 return False
235
236 debian2 = OSWithFile('/etc/debian_version')
237 fedora = OSWithFile('/etc/fedora-release')
238 gentoo = OSWithFile('/etc/gentoo-release')
239 lfsfile = OSWithFile('/etc/lfs-release')
240 mandrake = OSWithFile('/etc/mandrake-release')
241 mandriva = OSWithFile('/etc/mandriva-release')
242 pardus = OSWithFile('/etc/pardus-release')
243 slackware = OSWithFile('/etc/slackware-release')
244 solaris = OSWithFile('/etc/release')
245 sunjds = OSWithFile('/etc/sun-release')
246 pldlinux = OSWithFile('/etc/pld-release')
247
248 @OSWithFile('/etc/SuSE-release')
249 def suselinux(self, text):
250 import re
251 text = text.split('\n')[0].strip()
252 return re.sub('\‍(\w*\‍)$', '', text)
253
254 yellowdog = OSWithFile('/etc/yellowdog-release')
255 redhat = OSWithFile('/etc/redhat-release')
256
257 @OSFromUname
258 def freebsd(self, OS, version):
259 if OS == 'FreeBSD':
260 return 'FreeBSD '+version
261 return False
262
263 @OSFromUname
264 def OSX(self, OS, version):
265 if OS != 'Darwin':
266 return False
267 major,minor,point = [int(a) for a in version.split('.')]
268 return 'OS X 10.%s.%s' % (major-4, minor)
269
270 @OS
272 from subprocess import Popen, PIPE
273 executable = 'lsb_release'
274 for path in os.environ['PATH'].split(':'):
275 fullpath = os.path.join(path, executable)
276 if os.path.exists(fullpath):
277 break
278 else:
279 return False
280
281 p = Popen([fullpath, '--id', '--codename', '--release', '--short'],
282 stdout=PIPE, close_fds=True)
283 p.wait()
284 return p.stdout.read().decode().strip().replace('\n', ' ')
285
286 @OSFromUname
287 def Linux(self, OS, version):
288 if OS == 'Linux':
289 return 'Unknown Linux '+version
290 return False
291
292
293if __name__ == '__main__':
294 results = get_os_info()
295 print('Test results="{}"'.format(results))
def do_test(self, *args, **kwargs)
Definition: os_detect.py:162
def __new__(mcs, name, bases, attrs)
Definition: os_detect.py:168
def __init__(self, filename, ostype='posix', func=None, inst=None)
Definition: os_detect.py:109
def __get__(self, inst, owner)
Definition: os_detect.py:113
def do_test(self, *args, **kwargs)
Definition: os_detect.py:119
def __call__(self, func=None)
Definition: os_detect.py:68
def do_test(self, *args, **kwargs)
Definition: os_detect.py:93
def __init__(self, ostype=-1, func=None, inst=None)
Definition: os_detect.py:40
def __get__(self, inst, owner)
Definition: os_detect.py:61
def __new__(mcs, name, bases, attrs)
Definition: os_detect.py:33
def freebsd(self, OS, version)
Definition: os_detect.py:258
def OSX(self, OS, version)
Definition: os_detect.py:264
def Linux(self, OS, version)
Definition: os_detect.py:287
def read(device=None, features=[])
Definition: disc.py:35
static void print(const QList< uint > &raw_minimas, const QList< uint > &raw_maximas, const QList< float > &minimas, const QList< float > &maximas)