/* * 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 (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. */ /* * 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; /* * DELETE_LIST_PATH represents the path to a solaris10-branded zone's "delete * list", which is generated by patchrm when it needs to remove files after * the zone reboots. See set_zone_emul_bitmap() below for additional details. */ #define DELETE_LIST_PATH "/var/sadm/patch/.delete_list" #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_devtab devtab; 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 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); 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 *zonepath, char **patchlist) { char pkginfo[MAXPATHLEN]; FILE *fp; char *buf; int err = 0; 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; char zonepath[MAXPATHLEN]; char sanity_skip[MAXPATHLEN]; struct stat64 buf; boolean_t is_xpv = B_FALSE; char platform[80]; char *xpv_vers = "142910"; char *vers_table[] = { "141444-09", "141445-09"}; if (zone_get_zonepath(zonename, zonepath, sizeof (zonepath)) != Z_OK) s10_err(gettext("error getting zone's path")); /* * If the zone was installed to bypass sanity checking for internal * testing purposes, just return success. */ if (snprintf(sanity_skip, sizeof (sanity_skip), "%s/root/.sanity_skip", zonepath) >= sizeof (sanity_skip)) s10_err(gettext("error formating file path")); if (stat64(sanity_skip, &buf) == 0) return (B_TRUE); if (get_ku_patchlist(zonepath, &patchlist) != 0 || patchlist == NULL) return (B_FALSE); /* * Check if we're running on the i86xpv platform. If so, the zone * needs a different ku patch to work properly. */ if (sysinfo(SI_PLATFORM, platform, sizeof (platform)) != -1 && strcmp(platform, "i86xpv") == 0) is_xpv = B_TRUE; pstr = patchlist; while ((p = strtok_r(pstr, " ", &lastp)) != NULL) { if (is_xpv) { if (strncmp(p, xpv_vers, 6) == 0) return (B_TRUE); } else { if (strcmp(p, vers_table[0]) == 0 || strcmp(p, vers_table[1]) == 0) return (B_TRUE); } pstr = NULL; } if (is_xpv) s10_err(gettext("the zone must have patch 142910 installed " "when running in a paravirtualized domain")); return (B_FALSE); } /* * Convert the specified file basename into an unsigned integer. If the * basename contains characters that cannot be converted into digits or the * basename isn't NULL or newline-terminated, then this function returns * the unsigned equivalent of -1. */ static unsigned int basename_to_uint(const char *basenamep) { char *filename_endptr; unsigned int bit_index; errno = 0; bit_index = (unsigned int)strtoul(basenamep, &filename_endptr, 10); if (errno != 0 || (*filename_endptr != '\n' && *filename_endptr != '\0') || filename_endptr == basenamep) return ((unsigned int)-1); return (bit_index); } /* * Determine which features/behaviors should be emulated and construct a bitmap * representing the results. Associate the bitmap with the zone so that * the brand's emulation library will be able to retrieve the bitmap and * determine how the zone's process' behaviors should be emulated. * * This function does not return if an error occurs. */ static void set_zone_emul_bitmap(char *zonename) { char zoneroot[MAXPATHLEN]; char path[MAXPATHLEN]; DIR *req_emulation_dirp; struct dirent *emul_feature_filep; s10_emul_bitmap_t bitmap; unsigned int bit_index; zoneid_t zoneid; FILE *delete_listp; /* * If the Solaris 10 directory containing emulation feature files * doesn't exist in the zone, then assume that it only needs the * most basic emulation and, therefore, doesn't need a bitmap. */ if (zone_get_rootpath(zonename, zoneroot, sizeof (zoneroot)) != Z_OK) s10_err(gettext("error getting zone's path")); if (snprintf(path, sizeof (path), "%s" S10_REQ_EMULATION_DIR, zoneroot) >= sizeof (path)) s10_err(gettext("zone's emulation versioning directory's path " "%s" S10_REQ_EMULATION_DIR " is too long"), zoneroot); if ((req_emulation_dirp = opendir(path)) == NULL) return; bzero(bitmap, sizeof (bitmap)); /* * Iterate over the contents of the directory and determine which * features the brand should emulate for this zone. */ while ((emul_feature_filep = readdir(req_emulation_dirp)) != NULL) { if (strcmp(emul_feature_filep->d_name, ".") == 0 || strcmp(emul_feature_filep->d_name, "..") == 0) continue; /* * Convert the file's name to an unsigned integer. Ignore * files whose names aren't unsigned integers. */ bit_index = basename_to_uint(emul_feature_filep->d_name); if (bit_index == (unsigned int)-1) continue; /* * Determine if the brand can emulate the feature specified * by bit_index. */ if (bit_index >= S10_NUM_EMUL_FEATURES) { /* * The zone requires emulation that the brand can't * provide. Notify the user by displaying an error * message. */ s10_err(gettext("The zone's version of Solaris 10 is " "incompatible with the\ncurrent version of the " "solaris10 brand.\nPlease update your Solaris " "system to the latest release.")); } else { /* * Set the feature's flag in the bitmap. */ bitmap[(bit_index >> 3)] |= (1 << (bit_index & 0x7)); } } (void) closedir(req_emulation_dirp); /* * The zone's administrator might have removed a patch that delivered * an emulation feature file the last time the zone ran. If so, then * the zone's patch utilities won't delete the file until the zone's * svc:/system/patch-finish:delete SMF service runs. This is * problematic because the zone will be using system libraries whose * ioctl structures and syscall invocations will differ from those * expected by the emulation library. For example, if an administrator * removes a patch that affects the formats of MNTFS ioctls, then the * administrator's zone will use a version of libc.so.1 that issues * MNTFS ioctls that use older structure versions than the zone's * emulation library will expect. * * Fortunately, the patchrm utility creates a hidden file, * /var/sadm/patch/.delete_list, which lists all files that * svc:/system/patch-finish:delete will delete. We'll determine whether * this file exists in the zone and disable the emulation bits * associated with the emulation feature files that will be deleted. * * NOTE: The patch tools lofs mount backup copies of critical system * libraries, such as /lib/libc.so.1, over their replacements whenever * administrators add or remove DAP patches. Consequently, there isn't * a window of vulnerability between patch addition or removal and * zone reboot. The aforementioned problem only occurs after a zone * reboots. */ if (snprintf(path, sizeof (path), "%s" DELETE_LIST_PATH, zoneroot) >= sizeof (path)) s10_err(gettext("zone's delete list's path %s" DELETE_LIST_PATH " is too long"), zoneroot); if ((delete_listp = fopen(path, "r")) != NULL) { while (fgets(path, sizeof (path), delete_listp) != NULL) { char *const basenamep = path + sizeof (S10_REQ_EMULATION_DIR); /* * Make sure that the file is in the directory * containing emulation feature files. If it is, * then basenamep should refer to the basename of * the file. */ if (strncmp(path, S10_REQ_EMULATION_DIR, sizeof (S10_REQ_EMULATION_DIR) - 1) != 0) continue; if (*(basenamep - 1) != '/') continue; /* * Convert the file's basename into a bit index in * the emulation bitmap. If the file's basename isn't * integral, then skip the file. Otherwise, clear the * corresponding bit in the bitmap. */ bit_index = basename_to_uint(basenamep); if (bit_index == (unsigned int)-1) continue; if (bit_index < S10_NUM_EMUL_FEATURES) bitmap[(bit_index >> 3)] &= ~(1 << (bit_index & 0x7)); } if (ferror(delete_listp) != 0 || feof(delete_listp) == 0) s10_err(gettext("The program encountered an error while" " reading from %s" DELETE_LIST_PATH "."), zoneroot); (void) fclose(delete_listp); } else if (errno != ENOENT) { /* * The delete list exists but couldn't be opened. Warn the * administrator. */ s10_err(gettext("Unable to open %s" DELETE_LIST_PATH ": %s"), zoneroot, strerror(errno)); } /* * We're done scanning files. Set the zone's emulation bitmap. */ if ((zoneid = getzoneidbyname(zonename)) < 0) s10_err(gettext("unable to get zoneid")); if (zone_setattr(zoneid, S10_EMUL_BITMAP, bitmap, sizeof (bitmap)) != 0) s10_err(gettext("error setting zone's emulation bitmap")); } static int s10_boot(char *zonename) { if (!have_valid_ku(zonename)) s10_err(gettext("The installed version of Solaris 10 is " "not supported")); set_zone_emul_bitmap(zonename); 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*/ }