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