changelog shortlog tags branches files raw gz bz2 help

Mercurial > hg > ventivac / changeset: merge non-rabin trunk into rabin trunk

changeset 63: 9519bb640170
parent 62: 29e265a144f7
parent 56: 2e60a35e2b3c
child 65: 9ab0c984840c
author: Mechiel Lukkien <mechiel@ueber.net>
date: Fri, 13 Jul 2007 16:48:29 +0200
files: appl/cmd/vacput.b
description: merge non-rabin trunk into rabin trunk
     1.1--- a/appl/cmd/vacget.b	Fri Jul 13 16:34:26 2007 +0200
     1.2+++ b/appl/cmd/vacget.b	Fri Jul 13 16:48:29 2007 +0200
     1.3@@ -42,7 +42,7 @@
     1.4 	vac->init();
     1.5 
     1.6 	arg->init(args);
     1.7-	arg->setusage(sprint("%s [-dtv] [-a addr] [tag:]score", arg->progname()));
     1.8+	arg->setusage(sprint("%s [-dptv] [-a addr] [tag:]score", arg->progname()));
     1.9 	while((c := arg->opt()) != 0)
    1.10 		case c {
    1.11 		'a' =>	addr = arg->earg();
     2.1--- a/appl/cmd/vacput.b	Fri Jul 13 16:34:26 2007 +0200
     2.2+++ b/appl/cmd/vacput.b	Fri Jul 13 16:48:29 2007 +0200
     2.3@@ -62,7 +62,7 @@
     2.4 	blockmax = 32*1024;
     2.5 
     2.6 	arg->init(args);
     2.7-	arg->setusage(sprint("%s [-drtv] [-a addr] [-b blocksize] [-n name] path ...", arg->progname()));
     2.8+	arg->setusage(sprint("%s [-drv] [-a addr] [-b blocksize] [-n name] path ...", arg->progname()));
     2.9 	while((c := arg->opt()) != 0)
    2.10 		case c {
    2.11 		'a' =>	addr = arg->earg();
     3.1--- a/appl/cmd/vcache.b	Fri Jul 13 16:34:26 2007 +0200
     3.2+++ b/appl/cmd/vcache.b	Fri Jul 13 16:48:29 2007 +0200
     3.3@@ -33,7 +33,11 @@
     3.4 	init:	fn(nil: ref Draw->Context, args: list of string);
     3.5 };
     3.6 
     3.7+Eperm:	con "permission denied";
     3.8+
     3.9 laddr := "net!*!venti";
    3.10+statsdir := "/chan/";
    3.11+statsfile := "vcachestats";
    3.12 dflag := nflag := vflag := wflag := 0;
    3.13 
    3.14 maxcachesize := 0;
    3.15@@ -79,13 +83,18 @@
    3.16 	venti->init();
    3.17 
    3.18 	arg->init(args);
    3.19-	arg->setusage(arg->progname()+" [-dnvw] [-a laddr] [-s size] remoteaddr [proxyaddr]");
    3.20+	arg->setusage(arg->progname()+" [-dnvw] [-a laddr] [-s size] [-S statsfile] remoteaddr [proxyaddr]");
    3.21 	while((c := arg->opt()) != 0)
    3.22 		case c {
    3.23 		'a' =>	laddr = arg->earg();
    3.24 		'd' =>	dflag++;
    3.25 		'n' =>	nflag++;
    3.26 		's' =>	maxcachesize = int arg->earg();
    3.27+		'S' =>	(statsdir, statsfile) = str->splitstrr(arg->earg(), "/");
    3.28+			if(statsfile == nil) {
    3.29+				fprint(fildes(2), "bad statsfile\n");
    3.30+				arg->usage();
    3.31+			}
    3.32 		'v' =>	vflag++;
    3.33 		'w' =>	wflag = 1;
    3.34 		* =>	fprint(fildes(2), "bad option: -%c\n", c);
    3.35@@ -404,6 +413,7 @@
    3.36 		rscore := Score(sha1(msg.data));
    3.37 		return rscore.eq(*op.s);
    3.38 	Rwrite =>
    3.39+verbose(sprint("opokay, msg.score=%s op.s nil==%d", msg.score.text(), op.s==nil));
    3.40 		return msg.score.eq(*op.s);
    3.41 	};
    3.42 	return 1;
    3.43@@ -434,9 +444,12 @@
    3.44 	bogusreqc := chan of (ref Vmsg, int);
    3.45 	reqc := requestc;
    3.46 
    3.47-	tickch := chan of int;
    3.48-	if(dflag)
    3.49-		spawn tick(tickch);
    3.50+	fio := sys->file2chan(statsdir, statsfile);
    3.51+	if(fio == nil) {
    3.52+		fprint(fildes(2), "file2chan: %r;  not serving statistics\n");
    3.53+		fio = ref sys->FileIO(chan of (int, int, int, sys->Rread), chan of (int, array of byte, int, sys->Rwrite));
    3.54+	} else
    3.55+		if(dflag) debug(sprint("file2chan: serving %s%s", statsdir, statsfile));
    3.56 
    3.57 	debug("central: beginning loop");
    3.58 	initheap := heapused();
    3.59@@ -453,6 +466,33 @@
    3.60 		if(dflag) debug(sprint("central: ALT rclaim=%d pclaim=%d rfree=%d pfree=%d",
    3.61 			remote.claimed, proxy.claimed, remote.tidsfree(), proxy.tidsfree()));
    3.62 		alt {
    3.63+		(offset, nil, nil, rc) := <- fio.read =>
    3.64+			if(rc == nil)
    3.65+				continue;
    3.66+
    3.67+			buf := array of byte sprint(
    3.68+				"%14d clients\n%14d proxy connection\n%14d proxy transitops\n%14d remote connection\n%14d remote transitops\n"+
    3.69+				"%14d maxcachesize\n%14d cacheheads\n%14d cacheused\n"+
    3.70+				"%14d cachemiss\n%14d cachehit\n%14d cacherequest\n"+
    3.71+				"%14d proxymiss\n%14d proxyhit\n%14d proxyrequest\n"+
    3.72+				"%14d remotereads\n%14d remotewrites\n"+
    3.73+				"%14d heapused\n",
    3.74+				len clients, proxy.fd != nil, 256-proxy.tidsfree(), remote.fd != nil, 256-remote.tidsfree(),
    3.75+				maxcachesize, len cacheheads, cacheused,
    3.76+				cachemiss, cachereq-cachemiss, cachereq,
    3.77+				proxymiss, proxyreq-proxymiss, proxyreq,
    3.78+				remotereads, remotewrites, heapused()-initheap);
    3.79+
    3.80+			if(offset > len buf)
    3.81+				offset = len buf;
    3.82+			rc <-= (buf[offset:], nil);
    3.83+
    3.84+		(nil, nil, nil, wc) := <- fio.write =>
    3.85+			if(wc == nil)
    3.86+				continue;
    3.87+			if(dflag) debug("main: file2chan write");
    3.88+			wc <-= (0, Eperm);
    3.89+
    3.90 		(rpid, wpid, respc) := <- registerc =>
    3.91 			verbose("central: new client");
    3.92 			clientput(rpid, wpid, respc);
    3.93@@ -642,7 +682,7 @@
    3.94 				cacheput(*op.s, op.dtype, r.data);
    3.95 				isread = 1;
    3.96 				if(proxyaddr != nil && needconn(proxy, nil) >= 0) {
    3.97-					ptid := proxy.opput(ref Op(0, nil, 0, nil, Nothing, nil, 0, 0));
    3.98+					ptid := proxy.opput(ref Op(0, nil, 0, nil, Nothing, op.s, 0, 0));
    3.99 					proxy.writec <-= ref Vmsg.Twrite(1, ptid, op.dtype, r.data);
   3.100 					if(dflag) debug("central: wrote data from remote to proxy");
   3.101 				}
   3.102@@ -661,12 +701,6 @@
   3.103 		<- remote.errorc =>
   3.104 			verbose("central: remote writer error");
   3.105 			remote.close(proxy);
   3.106-
   3.107-		<- tickch =>
   3.108-			debug(sprint("cachemiss=%d cachehit=%d cacherequest=%d", cachemiss, cachereq-cachemiss, cachereq));
   3.109-			debug(sprint("proxymiss=%d proxyhit=%d proxyrequest=%d", proxymiss, proxyreq-proxymiss, proxyreq));
   3.110-			debug(sprint("remotereads=%d remotewrites=%d", remotereads, remotewrites));
   3.111-			debug(sprint("heapused=%d", heapused()-initheap));
   3.112 		}
   3.113 	}
   3.114 }
     4.1--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2+++ b/appl/cmd/venti/get.b	Fri Jul 13 16:48:29 2007 +0200
     4.3@@ -0,0 +1,124 @@
     4.4+implement Ventiget;
     4.5+
     4.6+include "sys.m";
     4.7+	sys: Sys;
     4.8+include "draw.m";
     4.9+include "bufio.m";
    4.10+	bufio: Bufio;
    4.11+	Iobuf: import bufio;
    4.12+include "arg.m";
    4.13+include "string.m";
    4.14+include "venti.m";
    4.15+include "vac.m";
    4.16+
    4.17+str: String;
    4.18+venti: Venti;
    4.19+vac: Vac;
    4.20+
    4.21+print, sprint, fprint, fildes: import sys;
    4.22+Score, Session: import venti;
    4.23+Dirtype, Datatype: import venti;
    4.24+Entry, Entrysize, Vacfile: import vac;
    4.25+
    4.26+Ventiget: module {
    4.27+	init:	fn(nil: ref Draw->Context, args: list of string);
    4.28+};
    4.29+
    4.30+addr := "net!$venti!venti";
    4.31+dflag := 0;
    4.32+session: ref Session;
    4.33+
    4.34+init(nil: ref Draw->Context, args: list of string)
    4.35+{
    4.36+	sys = load Sys Sys->PATH;
    4.37+	bufio = load Bufio Bufio->PATH;
    4.38+	arg := load Arg Arg->PATH;
    4.39+	str = load String String->PATH;
    4.40+	venti = load Venti Venti->PATH;
    4.41+	vac = load Vac Vac->PATH;
    4.42+	if(venti == nil || vac == nil)
    4.43+		error("loading venti,vac");
    4.44+	venti->init();
    4.45+	vac->init();
    4.46+
    4.47+	arg->init(args);
    4.48+	arg->setusage(sprint("%s [-d] [-a addr] [entry:]score", arg->progname()));
    4.49+	while((c := arg->opt()) != 0)
    4.50+		case c {
    4.51+		'a' =>	addr = arg->earg();
    4.52+		'd' =>	dflag++;
    4.53+			vac->dflag++;
    4.54+		* =>	fprint(fildes(2), "bad option: -%c\n", c);
    4.55+			arg->usage();
    4.56+		}
    4.57+	args = arg->argv();
    4.58+	if(len args != 1)
    4.59+		arg->usage();
    4.60+
    4.61+	(tag, scorestr) := str->splitstrr(hd args, ":");
    4.62+	if(tag != nil)
    4.63+		tag = tag[:len tag-1];
    4.64+	if(tag == nil)
    4.65+		tag = "entry";
    4.66+	if(tag != "entry")
    4.67+		error("bad score type: "+tag);
    4.68+
    4.69+	(sok, score) := Score.parse(scorestr);
    4.70+	if(sok != 0)
    4.71+		error("bad score: "+scorestr);
    4.72+	say("have score");
    4.73+
    4.74+	(cok, conn) := sys->dial(addr, nil);
    4.75+	if(cok < 0)
    4.76+		error(sprint("dialing %s: %r", addr));
    4.77+	say("have connection");
    4.78+
    4.79+	fd := conn.dfd;
    4.80+	session = Session.new(fd);
    4.81+	if(session == nil)
    4.82+		error(sprint("handshake: %r"));
    4.83+	say("have handshake");
    4.84+
    4.85+	d := session.read(score, Dirtype, Entrysize);
    4.86+	if(d == nil)
    4.87+		error(sprint("reading entry: %r"));
    4.88+	e := Entry.unpack(d);
    4.89+	if(e == nil)
    4.90+		error(sprint("unpacking entry: %r"));
    4.91+	say("have entry");
    4.92+
    4.93+	bio := bufio->fopen(fildes(1), bufio->OWRITE);
    4.94+	if(bio == nil)
    4.95+		error(sprint("bufio fopen: %r"));
    4.96+
    4.97+	say("reading");
    4.98+	buf := array[sys->ATOMICIO] of byte;
    4.99+	vf := Vacfile.new(session, e);
   4.100+	for(;;) {
   4.101+		rn := vf.read(buf, len buf);
   4.102+		if(rn == 0)
   4.103+			break;
   4.104+		if(rn < 0)
   4.105+			error(sprint("reading: %r"));
   4.106+		wn := bio.write(buf, rn);
   4.107+		if(wn != rn)
   4.108+			error(sprint("writing: %r"));
   4.109+	}
   4.110+	bok := bio.flush();
   4.111+	bio.close();
   4.112+	if(bok == bufio->ERROR || bok == bufio->EOF)
   4.113+		error(sprint("bufio close: %r"));
   4.114+	say("done");
   4.115+}
   4.116+
   4.117+error(s: string)
   4.118+{
   4.119+	fprint(fildes(2), "%s\n", s);
   4.120+	raise "fail:"+s;
   4.121+}
   4.122+
   4.123+say(s: string)
   4.124+{
   4.125+	if(dflag)
   4.126+		fprint(fildes(2), "%s\n", s);
   4.127+}
     5.1--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2+++ b/appl/cmd/venti/mkfile	Fri Jul 13 16:48:29 2007 +0200
     5.3@@ -0,0 +1,25 @@
     5.4+<../../../mkconfig
     5.5+
     5.6+TARG=\
     5.7+	sync.dis\
     5.8+	ping.dis\
     5.9+	get.dis\
    5.10+	put.dis\
    5.11+
    5.12+SYSMODULES=\
    5.13+	arg.m\
    5.14+	bufio.m\
    5.15+	daytime.m\
    5.16+	draw.m\
    5.17+	keyring.m\
    5.18+	string.m\
    5.19+	styx.m\
    5.20+	styxservers.m\
    5.21+	sys.m\
    5.22+	vac.m\
    5.23+	venti.m\
    5.24+
    5.25+DISBIN=$ROOT/dis/venti
    5.26+
    5.27+<$ROOT/mkfiles/mkdis
    5.28+<$ROOT/mkfiles/mksubdirs
     6.1--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2+++ b/appl/cmd/venti/ping.b	Fri Jul 13 16:48:29 2007 +0200
     6.3@@ -0,0 +1,81 @@
     6.4+implement Ventiping;
     6.5+
     6.6+include "sys.m";
     6.7+include "draw.m";
     6.8+include "arg.m";
     6.9+include "venti.m";
    6.10+
    6.11+sys: Sys;
    6.12+venti: Venti;
    6.13+
    6.14+print, sprint, fprint, fildes: import sys;
    6.15+Score, Session, Vmsg: import venti;
    6.16+
    6.17+Ventiping: module {
    6.18+	init:	fn(nil: ref Draw->Context, args: list of string);
    6.19+};
    6.20+
    6.21+addr := "net!$venti!venti";
    6.22+dflag := 0;
    6.23+n := 3;
    6.24+
    6.25+init(nil: ref Draw->Context, args: list of string)
    6.26+{
    6.27+	sys = load Sys Sys->PATH;
    6.28+	arg := load Arg Arg->PATH;
    6.29+	venti = load Venti Venti->PATH;
    6.30+	venti->init();
    6.31+
    6.32+	arg->init(args);
    6.33+	arg->setusage(arg->progname() + " [-d] [-n count] [-a addr]");
    6.34+	while((c := arg->opt()) != 0)
    6.35+		case c {
    6.36+		'a' =>	addr = arg->earg();
    6.37+		'd' =>	dflag++;
    6.38+		'n' =>	n = int arg->earg();
    6.39+		* =>	fprint(fildes(2), "bad option: -%c", c);
    6.40+			arg->usage();
    6.41+		}
    6.42+	args = arg->argv();
    6.43+	if(len args != 0)
    6.44+		arg->usage();
    6.45+
    6.46+	say("dialing");
    6.47+	(cok, conn) := sys->dial(addr, nil);
    6.48+	if(cok < 0)
    6.49+		fail(sprint("dialing %s: %r", addr));
    6.50+	fd := conn.dfd;
    6.51+	say("have connection");
    6.52+
    6.53+	session := Session.new(fd);
    6.54+	if(session == nil)
    6.55+		fail(sprint("handshake: %r"));
    6.56+	say("have handshake");
    6.57+
    6.58+	tm := ref Vmsg.Tping(1, 0);
    6.59+	i := 0;
    6.60+	for(;;) {
    6.61+		t0 := sys->millisec();
    6.62+		(rm, err) := session.rpc(tm);
    6.63+		if(rm == nil)
    6.64+			fail("ping: "+err);
    6.65+		t1 := sys->millisec();
    6.66+		print("%d ms\n", t1-t0);
    6.67+		i++;
    6.68+		if(i == n)
    6.69+			break;
    6.70+		sys->sleep(1*1000);
    6.71+	}
    6.72+}
    6.73+
    6.74+fail(s: string)
    6.75+{
    6.76+	fprint(fildes(2), "%s\n", s);
    6.77+	raise "fail:"+s;
    6.78+}
    6.79+
    6.80+say(s: string)
    6.81+{
    6.82+	if(dflag)
    6.83+		fprint(fildes(2), "%s\n", s);
    6.84+}
     7.1--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.2+++ b/appl/cmd/venti/put.b	Fri Jul 13 16:48:29 2007 +0200
     7.3@@ -0,0 +1,118 @@
     7.4+implement Ventiput;
     7.5+
     7.6+include "sys.m";
     7.7+	sys: Sys;
     7.8+include "draw.m";
     7.9+include "bufio.m";
    7.10+	bufio: Bufio;
    7.11+	Iobuf: import bufio;
    7.12+include "arg.m";
    7.13+include "venti.m";
    7.14+include "vac.m";
    7.15+
    7.16+venti: Venti;
    7.17+vac: Vac;
    7.18+
    7.19+print, sprint, fprint, fildes: import sys;
    7.20+Score, Session: import venti;
    7.21+Dirtype, Datatype: import venti;
    7.22+Entry, File: import vac;
    7.23+
    7.24+Ventiput: module {
    7.25+	init:	fn(nil: ref Draw->Context, args: list of string);
    7.26+};
    7.27+
    7.28+addr := "net!$venti!venti";
    7.29+dflag := 0;
    7.30+blocksize := vac->Dsize;
    7.31+session: ref Session;
    7.32+
    7.33+init(nil: ref Draw->Context, args: list of string)
    7.34+{
    7.35+	sys = load Sys Sys->PATH;
    7.36+	bufio = load Bufio Bufio->PATH;
    7.37+	arg := load Arg Arg->PATH;
    7.38+	venti = load Venti Venti->PATH;
    7.39+	vac = load Vac Vac->PATH;
    7.40+	if(venti == nil || vac == nil)
    7.41+		error("loading venti,vac");
    7.42+	venti->init();
    7.43+	vac->init();
    7.44+
    7.45+	arg->init(args);
    7.46+	arg->setusage(sprint("%s [-d] [-a addr] [-b blocksize]", arg->progname()));
    7.47+	while((c := arg->opt()) != 0)
    7.48+		case c {
    7.49+		'a' =>	addr = arg->earg();
    7.50+		'b' =>	blocksize = int arg->earg();
    7.51+		'd' =>	dflag++;
    7.52+			vac->dflag++;
    7.53+		* =>	fprint(fildes(2), "bad option: -%c", c);
    7.54+			arg->usage();
    7.55+		}
    7.56+	args = arg->argv();
    7.57+	if(len args != 0)
    7.58+		arg->usage();
    7.59+
    7.60+	(cok, conn) := sys->dial(addr, nil);
    7.61+	if(cok < 0)
    7.62+		error(sprint("dialing %s: %r", addr));
    7.63+	say("have connection");
    7.64+
    7.65+	session = Session.new(conn.dfd);
    7.66+	if(session == nil)
    7.67+		error(sprint("handshake: %r"));
    7.68+	say("have handshake");
    7.69+
    7.70+	bio := bufio->fopen(fildes(0), bufio->OREAD);
    7.71+	if(bio == nil)
    7.72+		error(sprint("bufio open: %r"));
    7.73+
    7.74+	say("writing");
    7.75+	f := File.new(session, Datatype, blocksize, 0);
    7.76+	for(;;) {
    7.77+		buf := array[blocksize] of byte;
    7.78+		n := 0;
    7.79+		while(n < len buf) {
    7.80+			want := len buf - n;
    7.81+			have := bio.read(buf[n:], want);
    7.82+			if(have == 0)
    7.83+				break;
    7.84+			if(have < 0)
    7.85+				error(sprint("reading: %r"));
    7.86+			n += have;
    7.87+		}
    7.88+		if(dflag) say(sprint("have buf, length %d", n));
    7.89+
    7.90+		if(f.write(buf[:n]) < 0)
    7.91+			error(sprint("writing: %r"));
    7.92+		if(n != len buf)
    7.93+			break;
    7.94+	}
    7.95+	bio.close();
    7.96+	e := f.finish();
    7.97+	if(e == nil)
    7.98+		error(sprint("flushing: %r"));
    7.99+	d := e.pack();
   7.100+
   7.101+	(rok, rscore) := session.write(Dirtype, d);
   7.102+	if(rok < 0)
   7.103+		error(sprint("writing root score: %r"));
   7.104+	say("entry written, "+rscore.text());
   7.105+	print("entry:%s\n", rscore.text());
   7.106+
   7.107+	if(session.sync() < 0)
   7.108+		error(sprint("syncing server: %r"));
   7.109+}
   7.110+
   7.111+error(s: string)
   7.112+{
   7.113+	fprint(fildes(2), "%s\n", s);
   7.114+	raise "fail:"+s;
   7.115+}
   7.116+
   7.117+say(s: string)
   7.118+{
   7.119+	if(dflag)
   7.120+		fprint(fildes(2), "%s\n", s);
   7.121+}
     8.1--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     8.2+++ b/appl/cmd/venti/sync.b	Fri Jul 13 16:48:29 2007 +0200
     8.3@@ -0,0 +1,68 @@
     8.4+implement Ventisync;
     8.5+
     8.6+include "sys.m";
     8.7+include "draw.m";
     8.8+include "arg.m";
     8.9+include "venti.m";
    8.10+
    8.11+sys: Sys;
    8.12+venti: Venti;
    8.13+
    8.14+print, sprint, fprint, fildes: import sys;
    8.15+Score, Session: import venti;
    8.16+
    8.17+Ventisync: module {
    8.18+	init:	fn(nil: ref Draw->Context, args: list of string);
    8.19+};
    8.20+
    8.21+addr := "net!$venti!venti";
    8.22+dflag := 0;
    8.23+
    8.24+init(nil: ref Draw->Context, args: list of string)
    8.25+{
    8.26+	sys = load Sys Sys->PATH;
    8.27+	arg := load Arg Arg->PATH;
    8.28+	venti = load Venti Venti->PATH;
    8.29+	venti->init();
    8.30+
    8.31+	arg->init(args);
    8.32+	arg->setusage(arg->progname() + " [-d] [-a addr]");
    8.33+	while((c := arg->opt()) != 0)
    8.34+		case c {
    8.35+		'a' =>	addr = arg->earg();
    8.36+		'd' =>	dflag++;
    8.37+		* =>	fprint(fildes(2), "bad option: -%c", c);
    8.38+			arg->usage();
    8.39+		}
    8.40+	args = arg->argv();
    8.41+	if(len args != 0)
    8.42+		arg->usage();
    8.43+
    8.44+	say("dialing");
    8.45+	(cok, conn) := sys->dial(addr, nil);
    8.46+	if(cok < 0)
    8.47+		fail(sprint("dialing %s: %r", addr));
    8.48+	fd := conn.dfd;
    8.49+	say("have connection");
    8.50+
    8.51+	session := Session.new(fd);
    8.52+	if(session == nil)
    8.53+		fail(sprint("handshake: %r"));
    8.54+	say("have handshake");
    8.55+
    8.56+	if(session.sync() < 0)
    8.57+		fail(sprint("sync: %r"));
    8.58+	say("synced");
    8.59+}
    8.60+
    8.61+fail(s: string)
    8.62+{
    8.63+	fprint(fildes(2), "%s\n", s);
    8.64+	raise "fail:"+s;
    8.65+}
    8.66+
    8.67+say(s: string)
    8.68+{
    8.69+	if(dflag)
    8.70+		fprint(fildes(2), "%s\n", s);
    8.71+}
     9.1--- a/appl/cmd/ventisrv.b	Fri Jul 13 16:34:26 2007 +0200
     9.2+++ b/appl/cmd/ventisrv.b	Fri Jul 13 16:48:29 2007 +0200
     9.3@@ -4,13 +4,12 @@
     9.4 # - queue index writes?
     9.5 # - starting up is slow.  reading index file is fast.  packing/unpacking is necessary.  inserting non-sorted at startup, and then sorting is faster, but involves unpacking the "blocks" in memory, sorting, and packing again.  premature optimisation.
     9.6 # - sync when client disconnects?  sync periodically?
     9.7-# - write only large buffers, e.g. 128k to disk?
     9.8 # - only compress data and dir blocks?  not pointer blocks (they have random scores).  should make sure the index file and data file remain in sync.
     9.9 
    9.10 # does not have:
    9.11 # - using raw disks for storage
    9.12 # - tools for analysing problems and fixing bad index/data file state
    9.13-# - authentication, or disallowing writes
    9.14+# - authentication
    9.15 
    9.16 implement Ventisrv;
    9.17 
    9.18@@ -153,9 +152,11 @@
    9.19 	finish:	fn(f: self ref Flate): (array of byte, string);
    9.20 };
    9.21 
    9.22+Eperm:	con "permission denied";
    9.23 
    9.24 Indexscoresize:	con 8;
    9.25-Ihdrsize:	con Indexscoresize+1+6;
    9.26+Icomprmask:	con big 1<<(6*8-1);
    9.27+Ihdrsize:	con Indexscoresize+1+6;	# scoresize+typesize+addrsize
    9.28 Iverify:	con 128;	# check the half scores of the last n entries in the index file against the data file
    9.29 Ichunksize:	con ((128*1024)/Ihdrsize)*Ihdrsize;	# chunk in bytes of index entries to read at a time
    9.30 Nqueuechunks:	con 32;		# queue n Ichunksize blocks when reading index at startup
    9.31@@ -182,6 +183,8 @@
    9.32 listenaddr: con "net!*!venti";
    9.33 indexfile := "index";
    9.34 datafile := "data";
    9.35+statsdir := "/chan/";
    9.36+statsfile := "ventisrvstats";
    9.37 debug, Cflag, cflag, qflag, verbose: int;
    9.38 raddrs, waddrs: list of string;
    9.39 
    9.40@@ -200,6 +203,7 @@
    9.41 storec: chan of ref Store;
    9.42 writererrorc: chan of string;
    9.43 sem: ref Semaphore;
    9.44+statsem: ref Semaphore;
    9.45 
    9.46 writequeue: ref Queue;
    9.47 flatequeue: ref Fifo;
    9.48@@ -207,9 +211,7 @@
    9.49 initheap, configheap: big;
    9.50 nreads, nwrites, nwritesdup, nsyncs, npings: int;
    9.51 nblocks, nflateblocks: int;
    9.52-lookupclashes: int;
    9.53-
    9.54-flatesequence := 1;
    9.55+lookupcollisions: int;
    9.56 
    9.57 
    9.58 init(nil: ref Draw->Context, args: list of string)
    9.59@@ -229,7 +231,7 @@
    9.60 	venti->init();
    9.61 
    9.62 	arg->init(args);
    9.63-	arg->setusage(arg->progname()+ " [-Dcqv] [-i index] [-d data] [-r addr] [-w addr] maxdatasize meanblocksize");
    9.64+	arg->setusage(arg->progname()+ " [-DcCqv] [-i index] [-d data] [-s statsfile] [-r addr] [-w addr] maxdatasize meanblocksize");
    9.65 	while((c := arg->opt()) != 0)
    9.66 		case c {
    9.67 		'D' =>	debug++;
    9.68@@ -241,6 +243,11 @@
    9.69 		'q' =>	qflag++;
    9.70 			verbose++;
    9.71 		'v' =>	verbose++;
    9.72+		's' =>	(statsdir, statsfile) = str->splitstrr(arg->earg(), "/");
    9.73+			if(statsfile == nil) {
    9.74+				fprint(fildes(2), "bad stats file");
    9.75+				arg->usage();
    9.76+			}
    9.77 		'r' =>	raddrs = arg->earg()::raddrs;
    9.78 		'w' =>	waddrs = arg->earg()::waddrs;
    9.79 		* =>
    9.80@@ -290,6 +297,7 @@
    9.81 	storec = chan[32] of ref Store;
    9.82 	writererrorc = chan of string;
    9.83 	sem = Semaphore.new();
    9.84+	statsem = Semaphore.new();
    9.85 
    9.86 	writequeue = ref Queue(array[2] of ref Block, 0);
    9.87 	flatequeue = ref Fifo(array[64] of ref Block, 0, 0, Semaphore.new());
    9.88@@ -389,6 +397,14 @@
    9.89 		io = big 0;
    9.90 	if(indexsize > big 0) {
    9.91 		if(debug) say(sprint("config: verifying last entries in index file at offset=%bd", io));
    9.92+		ih := getihdr(io);
    9.93+		while(ih.compressed) {
    9.94+			io += big Ihdrsize;
    9.95+			nih := getihdr(io);
    9.96+			if(nih.offset != ih.offset)
    9.97+				break;
    9.98+			ih = nih;
    9.99+		}
   9.100 		while(io < indexsize)
   9.101 			(doffset, io) = indexverify(io);
   9.102 	}
   9.103@@ -398,7 +414,7 @@
   9.104 	while(doffset < datasize) {
   9.105 		(dhdrs, compr, noffset, err) := offsetread(doffset);
   9.106 		if(err != nil)
   9.107-			fail("syncing index from data: "+err);
   9.108+			fail(sprint("syncing index from data at offset=%bd: %s", doffset, err));
   9.109 		for(i := 0; i < len dhdrs; i++) {
   9.110 			dhdr := dhdrs[i];
   9.111 			err = indexstore(ref Ihdr(dhdr.score.a[:Indexscoresize], dhdr.dtype, doffset, compr));
   9.112@@ -445,8 +461,8 @@
   9.113 {
   9.114 	if(halfcmp(ih.halfscore, dh.score.a, ih.dtype, dh.dtype) == 0)
   9.115 		return;
   9.116-	fail(sprint("partial score or type index file does not match block in data file at offset=%bd, index: score=%s type=%d, data: score=%s type=%d",
   9.117-		ih.offset, scorestr(ih.halfscore), ih.dtype, dh.score.text(), dh.dtype));
   9.118+	fail(sprint("partial score or type index file does not match block in data file at offset=%bd, index: score=%s type=%d compressed=%d, data: score=%s type=%d",
   9.119+		ih.offset, scorestr(ih.halfscore), ih.dtype, ih.compressed, dh.score.text(), dh.dtype));
   9.120 }
   9.121 
   9.122 getihdr(o: big): ref Ihdr
   9.123@@ -661,8 +677,10 @@
   9.124 if(debug) say("reader: have hit, already in datafile");
   9.125 				sem.obtain();
   9.126 				writequeue.remove(w.b);
   9.127+				sem.release();
   9.128+				statsem.obtain();
   9.129 				nwritesdup++;
   9.130-				sem.release();
   9.131+				statsem.release();
   9.132 			} else {
   9.133 if(debug) say("reader: no hit, need to write block");
   9.134 				if(debug) say("reader: data not yet present for write");
   9.135@@ -875,9 +893,13 @@
   9.136 {
   9.137 	writeerror: string;
   9.138 
   9.139-	tickch := chan of int;
   9.140-	if(debug)
   9.141-		spawn ticker(tickch);
   9.142+	fio := sys->file2chan(statsdir, statsfile);
   9.143+	if(fio == nil) {
   9.144+		say(sprint("file2chan: %r;  not serving statistics"));
   9.145+		fio = ref sys->FileIO(chan of (int, int, int, sys->Rread), chan of (int, array of byte, int, sys->Rwrite));
   9.146+	} else
   9.147+		if(debug) say(sprint("file2chan: serving %s%s", statsdir, statsfile));
   9.148+
   9.149 	spawn writer();
   9.150 
   9.151 	configheap = heapused();
   9.152@@ -885,6 +907,41 @@
   9.153 		say(sprint("heap used after startup=%bd", configheap-initheap));
   9.154 
   9.155 	for(;;) alt {
   9.156+	(offset, nil, nil, rc) := <- fio.read =>
   9.157+		if(rc == nil)
   9.158+			continue;
   9.159+
   9.160+		statsem.obtain();
   9.161+		lnblocks := nblocks;
   9.162+		lnflateblocks := nflateblocks;
   9.163+		ldatasize := datasize;
   9.164+		lnwritesdup := nwritesdup;
   9.165+		statsem.release();
   9.166+
   9.167+		h := heapused();
   9.168+		mean := 0;
   9.169+		if(lnblocks > 0)
   9.170+			mean = int (ldatasize/big lnblocks);
   9.171+		buf := array of byte sprint(
   9.172+			"%14d reads\n%14d writes\n%14d syncs\n%14d pings\n"+
   9.173+			"%14d dupwrites\n%14d lookupcollisions\n%14d blocks\n%14d flateblocks\n%14d meanblocksize\n"+
   9.174+			"%14d readerprocs\n%14d busyreaderprocs\n%14d readerwrites\n"+
   9.175+			"%14bd totalheap\n%14bd newheap\n%14bd datasize\n%s\n",
   9.176+			nreads, nwrites, nsyncs, npings,
   9.177+			lnwritesdup, lookupcollisions, lnblocks, lnflateblocks, mean,
   9.178+			nreaders, nbusyreaders, nreaderwrites,
   9.179+			h, h-configheap, ldatasize, writeerror);
   9.180+
   9.181+		if(offset > len buf)
   9.182+			offset = len buf;
   9.183+		rc <-= (buf[offset:], nil);
   9.184+
   9.185+	(nil, nil, nil, wc) := <- fio.write =>
   9.186+		if(wc == nil)
   9.187+			continue;
   9.188+		if(debug) say("main: file2chan write");
   9.189+		wc <-= (0, Eperm);
   9.190+
   9.191 	(iswrite, rmsg, c) := <-lookupdonec =>
   9.192 		lookupdone(iswrite, rmsg, c);
   9.193 
   9.194@@ -1000,16 +1057,6 @@
   9.195 		* =>
   9.196 			if(debug) say(sprint("main: bad tmsg, tag=%d", tagof vmsg));
   9.197 		}
   9.198-
   9.199-	<-tickch =>
   9.200-		h := heapused();
   9.201-		say(sprint("total heap used=%bd, new heap since startup=%bd", h, h-configheap));
   9.202-		say(sprint("nreads=%d nwrites=%d nwritesdup=%d nsyncs=%d npings=%d",
   9.203-			nreads, nwrites, nwritesdup, nsyncs, npings));
   9.204-		mean := 0;
   9.205-		if(nblocks > 0)
   9.206-			mean = int (datasize/big nblocks);
   9.207-		say(sprint("nblocks=%d nflateblocks=%d meanblocksize=%d lookupclashes=%d", nblocks, nflateblocks, mean, lookupclashes));
   9.208 	}
   9.209 }
   9.210 
   9.211@@ -1139,10 +1186,13 @@
   9.212 	n = sys->pwrite(datafd, d, len d, datasize+big len fhbuf);
   9.213 	if(n != len d)
   9.214 		return (datasize, sprint("writing flateblock data: %r"));
   9.215+
   9.216 	offset := datasize;
   9.217+	statsem.obtain();
   9.218 	datasize += big (len fhbuf+len d);
   9.219 	nblocks += len blocks;
   9.220 	nflateblocks += len blocks;
   9.221+	statsem.release();
   9.222 if(debug) say(sprint("datastoreflate: stored at offset=%bd datasize=%bd len blocks=%d comprsize=%d", offset, datasize, len blocks, len d));
   9.223 	return (offset, nil);
   9.224 }
   9.225@@ -1166,8 +1216,10 @@
   9.226 		return (datasize, sprint("writing data: %r"));
   9.227 
   9.228 	offset := datasize;
   9.229+	statsem.obtain();
   9.230 	datasize += big (len dhbuf+len d);
   9.231 	nblocks++;
   9.232+	statsem.release();
   9.233 if(debug) say(sprint("datastore: stored at offset=%bd datasize=%bd blocksize=%d", offset, datasize, dh.size));
   9.234 	return (offset, nil);
   9.235 }
   9.236@@ -1412,7 +1464,10 @@
   9.237 		v |= big c.d[o++] << (b-8);
   9.238 	if(b > 0)
   9.239 		v |= big c.d[o] >> (8-b);
   9.240-	return (int ((v&comprmask)>>(addrbits-1)), v&~comprmask);
   9.241+	iscompr := Cflag && int ((v&comprmask)>>(addrbits-1));
   9.242+	if(iscompr)
   9.243+		v &= ~comprmask;
   9.244+	return (iscompr, v);
   9.245 }
   9.246 
   9.247 Chain.lookup(c: self ref Chain, d: array of byte, l: list of (int, big)): list of (int, big)
   9.248@@ -1571,7 +1626,7 @@
   9.249 	for(c := heads[head(score.a)]; c != nil; c = c.next)
   9.250 		addrs = c.lookup(d, addrs);
   9.251 	if(len addrs > 1)
   9.252-		lookupclashes += len addrs-1;
   9.253+		lookupcollisions += len addrs-1;
   9.254 	return addrs;
   9.255 }
   9.256 
   9.257@@ -1642,9 +1697,9 @@
   9.258 	o += 1;
   9.259 	offset := get48(d, o);
   9.260 	compressed := 0;
   9.261-	if(int ((offset&comprmask)>>(addrbits-1)))
   9.262+	if((offset&Icomprmask) == Icomprmask)
   9.263 		compressed = 1;
   9.264-	offset &= ~comprmask;
   9.265+	offset &= ~Icomprmask;
   9.266 	o += 6;
   9.267 	if(o != Ihdrsize)
   9.268 		fail("bad iheader.unpack");
   9.269@@ -1660,7 +1715,7 @@
   9.270 	o += 1;
   9.271 	offset := ih.offset;
   9.272 	if(ih.compressed)
   9.273-		offset |= comprmask;
   9.274+		offset |= Icomprmask;
   9.275 	put48(d, o, offset);
   9.276 	o += 6;
   9.277 	if(o != Ihdrsize)
   9.278@@ -1671,7 +1726,7 @@
   9.279 {
   9.280 	o := 0;
   9.281 	if(get32(d, o) != Dhdrmagic)
   9.282-		return (nil, "bad magic");
   9.283+		return (nil, "bad dhdr magic");
   9.284 	o += 4;
   9.285 	score := d[o:o+Scoresize];
   9.286 	o += Scoresize;
   9.287@@ -1708,7 +1763,7 @@
   9.288 {
   9.289 	o := 0;
   9.290 	if(get32(d, o) != Fhdrmagic)
   9.291-		return (nil, nil, "bad magic");
   9.292+		return (nil, nil, "bad fhdr magic");
   9.293 	o += 4;
   9.294 	nb := int d[o++];
   9.295 	hsize := nb*Fbhdrsize;
    10.1--- a/man/1/vacget	Fri Jul 13 16:34:26 2007 +0200
    10.2+++ b/man/1/vacget	Fri Jul 13 16:48:29 2007 +0200
    10.3@@ -67,6 +67,9 @@
    10.4 .br
    10.5 .B /appl/cmd/vacput.b
    10.6 .SH SEE ALSO
    10.7+.IR venti (1),
    10.8+.IR vacget (1),
    10.9+.IR vacput (1),
   10.10 .IR vcache (1),
   10.11 .IR venti (2),
   10.12 .IR vacfs (4),
    11.1--- a/man/1/vcache	Fri Jul 13 16:34:26 2007 +0200
    11.2+++ b/man/1/vcache	Fri Jul 13 16:48:29 2007 +0200
    11.3@@ -11,17 +11,20 @@
    11.4 ] [
    11.5 .B -s
    11.6 .I size
    11.7+] [
    11.8+.B -S
    11.9+.I statsfile
   11.10 ]
   11.11 .I remoteaddr
   11.12 [
   11.13 .I proxyaddr
   11.14 ]
   11.15 .SH DESCRIPTION
   11.16-Vcache is a cache for venti blocks.  It acts as a venti server and serves requests using its in-memory cache, optionally a proxy server, and finally an authorative venti server.  Only read requests are cached, and only if they returned data.  Cache blocks are replaced using a clock replacement algorithm.  If not handled by the cache, read requests go to the proxy server, falling back to the authorative server if the data is absent at the proxy or no proxy server is used.  Read data returned by the authorative server are written to the proxy server.  If
   11.17+Vcache is a cache for venti blocks.  It acts as a venti server and serves requests using its in-memory cache, optionally a proxy server, and finally an authoritative venti server.  Only read requests are cached, and only if they returned data.  Cache blocks are replaced using a clock replacement algorithm.  If not handled by the cache, read requests go to the proxy server, falling back to the authoritative server if the data is absent at the proxy or no proxy server is used.  Read data returned by the authoritative server are written to the proxy server.  If
   11.18 .B -w
   11.19-has been specified, the proxy server is used as write through cache:  all write and sync requests from clients go to both the proxy and the authorative server, keeping them in sync.  Requests only succeed if both servers return success, this allows reads of previously written data to succeed, even when the authorative server is down.
   11.20+has been specified, the proxy server is used as write through cache:  all write and sync requests from clients go to both the proxy and the authoritative server, keeping them in sync.  Requests only succeed if both servers return success, this allows reads of previously written data to succeed, even when the authoritative server is down.
   11.21 .PP
   11.22-Typically, the proxy server is running locally and the authorative server is remote, possibly on a high latency link.  At startup, no connections to the venti server are initiated.  All clients share a single connection to the proxy server and authorative server.  The servers are dialed when needed only, no connections are made before the first client connects.  Once connected, the proxy connection is always kept.  The connection to the authorative server is closed when the last client disconnects.  When connections to the proxy or authorative server fail, all clients with pending requests are disconnected.
   11.23+Typically, the proxy server is running locally and the authoritative server is remote, possibly on a high latency link.  At startup, no connections to the venti server are initiated.  All clients share a single connection to the proxy server and authoritative server.  The servers are dialed when needed only, no connections are made before the first client connects.  Once connected, the proxy connection is always kept.  The connection to the authoritative server is closed when the last client disconnects.  When connections to the proxy or authoritative server fail, all clients with pending requests are disconnected.
   11.24 .TP
   11.25 .BI -a " address"
   11.26 Listen on
   11.27@@ -31,8 +34,13 @@
   11.28 .BI -s " size"
   11.29 Size of the in-memory cache in bytes.  The overhead of the score and data structures is included in the size.  Default is 0 bytes.
   11.30 .TP
   11.31+.BI -S " statsfile"
   11.32+File to serve statistics on.  Default is
   11.33+.IR /chan/vcachestats .
   11.34+Each line starts with a space-padded 14 byte number followed by a space and a textual description.
   11.35+.TP
   11.36 .B -n
   11.37-Do not check the results of the proxy and authorative server.  By default, vcache verifies the returned data and scores.
   11.38+Do not check the results of the proxy and authoritative server.  By default, vcache verifies the returned data and scores.
   11.39 .TP
   11.40 .B -w
   11.41 Use the proxy server as write-through cache.
   11.42@@ -45,6 +53,7 @@
   11.43 .SH SOURCE
   11.44 .B /appl/cmd/vcache.b
   11.45 .SH SEE ALSO
   11.46+.IR venti (1),
   11.47 .IR vacget (1),
   11.48 .IR vacput (1),
   11.49 .IR venti (2),
   11.50@@ -53,4 +62,4 @@
   11.51 .SH BUGS
   11.52 The in-memory cache needs a better replacement policy, with more efficient implementation.
   11.53 .br
   11.54-When a connection to a proxy or authorative server breaks, all clients with pending requests are closed down, open requests are not responded to.  Sending venti error responses for all open requests might be nicer.
   11.55+When a connection to a proxy or authoritative server breaks, all clients with pending requests are closed down, open requests are not responded to.  Sending venti error responses for all open requests might be nicer.
    12.1--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    12.2+++ b/man/1/venti	Fri Jul 13 16:48:29 2007 +0200
    12.3@@ -0,0 +1,93 @@
    12.4+.TH VENTI 1
    12.5+.SH NAME
    12.6+venti/sync, venti/ping, venti/get, venti/put \- venti utilities
    12.7+.SH SYNOPSIS
    12.8+.B venti/sync
    12.9+[
   12.10+.B -d
   12.11+] [
   12.12+.B -a
   12.13+.I addr
   12.14+]
   12.15+.br
   12.16+.B venti/ping
   12.17+[
   12.18+.B -d
   12.19+] [
   12.20+.B -a
   12.21+.I addr
   12.22+] [
   12.23+.B -n
   12.24+.I count
   12.25+]
   12.26+.br
   12.27+.B venti/put
   12.28+[
   12.29+.B -d
   12.30+] [
   12.31+.B -a
   12.32+.I addr
   12.33+] [
   12.34+.B -b
   12.35+.I blocksize
   12.36+]
   12.37+.br
   12.38+.B venti/get
   12.39+[
   12.40+.B -d
   12.41+] [
   12.42+.B -a
   12.43+.I addr
   12.44+]
   12.45+.I entry:score
   12.46+.SH DESCRIPTION
   12.47+.I Venti/sync 
   12.48+sends a sync messages to a venti server.  When it returns, the venti server has flushed its data to stable storage.
   12.49+.PP
   12.50+.I Venti/ping
   12.51+pings a venti server.  Option
   12.52+.B -n
   12.53+sets the number of ping messages to send.  The round trip time for each message is printed.
   12.54+.PP
   12.55+.I Venti/put
   12.56+reads data from standard input and writes it to venti.  When done, the score of the resulting venti entry is printed.
   12.57+.I Venti/get
   12.58+reads the data from the venti entry referenced by
   12.59+.I score
   12.60+and writes it to standard output.
   12.61+.PP
   12.62+The options the programs understand are:
   12.63+.TP
   12.64+.B -d
   12.65+Print debug messages.
   12.66+.TP
   12.67+.BI -a " address"
   12.68+Dial
   12.69+.I address
   12.70+instead of the default venti server.
   12.71+.TP
   12.72+.BI -b " blocksize"
   12.73+Use blocks with
   12.74+.I blocksize
   12.75+bytes instead of the default 8192 byte blocks.  Only for
   12.76+.IR venti/put .
   12.77+.TP
   12.78+.BI -n " count"
   12.79+Send
   12.80+.I count
   12.81+pings to the venti server.  The default is three.  When set to zero,
   12.82+.I venti/ping
   12.83+pings until interrupted.
   12.84+.SH SOURCE
   12.85+.B /appl/cmd/venti/sync.b
   12.86+.br
   12.87+.B /appl/cmd/venti/ping.b
   12.88+.br
   12.89+.B /appl/cmd/venti/get.b
   12.90+.br
   12.91+.B /appl/cmd/venti/put.b
   12.92+.SH SEE ALSO
   12.93+.IR vcache (1),
   12.94+.IR venti (2),
   12.95+.IR vacfs (4),
   12.96+.IR ventisrv (8)
    13.1--- a/man/4/vacfs	Fri Jul 13 16:34:26 2007 +0200
    13.2+++ b/man/4/vacfs	Fri Jul 13 16:48:29 2007 +0200
    13.3@@ -36,6 +36,7 @@
    13.4 .SH SOURCE
    13.5 .B /appl/cmd/vacfs.b
    13.6 .SH SEE ALSO
    13.7+.IR venti (1),
    13.8 .IR vacget (1),
    13.9 .IR vacput (1),
   13.10 .IR vcache (1),
    14.1--- a/man/8/ventisrv	Fri Jul 13 16:34:26 2007 +0200
    14.2+++ b/man/8/ventisrv	Fri Jul 13 16:48:29 2007 +0200
    14.3@@ -12,6 +12,9 @@
    14.4 .B -d
    14.5 .I data
    14.6 ] [
    14.7+.B -s
    14.8+.I statsfile
    14.9+] [
   14.10 .B -r
   14.11 .I addr
   14.12 ] [
   14.13@@ -63,8 +66,13 @@
   14.14 File to use as data file.  Default is
   14.15 .IR data .
   14.16 .TP
   14.17+.BI -s " statsfile"
   14.18+File to serve statistics on.  Default is
   14.19+.IR /chan/ventisrvstats .
   14.20+Each line starts with a space-padded 14 byte number followed by a space and a textual description.  The last line is empty unless a write error has occurred, in which it contains the error message.
   14.21+.TP
   14.22 .B -D
   14.23-Print (lots of) debug messages.  Prints multiple lines per venti transaction and periodically prints statistics about memory and block usage.
   14.24+Print (lots of) debug messages.  Prints multiple lines per venti transaction.
   14.25 .TP
   14.26 .B -v
   14.27 Be more verbose, especially at startup and when errors occur.
   14.28@@ -86,6 +94,7 @@
   14.29 .SH SOURCE
   14.30 .B /appl/cmd/ventisrv.b
   14.31 .SH SEE ALSO
   14.32+.IR venti (1),
   14.33 .IR vacget (1),
   14.34 .IR vacput (1),
   14.35 .IR vcache (1),