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 }