code: 9ferno

ref: b548687a8ed1d0a159c9d3f3f921d93bbb56908e
dir: /appl/cmd/install/create.b/

View raw version
implement Create;

include "sys.m";
	sys: Sys;
	Dir, sprint, fprint: import sys;
include "draw.m";
include "bufio.m";
	bufio: Bufio;
	Iobuf: import bufio;
include "string.m";
	str: String;
include "arg.m";
	arg: Arg;
include "daytime.m";
include "keyring.m";
	keyring : Keyring;
include "sh.m";
include "wrap.m";
	wrap : Wrap;
include "arch.m";
	arch : Arch;
include "proto.m";
	proto : Proto;
include "protocaller.m";
	protocaller : Protocaller;

WARN, ERROR, FATAL : import Protocaller;

Create: module{
	init:	fn(nil: ref Draw->Context, nil: list of string);
	protofile: fn(new : string, old : string, d : ref Sys->Dir);
	protoerr: fn(lev : int, line : int, err : string);
};

bout: ref Iobuf;			# stdout when writing archive
protof: string;
notesf: string;
oldroot: string;
buf: array of byte;
buflen := 1024-8;
verb: int;
xflag: int;
stderr: ref Sys->FD;
uid, gid : string;
desc : string;
pass : int;
update : int;
md5s : ref Keyring->DigestState;
w : ref Wrap->Wrapped;
root := "/";
prefix, notprefix: list of string;
onlist: list of (string, string);	# NEW
remfile: string;

n2o(n: string): string
{
	for(onl := onlist; onl != nil; onl = tl onl)
		if((hd onl).t1 == n)
			return (hd onl).t0;
	return n;
}

init(nil: ref Draw->Context, args: list of string)
{
	sys = load Sys Sys->PATH;
	bufio = load Bufio Bufio->PATH;
	str = load String String->PATH;
	arg = load Arg Arg->PATH;
	wrap = load Wrap Wrap->PATH;
	wrap->init(bufio);
	arch = load Arch Arch->PATH;
	arch->init(bufio);
	daytime := load Daytime Daytime->PATH;
	now := daytime->now();
	# {
	#	for(i := 0; i < 21; i++){
	#		n := now+(i-9)*100000000;
	#		sys->print("%d	->	%s\n", n, wrap->now2string(n));
	#		if(wrap->string2now(wrap->now2string(n)) != n)
	#			sys->print("%d wrong\n", n);
	#	}
	# }
	daytime = nil;
	proto = load Proto Proto->PATH;
	protocaller = load Protocaller "$self";

	sys->pctl(Sys->NEWPGRP|Sys->FORKNS|Sys->FORKFD, nil);
	stderr = sys->fildes(2);
	if(arg == nil)
		error(sys->sprint("can't load %s: %r", Arg->PATH));
	name := "";
	desc = "inferno";
	tostdout := 0;
	not := 0;
	arg->init(args);
	while((c := arg->opt()) != 0)
		case c {
		'n' =>
			not = 1;
		'o' =>
			tostdout = 1;
		'p' =>
			protof = reqarg("proto file (-p)");
		'r' => 
			root = reqarg("root directory (-r)");
		's' =>
			oldroot = reqarg("source directory (-d)");
		'u' =>
			update = 1;
		'v' =>
			verb = 1;
		'x' =>
			xflag = 1;
		'N' =>
			uid = reqarg("user name (-U)");
		'G' =>
			gid = reqarg("group name (-G)");
		'd' or 'D' =>
			desc = reqarg("product description (-D)");
		't' =>
			rt := reqarg("package time (-t)");
			now = int rt;
		'i' =>
			notesf = reqarg("file (-i)");
		'R' =>
			remfile = reqarg("remove file (-R)");
		'P' =>
			arch->addperms(0);
		* =>
			usage();
		}

	args = arg->argv();
	if(args == nil)
		usage();
	if (tostdout || xflag) {
		bout = bufio->fopen(sys->fildes(1), Sys->OWRITE);
		if(bout == nil)
			error(sys->sprint("can't open standard output for archive: %r"));
	}
	else {
		# ar := sys->sprint("%ud", now);
		ar := wrap->now2string(now, 0);
		bout = bufio->create(ar, Sys->OWRITE, 8r664);
		if(bout == nil)
			error(sys->sprint("can't create %s for archive: %r", ar));
		sys->print("archiving package %s to %s\n", hd args, ar);
	}
	buf = array [buflen] of byte;
	name = hd args;
	if(update){
		if(not)
			notprefix = tl args;
		else
			prefix = tl args;
	}
	else if (tl args != nil)
		fatal("only one name allowed");
	if (!xflag)
		digest := wrapinit(name, now);
	fprint(stderr, "processing %s\n", protof);
	proto->rdproto(protof, oldroot, protocaller);
	if (!xflag)
		wrapend(digest);
	if (!xflag)
		fprint(stderr, "file system made\n");
	arch->putend(bout);
	exits();
}

