implement Simdev; # simulated a device using a host os command # simdev [-abr] simdevname mountdir cmd ... # # runs cmd under the host OS and converts reads and writes # on mountdir/simdevname to reads and writes of cmd's stdin/stdout file # # example: given a host OS command cpaudio that copies # its stdin/stdout to and from the real OS's equivalent to /dev/audio, # perhaps with format conversion (a la sox), one might use: # simdev -a /dev audio cpaudio # to allow the audio applications to work # # of course, with Plan 9, it's easier: # os bind -a /dev /usr/inferno/dev # to set it up from the outside # # bugs to: forsyth@caldo.demon.co.uk include "sys.m"; sys: Sys; FD, FORKFD, NEWPGRP, FileIO, Rread, Rwrite: import Sys; include "draw.m"; Simdev: module { init: fn(nil: ref Draw->Context, argv: list of string); }; stderr: ref FD; MAXBSIZE: con 64*1024; usage() { sys->fprint(stderr, "Usage: simdev [-abr] devname dirname cmd ...\n"); exit; } init(nil: ref Draw->Context, argv: list of string) { sys = load Sys Sys->PATH; stderr = sys->fildes(2); sys->pctl(FORKFD, nil); # don't FORKNS argv = tl argv; flags := sys->MREPL; while(argv != nil) { s := hd argv; if(s[0] != '-') break; case s[1] { 'a' => flags = sys->MAFTER; 'b' => flags = sys->MBEFORE; 'r' => flags = sys->MREPL; * => usage(); } argv = tl argv; } if(len argv < 3) usage(); devname := hd argv; argv = tl argv; dirname := hd argv; argv = tl argv; (i, dirbuf) := sys->stat(dirname); if(i < 0) { sys->fprint(stderr, "simdev: can't stat %s: %r\n", dirname); exit; } if((dirbuf.mode & Sys->CHDIR) == 0) { sys->fprint(stderr, "simdev: target %s must be a directory\n", dirname); exit; } # the #C handling quotes /appl/cmd/os.b cmd := "exec "; while(argv != nil) { cmd += hd argv; argv = tl argv; if(argv != nil) cmd += " "; } cfd := sys->open("/cmd/clone", sys->ORDWR); if(cfd == nil) { sys->fprint(stderr, "simdev: open /cmd/clone: %r\n"); exit; } buf := array[32] of byte; n := sys->read(cfd, buf, len buf); if(n <= 0) { sys->fprint(stderr, "simdev: read /cmd/#/ctl: %r\n"); exit; } dir := "/cmd/"+string buf[0:n]; # Start the Command n = sys->fprint(cfd, "%s", cmd); if(n <= 0) { sys->fprint(stderr, "simdev: exec: %r\n"); exit; } io := sys->open(dir+"/data", sys->ORDWR); if(io == nil) { sys->fprint(stderr, "simdev: open /cmd/#/data: %r\n"); exit; } # make the fake file devio := sys->file2chan(dirname, devname, flags); if(devio == nil) { sys->fprint(stderr, "simdev: can't create %s/%d: %r\n", dirname, devname); exit; } spawn work(devio, io); # leave it running to serve the name space } work(devio: ref FileIO, io: ref FD) { sys->pctl(Sys->NEWPGRP, nil); leave := chan of int; spawn reader(io, devio.read, leave); writer(devio.write, io); <-leave; } reader(f: ref FD, c: chan of (int, int, int, Rread), leave: chan of int) { buf := array[MAXBSIZE] of byte; lastoff := 0; eof := 0; for(;;) { (off, count, fid, rc) := <-c; if(rc == nil) break; if(off != lastoff) { sys->seek(f, off, Sys->SEEKSTART); lastoff = off; } if(count > len buf) count = len buf; n := sys->read(f, buf, count); if(n > 0) { a := array[n] of byte; # send a copy a[0:] = buf[0:n]; rc <-= (a, nil); lastoff += n; } else { s: string = nil; if(n < 0) { s = sys->sprint("%r"); if(s == "write to hungup channel" && eof++ < 2) s = nil; } rc <-= (nil, s); } } leave <-= 1; } writer(c: chan of (int, array of byte, int, Rwrite), t: ref FD) { lastoff := 0; for(;;) { (off, buf, fid, wc) := <-c; if(wc == nil) break; if(off != lastoff) { sys->seek(t, off, Sys->SEEKSTART); lastoff = off; } n := sys->write(t, buf, len buf); if(n > 0) { wc <-= (n, nil); lastoff += n; } else wc <-= (0, sys->sprint("%r")); buf = nil; } }