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) 2010, Oracle and/or its affiliates. All rights reserved. 24 */ 25 26 #include <libnvpair.h> 27 #include <fm/topo_mod.h> 28 29 #include <sys/fm/protocol.h> 30 #include <sys/types.h> 31 32 #include <topo_method.h> 33 #include <topo_subr.h> 34 #include <sw.h> 35 36 static int sw_fmri_nvl2str(topo_mod_t *, tnode_t *, topo_version_t, 37 nvlist_t *, nvlist_t **); 38 static int sw_fmri_create(topo_mod_t *, tnode_t *, topo_version_t, 39 nvlist_t *, nvlist_t **); 40 41 static const topo_method_t sw_methods[] = { 42 { TOPO_METH_NVL2STR, TOPO_METH_NVL2STR_DESC, TOPO_METH_NVL2STR_VERSION, 43 TOPO_STABILITY_INTERNAL, sw_fmri_nvl2str }, 44 { TOPO_METH_FMRI, TOPO_METH_FMRI_DESC, TOPO_METH_FMRI_VERSION, 45 TOPO_STABILITY_INTERNAL, sw_fmri_create }, 46 { NULL } 47 }; 48 49 static int sw_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t, 50 topo_instance_t, void *, void *); 51 static void sw_release(topo_mod_t *, tnode_t *); 52 53 static const topo_modops_t sw_ops = 54 { sw_enum, sw_release }; 55 56 static const topo_modinfo_t sw_info = 57 { "sw", FM_FMRI_SCHEME_SW, SW_VERSION, &sw_ops }; 58 59 int 60 sw_init(topo_mod_t *mod, topo_version_t version) 61 { 62 if (getenv("TOPOSWDEBUG")) 63 topo_mod_setdebug(mod); 64 topo_mod_dprintf(mod, "initializing sw builtin\n"); 65 66 if (version != SW_VERSION) 67 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 68 69 if (topo_mod_register(mod, &sw_info, TOPO_VERSION) != 0) { 70 topo_mod_dprintf(mod, "failed to register sw_info: " 71 "%s\n", topo_mod_errmsg(mod)); 72 return (-1); 73 } 74 75 return (0); 76 } 77 78 void 79 sw_fini(topo_mod_t *mod) 80 { 81 topo_mod_unregister(mod); 82 } 83 84 static int 85 sw_get_optl_string(nvlist_t *nvl, char *name, char **dest) 86 { 87 if (nvlist_lookup_string(nvl, name, dest) == 0) { 88 return (0); 89 } else { 90 *dest = NULL; 91 return (errno == ENOENT ? 0 : 1); 92 } 93 } 94 95 static int 96 sw_get_optl_int64(nvlist_t *nvl, char *name, int64_t *dest) 97 { 98 if (nvlist_lookup_int64(nvl, name, dest) == 0) { 99 return (0); 100 } else { 101 *dest = -1; 102 return (errno == ENOENT ? 0 : 1); 103 } 104 } 105 106 static int 107 sw_get_optl_nvlist(nvlist_t *nvl, char *name, nvlist_t **dest) 108 { 109 if (nvlist_lookup_nvlist(nvl, name, dest) == 0) { 110 return (0); 111 } else { 112 *dest = NULL; 113 return (errno == ENOENT ? 0 : 1); 114 } 115 } 116 117 static int 118 sw_add_optl_string(nvlist_t *nvl, char *name, char *val) 119 { 120 if (val) 121 return (nvlist_add_string(nvl, name, val) != 0); 122 else 123 return (0); 124 } 125 126 /*ARGSUSED*/ 127 static int 128 sw_fmri_create(topo_mod_t *mod, tnode_t *node, topo_version_t version, 129 nvlist_t *in, nvlist_t **out) 130 { 131 nvlist_t *args, *fmri = NULL, *obj = NULL, *site = NULL, *ctxt = NULL; 132 topo_mod_errno_t moderr; 133 int err = 0; 134 135 char *obj_path, *obj_root; 136 nvlist_t *obj_pkg; 137 138 char *site_token, *site_module, *site_file, *site_func; 139 int64_t site_line; 140 141 char *ctxt_origin, *ctxt_execname, *ctxt_zone; 142 int64_t ctxt_pid, ctxt_ctid; 143 char **ctxt_stack; 144 uint_t ctxt_stackdepth; 145 146 147 if (version > TOPO_METH_FMRI_VERSION) 148 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 149 150 if (nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_NVL, &args) != 0) 151 return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL)); 152 153 if (nvlist_lookup_string(args, "obj_path", &obj_path) != 0) 154 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 155 err |= sw_get_optl_string(args, "obj_root", &obj_root); 156 err |= sw_get_optl_nvlist(args, "obj-pkg", &obj_pkg); 157 158 err |= sw_get_optl_string(args, "site_token", &site_token); 159 err |= sw_get_optl_string(args, "site_module", &site_module); 160 err |= sw_get_optl_string(args, "site_file", &site_file); 161 err |= sw_get_optl_string(args, "site_func", &site_func); 162 err |= sw_get_optl_int64(args, "site_line", &site_line); 163 164 err |= sw_get_optl_string(args, "ctxt_origin", &ctxt_origin); 165 err |= sw_get_optl_string(args, "ctxt_execname", &ctxt_execname); 166 err |= sw_get_optl_string(args, "ctxt_zone", &ctxt_zone); 167 err |= sw_get_optl_int64(args, "ctxt_pid", &ctxt_pid); 168 err |= sw_get_optl_int64(args, "ctxt_ctid", &ctxt_ctid); 169 170 if (nvlist_lookup_string_array(args, "stack", &ctxt_stack, 171 &ctxt_stackdepth) != 0) { 172 if (errno == ENOENT) 173 ctxt_stack = NULL; 174 else 175 err++; 176 } 177 178 if (err) 179 (void) topo_mod_seterrno(mod, EMOD_FMRI_NVL); 180 181 if (topo_mod_nvalloc(mod, &fmri, NV_UNIQUE_NAME) != 0 || 182 topo_mod_nvalloc(mod, &obj, NV_UNIQUE_NAME) != 0) { 183 moderr = EMOD_NOMEM; 184 goto out; 185 } 186 187 /* 188 * Add standard FMRI members 'version' and 'scheme'. 189 */ 190 err |= nvlist_add_uint8(fmri, FM_VERSION, FM_SW_SCHEME_VERSION); 191 err |= nvlist_add_string(fmri, FM_FMRI_SCHEME, FM_FMRI_SCHEME_SW); 192 193 /* 194 * Build up the 'object' nvlist. 195 */ 196 err |= nvlist_add_string(obj, FM_FMRI_SW_OBJ_PATH, obj_path); 197 err |= sw_add_optl_string(obj, FM_FMRI_SW_OBJ_ROOT, obj_root); 198 if (obj_pkg) 199 err |= nvlist_add_nvlist(obj, FM_FMRI_SW_OBJ_PKG, obj_pkg); 200 201 /* 202 * Add 'object' to the fmri. 203 */ 204 if (err == 0) 205 err |= nvlist_add_nvlist(fmri, FM_FMRI_SW_OBJ, obj); 206 207 if (err) { 208 moderr = EMOD_NOMEM; 209 goto out; 210 } 211 212 /* 213 * Do we have anything for a 'site' nvlist? 214 */ 215 if (site_token == NULL && site_module == NULL && site_file == NULL && 216 site_func == NULL && site_line == -1) 217 goto context; 218 219 /* 220 * Allocate and build 'site' nvlist. 221 */ 222 if (topo_mod_nvalloc(mod, &site, NV_UNIQUE_NAME) != 0) { 223 moderr = EMOD_NOMEM; 224 goto out; 225 } 226 227 err |= sw_add_optl_string(site, FM_FMRI_SW_SITE_TOKEN, site_token); 228 err |= sw_add_optl_string(site, FM_FMRI_SW_SITE_MODULE, site_module); 229 err |= sw_add_optl_string(site, FM_FMRI_SW_SITE_FILE, site_file); 230 err |= sw_add_optl_string(site, FM_FMRI_SW_SITE_FUNC, site_func); 231 if ((site_token || site_module || site_file || site_func) && 232 site_line != -1) 233 err |= nvlist_add_int64(site, FM_FMRI_SW_SITE_LINE, site_line); 234 235 /* 236 * Add 'site' to the fmri. 237 */ 238 if (err == 0) 239 err |= nvlist_add_nvlist(fmri, FM_FMRI_SW_SITE, site); 240 241 if (err) { 242 moderr = EMOD_NOMEM; 243 goto out; 244 } 245 246 context: 247 /* 248 * Do we have anything for a 'context' nvlist? 249 */ 250 if (ctxt_origin || ctxt_execname || ctxt_zone || 251 ctxt_pid != -1 || ctxt_ctid != -1 || ctxt_stack != NULL) 252 goto out; 253 254 /* 255 * Allocate and build 'context' nvlist. 256 */ 257 if (topo_mod_nvalloc(mod, &ctxt, NV_UNIQUE_NAME) != 0) { 258 moderr = EMOD_NOMEM; 259 goto out; 260 } 261 262 err |= sw_add_optl_string(ctxt, FM_FMRI_SW_CTXT_ORIGIN, ctxt_origin); 263 err |= sw_add_optl_string(ctxt, FM_FMRI_SW_CTXT_EXECNAME, 264 ctxt_execname); 265 err |= sw_add_optl_string(ctxt, FM_FMRI_SW_CTXT_ZONE, ctxt_zone); 266 if (ctxt_pid != -1) 267 err |= nvlist_add_int64(ctxt, FM_FMRI_SW_CTXT_PID, ctxt_pid); 268 if (ctxt_ctid != -1) 269 err |= nvlist_add_int64(ctxt, FM_FMRI_SW_CTXT_CTID, ctxt_ctid); 270 if (ctxt_stack != NULL) 271 err |= nvlist_add_string_array(ctxt, FM_FMRI_SW_CTXT_STACK, 272 ctxt_stack, ctxt_stackdepth); 273 274 /* 275 * Add 'context' to the fmri. 276 */ 277 if (err == 0) 278 err |= nvlist_add_nvlist(fmri, FM_FMRI_SW_CTXT, ctxt); 279 280 moderr = err ? EMOD_NOMEM : 0; 281 out: 282 if (moderr == 0) 283 *out = fmri; 284 285 if (moderr != 0 && fmri) 286 nvlist_free(fmri); 287 288 if (obj) 289 nvlist_free(obj); 290 291 if (site) 292 nvlist_free(site); 293 294 if (ctxt) 295 nvlist_free(ctxt); 296 297 return (moderr == 0 ? 0 : topo_mod_seterrno(mod, moderr)); 298 } 299 300 301 /*ARGSUSED*/ 302 static int 303 sw_enum(topo_mod_t *mod, tnode_t *pnode, const char *name, 304 topo_instance_t min, topo_instance_t max, void *notused1, void *notused2) 305 { 306 (void) topo_method_register(mod, pnode, sw_methods); 307 return (0); 308 } 309 310 static void 311 sw_release(topo_mod_t *mod, tnode_t *node) 312 { 313 topo_method_unregister_all(mod, node); 314 } 315 316 /* 317 * Lookup a string in an nvlist. Possible return values: 318 * if 'required' is B_TRUE: 319 * 1 = found 320 * 0 = not found 321 * if 'required' is B_FALSE: 322 * 1 = found 323 * 0 = not found, but some error other than ENOENT encountered 324 * -1 = not found, with ENOENT 325 * 326 * So 0 is an error condition in both cases. 327 * 328 * In all "not found" cases, *valp is NULLed. 329 */ 330 static int 331 lookup_string(nvlist_t *nvl, char *name, char **valp, boolean_t required) 332 { 333 int err; 334 335 err = nvlist_lookup_string(nvl, name, valp); 336 337 /* 338 * A return value of 1 always means "found" 339 */ 340 if (err == 0) 341 return (1); 342 343 /* 344 * Failure to lookup for whatever reason NULLs valp 345 */ 346 *valp = NULL; 347 348 /* 349 * Return 0 if not found but required, or optional but some error 350 * other than ENOENT was returned. 351 */ 352 if (required == B_TRUE || err != ENOENT) 353 return (0); 354 355 /* 356 * Return -1 if not found but was optional (and got ENOENT). 357 */ 358 return (-1); 359 } 360 361 /*ARGSUSED*/ 362 static int 363 sw_fmri_nvl2str(topo_mod_t *mod, tnode_t *node, topo_version_t version, 364 nvlist_t *nvl, nvlist_t **out) 365 { 366 nvlist_t *object, *site = NULL, *anvl = NULL; 367 char *file, *func, *token; 368 uint8_t scheme_version; 369 char *path, *root; 370 nvlist_t *fmristr; 371 size_t buflen = 0; 372 int linevalid = 0; 373 char *buf = NULL; 374 ssize_t size = 0; 375 char linebuf[32]; 376 int64_t line; 377 int pass; 378 int err; 379 380 if (version > TOPO_METH_NVL2STR_VERSION) 381 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 382 383 if (nvlist_lookup_uint8(nvl, FM_VERSION, &scheme_version) != 0 || 384 scheme_version > FM_SW_SCHEME_VERSION) 385 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 386 387 /* Get authority, if present */ 388 err = nvlist_lookup_nvlist(nvl, FM_FMRI_AUTHORITY, &anvl); 389 if (err != 0 && err != ENOENT) 390 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 391 392 /* 393 * The 'object' nvlist is required. It must include the path, 394 * but the root is optional. 395 */ 396 if (nvlist_lookup_nvlist(nvl, FM_FMRI_SW_OBJ, &object) != 0 || 397 !lookup_string(object, FM_FMRI_SW_OBJ_PATH, &path, B_TRUE) || 398 !lookup_string(object, FM_FMRI_SW_OBJ_ROOT, &root, B_FALSE)) 399 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 400 401 /* The 'site' nvlist is optional */ 402 file = func = token = NULL; 403 linevalid = 0; 404 if ((err = nvlist_lookup_nvlist(nvl, FM_FMRI_SW_SITE, &site)) == 0) { 405 /* 406 * Prefer 'token' to file/func/line 407 */ 408 if (lookup_string(site, FM_FMRI_SW_SITE_TOKEN, &token, 409 B_FALSE) <= 0) { 410 /* 411 * If no token then try file, func, line - but 412 * func and line are meaningless without file. 413 */ 414 if (lookup_string(site, FM_FMRI_SW_SITE_FILE, 415 &file, B_FALSE) == 1) { 416 (void) lookup_string(site, FM_FMRI_SW_SITE_FUNC, 417 &func, B_FALSE); 418 if (nvlist_lookup_int64(site, 419 FM_FMRI_SW_SITE_LINE, &line) == 0) 420 linevalid = 1; 421 } 422 } 423 } else if (err != ENOENT) { 424 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 425 } 426 427 /* On the first pass buf is NULL and size and buflen are 0 */ 428 pass = 1; 429 again: 430 /* 431 * sw://[<authority>]/ 432 * [:root=<object.root] 433 * :path=<object.path> 434 * [#<fragment-identifier>] 435 * 436 * <fragment-identifier> is one of 437 * 438 * :token=<site.token> 439 * or 440 * :file=<site.file>[:func=<site.func>][:line=<site.line>] 441 */ 442 443 /* sw:// */ 444 topo_fmristr_build(&size, buf, buflen, FM_FMRI_SCHEME_SW, 445 NULL, "://"); 446 447 /* authority, if any */ 448 if (anvl != NULL) { 449 nvpair_t *apair; 450 char *aname, *aval; 451 452 for (apair = nvlist_next_nvpair(anvl, NULL); 453 apair != NULL; apair = nvlist_next_nvpair(anvl, apair)) { 454 if (nvpair_type(apair) != DATA_TYPE_STRING || 455 nvpair_value_string(apair, &aval) != 0) 456 continue; 457 aname = nvpair_name(apair); 458 topo_fmristr_build(&size, buf, buflen, ":", NULL, NULL); 459 topo_fmristr_build(&size, buf, buflen, "=", 460 aname, aval); 461 } 462 } 463 464 /* separating slash */ 465 topo_fmristr_build(&size, buf, buflen, "/", NULL, NULL); 466 467 /* :root=... */ 468 if (root) { 469 topo_fmristr_build(&size, buf, buflen, root, 470 ":" FM_FMRI_SW_OBJ_ROOT "=", NULL); 471 } 472 473 /* :path=... */ 474 topo_fmristr_build(&size, buf, buflen, path, 475 ":" FM_FMRI_SW_OBJ_PATH "=", NULL); 476 477 if (token) { 478 /* #:token=... */ 479 topo_fmristr_build(&size, buf, buflen, token, 480 "#:" FM_FMRI_SW_SITE_TOKEN "=", NULL); 481 } else if (file) { 482 /* #:file=... */ 483 topo_fmristr_build(&size, buf, buflen, file, 484 "#:" FM_FMRI_SW_SITE_FILE "=", NULL); 485 486 /* :func=... */ 487 if (func) { 488 topo_fmristr_build(&size, buf, buflen, func, 489 ":" FM_FMRI_SW_SITE_FUNC "=", NULL); 490 } 491 492 /* :line=... */ 493 if (linevalid) { 494 if (pass == 1) 495 (void) snprintf(linebuf, sizeof (linebuf), 496 "%lld", line); 497 498 topo_fmristr_build(&size, buf, buflen, linebuf, 499 ":" FM_FMRI_SW_SITE_LINE "=", NULL); 500 } 501 } 502 503 if (buf == NULL) { 504 if ((buf = topo_mod_alloc(mod, size + 1)) == NULL) 505 return (topo_mod_seterrno(mod, EMOD_NOMEM)); 506 507 buflen = size + 1; 508 size = 0; 509 pass = 2; 510 goto again; 511 } 512 513 /* 514 * Construct the nvlist to return as the result. 515 */ 516 if (topo_mod_nvalloc(mod, &fmristr, NV_UNIQUE_NAME) != 0) { 517 topo_mod_strfree(mod, buf); 518 return (topo_mod_seterrno(mod, EMOD_NOMEM)); 519 } 520 521 if (nvlist_add_string(fmristr, "fmri-string", buf) != 0) { 522 topo_mod_strfree(mod, buf); 523 nvlist_free(fmristr); 524 return (topo_mod_seterrno(mod, EMOD_NOMEM)); 525 } 526 topo_mod_strfree(mod, buf); 527 *out = fmristr; 528 529 return (0); 530 } 531