shithub: riscv

Download patch

ref: bba834adeec95f66de642d43106112524aa48195
parent: b697e8655a2f0ef5cb0ad26844f1264334a416f1
author: cinap_lenrek <cinap_lenrek@centraldogma>
date: Sat Oct 22 20:55:56 EDT 2011

ip/torrent: add minimalistic BitTorrent client

--- a/sys/src/cmd/ip/mkfile
+++ b/sys/src/cmd/ip/mkfile
@@ -20,6 +20,7 @@
 	tftpd\
 	tftpfs\
 	traceroute\
+	torrent\
 	udpecho\
 	wol\
 
--- /dev/null
+++ b/sys/src/cmd/ip/torrent.c
@@ -1,0 +1,817 @@
+#include <u.h>
+#include <libc.h>
+#include <mp.h>
+#include <libsec.h>
+
+typedef struct Dict Dict;
+typedef struct Piece Piece;
+typedef struct File File;
+
+struct Dict
+{
+	char	typ;	// i, d, s, l
+	Dict	*val;
+	Dict	*next;
+	char	*start, *end;
+	int	len;
+	char	str[];
+};
+
+struct Piece
+{
+	uchar	*hash;
+	int	len;
+	int	brk;
+};
+
+struct File
+{
+	File	*next;
+	char	*name;
+	int	fd;
+	vlong	off;
+	vlong	len;
+};
+
+enum {
+	MAXIO = 16*1024,
+};
+
+int debug, sflag, pflag, vflag;
+int pidgroup = -1;
+int port = 48123;
+char *mntweb = "/mnt/web";
+uchar infohash[20];
+uchar peerid[20];
+int blocksize;
+
+int npieces;
+Piece *pieces;
+
+int nhavemap;
+uchar *havemap;
+
+File *files;
+
+void
+freedict(Dict *d)
+{
+	if(d){
+		if(d->val != d)
+			freedict(d->val);
+		freedict(d->next);
+		free(d);
+	}
+}
+
+char*
+bparse(char *s, char *e, Dict **dp)
+{
+	char *x, t;
+	Dict *d;
+	int n;
+
+	*dp = nil;
+	if(s >= e)
+		return e;
+
+	t = *s;
+	switch(t){
+	case 'd':
+	case 'l':
+		x = s++;
+		d = nil;
+		while(s < e){
+			if(*s == 'e'){
+				s++;
+				break;
+			}
+			if(t == 'd'){
+				s = bparse(s, e, dp);
+				if((d = *dp) == nil)
+					break;
+			} else
+				d = *dp = mallocz(sizeof(*d), 1);
+			d->typ = t;
+			d->start = x;
+			if(s < e){
+				s = bparse(s, e, &d->val);
+				dp = &d->next;
+				d->end = s;
+			}
+			x = s;
+		}
+		if(d)
+			d->end = s;
+		return s;
+	case 'i':
+		x = ++s;
+		if((s = memchr(x, 'e', e - x)) == nil)
+			return e;
+		n = s - x;
+		s++;
+		break;
+	default:
+		if((x = memchr(s, ':', e - s)) == nil)
+			return e;
+		x++;
+		if((n = atoi(s)) < 0)
+			return e;
+		s = x + n;
+		if((s > e) || (s < x)){
+			n = e - x;
+			s = e;
+		}
+		t = 's';
+	}
+	d = mallocz(sizeof(*d) + n+1, 1);
+	d->typ = t;
+	memmove(d->str, x, d->len = n);
+	d->str[n] = 0;
+	*dp = d;
+	return s;
+}
+
+char*
+dstr(Dict *d)
+{
+	if(d && (d->typ == 's' || d->typ == 'i'))
+		return d->str;
+	return nil;
+}
+
+Dict*
+dlook(Dict *d, char *s)
+{
+	for(; d && d->typ == 'd'; d = d->next)
+		if(d->len && strcmp(d->str, s) == 0)
+			return d->val;
+	return nil;
+}
+
+int
+readall(int fd, char **p)
+{
+	int n, r;
+
+	n = 0;
+	*p = nil;
+	while(*p = realloc(*p, n+1024)){
+		if((r = read(fd, *p+n, 1024)) <= 0)
+			break;
+		n += r;
+	}
+	return n;
+}
+
+int
+rwpiece(int wr, int index, uchar *data, int len, int poff)
+{
+	vlong off;
+	int n, m;
+	File *f;
+
+	if(len <= 0 || poff >= pieces[index].len)
+		return 0;
+	if(len+poff > pieces[index].len)
+		len = pieces[index].len - poff;
+	off = (vlong)index * blocksize;
+	off += poff;
+	for(f = files; f; f = f->next)
+		if((f->off+f->len) > off)
+			break;
+	off -= f->off;
+	n = ((off + len) > f->len) ? f->len - off : len;
+	if((n = (wr ? pwrite(f->fd, data, n, off) : pread(f->fd, data, n, off))) <= 0)
+		return -1;
+	if((m = rwpiece(wr, index, data + n, len - n, poff + n)) < 0)
+		return -1;
+	return n+m;
+}
+
+int
+havepiece(int x)
+{
+	uchar *p, m, hash[20];
+	int n;
+
+	m = 0x80>>(x&7);
+	if(havemap[x>>3] & m)
+		return 1;
+	p = malloc(blocksize);
+	n = pieces[x].len;
+	if(rwpiece(0, x, p, n, 0) != n){
+		free(p);
+		return 0;
+	}
+	sha1(p, n, hash, nil);
+	free(p);
+	if(memcmp(hash, pieces[x].hash, 20))
+		return 0;
+	havemap[x>>3] |= m;
+	return 1;
+}
+
+int
+pickpiece(uchar *map)
+{
+	int i, x, r, k;
+	uchar m;
+
+	r = -1;
+	k = 0;
+	for(i = 0; i<nhavemap; i++){
+		if(map[i] == 0)
+			continue;
+		for(x = i<<3, m = 0x80; m; m >>= 1, x++){
+			if((~map[i] | havemap[i]) & m)
+				continue;
+			if(nrand(++k) == 0)
+				r = x;
+		}
+	}
+	return r;
+}
+
+int
+unpack(uchar *s, int n, char *fmt, ...)
+{
+	va_list arg;
+	uchar *b, *e;
+
+	b = s;
+	e = b + n;
+	va_start(arg, fmt);
+	for(; *fmt; fmt++) {
+		switch(*fmt){
+		case '_':
+			s++;
+			break;
+		case 'b':
+			if(s+1 > e) goto Err;
+			*va_arg(arg, int*) = *s++;
+			break;
+		case 'l':
+			if(s+4 > e) goto Err;
+			*va_arg(arg, int*) = s[0]<<24 | s[1]<<16 | s[2]<<8 | s[3];
+			s += 4;
+			break;
+		}
+	}
+	va_end(arg);
+	return s - b;
+Err:
+	va_end(arg);
+	return -1;
+}
+
+int
+pack(uchar *s, int n, char *fmt, ...)
+{
+	va_list arg;
+	uchar *b, *e;
+	int i;
+
+	b = s;
+	e = b + n;
+	va_start(arg, fmt);
+	for(; *fmt; fmt++) {
+		switch(*fmt){
+		case '_':
+			i = 0;
+			if(0){
+		case 'b':
+			i = va_arg(arg, int);
+			}
+			if(s+1 > e) goto Err;
+			*s++ = i & 0xFF;
+			break;
+		case 'l':
+			i = va_arg(arg, int);
+			if(s+4 > e) goto Err;
+			*s++ = (i>>24) & 0xFF;
+			*s++ = (i>>16) & 0xFF;
+			*s++ = (i>>8) & 0xFF;
+			*s++ = i & 0xFF;
+			break;
+		case '*':
+			i = va_arg(arg, int);
+			if(s+i > e) goto Err;
+			memmove(s, va_arg(arg, uchar*), i);
+			s += i;
+			break;
+		}
+	}
+	va_end(arg);
+	return s - b;
+Err:
+	va_end(arg);
+	return -1;
+}
+
+void
+peer(char *ip, char *port)
+{
+	static Dict *peers;
+	static QLock peerslk;
+
+	uchar buf[64+MAXIO], *map, *told, *p, m;
+	char *addr;
+	int retry, i, o, l, x, n, fd;
+	int mechoking, hechoking;
+	int mewant, hewant;
+	int workpiece;
+	Dict *d;
+
+	if(ip == nil || port == nil)
+		return;
+
+	d = mallocz(sizeof(*d) + 64, 1);
+	snprint(addr = d->str, 64, "tcp!%s!%s", ip, port);
+	qlock(&peerslk);
+	if(dlook(peers, addr)){
+		qunlock(&peerslk);
+		free(d);
+		return;
+	}
+	d->len = strlen(addr);
+	d->typ = 'd';
+	d->val = d;
+	d->next = peers;
+	peers = d;
+	qunlock(&peerslk);
+
+	if(rfork(RFFDG|RFPROC|RFMEM) <= 0)
+		return;
+
+	fd = -1;
+	retry = 0;
+	map = malloc(nhavemap);
+	told = malloc(nhavemap);
+Retry:
+	if(fd >= 0){
+		close(fd);
+		sleep(10000 + nrand(5000));
+	}
+	if(++retry >= 10)
+		goto Exit;
+
+	if(debug) fprint(2, "dial %s\n", addr);
+	if((fd = dial(addr, nil, nil, nil)) < 0)
+		goto Retry;
+
+	if(debug) fprint(2, "peer %s: -> handshake\n", addr);
+	n = pack(buf, sizeof(buf), "*________**", 
+		20, "\x13BitTorrent protocol",
+		sizeof(infohash), infohash,
+		sizeof(peerid), peerid);
+	if(write(fd, buf, n) != n)
+		goto Retry;
+
+	if(read(fd, buf, 1) != 1)
+		goto Retry;
+	n = buf[0] + 8 + sizeof(infohash) + sizeof(peerid);
+	if((n = readn(fd, buf+1, n)) != n)
+		goto Retry;
+	if(debug) fprint(2, "peer %s: <- handshake %.*s\n", addr, buf[0], (char*)buf+1);
+	if(memcmp(infohash, buf + 1 + buf[0] + 8, sizeof(infohash)))
+		goto Exit;
+
+	if(debug) fprint(2, "peer %s: -> bitfield %d\n", addr, nhavemap);
+	memmove(told, havemap, nhavemap);
+	n = pack(buf, sizeof(buf), "lb*", nhavemap+1, 0x05, nhavemap, havemap);
+	if(write(fd, buf, n) != n)
+		goto Retry;
+
+	mechoking = 1;
+	hechoking = 1;
+	mewant = 0;
+	hewant = 0;
+	workpiece = -1;
+	memset(map, 0, nhavemap);
+	for(;;){
+		for(i=0; i<nhavemap; i++){
+			if(told[i] != havemap[i]){
+				for(x = i<<3, m = 0x80; m; m >>= 1, x++){
+					if((~havemap[i] | told[i] | map[i]) & m)
+						continue;
+					told[i] |= m;
+					if(debug) fprint(2, "peer %s: -> have %d\n", addr, x);
+					n = pack(buf, sizeof(buf), "lbl", 1+4, 0x04, x);
+					if(write(fd, buf, n) != n)
+						goto Retry;
+				}
+			}
+			if(!mewant && (map[i] & ~havemap[i])){
+				mewant = 1;
+				if(debug) fprint(2, "peer %s: -> interested\n", addr);
+				n = pack(buf, sizeof(buf), "lb", 1, 0x02);
+				if(write(fd, buf, n) != n)
+					goto Retry;
+			}
+		}
+		if(!hechoking && mewant){
+			x = workpiece;
+			if(x >= 0 && pieces[x].brk < pieces[x].len)
+				{}
+			else x = pickpiece(map);
+			if(x >= 0){
+				o = pieces[x].brk;
+				l = pieces[x].len - o;
+				if(l > MAXIO)
+					l = MAXIO;
+				if(debug) fprint(2, "peer %s: -> request %d %d %d\n", addr, x, o, l);
+				n = pack(buf, sizeof(buf), "lblll", 1+4+4+4, 0x06, x, o, l);
+				if(write(fd, buf, n) != n)
+					goto Retry;
+				workpiece = x;
+			}
+		}
+		if(mechoking && hewant){
+			mechoking = 0;
+			if(debug) fprint(2, "peer %s: -> unchoke\n", addr);
+			n = pack(buf, sizeof(buf), "lb", 1, 0x01);
+			if(write(fd, buf, n) != n)
+				goto Retry;
+		}
+
+		if(readn(fd, buf, 4) != 4)
+			goto Retry;
+		unpack(buf, 4, "l", &n);
+		if(n == 0)
+			continue;
+		if(n < 0 || n > sizeof(buf))
+			goto Retry;
+		if(readn(fd, buf, n) != n)
+			goto Retry;
+		retry = 0;
+		p = buf+1;
+		n--;
+		switch(*buf){
+		case 0x00:	// Choke
+			hechoking = 1;
+			workpiece = -1;
+			if(debug) fprint(2, "peer %s: <- choke\n", addr);
+			break;
+		case 0x01:	// Unchoke
+			hechoking = 0;
+			if(debug) fprint(2, "peer %s: <- unchoke\n", addr);
+			break;
+		case 0x02:	// Interested
+			hewant = 1;
+			if(debug) fprint(2, "peer %s: <- interested\n", addr);
+			break;
+		case 0x03:	// Notinterested
+			hewant = 0;
+			if(debug) fprint(2, "peer %s: <- notinterested\n", addr);
+			break;
+		case 0x04:	// Have <piceindex>
+			if(unpack(p, n, "l", &x) < 0)
+				goto Retry;
+			if(debug) fprint(2, "peer %s: <- have %d\n", addr, x);
+			if(x < 0 || x >= npieces)
+				continue;
+			map[x>>3] |= 0x80>>(x&7);
+			break;
+		case 0x05:	// Bitfield
+			if(debug) fprint(2, "peer %s: <- bitfield %d\n", addr, n);
+			if(n != nhavemap)
+				continue;
+			memmove(map, p, n);
+			break;
+		case 0x06:	// Request <index> <begin> <length>
+			if(unpack(p, n, "lll", &x, &o, &l) < 0)
+				goto Retry;
+			if(debug) fprint(2, "peer %s: <- request %d %d %d\n", addr, x, o, l);
+			if(x < 0 || x >= npieces)
+				continue;
+			if(!hewant || mechoking || (~havemap[x>>3]&(0x80>>(x&7))))
+				continue;
+			if(debug) fprint(2, "peer %s: -> piece %d %d\n", addr, x, o);
+			n = 4+1+4+4;
+			if(l > MAXIO)
+				l = MAXIO;
+			if((l = rwpiece(0, x, buf + n, l, o)) <= 0)
+				continue;
+			n = pack(buf, sizeof(buf), "lbll", 1+4+4+l, 0x07, x, o);
+			n += l;
+			if(write(fd, buf, n) != n)
+				goto Retry;
+			break;
+		case 0x07:	// Piece <index> <begin> <block>
+			if(unpack(p, n, "ll", &x, &o) != 8)
+				goto Retry;
+			p += 8;
+			n -= 8;
+			if(debug) fprint(2, "peer %s: <- piece %d %d %d\n", addr, x, o, n);
+			if(x < 0 || x >= npieces)
+				continue;
+			if((pieces[x].brk != o) || (havemap[x>>3]&(0x80>>(x&7))))
+				continue;
+			if(rwpiece(1, x, p, n, o) == n){
+				if((pieces[x].brk = o+n) == pieces[x].len){
+					if(!havepiece(x))
+						pieces[x].brk = 0;
+				}
+			}
+			break;
+		case 0x08:	// Cancel <index> <begin> <length>
+			if(unpack(p, n, "lll", &x, &o, &l) < 0)
+				goto Retry;
+			if(debug) fprint(2, "peer %s: <- cancel %d %d %d\n", addr, x, o, l);
+			break;
+		case 0x09:	// Port <port>
+			if(unpack(p, n, "l", &x) < 0)
+				goto Retry;
+			if(debug) fprint(2, "peer %s: <- port %d\n", addr, x);
+			break;
+		}
+	}
+Exit:
+	free(told);
+	free(map);
+	exits(0);
+}
+
+int
+hopen(char *url, ...)
+{
+	int conn, ctlfd, fd, n;
+	char buf[1024+1];
+	va_list arg;
+
+	snprint(buf, sizeof buf, "%s/clone", mntweb);
+	if((ctlfd = open(buf, ORDWR)) < 0)
+		return -1;
+	if((n = read(ctlfd, buf, sizeof buf-1)) <= 0){
+		close(ctlfd);
+		return -1;
+	}
+	buf[n] = 0;
+	conn = atoi(buf);
+	va_start(arg, url);
+	strcpy(buf, "url ");
+	n = 4+vsnprint(buf+4, sizeof(buf)-4, url, arg);
+	va_end(arg);
+	if(write(ctlfd, buf, n) != n){
+	ErrOut:
+		close(ctlfd);
+		return -1;
+	}
+	snprint(buf, sizeof buf, "%s/%d/body", mntweb, conn);
+	if((fd = open(buf, OREAD)) < 0)
+		goto ErrOut;
+	close(ctlfd);
+	return fd;
+}
+
+void
+tracker(char *url)
+{
+	static Dict *trackers;
+	static QLock trackerslk;
+
+	int n, fd;
+	char *p;
+	Dict *d, *l;
+
+	if(url == nil)
+		return;
+
+	qlock(&trackerslk);
+	if(dlook(trackers, url)){
+		qunlock(&trackerslk);
+		return;
+	}
+	n = strlen(url);
+	d = mallocz(sizeof(*d) + n+1, 1);
+	strcpy(d->str, url);
+	d->len = n;
+	d->typ = 'd';
+	d->val = d;
+	d->next = trackers;
+	trackers = d;
+	url = d->str;
+	qunlock(&trackerslk);
+
+	if(rfork(RFFDG|RFPROC|RFMEM) <= 0)
+		return;
+
+	for(;;){
+		d = nil;
+		if((fd = hopen("%s?info_hash=%.*H&peer_id=%.*H&port=%d&compact=1",
+			url, sizeof(infohash), infohash, sizeof(peerid), peerid, port)) >= 0){
+			n = readall(fd, &p);
+			close(fd);
+			bparse(p, p+n, &d);
+			free(p);
+		}
+		if(l = dlook(d, "peers")){
+			if(l->typ == 's'){
+				uchar *b, *e;
+
+				b = (uchar*)l->str;
+				e = b + l->len;
+				for(; b+6 <= e; b += 6){
+					char ip[16], port[6];
+
+					snprint(ip, sizeof(ip), "%d.%d.%d.%d", b[0], b[1], b[2], b[3]);
+					snprint(port, sizeof(port), "%d", b[4]<<8 | b[5]);
+					peer(ip, port);
+				}
+			} else for(; l && l->typ == 'l'; l = l->next)
+				peer(dstr(dlook(l->val, "ip")), dstr(dlook(l->val, "port")));
+		}
+		n = 0;
+		if(p = dstr(dlook(d, "interval")))
+			n = atoi(p);
+		if(n < 10 | n > 60*60)
+			n = 2*60;
+		freedict(d);
+		sleep(n * 1000 + nrand(5000));
+	}
+}
+
+int
+Hfmt(Fmt *f)
+{
+	uchar *s, *e;
+	s = va_arg(f->args, uchar*);
+	if(f->flags & FmtPrec)
+		e = s + f->prec;
+	else
+		e = s + strlen((char*)s);
+	for(; s < e; s++)
+		if(fmtprint(f, ((*s >= '0' && *s <= '9') || 
+			(*s >= 'a' && *s <= 'z') ||
+			(*s >= 'A' && *s <= 'Z') || 
+			strchr(".-_~", *s)) ? "%c" : "%%%.2x", *s) < 0)
+			return -1;
+	return 0;
+}
+
+int
+progress(void)
+{
+	int i, c;
+	uchar m;
+	c = 0;
+	for(i=0; i<nhavemap; i++)
+		for(m = 0x80; m; m>>=1)
+			if(havemap[i] & m)
+				c++;
+	if(pflag)
+		print("%d %d\n", c, npieces);
+	return c == npieces;
+}
+
+void
+killcohort(void)
+{
+	int i;
+	for(i=0;i!=3;i++){	/* It's a long way to the kitchen */
+		postnote(PNGROUP, pidgroup, "kill");
+		sleep(1);
+	}
+}
+
+int
+catchnote(void *, char *msg)
+{
+	exits(msg);
+	return 0;
+}
+
+void
+usage(void)
+{
+	fprint(2, "usage: %s [ -vsdp ] [ -m mtpt ] [ torrentfile ]\n", argv0);
+	exits("usage");
+}
+
+void
+main(int argc, char *argv[])
+{
+	Dict *info, *torrent, *d;
+	File **fp, *f;
+	char *p, *s, *e;
+	int fd, i, n;
+	vlong len;
+
+	fmtinstall('H', Hfmt);
+
+	ARGBEGIN {
+	case 'm':
+		mntweb = EARGF(usage());
+		break;
+	case 's':
+		sflag = 1;
+		break;
+	case 'p':
+		pflag = 1;
+		break;
+	case 'v':
+		vflag = 1;
+		break;
+	case 'd':
+		debug++;
+		break;
+	default:
+		usage();
+	} ARGEND;
+
+	fd = 0;
+	if(*argv)
+		if((fd = open(*argv, OREAD)) < 0)
+			sysfatal("open torrent: %r");
+	if((n = readall(fd, &p)) <= 0)
+		sysfatal("read torrent: %r");
+	bparse(p, p+n, &torrent);
+	if((d = info = dlook(torrent, "info")) == nil)
+		sysfatal("no meta info in torrent");
+	for(s = e = d->start; d && d->typ == 'd'; d = d->next)
+		e = d->end;
+	sha1((uchar*)s, e - s, (uchar*)infohash, nil);
+	free(p);
+
+	fp = &files;
+	if(d = dlook(info, "files")){		
+		for(; d && d->typ == 'l'; d = d->next){
+			Dict *di;
+
+			if((s = dstr(dlook(d->val, "length"))) == nil)
+				continue;
+			f = mallocz(sizeof(*f), 1);
+			f->len = atoll(s);
+			f->name = dstr(dlook(info, "name"));
+			for(di = dlook(d->val, "path"); di && di->typ == 'l'; di = di->next)
+				if(s = dstr(di->val))
+					f->name = f->name ? smprint("%s/%s", f->name, s) : s;
+			*fp = f;
+			fp = &f->next;
+		}
+	} else if(s = dstr(dlook(info, "length"))){
+		f = mallocz(sizeof(*f), 1);
+		f->len = atoll(s);
+		f->name = dstr(dlook(info, "name"));
+		*fp = f;
+	}
+	len = 0;
+	for(f = files; f; f = f->next){
+		if(f->name == nil || f->len <= 0)
+			sysfatal("bogus file entry in meta info");
+		if(vflag) fprint(pflag ? 2 : 1, "%s\n", f->name);
+		if((f->fd = open(f->name, ORDWR)) < 0)
+			if((f->fd = create(f->name, ORDWR, 0666)) < 0)
+				sysfatal("create: %r");
+		f->off = len;
+		len += f->len;
+	}
+	if(len <= 0)
+		sysfatal("no files in torrent");
+
+	if((s = dstr(dlook(info, "piece length"))) == nil)
+		sysfatal("missing piece length in meta info");
+	if((blocksize = atoi(s)) <= 0)
+		sysfatal("bogus piece length in meta info");
+	d = dlook(info, "pieces");
+	if(d == nil || d->typ != 's' || d->len <= 0 || d->len % 20)
+		sysfatal("bad or no pices in meta info");
+	npieces = d->len / 20;
+	pieces = mallocz(sizeof(Piece) * npieces, 1);
+	nhavemap = (npieces+7) / 8;
+	havemap = mallocz(nhavemap, 1);
+	for(i = 0; i<npieces; i++){
+		pieces[i].hash = (uchar*)d->str + i*20;
+		if(len < blocksize)
+			pieces[i].len = len;
+		else
+			pieces[i].len = blocksize;
+		len -= pieces[i].len;
+	}
+	if(len)
+		sysfatal("pieces do not match file length");
+
+	for(i = 0; i<npieces; i++)
+		havepiece(i);
+
+	switch(i = rfork(RFPROC|RFMEM|RFNOTEG|RFNAMEG)){
+	case -1:
+		sysfatal("fork: %r");
+	case 0:
+		memmove(peerid, "-NF9001-", 8);
+		genrandom(peerid+8, sizeof(peerid)-8);
+		tracker(dstr(dlook(torrent, "announce")));
+		for(d = dlook(torrent, "announce-list"); d && d->typ == 'l'; d = d->next)
+			if(d->val && d->val->typ == 'l')
+				tracker(dstr(d->val->val));
+		break;
+	default:
+		pidgroup = i;
+		atexit(killcohort);
+		atnotify(catchnote, 1);
+		while(!progress() || sflag)
+			sleep(1000);
+	}
+	exits(0);
+}