]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - sbin/hastd/nv.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.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 <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 = 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 }