rename all micng to mic
[tools/mic.git] / mic / utils / rpmmisc.py
1 import rpm, os, sys, re
2 import locale
3 import subprocess
4 import logging
5
6 class RPMInstallCallback:
7     """
8     command line callback class for callbacks from the RPM library.
9     """
10
11     def __init__(self, ts, output=1):
12         self.output = output
13         self.callbackfilehandles = {}
14         self.total_actions = 0
15         self.total_installed = 0
16         self.installed_pkg_names = []
17         self.total_removed = 0
18         self.mark = "+"
19         self.marks = 40
20         self.lastmsg = None
21         self.tsInfo = None # this needs to be set for anything else to work
22         self.ts = ts
23         self.logString = []
24
25     def _dopkgtup(self, hdr):
26         tmpepoch = hdr['epoch']
27         if tmpepoch is None: epoch = '0'
28         else: epoch = str(tmpepoch)
29
30         return (hdr['name'], hdr['arch'], epoch, hdr['version'], hdr['release'])
31
32     def _makeHandle(self, hdr):
33         handle = '%s:%s.%s-%s-%s' % (hdr['epoch'], hdr['name'], hdr['version'],
34           hdr['release'], hdr['arch'])
35
36         return handle
37
38     def _localprint(self, msg):
39         if self.output:
40             print msg
41
42     def _makefmt(self, percent, progress = True):
43         l = len(str(self.total_actions))
44         size = "%s.%s" % (l, l)
45         fmt_done = "[%" + size + "s/%" + size + "s]"
46         done = fmt_done % (self.total_installed + self.total_removed,
47                            self.total_actions)
48         marks = self.marks - (2 * l)
49         width = "%s.%s" % (marks, marks)
50         fmt_bar = "%-" + width + "s"
51         if progress:
52             bar = fmt_bar % (self.mark * int(marks * (percent / 100.0)), )
53             fmt = "\r  %-10.10s: " + bar + " " + done
54         else:
55             bar = fmt_bar % (self.mark * marks, )
56             fmt = "  %-10.10s: "  + bar + " " + done 
57         return fmt
58
59     def _logPkgString(self, hdr):
60         """return nice representation of the package for the log"""
61         (n,a,e,v,r) = self._dopkgtup(hdr)
62         if e == '0':
63             pkg = '%s.%s %s-%s' % (n, a, v, r)
64         else:
65             pkg = '%s.%s %s:%s-%s' % (n, a, e, v, r)
66
67         return pkg
68
69     def callback(self, what, bytes, total, h, user):
70         if what == rpm.RPMCALLBACK_TRANS_START:
71             if bytes == 6:
72                 self.total_actions = total
73
74         elif what == rpm.RPMCALLBACK_TRANS_PROGRESS:
75             pass
76
77         elif what == rpm.RPMCALLBACK_TRANS_STOP:
78             pass
79
80         elif what == rpm.RPMCALLBACK_INST_OPEN_FILE:
81             self.lastmsg = None
82             hdr = None
83             if h is not None:
84                 rpmloc = h
85                 hdr = readRpmHeader(self.ts, h)
86                 handle = self._makeHandle(hdr)
87                 fd = os.open(rpmloc, os.O_RDONLY)
88                 self.callbackfilehandles[handle]=fd
89                 self.total_installed += 1
90                 self.installed_pkg_names.append(hdr['name'])
91                 return fd
92             else:
93                 self._localprint("No header - huh?")
94
95         elif what == rpm.RPMCALLBACK_INST_CLOSE_FILE:
96             hdr = None
97             if h is not None:
98                 rpmloc = h
99                 hdr = readRpmHeader(self.ts, h)
100                 handle = self._makeHandle(hdr)
101                 os.close(self.callbackfilehandles[handle])
102                 fd = 0
103
104                 # log stuff
105                 #pkgtup = self._dopkgtup(hdr)
106                 self.logString.append(self._logPkgString(hdr))
107                 
108
109         elif what == rpm.RPMCALLBACK_INST_PROGRESS:
110             if h is not None:
111                 percent = (self.total_installed*100L)/self.total_actions
112                 if self.output and (sys.stdout.isatty() or self.total_installed == self.total_actions):
113                     fmt = self._makefmt(percent)
114                     msg = fmt % ("Installing")
115                     if msg != self.lastmsg:
116                         sys.stdout.write(msg)
117                         sys.stdout.flush()
118                         self.lastmsg = msg
119                         if self.total_installed == self.total_actions:
120                              sys.stdout.write("\n")
121                              logging.info('\n'.join(self.logString))
122
123         elif what == rpm.RPMCALLBACK_UNINST_START:
124             pass
125
126         elif what == rpm.RPMCALLBACK_UNINST_PROGRESS:
127             pass
128
129         elif what == rpm.RPMCALLBACK_UNINST_STOP:
130             self.total_removed += 1
131             
132         elif what == rpm.RPMCALLBACK_REPACKAGE_START:
133             pass
134         elif what == rpm.RPMCALLBACK_REPACKAGE_STOP:
135             pass
136         elif what == rpm.RPMCALLBACK_REPACKAGE_PROGRESS:
137             pass
138
139 def readRpmHeader(ts, filename):
140     """ Read an rpm header. """
141     fd = os.open(filename, os.O_RDONLY)
142     h = ts.hdrFromFdno(fd)
143     os.close(fd)
144     return h
145
146 def splitFilename(filename):
147     """
148     Pass in a standard style rpm fullname
149
150     Return a name, version, release, epoch, arch, e.g.::
151         foo-1.0-1.i386.rpm returns foo, 1.0, 1, i386
152         1:bar-9-123a.ia64.rpm returns bar, 9, 123a, 1, ia64
153     """
154
155     if filename[-4:] == '.rpm':
156         filename = filename[:-4]
157
158     archIndex = filename.rfind('.')
159     arch = filename[archIndex+1:]
160
161     relIndex = filename[:archIndex].rfind('-')
162     rel = filename[relIndex+1:archIndex]
163
164     verIndex = filename[:relIndex].rfind('-')
165     ver = filename[verIndex+1:relIndex]
166
167     epochIndex = filename.find(':')
168     if epochIndex == -1:
169         epoch = ''
170     else:
171         epoch = filename[:epochIndex]
172
173     name = filename[epochIndex + 1:verIndex]
174     return name, ver, rel, epoch, arch
175
176 def getCanonX86Arch(arch):
177     #
178     if arch == "i586":
179         f = open("/proc/cpuinfo", "r")
180         lines = f.readlines()
181         f.close()
182         for line in lines:
183             if line.startswith("model name") and line.find("Geode(TM)") != -1:
184                 return "geode"
185         return arch
186     # only athlon vs i686 isn't handled with uname currently
187     if arch != "i686":
188         return arch
189
190     # if we're i686 and AuthenticAMD, then we should be an athlon
191     f = open("/proc/cpuinfo", "r")
192     lines = f.readlines()
193     f.close()
194     for line in lines:
195         if line.startswith("vendor") and line.find("AuthenticAMD") != -1:
196             return "athlon"
197         # i686 doesn't guarantee cmov, but we depend on it
198         elif line.startswith("flags") and line.find("cmov") == -1:
199             return "i586"
200
201     return arch
202
203 def getCanonX86_64Arch(arch):
204     if arch != "x86_64":
205         return arch
206
207     vendor = None
208     f = open("/proc/cpuinfo", "r")
209     lines = f.readlines()
210     f.close()
211     for line in lines:
212         if line.startswith("vendor_id"):
213             vendor = line.split(':')[1]
214             break
215     if vendor is None:
216         return arch
217
218     if vendor.find("Authentic AMD") != -1 or vendor.find("AuthenticAMD") != -1:
219         return "amd64"
220     if vendor.find("GenuineIntel") != -1:
221         return "ia32e"
222     return arch
223
224 def getCanonArch():
225     arch = os.uname()[4]
226
227     if (len(arch) == 4 and arch[0] == "i" and arch[2:4] == "86"):
228         return getCanonX86Arch(arch)
229
230     if arch == "x86_64":
231         return getCanonX86_64Arch(arch)
232
233     return arch
234
235 # dict mapping arch -> ( multicompat, best personality, biarch personality )
236 multilibArches = { "x86_64":  ( "athlon", "x86_64", "athlon" ),
237                    "sparc64v": ( "sparc", "sparcv9v", "sparc64v" ),
238                    "sparc64": ( "sparc", "sparcv9", "sparc64" ),
239                    "ppc64":   ( "ppc", "ppc", "ppc64" ),
240                    "s390x":   ( "s390", "s390x", "s390" ),
241                    }
242
243 arches = {
244     # ia32
245     "athlon": "i686",
246     "i686": "i586",
247     "geode": "i586",
248     "i586": "i486",
249     "i486": "i386",
250     "i386": "noarch",
251
252     # amd64
253     "x86_64": "athlon",
254     "amd64": "x86_64",
255     "ia32e": "x86_64",
256
257     # ppc
258     "ppc64pseries": "ppc64",
259     "ppc64iseries": "ppc64",
260     "ppc64": "ppc",
261     "ppc": "noarch",
262
263     # s390{,x}
264     "s390x": "s390",
265     "s390": "noarch",
266
267     # sparc
268     "sparc64v": "sparc64",
269     "sparc64v": "sparcv9v",
270     "sparc64": "sparcv9",
271     "sparcv9v": "sparcv9",
272     "sparcv9": "sparcv8",
273     "sparcv8": "sparc",
274     "sparc": "noarch",
275
276     # alpha
277     "alphaev7":   "alphaev68",
278     "alphaev68":  "alphaev67",
279     "alphaev67":  "alphaev6",
280     "alphaev6":   "alphapca56",
281     "alphapca56": "alphaev56",
282     "alphaev56":  "alphaev5",
283     "alphaev5":   "alphaev45",
284     "alphaev45":  "alphaev4",
285     "alphaev4":   "alpha",
286     "alpha":      "noarch",
287
288     # arm
289     "armv7nhl": "armv7hl",
290     "armv7hl": "noarch",
291     "armv7l": "armv6l",
292     "armv6l": "armv5tejl",
293     "armv5tejl": "armv5tel",
294     "armv5tel": "noarch",
295
296     # super-h
297     "sh4a": "sh4",
298     "sh4": "noarch",
299     "sh3": "noarch",
300
301     #itanium
302     "ia64": "noarch",
303     }
304
305 def isMultiLibArch(arch=None):
306     """returns true if arch is a multilib arch, false if not"""
307     if arch is None:
308         arch = canonArch
309
310     if not arches.has_key(arch): # or we could check if it is noarch
311         return 0
312
313     if multilibArches.has_key(arch):
314         return 1
315
316     if multilibArches.has_key(arches[arch]):
317         return 1
318
319     return 0
320
321 def getBaseArch():
322     myarch = getCanonArch()
323     if not arches.has_key(myarch):
324         return myarch
325
326     if isMultiLibArch(arch=myarch):
327         if multilibArches.has_key(myarch):
328             return myarch
329         else:
330             return arches[myarch]
331
332     if arches.has_key(myarch):
333         basearch = myarch
334         value = arches[basearch]
335         while value != 'noarch':
336             basearch = value
337             value = arches[basearch]
338
339         return basearch
340
341 def checkRpmIntegrity(bin_rpm, package):
342     argv = [bin_rpm, "--checksig", "--nogpg", package]
343     dev_null = os.open("/dev/null", os.O_WRONLY)
344     try:
345         ret = subprocess.call(argv, stdout = dev_null, stderr = dev_null)
346     finally:
347         os.close(dev_null)
348     return ret 
349
350 def checkSig(ts, package):
351     """Takes a transaction set and a package, check it's sigs,
352     return 0 if they are all fine
353     return 1 if the gpg key can't be found
354     return 2 if the header is in someway damaged
355     return 3 if the key is not trusted
356     return 4 if the pkg is not gpg or pgp signed"""
357
358     value = 0
359     currentflags = ts.setVSFlags(0)
360     fdno = os.open(package, os.O_RDONLY)
361     try:
362         hdr = ts.hdrFromFdno(fdno)
363     except rpm.error, e:
364         if str(e) == "public key not availaiable":
365             value = 1
366         if str(e) == "public key not available":
367             value = 1
368         if str(e) == "public key not trusted":
369             value = 3
370         if str(e) == "error reading package header":
371             value = 2
372     else:
373         error, siginfo = getSigInfo(hdr)
374         if error == 101:
375             os.close(fdno)
376             del hdr
377             value = 4
378         else:
379             del hdr
380
381     try:
382         os.close(fdno)
383     except OSError, e: # if we're not opened, don't scream about it
384         pass
385
386     ts.setVSFlags(currentflags) # put things back like they were before
387     return value
388
389 def getSigInfo(hdr):
390     """checks signature from an hdr hand back signature information and/or
391        an error code"""
392
393     locale.setlocale(locale.LC_ALL, 'C')
394     string = '%|DSAHEADER?{%{DSAHEADER:pgpsig}}:{%|RSAHEADER?{%{RSAHEADER:pgpsig}}:{%|SIGGPG?{%{SIGGPG:pgpsig}}:{%|SIGPGP?{%{SIGPGP:pgpsig}}:{(none)}|}|}|}|'
395     siginfo = hdr.sprintf(string)
396     if siginfo != '(none)':
397         error = 0
398         sigtype, sigdate, sigid = siginfo.split(',')
399     else:
400         error = 101
401         sigtype = 'MD5'
402         sigdate = 'None'
403         sigid = 'None'
404
405     infotuple = (sigtype, sigdate, sigid)
406     return error, infotuple