xref: /freebsd/sbin/hastd/nv.c (revision 05248206f720394d95c2a7475429311df670a2e9)
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/cdefs.h>
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
88  struct nv {
89  	int	nv_magic;
90  	int	nv_error;
91  	struct ebuf *nv_ebuf;
92  };
93  
94  struct 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  
114  static void nv_add(struct nv *nv, const unsigned char *value, size_t vsize,
115      int type, const char *name);
116  static void nv_addv(struct nv *nv, const unsigned char *value, size_t vsize,
117      int type, const char *namefmt, va_list nameap);
118  static struct nvhdr *nv_find(struct nv *nv, int type, const char *namefmt,
119      va_list nameap);
120  static 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   */
126  struct nv *
127  nv_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   */
147  void
148  nv_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   */
164  int
165  nv_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   */
179  int
180  nv_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   */
198  int
199  nv_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   */
342  struct ebuf *
343  nv_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   */
372  struct nv *
373  nv_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)					\
404  void									\
405  nv_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  
415  NV_DEFINE_ADD(int8, INT8)
416  NV_DEFINE_ADD(uint8, UINT8)
417  NV_DEFINE_ADD(int16, INT16)
418  NV_DEFINE_ADD(uint16, UINT16)
419  NV_DEFINE_ADD(int32, INT32)
420  NV_DEFINE_ADD(uint32, UINT32)
421  NV_DEFINE_ADD(int64, INT64)
422  NV_DEFINE_ADD(uint64, UINT64)
423  
424  #undef	NV_DEFINE_ADD
425  
426  #define	NV_DEFINE_ADD_ARRAY(type, TYPE)					\
427  void									\
428  nv_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  
440  NV_DEFINE_ADD_ARRAY(int8, INT8)
441  NV_DEFINE_ADD_ARRAY(uint8, UINT8)
442  NV_DEFINE_ADD_ARRAY(int16, INT16)
443  NV_DEFINE_ADD_ARRAY(uint16, UINT16)
444  NV_DEFINE_ADD_ARRAY(int32, INT32)
445  NV_DEFINE_ADD_ARRAY(uint32, UINT32)
446  NV_DEFINE_ADD_ARRAY(int64, INT64)
447  NV_DEFINE_ADD_ARRAY(uint64, UINT64)
448  
449  #undef	NV_DEFINE_ADD_ARRAY
450  
451  void
452  nv_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  
465  void
466  nv_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  
475  void
476  nv_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)					\
494  type##_t								\
495  nv_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  
513  NV_DEFINE_GET(int8, INT8)
514  NV_DEFINE_GET(uint8, UINT8)
515  NV_DEFINE_GET(int16, INT16)
516  NV_DEFINE_GET(uint16, UINT16)
517  NV_DEFINE_GET(int32, INT32)
518  NV_DEFINE_GET(uint32, UINT32)
519  NV_DEFINE_GET(int64, INT64)
520  NV_DEFINE_GET(uint64, UINT64)
521  
522  #undef	NV_DEFINE_GET
523  
524  #define	NV_DEFINE_GET_ARRAY(type, TYPE)					\
525  const type##_t *							\
526  nv_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  
544  NV_DEFINE_GET_ARRAY(int8, INT8)
545  NV_DEFINE_GET_ARRAY(uint8, UINT8)
546  NV_DEFINE_GET_ARRAY(int16, INT16)
547  NV_DEFINE_GET_ARRAY(uint16, UINT16)
548  NV_DEFINE_GET_ARRAY(int32, INT32)
549  NV_DEFINE_GET_ARRAY(uint32, UINT32)
550  NV_DEFINE_GET_ARRAY(int64, INT64)
551  NV_DEFINE_GET_ARRAY(uint64, UINT64)
552  
553  #undef	NV_DEFINE_GET_ARRAY
554  
555  const char *
556  nv_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  
575  static bool
576  nv_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  
595  bool
596  nv_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  
608  void
609  nv_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   */
621  void
622  nv_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  
758  static void
759  nv_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  
815  static void
816  nv_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  
828  static struct nvhdr *
829  nv_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  
872  static void
873  nv_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