1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <limits.h> 30 #include <strings.h> 31 #include <string.h> 32 #include <unistd.h> 33 #include <stdio.h> 34 #include <alloca.h> 35 #include <libnvpair.h> 36 #include <fm/topo_mod.h> 37 #include <sys/fm/protocol.h> 38 39 #include <fcntl.h> 40 #include <sys/types.h> 41 #include <sys/stat.h> 42 #include <sys/objfs.h> 43 #include <sys/modctl.h> 44 #include <libelf.h> 45 #include <gelf.h> 46 47 #include <topo_method.h> 48 #include <topo_subr.h> 49 #include <pkg.h> 50 51 #define BUFLEN (2 * PATH_MAX) 52 53 static int pkg_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t, 54 topo_instance_t, void *, void *); 55 static void pkg_release(topo_mod_t *, tnode_t *); 56 static int pkg_fmri_create_meth(topo_mod_t *, tnode_t *, topo_version_t, 57 nvlist_t *, nvlist_t **); 58 static int pkg_fmri_nvl2str(topo_mod_t *, tnode_t *, topo_version_t, 59 nvlist_t *, nvlist_t **); 60 61 static const topo_method_t pkg_methods[] = { 62 { TOPO_METH_FMRI, TOPO_METH_FMRI_DESC, TOPO_METH_FMRI_VERSION, 63 TOPO_STABILITY_INTERNAL, pkg_fmri_create_meth }, 64 { TOPO_METH_NVL2STR, TOPO_METH_NVL2STR_DESC, TOPO_METH_NVL2STR_VERSION, 65 TOPO_STABILITY_INTERNAL, pkg_fmri_nvl2str }, 66 { NULL } 67 }; 68 69 static const topo_modops_t pkg_ops = 70 { pkg_enum, pkg_release }; 71 static const topo_modinfo_t pkg_info = 72 { "pkg", FM_FMRI_SCHEME_PKG, PKG_VERSION, &pkg_ops }; 73 74 int 75 pkg_init(topo_mod_t *mod, topo_version_t version) 76 { 77 if (getenv("TOPOPKGDEBUG")) 78 topo_mod_setdebug(mod); 79 topo_mod_dprintf(mod, "initializing mod builtin\n"); 80 81 if (version != PKG_VERSION) 82 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 83 84 if (topo_mod_register(mod, &pkg_info, TOPO_VERSION) != 0) { 85 topo_mod_dprintf(mod, "failed to register pkg_info: " 86 "%s\n", topo_mod_errmsg(mod)); 87 return (-1); 88 } 89 90 return (0); 91 } 92 93 void 94 pkg_fini(topo_mod_t *mod) 95 { 96 topo_mod_unregister(mod); 97 } 98 99 /*ARGSUSED*/ 100 static int 101 pkg_enum(topo_mod_t *mod, tnode_t *pnode, const char *name, 102 topo_instance_t min, topo_instance_t max, void *notused1, void *notused2) 103 { 104 (void) topo_method_register(mod, pnode, pkg_methods); 105 return (0); 106 } 107 108 static void 109 pkg_release(topo_mod_t *mod, tnode_t *node) 110 { 111 topo_method_unregister_all(mod, node); 112 } 113 114 static int 115 read_thru(topo_mod_t *mp, FILE *fp, const char *substr) 116 { 117 char *tmpbuf = alloca(2 * MAXPATHLEN); 118 int notfound = 1; 119 120 while (fgets(tmpbuf, 2 * MAXPATHLEN, fp) != NULL) { 121 if (substr == NULL) 122 topo_mod_dprintf(mp, "%s", tmpbuf); 123 else if (strstr(tmpbuf, substr) != NULL) { 124 notfound = 0; 125 break; 126 } 127 } 128 return (notfound); 129 } 130 131 static nvlist_t * 132 construct_fru_fmri(topo_mod_t *mp, const char *pkgname, FILE *fp) 133 { 134 nvlist_t *f = NULL; 135 char *tmpbuf = alloca(BUFLEN); 136 char *pkgdir = NULL; 137 char *pkgver = NULL; 138 char *token; 139 int e; 140 141 while (fgets(tmpbuf, BUFLEN, fp) != NULL) { 142 if (strstr(tmpbuf, "VERSION:") != NULL) { 143 token = strtok(tmpbuf, ":"); 144 token = strtok(NULL, ": \t\n"); 145 pkgver = topo_mod_strdup(mp, token); 146 } else if (strstr(tmpbuf, "BASEDIR:") != NULL) { 147 token = strtok(tmpbuf, ":"); 148 token = strtok(NULL, ": \t\n"); 149 pkgdir = topo_mod_strdup(mp, token); 150 } 151 } 152 153 if (pkgdir == NULL || pkgver == NULL) { 154 (void) topo_mod_seterrno(mp, EMOD_METHOD_INVAL); 155 goto fmrileave; 156 } 157 158 if (topo_mod_nvalloc(mp, &f, NV_UNIQUE_NAME) != 0) { 159 (void) topo_mod_seterrno(mp, EMOD_FMRI_NVL); 160 goto fmrileave; 161 } 162 163 e = nvlist_add_string(f, FM_FMRI_SCHEME, FM_FMRI_SCHEME_PKG); 164 e |= nvlist_add_uint8(f, FM_VERSION, FM_PKG_SCHEME_VERSION); 165 e |= nvlist_add_string(f, FM_FMRI_PKG_BASEDIR, pkgdir); 166 e |= nvlist_add_string(f, FM_FMRI_PKG_INST, pkgname); 167 e |= nvlist_add_string(f, FM_FMRI_PKG_VERSION, pkgver); 168 if (e == 0) 169 goto fmrileave; 170 171 topo_mod_dprintf(mp, "construction of pkg nvl failed"); 172 (void) topo_mod_seterrno(mp, EMOD_FMRI_NVL); 173 nvlist_free(f); 174 f = NULL; 175 176 fmrileave: 177 if (pkgdir != NULL) 178 topo_mod_strfree(mp, pkgdir); 179 if (pkgver != NULL) 180 topo_mod_strfree(mp, pkgver); 181 182 return (f); 183 } 184 185 #define PKGINFO_CMD "LC_MESSAGES= /usr/bin/pkginfo -l %s 2>/dev/null" 186 #define PKGCHK_CMD "LC_MESSAGES= /usr/sbin/pkgchk -lp %s 2>/dev/null" 187 #define PKG_KEYPHRASE "Referenced by the following packages:" 188 189 static nvlist_t * 190 pkg_fmri_create(topo_mod_t *mp, const char *path) 191 { 192 static char tmpbuf[BUFLEN]; 193 char *findpkgname; 194 char *pkgname = NULL; 195 FILE *pcout; 196 nvlist_t *out = NULL; 197 198 (void) snprintf(tmpbuf, BUFLEN, PKGCHK_CMD, path); 199 topo_mod_dprintf(mp, "popen of %s\n", tmpbuf); 200 pcout = popen(tmpbuf, "r"); 201 if (read_thru(mp, pcout, PKG_KEYPHRASE)) { 202 (void) pclose(pcout); 203 goto pfc_bail; 204 } 205 (void) fgets(tmpbuf, BUFLEN, pcout); 206 (void) pclose(pcout); 207 topo_mod_dprintf(mp, "%s", tmpbuf); 208 209 if ((findpkgname = strtok(tmpbuf, " \n")) == NULL) 210 goto pfc_bail; 211 pkgname = topo_mod_strdup(mp, findpkgname); 212 213 (void) snprintf(tmpbuf, BUFLEN, PKGINFO_CMD, pkgname); 214 topo_mod_dprintf(mp, "popen of %s\n", tmpbuf); 215 pcout = popen(tmpbuf, "r"); 216 out = construct_fru_fmri(mp, pkgname, pcout); 217 (void) pclose(pcout); 218 219 pfc_bail: 220 if (pkgname != NULL) 221 topo_mod_strfree(mp, pkgname); 222 return (out); 223 } 224 225 /*ARGSUSED*/ 226 static int 227 pkg_fmri_create_meth(topo_mod_t *mp, tnode_t *node, topo_version_t version, 228 nvlist_t *in, nvlist_t **out) 229 { 230 nvlist_t *args = NULL; 231 char *path; 232 233 if (version > TOPO_METH_FMRI_VERSION) 234 return (topo_mod_seterrno(mp, EMOD_VER_NEW)); 235 236 if (nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_NVL, &args) != 0 || 237 nvlist_lookup_string(args, "path", &path) != 0) { 238 topo_mod_dprintf(mp, "no path string in method argument\n"); 239 return (topo_mod_seterrno(mp, EMOD_METHOD_INVAL)); 240 } 241 242 if ((*out = pkg_fmri_create(mp, path)) == NULL) 243 return (-1); 244 return (0); 245 } 246 247 static ssize_t 248 fmri_nvl2str(nvlist_t *nvl, char *buf, size_t buflen) 249 { 250 nvlist_t *anvl = NULL; 251 uint8_t version; 252 ssize_t size = 0; 253 char *pkgname = NULL; 254 char *achas = NULL; 255 char *adom = NULL; 256 char *aprod = NULL; 257 char *asrvr = NULL; 258 char *ahost = NULL; 259 int err; 260 261 if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 || 262 version > FM_PKG_SCHEME_VERSION) 263 return (-1); 264 265 /* Get authority, if present */ 266 err = nvlist_lookup_nvlist(nvl, FM_FMRI_AUTHORITY, &anvl); 267 if (err != 0 && err != ENOENT) 268 return (-1); 269 270 /* 271 * For brevity, we only include the pkgname and any authority 272 * info present in the FMRI in our output string. The FMRI 273 * also has data on the package directory and version. 274 */ 275 err = nvlist_lookup_string(nvl, FM_FMRI_PKG_INST, &pkgname); 276 if (err != 0 || pkgname == NULL) 277 return (-1); 278 279 if (anvl != NULL) { 280 (void) nvlist_lookup_string(anvl, 281 FM_FMRI_AUTH_PRODUCT, &aprod); 282 (void) nvlist_lookup_string(anvl, 283 FM_FMRI_AUTH_CHASSIS, &achas); 284 (void) nvlist_lookup_string(anvl, 285 FM_FMRI_AUTH_DOMAIN, &adom); 286 (void) nvlist_lookup_string(anvl, 287 FM_FMRI_AUTH_SERVER, &asrvr); 288 (void) nvlist_lookup_string(anvl, 289 FM_FMRI_AUTH_HOST, &ahost); 290 } 291 292 /* pkg:// */ 293 topo_fmristr_build(&size, buf, buflen, FM_FMRI_SCHEME_PKG, NULL, "://"); 294 295 /* authority, if any */ 296 if (aprod != NULL) 297 topo_fmristr_build(&size, buf, buflen, aprod, 298 FM_FMRI_AUTH_PRODUCT "=", NULL); 299 if (achas != NULL) 300 topo_fmristr_build(&size, buf, buflen, achas, 301 FM_FMRI_AUTH_CHASSIS "=", NULL); 302 if (adom != NULL) 303 topo_fmristr_build(&size, buf, buflen, adom, 304 FM_FMRI_AUTH_DOMAIN "=", NULL); 305 if (asrvr != NULL) 306 topo_fmristr_build(&size, buf, buflen, asrvr, 307 FM_FMRI_AUTH_SERVER "=", NULL); 308 if (ahost != NULL) 309 topo_fmristr_build(&size, buf, buflen, ahost, 310 FM_FMRI_AUTH_HOST "=", NULL); 311 312 /* pkg-name part */ 313 topo_fmristr_build(&size, buf, buflen, pkgname, "/", NULL); 314 315 return (size); 316 } 317 318 /*ARGSUSED*/ 319 static int 320 pkg_fmri_nvl2str(topo_mod_t *mod, tnode_t *node, topo_version_t version, 321 nvlist_t *nvl, nvlist_t **out) 322 { 323 ssize_t len; 324 char *name = NULL; 325 nvlist_t *fmristr; 326 327 if (version > TOPO_METH_NVL2STR_VERSION) 328 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 329 330 if ((len = fmri_nvl2str(nvl, NULL, 0)) == 0 || 331 (name = topo_mod_alloc(mod, len + 1)) == NULL || 332 fmri_nvl2str(nvl, name, len + 1) == 0) { 333 if (name != NULL) 334 topo_mod_free(mod, name, len + 1); 335 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 336 } 337 338 if (topo_mod_nvalloc(mod, &fmristr, NV_UNIQUE_NAME) != 0) 339 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 340 if (nvlist_add_string(fmristr, "fmri-string", name) != 0) { 341 topo_mod_free(mod, name, len + 1); 342 nvlist_free(fmristr); 343 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 344 } 345 topo_mod_free(mod, name, len + 1); 346 *out = fmristr; 347 348 return (0); 349 } 350