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 (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. 23 */ 24 25 /* 26 * s10_support is a small cli utility used to perform some brand-specific 27 * tasks when verifying a zone. This utility is not intended to be called 28 * by users - it is intended to be invoked by the zones utilities. 29 */ 30 31 #include <ctype.h> 32 #include <errno.h> 33 #include <fcntl.h> 34 #include <libgen.h> 35 #include <limits.h> 36 #include <s10_brand.h> 37 #include <stdarg.h> 38 #include <stdio.h> 39 #include <stdlib.h> 40 #include <string.h> 41 #include <strings.h> 42 #include <stropts.h> 43 #include <sys/stat.h> 44 #include <sys/types.h> 45 #include <sys/utsname.h> 46 #include <sys/varargs.h> 47 #include <unistd.h> 48 #include <libintl.h> 49 #include <locale.h> 50 #include <dirent.h> 51 #include <sys/systeminfo.h> 52 53 #include <libzonecfg.h> 54 55 static void s10_err(char *msg, ...) __NORETURN; 56 static void usage(void) __NORETURN; 57 58 /* 59 * XXX This is a temporary flag for the initial release to enable the 60 * use of features which are not yet tested or fully implemented. 61 */ 62 static boolean_t override = B_FALSE; 63 64 static char *bname = NULL; 65 66 /* 67 * DELETE_LIST_PATH represents the path to a solaris10-branded zone's "delete 68 * list", which is generated by patchrm when it needs to remove files after 69 * the zone reboots. See set_zone_emul_bitmap() below for additional details. 70 */ 71 #define DELETE_LIST_PATH "/var/sadm/patch/.delete_list" 72 73 #define PKGINFO_RD_LEN 128 74 #define PATCHLIST "PATCHLIST=" 75 76 #if !defined(TEXT_DOMAIN) /* should be defined by cc -D */ 77 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it wasn't */ 78 #endif 79 80 /*PRINTFLIKE1*/ 81 static void 82 s10_err(char *msg, ...) 83 { 84 char buf[1024]; 85 va_list ap; 86 87 va_start(ap, msg); 88 (void) vsnprintf(buf, sizeof (buf), msg, ap); 89 va_end(ap); 90 91 /* This needs go to stdout so the msgs show up through zoneadm. */ 92 (void) printf("Error: %s\n", buf); 93 94 exit(1); 95 /*NOTREACHED*/ 96 } 97 98 static int 99 s10_verify(char *xmlfile) 100 { 101 zone_dochandle_t handle; 102 struct zone_devtab devtab; 103 zone_iptype_t iptype; 104 105 if ((handle = zonecfg_init_handle()) == NULL) 106 s10_err(gettext("internal libzonecfg.so.1 error"), 0); 107 108 if (zonecfg_get_xml_handle(xmlfile, handle) != Z_OK) { 109 zonecfg_fini_handle(handle); 110 s10_err(gettext("zonecfg provided an invalid XML file")); 111 } 112 113 /* 114 * Check to see whether the zone has any unsupported devices 115 * configured. 116 * 117 * The audio framework has changed in Solaris Next as compared to 118 * S10. Data indicates the less than 1/10 of 1 percent of zones 119 * are using /dev/sound. Given the low usage vs. the effort to 120 * provide emulation, /dev/sound is currently disallowed. We can 121 * revisit this if there is enough demand. 122 */ 123 if (zonecfg_setdevent(handle) != Z_OK) { 124 zonecfg_fini_handle(handle); 125 s10_err(gettext("zonecfg provided an invalid XML file")); 126 } 127 if (zonecfg_getdevent(handle, &devtab) == Z_OK) { 128 if (strncmp(devtab.zone_dev_match, "/dev/sound", 10) == 0 && 129 !override) { 130 zonecfg_fini_handle(handle); 131 s10_err(gettext("solaris10 zones do not currently " 132 "support /dev/sound")); 133 } 134 } 135 (void) zonecfg_enddevent(handle); 136 137 /* 138 * Check to see whether the zone has any experimental features 139 * configured. 140 */ 141 if (zonecfg_get_iptype(handle, &iptype) == Z_OK && 142 iptype == ZS_EXCLUSIVE && !override) { 143 zonecfg_fini_handle(handle); 144 s10_err(gettext("solaris10 zones do not currently support " 145 "exclusive ip-type stacks")); 146 } 147 148 zonecfg_fini_handle(handle); 149 return (0); 150 } 151 152 /* 153 * Read an entry from a pkginfo file. Some of these lines can 154 * either be arbitrarily long or be continued by a backslash at the end of 155 * the line. This function coalesces lines that are longer than the read 156 * buffer, and lines that are continued, into one buffer which is returned. 157 * The caller must free this memory. NULL is returned when we hit EOF or 158 * if we run out of memory (errno is set to ENOMEM). 159 */ 160 static char * 161 read_pkg_data(FILE *fp) 162 { 163 char *start; 164 char *inp; 165 char *p; 166 int char_cnt = 0; 167 168 errno = 0; 169 if ((start = (char *)malloc(PKGINFO_RD_LEN)) == NULL) { 170 errno = ENOMEM; 171 return (NULL); 172 } 173 174 inp = start; 175 while ((p = fgets(inp, PKGINFO_RD_LEN, fp)) != NULL) { 176 int len; 177 178 len = strlen(inp); 179 if (inp[len - 1] == '\n' && 180 (len == 1 || inp[len - 2] != '\\')) { 181 char_cnt = len; 182 break; 183 } 184 185 if (inp[len - 1] == '\n' && inp[len - 2] == '\\') 186 char_cnt += len - 2; 187 else 188 char_cnt += PKGINFO_RD_LEN - 1; 189 190 if ((p = realloc(start, char_cnt + PKGINFO_RD_LEN)) == NULL) { 191 errno = ENOMEM; 192 break; 193 } 194 195 start = p; 196 inp = start + char_cnt; 197 } 198 199 if (errno == ENOMEM || (p == NULL && char_cnt == 0)) { 200 free(start); 201 start = NULL; 202 } 203 204 return (start); 205 } 206 207 /* 208 * Read the SUNWcakr pkginfo file and get the PATCHLIST for the pkg. 209 */ 210 static int 211 get_ku_patchlist(char *zonepath, char **patchlist) 212 { 213 char pkginfo[MAXPATHLEN]; 214 FILE *fp; 215 char *buf; 216 int err = 0; 217 218 if (snprintf(pkginfo, sizeof (pkginfo), 219 "%s/root/var/sadm/pkg/SUNWcakr/pkginfo", zonepath) 220 >= sizeof (pkginfo)) 221 s10_err(gettext("error formating pkg path")); 222 223 if ((fp = fopen(pkginfo, "r")) == NULL) 224 return (errno); 225 226 while ((buf = read_pkg_data(fp)) != NULL) { 227 if (strncmp(buf, PATCHLIST, sizeof (PATCHLIST) - 1) == 0) { 228 int len; 229 230 /* remove trailing newline */ 231 len = strlen(buf); 232 buf[len - 1] = '\0'; 233 234 if ((*patchlist = 235 strdup(buf + sizeof (PATCHLIST) - 1)) == NULL) 236 err = ENOMEM; 237 238 free(buf); 239 break; 240 } 241 242 free(buf); 243 } 244 (void) fclose(fp); 245 246 return (err); 247 } 248 249 /* 250 * Verify that we have the minimum KU needed. 251 * Note that KU patches are accumulative so future KUs will still deliver 252 * 141444 or 141445. 253 */ 254 static boolean_t 255 have_valid_ku(char *zonename) 256 { 257 char *p; 258 char *lastp; 259 char *pstr; 260 char *patchlist = NULL; 261 char zonepath[MAXPATHLEN]; 262 char sanity_skip[MAXPATHLEN]; 263 struct stat64 buf; 264 boolean_t is_xpv = B_FALSE; 265 char platform[80]; 266 char *xpv_vers = "142910"; 267 char *vers_table[] = { 268 "141444-09", 269 "141445-09"}; 270 271 if (zone_get_zonepath(zonename, zonepath, sizeof (zonepath)) != Z_OK) 272 s10_err(gettext("error getting zone's path")); 273 274 /* 275 * If the zone was installed to bypass sanity checking for internal 276 * testing purposes, just return success. 277 */ 278 if (snprintf(sanity_skip, sizeof (sanity_skip), "%s/root/.sanity_skip", 279 zonepath) >= sizeof (sanity_skip)) 280 s10_err(gettext("error formating file path")); 281 282 if (stat64(sanity_skip, &buf) == 0) 283 return (B_TRUE); 284 285 if (get_ku_patchlist(zonepath, &patchlist) != 0 || patchlist == NULL) 286 return (B_FALSE); 287 288 /* 289 * Check if we're running on the i86xpv platform. If so, the zone 290 * needs a different ku patch to work properly. 291 */ 292 if (sysinfo(SI_PLATFORM, platform, sizeof (platform)) != -1 && 293 strcmp(platform, "i86xpv") == 0) 294 is_xpv = B_TRUE; 295 296 pstr = patchlist; 297 while ((p = strtok_r(pstr, " ", &lastp)) != NULL) { 298 if (is_xpv) { 299 if (strncmp(p, xpv_vers, 6) == 0) 300 return (B_TRUE); 301 } else { 302 if (strcmp(p, vers_table[0]) == 0 || 303 strcmp(p, vers_table[1]) == 0) 304 return (B_TRUE); 305 } 306 307 pstr = NULL; 308 } 309 310 if (is_xpv) 311 s10_err(gettext("the zone must have patch 142910 installed " 312 "when running in a paravirtualized domain")); 313 314 315 return (B_FALSE); 316 } 317 318 /* 319 * Convert the specified file basename into an unsigned integer. If the 320 * basename contains characters that cannot be converted into digits or the 321 * basename isn't NULL or newline-terminated, then this function returns 322 * the unsigned equivalent of -1. 323 */ 324 static unsigned int 325 basename_to_uint(const char *basenamep) 326 { 327 char *filename_endptr; 328 unsigned int bit_index; 329 330 errno = 0; 331 bit_index = (unsigned int)strtoul(basenamep, &filename_endptr, 10); 332 if (errno != 0 || (*filename_endptr != '\n' && 333 *filename_endptr != '\0') || filename_endptr == basenamep) 334 return ((unsigned int)-1); 335 return (bit_index); 336 } 337 338 /* 339 * Determine which features/behaviors should be emulated and construct a bitmap 340 * representing the results. Associate the bitmap with the zone so that 341 * the brand's emulation library will be able to retrieve the bitmap and 342 * determine how the zone's process' behaviors should be emulated. 343 * 344 * This function does not return if an error occurs. 345 */ 346 static void 347 set_zone_emul_bitmap(char *zonename) 348 { 349 char zoneroot[MAXPATHLEN]; 350 char path[MAXPATHLEN]; 351 DIR *req_emulation_dirp; 352 struct dirent *emul_feature_filep; 353 s10_emul_bitmap_t bitmap; 354 unsigned int bit_index; 355 zoneid_t zoneid; 356 FILE *delete_listp; 357 358 /* 359 * If the Solaris 10 directory containing emulation feature files 360 * doesn't exist in the zone, then assume that it only needs the 361 * most basic emulation and, therefore, doesn't need a bitmap. 362 */ 363 if (zone_get_rootpath(zonename, zoneroot, sizeof (zoneroot)) != Z_OK) 364 s10_err(gettext("error getting zone's path")); 365 if (snprintf(path, sizeof (path), "%s" S10_REQ_EMULATION_DIR, 366 zoneroot) >= sizeof (path)) 367 s10_err(gettext("zone's emulation versioning directory's path " 368 "%s" S10_REQ_EMULATION_DIR " is too long"), zoneroot); 369 if ((req_emulation_dirp = opendir(path)) == NULL) 370 return; 371 bzero(bitmap, sizeof (bitmap)); 372 373 /* 374 * Iterate over the contents of the directory and determine which 375 * features the brand should emulate for this zone. 376 */ 377 while ((emul_feature_filep = readdir(req_emulation_dirp)) != NULL) { 378 if (strcmp(emul_feature_filep->d_name, ".") == 0 || 379 strcmp(emul_feature_filep->d_name, "..") == 0) 380 continue; 381 382 /* 383 * Convert the file's name to an unsigned integer. Ignore 384 * files whose names aren't unsigned integers. 385 */ 386 bit_index = basename_to_uint(emul_feature_filep->d_name); 387 if (bit_index == (unsigned int)-1) 388 continue; 389 390 /* 391 * Determine if the brand can emulate the feature specified 392 * by bit_index. 393 */ 394 if (bit_index >= S10_NUM_EMUL_FEATURES) { 395 /* 396 * The zone requires emulation that the brand can't 397 * provide. Notify the user by displaying an error 398 * message. 399 */ 400 s10_err(gettext("The zone's version of Solaris 10 is " 401 "incompatible with the\ncurrent version of the " 402 "solaris10 brand.\nPlease update your Solaris " 403 "system to the latest release.")); 404 } else { 405 /* 406 * Set the feature's flag in the bitmap. 407 */ 408 bitmap[(bit_index >> 3)] |= (1 << (bit_index & 0x7)); 409 } 410 } 411 (void) closedir(req_emulation_dirp); 412 413 /* 414 * The zone's administrator might have removed a patch that delivered 415 * an emulation feature file the last time the zone ran. If so, then 416 * the zone's patch utilities won't delete the file until the zone's 417 * svc:/system/patch-finish:delete SMF service runs. This is 418 * problematic because the zone will be using system libraries whose 419 * ioctl structures and syscall invocations will differ from those 420 * expected by the emulation library. For example, if an administrator 421 * removes a patch that affects the formats of MNTFS ioctls, then the 422 * administrator's zone will use a version of libc.so.1 that issues 423 * MNTFS ioctls that use older structure versions than the zone's 424 * emulation library will expect. 425 * 426 * Fortunately, the patchrm utility creates a hidden file, 427 * /var/sadm/patch/.delete_list, which lists all files that 428 * svc:/system/patch-finish:delete will delete. We'll determine whether 429 * this file exists in the zone and disable the emulation bits 430 * associated with the emulation feature files that will be deleted. 431 * 432 * NOTE: The patch tools lofs mount backup copies of critical system 433 * libraries, such as /lib/libc.so.1, over their replacements whenever 434 * administrators add or remove DAP patches. Consequently, there isn't 435 * a window of vulnerability between patch addition or removal and 436 * zone reboot. The aforementioned problem only occurs after a zone 437 * reboots. 438 */ 439 if (snprintf(path, sizeof (path), "%s" DELETE_LIST_PATH, zoneroot) >= 440 sizeof (path)) 441 s10_err(gettext("zone's delete list's path %s" DELETE_LIST_PATH 442 " is too long"), zoneroot); 443 if ((delete_listp = fopen(path, "r")) != NULL) { 444 while (fgets(path, sizeof (path), delete_listp) != NULL) { 445 char *const basenamep = path + 446 sizeof (S10_REQ_EMULATION_DIR); 447 448 /* 449 * Make sure that the file is in the directory 450 * containing emulation feature files. If it is, 451 * then basenamep should refer to the basename of 452 * the file. 453 */ 454 if (strncmp(path, S10_REQ_EMULATION_DIR, 455 sizeof (S10_REQ_EMULATION_DIR) - 1) != 0) 456 continue; 457 if (*(basenamep - 1) != '/') 458 continue; 459 460 /* 461 * Convert the file's basename into a bit index in 462 * the emulation bitmap. If the file's basename isn't 463 * integral, then skip the file. Otherwise, clear the 464 * corresponding bit in the bitmap. 465 */ 466 bit_index = basename_to_uint(basenamep); 467 if (bit_index == (unsigned int)-1) 468 continue; 469 if (bit_index < S10_NUM_EMUL_FEATURES) 470 bitmap[(bit_index >> 3)] &= 471 ~(1 << (bit_index & 0x7)); 472 } 473 if (ferror(delete_listp) != 0 || feof(delete_listp) == 0) 474 s10_err(gettext("The program encountered an error while" 475 " reading from %s" DELETE_LIST_PATH "."), zoneroot); 476 (void) fclose(delete_listp); 477 } else if (errno != ENOENT) { 478 /* 479 * The delete list exists but couldn't be opened. Warn the 480 * administrator. 481 */ 482 s10_err(gettext("Unable to open %s" DELETE_LIST_PATH ": %s"), 483 zoneroot, strerror(errno)); 484 } 485 486 /* 487 * We're done scanning files. Set the zone's emulation bitmap. 488 */ 489 if ((zoneid = getzoneidbyname(zonename)) < 0) 490 s10_err(gettext("unable to get zoneid")); 491 if (zone_setattr(zoneid, S10_EMUL_BITMAP, bitmap, sizeof (bitmap)) != 0) 492 s10_err(gettext("error setting zone's emulation bitmap")); 493 } 494 495 static int 496 s10_boot(char *zonename) 497 { 498 if (!have_valid_ku(zonename)) 499 s10_err(gettext("The installed version of Solaris 10 is " 500 "not supported")); 501 502 set_zone_emul_bitmap(zonename); 503 504 return (0); 505 } 506 507 static void 508 usage() 509 { 510 (void) fprintf(stderr, gettext( 511 "usage:\t%s verify <xml file>\n" 512 "\t%s boot\n"), 513 bname, bname); 514 exit(1); 515 } 516 517 int 518 main(int argc, char *argv[]) 519 { 520 (void) setlocale(LC_ALL, ""); 521 (void) textdomain(TEXT_DOMAIN); 522 523 bname = basename(argv[0]); 524 525 if (argc != 3) 526 usage(); 527 528 /* 529 * XXX This is a temporary env variable for the initial release to 530 * enable the use of features which are not yet tested or fully 531 * implemented. 532 */ 533 if (getenv("S10BRAND_TEST") != NULL) 534 override = B_TRUE; 535 536 if (strcmp(argv[1], "verify") == 0) 537 return (s10_verify(argv[2])); 538 539 if (strcmp(argv[1], "boot") == 0) 540 return (s10_boot(argv[2])); 541 542 usage(); 543 /*NOTREACHED*/ 544 } 545