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

Cache object: c6cbf9633f99d8959a230a1f83a64769


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