shithub: riscv

ref: 349abfcd795125bf09fae588944d98b9d871cbc9
dir: /sys/src/cmd/tty.c/

View raw version
#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);
}