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