add linux_emul base, reorganize docs
[openbsd_emul.git] / linux_emul_base / ptydev.c
1 #include <u.h>
2 #include <libc.h>
3 #include <ureg.h>
4 #include "dat.h"
5 #include "fns.h"
6 #include "linux.h"
7
8 typedef struct Termios Termios;
9 typedef struct Winsize Winsize;
10 typedef struct Cbuf Cbuf;
11 typedef struct Tty Tty;
12 typedef struct Pty Pty;
13 typedef struct Ptyfile Ptyfile;
14
15 /* cflags */
16 enum {
17 IGNBRK = 01,
18 BRKINT = 02,
19 IGNPAR = 04,
20 PARMRK = 010,
21 INPCK = 020,
22 ISTRIP = 040,
23 INLCR = 0100,
24 IGNCR = 0200,
25 ICRNL = 0400,
26 IUCLC = 01000,
27 IXON = 02000,
28 IXANY = 04000,
29 IXOFF = 010000,
30 IMAXBEL = 020000,
31 IUTF8 = 040000,
32 };
33
34 /* oflags */
35 enum {
36 OPOST = 01,
37 OLCUC = 02,
38 ONLCR = 04,
39 OCRNL = 010,
40 ONOCR = 020,
41 ONLRET = 040,
42 OFILL = 0100,
43 OFDEL = 0200,
44 NLDLY = 0400,
45 NL0 = 0,
46 NL1 = 0400,
47 CRDLY = 03000,
48 CR0 = 0,
49 CR1 = 01000,
50 CR2 = 02000,
51 CR3 = 03000,
52 TABDLY = 014000,
53 TAB0 = 0,
54 TAB1 = 04000,
55 TAB2 = 010000,
56 TAB3 = 014000,
57 XTABS = 014000,
58 BSDLY = 020000,
59 BS0 = 0,
60 BS1 = 020000,
61 VTDLY = 040000,
62 VT0 = 0,
63 VT1 = 040000,
64 FFDLY = 0100000,
65 FF0 = 0,
66 FF1 = 0100000,
67 };
68
69 /* cflags */
70 enum {
71 CSIZE = 060,
72 CS5 = 0,
73 CS6 = 020,
74 CS7 = 040,
75 CS8 = 060,
76 CREAD = 0200,
77 CLOCAL = 04000,
78 HUPCL = 02000,
79 };
80
81 /* lflags */
82 enum {
83 ISIG = 01,
84 ICANON = 02,
85 XCASE = 04,
86 ECHO = 010,
87 ECHOE = 020,
88 ECHOK = 040,
89 ECHONL = 0100,
90 NOFLSH = 0200,
91 TOSTOP = 0400,
92 ECHOCTL = 01000,
93 ECHOPRT = 02000,
94 ECHOKE = 04000,
95 FLUSH0 = 010000,
96 PENDIN = 040000,
97 IEXTEN = 0100000,
98 };
99
100 /* cc */
101 enum {
102 VINTR = 0,
103 VQUIT,
104 VERASE,
105 VKILL,
106 VEOF,
107 VTIME,
108 VMIN,
109 VSWTCH,
110 VSTART,
111 VSTOP,
112 VSUSP,
113 VEOL,
114 VREPRINT,
115 VDISCARD,
116 VERASEW,
117 VLNEXT,
118 VEOL2,
119 NCCS,
120 };
121
122 struct Termios
123 {
124 int iflag; /* input modes */
125 int oflag; /* output modes */
126 int cflag; /* control modes */
127 int lflag; /* local modes */
128 uchar cline;
129 uchar cc[NCCS]; /* control characters */
130 };
131
132 struct Winsize
133 {
134 ushort row;
135 ushort col;
136 ushort px;
137 ushort py;
138 };
139
140 struct Cbuf
141 {
142 int rp;
143 int wp;
144 char cb[256];
145 };
146
147 struct Tty
148 {
149 Termios t;
150 Winsize winsize;
151
152 int escaped;
153 int eol;
154
155 int pgid;
156
157 Cbuf wb;
158 Cbuf rb;
159 };
160
161 struct Pty
162 {
163 Tty;
164
165 int id;
166 int closed;
167 int locked;
168
169 struct {
170 Uwaitq r;
171 Uwaitq w;
172 } q[2];
173
174 Ref;
175 QLock;
176 };
177
178 struct Ptyfile
179 {
180 Ufile;
181
182 Pty *pty;
183
184 int master;
185 };
186
187 static Pty *ptys[64];
188
189 int cbput(Cbuf *b, char c)
190 {
191 int x;
192 x = b->wp+1&(sizeof(b->cb)-1);
193 if(x == b->rp)
194 return -1;
195 b->cb[b->wp] = c;
196 b->wp = x;
197 return 0;
198 }
199
200 int cbget(Cbuf *b)
201 {
202 char c;
203 if(b->rp == b->wp)
204 return -1;
205 c = b->cb[b->rp];
206 b->rp = (b->rp + 1) & (sizeof(b->cb)-1);
207 return c;
208 }
209
210 int cbfill(Cbuf *b)
211 {
212 return (b->wp - b->rp) & (sizeof(b->cb)-1);
213 }
214
215 void ttyinit(Tty *t)
216 {
217 memset(&t->t, 0, sizeof(t->t));
218
219 t->t.iflag = ICRNL;
220 t->t.oflag = OPOST|ONLCR|NL0|CR0|TAB0|BS0|VT0|FF0;
221 t->t.lflag = ICANON|IEXTEN|ECHO|ECHOE|ECHOK;
222
223 if(current)
224 t->pgid = current->pgid;
225 }
226
227 int ttywrite(Tty *t, char *buf, int len)
228 {
229 char *p, *e;
230
231 for(p=buf, e=buf+len; p<e; p++){
232 char c;
233
234 c = *p;
235 if((t->t.oflag & OPOST) == 0) {
236 if(cbput(&t->wb, c) < 0)
237 break;
238 continue;
239 }
240 switch(c) {
241 case '\n':
242 if(t->t.oflag & ONLCR) {
243 if(cbput(&t->wb, '\r') < 0)
244 goto done;
245 }
246 if(cbput(&t->wb, c) < 0)
247 goto done;
248 break;
249
250 case '\t':
251 if((t->t.oflag & TAB3) == TAB3) {
252 int tab;
253
254 tab = 8;
255 while(tab--)
256 cbput(&t->wb, ' ');
257 break;
258 }
259 /* Fall Through */
260 default:
261 if(t->t.oflag & OLCUC)
262 if(c >= 'a' && c <= 'z')
263 c = 'A' + (c-'a');
264 if(cbput(&t->wb, c) < 0)
265 goto done;
266 }
267 }
268 done:
269 return p-buf;
270 }
271
272 int ttycanread(Tty *t, int *n)
273 {
274 int x;
275
276 x = cbfill(&t->rb);
277 if(t->t.lflag & ICANON){
278 if(t->eol == 0)
279 return 0;
280 } else {
281 if(x == 0)
282 return 0;
283 }
284 if(n != nil)
285 *n = x;
286 return 1;
287 }
288
289 int ttyread(Tty *t, char *buf, int len)
290 {
291 char *p, *e;
292
293 if((t->t.lflag & ICANON) && t->eol == 0)
294 return 0;
295
296 for(p=buf, e=buf+len; p<e; p++){
297 int c;
298
299 if((c = cbget(&t->rb)) < 0)
300 break;
301
302 if(c==0 || c=='\n'){
303 t->eol--;
304 if(t->t.lflag & ICANON){
305 if(c == 0)
306 break;
307 *p++ = c;
308 break;
309 }
310 }
311
312 *p = c;
313 }
314 return p-buf;
315 }
316
317
318 static void
319 echo(Tty *t, char c)
320 {
321 if(t->t.lflag & ECHO) {
322 switch(c) {
323 case '\r':
324 if(t->t.oflag & OCRNL) {
325 cbput(&t->wb, '\n');
326 break;
327 }
328 cbput(&t->wb, c);
329 break;
330 case '\n':
331 if(t->t.oflag & ONLCR)
332 cbput(&t->wb, '\r');
333 cbput(&t->wb, '\n');
334 break;
335 case '\t':
336 if((t->t.oflag & TAB3) == TAB3) {
337 int tab;
338
339 tab = 8;
340 while(tab--)
341 cbput(&t->wb, ' ');
342 break;
343 }
344 /* Fall Through */
345 default:
346 cbput(&t->wb, c);
347 break;
348 }
349 }
350 else
351 if(c == '\n' && (t->t.lflag&(ECHONL|ICANON)) == (ECHONL|ICANON)) {
352 if(t->t.oflag & ONLCR)
353 cbput(&t->wb, '\r');
354 cbput(&t->wb, '\n');
355 }
356 }
357
358 static int
359 bs(Tty *t)
360 {
361 char c;
362 int x;
363
364 if(cbfill(&t->rb) == 0)
365 return 0;
366 x = (t->rb.wp-1)&(sizeof(t->rb.cb)-1);
367 c = t->rb.cb[x];
368 if(c == 0 || c == '\n')
369 return 0;
370 t->rb.wp = x;
371 echo(t, '\b');
372 if(t->t.lflag & ECHOE) {
373 echo(t, ' ');
374 echo(t, '\b');
375 }
376 return 1;
377 }
378
379 int ttywriteinput(Tty *t, char *buf, int len)
380 {
381 char *p, *e;
382
383 for(p=buf, e=buf+len; p<e; p++){
384 char c;
385
386 c = *p;
387
388 if(t->t.iflag & ISTRIP)
389 c &= 0177;
390
391 if((t->t.iflag & IXON) && c == t->t.cc[VSTOP]) {
392 p++;
393 break;
394 }
395
396 switch(c) {
397 case '\r':
398 if(t->t.iflag & IGNCR)
399 continue;
400 if(t->t.iflag & ICRNL)
401 c = '\n';
402 break;
403 case '\n':
404 if(t->t.iflag&INLCR)
405 c = '\r';
406 break;
407 }
408
409 if(t->t.lflag & ISIG) {
410 if(c == t->t.cc[VINTR]){
411 if(t->pgid > 0)
412 sys_kill(-t->pgid, SIGINT);
413 continue;
414 }
415 if(c == t->t.cc[VQUIT]){
416 if(t->pgid > 0)
417 sys_kill(-t->pgid, SIGQUIT);
418 continue;
419 }
420 }
421
422 if((t->t.lflag & ICANON) && t->escaped == 0) {
423 if(c == t->t.cc[VERASE]) {
424 bs(t);
425 continue;
426 }
427 if(c == t->t.cc[VKILL]) {
428 while(bs(t))
429 ;
430 if(t->t.lflag & ECHOK)
431 echo(t, '\n');
432 continue;
433 }
434 }
435
436 if(t->escaped == 0 && (c == t->t.cc[VEOF] || c == '\n'))
437 t->eol++;
438
439 if((t->t.lflag & ICANON) == 0) {
440 echo(t, c);
441 cbput(&t->rb, c);
442 continue;
443 }
444
445 if(t->escaped)
446 echo(t, '\b');
447
448 if(c != t->t.cc[VEOF])
449 echo(t, c);
450
451 if(c != '\\') {
452 if(c == t->t.cc[VEOF])
453 c = 0;
454 cbput(&t->rb, c);
455 t->escaped = 0;
456 continue;
457 }
458 if(t->escaped) {
459 cbput(&t->rb, '\\');
460 t->escaped = 0;
461 }
462 else
463 t->escaped = 1;
464 }
465
466 return p-buf;
467 }
468
469 int ttycanreadoutput(Tty *t, int *n)
470 {
471 int x;
472
473 x = cbfill(&t->wb);
474 if(n != nil)
475 *n = x;
476 return x > 0 ? 1 : 0;
477 }
478
479 int ttyreadoutput(Tty *t, char *buf, int len)
480 {
481 char *p, *e;
482
483 for(p=buf, e=buf+len; p<e; p++){
484 int c;
485
486 if((c = cbget(&t->wb)) < 0)
487 break;
488 *p = c;
489 }
490 return p-buf;
491 }
492
493 static int
494 pollpty(Ufile *f, void *tab)
495 {
496 Ptyfile *p = (Ptyfile*)f;
497 int err;
498 int n;
499
500 if(p->pty == nil)
501 return 0;
502
503 qlock(p->pty);
504 if(p->master){
505 pollwait(p, &p->pty->q[1].r, tab);
506 n = ttycanreadoutput(p->pty, nil);
507 } else {
508 pollwait(p, &p->pty->q[0].r, tab);
509 n = ttycanread(p->pty, nil);
510 }
511 err = POLLOUT;
512 if(n){
513 err |= POLLIN;
514 } else if(p->master==0 && p->pty->closed){
515 err |= (POLLIN | POLLHUP);
516 }
517 qunlock(p->pty);
518
519 return err;
520 }
521
522 static int
523 readpty(Ufile *f, void *data, int len, vlong)
524 {
525 int err;
526 Ptyfile *p = (Ptyfile*)f;
527
528 if(p->pty == nil)
529 return -EPERM;
530 qlock(p->pty);
531 for(;;){
532 if(p->master){
533 err = ttycanreadoutput(p->pty, nil);
534 } else {
535 err = ttycanread(p->pty, nil);
536 }
537 if(err > 0){
538 if(p->master){
539 err = ttyreadoutput(p->pty, (char*)data, len);
540 }else{
541 err = ttyread(p->pty, (char*)data, len);
542 }
543 } else {
544 if(p->master == 0 && p->pty->closed){
545 err = -EIO;
546 } else if(p->mode & O_NONBLOCK){
547 err = -EAGAIN;
548 } else {
549 if((err = sleepq(&p->pty->q[p->master].r, p->pty, 1)) == 0)
550 continue;
551 }
552 }
553 wakeq(&p->pty->q[!p->master].w, MAXPROC);
554 break;
555 }
556 qunlock(p->pty);
557
558 return err;
559 }
560
561 static int
562 writepty(Ufile *f, void *data, int len, vlong)
563 {
564 Ptyfile *p = (Ptyfile*)f;
565 int err;
566
567 if(p->pty == nil)
568 return -EPERM;
569 if(len == 0)
570 return len;
571
572 qlock(p->pty);
573 for(;;){
574 if(p->pty->closed){
575 err = -EIO;
576 break;
577 }
578 if(p->master){
579 err = ttywriteinput(p->pty, (char*)data, len);
580 } else{
581 err = ttywrite(p->pty, (char*)data, len);
582 }
583 if(err == 0){
584 if((err = sleepq(&p->pty->q[p->master].w, p->pty, 1)) == 0)
585 continue;
586 } else {
587 if(ttycanread(p->pty, nil))
588 wakeq(&p->pty->q[0].r, MAXPROC);
589 if(ttycanreadoutput(p->pty, nil))
590 wakeq(&p->pty->q[1].r, MAXPROC);
591 }
592 break;
593 }
594 qunlock(p->pty);
595
596 return err;
597 }
598
599 static int
600 closepty(Ufile *f)
601 {
602 Ptyfile *p = (Ptyfile*)f;
603
604 if(p->pty == nil)
605 return 0;
606
607 qlock(p->pty);
608 if(p->master)
609 p->pty->closed = 1;
610 if(!decref(p->pty)){
611 ptys[p->pty->id] = nil;
612 qunlock(p->pty);
613 free(p->pty);
614 } else {
615 wakeq(&p->pty->q[0].r, MAXPROC);
616 wakeq(&p->pty->q[0].w, MAXPROC);
617 wakeq(&p->pty->q[1].r, MAXPROC);
618 wakeq(&p->pty->q[1].w, MAXPROC);
619 qunlock(p->pty);
620 }
621 return 0;
622 }
623
624 static int
625 changetty(Ptyfile *tty)
626 {
627 Ufile *old;
628
629 if(old = gettty()){
630 putfile(old);
631 return (old == tty) ? 0 : -EPERM;
632 }
633 tty->pty->pgid = current->pgid;
634 settty(tty);
635 return 0;
636 }
637
638 static int
639 ioctlpty(Ufile *f, int cmd, void *arg)
640 {
641 Ptyfile *p = (Ptyfile*)f;
642 int err, pid;
643
644 if(p->pty == nil)
645 return -ENOTTY;
646
647 trace("ioctlpty(%s, %lux, %p)", p->path, (ulong)cmd, arg);
648
649 err = 0;
650 qlock(p->pty);
651 switch(cmd){
652 default:
653 trace("ioctlpty: unknown: 0x%x", cmd);
654 err = -ENOTTY;
655 break;
656
657 case 0x5401: /* TCGETS */
658 memmove(arg, &p->pty->t, sizeof(Termios));
659 break;
660
661 case 0x5402: /* TCSETS */
662 case 0x5403: /* TCSETSW */
663 case 0x5404: /* TCSETSF */
664 memmove(&p->pty->t, arg, sizeof(Termios));
665 break;
666
667 case 0x5422: // TIOCNOTTY
668 if((f = gettty()) && (f != p)){
669 putfile(f);
670 err = -ENOTTY;
671 break;
672 }
673 settty(nil);
674 break;
675
676 case 0x540E: // TIOCSCTTY
677 err = changetty(p);
678 break;
679
680 case 0x540F: // TIOCGPGRP
681 *(int*)arg = p->pty->pgid;
682 break;
683
684 case 0x5410: // TIOCSPGRP
685 p->pty->pgid = *(int*)arg;
686 break;
687
688 case 0x5413: // TIOCGWINSZ
689 memmove(arg, &p->pty->winsize, sizeof(Winsize));
690 break;
691
692 case 0x5414: // TIOCSWINSZ
693 if(memcmp(&p->pty->winsize, arg, sizeof(Winsize)) == 0)
694 break;
695 memmove(&p->pty->winsize, arg, sizeof(Winsize));
696 if((pid = p->pty->pgid) > 0){
697 qunlock(p->pty);
698
699 sys_kill(-pid, SIGWINCH);
700 return 0;
701 }
702 break;
703 case 0x40045431: // TIOCSPTLCK
704 if(p->master)
705 p->pty->locked = *(int*)arg;
706 break;
707
708 case 0x80045430:
709 *(int*)arg = p->pty->id;
710 break;
711
712 case 0x541B:
713 if(arg == nil)
714 break;
715 if(p->master){
716 ttycanreadoutput(p->pty, &err);
717 } else {
718 ttycanread(p->pty, &err);
719 }
720 if(err < 0){
721 *((int*)arg) = 0;
722 break;
723 }
724 *((int*)arg) = err;
725 err = 0;
726 break;
727 }
728 qunlock(p->pty);
729
730 return err;
731 }
732
733 static int
734 openpty(char *path, int mode, int perm, Ufile **pf)
735 {
736 Pty *pty;
737 Ptyfile *p;
738 int id;
739 int master;
740
741 USED(perm);
742
743 if(strcmp("/dev/tty", path)==0){
744 if(*pf = gettty())
745 return 0;
746 return -ENOTTY;
747 } else if(strcmp("/dev/pts", path)==0){
748 pty = nil;
749 master = -1;
750 } else if(strcmp("/dev/ptmx", path)==0){
751 master = 1;
752 for(id=0; id<nelem(ptys); id++){
753 if(ptys[id] == nil)
754 break;
755 }
756 if(id == nelem(ptys))
757 return -EBUSY;
758
759 pty = kmallocz(sizeof(*pty), 1);
760 pty->ref = 1;
761
762 ttyinit(pty);
763
764 ptys[pty->id = id] = pty;
765 } else {
766 master = 0;
767 if(strncmp("/dev/pts/", path, 9) != 0)
768 return -ENOENT;
769 id = atoi(path + 9);
770 if(id < 0 || id >= nelem(ptys))
771 return -ENOENT;
772 if((pty = ptys[id]) == nil)
773 return -ENOENT;
774
775 qlock(pty);
776 if(pty->closed || pty->locked){
777 qunlock(pty);
778 return -EIO;
779 }
780 incref(pty);
781 qunlock(pty);
782 }
783
784 p = kmallocz(sizeof(*p), 1);
785 p->dev = PTYDEV;
786 p->ref = 1;
787 p->fd = -1;
788 p->mode = mode;
789 p->path = kstrdup(path);
790 p->pty = pty;
791 p->master = master;
792
793 if(!master && !(mode & O_NOCTTY))
794 changetty(p);
795
796 *pf = p;
797
798 return 0;
799 }
800
801 static int
802 readdirpty(Ufile *f, Udirent **pd)
803 {
804 Ptyfile *p = (Ptyfile*)f;
805 int i, n;
806
807 *pd = nil;
808 if(p->pty != nil)
809 return -EPERM;
810 n = 0;
811 for(i=0; i<nelem(ptys); i++){
812 char buf[12];
813
814 if(ptys[i] == nil)
815 continue;
816 snprint(buf, sizeof(buf), "%d", i);
817 if((*pd = newdirent(f->path, buf, S_IFCHR | 0666)) == nil)
818 break;
819 pd = &((*pd)->next);
820 n++;
821 }
822 return n;
823 }
824
825 static int
826 fstatpty(Ufile *f, Ustat *s)
827 {
828 Ptyfile *p = (Ptyfile*)f;
829
830 if(p->pty != nil){
831 s->mode = 0666 | S_IFCHR;
832 if(p->master){
833 s->rdev = 5<<8 | 2;
834 } else {
835 s->rdev = 3<<8;
836 }
837 } else {
838 s->mode = 0777 | S_IFDIR;
839 s->rdev = 0;
840 }
841 s->ino = hashpath(p->path);
842 s->dev = 0;
843 s->uid = current->uid;
844 s->gid = current->gid;
845 s->size = 0;
846 s->atime = s->mtime = s->ctime = boottime/1000000000LL;
847 return 0;
848 };
849
850 static int
851 statpty(char *path, int, Ustat *s)
852 {
853 if(strcmp("/dev/tty", path)==0){
854 s->mode = 0666 | S_IFCHR;
855 } else if(strcmp("/dev/ptmx", path)==0){
856 s->mode = 0666 | S_IFCHR;
857 s->rdev = 5<<8 | 2;
858 } else if(strcmp("/dev/pts", path)==0){
859 s->mode = 0777 | S_IFDIR;
860 } else if(strncmp("/dev/pts/", path, 9)==0){
861 int id;
862
863 id = atoi(path + 9);
864 if(id < 0 || id >= nelem(ptys))
865 return -ENOENT;
866 if(ptys[id] == nil)
867 return -ENOENT;
868
869 s->mode = 0666 | S_IFCHR;
870 s->rdev = 3<<8;
871 } else {
872 return -ENOENT;
873 }
874 s->ino = hashpath(path);
875 s->uid = current->uid;
876 s->gid = current->gid;
877 s->size = 0;
878 s->atime = s->mtime = s->ctime = boottime/1000000000LL;
879 return 0;
880 }
881
882 static int
883 chmodpty(char *path, int mode)
884 {
885 USED(path);
886 USED(mode);
887
888 return 0;
889 }
890
891 static int
892 chownpty(char *path, int uid, int gid, int link)
893 {
894 USED(path);
895 USED(uid);
896 USED(gid);
897 USED(link);
898
899 return 0;
900 }
901
902 static int
903 fchmodpty(Ufile *f, int mode)
904 {
905 USED(f);
906 USED(mode);
907
908 return 0;
909 }
910
911 static int
912 fchownpty(Ufile *f, int uid, int gid)
913 {
914 USED(f);
915 USED(uid);
916 USED(gid);
917
918 return 0;
919 }
920
921 static Udev ptydev =
922 {
923 .open = openpty,
924 .read = readpty,
925 .write = writepty,
926 .poll = pollpty,
927 .close = closepty,
928 .readdir = readdirpty,
929 .ioctl = ioctlpty,
930 .fstat = fstatpty,
931 .stat = statpty,
932 .fchmod = fchmodpty,
933 .fchown = fchownpty,
934 .chmod = chmodpty,
935 .chown = chownpty,
936 };
937
938 void ptydevinit(void)
939 {
940 devtab[PTYDEV] = &ptydev;
941 fsmount(&ptydev, "/dev/pts");
942 fsmount(&ptydev, "/dev/ptmx");
943 fsmount(&ptydev, "/dev/tty");
944 }