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 2006 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 aquired 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 * Remove vp from the redirection list rooted at iwscn_list, should it 134 * be there. If iwscn_list is non-NULL, deallocate the entry. If 135 * the entry doesn't exist upon completion, return NULL; otherwise 136 * return a pointer to it. 137 */ 138 static iwscn_list_t * 139 srrm(vnode_t *vp, boolean_t free_entry) 140 { 141 iwscn_list_t *lp, **lpp; 142 143 ASSERT(MUTEX_HELD(&iwscn_list_lock)); 144 145 /* Get the stream vnode */ 146 vp = str_vp(vp); 147 ASSERT(vp); 148 149 /* Look for this vnode on the redirection list */ 150 for (lpp = &iwscn_list; (lp = *lpp) != NULL; lpp = &lp->wl_next) { 151 if (lp->wl_vp == vp) 152 break; 153 } 154 if (lp == NULL) 155 return (NULL); 156 157 /* Found it, remove this entry from the redirection list */ 158 *lpp = lp->wl_next; 159 160 if (free_entry == B_FALSE) 161 return (lp); 162 163 /* 164 * This entry is no longer on the global redirection list so now 165 * we have to wait for all operations currently in progress to 166 * finish before we can actually delete this entry. We don't 167 * have to worry about a new operation on this vnode starting up 168 * because we've removed it from the redirection list. 169 */ 170 while (lp->wl_ref_cnt != 0) { 171 /* 172 * Interrupt any operations that may be outstanding 173 * against this vnode and wait for them to complete. 174 */ 175 strsetrerror(lp->wl_vp, EINTR, 0, NULL); 176 strsetwerror(lp->wl_vp, EINTR, 0, NULL); 177 cv_wait(&iwscn_list_cv, &iwscn_list_lock); 178 } 179 180 if (lp->wl_is_console == B_TRUE) { 181 /* 182 * Special case. If this is the underlying console device 183 * then we opened it so we need to close it. 184 */ 185 (void) VOP_CLOSE(lp->wl_vp, 0, 1, (offset_t)0, kcred); 186 } else { 187 /* Release our hold on this vnode */ 188 VN_RELE(lp->wl_vp); 189 } 190 191 /* Free the entry */ 192 kmem_free(lp, sizeof (*lp)); 193 return (NULL); 194 } 195 196 /* 197 * Push vp onto the redirection list. 198 * If it's already there move it to the front position. 199 */ 200 static void 201 srpush(vnode_t *vp, boolean_t is_console) 202 { 203 iwscn_list_t *lp; 204 205 ASSERT(MUTEX_HELD(&iwscn_list_lock)); 206 207 /* Get the stream vnode */ 208 vp = str_vp(vp); 209 ASSERT(vp); 210 211 /* Check if it's already on the redirection list */ 212 if ((lp = srrm(vp, B_FALSE)) == NULL) { 213 lp = kmem_zalloc(sizeof (*lp), KM_SLEEP); 214 lp->wl_vp = vp; 215 216 if (is_console == B_TRUE) { 217 lp->wl_is_console = B_TRUE; 218 } else { 219 /* 220 * Hold the vnode. Note that this hold will not 221 * prevent the device stream associated with the 222 * vnode from being closed. (We protect against 223 * that by pushing our streams redirection module 224 * onto the stream to intercept close requests.) 225 */ 226 VN_HOLD(lp->wl_vp); 227 lp->wl_is_console = B_FALSE; 228 } 229 } 230 231 /* 232 * Note that if this vnode was already somewhere on the redirection 233 * list then we removed it above and are now bumping it up to the 234 * from of the redirection list. 235 */ 236 lp->wl_next = iwscn_list; 237 iwscn_list = lp; 238 } 239 240 /* 241 * srpop() - Remove redirection because the target stream is being closed. 242 * Called from wcmclose(). 243 */ 244 void 245 srpop(vnode_t *vp) 246 { 247 mutex_enter(&iwscn_list_lock); 248 (void) srrm(vp, B_TRUE); 249 mutex_exit(&iwscn_list_lock); 250 } 251 252 /* Get a hold on the current target */ 253 static iwscn_list_t * 254 srhold() 255 { 256 iwscn_list_t *lp; 257 258 mutex_enter(&iwscn_list_lock); 259 ASSERT(iwscn_list != NULL); 260 lp = iwscn_list; 261 ASSERT(lp->wl_ref_cnt >= 0); 262 lp->wl_ref_cnt++; 263 mutex_exit(&iwscn_list_lock); 264 265 return (lp); 266 } 267 268 /* Release a hold on an entry from the redirection list */ 269 static void 270 srrele(iwscn_list_t *lp) 271 { 272 ASSERT(lp != NULL); 273 mutex_enter(&iwscn_list_lock); 274 ASSERT(lp->wl_ref_cnt > 0); 275 lp->wl_ref_cnt--; 276 cv_broadcast(&iwscn_list_cv); 277 mutex_exit(&iwscn_list_lock); 278 } 279 280 static int 281 iwscnread(dev_t dev, uio_t *uio, cred_t *cred) 282 { 283 iwscn_list_t *lp; 284 int error; 285 286 ASSERT(getminor(dev) == 0); 287 288 lp = srhold(); 289 error = strread(lp->wl_vp, uio, cred); 290 srrele(lp); 291 292 return (error); 293 } 294 295 static int 296 iwscnwrite(dev_t dev, uio_t *uio, cred_t *cred) 297 { 298 iwscn_list_t *lp; 299 int error; 300 301 ASSERT(getminor(dev) == 0); 302 303 lp = srhold(); 304 error = strwrite(lp->wl_vp, uio, cred); 305 srrele(lp); 306 307 return (error); 308 } 309 310 static int 311 iwscnpoll(dev_t dev, short events, int anyyet, short *reventsp, 312 struct pollhead **phpp) 313 { 314 iwscn_list_t *lp; 315 int error; 316 317 ASSERT(getminor(dev) == 0); 318 319 lp = srhold(); 320 error = VOP_POLL(lp->wl_vp, events, anyyet, reventsp, phpp); 321 srrele(lp); 322 323 return (error); 324 } 325 326 static int 327 iwscnioctl(dev_t dev, int cmd, intptr_t arg, int flag, 328 cred_t *cred, int *rvalp) 329 { 330 iwscn_list_t *lp; 331 file_t *f; 332 char modname[FMNAMESZ + 1] = " "; 333 int error = 0; 334 335 ASSERT(getminor(dev) == 0); 336 337 switch (cmd) { 338 case SRIOCSREDIR: 339 /* Serialize all pushes of the redirection module */ 340 mutex_enter(&iwscn_redirect_lock); 341 342 /* 343 * Find the vnode corresponding to the file descriptor 344 * argument and verify that it names a stream. 345 */ 346 if ((f = getf((int)arg)) == NULL) { 347 mutex_exit(&iwscn_redirect_lock); 348 return (EBADF); 349 } 350 if (f->f_vnode->v_stream == NULL) { 351 releasef((int)arg); 352 mutex_exit(&iwscn_redirect_lock); 353 return (ENOSTR); 354 } 355 356 /* 357 * If the user is trying to redirect console output 358 * back to the underlying console via SRIOCSREDIR 359 * then they are evil and we'll stop them here. 360 */ 361 if (str_vp(f->f_vnode) == str_vp(rwsconsvp)) { 362 releasef((int)arg); 363 mutex_exit(&iwscn_redirect_lock); 364 return (EINVAL); 365 } 366 367 /* 368 * Check if this stream already has the redirection 369 * module pushed onto it. I_LOOK returns an error 370 * if there are no modules pushed onto the stream. 371 */ 372 (void) strioctl(f->f_vnode, I_LOOK, (intptr_t)modname, 373 FKIOCTL, K_TO_K, cred, rvalp); 374 if (strcmp(modname, STRREDIR_MOD) != 0) { 375 376 /* 377 * Push a new instance of the redirecting module onto 378 * the stream, so that its close routine can notify 379 * us when the overall stream is closed. (In turn, 380 * we'll then remove it from the redirection list.) 381 */ 382 error = strioctl(f->f_vnode, I_PUSH, 383 (intptr_t)STRREDIR_MOD, FKIOCTL, K_TO_K, 384 cred, rvalp); 385 386 if (error != 0) { 387 releasef((int)arg); 388 mutex_exit(&iwscn_redirect_lock); 389 return (error); 390 } 391 } 392 393 /* Push it onto the redirection stack */ 394 mutex_enter(&iwscn_list_lock); 395 srpush(f->f_vnode, B_FALSE); 396 mutex_exit(&iwscn_list_lock); 397 398 releasef((int)arg); 399 mutex_exit(&iwscn_redirect_lock); 400 return (0); 401 402 case SRIOCISREDIR: 403 /* 404 * Find the vnode corresponding to the file descriptor 405 * argument and verify that it names a stream. 406 */ 407 if ((f = getf((int)arg)) == NULL) { 408 return (EBADF); 409 } 410 if (f->f_vnode->v_stream == NULL) { 411 releasef((int)arg); 412 return (ENOSTR); 413 } 414 415 lp = srhold(); 416 *rvalp = (str_vp(f->f_vnode) == lp->wl_vp); 417 srrele(lp); 418 releasef((int)arg); 419 return (0); 420 421 case I_POP: 422 /* 423 * We need to serialize I_POP operations with 424 * SRIOCSREDIR operations so we don't accidently 425 * remove the redirection module from a stream. 426 */ 427 mutex_enter(&iwscn_redirect_lock); 428 lp = srhold(); 429 430 /* 431 * Here we need to protect against process that might 432 * try to pop off the redirection module from the 433 * redirected stream. Popping other modules is allowed. 434 * 435 * It's ok to hold iwscn_list_lock while doing the 436 * I_LOOK since it's such a simple operation. 437 */ 438 (void) strioctl(lp->wl_vp, I_LOOK, (intptr_t)modname, 439 FKIOCTL, K_TO_K, cred, rvalp); 440 441 if (strcmp(STRREDIR_MOD, modname) == 0) { 442 srrele(lp); 443 mutex_exit(&iwscn_redirect_lock); 444 return (EINVAL); 445 } 446 447 /* Process the ioctl normally */ 448 error = VOP_IOCTL(lp->wl_vp, cmd, arg, flag, cred, rvalp); 449 450 srrele(lp); 451 mutex_exit(&iwscn_redirect_lock); 452 return (error); 453 } 454 455 /* Process the ioctl normally */ 456 lp = srhold(); 457 error = VOP_IOCTL(lp->wl_vp, cmd, arg, flag, cred, rvalp); 458 srrele(lp); 459 return (error); 460 } 461 462 /* ARGSUSED */ 463 static int 464 iwscnopen(dev_t *devp, int flag, int state, cred_t *cred) 465 { 466 iwscn_list_t *lp; 467 vnode_t *vp = rwsconsvp; 468 469 if (state != OTYP_CHR) 470 return (ENXIO); 471 472 if (getminor(*devp) != 0) 473 return (ENXIO); 474 475 /* 476 * You can't really open us until the console subsystem 477 * has been configured. 478 */ 479 if (rwsconsvp == NULL) 480 return (ENXIO); 481 482 /* 483 * Check if this is the first open of this device or if 484 * there is currently no redirection going on. (Ie, we're 485 * sending output to underlying console device.) 486 */ 487 mutex_enter(&iwscn_list_lock); 488 if ((iwscn_list == NULL) || (iwscn_list->wl_vp == str_vp(vp))) { 489 int error = 0; 490 491 /* Don't hold the list lock across an VOP_OPEN */ 492 mutex_exit(&iwscn_list_lock); 493 494 /* 495 * There is currently no redirection going on. 496 * pass this open request onto the console driver 497 */ 498 error = VOP_OPEN(&vp, flag, cred); 499 if (error != 0) 500 return (error); 501 502 /* Re-aquire the list lock */ 503 mutex_enter(&iwscn_list_lock); 504 505 if (iwscn_list == NULL) { 506 /* Save this vnode on the redirection list */ 507 srpush(vp, B_TRUE); 508 } else { 509 /* 510 * In this case there must already be a copy of 511 * this vnode on the list, so we can free up this one. 512 */ 513 (void) VOP_CLOSE(vp, flag, 1, (offset_t)0, cred); 514 } 515 } 516 517 /* 518 * XXX This is an ugly legacy hack that has been around 519 * forever. This code is here because this driver (the 520 * iwscn driver) is a character driver layered over a 521 * streams driver. 522 * 523 * Normally streams recieve notification whenever a process 524 * closes its last reference to that stream so that it can 525 * clean up any signal handling related configuration. (Ie, 526 * when a stream is configured to deliver a signal to a 527 * process upon certain events.) This is a feature supported 528 * by the streams framework. 529 * 530 * But character/block drivers don't recieve this type 531 * of notification. A character/block driver's close routine 532 * is only invoked upon the last close of the device. This 533 * is an artifact of the multiple open/single close driver 534 * model currently supported by solaris. 535 * 536 * So a problem occurs when a character driver layers itself 537 * on top of a streams driver. Since this driver doesn't always 538 * receive a close notification when a process closes its 539 * last reference to it, this driver can't tell the stream 540 * it's layered upon to clean up any signal handling 541 * configuration for that process. 542 * 543 * So here we hack around that by manually cleaning up the 544 * signal handling list upon each open. It doesn't guarantee 545 * that the signaling handling data stored in the stream will 546 * always be up to date, but it'll be more up to date than 547 * it would be if we didn't do this. 548 * 549 * The real way to solve this problem would be to change 550 * the device framework from an multiple open/single close 551 * model to a multiple open/multiple close model. Then 552 * character/block drivers could pass on close requests 553 * to streams layered underneath. 554 */ 555 str_cn_clean(VTOS(rwsconsvp)->s_commonvp); 556 for (lp = iwscn_list; lp != NULL; lp = lp->wl_next) { 557 ASSERT(lp->wl_vp->v_stream != NULL); 558 str_cn_clean(lp->wl_vp); 559 } 560 561 mutex_exit(&iwscn_list_lock); 562 return (0); 563 } 564 565 /* ARGSUSED */ 566 static int 567 iwscnclose(dev_t dev, int flag, int state, cred_t *cred) 568 { 569 ASSERT(getminor(dev) == 0); 570 571 if (state != OTYP_CHR) 572 return (ENXIO); 573 574 mutex_enter(&iwscn_list_lock); 575 576 /* Remove all outstanding redirections */ 577 while (iwscn_list != NULL) 578 (void) srrm(iwscn_list->wl_vp, B_TRUE); 579 iwscn_list = NULL; 580 581 mutex_exit(&iwscn_list_lock); 582 return (0); 583 } 584 585 /*ARGSUSED*/ 586 static int 587 iwscnattach(dev_info_t *devi, ddi_attach_cmd_t cmd) 588 { 589 /* 590 * This is a pseudo device so there will never be more than 591 * one instance attached at a time 592 */ 593 ASSERT(iwscn_dip == NULL); 594 595 if (ddi_create_minor_node(devi, "iwscn", S_IFCHR, 596 0, DDI_PSEUDO, NULL) == DDI_FAILURE) { 597 return (DDI_FAILURE); 598 } 599 600 iwscn_dip = devi; 601 mutex_init(&iwscn_list_lock, NULL, MUTEX_DRIVER, NULL); 602 mutex_init(&iwscn_redirect_lock, NULL, MUTEX_DRIVER, NULL); 603 cv_init(&iwscn_list_cv, NULL, CV_DRIVER, NULL); 604 605 return (DDI_SUCCESS); 606 } 607 608 /* ARGSUSED */ 609 static int 610 iwscninfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 611 { 612 int error; 613 614 switch (infocmd) { 615 case DDI_INFO_DEVT2DEVINFO: 616 if (iwscn_dip == NULL) { 617 error = DDI_FAILURE; 618 } else { 619 *result = (void *)iwscn_dip; 620 error = DDI_SUCCESS; 621 } 622 break; 623 case DDI_INFO_DEVT2INSTANCE: 624 *result = (void *)0; 625 error = DDI_SUCCESS; 626 break; 627 default: 628 error = DDI_FAILURE; 629 } 630 return (error); 631 } 632 633 struct cb_ops iwscn_cb_ops = { 634 iwscnopen, /* open */ 635 iwscnclose, /* close */ 636 nodev, /* strategy */ 637 nodev, /* print */ 638 nodev, /* dump */ 639 iwscnread, /* read */ 640 iwscnwrite, /* write */ 641 iwscnioctl, /* ioctl */ 642 nodev, /* devmap */ 643 nodev, /* mmap */ 644 nodev, /* segmap */ 645 iwscnpoll, /* poll */ 646 ddi_prop_op, /* cb_prop_op */ 647 NULL, /* streamtab */ 648 D_MP /* Driver compatibility flag */ 649 }; 650 651 struct dev_ops iwscn_ops = { 652 DEVO_REV, /* devo_rev, */ 653 0, /* refcnt */ 654 iwscninfo, /* info */ 655 nulldev, /* identify */ 656 nulldev, /* probe */ 657 iwscnattach, /* attach */ 658 nodev, /* detach */ 659 nodev, /* reset */ 660 &iwscn_cb_ops, /* driver operations */ 661 NULL /* bus operations */ 662 }; 663 664 /* 665 * Module linkage information for the kernel. 666 */ 667 static struct modldrv modldrv = { 668 &mod_driverops, /* Type of module. This one is a pseudo driver */ 669 "Workstation Redirection driver %I%", 670 &iwscn_ops, /* driver ops */ 671 }; 672 673 static struct modlinkage modlinkage = { 674 MODREV_1, 675 &modldrv, 676 NULL 677 }; 678 679 int 680 _init(void) 681 { 682 return (mod_install(&modlinkage)); 683 } 684 685 int 686 _fini(void) 687 { 688 return (EBUSY); 689 } 690 691 int 692 _info(struct modinfo *modinfop) 693 { 694 return (mod_info(&modlinkage, modinfop)); 695 } 696