The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/kern/tty_pts.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

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

Cache object: 7853cd8b34d5db168879baa28780f92b


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.