cae36a52 |
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 | } |