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