Code

Merge branch 'jk/git-connection-deadlock-fix'
[git.git] / connect.c
index beaff987c7bc36096d09b2557f5208c5606ce2dd..2119c3f74edcd2a976ff0e375c5d8d60d40da2ec 100644 (file)
--- a/connect.c
+++ b/connect.c
@@ -5,6 +5,7 @@
 #include "refs.h"
 #include "run-command.h"
 #include "remote.h"
+#include "url.h"
 
 static char *server_capabilities;
 
@@ -131,7 +132,7 @@ int path_match(const char *path, int nr, char **match)
 enum protocol {
        PROTO_LOCAL = 1,
        PROTO_SSH,
-       PROTO_GIT,
+       PROTO_GIT
 };
 
 static enum protocol get_protocol(const char *name)
@@ -152,6 +153,28 @@ static enum protocol get_protocol(const char *name)
 #define STR_(s)        # s
 #define STR(s) STR_(s)
 
+static void get_host_and_port(char **host, const char **port)
+{
+       char *colon, *end;
+
+       if (*host[0] == '[') {
+               end = strchr(*host + 1, ']');
+               if (end) {
+                       *end = 0;
+                       end++;
+                       (*host)++;
+               } else
+                       end = *host;
+       } else
+               end = *host;
+       colon = strchr(end, ':');
+
+       if (colon) {
+               *colon = 0;
+               *port = colon + 1;
+       }
+}
+
 #ifndef NO_IPV6
 
 static const char *ai_name(const struct addrinfo *ai)
