nv.c revision 214282
1214501Srpaulo/*-
2214501Srpaulo * Copyright (c) 2009-2010 The FreeBSD Foundation
3214501Srpaulo * All rights reserved.
4214501Srpaulo *
5252726Srpaulo * This software was developed by Pawel Jakub Dawidek under sponsorship from
6252726Srpaulo * the FreeBSD Foundation.
7214501Srpaulo *
8214501Srpaulo * Redistribution and use in source and binary forms, with or without
9214501Srpaulo * modification, are permitted provided that the following conditions
10214501Srpaulo * are met:
11214501Srpaulo * 1. Redistributions of source code must retain the above copyright
12214501Srpaulo *    notice, this list of conditions and the following disclaimer.
13252726Srpaulo * 2. Redistributions in binary form must reproduce the above copyright
14214501Srpaulo *    notice, this list of conditions and the following disclaimer in the
15214501Srpaulo *    documentation and/or other materials provided with the distribution.
16214501Srpaulo *
17214501Srpaulo * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
18214501Srpaulo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19214501Srpaulo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20214501Srpaulo * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
21214501Srpaulo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22214501Srpaulo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23214501Srpaulo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24214501Srpaulo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25214501Srpaulo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26214501Srpaulo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27214501Srpaulo * SUCH DAMAGE.
28214501Srpaulo */
29214501Srpaulo
30214501Srpaulo#include <sys/cdefs.h>
31214501Srpaulo__FBSDID("$FreeBSD: head/sbin/hastd/nv.c 214282 2010-10-24 17:22:34Z pjd $");
32214501Srpaulo
33214501Srpaulo#include <sys/param.h>
34214501Srpaulo#include <sys/endian.h>
35214501Srpaulo
36214501Srpaulo#include <assert.h>
37214501Srpaulo#include <bitstring.h>
38214501Srpaulo#include <errno.h>
39214501Srpaulo#include <stdarg.h>
40214501Srpaulo#include <stdbool.h>
41214501Srpaulo#include <stdint.h>
42214501Srpaulo#include <stdlib.h>
43214501Srpaulo#include <string.h>
44214501Srpaulo#include <unistd.h>
45214501Srpaulo
46214501Srpaulo#include <ebuf.h>
47214501Srpaulo#include <nv.h>
48214501Srpaulo
49214501Srpaulo#define	NV_TYPE_INT8		1
50214501Srpaulo#define	NV_TYPE_UINT8		2
51214501Srpaulo#define	NV_TYPE_INT16		3
52214501Srpaulo#define	NV_TYPE_UINT16		4
53214501Srpaulo#define	NV_TYPE_INT32		5
54214501Srpaulo#define	NV_TYPE_UINT32		6
55214501Srpaulo#define	NV_TYPE_INT64		7
56214501Srpaulo#define	NV_TYPE_UINT64		8
57214501Srpaulo#define	NV_TYPE_INT8_ARRAY	9
58214501Srpaulo#define	NV_TYPE_UINT8_ARRAY	10
59214501Srpaulo#define	NV_TYPE_INT16_ARRAY	11
60214501Srpaulo#define	NV_TYPE_UINT16_ARRAY	12
61214501Srpaulo#define	NV_TYPE_INT32_ARRAY	13
62214501Srpaulo#define	NV_TYPE_UINT32_ARRAY	14
63214501Srpaulo#define	NV_TYPE_INT64_ARRAY	15
64214501Srpaulo#define	NV_TYPE_UINT64_ARRAY	16
65214501Srpaulo#define	NV_TYPE_STRING		17
66214501Srpaulo
67214501Srpaulo#define	NV_TYPE_MASK		0x7f
68214501Srpaulo#define	NV_TYPE_FIRST		NV_TYPE_INT8
69214501Srpaulo#define	NV_TYPE_LAST		NV_TYPE_STRING
70214501Srpaulo
71214501Srpaulo#define	NV_ORDER_NETWORK	0x00
72214501Srpaulo#define	NV_ORDER_HOST		0x80
73214501Srpaulo
74214501Srpaulo#define	NV_ORDER_MASK		0x80
75214501Srpaulo
76214501Srpaulo#define	NV_MAGIC	0xaea1e
77214501Srpaulostruct nv {
78214501Srpaulo	int	nv_magic;
79214501Srpaulo	int	nv_error;
80214501Srpaulo	struct ebuf *nv_ebuf;
81214501Srpaulo};
82214501Srpaulo
83214501Srpaulostruct nvhdr {
84214501Srpaulo	uint8_t		nvh_type;
85214501Srpaulo	uint8_t		nvh_namesize;
86214501Srpaulo	uint32_t	nvh_dsize;
87214501Srpaulo	char		nvh_name[0];
88214501Srpaulo} __packed;
89214501Srpaulo#define	NVH_DATA(nvh)	((unsigned char *)nvh + NVH_HSIZE(nvh))
90214501Srpaulo#define	NVH_HSIZE(nvh)	\
91214501Srpaulo	(sizeof(struct nvhdr) + roundup2((nvh)->nvh_namesize, 8))
92214501Srpaulo#define	NVH_DSIZE(nvh)	\
93214501Srpaulo	(((nvh)->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST ?		\
94281806Srpaulo	(nvh)->nvh_dsize :						\
95214501Srpaulo	le32toh((nvh)->nvh_dsize))
96214501Srpaulo#define	NVH_SIZE(nvh)	(NVH_HSIZE(nvh) + roundup2(NVH_DSIZE(nvh), 8))
97214501Srpaulo
98214501Srpaulo#define	NV_CHECK(nv)	do {						\
99214501Srpaulo	assert((nv) != NULL);						\
100214501Srpaulo	assert((nv)->nv_magic == NV_MAGIC);				\
101214501Srpaulo} while (0)
102214501Srpaulo
103214501Srpaulostatic void nv_add(struct nv *nv, const unsigned char *value, size_t vsize,
104214501Srpaulo    int type, const char *name);
105214501Srpaulostatic void nv_addv(struct nv *nv, const unsigned char *value, size_t vsize,
106252726Srpaulo    int type, const char *namefmt, va_list nameap);
107214501Srpaulostatic struct nvhdr *nv_find(struct nv *nv, int type, const char *namefmt,
108214501Srpaulo    va_list nameap);
109214501Srpaulostatic void nv_swap(struct nvhdr *nvh, bool tohost);
110214501Srpaulo
111214501Srpaulo/*
112214501Srpaulo * Allocate and initialize new nv structure.
113281806Srpaulo * Return NULL in case of malloc(3) failure.
114214501Srpaulo */
115214501Srpaulostruct nv *
116214501Srpaulonv_alloc(void)
117214501Srpaulo{
118214501Srpaulo	struct nv *nv;
119214501Srpaulo
120214501Srpaulo	nv = malloc(sizeof(*nv));
121214501Srpaulo	if (nv == NULL)
122214501Srpaulo		return (NULL);
123214501Srpaulo	nv->nv_ebuf = ebuf_alloc(0);
124214501Srpaulo	if (nv->nv_ebuf == NULL) {
125214501Srpaulo		free(nv);
126214501Srpaulo		return (NULL);
127214501Srpaulo	}
128214501Srpaulo	nv->nv_error = 0;
129214501Srpaulo	nv->nv_magic = NV_MAGIC;
130214501Srpaulo	return (nv);
131214501Srpaulo}
132214501Srpaulo
133214501Srpaulo/*
134214501Srpaulo * Free the given nv structure.
135281806Srpaulo */
136214501Srpaulovoid
137214501Srpaulonv_free(struct nv *nv)
138214501Srpaulo{
139214501Srpaulo
140214501Srpaulo	if (nv == NULL)
141214501Srpaulo		return;
142214501Srpaulo
143214501Srpaulo	NV_CHECK(nv);
144214501Srpaulo
145214501Srpaulo	nv->nv_magic = 0;
146214501Srpaulo	ebuf_free(nv->nv_ebuf);
147214501Srpaulo	free(nv);
148214501Srpaulo}
149214501Srpaulo
150214501Srpaulo/*
151214501Srpaulo * Return error for the given nv structure.
152214501Srpaulo */
153214501Srpauloint
154214501Srpaulonv_error(const struct nv *nv)
155214501Srpaulo{
156214501Srpaulo
157214501Srpaulo	if (nv == NULL)
158214501Srpaulo		return (ENOMEM);
159214501Srpaulo
160214501Srpaulo	NV_CHECK(nv);
161214501Srpaulo
162214501Srpaulo	return (nv->nv_error);
163214501Srpaulo}
164214501Srpaulo
165214501Srpaulo/*
166214501Srpaulo * Set error for the given nv structure and return previous error.
167214501Srpaulo */
168214501Srpauloint
169214501Srpaulonv_set_error(struct nv *nv, int error)
170214501Srpaulo{
171214501Srpaulo	int preverr;
172214501Srpaulo
173214501Srpaulo	if (nv == NULL)
174214501Srpaulo		return (ENOMEM);
175214501Srpaulo
176214501Srpaulo	NV_CHECK(nv);
177214501Srpaulo
178214501Srpaulo	preverr = nv->nv_error;
179214501Srpaulo	nv->nv_error = error;
180214501Srpaulo	return (preverr);
181214501Srpaulo}
182214501Srpaulo
183214501Srpaulo/*
184214501Srpaulo * Validate correctness of the entire nv structure and all its elements.
185214501Srpaulo * If extrap is not NULL, store number of extra bytes at the end of the buffer.
186214501Srpaulo */
187214501Srpauloint
188214501Srpaulonv_validate(struct nv *nv, size_t *extrap)
189214501Srpaulo{
190214501Srpaulo	struct nvhdr *nvh;
191214501Srpaulo	unsigned char *data, *ptr;
192214501Srpaulo	size_t dsize, size, vsize;
193214501Srpaulo	int error;
194214501Srpaulo
195214501Srpaulo	if (nv == NULL) {
196214501Srpaulo		errno = ENOMEM;
197214501Srpaulo		return (-1);
198214501Srpaulo	}
199214501Srpaulo
200214501Srpaulo	NV_CHECK(nv);
201214501Srpaulo	assert(nv->nv_error == 0);
202214501Srpaulo
203214501Srpaulo	/* TODO: Check that names are unique? */
204214501Srpaulo
205214501Srpaulo	error = 0;
206214501Srpaulo	ptr = ebuf_data(nv->nv_ebuf, &size);
207214501Srpaulo	while (size > 0) {
208214501Srpaulo		/*
209214501Srpaulo		 * Zeros at the end of the buffer are acceptable.
210214501Srpaulo		 */
211214501Srpaulo		if (ptr[0] == '\0')
212214501Srpaulo			break;
213214501Srpaulo		/*
214214501Srpaulo		 * Minimum size at this point is size of nvhdr structure, one
215214501Srpaulo		 * character long name plus terminating '\0'.
216214501Srpaulo		 */
217214501Srpaulo		if (size < sizeof(*nvh) + 2) {
218214501Srpaulo			error = EINVAL;
219214501Srpaulo			break;
220214501Srpaulo		}
221214501Srpaulo		nvh = (struct nvhdr *)ptr;
222214501Srpaulo		if (size < NVH_HSIZE(nvh)) {
223214501Srpaulo			error = EINVAL;
224214501Srpaulo			break;
225214501Srpaulo		}
226214501Srpaulo		if (nvh->nvh_name[nvh->nvh_namesize - 1] != '\0') {
227214501Srpaulo			error = EINVAL;
228214501Srpaulo			break;
229214501Srpaulo		}
230214501Srpaulo		if (strlen(nvh->nvh_name) !=
231214501Srpaulo		    (size_t)(nvh->nvh_namesize - 1)) {
232214501Srpaulo			error = EINVAL;
233214501Srpaulo			break;
234214501Srpaulo		}
235214501Srpaulo		if ((nvh->nvh_type & NV_TYPE_MASK) < NV_TYPE_FIRST ||
236214501Srpaulo		    (nvh->nvh_type & NV_TYPE_MASK) > NV_TYPE_LAST) {
237214501Srpaulo			error = EINVAL;
238214501Srpaulo			break;
239214501Srpaulo		}
240214501Srpaulo		dsize = NVH_DSIZE(nvh);
241214501Srpaulo		if (dsize == 0) {
242214501Srpaulo			error = EINVAL;
243214501Srpaulo			break;
244214501Srpaulo		}
245214501Srpaulo		if (size < NVH_SIZE(nvh)) {
246214501Srpaulo			error = EINVAL;
247214501Srpaulo			break;
248214501Srpaulo		}
249214501Srpaulo		vsize = 0;
250214501Srpaulo		switch (nvh->nvh_type & NV_TYPE_MASK) {
251214501Srpaulo		case NV_TYPE_INT8:
252214501Srpaulo		case NV_TYPE_UINT8:
253214501Srpaulo			if (vsize == 0)
254214501Srpaulo				vsize = 1;
255214501Srpaulo			/* FALLTHOUGH */
256214501Srpaulo		case NV_TYPE_INT16:
257214501Srpaulo		case NV_TYPE_UINT16:
258214501Srpaulo			if (vsize == 0)
259214501Srpaulo				vsize = 2;
260214501Srpaulo			/* FALLTHOUGH */
261214501Srpaulo		case NV_TYPE_INT32:
262214501Srpaulo		case NV_TYPE_UINT32:
263214501Srpaulo			if (vsize == 0)
264214501Srpaulo				vsize = 4;
265214501Srpaulo			/* FALLTHOUGH */
266214501Srpaulo		case NV_TYPE_INT64:
267214501Srpaulo		case NV_TYPE_UINT64:
268214501Srpaulo			if (vsize == 0)
269214501Srpaulo				vsize = 8;
270214501Srpaulo			if (dsize != vsize) {
271214501Srpaulo				error = EINVAL;
272214501Srpaulo				break;
273214501Srpaulo			}
274214501Srpaulo			break;
275214501Srpaulo		case NV_TYPE_INT8_ARRAY:
276214501Srpaulo		case NV_TYPE_UINT8_ARRAY:
277214501Srpaulo			break;
278214501Srpaulo		case NV_TYPE_INT16_ARRAY:
279214501Srpaulo		case NV_TYPE_UINT16_ARRAY:
280214501Srpaulo			if (vsize == 0)
281214501Srpaulo				vsize = 2;
282214501Srpaulo			/* FALLTHOUGH */
283214501Srpaulo		case NV_TYPE_INT32_ARRAY:
284214501Srpaulo		case NV_TYPE_UINT32_ARRAY:
285214501Srpaulo			if (vsize == 0)
286214501Srpaulo				vsize = 4;
287214501Srpaulo			/* FALLTHOUGH */
288214501Srpaulo		case NV_TYPE_INT64_ARRAY:
289214501Srpaulo		case NV_TYPE_UINT64_ARRAY:
290214501Srpaulo			if (vsize == 0)
291214501Srpaulo				vsize = 8;
292214501Srpaulo			if ((dsize % vsize) != 0) {
293281806Srpaulo				error = EINVAL;
294214501Srpaulo				break;
295214501Srpaulo			}
296214501Srpaulo			break;
297214501Srpaulo		case NV_TYPE_STRING:
298214501Srpaulo			data = NVH_DATA(nvh);
299214501Srpaulo			if (data[dsize - 1] != '\0') {
300214501Srpaulo				error = EINVAL;
301214501Srpaulo				break;
302214501Srpaulo			}
303214501Srpaulo			if (strlen((char *)data) != dsize - 1) {
304214501Srpaulo				error = EINVAL;
305214501Srpaulo				break;
306214501Srpaulo			}
307214501Srpaulo			break;
308214501Srpaulo		default:
309214501Srpaulo			assert(!"invalid condition");
310214501Srpaulo		}
311214501Srpaulo		if (error != 0)
312214501Srpaulo			break;
313214501Srpaulo		ptr += NVH_SIZE(nvh);
314214501Srpaulo		size -= NVH_SIZE(nvh);
315214501Srpaulo	}
316214501Srpaulo	if (error != 0) {
317214501Srpaulo		errno = error;
318214501Srpaulo		if (nv->nv_error == 0)
319214501Srpaulo			nv->nv_error = error;
320214501Srpaulo		return (-1);
321214501Srpaulo	}
322214501Srpaulo	if (extrap != NULL)
323214501Srpaulo		*extrap = size;
324214501Srpaulo	return (0);
325214501Srpaulo}
326214501Srpaulo
327214501Srpaulo/*
328214501Srpaulo * Convert the given nv structure to network byte order and return ebuf
329214501Srpaulo * structure.
330214501Srpaulo */
331214501Srpaulostruct ebuf *
332214501Srpaulonv_hton(struct nv *nv)
333281806Srpaulo{
334281806Srpaulo	struct nvhdr *nvh;
335281806Srpaulo	unsigned char *ptr;
336281806Srpaulo	size_t size;
337281806Srpaulo
338281806Srpaulo	NV_CHECK(nv);
339281806Srpaulo	assert(nv->nv_error == 0);
340214501Srpaulo
341214501Srpaulo	ptr = ebuf_data(nv->nv_ebuf, &size);
342214501Srpaulo	while (size > 0) {
343214501Srpaulo		/*
344214501Srpaulo		 * Minimum size at this point is size of nvhdr structure,
345214501Srpaulo		 * one character long name plus terminating '\0'.
346214501Srpaulo		 */
347214501Srpaulo		assert(size >= sizeof(*nvh) + 2);
348214501Srpaulo		nvh = (struct nvhdr *)ptr;
349214501Srpaulo		assert(NVH_SIZE(nvh) <= size);
350214501Srpaulo		nv_swap(nvh, false);
351214501Srpaulo		ptr += NVH_SIZE(nvh);
352214501Srpaulo		size -= NVH_SIZE(nvh);
353214501Srpaulo	}
354214501Srpaulo
355214501Srpaulo	return (nv->nv_ebuf);
356214501Srpaulo}
357214501Srpaulo
358214501Srpaulo/*
359214501Srpaulo * Create nv structure based on ebuf received from the network.
360214501Srpaulo */
361214501Srpaulostruct nv *
362214501Srpaulonv_ntoh(struct ebuf *eb)
363289549Srpaulo{
364289549Srpaulo	struct nv *nv;
365289549Srpaulo	size_t extra;
366289549Srpaulo	int rerrno;
367289549Srpaulo
368289549Srpaulo	assert(eb != NULL);
369289549Srpaulo
370289549Srpaulo	nv = malloc(sizeof(*nv));
371289549Srpaulo	if (nv == NULL)
372289549Srpaulo		return (NULL);
373289549Srpaulo	nv->nv_error = 0;
374289549Srpaulo	nv->nv_ebuf = eb;
375289549Srpaulo	nv->nv_magic = NV_MAGIC;
376214501Srpaulo
377214501Srpaulo	if (nv_validate(nv, &extra) < 0) {
378214501Srpaulo		rerrno = errno;
379214501Srpaulo		nv->nv_magic = 0;
380214501Srpaulo		free(nv);
381214501Srpaulo		errno = rerrno;
382214501Srpaulo		return (NULL);
383214501Srpaulo	}
384214501Srpaulo	/*
385214501Srpaulo	 * Remove extra zeros at the end of the buffer.
386214501Srpaulo	 */
387214501Srpaulo	ebuf_del_tail(eb, extra);
388214501Srpaulo
389214501Srpaulo	return (nv);
390214501Srpaulo}
391214501Srpaulo
392214501Srpaulo#define	NV_DEFINE_ADD(type, TYPE)					\
393214501Srpaulovoid									\
394214501Srpaulonv_add_##type(struct nv *nv, type##_t value, const char *namefmt, ...)	\
395214501Srpaulo{									\
396214501Srpaulo	va_list nameap;							\
397214501Srpaulo									\
398214501Srpaulo	va_start(nameap, namefmt);					\
399214501Srpaulo	nv_addv(nv, (unsigned char *)&value, sizeof(value),		\
400214501Srpaulo	    NV_TYPE_##TYPE, namefmt, nameap);				\
401214501Srpaulo	va_end(nameap);							\
402214501Srpaulo}
403214501Srpaulo
404214501SrpauloNV_DEFINE_ADD(int8, INT8)
405214501SrpauloNV_DEFINE_ADD(uint8, UINT8)
406214501SrpauloNV_DEFINE_ADD(int16, INT16)
407214501SrpauloNV_DEFINE_ADD(uint16, UINT16)
408214501SrpauloNV_DEFINE_ADD(int32, INT32)
409281806SrpauloNV_DEFINE_ADD(uint32, UINT32)
410214501SrpauloNV_DEFINE_ADD(int64, INT64)
411214501SrpauloNV_DEFINE_ADD(uint64, UINT64)
412214501Srpaulo
413214501Srpaulo#undef	NV_DEFINE_ADD
414214501Srpaulo
415214501Srpaulo#define	NV_DEFINE_ADD_ARRAY(type, TYPE)					\
416214501Srpaulovoid									\
417214501Srpaulonv_add_##type##_array(struct nv *nv, const type##_t *value,		\
418214501Srpaulo    size_t nsize, const char *namefmt, ...)				\
419214501Srpaulo{									\
420214501Srpaulo	va_list nameap;							\
421214501Srpaulo									\
422252726Srpaulo	va_start(nameap, namefmt);					\
423252726Srpaulo	nv_addv(nv, (const unsigned char *)value,			\
424252726Srpaulo	    sizeof(value[0]) * nsize, NV_TYPE_##TYPE##_ARRAY, namefmt,	\
425252726Srpaulo	    nameap);							\
426252726Srpaulo	va_end(nameap);							\
427252726Srpaulo}
428214501Srpaulo
429214501SrpauloNV_DEFINE_ADD_ARRAY(int8, INT8)
430281806SrpauloNV_DEFINE_ADD_ARRAY(uint8, UINT8)
431281806SrpauloNV_DEFINE_ADD_ARRAY(int16, INT16)
432281806SrpauloNV_DEFINE_ADD_ARRAY(uint16, UINT16)
433281806SrpauloNV_DEFINE_ADD_ARRAY(int32, INT32)
434281806SrpauloNV_DEFINE_ADD_ARRAY(uint32, UINT32)
435281806SrpauloNV_DEFINE_ADD_ARRAY(int64, INT64)
436281806SrpauloNV_DEFINE_ADD_ARRAY(uint64, UINT64)
437281806Srpaulo
438281806Srpaulo#undef	NV_DEFINE_ADD_ARRAY
439281806Srpaulo
440214501Srpaulovoid
441214501Srpaulonv_add_string(struct nv *nv, const char *value, const char *namefmt, ...)
442214501Srpaulo{
443214501Srpaulo	va_list nameap;
444214501Srpaulo	size_t size;
445214501Srpaulo
446214501Srpaulo	size = strlen(value) + 1;
447214501Srpaulo
448214501Srpaulo	va_start(nameap, namefmt);
449214501Srpaulo	nv_addv(nv, (const unsigned char *)value, size, NV_TYPE_STRING,
450214501Srpaulo	    namefmt, nameap);
451214501Srpaulo	va_end(nameap);
452214501Srpaulo}
453214501Srpaulo
454214501Srpaulovoid
455214501Srpaulonv_add_stringf(struct nv *nv, const char *name, const char *valuefmt, ...)
456214501Srpaulo{
457214501Srpaulo	va_list valueap;
458214501Srpaulo
459214501Srpaulo	va_start(valueap, valuefmt);
460214501Srpaulo	nv_add_stringv(nv, name, valuefmt, valueap);
461214501Srpaulo	va_end(valueap);
462214501Srpaulo}
463214501Srpaulo
464214501Srpaulovoid
465214501Srpaulonv_add_stringv(struct nv *nv, const char *name, const char *valuefmt,
466214501Srpaulo    va_list valueap)
467214501Srpaulo{
468214501Srpaulo	char *value;
469214501Srpaulo	ssize_t size;
470214501Srpaulo
471214501Srpaulo	size = vasprintf(&value, valuefmt, valueap);
472214501Srpaulo	if (size < 0) {
473214501Srpaulo		if (nv->nv_error == 0)
474214501Srpaulo			nv->nv_error = ENOMEM;
475214501Srpaulo		return;
476214501Srpaulo	}
477214501Srpaulo	size++;
478214501Srpaulo	nv_add(nv, (const unsigned char *)value, size, NV_TYPE_STRING, name);
479214501Srpaulo	free(value);
480214501Srpaulo}
481214501Srpaulo
482214501Srpaulo#define	NV_DEFINE_GET(type, TYPE)					\
483214501Srpaulotype##_t								\
484214501Srpaulonv_get_##type(struct nv *nv, const char *namefmt, ...)			\
485214501Srpaulo{									\
486214501Srpaulo	struct nvhdr *nvh;						\
487214501Srpaulo	va_list nameap;							\
488214501Srpaulo	type##_t value;							\
489214501Srpaulo									\
490214501Srpaulo	va_start(nameap, namefmt);					\
491214501Srpaulo	nvh = nv_find(nv, NV_TYPE_##TYPE, namefmt, nameap);		\
492214501Srpaulo	va_end(nameap);							\
493214501Srpaulo	if (nvh == NULL)						\
494214501Srpaulo		return (0);						\
495214501Srpaulo	assert((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST);	\
496214501Srpaulo	assert(sizeof(value) == nvh->nvh_dsize);			\
497214501Srpaulo	bcopy(NVH_DATA(nvh), &value, sizeof(value));			\
498214501Srpaulo									\
499214501Srpaulo	return (value);							\
500214501Srpaulo}
501214501Srpaulo
502214501SrpauloNV_DEFINE_GET(int8, INT8)
503214501SrpauloNV_DEFINE_GET(uint8, UINT8)
504214501SrpauloNV_DEFINE_GET(int16, INT16)
505214501SrpauloNV_DEFINE_GET(uint16, UINT16)
506214501SrpauloNV_DEFINE_GET(int32, INT32)
507214501SrpauloNV_DEFINE_GET(uint32, UINT32)
508214501SrpauloNV_DEFINE_GET(int64, INT64)
509214501SrpauloNV_DEFINE_GET(uint64, UINT64)
510214501Srpaulo
511214501Srpaulo#undef	NV_DEFINE_GET
512214501Srpaulo
513214501Srpaulo#define	NV_DEFINE_GET_ARRAY(type, TYPE)					\
514214501Srpauloconst type##_t *							\
515214501Srpaulonv_get_##type##_array(struct nv *nv, size_t *sizep,			\
516214501Srpaulo    const char *namefmt, ...)						\
517214501Srpaulo{									\
518214501Srpaulo	struct nvhdr *nvh;						\
519214501Srpaulo	va_list nameap;							\
520214501Srpaulo									\
521214501Srpaulo	va_start(nameap, namefmt);					\
522214501Srpaulo	nvh = nv_find(nv, NV_TYPE_##TYPE##_ARRAY, namefmt, nameap);	\
523214501Srpaulo	va_end(nameap);							\
524214501Srpaulo	if (nvh == NULL)						\
525214501Srpaulo		return (NULL);						\
526214501Srpaulo	assert((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST);	\
527214501Srpaulo	assert((nvh->nvh_dsize % sizeof(type##_t)) == 0);		\
528214501Srpaulo	if (sizep != NULL)						\
529214501Srpaulo		*sizep = nvh->nvh_dsize / sizeof(type##_t);		\
530214501Srpaulo	return ((type##_t *)(void *)NVH_DATA(nvh));			\
531214501Srpaulo}
532214501Srpaulo
533214501SrpauloNV_DEFINE_GET_ARRAY(int8, INT8)
534214501SrpauloNV_DEFINE_GET_ARRAY(uint8, UINT8)
535214501SrpauloNV_DEFINE_GET_ARRAY(int16, INT16)
536214501SrpauloNV_DEFINE_GET_ARRAY(uint16, UINT16)
537214501SrpauloNV_DEFINE_GET_ARRAY(int32, INT32)
538214501SrpauloNV_DEFINE_GET_ARRAY(uint32, UINT32)
539214501SrpauloNV_DEFINE_GET_ARRAY(int64, INT64)
540214501SrpauloNV_DEFINE_GET_ARRAY(uint64, UINT64)
541214501Srpaulo
542214501Srpaulo#undef	NV_DEFINE_GET_ARRAY
543214501Srpaulo
544214501Srpauloconst char *
545214501Srpaulonv_get_string(struct nv *nv, const char *namefmt, ...)
546214501Srpaulo{
547214501Srpaulo	struct nvhdr *nvh;
548214501Srpaulo	va_list nameap;
549214501Srpaulo	char *str;
550214501Srpaulo
551214501Srpaulo	va_start(nameap, namefmt);
552214501Srpaulo	nvh = nv_find(nv, NV_TYPE_STRING, namefmt, nameap);
553214501Srpaulo	va_end(nameap);
554214501Srpaulo	if (nvh == NULL)
555214501Srpaulo		return (NULL);
556214501Srpaulo	assert((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST);
557214501Srpaulo	assert(nvh->nvh_dsize >= 1);
558214501Srpaulo	str = NVH_DATA(nvh);
559214501Srpaulo	assert(str[nvh->nvh_dsize - 1] == '\0');
560214501Srpaulo	assert(strlen(str) == nvh->nvh_dsize - 1);
561214501Srpaulo	return (str);
562214501Srpaulo}
563214501Srpaulo
564214501Srpaulo/*
565214501Srpaulo * Dump content of the nv structure.
566214501Srpaulo */
567214501Srpaulovoid
568214501Srpaulonv_dump(struct nv *nv)
569214501Srpaulo{
570214501Srpaulo	struct nvhdr *nvh;
571214501Srpaulo	unsigned char *data, *ptr;
572214501Srpaulo	size_t dsize, size;
573214501Srpaulo	unsigned int ii;
574214501Srpaulo	bool swap;
575214501Srpaulo
576214501Srpaulo	if (nv_validate(nv, NULL) < 0) {
577214501Srpaulo		printf("error: %d\n", errno);
578214501Srpaulo		return;
579214501Srpaulo	}
580214501Srpaulo
581214501Srpaulo	NV_CHECK(nv);
582214501Srpaulo	assert(nv->nv_error == 0);
583214501Srpaulo
584214501Srpaulo	ptr = ebuf_data(nv->nv_ebuf, &size);
585214501Srpaulo	while (size > 0) {
586214501Srpaulo		assert(size >= sizeof(*nvh) + 2);
587214501Srpaulo		nvh = (struct nvhdr *)ptr;
588214501Srpaulo		assert(size >= NVH_SIZE(nvh));
589214501Srpaulo		swap = ((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_NETWORK);
590214501Srpaulo		dsize = NVH_DSIZE(nvh);
591214501Srpaulo		data = NVH_DATA(nvh);
592214501Srpaulo		printf("  %s", nvh->nvh_name);
593214501Srpaulo		switch (nvh->nvh_type & NV_TYPE_MASK) {
594214501Srpaulo		case NV_TYPE_INT8:
595214501Srpaulo			printf("(int8): %jd", (intmax_t)(*(int8_t *)data));
596			break;
597		case NV_TYPE_UINT8:
598			printf("(uint8): %ju", (uintmax_t)(*(uint8_t *)data));
599			break;
600		case NV_TYPE_INT16:
601			printf("(int16): %jd", swap ?
602			    (intmax_t)le16toh(*(int16_t *)(void *)data) :
603			    (intmax_t)*(int16_t *)(void *)data);
604			break;
605		case NV_TYPE_UINT16:
606			printf("(uint16): %ju", swap ?
607			    (uintmax_t)le16toh(*(uint16_t *)(void *)data) :
608			    (uintmax_t)*(uint16_t *)(void *)data);
609			break;
610		case NV_TYPE_INT32:
611			printf("(int32): %jd", swap ?
612			    (intmax_t)le32toh(*(int32_t *)(void *)data) :
613			    (intmax_t)*(int32_t *)(void *)data);
614			break;
615		case NV_TYPE_UINT32:
616			printf("(uint32): %ju", swap ?
617			    (uintmax_t)le32toh(*(uint32_t *)(void *)data) :
618			    (uintmax_t)*(uint32_t *)(void *)data);
619			break;
620		case NV_TYPE_INT64:
621			printf("(int64): %jd", swap ?
622			    (intmax_t)le64toh(*(int64_t *)(void *)data) :
623			    (intmax_t)*(int64_t *)(void *)data);
624			break;
625		case NV_TYPE_UINT64:
626			printf("(uint64): %ju", swap ?
627			    (uintmax_t)le64toh(*(uint64_t *)(void *)data) :
628			    (uintmax_t)*(uint64_t *)(void *)data);
629			break;
630		case NV_TYPE_INT8_ARRAY:
631			printf("(int8 array):");
632			for (ii = 0; ii < dsize; ii++)
633				printf(" %jd", (intmax_t)((int8_t *)data)[ii]);
634			break;
635		case NV_TYPE_UINT8_ARRAY:
636			printf("(uint8 array):");
637			for (ii = 0; ii < dsize; ii++)
638				printf(" %ju", (uintmax_t)((uint8_t *)data)[ii]);
639			break;
640		case NV_TYPE_INT16_ARRAY:
641			printf("(int16 array):");
642			for (ii = 0; ii < dsize / 2; ii++) {
643				printf(" %jd", swap ?
644				    (intmax_t)le16toh(((int16_t *)(void *)data)[ii]) :
645				    (intmax_t)((int16_t *)(void *)data)[ii]);
646			}
647			break;
648		case NV_TYPE_UINT16_ARRAY:
649			printf("(uint16 array):");
650			for (ii = 0; ii < dsize / 2; ii++) {
651				printf(" %ju", swap ?
652				    (uintmax_t)le16toh(((uint16_t *)(void *)data)[ii]) :
653				    (uintmax_t)((uint16_t *)(void *)data)[ii]);
654			}
655			break;
656		case NV_TYPE_INT32_ARRAY:
657			printf("(int32 array):");
658			for (ii = 0; ii < dsize / 4; ii++) {
659				printf(" %jd", swap ?
660				    (intmax_t)le32toh(((int32_t *)(void *)data)[ii]) :
661				    (intmax_t)((int32_t *)(void *)data)[ii]);
662			}
663			break;
664		case NV_TYPE_UINT32_ARRAY:
665			printf("(uint32 array):");
666			for (ii = 0; ii < dsize / 4; ii++) {
667				printf(" %ju", swap ?
668				    (uintmax_t)le32toh(((uint32_t *)(void *)data)[ii]) :
669				    (uintmax_t)((uint32_t *)(void *)data)[ii]);
670			}
671			break;
672		case NV_TYPE_INT64_ARRAY:
673			printf("(int64 array):");
674			for (ii = 0; ii < dsize / 8; ii++) {
675				printf(" %ju", swap ?
676				    (uintmax_t)le64toh(((uint64_t *)(void *)data)[ii]) :
677				    (uintmax_t)((uint64_t *)(void *)data)[ii]);
678			}
679			break;
680		case NV_TYPE_UINT64_ARRAY:
681			printf("(uint64 array):");
682			for (ii = 0; ii < dsize / 8; ii++) {
683				printf(" %ju", swap ?
684				    (uintmax_t)le64toh(((uint64_t *)(void *)data)[ii]) :
685				    (uintmax_t)((uint64_t *)(void *)data)[ii]);
686			}
687			break;
688		case NV_TYPE_STRING:
689			printf("(string): %s", (char *)data);
690			break;
691		default:
692			assert(!"invalid condition");
693		}
694		printf("\n");
695		ptr += NVH_SIZE(nvh);
696		size -= NVH_SIZE(nvh);
697	}
698}
699
700/*
701 * Local routines below.
702 */
703
704static void
705nv_add(struct nv *nv, const unsigned char *value, size_t vsize, int type,
706    const char *name)
707{
708	static unsigned char align[7];
709	struct nvhdr *nvh;
710	size_t namesize;
711
712	if (nv == NULL) {
713		errno = ENOMEM;
714		return;
715	}
716
717	NV_CHECK(nv);
718
719	namesize = strlen(name) + 1;
720
721	nvh = malloc(sizeof(*nvh) + roundup2(namesize, 8));
722	if (nvh == NULL) {
723		if (nv->nv_error == 0)
724			nv->nv_error = ENOMEM;
725		return;
726	}
727	nvh->nvh_type = NV_ORDER_HOST | type;
728	nvh->nvh_namesize = (uint8_t)namesize;
729	nvh->nvh_dsize = (uint32_t)vsize;
730	bcopy(name, nvh->nvh_name, namesize);
731
732	/* Add header first. */
733	if (ebuf_add_tail(nv->nv_ebuf, nvh, NVH_HSIZE(nvh)) < 0) {
734		assert(errno != 0);
735		if (nv->nv_error == 0)
736			nv->nv_error = errno;
737		free(nvh);
738		return;
739	}
740	free(nvh);
741	/* Add the actual data. */
742	if (ebuf_add_tail(nv->nv_ebuf, value, vsize) < 0) {
743		assert(errno != 0);
744		if (nv->nv_error == 0)
745			nv->nv_error = errno;
746		return;
747	}
748	/* Align the data (if needed). */
749	vsize = roundup2(vsize, 8) - vsize;
750	if (vsize == 0)
751		return;
752	assert(vsize > 0 && vsize <= sizeof(align));
753	if (ebuf_add_tail(nv->nv_ebuf, align, vsize) < 0) {
754		assert(errno != 0);
755		if (nv->nv_error == 0)
756			nv->nv_error = errno;
757		return;
758	}
759}
760
761static void
762nv_addv(struct nv *nv, const unsigned char *value, size_t vsize, int type,
763    const char *namefmt, va_list nameap)
764{
765	char name[255];
766	size_t namesize;
767
768	namesize = vsnprintf(name, sizeof(name), namefmt, nameap);
769	assert(namesize > 0 && namesize < sizeof(name));
770
771	nv_add(nv, value, vsize, type, name);
772}
773
774static struct nvhdr *
775nv_find(struct nv *nv, int type, const char *namefmt, va_list nameap)
776{
777	char name[255];
778	struct nvhdr *nvh;
779	unsigned char *ptr;
780	size_t size, namesize;
781
782	if (nv == NULL) {
783		errno = ENOMEM;
784		return (NULL);
785	}
786
787	NV_CHECK(nv);
788
789	namesize = vsnprintf(name, sizeof(name), namefmt, nameap);
790	assert(namesize > 0 && namesize < sizeof(name));
791	namesize++;
792
793	ptr = ebuf_data(nv->nv_ebuf, &size);
794	while (size > 0) {
795		assert(size >= sizeof(*nvh) + 2);
796		nvh = (struct nvhdr *)ptr;
797		assert(size >= NVH_SIZE(nvh));
798		nv_swap(nvh, true);
799		if (strcmp(nvh->nvh_name, name) == 0) {
800			if ((nvh->nvh_type & NV_TYPE_MASK) != type) {
801				errno = EINVAL;
802				if (nv->nv_error == 0)
803					nv->nv_error = EINVAL;
804				return (NULL);
805			}
806			return (nvh);
807		}
808		ptr += NVH_SIZE(nvh);
809		size -= NVH_SIZE(nvh);
810	}
811	errno = ENOENT;
812	if (nv->nv_error == 0)
813		nv->nv_error = ENOENT;
814	return (NULL);
815}
816
817static void
818nv_swap(struct nvhdr *nvh, bool tohost)
819{
820	unsigned char *data, *end, *p;
821	size_t vsize;
822
823	data = NVH_DATA(nvh);
824	if (tohost) {
825		if ((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST)
826			return;
827		nvh->nvh_dsize = le32toh(nvh->nvh_dsize);
828		end = data + nvh->nvh_dsize;
829		nvh->nvh_type &= ~NV_ORDER_MASK;
830		nvh->nvh_type |= NV_ORDER_HOST;
831	} else {
832		if ((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_NETWORK)
833			return;
834		end = data + nvh->nvh_dsize;
835		nvh->nvh_dsize = htole32(nvh->nvh_dsize);
836		nvh->nvh_type &= ~NV_ORDER_MASK;
837		nvh->nvh_type |= NV_ORDER_NETWORK;
838	}
839
840	vsize = 0;
841
842	switch (nvh->nvh_type & NV_TYPE_MASK) {
843	case NV_TYPE_INT8:
844	case NV_TYPE_UINT8:
845	case NV_TYPE_INT8_ARRAY:
846	case NV_TYPE_UINT8_ARRAY:
847		break;
848	case NV_TYPE_INT16:
849	case NV_TYPE_UINT16:
850	case NV_TYPE_INT16_ARRAY:
851	case NV_TYPE_UINT16_ARRAY:
852		if (vsize == 0)
853			vsize = 2;
854		/* FALLTHOUGH */
855	case NV_TYPE_INT32:
856	case NV_TYPE_UINT32:
857	case NV_TYPE_INT32_ARRAY:
858	case NV_TYPE_UINT32_ARRAY:
859		if (vsize == 0)
860			vsize = 4;
861		/* FALLTHOUGH */
862	case NV_TYPE_INT64:
863	case NV_TYPE_UINT64:
864	case NV_TYPE_INT64_ARRAY:
865	case NV_TYPE_UINT64_ARRAY:
866		if (vsize == 0)
867			vsize = 8;
868		for (p = data; p < end; p += vsize) {
869			if (tohost) {
870				switch (vsize) {
871				case 2:
872					*(uint16_t *)(void *)p =
873					    le16toh(*(uint16_t *)(void *)p);
874					break;
875				case 4:
876					*(uint32_t *)(void *)p =
877					    le32toh(*(uint32_t *)(void *)p);
878					break;
879				case 8:
880					*(uint64_t *)(void *)p =
881					    le64toh(*(uint64_t *)(void *)p);
882					break;
883				default:
884					assert(!"invalid condition");
885				}
886			} else {
887				switch (vsize) {
888				case 2:
889					*(uint16_t *)(void *)p =
890					    htole16(*(uint16_t *)(void *)p);
891					break;
892				case 4:
893					*(uint32_t *)(void *)p =
894					    htole32(*(uint32_t *)(void *)p);
895					break;
896				case 8:
897					*(uint64_t *)(void *)p =
898					    htole64(*(uint64_t *)(void *)p);
899					break;
900				default:
901					assert(!"invalid condition");
902				}
903			}
904		}
905		break;
906	case NV_TYPE_STRING:
907		break;
908	default:
909		assert(!"unrecognized type");
910	}
911}
912