1 /* 2 * 3 * CDDL HEADER START 4 * 5 * The contents of this file are subject to the terms of the 6 * Common Development and Distribution License (the "License"). 7 * You may not use this file except in compliance with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 23 /* 24 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 25 * Use is subject to license terms. 26 */ 27 28 #pragma ident "%Z%%M% %I% %E% SMI" 29 30 #include <stdio.h> 31 #include <stdlib.h> 32 #include <string.h> 33 #include <errno.h> 34 #include <ctype.h> 35 #include <alloca.h> 36 #include <limits.h> 37 #include <fm/topo_mod.h> 38 #include <sys/param.h> 39 #include <sys/systeminfo.h> 40 #include <sys/fm/protocol.h> 41 #include <topo_parse.h> 42 43 #include <hc_canon.h> 44 45 #define HC "hc" 46 #define HC_VERSION TOPO_VERSION 47 48 static int hc_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t, 49 topo_instance_t, void *); 50 static void hc_release(topo_mod_t *, tnode_t *); 51 static int hc_contains(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *, 52 nvlist_t **); 53 static int hc_present(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *, 54 nvlist_t **); 55 static int hc_unusable(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *, 56 nvlist_t **); 57 static int hc_fmri_nvl2str(topo_mod_t *, tnode_t *, topo_version_t, 58 nvlist_t *, nvlist_t **); 59 static int hc_fmri_str2nvl(topo_mod_t *, tnode_t *, topo_version_t, 60 nvlist_t *, nvlist_t **); 61 static int hc_compare(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *, 62 nvlist_t **); 63 static int hc_fmri_create_meth(topo_mod_t *, tnode_t *, topo_version_t, 64 nvlist_t *, nvlist_t **); 65 66 static nvlist_t *hc_fmri_create(topo_mod_t *, nvlist_t *, int, const char *, 67 topo_instance_t inst, const nvlist_t *, const char *, const char *, 68 const char *); 69 70 const topo_method_t hc_methods[] = { 71 { "hc_contains", "Hardware Component Contains", HC_VERSION, 72 TOPO_STABILITY_INTERNAL, hc_contains }, 73 { "hc_present", "Hardware Component Present", HC_VERSION, 74 TOPO_STABILITY_INTERNAL, hc_present }, 75 { "hc_unusable", "Hardware Component Unusable", HC_VERSION, 76 TOPO_STABILITY_INTERNAL, hc_unusable }, 77 { TOPO_METH_NVL2STR, TOPO_METH_NVL2STR_DESC, TOPO_METH_NVL2STR_VERSION, 78 TOPO_STABILITY_INTERNAL, hc_fmri_nvl2str }, 79 { TOPO_METH_STR2NVL, TOPO_METH_STR2NVL_DESC, TOPO_METH_STR2NVL_VERSION, 80 TOPO_STABILITY_INTERNAL, hc_fmri_str2nvl }, 81 { TOPO_METH_COMPARE, TOPO_METH_COMPARE_DESC, TOPO_METH_COMPARE_VERSION, 82 TOPO_STABILITY_INTERNAL, hc_compare }, 83 { TOPO_METH_FMRI, TOPO_METH_FMRI_DESC, TOPO_METH_FMRI_VERSION, 84 TOPO_STABILITY_INTERNAL, hc_fmri_create_meth }, 85 { NULL } 86 }; 87 88 const topo_modinfo_t hc_info = 89 { HC, HC_VERSION, hc_enum, hc_release }; 90 91 void 92 hc_init(topo_mod_t *mp) 93 { 94 /* 95 * Turn on module debugging output 96 */ 97 topo_mod_setdebug(mp, TOPO_DBG_ALL); 98 topo_mod_dprintf(mp, "initializing hc builtin\n"); 99 100 if (topo_mod_register(mp, &hc_info, NULL) != 0) { 101 topo_mod_dprintf(mp, "failed to register hc: " 102 "%s\n", topo_mod_errmsg(mp)); 103 } 104 } 105 106 void 107 hc_fini(topo_mod_t *mp) 108 { 109 topo_mod_unregister(mp); 110 } 111 112 /*ARGSUSED*/ 113 int 114 hc_enum(topo_mod_t *mp, tnode_t *pnode, const char *name, topo_instance_t min, 115 topo_instance_t max, void *notused) 116 { 117 nvlist_t *pfmri = NULL; 118 nvlist_t *nvl; 119 int err; 120 /* 121 * Register root node methods 122 */ 123 if (strcmp(name, HC) == 0) { 124 (void) topo_method_register(mp, pnode, hc_methods); 125 return (0); 126 } 127 if (min != max) { 128 topo_mod_dprintf(mp, 129 "Request to enumerate %s component with an " 130 "ambiguous instance number, min (%d) != max (%d).\n", 131 HC, min, max); 132 return (topo_mod_seterrno(mp, EINVAL)); 133 } 134 135 (void) topo_node_resource(pnode, &pfmri, &err); 136 nvl = hc_fmri_create(mp, pfmri, FM_HC_SCHEME_VERSION, name, min, 137 NULL, NULL, NULL, NULL); 138 nvlist_free(pfmri); /* callee ignores NULLs */ 139 if (nvl == NULL) 140 return (-1); 141 142 if (topo_node_bind(mp, pnode, name, min, nvl, NULL) == NULL) { 143 topo_mod_dprintf(mp, "topo_node_bind failed: %s\n", 144 topo_strerror(topo_mod_errno(mp))); 145 nvlist_free(nvl); 146 return (-1); 147 } 148 nvlist_free(nvl); 149 return (0); 150 } 151 152 /*ARGSUSED*/ 153 static void 154 hc_release(topo_mod_t *mp, tnode_t *node) 155 { 156 topo_method_unregister_all(mp, node); 157 } 158 159 /*ARGSUSED*/ 160 static int 161 hc_contains(topo_mod_t *mp, tnode_t *node, topo_version_t version, 162 nvlist_t *in, nvlist_t **out) 163 { 164 return (topo_mod_seterrno(mp, EMOD_METHOD_NOTSUP)); 165 } 166 167 /*ARGSUSED*/ 168 static int 169 hc_present(topo_mod_t *mp, tnode_t *node, topo_version_t version, 170 nvlist_t *in, nvlist_t **out) 171 { 172 return (topo_mod_seterrno(mp, EMOD_METHOD_NOTSUP)); 173 } 174 175 /*ARGSUSED*/ 176 static int 177 hc_unusable(topo_mod_t *mp, tnode_t *node, topo_version_t version, 178 nvlist_t *in, nvlist_t **out) 179 { 180 return (topo_mod_seterrno(mp, EMOD_METHOD_NOTSUP)); 181 } 182 183 /*ARGSUSED*/ 184 static int 185 hc_compare(topo_mod_t *mp, tnode_t *node, topo_version_t version, 186 nvlist_t *in, nvlist_t **out) 187 { 188 uint8_t v1, v2; 189 nvlist_t *nv1, *nv2; 190 nvlist_t **hcp1, **hcp2; 191 int err, i; 192 uint_t nhcp1, nhcp2; 193 194 if (version > TOPO_METH_COMPARE_VERSION) 195 return (topo_mod_seterrno(mp, EMOD_VER_NEW)); 196 197 if (nvlist_lookup_nvlist(in, "nv1", &nv1) != 0 || 198 nvlist_lookup_nvlist(in, "nv2", &nv2) != 0) 199 return (topo_mod_seterrno(mp, EMOD_METHOD_INVAL)); 200 201 if (nvlist_lookup_uint8(nv1, FM_VERSION, &v1) != 0 || 202 nvlist_lookup_uint8(nv2, FM_VERSION, &v2) != 0 || 203 v1 > FM_HC_SCHEME_VERSION || v2 > FM_HC_SCHEME_VERSION) 204 return (topo_mod_seterrno(mp, EMOD_FMRI_VERSION)); 205 206 err = nvlist_lookup_nvlist_array(nv1, FM_FMRI_HC_LIST, &hcp1, &nhcp1); 207 err |= nvlist_lookup_nvlist_array(nv2, FM_FMRI_HC_LIST, &hcp2, &nhcp2); 208 if (err != 0) 209 return (topo_mod_seterrno(mp, EMOD_FMRI_NVL)); 210 211 if (nhcp1 != nhcp2) 212 return (0); 213 214 for (i = 0; i < nhcp1; i++) { 215 char *nm1 = NULL; 216 char *nm2 = NULL; 217 char *id1 = NULL; 218 char *id2 = NULL; 219 220 (void) nvlist_lookup_string(hcp1[i], FM_FMRI_HC_NAME, &nm1); 221 (void) nvlist_lookup_string(hcp2[i], FM_FMRI_HC_NAME, &nm2); 222 (void) nvlist_lookup_string(hcp1[i], FM_FMRI_HC_ID, &id1); 223 (void) nvlist_lookup_string(hcp2[i], FM_FMRI_HC_ID, &id2); 224 if (nm1 == NULL || nm2 == NULL || id1 == NULL || id2 == NULL) 225 return (topo_mod_seterrno(mp, EMOD_FMRI_NVL)); 226 227 if (strcmp(nm1, nm2) == 0 && strcmp(id1, id2) == 0) 228 continue; 229 230 return (0); 231 } 232 233 return (1); 234 } 235 236 /* 237 * buf_append -- Append str to buf (if it's non-NULL). Place prepend 238 * in buf in front of str and append behind it (if they're non-NULL). 239 * Continue to update size even if we run out of space to actually 240 * stuff characters in the buffer. 241 */ 242 static void 243 buf_append(ssize_t *sz, char *buf, size_t buflen, char *str, 244 char *prepend, char *append) 245 { 246 ssize_t left; 247 248 if (str == NULL) 249 return; 250 251 if (buflen == 0 || (left = buflen - *sz) < 0) 252 left = 0; 253 254 if (buf != NULL && left != 0) 255 buf += *sz; 256 257 if (prepend == NULL && append == NULL) 258 *sz += snprintf(buf, left, "%s", str); 259 else if (append == NULL) 260 *sz += snprintf(buf, left, "%s%s", prepend, str); 261 else if (prepend == NULL) 262 *sz += snprintf(buf, left, "%s%s", str, append); 263 else 264 *sz += snprintf(buf, left, "%s%s%s", prepend, str, append); 265 } 266 267 static ssize_t 268 fmri_nvl2str(nvlist_t *nvl, char *buf, size_t buflen) 269 { 270 nvlist_t **hcprs = NULL; 271 nvlist_t *anvl = NULL; 272 uint8_t version; 273 ssize_t size = 0; 274 uint_t hcnprs; 275 char *achas = NULL; 276 char *adom = NULL; 277 char *aprod = NULL; 278 char *asrvr = NULL; 279 char *ahost = NULL; 280 char *serial = NULL; 281 char *part = NULL; 282 char *root = NULL; 283 char *rev = NULL; 284 int more_auth = 0; 285 int err, i; 286 287 if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 || 288 version > FM_HC_SCHEME_VERSION) 289 return (-1); 290 291 /* Get authority, if present */ 292 err = nvlist_lookup_nvlist(nvl, FM_FMRI_AUTHORITY, &anvl); 293 if (err != 0 && err != ENOENT) 294 return (-1); 295 296 if ((err = nvlist_lookup_string(nvl, FM_FMRI_HC_ROOT, &root)) != 0) 297 return (-1); 298 299 err = nvlist_lookup_nvlist_array(nvl, FM_FMRI_HC_LIST, &hcprs, &hcnprs); 300 if (err != 0 || hcprs == NULL) 301 return (-1); 302 303 if (anvl != NULL) { 304 (void) nvlist_lookup_string(anvl, 305 FM_FMRI_AUTH_PRODUCT, &aprod); 306 (void) nvlist_lookup_string(anvl, 307 FM_FMRI_AUTH_CHASSIS, &achas); 308 (void) nvlist_lookup_string(anvl, 309 FM_FMRI_AUTH_DOMAIN, &adom); 310 (void) nvlist_lookup_string(anvl, 311 FM_FMRI_AUTH_SERVER, &asrvr); 312 (void) nvlist_lookup_string(anvl, 313 FM_FMRI_AUTH_HOST, &ahost); 314 if (aprod != NULL) 315 more_auth++; 316 if (achas != NULL) 317 more_auth++; 318 if (adom != NULL) 319 more_auth++; 320 if (asrvr != NULL) 321 more_auth++; 322 if (ahost != NULL) 323 more_auth++; 324 } 325 326 (void) nvlist_lookup_string(nvl, FM_FMRI_HC_SERIAL_ID, &serial); 327 (void) nvlist_lookup_string(nvl, FM_FMRI_HC_PART, &part); 328 (void) nvlist_lookup_string(nvl, FM_FMRI_HC_REVISION, &rev); 329 330 /* hc:// */ 331 buf_append(&size, buf, buflen, FM_FMRI_SCHEME_HC, NULL, "://"); 332 333 /* authority, if any */ 334 if (aprod != NULL) 335 buf_append(&size, buf, buflen, aprod, FM_FMRI_AUTH_PRODUCT "=", 336 --more_auth > 0 ? "," : NULL); 337 if (achas != NULL) 338 buf_append(&size, buf, buflen, achas, FM_FMRI_AUTH_CHASSIS "=", 339 --more_auth > 0 ? "," : NULL); 340 if (adom != NULL) 341 buf_append(&size, buf, buflen, adom, FM_FMRI_AUTH_DOMAIN "=", 342 --more_auth > 0 ? "," : NULL); 343 if (asrvr != NULL) 344 buf_append(&size, buf, buflen, asrvr, FM_FMRI_AUTH_SERVER "=", 345 --more_auth > 0 ? "," : NULL); 346 if (ahost != NULL) 347 buf_append(&size, buf, buflen, ahost, FM_FMRI_AUTH_HOST "=", 348 NULL); 349 350 /* separating slash */ 351 if (serial != NULL || part != NULL || rev != NULL) 352 buf_append(&size, buf, buflen, "/", NULL, NULL); 353 354 /* hardware-id part */ 355 buf_append(&size, buf, buflen, serial, ":" FM_FMRI_HC_SERIAL_ID "=", 356 NULL); 357 buf_append(&size, buf, buflen, part, ":" FM_FMRI_HC_PART "=", NULL); 358 buf_append(&size, buf, buflen, rev, ":" FM_FMRI_HC_REVISION "=", NULL); 359 360 /* separating slash */ 361 buf_append(&size, buf, buflen, "/", NULL, NULL); 362 363 /* hc-root */ 364 buf_append(&size, buf, buflen, root, NULL, NULL); 365 366 /* all the pairs */ 367 for (i = 0; i < hcnprs; i++) { 368 char *nm = NULL; 369 char *id = NULL; 370 371 if (i > 0) 372 buf_append(&size, buf, buflen, "/", NULL, NULL); 373 (void) nvlist_lookup_string(hcprs[i], FM_FMRI_HC_NAME, &nm); 374 (void) nvlist_lookup_string(hcprs[i], FM_FMRI_HC_ID, &id); 375 if (nm == NULL || id == NULL) 376 return (0); 377 buf_append(&size, buf, buflen, nm, NULL, "="); 378 buf_append(&size, buf, buflen, id, NULL, NULL); 379 } 380 381 return (size); 382 } 383 384 /*ARGSUSED*/ 385 static int 386 hc_fmri_nvl2str(topo_mod_t *mod, tnode_t *node, topo_version_t version, 387 nvlist_t *nvl, nvlist_t **out) 388 { 389 ssize_t len; 390 char *name = NULL; 391 nvlist_t *fmristr; 392 393 if (version > TOPO_METH_NVL2STR_VERSION) 394 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 395 396 if ((len = fmri_nvl2str(nvl, NULL, 0)) == 0 || 397 (name = topo_mod_alloc(mod, len + 1)) == NULL || 398 fmri_nvl2str(nvl, name, len + 1) == 0) { 399 if (name != NULL) 400 topo_mod_free(mod, name, len + 1); 401 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 402 } 403 404 if (topo_mod_nvalloc(mod, &fmristr, NV_UNIQUE_NAME) != 0) 405 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 406 if (nvlist_add_string(fmristr, "fmri-string", name) != 0) { 407 topo_mod_free(mod, name, len + 1); 408 nvlist_free(fmristr); 409 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 410 } 411 topo_mod_free(mod, name, len + 1); 412 *out = fmristr; 413 414 return (0); 415 } 416 417 static nvlist_t * 418 hc_base_fmri_create(topo_mod_t *mod, const nvlist_t *auth, const char *part, 419 const char *rev, const char *serial) 420 { 421 nvlist_t *fmri; 422 int err = 0; 423 424 /* 425 * Create base HC nvlist 426 */ 427 if (topo_mod_nvalloc(mod, &fmri, NV_UNIQUE_NAME) != 0) 428 return (NULL); 429 430 err = nvlist_add_uint8(fmri, FM_VERSION, FM_HC_SCHEME_VERSION); 431 err |= nvlist_add_string(fmri, FM_FMRI_SCHEME, FM_FMRI_SCHEME_HC); 432 err |= nvlist_add_string(fmri, FM_FMRI_HC_ROOT, ""); 433 if (err != 0) { 434 nvlist_free(fmri); 435 return (NULL); 436 } 437 438 /* 439 * Add optional payload members 440 */ 441 if (serial != NULL) 442 (void) nvlist_add_string(fmri, FM_FMRI_HC_SERIAL_ID, serial); 443 if (part != NULL) 444 (void) nvlist_add_string(fmri, FM_FMRI_HC_PART, part); 445 if (rev != NULL) 446 (void) nvlist_add_string(fmri, FM_FMRI_HC_REVISION, rev); 447 if (auth != NULL) 448 (void) nvlist_add_nvlist(fmri, FM_FMRI_AUTHORITY, 449 (nvlist_t *)auth); 450 451 return (fmri); 452 } 453 454 static nvlist_t ** 455 make_hc_pairs(topo_mod_t *mod, char *fromstr, int *num) 456 { 457 nvlist_t **pa; 458 char *starti, *startn, *endi, *endi2; 459 char *ne, *ns; 460 char *cname; 461 char *find; 462 char *cid; 463 int nslashes = 0; 464 int npairs = 0; 465 int i, e; 466 467 /* 468 * Count equal signs and slashes to determine how many 469 * hc-pairs will be present in the final FMRI. There should 470 * be at least as many slashes as equal signs. There can be 471 * more, though if the string after an = includes them. 472 */ 473 find = fromstr; 474 while ((ne = strchr(find, '=')) != NULL) { 475 find = ne + 1; 476 npairs++; 477 } 478 479 find = fromstr; 480 while ((ns = strchr(find, '/')) != NULL) { 481 find = ns + 1; 482 nslashes++; 483 } 484 485 /* 486 * Do we appear to have a well-formed string version of the FMRI? 487 */ 488 if (nslashes < npairs || npairs == 0) 489 return (NULL); 490 491 *num = npairs; 492 493 find = fromstr; 494 495 pa = topo_mod_alloc(mod, npairs * sizeof (nvlist_t *)); 496 /* 497 * We go through a pretty complicated procedure to find the 498 * name and id for each pair. That's because, unfortunately, 499 * we have some ids that can have slashes within them. So 500 * we can't just search for the next slash after the equal sign 501 * and decide that starts a new pair. Instead we have to find 502 * an equal sign for the next pair and work our way back to the 503 * slash from there. 504 */ 505 for (i = 0; i < npairs; i++) { 506 pa[i] = NULL; 507 startn = strchr(find, '/'); 508 if (startn == NULL) 509 break; 510 startn++; 511 starti = strchr(find, '='); 512 if (starti == NULL) 513 break; 514 *starti = '\0'; 515 cname = topo_mod_strdup(mod, startn); 516 *starti++ = '='; 517 endi = strchr(starti, '='); 518 if (endi != NULL) { 519 *endi = '\0'; 520 endi2 = strrchr(starti, '/'); 521 if (endi2 == NULL) 522 break; 523 *endi = '='; 524 *endi2 = '\0'; 525 cid = topo_mod_strdup(mod, starti); 526 *endi2 = '/'; 527 find = endi2; 528 } else { 529 cid = topo_mod_strdup(mod, starti); 530 find = starti + strlen(starti); 531 } 532 if ((e = topo_mod_nvalloc(mod, &pa[i], NV_UNIQUE_NAME)) != 0) { 533 topo_mod_strfree(mod, cname); 534 topo_mod_strfree(mod, cid); 535 break; 536 } 537 538 e = nvlist_add_string(pa[i], FM_FMRI_HC_NAME, cname); 539 e |= nvlist_add_string(pa[i], FM_FMRI_HC_ID, cid); 540 541 topo_mod_strfree(mod, cname); 542 topo_mod_strfree(mod, cid); 543 544 if (e != 0) { 545 break; 546 } 547 } 548 if (i < npairs) { 549 while (i >= 0) 550 if (pa[i--] != NULL) 551 nvlist_free(pa[i + 1]); 552 topo_mod_free(mod, pa, npairs * sizeof (nvlist_t *)); 553 return (NULL); 554 } 555 556 return (pa); 557 } 558 559 /*ARGSUSED*/ 560 static int 561 hc_fmri_str2nvl(topo_mod_t *mod, tnode_t *node, topo_version_t version, 562 nvlist_t *in, nvlist_t **out) 563 { 564 nvlist_t **pa = NULL; 565 nvlist_t *nf = NULL; 566 char *str, *copy; 567 int npairs; 568 int i, e; 569 570 if (version > TOPO_METH_STR2NVL_VERSION) 571 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 572 573 if (nvlist_lookup_string(in, "fmri-string", &str) != 0) 574 return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL)); 575 576 /* We're expecting a string version of an hc scheme FMRI */ 577 if (strncmp(str, "hc:///", 6) != 0) 578 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 579 580 copy = topo_mod_strdup(mod, str + 5); 581 if ((pa = make_hc_pairs(mod, copy, &npairs)) == NULL) { 582 topo_mod_strfree(mod, copy); 583 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 584 } 585 topo_mod_strfree(mod, copy); 586 587 if ((nf = hc_base_fmri_create(mod, NULL, NULL, NULL, NULL)) == NULL) 588 goto hcfmbail; 589 if ((e = nvlist_add_uint32(nf, FM_FMRI_HC_LIST_SZ, npairs)) == 0) 590 e = nvlist_add_nvlist_array(nf, FM_FMRI_HC_LIST, pa, npairs); 591 if (e != 0) { 592 topo_mod_dprintf(mod, "construction of new hc nvl failed"); 593 goto hcfmbail; 594 } 595 for (i = 0; i < npairs; i++) 596 nvlist_free(pa[i]); 597 topo_mod_free(mod, pa, npairs * sizeof (nvlist_t *)); 598 *out = nf; 599 600 return (0); 601 602 hcfmbail: 603 if (nf != NULL) 604 nvlist_free(nf); 605 for (i = 0; i < npairs; i++) 606 nvlist_free(pa[i]); 607 topo_mod_free(mod, pa, npairs * sizeof (nvlist_t *)); 608 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 609 } 610 611 static nvlist_t * 612 hc_list_create(topo_mod_t *mod, const char *name, char *inst) 613 { 614 int err; 615 nvlist_t *hc; 616 617 if (topo_mod_nvalloc(mod, &hc, NV_UNIQUE_NAME) != 0) 618 return (NULL); 619 620 err = nvlist_add_string(hc, FM_FMRI_HC_NAME, name); 621 err |= nvlist_add_string(hc, FM_FMRI_HC_ID, inst); 622 if (err != 0) { 623 nvlist_free(hc); 624 return (NULL); 625 } 626 627 return (hc); 628 } 629 630 static nvlist_t * 631 hc_create_seterror(topo_mod_t *mod, nvlist_t **hcl, int n, nvlist_t *fmri, 632 int err) 633 { 634 int i; 635 636 if (hcl != NULL) { 637 for (i = 0; i < n + 1; ++i) 638 nvlist_free(hcl[i]); 639 640 topo_mod_free(mod, hcl, sizeof (nvlist_t *) * (n + 1)); 641 } 642 643 nvlist_free(fmri); 644 645 (void) topo_mod_seterrno(mod, err); 646 647 topo_mod_dprintf(mod, "unable to create hc FMRI: %s\n", 648 topo_mod_errmsg(mod)); 649 650 return (NULL); 651 } 652 653 static int 654 hc_name_canonical(const char *name) 655 { 656 int i; 657 /* 658 * Only enumerate elements with correct canonical names 659 */ 660 for (i = 0; i < Hc_ncanon; i++) { 661 if (strcmp(name, Hc_canon[i]) == 0) 662 break; 663 } 664 if (i >= Hc_ncanon) 665 return (0); 666 else 667 return (1); 668 } 669 670 static nvlist_t * 671 hc_fmri_create(topo_mod_t *mod, nvlist_t *pfmri, int version, const char *name, 672 topo_instance_t inst, const nvlist_t *auth, const char *part, 673 const char *rev, const char *serial) 674 { 675 int i; 676 char str[21]; /* sizeof (UINT64_MAX) + '\0' */ 677 uint_t pelems = 0; 678 nvlist_t **phcl = NULL; 679 nvlist_t **hcl = NULL; 680 nvlist_t *fmri = NULL; 681 682 if (version > FM_HC_SCHEME_VERSION) 683 return (hc_create_seterror(mod, 684 hcl, pelems, fmri, EMOD_VER_OLD)); 685 else if (version < FM_HC_SCHEME_VERSION) 686 return (hc_create_seterror(mod, 687 hcl, pelems, fmri, EMOD_VER_NEW)); 688 689 /* 690 * Check that the requested name is in our canonical list 691 */ 692 if (hc_name_canonical(name) == 0) 693 return (hc_create_seterror(mod, 694 hcl, pelems, fmri, EMOD_NONCANON)); 695 /* 696 * Copy the parent's HC_LIST 697 */ 698 if (pfmri != NULL) { 699 if (nvlist_lookup_nvlist_array(pfmri, FM_FMRI_HC_LIST, 700 &phcl, &pelems) != 0) 701 return (hc_create_seterror(mod, 702 hcl, pelems, fmri, EMOD_FMRI_MALFORM)); 703 } 704 705 hcl = topo_mod_zalloc(mod, sizeof (nvlist_t *) * (pelems + 1)); 706 if (hcl == NULL) 707 return (hc_create_seterror(mod, hcl, pelems, fmri, 708 EMOD_NOMEM)); 709 710 for (i = 0; i < pelems; ++i) 711 if (topo_mod_nvdup(mod, phcl[i], &hcl[i]) != 0) 712 return (hc_create_seterror(mod, 713 hcl, pelems, fmri, EMOD_FMRI_NVL)); 714 715 (void) snprintf(str, sizeof (str), "%d", inst); 716 if ((hcl[i] = hc_list_create(mod, name, str)) == NULL) 717 return (hc_create_seterror(mod, 718 hcl, pelems, fmri, EMOD_FMRI_NVL)); 719 720 if ((fmri = hc_base_fmri_create(mod, auth, part, rev, serial)) == NULL) 721 return (hc_create_seterror(mod, 722 hcl, pelems, fmri, EMOD_FMRI_NVL)); 723 724 if (nvlist_add_nvlist_array(fmri, FM_FMRI_HC_LIST, hcl, pelems + 1) 725 != 0) 726 return (hc_create_seterror(mod, 727 hcl, pelems, fmri, EMOD_FMRI_NVL)); 728 729 if (hcl != NULL) { 730 for (i = 0; i < pelems + 1; ++i) { 731 if (hcl[i] != NULL) 732 nvlist_free(hcl[i]); 733 } 734 topo_mod_free(mod, hcl, sizeof (nvlist_t *) * (pelems + 1)); 735 } 736 737 return (fmri); 738 } 739 740 /*ARGSUSED*/ 741 static int 742 hc_fmri_create_meth(topo_mod_t *mp, tnode_t *node, topo_version_t version, 743 nvlist_t *in, nvlist_t **out) 744 { 745 nvlist_t *args, *pfmri; 746 nvlist_t *auth; 747 uint32_t inst; 748 char *name, *serial, *rev, *part; 749 750 if (version > TOPO_METH_FMRI_VERSION) 751 return (topo_mod_seterrno(mp, EMOD_VER_NEW)); 752 753 /* First the must-have fields */ 754 if (nvlist_lookup_string(in, TOPO_METH_FMRI_ARG_NAME, &name) != 0) 755 return (topo_mod_seterrno(mp, EMOD_METHOD_INVAL)); 756 if (nvlist_lookup_uint32(in, TOPO_METH_FMRI_ARG_INST, &inst) != 0) 757 return (topo_mod_seterrno(mp, EMOD_METHOD_INVAL)); 758 if (nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_NVL, &args) != 0) 759 return (topo_mod_seterrno(mp, EMOD_METHOD_INVAL)); 760 if (nvlist_lookup_nvlist(args, TOPO_METH_FMRI_ARG_PARENT, &pfmri) != 0) 761 return (topo_mod_seterrno(mp, EMOD_METHOD_INVAL)); 762 763 /* And then optional arguments */ 764 auth = NULL; 765 serial = rev = part = NULL; 766 (void) nvlist_lookup_nvlist(args, TOPO_METH_FMRI_ARG_AUTH, &auth); 767 (void) nvlist_lookup_string(args, TOPO_METH_FMRI_ARG_PART, &part); 768 (void) nvlist_lookup_string(args, TOPO_METH_FMRI_ARG_REV, &rev); 769 (void) nvlist_lookup_string(args, TOPO_METH_FMRI_ARG_SER, &serial); 770 771 *out = hc_fmri_create(mp, 772 pfmri, version, name, inst, auth, part, rev, serial); 773 if (*out == NULL) 774 return (-1); 775 return (0); 776 } 777