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