xref: /freebsd-src/sys/kern/tty_pts.c (revision ef9ffb8594eee294334ced627755bf5b46b48f9f)
1bc093719SEd Schouten /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
38a36da99SPedro F. Giffuni  *
4bc093719SEd Schouten  * Copyright (c) 2008 Ed Schouten <ed@FreeBSD.org>
512af2a0fSOlivier Houchard  * All rights reserved.
612af2a0fSOlivier Houchard  *
7bc093719SEd Schouten  * Portions of this software were developed under sponsorship from Snow
8bc093719SEd Schouten  * B.V., the Netherlands.
912af2a0fSOlivier Houchard  *
1012af2a0fSOlivier Houchard  * Redistribution and use in source and binary forms, with or without
1112af2a0fSOlivier Houchard  * modification, are permitted provided that the following conditions
1212af2a0fSOlivier Houchard  * are met:
1312af2a0fSOlivier Houchard  * 1. Redistributions of source code must retain the above copyright
1412af2a0fSOlivier Houchard  *    notice, this list of conditions and the following disclaimer.
1512af2a0fSOlivier Houchard  * 2. Redistributions in binary form must reproduce the above copyright
1612af2a0fSOlivier Houchard  *    notice, this list of conditions and the following disclaimer in the
1712af2a0fSOlivier Houchard  *    documentation and/or other materials provided with the distribution.
1812af2a0fSOlivier Houchard  *
19bc093719SEd Schouten  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
2012af2a0fSOlivier Houchard  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2112af2a0fSOlivier Houchard  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22bc093719SEd Schouten  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2312af2a0fSOlivier Houchard  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2412af2a0fSOlivier Houchard  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2512af2a0fSOlivier Houchard  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2612af2a0fSOlivier Houchard  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2712af2a0fSOlivier Houchard  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2812af2a0fSOlivier Houchard  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2912af2a0fSOlivier Houchard  * SUCH DAMAGE.
3012af2a0fSOlivier Houchard  */
3112af2a0fSOlivier Houchard 
3212af2a0fSOlivier Houchard #include <sys/cdefs.h>
33bc093719SEd Schouten /* Add compatibility bits for FreeBSD. */
34bc093719SEd Schouten #define PTS_COMPAT
354d3b1aacSEd Schouten /* Add pty(4) compat bits. */
36bc093719SEd Schouten #define PTS_EXTERNAL
37bc093719SEd Schouten /* Add bits to make Linux binaries work. */
38bc093719SEd Schouten #define PTS_LINUX
39bc093719SEd Schouten 
4012af2a0fSOlivier Houchard #include <sys/param.h>
4112af2a0fSOlivier Houchard #include <sys/lock.h>
42bc093719SEd Schouten #include <sys/condvar.h>
4312af2a0fSOlivier Houchard #include <sys/conf.h>
44bc093719SEd Schouten #include <sys/fcntl.h>
45bc093719SEd Schouten #include <sys/file.h>
46bc093719SEd Schouten #include <sys/filedesc.h>
4712af2a0fSOlivier Houchard #include <sys/filio.h>
48bc093719SEd Schouten #include <sys/kernel.h>
49ccfd3aabSEd Schouten #include <sys/limits.h>
50bc093719SEd Schouten #include <sys/malloc.h>
51cf294330SKyle Evans #include <sys/mutex.h>
52bc093719SEd Schouten #include <sys/poll.h>
53bc093719SEd Schouten #include <sys/proc.h>
546fd8c2bdSEdward Tomasz Napierala #include <sys/racct.h>
55bc093719SEd Schouten #include <sys/resourcevar.h>
56bc093719SEd Schouten #include <sys/serial.h>
57bc093719SEd Schouten #include <sys/stat.h>
58bc093719SEd Schouten #include <sys/syscall.h>
59bc093719SEd Schouten #include <sys/syscallsubr.h>
60ccfd3aabSEd Schouten #include <sys/sysctl.h>
61bc093719SEd Schouten #include <sys/sysproto.h>
62bc093719SEd Schouten #include <sys/systm.h>
63bc093719SEd Schouten #include <sys/tty.h>
64bc093719SEd Schouten #include <sys/ttycom.h>
6534a77b97SBrooks Davis #include <sys/uio.h>
669696feebSJohn Baldwin #include <sys/user.h>
6712af2a0fSOlivier Houchard 
68bc093719SEd Schouten #include <machine/stdarg.h>
6912af2a0fSOlivier Houchard 
70ccfd3aabSEd Schouten /*
71ccfd3aabSEd Schouten  * Our utmp(5) format is limited to 8-byte TTY line names.  This means
72ccfd3aabSEd Schouten  * we can at most allocate 1000 pseudo-terminals ("pts/999").  Allow
73ccfd3aabSEd Schouten  * users to increase this number, assuming they have manually increased
74ccfd3aabSEd Schouten  * UT_LINESIZE.
75ccfd3aabSEd Schouten  */
76bc093719SEd Schouten static struct unrhdr *pts_pool;
7712af2a0fSOlivier Houchard 
78bc093719SEd Schouten static MALLOC_DEFINE(M_PTS, "pts", "pseudo tty device");
7912af2a0fSOlivier Houchard 
80bc093719SEd Schouten /*
81bc093719SEd Schouten  * Per-PTS structure.
8212af2a0fSOlivier Houchard  *
83bc093719SEd Schouten  * List of locks
84bc093719SEd Schouten  * (t)	locked by tty_lock()
85bc093719SEd Schouten  * (c)	const until freeing
8612af2a0fSOlivier Houchard  */
87bc093719SEd Schouten struct pts_softc {
88bc093719SEd Schouten 	int		pts_unit;	/* (c) Device unit number. */
89bc093719SEd Schouten 	unsigned int	pts_flags;	/* (t) Device flags. */
90bc093719SEd Schouten #define	PTS_PKT		0x1	/* Packet mode. */
91b6163710SEd Schouten #define	PTS_FINISHED	0x2	/* Return errors on read()/write(). */
9264308260SEd Schouten 	char		pts_pkt;	/* (t) Unread packet mode data. */
9312af2a0fSOlivier Houchard 
94bc093719SEd Schouten 	struct cv	pts_inwait;	/* (t) Blocking write() on master. */
95bc093719SEd Schouten 	struct selinfo	pts_inpoll;	/* (t) Select queue for write(). */
96bc093719SEd Schouten 	struct cv	pts_outwait;	/* (t) Blocking read() on master. */
97bc093719SEd Schouten 	struct selinfo	pts_outpoll;	/* (t) Select queue for read(). */
98bc093719SEd Schouten 
99bc093719SEd Schouten #ifdef PTS_EXTERNAL
100bc093719SEd Schouten 	struct cdev	*pts_cdev;	/* (c) Master device node. */
101bc093719SEd Schouten #endif /* PTS_EXTERNAL */
102bc093719SEd Schouten 
103953bb3b9SEdward Tomasz Napierala 	struct ucred	*pts_cred;	/* (c) Resource limit. */
10412af2a0fSOlivier Houchard };
10512af2a0fSOlivier Houchard 
10612af2a0fSOlivier Houchard /*
107bc093719SEd Schouten  * Controller-side file operations.
10812af2a0fSOlivier Houchard  */
10912af2a0fSOlivier Houchard 
11012af2a0fSOlivier Houchard static int
111bc093719SEd Schouten ptsdev_read(struct file *fp, struct uio *uio, struct ucred *active_cred,
112bc093719SEd Schouten     int flags, struct thread *td)
11312af2a0fSOlivier Houchard {
114bc093719SEd Schouten 	struct tty *tp = fp->f_data;
115bc093719SEd Schouten 	struct pts_softc *psc = tty_softc(tp);
11664308260SEd Schouten 	int error = 0;
11764308260SEd Schouten 	char pkt;
11812af2a0fSOlivier Houchard 
119bc093719SEd Schouten 	if (uio->uio_resid == 0)
120bc093719SEd Schouten 		return (0);
121bc093719SEd Schouten 
122bc093719SEd Schouten 	tty_lock(tp);
12364308260SEd Schouten 
124bc093719SEd Schouten 	for (;;) {
12564308260SEd Schouten 		/*
12664308260SEd Schouten 		 * Implement packet mode. When packet mode is turned on,
12764308260SEd Schouten 		 * the first byte contains a bitmask of events that
128e3043798SPedro F. Giffuni 		 * occurred (start, stop, flush, window size, etc).
12964308260SEd Schouten 		 */
13064308260SEd Schouten 		if (psc->pts_flags & PTS_PKT && psc->pts_pkt) {
13164308260SEd Schouten 			pkt = psc->pts_pkt;
13264308260SEd Schouten 			psc->pts_pkt = 0;
13364308260SEd Schouten 			tty_unlock(tp);
13464308260SEd Schouten 
13564308260SEd Schouten 			error = ureadc(pkt, uio);
13664308260SEd Schouten 			return (error);
13764308260SEd Schouten 		}
13864308260SEd Schouten 
13964308260SEd Schouten 		/*
14064308260SEd Schouten 		 * Transmit regular data.
14164308260SEd Schouten 		 *
14264308260SEd Schouten 		 * XXX: We shouldn't use ttydisc_getc_poll()! Even
14364308260SEd Schouten 		 * though in this implementation, there is likely going
14464308260SEd Schouten 		 * to be data, we should just call ttydisc_getc_uio()
14564308260SEd Schouten 		 * and use its return value to sleep.
14664308260SEd Schouten 		 */
14764308260SEd Schouten 		if (ttydisc_getc_poll(tp)) {
14864308260SEd Schouten 			if (psc->pts_flags & PTS_PKT) {
14964308260SEd Schouten 				/*
15064308260SEd Schouten 				 * XXX: Small race. Fortunately PTY
15164308260SEd Schouten 				 * consumers aren't multithreaded.
15264308260SEd Schouten 				 */
15364308260SEd Schouten 
15464308260SEd Schouten 				tty_unlock(tp);
15564308260SEd Schouten 				error = ureadc(TIOCPKT_DATA, uio);
15664308260SEd Schouten 				if (error)
15764308260SEd Schouten 					return (error);
15864308260SEd Schouten 				tty_lock(tp);
15964308260SEd Schouten 			}
16064308260SEd Schouten 
161bc093719SEd Schouten 			error = ttydisc_getc_uio(tp, uio);
16212af2a0fSOlivier Houchard 			break;
16364308260SEd Schouten 		}
164bc093719SEd Schouten 
165bc093719SEd Schouten 		/* Maybe the device isn't used anyway. */
166b6163710SEd Schouten 		if (psc->pts_flags & PTS_FINISHED)
167bc093719SEd Schouten 			break;
168bc093719SEd Schouten 
169bc093719SEd Schouten 		/* Wait for more data. */
170bc093719SEd Schouten 		if (fp->f_flag & O_NONBLOCK) {
171bc093719SEd Schouten 			error = EWOULDBLOCK;
172bc093719SEd Schouten 			break;
17312af2a0fSOlivier Houchard 		}
174bc093719SEd Schouten 		error = cv_wait_sig(&psc->pts_outwait, tp->t_mtx);
175bc093719SEd Schouten 		if (error != 0)
176bc093719SEd Schouten 			break;
177bc093719SEd Schouten 	}
17864308260SEd Schouten 
179bc093719SEd Schouten 	tty_unlock(tp);
180bc093719SEd Schouten 
18112af2a0fSOlivier Houchard 	return (error);
18212af2a0fSOlivier Houchard }
18312af2a0fSOlivier Houchard 
18412af2a0fSOlivier Houchard static int
185bc093719SEd Schouten ptsdev_write(struct file *fp, struct uio *uio, struct ucred *active_cred,
186bc093719SEd Schouten     int flags, struct thread *td)
18712af2a0fSOlivier Houchard {
188bc093719SEd Schouten 	struct tty *tp = fp->f_data;
189bc093719SEd Schouten 	struct pts_softc *psc = tty_softc(tp);
190bc093719SEd Schouten 	char ib[256], *ibstart;
191bc093719SEd Schouten 	size_t iblen, rintlen;
19212af2a0fSOlivier Houchard 	int error = 0;
19312af2a0fSOlivier Houchard 
19464308260SEd Schouten 	if (uio->uio_resid == 0)
19564308260SEd Schouten 		return (0);
196bc093719SEd Schouten 
19764308260SEd Schouten 	for (;;) {
198bc093719SEd Schouten 		ibstart = ib;
199bc093719SEd Schouten 		iblen = MIN(uio->uio_resid, sizeof ib);
200bc093719SEd Schouten 		error = uiomove(ib, iblen, uio);
20164308260SEd Schouten 
202bc093719SEd Schouten 		tty_lock(tp);
203d40b91cbSEd Schouten 		if (error != 0) {
204d40b91cbSEd Schouten 			iblen = 0;
205bc093719SEd Schouten 			goto done;
206d40b91cbSEd Schouten 		}
20712af2a0fSOlivier Houchard 
20812af2a0fSOlivier Houchard 		/*
209bc093719SEd Schouten 		 * When possible, avoid the slow path. rint_bypass()
210bc093719SEd Schouten 		 * copies all input to the input queue at once.
21112af2a0fSOlivier Houchard 		 */
21264308260SEd Schouten 		MPASS(iblen > 0);
21364308260SEd Schouten 		do {
2145c67885aSEd Schouten 			rintlen = ttydisc_rint_simple(tp, ibstart, iblen);
215bc093719SEd Schouten 			ibstart += rintlen;
216bc093719SEd Schouten 			iblen -= rintlen;
217bc093719SEd Schouten 			if (iblen == 0) {
218bc093719SEd Schouten 				/* All data written. */
21964308260SEd Schouten 				break;
22012af2a0fSOlivier Houchard 			}
22112af2a0fSOlivier Houchard 
222bc093719SEd Schouten 			/* Maybe the device isn't used anyway. */
223b6163710SEd Schouten 			if (psc->pts_flags & PTS_FINISHED) {
2246137be43SEd Schouten 				error = EIO;
225bc093719SEd Schouten 				goto done;
226bc093719SEd Schouten 			}
227bc093719SEd Schouten 
228bc093719SEd Schouten 			/* Wait for more data. */
229bc093719SEd Schouten 			if (fp->f_flag & O_NONBLOCK) {
230bc093719SEd Schouten 				error = EWOULDBLOCK;
231bc093719SEd Schouten 				goto done;
232bc093719SEd Schouten 			}
233bc093719SEd Schouten 
234bc093719SEd Schouten 			/* Wake up users on the slave side. */
235bc093719SEd Schouten 			ttydisc_rint_done(tp);
236bc093719SEd Schouten 			error = cv_wait_sig(&psc->pts_inwait, tp->t_mtx);
237bc093719SEd Schouten 			if (error != 0)
238bc093719SEd Schouten 				goto done;
23964308260SEd Schouten 		} while (iblen > 0);
24064308260SEd Schouten 
24164308260SEd Schouten 		if (uio->uio_resid == 0)
24264308260SEd Schouten 			break;
24364308260SEd Schouten 		tty_unlock(tp);
244bc093719SEd Schouten 	}
245bc093719SEd Schouten 
246bc093719SEd Schouten done:	ttydisc_rint_done(tp);
247bc093719SEd Schouten 	tty_unlock(tp);
248d40b91cbSEd Schouten 
249d40b91cbSEd Schouten 	/*
250d40b91cbSEd Schouten 	 * Don't account for the part of the buffer that we couldn't
251d40b91cbSEd Schouten 	 * pass to the TTY.
252d40b91cbSEd Schouten 	 */
253d40b91cbSEd Schouten 	uio->uio_resid += iblen;
254bc093719SEd Schouten 	return (error);
255bc093719SEd Schouten }
256bc093719SEd Schouten 
257bc093719SEd Schouten static int
258bc093719SEd Schouten ptsdev_ioctl(struct file *fp, u_long cmd, void *data,
259bc093719SEd Schouten     struct ucred *active_cred, struct thread *td)
260bc093719SEd Schouten {
261bc093719SEd Schouten 	struct tty *tp = fp->f_data;
262bc093719SEd Schouten 	struct pts_softc *psc = tty_softc(tp);
263bc093719SEd Schouten 	int error = 0, sig;
264bc093719SEd Schouten 
265bc093719SEd Schouten 	switch (cmd) {
266f821fad4SKonstantin Belousov 	case FIODTYPE:
267f821fad4SKonstantin Belousov 		*(int *)data = D_TTY;
268f821fad4SKonstantin Belousov 		return (0);
269bc093719SEd Schouten 	case FIONBIO:
270bc093719SEd Schouten 		/* This device supports non-blocking operation. */
271bc093719SEd Schouten 		return (0);
272d4892ee5SEd Schouten 	case FIONREAD:
273d4892ee5SEd Schouten 		tty_lock(tp);
274d4892ee5SEd Schouten 		*(int *)data = ttydisc_getc_poll(tp);
275d4892ee5SEd Schouten 		tty_unlock(tp);
276d4892ee5SEd Schouten 		return (0);
277ed34a7fcSBrooks Davis 	case FIODGNAME:
278ed34a7fcSBrooks Davis #ifdef COMPAT_FREEBSD32
279ed34a7fcSBrooks Davis 	case FIODGNAME_32:
280ed34a7fcSBrooks Davis #endif
281ed34a7fcSBrooks Davis 	{
282bc093719SEd Schouten 		struct fiodgname_arg *fgn;
283bc093719SEd Schouten 		const char *p;
284bc093719SEd Schouten 		int i;
285bc093719SEd Schouten 
286bc093719SEd Schouten 		/* Reverse device name lookups, for ptsname() and ttyname(). */
287bc093719SEd Schouten 		fgn = data;
288bc093719SEd Schouten 		p = tty_devname(tp);
289bc093719SEd Schouten 		i = strlen(p) + 1;
290bc093719SEd Schouten 		if (i > fgn->len)
291bc093719SEd Schouten 			return (EINVAL);
292ed34a7fcSBrooks Davis 		return (copyout(p, fiodgname_buf_get_ptr(fgn, cmd), i));
29312af2a0fSOlivier Houchard 	}
29412af2a0fSOlivier Houchard 
29512af2a0fSOlivier Houchard 	/*
296bc093719SEd Schouten 	 * We need to implement TIOCGPGRP and TIOCGSID here again. When
297bc093719SEd Schouten 	 * called on the pseudo-terminal master, it should not check if
298bc093719SEd Schouten 	 * the terminal is the foreground terminal of the calling
299bc093719SEd Schouten 	 * process.
300bc093719SEd Schouten 	 *
301bc093719SEd Schouten 	 * TIOCGETA is also implemented here. Various Linux PTY routines
302bc093719SEd Schouten 	 * often call isatty(), which is implemented by tcgetattr().
30312af2a0fSOlivier Houchard 	 */
304bc093719SEd Schouten #ifdef PTS_LINUX
305bc093719SEd Schouten 	case TIOCGETA:
306bc093719SEd Schouten 		/* Obtain terminal flags through tcgetattr(). */
307bc093719SEd Schouten 		tty_lock(tp);
308c4d4bcdaSEd Schouten 		*(struct termios*)data = tp->t_termios;
309bc093719SEd Schouten 		tty_unlock(tp);
31012af2a0fSOlivier Houchard 		return (0);
311bc093719SEd Schouten #endif /* PTS_LINUX */
312bc093719SEd Schouten 	case TIOCSETAF:
313bc093719SEd Schouten 	case TIOCSETAW:
31412af2a0fSOlivier Houchard 		/*
315bc093719SEd Schouten 		 * We must make sure we turn tcsetattr() calls of TCSAFLUSH and
316bc093719SEd Schouten 		 * TCSADRAIN into something different. If an application would
317bc093719SEd Schouten 		 * call TCSAFLUSH or TCSADRAIN on the master descriptor, it may
318bc093719SEd Schouten 		 * deadlock waiting for all data to be read.
31912af2a0fSOlivier Houchard 		 */
320bc093719SEd Schouten 		cmd = TIOCSETA;
321bc093719SEd Schouten 		break;
322bc093719SEd Schouten #if defined(PTS_COMPAT) || defined(PTS_LINUX)
323bc093719SEd Schouten 	case TIOCGPTN:
324bc093719SEd Schouten 		/*
325bc093719SEd Schouten 		 * Get the device unit number.
326bc093719SEd Schouten 		 */
327bc093719SEd Schouten 		if (psc->pts_unit < 0)
328bc093719SEd Schouten 			return (ENOTTY);
329bc093719SEd Schouten 		*(unsigned int *)data = psc->pts_unit;
330bc093719SEd Schouten 		return (0);
331bc093719SEd Schouten #endif /* PTS_COMPAT || PTS_LINUX */
332bc093719SEd Schouten 	case TIOCGPGRP:
333bc093719SEd Schouten 		/* Get the foreground process group ID. */
334bc093719SEd Schouten 		tty_lock(tp);
335bc093719SEd Schouten 		if (tp->t_pgrp != NULL)
336bc093719SEd Schouten 			*(int *)data = tp->t_pgrp->pg_id;
337bc093719SEd Schouten 		else
338bc093719SEd Schouten 			*(int *)data = NO_PID;
339bc093719SEd Schouten 		tty_unlock(tp);
340bc093719SEd Schouten 		return (0);
341bc093719SEd Schouten 	case TIOCGSID:
342bc093719SEd Schouten 		/* Get the session leader process ID. */
343bc093719SEd Schouten 		tty_lock(tp);
344bc093719SEd Schouten 		if (tp->t_session == NULL)
345bc093719SEd Schouten 			error = ENOTTY;
346bc093719SEd Schouten 		else
347bc093719SEd Schouten 			*(int *)data = tp->t_session->s_sid;
348bc093719SEd Schouten 		tty_unlock(tp);
34912af2a0fSOlivier Houchard 		return (error);
350bc093719SEd Schouten 	case TIOCPTMASTER:
351bc093719SEd Schouten 		/* Yes, we are a pseudo-terminal master. */
352bc093719SEd Schouten 		return (0);
353bc093719SEd Schouten 	case TIOCSIG:
354bc093719SEd Schouten 		/* Signal the foreground process group. */
355bc093719SEd Schouten 		sig = *(int *)data;
356bc093719SEd Schouten 		if (sig < 1 || sig >= NSIG)
357bc093719SEd Schouten 			return (EINVAL);
358bc093719SEd Schouten 
359bc093719SEd Schouten 		tty_lock(tp);
360bc093719SEd Schouten 		tty_signal_pgrp(tp, sig);
361bc093719SEd Schouten 		tty_unlock(tp);
362bc093719SEd Schouten 		return (0);
363bc093719SEd Schouten 	case TIOCPKT:
364bc093719SEd Schouten 		/* Enable/disable packet mode. */
365bc093719SEd Schouten 		tty_lock(tp);
366bc093719SEd Schouten 		if (*(int *)data)
367bc093719SEd Schouten 			psc->pts_flags |= PTS_PKT;
368bc093719SEd Schouten 		else
369bc093719SEd Schouten 			psc->pts_flags &= ~PTS_PKT;
370bc093719SEd Schouten 		tty_unlock(tp);
37112af2a0fSOlivier Houchard 		return (0);
37212af2a0fSOlivier Houchard 	}
37312af2a0fSOlivier Houchard 
374bc093719SEd Schouten 	/* Just redirect this ioctl to the slave device. */
375bc093719SEd Schouten 	tty_lock(tp);
376328d9d2cSEd Schouten 	error = tty_ioctl(tp, cmd, data, fp->f_flag, td);
377bc093719SEd Schouten 	tty_unlock(tp);
37840d05103SEd Schouten 	if (error == ENOIOCTL)
37940d05103SEd Schouten 		error = ENOTTY;
38012af2a0fSOlivier Houchard 
381bc093719SEd Schouten 	return (error);
38212af2a0fSOlivier Houchard }
38312af2a0fSOlivier Houchard 
38412af2a0fSOlivier Houchard static int
385bc093719SEd Schouten ptsdev_poll(struct file *fp, int events, struct ucred *active_cred,
386bc093719SEd Schouten     struct thread *td)
38712af2a0fSOlivier Houchard {
388bc093719SEd Schouten 	struct tty *tp = fp->f_data;
389bc093719SEd Schouten 	struct pts_softc *psc = tty_softc(tp);
39012af2a0fSOlivier Houchard 	int revents = 0;
39112af2a0fSOlivier Houchard 
392bc093719SEd Schouten 	tty_lock(tp);
393bc093719SEd Schouten 
394b6163710SEd Schouten 	if (psc->pts_flags & PTS_FINISHED) {
395bc093719SEd Schouten 		/* Slave device is not opened. */
396bc093719SEd Schouten 		tty_unlock(tp);
3976b53d5c0SEd Schouten 		return ((events & (POLLIN|POLLRDNORM)) | POLLHUP);
398bc093719SEd Schouten 	}
399bc093719SEd Schouten 
400bc093719SEd Schouten 	if (events & (POLLIN|POLLRDNORM)) {
401bc093719SEd Schouten 		/* See if we can getc something. */
40264308260SEd Schouten 		if (ttydisc_getc_poll(tp) ||
40364308260SEd Schouten 		    (psc->pts_flags & PTS_PKT && psc->pts_pkt))
404bc093719SEd Schouten 			revents |= events & (POLLIN|POLLRDNORM);
405bc093719SEd Schouten 	}
406bc093719SEd Schouten 	if (events & (POLLOUT|POLLWRNORM)) {
407bc093719SEd Schouten 		/* See if we can rint something. */
408bc093719SEd Schouten 		if (ttydisc_rint_poll(tp))
409bc093719SEd Schouten 			revents |= events & (POLLOUT|POLLWRNORM);
410bc093719SEd Schouten 	}
41112af2a0fSOlivier Houchard 
41212af2a0fSOlivier Houchard 	/*
413bc093719SEd Schouten 	 * No need to check for POLLHUP here. This device cannot be used
414bc093719SEd Schouten 	 * as a callout device, which means we always have a carrier,
415bc093719SEd Schouten 	 * because the master is.
41612af2a0fSOlivier Houchard 	 */
41712af2a0fSOlivier Houchard 
41812af2a0fSOlivier Houchard 	if (revents == 0) {
419bc093719SEd Schouten 		/*
420bc093719SEd Schouten 		 * This code might look misleading, but the naming of
421bc093719SEd Schouten 		 * poll events on this side is the opposite of the slave
422bc093719SEd Schouten 		 * device.
423bc093719SEd Schouten 		 */
42412af2a0fSOlivier Houchard 		if (events & (POLLIN|POLLRDNORM))
425bc093719SEd Schouten 			selrecord(td, &psc->pts_outpoll);
42612af2a0fSOlivier Houchard 		if (events & (POLLOUT|POLLWRNORM))
427bc093719SEd Schouten 			selrecord(td, &psc->pts_inpoll);
42812af2a0fSOlivier Houchard 	}
429bc093719SEd Schouten 
430bc093719SEd Schouten 	tty_unlock(tp);
43112af2a0fSOlivier Houchard 
43212af2a0fSOlivier Houchard 	return (revents);
43312af2a0fSOlivier Houchard }
43412af2a0fSOlivier Houchard 
4351ff90be7SEd Schouten /*
4361ff90be7SEd Schouten  * kqueue support.
4371ff90be7SEd Schouten  */
4381ff90be7SEd Schouten 
4391ff90be7SEd Schouten static void
4401ff90be7SEd Schouten pts_kqops_read_detach(struct knote *kn)
4411ff90be7SEd Schouten {
4421ff90be7SEd Schouten 	struct file *fp = kn->kn_fp;
4431ff90be7SEd Schouten 	struct tty *tp = fp->f_data;
4441ff90be7SEd Schouten 	struct pts_softc *psc = tty_softc(tp);
4451ff90be7SEd Schouten 
4461ff90be7SEd Schouten 	knlist_remove(&psc->pts_outpoll.si_note, kn, 0);
4471ff90be7SEd Schouten }
4481ff90be7SEd Schouten 
4491ff90be7SEd Schouten static int
4501ff90be7SEd Schouten pts_kqops_read_event(struct knote *kn, long hint)
4511ff90be7SEd Schouten {
4521ff90be7SEd Schouten 	struct file *fp = kn->kn_fp;
4531ff90be7SEd Schouten 	struct tty *tp = fp->f_data;
4541ff90be7SEd Schouten 	struct pts_softc *psc = tty_softc(tp);
4551ff90be7SEd Schouten 
4561ff90be7SEd Schouten 	if (psc->pts_flags & PTS_FINISHED) {
4571ff90be7SEd Schouten 		kn->kn_flags |= EV_EOF;
4581ff90be7SEd Schouten 		return (1);
4591ff90be7SEd Schouten 	} else {
4601ff90be7SEd Schouten 		kn->kn_data = ttydisc_getc_poll(tp);
4611ff90be7SEd Schouten 		return (kn->kn_data > 0);
4621ff90be7SEd Schouten 	}
4631ff90be7SEd Schouten }
4641ff90be7SEd Schouten 
4651ff90be7SEd Schouten static void
4661ff90be7SEd Schouten pts_kqops_write_detach(struct knote *kn)
4671ff90be7SEd Schouten {
4681ff90be7SEd Schouten 	struct file *fp = kn->kn_fp;
4691ff90be7SEd Schouten 	struct tty *tp = fp->f_data;
4701ff90be7SEd Schouten 	struct pts_softc *psc = tty_softc(tp);
4711ff90be7SEd Schouten 
4721ff90be7SEd Schouten 	knlist_remove(&psc->pts_inpoll.si_note, kn, 0);
4731ff90be7SEd Schouten }
4741ff90be7SEd Schouten 
4751ff90be7SEd Schouten static int
4761ff90be7SEd Schouten pts_kqops_write_event(struct knote *kn, long hint)
4771ff90be7SEd Schouten {
4781ff90be7SEd Schouten 	struct file *fp = kn->kn_fp;
4791ff90be7SEd Schouten 	struct tty *tp = fp->f_data;
4801ff90be7SEd Schouten 	struct pts_softc *psc = tty_softc(tp);
4811ff90be7SEd Schouten 
4821ff90be7SEd Schouten 	if (psc->pts_flags & PTS_FINISHED) {
4831ff90be7SEd Schouten 		kn->kn_flags |= EV_EOF;
4841ff90be7SEd Schouten 		return (1);
4851ff90be7SEd Schouten 	} else {
4861ff90be7SEd Schouten 		kn->kn_data = ttydisc_rint_poll(tp);
4871ff90be7SEd Schouten 		return (kn->kn_data > 0);
4881ff90be7SEd Schouten 	}
4891ff90be7SEd Schouten }
4901ff90be7SEd Schouten 
491*ef9ffb85SMark Johnston static const struct filterops pts_kqops_read = {
492e76d823bSRobert Watson 	.f_isfd = 1,
493e76d823bSRobert Watson 	.f_detach = pts_kqops_read_detach,
494e76d823bSRobert Watson 	.f_event = pts_kqops_read_event,
495e76d823bSRobert Watson };
496*ef9ffb85SMark Johnston static const struct filterops pts_kqops_write = {
497e76d823bSRobert Watson 	.f_isfd = 1,
498e76d823bSRobert Watson 	.f_detach = pts_kqops_write_detach,
499e76d823bSRobert Watson 	.f_event = pts_kqops_write_event,
500e76d823bSRobert Watson };
5011ff90be7SEd Schouten 
5021ff90be7SEd Schouten static int
5031ff90be7SEd Schouten ptsdev_kqfilter(struct file *fp, struct knote *kn)
5041ff90be7SEd Schouten {
5051ff90be7SEd Schouten 	struct tty *tp = fp->f_data;
5061ff90be7SEd Schouten 	struct pts_softc *psc = tty_softc(tp);
5071ff90be7SEd Schouten 	int error = 0;
5081ff90be7SEd Schouten 
5091ff90be7SEd Schouten 	tty_lock(tp);
5101ff90be7SEd Schouten 
5111ff90be7SEd Schouten 	switch (kn->kn_filter) {
5121ff90be7SEd Schouten 	case EVFILT_READ:
5131ff90be7SEd Schouten 		kn->kn_fop = &pts_kqops_read;
5141ff90be7SEd Schouten 		knlist_add(&psc->pts_outpoll.si_note, kn, 1);
5151ff90be7SEd Schouten 		break;
5161ff90be7SEd Schouten 	case EVFILT_WRITE:
5171ff90be7SEd Schouten 		kn->kn_fop = &pts_kqops_write;
5181ff90be7SEd Schouten 		knlist_add(&psc->pts_inpoll.si_note, kn, 1);
5191ff90be7SEd Schouten 		break;
5201ff90be7SEd Schouten 	default:
5211ff90be7SEd Schouten 		error = EINVAL;
5221ff90be7SEd Schouten 		break;
5231ff90be7SEd Schouten 	}
5241ff90be7SEd Schouten 
5251ff90be7SEd Schouten 	tty_unlock(tp);
5261ff90be7SEd Schouten 	return (error);
5271ff90be7SEd Schouten }
5281ff90be7SEd Schouten 
52912af2a0fSOlivier Houchard static int
5302b68eb8eSMateusz Guzik ptsdev_stat(struct file *fp, struct stat *sb, struct ucred *active_cred)
53112af2a0fSOlivier Houchard {
532bc093719SEd Schouten 	struct tty *tp = fp->f_data;
533bc093719SEd Schouten #ifdef PTS_EXTERNAL
534bc093719SEd Schouten 	struct pts_softc *psc = tty_softc(tp);
535bc093719SEd Schouten #endif /* PTS_EXTERNAL */
53637ddf38eSEd Schouten 	struct cdev *dev = tp->t_dev;
53712af2a0fSOlivier Houchard 
53812af2a0fSOlivier Houchard 	/*
539bc093719SEd Schouten 	 * According to POSIX, we must implement an fstat(). This also
540bc093719SEd Schouten 	 * makes this implementation compatible with Linux binaries,
541bc093719SEd Schouten 	 * because Linux calls fstat() on the pseudo-terminal master to
542bc093719SEd Schouten 	 * obtain st_rdev.
543bc093719SEd Schouten 	 *
54437ddf38eSEd Schouten 	 * XXX: POSIX also mentions we must fill in st_dev, but how?
54512af2a0fSOlivier Houchard 	 */
546bc093719SEd Schouten 
547bc093719SEd Schouten 	bzero(sb, sizeof *sb);
548bc093719SEd Schouten #ifdef PTS_EXTERNAL
549bc093719SEd Schouten 	if (psc->pts_cdev != NULL)
550bc093719SEd Schouten 		sb->st_ino = sb->st_rdev = dev2udev(psc->pts_cdev);
551bc093719SEd Schouten 	else
552bc093719SEd Schouten #endif /* PTS_EXTERNAL */
553bc093719SEd Schouten 		sb->st_ino = sb->st_rdev = tty_udev(tp);
55437ddf38eSEd Schouten 
555510ea843SEd Schouten 	sb->st_atim = dev->si_atime;
556510ea843SEd Schouten 	sb->st_ctim = dev->si_ctime;
557510ea843SEd Schouten 	sb->st_mtim = dev->si_mtime;
55837ddf38eSEd Schouten 	sb->st_uid = dev->si_uid;
55937ddf38eSEd Schouten 	sb->st_gid = dev->si_gid;
56037ddf38eSEd Schouten 	sb->st_mode = dev->si_mode | S_IFCHR;
561bc093719SEd Schouten 
56212af2a0fSOlivier Houchard 	return (0);
56312af2a0fSOlivier Houchard }
56412af2a0fSOlivier Houchard 
56512af2a0fSOlivier Houchard static int
566bc093719SEd Schouten ptsdev_close(struct file *fp, struct thread *td)
56712af2a0fSOlivier Houchard {
568bc093719SEd Schouten 	struct tty *tp = fp->f_data;
56912af2a0fSOlivier Houchard 
570bc093719SEd Schouten 	/* Deallocate TTY device. */
571bc093719SEd Schouten 	tty_lock(tp);
572bc093719SEd Schouten 	tty_rel_gone(tp);
57312af2a0fSOlivier Houchard 
5746d517473SKonstantin Belousov 	/*
5756d517473SKonstantin Belousov 	 * Open of /dev/ptmx or /dev/ptyXX changes the type of file
5766d517473SKonstantin Belousov 	 * from DTYPE_VNODE to DTYPE_PTS. vn_open() increases vnode
5776d517473SKonstantin Belousov 	 * use count, we need to decrement it, and possibly do other
5786d517473SKonstantin Belousov 	 * required cleanup.
5796d517473SKonstantin Belousov 	 */
5806d517473SKonstantin Belousov 	if (fp->f_vnode != NULL)
5816d517473SKonstantin Belousov 		return (vnops.fo_close(fp, td));
5826d517473SKonstantin Belousov 
58312af2a0fSOlivier Houchard 	return (0);
58412af2a0fSOlivier Houchard }
58512af2a0fSOlivier Houchard 
5869696feebSJohn Baldwin static int
5879696feebSJohn Baldwin ptsdev_fill_kinfo(struct file *fp, struct kinfo_file *kif, struct filedesc *fdp)
5889696feebSJohn Baldwin {
5899696feebSJohn Baldwin 	struct tty *tp;
5909696feebSJohn Baldwin 
5919696feebSJohn Baldwin 	kif->kf_type = KF_TYPE_PTS;
5929696feebSJohn Baldwin 	tp = fp->f_data;
5939696feebSJohn Baldwin 	kif->kf_un.kf_pts.kf_pts_dev = tty_udev(tp);
59469921123SKonstantin Belousov 	kif->kf_un.kf_pts.kf_pts_dev_freebsd11 =
59569921123SKonstantin Belousov 	    kif->kf_un.kf_pts.kf_pts_dev; /* truncate */
5969696feebSJohn Baldwin 	strlcpy(kif->kf_path, tty_devname(tp), sizeof(kif->kf_path));
5979696feebSJohn Baldwin 	return (0);
5989696feebSJohn Baldwin }
5999696feebSJohn Baldwin 
600*ef9ffb85SMark Johnston static const struct fileops ptsdev_ops = {
601bc093719SEd Schouten 	.fo_read	= ptsdev_read,
602bc093719SEd Schouten 	.fo_write	= ptsdev_write,
6032d69d0dcSJohn Baldwin 	.fo_truncate	= invfo_truncate,
604bc093719SEd Schouten 	.fo_ioctl	= ptsdev_ioctl,
605bc093719SEd Schouten 	.fo_poll	= ptsdev_poll,
6061ff90be7SEd Schouten 	.fo_kqfilter	= ptsdev_kqfilter,
607bc093719SEd Schouten 	.fo_stat	= ptsdev_stat,
608bc093719SEd Schouten 	.fo_close	= ptsdev_close,
6099c00bb91SKonstantin Belousov 	.fo_chmod	= invfo_chmod,
6109c00bb91SKonstantin Belousov 	.fo_chown	= invfo_chown,
611ca04d21dSGleb Smirnoff 	.fo_sendfile	= invfo_sendfile,
6129696feebSJohn Baldwin 	.fo_fill_kinfo	= ptsdev_fill_kinfo,
613f28526e9SKonstantin Belousov 	.fo_cmp		= file_kcmp_generic,
614bc093719SEd Schouten 	.fo_flags	= DFLAG_PASSABLE,
615bc093719SEd Schouten };
616bc093719SEd Schouten 
61712af2a0fSOlivier Houchard /*
618bc093719SEd Schouten  * Driver-side hooks.
61912af2a0fSOlivier Houchard  */
620bc093719SEd Schouten 
621bc093719SEd Schouten static void
622bc093719SEd Schouten ptsdrv_outwakeup(struct tty *tp)
623bc093719SEd Schouten {
624bc093719SEd Schouten 	struct pts_softc *psc = tty_softc(tp);
625bc093719SEd Schouten 
626bc093719SEd Schouten 	cv_broadcast(&psc->pts_outwait);
627bc093719SEd Schouten 	selwakeup(&psc->pts_outpoll);
6281ff90be7SEd Schouten 	KNOTE_LOCKED(&psc->pts_outpoll.si_note, 0);
62912af2a0fSOlivier Houchard }
630bc093719SEd Schouten 
631bc093719SEd Schouten static void
632bc093719SEd Schouten ptsdrv_inwakeup(struct tty *tp)
633bc093719SEd Schouten {
634bc093719SEd Schouten 	struct pts_softc *psc = tty_softc(tp);
635bc093719SEd Schouten 
636bc093719SEd Schouten 	cv_broadcast(&psc->pts_inwait);
637bc093719SEd Schouten 	selwakeup(&psc->pts_inpoll);
6381ff90be7SEd Schouten 	KNOTE_LOCKED(&psc->pts_inpoll.si_note, 0);
639bc093719SEd Schouten }
640bc093719SEd Schouten 
641b6163710SEd Schouten static int
642b6163710SEd Schouten ptsdrv_open(struct tty *tp)
643b6163710SEd Schouten {
644b6163710SEd Schouten 	struct pts_softc *psc = tty_softc(tp);
645b6163710SEd Schouten 
646b6163710SEd Schouten 	psc->pts_flags &= ~PTS_FINISHED;
647b6163710SEd Schouten 
648b6163710SEd Schouten 	return (0);
649b6163710SEd Schouten }
650b6163710SEd Schouten 
651bc093719SEd Schouten static void
652bc093719SEd Schouten ptsdrv_close(struct tty *tp)
653bc093719SEd Schouten {
654b6163710SEd Schouten 	struct pts_softc *psc = tty_softc(tp);
655bc093719SEd Schouten 
656bc093719SEd Schouten 	/* Wake up any blocked readers/writers. */
65767dd0ccbSEd Schouten 	psc->pts_flags |= PTS_FINISHED;
658bc093719SEd Schouten 	ptsdrv_outwakeup(tp);
659bc093719SEd Schouten 	ptsdrv_inwakeup(tp);
660bc093719SEd Schouten }
661bc093719SEd Schouten 
662bc093719SEd Schouten static void
66364308260SEd Schouten ptsdrv_pktnotify(struct tty *tp, char event)
66464308260SEd Schouten {
66564308260SEd Schouten 	struct pts_softc *psc = tty_softc(tp);
66664308260SEd Schouten 
66764308260SEd Schouten 	/*
66864308260SEd Schouten 	 * Clear conflicting flags.
66964308260SEd Schouten 	 */
67064308260SEd Schouten 
67164308260SEd Schouten 	switch (event) {
67264308260SEd Schouten 	case TIOCPKT_STOP:
67364308260SEd Schouten 		psc->pts_pkt &= ~TIOCPKT_START;
67464308260SEd Schouten 		break;
67564308260SEd Schouten 	case TIOCPKT_START:
67664308260SEd Schouten 		psc->pts_pkt &= ~TIOCPKT_STOP;
67764308260SEd Schouten 		break;
67864308260SEd Schouten 	case TIOCPKT_NOSTOP:
67964308260SEd Schouten 		psc->pts_pkt &= ~TIOCPKT_DOSTOP;
68064308260SEd Schouten 		break;
68164308260SEd Schouten 	case TIOCPKT_DOSTOP:
68264308260SEd Schouten 		psc->pts_pkt &= ~TIOCPKT_NOSTOP;
68364308260SEd Schouten 		break;
68464308260SEd Schouten 	}
68564308260SEd Schouten 
68664308260SEd Schouten 	psc->pts_pkt |= event;
68764308260SEd Schouten 	ptsdrv_outwakeup(tp);
68864308260SEd Schouten }
68964308260SEd Schouten 
69064308260SEd Schouten static void
691bc093719SEd Schouten ptsdrv_free(void *softc)
692bc093719SEd Schouten {
693bc093719SEd Schouten 	struct pts_softc *psc = softc;
694bc093719SEd Schouten 
695bc093719SEd Schouten 	/* Make device number available again. */
696bc093719SEd Schouten 	if (psc->pts_unit >= 0)
697bc093719SEd Schouten 		free_unr(pts_pool, psc->pts_unit);
698bc093719SEd Schouten 
699953bb3b9SEdward Tomasz Napierala 	chgptscnt(psc->pts_cred->cr_ruidinfo, -1, 0);
7006fd8c2bdSEdward Tomasz Napierala 	racct_sub_cred(psc->pts_cred, RACCT_NPTS, 1);
701953bb3b9SEdward Tomasz Napierala 	crfree(psc->pts_cred);
702bc093719SEd Schouten 
7036aba400aSAttilio Rao 	seldrain(&psc->pts_inpoll);
7046aba400aSAttilio Rao 	seldrain(&psc->pts_outpoll);
7051ff90be7SEd Schouten 	knlist_destroy(&psc->pts_inpoll.si_note);
7061ff90be7SEd Schouten 	knlist_destroy(&psc->pts_outpoll.si_note);
7071ff90be7SEd Schouten 
708bc093719SEd Schouten #ifdef PTS_EXTERNAL
709bc093719SEd Schouten 	/* Destroy master device as well. */
710bc093719SEd Schouten 	if (psc->pts_cdev != NULL)
711bc093719SEd Schouten 		destroy_dev_sched(psc->pts_cdev);
712bc093719SEd Schouten #endif /* PTS_EXTERNAL */
713bc093719SEd Schouten 
714bc093719SEd Schouten 	free(psc, M_PTS);
715bc093719SEd Schouten }
716bc093719SEd Schouten 
717bc093719SEd Schouten static struct ttydevsw pts_class = {
718bc093719SEd Schouten 	.tsw_flags	= TF_NOPREFIX,
719bc093719SEd Schouten 	.tsw_outwakeup	= ptsdrv_outwakeup,
720bc093719SEd Schouten 	.tsw_inwakeup	= ptsdrv_inwakeup,
721b6163710SEd Schouten 	.tsw_open	= ptsdrv_open,
722bc093719SEd Schouten 	.tsw_close	= ptsdrv_close,
72364308260SEd Schouten 	.tsw_pktnotify	= ptsdrv_pktnotify,
724bc093719SEd Schouten 	.tsw_free	= ptsdrv_free,
725bc093719SEd Schouten };
726bc093719SEd Schouten 
7274d3b1aacSEd Schouten #ifndef PTS_EXTERNAL
7284d3b1aacSEd Schouten static
7294d3b1aacSEd Schouten #endif /* !PTS_EXTERNAL */
7304d3b1aacSEd Schouten int
731bc093719SEd Schouten pts_alloc(int fflags, struct thread *td, struct file *fp)
732bc093719SEd Schouten {
7336fd8c2bdSEdward Tomasz Napierala 	int unit, ok, error;
734bc093719SEd Schouten 	struct tty *tp;
735bc093719SEd Schouten 	struct pts_softc *psc;
736bc093719SEd Schouten 	struct proc *p = td->td_proc;
737953bb3b9SEdward Tomasz Napierala 	struct ucred *cred = td->td_ucred;
738bc093719SEd Schouten 
739bc093719SEd Schouten 	/* Resource limiting. */
740bc093719SEd Schouten 	PROC_LOCK(p);
7416fd8c2bdSEdward Tomasz Napierala 	error = racct_add(p, RACCT_NPTS, 1);
7426fd8c2bdSEdward Tomasz Napierala 	if (error != 0) {
743bc093719SEd Schouten 		PROC_UNLOCK(p);
744bc093719SEd Schouten 		return (EAGAIN);
7456fd8c2bdSEdward Tomasz Napierala 	}
746f6f6d240SMateusz Guzik 	ok = chgptscnt(cred->cr_ruidinfo, 1, lim_cur(td, RLIMIT_NPTS));
7476fd8c2bdSEdward Tomasz Napierala 	if (!ok) {
7486fd8c2bdSEdward Tomasz Napierala 		racct_sub(p, RACCT_NPTS, 1);
7496fd8c2bdSEdward Tomasz Napierala 		PROC_UNLOCK(p);
7506fd8c2bdSEdward Tomasz Napierala 		return (EAGAIN);
7516fd8c2bdSEdward Tomasz Napierala 	}
7526fd8c2bdSEdward Tomasz Napierala 	PROC_UNLOCK(p);
753bc093719SEd Schouten 
754bc093719SEd Schouten 	/* Try to allocate a new pts unit number. */
755bc093719SEd Schouten 	unit = alloc_unr(pts_pool);
756bc093719SEd Schouten 	if (unit < 0) {
7576fd8c2bdSEdward Tomasz Napierala 		racct_sub(p, RACCT_NPTS, 1);
758953bb3b9SEdward Tomasz Napierala 		chgptscnt(cred->cr_ruidinfo, -1, 0);
75912af2a0fSOlivier Houchard 		return (EAGAIN);
76012af2a0fSOlivier Houchard 	}
76112af2a0fSOlivier Houchard 
762bc093719SEd Schouten 	/* Allocate TTY and softc. */
763bc093719SEd Schouten 	psc = malloc(sizeof(struct pts_softc), M_PTS, M_WAITOK|M_ZERO);
764e6d84d05SEd Schouten 	cv_init(&psc->pts_inwait, "ptsin");
765e6d84d05SEd Schouten 	cv_init(&psc->pts_outwait, "ptsout");
76612af2a0fSOlivier Houchard 
767bc093719SEd Schouten 	psc->pts_unit = unit;
768953bb3b9SEdward Tomasz Napierala 	psc->pts_cred = crhold(cred);
769bc093719SEd Schouten 
770c5e30cc0SEd Schouten 	tp = tty_alloc(&pts_class, psc);
771d8b0556cSKonstantin Belousov 	knlist_init_mtx(&psc->pts_inpoll.si_note, tp->t_mtx);
772d8b0556cSKonstantin Belousov 	knlist_init_mtx(&psc->pts_outpoll.si_note, tp->t_mtx);
773bc093719SEd Schouten 
774bc093719SEd Schouten 	/* Expose the slave device as well. */
775bc093719SEd Schouten 	tty_makedev(tp, td->td_ucred, "pts/%u", psc->pts_unit);
776bc093719SEd Schouten 
777bc093719SEd Schouten 	finit(fp, fflags, DTYPE_PTS, tp, &ptsdev_ops);
778bc093719SEd Schouten 
77912af2a0fSOlivier Houchard 	return (0);
78012af2a0fSOlivier Houchard }
781bc093719SEd Schouten 
782bc093719SEd Schouten #ifdef PTS_EXTERNAL
783bc093719SEd Schouten int
784bc093719SEd Schouten pts_alloc_external(int fflags, struct thread *td, struct file *fp,
785bc093719SEd Schouten     struct cdev *dev, const char *name)
78612af2a0fSOlivier Houchard {
7876fd8c2bdSEdward Tomasz Napierala 	int ok, error;
788bc093719SEd Schouten 	struct tty *tp;
789bc093719SEd Schouten 	struct pts_softc *psc;
790bc093719SEd Schouten 	struct proc *p = td->td_proc;
791953bb3b9SEdward Tomasz Napierala 	struct ucred *cred = td->td_ucred;
79212af2a0fSOlivier Houchard 
793bc093719SEd Schouten 	/* Resource limiting. */
794bc093719SEd Schouten 	PROC_LOCK(p);
7956fd8c2bdSEdward Tomasz Napierala 	error = racct_add(p, RACCT_NPTS, 1);
7966fd8c2bdSEdward Tomasz Napierala 	if (error != 0) {
797bc093719SEd Schouten 		PROC_UNLOCK(p);
798bc093719SEd Schouten 		return (EAGAIN);
7996fd8c2bdSEdward Tomasz Napierala 	}
800f6f6d240SMateusz Guzik 	ok = chgptscnt(cred->cr_ruidinfo, 1, lim_cur(td, RLIMIT_NPTS));
8016fd8c2bdSEdward Tomasz Napierala 	if (!ok) {
8026fd8c2bdSEdward Tomasz Napierala 		racct_sub(p, RACCT_NPTS, 1);
8036fd8c2bdSEdward Tomasz Napierala 		PROC_UNLOCK(p);
8046fd8c2bdSEdward Tomasz Napierala 		return (EAGAIN);
8056fd8c2bdSEdward Tomasz Napierala 	}
8066fd8c2bdSEdward Tomasz Napierala 	PROC_UNLOCK(p);
807bc093719SEd Schouten 
808bc093719SEd Schouten 	/* Allocate TTY and softc. */
809bc093719SEd Schouten 	psc = malloc(sizeof(struct pts_softc), M_PTS, M_WAITOK|M_ZERO);
810e6d84d05SEd Schouten 	cv_init(&psc->pts_inwait, "ptsin");
811e6d84d05SEd Schouten 	cv_init(&psc->pts_outwait, "ptsout");
812bc093719SEd Schouten 
813bc093719SEd Schouten 	psc->pts_unit = -1;
814bc093719SEd Schouten 	psc->pts_cdev = dev;
815953bb3b9SEdward Tomasz Napierala 	psc->pts_cred = crhold(cred);
816bc093719SEd Schouten 
817c5e30cc0SEd Schouten 	tp = tty_alloc(&pts_class, psc);
818d8b0556cSKonstantin Belousov 	knlist_init_mtx(&psc->pts_inpoll.si_note, tp->t_mtx);
819d8b0556cSKonstantin Belousov 	knlist_init_mtx(&psc->pts_outpoll.si_note, tp->t_mtx);
820bc093719SEd Schouten 
821bc093719SEd Schouten 	/* Expose the slave device as well. */
822bc093719SEd Schouten 	tty_makedev(tp, td->td_ucred, "%s", name);
823bc093719SEd Schouten 
824bc093719SEd Schouten 	finit(fp, fflags, DTYPE_PTS, tp, &ptsdev_ops);
825bc093719SEd Schouten 
82612af2a0fSOlivier Houchard 	return (0);
82712af2a0fSOlivier Houchard }
828bc093719SEd Schouten #endif /* PTS_EXTERNAL */
829bc093719SEd Schouten 
830bc093719SEd Schouten int
8318451d0ddSKip Macy sys_posix_openpt(struct thread *td, struct posix_openpt_args *uap)
832bc093719SEd Schouten {
833bc093719SEd Schouten 	int error, fd;
834bc093719SEd Schouten 	struct file *fp;
835bc093719SEd Schouten 
83612af2a0fSOlivier Houchard 	/*
837bc093719SEd Schouten 	 * POSIX states it's unspecified when other flags are passed. We
838bc093719SEd Schouten 	 * don't allow this.
83912af2a0fSOlivier Houchard 	 */
84046f10cc2SJilles Tjoelker 	if (uap->flags & ~(O_RDWR|O_NOCTTY|O_CLOEXEC))
841bc093719SEd Schouten 		return (EINVAL);
842bc093719SEd Schouten 
84346f10cc2SJilles Tjoelker 	error = falloc(td, &fp, &fd, uap->flags);
844bc093719SEd Schouten 	if (error)
845bc093719SEd Schouten 		return (error);
846bc093719SEd Schouten 
847bc093719SEd Schouten 	/* Allocate the actual pseudo-TTY. */
848bc093719SEd Schouten 	error = pts_alloc(FFLAGS(uap->flags & O_ACCMODE), td, fp);
849bc093719SEd Schouten 	if (error != 0) {
85090f54cbfSMateusz Guzik 		fdclose(td, fp, fd);
851dd103d4dSMateusz Guzik 		fdrop(fp, td);
85212af2a0fSOlivier Houchard 		return (error);
85312af2a0fSOlivier Houchard 	}
85412af2a0fSOlivier Houchard 
855bc093719SEd Schouten 	/* Pass it back to userspace. */
856bc093719SEd Schouten 	td->td_retval[0] = fd;
857bc093719SEd Schouten 	fdrop(fp, td);
858bc093719SEd Schouten 
859bc093719SEd Schouten 	return (0);
860bc093719SEd Schouten }
861bc093719SEd Schouten 
86212af2a0fSOlivier Houchard static void
863bc093719SEd Schouten pts_init(void *unused)
86412af2a0fSOlivier Houchard {
86512af2a0fSOlivier Houchard 
866ccfd3aabSEd Schouten 	pts_pool = new_unrhdr(0, INT_MAX, NULL);
86712af2a0fSOlivier Houchard }
86812af2a0fSOlivier Houchard 
869bc093719SEd Schouten SYSINIT(pts, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, pts_init, NULL);
870