rsc | e961291 | 2005-03-21 07:37:13 +0000 | [diff] [blame] | 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> |
| 17 | |
| 18 | #undef pipe |
| 19 | |
| 20 | int debug; |
| 21 | #define dprint if(debug)print |
| 22 | int sftpfd; |
| 23 | int sftperr; |
| 24 | Biobuf bin; |
| 25 | |
| 26 | void |
| 27 | usage(void) |
| 28 | { |
| 29 | fprint(2, "usage: sftpcache system\n"); |
| 30 | exits("usage"); |
| 31 | } |
| 32 | |
| 33 | char* |
| 34 | Brd(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; |
rsc | 3ae1a86 | 2005-03-24 05:17:21 +0000 | [diff] [blame] | 44 | dprint("OUT %s", buf); |
rsc | e961291 | 2005-03-21 07:37:13 +0000 | [diff] [blame] | 45 | return buf; |
| 46 | } |
| 47 | if(c == ' ' && tot == 6 && memcmp(buf, "sftp> ", 5) == 0){ |
| 48 | buf[tot] = 0; |
rsc | 3ae1a86 | 2005-03-24 05:17:21 +0000 | [diff] [blame] | 49 | dprint("OUT %s\n", buf); |
rsc | e961291 | 2005-03-21 07:37:13 +0000 | [diff] [blame] | 50 | return buf; |
| 51 | } |
| 52 | } |
| 53 | if(tot == sizeof buf) |
| 54 | sysfatal("response too long"); |
| 55 | return nil; |
| 56 | } |
| 57 | |
| 58 | int |
| 59 | readstr(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 | |
| 74 | void |
| 75 | doerrors(int fd) |
| 76 | { |
| 77 | char buf[100]; |
rsc | 3ae1a86 | 2005-03-24 05:17:21 +0000 | [diff] [blame] | 78 | int n, first; |
rsc | e961291 | 2005-03-21 07:37:13 +0000 | [diff] [blame] | 79 | |
rsc | 3ae1a86 | 2005-03-24 05:17:21 +0000 | [diff] [blame] | 80 | first = 1; |
rsc | e961291 | 2005-03-21 07:37:13 +0000 | [diff] [blame] | 81 | while((n = read(sftperr, buf, sizeof buf)) > 0){ |
rsc | 3ae1a86 | 2005-03-24 05:17:21 +0000 | [diff] [blame] | 82 | if(debug){ |
| 83 | if(first){ |
| 84 | first = 0; |
| 85 | fprint(2, "OUT errors:\n"); |
| 86 | } |
rsc | e961291 | 2005-03-21 07:37:13 +0000 | [diff] [blame] | 87 | write(1, buf, n); |
rsc | 3ae1a86 | 2005-03-24 05:17:21 +0000 | [diff] [blame] | 88 | } |
rsc | e961291 | 2005-03-21 07:37:13 +0000 | [diff] [blame] | 89 | write(fd, buf, n); |
| 90 | } |
| 91 | } |
| 92 | |
| 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); |
| 101 | } |
| 102 | |
| 103 | void |
| 104 | main(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(;;){ |
rsc | cbeb0b2 | 2006-04-01 19:24:03 +0000 | [diff] [blame^] | 182 | /* alarm(1000); */ |
rsc | e961291 | 2005-03-21 07:37:13 +0000 | [diff] [blame] | 183 | n = readstr(fd, cmd, sizeof cmd); |
rsc | cbeb0b2 | 2006-04-01 19:24:03 +0000 | [diff] [blame^] | 184 | /* alarm(0); */ |
rsc | e961291 | 2005-03-21 07:37:13 +0000 | [diff] [blame] | 185 | if(n <= 0) |
| 186 | break; |
| 187 | dprint("CMD %s\n", cmd); |
rsc | 3ae1a86 | 2005-03-24 05:17:21 +0000 | [diff] [blame] | 188 | if(strcmp(cmd, "DONE") == 0){ |
| 189 | fprint(fd, "DONE\n"); |
rsc | e961291 | 2005-03-21 07:37:13 +0000 | [diff] [blame] | 190 | break; |
rsc | 3ae1a86 | 2005-03-24 05:17:21 +0000 | [diff] [blame] | 191 | } |
| 192 | fprint(sftpfd, "-%s\n", cmd); |
rsc | e961291 | 2005-03-21 07:37:13 +0000 | [diff] [blame] | 193 | q = Brd(&bin); |
| 194 | if(*q==0 || q[strlen(q)-1] != '\n') |
| 195 | sysfatal("unexpected response"); |
| 196 | q[strlen(q)-1] = 0; |
rsc | 3ae1a86 | 2005-03-24 05:17:21 +0000 | [diff] [blame] | 197 | if(q[0] != '-' || strcmp(q+1, cmd) != 0) |
rsc | e961291 | 2005-03-21 07:37:13 +0000 | [diff] [blame] | 198 | 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 | |