From owner-svn-src-all@freebsd.org Tue Mar 19 17:38:56 2019 Return-Path: Delivered-To: svn-src-all@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id 18E0F152CA05; Tue, 19 Mar 2019 17:38:56 +0000 (UTC) (envelope-from kevans@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) server-signature RSA-PSS (4096 bits) client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "Let's Encrypt Authority X3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 98BF58E206; Tue, 19 Mar 2019 17:38:55 +0000 (UTC) (envelope-from kevans@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 7DE01C9BC; Tue, 19 Mar 2019 17:38:55 +0000 (UTC) (envelope-from kevans@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id x2JHcte3058062; Tue, 19 Mar 2019 17:38:55 GMT (envelope-from kevans@FreeBSD.org) Received: (from kevans@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id x2JHctd3058061; Tue, 19 Mar 2019 17:38:55 GMT (envelope-from kevans@FreeBSD.org) Message-Id: <201903191738.x2JHctd3058061@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: kevans set sender to kevans@FreeBSD.org using -f From: Kyle Evans Date: Tue, 19 Mar 2019 17:38:55 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r345302 - head/sbin/bectl X-SVN-Group: head X-SVN-Commit-Author: kevans X-SVN-Commit-Paths: head/sbin/bectl X-SVN-Commit-Revision: 345302 X-SVN-Commit-Repository: base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Rspamd-Queue-Id: 98BF58E206 X-Spamd-Bar: -- Authentication-Results: mx1.freebsd.org X-Spamd-Result: default: False [-2.96 / 15.00]; local_wl_from(0.00)[FreeBSD.org]; NEURAL_HAM_MEDIUM(-1.00)[-0.998,0]; NEURAL_HAM_LONG(-1.00)[-1.000,0]; NEURAL_HAM_SHORT(-0.96)[-0.964,0]; ASN(0.00)[asn:11403, ipnet:2610:1c1:1::/48, country:US] X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 19 Mar 2019 17:38:56 -0000 Author: kevans Date: Tue Mar 19 17:38:54 2019 New Revision: 345302 URL: https://svnweb.freebsd.org/changeset/base/345302 Log: bectl(8): change jail command to execute jail(8) The jail(8) command provides a variety of jail pseudo-parameters that are useful to consumers of bectl, mount.devfs being the most-often-requested paramater by bectl users. command, exec.start, nopersist, and persist may not be specified via -o to bectl. The command/exec.start remains passed as it always has at the end of bectl, and persistence is dictated by -b/-U bectl jail arguments. Submitted by: Wes Maag MFC after: 1 week Differential Revision: https://reviews.freebsd.org/D19282 Modified: head/sbin/bectl/bectl_jail.c Modified: head/sbin/bectl/bectl_jail.c ============================================================================== --- head/sbin/bectl/bectl_jail.c Tue Mar 19 17:16:37 2019 (r345301) +++ head/sbin/bectl/bectl_jail.c Tue Mar 19 17:38:54 2019 (r345302) @@ -40,10 +40,10 @@ __FBSDID("$FreeBSD$"); #include #include - #include "bectl.h" -static void jailparam_grow(void); +#define MNTTYPE_ZFS 222 + static void jailparam_add(const char *name, const char *val); static int jailparam_del(const char *name); static bool jailparam_addarg(char *arg); @@ -51,84 +51,28 @@ static int jailparam_delarg(char *arg); static int bectl_search_jail_paths(const char *mnt); static int bectl_locate_jail(const char *ident); +static int bectl_jail_cleanup(char *mountpoint, int jid); -/* We'll start with 8 parameters initially and grow as needed. */ -#define INIT_PARAMCOUNT 8 - -static struct jailparam *jp; -static int jpcnt; -static int jpused; static char mnt_loc[BE_MAXPATHLEN]; +static nvlist_t *jailparams; -static void -jailparam_grow(void) -{ +static const char *disabled_params[] = { + "command", "exec.start", "nopersist", "persist", NULL +}; - jpcnt *= 2; - jp = realloc(jp, jpcnt * sizeof(*jp)); - if (jp == NULL) - err(2, "realloc"); -} static void jailparam_add(const char *name, const char *val) { - int i; - for (i = 0; i < jpused; ++i) { - if (strcmp(name, jp[i].jp_name) == 0) - break; - } - - if (i < jpused) - jailparam_free(&jp[i], 1); - else if (jpused == jpcnt) - /* The next slot isn't allocated yet */ - jailparam_grow(); - - if (jailparam_init(&jp[i], name) != 0) - return; - if (jailparam_import(&jp[i], val) != 0) - return; - ++jpused; + nvlist_add_string(jailparams, name, val); } static int jailparam_del(const char *name) { - int i; - char *val; - for (i = 0; i < jpused; ++i) { - if (strcmp(name, jp[i].jp_name) == 0) - break; - } - - if (i == jpused) - return (ENOENT); - - for (; i < jpused - 1; ++i) { - val = jailparam_export(&jp[i + 1]); - - jailparam_free(&jp[i], 1); - /* - * Given the context, the following will really only fail if - * they can't allocate the copy of the name or value. - */ - if (jailparam_init(&jp[i], jp[i + 1].jp_name) != 0) { - free(val); - return (ENOMEM); - } - if (jailparam_import(&jp[i], val) != 0) { - jailparam_free(&jp[i], 1); - free(val); - return (ENOMEM); - } - free(val); - } - - jailparam_free(&jp[i], 1); - --jpused; + nvlist_remove_all(jailparams, name); return (0); } @@ -136,6 +80,7 @@ static bool jailparam_addarg(char *arg) { char *name, *val; + size_t i, len; if (arg == NULL) return (false); @@ -156,6 +101,15 @@ jailparam_addarg(char *arg) } strlcpy(mnt_loc, val, sizeof(mnt_loc)); } + + for (i = 0; disabled_params[i] != NULL; i++) { + len = strlen(disabled_params[i]); + if (strncmp(disabled_params[i], name, len) == 0) { + fprintf(stderr, "invalid jail parameter: %s\n", name); + return (false); + } + } + jailparam_add(name, val); return (true); } @@ -176,22 +130,128 @@ jailparam_delarg(char *arg) return (jailparam_del(name)); } +static int +build_jailcmd(char ***argvp, bool interactive, int argc, char *argv[]) +{ + char *cmd, **jargv, *name, *val; + nvpair_t *nvp; + size_t i, iarg, nargv; + + cmd = NULL; + nvp = NULL; + iarg = i = 0; + if (nvlist_size(jailparams, &nargv, NV_ENCODE_NATIVE) != 0) + return (1); + + /* + * Number of args + "/usr/sbin/jail", "-c", and ending NULL. + * If interactive also include command. + */ + nargv += 3; + if (interactive) { + if (argc == 0) + nargv++; + else + nargv += argc; + } + + jargv = *argvp = calloc(nargv, sizeof(jargv)); + if (jargv == NULL) + err(2, "calloc"); + + jargv[iarg++] = strdup("/usr/sbin/jail"); + jargv[iarg++] = strdup("-c"); + while ((nvp = nvlist_next_nvpair(jailparams, nvp)) != NULL) { + name = nvpair_name(nvp); + if (nvpair_value_string(nvp, &val) != 0) + continue; + + if (asprintf(&jargv[iarg++], "%s=%s", name, val) < 0) + goto error; + } + if (interactive) { + if (argc < 1) + cmd = strdup("/bin/sh"); + else { + cmd = argv[0]; + argc--; + argv++; + } + + if (asprintf(&jargv[iarg++], "command=%s", cmd) < 0) { + goto error; + } + if (argc < 1) { + free(cmd); + cmd = NULL; + } + + for (; argc > 0; argc--) { + if (asprintf(&jargv[iarg++], "%s", argv[0]) < 0) + goto error; + argv++; + } + } + + return (0); + +error: + if (interactive && argc < 1) + free(cmd); + for (; i < iarg - 1; i++) { + free(jargv[i]); + } + free(jargv); + return (1); +} + +/* Remove jail and cleanup any non zfs mounts. */ +static int +bectl_jail_cleanup(char *mountpoint, int jid) +{ + struct statfs *mntbuf; + size_t i, searchlen, mntsize; + + if (jid >= 0 && jail_remove(jid) != 0) { + fprintf(stderr, "unable to remove jail"); + return (1); + } + + searchlen = strnlen(mountpoint, MAXPATHLEN); + mntsize = getmntinfo(&mntbuf, MNT_NOWAIT); + for (i = 0; i < mntsize; i++) { + if (strncmp(mountpoint, mntbuf[i].f_mntonname, searchlen) == 0 && + mntbuf[i].f_type != MNTTYPE_ZFS) { + + if (unmount(mntbuf[i].f_mntonname, 0) != 0) { + fprintf(stderr, "bectl jail: unable to unmount filesystem %s", + mntbuf[i].f_mntonname); + return (1); + } + } + } + + return (0); +} + int bectl_cmd_jail(int argc, char *argv[]) { - char *bootenv, *mountpoint; - int jid, mntflags, opt, ret; + char *bootenv, **jargv, *mountpoint; + int i, jid, mntflags, opt, ret; bool default_hostname, interactive, unjail; pid_t pid; + /* XXX TODO: Allow shallow */ mntflags = BE_MNT_DEEP; default_hostname = interactive = unjail = true; - jpcnt = INIT_PARAMCOUNT; - jp = malloc(jpcnt * sizeof(*jp)); - if (jp == NULL) - err(2, "malloc"); + if ((nvlist_alloc(&jailparams, NV_UNIQUE_NAME, 0)) != 0) { + fprintf(stderr, "nvlist_alloc() failed\n"); + return (1); + } + jailparam_add("persist", "true"); jailparam_add("allow.mount", "true"); jailparam_add("allow.mount.devfs", "true"); @@ -210,6 +270,8 @@ bectl_cmd_jail(int argc, char *argv[]) */ if (strcmp(optarg, "host.hostname") == 0) default_hostname = false; + } else { + return (1); } break; case 'U': @@ -236,13 +298,14 @@ bectl_cmd_jail(int argc, char *argv[]) argc -= optind; argv += optind; - /* struct jail be_jail = { 0 }; */ if (argc < 1) { fprintf(stderr, "bectl jail: missing boot environment name\n"); return (usage(false)); } bootenv = argv[0]; + argc--; + argv++; /* * XXX TODO: if its already mounted, perhaps there should be a flag to @@ -264,45 +327,46 @@ bectl_cmd_jail(int argc, char *argv[]) * This is our indicator that path was not set by the user, so we'll use * the path that libbe generated for us. */ - if (mountpoint == NULL) + if (mountpoint == NULL) { jailparam_add("path", mnt_loc); - /* Create the jail for now, attach later as-needed */ - jid = jailparam_set(jp, jpused, JAIL_CREATE); - if (jid == -1) { - fprintf(stderr, "unable to create jail. error: %d\n", errno); + mountpoint = mnt_loc; + } + + if ((build_jailcmd(&jargv, interactive, argc, argv)) != 0) { + fprintf(stderr, "unable to build argument list for jail command\n"); return (1); } - jailparam_free(jp, jpused); - free(jp); - - /* We're not interactive, nothing more to do here. */ - if (!interactive) - return (0); - pid = fork(); - switch(pid) { + + switch (pid) { case -1: perror("fork"); return (1); case 0: - jail_attach(jid); - /* We're attached within the jail... good bye! */ - chdir("/"); - if (argc > 1) - execve(argv[1], &argv[1], NULL); - else - execl("/bin/sh", "/bin/sh", NULL); - fprintf(stderr, "bectl jail: failed to execute %s\n", - (argc > 1 ? argv[1] : "/bin/sh")); - _exit(1); + execv("/usr/sbin/jail", jargv); + fprintf(stderr, "bectl jail: failed to execute\n"); default: - /* Wait for the child to get back, see if we need to unjail */ waitpid(pid, NULL, 0); } + for (i = 0; jargv[i] != NULL; i++) { + free(jargv[i]); + } + free(jargv); + + if (!interactive) + return (0); + if (unjail) { - jail_remove(jid); + /* + * We're not checking the jail id result here because in the + * case of invalid param, or last command in jail was an error + * the jail will not exist upon exit. bectl_jail_cleanup will + * only jail_remove if the jid is >= 0. + */ + jid = bectl_locate_jail(bootenv); + bectl_jail_cleanup(mountpoint, jid); be_unmount(be, bootenv, 0); } @@ -319,7 +383,6 @@ bectl_search_jail_paths(const char *mnt) /* jail_getv expects name/value strings */ snprintf(lastjid, sizeof(lastjid), "%d", 0); - jid = 0; while ((jid = jail_getv(0, "lastjid", lastjid, "path", &jailpath, NULL)) != -1) { @@ -416,7 +479,7 @@ bectl_cmd_unjail(int argc, char *argv[]) return (1); } - jail_remove(jid); + bectl_jail_cleanup(path, jid); be_unmount(be, target, 0); return (0);