xref: /inferno-os/appl/lib/tftp.b (revision fbc1184c08d18d5ac0f8763a058e015e95353341)
137da2899SCharles.Forsythimplement Tftp;
237da2899SCharles.Forsyth
337da2899SCharles.Forsythinclude "sys.m";
437da2899SCharles.Forsyth	sys: Sys;
537da2899SCharles.Forsyth
6*fbc1184cSCharles Forsythinclude "dial.m";
7*fbc1184cSCharles Forsyth	dial: Dial;
8*fbc1184cSCharles Forsyth
937da2899SCharles.Forsythinclude "tftp.m";
1037da2899SCharles.Forsyth
1137da2899SCharles.ForsythMaxretry: con 5;	# retries per block
1237da2899SCharles.ForsythMaxblock: con 512;	# protocol's usual maximum data block size
1337da2899SCharles.ForsythTftphdrlen: con 4;
1437da2899SCharles.ForsythRead, Write, Data, Ack, Error: con 1+iota;	# tftp opcode
1537da2899SCharles.Forsyth
1637da2899SCharles.Forsythprogress: int;
1737da2899SCharles.Forsyth
1837da2899SCharles.Forsythput2(buf: array of byte, o: int, val: int)
1937da2899SCharles.Forsyth{
2037da2899SCharles.Forsyth	buf[o] = byte (val >> 8);
2137da2899SCharles.Forsyth	buf[o+1] = byte val;
2237da2899SCharles.Forsyth}
2337da2899SCharles.Forsyth
2437da2899SCharles.Forsythget2(buf: array of byte, o: int): int
2537da2899SCharles.Forsyth{
2637da2899SCharles.Forsyth	return (int buf[o] << 8) | int buf[o+1];
2737da2899SCharles.Forsyth}
2837da2899SCharles.Forsyth
2937da2899SCharles.Forsythkill(pid: int)
3037da2899SCharles.Forsyth{
3137da2899SCharles.Forsyth	fd := sys->open("#p/" + string pid + "/ctl", sys->OWRITE);
3237da2899SCharles.Forsyth	if(fd == nil)
3337da2899SCharles.Forsyth		return;
3437da2899SCharles.Forsyth
3537da2899SCharles.Forsyth	msg := array of byte "kill";
3637da2899SCharles.Forsyth	sys->write(fd, msg, len msg);
3737da2899SCharles.Forsyth}
3837da2899SCharles.Forsyth
3937da2899SCharles.Forsythtimeoutproc(c: chan of int, howlong: int)
4037da2899SCharles.Forsyth{
4137da2899SCharles.Forsyth	c <-= sys->pctl(0, nil);
4237da2899SCharles.Forsyth	sys->sleep(howlong);
4337da2899SCharles.Forsyth	c <-= 1;
4437da2899SCharles.Forsyth}
4537da2899SCharles.Forsyth
4637da2899SCharles.Forsythtpid := -1;
4737da2899SCharles.Forsyth
4837da2899SCharles.Forsythtimeoutcancel()
4937da2899SCharles.Forsyth{
5037da2899SCharles.Forsyth	if(tpid >= 0) {
5137da2899SCharles.Forsyth		kill(tpid);
5237da2899SCharles.Forsyth		tpid = -1;
5337da2899SCharles.Forsyth	}
5437da2899SCharles.Forsyth}
5537da2899SCharles.Forsyth
5637da2899SCharles.Forsythtimeoutstart(howlong: int): chan of int
5737da2899SCharles.Forsyth{
5837da2899SCharles.Forsyth	timeoutcancel();
5937da2899SCharles.Forsyth	tc := chan of int;
6037da2899SCharles.Forsyth	spawn timeoutproc(tc, howlong);
6137da2899SCharles.Forsyth	tpid = <-tc;
6237da2899SCharles.Forsyth	return tc;
6337da2899SCharles.Forsyth}
6437da2899SCharles.Forsyth
6537da2899SCharles.Forsythinit(p: int)
6637da2899SCharles.Forsyth{
6737da2899SCharles.Forsyth	sys = load Sys Sys->PATH;
68*fbc1184cSCharles Forsyth	dial = load Dial Dial->PATH;
6937da2899SCharles.Forsyth	progress = p;
7037da2899SCharles.Forsyth}
7137da2899SCharles.Forsyth
7237da2899SCharles.Forsythreader(pidc: chan of int, fd: ref Sys->FD, bc: chan of array of byte)
7337da2899SCharles.Forsyth{
7437da2899SCharles.Forsyth	pid := sys->pctl(0, nil);
7537da2899SCharles.Forsyth	pidc <-= pid;
7637da2899SCharles.Forsyth	buf := array[Tftphdrlen + Maxblock] of byte;
7737da2899SCharles.Forsyth	for(;;){
7837da2899SCharles.Forsyth		n := sys->read(fd, buf, len buf);
7937da2899SCharles.Forsyth		bc <-= buf[0 : n];
8037da2899SCharles.Forsyth	}
8137da2899SCharles.Forsyth}
8237da2899SCharles.Forsyth
8337da2899SCharles.Forsythreceive(host: string, filename: string, fd: ref Sys->FD): string
8437da2899SCharles.Forsyth{
8537da2899SCharles.Forsyth	rbuf: array of byte;
8637da2899SCharles.Forsyth
87*fbc1184cSCharles Forsyth	conn := dial->dial(dial->netmkaddr(host, "udp", "69"), nil);
88*fbc1184cSCharles Forsyth	if(conn == nil)
8937da2899SCharles.Forsyth		return sys->sprint("can't dial %s: %r", host);
9037da2899SCharles.Forsyth	buf := array[Tftphdrlen + Maxblock] of byte;
9137da2899SCharles.Forsyth	i := 0;
9237da2899SCharles.Forsyth	put2(buf, i, Read);
9337da2899SCharles.Forsyth	i += 2;
9437da2899SCharles.Forsyth	a := array of byte filename;
9537da2899SCharles.Forsyth	buf[i:] = a;
9637da2899SCharles.Forsyth	i += len a;
9737da2899SCharles.Forsyth	buf[i++] = byte 0;
9837da2899SCharles.Forsyth	mode := array of byte "binary";
9937da2899SCharles.Forsyth	buf[i:] = mode;
10037da2899SCharles.Forsyth	i += len mode;
10137da2899SCharles.Forsyth	buf[i++] = byte 0;
10237da2899SCharles.Forsyth	pidc := chan of int;
10337da2899SCharles.Forsyth	bc := chan of array of byte;
10437da2899SCharles.Forsyth	spawn reader(pidc, conn.dfd, bc);
10537da2899SCharles.Forsyth	tftppid := <-pidc;
10637da2899SCharles.Forsyth	lastblock := 0;
10737da2899SCharles.Forsyth	for(;;) {
10837da2899SCharles.Forsyth	  Retry:
10937da2899SCharles.Forsyth		for(count := 0;; count++) {
11037da2899SCharles.Forsyth			if(count >= Maxretry){
11137da2899SCharles.Forsyth				kill(tftppid);
11237da2899SCharles.Forsyth				return sys->sprint("tftp timeout");
11337da2899SCharles.Forsyth			}
11437da2899SCharles.Forsyth
11537da2899SCharles.Forsyth			# (re)send request/ack
11637da2899SCharles.Forsyth			if(sys->write(conn.dfd, buf, i) < 0) {
11737da2899SCharles.Forsyth				kill(tftppid);
11837da2899SCharles.Forsyth				return sys->sprint( "error writing %s/data: %r", conn.dir);
11937da2899SCharles.Forsyth			}
12037da2899SCharles.Forsyth
12137da2899SCharles.Forsyth			# wait for next block
12237da2899SCharles.Forsyth			mtc := timeoutstart(3000);
12337da2899SCharles.Forsyth			for(;;){
12437da2899SCharles.Forsyth				alt {
12537da2899SCharles.Forsyth				<-mtc =>
12637da2899SCharles.Forsyth					if(progress)
12737da2899SCharles.Forsyth						sys->print("T");
12837da2899SCharles.Forsyth					continue Retry;
12937da2899SCharles.Forsyth				rbuf = <-bc =>
13037da2899SCharles.Forsyth					if(len rbuf < Tftphdrlen)
13137da2899SCharles.Forsyth						break;
13237da2899SCharles.Forsyth					op := get2(rbuf, 0);
13337da2899SCharles.Forsyth					case op {
13437da2899SCharles.Forsyth					Data =>
13537da2899SCharles.Forsyth						block := get2(rbuf, 2);
13637da2899SCharles.Forsyth						if(block == lastblock + 1) {
13737da2899SCharles.Forsyth							timeoutcancel();
13837da2899SCharles.Forsyth							break Retry;
13937da2899SCharles.Forsyth						}else if(progress)
14037da2899SCharles.Forsyth							sys->print("S");
14137da2899SCharles.Forsyth					Error =>
14237da2899SCharles.Forsyth						timeoutcancel();
14337da2899SCharles.Forsyth						kill(tftppid);
14437da2899SCharles.Forsyth						return sys->sprint("server error %d: %s", get2(rbuf, 2), string rbuf[4:]);
14537da2899SCharles.Forsyth					* =>
14637da2899SCharles.Forsyth						timeoutcancel();
14737da2899SCharles.Forsyth						kill(tftppid);
14837da2899SCharles.Forsyth						return sys->sprint("phase error op=%d", op);
14937da2899SCharles.Forsyth					}
15037da2899SCharles.Forsyth				}
15137da2899SCharles.Forsyth			}
15237da2899SCharles.Forsyth		}
15337da2899SCharles.Forsyth		n := len rbuf;
15437da2899SCharles.Forsyth		# copy the data somewhere
15537da2899SCharles.Forsyth		if(sys->write(fd, rbuf[Tftphdrlen:], n - Tftphdrlen) < 0) {
15637da2899SCharles.Forsyth			kill(tftppid);
15737da2899SCharles.Forsyth			return sys->sprint("writing destination: %r");
15837da2899SCharles.Forsyth		}
15937da2899SCharles.Forsyth		lastblock++;
16037da2899SCharles.Forsyth		if(progress && lastblock % 25 == 0)
16137da2899SCharles.Forsyth			sys->print(".");
16237da2899SCharles.Forsyth		if(n < Maxblock + Tftphdrlen) {
16337da2899SCharles.Forsyth			if(progress)
16437da2899SCharles.Forsyth				sys->print("\n");
16537da2899SCharles.Forsyth			break;
16637da2899SCharles.Forsyth		}
16737da2899SCharles.Forsyth
16837da2899SCharles.Forsyth		# send an ack
16937da2899SCharles.Forsyth		put2(buf, 0, Ack);
17037da2899SCharles.Forsyth		put2(buf, 2, lastblock);
17137da2899SCharles.Forsyth	}
17237da2899SCharles.Forsyth	kill(tftppid);
17337da2899SCharles.Forsyth	return nil;
17437da2899SCharles.Forsyth}
175