nv.c revision 214282
1214501Srpaulo/*- 2214501Srpaulo * Copyright (c) 2009-2010 The FreeBSD Foundation 3214501Srpaulo * All rights reserved. 4214501Srpaulo * 5252726Srpaulo * This software was developed by Pawel Jakub Dawidek under sponsorship from 6252726Srpaulo * the FreeBSD Foundation. 7214501Srpaulo * 8214501Srpaulo * Redistribution and use in source and binary forms, with or without 9214501Srpaulo * modification, are permitted provided that the following conditions 10214501Srpaulo * are met: 11214501Srpaulo * 1. Redistributions of source code must retain the above copyright 12214501Srpaulo * notice, this list of conditions and the following disclaimer. 13252726Srpaulo * 2. Redistributions in binary form must reproduce the above copyright 14214501Srpaulo * notice, this list of conditions and the following disclaimer in the 15214501Srpaulo * documentation and/or other materials provided with the distribution. 16214501Srpaulo * 17214501Srpaulo * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 18214501Srpaulo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19214501Srpaulo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20214501Srpaulo * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 21214501Srpaulo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22214501Srpaulo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23214501Srpaulo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24214501Srpaulo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25214501Srpaulo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26214501Srpaulo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27214501Srpaulo * SUCH DAMAGE. 28214501Srpaulo */ 29214501Srpaulo 30214501Srpaulo#include <sys/cdefs.h> 31214501Srpaulo__FBSDID("$FreeBSD: head/sbin/hastd/nv.c 214282 2010-10-24 17:22:34Z pjd $"); 32214501Srpaulo 33214501Srpaulo#include <sys/param.h> 34214501Srpaulo#include <sys/endian.h> 35214501Srpaulo 36214501Srpaulo#include <assert.h> 37214501Srpaulo#include <bitstring.h> 38214501Srpaulo#include <errno.h> 39214501Srpaulo#include <stdarg.h> 40214501Srpaulo#include <stdbool.h> 41214501Srpaulo#include <stdint.h> 42214501Srpaulo#include <stdlib.h> 43214501Srpaulo#include <string.h> 44214501Srpaulo#include <unistd.h> 45214501Srpaulo 46214501Srpaulo#include <ebuf.h> 47214501Srpaulo#include <nv.h> 48214501Srpaulo 49214501Srpaulo#define NV_TYPE_INT8 1 50214501Srpaulo#define NV_TYPE_UINT8 2 51214501Srpaulo#define NV_TYPE_INT16 3 52214501Srpaulo#define NV_TYPE_UINT16 4 53214501Srpaulo#define NV_TYPE_INT32 5 54214501Srpaulo#define NV_TYPE_UINT32 6 55214501Srpaulo#define NV_TYPE_INT64 7 56214501Srpaulo#define NV_TYPE_UINT64 8 57214501Srpaulo#define NV_TYPE_INT8_ARRAY 9 58214501Srpaulo#define NV_TYPE_UINT8_ARRAY 10 59214501Srpaulo#define NV_TYPE_INT16_ARRAY 11 60214501Srpaulo#define NV_TYPE_UINT16_ARRAY 12 61214501Srpaulo#define NV_TYPE_INT32_ARRAY 13 62214501Srpaulo#define NV_TYPE_UINT32_ARRAY 14 63214501Srpaulo#define NV_TYPE_INT64_ARRAY 15 64214501Srpaulo#define NV_TYPE_UINT64_ARRAY 16 65214501Srpaulo#define NV_TYPE_STRING 17 66214501Srpaulo 67214501Srpaulo#define NV_TYPE_MASK 0x7f 68214501Srpaulo#define NV_TYPE_FIRST NV_TYPE_INT8 69214501Srpaulo#define NV_TYPE_LAST NV_TYPE_STRING 70214501Srpaulo 71214501Srpaulo#define NV_ORDER_NETWORK 0x00 72214501Srpaulo#define NV_ORDER_HOST 0x80 73214501Srpaulo 74214501Srpaulo#define NV_ORDER_MASK 0x80 75214501Srpaulo 76214501Srpaulo#define NV_MAGIC 0xaea1e 77214501Srpaulostruct nv { 78214501Srpaulo int nv_magic; 79214501Srpaulo int nv_error; 80214501Srpaulo struct ebuf *nv_ebuf; 81214501Srpaulo}; 82214501Srpaulo 83214501Srpaulostruct nvhdr { 84214501Srpaulo uint8_t nvh_type; 85214501Srpaulo uint8_t nvh_namesize; 86214501Srpaulo uint32_t nvh_dsize; 87214501Srpaulo char nvh_name[0]; 88214501Srpaulo} __packed; 89214501Srpaulo#define NVH_DATA(nvh) ((unsigned char *)nvh + NVH_HSIZE(nvh)) 90214501Srpaulo#define NVH_HSIZE(nvh) \ 91214501Srpaulo (sizeof(struct nvhdr) + roundup2((nvh)->nvh_namesize, 8)) 92214501Srpaulo#define NVH_DSIZE(nvh) \ 93214501Srpaulo (((nvh)->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST ? \ 94281806Srpaulo (nvh)->nvh_dsize : \ 95214501Srpaulo le32toh((nvh)->nvh_dsize)) 96214501Srpaulo#define NVH_SIZE(nvh) (NVH_HSIZE(nvh) + roundup2(NVH_DSIZE(nvh), 8)) 97214501Srpaulo 98214501Srpaulo#define NV_CHECK(nv) do { \ 99214501Srpaulo assert((nv) != NULL); \ 100214501Srpaulo assert((nv)->nv_magic == NV_MAGIC); \ 101214501Srpaulo} while (0) 102214501Srpaulo 103214501Srpaulostatic void nv_add(struct nv *nv, const unsigned char *value, size_t vsize, 104214501Srpaulo int type, const char *name); 105214501Srpaulostatic void nv_addv(struct nv *nv, const unsigned char *value, size_t vsize, 106252726Srpaulo int type, const char *namefmt, va_list nameap); 107214501Srpaulostatic struct nvhdr *nv_find(struct nv *nv, int type, const char *namefmt, 108214501Srpaulo va_list nameap); 109214501Srpaulostatic void nv_swap(struct nvhdr *nvh, bool tohost); 110214501Srpaulo 111214501Srpaulo/* 112214501Srpaulo * Allocate and initialize new nv structure. 113281806Srpaulo * Return NULL in case of malloc(3) failure. 114214501Srpaulo */ 115214501Srpaulostruct nv * 116214501Srpaulonv_alloc(void) 117214501Srpaulo{ 118214501Srpaulo struct nv *nv; 119214501Srpaulo 120214501Srpaulo nv = malloc(sizeof(*nv)); 121214501Srpaulo if (nv == NULL) 122214501Srpaulo return (NULL); 123214501Srpaulo nv->nv_ebuf = ebuf_alloc(0); 124214501Srpaulo if (nv->nv_ebuf == NULL) { 125214501Srpaulo free(nv); 126214501Srpaulo return (NULL); 127214501Srpaulo } 128214501Srpaulo nv->nv_error = 0; 129214501Srpaulo nv->nv_magic = NV_MAGIC; 130214501Srpaulo return (nv); 131214501Srpaulo} 132214501Srpaulo 133214501Srpaulo/* 134214501Srpaulo * Free the given nv structure. 135281806Srpaulo */ 136214501Srpaulovoid 137214501Srpaulonv_free(struct nv *nv) 138214501Srpaulo{ 139214501Srpaulo 140214501Srpaulo if (nv == NULL) 141214501Srpaulo return; 142214501Srpaulo 143214501Srpaulo NV_CHECK(nv); 144214501Srpaulo 145214501Srpaulo nv->nv_magic = 0; 146214501Srpaulo ebuf_free(nv->nv_ebuf); 147214501Srpaulo free(nv); 148214501Srpaulo} 149214501Srpaulo 150214501Srpaulo/* 151214501Srpaulo * Return error for the given nv structure. 152214501Srpaulo */ 153214501Srpauloint 154214501Srpaulonv_error(const struct nv *nv) 155214501Srpaulo{ 156214501Srpaulo 157214501Srpaulo if (nv == NULL) 158214501Srpaulo return (ENOMEM); 159214501Srpaulo 160214501Srpaulo NV_CHECK(nv); 161214501Srpaulo 162214501Srpaulo return (nv->nv_error); 163214501Srpaulo} 164214501Srpaulo 165214501Srpaulo/* 166214501Srpaulo * Set error for the given nv structure and return previous error. 167214501Srpaulo */ 168214501Srpauloint 169214501Srpaulonv_set_error(struct nv *nv, int error) 170214501Srpaulo{ 171214501Srpaulo int preverr; 172214501Srpaulo 173214501Srpaulo if (nv == NULL) 174214501Srpaulo return (ENOMEM); 175214501Srpaulo 176214501Srpaulo NV_CHECK(nv); 177214501Srpaulo 178214501Srpaulo preverr = nv->nv_error; 179214501Srpaulo nv->nv_error = error; 180214501Srpaulo return (preverr); 181214501Srpaulo} 182214501Srpaulo 183214501Srpaulo/* 184214501Srpaulo * Validate correctness of the entire nv structure and all its elements. 185214501Srpaulo * If extrap is not NULL, store number of extra bytes at the end of the buffer. 186214501Srpaulo */ 187214501Srpauloint 188214501Srpaulonv_validate(struct nv *nv, size_t *extrap) 189214501Srpaulo{ 190214501Srpaulo struct nvhdr *nvh; 191214501Srpaulo unsigned char *data, *ptr; 192214501Srpaulo size_t dsize, size, vsize; 193214501Srpaulo int error; 194214501Srpaulo 195214501Srpaulo if (nv == NULL) { 196214501Srpaulo errno = ENOMEM; 197214501Srpaulo return (-1); 198214501Srpaulo } 199214501Srpaulo 200214501Srpaulo NV_CHECK(nv); 201214501Srpaulo assert(nv->nv_error == 0); 202214501Srpaulo 203214501Srpaulo /* TODO: Check that names are unique? */ 204214501Srpaulo 205214501Srpaulo error = 0; 206214501Srpaulo ptr = ebuf_data(nv->nv_ebuf, &size); 207214501Srpaulo while (size > 0) { 208214501Srpaulo /* 209214501Srpaulo * Zeros at the end of the buffer are acceptable. 210214501Srpaulo */ 211214501Srpaulo if (ptr[0] == '\0') 212214501Srpaulo break; 213214501Srpaulo /* 214214501Srpaulo * Minimum size at this point is size of nvhdr structure, one 215214501Srpaulo * character long name plus terminating '\0'. 216214501Srpaulo */ 217214501Srpaulo if (size < sizeof(*nvh) + 2) { 218214501Srpaulo error = EINVAL; 219214501Srpaulo break; 220214501Srpaulo } 221214501Srpaulo nvh = (struct nvhdr *)ptr; 222214501Srpaulo if (size < NVH_HSIZE(nvh)) { 223214501Srpaulo error = EINVAL; 224214501Srpaulo break; 225214501Srpaulo } 226214501Srpaulo if (nvh->nvh_name[nvh->nvh_namesize - 1] != '\0') { 227214501Srpaulo error = EINVAL; 228214501Srpaulo break; 229214501Srpaulo } 230214501Srpaulo if (strlen(nvh->nvh_name) != 231214501Srpaulo (size_t)(nvh->nvh_namesize - 1)) { 232214501Srpaulo error = EINVAL; 233214501Srpaulo break; 234214501Srpaulo } 235214501Srpaulo if ((nvh->nvh_type & NV_TYPE_MASK) < NV_TYPE_FIRST || 236214501Srpaulo (nvh->nvh_type & NV_TYPE_MASK) > NV_TYPE_LAST) { 237214501Srpaulo error = EINVAL; 238214501Srpaulo break; 239214501Srpaulo } 240214501Srpaulo dsize = NVH_DSIZE(nvh); 241214501Srpaulo if (dsize == 0) { 242214501Srpaulo error = EINVAL; 243214501Srpaulo break; 244214501Srpaulo } 245214501Srpaulo if (size < NVH_SIZE(nvh)) { 246214501Srpaulo error = EINVAL; 247214501Srpaulo break; 248214501Srpaulo } 249214501Srpaulo vsize = 0; 250214501Srpaulo switch (nvh->nvh_type & NV_TYPE_MASK) { 251214501Srpaulo case NV_TYPE_INT8: 252214501Srpaulo case NV_TYPE_UINT8: 253214501Srpaulo if (vsize == 0) 254214501Srpaulo vsize = 1; 255214501Srpaulo /* FALLTHOUGH */ 256214501Srpaulo case NV_TYPE_INT16: 257214501Srpaulo case NV_TYPE_UINT16: 258214501Srpaulo if (vsize == 0) 259214501Srpaulo vsize = 2; 260214501Srpaulo /* FALLTHOUGH */ 261214501Srpaulo case NV_TYPE_INT32: 262214501Srpaulo case NV_TYPE_UINT32: 263214501Srpaulo if (vsize == 0) 264214501Srpaulo vsize = 4; 265214501Srpaulo /* FALLTHOUGH */ 266214501Srpaulo case NV_TYPE_INT64: 267214501Srpaulo case NV_TYPE_UINT64: 268214501Srpaulo if (vsize == 0) 269214501Srpaulo vsize = 8; 270214501Srpaulo if (dsize != vsize) { 271214501Srpaulo error = EINVAL; 272214501Srpaulo break; 273214501Srpaulo } 274214501Srpaulo break; 275214501Srpaulo case NV_TYPE_INT8_ARRAY: 276214501Srpaulo case NV_TYPE_UINT8_ARRAY: 277214501Srpaulo break; 278214501Srpaulo case NV_TYPE_INT16_ARRAY: 279214501Srpaulo case NV_TYPE_UINT16_ARRAY: 280214501Srpaulo if (vsize == 0) 281214501Srpaulo vsize = 2; 282214501Srpaulo /* FALLTHOUGH */ 283214501Srpaulo case NV_TYPE_INT32_ARRAY: 284214501Srpaulo case NV_TYPE_UINT32_ARRAY: 285214501Srpaulo if (vsize == 0) 286214501Srpaulo vsize = 4; 287214501Srpaulo /* FALLTHOUGH */ 288214501Srpaulo case NV_TYPE_INT64_ARRAY: 289214501Srpaulo case NV_TYPE_UINT64_ARRAY: 290214501Srpaulo if (vsize == 0) 291214501Srpaulo vsize = 8; 292214501Srpaulo if ((dsize % vsize) != 0) { 293281806Srpaulo error = EINVAL; 294214501Srpaulo break; 295214501Srpaulo } 296214501Srpaulo break; 297214501Srpaulo case NV_TYPE_STRING: 298214501Srpaulo data = NVH_DATA(nvh); 299214501Srpaulo if (data[dsize - 1] != '\0') { 300214501Srpaulo error = EINVAL; 301214501Srpaulo break; 302214501Srpaulo } 303214501Srpaulo if (strlen((char *)data) != dsize - 1) { 304214501Srpaulo error = EINVAL; 305214501Srpaulo break; 306214501Srpaulo } 307214501Srpaulo break; 308214501Srpaulo default: 309214501Srpaulo assert(!"invalid condition"); 310214501Srpaulo } 311214501Srpaulo if (error != 0) 312214501Srpaulo break; 313214501Srpaulo ptr += NVH_SIZE(nvh); 314214501Srpaulo size -= NVH_SIZE(nvh); 315214501Srpaulo } 316214501Srpaulo if (error != 0) { 317214501Srpaulo errno = error; 318214501Srpaulo if (nv->nv_error == 0) 319214501Srpaulo nv->nv_error = error; 320214501Srpaulo return (-1); 321214501Srpaulo } 322214501Srpaulo if (extrap != NULL) 323214501Srpaulo *extrap = size; 324214501Srpaulo return (0); 325214501Srpaulo} 326214501Srpaulo 327214501Srpaulo/* 328214501Srpaulo * Convert the given nv structure to network byte order and return ebuf 329214501Srpaulo * structure. 330214501Srpaulo */ 331214501Srpaulostruct ebuf * 332214501Srpaulonv_hton(struct nv *nv) 333281806Srpaulo{ 334281806Srpaulo struct nvhdr *nvh; 335281806Srpaulo unsigned char *ptr; 336281806Srpaulo size_t size; 337281806Srpaulo 338281806Srpaulo NV_CHECK(nv); 339281806Srpaulo assert(nv->nv_error == 0); 340214501Srpaulo 341214501Srpaulo ptr = ebuf_data(nv->nv_ebuf, &size); 342214501Srpaulo while (size > 0) { 343214501Srpaulo /* 344214501Srpaulo * Minimum size at this point is size of nvhdr structure, 345214501Srpaulo * one character long name plus terminating '\0'. 346214501Srpaulo */ 347214501Srpaulo assert(size >= sizeof(*nvh) + 2); 348214501Srpaulo nvh = (struct nvhdr *)ptr; 349214501Srpaulo assert(NVH_SIZE(nvh) <= size); 350214501Srpaulo nv_swap(nvh, false); 351214501Srpaulo ptr += NVH_SIZE(nvh); 352214501Srpaulo size -= NVH_SIZE(nvh); 353214501Srpaulo } 354214501Srpaulo 355214501Srpaulo return (nv->nv_ebuf); 356214501Srpaulo} 357214501Srpaulo 358214501Srpaulo/* 359214501Srpaulo * Create nv structure based on ebuf received from the network. 360214501Srpaulo */ 361214501Srpaulostruct nv * 362214501Srpaulonv_ntoh(struct ebuf *eb) 363289549Srpaulo{ 364289549Srpaulo struct nv *nv; 365289549Srpaulo size_t extra; 366289549Srpaulo int rerrno; 367289549Srpaulo 368289549Srpaulo assert(eb != NULL); 369289549Srpaulo 370289549Srpaulo nv = malloc(sizeof(*nv)); 371289549Srpaulo if (nv == NULL) 372289549Srpaulo return (NULL); 373289549Srpaulo nv->nv_error = 0; 374289549Srpaulo nv->nv_ebuf = eb; 375289549Srpaulo nv->nv_magic = NV_MAGIC; 376214501Srpaulo 377214501Srpaulo if (nv_validate(nv, &extra) < 0) { 378214501Srpaulo rerrno = errno; 379214501Srpaulo nv->nv_magic = 0; 380214501Srpaulo free(nv); 381214501Srpaulo errno = rerrno; 382214501Srpaulo return (NULL); 383214501Srpaulo } 384214501Srpaulo /* 385214501Srpaulo * Remove extra zeros at the end of the buffer. 386214501Srpaulo */ 387214501Srpaulo ebuf_del_tail(eb, extra); 388214501Srpaulo 389214501Srpaulo return (nv); 390214501Srpaulo} 391214501Srpaulo 392214501Srpaulo#define NV_DEFINE_ADD(type, TYPE) \ 393214501Srpaulovoid \ 394214501Srpaulonv_add_##type(struct nv *nv, type##_t value, const char *namefmt, ...) \ 395214501Srpaulo{ \ 396214501Srpaulo va_list nameap; \ 397214501Srpaulo \ 398214501Srpaulo va_start(nameap, namefmt); \ 399214501Srpaulo nv_addv(nv, (unsigned char *)&value, sizeof(value), \ 400214501Srpaulo NV_TYPE_##TYPE, namefmt, nameap); \ 401214501Srpaulo va_end(nameap); \ 402214501Srpaulo} 403214501Srpaulo 404214501SrpauloNV_DEFINE_ADD(int8, INT8) 405214501SrpauloNV_DEFINE_ADD(uint8, UINT8) 406214501SrpauloNV_DEFINE_ADD(int16, INT16) 407214501SrpauloNV_DEFINE_ADD(uint16, UINT16) 408214501SrpauloNV_DEFINE_ADD(int32, INT32) 409281806SrpauloNV_DEFINE_ADD(uint32, UINT32) 410214501SrpauloNV_DEFINE_ADD(int64, INT64) 411214501SrpauloNV_DEFINE_ADD(uint64, UINT64) 412214501Srpaulo 413214501Srpaulo#undef NV_DEFINE_ADD 414214501Srpaulo 415214501Srpaulo#define NV_DEFINE_ADD_ARRAY(type, TYPE) \ 416214501Srpaulovoid \ 417214501Srpaulonv_add_##type##_array(struct nv *nv, const type##_t *value, \ 418214501Srpaulo size_t nsize, const char *namefmt, ...) \ 419214501Srpaulo{ \ 420214501Srpaulo va_list nameap; \ 421214501Srpaulo \ 422252726Srpaulo va_start(nameap, namefmt); \ 423252726Srpaulo nv_addv(nv, (const unsigned char *)value, \ 424252726Srpaulo sizeof(value[0]) * nsize, NV_TYPE_##TYPE##_ARRAY, namefmt, \ 425252726Srpaulo nameap); \ 426252726Srpaulo va_end(nameap); \ 427252726Srpaulo} 428214501Srpaulo 429214501SrpauloNV_DEFINE_ADD_ARRAY(int8, INT8) 430281806SrpauloNV_DEFINE_ADD_ARRAY(uint8, UINT8) 431281806SrpauloNV_DEFINE_ADD_ARRAY(int16, INT16) 432281806SrpauloNV_DEFINE_ADD_ARRAY(uint16, UINT16) 433281806SrpauloNV_DEFINE_ADD_ARRAY(int32, INT32) 434281806SrpauloNV_DEFINE_ADD_ARRAY(uint32, UINT32) 435281806SrpauloNV_DEFINE_ADD_ARRAY(int64, INT64) 436281806SrpauloNV_DEFINE_ADD_ARRAY(uint64, UINT64) 437281806Srpaulo 438281806Srpaulo#undef NV_DEFINE_ADD_ARRAY 439281806Srpaulo 440214501Srpaulovoid 441214501Srpaulonv_add_string(struct nv *nv, const char *value, const char *namefmt, ...) 442214501Srpaulo{ 443214501Srpaulo va_list nameap; 444214501Srpaulo size_t size; 445214501Srpaulo 446214501Srpaulo size = strlen(value) + 1; 447214501Srpaulo 448214501Srpaulo va_start(nameap, namefmt); 449214501Srpaulo nv_addv(nv, (const unsigned char *)value, size, NV_TYPE_STRING, 450214501Srpaulo namefmt, nameap); 451214501Srpaulo va_end(nameap); 452214501Srpaulo} 453214501Srpaulo 454214501Srpaulovoid 455214501Srpaulonv_add_stringf(struct nv *nv, const char *name, const char *valuefmt, ...) 456214501Srpaulo{ 457214501Srpaulo va_list valueap; 458214501Srpaulo 459214501Srpaulo va_start(valueap, valuefmt); 460214501Srpaulo nv_add_stringv(nv, name, valuefmt, valueap); 461214501Srpaulo va_end(valueap); 462214501Srpaulo} 463214501Srpaulo 464214501Srpaulovoid 465214501Srpaulonv_add_stringv(struct nv *nv, const char *name, const char *valuefmt, 466214501Srpaulo va_list valueap) 467214501Srpaulo{ 468214501Srpaulo char *value; 469214501Srpaulo ssize_t size; 470214501Srpaulo 471214501Srpaulo size = vasprintf(&value, valuefmt, valueap); 472214501Srpaulo if (size < 0) { 473214501Srpaulo if (nv->nv_error == 0) 474214501Srpaulo nv->nv_error = ENOMEM; 475214501Srpaulo return; 476214501Srpaulo } 477214501Srpaulo size++; 478214501Srpaulo nv_add(nv, (const unsigned char *)value, size, NV_TYPE_STRING, name); 479214501Srpaulo free(value); 480214501Srpaulo} 481214501Srpaulo 482214501Srpaulo#define NV_DEFINE_GET(type, TYPE) \ 483214501Srpaulotype##_t \ 484214501Srpaulonv_get_##type(struct nv *nv, const char *namefmt, ...) \ 485214501Srpaulo{ \ 486214501Srpaulo struct nvhdr *nvh; \ 487214501Srpaulo va_list nameap; \ 488214501Srpaulo type##_t value; \ 489214501Srpaulo \ 490214501Srpaulo va_start(nameap, namefmt); \ 491214501Srpaulo nvh = nv_find(nv, NV_TYPE_##TYPE, namefmt, nameap); \ 492214501Srpaulo va_end(nameap); \ 493214501Srpaulo if (nvh == NULL) \ 494214501Srpaulo return (0); \ 495214501Srpaulo assert((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST); \ 496214501Srpaulo assert(sizeof(value) == nvh->nvh_dsize); \ 497214501Srpaulo bcopy(NVH_DATA(nvh), &value, sizeof(value)); \ 498214501Srpaulo \ 499214501Srpaulo return (value); \ 500214501Srpaulo} 501214501Srpaulo 502214501SrpauloNV_DEFINE_GET(int8, INT8) 503214501SrpauloNV_DEFINE_GET(uint8, UINT8) 504214501SrpauloNV_DEFINE_GET(int16, INT16) 505214501SrpauloNV_DEFINE_GET(uint16, UINT16) 506214501SrpauloNV_DEFINE_GET(int32, INT32) 507214501SrpauloNV_DEFINE_GET(uint32, UINT32) 508214501SrpauloNV_DEFINE_GET(int64, INT64) 509214501SrpauloNV_DEFINE_GET(uint64, UINT64) 510214501Srpaulo 511214501Srpaulo#undef NV_DEFINE_GET 512214501Srpaulo 513214501Srpaulo#define NV_DEFINE_GET_ARRAY(type, TYPE) \ 514214501Srpauloconst type##_t * \ 515214501Srpaulonv_get_##type##_array(struct nv *nv, size_t *sizep, \ 516214501Srpaulo const char *namefmt, ...) \ 517214501Srpaulo{ \ 518214501Srpaulo struct nvhdr *nvh; \ 519214501Srpaulo va_list nameap; \ 520214501Srpaulo \ 521214501Srpaulo va_start(nameap, namefmt); \ 522214501Srpaulo nvh = nv_find(nv, NV_TYPE_##TYPE##_ARRAY, namefmt, nameap); \ 523214501Srpaulo va_end(nameap); \ 524214501Srpaulo if (nvh == NULL) \ 525214501Srpaulo return (NULL); \ 526214501Srpaulo assert((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST); \ 527214501Srpaulo assert((nvh->nvh_dsize % sizeof(type##_t)) == 0); \ 528214501Srpaulo if (sizep != NULL) \ 529214501Srpaulo *sizep = nvh->nvh_dsize / sizeof(type##_t); \ 530214501Srpaulo return ((type##_t *)(void *)NVH_DATA(nvh)); \ 531214501Srpaulo} 532214501Srpaulo 533214501SrpauloNV_DEFINE_GET_ARRAY(int8, INT8) 534214501SrpauloNV_DEFINE_GET_ARRAY(uint8, UINT8) 535214501SrpauloNV_DEFINE_GET_ARRAY(int16, INT16) 536214501SrpauloNV_DEFINE_GET_ARRAY(uint16, UINT16) 537214501SrpauloNV_DEFINE_GET_ARRAY(int32, INT32) 538214501SrpauloNV_DEFINE_GET_ARRAY(uint32, UINT32) 539214501SrpauloNV_DEFINE_GET_ARRAY(int64, INT64) 540214501SrpauloNV_DEFINE_GET_ARRAY(uint64, UINT64) 541214501Srpaulo 542214501Srpaulo#undef NV_DEFINE_GET_ARRAY 543214501Srpaulo 544214501Srpauloconst char * 545214501Srpaulonv_get_string(struct nv *nv, const char *namefmt, ...) 546214501Srpaulo{ 547214501Srpaulo struct nvhdr *nvh; 548214501Srpaulo va_list nameap; 549214501Srpaulo char *str; 550214501Srpaulo 551214501Srpaulo va_start(nameap, namefmt); 552214501Srpaulo nvh = nv_find(nv, NV_TYPE_STRING, namefmt, nameap); 553214501Srpaulo va_end(nameap); 554214501Srpaulo if (nvh == NULL) 555214501Srpaulo return (NULL); 556214501Srpaulo assert((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST); 557214501Srpaulo assert(nvh->nvh_dsize >= 1); 558214501Srpaulo str = NVH_DATA(nvh); 559214501Srpaulo assert(str[nvh->nvh_dsize - 1] == '\0'); 560214501Srpaulo assert(strlen(str) == nvh->nvh_dsize - 1); 561214501Srpaulo return (str); 562214501Srpaulo} 563214501Srpaulo 564214501Srpaulo/* 565214501Srpaulo * Dump content of the nv structure. 566214501Srpaulo */ 567214501Srpaulovoid 568214501Srpaulonv_dump(struct nv *nv) 569214501Srpaulo{ 570214501Srpaulo struct nvhdr *nvh; 571214501Srpaulo unsigned char *data, *ptr; 572214501Srpaulo size_t dsize, size; 573214501Srpaulo unsigned int ii; 574214501Srpaulo bool swap; 575214501Srpaulo 576214501Srpaulo if (nv_validate(nv, NULL) < 0) { 577214501Srpaulo printf("error: %d\n", errno); 578214501Srpaulo return; 579214501Srpaulo } 580214501Srpaulo 581214501Srpaulo NV_CHECK(nv); 582214501Srpaulo assert(nv->nv_error == 0); 583214501Srpaulo 584214501Srpaulo ptr = ebuf_data(nv->nv_ebuf, &size); 585214501Srpaulo while (size > 0) { 586214501Srpaulo assert(size >= sizeof(*nvh) + 2); 587214501Srpaulo nvh = (struct nvhdr *)ptr; 588214501Srpaulo assert(size >= NVH_SIZE(nvh)); 589214501Srpaulo swap = ((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_NETWORK); 590214501Srpaulo dsize = NVH_DSIZE(nvh); 591214501Srpaulo data = NVH_DATA(nvh); 592214501Srpaulo printf(" %s", nvh->nvh_name); 593214501Srpaulo switch (nvh->nvh_type & NV_TYPE_MASK) { 594214501Srpaulo case NV_TYPE_INT8: 595214501Srpaulo printf("(int8): %jd", (intmax_t)(*(int8_t *)data)); 596 break; 597 case NV_TYPE_UINT8: 598 printf("(uint8): %ju", (uintmax_t)(*(uint8_t *)data)); 599 break; 600 case NV_TYPE_INT16: 601 printf("(int16): %jd", swap ? 602 (intmax_t)le16toh(*(int16_t *)(void *)data) : 603 (intmax_t)*(int16_t *)(void *)data); 604 break; 605 case NV_TYPE_UINT16: 606 printf("(uint16): %ju", swap ? 607 (uintmax_t)le16toh(*(uint16_t *)(void *)data) : 608 (uintmax_t)*(uint16_t *)(void *)data); 609 break; 610 case NV_TYPE_INT32: 611 printf("(int32): %jd", swap ? 612 (intmax_t)le32toh(*(int32_t *)(void *)data) : 613 (intmax_t)*(int32_t *)(void *)data); 614 break; 615 case NV_TYPE_UINT32: 616 printf("(uint32): %ju", swap ? 617 (uintmax_t)le32toh(*(uint32_t *)(void *)data) : 618 (uintmax_t)*(uint32_t *)(void *)data); 619 break; 620 case NV_TYPE_INT64: 621 printf("(int64): %jd", swap ? 622 (intmax_t)le64toh(*(int64_t *)(void *)data) : 623 (intmax_t)*(int64_t *)(void *)data); 624 break; 625 case NV_TYPE_UINT64: 626 printf("(uint64): %ju", swap ? 627 (uintmax_t)le64toh(*(uint64_t *)(void *)data) : 628 (uintmax_t)*(uint64_t *)(void *)data); 629 break; 630 case NV_TYPE_INT8_ARRAY: 631 printf("(int8 array):"); 632 for (ii = 0; ii < dsize; ii++) 633 printf(" %jd", (intmax_t)((int8_t *)data)[ii]); 634 break; 635 case NV_TYPE_UINT8_ARRAY: 636 printf("(uint8 array):"); 637 for (ii = 0; ii < dsize; ii++) 638 printf(" %ju", (uintmax_t)((uint8_t *)data)[ii]); 639 break; 640 case NV_TYPE_INT16_ARRAY: 641 printf("(int16 array):"); 642 for (ii = 0; ii < dsize / 2; ii++) { 643 printf(" %jd", swap ? 644 (intmax_t)le16toh(((int16_t *)(void *)data)[ii]) : 645 (intmax_t)((int16_t *)(void *)data)[ii]); 646 } 647 break; 648 case NV_TYPE_UINT16_ARRAY: 649 printf("(uint16 array):"); 650 for (ii = 0; ii < dsize / 2; ii++) { 651 printf(" %ju", swap ? 652 (uintmax_t)le16toh(((uint16_t *)(void *)data)[ii]) : 653 (uintmax_t)((uint16_t *)(void *)data)[ii]); 654 } 655 break; 656 case NV_TYPE_INT32_ARRAY: 657 printf("(int32 array):"); 658 for (ii = 0; ii < dsize / 4; ii++) { 659 printf(" %jd", swap ? 660 (intmax_t)le32toh(((int32_t *)(void *)data)[ii]) : 661 (intmax_t)((int32_t *)(void *)data)[ii]); 662 } 663 break; 664 case NV_TYPE_UINT32_ARRAY: 665 printf("(uint32 array):"); 666 for (ii = 0; ii < dsize / 4; ii++) { 667 printf(" %ju", swap ? 668 (uintmax_t)le32toh(((uint32_t *)(void *)data)[ii]) : 669 (uintmax_t)((uint32_t *)(void *)data)[ii]); 670 } 671 break; 672 case NV_TYPE_INT64_ARRAY: 673 printf("(int64 array):"); 674 for (ii = 0; ii < dsize / 8; ii++) { 675 printf(" %ju", swap ? 676 (uintmax_t)le64toh(((uint64_t *)(void *)data)[ii]) : 677 (uintmax_t)((uint64_t *)(void *)data)[ii]); 678 } 679 break; 680 case NV_TYPE_UINT64_ARRAY: 681 printf("(uint64 array):"); 682 for (ii = 0; ii < dsize / 8; ii++) { 683 printf(" %ju", swap ? 684 (uintmax_t)le64toh(((uint64_t *)(void *)data)[ii]) : 685 (uintmax_t)((uint64_t *)(void *)data)[ii]); 686 } 687 break; 688 case NV_TYPE_STRING: 689 printf("(string): %s", (char *)data); 690 break; 691 default: 692 assert(!"invalid condition"); 693 } 694 printf("\n"); 695 ptr += NVH_SIZE(nvh); 696 size -= NVH_SIZE(nvh); 697 } 698} 699 700/* 701 * Local routines below. 702 */ 703 704static void 705nv_add(struct nv *nv, const unsigned char *value, size_t vsize, int type, 706 const char *name) 707{ 708 static unsigned char align[7]; 709 struct nvhdr *nvh; 710 size_t namesize; 711 712 if (nv == NULL) { 713 errno = ENOMEM; 714 return; 715 } 716 717 NV_CHECK(nv); 718 719 namesize = strlen(name) + 1; 720 721 nvh = malloc(sizeof(*nvh) + roundup2(namesize, 8)); 722 if (nvh == NULL) { 723 if (nv->nv_error == 0) 724 nv->nv_error = ENOMEM; 725 return; 726 } 727 nvh->nvh_type = NV_ORDER_HOST | type; 728 nvh->nvh_namesize = (uint8_t)namesize; 729 nvh->nvh_dsize = (uint32_t)vsize; 730 bcopy(name, nvh->nvh_name, namesize); 731 732 /* Add header first. */ 733 if (ebuf_add_tail(nv->nv_ebuf, nvh, NVH_HSIZE(nvh)) < 0) { 734 assert(errno != 0); 735 if (nv->nv_error == 0) 736 nv->nv_error = errno; 737 free(nvh); 738 return; 739 } 740 free(nvh); 741 /* Add the actual data. */ 742 if (ebuf_add_tail(nv->nv_ebuf, value, vsize) < 0) { 743 assert(errno != 0); 744 if (nv->nv_error == 0) 745 nv->nv_error = errno; 746 return; 747 } 748 /* Align the data (if needed). */ 749 vsize = roundup2(vsize, 8) - vsize; 750 if (vsize == 0) 751 return; 752 assert(vsize > 0 && vsize <= sizeof(align)); 753 if (ebuf_add_tail(nv->nv_ebuf, align, vsize) < 0) { 754 assert(errno != 0); 755 if (nv->nv_error == 0) 756 nv->nv_error = errno; 757 return; 758 } 759} 760 761static void 762nv_addv(struct nv *nv, const unsigned char *value, size_t vsize, int type, 763 const char *namefmt, va_list nameap) 764{ 765 char name[255]; 766 size_t namesize; 767 768 namesize = vsnprintf(name, sizeof(name), namefmt, nameap); 769 assert(namesize > 0 && namesize < sizeof(name)); 770 771 nv_add(nv, value, vsize, type, name); 772} 773 774static struct nvhdr * 775nv_find(struct nv *nv, int type, const char *namefmt, va_list nameap) 776{ 777 char name[255]; 778 struct nvhdr *nvh; 779 unsigned char *ptr; 780 size_t size, namesize; 781 782 if (nv == NULL) { 783 errno = ENOMEM; 784 return (NULL); 785 } 786 787 NV_CHECK(nv); 788 789 namesize = vsnprintf(name, sizeof(name), namefmt, nameap); 790 assert(namesize > 0 && namesize < sizeof(name)); 791 namesize++; 792 793 ptr = ebuf_data(nv->nv_ebuf, &size); 794 while (size > 0) { 795 assert(size >= sizeof(*nvh) + 2); 796 nvh = (struct nvhdr *)ptr; 797 assert(size >= NVH_SIZE(nvh)); 798 nv_swap(nvh, true); 799 if (strcmp(nvh->nvh_name, name) == 0) { 800 if ((nvh->nvh_type & NV_TYPE_MASK) != type) { 801 errno = EINVAL; 802 if (nv->nv_error == 0) 803 nv->nv_error = EINVAL; 804 return (NULL); 805 } 806 return (nvh); 807 } 808 ptr += NVH_SIZE(nvh); 809 size -= NVH_SIZE(nvh); 810 } 811 errno = ENOENT; 812 if (nv->nv_error == 0) 813 nv->nv_error = ENOENT; 814 return (NULL); 815} 816 817static void 818nv_swap(struct nvhdr *nvh, bool tohost) 819{ 820 unsigned char *data, *end, *p; 821 size_t vsize; 822 823 data = NVH_DATA(nvh); 824 if (tohost) { 825 if ((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST) 826 return; 827 nvh->nvh_dsize = le32toh(nvh->nvh_dsize); 828 end = data + nvh->nvh_dsize; 829 nvh->nvh_type &= ~NV_ORDER_MASK; 830 nvh->nvh_type |= NV_ORDER_HOST; 831 } else { 832 if ((nvh->nvh_type & NV_ORDER_MASK) == NV_ORDER_NETWORK) 833 return; 834 end = data + nvh->nvh_dsize; 835 nvh->nvh_dsize = htole32(nvh->nvh_dsize); 836 nvh->nvh_type &= ~NV_ORDER_MASK; 837 nvh->nvh_type |= NV_ORDER_NETWORK; 838 } 839 840 vsize = 0; 841 842 switch (nvh->nvh_type & NV_TYPE_MASK) { 843 case NV_TYPE_INT8: 844 case NV_TYPE_UINT8: 845 case NV_TYPE_INT8_ARRAY: 846 case NV_TYPE_UINT8_ARRAY: 847 break; 848 case NV_TYPE_INT16: 849 case NV_TYPE_UINT16: 850 case NV_TYPE_INT16_ARRAY: 851 case NV_TYPE_UINT16_ARRAY: 852 if (vsize == 0) 853 vsize = 2; 854 /* FALLTHOUGH */ 855 case NV_TYPE_INT32: 856 case NV_TYPE_UINT32: 857 case NV_TYPE_INT32_ARRAY: 858 case NV_TYPE_UINT32_ARRAY: 859 if (vsize == 0) 860 vsize = 4; 861 /* FALLTHOUGH */ 862 case NV_TYPE_INT64: 863 case NV_TYPE_UINT64: 864 case NV_TYPE_INT64_ARRAY: 865 case NV_TYPE_UINT64_ARRAY: 866 if (vsize == 0) 867 vsize = 8; 868 for (p = data; p < end; p += vsize) { 869 if (tohost) { 870 switch (vsize) { 871 case 2: 872 *(uint16_t *)(void *)p = 873 le16toh(*(uint16_t *)(void *)p); 874 break; 875 case 4: 876 *(uint32_t *)(void *)p = 877 le32toh(*(uint32_t *)(void *)p); 878 break; 879 case 8: 880 *(uint64_t *)(void *)p = 881 le64toh(*(uint64_t *)(void *)p); 882 break; 883 default: 884 assert(!"invalid condition"); 885 } 886 } else { 887 switch (vsize) { 888 case 2: 889 *(uint16_t *)(void *)p = 890 htole16(*(uint16_t *)(void *)p); 891 break; 892 case 4: 893 *(uint32_t *)(void *)p = 894 htole32(*(uint32_t *)(void *)p); 895 break; 896 case 8: 897 *(uint64_t *)(void *)p = 898 htole64(*(uint64_t *)(void *)p); 899 break; 900 default: 901 assert(!"invalid condition"); 902 } 903 } 904 } 905 break; 906 case NV_TYPE_STRING: 907 break; 908 default: 909 assert(!"unrecognized type"); 910 } 911} 912