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