diff options
Diffstat (limited to 'appl/cmd/install/install.b')
| -rw-r--r-- | appl/cmd/install/install.b | 430 |
1 files changed, 430 insertions, 0 deletions
diff --git a/appl/cmd/install/install.b b/appl/cmd/install/install.b new file mode 100644 index 00000000..858f3a27 --- /dev/null +++ b/appl/cmd/install/install.b @@ -0,0 +1,430 @@ +implement Install; + +# +# Determine which packages need installing and calls install/inst +# to actually install each one +# + +# usage: install/install -d -F -g -s -u -i installdir -p platform -r root -P package + +include "sys.m"; + sys: Sys; +include "draw.m"; +include "bufio.m"; + bufio: Bufio; + Iobuf: import bufio; +include "string.m"; + str: String; +include "arg.m"; + arg: Arg; +include "readdir.m"; + readdir : Readdir; +include "sh.m"; + +Install: module +{ + init: fn(nil: ref Draw->Context, nil: list of string); +}; + +# required dirs, usually in the standard inferno root. +# The network download doesn't include them because of +# problems with versions of tar that won't create empty dirs +# so we'll make sure they exist. + +reqdirs := array [] of { + "/mnt", + "/mnt/wrap", + "/n", + "/n/remote", + "/tmp", +}; + +YES, NO, QUIT, ERR : con iota; +INST : con "install/inst"; # actual install program +MTPT : con "/n/remote"; # mount point for user's inferno root + +debug := 0; +force := 0; +exitemu := 0; +uflag := 0; +stderr : ref Sys->FD; +installdir := "/install"; +platform := "Plan9"; +lcplatform : string; +root := "/usr/inferno"; +local: int; +global: int = 1; +waitfd : ref Sys->FD; + +Product : adt { + name : string; + pkgs : ref Package; + nxt : ref Product; +}; + +Package : adt { + name : string; + nxt : ref Package; +}; + +instprods : ref Product; # products/packages already installed + +# platform independent packages +xpkgs := array[] of { "inferno", "utils", "src", "ipaq", "minitel", "sds" }; +ypkgs: list of string; + +init(nil: ref Draw->Context, args: list of string) +{ + sys = load Sys Sys->PATH; + stderr = sys->fildes(2); + + # Hack for network download... + # make sure the dirs we need exist + for (dirix := 0; dirix < len reqdirs; dirix++) { + dir := reqdirs[dirix]; + (exists, nil) := sys->stat(dir); + if (exists == -1) { + fd := sys->create(dir, Sys->OREAD, Sys->DMDIR + 8r7775); + if (fd == nil) + fatal(sys->sprint("cannot create directory %s: %r\n", dir)); + fd = nil; + } + } + + bufio = load Bufio Bufio->PATH; + if(bufio == nil) + fatal(sys->sprint("cannot load %s: %r\n", Bufio->PATH)); + readdir = load Readdir Readdir->PATH; + if(readdir == nil) + fatal(sys->sprint("cannot load %s: %r\n", Readdir->PATH)); + str = load String String->PATH; + if(str == nil) + fatal(sys->sprint("cannot load %s: %r\n", String->PATH)); + arg = load Arg Arg->PATH; + if(arg == nil) + fatal(sys->sprint("cannot load %s: %r\n", Arg->PATH)); + arg->init(args); + while((c := arg->opt()) != 0) { + case c { + 'd' => + debug = 1; + 'F' => + force = 1; + 's' => + exitemu = 1; + 'i' => + installdir = arg->arg(); + if (installdir == nil) + fatal("install directory missing"); + 'p' => + platform = arg->arg(); + if (platform == nil) + fatal("platform missing"); + 'P' => + pkg := arg->arg(); + if (pkg == nil) + fatal("package missing"); + ypkgs = pkg :: ypkgs; + 'r' => + root = arg->arg(); + if (root == nil) + fatal("inferno root missing"); + 'u' => + uflag = 1; + 'g' => + global = 0; + '*' => + usage(); + } + } + if (arg->argv() != nil) + usage(); + lcplatform = str->tolower(platform); + (ok, dir) := sys->stat(installdir); + if (ok < 0) + fatal(sys->sprint("cannot open install directory %s", installdir)); + nt := lcplatform == "nt"; + if (nt) { + # root os of the form ?:/......... + if (len root < 3 || root[1] != ':' || root[2] != '/') + fatal(sys->sprint("root %s not of the form ?:/.......", root)); + spec := root[0:2]; + root = root[2:]; + if (sys->bind("#U"+spec, MTPT, Sys->MREPL|Sys->MCREATE) < 0) + fatal(sys->sprint("cannot bind to drive %s", spec)); + } + else { + if (root[0] != '/') + fatal(sys->sprint("root %s must be an absolute path name", root)); + if (sys->bind("#U*", MTPT, Sys->MREPL|Sys->MCREATE) < 0) + fatal("cannot bind to system root"); + } + (ok, dir) = sys->stat(MTPT+root); + if (ok >= 0) { + if ((dir.mode & Sys->DMDIR) == 0) + fatal(sys->sprint("inferno root %s is not a directory", root)); + } + else if (sys->create(MTPT+root, Sys->OREAD, 8r775 | Sys->DMDIR) == nil) + fatal(sys->sprint("cannot create inferno root %s: %r", root)); + # need a writable tmp directory /tmp in case installing from CD + (ok, dir) = sys->stat(MTPT+root+"/tmp"); + if (ok >= 0) { + if ((dir.mode & Sys->DMDIR) == 0) + fatal(sys->sprint("inferno root tmp %s is not a directory", root+"/tmp")); + } + else if (sys->create(MTPT+root+"/tmp", Sys->OREAD, 8r775 | Sys->DMDIR) == nil) + fatal(sys->sprint("cannot create inferno root tmp %s: %r", root+"/tmp")); + if (sys->bind(MTPT+root, MTPT, Sys->MREPL | Sys->MCREATE) < 0) + fatal("cannot bind inferno root"); + if (sys->bind(MTPT+"/tmp", "/tmp", Sys->MREPL | Sys->MCREATE) < 0) + fatal("cannot bind inferno root tmp"); + root = MTPT; + + if (nt || 1) + local = 1; + else { + sys->print("You can either install software specific to %s only or\n", platform); + sys->print(" install software for all platforms that we support.\n"); + sys->print("If you are unsure what to do, answer yes to the question following.\n"); + sys->print(" You can install the remainder of the software at a later date if desired.\n"); + sys->print("\n"); + b := bufio->fopen(sys->fildes(0), Bufio->OREAD); + if (b == nil) + fatal("cannot open stdin"); + for (;;) { + sys->print("Install software specific to %s only ? (yes/no/quit) ", platform); + resp := getresponse(b); + ans := answer(resp); + if (ans == QUIT) + exit; + else if (ans == ERR) + sys->print("bad response %s\n\n", resp); + else { + local = ans == YES; + break; + } + } + } + instprods = dowraps(root+"/wrap"); + doprods(installdir); + if (!nt) + sys->print("installation complete\n"); + if (exitemu) + shutdown(); +} + +getresponse(b : ref Iobuf) : string +{ + s := b.gets('\n'); + while (s != nil && (s[0] == ' ' || s[0] == '\t')) + s = s[1:]; + while (s != nil && ((c := s[len s - 1]) == ' ' || c == '\t' || c == '\n')) + s = s[0: len s - 1]; + return s; +} + +answer(s : string) : int +{ + s = str->tolower(s); + if (s == "y" || s == "yes") + return YES; + if (s == "n" || s == "no") + return NO; + if (s == "q" || s == "quit") + return QUIT; + return ERR; +} + +usage() +{ + fatal("Usage: install [-d] [-F] [-s] [-u] [-i installdir ] [-p platform ] [-r root]"); +} + +fatal(s : string) +{ + sys->fprint(stderr, "install: %s\n", s); + exit; +} + +dowraps(d : string) : ref Product +{ + p : ref Product; + + # make an inventory of what is already apparently installed + (dir, n) := readdir->init(d, Readdir->NAME|Readdir->COMPACT); + for (i := 0; i < n; i++) { + if (dir[i].mode & Sys->DMDIR) { + p = ref Product(str->tolower(dir[i].name), nil, p); + p.pkgs = dowrap(d + "/" + dir[i].name); + } + } + return p; +} + +dowrap(d : string) : ref Package +{ + p : ref Package; + + (dir, n) := readdir->init(d, Readdir->NAME|Readdir->COMPACT); + for (i := 0; i < n; i++) + p = ref Package(dir[i].name, p); + return p; +} + +doprods(d : string) +{ + (dir, n) := readdir->init(d, Readdir->NAME|Readdir->COMPACT); + for (i := 0; i < n; i++) { + if (dir[i].mode & Sys->DMDIR) + doprod(str->tolower(dir[i].name), d + "/" + dir[i].name); + } +} + +doprod(pr : string, d : string) +{ + # base package, updates and update packages have the name + # <timestamp> or <timestamp.gz> + if (!wanted(pr)) + return; + (dir, n) := readdir->init(d, Readdir->NAME|Readdir->COMPACT); + for (i := 0; i < n; i++) { + pk := dir[i].name; + l := len pk; + if (l >= 4 && pk[l-3:l] == ".gz") + pk = pk[0:l-3]; + else if (l >= 5 && (pk[l-4:] == ".tgz" || pk[l-4:] == ".9gz")) + pk = pk[0:l-4]; + dopkg(pk, pr, d+"/"+dir[i].name); + + } +} + +dopkg(pk : string, pr : string, d : string) +{ + if (!installed(pk, pr)) + install(d); +} + +installed(pkg : string, prd : string) : int +{ + for (pr := instprods; pr != nil; pr = pr.nxt) { + if (pr.name == prd) { + for (pk := pr.pkgs; pk != nil; pk = pk.nxt) { + if (pk.name == pkg) + return 1; + } + return 0; + } + } + return 0; +} + +lookup(pr : string) : int +{ + for (i := 0; i < len xpkgs; i++) { + if (xpkgs[i] == pr) + return i; + } + return -1; +} + +plookup(pr: string): int +{ + for(ps := ypkgs; ps != nil; ps = tl ps) + if(pr == hd ps) + return 1; + return 0; +} + +wanted(pr : string) : int +{ + if (!local || global) + return 1; + if(ypkgs != nil) # overrides everything else + return plookup(pr); + found := lookup(pr); + if (found >= 0) + return 1; + return pr == lcplatform || prefix(lcplatform, pr); +} + +install(d : string) +{ + if (waitfd == nil) + waitfd = openwait(sys->pctl(0, nil)); + sys->fprint(stderr, "installing package %s\n", d); + if (debug) + return; + c := chan of int; + args := "-t" :: "-v" :: "-r" :: root :: d :: nil; + if (uflag) + args = "-u" :: args; + if (force) + args = "-F" :: args; + spawn exec(INST, INST :: args, c); + execpid := <- c; + wait(waitfd, execpid); +} + +exec(cmd : string, argl : list of string, ci : chan of int) +{ + ci <-= sys->pctl(Sys->FORKNS|Sys->NEWFD|Sys->NEWPGRP, 0 :: 1 :: 2 :: stderr.fd :: nil); + file := cmd; + if(len file<4 || file[len file-4:] !=".dis") + file += ".dis"; + c := load Command file; + if(c == nil) { + err := sys->sprint("%r"); + if(file[0] !='/' && file[0:2] !="./") { + c = load Command "/dis/"+file; + if(c == nil) + err = sys->sprint("%r"); + } + if(c == nil) + fatal(sys->sprint("%s: %s\n", cmd, err)); + } + c->init(nil, argl); +} + +openwait(pid : int) : ref Sys->FD +{ + w := sys->sprint("#p/%d/wait", pid); + fd := sys->open(w, Sys->OREAD); + if (fd == nil) + fatal("fd == nil in wait"); + return fd; +} + +wait(wfd : ref Sys->FD, wpid : int) +{ + n : int; + + buf := array[Sys->WAITLEN] of byte; + status := ""; + for(;;) { + if ((n = sys->read(wfd, buf, len buf)) < 0) + fatal("bad read in wait"); + status = string buf[0:n]; + break; + } + if (int status != wpid) + fatal("bad status in wait"); + if(status[len status - 1] != ':') + fatal(sys->sprint("%s\n", status)); +} + +shutdown() +{ + fd := sys->open("/dev/sysctl", sys->OWRITE); + if(fd == nil) + fatal("cannot shutdown emu"); + if (sys->write(fd, array of byte "halt", 4) < 0) + fatal(sys->sprint("shutdown: write failed: %r\n")); +} + +prefix(s, t : string) : int +{ + if (len s <= len t) + return t[0:len s] == s; + return 0; +} |
