From 1e984dab27458e3026163c5690aec5fd3298dd8b Mon Sep 17 00:00:00 2001 From: Zac Medico Date: Sat, 18 May 2013 15:24:05 -0700 Subject: [PATCH] Support PORTAGE_SSH_OPTS, bug #470002. Additional ssh options to be used when portage executes ssh or sftp. This variable supports use of embedded quote characters to quote whitespace or special shell characters within arguments (embedded quotes must be escaped in make.conf settings). --- cnf/make.globals | 6 ++-- man/make.conf.5 | 8 +++++ pym/_emerge/BinpkgFetcher.py | 8 ++++- pym/_emerge/actions.py | 6 ++++ pym/portage/dbapi/bintree.py | 31 ++++++++++++++--- pym/portage/getbinpkg.py | 33 ++++++++++++++----- .../ebuild/_config/special_env_vars.py | 2 +- pym/portage/package/ebuild/fetch.py | 9 +++-- 8 files changed, 84 insertions(+), 19 deletions(-) diff --git a/cnf/make.globals b/cnf/make.globals index 5f5098b06..1f07df39b 100644 --- a/cnf/make.globals +++ b/cnf/make.globals @@ -46,10 +46,12 @@ RESUMECOMMAND="wget -c -t 3 -T 60 --passive-ftp -O \"\${DISTDIR}/\${FILE}\" \"\$ FETCHCOMMAND_RSYNC="rsync -avP \"\${URI}\" \"\${DISTDIR}/\${FILE}\"" RESUMECOMMAND_RSYNC="rsync -avP \"\${URI}\" \"\${DISTDIR}/\${FILE}\"" -FETCHCOMMAND_SSH="bash -c \"x=\\\${2#ssh://} ; host=\\\${x%%/*} ; port=\\\${host##*:} ; host=\\\${host%:*} ; [[ \\\${host} = \\\${port} ]] && port=22 ; exec rsync --rsh=\\\"ssh -p\\\${port}\\\" -avP \\\"\\\${host}:/\\\${x#*/}\\\" \\\"\\\$1\\\"\" rsync \"\${DISTDIR}/\${FILE}\" \"\${URI}\"" +# NOTE: rsync will evaluate quotes embedded inside PORTAGE_SSH_OPTS +FETCHCOMMAND_SSH="bash -c \"x=\\\${2#ssh://} ; host=\\\${x%%/*} ; port=\\\${host##*:} ; host=\\\${host%:*} ; [[ \\\${host} = \\\${port} ]] && port=22 ; exec rsync --rsh=\\\"ssh -p\\\${port} \\\${3}\\\" -avP \\\"\\\${host}:/\\\${x#*/}\\\" \\\"\\\$1\\\"\" rsync \"\${DISTDIR}/\${FILE}\" \"\${URI}\" \"\${PORTAGE_SSH_OPTS}\"" RESUMECOMMAND_SSH=${FETCHCOMMAND_SSH} -FETCHCOMMAND_SFTP="bash -c \"x=\\\${2#sftp://} ; host=\\\${x%%/*} ; port=\\\${host##*:} ; host=\\\${host%:*} ; [[ \\\${host} = \\\${port} ]] && port=22 ; exec sftp -P \\\${port} \\\"\\\${host}:/\\\${x#*/}\\\" \\\"\\\$1\\\"\" sftp \"\${DISTDIR}/\${FILE}\" \"\${URI}\"" +# NOTE: bash eval is used to evaluate quotes embedded inside PORTAGE_SSH_OPTS +FETCHCOMMAND_SFTP="bash -c \"x=\\\${2#sftp://} ; host=\\\${x%%/*} ; port=\\\${host##*:} ; host=\\\${host%:*} ; [[ \\\${host} = \\\${port} ]] && port=22 ; eval \\\"declare -a ssh_opts=(\\\${3})\\\" ; exec sftp -P \\\${port} \\\"\\\${ssh_opts[@]}\\\" \\\"\\\${host}:/\\\${x#*/}\\\" \\\"\\\$1\\\"\" sftp \"\${DISTDIR}/\${FILE}\" \"\${URI}\" \"\${PORTAGE_SSH_OPTS}\"" # Default user options FEATURES="assume-digests binpkg-logs diff --git a/man/make.conf.5 b/man/make.conf.5 index 9b4121ce6..fed71f55e 100644 --- a/man/make.conf.5 +++ b/man/make.conf.5 @@ -854,6 +854,14 @@ addresses are exhausted. .br Defaults to -1. .TP +\fBPORTAGE_SSH_OPTS\fR = \fI[list of ssh options]\fR +Additional ssh options to be used when portage executes ssh or sftp. +This variable supports use of embedded quote characters to quote +whitespace or special shell characters within arguments (embedded +quotes must be escaped in make.conf settings). +.br +Defaults to no value. +.TP \fBPORTAGE_SYNC_STALE\fR = \fI[NUMBER]\fR Defines the number of days after the last `emerge \-\-sync` that a warning message should be produced. A value of 0 will disable warnings. diff --git a/pym/_emerge/BinpkgFetcher.py b/pym/_emerge/BinpkgFetcher.py index 099d3c498..543881ee6 100644 --- a/pym/_emerge/BinpkgFetcher.py +++ b/pym/_emerge/BinpkgFetcher.py @@ -1,4 +1,4 @@ -# Copyright 1999-2012 Gentoo Foundation +# Copyright 1999-2013 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 from _emerge.AsynchronousLock import AsynchronousLock @@ -80,6 +80,12 @@ class BinpkgFetcher(SpawnProcess): "FILE" : os.path.basename(pkg_path) } + for k in ("PORTAGE_SSH_OPTS",): + try: + fcmd_vars[k] = settings[k] + except KeyError: + pass + fetch_env = dict(settings.items()) fetch_args = [portage.util.varexpand(x, mydict=fcmd_vars) \ for x in portage.util.shlex_split(fcmd)] diff --git a/pym/_emerge/actions.py b/pym/_emerge/actions.py index 3982eb363..a46f565a3 100644 --- a/pym/_emerge/actions.py +++ b/pym/_emerge/actions.py @@ -2253,6 +2253,9 @@ def action_sync(settings, trees, mtimedb, myopts, myaction): writemsg_level("!!! SYNC is invalid: %s\n" % syncuri, noiselevel=-1, level=logging.ERROR) return 1 + + ssh_opts = settings.get("PORTAGE_SSH_OPTS") + if port is None: port="" if user_name is None: @@ -2370,6 +2373,9 @@ def action_sync(settings, trees, mtimedb, myopts, myaction): rsynccommand = [rsync_binary] + rsync_opts + extra_rsync_opts + if proto == 'ssh' and ssh_opts: + rsynccommand.append("--rsh=ssh " + ssh_opts) + if "--debug" in myopts: print(rsynccommand) diff --git a/pym/portage/dbapi/bintree.py b/pym/portage/dbapi/bintree.py index 14d05ad48..77b288686 100644 --- a/pym/portage/dbapi/bintree.py +++ b/pym/portage/dbapi/bintree.py @@ -915,10 +915,18 @@ class binarytree(object): # Use a pipe so that we can terminate the download # early if we detect that the TIMESTAMP header # matches that of the cached Packages file. + ssh_args = ['ssh'] if port is not None: - port_args = ['-p', "%s" % (port,)] - proc = subprocess.Popen(['ssh'] + port_args + \ - [user_passwd + host, '--', 'cat', path], + ssh_args.append("-p%s" % (port,)) + # NOTE: shlex evaluates embedded quotes + ssh_args.extend(portage.util.shlex_split( + self.settings.get("PORTAGE_SSH_OPTS", ""))) + ssh_args.append(user_passwd + host) + ssh_args.append('--') + ssh_args.append('cat') + ssh_args.append(path) + + proc = subprocess.Popen(ssh_args, stdout=subprocess.PIPE) f = proc.stdout else: @@ -932,8 +940,21 @@ class binarytree(object): fd, tmp_filename = tempfile.mkstemp() tmp_dirname, tmp_basename = os.path.split(tmp_filename) os.close(fd) - success = portage.getbinpkg.file_get(url, - tmp_dirname, fcmd=fcmd, filename=tmp_basename) + + fcmd_vars = { + "DISTDIR": tmp_dirname, + "FILE": tmp_basename, + "URI": url + } + + for k in ("PORTAGE_SSH_OPTS",): + try: + fcmd_vars[k] = self.settings[k] + except KeyError: + pass + + success = portage.getbinpkg.file_get( + fcmd=fcmd, fcmd_vars=fcmd_vars) if not success: raise EnvironmentError("%s failed" % (setting,)) f = open(tmp_filename, 'rb') diff --git a/pym/portage/getbinpkg.py b/pym/portage/getbinpkg.py index 77c1c8f79..ff656ba39 100644 --- a/pym/portage/getbinpkg.py +++ b/pym/portage/getbinpkg.py @@ -477,7 +477,8 @@ def file_get_metadata(baseurl,conn=None, chunk_size=3000): return myid -def file_get(baseurl,dest,conn=None,fcmd=None,filename=None): +def file_get(baseurl=None, dest=None, conn=None, fcmd=None, filename=None, + fcmd_vars=None): """(baseurl,dest,fcmd=) -- Takes a base url to connect to and read from. URI should be in the form ://[user[:pass]@][:port]""" @@ -487,14 +488,30 @@ def file_get(baseurl,dest,conn=None,fcmd=None,filename=None): "parameter is deprecated", DeprecationWarning, stacklevel=2) return file_get_lib(baseurl,dest,conn) - if not filename: - filename = os.path.basename(baseurl) - variables = { - "DISTDIR": dest, - "URI": baseurl, - "FILE": filename - } + variables = {} + + if fcmd_vars is not None: + variables.update(fcmd_vars) + + if "DISTDIR" not in variables: + if dest is None: + raise portage.exception.MissingParameter( + _("%s is missing required '%s' key") % + ("fcmd_vars", "DISTDIR")) + variables["DISTDIR"] = dest + + if "URI" not in variables: + if baseurl is None: + raise portage.exception.MissingParameter( + _("%s is missing required '%s' key") % + ("fcmd_vars", "URI")) + variables["URI"] = baseurl + + if "FILE" not in variables: + if filename is None: + filename = os.path.basename(variables["URI"]) + variables["FILE"] = filename from portage.util import varexpand from portage.process import spawn diff --git a/pym/portage/package/ebuild/_config/special_env_vars.py b/pym/portage/package/ebuild/_config/special_env_vars.py index 96625713b..8b9cac12b 100644 --- a/pym/portage/package/ebuild/_config/special_env_vars.py +++ b/pym/portage/package/ebuild/_config/special_env_vars.py @@ -171,7 +171,7 @@ environ_filter += [ "PORTAGE_REPO_DUPLICATE_WARN", "PORTAGE_RO_DISTDIRS", "PORTAGE_RSYNC_EXTRA_OPTS", "PORTAGE_RSYNC_OPTS", - "PORTAGE_RSYNC_RETRIES", "PORTAGE_SYNC_STALE", + "PORTAGE_RSYNC_RETRIES", "PORTAGE_SSH_OPTS", "PORTAGE_SYNC_STALE", "PORTAGE_USE", "PORTAGE_XATTR_EXCLUDE", "PORT_LOGDIR", "PORT_LOGDIR_CLEAN", "QUICKPKG_DEFAULT_OPTS", "REPOMAN_DEFAULT_OPTS", diff --git a/pym/portage/package/ebuild/fetch.py b/pym/portage/package/ebuild/fetch.py index 0a7bf10bb..162c7c274 100644 --- a/pym/portage/package/ebuild/fetch.py +++ b/pym/portage/package/ebuild/fetch.py @@ -1,4 +1,4 @@ -# Copyright 2010-2012 Gentoo Foundation +# Copyright 2010-2013 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 from __future__ import print_function @@ -964,11 +964,16 @@ def fetch(myuris, mysettings, listonly=0, fetchonly=0, writemsg_stdout(_(">>> Downloading '%s'\n") % \ _hide_url_passwd(loc)) variables = { - "DISTDIR": mysettings["DISTDIR"], "URI": loc, "FILE": myfile } + for k in ("DISTDIR", "PORTAGE_SSH_OPTS"): + try: + variables[k] = mysettings[k] + except KeyError: + pass + myfetch = shlex_split(locfetch) myfetch = [varexpand(x, mydict=variables) for x in myfetch] myret = -1 -- 2.26.2