summaryrefslogtreecommitdiff
path: root/usr/src/cmd/zoneadmd/zoneadmd.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/cmd/zoneadmd/zoneadmd.c')
-rw-r--r--usr/src/cmd/zoneadmd/zoneadmd.c219
1 files changed, 180 insertions, 39 deletions
diff --git a/usr/src/cmd/zoneadmd/zoneadmd.c b/usr/src/cmd/zoneadmd/zoneadmd.c
index 9208b8177d..0612da87df 100644
--- a/usr/src/cmd/zoneadmd/zoneadmd.c
+++ b/usr/src/cmd/zoneadmd/zoneadmd.c
@@ -93,17 +93,19 @@
#include <wait.h>
#include <limits.h>
#include <zone.h>
+#include <libbrand.h>
#include <libcontract.h>
#include <libcontract_priv.h>
#include <sys/contract/process.h>
#include <sys/ctfs.h>
-#include <sys/objfs.h>
#include <libzonecfg.h>
#include "zoneadmd.h"
static char *progname;
char *zone_name; /* zone which we are managing */
+char brand_name[MAXNAMELEN];
+boolean_t zone_isnative;
static zoneid_t zone_id;
zlog_t logsys;
@@ -123,8 +125,6 @@ boolean_t bringup_failure_recovery = B_FALSE; /* ignore certain failures */
#define TEXT_DOMAIN "SYS_TEST" /* Use this only if it wasn't */
#endif
-#define PATH_TO_INIT "/sbin/init"
-
#define DEFAULT_LOCALE "C"
static const char *
@@ -132,8 +132,8 @@ z_cmd_name(zone_cmd_t zcmd)
{
/* This list needs to match the enum in sys/zone.h */
static const char *zcmdstr[] = {
- "ready", "boot", "reboot", "halt", "note_uninstalling",
- "mount", "unmount"
+ "ready", "boot", "forceboot", "reboot", "halt",
+ "note_uninstalling", "mount", "forcemount", "unmount"
};
if (zcmd >= sizeof (zcmdstr) / sizeof (*zcmdstr))
@@ -271,8 +271,6 @@ filter_bootargs(zlog_t *zlogp, const char *inargs, char *outargs,
bzero(outargs, BOOTARGS_MAX);
bzero(badarg, BOOTARGS_MAX);
- (void) strlcpy(init_file, PATH_TO_INIT, MAXPATHLEN);
-
/*
* If the user didn't specify transient boot arguments, check
* to see if there were any specified in the zone configuration,
@@ -357,7 +355,7 @@ filter_bootargs(zlog_t *zlogp, const char *inargs, char *outargs,
optind = 0;
opterr = 0;
err = Z_OK;
- while ((c = getopt(argc, argv, "i:m:s")) != -1) {
+ while ((c = getopt(argc, argv, "fi:m:s")) != -1) {
switch (c) {
case 'i':
/*
@@ -366,6 +364,9 @@ filter_bootargs(zlog_t *zlogp, const char *inargs, char *outargs,
*/
(void) strlcpy(init_file, optarg, MAXPATHLEN);
break;
+ case 'f':
+ /* This has already been processed by zoneadm */
+ break;
case 'm':
case 's':
/* These pass through unmolested */
@@ -498,10 +499,17 @@ init_template(void)
return (fd);
}
+typedef struct fs_callback {
+ zlog_t *zlogp;
+ zoneid_t zoneid;
+} fs_callback_t;
+
static int
-mount_early_fs(zlog_t *zlogp, zoneid_t zoneid, const char *spec,
- const char *dir, char *fstype)
+mount_early_fs(void *data, const char *spec, const char *dir,
+ const char *fstype, const char *opt)
{
+ zlog_t *zlogp = ((fs_callback_t *)data)->zlogp;
+ zoneid_t zoneid = ((fs_callback_t *)data)->zoneid;
pid_t child;
int child_status;
int tmpl_fd;
@@ -519,6 +527,10 @@ mount_early_fs(zlog_t *zlogp, zoneid_t zoneid, const char *spec,
return (-1);
} else if (child == 0) { /* child */
+ char opt_buf[MAX_MNTOPT_STR];
+ int optlen = 0;
+ int mflag = MS_DATA;
+
(void) ct_tmpl_clear(tmpl_fd);
/*
* Even though there are no procs running in the zone, we
@@ -529,7 +541,23 @@ mount_early_fs(zlog_t *zlogp, zoneid_t zoneid, const char *spec,
if (zone_enter(zoneid) == -1) {
_exit(errno);
}
- if (mount(spec, dir, MS_DATA, fstype, NULL, 0, NULL, 0) != 0)
+ if (opt != NULL) {
+ /*
+ * The mount() system call is incredibly annoying.
+ * If options are specified, we need to copy them
+ * into a temporary buffer since the mount() system
+ * call will overwrite the options string. It will
+ * also fail if the new option string it wants to
+ * write is bigger than the one we passed in, so
+ * you must pass in a buffer of the maximum possible
+ * option string length. sigh.
+ */
+ (void) strlcpy(opt_buf, opt, sizeof (opt_buf));
+ opt = opt_buf;
+ optlen = MAX_MNTOPT_STR;
+ mflag = MS_OPTIONSTR;
+ }
+ if (mount(spec, dir, mflag, fstype, NULL, 0, opt, optlen) != 0)
_exit(errno);
_exit(0);
}
@@ -554,27 +582,35 @@ mount_early_fs(zlog_t *zlogp, zoneid_t zoneid, const char *spec,
return (0);
}
-static int
-zone_mount_early(zlog_t *zlogp, zoneid_t zoneid)
+int
+do_subproc(zlog_t *zlogp, char *cmdbuf)
{
- if (mount_early_fs(zlogp, zoneid, "/proc", "/proc", "proc") != 0)
- return (-1);
+ char inbuf[1024]; /* arbitrary large amount */
+ FILE *file;
+ int status;
- if (mount_early_fs(zlogp, zoneid, "ctfs", CTFS_ROOT, "ctfs") != 0)
+ file = popen(cmdbuf, "r");
+ if (file == NULL) {
+ zerror(zlogp, B_TRUE, "could not launch: %s", cmdbuf);
return (-1);
+ }
- if (mount_early_fs(zlogp, zoneid, "objfs", OBJFS_ROOT, "objfs") != 0)
- return (-1);
+ while (fgets(inbuf, sizeof (inbuf), file) != NULL)
+ if (zlogp != &logsys)
+ zerror(zlogp, B_FALSE, "%s", inbuf);
+ status = pclose(file);
- if (mount_early_fs(zlogp, zoneid, "swap", "/etc/svc/volatile",
- "tmpfs") != 0)
+ if (WIFSIGNALED(status)) {
+ zerror(zlogp, B_FALSE, "%s unexpectedly terminated due to "
+ "signal %d", cmdbuf, WTERMSIG(status));
return (-1);
-
- if (mount_early_fs(zlogp, zoneid, "mnttab", "/etc/mnttab",
- "mntfs") != 0)
+ }
+ assert(WIFEXITED(status));
+ if (WEXITSTATUS(status) == ZEXIT_EXEC) {
+ zerror(zlogp, B_FALSE, "failed to exec %s", cmdbuf);
return (-1);
-
- return (0);
+ }
+ return (WEXITSTATUS(status));
}
static int
@@ -584,6 +620,9 @@ zone_bootup(zlog_t *zlogp, const char *bootargs)
struct stat st;
char zroot[MAXPATHLEN], initpath[MAXPATHLEN], init_file[MAXPATHLEN];
char nbootargs[BOOTARGS_MAX];
+ char cmdbuf[MAXPATHLEN];
+ fs_callback_t cb;
+ brand_handle_t *bhp;
int err;
if (init_console_slave(zlogp) != 0)
@@ -595,8 +634,52 @@ zone_bootup(zlog_t *zlogp, const char *bootargs)
return (-1);
}
- if (zone_mount_early(zlogp, zoneid) != 0)
+ cb.zlogp = zlogp;
+ cb.zoneid = zoneid;
+
+ /* Get a handle to the brand info for this zone */
+ if ((bhp = brand_open(brand_name)) == NULL) {
+ zerror(zlogp, B_FALSE, "unable to determine zone brand");
+ return (-1);
+ }
+
+ /*
+ * Get the list of filesystems to mount from the brand
+ * configuration. These mounts are done via a thread that will
+ * enter the zone, so they are done from within the context of the
+ * zone.
+ */
+ if (brand_platform_iter_mounts(bhp, mount_early_fs, &cb) != 0) {
+ zerror(zlogp, B_FALSE, "unable to mount filesystems");
+ brand_close(bhp);
+ return (-1);
+ }
+
+ /*
+ * Get the brand's boot callback if it exists.
+ */
+ if (zone_get_zonepath(zone_name, zroot, sizeof (zroot)) != Z_OK) {
+ zerror(zlogp, B_FALSE, "unable to determine zone root");
+ return (-1);
+ }
+ (void) strcpy(cmdbuf, EXEC_PREFIX);
+ if (brand_get_boot(bhp, zone_name, zroot, cmdbuf + EXEC_LEN,
+ sizeof (cmdbuf) - EXEC_LEN, 0, NULL) != 0) {
+ zerror(zlogp, B_FALSE,
+ "unable to determine branded zone's boot callback");
+ brand_close(bhp);
+ return (-1);
+ }
+
+ /* Get the path for this zone's init(1M) (or equivalent) process. */
+ if (brand_get_initname(bhp, init_file, MAXPATHLEN) != 0) {
+ zerror(zlogp, B_FALSE,
+ "unable to determine zone's init(1M) location");
+ brand_close(bhp);
return (-1);
+ }
+
+ brand_close(bhp);
err = filter_bootargs(zlogp, bootargs, nbootargs, init_file,
bad_boot_arg);
@@ -607,14 +690,12 @@ zone_bootup(zlog_t *zlogp, const char *bootargs)
assert(init_file[0] != '\0');
- /*
- * Try to anticipate possible problems: Make sure whatever binary
- * is supposed to be init is executable.
- */
+ /* Try to anticipate possible problems: Make sure init is executable. */
if (zone_get_rootpath(zone_name, zroot, sizeof (zroot)) != Z_OK) {
zerror(zlogp, B_FALSE, "unable to determine zone root");
return (-1);
}
+
(void) snprintf(initpath, sizeof (initpath), "%s%s", zroot, init_file);
if (stat(initpath, &st) == -1) {
@@ -627,6 +708,17 @@ zone_bootup(zlog_t *zlogp, const char *bootargs)
return (-1);
}
+ /*
+ * If there is a brand 'boot' callback, execute it now to give the
+ * brand one last chance to do any additional setup before the zone
+ * is booted.
+ */
+ if ((strlen(cmdbuf) > EXEC_LEN) &&
+ (do_subproc(zlogp, cmdbuf) != Z_OK)) {
+ zerror(zlogp, B_FALSE, "%s failed", cmdbuf);
+ return (-1);
+ }
+
if (zone_setattr(zoneid, ZONE_ATTR_INITNAME, init_file, 0) == -1) {
zerror(zlogp, B_TRUE, "could not set zone boot file");
return (-1);
@@ -736,6 +828,8 @@ server(void *cookie, char *args, size_t alen, door_desc_t *dp,
zlog_t *zlogp;
zone_cmd_rval_t *rvalp;
size_t rlen = getpagesize(); /* conservative */
+ fs_callback_t cb;
+ brand_handle_t *bhp;
/* LINTED E_BAD_PTR_CAST_ALIGN */
zargp = (zone_cmd_arg_t *)args;
@@ -811,9 +905,9 @@ server(void *cookie, char *args, size_t alen, door_desc_t *dp,
/*
* Check for validity of command.
*/
- if (cmd != Z_READY && cmd != Z_BOOT && cmd != Z_REBOOT &&
- cmd != Z_HALT && cmd != Z_NOTE_UNINSTALLING && cmd != Z_MOUNT &&
- cmd != Z_UNMOUNT) {
+ if (cmd != Z_READY && cmd != Z_BOOT && cmd != Z_FORCEBOOT &&
+ cmd != Z_REBOOT && cmd != Z_HALT && cmd != Z_NOTE_UNINSTALLING &&
+ cmd != Z_MOUNT && cmd != Z_FORCEMOUNT && cmd != Z_UNMOUNT) {
zerror(&logsys, B_FALSE, "invalid command %d", (int)cmd);
goto out;
}
@@ -856,6 +950,14 @@ server(void *cookie, char *args, size_t alen, door_desc_t *dp,
zlogp = &logsys; /* Log errors to syslog */
}
+ /*
+ * If we are being asked to forcibly mount or boot a zone, we
+ * pretend that an INCOMPLETE zone is actually INSTALLED.
+ */
+ if (zstate == ZONE_STATE_INCOMPLETE &&
+ (cmd == Z_FORCEBOOT || cmd == Z_FORCEMOUNT))
+ zstate = ZONE_STATE_INSTALLED;
+
switch (zstate) {
case ZONE_STATE_CONFIGURED:
case ZONE_STATE_INCOMPLETE:
@@ -876,6 +978,7 @@ server(void *cookie, char *args, size_t alen, door_desc_t *dp,
eventstream_write(Z_EVT_ZONE_READIED);
break;
case Z_BOOT:
+ case Z_FORCEBOOT:
eventstream_write(Z_EVT_ZONE_BOOTING);
if ((rval = zone_ready(zlogp, B_FALSE)) == 0)
rval = zone_bootup(zlogp, zargp->bootbuf);
@@ -916,23 +1019,51 @@ server(void *cookie, char *args, size_t alen, door_desc_t *dp,
eventstream_write(Z_EVT_ZONE_UNINSTALLING);
break;
case Z_MOUNT:
+ case Z_FORCEMOUNT:
if (kernelcall) /* Invalid; can't happen */
abort();
+ if (!zone_isnative) {
+ zerror(zlogp, B_FALSE,
+ "%s operation is invalid for branded "
+ "zones", z_cmd_name(cmd));
+ rval = -1;
+ break;
+ }
+
rval = zone_ready(zlogp, B_TRUE);
- if (rval == 0) {
- eventstream_write(Z_EVT_ZONE_READIED);
- rval = zone_mount_early(zlogp, zone_id);
+ if (rval != 0)
+ break;
+
+ eventstream_write(Z_EVT_ZONE_READIED);
+
+ /* Get a handle to the brand info for this zone */
+ if ((bhp = brand_open(brand_name)) == NULL) {
+ rval = -1;
+ break;
}
/*
+ * Get the list of filesystems to mount from
+ * the brand configuration. These mounts are done
+ * via a thread that will enter the zone, so they
+ * are done from within the context of the zone.
+ */
+ cb.zlogp = zlogp;
+ cb.zoneid = zone_id;
+ rval = brand_platform_iter_mounts(bhp,
+ mount_early_fs, &cb);
+
+ brand_close(bhp);
+
+ /*
* Ordinarily, /dev/fd would be mounted inside the zone
* by svc:/system/filesystem/usr:default, but since
* we're not booting the zone, we need to do this
* manually.
*/
if (rval == 0)
- rval = mount_early_fs(zlogp, zone_id, "fd",
- "/dev/fd", "fd");
+ rval = mount_early_fs(&cb,
+ "fd", "/dev/fd", "fd", NULL);
break;
case Z_UNMOUNT:
if (kernelcall) /* Invalid; can't happen */
@@ -1245,6 +1376,7 @@ main(int argc, char *argv[])
priv_set_t *privset;
zone_state_t zstate;
char parents_locale[MAXPATHLEN];
+ brand_handle_t *bhp;
int err;
pid_t pid;
@@ -1347,13 +1479,22 @@ main(int argc, char *argv[])
zonecfg_strerror(err));
return (1);
}
- if (zstate < ZONE_STATE_INSTALLED) {
+ if (zstate < ZONE_STATE_INCOMPLETE) {
zerror(zlogp, B_FALSE,
"cannot manage a zone which is in state '%s'",
zone_state_str(zstate));
return (1);
}
+ /* Get a handle to the brand info for this zone */
+ if ((zone_get_brand(zone_name, brand_name, sizeof (brand_name))
+ != Z_OK) || (bhp = brand_open(brand_name)) == NULL) {
+ zerror(zlogp, B_FALSE, "unable to determine zone brand");
+ return (1);
+ }
+ zone_isnative = brand_is_native(bhp);
+ brand_close(bhp);
+
/*
* Check that we have all privileges. It would be nice to pare
* this down, but this is at least a first cut.