| [2] | 1 | #!/usr/bin/python -t
|
|---|
| 2 | # This program is free software; you can redistribute it and/or modify
|
|---|
| 3 | # it under the terms of the GNU General Public License as published by
|
|---|
| 4 | # the Free Software Foundation; either version 2 of the License, or
|
|---|
| 5 | # (at your option) any later version.
|
|---|
| 6 | #
|
|---|
| 7 | # This program is distributed in the hope that it will be useful,
|
|---|
| 8 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|---|
| 9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|---|
| 10 | # GNU Library General Public License for more details.
|
|---|
| 11 | #
|
|---|
| 12 | # You should have received a copy of the GNU General Public License
|
|---|
| 13 | # along with this program; if not, write to the Free Software
|
|---|
| 14 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|---|
| 15 |
|
|---|
| 16 | import sys
|
|---|
| 17 | import time
|
|---|
| 18 | import exceptions
|
|---|
| 19 |
|
|---|
| 20 | import yum
|
|---|
| 21 | from cli import *
|
|---|
| 22 | from yum import Errors
|
|---|
| 23 | from yum import _
|
|---|
| [516] | 24 | from yum.i18n import utf8_width
|
|---|
| [2] | 25 | from yum import logginglevels
|
|---|
| 26 | from optparse import OptionGroup
|
|---|
| 27 |
|
|---|
| 28 | import yum.plugins as plugins
|
|---|
| 29 | from urlgrabber.progress import format_number
|
|---|
| 30 |
|
|---|
| 31 | def suppress_keyboard_interrupt_message():
|
|---|
| 32 | old_excepthook = sys.excepthook
|
|---|
| 33 |
|
|---|
| 34 | def new_hook(type, value, traceback):
|
|---|
| 35 | if type != exceptions.KeyboardInterrupt:
|
|---|
| 36 | old_excepthook(type, value, traceback)
|
|---|
| 37 | else:
|
|---|
| 38 | pass
|
|---|
| 39 |
|
|---|
| 40 | sys.excepthook = new_hook
|
|---|
| 41 |
|
|---|
| 42 | def jiffies_to_seconds(jiffies):
|
|---|
| 43 | Hertz = 100 # FIXME: Hack, need to get this, AT_CLKTCK elf note *sigh*
|
|---|
| 44 | return int(jiffies) / Hertz
|
|---|
| 45 |
|
|---|
| 46 | def seconds_to_ui_time(seconds):
|
|---|
| 47 | if seconds >= 60 * 60 * 24:
|
|---|
| 48 | return "%d day(s) %d:%02d:%02d" % (seconds / (60 * 60 * 24),
|
|---|
| 49 | (seconds / (60 * 60)) % 24,
|
|---|
| 50 | (seconds / 60) % 60,
|
|---|
| 51 | seconds % 60)
|
|---|
| 52 | if seconds >= 60 * 60:
|
|---|
| 53 | return "%d:%02d:%02d" % (seconds / (60 * 60), (seconds / 60) % 60,
|
|---|
| 54 | (seconds % 60))
|
|---|
| 55 | return "%02d:%02d" % ((seconds / 60), seconds % 60)
|
|---|
| 56 |
|
|---|
| 57 | def get_process_info(pid):
|
|---|
| 58 | if not pid:
|
|---|
| 59 | return
|
|---|
| 60 |
|
|---|
| [516] | 61 | try:
|
|---|
| 62 | pid = int(pid)
|
|---|
| 63 | except ValueError, e:
|
|---|
| 64 | return
|
|---|
| 65 |
|
|---|
| [2] | 66 | # Maybe true if /proc isn't mounted, or not Linux ... or something.
|
|---|
| 67 | if (not os.path.exists("/proc/%d/status" % pid) or
|
|---|
| 68 | not os.path.exists("/proc/stat") or
|
|---|
| 69 | not os.path.exists("/proc/%d/stat" % pid)):
|
|---|
| 70 | return
|
|---|
| 71 |
|
|---|
| 72 | ps = {}
|
|---|
| 73 | for line in open("/proc/%d/status" % pid):
|
|---|
| 74 | if line[-1] != '\n':
|
|---|
| 75 | continue
|
|---|
| 76 | data = line[:-1].split(':\t', 1)
|
|---|
| 77 | if len(data) < 2:
|
|---|
| 78 | continue
|
|---|
| 79 | if data[1].endswith(' kB'):
|
|---|
| 80 | data[1] = data[1][:-3]
|
|---|
| 81 | ps[data[0].strip().lower()] = data[1].strip()
|
|---|
| 82 | if 'vmrss' not in ps:
|
|---|
| 83 | return
|
|---|
| 84 | if 'vmsize' not in ps:
|
|---|
| 85 | return
|
|---|
| 86 | boot_time = None
|
|---|
| 87 | for line in open("/proc/stat"):
|
|---|
| 88 | if line.startswith("btime "):
|
|---|
| 89 | boot_time = int(line[len("btime "):-1])
|
|---|
| 90 | break
|
|---|
| 91 | if boot_time is None:
|
|---|
| 92 | return
|
|---|
| 93 | ps_stat = open("/proc/%d/stat" % pid).read().split()
|
|---|
| 94 | ps['utime'] = jiffies_to_seconds(ps_stat[13])
|
|---|
| 95 | ps['stime'] = jiffies_to_seconds(ps_stat[14])
|
|---|
| 96 | ps['cutime'] = jiffies_to_seconds(ps_stat[15])
|
|---|
| 97 | ps['cstime'] = jiffies_to_seconds(ps_stat[16])
|
|---|
| 98 | ps['start_time'] = boot_time + jiffies_to_seconds(ps_stat[21])
|
|---|
| 99 | ps['state'] = {'R' : _('Running'),
|
|---|
| 100 | 'S' : _('Sleeping'),
|
|---|
| 101 | 'D' : _('Uninterruptible'),
|
|---|
| 102 | 'Z' : _('Zombie'),
|
|---|
| 103 | 'T' : _('Traced/Stopped')
|
|---|
| 104 | }.get(ps_stat[2], _('Unknown'))
|
|---|
| 105 |
|
|---|
| 106 | return ps
|
|---|
| 107 |
|
|---|
| 108 | def show_lock_owner(pid, logger):
|
|---|
| [516] | 109 | ps = get_process_info(pid)
|
|---|
| 110 | if not ps:
|
|---|
| 111 | return None
|
|---|
| [2] | 112 |
|
|---|
| 113 | # This yumBackend isn't very friendly, so...
|
|---|
| 114 | if ps['name'] == 'yumBackend.py':
|
|---|
| 115 | nmsg = _(" The other application is: PackageKit")
|
|---|
| 116 | else:
|
|---|
| 117 | nmsg = _(" The other application is: %s") % ps['name']
|
|---|
| 118 |
|
|---|
| 119 | logger.critical("%s", nmsg)
|
|---|
| 120 | logger.critical(_(" Memory : %5s RSS (%5sB VSZ)") %
|
|---|
| 121 | (format_number(int(ps['vmrss']) * 1024),
|
|---|
| 122 | format_number(int(ps['vmsize']) * 1024)))
|
|---|
| 123 |
|
|---|
| 124 | ago = seconds_to_ui_time(int(time.time()) - ps['start_time'])
|
|---|
| 125 | logger.critical(_(" Started: %s - %s ago") %
|
|---|
| 126 | (time.ctime(ps['start_time']), ago))
|
|---|
| 127 | logger.critical(_(" State : %s, pid: %d") % (ps['state'], pid))
|
|---|
| 128 |
|
|---|
| [516] | 129 | return ps
|
|---|
| 130 |
|
|---|
| 131 |
|
|---|
| 132 | def exception2msg(e):
|
|---|
| 133 | """ DIE python DIE! Which one works:
|
|---|
| 134 | to_unicode(e.value); unicode(e); str(e);
|
|---|
| 135 | Call this so you don't have to care. """
|
|---|
| 136 | try:
|
|---|
| 137 | return to_unicode(e.value)
|
|---|
| 138 | except:
|
|---|
| 139 | pass
|
|---|
| 140 |
|
|---|
| 141 | try:
|
|---|
| 142 | return unicode(e)
|
|---|
| 143 | except:
|
|---|
| 144 | pass
|
|---|
| 145 |
|
|---|
| 146 | try:
|
|---|
| 147 | return str(e)
|
|---|
| 148 | except:
|
|---|
| 149 | pass
|
|---|
| 150 | return "<exception failed to convert to text>"
|
|---|
| 151 |
|
|---|
| 152 |
|
|---|
| [2] | 153 | class YumUtilBase(YumBaseCli):
|
|---|
| 154 | def __init__(self,name,ver,usage):
|
|---|
| 155 | YumBaseCli.__init__(self)
|
|---|
| 156 | self._parser = YumOptionParser(base=self,utils=True,usage=usage)
|
|---|
| 157 | self._usage = usage
|
|---|
| 158 | self._utilName = name
|
|---|
| 159 | self._utilVer = ver
|
|---|
| 160 | self._option_group = OptionGroup(self._parser, "%s options" % self._utilName,"")
|
|---|
| 161 | self._parser.add_option_group(self._option_group)
|
|---|
| 162 | suppress_keyboard_interrupt_message()
|
|---|
| 163 | logger = logging.getLogger("yum.util")
|
|---|
| 164 | verbose_logger = logging.getLogger("yum.verbose.util")
|
|---|
| [516] | 165 | # Add yum-utils version to history records.
|
|---|
| 166 | if hasattr(self, 'run_with_package_names'):
|
|---|
| 167 | self.run_with_package_names.add("yum-utils")
|
|---|
| 168 |
|
|---|
| 169 | def exUserCancel(self):
|
|---|
| 170 | self.logger.critical(_('\n\nExiting on user cancel'))
|
|---|
| 171 | if self.unlock(): return 200
|
|---|
| 172 | return 1
|
|---|
| 173 |
|
|---|
| 174 | def exIOError(self, e):
|
|---|
| 175 | if e.errno == 32:
|
|---|
| 176 | self.logger.critical(_('\n\nExiting on Broken Pipe'))
|
|---|
| 177 | else:
|
|---|
| 178 | self.logger.critical(_('\n\n%s') % exception2msg(e))
|
|---|
| 179 | if self.unlock(): return 200
|
|---|
| 180 | return 1
|
|---|
| 181 |
|
|---|
| 182 | def exPluginExit(self, e):
|
|---|
| 183 | '''Called when a plugin raises PluginYumExit.
|
|---|
| 184 |
|
|---|
| 185 | Log the plugin's exit message if one was supplied.
|
|---|
| 186 | ''' # ' xemacs hack
|
|---|
| 187 | exitmsg = exception2msg(e)
|
|---|
| 188 | if exitmsg:
|
|---|
| 189 | self.logger.warn('\n\n%s', exitmsg)
|
|---|
| 190 | if self.unlock(): return 200
|
|---|
| 191 | return 1
|
|---|
| 192 |
|
|---|
| 193 | def exFatal(self, e):
|
|---|
| 194 | self.logger.critical('\n\n%s', exception2msg(e))
|
|---|
| 195 | if self.unlock(): return 200
|
|---|
| 196 | return 1
|
|---|
| [2] | 197 |
|
|---|
| [516] | 198 | def unlock(self):
|
|---|
| 199 | try:
|
|---|
| 200 | self.closeRpmDB()
|
|---|
| 201 | self.doUnlock()
|
|---|
| 202 | except Errors.LockError, e:
|
|---|
| 203 | return 200
|
|---|
| 204 | return 0
|
|---|
| [2] | 205 |
|
|---|
| [516] | 206 |
|
|---|
| [2] | 207 | def getOptionParser(self):
|
|---|
| 208 | return self._parser
|
|---|
| 209 |
|
|---|
| 210 | def getOptionGroup(self):
|
|---|
| 211 | """ Get an option group to add non inherited options"""
|
|---|
| 212 | return self._option_group
|
|---|
| 213 |
|
|---|
| 214 | def waitForLock(self):
|
|---|
| 215 | lockerr = ""
|
|---|
| 216 | while True:
|
|---|
| 217 | try:
|
|---|
| 218 | self.doLock()
|
|---|
| 219 | except Errors.LockError, e:
|
|---|
| [516] | 220 | if exception2msg(e) != lockerr:
|
|---|
| 221 | lockerr = exception2msg(e)
|
|---|
| [2] | 222 | self.logger.critical(lockerr)
|
|---|
| [516] | 223 | if not self.conf.exit_on_lock:
|
|---|
| 224 | self.logger.critical("Another app is currently holding the yum lock; waiting for it to exit...")
|
|---|
| 225 | show_lock_owner(e.pid, self.logger)
|
|---|
| 226 | time.sleep(2)
|
|---|
| 227 | else:
|
|---|
| 228 | raise Errors.YumBaseError, _("Another app is currently holding the yum lock; exiting as configured by exit_on_lock")
|
|---|
| [2] | 229 | else:
|
|---|
| 230 | break
|
|---|
| 231 |
|
|---|
| 232 | def _printUtilVersion(self):
|
|---|
| 233 | print "%s - %s (yum - %s)" % (self._utilName,self._utilVer,yum.__version__)
|
|---|
| 234 |
|
|---|
| 235 | def doUtilConfigSetup(self,args = sys.argv[1:],pluginsTypes=(plugins.TYPE_CORE,)):
|
|---|
| 236 | # Parse only command line options that affect basic yum setup
|
|---|
| 237 | opts = self._parser.firstParse(args)
|
|---|
| [516] | 238 |
|
|---|
| 239 | # go through all the setopts and set the global ones
|
|---|
| 240 | self._parseSetOpts(opts.setopts)
|
|---|
| 241 |
|
|---|
| 242 | if self.main_setopts:
|
|---|
| 243 | for opt in self.main_setopts.items:
|
|---|
| 244 | setattr(opts, opt, getattr(self.main_setopts, opt))
|
|---|
| 245 |
|
|---|
| [2] | 246 | # Just print out the version if that's what the user wanted
|
|---|
| 247 | if opts.version:
|
|---|
| 248 | self._printUtilVersion()
|
|---|
| 249 | sys.exit(0)
|
|---|
| 250 | # get the install root to use
|
|---|
| 251 | root = self._parser.getRoot(opts)
|
|---|
| 252 | if opts.quiet:
|
|---|
| 253 | opts.debuglevel = 0
|
|---|
| 254 | if opts.verbose:
|
|---|
| 255 | opts.debuglevel = opts.errorlevel = 6
|
|---|
| 256 |
|
|---|
| 257 | # Read up configuration options and initialise plugins
|
|---|
| 258 | try:
|
|---|
| 259 | pc = self.preconf
|
|---|
| 260 | pc.fn = opts.conffile
|
|---|
| 261 | pc.root = root
|
|---|
| 262 | pc.init_plugins = not opts.noplugins
|
|---|
| 263 | pc.plugin_types = pluginsTypes
|
|---|
| 264 | pc.optparser = self._parser
|
|---|
| 265 | pc.debuglevel = opts.debuglevel
|
|---|
| 266 | pc.errorlevel = opts.errorlevel
|
|---|
| 267 | if hasattr(opts, "disableplugins"):
|
|---|
| 268 | pc.disabled_plugins =self._parser._splitArg(opts.disableplugins)
|
|---|
| 269 | if hasattr(opts, "enableplugins"):
|
|---|
| 270 | pc.enabled_plugins = self._parser._splitArg(opts.enableplugins)
|
|---|
| [516] | 271 | if hasattr(opts, "releasever"):
|
|---|
| 272 | pc.releasever = opts.releasever
|
|---|
| [2] | 273 | self.conf
|
|---|
| 274 |
|
|---|
| [516] | 275 | # now set all the non-first-start opts from main from our setopts
|
|---|
| 276 | if self.main_setopts:
|
|---|
| 277 | for opt in self.main_setopts.items:
|
|---|
| 278 | setattr(self.conf, opt, getattr(self.main_setopts, opt))
|
|---|
| 279 |
|
|---|
| [2] | 280 | except Errors.ConfigError, e:
|
|---|
| [516] | 281 | self.logger.critical(_('Config Error: %s'), exception2msg(e))
|
|---|
| [2] | 282 | sys.exit(1)
|
|---|
| 283 | except ValueError, e:
|
|---|
| [516] | 284 | self.logger.critical(_('Options Error: %s'), exception2msg(e))
|
|---|
| [2] | 285 | sys.exit(1)
|
|---|
| 286 | except plugins.PluginYumExit, e:
|
|---|
| [516] | 287 | self.logger.critical(_('PluginExit Error: %s'), exception2msg(e))
|
|---|
| [2] | 288 | sys.exit(1)
|
|---|
| 289 | except Errors.YumBaseError, e:
|
|---|
| [516] | 290 | self.logger.critical(_('Yum Error: %s'), exception2msg(e))
|
|---|
| [2] | 291 | sys.exit(1)
|
|---|
| 292 |
|
|---|
| 293 | # update usage in case plugins have added commands
|
|---|
| 294 | self._parser.set_usage(self._usage)
|
|---|
| 295 |
|
|---|
| 296 | # Now parse the command line for real and
|
|---|
| 297 | # apply some of the options to self.conf
|
|---|
| 298 | (opts, self.cmds) = self._parser.setupYumConfig()
|
|---|
| 299 | if self.cmds:
|
|---|
| 300 | self.basecmd = self.cmds[0] # our base command
|
|---|
| 301 | else:
|
|---|
| 302 | self.basecmd = None
|
|---|
| 303 | self.extcmds = self.cmds[1:] # out extended arguments/commands
|
|---|
| 304 |
|
|---|
| 305 | return opts
|
|---|
| 306 |
|
|---|
| 307 | def doUtilYumSetup(self):
|
|---|
| 308 | """do a default setup for all the normal/necessary yum components,
|
|---|
| 309 | really just a shorthand for testing"""
|
|---|
| 310 | # FIXME - we need another way to do this, I think.
|
|---|
| 311 | try:
|
|---|
| 312 | self.waitForLock()
|
|---|
| 313 | self._getTs()
|
|---|
| 314 | self._getRpmDB()
|
|---|
| 315 | self._getRepos(doSetup = True)
|
|---|
| 316 | self._getSacks()
|
|---|
| 317 | except Errors.YumBaseError, msg:
|
|---|
| [516] | 318 | self.logger.critical(exception2msg(msg))
|
|---|
| [2] | 319 | sys.exit(1)
|
|---|
| 320 |
|
|---|
| [516] | 321 | def doUtilBuildTransaction(self, unfinished_transactions_check=True):
|
|---|
| 322 | try:
|
|---|
| 323 | (result, resultmsgs) = self.buildTransaction(unfinished_transactions_check = unfinished_transactions_check)
|
|---|
| 324 | except plugins.PluginYumExit, e:
|
|---|
| 325 | return self.exPluginExit(e)
|
|---|
| 326 | except Errors.YumBaseError, e:
|
|---|
| 327 | result = 1
|
|---|
| 328 | resultmsgs = [exception2msg(e)]
|
|---|
| 329 | except KeyboardInterrupt:
|
|---|
| 330 | return self.exUserCancel()
|
|---|
| 331 | except IOError, e:
|
|---|
| 332 | return self.exIOError(e)
|
|---|
| 333 |
|
|---|
| 334 | # Act on the depsolve result
|
|---|
| 335 | if result == 0:
|
|---|
| 336 | # Normal exit
|
|---|
| 337 | if self.unlock(): return 200
|
|---|
| 338 | return 0
|
|---|
| 339 | elif result == 1:
|
|---|
| 340 | # Fatal error
|
|---|
| 341 | for msg in resultmsgs:
|
|---|
| 342 | prefix = _('Error: %s')
|
|---|
| 343 | prefix2nd = (' ' * (utf8_width(prefix) - 2))
|
|---|
| 344 | self.logger.critical(prefix, msg.replace('\n', '\n' + prefix2nd))
|
|---|
| 345 | if not self.conf.skip_broken:
|
|---|
| 346 | self.verbose_logger.info(_(" You could try using --skip-broken to work around the problem"))
|
|---|
| 347 | if not self._rpmdb_warn_checks(out=self.verbose_logger.info, warn=False):
|
|---|
| 348 | self.verbose_logger.info(_(" You could try running: rpm -Va --nofiles --nodigest"))
|
|---|
| 349 | if self.unlock(): return 200
|
|---|
| [2] | 350 | return 1
|
|---|
| [516] | 351 | elif result == 2:
|
|---|
| 352 | # Continue on
|
|---|
| 353 | pass
|
|---|
| 354 | else:
|
|---|
| 355 | self.logger.critical(_('Unknown Error(s): Exit Code: %d:'), result)
|
|---|
| 356 | for msg in resultmsgs:
|
|---|
| 357 | self.logger.critical(msg)
|
|---|
| 358 | if self.unlock(): return 200
|
|---|
| 359 | return 3
|
|---|
| [2] | 360 |
|
|---|
| [516] | 361 | self.verbose_logger.log(logginglevels.INFO_2, _('\nDependencies Resolved'))
|
|---|
| 362 |
|
|---|
| 363 | def doUtilTransaction(self):
|
|---|
| [2] | 364 |
|
|---|
| 365 | try:
|
|---|
| 366 | return_code = self.doTransaction()
|
|---|
| 367 | except plugins.PluginYumExit, e:
|
|---|
| [516] | 368 | return self.exPluginExit(e)
|
|---|
| [2] | 369 | except Errors.YumBaseError, e:
|
|---|
| [516] | 370 | return self.exFatal(e)
|
|---|
| [2] | 371 | except KeyboardInterrupt:
|
|---|
| [516] | 372 | return self.exUserCancel()
|
|---|
| [2] | 373 | except IOError, e:
|
|---|
| [516] | 374 | return self.exIOError(e,)
|
|---|
| [2] | 375 |
|
|---|
| 376 | self.verbose_logger.log(logginglevels.INFO_2, _('Complete!'))
|
|---|
| [516] | 377 | if self.unlock(): return 200
|
|---|
| [2] | 378 | return return_code
|
|---|
| 379 |
|
|---|
| 380 | def main():
|
|---|
| 381 | name = 'testutil'
|
|---|
| 382 | ver = '0.1'
|
|---|
| 383 | usage = 'testutil [options] [args]'
|
|---|
| 384 | util = YumUtilBase(name,ver,usage)
|
|---|
| 385 | parser = util.getOptionParser()
|
|---|
| 386 | parser.add_option("", "--myoption", dest="myoption",
|
|---|
| 387 | action="store_true", default=False,
|
|---|
| 388 | help="This is an util option")
|
|---|
| 389 | util.logger.info("Setup Yum Config")
|
|---|
| 390 | opts = util.doUtilConfigSetup()
|
|---|
| 391 | util.logger.info("Setup Yum")
|
|---|
| 392 | util.doUtilYumSetup()
|
|---|
| 393 | print "Command line args: %s" % " ".join(util.cmds)
|
|---|
| 394 | print "Command line options :"
|
|---|
| 395 | print opts
|
|---|
| 396 |
|
|---|
| 397 | util.logger.info("%s Completed" % name)
|
|---|
| 398 | if __name__ == '__main__':
|
|---|
| 399 | main()
|
|---|
| 400 |
|
|---|
| 401 |
|
|---|