/*** This file is part of systemd. Copyright 2010 Lennart Poettering systemd is free software; you can redistribute it and/or modify it 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with systemd; If not, see . ***/ #include #include #include #include #include "fd-util.h" /* _cleanup_close_ */ #include "mount-util.h" /* mount_verbose */ static char *arg_directory = NULL; static char **arg_parameters = NULL; static int outer_child( const char *directory, const char *console, int pid_socket) { pid_t pid; ssize_t l; int r; assert(directory); assert(console); assert(pid_socket >= 0); r = open(console, O_RDWR, 0); if (r < 0) return log_error_errno(r, "Failed to open console: %m"); if (chdir(directory) < 0) return log_error_errno(errno, "Failed to chdir: %m"); if (chroot(".") < 0) return log_error_errno(errno, "Failed to chroot: %m"); if (unshare(CLONE_NEWNS|CLONE_NEWPID) < 0) return log_error_errno(errno, "unshare: %m"); pid = fork(); switch (pid) { case -1: return log_error_errno(errno, "Failed to fork inner child: %m"); case 0: pid_socket = safe_close(pid_socket); printf("a\n"); r = mount_verbose(LOG_ERR, "proc", "/proc", "proc", MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL); if (r < 0) return r; printf("b\n"); execvp(arg_parameters[0], arg_parameters); return -errno; default: l = send(pid_socket, &pid, sizeof(pid), MSG_NOSIGNAL); if (l < 0) return log_error_errno(errno, "Failed to send PID: %m"); if (l != sizeof(pid)) { log_error("Short write while sending PID."); return -EIO; } pid_socket = safe_close(pid_socket); return 0; } } static int run(int master, const char* console, int *ret) { pid_t pid; _cleanup_close_pair_ int pid_socket_pair[2] = { -1, -1 }; int r; ssize_t l; if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, pid_socket_pair) < 0) return log_error_errno(errno, "Failed to create pid socket pair: %m"); pid = fork(); switch (pid) { case -1: return log_error_errno(errno, "clone() failed%s: %m", errno == EINVAL ? ", do you have namespace support enabled in your kernel? (You need UTS, IPC, PID and NET namespacing built in)" : ""); case 0: if (unshare(CLONE_NEWNS|CLONE_NEWUSER) < 0) return log_error_errno(errno, "unshare: %m"); if (unshare(CLONE_NEWNS) < 0) return log_error_errno(errno, "unshare: %m"); master = safe_close(master); pid_socket_pair[0] = safe_close(pid_socket_pair[0]); r = outer_child(arg_directory, console, pid_socket_pair[1]); if (r < 0) _exit(EXIT_FAILURE); _exit(EXIT_SUCCESS); default: pid_socket_pair[1] = safe_close(pid_socket_pair[1]); printf("pid: %ld\n", (long)pid); r = wait_for_terminate_and_warn("outer child", pid, NULL); if (r != 0) return r < 0 ? r : -EIO; /* And now retrieve the PID of the inner child. */ l = recv(pid_socket_pair[0], &pid, sizeof pid, 0); if (l < 0) return log_error_errno(errno, "Failed to read inner child PID: %m"); if (l != sizeof pid) { log_error("Short read while reading inner child PID."); return -EIO; } printf("pid: %ld\n", (long)pid); r = wait_for_terminate_and_warn("inner child", pid, NULL); if (r < 0) { /* We failed to wait for the container, or the container exited abnormally. */ return r; } else { *ret = r; return 0; } } } int main(int argc, char *argv[]) { char *console = NULL; _cleanup_close_ int master = -1; int r, ret = EXIT_SUCCESS; log_parse_environment(); log_open(); if (argc < 3) return 2; arg_directory = argv[1]; arg_parameters = &argv[2]; master = posix_openpt(O_RDWR|O_NOCTTY|O_CLOEXEC|O_NDELAY); if (master < 0) { r = log_error_errno(errno, "Failed to acquire pseudo tty: %m"); goto finish; } console = ptsname(master); if (!console) { r = log_error_errno(errno, "Failed to determine tty name: %m"); goto finish; } if (unlockpt(master) < 0) { r = log_error_errno(errno, "Failed to unlock tty: %m"); goto finish; } /* for some reason removing this appends " (deleted)" to the entry in /proc/self/fd/ */ if (prctl(PR_SET_CHILD_SUBREAPER, 1) < 0) { r = log_error_errno(errno, "Failed to become subreaper: %m"); goto finish; } r = run(master, console, &ret); finish: return r < 0 ? EXIT_FAILURE : ret; }