diff options
author | Carlos Martín Nieto <cmn@dwim.me> | 2014-07-04 10:00:39 +0200 |
---|---|---|
committer | Carlos Martín Nieto <cmn@dwim.me> | 2014-07-07 14:51:51 +0200 |
commit | d4256ed554fa64f762d53cb2a64663e5095d3eb5 (patch) | |
tree | 45a5b926a5a04f346a0feb10242012a2677e8d62 | |
parent | 9b87998c97fedcf0c22cef28bb0839ecd68c1efd (diff) | |
download | libgit2-cmn/ssh-factory-for-paths.tar.gz |
ssh: provide a factory function for setting ssh pathscmn/ssh-factory-for-paths
git allows you to set which paths to use for the git server programs
when connecting over ssh; and we want to provide something similar.
We do this by providing a factory function which can be set as the
remote's transport callback which will set the given paths upon
creation.
-rw-r--r-- | CHANGELOG.md | 4 | ||||
-rw-r--r-- | include/git2/transport.h | 16 | ||||
-rwxr-xr-x | script/cibuild.sh | 5 | ||||
-rw-r--r-- | src/transports/ssh.c | 54 | ||||
-rw-r--r-- | tests/online/clone.c | 65 |
5 files changed, 141 insertions, 3 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index f4714993a..d6d7438f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,10 @@ v0.21 + 1 * The git_remote_set_transport function now sets a transport factory function, rather than a pre-existing transport instance. +* A factory function for ssh has been added which allows to change the + path of the programs to execute for receive-pack and upload-pack on + the server, git_transport_ssh_with_paths. + * The git_clone_options struct no longer provides the ignore_cert_errors or remote_name members for remote customization. diff --git a/include/git2/transport.h b/include/git2/transport.h index 1df264ea1..67939a747 100644 --- a/include/git2/transport.h +++ b/include/git2/transport.h @@ -336,6 +336,22 @@ GIT_EXTERN(int) git_transport_init( */ GIT_EXTERN(int) git_transport_new(git_transport **out, git_remote *owner, const char *url); +/** + * Create an ssh transport with custom git command paths + * + * This is a factory function suitable for setting as the transport + * callback in a remote (or for a clone in the options). + * + * The payload argument must be a strarray pointer with the paths for + * the `git-upload-pack` and `git-receive-pack` at index 0 and 1. + * + * @param out the resulting transport + * @param owner the owning remote + * @param payload a strarray with the paths + * @return 0 or an error code + */ +GIT_EXTERN(int) git_transport_ssh_with_paths(git_transport **out, git_remote *owner, void *payload); + /* Signature of a function which creates a transport */ typedef int (*git_transport_cb)(git_transport **out, git_remote *owner, void *param); diff --git a/script/cibuild.sh b/script/cibuild.sh index 699404bd2..5ba07460c 100755 --- a/script/cibuild.sh +++ b/script/cibuild.sh @@ -34,5 +34,8 @@ export GITTEST_REMOTE_SSH_PUBKEY="$HOME/.ssh/id_rsa.pub" export GITTEST_REMOTE_SSH_PASSPHRASE="" if [ -e ./libgit2_clar ]; then - ./libgit2_clar -sonline::push -sonline::clone::cred_callback_failure + ./libgit2_clar -sonline::push -sonline::clone::cred_callback_failure && + rm -rf $HOME/_temp/test.git && + git init --bare $HOME/_temp/test.git && # create an empty one + ./libgit2_clar -sonline::clone::ssh_with_paths fi diff --git a/src/transports/ssh.c b/src/transports/ssh.c index a1081b3ff..f84ea4dec 100644 --- a/src/transports/ssh.c +++ b/src/transports/ssh.c @@ -37,6 +37,8 @@ typedef struct { transport_smart *owner; ssh_stream *current_stream; git_cred *cred; + char *cmd_uploadpack; + char *cmd_receivepack; } ssh_subtransport; static void ssh_error(LIBSSH2_SESSION *session, const char *errmsg) @@ -504,7 +506,9 @@ static int ssh_uploadpack_ls( const char *url, git_smart_subtransport_stream **stream) { - if (_git_ssh_setup_conn(t, url, cmd_uploadpack, stream) < 0) + const char *cmd = t->cmd_uploadpack ? t->cmd_uploadpack : cmd_uploadpack; + + if (_git_ssh_setup_conn(t, url, cmd, stream) < 0) return -1; return 0; @@ -531,7 +535,9 @@ static int ssh_receivepack_ls( const char *url, git_smart_subtransport_stream **stream) { - if (_git_ssh_setup_conn(t, url, cmd_receivepack, stream) < 0) + const char *cmd = t->cmd_receivepack ? t->cmd_receivepack : cmd_receivepack; + + if (_git_ssh_setup_conn(t, url, cmd, stream) < 0) return -1; return 0; @@ -596,6 +602,8 @@ static void _ssh_free(git_smart_subtransport *subtransport) assert(!t->current_stream); + git__free(t->cmd_uploadpack); + git__free(t->cmd_receivepack); git__free(t); } #endif @@ -628,3 +636,45 @@ int git_smart_subtransport_ssh( return -1; #endif } + +int git_transport_ssh_with_paths(git_transport **out, git_remote *owner, void *payload) +{ +#ifdef GIT_SSH + git_strarray *paths = (git_strarray *) payload; + git_transport *transport; + transport_smart *smart; + ssh_subtransport *t; + int error; + git_smart_subtransport_definition ssh_definition = { + git_smart_subtransport_ssh, + 0, /* no RPC */ + }; + + if (paths->count != 2) { + giterr_set(GITERR_SSH, "invalid ssh paths, must be two strings"); + return GIT_EINVALIDSPEC; + } + + if ((error = git_transport_smart(&transport, owner, &ssh_definition)) < 0) + return error; + + smart = (transport_smart *) transport; + t = (ssh_subtransport *) smart->wrapped; + + t->cmd_uploadpack = git__strdup(paths->strings[0]); + GITERR_CHECK_ALLOC(t->cmd_uploadpack); + t->cmd_receivepack = git__strdup(paths->strings[1]); + GITERR_CHECK_ALLOC(t->cmd_receivepack); + + *out = transport; + return 0; +#else + GIT_UNUSED(owner); + + assert(out); + *out = NULL; + + giterr_set(GITERR_INVALID, "Cannot create SSH transport. Library was built without SSH support"); + return -1; +#endif +} diff --git a/tests/online/clone.c b/tests/online/clone.c index 2e2e97675..b672a099a 100644 --- a/tests/online/clone.c +++ b/tests/online/clone.c @@ -288,8 +288,73 @@ void test_online_clone__can_cancel(void) git_clone(&g_repo, LIVE_REPO_URL, "./foo", &g_options), 4321); } +static int cred_cb(git_cred **cred, const char *url, const char *user_from_url, + unsigned int allowed_types, void *payload) +{ + const char *remote_user = cl_getenv("GITTEST_REMOTE_USER"); + const char *pubkey = cl_getenv("GITTEST_REMOTE_SSH_PUBKEY"); + const char *privkey = cl_getenv("GITTEST_REMOTE_SSH_KEY"); + const char *passphrase = cl_getenv("GITTEST_REMOTE_SSH_PASSPHRASE"); + + GIT_UNUSED(url); GIT_UNUSED(user_from_url); GIT_UNUSED(payload); + + if (allowed_types & GIT_CREDTYPE_SSH_KEY) + return git_cred_ssh_key_new(cred, remote_user, pubkey, privkey, passphrase); + + giterr_set(GITERR_NET, "unexpected cred type"); + return -1; +} + +static int custom_remote_ssh_with_paths( + git_remote **out, + git_repository *repo, + const char *name, + const char *url, + void *payload) +{ + int error; + + git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT; + + if ((error = git_remote_create(out, repo, name, url)) < 0) + return error; + + if ((error = git_remote_set_transport(*out, git_transport_ssh_with_paths, payload)) < 0) + return error; + callbacks.credentials = cred_cb; + git_remote_set_callbacks(*out, &callbacks); + return 0; +} + +void test_online_clone__ssh_with_paths(void) +{ + char *bad_paths[] = { + "/bin/yes", + "/bin/false", + }; + char *good_paths[] = { + "/usr/bin/git-upload-pack", + "/usr/bin/git-receive-pack", + }; + git_strarray arr = { + bad_paths, + 2, + }; + + const char *remote_url = cl_getenv("GITTEST_REMOTE_URL"); + const char *remote_user = cl_getenv("GITTEST_REMOTE_USER"); + + if (!remote_url || !remote_user) + clar__skip(); + + g_options.remote_cb = custom_remote_ssh_with_paths; + g_options.remote_cb_payload = &arr; + cl_git_fail(git_clone(&g_repo, remote_url, "./foo", &g_options)); + arr.strings = good_paths; + cl_git_pass(git_clone(&g_repo, remote_url, "./foo", &g_options)); +} |