Merge branch 'pe/date' into next
[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         pid_t pid;
52
53         if (!strcmp(url, "-")) {
54                 *fd_in = 0;
55                 *fd_out = 1;
56                 return 0;
57         }
58
59         host = strstr(url, "//");
60         if (host) {
61                 host += 2;
62                 path = strchr(host, '/');
63         } else {
64                 host = url;
65                 path = strchr(host, ':');
66                 if (path)
67                         *(path++) = '\0';
68         }
69         if (!path) {
70                 return error("Bad URL: %s", url);
71         }
72         /* $GIT_RSH <host> "env GIT_DIR=<path> <remote_prog> <args...>" */
73         sizen = COMMAND_SIZE;
74         posn = command;
75         of = 0;
76         of |= add_to_string(&posn, &sizen, "env ", 0);
77         of |= add_to_string(&posn, &sizen, GIT_DIR_ENVIRONMENT "=", 0);
78         of |= add_to_string(&posn, &sizen, path, 1);
79         of |= add_to_string(&posn, &sizen, " ", 0);
80         of |= add_to_string(&posn, &sizen, remote_prog, 1);
81
82         for ( i = 0 ; i < rmt_argc ; i++ ) {
83                 of |= add_to_string(&posn, &sizen, " ", 0);
84                 of |= add_to_string(&posn, &sizen, rmt_argv[i], 1);
85         }
86
87         of |= add_to_string(&posn, &sizen, " -", 0);
88
89         if ( of )
90                 return error("Command line too long");
91
92         if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv))
93                 return error("Couldn't create socket");
94
95         pid = fork();
96         if (pid < 0)
97                 return error("Couldn't fork");
98         if (!pid) {
99                 const char *ssh, *ssh_basename;
100                 ssh = getenv("GIT_SSH");
101                 if (!ssh) ssh = "ssh";
102                 ssh_basename = strrchr(ssh, '/');
103                 if (!ssh_basename)
104                         ssh_basename = ssh;
105                 else
106                         ssh_basename++;
107                 close(sv[1]);
108                 dup2(sv[0], 0);
109                 dup2(sv[0], 1);
110                 execlp(ssh, ssh_basename, host, command, NULL);
111         }
112         close(sv[0]);
113         *fd_in = sv[1];
114         *fd_out = sv[1];
115         return 0;
116 }