#endif
#define STACK_SIZE (1024 * 1024)
-#define OPT_ARGS "cC:d:EfFG:h:ij:J:ln:NoO:pP:r:R:sS:uU:w:T:y"
+#define OPT_ARGS "cC:d:e:EfFG:h:ij:J:ln:NoO:pP:r:R:sS:uU:w:t:T:y"
#define OCI_VERSION_STRING "1.0.2"
char *ocibundle;
bool immediately;
struct blob_attr *annotations;
+ int term_timeout;
} opts;
static struct blob_buf ocibuf;
static void free_sysctl(void) {
struct sysctl_val *cur;
+
+ if (!opts.sysctl)
+ return;
+
cur = *opts.sysctl;
while (cur) {
{
char *console_fname;
char dev_console_path[PATH_MAX];
- int slave_console_fd;
+ int slave_console_fd, dev_console_dummy;
/* Open UNIX/98 virtual console */
console_fd = posix_openpt(O_RDWR | O_NOCTTY);
/* mount-bind PTY slave to /dev/console in jail */
snprintf(dev_console_path, sizeof(dev_console_path), "%s/dev/console", jail_root);
- close(creat(dev_console_path, 0620));
+ dev_console_dummy = creat(dev_console_path, 0620);
+ if (dev_console_dummy < 0)
+ goto no_console;
+
+ close(dev_console_dummy);
if (mount(console_fname, dev_console_path, "bind", MS_BIND, NULL))
goto no_console;
{
struct mknod_args **cur, *curdef;
char *path, *tmp;
+ int ret;
if (!opts.devices)
goto only_default_devices;
}
/* Dev symbolic links as defined in OCI spec */
- (void) symlink("/dev/pts/ptmx", "/dev/ptmx");
- (void) symlink("/proc/self/fd", "/dev/fd");
- (void) symlink("/proc/self/fd/0", "/dev/stdin");
- (void) symlink("/proc/self/fd/1", "/dev/stdout");
- (void) symlink("/proc/self/fd/2", "/dev/stderr");
+ ret = symlink("/dev/pts/ptmx", "/dev/ptmx");
+ if (ret < 0)
+ WARNING("symlink() failed to create link to /dev/pts/ptmx");
+
+ ret = symlink("/proc/self/fd", "/dev/fd");
+ if (ret < 0)
+ WARNING("symlink() failed to create link to /proc/self/fd");
+
+ ret = symlink("/proc/self/fd/0", "/dev/stdin");
+ if (ret < 0)
+ WARNING("symlink() failed to create link to /proc/self/fd/0");
+
+ ret = symlink("/proc/self/fd/1", "/dev/stdout");
+ if (ret < 0)
+ WARNING("symlink() failed to create link to /proc/self/fd/1");
+
+ ret = symlink("/proc/self/fd/2", "/dev/stderr");
+ if (ret < 0)
+ WARNING("symlink() failed to create link to /proc/self/fd/2");
return 0;
}
if (overlaydir)
unlink(jaillink);
- (void) symlink("../dev/resolv.conf.d/resolv.conf.auto", jaillink);
+ ret = symlink("../dev/resolv.conf.d/resolv.conf.auto", jaillink);
+ if (ret < 0)
+ WARNING("symlink() failed to create link to ../dev/resolv.conf.d/resolv.conf.auto");
}
run_hooks(opts.hooks.createContainer, enter_jail_fs);
fprintf(stderr, " -C <file>\tcapabilities drop config\n");
fprintf(stderr, " -c\t\tset PR_SET_NO_NEW_PRIVS\n");
fprintf(stderr, " -n <name>\tthe name of the jail\n");
+ fprintf(stderr, " -e <var>\timport environment variable\n");
fprintf(stderr, "namespace jail options:\n");
fprintf(stderr, " -h <hostname>\tchange the hostname of the jail\n");
fprintf(stderr, " -N\t\tjail has network namespace\n");
if (hook_running) {
DEBUG("forwarding signal %d to the hook process\n", signo);
kill(hook_process.pid, signo);
+ /* set timeout to send SIGKILL hook process in case SIGTERM doesn't succeed */
+ if (signo == SIGTERM)
+ uloop_timeout_set(&hook_process_timeout, opts.term_timeout * 1000);
}
if (jail_running) {
DEBUG("forwarding signal %d to the jailed process\n", signo);
kill(jail_process.pid, signo);
+ /* set timeout to send SIGKILL jail process in case SIGTERM doesn't succeed */
+ if (signo == SIGTERM)
+ uloop_timeout_set(&jail_process_timeout, opts.term_timeout * 1000);
}
}
if (!sigismember(&sigmask, i))
continue;
- if ((i == SIGCHLD) || (i == SIGPIPE) || (i == SIGSEGV))
+ if ((i == SIGCHLD) || (i == SIGPIPE) || (i == SIGSEGV) || (i == SIGSTOP) || (i == SIGKILL))
continue;
s.sa_handler = jail_handle_signal;
exit(EXIT_FAILURE);
}
-static int ns_open_pid(const char *nstype, const pid_t target_ns)
+int ns_open_pid(const char *nstype, const pid_t target_ns)
{
char pid_pid_path[PATH_MAX];
return open(pid_pid_path, O_RDONLY);
}
-static void netns_updown(pid_t pid, bool start)
-{
- static struct blob_buf req;
- uint32_t id;
-
- if (!parent_ctx)
- return;
-
- blob_buf_init(&req, 0);
- blobmsg_add_string(&req, "jail", opts.name);
- blobmsg_add_u32(&req, "pid", pid);
- blobmsg_add_u8(&req, "start", start);
-
- if (ubus_lookup_id(parent_ctx, "network", &id) ||
- ubus_invoke(parent_ctx, id, "netns_updown", req.head, NULL, NULL, 3000))
- INFO("ubus request failed\n");
-
- blob_buf_free(&req);
-}
-
static int parseOCIenvarray(struct blob_attr *msg, char ***envp)
{
struct blob_attr *cur;
static int timens_fd;
#endif
static void post_create_runtime(void);
+
+struct env_e {
+ struct list_head list;
+ char *envarg;
+};
+
int main(int argc, char **argv)
{
uid_t uid = getuid();
const char ubus[] = "/var/run/ubus/ubus.sock";
int ret = EXIT_FAILURE;
int ch;
+ char *tmp;
+ struct list_head envl = LIST_HEAD_INIT(envl);
+ struct env_e *enve, *tmpenve;
+ unsigned short int envn = 0, envc = 0;
if (uid) {
ERROR("not root, aborting: %m\n");
opts.setns.time = -1;
#endif
+ /* default 5 seconds timeout after SIGTERM before SIGKILL is sent */
+ opts.term_timeout = 5;
+
umask(022);
mount_list_init();
init_library_search();
case 'd':
debug = atoi(optarg);
break;
+ case 'e':
+ enve = calloc(1, sizeof(*enve));
+ enve->envarg = optarg;
+ list_add_tail(&enve->list, &envl);
+ break;
case 'p':
opts.namespace |= CLONE_NEWNS;
opts.procfs = 1;
break;
case 'r':
opts.namespace |= CLONE_NEWNS;
- add_path_and_deps(optarg, 1, 0, 0);
+ tmp = strchr(optarg, ':');
+ if (tmp) {
+ *(tmp++) = '\0';
+ add_2paths_and_deps(optarg, tmp, 1, 0, 0);
+ } else {
+ add_path_and_deps(optarg, 1, 0, 0);
+ }
break;
case 'w':
opts.namespace |= CLONE_NEWNS;
- add_path_and_deps(optarg, 0, 0, 0);
+ tmp = strchr(optarg, ':');
+ if (tmp) {
+ *(tmp++) = '\0';
+ add_2paths_and_deps(optarg, tmp, 0, 0, 0);
+ } else {
+ add_path_and_deps(optarg, 0, 0, 0);
+ }
break;
case 'u':
opts.namespace |= CLONE_NEWNS;
case 'O':
opts.overlaydir = realpath(optarg, NULL);
break;
+ case 't':
+ opts.term_timeout = atoi(optarg);
+ break;
case 'T':
opts.tmpoverlaysize = optarg;
break;
if (opts.namespace && !opts.ocibundle)
opts.namespace |= CLONE_NEWIPC | CLONE_NEWPID;
+ /*
+ * env import from cmdline is not available for OCI containers
+ */
+ if (opts.ocibundle && !list_empty(&envl)) {
+ ret=-ENOTSUP;
+ goto errout;
+ }
+
+ /*
+ * prepare list of env variables to import for slim containers
+ */
+ if (!list_empty(&envl)) {
+ list_for_each_entry(enve, &envl, list)
+ ++envn;
+
+ opts.envp = calloc(1 + envn, sizeof(char*));
+ list_for_each_entry_safe(enve, tmpenve, &envl, list) {
+ tmp = getenv(enve->envarg);
+ if (tmp) {
+ ret = asprintf(&opts.envp[envc++], "%s=%s", enve->envarg, tmp);
+ if (ret < 0) {
+ ERROR("filed to handle envargs %s\n", tmp);
+ free(enve);
+ goto errout;
+ }
+ }
+
+ list_del(&enve->list);
+ free(enve);
+ }
+
+ opts.envp[envc] = NULL;
+ }
+
/*
* uid in parent user namespace representing root user in new
* user namespace, defaults to nobody unless specified in uidMappings
}
}
- if (opts.namespace & CLONE_NEWNET) {
+ if (opts.namespace & CLONE_NEWNET)
jail_network_start(parent_ctx, opts.name, jail_process.pid);
- netns_fd = ns_open_pid("net", jail_process.pid);
- netns_updown(jail_process.pid, true);
- }
if (jail_writepid(jail_process.pid)) {
ERROR("failed to write pidfile: %m\n");
static void poststop(void) {
if (opts.namespace & CLONE_NEWNET) {
setns(netns_fd, CLONE_NEWNET);
- netns_updown(getpid(), false);
jail_network_stop();
close(netns_fd);
}