Drop main() prototype. Syncs with NetBSD-8
[minix.git] / minix / drivers / tty / pty / pty.c
blob9a1ec7a0ed06c7268be90227032e4fd5e6755bd6
1 /* pty.c - pseudo terminal driver Author: Kees J. Bot
2 * 30 Dec 1995
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>
35 #include <paths.h>
36 #include <termios.h>
37 #include <assert.h>
38 #include <sys/termios.h>
39 #include <signal.h>
40 #include "tty.h"
41 #include "ptyfs.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. */
50 typedef struct pty {
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 */
68 /* Output buffer. */
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 */
74 /* select() data. */
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? */
78 } pty_t;
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,
97 cdev_id_t id);
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,
100 cdev_id_t id);
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,
106 endpoint_t endpt);
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 /*===========================================================================*
119 * get_free_pty *
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. */
124 tty_t *tp;
125 pty_t *pp;
127 for (tp = &tty_table[0]; tp < &tty_table[NR_PTYS]; tp++) {
128 pp = tp->tty_priv;
130 if (!(pp->state & (PTY_ACTIVE | TTY_ACTIVE)))
131 return tp;
134 return NULL;
137 /*===========================================================================*
138 * pty_master_open *
139 *===========================================================================*/
140 static int pty_master_open(devminor_t minor, int UNUSED(access),
141 endpoint_t UNUSED(user_endpt))
143 tty_t *tp;
144 pty_t *pp;
145 int r;
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)
163 return EAGAIN;
165 pp = tp->tty_priv;
166 pp->state |= PTY_UNIX98;
168 minor = UNIX98_MASTER(tp->tty_index);
170 r = CDEV_CLONED | minor;
171 } else {
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)
177 return EIO;
179 if ((tp = line2tty(minor)) == NULL)
180 return ENXIO;
181 pp = tp->tty_priv;
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)
188 return EIO;
189 assert(!(pp->state & PTY_UNIX98));
191 r = OK;
194 pp->state |= PTY_ACTIVE;
196 pp->rdcum = 0;
197 pp->wrcum = 0;
199 return r;
202 /*===========================================================================*
203 * pty_reset *
204 *===========================================================================*/
205 static void pty_reset(tty_t *tp)
207 /* Both sides of a PTY pair have been closed. Clean up its state. */
208 pty_t *pp;
210 pp = tp->tty_priv;
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);
218 pp->state = 0;
221 /*===========================================================================*
222 * pty_master_close *
223 *===========================================================================*/
224 static int pty_master_close(devminor_t minor)
226 tty_t *tp;
227 pty_t *pp;
229 if ((tp = line2tty(minor)) == NULL)
230 return ENXIO;
231 pp = tp->tty_priv;
233 if ((pp->state & (TTY_ACTIVE | TTY_CLOSED)) != TTY_ACTIVE) {
234 pty_reset(tp);
235 } else {
236 pp->state |= PTY_CLOSED;
237 tp->tty_termios.c_ospeed = B0; /* cause EOF on slave side */
238 sigchar(tp, SIGHUP, 1);
241 return OK;
244 /*===========================================================================*
245 * pty_master_read *
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,
249 cdev_id_t id)
251 tty_t *tp;
252 pty_t *pp;
253 ssize_t r;
255 if ((tp = line2tty(minor)) == NULL)
256 return ENXIO;
257 pp = tp->tty_priv;
259 /* Check, store information on the reader, do I/O. */
260 if (pp->state & TTY_CLOSED)
261 return 0; /* EOF */
263 if (pp->rdcaller != NONE || pp->rdleft != 0 || pp->rdcum != 0)
264 return EIO;
266 if (size <= 0)
267 return EINVAL;
269 pp->rdcaller = endpt;
270 pp->rdid = id;
271 pp->rdgrant = grant;
272 pp->rdleft = size;
273 pty_start(pp);
275 handle_events(tp);
277 if (pp->rdleft == 0) {
278 pp->rdcaller = NONE;
279 return EDONTREPLY; /* already done */
282 if (flags & CDEV_NONBLOCK) {
283 r = pp->rdcum > 0 ? pp->rdcum : EAGAIN;
284 pp->rdleft = pp->rdcum = 0;
285 pp->rdcaller = NONE;
286 return r;
289 return EDONTREPLY; /* do suspend */
292 /*===========================================================================*
293 * pty_master_write *
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,
297 cdev_id_t id)
299 tty_t *tp;
300 pty_t *pp;
301 ssize_t r;
303 if ((tp = line2tty(minor)) == NULL)
304 return ENXIO;
305 pp = tp->tty_priv;
307 /* Check, store information on the writer, do I/O. */
308 if (pp->state & TTY_CLOSED)
309 return EIO;
311 if (pp->wrcaller != NONE || pp->wrleft != 0 || pp->wrcum != 0)
312 return EIO;
314 if (size <= 0)
315 return EINVAL;
317 pp->wrcaller = endpt;
318 pp->wrid = id;
319 pp->wrgrant = grant;
320 pp->wrleft = size;
322 handle_events(tp);
324 if (pp->wrleft == 0) {
325 pp->wrcaller = NONE;
326 return EDONTREPLY; /* already done */
329 if (flags & CDEV_NONBLOCK) {
330 r = pp->wrcum > 0 ? pp->wrcum : EAGAIN;
331 pp->wrleft = pp->wrcum = 0;
332 pp->wrcaller = NONE;
333 return r;
336 return EDONTREPLY; /* do suspend */
339 /*===========================================================================*
340 * pty_master_ioctl *
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)
346 tty_t *tp;
347 pty_t *pp;
348 uid_t uid;
349 struct ptmget pm;
350 size_t len;
351 int r, val;
353 if ((tp = line2tty(minor)) == NULL)
354 return ENXIO;
355 pp = tp->tty_priv;
357 /* Some IOCTLs are for the master side only. */
358 switch (request) {
359 case TIOCGRANTPT: /* grantpt(3) */
360 if (!(pp->state & PTY_UNIX98))
361 break;
363 if ((int)(uid = getnuid(user_endpt)) == -1)
364 return EACCES;
365 if (tty_gid == -1) {
366 printf("PTY: no tty group ID given at startup\n");
367 return EACCES;
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)
373 return EACCES;
375 return OK;
377 case TIOCPTSNAME: /* ptsname(3) */
378 if (!(pp->state & PTY_UNIX98))
379 break;
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));
385 len = strlen(pm.sn);
387 if (ptyfs_name(tp->tty_index, &pm.sn[len], sizeof(pm.sn) - len) != OK)
388 return EINVAL;
390 return sys_safecopyto(endpt, grant, 0, (vir_bytes)&pm, sizeof(pm));
392 case TIOCPKT:
393 r = sys_safecopyfrom(endpt, grant, 0, (vir_bytes)&val, sizeof(val));
394 if (r != OK)
395 return r;
397 if (val)
398 pp->state |= PTY_PKTMODE;
399 else
400 pp->state &= ~PTY_PKTMODE;
402 return OK;
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)
417 tty_t *tp;
418 pty_t *pp;
419 int r;
421 if ((tp = line2tty(minor)) == NULL)
422 return ENXIO;
423 pp = tp->tty_priv;
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;
429 pp->rdcaller = NONE;
430 return r;
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;
437 pp->wrcaller = NONE;
438 return r;
441 /* Request not found. */
442 return EDONTREPLY;
445 /*===========================================================================*
446 * select_try_pty *
447 *===========================================================================*/
448 static int select_try_pty(tty_t *tp, int ops)
450 pty_t *pp = tp->tty_priv;
451 int r = 0;
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. */
467 return r;
470 /*===========================================================================*
471 * select_retry_pty *
472 *===========================================================================*/
473 void select_retry_pty(tty_t *tp)
475 pty_t *pp = tp->tty_priv;
476 int r;
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,
489 endpoint_t endpt)
491 tty_t *tp;
492 pty_t *pp;
493 int ready_ops, watch;
495 if ((tp = line2tty(minor)) == NULL)
496 return ENXIO;
497 pp = tp->tty_priv;
499 watch = (ops & CDEV_NOTIFY);
500 ops &= (CDEV_OP_RD | CDEV_OP_WR | CDEV_OP_ERR);
502 ready_ops = select_try_pty(tp, ops);
504 ops &= ~ready_ops;
505 if (ops && watch) {
506 pp->select_ops |= ops;
507 pp->select_proc = endpt;
508 pp->select_minor = minor;
511 return ready_ops;
514 /*===========================================================================*
515 * do_pty *
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 /*===========================================================================*
525 * pty_slave_write *
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) {
537 if (try) return 1;
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;
543 return 0;
546 /* While there is something to do. */
547 for (;;) {
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)
554 break;
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,
560 count);
561 } else {
562 if ((s = sys_safecopyfrom(tp->tty_outcaller, tp->tty_outgrant,
563 tp->tty_outcum, (vir_bytes) pp->ohead,
564 count)) != OK) {
565 break;
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;
576 /* Bookkeeping. */
577 pp->ocount += ocount;
578 if ((pp->ohead += ocount) >= bufend(pp->obuf))
579 pp->ohead -= buflen(pp->obuf);
580 pty_start(pp);
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,
586 tp->tty_outcum);
587 tp->tty_outcum = 0;
588 tp->tty_outcaller = NONE;
591 pty_finish(pp);
592 return 1;
595 /*===========================================================================*
596 * pty_slave_echo *
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;
603 int count, ocount;
605 ocount = buflen(pp->obuf) - pp->ocount;
606 if (ocount == 0) return; /* output buffer full */
607 count = 1;
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);
615 pty_start(pp);
618 /*===========================================================================*
619 * pty_start *
620 *===========================================================================*/
621 static void pty_start(pty_t *pp)
623 /* Transfer bytes written to the output buffer to the PTY reader. */
624 int count;
625 char c;
627 /* While there are things to do. */
628 for (;;) {
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)) {
639 c = 0;
640 if (sys_safecopyto(pp->rdcaller, pp->rdgrant, 0, (vir_bytes)&c,
641 sizeof(c)) != OK)
642 break;
644 pp->rdcum++;
645 pp->rdleft--;
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)
654 break;
656 /* Bookkeeping. */
657 pp->ocount -= count;
658 if ((pp->otail += count) == bufend(pp->obuf)) pp->otail = pp->obuf;
659 pp->rdcum += count;
660 pp->rdleft -= count;
664 /*===========================================================================*
665 * pty_finish *
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
670 * transferred.
673 if (pp->rdcum > 0) {
674 chardriver_reply_task(pp->rdcaller, pp->rdid, pp->rdcum);
675 pp->rdleft = pp->rdcum = 0;
676 pp->rdcaller = NONE;
680 /*===========================================================================*
681 * pty_slave_read *
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;
689 char c;
691 if (pp->state & PTY_CLOSED) {
692 if (try) return 1;
693 if (tp->tty_inleft > 0) {
694 chardriver_reply_task(tp->tty_incaller, tp->tty_inid,
695 tp->tty_incum);
696 tp->tty_inleft = tp->tty_incum = 0;
697 tp->tty_incaller = NONE;
699 return 1;
702 if (try) {
703 if (pp->wrleft > 0)
704 return 1;
705 return 0;
708 while (pp->wrleft > 0) {
709 int s;
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);
715 break;
718 /* Input processing. */
719 if (in_process(tp, &c, 1) == 0) break;
721 /* PTY writer bookkeeping. */
722 pp->wrcum++;
723 if (--pp->wrleft == 0) {
724 chardriver_reply_task(pp->wrcaller, pp->wrid, pp->wrcum);
725 pp->wrcum = 0;
726 pp->wrcaller = NONE;
730 return 0;
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. */
739 pty_t *pp;
740 int unix98_line, unix98_pty;
742 pp = tp->tty_priv;
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 /*===========================================================================*
755 * pty_slave_open *
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;
769 return 0;
772 /*===========================================================================*
773 * pty_slave_close *
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;
785 pp->rdcaller = NONE;
788 if (pp->wrleft > 0) {
789 chardriver_reply_task(pp->wrcaller, pp->wrid, pp->wrcum);
790 pp->wrleft = pp->wrcum = 0;
791 pp->wrcaller = NONE;
794 if (pp->state & PTY_CLOSED) pty_reset(tp);
795 else pp->state |= TTY_CLOSED;
797 return 0;
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;
811 pp->wrcaller = NONE;
814 return 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;
825 pp->ocount = 0;
826 pp->otail = pp->ohead;
828 return 0;
831 /*===========================================================================*
832 * pty_init *
833 *===========================================================================*/
834 void pty_init(tty_t *tp)
836 pty_t *pp;
837 int line;
839 /* Associate PTY and TTY structures. */
840 line = tp - tty_table;
841 pp = tp->tty_priv = &pty_table[line];
842 pp->tty = tp;
843 pp->select_ops = 0;
844 pp->rdcaller = NONE;
845 pp->wrcaller = NONE;
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;