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