Copyright 2010 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
+ Lesser General Public License for more details.
- You should have received a copy of the GNU General Public License
+ You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include "log.h"
#include "util.h"
+#include "mkdir.h"
+#include "audit.h"
#include "missing.h"
#include "cgroup-util.h"
#include "strv.h"
static char *arg_directory = NULL;
static char *arg_user = NULL;
+static char **arg_controllers = NULL;
static bool arg_private_network = false;
static int help(void) {
printf("%s [OPTIONS...] [PATH] [ARGUMENTS...]\n\n"
"Spawn a minimal namespace container for debugging, testing and building.\n\n"
- " -h --help Show this help\n"
- " -D --directory=NAME Root directory for the container\n"
- " -u --user=USER Run the command under specified user or uid\n"
- " --private-network Disable network in container\n",
+ " -h --help Show this help\n"
+ " -D --directory=NAME Root directory for the container\n"
+ " -u --user=USER Run the command under specified user or uid\n"
+ " -C --controllers=LIST Put the container in specified comma-separated cgroup hierarchies\n"
+ " --private-network Disable network in container\n",
program_invocation_short_name);
return 0;
{ "help", no_argument, NULL, 'h' },
{ "directory", required_argument, NULL, 'D' },
{ "user", required_argument, NULL, 'u' },
+ { "controllers", required_argument, NULL, 'C' },
{ "private-network", no_argument, NULL, ARG_PRIVATE_NETWORK },
{ NULL, 0, NULL, 0 }
};
assert(argc >= 0);
assert(argv);
- while ((c = getopt_long(argc, argv, "+hD:u:", options, NULL)) >= 0) {
+ while ((c = getopt_long(argc, argv, "+hD:u:C:", options, NULL)) >= 0) {
switch (c) {
break;
+ case 'C':
+ strv_free(arg_controllers);
+ arg_controllers = strv_split(optarg, ",");
+ if (!arg_controllers) {
+ log_error("Failed to split controllers list.");
+ return -ENOMEM;
+ }
+ strv_uniq(arg_controllers);
+
+ break;
+
case ARG_PRIVATE_NETWORK:
arg_private_network = true;
break;
{ "/proc/sys", "/proc/sys", "bind", NULL, MS_BIND|MS_RDONLY|MS_REMOUNT, true }, /* Then, make it r/o */
{ "/sys", "/sys", "bind", NULL, MS_BIND, true }, /* Bind mount first */
{ "/sys", "/sys", "bind", NULL, MS_BIND|MS_RDONLY|MS_REMOUNT, true }, /* Then, make it r/o */
- { "tmpfs", "/dev", "tmpfs", "mode=755", MS_NOSUID, true },
+ { "tmpfs", "/dev", "tmpfs", "mode=755", MS_NOSUID|MS_STRICTATIME, true },
{ "/dev/pts", "/dev/pts", "bind", NULL, MS_BIND, true },
- { "tmpfs", "/run", "tmpfs", "mode=755", MS_NOSUID|MS_NODEV, true },
+ { "tmpfs", "/run", "tmpfs", "mode=755", MS_NOSUID|MS_NODEV|MS_STRICTATIME, true },
#ifdef HAVE_SELINUX
{ "/sys/fs/selinux", "/sys/fs/selinux", "bind", NULL, MS_BIND, false }, /* Bind mount first */
{ "/sys/fs/selinux", "/sys/fs/selinux", "bind", NULL, MS_BIND|MS_RDONLY|MS_REMOUNT, false }, /* Then, make it r/o */
}
/* Fix the timezone, if possible */
- if (asprintf(&where, "%s/%s", dest, "/etc/localtime") >= 0) {
+ if (asprintf(&where, "%s/etc/localtime", dest) >= 0) {
if (mount("/etc/localtime", where, "bind", MS_BIND, NULL) >= 0)
mount("/etc/localtime", where, "bind", MS_BIND|MS_REMOUNT|MS_RDONLY, NULL);
free(where);
}
+ if (asprintf(&where, "%s/etc/timezone", dest) >= 0) {
+
+ if (mount("/etc/timezone", where, "bind", MS_BIND, NULL) >= 0)
+ mount("/etc/timezone", where, "bind", MS_BIND|MS_REMOUNT|MS_RDONLY, NULL);
+
+ free(where);
+ }
+
return r;
}
pid_t pid = 0;
int r = EXIT_FAILURE, k;
char *oldcg = NULL, *newcg = NULL;
+ char **controller = NULL;
int master = -1;
const char *console = NULL;
struct termios saved_attr, raw_attr;
goto finish;
}
- if ((k = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, newcg, 0)) < 0) {
+ k = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, newcg, 0);
+ if (k < 0) {
log_error("Failed to create cgroup: %s", strerror(-k));
goto finish;
}
+ STRV_FOREACH(controller,arg_controllers) {
+ k = cg_create_and_attach(*controller, newcg, 0);
+ if (k < 0)
+ log_warning("Failed to create cgroup in controller %s: %s", *controller, strerror(-k));
+ }
+
if ((master = posix_openpt(O_RDWR|O_NOCTTY|O_CLOEXEC|O_NDELAY)) < 0) {
log_error("Failed to acquire pseudo tty: %m");
goto finish;
sigset_add_many(&mask, SIGCHLD, SIGWINCH, SIGTERM, SIGINT, -1);
assert_se(sigprocmask(SIG_BLOCK, &mask, NULL) == 0);
- if ((pid = syscall(__NR_clone, SIGCHLD|CLONE_NEWIPC|CLONE_NEWNS|CLONE_NEWPID|CLONE_NEWUTS|(arg_private_network ? CLONE_NEWNET : 0), NULL)) < 0) {
- log_error("clone() failed: %m");
+ pid = syscall(__NR_clone, SIGCHLD|CLONE_NEWIPC|CLONE_NEWNS|CLONE_NEWPID|CLONE_NEWUTS|(arg_private_network ? CLONE_NEWNET : 0), NULL);
+ if (pid < 0) {
+ if (errno == EINVAL)
+ log_error("clone() failed, do you have namespace support enabled in your kernel? (You need UTS, IPC, PID and NET namespacing built in): %m");
+ else
+ log_error("clone() failed: %m");
+
goto finish;
}
cg_kill_recursive_and_wait(SYSTEMD_CGROUP_CONTROLLER, newcg, true);
free(arg_directory);
+ strv_free(arg_controllers);
free(oldcg);
free(newcg);