1 /* pty.c - pseudo terminal driver Author: Kees J. Bot
3 * PTYs can be seen as a bidirectional pipe with TTY
4 * input and output processing. For example a simple rlogin session:
6 * keyboard -> rlogin -> in.rld -> /dev/ptypX -> /dev/ttypX -> shell
7 * shell -> /dev/ttypX -> /dev/ptypX -> in.rld -> rlogin -> screen
9 * This file takes care of copying data between the tty/pty device pairs and
10 * the open/read/write/close calls on the pty devices. The TTY task takes
11 * care of the input and output processing (interrupt, backspace, raw I/O,
12 * etc.) using the pty_slave_read() and pty_slave_write() functions as the
13 * "keyboard" and "screen" functions of the ttypX devices.
14 * Be careful when reading this code, the terms "reading" and "writing" are
15 * used both for the tty (slave) and the pty (master) end of the pseudo tty.
16 * Writes to one end are to be read at the other end and vice-versa.
18 * In addition to the above, PTY service now also supports Unix98 pseudo-
19 * terminal pairs, thereby allowing non-root users to allocate pseudoterminals.
20 * It requires the presence for PTYFS for this, and supports only old-style
21 * ptys when PTYFS is not running. For Unix98 ptys, the general idea is that a
22 * userland program opens a pty master by opening /dev/ptmx through the use of
23 * posxix_openpt(3). A slave node is allocated on PTYFS when the program calls
24 * grantpt(3) on the master. The program can then obtain the path name for the
25 * slave end through ptsname(3), and open the slave end using this path.
27 * Implementation-wise, the Unix98 and non-Unix98 pseudoterminals share the
28 * same pool of data structures, but use different ranges of minor numbers.
29 * Access to the two types may not be mixed, and thus, some parts of the code
30 * have checks to make sure a traditional slave is not opened for a master
31 * allocated through /dev/ptmx, etcetera.
34 #include <minix/drivers.h>
38 #include <sys/termios.h>
43 /* Device node attributes used for Unix98 slave nodes. */
44 #define UNIX98_MODE (S_IFCHR | 0620) /* crw--w---- */
46 #define UNIX98_MASTER(index) (UNIX98_MINOR + (index) * 2)
47 #define UNIX98_SLAVE(index) (UNIX98_MINOR + (index) * 2 + 1)
49 /* PTY bookkeeping structure, one per pty/tty pair. */
51 tty_t
*tty
; /* associated TTY structure */
52 char state
; /* flags: busy, closed, ... */
54 /* Read call on master (/dev/ptypX). */
55 endpoint_t rdcaller
; /* process making the call, or NONE if none */
56 cdev_id_t rdid
; /* ID of suspended read request */
57 cp_grant_id_t rdgrant
; /* grant for reader's address space */
58 size_t rdleft
; /* # bytes yet to be read */
59 size_t rdcum
; /* # bytes written so far */
61 /* Write call to master (/dev/ptypX). */
62 endpoint_t wrcaller
; /* process making the call, or NONE if none*/
63 cdev_id_t wrid
; /* ID of suspended write request */
64 cp_grant_id_t wrgrant
; /* grant for writer's address space */
65 size_t wrleft
; /* # bytes yet to be written */
66 size_t wrcum
; /* # bytes written so far */
69 int ocount
; /* # characters in the buffer */
70 char *ohead
, *otail
; /* head and tail of the circular buffer */
71 char obuf
[TTY_OUT_BYTES
];
72 /* buffer for bytes going to the pty reader */
75 unsigned int select_ops
; /* Which operations do we want to know about? */
76 endpoint_t select_proc
; /* Who wants to know about it? */
77 devminor_t select_minor
; /* Which minor was being selected on? */
80 #define TTY_ACTIVE 0x01 /* tty is open/active */
81 #define PTY_ACTIVE 0x02 /* pty is open/active */
82 #define TTY_CLOSED 0x04 /* tty side has closed down */
83 #define PTY_CLOSED 0x08 /* pty side has closed down */
84 #define PTY_UNIX98 0x10 /* pty pair is Unix98 */
85 #define PTY_PKTMODE 0x20 /* pty side is in packet mode (TIOCPKT) */
87 static pty_t pty_table
[NR_PTYS
]; /* PTY bookkeeping */
89 static void pty_start(pty_t
*pp
);
90 static void pty_finish(pty_t
*pp
);
92 static int pty_master_open(devminor_t minor
, int access
,
93 endpoint_t user_endpt
);
94 static int pty_master_close(devminor_t minor
);
95 static ssize_t
pty_master_read(devminor_t minor
, u64_t position
,
96 endpoint_t endpt
, cp_grant_id_t grant
, size_t size
, int flags
,
98 static ssize_t
pty_master_write(devminor_t minor
, u64_t position
,
99 endpoint_t endpt
, cp_grant_id_t grant
, size_t size
, int flags
,
101 static int pty_master_ioctl(devminor_t minor
, unsigned long request
,
102 endpoint_t endpt
, cp_grant_id_t grant
, int flags
,
103 endpoint_t user_endpt
, cdev_id_t id
);
104 static int pty_master_cancel(devminor_t minor
, endpoint_t endpt
, cdev_id_t id
);
105 static int pty_master_select(devminor_t minor
, unsigned int ops
,
108 static struct chardriver pty_master_tab
= {
109 .cdr_open
= pty_master_open
,
110 .cdr_close
= pty_master_close
,
111 .cdr_read
= pty_master_read
,
112 .cdr_write
= pty_master_write
,
113 .cdr_ioctl
= pty_master_ioctl
,
114 .cdr_cancel
= pty_master_cancel
,
115 .cdr_select
= pty_master_select
118 /*===========================================================================*
120 *===========================================================================*/
121 static tty_t
*get_free_pty(void)
123 /* Return a pointer to a free tty structure, or NULL if no tty is free. */
127 for (tp
= &tty_table
[0]; tp
< &tty_table
[NR_PTYS
]; tp
++) {
130 if (!(pp
->state
& (PTY_ACTIVE
| TTY_ACTIVE
)))
137 /*===========================================================================*
139 *===========================================================================*/
140 static int pty_master_open(devminor_t minor
, int UNUSED(access
),
141 endpoint_t
UNUSED(user_endpt
))
147 if (minor
== PTMX_MINOR
) {
148 /* /dev/ptmx acts as a cloning device. We return a free PTY master and
149 * mark it as a UNIX98 type.
151 if ((tp
= get_free_pty()) == NULL
)
152 return EAGAIN
; /* POSIX says this is the right error code */
154 /* The following call has two purposes. First, we check right here
155 * whether PTYFS is running at all; if not, the PTMX device cannot be
156 * opened at all and userland can fall back to other allocation
157 * methods right away. Second, in the exceptional case that the PTY
158 * service is restarted while PTYFS keeps running, PTYFS may expose
159 * stale slave nodes, which are a security hole if not removed as soon
160 * as a new PTY pair is allocated.
162 if (ptyfs_clear(tp
->tty_index
) != OK
)
166 pp
->state
|= PTY_UNIX98
;
168 minor
= UNIX98_MASTER(tp
->tty_index
);
170 r
= CDEV_CLONED
| minor
;
172 /* There is no way to open Unix98 masters directly, except by messing
173 * with mknod. We disallow such tricks altogether, and thus, the rest
174 * of the code deals with opening a non-Unix98 master only.
176 if (minor
< PTYPX_MINOR
|| minor
>= PTYPX_MINOR
+ NR_PTYS
)
179 if ((tp
= line2tty(minor
)) == NULL
)
183 /* For non-Unix98 PTYs, we allow the slave to be opened before the
184 * master, but the master may be opened only once. This is how userland
185 * is able to find a free non-Unix98 PTY pair.
187 if (pp
->state
& PTY_ACTIVE
)
189 assert(!(pp
->state
& PTY_UNIX98
));
194 pp
->state
|= PTY_ACTIVE
;
202 /*===========================================================================*
204 *===========================================================================*/
205 static void pty_reset(tty_t
*tp
)
207 /* Both sides of a PTY pair have been closed. Clean up its state. */
212 /* For Unix98 pairs, clean up the Unix98 slave node. It may never have been
213 * allocated, but we don't care. Ignore failures altogether.
215 if (pp
->state
& PTY_UNIX98
)
216 (void)ptyfs_clear(tp
->tty_index
);
221 /*===========================================================================*
223 *===========================================================================*/
224 static int pty_master_close(devminor_t minor
)
229 if ((tp
= line2tty(minor
)) == NULL
)
233 if ((pp
->state
& (TTY_ACTIVE
| TTY_CLOSED
)) != TTY_ACTIVE
) {
236 pp
->state
|= PTY_CLOSED
;
237 tp
->tty_termios
.c_ospeed
= B0
; /* cause EOF on slave side */
238 sigchar(tp
, SIGHUP
, 1);
244 /*===========================================================================*
246 *===========================================================================*/
247 static ssize_t
pty_master_read(devminor_t minor
, u64_t
UNUSED(position
),
248 endpoint_t endpt
, cp_grant_id_t grant
, size_t size
, int flags
,
255 if ((tp
= line2tty(minor
)) == NULL
)
259 /* Check, store information on the reader, do I/O. */
260 if (pp
->state
& TTY_CLOSED
)
263 if (pp
->rdcaller
!= NONE
|| pp
->rdleft
!= 0 || pp
->rdcum
!= 0)
269 pp
->rdcaller
= endpt
;
277 if (pp
->rdleft
== 0) {
279 return EDONTREPLY
; /* already done */
282 if (flags
& CDEV_NONBLOCK
) {
283 r
= pp
->rdcum
> 0 ? pp
->rdcum
: EAGAIN
;
284 pp
->rdleft
= pp
->rdcum
= 0;
289 return EDONTREPLY
; /* do suspend */
292 /*===========================================================================*
294 *===========================================================================*/
295 static ssize_t
pty_master_write(devminor_t minor
, u64_t
UNUSED(position
),
296 endpoint_t endpt
, cp_grant_id_t grant
, size_t size
, int flags
,
303 if ((tp
= line2tty(minor
)) == NULL
)
307 /* Check, store information on the writer, do I/O. */
308 if (pp
->state
& TTY_CLOSED
)
311 if (pp
->wrcaller
!= NONE
|| pp
->wrleft
!= 0 || pp
->wrcum
!= 0)
317 pp
->wrcaller
= endpt
;
324 if (pp
->wrleft
== 0) {
326 return EDONTREPLY
; /* already done */
329 if (flags
& CDEV_NONBLOCK
) {
330 r
= pp
->wrcum
> 0 ? pp
->wrcum
: EAGAIN
;
331 pp
->wrleft
= pp
->wrcum
= 0;
336 return EDONTREPLY
; /* do suspend */
339 /*===========================================================================*
341 *===========================================================================*/
342 static int pty_master_ioctl(devminor_t minor
, unsigned long request
,
343 endpoint_t endpt
, cp_grant_id_t grant
, int flags
,
344 endpoint_t user_endpt
, cdev_id_t id
)
353 if ((tp
= line2tty(minor
)) == NULL
)
357 /* Some IOCTLs are for the master side only. */
359 case TIOCGRANTPT
: /* grantpt(3) */
360 if (!(pp
->state
& PTY_UNIX98
))
363 if ((int)(uid
= getnuid(user_endpt
)) == -1)
366 printf("PTY: no tty group ID given at startup\n");
370 /* Create or update the slave node. */
371 if (ptyfs_set(tp
->tty_index
, UNIX98_MODE
, uid
, tty_gid
,
372 makedev(PTY_MAJOR
, UNIX98_SLAVE(tp
->tty_index
))) != OK
)
377 case TIOCPTSNAME
: /* ptsname(3) */
378 if (!(pp
->state
& PTY_UNIX98
))
381 /* Since pm.sn is 16 bytes, we can have up to a million slaves. */
382 memset(&pm
, 0, sizeof(pm
));
384 strlcpy(pm
.sn
, _PATH_DEV_PTS
, sizeof(pm
.sn
));
387 if (ptyfs_name(tp
->tty_index
, &pm
.sn
[len
], sizeof(pm
.sn
) - len
) != OK
)
390 return sys_safecopyto(endpt
, grant
, 0, (vir_bytes
)&pm
, sizeof(pm
));
393 r
= sys_safecopyfrom(endpt
, grant
, 0, (vir_bytes
)&val
, sizeof(val
));
398 pp
->state
|= PTY_PKTMODE
;
400 pp
->state
&= ~PTY_PKTMODE
;
405 /* TODO: historically, all IOCTLs on the master are processed as if issued on
406 * the slave end. Make sure that this can not cause problems, in particular
407 * with blocking IOCTLs.
409 return tty_ioctl(minor
, request
, endpt
, grant
, flags
, user_endpt
, id
);
412 /*===========================================================================*
413 * pty_master_cancel *
414 *===========================================================================*/
415 static int pty_master_cancel(devminor_t minor
, endpoint_t endpt
, cdev_id_t id
)
421 if ((tp
= line2tty(minor
)) == NULL
)
425 if (pp
->rdcaller
== endpt
&& pp
->rdid
== id
) {
426 /* Cancel a read from a PTY. */
427 r
= pp
->rdcum
> 0 ? pp
->rdcum
: EINTR
;
428 pp
->rdleft
= pp
->rdcum
= 0;
433 if (pp
->wrcaller
== endpt
&& pp
->wrid
== id
) {
434 /* Cancel a write to a PTY. */
435 r
= pp
->wrcum
> 0 ? pp
->wrcum
: EINTR
;
436 pp
->wrleft
= pp
->wrcum
= 0;
441 /* Request not found. */
445 /*===========================================================================*
447 *===========================================================================*/
448 static int select_try_pty(tty_t
*tp
, int ops
)
450 pty_t
*pp
= tp
->tty_priv
;
453 if (ops
& CDEV_OP_WR
) {
454 /* Write won't block on error. */
455 if (pp
->state
& TTY_CLOSED
) r
|= CDEV_OP_WR
;
456 else if (pp
->wrleft
!= 0 || pp
->wrcum
!= 0) r
|= CDEV_OP_WR
;
457 else if (tp
->tty_incount
< buflen(tp
->tty_inbuf
)) r
|= CDEV_OP_WR
;
460 if (ops
& CDEV_OP_RD
) {
461 /* Read won't block on error. */
462 if (pp
->state
& TTY_CLOSED
) r
|= CDEV_OP_RD
;
463 else if (pp
->rdleft
!= 0 || pp
->rdcum
!= 0) r
|= CDEV_OP_RD
;
464 else if (pp
->ocount
> 0) r
|= CDEV_OP_RD
; /* Actual data. */
470 /*===========================================================================*
472 *===========================================================================*/
473 void select_retry_pty(tty_t
*tp
)
475 pty_t
*pp
= tp
->tty_priv
;
478 /* See if the pty side of a pty is ready to return a select. */
479 if (pp
->select_ops
&& (r
= select_try_pty(tp
, pp
->select_ops
))) {
480 chardriver_reply_select(pp
->select_proc
, pp
->select_minor
, r
);
481 pp
->select_ops
&= ~r
;
485 /*===========================================================================*
486 * pty_master_select *
487 *===========================================================================*/
488 static int pty_master_select(devminor_t minor
, unsigned int ops
,
493 int ready_ops
, watch
;
495 if ((tp
= line2tty(minor
)) == NULL
)
499 watch
= (ops
& CDEV_NOTIFY
);
500 ops
&= (CDEV_OP_RD
| CDEV_OP_WR
| CDEV_OP_ERR
);
502 ready_ops
= select_try_pty(tp
, ops
);
506 pp
->select_ops
|= ops
;
507 pp
->select_proc
= endpt
;
508 pp
->select_minor
= minor
;
514 /*===========================================================================*
516 *===========================================================================*/
517 void do_pty(message
*m_ptr
, int ipc_status
)
519 /* Process a request for a PTY master (/dev/ptypX) device. */
521 chardriver_process(&pty_master_tab
, m_ptr
, ipc_status
);
524 /*===========================================================================*
526 *===========================================================================*/
527 static int pty_slave_write(tty_t
*tp
, int try)
529 /* (*dev_write)() routine for PTYs. Transfer bytes from the writer on
530 * /dev/ttypX to the output buffer.
532 pty_t
*pp
= tp
->tty_priv
;
533 int count
, ocount
, s
;
535 /* PTY closed down? */
536 if (pp
->state
& PTY_CLOSED
) {
538 if (tp
->tty_outleft
> 0) {
539 chardriver_reply_task(tp
->tty_outcaller
, tp
->tty_outid
, EIO
);
540 tp
->tty_outleft
= tp
->tty_outcum
= 0;
541 tp
->tty_outcaller
= NONE
;
546 /* While there is something to do. */
548 ocount
= buflen(pp
->obuf
) - pp
->ocount
;
549 if (try) return (ocount
> 0);
550 count
= bufend(pp
->obuf
) - pp
->ohead
;
551 if (count
> ocount
) count
= ocount
;
552 if (count
> tp
->tty_outleft
) count
= tp
->tty_outleft
;
553 if (count
== 0 || tp
->tty_inhibited
)
556 /* Copy from user space to the PTY output buffer. */
557 if (tp
->tty_outcaller
== KERNEL
) {
558 /* We're trying to print on kernel's behalf */
559 memcpy(pp
->ohead
, (void *) tp
->tty_outgrant
+ tp
->tty_outcum
,
562 if ((s
= sys_safecopyfrom(tp
->tty_outcaller
, tp
->tty_outgrant
,
563 tp
->tty_outcum
, (vir_bytes
) pp
->ohead
,
569 /* Perform output processing on the output buffer. */
570 out_process(tp
, pp
->obuf
, pp
->ohead
, bufend(pp
->obuf
), &count
, &ocount
);
571 if (count
== 0) break;
573 /* Assume echoing messed up by output. */
574 tp
->tty_reprint
= TRUE
;
577 pp
->ocount
+= ocount
;
578 if ((pp
->ohead
+= ocount
) >= bufend(pp
->obuf
))
579 pp
->ohead
-= buflen(pp
->obuf
);
582 tp
->tty_outcum
+= count
;
583 if ((tp
->tty_outleft
-= count
) == 0) {
584 /* Output is finished, reply to the writer. */
585 chardriver_reply_task(tp
->tty_outcaller
, tp
->tty_outid
,
588 tp
->tty_outcaller
= NONE
;
595 /*===========================================================================*
597 *===========================================================================*/
598 static void pty_slave_echo(tty_t
*tp
, int c
)
600 /* Echo one character. (Like pty_write, but only one character, optionally.) */
602 pty_t
*pp
= tp
->tty_priv
;
605 ocount
= buflen(pp
->obuf
) - pp
->ocount
;
606 if (ocount
== 0) return; /* output buffer full */
608 *pp
->ohead
= c
; /* add one character */
610 out_process(tp
, pp
->obuf
, pp
->ohead
, bufend(pp
->obuf
), &count
, &ocount
);
611 if (count
== 0) return;
613 pp
->ocount
+= ocount
;
614 if ((pp
->ohead
+= ocount
) >= bufend(pp
->obuf
)) pp
->ohead
-= buflen(pp
->obuf
);
618 /*===========================================================================*
620 *===========================================================================*/
621 static void pty_start(pty_t
*pp
)
623 /* Transfer bytes written to the output buffer to the PTY reader. */
627 /* While there are things to do. */
629 count
= bufend(pp
->obuf
) - pp
->otail
;
630 if (count
> pp
->ocount
) count
= pp
->ocount
;
631 if (count
== 0 || pp
->rdleft
== 0) break;
633 /* If there is output at all, and packet mode is enabled, then prepend
634 * the output with a zero byte. This is absolutely minimal "support"
635 * for the TIOCPKT receipt mode to get telnetd(8) going. Implementing
636 * full support for all the TIOCPKT bits will require more work.
638 if (pp
->rdcum
== 0 && (pp
->state
& PTY_PKTMODE
)) {
640 if (sys_safecopyto(pp
->rdcaller
, pp
->rdgrant
, 0, (vir_bytes
)&c
,
648 if (count
> pp
->rdleft
) count
= pp
->rdleft
;
649 if (count
== 0) break;
651 /* Copy from the output buffer to the readers address space. */
652 if (sys_safecopyto(pp
->rdcaller
, pp
->rdgrant
, pp
->rdcum
,
653 (vir_bytes
)pp
->otail
, count
) != OK
)
658 if ((pp
->otail
+= count
) == bufend(pp
->obuf
)) pp
->otail
= pp
->obuf
;
664 /*===========================================================================*
666 *===========================================================================*/
667 static void pty_finish(pty_t
*pp
)
669 /* Finish the read request of a PTY reader if there is at least one byte
674 chardriver_reply_task(pp
->rdcaller
, pp
->rdid
, pp
->rdcum
);
675 pp
->rdleft
= pp
->rdcum
= 0;
680 /*===========================================================================*
682 *===========================================================================*/
683 static int pty_slave_read(tty_t
*tp
, int try)
685 /* Offer bytes from the PTY writer for input on the TTY. (Do it one byte at
686 * a time, 99% of the writes will be for one byte, so no sense in being smart.)
688 pty_t
*pp
= tp
->tty_priv
;
691 if (pp
->state
& PTY_CLOSED
) {
693 if (tp
->tty_inleft
> 0) {
694 chardriver_reply_task(tp
->tty_incaller
, tp
->tty_inid
,
696 tp
->tty_inleft
= tp
->tty_incum
= 0;
697 tp
->tty_incaller
= NONE
;
708 while (pp
->wrleft
> 0) {
711 /* Transfer one character to 'c'. */
712 if ((s
= sys_safecopyfrom(pp
->wrcaller
, pp
->wrgrant
, pp
->wrcum
,
713 (vir_bytes
) &c
, 1)) != OK
) {
714 printf("pty: safecopy failed (error %d)\n", s
);
718 /* Input processing. */
719 if (in_process(tp
, &c
, 1) == 0) break;
721 /* PTY writer bookkeeping. */
723 if (--pp
->wrleft
== 0) {
724 chardriver_reply_task(pp
->wrcaller
, pp
->wrid
, pp
->wrcum
);
733 /*===========================================================================*
734 * pty_slave_mayopen *
735 *===========================================================================*/
736 static int pty_slave_mayopen(tty_t
*tp
, devminor_t line
)
738 /* Check if the user is not mixing Unix98 and non-Unix98 terminal ends. */
740 int unix98_line
, unix98_pty
;
744 /* A non-Unix98 slave may be opened even if the corresponding master is not
745 * opened yet, but PTY_UNIX98 is always clear for free ptys. A Unix98 slave
746 * may not be opened before its master, but this should not occur anyway.
748 unix98_line
= (line
>= UNIX98_MINOR
&& line
< UNIX98_MINOR
+ NR_PTYS
* 2);
749 unix98_pty
= !!(pp
->state
& PTY_UNIX98
);
751 return (unix98_line
== unix98_pty
);
754 /*===========================================================================*
756 *===========================================================================*/
757 static int pty_slave_open(tty_t
*tp
, int UNUSED(try))
759 /* The tty side has been opened. */
760 pty_t
*pp
= tp
->tty_priv
;
762 /* TTY_ACTIVE may already be set, which would indicate that the slave is
763 * reopened after being fully closed while the master is still open. In that
764 * case TTY_CLOSED will also be set, so clear that one.
766 pp
->state
|= TTY_ACTIVE
;
767 pp
->state
&= ~TTY_CLOSED
;
772 /*===========================================================================*
774 *===========================================================================*/
775 static int pty_slave_close(tty_t
*tp
, int UNUSED(try))
777 /* The tty side has closed, so shut down the pty side. */
778 pty_t
*pp
= tp
->tty_priv
;
780 if (!(pp
->state
& PTY_ACTIVE
)) return 0;
782 if (pp
->rdleft
> 0) {
783 chardriver_reply_task(pp
->rdcaller
, pp
->rdid
, pp
->rdcum
);
784 pp
->rdleft
= pp
->rdcum
= 0;
788 if (pp
->wrleft
> 0) {
789 chardriver_reply_task(pp
->wrcaller
, pp
->wrid
, pp
->wrcum
);
790 pp
->wrleft
= pp
->wrcum
= 0;
794 if (pp
->state
& PTY_CLOSED
) pty_reset(tp
);
795 else pp
->state
|= TTY_CLOSED
;
800 /*===========================================================================*
801 * pty_slave_icancel *
802 *===========================================================================*/
803 static int pty_slave_icancel(tty_t
*tp
, int UNUSED(try))
805 /* Discard waiting input. */
806 pty_t
*pp
= tp
->tty_priv
;
808 if (pp
->wrleft
> 0) {
809 chardriver_reply_task(pp
->wrcaller
, pp
->wrid
, pp
->wrcum
+ pp
->wrleft
);
810 pp
->wrcum
= pp
->wrleft
= 0;
817 /*===========================================================================*
818 * pty_slave_ocancel *
819 *===========================================================================*/
820 static int pty_slave_ocancel(tty_t
*tp
, int UNUSED(try))
822 /* Drain the output buffer. */
823 pty_t
*pp
= tp
->tty_priv
;
826 pp
->otail
= pp
->ohead
;
831 /*===========================================================================*
833 *===========================================================================*/
834 void pty_init(tty_t
*tp
)
839 /* Associate PTY and TTY structures. */
840 line
= tp
- tty_table
;
841 pp
= tp
->tty_priv
= &pty_table
[line
];
847 /* Set up output queue. */
848 pp
->ohead
= pp
->otail
= pp
->obuf
;
850 /* Fill in TTY function hooks. */
851 tp
->tty_devread
= pty_slave_read
;
852 tp
->tty_devwrite
= pty_slave_write
;
853 tp
->tty_echo
= pty_slave_echo
;
854 tp
->tty_icancel
= pty_slave_icancel
;
855 tp
->tty_ocancel
= pty_slave_ocancel
;
856 tp
->tty_mayopen
= pty_slave_mayopen
;
857 tp
->tty_open
= pty_slave_open
;
858 tp
->tty_close
= pty_slave_close
;
859 tp
->tty_select_ops
= 0;