root/usr/src/cmd/fm/fmtopo/common/fmtopo.c
/*
 * 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 http://www.opensolaris.org/os/licensing.
 * 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) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
 * Copyright (c) 2019, Joyent, Inc. All rights reserved.
 * Copyright 2023 Oxide Computer Company
 */


#include <sys/fm/protocol.h>
#include <fm/libtopo.h>
#include <ctype.h>
#include <fnmatch.h>
#include <limits.h>
#include <strings.h>
#include <stdio.h>
#include <errno.h>
#include <umem.h>
#include <zone.h>
#include <sys/param.h>

#define FMTOPO_EXIT_SUCCESS     0
#define FMTOPO_EXIT_ERROR       1
#define FMTOPO_EXIT_USAGE       2

#define STDERR  "stderr"
#define ALL     "all"

static const char *g_pname;
static const char *g_fmri = NULL;

static const char *opt_R = "/";
static const char *opt_s = FM_FMRI_SCHEME_HC;
static const char optstr[] = "bCdelm:P:pR:s:StVx";
static const char *opt_m;

static int opt_b = 0;
static int opt_d = 0;
static int opt_e = 0;
static int opt_l = 0;
static int opt_p = 0;
static int opt_S = 0;
static int opt_t = 0;
static int opt_V = 0;
static int opt_x = 0;
static int opt_all = 0;

struct prop_args {
        const char *group;
        const char *prop;
        const char *type;
        const char *value;
};

static struct prop_args **pargs = NULL;
static int pcnt = 0;

static int
usage(FILE *fp)
{
        (void) fprintf(fp,
            "Usage: %s [-bCedlpSVx] [-P group.property[=type:value]] "
            "[-R root] [-m method] [-s scheme] [fmri]\n", g_pname);

        (void) fprintf(fp,
            "\t-b  walk in sibling-first order (default is child-first)\n"
            "\t-C  dump core after completing execution\n"
            "\t-d  set debug mode for libtopo modules\n"
            "\t-e  display FMRIs as paths using esc/eft notation\n"
            "\t-l  list available schemes rather than print a tree\n"
            "\t-m  execute given method\n"
            "\t-P  get/set specified properties\n"
            "\t-p  display of FMRI protocol properties\n"
            "\t-R  set root directory for libtopo plug-ins and other files\n"
            "\t-s  display topology for the specified FMRI scheme\n"
            "\t-S  display FMRI status (present/usable/occupied)\n"
            "\t-V  set verbose mode\n"
            "\t-x  display a xml formatted topology\n");

        return (FMTOPO_EXIT_USAGE);
}

static topo_type_t
str2type(const char *tstr)
{
        topo_type_t type;

        if (tstr == NULL)
                return (TOPO_TYPE_INVALID);

        if (strcmp(tstr, "int32") == 0)
                type = TOPO_TYPE_INT32;
        else if (strcmp(tstr, "uint32") == 0)
                type = TOPO_TYPE_UINT32;
        else if (strcmp(tstr, "int64") == 0)
                type = TOPO_TYPE_INT64;
        else if (strcmp(tstr, "uint64") == 0)
                type = TOPO_TYPE_UINT64;
        else if (strcmp(tstr, "string") == 0)
                type = TOPO_TYPE_STRING;
        else if (strcmp(tstr, "fmri") == 0)
                type = TOPO_TYPE_FMRI;
        else {
                type = TOPO_TYPE_INVALID;
        }

        return (type);
}

static void
print_node(topo_hdl_t *thp, tnode_t *node, nvlist_t *nvl, const char *fmri)
{
        int err, ret;
        boolean_t is_occupied;

        (void) printf("%s\n", (char *)fmri);

        if (opt_p && !(pcnt > 0 || opt_V || opt_all)) {
                char *aname = NULL, *fname = NULL, *lname = NULL;
                nvlist_t *asru = NULL;
                nvlist_t *fru = NULL;

                if (topo_node_asru(node, &asru, NULL, &err) == 0)
                        (void) topo_fmri_nvl2str(thp, asru, &aname, &err);
                if (topo_node_fru(node, &fru, NULL, &err) == 0)
                        (void) topo_fmri_nvl2str(thp, fru, &fname, &err);
                (void) topo_node_label(node, &lname, &err);
                if (aname != NULL) {
                        nvlist_free(asru);
                        (void) printf("\tASRU: %s\n", aname);
                        topo_hdl_strfree(thp, aname);
                } else {
                        (void) printf("\tASRU: -\n");
                }
                if (fname != NULL) {
                        nvlist_free(fru);
                        (void) printf("\tFRU: %s\n", fname);
                        topo_hdl_strfree(thp, fname);
                } else {
                        (void) printf("\tFRU: -\n");
                }
                if (lname != NULL) {
                        (void) printf("\tLabel: %s\n", lname);
                        topo_hdl_strfree(thp, lname);
                } else {
                        (void) printf("\tLabel: -\n");
                }
        }

        if (opt_S) {
                if ((ret = topo_fmri_present(thp, nvl, &err)) < 0)
                        (void) printf("\tPresent: -\n");
                else
                        (void) printf("\tPresent: %s\n",
                            ret ? "true" : "false");

                if ((ret = topo_fmri_unusable(thp, nvl, &err)) < 0)
                        (void) printf("\tUnusable: -\n");
                else
                        (void) printf("\tUnusable: %s\n",
                            ret ? "true" : "false");

                ret = topo_node_occupied(node, &is_occupied);
                if (ret == 0)
                        (void) printf("\tOccupied: %s\n",
                            is_occupied ? "true" : "false");
                else if (ret != ETOPO_METHOD_NOTSUP)
                        (void) printf("\tOccupied: -\n");
        }
}

