diff --git a/contrib/mdocml/lib.in b/contrib/mdocml/lib.in index 7198a79799c9..0e3162f0534b 100644 --- a/contrib/mdocml/lib.in +++ b/contrib/mdocml/lib.in @@ -67,6 +67,7 @@ LINE("libmemstat", "Kernel Memory Allocator Statistics Library (libmemstat, \\-l LINE("libmenu", "Curses Menu Library (libmenu, \\-lmenu)") LINE("libnetgraph", "Netgraph User Library (libnetgraph, \\-lnetgraph)") LINE("libnetpgp", "Netpgp signing, verification, encryption and decryption (libnetpgp, \\-lnetpgp)") +LINE("libnv", "Name/value pairs library (libnv, \\-lnv)") LINE("libossaudio", "OSS Audio Emulation Library (libossaudio, \\-lossaudio)") LINE("libpam", "Pluggable Authentication Module Library (libpam, \\-lpam)") LINE("libpcap", "Packet Capture Library (libpcap, \\-lpcap)") diff --git a/lib/Makefile b/lib/Makefile index 8af9f5fac44a..ca368aaf215c 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -94,6 +94,7 @@ SUBDIR= ${SUBDIR_ORDERED} \ ${_libnandfs} \ libnetbsd \ ${_libngatm} \ + libnv \ libopie \ libpam \ libpcap \ diff --git a/lib/libnv/Makefile b/lib/libnv/Makefile new file mode 100644 index 000000000000..710c2950671c --- /dev/null +++ b/lib/libnv/Makefile @@ -0,0 +1,161 @@ +# $FreeBSD$ + +LIB= nv +SHLIBDIR?= /lib +SHLIB_MAJOR= 0 + +SRCS= dnvlist.c +SRCS+= msgio.c +SRCS+= nvlist.c +SRCS+= nvpair.c + +INCS= dnv.h +INCS+= nv.h + +MAN+= nv.3 + +MLINKS+=nv.3 libnv.3 \ + nv.3 nvlist.3 +MLINKS+=nv.3 nvlist_create.3 \ + nv.3 nvlist_destroy.3 \ + nv.3 nvlist_error.3 \ + nv.3 nvlist_empty.3 \ + nv.3 nvlist_clone.3 \ + nv.3 nvlist_dump.3 \ + nv.3 nvlist_fdump.3 \ + nv.3 nvlist_size.3 \ + nv.3 nvlist_pack.3 \ + nv.3 nvlist_unpack.3 \ + nv.3 nvlist_send.3 \ + nv.3 nvlist_recv.3 \ + nv.3 nvlist_xfer.3 \ + nv.3 nvlist_next.3 \ + nv.3 nvlist_exists.3 \ + nv.3 nvlist_exists_type.3 \ + nv.3 nvlist_exists_null.3 \ + nv.3 nvlist_exists_bool.3 \ + nv.3 nvlist_exists_number.3 \ + nv.3 nvlist_exists_string.3 \ + nv.3 nvlist_exists_nvlist.3 \ + nv.3 nvlist_exists_descriptor.3 \ + nv.3 nvlist_exists_binary.3 \ + nv.3 nvlist_add_null.3 \ + nv.3 nvlist_add_bool.3 \ + nv.3 nvlist_add_number.3 \ + nv.3 nvlist_add_string.3 \ + nv.3 nvlist_add_stringf.3 \ + nv.3 nvlist_add_stringv.3 \ + nv.3 nvlist_add_nvlist.3 \ + nv.3 nvlist_add_descriptor.3 \ + nv.3 nvlist_add_binary.3 \ + nv.3 nvlist_move_string.3 \ + nv.3 nvlist_move_nvlist.3 \ + nv.3 nvlist_move_descriptor.3 \ + nv.3 nvlist_move_binary.3 \ + nv.3 nvlist_get_bool.3 \ + nv.3 nvlist_get_number.3 \ + nv.3 nvlist_get_string.3 \ + nv.3 nvlist_get_nvlist.3 \ + nv.3 nvlist_get_descriptor.3 \ + nv.3 nvlist_get_binary.3 \ + nv.3 nvlist_take_bool.3 \ + nv.3 nvlist_take_number.3 \ + nv.3 nvlist_take_string.3 \ + nv.3 nvlist_take_nvlist.3 \ + nv.3 nvlist_take_descriptor.3 \ + nv.3 nvlist_take_binary.3 \ + nv.3 nvlist_free.3 \ + nv.3 nvlist_free_type.3 \ + nv.3 nvlist_free_null.3 \ + nv.3 nvlist_free_bool.3 \ + nv.3 nvlist_free_number.3 \ + nv.3 nvlist_free_string.3 \ + nv.3 nvlist_free_nvlist.3 \ + nv.3 nvlist_free_descriptor.3 \ + nv.3 nvlist_free_binary.3 +MLINKS+=nv.3 nvlist_existsf.3 \ + nv.3 nvlist_existsf_type.3 \ + nv.3 nvlist_existsf_null.3 \ + nv.3 nvlist_existsf_bool.3 \ + nv.3 nvlist_existsf_number.3 \ + nv.3 nvlist_existsf_string.3 \ + nv.3 nvlist_existsf_nvlist.3 \ + nv.3 nvlist_existsf_descriptor.3 \ + nv.3 nvlist_existsf_binary.3 \ + nv.3 nvlist_addf_null.3 \ + nv.3 nvlist_addf_bool.3 \ + nv.3 nvlist_addf_number.3 \ + nv.3 nvlist_addf_string.3 \ + nv.3 nvlist_addf_nvlist.3 \ + nv.3 nvlist_addf_descriptor.3 \ + nv.3 nvlist_addf_binary.3 \ + nv.3 nvlist_movef_string.3 \ + nv.3 nvlist_movef_nvlist.3 \ + nv.3 nvlist_movef_descriptor.3 \ + nv.3 nvlist_movef_binary.3 \ + nv.3 nvlist_getf_bool.3 \ + nv.3 nvlist_getf_number.3 \ + nv.3 nvlist_getf_string.3 \ + nv.3 nvlist_getf_nvlist.3 \ + nv.3 nvlist_getf_descriptor.3 \ + nv.3 nvlist_getf_binary.3 \ + nv.3 nvlist_takef_bool.3 \ + nv.3 nvlist_takef_number.3 \ + nv.3 nvlist_takef_string.3 \ + nv.3 nvlist_takef_nvlist.3 \ + nv.3 nvlist_takef_descriptor.3 \ + nv.3 nvlist_takef_binary.3 \ + nv.3 nvlist_freef.3 \ + nv.3 nvlist_freef_type.3 \ + nv.3 nvlist_freef_null.3 \ + nv.3 nvlist_freef_bool.3 \ + nv.3 nvlist_freef_number.3 \ + nv.3 nvlist_freef_string.3 \ + nv.3 nvlist_freef_nvlist.3 \ + nv.3 nvlist_freef_descriptor.3 \ + nv.3 nvlist_freef_binary.3 +MLINKS+=nv.3 nvlist_existsv.3 \ + nv.3 nvlist_existsv_type.3 \ + nv.3 nvlist_existsv_null.3 \ + nv.3 nvlist_existsv_bool.3 \ + nv.3 nvlist_existsv_number.3 \ + nv.3 nvlist_existsv_string.3 \ + nv.3 nvlist_existsv_nvlist.3 \ + nv.3 nvlist_existsv_descriptor.3 \ + nv.3 nvlist_existsv_binary.3 \ + nv.3 nvlist_addv_null.3 \ + nv.3 nvlist_addv_bool.3 \ + nv.3 nvlist_addv_number.3 \ + nv.3 nvlist_addv_string.3 \ + nv.3 nvlist_addv_nvlist.3 \ + nv.3 nvlist_addv_descriptor.3 \ + nv.3 nvlist_addv_binary.3 \ + nv.3 nvlist_movev_string.3 \ + nv.3 nvlist_movev_nvlist.3 \ + nv.3 nvlist_movev_descriptor.3 \ + nv.3 nvlist_movev_binary.3 \ + nv.3 nvlist_getv_bool.3 \ + nv.3 nvlist_getv_number.3 \ + nv.3 nvlist_getv_string.3 \ + nv.3 nvlist_getv_nvlist.3 \ + nv.3 nvlist_getv_descriptor.3 \ + nv.3 nvlist_getv_binary.3 \ + nv.3 nvlist_takev_bool.3 \ + nv.3 nvlist_takev_number.3 \ + nv.3 nvlist_takev_string.3 \ + nv.3 nvlist_takev_nvlist.3 \ + nv.3 nvlist_takev_descriptor.3 \ + nv.3 nvlist_takev_binary.3 \ + nv.3 nvlist_freef.3 \ + nv.3 nvlist_freev_type.3 \ + nv.3 nvlist_freev_null.3 \ + nv.3 nvlist_freev_bool.3 \ + nv.3 nvlist_freev_number.3 \ + nv.3 nvlist_freev_string.3 \ + nv.3 nvlist_freev_nvlist.3 \ + nv.3 nvlist_freev_descriptor.3 \ + nv.3 nvlist_freev_binary.3 + +WARNS?= 6 + +.include diff --git a/lib/libnv/common_impl.h b/lib/libnv/common_impl.h new file mode 100644 index 000000000000..5af4db2b835a --- /dev/null +++ b/lib/libnv/common_impl.h @@ -0,0 +1,37 @@ +/*- + * Copyright (c) 2013 The FreeBSD Foundation + * 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. + * + * $FreeBSD$ + */ + +#ifndef _COMMON_IMPL_H_ +#define _COMMON_IMPL_H_ + +#define fd_is_valid(fd) (fcntl((fd), F_GETFL) != -1 || errno != EBADF) + +#endif /* !_COMMON_IMPL_H_ */ diff --git a/lib/libnv/dnv.h b/lib/libnv/dnv.h new file mode 100644 index 000000000000..ac1e57c2e848 --- /dev/null +++ b/lib/libnv/dnv.h @@ -0,0 +1,106 @@ +/*- + * Copyright (c) 2013 The FreeBSD Foundation + * 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. + * + * $FreeBSD$ + */ + +#ifndef _DNV_H_ +#define _DNV_H_ + +#include + +#include +#include +#include + +#ifndef _NVLIST_T_DECLARED +#define _NVLIST_T_DECLARED +struct nvlist; + +typedef struct nvlist nvlist_t; +#endif + +/* + * The dnvlist_get functions returns value associated with the given name. + * If it returns a pointer, the pointer represents internal buffer and should + * not be freed by the caller. + * If no element of the given name and type exists, the function will return + * provided default value. + */ + +bool dnvlist_get_bool(const nvlist_t *nvl, const char *name, bool defval); +uint64_t dnvlist_get_number(const nvlist_t *nvl, const char *name, uint64_t defval); +const char *dnvlist_get_string(const nvlist_t *nvl, const char *name, const char *defval); +const nvlist_t *dnvlist_get_nvlist(const nvlist_t *nvl, const char *name, const nvlist_t *defval); +int dnvlist_get_descriptor(const nvlist_t *nvl, const char *name, int defval); +const void *dnvlist_get_binary(const nvlist_t *nvl, const char *name, size_t *sizep, const void *defval, size_t defsize); + +bool dnvlist_getf_bool(const nvlist_t *nvl, bool defval, const char *namefmt, ...) __printflike(3, 4); +uint64_t dnvlist_getf_number(const nvlist_t *nvl, uint64_t defval, const char *namefmt, ...) __printflike(3, 4); +const char *dnvlist_getf_string(const nvlist_t *nvl, const char *defval, const char *namefmt, ...) __printflike(3, 4); +const nvlist_t *dnvlist_getf_nvlist(const nvlist_t *nvl, const nvlist_t *defval, const char *namefmt, ...) __printflike(3, 4); +int dnvlist_getf_descriptor(const nvlist_t *nvl, int defval, const char *namefmt, ...) __printflike(3, 4); +const void *dnvlist_getf_binary(const nvlist_t *nvl, size_t *sizep, const void *defval, size_t defsize, const char *namefmt, ...) __printflike(5, 6); + +bool dnvlist_getv_bool(const nvlist_t *nvl, bool defval, const char *namefmt, va_list nameap) __printflike(3, 0); +uint64_t dnvlist_getv_number(const nvlist_t *nvl, uint64_t defval, const char *namefmt, va_list nameap) __printflike(3, 0); +const char *dnvlist_getv_string(const nvlist_t *nvl, const char *defval, const char *namefmt, va_list nameap) __printflike(3, 0); +const nvlist_t *dnvlist_getv_nvlist(const nvlist_t *nvl, const nvlist_t *defval, const char *namefmt, va_list nameap) __printflike(3, 0); +int dnvlist_getv_descriptor(const nvlist_t *nvl, int defval, const char *namefmt, va_list nameap) __printflike(3, 0); +const void *dnvlist_getv_binary(const nvlist_t *nvl, size_t *sizep, const void *defval, size_t defsize, const char *namefmt, va_list nameap) __printflike(5, 0); + +/* + * The dnvlist_take functions returns value associated with the given name and + * remove corresponding nvpair. + * If it returns a pointer, the caller has to free it. + * If no element of the given name and type exists, the function will return + * provided default value. + */ + +bool dnvlist_take_bool(nvlist_t *nvl, const char *name, bool defval); +uint64_t dnvlist_take_number(nvlist_t *nvl, const char *name, uint64_t defval); +char *dnvlist_take_string(nvlist_t *nvl, const char *name, char *defval); +nvlist_t *dnvlist_take_nvlist(nvlist_t *nvl, const char *name, nvlist_t *defval); +int dnvlist_take_descriptor(nvlist_t *nvl, const char *name, int defval); +void *dnvlist_take_binary(nvlist_t *nvl, const char *name, size_t *sizep, void *defval, size_t defsize); + +bool dnvlist_takef_bool(nvlist_t *nvl, bool defval, const char *namefmt, ...) __printflike(3, 4); +uint64_t dnvlist_takef_number(nvlist_t *nvl, uint64_t defval, const char *namefmt, ...) __printflike(3, 4); +char *dnvlist_takef_string(nvlist_t *nvl, char *defval, const char *namefmt, ...) __printflike(3, 4); +nvlist_t *dnvlist_takef_nvlist(nvlist_t *nvl, nvlist_t *defval, const char *namefmt, ...) __printflike(3, 4); +int dnvlist_takef_descriptor(nvlist_t *nvl, int defval, const char *namefmt, ...) __printflike(3, 4); +void *dnvlist_takef_binary(nvlist_t *nvl, size_t *sizep, void *defval, size_t defsize, const char *namefmt, ...) __printflike(5, 6); + +bool dnvlist_takev_bool(nvlist_t *nvl, bool defval, const char *namefmt, va_list nameap) __printflike(3, 0); +uint64_t dnvlist_takev_number(nvlist_t *nvl, uint64_t defval, const char *namefmt, va_list nameap) __printflike(3, 0); +char *dnvlist_takev_string(nvlist_t *nvl, char *defval, const char *namefmt, va_list nameap) __printflike(3, 0); +nvlist_t *dnvlist_takev_nvlist(nvlist_t *nvl, nvlist_t *defval, const char *namefmt, va_list nameap) __printflike(3, 0); +int dnvlist_takev_descriptor(nvlist_t *nvl, int defval, const char *namefmt, va_list nameap) __printflike(3, 0); +void *dnvlist_takev_binary(nvlist_t *nvl, size_t *sizep, void *defval, size_t defsize, const char *namefmt, va_list nameap) __printflike(5, 0); + +#endif /* !_DNV_H_ */ diff --git a/lib/libnv/dnvlist.c b/lib/libnv/dnvlist.c new file mode 100644 index 000000000000..b758bbf97d5d --- /dev/null +++ b/lib/libnv/dnvlist.c @@ -0,0 +1,252 @@ +/*- + * Copyright (c) 2013 The FreeBSD Foundation + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include "nv.h" +#include "nv_impl.h" + +#include "dnv.h" + +#define DNVLIST_GET(ftype, type) \ +ftype \ +dnvlist_get_##type(const nvlist_t *nvl, const char *name, ftype defval) \ +{ \ + \ + return (dnvlist_getf_##type(nvl, defval, "%s", name)); \ +} + +DNVLIST_GET(bool, bool) +DNVLIST_GET(uint64_t, number) +DNVLIST_GET(const char *, string) +DNVLIST_GET(const nvlist_t *, nvlist) +DNVLIST_GET(int, descriptor) + +#undef DNVLIST_GET + +const void * +dnvlist_get_binary(const nvlist_t *nvl, const char *name, size_t *sizep, + const void *defval, size_t defsize) +{ + + return (dnvlist_getf_binary(nvl, sizep, defval, defsize, "%s", name)); +} + +#define DNVLIST_GETF(ftype, type) \ +ftype \ +dnvlist_getf_##type(const nvlist_t *nvl, ftype defval, \ + const char *namefmt, ...) \ +{ \ + va_list nameap; \ + ftype value; \ + \ + va_start(nameap, namefmt); \ + value = dnvlist_getv_##type(nvl, defval, namefmt, nameap); \ + va_end(nameap); \ + \ + return (value); \ +} + +DNVLIST_GETF(bool, bool) +DNVLIST_GETF(uint64_t, number) +DNVLIST_GETF(const char *, string) +DNVLIST_GETF(const nvlist_t *, nvlist) +DNVLIST_GETF(int, descriptor) + +#undef DNVLIST_GETF + +const void * +dnvlist_getf_binary(const nvlist_t *nvl, size_t *sizep, const void *defval, + size_t defsize, const char *namefmt, ...) +{ + va_list nameap; + const void *value; + + va_start(nameap, namefmt); + value = dnvlist_getv_binary(nvl, sizep, defval, defsize, namefmt, + nameap); + va_end(nameap); + + return (value); +} + +#define DNVLIST_GETV(ftype, type) \ +ftype \ +dnvlist_getv_##type(const nvlist_t *nvl, ftype defval, \ + const char *namefmt, va_list nameap) \ +{ \ + va_list cnameap; \ + ftype value; \ + \ + va_copy(cnameap, nameap); \ + if (nvlist_existsv_##type(nvl, namefmt, cnameap)) \ + value = nvlist_getv_##type(nvl, namefmt, nameap); \ + else \ + value = defval; \ + va_end(cnameap); \ + return (value); \ +} + +DNVLIST_GETV(bool, bool) +DNVLIST_GETV(uint64_t, number) +DNVLIST_GETV(const char *, string) +DNVLIST_GETV(const nvlist_t *, nvlist) +DNVLIST_GETV(int, descriptor) + +#undef DNVLIST_GETV + +const void * +dnvlist_getv_binary(const nvlist_t *nvl, size_t *sizep, const void *defval, + size_t defsize, const char *namefmt, va_list nameap) +{ + va_list cnameap; + const void *value; + + va_copy(cnameap, nameap); + if (nvlist_existsv_binary(nvl, namefmt, cnameap)) { + value = nvlist_getv_binary(nvl, sizep, namefmt, nameap); + } else { + if (sizep != NULL) + *sizep = defsize; + value = defval; + } + va_end(cnameap); + return (value); +} + +#define DNVLIST_TAKE(ftype, type) \ +ftype \ +dnvlist_take_##type(nvlist_t *nvl, const char *name, ftype defval) \ +{ \ + \ + return (dnvlist_takef_##type(nvl, defval, "%s", name)); \ +} + +DNVLIST_TAKE(bool, bool) +DNVLIST_TAKE(uint64_t, number) +DNVLIST_TAKE(char *, string) +DNVLIST_TAKE(nvlist_t *, nvlist) +DNVLIST_TAKE(int, descriptor) + +#undef DNVLIST_TAKE + +void * +dnvlist_take_binary(nvlist_t *nvl, const char *name, size_t *sizep, + void *defval, size_t defsize) +{ + + return (dnvlist_takef_binary(nvl, sizep, defval, defsize, "%s", name)); +} + +#define DNVLIST_TAKEF(ftype, type) \ +ftype \ +dnvlist_takef_##type(nvlist_t *nvl, ftype defval, \ + const char *namefmt, ...) \ +{ \ + va_list nameap; \ + ftype value; \ + \ + va_start(nameap, namefmt); \ + value = dnvlist_takev_##type(nvl, defval, namefmt, nameap); \ + va_end(nameap); \ + \ + return (value); \ +} + +DNVLIST_TAKEF(bool, bool) +DNVLIST_TAKEF(uint64_t, number) +DNVLIST_TAKEF(char *, string) +DNVLIST_TAKEF(nvlist_t *, nvlist) +DNVLIST_TAKEF(int, descriptor) + +#undef DNVLIST_TAKEF + +void * +dnvlist_takef_binary(nvlist_t *nvl, size_t *sizep, void *defval, + size_t defsize, const char *namefmt, ...) +{ + va_list nameap; + void *value; + + va_start(nameap, namefmt); + value = dnvlist_takev_binary(nvl, sizep, defval, defsize, namefmt, + nameap); + va_end(nameap); + + return (value); +} + +#define DNVLIST_TAKEV(ftype, type) \ +ftype \ +dnvlist_takev_##type(nvlist_t *nvl, ftype defval, const char *namefmt, \ + va_list nameap) \ +{ \ + va_list cnameap; \ + ftype value; \ + \ + va_copy(cnameap, nameap); \ + if (nvlist_existsv_##type(nvl, namefmt, cnameap)) \ + value = nvlist_takev_##type(nvl, namefmt, nameap); \ + else \ + value = defval; \ + va_end(cnameap); \ + return (value); \ +} + +DNVLIST_TAKEV(bool, bool) +DNVLIST_TAKEV(uint64_t, number) +DNVLIST_TAKEV(char *, string) +DNVLIST_TAKEV(nvlist_t *, nvlist) +DNVLIST_TAKEV(int, descriptor) + +#undef DNVLIST_TAKEV + +void * +dnvlist_takev_binary(nvlist_t *nvl, size_t *sizep, void *defval, + size_t defsize, const char *namefmt, va_list nameap) +{ + va_list cnameap; + void *value; + + va_copy(cnameap, nameap); + if (nvlist_existsv_binary(nvl, namefmt, cnameap)) { + value = nvlist_takev_binary(nvl, sizep, namefmt, nameap); + } else { + if (sizep != NULL) + *sizep = defsize; + value = defval; + } + va_end(cnameap); + return (value); +} diff --git a/lib/libnv/msgio.c b/lib/libnv/msgio.c new file mode 100644 index 000000000000..a37d1cd0bcf5 --- /dev/null +++ b/lib/libnv/msgio.c @@ -0,0 +1,407 @@ +/*- + * Copyright (c) 2013 The FreeBSD Foundation + * Copyright (c) 2013 Mariusz Zaborski + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_PJDLOG +#include +#endif + +#include "common_impl.h" +#include "msgio.h" + +#ifndef HAVE_PJDLOG +#include +#define PJDLOG_ASSERT(...) assert(__VA_ARGS__) +#define PJDLOG_RASSERT(expr, ...) assert(expr) +#define PJDLOG_ABORT(...) abort() +#endif + +static int +msghdr_add_fd(struct cmsghdr *cmsg, int fd) +{ + + PJDLOG_ASSERT(fd >= 0); + + if (!fd_is_valid(fd)) { + errno = EBADF; + return (-1); + } + + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(fd)); + bcopy(&fd, CMSG_DATA(cmsg), sizeof(fd)); + + return (0); +} + +static int +msghdr_get_fd(struct cmsghdr *cmsg) +{ + int fd; + + if (cmsg == NULL || cmsg->cmsg_level != SOL_SOCKET || + cmsg->cmsg_type != SCM_RIGHTS || + cmsg->cmsg_len != CMSG_LEN(sizeof(fd))) { + errno = EINVAL; + return (-1); + } + + bcopy(CMSG_DATA(cmsg), &fd, sizeof(fd)); +#ifndef MSG_CMSG_CLOEXEC + /* + * If the MSG_CMSG_CLOEXEC flag is not available we cannot set the + * close-on-exec flag atomically, but we still want to set it for + * consistency. + */ + (void) fcntl(fd, F_SETFD, FD_CLOEXEC); +#endif + + return (fd); +} + +static void +fd_wait(int fd, bool doread) +{ + fd_set fds; + + PJDLOG_ASSERT(fd >= 0); + + FD_ZERO(&fds); + FD_SET(fd, &fds); + (void)select(fd + 1, doread ? &fds : NULL, doread ? NULL : &fds, + NULL, NULL); +} + +int +msg_peek(int sock, void *buf, size_t size) +{ + ssize_t done; + + PJDLOG_ASSERT(sock >= 0); + PJDLOG_ASSERT(size > 0); + + do { + fd_wait(sock, true); + done = recv(sock, buf, size, MSG_PEEK | MSG_WAITALL); + if (done == -1) { + if (errno == EAGAIN || errno == EINTR) + continue; + return (-1); + } else if (done == 0) { + errno = ENOTCONN; + return (-1); + } + } while (done != (ssize_t)size); + + return (0); +} + +static int +msg_recv(int sock, struct msghdr *msg) +{ + int flags; + + PJDLOG_ASSERT(sock >= 0); + +#ifdef MSG_CMSG_CLOEXEC + flags = MSG_CMSG_CLOEXEC; +#else + flags = 0; +#endif + + for (;;) { + fd_wait(sock, true); + if (recvmsg(sock, msg, flags) == -1) { + if (errno == EINTR) + continue; + return (-1); + } + break; + } + + return (0); +} + +static int +msg_send(int sock, const struct msghdr *msg) +{ + + PJDLOG_ASSERT(sock >= 0); + + for (;;) { + fd_wait(sock, false); + if (sendmsg(sock, msg, 0) == -1) { + if (errno == EINTR) + continue; + return (-1); + } + break; + } + + return (0); +} + +int +cred_send(int sock) +{ + unsigned char credbuf[CMSG_SPACE(sizeof(struct cmsgcred))]; + struct msghdr msg; + struct cmsghdr *cmsg; + struct iovec iov; + uint8_t dummy; + + bzero(credbuf, sizeof(credbuf)); + bzero(&msg, sizeof(msg)); + bzero(&iov, sizeof(iov)); + + /* + * XXX: We send one byte along with the control message, because + * setting msg_iov to NULL only works if this is the first + * packet send over the socket. Once we send some data we + * won't be able to send credentials anymore. This is most + * likely a kernel bug. + */ + dummy = 0; + iov.iov_base = &dummy; + iov.iov_len = sizeof(dummy); + + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = credbuf; + msg.msg_controllen = sizeof(credbuf); + + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_len = CMSG_LEN(sizeof(struct cmsgcred)); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_CREDS; + + if (msg_send(sock, &msg) == -1) + return (-1); + + return (0); +} + +int +cred_recv(int sock, struct cmsgcred *cred) +{ + unsigned char credbuf[CMSG_SPACE(sizeof(struct cmsgcred))]; + struct msghdr msg; + struct cmsghdr *cmsg; + struct iovec iov; + uint8_t dummy; + + bzero(credbuf, sizeof(credbuf)); + bzero(&msg, sizeof(msg)); + bzero(&iov, sizeof(iov)); + + iov.iov_base = &dummy; + iov.iov_len = sizeof(dummy); + + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = credbuf; + msg.msg_controllen = sizeof(credbuf); + + if (msg_recv(sock, &msg) == -1) + return (-1); + + cmsg = CMSG_FIRSTHDR(&msg); + if (cmsg == NULL || + cmsg->cmsg_len != CMSG_LEN(sizeof(struct cmsgcred)) || + cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_CREDS) { + errno = EINVAL; + return (-1); + } + bcopy(CMSG_DATA(cmsg), cred, sizeof(*cred)); + + return (0); +} + +int +fd_send(int sock, const int *fds, size_t nfds) +{ + struct msghdr msg; + struct cmsghdr *cmsg; + unsigned int i; + int serrno, ret; + + if (nfds == 0 || fds == NULL) { + errno = EINVAL; + return (-1); + } + + bzero(&msg, sizeof(msg)); + msg.msg_iov = NULL; + msg.msg_iovlen = 0; + msg.msg_controllen = nfds * CMSG_SPACE(sizeof(int)); + msg.msg_control = calloc(1, msg.msg_controllen); + if (msg.msg_control == NULL) + return (-1); + + ret = -1; + + for (i = 0, cmsg = CMSG_FIRSTHDR(&msg); i < nfds && cmsg != NULL; + i++, cmsg = CMSG_NXTHDR(&msg, cmsg)) { + if (msghdr_add_fd(cmsg, fds[i]) == -1) + goto end; + } + + if (msg_send(sock, &msg) == -1) + goto end; + + ret = 0; +end: + serrno = errno; + free(msg.msg_control); + errno = serrno; + return (ret); +} + +int +fd_recv(int sock, int *fds, size_t nfds) +{ + struct msghdr msg; + struct cmsghdr *cmsg; + unsigned int i; + int serrno, ret; + + if (nfds == 0 || fds == NULL) { + errno = EINVAL; + return (-1); + } + + bzero(&msg, sizeof(msg)); + msg.msg_iov = NULL; + msg.msg_iovlen = 0; + msg.msg_controllen = nfds * CMSG_SPACE(sizeof(int)); + msg.msg_control = calloc(1, msg.msg_controllen); + if (msg.msg_control == NULL) + return (-1); + + ret = -1; + + if (msg_recv(sock, &msg) == -1) + goto end; + + for (i = 0, cmsg = CMSG_FIRSTHDR(&msg); i < nfds && cmsg != NULL; + i++, cmsg = CMSG_NXTHDR(&msg, cmsg)) { + fds[i] = msghdr_get_fd(cmsg); + if (fds[i] < 0) + break; + } + + if (cmsg != NULL || i < nfds) { + int fd; + + /* + * We need to close all received descriptors, even if we have + * different control message (eg. SCM_CREDS) in between. + */ + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; + cmsg = CMSG_NXTHDR(&msg, cmsg)) { + fd = msghdr_get_fd(cmsg); + if (fd >= 0) + close(fd); + } + errno = EINVAL; + goto end; + } + + ret = 0; +end: + serrno = errno; + free(msg.msg_control); + errno = serrno; + return (ret); +} + +int +buf_send(int sock, void *buf, size_t size) +{ + ssize_t done; + unsigned char *ptr; + + ptr = buf; + do { + fd_wait(sock, false); + done = send(sock, ptr, size, 0); + if (done == -1) { + if (errno == EINTR) + continue; + return (-1); + } else if (done == 0) { + errno = ENOTCONN; + return (-1); + } + size -= done; + ptr += done; + } while (size > 0); + + return (0); +} + +int +buf_recv(int sock, void *buf, size_t size) +{ + ssize_t done; + unsigned char *ptr; + + ptr = buf; + do { + fd_wait(sock, true); + done = recv(sock, ptr, size, 0); + if (done == -1) { + if (errno == EINTR) + continue; + return (-1); + } else if (done == 0) { + errno = ENOTCONN; + return (-1); + } + size -= done; + ptr += done; + } while (size > 0); + + return (0); +} diff --git a/lib/libnv/msgio.h b/lib/libnv/msgio.h new file mode 100644 index 000000000000..c6de92de10d7 --- /dev/null +++ b/lib/libnv/msgio.h @@ -0,0 +1,52 @@ +/*- + * Copyright (c) 2013 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Pawel Jakub Dawidek under sponsorship from + * the FreeBSD Foundation. + * + * Copyright (c) 2013 Mariusz Zaborski + * + * 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. + * + * $FreeBSD$ + */ + +#ifndef _MSGIO_H_ +#define _MSGIO_H_ + +struct cmsgcred; +struct iovec; +struct msghdr; + +int msg_peek(int sock, void *buf, size_t size); + +int cred_send(int sock); +int cred_recv(int sock, struct cmsgcred *cred); + +int fd_send(int sock, const int *fds, size_t nfds); +int fd_recv(int sock, int *fds, size_t nfds); + +int buf_send(int sock, void *buf, size_t size); +int buf_recv(int sock, void *buf, size_t size); + +#endif /* !_MSGIO_H_ */ diff --git a/lib/libnv/nv.3 b/lib/libnv/nv.3 new file mode 100644 index 000000000000..0da2af7054af --- /dev/null +++ b/lib/libnv/nv.3 @@ -0,0 +1,604 @@ +.\" +.\" Copyright (c) 2013 The FreeBSD Foundation +.\" All rights reserved. +.\" +.\" This documentation was written by Pawel Jakub Dawidek under sponsorship +.\" 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 AUTHOR 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 AUTHOR 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. +.\" +.\" $FreeBSD$ +.\" +.Dd October 8, 2013 +.Dt NV 3 +.Os +.Sh NAME +.Nm nvlist_create , +.Nm nvlist_destroy , +.Nm nvlist_error , +.Nm nvlist_empty , +.Nm nvlist_exists , +.Nm nvlist_free , +.Nm nvlist_clone , +.Nm nvlist_dump , +.Nm nvlist_fdump , +.Nm nvlist_size , +.Nm nvlist_pack , +.Nm nvlist_unpack , +.Nm nvlist_send , +.Nm nvlist_recv , +.Nm nvlist_xfer , +.Nm nvlist_next , +.Nm nvlist_add , +.Nm nvlist_move , +.Nm nvlist_get , +.Nm nvlist_take +.Nd "library for name/value pairs" +.Sh LIBRARY +.Lb libnv +.Sh SYNOPSIS +.In nv.h +.Ft "nvlist_t *" +.Fn nvlist_create "int flags" +.Ft void +.Fn nvlist_destroy "nvlist_t *nvl" +.Ft int +.Fn nvlist_error "const nvlist_t *nvl" +.Ft bool +.Fn nvlist_empty "const nvlist_t *nvl" +.\" +.Ft "nvlist_t *" +.Fn nvlist_clone "const nvlist_t *nvl" +.\" +.Ft void +.Fn nvlist_dump "const nvlist_t *nvl, int fd" +.Ft void +.Fn nvlist_fdump "const nvlist_t *nvl, FILE *fp" +.\" +.Ft size_t +.Fn nvlist_size "const nvlist_t *nvl" +.Ft "void *" +.Fn nvlist_pack "const nvlist_t *nvl" "size_t *sizep" +.Ft "nvlist_t *" +.Fn nvlist_unpack "const void *buf" "size_t size" +.\" +.Ft int +.Fn nvlist_send "int sock" "const nvlist_t *nvl" +.Ft "nvlist_t *" +.Fn nvlist_recv "int sock" +.Ft "nvlist_t *" +.Fn nvlist_xfer "int sock" "nvlist_t *nvl" +.\" +.Ft "const char *" +.Fn nvlist_next "const nvlist_t *nvl" "int *typep" "void **cookiep" +.\" +.Ft bool +.Fn nvlist_exists "const nvlist_t *nvl" "const char *name" +.Ft bool +.Fn nvlist_exists_type "const nvlist_t *nvl" "const char *name" "int type" +.Ft bool +.Fn nvlist_exists_null "const nvlist_t *nvl" "const char *name" +.Ft bool +.Fn nvlist_exists_bool "const nvlist_t *nvl" "const char *name" +.Ft bool +.Fn nvlist_exists_number "const nvlist_t *nvl" "const char *name" +.Ft bool +.Fn nvlist_exists_string "const nvlist_t *nvl" "const char *name" +.Ft bool +.Fn nvlist_exists_nvlist "const nvlist_t *nvl" "const char *name" +.Ft bool +.Fn nvlist_exists_descriptor "const nvlist_t *nvl" "const char *name" +.Ft bool +.Fn nvlist_exists_binary "const nvlist_t *nvl" "const char *name" +.\" +.Ft void +.Fn nvlist_add_null "nvlist_t *nvl" "const char *name" +.Ft void +.Fn nvlist_add_bool "nvlist_t *nvl" "const char *name" "bool value" +.Ft void +.Fn nvlist_add_number "nvlist_t *nvl" "const char *name" "uint64_t value" +.Ft void +.Fn nvlist_add_string "nvlist_t *nvl" "const char *name" "const char *value" +.Ft void +.Fn nvlist_add_stringf "nvlist_t *nvl" "const char *name" "const char *valuefmt" "..." +.Ft void +.Fn nvlist_add_stringv "nvlist_t *nvl" "const char *name" "const char *valuefmt" "va_list valueap" +.Ft void +.Fn nvlist_add_nvlist "nvlist_t *nvl" "const char *name" "const nvlist_t *value" +.Ft void +.Fn nvlist_add_descriptor "nvlist_t *nvl" "const char *name" "int value" +.Ft void +.Fn nvlist_add_binary "nvlist_t *nvl" "const char *name" "const void *value" "size_t size" +.\" +.Ft void +.Fn nvlist_move_string "nvlist_t *nvl" "const char *name" "char *value" +.Ft void +.Fn nvlist_move_nvlist "nvlist_t *nvl" "const char *name" "nvlist_t *value" +.Ft void +.Fn nvlist_move_descriptor "nvlist_t *nvl" "const char *name" "int value" +.Ft void +.Fn nvlist_move_binary "nvlist_t *nvl" "const char *name" "void *value" "size_t size" +.\" +.Ft bool +.Fn nvlist_get_bool "const nvlist_t *nvl" "const char *name" +.Ft uint64_t +.Fn nvlist_get_number "const nvlist_t *nvl" "const char *name" +.Ft "const char *" +.Fn nvlist_get_string "const nvlist_t *nvl" "const char *name" +.Ft "const nvlist_t *" +.Fn nvlist_get_nvlist "const nvlist_t *nvl" "const char *name" +.Ft int +.Fn nvlist_get_descriptor "const nvlist_t *nvl" "const char *name" +.Ft "const void *" +.Fn nvlist_get_binary "const nvlist_t *nvl" "const char *name" "size_t *sizep" +.\" +.Ft bool +.Fn nvlist_take_bool "nvlist_t *nvl" "const char *name" +.Ft uint64_t +.Fn nvlist_take_number "nvlist_t *nvl" "const char *name" +.Ft "char *" +.Fn nvlist_take_string "nvlist_t *nvl" "const char *name" +.Ft "nvlist_t *" +.Fn nvlist_take_nvlist "nvlist_t *nvl" "const char *name" +.Ft int +.Fn nvlist_take_descriptor "nvlist_t *nvl" "const char *name" +.Ft "void *" +.Fn nvlist_take_binary "nvlist_t *nvl" "const char *name" "size_t *sizep" +.\" +.Ft void +.Fn nvlist_free "nvlist_t *nvl" "const char *name" +.Ft void +.Fn nvlist_free_type "nvlist_t *nvl" "const char *name" "int type" +.\" +.Ft void +.Fn nvlist_free_null "nvlist_t *nvl" "const char *name" +.Ft void +.Fn nvlist_free_bool "nvlist_t *nvl" "const char *name" +.Ft void +.Fn nvlist_free_number "nvlist_t *nvl" "const char *name" +.Ft void +.Fn nvlist_free_string "nvlist_t *nvl" "const char *name" +.Ft void +.Fn nvlist_free_nvlist "nvlist_t *nvl" "const char *name" +.Ft void +.Fn nvlist_free_descriptor "nvlist_t *nvl" "const char *name" +.Ft void +.Fn nvlist_free_binary "nvlist_t *nvl" "const char *name" +.Sh DESCRIPTION +The +.Nm libnv +library allows to easily manage name value pairs as well as send and receive +them over sockets. +A group (list) of name value pairs is called an +.Nm nvlist . +The API supports the following data types: +.Bl -ohang -offset indent +.It Sy null ( NV_TYPE_NULL ) +There is no data associated with the name. +.It Sy bool ( NV_TYPE_BOLL ) +The value can be either +.Dv true +or +.Dv false . +.It Sy number ( NV_TYPE_NUMBER ) +The value is a number stored as +.Vt uint64_t . +.It Sy string ( NV_TYPE_STRING ) +The value is a C string. +.It Sy nvlist ( NV_TYPE_NVLIST ) +The value is a nested nvlist. +.It Sy descriptor ( NV_TYPE_DESCRIPTOR ) +The value is a file descriptor. +Note that file descriptors can be sent only over +.Xr unix 4 +domain sockets. +.It Sy binary ( NV_TYPE_BINARY ) +The value is a binary buffer. +.El +.Pp +The +.Fn nvlist_create +function allocates memory and initializes an nvlist. +.Pp +The following flag can be provided: +.Pp +.Bl -tag -width "NV_FLAG_IGNORE_CASE" -compact -offset indent +.It Dv NV_FLAG_IGNORE_CASE +Perform case-insensitive lookups of provided names. +.El +.Pp +The +.Fn nvlist_destroy +function destroys the given nvlist. +Function does nothing if +.Dv NULL +nvlist is provided. +Function never modifies the +.Va errno +global variable. +.Pp +The +.Fn nvlist_error +function returns any error value that the nvlist accumulated. +If the given nvlist is +.Dv NULL +the +.Er ENOMEM +error will be returned. +.Pp +The +.Fn nvlist_empty +functions returns +.Dv true +if the given nvlist is empty and +.Dv false +otherwise. +The nvlist must not be in error state. +.Pp +The +.Fn nvlist_clone +functions clones the given nvlist. +The clone shares no resources with its origin. +This also means that all file descriptors that are part of the nvlist will be +duplicated with the +.Xr dup 2 +system call before placing them in the clone. +.Pp +The +.Fn nvlist_dump +dumps nvlist content for debugging purposes to the given file descriptor +.Fa fd . +.Pp +The +.Fn nvlist_fdump +dumps nvlist content for debugging purposes to the given file stream +.Fa fp . +.Pp +The +.Fn nvlist_size +function returns the size of the given nvlist after converting it to binary +buffer with the +.Fn nvlist_pack +function. +.Pp +The +.Fn nvlist_pack +function converts the given nvlist to a binary buffer. +The function allocates memory for the buffer, which should be freed with the +.Xr free 3 +function. +If the +.Fa sizep +argument is not +.Dv NULL , +the size of the buffer will be stored there. +The function returns +.Dv NULL +in case of an error (allocation failure). +If the nvlist contains any file descriptors +.Dv NULL +will be returned. +The nvlist must not be in error state. +.Pp +The +.Fn nvlist_unpack +function converts the given buffer to the nvlist. +The function returns +.Dv NULL +in case of an error. +.Pp +The +.Fn nvlist_send +function sends the given nvlist over the socket given by the +.Fa sock +argument. +Note that nvlist that contains file descriptors can only be send over +.Xr unix 4 +domain sockets. +.Pp +The +.Fn nvlist_recv +function receives nvlist over the socket given by the +.Fa sock +argument. +.Pp +The +.Fn nvlist_xfer +function sends the given nvlist over the socket given by the +.Fa sock +argument and receives nvlist over the same socket. +The given nvlist is always destroyed. +.Pp +The +.Fn nvlist_next +function iterates over the given nvlist returning names and types of subsequent +elements. +The +.Fa cookiep +argument allows the function to figure out which element should be returned +next. +The +.Va *cookiep +should be set to +.Dv NULL +for the first call and should not be changed later. +Returning +.Dv NULL +means there are no more elements on the nvlist. +The +.Fa typep +argument can be NULL. +Elements may not be removed from the nvlist while traversing it. +The nvlist must not be in error state. +.Pp +The +.Fn nvlist_exists +function returns +.Dv true +if element of the given name exists (besides of its type) or +.Dv false +otherwise. +The nvlist must not be in error state. +.Pp +The +.Fn nvlist_exists_type +function returns +.Dv true +if element of the given name and the given type exists or +.Dv false +otherwise. +The nvlist must not be in error state. +.Pp +The +.Fn nvlist_exists_null , +.Fn nvlist_exists_bool , +.Fn nvlist_exists_number , +.Fn nvlist_exists_string , +.Fn nvlist_exists_nvlist , +.Fn nvlist_exists_descriptor , +.Fn nvlist_exists_binary +functions return +.Dv true +if element of the given name and the given type determined by the function name +exists or +.Dv false +otherwise. +The nvlist must not be in error state. +.Pp +The +.Fn nvlist_add_null , +.Fn nvlist_add_bool , +.Fn nvlist_add_number , +.Fn nvlist_add_string , +.Fn nvlist_add_stringf , +.Fn nvlist_add_stringv , +.Fn nvlist_add_nvlist , +.Fn nvlist_add_descriptor , +.Fn nvlist_add_binary +functions add element to the given nvlist. +When adding string or binary buffor the functions will allocate memory +and copy the data over. +When adding nvlist, the nvlist will be cloned and clone will be added. +When adding descriptor, the descriptor will be duplicated using the +.Xr dup 2 +system call and the new descriptor will be added. +If an error occurs while adding new element, internal error is set which can be +examined using the +.Fn nvlist_error +function. +.Pp +The +.Fn nvlist_move_string , +.Fn nvlist_move_nvlist , +.Fn nvlist_move_descriptor , +.Fn nvlist_move_binary +functions add new element to the given nvlist, but unlike +.Fn nvlist_add_ +functions they will consume the given resource. +If an error occurs while adding new element, the resource is destroyed and +internal error is set which can be examined using the +.Fn nvlist_error +function. +.Pp +The +.Fn nvlist_get_bool , +.Fn nvlist_get_number , +.Fn nvlist_get_string , +.Fn nvlist_get_nvlist , +.Fn nvlist_get_descriptor , +.Fn nvlist_get_binary +functions allow to obtain value of the given name. +In case of string, nvlist, descriptor or binary, returned resource should +not be modified - it still belongs to the nvlist. +If element of the given name does not exist, the program will be aborted. +To avoid that the caller should check for existence before trying to obtain +the value or use +.Xr dnvlist 3 +extension, which allows to provide default value for a missing element. +The nvlist must not be in error state. +.Pp +The +.Fn nvlist_take_bool , +.Fn nvlist_take_number , +.Fn nvlist_take_string , +.Fn nvlist_take_nvlist , +.Fn nvlist_take_descriptor , +.Fn nvlist_take_binary +functions return value associated with the given name and remove the element +from the nvlist. +In case of string and binary values, the caller is responsible for free returned +memory using the +.Xr free 3 +function. +In case of nvlist, the caller is responsible for destroying returned nvlist +using the +.Fn nvlist_destroy +function. +In case of descriptor, the caller is responsible for closing returned descriptor +using the +.Fn close 2 +system call. +If element of the given name does not exist, the program will be aborted. +To avoid that the caller should check for existence before trying to obtain +the value or use +.Xr dnvlist 3 +extension, which allows to provide default value for a missing element. +The nvlist must not be in error state. +.Pp +The +.Fn nvlist_free +function removes element of the given name from the nvlist (besides of its type) +and frees all resources associated with it. +If element of the given name does not exist, the program will be aborted. +The nvlist must not be in error state. +.Pp +The +.Fn nvlist_free_type +function removes element of the given name and the given type from the nvlist +and frees all resources associated with it. +If element of the given name and the given type does not exist, the program +will be aborted. +The nvlist must not be in error state. +.Pp +The +.Fn nvlist_free_null , +.Fn nvlist_free_bool , +.Fn nvlist_free_number , +.Fn nvlist_free_string , +.Fn nvlist_free_nvlist , +.Fn nvlist_free_descriptor , +.Fn nvlist_free_binary +functions remove element of the given name and the given type determined by the +function name from the nvlist and free all resources associated with it. +If element of the given name and the given type does not exist, the program +will be aborted. +The nvlist must not be in error state. +.Sh EXAMPLES +The following example demonstrates how to prepare an nvlist and send it over +.Xr unix 4 +domain socket. +.Bd -literal +nvlist_t *nvl; +int fd; + +fd = open("/tmp/foo", O_RDONLY); +if (fd < 0) + err(1, "open(\\"/tmp/foo\\") failed"); + +nvl = nvlist_create(0); +/* + * There is no need to check if nvlist_create() succeeded, + * as the nvlist_add_() functions can cope. + * If it failed, nvlist_send() will fail. + */ +nvlist_add_string(nvl, "filename", "/tmp/foo"); +nvlist_add_number(nvl, "flags", O_RDONLY); +/* + * We just want to send the descriptor, so we can give it + * for the nvlist to consume (that's why we use nvlist_move + * not nvlist_add). + */ +nvlist_move_descriptor(nvl, "fd", fd); +if (nvlist_send(sock, nvl) < 0) { + nvlist_destroy(nvl); + err(1, "nvlist_send() failed"); +} +nvlist_destroy(nvl); +.Ed +.Pp +Receiving nvlist and getting data: +.Bd -literal +nvlist_t *nvl; +const char *command; +char *filename; +int fd; + +nvl = nvlist_recv(sock); +if (nvl == NULL) + err(1, "nvlist_recv() failed"); + +/* For command we take pointer to nvlist's buffer. */ +command = nvlist_get_string(nvl, "command"); +/* + * For filename we remove it from the nvlist and take + * ownership of the buffer. + */ +filename = nvlist_take_string(nvl, "filename"); +/* The same for the descriptor. */ +fd = nvlist_take_descriptor(nvl, "fd"); + +printf("command=%s filename=%s fd=%d\n", command, filename, fd); + +nvlist_destroy(nvl); +free(filename); +close(fd); +/* command was freed by nvlist_destroy() */ +.Ed +.Pp +Iterating over nvlist: +.Bd -literal +nvlist_t *nvl; +const char *name; +void *cookie; +int type; + +nvl = nvlist_recv(sock); +if (nvl == NULL) + err(1, "nvlist_recv() failed"); + +cookie = NULL; +while ((name = nvlist_next(nvl, &type, &cookie)) != NULL) { + printf("%s=", name); + switch (type) { + case NV_TYPE_NUMBER: + printf("%ju", (uintmax_t)nvlist_get_number(nvl, name)); + break; + case NV_TYPE_STRING: + printf("%s", nvlist_get_string(nvl, name)); + break; + default: + printf("N/A"); + break; + } + printf("\\n"); +} +.Ed +.Sh SEE ALSO +.Xr close 2 , +.Xr dup 2 , +.Xr open 2 , +.Xr err 3 , +.Xr free 3 , +.Xr printf 3 , +.Xr unix 4 +.Sh HISTORY +The +.Nm libnv +library appeared in +.Fx 10.0 . +.Sh AUTHORS +.An -nosplit +The +.Nm libnv +library was implemented by +.An Pawel Jakub Dawidek Aq pawel@dawidek.net +under sponsorship from the FreeBSD Foundation. diff --git a/lib/libnv/nv.h b/lib/libnv/nv.h new file mode 100644 index 000000000000..e2d7030f665b --- /dev/null +++ b/lib/libnv/nv.h @@ -0,0 +1,273 @@ +/*- + * Copyright (c) 2009-2013 The FreeBSD Foundation + * 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. + * + * $FreeBSD$ + */ + +#ifndef _NV_H_ +#define _NV_H_ + +#include + +#include +#include +#include +#include + +#ifndef _NVLIST_T_DECLARED +#define _NVLIST_T_DECLARED +struct nvlist; + +typedef struct nvlist nvlist_t; +#endif + +#define NV_NAME_MAX 2048 + +#define NV_TYPE_NONE 0 + +#define NV_TYPE_NULL 1 +#define NV_TYPE_BOOL 2 +#define NV_TYPE_NUMBER 3 +#define NV_TYPE_STRING 4 +#define NV_TYPE_NVLIST 5 +#define NV_TYPE_DESCRIPTOR 6 +#define NV_TYPE_BINARY 7 + +/* + * Perform case-insensitive lookups of provided names. + */ +#define NV_FLAG_IGNORE_CASE 0x01 + +nvlist_t *nvlist_create(int flags); +void nvlist_destroy(nvlist_t *nvl); +int nvlist_error(const nvlist_t *nvl); +bool nvlist_empty(const nvlist_t *nvl); + +nvlist_t *nvlist_clone(const nvlist_t *nvl); + +void nvlist_dump(const nvlist_t *nvl, int fd); +void nvlist_fdump(const nvlist_t *nvl, FILE *fp); + +size_t nvlist_size(const nvlist_t *nvl); +void *nvlist_pack(const nvlist_t *nvl, size_t *sizep); +nvlist_t *nvlist_unpack(const void *buf, size_t size); + +int nvlist_send(int sock, const nvlist_t *nvl); +nvlist_t *nvlist_recv(int sock); +nvlist_t *nvlist_xfer(int sock, nvlist_t *nvl); + +const char *nvlist_next(const nvlist_t *nvl, int *typep, void **cookiep); + +/* + * The nvlist_exists functions check if the given name (optionally of the given + * type) exists on nvlist. + */ + +bool nvlist_exists(const nvlist_t *nvl, const char *name); +bool nvlist_exists_type(const nvlist_t *nvl, const char *name, int type); + +bool nvlist_exists_null(const nvlist_t *nvl, const char *name); +bool nvlist_exists_bool(const nvlist_t *nvl, const char *name); +bool nvlist_exists_number(const nvlist_t *nvl, const char *name); +bool nvlist_exists_string(const nvlist_t *nvl, const char *name); +bool nvlist_exists_nvlist(const nvlist_t *nvl, const char *name); +bool nvlist_exists_descriptor(const nvlist_t *nvl, const char *name); +bool nvlist_exists_binary(const nvlist_t *nvl, const char *name); + +/* + * The nvlist_add functions add the given name/value pair. + * If a pointer is provided, nvlist_add will internally allocate memory for the + * given data (in other words it won't consume provided buffer). + */ + +void nvlist_add_null(nvlist_t *nvl, const char *name); +void nvlist_add_bool(nvlist_t *nvl, const char *name, bool value); +void nvlist_add_number(nvlist_t *nvl, const char *name, uint64_t value); +void nvlist_add_string(nvlist_t *nvl, const char *name, const char *value); +void nvlist_add_stringf(nvlist_t *nvl, const char *name, const char *valuefmt, ...) __printflike(3, 4); +void nvlist_add_stringv(nvlist_t *nvl, const char *name, const char *valuefmt, va_list valueap) __printflike(3, 0); +void nvlist_add_nvlist(nvlist_t *nvl, const char *name, const nvlist_t *value); +void nvlist_add_descriptor(nvlist_t *nvl, const char *name, int value); +void nvlist_add_binary(nvlist_t *nvl, const char *name, const void *value, size_t size); + +/* + * The nvlist_move functions add the given name/value pair. + * The functions consumes provided buffer. + */ + +void nvlist_move_string(nvlist_t *nvl, const char *name, char *value); +void nvlist_move_nvlist(nvlist_t *nvl, const char *name, nvlist_t *value); +void nvlist_move_descriptor(nvlist_t *nvl, const char *name, int value); +void nvlist_move_binary(nvlist_t *nvl, const char *name, void *value, size_t size); + +/* + * The nvlist_get functions returns value associated with the given name. + * If it returns a pointer, the pointer represents internal buffer and should + * not be freed by the caller. + */ + +bool nvlist_get_bool(const nvlist_t *nvl, const char *name); +uint64_t nvlist_get_number(const nvlist_t *nvl, const char *name); +const char *nvlist_get_string(const nvlist_t *nvl, const char *name); +const nvlist_t *nvlist_get_nvlist(const nvlist_t *nvl, const char *name); +int nvlist_get_descriptor(const nvlist_t *nvl, const char *name); +const void *nvlist_get_binary(const nvlist_t *nvl, const char *name, size_t *sizep); + +/* + * The nvlist_take functions returns value associated with the given name and + * remove the given entry from the nvlist. + * The caller is responsible for freeing received data. + */ + +bool nvlist_take_bool(nvlist_t *nvl, const char *name); +uint64_t nvlist_take_number(nvlist_t *nvl, const char *name); +char *nvlist_take_string(nvlist_t *nvl, const char *name); +nvlist_t *nvlist_take_nvlist(nvlist_t *nvl, const char *name); +int nvlist_take_descriptor(nvlist_t *nvl, const char *name); +void *nvlist_take_binary(nvlist_t *nvl, const char *name, size_t *sizep); + +/* + * The nvlist_free functions removes the given name/value pair from the nvlist + * and frees memory associated with it. + */ + +void nvlist_free(nvlist_t *nvl, const char *name); +void nvlist_free_type(nvlist_t *nvl, const char *name, int type); + +void nvlist_free_null(nvlist_t *nvl, const char *name); +void nvlist_free_bool(nvlist_t *nvl, const char *name); +void nvlist_free_number(nvlist_t *nvl, const char *name); +void nvlist_free_string(nvlist_t *nvl, const char *name); +void nvlist_free_nvlist(nvlist_t *nvl, const char *name); +void nvlist_free_descriptor(nvlist_t *nvl, const char *name); +void nvlist_free_binary(nvlist_t *nvl, const char *name); + +/* + * Below are the same functions, but which operate on format strings and + * variable argument lists. + */ + +bool nvlist_existsf(const nvlist_t *nvl, const char *namefmt, ...) __printflike(2, 3); +bool nvlist_existsf_type(const nvlist_t *nvl, int type, const char *namefmt, ...) __printflike(3, 4); + +bool nvlist_existsf_null(const nvlist_t *nvl, const char *namefmt, ...) __printflike(2, 3); +bool nvlist_existsf_bool(const nvlist_t *nvl, const char *namefmt, ...) __printflike(2, 3); +bool nvlist_existsf_number(const nvlist_t *nvl, const char *namefmt, ...) __printflike(2, 3); +bool nvlist_existsf_string(const nvlist_t *nvl, const char *namefmt, ...) __printflike(2, 3); +bool nvlist_existsf_nvlist(const nvlist_t *nvl, const char *namefmt, ...) __printflike(2, 3); +bool nvlist_existsf_descriptor(const nvlist_t *nvl, const char *namefmt, ...) __printflike(2, 3); +bool nvlist_existsf_binary(const nvlist_t *nvl, const char *namefmt, ...) __printflike(2, 3); + +bool nvlist_existsv(const nvlist_t *nvl, const char *namefmt, va_list nameap) __printflike(2, 0); +bool nvlist_existsv_type(const nvlist_t *nvl, int type, const char *namefmt, va_list nameap) __printflike(3, 0); + +bool nvlist_existsv_null(const nvlist_t *nvl, const char *namefmt, va_list nameap) __printflike(2, 0); +bool nvlist_existsv_bool(const nvlist_t *nvl, const char *namefmt, va_list nameap) __printflike(2, 0); +bool nvlist_existsv_number(const nvlist_t *nvl, const char *namefmt, va_list nameap) __printflike(2, 0); +bool nvlist_existsv_string(const nvlist_t *nvl, const char *namefmt, va_list nameap) __printflike(2, 0); +bool nvlist_existsv_nvlist(const nvlist_t *nvl, const char *namefmt, va_list nameap) __printflike(2, 0); +bool nvlist_existsv_descriptor(const nvlist_t *nvl, const char *namefmt, va_list nameap) __printflike(2, 0); +bool nvlist_existsv_binary(const nvlist_t *nvl, const char *namefmt, va_list nameap) __printflike(2, 0); + +void nvlist_addf_null(nvlist_t *nvl, const char *namefmt, ...) __printflike(2, 3); +void nvlist_addf_bool(nvlist_t *nvl, bool value, const char *namefmt, ...) __printflike(3, 4); +void nvlist_addf_number(nvlist_t *nvl, uint64_t value, const char *namefmt, ...) __printflike(3, 4); +void nvlist_addf_string(nvlist_t *nvl, const char *value, const char *namefmt, ...) __printflike(3, 4); +void nvlist_addf_nvlist(nvlist_t *nvl, const nvlist_t *value, const char *namefmt, ...) __printflike(3, 4); +void nvlist_addf_descriptor(nvlist_t *nvl, int value, const char *namefmt, ...) __printflike(3, 4); +void nvlist_addf_binary(nvlist_t *nvl, const void *value, size_t size, const char *namefmt, ...) __printflike(4, 5); + +void nvlist_addv_null(nvlist_t *nvl, const char *namefmt, va_list nameap) __printflike(2, 0); +void nvlist_addv_bool(nvlist_t *nvl, bool value, const char *namefmt, va_list nameap) __printflike(3, 0); +void nvlist_addv_number(nvlist_t *nvl, uint64_t value, const char *namefmt, va_list nameap) __printflike(3, 0); +void nvlist_addv_string(nvlist_t *nvl, const char *value, const char *namefmt, va_list nameap) __printflike(3, 0); +void nvlist_addv_nvlist(nvlist_t *nvl, const nvlist_t *value, const char *namefmt, va_list nameap) __printflike(3, 0); +void nvlist_addv_descriptor(nvlist_t *nvl, int value, const char *namefmt, va_list nameap) __printflike(3, 0); +void nvlist_addv_binary(nvlist_t *nvl, const void *value, size_t size, const char *namefmt, va_list nameap) __printflike(4, 0); + +void nvlist_movef_string(nvlist_t *nvl, char *value, const char *namefmt, ...) __printflike(3, 4); +void nvlist_movef_nvlist(nvlist_t *nvl, nvlist_t *value, const char *namefmt, ...) __printflike(3, 4); +void nvlist_movef_descriptor(nvlist_t *nvl, int value, const char *namefmt, ...) __printflike(3, 4); +void nvlist_movef_binary(nvlist_t *nvl, void *value, size_t size, const char *namefmt, ...) __printflike(4, 5); + +void nvlist_movev_string(nvlist_t *nvl, char *value, const char *namefmt, va_list nameap) __printflike(3, 0); +void nvlist_movev_nvlist(nvlist_t *nvl, nvlist_t *value, const char *namefmt, va_list nameap) __printflike(3, 0); +void nvlist_movev_descriptor(nvlist_t *nvl, int value, const char *namefmt, va_list nameap) __printflike(3, 0); +void nvlist_movev_binary(nvlist_t *nvl, void *value, size_t size, const char *namefmt, va_list nameap) __printflike(4, 0); + +bool nvlist_getf_bool(const nvlist_t *nvl, const char *namefmt, ...) __printflike(2, 3); +uint64_t nvlist_getf_number(const nvlist_t *nvl, const char *namefmt, ...) __printflike(2, 3); +const char *nvlist_getf_string(const nvlist_t *nvl, const char *namefmt, ...) __printflike(2, 3); +const nvlist_t *nvlist_getf_nvlist(const nvlist_t *nvl, const char *namefmt, ...) __printflike(2, 3); +int nvlist_getf_descriptor(const nvlist_t *nvl, const char *namefmt, ...) __printflike(2, 3); +const void *nvlist_getf_binary(const nvlist_t *nvl, size_t *sizep, const char *namefmt, ...) __printflike(3, 4); + +bool nvlist_getv_bool(const nvlist_t *nvl, const char *namefmt, va_list nameap) __printflike(2, 0); +uint64_t nvlist_getv_number(const nvlist_t *nvl, const char *namefmt, va_list nameap) __printflike(2, 0); +const char *nvlist_getv_string(const nvlist_t *nvl, const char *namefmt, va_list nameap) __printflike(2, 0); +const nvlist_t *nvlist_getv_nvlist(const nvlist_t *nvl, const char *namefmt, va_list nameap) __printflike(2, 0); +int nvlist_getv_descriptor(const nvlist_t *nvl, const char *namefmt, va_list nameap) __printflike(2, 0); +const void *nvlist_getv_binary(const nvlist_t *nvl, size_t *sizep, const char *namefmt, va_list nameap) __printflike(3, 0); + +bool nvlist_takef_bool(nvlist_t *nvl, const char *namefmt, ...) __printflike(2, 3); +uint64_t nvlist_takef_number(nvlist_t *nvl, const char *namefmt, ...) __printflike(2, 3); +char *nvlist_takef_string(nvlist_t *nvl, const char *namefmt, ...) __printflike(2, 3); +nvlist_t *nvlist_takef_nvlist(nvlist_t *nvl, const char *namefmt, ...) __printflike(2, 3); +int nvlist_takef_descriptor(nvlist_t *nvl, const char *namefmt, ...) __printflike(2, 3); +void *nvlist_takef_binary(nvlist_t *nvl, size_t *sizep, const char *namefmt, ...) __printflike(3, 4); + +bool nvlist_takev_bool(nvlist_t *nvl, const char *namefmt, va_list nameap) __printflike(2, 0); +uint64_t nvlist_takev_number(nvlist_t *nvl, const char *namefmt, va_list nameap) __printflike(2, 0); +char *nvlist_takev_string(nvlist_t *nvl, const char *namefmt, va_list nameap) __printflike(2, 0); +nvlist_t *nvlist_takev_nvlist(nvlist_t *nvl, const char *namefmt, va_list nameap) __printflike(2, 0); +int nvlist_takev_descriptor(nvlist_t *nvl, const char *namefmt, va_list nameap) __printflike(2, 0); +void *nvlist_takev_binary(nvlist_t *nvl, size_t *sizep, const char *namefmt, va_list nameap) __printflike(3, 0); + +void nvlist_freef(nvlist_t *nvl, const char *namefmt, ...) __printflike(2, 3); +void nvlist_freef_type(nvlist_t *nvl, int type, const char *namefmt, ...) __printflike(3, 4); + +void nvlist_freef_null(nvlist_t *nvl, const char *namefmt, ...) __printflike(2, 3); +void nvlist_freef_bool(nvlist_t *nvl, const char *namefmt, ...) __printflike(2, 3); +void nvlist_freef_number(nvlist_t *nvl, const char *namefmt, ...) __printflike(2, 3); +void nvlist_freef_string(nvlist_t *nvl, const char *namefmt, ...) __printflike(2, 3); +void nvlist_freef_nvlist(nvlist_t *nvl, const char *namefmt, ...) __printflike(2, 3); +void nvlist_freef_descriptor(nvlist_t *nvl, const char *namefmt, ...) __printflike(2, 3); +void nvlist_freef_binary(nvlist_t *nvl, const char *namefmt, ...) __printflike(2, 3); + +void nvlist_freev(nvlist_t *nvl, const char *namefmt, va_list nameap) __printflike(2, 0); +void nvlist_freev_type(nvlist_t *nvl, int type, const char *namefmt, va_list nameap) __printflike(3, 0); + +void nvlist_freev_null(nvlist_t *nvl, const char *namefmt, va_list nameap) __printflike(2, 0); +void nvlist_freev_bool(nvlist_t *nvl, const char *namefmt, va_list nameap) __printflike(2, 0); +void nvlist_freev_number(nvlist_t *nvl, const char *namefmt, va_list nameap) __printflike(2, 0); +void nvlist_freev_string(nvlist_t *nvl, const char *namefmt, va_list nameap) __printflike(2, 0); +void nvlist_freev_nvlist(nvlist_t *nvl, const char *namefmt, va_list nameap) __printflike(2, 0); +void nvlist_freev_descriptor(nvlist_t *nvl, const char *namefmt, va_list nameap) __printflike(2, 0); +void nvlist_freev_binary(nvlist_t *nvl, const char *namefmt, va_list nameap) __printflike(2, 0); + +#endif /* !_NV_H_ */ diff --git a/lib/libnv/nv_impl.h b/lib/libnv/nv_impl.h new file mode 100644 index 000000000000..32060061f413 --- /dev/null +++ b/lib/libnv/nv_impl.h @@ -0,0 +1,130 @@ +/*- + * Copyright (c) 2013 The FreeBSD Foundation + * 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. + * + * $FreeBSD$ + */ + +#ifndef _NV_IMPL_H_ +#define _NV_IMPL_H_ + +#ifndef _NVPAIR_T_DECLARED +#define _NVPAIR_T_DECLARED +struct nvpair; + +typedef struct nvpair nvpair_t; +#endif + +#define NV_TYPE_FIRST NV_TYPE_NULL +#define NV_TYPE_LAST NV_TYPE_BINARY + +#define NV_FLAG_BIG_ENDIAN 0x80 + +int *nvlist_descriptors(const nvlist_t *nvl, size_t *nitemsp); +size_t nvlist_ndescriptors(const nvlist_t *nvl); + +nvpair_t *nvlist_first_nvpair(const nvlist_t *nvl); +nvpair_t *nvlist_next_nvpair(const nvlist_t *nvl, const nvpair_t *nvp); +nvpair_t *nvlist_prev_nvpair(const nvlist_t *nvl, const nvpair_t *nvp); + +void nvlist_add_nvpair(nvlist_t *nvl, const nvpair_t *nvp); + +void nvlist_move_nvpair(nvlist_t *nvl, nvpair_t *nvp); + +const nvpair_t *nvlist_get_nvpair(const nvlist_t *nvl, const char *name); + +nvpair_t *nvlist_take_nvpair(nvlist_t *nvl, const char *name); + +/* Function removes the given nvpair from the nvlist. */ +void nvlist_remove_nvpair(nvlist_t *nvl, nvpair_t *nvp); + +void nvlist_free_nvpair(nvlist_t *nvl, nvpair_t *nvp); + +int nvpair_type(const nvpair_t *nvp); +const char *nvpair_name(const nvpair_t *nvp); + +nvpair_t *nvpair_clone(const nvpair_t *nvp); + +nvpair_t *nvpair_create_null(const char *name); +nvpair_t *nvpair_create_bool(const char *name, bool value); +nvpair_t *nvpair_create_number(const char *name, uint64_t value); +nvpair_t *nvpair_create_string(const char *name, const char *value); +nvpair_t *nvpair_create_stringf(const char *name, const char *valuefmt, ...) __printflike(2, 3); +nvpair_t *nvpair_create_stringv(const char *name, const char *valuefmt, va_list valueap) __printflike(2, 0); +nvpair_t *nvpair_create_nvlist(const char *name, const nvlist_t *value); +nvpair_t *nvpair_create_descriptor(const char *name, int value); +nvpair_t *nvpair_create_binary(const char *name, const void *value, size_t size); + +nvpair_t *nvpair_move_string(const char *name, char *value); +nvpair_t *nvpair_move_nvlist(const char *name, nvlist_t *value); +nvpair_t *nvpair_move_descriptor(const char *name, int value); +nvpair_t *nvpair_move_binary(const char *name, void *value, size_t size); + +bool nvpair_get_bool(const nvpair_t *nvp); +uint64_t nvpair_get_number(const nvpair_t *nvp); +const char *nvpair_get_string(const nvpair_t *nvp); +const nvlist_t *nvpair_get_nvlist(const nvpair_t *nvp); +int nvpair_get_descriptor(const nvpair_t *nvp); +const void *nvpair_get_binary(const nvpair_t *nvp, size_t *sizep); + +void nvpair_free(nvpair_t *nvp); + +const nvpair_t *nvlist_getf_nvpair(const nvlist_t *nvl, const char *namefmt, ...) __printflike(2, 3); + +const nvpair_t *nvlist_getv_nvpair(const nvlist_t *nvl, const char *namefmt, va_list nameap) __printflike(2, 0); + +nvpair_t *nvlist_takef_nvpair(nvlist_t *nvl, const char *namefmt, ...) __printflike(2, 3); + +nvpair_t *nvlist_takev_nvpair(nvlist_t *nvl, const char *namefmt, va_list nameap) __printflike(2, 0); + +nvpair_t *nvpair_createf_null(const char *namefmt, ...) __printflike(1, 2); +nvpair_t *nvpair_createf_bool(bool value, const char *namefmt, ...) __printflike(2, 3); +nvpair_t *nvpair_createf_number(uint64_t value, const char *namefmt, ...) __printflike(2, 3); +nvpair_t *nvpair_createf_string(const char *value, const char *namefmt, ...) __printflike(2, 3); +nvpair_t *nvpair_createf_nvlist(const nvlist_t *value, const char *namefmt, ...) __printflike(2, 3); +nvpair_t *nvpair_createf_descriptor(int value, const char *namefmt, ...) __printflike(2, 3); +nvpair_t *nvpair_createf_binary(const void *value, size_t size, const char *namefmt, ...) __printflike(3, 4); + +nvpair_t *nvpair_createv_null(const char *namefmt, va_list nameap) __printflike(1, 0); +nvpair_t *nvpair_createv_bool(bool value, const char *namefmt, va_list nameap) __printflike(2, 0); +nvpair_t *nvpair_createv_number(uint64_t value, const char *namefmt, va_list nameap) __printflike(2, 0); +nvpair_t *nvpair_createv_string(const char *value, const char *namefmt, va_list nameap) __printflike(2, 0); +nvpair_t *nvpair_createv_nvlist(const nvlist_t *value, const char *namefmt, va_list nameap) __printflike(2, 0); +nvpair_t *nvpair_createv_descriptor(int value, const char *namefmt, va_list nameap) __printflike(2, 0); +nvpair_t *nvpair_createv_binary(const void *value, size_t size, const char *namefmt, va_list nameap) __printflike(3, 0); + +nvpair_t *nvpair_movef_string(char *value, const char *namefmt, ...) __printflike(2, 3); +nvpair_t *nvpair_movef_nvlist(nvlist_t *value, const char *namefmt, ...) __printflike(2, 3); +nvpair_t *nvpair_movef_descriptor(int value, const char *namefmt, ...) __printflike(2, 3); +nvpair_t *nvpair_movef_binary(void *value, size_t size, const char *namefmt, ...) __printflike(3, 4); + +nvpair_t *nvpair_movev_string(char *value, const char *namefmt, va_list nameap) __printflike(2, 0); +nvpair_t *nvpair_movev_nvlist(nvlist_t *value, const char *namefmt, va_list nameap) __printflike(2, 0); +nvpair_t *nvpair_movev_descriptor(int value, const char *namefmt, va_list nameap) __printflike(2, 0); +nvpair_t *nvpair_movev_binary(void *value, size_t size, const char *namefmt, va_list nameap) __printflike(3, 0); + +#endif /* !_NV_IMPL_H_ */ diff --git a/lib/libnv/nvlist.c b/lib/libnv/nvlist.c new file mode 100644 index 000000000000..323b2c8d6307 --- /dev/null +++ b/lib/libnv/nvlist.c @@ -0,0 +1,1703 @@ +/*- + * Copyright (c) 2009-2013 The FreeBSD Foundation + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include + +#include +#include +#include +#include +#define _WITH_DPRINTF +#include +#include +#include +#include + +#ifdef HAVE_PJDLOG +#include +#endif + +#include "msgio.h" +#include "nv.h" +#include "nv_impl.h" +#include "nvlist_impl.h" +#include "nvpair_impl.h" + +#ifndef HAVE_PJDLOG +#include +#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 + +#define NV_FLAG_PRIVATE_MASK (NV_FLAG_BIG_ENDIAN) +#define NV_FLAG_PUBLIC_MASK (NV_FLAG_IGNORE_CASE) +#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; + struct nvl_head nvl_head; +}; + +#define NVLIST_ASSERT(nvl) do { \ + PJDLOG_ASSERT((nvl) != NULL); \ + PJDLOG_ASSERT((nvl)->nvl_magic == NVLIST_MAGIC); \ +} while (0) + +#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 = malloc(sizeof(*nvl)); + nvl->nvl_error = 0; + nvl->nvl_flags = flags; + TAILQ_INIT(&nvl->nvl_head); + nvl->nvl_magic = NVLIST_MAGIC; + + return (nvl); +} + +void +nvlist_destroy(nvlist_t *nvl) +{ + nvpair_t *nvp; + int serrno; + + if (nvl == NULL) + return; + + serrno = errno; + + NVLIST_ASSERT(nvl); + + while ((nvp = nvlist_first_nvpair(nvl)) != NULL) + nvlist_remove_nvpair(nvl, nvp); + nvl->nvl_magic = 0; + free(nvl); + + errno = serrno; +} + +int +nvlist_error(const nvlist_t *nvl) +{ + + if (nvl == NULL) + return (ENOMEM); + + NVLIST_ASSERT(nvl); + + return (nvl->nvl_error); +} + +bool +nvlist_empty(const nvlist_t *nvl) +{ + + NVLIST_ASSERT(nvl); + PJDLOG_ASSERT(nvl->nvl_error == 0); + + return (nvlist_first_nvpair(nvl) == NULL); +} + +static void +nvlist_report_missing(int type, const char *namefmt, va_list nameap) +{ + char *name; + + vasprintf(&name, namefmt, nameap); + PJDLOG_ABORT("Element '%s' of type %s doesn't exist.", + name != NULL ? name : "N/A", nvpair_type_string(type)); +} + +static nvpair_t * +nvlist_findv(const nvlist_t *nvl, int type, const char *namefmt, va_list nameap) +{ + nvpair_t *nvp; + char *name; + + NVLIST_ASSERT(nvl); + PJDLOG_ASSERT(nvl->nvl_error == 0); + PJDLOG_ASSERT(type == NV_TYPE_NONE || + (type >= NV_TYPE_FIRST && type <= NV_TYPE_LAST)); + + if (vasprintf(&name, namefmt, nameap) < 0) + return (NULL); + + 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; + } + + free(name); + + if (nvp == NULL) + errno = ENOENT; + + return (nvp); +} + +bool +nvlist_exists_type(const nvlist_t *nvl, const char *name, int type) +{ + + return (nvlist_existsf_type(nvl, type, "%s", name)); +} + +bool +nvlist_existsf_type(const nvlist_t *nvl, int type, const char *namefmt, ...) +{ + va_list nameap; + bool ret; + + va_start(nameap, namefmt); + ret = nvlist_existsv_type(nvl, type, namefmt, nameap); + va_end(nameap); + + return (ret); +} + +bool +nvlist_existsv_type(const nvlist_t *nvl, int type, const char *namefmt, + va_list nameap) +{ + + 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_findv(nvl, type, namefmt, nameap) != NULL); +} + +void +nvlist_free_type(nvlist_t *nvl, const char *name, int type) +{ + + nvlist_freef_type(nvl, type, "%s", name); +} + +void +nvlist_freef_type(nvlist_t *nvl, int type, const char *namefmt, ...) +{ + va_list nameap; + + va_start(nameap, namefmt); + nvlist_freev_type(nvl, type, namefmt, nameap); + va_end(nameap); +} + +void +nvlist_freev_type(nvlist_t *nvl, int type, const char *namefmt, va_list nameap) +{ + va_list cnameap; + 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)); + + va_copy(cnameap, nameap); + nvp = nvlist_findv(nvl, type, namefmt, cnameap); + va_end(cnameap); + if (nvp != NULL) + nvlist_free_nvpair(nvl, nvp); + else + nvlist_report_missing(type, namefmt, nameap); +} + +nvlist_t * +nvlist_clone(const nvlist_t *nvl) +{ + nvlist_t *newnvl; + nvpair_t *nvp, *newnvp; + + NVLIST_ASSERT(nvl); + + if (nvl->nvl_error != 0) { + errno = 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; + nvlist_move_nvpair(newnvl, newnvp); + } + if (nvp != NULL) { + nvlist_destroy(newnvl); + return (NULL); + } + return (newnvl); +} + +/* + * Dump content of nvlist. + */ +static void +nvlist_xdump(const nvlist_t *nvl, int fd, int level) +{ + nvpair_t *nvp; + + PJDLOG_ASSERT(level < 3); + + if (nvlist_error(nvl) != 0) { + dprintf(fd, "%*serror: %d\n", level * 4, "", + nvlist_error(nvl)); + return; + } + + for (nvp = nvlist_first_nvpair(nvl); nvp != NULL; + nvp = nvlist_next_nvpair(nvl, nvp)) { + 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"); + nvlist_xdump(nvpair_get_nvlist(nvp), fd, level + 1); + 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; + } + default: + PJDLOG_ABORT("Unknown type: %d.", nvpair_type(nvp)); + } + } +} + +void +nvlist_dump(const nvlist_t *nvl, int fd) +{ + + nvlist_xdump(nvl, fd, 0); +} + +void +nvlist_fdump(const nvlist_t *nvl, FILE *fp) +{ + + fflush(fp); + nvlist_dump(nvl, fileno(fp)); +} + +/* + * The function obtains size of the nvlist after nvlist_pack(). + * Additional argument 'level' allows to track how deep are we as we obtain + * size of the NV_TYPE_NVLIST elements using recursion. We allow at most + * three levels of recursion. + */ +static size_t +nvlist_xsize(const nvlist_t *nvl, int level) +{ + const nvpair_t *nvp; + size_t size; + + NVLIST_ASSERT(nvl); + PJDLOG_ASSERT(nvl->nvl_error == 0); + PJDLOG_ASSERT(level < 3); + + size = sizeof(struct nvlist_header); + for (nvp = nvlist_first_nvpair(nvl); nvp != NULL; + nvp = nvlist_next_nvpair(nvl, nvp)) { + size += nvpair_header_size(); + size += strlen(nvpair_name(nvp)) + 1; + if (nvpair_type(nvp) == NV_TYPE_NVLIST) + size += nvlist_xsize(nvpair_get_nvlist(nvp), level + 1); + else + size += nvpair_size(nvp); + } + + return (size); +} + +size_t +nvlist_size(const nvlist_t *nvl) +{ + + return (nvlist_xsize(nvl, 0)); +} + +static int * +nvlist_xdescriptors(const nvlist_t *nvl, int *descs, int level) +{ + const nvpair_t *nvp; + + NVLIST_ASSERT(nvl); + PJDLOG_ASSERT(nvl->nvl_error == 0); + PJDLOG_ASSERT(level < 3); + + for (nvp = nvlist_first_nvpair(nvl); nvp != NULL; + nvp = nvlist_next_nvpair(nvl, nvp)) { + switch (nvpair_type(nvp)) { + case NV_TYPE_DESCRIPTOR: + *descs = nvpair_get_descriptor(nvp); + descs++; + break; + case NV_TYPE_NVLIST: + descs = nvlist_xdescriptors(nvpair_get_nvlist(nvp), + descs, level + 1); + break; + } + } + + return (descs); +} + +int * +nvlist_descriptors(const nvlist_t *nvl, size_t *nitemsp) +{ + size_t nitems; + int *fds; + + nitems = nvlist_ndescriptors(nvl); + fds = malloc(sizeof(fds[0]) * (nitems + 1)); + if (fds == NULL) + return (NULL); + if (nitems > 0) + nvlist_xdescriptors(nvl, fds, 0); + fds[nitems] = -1; + if (nitemsp != NULL) + *nitemsp = nitems; + return (fds); +} + +static size_t +nvlist_xndescriptors(const nvlist_t *nvl, int level) +{ + const nvpair_t *nvp; + size_t ndescs; + + NVLIST_ASSERT(nvl); + PJDLOG_ASSERT(nvl->nvl_error == 0); + PJDLOG_ASSERT(level < 3); + + ndescs = 0; + for (nvp = nvlist_first_nvpair(nvl); nvp != NULL; + nvp = nvlist_next_nvpair(nvl, nvp)) { + switch (nvpair_type(nvp)) { + case NV_TYPE_DESCRIPTOR: + ndescs++; + break; + case NV_TYPE_NVLIST: + ndescs += nvlist_xndescriptors(nvpair_get_nvlist(nvp), + level + 1); + break; + } + } + + return (ndescs); +} + +size_t +nvlist_ndescriptors(const nvlist_t *nvl) +{ + + return (nvlist_xndescriptors(nvl, 0)); +} + +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); +} + +void * +nvlist_xpack(const nvlist_t *nvl, int64_t *fdidxp, size_t *sizep) +{ + unsigned char *buf, *ptr; + size_t left, size; + nvpair_t *nvp; + + NVLIST_ASSERT(nvl); + + if (nvl->nvl_error != 0) { + errno = nvl->nvl_error; + return (NULL); + } + + size = nvlist_size(nvl); + buf = malloc(size); + if (buf == NULL) + return (NULL); + + ptr = buf; + left = size; + + ptr = nvlist_pack_header(nvl, ptr, &left); + + for (nvp = nvlist_first_nvpair(nvl); nvp != NULL; + nvp = nvlist_next_nvpair(nvl, nvp)) { + ptr = nvpair_pack(nvp, ptr, fdidxp, &left); + if (ptr == NULL) { + free(buf); + return (NULL); + } + } + + if (sizep != NULL) + *sizep = size; + return (buf); +} + +void * +nvlist_pack(const nvlist_t *nvl, size_t *sizep) +{ + + NVLIST_ASSERT(nvl); + + if (nvl->nvl_error != 0) { + errno = nvl->nvl_error; + return (NULL); + } + + if (nvlist_ndescriptors(nvl) > 0) { + errno = 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 = EINVAL; + return (false); + } + if ((nvlhdrp->nvlh_flags &= ~NV_FLAG_ALL_MASK) != 0) { + errno = 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); +} + +static const unsigned char * +nvlist_unpack_header(nvlist_t *nvl, const unsigned char *ptr, size_t nfds, + int *flagsp, size_t *leftp) +{ + struct nvlist_header nvlhdr; + + 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; + + nvl->nvl_flags = (nvlhdr.nvlh_flags & NV_FLAG_PUBLIC_MASK); + + ptr += sizeof(nvlhdr); + *flagsp = (int)nvlhdr.nvlh_flags; + *leftp -= sizeof(nvlhdr); + + return (ptr); +failed: + errno = EINVAL; + return (NULL); +} + +nvlist_t * +nvlist_xunpack(const void *buf, size_t size, const int *fds, size_t nfds) +{ + const unsigned char *ptr; + nvlist_t *nvl; + nvpair_t *nvp; + size_t left; + int flags; + + left = size; + ptr = buf; + + nvl = nvlist_create(0); + if (nvl == NULL) + goto failed; + + ptr = nvlist_unpack_header(nvl, ptr, nfds, &flags, &left); + if (ptr == NULL) + goto failed; + + while (left > 0) { + ptr = nvpair_unpack(flags, ptr, &left, fds, nfds, &nvp); + if (ptr == NULL) + goto failed; + nvlist_move_nvpair(nvl, nvp); + } + + return (nvl); +failed: + nvlist_destroy(nvl); + return (NULL); +} + +nvlist_t * +nvlist_unpack(const void *buf, size_t size) +{ + + return (nvlist_xunpack(buf, size, NULL, 0)); +} + +int +nvlist_send(int sock, const nvlist_t *nvl) +{ + size_t datasize, nfds; + int *fds; + void *data; + int64_t fdidx; + int serrno, ret; + + if (nvlist_error(nvl) != 0) { + errno = 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: + serrno = errno; + free(fds); + free(data); + errno = serrno; + return (ret); +} + +nvlist_t * +nvlist_recv(int sock) +{ + struct nvlist_header nvlhdr; + nvlist_t *nvl, *ret; + size_t nfds, size; + void *buf; + int serrno, *fds; + + if (msg_peek(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 = malloc(size); + if (buf == NULL) + return (NULL); + + ret = NULL; + fds = NULL; + + if (buf_recv(sock, buf, size) == -1) + goto out; + + if (nfds > 0) { + fds = 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); + if (nvl == NULL) + goto out; + + ret = nvl; +out: + serrno = errno; + free(buf); + free(fds); + errno = serrno; + + return (ret); +} + +nvlist_t * +nvlist_xfer(int sock, nvlist_t *nvl) +{ + + if (nvlist_send(sock, nvl) < 0) { + nvlist_destroy(nvl); + return (NULL); + } + nvlist_destroy(nvl); + return (nvlist_recv(sock)); +} + +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); + PJDLOG_ASSERT(cookiep != NULL); + + if (*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); + *cookiep = nvp; + return (nvpair_name(nvp)); +} + +bool +nvlist_exists(const nvlist_t *nvl, const char *name) +{ + + return (nvlist_existsf(nvl, "%s", name)); +} + +#define NVLIST_EXISTS(type) \ +bool \ +nvlist_exists_##type(const nvlist_t *nvl, const char *name) \ +{ \ + \ + return (nvlist_existsf_##type(nvl, "%s", name)); \ +} + +NVLIST_EXISTS(null) +NVLIST_EXISTS(bool) +NVLIST_EXISTS(number) +NVLIST_EXISTS(string) +NVLIST_EXISTS(nvlist) +NVLIST_EXISTS(descriptor) +NVLIST_EXISTS(binary) + +#undef NVLIST_EXISTS + +bool +nvlist_existsf(const nvlist_t *nvl, const char *namefmt, ...) +{ + va_list nameap; + bool ret; + + va_start(nameap, namefmt); + ret = nvlist_existsv(nvl, namefmt, nameap); + va_end(nameap); + return (ret); +} + +#define NVLIST_EXISTSF(type) \ +bool \ +nvlist_existsf_##type(const nvlist_t *nvl, const char *namefmt, ...) \ +{ \ + va_list nameap; \ + bool ret; \ + \ + va_start(nameap, namefmt); \ + ret = nvlist_existsv_##type(nvl, namefmt, nameap); \ + va_end(nameap); \ + return (ret); \ +} + +NVLIST_EXISTSF(null) +NVLIST_EXISTSF(bool) +NVLIST_EXISTSF(number) +NVLIST_EXISTSF(string) +NVLIST_EXISTSF(nvlist) +NVLIST_EXISTSF(descriptor) +NVLIST_EXISTSF(binary) + +#undef NVLIST_EXISTSF + +bool +nvlist_existsv(const nvlist_t *nvl, const char *namefmt, va_list nameap) +{ + + return (nvlist_findv(nvl, NV_TYPE_NONE, namefmt, nameap) != NULL); +} + +#define NVLIST_EXISTSV(type, TYPE) \ +bool \ +nvlist_existsv_##type(const nvlist_t *nvl, const char *namefmt, \ + va_list nameap) \ +{ \ + \ + return (nvlist_findv(nvl, NV_TYPE_##TYPE, namefmt, nameap) != \ + NULL); \ +} + +NVLIST_EXISTSV(null, NULL) +NVLIST_EXISTSV(bool, BOOL) +NVLIST_EXISTSV(number, NUMBER) +NVLIST_EXISTSV(string, STRING) +NVLIST_EXISTSV(nvlist, NVLIST) +NVLIST_EXISTSV(descriptor, DESCRIPTOR) +NVLIST_EXISTSV(binary, BINARY) + +#undef NVLIST_EXISTSV + +void +nvlist_add_nvpair(nvlist_t *nvl, const nvpair_t *nvp) +{ + nvpair_t *newnvp; + + NVPAIR_ASSERT(nvp); + + if (nvlist_error(nvl) != 0) { + errno = nvlist_error(nvl); + return; + } + if (nvlist_exists(nvl, nvpair_name(nvp))) { + nvl->nvl_error = errno = EEXIST; + return; + } + + newnvp = nvpair_clone(nvp); + if (newnvp == NULL) { + nvl->nvl_error = errno = (errno != 0 ? errno : ENOMEM); + return; + } + + nvpair_insert(&nvl->nvl_head, newnvp, nvl); +} + +void +nvlist_add_null(nvlist_t *nvl, const char *name) +{ + + nvlist_addf_null(nvl, "%s", name); +} + +void +nvlist_add_bool(nvlist_t *nvl, const char *name, bool value) +{ + + nvlist_addf_bool(nvl, value, "%s", name); +} + +void +nvlist_add_number(nvlist_t *nvl, const char *name, uint64_t value) +{ + + nvlist_addf_number(nvl, value, "%s", name); +} + +void +nvlist_add_string(nvlist_t *nvl, const char *name, const char *value) +{ + + nvlist_addf_string(nvl, value, "%s", name); +} + +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 = nvlist_error(nvl); + return; + } + + nvp = nvpair_create_stringv(name, valuefmt, valueap); + if (nvp == NULL) + nvl->nvl_error = errno = (errno != 0 ? errno : ENOMEM); + else + nvlist_move_nvpair(nvl, nvp); +} + +void +nvlist_add_nvlist(nvlist_t *nvl, const char *name, const nvlist_t *value) +{ + + nvlist_addf_nvlist(nvl, value, "%s", name); +} + +void +nvlist_add_descriptor(nvlist_t *nvl, const char *name, int value) +{ + + nvlist_addf_descriptor(nvl, value, "%s", name); +} + +void +nvlist_add_binary(nvlist_t *nvl, const char *name, const void *value, + size_t size) +{ + + nvlist_addf_binary(nvl, value, size, "%s", name); +} + +void +nvlist_addf_null(nvlist_t *nvl, const char *namefmt, ...) +{ + va_list nameap; + + va_start(nameap, namefmt); + nvlist_addv_null(nvl, namefmt, nameap); + va_end(nameap); +} + +void +nvlist_addf_bool(nvlist_t *nvl, bool value, const char *namefmt, ...) +{ + va_list nameap; + + va_start(nameap, namefmt); + nvlist_addv_bool(nvl, value, namefmt, nameap); + va_end(nameap); +} + +void +nvlist_addf_number(nvlist_t *nvl, uint64_t value, const char *namefmt, ...) +{ + va_list nameap; + + va_start(nameap, namefmt); + nvlist_addv_number(nvl, value, namefmt, nameap); + va_end(nameap); +} + +void +nvlist_addf_string(nvlist_t *nvl, const char *value, const char *namefmt, ...) +{ + va_list nameap; + + va_start(nameap, namefmt); + nvlist_addv_string(nvl, value, namefmt, nameap); + va_end(nameap); +} + +void +nvlist_addf_nvlist(nvlist_t *nvl, const nvlist_t *value, const char *namefmt, + ...) +{ + va_list nameap; + + va_start(nameap, namefmt); + nvlist_addv_nvlist(nvl, value, namefmt, nameap); + va_end(nameap); +} + +void +nvlist_addf_descriptor(nvlist_t *nvl, int value, const char *namefmt, ...) +{ + va_list nameap; + + va_start(nameap, namefmt); + nvlist_addv_descriptor(nvl, value, namefmt, nameap); + va_end(nameap); +} + +void +nvlist_addf_binary(nvlist_t *nvl, const void *value, size_t size, + const char *namefmt, ...) +{ + va_list nameap; + + va_start(nameap, namefmt); + nvlist_addv_binary(nvl, value, size, namefmt, nameap); + va_end(nameap); +} + +void +nvlist_addv_null(nvlist_t *nvl, const char *namefmt, va_list nameap) +{ + nvpair_t *nvp; + + if (nvlist_error(nvl) != 0) { + errno = nvlist_error(nvl); + return; + } + + nvp = nvpair_createv_null(namefmt, nameap); + if (nvp == NULL) + nvl->nvl_error = errno = (errno != 0 ? errno : ENOMEM); + else + nvlist_move_nvpair(nvl, nvp); +} + +void +nvlist_addv_bool(nvlist_t *nvl, bool value, const char *namefmt, va_list nameap) +{ + nvpair_t *nvp; + + if (nvlist_error(nvl) != 0) { + errno = nvlist_error(nvl); + return; + } + + nvp = nvpair_createv_bool(value, namefmt, nameap); + if (nvp == NULL) + nvl->nvl_error = errno = (errno != 0 ? errno : ENOMEM); + else + nvlist_move_nvpair(nvl, nvp); +} + +void +nvlist_addv_number(nvlist_t *nvl, uint64_t value, const char *namefmt, + va_list nameap) +{ + nvpair_t *nvp; + + if (nvlist_error(nvl) != 0) { + errno = nvlist_error(nvl); + return; + } + + nvp = nvpair_createv_number(value, namefmt, nameap); + if (nvp == NULL) + nvl->nvl_error = errno = (errno != 0 ? errno : ENOMEM); + else + nvlist_move_nvpair(nvl, nvp); +} + +void +nvlist_addv_string(nvlist_t *nvl, const char *value, const char *namefmt, + va_list nameap) +{ + nvpair_t *nvp; + + if (nvlist_error(nvl) != 0) { + errno = nvlist_error(nvl); + return; + } + + nvp = nvpair_createv_string(value, namefmt, nameap); + if (nvp == NULL) + nvl->nvl_error = errno = (errno != 0 ? errno : ENOMEM); + else + nvlist_move_nvpair(nvl, nvp); +} + +void +nvlist_addv_nvlist(nvlist_t *nvl, const nvlist_t *value, const char *namefmt, + va_list nameap) +{ + nvpair_t *nvp; + + if (nvlist_error(nvl) != 0) { + errno = nvlist_error(nvl); + return; + } + + nvp = nvpair_createv_nvlist(value, namefmt, nameap); + if (nvp == NULL) + nvl->nvl_error = errno = (errno != 0 ? errno : ENOMEM); + else + nvlist_move_nvpair(nvl, nvp); +} + +void +nvlist_addv_descriptor(nvlist_t *nvl, int value, const char *namefmt, + va_list nameap) +{ + nvpair_t *nvp; + + if (nvlist_error(nvl) != 0) { + errno = nvlist_error(nvl); + return; + } + + nvp = nvpair_createv_descriptor(value, namefmt, nameap); + if (nvp == NULL) + nvl->nvl_error = errno = (errno != 0 ? errno : ENOMEM); + else + nvlist_move_nvpair(nvl, nvp); +} + +void +nvlist_addv_binary(nvlist_t *nvl, const void *value, size_t size, + const char *namefmt, va_list nameap) +{ + nvpair_t *nvp; + + if (nvlist_error(nvl) != 0) { + errno = nvlist_error(nvl); + return; + } + + nvp = nvpair_createv_binary(value, size, namefmt, nameap); + if (nvp == NULL) + nvl->nvl_error = errno = (errno != 0 ? errno : ENOMEM); + else + nvlist_move_nvpair(nvl, nvp); +} + +void +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 = nvlist_error(nvl); + return; + } + if (nvlist_exists(nvl, nvpair_name(nvp))) { + nvpair_free(nvp); + nvl->nvl_error = errno = EEXIST; + return; + } + + nvpair_insert(&nvl->nvl_head, nvp, nvl); +} + +#define NVLIST_MOVE(vtype, type) \ +void \ +nvlist_move_##type(nvlist_t *nvl, const char *name, vtype value) \ +{ \ + \ + nvlist_movef_##type(nvl, value, "%s", name); \ +} + +NVLIST_MOVE(char *, string) +NVLIST_MOVE(nvlist_t *, nvlist) +NVLIST_MOVE(int, descriptor) + +#undef NVLIST_MOVE + +void +nvlist_move_binary(nvlist_t *nvl, const char *name, void *value, size_t size) +{ + + nvlist_movef_binary(nvl, value, size, "%s", name); +} + +#define NVLIST_MOVEF(vtype, type) \ +void \ +nvlist_movef_##type(nvlist_t *nvl, vtype value, const char *namefmt, \ + ...) \ +{ \ + va_list nameap; \ + \ + va_start(nameap, namefmt); \ + nvlist_movev_##type(nvl, value, namefmt, nameap); \ + va_end(nameap); \ +} + +NVLIST_MOVEF(char *, string) +NVLIST_MOVEF(nvlist_t *, nvlist) +NVLIST_MOVEF(int, descriptor) + +#undef NVLIST_MOVEF + +void +nvlist_movef_binary(nvlist_t *nvl, void *value, size_t size, + const char *namefmt, ...) +{ + va_list nameap; + + va_start(nameap, namefmt); + nvlist_movev_binary(nvl, value, size, namefmt, nameap); + va_end(nameap); +} + +void +nvlist_movev_string(nvlist_t *nvl, char *value, const char *namefmt, + va_list nameap) +{ + nvpair_t *nvp; + + if (nvlist_error(nvl) != 0) { + free(value); + errno = nvlist_error(nvl); + return; + } + + nvp = nvpair_movev_string(value, namefmt, nameap); + if (nvp == NULL) + nvl->nvl_error = errno = (errno != 0 ? errno : ENOMEM); + else + nvlist_move_nvpair(nvl, nvp); +} + +void +nvlist_movev_nvlist(nvlist_t *nvl, nvlist_t *value, const char *namefmt, + va_list nameap) +{ + nvpair_t *nvp; + + if (nvlist_error(nvl) != 0) { + nvlist_destroy(value); + errno = nvlist_error(nvl); + return; + } + + nvp = nvpair_movev_nvlist(value, namefmt, nameap); + if (nvp == NULL) + nvl->nvl_error = errno = (errno != 0 ? errno : ENOMEM); + else + nvlist_move_nvpair(nvl, nvp); +} + +void +nvlist_movev_descriptor(nvlist_t *nvl, int value, const char *namefmt, + va_list nameap) +{ + nvpair_t *nvp; + + if (nvlist_error(nvl) != 0) { + close(value); + errno = nvlist_error(nvl); + return; + } + + nvp = nvpair_movev_descriptor(value, namefmt, nameap); + if (nvp == NULL) + nvl->nvl_error = errno = (errno != 0 ? errno : ENOMEM); + else + nvlist_move_nvpair(nvl, nvp); +} + +void +nvlist_movev_binary(nvlist_t *nvl, void *value, size_t size, + const char *namefmt, va_list nameap) +{ + nvpair_t *nvp; + + if (nvlist_error(nvl) != 0) { + free(value); + errno = nvlist_error(nvl); + return; + } + + nvp = nvpair_movev_binary(value, size, namefmt, nameap); + if (nvp == NULL) + nvl->nvl_error = errno = (errno != 0 ? errno : ENOMEM); + else + nvlist_move_nvpair(nvl, nvp); +} + +#define NVLIST_GET(ftype, type) \ +ftype \ +nvlist_get_##type(const nvlist_t *nvl, const char *name) \ +{ \ + \ + return (nvlist_getf_##type(nvl, "%s", name)); \ +} + +NVLIST_GET(const nvpair_t *, nvpair) +NVLIST_GET(bool, bool) +NVLIST_GET(uint64_t, number) +NVLIST_GET(const char *, string) +NVLIST_GET(const nvlist_t *, nvlist) +NVLIST_GET(int, descriptor) + +#undef NVLIST_GET + +const void * +nvlist_get_binary(const nvlist_t *nvl, const char *name, size_t *sizep) +{ + + return (nvlist_getf_binary(nvl, sizep, "%s", name)); +} + +#define NVLIST_GETF(ftype, type) \ +ftype \ +nvlist_getf_##type(const nvlist_t *nvl, const char *namefmt, ...) \ +{ \ + va_list nameap; \ + ftype value; \ + \ + va_start(nameap, namefmt); \ + value = nvlist_getv_##type(nvl, namefmt, nameap); \ + va_end(nameap); \ + \ + return (value); \ +} + +NVLIST_GETF(const nvpair_t *, nvpair) +NVLIST_GETF(bool, bool) +NVLIST_GETF(uint64_t, number) +NVLIST_GETF(const char *, string) +NVLIST_GETF(const nvlist_t *, nvlist) +NVLIST_GETF(int, descriptor) + +#undef NVLIST_GETF + +const void * +nvlist_getf_binary(const nvlist_t *nvl, size_t *sizep, const char *namefmt, ...) +{ + va_list nameap; + const void *value; + + va_start(nameap, namefmt); + value = nvlist_getv_binary(nvl, sizep, namefmt, nameap); + va_end(nameap); + + return (value); +} + +const nvpair_t * +nvlist_getv_nvpair(const nvlist_t *nvl, const char *namefmt, va_list nameap) +{ + + return (nvlist_findv(nvl, NV_TYPE_NONE, namefmt, nameap)); +} + +#define NVLIST_GETV(ftype, type, TYPE) \ +ftype \ +nvlist_getv_##type(const nvlist_t *nvl, const char *namefmt, \ + va_list nameap) \ +{ \ + va_list cnameap; \ + const nvpair_t *nvp; \ + \ + va_copy(cnameap, nameap); \ + nvp = nvlist_findv(nvl, NV_TYPE_##TYPE, namefmt, cnameap); \ + va_end(cnameap); \ + if (nvp == NULL) \ + nvlist_report_missing(NV_TYPE_##TYPE, namefmt, nameap); \ + return (nvpair_get_##type(nvp)); \ +} + +NVLIST_GETV(bool, bool, BOOL) +NVLIST_GETV(uint64_t, number, NUMBER) +NVLIST_GETV(const char *, string, STRING) +NVLIST_GETV(const nvlist_t *, nvlist, NVLIST) +NVLIST_GETV(int, descriptor, DESCRIPTOR) + +#undef NVLIST_GETV + +const void * +nvlist_getv_binary(const nvlist_t *nvl, size_t *sizep, const char *namefmt, + va_list nameap) +{ + va_list cnameap; + const nvpair_t *nvp; + + va_copy(cnameap, nameap); + nvp = nvlist_findv(nvl, NV_TYPE_BINARY, namefmt, cnameap); + va_end(cnameap); + if (nvp == NULL) + nvlist_report_missing(NV_TYPE_BINARY, namefmt, nameap); + + return (nvpair_get_binary(nvp, sizep)); +} + +#define NVLIST_TAKE(ftype, type) \ +ftype \ +nvlist_take_##type(nvlist_t *nvl, const char *name) \ +{ \ + \ + return (nvlist_takef_##type(nvl, "%s", name)); \ +} + +NVLIST_TAKE(nvpair_t *, nvpair) +NVLIST_TAKE(bool, bool) +NVLIST_TAKE(uint64_t, number) +NVLIST_TAKE(char *, string) +NVLIST_TAKE(nvlist_t *, nvlist) +NVLIST_TAKE(int, descriptor) + +#undef NVLIST_TAKE + +void * +nvlist_take_binary(nvlist_t *nvl, const char *name, size_t *sizep) +{ + + return (nvlist_takef_binary(nvl, sizep, "%s", name)); +} + +#define NVLIST_TAKEF(ftype, type) \ +ftype \ +nvlist_takef_##type(nvlist_t *nvl, const char *namefmt, ...) \ +{ \ + va_list nameap; \ + ftype value; \ + \ + va_start(nameap, namefmt); \ + value = nvlist_takev_##type(nvl, namefmt, nameap); \ + va_end(nameap); \ + \ + return (value); \ +} + +NVLIST_TAKEF(nvpair_t *, nvpair) +NVLIST_TAKEF(bool, bool) +NVLIST_TAKEF(uint64_t, number) +NVLIST_TAKEF(char *, string) +NVLIST_TAKEF(nvlist_t *, nvlist) +NVLIST_TAKEF(int, descriptor) + +#undef NVLIST_TAKEF + +void * +nvlist_takef_binary(nvlist_t *nvl, size_t *sizep, const char *namefmt, ...) +{ + va_list nameap; + void *value; + + va_start(nameap, namefmt); + value = nvlist_takev_binary(nvl, sizep, namefmt, nameap); + va_end(nameap); + + return (value); +} + +nvpair_t * +nvlist_takev_nvpair(nvlist_t *nvl, const char *namefmt, va_list nameap) +{ + nvpair_t *nvp; + + nvp = nvlist_findv(nvl, NV_TYPE_NONE, namefmt, nameap); + if (nvp != NULL) + nvlist_remove_nvpair(nvl, nvp); + return (nvp); +} + +#define NVLIST_TAKEV(ftype, type, TYPE) \ +ftype \ +nvlist_takev_##type(nvlist_t *nvl, const char *namefmt, va_list nameap) \ +{ \ + va_list cnameap; \ + nvpair_t *nvp; \ + ftype value; \ + \ + va_copy(cnameap, nameap); \ + nvp = nvlist_findv(nvl, NV_TYPE_##TYPE, namefmt, cnameap); \ + va_end(cnameap); \ + if (nvp == NULL) \ + nvlist_report_missing(NV_TYPE_##TYPE, namefmt, nameap); \ + value = (ftype)(intptr_t)nvpair_get_##type(nvp); \ + nvlist_remove_nvpair(nvl, nvp); \ + nvpair_free_structure(nvp); \ + return (value); \ +} + +NVLIST_TAKEV(bool, bool, BOOL) +NVLIST_TAKEV(uint64_t, number, NUMBER) +NVLIST_TAKEV(char *, string, STRING) +NVLIST_TAKEV(nvlist_t *, nvlist, NVLIST) +NVLIST_TAKEV(int, descriptor, DESCRIPTOR) + +#undef NVLIST_TAKEV + +void * +nvlist_takev_binary(nvlist_t *nvl, size_t *sizep, const char *namefmt, + va_list nameap) +{ + va_list cnameap; + nvpair_t *nvp; + void *value; + + va_copy(cnameap, nameap); + nvp = nvlist_findv(nvl, NV_TYPE_BINARY, namefmt, cnameap); + va_end(cnameap); + if (nvp == NULL) + nvlist_report_missing(NV_TYPE_BINARY, namefmt, nameap); + + value = (void *)(intptr_t)nvpair_get_binary(nvp, sizep); + nvlist_remove_nvpair(nvl, nvp); + nvpair_free_structure(nvp); + return (value); +} + +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_freef(nvl, "%s", name); +} + +#define NVLIST_FREE(type) \ +void \ +nvlist_free_##type(nvlist_t *nvl, const char *name) \ +{ \ + \ + nvlist_freef_##type(nvl, "%s", name); \ +} + +NVLIST_FREE(null) +NVLIST_FREE(bool) +NVLIST_FREE(number) +NVLIST_FREE(string) +NVLIST_FREE(nvlist) +NVLIST_FREE(descriptor) +NVLIST_FREE(binary) + +#undef NVLIST_FREE + +void +nvlist_freef(nvlist_t *nvl, const char *namefmt, ...) +{ + va_list nameap; + + va_start(nameap, namefmt); + nvlist_freev(nvl, namefmt, nameap); + va_end(nameap); +} + +#define NVLIST_FREEF(type) \ +void \ +nvlist_freef_##type(nvlist_t *nvl, const char *namefmt, ...) \ +{ \ + va_list nameap; \ + \ + va_start(nameap, namefmt); \ + nvlist_freev_##type(nvl, namefmt, nameap); \ + va_end(nameap); \ +} + +NVLIST_FREEF(null) +NVLIST_FREEF(bool) +NVLIST_FREEF(number) +NVLIST_FREEF(string) +NVLIST_FREEF(nvlist) +NVLIST_FREEF(descriptor) +NVLIST_FREEF(binary) + +#undef NVLIST_FREEF + +void +nvlist_freev(nvlist_t *nvl, const char *namefmt, va_list nameap) +{ + + nvlist_freev_type(nvl, NV_TYPE_NONE, namefmt, nameap); +} + +#define NVLIST_FREEV(type, TYPE) \ +void \ +nvlist_freev_##type(nvlist_t *nvl, const char *namefmt, va_list nameap) \ +{ \ + \ + nvlist_freev_type(nvl, NV_TYPE_##TYPE, namefmt, nameap); \ +} + +NVLIST_FREEV(null, NULL) +NVLIST_FREEV(bool, BOOL) +NVLIST_FREEV(number, NUMBER) +NVLIST_FREEV(string, STRING) +NVLIST_FREEV(nvlist, NVLIST) +NVLIST_FREEV(descriptor, DESCRIPTOR) +NVLIST_FREEV(binary, BINARY) +#undef NVLIST_FREEV + +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); +} diff --git a/lib/libnv/nvlist_impl.h b/lib/libnv/nvlist_impl.h new file mode 100644 index 000000000000..43a7bb0a80c1 --- /dev/null +++ b/lib/libnv/nvlist_impl.h @@ -0,0 +1,43 @@ +/*- + * Copyright (c) 2013 The FreeBSD Foundation + * 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. + * + * $FreeBSD$ + */ + +#ifndef _NVLIST_IMPL_H_ +#define _NVLIST_IMPL_H_ + +#include + +#include "nv.h" + +void *nvlist_xpack(const nvlist_t *nvl, int64_t *fdidxp, size_t *sizep); +nvlist_t *nvlist_xunpack(const void *buf, size_t size, const int *fds, + size_t nfds); + +#endif /* !_NVLIST_IMPL_H_ */ diff --git a/lib/libnv/nvpair.c b/lib/libnv/nvpair.c new file mode 100644 index 000000000000..a6a2a0079bbd --- /dev/null +++ b/lib/libnv/nvpair.c @@ -0,0 +1,1335 @@ +/*- + * Copyright (c) 2009-2013 The FreeBSD Foundation + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_PJDLOG +#include +#endif + +#include "common_impl.h" +#include "nv.h" +#include "nv_impl.h" +#include "nvlist_impl.h" +#include "nvpair_impl.h" + +#ifndef HAVE_PJDLOG +#include +#define PJDLOG_ASSERT(...) assert(__VA_ARGS__) +#define PJDLOG_RASSERT(expr, ...) assert(expr) +#define PJDLOG_ABORT(...) abort() +#endif + +#define NVPAIR_MAGIC 0x6e7670 /* "nvp" */ +struct nvpair { + int nvp_magic; + char *nvp_name; + int nvp_type; + uint64_t nvp_data; + size_t nvp_datasize; + nvlist_t *nvp_list; /* Used for sanity checks. */ + TAILQ_ENTRY(nvpair) nvp_next; +}; + +#define NVPAIR_ASSERT(nvp) do { \ + PJDLOG_ASSERT((nvp) != NULL); \ + PJDLOG_ASSERT((nvp)->nvp_magic == NVPAIR_MAGIC); \ +} while (0) + +struct nvpair_header { + uint8_t nvph_type; + uint16_t nvph_namesize; + uint64_t nvph_datasize; +} __packed; + + +void +nvpair_assert(const nvpair_t *nvp) +{ + + NVPAIR_ASSERT(nvp); +} + +const nvlist_t * +nvpair_nvlist(const nvpair_t *nvp) +{ + + NVPAIR_ASSERT(nvp); + + return (nvp->nvp_list); +} + +nvpair_t * +nvpair_next(const nvpair_t *nvp) +{ + + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvp->nvp_list != NULL); + + return (TAILQ_NEXT(nvp, nvp_next)); +} + +nvpair_t * +nvpair_prev(const nvpair_t *nvp) +{ + + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvp->nvp_list != NULL); + + return (TAILQ_PREV(nvp, nvl_head, nvp_next)); +} + +void +nvpair_insert(struct nvl_head *head, nvpair_t *nvp, nvlist_t *nvl) +{ + + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvp->nvp_list == NULL); + PJDLOG_ASSERT(!nvlist_exists(nvl, nvpair_name(nvp))); + + TAILQ_INSERT_TAIL(head, nvp, nvp_next); + nvp->nvp_list = nvl; +} + +void +nvpair_remove(struct nvl_head *head, nvpair_t *nvp, const nvlist_t *nvl) +{ + + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvp->nvp_list == nvl); + + TAILQ_REMOVE(head, nvp, nvp_next); + nvp->nvp_list = NULL; +} + +nvpair_t * +nvpair_clone(const nvpair_t *nvp) +{ + nvpair_t *newnvp; + const char *name; + const void *data; + size_t datasize; + + NVPAIR_ASSERT(nvp); + + name = nvpair_name(nvp); + + switch (nvpair_type(nvp)) { + case NV_TYPE_NULL: + newnvp = nvpair_create_null(name); + break; + case NV_TYPE_BOOL: + newnvp = nvpair_create_bool(name, nvpair_get_bool(nvp)); + break; + case NV_TYPE_NUMBER: + newnvp = nvpair_create_number(name, nvpair_get_number(nvp)); + break; + case NV_TYPE_STRING: + newnvp = nvpair_create_string(name, nvpair_get_string(nvp)); + break; + case NV_TYPE_NVLIST: + newnvp = nvpair_create_nvlist(name, nvpair_get_nvlist(nvp)); + break; + case NV_TYPE_DESCRIPTOR: + newnvp = nvpair_create_descriptor(name, + nvpair_get_descriptor(nvp)); + break; + case NV_TYPE_BINARY: + data = nvpair_get_binary(nvp, &datasize); + newnvp = nvpair_create_binary(name, data, datasize); + break; + default: + PJDLOG_ABORT("Unknown type: %d.", nvpair_type(nvp)); + } + + return (newnvp); +} + +size_t +nvpair_header_size(void) +{ + + return (sizeof(struct nvpair_header)); +} + +size_t +nvpair_size(const nvpair_t *nvp) +{ + + NVPAIR_ASSERT(nvp); + + return (nvp->nvp_datasize); +} + +static unsigned char * +nvpair_pack_header(const nvpair_t *nvp, unsigned char *ptr, size_t *leftp) +{ + struct nvpair_header nvphdr; + size_t namesize; + + NVPAIR_ASSERT(nvp); + + nvphdr.nvph_type = nvp->nvp_type; + namesize = strlen(nvp->nvp_name) + 1; + PJDLOG_ASSERT(namesize > 0 && namesize <= UINT16_MAX); + nvphdr.nvph_namesize = namesize; + nvphdr.nvph_datasize = nvp->nvp_datasize; + PJDLOG_ASSERT(*leftp >= sizeof(nvphdr)); + memcpy(ptr, &nvphdr, sizeof(nvphdr)); + ptr += sizeof(nvphdr); + *leftp -= sizeof(nvphdr); + + PJDLOG_ASSERT(*leftp >= namesize); + memcpy(ptr, nvp->nvp_name, namesize); + ptr += namesize; + *leftp -= namesize; + + return (ptr); +} + +static unsigned char * +nvpair_pack_null(const nvpair_t *nvp, unsigned char *ptr, + size_t *leftp __unused) +{ + + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_NULL); + + return (ptr); +} + +static unsigned char * +nvpair_pack_bool(const nvpair_t *nvp, unsigned char *ptr, size_t *leftp) +{ + uint8_t value; + + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_BOOL); + + value = (uint8_t)nvp->nvp_data; + + PJDLOG_ASSERT(*leftp >= sizeof(value)); + memcpy(ptr, &value, sizeof(value)); + ptr += sizeof(value); + *leftp -= sizeof(value); + + return (ptr); +} + +static unsigned char * +nvpair_pack_number(const nvpair_t *nvp, unsigned char *ptr, size_t *leftp) +{ + uint64_t value; + + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_NUMBER); + + value = (uint64_t)nvp->nvp_data; + + PJDLOG_ASSERT(*leftp >= sizeof(value)); + memcpy(ptr, &value, sizeof(value)); + ptr += sizeof(value); + *leftp -= sizeof(value); + + return (ptr); +} + +static unsigned char * +nvpair_pack_string(const nvpair_t *nvp, unsigned char *ptr, size_t *leftp) +{ + + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_STRING); + + PJDLOG_ASSERT(*leftp >= nvp->nvp_datasize); + memcpy(ptr, (const void *)(intptr_t)nvp->nvp_data, nvp->nvp_datasize); + ptr += nvp->nvp_datasize; + *leftp -= nvp->nvp_datasize; + + return (ptr); +} + +static unsigned char * +nvpair_pack_nvlist(const nvpair_t *nvp, unsigned char *ptr, int64_t *fdidxp, + size_t *leftp) +{ + unsigned char *data; + size_t size; + + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_NVLIST); + + if (nvp->nvp_datasize == 0) + return (ptr); + + data = nvlist_xpack((const nvlist_t *)(intptr_t)nvp->nvp_data, fdidxp, + &size); + if (data == NULL) + return (NULL); + + PJDLOG_ASSERT(size == nvp->nvp_datasize); + PJDLOG_ASSERT(*leftp >= nvp->nvp_datasize); + + memcpy(ptr, data, nvp->nvp_datasize); + free(data); + + ptr += nvp->nvp_datasize; + *leftp -= nvp->nvp_datasize; + + return (ptr); +} + +static unsigned char * +nvpair_pack_descriptor(const nvpair_t *nvp, unsigned char *ptr, int64_t *fdidxp, + size_t *leftp) +{ + int64_t value; + + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_DESCRIPTOR); + + value = (int64_t)nvp->nvp_data; + if (value != -1) { + /* + * If there is a real descriptor here, we change its number + * to position in the array of descriptors send via control + * message. + */ + PJDLOG_ASSERT(fdidxp != NULL); + + value = *fdidxp; + (*fdidxp)++; + } + + PJDLOG_ASSERT(*leftp >= sizeof(value)); + memcpy(ptr, &value, sizeof(value)); + ptr += sizeof(value); + *leftp -= sizeof(value); + + return (ptr); +} + +static unsigned char * +nvpair_pack_binary(const nvpair_t *nvp, unsigned char *ptr, size_t *leftp) +{ + + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_BINARY); + + PJDLOG_ASSERT(*leftp >= nvp->nvp_datasize); + memcpy(ptr, (const void *)(intptr_t)nvp->nvp_data, nvp->nvp_datasize); + ptr += nvp->nvp_datasize; + *leftp -= nvp->nvp_datasize; + + return (ptr); +} + +unsigned char * +nvpair_pack(nvpair_t *nvp, unsigned char *ptr, int64_t *fdidxp, size_t *leftp) +{ + + NVPAIR_ASSERT(nvp); + + /* + * We have to update datasize for NV_TYPE_NVLIST on every pack, + * so that proper datasize is placed into nvpair_header + * during the nvpair_pack_header() call below. + */ + if (nvp->nvp_type == NV_TYPE_NVLIST) { + if (nvp->nvp_data == 0) { + nvp->nvp_datasize = 0; + } else { + nvp->nvp_datasize = + nvlist_size((const nvlist_t *)(intptr_t)nvp->nvp_data); + } + } + + ptr = nvpair_pack_header(nvp, ptr, leftp); + if (ptr == NULL) + return (NULL); + + switch (nvp->nvp_type) { + case NV_TYPE_NULL: + ptr = nvpair_pack_null(nvp, ptr, leftp); + break; + case NV_TYPE_BOOL: + ptr = nvpair_pack_bool(nvp, ptr, leftp); + break; + case NV_TYPE_NUMBER: + ptr = nvpair_pack_number(nvp, ptr, leftp); + break; + case NV_TYPE_STRING: + ptr = nvpair_pack_string(nvp, ptr, leftp); + break; + case NV_TYPE_NVLIST: + ptr = nvpair_pack_nvlist(nvp, ptr, fdidxp, leftp); + break; + case NV_TYPE_DESCRIPTOR: + ptr = nvpair_pack_descriptor(nvp, ptr, fdidxp, leftp); + break; + case NV_TYPE_BINARY: + ptr = nvpair_pack_binary(nvp, ptr, leftp); + break; + default: + PJDLOG_ABORT("Invalid type (%d).", nvp->nvp_type); + } + + return (ptr); +} + +static const unsigned char * +nvpair_unpack_header(int flags, nvpair_t *nvp, const unsigned char *ptr, + size_t *leftp) +{ + struct nvpair_header nvphdr; + + if (*leftp < sizeof(nvphdr)) + goto failed; + + memcpy(&nvphdr, ptr, sizeof(nvphdr)); + ptr += sizeof(nvphdr); + *leftp -= sizeof(nvphdr); + +#if NV_TYPE_FIRST > 0 + if (nvphdr.nvph_type < NV_TYPE_FIRST) + goto failed; +#endif + if (nvphdr.nvph_type > NV_TYPE_LAST) + goto failed; + +#if BYTE_ORDER == BIG_ENDIAN + if ((flags & NV_FLAG_BIG_ENDIAN) == 0) { + nvphdr.nvph_namesize = le16toh(nvphdr.nvph_namesize); + nvphdr.nvph_datasize = le64toh(nvphdr.nvph_datasize); + } +#else + if ((flags & NV_FLAG_BIG_ENDIAN) != 0) { + nvphdr.nvph_namesize = be16toh(nvphdr.nvph_namesize); + nvphdr.nvph_datasize = be64toh(nvphdr.nvph_datasize); + } +#endif + + if (nvphdr.nvph_namesize > NV_NAME_MAX) + goto failed; + if (*leftp < nvphdr.nvph_namesize) + goto failed; + if (nvphdr.nvph_namesize < 1) + goto failed; + if (strnlen((const char *)ptr, nvphdr.nvph_namesize) != + (size_t)(nvphdr.nvph_namesize - 1)) { + goto failed; + } + + memcpy(nvp->nvp_name, ptr, nvphdr.nvph_namesize); + ptr += nvphdr.nvph_namesize; + *leftp -= nvphdr.nvph_namesize; + + if (*leftp < nvphdr.nvph_datasize) + goto failed; + + nvp->nvp_type = nvphdr.nvph_type; + nvp->nvp_data = 0; + nvp->nvp_datasize = nvphdr.nvph_datasize; + + return (ptr); +failed: + errno = EINVAL; + return (NULL); +} + +static const unsigned char * +nvpair_unpack_null(int flags __unused, nvpair_t *nvp, const unsigned char *ptr, + size_t *leftp __unused) +{ + + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_NULL); + + if (nvp->nvp_datasize != 0) { + errno = EINVAL; + return (NULL); + } + + return (ptr); +} + +static const unsigned char * +nvpair_unpack_bool(int flags __unused, nvpair_t *nvp, const unsigned char *ptr, + size_t *leftp) +{ + uint8_t value; + + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_BOOL); + + if (nvp->nvp_datasize != sizeof(value)) { + errno = EINVAL; + return (NULL); + } + if (*leftp < sizeof(value)) { + errno = EINVAL; + return (NULL); + } + + memcpy(&value, ptr, sizeof(value)); + ptr += sizeof(value); + *leftp -= sizeof(value); + + if (value != 0 && value != 1) { + errno = EINVAL; + return (NULL); + } + + nvp->nvp_data = (uint64_t)value; + + return (ptr); +} + +static const unsigned char * +nvpair_unpack_number(int flags, nvpair_t *nvp, const unsigned char *ptr, + size_t *leftp) +{ + + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_NUMBER); + + if (nvp->nvp_datasize != sizeof(uint64_t)) { + errno = EINVAL; + return (NULL); + } + if (*leftp < sizeof(uint64_t)) { + errno = EINVAL; + return (NULL); + } + + if ((flags & NV_FLAG_BIG_ENDIAN) != 0) + nvp->nvp_data = be64dec(ptr); + else + nvp->nvp_data = le64dec(ptr); + ptr += sizeof(uint64_t); + *leftp -= sizeof(uint64_t); + + return (ptr); +} + +static const unsigned char * +nvpair_unpack_string(int flags __unused, nvpair_t *nvp, + const unsigned char *ptr, size_t *leftp) +{ + + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_STRING); + + if (*leftp < nvp->nvp_datasize || nvp->nvp_datasize == 0) { + errno = EINVAL; + return (NULL); + } + + if (strnlen((const char *)ptr, nvp->nvp_datasize) != + nvp->nvp_datasize - 1) { + errno = EINVAL; + return (NULL); + } + + nvp->nvp_data = (uint64_t)(uintptr_t)strdup((const char *)ptr); + if (nvp->nvp_data == 0) + return (NULL); + + ptr += nvp->nvp_datasize; + *leftp -= nvp->nvp_datasize; + + return (ptr); +} + +static const unsigned char * +nvpair_unpack_nvlist(int flags __unused, nvpair_t *nvp, + const unsigned char *ptr, size_t *leftp, const int *fds, size_t nfds) +{ + nvlist_t *value; + + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_NVLIST); + + if (*leftp < nvp->nvp_datasize || nvp->nvp_datasize == 0) { + errno = EINVAL; + return (NULL); + } + + value = nvlist_xunpack(ptr, nvp->nvp_datasize, fds, nfds); + if (value == NULL) + return (NULL); + + nvp->nvp_data = (uint64_t)(uintptr_t)value; + + ptr += nvp->nvp_datasize; + *leftp -= nvp->nvp_datasize; + + return (ptr); +} + +static const unsigned char * +nvpair_unpack_descriptor(int flags, nvpair_t *nvp, const unsigned char *ptr, + size_t *leftp, const int *fds, size_t nfds) +{ + int64_t idx; + + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_DESCRIPTOR); + + if (nvp->nvp_datasize != sizeof(idx)) { + errno = EINVAL; + return (NULL); + } + if (*leftp < sizeof(idx)) { + errno = EINVAL; + return (NULL); + } + + if ((flags & NV_FLAG_BIG_ENDIAN) != 0) + idx = be64dec(ptr); + else + idx = le64dec(ptr); + + if (idx < 0) { + errno = EINVAL; + return (NULL); + } + + if ((size_t)idx >= nfds) { + errno = EINVAL; + return (NULL); + } + + nvp->nvp_data = (uint64_t)fds[idx]; + + ptr += sizeof(idx); + *leftp -= sizeof(idx); + + return (ptr); +} + +static const unsigned char * +nvpair_unpack_binary(int flags __unused, nvpair_t *nvp, + const unsigned char *ptr, size_t *leftp) +{ + void *value; + + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_BINARY); + + if (*leftp < nvp->nvp_datasize || nvp->nvp_datasize == 0) { + errno = EINVAL; + return (NULL); + } + + value = malloc(nvp->nvp_datasize); + if (value == NULL) + return (NULL); + + memcpy(value, ptr, nvp->nvp_datasize); + ptr += nvp->nvp_datasize; + *leftp -= nvp->nvp_datasize; + + nvp->nvp_data = (uint64_t)(uintptr_t)value; + + return (ptr); +} + +const unsigned char * +nvpair_unpack(int flags, const unsigned char *ptr, size_t *leftp, + const int *fds, size_t nfds, nvpair_t **nvpp) +{ + nvpair_t *nvp, *tmp; + + nvp = calloc(1, sizeof(*nvp) + NV_NAME_MAX); + if (nvp == NULL) + return (NULL); + nvp->nvp_name = (char *)(nvp + 1); + + ptr = nvpair_unpack_header(flags, nvp, ptr, leftp); + if (ptr == NULL) + goto failed; + tmp = realloc(nvp, sizeof(*nvp) + strlen(nvp->nvp_name) + 1); + if (tmp == NULL) { + free(nvp); + goto failed; + } + nvp = tmp; + /* Update nvp_name after realloc(). */ + nvp->nvp_name = (char *)(nvp + 1); + + switch (nvp->nvp_type) { + case NV_TYPE_NULL: + ptr = nvpair_unpack_null(flags, nvp, ptr, leftp); + break; + case NV_TYPE_BOOL: + ptr = nvpair_unpack_bool(flags, nvp, ptr, leftp); + break; + case NV_TYPE_NUMBER: + ptr = nvpair_unpack_number(flags, nvp, ptr, leftp); + break; + case NV_TYPE_STRING: + ptr = nvpair_unpack_string(flags, nvp, ptr, leftp); + break; + case NV_TYPE_NVLIST: + ptr = nvpair_unpack_nvlist(flags, nvp, ptr, leftp, fds, + nfds); + break; + case NV_TYPE_DESCRIPTOR: + ptr = nvpair_unpack_descriptor(flags, nvp, ptr, leftp, fds, + nfds); + break; + case NV_TYPE_BINARY: + ptr = nvpair_unpack_binary(flags, nvp, ptr, leftp); + break; + default: + PJDLOG_ABORT("Invalid type (%d).", nvp->nvp_type); + } + + if (ptr == NULL) + goto failed; + + nvp->nvp_magic = NVPAIR_MAGIC; + *nvpp = nvp; + return (ptr); +failed: + free(nvp); + return (NULL); +} + +int +nvpair_type(const nvpair_t *nvp) +{ + + NVPAIR_ASSERT(nvp); + + return (nvp->nvp_type); +} + +const char * +nvpair_name(const nvpair_t *nvp) +{ + + NVPAIR_ASSERT(nvp); + + return (nvp->nvp_name); +} + +static nvpair_t * +nvpair_allocv(int type, uint64_t data, size_t datasize, const char *namefmt, + va_list nameap) +{ + nvpair_t *nvp; + char *name; + int namelen; + + PJDLOG_ASSERT(type >= NV_TYPE_FIRST && type <= NV_TYPE_LAST); + + namelen = vasprintf(&name, namefmt, nameap); + if (namelen < 0) + return (NULL); + + PJDLOG_ASSERT(namelen > 0); + if (namelen >= NV_NAME_MAX) { + free(name); + errno = ENAMETOOLONG; + return (NULL); + } + + nvp = calloc(1, sizeof(*nvp) + namelen + 1); + if (nvp != NULL) { + nvp->nvp_name = (char *)(nvp + 1); + memcpy(nvp->nvp_name, name, namelen + 1); + nvp->nvp_type = type; + nvp->nvp_data = data; + nvp->nvp_datasize = datasize; + nvp->nvp_magic = NVPAIR_MAGIC; + } + free(name); + + return (nvp); +}; + +nvpair_t * +nvpair_create_null(const char *name) +{ + + return (nvpair_createf_null("%s", name)); +} + +nvpair_t * +nvpair_create_bool(const char *name, bool value) +{ + + return (nvpair_createf_bool(value, "%s", name)); +} + +nvpair_t * +nvpair_create_number(const char *name, uint64_t value) +{ + + return (nvpair_createf_number(value, "%s", name)); +} + +nvpair_t * +nvpair_create_string(const char *name, const char *value) +{ + + return (nvpair_createf_string(value, "%s", name)); +} + +nvpair_t * +nvpair_create_stringf(const char *name, const char *valuefmt, ...) +{ + va_list valueap; + nvpair_t *nvp; + + va_start(valueap, valuefmt); + nvp = nvpair_create_stringv(name, valuefmt, valueap); + va_end(valueap); + + return (nvp); +} + +nvpair_t * +nvpair_create_stringv(const char *name, const char *valuefmt, va_list valueap) +{ + nvpair_t *nvp; + char *str; + int len; + + len = vasprintf(&str, valuefmt, valueap); + if (len < 0) + return (NULL); + nvp = nvpair_create_string(name, str); + if (nvp == NULL) + free(str); + return (nvp); +} + +nvpair_t * +nvpair_create_nvlist(const char *name, const nvlist_t *value) +{ + + return (nvpair_createf_nvlist(value, "%s", name)); +} + +nvpair_t * +nvpair_create_descriptor(const char *name, int value) +{ + + return (nvpair_createf_descriptor(value, "%s", name)); +} + +nvpair_t * +nvpair_create_binary(const char *name, const void *value, size_t size) +{ + + return (nvpair_createf_binary(value, size, "%s", name)); +} + +nvpair_t * +nvpair_createf_null(const char *namefmt, ...) +{ + va_list nameap; + nvpair_t *nvp; + + va_start(nameap, namefmt); + nvp = nvpair_createv_null(namefmt, nameap); + va_end(nameap); + + return (nvp); +} + +nvpair_t * +nvpair_createf_bool(bool value, const char *namefmt, ...) +{ + va_list nameap; + nvpair_t *nvp; + + va_start(nameap, namefmt); + nvp = nvpair_createv_bool(value, namefmt, nameap); + va_end(nameap); + + return (nvp); +} + +nvpair_t * +nvpair_createf_number(uint64_t value, const char *namefmt, ...) +{ + va_list nameap; + nvpair_t *nvp; + + va_start(nameap, namefmt); + nvp = nvpair_createv_number(value, namefmt, nameap); + va_end(nameap); + + return (nvp); +} + +nvpair_t * +nvpair_createf_string(const char *value, const char *namefmt, ...) +{ + va_list nameap; + nvpair_t *nvp; + + va_start(nameap, namefmt); + nvp = nvpair_createv_string(value, namefmt, nameap); + va_end(nameap); + + return (nvp); +} + +nvpair_t * +nvpair_createf_nvlist(const nvlist_t *value, const char *namefmt, ...) +{ + va_list nameap; + nvpair_t *nvp; + + va_start(nameap, namefmt); + nvp = nvpair_createv_nvlist(value, namefmt, nameap); + va_end(nameap); + + return (nvp); +} + +nvpair_t * +nvpair_createf_descriptor(int value, const char *namefmt, ...) +{ + va_list nameap; + nvpair_t *nvp; + + va_start(nameap, namefmt); + nvp = nvpair_createv_descriptor(value, namefmt, nameap); + va_end(nameap); + + return (nvp); +} + +nvpair_t * +nvpair_createf_binary(const void *value, size_t size, const char *namefmt, ...) +{ + va_list nameap; + nvpair_t *nvp; + + va_start(nameap, namefmt); + nvp = nvpair_createv_binary(value, size, namefmt, nameap); + va_end(nameap); + + return (nvp); +} + +nvpair_t * +nvpair_createv_null(const char *namefmt, va_list nameap) +{ + + return (nvpair_allocv(NV_TYPE_NULL, 0, 0, namefmt, nameap)); +} + +nvpair_t * +nvpair_createv_bool(bool value, const char *namefmt, va_list nameap) +{ + + return (nvpair_allocv(NV_TYPE_BOOL, value ? 1 : 0, sizeof(uint8_t), + namefmt, nameap)); +} + +nvpair_t * +nvpair_createv_number(uint64_t value, const char *namefmt, va_list nameap) +{ + + return (nvpair_allocv(NV_TYPE_NUMBER, value, sizeof(value), namefmt, + nameap)); +} + +nvpair_t * +nvpair_createv_string(const char *value, const char *namefmt, va_list nameap) +{ + nvpair_t *nvp; + size_t size; + char *data; + + if (value == NULL) { + errno = EINVAL; + return (NULL); + } + + data = strdup(value); + if (data == NULL) + return (NULL); + size = strlen(value) + 1; + + nvp = nvpair_allocv(NV_TYPE_STRING, (uint64_t)(uintptr_t)data, size, + namefmt, nameap); + if (nvp == NULL) + free(data); + + return (nvp); +} + +nvpair_t * +nvpair_createv_nvlist(const nvlist_t *value, const char *namefmt, + va_list nameap) +{ + nvlist_t *nvl; + nvpair_t *nvp; + + if (value == NULL) { + errno = EINVAL; + return (NULL); + } + + nvl = nvlist_clone(value); + if (nvl == NULL) + return (NULL); + + nvp = nvpair_allocv(NV_TYPE_NVLIST, (uint64_t)(uintptr_t)nvl, 0, + namefmt, nameap); + if (nvp == NULL) + nvlist_destroy(nvl); + + return (nvp); +} + +nvpair_t * +nvpair_createv_descriptor(int value, const char *namefmt, va_list nameap) +{ + nvpair_t *nvp; + + if (value < 0 || !fd_is_valid(value)) { + errno = EBADF; + return (NULL); + } + + value = fcntl(value, F_DUPFD_CLOEXEC, 0); + if (value < 0) + return (NULL); + + nvp = nvpair_allocv(NV_TYPE_DESCRIPTOR, (uint64_t)value, + sizeof(int64_t), namefmt, nameap); + if (nvp == NULL) + close(value); + + return (nvp); +} + +nvpair_t * +nvpair_createv_binary(const void *value, size_t size, const char *namefmt, + va_list nameap) +{ + nvpair_t *nvp; + void *data; + + if (value == NULL || size == 0) { + errno = EINVAL; + return (NULL); + } + + data = malloc(size); + if (data == NULL) + return (NULL); + memcpy(data, value, size); + + nvp = nvpair_allocv(NV_TYPE_BINARY, (uint64_t)(uintptr_t)data, size, + namefmt, nameap); + if (nvp == NULL) + free(data); + + return (nvp); +} + +nvpair_t * +nvpair_move_string(const char *name, char *value) +{ + + return (nvpair_movef_string(value, "%s", name)); +} + +nvpair_t * +nvpair_move_nvlist(const char *name, nvlist_t *value) +{ + + return (nvpair_movef_nvlist(value, "%s", name)); +} + +nvpair_t * +nvpair_move_descriptor(const char *name, int value) +{ + + return (nvpair_movef_descriptor(value, "%s", name)); +} + +nvpair_t * +nvpair_move_binary(const char *name, void *value, size_t size) +{ + + return (nvpair_movef_binary(value, size, "%s", name)); +} + +nvpair_t * +nvpair_movef_string(char *value, const char *namefmt, ...) +{ + va_list nameap; + nvpair_t *nvp; + + va_start(nameap, namefmt); + nvp = nvpair_movev_string(value, namefmt, nameap); + va_end(nameap); + + return (nvp); +} + +nvpair_t * +nvpair_movef_nvlist(nvlist_t *value, const char *namefmt, ...) +{ + va_list nameap; + nvpair_t *nvp; + + va_start(nameap, namefmt); + nvp = nvpair_movev_nvlist(value, namefmt, nameap); + va_end(nameap); + + return (nvp); +} + +nvpair_t * +nvpair_movef_descriptor(int value, const char *namefmt, ...) +{ + va_list nameap; + nvpair_t *nvp; + + va_start(nameap, namefmt); + nvp = nvpair_movev_descriptor(value, namefmt, nameap); + va_end(nameap); + + return (nvp); +} + +nvpair_t * +nvpair_movef_binary(void *value, size_t size, const char *namefmt, ...) +{ + va_list nameap; + nvpair_t *nvp; + + va_start(nameap, namefmt); + nvp = nvpair_movev_binary(value, size, namefmt, nameap); + va_end(nameap); + + return (nvp); +} + +nvpair_t * +nvpair_movev_string(char *value, const char *namefmt, va_list nameap) +{ + nvpair_t *nvp; + + if (value == NULL) { + errno = EINVAL; + return (NULL); + } + + nvp = nvpair_allocv(NV_TYPE_STRING, (uint64_t)(uintptr_t)value, + strlen(value) + 1, namefmt, nameap); + if (nvp == NULL) + free(value); + + return (nvp); +} + +nvpair_t * +nvpair_movev_nvlist(nvlist_t *value, const char *namefmt, va_list nameap) +{ + nvpair_t *nvp; + + if (value == NULL) { + errno = EINVAL; + return (NULL); + } + + nvp = nvpair_allocv(NV_TYPE_NVLIST, (uint64_t)(uintptr_t)value, 0, + namefmt, nameap); + if (nvp == NULL) + nvlist_destroy(value); + + return (nvp); +} + +nvpair_t * +nvpair_movev_descriptor(int value, const char *namefmt, va_list nameap) +{ + + if (value < 0 || !fd_is_valid(value)) { + errno = EBADF; + return (NULL); + } + + return (nvpair_allocv(NV_TYPE_DESCRIPTOR, (uint64_t)value, + sizeof(int64_t), namefmt, nameap)); +} + +nvpair_t * +nvpair_movev_binary(void *value, size_t size, const char *namefmt, + va_list nameap) +{ + + if (value == NULL || size == 0) { + errno = EINVAL; + return (NULL); + } + + return (nvpair_allocv(NV_TYPE_BINARY, (uint64_t)(uintptr_t)value, size, + namefmt, nameap)); +} + +bool +nvpair_get_bool(const nvpair_t *nvp) +{ + + NVPAIR_ASSERT(nvp); + + return (nvp->nvp_data == 1); +} + +uint64_t +nvpair_get_number(const nvpair_t *nvp) +{ + + NVPAIR_ASSERT(nvp); + + return (nvp->nvp_data); +} + +const char * +nvpair_get_string(const nvpair_t *nvp) +{ + + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_STRING); + + return ((const char *)(intptr_t)nvp->nvp_data); +} + +const nvlist_t * +nvpair_get_nvlist(const nvpair_t *nvp) +{ + + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_NVLIST); + + return ((const nvlist_t *)(intptr_t)nvp->nvp_data); +} + +int +nvpair_get_descriptor(const nvpair_t *nvp) +{ + + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_DESCRIPTOR); + + return ((int)nvp->nvp_data); +} + +const void * +nvpair_get_binary(const nvpair_t *nvp, size_t *sizep) +{ + + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvp->nvp_type == NV_TYPE_BINARY); + + if (sizep != NULL) + *sizep = nvp->nvp_datasize; + return ((const void *)(intptr_t)nvp->nvp_data); +} + +void +nvpair_free(nvpair_t *nvp) +{ + + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvp->nvp_list == NULL); + + nvp->nvp_magic = 0; + switch (nvp->nvp_type) { + case NV_TYPE_DESCRIPTOR: + close((int)nvp->nvp_data); + break; + case NV_TYPE_NVLIST: + nvlist_destroy((nvlist_t *)(intptr_t)nvp->nvp_data); + break; + case NV_TYPE_STRING: + free((char *)(intptr_t)nvp->nvp_data); + break; + case NV_TYPE_BINARY: + free((void *)(intptr_t)nvp->nvp_data); + break; + } + free(nvp); +} + +void +nvpair_free_structure(nvpair_t *nvp) +{ + + NVPAIR_ASSERT(nvp); + PJDLOG_ASSERT(nvp->nvp_list == NULL); + + nvp->nvp_magic = 0; + free(nvp); +} + +const char * +nvpair_type_string(int type) +{ + + switch (type) { + case NV_TYPE_NULL: + return ("NULL"); + case NV_TYPE_BOOL: + return ("BOOL"); + case NV_TYPE_NUMBER: + return ("NUMBER"); + case NV_TYPE_STRING: + return ("STRING"); + case NV_TYPE_NVLIST: + return ("NVLIST"); + case NV_TYPE_DESCRIPTOR: + return ("DESCRIPTOR"); + case NV_TYPE_BINARY: + return ("BINARY"); + default: + return (""); + } +} diff --git a/lib/libnv/nvpair_impl.h b/lib/libnv/nvpair_impl.h new file mode 100644 index 000000000000..aa4046c2c3b8 --- /dev/null +++ b/lib/libnv/nvpair_impl.h @@ -0,0 +1,58 @@ +/*- + * Copyright (c) 2009-2013 The FreeBSD Foundation + * 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. + * + * $FreeBSD$ + */ + +#ifndef _NVPAIR_IMPL_H_ +#define _NVPAIR_IMPL_H_ + +#include + +#include + +#include "nv.h" + +TAILQ_HEAD(nvl_head, nvpair); + +void nvpair_assert(const nvpair_t *nvp); +const nvlist_t *nvpair_nvlist(const nvpair_t *nvp); +nvpair_t *nvpair_next(const nvpair_t *nvp); +nvpair_t *nvpair_prev(const nvpair_t *nvp); +void nvpair_insert(struct nvl_head *head, nvpair_t *nvp, nvlist_t *nvl); +void nvpair_remove(struct nvl_head *head, nvpair_t *nvp, const nvlist_t *nvl); +size_t nvpair_header_size(void); +size_t nvpair_size(const nvpair_t *nvp); +unsigned char *nvpair_pack(nvpair_t *nvp, unsigned char *ptr, int64_t *fdidxp, + size_t *leftp); +const unsigned char *nvpair_unpack(int flags, const unsigned char *ptr, + size_t *leftp, const int *fds, size_t nfds, nvpair_t **nvpp); +void nvpair_free_structure(nvpair_t *nvp); +const char *nvpair_type_string(int type); + +#endif /* !_NVPAIR_IMPL_H_ */ diff --git a/share/mk/bsd.libnames.mk b/share/mk/bsd.libnames.mk index 7d224cf8e711..4ccf6a432922 100644 --- a/share/mk/bsd.libnames.mk +++ b/share/mk/bsd.libnames.mk @@ -97,6 +97,7 @@ LIBNCURSES?= ${DESTDIR}${LIBDIR}/libncurses.a LIBNCURSESW?= ${DESTDIR}${LIBDIR}/libncursesw.a LIBNETGRAPH?= ${DESTDIR}${LIBDIR}/libnetgraph.a LIBNGATM?= ${DESTDIR}${LIBDIR}/libngatm.a +LIBNV?= ${DESTDIR}${LIBDIR}/libnv.a LIBNVPAIR?= ${DESTDIR}${LIBDIR}/libnvpair.a LIBOPIE?= ${DESTDIR}${LIBDIR}/libopie.a