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