static void
print_everstyle(tnode_t *node)
{
        char buf[PATH_MAX], numbuf[64];
        nvlist_t *fmri, **hcl;
        int i, err;
        uint_t n;

        if (topo_prop_get_fmri(node, TOPO_PGROUP_PROTOCOL,
            TOPO_PROP_RESOURCE, &fmri, &err) < 0) {
                (void) fprintf(stderr, "%s: failed to get fmri for %s=%d: %s\n",
                    g_pname, topo_node_name(node),
                    topo_node_instance(node), topo_strerror(err));
                return;
        }

        if (nvlist_lookup_nvlist_array(fmri, FM_FMRI_HC_LIST, &hcl, &n) != 0) {
                (void) fprintf(stderr, "%s: failed to find %s for %s=%d\n",
                    g_pname, FM_FMRI_HC_LIST, topo_node_name(node),
                    topo_node_instance(node));
                nvlist_free(fmri);
                return;
        }

        buf[0] = '\0';

        for (i = 0; i < n; i++) {
                char *name, *inst, *estr;
                ulong_t ul;

                if (nvlist_lookup_string(hcl[i], FM_FMRI_HC_NAME, &name) != 0 ||
                    nvlist_lookup_string(hcl[i], FM_FMRI_HC_ID, &inst) != 0) {
                        (void) fprintf(stderr, "%s: failed to get "
                            "name-instance for %s=%d\n", g_pname,
                            topo_node_name(node), topo_node_instance(node));
                        nvlist_free(fmri);
                        return;
                }

                errno = 0;
                ul = strtoul(inst, &estr, 10);

                if (errno != 0 || estr == inst) {
                        (void) fprintf(stderr, "%s: instance %s does not "
                            "convert to an unsigned integer\n", g_pname, inst);
                }

                (void) strlcat(buf, "/", sizeof (buf));
                (void) strlcat(buf, name, sizeof (buf));
                (void) snprintf(numbuf, sizeof (numbuf), "%u", ul);
                (void) strlcat(buf, numbuf, sizeof (buf));
        }
        nvlist_free(fmri);

        (void) printf("%s\n", buf);
}

