1204076Spjd/*- 2204076Spjd * Copyright (c) 2009-2010 The FreeBSD Foundation 3204076Spjd * All rights reserved. 4204076Spjd * 5204076Spjd * This software was developed by Pawel Jakub Dawidek under sponsorship from 6204076Spjd * the FreeBSD Foundation. 7204076Spjd * 8204076Spjd * Redistribution and use in source and binary forms, with or without 9204076Spjd * modification, are permitted provided that the following conditions 10204076Spjd * are met: 11204076Spjd * 1. Redistributions of source code must retain the above copyright 12204076Spjd * notice, this list of conditions and the following disclaimer. 13204076Spjd * 2. Redistributions in binary form must reproduce the above copyright 14204076Spjd * notice, this list of conditions and the following disclaimer in the 15204076Spjd * documentation and/or other materials provided with the distribution. 16204076Spjd * 17204076Spjd * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 18204076Spjd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19204076Spjd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20204076Spjd * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 21204076Spjd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22204076Spjd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23204076Spjd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24204076Spjd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25204076Spjd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26204076Spjd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27204076Spjd * SUCH DAMAGE. 28204076Spjd */ 29204076Spjd 30204076Spjd#include <sys/cdefs.h> 31204076Spjd__FBSDID("$FreeBSD$"); 32204076Spjd 33204076Spjd#include <sys/param.h> 34204076Spjd#include <sys/endian.h> 35204076Spjd 36204076Spjd#include <bitstring.h> 37204076Spjd#include <errno.h> 38204076Spjd#include <stdarg.h> 39204076Spjd#include <stdbool.h> 40204076Spjd#include <stdint.h> 41204076Spjd#include <stdlib.h> 42204076Spjd#include <string.h> 43204076Spjd#include <unistd.h> 44204076Spjd 45204076Spjd#include <ebuf.h> 46229509Strociny#include <pjdlog.h> 47204076Spjd 48229509Strociny#include "nv.h" 49229509Strociny 50229509Strociny#ifndef PJDLOG_ASSERT 51229509Strociny#include <assert.h> 52229509Strociny#define PJDLOG_ASSERT(...) assert(__VA_ARGS__) 53229509Strociny#endif 54229509Strociny#ifndef PJDLOG_ABORT 55229509Strociny#define PJDLOG_ABORT(...) abort() 56229509Strociny#endif 57229509Strociny 58214283Spjd#define NV_TYPE_NONE 0 59214283Spjd 60214282Spjd#define NV_TYPE_INT8 1 61214282Spjd#define NV_TYPE_UINT8 2 62214282Spjd#define NV_TYPE_INT16 3 63214282Spjd#define NV_TYPE_UINT16 4 64214282Spjd#define NV_TYPE_INT32 5 65214282Spjd#define NV_TYPE_UINT32 6 66214282Spjd#define NV_TYPE_INT64 7 67214282Spjd#define NV_TYPE_UINT64 8 68214282Spjd#define NV_TYPE_INT8_ARRAY 9 69214282Spjd#define NV_TYPE_UINT8_ARRAY 10 70214282Spjd#define NV_TYPE_INT16_ARRAY 11 71214282Spjd#define NV_TYPE_UINT16_ARRAY 12 72214282Spjd#define NV_TYPE_INT32_ARRAY 13 73214282Spjd#define NV_TYPE_UINT32_ARRAY 14 74214282Spjd#define NV_TYPE_INT64_ARRAY 15 75214282Spjd#define NV_TYPE_UINT64_ARRAY 16 76214282Spjd#define NV_TYPE_STRING 17 77214282Spjd 78214282Spjd#define NV_TYPE_MASK 0x7f 79214282Spjd#define NV_TYPE_FIRST NV_TYPE_INT8 80214282Spjd#define NV_TYPE_LAST NV_TYPE_STRING 81214282Spjd 82214282Spjd#define NV_ORDER_NETWORK 0x00 83214282Spjd#define NV_ORDER_HOST 0x80 84214282Spjd 85214282Spjd#define NV_ORDER_MASK 0x80 86214282Spjd 87204076Spjd#define NV_MAGIC 0xaea1e 88204076Spjdstruct nv { 89204076Spjd int nv_magic; 90204076Spjd int nv_error; 91204076Spjd struct ebuf *nv_ebuf; 92204076Spjd}; 93204076Spjd 94204076Spjdstruct nvhdr { 95204076Spjd uint8_t nvh_type; 96204076Spjd uint8_t nvh_namesize; 97204076Spjd uint32_t nvh_dsize; 98204076Spjd char nvh_name[0]; 99204076Spjd} __packed; 100204076Spjd#define NVH_DATA(nvh) ((unsigned char *)nvh + NVH_HSIZE(nvh)) 101204076Spjd#define NVH_HSIZE(nvh) \ 102204076Spjd (sizeof(struct nvhdr) + roundup2((nvh)->nvh_namesize, 8)) 103204076Spjd#define NVH_DSIZE(nvh) \ 104204076Spjd (((nvh)->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST ? \ 105204076Spjd (nvh)->nvh_dsize : \ 106204076Spjd le32toh((nvh)->nvh_dsize)) 107204076Spjd#define NVH_SIZE(nvh) (NVH_HSIZE(nvh) + roundup2(NVH_DSIZE(nvh), 8)) 108204076Spjd 109204076Spjd#define NV_CHECK(nv) do { \ 110229509Strociny PJDLOG_ASSERT((nv) != NULL); \ 111229509Strociny PJDLOG_ASSERT((nv)->nv_magic == NV_MAGIC); \ 112204076Spjd} while (0) 113204076Spjd 114204076Spjdstatic void nv_add(struct nv *nv, const unsigned char *value, size_t vsize, 115204076Spjd int type, const char *name); 116204076Spjdstatic void nv_addv(struct nv *nv, const unsigned char *value, size_t vsize, 117204076Spjd int type, const char *namefmt, va_list nameap); 118204076Spjdstatic struct nvhdr *nv_find(struct nv *nv, int type, const char *namefmt, 119204076Spjd va_list nameap); 120204076Spjdstatic void nv_swap(struct nvhdr *nvh, bool tohost); 121204076Spjd 122204076Spjd/* 123204076Spjd * Allocate and initialize new nv structure. 124204076Spjd * Return NULL in case of malloc(3) failure. 125204076Spjd */ 126204076Spjdstruct nv * 127204076Spjdnv_alloc(void) 128204076Spjd{ 129204076Spjd struct nv *nv; 130204076Spjd 131204076Spjd nv = malloc(sizeof(*nv)); 132204076Spjd if (nv == NULL) 133204076Spjd return (NULL); 134204076Spjd nv->nv_ebuf = ebuf_alloc(0); 135204076Spjd if (nv->nv_ebuf == NULL) { 136204076Spjd free(nv); 137204076Spjd return (NULL); 138204076Spjd } 139204076Spjd nv->nv_error = 0; 140204076Spjd nv->nv_magic = NV_MAGIC; 141204076Spjd return (nv); 142204076Spjd} 143204076Spjd 144204076Spjd/* 145204076Spjd * Free the given nv structure. 146204076Spjd */ 147204076Spjdvoid 148204076Spjdnv_free(struct nv *nv) 149204076Spjd{ 150204076Spjd 151204076Spjd if (nv == NULL) 152204076Spjd return; 153204076Spjd 154204076Spjd NV_CHECK(nv); 155204076Spjd 156204076Spjd nv->nv_magic = 0; 157204076Spjd ebuf_free(nv->nv_ebuf); 158204076Spjd free(nv); 159204076Spjd} 160204076Spjd 161204076Spjd/* 162204076Spjd * Return error for the given nv structure. 163204076Spjd */ 164204076Spjdint 165204076Spjdnv_error(const struct nv *nv) 166204076Spjd{ 167204076Spjd 168204076Spjd if (nv == NULL) 169204076Spjd return (ENOMEM); 170204076Spjd 171204076Spjd NV_CHECK(nv); 172204076Spjd 173204076Spjd return (nv->nv_error); 174204076Spjd} 175204076Spjd 176204076Spjd/* 177204076Spjd * Set error for the given nv structure and return previous error. 178204076Spjd */ 179204076Spjdint 180204076Spjdnv_set_error(struct nv *nv, int error) 181204076Spjd{ 182204076Spjd int preverr; 183204076Spjd 184204076Spjd if (nv == NULL) 185204076Spjd return (ENOMEM); 186204076Spjd 187204076Spjd NV_CHECK(nv); 188204076Spjd 189204076Spjd preverr = nv->nv_error; 190204076Spjd nv->nv_error = error; 191204076Spjd return (preverr); 192204076Spjd} 193204076Spjd 194204076Spjd/* 195204076Spjd * Validate correctness of the entire nv structure and all its elements. 196204076Spjd * If extrap is not NULL, store number of extra bytes at the end of the buffer. 197204076Spjd */ 198204076Spjdint 199204076Spjdnv_validate(struct nv *nv, size_t *extrap) 200204076Spjd{ 201204076Spjd struct nvhdr *nvh; 202204076Spjd unsigned char *data, *ptr; 203204076Spjd size_t dsize, size, vsize; 204204076Spjd int error; 205204076Spjd 206204076Spjd if (nv == NULL) { 207204076Spjd errno = ENOMEM; 208204076Spjd return (-1); 209204076Spjd } 210204076Spjd 211204076Spjd NV_CHECK(nv); 212229509Strociny PJDLOG_ASSERT(nv->nv_error == 0); 213204076Spjd 214204076Spjd /* TODO: Check that names are unique? */ 215204076Spjd 216204076Spjd error = 0; 217204076Spjd ptr = ebuf_data(nv->nv_ebuf, &size); 218204076Spjd while (size > 0) { 219204076Spjd /* 220204076Spjd * Zeros at the end of the buffer are acceptable. 221204076Spjd */ 222204076Spjd if (ptr[0] == '\0') 223204076Spjd break; 224204076Spjd /* 225204076Spjd * Minimum size at this point is size of nvhdr structure, one 226204076Spjd * character long name plus terminating '\0'. 227204076Spjd */ 228204076Spjd if (size < sizeof(*nvh) + 2) { 229204076Spjd error = EINVAL; 230204076Spjd break; 231204076Spjd } 232204076Spjd nvh = (struct nvhdr *)ptr; 233204076Spjd if (size < NVH_HSIZE(nvh)) { 234204076Spjd error = EINVAL; 235204076Spjd break; 236204076Spjd } 237204076Spjd if (nvh->nvh_name[nvh->nvh_namesize - 1] != '\0') { 238204076Spjd error = EINVAL; 239204076Spjd break; 240204076Spjd } 241204076Spjd if (strlen(nvh->nvh_name) != 242204076Spjd (size_t)(nvh->nvh_namesize - 1)) { 243204076Spjd error = EINVAL; 244204076Spjd break; 245204076Spjd } 246204076Spjd if ((nvh->nvh_type & NV_TYPE_MASK) < NV_TYPE_FIRST || 247204076Spjd (nvh->nvh_type & NV_TYPE_MASK) > NV_TYPE_LAST) { 248204076Spjd error = EINVAL; 249204076Spjd break; 250204076Spjd } 251204076Spjd dsize = NVH_DSIZE(nvh); 252204076Spjd if (dsize == 0) { 253204076Spjd error = EINVAL; 254204076Spjd break; 255204076Spjd } 256204076Spjd if (size < NVH_SIZE(nvh)) { 257204076Spjd error = EINVAL; 258204076Spjd break; 259204076Spjd } 260204076Spjd vsize = 0; 261204076Spjd switch (nvh->nvh_type & NV_TYPE_MASK) { 262204076Spjd case NV_TYPE_INT8: 263204076Spjd case NV_TYPE_UINT8: 264204076Spjd if (vsize == 0) 265204076Spjd vsize = 1; 266234294Strociny /* FALLTHROUGH */ 267204076Spjd case NV_TYPE_INT16: 268204076Spjd case NV_TYPE_UINT16: 269204076Spjd if (vsize == 0) 270204076Spjd vsize = 2; 271234294Strociny /* FALLTHROUGH */ 272204076Spjd case NV_TYPE_INT32: 273204076Spjd case NV_TYPE_UINT32: 274204076Spjd if (vsize == 0) 275204076Spjd vsize = 4; 276234294Strociny /* FALLTHROUGH */ 277204076Spjd case NV_TYPE_INT64: 278204076Spjd case NV_TYPE_UINT64: 279204076Spjd if (vsize == 0) 280204076Spjd vsize = 8; 281204076Spjd if (dsize != vsize) { 282204076Spjd error = EINVAL; 283204076Spjd break; 284204076Spjd } 285204076Spjd break; 286204076Spjd case NV_TYPE_INT8_ARRAY: 287204076Spjd case NV_TYPE_UINT8_ARRAY: 288204076Spjd break; 289204076Spjd case NV_TYPE_INT16_ARRAY: 290204076Spjd case NV_TYPE_UINT16_ARRAY: 291204076Spjd if (vsize == 0) 292204076Spjd vsize = 2; 293234294Strociny /* FALLTHROUGH */ 294204076Spjd case NV_TYPE_INT32_ARRAY: 295204076Spjd case NV_TYPE_UINT32_ARRAY: 296204076Spjd if (vsize == 0) 297204076Spjd vsize = 4; 298234294Strociny /* FALLTHROUGH */ 299204076Spjd case NV_TYPE_INT64_ARRAY: 300204076Spjd case NV_TYPE_UINT64_ARRAY: 301204076Spjd if (vsize == 0) 302204076Spjd vsize = 8; 303204076Spjd if ((dsize % vsize) != 0) { 304204076Spjd error = EINVAL; 305204076Spjd break; 306204076Spjd } 307204076Spjd break; 308204076Spjd case NV_TYPE_STRING: 309204076Spjd data = NVH_DATA(nvh); 310204076Spjd if (data[dsize - 1] != '\0') { 311204076Spjd error = EINVAL; 312204076Spjd break; 313204076Spjd } 314204076Spjd if (strlen((char *)data) != dsize - 1) { 315204076Spjd error = EINVAL; 316204076Spjd break; 317204076Spjd } 318204076Spjd break; 319204076Spjd default: 320229509Strociny PJDLOG_ABORT("invalid condition"); 321204076Spjd } 322204076Spjd if (error != 0) 323204076Spjd break; 324204076Spjd ptr += NVH_SIZE(nvh); 325204076Spjd size -= NVH_SIZE(nvh); 326204076Spjd } 327204076Spjd if (error != 0) { 328204076Spjd errno = error; 329204076Spjd if (nv->nv_error == 0) 330204076Spjd nv->nv_error = error; 331204076Spjd return (-1); 332204076Spjd } 333204076Spjd if (extrap != NULL) 334204076Spjd *extrap = size; 335204076Spjd return (0); 336204076Spjd} 337204076Spjd 338204076Spjd/* 339204076Spjd * Convert the given nv structure to network byte order and return ebuf 340204076Spjd * structure. 341204076Spjd */ 342204076Spjdstruct ebuf * 343204076Spjdnv_hton(struct nv *nv) 344204076Spjd{ 345204076Spjd struct nvhdr *nvh; 346204076Spjd unsigned char *ptr; 347204076Spjd size_t size; 348204076Spjd 349204076Spjd NV_CHECK(nv); 350229509Strociny PJDLOG_ASSERT(nv->nv_error == 0); 351204076Spjd 352204076Spjd ptr = ebuf_data(nv->nv_ebuf, &size); 353204076Spjd while (size > 0) { 354204076Spjd /* 355204076Spjd * Minimum size at this point is size of nvhdr structure, 356204076Spjd * one character long name plus terminating '\0'. 357204076Spjd */ 358229509Strociny PJDLOG_ASSERT(size >= sizeof(*nvh) + 2); 359204076Spjd nvh = (struct nvhdr *)ptr; 360229509Strociny PJDLOG_ASSERT(NVH_SIZE(nvh) <= size); 361204076Spjd nv_swap(nvh, false); 362204076Spjd ptr += NVH_SIZE(nvh); 363204076Spjd size -= NVH_SIZE(nvh); 364204076Spjd } 365204076Spjd 366204076Spjd return (nv->nv_ebuf); 367204076Spjd} 368204076Spjd 369204076Spjd/* 370204076Spjd * Create nv structure based on ebuf received from the network. 371204076Spjd */ 372204076Spjdstruct nv * 373204076Spjdnv_ntoh(struct ebuf *eb) 374204076Spjd{ 375204076Spjd struct nv *nv; 376204076Spjd size_t extra; 377204076Spjd int rerrno; 378204076Spjd 379229509Strociny PJDLOG_ASSERT(eb != NULL); 380204076Spjd 381204076Spjd nv = malloc(sizeof(*nv)); 382204076Spjd if (nv == NULL) 383204076Spjd return (NULL); 384204076Spjd nv->nv_error = 0; 385204076Spjd nv->nv_ebuf = eb; 386204076Spjd nv->nv_magic = NV_MAGIC; 387204076Spjd 388231017Strociny if (nv_validate(nv, &extra) == -1) { 389204076Spjd rerrno = errno; 390204076Spjd nv->nv_magic = 0; 391204076Spjd free(nv); 392204076Spjd errno = rerrno; 393204076Spjd return (NULL); 394204076Spjd } 395204076Spjd /* 396204076Spjd * Remove extra zeros at the end of the buffer. 397204076Spjd */ 398204076Spjd ebuf_del_tail(eb, extra); 399204076Spjd 400204076Spjd return (nv); 401204076Spjd} 402204076Spjd 403204076Spjd#define NV_DEFINE_ADD(type, TYPE) \ 404204076Spjdvoid \ 405204076Spjdnv_add_##type(struct nv *nv, type##_t value, const char *namefmt, ...) \ 406204076Spjd{ \ 407204076Spjd va_list nameap; \ 408204076Spjd \ 409204076Spjd va_start(nameap, namefmt); \ 410204076Spjd nv_addv(nv, (unsigned char *)&value, sizeof(value), \ 411204076Spjd NV_TYPE_##TYPE, namefmt, nameap); \ 412204076Spjd va_end(nameap); \ 413204076Spjd} 414204076Spjd 415204076SpjdNV_DEFINE_ADD(int8, INT8) 416204076SpjdNV_DEFINE_ADD(uint8, UINT8) 417204076SpjdNV_DEFINE_ADD(int16, INT16) 418204076SpjdNV_DEFINE_ADD(uint16, UINT16) 419204076SpjdNV_DEFINE_ADD(int32, INT32) 420204076SpjdNV_DEFINE_ADD(uint32, UINT32) 421204076SpjdNV_DEFINE_ADD(int64, INT64) 422204076SpjdNV_DEFINE_ADD(uint64, UINT64) 423204076Spjd 424204076Spjd#undef NV_DEFINE_ADD 425204076Spjd 426204076Spjd#define NV_DEFINE_ADD_ARRAY(type, TYPE) \ 427204076Spjdvoid \ 428204076Spjdnv_add_##type##_array(struct nv *nv, const type##_t *value, \ 429204076Spjd size_t nsize, const char *namefmt, ...) \ 430204076Spjd{ \ 431204076Spjd va_list nameap; \ 432204076Spjd \ 433204076Spjd va_start(nameap, namefmt); \ 434204076Spjd nv_addv(nv, (const unsigned char *)value, \ 435204076Spjd sizeof(value[0]) * nsize, NV_TYPE_##TYPE##_ARRAY, namefmt, \ 436204076Spjd nameap); \ 437204076Spjd va_end(nameap); \ 438204076Spjd} 439204076Spjd 440204076SpjdNV_DEFINE_ADD_ARRAY(int8, INT8) 441204076SpjdNV_DEFINE_ADD_ARRAY(uint8, UINT8) 442204076SpjdNV_DEFINE_ADD_ARRAY(int16, INT16) 443204076SpjdNV_DEFINE_ADD_ARRAY(uint16, UINT16) 444204076SpjdNV_DEFINE_ADD_ARRAY(int32, INT32) 445204076SpjdNV_DEFINE_ADD_ARRAY(uint32, UINT32) 446204076SpjdNV_DEFINE_ADD_ARRAY(int64, INT64) 447204076SpjdNV_DEFINE_ADD_ARRAY(uint64, UINT64) 448204076Spjd 449204076Spjd#undef NV_DEFINE_ADD_ARRAY 450204076Spjd 451204076Spjdvoid 452204076Spjdnv_add_string(struct nv *nv, const char *value, const char *namefmt, ...) 453204076Spjd{ 454204076Spjd va_list nameap; 455204076Spjd size_t size; 456204076Spjd 457204076Spjd size = strlen(value) + 1; 458204076Spjd 459204076Spjd va_start(nameap, namefmt); 460204076Spjd nv_addv(nv, (const unsigned char *)value, size, NV_TYPE_STRING, 461204076Spjd namefmt, nameap); 462204076Spjd va_end(nameap); 463204076Spjd} 464204076Spjd 465204076Spjdvoid 466204076Spjdnv_add_stringf(struct nv *nv, const char *name, const char *valuefmt, ...) 467204076Spjd{ 468204076Spjd va_list valueap; 469204076Spjd 470204076Spjd va_start(valueap, valuefmt); 471204076Spjd nv_add_stringv(nv, name, valuefmt, valueap); 472204076Spjd va_end(valueap); 473204076Spjd} 474204076Spjd 475204076Spjdvoid 476204076Spjdnv_add_stringv(struct nv *nv, const char *name, const char *valuefmt, 477204076Spjd va_list valueap) 478204076Spjd{ 479204076Spjd char *value; 480204076Spjd ssize_t size; 481204076Spjd 482204076Spjd size = vasprintf(&value, valuefmt, valueap); 483231017Strociny if (size == -1) { 484204076Spjd if (nv->nv_error == 0) 485204076Spjd nv->nv_error = ENOMEM; 486204076Spjd return; 487204076Spjd } 488204076Spjd size++; 489204076Spjd nv_add(nv, (const unsigned char *)value, size, NV_TYPE_STRING, name); 490204076Spjd free(value); 491204076Spjd} 492204076Spjd 493204076Spjd#define NV_DEFINE_GET(type, TYPE) \ 494204076Spjdtype##_t \ 495204076Spjdnv_get_##type(struct nv *nv, const char *namefmt, ...) \ 496204076Spjd{ \ 497204076Spjd struct nvhdr *nvh; \ 498204076Spjd va_list nameap; \ 499204076Spjd type##_t value; \ 500204076Spjd \ 501204076Spjd va_start(nameap, namefmt); \ 502204076Spjd nvh = nv_find(nv, NV_TYPE_##TYPE, namefmt, nameap); \ 503204076Spjd va_end(nameap); \ 504204076Spjd if (nvh == NULL) \ 505204076Spjd return (0); \ 506229509Strociny PJDLOG_ASSERT((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST);\ 507229509Strociny PJDLOG_ASSERT(sizeof(value) == nvh->nvh_dsize); \ 508204076Spjd bcopy(NVH_DATA(nvh), &value, sizeof(value)); \ 509204076Spjd \ 510204076Spjd return (value); \ 511204076Spjd} 512204076Spjd 513204076SpjdNV_DEFINE_GET(int8, INT8) 514204076SpjdNV_DEFINE_GET(uint8, UINT8) 515204076SpjdNV_DEFINE_GET(int16, INT16) 516204076SpjdNV_DEFINE_GET(uint16, UINT16) 517204076SpjdNV_DEFINE_GET(int32, INT32) 518204076SpjdNV_DEFINE_GET(uint32, UINT32) 519204076SpjdNV_DEFINE_GET(int64, INT64) 520204076SpjdNV_DEFINE_GET(uint64, UINT64) 521204076Spjd 522204076Spjd#undef NV_DEFINE_GET 523204076Spjd 524204076Spjd#define NV_DEFINE_GET_ARRAY(type, TYPE) \ 525204076Spjdconst type##_t * \ 526204076Spjdnv_get_##type##_array(struct nv *nv, size_t *sizep, \ 527204076Spjd const char *namefmt, ...) \ 528204076Spjd{ \ 529204076Spjd struct nvhdr *nvh; \ 530204076Spjd va_list nameap; \ 531204076Spjd \ 532204076Spjd va_start(nameap, namefmt); \ 533204076Spjd nvh = nv_find(nv, NV_TYPE_##TYPE##_ARRAY, namefmt, nameap); \ 534204076Spjd va_end(nameap); \ 535204076Spjd if (nvh == NULL) \ 536204076Spjd return (NULL); \ 537229509Strociny PJDLOG_ASSERT((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST);\ 538229509Strociny PJDLOG_ASSERT((nvh->nvh_dsize % sizeof(type##_t)) == 0); \ 539204076Spjd if (sizep != NULL) \ 540204076Spjd *sizep = nvh->nvh_dsize / sizeof(type##_t); \ 541204076Spjd return ((type##_t *)(void *)NVH_DATA(nvh)); \ 542204076Spjd} 543204076Spjd 544204076SpjdNV_DEFINE_GET_ARRAY(int8, INT8) 545204076SpjdNV_DEFINE_GET_ARRAY(uint8, UINT8) 546204076SpjdNV_DEFINE_GET_ARRAY(int16, INT16) 547204076SpjdNV_DEFINE_GET_ARRAY(uint16, UINT16) 548204076SpjdNV_DEFINE_GET_ARRAY(int32, INT32) 549204076SpjdNV_DEFINE_GET_ARRAY(uint32, UINT32) 550204076SpjdNV_DEFINE_GET_ARRAY(int64, INT64) 551204076SpjdNV_DEFINE_GET_ARRAY(uint64, UINT64) 552204076Spjd 553204076Spjd#undef NV_DEFINE_GET_ARRAY 554204076Spjd 555204076Spjdconst char * 556204076Spjdnv_get_string(struct nv *nv, const char *namefmt, ...) 557204076Spjd{ 558204076Spjd struct nvhdr *nvh; 559204076Spjd va_list nameap; 560204076Spjd char *str; 561204076Spjd 562204076Spjd va_start(nameap, namefmt); 563204076Spjd nvh = nv_find(nv, NV_TYPE_STRING, namefmt, nameap); 564204076Spjd va_end(nameap); 565204076Spjd if (nvh == NULL) 566204076Spjd return (NULL); 567229509Strociny PJDLOG_ASSERT((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST); 568229509Strociny PJDLOG_ASSERT(nvh->nvh_dsize >= 1); 569260007Strociny str = (char *)NVH_DATA(nvh); 570229509Strociny PJDLOG_ASSERT(str[nvh->nvh_dsize - 1] == '\0'); 571229509Strociny PJDLOG_ASSERT(strlen(str) == nvh->nvh_dsize - 1); 572204076Spjd return (str); 573204076Spjd} 574204076Spjd 575217732Spjdstatic bool 576217732Spjdnv_vexists(struct nv *nv, const char *namefmt, va_list nameap) 577214283Spjd{ 578214283Spjd struct nvhdr *nvh; 579214283Spjd int snverror, serrno; 580214283Spjd 581214283Spjd if (nv == NULL) 582214283Spjd return (false); 583214283Spjd 584214283Spjd serrno = errno; 585214283Spjd snverror = nv->nv_error; 586214283Spjd 587214283Spjd nvh = nv_find(nv, NV_TYPE_NONE, namefmt, nameap); 588214283Spjd 589214283Spjd errno = serrno; 590214283Spjd nv->nv_error = snverror; 591214283Spjd 592214283Spjd return (nvh != NULL); 593214283Spjd} 594214283Spjd 595217732Spjdbool 596217732Spjdnv_exists(struct nv *nv, const char *namefmt, ...) 597217732Spjd{ 598217732Spjd va_list nameap; 599217732Spjd bool ret; 600217732Spjd 601217732Spjd va_start(nameap, namefmt); 602217732Spjd ret = nv_vexists(nv, namefmt, nameap); 603217732Spjd va_end(nameap); 604217732Spjd 605217732Spjd return (ret); 606217732Spjd} 607217732Spjd 608217732Spjdvoid 609217732Spjdnv_assert(struct nv *nv, const char *namefmt, ...) 610217732Spjd{ 611217732Spjd va_list nameap; 612217732Spjd 613217732Spjd va_start(nameap, namefmt); 614229509Strociny PJDLOG_ASSERT(nv_vexists(nv, namefmt, nameap)); 615217732Spjd va_end(nameap); 616217732Spjd} 617217732Spjd 618204076Spjd/* 619204076Spjd * Dump content of the nv structure. 620204076Spjd */ 621204076Spjdvoid 622204076Spjdnv_dump(struct nv *nv) 623204076Spjd{ 624204076Spjd struct nvhdr *nvh; 625204076Spjd unsigned char *data, *ptr; 626204076Spjd size_t dsize, size; 627204076Spjd unsigned int ii; 628204076Spjd bool swap; 629204076Spjd 630231017Strociny if (nv_validate(nv, NULL) == -1) { 631204076Spjd printf("error: %d\n", errno); 632204076Spjd return; 633204076Spjd } 634204076Spjd 635204076Spjd NV_CHECK(nv); 636229509Strociny PJDLOG_ASSERT(nv->nv_error == 0); 637204076Spjd 638204076Spjd ptr = ebuf_data(nv->nv_ebuf, &size); 639204076Spjd while (size > 0) { 640229509Strociny PJDLOG_ASSERT(size >= sizeof(*nvh) + 2); 641204076Spjd nvh = (struct nvhdr *)ptr; 642229509Strociny PJDLOG_ASSERT(size >= NVH_SIZE(nvh)); 643204076Spjd swap = ((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_NETWORK); 644204076Spjd dsize = NVH_DSIZE(nvh); 645204076Spjd data = NVH_DATA(nvh); 646204076Spjd printf(" %s", nvh->nvh_name); 647204076Spjd switch (nvh->nvh_type & NV_TYPE_MASK) { 648204076Spjd case NV_TYPE_INT8: 649204076Spjd printf("(int8): %jd", (intmax_t)(*(int8_t *)data)); 650204076Spjd break; 651204076Spjd case NV_TYPE_UINT8: 652204076Spjd printf("(uint8): %ju", (uintmax_t)(*(uint8_t *)data)); 653204076Spjd break; 654204076Spjd case NV_TYPE_INT16: 655204076Spjd printf("(int16): %jd", swap ? 656204076Spjd (intmax_t)le16toh(*(int16_t *)(void *)data) : 657204076Spjd (intmax_t)*(int16_t *)(void *)data); 658204076Spjd break; 659204076Spjd case NV_TYPE_UINT16: 660204076Spjd printf("(uint16): %ju", swap ? 661204076Spjd (uintmax_t)le16toh(*(uint16_t *)(void *)data) : 662204076Spjd (uintmax_t)*(uint16_t *)(void *)data); 663204076Spjd break; 664204076Spjd case NV_TYPE_INT32: 665204076Spjd printf("(int32): %jd", swap ? 666204076Spjd (intmax_t)le32toh(*(int32_t *)(void *)data) : 667204076Spjd (intmax_t)*(int32_t *)(void *)data); 668204076Spjd break; 669204076Spjd case NV_TYPE_UINT32: 670204076Spjd printf("(uint32): %ju", swap ? 671204076Spjd (uintmax_t)le32toh(*(uint32_t *)(void *)data) : 672204076Spjd (uintmax_t)*(uint32_t *)(void *)data); 673204076Spjd break; 674204076Spjd case NV_TYPE_INT64: 675204076Spjd printf("(int64): %jd", swap ? 676204076Spjd (intmax_t)le64toh(*(int64_t *)(void *)data) : 677204076Spjd (intmax_t)*(int64_t *)(void *)data); 678204076Spjd break; 679204076Spjd case NV_TYPE_UINT64: 680204076Spjd printf("(uint64): %ju", swap ? 681204076Spjd (uintmax_t)le64toh(*(uint64_t *)(void *)data) : 682204076Spjd (uintmax_t)*(uint64_t *)(void *)data); 683204076Spjd break; 684204076Spjd case NV_TYPE_INT8_ARRAY: 685204076Spjd printf("(int8 array):"); 686204076Spjd for (ii = 0; ii < dsize; ii++) 687204076Spjd printf(" %jd", (intmax_t)((int8_t *)data)[ii]); 688204076Spjd break; 689204076Spjd case NV_TYPE_UINT8_ARRAY: 690204076Spjd printf("(uint8 array):"); 691204076Spjd for (ii = 0; ii < dsize; ii++) 692204076Spjd printf(" %ju", (uintmax_t)((uint8_t *)data)[ii]); 693204076Spjd break; 694204076Spjd case NV_TYPE_INT16_ARRAY: 695204076Spjd printf("(int16 array):"); 696204076Spjd for (ii = 0; ii < dsize / 2; ii++) { 697204076Spjd printf(" %jd", swap ? 698204076Spjd (intmax_t)le16toh(((int16_t *)(void *)data)[ii]) : 699204076Spjd (intmax_t)((int16_t *)(void *)data)[ii]); 700204076Spjd } 701204076Spjd break; 702204076Spjd case NV_TYPE_UINT16_ARRAY: 703204076Spjd printf("(uint16 array):"); 704204076Spjd for (ii = 0; ii < dsize / 2; ii++) { 705204076Spjd printf(" %ju", swap ? 706204076Spjd (uintmax_t)le16toh(((uint16_t *)(void *)data)[ii]) : 707204076Spjd (uintmax_t)((uint16_t *)(void *)data)[ii]); 708204076Spjd } 709204076Spjd break; 710204076Spjd case NV_TYPE_INT32_ARRAY: 711204076Spjd printf("(int32 array):"); 712204076Spjd for (ii = 0; ii < dsize / 4; ii++) { 713204076Spjd printf(" %jd", swap ? 714204076Spjd (intmax_t)le32toh(((int32_t *)(void *)data)[ii]) : 715204076Spjd (intmax_t)((int32_t *)(void *)data)[ii]); 716204076Spjd } 717204076Spjd break; 718204076Spjd case NV_TYPE_UINT32_ARRAY: 719204076Spjd printf("(uint32 array):"); 720204076Spjd for (ii = 0; ii < dsize / 4; ii++) { 721204076Spjd printf(" %ju", swap ? 722204076Spjd (uintmax_t)le32toh(((uint32_t *)(void *)data)[ii]) : 723204076Spjd (uintmax_t)((uint32_t *)(void *)data)[ii]); 724204076Spjd } 725204076Spjd break; 726204076Spjd case NV_TYPE_INT64_ARRAY: 727204076Spjd printf("(int64 array):"); 728204076Spjd for (ii = 0; ii < dsize / 8; ii++) { 729204076Spjd printf(" %ju", swap ? 730204076Spjd (uintmax_t)le64toh(((uint64_t *)(void *)data)[ii]) : 731204076Spjd (uintmax_t)((uint64_t *)(void *)data)[ii]); 732204076Spjd } 733204076Spjd break; 734204076Spjd case NV_TYPE_UINT64_ARRAY: 735204076Spjd printf("(uint64 array):"); 736204076Spjd for (ii = 0; ii < dsize / 8; ii++) { 737204076Spjd printf(" %ju", swap ? 738204076Spjd (uintmax_t)le64toh(((uint64_t *)(void *)data)[ii]) : 739204076Spjd (uintmax_t)((uint64_t *)(void *)data)[ii]); 740204076Spjd } 741204076Spjd break; 742204076Spjd case NV_TYPE_STRING: 743204076Spjd printf("(string): %s", (char *)data); 744204076Spjd break; 745204076Spjd default: 746229509Strociny PJDLOG_ABORT("invalid condition"); 747204076Spjd } 748204076Spjd printf("\n"); 749204076Spjd ptr += NVH_SIZE(nvh); 750204076Spjd size -= NVH_SIZE(nvh); 751204076Spjd } 752204076Spjd} 753204076Spjd 754204076Spjd/* 755204076Spjd * Local routines below. 756204076Spjd */ 757204076Spjd 758204076Spjdstatic void 759204076Spjdnv_add(struct nv *nv, const unsigned char *value, size_t vsize, int type, 760204076Spjd const char *name) 761204076Spjd{ 762204076Spjd static unsigned char align[7]; 763204076Spjd struct nvhdr *nvh; 764204076Spjd size_t namesize; 765204076Spjd 766204076Spjd if (nv == NULL) { 767204076Spjd errno = ENOMEM; 768204076Spjd return; 769204076Spjd } 770204076Spjd 771204076Spjd NV_CHECK(nv); 772204076Spjd 773204076Spjd namesize = strlen(name) + 1; 774204076Spjd 775204076Spjd nvh = malloc(sizeof(*nvh) + roundup2(namesize, 8)); 776204076Spjd if (nvh == NULL) { 777204076Spjd if (nv->nv_error == 0) 778204076Spjd nv->nv_error = ENOMEM; 779204076Spjd return; 780204076Spjd } 781204076Spjd nvh->nvh_type = NV_ORDER_HOST | type; 782204076Spjd nvh->nvh_namesize = (uint8_t)namesize; 783204076Spjd nvh->nvh_dsize = (uint32_t)vsize; 784204076Spjd bcopy(name, nvh->nvh_name, namesize); 785204076Spjd 786204076Spjd /* Add header first. */ 787231017Strociny if (ebuf_add_tail(nv->nv_ebuf, nvh, NVH_HSIZE(nvh)) == -1) { 788229509Strociny PJDLOG_ASSERT(errno != 0); 789204076Spjd if (nv->nv_error == 0) 790204076Spjd nv->nv_error = errno; 791209180Spjd free(nvh); 792204076Spjd return; 793204076Spjd } 794209180Spjd free(nvh); 795204076Spjd /* Add the actual data. */ 796231017Strociny if (ebuf_add_tail(nv->nv_ebuf, value, vsize) == -1) { 797229509Strociny PJDLOG_ASSERT(errno != 0); 798204076Spjd if (nv->nv_error == 0) 799204076Spjd nv->nv_error = errno; 800204076Spjd return; 801204076Spjd } 802204076Spjd /* Align the data (if needed). */ 803204076Spjd vsize = roundup2(vsize, 8) - vsize; 804204076Spjd if (vsize == 0) 805204076Spjd return; 806229509Strociny PJDLOG_ASSERT(vsize > 0 && vsize <= sizeof(align)); 807231017Strociny if (ebuf_add_tail(nv->nv_ebuf, align, vsize) == -1) { 808229509Strociny PJDLOG_ASSERT(errno != 0); 809204076Spjd if (nv->nv_error == 0) 810204076Spjd nv->nv_error = errno; 811204076Spjd return; 812204076Spjd } 813204076Spjd} 814204076Spjd 815204076Spjdstatic void 816204076Spjdnv_addv(struct nv *nv, const unsigned char *value, size_t vsize, int type, 817204076Spjd const char *namefmt, va_list nameap) 818204076Spjd{ 819204076Spjd char name[255]; 820204076Spjd size_t namesize; 821204076Spjd 822204076Spjd namesize = vsnprintf(name, sizeof(name), namefmt, nameap); 823229509Strociny PJDLOG_ASSERT(namesize > 0 && namesize < sizeof(name)); 824204076Spjd 825204076Spjd nv_add(nv, value, vsize, type, name); 826204076Spjd} 827204076Spjd 828204076Spjdstatic struct nvhdr * 829204076Spjdnv_find(struct nv *nv, int type, const char *namefmt, va_list nameap) 830204076Spjd{ 831204076Spjd char name[255]; 832204076Spjd struct nvhdr *nvh; 833204076Spjd unsigned char *ptr; 834204076Spjd size_t size, namesize; 835204076Spjd 836204076Spjd if (nv == NULL) { 837204076Spjd errno = ENOMEM; 838204076Spjd return (NULL); 839204076Spjd } 840204076Spjd 841204076Spjd NV_CHECK(nv); 842204076Spjd 843204076Spjd namesize = vsnprintf(name, sizeof(name), namefmt, nameap); 844229509Strociny PJDLOG_ASSERT(namesize > 0 && namesize < sizeof(name)); 845204076Spjd namesize++; 846204076Spjd 847204076Spjd ptr = ebuf_data(nv->nv_ebuf, &size); 848204076Spjd while (size > 0) { 849229509Strociny PJDLOG_ASSERT(size >= sizeof(*nvh) + 2); 850204076Spjd nvh = (struct nvhdr *)ptr; 851229509Strociny PJDLOG_ASSERT(size >= NVH_SIZE(nvh)); 852204076Spjd nv_swap(nvh, true); 853204076Spjd if (strcmp(nvh->nvh_name, name) == 0) { 854214283Spjd if (type != NV_TYPE_NONE && 855214283Spjd (nvh->nvh_type & NV_TYPE_MASK) != type) { 856204076Spjd errno = EINVAL; 857204076Spjd if (nv->nv_error == 0) 858204076Spjd nv->nv_error = EINVAL; 859204076Spjd return (NULL); 860204076Spjd } 861204076Spjd return (nvh); 862204076Spjd } 863204076Spjd ptr += NVH_SIZE(nvh); 864204076Spjd size -= NVH_SIZE(nvh); 865204076Spjd } 866204076Spjd errno = ENOENT; 867204076Spjd if (nv->nv_error == 0) 868204076Spjd nv->nv_error = ENOENT; 869204076Spjd return (NULL); 870204076Spjd} 871204076Spjd 872204076Spjdstatic void 873204076Spjdnv_swap(struct nvhdr *nvh, bool tohost) 874204076Spjd{ 875204076Spjd unsigned char *data, *end, *p; 876204076Spjd size_t vsize; 877204076Spjd 878204076Spjd data = NVH_DATA(nvh); 879204076Spjd if (tohost) { 880204076Spjd if ((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST) 881204076Spjd return; 882204076Spjd nvh->nvh_dsize = le32toh(nvh->nvh_dsize); 883204076Spjd end = data + nvh->nvh_dsize; 884204076Spjd nvh->nvh_type &= ~NV_ORDER_MASK; 885204076Spjd nvh->nvh_type |= NV_ORDER_HOST; 886204076Spjd } else { 887204076Spjd if ((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_NETWORK) 888204076Spjd return; 889204076Spjd end = data + nvh->nvh_dsize; 890204076Spjd nvh->nvh_dsize = htole32(nvh->nvh_dsize); 891204076Spjd nvh->nvh_type &= ~NV_ORDER_MASK; 892204076Spjd nvh->nvh_type |= NV_ORDER_NETWORK; 893204076Spjd } 894204076Spjd 895204076Spjd vsize = 0; 896204076Spjd 897204076Spjd switch (nvh->nvh_type & NV_TYPE_MASK) { 898204076Spjd case NV_TYPE_INT8: 899204076Spjd case NV_TYPE_UINT8: 900204076Spjd case NV_TYPE_INT8_ARRAY: 901204076Spjd case NV_TYPE_UINT8_ARRAY: 902204076Spjd break; 903204076Spjd case NV_TYPE_INT16: 904204076Spjd case NV_TYPE_UINT16: 905204076Spjd case NV_TYPE_INT16_ARRAY: 906204076Spjd case NV_TYPE_UINT16_ARRAY: 907204076Spjd if (vsize == 0) 908204076Spjd vsize = 2; 909234294Strociny /* FALLTHROUGH */ 910204076Spjd case NV_TYPE_INT32: 911204076Spjd case NV_TYPE_UINT32: 912204076Spjd case NV_TYPE_INT32_ARRAY: 913204076Spjd case NV_TYPE_UINT32_ARRAY: 914204076Spjd if (vsize == 0) 915204076Spjd vsize = 4; 916234294Strociny /* FALLTHROUGH */ 917204076Spjd case NV_TYPE_INT64: 918204076Spjd case NV_TYPE_UINT64: 919204076Spjd case NV_TYPE_INT64_ARRAY: 920204076Spjd case NV_TYPE_UINT64_ARRAY: 921204076Spjd if (vsize == 0) 922204076Spjd vsize = 8; 923204076Spjd for (p = data; p < end; p += vsize) { 924204076Spjd if (tohost) { 925204076Spjd switch (vsize) { 926204076Spjd case 2: 927204076Spjd *(uint16_t *)(void *)p = 928204076Spjd le16toh(*(uint16_t *)(void *)p); 929204076Spjd break; 930204076Spjd case 4: 931204076Spjd *(uint32_t *)(void *)p = 932204076Spjd le32toh(*(uint32_t *)(void *)p); 933204076Spjd break; 934204076Spjd case 8: 935204076Spjd *(uint64_t *)(void *)p = 936204076Spjd le64toh(*(uint64_t *)(void *)p); 937204076Spjd break; 938204076Spjd default: 939229509Strociny PJDLOG_ABORT("invalid condition"); 940204076Spjd } 941204076Spjd } else { 942204076Spjd switch (vsize) { 943204076Spjd case 2: 944204076Spjd *(uint16_t *)(void *)p = 945204076Spjd htole16(*(uint16_t *)(void *)p); 946204076Spjd break; 947204076Spjd case 4: 948204076Spjd *(uint32_t *)(void *)p = 949204076Spjd htole32(*(uint32_t *)(void *)p); 950204076Spjd break; 951204076Spjd case 8: 952204076Spjd *(uint64_t *)(void *)p = 953204076Spjd htole64(*(uint64_t *)(void *)p); 954204076Spjd break; 955204076Spjd default: 956229509Strociny PJDLOG_ABORT("invalid condition"); 957204076Spjd } 958204076Spjd } 959204076Spjd } 960204076Spjd break; 961204076Spjd case NV_TYPE_STRING: 962204076Spjd break; 963204076Spjd default: 964229509Strociny PJDLOG_ABORT("unrecognized type"); 965204076Spjd } 966204076Spjd} 967