Invokes specified command and directs smart transport streams to its stdin/stdout. Handy for e.g. invoking ssh with some one-off parameters. Signed-off-by: Ilari Liusvaara <ilari.liusvaara@xxxxxxxxxxx> --- Example URL: "ext::ssh -i foo.pub user@xxxxxxxxxxxx \\S 'some/repo'". Makefile | 1 + builtin.h | 1 + builtin/remote-ext.c | 195 ++++++++++++++++++++++++++++++++++++++++++++++++++ git.c | 1 + 4 files changed, 198 insertions(+), 0 deletions(-) create mode 100644 builtin/remote-ext.c diff --git a/Makefile b/Makefile index ad53b52..88e752f 100644 --- a/Makefile +++ b/Makefile @@ -702,6 +702,7 @@ BUILTIN_OBJS += builtin/read-tree.o BUILTIN_OBJS += builtin/receive-pack.o BUILTIN_OBJS += builtin/reflog.o BUILTIN_OBJS += builtin/remote.o +BUILTIN_OBJS += builtin/remote-ext.o BUILTIN_OBJS += builtin/remote-fd.o BUILTIN_OBJS += builtin/replace.o BUILTIN_OBJS += builtin/rerere.o diff --git a/builtin.h b/builtin.h index af60b28..eb9074d 100644 --- a/builtin.h +++ b/builtin.h @@ -139,5 +139,6 @@ extern int cmd_show_ref(int argc, const char **argv, const char *prefix); extern int cmd_pack_refs(int argc, const char **argv, const char *prefix); extern int cmd_replace(int argc, const char **argv, const char *prefix); extern int cmd_remote_fd(int argc, const char **argv, const char *prefix); +extern int cmd_remote_ext(int argc, const char **argv, const char *prefix); #endif diff --git a/builtin/remote-ext.c b/builtin/remote-ext.c new file mode 100644 index 0000000..e6caca4 --- /dev/null +++ b/builtin/remote-ext.c @@ -0,0 +1,195 @@ +#include "git-compat-util.h" +#include "transport.h" +#include "run-command.h" +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <unistd.h> + +/* + * URL syntax: + * 'command [arg1 [arg2 [...]]]' Invoke command with given arguments. + * Special characters: + * '\ ': Literal space in argument. + * '\\': Literal backslash. + * '\S': Name of service (git-upload-pack/git-upload-archive/ + * git-receive-pack. + * '\s': Same as \s, but with possible git- prefix stripped. + */ + +static char *strip_escapes(const char *str, const char *service, + const char **next) +{ + char *ret; + size_t rpos = 0; + size_t wpos = 0; + size_t finallen = 0; + int escape = 0; + size_t pslen = 0; + size_t pSlen = 0; + size_t psoff = 0; + + /* Calculate prefix length for \s and lengths for \s and \S */ + if(!strncmp(service, "git-", 4)) + psoff = 4; + pSlen = strlen(service); + pslen = pSlen - psoff; + + /* Calculate output length and start of next argument. */ + while (str[rpos] && (escape || str[rpos] != ' ')) { + if (escape) { + switch (str[rpos]) { + case ' ': + case '\\': + finallen++; + break; + case 's': + finallen += pslen; + break; + case 'S': + finallen += pSlen; + break; + default: + die("Bad remote-ext placeholder '\\%c'.", + str[rpos]); + } + escape = 0; + } else + switch (str[rpos]) { + case '\\': + escape = 1; + break; + default: + finallen++; + break; + } + rpos++; + } + if (escape && !str[rpos]) + die("remote-ext command has incomplete placeholder"); + *next = str + rpos; + if (**next == ' ') + ++*next; /* Skip over space */ + + /* + * Do the actual placeholder substitution. The string will be short + * enough not to overflow integers. + */ + ret = xmalloc(finallen + 1); + rpos = 0; + escape = 0; + while (str[rpos] && (escape || str[rpos] != ' ')) { + if (escape) { + switch(str[rpos]) { + case ' ': + case '\\': + ret[wpos++] = str[rpos]; + break; + case 's': + strcpy(ret + wpos, service + psoff); + wpos += pslen; + break; + case 'S': + strcpy(ret + wpos, service); + wpos += pSlen; + break; + } + escape = 0; + } else + switch(str[rpos]) { + case '\\': + escape = 1; + break; + default: + ret[wpos++] = str[rpos]; + break; + } + rpos++; + } + ret[wpos] = 0; + return ret; +} + +/* Should be enough... */ +#define MAXARGUMENTS 256 + +static const char **parse_argv(const char *arg, const char *service) +{ + int arguments = 0; + int i; + char** ret; + char *(temparray[MAXARGUMENTS + 1]); + + while (*arg) { + if(arguments == MAXARGUMENTS) + die("remote-ext command has too many arguments"); + temparray[arguments++] = strip_escapes(arg, service, &arg); + } + + ret = xcalloc(arguments + 1, sizeof(char*)); + for (i = 0; i < arguments; i++) + ret[i] = temparray[i]; + + return (const char**)ret; +} + +static int run_child(const char *arg, const char *service) +{ + int r; + struct child_process child; + + memset(&child, 0, sizeof(child)); + child.in = -1; + child.out = -1; + child.err = 0; + child.argv = parse_argv(arg, service); + + if (start_command(&child) < 0) { + fprintf(stderr, "fatal: Can't run specified command"); + return 128; + } + r = bidirectional_transfer_loop(child.out, child.in); + if (!r) + r = finish_command(&child); + else + finish_command(&child); + return r; +} + +#define MAXCOMMAND 4096 + +static int command_loop(const char *child) +{ + char buffer[MAXCOMMAND]; + + while (1) { + if (!fgets(buffer, MAXCOMMAND - 1, stdin)) + exit(0); + //Strip end of line characters. + while (isspace((unsigned char)buffer[strlen(buffer) - 1])) + buffer[strlen(buffer) - 1] = 0; + + if (!strcmp(buffer, "capabilities")) { + printf("*connect\n\n"); + fflush(stdout); + } else if (!strncmp(buffer, "connect ", 8)) { + printf("\n"); + fflush(stdout); + return run_child(child, buffer + 8); + } else { + fprintf(stderr, "Bad command"); + return 1; + } + } +} + +int cmd_remote_ext(int argc, const char **argv, const char *prefix) +{ + if (argc < 3) { + fprintf(stderr, "Error: URL missing"); + exit(1); + } + + return command_loop(argv[2]); +} diff --git a/git.c b/git.c index 91de3d6..2cc0826 100644 --- a/git.c +++ b/git.c @@ -368,6 +368,7 @@ static void handle_internal_command(int argc, const char **argv) { "receive-pack", cmd_receive_pack }, { "reflog", cmd_reflog, RUN_SETUP }, { "remote", cmd_remote, RUN_SETUP }, + { "remote-ext", cmd_remote_ext, 0 }, { "remote-fd", cmd_remote_fd, 0 }, { "replace", cmd_replace, RUN_SETUP }, { "repo-config", cmd_config }, -- 1.7.1 -- 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