11414Scindi /* 21414Scindi * CDDL HEADER START 31414Scindi * 41414Scindi * The contents of this file are subject to the terms of the 51414Scindi * Common Development and Distribution License (the "License"). 61414Scindi * You may not use this file except in compliance with the License. 71414Scindi * 81414Scindi * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 91414Scindi * or http://www.opensolaris.org/os/licensing. 101414Scindi * See the License for the specific language governing permissions 111414Scindi * and limitations under the License. 121414Scindi * 131414Scindi * When distributing Covered Code, include this CDDL HEADER in each 141414Scindi * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 151414Scindi * If applicable, add the following below this CDDL HEADER, with the 161414Scindi * fields enclosed by brackets "[]" replaced with your own identifying 171414Scindi * information: Portions Copyright [yyyy] [name of copyright owner] 181414Scindi * 191414Scindi * CDDL HEADER END 201414Scindi */ 211414Scindi 221414Scindi /* 23*7197Sstephh * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 241414Scindi * Use is subject to license terms. 251414Scindi */ 261414Scindi 271414Scindi #pragma ident "%Z%%M% %I% %E% SMI" 281414Scindi 291414Scindi #include <limits.h> 301414Scindi #include <strings.h> 311414Scindi #include <string.h> 321414Scindi #include <unistd.h> 331414Scindi #include <stdio.h> 341414Scindi #include <alloca.h> 351414Scindi #include <libnvpair.h> 361414Scindi #include <fm/topo_mod.h> 371414Scindi #include <sys/fm/protocol.h> 381414Scindi 391414Scindi #include <fcntl.h> 401414Scindi #include <sys/types.h> 411414Scindi #include <sys/stat.h> 421414Scindi #include <sys/objfs.h> 431414Scindi #include <sys/modctl.h> 441414Scindi #include <libelf.h> 451414Scindi #include <gelf.h> 461414Scindi 473062Scindi #include <topo_method.h> 48*7197Sstephh #include <topo_subr.h> 493062Scindi #include <pkg.h> 501414Scindi 511414Scindi #define BUFLEN (2 * PATH_MAX) 521414Scindi 531414Scindi static int pkg_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t, 543062Scindi topo_instance_t, void *, void *); 551414Scindi static void pkg_release(topo_mod_t *, tnode_t *); 561414Scindi static int pkg_fmri_create_meth(topo_mod_t *, tnode_t *, topo_version_t, 571414Scindi nvlist_t *, nvlist_t **); 58*7197Sstephh static int pkg_fmri_nvl2str(topo_mod_t *, tnode_t *, topo_version_t, 59*7197Sstephh nvlist_t *, nvlist_t **); 601414Scindi 611414Scindi static const topo_method_t pkg_methods[] = { 621414Scindi { TOPO_METH_FMRI, TOPO_METH_FMRI_DESC, TOPO_METH_FMRI_VERSION, 631414Scindi TOPO_STABILITY_INTERNAL, pkg_fmri_create_meth }, 64*7197Sstephh { TOPO_METH_NVL2STR, TOPO_METH_NVL2STR_DESC, TOPO_METH_NVL2STR_VERSION, 65*7197Sstephh TOPO_STABILITY_INTERNAL, pkg_fmri_nvl2str }, 661414Scindi { NULL } 671414Scindi }; 681414Scindi 693062Scindi static const topo_modops_t pkg_ops = 703062Scindi { pkg_enum, pkg_release }; 711414Scindi static const topo_modinfo_t pkg_info = 723062Scindi { "pkg", FM_FMRI_SCHEME_PKG, PKG_VERSION, &pkg_ops }; 731414Scindi 743062Scindi int 753062Scindi pkg_init(topo_mod_t *mod, topo_version_t version) 761414Scindi { 773062Scindi if (getenv("TOPOPKGDEBUG")) 783062Scindi topo_mod_setdebug(mod); 791414Scindi topo_mod_dprintf(mod, "initializing mod builtin\n"); 801414Scindi 813062Scindi if (version != PKG_VERSION) 823062Scindi return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 833062Scindi 843062Scindi if (topo_mod_register(mod, &pkg_info, TOPO_VERSION) != 0) { 851414Scindi topo_mod_dprintf(mod, "failed to register pkg_info: " 861414Scindi "%s\n", topo_mod_errmsg(mod)); 873062Scindi return (-1); 881414Scindi } 893062Scindi 903062Scindi return (0); 911414Scindi } 921414Scindi 931414Scindi void 941414Scindi pkg_fini(topo_mod_t *mod) 951414Scindi { 961414Scindi topo_mod_unregister(mod); 971414Scindi } 981414Scindi 991414Scindi /*ARGSUSED*/ 1001414Scindi static int 1011414Scindi pkg_enum(topo_mod_t *mod, tnode_t *pnode, const char *name, 1023062Scindi topo_instance_t min, topo_instance_t max, void *notused1, void *notused2) 1031414Scindi { 1041414Scindi (void) topo_method_register(mod, pnode, pkg_methods); 1051414Scindi return (0); 1061414Scindi } 1071414Scindi 1081414Scindi static void 1091414Scindi pkg_release(topo_mod_t *mod, tnode_t *node) 1101414Scindi { 1111414Scindi topo_method_unregister_all(mod, node); 1121414Scindi } 1131414Scindi 1141414Scindi static int 1151414Scindi read_thru(topo_mod_t *mp, FILE *fp, const char *substr) 1161414Scindi { 1171414Scindi char *tmpbuf = alloca(2 * MAXPATHLEN); 1181414Scindi int notfound = 1; 1191414Scindi 1201414Scindi while (fgets(tmpbuf, 2 * MAXPATHLEN, fp) != NULL) { 1211414Scindi if (substr == NULL) 1221414Scindi topo_mod_dprintf(mp, "%s", tmpbuf); 1231414Scindi else if (strstr(tmpbuf, substr) != NULL) { 1241414Scindi notfound = 0; 1251414Scindi break; 1261414Scindi } 1271414Scindi } 1281414Scindi return (notfound); 1291414Scindi } 1301414Scindi 1311414Scindi static nvlist_t * 1321414Scindi construct_fru_fmri(topo_mod_t *mp, const char *pkgname, FILE *fp) 1331414Scindi { 1341414Scindi nvlist_t *f = NULL; 1351414Scindi char *tmpbuf = alloca(BUFLEN); 1361414Scindi char *pkgdir = NULL; 1371414Scindi char *pkgver = NULL; 1381414Scindi char *token; 1391414Scindi int e; 1401414Scindi 1411414Scindi while (fgets(tmpbuf, BUFLEN, fp) != NULL) { 1421414Scindi if (strstr(tmpbuf, "VERSION:") != NULL) { 1431414Scindi token = strtok(tmpbuf, ":"); 1441414Scindi token = strtok(NULL, ": \t\n"); 1451414Scindi pkgver = topo_mod_strdup(mp, token); 1461414Scindi } else if (strstr(tmpbuf, "BASEDIR:") != NULL) { 1471414Scindi token = strtok(tmpbuf, ":"); 1481414Scindi token = strtok(NULL, ": \t\n"); 1491414Scindi pkgdir = topo_mod_strdup(mp, token); 1501414Scindi } 1511414Scindi } 1521414Scindi 1531414Scindi if (pkgdir == NULL || pkgver == NULL) { 1541414Scindi (void) topo_mod_seterrno(mp, EMOD_METHOD_INVAL); 1551414Scindi goto fmrileave; 1561414Scindi } 1571414Scindi 1581414Scindi if (topo_mod_nvalloc(mp, &f, NV_UNIQUE_NAME) != 0) { 1591414Scindi (void) topo_mod_seterrno(mp, EMOD_FMRI_NVL); 1601414Scindi goto fmrileave; 1611414Scindi } 1621414Scindi 1631414Scindi e = nvlist_add_string(f, FM_FMRI_SCHEME, FM_FMRI_SCHEME_PKG); 1641414Scindi e |= nvlist_add_uint8(f, FM_VERSION, FM_PKG_SCHEME_VERSION); 1651414Scindi e |= nvlist_add_string(f, FM_FMRI_PKG_BASEDIR, pkgdir); 1661414Scindi e |= nvlist_add_string(f, FM_FMRI_PKG_INST, pkgname); 1671414Scindi e |= nvlist_add_string(f, FM_FMRI_PKG_VERSION, pkgver); 1681414Scindi if (e == 0) 1691414Scindi goto fmrileave; 1701414Scindi 1711414Scindi topo_mod_dprintf(mp, "construction of pkg nvl failed"); 1721414Scindi (void) topo_mod_seterrno(mp, EMOD_FMRI_NVL); 1731414Scindi nvlist_free(f); 1741414Scindi f = NULL; 1751414Scindi 1761414Scindi fmrileave: 1771414Scindi if (pkgdir != NULL) 1781414Scindi topo_mod_strfree(mp, pkgdir); 1791414Scindi if (pkgver != NULL) 1801414Scindi topo_mod_strfree(mp, pkgver); 1811414Scindi 1821414Scindi return (f); 1831414Scindi } 1841414Scindi 1851414Scindi #define PKGINFO_CMD "LC_MESSAGES= /usr/bin/pkginfo -l %s 2>/dev/null" 1861414Scindi #define PKGCHK_CMD "LC_MESSAGES= /usr/sbin/pkgchk -lp %s 2>/dev/null" 1871414Scindi #define PKG_KEYPHRASE "Referenced by the following packages:" 1881414Scindi 1891414Scindi static nvlist_t * 1901414Scindi pkg_fmri_create(topo_mod_t *mp, const char *path) 1911414Scindi { 1921414Scindi static char tmpbuf[BUFLEN]; 1931414Scindi char *findpkgname; 1941414Scindi char *pkgname = NULL; 1951414Scindi FILE *pcout; 1961414Scindi nvlist_t *out = NULL; 1971414Scindi 1981414Scindi (void) snprintf(tmpbuf, BUFLEN, PKGCHK_CMD, path); 1991414Scindi topo_mod_dprintf(mp, "popen of %s\n", tmpbuf); 2001414Scindi pcout = popen(tmpbuf, "r"); 2011414Scindi if (read_thru(mp, pcout, PKG_KEYPHRASE)) { 2021414Scindi (void) pclose(pcout); 2031414Scindi goto pfc_bail; 2041414Scindi } 2051414Scindi (void) fgets(tmpbuf, BUFLEN, pcout); 2061414Scindi (void) pclose(pcout); 2071414Scindi topo_mod_dprintf(mp, "%s", tmpbuf); 2081414Scindi 2091414Scindi if ((findpkgname = strtok(tmpbuf, " \n")) == NULL) 2101414Scindi goto pfc_bail; 2111414Scindi pkgname = topo_mod_strdup(mp, findpkgname); 2121414Scindi 2131414Scindi (void) snprintf(tmpbuf, BUFLEN, PKGINFO_CMD, pkgname); 2141414Scindi topo_mod_dprintf(mp, "popen of %s\n", tmpbuf); 2151414Scindi pcout = popen(tmpbuf, "r"); 2161414Scindi out = construct_fru_fmri(mp, pkgname, pcout); 2171414Scindi (void) pclose(pcout); 2181414Scindi 2191414Scindi pfc_bail: 2201414Scindi if (pkgname != NULL) 2211414Scindi topo_mod_strfree(mp, pkgname); 2221414Scindi return (out); 2231414Scindi } 2241414Scindi 2251414Scindi /*ARGSUSED*/ 2261414Scindi static int 2271414Scindi pkg_fmri_create_meth(topo_mod_t *mp, tnode_t *node, topo_version_t version, 2281414Scindi nvlist_t *in, nvlist_t **out) 2291414Scindi { 2301414Scindi nvlist_t *args = NULL; 2311414Scindi char *path; 2321414Scindi 2331414Scindi if (version > TOPO_METH_FMRI_VERSION) 2341414Scindi return (topo_mod_seterrno(mp, EMOD_VER_NEW)); 2351414Scindi 2361414Scindi if (nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_NVL, &args) != 0 || 2371414Scindi nvlist_lookup_string(args, "path", &path) != 0) { 2381414Scindi topo_mod_dprintf(mp, "no path string in method argument\n"); 2391414Scindi return (topo_mod_seterrno(mp, EMOD_METHOD_INVAL)); 2401414Scindi } 2411414Scindi 2421414Scindi if ((*out = pkg_fmri_create(mp, path)) == NULL) 2431414Scindi return (-1); 2441414Scindi return (0); 2451414Scindi } 246*7197Sstephh 247*7197Sstephh static ssize_t 248*7197Sstephh fmri_nvl2str(nvlist_t *nvl, char *buf, size_t buflen) 249*7197Sstephh { 250*7197Sstephh nvlist_t *anvl = NULL; 251*7197Sstephh uint8_t version; 252*7197Sstephh ssize_t size = 0; 253*7197Sstephh char *pkgname = NULL; 254*7197Sstephh char *achas = NULL; 255*7197Sstephh char *adom = NULL; 256*7197Sstephh char *aprod = NULL; 257*7197Sstephh char *asrvr = NULL; 258*7197Sstephh char *ahost = NULL; 259*7197Sstephh int err; 260*7197Sstephh 261*7197Sstephh if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 || 262*7197Sstephh version > FM_PKG_SCHEME_VERSION) 263*7197Sstephh return (-1); 264*7197Sstephh 265*7197Sstephh /* Get authority, if present */ 266*7197Sstephh err = nvlist_lookup_nvlist(nvl, FM_FMRI_AUTHORITY, &anvl); 267*7197Sstephh if (err != 0 && err != ENOENT) 268*7197Sstephh return (-1); 269*7197Sstephh 270*7197Sstephh /* 271*7197Sstephh * For brevity, we only include the pkgname and any authority 272*7197Sstephh * info present in the FMRI in our output string. The FMRI 273*7197Sstephh * also has data on the package directory and version. 274*7197Sstephh */ 275*7197Sstephh err = nvlist_lookup_string(nvl, FM_FMRI_PKG_INST, &pkgname); 276*7197Sstephh if (err != 0 || pkgname == NULL) 277*7197Sstephh return (-1); 278*7197Sstephh 279*7197Sstephh if (anvl != NULL) { 280*7197Sstephh (void) nvlist_lookup_string(anvl, 281*7197Sstephh FM_FMRI_AUTH_PRODUCT, &aprod); 282*7197Sstephh (void) nvlist_lookup_string(anvl, 283*7197Sstephh FM_FMRI_AUTH_CHASSIS, &achas); 284*7197Sstephh (void) nvlist_lookup_string(anvl, 285*7197Sstephh FM_FMRI_AUTH_DOMAIN, &adom); 286*7197Sstephh (void) nvlist_lookup_string(anvl, 287*7197Sstephh FM_FMRI_AUTH_SERVER, &asrvr); 288*7197Sstephh (void) nvlist_lookup_string(anvl, 289*7197Sstephh FM_FMRI_AUTH_HOST, &ahost); 290*7197Sstephh } 291*7197Sstephh 292*7197Sstephh /* pkg:// */ 293*7197Sstephh topo_fmristr_build(&size, buf, buflen, FM_FMRI_SCHEME_PKG, NULL, "://"); 294*7197Sstephh 295*7197Sstephh /* authority, if any */ 296*7197Sstephh if (aprod != NULL) 297*7197Sstephh topo_fmristr_build(&size, buf, buflen, aprod, 298*7197Sstephh FM_FMRI_AUTH_PRODUCT "=", NULL); 299*7197Sstephh if (achas != NULL) 300*7197Sstephh topo_fmristr_build(&size, buf, buflen, achas, 301*7197Sstephh FM_FMRI_AUTH_CHASSIS "=", NULL); 302*7197Sstephh if (adom != NULL) 303*7197Sstephh topo_fmristr_build(&size, buf, buflen, adom, 304*7197Sstephh FM_FMRI_AUTH_DOMAIN "=", NULL); 305*7197Sstephh if (asrvr != NULL) 306*7197Sstephh topo_fmristr_build(&size, buf, buflen, asrvr, 307*7197Sstephh FM_FMRI_AUTH_SERVER "=", NULL); 308*7197Sstephh if (ahost != NULL) 309*7197Sstephh topo_fmristr_build(&size, buf, buflen, ahost, 310*7197Sstephh FM_FMRI_AUTH_HOST "=", NULL); 311*7197Sstephh 312*7197Sstephh /* pkg-name part */ 313*7197Sstephh topo_fmristr_build(&size, buf, buflen, pkgname, "/", NULL); 314*7197Sstephh 315*7197Sstephh return (size); 316*7197Sstephh } 317*7197Sstephh 318*7197Sstephh /*ARGSUSED*/ 319*7197Sstephh static int 320*7197Sstephh pkg_fmri_nvl2str(topo_mod_t *mod, tnode_t *node, topo_version_t version, 321*7197Sstephh nvlist_t *nvl, nvlist_t **out) 322*7197Sstephh { 323*7197Sstephh ssize_t len; 324*7197Sstephh char *name = NULL; 325*7197Sstephh nvlist_t *fmristr; 326*7197Sstephh 327*7197Sstephh if (version > TOPO_METH_NVL2STR_VERSION) 328*7197Sstephh return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 329*7197Sstephh 330*7197Sstephh if ((len = fmri_nvl2str(nvl, NULL, 0)) == 0 || 331*7197Sstephh (name = topo_mod_alloc(mod, len + 1)) == NULL || 332*7197Sstephh fmri_nvl2str(nvl, name, len + 1) == 0) { 333*7197Sstephh if (name != NULL) 334*7197Sstephh topo_mod_free(mod, name, len + 1); 335*7197Sstephh return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 336*7197Sstephh } 337*7197Sstephh 338*7197Sstephh if (topo_mod_nvalloc(mod, &fmristr, NV_UNIQUE_NAME) != 0) 339*7197Sstephh return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 340*7197Sstephh if (nvlist_add_string(fmristr, "fmri-string", name) != 0) { 341*7197Sstephh topo_mod_free(mod, name, len + 1); 342*7197Sstephh nvlist_free(fmristr); 343*7197Sstephh return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 344*7197Sstephh } 345*7197Sstephh topo_mod_free(mod, name, len + 1); 346*7197Sstephh *out = fmristr; 347*7197Sstephh 348*7197Sstephh return (0); 349*7197Sstephh } 350