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 /* 23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 28 /* 29 * workstation console redirecting driver 30 * 31 * Redirects all I/O through a given device instance to the device designated 32 * as the current target, as given by the vnode associated with the first 33 * entry in the list of redirections for the given device instance. The 34 * implementation assumes that this vnode denotes a STREAMS device; this is 35 * perhaps a bug. 36 * 37 * Supports the SRIOCSREDIR ioctl for designating a new redirection target. 38 * The new target is added to the front of a list of potentially active 39 * designees. Should the device at the front of this list be closed, the new 40 * front entry assumes active duty. (Stated differently, redirection targets 41 * stack, except that it's possible for entries in the interior of the stack 42 * to go away.) 43 * 44 * Supports the SRIOCISREDIR ioctl for inquiring whether the descriptor given 45 * as argument is the current front of the redirection list associated with 46 * the descriptor on which the ioctl was issued. 47 */ 48 49 #include <sys/types.h> 50 #include <sys/sysmacros.h> 51 #include <sys/open.h> 52 #include <sys/param.h> 53 #include <sys/systm.h> 54 #include <sys/signal.h> 55 #include <sys/cred.h> 56 #include <sys/user.h> 57 #include <sys/proc.h> 58 #include <sys/vnode.h> 59 #include <sys/uio.h> 60 #include <sys/file.h> 61 #include <sys/kmem.h> 62 #include <sys/stat.h> 63 #include <sys/stream.h> 64 #include <sys/stropts.h> 65 #include <sys/strsubr.h> 66 #include <sys/poll.h> 67 #include <sys/debug.h> 68 #include <sys/strredir.h> 69 #include <sys/conf.h> 70 #include <sys/ddi.h> 71 #include <sys/sunddi.h> 72 #include <sys/errno.h> 73 #include <sys/modctl.h> 74 #include <sys/sunldi.h> 75 #include <sys/consdev.h> 76 #include <sys/fs/snode.h> 77 78 /* 79 * Global data 80 */ 81 static dev_info_t *iwscn_dip; 82 83 /* 84 * We record the list of redirections as a linked list of iwscn_list_t 85 * structures. We need to keep track of the target's vp, so that 86 * we can vector reads, writes, etc. off to the current designee. 87 */ 88 typedef struct _iwscn_list { 89 struct _iwscn_list *wl_next; /* next entry */ 90 vnode_t *wl_vp; /* target's vnode */ 91 int wl_ref_cnt; /* operation in progress */ 92 boolean_t wl_is_console; /* is the real console */ 93 } iwscn_list_t; 94 static iwscn_list_t *iwscn_list; 95 96 /* 97 * iwscn_list_lock serializes modifications to the global iwscn_list list. 98 * 99 * iwscn_list_cv is used when freeing an entry from iwscn_list to allow 100 * the caller to wait till the wl_ref_cnt field is zero. 101 * 102 * iwscn_redirect_lock is used to serialize redirection requests. This 103 * is required to ensure that all active redirection streams have 104 * the redirection streams module (redirmod) pushed on them. 105 * 106 * If both iwscn_redirect_lock and iwscn_list_lock must be held then 107 * iwscn_redirect_lock must be acquired first. 108 */ 109 static kcondvar_t iwscn_list_cv; 110 static kmutex_t iwscn_list_lock; 111 static kmutex_t iwscn_redirect_lock; 112 113 /* 114 * Routines for managing iwscn_list 115 */ 116 static vnode_t * 117 str_vp(vnode_t *vp) 118 { 119 /* 120 * Here we switch to using the vnode that is linked 121 * to from the stream queue. (In the case of device 122 * streams this will correspond to the common vnode 123 * for the device.) The reason we use this vnode 124 * is that when wcmclose() calls srpop(), this is the 125 * only vnode that it has access to. 126 */ 127 ASSERT(vp->v_stream != NULL); 128 return (vp->v_stream->sd_vnode); 129 } 130 131 /* 132 * Interrupt any operations that may be outstanding against this vnode. 133 * optionally, wait for them to complete. 134 */ 135 static void 136 srinterrupt(iwscn_list_t *lp, boolean_t wait) 137 { 138 ASSERT(MUTEX_HELD(&iwscn_list_lock)); 139 140 while (lp->wl_ref_cnt != 0) { 141 strsetrerror(lp->wl_vp, EINTR, 0, NULL); 142 strsetwerror(lp->wl_vp, EINTR, 0, NULL); 143 if (!wait) 144 break; 145 cv_wait(&iwscn_list_cv, &iwscn_list_lock); 146 } 147 } 148 149 /* 150 * Remove vp from the redirection list rooted at iwscn_list, should it 151 * be there. Return a pointer to the removed entry. 152 */ 153 static iwscn_list_t * 154 srrm(vnode_t *vp) 155 { 156 iwscn_list_t *lp, **lpp; 157 158 ASSERT(MUTEX_HELD(&iwscn_list_lock)); 159 160 /* Get the stream vnode */ 161 vp = str_vp(vp); 162 ASSERT(vp); 163 164 /* Look for this vnode on the redirection list */ 165 for (lpp = &iwscn_list; (lp = *lpp) != NULL; lpp = &lp->wl_next) { 166 if (lp->wl_vp == vp) 167 break; 168 } 169 if (lp != NULL) 170 /* Found it, remove this entry from the redirection list */ 171 *lpp = lp->wl_next; 172 173 return (lp); 174 } 175 176 /* 177 * Push vp onto the redirection list. 178 * If it's already there move it to the front position. 179 */ 180 static void 181 srpush(vnode_t *vp, boolean_t is_console) 182 { 183 iwscn_list_t *lp; 184 185 ASSERT(MUTEX_HELD(&iwscn_list_lock)); 186 187 /* Get the stream vnode */ 188 vp = str_vp(vp); 189 ASSERT(vp); 190 191 /* Check if it's already on the redirection list */ 192 if ((lp = srrm(vp)) == NULL) { 193 lp = kmem_zalloc(sizeof (*lp), KM_SLEEP); 194 lp->wl_vp = vp; 195 lp->wl_is_console = is_console; 196 } 197 /* 198 * Note that if this vnode was already somewhere on the redirection 199 * list then we removed it above and are now bumping it up to the 200 * front of the redirection list. 201 */ 202 lp->wl_next = iwscn_list; 203 iwscn_list = lp; 204 } 205 206 /* 207 * This vnode is no longer a valid redirection target. Terminate any current 208 * operations. If closing, wait for them to complete, then free the entry. 209 * If called because a hangup has occurred, just deprecate the entry to ensure 210 * it won't become the target again. 211 */ 212 void 213 srpop(vnode_t *vp, boolean_t close) 214 { 215 iwscn_list_t *tlp; /* This target's entry */ 216 iwscn_list_t *lp, **lpp; 217 218 mutex_enter(&iwscn_list_lock); 219 220 /* 221 * Ensure no further operations are directed at the target 222 * by removing it from the redirection list. 223 */ 224 if ((tlp = srrm(vp)) == NULL) { 225 /* vnode wasn't in the list */ 226 mutex_exit(&iwscn_list_lock); 227 return; 228 } 229 /* 230 * Terminate any current operations. 231 * If we're closing, wait until they complete. 232 */ 233 srinterrupt(tlp, close); 234 235 if (close) { 236 /* We're finished with this target */ 237 kmem_free(tlp, sizeof (*tlp)); 238 } else { 239 /* 240 * Deprecate the entry. There's no need for a flag to indicate 241 * this state, it just needs to be moved to the back of the list 242 * behind the underlying console device. Since the underlying 243 * device anchors the list and is never removed, this entry can 244 * never return to the front again to become the target. 245 */ 246 for (lpp = &iwscn_list; (lp = *lpp) != NULL; ) 247 lpp = &lp->wl_next; 248 tlp->wl_next = NULL; 249 *lpp = tlp; 250 } 251 mutex_exit(&iwscn_list_lock); 252 } 253 254 /* Get a hold on the current target */ 255 static iwscn_list_t * 256 srhold() 257 { 258 iwscn_list_t *lp; 259 260 mutex_enter(&iwscn_list_lock); 261 ASSERT(iwscn_list != NULL); 262 lp = iwscn_list; 263 ASSERT(lp->wl_ref_cnt >= 0); 264 lp->wl_ref_cnt++; 265 mutex_exit(&iwscn_list_lock); 266 267 return (lp); 268 } 269 270 /* Release a hold on an entry from the redirection list */ 271 static void 272 srrele(iwscn_list_t *lp) 273 { 274 ASSERT(lp != NULL); 275 mutex_enter(&iwscn_list_lock); 276 ASSERT(lp->wl_ref_cnt > 0); 277 lp->wl_ref_cnt--; 278 cv_broadcast(&iwscn_list_cv); 279 mutex_exit(&iwscn_list_lock); 280 } 281 282 static int 283 iwscnread(dev_t dev, uio_t *uio, cred_t *cred) 284 { 285 iwscn_list_t *lp; 286 int error; 287 288 ASSERT(getminor(dev) == 0); 289 290 lp = srhold(); 291 error = strread(lp->wl_vp, uio, cred); 292 srrele(lp); 293 294 return (error); 295 } 296 297 static int 298 iwscnwrite(dev_t dev, uio_t *uio, cred_t *cred) 299 { 300 iwscn_list_t *lp; 301 int error; 302 303 ASSERT(getminor(dev) == 0); 304 305 lp = srhold(); 306 error = strwrite(lp->wl_vp, uio, cred); 307 srrele(lp); 308 309 return (error); 310 } 311 312 static int 313 iwscnpoll(dev_t dev, short events, int anyyet, short *reventsp, 314 struct pollhead **phpp) 315 { 316 iwscn_list_t *lp; 317 int error; 318 319 ASSERT(getminor(dev) == 0); 320 321 lp = srhold(); 322 error = VOP_POLL(lp->wl_vp, events, anyyet, reventsp, phpp, NULL); 323 srrele(lp); 324 325 return (error); 326 } 327 328 static int 329 iwscnioctl(dev_t dev, int cmd, intptr_t arg, int flag, 330 cred_t *cred, int *rvalp) 331 { 332 iwscn_list_t *lp; 333 file_t *f; 334 char modname[FMNAMESZ + 1] = " "; 335 int error = 0; 336 337 ASSERT(getminor(dev) == 0); 338 339 switch (cmd) { 340 case SRIOCSREDIR: 341 /* Serialize all pushes of the redirection module */ 342 mutex_enter(&iwscn_redirect_lock); 343 344 /* 345 * Find the vnode corresponding to the file descriptor 346 * argument and verify that it names a stream. 347 */ 348 if ((f = getf((int)arg)) == NULL) { 349 mutex_exit(&iwscn_redirect_lock); 350 return (EBADF); 351 } 352 if (f->f_vnode->v_stream == NULL) { 353 releasef((int)arg); 354 mutex_exit(&iwscn_redirect_lock); 355 return (ENOSTR); 356 } 357 358 /* 359 * If the user is trying to redirect console output 360 * back to the underlying console via SRIOCSREDIR 361 * then they are evil and we'll stop them here. 362 */ 363 if (str_vp(f->f_vnode) == str_vp(rwsconsvp)) { 364 releasef((int)arg); 365 mutex_exit(&iwscn_redirect_lock); 366 return (EINVAL); 367 } 368 369 /* 370 * Check if this stream already has the redirection 371 * module pushed onto it. I_LOOK returns an error 372 * if there are no modules pushed onto the stream. 373 */ 374 (void) strioctl(f->f_vnode, I_LOOK, (intptr_t)modname, 375 FKIOCTL, K_TO_K, cred, rvalp); 376 if (strcmp(modname, STRREDIR_MOD) != 0) { 377 378 /* 379 * Push a new instance of the redirecting module onto 380 * the stream, so that its close routine can notify 381 * us when the overall stream is closed. (In turn, 382 * we'll then remove it from the redirection list.) 383 */ 384 error = strioctl(f->f_vnode, I_PUSH, 385 (intptr_t)STRREDIR_MOD, FKIOCTL, K_TO_K, 386 cred, rvalp); 387 388 if (error != 0) { 389 releasef((int)arg); 390 mutex_exit(&iwscn_redirect_lock); 391 return (error); 392 } 393 } 394 395 /* Push it onto the redirection stack */ 396 mutex_enter(&iwscn_list_lock); 397 srpush(f->f_vnode, B_FALSE); 398 mutex_exit(&iwscn_list_lock); 399 400 releasef((int)arg); 401 mutex_exit(&iwscn_redirect_lock); 402 return (0); 403 404 case SRIOCISREDIR: 405 /* 406 * Find the vnode corresponding to the file descriptor 407 * argument and verify that it names a stream. 408 */ 409 if ((f = getf((int)arg)) == NULL) { 410 return (EBADF); 411 } 412 if (f->f_vnode->v_stream == NULL) { 413 releasef((int)arg); 414 return (ENOSTR); 415 } 416 417 lp = srhold(); 418 *rvalp = (str_vp(f->f_vnode) == lp->wl_vp); 419 srrele(lp); 420 releasef((int)arg); 421 return (0); 422 423 case I_POP: 424 /* 425 * We need to serialize I_POP operations with 426 * SRIOCSREDIR operations so we don't accidently 427 * remove the redirection module from a stream. 428 */ 429 mutex_enter(&iwscn_redirect_lock); 430 lp = srhold(); 431 432 /* 433 * Here we need to protect against process that might 434 * try to pop off the redirection module from the 435 * redirected stream. Popping other modules is allowed. 436 * 437 * It's ok to hold iwscn_list_lock while doing the 438 * I_LOOK since it's such a simple operation. 439 */ 440 (void) strioctl(lp->wl_vp, I_LOOK, (intptr_t)modname, 441 FKIOCTL, K_TO_K, cred, rvalp); 442 443 if (strcmp(STRREDIR_MOD, modname) == 0) { 444 srrele(lp); 445 mutex_exit(&iwscn_redirect_lock); 446 return (EINVAL); 447 } 448 449 /* Process the ioctl normally */ 450 error = VOP_IOCTL(lp->wl_vp, cmd, arg, flag, cred, rvalp, NULL); 451 452 srrele(lp); 453 mutex_exit(&iwscn_redirect_lock); 454 return (error); 455 } 456 457 /* Process the ioctl normally */ 458 lp = srhold(); 459 error = VOP_IOCTL(lp->wl_vp, cmd, arg, flag, cred, rvalp, NULL); 460 srrele(lp); 461 return (error); 462 } 463 464 /* ARGSUSED */ 465 static int 466 iwscnopen(dev_t *devp, int flag, int state, cred_t *cred) 467 { 468 iwscn_list_t *lp; 469 vnode_t *vp = rwsconsvp; 470 471 if (state != OTYP_CHR) 472 return (ENXIO); 473 474 if (getminor(*devp) != 0) 475 return (ENXIO); 476 477 /* 478 * You can't really open us until the console subsystem 479 * has been configured. 480 */ 481 if (rwsconsvp == NULL) 482 return (ENXIO); 483 484 /* 485 * Check if this is the first open of this device or if 486 * there is currently no redirection going on. (Ie, we're 487 * sending output to underlying console device.) 488 */ 489 mutex_enter(&iwscn_list_lock); 490 if ((iwscn_list == NULL) || (iwscn_list->wl_vp == str_vp(vp))) { 491 int error = 0; 492 493 /* Don't hold the list lock across an VOP_OPEN */ 494 mutex_exit(&iwscn_list_lock); 495 496 /* 497 * There is currently no redirection going on. 498 * pass this open request onto the console driver 499 */ 500 error = VOP_OPEN(&vp, flag, cred, NULL); 501 if (error != 0) 502 return (error); 503 504 /* Re-acquire the list lock */ 505 mutex_enter(&iwscn_list_lock); 506 507 if (iwscn_list == NULL) { 508 /* Save this vnode on the redirection list */ 509 srpush(vp, B_TRUE); 510 } else { 511 /* 512 * In this case there must already be a copy of 513 * this vnode on the list, so we can free up this one. 514 */ 515 (void) VOP_CLOSE(vp, flag, 1, (offset_t)0, cred, NULL); 516 } 517 } 518 519 /* 520 * XXX This is an ugly legacy hack that has been around 521 * forever. This code is here because this driver (the 522 * iwscn driver) is a character driver layered over a 523 * streams driver. 524 * 525 * Normally streams recieve notification whenever a process 526 * closes its last reference to that stream so that it can 527 * clean up any signal handling related configuration. (Ie, 528 * when a stream is configured to deliver a signal to a 529 * process upon certain events.) This is a feature supported 530 * by the streams framework. 531 * 532 * But character/block drivers don't recieve this type 533 * of notification. A character/block driver's close routine 534 * is only invoked upon the last close of the device. This 535 * is an artifact of the multiple open/single close driver 536 * model currently supported by solaris. 537 * 538 * So a problem occurs when a character driver layers itself 539 * on top of a streams driver. Since this driver doesn't always 540 * receive a close notification when a process closes its 541 * last reference to it, this driver can't tell the stream 542 * it's layered upon to clean up any signal handling 543 * configuration for that process. 544 * 545 * So here we hack around that by manually cleaning up the 546 * signal handling list upon each open. It doesn't guarantee 547 * that the signaling handling data stored in the stream will 548 * always be up to date, but it'll be more up to date than 549 * it would be if we didn't do this. 550 * 551 * The real way to solve this problem would be to change 552 * the device framework from an multiple open/single close 553 * model to a multiple open/multiple close model. Then 554 * character/block drivers could pass on close requests 555 * to streams layered underneath. 556 */ 557 str_cn_clean(VTOS(rwsconsvp)->s_commonvp); 558 for (lp = iwscn_list; lp != NULL; lp = lp->wl_next) { 559 ASSERT(lp->wl_vp->v_stream != NULL); 560 str_cn_clean(lp->wl_vp); 561 } 562 563 mutex_exit(&iwscn_list_lock); 564 return (0); 565 } 566 567 /* ARGSUSED */ 568 static int 569 iwscnclose(dev_t dev, int flag, int state, cred_t *cred) 570 { 571 iwscn_list_t *lp; 572 573 ASSERT(getminor(dev) == 0); 574 575 if (state != OTYP_CHR) 576 return (ENXIO); 577 578 mutex_enter(&iwscn_list_lock); 579 /* 580 * Remove each entry from the redirection list, terminate any 581 * current operations, wait for them to finish, then free the entry. 582 */ 583 while (iwscn_list != NULL) { 584 lp = srrm(iwscn_list->wl_vp); 585 ASSERT(lp != NULL); 586 srinterrupt(lp, B_TRUE); 587 588 if (lp->wl_is_console == B_TRUE) 589 /* Close the underlying console device. */ 590 (void) VOP_CLOSE(lp->wl_vp, 0, 1, (offset_t)0, kcred, 591 NULL); 592 593 kmem_free(lp, sizeof (*lp)); 594 } 595 mutex_exit(&iwscn_list_lock); 596 return (0); 597 } 598 599 /*ARGSUSED*/ 600 static int 601 iwscnattach(dev_info_t *devi, ddi_attach_cmd_t cmd) 602 { 603 /* 604 * This is a pseudo device so there will never be more than 605 * one instance attached at a time 606 */ 607 ASSERT(iwscn_dip == NULL); 608 609 if (ddi_create_minor_node(devi, "iwscn", S_IFCHR, 610 0, DDI_PSEUDO, NULL) == DDI_FAILURE) { 611 return (DDI_FAILURE); 612 } 613 614 iwscn_dip = devi; 615 mutex_init(&iwscn_list_lock, NULL, MUTEX_DRIVER, NULL); 616 mutex_init(&iwscn_redirect_lock, NULL, MUTEX_DRIVER, NULL); 617 cv_init(&iwscn_list_cv, NULL, CV_DRIVER, NULL); 618 619 return (DDI_SUCCESS); 620 } 621 622 /* ARGSUSED */ 623 static int 624 iwscninfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 625 { 626 int error; 627 628 switch (infocmd) { 629 case DDI_INFO_DEVT2DEVINFO: 630 if (iwscn_dip == NULL) { 631 error = DDI_FAILURE; 632 } else { 633 *result = (void *)iwscn_dip; 634 error = DDI_SUCCESS; 635 } 636 break; 637 case DDI_INFO_DEVT2INSTANCE: 638 *result = (void *)0; 639 error = DDI_SUCCESS; 640 break; 641 default: 642 error = DDI_FAILURE; 643 } 644 return (error); 645 } 646 647 struct cb_ops iwscn_cb_ops = { 648 iwscnopen, /* open */ 649 iwscnclose, /* close */ 650 nodev, /* strategy */ 651 nodev, /* print */ 652 nodev, /* dump */ 653 iwscnread, /* read */ 654 iwscnwrite, /* write */ 655 iwscnioctl, /* ioctl */ 656 nodev, /* devmap */ 657 nodev, /* mmap */ 658 nodev, /* segmap */ 659 iwscnpoll, /* poll */ 660 ddi_prop_op, /* cb_prop_op */ 661 NULL, /* streamtab */ 662 D_MP /* Driver compatibility flag */ 663 }; 664 665 struct dev_ops iwscn_ops = { 666 DEVO_REV, /* devo_rev, */ 667 0, /* refcnt */ 668 iwscninfo, /* info */ 669 nulldev, /* identify */ 670 nulldev, /* probe */ 671 iwscnattach, /* attach */ 672 nodev, /* detach */ 673 nodev, /* reset */ 674 &iwscn_cb_ops, /* driver operations */ 675 NULL, /* bus operations */ 676 NULL, /* power */ 677 ddi_quiesce_not_needed, /* quiesce */ 678 }; 679 680 /* 681 * Module linkage information for the kernel. 682 */ 683 static struct modldrv modldrv = { 684 &mod_driverops, /* Type of module. This one is a pseudo driver */ 685 "Workstation Redirection driver", 686 &iwscn_ops, /* driver ops */ 687 }; 688 689 static struct modlinkage modlinkage = { 690 MODREV_1, 691 { &modldrv, NULL } 692 }; 693 694 int 695 _init(void) 696 { 697 return (mod_install(&modlinkage)); 698 } 699 700 int 701 _fini(void) 702 { 703 return (EBUSY); 704 } 705 706 int 707 _info(struct modinfo *modinfop) 708 { 709 return (mod_info(&modlinkage, modinfop)); 710 }