From 3a9ed4bdee5032ecb9328aea51a752d69a2d966e Mon Sep 17 00:00:00 2001 From: Ilari Liusvaara Date: Tue, 12 Oct 2010 19:39:42 +0300 Subject: git-remote-fd This remote helper reflects raw smart remote transport stream back to the calling program. This is useful for example if some UI wants to handle ssh itself and not use hacks via GIT_SSH. Signed-off-by: Ilari Liusvaara Signed-off-by: Junio C Hamano --- builtin/remote-fd.c | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 builtin/remote-fd.c (limited to 'builtin') diff --git a/builtin/remote-fd.c b/builtin/remote-fd.c new file mode 100644 index 0000000000..7517f2476c --- /dev/null +++ b/builtin/remote-fd.c @@ -0,0 +1,79 @@ +#include "git-compat-util.h" +#include "transport.h" + +/* + * URL syntax: + * 'fd::[/]' Read/write socket pair + * . + * 'fd::,[/]' Read pipe and write + * pipe . + * [foo] indicates 'foo' is optional. is any string. + * + * The data output to / should be passed unmolested to + * git-receive-pack/git-upload-pack/git-upload-archive and output of + * git-receive-pack/git-upload-pack/git-upload-archive should be passed + * unmolested to /. + * + */ + +#define MAXCOMMAND 4096 + +static void command_loop(int input_fd, int output_fd) +{ + char buffer[MAXCOMMAND]; + + while (1) { + size_t i; + if (!fgets(buffer, MAXCOMMAND - 1, stdin)) { + if (ferror(stdin)) + die("Input error"); + return; + } + /* Strip end of line characters. */ + i = strlen(buffer); + while (isspace(buffer[i - 1])) + buffer[--i] = 0; + + if (!strcmp(buffer, "capabilities")) { + printf("*connect\n\n"); + fflush(stdout); + } else if (!strncmp(buffer, "connect ", 8)) { + printf("\n"); + fflush(stdout); + if (bidirectional_transfer_loop(input_fd, + output_fd)) + die("Copying data between file descriptors failed"); + return; + } else { + die("Bad command: %s", buffer); + } + } +} + +int cmd_remote_fd(int argc, const char **argv, const char *prefix) +{ + int input_fd = -1; + int output_fd = -1; + char *end; + + if (argc < 3) + die("URL missing"); + + input_fd = (int)strtoul(argv[2], &end, 10); + + if ((end == argv[2]) || (*end != ',' && *end != '/' && *end)) + die("Bad URL syntax"); + + if (*end == '/' || !*end) { + output_fd = input_fd; + } else { + char *end2; + output_fd = (int)strtoul(end + 1, &end2, 10); + + if ((end2 == end + 1) || (*end2 != '/' && *end2)) + die("Bad URL syntax"); + } + + command_loop(input_fd, output_fd); + return 0; +} -- cgit v1.2.1 From 7f3ecebfcda9b32252a4791dc716c1e3d88c9f70 Mon Sep 17 00:00:00 2001 From: Ilari Liusvaara Date: Tue, 12 Oct 2010 19:39:43 +0300 Subject: git-remote-ext This remote helper invokes external command and passes raw smart transport stream through it. This is useful for instance for invoking ssh with one-off odd options, connecting to git services in unix domain sockets, in abstract namespace, using TLS or other secure protocols, etc... Signed-off-by: Ilari Liusvaara Signed-off-by: Junio C Hamano --- builtin/remote-ext.c | 248 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 248 insertions(+) create mode 100644 builtin/remote-ext.c (limited to 'builtin') diff --git a/builtin/remote-ext.c b/builtin/remote-ext.c new file mode 100644 index 0000000000..1d3f1cf3cc --- /dev/null +++ b/builtin/remote-ext.c @@ -0,0 +1,248 @@ +#include "git-compat-util.h" +#include "transport.h" +#include "run-command.h" + +/* + * URL syntax: + * 'command [arg1 [arg2 [...]]]' Invoke command with given arguments. + * Special characters: + * '% ': Literal space in argument. + * '%%': Literal percent sign. + * '%S': Name of service (git-upload-pack/git-upload-archive/ + * git-receive-pack. + * '%s': Same as \s, but with possible git- prefix stripped. + * '%G': Only allowed as first 'character' of argument. Do not pass this + * Argument to command, instead send this as name of repository + * in in-line git://-style request (also activates sending this + * style of request). + * '%V': Only allowed as first 'character' of argument. Used in + * conjunction with '%G': Do not pass this argument to command, + * instead send this as vhost in git://-style request (note: does + * not activate sending git:// style request). + */ + +static char *git_req; +static char *git_req_vhost; + +static char *strip_escapes(const char *str, const char *service, + const char **next) +{ + size_t rpos = 0; + int escape = 0; + char special = 0; + size_t pslen = 0; + size_t pSlen = 0; + size_t psoff = 0; + struct strbuf ret = STRBUF_INIT; + + /* Calculate prefix length for \s and lengths for \s and \S */ + if (!strncmp(service, "git-", 4)) + psoff = 4; + pSlen = strlen(service); + pslen = pSlen - psoff; + + /* Pass the service to command. */ + setenv("GIT_EXT_SERVICE", service, 1); + setenv("GIT_EXT_SERVICE_NOPREFIX", service + psoff, 1); + + /* Scan the length of argument. */ + while (str[rpos] && (escape || str[rpos] != ' ')) { + if (escape) { + switch (str[rpos]) { + case ' ': + case '%': + case 's': + case 'S': + break; + case 'G': + case 'V': + special = str[rpos]; + if (rpos == 1) + break; + /* Fall-through to error. */ + default: + die("Bad remote-ext placeholder '%%%c'.", + str[rpos]); + } + escape = 0; + } else + escape = (str[rpos] == '%'); + 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. + */ + rpos = special ? 2 : 0; /* Skip first 2 bytes in specials. */ + escape = 0; + while (str[rpos] && (escape || str[rpos] != ' ')) { + if (escape) { + switch (str[rpos]) { + case ' ': + case '%': + strbuf_addch(&ret, str[rpos]); + break; + case 's': + strbuf_addstr(&ret, service + psoff); + break; + case 'S': + strbuf_addstr(&ret, service); + break; + } + escape = 0; + } else + switch (str[rpos]) { + case '%': + escape = 1; + break; + default: + strbuf_addch(&ret, str[rpos]); + break; + } + rpos++; + } + switch (special) { + case 'G': + git_req = strbuf_detach(&ret, NULL); + return NULL; + case 'V': + git_req_vhost = strbuf_detach(&ret, NULL); + return NULL; + default: + return strbuf_detach(&ret, NULL); + } +} + +/* Should be enough... */ +#define MAXARGUMENTS 256 + +static const char **parse_argv(const char *arg, const char *service) +{ + int arguments = 0; + int i; + const char **ret; + char *temparray[MAXARGUMENTS + 1]; + + while (*arg) { + char *expanded; + if (arguments == MAXARGUMENTS) + die("remote-ext command has too many arguments"); + expanded = strip_escapes(arg, service, &arg); + if (expanded) + temparray[arguments++] = expanded; + } + + ret = xmalloc((arguments + 1) * sizeof(char *)); + for (i = 0; i < arguments; i++) + ret[i] = temparray[i]; + ret[arguments] = NULL; + return (const char **)ret; +} + +static void send_git_request(int stdin_fd, const char *serv, const char *repo, + const char *vhost) +{ + size_t bufferspace; + size_t wpos = 0; + char *buffer; + + /* + * Request needs 12 bytes extra if there is vhost (xxxx \0host=\0) and + * 6 bytes extra (xxxx \0) if there is no vhost. + */ + if (vhost) + bufferspace = strlen(serv) + strlen(repo) + strlen(vhost) + 12; + else + bufferspace = strlen(serv) + strlen(repo) + 6; + + if (bufferspace > 0xFFFF) + die("Request too large to send"); + buffer = xmalloc(bufferspace); + + /* Make the packet. */ + wpos = sprintf(buffer, "%04x%s %s%c", (unsigned)bufferspace, + serv, repo, 0); + + /* Add vhost if any. */ + if (vhost) + sprintf(buffer + wpos, "host=%s%c", vhost, 0); + + /* Send the request */ + if (write_in_full(stdin_fd, buffer, bufferspace) < 0) + die_errno("Failed to send request"); + + free(buffer); +} + +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) + die("Can't run specified command"); + + if (git_req) + send_git_request(child.in, service, git_req, git_req_vhost); + + 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) { + size_t length; + if (!fgets(buffer, MAXCOMMAND - 1, stdin)) { + if (ferror(stdin)) + die("Comammand input error"); + exit(0); + } + /* Strip end of line characters. */ + length = strlen(buffer); + while (isspace((unsigned char)buffer[length - 1])) + buffer[--length] = 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]); +} -- cgit v1.2.1 From 7851b1e60fabe350216d48e6de03a3ed21a49953 Mon Sep 17 00:00:00 2001 From: Ilari Liusvaara Date: Wed, 17 Nov 2010 09:15:34 -0800 Subject: remote-fd/ext: finishing touches after code review When compiling with pthread support, transport-helper.c needs to include necessary header files. Also fix a few error messages in remote-ext and remote-fd programs, and a potential buffer underrun in remote-fd. In the documentation, clarify how %G and %V are used; the old description looked as if they take repository/vhost parameters, which was wrong. Also fix AsciiDoc markup for the page title of remote-fd/remote-ext manpages, and tweak the way how section headers are shown. Signed-off-by: Ilari Liusvaara Signed-off-by: Junio C Hamano --- builtin/remote-ext.c | 8 +++----- builtin/remote-fd.c | 6 +++--- 2 files changed, 6 insertions(+), 8 deletions(-) (limited to 'builtin') diff --git a/builtin/remote-ext.c b/builtin/remote-ext.c index 1d3f1cf3cc..1f773171cb 100644 --- a/builtin/remote-ext.c +++ b/builtin/remote-ext.c @@ -142,7 +142,7 @@ static const char **parse_argv(const char *arg, const char *service) for (i = 0; i < arguments; i++) ret[i] = temparray[i]; ret[arguments] = NULL; - return (const char **)ret; + return ret; } static void send_git_request(int stdin_fd, const char *serv, const char *repo, @@ -239,10 +239,8 @@ static int command_loop(const char *child) int cmd_remote_ext(int argc, const char **argv, const char *prefix) { - if (argc < 3) { - fprintf(stderr, "Error: URL missing"); - exit(1); - } + if (argc != 3) + die("Expected two arguments"); return command_loop(argv[2]); } diff --git a/builtin/remote-fd.c b/builtin/remote-fd.c index 7517f2476c..1f2467bdb7 100644 --- a/builtin/remote-fd.c +++ b/builtin/remote-fd.c @@ -31,7 +31,7 @@ static void command_loop(int input_fd, int output_fd) } /* Strip end of line characters. */ i = strlen(buffer); - while (isspace(buffer[i - 1])) + while (i > 0 && isspace(buffer[i - 1])) buffer[--i] = 0; if (!strcmp(buffer, "capabilities")) { @@ -56,8 +56,8 @@ int cmd_remote_fd(int argc, const char **argv, const char *prefix) int output_fd = -1; char *end; - if (argc < 3) - die("URL missing"); + if (argc != 3) + die("Expected two arguments"); input_fd = (int)strtoul(argv[2], &end, 10); -- cgit v1.2.1