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((size_t)(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 (roundup2(dsize, 8) == 0 || 251 roundup2(dsize, 8) > size - NVH_HSIZE(nvh)) { 252 error = EINVAL; 253 break; 254 } 255 vsize = 0; 256 switch (nvh->nvh_type & NV_TYPE_MASK) { 257 case NV_TYPE_INT8: 258 case NV_TYPE_UINT8: 259 if (vsize == 0) 260 vsize = 1; 261 /* FALLTHROUGH */ 262 case NV_TYPE_INT16: 263 case NV_TYPE_UINT16: 264 if (vsize == 0) 265 vsize = 2; 266 /* FALLTHROUGH */ 267 case NV_TYPE_INT32: 268 case NV_TYPE_UINT32: 269 if (vsize == 0) 270 vsize = 4; 271 /* FALLTHROUGH */ 272 case NV_TYPE_INT64: 273 case NV_TYPE_UINT64: 274 if (vsize == 0) 275 vsize = 8; 276 if (dsize != vsize) { 277 error = EINVAL; 278 break; 279 } 280 break; 281 case NV_TYPE_INT8_ARRAY: 282 case NV_TYPE_UINT8_ARRAY: 283 break; 284 case NV_TYPE_INT16_ARRAY: 285 case NV_TYPE_UINT16_ARRAY: 286 if (vsize == 0) 287 vsize = 2; 288 /* FALLTHROUGH */ 289 case NV_TYPE_INT32_ARRAY: 290 case NV_TYPE_UINT32_ARRAY: 291 if (vsize == 0) 292 vsize = 4; 293 /* FALLTHROUGH */ 294 case NV_TYPE_INT64_ARRAY: 295 case NV_TYPE_UINT64_ARRAY: 296 if (vsize == 0) 297 vsize = 8; 298 if ((dsize % vsize) != 0) { 299 error = EINVAL; 300 break; 301 } 302 break; 303 case NV_TYPE_STRING: 304 data = NVH_DATA(nvh); 305 if (data[dsize - 1] != '\0') { 306 error = EINVAL; 307 break; 308 } 309 if (strlen((char *)data) != dsize - 1) { 310 error = EINVAL; 311 break; 312 } 313 break; 314 default: 315 PJDLOG_ABORT("invalid condition"); 316 } 317 if (error != 0) 318 break; 319 ptr += NVH_SIZE(nvh); 320 size -= NVH_SIZE(nvh); 321 } 322 if (error != 0) { 323 errno = error; 324 if (nv->nv_error == 0) 325 nv->nv_error = error; 326 return (-1); 327 } 328 if (extrap != NULL) 329 *extrap = size; 330 return (0); 331 } 332 333 /* 334 * Convert the given nv structure to network byte order and return ebuf 335 * structure. 336 */ 337 struct ebuf * 338 nv_hton(struct nv *nv) 339 { 340 struct nvhdr *nvh; 341 unsigned char *ptr; 342 size_t size; 343 344 NV_CHECK(nv); 345 PJDLOG_ASSERT(nv->nv_error == 0); 346 347 ptr = ebuf_data(nv->nv_ebuf, &size); 348 while (size > 0) { 349 /* 350 * Minimum size at this point is size of nvhdr structure, 351 * one character long name plus terminating '\0'. 352 */ 353 PJDLOG_ASSERT(size >= sizeof(*nvh) + 2); 354 nvh = (struct nvhdr *)ptr; 355 PJDLOG_ASSERT(NVH_SIZE(nvh) <= size); 356 nv_swap(nvh, false); 357 ptr += NVH_SIZE(nvh); 358 size -= NVH_SIZE(nvh); 359 } 360 361 return (nv->nv_ebuf); 362 } 363 364 /* 365 * Create nv structure based on ebuf received from the network. 366 */ 367 struct nv * 368 nv_ntoh(struct ebuf *eb) 369 { 370 struct nv *nv; 371 size_t extra; 372 int rerrno; 373 374 PJDLOG_ASSERT(eb != NULL); 375 376 nv = malloc(sizeof(*nv)); 377 if (nv == NULL) 378 return (NULL); 379 nv->nv_error = 0; 380 nv->nv_ebuf = eb; 381 nv->nv_magic = NV_MAGIC; 382 383 if (nv_validate(nv, &extra) == -1) { 384 rerrno = errno; 385 nv->nv_magic = 0; 386 free(nv); 387 errno = rerrno; 388 return (NULL); 389 } 390 /* 391 * Remove extra zeros at the end of the buffer. 392 */ 393 ebuf_del_tail(eb, extra); 394 395 return (nv); 396 } 397 398 #define NV_DEFINE_ADD(type, TYPE) \ 399 void \ 400 nv_add_##type(struct nv *nv, type##_t value, const char *namefmt, ...) \ 401 { \ 402 va_list nameap; \ 403 \ 404 va_start(nameap, namefmt); \ 405 nv_addv(nv, (unsigned char *)&value, sizeof(value), \ 406 NV_TYPE_##TYPE, namefmt, nameap); \ 407 va_end(nameap); \ 408 } 409 410 NV_DEFINE_ADD(int8, INT8) 411 NV_DEFINE_ADD(uint8, UINT8) 412 NV_DEFINE_ADD(int16, INT16) 413 NV_DEFINE_ADD(uint16, UINT16) 414 NV_DEFINE_ADD(int32, INT32) 415 NV_DEFINE_ADD(uint32, UINT32) 416 NV_DEFINE_ADD(int64, INT64) 417 NV_DEFINE_ADD(uint64, UINT64) 418 419 #undef NV_DEFINE_ADD 420 421 #define NV_DEFINE_ADD_ARRAY(type, TYPE) \ 422 void \ 423 nv_add_##type##_array(struct nv *nv, const type##_t *value, \ 424 size_t nsize, const char *namefmt, ...) \ 425 { \ 426 va_list nameap; \ 427 \ 428 va_start(nameap, namefmt); \ 429 nv_addv(nv, (const unsigned char *)value, \ 430 sizeof(value[0]) * nsize, NV_TYPE_##TYPE##_ARRAY, namefmt, \ 431 nameap); \ 432 va_end(nameap); \ 433 } 434 435 NV_DEFINE_ADD_ARRAY(int8, INT8) 436 NV_DEFINE_ADD_ARRAY(uint8, UINT8) 437 NV_DEFINE_ADD_ARRAY(int16, INT16) 438 NV_DEFINE_ADD_ARRAY(uint16, UINT16) 439 NV_DEFINE_ADD_ARRAY(int32, INT32) 440 NV_DEFINE_ADD_ARRAY(uint32, UINT32) 441 NV_DEFINE_ADD_ARRAY(int64, INT64) 442 NV_DEFINE_ADD_ARRAY(uint64, UINT64) 443 444 #undef NV_DEFINE_ADD_ARRAY 445 446 void 447 nv_add_string(struct nv *nv, const char *value, const char *namefmt, ...) 448 { 449 va_list nameap; 450 size_t size; 451 452 size = strlen(value) + 1; 453 454 va_start(nameap, namefmt); 455 nv_addv(nv, (const unsigned char *)value, size, NV_TYPE_STRING, 456 namefmt, nameap); 457 va_end(nameap); 458 } 459 460 void 461 nv_add_stringf(struct nv *nv, const char *name, const char *valuefmt, ...) 462 { 463 va_list valueap; 464 465 va_start(valueap, valuefmt); 466 nv_add_stringv(nv, name, valuefmt, valueap); 467 va_end(valueap); 468 } 469 470 void 471 nv_add_stringv(struct nv *nv, const char *name, const char *valuefmt, 472 va_list valueap) 473 { 474 char *value; 475 ssize_t size; 476 477 size = vasprintf(&value, valuefmt, valueap); 478 if (size == -1) { 479 if (nv->nv_error == 0) 480 nv->nv_error = ENOMEM; 481 return; 482 } 483 size++; 484 nv_add(nv, (const unsigned char *)value, size, NV_TYPE_STRING, name); 485 free(value); 486 } 487 488 #define NV_DEFINE_GET(type, TYPE) \ 489 type##_t \ 490 nv_get_##type(struct nv *nv, const char *namefmt, ...) \ 491 { \ 492 struct nvhdr *nvh; \ 493 va_list nameap; \ 494 type##_t value; \ 495 \ 496 va_start(nameap, namefmt); \ 497 nvh = nv_find(nv, NV_TYPE_##TYPE, namefmt, nameap); \ 498 va_end(nameap); \ 499 if (nvh == NULL) \ 500 return (0); \ 501 PJDLOG_ASSERT((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST);\ 502 PJDLOG_ASSERT(sizeof(value) == nvh->nvh_dsize); \ 503 bcopy(NVH_DATA(nvh), &value, sizeof(value)); \ 504 \ 505 return (value); \ 506 } 507 508 NV_DEFINE_GET(int8, INT8) 509 NV_DEFINE_GET(uint8, UINT8) 510 NV_DEFINE_GET(int16, INT16) 511 NV_DEFINE_GET(uint16, UINT16) 512 NV_DEFINE_GET(int32, INT32) 513 NV_DEFINE_GET(uint32, UINT32) 514 NV_DEFINE_GET(int64, INT64) 515 NV_DEFINE_GET(uint64, UINT64) 516 517 #undef NV_DEFINE_GET 518 519 #define NV_DEFINE_GET_ARRAY(type, TYPE) \ 520 const type##_t * \ 521 nv_get_##type##_array(struct nv *nv, size_t *sizep, \ 522 const char *namefmt, ...) \ 523 { \ 524 struct nvhdr *nvh; \ 525 va_list nameap; \ 526 \ 527 va_start(nameap, namefmt); \ 528 nvh = nv_find(nv, NV_TYPE_##TYPE##_ARRAY, namefmt, nameap); \ 529 va_end(nameap); \ 530 if (nvh == NULL) \ 531 return (NULL); \ 532 PJDLOG_ASSERT((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST);\ 533 PJDLOG_ASSERT((nvh->nvh_dsize % sizeof(type##_t)) == 0); \ 534 if (sizep != NULL) \ 535 *sizep = nvh->nvh_dsize / sizeof(type##_t); \ 536 return ((type##_t *)(void *)NVH_DATA(nvh)); \ 537 } 538 539 NV_DEFINE_GET_ARRAY(int8, INT8) 540 NV_DEFINE_GET_ARRAY(uint8, UINT8) 541 NV_DEFINE_GET_ARRAY(int16, INT16) 542 NV_DEFINE_GET_ARRAY(uint16, UINT16) 543 NV_DEFINE_GET_ARRAY(int32, INT32) 544 NV_DEFINE_GET_ARRAY(uint32, UINT32) 545 NV_DEFINE_GET_ARRAY(int64, INT64) 546 NV_DEFINE_GET_ARRAY(uint64, UINT64) 547 548 #undef NV_DEFINE_GET_ARRAY 549 550 const char * 551 nv_get_string(struct nv *nv, const char *namefmt, ...) 552 { 553 struct nvhdr *nvh; 554 va_list nameap; 555 char *str; 556 557 va_start(nameap, namefmt); 558 nvh = nv_find(nv, NV_TYPE_STRING, namefmt, nameap); 559 va_end(nameap); 560 if (nvh == NULL) 561 return (NULL); 562 PJDLOG_ASSERT((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST); 563 PJDLOG_ASSERT(nvh->nvh_dsize >= 1); 564 str = (char *)NVH_DATA(nvh); 565 PJDLOG_ASSERT(str[nvh->nvh_dsize - 1] == '\0'); 566 PJDLOG_ASSERT(strlen(str) == nvh->nvh_dsize - 1); 567 return (str); 568 } 569 570 static bool 571 nv_vexists(struct nv *nv, const char *namefmt, va_list nameap) 572 { 573 struct nvhdr *nvh; 574 int snverror, serrno; 575 576 if (nv == NULL) 577 return (false); 578 579 serrno = errno; 580 snverror = nv->nv_error; 581 582 nvh = nv_find(nv, NV_TYPE_NONE, namefmt, nameap); 583 584 errno = serrno; 585 nv->nv_error = snverror; 586 587 return (nvh != NULL); 588 } 589 590 bool 591 nv_exists(struct nv *nv, const char *namefmt, ...) 592 { 593 va_list nameap; 594 bool ret; 595 596 va_start(nameap, namefmt); 597 ret = nv_vexists(nv, namefmt, nameap); 598 va_end(nameap); 599 600 return (ret); 601 } 602 603 void 604 nv_assert(struct nv *nv, const char *namefmt, ...) 605 { 606 va_list nameap; 607 608 va_start(nameap, namefmt); 609 PJDLOG_ASSERT(nv_vexists(nv, namefmt, nameap)); 610 va_end(nameap); 611 } 612 613 /* 614 * Dump content of the nv structure. 615 */ 616 void 617 nv_dump(struct nv *nv) 618 { 619 struct nvhdr *nvh; 620 unsigned char *data, *ptr; 621 size_t dsize, size; 622 unsigned int ii; 623 bool swap; 624 625 if (nv_validate(nv, NULL) == -1) { 626 printf("error: %d\n", errno); 627 return; 628 } 629 630 NV_CHECK(nv); 631 PJDLOG_ASSERT(nv->nv_error == 0); 632 633 ptr = ebuf_data(nv->nv_ebuf, &size); 634 while (size > 0) { 635 PJDLOG_ASSERT(size >= sizeof(*nvh) + 2); 636 nvh = (struct nvhdr *)ptr; 637 PJDLOG_ASSERT(size >= NVH_SIZE(nvh)); 638 swap = ((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_NETWORK); 639 dsize = NVH_DSIZE(nvh); 640 data = NVH_DATA(nvh); 641 printf(" %s", nvh->nvh_name); 642 switch (nvh->nvh_type & NV_TYPE_MASK) { 643 case NV_TYPE_INT8: 644 printf("(int8): %jd", (intmax_t)(*(int8_t *)data)); 645 break; 646 case NV_TYPE_UINT8: 647 printf("(uint8): %ju", (uintmax_t)(*(uint8_t *)data)); 648 break; 649 case NV_TYPE_INT16: 650 printf("(int16): %jd", swap ? 651 (intmax_t)le16toh(*(int16_t *)(void *)data) : 652 (intmax_t)*(int16_t *)(void *)data); 653 break; 654 case NV_TYPE_UINT16: 655 printf("(uint16): %ju", swap ? 656 (uintmax_t)le16toh(*(uint16_t *)(void *)data) : 657 (uintmax_t)*(uint16_t *)(void *)data); 658 break; 659 case NV_TYPE_INT32: 660 printf("(int32): %jd", swap ? 661 (intmax_t)le32toh(*(int32_t *)(void *)data) : 662 (intmax_t)*(int32_t *)(void *)data); 663 break; 664 case NV_TYPE_UINT32: 665 printf("(uint32): %ju", swap ? 666 (uintmax_t)le32toh(*(uint32_t *)(void *)data) : 667 (uintmax_t)*(uint32_t *)(void *)data); 668 break; 669 case NV_TYPE_INT64: 670 printf("(int64): %jd", swap ? 671 (intmax_t)le64toh(*(int64_t *)(void *)data) : 672 (intmax_t)*(int64_t *)(void *)data); 673 break; 674 case NV_TYPE_UINT64: 675 printf("(uint64): %ju", swap ? 676 (uintmax_t)le64toh(*(uint64_t *)(void *)data) : 677 (uintmax_t)*(uint64_t *)(void *)data); 678 break; 679 case NV_TYPE_INT8_ARRAY: 680 printf("(int8 array):"); 681 for (ii = 0; ii < dsize; ii++) 682 printf(" %jd", (intmax_t)((int8_t *)data)[ii]); 683 break; 684 case NV_TYPE_UINT8_ARRAY: 685 printf("(uint8 array):"); 686 for (ii = 0; ii < dsize; ii++) 687 printf(" %ju", (uintmax_t)((uint8_t *)data)[ii]); 688 break; 689 case NV_TYPE_INT16_ARRAY: 690 printf("(int16 array):"); 691 for (ii = 0; ii < dsize / 2; ii++) { 692 printf(" %jd", swap ? 693 (intmax_t)le16toh(((int16_t *)(void *)data)[ii]) : 694 (intmax_t)((int16_t *)(void *)data)[ii]); 695 } 696 break; 697 case NV_TYPE_UINT16_ARRAY: 698 printf("(uint16 array):"); 699 for (ii = 0; ii < dsize / 2; ii++) { 700 printf(" %ju", swap ? 701 (uintmax_t)le16toh(((uint16_t *)(void *)data)[ii]) : 702 (uintmax_t)((uint16_t *)(void *)data)[ii]); 703 } 704 break; 705 case NV_TYPE_INT32_ARRAY: 706 printf("(int32 array):"); 707 for (ii = 0; ii < dsize / 4; ii++) { 708 printf(" %jd", swap ? 709 (intmax_t)le32toh(((int32_t *)(void *)data)[ii]) : 710 (intmax_t)((int32_t *)(void *)data)[ii]); 711 } 712 break; 713 case NV_TYPE_UINT32_ARRAY: 714 printf("(uint32 array):"); 715 for (ii = 0; ii < dsize / 4; ii++) { 716 printf(" %ju", swap ? 717 (uintmax_t)le32toh(((uint32_t *)(void *)data)[ii]) : 718 (uintmax_t)((uint32_t *)(void *)data)[ii]); 719 } 720 break; 721 case NV_TYPE_INT64_ARRAY: 722 printf("(int64 array):"); 723 for (ii = 0; ii < dsize / 8; ii++) { 724 printf(" %ju", swap ? 725 (uintmax_t)le64toh(((uint64_t *)(void *)data)[ii]) : 726 (uintmax_t)((uint64_t *)(void *)data)[ii]); 727 } 728 break; 729 case NV_TYPE_UINT64_ARRAY: 730 printf("(uint64 array):"); 731 for (ii = 0; ii < dsize / 8; ii++) { 732 printf(" %ju", swap ? 733 (uintmax_t)le64toh(((uint64_t *)(void *)data)[ii]) : 734 (uintmax_t)((uint64_t *)(void *)data)[ii]); 735 } 736 break; 737 case NV_TYPE_STRING: 738 printf("(string): %s", (char *)data); 739 break; 740 default: 741 PJDLOG_ABORT("invalid condition"); 742 } 743 printf("\n"); 744 ptr += NVH_SIZE(nvh); 745 size -= NVH_SIZE(nvh); 746 } 747 } 748 749 /* 750 * Local routines below. 751 */ 752 753 static void 754 nv_add(struct nv *nv, const unsigned char *value, size_t vsize, int type, 755 const char *name) 756 { 757 static unsigned char align[7]; 758 struct nvhdr *nvh; 759 size_t namesize; 760 761 if (nv == NULL) { 762 errno = ENOMEM; 763 return; 764 } 765 766 NV_CHECK(nv); 767 768 namesize = strlen(name) + 1; 769 770 nvh = malloc(sizeof(*nvh) + roundup2(namesize, 8)); 771 if (nvh == NULL) { 772 if (nv->nv_error == 0) 773 nv->nv_error = ENOMEM; 774 return; 775 } 776 nvh->nvh_type = NV_ORDER_HOST | type; 777 nvh->nvh_namesize = (uint8_t)namesize; 778 nvh->nvh_dsize = (uint32_t)vsize; 779 bcopy(name, nvh->nvh_name, namesize); 780 781 /* Add header first. */ 782 if (ebuf_add_tail(nv->nv_ebuf, nvh, NVH_HSIZE(nvh)) == -1) { 783 PJDLOG_ASSERT(errno != 0); 784 if (nv->nv_error == 0) 785 nv->nv_error = errno; 786 free(nvh); 787 return; 788 } 789 free(nvh); 790 /* Add the actual data. */ 791 if (ebuf_add_tail(nv->nv_ebuf, value, vsize) == -1) { 792 PJDLOG_ASSERT(errno != 0); 793 if (nv->nv_error == 0) 794 nv->nv_error = errno; 795 return; 796 } 797 /* Align the data (if needed). */ 798 vsize = roundup2(vsize, 8) - vsize; 799 if (vsize == 0) 800 return; 801 PJDLOG_ASSERT(vsize > 0 && vsize <= sizeof(align)); 802 if (ebuf_add_tail(nv->nv_ebuf, align, vsize) == -1) { 803 PJDLOG_ASSERT(errno != 0); 804 if (nv->nv_error == 0) 805 nv->nv_error = errno; 806 return; 807 } 808 } 809 810 static void 811 nv_addv(struct nv *nv, const unsigned char *value, size_t vsize, int type, 812 const char *namefmt, va_list nameap) 813 { 814 char name[255]; 815 size_t namesize; 816 817 namesize = vsnprintf(name, sizeof(name), namefmt, nameap); 818 PJDLOG_ASSERT(namesize > 0 && namesize < sizeof(name)); 819 820 nv_add(nv, value, vsize, type, name); 821 } 822 823 static struct nvhdr * 824 nv_find(struct nv *nv, int type, const char *namefmt, va_list nameap) 825 { 826 char name[255]; 827 struct nvhdr *nvh; 828 unsigned char *ptr; 829 size_t size, namesize; 830 831 if (nv == NULL) { 832 errno = ENOMEM; 833 return (NULL); 834 } 835 836 NV_CHECK(nv); 837 838 namesize = vsnprintf(name, sizeof(name), namefmt, nameap); 839 PJDLOG_ASSERT(namesize > 0 && namesize < sizeof(name)); 840 namesize++; 841 842 ptr = ebuf_data(nv->nv_ebuf, &size); 843 while (size > 0) { 844 PJDLOG_ASSERT(size >= sizeof(*nvh) + 2); 845 nvh = (struct nvhdr *)ptr; 846 PJDLOG_ASSERT(size >= NVH_SIZE(nvh)); 847 nv_swap(nvh, true); 848 if (strcmp(nvh->nvh_name, name) == 0) { 849 if (type != NV_TYPE_NONE && 850 (nvh->nvh_type & NV_TYPE_MASK) != type) { 851 errno = EINVAL; 852 if (nv->nv_error == 0) 853 nv->nv_error = EINVAL; 854 return (NULL); 855 } 856 return (nvh); 857 } 858 ptr += NVH_SIZE(nvh); 859 size -= NVH_SIZE(nvh); 860 } 861 errno = ENOENT; 862 if (nv->nv_error == 0) 863 nv->nv_error = ENOENT; 864 return (NULL); 865 } 866 867 static void 868 nv_swap(struct nvhdr *nvh, bool tohost) 869 { 870 unsigned char *data, *end, *p; 871 size_t vsize; 872 873 data = NVH_DATA(nvh); 874 if (tohost) { 875 if ((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST) 876 return; 877 nvh->nvh_dsize = le32toh(nvh->nvh_dsize); 878 end = data + nvh->nvh_dsize; 879 nvh->nvh_type &= ~NV_ORDER_MASK; 880 nvh->nvh_type |= NV_ORDER_HOST; 881 } else { 882 if ((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_NETWORK) 883 return; 884 end = data + nvh->nvh_dsize; 885 nvh->nvh_dsize = htole32(nvh->nvh_dsize); 886 nvh->nvh_type &= ~NV_ORDER_MASK; 887 nvh->nvh_type |= NV_ORDER_NETWORK; 888 } 889 890 vsize = 0; 891 892 switch (nvh->nvh_type & NV_TYPE_MASK) { 893 case NV_TYPE_INT8: 894 case NV_TYPE_UINT8: 895 case NV_TYPE_INT8_ARRAY: 896 case NV_TYPE_UINT8_ARRAY: 897 break; 898 case NV_TYPE_INT16: 899 case NV_TYPE_UINT16: 900 case NV_TYPE_INT16_ARRAY: 901 case NV_TYPE_UINT16_ARRAY: 902 if (vsize == 0) 903 vsize = 2; 904 /* FALLTHROUGH */ 905 case NV_TYPE_INT32: 906 case NV_TYPE_UINT32: 907 case NV_TYPE_INT32_ARRAY: 908 case NV_TYPE_UINT32_ARRAY: 909 if (vsize == 0) 910 vsize = 4; 911 /* FALLTHROUGH */ 912 case NV_TYPE_INT64: 913 case NV_TYPE_UINT64: 914 case NV_TYPE_INT64_ARRAY: 915 case NV_TYPE_UINT64_ARRAY: 916 if (vsize == 0) 917 vsize = 8; 918 for (p = data; p < end; p += vsize) { 919 if (tohost) { 920 switch (vsize) { 921 case 2: 922 *(uint16_t *)(void *)p = 923 le16toh(*(uint16_t *)(void *)p); 924 break; 925 case 4: 926 *(uint32_t *)(void *)p = 927 le32toh(*(uint32_t *)(void *)p); 928 break; 929 case 8: 930 *(uint64_t *)(void *)p = 931 le64toh(*(uint64_t *)(void *)p); 932 break; 933 default: 934 PJDLOG_ABORT("invalid condition"); 935 } 936 } else { 937 switch (vsize) { 938 case 2: 939 *(uint16_t *)(void *)p = 940 htole16(*(uint16_t *)(void *)p); 941 break; 942 case 4: 943 *(uint32_t *)(void *)p = 944 htole32(*(uint32_t *)(void *)p); 945 break; 946 case 8: 947 *(uint64_t *)(void *)p = 948 htole64(*(uint64_t *)(void *)p); 949 break; 950 default: 951 PJDLOG_ABORT("invalid condition"); 952 } 953 } 954 } 955 break; 956 case NV_TYPE_STRING: 957 break; 958 default: 959 PJDLOG_ABORT("unrecognized type"); 960 } 961 } 962