OpenSolaris_b135/lib/libipp/libipp.c

Compare this file to the similar file:
Show the results in this format:

/*
 * 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__