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

Cache object: 340df88c0ec8fccefe17e771ac71c2ea


[ 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.