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