2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A
2N/A/*
2N/A * Copyright (c) 2006, 2012, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A#include <ctype.h>
2N/A#include <stdio.h>
2N/A#include <strings.h>
2N/A#include <time.h>
2N/A#include <sys/types.h>
2N/A#include <sys/fm/protocol.h>
2N/A#include <sys/utsname.h>
2N/A
2N/A#include <topo_parse.h>
2N/A#include <topo_prop.h>
2N/A#include <topo_tree.h>
2N/A#include <topo_subr.h>
2N/A
2N/A#define INT32BUFSZ sizeof (UINT32_MAX) + 1
2N/A/* 2 bytes for "0x" + 16 bytes for the hex value + 1 for sign + null */
2N/A#define INT64BUFSZ 20
2N/A#define XML_VERSION "1.0"
2N/A
2N/Astatic int txml_print_range(topo_hdl_t *, FILE *, tnode_t *, int);
2N/A
2N/Avoid
2N/Aprint_header(FILE *fp)
2N/A{
2N/A char buf[32];
2N/A time_t tod = time(NULL);
2N/A struct utsname uts;
2N/A
2N/A (void) fprintf(fp, "<?xml version=\"%s\"?>\n", XML_VERSION);
2N/A (void) fprintf(fp, "<!DOCTYPE topology SYSTEM \"%s\">\n",
2N/A TOPO_DTD_PATH);
2N/A
2N/A (void) uname(&uts);
2N/A (void) strftime(buf, sizeof (buf), "%b %d %T", localtime(&tod));
2N/A (void) fprintf(fp, "<!--\n");
2N/A (void) fprintf(fp, " This topology map file was generated on "
2N/A "%-15s for %s\n", buf, uts.nodename);
2N/A (void) fprintf(fp, "<-->\n\n");
2N/A}
2N/A
2N/Avoid
2N/Abegin_element(FILE *fp, const char *ename, ...)
2N/A{
2N/A char *name, *value;
2N/A va_list ap;
2N/A
2N/A (void) fprintf(fp, "<%s ", ename);
2N/A va_start(ap, ename);
2N/A name = va_arg(ap, char *);
2N/A while (name != NULL) {
2N/A value = va_arg(ap, char *);
2N/A (void) fprintf(fp, "%s='%s' ", name, value);
2N/A name = va_arg(ap, char *);
2N/A }
2N/A (void) fprintf(fp, ">\n");
2N/A}
2N/A
2N/Avoid
2N/Abegin_end_element(FILE *fp, const char *ename, ...)
2N/A{
2N/A char *name, *value;
2N/A va_list ap;
2N/A
2N/A (void) fprintf(fp, "<%s ", ename);
2N/A va_start(ap, ename);
2N/A name = va_arg(ap, char *);
2N/A while (name != NULL) {
2N/A value = va_arg(ap, char *);
2N/A (void) fprintf(fp, "%s='%s' ", name, value);
2N/A name = va_arg(ap, char *);
2N/A }
2N/A (void) fprintf(fp, "/>\n");
2N/A}
2N/A
2N/Avoid
2N/Aend_element(FILE *fp, const char *ename)
2N/A{
2N/A (void) fprintf(fp, "</%s>\n", ename);
2N/A}
2N/A
2N/A/*
2N/A * Special character escape sequences.
2N/A */
2N/Astatic struct {
2N/A char c;
2N/A char *esc;
2N/A} c2esc[] = {
2N/A { '&', "&amp;" },
2N/A { '<', "&lt;" },
2N/A { '>', "&gt;" },
2N/A { '"', "&quot;" },
2N/A { '\'', "&apos;" }
2N/A};
2N/A
2N/Astatic int
2N/Atxml_str2xmlstr(topo_hdl_t *thp, char **oldstrpp)
2N/A{
2N/A int oldlen, newlen, esclen, i, j;
2N/A int nent = sizeof (c2esc) / sizeof (*c2esc);
2N/A char *newstrp, *xp, *oldstrp = *oldstrpp;
2N/A
2N/A if (oldstrp == NULL)
2N/A return (-1);
2N/A
2N/A oldlen = strlen(oldstrp);
2N/A
2N/A topo_dprintf(thp, TOPO_DBG_XML, "%s: thp=0x%p, oldstrp=0x%p=%s, "
2N/A "oldlen=%d\n", __func__, (void *)thp, (void *)oldstrp,
2N/A oldstrp, oldlen);
2N/A
2N/A /*
2N/A * Parse the string for special characters that must be
2N/A * escaped, and calculate the length of the new string.
2N/A */
2N/A for (i = 0, newlen = 0; i < oldlen; i++) {
2N/A /*
2N/A * If the character is non-printable, replace it with '-'.
2N/A */
2N/A if (!isprint(oldstrp[i])) {
2N/A topo_dprintf(thp, TOPO_DBG_XML,
2N/A "%s: [%d]: non-printable char\n", __func__, i);
2N/A oldstrp[i] = '-';
2N/A newlen++;
2N/A continue;
2N/A }
2N/A for (j = 0; j < nent; j++) {
2N/A if (oldstrp[i] == c2esc[j].c)
2N/A break;
2N/A }
2N/A if (j < nent) {
2N/A newlen += strlen(c2esc[j].esc);
2N/A topo_dprintf(thp, TOPO_DBG_XML,
2N/A "%s: [%d]: special char=%c, newlen=%d\n",
2N/A __func__, i, oldstrp[i], newlen);
2N/A } else {
2N/A newlen++;
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * If no special characters were found just return,
2N/A * else allocate a new string.
2N/A */
2N/A if (newlen == oldlen)
2N/A return (0);
2N/A if ((newstrp = topo_hdl_zalloc(thp, newlen + 1)) == NULL)
2N/A return (-1);
2N/A
2N/A topo_dprintf(thp, TOPO_DBG_XML, "%s: newstrp=0x%p, newlen=%d\n",
2N/A __func__, (void *)newstrp, newlen);
2N/A
2N/A /*
2N/A * Replace any special characters with their escape sequences.
2N/A */
2N/A for (i = 0, xp = newstrp; i < oldlen; i++) {
2N/A for (j = 0; j < nent; j++) {
2N/A if (oldstrp[i] == c2esc[j].c)
2N/A break;
2N/A }
2N/A if (j < nent) {
2N/A esclen = strlen(c2esc[j].esc);
2N/A (void) strncpy(xp, c2esc[j].esc, esclen);
2N/A xp += esclen;
2N/A topo_dprintf(thp, TOPO_DBG_XML,
2N/A "%s: [%d]: special char=%c, newstr=%s\n",
2N/A __func__, i, oldstrp[i], newstrp);
2N/A } else {
2N/A *xp++ = oldstrp[i];
2N/A }
2N/A }
2N/A
2N/A topo_dprintf(thp, TOPO_DBG_XML, "%s: oldstrp=0x%p=%s, "
2N/A "newstrp=0x%p=%s\n", __func__, (void *)oldstrp, oldstrp,
2N/A (void *)newstrp, newstrp);
2N/A
2N/A topo_hdl_strfree(thp, oldstrp);
2N/A *oldstrpp = newstrp;
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Astatic void
2N/Atxml_print_prop(topo_hdl_t *thp, FILE *fp, tnode_t *node, const char *pgname,
2N/A topo_propval_t *pv)
2N/A{
2N/A int err;
2N/A char *fmri = NULL;
2N/A char vbuf[INT64BUFSZ], tbuf[32], *pval = NULL, *aval = NULL;
2N/A
2N/A switch (pv->tp_type) {
2N/A case TOPO_TYPE_INT32: {
2N/A int32_t val;
2N/A if (topo_prop_get_int32(node, pgname, pv->tp_name, &val,
2N/A &err) == 0) {
2N/A (void) snprintf(vbuf, INT64BUFSZ, "%d", val);
2N/A (void) snprintf(tbuf, 10, "%s", Int32);
2N/A pval = vbuf;
2N/A } else
2N/A return;
2N/A break;
2N/A }
2N/A case TOPO_TYPE_UINT32: {
2N/A uint32_t val;
2N/A if (topo_prop_get_uint32(node, pgname, pv->tp_name,
2N/A &val, &err) == 0) {
2N/A (void) snprintf(vbuf, INT64BUFSZ, "0x%x", val);
2N/A (void) snprintf(tbuf, 10, "%s", UInt32);
2N/A pval = vbuf;
2N/A } else
2N/A return;
2N/A break;
2N/A }
2N/A case TOPO_TYPE_INT64: {
2N/A int64_t val;
2N/A if (topo_prop_get_int64(node, pgname, pv->tp_name, &val,
2N/A &err) == 0) {
2N/A (void) snprintf(vbuf, INT64BUFSZ, "0x%llx",
2N/A (longlong_t)val);
2N/A (void) snprintf(tbuf, 10, "%s", Int64);
2N/A pval = vbuf;
2N/A } else
2N/A return;
2N/A break;
2N/A }
2N/A case TOPO_TYPE_UINT64: {
2N/A uint64_t val;
2N/A if (topo_prop_get_uint64(node, pgname, pv->tp_name,
2N/A &val, &err) == 0) {
2N/A (void) snprintf(vbuf, INT64BUFSZ, "0x%llx",
2N/A (u_longlong_t)val);
2N/A (void) snprintf(tbuf, 10, "%s", UInt64);
2N/A pval = vbuf;
2N/A } else
2N/A return;
2N/A break;
2N/A }
2N/A case TOPO_TYPE_STRING: {
2N/A if (topo_prop_get_string(node, pgname, pv->tp_name,
2N/A &pval, &err) != 0)
2N/A return;
2N/A if (txml_str2xmlstr(thp, &pval) != 0)
2N/A return;
2N/A (void) snprintf(tbuf, 10, "%s", "string");
2N/A break;
2N/A }
2N/A case TOPO_TYPE_FMRI: {
2N/A nvlist_t *val;
2N/A
2N/A if (topo_prop_get_fmri(node, pgname, pv->tp_name, &val,
2N/A &err) == 0) {
2N/A if (topo_fmri_nvl2str(thp, val, &fmri, &err)
2N/A == 0) {
2N/A nvlist_free(val);
2N/A pval = fmri;
2N/A } else {
2N/A nvlist_free(val);
2N/A return;
2N/A }
2N/A } else
2N/A return;
2N/A (void) snprintf(tbuf, 10, "%s", FMRI);
2N/A break;
2N/A }
2N/A case TOPO_TYPE_UINT32_ARRAY: {
2N/A uint32_t *val;
2N/A uint_t nelem, i;
2N/A if (topo_prop_get_uint32_array(node, pgname,
2N/A pv->tp_name, &val, &nelem, &err) != 0)
2N/A return;
2N/A
2N/A /*
2N/A * This does not generating proper XML arrays.
2N/A * Note that if we fix this then we must also fix the
2N/A * fabric translator module which parses the existing
2N/A * uint32_array property "assigned-addresses".
2N/A */
2N/A if (nelem > 0) {
2N/A if ((aval = calloc((nelem * 9 - 1),
2N/A sizeof (uchar_t))) == NULL) {
2N/A
2N/A topo_hdl_free(thp, val,
2N/A nelem * sizeof (uint32_t));
2N/A return;
2N/A }
2N/A
2N/A (void) sprintf(aval, "0x%x", val[0]);
2N/A for (i = 1; i < nelem; i++) {
2N/A (void) sprintf(vbuf, " 0x%x", val[i]);
2N/A (void) strcat(aval, vbuf);
2N/A }
2N/A topo_hdl_free(thp, val,
2N/A nelem * sizeof (uint32_t));
2N/A (void) snprintf(tbuf, 13, "%s", UInt32_Arr);
2N/A pval = aval;
2N/A }
2N/A break;
2N/A }
2N/A default:
2N/A return;
2N/A }
2N/A
2N/A begin_end_element(fp, Propval, Name, pv->tp_name, Type, tbuf,
2N/A Value, pval, NULL);
2N/A
2N/A if (pval != NULL && pv->tp_type == TOPO_TYPE_STRING)
2N/A topo_hdl_strfree(thp, pval);
2N/A
2N/A if (fmri != NULL)
2N/A topo_hdl_strfree(thp, fmri);
2N/A
2N/A if (aval != NULL)
2N/A free(aval);
2N/A}
2N/A
2N/Astatic void
2N/Atxml_print_pgroup(topo_hdl_t *thp, FILE *fp, tnode_t *node, topo_pgroup_t *pg)
2N/A{
2N/A topo_ipgroup_info_t *pip = pg->tpg_info;
2N/A topo_proplist_t *plp;
2N/A const char *namestab, *datastab;
2N/A char version[INT32BUFSZ];
2N/A
2N/A namestab = topo_stability2name(pip->tpi_namestab);
2N/A datastab = topo_stability2name(pip->tpi_datastab);
2N/A (void) snprintf(version, INT32BUFSZ, "%d", pip->tpi_version);
2N/A begin_element(fp, Propgrp, Name, pip->tpi_name, Namestab,
2N/A namestab, Datastab, datastab, Version, version, NULL);
2N/A for (plp = topo_list_next(&pg->tpg_pvals); plp != NULL;
2N/A plp = topo_list_next(plp)) {
2N/A txml_print_prop(thp, fp, node, pip->tpi_name, plp->tp_pval);
2N/A }
2N/A end_element(fp, Propgrp);
2N/A}
2N/A
2N/Astatic void
2N/Atxml_print_dependents(topo_hdl_t *thp, FILE *fp, tnode_t *node)
2N/A{
2N/A if (topo_list_next(&node->tn_children) == NULL)
2N/A return;
2N/A
2N/A if (txml_print_range(thp, fp, node, 1) == 1)
2N/A end_element(fp, Dependents);
2N/A}
2N/A
2N/Astatic void
2N/Atxml_print_node(topo_hdl_t *thp, FILE *fp, tnode_t *node)
2N/A{
2N/A char inst[INT32BUFSZ];
2N/A topo_pgroup_t *pg;
2N/A
2N/A (void) snprintf(inst, INT32BUFSZ, "%d", node->tn_instance);
2N/A begin_element(fp, Node, Instance, inst, Static, True, NULL);
2N/A for (pg = topo_list_next(&node->tn_pgroups); pg != NULL;
2N/A pg = topo_list_next(pg)) {
2N/A txml_print_pgroup(thp, fp, node, pg);
2N/A }
2N/A txml_print_dependents(thp, fp, node);
2N/A end_element(fp, Node);
2N/A
2N/A}
2N/A
2N/Astatic int
2N/Atxml_print_range(topo_hdl_t *thp, FILE *fp, tnode_t *node, int dependent)
2N/A{
2N/A int i, create = 0, ret = 0;
2N/A topo_nodehash_t *nhp;
2N/A char min[INT32BUFSZ], max[INT32BUFSZ];
2N/A
2N/A for (nhp = topo_list_next(&node->tn_children); nhp != NULL;
2N/A nhp = topo_list_next(nhp)) {
2N/A (void) snprintf(min, INT32BUFSZ, "%d", nhp->th_range.tr_min);
2N/A (void) snprintf(max, INT32BUFSZ, "%d", nhp->th_range.tr_max);
2N/A
2N/A /*
2N/A * Some enumerators create empty ranges: make sure there
2N/A * are real nodes before creating this range
2N/A */
2N/A for (i = 0; i < nhp->th_arrlen; ++i) {
2N/A if (nhp->th_nodearr[i] != NULL)
2N/A ++create;
2N/A }
2N/A if (!create)
2N/A continue;
2N/A
2N/A if (dependent) {
2N/A begin_element(fp, Dependents, Grouping, Children, NULL);
2N/A dependent = 0;
2N/A ret = 1;
2N/A }
2N/A begin_element(fp, Range, Name, nhp->th_name, Min, min, Max,
2N/A max, NULL);
2N/A for (i = 0; i < nhp->th_arrlen; ++i) {
2N/A if (nhp->th_nodearr[i] != NULL)
2N/A txml_print_node(thp, fp, nhp->th_nodearr[i]);
2N/A }
2N/A end_element(fp, Range);
2N/A }
2N/A
2N/A return (ret);
2N/A}
2N/A
2N/Astatic void
2N/Atxml_print_topology(topo_hdl_t *thp, FILE *fp, char *scheme, tnode_t *node)
2N/A{
2N/A char timestamp_str[20];
2N/A time_t t = thp->th_timestamp;
2N/A char *sys_mfg = NULL, *sys_name = NULL;
2N/A char *sys_pn = NULL, *sys_sn = NULL;
2N/A char *sc_mfg = NULL, *sc_name = NULL;
2N/A char *sc_pn = NULL, *sc_sn = NULL;
2N/A char *ch_mfg = NULL, *ch_name = NULL;
2N/A char *ch_pn, *ch_sn = NULL;
2N/A
2N/A (void) sprintf((char *)timestamp_str, "0x%lx", (ulong_t)t);
2N/A
2N/A (void) nvlist_lookup_string(thp->th_auth, FM_FMRI_AUTH_V1_SYSTEM_MFG,
2N/A &sys_mfg);
2N/A (void) nvlist_lookup_string(thp->th_auth, FM_FMRI_AUTH_V1_SYSTEM_NM,
2N/A &sys_name);
2N/A (void) nvlist_lookup_string(thp->th_auth, FM_FMRI_AUTH_V1_SYSTEM_PN,
2N/A &sys_pn);
2N/A (void) nvlist_lookup_string(thp->th_auth, FM_FMRI_AUTH_V1_SYSTEM_SN,
2N/A &sys_sn);
2N/A
2N/A (void) nvlist_lookup_string(thp->th_auth, FM_FMRI_AUTH_V1_SYS_COMP_MFG,
2N/A &sc_mfg);
2N/A (void) nvlist_lookup_string(thp->th_auth, FM_FMRI_AUTH_V1_SYS_COMP_NM,
2N/A &sc_name);
2N/A (void) nvlist_lookup_string(thp->th_auth, FM_FMRI_AUTH_V1_SYS_COMP_PN,
2N/A &sc_pn);
2N/A (void) nvlist_lookup_string(thp->th_auth, FM_FMRI_AUTH_V1_SYS_COMP_SN,
2N/A &sc_sn);
2N/A
2N/A (void) nvlist_lookup_string(thp->th_auth, FM_FMRI_AUTH_V1_CHASSIS_MFG,
2N/A &ch_mfg);
2N/A (void) nvlist_lookup_string(thp->th_auth, FM_FMRI_AUTH_V1_CHASSIS_NM,
2N/A &ch_name);
2N/A (void) nvlist_lookup_string(thp->th_auth, FM_FMRI_AUTH_V1_CHASSIS_PN,
2N/A &ch_pn);
2N/A (void) nvlist_lookup_string(thp->th_auth, FM_FMRI_AUTH_V1_CHASSIS_SN,
2N/A &ch_sn);
2N/A
2N/A topo_dprintf(thp, TOPO_DBG_XML, "%s: thp=0x%p,"
2N/A "scheme=%s, uuid=%s, timestamp=%s=%s\n"
2N/A "system-mfg=%s, system-name=%s, system-part=%s, system-serial=%s\n"
2N/A "sys-comp-mfg=%s, sys-comp-name=%s, sys-comp-part=%s "
2N/A "sys-comp-serial=%s\nchassis-mfg=%s, chassis-name=%s "
2N/A "chassis-part=%s, chassis-serial=%s\n",
2N/A __func__, (void *)thp, scheme, thp->th_uuid,
2N/A timestamp_str, ctime(&t), sys_mfg ? sys_mfg : "unknown",
2N/A sys_name ? sys_name : "unknown", sys_pn ? sys_pn : "unknown",
2N/A sys_sn ? sys_sn : "unknown", sc_mfg ? sc_mfg : "unknown",
2N/A sc_name ? sc_name : "unknown", sc_pn ? sc_pn : "unknown",
2N/A sc_sn ? sc_sn : "unknown", ch_mfg ? ch_mfg : "unknown",
2N/A ch_name ? ch_name : "unknown", ch_pn ? ch_pn : "unknown",
2N/A ch_sn ? ch_sn : "unknown");
2N/A
2N/A begin_element(fp, Topology, Scheme, scheme, UUID,
2N/A thp->th_uuid, Timestamp, timestamp_str,
2N/A FM_FMRI_AUTH_V1_SYSTEM_MFG, sys_mfg ? sys_mfg : "unknown",
2N/A FM_FMRI_AUTH_V1_SYSTEM_NM, sys_name ? sys_name : "unknown",
2N/A FM_FMRI_AUTH_V1_SYSTEM_PN, sys_pn ? sys_pn : "unknown",
2N/A FM_FMRI_AUTH_V1_SYSTEM_SN, sys_sn ? sys_sn : "unknown",
2N/A FM_FMRI_AUTH_V1_SYS_COMP_MFG, sc_mfg ? sc_mfg : "unknown",
2N/A FM_FMRI_AUTH_V1_SYS_COMP_NM, sc_name ? sc_name : "unknown",
2N/A FM_FMRI_AUTH_V1_SYS_COMP_PN, sc_pn ? sc_pn : "unknown",
2N/A FM_FMRI_AUTH_V1_SYS_COMP_SN, sc_sn ? sc_sn : "unknown",
2N/A FM_FMRI_AUTH_V1_CHASSIS_MFG, ch_mfg ? ch_mfg : "unknown",
2N/A FM_FMRI_AUTH_V1_CHASSIS_NM, ch_name ? ch_name : "unknown",
2N/A FM_FMRI_AUTH_V1_CHASSIS_PN, ch_pn ? ch_pn : "unknown",
2N/A FM_FMRI_AUTH_V1_CHASSIS_SN, ch_sn ? ch_sn : "unknown", NULL);
2N/A (void) txml_print_range(thp, fp, node, 0);
2N/A end_element(fp, Topology);
2N/A
2N/A}
2N/A
2N/Aint
2N/Atopo_xml_print(topo_hdl_t *thp, FILE *fp, const char *scheme, int *err)
2N/A{
2N/A ttree_t *tp;
2N/A
2N/A print_header(fp);
2N/A for (tp = topo_list_next(&thp->th_trees); tp != NULL;
2N/A tp = topo_list_next(tp)) {
2N/A if (strcmp(scheme, tp->tt_scheme) == 0) {
2N/A txml_print_topology(thp, fp, tp->tt_scheme,
2N/A tp->tt_root);
2N/A (void) fflush(fp);
2N/A return (0);
2N/A }
2N/A }
2N/A
2N/A *err = EINVAL;
2N/A return (-1);
2N/A}