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