/* * Open a connection using Git's native protocol. * * The caller is responsible for freeing hostandport, but this function may * modify it (for example, to truncate it to remove the port part). */ static struct child_process *git_connect_git(int fd[2], char *hostandport, const char *path, const char *prog, enum protocol_version version, int flags) { struct child_process *conn; struct strbuf request = STRBUF_INIT; /* * Set up virtual host information based on where we will * connect, unless the user has overridden us in * the environment. */ char *target_host = getenv("GIT_OVERRIDE_VIRTUAL_HOST"); if (target_host) target_host = xstrdup(target_host); else target_host = xstrdup(hostandport); transport_check_allowed("git"); /* * These underlying connection commands die() if they * cannot connect. */ if (git_use_proxy(hostandport)) conn = git_proxy_connect(fd, hostandport); else conn = git_tcp_connect(fd, hostandport, flags); /* * Separate original protocol components prog and path * from extended host header with a NUL byte. * * Note: Do not add any other headers here! Doing so * will cause older git-daemon servers to crash. */ strbuf_addf(&request, "%s %s%chost=%s%c", prog, path, 0, target_host, 0); /* If using a new version put that stuff here after a second null byte */ if (version > 0) { strbuf_addch(&request, '\0'); strbuf_addf(&request, "version=%d%c", version, '\0'); } packet_write(fd[1], request.buf, request.len); free(target_host); strbuf_release(&request); return conn; }
/* * This returns a dummy child_process if the transport protocol does not * need fork(2), or a struct child_process object if it does. Once done, * finish the connection with finish_connect() with the value returned from * this function (it is safe to call finish_connect() with NULL to support * the former case). * * If it returns, the connect is successful; it just dies on errors (this * will hopefully be changed in a libification effort, to return NULL when * the connection failed). */ struct child_process *git_connect(int fd[2], const char *url_orig, const char *prog, int flags) { char *url = xstrdup(url_orig); char *host, *path; char *end; int c; struct child_process *conn; enum protocol protocol = PROTO_LOCAL; int free_path = 0; char *port = NULL; const char **arg; struct strbuf cmd; /* Without this we cannot rely on waitpid() to tell * what happened to our children. */ signal(SIGCHLD, SIG_DFL); host = strstr(url, "://"); if (host) { *host = '\0'; protocol = get_protocol(url); host += 3; c = '/'; } else { host = url; c = ':'; } /* * Don't do destructive transforms with git:// as that * protocol code does '[]' unwrapping of its own. */ if (host[0] == '[') { end = strchr(host + 1, ']'); if (end) { if (protocol != PROTO_GIT) { *end = 0; host++; } end++; } else end = host; } else end = host; path = strchr(end, c); if (path && !has_dos_drive_prefix(end)) { if (c == ':') { protocol = PROTO_SSH; *path++ = '\0'; } } else path = end; if (!path || !*path) die("No path specified. See 'man git-pull' for valid url syntax"); /* * null-terminate hostname and point path to ~ for URL's like this: * ssh://host.xz/~user/repo */ if (protocol != PROTO_LOCAL && host != url) { char *ptr = path; if (path[1] == '~') path++; else { path = xstrdup(ptr); free_path = 1; } *ptr = '\0'; } /* * Add support for ssh port: ssh://host.xy:<port>/... */ if (protocol == PROTO_SSH && host != url) port = get_port(host); if (protocol == PROTO_GIT) { /* These underlying connection commands die() if they * cannot connect. */ char *target_host = xstrdup(host); if (git_use_proxy(host)) git_proxy_connect(fd, host); else git_tcp_connect(fd, host, flags); /* * Separate original protocol components prog and path * from extended host header with a NUL byte. * * Note: Do not add any other headers here! Doing so * will cause older git-daemon servers to crash. */ packet_write(fd[1], "%s %s%chost=%s%c", prog, path, 0, target_host, 0); free(target_host); free(url); if (free_path) free(path); return &no_fork; } conn = xcalloc(1, sizeof(*conn)); strbuf_init(&cmd, MAX_CMD_LEN); strbuf_addstr(&cmd, prog); strbuf_addch(&cmd, ' '); sq_quote_buf(&cmd, path); if (cmd.len >= MAX_CMD_LEN) die("command line too long"); conn->in = conn->out = -1; conn->argv = arg = xcalloc(7, sizeof(*arg)); if (protocol == PROTO_SSH) { const char *ssh = getenv("GIT_SSH"); int putty = ssh && strcasestr(ssh, "plink"); if (!ssh) ssh = "ssh"; *arg++ = ssh; if (putty && !strcasestr(ssh, "tortoiseplink")) *arg++ = "-batch"; if (port) { /* P is for PuTTY, p is for OpenSSH */ *arg++ = putty ? "-P" : "-p"; *arg++ = port; } *arg++ = host; } else { /* remove these from the environment */ const char *env[] = { ALTERNATE_DB_ENVIRONMENT, DB_ENVIRONMENT, GIT_DIR_ENVIRONMENT, GIT_WORK_TREE_ENVIRONMENT, GRAFT_ENVIRONMENT, INDEX_ENVIRONMENT, NO_REPLACE_OBJECTS_ENVIRONMENT, NULL }; conn->env = env; conn->use_shell = 1; } *arg++ = cmd.buf; *arg = NULL; if (start_command(conn)) die("unable to fork"); fd[0] = conn->out; /* read from child's stdout */ fd[1] = conn->in; /* write to child's stdin */ strbuf_release(&cmd); free(url); if (free_path) free(path); return conn; }
/* * This returns a dummy child_process if the transport protocol does not * need fork(2), or a struct child_process object if it does. Once done, * finish the connection with finish_connect() with the value returned from * this function (it is safe to call finish_connect() with NULL to support * the former case). * * If it returns, the connect is successful; it just dies on errors (this * will hopefully be changed in a libification effort, to return NULL when * the connection failed). */ struct child_process *git_connect(int fd[2], const char *url, const char *prog, int flags) { char *hostandport, *path; struct child_process *conn = &no_fork; enum protocol protocol; struct strbuf cmd = STRBUF_INIT; /* Without this we cannot rely on waitpid() to tell * what happened to our children. */ signal(SIGCHLD, SIG_DFL); protocol = parse_connect_url(url, &hostandport, &path); if ((flags & CONNECT_DIAG_URL) && (protocol != PROTO_SSH)) { printf("Diag: url=%s\n", url ? url : "NULL"); printf("Diag: protocol=%s\n", prot_name(protocol)); printf("Diag: hostandport=%s\n", hostandport ? hostandport : "NULL"); printf("Diag: path=%s\n", path ? path : "NULL"); conn = NULL; } else if (protocol == PROTO_GIT) { /* * Set up virtual host information based on where we will * connect, unless the user has overridden us in * the environment. */ char *target_host = getenv("GIT_OVERRIDE_VIRTUAL_HOST"); if (target_host) target_host = xstrdup(target_host); else target_host = xstrdup(hostandport); transport_check_allowed("git"); /* These underlying connection commands die() if they * cannot connect. */ if (git_use_proxy(hostandport)) conn = git_proxy_connect(fd, hostandport); else git_tcp_connect(fd, hostandport, flags); /* * Separate original protocol components prog and path * from extended host header with a NUL byte. * * Note: Do not add any other headers here! Doing so * will cause older git-daemon servers to crash. */ packet_write(fd[1], "%s %s%chost=%s%c", prog, path, 0, target_host, 0); free(target_host); } else { conn = xmalloc(sizeof(*conn)); child_process_init(conn); strbuf_addstr(&cmd, prog); strbuf_addch(&cmd, ' '); sq_quote_buf(&cmd, path); conn->in = conn->out = -1; if (protocol == PROTO_SSH) { const char *ssh; int putty, tortoiseplink = 0; char *ssh_host = hostandport; const char *port = NULL; transport_check_allowed("ssh"); get_host_and_port(&ssh_host, &port); if (!port) port = get_port(ssh_host); if (flags & CONNECT_DIAG_URL) { printf("Diag: url=%s\n", url ? url : "NULL"); printf("Diag: protocol=%s\n", prot_name(protocol)); printf("Diag: userandhost=%s\n", ssh_host ? ssh_host : "NULL"); printf("Diag: port=%s\n", port ? port : "NONE"); printf("Diag: path=%s\n", path ? path : "NULL"); free(hostandport); free(path); free(conn); return NULL; } ssh = getenv("GIT_SSH_COMMAND"); if (ssh) { conn->use_shell = 1; putty = 0; } else { const char *base; char *ssh_dup; ssh = getenv("GIT_SSH"); if (!ssh) ssh = "ssh"; ssh_dup = xstrdup(ssh); base = basename(ssh_dup); tortoiseplink = !strcasecmp(base, "tortoiseplink") || !strcasecmp(base, "tortoiseplink.exe"); putty = !strcasecmp(base, "plink") || !strcasecmp(base, "plink.exe") || tortoiseplink; free(ssh_dup); } argv_array_push(&conn->args, ssh); if (tortoiseplink) argv_array_push(&conn->args, "-batch"); if (port) { /* P is for PuTTY, p is for OpenSSH */ argv_array_push(&conn->args, putty ? "-P" : "-p"); argv_array_push(&conn->args, port); } argv_array_push(&conn->args, ssh_host); } else { /* remove repo-local variables from the environment */ conn->env = local_repo_env; conn->use_shell = 1; transport_check_allowed("file"); } argv_array_push(&conn->args, cmd.buf); if (start_command(conn)) die("unable to fork"); fd[0] = conn->out; /* read from child's stdout */ fd[1] = conn->in; /* write to child's stdin */ strbuf_release(&cmd); } free(hostandport); free(path); return conn; }