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
87struct nv {
88	int	nv_magic;
89	int	nv_error;
90	struct ebuf *nv_ebuf;
91};
92
93struct 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
113static void nv_add(struct nv *nv, const unsigned char *value, size_t vsize,
114    int type, const char *name);
115static void nv_addv(struct nv *nv, const unsigned char *value, size_t vsize,
116    int type, const char *namefmt, va_list nameap);
117static struct nvhdr *nv_find(struct nv *nv, int type, const char *namefmt,
118    va_list nameap);
119static 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 */
125struct nv *
126nv_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 */
146void
147nv_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 */
163int
164nv_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 */
178int
179nv_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 */
197int
198nv_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 */
341struct ebuf *
342nv_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 */
371struct nv *
372nv_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)					\
403void									\
404nv_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
414NV_DEFINE_ADD(int8, INT8)
415NV_DEFINE_ADD(uint8, UINT8)
416NV_DEFINE_ADD(int16, INT16)
417NV_DEFINE_ADD(uint16, UINT16)
418NV_DEFINE_ADD(int32, INT32)
419NV_DEFINE_ADD(uint32, UINT32)
420NV_DEFINE_ADD(int64, INT64)
421NV_DEFINE_ADD(uint64, UINT64)
422
423#undef	NV_DEFINE_ADD
424
425#define	NV_DEFINE_ADD_ARRAY(type, TYPE)					\
426void									\
427nv_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
439NV_DEFINE_ADD_ARRAY(int8, INT8)
440NV_DEFINE_ADD_ARRAY(uint8, UINT8)
441NV_DEFINE_ADD_ARRAY(int16, INT16)
442NV_DEFINE_ADD_ARRAY(uint16, UINT16)
443NV_DEFINE_ADD_ARRAY(int32, INT32)
444NV_DEFINE_ADD_ARRAY(uint32, UINT32)
445NV_DEFINE_ADD_ARRAY(int64, INT64)
446NV_DEFINE_ADD_ARRAY(uint64, UINT64)
447
448#undef	NV_DEFINE_ADD_ARRAY
449
450void
451nv_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
464void
465nv_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
474void
475nv_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)					\
493type##_t								\
494nv_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
512NV_DEFINE_GET(int8, INT8)
513NV_DEFINE_GET(uint8, UINT8)
514NV_DEFINE_GET(int16, INT16)
515NV_DEFINE_GET(uint16, UINT16)
516NV_DEFINE_GET(int32, INT32)
517NV_DEFINE_GET(uint32, UINT32)
518NV_DEFINE_GET(int64, INT64)
519NV_DEFINE_GET(uint64, UINT64)
520
521#undef	NV_DEFINE_GET
522
523#define	NV_DEFINE_GET_ARRAY(type, TYPE)					\
524const type##_t *							\
525nv_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
543NV_DEFINE_GET_ARRAY(int8, INT8)
544NV_DEFINE_GET_ARRAY(uint8, UINT8)
545NV_DEFINE_GET_ARRAY(int16, INT16)
546NV_DEFINE_GET_ARRAY(uint16, UINT16)
547NV_DEFINE_GET_ARRAY(int32, INT32)
548NV_DEFINE_GET_ARRAY(uint32, UINT32)
549NV_DEFINE_GET_ARRAY(int64, INT64)
550NV_DEFINE_GET_ARRAY(uint64, UINT64)
551
552#undef	NV_DEFINE_GET_ARRAY
553
554const char *
555nv_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
574static bool
575nv_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
594bool
595nv_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
607void
608nv_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 */
620void
621nv_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
757static void
758nv_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
814static void
815nv_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
827static struct nvhdr *
828nv_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
871static void
872nv_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