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 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 /* 27 * Copyright 2020 Joyent, Inc. 28 */ 29 30 #include <ctype.h> 31 #include <string.h> 32 #include <limits.h> 33 #include <fm/topo_mod.h> 34 #include <fm/fmd_fmri.h> 35 #include <sys/fm/protocol.h> 36 #include <topo_alloc.h> 37 #include <topo_digraph.h> 38 #include <topo_error.h> 39 #include <topo_hc.h> 40 #include <topo_method.h> 41 #include <topo_subr.h> 42 #include <topo_string.h> 43 44 /* 45 * Topology node properties and method operations may be accessed by FMRI. 46 * The FMRI used to perform property look-ups and method operations is 47 * the FMRI contained in the matching topology node's protocol property 48 * grouping for the resource property. The full range of fmd(1M) 49 * scheme plugin operations are supported as long as a backend method is 50 * supplied by a scheme-specific enumerator or the enumerator module that 51 * created the matching topology node. Support for fmd scheme operations 52 * include: 53 * 54 * - expand 55 * - present 56 * - replaced 57 * - contains 58 * - unusable 59 * - service_state 60 * - nvl2str 61 * - retire 62 * - unretire 63 * 64 * In addition, the following operations are supported per-FMRI: 65 * 66 * - str2nvl: convert string-based FMRI to nvlist 67 * - compare: compare two FMRIs 68 * - asru: lookup associated ASRU property by FMRI 69 * - fru: lookup associated FRU by FMRI 70 * - create: an FMRI nvlist by scheme type 71 * - propery lookup 72 * 73 * These routines may only be called by consumers of a topology snapshot. 74 * They may not be called by libtopo enumerator or method modules. 75 */ 76 77 /*ARGSUSED*/ 78 static int 79 set_error(topo_hdl_t *thp, int err, int *errp, char *method, nvlist_t *nvlp) 80 { 81 nvlist_free(nvlp); 82 83 topo_dprintf(thp, TOPO_DBG_ERR, "%s failed: %s\n", method, 84 topo_strerror(err)); 85 86 *errp = err; 87 return (-1); 88 } 89 90 /*ARGSUSED*/ 91 static nvlist_t * 92 set_nverror(topo_hdl_t *thp, int err, int *errp, char *method, nvlist_t *nvlp) 93 { 94 nvlist_free(nvlp); 95 96 topo_dprintf(thp, TOPO_DBG_ERR, "%s failed: %s\n", method, 97 topo_strerror(err)); 98 99 *errp = err; 100 return (NULL); 101 } 102 103 int 104 topo_fmri_nvl2str(topo_hdl_t *thp, nvlist_t *fmri, char **fmristr, int *err) 105 { 106 char *scheme, *str; 107 nvlist_t *out = NULL; 108 tnode_t *rnode; 109 110 if (nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &scheme) != 0) 111 return (set_error(thp, ETOPO_FMRI_MALFORM, err, 112 TOPO_METH_NVL2STR, out)); 113 114 if ((rnode = topo_hdl_root(thp, scheme)) == NULL) 115 return (set_error(thp, ETOPO_METHOD_NOTSUP, err, 116 TOPO_METH_NVL2STR, out)); 117 118 if (topo_method_invoke(rnode, TOPO_METH_NVL2STR, 119 TOPO_METH_NVL2STR_VERSION, fmri, &out, err) != 0) 120 return (set_error(thp, *err, err, TOPO_METH_NVL2STR, out)); 121 122 if (out == NULL || nvlist_lookup_string(out, "fmri-string", &str) != 0) 123 return (set_error(thp, ETOPO_METHOD_INVAL, err, 124 TOPO_METH_NVL2STR, out)); 125 126 if ((*fmristr = topo_hdl_strdup(thp, str)) == NULL) 127 return (set_error(thp, ETOPO_NOMEM, err, 128 TOPO_METH_NVL2STR, out)); 129 130 nvlist_free(out); 131 132 return (0); 133 } 134 135 int 136 topo_fmri_str2nvl(topo_hdl_t *thp, const char *fmristr, nvlist_t **fmri, 137 int *err) 138 { 139 char *f, buf[PATH_MAX], *method = TOPO_METH_STR2NVL; 140 nvlist_t *out = NULL, *in = NULL; 141 tnode_t *rnode; 142 boolean_t is_path = B_FALSE; 143 144 /* 145 * For path FMRI's the scheme is encoded in the authority portion of 146 * the FMRI - e.g. 147 * 148 * path://scheme=<scheme>/... 149 */ 150 if (strncmp(fmristr, "path://", 7) == 0) { 151 char *scheme_start, *scheme_end; 152 153 is_path = B_TRUE; 154 method = TOPO_METH_PATH_STR2NVL; 155 156 if ((scheme_start = strchr(fmristr, '=')) == NULL) { 157 return (set_error(thp, ETOPO_FMRI_MALFORM, err, 158 TOPO_METH_STR2NVL, in)); 159 } 160 scheme_start++; 161 if ((scheme_end = strchr(scheme_start, '/')) == NULL) { 162 return (set_error(thp, ETOPO_FMRI_MALFORM, err, 163 TOPO_METH_STR2NVL, in)); 164 } 165 (void) strlcpy(buf, scheme_start, 166 (scheme_end - scheme_start) + 1); 167 } else { 168 (void) strlcpy(buf, fmristr, sizeof (buf)); 169 170 if ((f = strchr(buf, ':')) == NULL) 171 return (set_error(thp, ETOPO_FMRI_MALFORM, err, 172 TOPO_METH_STR2NVL, in)); 173 174 *f = '\0'; /* strip trailing FMRI path */ 175 } 176 177 if (is_path) { 178 topo_digraph_t *tdg; 179 180 if ((tdg = topo_digraph_get(thp, buf)) == NULL) { 181 return (set_error(thp, ETOPO_METHOD_NOTSUP, err, 182 TOPO_METH_STR2NVL, in)); 183 } 184 rnode = tdg->tdg_rootnode; 185 } else if ((rnode = topo_hdl_root(thp, buf)) == NULL) { 186 return (set_error(thp, ETOPO_METHOD_NOTSUP, err, 187 TOPO_METH_STR2NVL, in)); 188 } 189 190 if (topo_hdl_nvalloc(thp, &in, NV_UNIQUE_NAME) != 0) 191 return (set_error(thp, ETOPO_FMRI_NVL, err, TOPO_METH_STR2NVL, 192 in)); 193 194 if (nvlist_add_string(in, "fmri-string", fmristr) != 0) 195 return (set_error(thp, ETOPO_FMRI_NVL, err, TOPO_METH_STR2NVL, 196 in)); 197 198 if (topo_method_invoke(rnode, method, TOPO_METH_STR2NVL_VERSION, in, 199 &out, err) != 0) 200 return (set_error(thp, *err, err, TOPO_METH_STR2NVL, in)); 201 202 nvlist_free(in); 203 204 if (out == NULL || 205 topo_hdl_nvdup(thp, out, fmri) != 0) 206 return (set_error(thp, ETOPO_FMRI_NVL, err, 207 TOPO_METH_STR2NVL, out)); 208 209 nvlist_free(out); 210 211 return (0); 212 } 213 214 int 215 topo_fmri_present(topo_hdl_t *thp, nvlist_t *fmri, int *err) 216 { 217 uint32_t present = 0; 218 char *scheme; 219 nvlist_t *out = NULL; 220 tnode_t *rnode; 221 222 if (nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &scheme) != 0) 223 return (set_error(thp, ETOPO_FMRI_MALFORM, err, 224 TOPO_METH_PRESENT, out)); 225 226 if ((rnode = topo_hdl_root(thp, scheme)) == NULL) 227 return (set_error(thp, ETOPO_METHOD_NOTSUP, err, 228 TOPO_METH_PRESENT, out)); 229 230 if (topo_method_invoke(rnode, TOPO_METH_PRESENT, 231 TOPO_METH_PRESENT_VERSION, fmri, &out, err) < 0) { 232 (void) set_error(thp, *err, err, TOPO_METH_PRESENT, out); 233 return (present); 234 } 235 236 (void) nvlist_lookup_uint32(out, TOPO_METH_PRESENT_RET, &present); 237 nvlist_free(out); 238 239 return (present); 240 } 241 242 int 243 topo_fmri_replaced(topo_hdl_t *thp, nvlist_t *fmri, int *err) 244 { 245 uint32_t replaced = FMD_OBJ_STATE_NOT_PRESENT; 246 char *scheme; 247 nvlist_t *out = NULL; 248 tnode_t *rnode; 249 250 if (nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &scheme) != 0) 251 return (set_error(thp, ETOPO_FMRI_MALFORM, err, 252 TOPO_METH_REPLACED, out)); 253 254 if ((rnode = topo_hdl_root(thp, scheme)) == NULL) 255 return (set_error(thp, ETOPO_METHOD_NOTSUP, err, 256 TOPO_METH_REPLACED, out)); 257 258 if (topo_method_invoke(rnode, TOPO_METH_REPLACED, 259 TOPO_METH_REPLACED_VERSION, fmri, &out, err) < 0) { 260 (void) set_error(thp, *err, err, TOPO_METH_REPLACED, out); 261 return (FMD_OBJ_STATE_UNKNOWN); 262 } 263 264 (void) nvlist_lookup_uint32(out, TOPO_METH_REPLACED_RET, &replaced); 265 nvlist_free(out); 266 267 return (replaced); 268 } 269 270 int 271 topo_fmri_contains(topo_hdl_t *thp, nvlist_t *fmri, nvlist_t *subfmri, int *err) 272 { 273 uint32_t contains; 274 char *scheme; 275 nvlist_t *in = NULL, *out = NULL; 276 tnode_t *rnode; 277 278 if (nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &scheme) != 0) 279 return (set_error(thp, ETOPO_FMRI_MALFORM, err, 280 TOPO_METH_CONTAINS, NULL)); 281 282 if ((rnode = topo_hdl_root(thp, scheme)) == NULL) 283 return (set_error(thp, ETOPO_METHOD_NOTSUP, err, 284 TOPO_METH_CONTAINS, NULL)); 285 286 if (topo_hdl_nvalloc(thp, &in, NV_UNIQUE_NAME) != 0) 287 return (set_error(thp, ETOPO_FMRI_NVL, err, TOPO_METH_CONTAINS, 288 NULL)); 289 290 if (nvlist_add_nvlist(in, TOPO_METH_FMRI_ARG_FMRI, fmri) != 0 || 291 nvlist_add_nvlist(in, TOPO_METH_FMRI_ARG_SUBFMRI, subfmri) != 0) 292 return (set_error(thp, ETOPO_FMRI_NVL, err, TOPO_METH_CONTAINS, 293 in)); 294 295 if (topo_method_invoke(rnode, TOPO_METH_CONTAINS, 296 TOPO_METH_CONTAINS_VERSION, in, &out, err) < 0) 297 return (set_error(thp, *err, err, TOPO_METH_CONTAINS, in)); 298 299 (void) nvlist_lookup_uint32(out, TOPO_METH_CONTAINS_RET, &contains); 300 nvlist_free(in); 301 nvlist_free(out); 302 303 return (contains); 304 } 305 306 int 307 topo_fmri_unusable(topo_hdl_t *thp, nvlist_t *fmri, int *err) 308 { 309 char *scheme; 310 uint32_t unusable = 0; 311 nvlist_t *out = NULL; 312 tnode_t *rnode; 313 314 if (nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &scheme) != 0) 315 return (set_error(thp, ETOPO_FMRI_MALFORM, err, 316 TOPO_METH_UNUSABLE, out)); 317 318 if ((rnode = topo_hdl_root(thp, scheme)) == NULL) 319 return (set_error(thp, ETOPO_METHOD_NOTSUP, err, 320 TOPO_METH_UNUSABLE, out)); 321 322 if (topo_method_invoke(rnode, TOPO_METH_UNUSABLE, 323 TOPO_METH_UNUSABLE_VERSION, fmri, &out, err) < 0) 324 return (set_error(thp, *err, err, TOPO_METH_UNUSABLE, out)); 325 326 (void) nvlist_lookup_uint32(out, TOPO_METH_UNUSABLE_RET, &unusable); 327 nvlist_free(out); 328 329 return (unusable); 330 } 331 332 int 333 topo_fmri_retire(topo_hdl_t *thp, nvlist_t *fmri, int *err) 334 { 335 char *scheme; 336 uint32_t status; 337 nvlist_t *out = NULL; 338 tnode_t *rnode; 339 340 if (nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &scheme) != 0) 341 return (set_error(thp, ETOPO_FMRI_MALFORM, err, 342 TOPO_METH_RETIRE, out)); 343 344 if ((rnode = topo_hdl_root(thp, scheme)) == NULL) 345 return (set_error(thp, ETOPO_METHOD_NOTSUP, err, 346 TOPO_METH_RETIRE, out)); 347 348 if (topo_method_invoke(rnode, TOPO_METH_RETIRE, 349 TOPO_METH_RETIRE_VERSION, fmri, &out, err) < 0) 350 return (set_error(thp, *err, err, TOPO_METH_RETIRE, out)); 351 352 if (nvlist_lookup_uint32(out, TOPO_METH_RETIRE_RET, &status) != 0) 353 return (set_error(thp, ETOPO_METHOD_FAIL, err, 354 TOPO_METH_RETIRE, out)); 355 nvlist_free(out); 356 357 return (status); 358 } 359 360 int 361 topo_fmri_unretire(topo_hdl_t *thp, nvlist_t *fmri, int *err) 362 { 363 char *scheme; 364 uint32_t status; 365 nvlist_t *out = NULL; 366 tnode_t *rnode; 367 368 if (nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &scheme) != 0) 369 return (set_error(thp, ETOPO_FMRI_MALFORM, err, 370 TOPO_METH_UNRETIRE, out)); 371 372 if ((rnode = topo_hdl_root(thp, scheme)) == NULL) 373 return (set_error(thp, ETOPO_METHOD_NOTSUP, err, 374 TOPO_METH_UNRETIRE, out)); 375 376 if (topo_method_invoke(rnode, TOPO_METH_UNRETIRE, 377 TOPO_METH_UNRETIRE_VERSION, fmri, &out, err) < 0) 378 return (set_error(thp, *err, err, TOPO_METH_UNRETIRE, out)); 379 380 if (nvlist_lookup_uint32(out, TOPO_METH_UNRETIRE_RET, &status) != 0) { 381 nvlist_free(out); 382 return (set_error(thp, ETOPO_METHOD_FAIL, err, 383 TOPO_METH_UNRETIRE, out)); 384 } 385 nvlist_free(out); 386 387 return (status); 388 } 389 390 int 391 topo_fmri_service_state(topo_hdl_t *thp, nvlist_t *fmri, int *err) 392 { 393 char *scheme; 394 uint32_t service_state = FMD_SERVICE_STATE_UNKNOWN; 395 nvlist_t *out = NULL; 396 tnode_t *rnode; 397 398 if (nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &scheme) != 0) 399 return (set_error(thp, ETOPO_FMRI_MALFORM, err, 400 TOPO_METH_SERVICE_STATE, out)); 401 402 if ((rnode = topo_hdl_root(thp, scheme)) == NULL) 403 return (set_error(thp, ETOPO_METHOD_NOTSUP, err, 404 TOPO_METH_SERVICE_STATE, out)); 405 406 if (topo_method_invoke(rnode, TOPO_METH_SERVICE_STATE, 407 TOPO_METH_SERVICE_STATE_VERSION, fmri, &out, err) < 0) 408 return (set_error(thp, *err, err, TOPO_METH_SERVICE_STATE, 409 out)); 410 411 (void) nvlist_lookup_uint32(out, TOPO_METH_SERVICE_STATE_RET, 412 &service_state); 413 nvlist_free(out); 414 415 return (service_state); 416 } 417 418 int 419 topo_fmri_expand(topo_hdl_t *thp, nvlist_t *fmri, int *err) 420 { 421 char *scheme; 422 nvlist_t *out = NULL; 423 tnode_t *rnode; 424 425 if (nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &scheme) != 0) 426 return (set_error(thp, ETOPO_FMRI_MALFORM, err, 427 TOPO_METH_EXPAND, out)); 428 429 if ((rnode = topo_hdl_root(thp, scheme)) == NULL) 430 return (set_error(thp, ETOPO_METHOD_NOTSUP, err, 431 TOPO_METH_EXPAND, out)); 432 433 if (topo_method_invoke(rnode, TOPO_METH_EXPAND, 434 TOPO_METH_EXPAND_VERSION, fmri, &out, err) != 0) 435 return (set_error(thp, *err, err, TOPO_METH_EXPAND, out)); 436 437 return (0); 438 } 439 440 static int 441 fmri_prop(topo_hdl_t *thp, nvlist_t *rsrc, const char *pgname, 442 const char *pname, nvlist_t *args, nvlist_t **prop, 443 int *err) 444 { 445 int rv; 446 nvlist_t *in = NULL; 447 tnode_t *rnode; 448 char *scheme; 449 450 if (nvlist_lookup_string(rsrc, FM_FMRI_SCHEME, &scheme) != 0) 451 return (set_error(thp, ETOPO_FMRI_MALFORM, err, 452 TOPO_METH_PROP_GET, in)); 453 454 if ((rnode = topo_hdl_root(thp, scheme)) == NULL) 455 return (set_error(thp, ETOPO_METHOD_NOTSUP, err, 456 TOPO_METH_PROP_GET, in)); 457 458 if (topo_hdl_nvalloc(thp, &in, NV_UNIQUE_NAME) != 0) 459 return (set_error(thp, ETOPO_FMRI_NVL, err, 460 TOPO_METH_PROP_GET, in)); 461 462 rv = nvlist_add_nvlist(in, TOPO_PROP_RESOURCE, rsrc); 463 rv |= nvlist_add_string(in, TOPO_PROP_GROUP, pgname); 464 rv |= nvlist_add_string(in, TOPO_PROP_VAL_NAME, pname); 465 if (args != NULL) 466 rv |= nvlist_add_nvlist(in, TOPO_PROP_PARGS, args); 467 if (rv != 0) 468 return (set_error(thp, ETOPO_FMRI_NVL, err, 469 TOPO_METH_PROP_GET, in)); 470 471 *prop = NULL; 472 rv = topo_method_invoke(rnode, TOPO_METH_PROP_GET, 473 TOPO_METH_PROP_GET_VERSION, in, prop, err); 474 475 nvlist_free(in); 476 477 if (rv != 0) 478 return (-1); /* *err is set for us */ 479 480 if (*prop == NULL) 481 return (set_error(thp, ETOPO_PROP_NOENT, err, 482 TOPO_METH_PROP_GET, NULL)); 483 return (0); 484 } 485 486 int 487 topo_fmri_asru(topo_hdl_t *thp, nvlist_t *nvl, nvlist_t **asru, int *err) 488 { 489 nvlist_t *ap, *prop = NULL; 490 491 if (fmri_prop(thp, nvl, TOPO_PGROUP_PROTOCOL, TOPO_PROP_ASRU, 492 nvl, &prop, err) < 0) 493 return (set_error(thp, *err, err, "topo_fmri_asru", NULL)); 494 495 if (nvlist_lookup_nvlist(prop, TOPO_PROP_VAL_VAL, &ap) != 0) 496 return (set_error(thp, ETOPO_PROP_NVL, err, "topo_fmri_asru", 497 prop)); 498 499 if (topo_hdl_nvdup(thp, ap, asru) < 0) 500 return (set_error(thp, ETOPO_PROP_NOMEM, err, "topo_fmri_asru", 501 prop)); 502 503 nvlist_free(prop); 504 505 return (0); 506 } 507 508 int 509 topo_fmri_fru(topo_hdl_t *thp, nvlist_t *nvl, nvlist_t **fru, int *err) 510 { 511 nvlist_t *fp, *prop = NULL; 512 513 if (fmri_prop(thp, nvl, TOPO_PGROUP_PROTOCOL, TOPO_PROP_FRU, 514 nvl, &prop, err) < 0) 515 return (set_error(thp, *err, err, "topo_fmri_fru", NULL)); 516 517 if (nvlist_lookup_nvlist(prop, TOPO_PROP_VAL_VAL, &fp) != 0) 518 return (set_error(thp, ETOPO_PROP_NVL, err, "topo_fmri_fru", 519 prop)); 520 521 if (topo_hdl_nvdup(thp, fp, fru) < 0) 522 return (set_error(thp, ETOPO_PROP_NOMEM, err, "topo_fmri_fru", 523 prop)); 524 525 nvlist_free(prop); 526 527 return (0); 528 } 529 530 int 531 topo_fmri_label(topo_hdl_t *thp, nvlist_t *nvl, char **label, int *err) 532 { 533 nvlist_t *prop = NULL; 534 char *lp; 535 536 if (fmri_prop(thp, nvl, TOPO_PGROUP_PROTOCOL, TOPO_PROP_LABEL, 537 NULL, &prop, err) < 0) 538 return (set_error(thp, *err, err, "topo_fmri_label", NULL)); 539 540 if (nvlist_lookup_string(prop, TOPO_PROP_VAL_VAL, &lp) != 0) 541 return (set_error(thp, ETOPO_PROP_NVL, err, "topo_fmri_label", 542 prop)); 543 544 if ((*label = topo_hdl_strdup(thp, lp)) == NULL) 545 return (set_error(thp, ETOPO_PROP_NOMEM, err, "topo_fmri_label", 546 prop)); 547 548 nvlist_free(prop); 549 550 return (0); 551 } 552 553 int 554 topo_fmri_serial(topo_hdl_t *thp, nvlist_t *nvl, char **serial, int *err) 555 { 556 nvlist_t *prop = NULL; 557 char *sp; 558 559 /* 560 * If there is a serial id in the resource fmri, then use that. 561 * Otherwise fall back to looking for a serial id property in the 562 * protocol group. 563 */ 564 if (nvlist_lookup_string(nvl, FM_FMRI_HC_SERIAL_ID, &sp) == 0) { 565 if ((*serial = topo_hdl_strdup(thp, sp)) == NULL) 566 return (set_error(thp, ETOPO_PROP_NOMEM, err, 567 "topo_fmri_serial", prop)); 568 else 569 return (0); 570 } 571 572 if (fmri_prop(thp, nvl, TOPO_PGROUP_PROTOCOL, FM_FMRI_HC_SERIAL_ID, 573 NULL, &prop, err) < 0) 574 return (set_error(thp, *err, err, "topo_fmri_serial", NULL)); 575 576 if (nvlist_lookup_string(prop, TOPO_PROP_VAL_VAL, &sp) != 0) 577 return (set_error(thp, ETOPO_PROP_NVL, err, "topo_fmri_serial", 578 prop)); 579 580 if ((*serial = topo_hdl_strdup(thp, sp)) == NULL) 581 return (set_error(thp, ETOPO_PROP_NOMEM, err, 582 "topo_fmri_serial", prop)); 583 584 nvlist_free(prop); 585 586 return (0); 587 } 588 589 int topo_fmri_getprop(topo_hdl_t *thp, nvlist_t *nvl, const char *pg, 590 const char *pname, nvlist_t *args, nvlist_t **prop, 591 int *err) 592 { 593 *prop = NULL; 594 595 return (fmri_prop(thp, nvl, pg, pname, args, prop, err)); 596 } 597 598 int topo_fmri_setprop(topo_hdl_t *thp, nvlist_t *nvl, const char *pg, 599 nvlist_t *prop, int flag, nvlist_t *args, int *err) 600 { 601 int rv; 602 nvlist_t *in = NULL, *out = NULL; 603 tnode_t *rnode; 604 char *scheme; 605 606 if (nvlist_lookup_string(nvl, FM_FMRI_SCHEME, &scheme) != 0) 607 return (set_error(thp, ETOPO_FMRI_MALFORM, err, 608 TOPO_METH_PROP_SET, in)); 609 610 if ((rnode = topo_hdl_root(thp, scheme)) == NULL) 611 return (set_error(thp, ETOPO_METHOD_NOTSUP, err, 612 TOPO_METH_PROP_SET, in)); 613 614 if (topo_hdl_nvalloc(thp, &in, NV_UNIQUE_NAME) != 0) 615 return (set_error(thp, ETOPO_FMRI_NVL, err, 616 TOPO_METH_PROP_SET, in)); 617 618 rv = nvlist_add_nvlist(in, TOPO_PROP_RESOURCE, nvl); 619 rv |= nvlist_add_string(in, TOPO_PROP_GROUP, pg); 620 rv |= nvlist_add_nvlist(in, TOPO_PROP_VAL, prop); 621 rv |= nvlist_add_int32(in, TOPO_PROP_FLAG, (int32_t)flag); 622 if (args != NULL) 623 rv |= nvlist_add_nvlist(in, TOPO_PROP_PARGS, args); 624 if (rv != 0) 625 return (set_error(thp, ETOPO_FMRI_NVL, err, 626 TOPO_METH_PROP_SET, in)); 627 628 rv = topo_method_invoke(rnode, TOPO_METH_PROP_SET, 629 TOPO_METH_PROP_SET_VERSION, in, &out, err); 630 631 nvlist_free(in); 632 633 /* no return values */ 634 nvlist_free(out); 635 636 if (rv) 637 return (-1); 638 639 return (0); 640 641 } 642 643 int 644 topo_fmri_getpgrp(topo_hdl_t *thp, nvlist_t *rsrc, const char *pgname, 645 nvlist_t **pgroup, int *err) 646 { 647 int rv; 648 nvlist_t *in = NULL; 649 tnode_t *rnode; 650 char *scheme; 651 652 if (nvlist_lookup_string(rsrc, FM_FMRI_SCHEME, &scheme) != 0) 653 return (set_error(thp, ETOPO_FMRI_MALFORM, err, 654 TOPO_METH_PROP_GET, in)); 655 656 if ((rnode = topo_hdl_root(thp, scheme)) == NULL) 657 return (set_error(thp, ETOPO_METHOD_NOTSUP, err, 658 TOPO_METH_PROP_GET, in)); 659 660 if (topo_hdl_nvalloc(thp, &in, NV_UNIQUE_NAME) != 0) 661 return (set_error(thp, ETOPO_FMRI_NVL, err, 662 TOPO_METH_PROP_GET, in)); 663 664 rv = nvlist_add_nvlist(in, TOPO_PROP_RESOURCE, rsrc); 665 rv |= nvlist_add_string(in, TOPO_PROP_GROUP, pgname); 666 if (rv != 0) 667 return (set_error(thp, ETOPO_FMRI_NVL, err, 668 TOPO_METH_PROP_GET, in)); 669 670 *pgroup = NULL; 671 rv = topo_method_invoke(rnode, TOPO_METH_PGRP_GET, 672 TOPO_METH_PGRP_GET_VERSION, in, pgroup, err); 673 674 nvlist_free(in); 675 676 if (rv != 0) 677 return (-1); /* *err is set for us */ 678 679 if (*pgroup == NULL) 680 return (set_error(thp, ETOPO_PROP_NOENT, err, 681 TOPO_METH_PROP_GET, NULL)); 682 return (0); 683 } 684 685 int 686 topo_fmri_compare(topo_hdl_t *thp, nvlist_t *f1, nvlist_t *f2, int *err) 687 { 688 uint32_t compare; 689 char *scheme1, *scheme2; 690 nvlist_t *in; 691 nvlist_t *out = NULL; 692 tnode_t *rnode; 693 694 if (nvlist_lookup_string(f1, FM_FMRI_SCHEME, &scheme1) != 0) 695 return (set_error(thp, ETOPO_FMRI_MALFORM, err, 696 TOPO_METH_COMPARE, NULL)); 697 if (nvlist_lookup_string(f2, FM_FMRI_SCHEME, &scheme2) != 0) 698 return (set_error(thp, ETOPO_FMRI_MALFORM, err, 699 TOPO_METH_COMPARE, NULL)); 700 701 if (strcmp(scheme1, scheme2) != 0) 702 return (0); 703 704 if ((rnode = topo_hdl_root(thp, scheme1)) == NULL) 705 return (set_error(thp, ETOPO_METHOD_NOTSUP, err, 706 TOPO_METH_COMPARE, NULL)); 707 708 if (topo_hdl_nvalloc(thp, &in, NV_UNIQUE_NAME) != 0) 709 return (set_error(thp, ETOPO_FMRI_NVL, err, TOPO_METH_COMPARE, 710 NULL)); 711 712 if (nvlist_add_nvlist(in, TOPO_METH_FMRI_ARG_NV1, f1) != 0 || 713 nvlist_add_nvlist(in, TOPO_METH_FMRI_ARG_NV2, f2) != 0) 714 return (set_error(thp, ETOPO_FMRI_NVL, err, TOPO_METH_COMPARE, 715 in)); 716 717 if (topo_method_invoke(rnode, TOPO_METH_COMPARE, 718 TOPO_METH_COMPARE_VERSION, in, &out, err) < 0) 719 return (set_error(thp, *err, err, TOPO_METH_COMPARE, in)); 720 721 (void) nvlist_lookup_uint32(out, TOPO_METH_COMPARE_RET, &compare); 722 nvlist_free(out); 723 nvlist_free(in); 724 725 return (compare); 726 } 727 728 /* 729 * topo_fmri_create 730 * 731 * If possible, creates an FMRI of the requested version in the 732 * requested scheme. Args are passed as part of the inputs to the 733 * fmri-create method of the scheme. 734 */ 735 nvlist_t * 736 topo_fmri_create(topo_hdl_t *thp, const char *scheme, const char *name, 737 topo_instance_t inst, nvlist_t *nvl, int *err) 738 { 739 nvlist_t *ins; 740 nvlist_t *out; 741 tnode_t *rnode; 742 743 ins = out = NULL; 744 745 if ((rnode = topo_hdl_root(thp, scheme)) == NULL) 746 return (set_nverror(thp, ETOPO_METHOD_NOTSUP, err, 747 TOPO_METH_FMRI, NULL)); 748 749 if ((*err = topo_hdl_nvalloc(thp, &ins, NV_UNIQUE_NAME)) != 0) 750 return (set_nverror(thp, ETOPO_FMRI_NVL, err, 751 TOPO_METH_FMRI, NULL)); 752 753 if (nvlist_add_string(ins, TOPO_METH_FMRI_ARG_NAME, name) != 0 || 754 nvlist_add_uint64(ins, TOPO_METH_FMRI_ARG_INST, inst) != 0) { 755 return (set_nverror(thp, ETOPO_FMRI_NVL, err, 756 TOPO_METH_FMRI, ins)); 757 } 758 759 if (nvl != NULL && 760 nvlist_add_nvlist(ins, TOPO_METH_FMRI_ARG_NVL, nvl) != 0) { 761 return (set_nverror(thp, ETOPO_FMRI_NVL, err, 762 TOPO_METH_FMRI, ins)); 763 } 764 if (topo_method_invoke(rnode, 765 TOPO_METH_FMRI, TOPO_METH_FMRI_VERSION, ins, &out, err) != 0) { 766 return (set_nverror(thp, *err, err, TOPO_METH_FMRI, ins)); 767 } 768 nvlist_free(ins); 769 return (out); 770 } 771 772 /* 773 * These private utility functions are used by fmd to maintain its resource 774 * cache. Because hc instance numbers are not guaranteed, it's possible to 775 * have two different FMRI strings represent the same logical entity. These 776 * functions hide this implementation detail from unknowing consumers such as 777 * fmd. 778 * 779 * Ideally, we'd like to do a str2nvl() and then a full FMRI hash and 780 * comparison, but these functions are designed to be fast and efficient. 781 * Given that there is only a single hc node that has this property 782 * (ses-enclosure), we hard-code this behavior here. If there are more 783 * instances of this behavior in the future, this function could be made more 784 * generic. 785 * 786 * This code also handles changes in the server-id or revision fields of the hc 787 * FMRI, as these fields have no bearing on equivalence of FRUs. 788 */ 789 static ulong_t 790 topo_fmri_strhash_one(const char *fmri, size_t len) 791 { 792 ulong_t g, h = 0; 793 size_t i; 794 795 for (i = 0; i < len; i++) { 796 h = (h << 4) + fmri[i]; 797 798 if ((g = (h & 0xf0000000)) != 0) { 799 h ^= (g >> 24); 800 h ^= g; 801 } 802 } 803 804 return (h); 805 } 806 807 static const char * 808 topo_fmri_next_auth(const char *auth) 809 { 810 const char *colon, *slash; 811 812 colon = strchr(auth + 1, ':'); 813 slash = strchr(auth, '/'); 814 815 if (colon == NULL && slash == NULL) 816 return (NULL); 817 818 if (colon == NULL) 819 return (slash); 820 else if (slash < colon) 821 return (slash); 822 else 823 return (colon); 824 } 825 826 /* 827 * List of authority information we care about. Note that we explicitly ignore 828 * things that are properties of the chassis and not the resource itself: 829 * 830 * FM_FMRI_AUTH_PRODUCT_SN "product-sn" 831 * FM_FMRI_AUTH_PRODUCT "product-id" 832 * FM_FMRI_AUTH_DOMAIN "domain-id" 833 * FM_FMRI_AUTH_SERVER "server-id" 834 * FM_FMRI_AUTH_HOST "host-id" 835 * 836 * We also ignore the "revision" authority member, as that typically indicates 837 * the firmware revision and is not a static property of the FRU. This leaves 838 * the following interesting members: 839 * 840 * FM_FMRI_AUTH_CHASSIS "chassis-id" 841 * FM_FMRI_HC_SERIAL_ID "serial" 842 * FM_FMRI_HC_PART "part" 843 */ 844 typedef enum { 845 HC_AUTH_CHASSIS, 846 HC_AUTH_SERIAL, 847 HC_AUTH_PART, 848 HC_AUTH_MAX 849 } hc_auth_type_t; 850 851 static char *hc_auth_table[] = { 852 FM_FMRI_AUTH_CHASSIS, 853 FM_FMRI_HC_SERIAL_ID, 854 FM_FMRI_HC_PART 855 }; 856 857 /* 858 * Takes an authority member, with leading ":" and trailing "=", and returns 859 * one of the above types if it's one of the things we care about. If 860 * 'authlen' is specified, it is filled in with the length of the authority 861 * member, including leading and trailing characters. 862 */ 863 static hc_auth_type_t 864 hc_auth_to_type(const char *auth, size_t *authlen) 865 { 866 int i; 867 size_t len; 868 869 if (auth[0] != ':') 870 return (HC_AUTH_MAX); 871 872 for (i = 0; i < HC_AUTH_MAX; i++) { 873 len = strlen(hc_auth_table[i]); 874 875 if (strncmp(auth + 1, hc_auth_table[i], len) == 0 && 876 auth[len + 1] == '=') { 877 if (authlen) 878 *authlen = len + 2; 879 break; 880 } 881 } 882 883 return (i); 884 } 885 886 /*ARGSUSED*/ 887 ulong_t 888 topo_fmri_strhash_internal(topo_hdl_t *thp, const char *fmri, boolean_t noauth) 889 { 890 const char *auth, *next; 891 const char *enclosure; 892 ulong_t h; 893 hc_auth_type_t type; 894 895 if (strncmp(fmri, "hc://", 5) != 0) 896 return (topo_fmri_strhash_one(fmri, strlen(fmri))); 897 898 enclosure = strstr(fmri, SES_ENCLOSURE); 899 900 h = 0; 901 902 auth = next = fmri + 5; 903 while (*next != '/') { 904 auth = next; 905 906 if ((next = topo_fmri_next_auth(auth)) == NULL) { 907 next = auth; 908 break; 909 } 910 911 if ((type = hc_auth_to_type(auth, NULL)) == HC_AUTH_MAX) 912 continue; 913 914 if (!noauth || type == HC_AUTH_CHASSIS) 915 h += topo_fmri_strhash_one(auth, next - auth); 916 } 917 918 if (enclosure) { 919 next = enclosure + sizeof (SES_ENCLOSURE); 920 while (isdigit(*next)) 921 next++; 922 } 923 924 h += topo_fmri_strhash_one(next, strlen(next)); 925 926 return (h); 927 } 928 929 /*ARGSUSED*/ 930 ulong_t 931 topo_fmri_strhash(topo_hdl_t *thp, const char *fmri) 932 { 933 return (topo_fmri_strhash_internal(thp, fmri, B_FALSE)); 934 } 935 936 /*ARGSUSED*/ 937 ulong_t 938 topo_fmri_strhash_noauth(topo_hdl_t *thp, const char *fmri) 939 { 940 return (topo_fmri_strhash_internal(thp, fmri, B_TRUE)); 941 } 942 943 944 static void 945 topo_fmri_strcmp_parse_auth(const char *auth, const char *authtype[], 946 size_t authlen[]) 947 { 948 int i; 949 const char *next; 950 hc_auth_type_t type; 951 size_t len; 952 953 for (i = 0; i < HC_AUTH_MAX; i++) 954 authlen[i] = 0; 955 956 while (*auth != '/' && 957 (next = topo_fmri_next_auth(auth)) != NULL) { 958 if ((type = hc_auth_to_type(auth, &len)) == HC_AUTH_MAX) { 959 auth = next; 960 continue; 961 } 962 963 authtype[type] = auth + len; 964 authlen[type] = next - (auth + len); 965 auth = next; 966 } 967 } 968 969 /*ARGSUSED*/ 970 static boolean_t 971 topo_fmri_strcmp_internal(topo_hdl_t *thp, const char *a, const char *b, 972 boolean_t noauth) 973 { 974 const char *fmria, *fmrib; 975 const char *autha[HC_AUTH_MAX], *authb[HC_AUTH_MAX]; 976 size_t authlena[HC_AUTH_MAX], authlenb[HC_AUTH_MAX]; 977 int i; 978 979 /* 980 * For non-hc FMRIs, we don't do anything. 981 */ 982 if (strncmp(a, "hc://", 5) != 0 || 983 strncmp(b, "hc://", 5) != 0) 984 return (strcmp(a, b) == 0); 985 986 /* 987 * Get the portion of the FMRI independent of the authority 988 * information. 989 */ 990 fmria = strchr(a + 5, '/'); 991 fmrib = strchr(b + 5, '/'); 992 if (fmria == NULL || fmrib == NULL) 993 return (strcmp(a, b)); 994 fmria++; 995 fmrib++; 996 997 /* 998 * Comparing fmri authority information is a bit of a pain, because 999 * there may be a different number of members, and they can (but 1000 * shouldn't be) in a different order. We need to create a copy of the 1001 * authority and parse it into pieces. Because this function is 1002 * intended to be fast (and not necessarily extensible), we hard-code 1003 * the list of possible authority members in an enum and parse it into 1004 * an array. 1005 */ 1006 topo_fmri_strcmp_parse_auth(a + 5, autha, authlena); 1007 topo_fmri_strcmp_parse_auth(b + 5, authb, authlenb); 1008 1009 for (i = 0; i < HC_AUTH_MAX; i++) { 1010 if (noauth && i != HC_AUTH_CHASSIS) 1011 continue; 1012 1013 if (authlena[i] == 0 && authlenb[i] == 0) 1014 continue; 1015 1016 if (authlena[i] != authlenb[i]) 1017 return (B_FALSE); 1018 1019 if (strncmp(autha[i], authb[i], authlena[i]) != 0) 1020 return (B_FALSE); 1021 } 1022 1023 /* 1024 * If this is rooted at a ses-enclosure node, skip past the instance 1025 * number, as it has no meaning. 1026 */ 1027 if (strncmp(fmria, SES_ENCLOSURE, sizeof (SES_ENCLOSURE) - 1) == 0 && 1028 strncmp(fmrib, SES_ENCLOSURE, sizeof (SES_ENCLOSURE) - 1) == 0) { 1029 fmria += sizeof (SES_ENCLOSURE); 1030 fmrib += sizeof (SES_ENCLOSURE); 1031 1032 while (isdigit(*fmria)) 1033 fmria++; 1034 while (isdigit(*fmrib)) 1035 fmrib++; 1036 } 1037 1038 return (strcmp(fmria, fmrib) == 0); 1039 } 1040 1041 /*ARGSUSED*/ 1042 boolean_t 1043 topo_fmri_strcmp(topo_hdl_t *thp, const char *a, const char *b) 1044 { 1045 return (topo_fmri_strcmp_internal(thp, a, b, B_FALSE)); 1046 } 1047 1048 /*ARGSUSED*/ 1049 boolean_t 1050 topo_fmri_strcmp_noauth(topo_hdl_t *thp, const char *a, const char *b) 1051 { 1052 return (topo_fmri_strcmp_internal(thp, a, b, B_TRUE)); 1053 } 1054 1055 int 1056 topo_fmri_facility(topo_hdl_t *thp, nvlist_t *rsrc, const char *fac_type, 1057 uint32_t fac_subtype, topo_walk_cb_t cb, void *cb_args, int *err) 1058 { 1059 int rv; 1060 nvlist_t *in = NULL, *out; 1061 tnode_t *rnode; 1062 char *scheme; 1063 1064 if (nvlist_lookup_string(rsrc, FM_FMRI_SCHEME, &scheme) != 0) 1065 return (set_error(thp, ETOPO_FMRI_MALFORM, err, 1066 TOPO_METH_PROP_GET, in)); 1067 1068 if ((rnode = topo_hdl_root(thp, scheme)) == NULL) 1069 return (set_error(thp, ETOPO_METHOD_NOTSUP, err, 1070 TOPO_METH_PROP_GET, in)); 1071 1072 if (topo_hdl_nvalloc(thp, &in, NV_UNIQUE_NAME) != 0) 1073 return (set_error(thp, ETOPO_FMRI_NVL, err, 1074 TOPO_METH_PROP_GET, in)); 1075 1076 rv = nvlist_add_nvlist(in, TOPO_PROP_RESOURCE, rsrc); 1077 rv |= nvlist_add_string(in, FM_FMRI_FACILITY_TYPE, fac_type); 1078 rv |= nvlist_add_uint32(in, "type", fac_subtype); 1079 #ifdef _LP64 1080 rv |= nvlist_add_uint64(in, "callback", (uint64_t)cb); 1081 rv |= nvlist_add_uint64(in, "callback-args", (uint64_t)cb_args); 1082 #else 1083 rv |= nvlist_add_uint32(in, "callback", (uint32_t)cb); 1084 rv |= nvlist_add_uint32(in, "callback-args", (uint32_t)cb_args); 1085 #endif 1086 if (rv != 0) 1087 return (set_error(thp, ETOPO_FMRI_NVL, err, 1088 TOPO_METH_PROP_GET, in)); 1089 1090 rv = topo_method_invoke(rnode, TOPO_METH_FACILITY, 1091 TOPO_METH_FACILITY_VERSION, in, &out, err); 1092 1093 nvlist_free(in); 1094 1095 if (rv != 0) 1096 return (-1); /* *err is set for us */ 1097 1098 return (0); 1099 } 1100