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