aboutsummaryrefslogtreecommitdiff
path: root/connect.c
diff options
context:
space:
mode:
authorJonathan Nieder <jrnieder@gmail.com>2017-11-20 13:30:04 -0800
committerJunio C Hamano <gitster@pobox.com>2017-11-21 14:01:03 +0900
commit0da0e49ba12225684b75e86a4c9344ad121652cb (patch)
treed067e52acdec5b2a7d0b6933e9d72f7aecb20f6f /connect.c
parent957e2ad28290076fffe3bf28ae8609c637cf8151 (diff)
downloadgit-0da0e49ba12225684b75e86a4c9344ad121652cb.tar.gz
git-0da0e49ba12225684b75e86a4c9344ad121652cb.tar.xz
ssh: 'auto' variant to select between 'ssh' and 'simple'
Android's "repo" tool is a tool for managing a large codebase consisting of multiple smaller repositories, similar to Git's submodule feature. Starting with Git 94b8ae5a (ssh: introduce a 'simple' ssh variant, 2017-10-16), users noticed that it stopped handling the port in ssh:// URLs. The cause: when it encounters ssh:// URLs, repo pre-connects to the server and sets GIT_SSH to a helper ".repo/repo/git_ssh" that reuses that connection. Before 94b8ae5a, the helper was assumed to support OpenSSH options for lack of a better guess and got passed a -p option to set the port. After that patch, it uses the new default of a simple helper that does not accept an option to set the port. The next release of "repo" will set GIT_SSH_VARIANT to "ssh" to avoid that. But users of old versions and of other similar GIT_SSH implementations would not get the benefit of that fix. So update the default to use OpenSSH options again, with a twist. As observed in 94b8ae5a, we cannot assume that $GIT_SSH always handles OpenSSH options: common helpers such as travis-ci's dpl[*] are configured using GIT_SSH and do not accept OpenSSH options. So make the default a new variant "auto", with the following behavior: 1. First, check for a recognized basename, like today. 2. If the basename is not recognized, check whether $GIT_SSH supports OpenSSH options by running $GIT_SSH -G <options> <host> This returns status 0 and prints configuration in OpenSSH if it recognizes all <options> and returns status 255 if it encounters an unrecognized option. A wrapper script like exec ssh -- "$@" would fail with ssh: Could not resolve hostname -g: Name or service not known , correctly reflecting that it does not support OpenSSH options. The command is run with stdin, stdout, and stderr redirected to /dev/null so even a command that expects a terminal would exit immediately. 3. Based on the result from step (2), behave like "ssh" (if it succeeded) or "simple" (if it failed). This way, the default ssh variant for unrecognized commands can handle both the repo and dpl cases as intended. This autodetection has been running on Google workstations since 2017-10-23 with no reported negative effects. [*] https://github.com/travis-ci/dpl/blob/6c3fddfda1f2a85944c544446b068bac0a77c049/lib/dpl/provider.rb#L215 Reported-by: William Yan <wyan@google.com> Improved-by: Jonathan Tan <jonathantanmy@google.com> Signed-off-by: Jonathan Nieder <jrnieder@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Diffstat (limited to 'connect.c')
-rw-r--r--connect.c32
1 files changed, 25 insertions, 7 deletions
diff --git a/connect.c b/connect.c
index f3370510a..a939323d0 100644
--- a/connect.c
+++ b/connect.c
@@ -788,6 +788,7 @@ static const char *get_ssh_command(void)
}
enum ssh_variant {
+ VARIANT_AUTO,
VARIANT_SIMPLE,
VARIANT_SSH,
VARIANT_PLINK,
@@ -795,14 +796,16 @@ enum ssh_variant {
VARIANT_TORTOISEPLINK,
};
-static int override_ssh_variant(enum ssh_variant *ssh_variant)
+static void override_ssh_variant(enum ssh_variant *ssh_variant)
{
const char *variant = getenv("GIT_SSH_VARIANT");
if (!variant && git_config_get_string_const("ssh.variant", &variant))
- return 0;
+ return;
- if (!strcmp(variant, "plink"))
+ if (!strcmp(variant, "auto"))
+ *ssh_variant = VARIANT_AUTO;
+ else if (!strcmp(variant, "plink"))
*ssh_variant = VARIANT_PLINK;
else if (!strcmp(variant, "putty"))
*ssh_variant = VARIANT_PUTTY;
@@ -812,18 +815,18 @@ static int override_ssh_variant(enum ssh_variant *ssh_variant)
*ssh_variant = VARIANT_SIMPLE;
else
*ssh_variant = VARIANT_SSH;
-
- return 1;
}
static enum ssh_variant determine_ssh_variant(const char *ssh_command,
int is_cmdline)
{
- enum ssh_variant ssh_variant = VARIANT_SIMPLE;
+ enum ssh_variant ssh_variant = VARIANT_AUTO;
const char *variant;
char *p = NULL;
- if (override_ssh_variant(&ssh_variant))
+ override_ssh_variant(&ssh_variant);
+
+ if (ssh_variant != VARIANT_AUTO)
return ssh_variant;
if (!is_cmdline) {
@@ -982,6 +985,21 @@ static void fill_ssh_args(struct child_process *conn, const char *ssh_host,
variant = determine_ssh_variant(ssh, 0);
}
+ if (variant == VARIANT_AUTO) {
+ struct child_process detect = CHILD_PROCESS_INIT;
+
+ detect.use_shell = conn->use_shell;
+ detect.no_stdin = detect.no_stdout = detect.no_stderr = 1;
+
+ argv_array_push(&detect.args, ssh);
+ argv_array_push(&detect.args, "-G");
+ push_ssh_options(&detect.args, &detect.env_array,
+ VARIANT_SSH, port, flags);
+ argv_array_push(&detect.args, ssh_host);
+
+ variant = run_command(&detect) ? VARIANT_SIMPLE : VARIANT_SSH;
+ }
+
argv_array_push(&conn->args, ssh);
push_ssh_options(&conn->args, &conn->env_array, variant, port, flags);
argv_array_push(&conn->args, ssh_host);