xref: /minix3/minix/drivers/tty/pty/pty.c (revision e6dabba58b8472f95e59c6a720793cc11c3b41fe)
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.
17da21d850SDavid van Moolenbroek  *
18da21d850SDavid van Moolenbroek  * In addition to the above, PTY service now also supports Unix98 pseudo-
19da21d850SDavid van Moolenbroek  * terminal pairs, thereby allowing non-root users to allocate pseudoterminals.
20da21d850SDavid van Moolenbroek  * It requires the presence for PTYFS for this, and supports only old-style
21da21d850SDavid van Moolenbroek  * ptys when PTYFS is not running. For Unix98 ptys, the general idea is that a
22da21d850SDavid van Moolenbroek  * userland program opens a pty master by opening /dev/ptmx through the use of
23da21d850SDavid van Moolenbroek  * posxix_openpt(3). A slave node is allocated on PTYFS when the program calls
24da21d850SDavid van Moolenbroek  * grantpt(3) on the master. The program can then obtain the path name for the
25da21d850SDavid van Moolenbroek  * slave end through ptsname(3), and open the slave end using this path.
26da21d850SDavid van Moolenbroek  *
27da21d850SDavid van Moolenbroek  * Implementation-wise, the Unix98 and non-Unix98 pseudoterminals share the
28da21d850SDavid van Moolenbroek  * same pool of data structures, but use different ranges of minor numbers.
29da21d850SDavid van Moolenbroek  * Access to the two types may not be mixed, and thus, some parts of the code
30da21d850SDavid van Moolenbroek  * have checks to make sure a traditional slave is not opened for a master
31da21d850SDavid van Moolenbroek  * allocated through /dev/ptmx, etcetera.
32433d6423SLionel Sambuc  */
33433d6423SLionel Sambuc 
34433d6423SLionel Sambuc #include <minix/drivers.h>
35da21d850SDavid 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"
41da21d850SDavid van Moolenbroek #include "ptyfs.h"
42da21d850SDavid van Moolenbroek 
43da21d850SDavid van Moolenbroek /* Device node attributes used for Unix98 slave nodes. */
44da21d850SDavid van Moolenbroek #define UNIX98_MODE		(S_IFCHR | 0620)	/* crw--w---- */
45da21d850SDavid van Moolenbroek 
46da21d850SDavid van Moolenbroek #define UNIX98_MASTER(index)	(UNIX98_MINOR + (index) * 2)
47da21d850SDavid 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? */
77da21d850SDavid 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 */
84da21d850SDavid van Moolenbroek #define PTY_UNIX98	0x10	/* pty pair is Unix98 */
85*e6dabba5SDavid van Moolenbroek #define PTY_PKTMODE	0x20	/* pty side is in packet mode (TIOCPKT) */
86433d6423SLionel Sambuc 
87433d6423SLionel Sambuc static pty_t pty_table[NR_PTYS];	/* PTY bookkeeping */
88433d6423SLionel Sambuc 
89433d6423SLionel Sambuc static void pty_start(pty_t *pp);
90433d6423SLionel Sambuc static void pty_finish(pty_t *pp);
91433d6423SLionel Sambuc 
92433d6423SLionel Sambuc static int pty_master_open(devminor_t minor, int access,
93433d6423SLionel Sambuc 	endpoint_t user_endpt);
94433d6423SLionel Sambuc static int pty_master_close(devminor_t minor);
95433d6423SLionel Sambuc static ssize_t pty_master_read(devminor_t minor, u64_t position,
96433d6423SLionel Sambuc 	endpoint_t endpt, cp_grant_id_t grant, size_t size, int flags,
97433d6423SLionel Sambuc 	cdev_id_t id);
98433d6423SLionel Sambuc static ssize_t pty_master_write(devminor_t minor, u64_t position,
99433d6423SLionel Sambuc 	endpoint_t endpt, cp_grant_id_t grant, size_t size, int flags,
100433d6423SLionel Sambuc 	cdev_id_t id);
101da21d850SDavid van Moolenbroek static int pty_master_ioctl(devminor_t minor, unsigned long request,
102da21d850SDavid van Moolenbroek 	endpoint_t endpt, cp_grant_id_t grant, int flags,
103da21d850SDavid van Moolenbroek 	endpoint_t user_endpt, cdev_id_t id);
104433d6423SLionel Sambuc static int pty_master_cancel(devminor_t minor, endpoint_t endpt, cdev_id_t id);
105433d6423SLionel Sambuc static int pty_master_select(devminor_t minor, unsigned int ops,
106433d6423SLionel Sambuc 	endpoint_t endpt);
107433d6423SLionel Sambuc 
108433d6423SLionel Sambuc static struct chardriver pty_master_tab = {
109433d6423SLionel Sambuc   .cdr_open	= pty_master_open,
110433d6423SLionel Sambuc   .cdr_close	= pty_master_close,
111433d6423SLionel Sambuc   .cdr_read	= pty_master_read,
112433d6423SLionel Sambuc   .cdr_write	= pty_master_write,
113da21d850SDavid van Moolenbroek   .cdr_ioctl	= pty_master_ioctl,
114433d6423SLionel Sambuc   .cdr_cancel	= pty_master_cancel,
115433d6423SLionel Sambuc   .cdr_select	= pty_master_select
116433d6423SLionel Sambuc };
117433d6423SLionel Sambuc 
118433d6423SLionel Sambuc /*===========================================================================*
119da21d850SDavid van Moolenbroek  *				get_free_pty				     *
120da21d850SDavid van Moolenbroek  *===========================================================================*/
get_free_pty(void)121da21d850SDavid van Moolenbroek static tty_t *get_free_pty(void)
122da21d850SDavid van Moolenbroek {
123da21d850SDavid van Moolenbroek /* Return a pointer to a free tty structure, or NULL if no tty is free. */
124da21d850SDavid van Moolenbroek   tty_t *tp;
125da21d850SDavid van Moolenbroek   pty_t *pp;
126da21d850SDavid van Moolenbroek 
127da21d850SDavid van Moolenbroek   for (tp = &tty_table[0]; tp < &tty_table[NR_PTYS]; tp++) {
128da21d850SDavid van Moolenbroek 	pp = tp->tty_priv;
129da21d850SDavid van Moolenbroek 
130da21d850SDavid van Moolenbroek 	if (!(pp->state & (PTY_ACTIVE | TTY_ACTIVE)))
131da21d850SDavid van Moolenbroek 		return tp;
132da21d850SDavid van Moolenbroek   }
133da21d850SDavid van Moolenbroek 
134da21d850SDavid van Moolenbroek   return NULL;
135da21d850SDavid van Moolenbroek }
136da21d850SDavid van Moolenbroek 
137da21d850SDavid van Moolenbroek /*===========================================================================*
138433d6423SLionel Sambuc  *				pty_master_open				     *
139433d6423SLionel Sambuc  *===========================================================================*/
pty_master_open(devminor_t minor,int UNUSED (access),endpoint_t UNUSED (user_endpt))140433d6423SLionel Sambuc static int pty_master_open(devminor_t minor, int UNUSED(access),
141433d6423SLionel Sambuc 	endpoint_t UNUSED(user_endpt))
142433d6423SLionel Sambuc {
143433d6423SLionel Sambuc   tty_t *tp;
144433d6423SLionel Sambuc   pty_t *pp;
145da21d850SDavid van Moolenbroek   int r;
146433d6423SLionel Sambuc 
147da21d850SDavid van Moolenbroek   if (minor == PTMX_MINOR) {
148da21d850SDavid van Moolenbroek 	/* /dev/ptmx acts as a cloning device. We return a free PTY master and
149da21d850SDavid van Moolenbroek 	 * mark it as a UNIX98 type.
150da21d850SDavid van Moolenbroek 	 */
151da21d850SDavid van Moolenbroek 	if ((tp = get_free_pty()) == NULL)
152da21d850SDavid van Moolenbroek 		return EAGAIN; /* POSIX says this is the right error code */
153da21d850SDavid van Moolenbroek 
154da21d850SDavid van Moolenbroek 	/* The following call has two purposes. First, we check right here
155da21d850SDavid van Moolenbroek 	 * whether PTYFS is running at all; if not, the PTMX device cannot be
156da21d850SDavid van Moolenbroek 	 * opened at all and userland can fall back to other allocation
157da21d850SDavid van Moolenbroek 	 * methods right away. Second, in the exceptional case that the PTY
158da21d850SDavid van Moolenbroek 	 * service is restarted while PTYFS keeps running, PTYFS may expose
159da21d850SDavid van Moolenbroek 	 * stale slave nodes, which are a security hole if not removed as soon
160da21d850SDavid van Moolenbroek 	 * as a new PTY pair is allocated.
161da21d850SDavid van Moolenbroek 	 */
162da21d850SDavid van Moolenbroek 	if (ptyfs_clear(tp->tty_index) != OK)
163da21d850SDavid van Moolenbroek 		return EAGAIN;
164da21d850SDavid van Moolenbroek 
165da21d850SDavid van Moolenbroek 	pp = tp->tty_priv;
166da21d850SDavid van Moolenbroek 	pp->state |= PTY_UNIX98;
167da21d850SDavid van Moolenbroek 
168da21d850SDavid van Moolenbroek 	minor = UNIX98_MASTER(tp->tty_index);
169da21d850SDavid van Moolenbroek 
170da21d850SDavid van Moolenbroek 	r = CDEV_CLONED | minor;
171da21d850SDavid van Moolenbroek   } else {
172da21d850SDavid van Moolenbroek 	/* There is no way to open Unix98 masters directly, except by messing
173da21d850SDavid van Moolenbroek 	 * with mknod. We disallow such tricks altogether, and thus, the rest
174da21d850SDavid van Moolenbroek 	 * of the code deals with opening a non-Unix98 master only.
175da21d850SDavid van Moolenbroek 	 */
176da21d850SDavid van Moolenbroek 	if (minor < PTYPX_MINOR || minor >= PTYPX_MINOR + NR_PTYS)
177da21d850SDavid van Moolenbroek 		return EIO;
178433d6423SLionel Sambuc 
179433d6423SLionel Sambuc 	if ((tp = line2tty(minor)) == NULL)
180433d6423SLionel Sambuc 		return ENXIO;
181433d6423SLionel Sambuc 	pp = tp->tty_priv;
182433d6423SLionel Sambuc 
183da21d850SDavid van Moolenbroek 	/* For non-Unix98 PTYs, we allow the slave to be opened before the
184da21d850SDavid van Moolenbroek 	 * master, but the master may be opened only once. This is how userland
185da21d850SDavid van Moolenbroek 	 * is able to find a free non-Unix98 PTY pair.
186da21d850SDavid van Moolenbroek 	 */
187433d6423SLionel Sambuc 	if (pp->state & PTY_ACTIVE)
188433d6423SLionel Sambuc 		return EIO;
189da21d850SDavid van Moolenbroek 	assert(!(pp->state & PTY_UNIX98));
190da21d850SDavid van Moolenbroek 
191da21d850SDavid van Moolenbroek 	r = OK;
192da21d850SDavid van Moolenbroek   }
193433d6423SLionel Sambuc 
194433d6423SLionel Sambuc   pp->state |= PTY_ACTIVE;
195da21d850SDavid van Moolenbroek 
196433d6423SLionel Sambuc   pp->rdcum = 0;
197433d6423SLionel Sambuc   pp->wrcum = 0;
198433d6423SLionel Sambuc 
199da21d850SDavid van Moolenbroek   return r;
200da21d850SDavid van Moolenbroek }
201da21d850SDavid van Moolenbroek 
202da21d850SDavid van Moolenbroek /*===========================================================================*
203da21d850SDavid van Moolenbroek  *				pty_reset				     *
204da21d850SDavid van Moolenbroek  *===========================================================================*/
pty_reset(tty_t * tp)205da21d850SDavid van Moolenbroek static void pty_reset(tty_t *tp)
206da21d850SDavid van Moolenbroek {
207da21d850SDavid van Moolenbroek /* Both sides of a PTY pair have been closed. Clean up its state. */
208da21d850SDavid van Moolenbroek   pty_t *pp;
209da21d850SDavid van Moolenbroek 
210da21d850SDavid van Moolenbroek   pp = tp->tty_priv;
211da21d850SDavid van Moolenbroek 
212da21d850SDavid van Moolenbroek   /* For Unix98 pairs, clean up the Unix98 slave node. It may never have been
213da21d850SDavid van Moolenbroek    * allocated, but we don't care. Ignore failures altogether.
214da21d850SDavid van Moolenbroek    */
215da21d850SDavid van Moolenbroek   if (pp->state & PTY_UNIX98)
216da21d850SDavid van Moolenbroek 	(void)ptyfs_clear(tp->tty_index);
217da21d850SDavid van Moolenbroek 
218da21d850SDavid van Moolenbroek   pp->state = 0;
219433d6423SLionel Sambuc }
220433d6423SLionel Sambuc 
221433d6423SLionel Sambuc /*===========================================================================*
222433d6423SLionel Sambuc  *				pty_master_close			     *
223433d6423SLionel Sambuc  *===========================================================================*/
pty_master_close(devminor_t minor)224433d6423SLionel Sambuc static int pty_master_close(devminor_t minor)
225433d6423SLionel Sambuc {
226433d6423SLionel Sambuc   tty_t *tp;
227433d6423SLionel Sambuc   pty_t *pp;
228433d6423SLionel Sambuc 
229433d6423SLionel Sambuc   if ((tp = line2tty(minor)) == NULL)
230433d6423SLionel Sambuc 	return ENXIO;
231433d6423SLionel Sambuc   pp = tp->tty_priv;
232433d6423SLionel Sambuc 
233433d6423SLionel Sambuc   if ((pp->state & (TTY_ACTIVE | TTY_CLOSED)) != TTY_ACTIVE) {
234da21d850SDavid van Moolenbroek 	pty_reset(tp);
235433d6423SLionel Sambuc   } else {
236433d6423SLionel Sambuc 	pp->state |= PTY_CLOSED;
237673c4e01SDavid van Moolenbroek 	tp->tty_termios.c_ospeed = B0; /* cause EOF on slave side */
238433d6423SLionel Sambuc 	sigchar(tp, SIGHUP, 1);
239433d6423SLionel Sambuc   }
240433d6423SLionel Sambuc 
241433d6423SLionel Sambuc   return OK;
242433d6423SLionel Sambuc }
243433d6423SLionel Sambuc 
244433d6423SLionel Sambuc /*===========================================================================*
245433d6423SLionel Sambuc  *				pty_master_read				     *
246433d6423SLionel Sambuc  *===========================================================================*/
pty_master_read(devminor_t minor,u64_t UNUSED (position),endpoint_t endpt,cp_grant_id_t grant,size_t size,int flags,cdev_id_t id)247433d6423SLionel Sambuc static ssize_t pty_master_read(devminor_t minor, u64_t UNUSED(position),
248433d6423SLionel Sambuc 	endpoint_t endpt, cp_grant_id_t grant, size_t size, int flags,
249433d6423SLionel Sambuc 	cdev_id_t id)
250433d6423SLionel Sambuc {
251433d6423SLionel Sambuc   tty_t *tp;
252433d6423SLionel Sambuc   pty_t *pp;
253433d6423SLionel Sambuc   ssize_t r;
254433d6423SLionel Sambuc 
255433d6423SLionel Sambuc   if ((tp = line2tty(minor)) == NULL)
256433d6423SLionel Sambuc 	return ENXIO;
257433d6423SLionel Sambuc   pp = tp->tty_priv;
258433d6423SLionel Sambuc 
259433d6423SLionel Sambuc   /* Check, store information on the reader, do I/O. */
260433d6423SLionel Sambuc   if (pp->state & TTY_CLOSED)
261433d6423SLionel Sambuc 	return 0; /* EOF */
262433d6423SLionel Sambuc 
263433d6423SLionel Sambuc   if (pp->rdcaller != NONE || pp->rdleft != 0 || pp->rdcum != 0)
264433d6423SLionel Sambuc 	return EIO;
265433d6423SLionel Sambuc 
266433d6423SLionel Sambuc   if (size <= 0)
267433d6423SLionel Sambuc 	return EINVAL;
268433d6423SLionel Sambuc 
269433d6423SLionel Sambuc   pp->rdcaller = endpt;
270433d6423SLionel Sambuc   pp->rdid = id;
271433d6423SLionel Sambuc   pp->rdgrant = grant;
272433d6423SLionel Sambuc   pp->rdleft = size;
273433d6423SLionel Sambuc   pty_start(pp);
274433d6423SLionel Sambuc 
275433d6423SLionel Sambuc   handle_events(tp);
276433d6423SLionel Sambuc 
277433d6423SLionel Sambuc   if (pp->rdleft == 0) {
278433d6423SLionel Sambuc 	pp->rdcaller = NONE;
279433d6423SLionel Sambuc 	return EDONTREPLY;		/* already done */
280433d6423SLionel Sambuc   }
281433d6423SLionel Sambuc 
282433d6423SLionel Sambuc   if (flags & CDEV_NONBLOCK) {
283433d6423SLionel Sambuc 	r = pp->rdcum > 0 ? pp->rdcum : EAGAIN;
284433d6423SLionel Sambuc 	pp->rdleft = pp->rdcum = 0;
285433d6423SLionel Sambuc 	pp->rdcaller = NONE;
286433d6423SLionel Sambuc 	return r;
287433d6423SLionel Sambuc   }
288433d6423SLionel Sambuc 
289433d6423SLionel Sambuc   return EDONTREPLY;			/* do suspend */
290433d6423SLionel Sambuc }
291433d6423SLionel Sambuc 
292433d6423SLionel Sambuc /*===========================================================================*
293433d6423SLionel Sambuc  *				pty_master_write			     *
294433d6423SLionel Sambuc  *===========================================================================*/
pty_master_write(devminor_t minor,u64_t UNUSED (position),endpoint_t endpt,cp_grant_id_t grant,size_t size,int flags,cdev_id_t id)295433d6423SLionel Sambuc static ssize_t pty_master_write(devminor_t minor, u64_t UNUSED(position),
296433d6423SLionel Sambuc 	endpoint_t endpt, cp_grant_id_t grant, size_t size, int flags,
297433d6423SLionel Sambuc 	cdev_id_t id)
298433d6423SLionel Sambuc {
299433d6423SLionel Sambuc   tty_t *tp;
300433d6423SLionel Sambuc   pty_t *pp;
301433d6423SLionel Sambuc   ssize_t r;
302433d6423SLionel Sambuc 
303433d6423SLionel Sambuc   if ((tp = line2tty(minor)) == NULL)
304433d6423SLionel Sambuc 	return ENXIO;
305433d6423SLionel Sambuc   pp = tp->tty_priv;
306433d6423SLionel Sambuc 
307433d6423SLionel Sambuc   /* Check, store information on the writer, do I/O. */
308433d6423SLionel Sambuc   if (pp->state & TTY_CLOSED)
309433d6423SLionel Sambuc 	return EIO;
310433d6423SLionel Sambuc 
311433d6423SLionel Sambuc   if (pp->wrcaller != NONE || pp->wrleft != 0 || pp->wrcum != 0)
312433d6423SLionel Sambuc 	return EIO;
313433d6423SLionel Sambuc 
314433d6423SLionel Sambuc   if (size <= 0)
315433d6423SLionel Sambuc 	return EINVAL;
316433d6423SLionel Sambuc 
317433d6423SLionel Sambuc   pp->wrcaller = endpt;
318433d6423SLionel Sambuc   pp->wrid = id;
319433d6423SLionel Sambuc   pp->wrgrant = grant;
320433d6423SLionel Sambuc   pp->wrleft = size;
321433d6423SLionel Sambuc 
322433d6423SLionel Sambuc   handle_events(tp);
323433d6423SLionel Sambuc 
324433d6423SLionel Sambuc   if (pp->wrleft == 0) {
325433d6423SLionel Sambuc 	pp->wrcaller = NONE;
326433d6423SLionel Sambuc 	return EDONTREPLY;		/* already done */
327433d6423SLionel Sambuc   }
328433d6423SLionel Sambuc 
329433d6423SLionel Sambuc   if (flags & CDEV_NONBLOCK) {
330433d6423SLionel Sambuc 	r = pp->wrcum > 0 ? pp->wrcum : EAGAIN;
331433d6423SLionel Sambuc 	pp->wrleft = pp->wrcum = 0;
332433d6423SLionel Sambuc 	pp->wrcaller = NONE;
333433d6423SLionel Sambuc 	return r;
334433d6423SLionel Sambuc   }
335433d6423SLionel Sambuc 
336433d6423SLionel Sambuc   return EDONTREPLY;			/* do suspend */
337433d6423SLionel Sambuc }
338433d6423SLionel Sambuc 
339433d6423SLionel Sambuc /*===========================================================================*
340da21d850SDavid van Moolenbroek  *				pty_master_ioctl			     *
341da21d850SDavid van Moolenbroek  *===========================================================================*/
pty_master_ioctl(devminor_t minor,unsigned long request,endpoint_t endpt,cp_grant_id_t grant,int flags,endpoint_t user_endpt,cdev_id_t id)342da21d850SDavid van Moolenbroek static int pty_master_ioctl(devminor_t minor, unsigned long request,
343da21d850SDavid van Moolenbroek 	endpoint_t endpt, cp_grant_id_t grant, int flags,
344da21d850SDavid van Moolenbroek 	endpoint_t user_endpt, cdev_id_t id)
345da21d850SDavid van Moolenbroek {
346da21d850SDavid van Moolenbroek   tty_t *tp;
347da21d850SDavid van Moolenbroek   pty_t *pp;
348da21d850SDavid van Moolenbroek   uid_t uid;
349da21d850SDavid van Moolenbroek   struct ptmget pm;
350da21d850SDavid van Moolenbroek   size_t len;
351*e6dabba5SDavid van Moolenbroek   int r, val;
352da21d850SDavid van Moolenbroek 
353da21d850SDavid van Moolenbroek   if ((tp = line2tty(minor)) == NULL)
354da21d850SDavid van Moolenbroek 	return ENXIO;
355da21d850SDavid van Moolenbroek   pp = tp->tty_priv;
356da21d850SDavid van Moolenbroek 
357da21d850SDavid van Moolenbroek   /* Some IOCTLs are for the master side only. */
358da21d850SDavid van Moolenbroek   switch (request) {
359da21d850SDavid van Moolenbroek   case TIOCGRANTPT:	/* grantpt(3) */
360da21d850SDavid van Moolenbroek 	if (!(pp->state & PTY_UNIX98))
361da21d850SDavid van Moolenbroek 		break;
362da21d850SDavid van Moolenbroek 
363da21d850SDavid van Moolenbroek 	if ((int)(uid = getnuid(user_endpt)) == -1)
364da21d850SDavid van Moolenbroek 		return EACCES;
365da21d850SDavid van Moolenbroek 	if (tty_gid == -1) {
366da21d850SDavid van Moolenbroek 		printf("PTY: no tty group ID given at startup\n");
367da21d850SDavid van Moolenbroek 		return EACCES;
368da21d850SDavid van Moolenbroek 	}
369da21d850SDavid van Moolenbroek 
370da21d850SDavid van Moolenbroek 	/* Create or update the slave node. */
371da21d850SDavid van Moolenbroek 	if (ptyfs_set(tp->tty_index, UNIX98_MODE, uid, tty_gid,
372da21d850SDavid van Moolenbroek 	    makedev(PTY_MAJOR, UNIX98_SLAVE(tp->tty_index))) != OK)
373da21d850SDavid van Moolenbroek 		return EACCES;
374da21d850SDavid van Moolenbroek 
375da21d850SDavid van Moolenbroek 	return OK;
376da21d850SDavid van Moolenbroek 
377da21d850SDavid van Moolenbroek   case TIOCPTSNAME:	/* ptsname(3) */
378da21d850SDavid van Moolenbroek 	if (!(pp->state & PTY_UNIX98))
379da21d850SDavid van Moolenbroek 		break;
380da21d850SDavid van Moolenbroek 
381da21d850SDavid van Moolenbroek 	/* Since pm.sn is 16 bytes, we can have up to a million slaves. */
382da21d850SDavid van Moolenbroek 	memset(&pm, 0, sizeof(pm));
383da21d850SDavid van Moolenbroek 
384da21d850SDavid van Moolenbroek 	strlcpy(pm.sn, _PATH_DEV_PTS, sizeof(pm.sn));
385da21d850SDavid van Moolenbroek 	len = strlen(pm.sn);
386da21d850SDavid van Moolenbroek 
387da21d850SDavid van Moolenbroek 	if (ptyfs_name(tp->tty_index, &pm.sn[len], sizeof(pm.sn) - len) != OK)
388da21d850SDavid van Moolenbroek 		return EINVAL;
389da21d850SDavid van Moolenbroek 
390da21d850SDavid van Moolenbroek 	return sys_safecopyto(endpt, grant, 0, (vir_bytes)&pm, sizeof(pm));
391*e6dabba5SDavid van Moolenbroek 
392*e6dabba5SDavid van Moolenbroek   case TIOCPKT:
393*e6dabba5SDavid van Moolenbroek 	r = sys_safecopyfrom(endpt, grant, 0, (vir_bytes)&val, sizeof(val));
394*e6dabba5SDavid van Moolenbroek 	if (r != OK)
395*e6dabba5SDavid van Moolenbroek 		return r;
396*e6dabba5SDavid van Moolenbroek 
397*e6dabba5SDavid van Moolenbroek 	if (val)
398*e6dabba5SDavid van Moolenbroek 		pp->state |= PTY_PKTMODE;
399*e6dabba5SDavid van Moolenbroek 	else
400*e6dabba5SDavid van Moolenbroek 		pp->state &= ~PTY_PKTMODE;
401*e6dabba5SDavid van Moolenbroek 
402*e6dabba5SDavid van Moolenbroek 	return OK;
403da21d850SDavid van Moolenbroek   }
404da21d850SDavid van Moolenbroek 
405da21d850SDavid van Moolenbroek   /* TODO: historically, all IOCTLs on the master are processed as if issued on
406da21d850SDavid van Moolenbroek    * the slave end. Make sure that this can not cause problems, in particular
407da21d850SDavid van Moolenbroek    * with blocking IOCTLs.
408da21d850SDavid van Moolenbroek    */
409da21d850SDavid van Moolenbroek   return tty_ioctl(minor, request, endpt, grant, flags, user_endpt, id);
410da21d850SDavid van Moolenbroek }
411da21d850SDavid van Moolenbroek 
412da21d850SDavid van Moolenbroek /*===========================================================================*
413433d6423SLionel Sambuc  *				pty_master_cancel			     *
414433d6423SLionel Sambuc  *===========================================================================*/
pty_master_cancel(devminor_t minor,endpoint_t endpt,cdev_id_t id)415433d6423SLionel Sambuc static int pty_master_cancel(devminor_t minor, endpoint_t endpt, cdev_id_t id)
416433d6423SLionel Sambuc {
417433d6423SLionel Sambuc   tty_t *tp;
418433d6423SLionel Sambuc   pty_t *pp;
419433d6423SLionel Sambuc   int r;
420433d6423SLionel Sambuc 
421433d6423SLionel Sambuc   if ((tp = line2tty(minor)) == NULL)
422433d6423SLionel Sambuc 	return ENXIO;
423433d6423SLionel Sambuc   pp = tp->tty_priv;
424433d6423SLionel Sambuc 
425433d6423SLionel Sambuc   if (pp->rdcaller == endpt && pp->rdid == id) {
426433d6423SLionel Sambuc 	/* Cancel a read from a PTY. */
427433d6423SLionel Sambuc 	r = pp->rdcum > 0 ? pp->rdcum : EINTR;
428433d6423SLionel Sambuc 	pp->rdleft = pp->rdcum = 0;
429433d6423SLionel Sambuc 	pp->rdcaller = NONE;
430433d6423SLionel Sambuc 	return r;
431433d6423SLionel Sambuc   }
432433d6423SLionel Sambuc 
433433d6423SLionel Sambuc   if (pp->wrcaller == endpt && pp->wrid == id) {
434433d6423SLionel Sambuc 	/* Cancel a write to a PTY. */
435433d6423SLionel Sambuc 	r = pp->wrcum > 0 ? pp->wrcum : EINTR;
436433d6423SLionel Sambuc 	pp->wrleft = pp->wrcum = 0;
437433d6423SLionel Sambuc 	pp->wrcaller = NONE;
438433d6423SLionel Sambuc 	return r;
439433d6423SLionel Sambuc   }
440433d6423SLionel Sambuc 
441433d6423SLionel Sambuc   /* Request not found. */
442433d6423SLionel Sambuc   return EDONTREPLY;
443433d6423SLionel Sambuc }
444433d6423SLionel Sambuc 
445433d6423SLionel Sambuc /*===========================================================================*
446433d6423SLionel Sambuc  *				select_try_pty				     *
447433d6423SLionel Sambuc  *===========================================================================*/
select_try_pty(tty_t * tp,int ops)448433d6423SLionel Sambuc static int select_try_pty(tty_t *tp, int ops)
449433d6423SLionel Sambuc {
450433d6423SLionel Sambuc   pty_t *pp = tp->tty_priv;
451433d6423SLionel Sambuc   int r = 0;
452433d6423SLionel Sambuc 
453433d6423SLionel Sambuc   if (ops & CDEV_OP_WR)  {
454433d6423SLionel Sambuc 	/* Write won't block on error. */
455433d6423SLionel Sambuc 	if (pp->state & TTY_CLOSED) r |= CDEV_OP_WR;
456433d6423SLionel Sambuc 	else if (pp->wrleft != 0 || pp->wrcum != 0) r |= CDEV_OP_WR;
457433d6423SLionel Sambuc 	else if (tp->tty_incount < buflen(tp->tty_inbuf)) r |= CDEV_OP_WR;
458433d6423SLionel Sambuc   }
459433d6423SLionel Sambuc 
460433d6423SLionel Sambuc   if (ops & CDEV_OP_RD) {
461433d6423SLionel Sambuc 	/* Read won't block on error. */
462433d6423SLionel Sambuc 	if (pp->state & TTY_CLOSED) r |= CDEV_OP_RD;
463433d6423SLionel Sambuc 	else if (pp->rdleft != 0 || pp->rdcum != 0) r |= CDEV_OP_RD;
464433d6423SLionel Sambuc 	else if (pp->ocount > 0) r |= CDEV_OP_RD;	/* Actual data. */
465433d6423SLionel Sambuc   }
466433d6423SLionel Sambuc 
467433d6423SLionel Sambuc   return r;
468433d6423SLionel Sambuc }
469433d6423SLionel Sambuc 
470433d6423SLionel Sambuc /*===========================================================================*
471433d6423SLionel Sambuc  *				select_retry_pty			     *
472433d6423SLionel Sambuc  *===========================================================================*/
select_retry_pty(tty_t * tp)473433d6423SLionel Sambuc void select_retry_pty(tty_t *tp)
474433d6423SLionel Sambuc {
475433d6423SLionel Sambuc   pty_t *pp = tp->tty_priv;
476433d6423SLionel Sambuc   int r;
477433d6423SLionel Sambuc 
478433d6423SLionel Sambuc   /* See if the pty side of a pty is ready to return a select. */
479433d6423SLionel Sambuc   if (pp->select_ops && (r = select_try_pty(tp, pp->select_ops))) {
480da21d850SDavid van Moolenbroek 	chardriver_reply_select(pp->select_proc, pp->select_minor, r);
481433d6423SLionel Sambuc 	pp->select_ops &= ~r;
482433d6423SLionel Sambuc   }
483433d6423SLionel Sambuc }
484433d6423SLionel Sambuc 
485433d6423SLionel Sambuc /*===========================================================================*
486433d6423SLionel Sambuc  *				pty_master_select			     *
487433d6423SLionel Sambuc  *===========================================================================*/
pty_master_select(devminor_t minor,unsigned int ops,endpoint_t endpt)488433d6423SLionel Sambuc static int pty_master_select(devminor_t minor, unsigned int ops,
489433d6423SLionel Sambuc 	endpoint_t endpt)
490433d6423SLionel Sambuc {
491433d6423SLionel Sambuc   tty_t *tp;
492433d6423SLionel Sambuc   pty_t *pp;
493433d6423SLionel Sambuc   int ready_ops, watch;
494433d6423SLionel Sambuc 
495433d6423SLionel Sambuc   if ((tp = line2tty(minor)) == NULL)
496433d6423SLionel Sambuc 	return ENXIO;
497433d6423SLionel Sambuc   pp = tp->tty_priv;
498433d6423SLionel Sambuc 
499433d6423SLionel Sambuc   watch = (ops & CDEV_NOTIFY);
500433d6423SLionel Sambuc   ops &= (CDEV_OP_RD | CDEV_OP_WR | CDEV_OP_ERR);
501433d6423SLionel Sambuc 
502433d6423SLionel Sambuc   ready_ops = select_try_pty(tp, ops);
503433d6423SLionel Sambuc 
504433d6423SLionel Sambuc   ops &= ~ready_ops;
505433d6423SLionel Sambuc   if (ops && watch) {
506433d6423SLionel Sambuc 	pp->select_ops |= ops;
507433d6423SLionel Sambuc 	pp->select_proc = endpt;
508da21d850SDavid van Moolenbroek 	pp->select_minor = minor;
509433d6423SLionel Sambuc   }
510433d6423SLionel Sambuc 
511433d6423SLionel Sambuc   return ready_ops;
512433d6423SLionel Sambuc }
513433d6423SLionel Sambuc 
514433d6423SLionel Sambuc /*===========================================================================*
515433d6423SLionel Sambuc  *				do_pty					     *
516433d6423SLionel Sambuc  *===========================================================================*/
do_pty(message * m_ptr,int ipc_status)517433d6423SLionel Sambuc void do_pty(message *m_ptr, int ipc_status)
518433d6423SLionel Sambuc {
519433d6423SLionel Sambuc /* Process a request for a PTY master (/dev/ptypX) device. */
520433d6423SLionel Sambuc 
521433d6423SLionel Sambuc   chardriver_process(&pty_master_tab, m_ptr, ipc_status);
522433d6423SLionel Sambuc }
523433d6423SLionel Sambuc 
524433d6423SLionel Sambuc /*===========================================================================*
525433d6423SLionel Sambuc  *				pty_slave_write				     *
526433d6423SLionel Sambuc  *===========================================================================*/
pty_slave_write(tty_t * tp,int try)527433d6423SLionel Sambuc static int pty_slave_write(tty_t *tp, int try)
528433d6423SLionel Sambuc {
529433d6423SLionel Sambuc /* (*dev_write)() routine for PTYs.  Transfer bytes from the writer on
530433d6423SLionel Sambuc  * /dev/ttypX to the output buffer.
531433d6423SLionel Sambuc  */
532433d6423SLionel Sambuc   pty_t *pp = tp->tty_priv;
533433d6423SLionel Sambuc   int count, ocount, s;
534433d6423SLionel Sambuc 
535433d6423SLionel Sambuc   /* PTY closed down? */
536433d6423SLionel Sambuc   if (pp->state & PTY_CLOSED) {
537433d6423SLionel Sambuc   	if (try) return 1;
538433d6423SLionel Sambuc 	if (tp->tty_outleft > 0) {
539433d6423SLionel Sambuc 		chardriver_reply_task(tp->tty_outcaller, tp->tty_outid, EIO);
540433d6423SLionel Sambuc 		tp->tty_outleft = tp->tty_outcum = 0;
541433d6423SLionel Sambuc 		tp->tty_outcaller = NONE;
542433d6423SLionel Sambuc 	}
543433d6423SLionel Sambuc 	return 0;
544433d6423SLionel Sambuc   }
545433d6423SLionel Sambuc 
546433d6423SLionel Sambuc   /* While there is something to do. */
547433d6423SLionel Sambuc   for (;;) {
548433d6423SLionel Sambuc 	ocount = buflen(pp->obuf) - pp->ocount;
549433d6423SLionel Sambuc 	if (try) return (ocount > 0);
550433d6423SLionel Sambuc 	count = bufend(pp->obuf) - pp->ohead;
551433d6423SLionel Sambuc 	if (count > ocount) count = ocount;
552433d6423SLionel Sambuc 	if (count > tp->tty_outleft) count = tp->tty_outleft;
553433d6423SLionel Sambuc 	if (count == 0 || tp->tty_inhibited)
554433d6423SLionel Sambuc 		break;
555433d6423SLionel Sambuc 
556433d6423SLionel Sambuc 	/* Copy from user space to the PTY output buffer. */
557433d6423SLionel Sambuc 	if (tp->tty_outcaller == KERNEL) {
558433d6423SLionel Sambuc 		/* We're trying to print on kernel's behalf */
559433d6423SLionel Sambuc 		memcpy(pp->ohead, (void *) tp->tty_outgrant + tp->tty_outcum,
560433d6423SLionel Sambuc 			count);
561433d6423SLionel Sambuc 	} else {
562433d6423SLionel Sambuc 		if ((s = sys_safecopyfrom(tp->tty_outcaller, tp->tty_outgrant,
563433d6423SLionel Sambuc 				tp->tty_outcum, (vir_bytes) pp->ohead,
564433d6423SLionel Sambuc 				count)) != OK) {
565433d6423SLionel Sambuc 			break;
566433d6423SLionel Sambuc 		}
567433d6423SLionel Sambuc 	}
568433d6423SLionel Sambuc 
569433d6423SLionel Sambuc 	/* Perform output processing on the output buffer. */
570433d6423SLionel Sambuc 	out_process(tp, pp->obuf, pp->ohead, bufend(pp->obuf), &count, &ocount);
571433d6423SLionel Sambuc 	if (count == 0) break;
572433d6423SLionel Sambuc 
573433d6423SLionel Sambuc 	/* Assume echoing messed up by output. */
574433d6423SLionel Sambuc 	tp->tty_reprint = TRUE;
575433d6423SLionel Sambuc 
576433d6423SLionel Sambuc 	/* Bookkeeping. */
577433d6423SLionel Sambuc 	pp->ocount += ocount;
578433d6423SLionel Sambuc 	if ((pp->ohead += ocount) >= bufend(pp->obuf))
579433d6423SLionel Sambuc 		pp->ohead -= buflen(pp->obuf);
580433d6423SLionel Sambuc 	pty_start(pp);
581433d6423SLionel Sambuc 
582433d6423SLionel Sambuc 	tp->tty_outcum += count;
583433d6423SLionel Sambuc 	if ((tp->tty_outleft -= count) == 0) {
584433d6423SLionel Sambuc 		/* Output is finished, reply to the writer. */
585433d6423SLionel Sambuc 		chardriver_reply_task(tp->tty_outcaller, tp->tty_outid,
586433d6423SLionel Sambuc 			tp->tty_outcum);
587433d6423SLionel Sambuc 		tp->tty_outcum = 0;
588433d6423SLionel Sambuc 		tp->tty_outcaller = NONE;
589433d6423SLionel Sambuc 	}
590433d6423SLionel Sambuc   }
591433d6423SLionel Sambuc   pty_finish(pp);
592433d6423SLionel Sambuc   return 1;
593433d6423SLionel Sambuc }
594433d6423SLionel Sambuc 
595433d6423SLionel Sambuc /*===========================================================================*
596433d6423SLionel Sambuc  *				pty_slave_echo				     *
597433d6423SLionel Sambuc  *===========================================================================*/
pty_slave_echo(tty_t * tp,int c)598433d6423SLionel Sambuc static void pty_slave_echo(tty_t *tp, int c)
599433d6423SLionel Sambuc {
600433d6423SLionel Sambuc /* Echo one character.  (Like pty_write, but only one character, optionally.) */
601433d6423SLionel Sambuc 
602433d6423SLionel Sambuc   pty_t *pp = tp->tty_priv;
603433d6423SLionel Sambuc   int count, ocount;
604433d6423SLionel Sambuc 
605433d6423SLionel Sambuc   ocount = buflen(pp->obuf) - pp->ocount;
606433d6423SLionel Sambuc   if (ocount == 0) return;		/* output buffer full */
607433d6423SLionel Sambuc   count = 1;
608433d6423SLionel Sambuc   *pp->ohead = c;			/* add one character */
609433d6423SLionel Sambuc 
610433d6423SLionel Sambuc   out_process(tp, pp->obuf, pp->ohead, bufend(pp->obuf), &count, &ocount);
611433d6423SLionel Sambuc   if (count == 0) return;
612433d6423SLionel Sambuc 
613433d6423SLionel Sambuc   pp->ocount += ocount;
614433d6423SLionel Sambuc   if ((pp->ohead += ocount) >= bufend(pp->obuf)) pp->ohead -= buflen(pp->obuf);
615433d6423SLionel Sambuc   pty_start(pp);
616433d6423SLionel Sambuc }
617433d6423SLionel Sambuc 
618433d6423SLionel Sambuc /*===========================================================================*
619433d6423SLionel Sambuc  *				pty_start				     *
620433d6423SLionel Sambuc  *===========================================================================*/
pty_start(pty_t * pp)621433d6423SLionel Sambuc static void pty_start(pty_t *pp)
622433d6423SLionel Sambuc {
623433d6423SLionel Sambuc /* Transfer bytes written to the output buffer to the PTY reader. */
624433d6423SLionel Sambuc   int count;
625*e6dabba5SDavid van Moolenbroek   char c;
626433d6423SLionel Sambuc 
627433d6423SLionel Sambuc   /* While there are things to do. */
628433d6423SLionel Sambuc   for (;;) {
629433d6423SLionel Sambuc 	count = bufend(pp->obuf) - pp->otail;
630433d6423SLionel Sambuc 	if (count > pp->ocount) count = pp->ocount;
631*e6dabba5SDavid van Moolenbroek 	if (count == 0 || pp->rdleft == 0) break;
632*e6dabba5SDavid van Moolenbroek 
633*e6dabba5SDavid van Moolenbroek 	/* If there is output at all, and packet mode is enabled, then prepend
634*e6dabba5SDavid van Moolenbroek 	 * the output with a zero byte. This is absolutely minimal "support"
635*e6dabba5SDavid van Moolenbroek 	 * for the TIOCPKT receipt mode to get telnetd(8) going. Implementing
636*e6dabba5SDavid van Moolenbroek 	 * full support for all the TIOCPKT bits will require more work.
637*e6dabba5SDavid van Moolenbroek 	 */
638*e6dabba5SDavid van Moolenbroek 	if (pp->rdcum == 0 && (pp->state & PTY_PKTMODE)) {
639*e6dabba5SDavid van Moolenbroek 		c = 0;
640*e6dabba5SDavid van Moolenbroek 		if (sys_safecopyto(pp->rdcaller, pp->rdgrant, 0, (vir_bytes)&c,
641*e6dabba5SDavid van Moolenbroek 		    sizeof(c)) != OK)
642*e6dabba5SDavid van Moolenbroek 			break;
643*e6dabba5SDavid van Moolenbroek 
644*e6dabba5SDavid van Moolenbroek 		pp->rdcum++;
645*e6dabba5SDavid van Moolenbroek 		pp->rdleft--;
646*e6dabba5SDavid van Moolenbroek 	}
647*e6dabba5SDavid van Moolenbroek 
648433d6423SLionel Sambuc 	if (count > pp->rdleft) count = pp->rdleft;
649433d6423SLionel Sambuc 	if (count == 0) break;
650433d6423SLionel Sambuc 
651433d6423SLionel Sambuc 	/* Copy from the output buffer to the readers address space. */
652*e6dabba5SDavid van Moolenbroek 	if (sys_safecopyto(pp->rdcaller, pp->rdgrant, pp->rdcum,
653*e6dabba5SDavid van Moolenbroek 	    (vir_bytes)pp->otail, count) != OK)
654433d6423SLionel Sambuc 		break;
655433d6423SLionel Sambuc 
656433d6423SLionel Sambuc 	/* Bookkeeping. */
657433d6423SLionel Sambuc 	pp->ocount -= count;
658433d6423SLionel Sambuc 	if ((pp->otail += count) == bufend(pp->obuf)) pp->otail = pp->obuf;
659433d6423SLionel Sambuc 	pp->rdcum += count;
660433d6423SLionel Sambuc 	pp->rdleft -= count;
661433d6423SLionel Sambuc   }
662433d6423SLionel Sambuc }
663433d6423SLionel Sambuc 
664433d6423SLionel Sambuc /*===========================================================================*
665433d6423SLionel Sambuc  *				pty_finish				     *
666433d6423SLionel Sambuc  *===========================================================================*/
pty_finish(pty_t * pp)667433d6423SLionel Sambuc static void pty_finish(pty_t *pp)
668433d6423SLionel Sambuc {
669433d6423SLionel Sambuc /* Finish the read request of a PTY reader if there is at least one byte
670433d6423SLionel Sambuc  * transferred.
671433d6423SLionel Sambuc  */
672433d6423SLionel Sambuc 
673433d6423SLionel Sambuc   if (pp->rdcum > 0) {
674433d6423SLionel Sambuc 	chardriver_reply_task(pp->rdcaller, pp->rdid, pp->rdcum);
675433d6423SLionel Sambuc 	pp->rdleft = pp->rdcum = 0;
676433d6423SLionel Sambuc 	pp->rdcaller = NONE;
677433d6423SLionel Sambuc   }
678433d6423SLionel Sambuc }
679433d6423SLionel Sambuc 
680433d6423SLionel Sambuc /*===========================================================================*
681433d6423SLionel Sambuc  *				pty_slave_read				     *
682433d6423SLionel Sambuc  *===========================================================================*/
pty_slave_read(tty_t * tp,int try)683433d6423SLionel Sambuc static int pty_slave_read(tty_t *tp, int try)
684433d6423SLionel Sambuc {
685433d6423SLionel Sambuc /* Offer bytes from the PTY writer for input on the TTY.  (Do it one byte at
686433d6423SLionel Sambuc  * a time, 99% of the writes will be for one byte, so no sense in being smart.)
687433d6423SLionel Sambuc  */
688433d6423SLionel Sambuc   pty_t *pp = tp->tty_priv;
689433d6423SLionel Sambuc   char c;
690433d6423SLionel Sambuc 
691433d6423SLionel Sambuc   if (pp->state & PTY_CLOSED) {
692433d6423SLionel Sambuc 	if (try) return 1;
693433d6423SLionel Sambuc 	if (tp->tty_inleft > 0) {
694433d6423SLionel Sambuc 		chardriver_reply_task(tp->tty_incaller, tp->tty_inid,
695433d6423SLionel Sambuc 			tp->tty_incum);
696433d6423SLionel Sambuc 		tp->tty_inleft = tp->tty_incum = 0;
697433d6423SLionel Sambuc 		tp->tty_incaller = NONE;
698433d6423SLionel Sambuc 	}
699433d6423SLionel Sambuc 	return 1;
700433d6423SLionel Sambuc   }
701433d6423SLionel Sambuc 
702433d6423SLionel Sambuc   if (try) {
703433d6423SLionel Sambuc   	if (pp->wrleft > 0)
704433d6423SLionel Sambuc   		return 1;
705433d6423SLionel Sambuc   	return 0;
706433d6423SLionel Sambuc   }
707433d6423SLionel Sambuc 
708433d6423SLionel Sambuc   while (pp->wrleft > 0) {
709433d6423SLionel Sambuc   	int s;
710433d6423SLionel Sambuc 
711433d6423SLionel Sambuc 	/* Transfer one character to 'c'. */
712433d6423SLionel Sambuc 	if ((s = sys_safecopyfrom(pp->wrcaller, pp->wrgrant, pp->wrcum,
713433d6423SLionel Sambuc 		(vir_bytes) &c, 1)) != OK) {
714433d6423SLionel Sambuc 		printf("pty: safecopy failed (error %d)\n", s);
715433d6423SLionel Sambuc 		break;
716433d6423SLionel Sambuc 	}
717433d6423SLionel Sambuc 
718433d6423SLionel Sambuc 	/* Input processing. */
719433d6423SLionel Sambuc 	if (in_process(tp, &c, 1) == 0) break;
720433d6423SLionel Sambuc 
721433d6423SLionel Sambuc 	/* PTY writer bookkeeping. */
722433d6423SLionel Sambuc 	pp->wrcum++;
723433d6423SLionel Sambuc 	if (--pp->wrleft == 0) {
724433d6423SLionel Sambuc 		chardriver_reply_task(pp->wrcaller, pp->wrid, pp->wrcum);
725433d6423SLionel Sambuc 		pp->wrcum = 0;
726433d6423SLionel Sambuc 		pp->wrcaller = NONE;
727433d6423SLionel Sambuc 	}
728433d6423SLionel Sambuc   }
729433d6423SLionel Sambuc 
730433d6423SLionel Sambuc   return 0;
731433d6423SLionel Sambuc }
732433d6423SLionel Sambuc 
733433d6423SLionel Sambuc /*===========================================================================*
734da21d850SDavid van Moolenbroek  *				pty_slave_mayopen			     *
735da21d850SDavid van Moolenbroek  *===========================================================================*/
pty_slave_mayopen(tty_t * tp,devminor_t line)736da21d850SDavid van Moolenbroek static int pty_slave_mayopen(tty_t *tp, devminor_t line)
737da21d850SDavid van Moolenbroek {
738da21d850SDavid van Moolenbroek /* Check if the user is not mixing Unix98 and non-Unix98 terminal ends. */
739da21d850SDavid van Moolenbroek   pty_t *pp;
740da21d850SDavid van Moolenbroek   int unix98_line, unix98_pty;
741da21d850SDavid van Moolenbroek 
742da21d850SDavid van Moolenbroek   pp = tp->tty_priv;
743da21d850SDavid van Moolenbroek 
744da21d850SDavid van Moolenbroek   /* A non-Unix98 slave may be opened even if the corresponding master is not
745da21d850SDavid van Moolenbroek    * opened yet, but PTY_UNIX98 is always clear for free ptys.  A Unix98 slave
746da21d850SDavid van Moolenbroek    * may not be opened before its master, but this should not occur anyway.
747da21d850SDavid van Moolenbroek    */
748da21d850SDavid van Moolenbroek   unix98_line = (line >= UNIX98_MINOR && line < UNIX98_MINOR + NR_PTYS * 2);
749da21d850SDavid van Moolenbroek   unix98_pty = !!(pp->state & PTY_UNIX98);
750da21d850SDavid van Moolenbroek 
751da21d850SDavid van Moolenbroek   return (unix98_line == unix98_pty);
752da21d850SDavid van Moolenbroek }
753da21d850SDavid van Moolenbroek 
754da21d850SDavid van Moolenbroek /*===========================================================================*
755433d6423SLionel Sambuc  *				pty_slave_open				     *
756433d6423SLionel Sambuc  *===========================================================================*/
pty_slave_open(tty_t * tp,int UNUSED (try))757433d6423SLionel Sambuc static int pty_slave_open(tty_t *tp, int UNUSED(try))
758433d6423SLionel Sambuc {
759433d6423SLionel Sambuc /* The tty side has been opened. */
760433d6423SLionel Sambuc   pty_t *pp = tp->tty_priv;
761433d6423SLionel Sambuc 
762433d6423SLionel Sambuc   /* TTY_ACTIVE may already be set, which would indicate that the slave is
763433d6423SLionel Sambuc    * reopened after being fully closed while the master is still open. In that
764433d6423SLionel Sambuc    * case TTY_CLOSED will also be set, so clear that one.
765433d6423SLionel Sambuc    */
766433d6423SLionel Sambuc   pp->state |= TTY_ACTIVE;
767433d6423SLionel Sambuc   pp->state &= ~TTY_CLOSED;
768433d6423SLionel Sambuc 
769433d6423SLionel Sambuc   return 0;
770433d6423SLionel Sambuc }
771433d6423SLionel Sambuc 
772433d6423SLionel Sambuc /*===========================================================================*
773433d6423SLionel Sambuc  *				pty_slave_close				     *
774433d6423SLionel Sambuc  *===========================================================================*/
pty_slave_close(tty_t * tp,int UNUSED (try))775433d6423SLionel Sambuc static int pty_slave_close(tty_t *tp, int UNUSED(try))
776433d6423SLionel Sambuc {
777433d6423SLionel Sambuc /* The tty side has closed, so shut down the pty side. */
778433d6423SLionel Sambuc   pty_t *pp = tp->tty_priv;
779433d6423SLionel Sambuc 
780433d6423SLionel Sambuc   if (!(pp->state & PTY_ACTIVE)) return 0;
781433d6423SLionel Sambuc 
782433d6423SLionel Sambuc   if (pp->rdleft > 0) {
783433d6423SLionel Sambuc 	chardriver_reply_task(pp->rdcaller, pp->rdid, pp->rdcum);
784433d6423SLionel Sambuc 	pp->rdleft = pp->rdcum = 0;
785433d6423SLionel Sambuc 	pp->rdcaller = NONE;
786433d6423SLionel Sambuc   }
787433d6423SLionel Sambuc 
788433d6423SLionel Sambuc   if (pp->wrleft > 0) {
789433d6423SLionel Sambuc 	chardriver_reply_task(pp->wrcaller, pp->wrid, pp->wrcum);
790433d6423SLionel Sambuc 	pp->wrleft = pp->wrcum = 0;
791433d6423SLionel Sambuc 	pp->wrcaller = NONE;
792433d6423SLionel Sambuc   }
793433d6423SLionel Sambuc 
794da21d850SDavid van Moolenbroek   if (pp->state & PTY_CLOSED) pty_reset(tp);
795433d6423SLionel Sambuc   else pp->state |= TTY_CLOSED;
796433d6423SLionel Sambuc 
797433d6423SLionel Sambuc   return 0;
798433d6423SLionel Sambuc }
799433d6423SLionel Sambuc 
800433d6423SLionel Sambuc /*===========================================================================*
801433d6423SLionel Sambuc  *				pty_slave_icancel			     *
802433d6423SLionel Sambuc  *===========================================================================*/
pty_slave_icancel(tty_t * tp,int UNUSED (try))803433d6423SLionel Sambuc static int pty_slave_icancel(tty_t *tp, int UNUSED(try))
804433d6423SLionel Sambuc {
805433d6423SLionel Sambuc /* Discard waiting input. */
806433d6423SLionel Sambuc   pty_t *pp = tp->tty_priv;
807433d6423SLionel Sambuc 
808433d6423SLionel Sambuc   if (pp->wrleft > 0) {
809433d6423SLionel Sambuc 	chardriver_reply_task(pp->wrcaller, pp->wrid, pp->wrcum + pp->wrleft);
810433d6423SLionel Sambuc 	pp->wrcum = pp->wrleft = 0;
811433d6423SLionel Sambuc 	pp->wrcaller = NONE;
812433d6423SLionel Sambuc   }
813433d6423SLionel Sambuc 
814433d6423SLionel Sambuc   return 0;
815433d6423SLionel Sambuc }
816433d6423SLionel Sambuc 
817433d6423SLionel Sambuc /*===========================================================================*
818433d6423SLionel Sambuc  *				pty_slave_ocancel			     *
819433d6423SLionel Sambuc  *===========================================================================*/
pty_slave_ocancel(tty_t * tp,int UNUSED (try))820433d6423SLionel Sambuc static int pty_slave_ocancel(tty_t *tp, int UNUSED(try))
821433d6423SLionel Sambuc {
822433d6423SLionel Sambuc /* Drain the output buffer. */
823433d6423SLionel Sambuc   pty_t *pp = tp->tty_priv;
824433d6423SLionel Sambuc 
825433d6423SLionel Sambuc   pp->ocount = 0;
826433d6423SLionel Sambuc   pp->otail = pp->ohead;
827433d6423SLionel Sambuc 
828433d6423SLionel Sambuc   return 0;
829433d6423SLionel Sambuc }
830433d6423SLionel Sambuc 
831433d6423SLionel Sambuc /*===========================================================================*
832433d6423SLionel Sambuc  *				pty_init				     *
833433d6423SLionel Sambuc  *===========================================================================*/
pty_init(tty_t * tp)834433d6423SLionel Sambuc void pty_init(tty_t *tp)
835433d6423SLionel Sambuc {
836433d6423SLionel Sambuc   pty_t *pp;
837433d6423SLionel Sambuc   int line;
838433d6423SLionel Sambuc 
839433d6423SLionel Sambuc   /* Associate PTY and TTY structures. */
840433d6423SLionel Sambuc   line = tp - tty_table;
841433d6423SLionel Sambuc   pp = tp->tty_priv = &pty_table[line];
842433d6423SLionel Sambuc   pp->tty = tp;
843433d6423SLionel Sambuc   pp->select_ops = 0;
844433d6423SLionel Sambuc   pp->rdcaller = NONE;
845433d6423SLionel Sambuc   pp->wrcaller = NONE;
846433d6423SLionel Sambuc 
847433d6423SLionel Sambuc   /* Set up output queue. */
848433d6423SLionel Sambuc   pp->ohead = pp->otail = pp->obuf;
849433d6423SLionel Sambuc 
850433d6423SLionel Sambuc   /* Fill in TTY function hooks. */
851433d6423SLionel Sambuc   tp->tty_devread = pty_slave_read;
852433d6423SLionel Sambuc   tp->tty_devwrite = pty_slave_write;
853433d6423SLionel Sambuc   tp->tty_echo = pty_slave_echo;
854433d6423SLionel Sambuc   tp->tty_icancel = pty_slave_icancel;
855433d6423SLionel Sambuc   tp->tty_ocancel = pty_slave_ocancel;
856da21d850SDavid van Moolenbroek   tp->tty_mayopen = pty_slave_mayopen;
857433d6423SLionel Sambuc   tp->tty_open = pty_slave_open;
858433d6423SLionel Sambuc   tp->tty_close = pty_slave_close;
859433d6423SLionel Sambuc   tp->tty_select_ops = 0;
860433d6423SLionel Sambuc }
861