diff options
author | cinap_lenrek <cinap_lenrek@felloff.net> | 2019-11-30 20:10:08 +0100 |
---|---|---|
committer | cinap_lenrek <cinap_lenrek@felloff.net> | 2019-11-30 20:10:08 +0100 |
commit | 2359389570a5ba761fceb5ba1db295a1df8cbc64 (patch) | |
tree | 1e44cfc0fd210f9c0691b366bde8e6ef805f4b56 /sys/src/cmd/os.c | |
parent | d32e5d130c3940d4a6793fb50a31c3c9fd167508 (diff) |
os(1): add c implementation of inferno os command and cmd(3) device manpages
this is a reimplementation of infernos os(1) command, which
allows running commands in the underhying host operating
system when inferno runs in hosted mode (emu). but unlike
inferno, we want to use it to run commands on the client
side of a inferno or drawterm session from the plan9 cpu
server, so it defaults to /mnt/term/cmd for the mountpoint.
Diffstat (limited to 'sys/src/cmd/os.c')
-rw-r--r-- | sys/src/cmd/os.c | 261 |
1 files changed, 261 insertions, 0 deletions
diff --git a/sys/src/cmd/os.c b/sys/src/cmd/os.c new file mode 100644 index 000000000..54b873897 --- /dev/null +++ b/sys/src/cmd/os.c @@ -0,0 +1,261 @@ +#include <u.h> +#include <libc.h> + +enum { + Fstdin, + Fstdout, + Fstderr, + Fwait, + Fctl, + Nfd, +}; + +enum { + Pcopyin, + Pcopyout, + Pcopyerr, + Preadwait, + Npid, +}; + +char *mnt = "/mnt/term/cmd"; +char *dir = nil; +char buf[8192]; +int fd[Nfd] = {-1}; +int pid[Npid]; +int nice, foreground = 1; + +void +killstdin(void) +{ + if(pid[Pcopyin] != 0) + postnote(PNPROC, pid[Pcopyin], "kill"); +} + +void +killproc(void) +{ + if(fd[Fctl] >= 0) + write(fd[Fctl], "kill", 4); +} + +int +catch(void*, char *msg) +{ + if(strcmp(msg, "interrupt") == 0 + || strcmp(msg, "hangup") == 0 + || strcmp(msg, "kill") == 0){ + killproc(); + return 1; + } + return 0; +} + +void +fd01(int fd0, int fd1) +{ + int i; + + if(fd0 >= 0 && fd0 != 0){ + if(dup(fd0, 0) < 0) + sysfatal("dup: %r"); + } + if(fd1 >= 0 && fd1 != 1){ + if(dup(fd1, 1) < 0) + sysfatal("dup: %r"); + } + for(i = 0; i<Nfd; i++){ + if(fd[i] > 2) + close(fd[i]); + if(fd[i] == fd0) + fd[i] = 0; + else if(fd[i] == fd1) + fd[i] = 1; + else + fd[i] = -1; + } +} + +void +copy(void) +{ + int n; + + while((n = read(0, buf, sizeof(buf))) > 0) + if(write(1, buf, n) != n) + break; +} + +void +usage(void) +{ + fprint(2, "%s: [ -b ] [ -m mountpoint ] [ -d dir ] [ -n ] [ -N level ] cmd [ arg... ]\n", argv0); + exits("usage"); +} + +void +main(int argc, char **argv) +{ + Waitmsg *w; + char *s; + int n; + + quotefmtinstall(); + + ARGBEGIN { + case 'b': + foreground = 0; + break; + case 'm': + mnt = cleanname(EARGF(usage())); + break; + case 'd': + dir = EARGF(usage()); + break; + case 'n': + nice = 1; + break; + case 'N': + nice = atoi(EARGF(usage())); + break; + default: + usage(); + } ARGEND; + + if(argc < 1) + usage(); + + seprint(buf, &buf[sizeof(buf)], "%s/clone", mnt); + if((fd[Fctl] = open(buf, ORDWR)) < 0) + sysfatal("open: %r"); + s = &buf[strlen(mnt)+1]; + if((n = read(fd[Fctl], s, &buf[sizeof(buf)-1]-s)) < 0) + sysfatal("read clone: %r"); + while(n > 0 && buf[n-1] == '\n') + n--; + s += n; + + seprint(s, &buf[sizeof(buf)], "/wait"); + if((fd[Fwait] = open(buf, OREAD)) < 0) + sysfatal("open: %r"); + + if(foreground){ + seprint(s, &buf[sizeof(buf)], "/data"); + if((fd[Fstdin] = open(buf, OWRITE)) < 0) + sysfatal("open: %r"); + + seprint(s, &buf[sizeof(buf)], "/data"); + if((fd[Fstdout] = open(buf, OREAD)) < 0) + sysfatal("open: %r"); + + seprint(s, &buf[sizeof(buf)], "/stderr"); + if((fd[Fstderr] = open(buf, OREAD)) < 0) + sysfatal("open: %r"); + } + + if(dir != nil){ + if(fprint(fd[Fctl], "dir %q", dir) < 0) + sysfatal("cannot change directory: %r"); + } else { + /* + * try to automatically change directory if we are in + * /mnt/term or /mnt/term/root, but unlike -d flag, + * do not error when the dir ctl command fails. + */ + if((s = strrchr(mnt, '/')) != nil){ + n = s - mnt; + dir = getwd(buf, sizeof(buf)); + if(strncmp(dir, mnt, n) == 0 && (dir[n] == 0 || dir[n] == '/')){ + dir += n; + if(strncmp(dir, "/root", 5) == 0 && (dir[5] == 0 || dir[5] == '/')) + dir += 5; + if(*dir == 0) + dir = "/"; + /* hack for win32: /C:... -> C:/... */ + if(dir[1] >= 'A' && dir[1] <= 'Z' && dir[2] == ':') + dir[0] = dir[1], dir[1] = ':', dir[2] = '/'; + fprint(fd[Fctl], "dir %q", dir); + } + } + } + + if(nice != 0) + fprint(fd[Fctl], "nice %d", nice); + + if(foreground) + fprint(fd[Fctl], "killonclose"); + + s = seprint(buf, &buf[sizeof(buf)], "exec"); + while(*argv != nil){ + s = seprint(s, &buf[sizeof(buf)], " %q", *argv++); + if(s >= &buf[sizeof(buf)-1]) + sysfatal("too many arguments"); + } + + if(write(fd[Fctl], buf, s - buf) < 0) + sysfatal("write: %r"); + + if((pid[Preadwait] = fork()) == -1) + sysfatal("fork: %r"); + if(pid[Preadwait] == 0){ + fd01(fd[Fwait], 2); + if((n = read(0, buf, sizeof(buf)-1)) < 0) + rerrstr(buf, sizeof(buf)); + else { + char *f[5]; + + while(n > 0 && buf[n-1] == '\n') + n--; + buf[n] = 0; + if(tokenize(buf, f, 5) == 5) + exits(f[4]); + } + exits(buf); + } + + if(foreground){ + if((pid[Pcopyerr] = fork()) == -1) + sysfatal("fork: %r"); + if(pid[Pcopyerr] == 0){ + fd01(fd[Fstderr], 2); + copy(); + rerrstr(buf, sizeof(buf)); + exits(buf); + } + if((pid[Pcopyout] = fork()) == -1) + sysfatal("fork: %r"); + if(pid[Pcopyout] == 0){ + fd01(fd[Fstdout], 1); + copy(); + rerrstr(buf, sizeof(buf)); + exits(buf); + } + if((pid[Pcopyin] = fork()) == -1) + sysfatal("fork: %r"); + if(pid[Pcopyin] == 0){ + fd01(0, fd[Fstdin]); + copy(); + rerrstr(buf, sizeof(buf)); + exits(buf); + } + } + fd01(fd[Fctl], 2); + atexit(killstdin); + atnotify(catch, 1); + + while((w = wait()) != nil){ + if((s = strstr(w->msg, ": ")) == nil) + s = w->msg; + else + s += 2; + for(n = 0; n < Npid; n++){ + if(pid[n] == w->pid){ + pid[n] = 0; + break; + } + } + if(n == Preadwait) + exits(s); + free(w); + } +} |