freebsd-dev/sys/contrib/libnv/nvlist.c
Mariusz Zaborski 347a39b4a6 Add support for the arrays in nvlist library.
- Add
  nvlist_{add,get,take,move,exists,free}_{number,bool,string,nvlist,
  descriptor} functions.
- Add support for (un)packing arrays.
- Add the nvl_array_next field to the nvlist structure.
  If an array is added by the nvlist_{move,add}_nvlist_array function
  this field will contains next element in the array.
- Add the nitems field to the nvpair and nvpair_header structure.
  This field contains number of elements in the array.
- Add special flag (NV_FLAG_IN_ARRAY) which is set if nvlist is a part of
  an array.
- Add special type (NV_TYPE_NVLIST_ARRAY_NEXT).This type is used only
  on packing/unpacking.
- Add new API for traversing arrays (nvlist_get_array_next).
- Add the nvlist_get_pararr function which combines the
  nvlist_get_array_next and nvlist_get_parent functions. If nvlist is in
  the array it will return next element from array. If nvlist is last
  element in array or it isn't in array it will return his
  container (parent). This function should simplify traveling over nvlist.
- Add tests for new features.
- Add documentation for new functions.
- Add my copyright.
- Regenerate the sys/cddl/compat/opensolaris/sys/nvpair.h file.

PR:		191083
Reviewed by:	allanjude (doc)
Approved by:	pjd (mentor)
2015-08-15 06:34:49 +00:00

2025 lines
42 KiB
C

