URI:
       tsftpcache.c - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       tsftpcache.c (4039B)
       ---
            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;
           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 }
           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];
           78         int n, first;
           79 
           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 }
           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(;;){
          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;
          191                         }
          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;
          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 }