6 typedef struct Dict Dict;
7 typedef struct Piece Piece;
8 typedef struct File File;
9 typedef struct Stats Stats;
17 char typ; // i, d, s, l
55 char *deftrack = "http://exodus.desync.com/announce";
56 char *mntweb = "/mnt/web";
57 char *useragent = "torrent";
58 uchar infohash[SHA1dlen];
75 return nhavepieces >= npieces;
90 bparse(char *s, char *e, Dict **dp)
112 s = bparse(s, e, dp);
116 d = *dp = mallocz(sizeof(*d), 1);
120 s = bparse(s, e, &d->val);
131 if((s = memchr(x, 'e', e - x)) == nil)
137 if((x = memchr(s, ':', e - s)) == nil)
140 if((n = atoi(s)) < 0)
143 if((s > e) || (s < x)){
149 d = mallocz(sizeof(*d) + n+1, 1);
151 memmove(d->str, x, d->len = n);
160 if(d && (d->typ == 's' || d->typ == 'i'))
166 dlook(Dict *d, char *s)
168 for(; d && d->typ == 'd'; d = d->next)
169 if(d->len && strcmp(d->str, s) == 0)
175 readall(int fd, char **p)
181 while(*p = realloc(*p, n+1024)){
182 if((r = read(fd, *p+n, 1024)) <= 0)
190 rwpiece(int wr, int index, uchar *data, int len, int poff)
196 if(len <= 0 || poff < 0 || poff >= pieces[index].len)
198 if(len+poff > pieces[index].len)
199 len = pieces[index].len - poff;
200 off = (vlong)index * (vlong)blocksize;
202 for(f = files; f; f = f->next)
203 if((f->off+f->len) > off)
206 n = ((off + len) > f->len) ? f->len - off : len;
207 if((n = (wr ? pwrite(f->fd, data, n, off) : pread(f->fd, data, n, off))) <= 0)
209 if((m = rwpiece(wr, index, data + n, len - n, poff + n)) < 0)
215 havepiece(int x, char *from)
217 uchar *p, m, hash[SHA1dlen];
221 if(havemap[x>>3] & m)
223 p = malloc(blocksize);
225 if(rwpiece(0, x, p, n, 0) != n){
229 sha1(p, n, hash, nil);
231 if(memcmp(hash, pieces[x].hash, sizeof(hash))){
232 if(debug && from != nil)
233 fprint(2, "peer %s: damaged piece %d\n", from, x);
237 if((havemap[x>>3] & m) == 0){
240 stats.left -= pieces[x].len;
243 if(debug && from != nil)
244 fprint(2, "peer %s: completed piece %d\n", from, x);
249 pickpiece(uchar *map)
256 for(i = 0; i<nhavemap; i++){
259 for(x = i<<3, m = 0x80; m; m >>= 1, x++){
260 if((~map[i] | havemap[i]) & m)
270 unpack(uchar *s, int n, char *fmt, ...)
284 if(s+1 > e) goto Err;
285 *va_arg(arg, int*) = *s++;
288 if(s+2 > e) goto Err;
289 *va_arg(arg, int*) = s[0]<<8 | s[1];
293 if(s+4 > e) goto Err;
294 *va_arg(arg, int*) = s[0]<<24 | s[1]<<16 | s[2]<<8 | s[3];
298 if(s+8 > e) goto Err;
299 *va_arg(arg, vlong*) =
320 pack(uchar *s, int n, char *fmt, ...)
336 i = va_arg(arg, int);
338 if(s+1 > e) goto Err;
342 i = va_arg(arg, int);
343 if(s+2 > e) goto Err;
344 *s++ = (i>>8) & 0xFF;
348 i = va_arg(arg, int);
349 if(s+4 > e) goto Err;
350 *s++ = (i>>24) & 0xFF;
351 *s++ = (i>>16) & 0xFF;
352 *s++ = (i>>8) & 0xFF;
356 v = va_arg(arg, vlong);
357 if(s+8 > e) goto Err;
358 *s++ = (v>>56) & 0xFF;
359 *s++ = (v>>48) & 0xFF;
360 *s++ = (v>>40) & 0xFF;
361 *s++ = (v>>32) & 0xFF;
362 *s++ = (v>>24) & 0xFF;
363 *s++ = (v>>16) & 0xFF;
364 *s++ = (v>>8) & 0xFF;
368 i = va_arg(arg, int);
369 if(s+i > e) goto Err;
370 memmove(s, va_arg(arg, void*), i);
385 peer(int fd, int incoming, char *addr)
387 uchar buf[64+MAXIO], *map, *told, *p, m;
388 int mechoking, hechoking;
390 int workpiece, workoffset;
393 if(debug) fprint(2, "peer %s: %s connected\n", addr, incoming ? "incoming" : "outgoing");
396 if((incoming && i) || (!incoming && !i)){
397 if(debug) fprint(2, "peer %s: -> handshake\n", addr);
398 n = pack(buf, sizeof(buf), "*________**",
399 20, "\x13BitTorrent protocol",
400 sizeof(infohash), infohash,
401 sizeof(peerid), peerid);
402 if(write(fd, buf, n) != n)
405 if((incoming && !i) || (!incoming && i)){
406 n = 20 + 8 + sizeof(infohash);
407 if((n = readn(fd, buf, n)) != n)
409 if(memcmp(buf, "\x13BitTorrent protocol", 20))
411 if(memcmp(infohash, buf + 20 + 8, sizeof(infohash)))
413 if(debug) fprint(2, "peer %s: <- handshake\n", addr);
416 if(readn(fd, buf, sizeof(peerid)) != sizeof(peerid))
418 if(memcmp(peerid, buf, sizeof(peerid)) == 0)
420 if(debug) fprint(2, "peer %s: peerid %.*s\n", addr, sizeof(peerid), (char*)buf);
429 map = mallocz(nhavemap, 1);
430 told = malloc(nhavemap);
432 if(debug) fprint(2, "peer %s: -> bitfield %d\n", addr, nhavemap);
433 memmove(told, havemap, nhavemap);
434 n = pack(buf, sizeof(buf), "lb*", nhavemap+1, 0x05, nhavemap, told);
435 if(write(fd, buf, n) != n)
439 for(i=0; i<nhavemap; i++){
440 if(told[i] != havemap[i]){
441 for(x = i<<3, m = 0x80; m; m >>= 1, x++){
442 if((~havemap[i] | told[i] | map[i]) & m)
445 if(debug) fprint(2, "peer %s: -> have %d\n", addr, x);
446 n = pack(buf, sizeof(buf), "lbl", 1+4, 0x04, x);
447 if(write(fd, buf, n) != n)
451 if(!mewant && (map[i] & ~havemap[i])){
453 if(debug) fprint(2, "peer %s: -> interested\n", addr);
454 n = pack(buf, sizeof(buf), "lb", 1, 0x02);
455 if(write(fd, buf, n) != n)
459 if(!hechoking && mewant){
461 if(x < 0 || (havemap[x>>3]&(0x80>>(x&7))) != 0 || workoffset >= pieces[x].len)
464 o = workpiece != x ? pieces[x].brk : workoffset;
465 l = pieces[x].len - o;
470 if(debug) fprint(2, "peer %s: -> request %d %d %d\n", addr, x, o, l);
471 n = pack(buf, sizeof(buf), "lblll", 1+4+4+4, 0x06, x, o, l);
472 if(write(fd, buf, n) != n)
476 if(mechoking && hewant){
478 if(debug) fprint(2, "peer %s: -> unchoke\n", addr);
479 n = pack(buf, sizeof(buf), "lb", 1, 0x01);
480 if(write(fd, buf, n) != n)
484 if(readn(fd, buf, 4) != 4)
486 unpack(buf, 4, "l", &n);
487 if(n < 0 || n > sizeof(buf))
491 if(readn(fd, buf, n) != n)
500 if(debug) fprint(2, "peer %s: <- choke\n", addr);
502 case 0x01: // Unchoke
504 if(debug) fprint(2, "peer %s: <- unchoke\n", addr);
506 case 0x02: // Interested
508 if(debug) fprint(2, "peer %s: <- interested\n", addr);
510 case 0x03: // Notinterested
512 if(debug) fprint(2, "peer %s: <- notinterested\n", addr);
514 case 0x04: // Have <piceindex>
515 if(unpack(p, n, "l", &x) < 0)
517 if(debug) fprint(2, "peer %s: <- have %d\n", addr, x);
518 if(x < 0 || x >= npieces)
520 map[x>>3] |= 0x80>>(x&7);
522 case 0x05: // Bitfield
523 if(debug) fprint(2, "peer %s: <- bitfield %d\n", addr, n);
528 case 0x06: // Request <index> <begin> <length>
529 if(unpack(p, n, "lll", &x, &o, &l) < 0)
531 if(debug) fprint(2, "peer %s: <- request %d %d %d\n", addr, x, o, l);
532 if(x < 0 || x >= npieces)
534 if(!hewant || mechoking || (~havemap[x>>3]&(0x80>>(x&7))))
536 if(debug) fprint(2, "peer %s: -> piece %d %d\n", addr, x, o);
540 if((l = rwpiece(0, x, buf + n, l, o)) <= 0)
542 n = pack(buf, sizeof(buf), "lbll", 1+4+4+l, 0x07, x, o);
544 if(write(fd, buf, n) != n)
550 case 0x07: // Piece <index> <begin> <block>
551 if(unpack(p, n, "ll", &x, &o) != 8)
558 if(debug) fprint(2, "peer %s: <- piece %d %d %d\n", addr, x, o, n);
559 if(x < 0 || x >= npieces)
561 if((havemap[x>>3]&(0x80>>(x&7))) != 0)
563 if(o < 0 || o >= pieces[x].len)
565 if(o+n > pieces[x].len)
566 n = o - pieces[x].len;
567 if((o > pieces[x].brk) || (o+n <= pieces[x].brk))
569 n = rwpiece(1, x, p, n, o);
573 if(o+n >= pieces[x].len && !havepiece(x, addr)){
575 /* backoff from this piece for a while */
580 case 0x08: // Cancel <index> <begin> <length>
581 if(unpack(p, n, "lll", &x, &o, &l) < 0)
583 if(debug) fprint(2, "peer %s: <- cancel %d %d %d\n", addr, x, o, l);
585 case 0x09: // Port <port>
586 if(unpack(p, n, "l", &x) < 0)
588 if(debug) fprint(2, "peer %s: <- port %d\n", addr, x);
602 char addr[64], adir[40], ldir[40];
603 int afd, lfd, dfd, pid, nprocs;
608 for(port=6881; port<6890; port++){
609 snprint(addr, sizeof(addr), "tcp!*!%d", port);
610 if((afd = announce(addr, adir)) >= 0)
614 fprint(2, "announce: %r");
617 if(rfork(RFFDG|RFPROC|RFMEM))
620 if((lfd = listen(adir, ldir)) < 0){
621 fprint(2, "listen: %r");
624 while(nprocs >= SRVPROCS)
628 if(pid = rfork(RFFDG|RFPROC|RFMEM)){
634 if((dfd = accept(lfd, ldir)) < 0){
635 fprint(2, "accept: %r");
638 ni = getnetconninfo(ldir, dfd);
639 peer(dfd, 1, ni ? ni->raddr : "???");
640 if(ni) freenetconninfo(ni);
647 client(char *ip, char *port)
649 static Dict *peerqh, *peerqt;
650 static QLock peerslk;
656 if(ip == nil || port == nil)
659 d = mallocz(sizeof(*d) + 64, 1);
660 snprint(addr = d->str, 64, "tcp!%s!%s", ip, port);
662 if(dlook(peerqh, addr)){
667 d->len = strlen(addr);
670 /* enqueue to front */
671 if((d->next = peerqh) == nil)
674 if(nprocs >= CLIPROCS){
680 if(rfork(RFFDG|RFPROC|RFMEM|RFNOWAIT))
685 /* dequeue and put to tail */
687 if((peerqh = d->next) == nil)
701 if(debug) fprint(2, "client %s\n", addr);
702 if((fd = dial(addr, nil, nil, nil)) >= 0){
706 sleep(1000+nrand(5000));
711 hopen(char *url, ...)
713 int conn, ctlfd, fd, n;
717 snprint(buf, sizeof buf, "%s/clone", mntweb);
718 if((ctlfd = open(buf, ORDWR)) < 0)
720 if((n = read(ctlfd, buf, sizeof buf-1)) <= 0){
728 n = 4+vsnprint(buf+4, sizeof(buf)-4, url, arg);
730 if(write(ctlfd, buf, n) != n){
735 if(useragent != nil && useragent[0] != '\0'){
736 n = snprint(buf, sizeof buf, "useragent %s", useragent);
737 write(ctlfd, buf, n);
739 snprint(buf, sizeof buf, "%s/%d/body", mntweb, conn);
740 if((fd = open(buf, OREAD)) < 0)
747 webseed(Dict *w, File *f)
749 int fd, err, n, m, o, p, x, y;
755 if(w == nil || f == nil || finished())
757 if(rfork(RFPROC|RFMEM))
761 if(debug) fprint(2, "webseed %s %s\n", w->str, f->name);
762 s = strrchr(w->str, '/');
764 fd = hopen("%s%s", w->str, f->name);
766 fd = hopen("%s", w->str);
769 if(debug) fprint(2, "webseed %s %s: %r\n", w->str, f->name);
772 if((w = w->next) == w0)
780 while(len > 0 && !finished()){
784 if((n = read(fd, buf, m)) <= 0)
788 p = off - (vlong)x*blocksize;
795 m = pieces[x].len - p;
798 if((havemap[x>>3] & (0x80>>(x&7))) == 0)
799 rwpiece(1, x, buf+o, m, p);
805 if(havepiece(x++, w->str))
809 werrstr("file corrupted");
814 if(off < f->off + f->len)
815 havepiece(off / blocksize, w->str);
816 havepiece(f->off / blocksize, w->str);
822 clients4(uchar *p, int len)
824 char ip[16], port[6];
828 snprint(ip, sizeof(ip), "%d.%d.%d.%d", p[0], p[1], p[2], p[3]);
829 snprint(port, sizeof(port), "%d", p[4]<<8 | p[5]);
836 webtracker(char *url)
842 if(rfork(RFPROC|RFMEM))
844 if(debug) fprint(2, "webtracker %s\n", url);
846 event = "&event=started";
848 vlong up, down, left;
857 if((fd = hopen("%s?info_hash=%.*H&peer_id=%.*H&port=%d&"
858 "uploaded=%lld&downloaded=%lld&left=%lld&compact=1&no_peer_id=1%s",
859 url, sizeof(infohash), infohash, sizeof(peerid), peerid, port,
860 up, down, left, event)) >= 0){
866 } else if(debug) fprint(2, "tracker %s: %r\n", url);
867 /* check errors and warnings */
868 if(p = dstr(dlook(d, "failure reason"))) {
870 fprint(2, "tracker failure: %s\n", p);
873 if(p = dstr(dlook(d, "warning message")))
875 fprint(2, "tracker warning: %s\n", p);
876 if(l = dlook(d, "peers")){
878 clients4((uchar*)l->str, l->len);
879 else for(; l && l->typ == 'l'; l = l->next)
880 client(dstr(dlook(l->val, "ip")), dstr(dlook(l->val, "port")));
883 if(p = dstr(dlook(d, "interval")))
885 if(n < 10 | n > 60*60)
888 sleep(n * 1000 + nrand(5000));
893 udpaddr(char addr[64], int naddr, char *url)
898 if((url = strchr(url, ':')) == nil)
903 if(x = strchr(url, ':')){
907 if((x = strchr(url, '/')) == nil)
910 snprint(addr, naddr, "udp!%.*s!%d", utfnlen(url, x-url), url, port);
915 udptracker(char *url)
917 int fd, event, n, m, a, i;
918 int transid, interval;
923 if(udpaddr(addr, sizeof(addr), url) < 0)
925 if(rfork(RFPROC|RFMEM))
927 if(debug) fprint(2, "udptracker %s\n", addr);
932 if((fd = dial(addr, 0, 0, 0)) < 0)
937 n = pack(buf, sizeof(buf), "vll", 0x41727101980LL, 0, transid);
938 if(write(fd, buf, n) != n)
941 if((n = read(fd, buf, sizeof(buf))) <= 0)
943 if(unpack(buf, n, "llv", &a, &i, &connid) < 0)
945 if(a == 0 && i == transid)
953 n = pack(buf, sizeof(buf), "vll**vvvl____llw",
955 sizeof(infohash), infohash,
956 sizeof(peerid), peerid,
967 if(write(fd, buf, n) != n)
970 if((n = read(fd, buf, sizeof(buf))) <= 0)
972 if((m = unpack(buf, n, "lll________", &a, &i, &interval)) < 0)
974 if(a == 1 && i == transid){
975 clients4(buf+m, n - m);
984 if(interval < 10 | interval > 60*60)
986 sleep(interval * 1000 + nrand(5000));
993 static Dict *trackers;
994 static QLock trackerslk;
1001 if(dlook(trackers, url)){
1002 qunlock(&trackerslk);
1006 d = mallocz(sizeof(*d) + n+1, 1);
1007 strcpy(d->str, url);
1014 qunlock(&trackerslk);
1015 if(!cistrncmp(url, "udp:", 4))
1025 s = va_arg(f->args, uchar*);
1026 if(f->flags & FmtPrec)
1029 e = s + strlen((char*)s);
1031 if(fmtprint(f, *s && ((*s >= '0' && *s <= '9') ||
1032 (*s >= 'a' && *s <= 'z') ||
1033 (*s >= 'A' && *s <= 'Z') ||
1034 strchr(".-_~", *s)) ? "%c" : "%%%.2x", *s) < 0)
1040 mktorrent(int fd, Dict *alist, Dict *wlist)
1042 uchar *b, h[SHA1dlen];
1046 if((d = dirfstat(fd)) == nil)
1048 if(d->qid.type & QTDIR){
1050 werrstr("file is a directory");
1055 werrstr("empty file");
1058 for(blocksize = 256*1024;;blocksize<<=1){
1059 npieces = (d->length + blocksize-1) / blocksize;
1060 if(npieces <= 8*1024 || blocksize >= 2*1024*1024)
1065 * keys in dictionaries have to be ordered alphabetically
1067 print("d8:announce%ld:%s", strlen(alist->str), alist->str);
1069 print("13:announce-listl");
1070 print("l%ld:%se", strlen(alist->str), alist->str);
1071 for(alist = alist->next; alist; alist = alist->next)
1072 print("l%ld:%se", strlen(alist->str), alist->str);
1077 print("6:lengthi%llde", d->length);
1078 print("4:name%ld:%s", strlen(d->name), d->name);
1079 print("12:piece lengthi%de", blocksize);
1080 print("6:pieces%d:", npieces*sizeof(h));
1082 b = malloc(blocksize);
1083 while((n = readn(fd, b, blocksize)) > 0){
1085 if(write(1, h, sizeof(h)) != sizeof(h)){
1092 werrstr("read failed: %r");
1100 print("8:url-listl");
1101 for(; wlist; wlist = wlist->next)
1102 print("%ld:%s", strlen(wlist->str), wlist->str);
1105 print("8:url-list%ld:%s", strlen(wlist->str), wlist->str);
1118 if(access(s, AEXIST) == 0)
1120 for(p=strchr(s+1, '/'); p; p=strchr(p+1, '/')){
1122 if(access(s, AEXIST)){
1123 if((f = create(s, OREAD, DMDIR | 0777)) < 0){
1145 s += chartorune(&r, s);
1148 if((n + runelen(r)) >= l){
1152 n += runetochar(d + n, &r);
1155 return cleanname(d);
1159 catch(void *, char *msg)
1161 if(strstr(msg, "alarm"))
1163 postnote(PNGROUP, killgroup, "kill");
1170 fprint(2, "usage: %s [ -vsdpc ] [ -m mtpt ] [ -t tracker-url ] "
1171 "[ -w webseed-url ] [ -i peerid ] [ -A useragent ] [ file ]\n", argv0);
1176 scons(char *s, Dict *t)
1182 for(l = t; l; l = l->next)
1183 if(strcmp(l->str, s) == 0)
1185 l = mallocz(sizeof(*l) + strlen(s)+1, 1);
1192 main(int argc, char *argv[])
1194 int sflag, pflag, vflag, cflag, fd, i, n;
1195 Dict *alist, *wlist, *info, *torrent, *d, *l;
1200 fmtinstall('H', Hfmt);
1201 alist = wlist = nil;
1202 sflag = pflag = vflag = cflag = 0;
1205 mntweb = EARGF(usage());
1208 alist = scons(EARGF(usage()), alist);
1211 wlist = scons(EARGF(usage()), wlist);
1229 strncpy((char*)peerid, EARGF(usage()), sizeof(peerid));
1232 useragent = EARGF(usage());
1238 if((s = getenv("NPROC")) != 0){
1239 if((nproc = atoi(s)) <= 0)
1246 if((fd = open(*argv, OREAD)) < 0)
1247 sysfatal("open: %r");
1250 alist = scons(deftrack, alist);
1251 if(mktorrent(fd, alist, wlist) < 0)
1255 if((n = readall(fd, &p)) <= 0)
1256 sysfatal("read torrent: %r");
1257 bparse(p, p+n, &torrent);
1259 alist = scons(dstr(dlook(torrent, "announce")), alist);
1260 for(d = dlook(torrent, "announce-list"); d && d->typ == 'l'; d = d->next)
1261 for(l = d->val; l && l->typ == 'l'; l = l->next)
1262 alist = scons(dstr(l->val), alist);
1264 if(d = dlook(torrent, "url-list")){
1266 wlist = scons(dstr(d->val), wlist);
1267 else for(l = d; l && l->typ == 'l'; l = l->next)
1268 wlist = scons(dstr(l->val), wlist);
1269 /* make wlist into a ring */
1270 for(l = wlist; l && l->next; l = l->next)
1272 if(l) l->next = wlist;
1275 if(alist == nil && wlist == nil)
1276 sysfatal("no trackers or webseeds in torrent");
1278 if((d = info = dlook(torrent, "info")) == nil)
1279 sysfatal("no meta info in torrent");
1280 for(s = e = d->start; d && d->typ == 'd'; d = d->next)
1282 sha1((uchar*)s, e - s, (uchar*)infohash, nil);
1286 if(d = dlook(info, "files")){
1287 for(; d && d->typ == 'l'; d = d->next){
1290 if((s = dstr(dlook(d->val, "length"))) == nil)
1292 f = mallocz(sizeof(*f), 1);
1294 f->name = dstr(dlook(info, "name"));
1295 for(di = dlook(d->val, "path"); di && di->typ == 'l'; di = di->next)
1296 if(s = dstr(di->val))
1297 f->name = f->name ? smprint("%s/%s", f->name, s) : s;
1301 } else if(s = dstr(dlook(info, "length"))){
1302 f = mallocz(sizeof(*f), 1);
1304 f->name = dstr(dlook(info, "name"));
1308 for(f = files; f; f = f->next){
1309 if(f->name == nil || f->len <= 0)
1310 sysfatal("bogus file entry in meta info");
1311 s = fixnamedup(f->name);
1312 if(vflag) fprint(pflag ? 2 : 1, "%s\n", s);
1313 if((f->fd = open(s, ORDWR)) < 0){
1315 sysfatal("mkdirs: %r");
1316 if((f->fd = create(s, ORDWR, 0666)) < 0)
1317 sysfatal("create: %r");
1323 sysfatal("no files in torrent");
1325 if((s = dstr(dlook(info, "piece length"))) == nil)
1326 sysfatal("missing piece length in meta info");
1327 if((blocksize = atoi(s)) <= 0)
1328 sysfatal("bogus piece length in meta info");
1329 d = dlook(info, "pieces");
1330 if(d == nil || d->typ != 's' || d->len <= 0 || d->len % SHA1dlen)
1331 sysfatal("bad or no pices in meta info");
1332 npieces = d->len / SHA1dlen;
1333 pieces = mallocz(sizeof(Piece) * npieces, 1);
1334 nhavemap = (npieces+7) / 8;
1335 havemap = mallocz(nhavemap, 1);
1336 for(i = 0; i<npieces; i++){
1337 pieces[i].hash = (uchar*)d->str + i*SHA1dlen;
1339 pieces[i].len = len;
1341 pieces[i].len = blocksize;
1342 len -= pieces[i].len;
1343 stats.left += pieces[i].len;
1346 sysfatal("pieces do not match file length");
1348 for(i=0; i<nproc; i++){
1349 switch(rfork(RFPROC|RFMEM)){
1351 sysfatal("fork: %r");
1353 for(; i<npieces; i+=nproc)
1358 while(waitpid() >= 0)
1361 if(finished() && !sflag)
1366 switch(i = rfork(RFPROC|RFMEM|RFNOTEG)){
1368 sysfatal("fork: %r");
1371 strncpy((char*)peerid, "-NF9001-", 9);
1372 for(i=sizeof(peerid)-1; i >= 0 && peerid[i] == 0; i--)
1373 peerid[i] = nrand(10)+'0';
1375 for(; alist; alist = alist->next)
1376 tracker(alist->str);
1377 for(f = files, l = wlist; f && l; f = f->next, l = l->next)
1379 while(waitpid() != -1)
1387 print("%d %d\n", nhavepieces, npieces);
1388 } while(!finished() || sflag);
1390 postnote(PNGROUP, killgroup, "kill");