1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 import rpm
20 import os
21 import fcntl
22 import time
23 import logging
24 import types
25 import sys
26 from yum.constants import *
27 from yum import _
28 import misc
29 import tempfile
30
34
35 - def event(self, package, action, te_current, te_total, ts_current, ts_total):
36 """
37 @param package: A yum package object or simple string of a package name
38 @param action: A yum.constant transaction set state or in the obscure
39 rpm repackage case it could be the string 'repackaging'
40 @param te_current: current number of bytes processed in the transaction
41 element being processed
42 @param te_total: total number of bytes in the transaction element being
43 processed
44 @param ts_current: number of processes completed in whole transaction
45 @param ts_total: total number of processes in the transaction.
46 """
47
48
49 pass
50
52 """package is the package. msgs is the messages that were
53 output (if any)."""
54 pass
55
57 """takes a simple error msg string"""
58
59 pass
60
61 - def filelog(self, package, action):
62
63 """package is the same as in event() - a package object or simple string
64 action is also the same as in event()"""
65 pass
66
68 '''
69 Base class for a RPMTransaction display callback class
70 '''
72 self.action = { TS_UPDATE : _('Updating'),
73 TS_ERASE: _('Erasing'),
74 TS_INSTALL: _('Installing'),
75 TS_TRUEINSTALL : _('Installing'),
76 TS_OBSOLETED: _('Obsoleted'),
77 TS_OBSOLETING: _('Installing'),
78 TS_UPDATED: _('Cleanup'),
79 'repackaging': _('Repackaging')}
80
81 self.fileaction = { TS_UPDATE: 'Updated',
82 TS_ERASE: 'Erased',
83 TS_INSTALL: 'Installed',
84 TS_TRUEINSTALL: 'Installed',
85 TS_OBSOLETED: 'Obsoleted',
86 TS_OBSOLETING: 'Installed',
87 TS_UPDATED: 'Cleanup'}
88 self.logger = logging.getLogger('yum.filelogging.RPMInstallCallback')
89
90 - def event(self, package, action, te_current, te_total, ts_current, ts_total):
91 """
92 @param package: A yum package object or simple string of a package name
93 @param action: A yum.constant transaction set state or in the obscure
94 rpm repackage case it could be the string 'repackaging'
95 @param te_current: Current number of bytes processed in the transaction
96 element being processed
97 @param te_total: Total number of bytes in the transaction element being
98 processed
99 @param ts_current: number of processes completed in whole transaction
100 @param ts_total: total number of processes in the transaction.
101 """
102 raise NotImplementedError()
103
105 """package is the package. msgs is the messages that were
106 output (if any)."""
107 pass
108
110
111 print >> sys.stderr, msg
112
113 - def filelog(self, package, action):
114
115
116 if action in self.fileaction:
117 msg = '%s: %s' % (self.fileaction[action], package)
118 else:
119 msg = '%s: %s' % (package, action)
120 self.logger.info(msg)
121
122
128
129 - def event(self, package, action, te_current, te_total, ts_current, ts_total):
130
131 msg = '%s: %s %s/%s [%s/%s]' % (self.action[action], package,
132 te_current, te_total, ts_current, ts_total)
133 if msg != self.lastmsg:
134 print msg
135 self.lastmsg = msg
136 self.lastpackage = package
137
139 if msgs:
140 print msgs,
141
142
143
144
145
146
149 self.__parent = parent
150
152 """ Wraps all access to the parent functions. This is so it'll eat all
153 exceptions because rpm doesn't like exceptions in the callback. """
154 func = getattr(self.__parent, name)
155
156 def newFunc(*args, **kwargs):
157 try:
158 func(*args, **kwargs)
159 except:
160 pass
161
162 newFunc.__name__ = func.__name__
163 newFunc.__doc__ = func.__doc__
164 newFunc.__dict__.update(func.__dict__)
165 return newFunc
166
169 if not callable(display):
170 self.display = display
171 else:
172 self.display = display()
173 self.display = _WrapNoExceptions(self.display)
174 self.base = base
175 self.test = test
176 self.trans_running = False
177 self.filehandles = {}
178 self.total_actions = 0
179 self.total_installed = 0
180 self.complete_actions = 0
181 self.installed_pkg_names = []
182 self.total_removed = 0
183 self.logger = logging.getLogger('yum.filelogging.RPMInstallCallback')
184 self.filelog = False
185
186 self._setupOutputLogging(base.conf.rpmverbosity)
187 if not os.path.exists(self.base.conf.persistdir):
188 os.makedirs(self.base.conf.persistdir)
189
190
192 """ Set the Non-blocking flag for a filedescriptor. """
193 flag = os.O_NONBLOCK
194 current_flags = fcntl.fcntl(fd, fcntl.F_GETFL)
195 if current_flags & flag:
196 return
197 fcntl.fcntl(fd, fcntl.F_SETFL, current_flags | flag)
198
200 """ Set the close on exec. flag for a filedescriptor. """
201 flag = fcntl.FD_CLOEXEC
202 current_flags = fcntl.fcntl(fd, fcntl.F_GETFD)
203 if current_flags & flag:
204 return
205 fcntl.fcntl(fd, fcntl.F_SETFD, current_flags | flag)
206
208
209 io_r = tempfile.NamedTemporaryFile()
210 self._readpipe = io_r
211 self._writepipe = open(io_r.name, 'w+b')
212
213 self.base.ts.ts.scriptFd = self._writepipe.fileno()
214 rpmverbosity = {'critical' : 'crit',
215 'emergency' : 'emerg',
216 'error' : 'err',
217 'information' : 'info',
218 'warn' : 'warning'}.get(rpmverbosity, rpmverbosity)
219 rpmverbosity = 'RPMLOG_' + rpmverbosity.upper()
220 if not hasattr(rpm, rpmverbosity):
221 rpmverbosity = 'RPMLOG_INFO'
222 rpm.setVerbosity(getattr(rpm, rpmverbosity))
223 rpm.setLogFile(self._writepipe)
224
226
227 rpm.setVerbosity(rpm.RPMLOG_NOTICE)
228 rpm.setLogFile(sys.stderr)
229 try:
230 self._writepipe.close()
231 except:
232 pass
233
235 try:
236 out = self._readpipe.read()
237 if not out:
238 return None
239 return out
240 except IOError:
241 pass
242
247
250
252 tmpepoch = hdr['epoch']
253 if tmpepoch is None: epoch = '0'
254 else: epoch = str(tmpepoch)
255
256 return (hdr['name'], hdr['arch'], epoch, hdr['version'], hdr['release'])
257
259 handle = '%s:%s.%s-%s-%s' % (hdr['epoch'], hdr['name'], hdr['version'],
260 hdr['release'], hdr['arch'])
261
262 return handle
263
264 - def ts_done(self, package, action):
265 """writes out the portions of the transaction which have completed"""
266
267 if self.test: return
268
269 if not hasattr(self, '_ts_done'):
270 self.ts_done_fn = '%s/transaction-done.%s' % (self.base.conf.persistdir, self._ts_time)
271
272 try:
273 self._ts_done = open(self.ts_done_fn, 'w')
274 except (IOError, OSError), e:
275 self.display.errorlog('could not open ts_done file: %s' % e)
276 return
277 self._fdSetCloseOnExec(self._ts_done.fileno())
278
279
280
281
282
283
284 if len(self._te_tuples) == 0:
285
286
287
288 return
289
290 (t,e,n,v,r,a) = self._te_tuples[0]
291
292
293 msg = 'ts_done state is %s %s should be %s %s' % (package, action, t, n)
294 if action in TS_REMOVE_STATES:
295 if t != 'erase':
296 self.display.filelog(package, msg)
297 if action in TS_INSTALL_STATES:
298 if t != 'install':
299 self.display.filelog(package, msg)
300
301
302 if type(package) in types.StringTypes:
303 name = package
304 else:
305 name = package.name
306
307 if n != name:
308 msg = 'ts_done name in te is %s should be %s' % (n, package)
309 self.display.filelog(package, msg)
310
311
312 msg = '%s %s:%s-%s-%s.%s\n' % (t,e,n,v,r,a)
313
314 try:
315 self._ts_done.write(msg)
316 self._ts_done.flush()
317 except (IOError, OSError), e:
318
319
320 del self._ts_done
321 misc.unlink_f(self.ts_done_fn)
322 self._te_tuples.pop(0)
323
325 """write out what our transaction will do"""
326
327
328 if not hasattr(self, '_te_tuples'):
329 self._te_tuples = []
330
331 for te in self.base.ts:
332 n = te.N()
333 a = te.A()
334 v = te.V()
335 r = te.R()
336 e = te.E()
337 if e is None:
338 e = '0'
339 if te.Type() == 1:
340 t = 'install'
341 elif te.Type() == 2:
342 t = 'erase'
343 else:
344 t = te.Type()
345
346
347 self._te_tuples.append((t,e,n,v,r,a))
348
349
350 self._ts_time = time.strftime('%Y-%m-%d.%H:%M.%S')
351 tsfn = '%s/transaction-all.%s' % (self.base.conf.persistdir, self._ts_time)
352 self.ts_all_fn = tsfn
353
354
355
356 if self.base.conf.installroot != '/':
357 tsfn = tsfn.replace(os.path.normpath(self.base.conf.installroot),'')
358 try:
359 if not os.path.exists(os.path.dirname(tsfn)):
360 os.makedirs(os.path.dirname(tsfn))
361 fo = open(tsfn, 'w')
362 except (IOError, OSError), e:
363 self.display.errorlog('could not open ts_all file: %s' % e)
364 return
365
366 try:
367 for (t,e,n,v,r,a) in self._te_tuples:
368 msg = "%s %s:%s-%s-%s.%s\n" % (t,e,n,v,r,a)
369 fo.write(msg)
370 fo.flush()
371 fo.close()
372 except (IOError, OSError), e:
373
374
375 misc.unlink_f(tsfn)
376
377 - def callback( self, what, bytes, total, h, user ):
378 if what == rpm.RPMCALLBACK_TRANS_START:
379 self._transStart( bytes, total, h )
380 elif what == rpm.RPMCALLBACK_TRANS_PROGRESS:
381 self._transProgress( bytes, total, h )
382 elif what == rpm.RPMCALLBACK_TRANS_STOP:
383 self._transStop( bytes, total, h )
384 elif what == rpm.RPMCALLBACK_INST_OPEN_FILE:
385 return self._instOpenFile( bytes, total, h )
386 elif what == rpm.RPMCALLBACK_INST_CLOSE_FILE:
387 self._instCloseFile( bytes, total, h )
388 elif what == rpm.RPMCALLBACK_INST_PROGRESS:
389 self._instProgress( bytes, total, h )
390 elif what == rpm.RPMCALLBACK_UNINST_START:
391 self._unInstStart( bytes, total, h )
392 elif what == rpm.RPMCALLBACK_UNINST_PROGRESS:
393 self._unInstProgress( bytes, total, h )
394 elif what == rpm.RPMCALLBACK_UNINST_STOP:
395 self._unInstStop( bytes, total, h )
396 elif what == rpm.RPMCALLBACK_REPACKAGE_START:
397 self._rePackageStart( bytes, total, h )
398 elif what == rpm.RPMCALLBACK_REPACKAGE_STOP:
399 self._rePackageStop( bytes, total, h )
400 elif what == rpm.RPMCALLBACK_REPACKAGE_PROGRESS:
401 self._rePackageProgress( bytes, total, h )
402 elif what == rpm.RPMCALLBACK_CPIO_ERROR:
403 self._cpioError(bytes, total, h)
404 elif what == rpm.RPMCALLBACK_UNPACK_ERROR:
405 self._unpackError(bytes, total, h)
406
407 elif hasattr(rpm, "RPMCALLBACK_SCRIPT_ERROR") and what == rpm.RPMCALLBACK_SCRIPT_ERROR:
408 self._scriptError(bytes, total, h)
409
410
412 if bytes == 6:
413 self.total_actions = total
414 if self.test: return
415 self.trans_running = True
416 self.ts_all()
417
420
423
425 self.lastmsg = None
426 hdr = None
427 if h is not None:
428 hdr, rpmloc = h[0], h[1]
429 handle = self._makeHandle(hdr)
430 try:
431 fd = os.open(rpmloc, os.O_RDONLY)
432 except OSError, e:
433 self.display.errorlog("Error: Cannot open file %s: %s" % (rpmloc, e))
434 else:
435 self.filehandles[handle]=fd
436 if self.trans_running:
437 self.total_installed += 1
438 self.complete_actions += 1
439 self.installed_pkg_names.append(hdr['name'])
440 return fd
441 else:
442 self.display.errorlog("Error: No Header to INST_OPEN_FILE")
443
465
467 if h is not None:
468
469
470 if type(h) == type(""):
471 self.display.event(h, 'repackaging', bytes, total,
472 self.complete_actions, self.total_actions)
473
474 else:
475 hdr, rpmloc = h[0], h[1]
476 pkgtup = self._dopkgtup(hdr)
477 txmbrs = self.base.tsInfo.getMembers(pkgtup=pkgtup)
478 for txmbr in txmbrs:
479 action = txmbr.output_state
480 self.display.event(txmbr.po, action, bytes, total,
481 self.complete_actions, self.total_actions)
484
487
489 self.total_removed += 1
490 self.complete_actions += 1
491 if h not in self.installed_pkg_names:
492 self.display.filelog(h, TS_ERASE)
493 action = TS_ERASE
494 else:
495 action = TS_UPDATED
496
497 self.display.event(h, action, 100, 100, self.complete_actions,
498 self.total_actions)
499 self._scriptout(h)
500
501 if self.test: return
502 self.ts_done(h, action)
503
504
507
510
513
515 hdr, rpmloc = h[0], h[1]
516 pkgtup = self._dopkgtup(hdr)
517 txmbrs = self.base.tsInfo.getMembers(pkgtup=pkgtup)
518 for txmbr in txmbrs:
519 msg = "Error in cpio payload of rpm package %s" % txmbr.po
520 txmbr.output_state = TS_FAILED
521 self.display.errorlog(msg)
522
523
525 hdr, rpmloc = h[0], h[1]
526 pkgtup = self._dopkgtup(hdr)
527 txmbrs = self.base.tsInfo.getMembers(pkgtup=pkgtup)
528 for txmbr in txmbrs:
529 txmbr.output_state = TS_FAILED
530 msg = "Error unpacking rpm package %s" % txmbr.po
531 self.display.errorlog(msg)
532
533
534
536 if not isinstance(h, types.TupleType):
537
538 h = (h, None)
539 hdr, rpmloc = h[0], h[1]
540 remove_hdr = False
541 if not isinstance(hdr, rpm.hdr):
542 txmbrs = [hdr]
543 remove_hdr = True
544 else:
545 pkgtup = self._dopkgtup(hdr)
546 txmbrs = self.base.tsInfo.getMembers(pkgtup=pkgtup)
547
548 for pkg in txmbrs:
549
550
551 scriptlet_name = rpm.tagnames.get(bytes, "<unknown>")
552 if remove_hdr:
553 package_name = pkg
554 else:
555 package_name = pkg.po
556
557 if total:
558 msg = ("Error in %s scriptlet in rpm package %s" %
559 (scriptlet_name, package_name))
560 if not remove_hdr:
561 pkg.output_state = TS_FAILED
562 else:
563 msg = ("Non-fatal %s scriptlet failure in rpm package %s" %
564 (scriptlet_name, package_name))
565 self.display.errorlog(msg)
566
567