static void
print_prop_nameval(topo_hdl_t *thp, tnode_t *node, nvlist_t *nvl)
{
        int err;
        topo_type_t type;
        char *tstr, *propn, *factype;
        nvpair_t *pv_nvp;
        int i;
        uint_t nelem;

        if ((pv_nvp = nvlist_next_nvpair(nvl, NULL)) == NULL)
                return;

        /* Print property name */
        if ((pv_nvp = nvlist_next_nvpair(nvl, NULL)) == NULL ||
            nvpair_name(pv_nvp) == NULL ||
            strcmp(TOPO_PROP_VAL_NAME, nvpair_name(pv_nvp)) != 0) {
                (void) fprintf(stderr, "%s: malformed property name\n",
                    g_pname);
                return;
        } else {
                (void) nvpair_value_string(pv_nvp, &propn);
        }

        if ((pv_nvp = nvlist_next_nvpair(nvl, pv_nvp)) == NULL ||
            nvpair_name(pv_nvp) == NULL ||
            strcmp(nvpair_name(pv_nvp), TOPO_PROP_VAL_TYPE) != 0 ||
            nvpair_type(pv_nvp) != DATA_TYPE_UINT32)  {
                (void) fprintf(stderr, "%s: malformed property type for %s\n",
                    g_pname, propn);
                return;
        } else {
                (void) nvpair_value_uint32(pv_nvp, (uint32_t *)&type);
        }

        switch (type) {
                case TOPO_TYPE_BOOLEAN: tstr = "boolean"; break;
                case TOPO_TYPE_INT32: tstr = "int32"; break;
                case TOPO_TYPE_UINT32: tstr = "uint32"; break;
                case TOPO_TYPE_INT64: tstr = "int64"; break;
                case TOPO_TYPE_UINT64: tstr = "uint64"; break;
                case TOPO_TYPE_DOUBLE: tstr = "double"; break;
                case TOPO_TYPE_STRING: tstr = "string"; break;
                case TOPO_TYPE_FMRI: tstr = "fmri"; break;
                case TOPO_TYPE_INT32_ARRAY: tstr = "int32[]"; break;
                case TOPO_TYPE_UINT32_ARRAY: tstr = "uint32[]"; break;
                case TOPO_TYPE_INT64_ARRAY: tstr = "int64[]"; break;
                case TOPO_TYPE_UINT64_ARRAY: tstr = "uint64[]"; break;
                case TOPO_TYPE_STRING_ARRAY: tstr = "string[]"; break;
                case TOPO_TYPE_FMRI_ARRAY: tstr = "fmri[]"; break;
                default: tstr = "unknown type";
        }

        (void) printf("    %-17s %-8s ", propn, tstr);

        /*
         * Get property value
         */
        if (nvpair_name(pv_nvp) == NULL ||
            (pv_nvp = nvlist_next_nvpair(nvl, pv_nvp)) == NULL) {
                (void) fprintf(stderr, "%s: malformed property value\n",
                    g_pname);
                return;
        }

        switch (nvpair_type(pv_nvp)) {
                case DATA_TYPE_INT32: {
                        int32_t val;
                        (void) nvpair_value_int32(pv_nvp, &val);
                        (void) printf(" %d", val);
                        break;
                }
                case DATA_TYPE_UINT32: {
                        uint32_t val, type;
                        char val_str[49];
                        nvlist_t *fac, *rsrc = NULL;

                        (void) nvpair_value_uint32(pv_nvp, &val);
                        if (node == NULL || topo_node_flags(node) !=
                            TOPO_NODE_FACILITY)
                                goto uint32_def;

                        if (topo_node_resource(node, &rsrc, &err) != 0)
                                goto uint32_def;

                        if (nvlist_lookup_nvlist(rsrc, "facility", &fac) != 0)
                                goto uint32_def;

                        if (nvlist_lookup_string(fac, FM_FMRI_FACILITY_TYPE,
                            &factype) != 0)
                                goto uint32_def;

                        nvlist_free(rsrc);
                        rsrc = NULL;

                        /*
                         * Special case code to do friendlier printing of
                         * facility node properties
                         */
                        if ((strcmp(propn, TOPO_FACILITY_TYPE) == 0) &&
                            (strcmp(factype, TOPO_FAC_TYPE_SENSOR) == 0)) {
                                topo_sensor_type_name(val, val_str, 48);
                                (void) printf(" 0x%x (%s)", val, val_str);
                                break;
                        } else if ((strcmp(propn, TOPO_FACILITY_TYPE) == 0) &&
                            (strcmp(factype, TOPO_FAC_TYPE_INDICATOR) == 0)) {
                                topo_led_type_name(val, val_str, 48);
                                (void) printf(" 0x%x (%s)", val, val_str);
                                break;
                        } else if (strcmp(propn, TOPO_SENSOR_UNITS) == 0) {
                                topo_sensor_units_name(val, val_str, 48);
                                (void) printf(" 0x%x (%s)", val, val_str);
                                break;
                        } else if (strcmp(propn, TOPO_LED_MODE) == 0) {
                                topo_led_state_name(val, val_str, 48);
                                (void) printf(" 0x%x (%s)", val, val_str);
                                break;
                        } else if ((strcmp(propn, TOPO_SENSOR_STATE) == 0) &&
                            (strcmp(factype, TOPO_FAC_TYPE_SENSOR) == 0)) {
                                if (topo_prop_get_uint32(node,
                                    TOPO_PGROUP_FACILITY, TOPO_FACILITY_TYPE,
                                    &type, &err) != 0) {
                                        goto uint32_def;
                                }
                                topo_sensor_state_name(type, val, val_str, 48);
                                (void) printf(" 0x%x (%s)", val, val_str);
                                break;
                        }
uint32_def:
                        (void) printf(" 0x%x", val);
                        nvlist_free(rsrc);
                        break;
                }
                case DATA_TYPE_INT64: {
                        int64_t val;
                        (void) nvpair_value_int64(pv_nvp, &val);
                        (void) printf(" %lld", (longlong_t)val);
                        break;
                }
                case DATA_TYPE_UINT64: {
                        uint64_t val;
                        (void) nvpair_value_uint64(pv_nvp, &val);
                        (void) printf(" 0x%llx", (u_longlong_t)val);
                        break;
                }
                case DATA_TYPE_DOUBLE: {
                        double val;
                        (void) nvpair_value_double(pv_nvp, &val);
                        (void) printf(" %lf", (double)val);
                        break;
                }
                case DATA_TYPE_STRING: {
                        char *val;
                        (void) nvpair_value_string(pv_nvp, &val);
                        (void) printf(" %s", val);
                        break;
                }
                case DATA_TYPE_NVLIST: {
                        nvlist_t *val;
                        char *fmri;
                        (void) nvpair_value_nvlist(pv_nvp, &val);
                        if (topo_fmri_nvl2str(thp, val, &fmri, &err) != 0) {
                                (void) fprintf(stderr, "failed to convert "
                                    "FMRI to string: (%s)\n",
                                    topo_strerror(err));
                                nvlist_print(stdout, nvl);
                                break;
                        }
                        (void) printf(" %s", fmri);
                        topo_hdl_strfree(thp, fmri);
                        break;
                }
                case DATA_TYPE_INT32_ARRAY: {
                        int32_t *val;

                        (void) nvpair_value_int32_array(pv_nvp, &val, &nelem);
                        (void) printf(" [ ");
                        for (i = 0; i < nelem; i++)
                                (void) printf("%d ", val[i]);
                        (void) printf("]");
                        break;
                }
                case DATA_TYPE_UINT32_ARRAY: {
                        uint32_t *val;

                        (void) nvpair_value_uint32_array(pv_nvp, &val, &nelem);
                        (void) printf(" [ ");
                        for (i = 0; i < nelem; i++)
                                (void) printf("0x%x ", val[i]);
                        (void) printf("]");
                        break;
                }
                case DATA_TYPE_INT64_ARRAY: {
                        int64_t *val;

                        (void) nvpair_value_int64_array(pv_nvp, &val, &nelem);
                        (void) printf(" [ ");
                        for (i = 0; i < nelem; i++)
                                (void) printf("%lld ", val[i]);
                        (void) printf("]");
                        break;
                }
                case DATA_TYPE_UINT64_ARRAY: {
                        uint64_t *val;

                        (void) nvpair_value_uint64_array(pv_nvp, &val, &nelem);
                        (void) printf(" [ ");
                        for (i = 0; i < nelem; i++)
                                (void) printf("0x%llx ", val[i]);
                        (void) printf("]");
                        break;
                }
                case DATA_TYPE_STRING_ARRAY: {
                        char **val;

                        (void) nvpair_value_string_array(pv_nvp, &val, &nelem);
                        (void) printf(" [ ");
                        for (i = 0; i < nelem; i++)
                                (void) printf("\"%s\" ", val[i]);
                        (void) printf("]");
                        break;
                }
                case DATA_TYPE_NVLIST_ARRAY: {
                        nvlist_t **val;
                        char *fmri;
                        int ret;

                        (void) nvpair_value_nvlist_array(pv_nvp, &val, &nelem);
                        (void) printf(" [ ");
                        for (i = 0; i < nelem; i++) {
                                ret = topo_fmri_nvl2str(thp, val[i], &fmri,
                                    &err);
                                if (ret != 0) {
                                        (void) fprintf(stderr, "failed to "
                                            "convert FMRI to string (%s)\n",
                                            topo_strerror(err));
                                        nvlist_print(stdout, val[i]);
                                        break;
                                }
                                (void) printf("\"%s\" ", fmri);
                                topo_hdl_strfree(thp, fmri);
                        }
                        (void) printf("]");
                        break;
                }
                default:
                        (void) fprintf(stderr, " unknown data type (%d)",
                            nvpair_type(pv_nvp));
                        break;
                }
                (void) printf("\n");
}

