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