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