chiark / gitweb /
nspawn: add --register=yes|no switch to optionally disable registration of the contai...
[elogind.git] / src / nspawn / nspawn.c
index f904ebea6439c80edaa91411e5cc3f0c3a1b7978..0a81f972989305cd6610b9103c3ee2eeb7c37c28 100644 (file)
@@ -80,8 +80,8 @@ static char *arg_directory = NULL;
 static char *arg_user = NULL;
 static sd_id128_t arg_uuid = {};
 static char *arg_machine = NULL;
-static char *arg_process_label = NULL;
-static char *arg_file_label = NULL;
+static char *arg_selinux_context = NULL;
+static char *arg_selinux_apifs_context = NULL;
 static const char *arg_slice = NULL;
 static bool arg_private_network = false;
 static bool arg_read_only = false;
@@ -118,6 +118,8 @@ static char **arg_bind = NULL;
 static char **arg_bind_ro = NULL;
 static char **arg_setenv = NULL;
 static bool arg_quiet = false;
+static bool arg_share_system = false;
+static bool arg_register = true;
 
 static int help(void) {
 
@@ -131,11 +133,14 @@ static int help(void) {
                "     --uuid=UUID            Set a specific machine UUID for the container\n"
                "  -M --machine=NAME         Set the machine name for the container\n"
                "  -S --slice=SLICE          Place the container in the specified slice\n"
-               "  -L --file-label=LABEL     Set the MAC file label to be used by tmpfs file\n"
-               "                            systems in the container\n"
-               "  -Z --process-label=LABEL  Set the MAC label to be used by processes in\n"
-               "                            the container\n"
+               "  -Z --selinux-context=SECLABEL\n"
+               "                            Set the SELinux security context to be used by\n"
+               "                            processes in the container\n"
+               "  -L --selinux-apifs-context=SECLABEL\n"
+               "                            Set the SELinux security context to be used by\n"
+               "                            API/tmpfs file systems in the container\n"
                "     --private-network      Disable network in container\n"
+               "     --share-system         Share system namespaces with host\n"
                "     --read-only            Mount the root directory read-only\n"
                "     --capability=CAP       In addition to the default, retain specified\n"
                "                            capability\n"
@@ -146,6 +151,7 @@ static int help(void) {
                "                            the container\n"
                "     --bind-ro=PATH[:PATH]  Similar, but creates a read-only bind mount\n"
                "     --setenv=NAME=VALUE    Pass an environment variable to PID 1\n"
+               "     --register=BOOLEAN     Register container as machine\n"
                "  -q --quiet                Do not show status information\n",
                program_invocation_short_name);
 
@@ -165,28 +171,32 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_BIND,
                 ARG_BIND_RO,
                 ARG_SETENV,
+                ARG_SHARE_SYSTEM,
+                ARG_REGISTER
         };
 
         static const struct option options[] = {
-                { "help",            no_argument,       NULL, 'h'                 },
-                { "version",         no_argument,       NULL, ARG_VERSION         },
-                { "directory",       required_argument, NULL, 'D'                 },
-                { "user",            required_argument, NULL, 'u'                 },
-                { "private-network", no_argument,       NULL, ARG_PRIVATE_NETWORK },
-                { "boot",            no_argument,       NULL, 'b'                 },
-                { "uuid",            required_argument, NULL, ARG_UUID            },
-                { "read-only",       no_argument,       NULL, ARG_READ_ONLY       },
-                { "capability",      required_argument, NULL, ARG_CAPABILITY      },
-                { "drop-capability", required_argument, NULL, ARG_DROP_CAPABILITY },
-                { "link-journal",    required_argument, NULL, ARG_LINK_JOURNAL    },
-                { "bind",            required_argument, NULL, ARG_BIND            },
-                { "bind-ro",         required_argument, NULL, ARG_BIND_RO         },
-                { "machine",         required_argument, NULL, 'M'                 },
-                { "slice",           required_argument, NULL, 'S'                 },
-                { "setenv",          required_argument, NULL, ARG_SETENV          },
-                { "process-label",   required_argument, NULL, 'Z'                 },
-                { "file-label",      required_argument, NULL, 'L'                 },
-                { "quiet",           no_argument,       NULL, 'q'                 },
+                { "help",                  no_argument,       NULL, 'h'                 },
+                { "version",               no_argument,       NULL, ARG_VERSION         },
+                { "directory",             required_argument, NULL, 'D'                 },
+                { "user",                  required_argument, NULL, 'u'                 },
+                { "private-network",       no_argument,       NULL, ARG_PRIVATE_NETWORK },
+                { "boot",                  no_argument,       NULL, 'b'                 },
+                { "uuid",                  required_argument, NULL, ARG_UUID            },
+                { "read-only",             no_argument,       NULL, ARG_READ_ONLY       },
+                { "capability",            required_argument, NULL, ARG_CAPABILITY      },
+                { "drop-capability",       required_argument, NULL, ARG_DROP_CAPABILITY },
+                { "link-journal",          required_argument, NULL, ARG_LINK_JOURNAL    },
+                { "bind",                  required_argument, NULL, ARG_BIND            },
+                { "bind-ro",               required_argument, NULL, ARG_BIND_RO         },
+                { "machine",               required_argument, NULL, 'M'                 },
+                { "slice",                 required_argument, NULL, 'S'                 },
+                { "setenv",                required_argument, NULL, ARG_SETENV          },
+                { "selinux-context",       required_argument, NULL, 'Z'                 },
+                { "selinux-apifs-context", required_argument, NULL, 'L'                 },
+                { "quiet",                 no_argument,       NULL, 'q'                 },
+                { "share-system",          no_argument,       NULL, ARG_SHARE_SYSTEM    },
+                { "register",              required_argument, NULL, ARG_REGISTER        },
                 {}
         };
 
@@ -249,24 +259,30 @@ static int parse_argv(int argc, char *argv[]) {
                         break;
 
                 case 'M':
-                        if (!hostname_is_valid(optarg)) {
-                                log_error("Invalid machine name: %s", optarg);
-                                return -EINVAL;
-                        }
+                        if (isempty(optarg)) {
+                                free(arg_machine);
+                                arg_machine = NULL;
+                        } else {
 
-                        free(arg_machine);
-                        arg_machine = strdup(optarg);
-                        if (!arg_machine)
-                                return log_oom();
+                                if (!hostname_is_valid(optarg)) {
+                                        log_error("Invalid machine name: %s", optarg);
+                                        return -EINVAL;
+                                }
 
-                        break;
+                                free(arg_machine);
+                                arg_machine = strdup(optarg);
+                                if (!arg_machine)
+                                        return log_oom();
 
-                case 'L':
-                        arg_file_label = optarg;
-                        break;
+                                break;
+                        }
 
                 case 'Z':
-                        arg_process_label = optarg;
+                        arg_selinux_context = optarg;
+                        break;
+
+                case 'L':
+                        arg_selinux_apifs_context = optarg;
                         break;
 
                 case ARG_READ_ONLY:
@@ -380,6 +396,20 @@ static int parse_argv(int argc, char *argv[]) {
                         arg_quiet = true;
                         break;
 
+                case ARG_SHARE_SYSTEM:
+                        arg_share_system = true;
+                        break;
+
+                case ARG_REGISTER:
+                        r = parse_boolean(optarg);
+                        if (r < 0) {
+                                log_error("Failed to parse --register= argument: %s", optarg);
+                                return r;
+                        }
+
+                        arg_register = r;
+                        break;
+
                 case '?':
                         return -EINVAL;
 
@@ -388,6 +418,14 @@ static int parse_argv(int argc, char *argv[]) {
                 }
         }
 
+        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;
+        }
+
         return 1;
 }
 
