Home | History | Annotate | Download | only in lx_support
      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 }
    593