ref: babf901b4a508c3ec5d1f89655f10377bbdf9637
dir: /appl/cmd/install/create.b/
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;
}