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