2 * Copyright (c) 2008 Ed Schouten <ed@FreeBSD.org>
5 * Portions of this software were developed under sponsorship from Snow
6 * B.V., the Netherlands.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
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.
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
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
35 /* Add compatibility bits for FreeBSD. */
38 /* Add /dev/ptyXX compat bits. */
41 /* Add bits to make Linux binaries work. */
44 #include <sys/param.h>
46 #include <sys/condvar.h>
48 #include <sys/fcntl.h>
50 #include <sys/filedesc.h>
51 #include <sys/filio.h>
52 #include <sys/kernel.h>
53 #include <sys/malloc.h>
56 #include <sys/resourcevar.h>
57 #include <sys/serial.h>
59 #include <sys/syscall.h>
60 #include <sys/syscallsubr.h>
61 #include <sys/sysent.h>
62 #include <sys/sysproto.h>
63 #include <sys/systm.h>
65 #include <sys/ttycom.h>
67 #include <machine/stdarg.h>
69 static struct unrhdr *pts_pool;
70 #define MAXPTSDEVS 999
72 static MALLOC_DEFINE(M_PTS, "pts", "pseudo tty device");
78 * (t) locked by tty_lock()
79 * (c) const until freeing
82 int pts_unit; /* (c) Device unit number. */
83 unsigned int pts_flags; /* (t) Device flags. */
84 #define PTS_PKT 0x1 /* Packet mode. */
85 #define PTS_FINISHED 0x2 /* Return errors on read()/write(). */
86 char pts_pkt; /* (t) Unread packet mode data. */
88 struct cv pts_inwait; /* (t) Blocking write() on master. */
89 struct selinfo pts_inpoll; /* (t) Select queue for write(). */
90 struct cv pts_outwait; /* (t) Blocking read() on master. */
91 struct selinfo pts_outpoll; /* (t) Select queue for read(). */
94 struct cdev *pts_cdev; /* (c) Master device node. */
95 #endif /* PTS_EXTERNAL */
97 struct uidinfo *pts_uidinfo; /* (c) Resource limit. */
101 * Controller-side file operations.
105 ptsdev_read(struct file *fp, struct uio *uio, struct ucred *active_cred,
106 int flags, struct thread *td)
108 struct tty *tp = fp->f_data;
109 struct pts_softc *psc = tty_softc(tp);
113 if (uio->uio_resid == 0)
120 * Implement packet mode. When packet mode is turned on,
121 * the first byte contains a bitmask of events that
122 * occured (start, stop, flush, window size, etc).
124 if (psc->pts_flags & PTS_PKT && psc->pts_pkt) {
129 error = ureadc(pkt, uio);
134 * Transmit regular data.
136 * XXX: We shouldn't use ttydisc_getc_poll()! Even
137 * though in this implementation, there is likely going
138 * to be data, we should just call ttydisc_getc_uio()
139 * and use its return value to sleep.
141 if (ttydisc_getc_poll(tp)) {
142 if (psc->pts_flags & PTS_PKT) {
144 * XXX: Small race. Fortunately PTY
145 * consumers aren't multithreaded.
149 error = ureadc(TIOCPKT_DATA, uio);
155 error = ttydisc_getc_uio(tp, uio);
159 /* Maybe the device isn't used anyway. */
160 if (psc->pts_flags & PTS_FINISHED)
163 /* Wait for more data. */
164 if (fp->f_flag & O_NONBLOCK) {
168 error = cv_wait_sig(&psc->pts_outwait, tp->t_mtx);
179 ptsdev_write(struct file *fp, struct uio *uio, struct ucred *active_cred,
180 int flags, struct thread *td)
182 struct tty *tp = fp->f_data;
183 struct pts_softc *psc = tty_softc(tp);
184 char ib[256], *ibstart;
185 size_t iblen, rintlen;
188 if (uio->uio_resid == 0)
193 iblen = MIN(uio->uio_resid, sizeof ib);
194 error = uiomove(ib, iblen, uio);
201 * When possible, avoid the slow path. rint_bypass()
202 * copies all input to the input queue at once.
206 if (ttydisc_can_bypass(tp)) {
207 /* Store data at once. */
208 rintlen = ttydisc_rint_bypass(tp,
214 /* All data written. */
218 error = ttydisc_rint(tp, *ibstart, 0);
220 /* Character stored successfully. */
227 /* Maybe the device isn't used anyway. */
228 if (psc->pts_flags & PTS_FINISHED) {
233 /* Wait for more data. */
234 if (fp->f_flag & O_NONBLOCK) {
239 /* Wake up users on the slave side. */
240 ttydisc_rint_done(tp);
241 error = cv_wait_sig(&psc->pts_inwait, tp->t_mtx);
246 if (uio->uio_resid == 0)
251 done: ttydisc_rint_done(tp);
257 ptsdev_ioctl(struct file *fp, u_long cmd, void *data,
258 struct ucred *active_cred, struct thread *td)
260 struct tty *tp = fp->f_data;
261 struct pts_softc *psc = tty_softc(tp);
266 /* This device supports non-blocking operation. */
269 struct fiodgname_arg *fgn;
273 /* Reverse device name lookups, for ptsname() and ttyname(). */
276 if (psc->pts_cdev != NULL)
277 p = devtoname(psc->pts_cdev);
279 #endif /* PTS_EXTERNAL */
284 return copyout(p, fgn->buf, i);
288 * We need to implement TIOCGPGRP and TIOCGSID here again. When
289 * called on the pseudo-terminal master, it should not check if
290 * the terminal is the foreground terminal of the calling
293 * TIOCGETA is also implemented here. Various Linux PTY routines
294 * often call isatty(), which is implemented by tcgetattr().
298 /* Obtain terminal flags through tcgetattr(). */
300 bcopy(&tp->t_termios, data, sizeof(struct termios));
303 #endif /* PTS_LINUX */
307 * We must make sure we turn tcsetattr() calls of TCSAFLUSH and
308 * TCSADRAIN into something different. If an application would
309 * call TCSAFLUSH or TCSADRAIN on the master descriptor, it may
310 * deadlock waiting for all data to be read.
314 #if defined(PTS_COMPAT) || defined(PTS_LINUX)
317 * Get the device unit number.
319 if (psc->pts_unit < 0)
321 *(unsigned int *)data = psc->pts_unit;
323 #endif /* PTS_COMPAT || PTS_LINUX */
325 /* Get the foreground process group ID. */
327 if (tp->t_pgrp != NULL)
328 *(int *)data = tp->t_pgrp->pg_id;
330 *(int *)data = NO_PID;
334 /* Get the session leader process ID. */
336 if (tp->t_session == NULL)
339 *(int *)data = tp->t_session->s_sid;
343 /* Yes, we are a pseudo-terminal master. */
346 /* Signal the foreground process group. */
348 if (sig < 1 || sig >= NSIG)
352 tty_signal_pgrp(tp, sig);
356 /* Enable/disable packet mode. */
359 psc->pts_flags |= PTS_PKT;
361 psc->pts_flags &= ~PTS_PKT;
366 /* Just redirect this ioctl to the slave device. */
368 error = tty_ioctl(tp, cmd, data, td);
375 ptsdev_poll(struct file *fp, int events, struct ucred *active_cred,
378 struct tty *tp = fp->f_data;
379 struct pts_softc *psc = tty_softc(tp);
384 if (psc->pts_flags & PTS_FINISHED) {
385 /* Slave device is not opened. */
388 (POLLHUP|POLLIN|POLLRDNORM|POLLOUT|POLLWRNORM));
391 if (events & (POLLIN|POLLRDNORM)) {
392 /* See if we can getc something. */
393 if (ttydisc_getc_poll(tp) ||
394 (psc->pts_flags & PTS_PKT && psc->pts_pkt))
395 revents |= events & (POLLIN|POLLRDNORM);
397 if (events & (POLLOUT|POLLWRNORM)) {
398 /* See if we can rint something. */
399 if (ttydisc_rint_poll(tp))
400 revents |= events & (POLLOUT|POLLWRNORM);
404 * No need to check for POLLHUP here. This device cannot be used
405 * as a callout device, which means we always have a carrier,
406 * because the master is.
411 * This code might look misleading, but the naming of
412 * poll events on this side is the opposite of the slave
415 if (events & (POLLIN|POLLRDNORM))
416 selrecord(td, &psc->pts_outpoll);
417 if (events & (POLLOUT|POLLWRNORM))
418 selrecord(td, &psc->pts_inpoll);
427 ptsdev_stat(struct file *fp, struct stat *sb, struct ucred *active_cred,
430 struct tty *tp = fp->f_data;
432 struct pts_softc *psc = tty_softc(tp);
433 #endif /* PTS_EXTERNAL */
434 struct cdev *dev = tp->t_dev;
437 * According to POSIX, we must implement an fstat(). This also
438 * makes this implementation compatible with Linux binaries,
439 * because Linux calls fstat() on the pseudo-terminal master to
442 * XXX: POSIX also mentions we must fill in st_dev, but how?
445 bzero(sb, sizeof *sb);
447 if (psc->pts_cdev != NULL)
448 sb->st_ino = sb->st_rdev = dev2udev(psc->pts_cdev);
450 #endif /* PTS_EXTERNAL */
451 sb->st_ino = sb->st_rdev = tty_udev(tp);
453 sb->st_atimespec = dev->si_atime;
454 sb->st_ctimespec = dev->si_ctime;
455 sb->st_mtimespec = dev->si_mtime;
456 sb->st_uid = dev->si_uid;
457 sb->st_gid = dev->si_gid;
458 sb->st_mode = dev->si_mode | S_IFCHR;
464 ptsdev_close(struct file *fp, struct thread *td)
466 struct tty *tp = fp->f_data;
468 /* Deallocate TTY device. */
475 static struct fileops ptsdev_ops = {
476 .fo_read = ptsdev_read,
477 .fo_write = ptsdev_write,
478 .fo_ioctl = ptsdev_ioctl,
479 .fo_poll = ptsdev_poll,
480 .fo_stat = ptsdev_stat,
481 .fo_close = ptsdev_close,
482 .fo_flags = DFLAG_PASSABLE,
490 ptsdrv_outwakeup(struct tty *tp)
492 struct pts_softc *psc = tty_softc(tp);
494 cv_broadcast(&psc->pts_outwait);
495 selwakeup(&psc->pts_outpoll);
499 ptsdrv_inwakeup(struct tty *tp)
501 struct pts_softc *psc = tty_softc(tp);
503 cv_broadcast(&psc->pts_inwait);
504 selwakeup(&psc->pts_inpoll);
508 ptsdrv_open(struct tty *tp)
510 struct pts_softc *psc = tty_softc(tp);
512 psc->pts_flags &= ~PTS_FINISHED;
518 ptsdrv_close(struct tty *tp)
520 struct pts_softc *psc = tty_softc(tp);
522 /* Wake up any blocked readers/writers. */
523 ptsdrv_outwakeup(tp);
526 psc->pts_flags |= PTS_FINISHED;
530 ptsdrv_pktnotify(struct tty *tp, char event)
532 struct pts_softc *psc = tty_softc(tp);
535 * Clear conflicting flags.
540 psc->pts_pkt &= ~TIOCPKT_START;
543 psc->pts_pkt &= ~TIOCPKT_STOP;
546 psc->pts_pkt &= ~TIOCPKT_DOSTOP;
549 psc->pts_pkt &= ~TIOCPKT_NOSTOP;
553 psc->pts_pkt |= event;
554 ptsdrv_outwakeup(tp);
558 ptsdrv_free(void *softc)
560 struct pts_softc *psc = softc;
562 /* Make device number available again. */
563 if (psc->pts_unit >= 0)
564 free_unr(pts_pool, psc->pts_unit);
566 chgptscnt(psc->pts_uidinfo, -1, 0);
567 uifree(psc->pts_uidinfo);
570 /* Destroy master device as well. */
571 if (psc->pts_cdev != NULL)
572 destroy_dev_sched(psc->pts_cdev);
573 #endif /* PTS_EXTERNAL */
578 static struct ttydevsw pts_class = {
579 .tsw_flags = TF_NOPREFIX,
580 .tsw_outwakeup = ptsdrv_outwakeup,
581 .tsw_inwakeup = ptsdrv_inwakeup,
582 .tsw_open = ptsdrv_open,
583 .tsw_close = ptsdrv_close,
584 .tsw_pktnotify = ptsdrv_pktnotify,
585 .tsw_free = ptsdrv_free,
589 pts_alloc(int fflags, struct thread *td, struct file *fp)
593 struct pts_softc *psc;
594 struct proc *p = td->td_proc;
595 struct uidinfo *uid = td->td_ucred->cr_ruidinfo;
597 /* Resource limiting. */
599 ok = chgptscnt(uid, 1, lim_cur(p, RLIMIT_NPTS));
604 /* Try to allocate a new pts unit number. */
605 unit = alloc_unr(pts_pool);
607 chgptscnt(uid, -1, 0);
611 /* Allocate TTY and softc. */
612 psc = malloc(sizeof(struct pts_softc), M_PTS, M_WAITOK|M_ZERO);
613 cv_init(&psc->pts_inwait, "pts inwait");
614 cv_init(&psc->pts_outwait, "pts outwait");
616 psc->pts_unit = unit;
617 psc->pts_uidinfo = uid;
620 tp = tty_alloc(&pts_class, psc, NULL);
622 /* Expose the slave device as well. */
623 tty_makedev(tp, td->td_ucred, "pts/%u", psc->pts_unit);
625 finit(fp, fflags, DTYPE_PTS, tp, &ptsdev_ops);
632 pts_alloc_external(int fflags, struct thread *td, struct file *fp,
633 struct cdev *dev, const char *name)
637 struct pts_softc *psc;
638 struct proc *p = td->td_proc;
639 struct uidinfo *uid = td->td_ucred->cr_ruidinfo;
641 /* Resource limiting. */
643 ok = chgptscnt(uid, 1, lim_cur(p, RLIMIT_NPTS));
648 /* Allocate TTY and softc. */
649 psc = malloc(sizeof(struct pts_softc), M_PTS, M_WAITOK|M_ZERO);
650 cv_init(&psc->pts_inwait, "pts inwait");
651 cv_init(&psc->pts_outwait, "pts outwait");
655 psc->pts_uidinfo = uid;
658 tp = tty_alloc(&pts_class, psc, NULL);
660 /* Expose the slave device as well. */
661 tty_makedev(tp, td->td_ucred, "%s", name);
663 finit(fp, fflags, DTYPE_PTS, tp, &ptsdev_ops);
667 #endif /* PTS_EXTERNAL */
670 posix_openpt(struct thread *td, struct posix_openpt_args *uap)
676 * POSIX states it's unspecified when other flags are passed. We
679 if (uap->flags & ~(O_RDWR|O_NOCTTY))
682 error = falloc(td, &fp, &fd);
686 /* Allocate the actual pseudo-TTY. */
687 error = pts_alloc(FFLAGS(uap->flags & O_ACCMODE), td, fp);
689 fdclose(td->td_proc->p_fd, fp, fd, td);
693 /* Pass it back to userspace. */
694 td->td_retval[0] = fd;
700 #if defined(PTS_COMPAT) || defined(PTS_LINUX)
702 ptmx_fdopen(struct cdev *dev, int fflags, struct thread *td, struct file *fp)
706 error = pts_alloc(fflags & (FREAD|FWRITE), td, fp);
713 static struct cdevsw ptmx_cdevsw = {
714 .d_version = D_VERSION,
715 .d_fdopen = ptmx_fdopen,
718 #endif /* PTS_COMPAT || PTS_LINUX */
721 pts_init(void *unused)
724 pts_pool = new_unrhdr(0, MAXPTSDEVS, NULL);
725 #if defined(PTS_COMPAT) || defined(PTS_LINUX)
726 make_dev(&ptmx_cdevsw, 0, UID_ROOT, GID_WHEEL, 0666, "ptmx");
727 #endif /* PTS_COMPAT || PTS_LINUX */
730 SYSINIT(pts, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, pts_init, NULL);