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*5331Samw * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 240Sstevel@tonic-gate * Use is subject to license terms. 250Sstevel@tonic-gate */ 260Sstevel@tonic-gate 270Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 280Sstevel@tonic-gate 290Sstevel@tonic-gate /* 301907Sedp * workstation console redirecting driver 310Sstevel@tonic-gate * 320Sstevel@tonic-gate * Redirects all I/O through a given device instance to the device designated 330Sstevel@tonic-gate * as the current target, as given by the vnode associated with the first 340Sstevel@tonic-gate * entry in the list of redirections for the given device instance. The 350Sstevel@tonic-gate * implementation assumes that this vnode denotes a STREAMS device; this is 360Sstevel@tonic-gate * perhaps a bug. 370Sstevel@tonic-gate * 380Sstevel@tonic-gate * Supports the SRIOCSREDIR ioctl for designating a new redirection target. 390Sstevel@tonic-gate * The new target is added to the front of a list of potentially active 400Sstevel@tonic-gate * designees. Should the device at the front of this list be closed, the new 410Sstevel@tonic-gate * front entry assumes active duty. (Stated differently, redirection targets 420Sstevel@tonic-gate * stack, except that it's possible for entries in the interior of the stack 430Sstevel@tonic-gate * to go away.) 440Sstevel@tonic-gate * 450Sstevel@tonic-gate * Supports the SRIOCISREDIR ioctl for inquiring whether the descriptor given 460Sstevel@tonic-gate * as argument is the current front of the redirection list associated with 470Sstevel@tonic-gate * the descriptor on which the ioctl was issued. 480Sstevel@tonic-gate */ 490Sstevel@tonic-gate 500Sstevel@tonic-gate #include <sys/types.h> 510Sstevel@tonic-gate #include <sys/sysmacros.h> 520Sstevel@tonic-gate #include <sys/open.h> 530Sstevel@tonic-gate #include <sys/param.h> 540Sstevel@tonic-gate #include <sys/systm.h> 550Sstevel@tonic-gate #include <sys/signal.h> 560Sstevel@tonic-gate #include <sys/cred.h> 570Sstevel@tonic-gate #include <sys/user.h> 580Sstevel@tonic-gate #include <sys/proc.h> 590Sstevel@tonic-gate #include <sys/vnode.h> 600Sstevel@tonic-gate #include <sys/uio.h> 610Sstevel@tonic-gate #include <sys/file.h> 620Sstevel@tonic-gate #include <sys/kmem.h> 630Sstevel@tonic-gate #include <sys/stat.h> 640Sstevel@tonic-gate #include <sys/stream.h> 650Sstevel@tonic-gate #include <sys/stropts.h> 660Sstevel@tonic-gate #include <sys/strsubr.h> 670Sstevel@tonic-gate #include <sys/poll.h> 680Sstevel@tonic-gate #include <sys/debug.h> 690Sstevel@tonic-gate #include <sys/strredir.h> 700Sstevel@tonic-gate #include <sys/conf.h> 710Sstevel@tonic-gate #include <sys/ddi.h> 720Sstevel@tonic-gate #include <sys/sunddi.h> 731907Sedp #include <sys/errno.h> 741907Sedp #include <sys/modctl.h> 751907Sedp #include <sys/sunldi.h> 761907Sedp #include <sys/consdev.h> 771907Sedp #include <sys/fs/snode.h> 780Sstevel@tonic-gate 790Sstevel@tonic-gate /* 801907Sedp * Global data 810Sstevel@tonic-gate */ 820Sstevel@tonic-gate static dev_info_t *iwscn_dip; 830Sstevel@tonic-gate 841907Sedp /* 851907Sedp * We record the list of redirections as a linked list of iwscn_list_t 861907Sedp * structures. We need to keep track of the target's vp, so that 871907Sedp * we can vector reads, writes, etc. off to the current designee. 881907Sedp */ 891907Sedp typedef struct _iwscn_list { 901907Sedp struct _iwscn_list *wl_next; /* next entry */ 911907Sedp vnode_t *wl_vp; /* target's vnode */ 921907Sedp int wl_ref_cnt; /* operation in progress */ 931907Sedp boolean_t wl_is_console; /* is the real console */ 941907Sedp } iwscn_list_t; 951907Sedp static iwscn_list_t *iwscn_list; 960Sstevel@tonic-gate 970Sstevel@tonic-gate /* 981907Sedp * iwscn_list_lock serializes modifications to the global iwscn_list list. 991907Sedp * 1001907Sedp * iwscn_list_cv is used when freeing an entry from iwscn_list to allow 1011907Sedp * the caller to wait till the wl_ref_cnt field is zero. 1021907Sedp * 1031907Sedp * iwscn_redirect_lock is used to serialize redirection requests. This 1041907Sedp * is required to ensure that all active redirection streams have 1051907Sedp * the redirection streams module (redirmod) pushed on them. 1061907Sedp * 1071907Sedp * If both iwscn_redirect_lock and iwscn_list_lock must be held then 108*5331Samw * iwscn_redirect_lock must be acquired first. 1090Sstevel@tonic-gate */ 1101907Sedp static kcondvar_t iwscn_list_cv; 1111907Sedp static kmutex_t iwscn_list_lock; 1121907Sedp static kmutex_t iwscn_redirect_lock; 1130Sstevel@tonic-gate 1140Sstevel@tonic-gate /* 1151907Sedp * Routines for managing iwscn_list 1160Sstevel@tonic-gate */ 1171907Sedp static vnode_t * 1181907Sedp str_vp(vnode_t *vp) 1191907Sedp { 1201907Sedp /* 1211907Sedp * Here we switch to using the vnode that is linked 1221907Sedp * to from the stream queue. (In the case of device 1231907Sedp * streams this will correspond to the common vnode 1241907Sedp * for the device.) The reason we use this vnode 1251907Sedp * is that when wcmclose() calls srpop(), this is the 1261907Sedp * only vnode that it has access to. 1271907Sedp */ 1281907Sedp ASSERT(vp->v_stream != NULL); 1291907Sedp return (vp->v_stream->sd_vnode); 1301907Sedp } 1310Sstevel@tonic-gate 1320Sstevel@tonic-gate /* 1332485Sns92644 * Interrupt any operations that may be outstanding against this vnode. 1342485Sns92644 * optionally, wait for them to complete. 1352485Sns92644 */ 1362485Sns92644 static void 1372485Sns92644 srinterrupt(iwscn_list_t *lp, boolean_t wait) 1382485Sns92644 { 1392485Sns92644 ASSERT(MUTEX_HELD(&iwscn_list_lock)); 1402485Sns92644 1412485Sns92644 while (lp->wl_ref_cnt != 0) { 1422485Sns92644 strsetrerror(lp->wl_vp, EINTR, 0, NULL); 1432485Sns92644 strsetwerror(lp->wl_vp, EINTR, 0, NULL); 1442485Sns92644 if (!wait) 1452485Sns92644 break; 1462485Sns92644 cv_wait(&iwscn_list_cv, &iwscn_list_lock); 1472485Sns92644 } 1482485Sns92644 } 1492485Sns92644 1502485Sns92644 /* 1511907Sedp * Remove vp from the redirection list rooted at iwscn_list, should it 1522485Sns92644 * be there. Return a pointer to the removed entry. 1530Sstevel@tonic-gate */ 1541907Sedp static iwscn_list_t * 1552485Sns92644 srrm(vnode_t *vp) 1561907Sedp { 1571907Sedp iwscn_list_t *lp, **lpp; 1581907Sedp 1591907Sedp ASSERT(MUTEX_HELD(&iwscn_list_lock)); 1601907Sedp 1611907Sedp /* Get the stream vnode */ 1621907Sedp vp = str_vp(vp); 1631907Sedp ASSERT(vp); 1641907Sedp 1651907Sedp /* Look for this vnode on the redirection list */ 1661907Sedp for (lpp = &iwscn_list; (lp = *lpp) != NULL; lpp = &lp->wl_next) { 1671907Sedp if (lp->wl_vp == vp) 1681907Sedp break; 1691907Sedp } 1702485Sns92644 if (lp != NULL) 1712485Sns92644 /* Found it, remove this entry from the redirection list */ 1722485Sns92644 *lpp = lp->wl_next; 1730Sstevel@tonic-gate 1742485Sns92644 return (lp); 1751907Sedp } 1760Sstevel@tonic-gate 1770Sstevel@tonic-gate /* 1781907Sedp * Push vp onto the redirection list. 1791907Sedp * If it's already there move it to the front position. 1800Sstevel@tonic-gate */ 1811907Sedp static void 1821907Sedp srpush(vnode_t *vp, boolean_t is_console) 1831907Sedp { 1841907Sedp iwscn_list_t *lp; 1850Sstevel@tonic-gate 1861907Sedp ASSERT(MUTEX_HELD(&iwscn_list_lock)); 1870Sstevel@tonic-gate 1881907Sedp /* Get the stream vnode */ 1891907Sedp vp = str_vp(vp); 1901907Sedp ASSERT(vp); 1910Sstevel@tonic-gate 1921907Sedp /* Check if it's already on the redirection list */ 1932485Sns92644 if ((lp = srrm(vp)) == NULL) { 1941907Sedp lp = kmem_zalloc(sizeof (*lp), KM_SLEEP); 1951907Sedp lp->wl_vp = vp; 1962485Sns92644 lp->wl_is_console = is_console; 1971907Sedp } 1981907Sedp /* 1991907Sedp * Note that if this vnode was already somewhere on the redirection 2001907Sedp * list then we removed it above and are now bumping it up to the 2012485Sns92644 * front of the redirection list. 2021907Sedp */ 2031907Sedp lp->wl_next = iwscn_list; 2041907Sedp iwscn_list = lp; 2050Sstevel@tonic-gate } 2060Sstevel@tonic-gate 2070Sstevel@tonic-gate /* 2082485Sns92644 * This vnode is no longer a valid redirection target. Terminate any current 2092485Sns92644 * operations. If closing, wait for them to complete, then free the entry. 2102485Sns92644 * If called because a hangup has occurred, just deprecate the entry to ensure 2112485Sns92644 * it won't become the target again. 2120Sstevel@tonic-gate */ 2131907Sedp void 2142485Sns92644 srpop(vnode_t *vp, boolean_t close) 2151907Sedp { 2162485Sns92644 iwscn_list_t *tlp; /* This target's entry */ 2172485Sns92644 iwscn_list_t *lp, **lpp; 2182485Sns92644 2191907Sedp mutex_enter(&iwscn_list_lock); 2202485Sns92644 2212485Sns92644 /* 2222485Sns92644 * Ensure no further operations are directed at the target 2232485Sns92644 * by removing it from the redirection list. 2242485Sns92644 */ 2252485Sns92644 if ((tlp = srrm(vp)) == NULL) { 2262485Sns92644 /* vnode wasn't in the list */ 2272485Sns92644 mutex_exit(&iwscn_list_lock); 2282485Sns92644 return; 2292485Sns92644 } 2302485Sns92644 /* 2312485Sns92644 * Terminate any current operations. 2322485Sns92644 * If we're closing, wait until they complete. 2332485Sns92644 */ 2342485Sns92644 srinterrupt(tlp, close); 2352485Sns92644 2362485Sns92644 if (close) { 2372485Sns92644 /* We're finished with this target */ 2382485Sns92644 kmem_free(tlp, sizeof (*tlp)); 2392485Sns92644 } else { 2402485Sns92644 /* 2412485Sns92644 * Deprecate the entry. There's no need for a flag to indicate 2422485Sns92644 * this state, it just needs to be moved to the back of the list 2432485Sns92644 * behind the underlying console device. Since the underlying 2442485Sns92644 * device anchors the list and is never removed, this entry can 2452485Sns92644 * never return to the front again to become the target. 2462485Sns92644 */ 2472485Sns92644 for (lpp = &iwscn_list; (lp = *lpp) != NULL; ) 2482485Sns92644 lpp = &lp->wl_next; 2492485Sns92644 tlp->wl_next = NULL; 2502485Sns92644 *lpp = tlp; 2512485Sns92644 } 2521907Sedp mutex_exit(&iwscn_list_lock); 2531907Sedp } 2541907Sedp 2551907Sedp /* Get a hold on the current target */ 2561907Sedp static iwscn_list_t * 2571907Sedp srhold() 2581907Sedp { 2591907Sedp iwscn_list_t *lp; 2601907Sedp 2611907Sedp mutex_enter(&iwscn_list_lock); 2621907Sedp ASSERT(iwscn_list != NULL); 2631907Sedp lp = iwscn_list; 2641907Sedp ASSERT(lp->wl_ref_cnt >= 0); 2651907Sedp lp->wl_ref_cnt++; 2661907Sedp mutex_exit(&iwscn_list_lock); 2671907Sedp 2681907Sedp return (lp); 2691907Sedp } 2701907Sedp 2711907Sedp /* Release a hold on an entry from the redirection list */ 2721907Sedp static void 2731907Sedp srrele(iwscn_list_t *lp) 2741907Sedp { 2751907Sedp ASSERT(lp != NULL); 2761907Sedp mutex_enter(&iwscn_list_lock); 2771907Sedp ASSERT(lp->wl_ref_cnt > 0); 2781907Sedp lp->wl_ref_cnt--; 2791907Sedp cv_broadcast(&iwscn_list_cv); 2801907Sedp mutex_exit(&iwscn_list_lock); 2811907Sedp } 2821907Sedp 2831907Sedp static int 2841907Sedp iwscnread(dev_t dev, uio_t *uio, cred_t *cred) 2851907Sedp { 2861907Sedp iwscn_list_t *lp; 2871907Sedp int error; 2881907Sedp 2891907Sedp ASSERT(getminor(dev) == 0); 2901907Sedp 2911907Sedp lp = srhold(); 2921907Sedp error = strread(lp->wl_vp, uio, cred); 2931907Sedp srrele(lp); 2941907Sedp 2951907Sedp return (error); 2961907Sedp } 2971907Sedp 2981907Sedp static int 2991907Sedp iwscnwrite(dev_t dev, uio_t *uio, cred_t *cred) 3001907Sedp { 3011907Sedp iwscn_list_t *lp; 3021907Sedp int error; 3031907Sedp 3041907Sedp ASSERT(getminor(dev) == 0); 3051907Sedp 3061907Sedp lp = srhold(); 3071907Sedp error = strwrite(lp->wl_vp, uio, cred); 3081907Sedp srrele(lp); 3091907Sedp 3101907Sedp return (error); 3111907Sedp } 3121907Sedp 3131907Sedp static int 3141907Sedp iwscnpoll(dev_t dev, short events, int anyyet, short *reventsp, 3151907Sedp struct pollhead **phpp) 3161907Sedp { 3171907Sedp iwscn_list_t *lp; 3181907Sedp int error; 3191907Sedp 3201907Sedp ASSERT(getminor(dev) == 0); 3211907Sedp 3221907Sedp lp = srhold(); 323*5331Samw error = VOP_POLL(lp->wl_vp, events, anyyet, reventsp, phpp, NULL); 3241907Sedp srrele(lp); 3251907Sedp 3261907Sedp return (error); 3271907Sedp } 3281907Sedp 3291907Sedp static int 3301907Sedp iwscnioctl(dev_t dev, int cmd, intptr_t arg, int flag, 3311907Sedp cred_t *cred, int *rvalp) 3321907Sedp { 3331907Sedp iwscn_list_t *lp; 3341907Sedp file_t *f; 3351907Sedp char modname[FMNAMESZ + 1] = " "; 3361907Sedp int error = 0; 3371907Sedp 3381907Sedp ASSERT(getminor(dev) == 0); 3391907Sedp 3401907Sedp switch (cmd) { 3411907Sedp case SRIOCSREDIR: 3421907Sedp /* Serialize all pushes of the redirection module */ 3431907Sedp mutex_enter(&iwscn_redirect_lock); 3441907Sedp 3451907Sedp /* 3461907Sedp * Find the vnode corresponding to the file descriptor 3471907Sedp * argument and verify that it names a stream. 3481907Sedp */ 3491907Sedp if ((f = getf((int)arg)) == NULL) { 3501907Sedp mutex_exit(&iwscn_redirect_lock); 3511907Sedp return (EBADF); 3521907Sedp } 3531907Sedp if (f->f_vnode->v_stream == NULL) { 3541907Sedp releasef((int)arg); 3551907Sedp mutex_exit(&iwscn_redirect_lock); 3561907Sedp return (ENOSTR); 3571907Sedp } 3581907Sedp 3591907Sedp /* 3601907Sedp * If the user is trying to redirect console output 3611907Sedp * back to the underlying console via SRIOCSREDIR 3621907Sedp * then they are evil and we'll stop them here. 3631907Sedp */ 3641907Sedp if (str_vp(f->f_vnode) == str_vp(rwsconsvp)) { 3651907Sedp releasef((int)arg); 3661907Sedp mutex_exit(&iwscn_redirect_lock); 3671907Sedp return (EINVAL); 3681907Sedp } 3691907Sedp 3701907Sedp /* 3711907Sedp * Check if this stream already has the redirection 3721907Sedp * module pushed onto it. I_LOOK returns an error 3731907Sedp * if there are no modules pushed onto the stream. 3741907Sedp */ 3751907Sedp (void) strioctl(f->f_vnode, I_LOOK, (intptr_t)modname, 3761907Sedp FKIOCTL, K_TO_K, cred, rvalp); 3771907Sedp if (strcmp(modname, STRREDIR_MOD) != 0) { 3781907Sedp 3791907Sedp /* 3801907Sedp * Push a new instance of the redirecting module onto 3811907Sedp * the stream, so that its close routine can notify 3821907Sedp * us when the overall stream is closed. (In turn, 3831907Sedp * we'll then remove it from the redirection list.) 3841907Sedp */ 3851907Sedp error = strioctl(f->f_vnode, I_PUSH, 3861907Sedp (intptr_t)STRREDIR_MOD, FKIOCTL, K_TO_K, 3871907Sedp cred, rvalp); 3881907Sedp 3891907Sedp if (error != 0) { 3901907Sedp releasef((int)arg); 3911907Sedp mutex_exit(&iwscn_redirect_lock); 3921907Sedp return (error); 3931907Sedp } 3941907Sedp } 3951907Sedp 3961907Sedp /* Push it onto the redirection stack */ 3971907Sedp mutex_enter(&iwscn_list_lock); 3981907Sedp srpush(f->f_vnode, B_FALSE); 3991907Sedp mutex_exit(&iwscn_list_lock); 4001907Sedp 4011907Sedp releasef((int)arg); 4021907Sedp mutex_exit(&iwscn_redirect_lock); 4031907Sedp return (0); 4041907Sedp 4051907Sedp case SRIOCISREDIR: 4061907Sedp /* 4071907Sedp * Find the vnode corresponding to the file descriptor 4081907Sedp * argument and verify that it names a stream. 4091907Sedp */ 4101907Sedp if ((f = getf((int)arg)) == NULL) { 4111907Sedp return (EBADF); 4121907Sedp } 4131907Sedp if (f->f_vnode->v_stream == NULL) { 4141907Sedp releasef((int)arg); 4151907Sedp return (ENOSTR); 4161907Sedp } 4171907Sedp 4181907Sedp lp = srhold(); 4191907Sedp *rvalp = (str_vp(f->f_vnode) == lp->wl_vp); 4201907Sedp srrele(lp); 4211907Sedp releasef((int)arg); 4221907Sedp return (0); 4231907Sedp 4241907Sedp case I_POP: 4251907Sedp /* 4261907Sedp * We need to serialize I_POP operations with 4271907Sedp * SRIOCSREDIR operations so we don't accidently 4281907Sedp * remove the redirection module from a stream. 4291907Sedp */ 4301907Sedp mutex_enter(&iwscn_redirect_lock); 4311907Sedp lp = srhold(); 4321907Sedp 4331907Sedp /* 4341907Sedp * Here we need to protect against process that might 4351907Sedp * try to pop off the redirection module from the 4361907Sedp * redirected stream. Popping other modules is allowed. 4371907Sedp * 4381907Sedp * It's ok to hold iwscn_list_lock while doing the 4391907Sedp * I_LOOK since it's such a simple operation. 4401907Sedp */ 4411907Sedp (void) strioctl(lp->wl_vp, I_LOOK, (intptr_t)modname, 4421907Sedp FKIOCTL, K_TO_K, cred, rvalp); 4431907Sedp 4441907Sedp if (strcmp(STRREDIR_MOD, modname) == 0) { 4451907Sedp srrele(lp); 4461907Sedp mutex_exit(&iwscn_redirect_lock); 4471907Sedp return (EINVAL); 4481907Sedp } 4491907Sedp 4501907Sedp /* Process the ioctl normally */ 451*5331Samw error = VOP_IOCTL(lp->wl_vp, cmd, arg, flag, cred, rvalp, NULL); 4521907Sedp 4531907Sedp srrele(lp); 4541907Sedp mutex_exit(&iwscn_redirect_lock); 4551907Sedp return (error); 4561907Sedp } 4571907Sedp 4581907Sedp /* Process the ioctl normally */ 4591907Sedp lp = srhold(); 460*5331Samw error = VOP_IOCTL(lp->wl_vp, cmd, arg, flag, cred, rvalp, NULL); 4611907Sedp srrele(lp); 4621907Sedp return (error); 4631907Sedp } 4641907Sedp 4651907Sedp /* ARGSUSED */ 4661907Sedp static int 4671907Sedp iwscnopen(dev_t *devp, int flag, int state, cred_t *cred) 4681907Sedp { 4691907Sedp iwscn_list_t *lp; 4701907Sedp vnode_t *vp = rwsconsvp; 4711907Sedp 4721907Sedp if (state != OTYP_CHR) 4731907Sedp return (ENXIO); 4741907Sedp 4751907Sedp if (getminor(*devp) != 0) 4761907Sedp return (ENXIO); 4771907Sedp 4781907Sedp /* 4791907Sedp * You can't really open us until the console subsystem 4801907Sedp * has been configured. 4811907Sedp */ 4821907Sedp if (rwsconsvp == NULL) 4831907Sedp return (ENXIO); 4841907Sedp 4851907Sedp /* 4861907Sedp * Check if this is the first open of this device or if 4871907Sedp * there is currently no redirection going on. (Ie, we're 4881907Sedp * sending output to underlying console device.) 4891907Sedp */ 4901907Sedp mutex_enter(&iwscn_list_lock); 4911907Sedp if ((iwscn_list == NULL) || (iwscn_list->wl_vp == str_vp(vp))) { 4921907Sedp int error = 0; 4931907Sedp 4941907Sedp /* Don't hold the list lock across an VOP_OPEN */ 4951907Sedp mutex_exit(&iwscn_list_lock); 4961907Sedp 4971907Sedp /* 4981907Sedp * There is currently no redirection going on. 4991907Sedp * pass this open request onto the console driver 5001907Sedp */ 501*5331Samw error = VOP_OPEN(&vp, flag, cred, NULL); 5021907Sedp if (error != 0) 5031907Sedp return (error); 5041907Sedp 505*5331Samw /* Re-acquire the list lock */ 5061907Sedp mutex_enter(&iwscn_list_lock); 5071907Sedp 5081907Sedp if (iwscn_list == NULL) { 5091907Sedp /* Save this vnode on the redirection list */ 5101907Sedp srpush(vp, B_TRUE); 5111907Sedp } else { 5121907Sedp /* 5131907Sedp * In this case there must already be a copy of 5141907Sedp * this vnode on the list, so we can free up this one. 5151907Sedp */ 516*5331Samw (void) VOP_CLOSE(vp, flag, 1, (offset_t)0, cred, NULL); 5171907Sedp } 5181907Sedp } 5191907Sedp 5201907Sedp /* 5211907Sedp * XXX This is an ugly legacy hack that has been around 5221907Sedp * forever. This code is here because this driver (the 5231907Sedp * iwscn driver) is a character driver layered over a 5241907Sedp * streams driver. 5251907Sedp * 5261907Sedp * Normally streams recieve notification whenever a process 5271907Sedp * closes its last reference to that stream so that it can 5281907Sedp * clean up any signal handling related configuration. (Ie, 5291907Sedp * when a stream is configured to deliver a signal to a 5301907Sedp * process upon certain events.) This is a feature supported 5311907Sedp * by the streams framework. 5321907Sedp * 5331907Sedp * But character/block drivers don't recieve this type 5341907Sedp * of notification. A character/block driver's close routine 5351907Sedp * is only invoked upon the last close of the device. This 5361907Sedp * is an artifact of the multiple open/single close driver 5371907Sedp * model currently supported by solaris. 5381907Sedp * 5391907Sedp * So a problem occurs when a character driver layers itself 5401907Sedp * on top of a streams driver. Since this driver doesn't always 5411907Sedp * receive a close notification when a process closes its 5421907Sedp * last reference to it, this driver can't tell the stream 5431907Sedp * it's layered upon to clean up any signal handling 5441907Sedp * configuration for that process. 5451907Sedp * 5461907Sedp * So here we hack around that by manually cleaning up the 5471907Sedp * signal handling list upon each open. It doesn't guarantee 5481907Sedp * that the signaling handling data stored in the stream will 5491907Sedp * always be up to date, but it'll be more up to date than 5501907Sedp * it would be if we didn't do this. 5511907Sedp * 5521907Sedp * The real way to solve this problem would be to change 5531907Sedp * the device framework from an multiple open/single close 5541907Sedp * model to a multiple open/multiple close model. Then 5551907Sedp * character/block drivers could pass on close requests 5561907Sedp * to streams layered underneath. 5571907Sedp */ 5581907Sedp str_cn_clean(VTOS(rwsconsvp)->s_commonvp); 5591907Sedp for (lp = iwscn_list; lp != NULL; lp = lp->wl_next) { 5601907Sedp ASSERT(lp->wl_vp->v_stream != NULL); 5611907Sedp str_cn_clean(lp->wl_vp); 5621907Sedp } 5631907Sedp 5641907Sedp mutex_exit(&iwscn_list_lock); 5651907Sedp return (0); 5661907Sedp } 5671907Sedp 5681907Sedp /* ARGSUSED */ 5691907Sedp static int 5701907Sedp iwscnclose(dev_t dev, int flag, int state, cred_t *cred) 5711907Sedp { 5722485Sns92644 iwscn_list_t *lp; 5732485Sns92644 5741907Sedp ASSERT(getminor(dev) == 0); 5751907Sedp 5761907Sedp if (state != OTYP_CHR) 5771907Sedp return (ENXIO); 5781907Sedp 5791907Sedp mutex_enter(&iwscn_list_lock); 5802485Sns92644 /* 5812485Sns92644 * Remove each entry from the redirection list, terminate any 5822485Sns92644 * current operations, wait for them to finish, then free the entry. 5832485Sns92644 */ 5842485Sns92644 while (iwscn_list != NULL) { 5852485Sns92644 lp = srrm(iwscn_list->wl_vp); 5862485Sns92644 ASSERT(lp != NULL); 5872485Sns92644 srinterrupt(lp, B_TRUE); 5881907Sedp 5892485Sns92644 if (lp->wl_is_console == B_TRUE) 5902485Sns92644 /* Close the underlying console device. */ 591*5331Samw (void) VOP_CLOSE(lp->wl_vp, 0, 1, (offset_t)0, kcred, 592*5331Samw NULL); 5931907Sedp 5942485Sns92644 kmem_free(lp, sizeof (*lp)); 5952485Sns92644 } 5961907Sedp mutex_exit(&iwscn_list_lock); 5971907Sedp return (0); 5981907Sedp } 5990Sstevel@tonic-gate 6000Sstevel@tonic-gate /*ARGSUSED*/ 6010Sstevel@tonic-gate static int 6020Sstevel@tonic-gate iwscnattach(dev_info_t *devi, ddi_attach_cmd_t cmd) 6030Sstevel@tonic-gate { 6041907Sedp /* 6051907Sedp * This is a pseudo device so there will never be more than 6061907Sedp * one instance attached at a time 6071907Sedp */ 6081907Sedp ASSERT(iwscn_dip == NULL); 6090Sstevel@tonic-gate 6100Sstevel@tonic-gate if (ddi_create_minor_node(devi, "iwscn", S_IFCHR, 6110Sstevel@tonic-gate 0, DDI_PSEUDO, NULL) == DDI_FAILURE) { 6121907Sedp return (DDI_FAILURE); 6130Sstevel@tonic-gate } 6141907Sedp 6150Sstevel@tonic-gate iwscn_dip = devi; 6161907Sedp mutex_init(&iwscn_list_lock, NULL, MUTEX_DRIVER, NULL); 6171907Sedp mutex_init(&iwscn_redirect_lock, NULL, MUTEX_DRIVER, NULL); 6181907Sedp cv_init(&iwscn_list_cv, NULL, CV_DRIVER, NULL); 6191907Sedp 6200Sstevel@tonic-gate return (DDI_SUCCESS); 6210Sstevel@tonic-gate } 6220Sstevel@tonic-gate 6230Sstevel@tonic-gate /* ARGSUSED */ 6240Sstevel@tonic-gate static int 6250Sstevel@tonic-gate iwscninfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 6260Sstevel@tonic-gate { 6270Sstevel@tonic-gate int error; 6280Sstevel@tonic-gate 6290Sstevel@tonic-gate switch (infocmd) { 6300Sstevel@tonic-gate case DDI_INFO_DEVT2DEVINFO: 6310Sstevel@tonic-gate if (iwscn_dip == NULL) { 6320Sstevel@tonic-gate error = DDI_FAILURE; 6330Sstevel@tonic-gate } else { 6340Sstevel@tonic-gate *result = (void *)iwscn_dip; 6350Sstevel@tonic-gate error = DDI_SUCCESS; 6360Sstevel@tonic-gate } 6370Sstevel@tonic-gate break; 6380Sstevel@tonic-gate case DDI_INFO_DEVT2INSTANCE: 6390Sstevel@tonic-gate *result = (void *)0; 6400Sstevel@tonic-gate error = DDI_SUCCESS; 6410Sstevel@tonic-gate break; 6420Sstevel@tonic-gate default: 6430Sstevel@tonic-gate error = DDI_FAILURE; 6440Sstevel@tonic-gate } 6450Sstevel@tonic-gate return (error); 6460Sstevel@tonic-gate } 6470Sstevel@tonic-gate 6481907Sedp struct cb_ops iwscn_cb_ops = { 6491907Sedp iwscnopen, /* open */ 6501907Sedp iwscnclose, /* close */ 6511907Sedp nodev, /* strategy */ 6521907Sedp nodev, /* print */ 6531907Sedp nodev, /* dump */ 6541907Sedp iwscnread, /* read */ 6551907Sedp iwscnwrite, /* write */ 6561907Sedp iwscnioctl, /* ioctl */ 6571907Sedp nodev, /* devmap */ 6581907Sedp nodev, /* mmap */ 6591907Sedp nodev, /* segmap */ 6601907Sedp iwscnpoll, /* poll */ 6611907Sedp ddi_prop_op, /* cb_prop_op */ 6621907Sedp NULL, /* streamtab */ 6631907Sedp D_MP /* Driver compatibility flag */ 6641907Sedp }; 6650Sstevel@tonic-gate 6661907Sedp struct dev_ops iwscn_ops = { 6671907Sedp DEVO_REV, /* devo_rev, */ 6681907Sedp 0, /* refcnt */ 6691907Sedp iwscninfo, /* info */ 6701907Sedp nulldev, /* identify */ 6711907Sedp nulldev, /* probe */ 6721907Sedp iwscnattach, /* attach */ 6731907Sedp nodev, /* detach */ 6741907Sedp nodev, /* reset */ 6751907Sedp &iwscn_cb_ops, /* driver operations */ 6761907Sedp NULL /* bus operations */ 6771907Sedp }; 6780Sstevel@tonic-gate 6791907Sedp /* 6801907Sedp * Module linkage information for the kernel. 6811907Sedp */ 6821907Sedp static struct modldrv modldrv = { 6831907Sedp &mod_driverops, /* Type of module. This one is a pseudo driver */ 6841907Sedp "Workstation Redirection driver %I%", 6851907Sedp &iwscn_ops, /* driver ops */ 6861907Sedp }; 6870Sstevel@tonic-gate 6881907Sedp static struct modlinkage modlinkage = { 6891907Sedp MODREV_1, 6901907Sedp &modldrv, 6911907Sedp NULL 6921907Sedp }; 6930Sstevel@tonic-gate 6941907Sedp int 6951907Sedp _init(void) 6960Sstevel@tonic-gate { 6971907Sedp return (mod_install(&modlinkage)); 6980Sstevel@tonic-gate } 6990Sstevel@tonic-gate 7001907Sedp int 7011907Sedp _fini(void) 7020Sstevel@tonic-gate { 7031907Sedp return (EBUSY); 7040Sstevel@tonic-gate } 7050Sstevel@tonic-gate 7061907Sedp int 7071907Sedp _info(struct modinfo *modinfop) 7080Sstevel@tonic-gate { 7091907Sedp return (mod_info(&modlinkage, modinfop)); 7100Sstevel@tonic-gate } 711