xref: /onnv-gate/usr/src/lib/fm/topo/libtopo/common/pkg.c (revision 12967:ab9ae749152f)
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