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 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* 27 * lx_support is a small cli utility used to perform some brand-specific 28 * tasks when booting, halting, or verifying a zone. This utility is not 29 * intended to be called by users - it is intended to be invoked by the 30 * zones utilities. 31 */ 32 33 #include <ctype.h> 34 #include <errno.h> 35 #include <fcntl.h> 36 #include <libgen.h> 37 #include <limits.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/ioccom.h> 45 #include <sys/stat.h> 46 #include <sys/systeminfo.h> 47 #include <sys/types.h> 48 #include <sys/varargs.h> 49 #include <unistd.h> 50 #include <libintl.h> 51 #include <locale.h> 52 53 #include <libzonecfg.h> 54 #include <sys/lx_audio.h> 55 #include <sys/lx_brand.h> 56 57 static void lxs_err(char *msg, ...) __NORETURN; 58 static void usage(void) __NORETURN; 59 60 #define CP_CMD "/usr/bin/cp" 61 #define MOUNT_CMD "/sbin/mount" 62 63 #define LXA_AUDIO_DEV "/dev/brand/lx/audio_devctl" 64 #define INTSTRLEN 32 65 #define KVSTRLEN 10 66 67 static char *bname = NULL; 68 static char *zonename = NULL; 69 static char *zoneroot = NULL; 70 71 #if !defined(TEXT_DOMAIN) /* should be defined by cc -D */ 72 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it wasn't */ 73 #endif 74 75 static void 76 lxs_err(char *msg, ...) 77 { 78 char buf[1024]; 79 va_list ap; 80 81 va_start(ap, msg); 82 /*LINTED*/ 83 (void) vsnprintf(buf, sizeof (buf), msg, ap); 84 va_end(ap); 85 86 (void) printf("%s error: %s\n", bname, buf); 87 88 exit(1); 89 /*NOTREACHED*/ 90 } 91 92 /* 93 * The Linux init(1M) command requires communication over the /dev/initctl 94 * FIFO. Since any attempt to create a file in /dev will fail, we must 95 * create it here. 96 */ 97 static void 98 lxs_make_initctl() 99 { 100 char cmdbuf[ARG_MAX]; 101 char path[MAXPATHLEN]; 102 char special[MAXPATHLEN]; 103 struct stat buf; 104 int err; 105 106 if (snprintf(special, sizeof (special), "%s/dev/initctl", zoneroot) >= 107 sizeof (special)) 108 lxs_err("%s: %s", gettext("Failed to create /dev/initctl"), 109 gettext("zoneroot is too long")); 110 111 if (snprintf(path, sizeof (path), "%s/root/dev/initctl", zoneroot) >= 112 sizeof (path)) 113 lxs_err("%s: %s", gettext("Failed to create /dev/initctl"), 114 gettext("zoneroot is too long")); 115 116 /* create the actual fifo as <zoneroot>/dev/initctl */ 117 if (stat(special, &buf) != 0) { 118 err = errno; 119 if (err != ENOENT) 120 lxs_err("%s: %s", 121 gettext("Failed to create /dev/initctl"), 122 strerror(err)); 123 if (mkfifo(special, 0644) < 0) { 124 err = errno; 125 lxs_err("%s: %s", 126 gettext("Failed to create /dev/initctl"), 127 strerror(err)); 128 } 129 } else { 130 if ((buf.st_mode & S_IFIFO) == 0) 131 lxs_err("%s: %s", 132 gettext("Failed to create /dev/initctl"), 133 gettext("It already exists, and is not a FIFO.")); 134 } 135 136 /* 137 * now lofs mount the <zoneroot>/dev/initctl fifo onto 138 * <zoneroot>/root/dev/initctl 139 */ 140 if (snprintf(cmdbuf, sizeof (cmdbuf), "%s -F lofs %s %s", MOUNT_CMD, 141 special, path) >= sizeof (cmdbuf)) 142 lxs_err("%s: %s", gettext("Failed to lofs mount /dev/initctl"), 143 gettext("zoneroot is too long")); 144 145 if (system(cmdbuf) < 0) { 146 err = errno; 147 lxs_err("%s: %s", gettext("Failed to lofs mount /dev/initctl"), 148 strerror(err)); 149 } 150 } 151 152 /* 153 * fsck gets really confused when run inside a zone. Removing this file 154 * prevents it from running 155 */ 156 static void 157 lxs_remove_autofsck() 158 { 159 char path[MAXPATHLEN]; 160 int err; 161 162 if (snprintf(path, MAXPATHLEN, "%s/root/.autofsck", zoneroot) >= 163 MAXPATHLEN) 164 lxs_err("%s: %s", gettext("Failed to remove /.autofsck"), 165 gettext("zoneroot is too long")); 166 167 if (unlink(path) < 0) { 168 err = errno; 169 if (err != ENOENT) 170 lxs_err("%s: %s", 171 gettext("Failed to remove /.autofsck"), 172 strerror(err)); 173 } 174 } 175 176 /* 177 * Extract any lx-supported attributes from the zone configuration file. 178 */ 179 static void 180 lxs_getattrs(zone_dochandle_t zdh, boolean_t *restart, boolean_t *audio, 181 char **idev, char **odev, char **kvers) 182 { 183 struct zone_attrtab attrtab; 184 int err; 185 186 /* initialize the attribute iterator */ 187 if (zonecfg_setattrent(zdh) != Z_OK) { 188 zonecfg_fini_handle(zdh); 189 lxs_err(gettext("error accessing zone configuration")); 190 } 191 192 *idev = (char *)malloc(INTSTRLEN); 193 *odev = (char *)malloc(INTSTRLEN); 194 *kvers = (char *)malloc(KVSTRLEN); 195 if (*idev == NULL || *odev == NULL || *kvers == NULL) 196 lxs_err(gettext("out of memory")); 197 198 *audio = B_FALSE; 199 *restart = B_FALSE; 200 bzero(*idev, INTSTRLEN); 201 bzero(*odev, INTSTRLEN); 202 bzero(*kvers, KVSTRLEN); 203 while ((err = zonecfg_getattrent(zdh, &attrtab)) == Z_OK) { 204 if ((strcmp(attrtab.zone_attr_name, "init-restart") == 0) && 205 (zonecfg_get_attr_boolean(&attrtab, restart) != Z_OK)) 206 lxs_err(gettext("invalid type for zone attribute: %s"), 207 attrtab.zone_attr_name); 208 if ((strcmp(attrtab.zone_attr_name, "audio") == 0) && 209 (zonecfg_get_attr_boolean(&attrtab, audio) != Z_OK)) 210 lxs_err(gettext("invalid type for zone attribute: %s"), 211 attrtab.zone_attr_name); 212 if ((strcmp(attrtab.zone_attr_name, "audio-inputdev") == 0) && 213 (zonecfg_get_attr_string(&attrtab, *idev, 214 INTSTRLEN) != Z_OK)) 215 lxs_err(gettext("invalid type for zone attribute: %s"), 216 attrtab.zone_attr_name); 217 if ((strcmp(attrtab.zone_attr_name, "audio-outputdev") == 0) && 218 (zonecfg_get_attr_string(&attrtab, *odev, 219 INTSTRLEN) != Z_OK)) 220 lxs_err(gettext("invalid type for zone attribute: %s"), 221 attrtab.zone_attr_name); 222 if ((strcmp(attrtab.zone_attr_name, "kernel-version") == 0) && 223 (zonecfg_get_attr_string(&attrtab, *kvers, 224 KVSTRLEN) != Z_OK)) 225 lxs_err(gettext("invalid type for zone attribute: %s"), 226 attrtab.zone_attr_name); 227 } 228 229 if (strlen(*kvers) == 0) { 230 free(*kvers); 231 *kvers = NULL; 232 } 233 234 /* some kind of error while looking up attributes */ 235 if (err != Z_NO_ENTRY) 236 lxs_err(gettext("error accessing zone configuration")); 237 } 238 239 static int 240 lxs_iodev_ok(char *dev) 241 { 242 int i, j; 243 244 if ((j = strlen(dev)) == 0) 245 return (1); 246 if (strcmp(dev, "default") == 0) 247 return (1); 248 if (strcmp(dev, "none") == 0) 249 return (1); 250 for (i = 0; i < j; i++) { 251 if (!isdigit(dev[i])) 252 return (0); 253 } 254 return (1); 255 } 256 257 /* 258 * The audio configuration settings are read from the zone configuration 259 * file. Audio configuration is specified via the following attributes 260 * (settable via zonecfg): 261 * attr name: audio 262 * attr type: boolean 263 * 264 * attr name: audio-inputdev 265 * attr type: string 266 * attr values: "none" | [0-9]+ 267 * 268 * attr name: audio-outputdev 269 * attr type: string 270 * attr values: "none" | [0-9]+ 271 * 272 * The user can enable linux brand audio device (ie /dev/dsp and /dev/mixer) 273 * for a zone by setting the "audio" attribute to true. (The absence of 274 * this attribute leads to an assumed value of false.) 275 * 276 * If the "audio" attribute is set to true and "audio-inputdev" and 277 * "audio-outputdev" are not set, then when a linux applications access 278 * audio devices these access will be mapped to the system default audio 279 * device, ie /dev/audio and/dev/audioctl. 280 * 281 * If "audio-inputdev" is set to none, then audio input will be disabled. 282 * If "audio-inputdev" is set to an integer, then when a Linux application 283 * attempts to access audio devices these access will be mapped to 284 * /dev/sound/<audio-inputdev attribute value>. The same behavior will 285 * apply to the "audio-outputdev" attribute for linux audio output 286 * device accesses. 287 * 288 * If "audio-inputdev" or "audio-outputdev" exist but the audio attribute 289 * is missing (or set to false) audio will not be enabled for the zone. 290 */ 291 static void 292 lxs_init_audio(char *idev, char *odev) 293 { 294 int err, fd; 295 lxa_zone_reg_t lxa_zr; 296 297 /* sanity check the input and output device properties */ 298 if (!lxs_iodev_ok(idev)) 299 lxs_err(gettext("invalid value for zone attribute: %s"), 300 "audio-inputdev"); 301 302 if (!lxs_iodev_ok(odev)) 303 lxs_err(gettext("invalid value for zone attribute: %s"), 304 "audio-outputdev"); 305 306 /* initialize the zone name in the ioctl request */ 307 bzero(&lxa_zr, sizeof (lxa_zr)); 308 (void) strlcpy(lxa_zr.lxa_zr_zone_name, zonename, 309 sizeof (lxa_zr.lxa_zr_zone_name)); 310 311 /* initialize the input device property in the ioctl request */ 312 (void) strlcpy(lxa_zr.lxa_zr_inputdev, idev, 313 sizeof (lxa_zr.lxa_zr_inputdev)); 314 if (lxa_zr.lxa_zr_inputdev[0] == '\0') { 315 /* 316 * if no input device was specified, set the input device 317 * to "default" 318 */ 319 (void) strlcpy(lxa_zr.lxa_zr_inputdev, "default", 320 sizeof (lxa_zr.lxa_zr_inputdev)); 321 } 322 323 /* initialize the output device property in the ioctl request */ 324 (void) strlcpy(lxa_zr.lxa_zr_outputdev, odev, 325 sizeof (lxa_zr.lxa_zr_outputdev)); 326 if (lxa_zr.lxa_zr_outputdev[0] == '\0') { 327 /* 328 * if no output device was specified, set the output device 329 * to "default" 330 */ 331 (void) strlcpy(lxa_zr.lxa_zr_outputdev, "default", 332 sizeof (lxa_zr.lxa_zr_outputdev)); 333 } 334 335 /* open the audio device control node */ 336 if ((fd = open(LXA_AUDIO_DEV, O_RDWR)) < 0) 337 lxs_err(gettext("error accessing lx_audio device")); 338 339 /* enable audio for this zone */ 340 err = ioctl(fd, LXA_IOC_ZONE_REG, &lxa_zr); 341 (void) close(fd); 342 if (err != 0) 343 lxs_err(gettext("error configuring lx_audio device")); 344 } 345 346 static int 347 lxs_boot() 348 { 349 zoneid_t zoneid; 350 zone_dochandle_t zdh; 351 boolean_t audio, restart; 352 char *idev, *odev, *kvers; 353 int kversnum; 354 355 lxs_make_initctl(); 356 lxs_remove_autofsck(); 357 358 if ((zdh = zonecfg_init_handle()) == NULL) 359 lxs_err(gettext("unable to initialize zone handle")); 360 361 if (zonecfg_get_handle((char *)zonename, zdh) != Z_OK) { 362 zonecfg_fini_handle(zdh); 363 lxs_err(gettext("unable to load zone configuration")); 364 } 365 366 /* Extract any relevant attributes from the config file. */ 367 lxs_getattrs(zdh, &restart, &audio, &idev, &odev, &kvers); 368 zonecfg_fini_handle(zdh); 369 370 /* Configure the zone's audio support (if any). */ 371 if (audio == B_TRUE) 372 lxs_init_audio(idev, odev); 373 374 /* 375 * Let the kernel know whether or not this zone's init process 376 * should be automatically restarted on its death. 377 */ 378 if ((zoneid = getzoneidbyname(zonename)) < 0) 379 lxs_err(gettext("unable to get zoneid")); 380 if (zone_setattr(zoneid, LX_ATTR_RESTART_INIT, &restart, 381 sizeof (boolean_t)) == -1) 382 lxs_err(gettext("error setting zone's restart_init property")); 383 384 if ((kvers != NULL) && (strcmp(kvers, "2.6") == 0)) 385 kversnum = LX_KERN_2_6; 386 else 387 kversnum = LX_KERN_2_4; 388 389 if (zone_setattr(zoneid, LX_KERN_VERSION_NUM, &kversnum, 390 sizeof (int)) < 0) 391 lxs_err(gettext("unable to set kernel version")); 392 393 return (0); 394 } 395 396 static int 397 lxs_halt() 398 { 399 lxa_zone_reg_t lxa_zr; 400 int fd, rv; 401 402 /* 403 * We don't bother to check if audio is configured for this zone 404 * before issuing a request to unconfigure it. There's no real 405 * reason to do this, it would require looking up the xml zone and 406 * brand configuration information (which could have been changed 407 * since the zone was booted), and it would involve more library 408 * calls there by increasing chances for failure. 409 */ 410 411 /* initialize the zone name in the ioctl request */ 412 bzero(&lxa_zr, sizeof (lxa_zr)); 413 (void) strlcpy(lxa_zr.lxa_zr_zone_name, zonename, 414 sizeof (lxa_zr.lxa_zr_zone_name)); 415 416 /* open the audio device control node */ 417 if ((fd = open(LXA_AUDIO_DEV, O_RDWR)) < 0) 418 lxs_err(gettext("error accessing lx_audio device")); 419 420 /* 421 * disable audio for this zone 422 * 423 * we ignore ENOENT errors here because it's possible that 424 * audio is not configured for this zone. (either it was 425 * already unconfigured or someone could have added the 426 * audio resource to this zone after it was booted.) 427 */ 428 rv = ioctl(fd, LXA_IOC_ZONE_UNREG, &lxa_zr); 429 (void) close(fd); 430 if ((rv == 0) || (errno == ENOENT)) 431 return (0); 432 lxs_err(gettext("error unconfiguring lx_audio device: %s"), 433 strerror(errno)); 434 /*NOTREACHED*/ 435 } 436 437 static int 438 lxs_verify(char *xmlfile) 439 { 440 zone_dochandle_t handle; 441 /* struct zone_fstab fstab; */ 442 struct zone_dstab dstab; 443 struct zone_devtab devtab; 444 boolean_t audio, restart; 445 char *idev, *odev, *kvers; 446 zone_iptype_t iptype; 447 char hostidp[HW_HOSTID_LEN]; 448 449 if ((handle = zonecfg_init_handle()) == NULL) 450 lxs_err(gettext("internal libzonecfg.so.1 error"), 0); 451 452 if (zonecfg_get_xml_handle(xmlfile, handle) != Z_OK) { 453 zonecfg_fini_handle(handle); 454 lxs_err(gettext("zonecfg provided an invalid XML file")); 455 } 456 457 /* 458 * Check to see whether the zone has any inherit-pkg-dirs 459 * configured. 460 */ 461 /* if (zonecfg_setipdent(handle) != Z_OK) { 462 zonecfg_fini_handle(handle); 463 lxs_err(gettext("zonecfg provided an invalid XML file")); 464 } 465 466 if (zonecfg_getipdent(handle, &fstab) == Z_OK) { 467 zonecfg_fini_handle(handle); 468 lxs_err(gettext("lx zones do not support inherit-pkg-dirs")); 469 } 470 */ 471 /* 472 * Check to see whether the zone has any ZFS datasets configured. 473 */ 474 if (zonecfg_setdsent(handle) != Z_OK) { 475 zonecfg_fini_handle(handle); 476 lxs_err(gettext("zonecfg provided an invalid XML file")); 477 } 478 479 if (zonecfg_getdsent(handle, &dstab) == Z_OK) { 480 zonecfg_fini_handle(handle); 481 lxs_err(gettext("lx zones do not support ZFS datasets")); 482 } 483 484 /* 485 * Check to see whether the zone has any devices configured. 486 */ 487 if (zonecfg_setdevent(handle) != Z_OK) { 488 zonecfg_fini_handle(handle); 489 lxs_err(gettext("zonecfg provided an invalid XML file")); 490 } 491 492 if (zonecfg_getdevent(handle, &devtab) == Z_OK) { 493 zonecfg_fini_handle(handle); 494 lxs_err(gettext("lx zones do not support added devices")); 495 } 496 497 /* 498 * Check to see whether the zone has ip-type configured as exclusive 499 */ 500 if (zonecfg_get_iptype(handle, &iptype) != Z_OK) { 501 zonecfg_fini_handle(handle); 502 lxs_err(gettext("zonecfg provided an invalid XML file")); 503 } 504 505 if (iptype == ZS_EXCLUSIVE) { 506 zonecfg_fini_handle(handle); 507 lxs_err(gettext("lx zones do not support an 'exclusive' " 508 "ip-type")); 509 } 510 511 /* 512 * Check to see whether the zone has hostid emulation enabled. 513 */ 514 if (zonecfg_get_hostid(handle, hostidp, sizeof (hostidp)) == Z_OK) { 515 zonecfg_fini_handle(handle); 516 lxs_err(gettext("lx zones do not support hostid emulation")); 517 } 518 519 /* Extract any relevant attributes from the config file. */ 520 lxs_getattrs(handle, &restart, &audio, &idev, &odev, &kvers); 521 zonecfg_fini_handle(handle); 522 523 if (audio) { 524 /* sanity check the input and output device properties */ 525 if (!lxs_iodev_ok(idev)) 526 lxs_err(gettext("invalid value for zone attribute: %s"), 527 "audio-inputdev"); 528 529 if (!lxs_iodev_ok(odev)) 530 lxs_err(gettext("invalid value for zone attribute: %s"), 531 "audio-outputdev"); 532 } 533 if (kvers) { 534 if ((strcmp(kvers, "2.4")) != 0 && (strcmp(kvers, "2.6") != 0)) 535 lxs_err(gettext("invalid value for zone attribute: %s"), 536 "kernel-version"); 537 } 538 return (0); 539 } 540 541 static void 542 usage() 543 { 544 545 (void) fprintf(stderr, 546 gettext("usage:\t%s boot <zoneroot> <zonename>\n"), bname); 547 (void) fprintf(stderr, 548 gettext(" \t%s halt <zoneroot> <zonename>\n"), bname); 549 (void) fprintf(stderr, 550 gettext(" \t%s verify <xml file>\n\n"), bname); 551 exit(1); 552 } 553 554 int 555 main(int argc, char *argv[]) 556 { 557 (void) setlocale(LC_ALL, ""); 558 (void) textdomain(TEXT_DOMAIN); 559 560 bname = basename(argv[0]); 561 562 if (argc < 3) 563 usage(); 564 565 if (strcmp(argv[1], "boot") == 0) { 566 if (argc != 4) 567 lxs_err(gettext("usage: %s %s <zoneroot> <zonename>"), 568 bname, argv[1]); 569 zoneroot = argv[2]; 570 zonename = argv[3]; 571 return (lxs_boot()); 572 } 573 574 if (strcmp(argv[1], "halt") == 0) { 575 if (argc != 4) 576 lxs_err(gettext("usage: %s %s <zoneroot> <zonename>"), 577 bname, argv[1]); 578 zoneroot = argv[2]; 579 zonename = argv[3]; 580 return (lxs_halt()); 581 } 582 583 if (strcmp(argv[1], "verify") == 0) { 584 if (argc != 3) 585 lxs_err(gettext("usage: %s verify <xml file>"), 586 bname); 587 return (lxs_verify(argv[2])); 588 } 589 590 usage(); 591 /*NOTREACHED*/ 592 }