xref: /illumos-gate/usr/src/lib/fm/topo/libtopo/common/pkg.c (revision 6a634c9d)
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
pkg_init(topo_mod_t * mod,topo_version_t version)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
pkg_fini(topo_mod_t * mod)917aec1d6eScindi pkg_fini(topo_mod_t *mod)
927aec1d6eScindi {
937aec1d6eScindi 	topo_mod_unregister(mod);
947aec1d6eScindi }
957aec1d6eScindi 
967aec1d6eScindi /*ARGSUSED*/
977aec1d6eScindi 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)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
pkg_release(topo_mod_t * mod,tnode_t * node)1117aec1d6eScindi pkg_release(topo_mod_t *mod, tnode_t *node)
1127aec1d6eScindi {
1137aec1d6eScindi 	topo_method_unregister_all(mod, node);
1147aec1d6eScindi }
1157aec1d6eScindi 
1167aec1d6eScindi static int
read_thru(topo_mod_t * mp,FILE * fp,const char * substr)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 *
construct_fru_fmri(topo_mod_t * mp,const char * pkgname,FILE * fp)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 *
pkg_fmri_create(topo_mod_t * mp,const char * path)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
pkg_fmri_create_meth(topo_mod_t * mp,tnode_t * node,topo_version_t version,nvlist_t * in,nvlist_t ** out)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
fmri_nvl2str(nvlist_t * nvl,char * buf,size_t buflen)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
pkg_fmri_nvl2str(topo_mod_t * mod,tnode_t * node,topo_version_t version,nvlist_t * nvl,nvlist_t ** out)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