static void
print_pgroup(topo_hdl_t *thp, tnode_t *node, const char *pgn, char *dstab,
    char *nstab, int32_t version)
{
        int err;
        topo_pgroup_info_t *pgi = NULL;

        if (pgn == NULL)
                return;

        if (node != NULL && (dstab == NULL || nstab == NULL || version == -1)) {
                if ((pgi = topo_pgroup_info(node, pgn, &err)) != NULL) {
                        dstab = (char *)topo_stability2name(pgi->tpi_datastab);
                        nstab = (char *)topo_stability2name(pgi->tpi_namestab);
                        version = pgi->tpi_version;
                }
        }

        if (dstab == NULL || nstab == NULL || version == -1) {
                (void) printf("  group: %-30s version: - stability: -/-\n",
                    pgn);
        } else {
                (void) printf("  group: %-30s version: %-3d stability: %s/%s\n",
                    pgn, version, nstab, dstab);
        }

        if (pgi != NULL) {
                topo_hdl_strfree(thp, (char *)pgi->tpi_name);
                topo_hdl_free(thp, pgi, sizeof (topo_pgroup_info_t));
        }
}

static void
print_all_props(topo_hdl_t *thp, tnode_t *node, nvlist_t *p_nv,
    const char *group)
{
        char *pgn = NULL, *dstab = NULL, *nstab = NULL;
        int32_t version;
        nvlist_t *pg_nv, *pv_nv;
        nvpair_t *nvp, *pg_nvp;
        int pg_done, match, all = strcmp(group, ALL) == 0;

        for (nvp = nvlist_next_nvpair(p_nv, NULL); nvp != NULL;
            nvp = nvlist_next_nvpair(p_nv, nvp)) {
                if (strcmp(TOPO_PROP_GROUP, nvpair_name(nvp)) != 0 ||
                    nvpair_type(nvp) != DATA_TYPE_NVLIST)
                        continue;

                nstab = NULL;
                dstab = NULL;
                version = -1;
                pg_done = match = 0;
                (void) nvpair_value_nvlist(nvp, &pg_nv);
                for (pg_nvp = nvlist_next_nvpair(pg_nv, NULL); pg_nvp != NULL;
                    pg_nvp = nvlist_next_nvpair(pg_nv, pg_nvp)) {
                        /*
                         * Print property group name and stability levels
                         */
                        if (strcmp(TOPO_PROP_GROUP_NAME, nvpair_name(pg_nvp))
                            == 0 && nvpair_type(pg_nvp) == DATA_TYPE_STRING) {
                                (void) nvpair_value_string(pg_nvp, &pgn);
                                match = strcmp(group, pgn) == 0;
                                continue;
                        }

                        if (strcmp(TOPO_PROP_GROUP_NSTAB,
                            nvpair_name(pg_nvp)) == 0 &&
                            nvpair_type(pg_nvp) == DATA_TYPE_STRING) {
                                (void) nvpair_value_string(pg_nvp, &nstab);
                                continue;
                        }

                        if (strcmp(TOPO_PROP_GROUP_DSTAB,
                            nvpair_name(pg_nvp)) == 0 &&
                            nvpair_type(pg_nvp) == DATA_TYPE_STRING) {
                                (void) nvpair_value_string(pg_nvp, &dstab);
                                continue;
                        }

                        if (strcmp(TOPO_PROP_GROUP_VERSION,
                            nvpair_name(pg_nvp)) == 0 &&
                            nvpair_type(pg_nvp) == DATA_TYPE_INT32) {
                                (void) nvpair_value_int32(pg_nvp, &version);
                                continue;
                        }

                        if ((match || all) && !pg_done) {
                                print_pgroup(thp, node, pgn, dstab, nstab,
                                    version);
                                pg_done++;
                        }

                        /*
                         * Print property group and property name-value pair
                         */
                        if (strcmp(TOPO_PROP_VAL, nvpair_name(pg_nvp))
                            == 0 && nvpair_type(pg_nvp) == DATA_TYPE_NVLIST) {
                                (void) nvpair_value_nvlist(pg_nvp, &pv_nv);
                                if ((match || all) && pg_done) {
                                        print_prop_nameval(thp, node, pv_nv);
                                }

                        }

                }
                if (match && !all)
                        return;
        }
}

