Work around missing hard links on FAT formatted media
[git.git] / rsh.c
1 #include <string.h>
2 #include <sys/types.h>
3 #include <sys/socket.h>
4
5 #include "rsh.h"
6 #include "quote.h"
7 #include "cache.h"
8
9 #define COMMAND_SIZE 4096
10
11 /*
12  * Append a string to a string buffer, with or without shell quoting.
13  * Return true if the buffer overflowed.
14  */
15 static int add_to_string(char **ptrp, int *sizep, const char *str, int quote)
16 {
17         char *p = *ptrp;
18         int size = *sizep;
19         int oc;
20         int err = 0;
21
22         if ( quote ) {
23                 oc = sq_quote_buf(p, size, str);
24         } else {
25                 oc = strlen(str);
26                 memcpy(p, str, (oc >= size) ? size-1 : oc);
27         }
28
29         if ( oc >= size ) {
30                 err = 1;
31                 oc = size-1;
32         }
33
34         *ptrp  += oc;
35         **ptrp  = '\0';
36         *sizep -= oc;
37         return err;
38 }
39
40 int setup_connection(int *fd_in, int *fd_out, const char *remote_prog, 
41                      char *url, int rmt_argc, char **rmt_argv)
42 {
43         char *host;
44         char *path;
45         int sv[2];
46         char command[COMMAND_SIZE];
47         char *posn;
48         int sizen;
49         int of;
50         int i;
51
52         if (!strcmp(url, "-")) {
53                 *fd_in = 0;
54                 *fd_out = 1;
55                 return 0;
56         }
57
58         host = strstr(url, "//");
59         if (host) {
60                 host += 2;
61                 path = strchr(host, '/');
62         } else {
63                 host = url;
64                 path = strchr(host, ':');
65                 if (path)
66                         *(path++) = '\0';
67         }
68         if (!path) {
69                 return error("Bad URL: %s", url);
70         }
71         /* $GIT_RSH <host> "env GIT_DIR=<path> <remote_prog> <args...>" */
72         sizen = COMMAND_SIZE;
73         posn = command;
74         of = 0;
75         of |= add_to_string(&posn, &sizen, "env ", 0);
76         of |= add_to_string(&posn, &sizen, GIT_DIR_ENVIRONMENT "=", 0);
77         of |= add_to_string(&posn, &sizen, path, 1);
78         of |= add_to_string(&posn, &sizen, " ", 0);
79         of |= add_to_string(&posn, &sizen, remote_prog, 1);
80
81         for ( i = 0 ; i < rmt_argc ; i++ ) {
82                 of |= add_to_string(&posn, &sizen, " ", 0);
83                 of |= add_to_string(&posn, &sizen, rmt_argv[i], 1);
84         }
85
86         of |= add_to_string(&posn, &sizen, " -", 0);
87
88         if ( of )
89                 return error("Command line too long");
90
91         if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv))
92                 return error("Couldn't create socket");
93
94         if (!fork()) {
95                 const char *ssh, *ssh_basename;
96                 ssh = getenv("GIT_SSH");
97                 if (!ssh) ssh = "ssh";
98                 ssh_basename = strrchr(ssh, '/');
99                 if (!ssh_basename)
100                         ssh_basename = ssh;
101                 else
102                         ssh_basename++;
103                 close(sv[1]);
104                 dup2(sv[0], 0);
105                 dup2(sv[0], 1);
106                 execlp(ssh, ssh_basename, host, command, NULL);
107         }
108         close(sv[0]);
109         *fd_in = sv[1];
110         *fd_out = sv[1];
111         return 0;
112 }