summaryrefslogtreecommitdiff
path: root/bin
diff options
context:
space:
mode:
authorfuzzyray <fuzzyray@gentoo.org>2009-05-05 17:39:24 +0000
committerfuzzyray <fuzzyray@gentoo.org>2009-05-05 17:39:24 +0000
commitc819d146be6bce86d97019494173253e71b85d2f (patch)
tree200d00c2b9a420540ff9c4e0d8b3080b762fb562 /bin
parent61823fe68081c6f8edf24455bbb34123598c5bb4 (diff)
downloadgentoolkit-c819d146be6bce86d97019494173253e71b85d2f.tar.gz
Rearrange trunk to support gentoolkit version 0.3. Split into gentoolkit, gentoolkit-dev, and deprecated. Import djanderson's work on the gentoolkit library and equery
svn path=/trunk/gentoolkit/; revision=589
Diffstat (limited to 'bin')
-rw-r--r--bin/eclean802
-rwxr-xr-xbin/epkginfo207
-rw-r--r--bin/equery32
-rwxr-xr-xbin/eread94
-rwxr-xr-xbin/euse547
-rw-r--r--bin/glsa-check366
-rwxr-xr-xbin/revdep-rebuild1094
7 files changed, 3142 insertions, 0 deletions
diff --git a/bin/eclean b/bin/eclean
new file mode 100644
index 0000000..8b473f9
--- /dev/null
+++ b/bin/eclean
@@ -0,0 +1,802 @@
+#!/usr/bin/env python
+# Copyright 2003-2005 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+# $Header: $
+
+
+###############################################################################
+# Meta:
+__author__ = "Thomas de Grenier de Latour (tgl)"
+__email__ = "degrenier@easyconnect.fr"
+__version__ = open('/etc/gentoolkit-version').read().strip()
+__productname__ = "eclean"
+__description__ = "A cleaning tool for Gentoo distfiles and binaries."
+
+
+###############################################################################
+# Python imports:
+import sys
+import os, stat
+import re
+import time
+import getopt
+import fpformat
+import signal
+
+import portage
+from portage.output import *
+
+listdir = portage.listdir
+
+###############################################################################
+# Misc. shortcuts to some portage stuff:
+port_settings = portage.settings
+distdir = port_settings["DISTDIR"]
+pkgdir = port_settings["PKGDIR"]
+
+
+###############################################################################
+# printVersion:
+def printVersion():
+ print "%s (%s) - %s" \
+ % (__productname__, __version__, __description__)
+ print
+ print "Author: %s <%s>" % (__author__,__email__)
+ print "Copyright 2003-2009 Gentoo Foundation"
+ print "Distributed under the terms of the GNU General Public License v2"
+
+
+###############################################################################
+# printUsage: print help message. May also print partial help to stderr if an
+# error from {'options','actions'} is specified.
+def printUsage(error=None,help=None):
+ out = sys.stdout
+ if error: out = sys.stderr
+ if not error in ('actions', 'global-options', \
+ 'packages-options', 'distfiles-options', \
+ 'merged-packages-options', 'merged-distfiles-options', \
+ 'time', 'size'):
+ error = None
+ if not error and not help: help = 'all'
+ if error == 'time':
+ eerror("Wrong time specification")
+ print >>out, "Time specification should be an integer followed by a"+ \
+ " single letter unit."
+ print >>out, "Available units are: y (years), m (months), w (weeks), "+ \
+ "d (days) and h (hours)."
+ print >>out, "For instance: \"1y\" is \"one year\", \"2w\" is \"two"+ \
+ " weeks\", etc. "
+ return
+ if error == 'size':
+ eerror("Wrong size specification")
+ print >>out, "Size specification should be an integer followed by a"+ \
+ " single letter unit."
+ print >>out, "Available units are: G, M, K and B."
+ print >>out, "For instance: \"10M\" is \"ten megabytes\", \"200K\" "+ \
+ "is \"two hundreds kilobytes\", etc."
+ return
+ if error in ('global-options', 'packages-options', 'distfiles-options', \
+ 'merged-packages-options', 'merged-distfiles-options',):
+ eerror("Wrong option on command line.")
+ print >>out
+ elif error == 'actions':
+ eerror("Wrong or missing action name on command line.")
+ print >>out
+ print >>out, white("Usage:")
+ if error in ('actions','global-options', 'packages-options', \
+ 'distfiles-options') or help == 'all':
+ print >>out, " "+turquoise(__productname__), \
+ yellow("[global-option] ..."), \
+ green("<action>"), \
+ yellow("[action-option] ...")
+ if error == 'merged-distfiles-options' or help in ('all','distfiles'):
+ print >>out, " "+turquoise(__productname__+'-dist'), \
+ yellow("[global-option, distfiles-option] ...")
+ if error == 'merged-packages-options' or help in ('all','packages'):
+ print >>out, " "+turquoise(__productname__+'-pkg'), \
+ yellow("[global-option, packages-option] ...")
+ if error in ('global-options', 'actions'):
+ print >>out, " "+turquoise(__productname__), \
+ yellow("[--help, --version]")
+ if help == 'all':
+ print >>out, " "+turquoise(__productname__+"(-dist,-pkg)"), \
+ yellow("[--help, --version]")
+ if error == 'merged-packages-options' or help == 'packages':
+ print >>out, " "+turquoise(__productname__+'-pkg'), \
+ yellow("[--help, --version]")
+ if error == 'merged-distfiles-options' or help == 'distfiles':
+ print >>out, " "+turquoise(__productname__+'-dist'), \
+ yellow("[--help, --version]")
+ print >>out
+ if error in ('global-options', 'merged-packages-options', \
+ 'merged-distfiles-options') or help:
+ print >>out, "Available global", yellow("options")+":"
+ print >>out, yellow(" -C, --nocolor")+ \
+ " - turn off colors on output"
+ print >>out, yellow(" -d, --destructive")+ \
+ " - only keep the minimum for a reinstallation"
+ print >>out, yellow(" -e, --exclude-file=<path>")+ \
+ " - path to the exclusion file"
+ print >>out, yellow(" -i, --interactive")+ \
+ " - ask confirmation before deletions"
+ print >>out, yellow(" -n, --package-names")+ \
+ " - protect all versions (when --destructive)"
+ print >>out, yellow(" -p, --pretend")+ \
+ " - only display what would be cleaned"
+ print >>out, yellow(" -q, --quiet")+ \
+ " - be as quiet as possible"
+ print >>out, yellow(" -t, --time-limit=<time>")+ \
+ " - don't delete files modified since "+yellow("<time>")
+ print >>out, " "+yellow("<time>"), "is a duration: \"1y\" is"+ \
+ " \"one year\", \"2w\" is \"two weeks\", etc. "
+ print >>out, " "+"Units are: y (years), m (months), w (weeks), "+ \
+ "d (days) and h (hours)."
+ print >>out, yellow(" -h, --help")+ \
+ " - display the help screen"
+ print >>out, yellow(" -V, --version")+ \
+ " - display version info"
+ print >>out
+ if error == 'actions' or help == 'all':
+ print >>out, "Available", green("actions")+":"
+ print >>out, green(" packages")+ \
+ " - clean outdated binary packages from:"
+ print >>out, " ",teal(pkgdir)
+ print >>out, green(" distfiles")+ \
+ " - clean outdated packages sources files from:"
+ print >>out, " ",teal(distdir)
+ print >>out
+ if error in ('packages-options','merged-packages-options') \
+ or help in ('all','packages'):
+ print >>out, "Available", yellow("options"),"for the", \
+ green("packages"),"action:"
+ print >>out, yellow(" NONE :)")
+ print >>out
+ if error in ('distfiles-options', 'merged-distfiles-options') \
+ or help in ('all','distfiles'):
+ print >>out, "Available", yellow("options"),"for the", \
+ green("distfiles"),"action:"
+ print >>out, yellow(" -f, --fetch-restricted")+ \
+ " - protect fetch-restricted files (when --destructive)"
+ print >>out, yellow(" -s, --size-limit=<size>")+ \
+ " - don't delete distfiles bigger than "+yellow("<size>")
+ print >>out, " "+yellow("<size>"), "is a size specification: "+ \
+ "\"10M\" is \"ten megabytes\", \"200K\" is"
+ print >>out, " "+"\"two hundreds kilobytes\", etc. Units are: "+ \
+ "G, M, K and B."
+ print >>out
+ print >>out, "More detailed instruction can be found in", \
+ turquoise("`man %s`" % __productname__)
+
+
+###############################################################################
+# einfo: display an info message depending on a color mode
+def einfo(message="", nocolor=False):
+ if not nocolor: prefix = " "+green('*')
+ else: prefix = ">>>"
+ print prefix,message
+
+
+###############################################################################
+# eerror: display an error depending on a color mode
+def eerror(message="", nocolor=False):
+ if not nocolor: prefix = " "+red('*')
+ else: prefix = "!!!"
+ print >>sys.stderr,prefix,message
+
+
+###############################################################################
+# eprompt: display a user question depending on a color mode.
+def eprompt(message, nocolor=False):
+ if not nocolor: prefix = " "+red('>')+" "
+ else: prefix = "??? "
+ sys.stdout.write(prefix+message)
+ sys.stdout.flush()
+
+
+###############################################################################
+# prettySize: integer -> byte/kilo/mega/giga converter. Optionnally justify the
+# result. Output is a string.
+def prettySize(size,justify=False):
+ units = [" G"," M"," K"," B"]
+ approx = 0
+ while len(units) and size >= 1000:
+ approx = 1
+ size = size / 1024.
+ units.pop()
+ sizestr = fpformat.fix(size,approx)+units[-1]
+ if justify:
+ sizestr = " " + blue("[ ") + " "*(7-len(sizestr)) \
+ + green(sizestr) + blue(" ]")
+ return sizestr
+
+
+###############################################################################
+# yesNoAllPrompt: print a prompt until user answer in yes/no/all. Return a
+# boolean for answer, and also may affect the 'accept_all' option.
+# Note: i gave up with getch-like functions, to much bugs in case of escape
+# sequences. Back to raw_input.
+def yesNoAllPrompt(myoptions,message="Do you want to proceed?"):
+ user_string="xxx"
+ while not user_string.lower() in ["","y","n","a","yes","no","all"]:
+ eprompt(message+" [Y/n/a]: ", myoptions['nocolor'])
+ user_string = raw_input()
+ if user_string.lower() in ["a","all"]:
+ myoptions['accept_all'] = True
+ myanswer = user_string.lower() in ["","y","a","yes","all"]
+ return myanswer
+
+
+###############################################################################
+# ParseArgsException: for parseArgs() -> main() communication
+class ParseArgsException(Exception):
+ def __init__(self, value):
+ self.value = value
+ def __str__(self):
+ return repr(self.value)
+
+
+###############################################################################
+# parseSize: convert a file size "Xu" ("X" is an integer, and "u" in [G,M,K,B])
+# into an integer (file size in Bytes). Raises ParseArgsException('size') in
+# case of failure.
+def parseSize(size):
+ myunits = { \
+ 'G': (1024**3), \
+ 'M': (1024**2), \
+ 'K': 1024, \
+ 'B': 1 \
+ }
+ try:
+ mymatch = re.match(r"^(?P<value>\d+)(?P<unit>[GMKBgmkb])?$",size)
+ mysize = int(mymatch.group('value'))
+ if mymatch.group('unit'):
+ mysize *= myunits[mymatch.group('unit').capitalize()]
+ except:
+ raise ParseArgsException('size')
+ return mysize
+
+
+###############################################################################
+# parseTime: convert a duration "Xu" ("X" is an int, and "u" a time unit in
+# [Y,M,W,D,H]) into an integer which is a past EPOCH date.
+# Raises ParseArgsException('time') in case of failure.
+# (yep, big approximations inside... who cares?)
+def parseTime(timespec):
+ myunits = {'H' : (60 * 60)}
+ myunits['D'] = myunits['H'] * 24
+ myunits['W'] = myunits['D'] * 7
+ myunits['M'] = myunits['D'] * 30
+ myunits['Y'] = myunits['D'] * 365
+ try:
+ # parse the time specification
+ mymatch = re.match(r"^(?P<value>\d+)(?P<unit>[YMWDHymwdh])?$",timespec)
+ myvalue = int(mymatch.group('value'))
+ if not mymatch.group('unit'): myunit = 'D'
+ else: myunit = mymatch.group('unit').capitalize()
+ except: raise ParseArgsException('time')
+ # calculate the limit EPOCH date
+ mytime = time.time() - (myvalue * myunits[myunit])
+ return mytime
+
+
+###############################################################################
+# parseCmdLine: parse the command line arguments. Raise exceptions on errors or
+# non-action modes (help/version). Returns an action, and affect the options
+# dict.
+def parseArgs(myoptions={}):
+
+ # local function for interpreting command line options
+ # and setting myoptions accordingly
+ def optionSwitch(myoption,opts,action=None):
+ return_code = True
+ for o, a in opts:
+ if o in ("-h", "--help"):
+ if action: raise ParseArgsException('help-'+action)
+ else: raise ParseArgsException('help')
+ elif o in ("-V", "--version"):
+ raise ParseArgsException('version')
+ elif o in ("-C", "--nocolor"):
+ myoptions['nocolor'] = True
+ nocolor()
+ elif o in ("-d", "--destructive"):
+ myoptions['destructive'] = True
+ elif o in ("-i", "--interactive") and not myoptions['pretend']:
+ myoptions['interactive'] = True
+ elif o in ("-p", "--pretend"):
+ myoptions['pretend'] = True
+ myoptions['interactive'] = False
+ elif o in ("-q", "--quiet"):
+ myoptions['quiet'] = True
+ elif o in ("-t", "--time-limit"):
+ myoptions['time-limit'] = parseTime(a)
+ elif o in ("-e", "--exclude-file"):
+ myoptions['exclude-file'] = a
+ elif o in ("-n", "--package-names"):
+ myoptions['package-names'] = True
+ elif o in ("-f", "--fetch-restricted"):
+ myoptions['fetch-restricted'] = True
+ elif o in ("-s", "--size-limit"):
+ myoptions['size-limit'] = parseSize(a)
+ else: return_code = False
+ # sanity check of --destructive only options:
+ for myopt in ('fetch-restricted', 'package-names'):
+ if (not myoptions['destructive']) and myoptions[myopt]:
+ if not myoptions['quiet']:
+ eerror("--%s only makes sense in --destructive mode." \
+ % myopt, myoptions['nocolor'])
+ myoptions[myopt] = False
+ return return_code
+
+ # here are the different allowed command line options (getopt args)
+ getopt_options = {'short':{}, 'long':{}}
+ getopt_options['short']['global'] = "Cdipqe:t:nhV"
+ getopt_options['long']['global'] = ["nocolor", "destructive", \
+ "interactive", "pretend", "quiet", "exclude-file=", "time-limit=", \
+ "package-names", "help", "version"]
+ getopt_options['short']['distfiles'] = "fs:"
+ getopt_options['long']['distfiles'] = ["fetch-restricted", "size-limit="]
+ getopt_options['short']['packages'] = ""
+ getopt_options['long']['packages'] = [""]
+ # set default options, except 'nocolor', which is set in main()
+ myoptions['interactive'] = False
+ myoptions['pretend'] = False
+ myoptions['quiet'] = False
+ myoptions['accept_all'] = False
+ myoptions['destructive'] = False
+ myoptions['time-limit'] = 0
+ myoptions['package-names'] = False
+ myoptions['fetch-restricted'] = False
+ myoptions['size-limit'] = 0
+ # if called by a well-named symlink, set the acction accordingly:
+ myaction = None
+ if os.path.basename(sys.argv[0]) in \
+ (__productname__+'-pkg', __productname__+'-packages'):
+ myaction = 'packages'
+ elif os.path.basename(sys.argv[0]) in \
+ (__productname__+'-dist', __productname__+'-distfiles'):
+ myaction = 'distfiles'
+ # prepare for the first getopt
+ if myaction:
+ short_opts = getopt_options['short']['global'] \
+ + getopt_options['short'][myaction]
+ long_opts = getopt_options['long']['global'] \
+ + getopt_options['long'][myaction]
+ opts_mode = 'merged-'+myaction
+ else:
+ short_opts = getopt_options['short']['global']
+ long_opts = getopt_options['long']['global']
+ opts_mode = 'global'
+ # apply getopts to command line, show partial help on failure
+ try: opts, args = getopt.getopt(sys.argv[1:], short_opts, long_opts)
+ except: raise ParseArgsException(opts_mode+'-options')
+ # set myoptions accordingly
+ optionSwitch(myoptions,opts,action=myaction)
+ # if action was already set, there should be no more args
+ if myaction and len(args): raise ParseArgsException(opts_mode+'-options')
+ # if action was set, there is nothing left to do
+ if myaction: return myaction
+ # So, we are in "eclean --foo action --bar" mode. Parse remaining args...
+ # Only two actions are allowed: 'packages' and 'distfiles'.
+ if not len(args) or not args[0] in ('packages','distfiles'):
+ raise ParseArgsException('actions')
+ myaction = args.pop(0)
+ # parse the action specific options
+ try: opts, args = getopt.getopt(args, \
+ getopt_options['short'][myaction], \
+ getopt_options['long'][myaction])
+ except: raise ParseArgsException(myaction+'-options')
+ # set myoptions again, for action-specific options
+ optionSwitch(myoptions,opts,action=myaction)
+ # any remaning args? Then die!
+ if len(args): raise ParseArgsException(myaction+'-options')
+ # returns the action. Options dictionary is modified by side-effect.
+ return myaction
+
+###############################################################################
+# isValidCP: check wether a string is a valid cat/pkg-name
+# This is for 2.0.51 vs. CVS HEAD compatibility, i've not found any function
+# for that which would exists in both. Weird...
+def isValidCP(cp):
+ if not '/' in cp: return False
+ try: portage.cpv_getkey(cp+"-0")
+ except: return False
+ else: return True
+
+
+###############################################################################
+# ParseExcludeFileException: for parseExcludeFile() -> main() communication
+class ParseExcludeFileException(Exception):
+ def __init__(self, value):
+ self.value = value
+ def __str__(self):
+ return repr(self.value)
+
+
+###############################################################################
+# parseExcludeFile: parses an exclusion file, returns an exclusion dictionnary
+# Raises ParseExcludeFileException in case of fatal error.
+def parseExcludeFile(filepath):
+ excl_dict = { \
+ 'categories':{}, \
+ 'packages':{}, \
+ 'anti-packages':{}, \
+ 'garbage':{} }
+ try: file = open(filepath,"r")
+ except IOError:
+ raise ParseExcludeFileException("Could not open exclusion file.")
+ filecontents = file.readlines()
+ file.close()
+ cat_re = re.compile('^(?P<cat>[a-zA-Z0-9]+-[a-zA-Z0-9]+)(/\*)?$')
+ cp_re = re.compile('^(?P<cp>[-a-zA-Z0-9_]+/[-a-zA-Z0-9_]+)$')
+ for line in filecontents:
+ line = line.strip()
+ if not len(line): continue
+ if line[0] == '#': continue
+ try: mycat = cat_re.match(line).group('cat')
+ except: pass
+ else:
+ if not mycat in portage.settings.categories:
+ raise ParseExcludeFileException("Invalid category: "+mycat)
+ excl_dict['categories'][mycat] = None
+ continue
+ dict_key = 'packages'
+ if line[0] == '!':
+ dict_key = 'anti-packages'
+ line = line[1:]
+ try:
+ mycp = cp_re.match(line).group('cp')
+ if isValidCP(mycp):
+ excl_dict[dict_key][mycp] = None
+ continue
+ else: raise ParseExcludeFileException("Invalid cat/pkg: "+mycp)
+ except: pass
+ #raise ParseExcludeFileException("Invalid line: "+line)
+ try:
+ excl_dict['garbage'][line] = re.compile(line)
+ except:
+ try:
+ excl_dict['garbage'][line] = re.compile(re.escape(line))
+ except:
+ raise ParseExcludeFileException("Invalid file name/regular expression: "+line)
+ return excl_dict
+
+
+###############################################################################
+# exclDictExpand: returns a dictionary of all CP from porttree which match
+# the exclusion dictionary
+def exclDictExpand(excl_dict):
+ mydict = {}
+ if 'categories' in excl_dict:
+ # XXX: i smell an access to something which is really out of API...
+ for mytree in portage.portdb.porttrees:
+ for mycat in excl_dict['categories']:
+ for mypkg in listdir(os.path.join(mytree,mycat),ignorecvs=1):
+ mydict[mycat+'/'+mypkg] = None
+ if 'packages' in excl_dict:
+ for mycp in excl_dict['packages']:
+ mydict[mycp] = None
+ if 'anti-packages' in excl_dict:
+ for mycp in excl_dict['anti-packages']:
+ if mycp in mydict:
+ del mydict[mycp]
+ return mydict
+
+
+###############################################################################
+# exclDictMatch: checks whether a CP matches the exclusion rules
+def exclDictMatch(excl_dict,pkg):
+ if 'anti-packages' in excl_dict \
+ and pkg in excl_dict['anti-packages']:
+ return False
+ if 'packages' in excl_dict \
+ and pkg in excl_dict['packages']:
+ return True
+ mycat = pkg.split('/')[0]
+ if 'categories' in excl_dict \
+ and mycat in excl_dict['categories']:
+ return True
+ return False
+
+
+###############################################################################
+# findDistfiles: find all obsolete distfiles.
+# XXX: what about cvs ebuilds? i should install some to see where it goes...
+def findDistfiles( \
+ exclude_dict={}, \
+ destructive=False,\
+ fetch_restricted=False, \
+ package_names=False, \
+ time_limit=0, \
+ size_limit=0):
+ # this regexp extracts files names from SRC_URI. It is not very precise,
+ # but we don't care (may return empty strings, etc.), since it is fast.
+ file_regexp = re.compile('([a-zA-Z0-9_,\.\-\+\~]*)[\s\)]')
+ clean_dict = {}
+ keep = []
+ pkg_dict = {}
+
+ # create a big CPV->SRC_URI dict of packages whose distfiles should be kept
+ if (not destructive) or fetch_restricted:
+ # list all CPV from portree (yeah, that takes time...)
+ for package in portage.portdb.cp_all():
+ for my_cpv in portage.portdb.cp_list(package):
+ # get SRC_URI and RESTRICT from aux_get
+ try: (src_uri,restrict) = \
+ portage.portdb.aux_get(my_cpv,["SRC_URI","RESTRICT"])
+ except KeyError: continue
+ # keep either all or fetch-restricted only
+ if (not destructive) or ('fetch' in restrict):
+ pkg_dict[my_cpv] = src_uri
+ if destructive:
+ if not package_names:
+ # list all CPV from vartree
+ pkg_list = portage.db[portage.root]["vartree"].dbapi.cpv_all()
+ else:
+ # list all CPV from portree for CP in vartree
+ pkg_list = []
+ for package in portage.db[portage.root]["vartree"].dbapi.cp_all():
+ pkg_list += portage.portdb.cp_list(package)
+ for my_cp in exclDictExpand(exclude_dict):
+ # add packages from the exclude file
+ pkg_list += portage.portdb.cp_list(my_cp)
+ for my_cpv in pkg_list:
+ # skip non-existing CPV (avoids ugly aux_get messages)
+ if not portage.portdb.cpv_exists(my_cpv): continue
+ # get SRC_URI from aux_get
+ try: pkg_dict[my_cpv] = \
+ portage.portdb.aux_get(my_cpv,["SRC_URI"])[0]
+ except KeyError: continue
+ del pkg_list
+
+ # create a dictionary of files which should be deleted
+ for file in os.listdir(distdir):
+ filepath = os.path.join(distdir, file)
+ try: file_stat = os.stat(filepath)
+ except: continue
+ if not stat.S_ISREG(file_stat[stat.ST_MODE]): continue
+ if size_limit and (file_stat[stat.ST_SIZE] >= size_limit):
+ continue
+ if time_limit and (file_stat[stat.ST_MTIME] >= time_limit):
+ continue
+ if 'garbage' in exclude_dict:
+ # Try to match file name directly
+ if file in exclude_dict['garbage']:
+ file_match = True
+ # See if file matches via regular expression matching
+ else:
+ file_match = False
+ for file_entry in exclude_dict['garbage']:
+ if exclude_dict['garbage'][file_entry].match(file):
+ file_match = True
+ break
+
+ if file_match:
+ continue
+ # this is a candidate for cleaning
+ clean_dict[file]=[filepath]
+ # remove files owned by some protected packages
+ for my_cpv in pkg_dict:
+ for file in file_regexp.findall(pkg_dict[my_cpv]+"\n"):
+ if file in clean_dict:
+ del clean_dict[file]
+ # no need to waste IO time if there is nothing left to clean
+ if not len(clean_dict): return clean_dict
+ return clean_dict
+
+
+###############################################################################
+# findPackages: find all obsolete binary packages.
+# XXX: packages are found only by symlinks. Maybe i should also return .tbz2
+# files from All/ that have no corresponding symlinks.
+def findPackages( \
+ exclude_dict={}, \
+ destructive=False, \
+ time_limit=0, \
+ package_names=False):
+ clean_dict = {}
+ # create a full package dictionnary
+ for root, dirs, files in os.walk(pkgdir):
+ if root[-3:] == 'All': continue
+ for file in files:
+ if not file[-5:] == ".tbz2":
+ # ignore non-tbz2 files
+ continue
+ path = os.path.join(root, file)
+ category = os.path.split(root)[-1]
+ cpv = category+"/"+file[:-5]
+ mystat = os.lstat(path)
+ if time_limit and (mystat[stat.ST_MTIME] >= time_limit):
+ # time-limit exclusion
+ continue
+ # dict is cpv->[files] (2 files in general, because of symlink)
+ clean_dict[cpv] = [path]
+ #if os.path.islink(path):
+ if stat.S_ISLNK(mystat[stat.ST_MODE]):
+ clean_dict[cpv].append(os.path.realpath(path))
+ # keep only obsolete ones
+ if destructive:
+ mydbapi = portage.db[portage.root]["vartree"].dbapi
+ if package_names: cp_all = dict.fromkeys(mydbapi.cp_all())
+ else: cp_all = {}
+ else:
+ mydbapi = portage.db[portage.root]["porttree"].dbapi
+ cp_all = {}
+ for mycpv in clean_dict.keys():
+ if exclDictMatch(exclude_dict,portage.cpv_getkey(mycpv)):
+ # exclusion because of the exclude file
+ del clean_dict[mycpv]
+ continue
+ if mydbapi.cpv_exists(mycpv):
+ # exclusion because pkg still exists (in porttree or vartree)
+ del clean_dict[mycpv]
+ continue
+ if portage.cpv_getkey(mycpv) in cp_all:
+ # exlusion because of --package-names
+ del clean_dict[mycpv]
+
+ return clean_dict
+
+
+###############################################################################
+# doCleanup: takes a dictionnary {'display name':[list of files]}. Calculate
+# size of each entry for display, prompt user if needed, delete files if needed
+# and return the total size of files that [have been / would be] deleted.
+def doCleanup(clean_dict,action,myoptions):
+ # define vocabulary of this action
+ if action == 'distfiles': file_type = 'file'
+ else: file_type = 'binary package'
+ # sorting helps reading
+ clean_keys = clean_dict.keys()
+ clean_keys.sort()
+ clean_size = 0
+ # clean all entries one by one
+ for mykey in clean_keys:
+ key_size = 0
+ for file in clean_dict[mykey]:
+ # get total size for an entry (may be several files, and
+ # symlinks count zero)
+ if os.path.islink(file): continue
+ try: key_size += os.path.getsize(file)
+ except: eerror("Could not read size of "+file, \
+ myoptions['nocolor'])
+ if not myoptions['quiet']:
+ # pretty print mode
+ print prettySize(key_size,True),teal(mykey)
+ elif myoptions['pretend'] or myoptions['interactive']:
+ # file list mode
+ for file in clean_dict[mykey]: print file
+ #else: actually delete stuff, but don't print anything
+ if myoptions['pretend']: clean_size += key_size
+ elif not myoptions['interactive'] \
+ or myoptions['accept_all'] \
+ or yesNoAllPrompt(myoptions, \
+ "Do you want to delete this " \
+ + file_type+"?"):
+ # non-interactive mode or positive answer.
+ # For each file,...
+ for file in clean_dict[mykey]:
+ # ...get its size...
+ filesize = 0
+ if not os.path.exists(file): continue
+ if not os.path.islink(file):
+ try: filesize = os.path.getsize(file)
+ except: eerror("Could not read size of "\
+ +file, myoptions['nocolor'])
+ # ...and try to delete it.
+ try: os.unlink(file)
+ except: eerror("Could not delete "+file, \
+ myoptions['nocolor'])
+ # only count size if successfully deleted
+ else: clean_size += filesize
+ # return total size of deleted or to delete files
+ return clean_size
+
+
+###############################################################################
+# doAction: execute one action, ie display a few message, call the right find*
+# function, and then call doCleanup with its result.
+def doAction(action,myoptions,exclude_dict={}):
+ # define vocabulary for the output
+ if action == 'packages': files_type = "binary packages"
+ else: files_type = "distfiles"
+ # find files to delete, depending on the action
+ if not myoptions['quiet']:
+ einfo("Building file list for "+action+" cleaning...", \
+ myoptions['nocolor'])
+ if action == 'packages':
+ clean_dict = findPackages( \
+ exclude_dict=exclude_dict, \
+ destructive=myoptions['destructive'], \
+ package_names=myoptions['package-names'], \
+ time_limit=myoptions['time-limit'])
+ else:
+ clean_dict = findDistfiles( \
+ exclude_dict=exclude_dict, \
+ destructive=myoptions['destructive'], \
+ fetch_restricted=myoptions['fetch-restricted'], \
+ package_names=myoptions['package-names'], \
+ time_limit=myoptions['time-limit'], \
+ size_limit=myoptions['size-limit'])
+ # actually clean files if something was found
+ if len(clean_dict.keys()):
+ # verbose pretend message
+ if myoptions['pretend'] and not myoptions['quiet']:
+ einfo("Here are "+files_type+" that would be deleted:", \
+ myoptions['nocolor'])
+ # verbose non-pretend message
+ elif not myoptions['quiet']:
+ einfo("Cleaning "+files_type+"...",myoptions['nocolor'])
+ # do the cleanup, and get size of deleted files
+ clean_size = doCleanup(clean_dict,action,myoptions)
+ # vocabulary for final message
+ if myoptions['pretend']: verb = "would be"
+ else: verb = "has been"
+ # display freed space
+ if not myoptions['quiet']:
+ einfo("Total space that "+verb+" freed in " \
+ + action + " directory: " \
+ + red(prettySize(clean_size)), \
+ myoptions['nocolor'])
+ # nothing was found, return
+ elif not myoptions['quiet']:
+ einfo("Your "+action+" directory was already clean.", \
+ myoptions['nocolor'])
+
+
+###############################################################################
+# main: parse command line and execute all actions
+def main():
+ # set default options
+ myoptions = {}
+ myoptions['nocolor'] = port_settings["NOCOLOR"] in ('yes','true') \
+ and sys.stdout.isatty()
+ if myoptions['nocolor']: nocolor()
+ # parse command line options and actions
+ try: myaction = parseArgs(myoptions)
+ # filter exception to know what message to display
+ except ParseArgsException, e:
+ if e.value == 'help':
+ printUsage(help='all')
+ sys.exit(0)
+ elif e.value[:5] == 'help-':
+ printUsage(help=e.value[5:])
+ sys.exit(0)
+ elif e.value == 'version':
+ printVersion()
+ sys.exit(0)
+ else:
+ printUsage(e.value)
+ sys.exit(2)
+ # parse the exclusion file
+ if not 'exclude-file' in myoptions:
+ my_exclude_file = "/etc/%s/%s.exclude" % (__productname__ , myaction)
+ if os.path.isfile(my_exclude_file):
+ myoptions['exclude-file'] = my_exclude_file
+ if 'exclude-file' in myoptions:
+ try: exclude_dict = parseExcludeFile(myoptions['exclude-file'])
+ except ParseExcludeFileException, e:
+ eerror(e, myoptions['nocolor'])
+ eerror("Invalid exclusion file: %s" % myoptions['exclude-file'], \
+ myoptions['nocolor'])
+ eerror("See format of this file in `man %s`" % __productname__, \
+ myoptions['nocolor'])
+ sys.exit(1)
+ else: exclude_dict={}
+ # security check for non-pretend mode
+ if not myoptions['pretend'] and portage.secpass == 0:
+ eerror("Permission denied: you must be root or belong to the portage group.", \
+ myoptions['nocolor'])
+ sys.exit(1)
+ # execute action
+ doAction(myaction, myoptions, exclude_dict=exclude_dict)
+
+
+###############################################################################
+# actually call main() if launched as a script
+if __name__ == "__main__":
+ try: main()
+ except KeyboardInterrupt:
+ print "Aborted."
+ sys.exit(130)
+ sys.exit(0)
+
diff --git a/bin/epkginfo b/bin/epkginfo
new file mode 100755
index 0000000..313170a
--- /dev/null
+++ b/bin/epkginfo
@@ -0,0 +1,207 @@
+#!/usr/bin/python
+##############################################################################
+# $Header: $
+##############################################################################
+# Distributed under the terms of the GNU General Public License, v2 or later
+# Author: Ned Ludd <solar@gentoo.org> (glue all the parts together)
+# Author: Eldad Zack <eldad@gentoo.org> (earch)
+# Author : Eric Olinger <EvvL AT RustedHalo DOT net> (metadata)
+
+# Gentoo metadata xml and arch keyword checking tool.
+
+import os
+import sys
+import re
+from stat import *
+from xml.sax import saxutils, make_parser, handler
+from xml.sax.handler import feature_namespaces
+
+import portage
+from portage.output import *
+
+version = open('/etc/gentoolkit-version').read().strip()
+
+def getvar(pkg, var):
+ file = open(pkg + ".ebuild")
+ for line in file.readlines():
+ line = line.rstrip()
+ if re.match("^"+var+"=",line):
+ vars = re.split("\"",line)[1]
+ file.close
+ return re.split(" ",vars)
+ file.close()
+
+def earch(workdir):
+ """Prints arch keywords for a given dir"""
+ portdir = portage.settings["PORTDIR"]
+ #workdir = "."
+ os.chdir(workdir)
+
+ archdict = {}
+ ebuildlist = []
+ for file in os.listdir(workdir):
+ if re.search("\.ebuild$",file):
+ ebuildlist.append(re.split("\.ebuild$",file)[0])
+
+ ebuildlist.sort(lambda x,y: portage.pkgcmp(portage.pkgsplit(x),portage.pkgsplit(y)))
+
+ for pkg in ebuildlist:
+ keywords = getvar(pkg, "KEYWORDS")
+ for arch in keywords:
+ if arch == "":
+ arch = None
+ archdict[arch] = pkg
+
+ archlist = archdict.keys();
+ archlist.sort()
+
+ for pkg in ebuildlist:
+ print darkgreen("Keywords: ") + pkg + ":",
+ for value in archlist:
+ if (value and archdict[value] == pkg):
+ if value[0] == "-":
+ print red(value),
+ elif "~" == value[0]:
+ print blue(value),
+ else:
+ print green(value),
+ print ""
+
+
+class Metadata_XML(handler.ContentHandler):
+ _inside_herd="No"
+ _inside_maintainer="No"
+ _inside_email="No"
+ _inside_longdescription="No"
+
+ _herd = ""
+ _maintainers = []
+ _longdescription = ""
+
+ def startElement(self, tag, attr):
+ if tag == "herd":
+ self._inside_herd="Yes"
+ if tag == "longdescription":
+ self._inside_longdescription="Yes"
+ if tag == "maintainer":
+ self._inside_maintainer="Yes"
+ if tag == "email":
+ self._inside_email="Yes"
+
+ def endElement(self, tag):
+ if tag == "herd":
+ self._inside_herd="No"
+ if tag == "longdescription":
+ self._inside_longdescription="No"
+ if tag == "maintainer":
+ self._inside_maintainer="No"
+ if tag == "email":
+ self._inside_email="No"
+
+ def characters(self, contents):
+ if self._inside_herd == "Yes":
+ self._herd = contents
+
+ if self._inside_longdescription == "Yes":
+ self._longdescription = contents
+
+ if self._inside_maintainer=="Yes" and self._inside_email=="Yes":
+ self._maintainers.append(contents)
+
+
+def check_metadata(full_package):
+ """Checks that the primary maintainer is still an active dev and list the hed the package belongs to"""
+ metadata_file=portage.settings["PORTDIR"] + "/" + portage.pkgsplit(full_package)[0] + "/metadata.xml"
+ if not os.path.exists(metadata_file):
+ print darkgreen("Maintainer: ") + red("Error (Missing metadata.xml)")
+ return 1
+
+ parser = make_parser()
+ handler = Metadata_XML()
+ handler._maintainers = []
+ parser.setContentHandler(handler)
+ parser.parse( metadata_file )
+
+ if len(handler._herd) < 1:
+ print darkgreen("Herd: ") + red("Error (No Herd)")
+ return 1
+ else:
+ print darkgreen("Herd: ") + handler._herd
+
+ if len(handler._maintainers) < 1:
+ print darkgreen("Maintainer: ") + handler._herd
+ else:
+ print darkgreen("Maintainer: ") + ", ".join(handler._maintainers)
+
+ if len(handler._longdescription) > 1:
+ print darkgreen("Description: ") + handler._longdescription
+ print darkgreen("Location: ") + os.path.normpath(portage.settings["PORTDIR"] + "/" + portage.pkgsplit(full_package)[0])
+
+
+def usage(code):
+ """Prints the uage information for this script"""
+ print green("epkginfo"), "(%s)" % version
+ print
+ print "Usage: epkginfo [package-cat/]package"
+ sys.exit(code)
+
+
+# default color setup
+if ( not sys.stdout.isatty() ) or ( portage.settings["NOCOLOR"] in ["yes","true"] ):
+ nocolor()
+
+def fc(x,y):
+ return cmp(y[0], x[0])
+
+
+def grab_changelog_devs(catpkg):
+ try:
+ os.chdir(portage.settings["PORTDIR"] + "/" + catpkg)
+ foo=""
+ r=re.compile("<[^@]+@gentoo.org>", re.I)
+ s="\n".join(portage.grabfile("ChangeLog"))
+ d={}
+ for x in r.findall(s):
+ if x not in d:
+ d[x] = 0
+ d[x] += 1
+
+ l=[(d[x], x) for x in d.keys()]
+ #l.sort(lambda x,y: cmp(y[0], x[0]))
+ l.sort(fc)
+ for x in l:
+ p = str(x[0]) +" "+ x[1].lstrip("<").rstrip(">")
+ foo += p[:p.find("@")]+", "
+ return foo
+ except:
+ raise
+
+def main ():
+ if len( sys.argv ) < 2:
+ usage(1)
+
+ for pkg in sys.argv[1:]:
+
+ if sys.argv[1:][:1] == "-":
+ print "NOT WORKING?=="+sys.argv[1:]
+ continue
+
+ try:
+ package_list = portage.portdb.xmatch("match-all", pkg)
+ if package_list:
+
+ catpkg = portage.pkgsplit(package_list[0])[0]
+
+ print darkgreen("Package: ") + catpkg
+ check_metadata(package_list[0])
+ earch(portage.settings["PORTDIR"] + "/" + catpkg)
+ #print darkgreen("ChangeLog: ") + grab_changelog_devs(catpkg)
+ print ""
+ else:
+ print "!!! No package '%s'" % pkg
+ except:
+ print red("Error: "+pkg+"\n")
+
+
+if __name__ == '__main__':
+ main()
diff --git a/bin/equery b/bin/equery
new file mode 100644
index 0000000..bac8a3a
--- /dev/null
+++ b/bin/equery
@@ -0,0 +1,32 @@
+#!/usr/bin/python
+#
+# Copyright 2002-2009 Gentoo Technologies, Inc.
+# Distributed under the terms of the GNU General Public License v2 or later
+
+"""equery is a flexible utility for Gentoo linux which can display various
+information about packages, such as the files they own, their USE flags,
+the MD5 sum of each file owned by a given package, and many other things.
+"""
+
+import sys
+# This block ensures that ^C interrupts are handled quietly.
+try:
+ import signal
+
+ def exithandler(signum,frame):
+ signal.signal(signal.SIGINT, signal.SIG_IGN)
+ signal.signal(signal.SIGTERM, signal.SIG_IGN)
+ print
+ sys.exit(1)
+
+ signal.signal(signal.SIGINT, exithandler)
+ signal.signal(signal.SIGTERM, exithandler)
+ signal.signal(signal.SIGPIPE, signal.SIG_DFL)
+
+except KeyboardInterrupt:
+ print
+ sys.exit(1)
+
+from gentoolkit import equery
+
+equery.main()
diff --git a/bin/eread b/bin/eread
new file mode 100755
index 0000000..c6d4de1
--- /dev/null
+++ b/bin/eread
@@ -0,0 +1,94 @@
+#!/bin/bash
+
+# This is a script to read portage log items from einfo, ewarn etc, new in the
+# portage-2.1 series.
+#
+# Author: Donnie Berkholz <spyderous@gentoo.org>
+# Updated by: Uwe Klosa <uwe.klosa@gmail.com>
+
+# set decent PATH for bug 172969
+
+PATH=/usr/bin:/bin:${PATH}
+
+# Set ELOGDIR
+PORT_LOGDIR="$(portageq envvar PORT_LOGDIR)"
+[ "$PORT_LOGDIR" = "" ] && PORT_LOGDIR="/var/log/portage"
+ELOGDIR="$PORT_LOGDIR/elog"
+
+# Verify that ELOGDIR exists
+if [ ! -d "$ELOGDIR" ]; then
+ echo "ELOG directory: $ELOGDIR does not exist!"
+ exit 1
+fi
+
+# Use the pager from the users environment
+[ -z "$PAGER" ] && PAGER="less"
+
+# Set up select prompt
+PS3="Choice? "
+
+select_loop() {
+ ANY_FILES=$(find . -type f)
+ ANY_FILES=$(echo ${ANY_FILES} | sed -e "s:\./::g")
+
+ if [[ -z ${ANY_FILES} ]]; then
+ echo "No log items to read"
+ break
+ fi
+
+ echo
+ echo "This is a list of portage log items. Choose a number to view that file or type q to quit."
+ echo
+
+ # Pick which file to read
+ select FILE in ${ANY_FILES}; do
+ case ${REPLY} in
+ q)
+ echo "Quitting"
+ QUIT="yes"
+ break
+ ;;
+ *)
+ if [ -f "$FILE" ]; then
+ ${PAGER} ${FILE}
+ read -p "Delete file? [y/N] " DELETE
+ case ${DELETE} in
+ q)
+ echo "Quitting"
+ QUIT="yes"
+ break
+ ;;
+ y|Y)
+ rm -f ${FILE}
+ SUCCESS=$?
+ if [[ ${SUCCESS} = 0 ]]; then
+ echo "Deleted ${FILE}"
+ else
+ echo "Unable to delete ${FILE}"
+ fi
+ ;;
+ # Empty string defaults to N (save file)
+ n|N|"")
+ echo "Saving ${FILE}"
+ ;;
+ *)
+ echo "Invalid response. Saving ${FILE}"
+ ;;
+ esac
+ else
+ echo
+ echo "Invalid response."
+ fi
+ ;;
+ esac
+ break
+ done
+}
+
+pushd ${ELOGDIR} > /dev/null
+
+until [[ -n ${QUIT} ]]; do
+ select_loop
+done
+
+popd > /dev/null
diff --git a/bin/euse b/bin/euse
new file mode 100755
index 0000000..b49b120
--- /dev/null
+++ b/bin/euse
@@ -0,0 +1,547 @@
+#!/bin/bash
+
+# $Header$
+
+# bash replacement for the original euse by Arun Bhanu
+# Author: Marius Mauch <genone@gentoo.org>
+# Licensed under the GPL v2
+
+PROGRAM_NAME=euse
+PROGRAM_VERSION=$(cat /etc/gentoolkit-version)
+
+MAKE_CONF_PATH=/etc/make.conf
+MAKE_GLOBALS_PATH=/etc/make.globals
+MAKE_PROFILE_PATH=/etc/make.profile
+MAKE_CONF_BACKUP_PATH=/etc/make.conf.euse_backup
+
+[ -z "${MODE}" ] && MODE="showhelp" # available operation modes: showhelp, showversion, showdesc, showflags, modify
+
+parse_arguments() {
+ if [ -z "${1}" ]; then
+ return
+ fi
+ while [ -n "${1}" ]; do
+ case "${1}" in
+ -h | --help) MODE="showhelp";;
+ -V | -v | --version) MODE="showversion";;
+ -i | --info) MODE="showdesc";;
+ -I | --info-installed) MODE="showinstdesc";;
+ -l | --local) SCOPE="local";;
+ -g | --global) SCOPE="global";;
+ -a | --active) MODE="showflags";;
+ -E | --enable) MODE="modify"; ACTION="add";;
+ -D | --disable) MODE="modify"; ACTION="remove";;
+ -P | --prune) MODE="modify"; ACTION="prune";;
+ -*)
+ echo "ERROR: unknown option ${1} specified."
+ echo
+ MODE="showhelp"
+ ;;
+ "%active")
+ get_useflags
+ ARGUMENTS="${ARGUMENTS} ${ACTIVE_FLAGS[9]}"
+ ;;
+ *)
+ ARGUMENTS="${ARGUMENTS} ${1}"
+ ;;
+ esac
+ shift
+ done
+}
+
+error() {
+ echo "ERROR: ${1}"
+ set +f
+ exit 1
+}
+
+get_real_path() {
+ set -P
+ cd "$1"
+ pwd
+ cd "$OLDPWD"
+ set +P
+}
+
+check_sanity() {
+ # file permission tests
+ local descdir
+ local make_defaults
+
+ descdir="$(get_portdir)/profiles"
+
+ [ ! -r "${MAKE_CONF_PATH}" ] && error "${MAKE_CONF_PATH} is not readable"
+ [ ! -r "${MAKE_GLOBALS_PATH}" ] && error "${MAKE_GLOBALS_PATH} is not readable"
+ [ ! -h "${MAKE_PROFILE_PATH}" ] && error "${MAKE_PROFILE_PATH} is not a symlink"
+ [ -z "$(get_portdir)" ] && error "\$PORTDIR couldn't be determined"
+ [ ! -d "${descdir}" ] && error "${descdir} does not exist or is not a directory"
+ [ ! -r "${descdir}/use.desc" ] && error "${descdir}/use.desc is not readable"
+ [ ! -r "${descdir}/use.local.desc" ] && error "${descdir}/use.local.desc is not readable"
+ for make_defaults in $(get_all_make_defaults); do
+ [ ! -r "$make_defaults" ] && error "$_make_defaults is not readable"
+ done
+# [ ! -r "$(get_make_defaults)" ] && error "$(get_make_defaults) is not readable"
+ [ "${MODE}" == "modify" -a ! -w "${MAKE_CONF_PATH}" ] && error ""${MAKE_CONF_PATH}" is not writable"
+}
+
+showhelp() {
+cat << HELP
+${PROGRAM_NAME} (${PROGRAM_VERSION})
+
+Syntax: ${PROGRAM_NAME} <option> [suboptions] [useflaglist]
+
+Options: -h, --help - show this message
+ -V, --version - show version information
+ -i, --info - show descriptions for the given useflags
+ -I, --info-installed - show descriptions for the given useflags and
+ their current impact on the installed system
+ -g, --global - show only global use flags (suboption)
+ -l, --local - show only local use flags (suboption)
+ -a, --active - show currently active useflags and their origin
+ -E, --enable - enable the given useflags
+ -D, --disable - disable the given useflags
+ -P, --prune - remove all references to the given flags from
+ make.conf to revert to default settings
+
+Notes: ${PROGRAM_NAME} currently only works for global flags defined
+ in make.globals, make.defaults or make.conf, it doesn't handle
+ use.defaults, use.mask or package.use yet (see portage(5) for details on
+ these files). It also might have issues with cascaded profiles.
+ If multiple options are specified only the last one will be used.
+HELP
+}
+
+showversion() {
+cat << VER
+${PROGRAM_NAME} (${PROGRAM_VERSION})
+Written by Marius Mauch
+
+Copyright (C) 2004-2009 Gentoo Foundation, Inc.
+This is free software; see the source for copying conditions.
+VER
+}
+
+# remove duplicate flags from the given list in both positive and negative forms
+# (but unlike portage always keep the last value even if it's negative)
+# Otherwise the status flags could be incorrect if a flag appers multiple times in
+# one location (like make.conf).
+# Using python here as bash sucks for list handling.
+# NOTE: bash isn't actually that bad at handling lists -- sh is. This may be
+# worth another look to avoid calling python unnecessariy. Or we could
+# just write the whole thing in python. ;)
+reduce_incrementals() {
+ echo $@ | python -c "import sys
+r=[]
+for x in sys.stdin.read().split():
+ if x[0] == '-' and x[1:] in r:
+ r.remove(x[1:])
+ r.append(x)
+ elif x[0] != '-' and '-'+x in r:
+ r.remove('-'+x)
+ r.append(x)
+ elif x == '-*':
+ r = []
+ r.append(x)
+ elif x not in r:
+ r.append(x)
+print ' '.join(r)"
+}
+
+# the following function creates a bash array ACTIVE_FLAGS that contains the
+# global use flags, indexed by origin: 0: environment, 1: make.conf,
+# 2: make.defaults, 3: make.globals
+get_useflags() {
+ # only calculate once as calling emerge is painfully slow
+ [ -n "${USE_FLAGS_CALCULATED}" ] && return
+
+ # backup portdir so get_portdir() doesn't give false results later
+ portdir_backup="${PORTDIR}"
+
+ ACTIVE_FLAGS[0]="$(reduce_incrementals ${USE})"
+ USE=""
+ source "${MAKE_CONF_PATH}"
+ ACTIVE_FLAGS[1]="$(reduce_incrementals ${USE})"
+ USE=""
+ for x in $(get_all_make_defaults); do
+ source "${x}"
+ ACTIVE_FLAGS[2]="$(reduce_incrementals ${ACTIVE_FLAGS[2]} ${USE})"
+ done
+ USE=""
+ source "${MAKE_GLOBALS_PATH}"
+ ACTIVE_FLAGS[3]="$(reduce_incrementals ${USE})"
+
+ # restore saved env variables
+ USE="${ACTIVE_FLAGS[0]}"
+ PORTDIR="${portdir_backup}"
+
+ # get the currently active USE flags as seen by portage, this has to be after
+ # restoring USE or portage won't see the original environment
+ ACTIVE_FLAGS[9]="$(emerge --info | grep 'USE=' | cut -b 5- | sed -e 's:"::g')" #'
+ USE_FLAGS_CALCULATED=1
+}
+
+# get the list of all known USE flags by reading use.desc and/or use.local.desc
+# (depending on the value of $SCOPE)
+get_useflaglist() {
+ local descdir
+
+ descdir="$(get_portdir)/profiles"
+
+ if [ -z "${SCOPE}" -o "${SCOPE}" == "global" ]; then
+ egrep "^[^# ]+ +-" "${descdir}/use.desc" | cut -d\ -f 1
+ fi
+ if [ -z "${SCOPE}" -o "${SCOPE}" == "local" ]; then
+ egrep "^[^# :]+:[^ ]+ +-" "${descdir}/use.local.desc" | cut -d: -f 2 | cut -d\ -f 1
+ fi
+}
+
+# get all make.defaults by traversing the cascaded profile directories
+get_all_make_defaults() {
+ local curdir
+ local parent
+ local rvalue
+
+ curdir="${1:-$(get_real_path ${MAKE_PROFILE_PATH})}"
+
+ [ -f "${curdir}/make.defaults" ] && rvalue="${curdir}/make.defaults ${rvalue}"
+ if [ -f "${curdir}/parent" ]; then
+ for parent in $(egrep -v '(^#|^ *$)' ${curdir}/parent); do
+ pdir="$(get_real_path ${curdir}/${parent})"
+ rvalue="$(get_all_make_defaults ${pdir}) ${rvalue}"
+ done
+ fi
+
+ echo "${rvalue}"
+}
+
+# get the path to make.defaults by traversing the cascaded profile directories
+get_make_defaults() {
+ local curdir
+ local parent
+
+ curdir="${1:-$(get_real_path ${MAKE_PROFILE_PATH})}"
+
+ if [ ! -f "${curdir}/make.defaults" -a -f "${curdir}/parent" ]; then
+ for parent in $(egrep -v '(^#|^ *$)' ${curdir}/parent); do
+ if [ -f "$(get_make_defaults ${curdir}/${parent})" ]; then
+ curdir="${curdir}/${parent}"
+ break
+ fi
+ done
+ fi
+
+ echo "${curdir}/make.defaults"
+}
+
+# little helper function to get the status of a given flag in one of the
+# ACTIVE_FLAGS elements. Arguments are 1: flag to test, 2: index of ACTIVE_FLAGS,
+# 3: echo value for positive (and as lowercase for negative) test result,
+# 4 (optional): echo value for "missing" test result, defaults to blank
+get_flagstatus_helper() {
+ if echo " ${ACTIVE_FLAGS[${2}]} " | grep " ${1} " > /dev/null; then
+ echo -n "${3}"
+ elif echo " ${ACTIVE_FLAGS[${2}]} " | grep " -${1} " > /dev/null; then
+ echo -n "$(echo ${3} | tr [[:upper:]] [[:lower:]])"
+ else
+ echo -n "${4:- }"
+ fi
+}
+
+# prints a status string for the given flag, each column indicating the presence
+# for portage, in the environment, in make.conf, in make.defaults and in make.globals.
+# full positive value would be "[+ECDG]", full negative value would be [-ecdg],
+# full missing value would be "[- ]" (portage only sees present or not present)
+get_flagstatus() {
+ get_useflags
+
+ echo -n '['
+ get_flagstatus_helper "${1}" 9 "+" "-"
+ get_flagstatus_helper "${1}" 0 "E"
+ get_flagstatus_helper "${1}" 1 "C"
+ get_flagstatus_helper "${1}" 2 "D"
+ get_flagstatus_helper "${1}" 3 "G"
+ echo -n '] '
+}
+
+# faster replacement to `portageq portdir`
+get_portdir() {
+ if [ -z "${PORTDIR}" ]; then
+ use_backup="${USE}"
+ source "${MAKE_GLOBALS_PATH}"
+ for x in $(get_all_make_defaults); do
+ source "${x}"
+ done
+ source "${MAKE_CONF_PATH}"
+ USE="${use_backup}"
+ fi
+ echo "${PORTDIR}"
+}
+
+# This function takes a list of use flags and shows the status and
+# the description for each one, honoring $SCOPE
+showdesc() {
+ local descdir
+ local current_desc
+ local found_one
+ local args
+
+ args="${*:-*}"
+
+ if [ -z "${SCOPE}" ]; then
+ SCOPE="global" showdesc ${args}
+ echo
+ SCOPE="local" showdesc ${args}
+ return
+ fi
+
+ descdir="$(get_portdir)/profiles"
+
+ [ "${SCOPE}" == "global" ] && echo "global use flags (searching: ${args})"
+ [ "${SCOPE}" == "local" ] && echo "local use flags (searching: ${args})"
+ echo "************************************************************"
+
+ if [ "${args}" == "*" ]; then
+ args="$(get_useflaglist | sort -u)"
+ fi
+
+ set ${args}
+
+ foundone=0
+ while [ -n "${1}" ]; do
+ if [ "${SCOPE}" == "global" ]; then
+ if grep "^${1} *-" "${descdir}/use.desc" > /dev/null; then
+ get_flagstatus "${1}"
+ foundone=1
+ fi
+ grep "^${1} *-" "${descdir}/use.desc"
+ fi
+ # local flags are a bit more complicated as there can be multiple
+ # entries per flag and we can't pipe into printf
+ if [ "${SCOPE}" == "local" ]; then
+ if grep ":${1} *-" "${descdir}/use.local.desc" > /dev/null; then
+ foundone=1
+ fi
+ grep ":${1} *-" "${descdir}/use.local.desc" \
+ | sed -e "s/^\([^:]\+\):\(${1}\) *- *\(.\+\)/\1|\2|\3/g" \
+ | while read line; do
+ pkg="$(echo $line | cut -d\| -f 1)"
+ flag="$(echo $line | cut -d\| -f 2)"
+ desc="$(echo $line | cut -d\| -f 3)"
+ get_flagstatus "${flag}"
+ printf "%s (%s):\n%s\n\n" "${flag}" "${pkg}" "${desc}"
+ done
+ fi
+ shift
+ done
+
+ if [ ${foundone} == 0 ]; then
+ echo "no matching entries found"
+ fi
+}
+
+# Works like showdesc() but displays only descriptions of which the appropriate
+# ebuild is installed and prints the name of those packages.
+showinstdesc() {
+ local descdir
+ local current_desc
+ local args
+ local -i foundone=0
+ local IFS
+
+ args=("${@:-*}")
+
+ case "${SCOPE}" in
+ "global") echo "global use flags (searching: ${args})";;
+ "local") echo "local use flags (searching: ${args})";;
+ *) SCOPE="global" showinstdesc "${args[@]}"
+ echo
+ SCOPE="local" showinstdesc "${args[@]}"
+ return;;
+ esac
+
+ descdir="$(get_portdir)/profiles"
+ echo "************************************************************"
+
+ if [ "${args}" = "*" ]; then
+ args="$(get_useflaglist | sort -u)"
+ fi
+
+ set "${args[@]}"
+
+ while [ -n "${1}" ]; do
+ case "${SCOPE}" in
+ "global")
+ if desc=$(grep "^${1} *-" "${descdir}/use.desc"); then
+ get_flagstatus "${1}"
+ echo "$desc"
+ # get list of installed packages matching this USE flag.
+ IFS=$'\n'
+ packages=($(equery -q -C hasuse -i "${1}" | awk '{ print $(NF-1) }'))
+ foundone+=${#packages[@]}
+ printf "\nInstalled packages matching this USE flag: "
+ if [ ${foundone} -gt 0 ]; then
+ echo $'\n'"${packages[*]}"
+ else
+ echo "none"
+ fi
+ fi
+ ;;
+ "local")
+ # local flags are a bit more complicated as there can be multiple
+ # entries per flag and we can't pipe into printf
+ IFS=': ' # Use a space instead of a dash because dashes occur in cat/pkg
+ while read pkg flag desc; do
+ # print name only if package is installed
+ # NOTE: If we implement bug #114086 's enhancement we can just use the
+ # exit status of equery instead of a subshell and pipe to wc -l
+ if [ $(equery -q -C list -i -e "${pkg}" | wc -l) -gt 0 ]; then
+ foundone=1
+ get_flagstatus "${flag}"
+ printf "%s (%s):\n%s\n\n" "${flag}" "${pkg}" "${desc#- }"
+ fi
+ done < <(grep ":${1} *-" "${descdir}/use.local.desc")
+ ;;
+ esac
+ shift
+ done
+
+ if [ ${foundone} -lt 1 ]; then
+ echo "no matching entries found"
+ fi
+}
+
+# show a list of all currently active flags and where they are activated
+showflags() {
+ local args
+
+ get_useflags
+
+ args="${*:-*}"
+
+ if [ "${args}" == "*" ]; then
+ args="$(get_useflaglist | sort -u)"
+ fi
+
+ set ${args}
+
+ while [ -n "${1}" ]; do
+ if echo " ${ACTIVE_FLAGS[9]} " | grep " ${1} " > /dev/null; then
+ printf "%-20s" ${1}
+ get_flagstatus ${1}
+ echo
+ fi
+ shift
+ done
+}
+
+# two small helpers to add or remove a flag from a USE string
+add_flag() {
+ NEW_MAKE_CONF_USE="${NEW_MAKE_CONF_USE} ${1}"
+}
+
+remove_flag() {
+ NEW_MAKE_CONF_USE="${NEW_MAKE_CONF_USE// ${1} / }"
+}
+
+# USE flag modification function. Mainly a loop with calls to add_flag and
+# remove_flag to create a new USE string which is then inserted into make.conf.
+modify() {
+ if [ -z "${*}" ]; then
+ if [ "${ACTION}" != "prune" ]; then
+ echo "WARNING: no USE flags listed for modification, do you really"
+ echo " want to ${ACTION} *all* known USE flags?"
+ echo " If you don't please press Ctrl-C NOW!!!"
+ sleep 5
+ set $(get_useflaglist | sort -u)
+ fi
+ fi
+
+ get_useflags
+
+ NEW_MAKE_CONF_USE=" ${ACTIVE_FLAGS[1]} "
+
+ while [ -n "${1}" ]; do
+ if [ "${ACTION}" == "add" ]; then
+ if echo " ${NEW_MAKE_CONF_USE} " | grep " ${1} " > /dev/null; then
+ shift
+ elif echo " ${NEW_MAKE_CONF_USE} " | grep " -${1} " > /dev/null; then
+ remove_flag "-${1}"
+ else
+ add_flag "${1}"
+ shift
+ fi
+ elif [ "${ACTION}" == "remove" ]; then
+ if echo " ${NEW_MAKE_CONF_USE} " | grep " -${1} " > /dev/null; then
+ shift
+ elif echo " ${NEW_MAKE_CONF_USE} " | grep " ${1} " > /dev/null; then
+ remove_flag "${1}"
+ else
+ add_flag "-${1}"
+ shift
+ fi
+ elif [ "${ACTION}" == "prune" ]; then
+ if echo " ${NEW_MAKE_CONF_USE} " | grep " ${1} " > /dev/null; then
+ remove_flag "${1}"
+ elif echo " ${NEW_MAKE_CONF_USE} " | grep " -${1} " > /dev/null; then
+ remove_flag "-${1}"
+ fi
+ shift
+ fi
+ done
+
+ #echo "old flags:"
+ #echo ${ACTIVE_FLAGS[1]}
+ #echo
+ #echo "new flags:"
+ #echo ${NEW_MAKE_CONF_USE}
+
+ # a little loop to add linebreaks so we don't end with one ultra-long line
+ NEW_MAKE_CONF_USE_2=""
+ for x in ${NEW_MAKE_CONF_USE}; do
+ if [ $(((${#NEW_MAKE_CONF_USE_2}%70)+${#x}+2)) -gt 70 ]; then
+ NEW_MAKE_CONF_USE_2="${NEW_MAKE_CONF_USE_2}\\ \\n $x "
+ else
+ NEW_MAKE_CONF_USE_2="${NEW_MAKE_CONF_USE_2}${x} "
+ fi
+ done
+
+ # make a backup just in case the user doesn't like the new make.conf
+ cp -p "${MAKE_CONF_PATH}" "${MAKE_CONF_BACKUP_PATH}"
+
+ # as sed doesn't really work with multi-line patterns we have to replace USE
+ # on our own here. Basically just skip everything between USE=" and the
+ # closing ", printing our new USE line there instead.
+ inuse=0
+ had_use=0
+ x=0
+ (while [ "$x" -eq "0" ]; do
+ read -r line
+ x="$?"
+ [ "${line:0:4}" == "USE=" ] && inuse=1
+ [ "${inuse}" == "0" ] && echo -E "${line}"
+ if [ "${inuse}" == "1" ] && echo "${line}" | egrep '" *(#.*)?$' > /dev/null; then
+ echo -n 'USE="'
+ echo -ne "${NEW_MAKE_CONF_USE_2%% }"
+ echo '"'
+ inuse=0
+ had_use=1
+ fi
+ done
+ if [ ${had_use} -eq 0 ]; then
+ echo -n 'USE="'
+ echo -ne "${NEW_MAKE_CONF_USE_2%% }"
+ echo '"'
+ fi ) < "${MAKE_CONF_BACKUP_PATH}" | sed -e 's:\\ $:\\:' > "${MAKE_CONF_PATH}"
+
+ echo "${MAKE_CONF_PATH} was modified, a backup copy has been placed at ${MAKE_CONF_BACKUP_PATH}"
+}
+
+##### main program comes now #####
+
+# disable globbing as it fucks up with args=*
+set -f
+parse_arguments "$@"
+check_sanity
+
+eval ${MODE} ${ARGUMENTS}
+set +f
diff --git a/bin/glsa-check b/bin/glsa-check
new file mode 100644
index 0000000..1fb577d
--- /dev/null
+++ b/bin/glsa-check
@@ -0,0 +1,366 @@
+#!/usr/bin/python
+
+# $Header: $
+# This program is licensed under the GPL, version 2
+
+import os
+import sys
+try:
+ import portage
+except ImportError:
+ sys.path.insert(0, "/usr/lib/portage/pym")
+ import portage
+
+try:
+ from portage.output import *
+except ImportError:
+ from output import *
+
+from getopt import getopt,GetoptError
+
+__program__ = "glsa-check"
+__author__ = "Marius Mauch <genone@gentoo.org>"
+__version__ = open("/etc/gentoolkit-version").read().strip()
+
+optionmap = [
+["-l", "--list", "list all unapplied GLSA"],
+["-d", "--dump", "--print", "show all information about the given GLSA"],
+["-t", "--test", "test if this system is affected by the given GLSA"],
+["-p", "--pretend", "show the necessary commands to apply this GLSA"],
+["-f", "--fix", "try to auto-apply this GLSA (experimental)"],
+["-i", "--inject", "inject the given GLSA into the checkfile"],
+["-n", "--nocolor", "disable colors (option)"],
+["-e", "--emergelike", "do not use a least-change algorithm (option)"],
+["-h", "--help", "show this help message"],
+["-V", "--version", "some information about this tool"],
+["-v", "--verbose", "print more information (option)"],
+["-c", "--cve", "show CAN ids in listing mode (option)"],
+["-m", "--mail", "send a mail with the given GLSAs to the administrator"]
+]
+
+# print a warning as this is beta code (but proven by now, so no more warning)
+#sys.stderr.write("WARNING: This tool is completely new and not very tested, so it should not be\n")
+#sys.stderr.write("used on production systems. It's mainly a test tool for the new GLSA release\n")
+#sys.stderr.write("and distribution system, it's functionality will later be merged into emerge\n")
+#sys.stderr.write("and equery.\n")
+#sys.stderr.write("Please read http://www.gentoo.org/proj/en/portage/glsa-integration.xml\n")
+#sys.stderr.write("before using this tool AND before reporting a bug.\n\n")
+
+# option parsing
+args = []
+params = []
+try:
+ args, params = getopt(sys.argv[1:], "".join([o[0][1] for o in optionmap]), \
+ [x[2:] for x in reduce(lambda x,y: x+y, [z[1:-1] for z in optionmap])])
+# ["dump", "print", "list", "pretend", "fix", "inject", "help", "verbose", "version", "test", "nocolor", "cve", "mail"])
+ args = [a for a,b in args]
+
+ for option in ["--nocolor", "-n"]:
+ if option in args:
+ nocolor()
+ args.remove(option)
+
+ verbose = False
+ for option in ["--verbose", "-v"]:
+ if option in args:
+ verbose = True
+ args.remove(option)
+
+ list_cve = False
+ for option in ["--cve", "-c"]:
+ if option in args:
+ list_cve = True
+ args.remove(option)
+
+ least_change = True
+ for option in ["--emergelike", "-e"]:
+ if option in args:
+ least_change = False
+ args.remove(option)
+
+ # sanity checking
+ if len(args) <= 0:
+ sys.stderr.write("no option given: what should I do ?\n")
+ mode="help"
+ elif len(args) > 1:
+ sys.stderr.write("please use only one command per call\n")
+ mode = "help"
+ else:
+ # in what mode are we ?
+ args = args[0]
+ for m in optionmap:
+ if args in [o for o in m[:-1]]:
+ mode = m[1][2:]
+
+except GetoptError, e:
+ sys.stderr.write("unknown option given: ")
+ sys.stderr.write(str(e)+"\n")
+ mode = "help"
+
+# we need a set of glsa for most operation modes
+if len(params) <= 0 and mode in ["fix", "test", "pretend", "dump", "inject", "mail"]:
+ sys.stderr.write("\nno GLSA given, so we'll do nothing for now. \n")
+ sys.stderr.write("If you want to run on all GLSA please tell me so \n")
+ sys.stderr.write("(specify \"all\" as parameter)\n\n")
+ mode = "help"
+elif len(params) <= 0 and mode == "list":
+ params.append("new")
+
+# show help message
+if mode == "help":
+ sys.stderr.write("\nSyntax: glsa-check <option> [glsa-list]\n\n")
+ for m in optionmap:
+ sys.stderr.write(m[0] + "\t" + m[1] + " \t: " + m[-1] + "\n")
+ for o in m[2:-1]:
+ sys.stderr.write("\t" + o + "\n")
+ sys.stderr.write("\nglsa-list can contain an arbitrary number of GLSA ids, \n")
+ sys.stderr.write("filenames containing GLSAs or the special identifiers \n")
+ sys.stderr.write("'all', 'new' and 'affected'\n")
+ sys.exit(1)
+
+# we need root priviledges for write access
+if mode in ["fix", "inject"] and os.geteuid() != 0:
+ sys.stderr.write("\nThis tool needs root access to "+mode+" this GLSA\n\n")
+ sys.exit(2)
+
+# show version and copyright information
+if mode == "version":
+ sys.stderr.write("%(program)s (%(version)s)\n" % {
+ "program": __program__,
+ "version": __version__
+ })
+ sys.stderr.write("Author: %s\n" % __author__)
+ sys.stderr.write("This program is licensed under the GPL, version 2\n")
+ sys.exit(0)
+
+# delay this for speed increase
+from gentoolkit.glsa import *
+
+glsaconfig = checkconfig(portage.config(clone=portage.settings))
+
+vardb = portage.db["/"]["vartree"].dbapi
+portdb = portage.db["/"]["porttree"].dbapi
+
+# Check that we really have a glsa dir to work on
+if not (os.path.exists(glsaconfig["GLSA_DIR"]) and os.path.isdir(glsaconfig["GLSA_DIR"])):
+ sys.stderr.write(red("ERROR")+": GLSA_DIR %s doesn't exist. Please fix this.\n" % glsaconfig["GLSA_DIR"])
+ sys.exit(1)
+
+# build glsa lists
+completelist = get_glsa_list(glsaconfig["GLSA_DIR"], glsaconfig)
+
+if os.access(glsaconfig["CHECKFILE"], os.R_OK):
+ checklist = [line.strip() for line in open(glsaconfig["CHECKFILE"], "r").readlines()]
+else:
+ checklist = []
+todolist = [e for e in completelist if e not in checklist]
+
+glsalist = []
+if "new" in params:
+ glsalist = todolist
+ params.remove("new")
+
+if "all" in params:
+ glsalist = completelist
+ params.remove("all")
+if "affected" in params:
+ # replaced completelist with todolist on request of wschlich
+ for x in todolist:
+ try:
+ myglsa = Glsa(x, glsaconfig)
+ except (GlsaTypeException, GlsaFormatException), e:
+ if verbose:
+ sys.stderr.write(("invalid GLSA: %s (error message was: %s)\n" % (x, e)))
+ continue
+ if myglsa.isVulnerable():
+ glsalist.append(x)
+ params.remove("affected")
+
+# remove invalid parameters
+for p in params[:]:
+ if not (p in completelist or os.path.exists(p)):
+ sys.stderr.write(("(removing %s from parameter list as it isn't a valid GLSA specification)\n" % p))
+ params.remove(p)
+
+glsalist.extend([g for g in params if g not in glsalist])
+
+def summarylist(myglsalist, fd1=sys.stdout, fd2=sys.stderr):
+ fd2.write(white("[A]")+" means this GLSA was already applied,\n")
+ fd2.write(green("[U]")+" means the system is not affected and\n")
+ fd2.write(red("[N]")+" indicates that the system might be affected.\n\n")
+
+ for myid in myglsalist:
+ try:
+ myglsa = Glsa(myid, glsaconfig)
+ except (GlsaTypeException, GlsaFormatException), e:
+ if verbose:
+ fd2.write(("invalid GLSA: %s (error message was: %s)\n" % (myid, e)))
+ continue
+ if myglsa.isApplied():
+ status = "[A]"
+ color = white
+ elif myglsa.isVulnerable():
+ status = "[N]"
+ color = red
+ else:
+ status = "[U]"
+ color = green
+
+ if verbose:
+ access = ("[%-8s] " % myglsa.access)
+ else:
+ access=""
+
+ fd1.write(color(myglsa.nr) + " " + color(status) + " " + color(access) + myglsa.title + " (")
+ if not verbose:
+ for pkg in myglsa.packages.keys()[:3]:
+ fd1.write(" " + pkg + " ")
+ if len(myglsa.packages) > 3:
+ fd1.write("... ")
+ else:
+ for pkg in myglsa.packages.keys():
+ mylist = vardb.match(portage.dep_getkey(pkg))
+ if len(mylist) > 0:
+ pkg = color(" ".join(mylist))
+ fd1.write(" " + pkg + " ")
+
+ fd1.write(")")
+ if list_cve:
+ fd1.write(" "+(",".join([r[:13] for r in myglsa.references if r[:4] in ["CAN-", "CVE-"]])))
+ fd1.write("\n")
+ return 0
+
+if mode == "list":
+ sys.exit(summarylist(glsalist))
+
+# dump, fix, inject and fix are nearly the same code, only the glsa method call differs
+if mode in ["dump", "fix", "inject", "pretend"]:
+ for myid in glsalist:
+ try:
+ myglsa = Glsa(myid, glsaconfig)
+ except (GlsaTypeException, GlsaFormatException), e:
+ if verbose:
+ sys.stderr.write(("invalid GLSA: %s (error message was: %s)\n" % (myid, e)))
+ continue
+ if mode == "dump":
+ myglsa.dump()
+ elif mode == "fix":
+ sys.stdout.write("fixing "+myid+"\n")
+ mergelist = myglsa.getMergeList(least_change=least_change)
+ for pkg in mergelist:
+ sys.stdout.write(">>> merging "+pkg+"\n")
+ # using emerge for the actual merging as it contains the dependency
+ # code and we want to be consistent in behaviour. Also this functionality
+ # will be integrated in emerge later, so it shouldn't hurt much.
+ emergecmd = "emerge --oneshot " + glsaconfig["EMERGE_OPTS"] + " =" + pkg
+ if verbose:
+ sys.stderr.write(emergecmd+"\n")
+ exitcode = os.system(emergecmd)
+ # system() returns the exitcode in the high byte of a 16bit integer
+ if exitcode >= 1<<8:
+ exitcode >>= 8
+ if exitcode:
+ sys.exit(exitcode)
+ myglsa.inject()
+ elif mode == "pretend":
+ sys.stdout.write("Checking GLSA "+myid+"\n")
+ mergelist = myglsa.getMergeList(least_change=least_change)
+ if mergelist:
+ sys.stdout.write("The following updates will be performed for this GLSA:\n")
+ for pkg in mergelist:
+ oldver = None
+ for x in vardb.match(portage.dep_getkey(pkg)):
+ if vardb.aux_get(x, ["SLOT"]) == portdb.aux_get(pkg, ["SLOT"]):
+ oldver = x
+ if oldver == None:
+ raise ValueError("could not find old version for package %s" % pkg)
+ oldver = oldver[len(portage.dep_getkey(oldver))+1:]
+ sys.stdout.write(" " + pkg + " (" + oldver + ")\n")
+ else:
+ sys.stdout.write("Nothing to do for this GLSA\n")
+ elif mode == "inject":
+ sys.stdout.write("injecting " + myid + "\n")
+ myglsa.inject()
+ sys.stdout.write("\n")
+ sys.exit(0)
+
+# test is a bit different as Glsa.test() produces no output
+if mode == "test":
+ outputlist = []
+ for myid in glsalist:
+ try:
+ myglsa = Glsa(myid, glsaconfig)
+ except (GlsaTypeException, GlsaFormatException), e:
+ if verbose:
+ sys.stderr.write(("invalid GLSA: %s (error message was: %s)\n" % (myid, e)))
+ continue
+ if myglsa.isVulnerable():
+ if verbose:
+ outputlist.append(str(myglsa.nr)+" ( "+myglsa.title+" ) ")
+ else:
+ outputlist.append(str(myglsa.nr))
+ if len(outputlist) > 0:
+ sys.stderr.write("This system is affected by the following GLSAs:\n")
+ sys.stdout.write("\n".join(outputlist)+"\n")
+ else:
+ sys.stderr.write("This system is not affected by any of the listed GLSAs\n")
+ sys.exit(0)
+
+# mail mode as requested by solar
+if mode == "mail":
+ try:
+ import portage.mail as portage_mail
+ except ImportError:
+ import portage_mail
+
+ import socket
+ from StringIO import StringIO
+ try:
+ from email.mime.text import MIMEText
+ except ImportError:
+ from email.MIMEText import MIMEText
+
+ # color doesn't make any sense for mail
+ nocolor()
+
+ if glsaconfig.has_key("PORTAGE_ELOG_MAILURI"):
+ myrecipient = glsaconfig["PORTAGE_ELOG_MAILURI"].split()[0]
+ else:
+ myrecipient = "root@localhost"
+
+ if glsaconfig.has_key("PORTAGE_ELOG_MAILFROM"):
+ myfrom = glsaconfig["PORTAGE_ELOG_MAILFROM"]
+ else:
+ myfrom = "glsa-check"
+
+ mysubject = "[glsa-check] Summary for %s" % socket.getfqdn()
+
+ # need a file object for summarylist()
+ myfd = StringIO()
+ myfd.write("GLSA Summary report for host %s\n" % socket.getfqdn())
+ myfd.write("(Command was: %s)\n\n" % " ".join(sys.argv))
+ summarylist(glsalist, fd1=myfd, fd2=myfd)
+ summary = str(myfd.getvalue())
+ myfd.close()
+
+ myattachments = []
+ for myid in glsalist:
+ try:
+ myglsa = Glsa(myid, glsaconfig)
+ except (GlsaTypeException, GlsaFormatException), e:
+ if verbose:
+ sys.stderr.write(("invalid GLSA: %s (error message was: %s)\n" % (myid, e)))
+ continue
+ myfd = StringIO()
+ myglsa.dump(outstream=myfd)
+ myattachments.append(MIMEText(str(myfd.getvalue()), _charset="utf8"))
+ myfd.close()
+
+ mymessage = portage_mail.create_message(myfrom, myrecipient, mysubject, summary, myattachments)
+ portage_mail.send_mail(glsaconfig, mymessage)
+
+ sys.exit(0)
+
+# something wrong here, all valid paths are covered with sys.exit()
+sys.stderr.write("nothing more to do\n")
+sys.exit(2)
diff --git a/bin/revdep-rebuild b/bin/revdep-rebuild
new file mode 100755
index 0000000..fac03d8
--- /dev/null
+++ b/bin/revdep-rebuild
@@ -0,0 +1,1094 @@
+#!/bin/bash
+# Copyright 1999-2008 Gentoo Foundation
+
+# revdep-rebuild: Reverse dependency rebuilder.
+# Original Author: Stanislav Brabec
+# Rewrite Author: Michael A. Smith
+# Current Maintainer: Paul Varner <fuzzyray@gentoo.org>
+
+# TODO:
+# - Use more /etc/init.d/functions.sh
+# - Try to reduce the number of global vars
+
+##
+# Global Variables:
+
+# Must-be-blank:
+unset GREP_OPTIONS
+
+# Readonly variables:
+declare -r APP_NAME="${0##*/}" # The name of this application
+declare -r OIFS="$IFS" # Save the IFS
+declare -r ENV_FILE=0_env.rr # Contains environment variables
+declare -r FILES_FILE=1_files.rr # Contains a list of files to search
+declare -r LDPATH_FILE=2_ldpath.rr # Contains the LDPATH
+declare -r BROKEN_FILE=3_broken.rr # Contains the list of broken files
+declare -r ERRORS_FILE=3_errors.rr # Contains the ldd error output
+declare -r RAW_FILE=4_raw.rr # Contains the raw list of packages
+declare -r OWNERS_FILE=4_owners.rr # Contains the file owners
+declare -r PKGS_FILE=4_pkgs.rr # Contains the unsorted bare package names
+declare -r EBUILDS_FILE=4_ebuilds.rr # Contains the unsorted atoms
+ # (Appropriately slotted or versioned)
+declare -r ORDER_FILE=5_order.rr # Contains the sorted atoms
+declare -r STATUS_FILE=6_status.rr # Contains the ldd error output
+declare -ra FILES=(
+ "$ENV_FILE"
+ "$FILES_FILE"
+ "$LDPATH_FILE"
+ "$BROKEN_FILE"
+ "$ERRORS_FILE"
+ "$RAW_FILE"
+ "$OWNERS_FILE"
+ "$PKGS_FILE"
+ "$EBUILDS_FILE"
+ "$ORDER_FILE"
+ "$STATUS_FILE"
+)
+
+# "Boolean" variables: Considered "true" if it has any value at all
+# "True" indicates we should...
+declare FULL_LD_PATH # ...search across the COMPLETE_LD_LIBRARY_PATH
+declare KEEP_TEMP # ...not delete tempfiles from the current run
+declare ORDER_PKGS # ...sort the atoms in deep dependency order
+declare PACKAGE_NAMES # ...emerge by slot, not by versionated atom
+declare RM_OLD_TEMPFILES # ...remove tempfiles from prior runs
+declare SEARCH_BROKEN # ...search for broken libraries and binaries
+declare VERBOSE # ...give verbose output
+
+# Globals that impact portage directly:
+declare EMERGE_DEFAULT_OPTS # String of options portage assumes to be set
+declare EMERGE_OPTIONS # Array of options to pass to portage
+declare PORTAGE_NICENESS # Renice to this value
+declare PORTAGE_ROOT # The root path for portage
+
+# Customizable incremental variables:
+# These variables can be prepended to either by setting the variable in
+# your environment prior to execution, or by placing an entry in
+# /etc/make.conf.
+#
+# An entry of "-*" means to clear the variable from that point forward.
+# Example: env SEARCH_DIRS="/usr/bin -*" revdep-rebuild will set SEARCH_DIRS
+# to contain only /usr/bin
+declare LD_LIBRARY_MASK # Mask of specially evaluated libraries
+declare SEARCH_DIRS # List of dirs to search for executables and libraries
+declare SEARCH_DIRS_MASK # List of dirs not to search
+
+# Other globals:
+declare OLDPROG # Previous pass through the progress meter
+declare EXACT_PKG # Versionated atom to emerge
+declare HEAD_TEXT # Feedback string about the search
+declare NOCOLOR # Set to "true" not to output term colors
+declare OK_TEXT # Feedback about a search which found no errors
+declare RC_NOCOLOR # Hack to insure we respect NOCOLOR
+declare REBUILD_LIST # Array of atoms to emerge
+declare SKIP_LIST # Array of atoms that cannot be emerged (masked?)
+declare SONAME # Soname/soname path pattern given on commandline
+declare SONAME_SEARCH # Value of SONAME modified to match ldd's output
+declare WORKING_TEXT # Feedback about the search
+declare WORKING_DIR # Working directory where cache files are kept
+
+main() {
+ # preliminary setup
+ get_opts "$@"
+ setup_portage
+ setup_search_paths_and_masks
+ get_search_env
+ echo
+
+ # Search for broken binaries
+ get_files
+ get_ldpath
+ main_checks
+
+ # Associate broken binaries with packages to rebuild
+ if [[ $PACKAGE_NAMES ]]; then
+ get_packages
+ clean_packages
+ assign_packages_to_ebuilds
+ else
+ get_exact_ebuilds
+ fi
+
+ # Rebuild packages owning broken binaries
+ get_build_order
+ rebuild
+
+ # All done
+ cleanup
+}
+##
+# Refuse to delete anything before we cd to our tmpdir
+# (See mkdir_and_cd_to_tmpdir()
+rm() {
+ eerror "I was instructed to rm '$@'"
+ die 1 "Refusing to delete anything before changing to temporary directory."
+}
+##
+# GNU find has -executable, but if our users' finds do not have that flag
+# we emulate it with this function. Also emulates -writable and -readable.
+# Usage: find PATH ARGS -- use find like normal, except use -executable instead
+# of various versions of -perm /+ blah blah and hacks
+find() {
+ hash find || { die 1 'find not found!'; }
+ # We can be pretty sure find itself should be executable.
+ local testsubject="$(type -P find)"
+ if [[ $(command find "$testsubject" -executable 2> /dev/null) ]]; then
+ unset -f find # We can just use the command find
+ elif [[ $(command find "$testsubject" -perm /u+x 2> /dev/null) ]]; then
+ find() {
+ a=(${@//-executable/-perm \/u+x})
+ a=(${a[@]//-writable/-perm \/u+w})
+ a=(${a[@]//-readable/-perm \/r+w})
+ command find "${a[@]}"
+ }
+ elif [[ $(command find "$testsubject" -perm +u+x 2> /dev/null) ]]; then
+ find() {
+ a=(${@//-executable/-perm +u+x})
+ a=(${a[@]//-writable/-perm +u+w})
+ a=(${a[@]//-readable/-perm +r+w})
+ command find "${a[@]}"
+ }
+ else # Last resort
+ find() {
+ a=(${@//-executable/-exec test -x '{}' \; -print})
+ a=(${a[@]//-writable/-exec test -w '{}' \; -print})
+ a=(${a[@]//-readable/-exec test -r '{}' \; -print})
+ command find "${a[@]}"
+ }
+ fi
+ find "$@"
+}
+
+print_usage() {
+cat << EOF
+Usage: $APP_NAME [OPTIONS] [--] [EMERGE_OPTIONS]
+
+Broken reverse dependency rebuilder.
+
+ -C, --nocolor Turn off colored output
+ -d, --debug Print way too much information (uses bash's set -xv)
+ -e, --exact Emerge based on exact package version
+ -h, --help Print this usage
+ -i, --ignore Ignore temporary files from previous runs
+ -k, --keep-temp Do not delete temporary files on exit
+ -L, --library NAME Emerge existing packages that use the library with NAME
+ --library=NAME NAME can be a full path to the library or a basic
+ regular expression (man grep)
+ -l, --no-ld-path Do not set LD_LIBRARY_PATH
+ -o, --no-order Do not check the build order
+ (Saves time, but may cause breakage.)
+ -p, --pretend Do a trial run without actually emerging anything
+ (also passed to emerge command)
+ -P, --no-progress Turn off the progress meter
+ -q, --quiet Be less verbose (also passed to emerge command)
+ -v, --verbose Be more verbose (also passed to emerge command)
+
+Calls emerge, options after -- are ignored by $APP_NAME
+and passed directly to emerge.
+
+Report bugs to <http://bugs.gentoo.org>
+EOF
+}
+##
+# Usage: progress i n
+# i: current item
+# n: total number of items to process
+progress() {
+ if [[ -t 1 ]]; then
+ progress() {
+ local curProg=$(( $1 * 100 / $2 ))
+ (( curProg == OLDPROG )) && return # no change, output nothing
+ OLDPROG="$curProg" # must be a global variable
+ (( $1 == $2 )) && local lb=$'\n'
+ echo -ne '\r \r'"[ $curProg% ] $lb"
+ }
+ progress $@
+ else # STDOUT is not a tty. Disable progress meter.
+ progress() { :; }
+ fi
+}
+##
+# Usage: countdown n
+# n: number of seconds to count
+countdown() {
+ local i
+ for ((i=1; i<$1; i++)); do
+ echo -ne '\a.'
+ ((i<$1)) && sleep 1
+ done
+ echo -e '\a.'
+}
+##
+# Replace whitespace with linebreaks, normalize repeated '/' chars, and sort -u
+# (If any libs have whitespace in their filenames, someone needs punishment.)
+clean_var() {
+ awk 'BEGIN {RS="[[:space:]]"}
+ /-\*/ {exit}
+ /[^[:space:]]/ {gsub(/\/\/+/, "/"); print}' | sort -u
+}
+##
+# Exit and optionally output to sterr
+die() {
+ local status=$1
+ shift
+ eerror "$@"
+ exit $status
+}
+##
+# What to do when dynamic linking is consistent
+clean_exit() {
+ if [[ ! $KEEP_TEMP ]]; then
+ rm -f "${FILES[@]}"
+ if [[ "$WORKING_DIR" != "/var/cache/${APP_NAME}" ]]; then
+ # Remove the working directory
+ builtin cd; rmdir "$WORKING_DIR"
+ fi
+ fi
+ echo
+ einfo "$OK_TEXT... All done. "
+ exit 0
+}
+##
+# Get the name of the package that owns a file or list of files given as args.
+get_file_owner() {
+ local IFS=$'\n'
+ # ${*/%/ } adds a space to the end of each object name to prevent false
+ # matches, for example /usr/bin/dia matching /usr/bin/dialog (bug #196460).
+ find -L /var/db/pkg -name CONTENTS -print0 |
+ xargs -0 grep -Fl "${*/%/ }" |
+ sed 's:/var/db/pkg/\(.*\)/CONTENTS:\1:'
+}
+##
+# Normalize some EMERGE_OPTIONS
+normalize_emerge_opts() {
+ # Normalize some EMERGE_OPTIONS
+ EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]/%-p/--pretend})
+ EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]/%-f/--fetchonly})
+ EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]/%-v/--verbose})
+}
+##
+# Use the color preference from portage
+setup_color() {
+ # This should still work if NOCOLOR is set by the -C flag or in the user's
+ # environment.
+ export NOCOLOR=$(portageq envvar NOCOLOR)
+ [[ $NOCOLOR = yes || $NOCOLOR = true ]] && export RC_NOCOLOR=yes # HACK! (grr)
+ . /etc/init.d/functions.sh
+}
+##
+# Die if an argument is missing.
+die_if_missing_arg() {
+ [[ ! $2 || $2 = -* ]] && die 1 "Missing expected argument to $1"
+}
+##
+# Die because an option is not recognized.
+die_invalid_option() {
+ # Can't use eerror and einfo because this gets called before function.sh
+ # is sourced
+ echo
+ echo "Encountered unrecognized option $1." >&2
+ echo
+ echo "$APP_NAME no longer automatically passes unrecognized options to portage."
+ echo "Separate emerge-only options from revdep-rebuild options with the -- flag."
+ echo
+ echo "For example, $APP_NAME -v -- --ask"
+ echo
+ echo "See the man page or $APP_NAME -h for more detail."
+ echo
+ exit 1
+}
+##
+# Warn about deprecated options.
+warn_deprecated_opt() {
+ # Can't use eerror and einfo because this gets called before function.sh
+ # is sourced
+ echo
+ echo "Encountered deprecated option $1." >&2
+ [[ $2 ]] && echo "Please use $2 instead." >&2
+}
+##
+# Get whole-word commandline options preceded by two dashes.
+get_longopts() {
+ case $1 in
+ --nocolor) export NOCOLOR="yes";;
+ --no-color) warn_deprecated_opt "$1" "--nocolor"
+ export NOCOLOR="yes";;
+ --debug) set -xv;;
+ --exact) unset PACKAGE_NAMES;;
+ --help) print_usage
+ exit 0;;
+ --ignore) RM_OLD_TEMPFILES=1;;
+ --keep-temp) KEEP_TEMP=1;;
+ --library=*) # TODO: check for invalid values
+ SONAME="${1#*=}"
+ unset SEARCH_BROKEN;;
+ --soname=*|--soname-regexp=*) # TODO: check for invalid values
+ warn_deprecated_opt "${1%=*}" "--library"
+ SONAME="${1#*=}"
+ unset SEARCH_BROKEN;;
+ --library) # TODO: check for invalid values
+ die_if_missing_arg $1 $2
+ shift
+ SONAME="$1"
+ unset SEARCH_BROKEN;;
+ --soname|--soname-regexp) # TODO: check for invalid values
+ warn_deprecated_opt "$1" "--library"
+ die_if_missing_arg $1 $2
+ shift
+ SONAME="$1"
+ unset SEARCH_BROKEN;;
+ --no-ld-path) unset FULL_LD_PATH;;
+ --no-order) unset ORDER_PKGS;;
+ --no-progress) progress() { :; };;
+ --pretend) EMERGE_OPTIONS+=("--pretend");;
+ --quiet) echo_v() { :; }
+ progress() { :; }
+ quiet=1
+ EMERGE_OPTIONS+=($1);;
+ --verbose) VERBOSE=1
+ EMERGE_OPTIONS+=("--verbose");;
+ --extra-verbose) warn_deprecated_opt "$1" "--verbose"
+ VERBOSE=1
+ EMERGE_OPTIONS+=("--verbose");;
+ --package-names) # No longer used, since it is the
+ # default. We accept it for
+ # backwards compatibility.
+ warn_deprecated_opt "$1"
+ PACKAGE_NAMES=1;;
+ *) die_invalid_option $1;;
+ esac
+}
+
+##
+# Get single-letter commandline options preceded by a single dash.
+get_shortopts() {
+ local OPT OPTSTRING OPTARG OPTIND
+ while getopts ":CdehikL:loPpqu:vX" OPT; do
+ case "$OPT" in
+ C) # TODO: Match syntax with the rest of gentoolkit
+ export NOCOLOR="yes";;
+ d) set -xv;;
+ e) unset PACKAGE_NAMES;;
+ h) print_usage
+ exit 0;;
+ i) RM_OLD_TEMPFILES=1;;
+ k) KEEP_TEMP=1;;
+ L) # TODO: Check for invalid values
+ SONAME="${OPTARG#*=}"
+ unset SEARCH_BROKEN;;
+ l) unset FULL_LD_PATH;;
+ o) unset ORDER_PKGS;;
+ P) progress() { :; };;
+ p) EMERGE_OPTIONS+=("--pretend");;
+ q) echo_v() { :; }
+ progress() { :; }
+ quiet=1
+ EMERGE_OPTIONS+=("--quiet");;
+ v) VERBOSE=1
+ EMERGE_OPTIONS+=("--verbose");;
+ X) # No longer used, since it is the default.
+ # We accept it for backwards compatibility.
+ warn_deprecated_opt "-X"
+ PACKAGE_NAMES=1;;
+ *) die_invalid_option "-$OPTARG";;
+ esac
+ done
+}
+##
+# Get command-line options.
+get_opts() {
+ local avoid_utils
+ local -a args
+ echo_v() { ewarn "$@"; }
+ unset VERBOSE KEEP_TEMP EMERGE_OPTIONS RM_OLD_TEMPFILES
+ ORDER_PKGS=1
+ PACKAGE_NAMES=1
+ SONAME="not found"
+ SEARCH_BROKEN=1
+ FULL_LD_PATH=1
+ while [[ $1 ]]; do
+ case $1 in
+ --) shift
+ EMERGE_OPTIONS+=("$@")
+ break;;
+ -*) while true; do
+ args+=("$1")
+ shift
+ [[ ${1:--} = -* ]] && break
+ done
+ if [[ ${args[0]} = --* ]]; then
+ get_longopts "${args[@]}"
+ else
+ get_shortopts "${args[@]}"
+ fi;;
+ *) die_invalid_option "$1";;
+ esac
+ unset args
+ done
+
+ setup_color
+ normalize_emerge_opts
+
+ # If the user is not super, add --pretend to EMERGE_OPTIONS
+ if [[ ${EMERGE_OPTIONS[@]} != *--pretend* && $UID -ne 0 ]]; then
+ ewarn "You are not superuser. Adding --pretend to emerge options."
+ EMERGE_OPTIONS+=(--pretend)
+ fi
+}
+##
+# Is there a --pretend or --fetchonly flag in the EMERGE_OPTIONS array?
+is_real_merge() {
+ [[ ${EMERGE_OPTIONS[@]} != *--pretend* &&
+ ${EMERGE_OPTIONS[@]} != *--fetchonly* ]]
+}
+##
+# Clean up temporary files and exit
+cleanup_and_die() {
+ rm -f "$@"
+ die 1 " ...terminated. Removing incomplete $@."
+}
+##
+# Clean trap
+clean_trap() {
+ trap "cleanup_and_die $*" SIGHUP SIGINT SIGQUIT SIGABRT SIGTERM
+ rm -f "$@"
+}
+##
+# Returns 0 if the first arg is found in the remaining args, 1 otherwise
+# (Returns 2 if given fewer than 2 arguments)
+has() {
+ (( $# > 1 )) || return 2
+ local IFS=$'\a' target="$1"
+ shift
+ [[ $'\a'"$*"$'\a' = *$'\a'$target$'\a'* ]]
+}
+##
+# Dies when it can't change directories
+cd() {
+ if builtin cd -P "$@"; then
+ if [[ $1 != $PWD ]]; then
+ # Some symlink malfeasance is going on
+ die 1 "Working directory expected to be $1, but it is $PWD"
+ fi
+ else
+ die 1 "Unable to change working directory to '$@'"
+ fi
+}
+##
+# Tries not to delete any files or directories it shouldn't
+setup_rm() {
+ ##
+ # Anything in the FILES array in tmpdir is fair game for removal
+ rm() {
+ local i IFS=$'\a'
+ [[ $APP_NAME ]] || die 1 '$APP_NAME is not defined! (This is a bug.)'
+ case $@ in
+ */*|*-r*|*-R*) die 1 "Oops, I'm not allowed to delete that. ($@)";;
+ esac
+ for i; do
+ # Don't delete files that are not listed in the array
+ # Allow no slashes or recursive deletes at all.
+ case $i in
+ */*|-*r*|-*R*) :;; # Not OK
+ -*) continue;; # OK
+ esac
+ has "$i" "${FILES[@]}" && continue
+ die 1 "Oops, I'm not allowed to delete that. ($@)"
+ done
+ command rm "$@"
+ }
+ # delete this setup function so it's harmless to re-run
+ setup_rm() { :; }
+}
+##
+# Make our temporary files directory
+# $1 - directory name
+# $2 - user name
+verify_tmpdir() {
+ umask 007 || die $? "Unable to set umask 007"
+ if [[ ! $1 ]]; then
+ die 1 'Temporary file path is unset! (This is a bug.)'
+ elif [[ -d $1 ]]; then
+ # HACK: I hate using find this way
+ if [[ $(find "$1" -type d ! \( -user $2 -perm -0700 \) ) ]]; then
+ eerror "Incorrect permissions on $1"
+ eerror "or at least one file in $1."
+ die 1 "Please make sure it's not a symlink and then remove it."
+ fi
+ cd "$1"
+ else
+ die 1 "Unable to find a satisfactory location for temporary files ($1)"
+ fi
+ [[ $VERBOSE ]] && einfo "Temporary cache files are located in $PWD"
+ setup_rm
+}
+get_search_env() {
+ local new_env
+ local old_env
+ local uid=$(python -c 'import os; import pwd; print pwd.getpwuid(os.getuid())[0]')
+ # Find a place to put temporary files
+ if [[ "$uid" == "root" ]]; then
+ local tmp_target="/var/cache/${APP_NAME}"
+ else
+ local tmp_target="$(mktemp -d -t revdep-rebuild.XXXXXXXXXX)"
+ fi
+
+ # From here on all work is done inside the temporary directory
+ verify_tmpdir "$tmp_target" "$uid"
+ WORKING_DIR="$tmp_target"
+
+ if [[ $SEARCH_BROKEN ]]; then
+ SONAME_SEARCH="$SONAME"
+ HEAD_TEXT="broken by a package update"
+ OK_TEXT="Dynamic linking on your system is consistent"
+ WORKING_TEXT="consistency"
+ else
+ # first case is needed to test against /path/to/foo.so
+ if [[ $SONAME = /* ]]; then
+ # Set to "<space>$SONAME<space>"
+ SONAME_SEARCH=" $SONAME "
+ # Escape the "/" characters
+ SONAME_SEARCH="${SONAME_SEARCH//\//\\/}"
+ else
+ # Set to "<tab>$SONAME<space>"
+ SONAME_SEARCH=$'\t'"$SONAME "
+ fi
+ HEAD_TEXT="using $SONAME"
+ OK_TEXT="There are no dynamic links to $SONAME"
+ unset WORKING_TEXT
+ fi
+
+ # If any of our temporary files are older than 1 day, remove them all
+ if [[ ! $KEEP_TEMP ]]; then
+ while read; do
+ RM_OLD_TEMPFILES=1
+ break
+ done < <(find -L . -maxdepth 1 -type f -name '*.rr' -mmin +1440 -print 2>/dev/null)
+ fi
+
+ # Compare old and new environments
+ # Don't use our previous files if environment doesn't match
+ new_env=$(
+ # We do not care if these emerge options change
+ EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]//--pretend/})
+ EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]//--fetchonly/})
+ EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]//--verbose/})
+ cat <<- EOF
+ SEARCH_DIRS="$SEARCH_DIRS"
+ SEARCH_DIRS_MASK="$SEARCH_DIRS_MASK"
+ LD_LIBRARY_MASK="$LD_LIBRARY_MASK"
+ PORTAGE_ROOT="$PORTAGE_ROOT"
+ EMERGE_OPTIONS="${EMERGE_OPTIONS[@]}"
+ ORDER_PKGS="$ORDER_PKGS"
+ FULL_LD_PATH="$FULL_LD_PATH"
+ EOF
+ )
+ if [[ -r "$ENV_FILE" && -s "$ENV_FILE" ]]; then
+ old_env=$(<"$ENV_FILE")
+ if [[ $old_env != $new_env ]]; then
+ ewarn 'Environment mismatch from previous run, deleting temporary files...'
+ RM_OLD_TEMPFILES=1
+ fi
+ else
+ # No env file found, silently delete any other tempfiles that may exist
+ RM_OLD_TEMPFILES=1
+ fi
+
+ # If we should remove old tempfiles, do so
+ if [[ $RM_OLD_TEMPFILES ]]; then
+ rm -f "${FILES[@]}"
+ else
+ for file in "${FILES[@]}"; do
+ if [ -e "$file" ]; then
+ chown ${uid}:portage "$file"
+ chmod 700 "$file"
+ fi
+ done
+ fi
+
+ # Save the environment in a file for next time
+ echo "$new_env" > "$ENV_FILE"
+
+ [[ $VERBOSE ]] && echo $'\n'"$APP_NAME environment:"$'\n'"$new_env"
+
+ echo
+ einfo "Checking reverse dependencies"
+ einfo "Packages containing binaries and libraries $HEAD_TEXT"
+ einfo "will be emerged."
+}
+
+get_files() {
+ einfo "Collecting system binaries and libraries"
+ if [[ -r "$FILES_FILE" && -s "$FILES_FILE" ]]; then
+ einfo "Found existing $FILES_FILE"
+ else
+ # Be safe and remove any extraneous temporary files
+ # Don't remove 0_env.rr - The first file in the array
+ rm -f "${FILES[@]:1}"
+
+ clean_trap "$FILES_FILE"
+
+ if [[ $SEARCH_DIRS_MASK ]]; then
+ findMask=($SEARCH_DIRS_MASK)
+ findMask="${findMask[@]/#/-o -path }"
+ findMask="( ${findMask#-o } ) -prune -o"
+ fi
+ # TODO: Check this
+ find ${SEARCH_DIRS[@]} $findMask -type f \( -executable -o \
+ -name '*.so' -o -name '*.so.*' -o -name '*.la' \) -print 2> /dev/null |
+ sort -u > "$FILES_FILE" ||
+ die $? "find failed to list binary files (This is a bug.)"
+ einfo "Generated new $FILES_FILE"
+ fi
+}
+get_ldpath() {
+ local COMPLETE_LD_LIBRARY_PATH
+ [[ $SEARCH_BROKEN && $FULL_LD_PATH ]] || return
+ einfo 'Collecting complete LD_LIBRARY_PATH'
+ if [[ -r "$LDPATH_FILE" && -s "$LDPATH_FILE" ]]; then
+ einfo "Found existing $LDPATH_FILE."
+ else
+ clean_trap "$LDPATH_FILE"
+ # Ensure that the "trusted" lib directories are at the start of the path
+ COMPLETE_LD_LIBRARY_PATH=(
+ /lib*
+ /usr/lib*
+ $(sed '/^#/d;s/#.*$//' < /etc/ld.so.conf)
+ $(sed 's:/[^/]*$::' < "$FILES_FILE" | sort -ru)
+ )
+ IFS=':'
+ COMPLETE_LD_LIBRARY_PATH="${COMPLETE_LD_LIBRARY_PATH[*]}"
+ IFS="$OIFS"
+ echo "$COMPLETE_LD_LIBRARY_PATH" > "$LDPATH_FILE"
+ einfo "Generated new $LDPATH_FILE"
+ fi
+}
+main_checks() {
+ local target_file
+ local -a files
+ local i=0
+ local ldd_output
+ local ldd_status
+ local numFiles
+ local COMPLETE_LD_LIBRARY_PATH
+ if [[ $SEARCH_BROKEN && $FULL_LD_PATH ]]; then
+ [[ -r "$LDPATH_FILE" && -s "$LDPATH_FILE" ]] ||
+ die 1 "Unable to find $LDPATH_FILE"
+ COMPLETE_LD_LIBRARY_PATH=$(<"$LDPATH_FILE")
+ fi
+ einfo "Checking dynamic linking $WORKING_TEXT"
+ if [[ -r "$BROKEN_FILE" && -s "$BROKEN_FILE" ]]; then
+ einfo "Found existing $BROKEN_FILE."
+ else
+ clean_trap "$BROKEN_FILE" "$ERRORS_FILE"
+ files=($(<"$FILES_FILE"))
+ numFiles="${#files[@]}"
+ for target_file in "${files[@]}"; do
+ if [[ $target_file != *.la ]]; then
+ # Note: double checking seems to be faster than single with complete path
+ # (special add ons are rare).
+ ldd_output=$(ldd "$target_file" 2>> "$ERRORS_FILE" | sort -u)
+ ldd_status=$? # TODO: Check this for problems with sort
+ # HACK: if LD_LIBRARY_MASK is null or undefined grep -vF doesn't work
+ if grep -vF "${LD_LIBRARY_MASK:=$'\a'}" <<< "$ldd_output" |
+ grep -q "$SONAME_SEARCH"; then
+ if [[ $SEARCH_BROKEN && $FULL_LD_PATH ]]; then
+ if LD_LIBRARY_PATH="$COMPLETE_LD_LIBRARY_PATH" ldd "$target_file" 2>/dev/null |
+ grep -vF "$LD_LIBRARY_MASK" | grep -q "$SONAME_SEARCH"; then
+ # FIXME: I hate duplicating code
+ # Only build missing direct dependencies
+ MISSING_LIBS=$(
+ expr='s/[[:space:]]*\([^[:space:]]*\) => not found/\1/p'
+ sed -n "$expr" <<< "$ldd_output"
+ )
+ REQUIRED_LIBS=$(
+ expr='s/^[[:space:]]*NEEDED[[:space:]]*\([^[:space:]]*\).*/\1/p';
+ objdump -x "$target_file" | grep NEEDED | sed "$expr" | sort -u
+ )
+ MISSING_LIBS=$(grep -F "$REQUIRED_LIBS" <<< "$MISSING_LIBS")
+ if [[ $MISSING_LIBS ]]; then
+ echo "obj $target_file" >> "$BROKEN_FILE"
+ echo_v " broken $target_file (requires $MISSING_LIBS)"
+ fi
+ fi
+ else
+ # FIXME: I hate duplicating code
+ # Only rebuild for direct dependencies
+ MISSING_LIBS=$(
+ expr="/$SONAME_SEARCH/s/^[[:space:]]*\([^[:space:]]*\).*$/\1/p"
+ sort -u <<< "$ldd_output" | sed -n "$expr"
+ )
+ REQUIRED_LIBS=$(
+ expr='s/^[[:space:]]*NEEDED[[:space:]]*\([^[:space:]]*\).*/\1/p';
+ objdump -x "$target_file" | grep NEEDED | sed "$expr" | sort -u
+ )
+ MISSING_LIBS=$(grep -F "$REQUIRED_LIBS" <<< "$MISSING_LIBS")
+ if [[ $MISSING_LIBS ]]; then
+ echo "obj $target_file" >> "$BROKEN_FILE"
+ if [[ $SEARCH_BROKEN ]]; then
+ echo_v " broken $target_file (requires $MISSING_LIBS)"
+ else
+ echo_v " found $target_file"
+ fi
+ fi
+ fi
+ fi
+ elif [[ $SEARCH_BROKEN ]]; then
+ # Look for broken .la files
+ for depend in $(
+ awk -F"[=']" '/^dependency_libs/{
+ gsub("^-[^[:space:]]*", "", $3);
+ gsub("[[:space:]]-[^[:space:]]*", "", $3);
+ print $3
+ }' "$target_file"
+ ); do
+ if [[ $depend = /* && ! -e $depend ]]; then
+ echo "obj $target_file" >> "$BROKEN_FILE"
+ echo_v " broken $target_file (requires $depend)"
+ fi
+ done
+ fi
+ [[ $VERBOSE ]] &&
+ progress $((++i)) $numFiles $target_file ||
+ progress $((++i)) $numFiles
+ done
+ if [[ $SEARCH_BROKEN ]]; then
+ # Look for missing version
+ while read target_file; do
+ echo "obj $target_file" >> "$BROKEN_FILE"
+ echo_v " broken $target_file (no version information available)"
+ done < <(
+ # Regexify LD_LIBRARY_MASK. Exclude it from the search.
+ LD_LIBRARY_MASK="${LD_LIBRARY_MASK//$'\n'/|}"
+ awk -v ldmask="(${LD_LIBRARY_MASK//./\\\.})" '
+ /no version information available/ && $0 !~ ldmask {
+ gsub(/[()]/, "", $NF)
+ if (seen[$NF]++) next
+ print $NF
+ }' "$ERRORS_FILE"
+ )
+ fi
+ [[ -r "$BROKEN_FILE" && -s "$BROKEN_FILE" ]] || clean_exit
+ sort -u "$BROKEN_FILE" -o "$BROKEN_FILE"
+ einfo "Generated new $BROKEN_FILE"
+ fi
+}
+get_packages() {
+ local target_file
+ local EXACT_PKG
+ local PKG
+ local obj
+ einfo 'Assigning files to packages'
+ if [[ -r "$RAW_FILE" && -s "$RAW_FILE" ]]; then
+ einfo "Found existing $RAW_FILE"
+ else
+ clean_trap "$RAW_FILE" "$OWNERS_FILE"
+ while read obj target_file; do
+ EXACT_PKG=$(get_file_owner $target_file)
+ if [[ $EXACT_PKG ]]; then
+ # Strip version information
+ PKG="${EXACT_PKG%%-r[[:digit:]]*}"
+ PKG="${PKG%-*}"
+ echo "$EXACT_PKG" >> "$RAW_FILE"
+ echo "$target_file -> $EXACT_PKG" >> "$OWNERS_FILE"
+ echo_v " $target_file -> $PKG"
+ else
+ ewarn " !!! $target_file not owned by any package is broken !!!"
+ echo "$target_file -> (none)" >> "$OWNERS_FILE"
+ echo_v " $target_file -> (none)"
+ fi
+ done < "$BROKEN_FILE"
+ einfo "Generated new $RAW_FILE and $OWNERS_FILE"
+ fi
+ # if we find '(none)' on every line, exit out
+ if ! grep -qvF '(none)' "$OWNERS_FILE"; then
+ ewarn "Found some broken files, but none of them were associated with known packages"
+ ewarn "Unable to proceed with automatic repairs."
+ ewarn "The broken files are listed in $OWNERS_FILE"
+ if [[ $VERBOSE ]]; then
+ ewarn "The broken files are:"
+ while read filename junk; do
+ ewarn " $filename"
+ done < "$OWNERS_FILE"
+ fi
+ exit 0 # FIXME: Should we exit 1 here?
+ fi
+}
+clean_packages() {
+ einfo 'Cleaning list of packages to rebuild'
+ if [[ -r "$PKGS_FILE" && -s "$PKGS_FILE" ]]; then
+ einfo "Found existing $PKGS_FILE"
+ else
+ sort -u "$RAW_FILE" > "$PKGS_FILE"
+ einfo "Generated new $PKGS_FILE"
+ fi
+}
+assign_packages_to_ebuilds() {
+ local EXACT_PKG
+ local PKG
+ local SLOT
+ einfo 'Assigning packages to ebuilds'
+ if [[ -r "$EBUILDS_FILE" && -s "$EBUILDS_FILE" ]]; then
+ einfo "Found existing $EBUILDS_FILE"
+ elif [[ -r "$PKGS_FILE" && -s "$PKGS_FILE" ]]; then
+ clean_trap "$EBUILDS_FILE"
+ while read EXACT_PKG; do
+ # Get the slot
+ PKG="${EXACT_PKG%%-r[[:digit:]]*}"
+ PKG="${PKG%-*}"
+ SLOT=$(</var/db/pkg/$EXACT_PKG/SLOT)
+ echo "$PKG:$SLOT"
+ done < "$PKGS_FILE" > "$EBUILDS_FILE"
+ einfo "Generated new $EBUILDS_FILE"
+ else
+ einfo 'Nothing to rebuild.'
+ die 1 '(The program should have already quit, so this is a minor bug.)'
+ fi
+}
+get_exact_ebuilds() {
+ einfo 'Assigning files to ebuilds'
+ if [[ -r $EBUILDS_FILE && -s $EBUILDS_FILE ]]; then
+ einfo "Found existing $EBUILDS_FILE"
+ elif [[ -r $BROKEN_FILE && -s $BROKEN_FILE ]]; then
+ rebuildList=" $(<"$BROKEN_FILE") "
+ rebuildList=(${rebuildList//[[:space:]]obj[[:space:]]/ })
+ get_file_owner "${rebuildList[@]}" | sed 's/^/=/' > "$EBUILDS_FILE"
+ einfo "Generated new $EBUILDS_FILE"
+ else
+ einfo 'Nothing to rebuild.'
+ die 1 '(The program should have already quit, so this is a minor bug.)'
+ fi
+}
+list_skipped_packages() {
+ ewarn
+ ewarn 'Portage could not find any version of the following packages it could build:'
+ ewarn "${SKIP_LIST[@]}"
+ ewarn
+ ewarn '(Perhaps they are masked, blocked, or removed from portage.)'
+ ewarn 'Try to emerge them manually.'
+ ewarn
+}
+get_build_order() {
+ local -r OLD_EMERGE_DEFAULT_OPTS="$EMERGE_DEFAULT_OPTS"
+ local RAW_REBUILD_LIST
+ local REBUILD_GREP
+ local i
+ if [[ ! $ORDER_PKGS ]]; then
+ einfo 'Skipping package ordering'
+ return
+ fi
+ einfo 'Evaluating package order'
+ if [[ -r "$ORDER_FILE" && -s "$ORDER_FILE" ]]; then
+ einfo "Found existing $ORDER_FILE"
+ else
+ clean_trap "$ORDER_FILE"
+ RAW_REBUILD_LIST=$(<"$EBUILDS_FILE")
+ if [[ $RAW_REBUILD_LIST ]]; then
+ export EMERGE_DEFAULT_OPTS="--nospinner --pretend --oneshot --quiet"
+ RAW_REBUILD_LIST=($RAW_REBUILD_LIST) # convert into array
+ # If PACKAGE_NAMES is defined we're using slots, not versions
+ if [[ $PACKAGE_NAMES ]]; then
+ # Eliminate atoms that can't be built
+ for i in "${!RAW_REBUILD_LIST[@]}"; do
+ if [[ "${RAW_REBUILD_LIST[i]}" = *[A-Za-z]* ]]; then
+ portageq best_visible "$PORTAGE_ROOT" "${RAW_REBUILD_LIST[i]}" >/dev/null && continue
+ SKIP_LIST+=("${RAW_REBUILD_LIST[i]}")
+ fi
+ unset RAW_REBUILD_LIST[i]
+ done
+ # If RAW_REBUILD_LIST is empty, then we have nothing to build.
+ if (( ${#RAW_REBUILD_LIST[@]} == 0 )); then
+ if (( ${#SKIP_LIST[@]} == 0 )); then
+ ewarn "The list of packages to skip is empty, but there are no"
+ ewarn "packages listed to rebuild either. (This is a bug.)"
+ else
+ list_skipped_packages
+ fi
+ die 1 'Warning: Portage cannot rebuild any of the necessary packages.'
+ fi
+ fi
+ RAW_REBUILD_LIST="${RAW_REBUILD_LIST[@]}"
+ REBUILD_GREP=$(emerge --nodeps $RAW_REBUILD_LIST | sed 's/\[[^]]*\]//g')
+ if (( ${PIPESTATUS[0]} == 0 )); then
+ emerge --deep $RAW_REBUILD_LIST |
+ sed 's/\[[^]]*\]//g' |
+ grep -F "$REBUILD_GREP" > "$ORDER_FILE"
+ fi
+
+ # Here we use the PIPESTATUS from the second emerge, the --deep one.
+ if (( ${PIPESTATUS[0]} != 0 )); then
+ eerror
+ eerror 'Warning: Failed to resolve package order.'
+ eerror 'Will merge in arbitrary order'
+ eerror
+ cat <<- EOF
+ Possible reasons:
+ - An ebuild is no longer in the portage tree.
+ - An ebuild is masked, use /etc/portage/packages.keyword
+ and/or /etc/portage/package.unmask to unmask it
+ EOF
+ countdown 5
+ rm -f "$ORDER_FILE"
+ fi
+ export EMERGE_DEFAULT_OPTS="$OLD_EMERGE_DEFAULT_OPTS"
+ else
+ einfo 'Nothing to rebuild.'
+ die 1 '(The program should have already quit, so this is a minor bug.)'
+ fi
+ fi
+ [[ -r "$ORDER_FILE" && -s "$ORDER_FILE" ]] && einfo "Generated new $ORDER_FILE"
+}
+
+show_unowned_files() {
+ if grep -qF '(none)' "$OWNERS_FILE"; then
+ ewarn "Found some broken files that weren't associated with known packages"
+ ewarn "The broken files are:"
+ while read filename junk; do
+ [[ $junk = *none* ]] && ewarn " $filename"
+ done < "$OWNERS_FILE" | awk '!s[$0]++' # (omit dupes)
+ fi
+}
+##
+# Setup portage and the search paths
+setup_portage() {
+ local PORTAGE_NICENESS=$(portageq envvar PORTAGE_NICENESS)
+ PORTAGE_ROOT=$(portageq envvar ROOT)
+
+ # Obey PORTAGE_NICENESS
+ if [[ $PORTAGE_NICENESS ]]; then
+ renice $PORTAGE_NICENESS $$ > /dev/null
+ # Since we have already set our nice value for our processes,
+ # reset PORTAGE_NICENESS to zero to avoid having emerge renice again.
+ export PORTAGE_NICENESS="0"
+ fi
+
+ PORTAGE_ROOT="${PORTAGE_ROOT:-/}"
+}
+
+##
+# Setup the paths to search (and filter the ones to avoid)
+setup_search_paths_and_masks() {
+ local configfile sdir mdir skip_me filter_SEARCH_DIRS
+
+ einfo "Configuring search environment for $APP_NAME"
+
+ # Update the incremental variables using /etc/profile.env, /etc/ld.so.conf,
+ # portage, and the environment
+
+ # Read the incremental variables from environment and portage
+ # Until such time as portage supports these variables as incrementals
+ # The value will be what is in /etc/make.conf
+ SEARCH_DIRS+=" "$(unset SEARCH_DIRS; portageq envvar SEARCH_DIRS)
+ SEARCH_DIRS_MASK+=" "$(unset SEARCH_DIRS_MASK; portageq envvar SEARCH_DIRS_MASK)
+ LD_LIBRARY_MASK+=" "$(unset LD_LIBRARY_MASK; portageq envvar LD_LIBRARY_MASK)
+
+ # Add the defaults
+ if [[ -d /etc/revdep-rebuild ]]; then
+ for configfile in /etc/revdep-rebuild/*; do
+ SEARCH_DIRS+=" "$(. $configfile; echo $SEARCH_DIRS)
+ SEARCH_DIRS_MASK+=" "$(. $configfile; echo $SEARCH_DIRS_MASK)
+ LD_LIBRARY_MASK+=" "$(. $configfile; echo $LD_LIBRARY_MASK)
+ done
+ else
+ SEARCH_DIRS+=" /bin /sbin /usr/bin /usr/sbin /lib* /usr/lib*"
+ SEARCH_DIRS_MASK+=" /opt/OpenOffice /usr/lib/openoffice"
+ LD_LIBRARY_MASK+=" libodbcinst.so libodbc.so libjava.so libjvm.so"
+ fi
+
+ # Get the ROOTPATH and PATH from /etc/profile.env
+ if [[ -r "/etc/profile.env" && -s "/etc/profile.env" ]]; then
+ SEARCH_DIRS+=" "$(. /etc/profile.env; /usr/bin/tr ':' ' ' <<< "$ROOTPATH $PATH")
+ fi
+
+ # Get the directories from /etc/ld.so.conf
+ if [[ -r /etc/ld.so.conf && -s /etc/ld.so.conf ]]; then
+ SEARCH_DIRS+=" "$(sed '/^#/d;s/#.*$//' /etc/ld.so.conf)
+ fi
+
+ # Set the final variables
+ SEARCH_DIRS=$(clean_var <<< "$SEARCH_DIRS")
+ SEARCH_DIRS_MASK=$(clean_var <<< "$SEARCH_DIRS_MASK")
+ LD_LIBRARY_MASK=$(clean_var <<< "$LD_LIBRARY_MASK")
+ # Filter masked paths from SEARCH_DIRS
+ for sdir in ${SEARCH_DIRS} ; do
+ skip_me=
+ for mdir in ${SEARCH_DIRS_MASK}; do
+ [[ ${sdir} == ${mdir}/* ]] && skip_me=1 && break
+ done
+ [[ -n ${skip_me} ]] || filter_SEARCH_DIRS+=" ${sdir}"
+ done
+ SEARCH_DIRS=$(clean_var <<< "${filter_SEARCH_DIRS}")
+ [[ $SEARCH_DIRS ]] || die 1 "No search defined -- this is a bug."
+}
+##
+# Rebuild packages owning broken binaries
+rebuild() {
+ if [[ -r $LIST.5_order && -s $LIST.5_order ]]; then
+ REBUILD_LIST=( $(<"$LIST.5_order") )
+ REBUILD_LIST="${REBUILD_LIST[@]/#/=}"
+ else
+ REBUILD_LIST=$(sort -u "$EBUILDS_FILE")
+ fi
+
+ trap - SIGHUP SIGINT SIGQUIT SIGABRT SIGTERM
+
+ einfo 'All prepared. Starting rebuild'
+ echo "emerge --oneshot ${EMERGE_OPTIONS[@]} $REBUILD_LIST"
+
+ is_real_merge && countdown 10
+
+ # Link file descriptor #6 with stdin so --ask will work
+ exec 6<&0
+
+ # Run in background to correctly handle Ctrl-C
+ {
+ EMERGE_DEFAULT_OPTS="--oneshot ${EMERGE_OPTIONS[@]}" emerge $REBUILD_LIST <&6
+ echo $? > "$STATUS_FILE"
+ } &
+ wait
+
+ # Now restore stdin from fd #6, where it had been saved, and close fd #6 ( 6<&- ) to free it for other processes to use.
+ exec 0<&6 6<&-
+}
+##
+# Finish up
+cleanup() {
+ if (( $(<"$STATUS_FILE") != 0 )); then
+ ewarn
+ ewarn "$APP_NAME failed to emerge all packages."
+ ewarn 'you have the following choices:'
+ einfo "- If emerge failed during the build, fix the problems and re-run $APP_NAME."
+ einfo '- Use /etc/portage/package.keywords to unmask a newer version of the package.'
+ einfo " (and remove $ORDER_FILE to be evaluated again)"
+ einfo '- Modify the above emerge command and run it manually.'
+ einfo '- Compile or unmerge unsatisfied packages manually,'
+ einfo ' remove temporary files, and try again.'
+ einfo ' (you can edit package/ebuild list first)'
+ einfo
+ einfo 'To remove temporary files, please run:'
+ einfo "rm ${WORKING_DIR}/*.rr"
+ show_unowned_files
+ exit $EMERGE_STATUS
+ elif is_real_merge; then
+ trap_cmd() {
+ eerror "terminated. Please remove the temporary files manually:"
+ eerror "rm ${WORKING_DIR}/*.rr"
+ exit 1
+ }
+ [[ "${SKIP_LIST[@]}" != "" ]] && list_skipped_packages
+ trap trap_cmd SIGHUP SIGINT SIGQUIT SIGABRT SIGTERM
+ einfo 'Build finished correctly. Removing temporary files...'
+ einfo
+ einfo 'You can re-run revdep-rebuild to verify that all libraries and binaries'
+ einfo 'are fixed. If some inconsistency remains, it can be orphaned file, deep'
+ einfo 'dependency, binary package or specially evaluated library.'
+ if [[ -r "$OWNERS_FILE" && -s "$OWNERS_FILE" ]]; then
+ show_unowned_files
+ fi
+ [[ $KEEP_TEMP ]] || rm "${FILES[@]}"
+ else
+ einfo 'Now you can remove -p (or --pretend) from arguments and re-run revdep-rebuild.'
+ fi
+}
+
+main "$@"