/*-
* Copyright (c) 2009-2013 The FreeBSD Foundation
* Copyright (c) 2013-2015 Mariusz Zaborski <oshogbo@FreeBSD.org>
* All rights reserved.
*
* This software was developed by Pawel Jakub Dawidek under sponsorship from
* the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/endian.h>
#include <sys/queue.h>
#ifdef _KERNEL
#include <sys/errno.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/systm.h>
#include <machine/stdarg.h>
#else
#include <sys/socket.h>
#include <errno.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#define _WITH_DPRINTF
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "msgio.h"
#endif
#ifdef HAVE_PJDLOG
#include <pjdlog.h>
#endif
#include <sys/nv.h>
#include "nv_impl.h"
#include "nvlist_impl.h"
#include "nvpair_impl.h"
#ifndef HAVE_PJDLOG
#ifdef _KERNEL
#define PJDLOG_ASSERT(...) MPASS(__VA_ARGS__)
#define PJDLOG_RASSERT(expr, ...) KASSERT(expr, (__VA_ARGS__))
#define PJDLOG_ABORT(...) panic(__VA_ARGS__)
#else
#include <assert.h>
#define PJDLOG_ASSERT(...) assert(__VA_ARGS__)
#define PJDLOG_RASSERT(expr, ...) assert(expr)
#define PJDLOG_ABORT(...) do { \
fprintf(stderr, "%s:%u: ", __FILE__, __LINE__); \
fprintf(stderr, __VA_ARGS__); \
fprintf(stderr, "\n"); \
abort(); \
} while (0)
#endif
#endif
#define NV_FLAG_PRIVATE_MASK (NV_FLAG_BIG_ENDIAN | NV_FLAG_IN_ARRAY)
#define NV_FLAG_PUBLIC_MASK (NV_FLAG_IGNORE_CASE | NV_FLAG_NO_UNIQUE)
#define NV_FLAG_ALL_MASK (NV_FLAG_PRIVATE_MASK | NV_FLAG_PUBLIC_MASK)
#define NVLIST_MAGIC 0x6e766c /* "nvl" */
struct nvlist {
int nvl_magic;
int nvl_error;
int nvl_flags;
nvpair_t *nvl_parent;
nvpair_t *nvl_array_next;
struct nvl_head nvl_head;
};
#define NVLIST_ASSERT(nvl) do { \
PJDLOG_ASSERT((nvl) != NULL); \
PJDLOG_ASSERT((nvl)->nvl_magic == NVLIST_MAGIC); \
} while (0)
#ifdef _KERNEL
MALLOC_DEFINE(M_NVLIST, "nvlist", "kernel nvlist");
#endif
#define NVPAIR_ASSERT(nvp) nvpair_assert(nvp)
#define NVLIST_HEADER_MAGIC 0x6c
#define NVLIST_HEADER_VERSION 0x00
struct nvlist_header {
uint8_t nvlh_magic;
uint8_t nvlh_version;
uint8_t nvlh_flags;
uint64_t nvlh_descriptors;
uint64_t nvlh_size;
} __packed;
nvlist_t *
nvlist_create(int flags)
{
nvlist_t *nvl;
PJDLOG_ASSERT((flags & ~(NV_FLAG_PUBLIC_MASK)) == 0);
nvl = nv_malloc(sizeof(*nvl));
if (nvl == NULL)
return (NULL);
nvl->nvl_error = 0;
nvl->nvl_flags = flags;
nvl->nvl_parent = NULL;
nvl->nvl_array_next = NULL;
TAILQ_INIT(&nvl->nvl_head);
nvl->nvl_magic = NVLIST_MAGIC;
return (nvl);
}
void
nvlist_destroy(nvlist_t *nvl)
{
nvpair_t *nvp;
if (nvl == NULL)
return;
ERRNO_SAVE();
NVLIST_ASSERT(nvl);
while ((nvp = nvlist_first_nvpair(nvl)) != NULL) {
nvlist_remove_nvpair(nvl, nvp);
nvpair_free(nvp);
}
if (nvl->nvl_array_next != NULL)
nvpair_free_structure(nvl->nvl_array_next);
nvl->nvl_array_next = NULL;
nvl->nvl_parent = NULL;
nvl->nvl_magic = 0;
nv_free(nvl);
ERRNO_RESTORE();
}
void
nvlist_set_error(nvlist_t *nvl, int error)
{
PJDLOG_ASSERT(error != 0);
/*
* Check for error != 0 so that we don't do the wrong thing if somebody
* tries to abuse this API when asserts are disabled.
*/
if (nvl != NULL && error != 0 && nvl->nvl_error == 0)
nvl->nvl_error = error;
}
int
nvlist_error(const nvlist_t *nvl)
{
if (nvl == NULL)
return (ENOMEM);
NVLIST_ASSERT(nvl);
return (nvl->nvl_error);
}
nvpair_t *
nvlist_get_nvpair_parent(const nvlist_t *nvl)
{
NVLIST_ASSERT(nvl);
return (nvl->nvl_parent);
}
const nvlist_t *
nvlist_get_parent(const nvlist_t *nvl, void **cookiep)
{
nvpair_t *nvp;
NVLIST_ASSERT(nvl);
nvp = nvl->nvl_parent;
if (cookiep != NULL)
*cookiep = nvp;
if (nvp == NULL)
return (NULL);
return (nvpair_nvlist(nvp));
}
void
nvlist_set_parent(nvlist_t *nvl, nvpair_t *parent)
{
NVLIST_ASSERT(nvl);
nvl->nvl_parent = parent;
}
void
nvlist_set_array_next(nvlist_t *nvl, nvpair_t *ele)
{
NVLIST_ASSERT(nvl);
if (ele != NULL)
nvl->nvl_flags |= NV_FLAG_IN_ARRAY;
else
nvl->nvl_flags &= ~NV_FLAG_IN_ARRAY;
nvl->nvl_array_next = ele;
}
bool
nvlist_in_array(const nvlist_t *nvl)
{
NVLIST_ASSERT(nvl);
return ((nvl->nvl_flags & NV_FLAG_IN_ARRAY) != 0);
}
const nvlist_t *
nvlist_get_array_next(const nvlist_t *nvl)
{
nvpair_t *nvp;
NVLIST_ASSERT(nvl);
nvp = nvl->nvl_array_next;
if (nvp == NULL)
return (NULL);
return (nvpair_get_nvlist(nvp));
}
const nvlist_t *
nvlist_get_pararr(const nvlist_t *nvl, void **cookiep)
{
const nvlist_t *ret;
ret = nvlist_get_array_next(nvl);
if (ret != NULL) {
if (cookiep != NULL)
*cookiep = NULL;
return (ret);
}
ret = nvlist_get_parent(nvl, cookiep);
return (ret);
}
bool
nvlist_empty(const nvlist_t *nvl)
{
NVLIST_ASSERT(nvl);
PJDLOG_ASSERT(nvl->nvl_error == 0);
return (nvlist_first_nvpair(nvl) == NULL);
}
int
nvlist_flags(const nvlist_t *nvl)
{
NVLIST_ASSERT(nvl);
PJDLOG_ASSERT(nvl->nvl_error == 0);
return (nvl->nvl_flags & NV_FLAG_PUBLIC_MASK);
}
void
nvlist_set_flags(nvlist_t *nvl, int flags)
{
NVLIST_ASSERT(nvl);
PJDLOG_ASSERT(nvl->nvl_error == 0);
nvl->nvl_flags = flags;
}
static void
nvlist_report_missing(int type, const char *name)
{
PJDLOG_ABORT("Element '%s' of type %s doesn't exist.",
name, nvpair_type_string(type));
}
static nvpair_t *
nvlist_find(const nvlist_t *nvl, int type, const char *name)
{
nvpair_t *nvp;
NVLIST_ASSERT(nvl);
PJDLOG_ASSERT(nvl->nvl_error == 0);
PJDLOG_ASSERT(type == NV_TYPE_NONE ||
(type >= NV_TYPE_FIRST && type <= NV_TYPE_LAST));
for (nvp = nvlist_first_nvpair(nvl); nvp != NULL;
nvp = nvlist_next_nvpair(nvl, nvp)) {
if (type != NV_TYPE_NONE && nvpair_type(nvp) != type)
continue;
if ((nvl->nvl_flags & NV_FLAG_IGNORE_CASE) != 0) {
if (strcasecmp(nvpair_name(nvp), name) != 0)
continue;
} else {
if (strcmp(nvpair_name(nvp), name) != 0)
continue;
}
break;
}
if (nvp == NULL)
ERRNO_SET(ENOENT);
return (nvp);
}
bool
nvlist_exists_type(const nvlist_t *nvl, const char *name, int type)
{
NVLIST_ASSERT(nvl);
PJDLOG_ASSERT(nvl->nvl_error == 0);
PJDLOG_ASSERT(type == NV_TYPE_NONE ||
(type >= NV_TYPE_FIRST && type <= NV_TYPE_LAST));
return (nvlist_find(nvl, type, name) != NULL);
}
void
nvlist_free_type(nvlist_t *nvl, const char *name, int type)
{
nvpair_t *nvp;
NVLIST_ASSERT(nvl);
PJDLOG_ASSERT(nvl->nvl_error == 0);
PJDLOG_ASSERT(type == NV_TYPE_NONE ||
(type >= NV_TYPE_FIRST && type <= NV_TYPE_LAST));
nvp = nvlist_find(nvl, type, name);
if (nvp != NULL)
nvlist_free_nvpair(nvl, nvp);
else
nvlist_report_missing(type, name);
}
nvlist_t *
nvlist_clone(const nvlist_t *nvl)
{
nvlist_t *newnvl;
nvpair_t *nvp, *newnvp;
NVLIST_ASSERT(nvl);
if (nvl->nvl_error != 0) {
ERRNO_SET(nvl->nvl_error);
return (NULL);
}
newnvl = nvlist_create(nvl->nvl_flags & NV_FLAG_PUBLIC_MASK);
for (nvp = nvlist_first_nvpair(nvl); nvp != NULL;
nvp = nvlist_next_nvpair(nvl, nvp)) {
newnvp = nvpair_clone(nvp);
if (newnvp == NULL)
break;
(void)nvlist_move_nvpair(newnvl, newnvp);
}
if (nvp != NULL) {
nvlist_destroy(newnvl);
return (NULL);
}
return (newnvl);
}
#ifndef _KERNEL
static bool
nvlist_dump_error_check(const nvlist_t *nvl, int fd, int level)
{
if (nvlist_error(nvl) != 0) {
dprintf(fd, "%*serror: %d\n", level * 4, "",
nvlist_error(nvl));
return (true);
}
return (false);
}
/*
* Dump content of nvlist.
*/
void
nvlist_dump(const nvlist_t *nvl, int fd)
{
const nvlist_t *tmpnvl;
nvpair_t *nvp, *tmpnvp;
void *cookie;
int level;
level = 0;
if (nvlist_dump_error_check(nvl, fd, level))
return;
nvp = nvlist_first_nvpair(nvl);
while (nvp != NULL) {
dprintf(fd, "%*s%s (%s):", level * 4, "", nvpair_name(nvp),
nvpair_type_string(nvpair_type(nvp)));
switch (nvpair_type(nvp)) {
case NV_TYPE_NULL:
dprintf(fd, " null\n");
break;
case NV_TYPE_BOOL:
dprintf(fd, " %s\n", nvpair_get_bool(nvp) ?
"TRUE" : "FALSE");
break;
case NV_TYPE_NUMBER:
dprintf(fd, " %ju (%jd) (0x%jx)\n",
(uintmax_t)nvpair_get_number(nvp),
(intmax_t)nvpair_get_number(nvp),
(uintmax_t)nvpair_get_number(nvp));
break;
case NV_TYPE_STRING:
dprintf(fd, " [%s]\n", nvpair_get_string(nvp));
break;
case NV_TYPE_NVLIST:
dprintf(fd, "\n");
tmpnvl = nvpair_get_nvlist(nvp);
if (nvlist_dump_error_check(tmpnvl, fd, level + 1))
break;
tmpnvp = nvlist_first_nvpair(tmpnvl);
if (tmpnvp != NULL) {
nvl = tmpnvl;
nvp = tmpnvp;
level++;
continue;
}
break;
case NV_TYPE_DESCRIPTOR:
dprintf(fd, " %d\n", nvpair_get_descriptor(nvp));
break;
case NV_TYPE_BINARY:
{
const unsigned char *binary;
unsigned int ii;
size_t size;
binary = nvpair_get_binary(nvp, &size);
dprintf(fd, " %zu ", size);
for (ii = 0; ii < size; ii++)
dprintf(fd, "%02hhx", binary[ii]);
dprintf(fd, "\n");
break;
}
case NV_TYPE_BOOL_ARRAY:
{
const bool *value;
unsigned int ii;
size_t nitems;
value = nvpair_get_bool_array(nvp, &nitems);
dprintf(fd, " [ ");
for (ii = 0; ii < nitems; ii++) {
dprintf(fd, "%s", value[ii] ? "TRUE" : "FALSE");
if (ii != nitems - 1)
dprintf(fd, ", ");
}
dprintf(fd, " ]\n");
break;
}
case NV_TYPE_STRING_ARRAY:
{
const char * const *value;
unsigned int ii;
size_t nitems;
value = nvpair_get_string_array(nvp, &nitems);
dprintf(fd, " [ ");
for (ii = 0; ii < nitems; ii++) {
if (value[ii] == NULL)
dprintf(fd, "NULL");
else
dprintf(fd, "\"%s\"", value[ii]);
if (ii != nitems - 1)
dprintf(fd, ", ");
}
dprintf(fd, " ]\n");
break;
}
case NV_TYPE_NUMBER_ARRAY:
{
const uint64_t *value;
unsigned int ii;
size_t nitems;
value = nvpair_get_number_array(nvp, &nitems);
dprintf(fd, " [ ");
for (ii = 0; ii < nitems; ii++) {
dprintf(fd, "%ju (%jd) (0x%jx)",
value[ii], value[ii], value[ii]);
if (ii != nitems - 1)
dprintf(fd, ", ");
}
dprintf(fd, " ]\n");
break;
}
case NV_TYPE_DESCRIPTOR_ARRAY:
{
const int *value;
unsigned int ii;
size_t nitems;
value = nvpair_get_descriptor_array(nvp, &nitems);
dprintf(fd, " [ ");
for (ii = 0; ii < nitems; ii++) {
dprintf(fd, "%d", value[ii]);
if (ii != nitems - 1)
dprintf(fd, ", ");
}
dprintf(fd, " ]\n");
break;
}
case NV_TYPE_NVLIST_ARRAY:
{
const nvlist_t * const *value;
unsigned int ii;
size_t nitems;
value = nvpair_get_nvlist_array(nvp, &nitems);
dprintf(fd, " %zu\n", nitems);
tmpnvl = NULL;
tmpnvp = NULL;
for (ii = 0; ii < nitems; ii++) {
if (nvlist_dump_error_check(value[ii], fd,
level + 1)) {
break;
}
if (tmpnvl == NULL) {
tmpnvp = nvlist_first_nvpair(value[ii]);
if (tmpnvp != NULL) {
tmpnvl = value[ii];
} else {
dprintf(fd, "%*s,\n",
(level + 1) * 4, "");
}
}
}
if (tmpnvp != NULL) {
nvl = tmpnvl;
nvp = tmpnvp;
level++;
continue;
}
break;
}
default:
PJDLOG_ABORT("Unknown type: %d.", nvpair_type(nvp));
}
while ((nvp = nvlist_next_nvpair(nvl, nvp)) == NULL) {
do {
cookie = NULL;
if (nvlist_in_array(nvl))
dprintf(fd, "%*s,\n", level * 4, "");
nvl = nvlist_get_pararr(nvl, &cookie);
if (nvl == NULL)
return;
if (nvlist_in_array(nvl) && cookie == NULL) {
nvp = nvlist_first_nvpair(nvl);
} else {
nvp = cookie;
level--;
}
} while (nvp == NULL);
if (nvlist_in_array(nvl) && cookie == NULL)
break;
}
}
}
void
nvlist_fdump(const nvlist_t *nvl, FILE *fp)
{
fflush(fp);
nvlist_dump(nvl, fileno(fp));
}
#endif
/*
* The function obtains size of the nvlist after nvlist_pack().
*/
size_t
nvlist_size(const nvlist_t *nvl)
{
const nvlist_t *tmpnvl;
const nvlist_t * const *nvlarray;
const nvpair_t *nvp, *tmpnvp;
void *cookie;
size_t size, nitems;
unsigned int ii;
NVLIST_ASSERT(nvl);
PJDLOG_ASSERT(nvl->nvl_error == 0);
size = sizeof(struct nvlist_header);
nvp = nvlist_first_nvpair(nvl);
while (nvp != NULL) {
size += nvpair_header_size();
size += strlen(nvpair_name(nvp)) + 1;
if (nvpair_type(nvp) == NV_TYPE_NVLIST) {
size += sizeof(struct nvlist_header);
size += nvpair_header_size() + 1;
tmpnvl = nvpair_get_nvlist(nvp);
PJDLOG_ASSERT(tmpnvl->nvl_error == 0);
tmpnvp = nvlist_first_nvpair(tmpnvl);
if (tmpnvp != NULL) {
nvl = tmpnvl;
nvp = tmpnvp;
continue;
}
} else if (nvpair_type(nvp) == NV_TYPE_NVLIST_ARRAY) {
nvlarray = nvpair_get_nvlist_array(nvp, &nitems);
PJDLOG_ASSERT(nitems > 0);
size += (nvpair_header_size() + 1) * nitems;
size += sizeof(struct nvlist_header) * nitems;
tmpnvl = NULL;
tmpnvp = NULL;
for (ii = 0; ii < nitems; ii++) {
PJDLOG_ASSERT(nvlarray[ii]->nvl_error == 0);
tmpnvp = nvlist_first_nvpair(nvlarray[ii]);
if (tmpnvp != NULL) {
tmpnvl = nvlarray[ii];
break;
}
}
if (tmpnvp != NULL) {
nvp = tmpnvp;
nvl = tmpnvl;
continue;
}
} else {
size += nvpair_size(nvp);
}
while ((nvp = nvlist_next_nvpair(nvl, nvp)) == NULL) {
do {
cookie = NULL;
nvl = nvlist_get_pararr(nvl, &cookie);
if (nvl == NULL)
goto out;
if (nvlist_in_array(nvl) && cookie == NULL) {
nvp = nvlist_first_nvpair(nvl);
} else {
nvp = cookie;
}
} while (nvp == NULL);
if (nvlist_in_array(nvl) && cookie == NULL)
break;
}
}
out:
return (size);
}
#ifndef _KERNEL
static int *
nvlist_xdescriptors(const nvlist_t *nvl, int *descs)
{
nvpair_t *nvp;
const char *name;
int type;
NVLIST_ASSERT(nvl);
PJDLOG_ASSERT(nvl->nvl_error == 0);
nvp = NULL;
do {
while ((name = nvlist_next(nvl, &type, (void**)&nvp)) != NULL) {
switch (type) {
case NV_TYPE_DESCRIPTOR:
*descs = nvpair_get_descriptor(nvp);
descs++;
break;
case NV_TYPE_DESCRIPTOR_ARRAY:
{
const int *value;
size_t nitems;
unsigned int ii;
value = nvpair_get_descriptor_array(nvp,
&nitems);
for (ii = 0; ii < nitems; ii++) {
*descs = value[ii];
descs++;
}
break;
}
case NV_TYPE_NVLIST:
nvl = nvpair_get_nvlist(nvp);
nvp = NULL;
break;
case NV_TYPE_NVLIST_ARRAY:
{
const nvlist_t * const *value;
size_t nitems;
value = nvpair_get_nvlist_array(nvp, &nitems);
PJDLOG_ASSERT(value != NULL);
PJDLOG_ASSERT(nitems > 0);
nvl = value[0];
nvp = NULL;
break;
}
}
}
} while ((nvl = nvlist_get_pararr(nvl, (void**)&nvp)) != NULL);
return (descs);
}
#endif
#ifndef _KERNEL
int *
nvlist_descriptors(const nvlist_t *nvl, size_t *nitemsp)
{
size_t nitems;
int *fds;
nitems = nvlist_ndescriptors(nvl);
fds = nv_malloc(sizeof(fds[0]) * (nitems + 1));
if (fds == NULL)
return (NULL);
if (nitems > 0)
nvlist_xdescriptors(nvl, fds);
fds[nitems] = -1;
if (nitemsp != NULL)
*nitemsp = nitems;
return (fds);
}
#endif
size_t
nvlist_ndescriptors(const nvlist_t *nvl)
{
#ifndef _KERNEL
nvpair_t *nvp;
const char *name;
size_t ndescs;
int type;
NVLIST_ASSERT(nvl);
PJDLOG_ASSERT(nvl->nvl_error == 0);
ndescs = 0;
nvp = NULL;
do {
while ((name = nvlist_next(nvl, &type, (void**)&nvp)) != NULL) {
switch (type) {
case NV_TYPE_DESCRIPTOR:
ndescs++;
break;
case NV_TYPE_NVLIST:
nvl = nvpair_get_nvlist(nvp);
nvp = NULL;
break;
case NV_TYPE_NVLIST_ARRAY:
{
const nvlist_t * const *value;
size_t nitems;
value = nvpair_get_nvlist_array(nvp, &nitems);
PJDLOG_ASSERT(value != NULL);
PJDLOG_ASSERT(nitems > 0);
nvl = value[0];
nvp = NULL;
break;
}
case NV_TYPE_DESCRIPTOR_ARRAY:
{
size_t nitems;
(void)nvpair_get_descriptor_array(nvp,
&nitems);
ndescs += nitems;
break;
}
}
}
} while ((nvl = nvlist_get_pararr(nvl, (void**)&nvp)) != NULL);
return (ndescs);
#else
return (0);
#endif
}
static unsigned char *
nvlist_pack_header(const nvlist_t *nvl, unsigned char *ptr, size_t *leftp)
{
struct nvlist_header nvlhdr;
NVLIST_ASSERT(nvl);
nvlhdr.nvlh_magic = NVLIST_HEADER_MAGIC;
nvlhdr.nvlh_version = NVLIST_HEADER_VERSION;
nvlhdr.nvlh_flags = nvl->nvl_flags;
#if BYTE_ORDER == BIG_ENDIAN
nvlhdr.nvlh_flags |= NV_FLAG_BIG_ENDIAN;
#endif
nvlhdr.nvlh_descriptors = nvlist_ndescriptors(nvl);
nvlhdr.nvlh_size = *leftp - sizeof(nvlhdr);
PJDLOG_ASSERT(*leftp >= sizeof(nvlhdr));
memcpy(ptr, &nvlhdr, sizeof(nvlhdr));
ptr += sizeof(nvlhdr);
*leftp -= sizeof(nvlhdr);
return (ptr);
}
static void *
nvlist_xpack(const nvlist_t *nvl, int64_t *fdidxp, size_t *sizep)
{
unsigned char *buf, *ptr;
size_t left, size;
const nvlist_t *tmpnvl;
nvpair_t *nvp, *tmpnvp;
void *cookie;
NVLIST_ASSERT(nvl);
if (nvl->nvl_error != 0) {
ERRNO_SET(nvl->nvl_error);
return (NULL);
}
size = nvlist_size(nvl);
buf = nv_malloc(size);
if (buf == NULL)
return (NULL);
ptr = buf;
left = size;
ptr = nvlist_pack_header(nvl, ptr, &left);
nvp = nvlist_first_nvpair(nvl);
while (nvp != NULL) {
NVPAIR_ASSERT(nvp);
nvpair_init_datasize(nvp);
ptr = nvpair_pack_header(nvp, ptr, &left);
if (ptr == NULL)
goto fail;
switch (nvpair_type(nvp)) {
case NV_TYPE_NULL:
ptr = nvpair_pack_null(nvp, ptr, &left);
break;
case NV_TYPE_BOOL:
ptr = nvpair_pack_bool(nvp, ptr, &left);
break;
case NV_TYPE_NUMBER:
ptr = nvpair_pack_number(nvp, ptr, &left);
break;
case NV_TYPE_STRING:
ptr = nvpair_pack_string(nvp, ptr, &left);
break;
case NV_TYPE_NVLIST:
tmpnvl = nvpair_get_nvlist(nvp);
ptr = nvlist_pack_header(tmpnvl, ptr, &left);
if (ptr == NULL)
goto fail;
tmpnvp = nvlist_first_nvpair(tmpnvl);
if (tmpnvp != NULL) {
nvl = tmpnvl;
nvp = tmpnvp;
continue;
}
ptr = nvpair_pack_nvlist_up(ptr, &left);
break;
#ifndef _KERNEL
case NV_TYPE_DESCRIPTOR:
ptr = nvpair_pack_descriptor(nvp, ptr, fdidxp, &left);
break;
case NV_TYPE_DESCRIPTOR_ARRAY:
ptr = nvpair_pack_descriptor_array(nvp, ptr, fdidxp,
&left);
break;
#endif
case NV_TYPE_BINARY:
ptr = nvpair_pack_binary(nvp, ptr, &left);
break;
case NV_TYPE_BOOL_ARRAY:
ptr = nvpair_pack_bool_array(nvp, ptr, &left);
break;
case NV_TYPE_NUMBER_ARRAY:
ptr = nvpair_pack_number_array(nvp, ptr, &left);
break;
case NV_TYPE_STRING_ARRAY:
ptr = nvpair_pack_string_array(nvp, ptr, &left);
break;
case NV_TYPE_NVLIST_ARRAY:
{
const nvlist_t * const * value;
size_t nitems;
unsigned int ii;
tmpnvl = NULL;
value = nvpair_get_nvlist_array(nvp, &nitems);
for (ii = 0; ii < nitems; ii++) {
ptr = nvlist_pack_header(value[ii], ptr, &left);
if (ptr == NULL)
goto out;
tmpnvp = nvlist_first_nvpair(value[ii]);
if (tmpnvp != NULL) {
tmpnvl = value[ii];
break;
}
ptr = nvpair_pack_nvlist_array_next(ptr, &left);
if (ptr == NULL)
goto out;
}
if (tmpnvl != NULL) {
nvl = tmpnvl;
nvp = tmpnvp;
continue;
}
break;
}
default:
PJDLOG_ABORT("Invalid type (%d).", nvpair_type(nvp));
}
if (ptr == NULL)
goto fail;
while ((nvp = nvlist_next_nvpair(nvl, nvp)) == NULL) {
do {
cookie = NULL;
if (nvlist_in_array(nvl)) {
ptr = nvpair_pack_nvlist_array_next(ptr,
&left);
if (ptr == NULL)
goto fail;
}
nvl = nvlist_get_pararr(nvl, &cookie);
if (nvl == NULL)
goto out;
if (nvlist_in_array(nvl) && cookie == NULL) {
nvp = nvlist_first_nvpair(nvl);
ptr = nvlist_pack_header(nvl, ptr,
&left);
if (ptr == NULL)
goto fail;
} else if (nvpair_type((nvpair_t *)cookie) !=
NV_TYPE_NVLIST_ARRAY) {
ptr = nvpair_pack_nvlist_up(ptr, &left);
if (ptr == NULL)
goto fail;
nvp = cookie;
} else {
nvp = cookie;
}
} while (nvp == NULL);
if (nvlist_in_array(nvl) && cookie == NULL)
break;
}
}
out:
if (sizep != NULL)
*sizep = size;
return (buf);
fail:
nv_free(buf);
return (NULL);
}
void *
nvlist_pack(const nvlist_t *nvl, size_t *sizep)
{
NVLIST_ASSERT(nvl);
if (nvl->nvl_error != 0) {
ERRNO_SET(nvl->nvl_error);
return (NULL);
}
if (nvlist_ndescriptors(nvl) > 0) {
ERRNO_SET(EOPNOTSUPP);
return (NULL);
}
return (nvlist_xpack(nvl, NULL, sizep));
}
static bool
nvlist_check_header(struct nvlist_header *nvlhdrp)
{
if (nvlhdrp->nvlh_magic != NVLIST_HEADER_MAGIC) {
ERRNO_SET(EINVAL);
return (false);
}
if ((nvlhdrp->nvlh_flags & ~NV_FLAG_ALL_MASK) != 0) {
ERRNO_SET(EINVAL);
return (false);
}
#if BYTE_ORDER == BIG_ENDIAN
if ((nvlhdrp->nvlh_flags & NV_FLAG_BIG_ENDIAN) == 0) {
nvlhdrp->nvlh_size = le64toh(nvlhdrp->nvlh_size);
nvlhdrp->nvlh_descriptors = le64toh(nvlhdrp->nvlh_descriptors);
}
#else
if ((nvlhdrp->nvlh_flags & NV_FLAG_BIG_ENDIAN) != 0) {
nvlhdrp->nvlh_size = be64toh(nvlhdrp->nvlh_size);
nvlhdrp->nvlh_descriptors = be64toh(nvlhdrp->nvlh_descriptors);
}
#endif
return (true);
}
const unsigned char *
nvlist_unpack_header(nvlist_t *nvl, const unsigned char *ptr, size_t nfds,
bool *isbep, size_t *leftp)
{
struct nvlist_header nvlhdr;
int inarrayf;
if (*leftp < sizeof(nvlhdr))
goto failed;
memcpy(&nvlhdr, ptr, sizeof(nvlhdr));
if (!nvlist_check_header(&nvlhdr))
goto failed;
if (nvlhdr.nvlh_size != *leftp - sizeof(nvlhdr))
goto failed;
/*
* nvlh_descriptors might be smaller than nfds in embedded nvlists.
*/
if (nvlhdr.nvlh_descriptors > nfds)
goto failed;
if ((nvlhdr.nvlh_flags & ~NV_FLAG_ALL_MASK) != 0)
goto failed;
inarrayf = (nvl->nvl_flags & NV_FLAG_IN_ARRAY);
nvl->nvl_flags = (nvlhdr.nvlh_flags & NV_FLAG_PUBLIC_MASK) | inarrayf;
ptr += sizeof(nvlhdr);
if (isbep != NULL)
*isbep = (((int)nvlhdr.nvlh_flags & NV_FLAG_BIG_ENDIAN) != 0);
*leftp -= sizeof(nvlhdr);
return (ptr);
failed:
ERRNO_SET(EINVAL);
return (NULL);
}
static nvlist_t *
nvlist_xunpack(const void *buf, size_t size, const int *fds, size_t nfds,
int flags)
{
const unsigned char *ptr;
nvlist_t *nvl, *retnvl, *tmpnvl, *array;
nvpair_t *nvp;
size_t left;
bool isbe;
PJDLOG_ASSERT((flags & ~(NV_FLAG_PUBLIC_MASK)) == 0);
left = size;
ptr = buf;
tmpnvl = array = NULL;
nvl = retnvl = nvlist_create(0);
if (nvl == NULL)
goto failed;
ptr = nvlist_unpack_header(nvl, ptr, nfds, &isbe, &left);
if (ptr == NULL)
goto failed;
if (nvl->nvl_flags != flags) {
ERRNO_SET(EILSEQ);
goto failed;
}
while (left > 0) {
ptr = nvpair_unpack(isbe, ptr, &left, &nvp);
if (ptr == NULL)
goto failed;
switch (nvpair_type(nvp)) {
case NV_TYPE_NULL:
ptr = nvpair_unpack_null(isbe, nvp, ptr, &left);
break;
case NV_TYPE_BOOL:
ptr = nvpair_unpack_bool(isbe, nvp, ptr, &left);
break;
case NV_TYPE_NUMBER:
ptr = nvpair_unpack_number(isbe, nvp, ptr, &left);
break;
case NV_TYPE_STRING:
ptr = nvpair_unpack_string(isbe, nvp, ptr, &left);
break;
case NV_TYPE_NVLIST:
ptr = nvpair_unpack_nvlist(isbe, nvp, ptr, &left, nfds,
&tmpnvl);
if (tmpnvl == NULL || ptr == NULL)
goto failed;
nvlist_set_parent(tmpnvl, nvp);
break;
#ifndef _KERNEL
case NV_TYPE_DESCRIPTOR:
ptr = nvpair_unpack_descriptor(isbe, nvp, ptr, &left,
fds, nfds);
break;
case NV_TYPE_DESCRIPTOR_ARRAY:
ptr = nvpair_unpack_descriptor_array(isbe, nvp, ptr,
&left, fds, nfds);
break;
#endif
case NV_TYPE_BINARY:
ptr = nvpair_unpack_binary(isbe, nvp, ptr, &left);
break;
case NV_TYPE_NVLIST_UP:
if (nvl->nvl_parent == NULL)
goto failed;
nvl = nvpair_nvlist(nvl->nvl_parent);
nvpair_free_structure(nvp);
continue;
case NV_TYPE_NVLIST_ARRAY_NEXT:
if (nvl->nvl_array_next == NULL) {
if (nvl->nvl_parent == NULL)
goto failed;
nvl = nvpair_nvlist(nvl->nvl_parent);
} else {
nvl = __DECONST(nvlist_t *,
nvlist_get_array_next(nvl));
ptr = nvlist_unpack_header(nvl, ptr, nfds,
&isbe, &left);
if (ptr == NULL)
goto failed;
}
nvpair_free_structure(nvp);
continue;
case NV_TYPE_BOOL_ARRAY:
ptr = nvpair_unpack_bool_array(isbe, nvp, ptr, &left);
break;
case NV_TYPE_NUMBER_ARRAY:
ptr = nvpair_unpack_number_array(isbe, nvp, ptr, &left);
break;
case NV_TYPE_STRING_ARRAY:
ptr = nvpair_unpack_string_array(isbe, nvp, ptr, &left);
break;
case NV_TYPE_NVLIST_ARRAY:
ptr = nvpair_unpack_nvlist_array(isbe, nvp, ptr, &left,
&array);
if (ptr == NULL)
goto failed;
tmpnvl = array;
while (array != NULL) {
nvlist_set_parent(array, nvp);
array = __DECONST(nvlist_t *,
nvlist_get_array_next(array));
}
ptr = nvlist_unpack_header(tmpnvl, ptr, nfds, &isbe,
&left);
break;
default:
PJDLOG_ABORT("Invalid type (%d).", nvpair_type(nvp));
}
if (ptr == NULL)
goto failed;
if (!nvlist_move_nvpair(nvl, nvp))
goto failed;
if (tmpnvl != NULL) {
nvl = tmpnvl;
tmpnvl = NULL;
}
}
return (retnvl);
failed:
nvlist_destroy(retnvl);
return (NULL);
}
nvlist_t *
nvlist_unpack(const void *buf, size_t size, int flags)
{
return (nvlist_xunpack(buf, size, NULL, 0, flags));
}
#ifndef _KERNEL
int
nvlist_send(int sock, const nvlist_t *nvl)
{
size_t datasize, nfds;
int *fds;
void *data;
int64_t fdidx;
int ret;
if (nvlist_error(nvl) != 0) {
ERRNO_SET(nvlist_error(nvl));
return (-1);
}
fds = nvlist_descriptors(nvl, &nfds);
if (fds == NULL)
return (-1);
ret = -1;
data = NULL;
fdidx = 0;
data = nvlist_xpack(nvl, &fdidx, &datasize);
if (data == NULL)
goto out;
if (buf_send(sock, data, datasize) == -1)
goto out;
if (nfds > 0) {
if (fd_send(sock, fds, nfds) == -1)
goto out;
}
ret = 0;
out:
ERRNO_SAVE();
nv_free(fds);
nv_free(data);
ERRNO_RESTORE();
return (ret);
}
nvlist_t *
nvlist_recv(int sock, int flags)
{
struct nvlist_header nvlhdr;
nvlist_t *nvl, *ret;
unsigned char *buf;
size_t nfds, size, i;
int *fds;
if (buf_recv(sock, &nvlhdr, sizeof(nvlhdr)) == -1)
return (NULL);
if (!nvlist_check_header(&nvlhdr))
return (NULL);
nfds = (size_t)nvlhdr.nvlh_descriptors;
size = sizeof(nvlhdr) + (size_t)nvlhdr.nvlh_size;
buf = nv_malloc(size);
if (buf == NULL)
return (NULL);
memcpy(buf, &nvlhdr, sizeof(nvlhdr));
ret = NULL;
fds = NULL;
if (buf_recv(sock, buf + sizeof(nvlhdr), size - sizeof(nvlhdr)) == -1)
goto out;
if (nfds > 0) {
fds = nv_malloc(nfds * sizeof(fds[0]));
if (fds == NULL)
goto out;
if (fd_recv(sock, fds, nfds) == -1)
goto out;
}
nvl = nvlist_xunpack(buf, size, fds, nfds, flags);
if (nvl == NULL) {
ERRNO_SAVE();
for (i = 0; i < nfds; i++)
close(fds[i]);
ERRNO_RESTORE();
goto out;
}
ret = nvl;
out:
ERRNO_SAVE();
nv_free(buf);
nv_free(fds);
ERRNO_RESTORE();
return (ret);
}
nvlist_t *
nvlist_xfer(int sock, nvlist_t *nvl, int flags)
{
if (nvlist_send(sock, nvl) < 0) {
nvlist_destroy(nvl);
return (NULL);
}
nvlist_destroy(nvl);
return (nvlist_recv(sock, flags));
}
#endif
nvpair_t *
nvlist_first_nvpair(const nvlist_t *nvl)
{
NVLIST_ASSERT(nvl);
return (TAILQ_FIRST(&nvl->nvl_head));
}
nvpair_t *
nvlist_next_nvpair(const nvlist_t *nvl, const nvpair_t *nvp)
{
nvpair_t *retnvp;
NVLIST_ASSERT(nvl);
NVPAIR_ASSERT(nvp);
PJDLOG_ASSERT(nvpair_nvlist(nvp) == nvl);
retnvp = nvpair_next(nvp);
PJDLOG_ASSERT(retnvp == NULL || nvpair_nvlist(retnvp) == nvl);
return (retnvp);
}
nvpair_t *
nvlist_prev_nvpair(const nvlist_t *nvl, const nvpair_t *nvp)
{
nvpair_t *retnvp;
NVLIST_ASSERT(nvl);
NVPAIR_ASSERT(nvp);
PJDLOG_ASSERT(nvpair_nvlist(nvp) == nvl);
retnvp = nvpair_prev(nvp);
PJDLOG_ASSERT(nvpair_nvlist(retnvp) == nvl);
return (retnvp);
}
const char *
nvlist_next(const nvlist_t *nvl, int *typep, void **cookiep)
{
nvpair_t *nvp;
NVLIST_ASSERT(nvl);
if (cookiep == NULL || *cookiep == NULL)
nvp = nvlist_first_nvpair(nvl);
else
nvp = nvlist_next_nvpair(nvl, *cookiep);
if (nvp == NULL)
return (NULL);
if (typep != NULL)
*typep = nvpair_type(nvp);
if (cookiep != NULL)
*cookiep = nvp;
return (nvpair_name(nvp));
}
bool
nvlist_exists(const nvlist_t *nvl, const char *name)
{
return (nvlist_find(nvl, NV_TYPE_NONE, name) != NULL);
}
#define NVLIST_EXISTS(type, TYPE) \
bool \
nvlist_exists_##type(const nvlist_t *nvl, const char *name) \
{ \
\
return (nvlist_find(nvl, NV_TYPE_##TYPE, name) != NULL); \
}
NVLIST_EXISTS(null, NULL)
NVLIST_EXISTS(bool, BOOL)
NVLIST_EXISTS(number, NUMBER)
NVLIST_EXISTS(string, STRING)
NVLIST_EXISTS(nvlist, NVLIST)
NVLIST_EXISTS(binary, BINARY)
NVLIST_EXISTS(bool_array, BOOL_ARRAY)
NVLIST_EXISTS(number_array, NUMBER_ARRAY)
NVLIST_EXISTS(string_array, STRING_ARRAY)
NVLIST_EXISTS(nvlist_array, NVLIST_ARRAY)
#ifndef _KERNEL
NVLIST_EXISTS(descriptor, DESCRIPTOR)
NVLIST_EXISTS(descriptor_array, DESCRIPTOR_ARRAY)
#endif
#undef NVLIST_EXISTS
void
nvlist_add_nvpair(nvlist_t *nvl, const nvpair_t *nvp)
{
nvpair_t *newnvp;
NVPAIR_ASSERT(nvp);
if (nvlist_error(nvl) != 0) {
ERRNO_SET(nvlist_error(nvl));
return;
}
if ((nvl->nvl_flags & NV_FLAG_NO_UNIQUE) == 0) {
if (nvlist_exists(nvl, nvpair_name(nvp))) {
nvl->nvl_error = EEXIST;
ERRNO_SET(nvlist_error(nvl));
return;
}
}
newnvp = nvpair_clone(nvp);
if (newnvp == NULL) {
nvl->nvl_error = ERRNO_OR_DEFAULT(ENOMEM);
ERRNO_SET(nvlist_error(nvl));
return;
}
nvpair_insert(&nvl->nvl_head, newnvp, nvl);
}
void
nvlist_add_stringf(nvlist_t *nvl, const char *name, const char *valuefmt, ...)
{
va_list valueap;
va_start(valueap, valuefmt);
nvlist_add_stringv(nvl, name, valuefmt, valueap);
va_end(valueap);
}
void
nvlist_add_stringv(nvlist_t *nvl, const char *name, const char *valuefmt,
va_list valueap)
{
nvpair_t *nvp;
if (nvlist_error(nvl) != 0) {
ERRNO_SET(nvlist_error(nvl));
return;
}
nvp = nvpair_create_stringv(name, valuefmt, valueap);
if (nvp == NULL) {
nvl->nvl_error = ERRNO_OR_DEFAULT(ENOMEM);
ERRNO_SET(nvl->nvl_error);
} else {
(void)nvlist_move_nvpair(nvl, nvp);
}
}
void
nvlist_add_null(nvlist_t *nvl, const char *name)
{
nvpair_t *nvp;
if (nvlist_error(nvl) != 0) {
ERRNO_SET(nvlist_error(nvl));
return;
}
nvp = nvpair_create_null(name);
if (nvp == NULL) {
nvl->nvl_error = ERRNO_OR_DEFAULT(ENOMEM);
ERRNO_SET(nvl->nvl_error);
} else {
(void)nvlist_move_nvpair(nvl, nvp);
}
}
void
nvlist_add_binary(nvlist_t *nvl, const char *name, const void *value,
size_t size)
{
nvpair_t *nvp;
if (nvlist_error(nvl) != 0) {
ERRNO_SET(nvlist_error(nvl));
return;
}
nvp = nvpair_create_binary(name, value, size);
if (nvp == NULL) {
nvl->nvl_error = ERRNO_OR_DEFAULT(ENOMEM);
ERRNO_SET(nvl->nvl_error);
} else {
(void)nvlist_move_nvpair(nvl, nvp);
}
}
#define NVLIST_ADD(vtype, type) \
void \
nvlist_add_##type(nvlist_t *nvl, const char *name, vtype value) \
{ \
nvpair_t *nvp; \
\
if (nvlist_error(nvl) != 0) { \
ERRNO_SET(nvlist_error(nvl)); \
return; \
} \
\
nvp = nvpair_create_##type(name, value); \
if (nvp == NULL) { \
nvl->nvl_error = ERRNO_OR_DEFAULT(ENOMEM); \
ERRNO_SET(nvl->nvl_error); \
} else { \
(void)nvlist_move_nvpair(nvl, nvp); \
} \
}
NVLIST_ADD(bool, bool)
NVLIST_ADD(uint64_t, number)
NVLIST_ADD(const char *, string)
NVLIST_ADD(const nvlist_t *, nvlist)
#ifndef _KERNEL
NVLIST_ADD(int, descriptor);
#endif
#undef NVLIST_ADD
#define NVLIST_ADD_ARRAY(vtype, type) \
void \
nvlist_add_##type##_array(nvlist_t *nvl, const char *name, vtype value, \
size_t nitems) \
{ \
nvpair_t *nvp; \
\
if (nvlist_error(nvl) != 0) { \
ERRNO_SET(nvlist_error(nvl)); \
return; \
} \
\
nvp = nvpair_create_##type##_array(name, value, nitems); \
if (nvp == NULL) { \
nvl->nvl_error = ERRNO_OR_DEFAULT(ENOMEM); \
ERRNO_SET(nvl->nvl_error); \
} else { \
(void)nvlist_move_nvpair(nvl, nvp); \
} \
}
NVLIST_ADD_ARRAY(const bool *, bool)
NVLIST_ADD_ARRAY(const uint64_t *, number)
NVLIST_ADD_ARRAY(const char * const *, string)
NVLIST_ADD_ARRAY(const nvlist_t * const *, nvlist)
#ifndef _KERNEL
NVLIST_ADD_ARRAY(const int *, descriptor)
#endif
#undef NVLIST_ADD_ARRAY
bool
nvlist_move_nvpair(nvlist_t *nvl, nvpair_t *nvp)
{
NVPAIR_ASSERT(nvp);
PJDLOG_ASSERT(nvpair_nvlist(nvp) == NULL);
if (nvlist_error(nvl) != 0) {
nvpair_free(nvp);
ERRNO_SET(nvlist_error(nvl));
return (false);
}
if ((nvl->nvl_flags & NV_FLAG_NO_UNIQUE) == 0) {
if (nvlist_exists(nvl, nvpair_name(nvp))) {
nvpair_free(nvp);
nvl->nvl_error = EEXIST;
ERRNO_SET(nvl->nvl_error);
return (false);
}
}
nvpair_insert(&nvl->nvl_head, nvp, nvl);
return (true);
}
void
nvlist_move_string(nvlist_t *nvl, const char *name, char *value)
{
nvpair_t *nvp;
if (nvlist_error(nvl) != 0) {
nv_free(value);
ERRNO_SET(nvlist_error(nvl));
return;
}
nvp = nvpair_move_string(name, value);
if (nvp == NULL) {
nvl->nvl_error = ERRNO_OR_DEFAULT(ENOMEM);
ERRNO_SET(nvl->nvl_error);
} else {
(void)nvlist_move_nvpair(nvl, nvp);
}
}
void
nvlist_move_nvlist(nvlist_t *nvl, const char *name, nvlist_t *value)
{
nvpair_t *nvp;
if (nvlist_error(nvl) != 0) {
if (value != NULL && nvlist_get_nvpair_parent(value) != NULL)
nvlist_destroy(value);
ERRNO_SET(nvlist_error(nvl));
return;
}
nvp = nvpair_move_nvlist(name, value);
if (nvp == NULL) {
nvl->nvl_error = ERRNO_OR_DEFAULT(ENOMEM);
ERRNO_SET(nvl->nvl_error);
} else {
(void)nvlist_move_nvpair(nvl, nvp);
}
}
#ifndef _KERNEL
void
nvlist_move_descriptor(nvlist_t *nvl, const char *name, int value)
{
nvpair_t *nvp;
if (nvlist_error(nvl) != 0) {
close(value);
ERRNO_SET(nvlist_error(nvl));
return;
}
nvp = nvpair_move_descriptor(name, value);
if (nvp == NULL) {
nvl->nvl_error = ERRNO_OR_DEFAULT(ENOMEM);
ERRNO_SET(nvl->nvl_error);
} else {
(void)nvlist_move_nvpair(nvl, nvp);
}
}
#endif
void
nvlist_move_binary(nvlist_t *nvl, const char *name, void *value, size_t size)
{
nvpair_t *nvp;
if (nvlist_error(nvl) != 0) {
nv_free(value);
ERRNO_SET(nvlist_error(nvl));
return;
}
nvp = nvpair_move_binary(name, value, size);
if (nvp == NULL) {
nvl->nvl_error = ERRNO_OR_DEFAULT(ENOMEM);
ERRNO_SET(nvl->nvl_error);
} else {
(void)nvlist_move_nvpair(nvl, nvp);
}
}
void
nvlist_move_bool_array(nvlist_t *nvl, const char *name, bool *value,
size_t nitems)
{
nvpair_t *nvp;
if (nvlist_error(nvl) != 0) {
nv_free(value);
ERRNO_SET(nvlist_error(nvl));
return;
}
nvp = nvpair_move_bool_array(name, value, nitems);
if (nvp == NULL) {
nvl->nvl_error = ERRNO_OR_DEFAULT(ENOMEM);
ERRNO_SET(nvl->nvl_error);
} else {
(void)nvlist_move_nvpair(nvl, nvp);
}
}
void
nvlist_move_string_array(nvlist_t *nvl, const char *name, char **value,
size_t nitems)
{
nvpair_t *nvp;
size_t i;
if (nvlist_error(nvl) != 0) {
if (value != NULL) {
for (i = 0; i < nitems; i++)
nv_free(value[i]);
nv_free(value);
}
ERRNO_SET(nvlist_error(nvl));
return;
}
nvp = nvpair_move_string_array(name, value, nitems);
if (nvp == NULL) {
nvl->nvl_error = ERRNO_OR_DEFAULT(ENOMEM);
ERRNO_SET(nvl->nvl_error);
} else {
(void)nvlist_move_nvpair(nvl, nvp);
}
}
void
nvlist_move_nvlist_array(nvlist_t *nvl, const char *name, nvlist_t **value,
size_t nitems)
{
nvpair_t *nvp;
size_t i;
if (nvlist_error(nvl) != 0) {
if (value != NULL) {
for (i = 0; i < nitems; i++) {
if (nvlist_get_pararr(value[i], NULL) == NULL)
nvlist_destroy(value[i]);
}
}
nv_free(value);
ERRNO_SET(nvlist_error(nvl));
return;
}
nvp = nvpair_move_nvlist_array(name, value, nitems);
if (nvp == NULL) {
nvl->nvl_error = ERRNO_OR_DEFAULT(ENOMEM);
ERRNO_SET(nvl->nvl_error);
} else {
(void)nvlist_move_nvpair(nvl, nvp);
}
}
void
nvlist_move_number_array(nvlist_t *nvl, const char *name, uint64_t *value,
size_t nitems)
{
nvpair_t *nvp;
if (nvlist_error(nvl) != 0) {
nv_free(value);
ERRNO_SET(nvlist_error(nvl));
return;
}
nvp = nvpair_move_number_array(name, value, nitems);
if (nvp == NULL) {
nvl->nvl_error = ERRNO_OR_DEFAULT(ENOMEM);
ERRNO_SET(nvl->nvl_error);
} else {
(void)nvlist_move_nvpair(nvl, nvp);
}
}
#ifndef _KERNEL
void
nvlist_move_descriptor_array(nvlist_t *nvl, const char *name, int *value,
size_t nitems)
{
nvpair_t *nvp;
size_t i;
if (nvlist_error(nvl) != 0) {
if (value != 0) {
for (i = 0; i < nitems; i++)
close(value[i]);
nv_free(value);
}
ERRNO_SET(nvlist_error(nvl));
return;
}
nvp = nvpair_move_descriptor_array(name, value, nitems);
if (nvp == NULL) {
nvl->nvl_error = ERRNO_OR_DEFAULT(ENOMEM);
ERRNO_SET(nvl->nvl_error);
} else {
(void)nvlist_move_nvpair(nvl, nvp);
}
}
#endif
const nvpair_t *
nvlist_get_nvpair(const nvlist_t *nvl, const char *name)
{
return (nvlist_find(nvl, NV_TYPE_NONE, name));
}
#define NVLIST_GET(ftype, type, TYPE) \
ftype \
nvlist_get_##type(const nvlist_t *nvl, const char *name) \
{ \
const nvpair_t *nvp; \
\
nvp = nvlist_find(nvl, NV_TYPE_##TYPE, name); \
if (nvp == NULL) \
nvlist_report_missing(NV_TYPE_##TYPE, name); \
return (nvpair_get_##type(nvp)); \
}
NVLIST_GET(bool, bool, BOOL)
NVLIST_GET(uint64_t, number, NUMBER)
NVLIST_GET(const char *, string, STRING)
NVLIST_GET(const nvlist_t *, nvlist, NVLIST)
#ifndef _KERNEL
NVLIST_GET(int, descriptor, DESCRIPTOR)
#endif
#undef NVLIST_GET
const void *
nvlist_get_binary(const nvlist_t *nvl, const char *name, size_t *sizep)
{
nvpair_t *nvp;
nvp = nvlist_find(nvl, NV_TYPE_BINARY, name);
if (nvp == NULL)
nvlist_report_missing(NV_TYPE_BINARY, name);
return (nvpair_get_binary(nvp, sizep));
}
#define NVLIST_GET_ARRAY(ftype, type, TYPE) \
ftype \
nvlist_get_##type##_array(const nvlist_t *nvl, const char *name, \
size_t *nitems) \
{ \
const nvpair_t *nvp; \
\
nvp = nvlist_find(nvl, NV_TYPE_##TYPE##_ARRAY, name); \
if (nvp == NULL) \
nvlist_report_missing(NV_TYPE_##TYPE##_ARRAY, name); \
return (nvpair_get_##type##_array(nvp, nitems)); \
}
NVLIST_GET_ARRAY(const bool *, bool, BOOL)
NVLIST_GET_ARRAY(const uint64_t *, number, NUMBER)
NVLIST_GET_ARRAY(const char * const *, string, STRING)
NVLIST_GET_ARRAY(const nvlist_t * const *, nvlist, NVLIST)
#ifndef _KERNEL
NVLIST_GET_ARRAY(const int *, descriptor, DESCRIPTOR)
#endif
#undef NVLIST_GET_ARRAY
#define NVLIST_TAKE(ftype, type, TYPE) \
ftype \
nvlist_take_##type(nvlist_t *nvl, const char *name) \
{ \
nvpair_t *nvp; \
ftype value; \
\
nvp = nvlist_find(nvl, NV_TYPE_##TYPE, name); \
if (nvp == NULL) \
nvlist_report_missing(NV_TYPE_##TYPE, name); \
value = (ftype)(intptr_t)nvpair_get_##type(nvp); \
nvlist_remove_nvpair(nvl, nvp); \
nvpair_free_structure(nvp); \
return (value); \
}
NVLIST_TAKE(bool, bool, BOOL)
NVLIST_TAKE(uint64_t, number, NUMBER)
NVLIST_TAKE(char *, string, STRING)
NVLIST_TAKE(nvlist_t *, nvlist, NVLIST)
#ifndef _KERNEL
NVLIST_TAKE(int, descriptor, DESCRIPTOR)
#endif
#undef NVLIST_TAKE
void *
nvlist_take_binary(nvlist_t *nvl, const char *name, size_t *sizep)
{
nvpair_t *nvp;
void *value;
nvp = nvlist_find(nvl, NV_TYPE_BINARY, name);
if (nvp == NULL)
nvlist_report_missing(NV_TYPE_BINARY, name);
value = (void *)(intptr_t)nvpair_get_binary(nvp, sizep);
nvlist_remove_nvpair(nvl, nvp);
nvpair_free_structure(nvp);
return (value);
}
#define NVLIST_TAKE_ARRAY(ftype, type, TYPE) \
ftype \
nvlist_take_##type##_array(nvlist_t *nvl, const char *name, \
size_t *nitems) \
{ \
nvpair_t *nvp; \
ftype value; \
\
nvp = nvlist_find(nvl, NV_TYPE_##TYPE##_ARRAY, name); \
if (nvp == NULL) \
nvlist_report_missing(NV_TYPE_##TYPE##_ARRAY, name); \
value = (ftype)(intptr_t)nvpair_get_##type##_array(nvp, nitems);\
nvlist_remove_nvpair(nvl, nvp); \
nvpair_free_structure(nvp); \
return (value); \
}
NVLIST_TAKE_ARRAY(bool *, bool, BOOL)
NVLIST_TAKE_ARRAY(uint64_t *, number, NUMBER)
NVLIST_TAKE_ARRAY(char **, string, STRING)
NVLIST_TAKE_ARRAY(nvlist_t **, nvlist, NVLIST)
#ifndef _KERNEL
NVLIST_TAKE_ARRAY(int *, descriptor, DESCRIPTOR)
#endif
void
nvlist_remove_nvpair(nvlist_t *nvl, nvpair_t *nvp)
{
NVLIST_ASSERT(nvl);
NVPAIR_ASSERT(nvp);
PJDLOG_ASSERT(nvpair_nvlist(nvp) == nvl);
nvpair_remove(&nvl->nvl_head, nvp, nvl);
}
void
nvlist_free(nvlist_t *nvl, const char *name)
{
nvlist_free_type(nvl, name, NV_TYPE_NONE);
}
#define NVLIST_FREE(type, TYPE) \
void \
nvlist_free_##type(nvlist_t *nvl, const char *name) \
{ \
\
nvlist_free_type(nvl, name, NV_TYPE_##TYPE); \
}
NVLIST_FREE(null, NULL)
NVLIST_FREE(bool, BOOL)
NVLIST_FREE(number, NUMBER)
NVLIST_FREE(string, STRING)
NVLIST_FREE(nvlist, NVLIST)
NVLIST_FREE(binary, BINARY)
NVLIST_FREE(bool_array, BOOL_ARRAY)
NVLIST_FREE(number_array, NUMBER_ARRAY)
NVLIST_FREE(string_array, STRING_ARRAY)
NVLIST_FREE(nvlist_array, NVLIST_ARRAY)
#ifndef _KERNEL
NVLIST_FREE(descriptor, DESCRIPTOR)
NVLIST_FREE(descriptor_array, DESCRIPTOR_ARRAY)
#endif
#undef NVLIST_FREE
void
nvlist_free_nvpair(nvlist_t *nvl, nvpair_t *nvp)
{
NVLIST_ASSERT(nvl);
NVPAIR_ASSERT(nvp);
PJDLOG_ASSERT(nvpair_nvlist(nvp) == nvl);
nvlist_remove_nvpair(nvl, nvp);
nvpair_free(nvp);
}