OpenSSH has the nice feature that it sets the IP TOS value of its connection depending on usage. When used in interactive mode, it is set to Minimize-Delay, and other wise to Maximize-Throughput. Its usage by Git is best served by Maximize-Throughput, for obvious reasons. However, it seems to use a DWIM heuristic for detecting interactive mode. The current implementation enters interactive mode if either a PTY is allocated or X11 forwarding is enabled, and even though Git SSH:ing does not allocate a PTY, X11 forwarding is often turned on by default. This patch allows the Git config file to specify the SSH command to use and its parameters in a rather flexible manner. It should also be enough to configure Git to use other SSH implementations than OpenSSH. Signed-off-by: Fredrik Tolf <fredrik@xxxxxxxxxxxxx> --- I'm following my previous SSH patch up with this one, which should at least solve the problems discussed, and probably some more. If anything, it might be considered a bit overkill for the problem at hand. I assume it might have to be documented as well, if people approve of it. connect.c | 130 ++++++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 files changed, 117 insertions(+), 13 deletions(-) diff --git a/connect.c b/connect.c index 574f42f..46379fa 100644 --- a/connect.c +++ b/connect.c @@ -474,6 +474,114 @@ char *get_port(char *host) return NULL; } +static char *git_ssh_command; +static struct { + char *name; + char *exp; +} ssh_templates[] = { + {"openssh", "ssh -xT %P-p %p %h"}, + {"plink", "plink %P-P %p %h"}, + {NULL, NULL} +}; + +static int git_ssh_command_options(const char *var, const char *value, void *cb) +{ + int i; + + if(!strcmp(var, "core.sshcommand")) { + if(git_ssh_command) + return 0; + if(!value) + return config_error_nonbool(var); + if(strchr(value, ' ')) { + git_ssh_command = xstrdup(value); + } else { + for(i = 0; ssh_templates[i].name; i++) { + if(!strcmp(ssh_templates[i].name, value)) { + git_ssh_command = xstrdup(ssh_templates[i].exp); + break; + } + } + if(git_ssh_command == NULL) + git_ssh_command = xstrdup(value); + } + } + + return git_default_config(var, value, cb); +} + +static char *ssh_arg_subst(char *arg, const char *host, const char *port) +{ + if(!strncmp(arg, "%P", 2)) { + if(!port) + return NULL; + return xstrdup(arg + 2); + } else if(!strcmp(arg, "%p")) { + if(!port) + return NULL; + return xstrdup(port); + } else if(!strcmp(arg, "%h")) { + return xstrdup(host); + } + return arg; +} + +static const char **setup_ssh_command(const char *host, const char *port, int extra_args) +{ + char **argv; + char *tok, *buf; + int i, sz; + + if((buf = getenv("GIT_SSH")) != NULL) { + if(port) { + argv = xcalloc(5 + extra_args, sizeof(*argv)); + argv[0] = xstrdup(buf); + argv[1] = xstrdup("-p"); + argv[2] = xstrdup(port); + argv[3] = xstrdup(host); + } else { + argv = xcalloc(3 + extra_args, sizeof(*argv)); + argv[0] = xstrdup(buf); + argv[1] = xstrdup(host); + } + return (const char **)argv; + } + + git_config(git_ssh_command_options, NULL); + if(git_ssh_command == NULL) + buf = xstrdup(ssh_templates[0].exp); + else + buf = xstrdup(git_ssh_command); + + argv = xmalloc((sz = 5) * sizeof(*argv)); + for(i = 0, tok = strtok(buf, " "); tok != NULL; tok = strtok(NULL, " ")) { + argv[i++] = xstrdup(tok); + if(sz - i < 1) + argv = xrealloc(argv, (sz += 5) * sizeof(*argv)); + } + argv[i] = NULL; + argv = xrealloc(argv, ((sz = i) + extra_args + 1) * sizeof(*argv)); + free(buf); + + for(i = 0; i < sz;) { + buf = ssh_arg_subst(argv[i], host, port); + if(buf == argv[i]) { + i++; + continue; + } else if(buf == NULL) { + free(argv[i]); + memmove(argv + i, argv + i + 1, sizeof(*argv) * (sz-- - i)); + continue; + } else { + free(argv[i]); + argv[i] = buf; + i++; + } + } + + return (const char **)argv; +} + static struct child_process no_fork; /* @@ -596,19 +704,12 @@ struct child_process *git_connect(int fd[2], const char *url_orig, die("command line too long"); conn->in = conn->out = -1; - conn->argv = arg = xcalloc(6, sizeof(*arg)); if (protocol == PROTO_SSH) { - const char *ssh = getenv("GIT_SSH"); - if (!ssh) ssh = "ssh"; - - *arg++ = ssh; - if (port) { - *arg++ = "-p"; - *arg++ = port; - } - *arg++ = host; + conn->argv = setup_ssh_command(host, port, 1); + for(arg = conn->argv; *arg; arg++); } else { + conn->argv = arg = xcalloc(6, sizeof(*arg)); /* remove these from the environment */ const char *env[] = { ALTERNATE_DB_ENVIRONMENT, @@ -620,10 +721,10 @@ struct child_process *git_connect(int fd[2], const char *url_orig, NULL }; conn->env = env; - *arg++ = "sh"; - *arg++ = "-c"; + *arg++ = xstrdup("sh"); + *arg++ = xstrdup("-c"); } - *arg++ = cmd.buf; + *arg++ = xstrdup(cmd.buf); *arg = NULL; if (start_command(conn)) @@ -640,11 +741,14 @@ struct child_process *git_connect(int fd[2], const char *url_orig, int finish_connect(struct child_process *conn) { + const char **argp; int code; if (!conn || conn == &no_fork) return 0; code = finish_command(conn); + for(argp = conn->argv; *argp; argp++) + free((void *)*argp); free(conn->argv); free(conn); return code; -- 1.5.6.2 -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html