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 *
nv_alloc(void)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
nv_free(struct nv * nv)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
nv_error(const struct nv * nv)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
nv_set_error(struct nv * nv,int error)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
nv_validate(struct nv * nv,size_t * extrap)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 *
nv_hton(struct nv * nv)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 *
nv_ntoh(struct ebuf * eb)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
NV_DEFINE_ADD(int8,INT8)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
nv_add_stringf(struct nv * nv,const char * name,const char * valuefmt,...)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
nv_add_stringv(struct nv * nv,const char * name,const char * valuefmt,va_list valueap)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
NV_DEFINE_GET(int8,INT8)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
nv_vexists(struct nv * nv,const char * namefmt,va_list nameap)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
nv_exists(struct nv * nv,const char * namefmt,...)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
nv_assert(struct nv * nv,const char * namefmt,...)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
nv_dump(struct nv * nv)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
nv_add(struct nv * nv,const unsigned char * value,size_t vsize,int type,const char * name)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
nv_addv(struct nv * nv,const unsigned char * value,size_t vsize,int type,const char * namefmt,va_list nameap)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 *
nv_find(struct nv * nv,int type,const char * namefmt,va_list nameap)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
nv_swap(struct nvhdr * nvh,bool tohost)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 }
966