/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or https://opensource.org/licenses/CDDL-1.0. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2012 by Delphix. All rights reserved. */ #include #include #include #include #include #include #include "libnvpair.h" /* * libnvpair - A tools library for manipulating pairs. * * This library provides routines packing an unpacking nv pairs * for transporting data across process boundaries, transporting * between kernel and userland, and possibly saving onto disk files. */ /* * Print control structure. */ #define DEFINEOP(opname, vtype) \ struct { \ int (*op)(struct nvlist_prtctl *, void *, nvlist_t *, \ const char *, vtype); \ void *arg; \ } opname #define DEFINEARROP(opname, vtype) \ struct { \ int (*op)(struct nvlist_prtctl *, void *, nvlist_t *, \ const char *, vtype, uint_t); \ void *arg; \ } opname struct nvlist_printops { DEFINEOP(print_boolean, int); DEFINEOP(print_boolean_value, boolean_t); DEFINEOP(print_byte, uchar_t); DEFINEOP(print_int8, int8_t); DEFINEOP(print_uint8, uint8_t); DEFINEOP(print_int16, int16_t); DEFINEOP(print_uint16, uint16_t); DEFINEOP(print_int32, int32_t); DEFINEOP(print_uint32, uint32_t); DEFINEOP(print_int64, int64_t); DEFINEOP(print_uint64, uint64_t); DEFINEOP(print_double, double); DEFINEOP(print_string, char *); DEFINEOP(print_hrtime, hrtime_t); DEFINEOP(print_nvlist, nvlist_t *); DEFINEARROP(print_boolean_array, boolean_t *); DEFINEARROP(print_byte_array, uchar_t *); DEFINEARROP(print_int8_array, int8_t *); DEFINEARROP(print_uint8_array, uint8_t *); DEFINEARROP(print_int16_array, int16_t *); DEFINEARROP(print_uint16_array, uint16_t *); DEFINEARROP(print_int32_array, int32_t *); DEFINEARROP(print_uint32_array, uint32_t *); DEFINEARROP(print_int64_array, int64_t *); DEFINEARROP(print_uint64_array, uint64_t *); DEFINEARROP(print_string_array, char **); DEFINEARROP(print_nvlist_array, nvlist_t **); }; struct nvlist_prtctl { FILE *nvprt_fp; /* output destination */ enum nvlist_indent_mode nvprt_indent_mode; /* see above */ int nvprt_indent; /* absolute indent, or tab depth */ int nvprt_indentinc; /* indent or tab increment */ const char *nvprt_nmfmt; /* member name format, max one %s */ const char *nvprt_eomfmt; /* after member format, e.g. "\n" */ const char *nvprt_btwnarrfmt; /* between array members */ int nvprt_btwnarrfmt_nl; /* nvprt_eoamfmt includes newline? */ struct nvlist_printops *nvprt_dfltops; struct nvlist_printops *nvprt_custops; }; #define DFLTPRTOP(pctl, type) \ ((pctl)->nvprt_dfltops->print_##type.op) #define DFLTPRTOPARG(pctl, type) \ ((pctl)->nvprt_dfltops->print_##type.arg) #define CUSTPRTOP(pctl, type) \ ((pctl)->nvprt_custops->print_##type.op) #define CUSTPRTOPARG(pctl, type) \ ((pctl)->nvprt_custops->print_##type.arg) #define RENDER(pctl, type, nvl, name, val) \ { \ int done = 0; \ if ((pctl)->nvprt_custops && CUSTPRTOP(pctl, type)) { \ done = CUSTPRTOP(pctl, type)(pctl, \ CUSTPRTOPARG(pctl, type), nvl, name, val); \ } \ if (!done) { \ (void) DFLTPRTOP(pctl, type)(pctl, \ DFLTPRTOPARG(pctl, type), nvl, name, val); \ } \ (void) fprintf(pctl->nvprt_fp, "%s", pctl->nvprt_eomfmt); \ } #define ARENDER(pctl, type, nvl, name, arrp, count) \ { \ int done = 0; \ if ((pctl)->nvprt_custops && CUSTPRTOP(pctl, type)) { \ done = CUSTPRTOP(pctl, type)(pctl, \ CUSTPRTOPARG(pctl, type), nvl, name, arrp, count); \ } \ if (!done) { \ (void) DFLTPRTOP(pctl, type)(pctl, \ DFLTPRTOPARG(pctl, type), nvl, name, arrp, count); \ } \ (void) fprintf(pctl->nvprt_fp, "%s", pctl->nvprt_eomfmt); \ } static void nvlist_print_with_indent(nvlist_t *, nvlist_prtctl_t); /* * ====================================================================== * | | * | Indentation | * | | * ====================================================================== */ static void indent(nvlist_prtctl_t pctl, int onemore) { int depth; switch (pctl->nvprt_indent_mode) { case NVLIST_INDENT_ABS: (void) fprintf(pctl->nvprt_fp, "%*s", pctl->nvprt_indent + onemore * pctl->nvprt_indentinc, ""); break; case NVLIST_INDENT_TABBED: depth = pctl->nvprt_indent + onemore; while (depth-- > 0) (void) fprintf(pctl->nvprt_fp, "\t"); } } /* * ====================================================================== * | | * | Default nvlist member rendering functions. | * | | * ====================================================================== */ /* * Generate functions to print single-valued nvlist members. * * type_and_variant - suffix to form function name * vtype - C type for the member value * ptype - C type to cast value to for printing * vfmt - format string for pair value, e.g "%d" or "0x%llx" */ #define NVLIST_PRTFUNC(type_and_variant, vtype, ptype, vfmt) \ static int \ nvprint_##type_and_variant(nvlist_prtctl_t pctl, void *private, \ nvlist_t *nvl, const char *name, vtype value) \ { \ (void) private; \ (void) nvl; \ FILE *fp = pctl->nvprt_fp; \ indent(pctl, 1); \ (void) fprintf(fp, pctl->nvprt_nmfmt, name); \ (void) fprintf(fp, vfmt, (ptype)value); \ return (1); \ } /* * Workaround for GCC 12+ with UBSan enabled deficencies. * * GCC 12+ invoked with -fsanitize=undefined incorrectly reports the code * below as violating -Wformat-overflow. */ #if defined(__GNUC__) && !defined(__clang__) && \ defined(ZFS_UBSAN_ENABLED) && defined(HAVE_FORMAT_OVERFLOW) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wformat-overflow" #endif NVLIST_PRTFUNC(boolean, int, int, "%d") NVLIST_PRTFUNC(boolean_value, boolean_t, int, "%d") NVLIST_PRTFUNC(byte, uchar_t, uchar_t, "0x%2.2x") NVLIST_PRTFUNC(int8, int8_t, int, "%d") NVLIST_PRTFUNC(uint8, uint8_t, uint8_t, "0x%x") NVLIST_PRTFUNC(int16, int16_t, int16_t, "%d") NVLIST_PRTFUNC(uint16, uint16_t, uint16_t, "0x%x") NVLIST_PRTFUNC(int32, int32_t, int32_t, "%d") NVLIST_PRTFUNC(uint32, uint32_t, uint32_t, "0x%x") NVLIST_PRTFUNC(int64, int64_t, longlong_t, "%lld") NVLIST_PRTFUNC(uint64, uint64_t, u_longlong_t, "0x%llx") NVLIST_PRTFUNC(double, double, double, "0x%f") NVLIST_PRTFUNC(string, char *, char *, "%s") NVLIST_PRTFUNC(hrtime, hrtime_t, hrtime_t, "0x%llx") #if defined(__GNUC__) && !defined(__clang__) && \ defined(ZFS_UBSAN_ENABLED) && defined(HAVE_FORMAT_OVERFLOW) #pragma GCC diagnostic pop #endif /* * Generate functions to print array-valued nvlist members. */ #define NVLIST_ARRPRTFUNC(type_and_variant, vtype, ptype, vfmt) \ static int \ nvaprint_##type_and_variant(nvlist_prtctl_t pctl, void *private, \ nvlist_t *nvl, const char *name, vtype *valuep, uint_t count) \ { \ (void) private; \ (void) nvl; \ FILE *fp = pctl->nvprt_fp; \ uint_t i; \ for (i = 0; i < count; i++) { \ if (i == 0 || pctl->nvprt_btwnarrfmt_nl) { \ indent(pctl, 1); \ (void) fprintf(fp, pctl->nvprt_nmfmt, name); \ if (pctl->nvprt_btwnarrfmt_nl) \ (void) fprintf(fp, "[%d]: ", i); \ } \ if (i != 0) \ (void) fprintf(fp, "%s", pctl->nvprt_btwnarrfmt); \ (void) fprintf(fp, vfmt, (ptype)valuep[i]); \ } \ return (1); \ } NVLIST_ARRPRTFUNC(boolean_array, boolean_t, boolean_t, "%d") NVLIST_ARRPRTFUNC(byte_array, uchar_t, uchar_t, "0x%2.2x") NVLIST_ARRPRTFUNC(int8_array, int8_t, int8_t, "%d") NVLIST_ARRPRTFUNC(uint8_array, uint8_t, uint8_t, "0x%x") NVLIST_ARRPRTFUNC(int16_array, int16_t, int16_t, "%d") NVLIST_ARRPRTFUNC(uint16_array, uint16_t, uint16_t, "0x%x") NVLIST_ARRPRTFUNC(int32_array, int32_t, int32_t, "%d") NVLIST_ARRPRTFUNC(uint32_array, uint32_t, uint32_t, "0x%x") NVLIST_ARRPRTFUNC(int64_array, int64_t, longlong_t, "%lld") NVLIST_ARRPRTFUNC(uint64_array, uint64_t, u_longlong_t, "0x%llx") NVLIST_ARRPRTFUNC(string_array, char *, char *, "%s") static int nvprint_nvlist(nvlist_prtctl_t pctl, void *private, nvlist_t *nvl, const char *name, nvlist_t *value) { (void) private, (void) nvl; FILE *fp = pctl->nvprt_fp; indent(pctl, 1); (void) fprintf(fp, "%s = (embedded nvlist)\n", name); pctl->nvprt_indent += pctl->nvprt_indentinc; nvlist_print_with_indent(value, pctl); pctl->nvprt_indent -= pctl->nvprt_indentinc; indent(pctl, 1); (void) fprintf(fp, "(end %s)\n", name); return (1); } static int nvaprint_nvlist_array(nvlist_prtctl_t pctl, void *private, nvlist_t *nvl, const char *name, nvlist_t **valuep, uint_t count) { (void) private, (void) nvl; FILE *fp = pctl->nvprt_fp; uint_t i; indent(pctl, 1); (void) fprintf(fp, "%s = (array of embedded nvlists)\n", name); for (i = 0; i < count; i++) { indent(pctl, 1); (void) fprintf(fp, "(start %s[%d])\n", name, i); pctl->nvprt_indent += pctl->nvprt_indentinc; nvlist_print_with_indent(valuep[i], pctl); pctl->nvprt_indent -= pctl->nvprt_indentinc; indent(pctl, 1); (void) fprintf(fp, "(end %s[%d])\n", name, i); } return (1); } /* * ====================================================================== * | | * | Interfaces that allow control over formatting. | * | | * ====================================================================== */ void nvlist_prtctl_setdest(nvlist_prtctl_t pctl, FILE *fp) { pctl->nvprt_fp = fp; } FILE * nvlist_prtctl_getdest(nvlist_prtctl_t pctl) { return (pctl->nvprt_fp); } void nvlist_prtctl_setindent(nvlist_prtctl_t pctl, enum nvlist_indent_mode mode, int start, int inc) { if (mode < NVLIST_INDENT_ABS || mode > NVLIST_INDENT_TABBED) mode = NVLIST_INDENT_TABBED; if (start < 0) start = 0; if (inc < 0) inc = 1; pctl->nvprt_indent_mode = mode; pctl->nvprt_indent = start; pctl->nvprt_indentinc = inc; } void nvlist_prtctl_doindent(nvlist_prtctl_t pctl, int onemore) { indent(pctl, onemore); } void nvlist_prtctl_setfmt(nvlist_prtctl_t pctl, enum nvlist_prtctl_fmt which, const char *fmt) { switch (which) { case NVLIST_FMT_MEMBER_NAME: if (fmt == NULL) fmt = "%s = "; pctl->nvprt_nmfmt = fmt; break; case NVLIST_FMT_MEMBER_POSTAMBLE: if (fmt == NULL) fmt = "\n"; pctl->nvprt_eomfmt = fmt; break; case NVLIST_FMT_BTWN_ARRAY: if (fmt == NULL) { pctl->nvprt_btwnarrfmt = " "; pctl->nvprt_btwnarrfmt_nl = 0; } else { pctl->nvprt_btwnarrfmt = fmt; pctl->nvprt_btwnarrfmt_nl = (strstr(fmt, "\n") != NULL); } break; default: break; } } void nvlist_prtctl_dofmt(nvlist_prtctl_t pctl, enum nvlist_prtctl_fmt which, ...) { FILE *fp = pctl->nvprt_fp; va_list ap; char *name; va_start(ap, which); switch (which) { case NVLIST_FMT_MEMBER_NAME: name = va_arg(ap, char *); (void) fprintf(fp, pctl->nvprt_nmfmt, name); break; case NVLIST_FMT_MEMBER_POSTAMBLE: (void) fprintf(fp, "%s", pctl->nvprt_eomfmt); break; case NVLIST_FMT_BTWN_ARRAY: (void) fprintf(fp, "%s", pctl->nvprt_btwnarrfmt); break; default: break; } va_end(ap); } /* * ====================================================================== * | | * | Interfaces to allow appointment of replacement rendering functions.| * | | * ====================================================================== */ #define NVLIST_PRINTCTL_REPLACE(type, vtype) \ void \ nvlist_prtctlop_##type(nvlist_prtctl_t pctl, \ int (*func)(nvlist_prtctl_t, void *, nvlist_t *, const char *, vtype), \ void *private) \ { \ CUSTPRTOP(pctl, type) = func; \ CUSTPRTOPARG(pctl, type) = private; \ } NVLIST_PRINTCTL_REPLACE(boolean, int) NVLIST_PRINTCTL_REPLACE(boolean_value, boolean_t) NVLIST_PRINTCTL_REPLACE(byte, uchar_t) NVLIST_PRINTCTL_REPLACE(int8, int8_t) NVLIST_PRINTCTL_REPLACE(uint8, uint8_t) NVLIST_PRINTCTL_REPLACE(int16, int16_t) NVLIST_PRINTCTL_REPLACE(uint16, uint16_t) NVLIST_PRINTCTL_REPLACE(int32, int32_t) NVLIST_PRINTCTL_REPLACE(uint32, uint32_t) NVLIST_PRINTCTL_REPLACE(int64, int64_t) NVLIST_PRINTCTL_REPLACE(uint64, uint64_t) NVLIST_PRINTCTL_REPLACE(double, double) NVLIST_PRINTCTL_REPLACE(string, char *) NVLIST_PRINTCTL_REPLACE(hrtime, hrtime_t) NVLIST_PRINTCTL_REPLACE(nvlist, nvlist_t *) #define NVLIST_PRINTCTL_AREPLACE(type, vtype) \ void \ nvlist_prtctlop_##type(nvlist_prtctl_t pctl, \ int (*func)(nvlist_prtctl_t, void *, nvlist_t *, const char *, vtype, \ uint_t), void *private) \ { \ CUSTPRTOP(pctl, type) = func; \ CUSTPRTOPARG(pctl, type) = private; \ } NVLIST_PRINTCTL_AREPLACE(boolean_array, boolean_t *) NVLIST_PRINTCTL_AREPLACE(byte_array, uchar_t *) NVLIST_PRINTCTL_AREPLACE(int8_array, int8_t *) NVLIST_PRINTCTL_AREPLACE(uint8_array, uint8_t *) NVLIST_PRINTCTL_AREPLACE(int16_array, int16_t *) NVLIST_PRINTCTL_AREPLACE(uint16_array, uint16_t *) NVLIST_PRINTCTL_AREPLACE(int32_array, int32_t *) NVLIST_PRINTCTL_AREPLACE(uint32_array, uint32_t *) NVLIST_PRINTCTL_AREPLACE(int64_array, int64_t *) NVLIST_PRINTCTL_AREPLACE(uint64_array, uint64_t *) NVLIST_PRINTCTL_AREPLACE(string_array, char **) NVLIST_PRINTCTL_AREPLACE(nvlist_array, nvlist_t **) /* * ====================================================================== * | | * | Interfaces to manage nvlist_prtctl_t cookies. | * | | * ====================================================================== */ static const struct nvlist_printops defprtops = { { nvprint_boolean, NULL }, { nvprint_boolean_value, NULL }, { nvprint_byte, NULL }, { nvprint_int8, NULL }, { nvprint_uint8, NULL }, { nvprint_int16, NULL }, { nvprint_uint16, NULL }, { nvprint_int32, NULL }, { nvprint_uint32, NULL }, { nvprint_int64, NULL }, { nvprint_uint64, NULL }, { nvprint_double, NULL }, { nvprint_string, NULL }, { nvprint_hrtime, NULL }, { nvprint_nvlist, NULL }, { nvaprint_boolean_array, NULL }, { nvaprint_byte_array, NULL }, { nvaprint_int8_array, NULL }, { nvaprint_uint8_array, NULL }, { nvaprint_int16_array, NULL }, { nvaprint_uint16_array, NULL }, { nvaprint_int32_array, NULL }, { nvaprint_uint32_array, NULL }, { nvaprint_int64_array, NULL }, { nvaprint_uint64_array, NULL }, { nvaprint_string_array, NULL }, { nvaprint_nvlist_array, NULL }, }; static void prtctl_defaults(FILE *fp, struct nvlist_prtctl *pctl, struct nvlist_printops *ops) { pctl->nvprt_fp = fp; pctl->nvprt_indent_mode = NVLIST_INDENT_TABBED; pctl->nvprt_indent = 0; pctl->nvprt_indentinc = 1; pctl->nvprt_nmfmt = "%s = "; pctl->nvprt_eomfmt = "\n"; pctl->nvprt_btwnarrfmt = " "; pctl->nvprt_btwnarrfmt_nl = 0; pctl->nvprt_dfltops = (struct nvlist_printops *)&defprtops; pctl->nvprt_custops = ops; } nvlist_prtctl_t nvlist_prtctl_alloc(void) { struct nvlist_prtctl *pctl; struct nvlist_printops *ops; if ((pctl = malloc(sizeof (*pctl))) == NULL) return (NULL); if ((ops = calloc(1, sizeof (*ops))) == NULL) { free(pctl); return (NULL); } prtctl_defaults(stdout, pctl, ops); return (pctl); } void nvlist_prtctl_free(nvlist_prtctl_t pctl) { if (pctl != NULL) { free(pctl->nvprt_custops); free(pctl); } } /* * ====================================================================== * | | * | Top-level print request interfaces. | * | | * ====================================================================== */ /* * nvlist_print - Prints elements in an event buffer */ static void nvlist_print_with_indent(nvlist_t *nvl, nvlist_prtctl_t pctl) { FILE *fp = pctl->nvprt_fp; char *name; uint_t nelem; nvpair_t *nvp; if (nvl == NULL) return; indent(pctl, 0); (void) fprintf(fp, "nvlist version: %d\n", NVL_VERSION(nvl)); nvp = nvlist_next_nvpair(nvl, NULL); while (nvp) { data_type_t type = nvpair_type(nvp); name = nvpair_name(nvp); nelem = 0; switch (type) { case DATA_TYPE_BOOLEAN: { RENDER(pctl, boolean, nvl, name, 1); break; } case DATA_TYPE_BOOLEAN_VALUE: { boolean_t val; (void) nvpair_value_boolean_value(nvp, &val); RENDER(pctl, boolean_value, nvl, name, val); break; } case DATA_TYPE_BYTE: { uchar_t val; (void) nvpair_value_byte(nvp, &val); RENDER(pctl, byte, nvl, name, val); break; } case DATA_TYPE_INT8: { int8_t val; (void) nvpair_value_int8(nvp, &val); RENDER(pctl, int8, nvl, name, val); break; } case DATA_TYPE_UINT8: { uint8_t val; (void) nvpair_value_uint8(nvp, &val); RENDER(pctl, uint8, nvl, name, val); break; } case DATA_TYPE_INT16: { int16_t val; (void) nvpair_value_int16(nvp, &val); RENDER(pctl, int16, nvl, name, val); break; } case DATA_TYPE_UINT16: { uint16_t val; (void) nvpair_value_uint16(nvp, &val); RENDER(pctl, uint16, nvl, name, val); break; } case DATA_TYPE_INT32: { int32_t val; (void) nvpair_value_int32(nvp, &val); RENDER(pctl, int32, nvl, name, val); break; } case DATA_TYPE_UINT32: { uint32_t val; (void) nvpair_value_uint32(nvp, &val); RENDER(pctl, uint32, nvl, name, val); break; } case DATA_TYPE_INT64: { int64_t val; (void) nvpair_value_int64(nvp, &val); RENDER(pctl, int64, nvl, name, val); break; } case DATA_TYPE_UINT64: { uint64_t val; (void) nvpair_value_uint64(nvp, &val); RENDER(pctl, uint64, nvl, name, val); break; } case DATA_TYPE_DOUBLE: { double val; (void) nvpair_value_double(nvp, &val); RENDER(pctl, double, nvl, name, val); break; } case DATA_TYPE_STRING: { char *val; (void) nvpair_value_string(nvp, &val); RENDER(pctl, string, nvl, name, val); break; } case DATA_TYPE_BOOLEAN_ARRAY: { boolean_t *val; (void) nvpair_value_boolean_array(nvp, &val, &nelem); ARENDER(pctl, boolean_array, nvl, name, val, nelem); break; } case DATA_TYPE_BYTE_ARRAY: { uchar_t *val; (void) nvpair_value_byte_array(nvp, &val, &nelem); ARENDER(pctl, byte_array, nvl, name, val, nelem); break; } case DATA_TYPE_INT8_ARRAY: { int8_t *val; (void) nvpair_value_int8_array(nvp, &val, &nelem); ARENDER(pctl, int8_array, nvl, name, val, nelem); break; } case DATA_TYPE_UINT8_ARRAY: { uint8_t *val; (void) nvpair_value_uint8_array(nvp, &val, &nelem); ARENDER(pctl, uint8_array, nvl, name, val, nelem); break; } case DATA_TYPE_INT16_ARRAY: { int16_t *val; (void) nvpair_value_int16_array(nvp, &val, &nelem); ARENDER(pctl, int16_array, nvl, name, val, nelem); break; } case DATA_TYPE_UINT16_ARRAY: { uint16_t *val; (void) nvpair_value_uint16_array(nvp, &val, &nelem); ARENDER(pctl, uint16_array, nvl, name, val, nelem); break; } case DATA_TYPE_INT32_ARRAY: { int32_t *val; (void) nvpair_value_int32_array(nvp, &val, &nelem); ARENDER(pctl, int32_array, nvl, name, val, nelem); break; } case DATA_TYPE_UINT32_ARRAY: { uint32_t *val; (void) nvpair_value_uint32_array(nvp, &val, &nelem); ARENDER(pctl, uint32_array, nvl, name, val, nelem); break; } case DATA_TYPE_INT64_ARRAY: { int64_t *val; (void) nvpair_value_int64_array(nvp, &val, &nelem); ARENDER(pctl, int64_array, nvl, name, val, nelem); break; } case DATA_TYPE_UINT64_ARRAY: { uint64_t *val; (void) nvpair_value_uint64_array(nvp, &val, &nelem); ARENDER(pctl, uint64_array, nvl, name, val, nelem); break; } case DATA_TYPE_STRING_ARRAY: { char **val; (void) nvpair_value_string_array(nvp, &val, &nelem); ARENDER(pctl, string_array, nvl, name, val, nelem); break; } case DATA_TYPE_HRTIME: { hrtime_t val; (void) nvpair_value_hrtime(nvp, &val); RENDER(pctl, hrtime, nvl, name, val); break; } case DATA_TYPE_NVLIST: { nvlist_t *val; (void) nvpair_value_nvlist(nvp, &val); RENDER(pctl, nvlist, nvl, name, val); break; } case DATA_TYPE_NVLIST_ARRAY: { nvlist_t **val; (void) nvpair_value_nvlist_array(nvp, &val, &nelem); ARENDER(pctl, nvlist_array, nvl, name, val, nelem); break; } default: (void) fprintf(fp, " unknown data type (%d)", type); break; } nvp = nvlist_next_nvpair(nvl, nvp); } } void nvlist_print(FILE *fp, nvlist_t *nvl) { struct nvlist_prtctl pc; prtctl_defaults(fp, &pc, NULL); nvlist_print_with_indent(nvl, &pc); } void nvlist_prt(nvlist_t *nvl, nvlist_prtctl_t pctl) { nvlist_print_with_indent(nvl, pctl); } #define NVP(elem, type, vtype, ptype, format) { \ vtype value; \ \ (void) nvpair_value_##type(elem, &value); \ (void) printf("%*s%s: " format "\n", indent, "", \ nvpair_name(elem), (ptype)value); \ } #define NVPA(elem, type, vtype, ptype, format) { \ uint_t i, count; \ vtype *value; \ \ (void) nvpair_value_##type(elem, &value, &count); \ for (i = 0; i < count; i++) { \ (void) printf("%*s%s[%d]: " format "\n", indent, "", \ nvpair_name(elem), i, (ptype)value[i]); \ } \ } /* * Similar to nvlist_print() but handles arrays slightly differently. */ void dump_nvlist(nvlist_t *list, int indent) { nvpair_t *elem = NULL; boolean_t bool_value; nvlist_t *nvlist_value; nvlist_t **nvlist_array_value; uint_t i, count; if (list == NULL) { return; } while ((elem = nvlist_next_nvpair(list, elem)) != NULL) { switch (nvpair_type(elem)) { case DATA_TYPE_BOOLEAN: (void) printf("%*s%s\n", indent, "", nvpair_name(elem)); break; case DATA_TYPE_BOOLEAN_VALUE: (void) nvpair_value_boolean_value(elem, &bool_value); (void) printf("%*s%s: %s\n", indent, "", nvpair_name(elem), bool_value ? "true" : "false"); break; case DATA_TYPE_BYTE: NVP(elem, byte, uchar_t, int, "%u"); break; case DATA_TYPE_INT8: NVP(elem, int8, int8_t, int, "%d"); break; case DATA_TYPE_UINT8: NVP(elem, uint8, uint8_t, int, "%u"); break; case DATA_TYPE_INT16: NVP(elem, int16, int16_t, int, "%d"); break; case DATA_TYPE_UINT16: NVP(elem, uint16, uint16_t, int, "%u"); break; case DATA_TYPE_INT32: NVP(elem, int32, int32_t, long, "%ld"); break; case DATA_TYPE_UINT32: NVP(elem, uint32, uint32_t, ulong_t, "%lu"); break; case DATA_TYPE_INT64: NVP(elem, int64, int64_t, longlong_t, "%lld"); break; case DATA_TYPE_UINT64: NVP(elem, uint64, uint64_t, u_longlong_t, "%llu"); break; case DATA_TYPE_STRING: NVP(elem, string, char *, char *, "'%s'"); break; case DATA_TYPE_BYTE_ARRAY: NVPA(elem, byte_array, uchar_t, int, "%u"); break; case DATA_TYPE_INT8_ARRAY: NVPA(elem, int8_array, int8_t, int, "%d"); break; case DATA_TYPE_UINT8_ARRAY: NVPA(elem, uint8_array, uint8_t, int, "%u"); break; case DATA_TYPE_INT16_ARRAY: NVPA(elem, int16_array, int16_t, int, "%d"); break; case DATA_TYPE_UINT16_ARRAY: NVPA(elem, uint16_array, uint16_t, int, "%u"); break; case DATA_TYPE_INT32_ARRAY: NVPA(elem, int32_array, int32_t, long, "%ld"); break; case DATA_TYPE_UINT32_ARRAY: NVPA(elem, uint32_array, uint32_t, ulong_t, "%lu"); break; case DATA_TYPE_INT64_ARRAY: NVPA(elem, int64_array, int64_t, longlong_t, "%lld"); break; case DATA_TYPE_UINT64_ARRAY: NVPA(elem, uint64_array, uint64_t, u_longlong_t, "%llu"); break; case DATA_TYPE_STRING_ARRAY: NVPA(elem, string_array, char *, char *, "'%s'"); break; case DATA_TYPE_NVLIST: (void) nvpair_value_nvlist(elem, &nvlist_value); (void) printf("%*s%s:\n", indent, "", nvpair_name(elem)); dump_nvlist(nvlist_value, indent + 4); break; case DATA_TYPE_NVLIST_ARRAY: (void) nvpair_value_nvlist_array(elem, &nvlist_array_value, &count); for (i = 0; i < count; i++) { (void) printf("%*s%s[%u]:\n", indent, "", nvpair_name(elem), i); dump_nvlist(nvlist_array_value[i], indent + 4); } break; default: (void) printf(dgettext(TEXT_DOMAIN, "bad config type " "%d for %s\n"), nvpair_type(elem), nvpair_name(elem)); } } } /* * ====================================================================== * | | * | Misc private interface. | * | | * ====================================================================== */ /* * Determine if string 'value' matches 'nvp' value. The 'value' string is * converted, depending on the type of 'nvp', prior to match. For numeric * types, a radix independent sscanf conversion of 'value' is used. If 'nvp' * is an array type, 'ai' is the index into the array against which we are * checking for match. If nvp is of DATA_TYPE_STRING*, the caller can pass * in a regex_t compilation of value in 'value_regex' to trigger regular * expression string match instead of simple strcmp(). * * Return 1 on match, 0 on no-match, and -1 on error. If the error is * related to value syntax error and 'ep' is non-NULL, *ep will point into * the 'value' string at the location where the error exists. * * NOTE: It may be possible to move the non-regex_t version of this into * common code used by library/kernel/boot. */ int nvpair_value_match_regex(nvpair_t *nvp, int ai, char *value, regex_t *value_regex, char **ep) { char *evalue; uint_t a_len; int sr; if (ep) *ep = NULL; if ((nvp == NULL) || (value == NULL)) return (-1); /* error fail match - invalid args */ /* make sure array and index combination make sense */ if ((nvpair_type_is_array(nvp) && (ai < 0)) || (!nvpair_type_is_array(nvp) && (ai >= 0))) return (-1); /* error fail match - bad index */ /* non-string values should be single 'chunk' */ if ((nvpair_type(nvp) != DATA_TYPE_STRING) && (nvpair_type(nvp) != DATA_TYPE_STRING_ARRAY)) { value += strspn(value, " \t"); evalue = value + strcspn(value, " \t"); if (*evalue) { if (ep) *ep = evalue; return (-1); /* error fail match - syntax */ } } sr = EOF; switch (nvpair_type(nvp)) { case DATA_TYPE_STRING: { char *val; /* check string value for match */ if (nvpair_value_string(nvp, &val) == 0) { if (value_regex) { if (regexec(value_regex, val, (size_t)0, NULL, 0) == 0) return (1); /* match */ } else { if (strcmp(value, val) == 0) return (1); /* match */ } } break; } case DATA_TYPE_STRING_ARRAY: { char **val_array; /* check indexed string value of array for match */ if ((nvpair_value_string_array(nvp, &val_array, &a_len) == 0) && (ai < a_len)) { if (value_regex) { if (regexec(value_regex, val_array[ai], (size_t)0, NULL, 0) == 0) return (1); } else { if (strcmp(value, val_array[ai]) == 0) return (1); } } break; } case DATA_TYPE_BYTE: { uchar_t val, val_arg; /* scanf uchar_t from value and check for match */ sr = sscanf(value, "%c", &val_arg); if ((sr == 1) && (nvpair_value_byte(nvp, &val) == 0) && (val == val_arg)) return (1); break; } case DATA_TYPE_BYTE_ARRAY: { uchar_t *val_array, val_arg; /* check indexed value of array for match */ sr = sscanf(value, "%c", &val_arg); if ((sr == 1) && (nvpair_value_byte_array(nvp, &val_array, &a_len) == 0) && (ai < a_len) && (val_array[ai] == val_arg)) return (1); break; } case DATA_TYPE_INT8: { int8_t val, val_arg; /* scanf int8_t from value and check for match */ sr = sscanf(value, "%"SCNi8, &val_arg); if ((sr == 1) && (nvpair_value_int8(nvp, &val) == 0) && (val == val_arg)) return (1); break; } case DATA_TYPE_INT8_ARRAY: { int8_t *val_array, val_arg; /* check indexed value of array for match */ sr = sscanf(value, "%"SCNi8, &val_arg); if ((sr == 1) && (nvpair_value_int8_array(nvp, &val_array, &a_len) == 0) && (ai < a_len) && (val_array[ai] == val_arg)) return (1); break; } case DATA_TYPE_UINT8: { uint8_t val, val_arg; /* scanf uint8_t from value and check for match */ sr = sscanf(value, "%"SCNi8, (int8_t *)&val_arg); if ((sr == 1) && (nvpair_value_uint8(nvp, &val) == 0) && (val == val_arg)) return (1); break; } case DATA_TYPE_UINT8_ARRAY: { uint8_t *val_array, val_arg; /* check indexed value of array for match */ sr = sscanf(value, "%"SCNi8, (int8_t *)&val_arg); if ((sr == 1) && (nvpair_value_uint8_array(nvp, &val_array, &a_len) == 0) && (ai < a_len) && (val_array[ai] == val_arg)) return (1); break; } case DATA_TYPE_INT16: { int16_t val, val_arg; /* scanf int16_t from value and check for match */ sr = sscanf(value, "%"SCNi16, &val_arg); if ((sr == 1) && (nvpair_value_int16(nvp, &val) == 0) && (val == val_arg)) return (1); break; } case DATA_TYPE_INT16_ARRAY: { int16_t *val_array, val_arg; /* check indexed value of array for match */ sr = sscanf(value, "%"SCNi16, &val_arg); if ((sr == 1) && (nvpair_value_int16_array(nvp, &val_array, &a_len) == 0) && (ai < a_len) && (val_array[ai] == val_arg)) return (1); break; } case DATA_TYPE_UINT16: { uint16_t val, val_arg; /* scanf uint16_t from value and check for match */ sr = sscanf(value, "%"SCNi16, (int16_t *)&val_arg); if ((sr == 1) && (nvpair_value_uint16(nvp, &val) == 0) && (val == val_arg)) return (1); break; } case DATA_TYPE_UINT16_ARRAY: { uint16_t *val_array, val_arg; /* check indexed value of array for match */ sr = sscanf(value, "%"SCNi16, (int16_t *)&val_arg); if ((sr == 1) && (nvpair_value_uint16_array(nvp, &val_array, &a_len) == 0) && (ai < a_len) && (val_array[ai] == val_arg)) return (1); break; } case DATA_TYPE_INT32: { int32_t val, val_arg; /* scanf int32_t from value and check for match */ sr = sscanf(value, "%"SCNi32, &val_arg); if ((sr == 1) && (nvpair_value_int32(nvp, &val) == 0) && (val == val_arg)) return (1); break; } case DATA_TYPE_INT32_ARRAY: { int32_t *val_array, val_arg; /* check indexed value of array for match */ sr = sscanf(value, "%"SCNi32, &val_arg); if ((sr == 1) && (nvpair_value_int32_array(nvp, &val_array, &a_len) == 0) && (ai < a_len) && (val_array[ai] == val_arg)) return (1); break; } case DATA_TYPE_UINT32: { uint32_t val, val_arg; /* scanf uint32_t from value and check for match */ sr = sscanf(value, "%"SCNi32, (int32_t *)&val_arg); if ((sr == 1) && (nvpair_value_uint32(nvp, &val) == 0) && (val == val_arg)) return (1); break; } case DATA_TYPE_UINT32_ARRAY: { uint32_t *val_array, val_arg; /* check indexed value of array for match */ sr = sscanf(value, "%"SCNi32, (int32_t *)&val_arg); if ((sr == 1) && (nvpair_value_uint32_array(nvp, &val_array, &a_len) == 0) && (ai < a_len) && (val_array[ai] == val_arg)) return (1); break; } case DATA_TYPE_INT64: { int64_t val, val_arg; /* scanf int64_t from value and check for match */ sr = sscanf(value, "%"SCNi64, &val_arg); if ((sr == 1) && (nvpair_value_int64(nvp, &val) == 0) && (val == val_arg)) return (1); break; } case DATA_TYPE_INT64_ARRAY: { int64_t *val_array, val_arg; /* check indexed value of array for match */ sr = sscanf(value, "%"SCNi64, &val_arg); if ((sr == 1) && (nvpair_value_int64_array(nvp, &val_array, &a_len) == 0) && (ai < a_len) && (val_array[ai] == val_arg)) return (1); break; } case DATA_TYPE_UINT64: { uint64_t val_arg, val; /* scanf uint64_t from value and check for match */ sr = sscanf(value, "%"SCNi64, (int64_t *)&val_arg); if ((sr == 1) && (nvpair_value_uint64(nvp, &val) == 0) && (val == val_arg)) return (1); break; } case DATA_TYPE_UINT64_ARRAY: { uint64_t *val_array, val_arg; /* check indexed value of array for match */ sr = sscanf(value, "%"SCNi64, (int64_t *)&val_arg); if ((sr == 1) && (nvpair_value_uint64_array(nvp, &val_array, &a_len) == 0) && (ai < a_len) && (val_array[ai] == val_arg)) return (1); break; } case DATA_TYPE_BOOLEAN_VALUE: { int32_t val_arg; boolean_t val; /* scanf boolean_t from value and check for match */ sr = sscanf(value, "%"SCNi32, (int32_t *)&val_arg); if ((sr == 1) && (nvpair_value_boolean_value(nvp, &val) == 0) && (val == val_arg)) return (1); break; } case DATA_TYPE_BOOLEAN_ARRAY: { boolean_t *val_array; int32_t val_arg; /* check indexed value of array for match */ sr = sscanf(value, "%"SCNi32, (int32_t *)&val_arg); if ((sr == 1) && (nvpair_value_boolean_array(nvp, &val_array, &a_len) == 0) && (ai < a_len) && (val_array[ai] == val_arg)) return (1); break; } case DATA_TYPE_HRTIME: case DATA_TYPE_NVLIST: case DATA_TYPE_NVLIST_ARRAY: case DATA_TYPE_BOOLEAN: case DATA_TYPE_DOUBLE: case DATA_TYPE_UNKNOWN: default: /* * unknown/unsupported data type */ return (-1); /* error fail match */ } /* * check to see if sscanf failed conversion, return approximate * pointer to problem */ if (sr != 1) { if (ep) *ep = value; return (-1); /* error fail match - syntax */ } return (0); /* fail match */ } int nvpair_value_match(nvpair_t *nvp, int ai, char *value, char **ep) { return (nvpair_value_match_regex(nvp, ai, value, NULL, ep)); }