Support PORTAGE_SSH_OPTS, bug #470002.
authorZac Medico <zmedico@gentoo.org>
Sat, 18 May 2013 22:24:05 +0000 (15:24 -0700)
committerZac Medico <zmedico@gentoo.org>
Sat, 18 May 2013 22:24:05 +0000 (15:24 -0700)
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
man/make.conf.5
pym/_emerge/BinpkgFetcher.py
pym/_emerge/actions.py
pym/portage/dbapi/bintree.py
pym/portage/getbinpkg.py
pym/portage/package/ebuild/_config/special_env_vars.py
pym/portage/package/ebuild/fetch.py

index 5f5098b0657f28582c497db9eb76767308c8d702..1f07df39ba2695f8da1ca9b5de713aa69a1a3c46 100644 (file)
@@ -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
index 9b4121ce6582d60b4a44f9498a4d6da0bf1fca4b..fed71f55e2b7e78ec924a61fd6487c8ba3976a6d 100644 (file)
@@ -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.
index 099d3c498a7c62277ae0d37e4918b67d48eb5d4a..543881ee6722ebb235bc589ce52f070d09741ec0 100644 (file)
@@ -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)]
index 3982eb3636ccb50a5db237e0527e01ba35db8acd..a46f565a3e39b555aac1e30feb6e5acab2ca9d73 100644 (file)
@@ -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)
 
index 14d05ad489e121a0127491679bb32e9ad891f084..77b28868623e896f256647c1aa2957babae4719a 100644 (file)
@@ -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')
index 77c1c8f7968df16069fb84e465cbc2f31fdeb67c..ff656ba392f3d903ebbf5d1fcec829e6e1796eda 100644 (file)
@@ -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 <proto>://[user[:pass]@]<site>[:port]<path>"""
 
@@ -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
index 96625713bfc66ec8779fe72e08ada15823139601..8b9cac12bf35362636b2ab4c9ec7489e84c14824 100644 (file)
@@ -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",
index 0a7bf10bba27590a245116bc0e55e7a84feb6eb8..162c7c274fdd8daf6e0f35143f1d82e53d07d3c6 100644 (file)
@@ -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