summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorment <thement@ibawizard.net>2011-05-09 00:07:29 +0200
committerment <thement@ibawizard.net>2011-05-09 00:07:29 +0200
commit3d0ba195dd1ca583accef3ae040a850745660bba (patch)
tree1e2e787bf8de2a78a940484de552bfebd7ffb957
parentdca7c99602b6aaf5bd8fdab80d0b4958054970e5 (diff)
cmd/tty: cooked mode emulator
-rw-r--r--sys/src/cmd/tty.c306
1 files changed, 306 insertions, 0 deletions
diff --git a/sys/src/cmd/tty.c b/sys/src/cmd/tty.c
new file mode 100644
index 000000000..22f86aec2
--- /dev/null
+++ b/sys/src/cmd/tty.c
@@ -0,0 +1,306 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#include <thread.h>
+#include <fcall.h>
+#include <9p.h>
+
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+
+enum {
+ Stacksz = 8192,
+};
+
+typedef struct Prog {
+ Channel *pidc;
+ char **argv;
+} Prog;
+
+typedef struct Line {
+ uchar *buf;
+ int x, len;
+} Line;
+
+int cons, consctl;
+Channel *rchan, *wchan, *ichan;
+File *devcons;
+
+static void
+killer(void *arg)
+{
+ int *pid = arg;
+ ulong yes;
+ for(;;){
+ yes = recvul(ichan);
+ if(yes)
+ postnote(PNGROUP, *pid, "interrupt");
+ }
+}
+
+static void
+sendline(Channel *c, Line *L)
+{
+ int n, k;
+ Req *r;
+ uchar *s;
+
+ s = L->buf;
+ n = L->x;
+ do{
+ while(!(r = recvp(c)));
+ k = MIN(n, r->ifcall.count);
+ memcpy(r->ofcall.data, s, k);
+ r->ofcall.count = k;
+ respond(r, nil);
+ s += k;
+ n -= k;
+ }while(n > 0);
+ L->x = 0;
+}
+
+static void
+senderr(Channel *c, char *err)
+{
+ Req *r;
+ while(!(r = recvp(c)));
+ respond(r, err);
+}
+
+static void
+echo(uchar c)
+{
+ write(cons, &c, 1);
+}
+
+static int
+addchar(Line *L, uchar c)
+{
+ int send;
+
+ send = 0;
+ switch(c){
+ case '\n':
+ send = 1;
+ break;
+ case 4:
+ return 1;
+ case 8:
+ if(L->x > 0){
+ echo(8);
+ L->x--;
+ }
+ return 0;
+ case 21:
+ while(L->x > 0){
+ echo(8);
+ L->x--;
+ }
+ return 0;
+ case 23:
+ while(L->x > 0){
+ c = L->buf[--L->x];
+ echo(8);
+ if(c == ' ' || c == '\t')
+ break;
+ }
+ return 0;
+ case 127:
+ L->x = 0;
+ sendul(ichan, 1);
+ return 1;
+ }
+ echo(c);
+ L->buf[L->x++] = c;
+ if(L->x == L->len)
+ send = 1;
+ return send;
+}
+
+static Line *
+makeline(int len)
+{
+ Line *L;
+ L = malloc(sizeof(*L));
+ L->buf = malloc(len);
+ L->x = 0;
+ L->len = len;
+ return L;
+}
+
+static void
+reader(void *)
+{
+ int n;
+ uchar c;
+ Line *L;
+ char err[ERRMAX];
+
+ L = makeline(128);
+ for(;;){
+ n = read(cons, &c, 1);
+ if(n < 0){
+ rerrstr(err, sizeof(err));
+ if(L->x > 0)
+ sendline(rchan, L);
+ senderr(rchan, err);
+ continue;
+ }
+ if(addchar(L, c))
+ sendline(rchan, L);
+ }
+}
+
+static void
+writer(void *)
+{
+ Req *r;
+ int n;
+ char err[ERRMAX];
+
+ for(;;){
+ do
+ r = recvp(wchan);
+ while(!r);
+
+ n = write(cons, r->ifcall.data, r->ifcall.count);
+ if(n < 0){
+ rerrstr(err, sizeof(err));
+ respond(r, err);
+ }else{
+ r->ofcall.count = n;
+ respond(r, nil);
+ }
+ }
+}
+
+static void
+fsread(Req *r)
+{
+ sendp(rchan, r);
+}
+
+static void
+fswrite(Req *r)
+{
+ sendp(wchan, r);
+}
+
+/* adapted from acme/win */
+int
+lookinbin(char *s)
+{
+ if(s[0] == '/')
+ return 0;
+ if(s[0]=='.' && s[1]=='/')
+ return 0;
+ if(s[0]=='.' && s[1]=='.' && s[2]=='/')
+ return 0;
+ return 1;
+}
+
+char*
+estrstrdup(char *s, char *t)
+{
+ char *u;
+
+ u = malloc(strlen(s)+strlen(t)+1);
+ sprint(u, "%s%s", s, t);
+ return u;
+}
+
+void
+cmdproc(void *arg)
+{
+ Prog *p = arg;
+ char **av = p->argv;
+ char *cmd;
+
+ rfork(RFCFDG | RFNOTEG);
+ open("/dev/cons", OREAD);
+ open("/dev/cons", OWRITE);
+ dup(1, 2);
+ procexec(p->pidc, av[0], av);
+ if(lookinbin(av[0])){
+ cmd = estrstrdup("/bin/", av[0]);
+ procexec(p->pidc, cmd, av);
+ }
+ threadexitsall("exec");
+}
+
+void
+runcmd(char **argv)
+{
+ Channel *waitc;
+ Channel *pidc;
+ Waitmsg *w;
+ Prog prog;
+ int pid;
+
+ waitc = threadwaitchan();
+ pidc = chancreate(sizeof(int), 0);
+ prog.argv = argv;
+ prog.pidc = pidc;
+ proccreate(cmdproc, &prog, Stacksz);
+ while(recv(pidc, &pid) == -1 || pid == -1);
+ threadcreate(killer, &pid, Stacksz);
+ while(recv(waitc, &w) == -1);
+ free(w);
+}
+
+void
+usage(void)
+{
+ print("usage: tty [-D] cmd arg1 arg2 ...\n");
+ exits("usage");
+}
+
+Srv fs = {
+ .read = fsread,
+ .write = fswrite,
+};
+
+void
+threadmain(int argc, char *argv[])
+{
+ char *user;
+
+ ARGBEGIN{
+ case 'D':
+ chatty9p++;
+ break;
+ default:
+ usage();
+ break;
+ }ARGEND;
+
+ if(argc == 0)
+ usage();
+
+ rfork(RFNAMEG);
+
+ cons = open("/dev/cons", ORDWR);
+ if(cons < 0)
+ sysfatal("cons: %r");
+ consctl = open("/dev/consctl", OWRITE);
+ if(consctl < 0)
+ sysfatal("ctl: %r");
+ fprint(consctl, "rawon\n");
+
+ rchan = chancreate(sizeof(void *), 8);
+ wchan = chancreate(sizeof(void *), 8);
+ ichan = chancreate(sizeof(ulong), 8);
+
+ proccreate(reader, nil, Stacksz);
+ proccreate(writer, nil, Stacksz);
+
+ user = getuser();
+ fs.tree = alloctree(user, getuser(), DMDIR|0555, nil);
+ devcons = createfile(fs.tree->root, "cons", user, 0666, nil);
+ threadpostmountsrv(&fs, nil, "/dev", MBEFORE);
+
+ runcmd(argv);
+
+ close(consctl);
+ close(cons);
+ threadexitsall(nil);
+}