2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A/*
2N/A * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A/*
2N/A * s10_support is a small cli utility used to perform some brand-specific
2N/A * tasks when verifying a zone. This utility is not intended to be called
2N/A * by users - it is intended to be invoked by the zones utilities.
2N/A */
2N/A
2N/A#include <ctype.h>
2N/A#include <errno.h>
2N/A#include <fcntl.h>
2N/A#include <libgen.h>
2N/A#include <limits.h>
2N/A#include <s10_brand.h>
2N/A#include <stdarg.h>
2N/A#include <stdio.h>
2N/A#include <stdlib.h>
2N/A#include <string.h>
2N/A#include <strings.h>
2N/A#include <stropts.h>
2N/A#include <sys/stat.h>
2N/A#include <sys/types.h>
2N/A#include <sys/utsname.h>
2N/A#include <sys/varargs.h>
2N/A#include <unistd.h>
2N/A#include <libintl.h>
2N/A#include <locale.h>
2N/A#include <dirent.h>
2N/A#include <sys/systeminfo.h>
2N/A
2N/A#include <libzonecfg.h>
2N/A
2N/Astatic void s10_err(char *msg, ...) __NORETURN;
2N/Astatic void usage(void) __NORETURN;
2N/A
2N/A/*
2N/A * XXX This is a temporary flag for the initial release to enable the
2N/A * use of features which are not yet tested or fully implemented.
2N/A */
2N/Astatic boolean_t override = B_FALSE;
2N/A
2N/Astatic char *bname = NULL;
2N/A
2N/A/*
2N/A * DELETE_LIST_PATH represents the path to a solaris10-branded zone's "delete
2N/A * list", which is generated by patchrm when it needs to remove files after
2N/A * the zone reboots. See set_zone_emul_bitmap() below for additional details.
2N/A */
2N/A#define DELETE_LIST_PATH "/var/sadm/patch/.delete_list"
2N/A
2N/A#define PKGINFO_RD_LEN 128
2N/A#define PATCHLIST "PATCHLIST="
2N/A
2N/A#if !defined(TEXT_DOMAIN) /* should be defined by cc -D */
2N/A#define TEXT_DOMAIN "SYS_TEST" /* Use this only if it wasn't */
2N/A#endif
2N/A
2N/A/*PRINTFLIKE1*/
2N/Astatic void
2N/As10_err(char *msg, ...)
2N/A{
2N/A char buf[1024];
2N/A va_list ap;
2N/A
2N/A va_start(ap, msg);
2N/A (void) vsnprintf(buf, sizeof (buf), msg, ap);
2N/A va_end(ap);
2N/A
2N/A /* This needs go to stdout so the msgs show up through zoneadm. */
2N/A (void) printf("Error: %s\n", buf);
2N/A
2N/A exit(1);
2N/A /*NOTREACHED*/
2N/A}
2N/A
2N/Astatic int
2N/As10_verify(char *xmlfile)
2N/A{
2N/A zone_dochandle_t handle;
2N/A struct zone_devtab devtab;
2N/A
2N/A if ((handle = zonecfg_init_handle()) == NULL)
2N/A s10_err(gettext("internal libzonecfg.so.1 error"), 0);
2N/A
2N/A if (zonecfg_get_xml_handle(xmlfile, handle) != Z_OK) {
2N/A zonecfg_fini_handle(handle);
2N/A s10_err(gettext("zonecfg provided an invalid XML file"));
2N/A }
2N/A
2N/A /*
2N/A * Check to see whether the zone has any unsupported devices
2N/A * configured.
2N/A *
2N/A * The audio framework has changed in Solaris Next as compared to
2N/A * S10. Data indicates the less than 1/10 of 1 percent of zones
2N/A * are using /dev/sound. Given the low usage vs. the effort to
2N/A * provide emulation, /dev/sound is currently disallowed. We can
2N/A * revisit this if there is enough demand.
2N/A */
2N/A if (zonecfg_setdevent(handle) != Z_OK) {
2N/A zonecfg_fini_handle(handle);
2N/A s10_err(gettext("zonecfg provided an invalid XML file"));
2N/A }
2N/A if (zonecfg_getdevent(handle, &devtab) == Z_OK) {
2N/A if (strncmp(devtab.zone_dev_match, "/dev/sound", 10) == 0 &&
2N/A !override) {
2N/A zonecfg_fini_handle(handle);
2N/A s10_err(gettext("solaris10 zones do not currently "
2N/A "support /dev/sound"));
2N/A }
2N/A }
2N/A (void) zonecfg_enddevent(handle);
2N/A
2N/A zonecfg_fini_handle(handle);
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Read an entry from a pkginfo file. Some of these lines can
2N/A * either be arbitrarily long or be continued by a backslash at the end of
2N/A * the line. This function coalesces lines that are longer than the read
2N/A * buffer, and lines that are continued, into one buffer which is returned.
2N/A * The caller must free this memory. NULL is returned when we hit EOF or
2N/A * if we run out of memory (errno is set to ENOMEM).
2N/A */
2N/Astatic char *
2N/Aread_pkg_data(FILE *fp)
2N/A{
2N/A char *start;
2N/A char *inp;
2N/A char *p;
2N/A int char_cnt = 0;
2N/A
2N/A errno = 0;
2N/A if ((start = (char *)malloc(PKGINFO_RD_LEN)) == NULL) {
2N/A errno = ENOMEM;
2N/A return (NULL);
2N/A }
2N/A
2N/A inp = start;
2N/A while ((p = fgets(inp, PKGINFO_RD_LEN, fp)) != NULL) {
2N/A int len;
2N/A
2N/A len = strlen(inp);
2N/A if (inp[len - 1] == '\n' &&
2N/A (len == 1 || inp[len - 2] != '\\')) {
2N/A char_cnt = len;
2N/A break;
2N/A }
2N/A
2N/A if (inp[len - 1] == '\n' && inp[len - 2] == '\\')
2N/A char_cnt += len - 2;
2N/A else
2N/A char_cnt += PKGINFO_RD_LEN - 1;
2N/A
2N/A if ((p = realloc(start, char_cnt + PKGINFO_RD_LEN)) == NULL) {
2N/A errno = ENOMEM;
2N/A break;
2N/A }
2N/A
2N/A start = p;
2N/A inp = start + char_cnt;
2N/A }
2N/A
2N/A if (errno == ENOMEM || (p == NULL && char_cnt == 0)) {
2N/A free(start);
2N/A start = NULL;
2N/A }
2N/A
2N/A return (start);
2N/A}
2N/A
2N/A/*
2N/A * Read the SUNWcakr pkginfo file and get the PATCHLIST for the pkg.
2N/A */
2N/Astatic int
2N/Aget_ku_patchlist(char *zonepath, char **patchlist)
2N/A{
2N/A char pkginfo[MAXPATHLEN];
2N/A FILE *fp;
2N/A char *buf;
2N/A int err = 0;
2N/A
2N/A if (snprintf(pkginfo, sizeof (pkginfo),
2N/A "%s/root/var/sadm/pkg/SUNWcakr/pkginfo", zonepath)
2N/A >= sizeof (pkginfo))
2N/A s10_err(gettext("error formating pkg path"));
2N/A
2N/A if ((fp = fopen(pkginfo, "r")) == NULL)
2N/A return (errno);
2N/A
2N/A while ((buf = read_pkg_data(fp)) != NULL) {
2N/A if (strncmp(buf, PATCHLIST, sizeof (PATCHLIST) - 1) == 0) {
2N/A int len;
2N/A
2N/A /* remove trailing newline */
2N/A len = strlen(buf);
2N/A buf[len - 1] = '\0';
2N/A
2N/A if ((*patchlist =
2N/A strdup(buf + sizeof (PATCHLIST) - 1)) == NULL)
2N/A err = ENOMEM;
2N/A
2N/A free(buf);
2N/A break;
2N/A }
2N/A
2N/A free(buf);
2N/A }
2N/A (void) fclose(fp);
2N/A
2N/A return (err);
2N/A}
2N/A
2N/A/*
2N/A * Verify that we have the minimum KU needed, which is the s10u9 KU.
2N/A * Note that KU patches are accumulative so future KUs will still deliver
2N/A * these.
2N/A */
2N/Astatic boolean_t
2N/Ahave_valid_ku(char *zonename)
2N/A{
2N/A char *p;
2N/A char *lastp;
2N/A char *pstr;
2N/A char *patchlist = NULL;
2N/A char zonepath[MAXPATHLEN];
2N/A char sanity_skip[MAXPATHLEN];
2N/A struct stat64 buf;
2N/A char *vers_table[] = {
2N/A "142909-17",
2N/A "142910-17"};
2N/A
2N/A if (zone_get_zonepath(zonename, zonepath, sizeof (zonepath)) != Z_OK)
2N/A s10_err(gettext("error getting zone's path"));
2N/A
2N/A /*
2N/A * If the zone was installed to bypass sanity checking for internal
2N/A * testing purposes, just return success.
2N/A */
2N/A if (snprintf(sanity_skip, sizeof (sanity_skip), "%s/root/.sanity_skip",
2N/A zonepath) >= sizeof (sanity_skip))
2N/A s10_err(gettext("error formating file path"));
2N/A
2N/A if (stat64(sanity_skip, &buf) == 0)
2N/A return (B_TRUE);
2N/A
2N/A if (get_ku_patchlist(zonepath, &patchlist) != 0 || patchlist == NULL)
2N/A return (B_FALSE);
2N/A
2N/A
2N/A pstr = patchlist;
2N/A while ((p = strtok_r(pstr, " ", &lastp)) != NULL) {
2N/A if (strcmp(p, vers_table[0]) == 0 ||
2N/A strcmp(p, vers_table[1]) == 0)
2N/A return (B_TRUE);
2N/A pstr = NULL;
2N/A }
2N/A return (B_FALSE);
2N/A}
2N/A
2N/A/*
2N/A * Convert the specified file basename into an unsigned integer. If the
2N/A * basename contains characters that cannot be converted into digits or the
2N/A * basename isn't NULL or newline-terminated, then this function returns
2N/A * the unsigned equivalent of -1.
2N/A */
2N/Astatic unsigned int
2N/Abasename_to_uint(const char *basenamep)
2N/A{
2N/A char *filename_endptr;
2N/A unsigned int bit_index;
2N/A
2N/A errno = 0;
2N/A bit_index = (unsigned int)strtoul(basenamep, &filename_endptr, 10);
2N/A if (errno != 0 || (*filename_endptr != '\n' &&
2N/A *filename_endptr != '\0') || filename_endptr == basenamep)
2N/A return ((unsigned int)-1);
2N/A return (bit_index);
2N/A}
2N/A
2N/A/*
2N/A * Determine which features/behaviors should be emulated and construct a bitmap
2N/A * representing the results. Associate the bitmap with the zone so that
2N/A * the brand's emulation library will be able to retrieve the bitmap and
2N/A * determine how the zone's process' behaviors should be emulated.
2N/A *
2N/A * This function does not return if an error occurs.
2N/A */
2N/Astatic void
2N/Aset_zone_emul_bitmap(char *zonename)
2N/A{
2N/A char zoneroot[MAXPATHLEN];
2N/A char path[MAXPATHLEN];
2N/A DIR *req_emulation_dirp;
2N/A struct dirent *emul_feature_filep;
2N/A s10_emul_bitmap_t bitmap;
2N/A unsigned int bit_index;
2N/A zoneid_t zoneid;
2N/A FILE *delete_listp;
2N/A
2N/A /*
2N/A * If the Solaris 10 directory containing emulation feature files
2N/A * doesn't exist in the zone, then assume that it only needs the
2N/A * most basic emulation and, therefore, doesn't need a bitmap.
2N/A */
2N/A if (zone_get_rootpath(zonename, zoneroot, sizeof (zoneroot)) != Z_OK)
2N/A s10_err(gettext("error getting zone's path"));
2N/A if (snprintf(path, sizeof (path), "%s" S10_REQ_EMULATION_DIR,
2N/A zoneroot) >= sizeof (path))
2N/A s10_err(gettext("zone's emulation versioning directory's path "
2N/A "%s" S10_REQ_EMULATION_DIR " is too long"), zoneroot);
2N/A if ((req_emulation_dirp = opendir(path)) == NULL)
2N/A return;
2N/A bzero(bitmap, sizeof (bitmap));
2N/A
2N/A /*
2N/A * Iterate over the contents of the directory and determine which
2N/A * features the brand should emulate for this zone.
2N/A */
2N/A while ((emul_feature_filep = readdir(req_emulation_dirp)) != NULL) {
2N/A if (strcmp(emul_feature_filep->d_name, ".") == 0 ||
2N/A strcmp(emul_feature_filep->d_name, "..") == 0)
2N/A continue;
2N/A
2N/A /*
2N/A * Convert the file's name to an unsigned integer. Ignore
2N/A * files whose names aren't unsigned integers.
2N/A */
2N/A bit_index = basename_to_uint(emul_feature_filep->d_name);
2N/A if (bit_index == (unsigned int)-1)
2N/A continue;
2N/A
2N/A /*
2N/A * Determine if the brand can emulate the feature specified
2N/A * by bit_index.
2N/A */
2N/A if (bit_index >= S10_NUM_EMUL_FEATURES) {
2N/A /*
2N/A * The zone requires emulation that the brand can't
2N/A * provide. Notify the user by displaying an error
2N/A * message.
2N/A */
2N/A s10_err(gettext("The zone's version of Solaris 10 is "
2N/A "incompatible with the\ncurrent version of the "
2N/A "solaris10 brand.\nPlease update your Solaris "
2N/A "system to the latest release."));
2N/A } else {
2N/A /*
2N/A * Set the feature's flag in the bitmap.
2N/A */
2N/A bitmap[(bit_index >> 3)] |= (1 << (bit_index & 0x7));
2N/A }
2N/A }
2N/A (void) closedir(req_emulation_dirp);
2N/A
2N/A /*
2N/A * The zone's administrator might have removed a patch that delivered
2N/A * an emulation feature file the last time the zone ran. If so, then
2N/A * the zone's patch utilities won't delete the file until the zone's
2N/A * svc:/system/patch-finish:delete SMF service runs. This is
2N/A * problematic because the zone will be using system libraries whose
2N/A * ioctl structures and syscall invocations will differ from those
2N/A * expected by the emulation library. For example, if an administrator
2N/A * removes a patch that affects the formats of MNTFS ioctls, then the
2N/A * administrator's zone will use a version of libc.so.1 that issues
2N/A * MNTFS ioctls that use older structure versions than the zone's
2N/A * emulation library will expect.
2N/A *
2N/A * Fortunately, the patchrm utility creates a hidden file,
2N/A * /var/sadm/patch/.delete_list, which lists all files that
2N/A * svc:/system/patch-finish:delete will delete. We'll determine whether
2N/A * this file exists in the zone and disable the emulation bits
2N/A * associated with the emulation feature files that will be deleted.
2N/A *
2N/A * NOTE: The patch tools lofs mount backup copies of critical system
2N/A * libraries, such as /lib/libc.so.1, over their replacements whenever
2N/A * administrators add or remove DAP patches. Consequently, there isn't
2N/A * a window of vulnerability between patch addition or removal and
2N/A * zone reboot. The aforementioned problem only occurs after a zone
2N/A * reboots.
2N/A */
2N/A if (snprintf(path, sizeof (path), "%s" DELETE_LIST_PATH, zoneroot) >=
2N/A sizeof (path))
2N/A s10_err(gettext("zone's delete list's path %s" DELETE_LIST_PATH
2N/A " is too long"), zoneroot);
2N/A if ((delete_listp = fopen(path, "r")) != NULL) {
2N/A while (fgets(path, sizeof (path), delete_listp) != NULL) {
2N/A char *const basenamep = path +
2N/A sizeof (S10_REQ_EMULATION_DIR);
2N/A
2N/A /*
2N/A * Make sure that the file is in the directory
2N/A * containing emulation feature files. If it is,
2N/A * then basenamep should refer to the basename of
2N/A * the file.
2N/A */
2N/A if (strncmp(path, S10_REQ_EMULATION_DIR,
2N/A sizeof (S10_REQ_EMULATION_DIR) - 1) != 0)
2N/A continue;
2N/A if (*(basenamep - 1) != '/')
2N/A continue;
2N/A
2N/A /*
2N/A * Convert the file's basename into a bit index in
2N/A * the emulation bitmap. If the file's basename isn't
2N/A * integral, then skip the file. Otherwise, clear the
2N/A * corresponding bit in the bitmap.
2N/A */
2N/A bit_index = basename_to_uint(basenamep);
2N/A if (bit_index == (unsigned int)-1)
2N/A continue;
2N/A if (bit_index < S10_NUM_EMUL_FEATURES)
2N/A bitmap[(bit_index >> 3)] &=
2N/A ~(1 << (bit_index & 0x7));
2N/A }
2N/A if (ferror(delete_listp) != 0 || feof(delete_listp) == 0)
2N/A s10_err(gettext("The program encountered an error while"
2N/A " reading from %s" DELETE_LIST_PATH "."), zoneroot);
2N/A (void) fclose(delete_listp);
2N/A } else if (errno != ENOENT) {
2N/A /*
2N/A * The delete list exists but couldn't be opened. Warn the
2N/A * administrator.
2N/A */
2N/A s10_err(gettext("Unable to open %s" DELETE_LIST_PATH ": %s"),
2N/A zoneroot, strerror(errno));
2N/A }
2N/A
2N/A /*
2N/A * We're done scanning files. Set the zone's emulation bitmap.
2N/A */
2N/A if ((zoneid = getzoneidbyname(zonename)) < 0)
2N/A s10_err(gettext("unable to get zoneid"));
2N/A if (zone_setattr(zoneid, S10_EMUL_BITMAP, bitmap, sizeof (bitmap)) != 0)
2N/A s10_err(gettext("error setting zone's emulation bitmap"));
2N/A}
2N/A
2N/Astatic int
2N/As10_boot(char *zonename)
2N/A{
2N/A if (!have_valid_ku(zonename))
2N/A s10_err(gettext(
2N/A "The installed version of Solaris 10 is not supported.\n"
2N/A "SPARC systems require patch 142909-17\n"
2N/A "x86/x64 systems require patch 142910-17"));
2N/A
2N/A set_zone_emul_bitmap(zonename);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Astatic void
2N/Ausage()
2N/A{
2N/A (void) fprintf(stderr, gettext(
2N/A "usage:\t%s verify <xml file>\n"
2N/A "\t%s boot\n"),
2N/A bname, bname);
2N/A exit(1);
2N/A}
2N/A
2N/Aint
2N/Amain(int argc, char *argv[])
2N/A{
2N/A (void) setlocale(LC_ALL, "");
2N/A (void) textdomain(TEXT_DOMAIN);
2N/A
2N/A bname = basename(argv[0]);
2N/A
2N/A if (argc != 3)
2N/A usage();
2N/A
2N/A /*
2N/A * XXX This is a temporary env variable for the initial release to
2N/A * enable the use of features which are not yet tested or fully
2N/A * implemented.
2N/A */
2N/A if (getenv("S10BRAND_TEST") != NULL)
2N/A override = B_TRUE;
2N/A
2N/A if (strcmp(argv[1], "verify") == 0)
2N/A return (s10_verify(argv[2]));
2N/A
2N/A if (strcmp(argv[1], "boot") == 0)
2N/A return (s10_boot(argv[2]));
2N/A
2N/A usage();
2N/A /*NOTREACHED*/
2N/A}