iwscons.c revision e493d0fad05322dfa9e424b7591501b5f9855a9f
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* workstation console redirecting driver
*
* Redirects all I/O through a given device instance to the device designated
* as the current target, as given by the vnode associated with the first
* entry in the list of redirections for the given device instance. The
* implementation assumes that this vnode denotes a STREAMS device; this is
* perhaps a bug.
*
* Supports the SRIOCSREDIR ioctl for designating a new redirection target.
* The new target is added to the front of a list of potentially active
* designees. Should the device at the front of this list be closed, the new
* front entry assumes active duty. (Stated differently, redirection targets
* stack, except that it's possible for entries in the interior of the stack
* to go away.)
*
* Supports the SRIOCISREDIR ioctl for inquiring whether the descriptor given
* as argument is the current front of the redirection list associated with
* the descriptor on which the ioctl was issued.
*/
#include <sys/sysmacros.h>
#include <sys/strredir.h>
/*
* Global data
*/
static dev_info_t *iwscn_dip;
/*
* We record the list of redirections as a linked list of iwscn_list_t
* structures. We need to keep track of the target's vp, so that
* we can vector reads, writes, etc. off to the current designee.
*/
typedef struct _iwscn_list {
int wl_ref_cnt; /* operation in progress */
} iwscn_list_t;
static iwscn_list_t *iwscn_list;
/*
* iwscn_list_lock serializes modifications to the global iwscn_list list.
*
* iwscn_list_cv is used when freeing an entry from iwscn_list to allow
* the caller to wait till the wl_ref_cnt field is zero.
*
* iwscn_redirect_lock is used to serialize redirection requests. This
* is required to ensure that all active redirection streams have
* the redirection streams module (redirmod) pushed on them.
*
* If both iwscn_redirect_lock and iwscn_list_lock must be held then
* iwscn_redirect_lock must be aquired first.
*/
static kcondvar_t iwscn_list_cv;
static kmutex_t iwscn_list_lock;
static kmutex_t iwscn_redirect_lock;
/*
* Routines for managing iwscn_list
*/
static vnode_t *
{
/*
* Here we switch to using the vnode that is linked
* to from the stream queue. (In the case of device
* streams this will correspond to the common vnode
* for the device.) The reason we use this vnode
* is that when wcmclose() calls srpop(), this is the
* only vnode that it has access to.
*/
}
/*
* Interrupt any operations that may be outstanding against this vnode.
* optionally, wait for them to complete.
*/
static void
{
while (lp->wl_ref_cnt != 0) {
if (!wait)
break;
}
}
/*
* Remove vp from the redirection list rooted at iwscn_list, should it
* be there. Return a pointer to the removed entry.
*/
static iwscn_list_t *
{
/* Get the stream vnode */
/* Look for this vnode on the redirection list */
break;
}
/* Found it, remove this entry from the redirection list */
return (lp);
}
/*
* Push vp onto the redirection list.
* If it's already there move it to the front position.
*/
static void
{
/* Get the stream vnode */
/* Check if it's already on the redirection list */
}
/*
* Note that if this vnode was already somewhere on the redirection
* list then we removed it above and are now bumping it up to the
* front of the redirection list.
*/
iwscn_list = lp;
}
/*
* This vnode is no longer a valid redirection target. Terminate any current
* operations. If closing, wait for them to complete, then free the entry.
* If called because a hangup has occurred, just deprecate the entry to ensure
* it won't become the target again.
*/
void
{
/*
* Ensure no further operations are directed at the target
* by removing it from the redirection list.
*/
/* vnode wasn't in the list */
return;
}
/*
* Terminate any current operations.
* If we're closing, wait until they complete.
*/
if (close) {
/* We're finished with this target */
} else {
/*
* Deprecate the entry. There's no need for a flag to indicate
* this state, it just needs to be moved to the back of the list
* behind the underlying console device. Since the underlying
* device anchors the list and is never removed, this entry can
* never return to the front again to become the target.
*/
}
}
/* Get a hold on the current target */
static iwscn_list_t *
srhold()
{
lp = iwscn_list;
lp->wl_ref_cnt++;
return (lp);
}
/* Release a hold on an entry from the redirection list */
static void
{
lp->wl_ref_cnt--;
}
static int
{
int error;
return (error);
}
static int
{
int error;
return (error);
}
static int
{
int error;
return (error);
}
static int
{
file_t *f;
int error = 0;
switch (cmd) {
case SRIOCSREDIR:
/* Serialize all pushes of the redirection module */
/*
* Find the vnode corresponding to the file descriptor
* argument and verify that it names a stream.
*/
return (EBADF);
}
return (ENOSTR);
}
/*
* If the user is trying to redirect console output
* back to the underlying console via SRIOCSREDIR
* then they are evil and we'll stop them here.
*/
return (EINVAL);
}
/*
* Check if this stream already has the redirection
* module pushed onto it. I_LOOK returns an error
* if there are no modules pushed onto the stream.
*/
/*
* Push a new instance of the redirecting module onto
* the stream, so that its close routine can notify
* us when the overall stream is closed. (In turn,
* we'll then remove it from the redirection list.)
*/
if (error != 0) {
return (error);
}
}
/* Push it onto the redirection stack */
return (0);
case SRIOCISREDIR:
/*
* Find the vnode corresponding to the file descriptor
* argument and verify that it names a stream.
*/
return (EBADF);
}
return (ENOSTR);
}
return (0);
case I_POP:
/*
* We need to serialize I_POP operations with
* SRIOCSREDIR operations so we don't accidently
* remove the redirection module from a stream.
*/
/*
* Here we need to protect against process that might
* try to pop off the redirection module from the
* redirected stream. Popping other modules is allowed.
*
* It's ok to hold iwscn_list_lock while doing the
* I_LOOK since it's such a simple operation.
*/
return (EINVAL);
}
/* Process the ioctl normally */
return (error);
}
/* Process the ioctl normally */
return (error);
}
/* ARGSUSED */
static int
{
return (ENXIO);
return (ENXIO);
/*
* You can't really open us until the console subsystem
* has been configured.
*/
return (ENXIO);
/*
* Check if this is the first open of this device or if
* there is currently no redirection going on. (Ie, we're
* sending output to underlying console device.)
*/
int error = 0;
/* Don't hold the list lock across an VOP_OPEN */
/*
* There is currently no redirection going on.
* pass this open request onto the console driver
*/
if (error != 0)
return (error);
/* Re-aquire the list lock */
if (iwscn_list == NULL) {
/* Save this vnode on the redirection list */
} else {
/*
* In this case there must already be a copy of
* this vnode on the list, so we can free up this one.
*/
}
}
/*
* XXX This is an ugly legacy hack that has been around
* forever. This code is here because this driver (the
* iwscn driver) is a character driver layered over a
* streams driver.
*
* Normally streams recieve notification whenever a process
* closes its last reference to that stream so that it can
* clean up any signal handling related configuration. (Ie,
* when a stream is configured to deliver a signal to a
* process upon certain events.) This is a feature supported
* by the streams framework.
*
* is only invoked upon the last close of the device. This
* model currently supported by solaris.
*
* So a problem occurs when a character driver layers itself
* on top of a streams driver. Since this driver doesn't always
* receive a close notification when a process closes its
* last reference to it, this driver can't tell the stream
* it's layered upon to clean up any signal handling
* configuration for that process.
*
* So here we hack around that by manually cleaning up the
* signal handling list upon each open. It doesn't guarantee
* that the signaling handling data stored in the stream will
* always be up to date, but it'll be more up to date than
* it would be if we didn't do this.
*
* The real way to solve this problem would be to change
* to streams layered underneath.
*/
}
return (0);
}
/* ARGSUSED */
static int
{
return (ENXIO);
/*
* Remove each entry from the redirection list, terminate any
* current operations, wait for them to finish, then free the entry.
*/
while (iwscn_list != NULL) {
/* Close the underlying console device. */
}
return (0);
}
/*ARGSUSED*/
static int
{
/*
* This is a pseudo device so there will never be more than
* one instance attached at a time
*/
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/* ARGSUSED */
static int
{
int error;
switch (infocmd) {
case DDI_INFO_DEVT2DEVINFO:
error = DDI_FAILURE;
} else {
error = DDI_SUCCESS;
}
break;
case DDI_INFO_DEVT2INSTANCE:
*result = (void *)0;
error = DDI_SUCCESS;
break;
default:
error = DDI_FAILURE;
}
return (error);
}
struct cb_ops iwscn_cb_ops = {
iwscnopen, /* open */
iwscnclose, /* close */
nodev, /* strategy */
nodev, /* print */
nodev, /* dump */
iwscnread, /* read */
iwscnwrite, /* write */
iwscnioctl, /* ioctl */
nodev, /* devmap */
nodev, /* mmap */
nodev, /* segmap */
iwscnpoll, /* poll */
ddi_prop_op, /* cb_prop_op */
NULL, /* streamtab */
D_MP /* Driver compatibility flag */
};
DEVO_REV, /* devo_rev, */
0, /* refcnt */
iwscninfo, /* info */
nulldev, /* identify */
nulldev, /* probe */
iwscnattach, /* attach */
nodev, /* detach */
nodev, /* reset */
&iwscn_cb_ops, /* driver operations */
NULL /* bus operations */
};
/*
* Module linkage information for the kernel.
*/
&mod_driverops, /* Type of module. This one is a pseudo driver */
"Workstation Redirection driver %I%",
&iwscn_ops, /* driver ops */
};
static struct modlinkage modlinkage = {
&modldrv,
};
int
_init(void)
{
return (mod_install(&modlinkage));
}
int
_fini(void)
{
return (EBUSY);
}
int
{
}