xref: /inferno-os/emu/port/devcons.c (revision 4eb166cf184c1f102fb79e31b1465ea3e2021c39)
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 			switch(b){
112 			case 0x15:
113 				write(1, "^U\n", 3);
114 				break;
115 			default:
116 				write(1, &b, 1);
117 				break;
118 			}
119 		}
120 		qproduce(kbdq, &b, 1);
121 	}
122 	/* pexit("kbdslave", 0); */	/* not reached */
123 }
124 
125 void
126 gkbdputc(Queue *q, int ch)
127 {
128 	int n;
129 	Rune r;
130 	static uchar kc[5*UTFmax];
131 	static int nk, collecting = 0;
132 	char buf[UTFmax];
133 
134 	r = ch;
135 	if(r == Latin) {
136 		collecting = 1;
137 		nk = 0;
138 		return;
139 	}
140 	if(collecting) {
141 		int c;
142 		nk += runetochar((char*)&kc[nk], &r);
143 		c = latin1(kc, nk);
144 		if(c < -1)	/* need more keystrokes */
145 			return;
146 		collecting = 0;
147 		if(c == -1) {	/* invalid sequence */
148 			qproduce(q, kc, nk);
149 			return;
150 		}
151 		r = (Rune)c;
152 	}
153 	n = runetochar(buf, &r);
154 	if(n == 0)
155 		return;
156 	/* if(!isdbgkey(r)) */
157 		qproduce(q, buf, n);
158 }
159 
160 void
161 consinit(void)
162 {
163 	kbdq = qopen(512, 0, nil, nil);
164 	if(kbdq == 0)
165 		panic("no memory");
166 	lineq = qopen(2*1024, 0, nil, nil);
167 	if(lineq == 0)
168 		panic("no memory");
169 	gkbdq = qopen(512, 0, nil, nil);
170 	if(gkbdq == 0)
171 		panic("no memory");
172 	randominit();
173 }
174 
175 /*
176  *  return true if current user is eve
177  */
178 int
179 iseve(void)
180 {
181 	return strcmp(eve, up->env->user) == 0;
182 }
183 
184 static Chan*
185 consattach(char *spec)
186 {
187 	static int kp;
188 
189 	if(kp == 0 && !dflag) {
190 		kp = 1;
191 		kproc("kbd", kbdslave, 0, 0);
192 	}
193 	return devattach('c', spec);
194 }
195 
196 static Walkqid*
197 conswalk(Chan *c, Chan *nc, char **name, int nname)
198 {
199 	return devwalk(c, nc, name, nname, contab, nelem(contab), devgen);
200 }
201 
202 static int
203 consstat(Chan *c, uchar *db, int n)
204 {
205 	return devstat(c, db, n, contab, nelem(contab), devgen);
206 }
207 
208 static Chan*
209 consopen(Chan *c, int omode)
210 {
211 	c = devopen(c, omode, contab, nelem(contab), devgen);
212 	switch((ulong)c->qid.path) {
213 	case Qconsctl:
214 		incref(&kbd.ctl);
215 		break;
216 
217 	case Qscancode:
218 		qlock(&kbd.gq);
219 		if(gkscanq != nil || gkscanid == nil) {
220 			qunlock(&kbd.q);
221 			c->flag &= ~COPEN;
222 			if(gkscanq)
223 				error(Einuse);
224 			else
225 				error("not supported");
226 		}
227 		gkscanq = qopen(256, 0, nil, nil);
228 		qunlock(&kbd.gq);
229 		break;
230 
231 	case Qkprint:
232 		wlock(&kprintq.l);
233 		if(waserror()){
234 			wunlock(&kprintq.l);
235 			c->flag &= ~COPEN;
236 			nexterror();
237 		}
238 		if(kprintq.q != nil)
239 			error(Einuse);
240 		kprintq.q = qopen(32*1024, Qcoalesce, nil, nil);
241 		if(kprintq.q == nil)
242 			error(Enomem);
243 		qnoblock(kprintq.q, 1);
244 		poperror();
245 		wunlock(&kprintq.l);
246 		c->iounit = qiomaxatomic;
247 		break;
248 	}
249 	return c;
250 }
251 
252 static void
253 consclose(Chan *c)
254 {
255 	if((c->flag & COPEN) == 0)
256 		return;
257 
258 	switch((ulong)c->qid.path) {
259 	case Qconsctl:
260 		/* last close of control file turns off raw */
261 		if(decref(&kbd.ctl) == 0)
262 			kbd.raw = 0;
263 		break;
264 
265 	case Qscancode:
266 		qlock(&kbd.gq);
267 		if(gkscanq) {
268 			qfree(gkscanq);
269 			gkscanq = nil;
270 		}
271 		qunlock(&kbd.gq);
272 		break;
273 
274 	case Qkprint:
275 		wlock(&kprintq.l);
276 		qfree(kprintq.q);
277 		kprintq.q = nil;
278 		wunlock(&kprintq.l);
279 		break;
280 	}
281 }
282 
283 static long
284 consread(Chan *c, void *va, long n, vlong offset)
285 {
286 	ulong l;
287 	int i, send;
288 	char *p, buf[64], ch;
289 
290 	if(c->qid.type & QTDIR)
291 		return devdirread(c, va, n, contab, nelem(contab), devgen);
292 
293 	switch((ulong)c->qid.path) {
294 	default:
295 		error(Egreg);
296 
297 	case Qsysctl:
298 		return readstr(offset, va, n, VERSION);
299 
300 	case Qsysname:
301 		if(ossysname == nil)
302 			return 0;
303 		return readstr(offset, va, n, ossysname);
304 
305 	case Qrandom:
306 		return randomread(va, n);
307 
308 	case Qnotquiterandom:
309 		genrandom(va, n);
310 		return n;
311 
312 	case Qpin:
313 		p = "pin set";
314 		if(up->env->pgrp->pin == Nopin)
315 			p = "no pin";
316 		return readstr(offset, va, n, p);
317 
318 	case Qhostowner:
319 		return readstr(offset, va, n, eve);
320 
321 	case Qhoststdin:
322 		return read(0, va, n);	/* should be pread */
323 
324 	case Quser:
325 		return readstr(offset, va, n, up->env->user);
326 
327 	case Qjit:
328 		snprint(buf, sizeof(buf), "%d", cflag);
329 		return readstr(offset, va, n, buf);
330 
331 	case Qtime:
332 		snprint(buf, sizeof(buf), "%.lld", timeoffset + osusectime());
333 		return readstr(offset, va, n, buf);
334 
335 	case Qdrivers:
336 		p = malloc(READSTR);
337 		if(p == nil)
338 			error(Enomem);
339 		l = 0;
340 		for(i = 0; devtab[i] != nil; i++)
341 			l += snprint(p+l, READSTR-l, "#%C %s\n", devtab[i]->dc,  devtab[i]->name);
342 		if(waserror()){
343 			free(p);
344 			nexterror();
345 		}
346 		n = readstr(offset, va, n, p);
347 		poperror();
348 		free(p);
349 		return n;
350 
351 	case Qmemory:
352 		return poolread(va, n, offset);
353 
354 	case Qnull:
355 		return 0;
356 
357 	case Qmsec:
358 		return readnum(offset, va, n, osmillisec(), NUMSIZE);
359 
360 	case Qcons:
361 		qlock(&kbd.q);
362 		if(waserror()){
363 			qunlock(&kbd.q);
364 			nexterror();
365 		}
366 
367 		if(dflag)
368 			error(Enonexist);
369 
370 		while(!qcanread(lineq)) {
371 			if(qread(kbdq, &ch, 1) == 0)
372 				continue;
373 			send = 0;
374 			if(ch == 0){
375 				/* flush output on rawoff -> rawon */
376 				if(kbd.x > 0)
377 					send = !qcanread(kbdq);
378 			}else if(kbd.raw){
379 				kbd.line[kbd.x++] = ch;
380 				send = !qcanread(kbdq);
381 			}else{
382 				switch(ch){
383 				case '\b':
384 					if(kbd.x)
385 						kbd.x--;
386 					break;
387 				case 0x15:
388 					kbd.x = 0;
389 					break;
390 				case 0x04:
391 					send = 1;
392 					break;
393 				case '\n':
394 					send = 1;
395 				default:
396 					kbd.line[kbd.x++] = ch;
397 					break;
398 				}
399 			}
400 			if(send || kbd.x == sizeof kbd.line){
401 				qwrite(lineq, kbd.line, kbd.x);
402 				kbd.x = 0;
403 			}
404 		}
405 		n = qread(lineq, va, n);
406 		qunlock(&kbd.q);
407 		poperror();
408 		return n;
409 
410 	case Qscancode:
411 		if(offset == 0)
412 			return readstr(0, va, n, gkscanid);
413 		return qread(gkscanq, va, n);
414 
415 	case Qkeyboard:
416 		return qread(gkbdq, va, n);
417 
418 	case Qkprint:
419 		rlock(&kprintq.l);
420 		if(waserror()){
421 			runlock(&kprintq.l);
422 			nexterror();
423 		}
424 		n = qread(kprintq.q, va, n);
425 		poperror();
426 		runlock(&kprintq.l);
427 		return n;
428 	}
429 }
430 
431 static long
432 conswrite(Chan *c, void *va, long n, vlong offset)
433 {
434 	char buf[128], *a, ch;
435 	int x;
436 
437 	if(c->qid.type & QTDIR)
438 		error(Eperm);
439 
440 	switch((ulong)c->qid.path) {
441 	default:
442 		error(Egreg);
443 
444 	case Qcons:
445 		if(canrlock(&kprintq.l)){
446 			if(kprintq.q != nil){
447 				if(waserror()){
448 					runlock(&kprintq.l);
449 					nexterror();
450 				}
451 				qwrite(kprintq.q, va, n);
452 				poperror();
453 				runlock(&kprintq.l);
454 				return n;
455 			}
456 			runlock(&kprintq.l);
457 		}
458 		return write(1, va, n);
459 
460 	case Qsysctl:
461 		return sysconwrite(va, n);
462 
463 	case Qconsctl:
464 		if(n >= sizeof(buf))
465 			n = sizeof(buf)-1;
466 		strncpy(buf, va, n);
467 		buf[n] = 0;
468 		for(a = buf; a;){
469 			if(strncmp(a, "rawon", 5) == 0){
470 				kbd.raw = 1;
471 				/* clumsy hack - wake up reader */
472 				ch = 0;
473 				qwrite(kbdq, &ch, 1);
474 			} else if(strncmp(buf, "rawoff", 6) == 0){
475 				kbd.raw = 0;
476 			}
477 			if((a = strchr(a, ' ')) != nil)
478 				a++;
479 		}
480 		break;
481 
482 	case Qkeyboard:
483 		for(x=0; x<n; ) {
484 			Rune r;
485 			x += chartorune(&r, &((char*)va)[x]);
486 			gkbdputc(gkbdq, r);
487 		}
488 		break;
489 
490 	case Qnull:
491 		break;
492 
493 	case Qpin:
494 		if(up->env->pgrp->pin != Nopin)
495 			error("pin already set");
496 		if(n >= sizeof(buf))
497 			n = sizeof(buf)-1;
498 		strncpy(buf, va, n);
499 		buf[n] = '\0';
500 		up->env->pgrp->pin = atoi(buf);
501 		break;
502 
503 	case Qtime:
504 		if(n >= sizeof(buf))
505 			n = sizeof(buf)-1;
506 		strncpy(buf, va, n);
507 		buf[n] = '\0';
508 		timeoffset = strtoll(buf, 0, 0)-osusectime();
509 		break;
510 
511 	case Qhostowner:
512 		if(!iseve())
513 			error(Eperm);
514 		if(offset != 0 || n >= sizeof(buf))
515 			error(Ebadarg);
516 		memmove(buf, va, n);
517 		buf[n] = '\0';
518 		if(n > 0 && buf[n-1] == '\n')
519 			buf[--n] = '\0';
520 		if(n == 0)
521 			error(Ebadarg);
522 		/* renameuser(eve, buf); */
523 		/* renameproguser(eve, buf); */
524 		kstrdup(&eve, buf);
525 		kstrdup(&up->env->user, buf);
526 		break;
527 
528 	case Quser:
529 		if(!iseve())
530 			error(Eperm);
531 		if(offset != 0)
532 			error(Ebadarg);
533 		if(n <= 0 || n >= sizeof(buf))
534 			error(Ebadarg);
535 		strncpy(buf, va, n);
536 		buf[n] = '\0';
537 		if(n > 0 && buf[n-1] == '\n')
538 			buf[--n] = '\0';
539 		if(n == 0)
540 			error(Ebadarg);
541 		setid(buf, 0);
542 		break;
543 
544 	case Qhoststdout:
545 		return write(1, va, n);
546 
547 	case Qhoststderr:
548 		return write(2, va, n);
549 
550 	case Qjit:
551 		if(n >= sizeof(buf))
552 			n = sizeof(buf)-1;
553 		strncpy(buf, va, n);
554 		buf[n] = '\0';
555 		x = atoi(buf);
556 		if(x < 0 || x > 9)
557 			error(Ebadarg);
558 		cflag = x;
559 		break;
560 
561 	case Qsysname:
562 		if(offset != 0)
563 			error(Ebadarg);
564 		if(n < 0 || n >= sizeof(buf))
565 			error(Ebadarg);
566 		strncpy(buf, va, n);
567 		buf[n] = '\0';
568 		if(buf[n-1] == '\n')
569 			buf[n-1] = 0;
570 		kstrdup(&ossysname, buf);
571 		break;
572 	}
573 	return n;
574 }
575 
576 static int
577 sysconwrite(void *va, ulong count)
578 {
579 	Cmdbuf *cb;
580 	int e;
581 	cb = parsecmd(va, count);
582 	if(waserror()){
583 		free(cb);
584 		nexterror();
585 	}
586 	if(cb->nf == 0)
587 		error(Enoctl);
588 	if(strcmp(cb->f[0], "reboot") == 0){
589 		osreboot(rebootargv[0], rebootargv);
590 		error("reboot not supported");
591 	}else if(strcmp(cb->f[0], "halt") == 0){
592 		if(cb->nf > 1)
593 			e = atoi(cb->f[1]);
594 		else
595 			e = 0;
596 		cleanexit(e);		/* XXX ignored for the time being (and should be a string anyway) */
597 	}else if(strcmp(cb->f[0], "broken") == 0)
598 		keepbroken = 1;
599 	else if(strcmp(cb->f[0], "nobroken") == 0)
600 		keepbroken = 0;
601 	else if(strcmp(cb->f[0], "exdebug") == 0)
602 		exdebug = !exdebug;
603 	else
604 		error(Enoctl);
605 	poperror();
606 	free(cb);
607 	return count;
608 }
609 
610 Dev consdevtab = {
611 	'c',
612 	"cons",
613 
614 	consinit,
615 	consattach,
616 	conswalk,
617 	consstat,
618 	consopen,
619 	devcreate,
620 	consclose,
621 	consread,
622 	devbread,
623 	conswrite,
624 	devbwrite,
625 	devremove,
626 	devwstat
627 };
628 
629 static	ulong	randn;
630 
631 static void
632 seedrand(void)
633 {
634 	randomread((void*)&randn, sizeof(randn));
635 }
636 
637 int
638 nrand(int n)
639 {
640 	if(randn == 0)
641 		seedrand();
642 	randn = randn*1103515245 + 12345 + osusectime();
643 	return (randn>>16) % n;
644 }
645 
646 int
647 rand(void)
648 {
649 	nrand(1);
650 	return randn;
651 }
652 
653 ulong
654 truerand(void)
655 {
656 	ulong x;
657 
658 	randomread(&x, sizeof(x));
659 	return x;
660 }
661 
662 QLock grandomlk;
663 
664 void
665 _genrandomqlock(void)
666 {
667 	qlock(&grandomlk);
668 }
669 
670 
671 void
672 _genrandomqunlock(void)
673 {
674 	qunlock(&grandomlk);
675 }
676