1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * Zone Console Driver. 31 * 32 * This driver, derived from the pts/ptm drivers, is the pseudo console driver 33 * for system zones. Its implementation is straightforward. Each instance 34 * of the driver represents a global-zone/local-zone pair (this maps in a 35 * straightforward way to the commonly used terminal notion of "master side" 36 * and "slave side", and we use that terminology throughout). 37 * 38 * Instances of zcons are onlined as children of /pseudo/zconsnex@1/ 39 * by zoneadmd in userland, using the devctl framework; thus the driver 40 * does not need to maintain any sort of "admin" node. 41 * 42 * The driver shuttles I/O from master side to slave side and back. In a break 43 * from the pts/ptm semantics, if one side is not open, I/O directed towards 44 * it will simply be discarded. This is so that if zoneadmd is not holding 45 * the master side console open (i.e. it has died somehow), processes in 46 * the zone do not experience any errors and I/O to the console does not 47 * hang. 48 * 49 * TODO: we may want to revisit the other direction; i.e. we may want 50 * zoneadmd to be able to detect whether no zone processes are holding the 51 * console open, an unusual situation. 52 */ 53 54 #include <sys/types.h> 55 #include <sys/cmn_err.h> 56 #include <sys/conf.h> 57 #include <sys/cred.h> 58 #include <sys/ddi.h> 59 #include <sys/debug.h> 60 #include <sys/devops.h> 61 #include <sys/errno.h> 62 #include <sys/file.h> 63 #include <sys/modctl.h> 64 #include <sys/param.h> 65 #include <sys/stat.h> 66 #include <sys/stream.h> 67 #include <sys/stropts.h> 68 #include <sys/strsun.h> 69 #include <sys/sunddi.h> 70 #include <sys/sysmacros.h> 71 #include <sys/systm.h> 72 #include <sys/types.h> 73 #include <sys/zcons.h> 74 75 static int zc_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); 76 static int zc_attach(dev_info_t *, ddi_attach_cmd_t); 77 static int zc_detach(dev_info_t *, ddi_detach_cmd_t); 78 79 static int zc_open(queue_t *, dev_t *, int, int, cred_t *); 80 static int zc_close(queue_t *, int, cred_t *); 81 static void zc_wput(queue_t *, mblk_t *); 82 static void zc_rsrv(queue_t *); 83 static void zc_wsrv(queue_t *); 84 85 /* 86 * The instance number is encoded in the dev_t in the minor number; the lowest 87 * bit of the minor number is used to track the master vs. slave side of the 88 * virtual console. The rest of the bits in the minor number are the instance. 89 */ 90 #define ZC_MASTER_MINOR 0 91 #define ZC_SLAVE_MINOR 1 92 93 #define ZC_INSTANCE(x) (getminor((x)) >> 1) 94 #define ZC_NODE(x) (getminor((x)) & 0x01) 95 96 int zcons_debug = 0; 97 #define DBG(a) if (zcons_debug) cmn_err(CE_NOTE, a) 98 #define DBG1(a, b) if (zcons_debug) cmn_err(CE_NOTE, a, b) 99 100 101 /* 102 * Zone Console Pseudo Terminal Module: stream data structure definitions 103 */ 104 static struct module_info zc_info = { 105 31337, /* c0z we r hAx0rs */ 106 "zcons", 107 0, 108 INFPSZ, 109 2048, 110 128 111 }; 112 113 static struct qinit zc_rinit = { 114 NULL, 115 (int (*)()) zc_rsrv, 116 zc_open, 117 zc_close, 118 NULL, 119 &zc_info, 120 NULL 121 }; 122 123 static struct qinit zc_winit = { 124 (int (*)()) zc_wput, 125 (int (*)()) zc_wsrv, 126 NULL, 127 NULL, 128 NULL, 129 &zc_info, 130 NULL 131 }; 132 133 static struct streamtab zc_tab_info = { 134 &zc_rinit, 135 &zc_winit, 136 NULL, 137 NULL 138 }; 139 140 #define ZC_CONF_FLAG (D_MP | D_MTQPAIR | D_MTOUTPERIM | D_MTOCEXCL) 141 142 /* 143 * this will define (struct cb_ops cb_zc_ops) and (struct dev_ops zc_ops) 144 */ 145 DDI_DEFINE_STREAM_OPS(zc_ops, nulldev, nulldev, zc_attach, zc_detach, nodev, \ 146 zc_getinfo, ZC_CONF_FLAG, &zc_tab_info); 147 148 /* 149 * Module linkage information for the kernel. 150 */ 151 152 static struct modldrv modldrv = { 153 &mod_driverops, /* Type of module. This one is a pseudo driver */ 154 "Zone console driver 'zcons' %I%", 155 &zc_ops /* driver ops */ 156 }; 157 158 static struct modlinkage modlinkage = { 159 MODREV_1, 160 &modldrv, 161 NULL 162 }; 163 164 typedef struct zc_state { 165 dev_info_t *zc_devinfo; 166 queue_t *zc_master_rdq; 167 queue_t *zc_slave_rdq; 168 int zc_state; 169 } zc_state_t; 170 171 #define ZC_STATE_MOPEN 0x01 172 #define ZC_STATE_SOPEN 0x02 173 174 static void *zc_soft_state; 175 176 int 177 _init(void) 178 { 179 int err; 180 181 if ((err = ddi_soft_state_init(&zc_soft_state, 182 sizeof (zc_state_t), 0)) != 0) { 183 return (err); 184 } 185 186 if ((err = mod_install(&modlinkage)) != 0) 187 ddi_soft_state_fini(zc_soft_state); 188 189 return (err); 190 } 191 192 193 int 194 _fini(void) 195 { 196 int err; 197 198 if ((err = mod_remove(&modlinkage)) != 0) { 199 return (err); 200 } 201 202 ddi_soft_state_fini(&zc_soft_state); 203 return (0); 204 } 205 206 int 207 _info(struct modinfo *modinfop) 208 { 209 return (mod_info(&modlinkage, modinfop)); 210 } 211 212 static int 213 zc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 214 { 215 zc_state_t *zcs; 216 int instance; 217 218 if (cmd != DDI_ATTACH) 219 return (DDI_FAILURE); 220 221 instance = ddi_get_instance(dip); 222 if (ddi_soft_state_zalloc(zc_soft_state, instance) != DDI_SUCCESS) 223 return (DDI_FAILURE); 224 225 if ((ddi_create_minor_node(dip, ZCONS_SLAVE_NAME, S_IFCHR, 226 instance << 1 | ZC_SLAVE_MINOR, DDI_PSEUDO, 0) == DDI_FAILURE) || 227 (ddi_create_minor_node(dip, ZCONS_MASTER_NAME, S_IFCHR, 228 instance << 1 | ZC_MASTER_MINOR, DDI_PSEUDO, 0) == DDI_FAILURE)) { 229 ddi_remove_minor_node(dip, NULL); 230 ddi_soft_state_free(zc_soft_state, instance); 231 return (DDI_FAILURE); 232 } 233 234 if ((zcs = ddi_get_soft_state(zc_soft_state, instance)) == NULL) { 235 ddi_remove_minor_node(dip, NULL); 236 ddi_soft_state_free(zc_soft_state, instance); 237 return (DDI_FAILURE); 238 } 239 zcs->zc_devinfo = dip; 240 241 return (DDI_SUCCESS); 242 } 243 244 static int 245 zc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 246 { 247 zc_state_t *zcs; 248 int instance; 249 250 if (cmd != DDI_DETACH) 251 return (DDI_FAILURE); 252 253 instance = ddi_get_instance(dip); 254 if ((zcs = ddi_get_soft_state(zc_soft_state, instance)) == NULL) 255 return (DDI_FAILURE); 256 257 if ((zcs->zc_state & ZC_STATE_MOPEN) || 258 (zcs->zc_state & ZC_STATE_SOPEN)) { 259 DBG1("zc_detach: device (dip=%p) still open\n", (void *)dip); 260 return (DDI_FAILURE); 261 } 262 263 ddi_remove_minor_node(dip, NULL); 264 ddi_soft_state_free(zc_soft_state, instance); 265 266 return (DDI_SUCCESS); 267 } 268 269 /* 270 * zc_getinfo() 271 * getinfo(9e) entrypoint. 272 */ 273 /*ARGSUSED*/ 274 static int 275 zc_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 276 { 277 zc_state_t *zcs; 278 int instance = ZC_INSTANCE((dev_t)arg); 279 280 switch (infocmd) { 281 case DDI_INFO_DEVT2DEVINFO: 282 if ((zcs = ddi_get_soft_state(zc_soft_state, instance)) == NULL) 283 return (DDI_FAILURE); 284 *result = zcs->zc_devinfo; 285 return (DDI_SUCCESS); 286 case DDI_INFO_DEVT2INSTANCE: 287 *result = (void *)(uintptr_t)instance; 288 return (DDI_SUCCESS); 289 } 290 return (DDI_FAILURE); 291 } 292 293 /* 294 * Return the equivalent queue from the other side of the relationship. 295 * e.g.: given the slave's write queue, return the master's write queue. 296 */ 297 static queue_t * 298 zc_switch(queue_t *qp) 299 { 300 zc_state_t *zcs = qp->q_ptr; 301 ASSERT(zcs != NULL); 302 303 if (qp == zcs->zc_master_rdq) 304 return (zcs->zc_slave_rdq); 305 else if (OTHERQ(qp) == zcs->zc_master_rdq && zcs->zc_slave_rdq != NULL) 306 return (OTHERQ(zcs->zc_slave_rdq)); 307 else if (qp == zcs->zc_slave_rdq) 308 return (zcs->zc_master_rdq); 309 else if (OTHERQ(qp) == zcs->zc_slave_rdq && zcs->zc_master_rdq != NULL) 310 return (OTHERQ(zcs->zc_master_rdq)); 311 else 312 return (NULL); 313 } 314 315 /* 316 * For debugging and outputting messages. Returns the name of the side of 317 * the relationship associated with this queue. 318 */ 319 static const char * 320 zc_side(queue_t *qp) 321 { 322 zc_state_t *zcs = qp->q_ptr; 323 ASSERT(zcs != NULL); 324 325 if (qp == zcs->zc_master_rdq || 326 OTHERQ(qp) == zcs->zc_master_rdq) { 327 return ("master"); 328 } 329 ASSERT(qp == zcs->zc_slave_rdq || OTHERQ(qp) == zcs->zc_slave_rdq); 330 return ("slave"); 331 } 332 333 /*ARGSUSED*/ 334 static int 335 zc_master_open(zc_state_t *zcs, 336 queue_t *rqp, /* pointer to the read side queue */ 337 dev_t *devp, /* pointer to stream tail's dev */ 338 int oflag, /* the user open(2) supplied flags */ 339 int sflag, /* open state flag */ 340 cred_t *credp) /* credentials */ 341 { 342 mblk_t *mop; 343 struct stroptions *sop; 344 345 /* 346 * Enforce exclusivity on the master side; the only consumer should 347 * be the zoneadmd for the zone. 348 */ 349 if ((zcs->zc_state & ZC_STATE_MOPEN) != 0) 350 return (EBUSY); 351 352 if ((mop = allocb(sizeof (struct stroptions), BPRI_MED)) == NULL) { 353 DBG("zc_master_open(): mop allocation failed\n"); 354 return (ENOMEM); 355 } 356 357 zcs->zc_state |= ZC_STATE_MOPEN; 358 359 /* 360 * q_ptr stores driver private data; stash the soft state data on both 361 * read and write sides of the queue. 362 */ 363 WR(rqp)->q_ptr = rqp->q_ptr = zcs; 364 qprocson(rqp); 365 366 /* 367 * Following qprocson(), the master side is fully plumbed into the 368 * STREAM and may send/receive messages. Setting zcs->zc_master_rdq 369 * will allow the slave to send messages to us (the master). 370 * This cannot occur before qprocson() because the master is not 371 * ready to process them until that point. 372 */ 373 zcs->zc_master_rdq = rqp; 374 375 /* 376 * set up hi/lo water marks on stream head read queue and add 377 * controlling tty as needed. 378 */ 379 mop->b_datap->db_type = M_SETOPTS; 380 mop->b_wptr += sizeof (struct stroptions); 381 sop = (struct stroptions *)mop->b_rptr; 382 if (oflag & FNOCTTY) 383 sop->so_flags = SO_HIWAT | SO_LOWAT; 384 else 385 sop->so_flags = SO_HIWAT | SO_LOWAT | SO_ISTTY; 386 sop->so_hiwat = 512; 387 sop->so_lowat = 256; 388 putnext(rqp, mop); 389 390 return (0); 391 } 392 393 /*ARGSUSED*/ 394 static int 395 zc_slave_open(zc_state_t *zcs, 396 queue_t *rqp, /* pointer to the read side queue */ 397 dev_t *devp, /* pointer to stream tail's dev */ 398 int oflag, /* the user open(2) supplied flags */ 399 int sflag, /* open state flag */ 400 cred_t *credp) /* credentials */ 401 { 402 mblk_t *mop; 403 struct stroptions *sop; 404 405 /* 406 * The slave side can be opened as many times as needed. 407 */ 408 if ((zcs->zc_state & ZC_STATE_SOPEN) != 0) { 409 ASSERT((rqp != NULL) && (WR(rqp)->q_ptr == zcs)); 410 return (0); 411 } 412 413 if ((mop = allocb(sizeof (struct stroptions), BPRI_MED)) == NULL) { 414 DBG("zc_slave_open(): mop allocation failed\n"); 415 return (ENOMEM); 416 } 417 418 zcs->zc_state |= ZC_STATE_SOPEN; 419 420 /* 421 * q_ptr stores driver private data; stash the soft state data on both 422 * read and write sides of the queue. 423 */ 424 WR(rqp)->q_ptr = rqp->q_ptr = zcs; 425 426 qprocson(rqp); 427 428 /* 429 * Must follow qprocson(), since we aren't ready to process until then. 430 */ 431 zcs->zc_slave_rdq = rqp; 432 433 /* 434 * set up hi/lo water marks on stream head read queue and add 435 * controlling tty as needed. 436 */ 437 mop->b_datap->db_type = M_SETOPTS; 438 mop->b_wptr += sizeof (struct stroptions); 439 sop = (struct stroptions *)mop->b_rptr; 440 sop->so_flags = SO_HIWAT | SO_LOWAT | SO_ISTTY; 441 sop->so_hiwat = 512; 442 sop->so_lowat = 256; 443 putnext(rqp, mop); 444 445 return (0); 446 } 447 448 /* 449 * open(9e) entrypoint; checks sflag, and rejects anything unordinary. 450 */ 451 static int 452 zc_open(queue_t *rqp, /* pointer to the read side queue */ 453 dev_t *devp, /* pointer to stream tail's dev */ 454 int oflag, /* the user open(2) supplied flags */ 455 int sflag, /* open state flag */ 456 cred_t *credp) /* credentials */ 457 { 458 int instance = ZC_INSTANCE(*devp); 459 int ret; 460 zc_state_t *zcs; 461 462 if (sflag != 0) 463 return (EINVAL); 464 465 if ((zcs = ddi_get_soft_state(zc_soft_state, instance)) == NULL) 466 return (ENXIO); 467 468 switch (ZC_NODE(*devp)) { 469 case ZC_MASTER_MINOR: 470 ret = zc_master_open(zcs, rqp, devp, oflag, sflag, credp); 471 break; 472 case ZC_SLAVE_MINOR: 473 ret = zc_slave_open(zcs, rqp, devp, oflag, sflag, credp); 474 break; 475 default: 476 ret = ENXIO; 477 break; 478 } 479 480 return (ret); 481 } 482 483 /* 484 * close(9e) entrypoint. 485 */ 486 /*ARGSUSED1*/ 487 static int 488 zc_close(queue_t *rqp, int flag, cred_t *credp) 489 { 490 queue_t *wqp; 491 mblk_t *bp; 492 zc_state_t *zcs; 493 494 zcs = (zc_state_t *)rqp->q_ptr; 495 496 if (rqp == zcs->zc_master_rdq) { 497 DBG("Closing master side"); 498 499 zcs->zc_master_rdq = NULL; 500 zcs->zc_state &= ~ZC_STATE_MOPEN; 501 502 /* 503 * qenable slave side write queue so that it can flush 504 * its messages as master's read queue is going away 505 */ 506 if (zcs->zc_slave_rdq != NULL) { 507 qenable(WR(zcs->zc_slave_rdq)); 508 } 509 510 qprocsoff(rqp); 511 WR(rqp)->q_ptr = rqp->q_ptr = NULL; 512 513 } else if (rqp == zcs->zc_slave_rdq) { 514 515 DBG("Closing slave side"); 516 zcs->zc_state &= ~ZC_STATE_SOPEN; 517 zcs->zc_slave_rdq = NULL; 518 519 wqp = WR(rqp); 520 while ((bp = getq(wqp)) != NULL) { 521 if (zcs->zc_master_rdq != NULL) 522 putnext(zcs->zc_master_rdq, bp); 523 else if (bp->b_datap->db_type == M_IOCTL) 524 miocnak(wqp, bp, 0, 0); 525 else 526 freemsg(bp); 527 } 528 529 /* 530 * Qenable master side write queue so that it can flush its 531 * messages as slaves's read queue is going away. 532 */ 533 if (zcs->zc_master_rdq != NULL) 534 qenable(WR(zcs->zc_master_rdq)); 535 536 qprocsoff(rqp); 537 WR(rqp)->q_ptr = rqp->q_ptr = NULL; 538 } 539 540 return (0); 541 } 542 543 static void 544 handle_mflush(queue_t *qp, mblk_t *mp) 545 { 546 mblk_t *nmp; 547 DBG1("M_FLUSH on %s side", zc_side(qp)); 548 549 if (*mp->b_rptr & FLUSHW) { 550 DBG1("M_FLUSH, FLUSHW, %s side", zc_side(qp)); 551 flushq(qp, FLUSHDATA); 552 *mp->b_rptr &= ~FLUSHW; 553 if ((*mp->b_rptr & FLUSHR) == 0) { 554 /* 555 * FLUSHW only. Change to FLUSHR and putnext other side, 556 * then we are done. 557 */ 558 *mp->b_rptr |= FLUSHR; 559 if (zc_switch(RD(qp)) != NULL) { 560 putnext(zc_switch(RD(qp)), mp); 561 return; 562 } 563 } else if ((zc_switch(RD(qp)) != NULL) && 564 (nmp = copyb(mp)) != NULL) { 565 /* 566 * It is a FLUSHRW; we copy the mblk and send 567 * it to the other side, since we still need to use 568 * the mblk in FLUSHR processing, below. 569 */ 570 putnext(zc_switch(RD(qp)), nmp); 571 } 572 } 573 574 if (*mp->b_rptr & FLUSHR) { 575 DBG("qreply(qp) turning FLUSHR around\n"); 576 qreply(qp, mp); 577 return; 578 } 579 freemsg(mp); 580 } 581 582 /* 583 * wput(9E) is symmetric for master and slave sides, so this handles both 584 * without splitting the codepath. 585 * 586 * zc_wput() looks at the other side; if there is no process holding that 587 * side open, it frees the message. This prevents processes from hanging 588 * if no one is holding open the console. Otherwise, it putnext's high 589 * priority messages, putnext's normal messages if possible, and otherwise 590 * enqueues the messages; in the case that something is enqueued, wsrv(9E) 591 * will take care of eventually shuttling I/O to the other side. 592 */ 593 static void 594 zc_wput(queue_t *qp, mblk_t *mp) 595 { 596 unsigned char type = mp->b_datap->db_type; 597 598 ASSERT(qp->q_ptr); 599 600 DBG1("entering zc_wput, %s side", zc_side(qp)); 601 602 if (zc_switch(RD(qp)) == NULL) { 603 DBG1("wput to %s side (no one listening)", zc_side(qp)); 604 switch (type) { 605 case M_FLUSH: 606 handle_mflush(qp, mp); 607 break; 608 case M_IOCTL: 609 miocnak(qp, mp, 0, 0); 610 break; 611 default: 612 freemsg(mp); 613 break; 614 } 615 return; 616 } 617 618 if (type >= QPCTL) { 619 DBG1("(hipri) wput, %s side", zc_side(qp)); 620 switch (type) { 621 case M_READ: /* supposedly from ldterm? */ 622 DBG("zc_wput: tossing M_READ\n"); 623 freemsg(mp); 624 break; 625 case M_FLUSH: 626 handle_mflush(qp, mp); 627 break; 628 default: 629 /* 630 * Put this to the other side. 631 */ 632 ASSERT(zc_switch(RD(qp)) != NULL); 633 putnext(zc_switch(RD(qp)), mp); 634 break; 635 } 636 DBG1("done (hipri) wput, %s side", zc_side(qp)); 637 return; 638 } 639 640 /* 641 * Only putnext if there isn't already something in the queue. 642 * otherwise things would wind up out of order. 643 */ 644 if (qp->q_first == NULL && bcanputnext(RD(zc_switch(qp)), mp->b_band)) { 645 DBG("wput: putting message to other side\n"); 646 putnext(RD(zc_switch(qp)), mp); 647 } else { 648 DBG("wput: putting msg onto queue\n"); 649 (void) putq(qp, mp); 650 } 651 DBG1("done wput, %s side", zc_side(qp)); 652 } 653 654 /* 655 * rsrv(9E) is symmetric for master and slave, so zc_rsrv() handles both 656 * without splitting up the codepath. 657 * 658 * Enable the write side of the partner. This triggers the partner to send 659 * messages queued on its write side to this queue's read side. 660 */ 661 static void 662 zc_rsrv(queue_t *qp) 663 { 664 zc_state_t *zcs; 665 zcs = (zc_state_t *)qp->q_ptr; 666 667 /* 668 * Care must be taken here, as either of the master or slave side 669 * qptr could be NULL. 670 */ 671 ASSERT(qp == zcs->zc_master_rdq || qp == zcs->zc_slave_rdq); 672 if (zc_switch(qp) == NULL) { 673 DBG("zc_rsrv: other side isn't listening\n"); 674 return; 675 } 676 qenable(WR(zc_switch(qp))); 677 } 678 679 /* 680 * This routine is symmetric for master and slave, so it handles both without 681 * splitting up the codepath. 682 * 683 * If there are messages on this queue that can be sent to the other, send 684 * them via putnext(). Else, if queued messages cannot be sent, leave them 685 * on this queue. 686 */ 687 static void 688 zc_wsrv(queue_t *qp) 689 { 690 mblk_t *mp; 691 692 DBG1("zc_wsrv master (%s) side", zc_side(qp)); 693 694 /* 695 * Partner has no read queue, so take the data, and throw it away. 696 */ 697 if (zc_switch(RD(qp)) == NULL) { 698 DBG("zc_wsrv: other side isn't listening"); 699 while ((mp = getq(qp)) != NULL) { 700 if (mp->b_datap->db_type == M_IOCTL) 701 miocnak(qp, mp, 0, 0); 702 else 703 freemsg(mp); 704 } 705 flushq(qp, FLUSHALL); 706 return; 707 } 708 709 /* 710 * while there are messages on this write queue... 711 */ 712 while ((mp = getq(qp)) != NULL) { 713 /* 714 * Due to the way zc_wput is implemented, we should never 715 * see a control message here. 716 */ 717 ASSERT(mp->b_datap->db_type < QPCTL); 718 719 if (bcanputnext(RD(zc_switch(qp)), mp->b_band)) { 720 DBG("wsrv: send message to other side\n"); 721 putnext(RD(zc_switch(qp)), mp); 722 } else { 723 DBG("wsrv: putting msg back on queue\n"); 724 (void) putbq(qp, mp); 725 break; 726 } 727 } 728 } 729