NV(9)
โจ NAME โจ
okay so bestie, this is NV(9) โ straight out of the Kernel Developer's Manual, and it's the whole manual page for a library that does exactly one iconic thing: name/value pairs ๐. no cap.
the squad of functions on this page is: nvlist_create, nvlist_destroy, nvlist_error, nvlist_set_error, nvlist_empty, nvlist_flags, nvlist_exists, nvlist_free, nvlist_clone, nvlist_dump, nvlist_fdump, nvlist_size, nvlist_pack, nvlist_unpack, nvlist_send, nvlist_recv, nvlist_xfer, nvlist_in_array, nvlist_next, nvlist_add, nvlist_move, nvlist_get, nvlist_take, and nvlist_append โ a library for name/value pairs ๐
.
๐ธ LIBRARY ๐ธ
the library is literally just: library โlibnvโ. that's it. that's the whole vibe. ๐ซถ
๐ฆ SYNOPSIS ๐ฆ
first things first, you gotta pull it in:
#include <sys/nv.h>
and here's every single prototype, verbatim, no edits, no skips, because we don't do that here:
nvlist_t *
nvlist_create(int flags);
void
nvlist_destroy(nvlist_t *nvl);
int
nvlist_error(const nvlist_t *nvl);
void
nvlist_set_error(nvlist_t *nvl, int error);
bool
nvlist_empty(const nvlist_t *nvl);
int
nvlist_flags(const nvlist_t *nvl);
bool
nvlist_in_array(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 flags);
int
nvlist_send(int sock, const nvlist_t *nvl);
nvlist_t *
nvlist_recv(int sock, int flags);
nvlist_t *
nvlist_xfer(int sock, nvlist_t *nvl, int flags);
const char *
nvlist_next(const nvlist_t *nvl, int *typep, void **cookiep);
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);
bool
nvlist_exists_bool_array(const nvlist_t *nvl, const char *name);
bool
nvlist_exists_number_array(const nvlist_t *nvl, const char *name);
bool
nvlist_exists_string_array(const nvlist_t *nvl, const char *name);
bool
nvlist_exists_nvlist_array(const nvlist_t *nvl, const char *name);
bool
nvlist_exists_descriptor_array(const nvlist_t *nvl, const char *name);
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, ...);
void
nvlist_add_stringv(nvlist_t *nvl, const char *name,
const char *valuefmt, va_list valueap);
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);
void
nvlist_add_bool_array(nvlist_t *nvl, const char *name,
const bool *value, size_t nitems);
void
nvlist_add_number_array(nvlist_t *nvl, const char *name,
const uint64_t *value, size_t nitems);
void
nvlist_add_string_array(nvlist_t *nvl, const char *name,
const char * const * value, size_t nitems);
void
nvlist_add_nvlist_array(nvlist_t *nvl, const char *name,
const nvlist_t * const * value, size_t nitems);
void
nvlist_add_descriptor_array(nvlist_t *nvl, const char *name,
const int *value, size_t nitems);
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);
void
nvlist_move_bool_array(nvlist_t *nvl, const char *name, bool *value,
size_t nitems);
void
nvlist_move_number_array(nvlist_t *nvl, const char *name,
uint64_t *value, size_t nitems);
void
nvlist_move_string_array(nvlist_t *nvl, const char *name, char **value,
size_t nitems);
void
nvlist_move_nvlist_array(nvlist_t *nvl, const char *name,
nvlist_t **value, size_t nitems);
void
nvlist_move_descriptor_array(nvlist_t *nvl, const char *name,
int *value, size_t nitems);
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);
const bool *
nvlist_get_bool_array(const nvlist_t *nvl, const char *name,
size_t *nitems);
const uint64_t *
nvlist_get_number_array(const nvlist_t *nvl, const char *name,
size_t *nitems);
const char * const *
nvlist_get_string_array(const nvlist_t *nvl, const char *name,
size_t *nitems);
const nvlist_t * const *
nvlist_get_nvlist_array(const nvlist_t *nvl, const char *name,
size_t *nitems);
const int *
nvlist_get_descriptor_array(const nvlist_t *nvl, const char *name,
size_t *nitems);
const nvlist_t *
nvlist_get_parent(const nvlist_t *nvl, void **cookiep);
const nvlist_t *
nvlist_get_array_next(const nvlist_t *nvl);
const nvlist_t *
nvlist_get_pararr(const nvlist_t *nvl, void **cookiep);
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);
bool *
nvlist_take_bool_array(nvlist_t *nvl, const char *name, size_t *nitems);
uint64_t **
nvlist_take_number_array(nvlist_t *nvl, const char *name,
size_t *nitems);
char **
nvlist_take_string_array(nvlist_t *nvl, const char *name,
size_t *nitems);
nvlist_t **
nvlist_take_nvlist_array(nvlist_t *nvl, const char *name,
size_t *nitems);
int *
nvlist_take_descriptor_array(nvlist_t *nvl, const char *name,
size_t *nitems);
void
nvlist_append_bool_array(nvlist_t *nvl, const char *name,
const bool value);
void
nvlist_append_number_array(nvlist_t *nvl, const char *name,
const uint64_t value);
void
nvlist_append_string_array(nvlist_t *nvl, const char *name,
const char * const value);
void
nvlist_append_nvlist_array(nvlist_t *nvl, const char *name,
const nvlist_t * const value);
void
nvlist_append_descriptor_array(nvlist_t *nvl, const char *name,
int value);
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);
void
nvlist_free_bool_array(nvlist_t *nvl, const char *name);
void
nvlist_free_number_array(nvlist_t *nvl, const char *name);
void
nvlist_free_string_array(nvlist_t *nvl, const char *name);
void
nvlist_free_nvlist_array(nvlist_t *nvl, const char *name);
void
nvlist_free_descriptor_array(nvlist_t *nvl, const char *name);
like. look at that list. it's giving thorough. ๐ญ
๐ DESCRIPTION ๐
so the libnv library is here to let you easily manage name value pairs, and also send and receive them over sockets. iconic behavior honestly. a group (list) of name value pairs is called an nvlist โ that's the word, learn it, love it ๐ซถ.
the API supports these data types, and you will be respecting all of them:
- null (
NV_TYPE_NULL) โ there is no data associated with the name. it's just a name, vibing. the name is the whole personality. - bool (
NV_TYPE_BOOL) โ the value can be eithertrueorfalse. binary. two options. very decisive of it. - number (
NV_TYPE_NUMBER) โ the value is a number stored asuint64_t. - string (
NV_TYPE_STRING) โ the value is a C string. - nvlist (
NV_TYPE_NVLIST) โ the value is a nested nvlist. nvlists inside nvlists, it's giving inception ๐. - descriptor (
NV_TYPE_DESCRIPTOR) โ the value is a file descriptor. โ ๏ธ note that file descriptors can be sent only overunix(4)domain sockets, so don't get cute with other socket types bestie, it will not work. - binary (
NV_TYPE_BINARY) โ the value is a binary buffer. - bool array (
NV_TYPE_BOOL_ARRAY) โ the value is an array of boolean values. - number array (
NV_TYPE_NUMBER_ARRAY) โ the value is an array of numbers, each stored asuint64_t. - string array (
NV_TYPE_STRING_ARRAY) โ the value is an array of C strings. - nvlist array (
NV_TYPE_NVLIST_ARRAY) โ the value is an array of nvlists. heads up: when an nvlist is added to an array, it becomes part of the primary nvlist. traversing these arrays can be done using thenvlist_get_array_next()andnvlist_get_pararr()functions. - descriptor array (
NV_TYPE_DESCRIPTOR_ARRAY) โ the value is an array of files descriptors. (yes, the plural is exactly like that in the original, we don't fix, we don't touch ๐ )
๐ the opening move: nvlist_create() ๐
the nvlist_create() function allocates memory and initializes an nvlist. new nvlist, who dis.
and bestie, you get to hand it a flag. here's what's on the menu:
NV_FLAG_IGNORE_CASEโ perform case-insensitive lookups of provided names. so"BESTIE"and"bestie"are the same girl.NV_FLAG_NO_UNIQUEโ names in the nvlist do not have to be unique. chaos mode, enabled.
๐ the cleanup: nvlist_destroy() ๐
the nvlist_destroy() function destroys the given nvlist. a few things to know:
- it does nothing if a
NULLnvlist is provided โ safe, no crash, no drama. - it never modifies the
errnoglobal variable. not once. not ever. respectful queen ๐.
๐ญ the drama: nvlist_error() and nvlist_set_error() ๐ญ
the nvlist_error() function returns any error value that the nvlist accumulated. and plot twist: if the given nvlist is NULL, the ENOMEM error will be returned. yeah. even NULL has an error. relatable.
the nvlist_set_error() function sets an nvlist to be in the error state. after that:
- subsequent calls to
nvlist_error()will return the given error value. - > this function cannot be used to clear the error state from an nvlist. you can set it, you cannot unset it. it's permanent. it's a tattoo.
- and this function does nothing if the nvlist is already in the error state. once it's in its era, it stays in its era. ๐
nvlist_empty() โ bestie, this one's simple: it hands back true if the nvlist you gave it is empty, and false if it's not. That's it, that's the whole vibe. โ ๏ธ but the nvlist must not be in error state, okay?
nvlist_flags() โ returns the flags that were used to create the nvlist, the ones you passed to nvlist_create(). ๐
nvlist_in_array() โ returns true if nvl is part of an array that's a member of another nvlist. Array-girlie behavior, we love to see it. ๐ฆ
nvlist_clone() โ clones the nvlist you hand it, and the clone shares no resources with its origin. None. Zero. Which also means all the file descriptors hanging out inside the nvlist get duplicated with the dup(2) system call before they get placed in the clone. โจ
nvlist_dump() โ dumps the nvlist content to the file descriptor fd, purely for debugging purposes. It's giving "peek behind the curtain" energy. ๐
nvlist_fdump() โ same debugging dump situation, but to the file stream fp instead. ๐
nvlist_size() โ returns the size of the nvlist after converting it to a binary buffer with nvlist_pack(). So it's the packed size, not the vibe size. ๐
nvlist_pack() โ converts the nvlist into a binary buffer. It allocates the memory for that buffer, and you are the one who frees it, with free(3). If sizep isn't NULL, the size of the buffer gets stashed right there. It returns NULL if something goes wrong (allocation failure). And heads up: if the nvlist contains any file descriptors, NULL is what you get back. Also the nvlist must not be in error state. ๐
nvlist_unpack() โ the reverse glow-up: turns the given buffer into an nvlist. The flags argument says what type of top level nvlist you're expecting, and those flags get set up with the nvlist_create() function. If the nvlist flags don't match the flags you passed to nvlist_unpack(), the nvlist will not be returned. And every nested nvlist should be checked with nvlist_flags(). Returns NULL on error. ๐ซถ
nvlist_send() โ sends the nvlist over the socket given by the sock argument. ๐ฎ Important: an nvlist that contains file descriptors can only be sent over unix(4) domain sockets. Periodt.
nvlist_recv() โ receives an nvlist over the socket given by sock. The flags argument defines what type of top level nvlist is expected, set up with nvlist_create(). If the nvlist flags don't match the flags passed to nvlist_recv(), the nvlist won't be returned. Check every nested nvlist with nvlist_flags(). ๐
nvlist_xfer() โ sends the nvlist over the socket sock and receives an nvlist over that same socket. Flags situation is identical to the other two: set up with nvlist_create(), must match what you pass to nvlist_xfer() or the nvlist isn't coming back, and every nested nvlist should be checked with nvlist_flags(). Oh and the nvlist you handed in? It's always destroyed. Gone. Consumed. ๐ฅ
nvlist_next() โ iterates over the nvlist, returning names and types of the subsequent elements. The cookiep argument is how it figures out which element comes next. Set *cookiep to NULL on the first call and then don't touch it after that. Returning NULL means there are no more elements on the nvlist. typep can be NULL. Elements may not be removed from the nvlist while you're traversing it โ do not do it, I'm serious. ๐ญ The nvlist must not be in error state. Fun detail: nvlist_next() handles cookiep being set to NULL, and in that case the first element is returned, or NULL if the nvlist is empty. That behavior is literally there to make removing the first element from the list easier. Iconic design tbh. ๐
nvlist_exists() โ returns true if an element of the given name exists (besides its type), false otherwise. nvlist must not be in error state.
nvlist_exists_type() โ returns true if an element with the given name and the given type exists, false otherwise. nvlist must not be in error state.
The type-specific existence squad โ nvlist_exists_null(), nvlist_exists_bool(), nvlist_exists_number(), nvlist_exists_string(), nvlist_exists_nvlist(), nvlist_exists_descriptor(), nvlist_exists_binary(), nvlist_exists_bool_array(), nvlist_exists_number_array(), nvlist_exists_string_array(), nvlist_exists_nvlist_array(), nvlist_exists_descriptor_array() โ all of these return true if an element with the given name and the given type (the type is determined by which function you called, iykyk) exists, false otherwise. nvlist must not be in error state. ๐
The add squad โ nvlist_add_null(), nvlist_add_bool(), nvlist_add_number(), nvlist_add_string(), nvlist_add_stringf(), nvlist_add_stringv(), nvlist_add_nvlist(), nvlist_add_descriptor(), nvlist_add_binary(), nvlist_add_bool_array(), nvlist_add_number_array(), nvlist_add_string_array(), nvlist_add_nvlist_array(), nvlist_add_descriptor_array() โ these add an element to the given nvlist. Here's the tea:
- Adding a string or a binary buffer? The function allocates memory and copies the data over. ๐ซถ
- Adding an nvlist? It gets cloned, and the clone is what gets added.
- Adding a descriptor? It gets duplicated with the
dup(2)system call, and the new descriptor is what gets added. - The array functions will fail if there are any
NULLelements in the array, or if the array pointer itself isNULL. - If an error occurs while adding the new element, an internal error is set, and you can inspect it with
nvlist_error(). ๐
The move squad โ nvlist_move_string(), nvlist_move_nvlist(), nvlist_move_descriptor(), nvlist_move_binary(), nvlist_move_bool_array(), nvlist_move_number_array(), nvlist_move_string_array(), nvlist_move_nvlist_array(), nvlist_move_descriptor_array() โ these add a new element to the nvlist too, but unlike the nvlist_add_<type>() functions they consume the resource you give them. โ ๏ธ For strings, descriptors, or nvlists, every element must be unique, or it could cause a double free. The array functions fail if there are any NULL elements or if the array pointer is NULL. And if an error occurs while adding, the resource is destroyed and an internal error is set, checkable with nvlist_error(). ๐
The get squad โ nvlist_get_bool(), nvlist_get_number(), nvlist_get_string(), nvlist_get_nvlist(), nvlist_get_descriptor(), nvlist_get_binary(), nvlist_get_bool_array(), nvlist_get_number_array(), nvlist_get_string_array(), nvlist_get_nvlist_array(), nvlist_get_descriptor_array() โ these return the value that corresponds to the given key name. For strings, nvlists, descriptors, binary, or arrays, the returned resource should not be modified โ it still belongs to the nvlist, bestie, don't go messing with it. ๐ซ If an element with the given name doesn't exist, the program will be aborted. To dodge that whole situation, check that the name exists before you try to grab the value, or use the dnvlist(3) extension, which can hand you a default value when the element is missing. The nvlist must not be in error state. ๐ญ
nvlist_get_parent() โ returns the parent nvlist of the nested nvlist. Family reunion. ๐ซถ
nvlist_get_array_next() โ returns the next element from the array, or NULL if the nvlist isn't in an array or if it's the last element. Note: this only works if you added the nvlist array using nvlist_move_nvlist_array() or nvlist_add_nvlist_array(). ๐
nvlist_get_pararr() โ returns the next element in the array, or if that's not available, the parent of the nested nvlist. Two-for-one. โจ
The take squad โ nvlist_take_bool(), nvlist_take_number(), nvlist_take_string(), nvlist_take_nvlist(), nvlist_take_descriptor(), nvlist_take_binary(), nvlist_take_bool_array(), nvlist_take_number_array(), nvlist_take_string_array(), nvlist_take_nvlist_array(), nvlist_take_descriptor_array() โ these return the value associated with the given name and remove the element from the nvlist. You're taking it, hence the name, slay. ๐ Now the fine print, and it's a lot:
- String and binary values? You're responsible for freeing the returned memory with
free(3). - nvlist? You're responsible for destroying the returned nvlist with
nvlist_destroy(). - Descriptor? You're responsible for closing the returned descriptor with the
close(2)system call. - If an element with the given name doesn't exist, the program will be aborted. To avoid that, check that the name exists before you try to take the value, or use the
dnvlist(3)extension, which can give you a default value when the element is missing. - Array of strings or binary values? You're responsible for freeing every element of the array with
free(3). - Array of nvlists? You're responsible for destroying every element of the array with
nvlist_destroy(). - Descriptors? You're responsible for closing every element of the array with the
close(2)system call. - And in every case involving an array, you must also free the pointer to the array using
free(3).
The nvlist must not be in error state. ๐
The append squad โ nvlist_append_bool_array(), nvlist_append_number_array(), nvlist_append_string_array(), nvlist_append_nvlist_array(), nvlist_append_descriptor_array() โ these append an element to the existing array, using the same semantics as the add functions (i.e. the element gets copied when applicable). If the array for a given key doesn't exist, it gets created as if you'd used the nvlist_add_<type>_array() function. The internal error is set on append failure. ๐ฅ
nvlist_free() โ removes the element of the given name from the nvlist (besides its type) and frees all the resources associated with it. If an element of the given name doesn't exist, the program will be aborted. The nvlist must not be in error state. ๐ง
nvlist_free_type() โ removes the element of the given name and the given type from the nvlist and frees all resources associated with it. If an element of the given name and the given type doesn't exist, the program will be aborted. The nvlist must not be in error state. ๐
The typed free squad โ nvlist_free_null(), nvlist_free_bool(), nvlist_free_number(), nvlist_free_string(), nvlist_free_nvlist(), nvlist_free_descriptor(), nvlist_free_binary(), nvlist_free_bool_array(), nvlist_free_number_array(), nvlist_free_string_array(), nvlist_free_nvlist_array(), nvlist_free_descriptor_array() โ these remove the element of the given name and the given type (the type is determined by the function name) from the nvlist and free all resources associated with it. If an element of the given name and the given type doesn't exist, the program will be aborted. The nvlist must not be in error state. ๐ค
๐ NOTES ๐
Okay so this part is genuinely so cool: the nvlist_pack() and nvlist_unpack() functions handle the byte-order conversions for you. Which means the binary buffer can be packed and unpacked by hosts with different endianness. No cap, it just works. โจ๐ฆ๐
โจ EXAMPLES โจ
okay bestie this is the part where it all comes together ๐ซถ these are the actual patterns you'll be copy-pasting into your own code, so pay attention, this is the good stuff.
๐ preparing an nvlist and sending it over a unix(4) domain socket
the following example demonstrates how to prepare an nvlist and send it over unix(4) domain socket. look at her go โจ
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_<type>() 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);
notice the little love note in there โ you don't need to check whether nvlist_create() succeeded, because the nvlist_add_<type>() functions can cope. and if it did fail, nvlist_send() will fail. the girlies at the API really said "we got you" ๐
also lowkey iconic: we use nvlist_move_descriptor and not nvlist_add because we want the nvlist to consume the descriptor. move = she's yours now, bestie ๐
๐ฆ receiving an nvlist and getting data out of it
nvlist_t *nvl;
const char *command;
char *filename;
int fd;
nvl = nvlist_recv(sock, 0);
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=%d0, command, filename, fd);
nvlist_destroy(nvl);
free(filename);
close(fd);
/* command was freed by nvlist_destroy() */
the tea here is the difference between get and take, and it's giving main character energy:
nvlist_get_string()โ you just get a pointer to the nvlist's own buffer. she still owns it, you're just borrowing. ๐nvlist_take_string()โ you remove it from the nvlist and take ownership of the buffer. it's yours now, you free it.nvlist_take_descriptor()โ same deal, but for the descriptor.
fr fr, remember:commandwas freed bynvlist_destroy(), butfilenameis on you โ hence thefree(filename)andclose(fd). don't leave her hanging ๐
๐ธ iterating over an nvlist
nvlist_t *nvl;
const char *name;
void *cookie;
int type;
nvl = nvlist_recv(sock, 0);
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");
}
see the cookie? that's your little bookmark ๐ช โ set it to NULL to start, and nvlist_next() hands you the next name and type, updating the cookie as she goes. when she returns NULL, you're done, periodt.
๐ง iterating over every nested nvlist
nvlist_t *nvl;
const char *name;
void *cookie;
int type;
nvl = nvlist_recv(sock, 0);
if (nvl == NULL)
err(1, "nvlist_recv() failed");
cookie = NULL;
do {
while ((name = nvlist_next(nvl, &type, &cookie)) != NULL) {
if (type == NV_TYPE_NVLIST) {
nvl = nvlist_get_nvlist(nvl, name);
cookie = NULL;
}
}
} while ((nvl = nvlist_get_parent(nvl, &cookie)) != NULL);
nested nvlists are like a whole friend group of nvlists ๐ when you hit an NV_TYPE_NVLIST, you dive into it with nvlist_get_nvlist() and reset that cookie to NULL because you're in a brand new list. then nvlist_get_parent() walks you back up. it's a whole journey.
๐ iterating over every nested nvlist and every nvlist element
nvlist_t *nvl;
const nvlist_t * const *array;
const char *name;
void *cookie;
int type;
nvl = nvlist_recv(sock, 0);
if (nvl == null)
err(1, "nvlist_recv() failed");
cookie = null;
do {
while ((name = nvlist_next(nvl, &type, &cookie)) != NULL) {
if (type == NV_TYPE_NVLIST) {
nvl = nvlist_get_nvlist(nvl, name);
cookie = NULL;
} else if (type == NV_TYPE_NVLIST_ARRAY) {
nvl = nvlist_get_nvlist_array(nvl, name, NULL)[0];
cookie = NULL;
}
}
} while ((nvl = nvlist_get_pararr(nvl, &cookie)) != NULL);
now we also handle NV_TYPE_NVLIST_ARRAY, because arrays of nvlists are absolutely valid and we are not ignoring them ๐
nvlist_get_pararr() walks back up through the parent array, no cap.
๐ or alternatively:
nvlist_t *nvl, *tmp;
const nvlist_t * const *array;
const char *name;
void *cookie;
int type;
nvl = nvlist_recv(sock, 0);
if (nvl == null)
err(1, "nvlist_recv() failed");
cooke = NULL;
tmp = nvl;
do {
do {
nvl = tmp;
while ((name = nvlist_next(nvl, &type, &cookie)) != NULL) {
if (type == NV_TYPE_NVLIST) {
nvl = nvlist_get_nvlist(nvl,
name);
cookie = NULL;
} else if (type == NV_TYPE_NVLIST_ARRAY) {
nvl = nvlist_get_nvlist_array(nvl, name,
NULL)[0];
cookie = NULL;
}
}
cookie = NULL;
} while ((tmp = nvlist_get_array_next(nvl)) != NULL);
} while ((tmp = nvlist_get_parent(nvl, &cookie)) != NULL);
two tmps, two nested loops, one absolutely unhinged but somehow gorgeous traversal strategy ๐ฅ the inner doโฆwhile rides nvlist_get_array_next() through the array, and the outer one climbs home with nvlist_get_parent(). obsessed.
โญ SEE ALSO โญ
close(2), dup(2), open(2), err(3), free(3), printf(3), unix(4)
๐ HISTORY ๐
the libnv library appeared in FreeBSD 11.0. she's a real one ๐
๐ AUTHORS ๐
the libnv library was implemented by Pawel Jakub Dawidek <pawel@dawidek.net> under sponsorship from the FreeBSD Foundation. iconic behaviour, we love to see it ๐ซถโจ
and that's a wrap on NV(9) bestie!! this page came from the Debian packaging, dated June 19, 2018 โ so go forth, build your nvlists, and remember: move โ add, cookies are your friends, and always free what you take ๐
๐ bye!!
๐ฆ where this page comes from
- debian bookworm: freebsd-manpages 12.2-1 ยท BSD-2-Clause-FreeBSD AND BSD-4-Clause
- debian trixie: freebsd-manpages 12.2-2 ยท BSD-2-Clause-FreeBSD AND BSD-4-Clause
- ubuntu jammy: freebsd-manpages 12.2-1 ยท BSD-2-Clause-FreeBSD AND BSD-4-Clause
- ubuntu noble: freebsd-manpages 12.2-1 ยท BSD-2-Clause-FreeBSD AND BSD-4-Clause
- ubuntu questing: freebsd-manpages 12.2-2
- ubuntu resolute: freebsd-manpages 12.2-2build1 ยท BSD-2-Clause-FreeBSD AND BSD-4-Clause
- ubuntu stonking: freebsd-manpages 12.2-2build1 ยท BSD-2-Clause-FreeBSD AND BSD-4-Clause
- Copyright (c) 2013 The FreeBSD Foundation
- Copyright (c) 2013-2015 Mariusz Zaborski <oshogbo@FreeBSD.org>
- ๐ girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms