1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <limits.h> 30 #include <strings.h> 31 #include <string.h> 32 #include <unistd.h> 33 #include <stdio.h> 34 #include <alloca.h> 35 #include <devid.h> 36 #include <sys/stat.h> 37 #include <libnvpair.h> 38 #include <fm/topo_mod.h> 39 #include <sys/fm/protocol.h> 40 41 #include <topo_method.h> 42 #include <topo_subr.h> 43 #include <dev.h> 44 45 static int dev_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t, 46 topo_instance_t, void *, void *); 47 static void dev_release(topo_mod_t *, tnode_t *); 48 static int dev_fmri_nvl2str(topo_mod_t *, tnode_t *, topo_version_t, 49 nvlist_t *, nvlist_t **); 50 static int dev_fmri_str2nvl(topo_mod_t *, tnode_t *, topo_version_t, 51 nvlist_t *, nvlist_t **); 52 static int dev_fmri_create_meth(topo_mod_t *, tnode_t *, topo_version_t, 53 nvlist_t *, nvlist_t **); 54 static int dev_fmri_present(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 59 static const topo_method_t dev_methods[] = { 60 { TOPO_METH_NVL2STR, TOPO_METH_NVL2STR_DESC, TOPO_METH_NVL2STR_VERSION, 61 TOPO_STABILITY_INTERNAL, dev_fmri_nvl2str }, 62 { TOPO_METH_STR2NVL, TOPO_METH_STR2NVL_DESC, TOPO_METH_STR2NVL_VERSION, 63 TOPO_STABILITY_INTERNAL, dev_fmri_str2nvl }, 64 { TOPO_METH_FMRI, TOPO_METH_FMRI_DESC, TOPO_METH_FMRI_VERSION, 65 TOPO_STABILITY_INTERNAL, dev_fmri_create_meth }, 66 { TOPO_METH_PRESENT, TOPO_METH_PRESENT_DESC, TOPO_METH_PRESENT_VERSION, 67 TOPO_STABILITY_INTERNAL, dev_fmri_present }, 68 { TOPO_METH_UNUSABLE, TOPO_METH_UNUSABLE_DESC, 69 TOPO_METH_UNUSABLE_VERSION, TOPO_STABILITY_INTERNAL, 70 dev_fmri_unusable }, 71 { NULL } 72 }; 73 74 static const topo_modops_t dev_ops = 75 { dev_enum, dev_release }; 76 static const topo_modinfo_t dev_info = 77 { "dev", FM_FMRI_SCHEME_DEV, DEV_VERSION, &dev_ops }; 78 79 int 80 dev_init(topo_mod_t *mod, topo_version_t version) 81 { 82 if (getenv("TOPOHCDEBUG")) 83 topo_mod_setdebug(mod); 84 topo_mod_dprintf(mod, "initializing dev builtin\n"); 85 86 if (version != DEV_VERSION) 87 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 88 89 if (topo_mod_register(mod, &dev_info, TOPO_VERSION) != 0) { 90 topo_mod_dprintf(mod, "failed to register dev_info: " 91 "%s\n", topo_mod_errmsg(mod)); 92 return (-1); 93 } 94 95 return (0); 96 } 97 98 void 99 dev_fini(topo_mod_t *mod) 100 { 101 topo_mod_unregister(mod); 102 } 103 104 /*ARGSUSED*/ 105 static int 106 dev_enum(topo_mod_t *mod, tnode_t *pnode, const char *name, 107 topo_instance_t min, topo_instance_t max, void *notused1, void *notused2) 108 { 109 (void) topo_method_register(mod, pnode, dev_methods); 110 return (0); 111 } 112 113 static void 114 dev_release(topo_mod_t *mod, tnode_t *node) 115 { 116 topo_method_unregister_all(mod, node); 117 } 118 119 static ssize_t 120 fmri_nvl2str(nvlist_t *nvl, char *buf, size_t buflen) 121 { 122 nvlist_t *anvl = NULL; 123 uint8_t version; 124 ssize_t size = 0; 125 char *devid = NULL; 126 char *devpath = NULL; 127 char *achas = NULL; 128 char *adom = NULL; 129 char *aprod = NULL; 130 char *asrvr = NULL; 131 char *ahost = NULL; 132 int more_auth = 0; 133 int err; 134 135 if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 || 136 version > FM_DEV_SCHEME_VERSION) 137 return (-1); 138 139 /* Get authority, if present */ 140 err = nvlist_lookup_nvlist(nvl, FM_FMRI_AUTHORITY, &anvl); 141 if (err != 0 && err != ENOENT) 142 return (-1); 143 144 /* Get devid, if present */ 145 err = nvlist_lookup_string(nvl, FM_FMRI_DEV_ID, &devid); 146 if (err != 0 && err != ENOENT) 147 return (-1); 148 149 /* There must be a device path present */ 150 err = nvlist_lookup_string(nvl, FM_FMRI_DEV_PATH, &devpath); 151 if (err != 0 || devpath == NULL) 152 return (-1); 153 154 if (anvl != NULL) { 155 (void) nvlist_lookup_string(anvl, 156 FM_FMRI_AUTH_PRODUCT, &aprod); 157 (void) nvlist_lookup_string(anvl, 158 FM_FMRI_AUTH_CHASSIS, &achas); 159 (void) nvlist_lookup_string(anvl, 160 FM_FMRI_AUTH_DOMAIN, &adom); 161 (void) nvlist_lookup_string(anvl, 162 FM_FMRI_AUTH_SERVER, &asrvr); 163 (void) nvlist_lookup_string(anvl, 164 FM_FMRI_AUTH_HOST, &ahost); 165 if (aprod != NULL) 166 more_auth++; 167 if (achas != NULL) 168 more_auth++; 169 if (adom != NULL) 170 more_auth++; 171 if (asrvr != NULL) 172 more_auth++; 173 if (ahost != NULL) 174 more_auth++; 175 } 176 177 /* dev:// */ 178 topo_fmristr_build(&size, 179 buf, buflen, FM_FMRI_SCHEME_DEV, NULL, "://"); 180 181 /* authority, if any */ 182 if (aprod != NULL) 183 topo_fmristr_build(&size, 184 buf, buflen, aprod, FM_FMRI_AUTH_PRODUCT "=", 185 --more_auth > 0 ? "," : NULL); 186 if (achas != NULL) 187 topo_fmristr_build(&size, 188 buf, buflen, achas, FM_FMRI_AUTH_CHASSIS "=", 189 --more_auth > 0 ? "," : NULL); 190 if (adom != NULL) 191 topo_fmristr_build(&size, 192 buf, buflen, adom, FM_FMRI_AUTH_DOMAIN "=", 193 --more_auth > 0 ? "," : NULL); 194 if (asrvr != NULL) 195 topo_fmristr_build(&size, 196 buf, buflen, asrvr, FM_FMRI_AUTH_SERVER "=", 197 --more_auth > 0 ? "," : NULL); 198 if (ahost != NULL) 199 topo_fmristr_build(&size, 200 buf, buflen, ahost, FM_FMRI_AUTH_HOST "=", 201 NULL); 202 203 /* device-id part, topo_fmristr_build does nothing if devid is NULL */ 204 topo_fmristr_build(&size, 205 buf, buflen, devid, "/:" FM_FMRI_DEV_ID "=", NULL); 206 207 /* device-path part */ 208 topo_fmristr_build(&size, buf, buflen, devpath, "/", NULL); 209 210 return (size); 211 } 212 213 /*ARGSUSED*/ 214 static int 215 dev_fmri_nvl2str(topo_mod_t *mod, tnode_t *node, topo_version_t version, 216 nvlist_t *nvl, nvlist_t **out) 217 { 218 ssize_t len; 219 char *name = NULL; 220 nvlist_t *fmristr; 221 222 if (version > TOPO_METH_NVL2STR_VERSION) 223 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 224 225 if ((len = fmri_nvl2str(nvl, NULL, 0)) == 0 || 226 (name = topo_mod_alloc(mod, len + 1)) == NULL || 227 fmri_nvl2str(nvl, name, len + 1) == 0) { 228 if (name != NULL) 229 topo_mod_free(mod, name, len + 1); 230 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 231 } 232 233 if (topo_mod_nvalloc(mod, &fmristr, NV_UNIQUE_NAME) != 0) 234 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 235 if (nvlist_add_string(fmristr, "fmri-string", name) != 0) { 236 topo_mod_free(mod, name, len + 1); 237 nvlist_free(fmristr); 238 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 239 } 240 topo_mod_free(mod, name, len + 1); 241 *out = fmristr; 242 243 return (0); 244 } 245 246 /*ARGSUSED*/ 247 static int 248 dev_fmri_str2nvl(topo_mod_t *mod, tnode_t *node, topo_version_t version, 249 nvlist_t *in, nvlist_t **out) 250 { 251 nvlist_t *fmri; 252 char *devpath; 253 char *devid = NULL; 254 char *str; 255 int err; 256 257 if (version > TOPO_METH_STR2NVL_VERSION) 258 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 259 260 if (nvlist_lookup_string(in, "fmri-string", &str) != 0) 261 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 262 263 /* We're expecting a string version of a dev scheme FMRI */ 264 if (strncmp(str, "dev:///", 7) != 0) 265 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 266 267 devpath = str + 7; 268 if (strncmp(devpath, ":" FM_FMRI_DEV_ID "=", 7) == 0) { 269 char *n; 270 int len; 271 272 n = strchr(devpath + 7, '/'); 273 if (n == NULL) 274 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 275 len = n - (devpath + 7); 276 devid = alloca(len + 1); 277 (void) memcpy(devid, devpath + 7, len); 278 devid[len] = 0; 279 devpath = n + 1; 280 } 281 282 /* the device-path should start with a slash */ 283 if (*devpath != '/') 284 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 285 286 if (topo_mod_nvalloc(mod, &fmri, NV_UNIQUE_NAME) != 0) 287 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 288 289 err = nvlist_add_uint8(fmri, FM_VERSION, FM_DEV_SCHEME_VERSION); 290 err |= nvlist_add_string(fmri, FM_FMRI_SCHEME, FM_FMRI_SCHEME_DEV); 291 err |= nvlist_add_string(fmri, FM_FMRI_DEV_PATH, devpath); 292 if (devid != NULL) 293 err |= nvlist_add_string(fmri, FM_FMRI_DEV_ID, devid); 294 295 if (err != 0) { 296 nvlist_free(fmri); 297 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 298 } 299 *out = fmri; 300 301 return (0); 302 } 303 304 /*ARGSUSED*/ 305 static int 306 dev_fmri_present(topo_mod_t *mod, tnode_t *node, topo_version_t version, 307 nvlist_t *in, nvlist_t **out) 308 { 309 uint8_t fmversion; 310 char *devpath = NULL; 311 uint32_t present; 312 char *devid = NULL, *path; 313 ddi_devid_t id; 314 ddi_devid_t matchid; 315 di_node_t dnode; 316 struct stat sb; 317 int len; 318 319 if (version > TOPO_METH_PRESENT_VERSION) 320 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 321 322 if (nvlist_lookup_uint8(in, FM_VERSION, &fmversion) != 0 || 323 fmversion > FM_DEV_SCHEME_VERSION || 324 nvlist_lookup_string(in, FM_FMRI_DEV_PATH, &devpath) != 0) 325 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 326 327 (void) nvlist_lookup_string(in, FM_FMRI_DEV_ID, &devid); 328 329 if (devpath == NULL || strlen(devpath) == 0) 330 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 331 332 /* 333 * stat() the device node in devfs. This will tell us if the device is 334 * present or not. Don't stat the minor, just the whole device. 335 * If the device is present and there is a devid, it must also match. 336 * so di_init that one node. No need for DINFOFORCE. 337 */ 338 len = strlen(devpath) + strlen("/devices") + 1; 339 path = topo_mod_alloc(mod, len); 340 (void) snprintf(path, len, "/devices%s", devpath); 341 if (devid == NULL) { 342 if (stat(path, &sb) != -1) 343 present = 1; 344 else if ((dnode = di_init("/", DINFOCACHE)) == DI_NODE_NIL) 345 present = 0; 346 else { 347 if (di_lookup_node(dnode, devpath) == DI_NODE_NIL) 348 present = 0; 349 else 350 present = 1; 351 di_fini(dnode); 352 } 353 } else { 354 if (stat(path, &sb) == -1) 355 present = 0; 356 else if ((dnode = di_init(devpath, DINFOCPYONE)) == DI_NODE_NIL) 357 present = 0; 358 else { 359 if ((id = di_devid(dnode)) == NULL || 360 devid_str_decode(devid, &matchid, NULL) != 0) 361 present = 0; 362 else { 363 if (devid_compare(id, matchid) != 0) 364 present = 0; 365 else 366 present = 1; 367 devid_free(matchid); 368 } 369 di_fini(dnode); 370 } 371 } 372 topo_mod_free(mod, path, len); 373 374 if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0) 375 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 376 if (nvlist_add_uint32(*out, TOPO_METH_PRESENT_RET, present) != 0) { 377 nvlist_free(*out); 378 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 379 } 380 381 return (0); 382 } 383 384 /*ARGSUSED*/ 385 static int 386 dev_fmri_unusable(topo_mod_t *mod, tnode_t *node, topo_version_t version, 387 nvlist_t *in, nvlist_t **out) 388 { 389 di_node_t dnode; 390 uint8_t fmversion; 391 char *devpath = NULL; 392 uint32_t unusable; 393 uint_t state; 394 395 if (version > TOPO_METH_PRESENT_VERSION) 396 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 397 398 if (nvlist_lookup_uint8(in, FM_VERSION, &fmversion) != 0 || 399 fmversion > FM_DEV_SCHEME_VERSION || 400 nvlist_lookup_string(in, FM_FMRI_DEV_PATH, &devpath) != 0) 401 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 402 403 if (devpath == NULL) 404 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 405 406 if ((dnode = di_init(devpath, DINFOCPYONE)) == DI_NODE_NIL) { 407 if (errno != ENXIO) 408 return (topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM)); 409 unusable = 1; 410 } else { 411 uint_t retired = di_retired(dnode); 412 state = di_state(dnode); 413 if (retired || (state & (DI_DEVICE_OFFLINE | DI_DEVICE_DOWN | 414 DI_BUS_QUIESCED | DI_BUS_DOWN))) 415 unusable = 1; 416 else 417 unusable = 0; 418 di_fini(dnode); 419 } 420 421 if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0) 422 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 423 if (nvlist_add_uint32(*out, TOPO_METH_UNUSABLE_RET, unusable) != 0) { 424 nvlist_free(*out); 425 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 426 } 427 428 return (0); 429 } 430 431 static nvlist_t * 432 dev_fmri_create(topo_mod_t *mp, const char *id, const char *path) 433 { 434 nvlist_t *out = NULL; 435 int e; 436 437 if (topo_mod_nvalloc(mp, &out, NV_UNIQUE_NAME) != 0) { 438 (void) topo_mod_seterrno(mp, EMOD_FMRI_NVL); 439 return (NULL); 440 } 441 e = nvlist_add_string(out, FM_FMRI_SCHEME, FM_FMRI_SCHEME_DEV); 442 e |= nvlist_add_uint8(out, FM_VERSION, FM_DEV_SCHEME_VERSION); 443 e |= nvlist_add_string(out, FM_FMRI_DEV_PATH, path); 444 445 if (id != NULL) 446 e |= nvlist_add_string(out, FM_FMRI_DEV_ID, id); 447 448 if (e == 0) 449 return (out); 450 451 topo_mod_dprintf(mp, "construction of dev nvl failed"); 452 (void) topo_mod_seterrno(mp, EMOD_FMRI_NVL); 453 nvlist_free(out); 454 return (NULL); 455 } 456 457 /*ARGSUSED*/ 458 static int 459 dev_fmri_create_meth(topo_mod_t *mp, tnode_t *node, topo_version_t version, 460 nvlist_t *in, nvlist_t **out) 461 { 462 nvlist_t *args = NULL; 463 char *path, *id = NULL; 464 465 if (version > TOPO_METH_FMRI_VERSION) 466 return (topo_mod_seterrno(mp, EMOD_VER_NEW)); 467 468 if (nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_NVL, &args) != 0 || 469 nvlist_lookup_string(args, FM_FMRI_DEV_PATH, &path) != 0) { 470 topo_mod_dprintf(mp, "no path string in method argument\n"); 471 return (topo_mod_seterrno(mp, EMOD_METHOD_INVAL)); 472 } 473 474 (void) nvlist_lookup_string(args, FM_FMRI_DEV_ID, &id); 475 476 if ((*out = dev_fmri_create(mp, id, path)) == NULL) 477 return (-1); 478 return (0); 479 } 480