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