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