protofile(new : string, old : string, d : ref Sys->Dir)
{
	if(xflag && bout != nil){
		bout.puts(sys->sprint("%s\t%d\t%bd\n", new, d.mtime, d.length));
		return;
	}
	d.uid = uid;
	d.gid = gid;
	if (!(d.mode & Sys->DMDIR)) {
		# if(verb)
		#	fprint(stderr, "%s\n", new);
		f := sys->open(old, Sys->OREAD);
		if(f == nil){
			warn(sys->sprint("can't open %s: %r", old));
			return;
		}
	}
	mkarch(new, old, d);
}

protoerr(lev : int, line : int, err : string)
{
	s := "line " + string line + " : " + err;
	case lev {
		WARN => warn(s);
		ERROR => error(s);
		FATAL => fatal(s);
	}
}

quit()
{
	if(bout != nil)
		bout.flush();
	exits();
}

reqarg(what: string): string
{
	if((o := arg->arg()) == nil){
		sys->fprint(stderr, "missing %s\n", what);
		exits();
	}
	return o;
}

puthdr(f : string, d: ref Dir)
{
	if (d.mode & Sys->DMDIR)
		d.length = big 0;
	arch->puthdr(bout, f, d);
}

error(s: string)
{
	fprint(stderr, "%s: %s\n", protof, s);
	quit();
}

fatal(s: string)
{
	fprint(stderr, "fatal: %s\n", s);
	exits();
}
 
warn(s: string)
{
	fprint(stderr, "%s: %s\n", protof, s);
}
	
usage()
{
	fprint(stderr, "usage: install/create [-ovx] [-N uid] [-G gid] [-r root] [-d desc] [-s src-fs] [-p proto] name\n");
	fprint(stderr, "or install/create -u [-ovx] [-N uid] [-G gid] [-r root] [-d desc] [-s src-fs] [-p proto] old-package [prefix ...]\n");
	exits();
}

wrapinit(name : string, t : int) : array of byte
{
	rmfile : string;
	rmfd: ref Sys->FD;

	if (uid == nil)
		uid = "inferno";
	if (gid == nil)
		gid = "inferno";
	if (update) {
		w = wrap->openwraphdr(name, root, nil, 0);
		if (w == nil)
			fatal("no such package found");
		# ignore any updates - NEW commented out
		# while (w.nu > 0 && w.u[w.nu-1].typ == wrap->UPD)
		#	w.nu--;

		# w.nu = 1;	NEW commented out
		if (protof == nil)
			protof = w.u[0].dir + "/proto";
		name = w.name;
	}
	else {
		if (protof == nil)
			fatal("proto file missing");
	}
	(md5file, md5fd) := opentemp("wrap.md5", t);
	if (md5fd == nil)
		fatal(sys->sprint("cannot create %s", md5file));
	keyring = load Keyring Keyring->PATH;
	md5s = keyring->md5(nil, 0, nil, nil);
	md5b := bufio->fopen(md5fd, Bufio->OWRITE);
	if (md5b == nil)
		fatal(sys->sprint("cannot open %s", md5file));
	fprint(stderr, "wrap pass %s\n", protof);
	obout := bout;
	bout = md5b;
	pass = 0;
	proto->rdproto(protof, oldroot, protocaller);
	bout.flush();
	bout = md5b = nil;
	digest := array[keyring->MD5dlen] of { * => byte 0 };
	keyring->md5(nil, 0, digest, md5s);
	md5s = nil;
	(md5sort, md5sfd) := opentemp("wrap.md5s", t);
	if (md5sfd == nil)
		fatal(sys->sprint("cannot create %s", md5sort));
	endc := chan of int;
	md5fd = nil;	# close md5file
	spawn fsort(md5sfd, md5file, endc);
	md5sfd = nil;
	res := <- endc;
	if (res < 0)
		fatal("sort failed");
	if (update) {
		(rmfile, rmfd) = opentemp("wrap.rm", t);
		if (rmfd == nil)
			fatal(sys->sprint("cannot create %s", rmfile));
		rmed: list of string;
		for(i := w.nu-1; i >= 0; i--){	# NEW does loop
			w.u[i].bmd5.seek(big 0, Bufio->SEEKSTART);
			while ((p := w.u[i].bmd5.gets('\n')) != nil) {
				if(prefix != nil && !wrap->match(p, prefix))
					continue;
				if(notprefix != nil && !wrap->notmatch(p, notprefix))
					continue;
				(q, nil) := str->splitl(p, " ");
				q = pathcat(root, q);
				(ok, nil) := sys->stat(q);
				if(ok < 0)
					(ok, nil) = sys->stat(n2o(q));
				if (len q >= 7 && q[len q - 7:] == "emu.new")	# quick hack for now
					continue;
				if (ok < 0){
					for(r := rmed; r != nil; r = tl r)	# NEW to avoid duplication
						if(hd r == q)
							break;
					if(r == nil){
						# sys->fprint(rmfd, "%s\n", q);
						rmed = q :: rmed;
					}
				}
			}
		}
		for(r := rmed; r != nil; r = tl r)
			sys->fprint(rmfd, "%s\n", hd r);
		if(remfile != nil){
			rfd := sys->open(remfile, Sys->OREAD);
			rbuf := array[128] of byte;
			for(;;){
				n := sys->read(rfd, rbuf, 128);
				if(n <= 0)
					break;
				sys->write(rmfd, rbuf, n);
			}
		}
		rmfd = nil;
		rmed = nil;
	}
	bout = obout;
	if (update)
		wrap->putwrap(bout, name, t, desc, w.tfull, prefix == nil && notprefix == nil, uid, gid);
	else
		wrap->putwrap(bout, name, t, desc, 0, 1, uid, gid);
	wrap->putwrapfile(bout, name, t, "proto", protof, uid, gid);
	wrap->putwrapfile(bout, name, t, "md5sum", md5sort, uid, gid);
	if (update)
		wrap->putwrapfile(bout, name, t, "remove", rmfile, uid, gid);
	if(notesf != nil)
		wrap->putwrapfile(bout, name, t, "notes", notesf, uid, gid);
	md5s = keyring->md5(nil, 0, nil, nil);
	pass = 1;
	return digest;
}

