| /* |
| * 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. |
| */ |
| |
| #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: |
| 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: |
| 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: |
| 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: |
| 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: |
| 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__ |