diff --git a/ci/test.sh b/ci/test.sh index e53313624..9a24398ca 100755 --- a/ci/test.sh +++ b/ci/test.sh @@ -414,6 +414,8 @@ if [ -z "$SKIP_SSH_TESTS" ]; then export GITTEST_REMOTE_SSH_PASSPHRASE="" export GITTEST_REMOTE_SSH_FINGERPRINT="${SSH_FINGERPRINT}" + export GITTEST_SSH_CMD="ssh -i ${HOME}/.ssh/id_rsa -o UserKnownHostsFile=${HOME}/.ssh/known_hosts" + echo "" echo "Running ssh tests" echo "" @@ -430,6 +432,8 @@ if [ -z "$SKIP_SSH_TESTS" ]; then run_test ssh unset GITTEST_REMOTE_URL + unset GITTEST_SSH_CMD + unset GITTEST_REMOTE_USER unset GITTEST_REMOTE_SSH_KEY unset GITTEST_REMOTE_SSH_PUBKEY diff --git a/src/libgit2/transports/ssh.c b/src/libgit2/transports/ssh.c index 98e1be2d1..1155e18a1 100644 --- a/src/libgit2/transports/ssh.c +++ b/src/libgit2/transports/ssh.c @@ -64,6 +64,8 @@ int git_transport_ssh_with_paths( *out = transport; return 0; +#elif GIT_SSH_EXEC + abort(); #else GIT_UNUSED(out); GIT_UNUSED(owner); diff --git a/src/libgit2/transports/ssh_exec.c b/src/libgit2/transports/ssh_exec.c index 61b30ee52..661cd4977 100644 --- a/src/libgit2/transports/ssh_exec.c +++ b/src/libgit2/transports/ssh_exec.c @@ -11,10 +11,12 @@ #include "common.h" +#include "config.h" #include "net.h" #include "path.h" #include "futils.h" #include "process.h" +#include "transports/smart.h" typedef struct { git_smart_subtransport_stream parent; @@ -114,17 +116,54 @@ GIT_INLINE(int) ensure_transport_state( return 0; } +static int get_ssh_cmdline( + git_str *out, + ssh_exec_subtransport *transport, + git_net_url *url, + const char *command) +{ + git_remote *remote = ((transport_smart *)transport->owner)->owner; + git_repository *repo = remote->repo; + git_config *cfg; + git_str ssh_cmd = GIT_STR_INIT; + const char *default_ssh_cmd = "ssh"; + int error; + + if ((error = git_repository_config_snapshot(&cfg, repo)) < 0) + return error; + + if ((error = git__getenv(&ssh_cmd, "GIT_SSH")) == 0) + ; + else if (error != GIT_ENOTFOUND) + goto done; + else if ((error = git_config__get_string_buf(&ssh_cmd, cfg, "core.sshcommand")) < 0 && error != GIT_ENOTFOUND) + goto done; + + error = git_str_printf(out, "%s -p %s \"%s%s%s\" \"%s\" \"%s\"", + ssh_cmd.size > 0 ? ssh_cmd.ptr : default_ssh_cmd, + url->port, + url->username ? url->username : "", + url->username ? "@" : "", + url->host, + command, + url->path); + +done: + git_str_dispose(&ssh_cmd); + git_config_free(cfg); + return error; +} + static int start_ssh( ssh_exec_subtransport *transport, git_smart_service_t action, const char *sshpath) { - const char *args[6]; const char *env[] = { "GIT_DIR=" }; git_process_options process_opts = GIT_PROCESS_OPTIONS_INIT; git_net_url url = GIT_NET_URL_INIT; - git_str userhost = GIT_STR_INIT; + git_str ssh_cmdline = GIT_STR_INIT; const char *command; int error; @@ -153,20 +192,11 @@ static int start_ssh( if (error < 0) goto done; - if (url.username) { - git_str_puts(&userhost, url.username); - git_str_putc(&userhost, '@'); - } - git_str_puts(&userhost, url.host); + if ((error = get_ssh_cmdline(&ssh_cmdline, transport, &url, command)) < 0) + goto done; - args[0] = "/usr/bin/ssh"; - args[1] = "-p"; - args[2] = url.port; - args[3] = userhost.ptr; - args[4] = command; - args[5] = url.path; - - if ((error = git_process_new(&transport->process, args, ARRAY_SIZE(args), env, ARRAY_SIZE(env), &process_opts)) < 0 || + if ((error = git_process_new_from_cmdline(&transport->process, + ssh_cmdline.ptr, env, ARRAY_SIZE(env), &process_opts)) < 0 || (error = git_process_start(transport->process)) < 0) { git_process_free(transport->process); transport->process = NULL; @@ -174,7 +204,7 @@ static int start_ssh( } done: - git_str_dispose(&userhost); + git_str_dispose(&ssh_cmdline); git_net_url_dispose(&url); return error; } diff --git a/tests/libgit2/online/clone.c b/tests/libgit2/online/clone.c index 1dc76d507..7a8f5ead7 100644 --- a/tests/libgit2/online/clone.c +++ b/tests/libgit2/online/clone.c @@ -47,6 +47,9 @@ static char *_orig_http_proxy = NULL; static char *_orig_https_proxy = NULL; static char *_orig_no_proxy = NULL; +static char *_ssh_cmd = NULL; +static char *_orig_ssh_cmd = NULL; + static int ssl_cert(git_cert *cert, int valid, const char *host, void *payload) { GIT_UNUSED(cert); @@ -102,6 +105,14 @@ void test_online_clone__initialize(void) _orig_https_proxy = cl_getenv("HTTPS_PROXY"); _orig_no_proxy = cl_getenv("NO_PROXY"); + _orig_ssh_cmd = cl_getenv("GIT_SSH"); + _ssh_cmd = cl_getenv("GITTEST_SSH_CMD"); + + if (_ssh_cmd) + cl_setenv("GIT_SSH", _ssh_cmd); + else + cl_setenv("GIT_SSH", NULL); + if (_remote_expectcontinue) git_libgit2_opts(GIT_OPT_ENABLE_HTTP_EXPECT_CONTINUE, 1); } @@ -149,6 +160,11 @@ void test_online_clone__cleanup(void) git__free(_orig_https_proxy); git__free(_orig_no_proxy); + cl_setenv("GIT_SSH", _orig_ssh_cmd); + git__free(_orig_ssh_cmd); + + git__free(_ssh_cmd); + git_libgit2_opts(GIT_OPT_SET_SSL_CERT_LOCATIONS, NULL, NULL); git_libgit2_opts(GIT_OPT_SET_SERVER_TIMEOUT, 0); git_libgit2_opts(GIT_OPT_SET_SERVER_CONNECT_TIMEOUT, 0); diff --git a/tests/libgit2/online/push.c b/tests/libgit2/online/push.c index 204572cf5..b18402bf1 100644 --- a/tests/libgit2/online/push.c +++ b/tests/libgit2/online/push.c @@ -20,6 +20,9 @@ static char *_remote_ssh_passphrase = NULL; static char *_remote_default = NULL; static char *_remote_expectcontinue = NULL; +static char *_orig_ssh_cmd = NULL; +static char *_ssh_cmd = NULL; + static int cred_acquire_cb(git_credential **, const char *, const char *, unsigned int, void *); static git_remote *_remote; @@ -369,6 +372,14 @@ void test_online_push__initialize(void) _remote_expectcontinue = cl_getenv("GITTEST_REMOTE_EXPECTCONTINUE"); _remote = NULL; + _orig_ssh_cmd = cl_getenv("GIT_SSH"); + _ssh_cmd = cl_getenv("GITTEST_SSH_CMD"); + + if (_ssh_cmd) + cl_setenv("GIT_SSH", _ssh_cmd); + else + cl_setenv("GIT_SSH", NULL); + /* Skip the test if we're missing the remote URL */ if (!_remote_url) cl_skip(); @@ -423,6 +434,9 @@ void test_online_push__cleanup(void) git__free(_remote_default); git__free(_remote_expectcontinue); + git__free(_orig_ssh_cmd); + git__free(_ssh_cmd); + /* Freed by cl_git_sandbox_cleanup */ _repo = NULL;