xref: /minix3/minix/drivers/tty/pty/pty.c (revision da21d850255e4f11bb2023f69a0b3aad4eab62e2)
1433d6423SLionel Sambuc /*	pty.c - pseudo terminal driver			Author: Kees J. Bot
2433d6423SLionel Sambuc  *								30 Dec 1995
3433d6423SLionel Sambuc  * PTYs can be seen as a bidirectional pipe with TTY
4433d6423SLionel Sambuc  * input and output processing.  For example a simple rlogin session:
5433d6423SLionel Sambuc  *
6433d6423SLionel Sambuc  *	keyboard -> rlogin -> in.rld -> /dev/ptypX -> /dev/ttypX -> shell
7433d6423SLionel Sambuc  *	shell -> /dev/ttypX -> /dev/ptypX -> in.rld -> rlogin -> screen
8433d6423SLionel Sambuc  *
9433d6423SLionel Sambuc  * This file takes care of copying data between the tty/pty device pairs and
10433d6423SLionel Sambuc  * the open/read/write/close calls on the pty devices.  The TTY task takes
11433d6423SLionel Sambuc  * care of the input and output processing (interrupt, backspace, raw I/O,
12433d6423SLionel Sambuc  * etc.) using the pty_slave_read() and pty_slave_write() functions as the
13433d6423SLionel Sambuc  * "keyboard" and "screen" functions of the ttypX devices.
14433d6423SLionel Sambuc  * Be careful when reading this code, the terms "reading" and "writing" are
15433d6423SLionel Sambuc  * used both for the tty (slave) and the pty (master) end of the pseudo tty.
16433d6423SLionel Sambuc  * Writes to one end are to be read at the other end and vice-versa.
17*da21d850SDavid van Moolenbroek  *
18*da21d850SDavid van Moolenbroek  * In addition to the above, PTY service now also supports Unix98 pseudo-
19*da21d850SDavid van Moolenbroek  * terminal pairs, thereby allowing non-root users to allocate pseudoterminals.
20*da21d850SDavid van Moolenbroek  * It requires the presence for PTYFS for this, and supports only old-style
21*da21d850SDavid van Moolenbroek  * ptys when PTYFS is not running. For Unix98 ptys, the general idea is that a
22*da21d850SDavid van Moolenbroek  * userland program opens a pty master by opening /dev/ptmx through the use of
23*da21d850SDavid van Moolenbroek  * posxix_openpt(3). A slave node is allocated on PTYFS when the program calls
24*da21d850SDavid van Moolenbroek  * grantpt(3) on the master. The program can then obtain the path name for the
25*da21d850SDavid van Moolenbroek  * slave end through ptsname(3), and open the slave end using this path.
26*da21d850SDavid van Moolenbroek  *
27*da21d850SDavid van Moolenbroek  * Implementation-wise, the Unix98 and non-Unix98 pseudoterminals share the
28*da21d850SDavid van Moolenbroek  * same pool of data structures, but use different ranges of minor numbers.
29*da21d850SDavid van Moolenbroek  * Access to the two types may not be mixed, and thus, some parts of the code
30*da21d850SDavid van Moolenbroek  * have checks to make sure a traditional slave is not opened for a master
31*da21d850SDavid van Moolenbroek  * allocated through /dev/ptmx, etcetera.
32433d6423SLionel Sambuc  */
33433d6423SLionel Sambuc 
34433d6423SLionel Sambuc #include <minix/drivers.h>
35*da21d850SDavid van Moolenbroek #include <paths.h>
36433d6423SLionel Sambuc #include <termios.h>
37433d6423SLionel Sambuc #include <assert.h>
38433d6423SLionel Sambuc #include <sys/termios.h>
39433d6423SLionel Sambuc #include <signal.h>
40433d6423SLionel Sambuc #include "tty.h"
41*da21d850SDavid van Moolenbroek #include "ptyfs.h"
42*da21d850SDavid van Moolenbroek 
43*da21d850SDavid van Moolenbroek /* Device node attributes used for Unix98 slave nodes. */
44*da21d850SDavid van Moolenbroek #define UNIX98_MODE		(S_IFCHR | 0620)	/* crw--w---- */
45*da21d850SDavid van Moolenbroek 
46*da21d850SDavid van Moolenbroek #define UNIX98_MASTER(index)	(UNIX98_MINOR + (index) * 2)
47*da21d850SDavid van Moolenbroek #define UNIX98_SLAVE(index)	(UNIX98_MINOR + (index) * 2 + 1)
48433d6423SLionel Sambuc 
49433d6423SLionel Sambuc /* PTY bookkeeping structure, one per pty/tty pair. */
50433d6423SLionel Sambuc typedef struct pty {
51433d6423SLionel Sambuc   tty_t		*tty;		/* associated TTY structure */
52433d6423SLionel Sambuc   char		state;		/* flags: busy, closed, ... */
53433d6423SLionel Sambuc 
54433d6423SLionel Sambuc   /* Read call on master (/dev/ptypX). */
55433d6423SLionel Sambuc   endpoint_t	rdcaller;	/* process making the call, or NONE if none */
56433d6423SLionel Sambuc   cdev_id_t	rdid;		/* ID of suspended read request */
57433d6423SLionel Sambuc   cp_grant_id_t	rdgrant;	/* grant for reader's address space */
58433d6423SLionel Sambuc   size_t	rdleft;		/* # bytes yet to be read */
59433d6423SLionel Sambuc   size_t	rdcum;		/* # bytes written so far */
60433d6423SLionel Sambuc 
61433d6423SLionel Sambuc   /* Write call to master (/dev/ptypX). */
62433d6423SLionel Sambuc   endpoint_t	wrcaller;	/* process making the call, or NONE if none*/
63433d6423SLionel Sambuc   cdev_id_t	wrid;		/* ID of suspended write request */
64433d6423SLionel Sambuc   cp_grant_id_t	wrgrant;	/* grant for writer's address space */
65433d6423SLionel Sambuc   size_t	wrleft;		/* # bytes yet to be written */
66433d6423SLionel Sambuc   size_t	wrcum;		/* # bytes written so far */
67433d6423SLionel Sambuc 
68433d6423SLionel Sambuc   /* Output buffer. */
69433d6423SLionel Sambuc   int		ocount;		/* # characters in the buffer */
70433d6423SLionel Sambuc   char		*ohead, *otail;	/* head and tail of the circular buffer */
71391516ddSDavid van Moolenbroek   char		obuf[TTY_OUT_BYTES];
72391516ddSDavid van Moolenbroek 				/* buffer for bytes going to the pty reader */
73433d6423SLionel Sambuc 
74433d6423SLionel Sambuc   /* select() data. */
75433d6423SLionel Sambuc   unsigned int	select_ops;	/* Which operations do we want to know about? */
76433d6423SLionel Sambuc   endpoint_t	select_proc;	/* Who wants to know about it? */
77*da21d850SDavid van Moolenbroek   devminor_t	select_minor;	/* Which minor was being selected on? */
78433d6423SLionel Sambuc } pty_t;
79433d6423SLionel Sambuc 
80433d6423SLionel Sambuc #define TTY_ACTIVE	0x01	/* tty is open/active */
81433d6423SLionel Sambuc #define PTY_ACTIVE	0x02	/* pty is open/active */
82433d6423SLionel Sambuc #define TTY_CLOSED	0x04	/* tty side has closed down */
83433d6423SLionel Sambuc #define PTY_CLOSED	0x08	/* pty side has closed down */
84*da21d850SDavid van Moolenbroek #define PTY_UNIX98	0x10	/* pty pair is Unix98 */
85433d6423SLionel Sambuc 
86433d6423SLionel Sambuc static pty_t pty_table[NR_PTYS];	/* PTY bookkeeping */
87433d6423SLionel Sambuc 
88433d6423SLionel Sambuc static void pty_start(pty_t *pp);
89433d6423SLionel Sambuc static void pty_finish(pty_t *pp);
90433d6423SLionel Sambuc 
91433d6423SLionel Sambuc static int pty_master_open(devminor_t minor, int access,
92433d6423SLionel Sambuc 	endpoint_t user_endpt);
93433d6423SLionel Sambuc static int pty_master_close(devminor_t minor);
94433d6423SLionel Sambuc static ssize_t pty_master_read(devminor_t minor, u64_t position,
95433d6423SLionel Sambuc 	endpoint_t endpt, cp_grant_id_t grant, size_t size, int flags,
96433d6423SLionel Sambuc 	cdev_id_t id);
97433d6423SLionel Sambuc static ssize_t pty_master_write(devminor_t minor, u64_t position,
98433d6423SLionel Sambuc 	endpoint_t endpt, cp_grant_id_t grant, size_t size, int flags,
99433d6423SLionel Sambuc 	cdev_id_t id);
100*da21d850SDavid van Moolenbroek static int pty_master_ioctl(devminor_t minor, unsigned long request,
101*da21d850SDavid van Moolenbroek 	endpoint_t endpt, cp_grant_id_t grant, int flags,
102*da21d850SDavid van Moolenbroek 	endpoint_t user_endpt, cdev_id_t id);
103433d6423SLionel Sambuc static int pty_master_cancel(devminor_t minor, endpoint_t endpt, cdev_id_t id);
104433d6423SLionel Sambuc static int pty_master_select(devminor_t minor, unsigned int ops,
105433d6423SLionel Sambuc 	endpoint_t endpt);
106433d6423SLionel Sambuc 
107433d6423SLionel Sambuc static struct chardriver pty_master_tab = {
108433d6423SLionel Sambuc   .cdr_open	= pty_master_open,
109433d6423SLionel Sambuc   .cdr_close	= pty_master_close,
110433d6423SLionel Sambuc   .cdr_read	= pty_master_read,
111433d6423SLionel Sambuc   .cdr_write	= pty_master_write,
112*da21d850SDavid van Moolenbroek   .cdr_ioctl	= pty_master_ioctl,
113433d6423SLionel Sambuc   .cdr_cancel	= pty_master_cancel,
114433d6423SLionel Sambuc   .cdr_select	= pty_master_select
115433d6423SLionel Sambuc };
116433d6423SLionel Sambuc 
117433d6423SLionel Sambuc /*===========================================================================*
118*da21d850SDavid van Moolenbroek  *				get_free_pty				     *
119*da21d850SDavid van Moolenbroek  *===========================================================================*/
120*da21d850SDavid van Moolenbroek static tty_t *get_free_pty(void)
121*da21d850SDavid van Moolenbroek {
122*da21d850SDavid van Moolenbroek /* Return a pointer to a free tty structure, or NULL if no tty is free. */
123*da21d850SDavid van Moolenbroek   tty_t *tp;
124*da21d850SDavid van Moolenbroek   pty_t *pp;
125*da21d850SDavid van Moolenbroek 
126*da21d850SDavid van Moolenbroek   for (tp = &tty_table[0]; tp < &tty_table[NR_PTYS]; tp++) {
127*da21d850SDavid van Moolenbroek 	pp = tp->tty_priv;
128*da21d850SDavid van Moolenbroek 
129*da21d850SDavid van Moolenbroek 	if (!(pp->state & (PTY_ACTIVE | TTY_ACTIVE)))
130*da21d850SDavid van Moolenbroek 		return tp;
131*da21d850SDavid van Moolenbroek   }
132*da21d850SDavid van Moolenbroek 
133*da21d850SDavid van Moolenbroek   return NULL;
134*da21d850SDavid van Moolenbroek }
135*da21d850SDavid van Moolenbroek 
136*da21d850SDavid van Moolenbroek /*===========================================================================*
137433d6423SLionel Sambuc  *				pty_master_open				     *
138433d6423SLionel Sambuc  *===========================================================================*/
139433d6423SLionel Sambuc static int pty_master_open(devminor_t minor, int UNUSED(access),
140433d6423SLionel Sambuc 	endpoint_t UNUSED(user_endpt))
141433d6423SLionel Sambuc {
142433d6423SLionel Sambuc   tty_t *tp;
143433d6423SLionel Sambuc   pty_t *pp;
144*da21d850SDavid van Moolenbroek   int r;
145433d6423SLionel Sambuc 
146*da21d850SDavid van Moolenbroek   if (minor == PTMX_MINOR) {
147*da21d850SDavid van Moolenbroek 	/* /dev/ptmx acts as a cloning device. We return a free PTY master and
148*da21d850SDavid van Moolenbroek 	 * mark it as a UNIX98 type.
149*da21d850SDavid van Moolenbroek 	 */
150*da21d850SDavid van Moolenbroek 	if ((tp = get_free_pty()) == NULL)
151*da21d850SDavid van Moolenbroek 		return EAGAIN; /* POSIX says this is the right error code */
152*da21d850SDavid van Moolenbroek 
153*da21d850SDavid van Moolenbroek 	/* The following call has two purposes. First, we check right here
154*da21d850SDavid van Moolenbroek 	 * whether PTYFS is running at all; if not, the PTMX device cannot be
155*da21d850SDavid van Moolenbroek 	 * opened at all and userland can fall back to other allocation
156*da21d850SDavid van Moolenbroek 	 * methods right away. Second, in the exceptional case that the PTY
157*da21d850SDavid van Moolenbroek 	 * service is restarted while PTYFS keeps running, PTYFS may expose
158*da21d850SDavid van Moolenbroek 	 * stale slave nodes, which are a security hole if not removed as soon
159*da21d850SDavid van Moolenbroek 	 * as a new PTY pair is allocated.
160*da21d850SDavid van Moolenbroek 	 */
161*da21d850SDavid van Moolenbroek 	if (ptyfs_clear(tp->tty_index) != OK)
162*da21d850SDavid van Moolenbroek 		return EAGAIN;
163*da21d850SDavid van Moolenbroek 
164*da21d850SDavid van Moolenbroek 	pp = tp->tty_priv;
165*da21d850SDavid van Moolenbroek 	pp->state |= PTY_UNIX98;
166*da21d850SDavid van Moolenbroek 
167*da21d850SDavid van Moolenbroek 	minor = UNIX98_MASTER(tp->tty_index);
168*da21d850SDavid van Moolenbroek 
169*da21d850SDavid van Moolenbroek 	r = CDEV_CLONED | minor;
170*da21d850SDavid van Moolenbroek   } else {
171*da21d850SDavid van Moolenbroek 	/* There is no way to open Unix98 masters directly, except by messing
172*da21d850SDavid van Moolenbroek 	 * with mknod. We disallow such tricks altogether, and thus, the rest
173*da21d850SDavid van Moolenbroek 	 * of the code deals with opening a non-Unix98 master only.
174*da21d850SDavid van Moolenbroek 	 */
175*da21d850SDavid van Moolenbroek 	if (minor < PTYPX_MINOR || minor >= PTYPX_MINOR + NR_PTYS)
176*da21d850SDavid van Moolenbroek 		return EIO;
177433d6423SLionel Sambuc 
178433d6423SLionel Sambuc 	if ((tp = line2tty(minor)) == NULL)
179433d6423SLionel Sambuc 		return ENXIO;
180433d6423SLionel Sambuc 	pp = tp->tty_priv;
181433d6423SLionel Sambuc 
182*da21d850SDavid van Moolenbroek 	/* For non-Unix98 PTYs, we allow the slave to be opened before the
183*da21d850SDavid van Moolenbroek 	 * master, but the master may be opened only once. This is how userland
184*da21d850SDavid van Moolenbroek 	 * is able to find a free non-Unix98 PTY pair.
185*da21d850SDavid van Moolenbroek 	 */
186433d6423SLionel Sambuc 	if (pp->state & PTY_ACTIVE)
187433d6423SLionel Sambuc 		return EIO;
188*da21d850SDavid van Moolenbroek 	assert(!(pp->state & PTY_UNIX98));
189*da21d850SDavid van Moolenbroek 
190*da21d850SDavid van Moolenbroek 	r = OK;
191*da21d850SDavid van Moolenbroek   }
192433d6423SLionel Sambuc 
193433d6423SLionel Sambuc   pp->state |= PTY_ACTIVE;
194*da21d850SDavid van Moolenbroek 
195433d6423SLionel Sambuc   pp->rdcum = 0;
196433d6423SLionel Sambuc   pp->wrcum = 0;
197433d6423SLionel Sambuc 
198*da21d850SDavid van Moolenbroek   return r;
199*da21d850SDavid van Moolenbroek }
200*da21d850SDavid van Moolenbroek 
201*da21d850SDavid van Moolenbroek /*===========================================================================*
202*da21d850SDavid van Moolenbroek  *				pty_reset				     *
203*da21d850SDavid van Moolenbroek  *===========================================================================*/
204*da21d850SDavid van Moolenbroek static void pty_reset(tty_t *tp)
205*da21d850SDavid van Moolenbroek {
206*da21d850SDavid van Moolenbroek /* Both sides of a PTY pair have been closed. Clean up its state. */
207*da21d850SDavid van Moolenbroek   pty_t *pp;
208*da21d850SDavid van Moolenbroek 
209*da21d850SDavid van Moolenbroek   pp = tp->tty_priv;
210*da21d850SDavid van Moolenbroek 
211*da21d850SDavid van Moolenbroek   /* For Unix98 pairs, clean up the Unix98 slave node. It may never have been
212*da21d850SDavid van Moolenbroek    * allocated, but we don't care. Ignore failures altogether.
213*da21d850SDavid van Moolenbroek    */
214*da21d850SDavid van Moolenbroek   if (pp->state & PTY_UNIX98)
215*da21d850SDavid van Moolenbroek 	(void)ptyfs_clear(tp->tty_index);
216*da21d850SDavid van Moolenbroek 
217*da21d850SDavid van Moolenbroek   pp->state = 0;
218433d6423SLionel Sambuc }
219433d6423SLionel Sambuc 
220433d6423SLionel Sambuc /*===========================================================================*
221433d6423SLionel Sambuc  *				pty_master_close			     *
222433d6423SLionel Sambuc  *===========================================================================*/
223433d6423SLionel Sambuc static int pty_master_close(devminor_t minor)
224433d6423SLionel Sambuc {
225433d6423SLionel Sambuc   tty_t *tp;
226433d6423SLionel Sambuc   pty_t *pp;
227433d6423SLionel Sambuc 
228433d6423SLionel Sambuc   if ((tp = line2tty(minor)) == NULL)
229433d6423SLionel Sambuc 	return ENXIO;
230433d6423SLionel Sambuc   pp = tp->tty_priv;
231433d6423SLionel Sambuc 
232433d6423SLionel Sambuc   if ((pp->state & (TTY_ACTIVE | TTY_CLOSED)) != TTY_ACTIVE) {
233*da21d850SDavid van Moolenbroek 	pty_reset(tp);
234433d6423SLionel Sambuc   } else {
235433d6423SLionel Sambuc 	pp->state |= PTY_CLOSED;
236673c4e01SDavid van Moolenbroek 	tp->tty_termios.c_ospeed = B0; /* cause EOF on slave side */
237433d6423SLionel Sambuc 	sigchar(tp, SIGHUP, 1);
238433d6423SLionel Sambuc   }
239433d6423SLionel Sambuc 
240433d6423SLionel Sambuc   return OK;
241433d6423SLionel Sambuc }
242433d6423SLionel Sambuc 
243433d6423SLionel Sambuc /*===========================================================================*
244433d6423SLionel Sambuc  *				pty_master_read				     *
245433d6423SLionel Sambuc  *===========================================================================*/
246433d6423SLionel Sambuc static ssize_t pty_master_read(devminor_t minor, u64_t UNUSED(position),
247433d6423SLionel Sambuc 	endpoint_t endpt, cp_grant_id_t grant, size_t size, int flags,
248433d6423SLionel Sambuc 	cdev_id_t id)
249433d6423SLionel Sambuc {
250433d6423SLionel Sambuc   tty_t *tp;
251433d6423SLionel Sambuc   pty_t *pp;
252433d6423SLionel Sambuc   ssize_t r;
253433d6423SLionel Sambuc 
254433d6423SLionel Sambuc   if ((tp = line2tty(minor)) == NULL)
255433d6423SLionel Sambuc 	return ENXIO;
256433d6423SLionel Sambuc   pp = tp->tty_priv;
257433d6423SLionel Sambuc 
258433d6423SLionel Sambuc   /* Check, store information on the reader, do I/O. */
259433d6423SLionel Sambuc   if (pp->state & TTY_CLOSED)
260433d6423SLionel Sambuc 	return 0; /* EOF */
261433d6423SLionel Sambuc 
262433d6423SLionel Sambuc   if (pp->rdcaller != NONE || pp->rdleft != 0 || pp->rdcum != 0)
263433d6423SLionel Sambuc 	return EIO;
264433d6423SLionel Sambuc 
265433d6423SLionel Sambuc   if (size <= 0)
266433d6423SLionel Sambuc 	return EINVAL;
267433d6423SLionel Sambuc 
268433d6423SLionel Sambuc   pp->rdcaller = endpt;
269433d6423SLionel Sambuc   pp->rdid = id;
270433d6423SLionel Sambuc   pp->rdgrant = grant;
271433d6423SLionel Sambuc   pp->rdleft = size;
272433d6423SLionel Sambuc   pty_start(pp);
273433d6423SLionel Sambuc 
274433d6423SLionel Sambuc   handle_events(tp);
275433d6423SLionel Sambuc 
276433d6423SLionel Sambuc   if (pp->rdleft == 0) {
277433d6423SLionel Sambuc 	pp->rdcaller = NONE;
278433d6423SLionel Sambuc 	return EDONTREPLY;		/* already done */
279433d6423SLionel Sambuc   }
280433d6423SLionel Sambuc 
281433d6423SLionel Sambuc   if (flags & CDEV_NONBLOCK) {
282433d6423SLionel Sambuc 	r = pp->rdcum > 0 ? pp->rdcum : EAGAIN;
283433d6423SLionel Sambuc 	pp->rdleft = pp->rdcum = 0;
284433d6423SLionel Sambuc 	pp->rdcaller = NONE;
285433d6423SLionel Sambuc 	return r;
286433d6423SLionel Sambuc   }
287433d6423SLionel Sambuc 
288433d6423SLionel Sambuc   return EDONTREPLY;			/* do suspend */
289433d6423SLionel Sambuc }
290433d6423SLionel Sambuc 
291433d6423SLionel Sambuc /*===========================================================================*
292433d6423SLionel Sambuc  *				pty_master_write			     *
293433d6423SLionel Sambuc  *===========================================================================*/
294433d6423SLionel Sambuc static ssize_t pty_master_write(devminor_t minor, u64_t UNUSED(position),
295433d6423SLionel Sambuc 	endpoint_t endpt, cp_grant_id_t grant, size_t size, int flags,
296433d6423SLionel Sambuc 	cdev_id_t id)
297433d6423SLionel Sambuc {
298433d6423SLionel Sambuc   tty_t *tp;
299433d6423SLionel Sambuc   pty_t *pp;
300433d6423SLionel Sambuc   ssize_t r;
301433d6423SLionel Sambuc 
302433d6423SLionel Sambuc   if ((tp = line2tty(minor)) == NULL)
303433d6423SLionel Sambuc 	return ENXIO;
304433d6423SLionel Sambuc   pp = tp->tty_priv;
305433d6423SLionel Sambuc 
306433d6423SLionel Sambuc   /* Check, store information on the writer, do I/O. */
307433d6423SLionel Sambuc   if (pp->state & TTY_CLOSED)
308433d6423SLionel Sambuc 	return EIO;
309433d6423SLionel Sambuc 
310433d6423SLionel Sambuc   if (pp->wrcaller != NONE || pp->wrleft != 0 || pp->wrcum != 0)
311433d6423SLionel Sambuc 	return EIO;
312433d6423SLionel Sambuc 
313433d6423SLionel Sambuc   if (size <= 0)
314433d6423SLionel Sambuc 	return EINVAL;
315433d6423SLionel Sambuc 
316433d6423SLionel Sambuc   pp->wrcaller = endpt;
317433d6423SLionel Sambuc   pp->wrid = id;
318433d6423SLionel Sambuc   pp->wrgrant = grant;
319433d6423SLionel Sambuc   pp->wrleft = size;
320433d6423SLionel Sambuc 
321433d6423SLionel Sambuc   handle_events(tp);
322433d6423SLionel Sambuc 
323433d6423SLionel Sambuc   if (pp->wrleft == 0) {
324433d6423SLionel Sambuc 	pp->wrcaller = NONE;
325433d6423SLionel Sambuc 	return EDONTREPLY;		/* already done */
326433d6423SLionel Sambuc   }
327433d6423SLionel Sambuc 
328433d6423SLionel Sambuc   if (flags & CDEV_NONBLOCK) {
329433d6423SLionel Sambuc 	r = pp->wrcum > 0 ? pp->wrcum : EAGAIN;
330433d6423SLionel Sambuc 	pp->wrleft = pp->wrcum = 0;
331433d6423SLionel Sambuc 	pp->wrcaller = NONE;
332433d6423SLionel Sambuc 	return r;
333433d6423SLionel Sambuc   }
334433d6423SLionel Sambuc 
335433d6423SLionel Sambuc   return EDONTREPLY;			/* do suspend */
336433d6423SLionel Sambuc }
337433d6423SLionel Sambuc 
338433d6423SLionel Sambuc /*===========================================================================*
339*da21d850SDavid van Moolenbroek  *				pty_master_ioctl			     *
340*da21d850SDavid van Moolenbroek  *===========================================================================*/
341*da21d850SDavid van Moolenbroek static int pty_master_ioctl(devminor_t minor, unsigned long request,
342*da21d850SDavid van Moolenbroek 	endpoint_t endpt, cp_grant_id_t grant, int flags,
343*da21d850SDavid van Moolenbroek 	endpoint_t user_endpt, cdev_id_t id)
344*da21d850SDavid van Moolenbroek {
345*da21d850SDavid van Moolenbroek   tty_t *tp;
346*da21d850SDavid van Moolenbroek   pty_t *pp;
347*da21d850SDavid van Moolenbroek   uid_t uid;
348*da21d850SDavid van Moolenbroek   struct ptmget pm;
349*da21d850SDavid van Moolenbroek   size_t len;
350*da21d850SDavid van Moolenbroek 
351*da21d850SDavid van Moolenbroek   if ((tp = line2tty(minor)) == NULL)
352*da21d850SDavid van Moolenbroek 	return ENXIO;
353*da21d850SDavid van Moolenbroek   pp = tp->tty_priv;
354*da21d850SDavid van Moolenbroek 
355*da21d850SDavid van Moolenbroek   /* Some IOCTLs are for the master side only. */
356*da21d850SDavid van Moolenbroek   switch (request) {
357*da21d850SDavid van Moolenbroek   case TIOCGRANTPT:	/* grantpt(3) */
358*da21d850SDavid van Moolenbroek 	if (!(pp->state & PTY_UNIX98))
359*da21d850SDavid van Moolenbroek 		break;
360*da21d850SDavid van Moolenbroek 
361*da21d850SDavid van Moolenbroek 	if ((int)(uid = getnuid(user_endpt)) == -1)
362*da21d850SDavid van Moolenbroek 		return EACCES;
363*da21d850SDavid van Moolenbroek 	if (tty_gid == -1) {
364*da21d850SDavid van Moolenbroek 		printf("PTY: no tty group ID given at startup\n");
365*da21d850SDavid van Moolenbroek 		return EACCES;
366*da21d850SDavid van Moolenbroek 	}
367*da21d850SDavid van Moolenbroek 
368*da21d850SDavid van Moolenbroek 	/* Create or update the slave node. */
369*da21d850SDavid van Moolenbroek 	if (ptyfs_set(tp->tty_index, UNIX98_MODE, uid, tty_gid,
370*da21d850SDavid van Moolenbroek 	    makedev(PTY_MAJOR, UNIX98_SLAVE(tp->tty_index))) != OK)
371*da21d850SDavid van Moolenbroek 		return EACCES;
372*da21d850SDavid van Moolenbroek 
373*da21d850SDavid van Moolenbroek 	return OK;
374*da21d850SDavid van Moolenbroek 
375*da21d850SDavid van Moolenbroek   case TIOCPTSNAME:	/* ptsname(3) */
376*da21d850SDavid van Moolenbroek 	if (!(pp->state & PTY_UNIX98))
377*da21d850SDavid van Moolenbroek 		break;
378*da21d850SDavid van Moolenbroek 
379*da21d850SDavid van Moolenbroek 	/* Since pm.sn is 16 bytes, we can have up to a million slaves. */
380*da21d850SDavid van Moolenbroek 	memset(&pm, 0, sizeof(pm));
381*da21d850SDavid van Moolenbroek 
382*da21d850SDavid van Moolenbroek 	strlcpy(pm.sn, _PATH_DEV_PTS, sizeof(pm.sn));
383*da21d850SDavid van Moolenbroek 	len = strlen(pm.sn);
384*da21d850SDavid van Moolenbroek 
385*da21d850SDavid van Moolenbroek 	if (ptyfs_name(tp->tty_index, &pm.sn[len], sizeof(pm.sn) - len) != OK)
386*da21d850SDavid van Moolenbroek 		return EINVAL;
387*da21d850SDavid van Moolenbroek 
388*da21d850SDavid van Moolenbroek 	return sys_safecopyto(endpt, grant, 0, (vir_bytes)&pm, sizeof(pm));
389*da21d850SDavid van Moolenbroek   }
390*da21d850SDavid van Moolenbroek 
391*da21d850SDavid van Moolenbroek   /* TODO: historically, all IOCTLs on the master are processed as if issued on
392*da21d850SDavid van Moolenbroek    * the slave end. Make sure that this can not cause problems, in particular
393*da21d850SDavid van Moolenbroek    * with blocking IOCTLs.
394*da21d850SDavid van Moolenbroek    */
395*da21d850SDavid van Moolenbroek   return tty_ioctl(minor, request, endpt, grant, flags, user_endpt, id);
396*da21d850SDavid van Moolenbroek }
397*da21d850SDavid van Moolenbroek 
398*da21d850SDavid van Moolenbroek /*===========================================================================*
399433d6423SLionel Sambuc  *				pty_master_cancel			     *
400433d6423SLionel Sambuc  *===========================================================================*/
401433d6423SLionel Sambuc static int pty_master_cancel(devminor_t minor, endpoint_t endpt, cdev_id_t id)
402433d6423SLionel Sambuc {
403433d6423SLionel Sambuc   tty_t *tp;
404433d6423SLionel Sambuc   pty_t *pp;
405433d6423SLionel Sambuc   int r;
406433d6423SLionel Sambuc 
407433d6423SLionel Sambuc   if ((tp = line2tty(minor)) == NULL)
408433d6423SLionel Sambuc 	return ENXIO;
409433d6423SLionel Sambuc   pp = tp->tty_priv;
410433d6423SLionel Sambuc 
411433d6423SLionel Sambuc   if (pp->rdcaller == endpt && pp->rdid == id) {
412433d6423SLionel Sambuc 	/* Cancel a read from a PTY. */
413433d6423SLionel Sambuc 	r = pp->rdcum > 0 ? pp->rdcum : EINTR;
414433d6423SLionel Sambuc 	pp->rdleft = pp->rdcum = 0;
415433d6423SLionel Sambuc 	pp->rdcaller = NONE;
416433d6423SLionel Sambuc 	return r;
417433d6423SLionel Sambuc   }
418433d6423SLionel Sambuc 
419433d6423SLionel Sambuc   if (pp->wrcaller == endpt && pp->wrid == id) {
420433d6423SLionel Sambuc 	/* Cancel a write to a PTY. */
421433d6423SLionel Sambuc 	r = pp->wrcum > 0 ? pp->wrcum : EINTR;
422433d6423SLionel Sambuc 	pp->wrleft = pp->wrcum = 0;
423433d6423SLionel Sambuc 	pp->wrcaller = NONE;
424433d6423SLionel Sambuc 	return r;
425433d6423SLionel Sambuc   }
426433d6423SLionel Sambuc 
427433d6423SLionel Sambuc   /* Request not found. */
428433d6423SLionel Sambuc   return EDONTREPLY;
429433d6423SLionel Sambuc }
430433d6423SLionel Sambuc 
431433d6423SLionel Sambuc /*===========================================================================*
432433d6423SLionel Sambuc  *				select_try_pty				     *
433433d6423SLionel Sambuc  *===========================================================================*/
434433d6423SLionel Sambuc static int select_try_pty(tty_t *tp, int ops)
435433d6423SLionel Sambuc {
436433d6423SLionel Sambuc   pty_t *pp = tp->tty_priv;
437433d6423SLionel Sambuc   int r = 0;
438433d6423SLionel Sambuc 
439433d6423SLionel Sambuc   if (ops & CDEV_OP_WR)  {
440433d6423SLionel Sambuc 	/* Write won't block on error. */
441433d6423SLionel Sambuc 	if (pp->state & TTY_CLOSED) r |= CDEV_OP_WR;
442433d6423SLionel Sambuc 	else if (pp->wrleft != 0 || pp->wrcum != 0) r |= CDEV_OP_WR;
443433d6423SLionel Sambuc 	else if (tp->tty_incount < buflen(tp->tty_inbuf)) r |= CDEV_OP_WR;
444433d6423SLionel Sambuc   }
445433d6423SLionel Sambuc 
446433d6423SLionel Sambuc   if (ops & CDEV_OP_RD) {
447433d6423SLionel Sambuc 	/* Read won't block on error. */
448433d6423SLionel Sambuc 	if (pp->state & TTY_CLOSED) r |= CDEV_OP_RD;
449433d6423SLionel Sambuc 	else if (pp->rdleft != 0 || pp->rdcum != 0) r |= CDEV_OP_RD;
450433d6423SLionel Sambuc 	else if (pp->ocount > 0) r |= CDEV_OP_RD;	/* Actual data. */
451433d6423SLionel Sambuc   }
452433d6423SLionel Sambuc 
453433d6423SLionel Sambuc   return r;
454433d6423SLionel Sambuc }
455433d6423SLionel Sambuc 
456433d6423SLionel Sambuc /*===========================================================================*
457433d6423SLionel Sambuc  *				select_retry_pty			     *
458433d6423SLionel Sambuc  *===========================================================================*/
459433d6423SLionel Sambuc void select_retry_pty(tty_t *tp)
460433d6423SLionel Sambuc {
461433d6423SLionel Sambuc   pty_t *pp = tp->tty_priv;
462433d6423SLionel Sambuc   int r;
463433d6423SLionel Sambuc 
464433d6423SLionel Sambuc   /* See if the pty side of a pty is ready to return a select. */
465433d6423SLionel Sambuc   if (pp->select_ops && (r = select_try_pty(tp, pp->select_ops))) {
466*da21d850SDavid van Moolenbroek 	chardriver_reply_select(pp->select_proc, pp->select_minor, r);
467433d6423SLionel Sambuc 	pp->select_ops &= ~r;
468433d6423SLionel Sambuc   }
469433d6423SLionel Sambuc }
470433d6423SLionel Sambuc 
471433d6423SLionel Sambuc /*===========================================================================*
472433d6423SLionel Sambuc  *				pty_master_select			     *
473433d6423SLionel Sambuc  *===========================================================================*/
474433d6423SLionel Sambuc static int pty_master_select(devminor_t minor, unsigned int ops,
475433d6423SLionel Sambuc 	endpoint_t endpt)
476433d6423SLionel Sambuc {
477433d6423SLionel Sambuc   tty_t *tp;
478433d6423SLionel Sambuc   pty_t *pp;
479433d6423SLionel Sambuc   int ready_ops, watch;
480433d6423SLionel Sambuc 
481433d6423SLionel Sambuc   if ((tp = line2tty(minor)) == NULL)
482433d6423SLionel Sambuc 	return ENXIO;
483433d6423SLionel Sambuc   pp = tp->tty_priv;
484433d6423SLionel Sambuc 
485433d6423SLionel Sambuc   watch = (ops & CDEV_NOTIFY);
486433d6423SLionel Sambuc   ops &= (CDEV_OP_RD | CDEV_OP_WR | CDEV_OP_ERR);
487433d6423SLionel Sambuc 
488433d6423SLionel Sambuc   ready_ops = select_try_pty(tp, ops);
489433d6423SLionel Sambuc 
490433d6423SLionel Sambuc   ops &= ~ready_ops;
491433d6423SLionel Sambuc   if (ops && watch) {
492433d6423SLionel Sambuc 	pp->select_ops |= ops;
493433d6423SLionel Sambuc 	pp->select_proc = endpt;
494*da21d850SDavid van Moolenbroek 	pp->select_minor = minor;
495433d6423SLionel Sambuc   }
496433d6423SLionel Sambuc 
497433d6423SLionel Sambuc   return ready_ops;
498433d6423SLionel Sambuc }
499433d6423SLionel Sambuc 
500433d6423SLionel Sambuc /*===========================================================================*
501433d6423SLionel Sambuc  *				do_pty					     *
502433d6423SLionel Sambuc  *===========================================================================*/
503433d6423SLionel Sambuc void do_pty(message *m_ptr, int ipc_status)
504433d6423SLionel Sambuc {
505433d6423SLionel Sambuc /* Process a request for a PTY master (/dev/ptypX) device. */
506433d6423SLionel Sambuc 
507433d6423SLionel Sambuc   chardriver_process(&pty_master_tab, m_ptr, ipc_status);
508433d6423SLionel Sambuc }
509433d6423SLionel Sambuc 
510433d6423SLionel Sambuc /*===========================================================================*
511433d6423SLionel Sambuc  *				pty_slave_write				     *
512433d6423SLionel Sambuc  *===========================================================================*/
513433d6423SLionel Sambuc static int pty_slave_write(tty_t *tp, int try)
514433d6423SLionel Sambuc {
515433d6423SLionel Sambuc /* (*dev_write)() routine for PTYs.  Transfer bytes from the writer on
516433d6423SLionel Sambuc  * /dev/ttypX to the output buffer.
517433d6423SLionel Sambuc  */
518433d6423SLionel Sambuc   pty_t *pp = tp->tty_priv;
519433d6423SLionel Sambuc   int count, ocount, s;
520433d6423SLionel Sambuc 
521433d6423SLionel Sambuc   /* PTY closed down? */
522433d6423SLionel Sambuc   if (pp->state & PTY_CLOSED) {
523433d6423SLionel Sambuc   	if (try) return 1;
524433d6423SLionel Sambuc 	if (tp->tty_outleft > 0) {
525433d6423SLionel Sambuc 		chardriver_reply_task(tp->tty_outcaller, tp->tty_outid, EIO);
526433d6423SLionel Sambuc 		tp->tty_outleft = tp->tty_outcum = 0;
527433d6423SLionel Sambuc 		tp->tty_outcaller = NONE;
528433d6423SLionel Sambuc 	}
529433d6423SLionel Sambuc 	return 0;
530433d6423SLionel Sambuc   }
531433d6423SLionel Sambuc 
532433d6423SLionel Sambuc   /* While there is something to do. */
533433d6423SLionel Sambuc   for (;;) {
534433d6423SLionel Sambuc 	ocount = buflen(pp->obuf) - pp->ocount;
535433d6423SLionel Sambuc 	if (try) return (ocount > 0);
536433d6423SLionel Sambuc 	count = bufend(pp->obuf) - pp->ohead;
537433d6423SLionel Sambuc 	if (count > ocount) count = ocount;
538433d6423SLionel Sambuc 	if (count > tp->tty_outleft) count = tp->tty_outleft;
539433d6423SLionel Sambuc 	if (count == 0 || tp->tty_inhibited)
540433d6423SLionel Sambuc 		break;
541433d6423SLionel Sambuc 
542433d6423SLionel Sambuc 	/* Copy from user space to the PTY output buffer. */
543433d6423SLionel Sambuc 	if (tp->tty_outcaller == KERNEL) {
544433d6423SLionel Sambuc 		/* We're trying to print on kernel's behalf */
545433d6423SLionel Sambuc 		memcpy(pp->ohead, (void *) tp->tty_outgrant + tp->tty_outcum,
546433d6423SLionel Sambuc 			count);
547433d6423SLionel Sambuc 	} else {
548433d6423SLionel Sambuc 		if ((s = sys_safecopyfrom(tp->tty_outcaller, tp->tty_outgrant,
549433d6423SLionel Sambuc 				tp->tty_outcum, (vir_bytes) pp->ohead,
550433d6423SLionel Sambuc 				count)) != OK) {
551433d6423SLionel Sambuc 			break;
552433d6423SLionel Sambuc 		}
553433d6423SLionel Sambuc 	}
554433d6423SLionel Sambuc 
555433d6423SLionel Sambuc 	/* Perform output processing on the output buffer. */
556433d6423SLionel Sambuc 	out_process(tp, pp->obuf, pp->ohead, bufend(pp->obuf), &count, &ocount);
557433d6423SLionel Sambuc 	if (count == 0) break;
558433d6423SLionel Sambuc 
559433d6423SLionel Sambuc 	/* Assume echoing messed up by output. */
560433d6423SLionel Sambuc 	tp->tty_reprint = TRUE;
561433d6423SLionel Sambuc 
562433d6423SLionel Sambuc 	/* Bookkeeping. */
563433d6423SLionel Sambuc 	pp->ocount += ocount;
564433d6423SLionel Sambuc 	if ((pp->ohead += ocount) >= bufend(pp->obuf))
565433d6423SLionel Sambuc 		pp->ohead -= buflen(pp->obuf);
566433d6423SLionel Sambuc 	pty_start(pp);
567433d6423SLionel Sambuc 
568433d6423SLionel Sambuc 	tp->tty_outcum += count;
569433d6423SLionel Sambuc 	if ((tp->tty_outleft -= count) == 0) {
570433d6423SLionel Sambuc 		/* Output is finished, reply to the writer. */
571433d6423SLionel Sambuc 		chardriver_reply_task(tp->tty_outcaller, tp->tty_outid,
572433d6423SLionel Sambuc 			tp->tty_outcum);
573433d6423SLionel Sambuc 		tp->tty_outcum = 0;
574433d6423SLionel Sambuc 		tp->tty_outcaller = NONE;
575433d6423SLionel Sambuc 	}
576433d6423SLionel Sambuc   }
577433d6423SLionel Sambuc   pty_finish(pp);
578433d6423SLionel Sambuc   return 1;
579433d6423SLionel Sambuc }
580433d6423SLionel Sambuc 
581433d6423SLionel Sambuc /*===========================================================================*
582433d6423SLionel Sambuc  *				pty_slave_echo				     *
583433d6423SLionel Sambuc  *===========================================================================*/
584433d6423SLionel Sambuc static void pty_slave_echo(tty_t *tp, int c)
585433d6423SLionel Sambuc {
586433d6423SLionel Sambuc /* Echo one character.  (Like pty_write, but only one character, optionally.) */
587433d6423SLionel Sambuc 
588433d6423SLionel Sambuc   pty_t *pp = tp->tty_priv;
589433d6423SLionel Sambuc   int count, ocount;
590433d6423SLionel Sambuc 
591433d6423SLionel Sambuc   ocount = buflen(pp->obuf) - pp->ocount;
592433d6423SLionel Sambuc   if (ocount == 0) return;		/* output buffer full */
593433d6423SLionel Sambuc   count = 1;
594433d6423SLionel Sambuc   *pp->ohead = c;			/* add one character */
595433d6423SLionel Sambuc 
596433d6423SLionel Sambuc   out_process(tp, pp->obuf, pp->ohead, bufend(pp->obuf), &count, &ocount);
597433d6423SLionel Sambuc   if (count == 0) return;
598433d6423SLionel Sambuc 
599433d6423SLionel Sambuc   pp->ocount += ocount;
600433d6423SLionel Sambuc   if ((pp->ohead += ocount) >= bufend(pp->obuf)) pp->ohead -= buflen(pp->obuf);
601433d6423SLionel Sambuc   pty_start(pp);
602433d6423SLionel Sambuc }
603433d6423SLionel Sambuc 
604433d6423SLionel Sambuc /*===========================================================================*
605433d6423SLionel Sambuc  *				pty_start				     *
606433d6423SLionel Sambuc  *===========================================================================*/
607433d6423SLionel Sambuc static void pty_start(pty_t *pp)
608433d6423SLionel Sambuc {
609433d6423SLionel Sambuc /* Transfer bytes written to the output buffer to the PTY reader. */
610433d6423SLionel Sambuc   int count;
611433d6423SLionel Sambuc 
612433d6423SLionel Sambuc   /* While there are things to do. */
613433d6423SLionel Sambuc   for (;;) {
614433d6423SLionel Sambuc   	int s;
615433d6423SLionel Sambuc 	count = bufend(pp->obuf) - pp->otail;
616433d6423SLionel Sambuc 	if (count > pp->ocount) count = pp->ocount;
617433d6423SLionel Sambuc 	if (count > pp->rdleft) count = pp->rdleft;
618433d6423SLionel Sambuc 	if (count == 0) break;
619433d6423SLionel Sambuc 
620433d6423SLionel Sambuc 	/* Copy from the output buffer to the readers address space. */
621433d6423SLionel Sambuc 	if((s = sys_safecopyto(pp->rdcaller, pp->rdgrant, pp->rdcum,
622433d6423SLionel Sambuc 		(vir_bytes) pp->otail, count)) != OK) {
623433d6423SLionel Sambuc 		break;
624433d6423SLionel Sambuc  	}
625433d6423SLionel Sambuc 
626433d6423SLionel Sambuc 	/* Bookkeeping. */
627433d6423SLionel Sambuc 	pp->ocount -= count;
628433d6423SLionel Sambuc 	if ((pp->otail += count) == bufend(pp->obuf)) pp->otail = pp->obuf;
629433d6423SLionel Sambuc 	pp->rdcum += count;
630433d6423SLionel Sambuc 	pp->rdleft -= count;
631433d6423SLionel Sambuc   }
632433d6423SLionel Sambuc }
633433d6423SLionel Sambuc 
634433d6423SLionel Sambuc /*===========================================================================*
635433d6423SLionel Sambuc  *				pty_finish				     *
636433d6423SLionel Sambuc  *===========================================================================*/
637433d6423SLionel Sambuc static void pty_finish(pty_t *pp)
638433d6423SLionel Sambuc {
639433d6423SLionel Sambuc /* Finish the read request of a PTY reader if there is at least one byte
640433d6423SLionel Sambuc  * transferred.
641433d6423SLionel Sambuc  */
642433d6423SLionel Sambuc 
643433d6423SLionel Sambuc   if (pp->rdcum > 0) {
644433d6423SLionel Sambuc 	chardriver_reply_task(pp->rdcaller, pp->rdid, pp->rdcum);
645433d6423SLionel Sambuc 	pp->rdleft = pp->rdcum = 0;
646433d6423SLionel Sambuc 	pp->rdcaller = NONE;
647433d6423SLionel Sambuc   }
648433d6423SLionel Sambuc }
649433d6423SLionel Sambuc 
650433d6423SLionel Sambuc /*===========================================================================*
651433d6423SLionel Sambuc  *				pty_slave_read				     *
652433d6423SLionel Sambuc  *===========================================================================*/
653433d6423SLionel Sambuc static int pty_slave_read(tty_t *tp, int try)
654433d6423SLionel Sambuc {
655433d6423SLionel Sambuc /* Offer bytes from the PTY writer for input on the TTY.  (Do it one byte at
656433d6423SLionel Sambuc  * a time, 99% of the writes will be for one byte, so no sense in being smart.)
657433d6423SLionel Sambuc  */
658433d6423SLionel Sambuc   pty_t *pp = tp->tty_priv;
659433d6423SLionel Sambuc   char c;
660433d6423SLionel Sambuc 
661433d6423SLionel Sambuc   if (pp->state & PTY_CLOSED) {
662433d6423SLionel Sambuc 	if (try) return 1;
663433d6423SLionel Sambuc 	if (tp->tty_inleft > 0) {
664433d6423SLionel Sambuc 		chardriver_reply_task(tp->tty_incaller, tp->tty_inid,
665433d6423SLionel Sambuc 			tp->tty_incum);
666433d6423SLionel Sambuc 		tp->tty_inleft = tp->tty_incum = 0;
667433d6423SLionel Sambuc 		tp->tty_incaller = NONE;
668433d6423SLionel Sambuc 	}
669433d6423SLionel Sambuc 	return 1;
670433d6423SLionel Sambuc   }
671433d6423SLionel Sambuc 
672433d6423SLionel Sambuc   if (try) {
673433d6423SLionel Sambuc   	if (pp->wrleft > 0)
674433d6423SLionel Sambuc   		return 1;
675433d6423SLionel Sambuc   	return 0;
676433d6423SLionel Sambuc   }
677433d6423SLionel Sambuc 
678433d6423SLionel Sambuc   while (pp->wrleft > 0) {
679433d6423SLionel Sambuc   	int s;
680433d6423SLionel Sambuc 
681433d6423SLionel Sambuc 	/* Transfer one character to 'c'. */
682433d6423SLionel Sambuc 	if ((s = sys_safecopyfrom(pp->wrcaller, pp->wrgrant, pp->wrcum,
683433d6423SLionel Sambuc 		(vir_bytes) &c, 1)) != OK) {
684433d6423SLionel Sambuc 		printf("pty: safecopy failed (error %d)\n", s);
685433d6423SLionel Sambuc 		break;
686433d6423SLionel Sambuc 	}
687433d6423SLionel Sambuc 
688433d6423SLionel Sambuc 	/* Input processing. */
689433d6423SLionel Sambuc 	if (in_process(tp, &c, 1) == 0) break;
690433d6423SLionel Sambuc 
691433d6423SLionel Sambuc 	/* PTY writer bookkeeping. */
692433d6423SLionel Sambuc 	pp->wrcum++;
693433d6423SLionel Sambuc 	if (--pp->wrleft == 0) {
694433d6423SLionel Sambuc 		chardriver_reply_task(pp->wrcaller, pp->wrid, pp->wrcum);
695433d6423SLionel Sambuc 		pp->wrcum = 0;
696433d6423SLionel Sambuc 		pp->wrcaller = NONE;
697433d6423SLionel Sambuc 	}
698433d6423SLionel Sambuc   }
699433d6423SLionel Sambuc 
700433d6423SLionel Sambuc   return 0;
701433d6423SLionel Sambuc }
702433d6423SLionel Sambuc 
703433d6423SLionel Sambuc /*===========================================================================*
704*da21d850SDavid van Moolenbroek  *				pty_slave_mayopen			     *
705*da21d850SDavid van Moolenbroek  *===========================================================================*/
706*da21d850SDavid van Moolenbroek static int pty_slave_mayopen(tty_t *tp, devminor_t line)
707*da21d850SDavid van Moolenbroek {
708*da21d850SDavid van Moolenbroek /* Check if the user is not mixing Unix98 and non-Unix98 terminal ends. */
709*da21d850SDavid van Moolenbroek   pty_t *pp;
710*da21d850SDavid van Moolenbroek   int unix98_line, unix98_pty;
711*da21d850SDavid van Moolenbroek 
712*da21d850SDavid van Moolenbroek   pp = tp->tty_priv;
713*da21d850SDavid van Moolenbroek 
714*da21d850SDavid van Moolenbroek   /* A non-Unix98 slave may be opened even if the corresponding master is not
715*da21d850SDavid van Moolenbroek    * opened yet, but PTY_UNIX98 is always clear for free ptys.  A Unix98 slave
716*da21d850SDavid van Moolenbroek    * may not be opened before its master, but this should not occur anyway.
717*da21d850SDavid van Moolenbroek    */
718*da21d850SDavid van Moolenbroek   unix98_line = (line >= UNIX98_MINOR && line < UNIX98_MINOR + NR_PTYS * 2);
719*da21d850SDavid van Moolenbroek   unix98_pty = !!(pp->state & PTY_UNIX98);
720*da21d850SDavid van Moolenbroek 
721*da21d850SDavid van Moolenbroek   return (unix98_line == unix98_pty);
722*da21d850SDavid van Moolenbroek }
723*da21d850SDavid van Moolenbroek 
724*da21d850SDavid van Moolenbroek /*===========================================================================*
725433d6423SLionel Sambuc  *				pty_slave_open				     *
726433d6423SLionel Sambuc  *===========================================================================*/
727433d6423SLionel Sambuc static int pty_slave_open(tty_t *tp, int UNUSED(try))
728433d6423SLionel Sambuc {
729433d6423SLionel Sambuc /* The tty side has been opened. */
730433d6423SLionel Sambuc   pty_t *pp = tp->tty_priv;
731433d6423SLionel Sambuc 
732433d6423SLionel Sambuc   /* TTY_ACTIVE may already be set, which would indicate that the slave is
733433d6423SLionel Sambuc    * reopened after being fully closed while the master is still open. In that
734433d6423SLionel Sambuc    * case TTY_CLOSED will also be set, so clear that one.
735433d6423SLionel Sambuc    */
736433d6423SLionel Sambuc   pp->state |= TTY_ACTIVE;
737433d6423SLionel Sambuc   pp->state &= ~TTY_CLOSED;
738433d6423SLionel Sambuc 
739433d6423SLionel Sambuc   return 0;
740433d6423SLionel Sambuc }
741433d6423SLionel Sambuc 
742433d6423SLionel Sambuc /*===========================================================================*
743433d6423SLionel Sambuc  *				pty_slave_close				     *
744433d6423SLionel Sambuc  *===========================================================================*/
745433d6423SLionel Sambuc static int pty_slave_close(tty_t *tp, int UNUSED(try))
746433d6423SLionel Sambuc {
747433d6423SLionel Sambuc /* The tty side has closed, so shut down the pty side. */
748433d6423SLionel Sambuc   pty_t *pp = tp->tty_priv;
749433d6423SLionel Sambuc 
750433d6423SLionel Sambuc   if (!(pp->state & PTY_ACTIVE)) return 0;
751433d6423SLionel Sambuc 
752433d6423SLionel Sambuc   if (pp->rdleft > 0) {
753433d6423SLionel Sambuc 	chardriver_reply_task(pp->rdcaller, pp->rdid, pp->rdcum);
754433d6423SLionel Sambuc 	pp->rdleft = pp->rdcum = 0;
755433d6423SLionel Sambuc 	pp->rdcaller = NONE;
756433d6423SLionel Sambuc   }
757433d6423SLionel Sambuc 
758433d6423SLionel Sambuc   if (pp->wrleft > 0) {
759433d6423SLionel Sambuc 	chardriver_reply_task(pp->wrcaller, pp->wrid, pp->wrcum);
760433d6423SLionel Sambuc 	pp->wrleft = pp->wrcum = 0;
761433d6423SLionel Sambuc 	pp->wrcaller = NONE;
762433d6423SLionel Sambuc   }
763433d6423SLionel Sambuc 
764*da21d850SDavid van Moolenbroek   if (pp->state & PTY_CLOSED) pty_reset(tp);
765433d6423SLionel Sambuc   else pp->state |= TTY_CLOSED;
766433d6423SLionel Sambuc 
767433d6423SLionel Sambuc   return 0;
768433d6423SLionel Sambuc }
769433d6423SLionel Sambuc 
770433d6423SLionel Sambuc /*===========================================================================*
771433d6423SLionel Sambuc  *				pty_slave_icancel			     *
772433d6423SLionel Sambuc  *===========================================================================*/
773433d6423SLionel Sambuc static int pty_slave_icancel(tty_t *tp, int UNUSED(try))
774433d6423SLionel Sambuc {
775433d6423SLionel Sambuc /* Discard waiting input. */
776433d6423SLionel Sambuc   pty_t *pp = tp->tty_priv;
777433d6423SLionel Sambuc 
778433d6423SLionel Sambuc   if (pp->wrleft > 0) {
779433d6423SLionel Sambuc 	chardriver_reply_task(pp->wrcaller, pp->wrid, pp->wrcum + pp->wrleft);
780433d6423SLionel Sambuc 	pp->wrcum = pp->wrleft = 0;
781433d6423SLionel Sambuc 	pp->wrcaller = NONE;
782433d6423SLionel Sambuc   }
783433d6423SLionel Sambuc 
784433d6423SLionel Sambuc   return 0;
785433d6423SLionel Sambuc }
786433d6423SLionel Sambuc 
787433d6423SLionel Sambuc /*===========================================================================*
788433d6423SLionel Sambuc  *				pty_slave_ocancel			     *
789433d6423SLionel Sambuc  *===========================================================================*/
790433d6423SLionel Sambuc static int pty_slave_ocancel(tty_t *tp, int UNUSED(try))
791433d6423SLionel Sambuc {
792433d6423SLionel Sambuc /* Drain the output buffer. */
793433d6423SLionel Sambuc   pty_t *pp = tp->tty_priv;
794433d6423SLionel Sambuc 
795433d6423SLionel Sambuc   pp->ocount = 0;
796433d6423SLionel Sambuc   pp->otail = pp->ohead;
797433d6423SLionel Sambuc 
798433d6423SLionel Sambuc   return 0;
799433d6423SLionel Sambuc }
800433d6423SLionel Sambuc 
801433d6423SLionel Sambuc /*===========================================================================*
802433d6423SLionel Sambuc  *				pty_init				     *
803433d6423SLionel Sambuc  *===========================================================================*/
804433d6423SLionel Sambuc void pty_init(tty_t *tp)
805433d6423SLionel Sambuc {
806433d6423SLionel Sambuc   pty_t *pp;
807433d6423SLionel Sambuc   int line;
808433d6423SLionel Sambuc 
809433d6423SLionel Sambuc   /* Associate PTY and TTY structures. */
810433d6423SLionel Sambuc   line = tp - tty_table;
811433d6423SLionel Sambuc   pp = tp->tty_priv = &pty_table[line];
812433d6423SLionel Sambuc   pp->tty = tp;
813433d6423SLionel Sambuc   pp->select_ops = 0;
814433d6423SLionel Sambuc   pp->rdcaller = NONE;
815433d6423SLionel Sambuc   pp->wrcaller = NONE;
816433d6423SLionel Sambuc 
817433d6423SLionel Sambuc   /* Set up output queue. */
818433d6423SLionel Sambuc   pp->ohead = pp->otail = pp->obuf;
819433d6423SLionel Sambuc 
820433d6423SLionel Sambuc   /* Fill in TTY function hooks. */
821433d6423SLionel Sambuc   tp->tty_devread = pty_slave_read;
822433d6423SLionel Sambuc   tp->tty_devwrite = pty_slave_write;
823433d6423SLionel Sambuc   tp->tty_echo = pty_slave_echo;
824433d6423SLionel Sambuc   tp->tty_icancel = pty_slave_icancel;
825433d6423SLionel Sambuc   tp->tty_ocancel = pty_slave_ocancel;
826*da21d850SDavid van Moolenbroek   tp->tty_mayopen = pty_slave_mayopen;
827433d6423SLionel Sambuc   tp->tty_open = pty_slave_open;
828433d6423SLionel Sambuc   tp->tty_close = pty_slave_close;
829433d6423SLionel Sambuc   tp->tty_select_ops = 0;
830433d6423SLionel Sambuc }
831