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

Cache object: e42b3b8c64cc87eb66fb72e848257580


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