xref: /inferno-os/appl/cmd/logfile.b (revision a3d4017d8d3be485b256b15d2344afac09112204)
137da2899SCharles.Forsythimplement Logfile;
237da2899SCharles.Forsyth
337da2899SCharles.Forsyth#
437da2899SCharles.Forsyth# Copyright © 1999 Vita Nuova Limited.  All rights reserved.
537da2899SCharles.Forsyth#
637da2899SCharles.Forsyth
737da2899SCharles.Forsythinclude "sys.m";
837da2899SCharles.Forsyth	sys: Sys;
937da2899SCharles.Forsyth	stderr: ref Sys->FD;
1037da2899SCharles.Forsythinclude "draw.m";
1137da2899SCharles.Forsyth
1237da2899SCharles.ForsythLogfile: module {
1337da2899SCharles.Forsyth	init: fn(nil: ref Draw->Context, argv: list of string);
1437da2899SCharles.Forsyth};
1537da2899SCharles.Forsyth
1637da2899SCharles.ForsythFidrec: adt {
1737da2899SCharles.Forsyth	fid: 	int;		# fid of read
1837da2899SCharles.Forsyth	rq: 	list of (int, Sys->Rread);	# outstanding read requests
1937da2899SCharles.Forsyth	pos:	int;		# current position in the logfile
2037da2899SCharles.Forsyth};
2137da2899SCharles.Forsyth
2237da2899SCharles.ForsythCircbuf: adt {
2337da2899SCharles.Forsyth	start: int;
2437da2899SCharles.Forsyth	data: array of byte;
2537da2899SCharles.Forsyth	new: fn(size: int): ref Circbuf;
2637da2899SCharles.Forsyth	put: fn(b: self ref Circbuf, d: array of byte): int;
2737da2899SCharles.Forsyth	get: fn(b: self ref Circbuf, s, n: int): (int, array of byte);
2837da2899SCharles.Forsyth};
2937da2899SCharles.Forsyth
3037da2899SCharles.ForsythFidhash: adt
3137da2899SCharles.Forsyth{
3237da2899SCharles.Forsyth	table: array of list of ref Fidrec;
3337da2899SCharles.Forsyth	get: fn(ht: self ref Fidhash, fid: int): ref Fidrec;
3437da2899SCharles.Forsyth	put: fn(ht: self ref Fidhash, fidrec: ref Fidrec);
3537da2899SCharles.Forsyth	del: fn(ht: self ref Fidhash, fidrec: ref Fidrec);
3637da2899SCharles.Forsyth	new: fn(): ref Fidhash;
3737da2899SCharles.Forsyth};
3837da2899SCharles.Forsyth
3937da2899SCharles.Forsythusage()
4037da2899SCharles.Forsyth{
4137da2899SCharles.Forsyth	sys->fprint(stderr, "usage: logfile [-size] file\n");
4237da2899SCharles.Forsyth	raise "fail: usage";
4337da2899SCharles.Forsyth}
4437da2899SCharles.Forsyth
4537da2899SCharles.Forsythinit(nil: ref Draw->Context, argv: list of string)
4637da2899SCharles.Forsyth{
4737da2899SCharles.Forsyth	sys = load Sys Sys->PATH;
4837da2899SCharles.Forsyth	stderr = sys->fildes(2);
4937da2899SCharles.Forsyth
5037da2899SCharles.Forsyth	bufsize := Sys->ATOMICIO * 4;
5137da2899SCharles.Forsyth
5237da2899SCharles.Forsyth	if (argv != nil)
5337da2899SCharles.Forsyth		argv = tl argv;
5437da2899SCharles.Forsyth	if (argv != nil && len hd argv && (hd argv)[0] == '-' && len hd argv > 1) {
5537da2899SCharles.Forsyth		if ((bufsize = int ((hd argv)[1:])) <= 0) {
5637da2899SCharles.Forsyth			sys->fprint(stderr, "logfile: can't have a zero buffer size\n");
5737da2899SCharles.Forsyth			usage();
5837da2899SCharles.Forsyth		}
5937da2899SCharles.Forsyth		argv = tl argv;
6037da2899SCharles.Forsyth	}
6137da2899SCharles.Forsyth	if (argv == nil || tl argv != nil)
6237da2899SCharles.Forsyth		usage();
6337da2899SCharles.Forsyth	path := hd argv;
6437da2899SCharles.Forsyth
6537da2899SCharles.Forsyth	(dir, f) := pathsplit(path);
66*a3d4017dSCharles.Forsyth	if (sys->bind("#s", dir, Sys->MBEFORE) == -1) {
6737da2899SCharles.Forsyth		sys->fprint(stderr, "logfile: bind #s failed: %r\n");
6837da2899SCharles.Forsyth		return;
6937da2899SCharles.Forsyth	}
7037da2899SCharles.Forsyth	fio := sys->file2chan(dir, f);
7137da2899SCharles.Forsyth	if (fio == nil) {
7237da2899SCharles.Forsyth		sys->fprint(stderr, "logfile: couldn't make %s: %r\n", path);
7337da2899SCharles.Forsyth		return;
7437da2899SCharles.Forsyth	}
7537da2899SCharles.Forsyth
7637da2899SCharles.Forsyth	spawn logserver(fio, bufsize);
7737da2899SCharles.Forsyth}
7837da2899SCharles.Forsyth
7937da2899SCharles.Forsythlogserver(fio: ref Sys->FileIO, bufsize: int)
8037da2899SCharles.Forsyth{
8137da2899SCharles.Forsyth	waitlist: list of ref Fidrec;
8237da2899SCharles.Forsyth	readers := Fidhash.new();
8337da2899SCharles.Forsyth	availcount := 0;
8437da2899SCharles.Forsyth	availchan := chan of int;
8537da2899SCharles.Forsyth	workchan := chan of (Sys->Rread, array of byte);
8637da2899SCharles.Forsyth	buf := Circbuf.new(bufsize);
8737da2899SCharles.Forsyth	for (;;) alt {
8837da2899SCharles.Forsyth	<-availchan =>
8937da2899SCharles.Forsyth		availcount++;
903f1f06c5SCharles.Forsyth	(nil, count, fid, rc) := <-fio.read =>
9137da2899SCharles.Forsyth		r := readers.get(fid);
9237da2899SCharles.Forsyth		if (rc == nil) {
9337da2899SCharles.Forsyth			if (r != nil)
9437da2899SCharles.Forsyth				readers.del(r);
9537da2899SCharles.Forsyth			continue;
9637da2899SCharles.Forsyth		}
9737da2899SCharles.Forsyth		if (r == nil) {
9837da2899SCharles.Forsyth			r = ref Fidrec(fid, nil, buf.start);
9937da2899SCharles.Forsyth			if (r.pos < len buf.data)
10037da2899SCharles.Forsyth				r.pos = len buf.data;		# first buffer's worth is garbage
10137da2899SCharles.Forsyth			readers.put(r);
10237da2899SCharles.Forsyth		}
10337da2899SCharles.Forsyth
10437da2899SCharles.Forsyth		(s, d) := buf.get(r.pos, count);
10537da2899SCharles.Forsyth		r.pos = s + len d;
10637da2899SCharles.Forsyth
10737da2899SCharles.Forsyth		if (d != nil) {
10837da2899SCharles.Forsyth			rc <-= (d, nil);
10937da2899SCharles.Forsyth		} else {
11037da2899SCharles.Forsyth			if (r.rq == nil)
11137da2899SCharles.Forsyth				waitlist = r :: waitlist;
11237da2899SCharles.Forsyth			r.rq = (count, rc) :: r.rq;
11337da2899SCharles.Forsyth		}
11437da2899SCharles.Forsyth
1153f1f06c5SCharles.Forsyth	(nil, data, nil, wc) := <-fio.write =>
11637da2899SCharles.Forsyth		if (wc == nil)
11737da2899SCharles.Forsyth			continue;
11837da2899SCharles.Forsyth		if ((n := buf.put(data)) < len data)
11937da2899SCharles.Forsyth			wc <-= (n, "write too long for buffer");
12037da2899SCharles.Forsyth		else
12137da2899SCharles.Forsyth			wc <-= (n, nil);
12237da2899SCharles.Forsyth
12337da2899SCharles.Forsyth		wl := waitlist;
12437da2899SCharles.Forsyth		for (waitlist = nil; wl != nil; wl = tl wl) {
12537da2899SCharles.Forsyth			r := hd wl;
12637da2899SCharles.Forsyth			if (availcount == 0) {
12737da2899SCharles.Forsyth				spawn worker(workchan, availchan);
12837da2899SCharles.Forsyth				availcount++;
12937da2899SCharles.Forsyth			}
13037da2899SCharles.Forsyth			(count, rc) := hd r.rq;
13137da2899SCharles.Forsyth			r.rq = tl r.rq;
13237da2899SCharles.Forsyth
13337da2899SCharles.Forsyth			# optimisation: if the read request wants exactly the data provided
13437da2899SCharles.Forsyth			# in the write request, then use the original data buffer.
13537da2899SCharles.Forsyth			s: int;
13637da2899SCharles.Forsyth			d: array of byte;
13737da2899SCharles.Forsyth			if (count >= n && r.pos == buf.start + len buf.data - n)
13837da2899SCharles.Forsyth				(s, d) = (r.pos, data);
13937da2899SCharles.Forsyth			else
14037da2899SCharles.Forsyth				(s, d) = buf.get(r.pos, count);
14137da2899SCharles.Forsyth			r.pos = s + len d;
14237da2899SCharles.Forsyth			workchan <-= (rc, d);
14337da2899SCharles.Forsyth			availcount--;
14437da2899SCharles.Forsyth			if (r.rq != nil)
14537da2899SCharles.Forsyth				waitlist = r :: waitlist;
14637da2899SCharles.Forsyth			d = nil;
14737da2899SCharles.Forsyth		}
14837da2899SCharles.Forsyth		data = nil;
14937da2899SCharles.Forsyth		wl = nil;
15037da2899SCharles.Forsyth	}
15137da2899SCharles.Forsyth}
15237da2899SCharles.Forsyth
15337da2899SCharles.Forsythworker(work: chan of (Sys->Rread, array of byte), ready: chan of int)
15437da2899SCharles.Forsyth{
15537da2899SCharles.Forsyth	for (;;) {
15637da2899SCharles.Forsyth		(rc, data) := <-work;	# blocks forever if the reading process is killed
15737da2899SCharles.Forsyth		rc <-= (data, nil);
15837da2899SCharles.Forsyth		(rc, data) = (nil, nil);
15937da2899SCharles.Forsyth		ready <-= 1;
16037da2899SCharles.Forsyth	}
16137da2899SCharles.Forsyth}
16237da2899SCharles.Forsyth
16337da2899SCharles.ForsythCircbuf.new(size: int): ref Circbuf
16437da2899SCharles.Forsyth{
16537da2899SCharles.Forsyth	return ref Circbuf(0, array[size] of byte);
16637da2899SCharles.Forsyth}
16737da2899SCharles.Forsyth
16837da2899SCharles.Forsyth# return number of bytes actually written
16937da2899SCharles.ForsythCircbuf.put(b: self ref Circbuf, d: array of byte): int
17037da2899SCharles.Forsyth{
17137da2899SCharles.Forsyth	blen := len b.data;
17237da2899SCharles.Forsyth	# if too big to fit in buffer, truncate the write.
17337da2899SCharles.Forsyth	if (len d > blen)
17437da2899SCharles.Forsyth		d = d[0:blen];
17537da2899SCharles.Forsyth	dlen := len d;
17637da2899SCharles.Forsyth
17737da2899SCharles.Forsyth	offset := b.start % blen;
17837da2899SCharles.Forsyth	if (offset + dlen <= blen) {
17937da2899SCharles.Forsyth		b.data[offset:] = d;
18037da2899SCharles.Forsyth	} else {
18137da2899SCharles.Forsyth		b.data[offset:] = d[0:blen - offset];
18237da2899SCharles.Forsyth		b.data[0:] = d[blen - offset:];
18337da2899SCharles.Forsyth	}
18437da2899SCharles.Forsyth	b.start += dlen;
18537da2899SCharles.Forsyth	return dlen;
18637da2899SCharles.Forsyth}
18737da2899SCharles.Forsyth
18837da2899SCharles.Forsyth# return (start, data)
18937da2899SCharles.ForsythCircbuf.get(b: self ref Circbuf, s, n: int): (int, array of byte)
19037da2899SCharles.Forsyth{
19137da2899SCharles.Forsyth	# if the beginning's been overrun, start from the earliest place we can.
19237da2899SCharles.Forsyth	# we could put some indication of elided bytes in the buffer.
19337da2899SCharles.Forsyth	if (s < b.start)
19437da2899SCharles.Forsyth		s = b.start;
19537da2899SCharles.Forsyth	blen := len b.data;
19637da2899SCharles.Forsyth	if (s + n > b.start + blen)
19737da2899SCharles.Forsyth		n = b.start + blen - s;
19837da2899SCharles.Forsyth	if (n <= 0)
19937da2899SCharles.Forsyth		return (s, nil);
20037da2899SCharles.Forsyth	o := s % blen;
20137da2899SCharles.Forsyth	d := array[n] of byte;
20237da2899SCharles.Forsyth	if (o + n <= blen)
20337da2899SCharles.Forsyth		d[0:] = b.data[o:o+n];
20437da2899SCharles.Forsyth	else {
20537da2899SCharles.Forsyth		d[0:] = b.data[o:];
20637da2899SCharles.Forsyth		d[blen - o:] = b.data[0:o+n-blen];
20737da2899SCharles.Forsyth	}
20837da2899SCharles.Forsyth	return (s, d);
20937da2899SCharles.Forsyth}
21037da2899SCharles.Forsyth
21137da2899SCharles.ForsythFIDHASHSIZE: con 32;
21237da2899SCharles.Forsyth
21337da2899SCharles.ForsythFidhash.new(): ref Fidhash
21437da2899SCharles.Forsyth{
21537da2899SCharles.Forsyth	return ref Fidhash(array[FIDHASHSIZE] of list of ref Fidrec);
21637da2899SCharles.Forsyth}
21737da2899SCharles.Forsyth
21837da2899SCharles.Forsyth# put an entry in the hash table.
21937da2899SCharles.Forsyth# assumes there is no current entry for the fid.
22037da2899SCharles.ForsythFidhash.put(ht: self ref Fidhash, f: ref Fidrec)
22137da2899SCharles.Forsyth{
22237da2899SCharles.Forsyth	slot := f.fid & (FIDHASHSIZE-1);
22337da2899SCharles.Forsyth	ht.table[slot] = f :: ht.table[slot];
22437da2899SCharles.Forsyth}
22537da2899SCharles.Forsyth
22637da2899SCharles.ForsythFidhash.get(ht: self ref Fidhash, fid: int): ref Fidrec
22737da2899SCharles.Forsyth{
22837da2899SCharles.Forsyth	for (l := ht.table[fid & (FIDHASHSIZE-1)]; l != nil; l = tl l)
22937da2899SCharles.Forsyth		if ((hd l).fid == fid)
23037da2899SCharles.Forsyth			return hd l;
23137da2899SCharles.Forsyth	return nil;
23237da2899SCharles.Forsyth}
23337da2899SCharles.Forsyth
23437da2899SCharles.ForsythFidhash.del(ht: self ref Fidhash, f: ref Fidrec)
23537da2899SCharles.Forsyth{
23637da2899SCharles.Forsyth	slot := f.fid & (FIDHASHSIZE-1);
23737da2899SCharles.Forsyth	nl: list of ref Fidrec;
23837da2899SCharles.Forsyth	for (l := ht.table[slot]; l != nil; l = tl l)
23937da2899SCharles.Forsyth		if ((hd l).fid != f.fid)
24037da2899SCharles.Forsyth			nl = (hd l) :: nl;
24137da2899SCharles.Forsyth	ht.table[slot] = nl;
24237da2899SCharles.Forsyth}
24337da2899SCharles.Forsyth
24437da2899SCharles.Forsythpathsplit(p: string): (string, string)
24537da2899SCharles.Forsyth{
24637da2899SCharles.Forsyth	for (i := len p - 1; i >= 0; i--)
24737da2899SCharles.Forsyth		if (p[i] != '/')
24837da2899SCharles.Forsyth			break;
24937da2899SCharles.Forsyth	if (i < 0)
25037da2899SCharles.Forsyth		return (p, nil);
25137da2899SCharles.Forsyth	p = p[0:i+1];
25237da2899SCharles.Forsyth	for (i = len p - 1; i >=0; i--)
25337da2899SCharles.Forsyth		if (p[i] == '/')
25437da2899SCharles.Forsyth			break;
25537da2899SCharles.Forsyth	if (i < 0)
25637da2899SCharles.Forsyth		return (".", p);
25737da2899SCharles.Forsyth	return (p[0:i+1], p[i+1:]);
25837da2899SCharles.Forsyth}
25937da2899SCharles.Forsyth
260