ip/torrent: webseed support
This commit is contained in:
parent
53511a9097
commit
fd02ddae19
2 changed files with 131 additions and 17 deletions
|
@ -14,7 +14,10 @@ torrent \- bittorrent client
|
||||||
.I mtpt
|
.I mtpt
|
||||||
] [
|
] [
|
||||||
.B -t
|
.B -t
|
||||||
.I url
|
.I tracker-url
|
||||||
|
] [
|
||||||
|
.B -w
|
||||||
|
.I webseed-url
|
||||||
] [
|
] [
|
||||||
.B -s
|
.B -s
|
||||||
] [
|
] [
|
||||||
|
@ -44,13 +47,22 @@ reads the file given at the final
|
||||||
.I file
|
.I file
|
||||||
argument (or standard-input when omited) and writes
|
argument (or standard-input when omited) and writes
|
||||||
a torrent file to standard-output and exits.
|
a torrent file to standard-output and exits.
|
||||||
A tracker
|
A
|
||||||
.I url
|
.I tracker-url
|
||||||
should be given with the
|
should be given with the
|
||||||
.B -t
|
.B -t
|
||||||
option in that case. A list of trackers can be obtained
|
option in that case. A list of trackers can be obtained
|
||||||
on the web, see the examples below.
|
on the web, see the examples below.
|
||||||
.PP
|
.PP
|
||||||
|
If the files in the torrent are also available from a url, a
|
||||||
|
.I webseed-url
|
||||||
|
can be passed with the
|
||||||
|
.B -w
|
||||||
|
option. If
|
||||||
|
.I webseed-url
|
||||||
|
ends with a slash, the filename, from the torrent, concatinated
|
||||||
|
with the url forms the target url.
|
||||||
|
.PP
|
||||||
Without the
|
Without the
|
||||||
.B -c
|
.B -c
|
||||||
option,
|
option,
|
||||||
|
|
|
@ -65,6 +65,12 @@ int nhavepieces;
|
||||||
File *files;
|
File *files;
|
||||||
Stats stats;
|
Stats stats;
|
||||||
|
|
||||||
|
int
|
||||||
|
finished(void)
|
||||||
|
{
|
||||||
|
return nhavepieces >= npieces;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
freedict(Dict *d)
|
freedict(Dict *d)
|
||||||
{
|
{
|
||||||
|
@ -646,6 +652,78 @@ hopen(char *url, ...)
|
||||||
return fd;
|
return fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
webseed(Dict *w, File *f)
|
||||||
|
{
|
||||||
|
vlong off, woff;
|
||||||
|
int fd, n, m, r, p, x, y, err;
|
||||||
|
uchar buf[MAXIO];
|
||||||
|
Dict *w0;
|
||||||
|
char *s;
|
||||||
|
|
||||||
|
if(w == nil || f == nil || finished())
|
||||||
|
return;
|
||||||
|
if(rfork(RFPROC|RFMEM))
|
||||||
|
return;
|
||||||
|
w0 = w;
|
||||||
|
Retry:
|
||||||
|
if(debug) fprint(2, "webseed %s %s\n", w->str, f->name);
|
||||||
|
s = strrchr(w->str, '/');
|
||||||
|
if(s && s[1] == 0)
|
||||||
|
fd = hopen("%s%s", w->str, f->name);
|
||||||
|
else
|
||||||
|
fd = hopen("%s", w->str);
|
||||||
|
if(fd < 0){
|
||||||
|
if(finished())
|
||||||
|
exits(0);
|
||||||
|
if((w = w->next) == w0)
|
||||||
|
exits(0);
|
||||||
|
goto Retry;
|
||||||
|
}
|
||||||
|
off = 0;
|
||||||
|
err = 0;
|
||||||
|
while(off < f->len){
|
||||||
|
if(finished())
|
||||||
|
break;
|
||||||
|
n = MAXIO;
|
||||||
|
if((f->len - off) < n)
|
||||||
|
n = (f->len - off);
|
||||||
|
if((n = read(fd, buf, n)) <= 0)
|
||||||
|
break;
|
||||||
|
woff = f->off + off;
|
||||||
|
x = woff / blocksize;
|
||||||
|
off += n;
|
||||||
|
y = (f->off + off) / blocksize;
|
||||||
|
p = woff - x*blocksize;
|
||||||
|
m = 0;
|
||||||
|
while(m < n){
|
||||||
|
r = pieces[x].len - p;
|
||||||
|
if(r > (n-m))
|
||||||
|
r = n-m;
|
||||||
|
if((havemap[x>>3] & (0x80>>(x&7))) == 0)
|
||||||
|
if(rwpiece(1, x, buf+m, r, p) != r)
|
||||||
|
goto Err;
|
||||||
|
if(x == y)
|
||||||
|
break;
|
||||||
|
m += r;
|
||||||
|
p = 0;
|
||||||
|
if(!havepiece(x++)){
|
||||||
|
if(++err > 10){
|
||||||
|
fprint(2, "webseed corrupt %s / %s\n",
|
||||||
|
w->str, f->name);
|
||||||
|
goto Err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
havepiece(f->off / blocksize);
|
||||||
|
havepiece((f->off+f->len) / blocksize);
|
||||||
|
Err:
|
||||||
|
close(fd);
|
||||||
|
exits(0);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
tracker(char *url)
|
tracker(char *url)
|
||||||
{
|
{
|
||||||
|
@ -746,7 +824,7 @@ Hfmt(Fmt *f)
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
mktorrent(int fd, Dict *alist)
|
mktorrent(int fd, Dict *alist, Dict *wlist)
|
||||||
{
|
{
|
||||||
uchar *b, h[20];
|
uchar *b, h[20];
|
||||||
Dir *d;
|
Dir *d;
|
||||||
|
@ -779,6 +857,15 @@ mktorrent(int fd, Dict *alist)
|
||||||
print("l%ld:%se", strlen(alist->str), alist->str);
|
print("l%ld:%se", strlen(alist->str), alist->str);
|
||||||
print("e");
|
print("e");
|
||||||
}
|
}
|
||||||
|
if(wlist){
|
||||||
|
if(wlist->next){
|
||||||
|
print("8:url-listl");
|
||||||
|
for(; wlist; wlist = wlist->next)
|
||||||
|
print("%ld:%s", strlen(wlist->str), wlist->str);
|
||||||
|
print("e");
|
||||||
|
} else
|
||||||
|
print("8:url-list%ld:%s", strlen(wlist->str), wlist->str);
|
||||||
|
}
|
||||||
print("4:info");
|
print("4:info");
|
||||||
print("d");
|
print("d");
|
||||||
print("4:name%ld:%s", strlen(d->name), d->name);
|
print("4:name%ld:%s", strlen(d->name), d->name);
|
||||||
|
@ -885,13 +972,13 @@ void
|
||||||
main(int argc, char *argv[])
|
main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
int sflag, pflag, vflag, cflag, fd, i, n;
|
int sflag, pflag, vflag, cflag, fd, i, n;
|
||||||
Dict *alist, *info, *torrent, *d, *l;
|
Dict *alist, *wlist, *info, *torrent, *d, *l;
|
||||||
char *p, *s, *e;
|
char *p, *s, *e;
|
||||||
File **fp, *f;
|
File **fp, *f;
|
||||||
vlong len;
|
vlong len;
|
||||||
|
|
||||||
fmtinstall('H', Hfmt);
|
fmtinstall('H', Hfmt);
|
||||||
alist = nil;
|
alist = wlist = nil;
|
||||||
sflag = pflag = vflag = cflag = 0;
|
sflag = pflag = vflag = cflag = 0;
|
||||||
ARGBEGIN {
|
ARGBEGIN {
|
||||||
case 'm':
|
case 'm':
|
||||||
|
@ -900,6 +987,9 @@ main(int argc, char *argv[])
|
||||||
case 't':
|
case 't':
|
||||||
alist = scons(EARGF(usage()), alist);
|
alist = scons(EARGF(usage()), alist);
|
||||||
break;
|
break;
|
||||||
|
case 'w':
|
||||||
|
wlist = scons(EARGF(usage()), wlist);
|
||||||
|
break;
|
||||||
case 's':
|
case 's':
|
||||||
sflag = 1;
|
sflag = 1;
|
||||||
break;
|
break;
|
||||||
|
@ -926,7 +1016,7 @@ main(int argc, char *argv[])
|
||||||
if(cflag){
|
if(cflag){
|
||||||
if(alist == nil)
|
if(alist == nil)
|
||||||
alist = scons(deftrack, alist);
|
alist = scons(deftrack, alist);
|
||||||
if(mktorrent(fd, alist) < 0)
|
if(mktorrent(fd, alist, wlist) < 0)
|
||||||
sysfatal("%r");
|
sysfatal("%r");
|
||||||
exits(0);
|
exits(0);
|
||||||
}
|
}
|
||||||
|
@ -937,8 +1027,18 @@ main(int argc, char *argv[])
|
||||||
for(d = dlook(torrent, "announce-list"); d && d->typ == 'l'; d = d->next)
|
for(d = dlook(torrent, "announce-list"); d && d->typ == 'l'; d = d->next)
|
||||||
for(l = d->val; l && l->typ == 'l'; l = l->next)
|
for(l = d->val; l && l->typ == 'l'; l = l->next)
|
||||||
alist = scons(dstr(l->val), alist);
|
alist = scons(dstr(l->val), alist);
|
||||||
if(alist == nil)
|
if(d = dlook(torrent, "url-list")){
|
||||||
sysfatal("no trackers in torrent");
|
if(d->typ == 's')
|
||||||
|
wlist = scons(dstr(d->val), wlist);
|
||||||
|
else for(; d && d->typ == 'l'; d = d->next)
|
||||||
|
wlist = scons(dstr(d->val), wlist);
|
||||||
|
/* make wlist into a ring */
|
||||||
|
for(l = wlist; l && l->next; l = l->next)
|
||||||
|
;
|
||||||
|
if(l) l->next = wlist;
|
||||||
|
}
|
||||||
|
if(alist == nil && wlist == nil)
|
||||||
|
sysfatal("no trackers or webseeds in torrent");
|
||||||
if((d = info = dlook(torrent, "info")) == nil)
|
if((d = info = dlook(torrent, "info")) == nil)
|
||||||
sysfatal("no meta info in torrent");
|
sysfatal("no meta info in torrent");
|
||||||
for(s = e = d->start; d && d->typ == 'd'; d = d->next)
|
for(s = e = d->start; d && d->typ == 'd'; d = d->next)
|
||||||
|
@ -972,12 +1072,12 @@ main(int argc, char *argv[])
|
||||||
for(f = files; f; f = f->next){
|
for(f = files; f; f = f->next){
|
||||||
if(f->name == nil || f->len <= 0)
|
if(f->name == nil || f->len <= 0)
|
||||||
sysfatal("bogus file entry in meta info");
|
sysfatal("bogus file entry in meta info");
|
||||||
f->name = fixnamedup(f->name);
|
s = fixnamedup(f->name);
|
||||||
if(vflag) fprint(pflag ? 2 : 1, "%s\n", f->name);
|
if(vflag) fprint(pflag ? 2 : 1, "%s\n", s);
|
||||||
if((f->fd = open(f->name, ORDWR)) < 0){
|
if((f->fd = open(s, ORDWR)) < 0){
|
||||||
if(mkdirs(f->name) < 0)
|
if(mkdirs(s) < 0)
|
||||||
sysfatal("mkdirs: %r");
|
sysfatal("mkdirs: %r");
|
||||||
if((f->fd = create(f->name, ORDWR, 0666)) < 0)
|
if((f->fd = create(s, ORDWR, 0666)) < 0)
|
||||||
sysfatal("create: %r");
|
sysfatal("create: %r");
|
||||||
}
|
}
|
||||||
f->off = len;
|
f->off = len;
|
||||||
|
@ -1024,16 +1124,18 @@ main(int argc, char *argv[])
|
||||||
server();
|
server();
|
||||||
for(; alist; alist = alist->next)
|
for(; alist; alist = alist->next)
|
||||||
tracker(alist->str);
|
tracker(alist->str);
|
||||||
|
for(f = files, l = wlist; f && l; f = f->next, l = l->next)
|
||||||
|
webseed(l, f);
|
||||||
while(waitpid() != -1)
|
while(waitpid() != -1)
|
||||||
;
|
;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
killgroup = i;
|
killgroup = i;
|
||||||
while((nhavepieces < npieces) || sflag){
|
do {
|
||||||
|
sleep(1000);
|
||||||
if(pflag)
|
if(pflag)
|
||||||
print("%d %d\n", nhavepieces, npieces);
|
print("%d %d\n", nhavepieces, npieces);
|
||||||
sleep(1000);
|
} while(!finished() || sflag);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
postnote(PNGROUP, killgroup, "kill");
|
postnote(PNGROUP, killgroup, "kill");
|
||||||
exits(0);
|
exits(0);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue