xref: /freebsd/sbin/hastd/nv.c (revision 9bc300465e48e19d794d88d0c158a2adb92c7197)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2009-2010 The FreeBSD Foundation
5  *
6  * This software was developed by Pawel Jakub Dawidek under sponsorship from
7  * the FreeBSD Foundation.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  */
30 
31 #include <sys/param.h>
32 #include <sys/endian.h>
33 
34 #include <bitstring.h>
35 #include <errno.h>
36 #include <stdarg.h>
37 #include <stdbool.h>
38 #include <stdint.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <unistd.h>
42 
43 #include <ebuf.h>
44 #include <pjdlog.h>
45 
46 #include "nv.h"
47 
48 #ifndef	PJDLOG_ASSERT
49 #include <assert.h>
50 #define	PJDLOG_ASSERT(...)	assert(__VA_ARGS__)
51 #endif
52 #ifndef	PJDLOG_ABORT
53 #define	PJDLOG_ABORT(...)	abort()
54 #endif
55 
56 #define	NV_TYPE_NONE		0
57 
58 #define	NV_TYPE_INT8		1
59 #define	NV_TYPE_UINT8		2
60 #define	NV_TYPE_INT16		3
61 #define	NV_TYPE_UINT16		4
62 #define	NV_TYPE_INT32		5
63 #define	NV_TYPE_UINT32		6
64 #define	NV_TYPE_INT64		7
65 #define	NV_TYPE_UINT64		8
66 #define	NV_TYPE_INT8_ARRAY	9
67 #define	NV_TYPE_UINT8_ARRAY	10
68 #define	NV_TYPE_INT16_ARRAY	11
69 #define	NV_TYPE_UINT16_ARRAY	12
70 #define	NV_TYPE_INT32_ARRAY	13
71 #define	NV_TYPE_UINT32_ARRAY	14
72 #define	NV_TYPE_INT64_ARRAY	15
73 #define	NV_TYPE_UINT64_ARRAY	16
74 #define	NV_TYPE_STRING		17
75 
76 #define	NV_TYPE_MASK		0x7f
77 #define	NV_TYPE_FIRST		NV_TYPE_INT8
78 #define	NV_TYPE_LAST		NV_TYPE_STRING
79 
80 #define	NV_ORDER_NETWORK	0x00
81 #define	NV_ORDER_HOST		0x80
82 
83 #define	NV_ORDER_MASK		0x80
84 
85 #define	NV_MAGIC	0xaea1e
86 struct nv {
87 	int	nv_magic;
88 	int	nv_error;
89 	struct ebuf *nv_ebuf;
90 };
91 
92 struct nvhdr {
93 	uint8_t		nvh_type;
94 	uint8_t		nvh_namesize;
95 	uint32_t	nvh_dsize;
96 	char		nvh_name[0];
97 } __packed;
98 #define	NVH_DATA(nvh)	((unsigned char *)nvh + NVH_HSIZE(nvh))
99 #define	NVH_HSIZE(nvh)	\
100 	(sizeof(struct nvhdr) + roundup2((nvh)->nvh_namesize, 8))
101 #define	NVH_DSIZE(nvh)	\
102 	(((nvh)->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST ?		\
103 	(nvh)->nvh_dsize :						\
104 	le32toh((nvh)->nvh_dsize))
105 #define	NVH_SIZE(nvh)	(NVH_HSIZE(nvh) + roundup2(NVH_DSIZE(nvh), 8))
106 
107 #define	NV_CHECK(nv)	do {						\
108 	PJDLOG_ASSERT((nv) != NULL);					\
109 	PJDLOG_ASSERT((nv)->nv_magic == NV_MAGIC);			\
110 } while (0)
111 
112 static void nv_add(struct nv *nv, const unsigned char *value, size_t vsize,
113     int type, const char *name);
114 static void nv_addv(struct nv *nv, const unsigned char *value, size_t vsize,
115     int type, const char *namefmt, va_list nameap);
116 static struct nvhdr *nv_find(struct nv *nv, int type, const char *namefmt,
117     va_list nameap);
118 static void nv_swap(struct nvhdr *nvh, bool tohost);
119 
120 /*
121  * Allocate and initialize new nv structure.
122  * Return NULL in case of malloc(3) failure.
123  */
124 struct nv *
125 nv_alloc(void)
126 {
127 	struct nv *nv;
128 
129 	nv = malloc(sizeof(*nv));
130 	if (nv == NULL)
131 		return (NULL);
132 	nv->nv_ebuf = ebuf_alloc(0);
133 	if (nv->nv_ebuf == NULL) {
134 		free(nv);
135 		return (NULL);
136 	}
137 	nv->nv_error = 0;
138 	nv->nv_magic = NV_MAGIC;
139 	return (nv);
140 }
141 
142 /*
143  * Free the given nv structure.
144  */
145 void
146 nv_free(struct nv *nv)
147 {
148 
149 	if (nv == NULL)
150 		return;
151 
152 	NV_CHECK(nv);
153 
154 	nv->nv_magic = 0;
155 	ebuf_free(nv->nv_ebuf);
156 	free(nv);
157 }
158 
159 /*
160  * Return error for the given nv structure.
161  */
162 int
163 nv_error(const struct nv *nv)
164 {
165 
166 	if (nv == NULL)
167 		return (ENOMEM);
168 
169 	NV_CHECK(nv);
170 
171 	return (nv->nv_error);
172 }
173 
174 /*
175  * Set error for the given nv structure and return previous error.
176  */
177 int
178 nv_set_error(struct nv *nv, int error)
179 {
180 	int preverr;
181 
182 	if (nv == NULL)
183 		return (ENOMEM);
184 
185 	NV_CHECK(nv);
186 
187 	preverr = nv->nv_error;
188 	nv->nv_error = error;
189 	return (preverr);
190 }
191 
192 /*
193  * Validate correctness of the entire nv structure and all its elements.
194  * If extrap is not NULL, store number of extra bytes at the end of the buffer.
195  */
196 int
197 nv_validate(struct nv *nv, size_t *extrap)
198 {
199 	struct nvhdr *nvh;
200 	unsigned char *data, *ptr;
201 	size_t dsize, size, vsize;
202 	int error;
203 
204 	if (nv == NULL) {
205 		errno = ENOMEM;
206 		return (-1);
207 	}
208 
209 	NV_CHECK(nv);
210 	PJDLOG_ASSERT(nv->nv_error == 0);
211 
212 	/* TODO: Check that names are unique? */
213 
214 	error = 0;
215 	ptr = ebuf_data(nv->nv_ebuf, &size);
216 	while (size > 0) {
217 		/*
218 		 * Zeros at the end of the buffer are acceptable.
219 		 */
220 		if (ptr[0] == '\0')
221 			break;
222 		/*
223 		 * Minimum size at this point is size of nvhdr structure, one
224 		 * character long name plus terminating '\0'.
225 		 */
226 		if (size < sizeof(*nvh) + 2) {
227 			error = EINVAL;
228 			break;
229 		}
230 		nvh = (struct nvhdr *)ptr;
231 		if (size < NVH_HSIZE(nvh)) {
232 			error = EINVAL;
233 			break;
234 		}
235 		if (nvh->nvh_name[nvh->nvh_namesize - 1] != '\0') {
236 			error = EINVAL;
237 			break;
238 		}
239 		if (strlen(nvh->nvh_name) !=
240 		    (size_t)(nvh->nvh_namesize - 1)) {
241 			error = EINVAL;
242 			break;
243 		}
244 		if ((nvh->nvh_type & NV_TYPE_MASK) < NV_TYPE_FIRST ||
245 		    (nvh->nvh_type & NV_TYPE_MASK) > NV_TYPE_LAST) {
246 			error = EINVAL;
247 			break;
248 		}
249 		dsize = NVH_DSIZE(nvh);
250 		if (dsize == 0) {
251 			error = EINVAL;
252 			break;
253 		}
254 		if (size < NVH_SIZE(nvh)) {
255 			error = EINVAL;
256 			break;
257 		}
258 		vsize = 0;
259 		switch (nvh->nvh_type & NV_TYPE_MASK) {
260 		case NV_TYPE_INT8:
261 		case NV_TYPE_UINT8:
262 			if (vsize == 0)
263 				vsize = 1;
264 			/* FALLTHROUGH */
265 		case NV_TYPE_INT16:
266 		case NV_TYPE_UINT16:
267 			if (vsize == 0)
268 				vsize = 2;
269 			/* FALLTHROUGH */
270 		case NV_TYPE_INT32:
271 		case NV_TYPE_UINT32:
272 			if (vsize == 0)
273 				vsize = 4;
274 			/* FALLTHROUGH */
275 		case NV_TYPE_INT64:
276 		case NV_TYPE_UINT64:
277 			if (vsize == 0)
278 				vsize = 8;
279 			if (dsize != vsize) {
280 				error = EINVAL;
281 				break;
282 			}
283 			break;
284 		case NV_TYPE_INT8_ARRAY:
285 		case NV_TYPE_UINT8_ARRAY:
286 			break;
287 		case NV_TYPE_INT16_ARRAY:
288 		case NV_TYPE_UINT16_ARRAY:
289 			if (vsize == 0)
290 				vsize = 2;
291 			/* FALLTHROUGH */
292 		case NV_TYPE_INT32_ARRAY:
293 		case NV_TYPE_UINT32_ARRAY:
294 			if (vsize == 0)
295 				vsize = 4;
296 			/* FALLTHROUGH */
297 		case NV_TYPE_INT64_ARRAY:
298 		case NV_TYPE_UINT64_ARRAY:
299 			if (vsize == 0)
300 				vsize = 8;
301 			if ((dsize % vsize) != 0) {
302 				error = EINVAL;
303 				break;
304 			}
305 			break;
306 		case NV_TYPE_STRING:
307 			data = NVH_DATA(nvh);
308 			if (data[dsize - 1] != '\0') {
309 				error = EINVAL;
310 				break;
311 			}
312 			if (strlen((char *)data) != dsize - 1) {
313 				error = EINVAL;
314 				break;
315 			}
316 			break;
317 		default:
318 			PJDLOG_ABORT("invalid condition");
319 		}
320 		if (error != 0)
321 			break;
322 		ptr += NVH_SIZE(nvh);
323 		size -= NVH_SIZE(nvh);
324 	}
325 	if (error != 0) {
326 		errno = error;
327 		if (nv->nv_error == 0)
328 			nv->nv_error = error;
329 		return (-1);
330 	}
331 	if (extrap != NULL)
332 		*extrap = size;
333 	return (0);
334 }
335 
336 /*
337  * Convert the given nv structure to network byte order and return ebuf
338  * structure.
339  */
340 struct ebuf *
341 nv_hton(struct nv *nv)
342 {
343 	struct nvhdr *nvh;
344 	unsigned char *ptr;
345 	size_t size;
346 
347 	NV_CHECK(nv);
348 	PJDLOG_ASSERT(nv->nv_error == 0);
349 
350 	ptr = ebuf_data(nv->nv_ebuf, &size);
351 	while (size > 0) {
352 		/*
353 		 * Minimum size at this point is size of nvhdr structure,
354 		 * one character long name plus terminating '\0'.
355 		 */
356 		PJDLOG_ASSERT(size >= sizeof(*nvh) + 2);
357 		nvh = (struct nvhdr *)ptr;
358 		PJDLOG_ASSERT(NVH_SIZE(nvh) <= size);
359 		nv_swap(nvh, false);
360 		ptr += NVH_SIZE(nvh);
361 		size -= NVH_SIZE(nvh);
362 	}
363 
364 	return (nv->nv_ebuf);
365 }
366 
367 /*
368  * Create nv structure based on ebuf received from the network.
369  */
370 struct nv *
371 nv_ntoh(struct ebuf *eb)
372 {
373 	struct nv *nv;
374 	size_t extra;
375 	int rerrno;
376 
377 	PJDLOG_ASSERT(eb != NULL);
378 
379 	nv = malloc(sizeof(*nv));
380 	if (nv == NULL)
381 		return (NULL);
382 	nv->nv_error = 0;
383 	nv->nv_ebuf = eb;
384 	nv->nv_magic = NV_MAGIC;
385 
386 	if (nv_validate(nv, &extra) == -1) {
387 		rerrno = errno;
388 		nv->nv_magic = 0;
389 		free(nv);
390 		errno = rerrno;
391 		return (NULL);
392 	}
393 	/*
394 	 * Remove extra zeros at the end of the buffer.
395 	 */
396 	ebuf_del_tail(eb, extra);
397 
398 	return (nv);
399 }
400 
401 #define	NV_DEFINE_ADD(type, TYPE)					\
402 void									\
403 nv_add_##type(struct nv *nv, type##_t value, const char *namefmt, ...)	\
404 {									\
405 	va_list nameap;							\
406 									\
407 	va_start(nameap, namefmt);					\
408 	nv_addv(nv, (unsigned char *)&value, sizeof(value),		\
409 	    NV_TYPE_##TYPE, namefmt, nameap);				\
410 	va_end(nameap);							\
411 }
412 
413 NV_DEFINE_ADD(int8, INT8)
414 NV_DEFINE_ADD(uint8, UINT8)
415 NV_DEFINE_ADD(int16, INT16)
416 NV_DEFINE_ADD(uint16, UINT16)
417 NV_DEFINE_ADD(int32, INT32)
418 NV_DEFINE_ADD(uint32, UINT32)
419 NV_DEFINE_ADD(int64, INT64)
420 NV_DEFINE_ADD(uint64, UINT64)
421 
422 #undef	NV_DEFINE_ADD
423 
424 #define	NV_DEFINE_ADD_ARRAY(type, TYPE)					\
425 void									\
426 nv_add_##type##_array(struct nv *nv, const type##_t *value,		\
427     size_t nsize, const char *namefmt, ...)				\
428 {									\
429 	va_list nameap;							\
430 									\
431 	va_start(nameap, namefmt);					\
432 	nv_addv(nv, (const unsigned char *)value,			\
433 	    sizeof(value[0]) * nsize, NV_TYPE_##TYPE##_ARRAY, namefmt,	\
434 	    nameap);							\
435 	va_end(nameap);							\
436 }
437 
438 NV_DEFINE_ADD_ARRAY(int8, INT8)
439 NV_DEFINE_ADD_ARRAY(uint8, UINT8)
440 NV_DEFINE_ADD_ARRAY(int16, INT16)
441 NV_DEFINE_ADD_ARRAY(uint16, UINT16)
442 NV_DEFINE_ADD_ARRAY(int32, INT32)
443 NV_DEFINE_ADD_ARRAY(uint32, UINT32)
444 NV_DEFINE_ADD_ARRAY(int64, INT64)
445 NV_DEFINE_ADD_ARRAY(uint64, UINT64)
446 
447 #undef	NV_DEFINE_ADD_ARRAY
448 
449 void
450 nv_add_string(struct nv *nv, const char *value, const char *namefmt, ...)
451 {
452 	va_list nameap;
453 	size_t size;
454 
455 	size = strlen(value) + 1;
456 
457 	va_start(nameap, namefmt);
458 	nv_addv(nv, (const unsigned char *)value, size, NV_TYPE_STRING,
459 	    namefmt, nameap);
460 	va_end(nameap);
461 }
462 
463 void
464 nv_add_stringf(struct nv *nv, const char *name, const char *valuefmt, ...)
465 {
466 	va_list valueap;
467 
468 	va_start(valueap, valuefmt);
469 	nv_add_stringv(nv, name, valuefmt, valueap);
470 	va_end(valueap);
471 }
472 
473 void
474 nv_add_stringv(struct nv *nv, const char *name, const char *valuefmt,
475     va_list valueap)
476 {
477 	char *value;
478 	ssize_t size;
479 
480 	size = vasprintf(&value, valuefmt, valueap);
481 	if (size == -1) {
482 		if (nv->nv_error == 0)
483 			nv->nv_error = ENOMEM;
484 		return;
485 	}
486 	size++;
487 	nv_add(nv, (const unsigned char *)value, size, NV_TYPE_STRING, name);
488 	free(value);
489 }
490 
491 #define	NV_DEFINE_GET(type, TYPE)					\
492 type##_t								\
493 nv_get_##type(struct nv *nv, const char *namefmt, ...)			\
494 {									\
495 	struct nvhdr *nvh;						\
496 	va_list nameap;							\
497 	type##_t value;							\
498 									\
499 	va_start(nameap, namefmt);					\
500 	nvh = nv_find(nv, NV_TYPE_##TYPE, namefmt, nameap);		\
501 	va_end(nameap);							\
502 	if (nvh == NULL)						\
503 		return (0);						\
504 	PJDLOG_ASSERT((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST);\
505 	PJDLOG_ASSERT(sizeof(value) == nvh->nvh_dsize);			\
506 	bcopy(NVH_DATA(nvh), &value, sizeof(value));			\
507 									\
508 	return (value);							\
509 }
510 
511 NV_DEFINE_GET(int8, INT8)
512 NV_DEFINE_GET(uint8, UINT8)
513 NV_DEFINE_GET(int16, INT16)
514 NV_DEFINE_GET(uint16, UINT16)
515 NV_DEFINE_GET(int32, INT32)
516 NV_DEFINE_GET(uint32, UINT32)
517 NV_DEFINE_GET(int64, INT64)
518 NV_DEFINE_GET(uint64, UINT64)
519 
520 #undef	NV_DEFINE_GET
521 
522 #define	NV_DEFINE_GET_ARRAY(type, TYPE)					\
523 const type##_t *							\
524 nv_get_##type##_array(struct nv *nv, size_t *sizep,			\
525     const char *namefmt, ...)						\
526 {									\
527 	struct nvhdr *nvh;						\
528 	va_list nameap;							\
529 									\
530 	va_start(nameap, namefmt);					\
531 	nvh = nv_find(nv, NV_TYPE_##TYPE##_ARRAY, namefmt, nameap);	\
532 	va_end(nameap);							\
533 	if (nvh == NULL)						\
534 		return (NULL);						\
535 	PJDLOG_ASSERT((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST);\
536 	PJDLOG_ASSERT((nvh->nvh_dsize % sizeof(type##_t)) == 0);	\
537 	if (sizep != NULL)						\
538 		*sizep = nvh->nvh_dsize / sizeof(type##_t);		\
539 	return ((type##_t *)(void *)NVH_DATA(nvh));			\
540 }
541 
542 NV_DEFINE_GET_ARRAY(int8, INT8)
543 NV_DEFINE_GET_ARRAY(uint8, UINT8)
544 NV_DEFINE_GET_ARRAY(int16, INT16)
545 NV_DEFINE_GET_ARRAY(uint16, UINT16)
546 NV_DEFINE_GET_ARRAY(int32, INT32)
547 NV_DEFINE_GET_ARRAY(uint32, UINT32)
548 NV_DEFINE_GET_ARRAY(int64, INT64)
549 NV_DEFINE_GET_ARRAY(uint64, UINT64)
550 
551 #undef	NV_DEFINE_GET_ARRAY
552 
553 const char *
554 nv_get_string(struct nv *nv, const char *namefmt, ...)
555 {
556 	struct nvhdr *nvh;
557 	va_list nameap;
558 	char *str;
559 
560 	va_start(nameap, namefmt);
561 	nvh = nv_find(nv, NV_TYPE_STRING, namefmt, nameap);
562 	va_end(nameap);
563 	if (nvh == NULL)
564 		return (NULL);
565 	PJDLOG_ASSERT((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST);
566 	PJDLOG_ASSERT(nvh->nvh_dsize >= 1);
567 	str = (char *)NVH_DATA(nvh);
568 	PJDLOG_ASSERT(str[nvh->nvh_dsize - 1] == '\0');
569 	PJDLOG_ASSERT(strlen(str) == nvh->nvh_dsize - 1);
570 	return (str);
571 }
572 
573 static bool
574 nv_vexists(struct nv *nv, const char *namefmt, va_list nameap)
575 {
576 	struct nvhdr *nvh;
577 	int snverror, serrno;
578 
579 	if (nv == NULL)
580 		return (false);
581 
582 	serrno = errno;
583 	snverror = nv->nv_error;
584 
585 	nvh = nv_find(nv, NV_TYPE_NONE, namefmt, nameap);
586 
587 	errno = serrno;
588 	nv->nv_error = snverror;
589 
590 	return (nvh != NULL);
591 }
592 
593 bool
594 nv_exists(struct nv *nv, const char *namefmt, ...)
595 {
596 	va_list nameap;
597 	bool ret;
598 
599 	va_start(nameap, namefmt);
600 	ret = nv_vexists(nv, namefmt, nameap);
601 	va_end(nameap);
602 
603 	return (ret);
604 }
605 
606 void
607 nv_assert(struct nv *nv, const char *namefmt, ...)
608 {
609 	va_list nameap;
610 
611 	va_start(nameap, namefmt);
612 	PJDLOG_ASSERT(nv_vexists(nv, namefmt, nameap));
613 	va_end(nameap);
614 }
615 
616 /*
617  * Dump content of the nv structure.
618  */
619 void
620 nv_dump(struct nv *nv)
621 {
622 	struct nvhdr *nvh;
623 	unsigned char *data, *ptr;
624 	size_t dsize, size;
625 	unsigned int ii;
626 	bool swap;
627 
628 	if (nv_validate(nv, NULL) == -1) {
629 		printf("error: %d\n", errno);
630 		return;
631 	}
632 
633 	NV_CHECK(nv);
634 	PJDLOG_ASSERT(nv->nv_error == 0);
635 
636 	ptr = ebuf_data(nv->nv_ebuf, &size);
637 	while (size > 0) {
638 		PJDLOG_ASSERT(size >= sizeof(*nvh) + 2);
639 		nvh = (struct nvhdr *)ptr;
640 		PJDLOG_ASSERT(size >= NVH_SIZE(nvh));
641 		swap = ((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_NETWORK);
642 		dsize = NVH_DSIZE(nvh);
643 		data = NVH_DATA(nvh);
644 		printf("  %s", nvh->nvh_name);
645 		switch (nvh->nvh_type & NV_TYPE_MASK) {
646 		case NV_TYPE_INT8:
647 			printf("(int8): %jd", (intmax_t)(*(int8_t *)data));
648 			break;
649 		case NV_TYPE_UINT8:
650 			printf("(uint8): %ju", (uintmax_t)(*(uint8_t *)data));
651 			break;
652 		case NV_TYPE_INT16:
653 			printf("(int16): %jd", swap ?
654 			    (intmax_t)le16toh(*(int16_t *)(void *)data) :
655 			    (intmax_t)*(int16_t *)(void *)data);
656 			break;
657 		case NV_TYPE_UINT16:
658 			printf("(uint16): %ju", swap ?
659 			    (uintmax_t)le16toh(*(uint16_t *)(void *)data) :
660 			    (uintmax_t)*(uint16_t *)(void *)data);
661 			break;
662 		case NV_TYPE_INT32:
663 			printf("(int32): %jd", swap ?
664 			    (intmax_t)le32toh(*(int32_t *)(void *)data) :
665 			    (intmax_t)*(int32_t *)(void *)data);
666 			break;
667 		case NV_TYPE_UINT32:
668 			printf("(uint32): %ju", swap ?
669 			    (uintmax_t)le32toh(*(uint32_t *)(void *)data) :
670 			    (uintmax_t)*(uint32_t *)(void *)data);
671 			break;
672 		case NV_TYPE_INT64:
673 			printf("(int64): %jd", swap ?
674 			    (intmax_t)le64toh(*(int64_t *)(void *)data) :
675 			    (intmax_t)*(int64_t *)(void *)data);
676 			break;
677 		case NV_TYPE_UINT64:
678 			printf("(uint64): %ju", swap ?
679 			    (uintmax_t)le64toh(*(uint64_t *)(void *)data) :
680 			    (uintmax_t)*(uint64_t *)(void *)data);
681 			break;
682 		case NV_TYPE_INT8_ARRAY:
683 			printf("(int8 array):");
684 			for (ii = 0; ii < dsize; ii++)
685 				printf(" %jd", (intmax_t)((int8_t *)data)[ii]);
686 			break;
687 		case NV_TYPE_UINT8_ARRAY:
688 			printf("(uint8 array):");
689 			for (ii = 0; ii < dsize; ii++)
690 				printf(" %ju", (uintmax_t)((uint8_t *)data)[ii]);
691 			break;
692 		case NV_TYPE_INT16_ARRAY:
693 			printf("(int16 array):");
694 			for (ii = 0; ii < dsize / 2; ii++) {
695 				printf(" %jd", swap ?
696 				    (intmax_t)le16toh(((int16_t *)(void *)data)[ii]) :
697 				    (intmax_t)((int16_t *)(void *)data)[ii]);
698 			}
699 			break;
700 		case NV_TYPE_UINT16_ARRAY:
701 			printf("(uint16 array):");
702 			for (ii = 0; ii < dsize / 2; ii++) {
703 				printf(" %ju", swap ?
704 				    (uintmax_t)le16toh(((uint16_t *)(void *)data)[ii]) :
705 				    (uintmax_t)((uint16_t *)(void *)data)[ii]);
706 			}
707 			break;
708 		case NV_TYPE_INT32_ARRAY:
709 			printf("(int32 array):");
710 			for (ii = 0; ii < dsize / 4; ii++) {
711 				printf(" %jd", swap ?
712 				    (intmax_t)le32toh(((int32_t *)(void *)data)[ii]) :
713 				    (intmax_t)((int32_t *)(void *)data)[ii]);
714 			}
715 			break;
716 		case NV_TYPE_UINT32_ARRAY:
717 			printf("(uint32 array):");
718 			for (ii = 0; ii < dsize / 4; ii++) {
719 				printf(" %ju", swap ?
720 				    (uintmax_t)le32toh(((uint32_t *)(void *)data)[ii]) :
721 				    (uintmax_t)((uint32_t *)(void *)data)[ii]);
722 			}
723 			break;
724 		case NV_TYPE_INT64_ARRAY:
725 			printf("(int64 array):");
726 			for (ii = 0; ii < dsize / 8; ii++) {
727 				printf(" %ju", swap ?
728 				    (uintmax_t)le64toh(((uint64_t *)(void *)data)[ii]) :
729 				    (uintmax_t)((uint64_t *)(void *)data)[ii]);
730 			}
731 			break;
732 		case NV_TYPE_UINT64_ARRAY:
733 			printf("(uint64 array):");
734 			for (ii = 0; ii < dsize / 8; ii++) {
735 				printf(" %ju", swap ?
736 				    (uintmax_t)le64toh(((uint64_t *)(void *)data)[ii]) :
737 				    (uintmax_t)((uint64_t *)(void *)data)[ii]);
738 			}
739 			break;
740 		case NV_TYPE_STRING:
741 			printf("(string): %s", (char *)data);
742 			break;
743 		default:
744 			PJDLOG_ABORT("invalid condition");
745 		}
746 		printf("\n");
747 		ptr += NVH_SIZE(nvh);
748 		size -= NVH_SIZE(nvh);
749 	}
750 }
751 
752 /*
753  * Local routines below.
754  */
755 
756 static void
757 nv_add(struct nv *nv, const unsigned char *value, size_t vsize, int type,
758     const char *name)
759 {
760 	static unsigned char align[7];
761 	struct nvhdr *nvh;
762 	size_t namesize;
763 
764 	if (nv == NULL) {
765 		errno = ENOMEM;
766 		return;
767 	}
768 
769 	NV_CHECK(nv);
770 
771 	namesize = strlen(name) + 1;
772 
773 	nvh = malloc(sizeof(*nvh) + roundup2(namesize, 8));
774 	if (nvh == NULL) {
775 		if (nv->nv_error == 0)
776 			nv->nv_error = ENOMEM;
777 		return;
778 	}
779 	nvh->nvh_type = NV_ORDER_HOST | type;
780 	nvh->nvh_namesize = (uint8_t)namesize;
781 	nvh->nvh_dsize = (uint32_t)vsize;
782 	bcopy(name, nvh->nvh_name, namesize);
783 
784 	/* Add header first. */
785 	if (ebuf_add_tail(nv->nv_ebuf, nvh, NVH_HSIZE(nvh)) == -1) {
786 		PJDLOG_ASSERT(errno != 0);
787 		if (nv->nv_error == 0)
788 			nv->nv_error = errno;
789 		free(nvh);
790 		return;
791 	}
792 	free(nvh);
793 	/* Add the actual data. */
794 	if (ebuf_add_tail(nv->nv_ebuf, value, vsize) == -1) {
795 		PJDLOG_ASSERT(errno != 0);
796 		if (nv->nv_error == 0)
797 			nv->nv_error = errno;
798 		return;
799 	}
800 	/* Align the data (if needed). */
801 	vsize = roundup2(vsize, 8) - vsize;
802 	if (vsize == 0)
803 		return;
804 	PJDLOG_ASSERT(vsize > 0 && vsize <= sizeof(align));
805 	if (ebuf_add_tail(nv->nv_ebuf, align, vsize) == -1) {
806 		PJDLOG_ASSERT(errno != 0);
807 		if (nv->nv_error == 0)
808 			nv->nv_error = errno;
809 		return;
810 	}
811 }
812 
813 static void
814 nv_addv(struct nv *nv, const unsigned char *value, size_t vsize, int type,
815     const char *namefmt, va_list nameap)
816 {
817 	char name[255];
818 	size_t namesize;
819 
820 	namesize = vsnprintf(name, sizeof(name), namefmt, nameap);
821 	PJDLOG_ASSERT(namesize > 0 && namesize < sizeof(name));
822 
823 	nv_add(nv, value, vsize, type, name);
824 }
825 
826 static struct nvhdr *
827 nv_find(struct nv *nv, int type, const char *namefmt, va_list nameap)
828 {
829 	char name[255];
830 	struct nvhdr *nvh;
831 	unsigned char *ptr;
832 	size_t size, namesize;
833 
834 	if (nv == NULL) {
835 		errno = ENOMEM;
836 		return (NULL);
837 	}
838 
839 	NV_CHECK(nv);
840 
841 	namesize = vsnprintf(name, sizeof(name), namefmt, nameap);
842 	PJDLOG_ASSERT(namesize > 0 && namesize < sizeof(name));
843 	namesize++;
844 
845 	ptr = ebuf_data(nv->nv_ebuf, &size);
846 	while (size > 0) {
847 		PJDLOG_ASSERT(size >= sizeof(*nvh) + 2);
848 		nvh = (struct nvhdr *)ptr;
849 		PJDLOG_ASSERT(size >= NVH_SIZE(nvh));
850 		nv_swap(nvh, true);
851 		if (strcmp(nvh->nvh_name, name) == 0) {
852 			if (type != NV_TYPE_NONE &&
853 			    (nvh->nvh_type & NV_TYPE_MASK) != type) {
854 				errno = EINVAL;
855 				if (nv->nv_error == 0)
856 					nv->nv_error = EINVAL;
857 				return (NULL);
858 			}
859 			return (nvh);
860 		}
861 		ptr += NVH_SIZE(nvh);
862 		size -= NVH_SIZE(nvh);
863 	}
864 	errno = ENOENT;
865 	if (nv->nv_error == 0)
866 		nv->nv_error = ENOENT;
867 	return (NULL);
868 }
869 
870 static void
871 nv_swap(struct nvhdr *nvh, bool tohost)
872 {
873 	unsigned char *data, *end, *p;
874 	size_t vsize;
875 
876 	data = NVH_DATA(nvh);
877 	if (tohost) {
878 		if ((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST)
879 			return;
880 		nvh->nvh_dsize = le32toh(nvh->nvh_dsize);
881 		end = data + nvh->nvh_dsize;
882 		nvh->nvh_type &= ~NV_ORDER_MASK;
883 		nvh->nvh_type |= NV_ORDER_HOST;
884 	} else {
885 		if ((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_NETWORK)
886 			return;
887 		end = data + nvh->nvh_dsize;
888 		nvh->nvh_dsize = htole32(nvh->nvh_dsize);
889 		nvh->nvh_type &= ~NV_ORDER_MASK;
890 		nvh->nvh_type |= NV_ORDER_NETWORK;
891 	}
892 
893 	vsize = 0;
894 
895 	switch (nvh->nvh_type & NV_TYPE_MASK) {
896 	case NV_TYPE_INT8:
897 	case NV_TYPE_UINT8:
898 	case NV_TYPE_INT8_ARRAY:
899 	case NV_TYPE_UINT8_ARRAY:
900 		break;
901 	case NV_TYPE_INT16:
902 	case NV_TYPE_UINT16:
903 	case NV_TYPE_INT16_ARRAY:
904 	case NV_TYPE_UINT16_ARRAY:
905 		if (vsize == 0)
906 			vsize = 2;
907 		/* FALLTHROUGH */
908 	case NV_TYPE_INT32:
909 	case NV_TYPE_UINT32:
910 	case NV_TYPE_INT32_ARRAY:
911 	case NV_TYPE_UINT32_ARRAY:
912 		if (vsize == 0)
913 			vsize = 4;
914 		/* FALLTHROUGH */
915 	case NV_TYPE_INT64:
916 	case NV_TYPE_UINT64:
917 	case NV_TYPE_INT64_ARRAY:
918 	case NV_TYPE_UINT64_ARRAY:
919 		if (vsize == 0)
920 			vsize = 8;
921 		for (p = data; p < end; p += vsize) {
922 			if (tohost) {
923 				switch (vsize) {
924 				case 2:
925 					*(uint16_t *)(void *)p =
926 					    le16toh(*(uint16_t *)(void *)p);
927 					break;
928 				case 4:
929 					*(uint32_t *)(void *)p =
930 					    le32toh(*(uint32_t *)(void *)p);
931 					break;
932 				case 8:
933 					*(uint64_t *)(void *)p =
934 					    le64toh(*(uint64_t *)(void *)p);
935 					break;
936 				default:
937 					PJDLOG_ABORT("invalid condition");
938 				}
939 			} else {
940 				switch (vsize) {
941 				case 2:
942 					*(uint16_t *)(void *)p =
943 					    htole16(*(uint16_t *)(void *)p);
944 					break;
945 				case 4:
946 					*(uint32_t *)(void *)p =
947 					    htole32(*(uint32_t *)(void *)p);
948 					break;
949 				case 8:
950 					*(uint64_t *)(void *)p =
951 					    htole64(*(uint64_t *)(void *)p);
952 					break;
953 				default:
954 					PJDLOG_ABORT("invalid condition");
955 				}
956 			}
957 		}
958 		break;
959 	case NV_TYPE_STRING:
960 		break;
961 	default:
962 		PJDLOG_ABORT("unrecognized type");
963 	}
964 }
965