1/*-
2 * Copyright (c) 2009-2010 The FreeBSD Foundation
3 * All rights reserved.
4 *
5 * This software was developed by Pawel Jakub Dawidek under sponsorship from
6 * the FreeBSD Foundation.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#include <sys/cdefs.h>
31__FBSDID("$FreeBSD$");
32
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
88struct nv {
89	int	nv_magic;
90	int	nv_error;
91	struct ebuf *nv_ebuf;
92};
93
94struct 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
114static void nv_add(struct nv *nv, const unsigned char *value, size_t vsize,
115    int type, const char *name);
116static void nv_addv(struct nv *nv, const unsigned char *value, size_t vsize,
117    int type, const char *namefmt, va_list nameap);
118static struct nvhdr *nv_find(struct nv *nv, int type, const char *namefmt,
119    va_list nameap);
120static 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 */
126struct nv *
127nv_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 */
147void
148nv_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 */
164int
165nv_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 */
179int
180nv_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 */
198int
199nv_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 */
342struct ebuf *
343nv_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 */
372struct nv *
373nv_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)					\
404void									\
405nv_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
415NV_DEFINE_ADD(int8, INT8)
416NV_DEFINE_ADD(uint8, UINT8)
417NV_DEFINE_ADD(int16, INT16)
418NV_DEFINE_ADD(uint16, UINT16)
419NV_DEFINE_ADD(int32, INT32)
420NV_DEFINE_ADD(uint32, UINT32)
421NV_DEFINE_ADD(int64, INT64)
422NV_DEFINE_ADD(uint64, UINT64)
423
424#undef	NV_DEFINE_ADD
425
426#define	NV_DEFINE_ADD_ARRAY(type, TYPE)					\
427void									\
428nv_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
440NV_DEFINE_ADD_ARRAY(int8, INT8)
441NV_DEFINE_ADD_ARRAY(uint8, UINT8)
442NV_DEFINE_ADD_ARRAY(int16, INT16)
443NV_DEFINE_ADD_ARRAY(uint16, UINT16)
444NV_DEFINE_ADD_ARRAY(int32, INT32)
445NV_DEFINE_ADD_ARRAY(uint32, UINT32)
446NV_DEFINE_ADD_ARRAY(int64, INT64)
447NV_DEFINE_ADD_ARRAY(uint64, UINT64)
448
449#undef	NV_DEFINE_ADD_ARRAY
450
451void
452nv_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
465void
466nv_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
475void
476nv_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)					\
494type##_t								\
495nv_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
513NV_DEFINE_GET(int8, INT8)
514NV_DEFINE_GET(uint8, UINT8)
515NV_DEFINE_GET(int16, INT16)
516NV_DEFINE_GET(uint16, UINT16)
517NV_DEFINE_GET(int32, INT32)
518NV_DEFINE_GET(uint32, UINT32)
519NV_DEFINE_GET(int64, INT64)
520NV_DEFINE_GET(uint64, UINT64)
521
522#undef	NV_DEFINE_GET
523
524#define	NV_DEFINE_GET_ARRAY(type, TYPE)					\
525const type##_t *							\
526nv_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
544NV_DEFINE_GET_ARRAY(int8, INT8)
545NV_DEFINE_GET_ARRAY(uint8, UINT8)
546NV_DEFINE_GET_ARRAY(int16, INT16)
547NV_DEFINE_GET_ARRAY(uint16, UINT16)
548NV_DEFINE_GET_ARRAY(int32, INT32)
549NV_DEFINE_GET_ARRAY(uint32, UINT32)
550NV_DEFINE_GET_ARRAY(int64, INT64)
551NV_DEFINE_GET_ARRAY(uint64, UINT64)
552
553#undef	NV_DEFINE_GET_ARRAY
554
555const char *
556nv_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
575static bool
576nv_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
595bool
596nv_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
608void
609nv_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 */
621void
622nv_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
758static void
759nv_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
815static void
816nv_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
828static struct nvhdr *
829nv_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
872static void
873nv_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