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*da6c28aaSamw * Copyright 2007 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 /* 308ec5a142Sedp * 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> 738ec5a142Sedp #include <sys/errno.h> 748ec5a142Sedp #include <sys/modctl.h> 758ec5a142Sedp #include <sys/sunldi.h> 768ec5a142Sedp #include <sys/consdev.h> 778ec5a142Sedp #include <sys/fs/snode.h> 787c478bd9Sstevel@tonic-gate 797c478bd9Sstevel@tonic-gate /* 808ec5a142Sedp * Global data 817c478bd9Sstevel@tonic-gate */ 827c478bd9Sstevel@tonic-gate static dev_info_t *iwscn_dip; 837c478bd9Sstevel@tonic-gate 847c478bd9Sstevel@tonic-gate /* 858ec5a142Sedp * We record the list of redirections as a linked list of iwscn_list_t 868ec5a142Sedp * structures. We need to keep track of the target's vp, so that 878ec5a142Sedp * we can vector reads, writes, etc. off to the current designee. 887c478bd9Sstevel@tonic-gate */ 898ec5a142Sedp typedef struct _iwscn_list { 908ec5a142Sedp struct _iwscn_list *wl_next; /* next entry */ 918ec5a142Sedp vnode_t *wl_vp; /* target's vnode */ 928ec5a142Sedp int wl_ref_cnt; /* operation in progress */ 938ec5a142Sedp boolean_t wl_is_console; /* is the real console */ 948ec5a142Sedp } iwscn_list_t; 958ec5a142Sedp static iwscn_list_t *iwscn_list; 967c478bd9Sstevel@tonic-gate 977c478bd9Sstevel@tonic-gate /* 988ec5a142Sedp * iwscn_list_lock serializes modifications to the global iwscn_list list. 997c478bd9Sstevel@tonic-gate * 1008ec5a142Sedp * iwscn_list_cv is used when freeing an entry from iwscn_list to allow 1018ec5a142Sedp * the caller to wait till the wl_ref_cnt field is zero. 1028ec5a142Sedp * 1038ec5a142Sedp * iwscn_redirect_lock is used to serialize redirection requests. This 1048ec5a142Sedp * is required to ensure that all active redirection streams have 1058ec5a142Sedp * the redirection streams module (redirmod) pushed on them. 1068ec5a142Sedp * 1078ec5a142Sedp * If both iwscn_redirect_lock and iwscn_list_lock must be held then 108*da6c28aaSamw * iwscn_redirect_lock must be acquired first. 1097c478bd9Sstevel@tonic-gate */ 1108ec5a142Sedp static kcondvar_t iwscn_list_cv; 1118ec5a142Sedp static kmutex_t iwscn_list_lock; 1128ec5a142Sedp static kmutex_t iwscn_redirect_lock; 1137c478bd9Sstevel@tonic-gate 1147c478bd9Sstevel@tonic-gate /* 1158ec5a142Sedp * Routines for managing iwscn_list 1167c478bd9Sstevel@tonic-gate */ 1178ec5a142Sedp static vnode_t * 1188ec5a142Sedp str_vp(vnode_t *vp) 1197c478bd9Sstevel@tonic-gate { 1208ec5a142Sedp /* 1218ec5a142Sedp * Here we switch to using the vnode that is linked 1228ec5a142Sedp * to from the stream queue. (In the case of device 1238ec5a142Sedp * streams this will correspond to the common vnode 1248ec5a142Sedp * for the device.) The reason we use this vnode 1258ec5a142Sedp * is that when wcmclose() calls srpop(), this is the 1268ec5a142Sedp * only vnode that it has access to. 1278ec5a142Sedp */ 1288ec5a142Sedp ASSERT(vp->v_stream != NULL); 1298ec5a142Sedp return (vp->v_stream->sd_vnode); 1307c478bd9Sstevel@tonic-gate } 1317c478bd9Sstevel@tonic-gate 1327c478bd9Sstevel@tonic-gate /* 133e493d0faSns92644 * Interrupt any operations that may be outstanding against this vnode. 134e493d0faSns92644 * optionally, wait for them to complete. 135e493d0faSns92644 */ 136e493d0faSns92644 static void 137e493d0faSns92644 srinterrupt(iwscn_list_t *lp, boolean_t wait) 138e493d0faSns92644 { 139e493d0faSns92644 ASSERT(MUTEX_HELD(&iwscn_list_lock)); 140e493d0faSns92644 141e493d0faSns92644 while (lp->wl_ref_cnt != 0) { 142e493d0faSns92644 strsetrerror(lp->wl_vp, EINTR, 0, NULL); 143e493d0faSns92644 strsetwerror(lp->wl_vp, EINTR, 0, NULL); 144e493d0faSns92644 if (!wait) 145e493d0faSns92644 break; 146e493d0faSns92644 cv_wait(&iwscn_list_cv, &iwscn_list_lock); 147e493d0faSns92644 } 148e493d0faSns92644 } 149e493d0faSns92644 150e493d0faSns92644 /* 1518ec5a142Sedp * Remove vp from the redirection list rooted at iwscn_list, should it 152e493d0faSns92644 * be there. Return a pointer to the removed entry. 1537c478bd9Sstevel@tonic-gate */ 1548ec5a142Sedp static iwscn_list_t * 155e493d0faSns92644 srrm(vnode_t *vp) 1568ec5a142Sedp { 1578ec5a142Sedp iwscn_list_t *lp, **lpp; 1588ec5a142Sedp 1598ec5a142Sedp ASSERT(MUTEX_HELD(&iwscn_list_lock)); 1608ec5a142Sedp 1618ec5a142Sedp /* Get the stream vnode */ 1628ec5a142Sedp vp = str_vp(vp); 1638ec5a142Sedp ASSERT(vp); 1648ec5a142Sedp 1658ec5a142Sedp /* Look for this vnode on the redirection list */ 1668ec5a142Sedp for (lpp = &iwscn_list; (lp = *lpp) != NULL; lpp = &lp->wl_next) { 1678ec5a142Sedp if (lp->wl_vp == vp) 1688ec5a142Sedp break; 1698ec5a142Sedp } 170e493d0faSns92644 if (lp != NULL) 1718ec5a142Sedp /* Found it, remove this entry from the redirection list */ 1728ec5a142Sedp *lpp = lp->wl_next; 1738ec5a142Sedp 1748ec5a142Sedp return (lp); 1758ec5a142Sedp } 1768ec5a142Sedp 1778ec5a142Sedp /* 1788ec5a142Sedp * Push vp onto the redirection list. 1798ec5a142Sedp * If it's already there move it to the front position. 1808ec5a142Sedp */ 1818ec5a142Sedp static void 1828ec5a142Sedp srpush(vnode_t *vp, boolean_t is_console) 1838ec5a142Sedp { 1848ec5a142Sedp iwscn_list_t *lp; 1858ec5a142Sedp 1868ec5a142Sedp ASSERT(MUTEX_HELD(&iwscn_list_lock)); 1878ec5a142Sedp 1888ec5a142Sedp /* Get the stream vnode */ 1898ec5a142Sedp vp = str_vp(vp); 1908ec5a142Sedp ASSERT(vp); 1918ec5a142Sedp 1928ec5a142Sedp /* Check if it's already on the redirection list */ 193e493d0faSns92644 if ((lp = srrm(vp)) == NULL) { 1948ec5a142Sedp lp = kmem_zalloc(sizeof (*lp), KM_SLEEP); 1958ec5a142Sedp lp->wl_vp = vp; 196e493d0faSns92644 lp->wl_is_console = is_console; 1978ec5a142Sedp } 1988ec5a142Sedp /* 1998ec5a142Sedp * Note that if this vnode was already somewhere on the redirection 2008ec5a142Sedp * list then we removed it above and are now bumping it up to the 201e493d0faSns92644 * front of the redirection list. 2028ec5a142Sedp */ 2038ec5a142Sedp lp->wl_next = iwscn_list; 2048ec5a142Sedp iwscn_list = lp; 2058ec5a142Sedp } 2068ec5a142Sedp 2078ec5a142Sedp /* 208e493d0faSns92644 * This vnode is no longer a valid redirection target. Terminate any current 209e493d0faSns92644 * operations. If closing, wait for them to complete, then free the entry. 210e493d0faSns92644 * If called because a hangup has occurred, just deprecate the entry to ensure 211e493d0faSns92644 * it won't become the target again. 2128ec5a142Sedp */ 2138ec5a142Sedp void 214e493d0faSns92644 srpop(vnode_t *vp, boolean_t close) 2158ec5a142Sedp { 216e493d0faSns92644 iwscn_list_t *tlp; /* This target's entry */ 217e493d0faSns92644 iwscn_list_t *lp, **lpp; 218e493d0faSns92644 2198ec5a142Sedp mutex_enter(&iwscn_list_lock); 220e493d0faSns92644 221e493d0faSns92644 /* 222e493d0faSns92644 * Ensure no further operations are directed at the target 223e493d0faSns92644 * by removing it from the redirection list. 224e493d0faSns92644 */ 225e493d0faSns92644 if ((tlp = srrm(vp)) == NULL) { 226e493d0faSns92644 /* vnode wasn't in the list */ 227e493d0faSns92644 mutex_exit(&iwscn_list_lock); 228e493d0faSns92644 return; 229e493d0faSns92644 } 230e493d0faSns92644 /* 231e493d0faSns92644 * Terminate any current operations. 232e493d0faSns92644 * If we're closing, wait until they complete. 233e493d0faSns92644 */ 234e493d0faSns92644 srinterrupt(tlp, close); 235e493d0faSns92644 236e493d0faSns92644 if (close) { 237e493d0faSns92644 /* We're finished with this target */ 238e493d0faSns92644 kmem_free(tlp, sizeof (*tlp)); 239e493d0faSns92644 } else { 240e493d0faSns92644 /* 241e493d0faSns92644 * Deprecate the entry. There's no need for a flag to indicate 242e493d0faSns92644 * this state, it just needs to be moved to the back of the list 243e493d0faSns92644 * behind the underlying console device. Since the underlying 244e493d0faSns92644 * device anchors the list and is never removed, this entry can 245e493d0faSns92644 * never return to the front again to become the target. 246e493d0faSns92644 */ 247e493d0faSns92644 for (lpp = &iwscn_list; (lp = *lpp) != NULL; ) 248e493d0faSns92644 lpp = &lp->wl_next; 249e493d0faSns92644 tlp->wl_next = NULL; 250e493d0faSns92644 *lpp = tlp; 251e493d0faSns92644 } 2528ec5a142Sedp mutex_exit(&iwscn_list_lock); 2538ec5a142Sedp } 2548ec5a142Sedp 2558ec5a142Sedp /* Get a hold on the current target */ 2568ec5a142Sedp static iwscn_list_t * 2578ec5a142Sedp srhold() 2588ec5a142Sedp { 2598ec5a142Sedp iwscn_list_t *lp; 2608ec5a142Sedp 2618ec5a142Sedp mutex_enter(&iwscn_list_lock); 2628ec5a142Sedp ASSERT(iwscn_list != NULL); 2638ec5a142Sedp lp = iwscn_list; 2648ec5a142Sedp ASSERT(lp->wl_ref_cnt >= 0); 2658ec5a142Sedp lp->wl_ref_cnt++; 2668ec5a142Sedp mutex_exit(&iwscn_list_lock); 2678ec5a142Sedp 2688ec5a142Sedp return (lp); 2698ec5a142Sedp } 2708ec5a142Sedp 2718ec5a142Sedp /* Release a hold on an entry from the redirection list */ 2728ec5a142Sedp static void 2738ec5a142Sedp srrele(iwscn_list_t *lp) 2748ec5a142Sedp { 2758ec5a142Sedp ASSERT(lp != NULL); 2768ec5a142Sedp mutex_enter(&iwscn_list_lock); 2778ec5a142Sedp ASSERT(lp->wl_ref_cnt > 0); 2788ec5a142Sedp lp->wl_ref_cnt--; 2798ec5a142Sedp cv_broadcast(&iwscn_list_cv); 2808ec5a142Sedp mutex_exit(&iwscn_list_lock); 2818ec5a142Sedp } 2828ec5a142Sedp 2838ec5a142Sedp static int 2848ec5a142Sedp iwscnread(dev_t dev, uio_t *uio, cred_t *cred) 2858ec5a142Sedp { 2868ec5a142Sedp iwscn_list_t *lp; 2878ec5a142Sedp int error; 2888ec5a142Sedp 2898ec5a142Sedp ASSERT(getminor(dev) == 0); 2908ec5a142Sedp 2918ec5a142Sedp lp = srhold(); 2928ec5a142Sedp error = strread(lp->wl_vp, uio, cred); 2938ec5a142Sedp srrele(lp); 2948ec5a142Sedp 2958ec5a142Sedp return (error); 2968ec5a142Sedp } 2978ec5a142Sedp 2988ec5a142Sedp static int 2998ec5a142Sedp iwscnwrite(dev_t dev, uio_t *uio, cred_t *cred) 3008ec5a142Sedp { 3018ec5a142Sedp iwscn_list_t *lp; 3028ec5a142Sedp int error; 3038ec5a142Sedp 3048ec5a142Sedp ASSERT(getminor(dev) == 0); 3058ec5a142Sedp 3068ec5a142Sedp lp = srhold(); 3078ec5a142Sedp error = strwrite(lp->wl_vp, uio, cred); 3088ec5a142Sedp srrele(lp); 3098ec5a142Sedp 3108ec5a142Sedp return (error); 3118ec5a142Sedp } 3128ec5a142Sedp 3138ec5a142Sedp static int 3148ec5a142Sedp iwscnpoll(dev_t dev, short events, int anyyet, short *reventsp, 3158ec5a142Sedp struct pollhead **phpp) 3168ec5a142Sedp { 3178ec5a142Sedp iwscn_list_t *lp; 3188ec5a142Sedp int error; 3198ec5a142Sedp 3208ec5a142Sedp ASSERT(getminor(dev) == 0); 3218ec5a142Sedp 3228ec5a142Sedp lp = srhold(); 323*da6c28aaSamw error = VOP_POLL(lp->wl_vp, events, anyyet, reventsp, phpp, NULL); 3248ec5a142Sedp srrele(lp); 3258ec5a142Sedp 3268ec5a142Sedp return (error); 3278ec5a142Sedp } 3288ec5a142Sedp 3298ec5a142Sedp static int 3308ec5a142Sedp iwscnioctl(dev_t dev, int cmd, intptr_t arg, int flag, 3318ec5a142Sedp cred_t *cred, int *rvalp) 3328ec5a142Sedp { 3338ec5a142Sedp iwscn_list_t *lp; 3348ec5a142Sedp file_t *f; 3358ec5a142Sedp char modname[FMNAMESZ + 1] = " "; 3368ec5a142Sedp int error = 0; 3378ec5a142Sedp 3388ec5a142Sedp ASSERT(getminor(dev) == 0); 3398ec5a142Sedp 3408ec5a142Sedp switch (cmd) { 3418ec5a142Sedp case SRIOCSREDIR: 3428ec5a142Sedp /* Serialize all pushes of the redirection module */ 3438ec5a142Sedp mutex_enter(&iwscn_redirect_lock); 3448ec5a142Sedp 3458ec5a142Sedp /* 3468ec5a142Sedp * Find the vnode corresponding to the file descriptor 3478ec5a142Sedp * argument and verify that it names a stream. 3488ec5a142Sedp */ 3498ec5a142Sedp if ((f = getf((int)arg)) == NULL) { 3508ec5a142Sedp mutex_exit(&iwscn_redirect_lock); 3518ec5a142Sedp return (EBADF); 3528ec5a142Sedp } 3538ec5a142Sedp if (f->f_vnode->v_stream == NULL) { 3548ec5a142Sedp releasef((int)arg); 3558ec5a142Sedp mutex_exit(&iwscn_redirect_lock); 3568ec5a142Sedp return (ENOSTR); 3578ec5a142Sedp } 3588ec5a142Sedp 3598ec5a142Sedp /* 3608ec5a142Sedp * If the user is trying to redirect console output 3618ec5a142Sedp * back to the underlying console via SRIOCSREDIR 3628ec5a142Sedp * then they are evil and we'll stop them here. 3638ec5a142Sedp */ 3648ec5a142Sedp if (str_vp(f->f_vnode) == str_vp(rwsconsvp)) { 3658ec5a142Sedp releasef((int)arg); 3668ec5a142Sedp mutex_exit(&iwscn_redirect_lock); 3678ec5a142Sedp return (EINVAL); 3688ec5a142Sedp } 3698ec5a142Sedp 3708ec5a142Sedp /* 3718ec5a142Sedp * Check if this stream already has the redirection 3728ec5a142Sedp * module pushed onto it. I_LOOK returns an error 3738ec5a142Sedp * if there are no modules pushed onto the stream. 3748ec5a142Sedp */ 3758ec5a142Sedp (void) strioctl(f->f_vnode, I_LOOK, (intptr_t)modname, 3768ec5a142Sedp FKIOCTL, K_TO_K, cred, rvalp); 3778ec5a142Sedp if (strcmp(modname, STRREDIR_MOD) != 0) { 3788ec5a142Sedp 3798ec5a142Sedp /* 3808ec5a142Sedp * Push a new instance of the redirecting module onto 3818ec5a142Sedp * the stream, so that its close routine can notify 3828ec5a142Sedp * us when the overall stream is closed. (In turn, 3838ec5a142Sedp * we'll then remove it from the redirection list.) 3848ec5a142Sedp */ 3858ec5a142Sedp error = strioctl(f->f_vnode, I_PUSH, 3868ec5a142Sedp (intptr_t)STRREDIR_MOD, FKIOCTL, K_TO_K, 3878ec5a142Sedp cred, rvalp); 3888ec5a142Sedp 3898ec5a142Sedp if (error != 0) { 3908ec5a142Sedp releasef((int)arg); 3918ec5a142Sedp mutex_exit(&iwscn_redirect_lock); 3928ec5a142Sedp return (error); 3938ec5a142Sedp } 3948ec5a142Sedp } 3958ec5a142Sedp 3968ec5a142Sedp /* Push it onto the redirection stack */ 3978ec5a142Sedp mutex_enter(&iwscn_list_lock); 3988ec5a142Sedp srpush(f->f_vnode, B_FALSE); 3998ec5a142Sedp mutex_exit(&iwscn_list_lock); 4008ec5a142Sedp 4018ec5a142Sedp releasef((int)arg); 4028ec5a142Sedp mutex_exit(&iwscn_redirect_lock); 4038ec5a142Sedp return (0); 4048ec5a142Sedp 4058ec5a142Sedp case SRIOCISREDIR: 4068ec5a142Sedp /* 4078ec5a142Sedp * Find the vnode corresponding to the file descriptor 4088ec5a142Sedp * argument and verify that it names a stream. 4098ec5a142Sedp */ 4108ec5a142Sedp if ((f = getf((int)arg)) == NULL) { 4118ec5a142Sedp return (EBADF); 4128ec5a142Sedp } 4138ec5a142Sedp if (f->f_vnode->v_stream == NULL) { 4148ec5a142Sedp releasef((int)arg); 4158ec5a142Sedp return (ENOSTR); 4168ec5a142Sedp } 4178ec5a142Sedp 4188ec5a142Sedp lp = srhold(); 4198ec5a142Sedp *rvalp = (str_vp(f->f_vnode) == lp->wl_vp); 4208ec5a142Sedp srrele(lp); 4218ec5a142Sedp releasef((int)arg); 4228ec5a142Sedp return (0); 4238ec5a142Sedp 4248ec5a142Sedp case I_POP: 4258ec5a142Sedp /* 4268ec5a142Sedp * We need to serialize I_POP operations with 4278ec5a142Sedp * SRIOCSREDIR operations so we don't accidently 4288ec5a142Sedp * remove the redirection module from a stream. 4298ec5a142Sedp */ 4308ec5a142Sedp mutex_enter(&iwscn_redirect_lock); 4318ec5a142Sedp lp = srhold(); 4328ec5a142Sedp 4338ec5a142Sedp /* 4348ec5a142Sedp * Here we need to protect against process that might 4358ec5a142Sedp * try to pop off the redirection module from the 4368ec5a142Sedp * redirected stream. Popping other modules is allowed. 4378ec5a142Sedp * 4388ec5a142Sedp * It's ok to hold iwscn_list_lock while doing the 4398ec5a142Sedp * I_LOOK since it's such a simple operation. 4408ec5a142Sedp */ 4418ec5a142Sedp (void) strioctl(lp->wl_vp, I_LOOK, (intptr_t)modname, 4428ec5a142Sedp FKIOCTL, K_TO_K, cred, rvalp); 4438ec5a142Sedp 4448ec5a142Sedp if (strcmp(STRREDIR_MOD, modname) == 0) { 4458ec5a142Sedp srrele(lp); 4468ec5a142Sedp mutex_exit(&iwscn_redirect_lock); 4478ec5a142Sedp return (EINVAL); 4488ec5a142Sedp } 4498ec5a142Sedp 4508ec5a142Sedp /* Process the ioctl normally */ 451*da6c28aaSamw error = VOP_IOCTL(lp->wl_vp, cmd, arg, flag, cred, rvalp, NULL); 4528ec5a142Sedp 4538ec5a142Sedp srrele(lp); 4548ec5a142Sedp mutex_exit(&iwscn_redirect_lock); 4558ec5a142Sedp return (error); 4568ec5a142Sedp } 4578ec5a142Sedp 4588ec5a142Sedp /* Process the ioctl normally */ 4598ec5a142Sedp lp = srhold(); 460*da6c28aaSamw error = VOP_IOCTL(lp->wl_vp, cmd, arg, flag, cred, rvalp, NULL); 4618ec5a142Sedp srrele(lp); 4628ec5a142Sedp return (error); 4638ec5a142Sedp } 4648ec5a142Sedp 4658ec5a142Sedp /* ARGSUSED */ 4668ec5a142Sedp static int 4678ec5a142Sedp iwscnopen(dev_t *devp, int flag, int state, cred_t *cred) 4688ec5a142Sedp { 4698ec5a142Sedp iwscn_list_t *lp; 4708ec5a142Sedp vnode_t *vp = rwsconsvp; 4718ec5a142Sedp 4728ec5a142Sedp if (state != OTYP_CHR) 4738ec5a142Sedp return (ENXIO); 4748ec5a142Sedp 4758ec5a142Sedp if (getminor(*devp) != 0) 4768ec5a142Sedp return (ENXIO); 4778ec5a142Sedp 4788ec5a142Sedp /* 4798ec5a142Sedp * You can't really open us until the console subsystem 4808ec5a142Sedp * has been configured. 4818ec5a142Sedp */ 4828ec5a142Sedp if (rwsconsvp == NULL) 4838ec5a142Sedp return (ENXIO); 4848ec5a142Sedp 4858ec5a142Sedp /* 4868ec5a142Sedp * Check if this is the first open of this device or if 4878ec5a142Sedp * there is currently no redirection going on. (Ie, we're 4888ec5a142Sedp * sending output to underlying console device.) 4898ec5a142Sedp */ 4908ec5a142Sedp mutex_enter(&iwscn_list_lock); 4918ec5a142Sedp if ((iwscn_list == NULL) || (iwscn_list->wl_vp == str_vp(vp))) { 4928ec5a142Sedp int error = 0; 4938ec5a142Sedp 4948ec5a142Sedp /* Don't hold the list lock across an VOP_OPEN */ 4958ec5a142Sedp mutex_exit(&iwscn_list_lock); 4968ec5a142Sedp 4978ec5a142Sedp /* 4988ec5a142Sedp * There is currently no redirection going on. 4998ec5a142Sedp * pass this open request onto the console driver 5008ec5a142Sedp */ 501*da6c28aaSamw error = VOP_OPEN(&vp, flag, cred, NULL); 5028ec5a142Sedp if (error != 0) 5038ec5a142Sedp return (error); 5048ec5a142Sedp 505*da6c28aaSamw /* Re-acquire the list lock */ 5068ec5a142Sedp mutex_enter(&iwscn_list_lock); 5078ec5a142Sedp 5088ec5a142Sedp if (iwscn_list == NULL) { 5098ec5a142Sedp /* Save this vnode on the redirection list */ 5108ec5a142Sedp srpush(vp, B_TRUE); 5118ec5a142Sedp } else { 5128ec5a142Sedp /* 5138ec5a142Sedp * In this case there must already be a copy of 5148ec5a142Sedp * this vnode on the list, so we can free up this one. 5158ec5a142Sedp */ 516*da6c28aaSamw (void) VOP_CLOSE(vp, flag, 1, (offset_t)0, cred, NULL); 5178ec5a142Sedp } 5188ec5a142Sedp } 5198ec5a142Sedp 5208ec5a142Sedp /* 5218ec5a142Sedp * XXX This is an ugly legacy hack that has been around 5228ec5a142Sedp * forever. This code is here because this driver (the 5238ec5a142Sedp * iwscn driver) is a character driver layered over a 5248ec5a142Sedp * streams driver. 5258ec5a142Sedp * 5268ec5a142Sedp * Normally streams recieve notification whenever a process 5278ec5a142Sedp * closes its last reference to that stream so that it can 5288ec5a142Sedp * clean up any signal handling related configuration. (Ie, 5298ec5a142Sedp * when a stream is configured to deliver a signal to a 5308ec5a142Sedp * process upon certain events.) This is a feature supported 5318ec5a142Sedp * by the streams framework. 5328ec5a142Sedp * 5338ec5a142Sedp * But character/block drivers don't recieve this type 5348ec5a142Sedp * of notification. A character/block driver's close routine 5358ec5a142Sedp * is only invoked upon the last close of the device. This 5368ec5a142Sedp * is an artifact of the multiple open/single close driver 5378ec5a142Sedp * model currently supported by solaris. 5388ec5a142Sedp * 5398ec5a142Sedp * So a problem occurs when a character driver layers itself 5408ec5a142Sedp * on top of a streams driver. Since this driver doesn't always 5418ec5a142Sedp * receive a close notification when a process closes its 5428ec5a142Sedp * last reference to it, this driver can't tell the stream 5438ec5a142Sedp * it's layered upon to clean up any signal handling 5448ec5a142Sedp * configuration for that process. 5458ec5a142Sedp * 5468ec5a142Sedp * So here we hack around that by manually cleaning up the 5478ec5a142Sedp * signal handling list upon each open. It doesn't guarantee 5488ec5a142Sedp * that the signaling handling data stored in the stream will 5498ec5a142Sedp * always be up to date, but it'll be more up to date than 5508ec5a142Sedp * it would be if we didn't do this. 5518ec5a142Sedp * 5528ec5a142Sedp * The real way to solve this problem would be to change 5538ec5a142Sedp * the device framework from an multiple open/single close 5548ec5a142Sedp * model to a multiple open/multiple close model. Then 5558ec5a142Sedp * character/block drivers could pass on close requests 5568ec5a142Sedp * to streams layered underneath. 5578ec5a142Sedp */ 5588ec5a142Sedp str_cn_clean(VTOS(rwsconsvp)->s_commonvp); 5598ec5a142Sedp for (lp = iwscn_list; lp != NULL; lp = lp->wl_next) { 5608ec5a142Sedp ASSERT(lp->wl_vp->v_stream != NULL); 5618ec5a142Sedp str_cn_clean(lp->wl_vp); 5628ec5a142Sedp } 5638ec5a142Sedp 5648ec5a142Sedp mutex_exit(&iwscn_list_lock); 5658ec5a142Sedp return (0); 5668ec5a142Sedp } 5678ec5a142Sedp 5688ec5a142Sedp /* ARGSUSED */ 5698ec5a142Sedp static int 5708ec5a142Sedp iwscnclose(dev_t dev, int flag, int state, cred_t *cred) 5718ec5a142Sedp { 572e493d0faSns92644 iwscn_list_t *lp; 573e493d0faSns92644 5748ec5a142Sedp ASSERT(getminor(dev) == 0); 5758ec5a142Sedp 5768ec5a142Sedp if (state != OTYP_CHR) 5778ec5a142Sedp return (ENXIO); 5788ec5a142Sedp 5798ec5a142Sedp mutex_enter(&iwscn_list_lock); 580e493d0faSns92644 /* 581e493d0faSns92644 * Remove each entry from the redirection list, terminate any 582e493d0faSns92644 * current operations, wait for them to finish, then free the entry. 583e493d0faSns92644 */ 584e493d0faSns92644 while (iwscn_list != NULL) { 585e493d0faSns92644 lp = srrm(iwscn_list->wl_vp); 586e493d0faSns92644 ASSERT(lp != NULL); 587e493d0faSns92644 srinterrupt(lp, B_TRUE); 5888ec5a142Sedp 589e493d0faSns92644 if (lp->wl_is_console == B_TRUE) 590e493d0faSns92644 /* Close the underlying console device. */ 591*da6c28aaSamw (void) VOP_CLOSE(lp->wl_vp, 0, 1, (offset_t)0, kcred, 592*da6c28aaSamw NULL); 5938ec5a142Sedp 594e493d0faSns92644 kmem_free(lp, sizeof (*lp)); 595e493d0faSns92644 } 5968ec5a142Sedp mutex_exit(&iwscn_list_lock); 5978ec5a142Sedp return (0); 5988ec5a142Sedp } 5997c478bd9Sstevel@tonic-gate 6007c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 6017c478bd9Sstevel@tonic-gate static int 6027c478bd9Sstevel@tonic-gate iwscnattach(dev_info_t *devi, ddi_attach_cmd_t cmd) 6037c478bd9Sstevel@tonic-gate { 6048ec5a142Sedp /* 6058ec5a142Sedp * This is a pseudo device so there will never be more than 6068ec5a142Sedp * one instance attached at a time 6078ec5a142Sedp */ 6088ec5a142Sedp ASSERT(iwscn_dip == NULL); 6097c478bd9Sstevel@tonic-gate 6107c478bd9Sstevel@tonic-gate if (ddi_create_minor_node(devi, "iwscn", S_IFCHR, 6117c478bd9Sstevel@tonic-gate 0, DDI_PSEUDO, NULL) == DDI_FAILURE) { 6128ec5a142Sedp return (DDI_FAILURE); 6137c478bd9Sstevel@tonic-gate } 6148ec5a142Sedp 6157c478bd9Sstevel@tonic-gate iwscn_dip = devi; 6168ec5a142Sedp mutex_init(&iwscn_list_lock, NULL, MUTEX_DRIVER, NULL); 6178ec5a142Sedp mutex_init(&iwscn_redirect_lock, NULL, MUTEX_DRIVER, NULL); 6188ec5a142Sedp cv_init(&iwscn_list_cv, NULL, CV_DRIVER, NULL); 6198ec5a142Sedp 6207c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 6217c478bd9Sstevel@tonic-gate } 6227c478bd9Sstevel@tonic-gate 6237c478bd9Sstevel@tonic-gate /* ARGSUSED */ 6247c478bd9Sstevel@tonic-gate static int 6257c478bd9Sstevel@tonic-gate iwscninfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 6267c478bd9Sstevel@tonic-gate { 6277c478bd9Sstevel@tonic-gate int error; 6287c478bd9Sstevel@tonic-gate 6297c478bd9Sstevel@tonic-gate switch (infocmd) { 6307c478bd9Sstevel@tonic-gate case DDI_INFO_DEVT2DEVINFO: 6317c478bd9Sstevel@tonic-gate if (iwscn_dip == NULL) { 6327c478bd9Sstevel@tonic-gate error = DDI_FAILURE; 6337c478bd9Sstevel@tonic-gate } else { 6347c478bd9Sstevel@tonic-gate *result = (void *)iwscn_dip; 6357c478bd9Sstevel@tonic-gate error = DDI_SUCCESS; 6367c478bd9Sstevel@tonic-gate } 6377c478bd9Sstevel@tonic-gate break; 6387c478bd9Sstevel@tonic-gate case DDI_INFO_DEVT2INSTANCE: 6397c478bd9Sstevel@tonic-gate *result = (void *)0; 6407c478bd9Sstevel@tonic-gate error = DDI_SUCCESS; 6417c478bd9Sstevel@tonic-gate break; 6427c478bd9Sstevel@tonic-gate default: 6437c478bd9Sstevel@tonic-gate error = DDI_FAILURE; 6447c478bd9Sstevel@tonic-gate } 6457c478bd9Sstevel@tonic-gate return (error); 6467c478bd9Sstevel@tonic-gate } 6477c478bd9Sstevel@tonic-gate 6488ec5a142Sedp struct cb_ops iwscn_cb_ops = { 6498ec5a142Sedp iwscnopen, /* open */ 6508ec5a142Sedp iwscnclose, /* close */ 6518ec5a142Sedp nodev, /* strategy */ 6528ec5a142Sedp nodev, /* print */ 6538ec5a142Sedp nodev, /* dump */ 6548ec5a142Sedp iwscnread, /* read */ 6558ec5a142Sedp iwscnwrite, /* write */ 6568ec5a142Sedp iwscnioctl, /* ioctl */ 6578ec5a142Sedp nodev, /* devmap */ 6588ec5a142Sedp nodev, /* mmap */ 6598ec5a142Sedp nodev, /* segmap */ 6608ec5a142Sedp iwscnpoll, /* poll */ 6618ec5a142Sedp ddi_prop_op, /* cb_prop_op */ 6628ec5a142Sedp NULL, /* streamtab */ 6638ec5a142Sedp D_MP /* Driver compatibility flag */ 6648ec5a142Sedp }; 6657c478bd9Sstevel@tonic-gate 6668ec5a142Sedp struct dev_ops iwscn_ops = { 6678ec5a142Sedp DEVO_REV, /* devo_rev, */ 6688ec5a142Sedp 0, /* refcnt */ 6698ec5a142Sedp iwscninfo, /* info */ 6708ec5a142Sedp nulldev, /* identify */ 6718ec5a142Sedp nulldev, /* probe */ 6728ec5a142Sedp iwscnattach, /* attach */ 6738ec5a142Sedp nodev, /* detach */ 6748ec5a142Sedp nodev, /* reset */ 6758ec5a142Sedp &iwscn_cb_ops, /* driver operations */ 6768ec5a142Sedp NULL /* bus operations */ 6778ec5a142Sedp }; 6788ec5a142Sedp 6798ec5a142Sedp /* 6808ec5a142Sedp * Module linkage information for the kernel. 6818ec5a142Sedp */ 6828ec5a142Sedp static struct modldrv modldrv = { 6838ec5a142Sedp &mod_driverops, /* Type of module. This one is a pseudo driver */ 6848ec5a142Sedp "Workstation Redirection driver %I%", 6858ec5a142Sedp &iwscn_ops, /* driver ops */ 6868ec5a142Sedp }; 6878ec5a142Sedp 6888ec5a142Sedp static struct modlinkage modlinkage = { 6898ec5a142Sedp MODREV_1, 6908ec5a142Sedp &modldrv, 6918ec5a142Sedp NULL 6928ec5a142Sedp }; 6938ec5a142Sedp 6948ec5a142Sedp int 6958ec5a142Sedp _init(void) 6967c478bd9Sstevel@tonic-gate { 6978ec5a142Sedp return (mod_install(&modlinkage)); 6987c478bd9Sstevel@tonic-gate } 6997c478bd9Sstevel@tonic-gate 7008ec5a142Sedp int 7018ec5a142Sedp _fini(void) 7027c478bd9Sstevel@tonic-gate { 7037c478bd9Sstevel@tonic-gate return (EBUSY); 7047c478bd9Sstevel@tonic-gate } 7057c478bd9Sstevel@tonic-gate 7068ec5a142Sedp int 7078ec5a142Sedp _info(struct modinfo *modinfop) 7087c478bd9Sstevel@tonic-gate { 7098ec5a142Sedp return (mod_info(&modlinkage, modinfop)); 7107c478bd9Sstevel@tonic-gate } 711