1implement Inst; 2 3include "sys.m"; 4 sys: Sys; 5 Dir, sprint, fprint: import sys; 6include "draw.m"; 7include "bufio.m"; 8 bufio: Bufio; 9 Iobuf: import bufio; 10include "string.m"; 11 str: String; 12include "arg.m"; 13 arg: Arg; 14include "keyring.m"; 15 keyring : Keyring; 16include "arch.m"; 17 arch : Arch; 18include "wrap.m"; 19 wrap : Wrap; 20 21Inst: module 22{ 23 init: fn(nil: ref Draw->Context, nil: list of string); 24}; 25 26LEN: con Sys->ATOMICIO; 27 28tflag := 0; 29uflag := 0; 30hflag := 0; 31vflag := 0; 32fflag := 1; 33stderr: ref Sys->FD; 34bout: ref Iobuf; 35argv0 := "inst"; 36oldw, w : ref Wrap->Wrapped; 37root := "/"; 38force := 0; 39stoponerr := 1; 40 41# membogus(argv: list of string) 42# { 43# 44# } 45 46init(nil: ref Draw->Context, args: list of string) 47{ 48 sys = load Sys Sys->PATH; 49 stderr = sys->fildes(2); 50 bufio = load Bufio Bufio->PATH; 51 if(bufio == nil) 52 error(sys->sprint("cannot load %s: %r\n", Bufio->PATH)); 53 54 str = load String String->PATH; 55 if(str == nil) 56 error(sys->sprint("cannot load %s: %r\n", String->PATH)); 57 58 arg = load Arg Arg->PATH; 59 if(arg == nil) 60 error(sys->sprint("cannot load %s: %r\n", Arg->PATH)); 61 keyring = load Keyring Keyring->PATH; 62 if(keyring == nil) 63 error(sys->sprint("cannot load %s: %r\n", Keyring->PATH)); 64 arch = load Arch Arch->PATH; 65 if(arch == nil) 66 error(sys->sprint("cannot load %s: %r\n", Arch->PATH)); 67 arch->init(bufio); 68 wrap = load Wrap Wrap->PATH; 69 if(wrap == nil) 70 error(sys->sprint("cannot load %s: %r\n", Wrap->PATH)); 71 wrap->init(bufio); 72 arg->init(args); 73 while((c := arg->opt()) != 0) 74 case c { 75 'f' => 76 fflag = 0; 77 'h' => 78 hflag = 1; 79 bout = bufio->fopen(sys->fildes(1), Sys->OWRITE); 80 if(bout == nil) 81 error(sys->sprint("can't access standard output: %r")); 82 't' => 83 tflag = 1; 84 'u' => 85 uflag = 1; 86 'v' => 87 vflag = 1; 88 'r' => 89 root = arg->arg(); 90 if (root == nil) 91 fatal("root missing"); 92 'F' => 93 force = 1; 94 'c' => 95 stoponerr = 0; 96 * => 97 usage(); 98 } 99 args = arg->argv(); 100 if (args == nil) 101 usage(); 102 ar := arch->openarch(hd args); 103 if(ar == nil || ar.b == nil) 104 error(sys->sprint("can't access %s: %r", hd args)); 105 w = wrap->openwraphdr(hd args, root, nil, 0); 106 if (w == nil) 107 fatal("no such package found"); 108 if(w.nu != 1) 109 fatal("strange package: more than one piece"); 110 if (force == 0) 111 oldw = wrap->openwrap(w.name, root, 0); 112 if (force == 0 && w.u[0].utime && (oldw == nil || oldw.tfull < w.u[0].utime)){ 113 tfull: int; 114 if(oldw == nil) 115 tfull = -1; 116 else 117 tfull = oldw.tfull; 118 fatal(sys->sprint("need %s version of %s already installed (pkg %d)", wrap->now2string(w.u[0].utime, 0), w.name, tfull)); 119 } 120 args = tl args; 121 digest := array[Keyring->MD5dlen] of byte; 122 digest0 := array[Keyring->MD5dlen] of byte; 123 digest1 := array[Keyring->MD5dlen] of byte; 124 125 while ((a := arch->gethdr(ar)) != nil) { 126 why := ""; 127 docopy := 0; 128 if(force) 129 docopy = 1; 130 else if(a.d.mode & Sys->DMDIR) 131 docopy = 1; 132 else if(wrap->md5file(root+a.name, digest) < 0) 133 docopy = 1; 134 else{ 135 wrap->md5filea(root+a.name, digest1); 136 (ok, t) := wrap->getfileinfo(oldw, a.name, digest, nil, digest1); 137 if (ok >= 0) { 138 if(t > w.u[0].time){ 139 docopy = 0; 140 why = "version from newer package exists"; 141 } 142 else 143 docopy = 1; 144 } 145 else { 146 (ok, t) = wrap->getfileinfo(oldw, a.name, nil, nil, nil); 147 if(ok >= 0){ 148 docopy = 0; 149 why = "locally modified"; 150 } 151 else{ 152 docopy = 0; 153 why = "locally created"; 154 } 155 } 156 } 157 if(!docopy){ 158 wrap->md5sum(ar.b, digest0, int a.d.length); 159 if(wrap->memcmp(digest, digest0, Keyring->MD5dlen)) 160 skipfile(a.name, why); 161 continue; 162 } 163 if(args != nil){ 164 if(!selected(a.name, args)){ 165 arch->drain(ar, int a.d.length); 166 continue; 167 } 168 if (!hflag) 169 mkdirs(root, a.name); 170 } 171 name := pathcat(root, a.name); 172 if(hflag){ 173 bout.puts(sys->sprint("%s %uo %s %s %ud %d\n", 174 name, a.d.mode, a.d.uid, a.d.gid, a.d.mtime, int a.d.length)); 175 arch->drain(ar, int a.d.length); 176 continue; 177 } 178 if(a.d.mode & Sys->DMDIR) 179 mkdir(name, a.d); 180 else 181 extract(ar, name, a.d); 182 } 183 arch->closearch(ar); 184 if(ar.err == nil){ 185 # fprint(stderr, "done\n"); 186 quit(nil); 187 } 188 else { 189 fprint(stderr, "%s\n", ar.err); 190 quit("eof"); 191 } 192} 193 194skipfile(f : string, why : string) 195{ 196 sys->fprint(stderr, "skipping %s: %s\n", f, why); 197} 198 199skiprmfile(f: string, why: string) 200{ 201 sys->fprint(stderr, "not removing %s: %s\n", f, why); 202} 203 204doremove(s : string) 205{ 206 p := pathcat(root, s); 207 digest := array[Keyring->MD5dlen] of { * => byte 0 }; 208 digest1 := array[Keyring->MD5dlen] of { * => byte 0 }; 209 if(wrap->md5file(p, digest) < 0) 210 ; 211 else{ 212 wrap->md5filea(p, digest1); 213 (ok, nil) := wrap->getfileinfo(oldw, s, digest, nil, digest1); 214 if(force == 0 && ok < 0) 215 skiprmfile(p, "locally modified"); 216 else{ 217 if (vflag) 218 sys->print("rm %s\n", p); 219 remove(p); 220 } 221 } 222} 223 224quit(s: string) 225{ 226 if (s == nil) { 227 p := w.u[0].dir + "/remove"; 228 if ((b := bufio->open(p, Bufio->OREAD)) != nil) { 229 while ((t := b.gets('\n')) != nil) { 230 lt := len t; 231 if (t[lt-1] == '\n') 232 t = t[0:lt-1]; 233 doremove(t); 234 } 235 } 236 } 237 if(bout != nil) 238 bout.flush(); 239 if(wrap != nil) 240 wrap->end(); 241 if(s != nil) 242 raise "fail: "+s; 243 else 244 fprint(stderr, "done\n"); 245 exit; 246} 247 248fileprefix(prefix, s: string): int 249{ 250 n := len prefix; 251 m := len s; 252 if(n > m || !str->prefix(prefix, s)) 253 return 0; 254 if(m > n && s[n] != '/') 255 return 0; 256 return 1; 257} 258 259selected(s: string, args: list of string): int 260{ 261 for(; args != nil; args = tl args) 262 if(fileprefix(hd args, s)) 263 return 1; 264 return 0; 265} 266 267mkdirs(basedir, name: string) 268{ 269 (nil, names) := sys->tokenize(name, "/"); 270 while(names != nil) { 271 create(basedir, Sys->OREAD, 8r775|Sys->DMDIR); 272 if(tl names == nil) 273 break; 274 basedir = basedir + "/" + hd names; 275 names = tl names; 276 } 277} 278 279mkdir(name: string, dir : ref Sys->Dir) 280{ 281 d: Dir; 282 i: int; 283 284 if(vflag) { 285 MTPT : con "/n/remote"; 286 s := name; 287 if (len name >= len MTPT && name[0:len MTPT] == MTPT) 288 s = name[len MTPT:]; 289 sys->print("installing directory %s\n", s); 290 } 291 fd := create(name, Sys->OREAD, dir.mode); 292 if(fd == nil) { 293 err := sys->sprint("%r"); 294 (i, d) = sys->stat(name); 295 if(i < 0 || !(d.mode & Sys->DMDIR)){ 296 werr(sys->sprint("can't make directory %s: %s", name, err)); 297 return; 298 } 299 } 300 else { 301 (i, d) = sys->fstat(fd); 302 if(i < 0) 303 warn(sys->sprint("can't stat %s: %r", name)); 304 fd = nil; 305 } 306 d = sys->nulldir; 307 (nil, p) := str->splitr(name, "/"); 308 if(p == nil) 309 p = name; 310 d.name = p; 311 d.mode = dir.mode; 312 if(tflag || uflag) 313 d.mtime = dir.mtime; 314 if(uflag){ 315 d.uid = dir.uid; 316 d.gid = dir.gid; 317 } 318 fd = nil; 319 if(sys->wstat(name, d) < 0){ 320 e := sys->sprint("%r"); 321 if(wstat(name, d) < 0) 322 warn(sys->sprint("can't set modes for %s: %s", name, e)); 323 } 324 if(uflag){ 325 (i, d) = sys->stat(name); 326 if(i < 0) 327 warn(sys->sprint("can't reread modes for %s: %r", name)); 328 if(dir.uid != d.uid) 329 warn(sys->sprint("%s: uid mismatch %s %s", name, dir.uid, d.uid)); 330 if(dir.gid != d.gid) 331 warn(sys->sprint("%s: gid mismatch %s %s", name, dir.gid, d.gid)); 332 } 333} 334 335extract(ar : ref Arch->Archive, name: string, dir : ref Sys->Dir) 336{ 337 sfd := create(name, Sys->OWRITE, dir.mode); 338 if(sfd == nil) { 339 if(!fflag || remove(name) == -1 || 340 (sfd = create(name, Sys->OWRITE, dir.mode)) == nil) { 341 werr(sys->sprint("can't make file %s: %r", name)); 342 arch->drain(ar, int dir.length); 343 return; 344 } 345 } 346 b := bufio->fopen(sfd, Bufio->OWRITE); 347 if (b == nil) { 348 warn(sys->sprint("can't open file %s for bufio : %r", name)); 349 arch->drain(ar, int dir.length); 350 return; 351 } 352 err := arch->getfile(ar, b, int dir.length); 353 if (err != nil) { 354 if (len err >= 9 && err[0:9] == "premature") 355 fatal(err); 356 else 357 warn(err); 358 } 359 (i, d) := sys->fstat(b.fd); 360 if(i < 0) 361 warn(sys->sprint("can't stat %s: %r", name)); 362 d = sys->nulldir; 363 (nil, p) := str->splitr(name, "/"); 364 if(p == nil) 365 p = name; 366 d.name = p; 367 d.mode = dir.mode; 368 if(tflag || uflag) 369 d.mtime = dir.mtime; 370 if(uflag){ 371 d.uid = dir.uid; 372 d.gid = dir.gid; 373 } 374 if(b.flush() == Bufio->ERROR) 375 werr(sys->sprint("error writing %s: %r", name)); 376 b.close(); 377 sfd = nil; 378 if(sys->wstat(name, d) < 0){ 379 e := sys->sprint("%r"); 380 if(wstat(name, d) < 0) 381 warn(sys->sprint("can't set modes for %s: %s", name, e)); 382 } 383 if(uflag){ 384 (i, d) = sys->stat(name); 385 if(i < 0) 386 warn(sys->sprint("can't reread modes for %s: %r", name)); 387 if(d.uid != dir.uid) 388 warn(sys->sprint("%s: uid mismatch %s %s", name, dir.uid, d.uid)); 389 if(d.gid != dir.gid) 390 warn(sys->sprint("%s: gid mismatch %s %s", name, dir.gid, d.gid)); 391 } 392} 393 394error(s: string) 395{ 396 fprint(stderr, "%s: %s\n", argv0, s); 397 quit("error"); 398} 399 400werr(s: string) 401{ 402 fprint(stderr, "%s: %s\n", argv0, s); 403 if(stoponerr) 404 quit("werr"); 405} 406 407warn(s: string) 408{ 409 fprint(stderr, "%s: %s\n", argv0, s); 410} 411 412usage() 413{ 414 fprint(stderr, "Usage: inst [-h] [-u] [-v] [-f] [-c] [-F] [-r dest-root] [file ...]\n"); 415 raise "fail: usage"; 416} 417 418fatal(s : string) 419{ 420 sys->fprint(stderr, "inst: %s\n", s); 421 if(wrap != nil) 422 wrap->end(); 423 exit; 424} 425 426parent(name : string) : string 427{ 428 slash := -1; 429 for (i := 0; i < len name; i++) 430 if (name[i] == '/') 431 slash = i; 432 if (slash > 0) 433 return name[0:slash]; 434 return "/"; 435} 436 437create(name : string, rw : int, mode : int) : ref Sys->FD 438{ 439 fd := sys->create(name, rw, mode); 440 if (fd == nil) { 441 p := parent(name); 442 (ok, d) := sys->stat(p); 443 if (ok < 0) 444 return nil; 445 omode := d.mode; 446 d = sys->nulldir; 447 d.mode = omode | 8r222; # ensure parent is writable 448 sys->wstat(p, d); 449 fd = sys->create(name, rw, mode); 450 d.mode = omode; 451 sys->wstat(p, d); 452 } 453 return fd; 454} 455 456remove(name : string) : int 457{ 458 if (sys->remove(name) < 0) { 459 (ok, d) := sys->stat(name); 460 if (ok < 0) 461 return -1; 462 omode := d.mode; 463 d.mode |= 8r222; 464 sys->wstat(name, d); 465 if (sys->remove(name) >= 0) 466 return 0; 467 d.mode = omode; 468 sys->wstat(name, d); 469 return -1; 470 } 471 return 0; 472} 473 474wstat(name : string, d : Dir) : int 475{ 476 (ok, dir) := sys->stat(name); 477 if (ok < 0) 478 return -1; 479 omode := dir.mode; 480 dir.mode |= 8r222; 481 sys->wstat(name, dir); 482 if (sys->wstat(name, d) >= 0) 483 return 0; 484 dir.mode = omode; 485 sys->wstat(name, dir); 486 return -1; 487} 488 489pathcat(s : string, t : string) : string 490{ 491 if (s == nil) return t; 492 if (t == nil) return s; 493 slashs := s[len s - 1] == '/'; 494 slasht := t[0] == '/'; 495 if (slashs && slasht) 496 return s + t[1:]; 497 if (!slashs && !slasht) 498 return s + "/" + t; 499 return s + t; 500} 501