1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <libxml/parser.h> 30 #include <libxml/xinclude.h> 31 #include <sys/fm/protocol.h> 32 #include <assert.h> 33 #include <string.h> 34 #include <strings.h> 35 #include <ctype.h> 36 #include <errno.h> 37 #include <limits.h> 38 #include <fm/libtopo.h> 39 #include <unistd.h> 40 #include <sys/stat.h> 41 #include <fcntl.h> 42 #include <topo_file.h> 43 #include <topo_mod.h> 44 #include <topo_subr.h> 45 #include <topo_alloc.h> 46 #include <topo_parse.h> 47 #include <topo_error.h> 48 49 static tf_rdata_t *topo_xml_walk(topo_mod_t *, tf_info_t *, xmlNodePtr, 50 tnode_t *); 51 static tf_edata_t *enum_attributes_process(topo_mod_t *, xmlNodePtr); 52 static int enum_run(topo_mod_t *, tf_rdata_t *); 53 static int decorate_nodes(topo_mod_t *, tf_rdata_t *, xmlNodePtr, tnode_t *, 54 tf_pad_t **); 55 56 57 int 58 xmlattr_to_stab(topo_mod_t *mp, xmlNodePtr n, const char *stabname, 59 topo_stability_t *rs) 60 { 61 xmlChar *str; 62 int rv = 0; 63 64 if (n == NULL) { 65 /* If there is no Stability defined, we default to private */ 66 *rs = TOPO_STABILITY_PRIVATE; 67 return (0); 68 } 69 if ((str = xmlGetProp(n, (xmlChar *)stabname)) == NULL) { 70 topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, 71 "attribute to stability:\n"); 72 return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR)); 73 } 74 75 if (xmlStrcmp(str, (xmlChar *)Internal) == 0) { 76 *rs = TOPO_STABILITY_INTERNAL; 77 } else if (xmlStrcmp(str, (xmlChar *)Private) == 0) { 78 *rs = TOPO_STABILITY_PRIVATE; 79 } else if (xmlStrcmp(str, (xmlChar *)Obsolete) == 0) { 80 *rs = TOPO_STABILITY_OBSOLETE; 81 } else if (xmlStrcmp(str, (xmlChar *)External) == 0) { 82 *rs = TOPO_STABILITY_EXTERNAL; 83 } else if (xmlStrcmp(str, (xmlChar *)Unstable) == 0) { 84 *rs = TOPO_STABILITY_UNSTABLE; 85 } else if (xmlStrcmp(str, (xmlChar *)Evolving) == 0) { 86 *rs = TOPO_STABILITY_EVOLVING; 87 } else if (xmlStrcmp(str, (xmlChar *)Stable) == 0) { 88 *rs = TOPO_STABILITY_STABLE; 89 } else if (xmlStrcmp(str, (xmlChar *)Standard) == 0) { 90 *rs = TOPO_STABILITY_STANDARD; 91 } else { 92 xmlFree(str); 93 return (topo_mod_seterrno(mp, ETOPO_PRSR_BADSTAB)); 94 } 95 xmlFree(str); 96 return (rv); 97 } 98 99 int 100 xmlattr_to_int(topo_mod_t *mp, 101 xmlNodePtr n, const char *propname, uint64_t *value) 102 { 103 xmlChar *str; 104 xmlChar *estr; 105 106 topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "attribute to int\n"); 107 if ((str = xmlGetProp(n, (xmlChar *)propname)) == NULL) 108 return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR)); 109 *value = strtoull((char *)str, (char **)&estr, 10); 110 if (estr == str) { 111 /* no conversion was done */ 112 xmlFree(str); 113 return (topo_mod_seterrno(mp, ETOPO_PRSR_BADNUM)); 114 } 115 xmlFree(str); 116 return (0); 117 } 118 119 static int 120 xmlattr_to_fmri(topo_mod_t *mp, 121 xmlNodePtr xn, const char *propname, nvlist_t **rnvl) 122 { 123 xmlChar *str; 124 125 topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "attribute to int\n"); 126 if ((str = xmlGetProp(xn, (xmlChar *)propname)) == NULL) 127 return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR)); 128 if (topo_mod_str2nvl(mp, (const char *)str, rnvl) < 0) { 129 xmlFree(str); 130 return (-1); 131 } 132 xmlFree(str); 133 return (0); 134 } 135 136 static topo_type_t 137 xmlattr_to_type(topo_mod_t *mp, xmlNodePtr xn, xmlChar *attr) 138 { 139 topo_type_t rv; 140 xmlChar *str; 141 if ((str = xmlGetProp(xn, (xmlChar *)attr)) == NULL) { 142 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "%s attribute missing", 143 attr); 144 (void) topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR); 145 return (TOPO_TYPE_INVALID); 146 } 147 if (xmlStrcmp(str, (xmlChar *)Int32) == 0) { 148 rv = TOPO_TYPE_INT32; 149 } else if (xmlStrcmp(str, (xmlChar *)UInt32) == 0) { 150 rv = TOPO_TYPE_UINT32; 151 } else if (xmlStrcmp(str, (xmlChar *)Int64) == 0) { 152 rv = TOPO_TYPE_INT64; 153 } else if (xmlStrcmp(str, (xmlChar *)UInt64) == 0) { 154 rv = TOPO_TYPE_UINT64; 155 } else if (xmlStrcmp(str, (xmlChar *)FMRI) == 0) { 156 rv = TOPO_TYPE_FMRI; 157 } else if (xmlStrcmp(str, (xmlChar *)String) == 0) { 158 rv = TOPO_TYPE_STRING; 159 } else { 160 xmlFree(str); 161 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, 162 "Unrecognized type attribute value.\n"); 163 (void) topo_mod_seterrno(mp, ETOPO_PRSR_BADTYPE); 164 return (TOPO_TYPE_INVALID); 165 } 166 xmlFree(str); 167 return (rv); 168 } 169 170 static int 171 xlate_common(topo_mod_t *mp, xmlNodePtr xn, topo_type_t ptype, nvlist_t *nvl, 172 const char *name) 173 { 174 int rv; 175 uint64_t ui; 176 nvlist_t *fmri; 177 xmlChar *str; 178 179 topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "xlate_common\n"); 180 switch (ptype) { 181 case TOPO_TYPE_INT32: 182 if (xmlattr_to_int(mp, xn, Value, &ui) < 0) 183 return (-1); 184 rv = nvlist_add_int32(nvl, name, (int32_t)ui); 185 break; 186 case TOPO_TYPE_UINT32: 187 if (xmlattr_to_int(mp, xn, Value, &ui) < 0) 188 return (-1); 189 rv = nvlist_add_uint32(nvl, name, (uint32_t)ui); 190 break; 191 case TOPO_TYPE_INT64: 192 if (xmlattr_to_int(mp, xn, Value, &ui) < 0) 193 return (-1); 194 rv = nvlist_add_int64(nvl, name, (int64_t)ui); 195 break; 196 case TOPO_TYPE_UINT64: 197 if (xmlattr_to_int(mp, xn, Value, &ui) < 0) 198 return (-1); 199 rv = nvlist_add_uint64(nvl, name, ui); 200 break; 201 case TOPO_TYPE_FMRI: 202 if (xmlattr_to_fmri(mp, xn, Value, &fmri) < 0) 203 return (-1); 204 rv = nvlist_add_nvlist(nvl, name, fmri); 205 nvlist_free(fmri); 206 break; 207 case TOPO_TYPE_STRING: 208 if ((str = xmlGetProp(xn, (xmlChar *)Value)) == NULL) 209 return (-1); 210 rv = nvlist_add_string(nvl, name, (char *)str); 211 xmlFree(str); 212 break; 213 default: 214 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, 215 "Unrecognized type attribute.\n"); 216 return (topo_mod_seterrno(mp, ETOPO_PRSR_BADTYPE)); 217 } 218 if (rv != 0) { 219 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, 220 "Nvlist construction failed.\n"); 221 return (topo_mod_seterrno(mp, ETOPO_NOMEM)); 222 } else 223 return (0); 224 } 225 226 static int 227 xmlprop_xlate(topo_mod_t *mp, xmlNodePtr xn, nvlist_t *nvl) 228 { 229 topo_type_t ptype; 230 xmlChar *str; 231 232 topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "xmlprop_xlate\n"); 233 if ((str = xmlGetProp(xn, (xmlChar *)Immutable)) != NULL) { 234 if (xmlStrcmp(str, (xmlChar *)False) == 0) 235 (void) nvlist_add_boolean_value(nvl, INV_IMMUTE, 236 B_FALSE); 237 else 238 (void) nvlist_add_boolean_value(nvl, INV_IMMUTE, 239 B_TRUE); 240 xmlFree(str); 241 } else { 242 (void) nvlist_add_boolean_value(nvl, INV_IMMUTE, B_TRUE); 243 } 244 245 if ((ptype = xmlattr_to_type(mp, xn, (xmlChar *)Type)) 246 == TOPO_TYPE_INVALID) 247 return (-1); 248 249 if (nvlist_add_int32(nvl, INV_PVALTYPE, ptype) != 0) 250 return (-1); 251 252 return (xlate_common(mp, xn, ptype, nvl, INV_PVAL)); 253 } 254 255 static int 256 dependent_create(topo_mod_t *mp, 257 tf_info_t *xinfo, tf_pad_t *pad, xmlNodePtr dxn, tnode_t *ptn) 258 { 259 tf_rdata_t *rp, *pp, *np; 260 xmlChar *grptype; 261 int sibs = 0; 262 263 topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "dependent_create\n"); 264 if ((grptype = xmlGetProp(dxn, (xmlChar *)Grouping)) == NULL) { 265 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, 266 "Dependents missing grouping attribute"); 267 return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR)); 268 } 269 270 pp = NULL; 271 if (xmlStrcmp(grptype, (xmlChar *)Siblings) == 0) { 272 rp = pad->tpad_sibs; 273 sibs++; 274 } else if (xmlStrcmp(grptype, (xmlChar *)Children) == 0) { 275 rp = pad->tpad_child; 276 } else { 277 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, 278 "Dependents have bogus grouping attribute"); 279 xmlFree(grptype); 280 return (topo_mod_seterrno(mp, ETOPO_PRSR_BADGRP)); 281 } 282 xmlFree(grptype); 283 /* Add processed dependents to the tail of the list */ 284 while (rp != NULL) { 285 pp = rp; 286 rp = rp->rd_next; 287 } 288 if ((np = topo_xml_walk(mp, xinfo, dxn, ptn)) == NULL) { 289 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, 290 "error within dependent .xml topology: " 291 "%s\n", topo_strerror(topo_mod_errno(mp))); 292 return (-1); 293 } 294 if (pp != NULL) 295 pp->rd_next = np; 296 else if (sibs == 1) 297 pad->tpad_sibs = np; 298 else 299 pad->tpad_child = np; 300 return (0); 301 } 302 303 static int 304 dependents_create(topo_mod_t *mp, 305 tf_info_t *xinfo, tf_pad_t *pad, xmlNodePtr pxn, tnode_t *ptn) 306 { 307 xmlNodePtr cn; 308 309 topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "dependents_create\n"); 310 for (cn = pxn->xmlChildrenNode; cn != NULL; cn = cn->next) { 311 if (xmlStrcmp(cn->name, (xmlChar *)Dependents) == 0) { 312 if (dependent_create(mp, xinfo, pad, cn, ptn) < 0) 313 return (-1); 314 } 315 } 316 return (0); 317 } 318 319 static int 320 prop_create(topo_mod_t *mp, 321 nvlist_t *pfmri, tnode_t *ptn, const char *gnm, const char *pnm, 322 topo_type_t ptype, int flag) 323 { 324 nvlist_t *fmri; 325 uint32_t ui32; 326 uint64_t ui64; 327 int32_t i32; 328 int64_t i64; 329 char *str; 330 int err, e; 331 332 topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "prop_create(gnm = %s, " 333 "pnm = %s)\n", gnm, pnm); 334 switch (ptype) { 335 case TOPO_TYPE_INT32: 336 e = nvlist_lookup_int32(pfmri, INV_PVAL, &i32); 337 break; 338 case TOPO_TYPE_UINT32: 339 e = nvlist_lookup_uint32(pfmri, INV_PVAL, &ui32); 340 break; 341 case TOPO_TYPE_INT64: 342 e = nvlist_lookup_int64(pfmri, INV_PVAL, &i64); 343 break; 344 case TOPO_TYPE_UINT64: 345 e = nvlist_lookup_uint64(pfmri, INV_PVAL, &ui64); 346 break; 347 case TOPO_TYPE_FMRI: 348 e = nvlist_lookup_nvlist(pfmri, INV_PVAL, &fmri); 349 break; 350 case TOPO_TYPE_STRING: 351 e = nvlist_lookup_string(pfmri, INV_PVAL, &str); 352 break; 353 default: 354 e = ETOPO_PRSR_BADTYPE; 355 } 356 if (e != 0) { 357 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, 358 "prop_create: prop value lookup failed.\n"); 359 return (topo_mod_seterrno(mp, e)); 360 } 361 switch (ptype) { 362 case TOPO_TYPE_INT32: 363 e = topo_prop_set_int32(ptn, gnm, pnm, flag, i32, &err); 364 break; 365 case TOPO_TYPE_UINT32: 366 e = topo_prop_set_uint32(ptn, gnm, pnm, flag, ui32, &err); 367 break; 368 case TOPO_TYPE_INT64: 369 e = topo_prop_set_int64(ptn, gnm, pnm, flag, i64, &err); 370 break; 371 case TOPO_TYPE_UINT64: 372 e = topo_prop_set_uint64(ptn, gnm, pnm, flag, ui64, &err); 373 break; 374 case TOPO_TYPE_FMRI: 375 e = topo_prop_set_fmri(ptn, gnm, pnm, flag, fmri, &err); 376 break; 377 case TOPO_TYPE_STRING: 378 e = topo_prop_set_string(ptn, gnm, pnm, flag, str, &err); 379 break; 380 } 381 if (e != 0 && err != ETOPO_PROP_DEFD) { 382 383 /* 384 * Some properties may have already been set 385 * in topo_node_bind() or topo_prop_inherit if we are 386 * enumerating from a static .xml file 387 */ 388 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "prop set " 389 "failed %s/%s:%s\n", gnm, pnm, topo_strerror(err)); 390 return (topo_mod_seterrno(mp, err)); 391 } 392 return (0); 393 } 394 395 static int 396 props_create(topo_mod_t *mp, 397 tnode_t *ptn, const char *gnm, nvlist_t **props, int nprops) 398 { 399 topo_type_t ptype; 400 boolean_t pim; 401 char *pnm; 402 int32_t i32; 403 int flag; 404 int pn; 405 int e; 406 407 topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "props_create(gnm = %s)\n", gnm); 408 for (pn = 0; pn < nprops; pn++) { 409 e = nvlist_lookup_string(props[pn], INV_PNAME, &pnm); 410 if (e != 0) { 411 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, 412 "props create lookup (%s) failure: %s", 413 INV_PNAME, strerror(e)); 414 return (topo_mod_seterrno(mp, ETOPO_PRSR_NVPROP)); 415 } 416 e = nvlist_lookup_boolean_value(props[pn], INV_IMMUTE, &pim); 417 if (e != 0) { 418 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, 419 "props create lookup (%s) failure: %s", 420 INV_IMMUTE, strerror(e)); 421 return (topo_mod_seterrno(mp, ETOPO_PRSR_NVPROP)); 422 } 423 flag = (pim == B_TRUE) ? 424 TOPO_PROP_IMMUTABLE : TOPO_PROP_MUTABLE; 425 426 e = nvlist_lookup_int32(props[pn], INV_PVALTYPE, &i32); 427 if (e != 0) { 428 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, 429 "props create lookup (%s) failure: %s", 430 INV_PVALTYPE, strerror(e)); 431 return (topo_mod_seterrno(mp, ETOPO_PRSR_NVPROP)); 432 } 433 ptype = (topo_type_t)i32; 434 if (prop_create(mp, props[pn], ptn, gnm, pnm, ptype, flag) < 0) 435 return (-1); 436 } 437 return (0); 438 } 439 440 static int 441 pgroups_create(topo_mod_t *mp, tf_pad_t *pad, tnode_t *ptn) 442 { 443 topo_pgroup_info_t pgi; 444 nvlist_t **props; 445 char *gnm; 446 char *nmstab, *dstab; 447 uint32_t rnprops, nprops; 448 uint32_t gv; 449 int pg; 450 int e; 451 452 topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "pgroups_create\n"); 453 for (pg = 0; pg < pad->tpad_pgcnt; pg++) { 454 e = nvlist_lookup_string(pad->tpad_pgs[pg], 455 INV_PGRP_NAME, &gnm); 456 if (e != 0) { 457 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, 458 "pad lookup (%s) failed (%s).\n", 459 INV_PGRP_NAME, strerror(errno)); 460 return (topo_mod_seterrno(mp, ETOPO_PRSR_NVPROP)); 461 } 462 e = nvlist_lookup_string(pad->tpad_pgs[pg], 463 INV_PGRP_NMSTAB, &nmstab); 464 if (e != 0) { 465 if (e != ENOENT) { 466 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, 467 "pad lookup (%s) " 468 "failed.\n", INV_PGRP_NMSTAB); 469 return (topo_mod_seterrno(mp, 470 ETOPO_PRSR_NVPROP)); 471 } else { 472 nmstab = TOPO_STABSTR_PRIVATE; 473 } 474 } 475 e = nvlist_lookup_string(pad->tpad_pgs[pg], 476 INV_PGRP_DSTAB, &dstab); 477 if (e != 0) { 478 if (e != ENOENT) { 479 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, 480 "pad lookup (%s) failed.\n", 481 INV_PGRP_DSTAB); 482 return (topo_mod_seterrno(mp, 483 ETOPO_PRSR_NVPROP)); 484 } else { 485 dstab = TOPO_STABSTR_PRIVATE; 486 } 487 } 488 e = nvlist_lookup_uint32(pad->tpad_pgs[pg], 489 INV_PGRP_VER, &gv); 490 if (e != 0) { 491 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, 492 "pad lookup (%s) failed.\n", 493 INV_PGRP_VER); 494 return (topo_mod_seterrno(mp, ETOPO_PRSR_NVPROP)); 495 } 496 pgi.tpi_name = gnm; 497 pgi.tpi_namestab = topo_name2stability(nmstab); 498 pgi.tpi_datastab = topo_name2stability(dstab); 499 pgi.tpi_version = gv; 500 if (topo_pgroup_create(ptn, &pgi, &e) != 0) { 501 if (e != ETOPO_PROP_DEFD) { 502 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, 503 "pgroups create failure: %s\n", 504 topo_strerror(e)); 505 return (-1); 506 } 507 } 508 e = nvlist_lookup_uint32(pad->tpad_pgs[pg], 509 INV_PGRP_NPROP, &rnprops); 510 /* 511 * The number of properties could be zero if the property 512 * group only contains propmethod declarations 513 */ 514 if (rnprops > 0) { 515 e |= nvlist_lookup_nvlist_array(pad->tpad_pgs[pg], 516 INV_PGRP_ALLPROPS, &props, &nprops); 517 if (rnprops != nprops) { 518 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, 519 "recorded number of props %d does not " 520 "match number of props recorded %d.\n", 521 rnprops, nprops); 522 } 523 if (props_create(mp, ptn, gnm, props, nprops) < 0) 524 return (-1); 525 } 526 } 527 return (0); 528 } 529 530 static nvlist_t * 531 pval_record(topo_mod_t *mp, xmlNodePtr xn) 532 { 533 nvlist_t *pnvl = NULL; 534 xmlChar *pname; 535 536 topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "pval_record\n"); 537 if ((pname = xmlGetProp(xn, (xmlChar *)Name)) == NULL) { 538 topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, 539 "propval lacks a name\n"); 540 (void) topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR); 541 return (NULL); 542 } 543 if (topo_mod_nvalloc(mp, &pnvl, NV_UNIQUE_NAME) < 0) { 544 xmlFree(pname); 545 return (NULL); 546 } 547 if (nvlist_add_string(pnvl, INV_PNAME, (char *)pname) < 0) { 548 xmlFree(pname); 549 nvlist_free(pnvl); 550 return (NULL); 551 } 552 xmlFree(pname); 553 /* FMXXX stability of the property name */ 554 555 if (xmlprop_xlate(mp, xn, pnvl) < 0) { 556 nvlist_free(pnvl); 557 return (NULL); 558 } 559 return (pnvl); 560 } 561 562 563 struct propmeth_data { 564 const char *pg_name; 565 const char *prop_name; 566 topo_type_t prop_type; 567 const char *meth_name; 568 topo_version_t meth_ver; 569 nvlist_t *arg_nvl; 570 }; 571 572 static int 573 register_method(topo_mod_t *mp, tnode_t *ptn, struct propmeth_data *meth) 574 { 575 int err; 576 577 if (topo_prop_method_version_register(ptn, meth->pg_name, 578 meth->prop_name, meth->prop_type, meth->meth_name, meth->meth_ver, 579 meth->arg_nvl, &err) != 0) { 580 581 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "failed to register " 582 "propmethod %s for property \"%s\" in propgrp %s on node " 583 "%s=%d (%s)\n", 584 meth->meth_name, meth->prop_name, meth->pg_name, 585 topo_node_name(ptn), topo_node_instance(ptn), 586 topo_strerror(err)); 587 return (-1); 588 } 589 topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, 590 "registered method %s on %s=%d\n", 591 meth->meth_name, topo_node_name(ptn), topo_node_instance(ptn)); 592 593 return (0); 594 } 595 596 static int 597 pmeth_record(topo_mod_t *mp, const char *pg_name, xmlNodePtr xn, tnode_t *tn, 598 const char *rname, const char *ppgrp_name) 599 { 600 nvlist_t *arg_nvl = NULL; 601 xmlNodePtr cn; 602 xmlChar *meth_name = NULL, *prop_name = NULL; 603 xmlChar *arg_name = NULL; 604 uint64_t meth_ver; 605 topo_type_t prop_type; 606 struct propmeth_data meth; 607 int ret = 0; 608 topo_type_t ptype; 609 tnode_t *tmp; 610 611 topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "pmeth_record\n"); 612 613 /* 614 * Get propmethod attribute values 615 */ 616 if ((meth_name = xmlGetProp(xn, (xmlChar *)Name)) == NULL) { 617 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, 618 "propmethod element lacks a name attribute\n"); 619 return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR)); 620 } 621 if (xmlattr_to_int(mp, xn, Version, &meth_ver) < 0) { 622 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, 623 "propmethod element lacks version attribute\n"); 624 ret = topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR); 625 goto pmr_done; 626 } 627 if ((prop_name = xmlGetProp(xn, (xmlChar *)Propname)) == NULL) { 628 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, 629 "propmethod element lacks propname attribute\n"); 630 ret = topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR); 631 goto pmr_done; 632 } 633 if ((prop_type = xmlattr_to_type(mp, xn, (xmlChar *)Proptype)) 634 == TOPO_TYPE_INVALID) { 635 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, 636 "error decoding proptype attribute\n"); 637 ret = topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR); 638 goto pmr_done; 639 } 640 641 /* 642 * Allocate method argument nvlist 643 */ 644 if (topo_mod_nvalloc(mp, &arg_nvl, NV_UNIQUE_NAME) < 0) { 645 ret = topo_mod_seterrno(mp, ETOPO_NOMEM); 646 goto pmr_done; 647 } 648 649 /* 650 * Iterate through the argval nodes and build the argval nvlist 651 */ 652 for (cn = xn->xmlChildrenNode; cn != NULL; cn = cn->next) { 653 if (xmlStrcmp(cn->name, (xmlChar *)Argval) == 0) { 654 topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, 655 "found argval element\n"); 656 if ((arg_name = xmlGetProp(cn, (xmlChar *)Name)) 657 == NULL) { 658 topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, 659 "argval element lacks a name attribute\n"); 660 ret = topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR); 661 goto pmr_done; 662 } 663 if ((ptype = xmlattr_to_type(mp, cn, (xmlChar *)Type)) 664 == TOPO_TYPE_INVALID) { 665 ret = topo_mod_seterrno(mp, ETOPO_PRSR_BADTYPE); 666 xmlFree(arg_name); 667 break; 668 } 669 if (xlate_common(mp, cn, ptype, arg_nvl, 670 (const char *)arg_name) != 0) { 671 ret = topo_mod_seterrno(mp, ETOPO_PRSR_BADTYPE); 672 xmlFree(arg_name); 673 break; 674 } 675 } 676 if (arg_name) { 677 xmlFree(arg_name); 678 arg_name = NULL; 679 } 680 } 681 682 if (ret != 0) 683 goto pmr_done; 684 685 /* 686 * Register the prop method for all of the nodes in our range 687 */ 688 meth.pg_name = (const char *)pg_name; 689 meth.prop_name = (const char *)prop_name; 690 meth.prop_type = prop_type; 691 meth.meth_name = (const char *)meth_name; 692 meth.meth_ver = meth_ver; 693 meth.arg_nvl = arg_nvl; 694 695 /* 696 * If the propgroup element is under a range element, we'll apply 697 * the method to all of the topo nodes at this level with the same 698 * range name. 699 * 700 * Otherwise, if the propgroup element is under a node element 701 * then we'll simply register the method for this node. 702 */ 703 if (strcmp(ppgrp_name, Range) == 0) { 704 for (tmp = tn; tmp != NULL; tmp = topo_child_next(NULL, tmp)) { 705 if (strcmp(rname, topo_node_name(tmp)) == 0) 706 if (register_method(mp, tmp, &meth) != 0) { 707 ret = topo_mod_seterrno(mp, 708 ETOPO_PRSR_REGMETH); 709 goto pmr_done; 710 } 711 } 712 } else { 713 if (register_method(mp, tn, &meth) != 0) { 714 ret = topo_mod_seterrno(mp, ETOPO_PRSR_REGMETH); 715 goto pmr_done; 716 } 717 } 718 719 pmr_done: 720 if (meth_name) 721 xmlFree(meth_name); 722 if (prop_name) 723 xmlFree(prop_name); 724 if (arg_nvl) 725 nvlist_free(arg_nvl); 726 return (ret); 727 } 728 729 730 static int 731 pgroup_record(topo_mod_t *mp, xmlNodePtr pxn, tnode_t *tn, const char *rname, 732 tf_pad_t *rpad, int pi, const char *ppgrp_name) 733 { 734 topo_stability_t nmstab, dstab; 735 uint64_t ver; 736 xmlNodePtr cn; 737 xmlChar *name; 738 nvlist_t **apl = NULL; 739 nvlist_t *pgnvl = NULL; 740 int pcnt = 0; 741 int ai = 0; 742 int e; 743 744 topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "pgroup_record\n"); 745 if ((name = xmlGetProp(pxn, (xmlChar *)Name)) == NULL) { 746 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, 747 "propgroup lacks a name\n"); 748 return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR)); 749 } 750 if (xmlattr_to_int(mp, pxn, Version, &ver) < 0) { 751 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, 752 "propgroup lacks a version\n"); 753 return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR)); 754 } 755 if (xmlattr_to_stab(mp, pxn, Namestab, &nmstab) < 0) { 756 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, 757 "propgroup lacks name-stability\n"); 758 return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR)); 759 } 760 if (xmlattr_to_stab(mp, pxn, Datastab, &dstab) < 0) { 761 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, 762 "propgroup lacks data-stability\n"); 763 return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR)); 764 } 765 766 topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "pgroup %s\n", (char *)name); 767 for (cn = pxn->xmlChildrenNode; cn != NULL; cn = cn->next) { 768 if (xmlStrcmp(cn->name, (xmlChar *)Propval) == 0) 769 pcnt++; 770 } 771 772 if (topo_mod_nvalloc(mp, &pgnvl, NV_UNIQUE_NAME) < 0) { 773 xmlFree(name); 774 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, 775 "failed to allocate propgroup nvlist\n"); 776 return (topo_mod_seterrno(mp, ETOPO_NOMEM)); 777 } 778 779 e = nvlist_add_string(pgnvl, INV_PGRP_NAME, (char *)name); 780 e |= nvlist_add_uint32(pgnvl, INV_PGRP_NMSTAB, nmstab); 781 e |= nvlist_add_uint32(pgnvl, INV_PGRP_DSTAB, dstab); 782 e |= nvlist_add_uint32(pgnvl, INV_PGRP_VER, ver); 783 e |= nvlist_add_uint32(pgnvl, INV_PGRP_NPROP, pcnt); 784 if (pcnt > 0) 785 if (e != 0 || 786 (apl = topo_mod_zalloc(mp, pcnt * sizeof (nvlist_t *))) 787 == NULL) { 788 xmlFree(name); 789 nvlist_free(pgnvl); 790 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, 791 "failed to allocate nvlist array for properties" 792 "(e=%d)\n", e); 793 return (topo_mod_seterrno(mp, ETOPO_NOMEM)); 794 } 795 for (cn = pxn->xmlChildrenNode; cn != NULL; cn = cn->next) { 796 if (xmlStrcmp(cn->name, (xmlChar *)Propval) == 0) { 797 if (ai < pcnt) { 798 if ((apl[ai] = pval_record(mp, cn)) == NULL) 799 break; 800 } 801 ai++; 802 } else if (xmlStrcmp(cn->name, (xmlChar *)Prop_meth) == 0) { 803 if (pmeth_record(mp, (const char *)name, cn, tn, rname, 804 ppgrp_name) < 0) 805 break; 806 } 807 } 808 xmlFree(name); 809 if (pcnt > 0) { 810 e |= (ai != pcnt); 811 e |= nvlist_add_nvlist_array(pgnvl, INV_PGRP_ALLPROPS, apl, 812 pcnt); 813 for (ai = 0; ai < pcnt; ai++) 814 if (apl[ai] != NULL) 815 nvlist_free(apl[ai]); 816 topo_mod_free(mp, apl, pcnt * sizeof (nvlist_t *)); 817 if (e != 0) { 818 nvlist_free(pgnvl); 819 return (-1); 820 } 821 } 822 rpad->tpad_pgs[pi] = pgnvl; 823 return (0); 824 } 825 826 static int 827 pgroups_record(topo_mod_t *mp, xmlNodePtr pxn, tnode_t *tn, const char *rname, 828 tf_pad_t *rpad, const char *ppgrp) 829 { 830 xmlNodePtr cn; 831 int pi = 0; 832 833 topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "pgroups_record: pxn->name=%s\n", 834 pxn->name); 835 for (cn = pxn->xmlChildrenNode; cn != NULL; cn = cn->next) { 836 if (xmlStrcmp(cn->name, (xmlChar *)Propgrp) == 0) { 837 if (pgroup_record(mp, cn, tn, rname, rpad, pi++, ppgrp) 838 < 0) 839 return (-1); 840 } 841 } 842 return (0); 843 } 844 845 /* 846 * psn: pointer to a "set" XML node 847 * key: string to search the set for 848 * 849 * returns: 1, if the set contains key 850 * 0, otherwise 851 */ 852 static int 853 set_contains(topo_mod_t *mp, char *key, char *set) 854 { 855 char *prod; 856 int rv = 0; 857 858 topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "set_contains(key = %s, " 859 "setlist = %s)\n", key, set); 860 861 prod = strtok((char *)set, "|"); 862 if (prod && (strcmp(key, prod) == 0)) 863 return (1); 864 865 while ((prod = strtok(NULL, "|"))) 866 if (strcmp(key, prod) == 0) 867 return (1); 868 869 return (rv); 870 } 871 872 873 /* 874 * Process the property group and dependents xmlNode children of 875 * parent xmlNode pxn. 876 */ 877 static int 878 pad_process(topo_mod_t *mp, tf_rdata_t *rd, xmlNodePtr pxn, tnode_t *ptn, 879 tf_pad_t **rpad) 880 { 881 xmlNodePtr cn, gcn, psn, ecn; 882 xmlNodePtr def_set = NULL; 883 tf_pad_t *new = *rpad; 884 tf_rdata_t tmp_rd; 885 int pgcnt = 0; 886 int dcnt = 0; 887 int ecnt = 0; 888 int joined_set = 0; 889 xmlChar *set; 890 char *key; 891 892 topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, 893 "pad_process beneath %s\n", topo_node_name(ptn)); 894 if (new == NULL) { 895 for (cn = pxn->xmlChildrenNode; cn != NULL; cn = cn->next) { 896 topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, 897 "cn->name is %s \n", (char *)cn->name); 898 /* 899 * We're iterating through the XML children looking for 900 * four types of elements: 901 * 1) dependents elements 902 * 2) unconstrained pgroup elements 903 * 3) pgroup elements constrained by set elements 904 * 4) enum-method elements for the case that we want 905 * to post-process a statically defined node 906 */ 907 if (xmlStrcmp(cn->name, (xmlChar *)Dependents) == 0) 908 dcnt++; 909 else if (xmlStrcmp(cn->name, (xmlChar *)Propgrp) == 0) 910 pgcnt++; 911 else if (xmlStrcmp(cn->name, (xmlChar *)Enum_meth) 912 == 0) { 913 ecn = cn; 914 ecnt++; 915 } else if (xmlStrcmp(cn->name, (xmlChar *)Set) == 0) { 916 if (joined_set) 917 continue; 918 set = xmlGetProp(cn, (xmlChar *)Setlist); 919 920 if (mp->tm_hdl->th_product) 921 key = mp->tm_hdl->th_product; 922 else 923 key = mp->tm_hdl->th_platform; 924 925 /* 926 * If it's the default set then we'll store 927 * a pointer to it so that if none of the other 928 * sets apply to our product we can fall 929 * back to this one. 930 */ 931 if (strcmp((char *)set, "default") == 0) 932 def_set = cn; 933 else if (set_contains(mp, key, (char *)set)) { 934 psn = cn; 935 joined_set = 1; 936 for (gcn = cn->xmlChildrenNode; 937 gcn != NULL; gcn = gcn->next) { 938 if (xmlStrcmp(gcn->name, 939 (xmlChar *)Propgrp) == 0) 940 pgcnt++; 941 } 942 } 943 xmlFree(set); 944 } 945 } 946 /* 947 * If we haven't found a set that contains our product AND 948 * a default set exists, then we'll process it. 949 */ 950 if (!joined_set && def_set) { 951 topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, 952 "Falling back to default set\n"); 953 joined_set = 1; 954 psn = def_set; 955 for (gcn = psn->xmlChildrenNode; gcn != NULL; 956 gcn = gcn->next) { 957 if (xmlStrcmp(gcn->name, (xmlChar *)Propgrp) 958 == 0) 959 pgcnt++; 960 } 961 } 962 topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, 963 "pad_process: dcnt=%d, pgcnt=%d, ecnt=%d, joined_set=%d\n", 964 dcnt, pgcnt, ecnt, joined_set); 965 /* 966 * If an enum-method element was found, AND we're a child of a 967 * node element, then we invoke the enumerator so that it can do 968 * post-processing of the node. 969 */ 970 if (ecnt && (strcmp((const char *)pxn->name, Node) == 0)) { 971 if ((tmp_rd.rd_einfo = enum_attributes_process(mp, ecn)) 972 == NULL) 973 return (-1); 974 tmp_rd.rd_mod = mp; 975 tmp_rd.rd_name = rd->rd_name; 976 tmp_rd.rd_min = rd->rd_min; 977 tmp_rd.rd_max = rd->rd_max; 978 tmp_rd.rd_pn = ptn; 979 if (enum_run(mp, &tmp_rd) < 0) { 980 /* 981 * Note the failure but continue on 982 */ 983 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, 984 "pad_process: enumeration failed.\n"); 985 } 986 topo_mod_free(mp, tmp_rd.rd_einfo, sizeof (tf_edata_t)); 987 } 988 /* 989 * Here we allocate an element in an intermediate data structure 990 * which keeps track property groups and dependents of the range 991 * currently being processed. 992 * 993 * This structure is referenced in pgroups_record() to create 994 * the actual property groups in the topo tree 995 */ 996 if ((new = tf_pad_new(mp, pgcnt, dcnt)) == NULL) 997 return (-1); 998 if (dcnt == 0 && pgcnt == 0) { 999 *rpad = new; 1000 return (0); 1001 } 1002 1003 if (pgcnt > 0) { 1004 new->tpad_pgs = 1005 topo_mod_zalloc(mp, pgcnt * sizeof (nvlist_t *)); 1006 if (new->tpad_pgs == NULL) { 1007 tf_pad_free(mp, new); 1008 return (-1); 1009 } 1010 1011 if (joined_set) { 1012 /* 1013 * If the property groups are contained within a 1014 * set then they will be one level lower in 1015 * the XML tree. 1016 */ 1017 if (pgroups_record(mp, psn, ptn, rd->rd_name, 1018 new, (const char *)pxn->name) < 0) { 1019 tf_pad_free(mp, new); 1020 return (-1); 1021 } 1022 } else { 1023 if (pgroups_record(mp, pxn, ptn, rd->rd_name, 1024 new, (const char *)pxn->name) < 0) { 1025 tf_pad_free(mp, new); 1026 return (-1); 1027 } 1028 } 1029 } 1030 *rpad = new; 1031 } 1032 1033 if (new->tpad_dcnt > 0) 1034 if (dependents_create(mp, rd->rd_finfo, new, pxn, ptn) < 0) 1035 return (-1); 1036 1037 if (new->tpad_pgcnt > 0) 1038 if (pgroups_create(mp, new, ptn) < 0) 1039 return (-1); 1040 1041 return (0); 1042 } 1043 1044 1045 static int 1046 node_process(topo_mod_t *mp, xmlNodePtr nn, tf_rdata_t *rd) 1047 { 1048 xmlChar *str; 1049 topo_instance_t inst; 1050 tf_idata_t *newi; 1051 tnode_t *ntn; 1052 uint64_t ui; 1053 int rv = -1; 1054 int s = 0; 1055 1056 topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, 1057 "node_process %s\n", rd->rd_name); 1058 1059 if (xmlattr_to_int(mp, nn, Instance, &ui) < 0) 1060 goto nodedone; 1061 inst = (topo_instance_t)ui; 1062 1063 if ((str = xmlGetProp(nn, (xmlChar *)Static)) != NULL) { 1064 if (xmlStrcmp(str, (xmlChar *)True) == 0) 1065 s = 1; 1066 xmlFree(str); 1067 } 1068 1069 if (s == 0) { 1070 if (topo_mod_enumerate(rd->rd_mod, rd->rd_pn, 1071 rd->rd_finfo->tf_scheme, rd->rd_name, inst, inst, 1072 s == 1 ? &s : NULL) < 0) 1073 goto nodedone; 1074 } 1075 ntn = topo_node_lookup(rd->rd_pn, rd->rd_name, inst); 1076 1077 if (ntn == NULL) { 1078 1079 /* 1080 * If this is a static node declaration, we can 1081 * ignore the lookup failure and continue 1082 * processing. Otherwise, something 1083 * went wrong during enumeration 1084 */ 1085 if (s == 1) 1086 rv = 0; 1087 goto nodedone; 1088 } 1089 if ((newi = tf_idata_new(mp, inst, ntn)) == NULL) { 1090 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, 1091 "node_process: tf_idata_new failed.\n"); 1092 goto nodedone; 1093 } 1094 if (tf_idata_insert(&rd->rd_instances, newi) < 0) { 1095 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, 1096 "node_process: tf_idata_insert failed.\n"); 1097 goto nodedone; 1098 } 1099 if (pad_process(mp, rd, nn, ntn, &newi->ti_pad) < 0) 1100 goto nodedone; 1101 rv = 0; 1102 nodedone: 1103 topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "done with node %s.\n", 1104 rd->rd_name); 1105 return (rv); 1106 } 1107 1108 static tf_edata_t * 1109 enum_attributes_process(topo_mod_t *mp, xmlNodePtr en) 1110 { 1111 tf_edata_t *einfo; 1112 uint64_t ui; 1113 1114 topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "enum_attributes_process\n"); 1115 if ((einfo = topo_mod_zalloc(mp, sizeof (tf_edata_t))) == NULL) { 1116 (void) topo_mod_seterrno(mp, ETOPO_NOMEM); 1117 return (NULL); 1118 } 1119 einfo->te_name = (char *)xmlGetProp(en, (xmlChar *)Name); 1120 if (einfo->te_name == NULL) { 1121 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, 1122 "Enumerator name attribute missing.\n"); 1123 (void) topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR); 1124 goto enodedone; 1125 } 1126 1127 /* 1128 * Check for recursive enumeration 1129 */ 1130 if (strcmp(einfo->te_name, mp->tm_name) == 0) { 1131 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, 1132 "Recursive enumeration detected for %s\n", 1133 einfo->te_name); 1134 (void) topo_mod_seterrno(mp, ETOPO_ENUM_RECURS); 1135 goto enodedone; 1136 } 1137 if (xmlattr_to_int(mp, en, Version, &ui) < 0) 1138 goto enodedone; 1139 einfo->te_vers = (int)ui; 1140 1141 return (einfo); 1142 1143 enodedone: 1144 if (einfo->te_name != NULL) 1145 xmlFree(einfo->te_name); 1146 topo_mod_free(mp, einfo, sizeof (tf_edata_t)); 1147 return (NULL); 1148 } 1149 1150 static int 1151 enum_run(topo_mod_t *mp, tf_rdata_t *rd) 1152 { 1153 topo_hdl_t *thp = mp->tm_hdl; 1154 int e = -1; 1155 1156 topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "enum_run\n"); 1157 /* 1158 * Check if the enumerator module is already loaded. 1159 * Module loading is single-threaded at this point so there's 1160 * no need to worry about the module going away or bumping the 1161 * ref count. 1162 */ 1163 if ((rd->rd_mod = topo_mod_lookup(thp, rd->rd_einfo->te_name, 1164 0)) == NULL) { 1165 if ((rd->rd_mod = topo_mod_load(mp, rd->rd_einfo->te_name, 1166 rd->rd_einfo->te_vers)) == NULL) { 1167 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, 1168 "enum_run: mod_load of %s failed: %s.\n", 1169 rd->rd_einfo->te_name, 1170 topo_strerror(topo_mod_errno(mp))); 1171 (void) topo_hdl_seterrno(thp, topo_mod_errno(mp)); 1172 return (e); 1173 } 1174 } 1175 /* 1176 * We're live, so let's enumerate. 1177 */ 1178 topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "enumerate request. (%s)\n", 1179 rd->rd_einfo->te_name); 1180 e = topo_mod_enumerate(rd->rd_mod, rd->rd_pn, rd->rd_einfo->te_name, 1181 rd->rd_name, rd->rd_min, rd->rd_max, NULL); 1182 topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "back from enumeration. %d\n", 1183 e); 1184 if (e != 0) { 1185 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, 1186 "Enumeration failed (%s)\n", 1187 topo_strerror(topo_mod_errno(mp))); 1188 (void) topo_hdl_seterrno(thp, EMOD_PARTIAL_ENUM); 1189 return (topo_mod_seterrno(mp, EMOD_PARTIAL_ENUM)); 1190 } 1191 return (e); 1192 } 1193 1194 1195 int 1196 decorate_nodes(topo_mod_t *mp, tf_rdata_t *rd, xmlNodePtr pxn, tnode_t *ptn, 1197 tf_pad_t **rpad) 1198 { 1199 tnode_t *ctn; 1200 1201 ctn = topo_child_first(ptn); 1202 while (ctn != NULL) { 1203 /* Only care about instances within the range */ 1204 if (strcmp(topo_node_name(ctn), rd->rd_name) != 0) { 1205 ctn = topo_child_next(ptn, ctn); 1206 continue; 1207 } 1208 if (pad_process(mp, rd, pxn, ctn, rpad) < 0) 1209 return (-1); 1210 if (decorate_nodes(mp, rd, pxn, ctn, rpad) < 0) 1211 return (-1); 1212 ctn = topo_child_next(ptn, ctn); 1213 } 1214 return (0); 1215 } 1216 1217 int 1218 topo_xml_range_process(topo_mod_t *mp, xmlNodePtr rn, tf_rdata_t *rd) 1219 { 1220 /* 1221 * The range may have several children xmlNodes, that may 1222 * represent the enumeration method, property groups, 1223 * dependents, nodes or services. 1224 */ 1225 xmlNodePtr cn, enum_node = NULL, pmap_node = NULL; 1226 xmlChar *pmap_name; 1227 tnode_t *ct; 1228 int e, ccnt = 0; 1229 1230 topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "topo_xml_range_process\n" 1231 "process %s range beneath %s\n", rd->rd_name, 1232 topo_node_name(rd->rd_pn)); 1233 1234 e = topo_node_range_create(mp, 1235 rd->rd_pn, rd->rd_name, rd->rd_min, rd->rd_max); 1236 if (e != 0 && topo_mod_errno(mp) != ETOPO_NODE_DUP) { 1237 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, 1238 "Range create failed due to %s.\n", 1239 topo_strerror(topo_mod_errno(mp))); 1240 return (-1); 1241 } 1242 1243 /* 1244 * Before we process any of the other child xmlNodes, we iterate through 1245 * the children and looking for either enum-method or propmap elements. 1246 */ 1247 for (cn = rn->xmlChildrenNode; cn != NULL; cn = cn->next) 1248 if (xmlStrcmp(cn->name, (xmlChar *)Enum_meth) == 0) 1249 enum_node = cn; 1250 else if (xmlStrcmp(cn->name, (xmlChar *)Propmap) == 0) 1251 pmap_node = cn; 1252 1253 /* 1254 * If we found an enum-method element, process it first 1255 */ 1256 if (enum_node != NULL) { 1257 if ((rd->rd_einfo = enum_attributes_process(mp, enum_node)) 1258 == NULL) 1259 return (-1); 1260 if (enum_run(mp, rd) < 0) { 1261 /* 1262 * Note the failure but continue on 1263 */ 1264 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, 1265 "Enumeration failed.\n"); 1266 } 1267 } 1268 1269 /* 1270 * Next, check if a propmap element was found and if so, load it in 1271 * and parse it. 1272 */ 1273 if (pmap_node != NULL) { 1274 topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "found a propmap " 1275 "element\n"); 1276 if ((pmap_name = xmlGetProp(pmap_node, (xmlChar *)Name)) 1277 == NULL) { 1278 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, 1279 "propmap element missing name attribute.\n"); 1280 } else { 1281 if (topo_file_load(mp, rd->rd_pn, 1282 (const char *)pmap_name, 1283 rd->rd_finfo->tf_scheme, 1) < 0) { 1284 1285 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, 1286 "topo_xml_range_process: topo_file_load" 1287 "failed: %s.\n", 1288 topo_strerror(topo_mod_errno(mp))); 1289 } 1290 xmlFree(pmap_name); 1291 } 1292 } 1293 1294 /* Now look for nodes, i.e., hard instances */ 1295 for (cn = rn->xmlChildrenNode; cn != NULL; cn = cn->next) { 1296 if (xmlStrcmp(cn->name, (xmlChar *)Node) == 0) { 1297 if (node_process(mp, cn, rd) < 0) { 1298 topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, 1299 "node processing failed: %s.\n", 1300 topo_strerror(topo_mod_errno(mp))); 1301 return (topo_mod_seterrno(mp, 1302 EMOD_PARTIAL_ENUM)); 1303 } 1304 ccnt++; 1305 } 1306 } 1307 1308 /* 1309 * Finally, process the property groups and dependents 1310 * 1311 * If the TF_PROPMAP flag is set for the XML file we're currently 1312 * processing, then this XML file was loaded via propmap. In that case 1313 * we call a special routine to recursively apply the propgroup settings 1314 * to all of nodes in this range 1315 */ 1316 if (rd->rd_finfo->tf_flags & TF_PROPMAP) 1317 (void) decorate_nodes(mp, rd, rn, rd->rd_pn, &rd->rd_pad); 1318 else { 1319 ct = topo_child_first(rd->rd_pn); 1320 while (ct != NULL) { 1321 /* Only care about instances within the range */ 1322 if (strcmp(topo_node_name(ct), rd->rd_name) != 0) { 1323 ct = topo_child_next(rd->rd_pn, ct); 1324 continue; 1325 } 1326 if (pad_process(mp, rd, rn, ct, &rd->rd_pad) 1327 < 0) 1328 return (-1); 1329 ct = topo_child_next(rd->rd_pn, ct); 1330 ccnt++; 1331 } 1332 if (ccnt == 0) { 1333 topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "no nodes " 1334 "processed for range %s\n", rd->rd_name); 1335 topo_node_range_destroy(rd->rd_pn, rd->rd_name); 1336 return (-1); 1337 } 1338 } 1339 1340 topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "topo_xml_range_process: end " 1341 "range process %s\n", rd->rd_name); 1342 1343 return (0); 1344 } 1345 1346 static tf_rdata_t * 1347 topo_xml_walk(topo_mod_t *mp, 1348 tf_info_t *xinfo, xmlNodePtr croot, tnode_t *troot) 1349 { 1350 xmlNodePtr curr, def_set = NULL; 1351 tf_rdata_t *rr, *pr, *rdp; 1352 xmlChar *set; 1353 char *key; 1354 int joined_set = 0; 1355 1356 topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "topo_xml_walk\n"); 1357 rr = pr = NULL; 1358 /* 1359 * First iterate through all the XML nodes at this level to look for 1360 * set nodes. 1361 */ 1362 for (curr = croot->xmlChildrenNode; curr != NULL; curr = curr->next) { 1363 if (curr->name == NULL) { 1364 topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, 1365 "topo_xml_walk: Ignoring nameless xmlnode\n"); 1366 continue; 1367 } 1368 if (xmlStrcmp(curr->name, (xmlChar *)Set) == 0) { 1369 if (joined_set) 1370 continue; 1371 1372 set = xmlGetProp(curr, (xmlChar *)Setlist); 1373 1374 if (mp->tm_hdl->th_product) 1375 key = mp->tm_hdl->th_product; 1376 else 1377 key = mp->tm_hdl->th_platform; 1378 1379 /* 1380 * If it's the default set then we'll store 1381 * a pointer to it so that if none of the other 1382 * sets apply to our product we can fall 1383 * back to this one. 1384 */ 1385 if (strcmp((char *)set, "default") == 0) 1386 def_set = curr; 1387 else if (set_contains(mp, key, (char *)set)) { 1388 joined_set = 1; 1389 if ((rdp = topo_xml_walk(mp, xinfo, curr, 1390 troot)) == NULL) { 1391 topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, 1392 "topo_xml_walk: failed1\n"); 1393 } 1394 if (pr == NULL) { 1395 rr = pr = rdp; 1396 } else { 1397 pr->rd_next = rdp; 1398 pr = rdp; 1399 } 1400 rr->rd_cnt++; 1401 } 1402 xmlFree(set); 1403 } 1404 } 1405 /* 1406 * If we haven't found a set that contains our product AND a default set 1407 * exists, then we'll process it. 1408 */ 1409 if (!joined_set && def_set) 1410 if (topo_xml_walk(mp, xinfo, def_set, troot) == NULL) { 1411 topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, 1412 "topo_xml_walk: failed2\n"); 1413 } 1414 1415 /* 1416 * Now we're interested in children xmlNodes of croot tagged 1417 * as 'ranges'. These define what topology nodes may exist, and need 1418 * to be verified. 1419 */ 1420 for (curr = croot->xmlChildrenNode; curr != NULL; curr = curr->next) { 1421 if (curr->name == NULL) { 1422 topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, 1423 "topo_xml_walk: Ignoring nameless xmlnode\n"); 1424 continue; 1425 } 1426 if (xmlStrcmp(curr->name, (xmlChar *)Range) != 0) { 1427 topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, 1428 "topo_xml_walk: Ignoring non-range %s.\n", 1429 curr->name); 1430 continue; 1431 } 1432 if ((rdp = tf_rdata_new(mp, xinfo, curr, troot)) == NULL) { 1433 /* 1434 * Range processing error, continue walk 1435 */ 1436 continue; 1437 } 1438 if (pr == NULL) { 1439 rr = pr = rdp; 1440 } else { 1441 pr->rd_next = rdp; 1442 pr = rdp; 1443 } 1444 rr->rd_cnt++; 1445 } 1446 1447 return (rr); 1448 } 1449 1450 /* 1451 * Convert parsed xml topology description into topology nodes 1452 */ 1453 int 1454 topo_xml_enum(topo_mod_t *tmp, tf_info_t *xinfo, tnode_t *troot) 1455 { 1456 xmlNodePtr xroot; 1457 1458 topo_dprintf(tmp->tm_hdl, TOPO_DBG_XML, "topo_xml_enum\n"); 1459 1460 if ((xroot = xmlDocGetRootElement(xinfo->tf_xdoc)) == NULL) { 1461 topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR, 1462 "Couldn't get root xmlNode.\n"); 1463 return (-1); 1464 } 1465 if ((xinfo->tf_rd = topo_xml_walk(tmp, xinfo, xroot, troot)) == NULL) { 1466 topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR, 1467 "error within .xml topology: %s\n", 1468 topo_strerror(topo_mod_errno(tmp))); 1469 return (-1); 1470 } 1471 return (0); 1472 } 1473 1474 /* 1475 * Load an XML tree from filename and read it into a DOM parse tree. 1476 */ 1477 static tf_info_t * 1478 txml_file_parse(topo_mod_t *tmp, 1479 int fd, const char *filenm, const char *escheme) 1480 { 1481 xmlValidCtxtPtr vcp; 1482 xmlNodePtr cursor; 1483 xmlDocPtr document; 1484 xmlDtdPtr dtd = NULL; 1485 xmlChar *scheme = NULL; 1486 char *dtdpath = NULL; 1487 int readflags = 0; 1488 tf_info_t *r; 1489 int e, validate = 0; 1490 1491 topo_dprintf(tmp->tm_hdl, TOPO_DBG_XML, 1492 "txml_file_parse(filenm=%s, escheme=%s)\n", filenm, escheme); 1493 1494 /* 1495 * Since topologies can XInclude other topologies, and libxml2 1496 * doesn't do DTD-based validation with XInclude, by default 1497 * we don't validate topology files. One can force 1498 * validation, though, by creating a TOPOXML_VALIDATE 1499 * environment variable and creating a TOPO_DTD environment 1500 * variable with the path to the DTD against which to validate. 1501 */ 1502 if (getenv("TOPOXML_VALIDATE") != NULL) { 1503 dtdpath = getenv("TOPO_DTD"); 1504 if (dtdpath != NULL) 1505 xmlLoadExtDtdDefaultValue = 0; 1506 validate = 1; 1507 } 1508 1509 /* 1510 * Splat warnings and errors related to parsing the topology 1511 * file if the TOPOXML_PERROR environment variable exists. 1512 */ 1513 if (getenv("TOPOXML_PERROR") == NULL) 1514 readflags = XML_PARSE_NOERROR | XML_PARSE_NOWARNING; 1515 1516 if ((document = xmlReadFd(fd, filenm, NULL, readflags)) == NULL) { 1517 topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR, 1518 "txml_file_parse: couldn't parse document.\n"); 1519 return (NULL); 1520 } 1521 1522 /* 1523 * Verify that this is a document type we understand. 1524 */ 1525 if ((dtd = xmlGetIntSubset(document)) == NULL) { 1526 topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR, 1527 "document has no DTD.\n"); 1528 xmlFreeDoc(document); 1529 return (NULL); 1530 } 1531 1532 if (strcmp((const char *)dtd->SystemID, TOPO_DTD_PATH) != 0) { 1533 topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR, 1534 "document DTD unknown; bad topology file\n"); 1535 xmlFreeDoc(document); 1536 return (NULL); 1537 } 1538 1539 if ((cursor = xmlDocGetRootElement(document)) == NULL) { 1540 topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR, "document is empty.\n"); 1541 xmlFreeDoc(document); 1542 return (NULL); 1543 } 1544 1545 /* 1546 * Make sure we're looking at a topology description in the 1547 * expected scheme. 1548 */ 1549 if (xmlStrcmp(cursor->name, (xmlChar *)Topology) != 0) { 1550 topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR, 1551 "document is not a topology description.\n"); 1552 xmlFreeDoc(document); 1553 return (NULL); 1554 } 1555 if ((scheme = xmlGetProp(cursor, (xmlChar *)Scheme)) == NULL) { 1556 topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR, 1557 "topology lacks a scheme.\n"); 1558 (void) topo_mod_seterrno(tmp, ETOPO_PRSR_NOATTR); 1559 xmlFreeDoc(document); 1560 return (NULL); 1561 } 1562 if (xmlStrcmp(scheme, (xmlChar *)escheme) != 0) { 1563 topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR, 1564 "topology in unrecognized scheme, %s, expecting %s\n", 1565 scheme, escheme); 1566 (void) topo_mod_seterrno(tmp, ETOPO_PRSR_BADSCH); 1567 xmlFree(scheme); 1568 xmlFreeDoc(document); 1569 return (NULL); 1570 } 1571 1572 if (dtdpath != NULL) { 1573 dtd = xmlParseDTD(NULL, (xmlChar *)dtdpath); 1574 if (dtd == NULL) { 1575 topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR, 1576 "Could not parse DTD \"%s\".\n", 1577 dtdpath); 1578 xmlFree(scheme); 1579 xmlFreeDoc(document); 1580 return (NULL); 1581 } 1582 1583 if (document->extSubset != NULL) 1584 xmlFreeDtd(document->extSubset); 1585 1586 document->extSubset = dtd; 1587 } 1588 1589 if (xmlXIncludeProcessFlags(document, XML_PARSE_XINCLUDE) == -1) { 1590 xmlFree(scheme); 1591 xmlFreeDoc(document); 1592 topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR, 1593 "couldn't handle XInclude statements in document\n"); 1594 return (NULL); 1595 } 1596 1597 if (validate) { 1598 if ((vcp = xmlNewValidCtxt()) == NULL) { 1599 xmlFree(scheme); 1600 xmlFreeDoc(document); 1601 return (NULL); 1602 } 1603 vcp->warning = xmlParserValidityWarning; 1604 vcp->error = xmlParserValidityError; 1605 1606 e = xmlValidateDocument(vcp, document); 1607 1608 xmlFreeValidCtxt(vcp); 1609 1610 if (e == 0) 1611 topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR, 1612 "Document is not valid.\n"); 1613 } 1614 1615 if ((r = tf_info_new(tmp, document, scheme)) == NULL) { 1616 xmlFree(scheme); 1617 xmlFreeDoc(document); 1618 return (NULL); 1619 } 1620 1621 xmlFree(scheme); 1622 scheme = NULL; 1623 return (r); 1624 } 1625 1626 tf_info_t * 1627 topo_xml_read(topo_mod_t *tmp, const char *path, const char *escheme) 1628 { 1629 int fd; 1630 tf_info_t *tip; 1631 1632 if ((fd = open(path, O_RDONLY)) < 0) { 1633 topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR, 1634 "failed to open %s for reading\n", path); 1635 return (NULL); 1636 } 1637 tip = txml_file_parse(tmp, fd, path, escheme); 1638 (void) close(fd); 1639 return (tip); 1640 } 1641