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