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