1implement Cs; 2 3# 4# Connection server translates net!machine!service into 5# /net/tcp/clone 135.104.9.53!564 6# 7 8include "sys.m"; 9 sys: Sys; 10 11include "draw.m"; 12 13include "srv.m"; 14 srv: Srv; 15 16include "bufio.m"; 17include "attrdb.m"; 18 attrdb: Attrdb; 19 Attr, Db, Dbentry, Tuples: import attrdb; 20 21include "ip.m"; 22 ip: IP; 23include "ipattr.m"; 24 ipattr: IPattr; 25 26include "arg.m"; 27 28Cs: module 29{ 30 init: fn(nil: ref Draw->Context, nil: list of string); 31}; 32 33# signature of dial-on-demand module 34CSdial: module 35{ 36 init: fn(nil: ref Draw->Context): string; 37 connect: fn(): string; 38}; 39 40Reply: adt 41{ 42 fid: int; 43 pid: int; 44 addrs: list of string; 45 err: string; 46}; 47 48Cached: adt 49{ 50 expire: int; 51 query: string; 52 addrs: list of string; 53}; 54 55Ncache: con 16; 56cache:= array[Ncache] of ref Cached; 57nextcache := 0; 58 59rlist: list of ref Reply; 60 61ndbfile := "/lib/ndb/local"; 62ndb: ref Db; 63mntpt := "/net"; 64myname: string; 65 66stderr: ref Sys->FD; 67 68verbose := 0; 69dialmod: CSdial; 70 71init(ctxt: ref Draw->Context, args: list of string) 72{ 73 sys = load Sys Sys->PATH; 74 stderr = sys->fildes(2); 75 attrdb = load Attrdb Attrdb->PATH; 76 if(attrdb == nil) 77 cantload(Attrdb->PATH); 78 attrdb->init(); 79 ip = load IP IP->PATH; 80 if(ip == nil) 81 cantload(IP->PATH); 82 ip->init(); 83 ipattr = load IPattr IPattr->PATH; 84 if(ipattr == nil) 85 cantload(IPattr->PATH); 86 ipattr->init(attrdb, ip); 87 88 svcname := "#scs"; 89 arg := load Arg Arg->PATH; 90 if (arg == nil) 91 cantload(Arg->PATH); 92 arg->init(args); 93 arg->setusage("cs [-v] [-x mntpt] [-f database] [-d dialmod]"); 94 while((c := arg->opt()) != 0) 95 case c { 96 'v' or 'D' => 97 verbose++; 98 'd' => # undocumented hack to replace svc/cs/cs 99 f := arg->arg(); 100 if(f != nil){ 101 dialmod = load CSdial f; 102 if(dialmod == nil) 103 cantload(f); 104 } 105 'f' => 106 ndbfile = arg->earg(); 107 'x' => 108 mntpt = arg->earg(); 109 svcname = "#scs"+svcpt(mntpt); 110 * => 111 arg->usage(); 112 } 113 114 if(arg->argv() != nil) 115 arg->usage(); 116 arg = nil; 117 118 srv = load Srv Srv->PATH; # hosted Inferno only 119 if(srv != nil) 120 srv->init(); 121 122 sys->remove(svcname+"/cs"); 123 sys->unmount(svcname, mntpt); 124 publish(svcname); 125 if(sys->bind(svcname, mntpt, Sys->MBEFORE) < 0) 126 error(sys->sprint("can't bind #s on %s: %r", mntpt)); 127 file := sys->file2chan(mntpt, "cs"); 128 if(file == nil) 129 error(sys->sprint("can't make %s/cs: %r", mntpt)); 130 sys->pctl(Sys->FORKFD|Sys->NEWPGRP, nil); 131 refresh(); 132 if(dialmod != nil){ 133 e := dialmod->init(ctxt); 134 if(e != nil) 135 error(sys->sprint("can't initialise dial-on-demand: %s", e)); 136 } 137 spawn cs(file); 138} 139 140svcpt(s: string): string 141{ 142 for(i:=0; i<len s; i++) 143 if(s[i] == '/') 144 s[i] = '_'; 145 return s; 146} 147 148publish(dir: string) 149{ 150 d := Sys->nulldir; 151 d.mode = 8r777; 152 if(sys->wstat(dir, d) < 0) 153 sys->fprint(sys->fildes(2), "cs: can't publish %s: %r\n", dir); 154} 155 156cantload(m: string) 157{ 158 error(sys->sprint("cannot load %s: %r", m)); 159} 160 161error(s: string) 162{ 163 sys->fprint(sys->fildes(2), "cs: %s\n", s); 164 raise "fail:error"; 165} 166 167refresh() 168{ 169 myname = sysname(); 170 if(ndb == nil){ 171 ndb2 := Db.open(ndbfile); 172 if(ndb2 == nil){ 173 err := sys->sprint("%r"); 174 ndb2 = Db.open("/lib/ndb/inferno"); # try to get service map at least 175 if(ndb2 == nil) 176 sys->fprint(sys->fildes(2), "cs: warning: can't open %s: %s\n", ndbfile, err); # continue without it 177 } 178 ndb = Db.open(mntpt+"/ndb"); 179 if(ndb != nil) 180 ndb = ndb.append(ndb2); 181 else 182 ndb = ndb2; 183 }else 184 ndb.reopen(); 185} 186 187sysname(): string 188{ 189 t := rf("/dev/sysname"); 190 if(t != nil) 191 return t; 192 t = rf("#e/sysname"); 193 if(t == nil){ 194 s := rf(mntpt+"/ndb"); 195 if(s != nil){ 196 db := Db.sopen(s); 197 if(db != nil){ 198 (e, nil) := db.find(nil, "sys"); 199 if(e != nil) 200 t = e.findfirst("sys"); 201 } 202 } 203 } 204 if(t != nil){ 205 fd := sys->open("/dev/sysname", Sys->OWRITE); 206 if(fd != nil) 207 sys->fprint(fd, "%s", t); 208 } 209 return t; 210} 211 212rf(name: string): string 213{ 214 fd := sys->open(name, Sys->OREAD); 215 buf := array[512] of byte; 216 n := sys->read(fd, buf, len buf); 217 if(n <= 0) 218 return nil; 219 return string buf[0:n]; 220} 221 222cs(file: ref Sys->FileIO) 223{ 224 pidc := chan of int; 225 donec := chan of ref Reply; 226 for (;;) { 227 alt { 228 (nil, buf, fid, wc) := <-file.write => 229 cleanfid(fid); # each write cancels previous requests 230 if(dialmod != nil){ 231 e := dialmod->connect(); 232 if(e != nil){ 233 if(len e > 5 && e[0:5]=="fail:") 234 e = e[5:]; 235 if(e == "") 236 e = "unknown error"; 237 wc <-= (0, "cs: dial on demand: "+e); 238 break; 239 } 240 } 241 if(wc != nil){ 242 nbytes := len buf; 243 query := string buf; 244 if(query == "refresh"){ 245 refresh(); 246 wc <-= (nbytes, nil); 247 break; 248 } 249 now := time(); 250 r := ref Reply; 251 r.fid = fid; 252 spawn request(r, query, nbytes, now, wc, pidc, donec); 253 r.pid = <-pidc; 254 rlist = r :: rlist; 255 } 256 257 (off, nbytes, fid, rc) := <-file.read => 258 if(rc != nil){ 259 r := findfid(fid); 260 if(r != nil) 261 reply(r, off, nbytes, rc); 262 else 263 rc <-= (nil, "unknown request"); 264 } else 265 ; # cleanfid(fid); # compensate for csendq in file2chan 266 267 r := <-donec => 268 r.pid = 0; 269 } 270 } 271} 272 273findfid(fid: int): ref Reply 274{ 275 for(rl := rlist; rl != nil; rl = tl rl){ 276 r := hd rl; 277 if(r.fid == fid) 278 return r; 279 } 280 return nil; 281} 282 283cleanfid(fid: int) 284{ 285 rl := rlist; 286 rlist = nil; 287 for(; rl != nil; rl = tl rl){ 288 r := hd rl; 289 if(r.fid != fid) 290 rlist = r :: rlist; 291 else 292 killgrp(r.pid); 293 } 294} 295 296killgrp(pid: int) 297{ 298 if(pid != 0){ 299 fd := sys->open("#p/"+string pid+"/ctl", Sys->OWRITE); 300 if(fd == nil || sys->fprint(fd, "killgrp") < 0) 301 sys->fprint(stderr, "cs: can't killgrp %d: %r\n", pid); 302 } 303} 304 305request(r: ref Reply, query: string, nbytes: int, now: int, wc: chan of (int, string), pidc: chan of int, donec: chan of ref Reply) 306{ 307 pidc <-= sys->pctl(Sys->NEWPGRP, nil); 308 if(query != nil && query[0] == '!'){ 309 # general query 310 (r.addrs, r.err) = genquery(query[1:]); 311 }else{ 312 (r.addrs, r.err) = xlate(query, now); 313 if(r.addrs == nil && r.err == nil) 314 r.err = "cs: can't translate address"; 315 } 316 if(r.err != nil){ 317 if(verbose) 318 sys->fprint(stderr, "cs: %s: %s\n", query, r.err); 319 wc <-= (0, r.err); 320 } else 321 wc <-= (nbytes, nil); 322 donec <-= r; 323} 324 325reply(r: ref Reply, off: int, nbytes: int, rc: chan of (array of byte, string)) 326{ 327 if(r.err != nil){ 328 rc <-= (nil, r.err); 329 return; 330 } 331 addr: string = nil; 332 if(r.addrs != nil){ 333 addr = hd r.addrs; 334 r.addrs = tl r.addrs; 335 } 336 off = 0; # this version ignores offset 337 rc <-= reads(addr, off, nbytes); 338} 339 340# 341# return the file2chan reply for a read of the given string 342# 343reads(str: string, off, nbytes: int): (array of byte, string) 344{ 345 bstr := array of byte str; 346 slen := len bstr; 347 if(off < 0 || off >= slen) 348 return (nil, nil); 349 if(off + nbytes > slen) 350 nbytes = slen - off; 351 if(nbytes <= 0) 352 return (nil, nil); 353 return (bstr[off:off+nbytes], nil); 354} 355 356lookcache(query: string, now: int): ref Cached 357{ 358 for(i:=0; i<len cache; i++){ 359 c := cache[i]; 360 if(c != nil && c.query == query && now < c.expire){ 361 if(verbose) 362 sys->print("cache: %s -> %s\n", query, hd c.addrs); 363 return c; 364 } 365 } 366 return nil; 367} 368 369putcache(query: string, addrs: list of string, now: int) 370{ 371 ce := ref Cached; 372 ce.expire = now+120; 373 ce.query = query; 374 ce.addrs = addrs; 375 cache[nextcache] = ce; 376 nextcache = (nextcache+1)%Ncache; 377} 378 379xlate(address: string, now: int): (list of string, string) 380{ 381 n: int; 382 l, rl, results: list of string; 383 repl, netw, mach, service: string; 384 385 ce := lookcache(address, now); 386 if(ce != nil && ce.addrs != nil) 387 return (ce.addrs, nil); 388 389 (n, l) = sys->tokenize(address, "!\n"); 390 if(n < 2) 391 return (nil, "bad format request"); 392 393 netw = hd l; 394 if(netw == "net") 395 netw = "tcp"; # TO DO: better (needs lib/ndb) 396 if(!isnetwork(netw)) 397 return (nil, "network unavailable "+netw); 398 l = tl l; 399 400 if(!isipnet(netw)) { 401 repl = mntpt + "/" + netw + "/clone "; 402 for(;;){ 403 repl += hd l; 404 if((l = tl l) == nil) 405 break; 406 repl += "!"; 407 } 408 return (repl :: nil, nil); # no need to cache 409 } 410 411 if(n != 3) 412 return (nil, "bad format request"); 413 mach = hd l; 414 service = hd tl l; 415 416 if(!isnumeric(service)) { 417 s := xlatesvc(netw, service); 418 if(s == nil){ 419 if(srv != nil) 420 s = srv->ipn2p(netw, service); 421 if(s == nil) 422 return (nil, "cs: can't translate service"); 423 } 424 service = s; 425 } 426 427 attr := ipattr->dbattr(mach); 428 if(mach == "*") 429 l = "" :: nil; 430 else if(attr != "ip") { 431 # Symbolic server == "$SVC" 432 if(mach[0] == '$' && len mach > 1 && ndb != nil){ 433 (s, nil) := ipattr->findnetattr(ndb, "sys", myname, mach[1:]); 434 if(s == nil){ 435 names := dblook("infernosite", "", mach[1:]); 436 if(names == nil) 437 return (nil, "cs: can't translate "+mach); 438 s = hd names; 439 } 440 mach = s; 441 attr = ipattr->dbattr(mach); 442 } 443 if(attr == "sys"){ 444 results = dblook("sys", mach, "ip"); 445 if(results != nil) 446 attr = "ip"; 447 } 448 if(attr != "ip"){ 449 err: string; 450 (results, err) = querydns(mach, "ip"); 451 if(err != nil) 452 return (nil, err); 453 }else if(results == nil) 454 results = mach :: nil; 455 l = results; 456 if(l == nil){ 457 if(srv != nil) 458 l = srv->iph2a(mach); 459 if(l == nil) 460 return (nil, "cs: unknown host"); 461 } 462 } else 463 l = mach :: nil; 464 465 while(l != nil) { 466 s := hd l; 467 l = tl l; 468 dnetw := netw; 469 if(s != nil){ 470 (divert, err) := ipattr->findnetattr(ndb, "ip", s, "divert-"+netw); 471 if(err == nil && divert != nil){ 472 dnetw = divert; 473 if(!isnetwork(dnetw)) 474 return (nil, "network unavailable "+dnetw); # XXX should only give up if all addresses fail? 475 } 476 } 477 478 if(s != "") 479 s[len s] = '!'; 480 s += service; 481 482 repl = mntpt+"/"+dnetw+"/clone "+s; 483 if(verbose) 484 sys->fprint(stderr, "cs: %s!%s!%s -> %s\n", netw, mach, service, repl); 485 486 rl = repl :: rl; 487 } 488 rl = reverse(rl); 489 putcache(address, rl, now); 490 return (rl, nil); 491} 492 493querydns(name: string, rtype: string): (list of string, string) 494{ 495 fd := sys->open(mntpt+"/dns", Sys->ORDWR); 496 if(fd == nil) 497 return (nil, nil); 498 if(sys->fprint(fd, "%s %s", name, rtype) < 0) 499 return (nil, sys->sprint("%r")); 500 rl: list of string; 501 buf := array[256] of byte; 502 sys->seek(fd, big 0, 0); 503 while((n := sys->read(fd, buf, len buf)) > 0){ 504 # name rtype value 505 (nf, fld) := sys->tokenize(string buf[0:n], " \t"); 506 if(nf != 3){ 507 sys->fprint(stderr, "cs: odd result from dns: %s\n", string buf[0:n]); 508 continue; 509 } 510 rl = hd tl tl fld :: rl; 511 } 512 return (reverse(rl), nil); 513} 514 515dblook(attr: string, val: string, rattr: string): list of string 516{ 517 rl: list of string; 518 ptr: ref Attrdb->Dbptr; 519 for(;;){ 520 e: ref Dbentry; 521 (e, ptr) = ndb.findbyattr(ptr, attr, val, rattr); 522 if(e == nil) 523 break; 524 for(l := e.findbyattr(attr, val, rattr); l != nil; l = tl l){ 525 (nil, al) := hd l; 526 for(; al != nil; al = tl al) 527 if(!inlist((hd al).val, rl)) 528 rl = (hd al).val :: rl; 529 } 530 } 531 return reverse(rl); 532} 533 534inlist(s: string, l: list of string): int 535{ 536 for(; l != nil; l = tl l) 537 if(hd l == s) 538 return 1; 539 return 0; 540} 541 542reverse(l: list of string): list of string 543{ 544 t: list of string; 545 for(; l != nil; l = tl l) 546 t = hd l :: t; 547 return t; 548} 549 550isnumeric(a: string): int 551{ 552 i, c: int; 553 554 for(i = 0; i < len a; i++) { 555 c = a[i]; 556 if(c < '0' || c > '9') 557 return 0; 558 } 559 return 1; 560} 561 562nets: list of string; 563 564isnetwork(s: string) : int 565{ 566 if(find(s, nets)) 567 return 1; 568 (ok, nil) := sys->stat(mntpt+"/"+s+"/clone"); 569 if(ok >= 0) { 570 nets = s :: nets; 571 return 1; 572 } 573 return 0; 574} 575 576find(e: string, l: list of string) : int 577{ 578 for(; l != nil; l = tl l) 579 if (e == hd l) 580 return 1; 581 return 0; 582} 583 584isipnet(s: string) : int 585{ 586 return s == "net" || s == "tcp" || s == "udp" || s == "il"; 587} 588 589xlatesvc(proto: string, s: string): string 590{ 591 if(ndb == nil || s == nil || isnumeric(s)) 592 return s; 593 (e, nil) := ndb.findbyattr(nil, proto, s, "port"); 594 if(e == nil) 595 return nil; 596 matches := e.findbyattr(proto, s, "port"); 597 if(matches == nil) 598 return nil; 599 (ts, al) := hd matches; 600 restricted := ""; 601 if(ts.hasattr("restricted")) 602 restricted = "!r"; 603 if(verbose > 1) 604 sys->print("%s=%q port=%s%s\n", proto, s, (hd al).val, restricted); 605 return (hd al).val+restricted; 606} 607 608time(): int 609{ 610 timefd := sys->open("/dev/time", Sys->OREAD); 611 if(timefd == nil) 612 return 0; 613 buf := array[128] of byte; 614 sys->seek(timefd, big 0, 0); 615 n := sys->read(timefd, buf, len buf); 616 if(n < 0) 617 return 0; 618 return int ((big string buf[0:n]) / big 1000000); 619} 620 621# 622# general query: attr1=val1 attr2=val2 ... finds matching tuple(s) 623# where attr1 is the key and val1 can't be * 624# 625genquery(query: string): (list of string, string) 626{ 627 (tups, err) := attrdb->parseline(query, 0); 628 if(err != nil) 629 return (nil, "bad query: "+err); 630 if(tups == nil) 631 return (nil, "bad query"); 632 pairs := tups.pairs; 633 a0 := (hd pairs).attr; 634 if(a0 == "ipinfo") 635 return (nil, "ipinfo not yet supported"); 636 v0 := (hd pairs).val; 637 638 # if((a0 == "dom" || a0 == "ip") && v0 != nil){ 639 # query dns ... 640 # } 641 642 ptr: ref Attrdb->Dbptr; 643 e: ref Dbentry; 644 for(;;){ 645 (e, ptr) = ndb.findpair(ptr, a0, v0); 646 if(e == nil) 647 break; 648 for(l := e.lines; l != nil; l = tl l) 649 if(qmatch(hd l, tl pairs)){ 650 ls: list of string; 651 for(l = e.lines; l != nil; l = tl l) 652 ls = tuptext(hd l) :: ls; 653 return (reverse(ls), nil); 654 } 655 } 656 return (nil, "no match"); 657} 658 659# 660# see if set of tuples t contains every non-* attr/val pair 661# 662qmatch(t: ref Tuples, av: list of ref Attr): int 663{ 664Match: 665 for(; av != nil; av = tl av){ 666 a := hd av; 667 for(pl := t.pairs; pl != nil; pl = tl pl) 668 if((hd pl).attr == a.attr && 669 (a.val == "*" || a.val == (hd pl).val)) 670 continue Match; 671 return 0; 672 } 673 return 1; 674} 675 676tuptext(t: ref Tuples): string 677{ 678 s: string; 679 for(pl := t.pairs; pl != nil; pl = tl pl){ 680 p := hd pl; 681 if(s != nil) 682 s[len s] = ' '; 683 s += sys->sprint("%s=%q", p.attr, p.val); 684 } 685 return s; 686} 687