@@ -449,8 +487,9 @@ static int mount_all(const char *dest) {
                 mkdir_p(where, 0755);
 
 #ifdef HAVE_SELINUX
-                if (arg_file_label && (streq_ptr(mount_table[k].what, "tmpfs") || streq_ptr(mount_table[k].what, "devpts"))) {
-                        options = strjoin(mount_table[k].options, ",context=\"", arg_file_label, "\"", NULL);
+                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();
 
@@ -625,6 +664,9 @@ static int setup_boot_id(const char *dest) {
 
         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 */
 
@@ -850,6 +892,9 @@ static int setup_kmsg(const char *dest, int kmsg_socket) {
 
 static int setup_hostname(void) {
 
+        if (arg_share_system)
+                return 0;
+
         if (sethostname(arg_machine, strlen(arg_machine)) < 0)
                 return -errno;
 
@@ -1032,6 +1077,9 @@ static int register_machine(pid_t pid) {
         _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));
@@ -1069,6 +1117,9 @@ static int terminate_machine(pid_t pid) {
         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));
@@ -1135,7 +1186,6 @@ int main(int argc, char *argv[]) {
         _cleanup_close_pipe_ int kmsg_socket_pair[2] = { -1, -1 };
         _cleanup_fdset_free_ FDSet *fds = NULL;
         _cleanup_free_ char *kdbus_domain = NULL;
-        const char *ns;
 
         log_parse_environment();
         log_open();
@@ -1200,7 +1250,7 @@ int main(int argc, char *argv[]) {
                 goto finish;
         }
 
-        if (path_is_os_tree(arg_directory) <= 0) {
+        if (arg_boot && 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;
         }
@@ -1237,12 +1287,26 @@ int main(int argc, char *argv[]) {
                 goto finish;
         }
 
-        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 (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");
@@ -1264,7 +1328,10 @@ int main(int argc, char *argv[]) {
                         goto finish;
                 }
 
-                pid = syscall(__NR_clone, SIGCHLD|CLONE_NEWIPC|CLONE_NEWNS|CLONE_NEWPID|CLONE_NEWUTS|(arg_private_network ? CLONE_NEWNET : 0), NULL);
+                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");
@@ -1424,7 +1491,8 @@ int main(int argc, char *argv[]) {
 
                         umask(0022);
 
-                        loopback_setup();
+                        if (arg_private_network)
+                                loopback_setup();
 
                         if (drop_capabilities() < 0) {
                                 log_error("drop_capabilities() failed: %m");
@@ -1535,9 +1603,9 @@ int main(int argc, char *argv[]) {
                                 env_use = (char**) envp;
 
 #ifdef HAVE_SELINUX
-                        if (arg_process_label)
-                                if (setexeccon(arg_process_label) < 0)
-                                        log_error("setexeccon(\"%s\") failed: %m", arg_process_label);
+                        if (arg_selinux_context)
+                                if (setexeccon(arg_selinux_context) < 0)
+                                        log_error("setexeccon(\"%s\") failed: %m", arg_selinux_context);
 #endif
                         if (arg_boot) {
                                 char **a;
@@ -1630,7 +1698,7 @@ int main(int argc, char *argv[]) {
                 } 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));
+                        log_error("Container %s terminated by signal %s.", arg_machine, signal_to_string(status.si_status));
                         r = EXIT_FAILURE;
                         break;
                 } else {