static void
set_prop(topo_hdl_t *thp, tnode_t *node, nvlist_t *fmri, struct prop_args *pp)
{
        int ret, err = 0;
        topo_type_t type;
        nvlist_t *nvl = NULL;
        char *end;

        if (pp->prop == NULL || pp->type == NULL || pp->value == NULL)
                goto out;

        if ((type = str2type(pp->type)) == TOPO_TYPE_INVALID) {
                (void) fprintf(stderr, "%s: invalid property type %s for %s\n",
                    g_pname, pp->type, pp->prop);
                goto out;
        }

        if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) {
                (void) fprintf(stderr, "%s: nvlist allocation failed for "
                    "%s=%s:%s\n", g_pname, pp->prop, pp->type, pp->value);
                goto out;
        }
        ret = nvlist_add_string(nvl, TOPO_PROP_VAL_NAME, pp->prop);
        ret |= nvlist_add_uint32(nvl, TOPO_PROP_VAL_TYPE, type);
        if (ret != 0) {
                (void) fprintf(stderr, "%s: invalid property type %s for %s\n",
                    g_pname, pp->type, pp->prop);
                goto out;
        }

        errno = 0;
        switch (type) {
                case TOPO_TYPE_INT32:
                {
                        int32_t val;

                        val = strtol(pp->value, &end, 0);
                        if (errno == ERANGE) {
                                ret = -1;
                                break;
                        }
                        ret = nvlist_add_int32(nvl, TOPO_PROP_VAL_VAL, val);
                        break;
                }
                case TOPO_TYPE_UINT32:
                {
                        uint32_t val;

                        val = strtoul(pp->value, &end, 0);
                        if (errno == ERANGE) {
                                ret = -1;
                                break;
                        }
                        ret = nvlist_add_uint32(nvl, TOPO_PROP_VAL_VAL, val);
                        break;
                }
                case TOPO_TYPE_INT64:
                {
                        int64_t val;

                        val = strtoll(pp->value, &end, 0);
                        if (errno == ERANGE) {
                                ret = -1;
                                break;
                        }
                        ret = nvlist_add_int64(nvl, TOPO_PROP_VAL_VAL, val);
                        break;
                }
                case TOPO_TYPE_UINT64:
                {
                        uint64_t val;

                        val = strtoull(pp->value, &end, 0);
                        if (errno == ERANGE) {
                                ret = -1;
                                break;
                        }
                        ret = nvlist_add_uint64(nvl, TOPO_PROP_VAL_VAL, val);
                        break;
                }
                case TOPO_TYPE_STRING:
                {
                        ret = nvlist_add_string(nvl, TOPO_PROP_VAL_VAL,
                            pp->value);
                        break;
                }
                case TOPO_TYPE_FMRI:
                {
                        nvlist_t *val = NULL;

                        if ((ret = topo_fmri_str2nvl(thp, pp->value, &val,
                            &err)) < 0)
                                break;

                        if ((ret = nvlist_add_nvlist(nvl, TOPO_PROP_VAL_VAL,
                            val)) != 0)
                                err = ETOPO_PROP_NVL;

                        nvlist_free(val);
                        break;
                }
                default:
                        ret = -1;
        }

        if (ret != 0) {
                (void) fprintf(stderr, "%s: unable to set property value for "
                    "%s: %s\n", g_pname, pp->prop,  topo_strerror(err));
                goto out;
        }

        if (node != NULL) {
                if ((ret = topo_prop_setprop(node, pp->group, nvl,
                    TOPO_PROP_MUTABLE, nvl, &err)) < 0) {
                        (void) fprintf(stderr, "%s: unable to set property "
                            "value for " "%s=%s:%s: %s\n", g_pname, pp->prop,
                            pp->type, pp->value, topo_strerror(err));
                        goto out;
                }
        } else {
                if ((ret = topo_fmri_setprop(thp, fmri,  pp->group, nvl,
                    TOPO_PROP_MUTABLE, nvl, &err)) < 0) {
                        (void) fprintf(stderr, "%s: unable to set property "
                            "value for " "%s=%s:%s: %s\n", g_pname, pp->prop,
                            pp->type, pp->value, topo_strerror(err));
                        goto out;
                }
        }

        nvlist_free(nvl);
        nvl = NULL;

        /*
         * Now, get the property back for printing
         */
        if (node != NULL) {
                if ((ret = topo_prop_getprop(node, pp->group, pp->prop, NULL,
                    &nvl, &err)) < 0) {
                        (void) fprintf(stderr, "%s: failed to get %s.%s: %s\n",
                            g_pname, pp->group, pp->prop, topo_strerror(err));
                        goto out;
                }
        } else {
                if ((ret = topo_fmri_getprop(thp, fmri, pp->group, pp->prop,
                    NULL, &nvl, &err)) < 0) {
                        (void) fprintf(stderr, "%s: failed to get %s.%s: %s\n",
                            g_pname, pp->group, pp->prop, topo_strerror(err));
                        goto out;
                }
        }

        print_pgroup(thp, node, pp->group, NULL, NULL, 0);
        print_prop_nameval(thp, node, nvl);

out:
        nvlist_free(nvl);
}

static void
print_props(topo_hdl_t *thp, tnode_t *node)
{
        int i, err;
        nvlist_t *nvl;
        struct prop_args *pp;

        if (pcnt == 0)
                return;

        for (i = 0; i < pcnt; ++i) {
                pp = pargs[i];

                if (pp->group == NULL)
                        continue;

                /*
                 * If we have a valid value, this is a request to
                 * set a property.  Otherwise, just print the property
                 * group and any specified properties.
                 */
                if (pp->value == NULL) {
                        if (pp->prop == NULL) {

                                /*
                                 * Print all properties in this group
                                 */
                                if ((nvl = topo_prop_getprops(node, &err))
                                    == NULL) {
                                        (void) fprintf(stderr, "%s: failed to "
                                            "get %s: %s\n", g_pname,
                                            pp->group,
                                            topo_strerror(err));
                                        continue;
                                } else {
                                        print_all_props(thp, node, nvl,
                                            pp->group);
                                        nvlist_free(nvl);
                                        continue;
                                }
                        }
                        if (topo_prop_getprop(node, pp->group, pp->prop,
                            NULL, &nvl, &err) < 0) {
                                (void) fprintf(stderr, "%s: failed to get "
                                    "%s.%s: %s\n", g_pname,
                                    pp->group, pp->prop,
                                    topo_strerror(err));
                                continue;
                        } else {
                                print_pgroup(thp, node, pp->group, NULL,
                                    NULL, 0);
                                print_prop_nameval(thp, node, nvl);
                                nvlist_free(nvl);
                        }
                } else {
                        set_prop(thp, node, NULL, pp);
                }
        }
}

