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