#include "paint.h" static int openwindow(int *cons) { int label; if(newwindow(nil) < 0) return -1; if((*cons = open("/dev/cons", ORDWR)) < 0) { werrstr("open /dev/cons: %r"); return -1; } if((label = open("/dev/label", OWRITE)) < 0) { werrstr("open /dev/label: %r"); close(*cons); return -1; } if(fprint(label, "%s cmd", argv0) < 0) { werrstr("write label: %r"); close(label); close(*cons); return -1; } close(label); return 0; } static void cmdproc(void *argvp) { void **argv; Channel *sync, *cmdc, *wmsgc; int cons; char *err; char *prompt = " "; char buf[512]; int n; char *s; Cmd cmd; Waitmsg *w; argv = argvp; sync = argv[0]; cmdc = argv[1]; threadsetname("cmd"); wmsgc = chancreate(sizeof(Waitmsg *), 0); /* newwindow will fork the namespace */ if(openwindow(&cons) < 0) { if((err = smprint("%r")) == nil) sysfatal("cmdproc: no memory"); sendp(sync, err); return; } sendp(sync, nil); fprint(cons, "%s", prompt); while((n = read(cons, buf, sizeof(buf) - 1)) > 0) { buf[n-1] = '\0'; for(s = buf; *s != '\0' && (*s == ' ' || *s == ' '); s++) ; if(*s == '\0') goto Cont; if((s = strdup(s)) == nil) { fprint(cons, "cmdproc: strdup: %r\n"); goto Cont; } cmd = (Cmd){s, cons, wmsgc}; if(nbsendp(cmdc, &cmd) == 0) { fprint(cons, "blocked\n"); sendp(cmdc, &cmd); } if((w = recvp(wmsgc)) == nil) goto Cont; if(strcmp(w->msg, "") != 0) fprint(cons, "%s\n", w->msg); free(w); Cont: fprint(cons, "%s", prompt); } close(cons); } static Waitmsg * cmdread(Canvas *canvas, Cmd *cmd, char *s) { int p[2]; if(pipe(p) < 0) { fprint(cmd->cons, "cmdread: pipe: %r\n"); return nil; } switch(rfork(RFPROC|RFFDG|RFREND|RFNAMEG|RFENVG)) { case -1: close(p[0]); close(p[1]); fprint(cmd->cons, "cmdread: fork: %r\n"); return nil; case 0: close(p[1]); dup(cmd->cons, 0); dup(p[0], 1); dup(cmd->cons, 2); close(p[0]); close(cmd->cons); execl("/bin/rc", "rc", "-c", s, nil); fprint(cmd->cons, "cmdread: exec: %r\n"); exits("exec"); } close(p[0]); if(loadcanvas(canvas, p[1]) < 0) { fprint(cmd->cons, "cmdread: load canvas: %r\n"); close(p[1]); return wait(); } close(p[1]); return wait(); } /* canvas -> p[1] -> p[0] -> ☁ -> spool -> canvas */ static Waitmsg * cmdpipe(Canvas *canvas, Cmd *cmd, char *s) { int p[2], spool; char spoolname[16]; Rectangle r; Waitmsg *w; if(pipe(p) < 0) { fprint(cmd->cons, "cmdpipe: pipe: %r\n"); return nil; } /* write the image to p[1] */ switch(rfork(RFPROC|RFFDG|RFNOWAIT)) { case -1: close(p[0]); close(p[1]); fprint(cmd->cons, "cmdpipe: fork: %r\n"); return nil; case 0: close(p[0]); if(canvas->sel.active) r = canvas->sel.r; else r = canvas->s->mem->r; if(unloadmem(p[1], canvas->s->mem, r) < 0) { fprint(cmd->cons, "cmdpipe: unloadmem: %r\n"); exits("unloadmem"); } exits(nil); } close(p[1]); snprint(spoolname, sizeof(spoolname) - 1, "/tmp/paintXXXXX"); if((spool = create(spoolname, ORDWR|ORCLOSE, 0600)) < 0) { fprint(cmd->cons, "cmdpipe: create spool: %r\n"); close(p[0]); return nil; } /* read input from p[0] and write output to spool */ switch(rfork(RFPROC|RFFDG|RFREND|RFNAMEG|RFENVG)) { case -1: close(p[0]); close(spool); fprint(cmd->cons, "cmdpipe: fork: %r\n"); return nil; case 0: dup(p[0], 0); dup(spool, 1); dup(cmd->cons, 2); close(p[0]); close(spool); close(cmd->cons); execl("/bin/rc", "rc", "-c", s, nil); fprint(cmd->cons, "cmdpipe: exec: %r\n"); exits("exec"); } close(p[0]); w = wait(); if(strcmp(w->msg, "") != 0) { close(spool); return w; } /* load the image from spool */ if(seek(spool, 0, 0) < 0) { fprint(cmd->cons, "cmdpipe: seek: %r\n"); close(spool); return w; } if(loadcanvas(canvas, spool) < 0) { fprint(cmd->cons, "cmdpipe: load canvas: %r\n"); close(spool); return w; } close(spool); return w; } static Waitmsg * cmdwrite(Canvas *canvas, Cmd *cmd, char *s) { int p[2]; Rectangle r; if(pipe(p) < 0) { fprint(cmd->cons, "cmdwrite: pipe: %r\n"); return nil; } switch(rfork(RFPROC|RFFDG|RFNOWAIT)) { case -1: close(p[0]); close(p[1]); fprint(cmd->cons, "cmdwrite: fork: %r\n"); return nil; case 0: close(p[0]); if(canvas->sel.active) r = canvas->sel.r; else r = canvas->s->mem->r; if(unloadmem(p[1], canvas->s->mem, r) < 0) { fprint(cmd->cons, "cmdwrite: unloadmem: %r\n"); exits("unloadmem"); } exits(nil); } close(p[1]); switch(rfork(RFPROC|RFFDG|RFREND|RFNAMEG|RFENVG)) { case -1: close(p[0]); fprint(cmd->cons, "cmdwrite: fork: %r\n"); return nil; case 0: dup(p[0], 0); dup(cmd->cons, 1); dup(cmd->cons, 2); close(p[0]); close(cmd->cons); execl("/bin/rc", "rc", "-c", s, nil); fprint(cmd->cons, "cmdwrite: exec: %r\n"); exits("exec"); } close(p[0]); return wait(); } void execcmd(Canvas *canvas, Cmd *cmd) { char *s; char c; Waitmsg *w; c = cmd->s[0]; s = cmd->s + 1; switch(c) { case '<': w = cmdread(canvas, cmd, s); rootfanout->redraw(rootfanout, RDall); break; case '>': w = cmdwrite(canvas, cmd, s); break; case '|': w = cmdpipe(canvas, cmd, s); rootfanout->redraw(rootfanout, RDall); break; default: fprint(2, "unrecognized command: %c\n", c); w = nil; break; } sendp(cmd->wmsg, w); } int newcmdwindow(Channel *cmdc) { Channel *sync; /* a malloc'd string */ void *argv[2]; char *err; sync = chancreate(sizeof(char *), 0); argv[0] = sync; argv[1] = cmdc; proccreate(cmdproc, argv, 8*1024); err = recvp(sync); chanfree(sync); if(err != nil) { werrstr(err); free(err); return -1; } return 0; }