documentation: help: add "man.<tool>.path" config variable
[gitweb.git] / connect.c
index ae49c5a367f9f5b77b07228fcd241fd73a5711b0..d12b105970bca82017998cae52f5807629736bb9 100644 (file)
--- a/connect.c
+++ b/connect.c
@@ -36,6 +36,11 @@ static int check_ref(const char *name, int len, unsigned int flags)
        return !(flags & ~REF_NORMAL);
 }
 
+int check_ref_type(const struct ref *ref, int flags)
+{
+       return check_ref(ref->name, strlen(ref->name), flags);
+}
+
 /*
  * Read all the refs from the other end
  */
@@ -63,8 +68,7 @@ struct ref **get_remote_heads(int in, struct ref **list,
 
                name_len = strlen(name);
                if (len != name_len + 41) {
-                       if (server_capabilities)
-                               free(server_capabilities);
+                       free(server_capabilities);
                        server_capabilities = xstrdup(name + name_len + 1);
                }
 
@@ -72,9 +76,9 @@ struct ref **get_remote_heads(int in, struct ref **list,
                        continue;
                if (nr_match && !path_match(name, nr_match, match))
                        continue;
-               ref = alloc_ref(len - 40);
+               ref = alloc_ref(name_len + 1);
                hashcpy(ref->old_sha1, old_sha1);
-               memcpy(ref->name, buffer + 41, len - 40);
+               memcpy(ref->name, buffer + 41, name_len + 1);
                *list = ref;
                list = &ref->next;
        }
@@ -365,6 +369,8 @@ static int git_proxy_command_options(const char *var, const char *value)
 
                if (git_proxy_command)
                        return 0;
+               if (!value)
+                       return config_error_nonbool(var);
                /* [core]
                 * ;# matches www.kernel.org as well
                 * gitproxy = netcatter-1 for kernel.org
@@ -393,9 +399,7 @@ static int git_proxy_command_options(const char *var, const char *value)
                        if (matchlen == 4 &&
                            !memcmp(value, "none", 4))
                                matchlen = 0;
-                       git_proxy_command = xmalloc(matchlen + 1);
-                       memcpy(git_proxy_command, value, matchlen);
-                       git_proxy_command[matchlen] = 0;
+                       git_proxy_command = xmemdupz(value, matchlen);
                }
                return 0;
        }
@@ -453,24 +457,48 @@ static void git_proxy_connect(int fd[2], char *host)
 
 #define MAX_CMD_LEN 1024
 
+char *get_port(char *host)
+{
+       char *end;
+       char *p = strchr(host, ':');
+
+       if (p) {
+               strtol(p+1, &end, 10);
+               if (*end == '\0') {
+                       *p = '\0';
+                       return p+1;
+               }
+       }
+
+       return NULL;
+}
+
+static struct child_process no_fork;
+
 /*
- * This returns 0 if the transport protocol does not need fork(2),
- * or a process id 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 0 to support the former
- * case).
+ * 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).
  *
- * Does not return a negative value on error; it just dies.
+ * 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).
  */
-pid_t git_connect(int fd[2], char *url, const char *prog, int flags)
+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 = url;
        char *end;
        int c;
-       int pipefd[2][2];
-       pid_t pid;
+       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.
@@ -527,6 +555,12 @@ pid_t git_connect(int fd[2], char *url, const char *prog, int flags)
                *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.
@@ -545,74 +579,72 @@ pid_t git_connect(int fd[2], char *url, const char *prog, int flags)
                             prog, path, 0,
                             target_host, 0);
                free(target_host);
+               free(url);
                if (free_path)
                        free(path);
-               return 0;
+               return &no_fork;
        }
 
-       if (pipe(pipefd[0]) < 0 || pipe(pipefd[1]) < 0)
-               die("unable to create pipe pair for communication");
-       pid = fork();
-       if (pid < 0)
-               die("unable to fork");
-       if (!pid) {
-               char command[MAX_CMD_LEN];
-               char *posn = command;
-               int size = MAX_CMD_LEN;
-               int of = 0;
-
-               of |= add_to_string(&posn, &size, prog, 0);
-               of |= add_to_string(&posn, &size, " ", 0);
-               of |= add_to_string(&posn, &size, path, 1);
-
-               if (of)
-                       die("command line too long");
-
-               dup2(pipefd[1][0], 0);
-               dup2(pipefd[0][1], 1);
-               close(pipefd[0][0]);
-               close(pipefd[0][1]);
-               close(pipefd[1][0]);
-               close(pipefd[1][1]);
-               if (protocol == PROTO_SSH) {
-                       const char *ssh, *ssh_basename;
-                       ssh = getenv("GIT_SSH");
-                       if (!ssh) ssh = "ssh";
-                       ssh_basename = strrchr(ssh, '/');
-                       if (!ssh_basename)
-                               ssh_basename = ssh;
-                       else
-                               ssh_basename++;
-                       execlp(ssh, ssh_basename, host, command, NULL);
-               }
-               else {
-                       unsetenv(ALTERNATE_DB_ENVIRONMENT);
-                       unsetenv(DB_ENVIRONMENT);
-                       unsetenv(GIT_DIR_ENVIRONMENT);
-                       unsetenv(GIT_WORK_TREE_ENVIRONMENT);
-                       unsetenv(GRAFT_ENVIRONMENT);
-                       unsetenv(INDEX_ENVIRONMENT);
-                       execlp("sh", "sh", "-c", command, NULL);
+       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(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;
                }
-               die("exec failed");
+               *arg++ = host;
        }
-       fd[0] = pipefd[0][0];
-       fd[1] = pipefd[1][1];
-       close(pipefd[0][1]);
-       close(pipefd[1][0]);
+       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,
+                       NULL
+               };
+               conn->env = env;
+               *arg++ = "sh";
+               *arg++ = "-c";
+       }
+       *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 pid;
+       return conn;
 }
 
-int finish_connect(pid_t pid)
+int finish_connect(struct child_process *conn)
 {
-       if (pid == 0)
+       int code;
+       if (!conn || conn == &no_fork)
                return 0;
 
-       while (waitpid(pid, NULL, 0) < 0) {
-               if (errno != EINTR)
-                       return -1;
-       }
-       return 0;
+       code = finish_command(conn);
+       free(conn->argv);
+       free(conn);
+       return code;
 }