xref: /illumos-gate/usr/src/uts/common/io/iwscons.c (revision 776eb96f)
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 /*
2319397407SSherry 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.
988ec5a142Sedp  *
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)
1188ec5a142Sedp {
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);
1298ec5a142Sedp }
1307c478bd9Sstevel@tonic-gate 
131e493d0faSns /*
132e493d0faSns  * Interrupt any operations that may be outstanding against this vnode.
133e493d0faSns  * optionally, wait for them to complete.
134e493d0faSns  */
135e493d0faSns static void
srinterrupt(iwscn_list_t * lp,boolean_t wait)136e493d0faSns srinterrupt(iwscn_list_t *lp, boolean_t wait)
137e493d0faSns {
138e493d0faSns 	ASSERT(MUTEX_HELD(&iwscn_list_lock));
139e493d0faSns 
140e493d0faSns 	while (lp->wl_ref_cnt != 0) {
141e493d0faSns 		strsetrerror(lp->wl_vp, EINTR, 0, NULL);
142e493d0faSns 		strsetwerror(lp->wl_vp, EINTR, 0, NULL);
143e493d0faSns 		if (!wait)
144e493d0faSns 			break;
145e493d0faSns 		cv_wait(&iwscn_list_cv, &iwscn_list_lock);
146e493d0faSns 	}
147e493d0faSns }
148e493d0faSns 
1497c478bd9Sstevel@tonic-gate /*
1508ec5a142Sedp  * Remove vp from the redirection list rooted at iwscn_list, should it
151e493d0faSns  * be there. Return a pointer to the removed entry.
1527c478bd9Sstevel@tonic-gate  */
1538ec5a142Sedp static iwscn_list_t *
srrm(vnode_t * vp)154e493d0faSns srrm(vnode_t *vp)
1558ec5a142Sedp {
1568ec5a142Sedp 	iwscn_list_t	*lp, **lpp;
1577c478bd9Sstevel@tonic-gate 
1588ec5a142Sedp 	ASSERT(MUTEX_HELD(&iwscn_list_lock));
1597c478bd9Sstevel@tonic-gate 
1608ec5a142Sedp 	/* Get the stream vnode */
1618ec5a142Sedp 	vp = str_vp(vp);
1628ec5a142Sedp 	ASSERT(vp);
1637c478bd9Sstevel@tonic-gate 
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 	}
169e493d0faSns 	if (lp != NULL)
170e493d0faSns 		/* Found it, remove this entry from the redirection list */
171e493d0faSns 		*lpp = lp->wl_next;
1727c478bd9Sstevel@tonic-gate 
173e493d0faSns 	return (lp);
1747c478bd9Sstevel@tonic-gate }
1757c478bd9Sstevel@tonic-gate 
1767c478bd9Sstevel@tonic-gate /*
1778ec5a142Sedp  * Push vp onto the redirection list.
1788ec5a142Sedp  * If it's already there move it to the front position.
1797c478bd9Sstevel@tonic-gate  */
1808ec5a142Sedp static void
srpush(vnode_t * vp,boolean_t is_console)1818ec5a142Sedp srpush(vnode_t *vp, boolean_t is_console)
1827c478bd9Sstevel@tonic-gate {
1838ec5a142Sedp 	iwscn_list_t	*lp;
1847c478bd9Sstevel@tonic-gate 
1858ec5a142Sedp 	ASSERT(MUTEX_HELD(&iwscn_list_lock));
1867c478bd9Sstevel@tonic-gate 
1878ec5a142Sedp 	/* Get the stream vnode */
1888ec5a142Sedp 	vp = str_vp(vp);
1898ec5a142Sedp 	ASSERT(vp);
1907c478bd9Sstevel@tonic-gate 
1918ec5a142Sedp 	/* Check if it's already on the redirection list */
192e493d0faSns 	if ((lp = srrm(vp)) == NULL) {
1938ec5a142Sedp 		lp = kmem_zalloc(sizeof (*lp), KM_SLEEP);
1948ec5a142Sedp 		lp->wl_vp = vp;
195e493d0faSns 		lp->wl_is_console = is_console;
1967c478bd9Sstevel@tonic-gate 	}
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
200e493d0faSns 	 * front of the redirection list.
2018ec5a142Sedp 	 */
2028ec5a142Sedp 	lp->wl_next = iwscn_list;
2038ec5a142Sedp 	iwscn_list = lp;
2048ec5a142Sedp }
2057c478bd9Sstevel@tonic-gate 
2068ec5a142Sedp /*
207e493d0faSns  * This vnode is no longer a valid redirection target. Terminate any current
208e493d0faSns  * operations. If closing, wait for them to complete, then free the entry.
209e493d0faSns  * If called because a hangup has occurred, just deprecate the entry to ensure
210e493d0faSns  * it won't become the target again.
2118ec5a142Sedp  */
2128ec5a142Sedp void
srpop(vnode_t * vp,boolean_t close)213e493d0faSns srpop(vnode_t *vp, boolean_t close)
2147c478bd9Sstevel@tonic-gate {
215e493d0faSns 	iwscn_list_t	*tlp;		/* This target's entry */
216e493d0faSns 	iwscn_list_t	*lp, **lpp;
217e493d0faSns 
2188ec5a142Sedp 	mutex_enter(&iwscn_list_lock);
219e493d0faSns 
220e493d0faSns 	/*
221e493d0faSns 	 * Ensure no further operations are directed at the target
222e493d0faSns 	 * by removing it from the redirection list.
223e493d0faSns 	 */
224e493d0faSns 	if ((tlp = srrm(vp)) == NULL) {
225e493d0faSns 		/* vnode wasn't in the list */
226e493d0faSns 		mutex_exit(&iwscn_list_lock);
227e493d0faSns 		return;
228e493d0faSns 	}
229e493d0faSns 	/*
230e493d0faSns 	 * Terminate any current operations.
231e493d0faSns 	 * If we're closing, wait until they complete.
232e493d0faSns 	 */
233e493d0faSns 	srinterrupt(tlp, close);
234e493d0faSns 
235e493d0faSns 	if (close) {
236e493d0faSns 		/* We're finished with this target */
237e493d0faSns 		kmem_free(tlp, sizeof (*tlp));
238e493d0faSns 	} else {
239e493d0faSns 		/*
240e493d0faSns 		 * Deprecate the entry. There's no need for a flag to indicate
241e493d0faSns 		 * this state, it just needs to be moved to the back of the list
242e493d0faSns 		 * behind the underlying console device. Since the underlying
243e493d0faSns 		 * device anchors the list and is never removed, this entry can
244e493d0faSns 		 * never return to the front again to become the target.
245e493d0faSns 		 */
246e493d0faSns 		for (lpp = &iwscn_list; (lp = *lpp) != NULL; )
247e493d0faSns 			lpp = &lp->wl_next;
248e493d0faSns 		tlp->wl_next = NULL;
249e493d0faSns 		*lpp = tlp;
250e493d0faSns 	}
2518ec5a142Sedp 	mutex_exit(&iwscn_list_lock);
2528ec5a142Sedp }
2537c478bd9Sstevel@tonic-gate 
2548ec5a142Sedp /* Get a hold on the current target */
2558ec5a142Sedp static iwscn_list_t *
srhold()2568ec5a142Sedp srhold()
2578ec5a142Sedp {
2588ec5a142Sedp 	iwscn_list_t	*lp;
2597c478bd9Sstevel@tonic-gate 
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);
2667c478bd9Sstevel@tonic-gate 
2678ec5a142Sedp 	return (lp);
2688ec5a142Sedp }
2697c478bd9Sstevel@tonic-gate 
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);
2807c478bd9Sstevel@tonic-gate }
2817c478bd9Sstevel@tonic-gate 
2827c478bd9Sstevel@tonic-gate static int
iwscnread(dev_t dev,uio_t * uio,cred_t * cred)2838ec5a142Sedp iwscnread(dev_t dev, uio_t *uio, cred_t *cred)
2847c478bd9Sstevel@tonic-gate {
2858ec5a142Sedp 	iwscn_list_t	*lp;
2868ec5a142Sedp 	int		error;
2877c478bd9Sstevel@tonic-gate 
2888ec5a142Sedp 	ASSERT(getminor(dev) == 0);
2897c478bd9Sstevel@tonic-gate 
2908ec5a142Sedp 	lp = srhold();
2918ec5a142Sedp 	error = strread(lp->wl_vp, uio, cred);
2928ec5a142Sedp 	srrele(lp);
2937c478bd9Sstevel@tonic-gate 
2948ec5a142Sedp 	return (error);
2957c478bd9Sstevel@tonic-gate }
2967c478bd9Sstevel@tonic-gate 
2977c478bd9Sstevel@tonic-gate static int
iwscnwrite(dev_t dev,uio_t * uio,cred_t * cred)2988ec5a142Sedp iwscnwrite(dev_t dev, uio_t *uio, cred_t *cred)
2997c478bd9Sstevel@tonic-gate {
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);
3087c478bd9Sstevel@tonic-gate 
3097c478bd9Sstevel@tonic-gate 	return (error);
3107c478bd9Sstevel@tonic-gate }
3117c478bd9Sstevel@tonic-gate 
3127c478bd9Sstevel@tonic-gate 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)
3157c478bd9Sstevel@tonic-gate {
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);
3247c478bd9Sstevel@tonic-gate 
3257c478bd9Sstevel@tonic-gate 	return (error);
3267c478bd9Sstevel@tonic-gate }
3277c478bd9Sstevel@tonic-gate 
3287c478bd9Sstevel@tonic-gate static int
iwscnioctl(dev_t dev,int cmd,intptr_t arg,int flag,cred_t * cred,int * rvalp)3297c478bd9Sstevel@tonic-gate iwscnioctl(dev_t dev, int cmd, intptr_t arg, int flag,
3307c478bd9Sstevel@tonic-gate     cred_t *cred, int *rvalp)
3317c478bd9Sstevel@tonic-gate {
3328ec5a142Sedp 	iwscn_list_t	*lp;
3338ec5a142Sedp 	file_t		*f;
3348ec5a142Sedp 	char		modname[FMNAMESZ + 1] = " ";
3358ec5a142Sedp 	int		error = 0;
3368ec5a142Sedp 
3378ec5a142Sedp 	ASSERT(getminor(dev) == 0);
3387c478bd9Sstevel@tonic-gate 
3397c478bd9Sstevel@tonic-gate 	switch (cmd) {
3408ec5a142Sedp 	case SRIOCSREDIR:
3418ec5a142Sedp 		/* Serialize all pushes of the redirection module */
3428ec5a142Sedp 		mutex_enter(&iwscn_redirect_lock);
3437c478bd9Sstevel@tonic-gate 
3447c478bd9Sstevel@tonic-gate 		/*
3457c478bd9Sstevel@tonic-gate 		 * Find the vnode corresponding to the file descriptor
3467c478bd9Sstevel@tonic-gate 		 * argument and verify that it names a stream.
3477c478bd9Sstevel@tonic-gate 		 */
3487c478bd9Sstevel@tonic-gate 		if ((f = getf((int)arg)) == NULL) {
3498ec5a142Sedp 			mutex_exit(&iwscn_redirect_lock);
3508ec5a142Sedp 			return (EBADF);
3517c478bd9Sstevel@tonic-gate 		}
3527c478bd9Sstevel@tonic-gate 		if (f->f_vnode->v_stream == NULL) {
3537c478bd9Sstevel@tonic-gate 			releasef((int)arg);
3548ec5a142Sedp 			mutex_exit(&iwscn_redirect_lock);
3558ec5a142Sedp 			return (ENOSTR);
3567c478bd9Sstevel@tonic-gate 		}
3577c478bd9Sstevel@tonic-gate 
3587c478bd9Sstevel@tonic-gate 		/*
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.
3627c478bd9Sstevel@tonic-gate 		 */
3638ec5a142Sedp 		if (str_vp(f->f_vnode) == str_vp(rwsconsvp)) {
3647c478bd9Sstevel@tonic-gate 			releasef((int)arg);
3658ec5a142Sedp 			mutex_exit(&iwscn_redirect_lock);
3668ec5a142Sedp 			return (EINVAL);
3677c478bd9Sstevel@tonic-gate 		}
3687c478bd9Sstevel@tonic-gate 
3697c478bd9Sstevel@tonic-gate 		/*
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.
3737c478bd9Sstevel@tonic-gate 		 */
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 		}
3947c478bd9Sstevel@tonic-gate 
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);
4037c478bd9Sstevel@tonic-gate 
4047c478bd9Sstevel@tonic-gate 	case SRIOCISREDIR:
4058ec5a142Sedp 		/*
4068ec5a142Sedp 		 * Find the vnode corresponding to the file descriptor
4078ec5a142Sedp 		 * argument and verify that it names a stream.
4088ec5a142Sedp 		 */
4097c478bd9Sstevel@tonic-gate 		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);
4157c478bd9Sstevel@tonic-gate 		}
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:
4247c478bd9Sstevel@tonic-gate 		/*
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.
4287c478bd9Sstevel@tonic-gate 		 */
4298ec5a142Sedp 		mutex_enter(&iwscn_redirect_lock);
4308ec5a142Sedp 		lp = srhold();
4317c478bd9Sstevel@tonic-gate 
4327c478bd9Sstevel@tonic-gate 		/*
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.
4397c478bd9Sstevel@tonic-gate 		 */
4408ec5a142Sedp 		(void) strioctl(lp->wl_vp, I_LOOK, (intptr_t)modname,
4418ec5a142Sedp 		    FKIOCTL, K_TO_K, cred, rvalp);
4427c478bd9Sstevel@tonic-gate 
4438ec5a142Sedp 		if (strcmp(STRREDIR_MOD, modname) == 0) {
4448ec5a142Sedp 			srrele(lp);
4458ec5a142Sedp 			mutex_exit(&iwscn_redirect_lock);
4468ec5a142Sedp 			return (EINVAL);
4477c478bd9Sstevel@tonic-gate 		}
4487c478bd9Sstevel@tonic-gate 
4498ec5a142Sedp 		/* Process the ioctl normally */
450da6c28aaSamw 		error = VOP_IOCTL(lp->wl_vp, cmd, arg, flag, cred, rvalp, NULL);
4517c478bd9Sstevel@tonic-gate 
4528ec5a142Sedp 		srrele(lp);
4538ec5a142Sedp 		mutex_exit(&iwscn_redirect_lock);
4548ec5a142Sedp 		return (error);
4557c478bd9Sstevel@tonic-gate 	}
4567c478bd9Sstevel@tonic-gate 
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);
4627c478bd9Sstevel@tonic-gate }
4637c478bd9Sstevel@tonic-gate 
4648ec5a142Sedp /* ARGSUSED */
4657c478bd9Sstevel@tonic-gate 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)
4677c478bd9Sstevel@tonic-gate {
4688ec5a142Sedp 	iwscn_list_t	*lp;
4698ec5a142Sedp 	vnode_t		*vp = rwsconsvp;
4707c478bd9Sstevel@tonic-gate 
4718ec5a142Sedp 	if (state != OTYP_CHR)
4728ec5a142Sedp 		return (ENXIO);
4737c478bd9Sstevel@tonic-gate 
4748ec5a142Sedp 	if (getminor(*devp) != 0)
4758ec5a142Sedp 		return (ENXIO);
4767c478bd9Sstevel@tonic-gate 
4778ec5a142Sedp 	/*
4788ec5a142Sedp 	 * You can't really open us until the console subsystem
4798ec5a142Sedp 	 * has been configured.
4808ec5a142Sedp 	 */
4818ec5a142Sedp 	if (rwsconsvp == NULL)
4828ec5a142Sedp 		return (ENXIO);
4837c478bd9Sstevel@tonic-gate 
4847c478bd9Sstevel@tonic-gate 	/*
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.)
4887c478bd9Sstevel@tonic-gate 	 */
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);
4957c478bd9Sstevel@tonic-gate 
4967c478bd9Sstevel@tonic-gate 		/*
4978ec5a142Sedp 		 * There is currently no redirection going on.
4988ec5a142Sedp 		 * pass this open request onto the console driver
4997c478bd9Sstevel@tonic-gate 		 */
500da6c28aaSamw 		error = VOP_OPEN(&vp, flag, cred, NULL);
5018ec5a142Sedp 		if (error != 0)
5028ec5a142Sedp 			return (error);
5037c478bd9Sstevel@tonic-gate 
504da6c28aaSamw 		/* Re-acquire the list lock */
5058ec5a142Sedp 		mutex_enter(&iwscn_list_lock);
5067c478bd9Sstevel@tonic-gate 
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 	}
5187c478bd9Sstevel@tonic-gate 
5197c478bd9Sstevel@tonic-gate 	/*
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.
5567c478bd9Sstevel@tonic-gate 	 */
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);
5617c478bd9Sstevel@tonic-gate 	}
5627c478bd9Sstevel@tonic-gate 
5638ec5a142Sedp 	mutex_exit(&iwscn_list_lock);
5648ec5a142Sedp 	return (0);
5657c478bd9Sstevel@tonic-gate }
5667c478bd9Sstevel@tonic-gate 
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)
5707c478bd9Sstevel@tonic-gate {
571e493d0faSns 	iwscn_list_t	*lp;
572e493d0faSns 
5738ec5a142Sedp 	ASSERT(getminor(dev) == 0);
5747c478bd9Sstevel@tonic-gate 
5758ec5a142Sedp 	if (state != OTYP_CHR)
5768ec5a142Sedp 		return (ENXIO);
5777c478bd9Sstevel@tonic-gate 
5788ec5a142Sedp 	mutex_enter(&iwscn_list_lock);
579e493d0faSns 	/*
580e493d0faSns 	 * Remove each entry from the redirection list, terminate any
581e493d0faSns 	 * current operations, wait for them to finish, then free the entry.
582e493d0faSns 	 */
583e493d0faSns 	while (iwscn_list != NULL) {
584e493d0faSns 		lp = srrm(iwscn_list->wl_vp);
585e493d0faSns 		ASSERT(lp != NULL);
586e493d0faSns 		srinterrupt(lp, B_TRUE);
5877c478bd9Sstevel@tonic-gate 
588e493d0faSns 		if (lp->wl_is_console == B_TRUE)
589e493d0faSns 			/* Close the underlying console device. */
590da6c28aaSamw 			(void) VOP_CLOSE(lp->wl_vp, 0, 1, (offset_t)0, kcred,
591da6c28aaSamw 			    NULL);
5927c478bd9Sstevel@tonic-gate 
593e493d0faSns 		kmem_free(lp, sizeof (*lp));
594e493d0faSns 	}
5958ec5a142Sedp 	mutex_exit(&iwscn_list_lock);
5968ec5a142Sedp 	return (0);
5977c478bd9Sstevel@tonic-gate }
5987c478bd9Sstevel@tonic-gate 
5998ec5a142Sedp /*ARGSUSED*/
6008ec5a142Sedp static int
iwscnattach(dev_info_t * devi,ddi_attach_cmd_t cmd)6018ec5a142Sedp iwscnattach(dev_info_t *devi, ddi_attach_cmd_t cmd)
6027c478bd9Sstevel@tonic-gate {
6037c478bd9Sstevel@tonic-gate 	/*
6048ec5a142Sedp 	 * This is a pseudo device so there will never be more than
6058ec5a142Sedp 	 * one instance attached at a time
6067c478bd9Sstevel@tonic-gate 	 */
6078ec5a142Sedp 	ASSERT(iwscn_dip == NULL);
6088ec5a142Sedp 
6098ec5a142Sedp 	if (ddi_create_minor_node(devi, "iwscn", S_IFCHR,
610*776eb96fSToomas Soome 	    0, DDI_PSEUDO, 0) == DDI_FAILURE) {
6118ec5a142Sedp 		return (DDI_FAILURE);
6128ec5a142Sedp 	}
6138ec5a142Sedp 
6148ec5a142Sedp 	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);
6187c478bd9Sstevel@tonic-gate 
6198ec5a142Sedp 	return (DDI_SUCCESS);
6207c478bd9Sstevel@tonic-gate }
6217c478bd9Sstevel@tonic-gate 
6228ec5a142Sedp /* ARGSUSED */
6238ec5a142Sedp static int
iwscninfo(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)6248ec5a142Sedp iwscninfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
6257c478bd9Sstevel@tonic-gate {
6268ec5a142Sedp 	int error;
6277c478bd9Sstevel@tonic-gate 
6288ec5a142Sedp 	switch (infocmd) {
6298ec5a142Sedp 	case DDI_INFO_DEVT2DEVINFO:
6308ec5a142Sedp 		if (iwscn_dip == NULL) {
6318ec5a142Sedp 			error = DDI_FAILURE;
6328ec5a142Sedp 		} else {
6338ec5a142Sedp 			*result = (void *)iwscn_dip;
6348ec5a142Sedp 			error = DDI_SUCCESS;
6358ec5a142Sedp 		}
6368ec5a142Sedp 		break;
6378ec5a142Sedp 	case DDI_INFO_DEVT2INSTANCE:
6388ec5a142Sedp 		*result = (void *)0;
6398ec5a142Sedp 		error = DDI_SUCCESS;
6408ec5a142Sedp 		break;
6418ec5a142Sedp 	default:
6428ec5a142Sedp 		error = DDI_FAILURE;
6437c478bd9Sstevel@tonic-gate 	}
6448ec5a142Sedp 	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 */
658*776eb96fSToomas Soome 	nodev,			/* segmap */
6598ec5a142Sedp 	iwscnpoll,		/* poll */
6608ec5a142Sedp 	ddi_prop_op,		/* cb_prop_op */
6618ec5a142Sedp 	NULL,			/* streamtab  */
6628ec5a142Sedp 	D_MP			/* Driver compatibility flag */
6638ec5a142Sedp };
6648ec5a142Sedp 
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 */
67519397407SSherry Moore 	NULL,			/* bus operations */
67619397407SSherry Moore 	NULL,			/* power */
67719397407SSherry Moore 	ddi_quiesce_not_needed,		/* quiesce */
6788ec5a142Sedp };
6797c478bd9Sstevel@tonic-gate 
6807c478bd9Sstevel@tonic-gate /*
6818ec5a142Sedp  * Module linkage information for the kernel.
6827c478bd9Sstevel@tonic-gate  */
6838ec5a142Sedp static struct modldrv modldrv = {
6848ec5a142Sedp 	&mod_driverops, /* Type of module.  This one is a pseudo driver */
68519397407SSherry 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));
6998ec5a142Sedp }
7007c478bd9Sstevel@tonic-gate 
7018ec5a142Sedp int
_fini(void)7028ec5a142Sedp _fini(void)
7038ec5a142Sedp {
7048ec5a142Sedp 	return (EBUSY);
7058ec5a142Sedp }
7067c478bd9Sstevel@tonic-gate 
7078ec5a142Sedp int
_info(struct modinfo * modinfop)7088ec5a142Sedp _info(struct modinfo *modinfop)
7098ec5a142Sedp {
7108ec5a142Sedp 	return (mod_info(&modlinkage, modinfop));
7117c478bd9Sstevel@tonic-gate }
712