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