Plan 9 from Bell Labs’s /usr/web/sources/contrib/nemo/octopus/port/spool.b

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


#
# File spooler. To build file systems that accept
# files to be given to underlying host commands.

# BUGS: This needs improvements:
#	-> listen to endc to detect when the file has been processed, and remove it.
#	-> accept general ctl requests via ctl, and pass them to the underlying spooler
#		module.
#	-> create FILE.status stream file, which reports (via read) any message sent
#		through endc until receiving nil, in which case it reports eof.


implement Spool;
include "sys.m";
	sys: Sys;
	Dir, pctl, NEWPGRP, DMDIR, open, OREAD, FD, OWRITE, ORCLOSE, FORKFD,
	ORDWR, FORKNS, NEWFD, MREPL, MBEFORE, MAFTER, MCREATE, pipe, mount,
	fprint, sprint, create, pwrite, read, QTDIR, QTFILE, fildes, Qid: import sys;
include "draw.m";
include "styx.m";
	styx: Styx;
	Rmsg, Tmsg: import styx;
include "error.m";
	err: Error;
	checkload, stderr, panic, kill, error: import err;
include "styxservers.m";
	styxs: Styxservers;
	Styxserver, readbytes, readstr, Navigator, Fid: import styxs;
	nametree: Nametree;
	Tree: import nametree;
include "daytime.m";
	daytime: Daytime;
	now: import daytime;
include "arg.m";
	arg: Arg;
	usage: import arg;
include "tbl.m";
	tbl: Tbl;
	Table: import tbl;
include "netget.m";
	netget: Netget;
include "string.m";
	str: String;
	splitr: import str;
include "env.m";
	env: Env;
	getenv: import env;
include "spooler.m";
	spooler: Spooler;
	Sfile: import spooler;

Spool: module {
	init: fn(nil: ref Draw->Context, argv: list of string);
};

File: adt {
	name: string;
	path:	string;
	fd:	ref FD;
	vers: int;
	sf:	ref Sfile;
};

files: ref Table[ref File];	# indexed by qid.

Qroot, Qctl, Qndb: con big iota;
qgen:= Qndb;
debug := 0;
user: string;
argv0 := "spool";
readstarts := 0;		# reading a file starts the spooler as well

readall(fname: string) : string
{
	fd := open(fname, OREAD);
	if (fd == nil)
		return "none";
	max : con int 1024;
	data := array[max] of byte;
	tot := nr := 0;
	do {
		nr = read(fd, data[tot:], len data - tot);
		if (nr > 0)
			tot += nr;
	} while(nr > 0 && tot < len data);
	if (tot == 0)
		return "none";
	return string data[0:tot];
	
}

newdir(name: string, perm: int, qid: big): Dir
{
	d := sys->zerodir;
	d.name = name;
	d.uid = user;
	d.gid = user;
	d.qid.path = qid;
	if (perm & DMDIR)
		d.qid.qtype = QTDIR;
	else
		d.qid.qtype = QTFILE;
	d.mode = perm;
	return d;
}

fsreq(srv: ref Styxserver, tree: ref Tree, req: ref Tmsg) : ref Rmsg
{
	pick m := req {
	Create =>
		(fid, mode, d, e) := srv.cancreate(m);
		if (e != nil)
			return ref Rmsg.Error(m.tag,  e);
		if (mode&DMDIR)
			return ref Rmsg.Error(m.tag, "can't handle directories");
		fpath := sprint("/tmp/%s.%d.%s", argv0, int qgen, d.name);
		f := ref File(d.name, fpath, nil, 0, nil);
		f.fd = create(f.path, OWRITE|ORCLOSE, 8r664);
		if (f.fd == nil)
			return ref Rmsg.Error(m.tag, sprint("tmpfile: %r"));
		d.qid = Qid(++qgen, 0, 0);
		d.atime = d.mtime = now();
		e = tree.create(Qroot, *d);
		if (e != nil)
			return ref Rmsg.Error(m.tag, e);
		fid.open(mode, d.qid);
		files.add(int fid.path, f);
		return ref Rmsg.Create(m.tag, d.qid, srv.iounit());
	Remove =>
		(fid, nil, e) := srv.canremove(m);
		srv.delfid(fid);
		if (e != nil)
			return ref Rmsg.Error(m.tag, e);
		if (fid.path == Qctl || fid.path == Qndb)
			return ref Rmsg.Error(m.tag, "permission denied");
		e = tree.remove(fid.path);
		if (e != nil)
			return ref Rmsg.Error(m.tag, e);
		f := files.del(int fid.path);
		if (f != nil && f.sf != nil)
			f.sf.stop(); 
		return ref Rmsg.Remove(m.tag);
	Read =>
		(fid, e) := srv.canread(m);
		if (e != nil)
			return ref Rmsg.Error(m.tag, e);
		if (fid.qtype&QTDIR)
			return nil;
		data := "";
		case fid.path {
		Qctl =>
			data = spooler->status() + "\n";
		Qndb =>
			data = netget->ndb() + "\n";
		}
		return readstr(m, data);
	Write =>
		(fid, e) := srv.canwrite(m);
		if (e != nil)
			return ref Rmsg.Error(m.tag, e);
		if (fid.path == Qctl || fid.path == Qndb)
			return ref Rmsg.Error(m.tag, "permission denied");
		f := files.find(int fid.path);
		if (f == nil || f.fd == nil)
			return ref Rmsg.Error(m.tag, "file not found");
		nw := pwrite(f.fd, m.data, len m.data, m.offset);
		if (nw < 0)
			return ref Rmsg.Error(m.tag, sprint("%r"));
		f.vers++;
		return ref Rmsg.Write(m.tag, nw);
	Clunk =>
		fid := srv.getfid(m.fid);
		if (fid == nil)
			return ref Rmsg.Error(m.tag, "bad fid");
		if (fid.path != Qctl && fid.path != Qroot && fid.path != Qndb){
			f := files.find(int fid.path);
			if (f != nil && f.fd != nil && f.vers > 0 && fid.isopen){
				if (fid.mode == OWRITE || fid.mode == ORDWR || readstarts)
					(f.sf, nil) = Sfile.start(f.path, nil);
					# BUG: should listen through endc (nil above)
					# and remove the file or report diagnostics
					# sent through it.
			}
		}
		srv.delfid(fid);
		return ref Rmsg.Clunk(m.tag);
	Wstat =>
		return ref Rmsg.Wstat(m.tag);
	* =>
		return nil;
	}
}

