1204076Spjd/*-
2204076Spjd * Copyright (c) 2009-2010 The FreeBSD Foundation
3204076Spjd * All rights reserved.
4204076Spjd *
5204076Spjd * This software was developed by Pawel Jakub Dawidek under sponsorship from
6204076Spjd * the FreeBSD Foundation.
7204076Spjd *
8204076Spjd * Redistribution and use in source and binary forms, with or without
9204076Spjd * modification, are permitted provided that the following conditions
10204076Spjd * are met:
11204076Spjd * 1. Redistributions of source code must retain the above copyright
12204076Spjd *    notice, this list of conditions and the following disclaimer.
13204076Spjd * 2. Redistributions in binary form must reproduce the above copyright
14204076Spjd *    notice, this list of conditions and the following disclaimer in the
15204076Spjd *    documentation and/or other materials provided with the distribution.
16204076Spjd *
17204076Spjd * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
18204076Spjd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19204076Spjd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20204076Spjd * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
21204076Spjd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22204076Spjd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23204076Spjd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24204076Spjd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25204076Spjd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26204076Spjd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27204076Spjd * SUCH DAMAGE.
28204076Spjd */
29204076Spjd
30204076Spjd#include <sys/cdefs.h>
31204076Spjd__FBSDID("$FreeBSD$");
32204076Spjd
33204076Spjd#include <sys/param.h>
34204076Spjd#include <sys/endian.h>
35204076Spjd
36204076Spjd#include <bitstring.h>
37204076Spjd#include <errno.h>
38204076Spjd#include <stdarg.h>
39204076Spjd#include <stdbool.h>
40204076Spjd#include <stdint.h>
41204076Spjd#include <stdlib.h>
42204076Spjd#include <string.h>
43204076Spjd#include <unistd.h>
44204076Spjd
45204076Spjd#include <ebuf.h>
46229509Strociny#include <pjdlog.h>
47204076Spjd
48229509Strociny#include "nv.h"
49229509Strociny
50229509Strociny#ifndef	PJDLOG_ASSERT
51229509Strociny#include <assert.h>
52229509Strociny#define	PJDLOG_ASSERT(...)	assert(__VA_ARGS__)
53229509Strociny#endif
54229509Strociny#ifndef	PJDLOG_ABORT
55229509Strociny#define	PJDLOG_ABORT(...)	abort()
56229509Strociny#endif
57229509Strociny
58214283Spjd#define	NV_TYPE_NONE		0
59214283Spjd
60214282Spjd#define	NV_TYPE_INT8		1
61214282Spjd#define	NV_TYPE_UINT8		2
62214282Spjd#define	NV_TYPE_INT16		3
63214282Spjd#define	NV_TYPE_UINT16		4
64214282Spjd#define	NV_TYPE_INT32		5
65214282Spjd#define	NV_TYPE_UINT32		6
66214282Spjd#define	NV_TYPE_INT64		7
67214282Spjd#define	NV_TYPE_UINT64		8
68214282Spjd#define	NV_TYPE_INT8_ARRAY	9
69214282Spjd#define	NV_TYPE_UINT8_ARRAY	10
70214282Spjd#define	NV_TYPE_INT16_ARRAY	11
71214282Spjd#define	NV_TYPE_UINT16_ARRAY	12
72214282Spjd#define	NV_TYPE_INT32_ARRAY	13
73214282Spjd#define	NV_TYPE_UINT32_ARRAY	14
74214282Spjd#define	NV_TYPE_INT64_ARRAY	15
75214282Spjd#define	NV_TYPE_UINT64_ARRAY	16
76214282Spjd#define	NV_TYPE_STRING		17
77214282Spjd
78214282Spjd#define	NV_TYPE_MASK		0x7f
79214282Spjd#define	NV_TYPE_FIRST		NV_TYPE_INT8
80214282Spjd#define	NV_TYPE_LAST		NV_TYPE_STRING
81214282Spjd
82214282Spjd#define	NV_ORDER_NETWORK	0x00
83214282Spjd#define	NV_ORDER_HOST		0x80
84214282Spjd
85214282Spjd#define	NV_ORDER_MASK		0x80
86214282Spjd
87204076Spjd#define	NV_MAGIC	0xaea1e
88204076Spjdstruct nv {
89204076Spjd	int	nv_magic;
90204076Spjd	int	nv_error;
91204076Spjd	struct ebuf *nv_ebuf;
92204076Spjd};
93204076Spjd
94204076Spjdstruct nvhdr {
95204076Spjd	uint8_t		nvh_type;
96204076Spjd	uint8_t		nvh_namesize;
97204076Spjd	uint32_t	nvh_dsize;
98204076Spjd	char		nvh_name[0];
99204076Spjd} __packed;
100204076Spjd#define	NVH_DATA(nvh)	((unsigned char *)nvh + NVH_HSIZE(nvh))
101204076Spjd#define	NVH_HSIZE(nvh)	\
102204076Spjd	(sizeof(struct nvhdr) + roundup2((nvh)->nvh_namesize, 8))
103204076Spjd#define	NVH_DSIZE(nvh)	\
104204076Spjd	(((nvh)->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST ?		\
105204076Spjd	(nvh)->nvh_dsize :						\
106204076Spjd	le32toh((nvh)->nvh_dsize))
107204076Spjd#define	NVH_SIZE(nvh)	(NVH_HSIZE(nvh) + roundup2(NVH_DSIZE(nvh), 8))
108204076Spjd
109204076Spjd#define	NV_CHECK(nv)	do {						\
110229509Strociny	PJDLOG_ASSERT((nv) != NULL);					\
111229509Strociny	PJDLOG_ASSERT((nv)->nv_magic == NV_MAGIC);			\
112204076Spjd} while (0)
113204076Spjd
114204076Spjdstatic void nv_add(struct nv *nv, const unsigned char *value, size_t vsize,
115204076Spjd    int type, const char *name);
116204076Spjdstatic void nv_addv(struct nv *nv, const unsigned char *value, size_t vsize,
117204076Spjd    int type, const char *namefmt, va_list nameap);
118204076Spjdstatic struct nvhdr *nv_find(struct nv *nv, int type, const char *namefmt,
119204076Spjd    va_list nameap);
120204076Spjdstatic void nv_swap(struct nvhdr *nvh, bool tohost);
121204076Spjd
122204076Spjd/*
123204076Spjd * Allocate and initialize new nv structure.
124204076Spjd * Return NULL in case of malloc(3) failure.
125204076Spjd */
126204076Spjdstruct nv *
127204076Spjdnv_alloc(void)
128204076Spjd{
129204076Spjd	struct nv *nv;
130204076Spjd
131204076Spjd	nv = malloc(sizeof(*nv));
132204076Spjd	if (nv == NULL)
133204076Spjd		return (NULL);
134204076Spjd	nv->nv_ebuf = ebuf_alloc(0);
135204076Spjd	if (nv->nv_ebuf == NULL) {
136204076Spjd		free(nv);
137204076Spjd		return (NULL);
138204076Spjd	}
139204076Spjd	nv->nv_error = 0;
140204076Spjd	nv->nv_magic = NV_MAGIC;
141204076Spjd	return (nv);
142204076Spjd}
143204076Spjd
144204076Spjd/*
145204076Spjd * Free the given nv structure.
146204076Spjd */
147204076Spjdvoid
148204076Spjdnv_free(struct nv *nv)
149204076Spjd{
150204076Spjd
151204076Spjd	if (nv == NULL)
152204076Spjd		return;
153204076Spjd
154204076Spjd	NV_CHECK(nv);
155204076Spjd
156204076Spjd	nv->nv_magic = 0;
157204076Spjd	ebuf_free(nv->nv_ebuf);
158204076Spjd	free(nv);
159204076Spjd}
160204076Spjd
161204076Spjd/*
162204076Spjd * Return error for the given nv structure.
163204076Spjd */
164204076Spjdint
165204076Spjdnv_error(const struct nv *nv)
166204076Spjd{
167204076Spjd
168204076Spjd	if (nv == NULL)
169204076Spjd		return (ENOMEM);
170204076Spjd
171204076Spjd	NV_CHECK(nv);
172204076Spjd
173204076Spjd	return (nv->nv_error);
174204076Spjd}
175204076Spjd
176204076Spjd/*
177204076Spjd * Set error for the given nv structure and return previous error.
178204076Spjd */
179204076Spjdint
180204076Spjdnv_set_error(struct nv *nv, int error)
181204076Spjd{
182204076Spjd	int preverr;
183204076Spjd
184204076Spjd	if (nv == NULL)
185204076Spjd		return (ENOMEM);
186204076Spjd
187204076Spjd	NV_CHECK(nv);
188204076Spjd
189204076Spjd	preverr = nv->nv_error;
190204076Spjd	nv->nv_error = error;
191204076Spjd	return (preverr);
192204076Spjd}
193204076Spjd
194204076Spjd/*
195204076Spjd * Validate correctness of the entire nv structure and all its elements.
196204076Spjd * If extrap is not NULL, store number of extra bytes at the end of the buffer.
197204076Spjd */
198204076Spjdint
199204076Spjdnv_validate(struct nv *nv, size_t *extrap)
200204076Spjd{
201204076Spjd	struct nvhdr *nvh;
202204076Spjd	unsigned char *data, *ptr;
203204076Spjd	size_t dsize, size, vsize;
204204076Spjd	int error;
205204076Spjd
206204076Spjd	if (nv == NULL) {
207204076Spjd		errno = ENOMEM;
208204076Spjd		return (-1);
209204076Spjd	}
210204076Spjd
211204076Spjd	NV_CHECK(nv);
212229509Strociny	PJDLOG_ASSERT(nv->nv_error == 0);
213204076Spjd
214204076Spjd	/* TODO: Check that names are unique? */
215204076Spjd
216204076Spjd	error = 0;
217204076Spjd	ptr = ebuf_data(nv->nv_ebuf, &size);
218204076Spjd	while (size > 0) {
219204076Spjd		/*
220204076Spjd		 * Zeros at the end of the buffer are acceptable.
221204076Spjd		 */
222204076Spjd		if (ptr[0] == '\0')
223204076Spjd			break;
224204076Spjd		/*
225204076Spjd		 * Minimum size at this point is size of nvhdr structure, one
226204076Spjd		 * character long name plus terminating '\0'.
227204076Spjd		 */
228204076Spjd		if (size < sizeof(*nvh) + 2) {
229204076Spjd			error = EINVAL;
230204076Spjd			break;
231204076Spjd		}
232204076Spjd		nvh = (struct nvhdr *)ptr;
233204076Spjd		if (size < NVH_HSIZE(nvh)) {
234204076Spjd			error = EINVAL;
235204076Spjd			break;
236204076Spjd		}
237204076Spjd		if (nvh->nvh_name[nvh->nvh_namesize - 1] != '\0') {
238204076Spjd			error = EINVAL;
239204076Spjd			break;
240204076Spjd		}
241204076Spjd		if (strlen(nvh->nvh_name) !=
242204076Spjd		    (size_t)(nvh->nvh_namesize - 1)) {
243204076Spjd			error = EINVAL;
244204076Spjd			break;
245204076Spjd		}
246204076Spjd		if ((nvh->nvh_type & NV_TYPE_MASK) < NV_TYPE_FIRST ||
247204076Spjd		    (nvh->nvh_type & NV_TYPE_MASK) > NV_TYPE_LAST) {
248204076Spjd			error = EINVAL;
249204076Spjd			break;
250204076Spjd		}
251204076Spjd		dsize = NVH_DSIZE(nvh);
252204076Spjd		if (dsize == 0) {
253204076Spjd			error = EINVAL;
254204076Spjd			break;
255204076Spjd		}
256204076Spjd		if (size < NVH_SIZE(nvh)) {
257204076Spjd			error = EINVAL;
258204076Spjd			break;
259204076Spjd		}
260204076Spjd		vsize = 0;
261204076Spjd		switch (nvh->nvh_type & NV_TYPE_MASK) {
262204076Spjd		case NV_TYPE_INT8:
263204076Spjd		case NV_TYPE_UINT8:
264204076Spjd			if (vsize == 0)
265204076Spjd				vsize = 1;
266234294Strociny			/* FALLTHROUGH */
267204076Spjd		case NV_TYPE_INT16:
268204076Spjd		case NV_TYPE_UINT16:
269204076Spjd			if (vsize == 0)
270204076Spjd				vsize = 2;
271234294Strociny			/* FALLTHROUGH */
272204076Spjd		case NV_TYPE_INT32:
273204076Spjd		case NV_TYPE_UINT32:
274204076Spjd			if (vsize == 0)
275204076Spjd				vsize = 4;
276234294Strociny			/* FALLTHROUGH */
277204076Spjd		case NV_TYPE_INT64:
278204076Spjd		case NV_TYPE_UINT64:
279204076Spjd			if (vsize == 0)
280204076Spjd				vsize = 8;
281204076Spjd			if (dsize != vsize) {
282204076Spjd				error = EINVAL;
283204076Spjd				break;
284204076Spjd			}
285204076Spjd			break;
286204076Spjd		case NV_TYPE_INT8_ARRAY:
287204076Spjd		case NV_TYPE_UINT8_ARRAY:
288204076Spjd			break;
289204076Spjd		case NV_TYPE_INT16_ARRAY:
290204076Spjd		case NV_TYPE_UINT16_ARRAY:
291204076Spjd			if (vsize == 0)
292204076Spjd				vsize = 2;
293234294Strociny			/* FALLTHROUGH */
294204076Spjd		case NV_TYPE_INT32_ARRAY:
295204076Spjd		case NV_TYPE_UINT32_ARRAY:
296204076Spjd			if (vsize == 0)
297204076Spjd				vsize = 4;
298234294Strociny			/* FALLTHROUGH */
299204076Spjd		case NV_TYPE_INT64_ARRAY:
300204076Spjd		case NV_TYPE_UINT64_ARRAY:
301204076Spjd			if (vsize == 0)
302204076Spjd				vsize = 8;
303204076Spjd			if ((dsize % vsize) != 0) {
304204076Spjd				error = EINVAL;
305204076Spjd				break;
306204076Spjd			}
307204076Spjd			break;
308204076Spjd		case NV_TYPE_STRING:
309204076Spjd			data = NVH_DATA(nvh);
310204076Spjd			if (data[dsize - 1] != '\0') {
311204076Spjd				error = EINVAL;
312204076Spjd				break;
313204076Spjd			}
314204076Spjd			if (strlen((char *)data) != dsize - 1) {
315204076Spjd				error = EINVAL;
316204076Spjd				break;
317204076Spjd			}
318204076Spjd			break;
319204076Spjd		default:
320229509Strociny			PJDLOG_ABORT("invalid condition");
321204076Spjd		}
322204076Spjd		if (error != 0)
323204076Spjd			break;
324204076Spjd		ptr += NVH_SIZE(nvh);
325204076Spjd		size -= NVH_SIZE(nvh);
326204076Spjd	}
327204076Spjd	if (error != 0) {
328204076Spjd		errno = error;
329204076Spjd		if (nv->nv_error == 0)
330204076Spjd			nv->nv_error = error;
331204076Spjd		return (-1);
332204076Spjd	}
333204076Spjd	if (extrap != NULL)
334204076Spjd		*extrap = size;
335204076Spjd	return (0);
336204076Spjd}
337204076Spjd
338204076Spjd/*
339204076Spjd * Convert the given nv structure to network byte order and return ebuf
340204076Spjd * structure.
341204076Spjd */
342204076Spjdstruct ebuf *
343204076Spjdnv_hton(struct nv *nv)
344204076Spjd{
345204076Spjd	struct nvhdr *nvh;
346204076Spjd	unsigned char *ptr;
347204076Spjd	size_t size;
348204076Spjd
349204076Spjd	NV_CHECK(nv);
350229509Strociny	PJDLOG_ASSERT(nv->nv_error == 0);
351204076Spjd
352204076Spjd	ptr = ebuf_data(nv->nv_ebuf, &size);
353204076Spjd	while (size > 0) {
354204076Spjd		/*
355204076Spjd		 * Minimum size at this point is size of nvhdr structure,
356204076Spjd		 * one character long name plus terminating '\0'.
357204076Spjd		 */
358229509Strociny		PJDLOG_ASSERT(size >= sizeof(*nvh) + 2);
359204076Spjd		nvh = (struct nvhdr *)ptr;
360229509Strociny		PJDLOG_ASSERT(NVH_SIZE(nvh) <= size);
361204076Spjd		nv_swap(nvh, false);
362204076Spjd		ptr += NVH_SIZE(nvh);
363204076Spjd		size -= NVH_SIZE(nvh);
364204076Spjd	}
365204076Spjd
366204076Spjd	return (nv->nv_ebuf);
367204076Spjd}
368204076Spjd
369204076Spjd/*
370204076Spjd * Create nv structure based on ebuf received from the network.
371204076Spjd */
372204076Spjdstruct nv *
373204076Spjdnv_ntoh(struct ebuf *eb)
374204076Spjd{
375204076Spjd	struct nv *nv;
376204076Spjd	size_t extra;
377204076Spjd	int rerrno;
378204076Spjd
379229509Strociny	PJDLOG_ASSERT(eb != NULL);
380204076Spjd
381204076Spjd	nv = malloc(sizeof(*nv));
382204076Spjd	if (nv == NULL)
383204076Spjd		return (NULL);
384204076Spjd	nv->nv_error = 0;
385204076Spjd	nv->nv_ebuf = eb;
386204076Spjd	nv->nv_magic = NV_MAGIC;
387204076Spjd
388231017Strociny	if (nv_validate(nv, &extra) == -1) {
389204076Spjd		rerrno = errno;
390204076Spjd		nv->nv_magic = 0;
391204076Spjd		free(nv);
392204076Spjd		errno = rerrno;
393204076Spjd		return (NULL);
394204076Spjd	}
395204076Spjd	/*
396204076Spjd	 * Remove extra zeros at the end of the buffer.
397204076Spjd	 */
398204076Spjd	ebuf_del_tail(eb, extra);
399204076Spjd
400204076Spjd	return (nv);
401204076Spjd}
402204076Spjd
403204076Spjd#define	NV_DEFINE_ADD(type, TYPE)					\
404204076Spjdvoid									\
405204076Spjdnv_add_##type(struct nv *nv, type##_t value, const char *namefmt, ...)	\
406204076Spjd{									\
407204076Spjd	va_list nameap;							\
408204076Spjd									\
409204076Spjd	va_start(nameap, namefmt);					\
410204076Spjd	nv_addv(nv, (unsigned char *)&value, sizeof(value),		\
411204076Spjd	    NV_TYPE_##TYPE, namefmt, nameap);				\
412204076Spjd	va_end(nameap);							\
413204076Spjd}
414204076Spjd
415204076SpjdNV_DEFINE_ADD(int8, INT8)
416204076SpjdNV_DEFINE_ADD(uint8, UINT8)
417204076SpjdNV_DEFINE_ADD(int16, INT16)
418204076SpjdNV_DEFINE_ADD(uint16, UINT16)
419204076SpjdNV_DEFINE_ADD(int32, INT32)
420204076SpjdNV_DEFINE_ADD(uint32, UINT32)
421204076SpjdNV_DEFINE_ADD(int64, INT64)
422204076SpjdNV_DEFINE_ADD(uint64, UINT64)
423204076Spjd
424204076Spjd#undef	NV_DEFINE_ADD
425204076Spjd
426204076Spjd#define	NV_DEFINE_ADD_ARRAY(type, TYPE)					\
427204076Spjdvoid									\
428204076Spjdnv_add_##type##_array(struct nv *nv, const type##_t *value,		\
429204076Spjd    size_t nsize, const char *namefmt, ...)				\
430204076Spjd{									\
431204076Spjd	va_list nameap;							\
432204076Spjd									\
433204076Spjd	va_start(nameap, namefmt);					\
434204076Spjd	nv_addv(nv, (const unsigned char *)value,			\
435204076Spjd	    sizeof(value[0]) * nsize, NV_TYPE_##TYPE##_ARRAY, namefmt,	\
436204076Spjd	    nameap);							\
437204076Spjd	va_end(nameap);							\
438204076Spjd}
439204076Spjd
440204076SpjdNV_DEFINE_ADD_ARRAY(int8, INT8)
441204076SpjdNV_DEFINE_ADD_ARRAY(uint8, UINT8)
442204076SpjdNV_DEFINE_ADD_ARRAY(int16, INT16)
443204076SpjdNV_DEFINE_ADD_ARRAY(uint16, UINT16)
444204076SpjdNV_DEFINE_ADD_ARRAY(int32, INT32)
445204076SpjdNV_DEFINE_ADD_ARRAY(uint32, UINT32)
446204076SpjdNV_DEFINE_ADD_ARRAY(int64, INT64)
447204076SpjdNV_DEFINE_ADD_ARRAY(uint64, UINT64)
448204076Spjd
449204076Spjd#undef	NV_DEFINE_ADD_ARRAY
450204076Spjd
451204076Spjdvoid
452204076Spjdnv_add_string(struct nv *nv, const char *value, const char *namefmt, ...)
453204076Spjd{
454204076Spjd	va_list nameap;
455204076Spjd	size_t size;
456204076Spjd
457204076Spjd	size = strlen(value) + 1;
458204076Spjd
459204076Spjd	va_start(nameap, namefmt);
460204076Spjd	nv_addv(nv, (const unsigned char *)value, size, NV_TYPE_STRING,
461204076Spjd	    namefmt, nameap);
462204076Spjd	va_end(nameap);
463204076Spjd}
464204076Spjd
465204076Spjdvoid
466204076Spjdnv_add_stringf(struct nv *nv, const char *name, const char *valuefmt, ...)
467204076Spjd{
468204076Spjd	va_list valueap;
469204076Spjd
470204076Spjd	va_start(valueap, valuefmt);
471204076Spjd	nv_add_stringv(nv, name, valuefmt, valueap);
472204076Spjd	va_end(valueap);
473204076Spjd}
474204076Spjd
475204076Spjdvoid
476204076Spjdnv_add_stringv(struct nv *nv, const char *name, const char *valuefmt,
477204076Spjd    va_list valueap)
478204076Spjd{
479204076Spjd	char *value;
480204076Spjd	ssize_t size;
481204076Spjd
482204076Spjd	size = vasprintf(&value, valuefmt, valueap);
483231017Strociny	if (size == -1) {
484204076Spjd		if (nv->nv_error == 0)
485204076Spjd			nv->nv_error = ENOMEM;
486204076Spjd		return;
487204076Spjd	}
488204076Spjd	size++;
489204076Spjd	nv_add(nv, (const unsigned char *)value, size, NV_TYPE_STRING, name);
490204076Spjd	free(value);
491204076Spjd}
492204076Spjd
493204076Spjd#define	NV_DEFINE_GET(type, TYPE)					\
494204076Spjdtype##_t								\
495204076Spjdnv_get_##type(struct nv *nv, const char *namefmt, ...)			\
496204076Spjd{									\
497204076Spjd	struct nvhdr *nvh;						\
498204076Spjd	va_list nameap;							\
499204076Spjd	type##_t value;							\
500204076Spjd									\
501204076Spjd	va_start(nameap, namefmt);					\
502204076Spjd	nvh = nv_find(nv, NV_TYPE_##TYPE, namefmt, nameap);		\
503204076Spjd	va_end(nameap);							\
504204076Spjd	if (nvh == NULL)						\
505204076Spjd		return (0);						\
506229509Strociny	PJDLOG_ASSERT((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST);\
507229509Strociny	PJDLOG_ASSERT(sizeof(value) == nvh->nvh_dsize);			\
508204076Spjd	bcopy(NVH_DATA(nvh), &value, sizeof(value));			\
509204076Spjd									\
510204076Spjd	return (value);							\
511204076Spjd}
512204076Spjd
513204076SpjdNV_DEFINE_GET(int8, INT8)
514204076SpjdNV_DEFINE_GET(uint8, UINT8)
515204076SpjdNV_DEFINE_GET(int16, INT16)
516204076SpjdNV_DEFINE_GET(uint16, UINT16)
517204076SpjdNV_DEFINE_GET(int32, INT32)
518204076SpjdNV_DEFINE_GET(uint32, UINT32)
519204076SpjdNV_DEFINE_GET(int64, INT64)
520204076SpjdNV_DEFINE_GET(uint64, UINT64)
521204076Spjd
522204076Spjd#undef	NV_DEFINE_GET
523204076Spjd
524204076Spjd#define	NV_DEFINE_GET_ARRAY(type, TYPE)					\
525204076Spjdconst type##_t *							\
526204076Spjdnv_get_##type##_array(struct nv *nv, size_t *sizep,			\
527204076Spjd    const char *namefmt, ...)						\
528204076Spjd{									\
529204076Spjd	struct nvhdr *nvh;						\
530204076Spjd	va_list nameap;							\
531204076Spjd									\
532204076Spjd	va_start(nameap, namefmt);					\
533204076Spjd	nvh = nv_find(nv, NV_TYPE_##TYPE##_ARRAY, namefmt, nameap);	\
534204076Spjd	va_end(nameap);							\
535204076Spjd	if (nvh == NULL)						\
536204076Spjd		return (NULL);						\
537229509Strociny	PJDLOG_ASSERT((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST);\
538229509Strociny	PJDLOG_ASSERT((nvh->nvh_dsize % sizeof(type##_t)) == 0);	\
539204076Spjd	if (sizep != NULL)						\
540204076Spjd		*sizep = nvh->nvh_dsize / sizeof(type##_t);		\
541204076Spjd	return ((type##_t *)(void *)NVH_DATA(nvh));			\
542204076Spjd}
543204076Spjd
544204076SpjdNV_DEFINE_GET_ARRAY(int8, INT8)
545204076SpjdNV_DEFINE_GET_ARRAY(uint8, UINT8)
546204076SpjdNV_DEFINE_GET_ARRAY(int16, INT16)
547204076SpjdNV_DEFINE_GET_ARRAY(uint16, UINT16)
548204076SpjdNV_DEFINE_GET_ARRAY(int32, INT32)
549204076SpjdNV_DEFINE_GET_ARRAY(uint32, UINT32)
550204076SpjdNV_DEFINE_GET_ARRAY(int64, INT64)
551204076SpjdNV_DEFINE_GET_ARRAY(uint64, UINT64)
552204076Spjd
553204076Spjd#undef	NV_DEFINE_GET_ARRAY
554204076Spjd
555204076Spjdconst char *
556204076Spjdnv_get_string(struct nv *nv, const char *namefmt, ...)
557204076Spjd{
558204076Spjd	struct nvhdr *nvh;
559204076Spjd	va_list nameap;
560204076Spjd	char *str;
561204076Spjd
562204076Spjd	va_start(nameap, namefmt);
563204076Spjd	nvh = nv_find(nv, NV_TYPE_STRING, namefmt, nameap);
564204076Spjd	va_end(nameap);
565204076Spjd	if (nvh == NULL)
566204076Spjd		return (NULL);
567229509Strociny	PJDLOG_ASSERT((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST);
568229509Strociny	PJDLOG_ASSERT(nvh->nvh_dsize >= 1);
569260007Strociny	str = (char *)NVH_DATA(nvh);
570229509Strociny	PJDLOG_ASSERT(str[nvh->nvh_dsize - 1] == '\0');
571229509Strociny	PJDLOG_ASSERT(strlen(str) == nvh->nvh_dsize - 1);
572204076Spjd	return (str);
573204076Spjd}
574204076Spjd
575217732Spjdstatic bool
576217732Spjdnv_vexists(struct nv *nv, const char *namefmt, va_list nameap)
577214283Spjd{
578214283Spjd	struct nvhdr *nvh;
579214283Spjd	int snverror, serrno;
580214283Spjd
581214283Spjd	if (nv == NULL)
582214283Spjd		return (false);
583214283Spjd
584214283Spjd	serrno = errno;
585214283Spjd	snverror = nv->nv_error;
586214283Spjd
587214283Spjd	nvh = nv_find(nv, NV_TYPE_NONE, namefmt, nameap);
588214283Spjd
589214283Spjd	errno = serrno;
590214283Spjd	nv->nv_error = snverror;
591214283Spjd
592214283Spjd	return (nvh != NULL);
593214283Spjd}
594214283Spjd
595217732Spjdbool
596217732Spjdnv_exists(struct nv *nv, const char *namefmt, ...)
597217732Spjd{
598217732Spjd	va_list nameap;
599217732Spjd	bool ret;
600217732Spjd
601217732Spjd	va_start(nameap, namefmt);
602217732Spjd	ret = nv_vexists(nv, namefmt, nameap);
603217732Spjd	va_end(nameap);
604217732Spjd
605217732Spjd	return (ret);
606217732Spjd}
607217732Spjd
608217732Spjdvoid
609217732Spjdnv_assert(struct nv *nv, const char *namefmt, ...)
610217732Spjd{
611217732Spjd	va_list nameap;
612217732Spjd
613217732Spjd	va_start(nameap, namefmt);
614229509Strociny	PJDLOG_ASSERT(nv_vexists(nv, namefmt, nameap));
615217732Spjd	va_end(nameap);
616217732Spjd}
617217732Spjd
618204076Spjd/*
619204076Spjd * Dump content of the nv structure.
620204076Spjd */
621204076Spjdvoid
622204076Spjdnv_dump(struct nv *nv)
623204076Spjd{
624204076Spjd	struct nvhdr *nvh;
625204076Spjd	unsigned char *data, *ptr;
626204076Spjd	size_t dsize, size;
627204076Spjd	unsigned int ii;
628204076Spjd	bool swap;
629204076Spjd
630231017Strociny	if (nv_validate(nv, NULL) == -1) {
631204076Spjd		printf("error: %d\n", errno);
632204076Spjd		return;
633204076Spjd	}
634204076Spjd
635204076Spjd	NV_CHECK(nv);
636229509Strociny	PJDLOG_ASSERT(nv->nv_error == 0);
637204076Spjd
638204076Spjd	ptr = ebuf_data(nv->nv_ebuf, &size);
639204076Spjd	while (size > 0) {
640229509Strociny		PJDLOG_ASSERT(size >= sizeof(*nvh) + 2);
641204076Spjd		nvh = (struct nvhdr *)ptr;
642229509Strociny		PJDLOG_ASSERT(size >= NVH_SIZE(nvh));
643204076Spjd		swap = ((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_NETWORK);
644204076Spjd		dsize = NVH_DSIZE(nvh);
645204076Spjd		data = NVH_DATA(nvh);
646204076Spjd		printf("  %s", nvh->nvh_name);
647204076Spjd		switch (nvh->nvh_type & NV_TYPE_MASK) {
648204076Spjd		case NV_TYPE_INT8:
649204076Spjd			printf("(int8): %jd", (intmax_t)(*(int8_t *)data));
650204076Spjd			break;
651204076Spjd		case NV_TYPE_UINT8:
652204076Spjd			printf("(uint8): %ju", (uintmax_t)(*(uint8_t *)data));
653204076Spjd			break;
654204076Spjd		case NV_TYPE_INT16:
655204076Spjd			printf("(int16): %jd", swap ?
656204076Spjd			    (intmax_t)le16toh(*(int16_t *)(void *)data) :
657204076Spjd			    (intmax_t)*(int16_t *)(void *)data);
658204076Spjd			break;
659204076Spjd		case NV_TYPE_UINT16:
660204076Spjd			printf("(uint16): %ju", swap ?
661204076Spjd			    (uintmax_t)le16toh(*(uint16_t *)(void *)data) :
662204076Spjd			    (uintmax_t)*(uint16_t *)(void *)data);
663204076Spjd			break;
664204076Spjd		case NV_TYPE_INT32:
665204076Spjd			printf("(int32): %jd", swap ?
666204076Spjd			    (intmax_t)le32toh(*(int32_t *)(void *)data) :
667204076Spjd			    (intmax_t)*(int32_t *)(void *)data);
668204076Spjd			break;
669204076Spjd		case NV_TYPE_UINT32:
670204076Spjd			printf("(uint32): %ju", swap ?
671204076Spjd			    (uintmax_t)le32toh(*(uint32_t *)(void *)data) :
672204076Spjd			    (uintmax_t)*(uint32_t *)(void *)data);
673204076Spjd			break;
674204076Spjd		case NV_TYPE_INT64:
675204076Spjd			printf("(int64): %jd", swap ?
676204076Spjd			    (intmax_t)le64toh(*(int64_t *)(void *)data) :
677204076Spjd			    (intmax_t)*(int64_t *)(void *)data);
678204076Spjd			break;
679204076Spjd		case NV_TYPE_UINT64:
680204076Spjd			printf("(uint64): %ju", swap ?
681204076Spjd			    (uintmax_t)le64toh(*(uint64_t *)(void *)data) :
682204076Spjd			    (uintmax_t)*(uint64_t *)(void *)data);
683204076Spjd			break;
684204076Spjd		case NV_TYPE_INT8_ARRAY:
685204076Spjd			printf("(int8 array):");
686204076Spjd			for (ii = 0; ii < dsize; ii++)
687204076Spjd				printf(" %jd", (intmax_t)((int8_t *)data)[ii]);
688204076Spjd			break;
689204076Spjd		case NV_TYPE_UINT8_ARRAY:
690204076Spjd			printf("(uint8 array):");
691204076Spjd			for (ii = 0; ii < dsize; ii++)
692204076Spjd				printf(" %ju", (uintmax_t)((uint8_t *)data)[ii]);
693204076Spjd			break;
694204076Spjd		case NV_TYPE_INT16_ARRAY:
695204076Spjd			printf("(int16 array):");
696204076Spjd			for (ii = 0; ii < dsize / 2; ii++) {
697204076Spjd				printf(" %jd", swap ?
698204076Spjd				    (intmax_t)le16toh(((int16_t *)(void *)data)[ii]) :
699204076Spjd				    (intmax_t)((int16_t *)(void *)data)[ii]);
700204076Spjd			}
701204076Spjd			break;
702204076Spjd		case NV_TYPE_UINT16_ARRAY:
703204076Spjd			printf("(uint16 array):");
704204076Spjd			for (ii = 0; ii < dsize / 2; ii++) {
705204076Spjd				printf(" %ju", swap ?
706204076Spjd				    (uintmax_t)le16toh(((uint16_t *)(void *)data)[ii]) :
707204076Spjd				    (uintmax_t)((uint16_t *)(void *)data)[ii]);
708204076Spjd			}
709204076Spjd			break;
710204076Spjd		case NV_TYPE_INT32_ARRAY:
711204076Spjd			printf("(int32 array):");
712204076Spjd			for (ii = 0; ii < dsize / 4; ii++) {
713204076Spjd				printf(" %jd", swap ?
714204076Spjd				    (intmax_t)le32toh(((int32_t *)(void *)data)[ii]) :
715204076Spjd				    (intmax_t)((int32_t *)(void *)data)[ii]);
716204076Spjd			}
717204076Spjd			break;
718204076Spjd		case NV_TYPE_UINT32_ARRAY:
719204076Spjd			printf("(uint32 array):");
720204076Spjd			for (ii = 0; ii < dsize / 4; ii++) {
721204076Spjd				printf(" %ju", swap ?
722204076Spjd				    (uintmax_t)le32toh(((uint32_t *)(void *)data)[ii]) :
723204076Spjd				    (uintmax_t)((uint32_t *)(void *)data)[ii]);
724204076Spjd			}
725204076Spjd			break;
726204076Spjd		case NV_TYPE_INT64_ARRAY:
727204076Spjd			printf("(int64 array):");
728204076Spjd			for (ii = 0; ii < dsize / 8; ii++) {
729204076Spjd				printf(" %ju", swap ?
730204076Spjd				    (uintmax_t)le64toh(((uint64_t *)(void *)data)[ii]) :
731204076Spjd				    (uintmax_t)((uint64_t *)(void *)data)[ii]);
732204076Spjd			}
733204076Spjd			break;
734204076Spjd		case NV_TYPE_UINT64_ARRAY:
735204076Spjd			printf("(uint64 array):");
736204076Spjd			for (ii = 0; ii < dsize / 8; ii++) {
737204076Spjd				printf(" %ju", swap ?
738204076Spjd				    (uintmax_t)le64toh(((uint64_t *)(void *)data)[ii]) :
739204076Spjd				    (uintmax_t)((uint64_t *)(void *)data)[ii]);
740204076Spjd			}
741204076Spjd			break;
742204076Spjd		case NV_TYPE_STRING:
743204076Spjd			printf("(string): %s", (char *)data);
744204076Spjd			break;
745204076Spjd		default:
746229509Strociny			PJDLOG_ABORT("invalid condition");
747204076Spjd		}
748204076Spjd		printf("\n");
749204076Spjd		ptr += NVH_SIZE(nvh);
750204076Spjd		size -= NVH_SIZE(nvh);
751204076Spjd	}
752204076Spjd}
753204076Spjd
754204076Spjd/*
755204076Spjd * Local routines below.
756204076Spjd */
757204076Spjd
758204076Spjdstatic void
759204076Spjdnv_add(struct nv *nv, const unsigned char *value, size_t vsize, int type,
760204076Spjd    const char *name)
761204076Spjd{
762204076Spjd	static unsigned char align[7];
763204076Spjd	struct nvhdr *nvh;
764204076Spjd	size_t namesize;
765204076Spjd
766204076Spjd	if (nv == NULL) {
767204076Spjd		errno = ENOMEM;
768204076Spjd		return;
769204076Spjd	}
770204076Spjd
771204076Spjd	NV_CHECK(nv);
772204076Spjd
773204076Spjd	namesize = strlen(name) + 1;
774204076Spjd
775204076Spjd	nvh = malloc(sizeof(*nvh) + roundup2(namesize, 8));
776204076Spjd	if (nvh == NULL) {
777204076Spjd		if (nv->nv_error == 0)
778204076Spjd			nv->nv_error = ENOMEM;
779204076Spjd		return;
780204076Spjd	}
781204076Spjd	nvh->nvh_type = NV_ORDER_HOST | type;
782204076Spjd	nvh->nvh_namesize = (uint8_t)namesize;
783204076Spjd	nvh->nvh_dsize = (uint32_t)vsize;
784204076Spjd	bcopy(name, nvh->nvh_name, namesize);
785204076Spjd
786204076Spjd	/* Add header first. */
787231017Strociny	if (ebuf_add_tail(nv->nv_ebuf, nvh, NVH_HSIZE(nvh)) == -1) {
788229509Strociny		PJDLOG_ASSERT(errno != 0);
789204076Spjd		if (nv->nv_error == 0)
790204076Spjd			nv->nv_error = errno;
791209180Spjd		free(nvh);
792204076Spjd		return;
793204076Spjd	}
794209180Spjd	free(nvh);
795204076Spjd	/* Add the actual data. */
796231017Strociny	if (ebuf_add_tail(nv->nv_ebuf, value, vsize) == -1) {
797229509Strociny		PJDLOG_ASSERT(errno != 0);
798204076Spjd		if (nv->nv_error == 0)
799204076Spjd			nv->nv_error = errno;
800204076Spjd		return;
801204076Spjd	}
802204076Spjd	/* Align the data (if needed). */
803204076Spjd	vsize = roundup2(vsize, 8) - vsize;
804204076Spjd	if (vsize == 0)
805204076Spjd		return;
806229509Strociny	PJDLOG_ASSERT(vsize > 0 && vsize <= sizeof(align));
807231017Strociny	if (ebuf_add_tail(nv->nv_ebuf, align, vsize) == -1) {
808229509Strociny		PJDLOG_ASSERT(errno != 0);
809204076Spjd		if (nv->nv_error == 0)
810204076Spjd			nv->nv_error = errno;
811204076Spjd		return;
812204076Spjd	}
813204076Spjd}
814204076Spjd
815204076Spjdstatic void
816204076Spjdnv_addv(struct nv *nv, const unsigned char *value, size_t vsize, int type,
817204076Spjd    const char *namefmt, va_list nameap)
818204076Spjd{
819204076Spjd	char name[255];
820204076Spjd	size_t namesize;
821204076Spjd
822204076Spjd	namesize = vsnprintf(name, sizeof(name), namefmt, nameap);
823229509Strociny	PJDLOG_ASSERT(namesize > 0 && namesize < sizeof(name));
824204076Spjd
825204076Spjd	nv_add(nv, value, vsize, type, name);
826204076Spjd}
827204076Spjd
828204076Spjdstatic struct nvhdr *
829204076Spjdnv_find(struct nv *nv, int type, const char *namefmt, va_list nameap)
830204076Spjd{
831204076Spjd	char name[255];
832204076Spjd	struct nvhdr *nvh;
833204076Spjd	unsigned char *ptr;
834204076Spjd	size_t size, namesize;
835204076Spjd
836204076Spjd	if (nv == NULL) {
837204076Spjd		errno = ENOMEM;
838204076Spjd		return (NULL);
839204076Spjd	}
840204076Spjd
841204076Spjd	NV_CHECK(nv);
842204076Spjd
843204076Spjd	namesize = vsnprintf(name, sizeof(name), namefmt, nameap);
844229509Strociny	PJDLOG_ASSERT(namesize > 0 && namesize < sizeof(name));
845204076Spjd	namesize++;
846204076Spjd
847204076Spjd	ptr = ebuf_data(nv->nv_ebuf, &size);
848204076Spjd	while (size > 0) {
849229509Strociny		PJDLOG_ASSERT(size >= sizeof(*nvh) + 2);
850204076Spjd		nvh = (struct nvhdr *)ptr;
851229509Strociny		PJDLOG_ASSERT(size >= NVH_SIZE(nvh));
852204076Spjd		nv_swap(nvh, true);
853204076Spjd		if (strcmp(nvh->nvh_name, name) == 0) {
854214283Spjd			if (type != NV_TYPE_NONE &&
855214283Spjd			    (nvh->nvh_type & NV_TYPE_MASK) != type) {
856204076Spjd				errno = EINVAL;
857204076Spjd				if (nv->nv_error == 0)
858204076Spjd					nv->nv_error = EINVAL;
859204076Spjd				return (NULL);
860204076Spjd			}
861204076Spjd			return (nvh);
862204076Spjd		}
863204076Spjd		ptr += NVH_SIZE(nvh);
864204076Spjd		size -= NVH_SIZE(nvh);
865204076Spjd	}
866204076Spjd	errno = ENOENT;
867204076Spjd	if (nv->nv_error == 0)
868204076Spjd		nv->nv_error = ENOENT;
869204076Spjd	return (NULL);
870204076Spjd}
871204076Spjd
872204076Spjdstatic void
873204076Spjdnv_swap(struct nvhdr *nvh, bool tohost)
874204076Spjd{
875204076Spjd	unsigned char *data, *end, *p;
876204076Spjd	size_t vsize;
877204076Spjd
878204076Spjd	data = NVH_DATA(nvh);
879204076Spjd	if (tohost) {
880204076Spjd		if ((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST)
881204076Spjd			return;
882204076Spjd		nvh->nvh_dsize = le32toh(nvh->nvh_dsize);
883204076Spjd		end = data + nvh->nvh_dsize;
884204076Spjd		nvh->nvh_type &= ~NV_ORDER_MASK;
885204076Spjd		nvh->nvh_type |= NV_ORDER_HOST;
886204076Spjd	} else {
887204076Spjd		if ((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_NETWORK)
888204076Spjd			return;
889204076Spjd		end = data + nvh->nvh_dsize;
890204076Spjd		nvh->nvh_dsize = htole32(nvh->nvh_dsize);
891204076Spjd		nvh->nvh_type &= ~NV_ORDER_MASK;
892204076Spjd		nvh->nvh_type |= NV_ORDER_NETWORK;
893204076Spjd	}
894204076Spjd
895204076Spjd	vsize = 0;
896204076Spjd
897204076Spjd	switch (nvh->nvh_type & NV_TYPE_MASK) {
898204076Spjd	case NV_TYPE_INT8:
899204076Spjd	case NV_TYPE_UINT8:
900204076Spjd	case NV_TYPE_INT8_ARRAY:
901204076Spjd	case NV_TYPE_UINT8_ARRAY:
902204076Spjd		break;
903204076Spjd	case NV_TYPE_INT16:
904204076Spjd	case NV_TYPE_UINT16:
905204076Spjd	case NV_TYPE_INT16_ARRAY:
906204076Spjd	case NV_TYPE_UINT16_ARRAY:
907204076Spjd		if (vsize == 0)
908204076Spjd			vsize = 2;
909234294Strociny		/* FALLTHROUGH */
910204076Spjd	case NV_TYPE_INT32:
911204076Spjd	case NV_TYPE_UINT32:
912204076Spjd	case NV_TYPE_INT32_ARRAY:
913204076Spjd	case NV_TYPE_UINT32_ARRAY:
914204076Spjd		if (vsize == 0)
915204076Spjd			vsize = 4;
916234294Strociny		/* FALLTHROUGH */
917204076Spjd	case NV_TYPE_INT64:
918204076Spjd	case NV_TYPE_UINT64:
919204076Spjd	case NV_TYPE_INT64_ARRAY:
920204076Spjd	case NV_TYPE_UINT64_ARRAY:
921204076Spjd		if (vsize == 0)
922204076Spjd			vsize = 8;
923204076Spjd		for (p = data; p < end; p += vsize) {
924204076Spjd			if (tohost) {
925204076Spjd				switch (vsize) {
926204076Spjd				case 2:
927204076Spjd					*(uint16_t *)(void *)p =
928204076Spjd					    le16toh(*(uint16_t *)(void *)p);
929204076Spjd					break;
930204076Spjd				case 4:
931204076Spjd					*(uint32_t *)(void *)p =
932204076Spjd					    le32toh(*(uint32_t *)(void *)p);
933204076Spjd					break;
934204076Spjd				case 8:
935204076Spjd					*(uint64_t *)(void *)p =
936204076Spjd					    le64toh(*(uint64_t *)(void *)p);
937204076Spjd					break;
938204076Spjd				default:
939229509Strociny					PJDLOG_ABORT("invalid condition");
940204076Spjd				}
941204076Spjd			} else {
942204076Spjd				switch (vsize) {
943204076Spjd				case 2:
944204076Spjd					*(uint16_t *)(void *)p =
945204076Spjd					    htole16(*(uint16_t *)(void *)p);
946204076Spjd					break;
947204076Spjd				case 4:
948204076Spjd					*(uint32_t *)(void *)p =
949204076Spjd					    htole32(*(uint32_t *)(void *)p);
950204076Spjd					break;
951204076Spjd				case 8:
952204076Spjd					*(uint64_t *)(void *)p =
953204076Spjd					    htole64(*(uint64_t *)(void *)p);
954204076Spjd					break;
955204076Spjd				default:
956229509Strociny					PJDLOG_ABORT("invalid condition");
957204076Spjd				}
958204076Spjd			}
959204076Spjd		}
960204076Spjd		break;
961204076Spjd	case NV_TYPE_STRING:
962204076Spjd		break;
963204076Spjd	default:
964229509Strociny		PJDLOG_ABORT("unrecognized type");
965204076Spjd	}
966204076Spjd}
967