/*ARGSUSED*/
static int
walk_node(topo_hdl_t *thp, tnode_t *node, void *arg)
{
        int err;
        nvlist_t *nvl;
        nvlist_t *rsrc, *out;
        char *s;

        if (opt_e && strcmp(opt_s, FM_FMRI_SCHEME_HC) == 0) {
                print_everstyle(node);
                return (TOPO_WALK_NEXT);
        }

        if (topo_node_resource(node, &rsrc, &err) < 0) {
                (void) fprintf(stderr, "%s: failed to get resource: "
                    "%s", g_pname, topo_strerror(err));
                return (TOPO_WALK_NEXT);
        }
        if (topo_fmri_nvl2str(thp, rsrc, &s, &err) < 0) {
                (void) fprintf(stderr, "%s: failed to convert "
                    "resource to FMRI string: %s", g_pname,
                    topo_strerror(err));
                nvlist_free(rsrc);
                return (TOPO_WALK_NEXT);
        }

        if (g_fmri != NULL && fnmatch(g_fmri, s, 0) != 0) {
                nvlist_free(rsrc);
                topo_hdl_strfree(thp, s);
                return (TOPO_WALK_NEXT);
        }

        print_node(thp, node, rsrc, s);
        topo_hdl_strfree(thp, s);
        nvlist_free(rsrc);

        if (opt_m != NULL) {
                if (topo_method_invoke(node, opt_m, 0, NULL, &out, &err) == 0) {
                        nvlist_print(stdout, out);
                        nvlist_free(out);
                } else if (err != ETOPO_METHOD_NOTSUP)
                        (void) fprintf(stderr, "%s: method failed unexpectedly "
                            "on %s=%d (%s)\n", g_pname, topo_node_name(node),
                            topo_node_instance(node), topo_strerror(err));
        }

        if (opt_V || opt_all) {
                if ((nvl = topo_prop_getprops(node, &err)) == NULL) {
                        (void) fprintf(stderr, "%s: failed to get "
                            "properties for %s=%d: %s\n", g_pname,
                            topo_node_name(node), topo_node_instance(node),
                            topo_strerror(err));
                } else {
                        print_all_props(thp, node, nvl, ALL);
                        nvlist_free(nvl);
                }
        } else if (pcnt > 0)
                print_props(thp, node);

        (void) printf("\n");

        return (TOPO_WALK_NEXT);
}

static void
get_pargs(int argc, char *argv[])
{
        struct prop_args *pp;
        char *s, *p;
        int c;
        int i = 0;

        if ((pargs = malloc(sizeof (struct prop_args *) * pcnt)) == NULL) {
                (void) fprintf(stderr, "%s: failed to allocate property "
                    "arguments\n", g_pname);
                return;
        }

        for (optind = 1; (c = getopt(argc, argv, optstr)) != EOF; ) {
                if (c == 'P') {

                        if (strcmp(optarg, ALL) == 0) {
                                opt_all++;
                                break;
                        }

                        if ((pp = pargs[i] = malloc(sizeof (struct prop_args)))
                            == NULL) {
                                (void) fprintf(stderr, "%s: failed to "
                                    "allocate propertyarguments\n", g_pname);
                                return;
                        }
                        ++i;
                        pp->group = NULL;
                        pp->prop = NULL;
                        pp->type = NULL;
                        pp->value = NULL;

                        p = optarg;
                        if ((s = strchr(p, '.')) != NULL) {
                                *s++ = '\0'; /* strike out delimiter */
                                pp->group = p;
                                p = s;
                                if ((s = strchr(p, '=')) != NULL) {
                                        *s++ = '\0'; /* strike out delimiter */
                                        pp->prop = p;
                                        p = s;
                                        if ((s = strchr(p, ':')) != NULL) {
                                                *s++ = '\0';
                                                pp->type = p;
                                                pp->value = s;
                                        } else {
                                                (void) fprintf(stderr, "%s: "
                                                    "property type not "
                                                    "specified for assignment "
                                                    " of %s.%s\n", g_pname,
                                                    pp->group, pp->prop);
                                                break;
                                        }
                                } else {
                                        pp->prop = p;
                                }
                        } else {
                                pp->group = p;
                        }
                        if (i >= pcnt)
                                break;
                }
        }

        if (opt_all > 0) {
                int j;

                for (j = 0; j < i; ++j)
                        free(pargs[i]);
                free(pargs);
                pargs = NULL;
        }
}

static int
walk_schemes_cb(topo_hdl_t *thp, const topo_scheme_info_t *info, void *arg)
{
        const char *type;
        char unknown[32];

        if (g_fmri != NULL && fnmatch(g_fmri, info->tsi_scheme, 0) != 0) {
                return (TOPO_WALK_NEXT);
        }

        switch (info->tsi_type) {
        case TOPO_SCHEME_TREE:
                type = "tree";
                break;
        case TOPO_SCHEME_DIGRAPH:
                type = "directed graph";
                break;
        default:
                (void) snprintf(unknown, sizeof (unknown), "unknown (0x%x)",
                    info->tsi_type);
                type = unknown;
                break;
        }

        (void) printf("%-15s %s\n", type, info->tsi_scheme);
        return (TOPO_WALK_NEXT);
}

