diff options
Diffstat (limited to 'pym/portage/dispatch_conf.py')
-rw-r--r-- | pym/portage/dispatch_conf.py | 326 |
1 files changed, 169 insertions, 157 deletions
diff --git a/pym/portage/dispatch_conf.py b/pym/portage/dispatch_conf.py index 4c68dfc7b..f975ccd59 100644 --- a/pym/portage/dispatch_conf.py +++ b/pym/portage/dispatch_conf.py @@ -1,5 +1,5 @@ # archive_conf.py -- functionality common to archive-conf and dispatch-conf -# Copyright 2003-2012 Gentoo Foundation +# Copyright 2003-2014 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 @@ -24,175 +24,187 @@ RCS_MERGE = "rcsmerge -p -r" + RCS_BRANCH + " '%s' > '%s'" DIFF3_MERGE = "diff3 -mE '%s' '%s' '%s' > '%s'" def diffstatusoutput(cmd, file1, file2): - """ - Execute the string cmd in a shell with getstatusoutput() and return a - 2-tuple (status, output). - """ - # Use Popen to emulate getstatusoutput(), since getstatusoutput() may - # raise a UnicodeDecodeError which makes the output inaccessible. - args = shlex_split(cmd % (file1, file2)) - if sys.hexversion < 0x3000000 or sys.hexversion >= 0x3020000: - # Python 3.1 does not support bytes in Popen args. - args = [portage._unicode_encode(x, errors='strict') for x in args] - proc = subprocess.Popen(args, - stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - output = portage._unicode_decode(proc.communicate()[0]) - if output and output[-1] == "\n": - # getstatusoutput strips one newline - output = output[:-1] - return (proc.wait(), output) + """ + Execute the string cmd in a shell with getstatusoutput() and return a + 2-tuple (status, output). + """ + # Use Popen to emulate getstatusoutput(), since getstatusoutput() may + # raise a UnicodeDecodeError which makes the output inaccessible. + args = shlex_split(cmd % (file1, file2)) + + if sys.hexversion < 0x3020000 and sys.hexversion >= 0x3000000 and \ + not os.path.isabs(args[0]): + # Python 3.1 _execvp throws TypeError for non-absolute executable + # path passed as bytes (see http://bugs.python.org/issue8513). + fullname = portage.process.find_binary(args[0]) + if fullname is None: + raise portage.exception.CommandNotFound(args[0]) + args[0] = fullname + + args = [portage._unicode_encode(x, errors='strict') for x in args] + proc = subprocess.Popen(args, + stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + output = portage._unicode_decode(proc.communicate()[0]) + if output and output[-1] == "\n": + # getstatusoutput strips one newline + output = output[:-1] + return (proc.wait(), output) def read_config(mandatory_opts): - eprefix = portage.const.EPREFIX - config_path = os.path.join(eprefix or os.sep, "etc/dispatch-conf.conf") - loader = KeyValuePairFileLoader(config_path, None) - opts, errors = loader.load() - if not opts: - print(_('dispatch-conf: Error reading /etc/dispatch-conf.conf; fatal'), file=sys.stderr) - sys.exit(1) + eprefix = portage.settings["EPREFIX"] + if portage._not_installed: + config_path = os.path.join(portage.PORTAGE_BASE_PATH, "cnf", "dispatch-conf.conf") + else: + config_path = os.path.join(eprefix or os.sep, "etc/dispatch-conf.conf") + loader = KeyValuePairFileLoader(config_path, None) + opts, _errors = loader.load() + if not opts: + print(_('dispatch-conf: Error reading /etc/dispatch-conf.conf; fatal'), file=sys.stderr) + sys.exit(1) # Handle quote removal here, since KeyValuePairFileLoader doesn't do that. - quotes = "\"'" - for k, v in opts.items(): - if v[:1] in quotes and v[:1] == v[-1:]: - opts[k] = v[1:-1] - - for key in mandatory_opts: - if key not in opts: - if key == "merge": - opts["merge"] = "sdiff --suppress-common-lines --output='%s' '%s' '%s'" - else: - print(_('dispatch-conf: Missing option "%s" in /etc/dispatch-conf.conf; fatal') % (key,), file=sys.stderr) - - # archive-dir supports ${EPREFIX} expansion, in order to avoid hardcoding - variables = {"EPREFIX": eprefix} - opts['archive-dir'] = varexpand(opts['archive-dir'], mydict=variables) - - if not os.path.exists(opts['archive-dir']): - os.mkdir(opts['archive-dir']) - # Use restrictive permissions by default, in order to protect - # against vulnerabilities (like bug #315603 involving rcs). - os.chmod(opts['archive-dir'], 0o700) - elif not os.path.isdir(opts['archive-dir']): - print(_('dispatch-conf: Config archive dir [%s] must exist; fatal') % (opts['archive-dir'],), file=sys.stderr) - sys.exit(1) - - return opts + quotes = "\"'" + for k, v in opts.items(): + if v[:1] in quotes and v[:1] == v[-1:]: + opts[k] = v[1:-1] + + for key in mandatory_opts: + if key not in opts: + if key == "merge": + opts["merge"] = "sdiff --suppress-common-lines --output='%s' '%s' '%s'" + else: + print(_('dispatch-conf: Missing option "%s" in /etc/dispatch-conf.conf; fatal') % (key,), file=sys.stderr) + + # archive-dir supports ${EPREFIX} expansion, in order to avoid hardcoding + variables = {"EPREFIX": eprefix} + opts['archive-dir'] = varexpand(opts['archive-dir'], mydict=variables) + + if not os.path.exists(opts['archive-dir']): + os.mkdir(opts['archive-dir']) + # Use restrictive permissions by default, in order to protect + # against vulnerabilities (like bug #315603 involving rcs). + os.chmod(opts['archive-dir'], 0o700) + elif not os.path.isdir(opts['archive-dir']): + print(_('dispatch-conf: Config archive dir [%s] must exist; fatal') % (opts['archive-dir'],), file=sys.stderr) + sys.exit(1) + + return opts def rcs_archive(archive, curconf, newconf, mrgconf): - """Archive existing config in rcs (on trunk). Then, if mrgconf is - specified and an old branch version exists, merge the user's changes - and the distributed changes and put the result into mrgconf. Lastly, - if newconf was specified, leave it in the archive dir with a .dist.new - suffix along with the last 1.1.1 branch version with a .dist suffix.""" - - try: - os.makedirs(os.path.dirname(archive)) - except OSError: - pass - - if os.path.isfile(curconf): - try: - shutil.copy2(curconf, archive) - except(IOError, os.error) as why: - print(_('dispatch-conf: Error copying %(curconf)s to %(archive)s: %(reason)s; fatal') % \ - {"curconf": curconf, "archive": archive, "reason": str(why)}, file=sys.stderr) - - if os.path.exists(archive + ',v'): - os.system(RCS_LOCK + ' ' + archive) - os.system(RCS_PUT + ' ' + archive) - - ret = 0 - if newconf != '': - os.system(RCS_GET + ' -r' + RCS_BRANCH + ' ' + archive) - has_branch = os.path.exists(archive) - if has_branch: - os.rename(archive, archive + '.dist') - - try: - shutil.copy2(newconf, archive) - except(IOError, os.error) as why: - print(_('dispatch-conf: Error copying %(newconf)s to %(archive)s: %(reason)s; fatal') % \ - {"newconf": newconf, "archive": archive, "reason": str(why)}, file=sys.stderr) - - if has_branch: - if mrgconf != '': - # This puts the results of the merge into mrgconf. - ret = os.system(RCS_MERGE % (archive, mrgconf)) - mystat = os.lstat(newconf) - os.chmod(mrgconf, mystat.st_mode) - os.chown(mrgconf, mystat.st_uid, mystat.st_gid) - os.rename(archive, archive + '.dist.new') - return ret + """Archive existing config in rcs (on trunk). Then, if mrgconf is + specified and an old branch version exists, merge the user's changes + and the distributed changes and put the result into mrgconf. Lastly, + if newconf was specified, leave it in the archive dir with a .dist.new + suffix along with the last 1.1.1 branch version with a .dist suffix.""" + + try: + os.makedirs(os.path.dirname(archive)) + except OSError: + pass + + if os.path.isfile(curconf): + try: + shutil.copy2(curconf, archive) + except(IOError, os.error) as why: + print(_('dispatch-conf: Error copying %(curconf)s to %(archive)s: %(reason)s; fatal') % \ + {"curconf": curconf, "archive": archive, "reason": str(why)}, file=sys.stderr) + + if os.path.exists(archive + ',v'): + os.system(RCS_LOCK + ' ' + archive) + os.system(RCS_PUT + ' ' + archive) + + ret = 0 + if newconf != '': + os.system(RCS_GET + ' -r' + RCS_BRANCH + ' ' + archive) + has_branch = os.path.exists(archive) + if has_branch: + os.rename(archive, archive + '.dist') + + try: + shutil.copy2(newconf, archive) + except(IOError, os.error) as why: + print(_('dispatch-conf: Error copying %(newconf)s to %(archive)s: %(reason)s; fatal') % \ + {"newconf": newconf, "archive": archive, "reason": str(why)}, file=sys.stderr) + + if has_branch: + if mrgconf != '': + # This puts the results of the merge into mrgconf. + ret = os.system(RCS_MERGE % (archive, mrgconf)) + mystat = os.lstat(newconf) + os.chmod(mrgconf, mystat.st_mode) + os.chown(mrgconf, mystat.st_uid, mystat.st_gid) + os.rename(archive, archive + '.dist.new') + + return ret def file_archive(archive, curconf, newconf, mrgconf): - """Archive existing config to the archive-dir, bumping old versions - out of the way into .# versions (log-rotate style). Then, if mrgconf - was specified and there is a .dist version, merge the user's changes - and the distributed changes and put the result into mrgconf. Lastly, - if newconf was specified, archive it as a .dist.new version (which - gets moved to the .dist version at the end of the processing).""" - - try: - os.makedirs(os.path.dirname(archive)) - except OSError: - pass - - # Archive the current config file if it isn't already saved - if os.path.exists(archive) \ - and len(diffstatusoutput("diff -aq '%s' '%s'", curconf, archive)[1]) != 0: - suf = 1 - while suf < 9 and os.path.exists(archive + '.' + str(suf)): - suf += 1 - - while suf > 1: - os.rename(archive + '.' + str(suf-1), archive + '.' + str(suf)) - suf -= 1 - - os.rename(archive, archive + '.1') - - if os.path.isfile(curconf): - try: - shutil.copy2(curconf, archive) - except(IOError, os.error) as why: - print(_('dispatch-conf: Error copying %(curconf)s to %(archive)s: %(reason)s; fatal') % \ - {"curconf": curconf, "archive": archive, "reason": str(why)}, file=sys.stderr) - - if newconf != '': - # Save off new config file in the archive dir with .dist.new suffix - try: - shutil.copy2(newconf, archive + '.dist.new') - except(IOError, os.error) as why: - print(_('dispatch-conf: Error copying %(newconf)s to %(archive)s: %(reason)s; fatal') % \ - {"newconf": newconf, "archive": archive + '.dist.new', "reason": str(why)}, file=sys.stderr) - - ret = 0 - if mrgconf != '' and os.path.exists(archive + '.dist'): - # This puts the results of the merge into mrgconf. - ret = os.system(DIFF3_MERGE % (curconf, archive + '.dist', newconf, mrgconf)) - mystat = os.lstat(newconf) - os.chmod(mrgconf, mystat.st_mode) - os.chown(mrgconf, mystat.st_uid, mystat.st_gid) - - return ret + """Archive existing config to the archive-dir, bumping old versions + out of the way into .# versions (log-rotate style). Then, if mrgconf + was specified and there is a .dist version, merge the user's changes + and the distributed changes and put the result into mrgconf. Lastly, + if newconf was specified, archive it as a .dist.new version (which + gets moved to the .dist version at the end of the processing).""" + + try: + os.makedirs(os.path.dirname(archive)) + except OSError: + pass + + # Archive the current config file if it isn't already saved + if (os.path.exists(archive) and + len(diffstatusoutput("diff -aq '%s' '%s'", curconf, archive)[1]) != 0): + suf = 1 + while suf < 9 and os.path.exists(archive + '.' + str(suf)): + suf += 1 + + while suf > 1: + os.rename(archive + '.' + str(suf-1), archive + '.' + str(suf)) + suf -= 1 + + os.rename(archive, archive + '.1') + + if os.path.isfile(curconf): + try: + shutil.copy2(curconf, archive) + except(IOError, os.error) as why: + print(_('dispatch-conf: Error copying %(curconf)s to %(archive)s: %(reason)s; fatal') % \ + {"curconf": curconf, "archive": archive, "reason": str(why)}, file=sys.stderr) + + if newconf != '': + # Save off new config file in the archive dir with .dist.new suffix + try: + shutil.copy2(newconf, archive + '.dist.new') + except(IOError, os.error) as why: + print(_('dispatch-conf: Error copying %(newconf)s to %(archive)s: %(reason)s; fatal') % \ + {"newconf": newconf, "archive": archive + '.dist.new', "reason": str(why)}, file=sys.stderr) + + ret = 0 + if mrgconf != '' and os.path.exists(archive + '.dist'): + # This puts the results of the merge into mrgconf. + ret = os.system(DIFF3_MERGE % (curconf, archive + '.dist', newconf, mrgconf)) + mystat = os.lstat(newconf) + os.chmod(mrgconf, mystat.st_mode) + os.chown(mrgconf, mystat.st_uid, mystat.st_gid) + + return ret def rcs_archive_post_process(archive): - """Check in the archive file with the .dist.new suffix on the branch - and remove the one with the .dist suffix.""" - os.rename(archive + '.dist.new', archive) - if os.path.exists(archive + '.dist'): - # Commit the last-distributed version onto the branch. - os.system(RCS_LOCK + RCS_BRANCH + ' ' + archive) - os.system(RCS_PUT + ' -r' + RCS_BRANCH + ' ' + archive) - os.unlink(archive + '.dist') - else: - # Forcefully commit the last-distributed version onto the branch. - os.system(RCS_PUT + ' -f -r' + RCS_BRANCH + ' ' + archive) + """Check in the archive file with the .dist.new suffix on the branch + and remove the one with the .dist suffix.""" + os.rename(archive + '.dist.new', archive) + if os.path.exists(archive + '.dist'): + # Commit the last-distributed version onto the branch. + os.system(RCS_LOCK + RCS_BRANCH + ' ' + archive) + os.system(RCS_PUT + ' -r' + RCS_BRANCH + ' ' + archive) + os.unlink(archive + '.dist') + else: + # Forcefully commit the last-distributed version onto the branch. + os.system(RCS_PUT + ' -f -r' + RCS_BRANCH + ' ' + archive) def file_archive_post_process(archive): - """Rename the archive file with the .dist.new suffix to a .dist suffix""" - os.rename(archive + '.dist.new', archive + '.dist') + """Rename the archive file with the .dist.new suffix to a .dist suffix""" + os.rename(archive + '.dist.new', archive + '.dist') |