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 }