summaryrefslogtreecommitdiff
path: root/appl/cmd/install/inst.b
diff options
context:
space:
mode:
Diffstat (limited to 'appl/cmd/install/inst.b')
-rw-r--r--appl/cmd/install/inst.b500
1 files changed, 500 insertions, 0 deletions
diff --git a/appl/cmd/install/inst.b b/appl/cmd/install/inst.b
new file mode 100644
index 00000000..dfec4785
--- /dev/null
+++ b/appl/cmd/install/inst.b
@@ -0,0 +1,500 @@
+implement Inst;
+
+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 "keyring.m";
+ keyring : Keyring;
+include "arch.m";
+ arch : Arch;
+include "wrap.m";
+ wrap : Wrap;
+
+Inst: module
+{
+ init: fn(nil: ref Draw->Context, nil: list of string);
+};
+
+LEN: con Sys->ATOMICIO;
+
+tflag := 0;
+uflag := 0;
+hflag := 0;
+vflag := 0;
+fflag := 1;
+stderr: ref Sys->FD;
+bout: ref Iobuf;
+argv0 := "inst";
+oldw, w : ref Wrap->Wrapped;
+root := "/";
+force := 0;
+stoponerr := 1;
+
+# membogus(argv: list of string)
+# {
+#
+# }
+
+init(nil: ref Draw->Context, args: list of string)
+{
+ sys = load Sys Sys->PATH;
+ stderr = sys->fildes(2);
+ bufio = load Bufio Bufio->PATH;
+ if(bufio == nil)
+ error(sys->sprint("cannot load %s: %r\n", Bufio->PATH));
+
+ str = load String String->PATH;
+ if(str == nil)
+ error(sys->sprint("cannot load %s: %r\n", String->PATH));
+
+ arg = load Arg Arg->PATH;
+ if(arg == nil)
+ error(sys->sprint("cannot load %s: %r\n", Arg->PATH));
+ keyring = load Keyring Keyring->PATH;
+ if(keyring == nil)
+ error(sys->sprint("cannot load %s: %r\n", Keyring->PATH));
+ arch = load Arch Arch->PATH;
+ if(arch == nil)
+ error(sys->sprint("cannot load %s: %r\n", Arch->PATH));
+ arch->init(bufio);
+ wrap = load Wrap Wrap->PATH;
+ if(wrap == nil)
+ error(sys->sprint("cannot load %s: %r\n", Wrap->PATH));
+ wrap->init(bufio);
+ arg->init(args);
+ while((c := arg->opt()) != 0)
+ case c {
+ 'f' =>
+ fflag = 0;
+ 'h' =>
+ hflag = 1;
+ bout = bufio->fopen(sys->fildes(1), Sys->OWRITE);
+ if(bout == nil)
+ error(sys->sprint("can't access standard output: %r"));
+ 't' =>
+ tflag = 1;
+ 'u' =>
+ uflag = 1;
+ 'v' =>
+ vflag = 1;
+ 'r' =>
+ root = arg->arg();
+ if (root == nil)
+ fatal("root missing");
+ 'F' =>
+ force = 1;
+ 'c' =>
+ stoponerr = 0;
+ * =>
+ usage();
+ }
+ args = arg->argv();
+ if (args == nil)
+ usage();
+ ar := arch->openarch(hd args);
+ if(ar == nil || ar.b == nil)
+ error(sys->sprint("can't access %s: %r", hd args));
+ w = wrap->openwraphdr(hd args, root, nil, 0);
+ if (w == nil)
+ fatal("no such package found");
+ if(w.nu != 1)
+ fatal("strange package: more than one piece");
+ if (force == 0)
+ oldw = wrap->openwrap(w.name, root, 0);
+ if (force == 0 && w.u[0].utime && (oldw == nil || oldw.tfull < w.u[0].utime)){
+ tfull: int;
+ if(oldw == nil)
+ tfull = -1;
+ else
+ tfull = oldw.tfull;
+ fatal(sys->sprint("need %s version of %s already installed (pkg %d)", wrap->now2string(w.u[0].utime, 0), w.name, tfull));
+ }
+ args = tl args;
+ digest := array[Keyring->MD5dlen] of byte;
+ digest0 := array[Keyring->MD5dlen] of byte;
+ digest1 := array[Keyring->MD5dlen] of byte;
+
+ while ((a := arch->gethdr(ar)) != nil) {
+ why := "";
+ docopy := 0;
+ if(force)
+ docopy = 1;
+ else if(a.d.mode & Sys->DMDIR)
+ docopy = 1;
+ else if(wrap->md5file(root+a.name, digest) < 0)
+ docopy = 1;
+ else{
+ wrap->md5filea(root+a.name, digest1);
+ (ok, t) := wrap->getfileinfo(oldw, a.name, digest, nil, digest1);
+ if (ok >= 0) {
+ if(t > w.u[0].time){
+ docopy = 0;
+ why = "version from newer package exists";
+ }
+ else
+ docopy = 1;
+ }
+ else {
+ (ok, t) = wrap->getfileinfo(oldw, a.name, nil, nil, nil);
+ if(ok >= 0){
+ docopy = 0;
+ why = "locally modified";
+ }
+ else{
+ docopy = 0;
+ why = "locally created";
+ }
+ }
+ }
+ if(!docopy){
+ wrap->md5sum(ar.b, digest0, int a.d.length);
+ if(wrap->memcmp(digest, digest0, Keyring->MD5dlen))
+ skipfile(a.name, why);
+ continue;
+ }
+ if(args != nil){
+ if(!selected(a.name, args)){
+ arch->drain(ar, int a.d.length);
+ continue;
+ }
+ if (!hflag)
+ mkdirs(root, a.name);
+ }
+ name := pathcat(root, a.name);
+ if(hflag){
+ bout.puts(sys->sprint("%s %uo %s %s %ud %d\n",
+ name, a.d.mode, a.d.uid, a.d.gid, a.d.mtime, int a.d.length));
+ arch->drain(ar, int a.d.length);
+ continue;
+ }
+ if(a.d.mode & Sys->DMDIR)
+ mkdir(name, a.d);
+ else
+ extract(ar, name, a.d);
+ }
+ arch->closearch(ar);
+ if(ar.err == nil){
+ # fprint(stderr, "done\n");
+ quit(nil);
+ }
+ else {
+ fprint(stderr, "%s\n", ar.err);
+ quit("eof");
+ }
+}
+
+skipfile(f : string, why : string)
+{
+ sys->fprint(stderr, "skipping %s: %s\n", f, why);
+}
+
+skiprmfile(f: string, why: string)
+{
+ sys->fprint(stderr, "not removing %s: %s\n", f, why);
+}
+
+doremove(s : string)
+{
+ p := pathcat(root, s);
+ digest := array[Keyring->MD5dlen] of { * => byte 0 };
+ digest1 := array[Keyring->MD5dlen] of { * => byte 0 };
+ if(wrap->md5file(p, digest) < 0)
+ ;
+ else{
+ wrap->md5filea(p, digest1);
+ (ok, nil) := wrap->getfileinfo(oldw, s, digest, nil, digest1);
+ if(force == 0 && ok < 0)
+ skiprmfile(p, "locally modified");
+ else{
+ if (vflag)
+ sys->print("rm %s\n", p);
+ remove(p);
+ }
+ }
+}
+
+quit(s: string)
+{
+ if (s == nil) {
+ p := w.u[0].dir + "/remove";
+ if ((b := bufio->open(p, Bufio->OREAD)) != nil) {
+ while ((t := b.gets('\n')) != nil) {
+ lt := len t;
+ if (t[lt-1] == '\n')
+ t = t[0:lt-1];
+ doremove(t);
+ }
+ }
+ }
+ if(bout != nil)
+ bout.flush();
+ if(wrap != nil)
+ wrap->end();
+ if(s != nil)
+ raise "fail: "+s;
+ else
+ fprint(stderr, "done\n");
+ exit;
+}
+
+fileprefix(prefix, s: string): int
+{
+ n := len prefix;
+ m := len s;
+ if(n > m || !str->prefix(prefix, s))
+ return 0;
+ if(m > n && s[n] != '/')
+ return 0;
+ return 1;
+}
+
+selected(s: string, args: list of string): int
+{
+ for(; args != nil; args = tl args)
+ if(fileprefix(hd args, s))
+ return 1;
+ return 0;
+}
+
+mkdirs(basedir, name: string)
+{
+ (nil, names) := sys->tokenize(name, "/");
+ while(names != nil) {
+ create(basedir, Sys->OREAD, 8r775|Sys->DMDIR);
+ if(tl names == nil)
+ break;
+ basedir = basedir + "/" + hd names;
+ names = tl names;
+ }
+}
+
+mkdir(name: string, dir : ref Sys->Dir)
+{
+ d: Dir;
+ i: int;
+
+ if(vflag) {
+ MTPT : con "/n/remote";
+ s := name;
+ if (len name >= len MTPT && name[0:len MTPT] == MTPT)
+ s = name[len MTPT:];
+ sys->print("installing directory %s\n", s);
+ }
+ fd := create(name, Sys->OREAD, dir.mode);
+ if(fd == nil) {
+ err := sys->sprint("%r");
+ (i, d) = sys->stat(name);
+ if(i < 0 || !(d.mode & Sys->DMDIR)){
+ werr(sys->sprint("can't make directory %s: %s", name, err));
+ return;
+ }
+ }
+ else {
+ (i, d) = sys->fstat(fd);
+ if(i < 0)
+ warn(sys->sprint("can't stat %s: %r", name));
+ fd = nil;
+ }
+ d = sys->nulldir;
+ (nil, p) := str->splitr(name, "/");
+ if(p == nil)
+ p = name;
+ d.name = p;
+ d.mode = dir.mode;
+ if(tflag || uflag)
+ d.mtime = dir.mtime;
+ if(uflag){
+ d.uid = dir.uid;
+ d.gid = dir.gid;
+ }
+ fd = nil;
+ if(sys->wstat(name, d) < 0){
+ e := sys->sprint("%r");
+ if(wstat(name, d) < 0)
+ warn(sys->sprint("can't set modes for %s: %s", name, e));
+ }
+ if(uflag){
+ (i, d) = sys->stat(name);
+ if(i < 0)
+ warn(sys->sprint("can't reread modes for %s: %r", name));
+ if(dir.uid != d.uid)
+ warn(sys->sprint("%s: uid mismatch %s %s", name, dir.uid, d.uid));
+ if(dir.gid != d.gid)
+ warn(sys->sprint("%s: gid mismatch %s %s", name, dir.gid, d.gid));
+ }
+}
+
+extract(ar : ref Arch->Archive, name: string, dir : ref Sys->Dir)
+{
+ sfd := create(name, Sys->OWRITE, dir.mode);
+ if(sfd == nil) {
+ if(!fflag || remove(name) == -1 ||
+ (sfd = create(name, Sys->OWRITE, dir.mode)) == nil) {
+ werr(sys->sprint("can't make file %s: %r", name));
+ arch->drain(ar, int dir.length);
+ return;
+ }
+ }
+ b := bufio->fopen(sfd, Bufio->OWRITE);
+ if (b == nil) {
+ warn(sys->sprint("can't open file %s for bufio : %r", name));
+ arch->drain(ar, int dir.length);
+ return;
+ }
+ err := arch->getfile(ar, b, int dir.length);
+ if (err != nil) {
+ if (len err >= 9 && err[0:9] == "premature")
+ fatal(err);
+ else
+ warn(err);
+ }
+ (i, d) := sys->fstat(b.fd);
+ if(i < 0)
+ warn(sys->sprint("can't stat %s: %r", name));
+ d = sys->nulldir;
+ (nil, p) := str->splitr(name, "/");
+ if(p == nil)
+ p = name;
+ d.name = p;
+ d.mode = dir.mode;
+ if(tflag || uflag)
+ d.mtime = dir.mtime;
+ if(uflag){
+ d.uid = dir.uid;
+ d.gid = dir.gid;
+ }
+ if(b.flush() == Bufio->ERROR)
+ werr(sys->sprint("error writing %s: %r", name));
+ b.close();
+ sfd = nil;
+ if(sys->wstat(name, d) < 0){
+ e := sys->sprint("%r");
+ if(wstat(name, d) < 0)
+ warn(sys->sprint("can't set modes for %s: %s", name, e));
+ }
+ if(uflag){
+ (i, d) = sys->stat(name);
+ if(i < 0)
+ warn(sys->sprint("can't reread modes for %s: %r", name));
+ if(d.uid != dir.uid)
+ warn(sys->sprint("%s: uid mismatch %s %s", name, dir.uid, d.uid));
+ if(d.gid != dir.gid)
+ warn(sys->sprint("%s: gid mismatch %s %s", name, dir.gid, d.gid));
+ }
+}
+
+error(s: string)
+{
+ fprint(stderr, "%s: %s\n", argv0, s);
+ quit("error");
+}
+
+werr(s: string)
+{
+ fprint(stderr, "%s: %s\n", argv0, s);
+ if(stoponerr)
+ quit("werr");
+}
+
+warn(s: string)
+{
+ fprint(stderr, "%s: %s\n", argv0, s);
+}
+
+usage()
+{
+ fprint(stderr, "Usage: inst [-h] [-u] [-v] [-f] [-c] [-F] [-r dest-root] [file ...]\n");
+ raise "fail: usage";
+}
+
+fatal(s : string)
+{
+ sys->fprint(stderr, "inst: %s\n", s);
+ if(wrap != nil)
+ wrap->end();
+ exit;
+}
+
+parent(name : string) : string
+{
+ slash := -1;
+ for (i := 0; i < len name; i++)
+ if (name[i] == '/')
+ slash = i;
+ if (slash > 0)
+ return name[0:slash];
+ return "/";
+}
+
+create(name : string, rw : int, mode : int) : ref Sys->FD
+{
+ fd := sys->create(name, rw, mode);
+ if (fd == nil) {
+ p := parent(name);
+ (ok, d) := sys->stat(p);
+ if (ok < 0)
+ return nil;
+ omode := d.mode;
+ d = sys->nulldir;
+ d.mode = omode | 8r222; # ensure parent is writable
+ sys->wstat(p, d);
+ fd = sys->create(name, rw, mode);
+ d.mode = omode;
+ sys->wstat(p, d);
+ }
+ return fd;
+}
+
+remove(name : string) : int
+{
+ if (sys->remove(name) < 0) {
+ (ok, d) := sys->stat(name);
+ if (ok < 0)
+ return -1;
+ omode := d.mode;
+ d.mode |= 8r222;
+ sys->wstat(name, d);
+ if (sys->remove(name) >= 0)
+ return 0;
+ d.mode = omode;
+ sys->wstat(name, d);
+ return -1;
+ }
+ return 0;
+}
+
+wstat(name : string, d : Dir) : int
+{
+ (ok, dir) := sys->stat(name);
+ if (ok < 0)
+ return -1;
+ omode := dir.mode;
+ dir.mode |= 8r222;
+ sys->wstat(name, dir);
+ if (sys->wstat(name, d) >= 0)
+ return 0;
+ dir.mode = omode;
+ sys->wstat(name, dir);
+ return -1;
+}
+
+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;
+}