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)
48uint32_t	ipp_debug_flags =
49/*
50 * DBG_IO |
51 */
52DBG_ERR |
530;
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
98typedef	struct array_desc_t {
99	char	*name;
100	char	**array;
101	int	nelt;
102} array_desc_t;
103
104/*
105 * Prototypes
106 */
107
108static int	nvlist_callback(nvlist_t *, void *);
109static int	string_callback(nvlist_t *, void *);
110static int	string_array_callback(nvlist_t *, void *);
111static int	dispatch(nvlist_t **, int (*)(nvlist_t *, void *), void *);
112
113/*
114 * API functions
115 */
116#define	__FN__	"ipp_action_create"
117int
118ipp_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));
171failed:
172	errno = rc;
173	return (-1);
174}
175#undef	__FN__
176
177#define	__FN__	"ipp_action_destroy"
178int
179ipp_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));
227failed:
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"
236int
237ipp_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));
281failed:
282	errno = rc;
283	return (-1);
284}
285#undef	__FN__
286
287#define	__FN__	"ipp_action_info"
288int
289ipp_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));
338failed:
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"
347int
348ipp_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));
391failed:
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"
400int
401ipp_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);
448failed:
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"
457int
458ipp_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);
512failed:
513	if (nvlp != NULL)
514		nvlist_free(nvlp);
515	errno = rc;
516	return (-1);
517}
518#undef	__FN__
519
520#define	__FN__	"ipp_free"
521void
522ipp_free(
523	char	*buf)
524{
525	free(buf);
526}
527#undef	__FN__
528
529#define	__FN__	"ipp_free_array"
530void
531ipp_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"
547static int
548nvlist_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"
581static int
582string_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"
628static int
629string_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"
697static int
698dispatch(
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
905command_failed:
906	free(cbuf);
907	if (fd != -1)
908		(void) close(fd);
909	return (-1);
910
911data_failed:
912	if (dbuf != NULL)
913		free(dbuf);
914	(void) close(fd);
915	return (-1);
916}
917#undef	__FN__
918