s10_support.c revision 11204:153262ed7017
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 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26/* 27 * s10_support is a small cli utility used to perform some brand-specific 28 * tasks when verifying a zone. This utility is not intended to be called 29 * by users - it is intended to be invoked by the zones utilities. 30 */ 31 32#include <ctype.h> 33#include <errno.h> 34#include <fcntl.h> 35#include <libgen.h> 36#include <limits.h> 37#include <s10_brand.h> 38#include <stdarg.h> 39#include <stdio.h> 40#include <stdlib.h> 41#include <string.h> 42#include <strings.h> 43#include <stropts.h> 44#include <sys/stat.h> 45#include <sys/types.h> 46#include <sys/utsname.h> 47#include <sys/varargs.h> 48#include <unistd.h> 49#include <libintl.h> 50#include <locale.h> 51#include <dirent.h> 52#include <sys/systeminfo.h> 53 54#include <libzonecfg.h> 55 56static void s10_err(char *msg, ...) __NORETURN; 57static void usage(void) __NORETURN; 58 59/* 60 * XXX This is a temporary flag for the initial release to enable the 61 * use of features which are not yet tested or fully implemented. 62 */ 63static boolean_t override = B_FALSE; 64 65static char *bname = NULL; 66 67#define PKGINFO_RD_LEN 128 68#define PATCHLIST "PATCHLIST=" 69 70#if !defined(TEXT_DOMAIN) /* should be defined by cc -D */ 71#define TEXT_DOMAIN "SYS_TEST" /* Use this only if it wasn't */ 72#endif 73 74/*PRINTFLIKE1*/ 75static void 76s10_err(char *msg, ...) 77{ 78 char buf[1024]; 79 va_list ap; 80 81 va_start(ap, msg); 82 (void) vsnprintf(buf, sizeof (buf), msg, ap); 83 va_end(ap); 84 85 /* This needs go to stdout so the msgs show up through zoneadm. */ 86 (void) printf("Error: %s\n", buf); 87 88 exit(1); 89 /*NOTREACHED*/ 90} 91 92static int 93s10_verify(char *xmlfile) 94{ 95 zone_dochandle_t handle; 96 struct zone_fstab fstab; 97 struct zone_devtab devtab; 98 zone_iptype_t iptype; 99 struct zone_dstab dstab; 100 101 if ((handle = zonecfg_init_handle()) == NULL) 102 s10_err(gettext("internal libzonecfg.so.1 error"), 0); 103 104 if (zonecfg_get_xml_handle(xmlfile, handle) != Z_OK) { 105 zonecfg_fini_handle(handle); 106 s10_err(gettext("zonecfg provided an invalid XML file")); 107 } 108 109 /* 110 * Check to see whether the zone has any inherit-pkg-dirs 111 * configured. 112 */ 113 if (zonecfg_setipdent(handle) != Z_OK) { 114 zonecfg_fini_handle(handle); 115 s10_err(gettext("zonecfg provided an invalid XML file")); 116 } 117 if (zonecfg_getipdent(handle, &fstab) == Z_OK) { 118 zonecfg_fini_handle(handle); 119 s10_err(gettext("solaris10 zones do not support " 120 "inherit-pkg-dirs")); 121 } 122 (void) zonecfg_endipdent(handle); 123 124 /* 125 * Check to see whether the zone has any unsupported devices 126 * configured. 127 * 128 * The audio framework has changed in Solaris Next as compared to 129 * S10. Data indicates the less than 1/10 of 1 percent of zones 130 * are using /dev/sound. Given the low usage vs. the effort to 131 * provide emulation, /dev/sound is currently disallowed. We can 132 * revisit this if there is enough demand. 133 */ 134 if (zonecfg_setdevent(handle) != Z_OK) { 135 zonecfg_fini_handle(handle); 136 s10_err(gettext("zonecfg provided an invalid XML file")); 137 } 138 if (zonecfg_getdevent(handle, &devtab) == Z_OK) { 139 if (strncmp(devtab.zone_dev_match, "/dev/sound", 10) == 0 && 140 !override) { 141 zonecfg_fini_handle(handle); 142 s10_err(gettext("solaris10 zones do not currently " 143 "support /dev/sound")); 144 } 145 } 146 (void) zonecfg_enddevent(handle); 147 148 /* 149 * Check to see whether the zone has any experimental features 150 * configured. 151 */ 152 if (zonecfg_get_iptype(handle, &iptype) == Z_OK && 153 iptype == ZS_EXCLUSIVE && !override) { 154 zonecfg_fini_handle(handle); 155 s10_err(gettext("solaris10 zones do not currently support " 156 "exclusive ip-type stacks")); 157 } 158 159 if (zonecfg_setdsent(handle) != Z_OK) { 160 zonecfg_fini_handle(handle); 161 s10_err(gettext("zonecfg provided an invalid XML file")); 162 } 163 if (zonecfg_getdsent(handle, &dstab) == Z_OK && !override) { 164 zonecfg_fini_handle(handle); 165 s10_err(gettext("solaris10 zones do not currently support " 166 "delegated datasets")); 167 } 168 (void) zonecfg_enddsent(handle); 169 170 zonecfg_fini_handle(handle); 171 return (0); 172} 173 174/* 175 * Read an entry from a pkginfo file. Some of these lines can 176 * either be arbitrarily long or be continued by a backslash at the end of 177 * the line. This function coalesces lines that are longer than the read 178 * buffer, and lines that are continued, into one buffer which is returned. 179 * The caller must free this memory. NULL is returned when we hit EOF or 180 * if we run out of memory (errno is set to ENOMEM). 181 */ 182static char * 183read_pkg_data(FILE *fp) 184{ 185 char *start; 186 char *inp; 187 char *p; 188 int char_cnt = 0; 189 190 errno = 0; 191 if ((start = (char *)malloc(PKGINFO_RD_LEN)) == NULL) { 192 errno = ENOMEM; 193 return (NULL); 194 } 195 196 inp = start; 197 while ((p = fgets(inp, PKGINFO_RD_LEN, fp)) != NULL) { 198 int len; 199 200 len = strlen(inp); 201 if (inp[len - 1] == '\n' && 202 (len == 1 || inp[len - 2] != '\\')) { 203 char_cnt = len; 204 break; 205 } 206 207 if (inp[len - 1] == '\n' && inp[len - 2] == '\\') 208 char_cnt += len - 2; 209 else 210 char_cnt += PKGINFO_RD_LEN - 1; 211 212 if ((p = realloc(start, char_cnt + PKGINFO_RD_LEN)) == NULL) { 213 errno = ENOMEM; 214 break; 215 } 216 217 start = p; 218 inp = start + char_cnt; 219 } 220 221 if (errno == ENOMEM || (p == NULL && char_cnt == 0)) { 222 free(start); 223 start = NULL; 224 } 225 226 return (start); 227} 228 229/* 230 * Read the SUNWcakr pkginfo file and get the PATCHLIST for the pkg. 231 */ 232static int 233get_ku_patchlist(char *zonename, char **patchlist) 234{ 235 char zonepath[MAXPATHLEN]; 236 char pkginfo[MAXPATHLEN]; 237 FILE *fp; 238 char *buf; 239 int err = 0; 240 241 if (zone_get_zonepath(zonename, zonepath, sizeof (zonepath)) != Z_OK) 242 s10_err(gettext("error getting zone's path")); 243 244 if (snprintf(pkginfo, sizeof (pkginfo), 245 "%s/root/var/sadm/pkg/SUNWcakr/pkginfo", zonepath) 246 >= sizeof (pkginfo)) 247 s10_err(gettext("error formating pkg path")); 248 249 if ((fp = fopen(pkginfo, "r")) == NULL) 250 return (errno); 251 252 while ((buf = read_pkg_data(fp)) != NULL) { 253 if (strncmp(buf, PATCHLIST, sizeof (PATCHLIST) - 1) == 0) { 254 int len; 255 256 /* remove trailing newline */ 257 len = strlen(buf); 258 buf[len - 1] = '\0'; 259 260 if ((*patchlist = 261 strdup(buf + sizeof (PATCHLIST) - 1)) == NULL) 262 err = ENOMEM; 263 264 free(buf); 265 break; 266 } 267 268 free(buf); 269 } 270 (void) fclose(fp); 271 272 return (err); 273} 274 275/* 276 * Verify that we have the minimum KU needed. 277 * Note that KU patches are accumulative so future KUs will still deliver 278 * 141444 or 141445. 279 */ 280static boolean_t 281have_valid_ku(char *zonename) 282{ 283 char *p; 284 char *lastp; 285 char *pstr; 286 char *patchlist = NULL; 287 int i; 288 char *vers_table[] = { 289 "141444-09", 290 "141445-09", 291 NULL}; 292 293 if (get_ku_patchlist(zonename, &patchlist) != 0 || patchlist == NULL) 294 return (B_FALSE); 295 296 pstr = patchlist; 297 while ((p = strtok_r(pstr, " ", &lastp)) != NULL) { 298 for (i = 0; vers_table[i] != NULL; i++) 299 if (strcmp(p, vers_table[i]) == 0) 300 return (B_TRUE); 301 302 pstr = NULL; 303 } 304 305 return (B_FALSE); 306} 307 308/* 309 * Determine which features/behaviors should be emulated and construct a bitmap 310 * representing the results. Associate the bitmap with the zone so that 311 * the brand's emulation library will be able to retrieve the bitmap and 312 * determine how the zone's process' behaviors should be emulated. 313 * 314 * This function does not return if an error occurs. 315 */ 316static void 317set_zone_emul_bitmap(char *zonename) 318{ 319 char req_emulation_dir_path[MAXPATHLEN]; 320 DIR *req_emulation_dirp; 321 struct dirent *emul_feature_filep; 322 char *filename_endptr; 323 s10_emul_bitmap_t bitmap; 324 unsigned int bit_index; 325 zoneid_t zoneid; 326 327 /* 328 * If the Solaris 10 directory containing emulation feature files 329 * doesn't exist in the zone, then assume that it only needs the 330 * most basic emulation and, therefore, doesn't need a bitmap. 331 */ 332 if (zone_get_rootpath(zonename, req_emulation_dir_path, 333 sizeof (req_emulation_dir_path)) != Z_OK) 334 s10_err(gettext("error getting zone's path")); 335 if (strlcat(req_emulation_dir_path, S10_REQ_EMULATION_DIR, 336 sizeof (req_emulation_dir_path)) >= sizeof (req_emulation_dir_path)) 337 s10_err(gettext("error formatting version path")); 338 if ((req_emulation_dirp = opendir(req_emulation_dir_path)) == NULL) 339 return; 340 bzero(bitmap, sizeof (bitmap)); 341 342 /* 343 * Iterate over the contents of the directory and determine which 344 * features the brand should emulate for this zone. 345 */ 346 while ((emul_feature_filep = readdir(req_emulation_dirp)) != NULL) { 347 if (strcmp(emul_feature_filep->d_name, ".") == 0 || 348 strcmp(emul_feature_filep->d_name, "..") == 0) 349 continue; 350 351 /* 352 * Convert the file's name to an unsigned integer. Ignore 353 * files whose names aren't unsigned integers. 354 */ 355 errno = 0; 356 bit_index = (unsigned int)strtoul(emul_feature_filep->d_name, 357 &filename_endptr, 10); 358 if (errno != 0 || *filename_endptr != '\0' || 359 filename_endptr == emul_feature_filep->d_name) 360 continue; 361 362 /* 363 * Determine if the brand can emulate the feature specified 364 * by bit_index. 365 */ 366 if (bit_index >= S10_NUM_EMUL_FEATURES) { 367 /* 368 * The zone requires emulation that the brand can't 369 * provide. Notify the user by displaying an error 370 * message. 371 */ 372 s10_err(gettext("The zone's version of Solaris 10 is " 373 "incompatible with the\ncurrent version of the " 374 "solaris10 brand.\nPlease update your Solaris " 375 "system to the latest release.")); 376 } else { 377 /* 378 * Set the feature's flag in the bitmap. 379 */ 380 bitmap[(bit_index >> 3)] |= (1 << (bit_index & 0x7)); 381 } 382 } 383 384 /* 385 * We're done scanning files. Set the zone's emulation bitmap. 386 */ 387 (void) closedir(req_emulation_dirp); 388 if ((zoneid = getzoneidbyname(zonename)) < 0) 389 s10_err(gettext("unable to get zoneid")); 390 if (zone_setattr(zoneid, S10_EMUL_BITMAP, bitmap, sizeof (bitmap)) != 0) 391 s10_err(gettext("error setting zone's emulation bitmap")); 392} 393 394static void 395fail_xvm() 396{ 397 char buf[80]; 398 399 if (sysinfo(SI_PLATFORM, buf, sizeof (buf)) != -1 && 400 strcmp(buf, "i86xpv") == 0 && !override) 401 s10_err(gettext("running the solaris10 brand " 402 "in a paravirtualized\ndomain is currently not supported")); 403} 404 405static int 406s10_boot(char *zonename) 407{ 408 if (!have_valid_ku(zonename)) 409 s10_err(gettext("The installed version of Solaris 10 is " 410 "not supported")); 411 412 set_zone_emul_bitmap(zonename); 413 414 fail_xvm(); 415 416 return (0); 417} 418 419static void 420usage() 421{ 422 (void) fprintf(stderr, gettext( 423 "usage:\t%s verify <xml file>\n" 424 "\t%s boot\n"), 425 bname, bname); 426 exit(1); 427} 428 429int 430main(int argc, char *argv[]) 431{ 432 (void) setlocale(LC_ALL, ""); 433 (void) textdomain(TEXT_DOMAIN); 434 435 bname = basename(argv[0]); 436 437 if (argc != 3) 438 usage(); 439 440 /* 441 * XXX This is a temporary env variable for the initial release to 442 * enable the use of features which are not yet tested or fully 443 * implemented. 444 */ 445 if (getenv("S10BRAND_TEST") != NULL) 446 override = B_TRUE; 447 448 if (strcmp(argv[1], "verify") == 0) 449 return (s10_verify(argv[2])); 450 451 if (strcmp(argv[1], "boot") == 0) 452 return (s10_boot(argv[2])); 453 454 usage(); 455 /*NOTREACHED*/ 456} 457