17aec1d6eScindi /* 27aec1d6eScindi * CDDL HEADER START 37aec1d6eScindi * 47aec1d6eScindi * The contents of this file are subject to the terms of the 57aec1d6eScindi * Common Development and Distribution License (the "License"). 67aec1d6eScindi * You may not use this file except in compliance with the License. 77aec1d6eScindi * 87aec1d6eScindi * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97aec1d6eScindi * or http://www.opensolaris.org/os/licensing. 107aec1d6eScindi * See the License for the specific language governing permissions 117aec1d6eScindi * and limitations under the License. 127aec1d6eScindi * 137aec1d6eScindi * When distributing Covered Code, include this CDDL HEADER in each 147aec1d6eScindi * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157aec1d6eScindi * If applicable, add the following below this CDDL HEADER, with the 167aec1d6eScindi * fields enclosed by brackets "[]" replaced with your own identifying 177aec1d6eScindi * information: Portions Copyright [yyyy] [name of copyright owner] 187aec1d6eScindi * 197aec1d6eScindi * CDDL HEADER END 207aec1d6eScindi */ 217aec1d6eScindi 227aec1d6eScindi /* 23*f6e214c7SGavin Maltby * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. 247aec1d6eScindi */ 257aec1d6eScindi 267aec1d6eScindi #include <limits.h> 277aec1d6eScindi #include <strings.h> 287aec1d6eScindi #include <string.h> 297aec1d6eScindi #include <unistd.h> 307aec1d6eScindi #include <stdio.h> 317aec1d6eScindi #include <alloca.h> 327aec1d6eScindi #include <libnvpair.h> 337aec1d6eScindi #include <fm/topo_mod.h> 347aec1d6eScindi #include <sys/fm/protocol.h> 357aec1d6eScindi 367aec1d6eScindi #include <fcntl.h> 377aec1d6eScindi #include <sys/types.h> 387aec1d6eScindi #include <sys/stat.h> 397aec1d6eScindi #include <sys/objfs.h> 407aec1d6eScindi #include <sys/modctl.h> 417aec1d6eScindi #include <libelf.h> 427aec1d6eScindi #include <gelf.h> 437aec1d6eScindi 440eb822a1Scindi #include <topo_method.h> 45b7d3956bSstephh #include <topo_subr.h> 460eb822a1Scindi #include <pkg.h> 477aec1d6eScindi 487aec1d6eScindi #define BUFLEN (2 * PATH_MAX) 497aec1d6eScindi 507aec1d6eScindi static int pkg_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t, 510eb822a1Scindi topo_instance_t, void *, void *); 527aec1d6eScindi static void pkg_release(topo_mod_t *, tnode_t *); 537aec1d6eScindi static int pkg_fmri_create_meth(topo_mod_t *, tnode_t *, topo_version_t, 547aec1d6eScindi nvlist_t *, nvlist_t **); 55b7d3956bSstephh static int pkg_fmri_nvl2str(topo_mod_t *, tnode_t *, topo_version_t, 56b7d3956bSstephh nvlist_t *, nvlist_t **); 577aec1d6eScindi 587aec1d6eScindi static const topo_method_t pkg_methods[] = { 597aec1d6eScindi { TOPO_METH_FMRI, TOPO_METH_FMRI_DESC, TOPO_METH_FMRI_VERSION, 607aec1d6eScindi TOPO_STABILITY_INTERNAL, pkg_fmri_create_meth }, 61b7d3956bSstephh { TOPO_METH_NVL2STR, TOPO_METH_NVL2STR_DESC, TOPO_METH_NVL2STR_VERSION, 62b7d3956bSstephh TOPO_STABILITY_INTERNAL, pkg_fmri_nvl2str }, 637aec1d6eScindi { NULL } 647aec1d6eScindi }; 657aec1d6eScindi 660eb822a1Scindi static const topo_modops_t pkg_ops = 670eb822a1Scindi { pkg_enum, pkg_release }; 687aec1d6eScindi static const topo_modinfo_t pkg_info = 690eb822a1Scindi { "pkg", FM_FMRI_SCHEME_PKG, PKG_VERSION, &pkg_ops }; 707aec1d6eScindi 710eb822a1Scindi int 720eb822a1Scindi pkg_init(topo_mod_t *mod, topo_version_t version) 737aec1d6eScindi { 740eb822a1Scindi if (getenv("TOPOPKGDEBUG")) 750eb822a1Scindi topo_mod_setdebug(mod); 76*f6e214c7SGavin Maltby topo_mod_dprintf(mod, "initializing pkg builtin\n"); 777aec1d6eScindi 780eb822a1Scindi if (version != PKG_VERSION) 790eb822a1Scindi return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 800eb822a1Scindi 810eb822a1Scindi if (topo_mod_register(mod, &pkg_info, TOPO_VERSION) != 0) { 827aec1d6eScindi topo_mod_dprintf(mod, "failed to register pkg_info: " 837aec1d6eScindi "%s\n", topo_mod_errmsg(mod)); 840eb822a1Scindi return (-1); 857aec1d6eScindi } 860eb822a1Scindi 870eb822a1Scindi return (0); 887aec1d6eScindi } 897aec1d6eScindi 907aec1d6eScindi void 917aec1d6eScindi pkg_fini(topo_mod_t *mod) 927aec1d6eScindi { 937aec1d6eScindi topo_mod_unregister(mod); 947aec1d6eScindi } 957aec1d6eScindi 967aec1d6eScindi /*ARGSUSED*/ 977aec1d6eScindi static int 987aec1d6eScindi pkg_enum(topo_mod_t *mod, tnode_t *pnode, const char *name, 990eb822a1Scindi topo_instance_t min, topo_instance_t max, void *notused1, void *notused2) 1007aec1d6eScindi { 101*f6e214c7SGavin Maltby /* 102*f6e214c7SGavin Maltby * Methods are registered, but there is no enumeration. Should 103*f6e214c7SGavin Maltby * enumeration be added be sure to cater for global vs non-global 104*f6e214c7SGavin Maltby * zones. 105*f6e214c7SGavin Maltby */ 1067aec1d6eScindi (void) topo_method_register(mod, pnode, pkg_methods); 1077aec1d6eScindi return (0); 1087aec1d6eScindi } 1097aec1d6eScindi 1107aec1d6eScindi static void 1117aec1d6eScindi pkg_release(topo_mod_t *mod, tnode_t *node) 1127aec1d6eScindi { 1137aec1d6eScindi topo_method_unregister_all(mod, node); 1147aec1d6eScindi } 1157aec1d6eScindi 1167aec1d6eScindi static int 1177aec1d6eScindi read_thru(topo_mod_t *mp, FILE *fp, const char *substr) 1187aec1d6eScindi { 1197aec1d6eScindi char *tmpbuf = alloca(2 * MAXPATHLEN); 1207aec1d6eScindi int notfound = 1; 1217aec1d6eScindi 1227aec1d6eScindi while (fgets(tmpbuf, 2 * MAXPATHLEN, fp) != NULL) { 1237aec1d6eScindi if (substr == NULL) 1247aec1d6eScindi topo_mod_dprintf(mp, "%s", tmpbuf); 1257aec1d6eScindi else if (strstr(tmpbuf, substr) != NULL) { 1267aec1d6eScindi notfound = 0; 1277aec1d6eScindi break; 1287aec1d6eScindi } 1297aec1d6eScindi } 1307aec1d6eScindi return (notfound); 1317aec1d6eScindi } 1327aec1d6eScindi 1337aec1d6eScindi static nvlist_t * 1347aec1d6eScindi construct_fru_fmri(topo_mod_t *mp, const char *pkgname, FILE *fp) 1357aec1d6eScindi { 1367aec1d6eScindi nvlist_t *f = NULL; 1377aec1d6eScindi char *tmpbuf = alloca(BUFLEN); 1387aec1d6eScindi char *pkgdir = NULL; 1397aec1d6eScindi char *pkgver = NULL; 1407aec1d6eScindi char *token; 1417aec1d6eScindi int e; 1427aec1d6eScindi 1437aec1d6eScindi while (fgets(tmpbuf, BUFLEN, fp) != NULL) { 1447aec1d6eScindi if (strstr(tmpbuf, "VERSION:") != NULL) { 1457aec1d6eScindi token = strtok(tmpbuf, ":"); 1467aec1d6eScindi token = strtok(NULL, ": \t\n"); 1477aec1d6eScindi pkgver = topo_mod_strdup(mp, token); 1487aec1d6eScindi } else if (strstr(tmpbuf, "BASEDIR:") != NULL) { 1497aec1d6eScindi token = strtok(tmpbuf, ":"); 1507aec1d6eScindi token = strtok(NULL, ": \t\n"); 1517aec1d6eScindi pkgdir = topo_mod_strdup(mp, token); 1527aec1d6eScindi } 1537aec1d6eScindi } 1547aec1d6eScindi 1557aec1d6eScindi if (pkgdir == NULL || pkgver == NULL) { 1567aec1d6eScindi (void) topo_mod_seterrno(mp, EMOD_METHOD_INVAL); 1577aec1d6eScindi goto fmrileave; 1587aec1d6eScindi } 1597aec1d6eScindi 1607aec1d6eScindi if (topo_mod_nvalloc(mp, &f, NV_UNIQUE_NAME) != 0) { 1617aec1d6eScindi (void) topo_mod_seterrno(mp, EMOD_FMRI_NVL); 1627aec1d6eScindi goto fmrileave; 1637aec1d6eScindi } 1647aec1d6eScindi 1657aec1d6eScindi e = nvlist_add_string(f, FM_FMRI_SCHEME, FM_FMRI_SCHEME_PKG); 1667aec1d6eScindi e |= nvlist_add_uint8(f, FM_VERSION, FM_PKG_SCHEME_VERSION); 1677aec1d6eScindi e |= nvlist_add_string(f, FM_FMRI_PKG_BASEDIR, pkgdir); 1687aec1d6eScindi e |= nvlist_add_string(f, FM_FMRI_PKG_INST, pkgname); 1697aec1d6eScindi e |= nvlist_add_string(f, FM_FMRI_PKG_VERSION, pkgver); 1707aec1d6eScindi if (e == 0) 1717aec1d6eScindi goto fmrileave; 1727aec1d6eScindi 1737aec1d6eScindi topo_mod_dprintf(mp, "construction of pkg nvl failed"); 1747aec1d6eScindi (void) topo_mod_seterrno(mp, EMOD_FMRI_NVL); 1757aec1d6eScindi nvlist_free(f); 1767aec1d6eScindi f = NULL; 1777aec1d6eScindi 1787aec1d6eScindi fmrileave: 1797aec1d6eScindi if (pkgdir != NULL) 1807aec1d6eScindi topo_mod_strfree(mp, pkgdir); 1817aec1d6eScindi if (pkgver != NULL) 1827aec1d6eScindi topo_mod_strfree(mp, pkgver); 1837aec1d6eScindi 1847aec1d6eScindi return (f); 1857aec1d6eScindi } 1867aec1d6eScindi 1877aec1d6eScindi #define PKGINFO_CMD "LC_MESSAGES= /usr/bin/pkginfo -l %s 2>/dev/null" 1887aec1d6eScindi #define PKGCHK_CMD "LC_MESSAGES= /usr/sbin/pkgchk -lp %s 2>/dev/null" 1897aec1d6eScindi #define PKG_KEYPHRASE "Referenced by the following packages:" 1907aec1d6eScindi 1917aec1d6eScindi static nvlist_t * 1927aec1d6eScindi pkg_fmri_create(topo_mod_t *mp, const char *path) 1937aec1d6eScindi { 1947aec1d6eScindi static char tmpbuf[BUFLEN]; 1957aec1d6eScindi char *findpkgname; 1967aec1d6eScindi char *pkgname = NULL; 1977aec1d6eScindi FILE *pcout; 1987aec1d6eScindi nvlist_t *out = NULL; 1997aec1d6eScindi 2007aec1d6eScindi (void) snprintf(tmpbuf, BUFLEN, PKGCHK_CMD, path); 2017aec1d6eScindi topo_mod_dprintf(mp, "popen of %s\n", tmpbuf); 2027aec1d6eScindi pcout = popen(tmpbuf, "r"); 2037aec1d6eScindi if (read_thru(mp, pcout, PKG_KEYPHRASE)) { 2047aec1d6eScindi (void) pclose(pcout); 2057aec1d6eScindi goto pfc_bail; 2067aec1d6eScindi } 2077aec1d6eScindi (void) fgets(tmpbuf, BUFLEN, pcout); 2087aec1d6eScindi (void) pclose(pcout); 2097aec1d6eScindi topo_mod_dprintf(mp, "%s", tmpbuf); 2107aec1d6eScindi 2117aec1d6eScindi if ((findpkgname = strtok(tmpbuf, " \n")) == NULL) 2127aec1d6eScindi goto pfc_bail; 2137aec1d6eScindi pkgname = topo_mod_strdup(mp, findpkgname); 2147aec1d6eScindi 2157aec1d6eScindi (void) snprintf(tmpbuf, BUFLEN, PKGINFO_CMD, pkgname); 2167aec1d6eScindi topo_mod_dprintf(mp, "popen of %s\n", tmpbuf); 2177aec1d6eScindi pcout = popen(tmpbuf, "r"); 2187aec1d6eScindi out = construct_fru_fmri(mp, pkgname, pcout); 2197aec1d6eScindi (void) pclose(pcout); 2207aec1d6eScindi 2217aec1d6eScindi pfc_bail: 2227aec1d6eScindi if (pkgname != NULL) 2237aec1d6eScindi topo_mod_strfree(mp, pkgname); 2247aec1d6eScindi return (out); 2257aec1d6eScindi } 2267aec1d6eScindi 2277aec1d6eScindi /*ARGSUSED*/ 2287aec1d6eScindi static int 2297aec1d6eScindi pkg_fmri_create_meth(topo_mod_t *mp, tnode_t *node, topo_version_t version, 2307aec1d6eScindi nvlist_t *in, nvlist_t **out) 2317aec1d6eScindi { 2327aec1d6eScindi nvlist_t *args = NULL; 2337aec1d6eScindi char *path; 2347aec1d6eScindi 2357aec1d6eScindi if (version > TOPO_METH_FMRI_VERSION) 2367aec1d6eScindi return (topo_mod_seterrno(mp, EMOD_VER_NEW)); 2377aec1d6eScindi 2387aec1d6eScindi if (nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_NVL, &args) != 0 || 2397aec1d6eScindi nvlist_lookup_string(args, "path", &path) != 0) { 2407aec1d6eScindi topo_mod_dprintf(mp, "no path string in method argument\n"); 2417aec1d6eScindi return (topo_mod_seterrno(mp, EMOD_METHOD_INVAL)); 2427aec1d6eScindi } 2437aec1d6eScindi 2447aec1d6eScindi if ((*out = pkg_fmri_create(mp, path)) == NULL) 2457aec1d6eScindi return (-1); 2467aec1d6eScindi return (0); 2477aec1d6eScindi } 248b7d3956bSstephh 249b7d3956bSstephh static ssize_t 250b7d3956bSstephh fmri_nvl2str(nvlist_t *nvl, char *buf, size_t buflen) 251b7d3956bSstephh { 252b7d3956bSstephh nvlist_t *anvl = NULL; 2539c94f155SCheng Sean Ye nvpair_t *apair; 254b7d3956bSstephh uint8_t version; 255b7d3956bSstephh ssize_t size = 0; 2569c94f155SCheng Sean Ye char *pkgname = NULL, *aname, *aval; 257b7d3956bSstephh int err; 258b7d3956bSstephh 259b7d3956bSstephh if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 || 260b7d3956bSstephh version > FM_PKG_SCHEME_VERSION) 261b7d3956bSstephh return (-1); 262b7d3956bSstephh 263b7d3956bSstephh /* Get authority, if present */ 264b7d3956bSstephh err = nvlist_lookup_nvlist(nvl, FM_FMRI_AUTHORITY, &anvl); 265b7d3956bSstephh if (err != 0 && err != ENOENT) 266b7d3956bSstephh return (-1); 267b7d3956bSstephh 268b7d3956bSstephh /* 269b7d3956bSstephh * For brevity, we only include the pkgname and any authority 270b7d3956bSstephh * info present in the FMRI in our output string. The FMRI 271b7d3956bSstephh * also has data on the package directory and version. 272b7d3956bSstephh */ 273b7d3956bSstephh err = nvlist_lookup_string(nvl, FM_FMRI_PKG_INST, &pkgname); 274b7d3956bSstephh if (err != 0 || pkgname == NULL) 275b7d3956bSstephh return (-1); 276b7d3956bSstephh 277b7d3956bSstephh /* pkg:// */ 278b7d3956bSstephh topo_fmristr_build(&size, buf, buflen, FM_FMRI_SCHEME_PKG, NULL, "://"); 279b7d3956bSstephh 280b7d3956bSstephh /* authority, if any */ 2819c94f155SCheng Sean Ye if (anvl != NULL) { 2829c94f155SCheng Sean Ye for (apair = nvlist_next_nvpair(anvl, NULL); 2839c94f155SCheng Sean Ye apair != NULL; apair = nvlist_next_nvpair(anvl, apair)) { 2849c94f155SCheng Sean Ye if (nvpair_type(apair) != DATA_TYPE_STRING || 2859c94f155SCheng Sean Ye nvpair_value_string(apair, &aval) != 0) 2869c94f155SCheng Sean Ye continue; 2879c94f155SCheng Sean Ye aname = nvpair_name(apair); 2889c94f155SCheng Sean Ye topo_fmristr_build(&size, buf, buflen, ":", NULL, NULL); 2899c94f155SCheng Sean Ye topo_fmristr_build(&size, buf, buflen, "=", 2909c94f155SCheng Sean Ye aname, aval); 2919c94f155SCheng Sean Ye } 2929c94f155SCheng Sean Ye } 293b7d3956bSstephh 294b7d3956bSstephh /* pkg-name part */ 295b7d3956bSstephh topo_fmristr_build(&size, buf, buflen, pkgname, "/", NULL); 296b7d3956bSstephh 297b7d3956bSstephh return (size); 298b7d3956bSstephh } 299b7d3956bSstephh 300b7d3956bSstephh /*ARGSUSED*/ 301b7d3956bSstephh static int 302b7d3956bSstephh pkg_fmri_nvl2str(topo_mod_t *mod, tnode_t *node, topo_version_t version, 303b7d3956bSstephh nvlist_t *nvl, nvlist_t **out) 304b7d3956bSstephh { 305b7d3956bSstephh ssize_t len; 306b7d3956bSstephh char *name = NULL; 307b7d3956bSstephh nvlist_t *fmristr; 308b7d3956bSstephh 309b7d3956bSstephh if (version > TOPO_METH_NVL2STR_VERSION) 310b7d3956bSstephh return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 311b7d3956bSstephh 312b7d3956bSstephh if ((len = fmri_nvl2str(nvl, NULL, 0)) == 0 || 313b7d3956bSstephh (name = topo_mod_alloc(mod, len + 1)) == NULL || 314b7d3956bSstephh fmri_nvl2str(nvl, name, len + 1) == 0) { 315b7d3956bSstephh if (name != NULL) 316b7d3956bSstephh topo_mod_free(mod, name, len + 1); 317b7d3956bSstephh return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 318b7d3956bSstephh } 319b7d3956bSstephh 320b7d3956bSstephh if (topo_mod_nvalloc(mod, &fmristr, NV_UNIQUE_NAME) != 0) 321b7d3956bSstephh return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 322b7d3956bSstephh if (nvlist_add_string(fmristr, "fmri-string", name) != 0) { 323b7d3956bSstephh topo_mod_free(mod, name, len + 1); 324b7d3956bSstephh nvlist_free(fmristr); 325b7d3956bSstephh return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 326b7d3956bSstephh } 327b7d3956bSstephh topo_mod_free(mod, name, len + 1); 328b7d3956bSstephh *out = fmristr; 329b7d3956bSstephh 330b7d3956bSstephh return (0); 331b7d3956bSstephh } 332