static int
walk_topo(topo_hdl_t *thp, char *uuid)
{
        int err;
        topo_walk_t *twp;
        int flag;

        if (getzoneid() != GLOBAL_ZONEID &&
            strcmp(opt_s, FM_FMRI_SCHEME_HC) == 0) {
                return (0);
        }

        if ((twp = topo_walk_init(thp, opt_s, walk_node, NULL, &err))
            == NULL) {
                (void) fprintf(stderr, "%s: failed to walk %s topology:"
                    " %s\n", g_pname, opt_s, topo_strerror(err));

                return (-1);
        }

        /*
         * Print standard header
         */
        if (!opt_e) {
                char buf[32];
                time_t tod = time(NULL);

                (void) printf("TIME                 UUID\n");
                (void) strftime(buf, sizeof (buf), "%b %d %T", localtime(&tod));
                (void) printf("%-15s %-32s\n", buf, uuid);
                (void) printf("\n");
        }

        flag = opt_b != 0 ? TOPO_WALK_SIBLING : TOPO_WALK_CHILD;

        if (topo_walk_step(twp, flag) == TOPO_WALK_ERR) {
                (void) fprintf(stderr, "%s: failed to walk topology\n",
                    g_pname);
                topo_walk_fini(twp);
                return (-1);
        }

        topo_walk_fini(twp);

        return (0);
}

static void
print_fmri_pgroup(topo_hdl_t *thp, const char *pgn, nvlist_t *nvl)
{
        char *dstab = NULL, *nstab = NULL;
        int32_t version = -1;
        nvlist_t *pnvl;
        nvpair_t *pnvp;

        (void) nvlist_lookup_string(nvl, TOPO_PROP_GROUP_NSTAB, &nstab);
        (void) nvlist_lookup_string(nvl, TOPO_PROP_GROUP_DSTAB, &dstab);
        (void) nvlist_lookup_int32(nvl, TOPO_PROP_GROUP_VERSION, &version);

        print_pgroup(thp, NULL, pgn, dstab, nstab, version);

        for (pnvp = nvlist_next_nvpair(nvl, NULL); pnvp != NULL;
            pnvp = nvlist_next_nvpair(nvl, pnvp)) {

                /*
                 * Print property group and property name-value pair
                 */
                if (strcmp(TOPO_PROP_VAL, nvpair_name(pnvp))
                    == 0 && nvpair_type(pnvp) == DATA_TYPE_NVLIST) {
                        (void) nvpair_value_nvlist(pnvp, &pnvl);
                                print_prop_nameval(thp, NULL, pnvl);

                }

        }
}

static void
print_fmri_props(topo_hdl_t *thp, nvlist_t *nvl)
{
        int i, err;
        struct prop_args *pp;
        nvlist_t *pnvl;

        for (i = 0; i < pcnt; ++i) {
                pp = pargs[i];

                if (pp->group == NULL)
                        continue;

                pnvl = NULL;

                /*
                 * If we have a valid value, this is a request to
                 * set a property.  Otherwise, just print the property
                 * group and any specified properties.
                 */
                if (pp->value == NULL) {
                        if (pp->prop == NULL) {

                                /*
                                 * Print all properties in this group
                                 */
                                if (topo_fmri_getpgrp(thp, nvl, pp->group,
                                    &pnvl, &err) < 0) {
                                        (void) fprintf(stderr, "%s: failed to "
                                            "get group %s: %s\n", g_pname,
                                            pp->group, topo_strerror(err));
                                        continue;
                                } else {
                                        print_fmri_pgroup(thp, pp->group,
                                            pnvl);
                                        nvlist_free(pnvl);
                                        continue;
                                }
                        }
                        if (topo_fmri_getprop(thp, nvl, pp->group, pp->prop,
                            NULL, &pnvl, &err) < 0) {
                                (void) fprintf(stderr, "%s: failed to get "
                                    "%s.%s: %s\n", g_pname,
                                    pp->group, pp->prop,
                                    topo_strerror(err));
                                continue;
                        } else {
                                print_fmri_pgroup(thp, pp->group, pnvl);
                                print_prop_nameval(thp, NULL, pnvl);
                                nvlist_free(nvl);
                        }
                } else {
                        set_prop(thp, NULL, nvl, pp);
                }
        }
}

void
print_fmri(topo_hdl_t *thp, char *uuid)
{
        int ret, err;
        nvlist_t *nvl;
        char buf[32];
        time_t tod = time(NULL);

        if (topo_fmri_str2nvl(thp, g_fmri, &nvl, &err) < 0) {
                (void) fprintf(stderr, "%s: failed to convert %s to nvlist: "
                    "%s\n", g_pname, g_fmri, topo_strerror(err));
                return;
        }

        (void) printf("TIME                 UUID\n");
        (void) strftime(buf, sizeof (buf), "%b %d %T", localtime(&tod));
        (void) printf("%-15s %-32s\n", buf, uuid);
        (void) printf("\n");

        (void) printf("%s\n", (char *)g_fmri);

        if (opt_p && !(pcnt > 0 || opt_V || opt_all)) {
                char *aname = NULL, *fname = NULL, *lname = NULL;
                nvlist_t *asru = NULL;
                nvlist_t *fru = NULL;

                if (topo_fmri_asru(thp, nvl, &asru, &err) == 0)
                        (void) topo_fmri_nvl2str(thp, asru, &aname, &err);
                if (topo_fmri_fru(thp, nvl, &fru, &err) == 0)
                        (void) topo_fmri_nvl2str(thp, fru, &fname, &err);
                (void) topo_fmri_label(thp, nvl, &lname, &err);

                nvlist_free(fru);
                nvlist_free(asru);

                if (aname != NULL) {
                        (void) printf("\tASRU: %s\n", aname);
                        topo_hdl_strfree(thp, aname);
                } else {
                        (void) printf("\tASRU: -\n");
                }
                if (fname != NULL) {
                        (void) printf("\tFRU: %s\n", fname);
                        topo_hdl_strfree(thp, fname);
                } else {
                        (void) printf("\tFRU: -\n");
                }
                if (lname != NULL) {
                        (void) printf("\tLabel: %s\n", lname);
                        topo_hdl_strfree(thp, lname);
                } else {
                        (void) printf("\tLabel: -\n");
                }
        }

        if (opt_S) {
                if (topo_fmri_str2nvl(thp, g_fmri, &nvl, &err) < 0) {
                        (void) printf("\tPresent: -\n");
                        (void) printf("\tUnusable: -\n");
                        return;
                }

                if ((ret = topo_fmri_present(thp, nvl, &err)) < 0)
                        (void) printf("\tPresent: -\n");
                else
                        (void) printf("\tPresent: %s\n",
                            ret ? "true" : "false");

                if ((ret = topo_fmri_unusable(thp, nvl, &err)) < 0)
                        (void) printf("\tUnusable: -\n");
                else
                        (void) printf("\tUnusable: %s\n",
                            ret ? "true" : "false");

                nvlist_free(nvl);
        }

        if (pargs && pcnt > 0)
                print_fmri_props(thp, nvl);
}

