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.
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.
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]
23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
29 * workstation console redirecting driver
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
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
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.
49 #include <sys/types.h>
50 #include <sys/sysmacros.h>
52 #include <sys/param.h>
53 #include <sys/systm.h>
54 #include <sys/signal.h>
58 #include <sys/vnode.h>
63 #include <sys/stream.h>
64 #include <sys/stropts.h>
65 #include <sys/strsubr.h>
67 #include <sys/debug.h>
68 #include <sys/strredir.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>
81 static dev_info_t
*iwscn_dip
;
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.
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 */
94 static iwscn_list_t
*iwscn_list
;
97 * iwscn_list_lock serializes modifications to the global iwscn_list list.
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.
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.
106 * If both iwscn_redirect_lock and iwscn_list_lock must be held then
107 * iwscn_redirect_lock must be acquired first.
109 static kcondvar_t iwscn_list_cv
;
110 static kmutex_t iwscn_list_lock
;
111 static kmutex_t iwscn_redirect_lock
;
114 * Routines for managing iwscn_list
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.
127 ASSERT(vp
->v_stream
!= NULL
);
128 return (vp
->v_stream
->sd_vnode
);
132 * Interrupt any operations that may be outstanding against this vnode.
133 * optionally, wait for them to complete.
136 srinterrupt(iwscn_list_t
*lp
, boolean_t wait
)
138 ASSERT(MUTEX_HELD(&iwscn_list_lock
));
140 while (lp
->wl_ref_cnt
!= 0) {
141 strsetrerror(lp
->wl_vp
, EINTR
, 0, NULL
);
142 strsetwerror(lp
->wl_vp
, EINTR
, 0, NULL
);
145 cv_wait(&iwscn_list_cv
, &iwscn_list_lock
);
150 * Remove vp from the redirection list rooted at iwscn_list, should it
151 * be there. Return a pointer to the removed entry.
153 static iwscn_list_t
*
156 iwscn_list_t
*lp
, **lpp
;
158 ASSERT(MUTEX_HELD(&iwscn_list_lock
));
160 /* Get the stream vnode */
164 /* Look for this vnode on the redirection list */
165 for (lpp
= &iwscn_list
; (lp
= *lpp
) != NULL
; lpp
= &lp
->wl_next
) {
170 /* Found it, remove this entry from the redirection list */
177 * Push vp onto the redirection list.
178 * If it's already there move it to the front position.
181 srpush(vnode_t
*vp
, boolean_t is_console
)
185 ASSERT(MUTEX_HELD(&iwscn_list_lock
));
187 /* Get the stream vnode */
191 /* Check if it's already on the redirection list */
192 if ((lp
= srrm(vp
)) == NULL
) {
193 lp
= kmem_zalloc(sizeof (*lp
), KM_SLEEP
);
195 lp
->wl_is_console
= is_console
;
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.
202 lp
->wl_next
= iwscn_list
;
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.
213 srpop(vnode_t
*vp
, boolean_t close
)
215 iwscn_list_t
*tlp
; /* This target's entry */
216 iwscn_list_t
*lp
, **lpp
;
218 mutex_enter(&iwscn_list_lock
);
221 * Ensure no further operations are directed at the target
222 * by removing it from the redirection list.
224 if ((tlp
= srrm(vp
)) == NULL
) {
225 /* vnode wasn't in the list */
226 mutex_exit(&iwscn_list_lock
);
230 * Terminate any current operations.
231 * If we're closing, wait until they complete.
233 srinterrupt(tlp
, close
);
236 /* We're finished with this target */
237 kmem_free(tlp
, sizeof (*tlp
));
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.
246 for (lpp
= &iwscn_list
; (lp
= *lpp
) != NULL
; )
251 mutex_exit(&iwscn_list_lock
);
254 /* Get a hold on the current target */
255 static iwscn_list_t
*
260 mutex_enter(&iwscn_list_lock
);
261 ASSERT(iwscn_list
!= NULL
);
263 ASSERT(lp
->wl_ref_cnt
>= 0);
265 mutex_exit(&iwscn_list_lock
);
270 /* Release a hold on an entry from the redirection list */
272 srrele(iwscn_list_t
*lp
)
275 mutex_enter(&iwscn_list_lock
);
276 ASSERT(lp
->wl_ref_cnt
> 0);
278 cv_broadcast(&iwscn_list_cv
);
279 mutex_exit(&iwscn_list_lock
);
283 iwscnread(dev_t dev
, uio_t
*uio
, cred_t
*cred
)
288 ASSERT(getminor(dev
) == 0);
291 error
= strread(lp
->wl_vp
, uio
, cred
);
298 iwscnwrite(dev_t dev
, uio_t
*uio
, cred_t
*cred
)
303 ASSERT(getminor(dev
) == 0);
306 error
= strwrite(lp
->wl_vp
, uio
, cred
);
313 iwscnpoll(dev_t dev
, short events
, int anyyet
, short *reventsp
,
314 struct pollhead
**phpp
)
319 ASSERT(getminor(dev
) == 0);
322 error
= fop_poll(lp
->wl_vp
, events
, anyyet
, reventsp
, phpp
, NULL
);
329 iwscnioctl(dev_t dev
, int cmd
, intptr_t arg
, int flag
,
330 cred_t
*cred
, int *rvalp
)
334 char modname
[FMNAMESZ
+ 1] = " ";
337 ASSERT(getminor(dev
) == 0);
341 /* Serialize all pushes of the redirection module */
342 mutex_enter(&iwscn_redirect_lock
);
345 * Find the vnode corresponding to the file descriptor
346 * argument and verify that it names a stream.
348 if ((f
= getf((int)arg
)) == NULL
) {
349 mutex_exit(&iwscn_redirect_lock
);
352 if (f
->f_vnode
->v_stream
== NULL
) {
354 mutex_exit(&iwscn_redirect_lock
);
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.
363 if (str_vp(f
->f_vnode
) == str_vp(rwsconsvp
)) {
365 mutex_exit(&iwscn_redirect_lock
);
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.
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) {
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.)
384 error
= strioctl(f
->f_vnode
, I_PUSH
,
385 (intptr_t)STRREDIR_MOD
, FKIOCTL
, K_TO_K
,
390 mutex_exit(&iwscn_redirect_lock
);
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
);
401 mutex_exit(&iwscn_redirect_lock
);
406 * Find the vnode corresponding to the file descriptor
407 * argument and verify that it names a stream.
409 if ((f
= getf((int)arg
)) == NULL
) {
412 if (f
->f_vnode
->v_stream
== NULL
) {
418 *rvalp
= (str_vp(f
->f_vnode
) == lp
->wl_vp
);
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.
429 mutex_enter(&iwscn_redirect_lock
);
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.
437 * It's ok to hold iwscn_list_lock while doing the
438 * I_LOOK since it's such a simple operation.
440 (void) strioctl(lp
->wl_vp
, I_LOOK
, (intptr_t)modname
,
441 FKIOCTL
, K_TO_K
, cred
, rvalp
);
443 if (strcmp(STRREDIR_MOD
, modname
) == 0) {
445 mutex_exit(&iwscn_redirect_lock
);
449 /* Process the ioctl normally */
450 error
= fop_ioctl(lp
->wl_vp
, cmd
, arg
, flag
, cred
, rvalp
, NULL
);
453 mutex_exit(&iwscn_redirect_lock
);
457 /* Process the ioctl normally */
459 error
= fop_ioctl(lp
->wl_vp
, cmd
, arg
, flag
, cred
, rvalp
, NULL
);
466 iwscnopen(dev_t
*devp
, int flag
, int state
, cred_t
*cred
)
469 vnode_t
*vp
= rwsconsvp
;
471 if (state
!= OTYP_CHR
)
474 if (getminor(*devp
) != 0)
478 * You can't really open us until the console subsystem
479 * has been configured.
481 if (rwsconsvp
== NULL
)
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.)
489 mutex_enter(&iwscn_list_lock
);
490 if ((iwscn_list
== NULL
) || (iwscn_list
->wl_vp
== str_vp(vp
))) {
493 /* Don't hold the list lock across an fop_open */
494 mutex_exit(&iwscn_list_lock
);
497 * There is currently no redirection going on.
498 * pass this open request onto the console driver
500 error
= fop_open(&vp
, flag
, cred
, NULL
);
504 /* Re-acquire the list lock */
505 mutex_enter(&iwscn_list_lock
);
507 if (iwscn_list
== NULL
) {
508 /* Save this vnode on the redirection list */
512 * In this case there must already be a copy of
513 * this vnode on the list, so we can free up this one.
515 (void) fop_close(vp
, flag
, 1, 0, cred
, NULL
);
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
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.
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.
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.
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.
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.
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
);
563 mutex_exit(&iwscn_list_lock
);
569 iwscnclose(dev_t dev
, int flag
, int state
, cred_t
*cred
)
573 ASSERT(getminor(dev
) == 0);
575 if (state
!= OTYP_CHR
)
578 mutex_enter(&iwscn_list_lock
);
580 * Remove each entry from the redirection list, terminate any
581 * current operations, wait for them to finish, then free the entry.
583 while (iwscn_list
!= NULL
) {
584 lp
= srrm(iwscn_list
->wl_vp
);
586 srinterrupt(lp
, B_TRUE
);
588 if (lp
->wl_is_console
== B_TRUE
)
589 /* Close the underlying console device. */
590 (void) fop_close(lp
->wl_vp
, 0, 1, 0, kcred
,
593 kmem_free(lp
, sizeof (*lp
));
595 mutex_exit(&iwscn_list_lock
);
601 iwscnattach(dev_info_t
*devi
, ddi_attach_cmd_t cmd
)
604 * This is a pseudo device so there will never be more than
605 * one instance attached at a time
607 ASSERT(iwscn_dip
== NULL
);
609 if (ddi_create_minor_node(devi
, "iwscn", S_IFCHR
,
610 0, DDI_PSEUDO
, 0) == DDI_FAILURE
) {
611 return (DDI_FAILURE
);
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
);
619 return (DDI_SUCCESS
);
624 iwscninfo(dev_info_t
*dip
, ddi_info_cmd_t infocmd
, void *arg
, void **result
)
629 case DDI_INFO_DEVT2DEVINFO
:
630 if (iwscn_dip
== NULL
) {
633 *result
= (void *)iwscn_dip
;
637 case DDI_INFO_DEVT2INSTANCE
:
647 struct cb_ops iwscn_cb_ops
= {
648 iwscnopen
, /* open */
649 iwscnclose
, /* close */
650 nodev
, /* strategy */
653 iwscnread
, /* read */
654 iwscnwrite
, /* write */
655 iwscnioctl
, /* ioctl */
659 iwscnpoll
, /* poll */
660 ddi_prop_op
, /* cb_prop_op */
661 NULL
, /* streamtab */
662 D_MP
/* Driver compatibility flag */
665 struct dev_ops iwscn_ops
= {
666 DEVO_REV
, /* devo_rev, */
668 iwscninfo
, /* info */
669 nulldev
, /* identify */
671 iwscnattach
, /* attach */
674 &iwscn_cb_ops
, /* driver operations */
675 NULL
, /* bus operations */
677 ddi_quiesce_not_needed
, /* quiesce */
681 * Module linkage information for the kernel.
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 */
689 static struct modlinkage modlinkage
= {
698 return (mod_install(&modlinkage
));
708 _info(struct modinfo
*modinfop
)
710 return (mod_info(&modlinkage
, modinfop
));