xref: /onnv-gate/usr/src/uts/common/io/iwscons.c (revision 7656:2621e50fdf4a)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
51907Sedp  * Common Development and Distribution License (the "License").
61907Sedp  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
211907Sedp 
220Sstevel@tonic-gate /*
23*7656SSherry.Moore@Sun.COM  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
240Sstevel@tonic-gate  * Use is subject to license terms.
250Sstevel@tonic-gate  */
260Sstevel@tonic-gate 
270Sstevel@tonic-gate 
280Sstevel@tonic-gate /*
291907Sedp  * workstation console redirecting driver
300Sstevel@tonic-gate  *
310Sstevel@tonic-gate  * Redirects all I/O through a given device instance to the device designated
320Sstevel@tonic-gate  * as the current target, as given by the vnode associated with the first
330Sstevel@tonic-gate  * entry in the list of redirections for the given device instance.  The
340Sstevel@tonic-gate  * implementation assumes that this vnode denotes a STREAMS device; this is
350Sstevel@tonic-gate  * perhaps a bug.
360Sstevel@tonic-gate  *
370Sstevel@tonic-gate  * Supports the SRIOCSREDIR ioctl for designating a new redirection target.
380Sstevel@tonic-gate  * The new target is added to the front of a list of potentially active
390Sstevel@tonic-gate  * designees.  Should the device at the front of this list be closed, the new
400Sstevel@tonic-gate  * front entry assumes active duty.  (Stated differently, redirection targets
410Sstevel@tonic-gate  * stack, except that it's possible for entries in the interior of the stack
420Sstevel@tonic-gate  * to go away.)
430Sstevel@tonic-gate  *
440Sstevel@tonic-gate  * Supports the SRIOCISREDIR ioctl for inquiring whether the descriptor given
450Sstevel@tonic-gate  * as argument is the current front of the redirection list associated with
460Sstevel@tonic-gate  * the descriptor on which the ioctl was issued.
470Sstevel@tonic-gate  */
480Sstevel@tonic-gate 
490Sstevel@tonic-gate #include <sys/types.h>
500Sstevel@tonic-gate #include <sys/sysmacros.h>
510Sstevel@tonic-gate #include <sys/open.h>
520Sstevel@tonic-gate #include <sys/param.h>
530Sstevel@tonic-gate #include <sys/systm.h>
540Sstevel@tonic-gate #include <sys/signal.h>
550Sstevel@tonic-gate #include <sys/cred.h>
560Sstevel@tonic-gate #include <sys/user.h>
570Sstevel@tonic-gate #include <sys/proc.h>
580Sstevel@tonic-gate #include <sys/vnode.h>
590Sstevel@tonic-gate #include <sys/uio.h>
600Sstevel@tonic-gate #include <sys/file.h>
610Sstevel@tonic-gate #include <sys/kmem.h>
620Sstevel@tonic-gate #include <sys/stat.h>
630Sstevel@tonic-gate #include <sys/stream.h>
640Sstevel@tonic-gate #include <sys/stropts.h>
650Sstevel@tonic-gate #include <sys/strsubr.h>
660Sstevel@tonic-gate #include <sys/poll.h>
670Sstevel@tonic-gate #include <sys/debug.h>
680Sstevel@tonic-gate #include <sys/strredir.h>
690Sstevel@tonic-gate #include <sys/conf.h>
700Sstevel@tonic-gate #include <sys/ddi.h>
710Sstevel@tonic-gate #include <sys/sunddi.h>
721907Sedp #include <sys/errno.h>
731907Sedp #include <sys/modctl.h>
741907Sedp #include <sys/sunldi.h>
751907Sedp #include <sys/consdev.h>
761907Sedp #include <sys/fs/snode.h>
770Sstevel@tonic-gate 
780Sstevel@tonic-gate /*
791907Sedp  * Global data
800Sstevel@tonic-gate  */
810Sstevel@tonic-gate static dev_info_t	*iwscn_dip;
820Sstevel@tonic-gate 
831907Sedp /*
841907Sedp  * We record the list of redirections as a linked list of iwscn_list_t
851907Sedp  * structures.  We need to keep track of the target's vp, so that
861907Sedp  * we can vector reads, writes, etc. off to the current designee.
871907Sedp  */
881907Sedp typedef struct _iwscn_list {
891907Sedp 	struct _iwscn_list	*wl_next;	/* next entry */
901907Sedp 	vnode_t			*wl_vp;		/* target's vnode */
911907Sedp 	int			wl_ref_cnt;	/* operation in progress */
921907Sedp 	boolean_t		wl_is_console;	/* is the real console */
931907Sedp } iwscn_list_t;
941907Sedp static iwscn_list_t	*iwscn_list;
950Sstevel@tonic-gate 
960Sstevel@tonic-gate /*
971907Sedp  * iwscn_list_lock serializes modifications to the global iwscn_list list.
981907Sedp  *
991907Sedp  * iwscn_list_cv is used when freeing an entry from iwscn_list to allow
1001907Sedp  * the caller to wait till the wl_ref_cnt field is zero.
1011907Sedp  *
1021907Sedp  * iwscn_redirect_lock is used to serialize redirection requests.  This
1031907Sedp  * is required to ensure that all active redirection streams have
1041907Sedp  * the redirection streams module (redirmod) pushed on them.
1051907Sedp  *
1061907Sedp  * If both iwscn_redirect_lock and iwscn_list_lock must be held then
1075331Samw  * iwscn_redirect_lock must be acquired first.
1080Sstevel@tonic-gate  */
1091907Sedp static kcondvar_t	iwscn_list_cv;
1101907Sedp static kmutex_t		iwscn_list_lock;
1111907Sedp static kmutex_t		iwscn_redirect_lock;
1120Sstevel@tonic-gate 
1130Sstevel@tonic-gate /*
1141907Sedp  * Routines for managing iwscn_list
1150Sstevel@tonic-gate  */
1161907Sedp static vnode_t *
str_vp(vnode_t * vp)1171907Sedp str_vp(vnode_t *vp)
1181907Sedp {
1191907Sedp 	/*
1201907Sedp 	 * Here we switch to using the vnode that is linked
1211907Sedp 	 * to from the stream queue.  (In the case of device
1221907Sedp 	 * streams this will correspond to the common vnode
1231907Sedp 	 * for the device.)  The reason we use this vnode
1241907Sedp 	 * is that when wcmclose() calls srpop(), this is the
1251907Sedp 	 * only vnode that it has access to.
1261907Sedp 	 */
1271907Sedp 	ASSERT(vp->v_stream != NULL);
1281907Sedp 	return (vp->v_stream->sd_vnode);
1291907Sedp }
1300Sstevel@tonic-gate 
1310Sstevel@tonic-gate /*
1322485Sns92644  * Interrupt any operations that may be outstanding against this vnode.
1332485Sns92644  * optionally, wait for them to complete.
1342485Sns92644  */
1352485Sns92644 static void
srinterrupt(iwscn_list_t * lp,boolean_t wait)1362485Sns92644 srinterrupt(iwscn_list_t *lp, boolean_t wait)
1372485Sns92644 {
1382485Sns92644 	ASSERT(MUTEX_HELD(&iwscn_list_lock));
1392485Sns92644 
1402485Sns92644 	while (lp->wl_ref_cnt != 0) {
1412485Sns92644 		strsetrerror(lp->wl_vp, EINTR, 0, NULL);
1422485Sns92644 		strsetwerror(lp->wl_vp, EINTR, 0, NULL);
1432485Sns92644 		if (!wait)
1442485Sns92644 			break;
1452485Sns92644 		cv_wait(&iwscn_list_cv, &iwscn_list_lock);
1462485Sns92644 	}
1472485Sns92644 }
1482485Sns92644 
1492485Sns92644 /*
1501907Sedp  * Remove vp from the redirection list rooted at iwscn_list, should it
1512485Sns92644  * be there. Return a pointer to the removed entry.
1520Sstevel@tonic-gate  */
1531907Sedp static iwscn_list_t *
srrm(vnode_t * vp)1542485Sns92644 srrm(vnode_t *vp)
1551907Sedp {
1561907Sedp 	iwscn_list_t	*lp, **lpp;
1571907Sedp 
1581907Sedp 	ASSERT(MUTEX_HELD(&iwscn_list_lock));
1591907Sedp 
1601907Sedp 	/* Get the stream vnode */
1611907Sedp 	vp = str_vp(vp);
1621907Sedp 	ASSERT(vp);
1631907Sedp 
1641907Sedp 	/* Look for this vnode on the redirection list */
1651907Sedp 	for (lpp = &iwscn_list; (lp = *lpp) != NULL; lpp = &lp->wl_next) {
1661907Sedp 		if (lp->wl_vp == vp)
1671907Sedp 			break;
1681907Sedp 	}
1692485Sns92644 	if (lp != NULL)
1702485Sns92644 		/* Found it, remove this entry from the redirection list */
1712485Sns92644 		*lpp = lp->wl_next;
1720Sstevel@tonic-gate 
1732485Sns92644 	return (lp);
1741907Sedp }
1750Sstevel@tonic-gate 
1760Sstevel@tonic-gate /*
1771907Sedp  * Push vp onto the redirection list.
1781907Sedp  * If it's already there move it to the front position.
1790Sstevel@tonic-gate  */
1801907Sedp static void
srpush(vnode_t * vp,boolean_t is_console)1811907Sedp srpush(vnode_t *vp, boolean_t is_console)
1821907Sedp {
1831907Sedp 	iwscn_list_t	*lp;
1840Sstevel@tonic-gate 
1851907Sedp 	ASSERT(MUTEX_HELD(&iwscn_list_lock));
1860Sstevel@tonic-gate 
1871907Sedp 	/* Get the stream vnode */
1881907Sedp 	vp = str_vp(vp);
1891907Sedp 	ASSERT(vp);
1900Sstevel@tonic-gate 
1911907Sedp 	/* Check if it's already on the redirection list */
1922485Sns92644 	if ((lp = srrm(vp)) == NULL) {
1931907Sedp 		lp = kmem_zalloc(sizeof (*lp), KM_SLEEP);
1941907Sedp 		lp->wl_vp = vp;
1952485Sns92644 		lp->wl_is_console = is_console;
1961907Sedp 	}
1971907Sedp 	/*
1981907Sedp 	 * Note that if this vnode was already somewhere on the redirection
1991907Sedp 	 * list then we removed it above and are now bumping it up to the
2002485Sns92644 	 * front of the redirection list.
2011907Sedp 	 */
2021907Sedp 	lp->wl_next = iwscn_list;
2031907Sedp 	iwscn_list = lp;
2040Sstevel@tonic-gate }
2050Sstevel@tonic-gate 
2060Sstevel@tonic-gate /*
2072485Sns92644  * This vnode is no longer a valid redirection target. Terminate any current
2082485Sns92644  * operations. If closing, wait for them to complete, then free the entry.
2092485Sns92644  * If called because a hangup has occurred, just deprecate the entry to ensure
2102485Sns92644  * it won't become the target again.
2110Sstevel@tonic-gate  */
2121907Sedp void
srpop(vnode_t * vp,boolean_t close)2132485Sns92644 srpop(vnode_t *vp, boolean_t close)
2141907Sedp {
2152485Sns92644 	iwscn_list_t	*tlp;		/* This target's entry */
2162485Sns92644 	iwscn_list_t	*lp, **lpp;
2172485Sns92644 
2181907Sedp 	mutex_enter(&iwscn_list_lock);
2192485Sns92644 
2202485Sns92644 	/*
2212485Sns92644 	 * Ensure no further operations are directed at the target
2222485Sns92644 	 * by removing it from the redirection list.
2232485Sns92644 	 */
2242485Sns92644 	if ((tlp = srrm(vp)) == NULL) {
2252485Sns92644 		/* vnode wasn't in the list */
2262485Sns92644 		mutex_exit(&iwscn_list_lock);
2272485Sns92644 		return;
2282485Sns92644 	}
2292485Sns92644 	/*
2302485Sns92644 	 * Terminate any current operations.
2312485Sns92644 	 * If we're closing, wait until they complete.
2322485Sns92644 	 */
2332485Sns92644 	srinterrupt(tlp, close);
2342485Sns92644 
2352485Sns92644 	if (close) {
2362485Sns92644 		/* We're finished with this target */
2372485Sns92644 		kmem_free(tlp, sizeof (*tlp));
2382485Sns92644 	} else {
2392485Sns92644 		/*
2402485Sns92644 		 * Deprecate the entry. There's no need for a flag to indicate
2412485Sns92644 		 * this state, it just needs to be moved to the back of the list
2422485Sns92644 		 * behind the underlying console device. Since the underlying
2432485Sns92644 		 * device anchors the list and is never removed, this entry can
2442485Sns92644 		 * never return to the front again to become the target.
2452485Sns92644 		 */
2462485Sns92644 		for (lpp = &iwscn_list; (lp = *lpp) != NULL; )
2472485Sns92644 			lpp = &lp->wl_next;
2482485Sns92644 		tlp->wl_next = NULL;
2492485Sns92644 		*lpp = tlp;
2502485Sns92644 	}
2511907Sedp 	mutex_exit(&iwscn_list_lock);
2521907Sedp }
2531907Sedp 
2541907Sedp /* Get a hold on the current target */
2551907Sedp static iwscn_list_t *
srhold()2561907Sedp srhold()
2571907Sedp {
2581907Sedp 	iwscn_list_t	*lp;
2591907Sedp 
2601907Sedp 	mutex_enter(&iwscn_list_lock);
2611907Sedp 	ASSERT(iwscn_list != NULL);
2621907Sedp 	lp = iwscn_list;
2631907Sedp 	ASSERT(lp->wl_ref_cnt >= 0);
2641907Sedp 	lp->wl_ref_cnt++;
2651907Sedp 	mutex_exit(&iwscn_list_lock);
2661907Sedp 
2671907Sedp 	return (lp);
2681907Sedp }
2691907Sedp 
2701907Sedp /* Release a hold on an entry from the redirection list */
2711907Sedp static void
srrele(iwscn_list_t * lp)2721907Sedp srrele(iwscn_list_t *lp)
2731907Sedp {
2741907Sedp 	ASSERT(lp != NULL);
2751907Sedp 	mutex_enter(&iwscn_list_lock);
2761907Sedp 	ASSERT(lp->wl_ref_cnt > 0);
2771907Sedp 	lp->wl_ref_cnt--;
2781907Sedp 	cv_broadcast(&iwscn_list_cv);
2791907Sedp 	mutex_exit(&iwscn_list_lock);
2801907Sedp }
2811907Sedp 
2821907Sedp static int
iwscnread(dev_t dev,uio_t * uio,cred_t * cred)2831907Sedp iwscnread(dev_t dev, uio_t *uio, cred_t *cred)
2841907Sedp {
2851907Sedp 	iwscn_list_t	*lp;
2861907Sedp 	int		error;
2871907Sedp 
2881907Sedp 	ASSERT(getminor(dev) == 0);
2891907Sedp 
2901907Sedp 	lp = srhold();
2911907Sedp 	error = strread(lp->wl_vp, uio, cred);
2921907Sedp 	srrele(lp);
2931907Sedp 
2941907Sedp 	return (error);
2951907Sedp }
2961907Sedp 
2971907Sedp static int
iwscnwrite(dev_t dev,uio_t * uio,cred_t * cred)2981907Sedp iwscnwrite(dev_t dev, uio_t *uio, cred_t *cred)
2991907Sedp {
3001907Sedp 	iwscn_list_t	*lp;
3011907Sedp 	int		error;
3021907Sedp 
3031907Sedp 	ASSERT(getminor(dev) == 0);
3041907Sedp 
3051907Sedp 	lp = srhold();
3061907Sedp 	error = strwrite(lp->wl_vp, uio, cred);
3071907Sedp 	srrele(lp);
3081907Sedp 
3091907Sedp 	return (error);
3101907Sedp }
3111907Sedp 
3121907Sedp static int
iwscnpoll(dev_t dev,short events,int anyyet,short * reventsp,struct pollhead ** phpp)3131907Sedp iwscnpoll(dev_t dev, short events, int anyyet, short *reventsp,
3141907Sedp     struct pollhead **phpp)
3151907Sedp {
3161907Sedp 	iwscn_list_t	*lp;
3171907Sedp 	int		error;
3181907Sedp 
3191907Sedp 	ASSERT(getminor(dev) == 0);
3201907Sedp 
3211907Sedp 	lp = srhold();
3225331Samw 	error = VOP_POLL(lp->wl_vp, events, anyyet, reventsp, phpp, NULL);
3231907Sedp 	srrele(lp);
3241907Sedp 
3251907Sedp 	return (error);
3261907Sedp }
3271907Sedp 
3281907Sedp static int
iwscnioctl(dev_t dev,int cmd,intptr_t arg,int flag,cred_t * cred,int * rvalp)3291907Sedp iwscnioctl(dev_t dev, int cmd, intptr_t arg, int flag,
3301907Sedp     cred_t *cred, int *rvalp)
3311907Sedp {
3321907Sedp 	iwscn_list_t	*lp;
3331907Sedp 	file_t		*f;
3341907Sedp 	char		modname[FMNAMESZ + 1] = " ";
3351907Sedp 	int		error = 0;
3361907Sedp 
3371907Sedp 	ASSERT(getminor(dev) == 0);
3381907Sedp 
3391907Sedp 	switch (cmd) {
3401907Sedp 	case SRIOCSREDIR:
3411907Sedp 		/* Serialize all pushes of the redirection module */
3421907Sedp 		mutex_enter(&iwscn_redirect_lock);
3431907Sedp 
3441907Sedp 		/*
3451907Sedp 		 * Find the vnode corresponding to the file descriptor
3461907Sedp 		 * argument and verify that it names a stream.
3471907Sedp 		 */
3481907Sedp 		if ((f = getf((int)arg)) == NULL) {
3491907Sedp 			mutex_exit(&iwscn_redirect_lock);
3501907Sedp 			return (EBADF);
3511907Sedp 		}
3521907Sedp 		if (f->f_vnode->v_stream == NULL) {
3531907Sedp 			releasef((int)arg);
3541907Sedp 			mutex_exit(&iwscn_redirect_lock);
3551907Sedp 			return (ENOSTR);
3561907Sedp 		}
3571907Sedp 
3581907Sedp 		/*
3591907Sedp 		 * If the user is trying to redirect console output
3601907Sedp 		 * back to the underlying console via SRIOCSREDIR
3611907Sedp 		 * then they are evil and we'll stop them here.
3621907Sedp 		 */
3631907Sedp 		if (str_vp(f->f_vnode) == str_vp(rwsconsvp)) {
3641907Sedp 			releasef((int)arg);
3651907Sedp 			mutex_exit(&iwscn_redirect_lock);
3661907Sedp 			return (EINVAL);
3671907Sedp 		}
3681907Sedp 
3691907Sedp 		/*
3701907Sedp 		 * Check if this stream already has the redirection
3711907Sedp 		 * module pushed onto it.  I_LOOK returns an error
3721907Sedp 		 * if there are no modules pushed onto the stream.
3731907Sedp 		 */
3741907Sedp 		(void) strioctl(f->f_vnode, I_LOOK, (intptr_t)modname,
3751907Sedp 		    FKIOCTL, K_TO_K, cred, rvalp);
3761907Sedp 		if (strcmp(modname, STRREDIR_MOD) != 0) {
3771907Sedp 
3781907Sedp 			/*
3791907Sedp 			 * Push a new instance of the redirecting module onto
3801907Sedp 			 * the stream, so that its close routine can notify
3811907Sedp 			 * us when the overall stream is closed.  (In turn,
3821907Sedp 			 * we'll then remove it from the redirection list.)
3831907Sedp 			 */
3841907Sedp 			error = strioctl(f->f_vnode, I_PUSH,
3851907Sedp 			    (intptr_t)STRREDIR_MOD, FKIOCTL, K_TO_K,
3861907Sedp 			    cred, rvalp);
3871907Sedp 
3881907Sedp 			if (error != 0) {
3891907Sedp 				releasef((int)arg);
3901907Sedp 				mutex_exit(&iwscn_redirect_lock);
3911907Sedp 				return (error);
3921907Sedp 			}
3931907Sedp 		}
3941907Sedp 
3951907Sedp 		/* Push it onto the redirection stack */
3961907Sedp 		mutex_enter(&iwscn_list_lock);
3971907Sedp 		srpush(f->f_vnode, B_FALSE);
3981907Sedp 		mutex_exit(&iwscn_list_lock);
3991907Sedp 
4001907Sedp 		releasef((int)arg);
4011907Sedp 		mutex_exit(&iwscn_redirect_lock);
4021907Sedp 		return (0);
4031907Sedp 
4041907Sedp 	case SRIOCISREDIR:
4051907Sedp 		/*
4061907Sedp 		 * Find the vnode corresponding to the file descriptor
4071907Sedp 		 * argument and verify that it names a stream.
4081907Sedp 		 */
4091907Sedp 		if ((f = getf((int)arg)) == NULL) {
4101907Sedp 			return (EBADF);
4111907Sedp 		}
4121907Sedp 		if (f->f_vnode->v_stream == NULL) {
4131907Sedp 			releasef((int)arg);
4141907Sedp 			return (ENOSTR);
4151907Sedp 		}
4161907Sedp 
4171907Sedp 		lp = srhold();
4181907Sedp 		*rvalp = (str_vp(f->f_vnode) == lp->wl_vp);
4191907Sedp 		srrele(lp);
4201907Sedp 		releasef((int)arg);
4211907Sedp 		return (0);
4221907Sedp 
4231907Sedp 	case I_POP:
4241907Sedp 		/*
4251907Sedp 		 * We need to serialize I_POP operations with
4261907Sedp 		 * SRIOCSREDIR operations so we don't accidently
4271907Sedp 		 * remove the redirection module from a stream.
4281907Sedp 		 */
4291907Sedp 		mutex_enter(&iwscn_redirect_lock);
4301907Sedp 		lp = srhold();
4311907Sedp 
4321907Sedp 		/*
4331907Sedp 		 * Here we need to protect against process that might
4341907Sedp 		 * try to pop off the redirection module from the
4351907Sedp 		 * redirected stream.  Popping other modules is allowed.
4361907Sedp 		 *
4371907Sedp 		 * It's ok to hold iwscn_list_lock while doing the
4381907Sedp 		 * I_LOOK since it's such a simple operation.
4391907Sedp 		 */
4401907Sedp 		(void) strioctl(lp->wl_vp, I_LOOK, (intptr_t)modname,
4411907Sedp 		    FKIOCTL, K_TO_K, cred, rvalp);
4421907Sedp 
4431907Sedp 		if (strcmp(STRREDIR_MOD, modname) == 0) {
4441907Sedp 			srrele(lp);
4451907Sedp 			mutex_exit(&iwscn_redirect_lock);
4461907Sedp 			return (EINVAL);
4471907Sedp 		}
4481907Sedp 
4491907Sedp 		/* Process the ioctl normally */
4505331Samw 		error = VOP_IOCTL(lp->wl_vp, cmd, arg, flag, cred, rvalp, NULL);
4511907Sedp 
4521907Sedp 		srrele(lp);
4531907Sedp 		mutex_exit(&iwscn_redirect_lock);
4541907Sedp 		return (error);
4551907Sedp 	}
4561907Sedp 
4571907Sedp 	/* Process the ioctl normally */
4581907Sedp 	lp = srhold();
4595331Samw 	error = VOP_IOCTL(lp->wl_vp, cmd, arg, flag, cred, rvalp, NULL);
4601907Sedp 	srrele(lp);
4611907Sedp 	return (error);
4621907Sedp }
4631907Sedp 
4641907Sedp /* ARGSUSED */
4651907Sedp static int
iwscnopen(dev_t * devp,int flag,int state,cred_t * cred)4661907Sedp iwscnopen(dev_t *devp, int flag, int state, cred_t *cred)
4671907Sedp {
4681907Sedp 	iwscn_list_t	*lp;
4691907Sedp 	vnode_t		*vp = rwsconsvp;
4701907Sedp 
4711907Sedp 	if (state != OTYP_CHR)
4721907Sedp 		return (ENXIO);
4731907Sedp 
4741907Sedp 	if (getminor(*devp) != 0)
4751907Sedp 		return (ENXIO);
4761907Sedp 
4771907Sedp 	/*
4781907Sedp 	 * You can't really open us until the console subsystem
4791907Sedp 	 * has been configured.
4801907Sedp 	 */
4811907Sedp 	if (rwsconsvp == NULL)
4821907Sedp 		return (ENXIO);
4831907Sedp 
4841907Sedp 	/*
4851907Sedp 	 * Check if this is the first open of this device or if
4861907Sedp 	 * there is currently no redirection going on.  (Ie, we're
4871907Sedp 	 * sending output to underlying console device.)
4881907Sedp 	 */
4891907Sedp 	mutex_enter(&iwscn_list_lock);
4901907Sedp 	if ((iwscn_list == NULL) || (iwscn_list->wl_vp == str_vp(vp))) {
4911907Sedp 		int		error = 0;
4921907Sedp 
4931907Sedp 		/* Don't hold the list lock across an VOP_OPEN */
4941907Sedp 		mutex_exit(&iwscn_list_lock);
4951907Sedp 
4961907Sedp 		/*
4971907Sedp 		 * There is currently no redirection going on.
4981907Sedp 		 * pass this open request onto the console driver
4991907Sedp 		 */
5005331Samw 		error = VOP_OPEN(&vp, flag, cred, NULL);
5011907Sedp 		if (error != 0)
5021907Sedp 			return (error);
5031907Sedp 
5045331Samw 		/* Re-acquire the list lock */
5051907Sedp 		mutex_enter(&iwscn_list_lock);
5061907Sedp 
5071907Sedp 		if (iwscn_list == NULL) {
5081907Sedp 			/* Save this vnode on the redirection list */
5091907Sedp 			srpush(vp, B_TRUE);
5101907Sedp 		} else {
5111907Sedp 			/*
5121907Sedp 			 * In this case there must already be a copy of
5131907Sedp 			 * this vnode on the list, so we can free up this one.
5141907Sedp 			 */
5155331Samw 			(void) VOP_CLOSE(vp, flag, 1, (offset_t)0, cred, NULL);
5161907Sedp 		}
5171907Sedp 	}
5181907Sedp 
5191907Sedp 	/*
5201907Sedp 	 * XXX This is an ugly legacy hack that has been around
5211907Sedp 	 * forever.  This code is here because this driver (the
5221907Sedp 	 * iwscn driver) is a character driver layered over a
5231907Sedp 	 * streams driver.
5241907Sedp 	 *
5251907Sedp 	 * Normally streams recieve notification whenever a process
5261907Sedp 	 * closes its last reference to that stream so that it can
5271907Sedp 	 * clean up any signal handling related configuration.  (Ie,
5281907Sedp 	 * when a stream is configured to deliver a signal to a
5291907Sedp 	 * process upon certain events.)  This is a feature supported
5301907Sedp 	 * by the streams framework.
5311907Sedp 	 *
5321907Sedp 	 * But character/block drivers don't recieve this type
5331907Sedp 	 * of notification.  A character/block driver's close routine
5341907Sedp 	 * is only invoked upon the last close of the device.  This
5351907Sedp 	 * is an artifact of the multiple open/single close driver
5361907Sedp 	 * model currently supported by solaris.
5371907Sedp 	 *
5381907Sedp 	 * So a problem occurs when a character driver layers itself
5391907Sedp 	 * on top of a streams driver.  Since this driver doesn't always
5401907Sedp 	 * receive a close notification when a process closes its
5411907Sedp 	 * last reference to it, this driver can't tell the stream
5421907Sedp 	 * it's layered upon to clean up any signal handling
5431907Sedp 	 * configuration for that process.
5441907Sedp 	 *
5451907Sedp 	 * So here we hack around that by manually cleaning up the
5461907Sedp 	 * signal handling list upon each open.  It doesn't guarantee
5471907Sedp 	 * that the signaling handling data stored in the stream will
5481907Sedp 	 * always be up to date, but it'll be more up to date than
5491907Sedp 	 * it would be if we didn't do this.
5501907Sedp 	 *
5511907Sedp 	 * The real way to solve this problem would be to change
5521907Sedp 	 * the device framework from an multiple open/single close
5531907Sedp 	 * model to a multiple open/multiple close model.  Then
5541907Sedp 	 * character/block drivers could pass on close requests
5551907Sedp 	 * to streams layered underneath.
5561907Sedp 	 */
5571907Sedp 	str_cn_clean(VTOS(rwsconsvp)->s_commonvp);
5581907Sedp 	for (lp = iwscn_list; lp != NULL; lp = lp->wl_next) {
5591907Sedp 		ASSERT(lp->wl_vp->v_stream != NULL);
5601907Sedp 		str_cn_clean(lp->wl_vp);
5611907Sedp 	}
5621907Sedp 
5631907Sedp 	mutex_exit(&iwscn_list_lock);
5641907Sedp 	return (0);
5651907Sedp }
5661907Sedp 
5671907Sedp /* ARGSUSED */
5681907Sedp static int
iwscnclose(dev_t dev,int flag,int state,cred_t * cred)5691907Sedp iwscnclose(dev_t dev, int flag, int state, cred_t *cred)
5701907Sedp {
5712485Sns92644 	iwscn_list_t	*lp;
5722485Sns92644 
5731907Sedp 	ASSERT(getminor(dev) == 0);
5741907Sedp 
5751907Sedp 	if (state != OTYP_CHR)
5761907Sedp 		return (ENXIO);
5771907Sedp 
5781907Sedp 	mutex_enter(&iwscn_list_lock);
5792485Sns92644 	/*
5802485Sns92644 	 * Remove each entry from the redirection list, terminate any
5812485Sns92644 	 * current operations, wait for them to finish, then free the entry.
5822485Sns92644 	 */
5832485Sns92644 	while (iwscn_list != NULL) {
5842485Sns92644 		lp = srrm(iwscn_list->wl_vp);
5852485Sns92644 		ASSERT(lp != NULL);
5862485Sns92644 		srinterrupt(lp, B_TRUE);
5871907Sedp 
5882485Sns92644 		if (lp->wl_is_console == B_TRUE)
5892485Sns92644 			/* Close the underlying console device. */
5905331Samw 			(void) VOP_CLOSE(lp->wl_vp, 0, 1, (offset_t)0, kcred,
5915331Samw 			    NULL);
5921907Sedp 
5932485Sns92644 		kmem_free(lp, sizeof (*lp));
5942485Sns92644 	}
5951907Sedp 	mutex_exit(&iwscn_list_lock);
5961907Sedp 	return (0);
5971907Sedp }
5980Sstevel@tonic-gate 
5990Sstevel@tonic-gate /*ARGSUSED*/
6000Sstevel@tonic-gate static int
iwscnattach(dev_info_t * devi,ddi_attach_cmd_t cmd)6010Sstevel@tonic-gate iwscnattach(dev_info_t *devi, ddi_attach_cmd_t cmd)
6020Sstevel@tonic-gate {
6031907Sedp 	/*
6041907Sedp 	 * This is a pseudo device so there will never be more than
6051907Sedp 	 * one instance attached at a time
6061907Sedp 	 */
6071907Sedp 	ASSERT(iwscn_dip == NULL);
6080Sstevel@tonic-gate 
6090Sstevel@tonic-gate 	if (ddi_create_minor_node(devi, "iwscn", S_IFCHR,
6100Sstevel@tonic-gate 	    0, DDI_PSEUDO, NULL) == DDI_FAILURE) {
6111907Sedp 		return (DDI_FAILURE);
6120Sstevel@tonic-gate 	}
6131907Sedp 
6140Sstevel@tonic-gate 	iwscn_dip = devi;
6151907Sedp 	mutex_init(&iwscn_list_lock, NULL, MUTEX_DRIVER, NULL);
6161907Sedp 	mutex_init(&iwscn_redirect_lock, NULL, MUTEX_DRIVER, NULL);
6171907Sedp 	cv_init(&iwscn_list_cv, NULL, CV_DRIVER, NULL);
6181907Sedp 
6190Sstevel@tonic-gate 	return (DDI_SUCCESS);
6200Sstevel@tonic-gate }
6210Sstevel@tonic-gate 
6220Sstevel@tonic-gate /* ARGSUSED */
6230Sstevel@tonic-gate static int
iwscninfo(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)6240Sstevel@tonic-gate iwscninfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
6250Sstevel@tonic-gate {
6260Sstevel@tonic-gate 	int error;
6270Sstevel@tonic-gate 
6280Sstevel@tonic-gate 	switch (infocmd) {
6290Sstevel@tonic-gate 	case DDI_INFO_DEVT2DEVINFO:
6300Sstevel@tonic-gate 		if (iwscn_dip == NULL) {
6310Sstevel@tonic-gate 			error = DDI_FAILURE;
6320Sstevel@tonic-gate 		} else {
6330Sstevel@tonic-gate 			*result = (void *)iwscn_dip;
6340Sstevel@tonic-gate 			error = DDI_SUCCESS;
6350Sstevel@tonic-gate 		}
6360Sstevel@tonic-gate 		break;
6370Sstevel@tonic-gate 	case DDI_INFO_DEVT2INSTANCE:
6380Sstevel@tonic-gate 		*result = (void *)0;
6390Sstevel@tonic-gate 		error = DDI_SUCCESS;
6400Sstevel@tonic-gate 		break;
6410Sstevel@tonic-gate 	default:
6420Sstevel@tonic-gate 		error = DDI_FAILURE;
6430Sstevel@tonic-gate 	}
6440Sstevel@tonic-gate 	return (error);
6450Sstevel@tonic-gate }
6460Sstevel@tonic-gate 
6471907Sedp struct cb_ops	iwscn_cb_ops = {
6481907Sedp 	iwscnopen,		/* open */
6491907Sedp 	iwscnclose,		/* close */
6501907Sedp 	nodev,			/* strategy */
6511907Sedp 	nodev,			/* print */
6521907Sedp 	nodev,			/* dump */
6531907Sedp 	iwscnread,		/* read */
6541907Sedp 	iwscnwrite,		/* write */
6551907Sedp 	iwscnioctl,		/* ioctl */
6561907Sedp 	nodev,			/* devmap */
6571907Sedp 	nodev,			/* mmap */
6581907Sedp 	nodev, 			/* segmap */
6591907Sedp 	iwscnpoll,		/* poll */
6601907Sedp 	ddi_prop_op,		/* cb_prop_op */
6611907Sedp 	NULL,			/* streamtab  */
6621907Sedp 	D_MP			/* Driver compatibility flag */
6631907Sedp };
6640Sstevel@tonic-gate 
6651907Sedp struct dev_ops	iwscn_ops = {
6661907Sedp 	DEVO_REV,		/* devo_rev, */
6671907Sedp 	0,			/* refcnt  */
6681907Sedp 	iwscninfo,		/* info */
6691907Sedp 	nulldev,		/* identify */
6701907Sedp 	nulldev,		/* probe */
6711907Sedp 	iwscnattach,		/* attach */
6721907Sedp 	nodev,			/* detach */
6731907Sedp 	nodev,			/* reset */
6741907Sedp 	&iwscn_cb_ops,		/* driver operations */
675*7656SSherry.Moore@Sun.COM 	NULL,			/* bus operations */
676*7656SSherry.Moore@Sun.COM 	NULL,			/* power */
677*7656SSherry.Moore@Sun.COM 	ddi_quiesce_not_needed,		/* quiesce */
6781907Sedp };
6790Sstevel@tonic-gate 
6801907Sedp /*
6811907Sedp  * Module linkage information for the kernel.
6821907Sedp  */
6831907Sedp static struct modldrv modldrv = {
6841907Sedp 	&mod_driverops, /* Type of module.  This one is a pseudo driver */
685*7656SSherry.Moore@Sun.COM 	"Workstation Redirection driver",
6861907Sedp 	&iwscn_ops,	/* driver ops */
6871907Sedp };
6880Sstevel@tonic-gate 
6891907Sedp static struct modlinkage modlinkage = {
6901907Sedp 	MODREV_1,
6911907Sedp 	&modldrv,
6921907Sedp 	NULL
6931907Sedp };
6940Sstevel@tonic-gate 
6951907Sedp int
_init(void)6961907Sedp _init(void)
6970Sstevel@tonic-gate {
6981907Sedp 	return (mod_install(&modlinkage));
6990Sstevel@tonic-gate }
7000Sstevel@tonic-gate 
7011907Sedp int
_fini(void)7021907Sedp _fini(void)
7030Sstevel@tonic-gate {
7041907Sedp 	return (EBUSY);
7050Sstevel@tonic-gate }
7060Sstevel@tonic-gate 
7071907Sedp int
_info(struct modinfo * modinfop)7081907Sedp _info(struct modinfo *modinfop)
7090Sstevel@tonic-gate {
7101907Sedp 	return (mod_info(&modlinkage, modinfop));
7110Sstevel@tonic-gate }
712