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