fs(pidc: chan of int, fd: ref FD)
{
	styx->init();
	styxs->init(styx);
 	user = getenv("user");
	if (user == nil)
		user = readall("/dev/user");
	if (pidc != nil)
		pidc <-= pctl(FORKNS|NEWPGRP|NEWFD, list of {0,1,2,fd.fd});
	else
		pctl(NEWPGRP, nil);
	stderr = fildes(2);
	netget->announce(argv0, sprint("path /%s", argv0));
	(tree, navc) := nametree->start();
	nav := Navigator.new(navc);
	(reqc, srv) := Styxserver.new(fd, nav, Qroot);
	tree.create(Qroot, newdir(".", DMDIR|8r775, Qroot));
	tree.create(Qroot, newdir("ctl", 8r444, Qctl));
	tree.create(Qroot, newdir("ndb", 8r444, Qndb));
	nullfile: ref File;
	files = Table[ref File].new(103, nullfile);
	for (;;) {
		req := <-reqc;
		if (req == nil)
			break;
		rep := fsreq(srv, tree, req);
		if (rep == nil)
			srv.default(req);
		else
			srv.reply(rep);
	}
	tree.quit();
	netget->terminate(); 
	kill(pctl(0, nil),"killgrp");	# be sure to quit
}

init(nil: ref Draw->Context, args: list of string)
{
	sys = load Sys Sys->PATH;
	err = load Error Error->PATH;
	err->init();
	str = checkload(load String String->PATH, String->PATH);
	styx = checkload(load Styx Styx->PATH, Styx->PATH);
	styxs = checkload(load Styxservers Styxservers->PATH, Styxservers->PATH);
	nametree = checkload(load Nametree Nametree->PATH, Nametree->PATH);
	nametree->init();
 	daytime = checkload(load Daytime Daytime->PATH, Daytime->PATH);
	tbl = checkload(load Tbl Tbl->PATH, Tbl->PATH);
	env = checkload(load Env Env->PATH, Env->PATH);
	netget = checkload(load Netget Netget->PATH, Netget->PATH);
	arg = checkload(load Arg Arg->PATH, Arg->PATH);
	arg->init(args);
	arg->setusage("spool [-abcdr] [-m mnt] module");
	mnt: string;
	flag := MREPL|MCREATE;
	while((opt := arg->opt()) != 0) {
		case opt{
		'b' =>
			flag = MBEFORE;
		'a' =>
			flag = MAFTER;
		'c' =>
			flag |= MCREATE;
		'm' =>
			mnt = arg->earg();
		'd' =>
			debug = 1;
			styxs->traceset(1);
		'r' =>
			readstarts = 1;
		* =>
			usage();
		}
	}
	args = arg->argv();
	if (len args < 1)
		usage();
	argv0 = hd args;
	(nil, s2) := splitr(argv0, "/");
	if (s2 != nil && s2 != "")
		argv0 = s2;
	dis := "/dis/" + (hd args) + ".dis";
	spooler = checkload(load Spooler dis, dis);
	spooler->init(tl args);
	spooler->debug = debug;
	if (mnt == nil)
		fs(nil, fildes(0));
	else {
		pfds := array[2] of ref FD;
		if (pipe(pfds) < 0)
			error(sprint("%s: pipe: %r", argv0));
		pidc := chan of int;
		spawn fs(pidc, pfds[0]);
		<-pidc;
		if (mount(pfds[1], nil, mnt, flag, nil) < 0)
			error(sprint("%s: mount: %r", argv0));
		pfds[0] = nil;
	}
}

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.