cmd/tty: cooked mode emulator
This commit is contained in:
parent
dca7c99602
commit
3d0ba195dd
1 changed files with 306 additions and 0 deletions
306
sys/src/cmd/tty.c
Normal file
306
sys/src/cmd/tty.c
Normal file
|
@ -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);
|
||||
}
|
Loading…
Reference in a new issue