1implement Tftp; 2 3include "sys.m"; 4 sys: Sys; 5 6include "dial.m"; 7 dial: Dial; 8 9include "tftp.m"; 10 11Maxretry: con 5; # retries per block 12Maxblock: con 512; # protocol's usual maximum data block size 13Tftphdrlen: con 4; 14Read, Write, Data, Ack, Error: con 1+iota; # tftp opcode 15 16progress: int; 17 18put2(buf: array of byte, o: int, val: int) 19{ 20 buf[o] = byte (val >> 8); 21 buf[o+1] = byte val; 22} 23 24get2(buf: array of byte, o: int): int 25{ 26 return (int buf[o] << 8) | int buf[o+1]; 27} 28 29kill(pid: int) 30{ 31 fd := sys->open("#p/" + string pid + "/ctl", sys->OWRITE); 32 if(fd == nil) 33 return; 34 35 msg := array of byte "kill"; 36 sys->write(fd, msg, len msg); 37} 38 39timeoutproc(c: chan of int, howlong: int) 40{ 41 c <-= sys->pctl(0, nil); 42 sys->sleep(howlong); 43 c <-= 1; 44} 45 46tpid := -1; 47 48timeoutcancel() 49{ 50 if(tpid >= 0) { 51 kill(tpid); 52 tpid = -1; 53 } 54} 55 56timeoutstart(howlong: int): chan of int 57{ 58 timeoutcancel(); 59 tc := chan of int; 60 spawn timeoutproc(tc, howlong); 61 tpid = <-tc; 62 return tc; 63} 64 65init(p: int) 66{ 67 sys = load Sys Sys->PATH; 68 dial = load Dial Dial->PATH; 69 progress = p; 70} 71 72reader(pidc: chan of int, fd: ref Sys->FD, bc: chan of array of byte) 73{ 74 pid := sys->pctl(0, nil); 75 pidc <-= pid; 76 buf := array[Tftphdrlen + Maxblock] of byte; 77 for(;;){ 78 n := sys->read(fd, buf, len buf); 79 bc <-= buf[0 : n]; 80 } 81} 82 83receive(host: string, filename: string, fd: ref Sys->FD): string 84{ 85 rbuf: array of byte; 86 87 conn := dial->dial(dial->netmkaddr(host, "udp", "69"), nil); 88 if(conn == nil) 89 return sys->sprint("can't dial %s: %r", host); 90 buf := array[Tftphdrlen + Maxblock] of byte; 91 i := 0; 92 put2(buf, i, Read); 93 i += 2; 94 a := array of byte filename; 95 buf[i:] = a; 96 i += len a; 97 buf[i++] = byte 0; 98 mode := array of byte "binary"; 99 buf[i:] = mode; 100 i += len mode; 101 buf[i++] = byte 0; 102 pidc := chan of int; 103 bc := chan of array of byte; 104 spawn reader(pidc, conn.dfd, bc); 105 tftppid := <-pidc; 106 lastblock := 0; 107 for(;;) { 108 Retry: 109 for(count := 0;; count++) { 110 if(count >= Maxretry){ 111 kill(tftppid); 112 return sys->sprint("tftp timeout"); 113 } 114 115 # (re)send request/ack 116 if(sys->write(conn.dfd, buf, i) < 0) { 117 kill(tftppid); 118 return sys->sprint( "error writing %s/data: %r", conn.dir); 119 } 120 121 # wait for next block 122 mtc := timeoutstart(3000); 123 for(;;){ 124 alt { 125 <-mtc => 126 if(progress) 127 sys->print("T"); 128 continue Retry; 129 rbuf = <-bc => 130 if(len rbuf < Tftphdrlen) 131 break; 132 op := get2(rbuf, 0); 133 case op { 134 Data => 135 block := get2(rbuf, 2); 136 if(block == lastblock + 1) { 137 timeoutcancel(); 138 break Retry; 139 }else if(progress) 140 sys->print("S"); 141 Error => 142 timeoutcancel(); 143 kill(tftppid); 144 return sys->sprint("server error %d: %s", get2(rbuf, 2), string rbuf[4:]); 145 * => 146 timeoutcancel(); 147 kill(tftppid); 148 return sys->sprint("phase error op=%d", op); 149 } 150 } 151 } 152 } 153 n := len rbuf; 154 # copy the data somewhere 155 if(sys->write(fd, rbuf[Tftphdrlen:], n - Tftphdrlen) < 0) { 156 kill(tftppid); 157 return sys->sprint("writing destination: %r"); 158 } 159 lastblock++; 160 if(progress && lastblock % 25 == 0) 161 sys->print("."); 162 if(n < Maxblock + Tftphdrlen) { 163 if(progress) 164 sys->print("\n"); 165 break; 166 } 167 168 # send an ack 169 put2(buf, 0, Ack); 170 put2(buf, 2, lastblock); 171 } 172 kill(tftppid); 173 return nil; 174} 175