from portage.process import find_binary
FIND_EXTANT_CONFIGS = "find '%s' %s -iname '._cfg????_%s' ! -iname '.*~' ! -iname '.*.bak'"
-DIFF_CONTENTS = 'diff -Nu %s %s'
-DIFF_CVS_INTERP = 'diff -Nu %s %s | grep "^[+-][^+-]" | grep -v "# .Header:.*"'
-DIFF_WSCOMMENTS = 'diff -Nu %s %s | grep "^[+-][^+-]" | grep -v "^[-+]#" | grep -v "^[-+][:space:]*$"'
+DIFF_CONTENTS = "diff -Nu '%s' '%s'"
+DIFF_CVS_INTERP = "diff -Nu '%s' '%s' | grep '^[+-][^+-]' | grep -v '# .Header:.*'"
+DIFF_WSCOMMENTS = "diff -Nu '%s' '%s' | grep '^[+-][^+-]' | grep -v '^[-+]#' | grep -v '^[-+][:space:]*$'"
# We need a secure scratch dir and python does silly verbose errors on the use of tempnam
oldmask = os.umask(0077)
fi
function get_config() {
- item=$1
-
- # First strip off comment lines, then grab the configuration
- # item. If there's more than one of the same configuration item,
- # then allow the last setting to take precedence.
- local result
- result=$(cut -d'#' -f1-1 ${PORTAGE_CONFIGROOT}etc/etc-update.conf | \
- sed -ne "s/^ *$item *= *\([\"']\{0,1\}\)\(.*\)\1/\2/p" |sed -e '$p;d')
- eval echo $result
+ # the sed here does:
+ # - strip off comments
+ # - match lines that set item in question
+ # - delete the "item =" part
+ # - store the actual value into the hold space
+ # - on the last line, restore the hold space and print it
+ # If there's more than one of the same configuration item, then
+ # the store to the hold space clobbers previous value so the last
+ # setting takes precedence.
+ local item=$1
+ eval echo $(sed -n \
+ -e 's:[[:space:]]*#.*$::' \
+ -e "/^[[:space:]]*$item[[:space:]]*=/{s:[^=]*=[[:space:]]*\([\"']\{0,1\}\)\(.*\)\1:\2:;h}" \
+ -e '${g;p}' \
+ "${PORTAGE_CONFIGROOT}"etc/etc-update.conf)
}
function scan() {
}
function do_merge() {
+ # make sure we keep the merged file in the secure tempdir
+ # so we dont leak any information contained in said file
+ # (think of case where the file has 0600 perms; during the
+ # merging process, the temp file gets umask perms!)
local file="${1}"
local ofile="${2}"
- local mfile="${2}.merged"
+ local mfile="${TMP}/${2}.merged"
local -i my_input=0
echo "${file} ${ofile} ${mfile}"
- if [ -e ${mfile} ] ; then
+ if [[ -e ${mfile} ]] ; then
echo "A previous version of the merged file exists, cleaning..."
- rm ${rm_opts} ${mfile}
+ rm ${rm_opts} "${mfile}"
fi
+ # since mfile will be like $TMP/path/to/original-file.merged, we
+ # need to make sure the full /path/to/ exists ahead of time
+ mkdir -p "${mfile%/*}"
+
until (( ${my_input} == -1 )); do
echo "Merging ${file} and ${ofile}"
$(echo "${merge_command}" |
chown "$(stat -f %Su:%Sg "${ofile}")" "${mfile}"
chmod $(stat -f %Mp%Lp "${ofile}") "${mfile}"
fi
- mv ${mv_opts} ${mfile} ${ofile}
- rm ${rm_opts} ${file}
+ mv ${mv_opts} "${mfile}" "${ofile}"
+ rm ${rm_opts} "${file}"
return 255
;;
2) ( echo "Showing differences between ${ofile} and ${mfile}"
4) ${EDITOR:-nano -w} "${mfile}"
continue
;;
- 5) rm ${rm_opts} ${mfile}
+ 5) rm ${rm_opts} "${mfile}"
return 0
;;
*) continue
esac
done
done
- rm ${rm_opts} ${mfile}
+ rm ${rm_opts} "${mfile}"
return 255
}
[ ${count} -gt 0 ] && echo "NOTE: ${count} updates remaining"
fi
- rm -rf ${TMP}
+ rm -rf "${TMP}"
exit ${2}
}
#
scriptname=$(basename $0)
+TMP="${PORTAGE_TMPDIR}/etc-update-$$"
trap die term
type portageq > /dev/null || exit $?
#echo $CONFIG_PROTECT_MASK
#export PORTAGE_TMPDIR=$(/usr/lib/portage/bin/portageq envvar PORTAGE_TMPDIR)
-TMP="${PORTAGE_TMPDIR}/$$"
-rm -rf ${TMP} 2> /dev/null
-mkdir ${TMP} || die "failed mkdir command!" 1
+rm -rf "${TMP}" 2> /dev/null
+mkdir "${TMP}" || die "failed to create temp dir" 1
+# make sure we have a secure directory to work in
+chmod 0700 "${TMP}" || die "failed to set perms on temp dir" 1
+chown ${UID:-0}:${GID:-0} "${TMP}" || die "failed to set ownership on temp dir" 1
# I need the CONFIG_PROTECT value
#CONFIG_PROTECT=$(@PORTAGE_BASE@/bin/portageq envvar CONFIG_PROTECT)
# Diff for display
# %s old file
# %s new file
-diff="diff -Nu %s %s | less --no-init --QUIT-AT-EOF"
+diff="diff -Nu '%s' '%s' | less --no-init --QUIT-AT-EOF"
# Diff for interactive merges.
# %s output file
# %s old file
# %s new file
-merge="sdiff --suppress-common-lines --output=%s %s %s"
+merge="sdiff --suppress-common-lines --output='%s' '%s' '%s'"
# Automerge files comprising only CVS interpolations (e.g. Header or Id)
# (yes or no)
File is not UTF8 compliant
.TP
.B file.executable
-Ebuilds, digests, metadata.xml, Manifest, and ChangeLog do note need the executable bit
+Ebuilds, digests, metadata.xml, Manifest, and ChangeLog do not need the executable bit
.TP
.B file.name
File/dir name must be composed of only the following chars: a-zA-Z0-9._-+:
return -1
if self.runtime_post:
return -2
- if self.rebuild:
- return -3
if self.buildtime:
+ if self.rebuild:
+ return -3
return -4
if self.runtime:
return -5
if "--buildpkgonly" in self.myopts:
edepend["RDEPEND"] = ""
edepend["PDEPEND"] = ""
- if not (arg and "--onlydeps" in self.myopts and \
- mytype == "ebuild") and \
- self.myopts.get("--with-bdeps", "n") == "n" and \
- (mytype == "binary" or mybigkey[3] == "nomerge"):
- edepend["DEPEND"] = ""
+ bdeps_satisfied = False
+ if mytype in ("installed", "binary"):
+ if self.myopts.get("--with-bdeps", "n") == "y":
+ # Pull in build time deps as requested, but marked them as
+ # "satisfied" since they are not strictly required. This allows
+ # more freedom in the merge order calculation for solving
+ # circular dependencies. Don't convert to PDEPEND since that
+ # could make --with-bdeps=y less effective if it is used to
+ # adjust merge order to prevent built_with_use() calls from
+ # failing.
+ bdeps_satisfied = True
+ else:
+ # built packages do not have build time dependencies.
+ edepend["DEPEND"] = ""
""" We have retrieve the dependency information, now we need to recursively
process them. DEPEND gets processed for root = "/", {R,P}DEPEND in myroot. """
try:
if not self.select_dep("/", edepend["DEPEND"], myparent=mp,
- myuse=myuse, priority=DepPriority(buildtime=True),
+ myuse=myuse, priority=DepPriority(buildtime=True,
+ satisfied=bdeps_satisfied),
parent_arg=arg):
return 0
"""RDEPEND is soft by definition. However, in order to ensure
("blocks", p_root, x[1:]), set()).add(myparent)
continue
else:
- #We are not processing a blocker but a normal dependency
- if myparent:
- """In some cases, dep_check will return deps that shouldn't
- be proccessed any further, so they are identified and
- discarded here."""
- if "empty" not in self.myparams and \
- "deep" not in self.myparams and \
- not ("--update" in self.myopts and parent_arg) and \
- vardb.match(x):
- continue
-
# List of acceptable packages, ordered by type preference.
matched_packages = []
myeb_matches = portdb.xmatch("match-visible", x)
# ordered by type preference ("ebuild" type is the last resort)
selected_pkg = matched_packages[0]
+ # In some cases, dep_check will return deps that shouldn't
+ # be proccessed any further, so they are identified and
+ # discarded here. Try to discard as few as possible since
+ # discarded dependencies reduce the amount of information
+ # available for optimization of merge order.
+ if myparent and not arg and vardb.match(x) and \
+ not existing_node and \
+ "empty" not in self.myparams and \
+ "deep" not in self.myparams and \
+ not ("--update" in self.myopts and parent_arg):
+ (mytype, myroot, mykey), metadata = selected_pkg
+ myarg = None
+ if myroot == self.target_root:
+ try:
+ myarg = self._set_atoms.findAtomForPackage(
+ mykey, metadata)
+ except portage.exception.InvalidDependString:
+ # This is already handled inside
+ # self.create() when necessary.
+ pass
+ if not myarg:
+ continue
+
if myparent:
#we are a dependency, so we want to be unconditionally added
mypriority = priority.copy()
if action == "prune":
return
+ if not cleanlist and "--quiet" in myopts:
+ return
+
print "Packages installed: "+str(len(myvarlist))
print "Packages in world: "+str(len(worldlist))
print "Packages in system: "+str(len(syslist))
bsd_chflags = None
if os.uname()[0] in ["FreeBSD"]:
- try:
- import freebsd as bsd_chflags
- except ImportError:
+ def bsd_chflags():
pass
+ def _chflags(path, flags, opts=""):
+ cmd = "chflags %s %o '%s'" % (opts, flags, path)
+ status, output = commands.getstatusoutput(cmd)
+ if os.WIFEXITED(status) and os.WEXITSTATUS(status) == os.EX_OK:
+ return
+ # Try to generate an ENOENT error if appropriate.
+ if "h" in opts:
+ os.lstat(path)
+ else:
+ os.stat(path)
+ # Make sure the binary exists.
+ if not portage.process.find_binary("chflags"):
+ raise portage.exception.CommandNotFound("chflags")
+ # Now we're not sure exactly why it failed or what
+ # the real errno was, so just report EPERM.
+ e = OSError(errno.EPERM, output)
+ e.errno = errno.EPERM
+ e.filename = path
+ e.message = output
+ raise e
+ def _lchflags(path, flags):
+ return _chflags(path, flags, opts="-h")
+ bsd_chflags.chflags = _chflags
+ bsd_chflags.lchflags = _lchflags
try:
from portage.cache.cache_errors import CacheError
env=mysettings.environ()
keywords["opt_name"]="[%s]" % mysettings["PF"]
+ fd_pipes = keywords.get("fd_pipes")
+ if fd_pipes is None:
+ fd_pipes = {0:0, 1:1, 2:2}
+ # In some cases the above print statements don't flush stdout, so
+ # it needs to be flushed before allowing a child process to use it
+ # so that output always shows in the correct order.
+ for fd in fd_pipes.itervalues():
+ if fd == sys.stdout.fileno():
+ sys.stdout.flush()
+ if fd == sys.stderr.fileno():
+ sys.stderr.flush()
+
# The default policy for the sesandbox domain only allows entry (via exec)
# from shells and from binaries that belong to portage (the number of entry
# points is minimized). The "tee" binary is not among the allowed entry
got_pty = False
if logfile:
del keywords["logfile"]
- fd_pipes = keywords.get("fd_pipes")
- if fd_pipes is None:
- fd_pipes = {0:0, 1:1, 2:2}
- elif 1 not in fd_pipes or 2 not in fd_pipes:
+ if 1 not in fd_pipes or 2 not in fd_pipes:
raise ValueError(fd_pipes)
from pty import openpty
try:
writemsg("openpty failed: '%s'\n" % str(e), noiselevel=1)
del e
master_fd, slave_fd = os.pipe()
+
+ # We must set non-blocking mode before we close the slave_fd
+ # since otherwise the fcntl call can fail on FreeBSD (the child
+ # process might have already exited and closed slave_fd so we
+ # have to keep it open in order to avoid FreeBSD potentially
+ # generating an EAGAIN exception).
+ import fcntl
+ fcntl.fcntl(master_fd, fcntl.F_SETFL,
+ fcntl.fcntl(master_fd, fcntl.F_GETFL) | os.O_NONBLOCK)
+
fd_pipes.setdefault(0, sys.stdin.fileno())
fd_pipes_orig = fd_pipes.copy()
if got_pty and os.isatty(fd_pipes_orig[1]):
try:
mypids.extend(spawn_func(mystring, env=env, **keywords))
finally:
- if slave_fd:
+ if logfile:
os.close(slave_fd)
if sesandbox:
selinux.setexec(None)
iwtd = [master_file]
owtd = []
ewtd = []
- import array, fcntl, select
- fd_flags = {}
- for f in iwtd:
- fd_flags[f] = fcntl.fcntl(f.fileno(), fcntl.F_GETFL)
+ import array, select
buffsize = 65536
eof = False
- # Use non-blocking mode to prevent read
- # calls from blocking indefinitely.
- try:
- fcntl.fcntl(master_file.fileno(), fcntl.F_SETFL,
- fd_flags[master_file] | os.O_NONBLOCK)
- except EnvironmentError, e:
- if e.errno != errno.EAGAIN:
- raise
- del e
- # The EAGAIN error signals eof on FreeBSD.
- eof = True
while not eof:
events = select.select(iwtd, owtd, ewtd)
for f in events[0]:
+ # Use non-blocking mode to prevent read
+ # calls from blocking indefinitely.
buf = array.array('B')
try:
buf.fromfile(f, buffsize)
if bsd_chflags:
if destexists and dstat.st_flags != 0:
bsd_chflags.lchflags(dest, 0)
+ # Use normal stat/chflags for the parent since we want to
+ # follow any symlinks to the real parent directory.
pflags = os.stat(os.path.dirname(dest)).st_flags
if pflags != 0:
- bsd_chflags.lchflags(os.path.dirname(dest), 0)
+ bsd_chflags.chflags(os.path.dirname(dest), 0)
if destexists:
if stat.S_ISLNK(dstat[stat.ST_MODE]):
if bsd_chflags:
# Restore the flags we saved before moving
if pflags:
- bsd_chflags.lchflags(os.path.dirname(dest), pflags)
+ bsd_chflags.chflags(os.path.dirname(dest), pflags)
return newmtime
import os
import errno
import stat
+import tempfile
import portage.exception
import portage.process
import portage.locks
"""
global prelink_capable
myfilename = filename[:]
- prelink_tmpfile = os.path.join("/", PRIVATE_PATH, "prelink-checksum.tmp." + str(os.getpid()))
+ prelink_tmpfile = None
mylock = None
try:
if calc_prelink and prelink_capable:
- mylock = portage.locks.lockfile(prelink_tmpfile, wantnewlockfile=1)
# Create non-prelinked temporary file to checksum.
# Files rejected by prelink are summed in place.
try:
- retval = portage.process.spawn([PRELINK_BINARY, "--undo", "-o",
- prelink_tmpfile, filename], fd_pipes={})
+ tmpfile_fd, prelink_tmpfile = tempfile.mkstemp()
+ try:
+ retval = portage.process.spawn([PRELINK_BINARY,
+ "--verify", filename], fd_pipes={1:tmpfile_fd})
+ finally:
+ os.close(tmpfile_fd)
if retval == os.EX_OK:
myfilename = prelink_tmpfile
except portage.exception.CommandNotFound:
if e.errno == errno.ENOENT:
raise portage.exception.FileNotFound(myfilename)
raise
- if calc_prelink and prelink_capable:
+ if prelink_tmpfile:
try:
os.unlink(prelink_tmpfile)
except OSError, e:
#process symlinks second-to-last, directories last.
mydirs = []
+ ignored_unlink_errnos = (errno.ENOENT, errno.EISDIR)
modprotect = os.path.join(self.vartree.root, "lib/modules/")
+
+ def unlink(file_name, lstatobj):
+ if bsd_chflags:
+ if lstatobj.st_flags != 0:
+ bsd_chflags.lchflags(file_name, 0)
+ parent_name = os.path.dirname(file_name)
+ # Use normal stat/chflags for the parent since we want to
+ # follow any symlinks to the real parent directory.
+ pflags = os.stat(parent_name).st_flags
+ if pflags != 0:
+ bsd_chflags.chflags(parent_name, 0)
+ try:
+ if not stat.S_ISLNK(lstatobj.st_mode):
+ # Remove permissions to ensure that any hardlinks to
+ # suid/sgid files are rendered harmless.
+ os.chmod(file_name, 0)
+ os.unlink(file_name)
+ finally:
+ if bsd_chflags and pflags != 0:
+ # Restore the parent flags we saved before unlinking
+ bsd_chflags.chflags(parent_name, pflags)
+
def show_unmerge(zing, desc, file_type, file_name):
writemsg_stdout("%s %s %s %s\n" % \
(zing, desc.ljust(8), file_type, file_name))
not (islink and statobj and stat.S_ISDIR(statobj.st_mode)) and \
not self.isprotected(obj):
try:
- # Remove permissions to ensure that any hardlinks to
- # suid/sgid files are rendered harmless.
- if statobj and not islink:
- os.chmod(obj, 0)
- os.unlink(obj)
+ unlink(obj, lstatobj)
except EnvironmentError, e:
- pass
+ if e.errno not in ignored_unlink_errnos:
+ raise
+ del e
show_unmerge("<<<", "", file_type, obj)
continue
# contents as a directory even if it happens to correspond
# to a symlink when it's merged to the live filesystem.
try:
- os.unlink(obj)
+ unlink(obj, lstatobj)
show_unmerge("<<<", "", file_type, obj)
except (OSError, IOError),e:
+ if e.errno not in ignored_unlink_errnos:
+ raise
+ del e
show_unmerge("!!!", "", file_type, obj)
elif pkgfiles[objkey][0] == "obj":
if statobj is None or not stat.S_ISREG(statobj.st_mode):
show_unmerge("---", "!md5", file_type, obj)
continue
try:
- # Remove permissions to ensure that any hardlinks to
- # suid/sgid files are rendered harmless.
- if not islink:
- os.chmod(obj, 0)
- os.unlink(obj)
+ unlink(obj, lstatobj)
except (OSError, IOError), e:
- pass
+ if e.errno not in ignored_unlink_errnos:
+ raise
+ del e
show_unmerge("<<<", "", file_type, obj)
elif pkgfiles[objkey][0] == "fif":
if not stat.S_ISFIFO(lstatobj[stat.ST_MODE]):
for obj in mydirs:
try:
- os.rmdir(obj)
+ if bsd_chflags:
+ lstatobj = os.lstat(obj)
+ if lstatobj.st_flags != 0:
+ bsd_chflags.lchflags(obj, 0)
+ parent_name = os.path.dirname(obj)
+ # Use normal stat/chflags for the parent since we want to
+ # follow any symlinks to the real parent directory.
+ pflags = os.stat(parent_name).st_flags
+ if pflags != 0:
+ bsd_chflags.chflags(parent_name, 0)
+ try:
+ os.rmdir(obj)
+ finally:
+ if bsd_chflags and pflags != 0:
+ # Restore the parent flags we saved before unlinking
+ bsd_chflags.chflags(parent_name, pflags)
show_unmerge("<<<", "", "dir", obj)
- except EnvironmentError:
- show_unmerge("---", "!empty", "dir", obj)
+ except EnvironmentError, e:
+ if e.errno not in (errno.ENOENT,
+ errno.EEXIST, errno.ENOTEMPTY,
+ errno.ENOTDIR):
+ raise
+ if e.errno != errno.ENOENT:
+ show_unmerge("---", "!empty", "dir", obj)
+ del e
#remove self from vartree database so that our own virtual gets zapped if we're the last node
self.vartree.zap(self.mycpv)
RCS_LOCK = 'rcs -ko -M -l'
RCS_PUT = 'ci -t-"Archived config file." -m"dispatch-conf update."'
RCS_GET = 'co'
-RCS_MERGE = 'rcsmerge -p -r' + RCS_BRANCH + ' %s >%s'
+RCS_MERGE = "rcsmerge -p -r" + RCS_BRANCH + " '%s' > '%s'"
-DIFF3_MERGE = 'diff3 -mE %s %s %s >%s'
+DIFF3_MERGE = "diff3 -mE '%s' '%s' '%s' > '%s'"
def read_config(mandatory_opts):
try:
for key in mandatory_opts:
if not opts.has_key(key):
if key == "merge":
- opts["merge"] = "sdiff --suppress-common-lines --output=%s %s %s"
+ opts["merge"] = "sdiff --suppress-common-lines --output='%s' '%s' '%s'"
else:
print >> sys.stderr, 'dispatch-conf: Missing option "%s" in /etc/dispatch-conf.conf; fatal' % (key,)
# Archive the current config file if it isn't already saved
if os.path.exists(archive) \
- and len(commands.getoutput('diff -aq %s %s' % (curconf,archive))) != 0:
+ and len(commands.getoutput("diff -aq '%s' '%s'" % (curconf,archive))) != 0:
suf = 1
while suf < 9 and os.path.exists(archive + '.' + str(suf)):
suf += 1