#include <sys/types.h>
#include <sys/cmn_err.h>
#include <sys/kmem.h>
#include <sys/errno.h>
#include <sys/cpuvar.h>
#include <sys/open.h>
#include <sys/stat.h>
#include <sys/conf.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/modctl.h>
#include <sys/stream.h>
#include <ipp/ipp.h>
#include <ipp/ippctl.h>
#include <sys/nvpair.h>
#include <sys/policy.h>
#if defined(DEBUG)
#define IPPCTL_DEBUG
#endif
#ifdef IPPCTL_DEBUG
#define DBG_MODLINK 0x00000001ull
#define DBG_DEVOPS 0x00000002ull
#define DBG_CBOPS 0x00000004ull
static uint64_t ippctl_debug_flags =
0;
static kmutex_t debug_mutex[1];
static void ippctl_debug(uint64_t, char *, char *, ...)
__KPRINTFLIKE(3);
#define DBG0(_type, _fmt) \
ippctl_debug((_type), __FN__, (_fmt));
#define DBG1(_type, _fmt, _a1) \
ippctl_debug((_type), __FN__, (_fmt), (_a1));
#define DBG2(_type, _fmt, _a1, _a2) \
ippctl_debug((_type), __FN__, (_fmt), (_a1), (_a2));
#define DBG3(_type, _fmt, _a1, _a2, _a3) \
ippctl_debug((_type), __FN__, (_fmt), (_a1), (_a2), \
(_a3));
#define DBG4(_type, _fmt, _a1, _a2, _a3, _a4) \
ippctl_debug((_type), __FN__, (_fmt), (_a1), (_a2), \
(_a3), (_a4));
#define DBG5(_type, _fmt, _a1, _a2, _a3, _a4, _a5) \
ippctl_debug((_type), __FN__, (_fmt), (_a1), (_a2), \
(_a3), (_a4), (_a5));
#else
#define DBG0(_type, _fmt)
#define DBG1(_type, _fmt, _a1)
#define DBG2(_type, _fmt, _a1, _a2)
#define DBG3(_type, _fmt, _a1, _a2, _a3)
#define DBG4(_type, _fmt, _a1, _a2, _a3, _a4)
#define DBG5(_type, _fmt, _a1, _a2, _a3, _a4, _a5)
#endif
static int ippctl_open(dev_t *, int, int, cred_t *);
static int ippctl_close(dev_t, int, int, cred_t *);
static int ippctl_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
static struct cb_ops ippctl_cb_ops = {
ippctl_open,
ippctl_close,
nodev,
nodev,
nodev,
nodev,
nodev,
ippctl_ioctl,
nodev,
nodev,
nodev,
nochpoll,
ddi_prop_op,
0,
D_NEW | D_MP,
CB_REV,
nodev,
nodev
};
static int ippctl_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
static int ippctl_attach(dev_info_t *, ddi_attach_cmd_t);
static int ippctl_detach(dev_info_t *, ddi_detach_cmd_t);
static struct dev_ops ippctl_dev_ops = {
DEVO_REV,
0,
ippctl_info,
nulldev,
nulldev,
ippctl_attach,
ippctl_detach,
nodev,
&ippctl_cb_ops,
(struct bus_ops *)0,
NULL,
ddi_quiesce_not_needed,
};
static struct modldrv modldrv = {
&mod_driverops,
"IP Policy Configuration Driver",
&ippctl_dev_ops,
};
static struct modlinkage modlinkage = {
MODREV_1,
&modldrv,
NULL
};
#define MAXUBUFLEN (1 << 16)
#define FREE_TEXT(_string) \
kmem_free((_string), strlen(_string) + 1)
#define FREE_TEXT_ARRAY(_array, _nelt) \
{ \
int j; \
\
for (j = 0; j < (_nelt); j++) \
if ((_array)[j] != NULL) \
FREE_TEXT((_array)[j]); \
kmem_free((_array), (_nelt) * sizeof (char *)); \
}
typedef struct ippctl_buf ippctl_buf_t;
struct ippctl_buf {
char *buf;
size_t buflen;
};
static int ippctl_copyin(caddr_t, int, char **, size_t *);
static int ippctl_copyout(caddr_t, int, char *, size_t);
static int ippctl_extract_op(nvlist_t *, uint8_t *);
static int ippctl_extract_aname(nvlist_t *, char **);
static int ippctl_extract_modname(nvlist_t *, char **);
static int ippctl_attach_modname(nvlist_t *nvlp, char *val);
static int ippctl_attach_modname_array(nvlist_t *nvlp, char **val, int);
static int ippctl_attach_aname_array(nvlist_t *nvlp, char **val, int);
static int ippctl_extract_flags(nvlist_t *, ipp_flags_t *);
static int ippctl_cmd(char *, size_t, size_t *);
static int ippctl_action_create(char *, char *, nvlist_t *, ipp_flags_t);
static int ippctl_action_destroy(char *, ipp_flags_t);
static int ippctl_action_modify(char *, nvlist_t *, 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 int ippctl_data(char **, size_t *, size_t *);
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);
static dev_info_t *ippctl_dip = NULL;
static kmutex_t ippctl_lock;
static boolean_t ippctl_busy;
static ippctl_buf_t *ippctl_array = NULL;
static int ippctl_limit = -1;
static int ippctl_rindex = -1;
static int ippctl_windex = -1;
#define __FN__ "_init"
int
_init(
void)
{
int rc;
if ((rc = mod_install(&modlinkage)) != 0) {
DBG0(DBG_MODLINK, "mod_install failed\n");
return (rc);
}
return (rc);
}
#undef __FN__
#define __FN__ "_fini"
int
_fini(
void)
{
int rc;
if ((rc = mod_remove(&modlinkage)) == 0) {
return (rc);
}
DBG0(DBG_MODLINK, "mod_remove failed\n");
return (rc);
}
#undef __FN__
#define __FN__ "_info"
int
_info(
struct modinfo *modinfop)
{
DBG0(DBG_MODLINK, "calling mod_info\n");
return (mod_info(&modlinkage, modinfop));
}
#undef __FN__
#define __FN__ "ippctl_info"
static int
ippctl_info(
dev_info_t *dip,
ddi_info_cmd_t cmd,
void *arg,
void **result)
{
int rc = DDI_FAILURE;
switch (cmd) {
case DDI_INFO_DEVT2INSTANCE:
*result = (void *)0;
rc = DDI_SUCCESS;
break;
case DDI_INFO_DEVT2DEVINFO:
*result = (void *)ippctl_dip;
rc = DDI_SUCCESS;
break;
default:
break;
}
return (rc);
}
#undef __FN__
#define __FN__ "ippctl_attach"
static int
ippctl_attach(
dev_info_t *dip,
ddi_attach_cmd_t cmd)
{
switch (cmd) {
case DDI_ATTACH:
break;
case DDI_PM_RESUME:
case DDI_RESUME:
default:
return (DDI_FAILURE);
}
DBG0(DBG_DEVOPS, "DDI_ATTACH\n");
if (ippctl_dip != NULL)
return (DDI_FAILURE);
if (ddi_create_minor_node(dip, "ctl", S_IFCHR, 0,
DDI_PSEUDO, 0) != DDI_SUCCESS)
return (DDI_FAILURE);
ippctl_dip = dip;
mutex_init(&ippctl_lock, NULL, MUTEX_DRIVER, NULL);
ippctl_busy = B_FALSE;
return (DDI_SUCCESS);
}
#undef __FN__
#define __FN__ "ippctl_detach"
static int
ippctl_detach(
dev_info_t *dip,
ddi_detach_cmd_t cmd)
{
switch (cmd) {
case DDI_DETACH:
break;
case DDI_PM_SUSPEND:
case DDI_SUSPEND:
default:
return (DDI_FAILURE);
}
DBG0(DBG_DEVOPS, "DDI_DETACH\n");
ASSERT(dip == ippctl_dip);
ddi_remove_minor_node(dip, NULL);
mutex_destroy(&ippctl_lock);
ippctl_dip = NULL;
return (DDI_SUCCESS);
}
#undef __FN__
#define __FN__ "ippctl_open"
static int
ippctl_open(
dev_t *devp,
int flag,
int otyp,
cred_t *credp)
{
minor_t minor = getminor(*devp);
#define LIMIT 4
DBG0(DBG_CBOPS, "open\n");
if (secpolicy_net_config(credp, B_FALSE) != 0) {
DBG0(DBG_CBOPS, "not privileged user\n");
return (EPERM);
}
if (minor != 0) {
DBG0(DBG_CBOPS, "bad minor\n");
return (ENXIO);
}
if (otyp != OTYP_CHR) {
DBG0(DBG_CBOPS, "bad device type\n");
return (EINVAL);
}
mutex_enter(&ippctl_lock);
if (ippctl_busy) {
mutex_exit(&ippctl_lock);
return (EBUSY);
}
ippctl_busy = B_TRUE;
mutex_exit(&ippctl_lock);
ippctl_alloc(LIMIT);
DBG0(DBG_CBOPS, "success\n");
return (0);
#undef LIMIT
}
#undef __FN__
#define __FN__ "ippctl_close"
static int
ippctl_close(
dev_t dev,
int flag,
int otyp,
cred_t *credp)
{
minor_t minor = getminor(dev);
DBG0(DBG_CBOPS, "close\n");
ASSERT(minor == 0);
ippctl_free();
mutex_enter(&ippctl_lock);
ippctl_busy = B_FALSE;
mutex_exit(&ippctl_lock);
DBG0(DBG_CBOPS, "success\n");
return (0);
}
#undef __FN__
#define __FN__ "ippctl_ioctl"
static int
ippctl_ioctl(
dev_t dev,
int cmd,
intptr_t arg,
int mode,
cred_t *credp,
int *rvalp)
{
minor_t minor = getminor(dev);
char *cbuf;
char *dbuf;
size_t cbuflen;
size_t dbuflen;
size_t nextbuflen;
int rc;
if (secpolicy_net_config(credp, B_FALSE) != 0) {
DBG0(DBG_CBOPS, "not privileged user\n");
return (EPERM);
}
if (minor != 0) {
DBG0(DBG_CBOPS, "bad minor\n");
return (ENXIO);
}
switch (cmd) {
case IPPCTL_CMD:
DBG0(DBG_CBOPS, "command\n");
if ((rc = ippctl_copyin((caddr_t)arg, mode, &cbuf,
&cbuflen)) != 0)
break;
rc = ippctl_cmd(cbuf, cbuflen, &nextbuflen);
DBG1(DBG_CBOPS, "nextbuflen = %lu\n", nextbuflen);
*rvalp = nextbuflen;
kmem_free(cbuf, cbuflen);
break;
case IPPCTL_DATA:
DBG0(DBG_CBOPS, "data\n");
if ((rc = ippctl_data(&dbuf, &dbuflen, &nextbuflen)) != 0)
break;
rc = ippctl_copyout((caddr_t)arg, mode, dbuf, dbuflen);
DBG1(DBG_CBOPS, "nextbuflen = %lu\n", nextbuflen);
*rvalp = nextbuflen;
break;
default:
DBG0(DBG_CBOPS, "unrecognized ioctl\n");
rc = EINVAL;
break;
}
DBG1(DBG_CBOPS, "rc = %d\n", rc);
return (rc);
}
#undef __FN__
#define __FN__ "ippctl_copyin"
static int
ippctl_copyin(
caddr_t arg,
int mode,
char **kbufp,
size_t *kbuflenp)
{
ippctl_ioctl_t iioc;
caddr_t ubuf;
char *kbuf;
size_t ubuflen;
DBG0(DBG_CBOPS, "copying in ioctl structure\n");
#ifdef _MULTI_DATAMODEL
switch (ddi_model_convert_from(mode & FMODELS)) {
case DDI_MODEL_ILP32:
{
ippctl_ioctl32_t iioc32;
DBG0(DBG_CBOPS, "converting from 32-bit\n");
if (ddi_copyin(arg, (caddr_t)&iioc32,
sizeof (ippctl_ioctl32_t), mode) != 0)
return (EFAULT);
ubuf = (caddr_t)(uintptr_t)iioc32.ii32_buf;
ubuflen = (size_t)iioc32.ii32_buflen;
}
break;
case DDI_MODEL_NONE:
if (ddi_copyin(arg, (caddr_t)&iioc, sizeof (ippctl_ioctl_t),
mode) != 0)
return (EFAULT);
ubuf = iioc.ii_buf;
ubuflen = iioc.ii_buflen;
break;
default:
return (EFAULT);
}
#else
if (ddi_copyin(arg, (caddr_t)&iioc, sizeof (ippctl_ioctl_t),
mode) != 0)
return (EFAULT);
ubuf = iioc.ii_buf;
ubuflen = iioc.ii_buflen;
#endif
DBG1(DBG_CBOPS, "ubuf = 0x%p\n", (void *)ubuf);
DBG1(DBG_CBOPS, "ubuflen = %lu\n", ubuflen);
if (ubuflen == 0 || ubuf == NULL)
return (EINVAL);
if (ubuflen > MAXUBUFLEN)
return (E2BIG);
kbuf = kmem_zalloc(ubuflen, KM_SLEEP);
DBG0(DBG_CBOPS, "copying in nvlist\n");
if (ddi_copyin(ubuf, (caddr_t)kbuf, ubuflen, mode) != 0) {
kmem_free(kbuf, ubuflen);
return (EFAULT);
}
*kbufp = kbuf;
*kbuflenp = ubuflen;
return (0);
}
#undef __FN__
#define __FN__ "ippctl_copyout"
static int
ippctl_copyout(
caddr_t arg,
int mode,
char *kbuf,
size_t kbuflen)
{
ippctl_ioctl_t iioc;
caddr_t ubuf;
int ubuflen;
DBG0(DBG_CBOPS, "copying out ioctl structure\n");
#ifdef _MULTI_DATAMODEL
switch (ddi_model_convert_from(mode & FMODELS)) {
case DDI_MODEL_ILP32:
{
ippctl_ioctl32_t iioc32;
if (ddi_copyin(arg, (caddr_t)&iioc32,
sizeof (ippctl_ioctl32_t), mode) != 0)
return (EFAULT);
ubuf = (caddr_t)(uintptr_t)iioc32.ii32_buf;
ubuflen = iioc32.ii32_buflen;
}
break;
case DDI_MODEL_NONE:
if (ddi_copyin(arg, (caddr_t)&iioc, sizeof (ippctl_ioctl_t),
mode) != 0)
return (EFAULT);
ubuf = iioc.ii_buf;
ubuflen = iioc.ii_buflen;
break;
default:
return (EFAULT);
}
#else
if (ddi_copyin(arg, (caddr_t)&iioc, sizeof (ippctl_ioctl_t),
mode) != 0)
return (EFAULT);
ubuf = iioc.ii_buf;
ubuflen = iioc.ii_buflen;
#endif
DBG1(DBG_CBOPS, "ubuf = 0x%p\n", (void *)ubuf);
DBG1(DBG_CBOPS, "ubuflen = %d\n", ubuflen);
if (ubuflen == 0 || ubuf == NULL)
return (EINVAL);
if (ubuflen < kbuflen)
return (ENOSPC);
if (ubuflen > MAXUBUFLEN)
return (E2BIG);
DBG0(DBG_CBOPS, "copying out nvlist\n");
if (ddi_copyout((caddr_t)kbuf, ubuf, kbuflen, mode) != 0)
return (EFAULT);
return (0);
}
#undef __FN__
#define __FN__ "ippctl_extract_op"
static int
ippctl_extract_op(
nvlist_t *nvlp,
uint8_t *valp)
{
int rc;
if ((rc = nvlist_lookup_byte(nvlp, IPPCTL_OP, valp)) != 0)
return (rc);
(void) nvlist_remove_all(nvlp, IPPCTL_OP);
return (0);
}
#undef __FN__
#define __FN__ "ippctl_extract_aname"
static int
ippctl_extract_aname(
nvlist_t *nvlp,
char **valp)
{
int rc;
char *ptr;
if ((rc = nvlist_lookup_string(nvlp, IPPCTL_ANAME, &ptr)) != 0)
return (rc);
*valp = kmem_alloc(strlen(ptr) + 1, KM_SLEEP);
(void) strcpy(*valp, ptr);
(void) nvlist_remove_all(nvlp, IPPCTL_ANAME);
return (0);
}
#undef __FN__
#define __FN__ "ippctl_extract_modname"
static int
ippctl_extract_modname(
nvlist_t *nvlp,
char **valp)
{
int rc;
char *ptr;
if ((rc = nvlist_lookup_string(nvlp, IPPCTL_MODNAME, &ptr)) != 0)
return (rc);
*valp = kmem_alloc(strlen(ptr) + 1, KM_SLEEP);
(void) strcpy(*valp, ptr);
(void) nvlist_remove_all(nvlp, IPPCTL_MODNAME);
return (0);
}
#undef __FN__
#define __FN__ "ippctl_attach_modname"
static int
ippctl_attach_modname(
nvlist_t *nvlp,
char *modname)
{
return (nvlist_add_string(nvlp, IPPCTL_MODNAME, modname));
}
#undef __FN__
#define __FN__ "ippctl_attach_modname_array"
static int
ippctl_attach_modname_array(
nvlist_t *nvlp,
char **modname_array,
int nelt)
{
return (nvlist_add_string_array(nvlp, IPPCTL_MODNAME_ARRAY,
modname_array, nelt));
}
#undef __FN__
#define __FN__ "ippctl_attach_aname_array"
static int
ippctl_attach_aname_array(
nvlist_t *nvlp,
char **aname_array,
int nelt)
{
return (nvlist_add_string_array(nvlp, IPPCTL_ANAME_ARRAY,
aname_array, nelt));
}
#undef __FN__
#define __FN__ "ippctl_extract_flags"
static int
ippctl_extract_flags(
nvlist_t *nvlp,
ipp_flags_t *valp)
{
int rc;
if ((rc = nvlist_lookup_uint32(nvlp, IPPCTL_FLAGS,
(uint32_t *)valp)) != 0)
return (rc);
(void) nvlist_remove_all(nvlp, IPPCTL_FLAGS);
return (0);
}
#undef __FN__
#define __FN__ "ippctl_cmd"
static int
ippctl_cmd(
char *cbuf,
size_t cbuflen,
size_t *nextbuflenp)
{
nvlist_t *nvlp = NULL;
int rc;
char *aname = NULL;
char *modname = NULL;
ipp_flags_t flags;
uint8_t op;
ippctl_flush();
*nextbuflenp = 0;
if ((rc = nvlist_unpack(cbuf, cbuflen, &nvlp, KM_SLEEP)) != 0)
return (rc);
if ((rc = ippctl_extract_op(nvlp, &op)) != 0) {
nvlist_free(nvlp);
return (rc);
}
switch (op) {
case IPPCTL_OP_ACTION_CREATE:
DBG0(DBG_CBOPS, "op = IPPCTL_OP_ACTION_CREATE\n");
if ((rc = ippctl_extract_modname(nvlp, &modname)) != 0) {
nvlist_free(nvlp);
return (rc);
}
if ((rc = ippctl_extract_aname(nvlp, &aname)) != 0) {
FREE_TEXT(modname);
nvlist_free(nvlp);
return (rc);
}
if ((rc = ippctl_extract_flags(nvlp, &flags)) != 0) {
FREE_TEXT(aname);
FREE_TEXT(modname);
nvlist_free(nvlp);
return (rc);
}
rc = ippctl_action_create(modname, aname, nvlp, flags);
break;
case IPPCTL_OP_ACTION_MODIFY:
DBG0(DBG_CBOPS, "op = IPPCTL_OP_ACTION_MODIFY\n");
if ((rc = ippctl_extract_aname(nvlp, &aname)) != 0) {
nvlist_free(nvlp);
return (rc);
}
if ((rc = ippctl_extract_flags(nvlp, &flags)) != 0) {
FREE_TEXT(aname);
nvlist_free(nvlp);
return (rc);
}
rc = ippctl_action_modify(aname, nvlp, flags);
break;
case IPPCTL_OP_ACTION_DESTROY:
DBG0(DBG_CBOPS, "op = IPPCTL_OP_ACTION_DESTROY\n");
if ((rc = ippctl_extract_aname(nvlp, &aname)) != 0) {
nvlist_free(nvlp);
return (rc);
}
if ((rc = ippctl_extract_flags(nvlp, &flags)) != 0) {
FREE_TEXT(aname);
nvlist_free(nvlp);
return (rc);
}
nvlist_free(nvlp);
rc = ippctl_action_destroy(aname, flags);
break;
case IPPCTL_OP_ACTION_INFO:
DBG0(DBG_CBOPS, "op = IPPCTL_OP_ACTION_INFO\n");
if ((rc = ippctl_extract_aname(nvlp, &aname)) != 0) {
nvlist_free(nvlp);
return (rc);
}
if ((rc = ippctl_extract_flags(nvlp, &flags)) != 0) {
nvlist_free(nvlp);
FREE_TEXT(aname);
return (rc);
}
nvlist_free(nvlp);
rc = ippctl_action_info(aname, flags);
break;
case IPPCTL_OP_ACTION_MOD:
DBG0(DBG_CBOPS, "op = IPPCTL_OP_ACTION_MOD\n");
if ((rc = ippctl_extract_aname(nvlp, &aname)) != 0) {
nvlist_free(nvlp);
return (rc);
}
nvlist_free(nvlp);
rc = ippctl_action_mod(aname);
break;
case IPPCTL_OP_LIST_MODS:
DBG0(DBG_CBOPS, "op = IPPCTL_OP_LIST_MODS\n");
nvlist_free(nvlp);
rc = ippctl_list_mods();
break;
case IPPCTL_OP_MOD_LIST_ACTIONS:
DBG0(DBG_CBOPS, "op = IPPCTL_OP_LIST_MODS\n");
if ((rc = ippctl_extract_modname(nvlp, &modname)) != 0) {
nvlist_free(nvlp);
return (rc);
}
nvlist_free(nvlp);
rc = ippctl_mod_list_actions(modname);
break;
default:
nvlist_free(nvlp);
rc = EINVAL;
break;
}
if (rc == 0)
*nextbuflenp = ippctl_array[0].buflen;
return (rc);
}
#undef __FN__
#define __FN__ "ippctl_action_create"
static int
ippctl_action_create(
char *modname,
char *aname,
nvlist_t *nvlp,
ipp_flags_t flags)
{
int ipp_rc;
int rc;
ipp_mod_id_t mid;
ipp_action_id_t aid;
mid = ipp_mod_lookup(modname);
FREE_TEXT(modname);
ipp_rc = ipp_action_create(mid, aname, &nvlp, flags, &aid);
FREE_TEXT(aname);
if ((rc = ippctl_set_rc(ipp_rc)) != 0) {
if (nvlp != NULL) {
nvlist_free(nvlp);
if (ipp_action_destroy(aid, 0) != 0) {
cmn_err(CE_PANIC,
"ippctl: unrecoverable error (aid = %d)",
aid);
}
}
return (rc);
}
if (nvlp != NULL) {
rc = ippctl_callback(nvlp, NULL);
nvlist_free(nvlp);
} else
rc = 0;
return (rc);
}
#undef __FN__
#define __FN__ "ippctl_action_destroy"
static int
ippctl_action_destroy(
char *aname,
ipp_flags_t flags)
{
ipp_action_id_t aid;
int ipp_rc;
int rc;
aid = ipp_action_lookup(aname);
FREE_TEXT(aname);
ipp_rc = ipp_action_destroy(aid, flags);
if ((rc = ippctl_set_rc(ipp_rc)) != 0)
return (rc);
return (0);
}
#undef __FN__
#define __FN__ "ippctl_action_modify"
static int
ippctl_action_modify(
char *aname,
nvlist_t *nvlp,
ipp_flags_t flags)
{
ipp_action_id_t aid;
int ipp_rc;
int rc;
aid = ipp_action_lookup(aname);
FREE_TEXT(aname);
ipp_rc = ipp_action_modify(aid, &nvlp, flags);
if ((rc = ippctl_set_rc(ipp_rc)) != 0) {
nvlist_free(nvlp);
return (rc);
}
if (nvlp != NULL) {
rc = ippctl_callback(nvlp, NULL);
nvlist_free(nvlp);
} else
rc = 0;
return (rc);
}
#undef __FN__
#define __FN__ "ippctl_action_info"
static int
ippctl_action_info(
char *aname,
ipp_flags_t flags)
{
ipp_action_id_t aid;
int ipp_rc;
int rc;
aid = ipp_action_lookup(aname);
FREE_TEXT(aname);
ipp_rc = ipp_action_info(aid, ippctl_callback, NULL, flags);
if ((rc = ippctl_set_rc(ipp_rc)) != 0)
return (rc);
return (0);
}
#undef __FN__
#define __FN__ "ippctl_action_mod"
static int
ippctl_action_mod(
char *aname)
{
ipp_mod_id_t mid;
ipp_action_id_t aid;
char *modname;
nvlist_t *nvlp;
int ipp_rc;
int rc;
aid = ipp_action_lookup(aname);
FREE_TEXT(aname);
if ((ipp_rc = ipp_action_mod(aid, &mid)) == 0)
ipp_rc = ipp_mod_name(mid, &modname);
if ((rc = ippctl_set_rc(ipp_rc)) != 0)
return (rc);
if (ipp_rc == 0) {
if ((rc = nvlist_alloc(&nvlp, NV_UNIQUE_NAME, KM_SLEEP)) != 0)
return (rc);
if ((rc = ippctl_attach_modname(nvlp, modname)) != 0) {
nvlist_free(nvlp);
return (rc);
}
FREE_TEXT(modname);
rc = ippctl_callback(nvlp, NULL);
nvlist_free(nvlp);
} else
rc = 0;
return (rc);
}
#undef __FN__
#define __FN__ "ippctl_list_mods"
static int
ippctl_list_mods(
void)
{
nvlist_t *nvlp;
int ipp_rc;
int rc = 0;
ipp_mod_id_t *mid_array;
char **modname_array = NULL;
int nelt;
int length;
int i;
if ((ipp_rc = ipp_list_mods(&mid_array, &nelt)) == 0) {
if (nelt > 0) {
length = nelt * sizeof (char *);
modname_array = kmem_zalloc(length, KM_SLEEP);
for (i = 0; i < nelt; i++) {
if (ipp_mod_name(mid_array[i],
&modname_array[i]) != 0) {
kmem_free(mid_array, nelt *
sizeof (ipp_mod_id_t));
FREE_TEXT_ARRAY(modname_array, nelt);
ipp_rc = EAGAIN;
goto done;
}
}
kmem_free(mid_array, nelt * sizeof (ipp_mod_id_t));
if ((rc = nvlist_alloc(&nvlp, NV_UNIQUE_NAME,
KM_SLEEP)) != 0) {
FREE_TEXT_ARRAY(modname_array, nelt);
return (rc);
}
if ((rc = ippctl_attach_modname_array(nvlp,
modname_array, nelt)) != 0) {
FREE_TEXT_ARRAY(modname_array, nelt);
nvlist_free(nvlp);
return (rc);
}
FREE_TEXT_ARRAY(modname_array, nelt);
if ((rc = ippctl_callback(nvlp, NULL)) != 0) {
nvlist_free(nvlp);
return (rc);
}
nvlist_free(nvlp);
}
}
done:
if ((rc = ippctl_set_rc(ipp_rc)) != 0)
return (rc);
return (0);
}
#undef __FN__
#define __FN__ "ippctl_mod_list_actions"
static int
ippctl_mod_list_actions(
char *modname)
{
ipp_mod_id_t mid;
nvlist_t *nvlp;
int ipp_rc;
int rc = 0;
ipp_action_id_t *aid_array;
char **aname_array = NULL;
int nelt;
int length;
int i;
mid = ipp_mod_lookup(modname);
FREE_TEXT(modname);
if ((ipp_rc = ipp_mod_list_actions(mid, &aid_array, &nelt)) == 0) {
if (nelt > 0) {
length = nelt * sizeof (char *);
aname_array = kmem_zalloc(length, KM_SLEEP);
for (i = 0; i < nelt; i++) {
if (ipp_action_name(aid_array[i],
&aname_array[i]) != 0) {
kmem_free(aid_array, nelt *
sizeof (ipp_action_id_t));
FREE_TEXT_ARRAY(aname_array, nelt);
ipp_rc = EAGAIN;
goto done;
}
}
kmem_free(aid_array, nelt * sizeof (ipp_action_id_t));
if ((rc = nvlist_alloc(&nvlp, NV_UNIQUE_NAME,
KM_SLEEP)) != 0) {
FREE_TEXT_ARRAY(aname_array, nelt);
return (rc);
}
if ((rc = ippctl_attach_aname_array(nvlp, aname_array,
nelt)) != 0) {
FREE_TEXT_ARRAY(aname_array, nelt);
nvlist_free(nvlp);
return (rc);
}
FREE_TEXT_ARRAY(aname_array, nelt);
if ((rc = ippctl_callback(nvlp, NULL)) != 0) {
nvlist_free(nvlp);
return (rc);
}
nvlist_free(nvlp);
}
}
done:
if ((rc = ippctl_set_rc(ipp_rc)) != 0)
return (rc);
return (0);
}
#undef __FN__
#define __FN__ "ippctl_data"
static int
ippctl_data(
char **dbufp,
size_t *dbuflenp,
size_t *nextbuflenp)
{
int i;
DBG0(DBG_CBOPS, "called\n");
i = ippctl_rindex;
if (i == ippctl_windex)
return (ENOENT);
*dbufp = ippctl_array[i].buf;
*dbuflenp = ippctl_array[i].buflen;
DBG2(DBG_CBOPS, "accessing nvlist[%d], length %lu\n", i, *dbuflenp);
ASSERT(*dbufp != NULL);
i++;
if (i < ippctl_windex)
*nextbuflenp = ippctl_array[i].buflen;
else
*nextbuflenp = 0;
ippctl_rindex = i;
return (0);
}
#undef __FN__
#define __FN__ "ippctl_flush"
static void
ippctl_flush(
void)
{
int i;
char *buf;
size_t buflen;
for (i = 0; i < ippctl_limit; i++) {
if ((buflen = ippctl_array[i].buflen) > 0) {
buf = ippctl_array[i].buf;
ASSERT(buf != NULL);
kmem_free(buf, buflen);
}
}
bzero(ippctl_array, ippctl_limit * sizeof (ippctl_buf_t));
ippctl_rindex = 0;
ippctl_windex = 1;
}
#undef __FN__
#define __FN__ "ippctl_add_nvlist"
static int
ippctl_add_nvlist(
nvlist_t *nvlp,
int i)
{
char *buf;
size_t buflen;
int rc;
buf = NULL;
if ((rc = nvlist_pack(nvlp, &buf, &buflen, NV_ENCODE_NATIVE,
KM_SLEEP)) != 0) {
ippctl_array[i].buf = NULL;
ippctl_array[i].buflen = 0;
return (rc);
}
DBG2(DBG_CBOPS, "added nvlist[%d]: length %lu\n", i, buflen);
ippctl_array[i].buf = buf;
ippctl_array[i].buflen = buflen;
return (0);
}
#undef __FN__
#define __FN__ "ippctl_callback"
static int
ippctl_callback(
nvlist_t *nvlp,
void *arg)
{
int i;
int rc;
i = ippctl_windex;
ASSERT(i != 0);
if (i == ippctl_limit)
ippctl_realloc();
if ((rc = ippctl_add_nvlist(nvlp, i)) == 0)
ippctl_windex++;
return (rc);
}
#undef __FN__
#define __FN__ "ippctl_set_rc"
static int
ippctl_set_rc(
int val)
{
nvlist_t *nvlp;
int rc;
if ((rc = nvlist_alloc(&nvlp, NV_UNIQUE_NAME, KM_SLEEP)) != 0)
return (ENOMEM);
if ((rc = nvlist_add_int32(nvlp, IPPCTL_RC, val)) != 0) {
nvlist_free(nvlp);
return (rc);
}
rc = ippctl_add_nvlist(nvlp, 0);
nvlist_free(nvlp);
return (rc);
}
#undef __FN__
#define __FN__ "ippctl_alloc"
static void
ippctl_alloc(
int limit)
{
ippctl_array = kmem_zalloc(limit * sizeof (ippctl_buf_t), KM_SLEEP);
ippctl_limit = limit;
ippctl_rindex = 0;
ippctl_windex = 1;
}
#undef __FN__
#define __FN__ "ippctl_realloc"
static void
ippctl_realloc(
void)
{
ippctl_buf_t *array;
int limit;
int i;
limit = ippctl_limit << 1;
array = kmem_zalloc(limit * sizeof (ippctl_buf_t), KM_SLEEP);
for (i = 0; i < ippctl_limit; i++)
array[i] = ippctl_array[i];
kmem_free(ippctl_array, ippctl_limit * sizeof (ippctl_buf_t));
ippctl_array = array;
ippctl_limit = limit;
}
#undef __FN__
#define __FN__ "ippctl_free"
static void
ippctl_free(
void)
{
ippctl_flush();
kmem_free(ippctl_array, ippctl_limit * sizeof (ippctl_buf_t));
ippctl_array = NULL;
ippctl_limit = -1;
ippctl_rindex = -1;
ippctl_windex = -1;
}
#undef __FN__
#ifdef IPPCTL_DEBUG
static void
ippctl_debug(
uint64_t type,
char *fn,
char *fmt,
...)
{
char buf[255];
va_list adx;
if ((type & ippctl_debug_flags) == 0)
return;
mutex_enter(debug_mutex);
va_start(adx, fmt);
(void) vsnprintf(buf, 255, fmt, adx);
va_end(adx);
printf("%s: %s", fn, buf);
mutex_exit(debug_mutex);
}
#endif