Blob


1 /*
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>
18 #undef pipe
20 int debug;
21 #define dprint if(debug)print
22 int sftpfd;
23 int sftperr;
24 Biobuf bin;
26 void
27 usage(void)
28 {
29 fprint(2, "usage: sftpcache system\n");
30 exits("usage");
31 }
33 char*
34 Brd(Biobuf *bin)
35 {
36 static char buf[1000];
37 int c, tot;
39 tot = 0;
40 while((c = Bgetc(bin)) >= 0 && tot<sizeof buf){
41 buf[tot++] = c;
42 if(c == '\n'){
43 buf[tot] = 0;
44 dprint("OUT %s", buf);
45 return buf;
46 }
47 if(c == ' ' && tot == 6 && memcmp(buf, "sftp> ", 5) == 0){
48 buf[tot] = 0;
49 dprint("OUT %s\n", buf);
50 return buf;
51 }
52 }
53 if(tot == sizeof buf)
54 sysfatal("response too long");
55 return nil;
56 }
58 int
59 readstr(int fd, char *a, int n)
60 {
61 int i;
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 }
74 void
75 doerrors(int fd)
76 {
77 char buf[100];
78 int n, first;
80 first = 1;
81 while((n = read(sftperr, buf, sizeof buf)) > 0){
82 if(debug){
83 if(first){
84 first = 0;
85 fprint(2, "OUT errors:\n");
86 }
87 write(1, buf, n);
88 }
89 write(fd, buf, n);
90 }
91 }
93 void
94 bell(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);
103 void
104 main(int argc, char **argv)
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;
110 notify(bell);
111 fmtinstall('H', encodefmt);
113 ARGBEGIN{
114 case 'D':
115 debug = 1;
116 break;
117 default:
118 usage();
119 }ARGEND
121 if(argc != 1)
122 usage();
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");
146 close(p[0]);
147 close(px[1]);
148 close(pe[1]);
150 sftpfd = p[1];
151 sftperr = pe[0];
152 Binit(&bin, px[0], OREAD);
154 fcntl(sftperr, F_SETFL, fcntl(sftperr, F_GETFL, 0)|O_NONBLOCK);
156 do
157 q = Brd(&bin);
158 while(q && strcmp(q, "sftp> ") != 0);
159 if(q == nil)
160 sysfatal("unexpected eof");
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);
167 pid = fork();
168 if(pid < 0)
169 sysfatal("fork");
170 if(pid != 0)
171 exits(nil);
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(;;){
182 /* alarm(1000); */
183 n = readstr(fd, cmd, sizeof cmd);
184 /* alarm(0); */
185 if(n <= 0)
186 break;
187 dprint("CMD %s\n", cmd);
188 if(strcmp(cmd, "DONE") == 0){
189 fprint(fd, "DONE\n");
190 break;
192 fprint(sftpfd, "-%s\n", cmd);
193 q = Brd(&bin);
194 if(*q==0 || q[strlen(q)-1] != '\n')
195 sysfatal("unexpected response");
196 q[strlen(q)-1] = 0;
197 if(q[0] != '-' || strcmp(q+1, cmd) != 0)
198 sysfatal("unexpected response");
199 while((q = Brd(&bin)) != nil){
200 if(strcmp(q, "sftp> ") == 0){
201 doerrors(fd);
202 break;
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);
210 if(q == nil){
211 fprint(fd, "!!! unexpected eof\n");
212 sysfatal("unexpected eof");
215 close(fd);