diff options
| author | Charles.Forsyth <devnull@localhost> | 2006-12-22 17:07:39 +0000 |
|---|---|---|
| committer | Charles.Forsyth <devnull@localhost> | 2006-12-22 17:07:39 +0000 |
| commit | 37da2899f40661e3e9631e497da8dc59b971cbd0 (patch) | |
| tree | cbc6d4680e347d906f5fa7fca73214418741df72 /appl/lib/virgil.b | |
| parent | 54bc8ff236ac10b3eaa928fd6bcfc0cdb2ba46ae (diff) | |
20060303a
Diffstat (limited to 'appl/lib/virgil.b')
| -rw-r--r-- | appl/lib/virgil.b | 177 |
1 files changed, 177 insertions, 0 deletions
diff --git a/appl/lib/virgil.b b/appl/lib/virgil.b new file mode 100644 index 00000000..eb68671b --- /dev/null +++ b/appl/lib/virgil.b @@ -0,0 +1,177 @@ +implement Virgil; + +include "sys.m"; + sys: Sys; +include "string.m"; +include "keyring.m"; +include "draw.m"; +include "security.m"; +include "ip.m"; + ip: IP; + IPaddr, Udphdr: import ip; + +stderr: ref Sys->FD; +done: int; +Udphdrsize: con IP->OUdphdrlen; # use oldheaders format for compatibility +Virgilport: con 2202; + +# +# this module is very udp dependent. it shouldn't be. -- presotto +# Call with first element of argv an arbitrary string, which is +# discarded here. argv must also contain at least a question. +# +virgil(argv: list of string): string +{ + s,question,reply,r : string; + timerpid, readerpid: int; + + if (argv == nil || tl argv == nil || hd (tl argv) == nil) + return nil; + done = 0; + sys = load Sys Sys->PATH; + str := load String String->PATH; + if(str == nil){ + cantload(String->PATH); + return nil; + } + ip = load IP IP->PATH; + if(ip == nil){ + cantload(IP->PATH); + return nil; + } + ip->init(); + stderr = sys->fildes(2); + + # We preserve the convention that the first arg is not an option. + # Undocumented '-v address' option allows passing in address + # of virgild, circumventing broadcast. Used for development, + # to avoid pestering servers on network. + dest := ip->v4bcast; + argv = tl argv; + s = hd argv; + if(s[0] == '-') { + if(s[1] != 'v') + return nil; + argv = tl argv; + if (argv == nil) + return nil; + s = hd argv; + ok: int; + (ok, dest) = IPaddr.parse(s); + if(ok < 0){ + sys->fprint(stderr, "virgil: invalid IP address %s\n", s); + return nil; + } + argv = tl argv; + } + + # Is there a question? + if (argv == nil) + return nil; + question = hd argv; + + (ok, c) := sys->announce("udp!*!0"); + if(ok < 0) + return nil; + if(sys->fprint(c.cfd, "headers") < 0) + return nil; + sys->fprint(c.cfd, "oldheaders"); + c.dfd = sys->open(c.dir+"/data", sys->ORDWR); + if(c.dfd == nil) + return nil; + + readerchan := chan of string; + timerchan := chan of int; + readerpidchan := chan of int; + + spawn timer(timerchan); + timerpid = <-timerchan; + spawn reader(c.dfd, readerchan, readerpidchan); + readerpid = <-readerpidchan; + + question = getid() + "?" + question; + qbuf := array of byte question; + hdr := Udphdr.new(); + hdr.raddr = dest; + hdr.rport = Virgilport; + buf := array[Udphdrsize + len qbuf] of byte; + buf[Udphdrsize:] = qbuf; + hdr.pack(buf, Udphdrsize); + for(tries := 0; tries < 5; ){ + if(sys->write(c.dfd, buf, len buf) < 0) + break; + + alt { + r = <-readerchan => + ; + <-timerchan => + tries++; + continue; + }; + + if(str->prefix(question + "=", r)){ + reply = r[len question + 1:]; + break; + } + } + + done = 1; + killpid(readerpid); + killpid(timerpid); + return reply; +} + +cantload(s: string) +{ + sys->fprint(stderr, "virgil: can't load %s: %r\n", s); +} + +getid(): string +{ + fd := sys->open("/dev/sysname", sys->OREAD); + if(fd == nil) + return "unknown"; + buf := array[256] of byte; + n := sys->read(fd, buf, len buf); + if(n < 1) + return "unknown"; + return string buf[0:n]; +} + +reader(fd: ref sys->FD, cstring: chan of string, cpid: chan of int) +{ + pid := sys->pctl(0, nil); + cpid <-= pid; + + buf := array[2048] of byte; + n := sys->read(fd, buf, len buf); + if(n <= Udphdrsize) + return; + + # dump cruft + for(i := Udphdrsize; i < n; i++) + if((int buf[i]) == 0) + break; + + if(!done) + cstring <-= string buf[Udphdrsize:i]; +} + +timer(c: chan of int) +{ + pid := sys->pctl(0, nil); + c <-= pid; + while(!done){ + sys->sleep(1000); + if(done) + break; + c <-= 1; + } +} + +killpid(pid: int) +{ + fd := sys->open("#p/"+(string pid)+"/ctl", sys->OWRITE); + if(fd != nil) + sys->fprint(fd, "kill"); +} |
