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