plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

sftpcache.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 }