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