summaryrefslogtreecommitdiff
path: root/sys/src/cmd/os.c
diff options
context:
space:
mode:
authorcinap_lenrek <cinap_lenrek@felloff.net>2019-11-30 20:10:08 +0100
committercinap_lenrek <cinap_lenrek@felloff.net>2019-11-30 20:10:08 +0100
commit2359389570a5ba761fceb5ba1db295a1df8cbc64 (patch)
tree1e44cfc0fd210f9c0691b366bde8e6ef805f4b56 /sys/src/cmd/os.c
parentd32e5d130c3940d4a6793fb50a31c3c9fd167508 (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.c261
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);
+ }
+}