1implement Install; 2 3# 4# Determine which packages need installing and calls install/inst 5# to actually install each one 6# 7 8# usage: install/install -d -F -g -s -u -i installdir -p platform -r root -P package 9 10include "sys.m"; 11 sys: Sys; 12include "draw.m"; 13include "bufio.m"; 14 bufio: Bufio; 15 Iobuf: import bufio; 16include "string.m"; 17 str: String; 18include "arg.m"; 19 arg: Arg; 20include "readdir.m"; 21 readdir : Readdir; 22include "sh.m"; 23 24Install: module 25{ 26 init: fn(nil: ref Draw->Context, nil: list of string); 27}; 28 29# required dirs, usually in the standard inferno root. 30# The network download doesn't include them because of 31# problems with versions of tar that won't create empty dirs 32# so we'll make sure they exist. 33 34reqdirs := array [] of { 35 "/mnt", 36 "/mnt/wrap", 37 "/n", 38 "/n/remote", 39 "/tmp", 40}; 41 42YES, NO, QUIT, ERR : con iota; 43INST : con "install/inst"; # actual install program 44MTPT : con "/n/remote"; # mount point for user's inferno root 45 46debug := 0; 47force := 0; 48exitemu := 0; 49uflag := 0; 50stderr : ref Sys->FD; 51installdir := "/install"; 52platform := "Plan9"; 53lcplatform : string; 54root := "/usr/inferno"; 55local: int; 56global: int = 1; 57waitfd : ref Sys->FD; 58 59Product : adt { 60 name : string; 61 pkgs : ref Package; 62 nxt : ref Product; 63}; 64 65Package : adt { 66 name : string; 67 nxt : ref Package; 68}; 69 70instprods : ref Product; # products/packages already installed 71 72# platform independent packages 73xpkgs := array[] of { "inferno", "utils", "src", "ipaq", "minitel", "sds" }; 74ypkgs: list of string; 75 76init(nil: ref Draw->Context, args: list of string) 77{ 78 sys = load Sys Sys->PATH; 79 stderr = sys->fildes(2); 80 81 # Hack for network download... 82 # make sure the dirs we need exist 83 for (dirix := 0; dirix < len reqdirs; dirix++) { 84 dir := reqdirs[dirix]; 85 (exists, nil) := sys->stat(dir); 86 if (exists == -1) { 87 fd := sys->create(dir, Sys->OREAD, Sys->DMDIR + 8r7775); 88 if (fd == nil) 89 fatal(sys->sprint("cannot create directory %s: %r\n", dir)); 90 fd = nil; 91 } 92 } 93 94 bufio = load Bufio Bufio->PATH; 95 if(bufio == nil) 96 fatal(sys->sprint("cannot load %s: %r\n", Bufio->PATH)); 97 readdir = load Readdir Readdir->PATH; 98 if(readdir == nil) 99 fatal(sys->sprint("cannot load %s: %r\n", Readdir->PATH)); 100 str = load String String->PATH; 101 if(str == nil) 102 fatal(sys->sprint("cannot load %s: %r\n", String->PATH)); 103 arg = load Arg Arg->PATH; 104 if(arg == nil) 105 fatal(sys->sprint("cannot load %s: %r\n", Arg->PATH)); 106 arg->init(args); 107 while((c := arg->opt()) != 0) { 108 case c { 109 'd' => 110 debug = 1; 111 'F' => 112 force = 1; 113 's' => 114 exitemu = 1; 115 'i' => 116 installdir = arg->arg(); 117 if (installdir == nil) 118 fatal("install directory missing"); 119 'p' => 120 platform = arg->arg(); 121 if (platform == nil) 122 fatal("platform missing"); 123 'P' => 124 pkg := arg->arg(); 125 if (pkg == nil) 126 fatal("package missing"); 127 ypkgs = pkg :: ypkgs; 128 'r' => 129 root = arg->arg(); 130 if (root == nil) 131 fatal("inferno root missing"); 132 'u' => 133 uflag = 1; 134 'g' => 135 global = 0; 136 '*' => 137 usage(); 138 } 139 } 140 if (arg->argv() != nil) 141 usage(); 142 lcplatform = str->tolower(platform); 143 (ok, dir) := sys->stat(installdir); 144 if (ok < 0) 145 fatal(sys->sprint("cannot open install directory %s", installdir)); 146 nt := lcplatform == "nt"; 147 if (nt) { 148 # root os of the form ?:/......... 149 if (len root < 3 || root[1] != ':' || root[2] != '/') 150 fatal(sys->sprint("root %s not of the form ?:/.......", root)); 151 spec := root[0:2]; 152 root = root[2:]; 153 if (sys->bind("#U"+spec, MTPT, Sys->MREPL|Sys->MCREATE) < 0) 154 fatal(sys->sprint("cannot bind to drive %s", spec)); 155 } 156 else { 157 if (root[0] != '/') 158 fatal(sys->sprint("root %s must be an absolute path name", root)); 159 if (sys->bind("#U*", MTPT, Sys->MREPL|Sys->MCREATE) < 0) 160 fatal("cannot bind to system root"); 161 } 162 (ok, dir) = sys->stat(MTPT+root); 163 if (ok >= 0) { 164 if ((dir.mode & Sys->DMDIR) == 0) 165 fatal(sys->sprint("inferno root %s is not a directory", root)); 166 } 167 else if (sys->create(MTPT+root, Sys->OREAD, 8r775 | Sys->DMDIR) == nil) 168 fatal(sys->sprint("cannot create inferno root %s: %r", root)); 169 # need a writable tmp directory /tmp in case installing from CD 170 (ok, dir) = sys->stat(MTPT+root+"/tmp"); 171 if (ok >= 0) { 172 if ((dir.mode & Sys->DMDIR) == 0) 173 fatal(sys->sprint("inferno root tmp %s is not a directory", root+"/tmp")); 174 } 175 else if (sys->create(MTPT+root+"/tmp", Sys->OREAD, 8r775 | Sys->DMDIR) == nil) 176 fatal(sys->sprint("cannot create inferno root tmp %s: %r", root+"/tmp")); 177 if (sys->bind(MTPT+root, MTPT, Sys->MREPL | Sys->MCREATE) < 0) 178 fatal("cannot bind inferno root"); 179 if (sys->bind(MTPT+"/tmp", "/tmp", Sys->MREPL | Sys->MCREATE) < 0) 180 fatal("cannot bind inferno root tmp"); 181 root = MTPT; 182 183 if (nt || 1) 184 local = 1; 185 else { 186 sys->print("You can either install software specific to %s only or\n", platform); 187 sys->print(" install software for all platforms that we support.\n"); 188 sys->print("If you are unsure what to do, answer yes to the question following.\n"); 189 sys->print(" You can install the remainder of the software at a later date if desired.\n"); 190 sys->print("\n"); 191 b := bufio->fopen(sys->fildes(0), Bufio->OREAD); 192 if (b == nil) 193 fatal("cannot open stdin"); 194 for (;;) { 195 sys->print("Install software specific to %s only ? (yes/no/quit) ", platform); 196 resp := getresponse(b); 197 ans := answer(resp); 198 if (ans == QUIT) 199 exit; 200 else if (ans == ERR) 201 sys->print("bad response %s\n\n", resp); 202 else { 203 local = ans == YES; 204 break; 205 } 206 } 207 } 208 instprods = dowraps(root+"/wrap"); 209 doprods(installdir); 210 if (!nt) 211 sys->print("installation complete\n"); 212 if (exitemu) 213 shutdown(); 214} 215 216getresponse(b : ref Iobuf) : string 217{ 218 s := b.gets('\n'); 219 while (s != nil && (s[0] == ' ' || s[0] == '\t')) 220 s = s[1:]; 221 while (s != nil && ((c := s[len s - 1]) == ' ' || c == '\t' || c == '\n')) 222 s = s[0: len s - 1]; 223 return s; 224} 225 226answer(s : string) : int 227{ 228 s = str->tolower(s); 229 if (s == "y" || s == "yes") 230 return YES; 231 if (s == "n" || s == "no") 232 return NO; 233 if (s == "q" || s == "quit") 234 return QUIT; 235 return ERR; 236} 237 238usage() 239{ 240 fatal("Usage: install [-d] [-F] [-s] [-u] [-i installdir ] [-p platform ] [-r root]"); 241} 242 243fatal(s : string) 244{ 245 sys->fprint(stderr, "install: %s\n", s); 246 exit; 247} 248 249dowraps(d : string) : ref Product 250{ 251 p : ref Product; 252 253 # make an inventory of what is already apparently installed 254 (dir, n) := readdir->init(d, Readdir->NAME|Readdir->COMPACT); 255 for (i := 0; i < n; i++) { 256 if (dir[i].mode & Sys->DMDIR) { 257 p = ref Product(str->tolower(dir[i].name), nil, p); 258 p.pkgs = dowrap(d + "/" + dir[i].name); 259 } 260 } 261 return p; 262} 263 264dowrap(d : string) : ref Package 265{ 266 p : ref Package; 267 268 (dir, n) := readdir->init(d, Readdir->NAME|Readdir->COMPACT); 269 for (i := 0; i < n; i++) 270 p = ref Package(dir[i].name, p); 271 return p; 272} 273 274doprods(d : string) 275{ 276 (dir, n) := readdir->init(d, Readdir->NAME|Readdir->COMPACT); 277 for (i := 0; i < n; i++) { 278 if (dir[i].mode & Sys->DMDIR) 279 doprod(str->tolower(dir[i].name), d + "/" + dir[i].name); 280 } 281} 282 283doprod(pr : string, d : string) 284{ 285 # base package, updates and update packages have the name 286 # <timestamp> or <timestamp.gz> 287 if (!wanted(pr)) 288 return; 289 (dir, n) := readdir->init(d, Readdir->NAME|Readdir->COMPACT); 290 for (i := 0; i < n; i++) { 291 pk := dir[i].name; 292 l := len pk; 293 if (l >= 4 && pk[l-3:l] == ".gz") 294 pk = pk[0:l-3]; 295 else if (l >= 5 && (pk[l-4:] == ".tgz" || pk[l-4:] == ".9gz")) 296 pk = pk[0:l-4]; 297 dopkg(pk, pr, d+"/"+dir[i].name); 298 299 } 300} 301 302dopkg(pk : string, pr : string, d : string) 303{ 304 if (!installed(pk, pr)) 305 install(d); 306} 307 308installed(pkg : string, prd : string) : int 309{ 310 for (pr := instprods; pr != nil; pr = pr.nxt) { 311 if (pr.name == prd) { 312 for (pk := pr.pkgs; pk != nil; pk = pk.nxt) { 313 if (pk.name == pkg) 314 return 1; 315 } 316 return 0; 317 } 318 } 319 return 0; 320} 321 322lookup(pr : string) : int 323{ 324 for (i := 0; i < len xpkgs; i++) { 325 if (xpkgs[i] == pr) 326 return i; 327 } 328 return -1; 329} 330 331plookup(pr: string): int 332{ 333 for(ps := ypkgs; ps != nil; ps = tl ps) 334 if(pr == hd ps) 335 return 1; 336 return 0; 337} 338 339wanted(pr : string) : int 340{ 341 if (!local || global) 342 return 1; 343 if(ypkgs != nil) # overrides everything else 344 return plookup(pr); 345 found := lookup(pr); 346 if (found >= 0) 347 return 1; 348 return pr == lcplatform || prefix(lcplatform, pr); 349} 350 351install(d : string) 352{ 353 if (waitfd == nil) 354 waitfd = openwait(sys->pctl(0, nil)); 355 sys->fprint(stderr, "installing package %s\n", d); 356 if (debug) 357 return; 358 c := chan of int; 359 args := "-t" :: "-v" :: "-r" :: root :: d :: nil; 360 if (uflag) 361 args = "-u" :: args; 362 if (force) 363 args = "-F" :: args; 364 spawn exec(INST, INST :: args, c); 365 execpid := <- c; 366 wait(waitfd, execpid); 367} 368 369exec(cmd : string, argl : list of string, ci : chan of int) 370{ 371 ci <-= sys->pctl(Sys->FORKNS|Sys->NEWFD|Sys->NEWPGRP, 0 :: 1 :: 2 :: stderr.fd :: nil); 372 file := cmd; 373 if(len file<4 || file[len file-4:] !=".dis") 374 file += ".dis"; 375 c := load Command file; 376 if(c == nil) { 377 err := sys->sprint("%r"); 378 if(file[0] !='/' && file[0:2] !="./") { 379 c = load Command "/dis/"+file; 380 if(c == nil) 381 err = sys->sprint("%r"); 382 } 383 if(c == nil) 384 fatal(sys->sprint("%s: %s\n", cmd, err)); 385 } 386 c->init(nil, argl); 387} 388 389openwait(pid : int) : ref Sys->FD 390{ 391 w := sys->sprint("#p/%d/wait", pid); 392 fd := sys->open(w, Sys->OREAD); 393 if (fd == nil) 394 fatal("fd == nil in wait"); 395 return fd; 396} 397 398wait(wfd : ref Sys->FD, wpid : int) 399{ 400 n : int; 401 402 buf := array[Sys->WAITLEN] of byte; 403 status := ""; 404 for(;;) { 405 if ((n = sys->read(wfd, buf, len buf)) < 0) 406 fatal("bad read in wait"); 407 status = string buf[0:n]; 408 break; 409 } 410 if (int status != wpid) 411 fatal("bad status in wait"); 412 if(status[len status - 1] != ':') 413 fatal(sys->sprint("%s\n", status)); 414} 415 416shutdown() 417{ 418 fd := sys->open("/dev/sysctl", sys->OWRITE); 419 if(fd == nil) 420 fatal("cannot shutdown emu"); 421 if (sys->write(fd, array of byte "halt", 4) < 0) 422 fatal(sys->sprint("shutdown: write failed: %r\n")); 423} 424 425prefix(s, t : string) : int 426{ 427 if (len s <= len t) 428 return t[0:len s] == s; 429 return 0; 430} 431