17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate * CDDL HEADER START
37c478bd9Sstevel@tonic-gate *
47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the
58ec5a142Sedp * Common Development and Distribution License (the "License").
68ec5a142Sedp * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate *
87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate * and limitations under the License.
127c478bd9Sstevel@tonic-gate *
137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate *
197c478bd9Sstevel@tonic-gate * CDDL HEADER END
207c478bd9Sstevel@tonic-gate */
218ec5a142Sedp
227c478bd9Sstevel@tonic-gate /*
23*19397407SSherry Moore * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
247c478bd9Sstevel@tonic-gate * Use is subject to license terms.
257c478bd9Sstevel@tonic-gate */
267c478bd9Sstevel@tonic-gate
277c478bd9Sstevel@tonic-gate
287c478bd9Sstevel@tonic-gate /*
298ec5a142Sedp * workstation console redirecting driver
307c478bd9Sstevel@tonic-gate *
317c478bd9Sstevel@tonic-gate * Redirects all I/O through a given device instance to the device designated
327c478bd9Sstevel@tonic-gate * as the current target, as given by the vnode associated with the first
337c478bd9Sstevel@tonic-gate * entry in the list of redirections for the given device instance. The
347c478bd9Sstevel@tonic-gate * implementation assumes that this vnode denotes a STREAMS device; this is
357c478bd9Sstevel@tonic-gate * perhaps a bug.
367c478bd9Sstevel@tonic-gate *
377c478bd9Sstevel@tonic-gate * Supports the SRIOCSREDIR ioctl for designating a new redirection target.
387c478bd9Sstevel@tonic-gate * The new target is added to the front of a list of potentially active
397c478bd9Sstevel@tonic-gate * designees. Should the device at the front of this list be closed, the new
407c478bd9Sstevel@tonic-gate * front entry assumes active duty. (Stated differently, redirection targets
417c478bd9Sstevel@tonic-gate * stack, except that it's possible for entries in the interior of the stack
427c478bd9Sstevel@tonic-gate * to go away.)
437c478bd9Sstevel@tonic-gate *
447c478bd9Sstevel@tonic-gate * Supports the SRIOCISREDIR ioctl for inquiring whether the descriptor given
457c478bd9Sstevel@tonic-gate * as argument is the current front of the redirection list associated with
467c478bd9Sstevel@tonic-gate * the descriptor on which the ioctl was issued.
477c478bd9Sstevel@tonic-gate */
487c478bd9Sstevel@tonic-gate
497c478bd9Sstevel@tonic-gate #include <sys/types.h>
507c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
517c478bd9Sstevel@tonic-gate #include <sys/open.h>
527c478bd9Sstevel@tonic-gate #include <sys/param.h>
537c478bd9Sstevel@tonic-gate #include <sys/systm.h>
547c478bd9Sstevel@tonic-gate #include <sys/signal.h>
557c478bd9Sstevel@tonic-gate #include <sys/cred.h>
567c478bd9Sstevel@tonic-gate #include <sys/user.h>
577c478bd9Sstevel@tonic-gate #include <sys/proc.h>
587c478bd9Sstevel@tonic-gate #include <sys/vnode.h>
597c478bd9Sstevel@tonic-gate #include <sys/uio.h>
607c478bd9Sstevel@tonic-gate #include <sys/file.h>
617c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
627c478bd9Sstevel@tonic-gate #include <sys/stat.h>
637c478bd9Sstevel@tonic-gate #include <sys/stream.h>
647c478bd9Sstevel@tonic-gate #include <sys/stropts.h>
657c478bd9Sstevel@tonic-gate #include <sys/strsubr.h>
667c478bd9Sstevel@tonic-gate #include <sys/poll.h>
677c478bd9Sstevel@tonic-gate #include <sys/debug.h>
687c478bd9Sstevel@tonic-gate #include <sys/strredir.h>
697c478bd9Sstevel@tonic-gate #include <sys/conf.h>
707c478bd9Sstevel@tonic-gate #include <sys/ddi.h>
717c478bd9Sstevel@tonic-gate #include <sys/sunddi.h>
728ec5a142Sedp #include <sys/errno.h>
738ec5a142Sedp #include <sys/modctl.h>
748ec5a142Sedp #include <sys/sunldi.h>
758ec5a142Sedp #include <sys/consdev.h>
768ec5a142Sedp #include <sys/fs/snode.h>
777c478bd9Sstevel@tonic-gate
787c478bd9Sstevel@tonic-gate /*
798ec5a142Sedp * Global data
807c478bd9Sstevel@tonic-gate */
817c478bd9Sstevel@tonic-gate static dev_info_t *iwscn_dip;
827c478bd9Sstevel@tonic-gate
837c478bd9Sstevel@tonic-gate /*
848ec5a142Sedp * We record the list of redirections as a linked list of iwscn_list_t
858ec5a142Sedp * structures. We need to keep track of the target's vp, so that
868ec5a142Sedp * we can vector reads, writes, etc. off to the current designee.
877c478bd9Sstevel@tonic-gate */
888ec5a142Sedp typedef struct _iwscn_list {
898ec5a142Sedp struct _iwscn_list *wl_next; /* next entry */
908ec5a142Sedp vnode_t *wl_vp; /* target's vnode */
918ec5a142Sedp int wl_ref_cnt; /* operation in progress */
928ec5a142Sedp boolean_t wl_is_console; /* is the real console */
938ec5a142Sedp } iwscn_list_t;
948ec5a142Sedp static iwscn_list_t *iwscn_list;
957c478bd9Sstevel@tonic-gate
967c478bd9Sstevel@tonic-gate /*
978ec5a142Sedp * iwscn_list_lock serializes modifications to the global iwscn_list list.
987c478bd9Sstevel@tonic-gate *
998ec5a142Sedp * iwscn_list_cv is used when freeing an entry from iwscn_list to allow
1008ec5a142Sedp * the caller to wait till the wl_ref_cnt field is zero.
1018ec5a142Sedp *
1028ec5a142Sedp * iwscn_redirect_lock is used to serialize redirection requests. This
1038ec5a142Sedp * is required to ensure that all active redirection streams have
1048ec5a142Sedp * the redirection streams module (redirmod) pushed on them.
1058ec5a142Sedp *
1068ec5a142Sedp * If both iwscn_redirect_lock and iwscn_list_lock must be held then
107da6c28aaSamw * iwscn_redirect_lock must be acquired first.
1087c478bd9Sstevel@tonic-gate */
1098ec5a142Sedp static kcondvar_t iwscn_list_cv;
1108ec5a142Sedp static kmutex_t iwscn_list_lock;
1118ec5a142Sedp static kmutex_t iwscn_redirect_lock;
1127c478bd9Sstevel@tonic-gate
1137c478bd9Sstevel@tonic-gate /*
1148ec5a142Sedp * Routines for managing iwscn_list
1157c478bd9Sstevel@tonic-gate */
1168ec5a142Sedp static vnode_t *
str_vp(vnode_t * vp)1178ec5a142Sedp str_vp(vnode_t *vp)
1187c478bd9Sstevel@tonic-gate {
1198ec5a142Sedp /*
1208ec5a142Sedp * Here we switch to using the vnode that is linked
1218ec5a142Sedp * to from the stream queue. (In the case of device
1228ec5a142Sedp * streams this will correspond to the common vnode
1238ec5a142Sedp * for the device.) The reason we use this vnode
1248ec5a142Sedp * is that when wcmclose() calls srpop(), this is the
1258ec5a142Sedp * only vnode that it has access to.
1268ec5a142Sedp */
1278ec5a142Sedp ASSERT(vp->v_stream != NULL);
1288ec5a142Sedp return (vp->v_stream->sd_vnode);
1297c478bd9Sstevel@tonic-gate }
1307c478bd9Sstevel@tonic-gate
1317c478bd9Sstevel@tonic-gate /*
132e493d0faSns92644 * Interrupt any operations that may be outstanding against this vnode.
133e493d0faSns92644 * optionally, wait for them to complete.
134e493d0faSns92644 */
135e493d0faSns92644 static void
srinterrupt(iwscn_list_t * lp,boolean_t wait)136e493d0faSns92644 srinterrupt(iwscn_list_t *lp, boolean_t wait)
137e493d0faSns92644 {
138e493d0faSns92644 ASSERT(MUTEX_HELD(&iwscn_list_lock));
139e493d0faSns92644
140e493d0faSns92644 while (lp->wl_ref_cnt != 0) {
141e493d0faSns92644 strsetrerror(lp->wl_vp, EINTR, 0, NULL);
142e493d0faSns92644 strsetwerror(lp->wl_vp, EINTR, 0, NULL);
143e493d0faSns92644 if (!wait)
144e493d0faSns92644 break;
145e493d0faSns92644 cv_wait(&iwscn_list_cv, &iwscn_list_lock);
146e493d0faSns92644 }
147e493d0faSns92644 }
148e493d0faSns92644
149e493d0faSns92644 /*
1508ec5a142Sedp * Remove vp from the redirection list rooted at iwscn_list, should it
151e493d0faSns92644 * be there. Return a pointer to the removed entry.
1527c478bd9Sstevel@tonic-gate */
1538ec5a142Sedp static iwscn_list_t *
srrm(vnode_t * vp)154e493d0faSns92644 srrm(vnode_t *vp)
1558ec5a142Sedp {
1568ec5a142Sedp iwscn_list_t *lp, **lpp;
1578ec5a142Sedp
1588ec5a142Sedp ASSERT(MUTEX_HELD(&iwscn_list_lock));
1598ec5a142Sedp
1608ec5a142Sedp /* Get the stream vnode */
1618ec5a142Sedp vp = str_vp(vp);
1628ec5a142Sedp ASSERT(vp);
1638ec5a142Sedp
1648ec5a142Sedp /* Look for this vnode on the redirection list */
1658ec5a142Sedp for (lpp = &iwscn_list; (lp = *lpp) != NULL; lpp = &lp->wl_next) {
1668ec5a142Sedp if (lp->wl_vp == vp)
1678ec5a142Sedp break;
1688ec5a142Sedp }
169e493d0faSns92644 if (lp != NULL)
1708ec5a142Sedp /* Found it, remove this entry from the redirection list */
1718ec5a142Sedp *lpp = lp->wl_next;
1728ec5a142Sedp
1738ec5a142Sedp return (lp);
1748ec5a142Sedp }
1758ec5a142Sedp
1768ec5a142Sedp /*
1778ec5a142Sedp * Push vp onto the redirection list.
1788ec5a142Sedp * If it's already there move it to the front position.
1798ec5a142Sedp */
1808ec5a142Sedp static void
srpush(vnode_t * vp,boolean_t is_console)1818ec5a142Sedp srpush(vnode_t *vp, boolean_t is_console)
1828ec5a142Sedp {
1838ec5a142Sedp iwscn_list_t *lp;
1848ec5a142Sedp
1858ec5a142Sedp ASSERT(MUTEX_HELD(&iwscn_list_lock));
1868ec5a142Sedp
1878ec5a142Sedp /* Get the stream vnode */
1888ec5a142Sedp vp = str_vp(vp);
1898ec5a142Sedp ASSERT(vp);
1908ec5a142Sedp
1918ec5a142Sedp /* Check if it's already on the redirection list */
192e493d0faSns92644 if ((lp = srrm(vp)) == NULL) {
1938ec5a142Sedp lp = kmem_zalloc(sizeof (*lp), KM_SLEEP);
1948ec5a142Sedp lp->wl_vp = vp;
195e493d0faSns92644 lp->wl_is_console = is_console;
1968ec5a142Sedp }
1978ec5a142Sedp /*
1988ec5a142Sedp * Note that if this vnode was already somewhere on the redirection
1998ec5a142Sedp * list then we removed it above and are now bumping it up to the
200e493d0faSns92644 * front of the redirection list.
2018ec5a142Sedp */
2028ec5a142Sedp lp->wl_next = iwscn_list;
2038ec5a142Sedp iwscn_list = lp;
2048ec5a142Sedp }
2058ec5a142Sedp
2068ec5a142Sedp /*
207e493d0faSns92644 * This vnode is no longer a valid redirection target. Terminate any current
208e493d0faSns92644 * operations. If closing, wait for them to complete, then free the entry.
209e493d0faSns92644 * If called because a hangup has occurred, just deprecate the entry to ensure
210e493d0faSns92644 * it won't become the target again.
2118ec5a142Sedp */
2128ec5a142Sedp void
srpop(vnode_t * vp,boolean_t close)213e493d0faSns92644 srpop(vnode_t *vp, boolean_t close)
2148ec5a142Sedp {
215e493d0faSns92644 iwscn_list_t *tlp; /* This target's entry */
216e493d0faSns92644 iwscn_list_t *lp, **lpp;
217e493d0faSns92644
2188ec5a142Sedp mutex_enter(&iwscn_list_lock);
219e493d0faSns92644
220e493d0faSns92644 /*
221e493d0faSns92644 * Ensure no further operations are directed at the target
222e493d0faSns92644 * by removing it from the redirection list.
223e493d0faSns92644 */
224e493d0faSns92644 if ((tlp = srrm(vp)) == NULL) {
225e493d0faSns92644 /* vnode wasn't in the list */
226e493d0faSns92644 mutex_exit(&iwscn_list_lock);
227e493d0faSns92644 return;
228e493d0faSns92644 }
229e493d0faSns92644 /*
230e493d0faSns92644 * Terminate any current operations.
231e493d0faSns92644 * If we're closing, wait until they complete.
232e493d0faSns92644 */
233e493d0faSns92644 srinterrupt(tlp, close);
234e493d0faSns92644
235e493d0faSns92644 if (close) {
236e493d0faSns92644 /* We're finished with this target */
237e493d0faSns92644 kmem_free(tlp, sizeof (*tlp));
238e493d0faSns92644 } else {
239e493d0faSns92644 /*
240e493d0faSns92644 * Deprecate the entry. There's no need for a flag to indicate
241e493d0faSns92644 * this state, it just needs to be moved to the back of the list
242e493d0faSns92644 * behind the underlying console device. Since the underlying
243e493d0faSns92644 * device anchors the list and is never removed, this entry can
244e493d0faSns92644 * never return to the front again to become the target.
245e493d0faSns92644 */
246e493d0faSns92644 for (lpp = &iwscn_list; (lp = *lpp) != NULL; )
247e493d0faSns92644 lpp = &lp->wl_next;
248e493d0faSns92644 tlp->wl_next = NULL;
249e493d0faSns92644 *lpp = tlp;
250e493d0faSns92644 }
2518ec5a142Sedp mutex_exit(&iwscn_list_lock);
2528ec5a142Sedp }
2538ec5a142Sedp
2548ec5a142Sedp /* Get a hold on the current target */
2558ec5a142Sedp static iwscn_list_t *
srhold()2568ec5a142Sedp srhold()
2578ec5a142Sedp {
2588ec5a142Sedp iwscn_list_t *lp;
2598ec5a142Sedp
2608ec5a142Sedp mutex_enter(&iwscn_list_lock);
2618ec5a142Sedp ASSERT(iwscn_list != NULL);
2628ec5a142Sedp lp = iwscn_list;
2638ec5a142Sedp ASSERT(lp->wl_ref_cnt >= 0);
2648ec5a142Sedp lp->wl_ref_cnt++;
2658ec5a142Sedp mutex_exit(&iwscn_list_lock);
2668ec5a142Sedp
2678ec5a142Sedp return (lp);
2688ec5a142Sedp }
2698ec5a142Sedp
2708ec5a142Sedp /* Release a hold on an entry from the redirection list */
2718ec5a142Sedp static void
srrele(iwscn_list_t * lp)2728ec5a142Sedp srrele(iwscn_list_t *lp)
2738ec5a142Sedp {
2748ec5a142Sedp ASSERT(lp != NULL);
2758ec5a142Sedp mutex_enter(&iwscn_list_lock);
2768ec5a142Sedp ASSERT(lp->wl_ref_cnt > 0);
2778ec5a142Sedp lp->wl_ref_cnt--;
2788ec5a142Sedp cv_broadcast(&iwscn_list_cv);
2798ec5a142Sedp mutex_exit(&iwscn_list_lock);
2808ec5a142Sedp }
2818ec5a142Sedp
2828ec5a142Sedp static int
iwscnread(dev_t dev,uio_t * uio,cred_t * cred)2838ec5a142Sedp iwscnread(dev_t dev, uio_t *uio, cred_t *cred)
2848ec5a142Sedp {
2858ec5a142Sedp iwscn_list_t *lp;
2868ec5a142Sedp int error;
2878ec5a142Sedp
2888ec5a142Sedp ASSERT(getminor(dev) == 0);
2898ec5a142Sedp
2908ec5a142Sedp lp = srhold();
2918ec5a142Sedp error = strread(lp->wl_vp, uio, cred);
2928ec5a142Sedp srrele(lp);
2938ec5a142Sedp
2948ec5a142Sedp return (error);
2958ec5a142Sedp }
2968ec5a142Sedp
2978ec5a142Sedp static int
iwscnwrite(dev_t dev,uio_t * uio,cred_t * cred)2988ec5a142Sedp iwscnwrite(dev_t dev, uio_t *uio, cred_t *cred)
2998ec5a142Sedp {
3008ec5a142Sedp iwscn_list_t *lp;
3018ec5a142Sedp int error;
3028ec5a142Sedp
3038ec5a142Sedp ASSERT(getminor(dev) == 0);
3048ec5a142Sedp
3058ec5a142Sedp lp = srhold();
3068ec5a142Sedp error = strwrite(lp->wl_vp, uio, cred);
3078ec5a142Sedp srrele(lp);
3088ec5a142Sedp
3098ec5a142Sedp return (error);
3108ec5a142Sedp }
3118ec5a142Sedp
3128ec5a142Sedp static int
iwscnpoll(dev_t dev,short events,int anyyet,short * reventsp,struct pollhead ** phpp)3138ec5a142Sedp iwscnpoll(dev_t dev, short events, int anyyet, short *reventsp,
3148ec5a142Sedp struct pollhead **phpp)
3158ec5a142Sedp {
3168ec5a142Sedp iwscn_list_t *lp;
3178ec5a142Sedp int error;
3188ec5a142Sedp
3198ec5a142Sedp ASSERT(getminor(dev) == 0);
3208ec5a142Sedp
3218ec5a142Sedp lp = srhold();
322da6c28aaSamw error = VOP_POLL(lp->wl_vp, events, anyyet, reventsp, phpp, NULL);
3238ec5a142Sedp srrele(lp);
3248ec5a142Sedp
3258ec5a142Sedp return (error);
3268ec5a142Sedp }
3278ec5a142Sedp
3288ec5a142Sedp static int
iwscnioctl(dev_t dev,int cmd,intptr_t arg,int flag,cred_t * cred,int * rvalp)3298ec5a142Sedp iwscnioctl(dev_t dev, int cmd, intptr_t arg, int flag,
3308ec5a142Sedp cred_t *cred, int *rvalp)
3318ec5a142Sedp {
3328ec5a142Sedp iwscn_list_t *lp;
3338ec5a142Sedp file_t *f;
3348ec5a142Sedp char modname[FMNAMESZ + 1] = " ";
3358ec5a142Sedp int error = 0;
3368ec5a142Sedp
3378ec5a142Sedp ASSERT(getminor(dev) == 0);
3388ec5a142Sedp
3398ec5a142Sedp switch (cmd) {
3408ec5a142Sedp case SRIOCSREDIR:
3418ec5a142Sedp /* Serialize all pushes of the redirection module */
3428ec5a142Sedp mutex_enter(&iwscn_redirect_lock);
3438ec5a142Sedp
3448ec5a142Sedp /*
3458ec5a142Sedp * Find the vnode corresponding to the file descriptor
3468ec5a142Sedp * argument and verify that it names a stream.
3478ec5a142Sedp */
3488ec5a142Sedp if ((f = getf((int)arg)) == NULL) {
3498ec5a142Sedp mutex_exit(&iwscn_redirect_lock);
3508ec5a142Sedp return (EBADF);
3518ec5a142Sedp }
3528ec5a142Sedp if (f->f_vnode->v_stream == NULL) {
3538ec5a142Sedp releasef((int)arg);
3548ec5a142Sedp mutex_exit(&iwscn_redirect_lock);
3558ec5a142Sedp return (ENOSTR);
3568ec5a142Sedp }
3578ec5a142Sedp
3588ec5a142Sedp /*
3598ec5a142Sedp * If the user is trying to redirect console output
3608ec5a142Sedp * back to the underlying console via SRIOCSREDIR
3618ec5a142Sedp * then they are evil and we'll stop them here.
3628ec5a142Sedp */
3638ec5a142Sedp if (str_vp(f->f_vnode) == str_vp(rwsconsvp)) {
3648ec5a142Sedp releasef((int)arg);
3658ec5a142Sedp mutex_exit(&iwscn_redirect_lock);
3668ec5a142Sedp return (EINVAL);
3678ec5a142Sedp }
3688ec5a142Sedp
3698ec5a142Sedp /*
3708ec5a142Sedp * Check if this stream already has the redirection
3718ec5a142Sedp * module pushed onto it. I_LOOK returns an error
3728ec5a142Sedp * if there are no modules pushed onto the stream.
3738ec5a142Sedp */
3748ec5a142Sedp (void) strioctl(f->f_vnode, I_LOOK, (intptr_t)modname,
3758ec5a142Sedp FKIOCTL, K_TO_K, cred, rvalp);
3768ec5a142Sedp if (strcmp(modname, STRREDIR_MOD) != 0) {
3778ec5a142Sedp
3788ec5a142Sedp /*
3798ec5a142Sedp * Push a new instance of the redirecting module onto
3808ec5a142Sedp * the stream, so that its close routine can notify
3818ec5a142Sedp * us when the overall stream is closed. (In turn,
3828ec5a142Sedp * we'll then remove it from the redirection list.)
3838ec5a142Sedp */
3848ec5a142Sedp error = strioctl(f->f_vnode, I_PUSH,
3858ec5a142Sedp (intptr_t)STRREDIR_MOD, FKIOCTL, K_TO_K,
3868ec5a142Sedp cred, rvalp);
3878ec5a142Sedp
3888ec5a142Sedp if (error != 0) {
3898ec5a142Sedp releasef((int)arg);
3908ec5a142Sedp mutex_exit(&iwscn_redirect_lock);
3918ec5a142Sedp return (error);
3928ec5a142Sedp }
3938ec5a142Sedp }
3948ec5a142Sedp
3958ec5a142Sedp /* Push it onto the redirection stack */
3968ec5a142Sedp mutex_enter(&iwscn_list_lock);
3978ec5a142Sedp srpush(f->f_vnode, B_FALSE);
3988ec5a142Sedp mutex_exit(&iwscn_list_lock);
3998ec5a142Sedp
4008ec5a142Sedp releasef((int)arg);
4018ec5a142Sedp mutex_exit(&iwscn_redirect_lock);
4028ec5a142Sedp return (0);
4038ec5a142Sedp
4048ec5a142Sedp case SRIOCISREDIR:
4058ec5a142Sedp /*
4068ec5a142Sedp * Find the vnode corresponding to the file descriptor
4078ec5a142Sedp * argument and verify that it names a stream.
4088ec5a142Sedp */
4098ec5a142Sedp if ((f = getf((int)arg)) == NULL) {
4108ec5a142Sedp return (EBADF);
4118ec5a142Sedp }
4128ec5a142Sedp if (f->f_vnode->v_stream == NULL) {
4138ec5a142Sedp releasef((int)arg);
4148ec5a142Sedp return (ENOSTR);
4158ec5a142Sedp }
4168ec5a142Sedp
4178ec5a142Sedp lp = srhold();
4188ec5a142Sedp *rvalp = (str_vp(f->f_vnode) == lp->wl_vp);
4198ec5a142Sedp srrele(lp);
4208ec5a142Sedp releasef((int)arg);
4218ec5a142Sedp return (0);
4228ec5a142Sedp
4238ec5a142Sedp case I_POP:
4248ec5a142Sedp /*
4258ec5a142Sedp * We need to serialize I_POP operations with
4268ec5a142Sedp * SRIOCSREDIR operations so we don't accidently
4278ec5a142Sedp * remove the redirection module from a stream.
4288ec5a142Sedp */
4298ec5a142Sedp mutex_enter(&iwscn_redirect_lock);
4308ec5a142Sedp lp = srhold();
4318ec5a142Sedp
4328ec5a142Sedp /*
4338ec5a142Sedp * Here we need to protect against process that might
4348ec5a142Sedp * try to pop off the redirection module from the
4358ec5a142Sedp * redirected stream. Popping other modules is allowed.
4368ec5a142Sedp *
4378ec5a142Sedp * It's ok to hold iwscn_list_lock while doing the
4388ec5a142Sedp * I_LOOK since it's such a simple operation.
4398ec5a142Sedp */
4408ec5a142Sedp (void) strioctl(lp->wl_vp, I_LOOK, (intptr_t)modname,
4418ec5a142Sedp FKIOCTL, K_TO_K, cred, rvalp);
4428ec5a142Sedp
4438ec5a142Sedp if (strcmp(STRREDIR_MOD, modname) == 0) {
4448ec5a142Sedp srrele(lp);
4458ec5a142Sedp mutex_exit(&iwscn_redirect_lock);
4468ec5a142Sedp return (EINVAL);
4478ec5a142Sedp }
4488ec5a142Sedp
4498ec5a142Sedp /* Process the ioctl normally */
450da6c28aaSamw error = VOP_IOCTL(lp->wl_vp, cmd, arg, flag, cred, rvalp, NULL);
4518ec5a142Sedp
4528ec5a142Sedp srrele(lp);
4538ec5a142Sedp mutex_exit(&iwscn_redirect_lock);
4548ec5a142Sedp return (error);
4558ec5a142Sedp }
4568ec5a142Sedp
4578ec5a142Sedp /* Process the ioctl normally */
4588ec5a142Sedp lp = srhold();
459da6c28aaSamw error = VOP_IOCTL(lp->wl_vp, cmd, arg, flag, cred, rvalp, NULL);
4608ec5a142Sedp srrele(lp);
4618ec5a142Sedp return (error);
4628ec5a142Sedp }
4638ec5a142Sedp
4648ec5a142Sedp /* ARGSUSED */
4658ec5a142Sedp static int
iwscnopen(dev_t * devp,int flag,int state,cred_t * cred)4668ec5a142Sedp iwscnopen(dev_t *devp, int flag, int state, cred_t *cred)
4678ec5a142Sedp {
4688ec5a142Sedp iwscn_list_t *lp;
4698ec5a142Sedp vnode_t *vp = rwsconsvp;
4708ec5a142Sedp
4718ec5a142Sedp if (state != OTYP_CHR)
4728ec5a142Sedp return (ENXIO);
4738ec5a142Sedp
4748ec5a142Sedp if (getminor(*devp) != 0)
4758ec5a142Sedp return (ENXIO);
4768ec5a142Sedp
4778ec5a142Sedp /*
4788ec5a142Sedp * You can't really open us until the console subsystem
4798ec5a142Sedp * has been configured.
4808ec5a142Sedp */
4818ec5a142Sedp if (rwsconsvp == NULL)
4828ec5a142Sedp return (ENXIO);
4838ec5a142Sedp
4848ec5a142Sedp /*
4858ec5a142Sedp * Check if this is the first open of this device or if
4868ec5a142Sedp * there is currently no redirection going on. (Ie, we're
4878ec5a142Sedp * sending output to underlying console device.)
4888ec5a142Sedp */
4898ec5a142Sedp mutex_enter(&iwscn_list_lock);
4908ec5a142Sedp if ((iwscn_list == NULL) || (iwscn_list->wl_vp == str_vp(vp))) {
4918ec5a142Sedp int error = 0;
4928ec5a142Sedp
4938ec5a142Sedp /* Don't hold the list lock across an VOP_OPEN */
4948ec5a142Sedp mutex_exit(&iwscn_list_lock);
4958ec5a142Sedp
4968ec5a142Sedp /*
4978ec5a142Sedp * There is currently no redirection going on.
4988ec5a142Sedp * pass this open request onto the console driver
4998ec5a142Sedp */
500da6c28aaSamw error = VOP_OPEN(&vp, flag, cred, NULL);
5018ec5a142Sedp if (error != 0)
5028ec5a142Sedp return (error);
5038ec5a142Sedp
504da6c28aaSamw /* Re-acquire the list lock */
5058ec5a142Sedp mutex_enter(&iwscn_list_lock);
5068ec5a142Sedp
5078ec5a142Sedp if (iwscn_list == NULL) {
5088ec5a142Sedp /* Save this vnode on the redirection list */
5098ec5a142Sedp srpush(vp, B_TRUE);
5108ec5a142Sedp } else {
5118ec5a142Sedp /*
5128ec5a142Sedp * In this case there must already be a copy of
5138ec5a142Sedp * this vnode on the list, so we can free up this one.
5148ec5a142Sedp */
515da6c28aaSamw (void) VOP_CLOSE(vp, flag, 1, (offset_t)0, cred, NULL);
5168ec5a142Sedp }
5178ec5a142Sedp }
5188ec5a142Sedp
5198ec5a142Sedp /*
5208ec5a142Sedp * XXX This is an ugly legacy hack that has been around
5218ec5a142Sedp * forever. This code is here because this driver (the
5228ec5a142Sedp * iwscn driver) is a character driver layered over a
5238ec5a142Sedp * streams driver.
5248ec5a142Sedp *
5258ec5a142Sedp * Normally streams recieve notification whenever a process
5268ec5a142Sedp * closes its last reference to that stream so that it can
5278ec5a142Sedp * clean up any signal handling related configuration. (Ie,
5288ec5a142Sedp * when a stream is configured to deliver a signal to a
5298ec5a142Sedp * process upon certain events.) This is a feature supported
5308ec5a142Sedp * by the streams framework.
5318ec5a142Sedp *
5328ec5a142Sedp * But character/block drivers don't recieve this type
5338ec5a142Sedp * of notification. A character/block driver's close routine
5348ec5a142Sedp * is only invoked upon the last close of the device. This
5358ec5a142Sedp * is an artifact of the multiple open/single close driver
5368ec5a142Sedp * model currently supported by solaris.
5378ec5a142Sedp *
5388ec5a142Sedp * So a problem occurs when a character driver layers itself
5398ec5a142Sedp * on top of a streams driver. Since this driver doesn't always
5408ec5a142Sedp * receive a close notification when a process closes its
5418ec5a142Sedp * last reference to it, this driver can't tell the stream
5428ec5a142Sedp * it's layered upon to clean up any signal handling
5438ec5a142Sedp * configuration for that process.
5448ec5a142Sedp *
5458ec5a142Sedp * So here we hack around that by manually cleaning up the
5468ec5a142Sedp * signal handling list upon each open. It doesn't guarantee
5478ec5a142Sedp * that the signaling handling data stored in the stream will
5488ec5a142Sedp * always be up to date, but it'll be more up to date than
5498ec5a142Sedp * it would be if we didn't do this.
5508ec5a142Sedp *
5518ec5a142Sedp * The real way to solve this problem would be to change
5528ec5a142Sedp * the device framework from an multiple open/single close
5538ec5a142Sedp * model to a multiple open/multiple close model. Then
5548ec5a142Sedp * character/block drivers could pass on close requests
5558ec5a142Sedp * to streams layered underneath.
5568ec5a142Sedp */
5578ec5a142Sedp str_cn_clean(VTOS(rwsconsvp)->s_commonvp);
5588ec5a142Sedp for (lp = iwscn_list; lp != NULL; lp = lp->wl_next) {
5598ec5a142Sedp ASSERT(lp->wl_vp->v_stream != NULL);
5608ec5a142Sedp str_cn_clean(lp->wl_vp);
5618ec5a142Sedp }
5628ec5a142Sedp
5638ec5a142Sedp mutex_exit(&iwscn_list_lock);
5648ec5a142Sedp return (0);
5658ec5a142Sedp }
5668ec5a142Sedp
5678ec5a142Sedp /* ARGSUSED */
5688ec5a142Sedp static int
iwscnclose(dev_t dev,int flag,int state,cred_t * cred)5698ec5a142Sedp iwscnclose(dev_t dev, int flag, int state, cred_t *cred)
5708ec5a142Sedp {
571e493d0faSns92644 iwscn_list_t *lp;
572e493d0faSns92644
5738ec5a142Sedp ASSERT(getminor(dev) == 0);
5748ec5a142Sedp
5758ec5a142Sedp if (state != OTYP_CHR)
5768ec5a142Sedp return (ENXIO);
5778ec5a142Sedp
5788ec5a142Sedp mutex_enter(&iwscn_list_lock);
579e493d0faSns92644 /*
580e493d0faSns92644 * Remove each entry from the redirection list, terminate any
581e493d0faSns92644 * current operations, wait for them to finish, then free the entry.
582e493d0faSns92644 */
583e493d0faSns92644 while (iwscn_list != NULL) {
584e493d0faSns92644 lp = srrm(iwscn_list->wl_vp);
585e493d0faSns92644 ASSERT(lp != NULL);
586e493d0faSns92644 srinterrupt(lp, B_TRUE);
5878ec5a142Sedp
588e493d0faSns92644 if (lp->wl_is_console == B_TRUE)
589e493d0faSns92644 /* Close the underlying console device. */
590da6c28aaSamw (void) VOP_CLOSE(lp->wl_vp, 0, 1, (offset_t)0, kcred,
591da6c28aaSamw NULL);
5928ec5a142Sedp
593e493d0faSns92644 kmem_free(lp, sizeof (*lp));
594e493d0faSns92644 }
5958ec5a142Sedp mutex_exit(&iwscn_list_lock);
5968ec5a142Sedp return (0);
5978ec5a142Sedp }
5987c478bd9Sstevel@tonic-gate
5997c478bd9Sstevel@tonic-gate /*ARGSUSED*/
6007c478bd9Sstevel@tonic-gate static int
iwscnattach(dev_info_t * devi,ddi_attach_cmd_t cmd)6017c478bd9Sstevel@tonic-gate iwscnattach(dev_info_t *devi, ddi_attach_cmd_t cmd)
6027c478bd9Sstevel@tonic-gate {
6038ec5a142Sedp /*
6048ec5a142Sedp * This is a pseudo device so there will never be more than
6058ec5a142Sedp * one instance attached at a time
6068ec5a142Sedp */
6078ec5a142Sedp ASSERT(iwscn_dip == NULL);
6087c478bd9Sstevel@tonic-gate
6097c478bd9Sstevel@tonic-gate if (ddi_create_minor_node(devi, "iwscn", S_IFCHR,
6107c478bd9Sstevel@tonic-gate 0, DDI_PSEUDO, NULL) == DDI_FAILURE) {
6118ec5a142Sedp return (DDI_FAILURE);
6127c478bd9Sstevel@tonic-gate }
6138ec5a142Sedp
6147c478bd9Sstevel@tonic-gate iwscn_dip = devi;
6158ec5a142Sedp mutex_init(&iwscn_list_lock, NULL, MUTEX_DRIVER, NULL);
6168ec5a142Sedp mutex_init(&iwscn_redirect_lock, NULL, MUTEX_DRIVER, NULL);
6178ec5a142Sedp cv_init(&iwscn_list_cv, NULL, CV_DRIVER, NULL);
6188ec5a142Sedp
6197c478bd9Sstevel@tonic-gate return (DDI_SUCCESS);
6207c478bd9Sstevel@tonic-gate }
6217c478bd9Sstevel@tonic-gate
6227c478bd9Sstevel@tonic-gate /* ARGSUSED */
6237c478bd9Sstevel@tonic-gate static int
iwscninfo(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)6247c478bd9Sstevel@tonic-gate iwscninfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
6257c478bd9Sstevel@tonic-gate {
6267c478bd9Sstevel@tonic-gate int error;
6277c478bd9Sstevel@tonic-gate
6287c478bd9Sstevel@tonic-gate switch (infocmd) {
6297c478bd9Sstevel@tonic-gate case DDI_INFO_DEVT2DEVINFO:
6307c478bd9Sstevel@tonic-gate if (iwscn_dip == NULL) {
6317c478bd9Sstevel@tonic-gate error = DDI_FAILURE;
6327c478bd9Sstevel@tonic-gate } else {
6337c478bd9Sstevel@tonic-gate *result = (void *)iwscn_dip;
6347c478bd9Sstevel@tonic-gate error = DDI_SUCCESS;
6357c478bd9Sstevel@tonic-gate }
6367c478bd9Sstevel@tonic-gate break;
6377c478bd9Sstevel@tonic-gate case DDI_INFO_DEVT2INSTANCE:
6387c478bd9Sstevel@tonic-gate *result = (void *)0;
6397c478bd9Sstevel@tonic-gate error = DDI_SUCCESS;
6407c478bd9Sstevel@tonic-gate break;
6417c478bd9Sstevel@tonic-gate default:
6427c478bd9Sstevel@tonic-gate error = DDI_FAILURE;
6437c478bd9Sstevel@tonic-gate }
6447c478bd9Sstevel@tonic-gate return (error);
6457c478bd9Sstevel@tonic-gate }
6467c478bd9Sstevel@tonic-gate
6478ec5a142Sedp struct cb_ops iwscn_cb_ops = {
6488ec5a142Sedp iwscnopen, /* open */
6498ec5a142Sedp iwscnclose, /* close */
6508ec5a142Sedp nodev, /* strategy */
6518ec5a142Sedp nodev, /* print */
6528ec5a142Sedp nodev, /* dump */
6538ec5a142Sedp iwscnread, /* read */
6548ec5a142Sedp iwscnwrite, /* write */
6558ec5a142Sedp iwscnioctl, /* ioctl */
6568ec5a142Sedp nodev, /* devmap */
6578ec5a142Sedp nodev, /* mmap */
6588ec5a142Sedp nodev, /* segmap */
6598ec5a142Sedp iwscnpoll, /* poll */
6608ec5a142Sedp ddi_prop_op, /* cb_prop_op */
6618ec5a142Sedp NULL, /* streamtab */
6628ec5a142Sedp D_MP /* Driver compatibility flag */
6638ec5a142Sedp };
6647c478bd9Sstevel@tonic-gate
6658ec5a142Sedp struct dev_ops iwscn_ops = {
6668ec5a142Sedp DEVO_REV, /* devo_rev, */
6678ec5a142Sedp 0, /* refcnt */
6688ec5a142Sedp iwscninfo, /* info */
6698ec5a142Sedp nulldev, /* identify */
6708ec5a142Sedp nulldev, /* probe */
6718ec5a142Sedp iwscnattach, /* attach */
6728ec5a142Sedp nodev, /* detach */
6738ec5a142Sedp nodev, /* reset */
6748ec5a142Sedp &iwscn_cb_ops, /* driver operations */
675*19397407SSherry Moore NULL, /* bus operations */
676*19397407SSherry Moore NULL, /* power */
677*19397407SSherry Moore ddi_quiesce_not_needed, /* quiesce */
6788ec5a142Sedp };
6798ec5a142Sedp
6808ec5a142Sedp /*
6818ec5a142Sedp * Module linkage information for the kernel.
6828ec5a142Sedp */
6838ec5a142Sedp static struct modldrv modldrv = {
6848ec5a142Sedp &mod_driverops, /* Type of module. This one is a pseudo driver */
685*19397407SSherry Moore "Workstation Redirection driver",
6868ec5a142Sedp &iwscn_ops, /* driver ops */
6878ec5a142Sedp };
6888ec5a142Sedp
6898ec5a142Sedp static struct modlinkage modlinkage = {
6908ec5a142Sedp MODREV_1,
6918ec5a142Sedp &modldrv,
6928ec5a142Sedp NULL
6938ec5a142Sedp };
6948ec5a142Sedp
6958ec5a142Sedp int
_init(void)6968ec5a142Sedp _init(void)
6977c478bd9Sstevel@tonic-gate {
6988ec5a142Sedp return (mod_install(&modlinkage));
6997c478bd9Sstevel@tonic-gate }
7007c478bd9Sstevel@tonic-gate
7018ec5a142Sedp int
_fini(void)7028ec5a142Sedp _fini(void)
7037c478bd9Sstevel@tonic-gate {
7047c478bd9Sstevel@tonic-gate return (EBUSY);
7057c478bd9Sstevel@tonic-gate }
7067c478bd9Sstevel@tonic-gate
7078ec5a142Sedp int
_info(struct modinfo * modinfop)7088ec5a142Sedp _info(struct modinfo *modinfop)
7097c478bd9Sstevel@tonic-gate {
7108ec5a142Sedp return (mod_info(&modlinkage, modinfop));
7117c478bd9Sstevel@tonic-gate }
712