Print this page
14249 pseudo-terminal nomenclature should reflect POSIX
Change-Id: Ib4a3cef899ff4c71b09cb0dc6878863c5e8357bc


  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 /*      Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T     */
  26 /*        All Rights Reserved   */
  27 
  28 /*
  29  * Copyright 2020 OmniOS Community Edition (OmniOSce) Association.

  30  */
  31 
  32 /*
  33  * Pseudo Terminal Slave Driver.
  34  *
  35  * The pseudo-tty subsystem simulates a terminal connection, where the master
  36  * side represents the terminal and the slave represents the user process's
  37  * special device end point. The master device is set up as a cloned device
  38  * where its major device number is the major for the clone device and its minor
  39  * device number is the major for the ptm driver. There are no nodes in the file
  40  * system for master devices. The master pseudo driver is opened using the
  41  * open(2) system call with /dev/ptmx as the device parameter.  The clone open
  42  * finds the next available minor device for the ptm major device.

  43  *
  44  * A master device is available only if it and its corresponding slave device
  45  * are not already open. When the master device is opened, the corresponding
  46  * slave device is automatically locked out. Only one open is allowed on a
  47  * master device.  Multiple opens are allowed on the slave device.  After both
  48  * the master and slave have been opened, the user has two file descriptors
  49  * which are the end points of a full duplex connection composed of two streams
  50  * which are automatically connected at the master and slave drivers. The user
  51  * may then push modules onto either side of the stream pair.

  52  *
  53  * The master and slave drivers pass all messages to their adjacent queues.
  54  * Only the M_FLUSH needs some processing.  Because the read queue of one side
  55  * is connected to the write queue of the other, the FLUSHR flag is changed to
  56  * the FLUSHW flag and vice versa. When the master device is closed an M_HANGUP
  57  * message is sent to the slave device which will render the device
  58  * unusable. The process on the slave side gets the EIO when attempting to write
  59  * on that stream but it will be able to read any data remaining on the stream
  60  * head read queue.  When all the data has been read, read() returns 0
  61  * indicating that the stream can no longer be used.  On the last close of the
  62  * slave device, a 0-length message is sent to the master device. When the
  63  * application on the master side issues a read() or getmsg() and 0 is returned,
  64  * the user of the master device decides whether to issue a close() that
  65  * dismantles the pseudo-terminal subsystem. If the master device is not closed,
  66  * the pseudo-tty subsystem will be available to another user to open the slave
  67  * device.
  68  *
  69  * Synchronization:
  70  *
  71  *   All global data synchronization between ptm/pts is done via global
  72  *   ptms_lock mutex which is initialized at system boot time from
  73  *   ptms_initspace (called from space.c).
  74  *




  75  *   Individual fields of pt_ttys structure (except ptm_rdq, pts_rdq and
  76  *   pt_nullmsg) are protected by pt_ttys.pt_lock mutex.
  77  *
  78  *   PT_ENTER_READ/PT_ENTER_WRITE are reference counter based read-write locks
  79  *   which allow reader locks to be reacquired by the same thread (usual
  80  *   reader/writer locks can't be used for that purpose since it is illegal for
  81  *   a thread to acquire a lock it already holds, even as a reader). The sole
  82  *   purpose of these macros is to guarantee that the peer queue will not
  83  *   disappear (due to closing peer) while it is used. It is safe to use
  84  *   PT_ENTER_READ/PT_EXIT_READ brackets across calls like putq/putnext (since
  85  *   they are not real locks but reference counts).
  86  *
  87  *   PT_ENTER_WRITE/PT_EXIT_WRITE brackets are used ONLY in master/slave
  88  *   open/close paths to modify ptm_rdq and pts_rdq fields. These fields should
  89  *   be set to appropriate queues *after* qprocson() is called during open (to
  90  *   prevent peer from accessing the queue with incomplete plumbing) and set to
  91  *   NULL before qprocsoff() is called during close.
  92  *
  93  *   The pt_nullmsg field is only used in open/close routines and it is also
  94  *   protected by PT_ENTER_WRITE/PT_EXIT_WRITE brackets to avoid extra mutex
  95  *   holds.
  96  *
  97  * Lock Ordering:
  98  *


  99  *   If both ptms_lock and per-pty lock should be held, ptms_lock should always
 100  *   be entered first, followed by per-pty lock.
 101  *
 102  * See ptms.h, ptm.c and ptms_conf.c fore more information.
 103  *
 104  */
 105 
 106 #include <sys/types.h>
 107 #include <sys/param.h>
 108 #include <sys/sysmacros.h>
 109 #include <sys/stream.h>
 110 #include <sys/stropts.h>
 111 #include <sys/strsubr.h>
 112 #include <sys/stat.h>
 113 #include <sys/errno.h>
 114 #include <sys/debug.h>
 115 #include <sys/cmn_err.h>
 116 #include <sys/ptms.h>
 117 #include <sys/systm.h>
 118 #include <sys/modctl.h>
 119 #include <sys/conf.h>
 120 #include <sys/ddi.h>
 121 #include <sys/sunddi.h>
 122 #include <sys/cred.h>
 123 #include <sys/zone.h>
 124 
 125 #ifdef DEBUG
 126 int pts_debug = 0;
 127 #define DBG(a)   if (pts_debug) cmn_err(CE_NOTE, a)
 128 #else
 129 #define DBG(a)
 130 #endif
 131 
 132 static int ptsopen(queue_t *, dev_t *, int, int, cred_t *);
 133 static int ptsclose(queue_t *, int, cred_t *);
 134 static int ptswput(queue_t *, mblk_t *);
 135 static int ptsrsrv(queue_t *);
 136 static int ptswsrv(queue_t *);
 137 
 138 /*
 139  * Slave Stream Pseudo Terminal Module: stream data structure definitions
 140  */
 141 static struct module_info pts_info = {
 142         0xface,
 143         "pts",
 144         0,
 145         _TTY_BUFSIZ,
 146         _TTY_BUFSIZ,
 147         128
 148 };
 149 
 150 static struct qinit ptsrint = {
 151         NULL,
 152         ptsrsrv,
 153         ptsopen,
 154         ptsclose,
 155         NULL,
 156         &pts_info,
 157         NULL
 158 };
 159 
 160 static struct qinit ptswint = {


 175 };
 176 
 177 static int pts_devinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
 178 static int pts_attach(dev_info_t *, ddi_attach_cmd_t);
 179 static int pts_detach(dev_info_t *, ddi_detach_cmd_t);
 180 
 181 #define PTS_CONF_FLAG   (D_NEW | D_MP)
 182 
 183 /*
 184  * this will define (struct cb_ops cb_pts_ops) and (struct dev_ops pts_ops)
 185  */
 186 DDI_DEFINE_STREAM_OPS(pts_ops, nulldev, nulldev,        \
 187     pts_attach, pts_detach, nodev,                      \
 188     pts_devinfo, PTS_CONF_FLAG, &ptsinfo, ddi_quiesce_not_supported);
 189 
 190 /*
 191  * Module linkage information for the kernel.
 192  */
 193 
 194 static struct modldrv modldrv = {
 195         &mod_driverops, /* Type of module.  This one is a pseudo driver */
 196         "Slave Stream Pseudo Terminal driver 'pts'",
 197         &pts_ops,   /* driver ops */
 198 };
 199 
 200 static struct modlinkage modlinkage = {
 201         MODREV_1,
 202         &modldrv,
 203         NULL
 204 };
 205 
 206 int
 207 _init(void)
 208 {
 209         int rc;
 210 
 211         if ((rc = mod_install(&modlinkage)) == 0)
 212                 ptms_init();
 213         return (rc);
 214 }
 215 
 216 
 217 int


 222 
 223 int
 224 _info(struct modinfo *modinfop)
 225 {
 226         return (mod_info(&modlinkage, modinfop));
 227 }
 228 
 229 static int
 230 pts_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
 231 {
 232         if (cmd != DDI_ATTACH)
 233                 return (DDI_FAILURE);
 234 
 235         mutex_enter(&ptms_lock);
 236         pts_dip = devi;
 237         mutex_exit(&ptms_lock);
 238 
 239         return (DDI_SUCCESS);
 240 }
 241 
 242 /*ARGSUSED*/
 243 static int
 244 pts_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
 245 {
 246         if (cmd != DDI_DETACH)
 247                 return (DDI_FAILURE);
 248 
 249         /*
 250          * For now, pts cannot be detached.
 251          */
 252         return (DDI_FAILURE);
 253 }
 254 
 255 /*ARGSUSED*/
 256 static int
 257 pts_devinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
 258     void **result)
 259 {
 260         int error;
 261 
 262         switch (infocmd) {
 263         case DDI_INFO_DEVT2DEVINFO:
 264                 if (pts_dip == NULL) {
 265                         error = DDI_FAILURE;
 266                 } else {
 267                         *result = (void *)pts_dip;
 268                         error = DDI_SUCCESS;
 269                 }
 270                 break;
 271         case DDI_INFO_DEVT2INSTANCE:
 272                 *result = (void *)0;
 273                 error = DDI_SUCCESS;
 274                 break;
 275         default:
 276                 error = DDI_FAILURE;
 277         }
 278         return (error);
 279 }
 280 
 281 /* ARGSUSED */
 282 /*
 283  * Open the slave device. Reject a clone open and do not allow the
 284  * driver to be pushed. If the slave/master pair is locked or if
 285  * the master is not open, return EACCESS.
 286  * Upon success, store the write queue pointer in private data and
 287  * set the PTSOPEN bit in the pt_state field.
 288  */
 289 static int
 290 ptsopen(
 291         queue_t *rqp,           /* pointer to the read side queue */
 292         dev_t   *devp,          /* pointer to stream tail's dev */
 293         int     oflag,          /* the user open(2) supplied flags */
 294         int     sflag,          /* open state flag */
 295         cred_t  *credp)         /* credentials */
 296 {
 297         struct pt_ttys  *ptsp;
 298         mblk_t          *mp;
 299         mblk_t          *mop;   /* ptr to a setopts message block */
 300         minor_t         dminor = getminor(*devp);
 301         struct stroptions *sop;
 302 
 303         DDBG("entering ptsopen(%d)", dminor);
 304 
 305         if (sflag != 0) {


 352          * if already open, simply return...
 353          */
 354         if (ptsp->pt_state & PTSOPEN) {
 355                 ASSERT(rqp->q_ptr == ptsp);
 356                 ASSERT(ptsp->pts_rdq == rqp);
 357                 mutex_exit(&ptsp->pt_lock);
 358                 mutex_exit(&ptms_lock);
 359                 return (0);
 360         }
 361 
 362         /*
 363          * Allocate message block for setting stream head options.
 364          */
 365         if ((mop = allocb(sizeof (struct stroptions), BPRI_MED)) == NULL) {
 366                 mutex_exit(&ptsp->pt_lock);
 367                 mutex_exit(&ptms_lock);
 368                 return (ENOMEM);
 369         }
 370 
 371         /*
 372          * Slave should send zero-length message to a master when it is
 373          * closing. If memory is low at that time, master will not detect slave
 374          * closes, this pty will not be deallocated. So, preallocate this
 375          * zero-length message block early.
 376          */
 377         if ((mp = allocb(0, BPRI_MED)) == NULL) {
 378                 mutex_exit(&ptsp->pt_lock);
 379                 mutex_exit(&ptms_lock);
 380                 freemsg(mop);
 381                 return (ENOMEM);
 382         }
 383 
 384         ptsp->pt_state |= PTSOPEN;
 385 
 386         WR(rqp)->q_ptr = rqp->q_ptr = ptsp;
 387 
 388         mutex_exit(&ptsp->pt_lock);
 389         mutex_exit(&ptms_lock);
 390 
 391         if (ptsp->pt_state & PTSTTY)
 392                 STREAM(rqp)->sd_flag |= STRXPG4TTY;
 393 
 394         qprocson(rqp);
 395 
 396         /*
 397          * After qprocson pts driver is fully plumbed into the stream and can
 398          * send/receive messages. Setting pts_rdq will allow master side to send
 399          * messages to the slave. This setting can't occur before qprocson() is
 400          * finished because slave is not ready to process them.

 401          */
 402         PT_ENTER_WRITE(ptsp);
 403         ptsp->pts_rdq = rqp;
 404         ASSERT(ptsp->pt_nullmsg == NULL);
 405         ptsp->pt_nullmsg = mp;
 406         PT_EXIT_WRITE(ptsp);
 407 
 408         /*
 409          * set up hi/lo water marks on stream head read queue
 410          * and add controlling tty if not set
 411          */
 412 
 413         mop->b_datap->db_type = M_SETOPTS;
 414         mop->b_wptr += sizeof (struct stroptions);
 415         sop = (struct stroptions *)mop->b_rptr;
 416         sop->so_flags = SO_HIWAT | SO_LOWAT | SO_ISTTY;
 417         sop->so_hiwat = _TTY_BUFSIZ;
 418         sop->so_lowat = 256;
 419         putnext(rqp, mop);
 420 
 421         return (0);
 422 }
 423 
 424 /*
 425  * Find the address to private data identifying the slave's write
 426  * queue. Send a 0-length msg up the slave's read queue to designate
 427  * the master is closing. Uattach the master from the slave by nulling
 428  * out master's write queue field in private data.
 429  */
 430 /*ARGSUSED1*/
 431 static int
 432 ptsclose(queue_t *rqp, int flag, cred_t *credp)
 433 {
 434         struct pt_ttys  *ptsp;
 435         queue_t *wqp;
 436         mblk_t  *mp;
 437         mblk_t  *bp;
 438 
 439         /*
 440          * q_ptr should never be NULL in the close routine and it is checked in
 441          * DEBUG kernel by ASSERT. For non-DEBUG kernel the attempt is made to
 442          * behave gracefully.
 443          */
 444         ASSERT(rqp->q_ptr != NULL);
 445         if (rqp->q_ptr == NULL) {
 446                 qprocsoff(rqp);
 447                 return (0);
 448         }
 449 
 450         ptsp = (struct pt_ttys *)rqp->q_ptr;
 451 
 452         /*
 453          * Slave is going to close and doesn't want any new  messages coming
 454          * from the master side, so set pts_rdq to NULL. This should be done
 455          * before call to qprocsoff() since slave can't process additional
 456          * messages from the master after qprocsoff is called.
 457          */
 458         PT_ENTER_WRITE(ptsp);
 459         mp = ptsp->pt_nullmsg;
 460         ptsp->pt_nullmsg = NULL;
 461         ptsp->pts_rdq = NULL;
 462         PT_EXIT_WRITE(ptsp);
 463 
 464         /*
 465          * Drain the ouput
 466          */
 467         wqp = WR(rqp);
 468         PT_ENTER_READ(ptsp);
 469         while ((bp = getq(wqp)) != NULL) {
 470                 if (ptsp->ptm_rdq) {
 471                         putnext(ptsp->ptm_rdq, bp);
 472                 } else if (bp->b_datap->db_type == M_IOCTL) {
 473                         bp->b_datap->db_type = M_IOCNAK;
 474                         freemsg(bp->b_cont);
 475                         bp->b_cont = NULL;
 476                         qreply(wqp, bp);
 477                 } else {
 478                         freemsg(bp);
 479                 }
 480         }
 481         /*
 482          * qenable master side write queue so that it can flush
 483          * its messages as slaves's read queue is going away
 484          */
 485         if (ptsp->ptm_rdq) {
 486                 if (mp)
 487                         putnext(ptsp->ptm_rdq, mp);
 488                 else
 489                         qenable(WR(ptsp->ptm_rdq));
 490         } else
 491                 freemsg(mp);
 492         PT_EXIT_READ(ptsp);
 493 
 494         qprocsoff(rqp);
 495 
 496         rqp->q_ptr = NULL;
 497         WR(rqp)->q_ptr = NULL;
 498 
 499         ptms_close(ptsp, PTSOPEN | PTSTTY);
 500 
 501         return (0);
 502 }
 503 
 504 
 505 /*
 506  * The wput procedure will only handle flush messages.
 507  * All other messages are queued and the write side
 508  * service procedure sends them off to the master side.
 509  */
 510 static int
 511 ptswput(queue_t *qp, mblk_t *mp)
 512 {
 513         struct pt_ttys *ptsp;
 514         struct iocblk  *iocp;
 515         unsigned char type = mp->b_datap->db_type;
 516 
 517         DBG(("entering ptswput\n"));
 518         ASSERT(qp->q_ptr);
 519 
 520         ptsp = (struct pt_ttys *)qp->q_ptr;
 521         PT_ENTER_READ(ptsp);
 522         if (ptsp->ptm_rdq == NULL) {
 523                 DBG(("in write put proc but no master\n"));
 524                 /*
 525                  * NAK ioctl as slave side read queue is gone.
 526                  * Or else free the message.
 527                  */
 528                 if (mp->b_datap->db_type == M_IOCTL) {
 529                         mp->b_datap->db_type = M_IOCNAK;
 530                         freemsg(mp->b_cont);
 531                         mp->b_cont = NULL;
 532                         qreply(qp, mp);
 533                 } else
 534                         freemsg(mp);
 535                 PT_EXIT_READ(ptsp);
 536                 return (0);
 537         }
 538 
 539         if (type >= QPCTL) {
 540                 switch (type) {
 541 
 542                 /*
 543                  * if write queue request, flush slave's write
 544                  * queue and send FLUSHR to ptm. If read queue
 545                  * request, send FLUSHR to ptm.
 546                  */
 547                 case M_FLUSH:
 548                 DBG(("pts got flush request\n"));
 549                 if (*mp->b_rptr & FLUSHW) {
 550 
 551                         DBG(("got FLUSHW, flush pts write Q\n"));
 552                         if (*mp->b_rptr & FLUSHBAND)
 553                                 /*
 554                                  * if it is a FLUSHBAND, do flushband.
 555                                  */
 556                                 flushband(qp, *(mp->b_rptr + 1), FLUSHDATA);
 557                         else
 558                                 flushq(qp, FLUSHDATA);
 559 
 560                         *mp->b_rptr &= ~FLUSHW;
 561                         if ((*mp->b_rptr & FLUSHR) == 0) {
 562                                 /*
 563                                  * FLUSHW only. Change to FLUSHR and putnext


 568                                         putnext(ptsp->ptm_rdq, mp);
 569                                 break;
 570                         } else {
 571                                 mblk_t *nmp;
 572 
 573                                 /* It is a FLUSHRW. Duplicate the mblk */
 574                                 nmp = copyb(mp);
 575                                 if (nmp) {
 576                                         /*
 577                                          * Change FLUSHW to FLUSHR before
 578                                          * putnext to ptm.
 579                                          */
 580                                         DBG(("putnext nmp(FLUSHR) to ptm\n"));
 581                                         *nmp->b_rptr |= FLUSHR;
 582                                         if (ptsp->ptm_rdq)
 583                                                 putnext(ptsp->ptm_rdq, nmp);
 584                                 }
 585                         }
 586                 }
 587                 /*
 588                  * Since the packet module will toss any
 589                  * M_FLUSHES sent to the master's stream head
 590                  * read queue, we simply turn it around here.
 591                  */
 592                 if (*mp->b_rptr & FLUSHR) {
 593                         ASSERT(RD(qp)->q_first == NULL);
 594                         DBG(("qreply(qp) turning FLUSHR around\n"));
 595                         qreply(qp, mp);
 596                 } else {
 597                         freemsg(mp);
 598                 }
 599                 break;
 600 
 601                 case M_READ:
 602                 /* Caused by ldterm - can not pass to master */
 603                 freemsg(mp);
 604                 break;
 605 
 606                 default:
 607                 if (ptsp->ptm_rdq)
 608                         putnext(ptsp->ptm_rdq, mp);
 609                 break;
 610                 }
 611                 PT_EXIT_READ(ptsp);
 612                 return (0);
 613         }
 614 
 615         switch (type) {
 616 
 617         case M_IOCTL:
 618                 /*
 619                  * For case PTSSTTY set the flag PTSTTY and ACK
 620                  * the ioctl so that the user program can push
 621                  * the associated modules to get tty semantics.
 622                  * See bugid 4025044


 628 
 629                 case PTSSTTY:
 630                         if (ptsp->pt_state & PTSTTY) {
 631                                 mp->b_datap->db_type = M_IOCNAK;
 632                                 iocp->ioc_error = EEXIST;
 633                         } else {
 634                                 mp->b_datap->db_type = M_IOCACK;
 635                                 mutex_enter(&ptsp->pt_lock);
 636                                 ptsp->pt_state |= PTSTTY;
 637                                 mutex_exit(&ptsp->pt_lock);
 638                                 iocp->ioc_error = 0;
 639                         }
 640                         iocp->ioc_count = 0;
 641                         qreply(qp, mp);
 642                         PT_EXIT_READ(ptsp);
 643                         return (0);
 644                 }
 645                 /* FALLTHROUGH */
 646         default:
 647                 /*
 648                  * send other messages to the master
 649                  */
 650                 DBG(("put msg on slave's write queue\n"));
 651                 (void) putq(qp, mp);
 652                 break;
 653         }
 654 
 655         PT_EXIT_READ(ptsp);
 656         DBG(("return from ptswput()\n"));
 657         return (0);
 658 }
 659 
 660 
 661 /*
 662  * enable the write side of the master. This triggers the
 663  * master to send any messages queued on its write side to
 664  * the read side of this slave.
 665  */
 666 static int
 667 ptsrsrv(queue_t *qp)
 668 {
 669         struct pt_ttys *ptsp;
 670 
 671         DBG(("entering ptsrsrv\n"));
 672         ASSERT(qp->q_ptr);
 673 
 674         ptsp = (struct pt_ttys *)qp->q_ptr;
 675         PT_ENTER_READ(ptsp);
 676         if (ptsp->ptm_rdq == NULL) {
 677                 DBG(("in read srv proc but no master\n"));
 678                 PT_EXIT_READ(ptsp);
 679                 return (0);
 680         }
 681         qenable(WR(ptsp->ptm_rdq));
 682         PT_EXIT_READ(ptsp);
 683         DBG(("leaving ptsrsrv\n"));
 684         return (0);
 685 }
 686 
 687 /*
 688  * If there are messages on this queue that can be sent to
 689  * master, send them via putnext(). Else, if queued messages
 690  * cannot be sent, leave them on this queue. If priority
 691  * messages on this queue, send them to master no matter what.
 692  */
 693 static int
 694 ptswsrv(queue_t *qp)
 695 {
 696         struct pt_ttys *ptsp;
 697         queue_t *ptm_rdq;
 698         mblk_t *mp;
 699 
 700         DBG(("entering ptswsrv\n"));
 701         ASSERT(qp->q_ptr);
 702 
 703         ptsp = (struct pt_ttys *)qp->q_ptr;
 704         PT_ENTER_READ(ptsp);
 705         if (ptsp->ptm_rdq == NULL) {
 706                 DBG(("in write srv proc but no master\n"));
 707                 /*
 708                  * Free messages on the write queue and send
 709                  * NAK for any M_IOCTL type messages to wakeup
 710                  * the user process waiting for ACK/NAK from
 711                  * the ioctl invocation
 712                  */
 713                 while ((mp = getq(qp)) != NULL) {
 714                         if (mp->b_datap->db_type == M_IOCTL) {
 715                                 mp->b_datap->db_type = M_IOCNAK;
 716                                 freemsg(mp->b_cont);
 717                                 mp->b_cont = NULL;
 718                                 qreply(qp, mp);
 719                         } else
 720                                 freemsg(mp);
 721                 }
 722                 PT_EXIT_READ(ptsp);
 723                 return (0);
 724         } else {
 725                 ptm_rdq = ptsp->ptm_rdq;
 726         }
 727 
 728         /*
 729          * while there are messages on this write queue...
 730          */
 731         while ((mp = getq(qp)) != NULL) {
 732                 /*
 733                  * if don't have control message and cannot put
 734                  * msg. on master's read queue, put it back on
 735                  * this queue.
 736                  */
 737                 if (mp->b_datap->db_type <= QPCTL &&
 738                     !bcanputnext(ptm_rdq, mp->b_band)) {
 739                         DBG(("put msg. back on Q\n"));
 740                         (void) putbq(qp, mp);
 741                         break;
 742                 }
 743                 /*
 744                  * else send the message up master's stream
 745                  */
 746                 DBG(("send message to master\n"));
 747                 putnext(ptm_rdq, mp);
 748         }
 749         DBG(("leaving ptswsrv\n"));
 750         PT_EXIT_READ(ptsp);
 751         return (0);
 752 }


  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 /*      Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T     */
  26 /*        All Rights Reserved   */
  27 
  28 /*
  29  * Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
  30  * Copyright 2021 Oxide Computer Company
  31  */
  32 
  33 /*
  34  * PSEUDO-TERMINAL SUBSIDIARY DRIVER (PTS)
  35  *
  36  * The pseudo-terminal subsystem simulates a terminal connection, where the
  37  * manager side represents the terminal and the subsidiary represents the user
  38  * process's special device end point.  The manager device is set up as a
  39  * cloned device where its major device number is the major for the clone
  40  * device and its minor device number is the major for the ptm driver.  There
  41  * are no nodes in the file system for manager devices.  The manager pseudo
  42  * driver is opened using the open(2) system call with /dev/ptmx as the device
  43  * parameter.  The clone open finds the next available minor device for the ptm
  44  * major device.
  45  *
  46  * A manager device is available only if it and its corresponding subsidiary
  47  * device are not already open.  When the manager device is opened, the
  48  * corresponding subsidiary device is automatically locked out.  Only one open
  49  * is allowed on a manager device.  Multiple opens are allowed on the
  50  * subsidiary device.  After both the manager and subsidiary have been opened,
  51  * the user has two file descriptors which are the end points of a full duplex
  52  * connection composed of two streams which are automatically connected at the
  53  * manager and subsidiary drivers.  The user may then push modules onto either
  54  * side of the stream pair.
  55  *
  56  * The manager and subsidiary drivers pass all messages to their adjacent
  57  * queues.  Only the M_FLUSH needs some processing.  Because the read queue of
  58  * one side is connected to the write queue of the other, the FLUSHR flag is
  59  * changed to the FLUSHW flag and vice versa.  When the manager device is
  60  * closed an M_HANGUP message is sent to the subsidiary device which will
  61  * render the device unusable.  The process on the subsidiary side gets the EIO
  62  * when attempting to write on that stream but it will be able to read any data
  63  * remaining on the stream head read queue.  When all the data has been read,
  64  * read() returns 0 indicating that the stream can no longer be used.  On the
  65  * last close of the subsidiary device, a 0-length message is sent to the
  66  * manager device.  When the application on the manager side issues a read() or
  67  * getmsg() and 0 is returned, the user of the manager device decides whether
  68  * to issue a close() that dismantles the pseudo-terminal subsystem.  If the
  69  * manager device is not closed, the pseudo-tty subsystem will be available to
  70  * another user to open the subsidiary device.
  71  *

  72  *
  73  * SYNCHRONIZATION


  74  *
  75  * All global data synchronization between ptm/pts is done via global ptms_lock
  76  * mutex which is initialized at system boot time from ptms_initspace (called
  77  * from space.c).
  78  *
  79  * Individual fields of pt_ttys structure (except ptm_rdq, pts_rdq and
  80  * pt_nullmsg) are protected by pt_ttys.pt_lock mutex.
  81  *
  82  * PT_ENTER_READ/PT_ENTER_WRITE are reference counter based read-write locks
  83  * which allow reader locks to be reacquired by the same thread (usual
  84  * reader/writer locks can't be used for that purpose since it is illegal for a
  85  * thread to acquire a lock it already holds, even as a reader).  The sole
  86  * purpose of these macros is to guarantee that the peer queue will not
  87  * disappear (due to closing peer) while it is used.  It is safe to use
  88  * PT_ENTER_READ/PT_EXIT_READ brackets across calls like putq/putnext (since
  89  * they are not real locks but reference counts).
  90  *
  91  * PT_ENTER_WRITE/PT_EXIT_WRITE brackets are used ONLY in manager/subsidiary
  92  * open/close paths to modify ptm_rdq and pts_rdq fields.  These fields should
  93  * be set to appropriate queues *after* qprocson() is called during open (to
  94  * prevent peer from accessing the queue with incomplete plumbing) and set to
  95  * NULL before qprocsoff() is called during close.
  96  *
  97  * The pt_nullmsg field is only used in open/close routines and it is also
  98  * protected by PT_ENTER_WRITE/PT_EXIT_WRITE brackets to avoid extra mutex
  99  * holds.
 100  *

 101  *
 102  * LOCK ORDERING
 103  *
 104  * If both ptms_lock and per-pty lock should be held, ptms_lock should always
 105  * be entered first, followed by per-pty lock.
 106  *
 107  * See ptms.h, ptm.c and ptms_conf.c fore more information.

 108  */
 109 
 110 #include <sys/types.h>
 111 #include <sys/param.h>
 112 #include <sys/sysmacros.h>
 113 #include <sys/stream.h>
 114 #include <sys/stropts.h>
 115 #include <sys/strsubr.h>
 116 #include <sys/stat.h>
 117 #include <sys/errno.h>
 118 #include <sys/debug.h>
 119 #include <sys/cmn_err.h>
 120 #include <sys/ptms.h>
 121 #include <sys/systm.h>
 122 #include <sys/modctl.h>
 123 #include <sys/conf.h>
 124 #include <sys/ddi.h>
 125 #include <sys/sunddi.h>
 126 #include <sys/cred.h>
 127 #include <sys/zone.h>
 128 
 129 #ifdef DEBUG
 130 int pts_debug = 0;
 131 #define DBG(a)   if (pts_debug) cmn_err(CE_NOTE, a)
 132 #else
 133 #define DBG(a)
 134 #endif
 135 
 136 static int ptsopen(queue_t *, dev_t *, int, int, cred_t *);
 137 static int ptsclose(queue_t *, int, cred_t *);
 138 static int ptswput(queue_t *, mblk_t *);
 139 static int ptsrsrv(queue_t *);
 140 static int ptswsrv(queue_t *);
 141 



 142 static struct module_info pts_info = {
 143         0xface,
 144         "pts",
 145         0,
 146         _TTY_BUFSIZ,
 147         _TTY_BUFSIZ,
 148         128
 149 };
 150 
 151 static struct qinit ptsrint = {
 152         NULL,
 153         ptsrsrv,
 154         ptsopen,
 155         ptsclose,
 156         NULL,
 157         &pts_info,
 158         NULL
 159 };
 160 
 161 static struct qinit ptswint = {


 176 };
 177 
 178 static int pts_devinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
 179 static int pts_attach(dev_info_t *, ddi_attach_cmd_t);
 180 static int pts_detach(dev_info_t *, ddi_detach_cmd_t);
 181 
 182 #define PTS_CONF_FLAG   (D_NEW | D_MP)
 183 
 184 /*
 185  * this will define (struct cb_ops cb_pts_ops) and (struct dev_ops pts_ops)
 186  */
 187 DDI_DEFINE_STREAM_OPS(pts_ops, nulldev, nulldev,        \
 188     pts_attach, pts_detach, nodev,                      \
 189     pts_devinfo, PTS_CONF_FLAG, &ptsinfo, ddi_quiesce_not_supported);
 190 
 191 /*
 192  * Module linkage information for the kernel.
 193  */
 194 
 195 static struct modldrv modldrv = {
 196         &mod_driverops,
 197         "Pseudo-Terminal Subsidiary Driver",
 198         &pts_ops,
 199 };
 200 
 201 static struct modlinkage modlinkage = {
 202         MODREV_1,
 203         &modldrv,
 204         NULL
 205 };
 206 
 207 int
 208 _init(void)
 209 {
 210         int rc;
 211 
 212         if ((rc = mod_install(&modlinkage)) == 0)
 213                 ptms_init();
 214         return (rc);
 215 }
 216 
 217 
 218 int


 223 
 224 int
 225 _info(struct modinfo *modinfop)
 226 {
 227         return (mod_info(&modlinkage, modinfop));
 228 }
 229 
 230 static int
 231 pts_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
 232 {
 233         if (cmd != DDI_ATTACH)
 234                 return (DDI_FAILURE);
 235 
 236         mutex_enter(&ptms_lock);
 237         pts_dip = devi;
 238         mutex_exit(&ptms_lock);
 239 
 240         return (DDI_SUCCESS);
 241 }
 242 

 243 static int
 244 pts_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
 245 {
 246         if (cmd != DDI_DETACH)
 247                 return (DDI_FAILURE);
 248 
 249         /*
 250          * For now, pts cannot be detached.
 251          */
 252         return (DDI_FAILURE);
 253 }
 254 

 255 static int
 256 pts_devinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
 257     void **result)
 258 {
 259         int error;
 260 
 261         switch (infocmd) {
 262         case DDI_INFO_DEVT2DEVINFO:
 263                 if (pts_dip == NULL) {
 264                         error = DDI_FAILURE;
 265                 } else {
 266                         *result = (void *)pts_dip;
 267                         error = DDI_SUCCESS;
 268                 }
 269                 break;
 270         case DDI_INFO_DEVT2INSTANCE:
 271                 *result = (void *)0;
 272                 error = DDI_SUCCESS;
 273                 break;
 274         default:
 275                 error = DDI_FAILURE;
 276         }
 277         return (error);
 278 }
 279 
 280 /* ARGSUSED */
 281 /*
 282  * Open the subsidiary device. Reject a clone open and do not allow the
 283  * driver to be pushed. If the subsidiary/manager pair is locked or if
 284  * the manager is not open, return EACCESS.
 285  * Upon success, store the write queue pointer in private data and
 286  * set the PTSOPEN bit in the pt_state field.
 287  */
 288 static int
 289 ptsopen(
 290         queue_t *rqp,           /* pointer to the read side queue */
 291         dev_t   *devp,          /* pointer to stream tail's dev */
 292         int     oflag,          /* the user open(2) supplied flags */
 293         int     sflag,          /* open state flag */
 294         cred_t  *credp)         /* credentials */
 295 {
 296         struct pt_ttys  *ptsp;
 297         mblk_t          *mp;
 298         mblk_t          *mop;   /* ptr to a setopts message block */
 299         minor_t         dminor = getminor(*devp);
 300         struct stroptions *sop;
 301 
 302         DDBG("entering ptsopen(%d)", dminor);
 303 
 304         if (sflag != 0) {


 351          * if already open, simply return...
 352          */
 353         if (ptsp->pt_state & PTSOPEN) {
 354                 ASSERT(rqp->q_ptr == ptsp);
 355                 ASSERT(ptsp->pts_rdq == rqp);
 356                 mutex_exit(&ptsp->pt_lock);
 357                 mutex_exit(&ptms_lock);
 358                 return (0);
 359         }
 360 
 361         /*
 362          * Allocate message block for setting stream head options.
 363          */
 364         if ((mop = allocb(sizeof (struct stroptions), BPRI_MED)) == NULL) {
 365                 mutex_exit(&ptsp->pt_lock);
 366                 mutex_exit(&ptms_lock);
 367                 return (ENOMEM);
 368         }
 369 
 370         /*
 371          * Subsidiary should send zero-length message to a manager when it is
 372          * closing.  If memory is low at that time, manager will not detect
 373          * subsidiary closes, this pty will not be deallocated.  So,
 374          * preallocate this zero-length message block early.
 375          */
 376         if ((mp = allocb(0, BPRI_MED)) == NULL) {
 377                 mutex_exit(&ptsp->pt_lock);
 378                 mutex_exit(&ptms_lock);
 379                 freemsg(mop);
 380                 return (ENOMEM);
 381         }
 382 
 383         ptsp->pt_state |= PTSOPEN;
 384 
 385         WR(rqp)->q_ptr = rqp->q_ptr = ptsp;
 386 
 387         mutex_exit(&ptsp->pt_lock);
 388         mutex_exit(&ptms_lock);
 389 
 390         if (ptsp->pt_state & PTSTTY)
 391                 STREAM(rqp)->sd_flag |= STRXPG4TTY;
 392 
 393         qprocson(rqp);
 394 
 395         /*
 396          * After qprocson pts driver is fully plumbed into the stream and can
 397          * send/receive messages.  Setting pts_rdq will allow manager side to
 398          * send messages to the subsidiary.  This setting can't occur before
 399          * qprocson() is finished because subsidiary is not ready to process
 400          * them.
 401          */
 402         PT_ENTER_WRITE(ptsp);
 403         ptsp->pts_rdq = rqp;
 404         ASSERT(ptsp->pt_nullmsg == NULL);
 405         ptsp->pt_nullmsg = mp;
 406         PT_EXIT_WRITE(ptsp);
 407 
 408         /*
 409          * set up hi/lo water marks on stream head read queue
 410          * and add controlling tty if not set
 411          */
 412 
 413         mop->b_datap->db_type = M_SETOPTS;
 414         mop->b_wptr += sizeof (struct stroptions);
 415         sop = (struct stroptions *)mop->b_rptr;
 416         sop->so_flags = SO_HIWAT | SO_LOWAT | SO_ISTTY;
 417         sop->so_hiwat = _TTY_BUFSIZ;
 418         sop->so_lowat = 256;
 419         putnext(rqp, mop);
 420 
 421         return (0);
 422 }
 423 
 424 /*
 425  * Find the address to private data identifying the subsidiary's write queue.
 426  * Send a 0-length msg up the subsidiary's read queue to designate the manager
 427  * is closing.  Uattach the manager from the subsidiary by nulling out
 428  * manager's write queue field in private data.
 429  */

 430 static int
 431 ptsclose(queue_t *rqp, int flag, cred_t *credp)
 432 {
 433         struct pt_ttys  *ptsp;
 434         queue_t *wqp;
 435         mblk_t  *mp;
 436         mblk_t  *bp;
 437 
 438         /*
 439          * q_ptr should never be NULL in the close routine and it is checked in
 440          * DEBUG kernel by ASSERT. For non-DEBUG kernel the attempt is made to
 441          * behave gracefully.
 442          */
 443         ASSERT(rqp->q_ptr != NULL);
 444         if (rqp->q_ptr == NULL) {
 445                 qprocsoff(rqp);
 446                 return (0);
 447         }
 448 
 449         ptsp = (struct pt_ttys *)rqp->q_ptr;
 450 
 451         /*
 452          * Subsidiary is going to close and doesn't want any new messages
 453          * coming from the manager side, so set pts_rdq to NULL.  This should
 454          * be done before call to qprocsoff() since subsidiary can't process
 455          * additional messages from the manager after qprocsoff is called.
 456          */
 457         PT_ENTER_WRITE(ptsp);
 458         mp = ptsp->pt_nullmsg;
 459         ptsp->pt_nullmsg = NULL;
 460         ptsp->pts_rdq = NULL;
 461         PT_EXIT_WRITE(ptsp);
 462 
 463         /*
 464          * Drain the ouput
 465          */
 466         wqp = WR(rqp);
 467         PT_ENTER_READ(ptsp);
 468         while ((bp = getq(wqp)) != NULL) {
 469                 if (ptsp->ptm_rdq) {
 470                         putnext(ptsp->ptm_rdq, bp);
 471                 } else if (bp->b_datap->db_type == M_IOCTL) {
 472                         bp->b_datap->db_type = M_IOCNAK;
 473                         freemsg(bp->b_cont);
 474                         bp->b_cont = NULL;
 475                         qreply(wqp, bp);
 476                 } else {
 477                         freemsg(bp);
 478                 }
 479         }
 480         /*
 481          * qenable manager side write queue so that it can flush its messages
 482          * as subsidiarys's read queue is going away:
 483          */
 484         if (ptsp->ptm_rdq) {
 485                 if (mp)
 486                         putnext(ptsp->ptm_rdq, mp);
 487                 else
 488                         qenable(WR(ptsp->ptm_rdq));
 489         } else
 490                 freemsg(mp);
 491         PT_EXIT_READ(ptsp);
 492 
 493         qprocsoff(rqp);
 494 
 495         rqp->q_ptr = NULL;
 496         WR(rqp)->q_ptr = NULL;
 497 
 498         ptms_close(ptsp, PTSOPEN | PTSTTY);
 499 
 500         return (0);
 501 }
 502 
 503 
 504 /*
 505  * The wput procedure will only handle flush messages.  All other messages are
 506  * queued and the write side service procedure sends them off to the manager
 507  * side.
 508  */
 509 static int
 510 ptswput(queue_t *qp, mblk_t *mp)
 511 {
 512         struct pt_ttys *ptsp;
 513         struct iocblk  *iocp;
 514         unsigned char type = mp->b_datap->db_type;
 515 
 516         DBG(("entering ptswput\n"));
 517         ASSERT(qp->q_ptr);
 518 
 519         ptsp = (struct pt_ttys *)qp->q_ptr;
 520         PT_ENTER_READ(ptsp);
 521         if (ptsp->ptm_rdq == NULL) {
 522                 DBG(("in write put proc but no manager\n"));
 523                 /*
 524                  * NAK ioctl as subsidiary side read queue is gone.
 525                  * Or else free the message.
 526                  */
 527                 if (mp->b_datap->db_type == M_IOCTL) {
 528                         mp->b_datap->db_type = M_IOCNAK;
 529                         freemsg(mp->b_cont);
 530                         mp->b_cont = NULL;
 531                         qreply(qp, mp);
 532                 } else
 533                         freemsg(mp);
 534                 PT_EXIT_READ(ptsp);
 535                 return (0);
 536         }
 537 
 538         if (type >= QPCTL) {
 539                 switch (type) {
 540 
 541                 /*
 542                  * if write queue request, flush subsidiary's write
 543                  * queue and send FLUSHR to ptm. If read queue
 544                  * request, send FLUSHR to ptm.
 545                  */
 546                 case M_FLUSH:
 547                 DBG(("pts got flush request\n"));
 548                 if (*mp->b_rptr & FLUSHW) {
 549 
 550                         DBG(("got FLUSHW, flush pts write Q\n"));
 551                         if (*mp->b_rptr & FLUSHBAND)
 552                                 /*
 553                                  * if it is a FLUSHBAND, do flushband.
 554                                  */
 555                                 flushband(qp, *(mp->b_rptr + 1), FLUSHDATA);
 556                         else
 557                                 flushq(qp, FLUSHDATA);
 558 
 559                         *mp->b_rptr &= ~FLUSHW;
 560                         if ((*mp->b_rptr & FLUSHR) == 0) {
 561                                 /*
 562                                  * FLUSHW only. Change to FLUSHR and putnext


 567                                         putnext(ptsp->ptm_rdq, mp);
 568                                 break;
 569                         } else {
 570                                 mblk_t *nmp;
 571 
 572                                 /* It is a FLUSHRW. Duplicate the mblk */
 573                                 nmp = copyb(mp);
 574                                 if (nmp) {
 575                                         /*
 576                                          * Change FLUSHW to FLUSHR before
 577                                          * putnext to ptm.
 578                                          */
 579                                         DBG(("putnext nmp(FLUSHR) to ptm\n"));
 580                                         *nmp->b_rptr |= FLUSHR;
 581                                         if (ptsp->ptm_rdq)
 582                                                 putnext(ptsp->ptm_rdq, nmp);
 583                                 }
 584                         }
 585                 }
 586                 /*
 587                  * Since the packet module will toss any M_FLUSHES sent to the
 588                  * manager's stream head read queue, we simply turn it around
 589                  * here.
 590                  */
 591                 if (*mp->b_rptr & FLUSHR) {
 592                         ASSERT(RD(qp)->q_first == NULL);
 593                         DBG(("qreply(qp) turning FLUSHR around\n"));
 594                         qreply(qp, mp);
 595                 } else {
 596                         freemsg(mp);
 597                 }
 598                 break;
 599 
 600                 case M_READ:
 601                 /* Caused by ldterm - can not pass to manager */
 602                 freemsg(mp);
 603                 break;
 604 
 605                 default:
 606                 if (ptsp->ptm_rdq)
 607                         putnext(ptsp->ptm_rdq, mp);
 608                 break;
 609                 }
 610                 PT_EXIT_READ(ptsp);
 611                 return (0);
 612         }
 613 
 614         switch (type) {
 615 
 616         case M_IOCTL:
 617                 /*
 618                  * For case PTSSTTY set the flag PTSTTY and ACK
 619                  * the ioctl so that the user program can push
 620                  * the associated modules to get tty semantics.
 621                  * See bugid 4025044


 627 
 628                 case PTSSTTY:
 629                         if (ptsp->pt_state & PTSTTY) {
 630                                 mp->b_datap->db_type = M_IOCNAK;
 631                                 iocp->ioc_error = EEXIST;
 632                         } else {
 633                                 mp->b_datap->db_type = M_IOCACK;
 634                                 mutex_enter(&ptsp->pt_lock);
 635                                 ptsp->pt_state |= PTSTTY;
 636                                 mutex_exit(&ptsp->pt_lock);
 637                                 iocp->ioc_error = 0;
 638                         }
 639                         iocp->ioc_count = 0;
 640                         qreply(qp, mp);
 641                         PT_EXIT_READ(ptsp);
 642                         return (0);
 643                 }
 644                 /* FALLTHROUGH */
 645         default:
 646                 /*
 647                  * send other messages to the manager
 648                  */
 649                 DBG(("put msg on subsidiary's write queue\n"));
 650                 (void) putq(qp, mp);
 651                 break;
 652         }
 653 
 654         PT_EXIT_READ(ptsp);
 655         DBG(("return from ptswput()\n"));
 656         return (0);
 657 }
 658 
 659 
 660 /*
 661  * Enable the write side of the manager.  This triggers the manager to send any
 662  * messages queued on its write side to the read side of this subsidiary.

 663  */
 664 static int
 665 ptsrsrv(queue_t *qp)
 666 {
 667         struct pt_ttys *ptsp;
 668 
 669         DBG(("entering ptsrsrv\n"));
 670         ASSERT(qp->q_ptr);
 671 
 672         ptsp = (struct pt_ttys *)qp->q_ptr;
 673         PT_ENTER_READ(ptsp);
 674         if (ptsp->ptm_rdq == NULL) {
 675                 DBG(("in read srv proc but no manager\n"));
 676                 PT_EXIT_READ(ptsp);
 677                 return (0);
 678         }
 679         qenable(WR(ptsp->ptm_rdq));
 680         PT_EXIT_READ(ptsp);
 681         DBG(("leaving ptsrsrv\n"));
 682         return (0);
 683 }
 684 
 685 /*
 686  * If there are messages on this queue that can be sent to manager, send them
 687  * via putnext().  Otherwise, if queued messages cannot be sent, leave them on
 688  * this queue.  If priority messages on this queue, send them to manager no
 689  * matter what.
 690  */
 691 static int
 692 ptswsrv(queue_t *qp)
 693 {
 694         struct pt_ttys *ptsp;
 695         queue_t *ptm_rdq;
 696         mblk_t *mp;
 697 
 698         DBG(("entering ptswsrv\n"));
 699         ASSERT(qp->q_ptr);
 700 
 701         ptsp = (struct pt_ttys *)qp->q_ptr;
 702         PT_ENTER_READ(ptsp);
 703         if (ptsp->ptm_rdq == NULL) {
 704                 DBG(("in write srv proc but no manager\n"));
 705                 /*
 706                  * Free messages on the write queue and send NAK for any
 707                  * M_IOCTL type messages to wakeup the user process waiting for
 708                  * ACK/NAK from the ioctl invocation

 709                  */
 710                 while ((mp = getq(qp)) != NULL) {
 711                         if (mp->b_datap->db_type == M_IOCTL) {
 712                                 mp->b_datap->db_type = M_IOCNAK;
 713                                 freemsg(mp->b_cont);
 714                                 mp->b_cont = NULL;
 715                                 qreply(qp, mp);
 716                         } else
 717                                 freemsg(mp);
 718                 }
 719                 PT_EXIT_READ(ptsp);
 720                 return (0);
 721         } else {
 722                 ptm_rdq = ptsp->ptm_rdq;
 723         }
 724 
 725         /*
 726          * While there are messages on this write queue...
 727          */
 728         while ((mp = getq(qp)) != NULL) {
 729                 /*
 730                  * If this is not a control message and we cannot put messages
 731                  * on the manager's read queue, put it back on this queue.

 732                  */
 733                 if (mp->b_datap->db_type <= QPCTL &&
 734                     !bcanputnext(ptm_rdq, mp->b_band)) {
 735                         DBG(("put msg. back on Q\n"));
 736                         (void) putbq(qp, mp);
 737                         break;
 738                 }
 739                 /*
 740                  * Otherwise, send the message up manager's stream:
 741                  */
 742                 DBG(("send message to manager\n"));
 743                 putnext(ptm_rdq, mp);
 744         }
 745         DBG(("leaving ptswsrv\n"));
 746         PT_EXIT_READ(ptsp);
 747         return (0);
 748 }