/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * s10_support is a small cli utility used to perform some brand-specific * tasks when verifying a zone. This utility is not intended to be called * by users - it is intended to be invoked by the zones utilities. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static void s10_err(char *msg, ...) __NORETURN; static void usage(void) __NORETURN; /* * XXX This is a temporary flag for the initial release to enable the * use of features which are not yet tested or fully implemented. */ static boolean_t override = B_FALSE; static char *bname = NULL; #define PKGINFO_RD_LEN 128 #define PATCHLIST "PATCHLIST=" #if !defined(TEXT_DOMAIN) /* should be defined by cc -D */ #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it wasn't */ #endif /*PRINTFLIKE1*/ static void s10_err(char *msg, ...) { char buf[1024]; va_list ap; va_start(ap, msg); (void) vsnprintf(buf, sizeof (buf), msg, ap); va_end(ap); /* This needs go to stdout so the msgs show up through zoneadm. */ (void) printf("Error: %s\n", buf); exit(1); /*NOTREACHED*/ } static int s10_verify(char *xmlfile) { zone_dochandle_t handle; struct zone_fstab fstab; struct zone_devtab devtab; zone_iptype_t iptype; struct zone_dstab dstab; if ((handle = zonecfg_init_handle()) == NULL) s10_err(gettext("internal libzonecfg.so.1 error"), 0); if (zonecfg_get_xml_handle(xmlfile, handle) != Z_OK) { zonecfg_fini_handle(handle); s10_err(gettext("zonecfg provided an invalid XML file")); } /* * Check to see whether the zone has any inherit-pkg-dirs * configured. */ if (zonecfg_setipdent(handle) != Z_OK) { zonecfg_fini_handle(handle); s10_err(gettext("zonecfg provided an invalid XML file")); } if (zonecfg_getipdent(handle, &fstab) == Z_OK) { zonecfg_fini_handle(handle); s10_err(gettext("solaris10 zones do not support " "inherit-pkg-dirs")); } (void) zonecfg_endipdent(handle); /* * Check to see whether the zone has any unsupported devices * configured. * * The audio framework has changed in Solaris Next as compared to * S10. Data indicates the less than 1/10 of 1 percent of zones * are using /dev/sound. Given the low usage vs. the effort to * provide emulation, /dev/sound is currently disallowed. We can * revisit this if there is enough demand. */ if (zonecfg_setdevent(handle) != Z_OK) { zonecfg_fini_handle(handle); s10_err(gettext("zonecfg provided an invalid XML file")); } if (zonecfg_getdevent(handle, &devtab) == Z_OK) { if (strncmp(devtab.zone_dev_match, "/dev/sound", 10) == 0 && !override) { zonecfg_fini_handle(handle); s10_err(gettext("solaris10 zones do not currently " "support /dev/sound")); } } (void) zonecfg_enddevent(handle); /* * Check to see whether the zone has any experimental features * configured. */ if (zonecfg_get_iptype(handle, &iptype) == Z_OK && iptype == ZS_EXCLUSIVE && !override) { zonecfg_fini_handle(handle); s10_err(gettext("solaris10 zones do not currently support " "exclusive ip-type stacks")); } if (zonecfg_setdsent(handle) != Z_OK) { zonecfg_fini_handle(handle); s10_err(gettext("zonecfg provided an invalid XML file")); } if (zonecfg_getdsent(handle, &dstab) == Z_OK && !override) { zonecfg_fini_handle(handle); s10_err(gettext("solaris10 zones do not currently support " "delegated datasets")); } (void) zonecfg_enddsent(handle); zonecfg_fini_handle(handle); return (0); } /* * Read an entry from a pkginfo file. Some of these lines can * either be arbitrarily long or be continued by a backslash at the end of * the line. This function coalesces lines that are longer than the read * buffer, and lines that are continued, into one buffer which is returned. * The caller must free this memory. NULL is returned when we hit EOF or * if we run out of memory (errno is set to ENOMEM). */ static char * read_pkg_data(FILE *fp) { char *start; char *inp; char *p; int char_cnt = 0; errno = 0; if ((start = (char *)malloc(PKGINFO_RD_LEN)) == NULL) { errno = ENOMEM; return (NULL); } inp = start; while ((p = fgets(inp, PKGINFO_RD_LEN, fp)) != NULL) { int len; len = strlen(inp); if (inp[len - 1] == '\n' && (len == 1 || inp[len - 2] != '\\')) { char_cnt = len; break; } if (inp[len - 1] == '\n' && inp[len - 2] == '\\') char_cnt += len - 2; else char_cnt += PKGINFO_RD_LEN - 1; if ((p = realloc(start, char_cnt + PKGINFO_RD_LEN)) == NULL) { errno = ENOMEM; break; } start = p; inp = start + char_cnt; } if (errno == ENOMEM || (p == NULL && char_cnt == 0)) { free(start); start = NULL; } return (start); } /* * Read the SUNWcakr pkginfo file and get the PATCHLIST for the pkg. */ static int get_ku_patchlist(char *zonename, char **patchlist) { char zonepath[MAXPATHLEN]; char pkginfo[MAXPATHLEN]; FILE *fp; char *buf; int err = 0; if (zone_get_zonepath(zonename, zonepath, sizeof (zonepath)) != Z_OK) s10_err(gettext("error getting zone's path")); if (snprintf(pkginfo, sizeof (pkginfo), "%s/root/var/sadm/pkg/SUNWcakr/pkginfo", zonepath) >= sizeof (pkginfo)) s10_err(gettext("error formating pkg path")); if ((fp = fopen(pkginfo, "r")) == NULL) return (errno); while ((buf = read_pkg_data(fp)) != NULL) { if (strncmp(buf, PATCHLIST, sizeof (PATCHLIST) - 1) == 0) { int len; /* remove trailing newline */ len = strlen(buf); buf[len - 1] = '\0'; if ((*patchlist = strdup(buf + sizeof (PATCHLIST) - 1)) == NULL) err = ENOMEM; free(buf); break; } free(buf); } (void) fclose(fp); return (err); } /* * Verify that we have the minimum KU needed. * Note that KU patches are accumulative so future KUs will still deliver * 141444 or 141445. */ static boolean_t have_valid_ku(char *zonename) { char *p; char *lastp; char *pstr; char *patchlist = NULL; int i; char *vers_table[] = { "141444-09", "141445-09", NULL}; if (get_ku_patchlist(zonename, &patchlist) != 0 || patchlist == NULL) return (B_FALSE); pstr = patchlist; while ((p = strtok_r(pstr, " ", &lastp)) != NULL) { for (i = 0; vers_table[i] != NULL; i++) if (strcmp(p, vers_table[i]) == 0) return (B_TRUE); pstr = NULL; } return (B_FALSE); } /* * Get the emulation version from the /usr/lib/brand/solaris10/version file * in either the global zone or the non-global zone. */ static int get_emul_version_number(char *verspath) { int vers = 0; FILE *fp; char buf[LINE_MAX]; /* If the file doesn't exist, assume version 0 */ if ((fp = fopen(verspath, "r")) == NULL) return (vers); while (fgets(buf, sizeof (buf), fp) != NULL) { if (buf[0] == '#') continue; errno = 0; vers = strtol(buf, (char **)NULL, 10); if (errno != 0) { (void) fclose(fp); s10_err(gettext("error reading minimum version")); } } (void) fclose(fp); return (vers); } /* * Get the current emulation version that is implemented. */ static int get_current_emul_version() { return (get_emul_version_number("/usr/lib/brand/solaris10/version")); } /* * Get the emulation version that the S10 image requires. This * reads the optional /usr/lib/brand/solaris10/version file that might * exist on Solaris 10. That file specifies the minimal solaris10 brand * emulation version that the specific release of S10 requires. If no * minimal version is specified, the initial emulation remains compatible. * * If a new KU patch is created which needs different handling by the * emulation, then the S10 /usr/lib/brand/solaris10/version file should be * updated to specify a new version. */ static int get_image_emul_rqd_version(char *zonename) { char zonepath[MAXPATHLEN]; char verspath[MAXPATHLEN]; if (zone_get_zonepath(zonename, zonepath, sizeof (zonepath)) != Z_OK) s10_err(gettext("error getting zone's path")); if (snprintf(verspath, sizeof (verspath), "%s/root/usr/lib/brand/solaris10/version", zonepath) >= sizeof (verspath)) s10_err(gettext("error formating version path")); return (get_emul_version_number(verspath)); } static void fail_xvm() { char buf[80]; if (sysinfo(SI_PLATFORM, buf, sizeof (buf)) != -1 && strcmp(buf, "i86xpv") == 0 && !override) s10_err(gettext("running the solaris10 brand " "in a paravirtualized\ndomain is currently not supported")); } static int s10_boot(char *zonename) { zoneid_t zoneid; int emul_vers; int rqd_emul_vers; if (!have_valid_ku(zonename)) s10_err(gettext("The installed version of Solaris 10 is " "not supported")); emul_vers = get_current_emul_version(); rqd_emul_vers = get_image_emul_rqd_version(zonename); if (rqd_emul_vers > emul_vers) s10_err(gettext("The zone's version of Solaris 10 is " "incompatible with the current version of the solaris10 " "brand.")); if ((zoneid = getzoneidbyname(zonename)) < 0) s10_err(gettext("unable to get zoneid")); if (zone_setattr(zoneid, S10_EMUL_VERSION_NUM, &rqd_emul_vers, sizeof (int)) == -1) s10_err(gettext("error setting zone's emulation version " "property")); fail_xvm(); return (0); } static void usage() { (void) fprintf(stderr, gettext( "usage:\t%s verify \n" "\t%s boot\n"), bname, bname); exit(1); } int main(int argc, char *argv[]) { (void) setlocale(LC_ALL, ""); (void) textdomain(TEXT_DOMAIN); bname = basename(argv[0]); if (argc != 3) usage(); /* * XXX This is a temporary env variable for the initial release to * enable the use of features which are not yet tested or fully * implemented. */ if (getenv("S10BRAND_TEST") != NULL) override = B_TRUE; if (strcmp(argv[1], "verify") == 0) return (s10_verify(argv[2])); if (strcmp(argv[1], "boot") == 0) return (s10_boot(argv[2])); usage(); /*NOTREACHED*/ }