Home | History | Annotate | Download | only in libipp
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License, Version 1.0 only
      6  * (the "License").  You may not use this file except in compliance
      7  * with the License.
      8  *
      9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
     10  * or http://www.opensolaris.org/os/licensing.
     11  * See the License for the specific language governing permissions
     12  * and limitations under the License.
     13  *
     14  * When distributing Covered Code, include this CDDL HEADER in each
     15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     16  * If applicable, add the following below this CDDL HEADER, with the
     17  * fields enclosed by brackets "[]" replaced with your own identifying
     18  * information: Portions Copyright [yyyy] [name of copyright owner]
     19  *
     20  * CDDL HEADER END
     21  */
     22 /*
     23  * Copyright 2001-2002 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     28 
     29 #include <stdio.h>
     30 #include <stdlib.h>
     31 #include <sys/types.h>
     32 #include <sys/stat.h>
     33 #include <unistd.h>
     34 #include <errno.h>
     35 #include <strings.h>
     36 #include <string.h>
     37 #include <fcntl.h>
     38 #include <assert.h>
     39 #include <libipp.h>
     40 #include <libnvpair.h>
     41 #include <ipp/ippctl.h>
     42 
     43 /*
     44  * Debug macros
     45  */
     46 
     47 #if	defined(DEBUG) && !defined(lint)
     48 uint32_t	ipp_debug_flags =
     49 /*
     50  * DBG_IO |
     51  */
     52 DBG_ERR |
     53 0;
     54 
     55 #define	DBG0(flags, fmt)						\
     56 	do {								\
     57 		if (flags & ipp_debug_flags)				\
     58 			fprintf(stderr, "libipp: " __FN__ ": " fmt);	\
     59 	} while (0)
     60 
     61 #define	DBG1(flags, fmt, a)						\
     62 	do {								\
     63 		if (flags & ipp_debug_flags)				\
     64 			fprintf(stderr, "libipp: " __FN__ ": " fmt, a);	\
     65 	} while (0)
     66 
     67 #define	DBG2(flags, fmt, a, b)						\
     68 	do {								\
     69 		if (flags & ipp_debug_flags)				\
     70 			fprintf(stderr, "libipp: " __FN__ ": " fmt, a,	\
     71 			    b);						\
     72 	} while (0)
     73 
     74 #define	DBG3(flags, fmt, a, b, c)					\
     75 	do {								\
     76 		if (flags & ipp_debug_flags)				\
     77 			fprintf(stderr, "libipp: " __FN__ ": " fmt, a,	\
     78 			    b, c);					\
     79 	} while (0)
     80 
     81 #else	/* defined(DEBUG) && !defined(lint) */
     82 #define	DBG0(flags, fmt)
     83 #define	DBG1(flags, fmt, a)
     84 #define	DBG2(flags, fmt, a, b)
     85 #define	DBG3(flags, fmt, a, b, c)
     86 #endif	/* defined(DEBUG) && !defined(lint) */
     87 
     88 /*
     89  * Control device node
     90  */
     91 
     92 #define	IPPCTL_DEVICE	"/devices/pseudo/ippctl@0:ctl"
     93 
     94 /*
     95  * Structures.
     96  */
     97 
     98 typedef	struct array_desc_t {
     99 	char	*name;
    100 	char	**array;
    101 	int	nelt;
    102 } array_desc_t;
    103 
    104 /*
    105  * Prototypes
    106  */
    107 
    108 static int	nvlist_callback(nvlist_t *, void *);
    109 static int	string_callback(nvlist_t *, void *);
    110 static int	string_array_callback(nvlist_t *, void *);
    111 static int	dispatch(nvlist_t **, int (*)(nvlist_t *, void *), void *);
    112 
    113 /*
    114  * API functions
    115  */
    116 #define	__FN__	"ipp_action_create"
    117 int
    118 ipp_action_create(
    119 	const char	*modname,
    120 	const char	*aname,
    121 	nvlist_t	**nvlpp,
    122 	ipp_flags_t	flags)
    123 {
    124 	nvlist_t	*nvlp;
    125 	int		rc;
    126 
    127 	/*
    128 	 * Sanity check the arguments.
    129 	 */
    130 
    131 	if (nvlpp == NULL || modname == NULL || aname == NULL) {
    132 		DBG0(DBG_ERR, "bad argument\n");
    133 		errno = EINVAL;
    134 		return (-1);
    135 	}
    136 
    137 	/*
    138 	 * Add our data to the nvlist. (This information will be removed for
    139 	 * use by ippctl).
    140 	 */
    141 
    142 	nvlp = *nvlpp;
    143 	if ((rc = nvlist_add_byte(nvlp, IPPCTL_OP,
    144 	    IPPCTL_OP_ACTION_CREATE)) != 0) {
    145 		DBG1(DBG_ERR, "failed to add '%s' to nvlist\n", IPPCTL_OP);
    146 		goto failed;
    147 	}
    148 
    149 	if ((rc = nvlist_add_string(nvlp, IPPCTL_MODNAME,
    150 	    (char *)modname)) != 0) {
    151 		DBG1(DBG_ERR, "failed to add '%s' to nvlist\n",
    152 		    IPPCTL_MODNAME);
    153 		goto failed;
    154 	}
    155 
    156 	if ((rc = nvlist_add_string(nvlp, IPPCTL_ANAME, (char *)aname)) != 0) {
    157 		DBG1(DBG_ERR, "failed to add '%s' to nvlist\n", IPPCTL_ANAME);
    158 		goto failed;
    159 	}
    160 
    161 	if ((rc = nvlist_add_uint32(nvlp, IPPCTL_FLAGS, flags)) != 0) {
    162 		DBG1(DBG_ERR, "failed to add '%s' to nvlist\n", IPPCTL_FLAGS);
    163 		goto failed;
    164 	}
    165 
    166 	/*
    167 	 * Talk to the kernel.
    168 	 */
    169 
    170 	return (dispatch(nvlpp, nvlist_callback, (void *)nvlpp));
    171 failed:
    172 	errno = rc;
    173 	return (-1);
    174 }
    175 #undef	__FN__
    176 
    177 #define	__FN__	"ipp_action_destroy"
    178 int
    179 ipp_action_destroy(
    180 	const char	*aname,
    181 	ipp_flags_t	flags)
    182 {
    183 	nvlist_t	*nvlp;
    184 	int		rc;
    185 
    186 	/*
    187 	 * Sanity check the arguments.
    188 	 */
    189 
    190 	if (aname == NULL) {
    191 		DBG0(DBG_ERR, "bad argument\n");
    192 		errno = EINVAL;
    193 		return (-1);
    194 	}
    195 
    196 	/*
    197 	 * Create an nvlist for our data as none is passed into the function.
    198 	 */
    199 
    200 	if ((rc = nvlist_alloc(&nvlp, NV_UNIQUE_NAME, 0)) != 0) {
    201 		DBG0(DBG_ERR, "failed to allocate nvlist\n");
    202 		nvlp = NULL;
    203 		goto failed;
    204 	}
    205 
    206 	if ((rc = nvlist_add_byte(nvlp, IPPCTL_OP,
    207 	    IPPCTL_OP_ACTION_DESTROY)) != 0) {
    208 		DBG1(DBG_ERR, "failed to add '%s' to nvlist\n", IPPCTL_OP);
    209 		goto failed;
    210 	}
    211 
    212 	if ((rc = nvlist_add_string(nvlp, IPPCTL_ANAME, (char *)aname)) != 0) {
    213 		DBG1(DBG_ERR, "failed to add '%s' to nvlist\n", IPPCTL_ANAME);
    214 		goto failed;
    215 	}
    216 
    217 	if ((rc = nvlist_add_uint32(nvlp, IPPCTL_FLAGS, flags)) != 0) {
    218 		DBG1(DBG_ERR, "failed to add '%s' to nvlist\n", IPPCTL_FLAGS);
    219 		goto failed;
    220 	}
    221 
    222 	/*
    223 	 * Talk to the kernel.
    224 	 */
    225 
    226 	return (dispatch(&nvlp, NULL, NULL));
    227 failed:
    228 	if (nvlp != NULL)
    229 		nvlist_free(nvlp);
    230 	errno = rc;
    231 	return (-1);
    232 }
    233 #undef	__FN__
    234 
    235 #define	__FN__	"ipp_action_modify"
    236 int
    237 ipp_action_modify(
    238 	const char	*aname,
    239 	nvlist_t	**nvlpp,
    240 	ipp_flags_t	flags)
    241 {
    242 	nvlist_t	*nvlp;
    243 	int		rc;
    244 
    245 	/*
    246 	 * Sanity check the arguments.
    247 	 */
    248 
    249 	if (nvlpp == NULL || aname == NULL) {
    250 		DBG0(DBG_ERR, "bad argument\n");
    251 		errno = EINVAL;
    252 		return (-1);
    253 	}
    254 
    255 	/*
    256 	 * Add our data to the nvlist.
    257 	 */
    258 
    259 	nvlp = *nvlpp;
    260 	if ((rc = nvlist_add_byte(nvlp, IPPCTL_OP,
    261 	    IPPCTL_OP_ACTION_MODIFY)) != 0) {
    262 		DBG1(DBG_ERR, "failed to add '%s' to nvlist\n", IPPCTL_OP);
    263 		goto failed;
    264 	}
    265 
    266 	if ((rc = nvlist_add_string(nvlp, IPPCTL_ANAME, (char *)aname)) != 0) {
    267 		DBG1(DBG_ERR, "failed to add '%s' to nvlist\n", IPPCTL_ANAME);
    268 		goto failed;
    269 	}
    270 
    271 	if ((rc = nvlist_add_uint32(nvlp, IPPCTL_FLAGS, flags)) != 0) {
    272 		DBG1(DBG_ERR, "failed to add '%s' to nvlist\n", IPPCTL_FLAGS);
    273 		goto failed;
    274 	}
    275 
    276 	/*
    277 	 * Talk to the kernel.
    278 	 */
    279 
    280 	return (dispatch(nvlpp, nvlist_callback, (void *)nvlpp));
    281 failed:
    282 	errno = rc;
    283 	return (-1);
    284 }
    285 #undef	__FN__
    286 
    287 #define	__FN__	"ipp_action_info"
    288 int
    289 ipp_action_info(
    290 	const char	*aname,
    291 	int		(*fn)(nvlist_t *, void *),
    292 	void		*arg,
    293 	ipp_flags_t	flags)
    294 {
    295 	nvlist_t	*nvlp;
    296 	int		rc;
    297 
    298 	/*
    299 	 * Sanity check the arguments.
    300 	 */
    301 
    302 	if (aname == NULL || fn == NULL) {
    303 		DBG0(DBG_ERR, "bad argument\n");
    304 		errno = EINVAL;
    305 		return (-1);
    306 	}
    307 
    308 	/*
    309 	 * Create an nvlist for our data.
    310 	 */
    311 
    312 	if ((rc = nvlist_alloc(&nvlp, NV_UNIQUE_NAME, 0)) != 0) {
    313 		DBG0(DBG_ERR, "failed to allocate nvlist\n");
    314 		nvlp = NULL;
    315 	}
    316 
    317 	if ((rc = nvlist_add_byte(nvlp, IPPCTL_OP,
    318 	    IPPCTL_OP_ACTION_INFO)) != 0) {
    319 		DBG1(DBG_ERR, "failed to add '%s' to nvlist\n", IPPCTL_OP);
    320 		goto failed;
    321 	}
    322 
    323 	if ((rc = nvlist_add_string(nvlp, IPPCTL_ANAME, (char *)aname)) != 0) {
    324 		DBG1(DBG_ERR, "failed to add '%s' to nvlist\n", IPPCTL_ANAME);
    325 		goto failed;
    326 	}
    327 
    328 	if ((rc = nvlist_add_uint32(nvlp, IPPCTL_FLAGS, flags)) != 0) {
    329 		DBG1(DBG_ERR, "failed to add '%s' to nvlist\n", IPPCTL_FLAGS);
    330 		goto failed;
    331 	}
    332 
    333 	/*
    334 	 * Talk to the kernel.
    335 	 */
    336 
    337 	return (dispatch(&nvlp, fn, arg));
    338 failed:
    339 	if (nvlp != NULL)
    340 		nvlist_free(nvlp);
    341 	errno = rc;
    342 	return (-1);
    343 }
    344 #undef	__FN__
    345 
    346 #define	__FN__	"ipp_action_mod"
    347 int
    348 ipp_action_mod(
    349 	const char	*aname,
    350 	char		**modnamep)
    351 {
    352 	nvlist_t	*nvlp;
    353 	int		rc;
    354 
    355 	/*
    356 	 * Sanity check the arguments.
    357 	 */
    358 
    359 	if (aname == NULL || modnamep == NULL) {
    360 		DBG0(DBG_ERR, "bad argument\n");
    361 		errno = EINVAL;
    362 		return (-1);
    363 	}
    364 
    365 	/*
    366 	 * Create an nvlist for our data.
    367 	 */
    368 
    369 	if ((rc = nvlist_alloc(&nvlp, NV_UNIQUE_NAME, 0)) != 0) {
    370 		DBG0(DBG_ERR, "failed to allocate nvlist\n");
    371 		nvlp = NULL;
    372 		goto failed;
    373 	}
    374 
    375 	if ((rc = nvlist_add_byte(nvlp, IPPCTL_OP,
    376 	    IPPCTL_OP_ACTION_MOD)) != 0) {
    377 		DBG1(DBG_ERR, "failed to add '%s' to nvlist\n", IPPCTL_OP);
    378 		goto failed;
    379 	}
    380 
    381 	if ((rc = nvlist_add_string(nvlp, IPPCTL_ANAME, (char *)aname)) != 0) {
    382 		DBG1(DBG_ERR, "failed to add '%s' to nvlist\n", IPPCTL_ANAME);
    383 		goto failed;
    384 	}
    385 
    386 	/*
    387 	 * Talk to the kernel.
    388 	 */
    389 
    390 	return (dispatch(&nvlp, string_callback, (void *)modnamep));
    391 failed:
    392 	if (nvlp != NULL)
    393 		nvlist_free(nvlp);
    394 	errno = rc;
    395 	return (-1);
    396 }
    397 #undef	__FN__
    398 
    399 #define	__FN__	"ipp_list_mods"
    400 int
    401 ipp_list_mods(
    402 	char		***modname_arrayp,
    403 	int		*neltp)
    404 {
    405 	nvlist_t	*nvlp;
    406 	array_desc_t	ad;
    407 	int		rc;
    408 
    409 	/*
    410 	 * Sanity check the arguments.
    411 	 */
    412 
    413 	if (modname_arrayp == NULL || neltp == NULL) {
    414 		DBG0(DBG_ERR, "bad argument");
    415 		errno = EINVAL;
    416 		return (-1);
    417 	}
    418 
    419 	/*
    420 	 * Create an nvlist for our data.
    421 	 */
    422 
    423 	if ((rc = nvlist_alloc(&nvlp, NV_UNIQUE_NAME, 0)) != 0) {
    424 		DBG0(DBG_ERR, "failed to allocate nvlist\n");
    425 		nvlp = NULL;
    426 	}
    427 
    428 	if ((rc = nvlist_add_byte(nvlp, IPPCTL_OP,
    429 	    IPPCTL_OP_LIST_MODS)) != 0) {
    430 		DBG1(DBG_ERR, "failed to add '%s' to nvlist\n", IPPCTL_OP);
    431 		goto failed;
    432 	}
    433 
    434 	/*
    435 	 * Talk to the kernel.
    436 	 */
    437 
    438 	ad.name = IPPCTL_MODNAME_ARRAY;
    439 	ad.array = NULL;
    440 	ad.nelt = 0;
    441 
    442 	if ((rc = dispatch(&nvlp, string_array_callback, (void *)&ad)) == 0) {
    443 		*modname_arrayp = ad.array;
    444 		*neltp = ad.nelt;
    445 	}
    446 
    447 	return (rc);
    448 failed:
    449 	if (nvlp != NULL)
    450 		nvlist_free(nvlp);
    451 	errno = rc;
    452 	return (-1);
    453 }
    454 #undef	__FN__
    455 
    456 #define	__FN__	"ipp_mod_list_actions"
    457 int
    458 ipp_mod_list_actions(
    459 	const char	*modname,
    460 	char		***aname_arrayp,
    461 	int		*neltp)
    462 {
    463 	nvlist_t	*nvlp;
    464 	array_desc_t	ad;
    465 	int		rc;
    466 
    467 	/*
    468 	 * Sanity check the arguments.
    469 	 */
    470 
    471 	if (modname == NULL || aname_arrayp == NULL || neltp == NULL) {
    472 		DBG0(DBG_ERR, "bad argument");
    473 		errno = EINVAL;
    474 		return (-1);
    475 	}
    476 
    477 	/*
    478 	 * Create an nvlist for our data.
    479 	 */
    480 
    481 	if ((rc = nvlist_alloc(&nvlp, NV_UNIQUE_NAME, 0)) != 0) {
    482 		DBG0(DBG_ERR, "failed to allocate nvlist\n");
    483 		nvlp = NULL;
    484 	}
    485 
    486 	if ((rc = nvlist_add_byte(nvlp, IPPCTL_OP,
    487 	    IPPCTL_OP_MOD_LIST_ACTIONS)) != 0) {
    488 		DBG1(DBG_ERR, "failed to add '%s' to nvlist\n", IPPCTL_OP);
    489 		goto failed;
    490 	}
    491 
    492 	if ((rc = nvlist_add_string(nvlp, IPPCTL_MODNAME,
    493 	    (char *)modname)) != 0) {
    494 		DBG1(DBG_ERR, "failed to add '%s' to nvlist\n", IPPCTL_MODNAME);
    495 		goto failed;
    496 	}
    497 
    498 	/*
    499 	 * Talk to the kernel.
    500 	 */
    501 
    502 	ad.name = IPPCTL_ANAME_ARRAY;
    503 	ad.array = NULL;
    504 	ad.nelt = 0;
    505 
    506 	if ((rc = dispatch(&nvlp, string_array_callback, (void *)&ad)) == 0) {
    507 		*aname_arrayp = ad.array;
    508 		*neltp = ad.nelt;
    509 	}
    510 
    511 	return (rc);
    512 failed:
    513 	if (nvlp != NULL)
    514 		nvlist_free(nvlp);
    515 	errno = rc;
    516 	return (-1);
    517 }
    518 #undef	__FN__
    519 
    520 #define	__FN__	"ipp_free"
    521 void
    522 ipp_free(
    523 	char	*buf)
    524 {
    525 	free(buf);
    526 }
    527 #undef	__FN__
    528 
    529 #define	__FN__	"ipp_free_array"
    530 void
    531 ipp_free_array(
    532 	char	**array,
    533 	int	nelt)
    534 {
    535 	int	i;
    536 
    537 	assert(array[nelt] == NULL);
    538 
    539 	for (i = 0; i < nelt; i++)
    540 		free(array[i]);
    541 
    542 	free(array);
    543 }
    544 #undef	__FN__
    545 
    546 #define	__FN__	"nvlist_callback"
    547 static int
    548 nvlist_callback(
    549 	nvlist_t	*nvlp,
    550 	void		*arg)
    551 {
    552 	nvlist_t	**nvlpp = (nvlist_t **)arg;
    553 	int		rc;
    554 
    555 	/*
    556 	 * Callback function used by ipp_action_create() and
    557 	 * ipp_action_modify()
    558 	 */
    559 
    560 	DBG0(DBG_IO, "called\n");
    561 
    562 	assert(nvlpp != NULL);
    563 	assert(*nvlpp == NULL);
    564 
    565 	/*
    566 	 * Duplicate the nvlist and set the given pointer to point at the new
    567 	 * copy.
    568 	 */
    569 
    570 	if ((rc = nvlist_dup(nvlp, nvlpp, 0)) != 0) {
    571 		DBG0(DBG_ERR, "failed to dup nvlist\n");
    572 		errno = rc;
    573 		return (-1);
    574 	}
    575 
    576 	return (0);
    577 }
    578 #undef	__FN__
    579 
    580 #define	__FN__	"string_callback"
    581 static int
    582 string_callback(
    583 	nvlist_t	*nvlp,
    584 	void		*arg)
    585 {
    586 	char		**namep = (char **)arg;
    587 	char		*name;
    588 	char		*ptr;
    589 	int		rc;
    590 
    591 	/*
    592 	 * Callback function used by ipp_action_mod()
    593 	 */
    594 
    595 	DBG0(DBG_IO, "called\n");
    596 
    597 	assert(namep != NULL);
    598 
    599 	/*
    600 	 * Look up the module name from the nvlist.
    601 	 */
    602 
    603 	if ((rc = nvlist_lookup_string(nvlp, IPPCTL_MODNAME, &ptr)) != 0) {
    604 		DBG0(DBG_ERR, "failed to find string\n");
    605 		errno = rc;
    606 		return (-1);
    607 	}
    608 
    609 	/*
    610 	 * Allocate a duplicate string.
    611 	 */
    612 
    613 	if ((name = strdup(ptr)) == NULL) {
    614 		DBG0(DBG_ERR, "failed to duplicate string\n");
    615 		return (-1);
    616 	}
    617 
    618 	/*
    619 	 * Set the given pointer to point at the string.
    620 	 */
    621 
    622 	*namep = name;
    623 	return (0);
    624 }
    625 #undef	__FN__
    626 
    627 #define	__FN__	"string_array_callback"
    628 static int
    629 string_array_callback(
    630 	nvlist_t	*nvlp,
    631 	void		*arg)
    632 {
    633 	array_desc_t	*adp = (array_desc_t *)arg;
    634 	char		**dst;
    635 	char		**src;
    636 	uint_t		nelt;
    637 	int		i;
    638 	int		rc;
    639 
    640 	/*
    641 	 * Callback function used by ipp_list_mods()
    642 	 */
    643 
    644 	DBG0(DBG_IO, "called\n");
    645 
    646 	assert(adp != NULL);
    647 
    648 	/*
    649 	 * Look up the module name from the nvlist.
    650 	 */
    651 
    652 	if ((rc = nvlist_lookup_string_array(nvlp, adp->name, &src,
    653 	    &nelt)) != 0) {
    654 		DBG0(DBG_ERR, "failed to find array\n");
    655 		errno = rc;
    656 		return (-1);
    657 	}
    658 
    659 	/*
    660 	 * Allocate an array.
    661 	 */
    662 
    663 	if ((dst = malloc((nelt + 1) * sizeof (char *))) == NULL) {
    664 		DBG0(DBG_ERR, "failed to allocate new array\n");
    665 		return (-1);
    666 	}
    667 
    668 	/*
    669 	 * For each string in the array, allocate a new buffer and copy
    670 	 * the string into it.
    671 	 */
    672 
    673 	for (i = 0; i < nelt; i++) {
    674 		if ((dst[i] = strdup(src[i])) == NULL) {
    675 			while (--i >= 0) {
    676 				free(dst[i]);
    677 			}
    678 			free(dst);
    679 			DBG0(DBG_ERR, "failed to duplicate array\n");
    680 			return (-1);
    681 		}
    682 	}
    683 	dst[nelt] = NULL;
    684 
    685 	/*
    686 	 * Set the information to be passed back.
    687 	 */
    688 
    689 	adp->array = dst;
    690 	adp->nelt = nelt;
    691 
    692 	return (0);
    693 }
    694 #undef	__FN__
    695 
    696 #define	__FN__	"dispatch"
    697 static int
    698 dispatch(
    699 	nvlist_t	**nvlpp,
    700 	int		(*fn)(nvlist_t *, void *),
    701 	void		*arg)
    702 {
    703 	char		*cbuf = NULL;
    704 	char		*dbuf = NULL;
    705 	size_t		cbuflen = 0;
    706 	size_t		dbuflen = 0;
    707 	size_t		thisbuflen = 0;
    708 	size_t		nextbuflen = 0;
    709 	int		rc;
    710 	ippctl_ioctl_t	iioc;
    711 	int		fd;
    712 	nvlist_t	*cnvlp;
    713 	nvlist_t	*dnvlp = NULL;
    714 	int		count;
    715 	int		rval;
    716 
    717 	/*
    718 	 * Sanity check the 'command' nvlist.
    719 	 */
    720 
    721 	cnvlp = *nvlpp;
    722 	if (cnvlp == NULL) {
    723 		rc = EINVAL;
    724 		return (-1);
    725 	}
    726 
    727 	/*
    728 	 * Pack the nvlist and then free the original.
    729 	 */
    730 
    731 	if ((rc = nvlist_pack(cnvlp, &cbuf, &cbuflen, NV_ENCODE_NATIVE,
    732 	    0)) != 0) {
    733 		DBG0(DBG_ERR, "failed to pack nvlist\n");
    734 		nvlist_free(cnvlp);
    735 		errno = rc;
    736 		return (-1);
    737 	}
    738 	nvlist_free(cnvlp);
    739 	*nvlpp = NULL;
    740 
    741 	/*
    742 	 * Open the control device node.
    743 	 */
    744 
    745 	DBG1(DBG_IO, "opening %s\n", IPPCTL_DEVICE);
    746 	if ((fd = open(IPPCTL_DEVICE, O_RDWR | O_NOCTTY)) == -1) {
    747 		DBG1(DBG_ERR, "failed to open %s\n", IPPCTL_DEVICE);
    748 		goto command_failed;
    749 	}
    750 
    751 	/*
    752 	 * Set up an ioctl structure to point at the packed nvlist.
    753 	 */
    754 
    755 	iioc.ii_buf = cbuf;
    756 	iioc.ii_buflen = cbuflen;
    757 
    758 	/*
    759 	 * Issue a command ioctl, passing the ioctl structure.
    760 	 */
    761 
    762 	DBG0(DBG_IO, "command\n");
    763 	if ((rc = ioctl(fd, IPPCTL_CMD, &iioc)) < 0) {
    764 		DBG0(DBG_ERR, "command ioctl failed\n");
    765 		goto command_failed;
    766 	}
    767 
    768 	/*
    769 	 * Get back the length of the first data buffer.
    770 	 */
    771 
    772 	if ((nextbuflen = (size_t)rc) == 0) {
    773 		DBG0(DBG_ERR, "no data buffer\n");
    774 		errno = EPROTO;
    775 		goto command_failed;
    776 	}
    777 
    778 	/*
    779 	 * Try to re-use the command buffer as the first data buffer.
    780 	 */
    781 
    782 	dbuf = cbuf;
    783 	thisbuflen = cbuflen;
    784 
    785 	count = 0;
    786 	while (nextbuflen != 0) {
    787 		dbuflen = nextbuflen;
    788 
    789 		/*
    790 		 * Check whether the buffer we have is long enough for the
    791 		 * next lot of data. If it isn't, allocate a new one of
    792 		 * the appropriate length.
    793 		 */
    794 
    795 		if (nextbuflen > thisbuflen) {
    796 			if ((dbuf = realloc(dbuf, nextbuflen)) == NULL) {
    797 				DBG0(DBG_ERR,
    798 				    "failed to allocate data buffer\n");
    799 				goto data_failed;
    800 			}
    801 			thisbuflen = nextbuflen;
    802 		}
    803 
    804 		/*
    805 		 * Set up an ioctl structure to point at the data buffer.
    806 		 */
    807 
    808 		iioc.ii_buf = dbuf;
    809 		iioc.ii_buflen = dbuflen;
    810 
    811 		/*
    812 		 * Issue a data ioctl, passing the ioctl structure.
    813 		 */
    814 
    815 		DBG2(DBG_IO, "data[%d]: length = %d\n", count, dbuflen);
    816 		if ((rc = ioctl(fd, IPPCTL_DATA, &iioc)) < 0) {
    817 			DBG0(DBG_ERR, "data ioctl failed\n");
    818 			goto data_failed;
    819 		}
    820 
    821 		/*
    822 		 * Get the length of the *next* data buffer, if there is
    823 		 * one.
    824 		 */
    825 
    826 		nextbuflen = (size_t)rc;
    827 		DBG1(DBG_IO, "nextbuflen = %d\n", nextbuflen);
    828 
    829 		/*
    830 		 * Unpack the nvlist that the current data buffer should
    831 		 * now contain.
    832 		 */
    833 
    834 		if ((rc = nvlist_unpack(dbuf, dbuflen, &dnvlp, 0)) != 0) {
    835 			DBG0(DBG_ERR, "failed to unpack nvlist\n");
    836 			errno = rc;
    837 			goto data_failed;
    838 		}
    839 
    840 		/*
    841 		 * The first data buffer should contain the kernel function's
    842 		 * return code. Subsequent buffers contain nvlists which
    843 		 * should be passed to the given callback function.
    844 		 */
    845 
    846 		if (count == 0) {
    847 			if ((rc = nvlist_lookup_int32(dnvlp, IPPCTL_RC,
    848 			    &rval)) != 0) {
    849 				DBG0(DBG_ERR, "failed to find return code\n");
    850 				nvlist_free(dnvlp);
    851 				errno = rc;
    852 				goto data_failed;
    853 			}
    854 		} else {
    855 			if (fn != NULL)
    856 				if (fn(dnvlp, arg) != 0) {
    857 
    858 					/*
    859 					 * The callback function returned
    860 					 * a non-zero value. Abort any further
    861 					 * data collection.
    862 					 */
    863 
    864 					nvlist_free(dnvlp);
    865 					free(dbuf);
    866 				}
    867 		}
    868 
    869 		/*
    870 		 * Free the nvlist now that we have extracted the return
    871 		 * code or called the callback function.
    872 		 */
    873 
    874 		nvlist_free(dnvlp);
    875 		dnvlp = NULL;
    876 
    877 		count++;
    878 	}
    879 
    880 	/*
    881 	 * Free the data buffer as data collection is now complete.
    882 	 */
    883 
    884 	free(dbuf);
    885 
    886 	/*
    887 	 * Close the control device.
    888 	 */
    889 
    890 	(void) close(fd);
    891 
    892 	/*
    893 	 * If the kernel returned an error, we should return an error.
    894 	 * and set errno.
    895 	 */
    896 
    897 	if (rval != 0) {
    898 		DBG1(DBG_IO, "kernel return code = %d\n", rval);
    899 		errno = rval;
    900 		return (-1);
    901 	}
    902 
    903 	return (0);
    904 
    905 command_failed:
    906 	free(cbuf);
    907 	if (fd != -1)
    908 		(void) close(fd);
    909 	return (-1);
    910 
    911 data_failed:
    912 	if (dbuf != NULL)
    913 		free(dbuf);
    914 	(void) close(fd);
    915 	return (-1);
    916 }
    917 #undef	__FN__
    918