]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/ip/torrent.c
cf5304274f872f1e35fdabd0f8b6cec12d6b3d48
[plan9front.git] / sys / src / cmd / ip / torrent.c
1 #include <u.h>
2 #include <libc.h>
3 #include <mp.h>
4 #include <libsec.h>
5
6 typedef struct Dict Dict;
7 typedef struct Piece Piece;
8 typedef struct File File;
9
10 struct Dict
11 {
12         char    typ;    // i, d, s, l
13         Dict    *val;
14         Dict    *next;
15         char    *start, *end;
16         int     len;
17         char    str[];
18 };
19
20 struct Piece
21 {
22         uchar   *hash;
23         int     len;
24         int     brk;
25 };
26
27 struct File
28 {
29         File    *next;
30         char    *name;
31         int     fd;
32         vlong   off;
33         vlong   len;
34 };
35
36 enum {
37         MAXIO = 16*1024,
38 };
39
40 int debug, sflag, pflag, vflag;
41 int pidgroup = -1;
42 int port = 48123;
43 char *mntweb = "/mnt/web";
44 uchar infohash[20];
45 uchar peerid[20];
46 int blocksize;
47
48 int npieces;
49 Piece *pieces;
50
51 int nhavemap;
52 uchar *havemap;
53
54 File *files;
55
56 void
57 freedict(Dict *d)
58 {
59         if(d){
60                 if(d->val != d)
61                         freedict(d->val);
62                 freedict(d->next);
63                 free(d);
64         }
65 }
66
67 char*
68 bparse(char *s, char *e, Dict **dp)
69 {
70         char *x, t;
71         Dict *d;
72         int n;
73
74         *dp = nil;
75         if(s >= e)
76                 return e;
77
78         t = *s;
79         switch(t){
80         case 'd':
81         case 'l':
82                 x = s++;
83                 d = nil;
84                 while(s < e){
85                         if(*s == 'e'){
86                                 s++;
87                                 break;
88                         }
89                         if(t == 'd'){
90                                 s = bparse(s, e, dp);
91                                 if((d = *dp) == nil)
92                                         break;
93                         } else
94                                 d = *dp = mallocz(sizeof(*d), 1);
95                         d->typ = t;
96                         d->start = x;
97                         if(s < e){
98                                 s = bparse(s, e, &d->val);
99                                 dp = &d->next;
100                                 d->end = s;
101                         }
102                         x = s;
103                 }
104                 if(d)
105                         d->end = s;
106                 return s;
107         case 'i':
108                 x = ++s;
109                 if((s = memchr(x, 'e', e - x)) == nil)
110                         return e;
111                 n = s - x;
112                 s++;
113                 break;
114         default:
115                 if((x = memchr(s, ':', e - s)) == nil)
116                         return e;
117                 x++;
118                 if((n = atoi(s)) < 0)
119                         return e;
120                 s = x + n;
121                 if((s > e) || (s < x)){
122                         n = e - x;
123                         s = e;
124                 }
125                 t = 's';
126         }
127         d = mallocz(sizeof(*d) + n+1, 1);
128         d->typ = t;
129         memmove(d->str, x, d->len = n);
130         d->str[n] = 0;
131         *dp = d;
132         return s;
133 }
134
135 char*
136 dstr(Dict *d)
137 {
138         if(d && (d->typ == 's' || d->typ == 'i'))
139                 return d->str;
140         return nil;
141 }
142
143 Dict*
144 dlook(Dict *d, char *s)
145 {
146         for(; d && d->typ == 'd'; d = d->next)
147                 if(d->len && strcmp(d->str, s) == 0)
148                         return d->val;
149         return nil;
150 }
151
152 int
153 readall(int fd, char **p)
154 {
155         int n, r;
156
157         n = 0;
158         *p = nil;
159         while(*p = realloc(*p, n+1024)){
160                 if((r = read(fd, *p+n, 1024)) <= 0)
161                         break;
162                 n += r;
163         }
164         return n;
165 }
166
167 int
168 rwpiece(int wr, int index, uchar *data, int len, int poff)
169 {
170         vlong off;
171         int n, m;
172         File *f;
173
174         if(len <= 0 || poff >= pieces[index].len)
175                 return 0;
176         if(len+poff > pieces[index].len)
177                 len = pieces[index].len - poff;
178         off = (vlong)index * blocksize;
179         off += poff;
180         for(f = files; f; f = f->next)
181                 if((f->off+f->len) > off)
182                         break;
183         off -= f->off;
184         n = ((off + len) > f->len) ? f->len - off : len;
185         if((n = (wr ? pwrite(f->fd, data, n, off) : pread(f->fd, data, n, off))) <= 0)
186                 return -1;
187         if((m = rwpiece(wr, index, data + n, len - n, poff + n)) < 0)
188                 return -1;
189         return n+m;
190 }
191
192 int
193 havepiece(int x)
194 {
195         uchar *p, m, hash[20];
196         int n;
197
198         m = 0x80>>(x&7);
199         if(havemap[x>>3] & m)
200                 return 1;
201         p = malloc(blocksize);
202         n = pieces[x].len;
203         if(rwpiece(0, x, p, n, 0) != n){
204                 free(p);
205                 return 0;
206         }
207         sha1(p, n, hash, nil);
208         free(p);
209         if(memcmp(hash, pieces[x].hash, 20))
210                 return 0;
211         havemap[x>>3] |= m;
212         return 1;
213 }
214
215 int
216 pickpiece(uchar *map)
217 {
218         int i, x, r, k;
219         uchar m;
220
221         r = -1;
222         k = 0;
223         for(i = 0; i<nhavemap; i++){
224                 if(map[i] == 0)
225                         continue;
226                 for(x = i<<3, m = 0x80; m; m >>= 1, x++){
227                         if((~map[i] | havemap[i]) & m)
228                                 continue;
229                         if(nrand(++k) == 0)
230                                 r = x;
231                 }
232         }
233         return r;
234 }
235
236 int
237 unpack(uchar *s, int n, char *fmt, ...)
238 {
239         va_list arg;
240         uchar *b, *e;
241
242         b = s;
243         e = b + n;
244         va_start(arg, fmt);
245         for(; *fmt; fmt++) {
246                 switch(*fmt){
247                 case '_':
248                         s++;
249                         break;
250                 case 'b':
251                         if(s+1 > e) goto Err;
252                         *va_arg(arg, int*) = *s++;
253                         break;
254                 case 'l':
255                         if(s+4 > e) goto Err;
256                         *va_arg(arg, int*) = s[0]<<24 | s[1]<<16 | s[2]<<8 | s[3];
257                         s += 4;
258                         break;
259                 }
260         }
261         va_end(arg);
262         return s - b;
263 Err:
264         va_end(arg);
265         return -1;
266 }
267
268 int
269 pack(uchar *s, int n, char *fmt, ...)
270 {
271         va_list arg;
272         uchar *b, *e;
273         int i;
274
275         b = s;
276         e = b + n;
277         va_start(arg, fmt);
278         for(; *fmt; fmt++) {
279                 switch(*fmt){
280                 case '_':
281                         i = 0;
282                         if(0){
283                 case 'b':
284                         i = va_arg(arg, int);
285                         }
286                         if(s+1 > e) goto Err;
287                         *s++ = i & 0xFF;
288                         break;
289                 case 'l':
290                         i = va_arg(arg, int);
291                         if(s+4 > e) goto Err;
292                         *s++ = (i>>24) & 0xFF;
293                         *s++ = (i>>16) & 0xFF;
294                         *s++ = (i>>8) & 0xFF;
295                         *s++ = i & 0xFF;
296                         break;
297                 case '*':
298                         i = va_arg(arg, int);
299                         if(s+i > e) goto Err;
300                         memmove(s, va_arg(arg, uchar*), i);
301                         s += i;
302                         break;
303                 }
304         }
305         va_end(arg);
306         return s - b;
307 Err:
308         va_end(arg);
309         return -1;
310 }
311
312 void
313 peer(char *ip, char *port)
314 {
315         static Dict *peers;
316         static QLock peerslk;
317
318         uchar buf[64+MAXIO], *map, *told, *p, m;
319         char *addr;
320         int retry, i, o, l, x, n, fd;
321         int mechoking, hechoking;
322         int mewant, hewant;
323         int workpiece;
324         Dict *d;
325
326         if(ip == nil || port == nil)
327                 return;
328
329         d = mallocz(sizeof(*d) + 64, 1);
330         snprint(addr = d->str, 64, "tcp!%s!%s", ip, port);
331         qlock(&peerslk);
332         if(dlook(peers, addr)){
333                 qunlock(&peerslk);
334                 free(d);
335                 return;
336         }
337         d->len = strlen(addr);
338         d->typ = 'd';
339         d->val = d;
340         d->next = peers;
341         peers = d;
342         qunlock(&peerslk);
343
344         if(rfork(RFFDG|RFPROC|RFMEM) <= 0)
345                 return;
346
347         fd = -1;
348         retry = 0;
349         map = malloc(nhavemap);
350         told = malloc(nhavemap);
351 Retry:
352         if(fd >= 0){
353                 close(fd);
354                 sleep(10000 + nrand(5000));
355         }
356         if(++retry >= 10)
357                 goto Exit;
358
359         if(debug) fprint(2, "dial %s\n", addr);
360         if((fd = dial(addr, nil, nil, nil)) < 0)
361                 goto Retry;
362
363         if(debug) fprint(2, "peer %s: -> handshake\n", addr);
364         n = pack(buf, sizeof(buf), "*________**", 
365                 20, "\x13BitTorrent protocol",
366                 sizeof(infohash), infohash,
367                 sizeof(peerid), peerid);
368         if(write(fd, buf, n) != n)
369                 goto Retry;
370
371         if(read(fd, buf, 1) != 1)
372                 goto Retry;
373         n = buf[0] + 8 + sizeof(infohash) + sizeof(peerid);
374         if((n = readn(fd, buf+1, n)) != n)
375                 goto Retry;
376         if(debug) fprint(2, "peer %s: <- handshake %.*s\n", addr, buf[0], (char*)buf+1);
377         if(memcmp(infohash, buf + 1 + buf[0] + 8, sizeof(infohash)))
378                 goto Exit;
379
380         if(debug) fprint(2, "peer %s: -> bitfield %d\n", addr, nhavemap);
381         memmove(told, havemap, nhavemap);
382         n = pack(buf, sizeof(buf), "lb*", nhavemap+1, 0x05, nhavemap, havemap);
383         if(write(fd, buf, n) != n)
384                 goto Retry;
385
386         mechoking = 1;
387         hechoking = 1;
388         mewant = 0;
389         hewant = 0;
390         workpiece = -1;
391         memset(map, 0, nhavemap);
392         for(;;){
393                 for(i=0; i<nhavemap; i++){
394                         if(told[i] != havemap[i]){
395                                 for(x = i<<3, m = 0x80; m; m >>= 1, x++){
396                                         if((~havemap[i] | told[i] | map[i]) & m)
397                                                 continue;
398                                         told[i] |= m;
399                                         if(debug) fprint(2, "peer %s: -> have %d\n", addr, x);
400                                         n = pack(buf, sizeof(buf), "lbl", 1+4, 0x04, x);
401                                         if(write(fd, buf, n) != n)
402                                                 goto Retry;
403                                 }
404                         }
405                         if(!mewant && (map[i] & ~havemap[i])){
406                                 mewant = 1;
407                                 if(debug) fprint(2, "peer %s: -> interested\n", addr);
408                                 n = pack(buf, sizeof(buf), "lb", 1, 0x02);
409                                 if(write(fd, buf, n) != n)
410                                         goto Retry;
411                         }
412                 }
413                 if(!hechoking && mewant){
414                         x = workpiece;
415                         if(x >= 0 && pieces[x].brk < pieces[x].len)
416                                 {}
417                         else x = pickpiece(map);
418                         if(x >= 0){
419                                 o = pieces[x].brk;
420                                 l = pieces[x].len - o;
421                                 if(l > MAXIO)
422                                         l = MAXIO;
423                                 if(debug) fprint(2, "peer %s: -> request %d %d %d\n", addr, x, o, l);
424                                 n = pack(buf, sizeof(buf), "lblll", 1+4+4+4, 0x06, x, o, l);
425                                 if(write(fd, buf, n) != n)
426                                         goto Retry;
427                                 workpiece = x;
428                         }
429                 }
430                 if(mechoking && hewant){
431                         mechoking = 0;
432                         if(debug) fprint(2, "peer %s: -> unchoke\n", addr);
433                         n = pack(buf, sizeof(buf), "lb", 1, 0x01);
434                         if(write(fd, buf, n) != n)
435                                 goto Retry;
436                 }
437
438                 if(readn(fd, buf, 4) != 4)
439                         goto Retry;
440                 unpack(buf, 4, "l", &n);
441                 if(n == 0)
442                         continue;
443                 if(n < 0 || n > sizeof(buf))
444                         goto Retry;
445                 if(readn(fd, buf, n) != n)
446                         goto Retry;
447                 retry = 0;
448                 p = buf+1;
449                 n--;
450                 switch(*buf){
451                 case 0x00:      // Choke
452                         hechoking = 1;
453                         workpiece = -1;
454                         if(debug) fprint(2, "peer %s: <- choke\n", addr);
455                         break;
456                 case 0x01:      // Unchoke
457                         hechoking = 0;
458                         if(debug) fprint(2, "peer %s: <- unchoke\n", addr);
459                         break;
460                 case 0x02:      // Interested
461                         hewant = 1;
462                         if(debug) fprint(2, "peer %s: <- interested\n", addr);
463                         break;
464                 case 0x03:      // Notinterested
465                         hewant = 0;
466                         if(debug) fprint(2, "peer %s: <- notinterested\n", addr);
467                         break;
468                 case 0x04:      // Have <piceindex>
469                         if(unpack(p, n, "l", &x) < 0)
470                                 goto Retry;
471                         if(debug) fprint(2, "peer %s: <- have %d\n", addr, x);
472                         if(x < 0 || x >= npieces)
473                                 continue;
474                         map[x>>3] |= 0x80>>(x&7);
475                         break;
476                 case 0x05:      // Bitfield
477                         if(debug) fprint(2, "peer %s: <- bitfield %d\n", addr, n);
478                         if(n != nhavemap)
479                                 continue;
480                         memmove(map, p, n);
481                         break;
482                 case 0x06:      // Request <index> <begin> <length>
483                         if(unpack(p, n, "lll", &x, &o, &l) < 0)
484                                 goto Retry;
485                         if(debug) fprint(2, "peer %s: <- request %d %d %d\n", addr, x, o, l);
486                         if(x < 0 || x >= npieces)
487                                 continue;
488                         if(!hewant || mechoking || (~havemap[x>>3]&(0x80>>(x&7))))
489                                 continue;
490                         if(debug) fprint(2, "peer %s: -> piece %d %d\n", addr, x, o);
491                         n = 4+1+4+4;
492                         if(l > MAXIO)
493                                 l = MAXIO;
494                         if((l = rwpiece(0, x, buf + n, l, o)) <= 0)
495                                 continue;
496                         n = pack(buf, sizeof(buf), "lbll", 1+4+4+l, 0x07, x, o);
497                         n += l;
498                         if(write(fd, buf, n) != n)
499                                 goto Retry;
500                         break;
501                 case 0x07:      // Piece <index> <begin> <block>
502                         if(unpack(p, n, "ll", &x, &o) != 8)
503                                 goto Retry;
504                         p += 8;
505                         n -= 8;
506                         if(debug) fprint(2, "peer %s: <- piece %d %d %d\n", addr, x, o, n);
507                         if(x < 0 || x >= npieces)
508                                 continue;
509                         if((pieces[x].brk != o) || (havemap[x>>3]&(0x80>>(x&7))))
510                                 continue;
511                         if(rwpiece(1, x, p, n, o) == n){
512                                 if((pieces[x].brk = o+n) == pieces[x].len){
513                                         if(!havepiece(x))
514                                                 pieces[x].brk = 0;
515                                 }
516                         }
517                         break;
518                 case 0x08:      // Cancel <index> <begin> <length>
519                         if(unpack(p, n, "lll", &x, &o, &l) < 0)
520                                 goto Retry;
521                         if(debug) fprint(2, "peer %s: <- cancel %d %d %d\n", addr, x, o, l);
522                         break;
523                 case 0x09:      // Port <port>
524                         if(unpack(p, n, "l", &x) < 0)
525                                 goto Retry;
526                         if(debug) fprint(2, "peer %s: <- port %d\n", addr, x);
527                         break;
528                 }
529         }
530 Exit:
531         free(told);
532         free(map);
533         exits(0);
534 }
535
536 int
537 hopen(char *url, ...)
538 {
539         int conn, ctlfd, fd, n;
540         char buf[1024+1];
541         va_list arg;
542
543         snprint(buf, sizeof buf, "%s/clone", mntweb);
544         if((ctlfd = open(buf, ORDWR)) < 0)
545                 return -1;
546         if((n = read(ctlfd, buf, sizeof buf-1)) <= 0){
547                 close(ctlfd);
548                 return -1;
549         }
550         buf[n] = 0;
551         conn = atoi(buf);
552         va_start(arg, url);
553         strcpy(buf, "url ");
554         n = 4+vsnprint(buf+4, sizeof(buf)-4, url, arg);
555         va_end(arg);
556         if(write(ctlfd, buf, n) != n){
557         ErrOut:
558                 close(ctlfd);
559                 return -1;
560         }
561         snprint(buf, sizeof buf, "%s/%d/body", mntweb, conn);
562         if((fd = open(buf, OREAD)) < 0)
563                 goto ErrOut;
564         close(ctlfd);
565         return fd;
566 }
567
568 void
569 tracker(char *url)
570 {
571         static Dict *trackers;
572         static QLock trackerslk;
573
574         int n, fd;
575         char *p;
576         Dict *d, *l;
577
578         if(url == nil)
579                 return;
580
581         qlock(&trackerslk);
582         if(dlook(trackers, url)){
583                 qunlock(&trackerslk);
584                 return;
585         }
586         n = strlen(url);
587         d = mallocz(sizeof(*d) + n+1, 1);
588         strcpy(d->str, url);
589         d->len = n;
590         d->typ = 'd';
591         d->val = d;
592         d->next = trackers;
593         trackers = d;
594         url = d->str;
595         qunlock(&trackerslk);
596
597         if(rfork(RFFDG|RFPROC|RFMEM) <= 0)
598                 return;
599
600         for(;;){
601                 d = nil;
602                 if((fd = hopen("%s?info_hash=%.*H&peer_id=%.*H&port=%d&compact=1",
603                         url, sizeof(infohash), infohash, sizeof(peerid), peerid, port)) >= 0){
604                         n = readall(fd, &p);
605                         close(fd);
606                         bparse(p, p+n, &d);
607                         free(p);
608                 }
609                 if(l = dlook(d, "peers")){
610                         if(l->typ == 's'){
611                                 uchar *b, *e;
612
613                                 b = (uchar*)l->str;
614                                 e = b + l->len;
615                                 for(; b+6 <= e; b += 6){
616                                         char ip[16], port[6];
617
618                                         snprint(ip, sizeof(ip), "%d.%d.%d.%d", b[0], b[1], b[2], b[3]);
619                                         snprint(port, sizeof(port), "%d", b[4]<<8 | b[5]);
620                                         peer(ip, port);
621                                 }
622                         } else for(; l && l->typ == 'l'; l = l->next)
623                                 peer(dstr(dlook(l->val, "ip")), dstr(dlook(l->val, "port")));
624                 }
625                 n = 0;
626                 if(p = dstr(dlook(d, "interval")))
627                         n = atoi(p);
628                 if(n < 10 | n > 60*60)
629                         n = 2*60;
630                 freedict(d);
631                 sleep(n * 1000 + nrand(5000));
632         }
633 }
634
635 int
636 Hfmt(Fmt *f)
637 {
638         uchar *s, *e;
639         s = va_arg(f->args, uchar*);
640         if(f->flags & FmtPrec)
641                 e = s + f->prec;
642         else
643                 e = s + strlen((char*)s);
644         for(; s < e; s++)
645                 if(fmtprint(f, ((*s >= '0' && *s <= '9') || 
646                         (*s >= 'a' && *s <= 'z') ||
647                         (*s >= 'A' && *s <= 'Z') || 
648                         strchr(".-_~", *s)) ? "%c" : "%%%.2x", *s) < 0)
649                         return -1;
650         return 0;
651 }
652
653 int
654 progress(void)
655 {
656         int i, c;
657         uchar m;
658         c = 0;
659         for(i=0; i<nhavemap; i++)
660                 for(m = 0x80; m; m>>=1)
661                         if(havemap[i] & m)
662                                 c++;
663         if(pflag)
664                 print("%d %d\n", c, npieces);
665         return c == npieces;
666 }
667
668 void
669 killcohort(void)
670 {
671         int i;
672         for(i=0;i!=3;i++){      /* It's a long way to the kitchen */
673                 postnote(PNGROUP, pidgroup, "kill");
674                 sleep(1);
675         }
676 }
677
678 int
679 catchnote(void *, char *msg)
680 {
681         exits(msg);
682         return 0;
683 }
684
685 void
686 usage(void)
687 {
688         fprint(2, "usage: %s [ -vsdp ] [ -m mtpt ] [ torrentfile ]\n", argv0);
689         exits("usage");
690 }
691
692 void
693 main(int argc, char *argv[])
694 {
695         Dict *info, *torrent, *d;
696         File **fp, *f;
697         char *p, *s, *e;
698         int fd, i, n;
699         vlong len;
700
701         fmtinstall('H', Hfmt);
702
703         ARGBEGIN {
704         case 'm':
705                 mntweb = EARGF(usage());
706                 break;
707         case 's':
708                 sflag = 1;
709                 break;
710         case 'p':
711                 pflag = 1;
712                 break;
713         case 'v':
714                 vflag = 1;
715                 break;
716         case 'd':
717                 debug++;
718                 break;
719         default:
720                 usage();
721         } ARGEND;
722
723         fd = 0;
724         if(*argv)
725                 if((fd = open(*argv, OREAD)) < 0)
726                         sysfatal("open torrent: %r");
727         if((n = readall(fd, &p)) <= 0)
728                 sysfatal("read torrent: %r");
729         bparse(p, p+n, &torrent);
730         if((d = info = dlook(torrent, "info")) == nil)
731                 sysfatal("no meta info in torrent");
732         for(s = e = d->start; d && d->typ == 'd'; d = d->next)
733                 e = d->end;
734         sha1((uchar*)s, e - s, (uchar*)infohash, nil);
735         free(p);
736
737         fp = &files;
738         if(d = dlook(info, "files")){           
739                 for(; d && d->typ == 'l'; d = d->next){
740                         Dict *di;
741
742                         if((s = dstr(dlook(d->val, "length"))) == nil)
743                                 continue;
744                         f = mallocz(sizeof(*f), 1);
745                         f->len = atoll(s);
746                         f->name = dstr(dlook(info, "name"));
747                         for(di = dlook(d->val, "path"); di && di->typ == 'l'; di = di->next)
748                                 if(s = dstr(di->val))
749                                         f->name = f->name ? smprint("%s/%s", f->name, s) : s;
750                         *fp = f;
751                         fp = &f->next;
752                 }
753         } else if(s = dstr(dlook(info, "length"))){
754                 f = mallocz(sizeof(*f), 1);
755                 f->len = atoll(s);
756                 f->name = dstr(dlook(info, "name"));
757                 *fp = f;
758         }
759         len = 0;
760         for(f = files; f; f = f->next){
761                 if(f->name == nil || f->len <= 0)
762                         sysfatal("bogus file entry in meta info");
763                 if(vflag) fprint(pflag ? 2 : 1, "%s\n", f->name);
764                 if((f->fd = open(f->name, ORDWR)) < 0)
765                         if((f->fd = create(f->name, ORDWR, 0666)) < 0)
766                                 sysfatal("create: %r");
767                 f->off = len;
768                 len += f->len;
769         }
770         if(len <= 0)
771                 sysfatal("no files in torrent");
772
773         if((s = dstr(dlook(info, "piece length"))) == nil)
774                 sysfatal("missing piece length in meta info");
775         if((blocksize = atoi(s)) <= 0)
776                 sysfatal("bogus piece length in meta info");
777         d = dlook(info, "pieces");
778         if(d == nil || d->typ != 's' || d->len <= 0 || d->len % 20)
779                 sysfatal("bad or no pices in meta info");
780         npieces = d->len / 20;
781         pieces = mallocz(sizeof(Piece) * npieces, 1);
782         nhavemap = (npieces+7) / 8;
783         havemap = mallocz(nhavemap, 1);
784         for(i = 0; i<npieces; i++){
785                 pieces[i].hash = (uchar*)d->str + i*20;
786                 if(len < blocksize)
787                         pieces[i].len = len;
788                 else
789                         pieces[i].len = blocksize;
790                 len -= pieces[i].len;
791         }
792         if(len)
793                 sysfatal("pieces do not match file length");
794
795         for(i = 0; i<npieces; i++)
796                 havepiece(i);
797
798         switch(i = rfork(RFPROC|RFMEM|RFNOTEG|RFNAMEG)){
799         case -1:
800                 sysfatal("fork: %r");
801         case 0:
802                 memmove(peerid, "-NF9001-", 8);
803                 genrandom(peerid+8, sizeof(peerid)-8);
804                 tracker(dstr(dlook(torrent, "announce")));
805                 for(d = dlook(torrent, "announce-list"); d && d->typ == 'l'; d = d->next)
806                         if(d->val && d->val->typ == 'l')
807                                 tracker(dstr(d->val->val));
808                 break;
809         default:
810                 pidgroup = i;
811                 atexit(killcohort);
812                 atnotify(catchnote, 1);
813                 while(!progress() || sflag)
814                         sleep(1000);
815         }
816         exits(0);
817 }