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 int i; 284 char zonepath[MAXPATHLEN]; 285 char sanity_skip[MAXPATHLEN]; 286 struct stat64 buf; 287 char *vers_table[] = { 288 "141444-09", 289 "141445-09", 290 NULL}; 291 292 if (zone_get_zonepath(zonename, zonepath, sizeof (zonepath)) != Z_OK) 293 s10_err(gettext("error getting zone's path")); 294 295 /* 296 * If the zone was installed to bypass sanity checking for internal 297 * testing purposes, just return success. 298 */ 299 if (snprintf(sanity_skip, sizeof (sanity_skip), "%s/root/.sanity_skip", 300 zonepath) >= sizeof (sanity_skip)) 301 s10_err(gettext("error formating file path")); 302 303 if (stat64(sanity_skip, &buf) == 0) 304 return (B_TRUE); 305 306 if (get_ku_patchlist(zonepath, &patchlist) != 0 || patchlist == NULL) 307 return (B_FALSE); 308 309 pstr = patchlist; 310 while ((p = strtok_r(pstr, " ", &lastp)) != NULL) { 311 for (i = 0; vers_table[i] != NULL; i++) 312 if (strcmp(p, vers_table[i]) == 0) 313 return (B_TRUE); 314 315 pstr = NULL; 316 } 317 318 return (B_FALSE); 319 } 320 321 /* 322 * Determine which features/behaviors should be emulated and construct a bitmap 323 * representing the results. Associate the bitmap with the zone so that 324 * the brand's emulation library will be able to retrieve the bitmap and 325 * determine how the zone's process' behaviors should be emulated. 326 * 327 * This function does not return if an error occurs. 328 */ 329 static void 330 set_zone_emul_bitmap(char *zonename) 331 { 332 char req_emulation_dir_path[MAXPATHLEN]; 333 DIR *req_emulation_dirp; 334 struct dirent *emul_feature_filep; 335 char *filename_endptr; 336 s10_emul_bitmap_t bitmap; 337 unsigned int bit_index; 338 zoneid_t zoneid; 339 340 /* 341 * If the Solaris 10 directory containing emulation feature files 342 * doesn't exist in the zone, then assume that it only needs the 343 * most basic emulation and, therefore, doesn't need a bitmap. 344 */ 345 if (zone_get_rootpath(zonename, req_emulation_dir_path, 346 sizeof (req_emulation_dir_path)) != Z_OK) 347 s10_err(gettext("error getting zone's path")); 348 if (strlcat(req_emulation_dir_path, S10_REQ_EMULATION_DIR, 349 sizeof (req_emulation_dir_path)) >= sizeof (req_emulation_dir_path)) 350 s10_err(gettext("error formatting version path")); 351 if ((req_emulation_dirp = opendir(req_emulation_dir_path)) == NULL) 352 return; 353 bzero(bitmap, sizeof (bitmap)); 354 355 /* 356 * Iterate over the contents of the directory and determine which 357 * features the brand should emulate for this zone. 358 */ 359 while ((emul_feature_filep = readdir(req_emulation_dirp)) != NULL) { 360 if (strcmp(emul_feature_filep->d_name, ".") == 0 || 361 strcmp(emul_feature_filep->d_name, "..") == 0) 362 continue; 363 364 /* 365 * Convert the file's name to an unsigned integer. Ignore 366 * files whose names aren't unsigned integers. 367 */ 368 errno = 0; 369 bit_index = (unsigned int)strtoul(emul_feature_filep->d_name, 370 &filename_endptr, 10); 371 if (errno != 0 || *filename_endptr != '\0' || 372 filename_endptr == emul_feature_filep->d_name) 373 continue; 374 375 /* 376 * Determine if the brand can emulate the feature specified 377 * by bit_index. 378 */ 379 if (bit_index >= S10_NUM_EMUL_FEATURES) { 380 /* 381 * The zone requires emulation that the brand can't 382 * provide. Notify the user by displaying an error 383 * message. 384 */ 385 s10_err(gettext("The zone's version of Solaris 10 is " 386 "incompatible with the\ncurrent version of the " 387 "solaris10 brand.\nPlease update your Solaris " 388 "system to the latest release.")); 389 } else { 390 /* 391 * Set the feature's flag in the bitmap. 392 */ 393 bitmap[(bit_index >> 3)] |= (1 << (bit_index & 0x7)); 394 } 395 } 396 397 /* 398 * We're done scanning files. Set the zone's emulation bitmap. 399 */ 400 (void) closedir(req_emulation_dirp); 401 if ((zoneid = getzoneidbyname(zonename)) < 0) 402 s10_err(gettext("unable to get zoneid")); 403 if (zone_setattr(zoneid, S10_EMUL_BITMAP, bitmap, sizeof (bitmap)) != 0) 404 s10_err(gettext("error setting zone's emulation bitmap")); 405 } 406 407 static void 408 fail_xvm() 409 { 410 char buf[80]; 411 412 if (sysinfo(SI_PLATFORM, buf, sizeof (buf)) != -1 && 413 strcmp(buf, "i86xpv") == 0 && !override) 414 s10_err(gettext("running the solaris10 brand " 415 "in a paravirtualized\ndomain is currently not supported")); 416 } 417 418 static int 419 s10_boot(char *zonename) 420 { 421 if (!have_valid_ku(zonename)) 422 s10_err(gettext("The installed version of Solaris 10 is " 423 "not supported")); 424 425 set_zone_emul_bitmap(zonename); 426 427 fail_xvm(); 428 429 return (0); 430 } 431 432 static void 433 usage() 434 { 435 (void) fprintf(stderr, gettext( 436 "usage:\t%s verify <xml file>\n" 437 "\t%s boot\n"), 438 bname, bname); 439 exit(1); 440 } 441 442 int 443 main(int argc, char *argv[]) 444 { 445 (void) setlocale(LC_ALL, ""); 446 (void) textdomain(TEXT_DOMAIN); 447 448 bname = basename(argv[0]); 449 450 if (argc != 3) 451 usage(); 452 453 /* 454 * XXX This is a temporary env variable for the initial release to 455 * enable the use of features which are not yet tested or fully 456 * implemented. 457 */ 458 if (getenv("S10BRAND_TEST") != NULL) 459 override = B_TRUE; 460 461 if (strcmp(argv[1], "verify") == 0) 462 return (s10_verify(argv[2])); 463 464 if (strcmp(argv[1], "boot") == 0) 465 return (s10_boot(argv[2])); 466 467 usage(); 468 /*NOTREACHED*/ 469 } 470