int
fmtopo_exit(topo_hdl_t *thp, char *uuid, int err)
{
        if (uuid != NULL)
                topo_hdl_strfree(thp, uuid);

        if (thp != NULL) {
                topo_snap_release(thp);
                topo_close(thp);
        }

        if (pargs) {
                int i;
                for (i = 0; i < pcnt; ++i)
                        free(pargs[i]);
                free(pargs);
        }

        return (err);
}

int
main(int argc, char *argv[])
{
        topo_hdl_t *thp = NULL;
        char *uuid = NULL;
        int c, err = 0;

        g_pname = argv[0];

        while (optind < argc) {
                while ((c = getopt(argc, argv, optstr)) != -1) {
                        switch (c) {
                        case 'b':
                                opt_b++;
                                break;
                        case 'C':
                                (void) atexit(abort);
                                break;
                        case 'd':
                                opt_d++;
                                break;
                        case 'e':
                                opt_e++;
                                break;
                        case 'l':
                                opt_l++;
                                break;
                        case 'm':
                                opt_m = optarg;
                                break;
                        case 'P':
                                pcnt++;
                                break;
                        case 'p':
                                opt_p++;
                                break;
                        case 'V':
                                opt_V++;
                                break;
                        case 'R':
                                opt_R = optarg;
                                break;
                        case 's':
                                opt_s = optarg;
                                break;
                        case 'S':
                                opt_S++;
                                break;
                        case 't':
                                opt_t++;
                                break;
                        case 'x':
                                opt_x++;
                                break;
                        default:
                                return (usage(stderr));
                        }
                }

                if (optind < argc) {
                        if (g_fmri != NULL) {
                                (void) fprintf(stderr, "%s: illegal argument "
                                    "-- %s\n", g_pname, argv[optind]);
                                return (FMTOPO_EXIT_USAGE);
                        } else {
                                g_fmri = argv[optind++];
                        }
                }
        }

        if (pcnt > 0)
                get_pargs(argc, argv);

        if ((thp = topo_open(TOPO_VERSION, opt_R, &err)) == NULL) {
                (void) fprintf(stderr, "%s: failed to open topology tree: %s\n",
                    g_pname, topo_strerror(err));
                return (fmtopo_exit(thp, uuid, FMTOPO_EXIT_ERROR));
        }

        if (opt_d)
                topo_debug_set(thp, "module", "stderr");

        if ((uuid = topo_snap_hold(thp, NULL, &err)) == NULL) {
                (void) fprintf(stderr, "%s: failed to snapshot topology: %s\n",
                    g_pname, topo_strerror(err));
                return (fmtopo_exit(thp, uuid, FMTOPO_EXIT_ERROR));
        } else if (err != 0) {
                (void) fprintf(stderr, "%s: topology snapshot incomplete%s\n",
                    g_pname, getzoneid() != GLOBAL_ZONEID &&
                    strcmp(opt_s, FM_FMRI_SCHEME_HC) == 0 ?
                    " (" FM_FMRI_SCHEME_HC " scheme does not enumerate "
                    "in a non-global zone)": "");
        }

        if (opt_x) {
                if (opt_b) {
                        (void) fprintf(stderr,
                            "%s: -b and -x cannot be specified together\n",
                            g_pname);
                        return (fmtopo_exit(thp, uuid, FMTOPO_EXIT_USAGE));
                }

                if (opt_l) {
                        (void) fprintf(stderr,
                            "%s: -l and -x cannot be specified together\n",
                            g_pname);
                        return (fmtopo_exit(thp, uuid, FMTOPO_EXIT_USAGE));
                }

                err = 0;
                if (topo_xml_print(thp, stdout, opt_s, &err) < 0)
                        (void) fprintf(stderr, "%s: failed to print xml "
                            "formatted topology:%s",  g_pname,
                            topo_strerror(err));

                return (fmtopo_exit(thp, uuid, err ? FMTOPO_EXIT_ERROR :
                    FMTOPO_EXIT_SUCCESS));
        }

        if (opt_l) {
                if (opt_b || opt_e || opt_m || pcnt > 0 || opt_p || opt_V ||
                    opt_S || opt_x) {
                        (void) fprintf(stderr,
                            "%s: -l may only be used with -c, -d, and -R\n",
                            g_pname);
                        return (fmtopo_exit(thp, uuid, FMTOPO_EXIT_USAGE));
                }

                (void) printf("%-15s %s\n", "TYPE", "NAME");
                (void) topo_scheme_walk(thp, walk_schemes_cb, NULL);
                return (fmtopo_exit(thp, uuid, FMTOPO_EXIT_SUCCESS));
        }

        if (opt_t || walk_topo(thp, uuid) < 0) {
                if (g_fmri != NULL)
                        /*
                         * Try getting some useful information
                         */
                        print_fmri(thp, uuid);

                return (fmtopo_exit(thp, uuid, FMTOPO_EXIT_ERROR));
        }

        return (fmtopo_exit(thp, uuid, FMTOPO_EXIT_SUCCESS));
}