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; 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: list of string; init(nil: ref Draw->Context, args: list of string) { sys = load Sys Sys->PATH; bufio = load Bufio Bufio->PATH; bufio->init(); str = load String String->PATH; arg = load Arg Arg->PATH; wrap = load Wrap Wrap->PATH; wrap->init(); arch = load Arch Arch->PATH; arch->init(nil, nil); daytime := load Daytime Daytime->PATH; now := daytime->now(); 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; arg->init(args); while((c := arg->opt()) != 0) case c { '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; * => 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); 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) 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); wrap->end(); } protofile(new : string, old : string, d : ref Sys->Dir) { if(xflag && bout != nil){ bout.puts(sys->sprint("%s\t%d\t%d\n", new, d.mtime, d.length)); return; } d.uid = uid; d.gid = gid; if (!(d.mode & Sys->CHDIR)) { 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(); exit; } reqarg(what: string): string { if((o := arg->arg()) == nil){ sys->fprint(stderr, "missing %s\n", what); exit; } return o; } puthdr(f : string, d: ref Dir) { if (d.mode & Sys->CHDIR) d.length = 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); exit; } 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"); exit; } wrapinit(name : string, t : int) : array of byte { rmfile : string; rmfd: ref Sys->FD; if (uid == nil) uid = "inferno"; if (gid == nil) gid = "inf"; if (update) { w = wrap->openwraphdr(name, root, nil, 0); if (w == nil) fatal("no such package found"); # ignore any updates while (w.nu > 0 && w.u[w.nu-1].typ == wrap->UPD) w.nu--; w.nu = 1; 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; spawn fsort(md5sfd, md5file, endc); md5fd = nil; # close and remove md5file 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)); w.u[0].bmd5.seek(0, Bufio->SEEKSTART); while ((p := w.u[0].bmd5.gets('\n')) != nil) { if(prefix != nil && !wrap->match(p, prefix)) continue; (q, nil) := str->splitl(p, " "); q = pathcat(root, q); (ok, nil) := sys->stat(q); if (len q >= 7 && q[len q - 7:] == "emu.new") # quick hack for now continue; if (ok < 0) sys->fprint(rmfd, "%s\n", q); } } bout = obout; if (update) wrap->putwrap(bout, name, t, desc, w.tfull, prefix == 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); 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(prefix != nil && !wrap->match(new, prefix)) return; digest := array[keyring->MD5dlen] of { * => byte 0 }; wrap->md5file(old, digest); (ok, t) := wrap->getfileinfo(w, new, digest, 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; } puthdr(new, d); if(!(d.mode & Sys->CHDIR)) { err := arch->putfile(bout, old, 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; } 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 | Sys->ORCLOSE, 8r666); return (name, fd); } 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; }