]> CyberLeo.Net >> Repos - FreeBSD/releng/8.2.git/blob - sbin/hastd/nv.c
MFC r217548:
[FreeBSD/releng/8.2.git] / sbin / hastd / nv.c
1 /*-
2  * Copyright (c) 2009-2010 The FreeBSD Foundation
3  * All rights reserved.
4  *
5  * This software was developed by Pawel Jakub Dawidek under sponsorship from
6  * the FreeBSD Foundation.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32
33 #include <sys/param.h>
34 #include <sys/endian.h>
35
36 #include <assert.h>
37 #include <bitstring.h>
38 #include <errno.h>
39 #include <stdarg.h>
40 #include <stdbool.h>
41 #include <stdint.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <unistd.h>
45
46 #include <ebuf.h>
47 #include <nv.h>
48
49 #define NV_TYPE_NONE            0
50
51 #define NV_TYPE_INT8            1
52 #define NV_TYPE_UINT8           2
53 #define NV_TYPE_INT16           3
54 #define NV_TYPE_UINT16          4
55 #define NV_TYPE_INT32           5
56 #define NV_TYPE_UINT32          6
57 #define NV_TYPE_INT64           7
58 #define NV_TYPE_UINT64          8
59 #define NV_TYPE_INT8_ARRAY      9
60 #define NV_TYPE_UINT8_ARRAY     10
61 #define NV_TYPE_INT16_ARRAY     11
62 #define NV_TYPE_UINT16_ARRAY    12
63 #define NV_TYPE_INT32_ARRAY     13
64 #define NV_TYPE_UINT32_ARRAY    14
65 #define NV_TYPE_INT64_ARRAY     15
66 #define NV_TYPE_UINT64_ARRAY    16
67 #define NV_TYPE_STRING          17
68
69 #define NV_TYPE_MASK            0x7f
70 #define NV_TYPE_FIRST           NV_TYPE_INT8
71 #define NV_TYPE_LAST            NV_TYPE_STRING
72
73 #define NV_ORDER_NETWORK        0x00
74 #define NV_ORDER_HOST           0x80
75
76 #define NV_ORDER_MASK           0x80
77
78 #define NV_MAGIC        0xaea1e
79 struct nv {
80         int     nv_magic;
81         int     nv_error;
82         struct ebuf *nv_ebuf;
83 };
84
85 struct nvhdr {
86         uint8_t         nvh_type;
87         uint8_t         nvh_namesize;
88         uint32_t        nvh_dsize;
89         char            nvh_name[0];
90 } __packed;
91 #define NVH_DATA(nvh)   ((unsigned char *)nvh + NVH_HSIZE(nvh))
92 #define NVH_HSIZE(nvh)  \
93         (sizeof(struct nvhdr) + roundup2((nvh)->nvh_namesize, 8))
94 #define NVH_DSIZE(nvh)  \
95         (((nvh)->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST ?           \
96         (nvh)->nvh_dsize :                                              \
97         le32toh((nvh)->nvh_dsize))
98 #define NVH_SIZE(nvh)   (NVH_HSIZE(nvh) + roundup2(NVH_DSIZE(nvh), 8))
99
100 #define NV_CHECK(nv)    do {                                            \
101         assert((nv) != NULL);                                           \
102         assert((nv)->nv_magic == NV_MAGIC);                             \
103 } while (0)
104
105 static void nv_add(struct nv *nv, const unsigned char *value, size_t vsize,
106     int type, const char *name);
107 static void nv_addv(struct nv *nv, const unsigned char *value, size_t vsize,
108     int type, const char *namefmt, va_list nameap);
109 static struct nvhdr *nv_find(struct nv *nv, int type, const char *namefmt,
110     va_list nameap);
111 static void nv_swap(struct nvhdr *nvh, bool tohost);
112
113 /*
114  * Allocate and initialize new nv structure.
115  * Return NULL in case of malloc(3) failure.
116  */
117 struct nv *
118 nv_alloc(void)
119 {
120         struct nv *nv;
121
122         nv = malloc(sizeof(*nv));
123         if (nv == NULL)
124                 return (NULL);
125         nv->nv_ebuf = ebuf_alloc(0);
126         if (nv->nv_ebuf == NULL) {
127                 free(nv);
128                 return (NULL);
129         }
130         nv->nv_error = 0;
131         nv->nv_magic = NV_MAGIC;
132         return (nv);
133 }
134
135 /*
136  * Free the given nv structure.
137  */
138 void
139 nv_free(struct nv *nv)
140 {
141
142         if (nv == NULL)
143                 return;
144
145         NV_CHECK(nv);
146
147         nv->nv_magic = 0;
148         ebuf_free(nv->nv_ebuf);
149         free(nv);
150 }
151
152 /*
153  * Return error for the given nv structure.
154  */
155 int
156 nv_error(const struct nv *nv)
157 {
158
159         if (nv == NULL)
160                 return (ENOMEM);
161
162         NV_CHECK(nv);
163
164         return (nv->nv_error);
165 }
166
167 /*
168  * Set error for the given nv structure and return previous error.
169  */
170 int
171 nv_set_error(struct nv *nv, int error)
172 {
173         int preverr;
174
175         if (nv == NULL)
176                 return (ENOMEM);
177
178         NV_CHECK(nv);
179
180         preverr = nv->nv_error;
181         nv->nv_error = error;
182         return (preverr);
183 }
184
185 /*
186  * Validate correctness of the entire nv structure and all its elements.
187  * If extrap is not NULL, store number of extra bytes at the end of the buffer.
188  */
189 int
190 nv_validate(struct nv *nv, size_t *extrap)
191 {
192         struct nvhdr *nvh;
193         unsigned char *data, *ptr;
194         size_t dsize, size, vsize;
195         int error;
196
197         if (nv == NULL) {
198                 errno = ENOMEM;
199                 return (-1);
200         }
201
202         NV_CHECK(nv);
203         assert(nv->nv_error == 0);
204
205         /* TODO: Check that names are unique? */
206
207         error = 0;
208         ptr = ebuf_data(nv->nv_ebuf, &size);
209         while (size > 0) {
210                 /*
211                  * Zeros at the end of the buffer are acceptable.
212                  */
213                 if (ptr[0] == '\0')
214                         break;
215                 /*
216                  * Minimum size at this point is size of nvhdr structure, one
217                  * character long name plus terminating '\0'.
218                  */
219                 if (size < sizeof(*nvh) + 2) {
220                         error = EINVAL;
221                         break;
222                 }
223                 nvh = (struct nvhdr *)ptr;
224                 if (size < NVH_HSIZE(nvh)) {
225                         error = EINVAL;
226                         break;
227                 }
228                 if (nvh->nvh_name[nvh->nvh_namesize - 1] != '\0') {
229                         error = EINVAL;
230                         break;
231                 }
232                 if (strlen(nvh->nvh_name) !=
233                     (size_t)(nvh->nvh_namesize - 1)) {
234                         error = EINVAL;
235                         break;
236                 }
237                 if ((nvh->nvh_type & NV_TYPE_MASK) < NV_TYPE_FIRST ||
238                     (nvh->nvh_type & NV_TYPE_MASK) > NV_TYPE_LAST) {
239                         error = EINVAL;
240                         break;
241                 }
242                 dsize = NVH_DSIZE(nvh);
243                 if (dsize == 0) {
244                         error = EINVAL;
245                         break;
246                 }
247                 if (size < NVH_SIZE(nvh)) {
248                         error = EINVAL;
249                         break;
250                 }
251                 vsize = 0;
252                 switch (nvh->nvh_type & NV_TYPE_MASK) {
253                 case NV_TYPE_INT8:
254                 case NV_TYPE_UINT8:
255                         if (vsize == 0)
256                                 vsize = 1;
257                         /* FALLTHOUGH */
258                 case NV_TYPE_INT16:
259                 case NV_TYPE_UINT16:
260                         if (vsize == 0)
261                                 vsize = 2;
262                         /* FALLTHOUGH */
263                 case NV_TYPE_INT32:
264                 case NV_TYPE_UINT32:
265                         if (vsize == 0)
266                                 vsize = 4;
267                         /* FALLTHOUGH */
268                 case NV_TYPE_INT64:
269                 case NV_TYPE_UINT64:
270                         if (vsize == 0)
271                                 vsize = 8;
272                         if (dsize != vsize) {
273                                 error = EINVAL;
274                                 break;
275                         }
276                         break;
277                 case NV_TYPE_INT8_ARRAY:
278                 case NV_TYPE_UINT8_ARRAY:
279                         break;
280                 case NV_TYPE_INT16_ARRAY:
281                 case NV_TYPE_UINT16_ARRAY:
282                         if (vsize == 0)
283                                 vsize = 2;
284                         /* FALLTHOUGH */
285                 case NV_TYPE_INT32_ARRAY:
286                 case NV_TYPE_UINT32_ARRAY:
287                         if (vsize == 0)
288                                 vsize = 4;
289                         /* FALLTHOUGH */
290                 case NV_TYPE_INT64_ARRAY:
291                 case NV_TYPE_UINT64_ARRAY:
292                         if (vsize == 0)
293                                 vsize = 8;
294                         if ((dsize % vsize) != 0) {
295                                 error = EINVAL;
296                                 break;
297                         }
298                         break;
299                 case NV_TYPE_STRING:
300                         data = NVH_DATA(nvh);
301                         if (data[dsize - 1] != '\0') {
302                                 error = EINVAL;
303                                 break;
304                         }
305                         if (strlen((char *)data) != dsize - 1) {
306                                 error = EINVAL;
307                                 break;
308                         }
309                         break;
310                 default:
311                         assert(!"invalid condition");
312                 }
313                 if (error != 0)
314                         break;
315                 ptr += NVH_SIZE(nvh);
316                 size -= NVH_SIZE(nvh);
317         }
318         if (error != 0) {
319                 errno = error;
320                 if (nv->nv_error == 0)
321                         nv->nv_error = error;
322                 return (-1);
323         }
324         if (extrap != NULL)
325                 *extrap = size;
326         return (0);
327 }
328
329 /*
330  * Convert the given nv structure to network byte order and return ebuf
331  * structure.
332  */
333 struct ebuf *
334 nv_hton(struct nv *nv)
335 {
336         struct nvhdr *nvh;
337         unsigned char *ptr;
338         size_t size;
339
340         NV_CHECK(nv);
341         assert(nv->nv_error == 0);
342
343         ptr = ebuf_data(nv->nv_ebuf, &size);
344         while (size > 0) {
345                 /*
346                  * Minimum size at this point is size of nvhdr structure,
347                  * one character long name plus terminating '\0'.
348                  */
349                 assert(size >= sizeof(*nvh) + 2);
350                 nvh = (struct nvhdr *)ptr;
351                 assert(NVH_SIZE(nvh) <= size);
352                 nv_swap(nvh, false);
353                 ptr += NVH_SIZE(nvh);
354                 size -= NVH_SIZE(nvh);
355         }
356
357         return (nv->nv_ebuf);
358 }
359
360 /*
361  * Create nv structure based on ebuf received from the network.
362  */
363 struct nv *
364 nv_ntoh(struct ebuf *eb)
365 {
366         struct nv *nv;
367         size_t extra;
368         int rerrno;
369
370         assert(eb != NULL);
371
372         nv = malloc(sizeof(*nv));
373         if (nv == NULL)
374                 return (NULL);
375         nv->nv_error = 0;
376         nv->nv_ebuf = eb;
377         nv->nv_magic = NV_MAGIC;
378
379         if (nv_validate(nv, &extra) < 0) {
380                 rerrno = errno;
381                 nv->nv_magic = 0;
382                 free(nv);
383                 errno = rerrno;
384                 return (NULL);
385         }
386         /*
387          * Remove extra zeros at the end of the buffer.
388          */
389         ebuf_del_tail(eb, extra);
390
391         return (nv);
392 }
393
394 #define NV_DEFINE_ADD(type, TYPE)                                       \
395 void                                                                    \
396 nv_add_##type(struct nv *nv, type##_t value, const char *namefmt, ...)  \
397 {                                                                       \
398         va_list nameap;                                                 \
399                                                                         \
400         va_start(nameap, namefmt);                                      \
401         nv_addv(nv, (unsigned char *)&value, sizeof(value),             \
402             NV_TYPE_##TYPE, namefmt, nameap);                           \
403         va_end(nameap);                                                 \
404 }
405
406 NV_DEFINE_ADD(int8, INT8)
407 NV_DEFINE_ADD(uint8, UINT8)
408 NV_DEFINE_ADD(int16, INT16)
409 NV_DEFINE_ADD(uint16, UINT16)
410 NV_DEFINE_ADD(int32, INT32)
411 NV_DEFINE_ADD(uint32, UINT32)
412 NV_DEFINE_ADD(int64, INT64)
413 NV_DEFINE_ADD(uint64, UINT64)
414
415 #undef  NV_DEFINE_ADD
416
417 #define NV_DEFINE_ADD_ARRAY(type, TYPE)                                 \
418 void                                                                    \
419 nv_add_##type##_array(struct nv *nv, const type##_t *value,             \
420     size_t nsize, const char *namefmt, ...)                             \
421 {                                                                       \
422         va_list nameap;                                                 \
423                                                                         \
424         va_start(nameap, namefmt);                                      \
425         nv_addv(nv, (const unsigned char *)value,                       \
426             sizeof(value[0]) * nsize, NV_TYPE_##TYPE##_ARRAY, namefmt,  \
427             nameap);                                                    \
428         va_end(nameap);                                                 \
429 }
430
431 NV_DEFINE_ADD_ARRAY(int8, INT8)
432 NV_DEFINE_ADD_ARRAY(uint8, UINT8)
433 NV_DEFINE_ADD_ARRAY(int16, INT16)
434 NV_DEFINE_ADD_ARRAY(uint16, UINT16)
435 NV_DEFINE_ADD_ARRAY(int32, INT32)
436 NV_DEFINE_ADD_ARRAY(uint32, UINT32)
437 NV_DEFINE_ADD_ARRAY(int64, INT64)
438 NV_DEFINE_ADD_ARRAY(uint64, UINT64)
439
440 #undef  NV_DEFINE_ADD_ARRAY
441
442 void
443 nv_add_string(struct nv *nv, const char *value, const char *namefmt, ...)
444 {
445         va_list nameap;
446         size_t size;
447
448         size = strlen(value) + 1;
449
450         va_start(nameap, namefmt);
451         nv_addv(nv, (const unsigned char *)value, size, NV_TYPE_STRING,
452             namefmt, nameap);
453         va_end(nameap);
454 }
455
456 void
457 nv_add_stringf(struct nv *nv, const char *name, const char *valuefmt, ...)
458 {
459         va_list valueap;
460
461         va_start(valueap, valuefmt);
462         nv_add_stringv(nv, name, valuefmt, valueap);
463         va_end(valueap);
464 }
465
466 void
467 nv_add_stringv(struct nv *nv, const char *name, const char *valuefmt,
468     va_list valueap)
469 {
470         char *value;
471         ssize_t size;
472
473         size = vasprintf(&value, valuefmt, valueap);
474         if (size < 0) {
475                 if (nv->nv_error == 0)
476                         nv->nv_error = ENOMEM;
477                 return;
478         }
479         size++;
480         nv_add(nv, (const unsigned char *)value, size, NV_TYPE_STRING, name);
481         free(value);
482 }
483
484 #define NV_DEFINE_GET(type, TYPE)                                       \
485 type##_t                                                                \
486 nv_get_##type(struct nv *nv, const char *namefmt, ...)                  \
487 {                                                                       \
488         struct nvhdr *nvh;                                              \
489         va_list nameap;                                                 \
490         type##_t value;                                                 \
491                                                                         \
492         va_start(nameap, namefmt);                                      \
493         nvh = nv_find(nv, NV_TYPE_##TYPE, namefmt, nameap);             \
494         va_end(nameap);                                                 \
495         if (nvh == NULL)                                                \
496                 return (0);                                             \
497         assert((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST);       \
498         assert(sizeof(value) == nvh->nvh_dsize);                        \
499         bcopy(NVH_DATA(nvh), &value, sizeof(value));                    \
500                                                                         \
501         return (value);                                                 \
502 }
503
504 NV_DEFINE_GET(int8, INT8)
505 NV_DEFINE_GET(uint8, UINT8)
506 NV_DEFINE_GET(int16, INT16)
507 NV_DEFINE_GET(uint16, UINT16)
508 NV_DEFINE_GET(int32, INT32)
509 NV_DEFINE_GET(uint32, UINT32)
510 NV_DEFINE_GET(int64, INT64)
511 NV_DEFINE_GET(uint64, UINT64)
512
513 #undef  NV_DEFINE_GET
514
515 #define NV_DEFINE_GET_ARRAY(type, TYPE)                                 \
516 const type##_t *                                                        \
517 nv_get_##type##_array(struct nv *nv, size_t *sizep,                     \
518     const char *namefmt, ...)                                           \
519 {                                                                       \
520         struct nvhdr *nvh;                                              \
521         va_list nameap;                                                 \
522                                                                         \
523         va_start(nameap, namefmt);                                      \
524         nvh = nv_find(nv, NV_TYPE_##TYPE##_ARRAY, namefmt, nameap);     \
525         va_end(nameap);                                                 \
526         if (nvh == NULL)                                                \
527                 return (NULL);                                          \
528         assert((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST);       \
529         assert((nvh->nvh_dsize % sizeof(type##_t)) == 0);               \
530         if (sizep != NULL)                                              \
531                 *sizep = nvh->nvh_dsize / sizeof(type##_t);             \
532         return ((type##_t *)(void *)NVH_DATA(nvh));                     \
533 }
534
535 NV_DEFINE_GET_ARRAY(int8, INT8)
536 NV_DEFINE_GET_ARRAY(uint8, UINT8)
537 NV_DEFINE_GET_ARRAY(int16, INT16)
538 NV_DEFINE_GET_ARRAY(uint16, UINT16)
539 NV_DEFINE_GET_ARRAY(int32, INT32)
540 NV_DEFINE_GET_ARRAY(uint32, UINT32)
541 NV_DEFINE_GET_ARRAY(int64, INT64)
542 NV_DEFINE_GET_ARRAY(uint64, UINT64)
543
544 #undef  NV_DEFINE_GET_ARRAY
545
546 const char *
547 nv_get_string(struct nv *nv, const char *namefmt, ...)
548 {
549         struct nvhdr *nvh;
550         va_list nameap;
551         char *str;
552
553         va_start(nameap, namefmt);
554         nvh = nv_find(nv, NV_TYPE_STRING, namefmt, nameap);
555         va_end(nameap);
556         if (nvh == NULL)
557                 return (NULL);
558         assert((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST);
559         assert(nvh->nvh_dsize >= 1);
560         str = NVH_DATA(nvh);
561         assert(str[nvh->nvh_dsize - 1] == '\0');
562         assert(strlen(str) == nvh->nvh_dsize - 1);
563         return (str);
564 }
565
566 bool
567 nv_exists(struct nv *nv, const char *namefmt, ...)
568 {
569         struct nvhdr *nvh;
570         va_list nameap;
571         int snverror, serrno;
572
573         if (nv == NULL)
574                 return (false);
575
576         serrno = errno;
577         snverror = nv->nv_error;
578
579         va_start(nameap, namefmt);
580         nvh = nv_find(nv, NV_TYPE_NONE, namefmt, nameap);
581         va_end(nameap);
582
583         errno = serrno;
584         nv->nv_error = snverror;
585
586         return (nvh != NULL);
587 }
588
589 /*
590  * Dump content of the nv structure.
591  */
592 void
593 nv_dump(struct nv *nv)
594 {
595         struct nvhdr *nvh;
596         unsigned char *data, *ptr;
597         size_t dsize, size;
598         unsigned int ii;
599         bool swap;
600
601         if (nv_validate(nv, NULL) < 0) {
602                 printf("error: %d\n", errno);
603                 return;
604         }
605
606         NV_CHECK(nv);
607         assert(nv->nv_error == 0);
608
609         ptr = ebuf_data(nv->nv_ebuf, &size);
610         while (size > 0) {
611                 assert(size >= sizeof(*nvh) + 2);
612                 nvh = (struct nvhdr *)ptr;
613                 assert(size >= NVH_SIZE(nvh));
614                 swap = ((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_NETWORK);
615                 dsize = NVH_DSIZE(nvh);
616                 data = NVH_DATA(nvh);
617                 printf("  %s", nvh->nvh_name);
618                 switch (nvh->nvh_type & NV_TYPE_MASK) {
619                 case NV_TYPE_INT8:
620                         printf("(int8): %jd", (intmax_t)(*(int8_t *)data));
621                         break;
622                 case NV_TYPE_UINT8:
623                         printf("(uint8): %ju", (uintmax_t)(*(uint8_t *)data));
624                         break;
625                 case NV_TYPE_INT16:
626                         printf("(int16): %jd", swap ?
627                             (intmax_t)le16toh(*(int16_t *)(void *)data) :
628                             (intmax_t)*(int16_t *)(void *)data);
629                         break;
630                 case NV_TYPE_UINT16:
631                         printf("(uint16): %ju", swap ?
632                             (uintmax_t)le16toh(*(uint16_t *)(void *)data) :
633                             (uintmax_t)*(uint16_t *)(void *)data);
634                         break;
635                 case NV_TYPE_INT32:
636                         printf("(int32): %jd", swap ?
637                             (intmax_t)le32toh(*(int32_t *)(void *)data) :
638                             (intmax_t)*(int32_t *)(void *)data);
639                         break;
640                 case NV_TYPE_UINT32:
641                         printf("(uint32): %ju", swap ?
642                             (uintmax_t)le32toh(*(uint32_t *)(void *)data) :
643                             (uintmax_t)*(uint32_t *)(void *)data);
644                         break;
645                 case NV_TYPE_INT64:
646                         printf("(int64): %jd", swap ?
647                             (intmax_t)le64toh(*(int64_t *)(void *)data) :
648                             (intmax_t)*(int64_t *)(void *)data);
649                         break;
650                 case NV_TYPE_UINT64:
651                         printf("(uint64): %ju", swap ?
652                             (uintmax_t)le64toh(*(uint64_t *)(void *)data) :
653                             (uintmax_t)*(uint64_t *)(void *)data);
654                         break;
655                 case NV_TYPE_INT8_ARRAY:
656                         printf("(int8 array):");
657                         for (ii = 0; ii < dsize; ii++)
658                                 printf(" %jd", (intmax_t)((int8_t *)data)[ii]);
659                         break;
660                 case NV_TYPE_UINT8_ARRAY:
661                         printf("(uint8 array):");
662                         for (ii = 0; ii < dsize; ii++)
663                                 printf(" %ju", (uintmax_t)((uint8_t *)data)[ii]);
664                         break;
665                 case NV_TYPE_INT16_ARRAY:
666                         printf("(int16 array):");
667                         for (ii = 0; ii < dsize / 2; ii++) {
668                                 printf(" %jd", swap ?
669                                     (intmax_t)le16toh(((int16_t *)(void *)data)[ii]) :
670                                     (intmax_t)((int16_t *)(void *)data)[ii]);
671                         }
672                         break;
673                 case NV_TYPE_UINT16_ARRAY:
674                         printf("(uint16 array):");
675                         for (ii = 0; ii < dsize / 2; ii++) {
676                                 printf(" %ju", swap ?
677                                     (uintmax_t)le16toh(((uint16_t *)(void *)data)[ii]) :
678                                     (uintmax_t)((uint16_t *)(void *)data)[ii]);
679                         }
680                         break;
681                 case NV_TYPE_INT32_ARRAY:
682                         printf("(int32 array):");
683                         for (ii = 0; ii < dsize / 4; ii++) {
684                                 printf(" %jd", swap ?
685                                     (intmax_t)le32toh(((int32_t *)(void *)data)[ii]) :
686                                     (intmax_t)((int32_t *)(void *)data)[ii]);
687                         }
688                         break;
689                 case NV_TYPE_UINT32_ARRAY:
690                         printf("(uint32 array):");
691                         for (ii = 0; ii < dsize / 4; ii++) {
692                                 printf(" %ju", swap ?
693                                     (uintmax_t)le32toh(((uint32_t *)(void *)data)[ii]) :
694                                     (uintmax_t)((uint32_t *)(void *)data)[ii]);
695                         }
696                         break;
697                 case NV_TYPE_INT64_ARRAY:
698                         printf("(int64 array):");
699                         for (ii = 0; ii < dsize / 8; ii++) {
700                                 printf(" %ju", swap ?
701                                     (uintmax_t)le64toh(((uint64_t *)(void *)data)[ii]) :
702                                     (uintmax_t)((uint64_t *)(void *)data)[ii]);
703                         }
704                         break;
705                 case NV_TYPE_UINT64_ARRAY:
706                         printf("(uint64 array):");
707                         for (ii = 0; ii < dsize / 8; ii++) {
708                                 printf(" %ju", swap ?
709                                     (uintmax_t)le64toh(((uint64_t *)(void *)data)[ii]) :
710                                     (uintmax_t)((uint64_t *)(void *)data)[ii]);
711                         }
712                         break;
713                 case NV_TYPE_STRING:
714                         printf("(string): %s", (char *)data);
715                         break;
716                 default:
717                         assert(!"invalid condition");
718                 }
719                 printf("\n");
720                 ptr += NVH_SIZE(nvh);
721                 size -= NVH_SIZE(nvh);
722         }
723 }
724
725 /*
726  * Local routines below.
727  */
728
729 static void
730 nv_add(struct nv *nv, const unsigned char *value, size_t vsize, int type,
731     const char *name)
732 {
733         static unsigned char align[7];
734         struct nvhdr *nvh;
735         size_t namesize;
736
737         if (nv == NULL) {
738                 errno = ENOMEM;
739                 return;
740         }
741
742         NV_CHECK(nv);
743
744         namesize = strlen(name) + 1;
745
746         nvh = malloc(sizeof(*nvh) + roundup2(namesize, 8));
747         if (nvh == NULL) {
748                 if (nv->nv_error == 0)
749                         nv->nv_error = ENOMEM;
750                 return;
751         }
752         nvh->nvh_type = NV_ORDER_HOST | type;
753         nvh->nvh_namesize = (uint8_t)namesize;
754         nvh->nvh_dsize = (uint32_t)vsize;
755         bcopy(name, nvh->nvh_name, namesize);
756
757         /* Add header first. */
758         if (ebuf_add_tail(nv->nv_ebuf, nvh, NVH_HSIZE(nvh)) < 0) {
759                 assert(errno != 0);
760                 if (nv->nv_error == 0)
761                         nv->nv_error = errno;
762                 free(nvh);
763                 return;
764         }
765         free(nvh);
766         /* Add the actual data. */
767         if (ebuf_add_tail(nv->nv_ebuf, value, vsize) < 0) {
768                 assert(errno != 0);
769                 if (nv->nv_error == 0)
770                         nv->nv_error = errno;
771                 return;
772         }
773         /* Align the data (if needed). */
774         vsize = roundup2(vsize, 8) - vsize;
775         if (vsize == 0)
776                 return;
777         assert(vsize > 0 && vsize <= sizeof(align));
778         if (ebuf_add_tail(nv->nv_ebuf, align, vsize) < 0) {
779                 assert(errno != 0);
780                 if (nv->nv_error == 0)
781                         nv->nv_error = errno;
782                 return;
783         }
784 }
785
786 static void
787 nv_addv(struct nv *nv, const unsigned char *value, size_t vsize, int type,
788     const char *namefmt, va_list nameap)
789 {
790         char name[255];
791         size_t namesize;
792
793         namesize = vsnprintf(name, sizeof(name), namefmt, nameap);
794         assert(namesize > 0 && namesize < sizeof(name));
795
796         nv_add(nv, value, vsize, type, name);
797 }
798
799 static struct nvhdr *
800 nv_find(struct nv *nv, int type, const char *namefmt, va_list nameap)
801 {
802         char name[255];
803         struct nvhdr *nvh;
804         unsigned char *ptr;
805         size_t size, namesize;
806
807         if (nv == NULL) {
808                 errno = ENOMEM;
809                 return (NULL);
810         }
811
812         NV_CHECK(nv);
813
814         namesize = vsnprintf(name, sizeof(name), namefmt, nameap);
815         assert(namesize > 0 && namesize < sizeof(name));
816         namesize++;
817
818         ptr = ebuf_data(nv->nv_ebuf, &size);
819         while (size > 0) {
820                 assert(size >= sizeof(*nvh) + 2);
821                 nvh = (struct nvhdr *)ptr;
822                 assert(size >= NVH_SIZE(nvh));
823                 nv_swap(nvh, true);
824                 if (strcmp(nvh->nvh_name, name) == 0) {
825                         if (type != NV_TYPE_NONE &&
826                             (nvh->nvh_type & NV_TYPE_MASK) != type) {
827                                 errno = EINVAL;
828                                 if (nv->nv_error == 0)
829                                         nv->nv_error = EINVAL;
830                                 return (NULL);
831                         }
832                         return (nvh);
833                 }
834                 ptr += NVH_SIZE(nvh);
835                 size -= NVH_SIZE(nvh);
836         }
837         errno = ENOENT;
838         if (nv->nv_error == 0)
839                 nv->nv_error = ENOENT;
840         return (NULL);
841 }
842
843 static void
844 nv_swap(struct nvhdr *nvh, bool tohost)
845 {
846         unsigned char *data, *end, *p;
847         size_t vsize;
848
849         data = NVH_DATA(nvh);
850         if (tohost) {
851                 if ((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST)
852                         return;
853                 nvh->nvh_dsize = le32toh(nvh->nvh_dsize);
854                 end = data + nvh->nvh_dsize;
855                 nvh->nvh_type &= ~NV_ORDER_MASK;
856                 nvh->nvh_type |= NV_ORDER_HOST;
857         } else {
858                 if ((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_NETWORK)
859                         return;
860                 end = data + nvh->nvh_dsize;
861                 nvh->nvh_dsize = htole32(nvh->nvh_dsize);
862                 nvh->nvh_type &= ~NV_ORDER_MASK;
863                 nvh->nvh_type |= NV_ORDER_NETWORK;
864         }
865
866         vsize = 0;
867
868         switch (nvh->nvh_type & NV_TYPE_MASK) {
869         case NV_TYPE_INT8:
870         case NV_TYPE_UINT8:
871         case NV_TYPE_INT8_ARRAY:
872         case NV_TYPE_UINT8_ARRAY:
873                 break;
874         case NV_TYPE_INT16:
875         case NV_TYPE_UINT16:
876         case NV_TYPE_INT16_ARRAY:
877         case NV_TYPE_UINT16_ARRAY:
878                 if (vsize == 0)
879                         vsize = 2;
880                 /* FALLTHOUGH */
881         case NV_TYPE_INT32:
882         case NV_TYPE_UINT32:
883         case NV_TYPE_INT32_ARRAY:
884         case NV_TYPE_UINT32_ARRAY:
885                 if (vsize == 0)
886                         vsize = 4;
887                 /* FALLTHOUGH */
888         case NV_TYPE_INT64:
889         case NV_TYPE_UINT64:
890         case NV_TYPE_INT64_ARRAY:
891         case NV_TYPE_UINT64_ARRAY:
892                 if (vsize == 0)
893                         vsize = 8;
894                 for (p = data; p < end; p += vsize) {
895                         if (tohost) {
896                                 switch (vsize) {
897                                 case 2:
898                                         *(uint16_t *)(void *)p =
899                                             le16toh(*(uint16_t *)(void *)p);
900                                         break;
901                                 case 4:
902                                         *(uint32_t *)(void *)p =
903                                             le32toh(*(uint32_t *)(void *)p);
904                                         break;
905                                 case 8:
906                                         *(uint64_t *)(void *)p =
907                                             le64toh(*(uint64_t *)(void *)p);
908                                         break;
909                                 default:
910                                         assert(!"invalid condition");
911                                 }
912                         } else {
913                                 switch (vsize) {
914                                 case 2:
915                                         *(uint16_t *)(void *)p =
916                                             htole16(*(uint16_t *)(void *)p);
917                                         break;
918                                 case 4:
919                                         *(uint32_t *)(void *)p =
920                                             htole32(*(uint32_t *)(void *)p);
921                                         break;
922                                 case 8:
923                                         *(uint64_t *)(void *)p =
924                                             htole64(*(uint64_t *)(void *)p);
925                                         break;
926                                 default:
927                                         assert(!"invalid condition");
928                                 }
929                         }
930                 }
931                 break;
932         case NV_TYPE_STRING:
933                 break;
934         default:
935                 assert(!"unrecognized type");
936         }
937 }