@@ -170,30 +193,14 @@ static const char *ai_name(const struct addrinfo *ai)
 static int git_tcp_connect_sock(char *host, int flags)
 {
        int sockfd = -1, saved_errno = 0;
-       char *colon, *end;
        const char *port = STR(DEFAULT_GIT_PORT);
        struct addrinfo hints, *ai0, *ai;
        int gai;
        int cnt = 0;
 
-       if (host[0] == '[') {
-               end = strchr(host + 1, ']');
-               if (end) {
-                       *end = 0;
-                       end++;
-                       host++;
-               } else
-                       end = host;
-       } else
-               end = host;
-       colon = strchr(end, ':');
-
-       if (colon) {
-               *colon = 0;
-               port = colon + 1;
-               if (!*port)
-                       port = "<none>";
-       }
+       get_host_and_port(&host, &port);
+       if (!*port)
+               port = "<none>";
 
        memset(&hints, 0, sizeof(hints));
        hints.ai_socktype = SOCK_STREAM;
@@ -251,30 +258,15 @@ static int git_tcp_connect_sock(char *host, int flags)
 static int git_tcp_connect_sock(char *host, int flags)
 {
        int sockfd = -1, saved_errno = 0;
-       char *colon, *end;
-       char *port = STR(DEFAULT_GIT_PORT), *ep;
+       const char *port = STR(DEFAULT_GIT_PORT);
+       char *ep;
        struct hostent *he;
        struct sockaddr_in sa;
        char **ap;
        unsigned int nport;
        int cnt;
 
-       if (host[0] == '[') {
-               end = strchr(host + 1, ']');
-               if (end) {
-                       *end = 0;
-                       end++;
-                       host++;
-               } else
-                       end = host;
-       } else
-               end = host;
-       colon = strchr(end, ':');
-
-       if (colon) {
-               *colon = 0;
-               port = colon + 1;
-       }
+       get_host_and_port(&host, &port);
 
        if (flags & CONNECT_VERBOSE)
                fprintf(stderr, "Looking up %s ... ", host);
@@ -403,42 +395,28 @@ static int git_use_proxy(const char *host)
        return (git_proxy_command && *git_proxy_command);
 }
 
-static void git_proxy_connect(int fd[2], char *host)
+static struct child_process *git_proxy_connect(int fd[2], char *host)
 {
        const char *port = STR(DEFAULT_GIT_PORT);
-       char *colon, *end;
-       const char *argv[4];
-       struct child_process proxy;
-
-       if (host[0] == '[') {
-               end = strchr(host + 1, ']');
-               if (end) {
-                       *end = 0;
-                       end++;
-                       host++;
-               } else
-                       end = host;
-       } else
-               end = host;
-       colon = strchr(end, ':');
+       const char **argv;
+       struct child_process *proxy;
 
-       if (colon) {
-               *colon = 0;
-               port = colon + 1;
-       }
+       get_host_and_port(&host, &port);
 
+       argv = xmalloc(sizeof(*argv) * 4);
        argv[0] = git_proxy_command;
        argv[1] = host;
        argv[2] = port;
        argv[3] = NULL;
-       memset(&proxy, 0, sizeof(proxy));
-       proxy.argv = argv;
-       proxy.in = -1;
-       proxy.out = -1;
-       if (start_command(&proxy))
+       proxy = xcalloc(1, sizeof(*proxy));
+       proxy->argv = argv;
+       proxy->in = -1;
+       proxy->out = -1;
+       if (start_command(proxy))
                die("cannot start proxy %s", argv[0]);
-       fd[0] = proxy.out; /* read from proxy stdout */
-       fd[1] = proxy.in;  /* write to proxy stdin */
+       fd[0] = proxy->out; /* read from proxy stdout */
+       fd[1] = proxy->in;  /* write to proxy stdin */
+       return proxy;
 }
 
 #define MAX_CMD_LEN 1024
@@ -475,11 +453,11 @@ static struct child_process no_fork;
 struct child_process *git_connect(int fd[2], const char *url_orig,
                                  const char *prog, int flags)
 {
-       char *url = xstrdup(url_orig);
+       char *url;
        char *host, *path;
        char *end;
        int c;
-       struct child_process *conn;
+       struct child_process *conn = &no_fork;
        enum protocol protocol = PROTO_LOCAL;
        int free_path = 0;
        char *port = NULL;
@@ -491,6 +469,11 @@ struct child_process *git_connect(int fd[2], const char *url_orig,
         */
        signal(SIGCHLD, SIG_DFL);
 
+       if (is_url(url_orig))
+               url = url_decode(url_orig);
+       else
+               url = xstrdup(url_orig);
+
        host = strstr(url, "://");
        if (host) {
                *host = '\0';
@@ -559,7 +542,7 @@ struct child_process *git_connect(int fd[2], const char *url_orig,
                 */
                char *target_host = xstrdup(host);
                if (git_use_proxy(host))
-                       git_proxy_connect(fd, host);
+                       conn = git_proxy_connect(fd, host);
                else
                        git_tcp_connect(fd, host, flags);
                /*
@@ -577,7 +560,7 @@ struct child_process *git_connect(int fd[2], const char *url_orig,
                free(url);
                if (free_path)
                        free(path);
-               return &no_fork;
+               return conn;
        }
 
        conn = xcalloc(1, sizeof(*conn));
@@ -626,10 +609,15 @@ struct child_process *git_connect(int fd[2], const char *url_orig,
        return conn;
 }
 
+int git_connection_is_socket(struct child_process *conn)
+{
+       return conn == &no_fork;
+}
+
 int finish_connect(struct child_process *conn)
 {
        int code;
-       if (!conn || conn == &no_fork)
+       if (!conn || git_connection_is_socket(conn))
                return 0;
 
        code = finish_command(conn);
@@ -637,3 +625,47 @@ int finish_connect(struct child_process *conn)
        free(conn);
        return code;
 }
+
+char *git_getpass(const char *prompt)
+{
+       const char *askpass;
+       struct child_process pass;
+       const char *args[3];
+       static struct strbuf buffer = STRBUF_INIT;
+
+       askpass = getenv("GIT_ASKPASS");
+       if (!askpass)
+               askpass = askpass_program;
+       if (!askpass)
+               askpass = getenv("SSH_ASKPASS");
+       if (!askpass || !(*askpass)) {
+               char *result = getpass(prompt);
+               if (!result)
+                       die_errno("Could not read password");
+               return result;
+       }
+
+       args[0] = askpass;
+       args[1] = prompt;
+       args[2] = NULL;
+
+       memset(&pass, 0, sizeof(pass));
+       pass.argv = args;
+       pass.out = -1;
+
+       if (start_command(&pass))
+               exit(1);
+
+       strbuf_reset(&buffer);
+       if (strbuf_read(&buffer, pass.out, 20) < 0)
+               die("failed to read password from %s\n", askpass);
+
+       close(pass.out);
+
+       if (finish_command(&pass))
+               exit(1);
+
+       strbuf_setlen(&buffer, strcspn(buffer.buf, "\r\n"));
+
+       return buffer.buf;
+}