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*12967Sgavin.maltby@oracle.com * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
241414Scindi */
251414Scindi
261414Scindi #include <limits.h>
271414Scindi #include <strings.h>
281414Scindi #include <string.h>
291414Scindi #include <unistd.h>
301414Scindi #include <stdio.h>
311414Scindi #include <alloca.h>
321414Scindi #include <libnvpair.h>
331414Scindi #include <fm/topo_mod.h>
341414Scindi #include <sys/fm/protocol.h>
351414Scindi
361414Scindi #include <fcntl.h>
371414Scindi #include <sys/types.h>
381414Scindi #include <sys/stat.h>
391414Scindi #include <sys/objfs.h>
401414Scindi #include <sys/modctl.h>
411414Scindi #include <libelf.h>
421414Scindi #include <gelf.h>
431414Scindi
443062Scindi #include <topo_method.h>
457197Sstephh #include <topo_subr.h>
463062Scindi #include <pkg.h>
471414Scindi
481414Scindi #define BUFLEN (2 * PATH_MAX)
491414Scindi
501414Scindi static int pkg_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t,
513062Scindi topo_instance_t, void *, void *);
521414Scindi static void pkg_release(topo_mod_t *, tnode_t *);
531414Scindi static int pkg_fmri_create_meth(topo_mod_t *, tnode_t *, topo_version_t,
541414Scindi nvlist_t *, nvlist_t **);
557197Sstephh static int pkg_fmri_nvl2str(topo_mod_t *, tnode_t *, topo_version_t,
567197Sstephh nvlist_t *, nvlist_t **);
571414Scindi
581414Scindi static const topo_method_t pkg_methods[] = {
591414Scindi { TOPO_METH_FMRI, TOPO_METH_FMRI_DESC, TOPO_METH_FMRI_VERSION,
601414Scindi TOPO_STABILITY_INTERNAL, pkg_fmri_create_meth },
617197Sstephh { TOPO_METH_NVL2STR, TOPO_METH_NVL2STR_DESC, TOPO_METH_NVL2STR_VERSION,
627197Sstephh TOPO_STABILITY_INTERNAL, pkg_fmri_nvl2str },
631414Scindi { NULL }
641414Scindi };
651414Scindi
663062Scindi static const topo_modops_t pkg_ops =
673062Scindi { pkg_enum, pkg_release };
681414Scindi static const topo_modinfo_t pkg_info =
693062Scindi { "pkg", FM_FMRI_SCHEME_PKG, PKG_VERSION, &pkg_ops };
701414Scindi
713062Scindi int
pkg_init(topo_mod_t * mod,topo_version_t version)723062Scindi pkg_init(topo_mod_t *mod, topo_version_t version)
731414Scindi {
743062Scindi if (getenv("TOPOPKGDEBUG"))
753062Scindi topo_mod_setdebug(mod);
76*12967Sgavin.maltby@oracle.com topo_mod_dprintf(mod, "initializing pkg builtin\n");
771414Scindi
783062Scindi if (version != PKG_VERSION)
793062Scindi return (topo_mod_seterrno(mod, EMOD_VER_NEW));
803062Scindi
813062Scindi if (topo_mod_register(mod, &pkg_info, TOPO_VERSION) != 0) {
821414Scindi topo_mod_dprintf(mod, "failed to register pkg_info: "
831414Scindi "%s\n", topo_mod_errmsg(mod));
843062Scindi return (-1);
851414Scindi }
863062Scindi
873062Scindi return (0);
881414Scindi }
891414Scindi
901414Scindi void
pkg_fini(topo_mod_t * mod)911414Scindi pkg_fini(topo_mod_t *mod)
921414Scindi {
931414Scindi topo_mod_unregister(mod);
941414Scindi }
951414Scindi
961414Scindi /*ARGSUSED*/
971414Scindi static int
pkg_enum(topo_mod_t * mod,tnode_t * pnode,const char * name,topo_instance_t min,topo_instance_t max,void * notused1,void * notused2)981414Scindi pkg_enum(topo_mod_t *mod, tnode_t *pnode, const char *name,
993062Scindi topo_instance_t min, topo_instance_t max, void *notused1, void *notused2)
1001414Scindi {
101*12967Sgavin.maltby@oracle.com /*
102*12967Sgavin.maltby@oracle.com * Methods are registered, but there is no enumeration. Should
103*12967Sgavin.maltby@oracle.com * enumeration be added be sure to cater for global vs non-global
104*12967Sgavin.maltby@oracle.com * zones.
105*12967Sgavin.maltby@oracle.com */
1061414Scindi (void) topo_method_register(mod, pnode, pkg_methods);
1071414Scindi return (0);
1081414Scindi }
1091414Scindi
1101414Scindi static void
pkg_release(topo_mod_t * mod,tnode_t * node)1111414Scindi pkg_release(topo_mod_t *mod, tnode_t *node)
1121414Scindi {
1131414Scindi topo_method_unregister_all(mod, node);
1141414Scindi }
1151414Scindi
1161414Scindi static int
read_thru(topo_mod_t * mp,FILE * fp,const char * substr)1171414Scindi read_thru(topo_mod_t *mp, FILE *fp, const char *substr)
1181414Scindi {
1191414Scindi char *tmpbuf = alloca(2 * MAXPATHLEN);
1201414Scindi int notfound = 1;
1211414Scindi
1221414Scindi while (fgets(tmpbuf, 2 * MAXPATHLEN, fp) != NULL) {
1231414Scindi if (substr == NULL)
1241414Scindi topo_mod_dprintf(mp, "%s", tmpbuf);
1251414Scindi else if (strstr(tmpbuf, substr) != NULL) {
1261414Scindi notfound = 0;
1271414Scindi break;
1281414Scindi }
1291414Scindi }
1301414Scindi return (notfound);
1311414Scindi }
1321414Scindi
1331414Scindi static nvlist_t *
construct_fru_fmri(topo_mod_t * mp,const char * pkgname,FILE * fp)1341414Scindi construct_fru_fmri(topo_mod_t *mp, const char *pkgname, FILE *fp)
1351414Scindi {
1361414Scindi nvlist_t *f = NULL;
1371414Scindi char *tmpbuf = alloca(BUFLEN);
1381414Scindi char *pkgdir = NULL;
1391414Scindi char *pkgver = NULL;
1401414Scindi char *token;
1411414Scindi int e;
1421414Scindi
1431414Scindi while (fgets(tmpbuf, BUFLEN, fp) != NULL) {
1441414Scindi if (strstr(tmpbuf, "VERSION:") != NULL) {
1451414Scindi token = strtok(tmpbuf, ":");
1461414Scindi token = strtok(NULL, ": \t\n");
1471414Scindi pkgver = topo_mod_strdup(mp, token);
1481414Scindi } else if (strstr(tmpbuf, "BASEDIR:") != NULL) {
1491414Scindi token = strtok(tmpbuf, ":");
1501414Scindi token = strtok(NULL, ": \t\n");
1511414Scindi pkgdir = topo_mod_strdup(mp, token);
1521414Scindi }
1531414Scindi }
1541414Scindi
1551414Scindi if (pkgdir == NULL || pkgver == NULL) {
1561414Scindi (void) topo_mod_seterrno(mp, EMOD_METHOD_INVAL);
1571414Scindi goto fmrileave;
1581414Scindi }
1591414Scindi
1601414Scindi if (topo_mod_nvalloc(mp, &f, NV_UNIQUE_NAME) != 0) {
1611414Scindi (void) topo_mod_seterrno(mp, EMOD_FMRI_NVL);
1621414Scindi goto fmrileave;
1631414Scindi }
1641414Scindi
1651414Scindi e = nvlist_add_string(f, FM_FMRI_SCHEME, FM_FMRI_SCHEME_PKG);
1661414Scindi e |= nvlist_add_uint8(f, FM_VERSION, FM_PKG_SCHEME_VERSION);
1671414Scindi e |= nvlist_add_string(f, FM_FMRI_PKG_BASEDIR, pkgdir);
1681414Scindi e |= nvlist_add_string(f, FM_FMRI_PKG_INST, pkgname);
1691414Scindi e |= nvlist_add_string(f, FM_FMRI_PKG_VERSION, pkgver);
1701414Scindi if (e == 0)
1711414Scindi goto fmrileave;
1721414Scindi
1731414Scindi topo_mod_dprintf(mp, "construction of pkg nvl failed");
1741414Scindi (void) topo_mod_seterrno(mp, EMOD_FMRI_NVL);
1751414Scindi nvlist_free(f);
1761414Scindi f = NULL;
1771414Scindi
1781414Scindi fmrileave:
1791414Scindi if (pkgdir != NULL)
1801414Scindi topo_mod_strfree(mp, pkgdir);
1811414Scindi if (pkgver != NULL)
1821414Scindi topo_mod_strfree(mp, pkgver);
1831414Scindi
1841414Scindi return (f);
1851414Scindi }
1861414Scindi
1871414Scindi #define PKGINFO_CMD "LC_MESSAGES= /usr/bin/pkginfo -l %s 2>/dev/null"
1881414Scindi #define PKGCHK_CMD "LC_MESSAGES= /usr/sbin/pkgchk -lp %s 2>/dev/null"
1891414Scindi #define PKG_KEYPHRASE "Referenced by the following packages:"
1901414Scindi
1911414Scindi static nvlist_t *
pkg_fmri_create(topo_mod_t * mp,const char * path)1921414Scindi pkg_fmri_create(topo_mod_t *mp, const char *path)
1931414Scindi {
1941414Scindi static char tmpbuf[BUFLEN];
1951414Scindi char *findpkgname;
1961414Scindi char *pkgname = NULL;
1971414Scindi FILE *pcout;
1981414Scindi nvlist_t *out = NULL;
1991414Scindi
2001414Scindi (void) snprintf(tmpbuf, BUFLEN, PKGCHK_CMD, path);
2011414Scindi topo_mod_dprintf(mp, "popen of %s\n", tmpbuf);
2021414Scindi pcout = popen(tmpbuf, "r");
2031414Scindi if (read_thru(mp, pcout, PKG_KEYPHRASE)) {
2041414Scindi (void) pclose(pcout);
2051414Scindi goto pfc_bail;
2061414Scindi }
2071414Scindi (void) fgets(tmpbuf, BUFLEN, pcout);
2081414Scindi (void) pclose(pcout);
2091414Scindi topo_mod_dprintf(mp, "%s", tmpbuf);
2101414Scindi
2111414Scindi if ((findpkgname = strtok(tmpbuf, " \n")) == NULL)
2121414Scindi goto pfc_bail;
2131414Scindi pkgname = topo_mod_strdup(mp, findpkgname);
2141414Scindi
2151414Scindi (void) snprintf(tmpbuf, BUFLEN, PKGINFO_CMD, pkgname);
2161414Scindi topo_mod_dprintf(mp, "popen of %s\n", tmpbuf);
2171414Scindi pcout = popen(tmpbuf, "r");
2181414Scindi out = construct_fru_fmri(mp, pkgname, pcout);
2191414Scindi (void) pclose(pcout);
2201414Scindi
2211414Scindi pfc_bail:
2221414Scindi if (pkgname != NULL)
2231414Scindi topo_mod_strfree(mp, pkgname);
2241414Scindi return (out);
2251414Scindi }
2261414Scindi
2271414Scindi /*ARGSUSED*/
2281414Scindi static int
pkg_fmri_create_meth(topo_mod_t * mp,tnode_t * node,topo_version_t version,nvlist_t * in,nvlist_t ** out)2291414Scindi pkg_fmri_create_meth(topo_mod_t *mp, tnode_t *node, topo_version_t version,
2301414Scindi nvlist_t *in, nvlist_t **out)
2311414Scindi {
2321414Scindi nvlist_t *args = NULL;
2331414Scindi char *path;
2341414Scindi
2351414Scindi if (version > TOPO_METH_FMRI_VERSION)
2361414Scindi return (topo_mod_seterrno(mp, EMOD_VER_NEW));
2371414Scindi
2381414Scindi if (nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_NVL, &args) != 0 ||
2391414Scindi nvlist_lookup_string(args, "path", &path) != 0) {
2401414Scindi topo_mod_dprintf(mp, "no path string in method argument\n");
2411414Scindi return (topo_mod_seterrno(mp, EMOD_METHOD_INVAL));
2421414Scindi }
2431414Scindi
2441414Scindi if ((*out = pkg_fmri_create(mp, path)) == NULL)
2451414Scindi return (-1);
2461414Scindi return (0);
2471414Scindi }
2487197Sstephh
2497197Sstephh static ssize_t
fmri_nvl2str(nvlist_t * nvl,char * buf,size_t buflen)2507197Sstephh fmri_nvl2str(nvlist_t *nvl, char *buf, size_t buflen)
2517197Sstephh {
2527197Sstephh nvlist_t *anvl = NULL;
25310462SSean.Ye@Sun.COM nvpair_t *apair;
2547197Sstephh uint8_t version;
2557197Sstephh ssize_t size = 0;
25610462SSean.Ye@Sun.COM char *pkgname = NULL, *aname, *aval;
2577197Sstephh int err;
2587197Sstephh
2597197Sstephh if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 ||
2607197Sstephh version > FM_PKG_SCHEME_VERSION)
2617197Sstephh return (-1);
2627197Sstephh
2637197Sstephh /* Get authority, if present */
2647197Sstephh err = nvlist_lookup_nvlist(nvl, FM_FMRI_AUTHORITY, &anvl);
2657197Sstephh if (err != 0 && err != ENOENT)
2667197Sstephh return (-1);
2677197Sstephh
2687197Sstephh /*
2697197Sstephh * For brevity, we only include the pkgname and any authority
2707197Sstephh * info present in the FMRI in our output string. The FMRI
2717197Sstephh * also has data on the package directory and version.
2727197Sstephh */
2737197Sstephh err = nvlist_lookup_string(nvl, FM_FMRI_PKG_INST, &pkgname);
2747197Sstephh if (err != 0 || pkgname == NULL)
2757197Sstephh return (-1);
2767197Sstephh
2777197Sstephh /* pkg:// */
2787197Sstephh topo_fmristr_build(&size, buf, buflen, FM_FMRI_SCHEME_PKG, NULL, "://");
2797197Sstephh
2807197Sstephh /* authority, if any */
28110462SSean.Ye@Sun.COM if (anvl != NULL) {
28210462SSean.Ye@Sun.COM for (apair = nvlist_next_nvpair(anvl, NULL);
28310462SSean.Ye@Sun.COM apair != NULL; apair = nvlist_next_nvpair(anvl, apair)) {
28410462SSean.Ye@Sun.COM if (nvpair_type(apair) != DATA_TYPE_STRING ||
28510462SSean.Ye@Sun.COM nvpair_value_string(apair, &aval) != 0)
28610462SSean.Ye@Sun.COM continue;
28710462SSean.Ye@Sun.COM aname = nvpair_name(apair);
28810462SSean.Ye@Sun.COM topo_fmristr_build(&size, buf, buflen, ":", NULL, NULL);
28910462SSean.Ye@Sun.COM topo_fmristr_build(&size, buf, buflen, "=",
29010462SSean.Ye@Sun.COM aname, aval);
29110462SSean.Ye@Sun.COM }
29210462SSean.Ye@Sun.COM }
2937197Sstephh
2947197Sstephh /* pkg-name part */
2957197Sstephh topo_fmristr_build(&size, buf, buflen, pkgname, "/", NULL);
2967197Sstephh
2977197Sstephh return (size);
2987197Sstephh }
2997197Sstephh
3007197Sstephh /*ARGSUSED*/
3017197Sstephh static int
pkg_fmri_nvl2str(topo_mod_t * mod,tnode_t * node,topo_version_t version,nvlist_t * nvl,nvlist_t ** out)3027197Sstephh pkg_fmri_nvl2str(topo_mod_t *mod, tnode_t *node, topo_version_t version,
3037197Sstephh nvlist_t *nvl, nvlist_t **out)
3047197Sstephh {
3057197Sstephh ssize_t len;
3067197Sstephh char *name = NULL;
3077197Sstephh nvlist_t *fmristr;
3087197Sstephh
3097197Sstephh if (version > TOPO_METH_NVL2STR_VERSION)
3107197Sstephh return (topo_mod_seterrno(mod, EMOD_VER_NEW));
3117197Sstephh
3127197Sstephh if ((len = fmri_nvl2str(nvl, NULL, 0)) == 0 ||
3137197Sstephh (name = topo_mod_alloc(mod, len + 1)) == NULL ||
3147197Sstephh fmri_nvl2str(nvl, name, len + 1) == 0) {
3157197Sstephh if (name != NULL)
3167197Sstephh topo_mod_free(mod, name, len + 1);
3177197Sstephh return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
3187197Sstephh }
3197197Sstephh
3207197Sstephh if (topo_mod_nvalloc(mod, &fmristr, NV_UNIQUE_NAME) != 0)
3217197Sstephh return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
3227197Sstephh if (nvlist_add_string(fmristr, "fmri-string", name) != 0) {
3237197Sstephh topo_mod_free(mod, name, len + 1);
3247197Sstephh nvlist_free(fmristr);
3257197Sstephh return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
3267197Sstephh }
3277197Sstephh topo_mod_free(mod, name, len + 1);
3287197Sstephh *out = fmristr;
3297197Sstephh
3307197Sstephh return (0);
3317197Sstephh }
332