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