blob: 3f8ea16554beca72061f5fcd47003c65432db85e [file] [log] [blame]
rsce9612912005-03-21 07:37:13 +00001/*
2 * Multiplexor for sftp sessions.
3 * Assumes can parse session with sftp> prompts.
4 * Assumes clients are well-behaved and don't hang up the system.
5 *
6 * Stupid sftp bug: sftp invokes ssh, which always set O_NONBLOCK
7 * on 0, 1, and 2. Ssh inherits sftp's 2, so we can't use the output pipe
8 * on fd 2, since it will get set O_NONBLOCK, sftp won't notice, and
9 * writes will be lost. So instead we use a separate pipe for errors
10 * and consult it after each command. Assume the pipe buffer is
11 * big enough to hold the error output.
12 */
13#include <u.h>
14#include <fcntl.h>
15#include <libc.h>
16#include <bio.h>
17
18#undef pipe
19
20int debug;
21#define dprint if(debug)print
22int sftpfd;
23int sftperr;
24Biobuf bin;
25
26void
27usage(void)
28{
29 fprint(2, "usage: sftpcache system\n");
30 exits("usage");
31}
32
33char*
34Brd(Biobuf *bin)
35{
36 static char buf[1000];
37 int c, tot;
38
39 tot = 0;
40 while((c = Bgetc(bin)) >= 0 && tot<sizeof buf){
41 buf[tot++] = c;
42 if(c == '\n'){
43 buf[tot] = 0;
rsc3ae1a862005-03-24 05:17:21 +000044 dprint("OUT %s", buf);
rsce9612912005-03-21 07:37:13 +000045 return buf;
46 }
47 if(c == ' ' && tot == 6 && memcmp(buf, "sftp> ", 5) == 0){
48 buf[tot] = 0;
rsc3ae1a862005-03-24 05:17:21 +000049 dprint("OUT %s\n", buf);
rsce9612912005-03-21 07:37:13 +000050 return buf;
51 }
52 }
53 if(tot == sizeof buf)
54 sysfatal("response too long");
55 return nil;
56}
57
58int
59readstr(int fd, char *a, int n)
60{
61 int i;
62
63 for(i=0; i<n; i++){
64 if(read(fd, a+i, 1) != 1)
65 return -1;
66 if(a[i] == '\n'){
67 a[i] = 0;
68 return i;
69 }
70 }
71 return n;
72}
73
74void
75doerrors(int fd)
76{
77 char buf[100];
rsc3ae1a862005-03-24 05:17:21 +000078 int n, first;
rsce9612912005-03-21 07:37:13 +000079
rsc3ae1a862005-03-24 05:17:21 +000080 first = 1;
rsce9612912005-03-21 07:37:13 +000081 while((n = read(sftperr, buf, sizeof buf)) > 0){
rsc3ae1a862005-03-24 05:17:21 +000082 if(debug){
83 if(first){
84 first = 0;
85 fprint(2, "OUT errors:\n");
86 }
rsce9612912005-03-21 07:37:13 +000087 write(1, buf, n);
rsc3ae1a862005-03-24 05:17:21 +000088 }
rsce9612912005-03-21 07:37:13 +000089 write(fd, buf, n);
90 }
91}
92
93void
94bell(void *x, char *msg)
95{
96 if(strcmp(msg, "sys: child") == 0 || strcmp(msg, "sys: write on closed pipe") == 0)
97 sysfatal("sftp exited");
98 if(strcmp(msg, "alarm") == 0)
99 noted(NCONT);
100 noted(NDFLT);
101}
102
103void
104main(int argc, char **argv)
105{
106 char buf[200], cmd[1000], *q, *s;
107 char dir[100], ndir[100];
108 int p[2], px[2], pe[2], pid, ctl, nctl, fd, n;
109
110 notify(bell);
111 fmtinstall('H', encodefmt);
112
113 ARGBEGIN{
114 case 'D':
115 debug = 1;
116 break;
117 default:
118 usage();
119 }ARGEND
120
121 if(argc != 1)
122 usage();
123
124 if(pipe(p) < 0 || pipe(px) < 0 || pipe(pe) < 0)
125 sysfatal("pipe: %r");
126 pid = fork();
127 if(pid < 0)
128 sysfatal("fork: %r");
129 if(pid == 0){
130 close(p[1]);
131 close(px[0]);
132 close(pe[0]);
133 dup(p[0], 0);
134 dup(px[1], 1);
135 dup(pe[1], 2);
136 if(p[0] > 2)
137 close(p[0]);
138 if(px[1] > 2)
139 close(px[1]);
140 if(pe[1] > 2)
141 close(pe[1]);
142 execl("sftp", "sftp", "-b", "/dev/stdin", argv[0], nil);
143 sysfatal("exec sftp: %r");
144 }
145
146 close(p[0]);
147 close(px[1]);
148 close(pe[1]);
149
150 sftpfd = p[1];
151 sftperr = pe[0];
152 Binit(&bin, px[0], OREAD);
153
154 fcntl(sftperr, F_SETFL, fcntl(sftperr, F_GETFL, 0)|O_NONBLOCK);
155
156 do
157 q = Brd(&bin);
158 while(q && strcmp(q, "sftp> ") != 0);
159 if(q == nil)
160 sysfatal("unexpected eof");
161
162 snprint(buf, sizeof buf, "unix!%s/%s.sftp", getns(), argv[0]);
163 ctl = announce(buf, dir);
164 if(ctl < 0)
165 sysfatal("announce %s: %r", buf);
166
167 pid = fork();
168 if(pid < 0)
169 sysfatal("fork");
170 if(pid != 0)
171 exits(nil);
172
173 for(;;){
174 nctl = listen(dir, ndir);
175 if(nctl < 0)
176 sysfatal("listen %s: %r", buf);
177 fd = accept(ctl, ndir);
178 close(nctl);
179 if(fd < 0)
180 continue;
181 for(;;){
rsccbeb0b22006-04-01 19:24:03 +0000182 /* alarm(1000); */
rsce9612912005-03-21 07:37:13 +0000183 n = readstr(fd, cmd, sizeof cmd);
rsccbeb0b22006-04-01 19:24:03 +0000184 /* alarm(0); */
rsce9612912005-03-21 07:37:13 +0000185 if(n <= 0)
186 break;
187 dprint("CMD %s\n", cmd);
rsc3ae1a862005-03-24 05:17:21 +0000188 if(strcmp(cmd, "DONE") == 0){
189 fprint(fd, "DONE\n");
rsce9612912005-03-21 07:37:13 +0000190 break;
rsc3ae1a862005-03-24 05:17:21 +0000191 }
192 fprint(sftpfd, "-%s\n", cmd);
rsce9612912005-03-21 07:37:13 +0000193 q = Brd(&bin);
194 if(*q==0 || q[strlen(q)-1] != '\n')
195 sysfatal("unexpected response");
196 q[strlen(q)-1] = 0;
rsc3ae1a862005-03-24 05:17:21 +0000197 if(q[0] != '-' || strcmp(q+1, cmd) != 0)
rsce9612912005-03-21 07:37:13 +0000198 sysfatal("unexpected response");
199 while((q = Brd(&bin)) != nil){
200 if(strcmp(q, "sftp> ") == 0){
201 doerrors(fd);
202 break;
203 }
204 s = q+strlen(q);
205 while(s > q && (s[-1] == ' ' || s[-1] == '\n' || s[-1] == '\t' || s[-1] == '\r'))
206 s--;
207 *s = 0;
208 fprint(fd, "%s\n", q);
209 }
210 if(q == nil){
211 fprint(fd, "!!! unexpected eof\n");
212 sysfatal("unexpected eof");
213 }
214 }
215 close(fd);
216 }
217}
218