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 #include <limits.h> 28 #include <strings.h> 29 #include <string.h> 30 #include <unistd.h> 31 #include <stdio.h> 32 #include <alloca.h> 33 #include <devid.h> 34 #include <sys/stat.h> 35 #include <libnvpair.h> 36 #include <fm/topo_mod.h> 37 #include <fm/fmd_fmri.h> 38 #include <sys/fm/protocol.h> 39 40 #include <topo_method.h> 41 #include <topo_subr.h> 42 #include <dev.h> 43 44 static int dev_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t, 45 topo_instance_t, void *, void *); 46 static void dev_release(topo_mod_t *, tnode_t *); 47 static int dev_fmri_nvl2str(topo_mod_t *, tnode_t *, topo_version_t, 48 nvlist_t *, nvlist_t **); 49 static int dev_fmri_str2nvl(topo_mod_t *, tnode_t *, topo_version_t, 50 nvlist_t *, nvlist_t **); 51 static int dev_fmri_create_meth(topo_mod_t *, tnode_t *, topo_version_t, 52 nvlist_t *, nvlist_t **); 53 static int dev_fmri_present(topo_mod_t *, tnode_t *, topo_version_t, 54 nvlist_t *, nvlist_t **); 55 static int dev_fmri_replaced(topo_mod_t *, tnode_t *, topo_version_t, 56 nvlist_t *, nvlist_t **); 57 static int dev_fmri_unusable(topo_mod_t *, tnode_t *, topo_version_t, 58 nvlist_t *, nvlist_t **); 59 static int dev_fmri_service_state(topo_mod_t *, tnode_t *, topo_version_t, 60 nvlist_t *, nvlist_t **); 61 62 static const topo_method_t dev_methods[] = { 63 { TOPO_METH_NVL2STR, TOPO_METH_NVL2STR_DESC, TOPO_METH_NVL2STR_VERSION, 64 TOPO_STABILITY_INTERNAL, dev_fmri_nvl2str }, 65 { TOPO_METH_STR2NVL, TOPO_METH_STR2NVL_DESC, TOPO_METH_STR2NVL_VERSION, 66 TOPO_STABILITY_INTERNAL, dev_fmri_str2nvl }, 67 { TOPO_METH_FMRI, TOPO_METH_FMRI_DESC, TOPO_METH_FMRI_VERSION, 68 TOPO_STABILITY_INTERNAL, dev_fmri_create_meth }, 69 { TOPO_METH_PRESENT, TOPO_METH_PRESENT_DESC, TOPO_METH_PRESENT_VERSION, 70 TOPO_STABILITY_INTERNAL, dev_fmri_present }, 71 { TOPO_METH_REPLACED, TOPO_METH_REPLACED_DESC, 72 TOPO_METH_REPLACED_VERSION, TOPO_STABILITY_INTERNAL, 73 dev_fmri_replaced }, 74 { TOPO_METH_UNUSABLE, TOPO_METH_UNUSABLE_DESC, 75 TOPO_METH_UNUSABLE_VERSION, TOPO_STABILITY_INTERNAL, 76 dev_fmri_unusable }, 77 { TOPO_METH_SERVICE_STATE, TOPO_METH_SERVICE_STATE_DESC, 78 TOPO_METH_SERVICE_STATE_VERSION, TOPO_STABILITY_INTERNAL, 79 dev_fmri_service_state }, 80 { NULL } 81 }; 82 83 static const topo_modops_t dev_ops = 84 { dev_enum, dev_release }; 85 static const topo_modinfo_t dev_info = 86 { "dev", FM_FMRI_SCHEME_DEV, DEV_VERSION, &dev_ops }; 87 88 int 89 dev_init(topo_mod_t *mod, topo_version_t version) 90 { 91 if (getenv("TOPOHCDEBUG")) 92 topo_mod_setdebug(mod); 93 topo_mod_dprintf(mod, "initializing dev builtin\n"); 94 95 if (version != DEV_VERSION) 96 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 97 98 if (topo_mod_register(mod, &dev_info, TOPO_VERSION) != 0) { 99 topo_mod_dprintf(mod, "failed to register dev_info: " 100 "%s\n", topo_mod_errmsg(mod)); 101 return (-1); 102 } 103 104 return (0); 105 } 106 107 void 108 dev_fini(topo_mod_t *mod) 109 { 110 topo_mod_unregister(mod); 111 } 112 113 /*ARGSUSED*/ 114 static int 115 dev_enum(topo_mod_t *mod, tnode_t *pnode, const char *name, 116 topo_instance_t min, topo_instance_t max, void *notused1, void *notused2) 117 { 118 (void) topo_method_register(mod, pnode, dev_methods); 119 return (0); 120 } 121 122 static void 123 dev_release(topo_mod_t *mod, tnode_t *node) 124 { 125 topo_method_unregister_all(mod, node); 126 } 127 128 static ssize_t 129 fmri_nvl2str(nvlist_t *nvl, char *buf, size_t buflen) 130 { 131 nvlist_t *anvl = NULL; 132 nvpair_t *apair; 133 uint8_t version; 134 ssize_t size = 0; 135 char *devid = NULL; 136 char *devpath = NULL; 137 char *aname, *aval; 138 int err; 139 140 if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 || 141 version > FM_DEV_SCHEME_VERSION) 142 return (-1); 143 144 /* Get authority, if present */ 145 err = nvlist_lookup_nvlist(nvl, FM_FMRI_AUTHORITY, &anvl); 146 if (err != 0 && err != ENOENT) 147 return (-1); 148 149 /* Get devid, if present */ 150 err = nvlist_lookup_string(nvl, FM_FMRI_DEV_ID, &devid); 151 if (err != 0 && err != ENOENT) 152 return (-1); 153 154 /* There must be a device path present */ 155 err = nvlist_lookup_string(nvl, FM_FMRI_DEV_PATH, &devpath); 156 if (err != 0 || devpath == NULL) 157 return (-1); 158 159 160 /* dev:// */ 161 topo_fmristr_build(&size, 162 buf, buflen, FM_FMRI_SCHEME_DEV, NULL, "://"); 163 164 /* authority, if any */ 165 if (anvl != NULL) { 166 for (apair = nvlist_next_nvpair(anvl, NULL); 167 apair != NULL; apair = nvlist_next_nvpair(anvl, apair)) { 168 if (nvpair_type(apair) != DATA_TYPE_STRING || 169 nvpair_value_string(apair, &aval) != 0) 170 continue; 171 aname = nvpair_name(apair); 172 topo_fmristr_build(&size, buf, buflen, ":", NULL, NULL); 173 topo_fmristr_build(&size, buf, buflen, "=", 174 aname, aval); 175 } 176 } 177 178 /* device-id part, topo_fmristr_build does nothing if devid is NULL */ 179 topo_fmristr_build(&size, 180 buf, buflen, devid, "/:" FM_FMRI_DEV_ID "=", NULL); 181 182 /* device-path part */ 183 topo_fmristr_build(&size, buf, buflen, devpath, "/", NULL); 184 185 return (size); 186 } 187 188 /*ARGSUSED*/ 189 static int 190 dev_fmri_nvl2str(topo_mod_t *mod, tnode_t *node, topo_version_t version, 191 nvlist_t *nvl, nvlist_t **out) 192 { 193 ssize_t len; 194 char *name = NULL; 195 nvlist_t *fmristr; 196 197 if (version > TOPO_METH_NVL2STR_VERSION) 198 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 199 200 if ((len = fmri_nvl2str(nvl, NULL, 0)) == 0 || 201 (name = topo_mod_alloc(mod, len + 1)) == NULL || 202 fmri_nvl2str(nvl, name, len + 1) == 0) { 203 if (name != NULL) 204 topo_mod_free(mod, name, len + 1); 205 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 206 } 207 208 if (topo_mod_nvalloc(mod, &fmristr, NV_UNIQUE_NAME) != 0) 209 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 210 if (nvlist_add_string(fmristr, "fmri-string", name) != 0) { 211 topo_mod_free(mod, name, len + 1); 212 nvlist_free(fmristr); 213 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 214 } 215 topo_mod_free(mod, name, len + 1); 216 *out = fmristr; 217 218 return (0); 219 } 220 221 /*ARGSUSED*/ 222 static int 223 dev_fmri_str2nvl(topo_mod_t *mod, tnode_t *node, topo_version_t version, 224 nvlist_t *in, nvlist_t **out) 225 { 226 nvlist_t *fmri; 227 char *devpath; 228 char *devid = NULL; 229 char *str; 230 int err; 231 232 if (version > TOPO_METH_STR2NVL_VERSION) 233 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 234 235 if (nvlist_lookup_string(in, "fmri-string", &str) != 0) 236 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 237 238 /* We're expecting a string version of a dev scheme FMRI */ 239 if (strncmp(str, "dev:///", 7) != 0) 240 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 241 242 devpath = str + 7; 243 if (strncmp(devpath, ":" FM_FMRI_DEV_ID "=", 7) == 0) { 244 char *n; 245 int len; 246 247 n = strchr(devpath + 7, '/'); 248 if (n == NULL) 249 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 250 len = n - (devpath + 7); 251 devid = alloca(len + 1); 252 (void) memcpy(devid, devpath + 7, len); 253 devid[len] = 0; 254 devpath = n + 1; 255 } 256 257 /* the device-path should start with a slash */ 258 if (*devpath != '/') 259 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 260 261 if (topo_mod_nvalloc(mod, &fmri, NV_UNIQUE_NAME) != 0) 262 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 263 264 err = nvlist_add_uint8(fmri, FM_VERSION, FM_DEV_SCHEME_VERSION); 265 err |= nvlist_add_string(fmri, FM_FMRI_SCHEME, FM_FMRI_SCHEME_DEV); 266 err |= nvlist_add_string(fmri, FM_FMRI_DEV_PATH, devpath); 267 if (devid != NULL) 268 err |= nvlist_add_string(fmri, FM_FMRI_DEV_ID, devid); 269 270 if (err != 0) { 271 nvlist_free(fmri); 272 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 273 } 274 *out = fmri; 275 276 return (0); 277 } 278 279 /*ARGSUSED*/ 280 static int 281 dev_fmri_present(topo_mod_t *mod, tnode_t *node, topo_version_t version, 282 nvlist_t *in, nvlist_t **out) 283 { 284 uint8_t fmversion; 285 char *devpath = NULL; 286 uint32_t present; 287 char *devid = NULL, *path; 288 ddi_devid_t id; 289 ddi_devid_t matchid; 290 di_node_t dnode; 291 struct stat sb; 292 int len; 293 294 if (version > TOPO_METH_PRESENT_VERSION) 295 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 296 297 if (nvlist_lookup_uint8(in, FM_VERSION, &fmversion) != 0 || 298 fmversion > FM_DEV_SCHEME_VERSION || 299 nvlist_lookup_string(in, FM_FMRI_DEV_PATH, &devpath) != 0) 300 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 301 302 (void) nvlist_lookup_string(in, FM_FMRI_DEV_ID, &devid); 303 304 if (devpath == NULL || strlen(devpath) == 0) 305 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 306 307 /* 308 * stat() the device node in devfs. This will tell us if the device is 309 * present or not. Don't stat the minor, just the whole device. 310 * If the device is present and there is a devid, it must also match. 311 * so di_init that one node. No need for DINFOFORCE. 312 */ 313 len = strlen(devpath) + strlen("/devices") + 1; 314 path = topo_mod_alloc(mod, len); 315 (void) snprintf(path, len, "/devices%s", devpath); 316 if (devid == NULL) { 317 if (stat(path, &sb) != -1) 318 present = 1; 319 else if ((dnode = di_init("/", DINFOCACHE)) == DI_NODE_NIL) 320 present = 0; 321 else { 322 if (di_lookup_node(dnode, devpath) == DI_NODE_NIL) 323 present = 0; 324 else 325 present = 1; 326 di_fini(dnode); 327 } 328 } else { 329 if (stat(path, &sb) == -1) 330 present = 0; 331 else if ((dnode = di_init(devpath, DINFOCPYONE)) == DI_NODE_NIL) 332 present = 0; 333 else { 334 if ((id = di_devid(dnode)) == NULL || 335 devid_str_decode(devid, &matchid, NULL) != 0) 336 present = 0; 337 else { 338 if (devid_compare(id, matchid) != 0) 339 present = 0; 340 else 341 present = 1; 342 devid_free(matchid); 343 } 344 di_fini(dnode); 345 } 346 } 347 topo_mod_free(mod, path, len); 348 349 if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0) 350 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 351 if (nvlist_add_uint32(*out, TOPO_METH_PRESENT_RET, present) != 0) { 352 nvlist_free(*out); 353 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 354 } 355 356 return (0); 357 } 358 359 /*ARGSUSED*/ 360 static int 361 dev_fmri_replaced(topo_mod_t *mod, tnode_t *node, topo_version_t version, 362 nvlist_t *in, nvlist_t **out) 363 { 364 uint8_t fmversion; 365 char *devpath = NULL; 366 uint32_t rval; 367 char *devid = NULL, *path; 368 ddi_devid_t id; 369 ddi_devid_t matchid; 370 di_node_t dnode; 371 struct stat sb; 372 int len; 373 374 if (version > TOPO_METH_REPLACED_VERSION) 375 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 376 377 if (nvlist_lookup_uint8(in, FM_VERSION, &fmversion) != 0 || 378 fmversion > FM_DEV_SCHEME_VERSION || 379 nvlist_lookup_string(in, FM_FMRI_DEV_PATH, &devpath) != 0) 380 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 381 382 (void) nvlist_lookup_string(in, FM_FMRI_DEV_ID, &devid); 383 384 if (devpath == NULL || strlen(devpath) == 0) 385 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 386 387 /* 388 * stat() the device node in devfs. This will tell us if the device is 389 * present or not. Don't stat the minor, just the whole device. 390 * If the device is present and there is a devid, it must also match. 391 * so di_init that one node. No need for DINFOFORCE. 392 */ 393 len = strlen(devpath) + strlen("/devices") + 1; 394 path = topo_mod_alloc(mod, len); 395 (void) snprintf(path, len, "/devices%s", devpath); 396 if (devid == NULL) { 397 if (stat(path, &sb) != -1) 398 rval = FMD_OBJ_STATE_UNKNOWN; 399 else if ((dnode = di_init("/", DINFOCACHE)) == DI_NODE_NIL) 400 rval = FMD_OBJ_STATE_NOT_PRESENT; 401 else { 402 if (di_lookup_node(dnode, devpath) == DI_NODE_NIL) 403 rval = FMD_OBJ_STATE_NOT_PRESENT; 404 else 405 rval = FMD_OBJ_STATE_UNKNOWN; 406 di_fini(dnode); 407 } 408 } else { 409 if (stat(path, &sb) == -1) 410 rval = FMD_OBJ_STATE_NOT_PRESENT; 411 else if ((dnode = di_init(devpath, DINFOCPYONE)) == DI_NODE_NIL) 412 rval = FMD_OBJ_STATE_NOT_PRESENT; 413 else { 414 if ((id = di_devid(dnode)) == NULL || 415 devid_str_decode(devid, &matchid, NULL) != 0) 416 rval = FMD_OBJ_STATE_UNKNOWN; 417 else { 418 if (devid_compare(id, matchid) != 0) 419 rval = FMD_OBJ_STATE_REPLACED; 420 else 421 rval = FMD_OBJ_STATE_STILL_PRESENT; 422 devid_free(matchid); 423 } 424 di_fini(dnode); 425 } 426 } 427 topo_mod_free(mod, path, len); 428 429 if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0) 430 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 431 if (nvlist_add_uint32(*out, TOPO_METH_REPLACED_RET, rval) != 0) { 432 nvlist_free(*out); 433 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 434 } 435 436 return (0); 437 } 438 439 /*ARGSUSED*/ 440 static int 441 dev_fmri_unusable(topo_mod_t *mod, tnode_t *node, topo_version_t version, 442 nvlist_t *in, nvlist_t **out) 443 { 444 di_node_t dnode; 445 uint8_t fmversion; 446 char *devpath = NULL; 447 uint32_t unusable; 448 uint_t state; 449 450 if (version > TOPO_METH_UNUSABLE_VERSION) 451 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 452 453 if (nvlist_lookup_uint8(in, FM_VERSION, &fmversion) != 0 || 454 fmversion > FM_DEV_SCHEME_VERSION || 455 nvlist_lookup_string(in, FM_FMRI_DEV_PATH, &devpath) != 0) 456 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 457 458 if (devpath == NULL) 459 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 460 461 if ((dnode = di_init(devpath, DINFOCPYONE)) == DI_NODE_NIL) { 462 if (errno != ENXIO) 463 return (topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM)); 464 unusable = 1; 465 } else { 466 uint_t retired = di_retired(dnode); 467 state = di_state(dnode); 468 if (retired || (state & (DI_DEVICE_OFFLINE | DI_DEVICE_DOWN | 469 DI_BUS_QUIESCED | DI_BUS_DOWN))) 470 unusable = 1; 471 else 472 unusable = 0; 473 di_fini(dnode); 474 } 475 476 if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0) 477 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 478 if (nvlist_add_uint32(*out, TOPO_METH_UNUSABLE_RET, unusable) != 0) { 479 nvlist_free(*out); 480 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 481 } 482 483 return (0); 484 } 485 486 /*ARGSUSED*/ 487 static int 488 dev_fmri_service_state(topo_mod_t *mod, tnode_t *node, topo_version_t version, 489 nvlist_t *in, nvlist_t **out) 490 { 491 di_node_t dnode; 492 uint8_t fmversion; 493 char *devpath = NULL; 494 uint32_t service_state; 495 uint_t state; 496 497 if (version > TOPO_METH_SERVICE_STATE_VERSION) 498 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 499 500 if (nvlist_lookup_uint8(in, FM_VERSION, &fmversion) != 0 || 501 fmversion > FM_DEV_SCHEME_VERSION || 502 nvlist_lookup_string(in, FM_FMRI_DEV_PATH, &devpath) != 0) 503 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 504 505 if (devpath == NULL) 506 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 507 508 if ((dnode = di_init(devpath, DINFOCPYONE)) == DI_NODE_NIL) { 509 if (errno != ENXIO) 510 return (topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM)); 511 service_state = FMD_SERVICE_STATE_UNUSABLE; 512 } else { 513 uint_t retired = di_retired(dnode); 514 state = di_state(dnode); 515 if (retired || (state & (DI_DEVICE_OFFLINE | DI_DEVICE_DOWN | 516 DI_BUS_QUIESCED | DI_BUS_DOWN))) 517 service_state = FMD_SERVICE_STATE_UNUSABLE; 518 else if (state & DI_DEVICE_DEGRADED) 519 service_state = FMD_SERVICE_STATE_DEGRADED; 520 else 521 service_state = FMD_SERVICE_STATE_OK; 522 di_fini(dnode); 523 } 524 525 if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0) 526 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 527 if (nvlist_add_uint32(*out, TOPO_METH_SERVICE_STATE_RET, 528 service_state) != 0) { 529 nvlist_free(*out); 530 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 531 } 532 533 return (0); 534 } 535 536 static nvlist_t * 537 dev_fmri_create(topo_mod_t *mp, const char *id, const char *path) 538 { 539 nvlist_t *out = NULL; 540 int e; 541 542 if (topo_mod_nvalloc(mp, &out, NV_UNIQUE_NAME) != 0) { 543 (void) topo_mod_seterrno(mp, EMOD_FMRI_NVL); 544 return (NULL); 545 } 546 e = nvlist_add_string(out, FM_FMRI_SCHEME, FM_FMRI_SCHEME_DEV); 547 e |= nvlist_add_uint8(out, FM_VERSION, FM_DEV_SCHEME_VERSION); 548 e |= nvlist_add_string(out, FM_FMRI_DEV_PATH, path); 549 550 if (id != NULL) 551 e |= nvlist_add_string(out, FM_FMRI_DEV_ID, id); 552 553 if (e == 0) 554 return (out); 555 556 topo_mod_dprintf(mp, "construction of dev nvl failed"); 557 (void) topo_mod_seterrno(mp, EMOD_FMRI_NVL); 558 nvlist_free(out); 559 return (NULL); 560 } 561 562 /*ARGSUSED*/ 563 static int 564 dev_fmri_create_meth(topo_mod_t *mp, tnode_t *node, topo_version_t version, 565 nvlist_t *in, nvlist_t **out) 566 { 567 nvlist_t *args = NULL; 568 char *path, *id = NULL; 569 570 if (version > TOPO_METH_FMRI_VERSION) 571 return (topo_mod_seterrno(mp, EMOD_VER_NEW)); 572 573 if (nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_NVL, &args) != 0 || 574 nvlist_lookup_string(args, FM_FMRI_DEV_PATH, &path) != 0) { 575 topo_mod_dprintf(mp, "no path string in method argument\n"); 576 return (topo_mod_seterrno(mp, EMOD_METHOD_INVAL)); 577 } 578 579 (void) nvlist_lookup_string(args, FM_FMRI_DEV_ID, &id); 580 581 if ((*out = dev_fmri_create(mp, id, path)) == NULL) 582 return (-1); 583 return (0); 584 } 585