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