wrapend(digest : array of byte)
{
	digest0 := array[keyring->MD5dlen] of { * => byte 0 };
	keyring->md5(nil, 0, digest0, md5s);
	md5s = nil;
	if (wrap->memcmp(digest, digest0, keyring->MD5dlen) != 0)
		warn(sys->sprint("files changed underfoot %s %s", wrap->md5conv(digest), wrap->md5conv(digest0)));
}

mkarch(new : string, old : string, d : ref Dir)
{
	if(pass == 0 && old != new)
		onlist = (old, new) :: onlist;
	if(prefix != nil && !wrap->match(new, prefix))
		return;
	if(notprefix != nil && !wrap->notmatch(new, notprefix))
		return;
	digest := array[keyring->MD5dlen] of { * => byte 0 };
	wrap->md5file(old, digest);
	(ok, nil) := wrap->getfileinfo(w, new, digest, nil, nil);
	if (ok >= 0)
		return;
	n := array of byte new;
	keyring->md5(n, len n, nil, md5s);
	if (pass == 0) {
		bout.puts(sys->sprint("%s %s\n", new, wrap->md5conv(digest)));
		return;
	}
	if(verb)
		fprint(stderr, "%s\n", new);
	puthdr(new, d);
	if(!(d.mode & Sys->DMDIR)) {
		err := arch->putfile(bout, old, int d.length);
		if (err != nil)
			warn(err);
	}
}

fsort(fd : ref Sys->FD, file : string, c : chan of int)
{
	sys->pctl(Sys->FORKFD, nil);
	sys->dup(fd.fd, 1);
	cmd := "/dis/sort.dis";
	m := load Command cmd;
	if(m == nil) {
		c <-= -1;
		return;
	}
	m->init(nil, cmd :: file :: nil);
	c <-= 0;
}

tmpfiles: list of string;

opentemp(prefix: string, t: int): (string, ref Sys->FD)
{
	name := sys->sprint("/tmp/%s.%ud.%d", prefix, t, sys->pctl(0, nil));
	fd := sys->create(name, Sys->ORDWR, 8r666);
	# fd := sys->create(name, Sys->ORDWR | Sys->ORCLOSE, 8r666); not on Nt
	tmpfiles = name :: tmpfiles;
	return (name, fd);
}

exits()
{
	wrap->end();
	for( ; tmpfiles != nil; tmpfiles = tl tmpfiles)
		sys->remove(hd tmpfiles);
	exit;
}

pathcat(s : string, t : string) : string
{
	if (s == nil) return t;
	if (t == nil) return s;
	slashs := s[len s - 1] == '/';
	slasht := t[0] == '/';
	if (slashs && slasht)
		return s + t[1:];
	if (!slashs && !slasht)
		return s + "/" + t;
	return s + t;
}