xref: /titanic_50/usr/src/uts/common/io/iwscons.c (revision 8ec5a1426a1784db6978a9712f6228fc7fb382c8)
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
5*8ec5a142Sedp  * Common Development and Distribution License (the "License").
6*8ec5a142Sedp  * 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  */
21*8ec5a142Sedp 
227c478bd9Sstevel@tonic-gate /*
23*8ec5a142Sedp  * Copyright 2006 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 /*
30*8ec5a142Sedp  * 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>
73*8ec5a142Sedp #include <sys/errno.h>
74*8ec5a142Sedp #include <sys/modctl.h>
75*8ec5a142Sedp #include <sys/sunldi.h>
76*8ec5a142Sedp #include <sys/consdev.h>
77*8ec5a142Sedp #include <sys/fs/snode.h>
787c478bd9Sstevel@tonic-gate 
797c478bd9Sstevel@tonic-gate /*
80*8ec5a142Sedp  * Global data
817c478bd9Sstevel@tonic-gate  */
827c478bd9Sstevel@tonic-gate static dev_info_t	*iwscn_dip;
837c478bd9Sstevel@tonic-gate 
847c478bd9Sstevel@tonic-gate /*
85*8ec5a142Sedp  * We record the list of redirections as a linked list of iwscn_list_t
86*8ec5a142Sedp  * structures.  We need to keep track of the target's vp, so that
87*8ec5a142Sedp  * we can vector reads, writes, etc. off to the current designee.
887c478bd9Sstevel@tonic-gate  */
89*8ec5a142Sedp typedef struct _iwscn_list {
90*8ec5a142Sedp 	struct _iwscn_list	*wl_next;	/* next entry */
91*8ec5a142Sedp 	vnode_t			*wl_vp;		/* target's vnode */
92*8ec5a142Sedp 	int			wl_ref_cnt;	/* operation in progress */
93*8ec5a142Sedp 	boolean_t		wl_is_console;	/* is the real console */
94*8ec5a142Sedp } iwscn_list_t;
95*8ec5a142Sedp static iwscn_list_t	*iwscn_list;
967c478bd9Sstevel@tonic-gate 
977c478bd9Sstevel@tonic-gate /*
98*8ec5a142Sedp  * iwscn_list_lock serializes modifications to the global iwscn_list list.
997c478bd9Sstevel@tonic-gate  *
100*8ec5a142Sedp  * iwscn_list_cv is used when freeing an entry from iwscn_list to allow
101*8ec5a142Sedp  * the caller to wait till the wl_ref_cnt field is zero.
102*8ec5a142Sedp  *
103*8ec5a142Sedp  * iwscn_redirect_lock is used to serialize redirection requests.  This
104*8ec5a142Sedp  * is required to ensure that all active redirection streams have
105*8ec5a142Sedp  * the redirection streams module (redirmod) pushed on them.
106*8ec5a142Sedp  *
107*8ec5a142Sedp  * If both iwscn_redirect_lock and iwscn_list_lock must be held then
108*8ec5a142Sedp  * iwscn_redirect_lock must be aquired first.
1097c478bd9Sstevel@tonic-gate  */
110*8ec5a142Sedp static kcondvar_t	iwscn_list_cv;
111*8ec5a142Sedp static kmutex_t		iwscn_list_lock;
112*8ec5a142Sedp static kmutex_t		iwscn_redirect_lock;
1137c478bd9Sstevel@tonic-gate 
1147c478bd9Sstevel@tonic-gate /*
115*8ec5a142Sedp  * Routines for managing iwscn_list
1167c478bd9Sstevel@tonic-gate  */
117*8ec5a142Sedp static vnode_t *
118*8ec5a142Sedp str_vp(vnode_t *vp)
1197c478bd9Sstevel@tonic-gate {
120*8ec5a142Sedp 	/*
121*8ec5a142Sedp 	 * Here we switch to using the vnode that is linked
122*8ec5a142Sedp 	 * to from the stream queue.  (In the case of device
123*8ec5a142Sedp 	 * streams this will correspond to the common vnode
124*8ec5a142Sedp 	 * for the device.)  The reason we use this vnode
125*8ec5a142Sedp 	 * is that when wcmclose() calls srpop(), this is the
126*8ec5a142Sedp 	 * only vnode that it has access to.
127*8ec5a142Sedp 	 */
128*8ec5a142Sedp 	ASSERT(vp->v_stream != NULL);
129*8ec5a142Sedp 	return (vp->v_stream->sd_vnode);
1307c478bd9Sstevel@tonic-gate }
1317c478bd9Sstevel@tonic-gate 
1327c478bd9Sstevel@tonic-gate /*
133*8ec5a142Sedp  * Remove vp from the redirection list rooted at iwscn_list, should it
134*8ec5a142Sedp  * be there.  If iwscn_list is non-NULL, deallocate the entry.  If
135*8ec5a142Sedp  * the entry doesn't exist upon completion, return NULL; otherwise
136*8ec5a142Sedp  * return a pointer to it.
1377c478bd9Sstevel@tonic-gate  */
138*8ec5a142Sedp static iwscn_list_t *
139*8ec5a142Sedp srrm(vnode_t *vp, boolean_t free_entry)
140*8ec5a142Sedp {
141*8ec5a142Sedp 	iwscn_list_t	*lp, **lpp;
142*8ec5a142Sedp 
143*8ec5a142Sedp 	ASSERT(MUTEX_HELD(&iwscn_list_lock));
144*8ec5a142Sedp 
145*8ec5a142Sedp 	/* Get the stream vnode */
146*8ec5a142Sedp 	vp = str_vp(vp);
147*8ec5a142Sedp 	ASSERT(vp);
148*8ec5a142Sedp 
149*8ec5a142Sedp 	/* Look for this vnode on the redirection list */
150*8ec5a142Sedp 	for (lpp = &iwscn_list; (lp = *lpp) != NULL; lpp = &lp->wl_next) {
151*8ec5a142Sedp 		if (lp->wl_vp == vp)
152*8ec5a142Sedp 			break;
153*8ec5a142Sedp 	}
154*8ec5a142Sedp 	if (lp == NULL)
155*8ec5a142Sedp 		return (NULL);
156*8ec5a142Sedp 
157*8ec5a142Sedp 	/* Found it, remove this entry from the redirection list */
158*8ec5a142Sedp 	*lpp = lp->wl_next;
159*8ec5a142Sedp 
160*8ec5a142Sedp 	if (free_entry == B_FALSE)
161*8ec5a142Sedp 		return (lp);
162*8ec5a142Sedp 
163*8ec5a142Sedp 	/*
164*8ec5a142Sedp 	 * This entry is no longer on the global redirection list so now
165*8ec5a142Sedp 	 * we have to wait for all operations currently in progress to
166*8ec5a142Sedp 	 * finish before we can actually delete this entry.  We don't
167*8ec5a142Sedp 	 * have to worry about a new operation on this vnode starting up
168*8ec5a142Sedp 	 * because we've removed it from the redirection list.
169*8ec5a142Sedp 	 */
170*8ec5a142Sedp 	while (lp->wl_ref_cnt != 0) {
171*8ec5a142Sedp 		/*
172*8ec5a142Sedp 		 * Interrupt any operations that may be outstanding
173*8ec5a142Sedp 		 * against this vnode and wait for them to complete.
174*8ec5a142Sedp 		 */
175*8ec5a142Sedp 		strsetrerror(lp->wl_vp, EINTR, 0, NULL);
176*8ec5a142Sedp 		strsetwerror(lp->wl_vp, EINTR, 0, NULL);
177*8ec5a142Sedp 		cv_wait(&iwscn_list_cv, &iwscn_list_lock);
178*8ec5a142Sedp 	}
179*8ec5a142Sedp 
180*8ec5a142Sedp 	if (lp->wl_is_console == B_TRUE) {
181*8ec5a142Sedp 		/*
182*8ec5a142Sedp 		 * Special case.  If this is the underlying console device
183*8ec5a142Sedp 		 * then we opened it so we need to close it.
184*8ec5a142Sedp 		 */
185*8ec5a142Sedp 		(void) VOP_CLOSE(lp->wl_vp, 0, 1, (offset_t)0, kcred);
186*8ec5a142Sedp 	} else {
187*8ec5a142Sedp 		/* Release our hold on this vnode */
188*8ec5a142Sedp 		VN_RELE(lp->wl_vp);
189*8ec5a142Sedp 	}
190*8ec5a142Sedp 
191*8ec5a142Sedp 	/* Free the entry */
192*8ec5a142Sedp 	kmem_free(lp, sizeof (*lp));
193*8ec5a142Sedp 	return (NULL);
194*8ec5a142Sedp }
195*8ec5a142Sedp 
196*8ec5a142Sedp /*
197*8ec5a142Sedp  * Push vp onto the redirection list.
198*8ec5a142Sedp  * If it's already there move it to the front position.
199*8ec5a142Sedp  */
200*8ec5a142Sedp static void
201*8ec5a142Sedp srpush(vnode_t *vp, boolean_t is_console)
202*8ec5a142Sedp {
203*8ec5a142Sedp 	iwscn_list_t	*lp;
204*8ec5a142Sedp 
205*8ec5a142Sedp 	ASSERT(MUTEX_HELD(&iwscn_list_lock));
206*8ec5a142Sedp 
207*8ec5a142Sedp 	/* Get the stream vnode */
208*8ec5a142Sedp 	vp = str_vp(vp);
209*8ec5a142Sedp 	ASSERT(vp);
210*8ec5a142Sedp 
211*8ec5a142Sedp 	/* Check if it's already on the redirection list */
212*8ec5a142Sedp 	if ((lp = srrm(vp, B_FALSE)) == NULL) {
213*8ec5a142Sedp 		lp = kmem_zalloc(sizeof (*lp), KM_SLEEP);
214*8ec5a142Sedp 		lp->wl_vp = vp;
215*8ec5a142Sedp 
216*8ec5a142Sedp 		if (is_console == B_TRUE) {
217*8ec5a142Sedp 			lp->wl_is_console = B_TRUE;
218*8ec5a142Sedp 		} else {
219*8ec5a142Sedp 			/*
220*8ec5a142Sedp 			 * Hold the vnode.  Note that this hold will not
221*8ec5a142Sedp 			 * prevent the device stream associated with the
222*8ec5a142Sedp 			 * vnode from being closed.  (We protect against
223*8ec5a142Sedp 			 * that by pushing our streams redirection module
224*8ec5a142Sedp 			 * onto the stream to intercept close requests.)
225*8ec5a142Sedp 			 */
226*8ec5a142Sedp 			VN_HOLD(lp->wl_vp);
227*8ec5a142Sedp 			lp->wl_is_console = B_FALSE;
228*8ec5a142Sedp 		}
229*8ec5a142Sedp 	}
230*8ec5a142Sedp 
231*8ec5a142Sedp 	/*
232*8ec5a142Sedp 	 * Note that if this vnode was already somewhere on the redirection
233*8ec5a142Sedp 	 * list then we removed it above and are now bumping it up to the
234*8ec5a142Sedp 	 * from of the redirection list.
235*8ec5a142Sedp 	 */
236*8ec5a142Sedp 	lp->wl_next = iwscn_list;
237*8ec5a142Sedp 	iwscn_list = lp;
238*8ec5a142Sedp }
239*8ec5a142Sedp 
240*8ec5a142Sedp /*
241*8ec5a142Sedp  * srpop() - Remove redirection because the target stream is being closed.
242*8ec5a142Sedp  * Called from wcmclose().
243*8ec5a142Sedp  */
244*8ec5a142Sedp void
245*8ec5a142Sedp srpop(vnode_t *vp)
246*8ec5a142Sedp {
247*8ec5a142Sedp 	mutex_enter(&iwscn_list_lock);
248*8ec5a142Sedp 	(void) srrm(vp, B_TRUE);
249*8ec5a142Sedp 	mutex_exit(&iwscn_list_lock);
250*8ec5a142Sedp }
251*8ec5a142Sedp 
252*8ec5a142Sedp /* Get a hold on the current target */
253*8ec5a142Sedp static iwscn_list_t *
254*8ec5a142Sedp srhold()
255*8ec5a142Sedp {
256*8ec5a142Sedp 	iwscn_list_t	*lp;
257*8ec5a142Sedp 
258*8ec5a142Sedp 	mutex_enter(&iwscn_list_lock);
259*8ec5a142Sedp 	ASSERT(iwscn_list != NULL);
260*8ec5a142Sedp 	lp = iwscn_list;
261*8ec5a142Sedp 	ASSERT(lp->wl_ref_cnt >= 0);
262*8ec5a142Sedp 	lp->wl_ref_cnt++;
263*8ec5a142Sedp 	mutex_exit(&iwscn_list_lock);
264*8ec5a142Sedp 
265*8ec5a142Sedp 	return (lp);
266*8ec5a142Sedp }
267*8ec5a142Sedp 
268*8ec5a142Sedp /* Release a hold on an entry from the redirection list */
269*8ec5a142Sedp static void
270*8ec5a142Sedp srrele(iwscn_list_t *lp)
271*8ec5a142Sedp {
272*8ec5a142Sedp 	ASSERT(lp != NULL);
273*8ec5a142Sedp 	mutex_enter(&iwscn_list_lock);
274*8ec5a142Sedp 	ASSERT(lp->wl_ref_cnt > 0);
275*8ec5a142Sedp 	lp->wl_ref_cnt--;
276*8ec5a142Sedp 	cv_broadcast(&iwscn_list_cv);
277*8ec5a142Sedp 	mutex_exit(&iwscn_list_lock);
278*8ec5a142Sedp }
279*8ec5a142Sedp 
280*8ec5a142Sedp static int
281*8ec5a142Sedp iwscnread(dev_t dev, uio_t *uio, cred_t *cred)
282*8ec5a142Sedp {
283*8ec5a142Sedp 	iwscn_list_t	*lp;
284*8ec5a142Sedp 	int		error;
285*8ec5a142Sedp 
286*8ec5a142Sedp 	ASSERT(getminor(dev) == 0);
287*8ec5a142Sedp 
288*8ec5a142Sedp 	lp = srhold();
289*8ec5a142Sedp 	error = strread(lp->wl_vp, uio, cred);
290*8ec5a142Sedp 	srrele(lp);
291*8ec5a142Sedp 
292*8ec5a142Sedp 	return (error);
293*8ec5a142Sedp }
294*8ec5a142Sedp 
295*8ec5a142Sedp static int
296*8ec5a142Sedp iwscnwrite(dev_t dev, uio_t *uio, cred_t *cred)
297*8ec5a142Sedp {
298*8ec5a142Sedp 	iwscn_list_t	*lp;
299*8ec5a142Sedp 	int		error;
300*8ec5a142Sedp 
301*8ec5a142Sedp 	ASSERT(getminor(dev) == 0);
302*8ec5a142Sedp 
303*8ec5a142Sedp 	lp = srhold();
304*8ec5a142Sedp 	error = strwrite(lp->wl_vp, uio, cred);
305*8ec5a142Sedp 	srrele(lp);
306*8ec5a142Sedp 
307*8ec5a142Sedp 	return (error);
308*8ec5a142Sedp }
309*8ec5a142Sedp 
310*8ec5a142Sedp static int
311*8ec5a142Sedp iwscnpoll(dev_t dev, short events, int anyyet, short *reventsp,
312*8ec5a142Sedp     struct pollhead **phpp)
313*8ec5a142Sedp {
314*8ec5a142Sedp 	iwscn_list_t	*lp;
315*8ec5a142Sedp 	int		error;
316*8ec5a142Sedp 
317*8ec5a142Sedp 	ASSERT(getminor(dev) == 0);
318*8ec5a142Sedp 
319*8ec5a142Sedp 	lp = srhold();
320*8ec5a142Sedp 	error = VOP_POLL(lp->wl_vp, events, anyyet, reventsp, phpp);
321*8ec5a142Sedp 	srrele(lp);
322*8ec5a142Sedp 
323*8ec5a142Sedp 	return (error);
324*8ec5a142Sedp }
325*8ec5a142Sedp 
326*8ec5a142Sedp static int
327*8ec5a142Sedp iwscnioctl(dev_t dev, int cmd, intptr_t arg, int flag,
328*8ec5a142Sedp     cred_t *cred, int *rvalp)
329*8ec5a142Sedp {
330*8ec5a142Sedp 	iwscn_list_t	*lp;
331*8ec5a142Sedp 	file_t		*f;
332*8ec5a142Sedp 	char		modname[FMNAMESZ + 1] = " ";
333*8ec5a142Sedp 	int		error = 0;
334*8ec5a142Sedp 
335*8ec5a142Sedp 	ASSERT(getminor(dev) == 0);
336*8ec5a142Sedp 
337*8ec5a142Sedp 	switch (cmd) {
338*8ec5a142Sedp 	case SRIOCSREDIR:
339*8ec5a142Sedp 		/* Serialize all pushes of the redirection module */
340*8ec5a142Sedp 		mutex_enter(&iwscn_redirect_lock);
341*8ec5a142Sedp 
342*8ec5a142Sedp 		/*
343*8ec5a142Sedp 		 * Find the vnode corresponding to the file descriptor
344*8ec5a142Sedp 		 * argument and verify that it names a stream.
345*8ec5a142Sedp 		 */
346*8ec5a142Sedp 		if ((f = getf((int)arg)) == NULL) {
347*8ec5a142Sedp 			mutex_exit(&iwscn_redirect_lock);
348*8ec5a142Sedp 			return (EBADF);
349*8ec5a142Sedp 		}
350*8ec5a142Sedp 		if (f->f_vnode->v_stream == NULL) {
351*8ec5a142Sedp 			releasef((int)arg);
352*8ec5a142Sedp 			mutex_exit(&iwscn_redirect_lock);
353*8ec5a142Sedp 			return (ENOSTR);
354*8ec5a142Sedp 		}
355*8ec5a142Sedp 
356*8ec5a142Sedp 		/*
357*8ec5a142Sedp 		 * If the user is trying to redirect console output
358*8ec5a142Sedp 		 * back to the underlying console via SRIOCSREDIR
359*8ec5a142Sedp 		 * then they are evil and we'll stop them here.
360*8ec5a142Sedp 		 */
361*8ec5a142Sedp 		if (str_vp(f->f_vnode) == str_vp(rwsconsvp)) {
362*8ec5a142Sedp 			releasef((int)arg);
363*8ec5a142Sedp 			mutex_exit(&iwscn_redirect_lock);
364*8ec5a142Sedp 			return (EINVAL);
365*8ec5a142Sedp 		}
366*8ec5a142Sedp 
367*8ec5a142Sedp 		/*
368*8ec5a142Sedp 		 * Check if this stream already has the redirection
369*8ec5a142Sedp 		 * module pushed onto it.  I_LOOK returns an error
370*8ec5a142Sedp 		 * if there are no modules pushed onto the stream.
371*8ec5a142Sedp 		 */
372*8ec5a142Sedp 		(void) strioctl(f->f_vnode, I_LOOK, (intptr_t)modname,
373*8ec5a142Sedp 		    FKIOCTL, K_TO_K, cred, rvalp);
374*8ec5a142Sedp 		if (strcmp(modname, STRREDIR_MOD) != 0) {
375*8ec5a142Sedp 
376*8ec5a142Sedp 			/*
377*8ec5a142Sedp 			 * Push a new instance of the redirecting module onto
378*8ec5a142Sedp 			 * the stream, so that its close routine can notify
379*8ec5a142Sedp 			 * us when the overall stream is closed.  (In turn,
380*8ec5a142Sedp 			 * we'll then remove it from the redirection list.)
381*8ec5a142Sedp 			 */
382*8ec5a142Sedp 			error = strioctl(f->f_vnode, I_PUSH,
383*8ec5a142Sedp 			    (intptr_t)STRREDIR_MOD, FKIOCTL, K_TO_K,
384*8ec5a142Sedp 			    cred, rvalp);
385*8ec5a142Sedp 
386*8ec5a142Sedp 			if (error != 0) {
387*8ec5a142Sedp 				releasef((int)arg);
388*8ec5a142Sedp 				mutex_exit(&iwscn_redirect_lock);
389*8ec5a142Sedp 				return (error);
390*8ec5a142Sedp 			}
391*8ec5a142Sedp 		}
392*8ec5a142Sedp 
393*8ec5a142Sedp 		/* Push it onto the redirection stack */
394*8ec5a142Sedp 		mutex_enter(&iwscn_list_lock);
395*8ec5a142Sedp 		srpush(f->f_vnode, B_FALSE);
396*8ec5a142Sedp 		mutex_exit(&iwscn_list_lock);
397*8ec5a142Sedp 
398*8ec5a142Sedp 		releasef((int)arg);
399*8ec5a142Sedp 		mutex_exit(&iwscn_redirect_lock);
400*8ec5a142Sedp 		return (0);
401*8ec5a142Sedp 
402*8ec5a142Sedp 	case SRIOCISREDIR:
403*8ec5a142Sedp 		/*
404*8ec5a142Sedp 		 * Find the vnode corresponding to the file descriptor
405*8ec5a142Sedp 		 * argument and verify that it names a stream.
406*8ec5a142Sedp 		 */
407*8ec5a142Sedp 		if ((f = getf((int)arg)) == NULL) {
408*8ec5a142Sedp 			return (EBADF);
409*8ec5a142Sedp 		}
410*8ec5a142Sedp 		if (f->f_vnode->v_stream == NULL) {
411*8ec5a142Sedp 			releasef((int)arg);
412*8ec5a142Sedp 			return (ENOSTR);
413*8ec5a142Sedp 		}
414*8ec5a142Sedp 
415*8ec5a142Sedp 		lp = srhold();
416*8ec5a142Sedp 		*rvalp = (str_vp(f->f_vnode) == lp->wl_vp);
417*8ec5a142Sedp 		srrele(lp);
418*8ec5a142Sedp 		releasef((int)arg);
419*8ec5a142Sedp 		return (0);
420*8ec5a142Sedp 
421*8ec5a142Sedp 	case I_POP:
422*8ec5a142Sedp 		/*
423*8ec5a142Sedp 		 * We need to serialize I_POP operations with
424*8ec5a142Sedp 		 * SRIOCSREDIR operations so we don't accidently
425*8ec5a142Sedp 		 * remove the redirection module from a stream.
426*8ec5a142Sedp 		 */
427*8ec5a142Sedp 		mutex_enter(&iwscn_redirect_lock);
428*8ec5a142Sedp 		lp = srhold();
429*8ec5a142Sedp 
430*8ec5a142Sedp 		/*
431*8ec5a142Sedp 		 * Here we need to protect against process that might
432*8ec5a142Sedp 		 * try to pop off the redirection module from the
433*8ec5a142Sedp 		 * redirected stream.  Popping other modules is allowed.
434*8ec5a142Sedp 		 *
435*8ec5a142Sedp 		 * It's ok to hold iwscn_list_lock while doing the
436*8ec5a142Sedp 		 * I_LOOK since it's such a simple operation.
437*8ec5a142Sedp 		 */
438*8ec5a142Sedp 		(void) strioctl(lp->wl_vp, I_LOOK, (intptr_t)modname,
439*8ec5a142Sedp 		    FKIOCTL, K_TO_K, cred, rvalp);
440*8ec5a142Sedp 
441*8ec5a142Sedp 		if (strcmp(STRREDIR_MOD, modname) == 0) {
442*8ec5a142Sedp 			srrele(lp);
443*8ec5a142Sedp 			mutex_exit(&iwscn_redirect_lock);
444*8ec5a142Sedp 			return (EINVAL);
445*8ec5a142Sedp 		}
446*8ec5a142Sedp 
447*8ec5a142Sedp 		/* Process the ioctl normally */
448*8ec5a142Sedp 		error = VOP_IOCTL(lp->wl_vp, cmd, arg, flag, cred, rvalp);
449*8ec5a142Sedp 
450*8ec5a142Sedp 		srrele(lp);
451*8ec5a142Sedp 		mutex_exit(&iwscn_redirect_lock);
452*8ec5a142Sedp 		return (error);
453*8ec5a142Sedp 	}
454*8ec5a142Sedp 
455*8ec5a142Sedp 	/* Process the ioctl normally */
456*8ec5a142Sedp 	lp = srhold();
457*8ec5a142Sedp 	error = VOP_IOCTL(lp->wl_vp, cmd, arg, flag, cred, rvalp);
458*8ec5a142Sedp 	srrele(lp);
459*8ec5a142Sedp 	return (error);
460*8ec5a142Sedp }
461*8ec5a142Sedp 
462*8ec5a142Sedp /* ARGSUSED */
463*8ec5a142Sedp static int
464*8ec5a142Sedp iwscnopen(dev_t *devp, int flag, int state, cred_t *cred)
465*8ec5a142Sedp {
466*8ec5a142Sedp 	iwscn_list_t	*lp;
467*8ec5a142Sedp 	vnode_t		*vp = rwsconsvp;
468*8ec5a142Sedp 
469*8ec5a142Sedp 	if (state != OTYP_CHR)
470*8ec5a142Sedp 		return (ENXIO);
471*8ec5a142Sedp 
472*8ec5a142Sedp 	if (getminor(*devp) != 0)
473*8ec5a142Sedp 		return (ENXIO);
474*8ec5a142Sedp 
475*8ec5a142Sedp 	/*
476*8ec5a142Sedp 	 * You can't really open us until the console subsystem
477*8ec5a142Sedp 	 * has been configured.
478*8ec5a142Sedp 	 */
479*8ec5a142Sedp 	if (rwsconsvp == NULL)
480*8ec5a142Sedp 		return (ENXIO);
481*8ec5a142Sedp 
482*8ec5a142Sedp 	/*
483*8ec5a142Sedp 	 * Check if this is the first open of this device or if
484*8ec5a142Sedp 	 * there is currently no redirection going on.  (Ie, we're
485*8ec5a142Sedp 	 * sending output to underlying console device.)
486*8ec5a142Sedp 	 */
487*8ec5a142Sedp 	mutex_enter(&iwscn_list_lock);
488*8ec5a142Sedp 	if ((iwscn_list == NULL) || (iwscn_list->wl_vp == str_vp(vp))) {
489*8ec5a142Sedp 		int		error = 0;
490*8ec5a142Sedp 
491*8ec5a142Sedp 		/* Don't hold the list lock across an VOP_OPEN */
492*8ec5a142Sedp 		mutex_exit(&iwscn_list_lock);
493*8ec5a142Sedp 
494*8ec5a142Sedp 		/*
495*8ec5a142Sedp 		 * There is currently no redirection going on.
496*8ec5a142Sedp 		 * pass this open request onto the console driver
497*8ec5a142Sedp 		 */
498*8ec5a142Sedp 		error = VOP_OPEN(&vp, flag, cred);
499*8ec5a142Sedp 		if (error != 0)
500*8ec5a142Sedp 			return (error);
501*8ec5a142Sedp 
502*8ec5a142Sedp 		/* Re-aquire the list lock */
503*8ec5a142Sedp 		mutex_enter(&iwscn_list_lock);
504*8ec5a142Sedp 
505*8ec5a142Sedp 		if (iwscn_list == NULL) {
506*8ec5a142Sedp 			/* Save this vnode on the redirection list */
507*8ec5a142Sedp 			srpush(vp, B_TRUE);
508*8ec5a142Sedp 		} else {
509*8ec5a142Sedp 			/*
510*8ec5a142Sedp 			 * In this case there must already be a copy of
511*8ec5a142Sedp 			 * this vnode on the list, so we can free up this one.
512*8ec5a142Sedp 			 */
513*8ec5a142Sedp 			(void) VOP_CLOSE(vp, flag, 1, (offset_t)0, cred);
514*8ec5a142Sedp 		}
515*8ec5a142Sedp 	}
516*8ec5a142Sedp 
517*8ec5a142Sedp 	/*
518*8ec5a142Sedp 	 * XXX This is an ugly legacy hack that has been around
519*8ec5a142Sedp 	 * forever.  This code is here because this driver (the
520*8ec5a142Sedp 	 * iwscn driver) is a character driver layered over a
521*8ec5a142Sedp 	 * streams driver.
522*8ec5a142Sedp 	 *
523*8ec5a142Sedp 	 * Normally streams recieve notification whenever a process
524*8ec5a142Sedp 	 * closes its last reference to that stream so that it can
525*8ec5a142Sedp 	 * clean up any signal handling related configuration.  (Ie,
526*8ec5a142Sedp 	 * when a stream is configured to deliver a signal to a
527*8ec5a142Sedp 	 * process upon certain events.)  This is a feature supported
528*8ec5a142Sedp 	 * by the streams framework.
529*8ec5a142Sedp 	 *
530*8ec5a142Sedp 	 * But character/block drivers don't recieve this type
531*8ec5a142Sedp 	 * of notification.  A character/block driver's close routine
532*8ec5a142Sedp 	 * is only invoked upon the last close of the device.  This
533*8ec5a142Sedp 	 * is an artifact of the multiple open/single close driver
534*8ec5a142Sedp 	 * model currently supported by solaris.
535*8ec5a142Sedp 	 *
536*8ec5a142Sedp 	 * So a problem occurs when a character driver layers itself
537*8ec5a142Sedp 	 * on top of a streams driver.  Since this driver doesn't always
538*8ec5a142Sedp 	 * receive a close notification when a process closes its
539*8ec5a142Sedp 	 * last reference to it, this driver can't tell the stream
540*8ec5a142Sedp 	 * it's layered upon to clean up any signal handling
541*8ec5a142Sedp 	 * configuration for that process.
542*8ec5a142Sedp 	 *
543*8ec5a142Sedp 	 * So here we hack around that by manually cleaning up the
544*8ec5a142Sedp 	 * signal handling list upon each open.  It doesn't guarantee
545*8ec5a142Sedp 	 * that the signaling handling data stored in the stream will
546*8ec5a142Sedp 	 * always be up to date, but it'll be more up to date than
547*8ec5a142Sedp 	 * it would be if we didn't do this.
548*8ec5a142Sedp 	 *
549*8ec5a142Sedp 	 * The real way to solve this problem would be to change
550*8ec5a142Sedp 	 * the device framework from an multiple open/single close
551*8ec5a142Sedp 	 * model to a multiple open/multiple close model.  Then
552*8ec5a142Sedp 	 * character/block drivers could pass on close requests
553*8ec5a142Sedp 	 * to streams layered underneath.
554*8ec5a142Sedp 	 */
555*8ec5a142Sedp 	str_cn_clean(VTOS(rwsconsvp)->s_commonvp);
556*8ec5a142Sedp 	for (lp = iwscn_list; lp != NULL; lp = lp->wl_next) {
557*8ec5a142Sedp 		ASSERT(lp->wl_vp->v_stream != NULL);
558*8ec5a142Sedp 		str_cn_clean(lp->wl_vp);
559*8ec5a142Sedp 	}
560*8ec5a142Sedp 
561*8ec5a142Sedp 	mutex_exit(&iwscn_list_lock);
562*8ec5a142Sedp 	return (0);
563*8ec5a142Sedp }
564*8ec5a142Sedp 
565*8ec5a142Sedp /* ARGSUSED */
566*8ec5a142Sedp static int
567*8ec5a142Sedp iwscnclose(dev_t dev, int flag, int state, cred_t *cred)
568*8ec5a142Sedp {
569*8ec5a142Sedp 	ASSERT(getminor(dev) == 0);
570*8ec5a142Sedp 
571*8ec5a142Sedp 	if (state != OTYP_CHR)
572*8ec5a142Sedp 		return (ENXIO);
573*8ec5a142Sedp 
574*8ec5a142Sedp 	mutex_enter(&iwscn_list_lock);
575*8ec5a142Sedp 
576*8ec5a142Sedp 	/* Remove all outstanding redirections */
577*8ec5a142Sedp 	while (iwscn_list != NULL)
578*8ec5a142Sedp 		(void) srrm(iwscn_list->wl_vp, B_TRUE);
579*8ec5a142Sedp 	iwscn_list = NULL;
580*8ec5a142Sedp 
581*8ec5a142Sedp 	mutex_exit(&iwscn_list_lock);
582*8ec5a142Sedp 	return (0);
583*8ec5a142Sedp }
5847c478bd9Sstevel@tonic-gate 
5857c478bd9Sstevel@tonic-gate /*ARGSUSED*/
5867c478bd9Sstevel@tonic-gate static int
5877c478bd9Sstevel@tonic-gate iwscnattach(dev_info_t *devi, ddi_attach_cmd_t cmd)
5887c478bd9Sstevel@tonic-gate {
589*8ec5a142Sedp 	/*
590*8ec5a142Sedp 	 * This is a pseudo device so there will never be more than
591*8ec5a142Sedp 	 * one instance attached at a time
592*8ec5a142Sedp 	 */
593*8ec5a142Sedp 	ASSERT(iwscn_dip == NULL);
5947c478bd9Sstevel@tonic-gate 
5957c478bd9Sstevel@tonic-gate 	if (ddi_create_minor_node(devi, "iwscn", S_IFCHR,
5967c478bd9Sstevel@tonic-gate 	    0, DDI_PSEUDO, NULL) == DDI_FAILURE) {
597*8ec5a142Sedp 		return (DDI_FAILURE);
5987c478bd9Sstevel@tonic-gate 	}
599*8ec5a142Sedp 
6007c478bd9Sstevel@tonic-gate 	iwscn_dip = devi;
601*8ec5a142Sedp 	mutex_init(&iwscn_list_lock, NULL, MUTEX_DRIVER, NULL);
602*8ec5a142Sedp 	mutex_init(&iwscn_redirect_lock, NULL, MUTEX_DRIVER, NULL);
603*8ec5a142Sedp 	cv_init(&iwscn_list_cv, NULL, CV_DRIVER, NULL);
604*8ec5a142Sedp 
6057c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
6067c478bd9Sstevel@tonic-gate }
6077c478bd9Sstevel@tonic-gate 
6087c478bd9Sstevel@tonic-gate /* ARGSUSED */
6097c478bd9Sstevel@tonic-gate static int
6107c478bd9Sstevel@tonic-gate iwscninfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
6117c478bd9Sstevel@tonic-gate {
6127c478bd9Sstevel@tonic-gate 	int error;
6137c478bd9Sstevel@tonic-gate 
6147c478bd9Sstevel@tonic-gate 	switch (infocmd) {
6157c478bd9Sstevel@tonic-gate 	case DDI_INFO_DEVT2DEVINFO:
6167c478bd9Sstevel@tonic-gate 		if (iwscn_dip == NULL) {
6177c478bd9Sstevel@tonic-gate 			error = DDI_FAILURE;
6187c478bd9Sstevel@tonic-gate 		} else {
6197c478bd9Sstevel@tonic-gate 			*result = (void *)iwscn_dip;
6207c478bd9Sstevel@tonic-gate 			error = DDI_SUCCESS;
6217c478bd9Sstevel@tonic-gate 		}
6227c478bd9Sstevel@tonic-gate 		break;
6237c478bd9Sstevel@tonic-gate 	case DDI_INFO_DEVT2INSTANCE:
6247c478bd9Sstevel@tonic-gate 		*result = (void *)0;
6257c478bd9Sstevel@tonic-gate 		error = DDI_SUCCESS;
6267c478bd9Sstevel@tonic-gate 		break;
6277c478bd9Sstevel@tonic-gate 	default:
6287c478bd9Sstevel@tonic-gate 		error = DDI_FAILURE;
6297c478bd9Sstevel@tonic-gate 	}
6307c478bd9Sstevel@tonic-gate 	return (error);
6317c478bd9Sstevel@tonic-gate }
6327c478bd9Sstevel@tonic-gate 
633*8ec5a142Sedp struct cb_ops	iwscn_cb_ops = {
634*8ec5a142Sedp 	iwscnopen,		/* open */
635*8ec5a142Sedp 	iwscnclose,		/* close */
636*8ec5a142Sedp 	nodev,			/* strategy */
637*8ec5a142Sedp 	nodev,			/* print */
638*8ec5a142Sedp 	nodev,			/* dump */
639*8ec5a142Sedp 	iwscnread,		/* read */
640*8ec5a142Sedp 	iwscnwrite,		/* write */
641*8ec5a142Sedp 	iwscnioctl,		/* ioctl */
642*8ec5a142Sedp 	nodev,			/* devmap */
643*8ec5a142Sedp 	nodev,			/* mmap */
644*8ec5a142Sedp 	nodev, 			/* segmap */
645*8ec5a142Sedp 	iwscnpoll,		/* poll */
646*8ec5a142Sedp 	ddi_prop_op,		/* cb_prop_op */
647*8ec5a142Sedp 	NULL,			/* streamtab  */
648*8ec5a142Sedp 	D_MP			/* Driver compatibility flag */
649*8ec5a142Sedp };
6507c478bd9Sstevel@tonic-gate 
651*8ec5a142Sedp struct dev_ops	iwscn_ops = {
652*8ec5a142Sedp 	DEVO_REV,		/* devo_rev, */
653*8ec5a142Sedp 	0,			/* refcnt  */
654*8ec5a142Sedp 	iwscninfo,		/* info */
655*8ec5a142Sedp 	nulldev,		/* identify */
656*8ec5a142Sedp 	nulldev,		/* probe */
657*8ec5a142Sedp 	iwscnattach,		/* attach */
658*8ec5a142Sedp 	nodev,			/* detach */
659*8ec5a142Sedp 	nodev,			/* reset */
660*8ec5a142Sedp 	&iwscn_cb_ops,		/* driver operations */
661*8ec5a142Sedp 	NULL			/* bus operations */
662*8ec5a142Sedp };
663*8ec5a142Sedp 
664*8ec5a142Sedp /*
665*8ec5a142Sedp  * Module linkage information for the kernel.
666*8ec5a142Sedp  */
667*8ec5a142Sedp static struct modldrv modldrv = {
668*8ec5a142Sedp 	&mod_driverops, /* Type of module.  This one is a pseudo driver */
669*8ec5a142Sedp 	"Workstation Redirection driver %I%",
670*8ec5a142Sedp 	&iwscn_ops,	/* driver ops */
671*8ec5a142Sedp };
672*8ec5a142Sedp 
673*8ec5a142Sedp static struct modlinkage modlinkage = {
674*8ec5a142Sedp 	MODREV_1,
675*8ec5a142Sedp 	&modldrv,
676*8ec5a142Sedp 	NULL
677*8ec5a142Sedp };
678*8ec5a142Sedp 
679*8ec5a142Sedp int
680*8ec5a142Sedp _init(void)
6817c478bd9Sstevel@tonic-gate {
682*8ec5a142Sedp 	return (mod_install(&modlinkage));
6837c478bd9Sstevel@tonic-gate }
6847c478bd9Sstevel@tonic-gate 
685*8ec5a142Sedp int
686*8ec5a142Sedp _fini(void)
6877c478bd9Sstevel@tonic-gate {
6887c478bd9Sstevel@tonic-gate 	return (EBUSY);
6897c478bd9Sstevel@tonic-gate }
6907c478bd9Sstevel@tonic-gate 
691*8ec5a142Sedp int
692*8ec5a142Sedp _info(struct modinfo *modinfop)
6937c478bd9Sstevel@tonic-gate {
694*8ec5a142Sedp 	return (mod_info(&modlinkage, modinfop));
6957c478bd9Sstevel@tonic-gate }
696