tty_pts.c revision 321020
1/*-
2 * Copyright (c) 2008 Ed Schouten <ed@FreeBSD.org>
3 * All rights reserved.
4 *
5 * Portions of this software were developed under sponsorship from Snow
6 * B.V., the Netherlands.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#include <sys/cdefs.h>
31__FBSDID("$FreeBSD: stable/10/sys/kern/tty_pts.c 321020 2017-07-15 17:25:40Z dchagin $");
32
33/* Add compatibility bits for FreeBSD. */
34#define PTS_COMPAT
35/* Add pty(4) compat bits. */
36#define PTS_EXTERNAL
37/* Add bits to make Linux binaries work. */
38#define PTS_LINUX
39
40#include <sys/param.h>
41#include <sys/lock.h>
42#include <sys/condvar.h>
43#include <sys/conf.h>
44#include <sys/fcntl.h>
45#include <sys/file.h>
46#include <sys/filedesc.h>
47#include <sys/filio.h>
48#include <sys/kernel.h>
49#include <sys/limits.h>
50#include <sys/malloc.h>
51#include <sys/poll.h>
52#include <sys/proc.h>
53#include <sys/racct.h>
54#include <sys/resourcevar.h>
55#include <sys/serial.h>
56#include <sys/stat.h>
57#include <sys/syscall.h>
58#include <sys/syscallsubr.h>
59#include <sys/sysctl.h>
60#include <sys/sysent.h>
61#include <sys/sysproto.h>
62#include <sys/systm.h>
63#include <sys/tty.h>
64#include <sys/ttycom.h>
65
66#include <machine/stdarg.h>
67
68/*
69 * Our utmp(5) format is limited to 8-byte TTY line names.  This means
70 * we can at most allocate 1000 pseudo-terminals ("pts/999").  Allow
71 * users to increase this number, assuming they have manually increased
72 * UT_LINESIZE.
73 */
74static struct unrhdr *pts_pool;
75
76static MALLOC_DEFINE(M_PTS, "pts", "pseudo tty device");
77
78/*
79 * Per-PTS structure.
80 *
81 * List of locks
82 * (t)	locked by tty_lock()
83 * (c)	const until freeing
84 */
85struct pts_softc {
86	int		pts_unit;	/* (c) Device unit number. */
87	unsigned int	pts_flags;	/* (t) Device flags. */
88#define	PTS_PKT		0x1	/* Packet mode. */
89#define	PTS_FINISHED	0x2	/* Return errors on read()/write(). */
90	char		pts_pkt;	/* (t) Unread packet mode data. */
91
92	struct cv	pts_inwait;	/* (t) Blocking write() on master. */
93	struct selinfo	pts_inpoll;	/* (t) Select queue for write(). */
94	struct cv	pts_outwait;	/* (t) Blocking read() on master. */
95	struct selinfo	pts_outpoll;	/* (t) Select queue for read(). */
96
97#ifdef PTS_EXTERNAL
98	struct cdev	*pts_cdev;	/* (c) Master device node. */
99#endif /* PTS_EXTERNAL */
100
101	struct ucred	*pts_cred;	/* (c) Resource limit. */
102};
103
104/*
105 * Controller-side file operations.
106 */
107
108static int
109ptsdev_read(struct file *fp, struct uio *uio, struct ucred *active_cred,
110    int flags, struct thread *td)
111{
112	struct tty *tp = fp->f_data;
113	struct pts_softc *psc = tty_softc(tp);
114	int error = 0;
115	char pkt;
116
117	if (uio->uio_resid == 0)
118		return (0);
119
120	tty_lock(tp);
121
122	for (;;) {
123		/*
124		 * Implement packet mode. When packet mode is turned on,
125		 * the first byte contains a bitmask of events that
126		 * occurred (start, stop, flush, window size, etc).
127		 */
128		if (psc->pts_flags & PTS_PKT && psc->pts_pkt) {
129			pkt = psc->pts_pkt;
130			psc->pts_pkt = 0;
131			tty_unlock(tp);
132
133			error = ureadc(pkt, uio);
134			return (error);
135		}
136
137		/*
138		 * Transmit regular data.
139		 *
140		 * XXX: We shouldn't use ttydisc_getc_poll()! Even
141		 * though in this implementation, there is likely going
142		 * to be data, we should just call ttydisc_getc_uio()
143		 * and use its return value to sleep.
144		 */
145		if (ttydisc_getc_poll(tp)) {
146			if (psc->pts_flags & PTS_PKT) {
147				/*
148				 * XXX: Small race. Fortunately PTY
149				 * consumers aren't multithreaded.
150				 */
151
152				tty_unlock(tp);
153				error = ureadc(TIOCPKT_DATA, uio);
154				if (error)
155					return (error);
156				tty_lock(tp);
157			}
158
159			error = ttydisc_getc_uio(tp, uio);
160			break;
161		}
162
163		/* Maybe the device isn't used anyway. */
164		if (psc->pts_flags & PTS_FINISHED)
165			break;
166
167		/* Wait for more data. */
168		if (fp->f_flag & O_NONBLOCK) {
169			error = EWOULDBLOCK;
170			break;
171		}
172		error = cv_wait_sig(&psc->pts_outwait, tp->t_mtx);
173		if (error != 0)
174			break;
175	}
176
177	tty_unlock(tp);
178
179	return (error);
180}
181
182static int
183ptsdev_write(struct file *fp, struct uio *uio, struct ucred *active_cred,
184    int flags, struct thread *td)
185{
186	struct tty *tp = fp->f_data;
187	struct pts_softc *psc = tty_softc(tp);
188	char ib[256], *ibstart;
189	size_t iblen, rintlen;
190	int error = 0;
191
192	if (uio->uio_resid == 0)
193		return (0);
194
195	for (;;) {
196		ibstart = ib;
197		iblen = MIN(uio->uio_resid, sizeof ib);
198		error = uiomove(ib, iblen, uio);
199
200		tty_lock(tp);
201		if (error != 0) {
202			iblen = 0;
203			goto done;
204		}
205
206		/*
207		 * When possible, avoid the slow path. rint_bypass()
208		 * copies all input to the input queue at once.
209		 */
210		MPASS(iblen > 0);
211		do {
212			rintlen = ttydisc_rint_simple(tp, ibstart, iblen);
213			ibstart += rintlen;
214			iblen -= rintlen;
215			if (iblen == 0) {
216				/* All data written. */
217				break;
218			}
219
220			/* Maybe the device isn't used anyway. */
221			if (psc->pts_flags & PTS_FINISHED) {
222				error = EIO;
223				goto done;
224			}
225
226			/* Wait for more data. */
227			if (fp->f_flag & O_NONBLOCK) {
228				error = EWOULDBLOCK;
229				goto done;
230			}
231
232			/* Wake up users on the slave side. */
233			ttydisc_rint_done(tp);
234			error = cv_wait_sig(&psc->pts_inwait, tp->t_mtx);
235			if (error != 0)
236				goto done;
237		} while (iblen > 0);
238
239		if (uio->uio_resid == 0)
240			break;
241		tty_unlock(tp);
242	}
243
244done:	ttydisc_rint_done(tp);
245	tty_unlock(tp);
246
247	/*
248	 * Don't account for the part of the buffer that we couldn't
249	 * pass to the TTY.
250	 */
251	uio->uio_resid += iblen;
252	return (error);
253}
254
255static int
256ptsdev_truncate(struct file *fp, off_t length, struct ucred *active_cred,
257    struct thread *td)
258{
259
260	return (EINVAL);
261}
262
263static int
264ptsdev_ioctl(struct file *fp, u_long cmd, void *data,
265    struct ucred *active_cred, struct thread *td)
266{
267	struct tty *tp = fp->f_data;
268	struct pts_softc *psc = tty_softc(tp);
269	int error = 0, sig;
270
271	switch (cmd) {
272	case FIODTYPE:
273		*(int *)data = D_TTY;
274		return (0);
275	case FIONBIO:
276		/* This device supports non-blocking operation. */
277		return (0);
278	case FIONREAD:
279		tty_lock(tp);
280		if (psc->pts_flags & PTS_FINISHED) {
281			/* Force read() to be called. */
282			*(int *)data = 1;
283		} else {
284			*(int *)data = ttydisc_getc_poll(tp);
285		}
286		tty_unlock(tp);
287		return (0);
288	case FIODGNAME: {
289		struct fiodgname_arg *fgn;
290		const char *p;
291		int i;
292
293		/* Reverse device name lookups, for ptsname() and ttyname(). */
294		fgn = data;
295		p = tty_devname(tp);
296		i = strlen(p) + 1;
297		if (i > fgn->len)
298			return (EINVAL);
299		return copyout(p, fgn->buf, i);
300	}
301
302	/*
303	 * We need to implement TIOCGPGRP and TIOCGSID here again. When
304	 * called on the pseudo-terminal master, it should not check if
305	 * the terminal is the foreground terminal of the calling
306	 * process.
307	 *
308	 * TIOCGETA is also implemented here. Various Linux PTY routines
309	 * often call isatty(), which is implemented by tcgetattr().
310	 */
311#ifdef PTS_LINUX
312	case TIOCGETA:
313		/* Obtain terminal flags through tcgetattr(). */
314		tty_lock(tp);
315		*(struct termios*)data = tp->t_termios;
316		tty_unlock(tp);
317		return (0);
318#endif /* PTS_LINUX */
319	case TIOCSETAF:
320	case TIOCSETAW:
321		/*
322		 * We must make sure we turn tcsetattr() calls of TCSAFLUSH and
323		 * TCSADRAIN into something different. If an application would
324		 * call TCSAFLUSH or TCSADRAIN on the master descriptor, it may
325		 * deadlock waiting for all data to be read.
326		 */
327		cmd = TIOCSETA;
328		break;
329#if defined(PTS_COMPAT) || defined(PTS_LINUX)
330	case TIOCGPTN:
331		/*
332		 * Get the device unit number.
333		 */
334		if (psc->pts_unit < 0)
335			return (ENOTTY);
336		*(unsigned int *)data = psc->pts_unit;
337		return (0);
338#endif /* PTS_COMPAT || PTS_LINUX */
339	case TIOCGPGRP:
340		/* Get the foreground process group ID. */
341		tty_lock(tp);
342		if (tp->t_pgrp != NULL)
343			*(int *)data = tp->t_pgrp->pg_id;
344		else
345			*(int *)data = NO_PID;
346		tty_unlock(tp);
347		return (0);
348	case TIOCGSID:
349		/* Get the session leader process ID. */
350		tty_lock(tp);
351		if (tp->t_session == NULL)
352			error = ENOTTY;
353		else
354			*(int *)data = tp->t_session->s_sid;
355		tty_unlock(tp);
356		return (error);
357	case TIOCPTMASTER:
358		/* Yes, we are a pseudo-terminal master. */
359		return (0);
360	case TIOCSIG:
361		/* Signal the foreground process group. */
362		sig = *(int *)data;
363		if (sig < 1 || sig >= NSIG)
364			return (EINVAL);
365
366		tty_lock(tp);
367		tty_signal_pgrp(tp, sig);
368		tty_unlock(tp);
369		return (0);
370	case TIOCPKT:
371		/* Enable/disable packet mode. */
372		tty_lock(tp);
373		if (*(int *)data)
374			psc->pts_flags |= PTS_PKT;
375		else
376			psc->pts_flags &= ~PTS_PKT;
377		tty_unlock(tp);
378		return (0);
379	}
380
381	/* Just redirect this ioctl to the slave device. */
382	tty_lock(tp);
383	error = tty_ioctl(tp, cmd, data, fp->f_flag, td);
384	tty_unlock(tp);
385	if (error == ENOIOCTL)
386		error = ENOTTY;
387
388	return (error);
389}
390
391static int
392ptsdev_poll(struct file *fp, int events, struct ucred *active_cred,
393    struct thread *td)
394{
395	struct tty *tp = fp->f_data;
396	struct pts_softc *psc = tty_softc(tp);
397	int revents = 0;
398
399	tty_lock(tp);
400
401	if (psc->pts_flags & PTS_FINISHED) {
402		/* Slave device is not opened. */
403		tty_unlock(tp);
404		return ((events & (POLLIN|POLLRDNORM)) | POLLHUP);
405	}
406
407	if (events & (POLLIN|POLLRDNORM)) {
408		/* See if we can getc something. */
409		if (ttydisc_getc_poll(tp) ||
410		    (psc->pts_flags & PTS_PKT && psc->pts_pkt))
411			revents |= events & (POLLIN|POLLRDNORM);
412	}
413	if (events & (POLLOUT|POLLWRNORM)) {
414		/* See if we can rint something. */
415		if (ttydisc_rint_poll(tp))
416			revents |= events & (POLLOUT|POLLWRNORM);
417	}
418
419	/*
420	 * No need to check for POLLHUP here. This device cannot be used
421	 * as a callout device, which means we always have a carrier,
422	 * because the master is.
423	 */
424
425	if (revents == 0) {
426		/*
427		 * This code might look misleading, but the naming of
428		 * poll events on this side is the opposite of the slave
429		 * device.
430		 */
431		if (events & (POLLIN|POLLRDNORM))
432			selrecord(td, &psc->pts_outpoll);
433		if (events & (POLLOUT|POLLWRNORM))
434			selrecord(td, &psc->pts_inpoll);
435	}
436
437	tty_unlock(tp);
438
439	return (revents);
440}
441
442/*
443 * kqueue support.
444 */
445
446static void
447pts_kqops_read_detach(struct knote *kn)
448{
449	struct file *fp = kn->kn_fp;
450	struct tty *tp = fp->f_data;
451	struct pts_softc *psc = tty_softc(tp);
452
453	knlist_remove(&psc->pts_outpoll.si_note, kn, 0);
454}
455
456static int
457pts_kqops_read_event(struct knote *kn, long hint)
458{
459	struct file *fp = kn->kn_fp;
460	struct tty *tp = fp->f_data;
461	struct pts_softc *psc = tty_softc(tp);
462
463	if (psc->pts_flags & PTS_FINISHED) {
464		kn->kn_flags |= EV_EOF;
465		return (1);
466	} else {
467		kn->kn_data = ttydisc_getc_poll(tp);
468		return (kn->kn_data > 0);
469	}
470}
471
472static void
473pts_kqops_write_detach(struct knote *kn)
474{
475	struct file *fp = kn->kn_fp;
476	struct tty *tp = fp->f_data;
477	struct pts_softc *psc = tty_softc(tp);
478
479	knlist_remove(&psc->pts_inpoll.si_note, kn, 0);
480}
481
482static int
483pts_kqops_write_event(struct knote *kn, long hint)
484{
485	struct file *fp = kn->kn_fp;
486	struct tty *tp = fp->f_data;
487	struct pts_softc *psc = tty_softc(tp);
488
489	if (psc->pts_flags & PTS_FINISHED) {
490		kn->kn_flags |= EV_EOF;
491		return (1);
492	} else {
493		kn->kn_data = ttydisc_rint_poll(tp);
494		return (kn->kn_data > 0);
495	}
496}
497
498static struct filterops pts_kqops_read = {
499	.f_isfd = 1,
500	.f_detach = pts_kqops_read_detach,
501	.f_event = pts_kqops_read_event,
502};
503static struct filterops pts_kqops_write = {
504	.f_isfd = 1,
505	.f_detach = pts_kqops_write_detach,
506	.f_event = pts_kqops_write_event,
507};
508
509static int
510ptsdev_kqfilter(struct file *fp, struct knote *kn)
511{
512	struct tty *tp = fp->f_data;
513	struct pts_softc *psc = tty_softc(tp);
514	int error = 0;
515
516	tty_lock(tp);
517
518	switch (kn->kn_filter) {
519	case EVFILT_READ:
520		kn->kn_fop = &pts_kqops_read;
521		knlist_add(&psc->pts_outpoll.si_note, kn, 1);
522		break;
523	case EVFILT_WRITE:
524		kn->kn_fop = &pts_kqops_write;
525		knlist_add(&psc->pts_inpoll.si_note, kn, 1);
526		break;
527	default:
528		error = EINVAL;
529		break;
530	}
531
532	tty_unlock(tp);
533	return (error);
534}
535
536static int
537ptsdev_stat(struct file *fp, struct stat *sb, struct ucred *active_cred,
538    struct thread *td)
539{
540	struct tty *tp = fp->f_data;
541#ifdef PTS_EXTERNAL
542	struct pts_softc *psc = tty_softc(tp);
543#endif /* PTS_EXTERNAL */
544	struct cdev *dev = tp->t_dev;
545
546	/*
547	 * According to POSIX, we must implement an fstat(). This also
548	 * makes this implementation compatible with Linux binaries,
549	 * because Linux calls fstat() on the pseudo-terminal master to
550	 * obtain st_rdev.
551	 *
552	 * XXX: POSIX also mentions we must fill in st_dev, but how?
553	 */
554
555	bzero(sb, sizeof *sb);
556#ifdef PTS_EXTERNAL
557	if (psc->pts_cdev != NULL)
558		sb->st_ino = sb->st_rdev = dev2udev(psc->pts_cdev);
559	else
560#endif /* PTS_EXTERNAL */
561		sb->st_ino = sb->st_rdev = tty_udev(tp);
562
563	sb->st_atim = dev->si_atime;
564	sb->st_ctim = dev->si_ctime;
565	sb->st_mtim = dev->si_mtime;
566	sb->st_uid = dev->si_uid;
567	sb->st_gid = dev->si_gid;
568	sb->st_mode = dev->si_mode | S_IFCHR;
569
570	return (0);
571}
572
573static int
574ptsdev_close(struct file *fp, struct thread *td)
575{
576	struct tty *tp = fp->f_data;
577
578	/* Deallocate TTY device. */
579	tty_lock(tp);
580	tty_rel_gone(tp);
581
582	/*
583	 * Open of /dev/ptmx or /dev/ptyXX changes the type of file
584	 * from DTYPE_VNODE to DTYPE_PTS. vn_open() increases vnode
585	 * use count, we need to decrement it, and possibly do other
586	 * required cleanup.
587	 */
588	if (fp->f_vnode != NULL)
589		return (vnops.fo_close(fp, td));
590
591	return (0);
592}
593
594static struct fileops ptsdev_ops = {
595	.fo_read	= ptsdev_read,
596	.fo_write	= ptsdev_write,
597	.fo_truncate	= ptsdev_truncate,
598	.fo_ioctl	= ptsdev_ioctl,
599	.fo_poll	= ptsdev_poll,
600	.fo_kqfilter	= ptsdev_kqfilter,
601	.fo_stat	= ptsdev_stat,
602	.fo_close	= ptsdev_close,
603	.fo_chmod	= invfo_chmod,
604	.fo_chown	= invfo_chown,
605	.fo_sendfile	= invfo_sendfile,
606	.fo_flags	= DFLAG_PASSABLE,
607};
608
609/*
610 * Driver-side hooks.
611 */
612
613static void
614ptsdrv_outwakeup(struct tty *tp)
615{
616	struct pts_softc *psc = tty_softc(tp);
617
618	cv_broadcast(&psc->pts_outwait);
619	selwakeup(&psc->pts_outpoll);
620	KNOTE_LOCKED(&psc->pts_outpoll.si_note, 0);
621}
622
623static void
624ptsdrv_inwakeup(struct tty *tp)
625{
626	struct pts_softc *psc = tty_softc(tp);
627
628	cv_broadcast(&psc->pts_inwait);
629	selwakeup(&psc->pts_inpoll);
630	KNOTE_LOCKED(&psc->pts_inpoll.si_note, 0);
631}
632
633static int
634ptsdrv_open(struct tty *tp)
635{
636	struct pts_softc *psc = tty_softc(tp);
637
638	psc->pts_flags &= ~PTS_FINISHED;
639
640	return (0);
641}
642
643static void
644ptsdrv_close(struct tty *tp)
645{
646	struct pts_softc *psc = tty_softc(tp);
647
648	/* Wake up any blocked readers/writers. */
649	psc->pts_flags |= PTS_FINISHED;
650	ptsdrv_outwakeup(tp);
651	ptsdrv_inwakeup(tp);
652}
653
654static void
655ptsdrv_pktnotify(struct tty *tp, char event)
656{
657	struct pts_softc *psc = tty_softc(tp);
658
659	/*
660	 * Clear conflicting flags.
661	 */
662
663	switch (event) {
664	case TIOCPKT_STOP:
665		psc->pts_pkt &= ~TIOCPKT_START;
666		break;
667	case TIOCPKT_START:
668		psc->pts_pkt &= ~TIOCPKT_STOP;
669		break;
670	case TIOCPKT_NOSTOP:
671		psc->pts_pkt &= ~TIOCPKT_DOSTOP;
672		break;
673	case TIOCPKT_DOSTOP:
674		psc->pts_pkt &= ~TIOCPKT_NOSTOP;
675		break;
676	}
677
678	psc->pts_pkt |= event;
679	ptsdrv_outwakeup(tp);
680}
681
682static void
683ptsdrv_free(void *softc)
684{
685	struct pts_softc *psc = softc;
686
687	/* Make device number available again. */
688	if (psc->pts_unit >= 0)
689		free_unr(pts_pool, psc->pts_unit);
690
691	chgptscnt(psc->pts_cred->cr_ruidinfo, -1, 0);
692	racct_sub_cred(psc->pts_cred, RACCT_NPTS, 1);
693	crfree(psc->pts_cred);
694
695	seldrain(&psc->pts_inpoll);
696	seldrain(&psc->pts_outpoll);
697	knlist_destroy(&psc->pts_inpoll.si_note);
698	knlist_destroy(&psc->pts_outpoll.si_note);
699
700#ifdef PTS_EXTERNAL
701	/* Destroy master device as well. */
702	if (psc->pts_cdev != NULL)
703		destroy_dev_sched(psc->pts_cdev);
704#endif /* PTS_EXTERNAL */
705
706	free(psc, M_PTS);
707}
708
709static struct ttydevsw pts_class = {
710	.tsw_flags	= TF_NOPREFIX,
711	.tsw_outwakeup	= ptsdrv_outwakeup,
712	.tsw_inwakeup	= ptsdrv_inwakeup,
713	.tsw_open	= ptsdrv_open,
714	.tsw_close	= ptsdrv_close,
715	.tsw_pktnotify	= ptsdrv_pktnotify,
716	.tsw_free	= ptsdrv_free,
717};
718
719#ifndef PTS_EXTERNAL
720static
721#endif /* !PTS_EXTERNAL */
722int
723pts_alloc(int fflags, struct thread *td, struct file *fp)
724{
725	int unit, ok, error;
726	struct tty *tp;
727	struct pts_softc *psc;
728	struct proc *p = td->td_proc;
729	struct ucred *cred = td->td_ucred;
730
731	/* Resource limiting. */
732	PROC_LOCK(p);
733	error = racct_add(p, RACCT_NPTS, 1);
734	if (error != 0) {
735		PROC_UNLOCK(p);
736		return (EAGAIN);
737	}
738	ok = chgptscnt(cred->cr_ruidinfo, 1, lim_cur(p, RLIMIT_NPTS));
739	if (!ok) {
740		racct_sub(p, RACCT_NPTS, 1);
741		PROC_UNLOCK(p);
742		return (EAGAIN);
743	}
744	PROC_UNLOCK(p);
745
746	/* Try to allocate a new pts unit number. */
747	unit = alloc_unr(pts_pool);
748	if (unit < 0) {
749		racct_sub(p, RACCT_NPTS, 1);
750		chgptscnt(cred->cr_ruidinfo, -1, 0);
751		return (EAGAIN);
752	}
753
754	/* Allocate TTY and softc. */
755	psc = malloc(sizeof(struct pts_softc), M_PTS, M_WAITOK|M_ZERO);
756	cv_init(&psc->pts_inwait, "ptsin");
757	cv_init(&psc->pts_outwait, "ptsout");
758
759	psc->pts_unit = unit;
760	psc->pts_cred = crhold(cred);
761
762	tp = tty_alloc(&pts_class, psc);
763	knlist_init_mtx(&psc->pts_inpoll.si_note, tp->t_mtx);
764	knlist_init_mtx(&psc->pts_outpoll.si_note, tp->t_mtx);
765
766	/* Expose the slave device as well. */
767	tty_makedev(tp, td->td_ucred, "pts/%u", psc->pts_unit);
768
769	finit(fp, fflags, DTYPE_PTS, tp, &ptsdev_ops);
770
771	return (0);
772}
773
774#ifdef PTS_EXTERNAL
775int
776pts_alloc_external(int fflags, struct thread *td, struct file *fp,
777    struct cdev *dev, const char *name)
778{
779	int ok, error;
780	struct tty *tp;
781	struct pts_softc *psc;
782	struct proc *p = td->td_proc;
783	struct ucred *cred = td->td_ucred;
784
785	/* Resource limiting. */
786	PROC_LOCK(p);
787	error = racct_add(p, RACCT_NPTS, 1);
788	if (error != 0) {
789		PROC_UNLOCK(p);
790		return (EAGAIN);
791	}
792	ok = chgptscnt(cred->cr_ruidinfo, 1, lim_cur(p, RLIMIT_NPTS));
793	if (!ok) {
794		racct_sub(p, RACCT_NPTS, 1);
795		PROC_UNLOCK(p);
796		return (EAGAIN);
797	}
798	PROC_UNLOCK(p);
799
800	/* Allocate TTY and softc. */
801	psc = malloc(sizeof(struct pts_softc), M_PTS, M_WAITOK|M_ZERO);
802	cv_init(&psc->pts_inwait, "ptsin");
803	cv_init(&psc->pts_outwait, "ptsout");
804
805	psc->pts_unit = -1;
806	psc->pts_cdev = dev;
807	psc->pts_cred = crhold(cred);
808
809	tp = tty_alloc(&pts_class, psc);
810	knlist_init_mtx(&psc->pts_inpoll.si_note, tp->t_mtx);
811	knlist_init_mtx(&psc->pts_outpoll.si_note, tp->t_mtx);
812
813	/* Expose the slave device as well. */
814	tty_makedev(tp, td->td_ucred, "%s", name);
815
816	finit(fp, fflags, DTYPE_PTS, tp, &ptsdev_ops);
817
818	return (0);
819}
820#endif /* PTS_EXTERNAL */
821
822int
823sys_posix_openpt(struct thread *td, struct posix_openpt_args *uap)
824{
825	int error, fd;
826	struct file *fp;
827
828	/*
829	 * POSIX states it's unspecified when other flags are passed. We
830	 * don't allow this.
831	 */
832	if (uap->flags & ~(O_RDWR|O_NOCTTY|O_CLOEXEC))
833		return (EINVAL);
834
835	error = falloc(td, &fp, &fd, uap->flags);
836	if (error)
837		return (error);
838
839	/* Allocate the actual pseudo-TTY. */
840	error = pts_alloc(FFLAGS(uap->flags & O_ACCMODE), td, fp);
841	if (error != 0) {
842		fdclose(td, fp, fd);
843		fdrop(fp, td);
844		return (error);
845	}
846
847	/* Pass it back to userspace. */
848	td->td_retval[0] = fd;
849	fdrop(fp, td);
850
851	return (0);
852}
853
854static void
855pts_init(void *unused)
856{
857
858	pts_pool = new_unrhdr(0, INT_MAX, NULL);
859}
860
861SYSINIT(pts, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, pts_init, NULL);
862