libipp.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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
* or http://www.opensolaris.org/os/licensing.
* 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 2001-2002 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <strings.h>
#include <string.h>
#include <fcntl.h>
#include <assert.h>
#include <libipp.h>
#include <libnvpair.h>
#include <ipp/ippctl.h>
/*
* Debug macros
*/
#if defined(DEBUG) && !defined(lint)
uint32_t ipp_debug_flags =
/*
* DBG_IO |
*/
DBG_ERR |
0;
#define DBG0(flags, fmt) \
do { \
if (flags & ipp_debug_flags) \
fprintf(stderr, "libipp: " __FN__ ": " fmt); \
} while (0)
#define DBG1(flags, fmt, a) \
do { \
if (flags & ipp_debug_flags) \
fprintf(stderr, "libipp: " __FN__ ": " fmt, a); \
} while (0)
#define DBG2(flags, fmt, a, b) \
do { \
if (flags & ipp_debug_flags) \
fprintf(stderr, "libipp: " __FN__ ": " fmt, a, \
b); \
} while (0)
#define DBG3(flags, fmt, a, b, c) \
do { \
if (flags & ipp_debug_flags) \
fprintf(stderr, "libipp: " __FN__ ": " fmt, a, \
b, c); \
} while (0)
#else /* defined(DEBUG) && !defined(lint) */
#define DBG0(flags, fmt)
#define DBG1(flags, fmt, a)
#define DBG2(flags, fmt, a, b)
#define DBG3(flags, fmt, a, b, c)
#endif /* defined(DEBUG) && !defined(lint) */
/*
* Control device node
*/
#define IPPCTL_DEVICE "/devices/pseudo/ippctl@0:ctl"
/*
* Structures.
*/
typedef struct array_desc_t {
char *name;
char **array;
int nelt;
} array_desc_t;
/*
* Prototypes
*/
static int nvlist_callback(nvlist_t *, void *);
static int string_callback(nvlist_t *, void *);
static int string_array_callback(nvlist_t *, void *);
static int dispatch(nvlist_t **, int (*)(nvlist_t *, void *), void *);
/*
* API functions
*/
#define __FN__ "ipp_action_create"
int
ipp_action_create(
const char *modname,
const char *aname,
nvlist_t **nvlpp,
ipp_flags_t flags)
{
nvlist_t *nvlp;
int rc;
/*
* Sanity check the arguments.
*/
if (nvlpp == NULL || modname == NULL || aname == NULL) {
DBG0(DBG_ERR, "bad argument\n");
errno = EINVAL;
return (-1);
}
/*
* Add our data to the nvlist. (This information will be removed for
* use by ippctl).
*/
nvlp = *nvlpp;
if ((rc = nvlist_add_byte(nvlp, IPPCTL_OP,
IPPCTL_OP_ACTION_CREATE)) != 0) {
DBG1(DBG_ERR, "failed to add '%s' to nvlist\n", IPPCTL_OP);
goto failed;
}
if ((rc = nvlist_add_string(nvlp, IPPCTL_MODNAME,
(char *)modname)) != 0) {
DBG1(DBG_ERR, "failed to add '%s' to nvlist\n",
IPPCTL_MODNAME);
goto failed;
}
if ((rc = nvlist_add_string(nvlp, IPPCTL_ANAME, (char *)aname)) != 0) {
DBG1(DBG_ERR, "failed to add '%s' to nvlist\n", IPPCTL_ANAME);
goto failed;
}
if ((rc = nvlist_add_uint32(nvlp, IPPCTL_FLAGS, flags)) != 0) {
DBG1(DBG_ERR, "failed to add '%s' to nvlist\n", IPPCTL_FLAGS);
goto failed;
}
/*
* Talk to the kernel.
*/
return (dispatch(nvlpp, nvlist_callback, (void *)nvlpp));
failed:
errno = rc;
return (-1);
}
#undef __FN__
#define __FN__ "ipp_action_destroy"
int
ipp_action_destroy(
const char *aname,
ipp_flags_t flags)
{
nvlist_t *nvlp;
int rc;
/*
* Sanity check the arguments.
*/
if (aname == NULL) {
DBG0(DBG_ERR, "bad argument\n");
errno = EINVAL;
return (-1);
}
/*
* Create an nvlist for our data as none is passed into the function.
*/
if ((rc = nvlist_alloc(&nvlp, NV_UNIQUE_NAME, 0)) != 0) {
DBG0(DBG_ERR, "failed to allocate nvlist\n");
nvlp = NULL;
goto failed;
}
if ((rc = nvlist_add_byte(nvlp, IPPCTL_OP,
IPPCTL_OP_ACTION_DESTROY)) != 0) {
DBG1(DBG_ERR, "failed to add '%s' to nvlist\n", IPPCTL_OP);
goto failed;
}
if ((rc = nvlist_add_string(nvlp, IPPCTL_ANAME, (char *)aname)) != 0) {
DBG1(DBG_ERR, "failed to add '%s' to nvlist\n", IPPCTL_ANAME);
goto failed;
}
if ((rc = nvlist_add_uint32(nvlp, IPPCTL_FLAGS, flags)) != 0) {
DBG1(DBG_ERR, "failed to add '%s' to nvlist\n", IPPCTL_FLAGS);
goto failed;
}
/*
* Talk to the kernel.
*/
return (dispatch(&nvlp, NULL, NULL));
failed:
if (nvlp != NULL)
nvlist_free(nvlp);
errno = rc;
return (-1);
}
#undef __FN__
#define __FN__ "ipp_action_modify"
int
ipp_action_modify(
const char *aname,
nvlist_t **nvlpp,
ipp_flags_t flags)
{
nvlist_t *nvlp;
int rc;
/*
* Sanity check the arguments.
*/
if (nvlpp == NULL || aname == NULL) {
DBG0(DBG_ERR, "bad argument\n");
errno = EINVAL;
return (-1);
}
/*
* Add our data to the nvlist.
*/
nvlp = *nvlpp;
if ((rc = nvlist_add_byte(nvlp, IPPCTL_OP,
IPPCTL_OP_ACTION_MODIFY)) != 0) {
DBG1(DBG_ERR, "failed to add '%s' to nvlist\n", IPPCTL_OP);
goto failed;
}
if ((rc = nvlist_add_string(nvlp, IPPCTL_ANAME, (char *)aname)) != 0) {
DBG1(DBG_ERR, "failed to add '%s' to nvlist\n", IPPCTL_ANAME);
goto failed;
}
if ((rc = nvlist_add_uint32(nvlp, IPPCTL_FLAGS, flags)) != 0) {
DBG1(DBG_ERR, "failed to add '%s' to nvlist\n", IPPCTL_FLAGS);
goto failed;
}
/*
* Talk to the kernel.
*/
return (dispatch(nvlpp, nvlist_callback, (void *)nvlpp));
failed:
errno = rc;
return (-1);
}
#undef __FN__
#define __FN__ "ipp_action_info"
int
ipp_action_info(
const char *aname,
int (*fn)(nvlist_t *, void *),
void *arg,
ipp_flags_t flags)
{
nvlist_t *nvlp;
int rc;
/*
* Sanity check the arguments.
*/
if (aname == NULL || fn == NULL) {
DBG0(DBG_ERR, "bad argument\n");
errno = EINVAL;
return (-1);
}
/*
* Create an nvlist for our data.
*/
if ((rc = nvlist_alloc(&nvlp, NV_UNIQUE_NAME, 0)) != 0) {
DBG0(DBG_ERR, "failed to allocate nvlist\n");
nvlp = NULL;
}
if ((rc = nvlist_add_byte(nvlp, IPPCTL_OP,
IPPCTL_OP_ACTION_INFO)) != 0) {
DBG1(DBG_ERR, "failed to add '%s' to nvlist\n", IPPCTL_OP);
goto failed;
}
if ((rc = nvlist_add_string(nvlp, IPPCTL_ANAME, (char *)aname)) != 0) {
DBG1(DBG_ERR, "failed to add '%s' to nvlist\n", IPPCTL_ANAME);
goto failed;
}
if ((rc = nvlist_add_uint32(nvlp, IPPCTL_FLAGS, flags)) != 0) {
DBG1(DBG_ERR, "failed to add '%s' to nvlist\n", IPPCTL_FLAGS);
goto failed;
}
/*
* Talk to the kernel.
*/
return (dispatch(&nvlp, fn, arg));
failed:
if (nvlp != NULL)
nvlist_free(nvlp);
errno = rc;
return (-1);
}
#undef __FN__
#define __FN__ "ipp_action_mod"
int
ipp_action_mod(
const char *aname,
char **modnamep)
{
nvlist_t *nvlp;
int rc;
/*
* Sanity check the arguments.
*/
if (aname == NULL || modnamep == NULL) {
DBG0(DBG_ERR, "bad argument\n");
errno = EINVAL;
return (-1);
}
/*
* Create an nvlist for our data.
*/
if ((rc = nvlist_alloc(&nvlp, NV_UNIQUE_NAME, 0)) != 0) {
DBG0(DBG_ERR, "failed to allocate nvlist\n");
nvlp = NULL;
goto failed;
}
if ((rc = nvlist_add_byte(nvlp, IPPCTL_OP,
IPPCTL_OP_ACTION_MOD)) != 0) {
DBG1(DBG_ERR, "failed to add '%s' to nvlist\n", IPPCTL_OP);
goto failed;
}
if ((rc = nvlist_add_string(nvlp, IPPCTL_ANAME, (char *)aname)) != 0) {
DBG1(DBG_ERR, "failed to add '%s' to nvlist\n", IPPCTL_ANAME);
goto failed;
}
/*
* Talk to the kernel.
*/
return (dispatch(&nvlp, string_callback, (void *)modnamep));
failed:
if (nvlp != NULL)
nvlist_free(nvlp);
errno = rc;
return (-1);
}
#undef __FN__
#define __FN__ "ipp_list_mods"
int
ipp_list_mods(
char ***modname_arrayp,
int *neltp)
{
nvlist_t *nvlp;
array_desc_t ad;
int rc;
/*
* Sanity check the arguments.
*/
if (modname_arrayp == NULL || neltp == NULL) {
DBG0(DBG_ERR, "bad argument");
errno = EINVAL;
return (-1);
}
/*
* Create an nvlist for our data.
*/
if ((rc = nvlist_alloc(&nvlp, NV_UNIQUE_NAME, 0)) != 0) {
DBG0(DBG_ERR, "failed to allocate nvlist\n");
nvlp = NULL;
}
if ((rc = nvlist_add_byte(nvlp, IPPCTL_OP,
IPPCTL_OP_LIST_MODS)) != 0) {
DBG1(DBG_ERR, "failed to add '%s' to nvlist\n", IPPCTL_OP);
goto failed;
}
/*
* Talk to the kernel.
*/
ad.name = IPPCTL_MODNAME_ARRAY;
ad.array = NULL;
ad.nelt = 0;
if ((rc = dispatch(&nvlp, string_array_callback, (void *)&ad)) == 0) {
*modname_arrayp = ad.array;
*neltp = ad.nelt;
}
return (rc);
failed:
if (nvlp != NULL)
nvlist_free(nvlp);
errno = rc;
return (-1);
}
#undef __FN__
#define __FN__ "ipp_mod_list_actions"
int
ipp_mod_list_actions(
const char *modname,
char ***aname_arrayp,
int *neltp)
{
nvlist_t *nvlp;
array_desc_t ad;
int rc;
/*
* Sanity check the arguments.
*/
if (modname == NULL || aname_arrayp == NULL || neltp == NULL) {
DBG0(DBG_ERR, "bad argument");
errno = EINVAL;
return (-1);
}
/*
* Create an nvlist for our data.
*/
if ((rc = nvlist_alloc(&nvlp, NV_UNIQUE_NAME, 0)) != 0) {
DBG0(DBG_ERR, "failed to allocate nvlist\n");
nvlp = NULL;
}
if ((rc = nvlist_add_byte(nvlp, IPPCTL_OP,
IPPCTL_OP_MOD_LIST_ACTIONS)) != 0) {
DBG1(DBG_ERR, "failed to add '%s' to nvlist\n", IPPCTL_OP);
goto failed;
}
if ((rc = nvlist_add_string(nvlp, IPPCTL_MODNAME,
(char *)modname)) != 0) {
DBG1(DBG_ERR, "failed to add '%s' to nvlist\n", IPPCTL_MODNAME);
goto failed;
}
/*
* Talk to the kernel.
*/
ad.name = IPPCTL_ANAME_ARRAY;
ad.array = NULL;
ad.nelt = 0;
if ((rc = dispatch(&nvlp, string_array_callback, (void *)&ad)) == 0) {
*aname_arrayp = ad.array;
*neltp = ad.nelt;
}
return (rc);
failed:
if (nvlp != NULL)
nvlist_free(nvlp);
errno = rc;
return (-1);
}
#undef __FN__
#define __FN__ "ipp_free"
void
ipp_free(
char *buf)
{
free(buf);
}
#undef __FN__
#define __FN__ "ipp_free_array"
void
ipp_free_array(
char **array,
int nelt)
{
int i;
assert(array[nelt] == NULL);
for (i = 0; i < nelt; i++)
free(array[i]);
free(array);
}
#undef __FN__
#define __FN__ "nvlist_callback"
static int
nvlist_callback(
nvlist_t *nvlp,
void *arg)
{
nvlist_t **nvlpp = (nvlist_t **)arg;
int rc;
/*
* Callback function used by ipp_action_create() and
* ipp_action_modify()
*/
DBG0(DBG_IO, "called\n");
assert(nvlpp != NULL);
assert(*nvlpp == NULL);
/*
* Duplicate the nvlist and set the given pointer to point at the new
* copy.
*/
if ((rc = nvlist_dup(nvlp, nvlpp, 0)) != 0) {
DBG0(DBG_ERR, "failed to dup nvlist\n");
errno = rc;
return (-1);
}
return (0);
}
#undef __FN__
#define __FN__ "string_callback"
static int
string_callback(
nvlist_t *nvlp,
void *arg)
{
char **namep = (char **)arg;
char *name;
char *ptr;
int rc;
/*
* Callback function used by ipp_action_mod()
*/
DBG0(DBG_IO, "called\n");
assert(namep != NULL);
/*
* Look up the module name from the nvlist.
*/
if ((rc = nvlist_lookup_string(nvlp, IPPCTL_MODNAME, &ptr)) != 0) {
DBG0(DBG_ERR, "failed to find string\n");
errno = rc;
return (-1);
}
/*
* Allocate a duplicate string.
*/
if ((name = strdup(ptr)) == NULL) {
DBG0(DBG_ERR, "failed to duplicate string\n");
return (-1);
}
/*
* Set the given pointer to point at the string.
*/
*namep = name;
return (0);
}
#undef __FN__
#define __FN__ "string_array_callback"
static int
string_array_callback(
nvlist_t *nvlp,
void *arg)
{
array_desc_t *adp = (array_desc_t *)arg;
char **dst;
char **src;
uint_t nelt;
int i;
int rc;
/*
* Callback function used by ipp_list_mods()
*/
DBG0(DBG_IO, "called\n");
assert(adp != NULL);
/*
* Look up the module name from the nvlist.
*/
if ((rc = nvlist_lookup_string_array(nvlp, adp->name, &src,
&nelt)) != 0) {
DBG0(DBG_ERR, "failed to find array\n");
errno = rc;
return (-1);
}
/*
* Allocate an array.
*/
if ((dst = malloc((nelt + 1) * sizeof (char *))) == NULL) {
DBG0(DBG_ERR, "failed to allocate new array\n");
return (-1);
}
/*
* For each string in the array, allocate a new buffer and copy
* the string into it.
*/
for (i = 0; i < nelt; i++) {
if ((dst[i] = strdup(src[i])) == NULL) {
while (--i >= 0) {
free(dst[i]);
}
free(dst);
DBG0(DBG_ERR, "failed to duplicate array\n");
return (-1);
}
}
dst[nelt] = NULL;
/*
* Set the information to be passed back.
*/
adp->array = dst;
adp->nelt = nelt;
return (0);
}
#undef __FN__
#define __FN__ "dispatch"
static int
dispatch(
nvlist_t **nvlpp,
int (*fn)(nvlist_t *, void *),
void *arg)
{
char *cbuf = NULL;
char *dbuf = NULL;
size_t cbuflen = 0;
size_t dbuflen = 0;
size_t thisbuflen = 0;
size_t nextbuflen = 0;
int rc;
ippctl_ioctl_t iioc;
int fd;
nvlist_t *cnvlp;
nvlist_t *dnvlp = NULL;
int count;
int rval;
/*
* Sanity check the 'command' nvlist.
*/
cnvlp = *nvlpp;
if (cnvlp == NULL) {
rc = EINVAL;
return (-1);
}
/*
* Pack the nvlist and then free the original.
*/
if ((rc = nvlist_pack(cnvlp, &cbuf, &cbuflen, NV_ENCODE_NATIVE,
0)) != 0) {
DBG0(DBG_ERR, "failed to pack nvlist\n");
nvlist_free(cnvlp);
errno = rc;
return (-1);
}
nvlist_free(cnvlp);
*nvlpp = NULL;
/*
* Open the control device node.
*/
DBG1(DBG_IO, "opening %s\n", IPPCTL_DEVICE);
if ((fd = open(IPPCTL_DEVICE, O_RDWR | O_NOCTTY)) == -1) {
DBG1(DBG_ERR, "failed to open %s\n", IPPCTL_DEVICE);
goto command_failed;
}
/*
* Set up an ioctl structure to point at the packed nvlist.
*/
iioc.ii_buf = cbuf;
iioc.ii_buflen = cbuflen;
/*
* Issue a command ioctl, passing the ioctl structure.
*/
DBG0(DBG_IO, "command\n");
if ((rc = ioctl(fd, IPPCTL_CMD, &iioc)) < 0) {
DBG0(DBG_ERR, "command ioctl failed\n");
goto command_failed;
}
/*
* Get back the length of the first data buffer.
*/
if ((nextbuflen = (size_t)rc) == 0) {
DBG0(DBG_ERR, "no data buffer\n");
errno = EPROTO;
goto command_failed;
}
/*
* Try to re-use the command buffer as the first data buffer.
*/
dbuf = cbuf;
thisbuflen = cbuflen;
count = 0;
while (nextbuflen != 0) {
dbuflen = nextbuflen;
/*
* Check whether the buffer we have is long enough for the
* next lot of data. If it isn't, allocate a new one of
* the appropriate length.
*/
if (nextbuflen > thisbuflen) {
if ((dbuf = realloc(dbuf, nextbuflen)) == NULL) {
DBG0(DBG_ERR,
"failed to allocate data buffer\n");
goto data_failed;
}
thisbuflen = nextbuflen;
}
/*
* Set up an ioctl structure to point at the data buffer.
*/
iioc.ii_buf = dbuf;
iioc.ii_buflen = dbuflen;
/*
* Issue a data ioctl, passing the ioctl structure.
*/
DBG2(DBG_IO, "data[%d]: length = %d\n", count, dbuflen);
if ((rc = ioctl(fd, IPPCTL_DATA, &iioc)) < 0) {
DBG0(DBG_ERR, "data ioctl failed\n");
goto data_failed;
}
/*
* Get the length of the *next* data buffer, if there is
* one.
*/
nextbuflen = (size_t)rc;
DBG1(DBG_IO, "nextbuflen = %d\n", nextbuflen);
/*
* Unpack the nvlist that the current data buffer should
* now contain.
*/
if ((rc = nvlist_unpack(dbuf, dbuflen, &dnvlp, 0)) != 0) {
DBG0(DBG_ERR, "failed to unpack nvlist\n");
errno = rc;
goto data_failed;
}
/*
* The first data buffer should contain the kernel function's
* return code. Subsequent buffers contain nvlists which
* should be passed to the given callback function.
*/
if (count == 0) {
if ((rc = nvlist_lookup_int32(dnvlp, IPPCTL_RC,
&rval)) != 0) {
DBG0(DBG_ERR, "failed to find return code\n");
nvlist_free(dnvlp);
errno = rc;
goto data_failed;
}
} else {
if (fn != NULL)
if (fn(dnvlp, arg) != 0) {
/*
* The callback function returned
* a non-zero value. Abort any further
* data collection.
*/
nvlist_free(dnvlp);
free(dbuf);
}
}
/*
* Free the nvlist now that we have extracted the return
* code or called the callback function.
*/
nvlist_free(dnvlp);
dnvlp = NULL;
count++;
}
/*
* Free the data buffer as data collection is now complete.
*/
free(dbuf);
/*
* Close the control device.
*/
(void) close(fd);
/*
* If the kernel returned an error, we should return an error.
* and set errno.
*/
if (rval != 0) {
DBG1(DBG_IO, "kernel return code = %d\n", rval);
errno = rval;
return (-1);
}
return (0);
command_failed:
free(cbuf);
if (fd != -1)
(void) close(fd);
return (-1);
data_failed:
if (dbuf != NULL)
free(dbuf);
(void) close(fd);
return (-1);
}
#undef __FN__