xref: /minix/minix/drivers/tty/pty/pty.c (revision 9f988b79)
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:
5  *
6  *	keyboard -> rlogin -> in.rld -> /dev/ptypX -> /dev/ttypX -> shell
7  *	shell -> /dev/ttypX -> /dev/ptypX -> in.rld -> rlogin -> screen
8  *
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.
17  *
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.
26  *
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.
32  */
33 
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"
42 
43 /* Device node attributes used for Unix98 slave nodes. */
44 #define UNIX98_MODE		(S_IFCHR | 0620)	/* crw--w---- */
45 
46 #define UNIX98_MASTER(index)	(UNIX98_MINOR + (index) * 2)
47 #define UNIX98_SLAVE(index)	(UNIX98_MINOR + (index) * 2 + 1)
48 
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, ... */
53 
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 */
60 
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 */
67 
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 */
73 
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;
79 
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 
86 static pty_t pty_table[NR_PTYS];	/* PTY bookkeeping */
87 
88 static void pty_start(pty_t *pp);
89 static void pty_finish(pty_t *pp);
90 
91 static int pty_master_open(devminor_t minor, int access,
92 	endpoint_t user_endpt);
93 static int pty_master_close(devminor_t minor);
94 static ssize_t pty_master_read(devminor_t minor, u64_t position,
95 	endpoint_t endpt, cp_grant_id_t grant, size_t size, int flags,
96 	cdev_id_t id);
97 static ssize_t pty_master_write(devminor_t minor, u64_t position,
98 	endpoint_t endpt, cp_grant_id_t grant, size_t size, int flags,
99 	cdev_id_t id);
100 static int pty_master_ioctl(devminor_t minor, unsigned long request,
101 	endpoint_t endpt, cp_grant_id_t grant, int flags,
102 	endpoint_t user_endpt, cdev_id_t id);
103 static int pty_master_cancel(devminor_t minor, endpoint_t endpt, cdev_id_t id);
104 static int pty_master_select(devminor_t minor, unsigned int ops,
105 	endpoint_t endpt);
106 
107 static struct chardriver pty_master_tab = {
108   .cdr_open	= pty_master_open,
109   .cdr_close	= pty_master_close,
110   .cdr_read	= pty_master_read,
111   .cdr_write	= pty_master_write,
112   .cdr_ioctl	= pty_master_ioctl,
113   .cdr_cancel	= pty_master_cancel,
114   .cdr_select	= pty_master_select
115 };
116 
117 /*===========================================================================*
118  *				get_free_pty				     *
119  *===========================================================================*/
120 static tty_t *get_free_pty(void)
121 {
122 /* Return a pointer to a free tty structure, or NULL if no tty is free. */
123   tty_t *tp;
124   pty_t *pp;
125 
126   for (tp = &tty_table[0]; tp < &tty_table[NR_PTYS]; tp++) {
127 	pp = tp->tty_priv;
128 
129 	if (!(pp->state & (PTY_ACTIVE | TTY_ACTIVE)))
130 		return tp;
131   }
132 
133   return NULL;
134 }
135 
136 /*===========================================================================*
137  *				pty_master_open				     *
138  *===========================================================================*/
139 static int pty_master_open(devminor_t minor, int UNUSED(access),
140 	endpoint_t UNUSED(user_endpt))
141 {
142   tty_t *tp;
143   pty_t *pp;
144   int r;
145 
146   if (minor == PTMX_MINOR) {
147 	/* /dev/ptmx acts as a cloning device. We return a free PTY master and
148 	 * mark it as a UNIX98 type.
149 	 */
150 	if ((tp = get_free_pty()) == NULL)
151 		return EAGAIN; /* POSIX says this is the right error code */
152 
153 	/* The following call has two purposes. First, we check right here
154 	 * whether PTYFS is running at all; if not, the PTMX device cannot be
155 	 * opened at all and userland can fall back to other allocation
156 	 * methods right away. Second, in the exceptional case that the PTY
157 	 * service is restarted while PTYFS keeps running, PTYFS may expose
158 	 * stale slave nodes, which are a security hole if not removed as soon
159 	 * as a new PTY pair is allocated.
160 	 */
161 	if (ptyfs_clear(tp->tty_index) != OK)
162 		return EAGAIN;
163 
164 	pp = tp->tty_priv;
165 	pp->state |= PTY_UNIX98;
166 
167 	minor = UNIX98_MASTER(tp->tty_index);
168 
169 	r = CDEV_CLONED | minor;
170   } else {
171 	/* There is no way to open Unix98 masters directly, except by messing
172 	 * with mknod. We disallow such tricks altogether, and thus, the rest
173 	 * of the code deals with opening a non-Unix98 master only.
174 	 */
175 	if (minor < PTYPX_MINOR || minor >= PTYPX_MINOR + NR_PTYS)
176 		return EIO;
177 
178 	if ((tp = line2tty(minor)) == NULL)
179 		return ENXIO;
180 	pp = tp->tty_priv;
181 
182 	/* For non-Unix98 PTYs, we allow the slave to be opened before the
183 	 * master, but the master may be opened only once. This is how userland
184 	 * is able to find a free non-Unix98 PTY pair.
185 	 */
186 	if (pp->state & PTY_ACTIVE)
187 		return EIO;
188 	assert(!(pp->state & PTY_UNIX98));
189 
190 	r = OK;
191   }
192 
193   pp->state |= PTY_ACTIVE;
194 
195   pp->rdcum = 0;
196   pp->wrcum = 0;
197 
198   return r;
199 }
200 
201 /*===========================================================================*
202  *				pty_reset				     *
203  *===========================================================================*/
204 static void pty_reset(tty_t *tp)
205 {
206 /* Both sides of a PTY pair have been closed. Clean up its state. */
207   pty_t *pp;
208 
209   pp = tp->tty_priv;
210 
211   /* For Unix98 pairs, clean up the Unix98 slave node. It may never have been
212    * allocated, but we don't care. Ignore failures altogether.
213    */
214   if (pp->state & PTY_UNIX98)
215 	(void)ptyfs_clear(tp->tty_index);
216 
217   pp->state = 0;
218 }
219 
220 /*===========================================================================*
221  *				pty_master_close			     *
222  *===========================================================================*/
223 static int pty_master_close(devminor_t minor)
224 {
225   tty_t *tp;
226   pty_t *pp;
227 
228   if ((tp = line2tty(minor)) == NULL)
229 	return ENXIO;
230   pp = tp->tty_priv;
231 
232   if ((pp->state & (TTY_ACTIVE | TTY_CLOSED)) != TTY_ACTIVE) {
233 	pty_reset(tp);
234   } else {
235 	pp->state |= PTY_CLOSED;
236 	tp->tty_termios.c_ospeed = B0; /* cause EOF on slave side */
237 	sigchar(tp, SIGHUP, 1);
238   }
239 
240   return OK;
241 }
242 
243 /*===========================================================================*
244  *				pty_master_read				     *
245  *===========================================================================*/
246 static ssize_t pty_master_read(devminor_t minor, u64_t UNUSED(position),
247 	endpoint_t endpt, cp_grant_id_t grant, size_t size, int flags,
248 	cdev_id_t id)
249 {
250   tty_t *tp;
251   pty_t *pp;
252   ssize_t r;
253 
254   if ((tp = line2tty(minor)) == NULL)
255 	return ENXIO;
256   pp = tp->tty_priv;
257 
258   /* Check, store information on the reader, do I/O. */
259   if (pp->state & TTY_CLOSED)
260 	return 0; /* EOF */
261 
262   if (pp->rdcaller != NONE || pp->rdleft != 0 || pp->rdcum != 0)
263 	return EIO;
264 
265   if (size <= 0)
266 	return EINVAL;
267 
268   pp->rdcaller = endpt;
269   pp->rdid = id;
270   pp->rdgrant = grant;
271   pp->rdleft = size;
272   pty_start(pp);
273 
274   handle_events(tp);
275 
276   if (pp->rdleft == 0) {
277 	pp->rdcaller = NONE;
278 	return EDONTREPLY;		/* already done */
279   }
280 
281   if (flags & CDEV_NONBLOCK) {
282 	r = pp->rdcum > 0 ? pp->rdcum : EAGAIN;
283 	pp->rdleft = pp->rdcum = 0;
284 	pp->rdcaller = NONE;
285 	return r;
286   }
287 
288   return EDONTREPLY;			/* do suspend */
289 }
290 
291 /*===========================================================================*
292  *				pty_master_write			     *
293  *===========================================================================*/
294 static ssize_t pty_master_write(devminor_t minor, u64_t UNUSED(position),
295 	endpoint_t endpt, cp_grant_id_t grant, size_t size, int flags,
296 	cdev_id_t id)
297 {
298   tty_t *tp;
299   pty_t *pp;
300   ssize_t r;
301 
302   if ((tp = line2tty(minor)) == NULL)
303 	return ENXIO;
304   pp = tp->tty_priv;
305 
306   /* Check, store information on the writer, do I/O. */
307   if (pp->state & TTY_CLOSED)
308 	return EIO;
309 
310   if (pp->wrcaller != NONE || pp->wrleft != 0 || pp->wrcum != 0)
311 	return EIO;
312 
313   if (size <= 0)
314 	return EINVAL;
315 
316   pp->wrcaller = endpt;
317   pp->wrid = id;
318   pp->wrgrant = grant;
319   pp->wrleft = size;
320 
321   handle_events(tp);
322 
323   if (pp->wrleft == 0) {
324 	pp->wrcaller = NONE;
325 	return EDONTREPLY;		/* already done */
326   }
327 
328   if (flags & CDEV_NONBLOCK) {
329 	r = pp->wrcum > 0 ? pp->wrcum : EAGAIN;
330 	pp->wrleft = pp->wrcum = 0;
331 	pp->wrcaller = NONE;
332 	return r;
333   }
334 
335   return EDONTREPLY;			/* do suspend */
336 }
337 
338 /*===========================================================================*
339  *				pty_master_ioctl			     *
340  *===========================================================================*/
341 static int pty_master_ioctl(devminor_t minor, unsigned long request,
342 	endpoint_t endpt, cp_grant_id_t grant, int flags,
343 	endpoint_t user_endpt, cdev_id_t id)
344 {
345   tty_t *tp;
346   pty_t *pp;
347   uid_t uid;
348   struct ptmget pm;
349   size_t len;
350 
351   if ((tp = line2tty(minor)) == NULL)
352 	return ENXIO;
353   pp = tp->tty_priv;
354 
355   /* Some IOCTLs are for the master side only. */
356   switch (request) {
357   case TIOCGRANTPT:	/* grantpt(3) */
358 	if (!(pp->state & PTY_UNIX98))
359 		break;
360 
361 	if ((int)(uid = getnuid(user_endpt)) == -1)
362 		return EACCES;
363 	if (tty_gid == -1) {
364 		printf("PTY: no tty group ID given at startup\n");
365 		return EACCES;
366 	}
367 
368 	/* Create or update the slave node. */
369 	if (ptyfs_set(tp->tty_index, UNIX98_MODE, uid, tty_gid,
370 	    makedev(PTY_MAJOR, UNIX98_SLAVE(tp->tty_index))) != OK)
371 		return EACCES;
372 
373 	return OK;
374 
375   case TIOCPTSNAME:	/* ptsname(3) */
376 	if (!(pp->state & PTY_UNIX98))
377 		break;
378 
379 	/* Since pm.sn is 16 bytes, we can have up to a million slaves. */
380 	memset(&pm, 0, sizeof(pm));
381 
382 	strlcpy(pm.sn, _PATH_DEV_PTS, sizeof(pm.sn));
383 	len = strlen(pm.sn);
384 
385 	if (ptyfs_name(tp->tty_index, &pm.sn[len], sizeof(pm.sn) - len) != OK)
386 		return EINVAL;
387 
388 	return sys_safecopyto(endpt, grant, 0, (vir_bytes)&pm, sizeof(pm));
389   }
390 
391   /* TODO: historically, all IOCTLs on the master are processed as if issued on
392    * the slave end. Make sure that this can not cause problems, in particular
393    * with blocking IOCTLs.
394    */
395   return tty_ioctl(minor, request, endpt, grant, flags, user_endpt, id);
396 }
397 
398 /*===========================================================================*
399  *				pty_master_cancel			     *
400  *===========================================================================*/
401 static int pty_master_cancel(devminor_t minor, endpoint_t endpt, cdev_id_t id)
402 {
403   tty_t *tp;
404   pty_t *pp;
405   int r;
406 
407   if ((tp = line2tty(minor)) == NULL)
408 	return ENXIO;
409   pp = tp->tty_priv;
410 
411   if (pp->rdcaller == endpt && pp->rdid == id) {
412 	/* Cancel a read from a PTY. */
413 	r = pp->rdcum > 0 ? pp->rdcum : EINTR;
414 	pp->rdleft = pp->rdcum = 0;
415 	pp->rdcaller = NONE;
416 	return r;
417   }
418 
419   if (pp->wrcaller == endpt && pp->wrid == id) {
420 	/* Cancel a write to a PTY. */
421 	r = pp->wrcum > 0 ? pp->wrcum : EINTR;
422 	pp->wrleft = pp->wrcum = 0;
423 	pp->wrcaller = NONE;
424 	return r;
425   }
426 
427   /* Request not found. */
428   return EDONTREPLY;
429 }
430 
431 /*===========================================================================*
432  *				select_try_pty				     *
433  *===========================================================================*/
434 static int select_try_pty(tty_t *tp, int ops)
435 {
436   pty_t *pp = tp->tty_priv;
437   int r = 0;
438 
439   if (ops & CDEV_OP_WR)  {
440 	/* Write won't block on error. */
441 	if (pp->state & TTY_CLOSED) r |= CDEV_OP_WR;
442 	else if (pp->wrleft != 0 || pp->wrcum != 0) r |= CDEV_OP_WR;
443 	else if (tp->tty_incount < buflen(tp->tty_inbuf)) r |= CDEV_OP_WR;
444   }
445 
446   if (ops & CDEV_OP_RD) {
447 	/* Read won't block on error. */
448 	if (pp->state & TTY_CLOSED) r |= CDEV_OP_RD;
449 	else if (pp->rdleft != 0 || pp->rdcum != 0) r |= CDEV_OP_RD;
450 	else if (pp->ocount > 0) r |= CDEV_OP_RD;	/* Actual data. */
451   }
452 
453   return r;
454 }
455 
456 /*===========================================================================*
457  *				select_retry_pty			     *
458  *===========================================================================*/
459 void select_retry_pty(tty_t *tp)
460 {
461   pty_t *pp = tp->tty_priv;
462   int r;
463 
464   /* See if the pty side of a pty is ready to return a select. */
465   if (pp->select_ops && (r = select_try_pty(tp, pp->select_ops))) {
466 	chardriver_reply_select(pp->select_proc, pp->select_minor, r);
467 	pp->select_ops &= ~r;
468   }
469 }
470 
471 /*===========================================================================*
472  *				pty_master_select			     *
473  *===========================================================================*/
474 static int pty_master_select(devminor_t minor, unsigned int ops,
475 	endpoint_t endpt)
476 {
477   tty_t *tp;
478   pty_t *pp;
479   int ready_ops, watch;
480 
481   if ((tp = line2tty(minor)) == NULL)
482 	return ENXIO;
483   pp = tp->tty_priv;
484 
485   watch = (ops & CDEV_NOTIFY);
486   ops &= (CDEV_OP_RD | CDEV_OP_WR | CDEV_OP_ERR);
487 
488   ready_ops = select_try_pty(tp, ops);
489 
490   ops &= ~ready_ops;
491   if (ops && watch) {
492 	pp->select_ops |= ops;
493 	pp->select_proc = endpt;
494 	pp->select_minor = minor;
495   }
496 
497   return ready_ops;
498 }
499 
500 /*===========================================================================*
501  *				do_pty					     *
502  *===========================================================================*/
503 void do_pty(message *m_ptr, int ipc_status)
504 {
505 /* Process a request for a PTY master (/dev/ptypX) device. */
506 
507   chardriver_process(&pty_master_tab, m_ptr, ipc_status);
508 }
509 
510 /*===========================================================================*
511  *				pty_slave_write				     *
512  *===========================================================================*/
513 static int pty_slave_write(tty_t *tp, int try)
514 {
515 /* (*dev_write)() routine for PTYs.  Transfer bytes from the writer on
516  * /dev/ttypX to the output buffer.
517  */
518   pty_t *pp = tp->tty_priv;
519   int count, ocount, s;
520 
521   /* PTY closed down? */
522   if (pp->state & PTY_CLOSED) {
523   	if (try) return 1;
524 	if (tp->tty_outleft > 0) {
525 		chardriver_reply_task(tp->tty_outcaller, tp->tty_outid, EIO);
526 		tp->tty_outleft = tp->tty_outcum = 0;
527 		tp->tty_outcaller = NONE;
528 	}
529 	return 0;
530   }
531 
532   /* While there is something to do. */
533   for (;;) {
534 	ocount = buflen(pp->obuf) - pp->ocount;
535 	if (try) return (ocount > 0);
536 	count = bufend(pp->obuf) - pp->ohead;
537 	if (count > ocount) count = ocount;
538 	if (count > tp->tty_outleft) count = tp->tty_outleft;
539 	if (count == 0 || tp->tty_inhibited)
540 		break;
541 
542 	/* Copy from user space to the PTY output buffer. */
543 	if (tp->tty_outcaller == KERNEL) {
544 		/* We're trying to print on kernel's behalf */
545 		memcpy(pp->ohead, (void *) tp->tty_outgrant + tp->tty_outcum,
546 			count);
547 	} else {
548 		if ((s = sys_safecopyfrom(tp->tty_outcaller, tp->tty_outgrant,
549 				tp->tty_outcum, (vir_bytes) pp->ohead,
550 				count)) != OK) {
551 			break;
552 		}
553 	}
554 
555 	/* Perform output processing on the output buffer. */
556 	out_process(tp, pp->obuf, pp->ohead, bufend(pp->obuf), &count, &ocount);
557 	if (count == 0) break;
558 
559 	/* Assume echoing messed up by output. */
560 	tp->tty_reprint = TRUE;
561 
562 	/* Bookkeeping. */
563 	pp->ocount += ocount;
564 	if ((pp->ohead += ocount) >= bufend(pp->obuf))
565 		pp->ohead -= buflen(pp->obuf);
566 	pty_start(pp);
567 
568 	tp->tty_outcum += count;
569 	if ((tp->tty_outleft -= count) == 0) {
570 		/* Output is finished, reply to the writer. */
571 		chardriver_reply_task(tp->tty_outcaller, tp->tty_outid,
572 			tp->tty_outcum);
573 		tp->tty_outcum = 0;
574 		tp->tty_outcaller = NONE;
575 	}
576   }
577   pty_finish(pp);
578   return 1;
579 }
580 
581 /*===========================================================================*
582  *				pty_slave_echo				     *
583  *===========================================================================*/
584 static void pty_slave_echo(tty_t *tp, int c)
585 {
586 /* Echo one character.  (Like pty_write, but only one character, optionally.) */
587 
588   pty_t *pp = tp->tty_priv;
589   int count, ocount;
590 
591   ocount = buflen(pp->obuf) - pp->ocount;
592   if (ocount == 0) return;		/* output buffer full */
593   count = 1;
594   *pp->ohead = c;			/* add one character */
595 
596   out_process(tp, pp->obuf, pp->ohead, bufend(pp->obuf), &count, &ocount);
597   if (count == 0) return;
598 
599   pp->ocount += ocount;
600   if ((pp->ohead += ocount) >= bufend(pp->obuf)) pp->ohead -= buflen(pp->obuf);
601   pty_start(pp);
602 }
603 
604 /*===========================================================================*
605  *				pty_start				     *
606  *===========================================================================*/
607 static void pty_start(pty_t *pp)
608 {
609 /* Transfer bytes written to the output buffer to the PTY reader. */
610   int count;
611 
612   /* While there are things to do. */
613   for (;;) {
614   	int s;
615 	count = bufend(pp->obuf) - pp->otail;
616 	if (count > pp->ocount) count = pp->ocount;
617 	if (count > pp->rdleft) count = pp->rdleft;
618 	if (count == 0) break;
619 
620 	/* Copy from the output buffer to the readers address space. */
621 	if((s = sys_safecopyto(pp->rdcaller, pp->rdgrant, pp->rdcum,
622 		(vir_bytes) pp->otail, count)) != OK) {
623 		break;
624  	}
625 
626 	/* Bookkeeping. */
627 	pp->ocount -= count;
628 	if ((pp->otail += count) == bufend(pp->obuf)) pp->otail = pp->obuf;
629 	pp->rdcum += count;
630 	pp->rdleft -= count;
631   }
632 }
633 
634 /*===========================================================================*
635  *				pty_finish				     *
636  *===========================================================================*/
637 static void pty_finish(pty_t *pp)
638 {
639 /* Finish the read request of a PTY reader if there is at least one byte
640  * transferred.
641  */
642 
643   if (pp->rdcum > 0) {
644 	chardriver_reply_task(pp->rdcaller, pp->rdid, pp->rdcum);
645 	pp->rdleft = pp->rdcum = 0;
646 	pp->rdcaller = NONE;
647   }
648 }
649 
650 /*===========================================================================*
651  *				pty_slave_read				     *
652  *===========================================================================*/
653 static int pty_slave_read(tty_t *tp, int try)
654 {
655 /* Offer bytes from the PTY writer for input on the TTY.  (Do it one byte at
656  * a time, 99% of the writes will be for one byte, so no sense in being smart.)
657  */
658   pty_t *pp = tp->tty_priv;
659   char c;
660 
661   if (pp->state & PTY_CLOSED) {
662 	if (try) return 1;
663 	if (tp->tty_inleft > 0) {
664 		chardriver_reply_task(tp->tty_incaller, tp->tty_inid,
665 			tp->tty_incum);
666 		tp->tty_inleft = tp->tty_incum = 0;
667 		tp->tty_incaller = NONE;
668 	}
669 	return 1;
670   }
671 
672   if (try) {
673   	if (pp->wrleft > 0)
674   		return 1;
675   	return 0;
676   }
677 
678   while (pp->wrleft > 0) {
679   	int s;
680 
681 	/* Transfer one character to 'c'. */
682 	if ((s = sys_safecopyfrom(pp->wrcaller, pp->wrgrant, pp->wrcum,
683 		(vir_bytes) &c, 1)) != OK) {
684 		printf("pty: safecopy failed (error %d)\n", s);
685 		break;
686 	}
687 
688 	/* Input processing. */
689 	if (in_process(tp, &c, 1) == 0) break;
690 
691 	/* PTY writer bookkeeping. */
692 	pp->wrcum++;
693 	if (--pp->wrleft == 0) {
694 		chardriver_reply_task(pp->wrcaller, pp->wrid, pp->wrcum);
695 		pp->wrcum = 0;
696 		pp->wrcaller = NONE;
697 	}
698   }
699 
700   return 0;
701 }
702 
703 /*===========================================================================*
704  *				pty_slave_mayopen			     *
705  *===========================================================================*/
706 static int pty_slave_mayopen(tty_t *tp, devminor_t line)
707 {
708 /* Check if the user is not mixing Unix98 and non-Unix98 terminal ends. */
709   pty_t *pp;
710   int unix98_line, unix98_pty;
711 
712   pp = tp->tty_priv;
713 
714   /* A non-Unix98 slave may be opened even if the corresponding master is not
715    * opened yet, but PTY_UNIX98 is always clear for free ptys.  A Unix98 slave
716    * may not be opened before its master, but this should not occur anyway.
717    */
718   unix98_line = (line >= UNIX98_MINOR && line < UNIX98_MINOR + NR_PTYS * 2);
719   unix98_pty = !!(pp->state & PTY_UNIX98);
720 
721   return (unix98_line == unix98_pty);
722 }
723 
724 /*===========================================================================*
725  *				pty_slave_open				     *
726  *===========================================================================*/
727 static int pty_slave_open(tty_t *tp, int UNUSED(try))
728 {
729 /* The tty side has been opened. */
730   pty_t *pp = tp->tty_priv;
731 
732   /* TTY_ACTIVE may already be set, which would indicate that the slave is
733    * reopened after being fully closed while the master is still open. In that
734    * case TTY_CLOSED will also be set, so clear that one.
735    */
736   pp->state |= TTY_ACTIVE;
737   pp->state &= ~TTY_CLOSED;
738 
739   return 0;
740 }
741 
742 /*===========================================================================*
743  *				pty_slave_close				     *
744  *===========================================================================*/
745 static int pty_slave_close(tty_t *tp, int UNUSED(try))
746 {
747 /* The tty side has closed, so shut down the pty side. */
748   pty_t *pp = tp->tty_priv;
749 
750   if (!(pp->state & PTY_ACTIVE)) return 0;
751 
752   if (pp->rdleft > 0) {
753 	chardriver_reply_task(pp->rdcaller, pp->rdid, pp->rdcum);
754 	pp->rdleft = pp->rdcum = 0;
755 	pp->rdcaller = NONE;
756   }
757 
758   if (pp->wrleft > 0) {
759 	chardriver_reply_task(pp->wrcaller, pp->wrid, pp->wrcum);
760 	pp->wrleft = pp->wrcum = 0;
761 	pp->wrcaller = NONE;
762   }
763 
764   if (pp->state & PTY_CLOSED) pty_reset(tp);
765   else pp->state |= TTY_CLOSED;
766 
767   return 0;
768 }
769 
770 /*===========================================================================*
771  *				pty_slave_icancel			     *
772  *===========================================================================*/
773 static int pty_slave_icancel(tty_t *tp, int UNUSED(try))
774 {
775 /* Discard waiting input. */
776   pty_t *pp = tp->tty_priv;
777 
778   if (pp->wrleft > 0) {
779 	chardriver_reply_task(pp->wrcaller, pp->wrid, pp->wrcum + pp->wrleft);
780 	pp->wrcum = pp->wrleft = 0;
781 	pp->wrcaller = NONE;
782   }
783 
784   return 0;
785 }
786 
787 /*===========================================================================*
788  *				pty_slave_ocancel			     *
789  *===========================================================================*/
790 static int pty_slave_ocancel(tty_t *tp, int UNUSED(try))
791 {
792 /* Drain the output buffer. */
793   pty_t *pp = tp->tty_priv;
794 
795   pp->ocount = 0;
796   pp->otail = pp->ohead;
797 
798   return 0;
799 }
800 
801 /*===========================================================================*
802  *				pty_init				     *
803  *===========================================================================*/
804 void pty_init(tty_t *tp)
805 {
806   pty_t *pp;
807   int line;
808 
809   /* Associate PTY and TTY structures. */
810   line = tp - tty_table;
811   pp = tp->tty_priv = &pty_table[line];
812   pp->tty = tp;
813   pp->select_ops = 0;
814   pp->rdcaller = NONE;
815   pp->wrcaller = NONE;
816 
817   /* Set up output queue. */
818   pp->ohead = pp->otail = pp->obuf;
819 
820   /* Fill in TTY function hooks. */
821   tp->tty_devread = pty_slave_read;
822   tp->tty_devwrite = pty_slave_write;
823   tp->tty_echo = pty_slave_echo;
824   tp->tty_icancel = pty_slave_icancel;
825   tp->tty_ocancel = pty_slave_ocancel;
826   tp->tty_mayopen = pty_slave_mayopen;
827   tp->tty_open = pty_slave_open;
828   tp->tty_close = pty_slave_close;
829   tp->tty_select_ops = 0;
830 }
831