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 else 285 nvlist_free(fmri); 286 287 nvlist_free(obj); 288 nvlist_free(site); 289 nvlist_free(ctxt); 290 291 return (moderr == 0 ? 0 : topo_mod_seterrno(mod, moderr)); 292 } 293 294 295 /*ARGSUSED*/ 296 static int 297 sw_enum(topo_mod_t *mod, tnode_t *pnode, const char *name, 298 topo_instance_t min, topo_instance_t max, void *notused1, void *notused2) 299 { 300 (void) topo_method_register(mod, pnode, sw_methods); 301 return (0); 302 } 303 304 static void 305 sw_release(topo_mod_t *mod, tnode_t *node) 306 { 307 topo_method_unregister_all(mod, node); 308 } 309 310 /* 311 * Lookup a string in an nvlist. Possible return values: 312 * if 'required' is B_TRUE: 313 * 1 = found 314 * 0 = not found 315 * if 'required' is B_FALSE: 316 * 1 = found 317 * 0 = not found, but some error other than ENOENT encountered 318 * -1 = not found, with ENOENT 319 * 320 * So 0 is an error condition in both cases. 321 * 322 * In all "not found" cases, *valp is NULLed. 323 */ 324 static int 325 lookup_string(nvlist_t *nvl, char *name, char **valp, boolean_t required) 326 { 327 int err; 328 329 err = nvlist_lookup_string(nvl, name, valp); 330 331 /* 332 * A return value of 1 always means "found" 333 */ 334 if (err == 0) 335 return (1); 336 337 /* 338 * Failure to lookup for whatever reason NULLs valp 339 */ 340 *valp = NULL; 341 342 /* 343 * Return 0 if not found but required, or optional but some error 344 * other than ENOENT was returned. 345 */ 346 if (required == B_TRUE || err != ENOENT) 347 return (0); 348 349 /* 350 * Return -1 if not found but was optional (and got ENOENT). 351 */ 352 return (-1); 353 } 354 355 /*ARGSUSED*/ 356 static int 357 sw_fmri_nvl2str(topo_mod_t *mod, tnode_t *node, topo_version_t version, 358 nvlist_t *nvl, nvlist_t **out) 359 { 360 nvlist_t *object, *site = NULL, *anvl = NULL; 361 char *file, *func, *token; 362 uint8_t scheme_version; 363 char *path, *root; 364 nvlist_t *fmristr; 365 size_t buflen = 0; 366 int linevalid = 0; 367 char *buf = NULL; 368 ssize_t size = 0; 369 char linebuf[32]; 370 int64_t line; 371 int pass; 372 int err; 373 374 if (version > TOPO_METH_NVL2STR_VERSION) 375 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 376 377 if (nvlist_lookup_uint8(nvl, FM_VERSION, &scheme_version) != 0 || 378 scheme_version > FM_SW_SCHEME_VERSION) 379 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 380 381 /* Get authority, if present */ 382 err = nvlist_lookup_nvlist(nvl, FM_FMRI_AUTHORITY, &anvl); 383 if (err != 0 && err != ENOENT) 384 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 385 386 /* 387 * The 'object' nvlist is required. It must include the path, 388 * but the root is optional. 389 */ 390 if (nvlist_lookup_nvlist(nvl, FM_FMRI_SW_OBJ, &object) != 0 || 391 !lookup_string(object, FM_FMRI_SW_OBJ_PATH, &path, B_TRUE) || 392 !lookup_string(object, FM_FMRI_SW_OBJ_ROOT, &root, B_FALSE)) 393 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 394 395 /* The 'site' nvlist is optional */ 396 file = func = token = NULL; 397 linevalid = 0; 398 if ((err = nvlist_lookup_nvlist(nvl, FM_FMRI_SW_SITE, &site)) == 0) { 399 /* 400 * Prefer 'token' to file/func/line 401 */ 402 if (lookup_string(site, FM_FMRI_SW_SITE_TOKEN, &token, 403 B_FALSE) <= 0) { 404 /* 405 * If no token then try file, func, line - but 406 * func and line are meaningless without file. 407 */ 408 if (lookup_string(site, FM_FMRI_SW_SITE_FILE, 409 &file, B_FALSE) == 1) { 410 (void) lookup_string(site, FM_FMRI_SW_SITE_FUNC, 411 &func, B_FALSE); 412 if (nvlist_lookup_int64(site, 413 FM_FMRI_SW_SITE_LINE, &line) == 0) 414 linevalid = 1; 415 } 416 } 417 } else if (err != ENOENT) { 418 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 419 } 420 421 /* On the first pass buf is NULL and size and buflen are 0 */ 422 pass = 1; 423 again: 424 /* 425 * sw://[<authority>]/ 426 * [:root=<object.root] 427 * :path=<object.path> 428 * [#<fragment-identifier>] 429 * 430 * <fragment-identifier> is one of 431 * 432 * :token=<site.token> 433 * or 434 * :file=<site.file>[:func=<site.func>][:line=<site.line>] 435 */ 436 437 /* sw:// */ 438 topo_fmristr_build(&size, buf, buflen, FM_FMRI_SCHEME_SW, 439 NULL, "://"); 440 441 /* authority, if any */ 442 if (anvl != NULL) { 443 nvpair_t *apair; 444 char *aname, *aval; 445 446 for (apair = nvlist_next_nvpair(anvl, NULL); 447 apair != NULL; apair = nvlist_next_nvpair(anvl, apair)) { 448 if (nvpair_type(apair) != DATA_TYPE_STRING || 449 nvpair_value_string(apair, &aval) != 0) 450 continue; 451 aname = nvpair_name(apair); 452 topo_fmristr_build(&size, buf, buflen, ":", NULL, NULL); 453 topo_fmristr_build(&size, buf, buflen, "=", 454 aname, aval); 455 } 456 } 457 458 /* separating slash */ 459 topo_fmristr_build(&size, buf, buflen, "/", NULL, NULL); 460 461 /* :root=... */ 462 if (root) { 463 topo_fmristr_build(&size, buf, buflen, root, 464 ":" FM_FMRI_SW_OBJ_ROOT "=", NULL); 465 } 466 467 /* :path=... */ 468 topo_fmristr_build(&size, buf, buflen, path, 469 ":" FM_FMRI_SW_OBJ_PATH "=", NULL); 470 471 if (token) { 472 /* #:token=... */ 473 topo_fmristr_build(&size, buf, buflen, token, 474 "#:" FM_FMRI_SW_SITE_TOKEN "=", NULL); 475 } else if (file) { 476 /* #:file=... */ 477 topo_fmristr_build(&size, buf, buflen, file, 478 "#:" FM_FMRI_SW_SITE_FILE "=", NULL); 479 480 /* :func=... */ 481 if (func) { 482 topo_fmristr_build(&size, buf, buflen, func, 483 ":" FM_FMRI_SW_SITE_FUNC "=", NULL); 484 } 485 486 /* :line=... */ 487 if (linevalid) { 488 if (pass == 1) 489 (void) snprintf(linebuf, sizeof (linebuf), 490 "%lld", line); 491 492 topo_fmristr_build(&size, buf, buflen, linebuf, 493 ":" FM_FMRI_SW_SITE_LINE "=", NULL); 494 } 495 } 496 497 if (buf == NULL) { 498 if ((buf = topo_mod_alloc(mod, size + 1)) == NULL) 499 return (topo_mod_seterrno(mod, EMOD_NOMEM)); 500 501 buflen = size + 1; 502 size = 0; 503 pass = 2; 504 goto again; 505 } 506 507 /* 508 * Construct the nvlist to return as the result. 509 */ 510 if (topo_mod_nvalloc(mod, &fmristr, NV_UNIQUE_NAME) != 0) { 511 topo_mod_strfree(mod, buf); 512 return (topo_mod_seterrno(mod, EMOD_NOMEM)); 513 } 514 515 if (nvlist_add_string(fmristr, "fmri-string", buf) != 0) { 516 topo_mod_strfree(mod, buf); 517 nvlist_free(fmristr); 518 return (topo_mod_seterrno(mod, EMOD_NOMEM)); 519 } 520 topo_mod_strfree(mod, buf); 521 *out = fmristr; 522 523 return (0); 524 } 525