nspawn.c revision 9875fd7875d433eea5c6e3319916e1be18722086
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering/***
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering This file is part of systemd.
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering Copyright 2010 Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering systemd is free software; you can redistribute it and/or modify it
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering under the terms of the GNU Lesser General Public License as published by
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering the Free Software Foundation; either version 2.1 of the License, or
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering (at your option) any later version.
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering systemd is distributed in the hope that it will be useful, but
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering Lesser General Public License for more details.
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering You should have received a copy of the GNU Lesser General Public License
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering***/
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include <signal.h>
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include <sched.h>
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include <unistd.h>
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include <sys/types.h>
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include <sys/syscall.h>
b5efdb8af40ea759a1ea584c1bc44ecc81dd00ceLennart Poettering#include <sys/mount.h>
4f5dd3943bef8a04be7e3b838b822bb9a7ad6cb3Lennart Poettering#include <sys/wait.h>
3ffd4af22052963e7a29431721ee204e634bea75Lennart Poettering#include <stdlib.h>
a5c32cff1f56afe6f0c6c70d91a88a7a8238b2d7Harald Hoyer#include <string.h>
6482f6269c87d2249e52e889a63adbdd50f2d691Ronny Chevalier#include <stdio.h>
3ffd4af22052963e7a29431721ee204e634bea75Lennart Poettering#include <errno.h>
4f5dd3943bef8a04be7e3b838b822bb9a7ad6cb3Lennart Poettering#include <sys/prctl.h>
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering#include <sys/capability.h>
8b43440b7ef4b81c69c31de7ff820dc07a780254Lennart Poettering#include <getopt.h>
07630cea1f3a845c09309f197ac7c4f11edd3b62Lennart Poettering#include <termios.h>
b1d4f8e154bf61b5de1b27461ef8e9c8c5e838a1Lennart Poettering#include <sys/signalfd.h>
4f5dd3943bef8a04be7e3b838b822bb9a7ad6cb3Lennart Poettering#include <grp.h>
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include <linux/fs.h>
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include <sys/un.h>
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include <sys/socket.h>
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include <linux/netlink.h>
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include <sys/eventfd.h>
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include <net/if.h>
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include <linux/veth.h>
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include <sys/personality.h>
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#ifdef HAVE_SELINUX
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include <selinux/selinux.h>
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#endif
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#ifdef HAVE_SECCOMP
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include <seccomp.h>
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#endif
2b6bf07dd23bb467099d213c97b3875c5e453491Zbigniew Jędrzejewski-Szmek
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include "sd-daemon.h"
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include "sd-bus.h"
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include "sd-id128.h"
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include "sd-rtnl.h"
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include "log.h"
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include "util.h"
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include "mkdir.h"
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include "macro.h"
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include "audit.h"
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include "missing.h"
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include "cgroup-util.h"
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include "strv.h"
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include "path-util.h"
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include "loopback-setup.h"
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include "dev-setup.h"
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include "fdset.h"
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include "build.h"
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering#include "fileio.h"
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include "bus-util.h"
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include "bus-error.h"
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering#include "ptyfwd.h"
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering#include "bus-kernel.h"
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering#include "env-util.h"
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include "def.h"
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include "rtnl-util.h"
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include "udev-util.h"
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#ifdef HAVE_SECCOMP
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#include "seccomp-util.h"
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering#endif
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poetteringtypedef enum LinkJournal {
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering LINK_NO,
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering LINK_AUTO,
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering LINK_HOST,
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering LINK_GUEST
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering} LinkJournal;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
d2e54fae5ca7a0f71b5ac8b356a589ff0a09ea0aKay Sieversstatic char *arg_directory = NULL;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poetteringstatic char *arg_user = NULL;
dacd6cee76a08331b8c8616c5f30f70ee49aa2f9Lennart Poetteringstatic sd_id128_t arg_uuid = {};
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poetteringstatic char *arg_machine = NULL;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poetteringstatic const char *arg_selinux_context = NULL;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poetteringstatic const char *arg_selinux_apifs_context = NULL;
dacd6cee76a08331b8c8616c5f30f70ee49aa2f9Lennart Poetteringstatic const char *arg_slice = NULL;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poetteringstatic bool arg_private_network = false;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poetteringstatic bool arg_read_only = false;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poetteringstatic bool arg_boot = false;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poetteringstatic LinkJournal arg_link_journal = LINK_AUTO;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poetteringstatic uint64_t arg_retain =
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering (1ULL << CAP_CHOWN) |
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering (1ULL << CAP_DAC_OVERRIDE) |
90b2de37b80603168f4e9c9c81cff7eea4efa21aZbigniew Jędrzejewski-Szmek (1ULL << CAP_DAC_READ_SEARCH) |
90b2de37b80603168f4e9c9c81cff7eea4efa21aZbigniew Jędrzejewski-Szmek (1ULL << CAP_FOWNER) |
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering (1ULL << CAP_FSETID) |
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering (1ULL << CAP_IPC_OWNER) |
90b2de37b80603168f4e9c9c81cff7eea4efa21aZbigniew Jędrzejewski-Szmek (1ULL << CAP_KILL) |
90b2de37b80603168f4e9c9c81cff7eea4efa21aZbigniew Jędrzejewski-Szmek (1ULL << CAP_LEASE) |
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering (1ULL << CAP_LINUX_IMMUTABLE) |
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering (1ULL << CAP_NET_BIND_SERVICE) |
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering (1ULL << CAP_NET_BROADCAST) |
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering (1ULL << CAP_NET_RAW) |
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering (1ULL << CAP_SETGID) |
88231eb62cafc8bb51406919c8cf6019dc1ac916Thomas Hindoe Paaboel Andersen (1ULL << CAP_SETFCAP) |
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering (1ULL << CAP_SETPCAP) |
88231eb62cafc8bb51406919c8cf6019dc1ac916Thomas Hindoe Paaboel Andersen (1ULL << CAP_SETUID) |
88231eb62cafc8bb51406919c8cf6019dc1ac916Thomas Hindoe Paaboel Andersen (1ULL << CAP_SYS_ADMIN) |
88231eb62cafc8bb51406919c8cf6019dc1ac916Thomas Hindoe Paaboel Andersen (1ULL << CAP_SYS_CHROOT) |
88231eb62cafc8bb51406919c8cf6019dc1ac916Thomas Hindoe Paaboel Andersen (1ULL << CAP_SYS_NICE) |
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering (1ULL << CAP_SYS_PTRACE) |
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering (1ULL << CAP_SYS_TTY_CONFIG) |
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering (1ULL << CAP_SYS_RESOURCE) |
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering (1ULL << CAP_SYS_BOOT) |
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering (1ULL << CAP_AUDIT_WRITE) |
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering (1ULL << CAP_AUDIT_CONTROL) |
88231eb62cafc8bb51406919c8cf6019dc1ac916Thomas Hindoe Paaboel Andersen (1ULL << CAP_MKNOD);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poetteringstatic char **arg_bind = NULL;
88231eb62cafc8bb51406919c8cf6019dc1ac916Thomas Hindoe Paaboel Andersenstatic char **arg_bind_ro = NULL;
88231eb62cafc8bb51406919c8cf6019dc1ac916Thomas Hindoe Paaboel Andersenstatic char **arg_setenv = NULL;
88231eb62cafc8bb51406919c8cf6019dc1ac916Thomas Hindoe Paaboel Andersenstatic bool arg_quiet = false;
88231eb62cafc8bb51406919c8cf6019dc1ac916Thomas Hindoe Paaboel Andersenstatic bool arg_share_system = false;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poetteringstatic bool arg_register = true;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poetteringstatic bool arg_keep_unit = false;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poetteringstatic char **arg_network_interfaces = NULL;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poetteringstatic char **arg_network_macvlan = NULL;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poetteringstatic bool arg_network_veth = false;
dacd6cee76a08331b8c8616c5f30f70ee49aa2f9Lennart Poetteringstatic const char *arg_network_bridge = NULL;
dacd6cee76a08331b8c8616c5f30f70ee49aa2f9Lennart Poetteringstatic unsigned long arg_personality = 0xffffffffLU;
dacd6cee76a08331b8c8616c5f30f70ee49aa2f9Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poetteringstatic int help(void) {
dacd6cee76a08331b8c8616c5f30f70ee49aa2f9Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering printf("%s [OPTIONS...] [PATH] [ARGUMENTS...]\n\n"
dacd6cee76a08331b8c8616c5f30f70ee49aa2f9Lennart Poettering "Spawn a minimal namespace container for debugging, testing and building.\n\n"
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering " -h --help Show this help\n"
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering " --version Print version string\n"
dacd6cee76a08331b8c8616c5f30f70ee49aa2f9Lennart Poettering " -q --quiet Do not show status information\n"
dacd6cee76a08331b8c8616c5f30f70ee49aa2f9Lennart Poettering " -D --directory=NAME Root directory for the container\n"
dacd6cee76a08331b8c8616c5f30f70ee49aa2f9Lennart Poettering " -b --boot Boot up full system (i.e. invoke init)\n"
dacd6cee76a08331b8c8616c5f30f70ee49aa2f9Lennart Poettering " -u --user=USER Run the command under specified user or uid\n"
dacd6cee76a08331b8c8616c5f30f70ee49aa2f9Lennart Poettering " -M --machine=NAME Set the machine name for the container\n"
dacd6cee76a08331b8c8616c5f30f70ee49aa2f9Lennart Poettering " --uuid=UUID Set a specific machine UUID for the container\n"
dacd6cee76a08331b8c8616c5f30f70ee49aa2f9Lennart Poettering " -S --slice=SLICE Place the container in the specified slice\n"
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering " --private-network Disable network in container\n"
dacd6cee76a08331b8c8616c5f30f70ee49aa2f9Lennart Poettering " --network-interface=INTERFACE\n"
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering " Assign an existing network interface to the\n"
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering " container\n"
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering " --network-macvlan=INTERFACE\n"
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering " Create a macvlan network interface based on an\n"
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering " existing network interface to the container\n"
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering " --network-veth Add a virtual ethernet connection between host\n"
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering " and container\n"
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering " --network-bridge=INTERFACE\n"
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering " Add a virtual ethernet connection between host\n"
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering " and container and add it to an existing bridge on\n"
de0671ee7fe465e108f62dcbbbe9366f81dd9e9aZbigniew Jędrzejewski-Szmek " the host\n"
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering " -Z --selinux-context=SECLABEL\n"
de0671ee7fe465e108f62dcbbbe9366f81dd9e9aZbigniew Jędrzejewski-Szmek " Set the SELinux security context to be used by\n"
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering " processes in the container\n"
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering " -L --selinux-apifs-context=SECLABEL\n"
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering " Set the SELinux security context to be used by\n"
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering " API/tmpfs file systems in the container\n"
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering " --capability=CAP In addition to the default, retain specified\n"
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering " capability\n"
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering " --drop-capability=CAP Drop the specified capability from the default set\n"
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering " --link-journal=MODE Link up guest journal, one of no, auto, guest, host\n"
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering " -j Equivalent to --link-journal=host\n"
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering " --read-only Mount the root directory read-only\n"
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering " --bind=PATH[:PATH] Bind mount a file or directory from the host into\n"
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering " the container\n"
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering " --bind-ro=PATH[:PATH] Similar, but creates a read-only bind mount\n"
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering " --setenv=NAME=VALUE Pass an environment variable to PID 1\n"
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering " --share-system Share system namespaces with host\n"
de0671ee7fe465e108f62dcbbbe9366f81dd9e9aZbigniew Jędrzejewski-Szmek " --register=BOOLEAN Register container as machine\n"
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering " --keep-unit Do not register a scope for the machine, reuse\n"
de0671ee7fe465e108f62dcbbbe9366f81dd9e9aZbigniew Jędrzejewski-Szmek " the service unit nspawn is running in\n",
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering program_invocation_short_name);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering return 0;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering}
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poetteringstatic int parse_argv(int argc, char *argv[]) {
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering enum {
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering ARG_VERSION = 0x100,
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering ARG_PRIVATE_NETWORK,
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering ARG_UUID,
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering ARG_READ_ONLY,
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering ARG_CAPABILITY,
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering ARG_DROP_CAPABILITY,
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering ARG_LINK_JOURNAL,
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering ARG_BIND,
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering ARG_BIND_RO,
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering ARG_SETENV,
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering ARG_SHARE_SYSTEM,
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering ARG_REGISTER,
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering ARG_KEEP_UNIT,
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering ARG_NETWORK_INTERFACE,
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering ARG_NETWORK_MACVLAN,
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering ARG_NETWORK_VETH,
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering ARG_NETWORK_BRIDGE,
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering ARG_PERSONALITY,
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering };
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering static const struct option options[] = {
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering { "help", no_argument, NULL, 'h' },
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering { "version", no_argument, NULL, ARG_VERSION },
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering { "directory", required_argument, NULL, 'D' },
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering { "user", required_argument, NULL, 'u' },
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering { "private-network", no_argument, NULL, ARG_PRIVATE_NETWORK },
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering { "boot", no_argument, NULL, 'b' },
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering { "uuid", required_argument, NULL, ARG_UUID },
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering { "read-only", no_argument, NULL, ARG_READ_ONLY },
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering { "capability", required_argument, NULL, ARG_CAPABILITY },
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering { "drop-capability", required_argument, NULL, ARG_DROP_CAPABILITY },
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering { "link-journal", required_argument, NULL, ARG_LINK_JOURNAL },
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering { "bind", required_argument, NULL, ARG_BIND },
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering { "bind-ro", required_argument, NULL, ARG_BIND_RO },
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering { "machine", required_argument, NULL, 'M' },
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering { "slice", required_argument, NULL, 'S' },
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering { "setenv", required_argument, NULL, ARG_SETENV },
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering { "selinux-context", required_argument, NULL, 'Z' },
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering { "selinux-apifs-context", required_argument, NULL, 'L' },
a34faf579d2be139b0b9e8cd0c73ad4d918ef736Lukas Nykryn { "quiet", no_argument, NULL, 'q' },
a34faf579d2be139b0b9e8cd0c73ad4d918ef736Lukas Nykryn { "share-system", no_argument, NULL, ARG_SHARE_SYSTEM },
a34faf579d2be139b0b9e8cd0c73ad4d918ef736Lukas Nykryn { "register", required_argument, NULL, ARG_REGISTER },
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering { "keep-unit", no_argument, NULL, ARG_KEEP_UNIT },
a34faf579d2be139b0b9e8cd0c73ad4d918ef736Lukas Nykryn { "network-interface", required_argument, NULL, ARG_NETWORK_INTERFACE },
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering { "network-macvlan", required_argument, NULL, ARG_NETWORK_MACVLAN },
a34faf579d2be139b0b9e8cd0c73ad4d918ef736Lukas Nykryn { "network-veth", no_argument, NULL, ARG_NETWORK_VETH },
a34faf579d2be139b0b9e8cd0c73ad4d918ef736Lukas Nykryn { "network-bridge", required_argument, NULL, ARG_NETWORK_BRIDGE },
a34faf579d2be139b0b9e8cd0c73ad4d918ef736Lukas Nykryn { "personality", required_argument, NULL, ARG_PERSONALITY },
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering {}
a34faf579d2be139b0b9e8cd0c73ad4d918ef736Lukas Nykryn };
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering int c, r;
527b7a421ff3927d4f3f170b1b143452e88ae1dcLennart Poettering uint64_t plus = 0, minus = 0;
527b7a421ff3927d4f3f170b1b143452e88ae1dcLennart Poettering
527b7a421ff3927d4f3f170b1b143452e88ae1dcLennart Poettering assert(argc >= 0);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering assert(argv);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering while ((c = getopt_long(argc, argv, "+hD:u:bL:M:jS:Z:q", options, NULL)) >= 0) {
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering switch (c) {
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
527b7a421ff3927d4f3f170b1b143452e88ae1dcLennart Poettering case 'h':
527b7a421ff3927d4f3f170b1b143452e88ae1dcLennart Poettering return help();
527b7a421ff3927d4f3f170b1b143452e88ae1dcLennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering case ARG_VERSION:
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering puts(PACKAGE_STRING);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering puts(SYSTEMD_FEATURES);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering return 0;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering case 'D':
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering free(arg_directory);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering arg_directory = canonicalize_file_name(optarg);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering if (!arg_directory) {
03e334a1c7dc8c20c38902aa039440763acc9b17Lennart Poettering log_error("Invalid root directory: %m");
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering return -ENOMEM;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering }
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering break;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering case 'u':
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering free(arg_user);
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering arg_user = strdup(optarg);
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering if (!arg_user)
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering return log_oom();
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering break;
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering case ARG_NETWORK_BRIDGE:
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering arg_network_bridge = optarg;
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering /* fall through */
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering case ARG_NETWORK_VETH:
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering arg_network_veth = true;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering arg_private_network = true;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering break;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering case ARG_NETWORK_INTERFACE:
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering if (strv_extend(&arg_network_interfaces, optarg) < 0)
d2e54fae5ca7a0f71b5ac8b356a589ff0a09ea0aKay Sievers return log_oom();
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering arg_private_network = true;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering break;
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering case ARG_NETWORK_MACVLAN:
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering if (strv_extend(&arg_network_macvlan, optarg) < 0)
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering return log_oom();
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering /* fall through */
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering case ARG_PRIVATE_NETWORK:
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering arg_private_network = true;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering break;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering case 'b':
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering arg_boot = true;
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering break;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering case ARG_UUID:
151b9b9662a90455262ce575a8a8ae74bf4ff336Lennart Poettering r = sd_id128_from_string(optarg, &arg_uuid);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering if (r < 0) {
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering log_error("Invalid UUID: %s", optarg);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering return r;
718db96199eb307751264e4163555662c9a389faLennart Poettering }
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering break;
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering case 'S':
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering arg_slice = optarg;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering break;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering case 'M':
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering if (isempty(optarg)) {
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering free(arg_machine);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering arg_machine = NULL;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering } else {
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering if (!hostname_is_valid(optarg)) {
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering log_error("Invalid machine name: %s", optarg);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering return -EINVAL;
03e334a1c7dc8c20c38902aa039440763acc9b17Lennart Poettering }
03e334a1c7dc8c20c38902aa039440763acc9b17Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering free(arg_machine);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering arg_machine = strdup(optarg);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering if (!arg_machine)
a1e58e8ee1c84b633d6d6d651d5328d4dd4eba5bLennart Poettering return log_oom();
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering break;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering }
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering case 'Z':
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering arg_selinux_context = optarg;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering break;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering case 'L':
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering arg_selinux_apifs_context = optarg;
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering break;
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering case ARG_READ_ONLY:
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering arg_read_only = true;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering break;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering case ARG_CAPABILITY:
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering case ARG_DROP_CAPABILITY: {
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering char *state, *word;
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering size_t length;
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering FOREACH_WORD_SEPARATOR(word, length, optarg, ",", state) {
2c4f86c1298f402220965682ab0e7729e150a562Lennart Poettering _cleanup_free_ char *t;
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering cap_value_t cap;
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering
2c4f86c1298f402220965682ab0e7729e150a562Lennart Poettering t = strndup(word, length);
2c4f86c1298f402220965682ab0e7729e150a562Lennart Poettering if (!t)
2c4f86c1298f402220965682ab0e7729e150a562Lennart Poettering return log_oom();
2c4f86c1298f402220965682ab0e7729e150a562Lennart Poettering
2c4f86c1298f402220965682ab0e7729e150a562Lennart Poettering if (streq(t, "all")) {
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering if (c == ARG_CAPABILITY)
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering plus = (uint64_t) -1;
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering else
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering minus = (uint64_t) -1;
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering } else {
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering if (cap_from_name(t, &cap) < 0) {
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering log_error("Failed to parse capability %s.", t);
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering return -EINVAL;
409133be63387fc04d927e8aecd2f6ba03d2f143Lennart Poettering }
409133be63387fc04d927e8aecd2f6ba03d2f143Lennart Poettering
85a428c69465b047731b6abb5005f01824f1444eLennart Poettering if (c == ARG_CAPABILITY)
85a428c69465b047731b6abb5005f01824f1444eLennart Poettering plus |= 1ULL << (uint64_t) cap;
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering else
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering minus |= 1ULL << (uint64_t) cap;
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering }
5cb14b3742038b28551b161635a0cba3559404b2Lennart Poettering }
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering break;
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering }
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering case 'j':
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering arg_link_journal = LINK_GUEST;
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering break;
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering case ARG_LINK_JOURNAL:
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering if (streq(optarg, "auto"))
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering arg_link_journal = LINK_AUTO;
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering else if (streq(optarg, "no"))
409133be63387fc04d927e8aecd2f6ba03d2f143Lennart Poettering arg_link_journal = LINK_NO;
409133be63387fc04d927e8aecd2f6ba03d2f143Lennart Poettering else if (streq(optarg, "guest"))
409133be63387fc04d927e8aecd2f6ba03d2f143Lennart Poettering arg_link_journal = LINK_GUEST;
409133be63387fc04d927e8aecd2f6ba03d2f143Lennart Poettering else if (streq(optarg, "host"))
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering arg_link_journal = LINK_HOST;
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering else {
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering log_error("Failed to parse link journal mode %s", optarg);
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering return -EINVAL;
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering }
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering break;
85a428c69465b047731b6abb5005f01824f1444eLennart Poettering
85a428c69465b047731b6abb5005f01824f1444eLennart Poettering case ARG_BIND:
85a428c69465b047731b6abb5005f01824f1444eLennart Poettering case ARG_BIND_RO: {
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering _cleanup_free_ char *a = NULL, *b = NULL;
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering char *e;
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering char ***x;
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering x = c == ARG_BIND ? &arg_bind : &arg_bind_ro;
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering e = strchr(optarg, ':');
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering if (e) {
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering a = strndup(optarg, e - optarg);
ec202eae8e84a4c99f054f771cb832046cb8769fShawn Landden b = strdup(e + 1);
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering } else {
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering a = strdup(optarg);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering b = strdup(optarg);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering }
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering if (!a || !b)
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering return log_oom();
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering if (!path_is_absolute(a) || !path_is_absolute(b)) {
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering log_error("Invalid bind mount specification: %s", optarg);
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering return -EINVAL;
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering }
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering r = strv_extend(x, a);
8e7fd6ade44ce5dde0867ba748c7978ed1206865Lennart Poettering if (r < 0)
8e7fd6ade44ce5dde0867ba748c7978ed1206865Lennart Poettering return log_oom();
8e7fd6ade44ce5dde0867ba748c7978ed1206865Lennart Poettering
8e7fd6ade44ce5dde0867ba748c7978ed1206865Lennart Poettering r = strv_extend(x, b);
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering if (r < 0)
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering return log_oom();
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering break;
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering }
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering case ARG_SETENV: {
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering char **n;
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering if (!env_assignment_is_valid(optarg)) {
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering log_error("Environment variable assignment '%s' is not valid.", optarg);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering return -EINVAL;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering }
a2a5291b3f5ab6ed4c92f51d0fd10a03047380d8Zbigniew Jędrzejewski-Szmek
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering n = strv_env_set(arg_setenv, optarg);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering if (!n)
a2a5291b3f5ab6ed4c92f51d0fd10a03047380d8Zbigniew Jędrzejewski-Szmek return log_oom();
a2a5291b3f5ab6ed4c92f51d0fd10a03047380d8Zbigniew Jędrzejewski-Szmek
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering strv_free(arg_setenv);
a2a5291b3f5ab6ed4c92f51d0fd10a03047380d8Zbigniew Jędrzejewski-Szmek arg_setenv = n;
4943c1c94ba751c98763f4232b4350481b22c90aLennart Poettering break;
a2a5291b3f5ab6ed4c92f51d0fd10a03047380d8Zbigniew Jędrzejewski-Szmek }
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering
a2a5291b3f5ab6ed4c92f51d0fd10a03047380d8Zbigniew Jędrzejewski-Szmek case 'q':
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering arg_quiet = true;
a2a5291b3f5ab6ed4c92f51d0fd10a03047380d8Zbigniew Jędrzejewski-Szmek break;
8e7fd6ade44ce5dde0867ba748c7978ed1206865Lennart Poettering
a2a5291b3f5ab6ed4c92f51d0fd10a03047380d8Zbigniew Jędrzejewski-Szmek case ARG_SHARE_SYSTEM:
8e7fd6ade44ce5dde0867ba748c7978ed1206865Lennart Poettering arg_share_system = true;
a2a5291b3f5ab6ed4c92f51d0fd10a03047380d8Zbigniew Jędrzejewski-Szmek break;
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering case ARG_REGISTER:
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering r = parse_boolean(optarg);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering if (r < 0) {
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering log_error("Failed to parse --register= argument: %s", optarg);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering return r;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering }
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering arg_register = r;
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering break;
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering case ARG_KEEP_UNIT:
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering arg_keep_unit = true;
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering break;
case ARG_PERSONALITY:
arg_personality = personality_from_string(optarg);
if (arg_personality == 0xffffffffLU) {
log_error("Unknown or unsupported personality '%s'.", optarg);
return -EINVAL;
}
break;
case '?':
return -EINVAL;
default:
assert_not_reached("Unhandled option");
}
}
if (arg_share_system)
arg_register = false;
if (arg_boot && arg_share_system) {
log_error("--boot and --share-system may not be combined.");
return -EINVAL;
}
if (arg_keep_unit && cg_pid_get_owner_uid(0, NULL) >= 0) {
log_error("--keep-unit may not be used when invoked from a user session.");
return -EINVAL;
}
arg_retain = (arg_retain | plus | (arg_private_network ? 1ULL << CAP_NET_ADMIN : 0)) & ~minus;
return 1;
}
static int mount_all(const char *dest) {
typedef struct MountPoint {
const char *what;
const char *where;
const char *type;
const char *options;
unsigned long flags;
bool fatal;
} MountPoint;
static const MountPoint mount_table[] = {
{ "proc", "/proc", "proc", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, true },
{ "/proc/sys", "/proc/sys", NULL, NULL, MS_BIND, true }, /* Bind mount first */
{ NULL, "/proc/sys", NULL, NULL, MS_BIND|MS_RDONLY|MS_REMOUNT, true }, /* Then, make it r/o */
{ "sysfs", "/sys", "sysfs", NULL, MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV, true },
{ "tmpfs", "/dev", "tmpfs", "mode=755", MS_NOSUID|MS_STRICTATIME, true },
{ "devpts", "/dev/pts", "devpts","newinstance,ptmxmode=0666,mode=620,gid=" STRINGIFY(TTY_GID), MS_NOSUID|MS_NOEXEC, true },
{ "tmpfs", "/dev/shm", "tmpfs", "mode=1777", MS_NOSUID|MS_NODEV|MS_STRICTATIME, true },
{ "tmpfs", "/run", "tmpfs", "mode=755", MS_NOSUID|MS_NODEV|MS_STRICTATIME, true },
#ifdef HAVE_SELINUX
{ "/sys/fs/selinux", "/sys/fs/selinux", NULL, NULL, MS_BIND, false }, /* Bind mount first */
{ NULL, "/sys/fs/selinux", NULL, NULL, MS_BIND|MS_RDONLY|MS_REMOUNT, false }, /* Then, make it r/o */
#endif
};
unsigned k;
int r = 0;
for (k = 0; k < ELEMENTSOF(mount_table); k++) {
_cleanup_free_ char *where = NULL;
#ifdef HAVE_SELINUX
_cleanup_free_ char *options = NULL;
#endif
const char *o;
int t;
where = strjoin(dest, "/", mount_table[k].where, NULL);
if (!where)
return log_oom();
t = path_is_mount_point(where, true);
if (t < 0) {
log_error("Failed to detect whether %s is a mount point: %s", where, strerror(-t));
if (r == 0)
r = t;
continue;
}
/* Skip this entry if it is not a remount. */
if (mount_table[k].what && t > 0)
continue;
mkdir_p(where, 0755);
#ifdef HAVE_SELINUX
if (arg_selinux_apifs_context &&
(streq_ptr(mount_table[k].what, "tmpfs") || streq_ptr(mount_table[k].what, "devpts"))) {
options = strjoin(mount_table[k].options, ",context=\"", arg_selinux_apifs_context, "\"", NULL);
if (!options)
return log_oom();
o = options;
} else
#endif
o = mount_table[k].options;
if (mount(mount_table[k].what,
where,
mount_table[k].type,
mount_table[k].flags,
o) < 0 &&
mount_table[k].fatal) {
log_error("mount(%s) failed: %m", where);
if (r == 0)
r = -errno;
}
}
return r;
}
static int mount_binds(const char *dest, char **l, unsigned long flags) {
char **x, **y;
STRV_FOREACH_PAIR(x, y, l) {
char *where;
struct stat source_st, dest_st;
int r;
if (stat(*x, &source_st) < 0) {
log_error("failed to stat %s: %m", *x);
return -errno;
}
where = strappenda(dest, *y);
r = stat(where, &dest_st);
if (r == 0) {
if ((source_st.st_mode & S_IFMT) != (dest_st.st_mode & S_IFMT)) {
log_error("The file types of %s and %s do not match. Refusing bind mount",
*x, where);
return -EINVAL;
}
} else if (errno == ENOENT) {
r = mkdir_parents_label(where, 0755);
if (r < 0) {
log_error("Failed to bind mount %s: %s", *x, strerror(-r));
return r;
}
} else {
log_error("Failed to bind mount %s: %s", *x, strerror(errno));
return -errno;
}
/* Create the mount point, but be conservative -- refuse to create block
* and char devices. */
if (S_ISDIR(source_st.st_mode))
mkdir_label(where, 0755);
else if (S_ISFIFO(source_st.st_mode))
mkfifo(where, 0644);
else if (S_ISSOCK(source_st.st_mode))
mknod(where, 0644 | S_IFSOCK, 0);
else if (S_ISREG(source_st.st_mode))
touch(where);
else {
log_error("Refusing to create mountpoint for file: %s", *x);
return -ENOTSUP;
}
if (mount(*x, where, "bind", MS_BIND, NULL) < 0) {
log_error("mount(%s) failed: %m", where);
return -errno;
}
if (flags && mount(NULL, where, NULL, MS_REMOUNT|MS_BIND|flags, NULL) < 0) {
log_error("mount(%s) failed: %m", where);
return -errno;
}
}
return 0;
}
static int setup_timezone(const char *dest) {
_cleanup_free_ char *where = NULL, *p = NULL, *q = NULL, *check = NULL, *what = NULL;
char *z, *y;
int r;
assert(dest);
/* Fix the timezone, if possible */
r = readlink_malloc("/etc/localtime", &p);
if (r < 0) {
log_warning("/etc/localtime is not a symlink, not updating container timezone.");
return 0;
}
z = path_startswith(p, "../usr/share/zoneinfo/");
if (!z)
z = path_startswith(p, "/usr/share/zoneinfo/");
if (!z) {
log_warning("/etc/localtime does not point into /usr/share/zoneinfo/, not updating container timezone.");
return 0;
}
where = strappend(dest, "/etc/localtime");
if (!where)
return log_oom();
r = readlink_malloc(where, &q);
if (r >= 0) {
y = path_startswith(q, "../usr/share/zoneinfo/");
if (!y)
y = path_startswith(q, "/usr/share/zoneinfo/");
/* Already pointing to the right place? Then do nothing .. */
if (y && streq(y, z))
return 0;
}
check = strjoin(dest, "/usr/share/zoneinfo/", z, NULL);
if (!check)
return log_oom();
if (access(check, F_OK) < 0) {
log_warning("Timezone %s does not exist in container, not updating container timezone.", z);
return 0;
}
what = strappend("../usr/share/zoneinfo/", z);
if (!what)
return log_oom();
unlink(where);
if (symlink(what, where) < 0) {
log_error("Failed to correct timezone of container: %m");
return 0;
}
return 0;
}
static int setup_resolv_conf(const char *dest) {
char _cleanup_free_ *where = NULL;
assert(dest);
if (arg_private_network)
return 0;
/* Fix resolv.conf, if possible */
where = strappend(dest, "/etc/resolv.conf");
if (!where)
return log_oom();
/* We don't really care for the results of this really. If it
* fails, it fails, but meh... */
copy_file("/etc/resolv.conf", where, O_TRUNC|O_NOFOLLOW);
return 0;
}
static int setup_boot_id(const char *dest) {
_cleanup_free_ char *from = NULL, *to = NULL;
sd_id128_t rnd = {};
char as_uuid[37];
int r;
assert(dest);
if (arg_share_system)
return 0;
/* Generate a new randomized boot ID, so that each boot-up of
* the container gets a new one */
from = strappend(dest, "/dev/proc-sys-kernel-random-boot-id");
to = strappend(dest, "/proc/sys/kernel/random/boot_id");
if (!from || !to)
return log_oom();
r = sd_id128_randomize(&rnd);
if (r < 0) {
log_error("Failed to generate random boot id: %s", strerror(-r));
return r;
}
snprintf(as_uuid, sizeof(as_uuid),
"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
SD_ID128_FORMAT_VAL(rnd));
char_array_0(as_uuid);
r = write_string_file(from, as_uuid);
if (r < 0) {
log_error("Failed to write boot id: %s", strerror(-r));
return r;
}
if (mount(from, to, "bind", MS_BIND, NULL) < 0) {
log_error("Failed to bind mount boot id: %m");
r = -errno;
} else if (mount(from, to, "bind", MS_BIND|MS_REMOUNT|MS_RDONLY, NULL))
log_warning("Failed to make boot id read-only: %m");
unlink(from);
return r;
}
static int copy_devnodes(const char *dest) {
static const char devnodes[] =
"null\0"
"zero\0"
"full\0"
"random\0"
"urandom\0"
"tty\0";
const char *d;
int r = 0;
_cleanup_umask_ mode_t u;
assert(dest);
u = umask(0000);
NULSTR_FOREACH(d, devnodes) {
_cleanup_free_ char *from = NULL, *to = NULL;
struct stat st;
from = strappend("/dev/", d);
to = strjoin(dest, "/dev/", d, NULL);
if (!from || !to)
return log_oom();
if (stat(from, &st) < 0) {
if (errno != ENOENT) {
log_error("Failed to stat %s: %m", from);
return -errno;
}
} else if (!S_ISCHR(st.st_mode) && !S_ISBLK(st.st_mode)) {
log_error("%s is not a char or block device, cannot copy", from);
return -EIO;
} else if (mknod(to, st.st_mode, st.st_rdev) < 0) {
log_error("mknod(%s) failed: %m", dest);
return -errno;
}
}
return r;
}
static int setup_ptmx(const char *dest) {
_cleanup_free_ char *p = NULL;
p = strappend(dest, "/dev/ptmx");
if (!p)
return log_oom();
if (symlink("pts/ptmx", p) < 0) {
log_error("Failed to create /dev/ptmx symlink: %m");
return -errno;
}
return 0;
}
static int setup_dev_console(const char *dest, const char *console) {
struct stat st;
_cleanup_free_ char *to = NULL;
int r;
_cleanup_umask_ mode_t u;
assert(dest);
assert(console);
u = umask(0000);
if (stat(console, &st) < 0) {
log_error("Failed to stat %s: %m", console);
return -errno;
} else if (!S_ISCHR(st.st_mode)) {
log_error("/dev/console is not a char device");
return -EIO;
}
r = chmod_and_chown(console, 0600, 0, 0);
if (r < 0) {
log_error("Failed to correct access mode for TTY: %s", strerror(-r));
return r;
}
if (asprintf(&to, "%s/dev/console", dest) < 0)
return log_oom();
/* We need to bind mount the right tty to /dev/console since
* ptys can only exist on pts file systems. To have something
* to bind mount things on we create a device node first, that
* has the right major/minor (note that the major minor
* doesn't actually matter here, since we mount it over
* anyway). */
if (mknod(to, (st.st_mode & ~07777) | 0600, st.st_rdev) < 0) {
log_error("mknod() for /dev/console failed: %m");
return -errno;
}
if (mount(console, to, "bind", MS_BIND, NULL) < 0) {
log_error("Bind mount for /dev/console failed: %m");
return -errno;
}
return 0;
}
static int setup_kmsg(const char *dest, int kmsg_socket) {
_cleanup_free_ char *from = NULL, *to = NULL;
int r, fd, k;
_cleanup_umask_ mode_t u;
union {
struct cmsghdr cmsghdr;
uint8_t buf[CMSG_SPACE(sizeof(int))];
} control = {};
struct msghdr mh = {
.msg_control = &control,
.msg_controllen = sizeof(control),
};
struct cmsghdr *cmsg;
assert(dest);
assert(kmsg_socket >= 0);
u = umask(0000);
/* We create the kmsg FIFO as /dev/kmsg, but immediately
* delete it after bind mounting it to /proc/kmsg. While FIFOs
* on the reading side behave very similar to /proc/kmsg,
* their writing side behaves differently from /dev/kmsg in
* that writing blocks when nothing is reading. In order to
* avoid any problems with containers deadlocking due to this
* we simply make /dev/kmsg unavailable to the container. */
if (asprintf(&from, "%s/dev/kmsg", dest) < 0 ||
asprintf(&to, "%s/proc/kmsg", dest) < 0)
return log_oom();
if (mkfifo(from, 0600) < 0) {
log_error("mkfifo() for /dev/kmsg failed: %m");
return -errno;
}
r = chmod_and_chown(from, 0600, 0, 0);
if (r < 0) {
log_error("Failed to correct access mode for /dev/kmsg: %s", strerror(-r));
return r;
}
if (mount(from, to, "bind", MS_BIND, NULL) < 0) {
log_error("Bind mount for /proc/kmsg failed: %m");
return -errno;
}
fd = open(from, O_RDWR|O_NDELAY|O_CLOEXEC);
if (fd < 0) {
log_error("Failed to open fifo: %m");
return -errno;
}
cmsg = CMSG_FIRSTHDR(&mh);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
memcpy(CMSG_DATA(cmsg), &fd, sizeof(int));
mh.msg_controllen = cmsg->cmsg_len;
/* Store away the fd in the socket, so that it stays open as
* long as we run the child */
k = sendmsg(kmsg_socket, &mh, MSG_DONTWAIT|MSG_NOSIGNAL);
close_nointr_nofail(fd);
if (k < 0) {
log_error("Failed to send FIFO fd: %m");
return -errno;
}
/* And now make the FIFO unavailable as /dev/kmsg... */
unlink(from);
return 0;
}
static int setup_hostname(void) {
if (arg_share_system)
return 0;
if (sethostname(arg_machine, strlen(arg_machine)) < 0)
return -errno;
return 0;
}
static int setup_journal(const char *directory) {
sd_id128_t machine_id, this_id;
_cleanup_free_ char *p = NULL, *b = NULL, *q = NULL, *d = NULL;
char *id;
int r;
p = strappend(directory, "/etc/machine-id");
if (!p)
return log_oom();
r = read_one_line_file(p, &b);
if (r == -ENOENT && arg_link_journal == LINK_AUTO)
return 0;
else if (r < 0) {
log_error("Failed to read machine ID from %s: %s", p, strerror(-r));
return r;
}
id = strstrip(b);
if (isempty(id) && arg_link_journal == LINK_AUTO)
return 0;
/* Verify validity */
r = sd_id128_from_string(id, &machine_id);
if (r < 0) {
log_error("Failed to parse machine ID from %s: %s", p, strerror(-r));
return r;
}
r = sd_id128_get_machine(&this_id);
if (r < 0) {
log_error("Failed to retrieve machine ID: %s", strerror(-r));
return r;
}
if (sd_id128_equal(machine_id, this_id)) {
log_full(arg_link_journal == LINK_AUTO ? LOG_WARNING : LOG_ERR,
"Host and machine ids are equal (%s): refusing to link journals", id);
if (arg_link_journal == LINK_AUTO)
return 0;
return
-EEXIST;
}
if (arg_link_journal == LINK_NO)
return 0;
free(p);
p = strappend("/var/log/journal/", id);
q = strjoin(directory, "/var/log/journal/", id, NULL);
if (!p || !q)
return log_oom();
if (path_is_mount_point(p, false) > 0) {
if (arg_link_journal != LINK_AUTO) {
log_error("%s: already a mount point, refusing to use for journal", p);
return -EEXIST;
}
return 0;
}
if (path_is_mount_point(q, false) > 0) {
if (arg_link_journal != LINK_AUTO) {
log_error("%s: already a mount point, refusing to use for journal", q);
return -EEXIST;
}
return 0;
}
r = readlink_and_make_absolute(p, &d);
if (r >= 0) {
if ((arg_link_journal == LINK_GUEST ||
arg_link_journal == LINK_AUTO) &&
path_equal(d, q)) {
r = mkdir_p(q, 0755);
if (r < 0)
log_warning("failed to create directory %s: %m", q);
return 0;
}
if (unlink(p) < 0) {
log_error("Failed to remove symlink %s: %m", p);
return -errno;
}
} else if (r == -EINVAL) {
if (arg_link_journal == LINK_GUEST &&
rmdir(p) < 0) {
if (errno == ENOTDIR) {
log_error("%s already exists and is neither a symlink nor a directory", p);
return r;
} else {
log_error("Failed to remove %s: %m", p);
return -errno;
}
}
} else if (r != -ENOENT) {
log_error("readlink(%s) failed: %m", p);
return r;
}
if (arg_link_journal == LINK_GUEST) {
if (symlink(q, p) < 0) {
log_error("Failed to symlink %s to %s: %m", q, p);
return -errno;
}
r = mkdir_p(q, 0755);
if (r < 0)
log_warning("failed to create directory %s: %m", q);
return 0;
}
if (arg_link_journal == LINK_HOST) {
r = mkdir_p(p, 0755);
if (r < 0) {
log_error("Failed to create %s: %m", p);
return r;
}
} else if (access(p, F_OK) < 0)
return 0;
if (dir_is_empty(q) == 0) {
log_error("%s not empty.", q);
return -ENOTEMPTY;
}
r = mkdir_p(q, 0755);
if (r < 0) {
log_error("Failed to create %s: %m", q);
return r;
}
if (mount(p, q, "bind", MS_BIND, NULL) < 0) {
log_error("Failed to bind mount journal from host into guest: %m");
return -errno;
}
return 0;
}
static int setup_kdbus(const char *dest, const char *path) {
const char *p;
if (!path)
return 0;
p = strappenda(dest, "/dev/kdbus");
if (mkdir(p, 0755) < 0) {
log_error("Failed to create kdbus path: %m");
return -errno;
}
if (mount(path, p, "bind", MS_BIND, NULL) < 0) {
log_error("Failed to mount kdbus domain path: %m");
return -errno;
}
return 0;
}
static int drop_capabilities(void) {
return capability_bounding_set_drop(~arg_retain, false);
}
static int register_machine(pid_t pid) {
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_bus_unref_ sd_bus *bus = NULL;
int r;
if (!arg_register)
return 0;
r = sd_bus_default_system(&bus);
if (r < 0) {
log_error("Failed to open system bus: %s", strerror(-r));
return r;
}
if (arg_keep_unit) {
r = sd_bus_call_method(
bus,
"org.freedesktop.machine1",
"/org/freedesktop/machine1",
"org.freedesktop.machine1.Manager",
"RegisterMachine",
&error,
NULL,
"sayssus",
arg_machine,
SD_BUS_MESSAGE_APPEND_ID128(arg_uuid),
"nspawn",
"container",
(uint32_t) pid,
strempty(arg_directory));
} else {
_cleanup_bus_message_unref_ sd_bus_message *m = NULL;
r = sd_bus_message_new_method_call(
bus,
&m,
"org.freedesktop.machine1",
"/org/freedesktop/machine1",
"org.freedesktop.machine1.Manager",
"CreateMachine");
if (r < 0) {
log_error("Failed to create message: %s", strerror(-r));
return r;
}
r = sd_bus_message_append(
m,
"sayssus",
arg_machine,
SD_BUS_MESSAGE_APPEND_ID128(arg_uuid),
"nspawn",
"container",
(uint32_t) pid,
strempty(arg_directory));
if (r < 0) {
log_error("Failed to append message arguments: %s", strerror(-r));
return r;
}
r = sd_bus_message_open_container(m, 'a', "(sv)");
if (r < 0) {
log_error("Failed to open container: %s", strerror(-r));
return r;
}
if (!isempty(arg_slice)) {
r = sd_bus_message_append(m, "(sv)", "Slice", "s", arg_slice);
if (r < 0) {
log_error("Failed to append slice: %s", strerror(-r));
return r;
}
}
r = sd_bus_message_append(m, "(sv)", "DevicePolicy", "s", "strict");
if (r < 0) {
log_error("Failed to add device policy: %s", strerror(-r));
return r;
}
r = sd_bus_message_append(m, "(sv)", "DeviceAllow", "a(ss)", 8,
/* Allow the container to
* access and create the API
* device nodes, so that
* PrivateDevices= in the
* container can work
* fine */
"/dev/null", "rwm",
"/dev/zero", "rwm",
"/dev/full", "rwm",
"/dev/random", "rwm",
"/dev/urandom", "rwm",
"/dev/tty", "rwm",
/* Allow the container
* access to ptys. However,
* do not permit the
* container to ever create
* these device nodes. */
"/dev/pts/ptmx", "rw",
"char-pts", "rw");
if (r < 0) {
log_error("Failed to add device whitelist: %s", strerror(-r));
return r;
}
r = sd_bus_message_close_container(m);
if (r < 0) {
log_error("Failed to close container: %s", strerror(-r));
return r;
}
r = sd_bus_call(bus, m, 0, &error, NULL);
}
if (r < 0) {
log_error("Failed to register machine: %s", bus_error_message(&error, r));
return r;
}
return 0;
}
static int terminate_machine(pid_t pid) {
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
_cleanup_bus_unref_ sd_bus *bus = NULL;
const char *path;
int r;
if (!arg_register)
return 0;
r = sd_bus_default_system(&bus);
if (r < 0) {
log_error("Failed to open system bus: %s", strerror(-r));
return r;
}
r = sd_bus_call_method(
bus,
"org.freedesktop.machine1",
"/org/freedesktop/machine1",
"org.freedesktop.machine1.Manager",
"GetMachineByPID",
&error,
&reply,
"u",
(uint32_t) pid);
if (r < 0) {
/* Note that the machine might already have been
* cleaned up automatically, hence don't consider it a
* failure if we cannot get the machine object. */
log_debug("Failed to get machine: %s", bus_error_message(&error, r));
return 0;
}
r = sd_bus_message_read(reply, "o", &path);
if (r < 0)
return bus_log_parse_error(r);
r = sd_bus_call_method(
bus,
"org.freedesktop.machine1",
path,
"org.freedesktop.machine1.Machine",
"Terminate",
&error,
NULL,
NULL);
if (r < 0) {
log_debug("Failed to terminate machine: %s", bus_error_message(&error, r));
return 0;
}
return 0;
}
static int reset_audit_loginuid(void) {
_cleanup_free_ char *p = NULL;
int r;
if (arg_share_system)
return 0;
r = read_one_line_file("/proc/self/loginuid", &p);
if (r == -EEXIST)
return 0;
if (r < 0) {
log_error("Failed to read /proc/self/loginuid: %s", strerror(-r));
return r;
}
/* Already reset? */
if (streq(p, "4294967295"))
return 0;
r = write_string_file("/proc/self/loginuid", "4294967295");
if (r < 0) {
log_error("Failed to reset audit login UID. This probably means that your kernel is too\n"
"old and you have audit enabled. Note that the auditing subsystem is known to\n"
"be incompatible with containers on old kernels. Please make sure to upgrade\n"
"your kernel or to off auditing with 'audit=0' on the kernel command line before\n"
"using systemd-nspawn. Sleeping for 5s... (%s)\n", strerror(-r));
sleep(5);
}
return 0;
}
static int setup_veth(pid_t pid, char iface_name[IFNAMSIZ]) {
_cleanup_rtnl_message_unref_ sd_rtnl_message *m = NULL;
_cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
int r;
if (!arg_private_network)
return 0;
if (!arg_network_veth)
return 0;
/* Use two different interface name prefixes depending whether
* we are in bridge mode or not. */
if (arg_network_bridge)
memcpy(iface_name, "vb-", 3);
else
memcpy(iface_name, "ve-", 3);
strncpy(iface_name+3, arg_machine, IFNAMSIZ - 3);
r = sd_rtnl_open(&rtnl, 0);
if (r < 0) {
log_error("Failed to connect to netlink: %s", strerror(-r));
return r;
}
r = sd_rtnl_message_new_link(rtnl, &m, RTM_NEWLINK, 0);
if (r < 0) {
log_error("Failed to allocate netlink message: %s", strerror(-r));
return r;
}
r = sd_rtnl_message_append_string(m, IFLA_IFNAME, iface_name);
if (r < 0) {
log_error("Failed to add netlink interface name: %s", strerror(-r));
return r;
}
r = sd_rtnl_message_open_container(m, IFLA_LINKINFO);
if (r < 0) {
log_error("Failed to open netlink container: %s", strerror(-r));
return r;
}
r = sd_rtnl_message_append_string(m, IFLA_INFO_KIND, "veth");
if (r < 0) {
log_error("Failed to append netlink kind: %s", strerror(-r));
return r;
}
r = sd_rtnl_message_open_container(m, IFLA_INFO_DATA);
if (r < 0) {
log_error("Failed to open netlink container: %s", strerror(-r));
return r;
}
r = sd_rtnl_message_open_container(m, VETH_INFO_PEER);
if (r < 0) {
log_error("Failed to open netlink container: %s", strerror(-r));
return r;
}
r = sd_rtnl_message_append_string(m, IFLA_IFNAME, "host0");
if (r < 0) {
log_error("Failed to add netlink interface name: %s", strerror(-r));
return r;
}
r = sd_rtnl_message_append_u32(m, IFLA_NET_NS_PID, pid);
if (r < 0) {
log_error("Failed to add netlink namespace field: %s", strerror(-r));
return r;
}
r = sd_rtnl_message_close_container(m);
if (r < 0) {
log_error("Failed to close netlink container: %s", strerror(-r));
return r;
}
r = sd_rtnl_message_close_container(m);
if (r < 0) {
log_error("Failed to close netlink container: %s", strerror(-r));
return r;
}
r = sd_rtnl_message_close_container(m);
if (r < 0) {
log_error("Failed to close netlink container: %s", strerror(-r));
return r;
}
r = sd_rtnl_call(rtnl, m, 0, NULL);
if (r < 0) {
log_error("Failed to add new veth interfaces: %s", strerror(-r));
return r;
}
return 0;
}
static int setup_bridge(const char veth_name[]) {
_cleanup_rtnl_message_unref_ sd_rtnl_message *m = NULL;
_cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
int r, bridge;
if (!arg_private_network)
return 0;
if (!arg_network_veth)
return 0;
if (!arg_network_bridge)
return 0;
bridge = (int) if_nametoindex(arg_network_bridge);
if (bridge <= 0) {
log_error("Failed to resolve interface %s: %m", arg_network_bridge);
return -errno;
}
r = sd_rtnl_open(&rtnl, 0);
if (r < 0) {
log_error("Failed to connect to netlink: %s", strerror(-r));
return r;
}
r = sd_rtnl_message_new_link(rtnl, &m, RTM_SETLINK, 0);
if (r < 0) {
log_error("Failed to allocate netlink message: %s", strerror(-r));
return r;
}
r = sd_rtnl_message_append_string(m, IFLA_IFNAME, veth_name);
if (r < 0) {
log_error("Failed to add netlink interface name field: %s", strerror(-r));
return r;
}
r = sd_rtnl_message_append_u32(m, IFLA_MASTER, bridge);
if (r < 0) {
log_error("Failed to add netlink master field: %s", strerror(-r));
return r;
}
r = sd_rtnl_call(rtnl, m, 0, NULL);
if (r < 0) {
log_error("Failed to add veth interface to bridge: %s", strerror(-r));
return r;
}
return 0;
}
static int parse_interface(struct udev *udev, const char *name) {
_cleanup_udev_device_unref_ struct udev_device *d = NULL;
char ifi_str[2 + DECIMAL_STR_MAX(int)];
int ifi;
ifi = (int) if_nametoindex(name);
if (ifi <= 0) {
log_error("Failed to resolve interface %s: %m", name);
return -errno;
}
sprintf(ifi_str, "n%i", ifi);
d = udev_device_new_from_device_id(udev, ifi_str);
if (!d) {
log_error("Failed to get udev device for interface %s: %m", name);
return -errno;
}
if (udev_device_get_is_initialized(d) <= 0) {
log_error("Network interface %s is not initialized yet.", name);
return -EBUSY;
}
return ifi;
}
static int move_network_interfaces(pid_t pid) {
_cleanup_udev_unref_ struct udev *udev = NULL;
_cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
char **i;
int r;
if (!arg_private_network)
return 0;
if (strv_isempty(arg_network_interfaces))
return 0;
r = sd_rtnl_open(&rtnl, 0);
if (r < 0) {
log_error("Failed to connect to netlink: %s", strerror(-r));
return r;
}
udev = udev_new();
if (!udev) {
log_error("Failed to connect to udev.");
return -ENOMEM;
}
STRV_FOREACH(i, arg_network_interfaces) {
_cleanup_rtnl_message_unref_ sd_rtnl_message *m = NULL;
int ifi;
ifi = parse_interface(udev, *i);
if (ifi < 0)
return ifi;
r = sd_rtnl_message_new_link(rtnl, &m, RTM_NEWLINK, ifi);
if (r < 0) {
log_error("Failed to allocate netlink message: %s", strerror(-r));
return r;
}
r = sd_rtnl_message_append_u32(m, IFLA_NET_NS_PID, pid);
if (r < 0) {
log_error("Failed to append namespace PID to netlink message: %s", strerror(-r));
return r;
}
r = sd_rtnl_call(rtnl, m, 0, NULL);
if (r < 0) {
log_error("Failed to move interface %s to namespace: %s", *i, strerror(-r));
return r;
}
}
return 0;
}
static int setup_macvlan(pid_t pid) {
_cleanup_udev_unref_ struct udev *udev = NULL;
_cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
char **i;
int r;
if (!arg_private_network)
return 0;
if (strv_isempty(arg_network_macvlan))
return 0;
r = sd_rtnl_open(&rtnl, 0);
if (r < 0) {
log_error("Failed to connect to netlink: %s", strerror(-r));
return r;
}
udev = udev_new();
if (!udev) {
log_error("Failed to connect to udev.");
return -ENOMEM;
}
STRV_FOREACH(i, arg_network_macvlan) {
_cleanup_rtnl_message_unref_ sd_rtnl_message *m = NULL;
_cleanup_free_ char *n = NULL;
int ifi;
ifi = parse_interface(udev, *i);
if (ifi < 0)
return ifi;
r = sd_rtnl_message_new_link(rtnl, &m, RTM_NEWLINK, 0);
if (r < 0) {
log_error("Failed to allocate netlink message: %s", strerror(-r));
return r;
}
r = sd_rtnl_message_append_u32(m, IFLA_LINK, ifi);
if (r < 0) {
log_error("Failed to add netlink interface index: %s", strerror(-r));
return r;
}
n = strappend("mv-", *i);
if (!n)
return log_oom();
strshorten(n, IFNAMSIZ-1);
r = sd_rtnl_message_append_string(m, IFLA_IFNAME, n);
if (r < 0) {
log_error("Failed to add netlink interface name: %s", strerror(-r));
return r;
}
r = sd_rtnl_message_append_u32(m, IFLA_NET_NS_PID, pid);
if (r < 0) {
log_error("Failed to add netlink namespace field: %s", strerror(-r));
return r;
}
r = sd_rtnl_message_open_container(m, IFLA_LINKINFO);
if (r < 0) {
log_error("Failed to open netlink container: %s", strerror(-r));
return r;
}
r = sd_rtnl_message_append_string(m, IFLA_INFO_KIND, "macvlan");
if (r < 0) {
log_error("Failed to append netlink kind: %s", strerror(-r));
return r;
}
r = sd_rtnl_message_open_container(m, IFLA_INFO_DATA);
if (r < 0) {
log_error("Failed to open netlink container: %s", strerror(-r));
return r;
}
r = sd_rtnl_message_append_u32(m, IFLA_MACVLAN_MODE, MACVLAN_MODE_BRIDGE);
if (r < 0) {
log_error("Failed to append macvlan mode: %s", strerror(-r));
return r;
}
r = sd_rtnl_message_close_container(m);
if (r < 0) {
log_error("Failed to close netlink container: %s", strerror(-r));
return r;
}
r = sd_rtnl_message_close_container(m);
if (r < 0) {
log_error("Failed to close netlink container: %s", strerror(-r));
return r;
}
r = sd_rtnl_call(rtnl, m, 0, NULL);
if (r < 0) {
log_error("Failed to add new macvlan interfaces: %s", strerror(-r));
return r;
}
}
return 0;
}
static int audit_still_doesnt_work_in_containers(void) {
#ifdef HAVE_SECCOMP
scmp_filter_ctx seccomp;
int r;
/*
Audit is broken in containers, much of the userspace audit
hookup will fail if running inside a container. We don't
care and just turn off creation of audit sockets.
This will make socket(AF_NETLINK, *, NETLINK_AUDIT) fail
with EAFNOSUPPORT which audit userspace uses as indication
that audit is disabled in the kernel.
*/
seccomp = seccomp_init(SCMP_ACT_ALLOW);
if (!seccomp)
return log_oom();
r = seccomp_add_secondary_archs(seccomp);
if (r < 0) {
log_error("Failed to add secondary archs to seccomp filter: %s", strerror(-r));
goto finish;
}
r = seccomp_rule_add(
seccomp,
SCMP_ACT_ERRNO(EAFNOSUPPORT),
SCMP_SYS(socket),
2,
SCMP_A0(SCMP_CMP_EQ, AF_NETLINK),
SCMP_A2(SCMP_CMP_EQ, NETLINK_AUDIT));
if (r < 0) {
log_error("Failed to add audit seccomp rule: %s", strerror(-r));
goto finish;
}
r = seccomp_attr_set(seccomp, SCMP_FLTATR_CTL_NNP, 0);
if (r < 0) {
log_error("Failed to unset NO_NEW_PRIVS: %s", strerror(-r));
goto finish;
}
r = seccomp_load(seccomp);
if (r < 0)
log_error("Failed to install seccomp audit filter: %s", strerror(-r));
finish:
seccomp_release(seccomp);
return r;
#else
return 0;
#endif
}
int main(int argc, char *argv[]) {
_cleanup_close_ int master = -1, kdbus_fd = -1, sync_fd = -1;
_cleanup_close_pipe_ int kmsg_socket_pair[2] = { -1, -1 };
_cleanup_free_ char *kdbus_domain = NULL;
_cleanup_fdset_free_ FDSet *fds = NULL;
const char *console = NULL;
int r = EXIT_FAILURE, k;
int n_fd_passed;
pid_t pid = 0;
sigset_t mask;
char veth_name[IFNAMSIZ];
log_parse_environment();
log_open();
k = parse_argv(argc, argv);
if (k < 0)
goto finish;
else if (k == 0) {
r = EXIT_SUCCESS;
goto finish;
}
if (arg_directory) {
char *p;
p = path_make_absolute_cwd(arg_directory);
free(arg_directory);
arg_directory = p;
} else
arg_directory = get_current_dir_name();
if (!arg_directory) {
log_error("Failed to determine path, please use -D.");
goto finish;
}
path_kill_slashes(arg_directory);
if (!arg_machine) {
arg_machine = strdup(basename(arg_directory));
if (!arg_machine) {
log_oom();
goto finish;
}
hostname_cleanup(arg_machine, false);
if (isempty(arg_machine)) {
log_error("Failed to determine machine name automatically, please use -M.");
goto finish;
}
}
if (geteuid() != 0) {
log_error("Need to be root.");
goto finish;
}
if (sd_booted() <= 0) {
log_error("Not running on a systemd system.");
goto finish;
}
if (path_equal(arg_directory, "/")) {
log_error("Spawning container on root directory not supported.");
goto finish;
}
if (arg_boot) {
if (path_is_os_tree(arg_directory) <= 0) {
log_error("Directory %s doesn't look like an OS root directory (/etc/os-release is missing). Refusing.", arg_directory);
goto finish;
}
} else {
const char *p;
p = strappenda(arg_directory,
argc > optind && path_is_absolute(argv[optind]) ? argv[optind] : "/usr/bin/");
if (access(p, F_OK) < 0) {
log_error("Directory %s lacks the binary to execute or doesn't look like a binary tree. Refusing.", arg_directory);
goto finish;
}
}
log_close();
n_fd_passed = sd_listen_fds(false);
if (n_fd_passed > 0) {
k = fdset_new_listen_fds(&fds, false);
if (k < 0) {
log_error("Failed to collect file descriptors: %s", strerror(-k));
goto finish;
}
}
fdset_close_others(fds);
log_open();
master = posix_openpt(O_RDWR|O_NOCTTY|O_CLOEXEC|O_NDELAY);
if (master < 0) {
log_error("Failed to acquire pseudo tty: %m");
goto finish;
}
console = ptsname(master);
if (!console) {
log_error("Failed to determine tty name: %m");
goto finish;
}
if (!arg_quiet)
log_info("Spawning container %s on %s. Press ^] three times within 1s to abort execution.", arg_machine, arg_directory);
if (unlockpt(master) < 0) {
log_error("Failed to unlock tty: %m");
goto finish;
}
if (access("/dev/kdbus/control", F_OK) >= 0) {
if (arg_share_system) {
kdbus_domain = strdup("/dev/kdbus");
if (!kdbus_domain) {
log_oom();
goto finish;
}
} else {
const char *ns;
ns = strappenda("machine-", arg_machine);
kdbus_fd = bus_kernel_create_domain(ns, &kdbus_domain);
if (r < 0)
log_debug("Failed to create kdbus domain: %s", strerror(-r));
else
log_debug("Successfully created kdbus domain as %s", kdbus_domain);
}
}
if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_NONBLOCK|SOCK_CLOEXEC, 0, kmsg_socket_pair) < 0) {
log_error("Failed to create kmsg socket pair: %m");
goto finish;
}
sd_notify(0, "READY=1");
assert_se(sigemptyset(&mask) == 0);
sigset_add_many(&mask, SIGCHLD, SIGWINCH, SIGTERM, SIGINT, -1);
assert_se(sigprocmask(SIG_BLOCK, &mask, NULL) == 0);
for (;;) {
siginfo_t status;
sync_fd = eventfd(0, EFD_CLOEXEC);
if (sync_fd < 0) {
log_error("Failed to create event fd: %m");
goto finish;
}
pid = syscall(__NR_clone,
SIGCHLD|CLONE_NEWNS|
(arg_share_system ? 0 : CLONE_NEWIPC|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;
}
if (pid == 0) {
/* child */
const char *home = NULL;
uid_t uid = (uid_t) -1;
gid_t gid = (gid_t) -1;
unsigned n_env = 2;
const char *envp[] = {
"PATH=" DEFAULT_PATH_SPLIT_USR,
"container=systemd-nspawn", /* LXC sets container=lxc, so follow the scheme here */
NULL, /* TERM */
NULL, /* HOME */
NULL, /* USER */
NULL, /* LOGNAME */
NULL, /* container_uuid */
NULL, /* LISTEN_FDS */
NULL, /* LISTEN_PID */
NULL
};
char **env_use;
eventfd_t x;
envp[n_env] = strv_find_prefix(environ, "TERM=");
if (envp[n_env])
n_env ++;
close_nointr_nofail(master);
master = -1;
close_nointr(STDIN_FILENO);
close_nointr(STDOUT_FILENO);
close_nointr(STDERR_FILENO);
close_nointr_nofail(kmsg_socket_pair[0]);
kmsg_socket_pair[0] = -1;
reset_all_signal_handlers();
assert_se(sigemptyset(&mask) == 0);
assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);
k = open_terminal(console, O_RDWR);
if (k != STDIN_FILENO) {
if (k >= 0) {
close_nointr_nofail(k);
k = -EINVAL;
}
log_error("Failed to open console: %s", strerror(-k));
goto child_fail;
}
if (dup2(STDIN_FILENO, STDOUT_FILENO) != STDOUT_FILENO ||
dup2(STDIN_FILENO, STDERR_FILENO) != STDERR_FILENO) {
log_error("Failed to duplicate console: %m");
goto child_fail;
}
if (setsid() < 0) {
log_error("setsid() failed: %m");
goto child_fail;
}
if (reset_audit_loginuid() < 0)
goto child_fail;
if (prctl(PR_SET_PDEATHSIG, SIGKILL) < 0) {
log_error("PR_SET_PDEATHSIG failed: %m");
goto child_fail;
}
/* Mark everything as slave, so that we still
* receive mounts from the real root, but don't
* propagate mounts to the real root. */
if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) < 0) {
log_error("MS_SLAVE|MS_REC failed: %m");
goto child_fail;
}
/* Turn directory into bind mount */
if (mount(arg_directory, arg_directory, "bind", MS_BIND|MS_REC, NULL) < 0) {
log_error("Failed to make bind mount.");
goto child_fail;
}
if (arg_read_only)
if (mount(arg_directory, arg_directory, "bind", MS_BIND|MS_REMOUNT|MS_RDONLY|MS_REC, NULL) < 0) {
log_error("Failed to make read-only.");
goto child_fail;
}
if (mount_all(arg_directory) < 0)
goto child_fail;
if (copy_devnodes(arg_directory) < 0)
goto child_fail;
if (setup_ptmx(arg_directory) < 0)
goto child_fail;
dev_setup(arg_directory);
if (audit_still_doesnt_work_in_containers() < 0)
goto child_fail;
if (setup_dev_console(arg_directory, console) < 0)
goto child_fail;
if (setup_kmsg(arg_directory, kmsg_socket_pair[1]) < 0)
goto child_fail;
close_nointr_nofail(kmsg_socket_pair[1]);
kmsg_socket_pair[1] = -1;
if (setup_boot_id(arg_directory) < 0)
goto child_fail;
if (setup_timezone(arg_directory) < 0)
goto child_fail;
if (setup_resolv_conf(arg_directory) < 0)
goto child_fail;
if (setup_journal(arg_directory) < 0)
goto child_fail;
if (mount_binds(arg_directory, arg_bind, 0) < 0)
goto child_fail;
if (mount_binds(arg_directory, arg_bind_ro, MS_RDONLY) < 0)
goto child_fail;
if (setup_kdbus(arg_directory, kdbus_domain) < 0)
goto child_fail;
if (chdir(arg_directory) < 0) {
log_error("chdir(%s) failed: %m", arg_directory);
goto child_fail;
}
if (mount(arg_directory, "/", NULL, MS_MOVE, NULL) < 0) {
log_error("mount(MS_MOVE) failed: %m");
goto child_fail;
}
if (chroot(".") < 0) {
log_error("chroot() failed: %m");
goto child_fail;
}
if (chdir("/") < 0) {
log_error("chdir() failed: %m");
goto child_fail;
}
umask(0022);
if (arg_private_network)
loopback_setup();
if (drop_capabilities() < 0) {
log_error("drop_capabilities() failed: %m");
goto child_fail;
}
if (arg_user) {
/* Note that this resolves user names
* inside the container, and hence
* accesses the NSS modules from the
* container and not the host. This is
* a bit weird... */
if (get_user_creds((const char**)&arg_user, &uid, &gid, &home, NULL) < 0) {
log_error("get_user_creds() failed: %m");
goto child_fail;
}
if (mkdir_parents_label(home, 0775) < 0) {
log_error("mkdir_parents_label() failed: %m");
goto child_fail;
}
if (mkdir_safe_label(home, 0775, uid, gid) < 0) {
log_error("mkdir_safe_label() failed: %m");
goto child_fail;
}
if (initgroups((const char*)arg_user, gid) < 0) {
log_error("initgroups() failed: %m");
goto child_fail;
}
if (setresgid(gid, gid, gid) < 0) {
log_error("setregid() failed: %m");
goto child_fail;
}
if (setresuid(uid, uid, uid) < 0) {
log_error("setreuid() failed: %m");
goto child_fail;
}
} else {
/* Reset everything fully to 0, just in case */
if (setgroups(0, NULL) < 0) {
log_error("setgroups() failed: %m");
goto child_fail;
}
if (setresgid(0, 0, 0) < 0) {
log_error("setregid() failed: %m");
goto child_fail;
}
if (setresuid(0, 0, 0) < 0) {
log_error("setreuid() failed: %m");
goto child_fail;
}
}
if ((asprintf((char**)(envp + n_env++), "HOME=%s", home ? home: "/root") < 0) ||
(asprintf((char**)(envp + n_env++), "USER=%s", arg_user ? arg_user : "root") < 0) ||
(asprintf((char**)(envp + n_env++), "LOGNAME=%s", arg_user ? arg_user : "root") < 0)) {
log_oom();
goto child_fail;
}
if (!sd_id128_equal(arg_uuid, SD_ID128_NULL)) {
if (asprintf((char**)(envp + n_env++), "container_uuid=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(arg_uuid)) < 0) {
log_oom();
goto child_fail;
}
}
if (fdset_size(fds) > 0) {
k = fdset_cloexec(fds, false);
if (k < 0) {
log_error("Failed to unset O_CLOEXEC for file descriptors.");
goto child_fail;
}
if ((asprintf((char **)(envp + n_env++), "LISTEN_FDS=%u", n_fd_passed) < 0) ||
(asprintf((char **)(envp + n_env++), "LISTEN_PID=1") < 0)) {
log_oom();
goto child_fail;
}
}
setup_hostname();
if (arg_personality != 0xffffffffLU) {
if (personality(arg_personality) < 0) {
log_error("personality() failed: %m");
goto child_fail;
}
}
eventfd_read(sync_fd, &x);
close_nointr_nofail(sync_fd);
sync_fd = -1;
if (!strv_isempty(arg_setenv)) {
char **n;
n = strv_env_merge(2, envp, arg_setenv);
if (!n) {
log_oom();
goto child_fail;
}
env_use = n;
} else
env_use = (char**) envp;
#ifdef HAVE_SELINUX
if (arg_selinux_context)
if (setexeccon((security_context_t) arg_selinux_context) < 0)
log_error("setexeccon(\"%s\") failed: %m", arg_selinux_context);
#endif
if (arg_boot) {
char **a;
size_t l;
/* Automatically search for the init system */
l = 1 + argc - optind;
a = newa(char*, l + 1);
memcpy(a + 1, argv + optind, l * sizeof(char*));
a[0] = (char*) "/usr/lib/systemd/systemd";
execve(a[0], a, env_use);
a[0] = (char*) "/lib/systemd/systemd";
execve(a[0], a, env_use);
a[0] = (char*) "/sbin/init";
execve(a[0], a, env_use);
} else if (argc > optind)
execvpe(argv[optind], argv + optind, env_use);
else {
chdir(home ? home : "/root");
execle("/bin/bash", "-bash", NULL, env_use);
execle("/bin/sh", "-sh", NULL, env_use);
}
log_error("execv() failed: %m");
child_fail:
_exit(EXIT_FAILURE);
}
fdset_free(fds);
fds = NULL;
r = register_machine(pid);
if (r < 0)
goto finish;
r = move_network_interfaces(pid);
if (r < 0)
goto finish;
r = setup_veth(pid, veth_name);
if (r < 0)
goto finish;
r = setup_bridge(veth_name);
if (r < 0)
goto finish;
r = setup_macvlan(pid);
if (r < 0)
goto finish;
eventfd_write(sync_fd, 1);
close_nointr_nofail(sync_fd);
sync_fd = -1;
k = process_pty(master, &mask, arg_boot ? pid : 0, SIGRTMIN+3);
if (k < 0) {
r = EXIT_FAILURE;
break;
}
if (!arg_quiet)
putc('\n', stdout);
/* Kill if it is not dead yet anyway */
terminate_machine(pid);
/* Redundant, but better safe than sorry */
kill(pid, SIGKILL);
k = wait_for_terminate(pid, &status);
pid = 0;
if (k < 0) {
r = EXIT_FAILURE;
break;
}
if (status.si_code == CLD_EXITED) {
r = status.si_status;
if (status.si_status != 0) {
log_error("Container %s failed with error code %i.", arg_machine, status.si_status);
break;
}
if (!arg_quiet)
log_debug("Container %s exited successfully.", arg_machine);
break;
} else if (status.si_code == CLD_KILLED &&
status.si_status == SIGINT) {
if (!arg_quiet)
log_info("Container %s has been shut down.", arg_machine);
r = 0;
break;
} else if (status.si_code == CLD_KILLED &&
status.si_status == SIGHUP) {
if (!arg_quiet)
log_info("Container %s is being rebooted.", arg_machine);
continue;
} else if (status.si_code == CLD_KILLED ||
status.si_code == CLD_DUMPED) {
log_error("Container %s terminated by signal %s.", arg_machine, signal_to_string(status.si_status));
r = EXIT_FAILURE;
break;
} else {
log_error("Container %s failed due to unknown reason.", arg_machine);
r = EXIT_FAILURE;
break;
}
}
finish:
if (pid > 0)
kill(pid, SIGKILL);
free(arg_directory);
free(arg_machine);
free(arg_user);
strv_free(arg_setenv);
strv_free(arg_network_interfaces);
strv_free(arg_network_macvlan);
strv_free(arg_bind);
strv_free(arg_bind_ro);
return r;
}