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 (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. 24 */ 25 26 #include <limits.h> 27 #include <strings.h> 28 #include <string.h> 29 #include <unistd.h> 30 #include <stdio.h> 31 #include <alloca.h> 32 #include <devid.h> 33 #include <sys/stat.h> 34 #include <libnvpair.h> 35 #include <fm/topo_mod.h> 36 #include <fm/fmd_fmri.h> 37 #include <sys/fm/protocol.h> 38 39 #include <topo_method.h> 40 #include <topo_subr.h> 41 #include <dev.h> 42 43 static int dev_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t, 44 topo_instance_t, void *, void *); 45 static void dev_release(topo_mod_t *, tnode_t *); 46 static int dev_fmri_nvl2str(topo_mod_t *, tnode_t *, topo_version_t, 47 nvlist_t *, nvlist_t **); 48 static int dev_fmri_str2nvl(topo_mod_t *, tnode_t *, topo_version_t, 49 nvlist_t *, nvlist_t **); 50 static int dev_fmri_create_meth(topo_mod_t *, tnode_t *, topo_version_t, 51 nvlist_t *, nvlist_t **); 52 static int dev_fmri_present(topo_mod_t *, tnode_t *, topo_version_t, 53 nvlist_t *, nvlist_t **); 54 static int dev_fmri_replaced(topo_mod_t *, tnode_t *, topo_version_t, 55 nvlist_t *, nvlist_t **); 56 static int dev_fmri_unusable(topo_mod_t *, tnode_t *, topo_version_t, 57 nvlist_t *, nvlist_t **); 58 static int dev_fmri_service_state(topo_mod_t *, tnode_t *, topo_version_t, 59 nvlist_t *, nvlist_t **); 60 61 static const topo_method_t dev_methods[] = { 62 { TOPO_METH_NVL2STR, TOPO_METH_NVL2STR_DESC, TOPO_METH_NVL2STR_VERSION, 63 TOPO_STABILITY_INTERNAL, dev_fmri_nvl2str }, 64 { TOPO_METH_STR2NVL, TOPO_METH_STR2NVL_DESC, TOPO_METH_STR2NVL_VERSION, 65 TOPO_STABILITY_INTERNAL, dev_fmri_str2nvl }, 66 { TOPO_METH_FMRI, TOPO_METH_FMRI_DESC, TOPO_METH_FMRI_VERSION, 67 TOPO_STABILITY_INTERNAL, dev_fmri_create_meth }, 68 { TOPO_METH_PRESENT, TOPO_METH_PRESENT_DESC, TOPO_METH_PRESENT_VERSION, 69 TOPO_STABILITY_INTERNAL, dev_fmri_present }, 70 { TOPO_METH_REPLACED, TOPO_METH_REPLACED_DESC, 71 TOPO_METH_REPLACED_VERSION, TOPO_STABILITY_INTERNAL, 72 dev_fmri_replaced }, 73 { TOPO_METH_UNUSABLE, TOPO_METH_UNUSABLE_DESC, 74 TOPO_METH_UNUSABLE_VERSION, TOPO_STABILITY_INTERNAL, 75 dev_fmri_unusable }, 76 { TOPO_METH_SERVICE_STATE, TOPO_METH_SERVICE_STATE_DESC, 77 TOPO_METH_SERVICE_STATE_VERSION, TOPO_STABILITY_INTERNAL, 78 dev_fmri_service_state }, 79 { NULL } 80 }; 81 82 static const topo_modops_t dev_ops = 83 { dev_enum, dev_release }; 84 static const topo_modinfo_t dev_info = 85 { "dev", FM_FMRI_SCHEME_DEV, DEV_VERSION, &dev_ops }; 86 87 int 88 dev_init(topo_mod_t *mod, topo_version_t version) 89 { 90 if (getenv("TOPOHCDEBUG")) 91 topo_mod_setdebug(mod); 92 topo_mod_dprintf(mod, "initializing dev builtin\n"); 93 94 if (version != DEV_VERSION) 95 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 96 97 if (topo_mod_register(mod, &dev_info, TOPO_VERSION) != 0) { 98 topo_mod_dprintf(mod, "failed to register dev_info: " 99 "%s\n", topo_mod_errmsg(mod)); 100 return (-1); 101 } 102 103 return (0); 104 } 105 106 void 107 dev_fini(topo_mod_t *mod) 108 { 109 topo_mod_unregister(mod); 110 } 111 112 /*ARGSUSED*/ 113 static int 114 dev_enum(topo_mod_t *mod, tnode_t *pnode, const char *name, 115 topo_instance_t min, topo_instance_t max, void *notused1, void *notused2) 116 { 117 /* 118 * Methods are registered, but there is no enumeration. Should 119 * enumeration be added be sure to cater for global vs non-global 120 * zones. 121 */ 122 (void) topo_method_register(mod, pnode, dev_methods); 123 return (0); 124 } 125 126 static void 127 dev_release(topo_mod_t *mod, tnode_t *node) 128 { 129 topo_method_unregister_all(mod, node); 130 } 131 132 static ssize_t 133 fmri_nvl2str(nvlist_t *nvl, char *buf, size_t buflen) 134 { 135 char *devid = NULL, *tpl0id = NULL; 136 char *devpath = NULL; 137 ssize_t size = 0; 138 uint8_t version; 139 int err; 140 141 if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 || 142 version > FM_DEV_SCHEME_VERSION) 143 return (-1); 144 145 /* Get devid, if present */ 146 err = nvlist_lookup_string(nvl, FM_FMRI_DEV_ID, &devid); 147 if (err != 0 && err != ENOENT) 148 return (-1); 149 150 /* Get target-port-l0id, if present */ 151 err = nvlist_lookup_string(nvl, FM_FMRI_DEV_TGTPTLUN0, &tpl0id); 152 if (err != 0 && err != ENOENT) 153 return (-1); 154 155 /* There must be a device path present */ 156 err = nvlist_lookup_string(nvl, FM_FMRI_DEV_PATH, &devpath); 157 if (err != 0 || devpath == NULL) 158 return (-1); 159 160 /* 161 * dev:/// 162 * 163 * The dev scheme does not render fmri authority information 164 * in the string form of an fmri. It is meaningless to 165 * transmit a dev scheme fmri outside of the immediate fault 166 * manager. 167 */ 168 topo_fmristr_build(&size, 169 buf, buflen, FM_FMRI_SCHEME_DEV, NULL, ":///"); 170 171 /* device-id part, topo_fmristr_build does nothing if devid is NULL */ 172 topo_fmristr_build(&size, 173 buf, buflen, devid, ":" FM_FMRI_DEV_ID "=", NULL); 174 175 /* target-port-l0id part */ 176 topo_fmristr_build(&size, 177 buf, buflen, tpl0id, ":" FM_FMRI_DEV_TGTPTLUN0 "=", NULL); 178 179 /* 180 * device-path part; the devpath should always start with a / 181 * so you'd think we don't need to add a further / prefix here; 182 * however past implementation has always added the / if 183 * there is a devid component so we continue to do that 184 * so strings match exactly as before. So we can have: 185 * 186 * dev:////pci@0,0/... 187 * dev:///<devid-and-tpl0>//pci@0,0/... 188 * 189 * where <devid-and-tpl0> = 190 * [:devid=<devid>][:target-port-l0id=<tpl0>] 191 */ 192 topo_fmristr_build(&size, buf, buflen, devpath, 193 devid || tpl0id ? "/" : NULL, NULL); 194 195 return (size); 196 } 197 198 /*ARGSUSED*/ 199 static int 200 dev_fmri_nvl2str(topo_mod_t *mod, tnode_t *node, topo_version_t version, 201 nvlist_t *nvl, nvlist_t **out) 202 { 203 ssize_t len; 204 char *name = NULL; 205 nvlist_t *fmristr; 206 207 if (version > TOPO_METH_NVL2STR_VERSION) 208 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 209 210 if ((len = fmri_nvl2str(nvl, NULL, 0)) == 0 || 211 (name = topo_mod_alloc(mod, len + 1)) == NULL || 212 fmri_nvl2str(nvl, name, len + 1) == 0) { 213 if (name != NULL) 214 topo_mod_free(mod, name, len + 1); 215 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 216 } 217 218 if (topo_mod_nvalloc(mod, &fmristr, NV_UNIQUE_NAME) != 0) 219 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 220 if (nvlist_add_string(fmristr, "fmri-string", name) != 0) { 221 topo_mod_free(mod, name, len + 1); 222 nvlist_free(fmristr); 223 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 224 } 225 topo_mod_free(mod, name, len + 1); 226 *out = fmristr; 227 228 return (0); 229 } 230 231 /*ARGSUSED*/ 232 static int 233 dev_fmri_str2nvl(topo_mod_t *mod, tnode_t *node, topo_version_t version, 234 nvlist_t *in, nvlist_t **out) 235 { 236 char *cur, *devid = NULL, *tpl0id = NULL; 237 char *str, *strcp; 238 nvlist_t *fmri; 239 char *devpath; 240 size_t len; 241 int err; 242 243 if (version > TOPO_METH_STR2NVL_VERSION) 244 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 245 246 if (nvlist_lookup_string(in, "fmri-string", &str) != 0) 247 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 248 249 len = strlen(str); 250 251 /* 252 * We're expecting a string version of a dev scheme FMRI, and 253 * no fmri authority information. 254 * 255 * The shortest legal string would be "dev:////" (len 8) for a string 256 * with no FMRI auth info, no devid or target-port-l0id and 257 * an empty devpath string. 258 */ 259 if (len < 8 || strncmp(str, "dev:///", 7) != 0) 260 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 261 262 strcp = alloca(len + 1); 263 (void) memcpy(strcp, str, len); 264 strcp[len] = '\0'; 265 cur = strcp + 7; /* already parsed "dev:///" */ 266 267 /* 268 * If the first character after the "/" that terminates the (empty) 269 * fmri authority is a colon then we have devid and/or target-port-l0id 270 * info. They could be in either order. 271 * 272 * If not a colon then it must be the / that begins the devpath. 273 */ 274 if (*cur == ':') { 275 char *eos, *part[2]; 276 int i; 277 /* 278 * Look ahead to the "/" that starts the devpath. If not 279 * found or if straight after the : then we're busted. 280 */ 281 eos = devpath = strchr(cur, '/'); 282 if (devpath == NULL || devpath == cur + 1) 283 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 284 285 part[0] = ++cur; 286 287 /* 288 * Replace the initial "/" of the devpath with a NUL 289 * to terminate the string before it. We'll undo this 290 * before rendering devpath. 291 */ 292 *eos = '\0'; 293 294 /* 295 * We should now have a NUL-terminated string matching 296 * foo=<pat1>[:bar=<pat2>] (we stepped over the initial :) 297 * Look for a second colon; if found there must be space 298 * after it for the additional component, but no more colons. 299 */ 300 if ((part[1] = strchr(cur, ':')) != NULL) { 301 if (part[1] + 1 == eos || 302 strchr(part[1] + 1, ':') != NULL) 303 return (topo_mod_seterrno(mod, 304 EMOD_FMRI_MALFORM)); 305 *part[1] = '\0'; /* terminate part[0] */ 306 part[1]++; 307 } 308 309 for (i = 0; i < 2; i++) { 310 char *eq; 311 312 if (!part[i]) 313 continue; 314 315 if ((eq = strchr(part[i], '=')) == NULL || 316 *(eq + 1) == '\0') 317 return (topo_mod_seterrno(mod, 318 EMOD_FMRI_MALFORM)); 319 320 *eq = '\0'; 321 if (strcmp(part[i], FM_FMRI_DEV_ID) == 0) 322 devid = eq + 1; 323 else if (strcmp(part[i], FM_FMRI_DEV_TGTPTLUN0) == 0) 324 tpl0id = eq + 1; 325 else 326 return (topo_mod_seterrno(mod, 327 EMOD_FMRI_MALFORM)); 328 } 329 330 if (devid == NULL && tpl0id == NULL) 331 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 332 333 cur = devpath; /* initial slash is NULled */ 334 } else if (*cur != '/') { 335 /* the device-path should start with a slash */ 336 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 337 } else { 338 devpath = cur; 339 } 340 341 if (topo_mod_nvalloc(mod, &fmri, NV_UNIQUE_NAME) != 0) 342 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 343 344 err = nvlist_add_uint8(fmri, FM_VERSION, FM_DEV_SCHEME_VERSION); 345 err |= nvlist_add_string(fmri, FM_FMRI_SCHEME, FM_FMRI_SCHEME_DEV); 346 347 if (devid != NULL) 348 err |= nvlist_add_string(fmri, FM_FMRI_DEV_ID, devid); 349 350 if (tpl0id != NULL) 351 err |= nvlist_add_string(fmri, FM_FMRI_DEV_TGTPTLUN0, tpl0id); 352 353 if (devid != NULL || tpl0id != NULL) 354 *devpath = '/'; /* we NULed this earlier; put it back */ 355 356 /* step over repeated initial / in the devpath */ 357 while (*(devpath + 1) == '/') 358 devpath++; 359 360 err |= nvlist_add_string(fmri, FM_FMRI_DEV_PATH, devpath); 361 362 if (err != 0) { 363 nvlist_free(fmri); 364 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 365 } 366 367 *out = fmri; 368 369 return (0); 370 } 371 372 /*ARGSUSED*/ 373 static int 374 dev_fmri_present(topo_mod_t *mod, tnode_t *node, topo_version_t version, 375 nvlist_t *in, nvlist_t **out) 376 { 377 uint8_t fmversion; 378 char *devpath = NULL; 379 uint32_t present; 380 char *devid = NULL, *path; 381 ddi_devid_t id; 382 ddi_devid_t matchid; 383 di_node_t dnode; 384 struct stat sb; 385 int len; 386 387 if (version > TOPO_METH_PRESENT_VERSION) 388 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 389 390 if (nvlist_lookup_uint8(in, FM_VERSION, &fmversion) != 0 || 391 fmversion > FM_DEV_SCHEME_VERSION || 392 nvlist_lookup_string(in, FM_FMRI_DEV_PATH, &devpath) != 0) 393 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 394 395 (void) nvlist_lookup_string(in, FM_FMRI_DEV_ID, &devid); 396 397 if (devpath == NULL || strlen(devpath) == 0) 398 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 399 400 /* 401 * stat() the device node in devfs. This will tell us if the device is 402 * present or not. Don't stat the minor, just the whole device. 403 * If the device is present and there is a devid, it must also match. 404 * so di_init that one node. No need for DINFOFORCE. 405 */ 406 len = strlen(devpath) + strlen("/devices") + 1; 407 path = topo_mod_alloc(mod, len); 408 (void) snprintf(path, len, "/devices%s", devpath); 409 if (devid == NULL) { 410 if (stat(path, &sb) != -1) 411 present = 1; 412 else if ((dnode = di_init("/", DINFOCACHE)) == DI_NODE_NIL) 413 present = 0; 414 else { 415 if (di_lookup_node(dnode, devpath) == DI_NODE_NIL) 416 present = 0; 417 else 418 present = 1; 419 di_fini(dnode); 420 } 421 } else { 422 if (stat(path, &sb) == -1) 423 present = 0; 424 else if ((dnode = di_init(devpath, DINFOCPYONE)) == DI_NODE_NIL) 425 present = 0; 426 else { 427 if ((id = di_devid(dnode)) == NULL || 428 devid_str_decode(devid, &matchid, NULL) != 0) 429 present = 0; 430 else { 431 if (devid_compare(id, matchid) != 0) 432 present = 0; 433 else 434 present = 1; 435 devid_free(matchid); 436 } 437 di_fini(dnode); 438 } 439 } 440 topo_mod_free(mod, path, len); 441 442 if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0) 443 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 444 if (nvlist_add_uint32(*out, TOPO_METH_PRESENT_RET, present) != 0) { 445 nvlist_free(*out); 446 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 447 } 448 449 return (0); 450 } 451 452 /*ARGSUSED*/ 453 static int 454 dev_fmri_replaced(topo_mod_t *mod, tnode_t *node, topo_version_t version, 455 nvlist_t *in, nvlist_t **out) 456 { 457 uint8_t fmversion; 458 char *devpath = NULL; 459 uint32_t rval; 460 char *devid = NULL, *path; 461 ddi_devid_t id; 462 ddi_devid_t matchid; 463 di_node_t dnode; 464 struct stat sb; 465 int len; 466 467 if (version > TOPO_METH_REPLACED_VERSION) 468 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 469 470 if (nvlist_lookup_uint8(in, FM_VERSION, &fmversion) != 0 || 471 fmversion > FM_DEV_SCHEME_VERSION || 472 nvlist_lookup_string(in, FM_FMRI_DEV_PATH, &devpath) != 0) 473 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 474 475 (void) nvlist_lookup_string(in, FM_FMRI_DEV_ID, &devid); 476 477 if (devpath == NULL || strlen(devpath) == 0) 478 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 479 480 /* 481 * stat() the device node in devfs. This will tell us if the device is 482 * present or not. Don't stat the minor, just the whole device. 483 * If the device is present and there is a devid, it must also match. 484 * so di_init that one node. No need for DINFOFORCE. 485 */ 486 len = strlen(devpath) + strlen("/devices") + 1; 487 path = topo_mod_alloc(mod, len); 488 (void) snprintf(path, len, "/devices%s", devpath); 489 if (devid == NULL) { 490 if (stat(path, &sb) != -1) 491 rval = FMD_OBJ_STATE_UNKNOWN; 492 else if ((dnode = di_init("/", DINFOCACHE)) == DI_NODE_NIL) 493 rval = FMD_OBJ_STATE_UNKNOWN; 494 else { 495 if (di_lookup_node(dnode, devpath) == DI_NODE_NIL) 496 rval = FMD_OBJ_STATE_UNKNOWN; 497 else 498 rval = FMD_OBJ_STATE_UNKNOWN; 499 di_fini(dnode); 500 } 501 } else { 502 if (stat(path, &sb) == -1) 503 rval = FMD_OBJ_STATE_UNKNOWN; 504 else if ((dnode = di_init(devpath, DINFOCPYONE)) == DI_NODE_NIL) 505 rval = FMD_OBJ_STATE_UNKNOWN; 506 else { 507 if ((id = di_devid(dnode)) == NULL || 508 devid_str_decode(devid, &matchid, NULL) != 0) 509 rval = FMD_OBJ_STATE_UNKNOWN; 510 else { 511 if (devid_compare(id, matchid) != 0) 512 rval = FMD_OBJ_STATE_REPLACED; 513 else 514 rval = FMD_OBJ_STATE_STILL_PRESENT; 515 devid_free(matchid); 516 } 517 di_fini(dnode); 518 } 519 } 520 topo_mod_free(mod, path, len); 521 522 if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0) 523 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 524 if (nvlist_add_uint32(*out, TOPO_METH_REPLACED_RET, rval) != 0) { 525 nvlist_free(*out); 526 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 527 } 528 529 return (0); 530 } 531 532 /*ARGSUSED*/ 533 static int 534 dev_fmri_unusable(topo_mod_t *mod, tnode_t *node, topo_version_t version, 535 nvlist_t *in, nvlist_t **out) 536 { 537 di_node_t dnode; 538 uint8_t fmversion; 539 char *devpath = NULL; 540 uint32_t unusable; 541 uint_t state; 542 543 if (version > TOPO_METH_UNUSABLE_VERSION) 544 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 545 546 if (nvlist_lookup_uint8(in, FM_VERSION, &fmversion) != 0 || 547 fmversion > FM_DEV_SCHEME_VERSION || 548 nvlist_lookup_string(in, FM_FMRI_DEV_PATH, &devpath) != 0) 549 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 550 551 if (devpath == NULL) 552 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 553 554 if ((dnode = di_init(devpath, DINFOCPYONE)) == DI_NODE_NIL) { 555 if (errno != ENXIO) 556 return (topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM)); 557 unusable = 1; 558 } else { 559 uint_t retired = di_retired(dnode); 560 state = di_state(dnode); 561 if (retired || (state & (DI_DEVICE_OFFLINE | DI_DEVICE_DOWN | 562 DI_BUS_QUIESCED | DI_BUS_DOWN))) 563 unusable = 1; 564 else 565 unusable = 0; 566 di_fini(dnode); 567 } 568 569 if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0) 570 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 571 if (nvlist_add_uint32(*out, TOPO_METH_UNUSABLE_RET, unusable) != 0) { 572 nvlist_free(*out); 573 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 574 } 575 576 return (0); 577 } 578 579 /*ARGSUSED*/ 580 static int 581 dev_fmri_service_state(topo_mod_t *mod, tnode_t *node, topo_version_t version, 582 nvlist_t *in, nvlist_t **out) 583 { 584 di_node_t dnode; 585 uint8_t fmversion; 586 char *devpath = NULL; 587 uint32_t service_state; 588 uint_t state; 589 590 if (version > TOPO_METH_SERVICE_STATE_VERSION) 591 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 592 593 if (nvlist_lookup_uint8(in, FM_VERSION, &fmversion) != 0 || 594 fmversion > FM_DEV_SCHEME_VERSION || 595 nvlist_lookup_string(in, FM_FMRI_DEV_PATH, &devpath) != 0) 596 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 597 598 if (devpath == NULL) 599 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 600 601 if ((dnode = di_init(devpath, DINFOCPYONE)) == DI_NODE_NIL) { 602 if (errno != ENXIO) 603 return (topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM)); 604 service_state = FMD_SERVICE_STATE_UNUSABLE; 605 } else { 606 uint_t retired = di_retired(dnode); 607 state = di_state(dnode); 608 if (retired || (state & (DI_DEVICE_OFFLINE | DI_DEVICE_DOWN | 609 DI_BUS_QUIESCED | DI_BUS_DOWN))) 610 service_state = FMD_SERVICE_STATE_UNUSABLE; 611 else if (state & DI_DEVICE_DEGRADED) 612 service_state = FMD_SERVICE_STATE_DEGRADED; 613 else 614 service_state = FMD_SERVICE_STATE_OK; 615 di_fini(dnode); 616 } 617 618 if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0) 619 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 620 if (nvlist_add_uint32(*out, TOPO_METH_SERVICE_STATE_RET, 621 service_state) != 0) { 622 nvlist_free(*out); 623 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 624 } 625 626 return (0); 627 } 628 629 static nvlist_t * 630 dev_fmri_create(topo_mod_t *mp, const char *id, const char *path) 631 { 632 nvlist_t *out = NULL; 633 int e; 634 635 if (topo_mod_nvalloc(mp, &out, NV_UNIQUE_NAME) != 0) { 636 (void) topo_mod_seterrno(mp, EMOD_FMRI_NVL); 637 return (NULL); 638 } 639 e = nvlist_add_string(out, FM_FMRI_SCHEME, FM_FMRI_SCHEME_DEV); 640 e |= nvlist_add_uint8(out, FM_VERSION, FM_DEV_SCHEME_VERSION); 641 e |= nvlist_add_string(out, FM_FMRI_DEV_PATH, path); 642 643 if (id != NULL) 644 e |= nvlist_add_string(out, FM_FMRI_DEV_ID, id); 645 646 if (e == 0) 647 return (out); 648 649 topo_mod_dprintf(mp, "construction of dev nvl failed"); 650 (void) topo_mod_seterrno(mp, EMOD_FMRI_NVL); 651 nvlist_free(out); 652 return (NULL); 653 } 654 655 /*ARGSUSED*/ 656 static int 657 dev_fmri_create_meth(topo_mod_t *mp, tnode_t *node, topo_version_t version, 658 nvlist_t *in, nvlist_t **out) 659 { 660 nvlist_t *args = NULL; 661 char *path, *id = NULL; 662 663 if (version > TOPO_METH_FMRI_VERSION) 664 return (topo_mod_seterrno(mp, EMOD_VER_NEW)); 665 666 if (nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_NVL, &args) != 0 || 667 nvlist_lookup_string(args, FM_FMRI_DEV_PATH, &path) != 0) { 668 topo_mod_dprintf(mp, "no path string in method argument\n"); 669 return (topo_mod_seterrno(mp, EMOD_METHOD_INVAL)); 670 } 671 672 (void) nvlist_lookup_string(args, FM_FMRI_DEV_ID, &id); 673 674 if ((*out = dev_fmri_create(mp, id, path)) == NULL) 675 return (-1); 676 return (0); 677 } 678