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