/*
* 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 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* IP Policy Framework config driver
*/
/*
* Debug switch.
*/
#if defined(DEBUG)
#define IPPCTL_DEBUG
#endif
/*
* Debug macros.
*/
#ifdef IPPCTL_DEBUG
/*
* DBG_MODLINK |
* DBG_DEVOPS |
* DBG_CBOPS |
*/
0;
/*PRINTFLIKE3*/
static void ippctl_debug(uint64_t, char *, char *, ...)
__PRINTFLIKE(3);
(_a3));
#else /* IPPCTL_DBG */
#endif /* IPPCTL_DBG */
/*
* cb_ops
*/
ippctl_open, /* cb_open */
ippctl_close, /* cb_close */
nodev, /* cb_strategy */
nodev, /* cb_print */
nodev, /* cb_dump */
nodev, /* cb_read */
nodev, /* cb_write */
ippctl_ioctl, /* cb_ioctl */
nodev, /* cb_devmap */
nodev, /* cb_mmap */
nodev, /* cb_segmap */
nochpoll, /* cb_chpoll */
ddi_prop_op, /* cb_prop_op */
0, /* cb_str */
CB_REV, /* cb_rev */
nodev, /* cb_aread */
nodev /* cb_awrite */
};
/*
* dev_ops
*/
DEVO_REV, /* devo_rev, */
0, /* devo_refcnt */
ippctl_info, /* devo_getinfo */
nulldev, /* devo_identify */
nulldev, /* devo_probe */
ippctl_attach, /* devo_attach */
ippctl_detach, /* devo_detach */
nodev, /* devo_reset */
&ippctl_cb_ops, /* devo_cb_ops */
(struct bus_ops *)0, /* devo_bus_ops */
NULL, /* devo_power */
ddi_quiesce_not_needed, /* devo_quiesce */
};
"IP Policy Configuration Driver",
};
&modldrv,
};
/*
* Local definitions, types and prototypes.
*/
{ \
int j; \
\
for (j = 0; j < (_nelt); j++) \
}
struct ippctl_buf {
char *buf;
};
static int ippctl_extract_aname(nvlist_t *, char **);
static int ippctl_extract_modname(nvlist_t *, char **);
static int ippctl_action_destroy(char *, ipp_flags_t);
static int ippctl_action_info(char *, ipp_flags_t);
static int ippctl_action_mod(char *);
static int ippctl_list_mods(void);
static int ippctl_mod_list_actions(char *);
static void ippctl_flush(void);
static int ippctl_add_nvlist(nvlist_t *, int);
static int ippctl_callback(nvlist_t *, void *);
static int ippctl_set_rc(int);
static void ippctl_alloc(int);
static void ippctl_realloc(void);
static void ippctl_free(void);
/*
* Global data
*/
/*
* Module linkage functions
*/
int
void)
{
int rc;
return (rc);
}
return (rc);
}
int
void)
{
int rc;
return (rc);
}
return (rc);
}
int
{
}
/*
* Driver interface functions (dev_ops and cb_ops)
*/
/*ARGSUSED*/
static int
void *arg,
void **result)
{
switch (cmd) {
case DDI_INFO_DEVT2INSTANCE:
*result = (void *)0; /* Single instance driver */
rc = DDI_SUCCESS;
break;
case DDI_INFO_DEVT2DEVINFO:
*result = (void *)ippctl_dip;
rc = DDI_SUCCESS;
break;
default:
break;
}
return (rc);
}
static int
{
switch (cmd) {
case DDI_ATTACH:
break;
case DDI_PM_RESUME:
/*FALLTHRU*/
case DDI_RESUME:
/*FALLTHRU*/
default:
return (DDI_FAILURE);
}
/*
* This is strictly a single instance driver.
*/
if (ippctl_dip != NULL)
return (DDI_FAILURE);
/*
* Create minor node.
*/
DDI_PSEUDO, 0) != DDI_SUCCESS)
return (DDI_FAILURE);
/*
* No need for per-instance structure, just store vital data in
* globals.
*/
ippctl_dip = dip;
return (DDI_SUCCESS);
}
/*ARGSUSED*/
static int
{
switch (cmd) {
case DDI_DETACH:
break;
case DDI_PM_SUSPEND:
/*FALLTHRU*/
case DDI_SUSPEND:
/*FALLTHRU*/
default:
return (DDI_FAILURE);
}
ippctl_dip = NULL;
return (DDI_SUCCESS);
}
/*ARGSUSED*/
static int
int flag,
int otyp,
{
/*
* Only allow privileged users to open our device.
*/
return (EPERM);
}
/*
* Sanity check other arguments.
*/
if (minor != 0) {
return (ENXIO);
}
return (EINVAL);
}
/*
* This is also a single dev_t driver.
*/
if (ippctl_busy) {
return (EBUSY);
}
/*
* Allocate data buffer array (starting with length LIMIT, defined
* at the start of this function).
*/
return (0);
}
/*ARGSUSED*/
static int
int flag,
int otyp,
{
/*
* Free the data buffer array.
*/
ippctl_free();
return (0);
}
static int
int cmd,
int mode,
int *rvalp)
{
char *cbuf;
char *dbuf;
int rc;
/*
* Paranoia check.
*/
return (EPERM);
}
if (minor != 0) {
return (ENXIO);
}
switch (cmd) {
case IPPCTL_CMD:
/*
* Copy in the command buffer from user space.
*/
&cbuflen)) != 0)
break;
/*
* Execute the command.
*/
/*
* Pass back the length of the first data buffer.
*/
*rvalp = nextbuflen;
/*
* Free the kernel copy of the command buffer.
*/
break;
case IPPCTL_DATA:
/*
* Grab the next data buffer from the array of pending
* buffers.
*/
break;
/*
* Copy it out to user space.
*/
/*
* Pass back the length of the next data buffer.
*/
*rvalp = nextbuflen;
break;
default:
break;
}
return (rc);
}
/*
* Local functions
*/
static int
int mode,
char **kbufp,
{
char *kbuf;
/*
* Copy in the ioctl structure from user-space, converting from 32-bit
* as necessary.
*/
#ifdef _MULTI_DATAMODEL
case DDI_MODEL_ILP32:
{
sizeof (ippctl_ioctl32_t), mode) != 0)
return (EFAULT);
}
break;
case DDI_MODEL_NONE:
mode) != 0)
return (EFAULT);
break;
default:
return (EFAULT);
}
#else /* _MULTI_DATAMODEL */
mode) != 0)
return (EFAULT);
#endif /* _MULTI_DATAMODEL */
/*
* Sanity check the command buffer information.
*/
return (EINVAL);
if (ubuflen > MAXUBUFLEN)
return (E2BIG);
/*
* Allocate some memory for the command buffer and copy it in.
*/
return (EFAULT);
}
return (0);
}
static int
int mode,
char *kbuf,
{
int ubuflen;
/*
* Copy in the ioctl structure from user-space, converting from 32-bit
* as necessary.
*/
#ifdef _MULTI_DATAMODEL
case DDI_MODEL_ILP32:
{
sizeof (ippctl_ioctl32_t), mode) != 0)
return (EFAULT);
}
break;
case DDI_MODEL_NONE:
mode) != 0)
return (EFAULT);
break;
default:
return (EFAULT);
}
#else /* _MULTI_DATAMODEL */
mode) != 0)
return (EFAULT);
#endif /* _MULTI_DATAMODEL */
/*
* Sanity check the data buffer details.
*/
return (EINVAL);
return (ENOSPC);
if (ubuflen > MAXUBUFLEN)
return (E2BIG);
/*
* Copy out the data buffer to user space.
*/
return (EFAULT);
return (0);
}
static int
{
int rc;
/*
* Look-up and remove the opcode passed from libipp from the
* nvlist.
*/
return (rc);
return (0);
}
static int
char **valp)
{
int rc;
char *ptr;
/*
* Look-up and remove the action name passed from libipp from the
* nvlist.
*/
return (rc);
return (0);
}
static int
char **valp)
{
int rc;
char *ptr;
/*
* Look-up and remove the module name passed from libipp from the
* nvlist.
*/
return (rc);
return (0);
}
static int
char *modname)
{
/*
* Add a module name to an nvlist for passing back to user
* space.
*/
}
static int
char **modname_array,
int nelt)
{
/*
* Add a module name array to an nvlist for passing back to user
* space.
*/
modname_array, nelt));
}
static int
char **aname_array,
int nelt)
{
/*
* Add an action name array to an nvlist for passing back to user
* space.
*/
aname_array, nelt));
}
static int
{
int rc;
/*
* Look-up and remove the flags passed from libipp from the
* nvlist.
*/
return (rc);
return (0);
}
static int
char *cbuf,
{
int rc;
/*
* Start a new command cycle by flushing any previous data buffers.
*/
ippctl_flush();
*nextbuflenp = 0;
/*
* Unpack the nvlist from the command buffer.
*/
return (rc);
/*
* Extract the opcode to find out what we should do.
*/
return (rc);
}
switch (op) {
case IPPCTL_OP_ACTION_CREATE:
/*
* Create a new action.
*/
/*
* Extract the module name, action name and flags from the
* nvlist.
*/
return (rc);
}
return (rc);
}
return (rc);
}
break;
case IPPCTL_OP_ACTION_MODIFY:
/*
* Modify an existing action.
*/
/*
* Extract the action name and flags from the nvlist.
*/
return (rc);
}
return (rc);
}
break;
case IPPCTL_OP_ACTION_DESTROY:
/*
* Destroy an action.
*/
/*
* Extract the action name and flags from the nvlist.
*/
return (rc);
}
return (rc);
}
break;
case IPPCTL_OP_ACTION_INFO:
/*
* Retrive the configuration of an action.
*/
/*
* Extract the action name and flags from the nvlist.
*/
return (rc);
}
return (rc);
}
break;
case IPPCTL_OP_ACTION_MOD:
/*
* Find the module that implements a given action.
*/
/*
* Extract the action name from the nvlist.
*/
return (rc);
}
break;
case IPPCTL_OP_LIST_MODS:
/*
* List all the modules.
*/
rc = ippctl_list_mods();
break;
/*
* List all the actions for a given module.
*/
return (rc);
}
break;
default:
/*
* Unrecognized opcode.
*/
break;
}
/*
* The length of buffer that we need to notify back to libipp with
* the command ioctl's return is the length of the first data buffer
* in the array. We only expact to pass back data buffers if the
* operation succeeds (NOTE: this does not mean the kernel call has
* to succeed, merely that we successfully issued it and processed
* the results).
*/
if (rc == 0)
return (rc);
}
static int
char *modname,
char *aname,
{
int ipp_rc;
int rc;
/*
* Look up the module id from the name and create the new
* action.
*/
/*
* Add an nvlist containing the kernel return code to the
* set of nvlists to pass back to libipp.
*/
if (ipp_action_destroy(aid, 0) != 0) {
"ippctl: unrecoverable error (aid = %d)",
aid);
/*NOTREACHED*/
}
}
return (rc);
}
/*
* If the module passed back an nvlist, add this as
* well.
*/
} else
rc = 0;
return (rc);
}
static int
char *aname,
{
int ipp_rc;
int rc;
/*
* Look up the action id and destroy the action.
*/
/*
* Add an nvlist containing the kernel return code to the
* set of nvlists to pass back to libipp.
*/
return (rc);
/*
* There's no more information to pass back.
*/
return (0);
}
static int
char *aname,
{
int ipp_rc;
int rc;
/*
* Look up the action id and modify the action.
*/
/*
* Add an nvlist containing the kernel return code to the
* set of nvlists to pass back to libipp.
*/
return (rc);
}
/*
* If the module passed back an nvlist, add this as
* well.
*/
} else
rc = 0;
return (rc);
}
static int
char *aname,
{
int ipp_rc;
int rc;
/*
* Look up the action and call the information retrieval
* entry point.
*
* NOTE: The callback function that is passed in packs and
* stores each of the nvlists it is called with in the array
* that will be passed back to libipp.
*/
/*
* Add an nvlist containing the kernel return code to the
* set of nvlists to pass back to libipp.
*/
return (rc);
/*
* There's no more information to pass back.
*/
return (0);
}
static int
char *aname)
{
char *modname;
int ipp_rc;
int rc;
/*
* Look up the action id and get the id of the module that
* implements the action. If that succeeds then look up the
* name of the module.
*/
/*
* Add an nvlist containing the kernel return code to the
* set of nvlists to pass back to libipp.
*/
return (rc);
/*
* If everything succeeded add an nvlist containing the
* module name to the set of nvlists to pass back to libipp.
*/
if (ipp_rc == 0) {
return (rc);
return (rc);
}
} else
rc = 0;
return (rc);
}
static int
void)
{
int ipp_rc;
int rc = 0;
int nelt;
int length;
int i;
/*
* Get a list of all the module ids. If that succeeds,
* translate the ids into names.
*
* NOTE: This translation may fail if a module is
* unloaded during this operation. If this occurs, EAGAIN
* will be passed back to libipp note that a transient
* problem occured.
*/
/*
* It is possible that there are no modules
* registered.
*/
if (nelt > 0) {
for (i = 0; i < nelt; i++) {
if (ipp_mod_name(mid_array[i],
&modname_array[i]) != 0) {
sizeof (ipp_mod_id_t));
goto done;
}
}
KM_SLEEP)) != 0) {
return (rc);
}
modname_array, nelt)) != 0) {
return (rc);
}
return (rc);
}
}
}
done:
/*
* Add an nvlist containing the kernel return code to the
* set of nvlists to pass back to libipp.
*/
return (rc);
return (0);
}
static int
char *modname)
{
int ipp_rc;
int rc = 0;
int nelt;
int length;
int i;
/*
* Get the module id.
*/
/*
* Get a list of all the action ids for the module. If that succeeds,
* translate the ids into names.
*
* NOTE: This translation may fail if an action is
* destroyed during this operation. If this occurs, EAGAIN
* will be passed back to libipp note that a transient
* problem occured.
*/
/*
* It is possible that there are no actions defined.
* (This is unlikely though as the module would normally
* be auto-unloaded fairly quickly)
*/
if (nelt > 0) {
for (i = 0; i < nelt; i++) {
if (ipp_action_name(aid_array[i],
&aname_array[i]) != 0) {
sizeof (ipp_action_id_t));
goto done;
}
}
KM_SLEEP)) != 0) {
return (rc);
}
nelt)) != 0) {
return (rc);
}
return (rc);
}
}
}
done:
/*
* Add an nvlist containing the kernel return code to the
* set of nvlists to pass back to libipp.
*/
return (rc);
return (0);
}
static int
char **dbufp,
{
int i;
/*
* Get the next data buffer from the array by looking at the
* 'read index'. If this is the same as the 'write index' then
* there's no more buffers in the array.
*/
i = ippctl_rindex;
if (i == ippctl_windex)
return (ENOENT);
/*
* Extract the buffer details. It is a pre-packed nvlist.
*/
/*
* Advance the 'read index' and check if there's another buffer.
* If there is then we need to pass back its length to libipp so that
* another data ioctl will be issued.
*/
i++;
if (i < ippctl_windex)
else
*nextbuflenp = 0;
ippctl_rindex = i;
return (0);
}
static void
void)
{
int i;
char *buf;
/*
* Free any buffers left in the array.
*/
for (i = 0; i < ippctl_limit; i++) {
}
}
/*
* NULL all the entries.
*/
/*
* Reset the indexes.
*/
ippctl_rindex = 0;
ippctl_windex = 1;
}
static int
int i)
{
char *buf;
int rc;
/*
* NULL the buffer pointer so that a buffer is automatically
* allocated for us.
*/
/*
* Pack the nvlist and get back the buffer pointer and length.
*/
KM_SLEEP)) != 0) {
ippctl_array[i].buflen = 0;
return (rc);
}
/*
* Store the pointer an length in the array at the given index.
*/
return (0);
}
/*ARGSUSED*/
static int
void *arg)
{
int i;
int rc;
/*
* Check the 'write index' to see if there's space in the array for
* a new entry.
*/
i = ippctl_windex;
ASSERT(i != 0);
/*
* If there's no space, re-allocate the array (see comments in
* ippctl_realloc() for details).
*/
if (i == ippctl_limit)
/*
* Add the nvlist to the array.
*/
return (rc);
}
static int
int val)
{
int rc;
/*
* Create an nvlist to store the return code,
*/
return (ENOMEM);
return (rc);
}
/*
* Add it at the beginning of the array.
*/
return (rc);
}
static void
int limit)
{
/*
* Allocate the data buffer array and initialize the indexes.
*/
ippctl_rindex = 0;
ippctl_windex = 1;
}
static void
void)
{
int limit;
int i;
/*
* Allocate a new array twice the size of the old one.
*/
/*
* Copy across the information from the old array into the new one.
*/
for (i = 0; i < ippctl_limit; i++)
array[i] = ippctl_array[i];
/*
* Free the old array.
*/
}
static void
void)
{
/*
* Flush the array prior to freeing it to make sure no buffers are
* leaked.
*/
ippctl_flush();
/*
* Free the array.
*/
ippctl_array = NULL;
ippctl_limit = -1;
ippctl_rindex = -1;
ippctl_windex = -1;
}
#ifdef IPPCTL_DEBUG
static void
char *fn,
char *fmt,
...)
{
if ((type & ippctl_debug_flags) == 0)
return;
}
#endif /* IPPCTL_DBG */