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 2010 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 56 static void s10_err(char *msg, ...) __NORETURN; 57 static 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 */ 63 static boolean_t override = B_FALSE; 64 65 static 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*/ 75 static void 76 s10_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 92 static int 93 s10_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 */ 182 static char * 183 read_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 */ 232 static int 233 get_ku_patchlist(char *zonepath, char **patchlist) 234 { 235 char pkginfo[MAXPATHLEN]; 236 FILE *fp; 237 char *buf; 238 int err = 0; 239 240 if (snprintf(pkginfo, sizeof (pkginfo), 241 "%s/root/var/sadm/pkg/SUNWcakr/pkginfo", zonepath) 242 >= sizeof (pkginfo)) 243 s10_err(gettext("error formating pkg path")); 244 245 if ((fp = fopen(pkginfo, "r")) == NULL) 246 return (errno); 247 248 while ((buf = read_pkg_data(fp)) != NULL) { 249 if (strncmp(buf, PATCHLIST, sizeof (PATCHLIST) - 1) == 0) { 250 int len; 251 252 /* remove trailing newline */ 253 len = strlen(buf); 254 buf[len - 1] = '\0'; 255 256 if ((*patchlist = 257 strdup(buf + sizeof (PATCHLIST) - 1)) == NULL) 258 err = ENOMEM; 259 260 free(buf); 261 break; 262 } 263 264 free(buf); 265 } 266 (void) fclose(fp); 267 268 return (err); 269 } 270 271 /* 272 * Verify that we have the minimum KU needed. 273 * Note that KU patches are accumulative so future KUs will still deliver 274 * 141444 or 141445. 275 */ 276 static boolean_t 277 have_valid_ku(char *zonename) 278 { 279 char *p; 280 char *lastp; 281 char *pstr; 282 char *patchlist = NULL; 283 char zonepath[MAXPATHLEN]; 284 char sanity_skip[MAXPATHLEN]; 285 struct stat64 buf; 286 boolean_t is_xpv = B_FALSE; 287 char platform[80]; 288 char *xpv_vers = "142910"; 289 char *vers_table[] = { 290 "141444-09", 291 "141445-09"}; 292 293 if (zone_get_zonepath(zonename, zonepath, sizeof (zonepath)) != Z_OK) 294 s10_err(gettext("error getting zone's path")); 295 296 /* 297 * If the zone was installed to bypass sanity checking for internal 298 * testing purposes, just return success. 299 */ 300 if (snprintf(sanity_skip, sizeof (sanity_skip), "%s/root/.sanity_skip", 301 zonepath) >= sizeof (sanity_skip)) 302 s10_err(gettext("error formating file path")); 303 304 if (stat64(sanity_skip, &buf) == 0) 305 return (B_TRUE); 306 307 if (get_ku_patchlist(zonepath, &patchlist) != 0 || patchlist == NULL) 308 return (B_FALSE); 309 310 /* 311 * Check if we're running on the i86xpv platform. If so, the zone 312 * needs a different ku patch to work properly. 313 */ 314 if (sysinfo(SI_PLATFORM, platform, sizeof (platform)) != -1 && 315 strcmp(platform, "i86xpv") == 0) 316 is_xpv = B_TRUE; 317 318 pstr = patchlist; 319 while ((p = strtok_r(pstr, " ", &lastp)) != NULL) { 320 if (is_xpv) { 321 if (strncmp(p, xpv_vers, 6) == 0) 322 return (B_TRUE); 323 } else { 324 if (strcmp(p, vers_table[0]) == 0 || 325 strcmp(p, vers_table[1]) == 0) 326 return (B_TRUE); 327 } 328 329 pstr = NULL; 330 } 331 332 if (is_xpv) 333 s10_err(gettext("the zone must have patch 142910 installed " 334 "when running in a paravirtualized domain")); 335 336 337 return (B_FALSE); 338 } 339 340 /* 341 * Determine which features/behaviors should be emulated and construct a bitmap 342 * representing the results. Associate the bitmap with the zone so that 343 * the brand's emulation library will be able to retrieve the bitmap and 344 * determine how the zone's process' behaviors should be emulated. 345 * 346 * This function does not return if an error occurs. 347 */ 348 static void 349 set_zone_emul_bitmap(char *zonename) 350 { 351 char req_emulation_dir_path[MAXPATHLEN]; 352 DIR *req_emulation_dirp; 353 struct dirent *emul_feature_filep; 354 char *filename_endptr; 355 s10_emul_bitmap_t bitmap; 356 unsigned int bit_index; 357 zoneid_t zoneid; 358 359 /* 360 * If the Solaris 10 directory containing emulation feature files 361 * doesn't exist in the zone, then assume that it only needs the 362 * most basic emulation and, therefore, doesn't need a bitmap. 363 */ 364 if (zone_get_rootpath(zonename, req_emulation_dir_path, 365 sizeof (req_emulation_dir_path)) != Z_OK) 366 s10_err(gettext("error getting zone's path")); 367 if (strlcat(req_emulation_dir_path, S10_REQ_EMULATION_DIR, 368 sizeof (req_emulation_dir_path)) >= sizeof (req_emulation_dir_path)) 369 s10_err(gettext("error formatting version path")); 370 if ((req_emulation_dirp = opendir(req_emulation_dir_path)) == NULL) 371 return; 372 bzero(bitmap, sizeof (bitmap)); 373 374 /* 375 * Iterate over the contents of the directory and determine which 376 * features the brand should emulate for this zone. 377 */ 378 while ((emul_feature_filep = readdir(req_emulation_dirp)) != NULL) { 379 if (strcmp(emul_feature_filep->d_name, ".") == 0 || 380 strcmp(emul_feature_filep->d_name, "..") == 0) 381 continue; 382 383 /* 384 * Convert the file's name to an unsigned integer. Ignore 385 * files whose names aren't unsigned integers. 386 */ 387 errno = 0; 388 bit_index = (unsigned int)strtoul(emul_feature_filep->d_name, 389 &filename_endptr, 10); 390 if (errno != 0 || *filename_endptr != '\0' || 391 filename_endptr == emul_feature_filep->d_name) 392 continue; 393 394 /* 395 * Determine if the brand can emulate the feature specified 396 * by bit_index. 397 */ 398 if (bit_index >= S10_NUM_EMUL_FEATURES) { 399 /* 400 * The zone requires emulation that the brand can't 401 * provide. Notify the user by displaying an error 402 * message. 403 */ 404 s10_err(gettext("The zone's version of Solaris 10 is " 405 "incompatible with the\ncurrent version of the " 406 "solaris10 brand.\nPlease update your Solaris " 407 "system to the latest release.")); 408 } else { 409 /* 410 * Set the feature's flag in the bitmap. 411 */ 412 bitmap[(bit_index >> 3)] |= (1 << (bit_index & 0x7)); 413 } 414 } 415 416 /* 417 * We're done scanning files. Set the zone's emulation bitmap. 418 */ 419 (void) closedir(req_emulation_dirp); 420 if ((zoneid = getzoneidbyname(zonename)) < 0) 421 s10_err(gettext("unable to get zoneid")); 422 if (zone_setattr(zoneid, S10_EMUL_BITMAP, bitmap, sizeof (bitmap)) != 0) 423 s10_err(gettext("error setting zone's emulation bitmap")); 424 } 425 426 static int 427 s10_boot(char *zonename) 428 { 429 if (!have_valid_ku(zonename)) 430 s10_err(gettext("The installed version of Solaris 10 is " 431 "not supported")); 432 433 set_zone_emul_bitmap(zonename); 434 435 return (0); 436 } 437 438 static void 439 usage() 440 { 441 (void) fprintf(stderr, gettext( 442 "usage:\t%s verify <xml file>\n" 443 "\t%s boot\n"), 444 bname, bname); 445 exit(1); 446 } 447 448 int 449 main(int argc, char *argv[]) 450 { 451 (void) setlocale(LC_ALL, ""); 452 (void) textdomain(TEXT_DOMAIN); 453 454 bname = basename(argv[0]); 455 456 if (argc != 3) 457 usage(); 458 459 /* 460 * XXX This is a temporary env variable for the initial release to 461 * enable the use of features which are not yet tested or fully 462 * implemented. 463 */ 464 if (getenv("S10BRAND_TEST") != NULL) 465 override = B_TRUE; 466 467 if (strcmp(argv[1], "verify") == 0) 468 return (s10_verify(argv[2])); 469 470 if (strcmp(argv[1], "boot") == 0) 471 return (s10_boot(argv[2])); 472 473 usage(); 474 /*NOTREACHED*/ 475 } 476