]> CyberLeo.Net >> Repos - FreeBSD/releng/9.0.git/blob - sbin/hastd/nv.c
Copy stable/9 to releng/9.0 as part of the FreeBSD 9.0-RELEASE release
[FreeBSD/releng/9.0.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 static bool
567 nv_vexists(struct nv *nv, const char *namefmt, va_list nameap)
568 {
569         struct nvhdr *nvh;
570         int snverror, serrno;
571
572         if (nv == NULL)
573                 return (false);
574
575         serrno = errno;
576         snverror = nv->nv_error;
577
578         nvh = nv_find(nv, NV_TYPE_NONE, namefmt, nameap);
579
580         errno = serrno;
581         nv->nv_error = snverror;
582
583         return (nvh != NULL);
584 }
585
586 bool
587 nv_exists(struct nv *nv, const char *namefmt, ...)
588 {
589         va_list nameap;
590         bool ret;
591
592         va_start(nameap, namefmt);
593         ret = nv_vexists(nv, namefmt, nameap);
594         va_end(nameap);
595
596         return (ret);
597 }
598
599 void
600 nv_assert(struct nv *nv, const char *namefmt, ...)
601 {
602         va_list nameap;
603
604         va_start(nameap, namefmt);
605         assert(nv_vexists(nv, namefmt, nameap));
606         va_end(nameap);
607 }
608
609 /*
610  * Dump content of the nv structure.
611  */
612 void
613 nv_dump(struct nv *nv)
614 {
615         struct nvhdr *nvh;
616         unsigned char *data, *ptr;
617         size_t dsize, size;
618         unsigned int ii;
619         bool swap;
620
621         if (nv_validate(nv, NULL) < 0) {
622                 printf("error: %d\n", errno);
623                 return;
624         }
625
626         NV_CHECK(nv);
627         assert(nv->nv_error == 0);
628
629         ptr = ebuf_data(nv->nv_ebuf, &size);
630         while (size > 0) {
631                 assert(size >= sizeof(*nvh) + 2);
632                 nvh = (struct nvhdr *)ptr;
633                 assert(size >= NVH_SIZE(nvh));
634                 swap = ((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_NETWORK);
635                 dsize = NVH_DSIZE(nvh);
636                 data = NVH_DATA(nvh);
637                 printf("  %s", nvh->nvh_name);
638                 switch (nvh->nvh_type & NV_TYPE_MASK) {
639                 case NV_TYPE_INT8:
640                         printf("(int8): %jd", (intmax_t)(*(int8_t *)data));
641                         break;
642                 case NV_TYPE_UINT8:
643                         printf("(uint8): %ju", (uintmax_t)(*(uint8_t *)data));
644                         break;
645                 case NV_TYPE_INT16:
646                         printf("(int16): %jd", swap ?
647                             (intmax_t)le16toh(*(int16_t *)(void *)data) :
648                             (intmax_t)*(int16_t *)(void *)data);
649                         break;
650                 case NV_TYPE_UINT16:
651                         printf("(uint16): %ju", swap ?
652                             (uintmax_t)le16toh(*(uint16_t *)(void *)data) :
653                             (uintmax_t)*(uint16_t *)(void *)data);
654                         break;
655                 case NV_TYPE_INT32:
656                         printf("(int32): %jd", swap ?
657                             (intmax_t)le32toh(*(int32_t *)(void *)data) :
658                             (intmax_t)*(int32_t *)(void *)data);
659                         break;
660                 case NV_TYPE_UINT32:
661                         printf("(uint32): %ju", swap ?
662                             (uintmax_t)le32toh(*(uint32_t *)(void *)data) :
663                             (uintmax_t)*(uint32_t *)(void *)data);
664                         break;
665                 case NV_TYPE_INT64:
666                         printf("(int64): %jd", swap ?
667                             (intmax_t)le64toh(*(int64_t *)(void *)data) :
668                             (intmax_t)*(int64_t *)(void *)data);
669                         break;
670                 case NV_TYPE_UINT64:
671                         printf("(uint64): %ju", swap ?
672                             (uintmax_t)le64toh(*(uint64_t *)(void *)data) :
673                             (uintmax_t)*(uint64_t *)(void *)data);
674                         break;
675                 case NV_TYPE_INT8_ARRAY:
676                         printf("(int8 array):");
677                         for (ii = 0; ii < dsize; ii++)
678                                 printf(" %jd", (intmax_t)((int8_t *)data)[ii]);
679                         break;
680                 case NV_TYPE_UINT8_ARRAY:
681                         printf("(uint8 array):");
682                         for (ii = 0; ii < dsize; ii++)
683                                 printf(" %ju", (uintmax_t)((uint8_t *)data)[ii]);
684                         break;
685                 case NV_TYPE_INT16_ARRAY:
686                         printf("(int16 array):");
687                         for (ii = 0; ii < dsize / 2; ii++) {
688                                 printf(" %jd", swap ?
689                                     (intmax_t)le16toh(((int16_t *)(void *)data)[ii]) :
690                                     (intmax_t)((int16_t *)(void *)data)[ii]);
691                         }
692                         break;
693                 case NV_TYPE_UINT16_ARRAY:
694                         printf("(uint16 array):");
695                         for (ii = 0; ii < dsize / 2; ii++) {
696                                 printf(" %ju", swap ?
697                                     (uintmax_t)le16toh(((uint16_t *)(void *)data)[ii]) :
698                                     (uintmax_t)((uint16_t *)(void *)data)[ii]);
699                         }
700                         break;
701                 case NV_TYPE_INT32_ARRAY:
702                         printf("(int32 array):");
703                         for (ii = 0; ii < dsize / 4; ii++) {
704                                 printf(" %jd", swap ?
705                                     (intmax_t)le32toh(((int32_t *)(void *)data)[ii]) :
706                                     (intmax_t)((int32_t *)(void *)data)[ii]);
707                         }
708                         break;
709                 case NV_TYPE_UINT32_ARRAY:
710                         printf("(uint32 array):");
711                         for (ii = 0; ii < dsize / 4; ii++) {
712                                 printf(" %ju", swap ?
713                                     (uintmax_t)le32toh(((uint32_t *)(void *)data)[ii]) :
714                                     (uintmax_t)((uint32_t *)(void *)data)[ii]);
715                         }
716                         break;
717                 case NV_TYPE_INT64_ARRAY:
718                         printf("(int64 array):");
719                         for (ii = 0; ii < dsize / 8; ii++) {
720                                 printf(" %ju", swap ?
721                                     (uintmax_t)le64toh(((uint64_t *)(void *)data)[ii]) :
722                                     (uintmax_t)((uint64_t *)(void *)data)[ii]);
723                         }
724                         break;
725                 case NV_TYPE_UINT64_ARRAY:
726                         printf("(uint64 array):");
727                         for (ii = 0; ii < dsize / 8; ii++) {
728                                 printf(" %ju", swap ?
729                                     (uintmax_t)le64toh(((uint64_t *)(void *)data)[ii]) :
730                                     (uintmax_t)((uint64_t *)(void *)data)[ii]);
731                         }
732                         break;
733                 case NV_TYPE_STRING:
734                         printf("(string): %s", (char *)data);
735                         break;
736                 default:
737                         assert(!"invalid condition");
738                 }
739                 printf("\n");
740                 ptr += NVH_SIZE(nvh);
741                 size -= NVH_SIZE(nvh);
742         }
743 }
744
745 /*
746  * Local routines below.
747  */
748
749 static void
750 nv_add(struct nv *nv, const unsigned char *value, size_t vsize, int type,
751     const char *name)
752 {
753         static unsigned char align[7];
754         struct nvhdr *nvh;
755         size_t namesize;
756
757         if (nv == NULL) {
758                 errno = ENOMEM;
759                 return;
760         }
761
762         NV_CHECK(nv);
763
764         namesize = strlen(name) + 1;
765
766         nvh = malloc(sizeof(*nvh) + roundup2(namesize, 8));
767         if (nvh == NULL) {
768                 if (nv->nv_error == 0)
769                         nv->nv_error = ENOMEM;
770                 return;
771         }
772         nvh->nvh_type = NV_ORDER_HOST | type;
773         nvh->nvh_namesize = (uint8_t)namesize;
774         nvh->nvh_dsize = (uint32_t)vsize;
775         bcopy(name, nvh->nvh_name, namesize);
776
777         /* Add header first. */
778         if (ebuf_add_tail(nv->nv_ebuf, nvh, NVH_HSIZE(nvh)) < 0) {
779                 assert(errno != 0);
780                 if (nv->nv_error == 0)
781                         nv->nv_error = errno;
782                 free(nvh);
783                 return;
784         }
785         free(nvh);
786         /* Add the actual data. */
787         if (ebuf_add_tail(nv->nv_ebuf, value, vsize) < 0) {
788                 assert(errno != 0);
789                 if (nv->nv_error == 0)
790                         nv->nv_error = errno;
791                 return;
792         }
793         /* Align the data (if needed). */
794         vsize = roundup2(vsize, 8) - vsize;
795         if (vsize == 0)
796                 return;
797         assert(vsize > 0 && vsize <= sizeof(align));
798         if (ebuf_add_tail(nv->nv_ebuf, align, vsize) < 0) {
799                 assert(errno != 0);
800                 if (nv->nv_error == 0)
801                         nv->nv_error = errno;
802                 return;
803         }
804 }
805
806 static void
807 nv_addv(struct nv *nv, const unsigned char *value, size_t vsize, int type,
808     const char *namefmt, va_list nameap)
809 {
810         char name[255];
811         size_t namesize;
812
813         namesize = vsnprintf(name, sizeof(name), namefmt, nameap);
814         assert(namesize > 0 && namesize < sizeof(name));
815
816         nv_add(nv, value, vsize, type, name);
817 }
818
819 static struct nvhdr *
820 nv_find(struct nv *nv, int type, const char *namefmt, va_list nameap)
821 {
822         char name[255];
823         struct nvhdr *nvh;
824         unsigned char *ptr;
825         size_t size, namesize;
826
827         if (nv == NULL) {
828                 errno = ENOMEM;
829                 return (NULL);
830         }
831
832         NV_CHECK(nv);
833
834         namesize = vsnprintf(name, sizeof(name), namefmt, nameap);
835         assert(namesize > 0 && namesize < sizeof(name));
836         namesize++;
837
838         ptr = ebuf_data(nv->nv_ebuf, &size);
839         while (size > 0) {
840                 assert(size >= sizeof(*nvh) + 2);
841                 nvh = (struct nvhdr *)ptr;
842                 assert(size >= NVH_SIZE(nvh));
843                 nv_swap(nvh, true);
844                 if (strcmp(nvh->nvh_name, name) == 0) {
845                         if (type != NV_TYPE_NONE &&
846                             (nvh->nvh_type & NV_TYPE_MASK) != type) {
847                                 errno = EINVAL;
848                                 if (nv->nv_error == 0)
849                                         nv->nv_error = EINVAL;
850                                 return (NULL);
851                         }
852                         return (nvh);
853                 }
854                 ptr += NVH_SIZE(nvh);
855                 size -= NVH_SIZE(nvh);
856         }
857         errno = ENOENT;
858         if (nv->nv_error == 0)
859                 nv->nv_error = ENOENT;
860         return (NULL);
861 }
862
863 static void
864 nv_swap(struct nvhdr *nvh, bool tohost)
865 {
866         unsigned char *data, *end, *p;
867         size_t vsize;
868
869         data = NVH_DATA(nvh);
870         if (tohost) {
871                 if ((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST)
872                         return;
873                 nvh->nvh_dsize = le32toh(nvh->nvh_dsize);
874                 end = data + nvh->nvh_dsize;
875                 nvh->nvh_type &= ~NV_ORDER_MASK;
876                 nvh->nvh_type |= NV_ORDER_HOST;
877         } else {
878                 if ((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_NETWORK)
879                         return;
880                 end = data + nvh->nvh_dsize;
881                 nvh->nvh_dsize = htole32(nvh->nvh_dsize);
882                 nvh->nvh_type &= ~NV_ORDER_MASK;
883                 nvh->nvh_type |= NV_ORDER_NETWORK;
884         }
885
886         vsize = 0;
887
888         switch (nvh->nvh_type & NV_TYPE_MASK) {
889         case NV_TYPE_INT8:
890         case NV_TYPE_UINT8:
891         case NV_TYPE_INT8_ARRAY:
892         case NV_TYPE_UINT8_ARRAY:
893                 break;
894         case NV_TYPE_INT16:
895         case NV_TYPE_UINT16:
896         case NV_TYPE_INT16_ARRAY:
897         case NV_TYPE_UINT16_ARRAY:
898                 if (vsize == 0)
899                         vsize = 2;
900                 /* FALLTHOUGH */
901         case NV_TYPE_INT32:
902         case NV_TYPE_UINT32:
903         case NV_TYPE_INT32_ARRAY:
904         case NV_TYPE_UINT32_ARRAY:
905                 if (vsize == 0)
906                         vsize = 4;
907                 /* FALLTHOUGH */
908         case NV_TYPE_INT64:
909         case NV_TYPE_UINT64:
910         case NV_TYPE_INT64_ARRAY:
911         case NV_TYPE_UINT64_ARRAY:
912                 if (vsize == 0)
913                         vsize = 8;
914                 for (p = data; p < end; p += vsize) {
915                         if (tohost) {
916                                 switch (vsize) {
917                                 case 2:
918                                         *(uint16_t *)(void *)p =
919                                             le16toh(*(uint16_t *)(void *)p);
920                                         break;
921                                 case 4:
922                                         *(uint32_t *)(void *)p =
923                                             le32toh(*(uint32_t *)(void *)p);
924                                         break;
925                                 case 8:
926                                         *(uint64_t *)(void *)p =
927                                             le64toh(*(uint64_t *)(void *)p);
928                                         break;
929                                 default:
930                                         assert(!"invalid condition");
931                                 }
932                         } else {
933                                 switch (vsize) {
934                                 case 2:
935                                         *(uint16_t *)(void *)p =
936                                             htole16(*(uint16_t *)(void *)p);
937                                         break;
938                                 case 4:
939                                         *(uint32_t *)(void *)p =
940                                             htole32(*(uint32_t *)(void *)p);
941                                         break;
942                                 case 8:
943                                         *(uint64_t *)(void *)p =
944                                             htole64(*(uint64_t *)(void *)p);
945                                         break;
946                                 default:
947                                         assert(!"invalid condition");
948                                 }
949                         }
950                 }
951                 break;
952         case NV_TYPE_STRING:
953                 break;
954         default:
955                 assert(!"unrecognized type");
956         }
957 }