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