xref: /inferno-os/emu/port/devcons.c (revision 37da2899f40661e3e9631e497da8dc59b971cbd0)
1 #include	"dat.h"
2 #include	"fns.h"
3 #include	"error.h"
4 #include	"version.h"
5 #include	"mp.h"
6 #include	"libsec.h"
7 #include	"keyboard.h"
8 
9 extern int cflag;
10 int	exdebug;
11 extern int keepbroken;
12 
13 enum
14 {
15 	Qdir,
16 	Qcons,
17 	Qconsctl,
18 	Qdrivers,
19 	Qhostowner,
20 	Qhoststdin,
21 	Qhoststdout,
22 	Qhoststderr,
23 	Qjit,
24 	Qkeyboard,
25 	Qkprint,
26 	Qmemory,
27 	Qmsec,
28 	Qnotquiterandom,
29 	Qnull,
30 	Qpin,
31 	Qrandom,
32 	Qscancode,
33 	Qsysctl,
34 	Qsysname,
35 	Qtime,
36 	Quser
37 };
38 
39 Dirtab contab[] =
40 {
41 	".",	{Qdir, 0, QTDIR},	0,		DMDIR|0555,
42 	"cons",		{Qcons},	0,	0666,
43 	"consctl",	{Qconsctl},	0,	0222,
44 	"drivers",	{Qdrivers},	0,	0444,
45 	"hostowner",	{Qhostowner},	0,	0644,
46 	"hoststdin",	{Qhoststdin},	0,	0444,
47 	"hoststdout",	{Qhoststdout},	0,	0222,
48 	"hoststderr",	{Qhoststderr},	0,	0222,
49 	"jit",	{Qjit},	0,	0666,
50 	"keyboard",	{Qkeyboard},	0,	0666,
51 	"kprint",	{Qkprint},	0,	0444,
52 	"memory",	{Qmemory},	0,	0444,
53 	"msec",		{Qmsec},	NUMSIZE,	0444,
54 	"notquiterandom",	{Qnotquiterandom},	0,	0444,
55 	"null",		{Qnull},	0,	0666,
56 	"pin",		{Qpin},		0,	0666,
57 	"random",	{Qrandom},	0,	0444,
58 	"scancode",	{Qscancode},	0,	0444,
59 	"sysctl",	{Qsysctl},	0,	0644,
60 	"sysname",	{Qsysname},	0,	0644,
61 	"time",		{Qtime},	0,	0644,
62 	"user",		{Quser},	0,	0644,
63 };
64 
65 Queue*	gkscanq;		/* Graphics keyboard raw scancodes */
66 char*	gkscanid;		/* name of raw scan format (if defined) */
67 Queue*	gkbdq;			/* Graphics keyboard unprocessed input */
68 Queue*	kbdq;			/* Console window unprocessed keyboard input */
69 Queue*	lineq;			/* processed console input */
70 
71 char	*ossysname;
72 
73 static struct
74 {
75 	RWlock l;
76 	Queue*	q;
77 } kprintq;
78 
79 vlong	timeoffset;
80 
81 extern int	dflag;
82 
83 static int	sysconwrite(void*, ulong);
84 extern char**	rebootargv;
85 
86 static struct
87 {
88 	QLock	q;
89 	QLock	gq;		/* separate lock for the graphical input */
90 
91 	int	raw;		/* true if we shouldn't process input */
92 	Ref	ctl;		/* number of opens to the control file */
93 	Ref	ptr;		/* number of opens to the ptr file */
94 	int	scan;		/* true if reading raw scancodes */
95 	int	x;		/* index into line */
96 	char	line[1024];	/* current input line */
97 
98 	Rune	c;
99 	int	count;
100 } kbd;
101 
102 void
103 kbdslave(void *a)
104 {
105 	char b;
106 
107 	USED(a);
108 	for(;;) {
109 		b = readkbd();
110 		if(kbd.raw == 0)
111 			write(1, &b, 1);
112 		qproduce(kbdq, &b, 1);
113 	}
114 	pexit("kbdslave", 0);
115 }
116 
117 void
118 gkbdputc(Queue *q, int ch)
119 {
120 	int n;
121 	Rune r;
122 	static uchar kc[5*UTFmax];
123 	static int nk, collecting = 0;
124 	char buf[UTFmax];
125 
126 	r = ch;
127 	if(r == Latin) {
128 		collecting = 1;
129 		nk = 0;
130 		return;
131 	}
132 	if(collecting) {
133 		int c;
134 		nk += runetochar((char*)&kc[nk], &r);
135 		c = latin1(kc, nk);
136 		if(c < -1)	/* need more keystrokes */
137 			return;
138 		collecting = 0;
139 		if(c == -1) {	/* invalid sequence */
140 			qproduce(q, kc, nk);
141 			return;
142 		}
143 		r = (Rune)c;
144 	}
145 	n = runetochar(buf, &r);
146 	if(n == 0)
147 		return;
148 	/* if(!isdbgkey(r)) */
149 		qproduce(q, buf, n);
150 }
151 
152 void
153 consinit(void)
154 {
155 	kbdq = qopen(512, 0, 0, 0);
156 	if(kbdq == 0)
157 		panic("no memory");
158 	lineq = qopen(512, 0, 0, 0);
159 	if(lineq == 0)
160 		panic("no memory");
161 	gkbdq = qopen(512, 0, 0, 0);
162 	if(gkbdq == 0)
163 		panic("no memory");
164 	randominit();
165 }
166 
167 /*
168  *  return true if current user is eve
169  */
170 int
171 iseve(void)
172 {
173 	return strcmp(eve, up->env->user) == 0;
174 }
175 
176 static Chan*
177 consattach(char *spec)
178 {
179 	static int kp;
180 
181 	if (kp == 0 && !dflag) {
182 		kproc("kbd", kbdslave, 0, 0);
183 		kp = 1;
184 	}
185 	return devattach('c', spec);
186 }
187 
188 static Walkqid*
189 conswalk(Chan *c, Chan *nc, char **name, int nname)
190 {
191 	return devwalk(c, nc, name, nname, contab, nelem(contab), devgen);
192 }
193 
194 static int
195 consstat(Chan *c, uchar *db, int n)
196 {
197 	return devstat(c, db, n, contab, nelem(contab), devgen);
198 }
199 
200 static Chan*
201 consopen(Chan *c, int omode)
202 {
203 	c = devopen(c, omode, contab, nelem(contab), devgen);
204 	switch((ulong)c->qid.path) {
205 	case Qconsctl:
206 		incref(&kbd.ctl);
207 		break;
208 	case Qscancode:
209 		qlock(&kbd.gq);
210 		if(gkscanq || !gkscanid) {
211 			qunlock(&kbd.q);
212 			c->flag &= ~COPEN;
213 			if(gkscanq)
214 				error(Einuse);
215 			else
216 				error(Ebadarg);
217 		}
218 		gkscanq = qopen(256, 0, nil, nil);
219 		qunlock(&kbd.gq);
220 		break;
221 	case Qkprint:
222 		wlock(&kprintq.l);
223 		if(kprintq.q != nil){
224 			wunlock(&kprintq.l);
225 			c->flag &= ~COPEN;
226 			error(Einuse);
227 		}
228 		kprintq.q = qopen(32*1024, 0, 0, 0);
229 		if(kprintq.q == nil){
230 			wunlock(&kprintq.l);
231 			c->flag &= ~COPEN;
232 			error(Enomem);
233 		}
234 		qnoblock(kprintq.q, 1);
235 		wunlock(&kprintq.l);
236 		break;
237 	}
238 	return c;
239 }
240 
241 static void
242 consclose(Chan *c)
243 {
244 	if((c->flag & COPEN) == 0)
245 		return;
246 
247 	switch((ulong)c->qid.path) {
248 	case Qconsctl:
249 		if(decref(&kbd.ctl) == 0)
250 			kbd.raw = 0;
251 		break;
252 	case Qscancode:
253 		qlock(&kbd.gq);
254 		if(gkscanq) {
255 			qfree(gkscanq);
256 			gkscanq = 0;
257 		}
258 		qunlock(&kbd.gq);
259 		break;
260 	case Qkprint:
261 		wlock(&kprintq.l);
262 		qfree(kprintq.q);
263 		kprintq.q = nil;
264 		wunlock(&kprintq.l);
265 		break;
266 	}
267 }
268 
269 static long
270 consread(Chan *c, void *va, long count, vlong offset)
271 {
272 	int i, n, ch, eol;
273 	char *p, buf[64];
274 
275 	if(c->qid.type & QTDIR)
276 		return devdirread(c, va, count, contab, nelem(contab), devgen);
277 
278 	switch((ulong)c->qid.path) {
279 	default:
280 		error(Egreg);
281 	case Qsysctl:
282 		return readstr(offset, va, count, VERSION);
283 	case Qsysname:
284 		if(ossysname == nil)
285 			return 0;
286 		return readstr(offset, va, count, ossysname);
287 	case Qrandom:
288 		return randomread(va, count);
289 	case Qnotquiterandom:
290 		genrandom(va, count);
291 		return count;
292 	case Qpin:
293 		p = "pin set";
294 		if(up->env->pgrp->pin == Nopin)
295 			p = "no pin";
296 		return readstr(offset, va, count, p);
297 	case Qhostowner:
298 		return readstr(offset, va, count, eve);
299 	case Qhoststdin:
300 		return read(0, va, count);	/* should be pread */
301 	case Quser:
302 		return readstr(offset, va, count, up->env->user);
303 	case Qjit:
304 		snprint(buf, sizeof(buf), "%d", cflag);
305 		return readstr(offset, va, count, buf);
306 	case Qtime:
307 		snprint(buf, sizeof(buf), "%.lld", timeoffset + osusectime());
308 		return readstr(offset, va, count, buf);
309 	case Qdrivers:
310 		p = malloc(READSTR);
311 		if(p == nil)
312 			error(Enomem);
313 		n = 0;
314 		for(i = 0; devtab[i] != nil; i++)
315 			n += snprint(p+n, READSTR-n, "#%C %s\n", devtab[i]->dc,  devtab[i]->name);
316 		n = readstr(offset, va, count, p);
317 		free(p);
318 		return n;
319 	case Qmemory:
320 		return poolread(va, count, offset);
321 
322 	case Qnull:
323 		return 0;
324 	case Qmsec:
325 		return readnum(offset, va, count, osmillisec(), NUMSIZE);
326 	case Qcons:
327 		qlock(&kbd.q);
328 		if(waserror()){
329 			qunlock(&kbd.q);
330 			nexterror();
331 		}
332 
333 		if(dflag)
334 			error(Enonexist);
335 
336 		while(!qcanread(lineq)) {
337 			qread(kbdq, &kbd.line[kbd.x], 1);
338 			ch = kbd.line[kbd.x];
339 			if(kbd.raw){
340 				qiwrite(lineq, &kbd.line[kbd.x], 1);
341 				continue;
342 			}
343 			eol = 0;
344 			switch(ch) {
345 			case '\b':
346 				if(kbd.x)
347 					kbd.x--;
348 				break;
349 			case 0x15:
350 				kbd.x = 0;
351 				break;
352 			case '\n':
353 			case 0x04:
354 				eol = 1;
355 			default:
356 				kbd.line[kbd.x++] = ch;
357 				break;
358 			}
359 			if(kbd.x == sizeof(kbd.line) || eol){
360 				if(ch == 0x04)
361 					kbd.x--;
362 				qwrite(lineq, kbd.line, kbd.x);
363 				kbd.x = 0;
364 			}
365 		}
366 		n = qread(lineq, va, count);
367 		qunlock(&kbd.q);
368 		poperror();
369 		return n;
370 	case Qscancode:
371 		if(offset == 0)
372 			return readstr(0, va, count, gkscanid);
373 		else
374 			return qread(gkscanq, va, count);
375 	case Qkeyboard:
376 		return qread(gkbdq, va, count);
377 	case Qkprint:
378 		rlock(&kprintq.l);
379 		if(waserror()){
380 			runlock(&kprintq.l);
381 			nexterror();
382 		}
383 		n = qread(kprintq.q, va, count);
384 		poperror();
385 		runlock(&kprintq.l);
386 		return n;
387 	}
388 }
389 
390 static long
391 conswrite(Chan *c, void *va, long count, vlong offset)
392 {
393 	char buf[128];
394 	int x;
395 
396 	USED(offset);
397 
398 	if(c->qid.type & QTDIR)
399 		error(Eperm);
400 
401 	switch((ulong)c->qid.path) {
402 	default:
403 		error(Egreg);
404 	case Qcons:
405 		if(canrlock(&kprintq.l)){
406 			if(kprintq.q != nil){
407 				if(waserror()){
408 					runlock(&kprintq.l);
409 					nexterror();
410 				}
411 				qwrite(kprintq.q, va, count);
412 				poperror();
413 				runlock(&kprintq.l);
414 				return count;
415 			}
416 			runlock(&kprintq.l);
417 		}
418 		return write(1, va, count);
419 	case Qsysctl:
420 		return sysconwrite(va, count);
421 	case Qconsctl:
422 		if(count >= sizeof(buf))
423 			count = sizeof(buf)-1;
424 		strncpy(buf, va, count);
425 		buf[count] = 0;
426 		if(strncmp(buf, "rawon", 5) == 0) {
427 			kbd.raw = 1;
428 			return count;
429 		}
430 		else
431 		if(strncmp(buf, "rawoff", 6) == 0) {
432 			kbd.raw = 0;
433 			return count;
434 		}
435 		error(Ebadctl);
436 	case Qkeyboard:
437 		for(x=0; x<count; ) {
438 			Rune r;
439 			x += chartorune(&r, &((char*)va)[x]);
440 			gkbdputc(gkbdq, r);
441 		}
442 		return count;
443 	case Qnull:
444 		return count;
445 	case Qpin:
446 		if(up->env->pgrp->pin != Nopin)
447 			error("pin already set");
448 		if(count >= sizeof(buf))
449 			count = sizeof(buf)-1;
450 		strncpy(buf, va, count);
451 		buf[count] = '\0';
452 		up->env->pgrp->pin = atoi(buf);
453 		return count;
454 	case Qtime:
455 		if(count >= sizeof(buf))
456 			count = sizeof(buf)-1;
457 		strncpy(buf, va, count);
458 		buf[count] = '\0';
459 		timeoffset = strtoll(buf, 0, 0)-osusectime();
460 		return count;
461 	case Quser:
462 		if(count >= sizeof(buf))
463 			error(Ebadarg);
464 		strncpy(buf, va, count);
465 		buf[count] = '\0';
466 		if(count > 0 && buf[count-1] == '\n')
467 			buf[--count] = '\0';
468 		if(count == 0)
469 			error(Ebadarg);
470 		if(strcmp(up->env->user, eve) != 0)
471 			error(Eperm);
472 		setid(buf, 0);
473 		return count;
474 	case Qhostowner:
475 		if(count >= sizeof(buf))
476 			error(Ebadarg);
477 		strncpy(buf, va, count);
478 		buf[count] = '\0';
479 		if(count > 0 && buf[count-1] == '\n')
480 			buf[--count] = '\0';
481 		if(count == 0)
482 			error(Ebadarg);
483 		if(strcmp(up->env->user, eve) != 0)
484 			error(Eperm);
485 		kstrdup(&eve, buf);
486 		return count;
487 	case Qhoststdout:
488 		return write(1, va, count);
489 	case Qhoststderr:
490 		return write(2, va, count);
491 	case Qjit:
492 		if(count >= sizeof(buf))
493 			count = sizeof(buf)-1;
494 		strncpy(buf, va, count);
495 		buf[count] = '\0';
496 		x = atoi(buf);
497 		if (x < 0 || x > 9)
498 			error(Ebadarg);
499 		cflag = x;
500 		return count;
501 	case Qsysname:
502 		if(count >= sizeof(buf))
503 			count = sizeof(buf)-1;
504 		strncpy(buf, va, count);
505 		buf[count] = '\0';
506 		kstrdup(&ossysname, buf);
507 		return count;
508 	}
509 	return 0;
510 }
511 
512 static int
513 sysconwrite(void *va, ulong count)
514 {
515 	Cmdbuf *cb;
516 	int e;
517 	cb = parsecmd(va, count);
518 	if(waserror()){
519 		free(cb);
520 		nexterror();
521 	}
522 	if(cb->nf == 0)
523 		error(Enoctl);
524 	if(strcmp(cb->f[0], "reboot") == 0){
525 		osreboot(rebootargv[0], rebootargv);
526 		error("reboot not supported");
527 	}else if(strcmp(cb->f[0], "halt") == 0){
528 		if(cb->nf > 1)
529 			e = atoi(cb->f[1]);
530 		else
531 			e = 0;
532 		cleanexit(e);		/* XXX ignored for the time being (and should be a string anyway) */
533 	}else if(strcmp(cb->f[0], "broken") == 0)
534 		keepbroken = 1;
535 	else if(strcmp(cb->f[0], "nobroken") == 0)
536 		keepbroken = 0;
537 	else if(strcmp(cb->f[0], "exdebug") == 0)
538 		exdebug = !exdebug;
539 	else
540 		error(Enoctl);
541 	poperror();
542 	free(cb);
543 	return count;
544 }
545 
546 Dev consdevtab = {
547 	'c',
548 	"cons",
549 
550 	consinit,
551 	consattach,
552 	conswalk,
553 	consstat,
554 	consopen,
555 	devcreate,
556 	consclose,
557 	consread,
558 	devbread,
559 	conswrite,
560 	devbwrite,
561 	devremove,
562 	devwstat
563 };
564 
565 static	ulong	randn;
566 
567 static void
568 seedrand(void)
569 {
570 	randomread((void*)&randn, sizeof(randn));
571 }
572 
573 int
574 nrand(int n)
575 {
576 	if(randn == 0)
577 		seedrand();
578 	randn = randn*1103515245 + 12345 + osusectime();
579 	return (randn>>16) % n;
580 }
581 
582 int
583 rand(void)
584 {
585 	nrand(1);
586 	return randn;
587 }
588 
589 ulong
590 truerand(void)
591 {
592 	ulong x;
593 
594 	randomread(&x, sizeof(x));
595 	return x;
596 }
597 
598 QLock grandomlk;
599 
600 void
601 _genrandomqlock(void)
602 {
603 	qlock(&grandomlk);
604 }
605 
606 
607 void
608 _genrandomqunlock(void)
609 {
610 	qunlock(&grandomlk);
611 }
612