[PATCH] rsh.c env and quoting cleanup, take 2
[git.git] / rsh.c
1 #include "rsh.h"
2
3 #include <string.h>
4 #include <sys/types.h>
5 #include <sys/socket.h>
6
7 #include "cache.h"
8
9 #define COMMAND_SIZE 4096
10
11 /*
12  * Write a shell-quoted version of a string into a buffer, and
13  * return bytes that ought to be output excluding final null.
14  */
15 static int shell_quote(char *buf, int nmax, const char *str)
16 {
17         char ch;
18         int nq;
19         int oc = 0;
20
21         while ( (ch = *str++) ) {
22                 nq = 0;
23                 if ( strchr(" !\"#$%&\'()*;<=>?[\\]^`{|}", ch) )
24                         nq = 1;
25
26                 if ( nq ) {
27                         if ( nmax > 1 ) {
28                                 *buf++ = '\\';
29                                 nmax--;
30                         }
31                         oc++;
32                 }
33
34                 if ( nmax > 1 ) {
35                         *buf++ = ch;
36                         nmax--;
37                 }
38                 oc++;
39         }
40
41         if ( nmax )
42                 *buf = '\0';
43
44         return oc;
45 }
46                         
47 /*
48  * Append a string to a string buffer, with or without quoting.  Return true
49  * if the buffer overflowed.
50  */
51 static int add_to_string(char **ptrp, int *sizep, const char *str, int quote)
52 {
53         char *p = *ptrp;
54         int size = *sizep;
55         int oc;
56
57         if ( quote ) {
58                 oc = shell_quote(p, size, str);
59         } else {
60                 oc = strlen(str);
61                 memcpy(p, str, (oc >= size) ? size-1 : oc);
62         }
63
64         if ( oc >= size ) {
65                 p[size-1] = '\0';
66                 *ptrp += size-1;
67                 *sizep = 1;
68                 return 1;       /* Overflow, string unusable */
69         }
70
71         *ptrp  += oc;
72         *sizep -= oc;
73         return 0;
74 }
75
76 int setup_connection(int *fd_in, int *fd_out, const char *remote_prog, 
77                      char *url, int rmt_argc, char **rmt_argv)
78 {
79         char *host;
80         char *path;
81         int sv[2];
82         char command[COMMAND_SIZE];
83         char *posn;
84         int sizen;
85         int of;
86         int i;
87
88         if (!strcmp(url, "-")) {
89                 *fd_in = 0;
90                 *fd_out = 1;
91                 return 0;
92         }
93
94         host = strstr(url, "//");
95         if (host) {
96                 host += 2;
97                 path = strchr(host, '/');
98         } else {
99                 host = url;
100                 path = strchr(host, ':');
101                 if (path)
102                         *(path++) = '\0';
103         }
104         if (!path) {
105                 return error("Bad URL: %s", url);
106         }
107         /* $GIT_RSH <host> "env GIR_DIR=<path> <remote_prog> <args...>" */
108         sizen = COMMAND_SIZE;
109         posn = command;
110         of = 0;
111         of |= add_to_string(&posn, &sizen, "env ", 0);
112         of |= add_to_string(&posn, &sizen, GIT_DIR_ENVIRONMENT, 0);
113         of |= add_to_string(&posn, &sizen, "=", 0);
114         of |= add_to_string(&posn, &sizen, path, 1);
115         of |= add_to_string(&posn, &sizen, " ", 0);
116         of |= add_to_string(&posn, &sizen, remote_prog, 1);
117
118         for ( i = 0 ; i < rmt_argc ; i++ ) {
119                 of |= add_to_string(&posn, &sizen, " ", 0);
120                 of |= add_to_string(&posn, &sizen, rmt_argv[i], 1);
121         }
122
123         of |= add_to_string(&posn, &sizen, " -", 0);
124
125         if ( of )
126                 return error("Command line too long");
127
128         if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv))
129                 return error("Couldn't create socket");
130
131         if (!fork()) {
132                 const char *ssh, *ssh_basename;
133                 ssh = getenv("GIT_SSH");
134                 if (!ssh) ssh = "ssh";
135                 ssh_basename = strrchr(ssh, '/');
136                 if (!ssh_basename)
137                         ssh_basename = ssh;
138                 else
139                         ssh_basename++;
140                 close(sv[1]);
141                 dup2(sv[0], 0);
142                 dup2(sv[0], 1);
143                 execlp(ssh, ssh_basename, host, command, NULL);
144         }
145         close(sv[0]);
146         *fd_in = sv[1];
147         *fd_out = sv[1];
148         return 0;
149 }