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