chiark / gitweb /
spawn: downgrade loopback detach errors to debug
[elogind.git] / src / nspawn / nspawn.c
index ce9a9e84f69af363b33739a7bba64140f27f9f76..2783c97f2065d46a163629ca68c8cf3aa3baac3a 100644 (file)
@@ -216,7 +216,7 @@ static void help(void) {
                "                            and container and add it to an existing bridge on\n"
                "                            the host\n"
                "  -p --port=[PROTOCOL:]HOSTPORT[:CONTAINERPORT]\n"
-               "                            Expose a container IP port ont the host\n"
+               "                            Expose a container IP port on the host\n"
                "  -Z --selinux-context=SECLABEL\n"
                "                            Set the SELinux security context to be used by\n"
                "                            processes in the container\n"
@@ -1655,7 +1655,7 @@ static int watch_rtnl(sd_event *event, int recv_fd, union in_addr_union *exposed
         cmsg = CMSG_FIRSTHDR(&mh);
         assert(cmsg->cmsg_level == SOL_SOCKET);
         assert(cmsg->cmsg_type == SCM_RIGHTS);
-        assert(cmsg->cmsg_len = CMSG_LEN(sizeof(int)));
+        assert(cmsg->cmsg_len == CMSG_LEN(sizeof(int)));
         memcpy(&fd, CMSG_DATA(cmsg), sizeof(int));
 
         r = sd_rtnl_open_fd(&rtnl, fd, 1, RTNLGRP_IPV4_IFADDR);
@@ -2557,6 +2557,12 @@ static int setup_image(char **device_path, int *loop_nr) {
         return r;
 }
 
+#define PARTITION_TABLE_BLURB \
+        "Note that the disk image needs to either contain only a single MBR partition of\n" \
+        "type 0x83 that is marked bootable, or follow\n" \
+        "    http://www.freedesktop.org/wiki/Specifications/DiscoverablePartitionsSpec/\n" \
+        "to be bootable with systemd-nspawn."
+
 static int dissect_image(
                 int fd,
                 char **root_device, bool *root_device_rw,
@@ -2584,6 +2590,7 @@ static int dissect_image(
         blkid_partlist pl;
         struct stat st;
         int r;
+        bool is_gpt, is_mbr;
 
         assert(fd >= 0);
         assert(root_device);
@@ -2612,8 +2619,9 @@ static int dissect_image(
         errno = 0;
         r = blkid_do_safeprobe(b);
         if (r == -2 || r == 1) {
-                log_error("Failed to identify any partition table on %s.\n"
-                          "Note that the disk image needs to follow http://www.freedesktop.org/wiki/Specifications/DiscoverablePartitionsSpec/ to be supported by systemd-nspawn.", arg_image);
+                log_error("Failed to identify any partition table on\n"
+                          "    %s\n"
+                          PARTITION_TABLE_BLURB, arg_image);
                 return -EINVAL;
         } else if (r != 0) {
                 if (errno == 0)
@@ -2623,9 +2631,14 @@ static int dissect_image(
         }
 
         blkid_probe_lookup_value(b, "PTTYPE", &pttype, NULL);
-        if (!streq_ptr(pttype, "gpt")) {
-                log_error("Image %s does not carry a GUID Partition Table.\n"
-                          "Note that the disk image needs to follow http://www.freedesktop.org/wiki/Specifications/DiscoverablePartitionsSpec/ to be supported by systemd-nspawn.", arg_image);
+
+        is_gpt = streq_ptr(pttype, "gpt");
+        is_mbr = streq_ptr(pttype, "dos");
+
+        if (!is_gpt && !is_mbr) {
+                log_error("No GPT or MBR partition table discovered on\n"
+                          "    %s\n"
+                          PARTITION_TABLE_BLURB, arg_image);
                 return -EINVAL;
         }
 
@@ -2665,9 +2678,8 @@ static int dissect_image(
         first = udev_enumerate_get_list_entry(e);
         udev_list_entry_foreach(item, first) {
                 _cleanup_udev_device_unref_ struct udev_device *q;
-                const char *stype, *node;
+                const char *node;
                 unsigned long long flags;
-                sd_id128_t type_id;
                 blkid_partition pp;
                 dev_t qn;
                 int nr;
@@ -2698,81 +2710,117 @@ static int dissect_image(
                         continue;
 
                 flags = blkid_partition_get_flags(pp);
-                if (flags & GPT_FLAG_NO_AUTO)
+                if (is_gpt && (flags & GPT_FLAG_NO_AUTO))
+                        continue;
+                if (is_mbr && (flags != 0x80)) /* Bootable flag */
                         continue;
 
                 nr = blkid_partition_get_partno(pp);
                 if (nr < 0)
                         continue;
 
-                stype = blkid_partition_get_type_string(pp);
-                if (!stype)
-                        continue;
+                if (is_gpt) {
+                        sd_id128_t type_id;
+                        const char *stype;
 
-                if (sd_id128_from_string(stype, &type_id) < 0)
-                        continue;
-
-                if (sd_id128_equal(type_id, GPT_HOME)) {
+                        stype = blkid_partition_get_type_string(pp);
+                        if (!stype)
+                                continue;
 
-                        if (home && nr >= home_nr)
+                        if (sd_id128_from_string(stype, &type_id) < 0)
                                 continue;
 
-                        home_nr = nr;
-                        home_rw = !(flags & GPT_FLAG_READ_ONLY);
+                        if (sd_id128_equal(type_id, GPT_HOME)) {
 
-                        free(home);
-                        home = strdup(node);
-                        if (!home)
-                                return log_oom();
-                } else if (sd_id128_equal(type_id, GPT_SRV)) {
+                                if (home && nr >= home_nr)
+                                        continue;
 
-                        if (srv && nr >= srv_nr)
-                                continue;
+                                home_nr = nr;
+                                home_rw = !(flags & GPT_FLAG_READ_ONLY);
 
-                        srv_nr = nr;
-                        srv_rw = !(flags & GPT_FLAG_READ_ONLY);
+                                r = free_and_strdup(&home, node);
+                                if (r < 0)
+                                        return log_oom();
 
-                        free(srv);
-                        srv = strdup(node);
-                        if (!srv)
-                                return log_oom();
-                }
+                        } else if (sd_id128_equal(type_id, GPT_SRV)) {
+
+                                if (srv && nr >= srv_nr)
+                                        continue;
+
+                                srv_nr = nr;
+                                srv_rw = !(flags & GPT_FLAG_READ_ONLY);
+
+                                r = free_and_strdup(&srv, node);
+                                if (r < 0)
+                                        return log_oom();
+                        }
 #ifdef GPT_ROOT_NATIVE
-                else if (sd_id128_equal(type_id, GPT_ROOT_NATIVE)) {
+                        else if (sd_id128_equal(type_id, GPT_ROOT_NATIVE)) {
 
-                        if (root && nr >= root_nr)
-                                continue;
+                                if (root && nr >= root_nr)
+                                        continue;
 
-                        root_nr = nr;
-                        root_rw = !(flags & GPT_FLAG_READ_ONLY);
+                                root_nr = nr;
+                                root_rw = !(flags & GPT_FLAG_READ_ONLY);
 
-                        free(root);
-                        root = strdup(node);
-                        if (!root)
-                                return log_oom();
-                }
+                                r = free_and_strdup(&root, node);
+                                if (r < 0)
+                                        return log_oom();
+                        }
 #endif
 #ifdef GPT_ROOT_SECONDARY
-                else if (sd_id128_equal(type_id, GPT_ROOT_SECONDARY)) {
+                        else if (sd_id128_equal(type_id, GPT_ROOT_SECONDARY)) {
+
+                                if (secondary_root && nr >= secondary_root_nr)
+                                        continue;
+
+                                secondary_root_nr = nr;
+                                secondary_root_rw = !(flags & GPT_FLAG_READ_ONLY);
+
+                                r = free_and_strdup(&secondary_root, node);
+                                if (r < 0)
+                                        return log_oom();
+                        }
+#endif
 
-                        if (secondary_root && nr >= secondary_root_nr)
+                } else if (is_mbr) {
+                        int type;
+
+                        type = blkid_partition_get_type(pp);
+                        if (type != 0x83) /* Linux partition */
                                 continue;
 
-                        secondary_root_nr = nr;
-                        secondary_root_rw = !(flags & GPT_FLAG_READ_ONLY);
+                        /* Note that there's a certain, intended
+                         * asymmetry here: while for GPT we simply
+                         * take the first valid partition and ignore
+                         * all others of the same type, for MBR we
+                         * fail if there are multiple suitable
+                         * partitions. This is because the GPT
+                         * partition types are defined by us, and
+                         * hence we can define their lookup semantics,
+                         * while for the MBR logic we reuse existing
+                         * definitions, and simply don't want to make
+                         * out the situation. */
+
+                        if (root) {
+                                log_error("Identified multiple bootable Linux 0x83 partitions on\n"
+                                          "    %s\n"
+                                          PARTITION_TABLE_BLURB, arg_image);
+                                return -EINVAL;
+                        }
 
+                        root_nr = nr;
 
-                        free(secondary_root);
-                        secondary_root = strdup(node);
-                        if (!secondary_root)
+                        r = free_and_strdup(&root, node);
+                        if (r < 0)
                                 return log_oom();
                 }
-#endif
         }
 
         if (!root && !secondary_root) {
-                log_error("Failed to identify root partition in disk image %s.\n"
-                          "Note that the disk image needs to follow http://www.freedesktop.org/wiki/Specifications/DiscoverablePartitionsSpec/ to be supported by systemd-nspawn.", arg_image);
+                log_error("Failed to identify root partition in disk image\n"
+                          "    %s\n"
+                          PARTITION_TABLE_BLURB, arg_image);
                 return -EINVAL;
         }
 
@@ -2915,7 +2963,7 @@ static void loop_remove(int nr, int *image_fd) {
         if (image_fd && *image_fd >= 0) {
                 r = ioctl(*image_fd, LOOP_CLR_FD);
                 if (r < 0)
-                        log_warning_errno(errno, "Failed to close loop image: %m");
+                        log_debug_errno(errno, "Failed to close loop image: %m");
                 *image_fd = safe_close(*image_fd);
         }
 
@@ -2927,7 +2975,7 @@ static void loop_remove(int nr, int *image_fd) {
 
         r = ioctl(control, LOOP_CTL_REMOVE, nr);
         if (r < 0)
-                log_warning_errno(errno, "Failed to remove loop %d: %m", nr);
+                log_debug_errno(errno, "Failed to remove loop %d: %m", nr);
 }
 
 static int spawn_getent(const char *database, const char *key, pid_t *rpid) {
@@ -3337,6 +3385,7 @@ int main(int argc, char *argv[]) {
         pid_t pid = 0;
         int ret = EXIT_SUCCESS;
         union in_addr_union exposed = {};
+        _cleanup_release_lock_file_ LockFile tree_global_lock = LOCK_FILE_INIT, tree_local_lock = LOCK_FILE_INIT;
 
         log_parse_environment();
         log_open();
@@ -3382,20 +3431,8 @@ int main(int argc, char *argv[]) {
                         goto finish;
                 }
 
-                if (arg_template) {
-                        r = btrfs_subvol_snapshot(arg_template, arg_directory, arg_read_only, true);
-                        if (r == -EEXIST) {
-                                if (!arg_quiet)
-                                        log_info("Directory %s already exists, not populating from template %s.", arg_directory, arg_template);
-                        } else if (r < 0) {
-                                log_error_errno(r, "Couldn't create snapshort %s from %s: %m", arg_directory, arg_template);
-                                goto finish;
-                        } else {
-                                if (!arg_quiet)
-                                        log_info("Populated %s from template %s.", arg_directory, arg_template);
-                        }
-
-                } else if (arg_ephemeral) {
+                if (arg_ephemeral) {
+                        _cleanup_release_lock_file_ LockFile original_lock = LOCK_FILE_INIT;
                         char *np;
 
                         /* If the specified path is a mount point we
@@ -3418,6 +3455,12 @@ int main(int argc, char *argv[]) {
                                 goto finish;
                         }
 
+                        r = image_path_lock(np, (arg_read_only ? LOCK_SH : LOCK_EX) | LOCK_NB, &tree_global_lock, &tree_local_lock);
+                        if (r < 0) {
+                                log_error_errno(r, "Failed to lock %s: %m", np);
+                                goto finish;
+                        }
+
                         r = btrfs_subvol_snapshot(arg_directory, np, arg_read_only, true);
                         if (r < 0) {
                                 free(np);
@@ -3429,6 +3472,31 @@ int main(int argc, char *argv[]) {
                         arg_directory = np;
 
                         remove_subvol = true;
+
+                } else {
+                        r = image_path_lock(arg_directory, (arg_read_only ? LOCK_SH : LOCK_EX) | LOCK_NB, &tree_global_lock, &tree_local_lock);
+                        if (r == -EBUSY) {
+                                log_error_errno(r, "Directory tree %s is currently busy.", arg_directory);
+                                goto finish;
+                        }
+                        if (r < 0) {
+                                log_error_errno(r, "Failed to lock %s: %m", arg_directory);
+                                return r;
+                        }
+
+                        if (arg_template) {
+                                r = btrfs_subvol_snapshot(arg_template, arg_directory, arg_read_only, true);
+                                if (r == -EEXIST) {
+                                        if (!arg_quiet)
+                                                log_info("Directory %s already exists, not populating from template %s.", arg_directory, arg_template);
+                                } else if (r < 0) {
+                                        log_error_errno(r, "Couldn't create snapshort %s from %s: %m", arg_directory, arg_template);
+                                        goto finish;
+                                } else {
+                                        if (!arg_quiet)
+                                                log_info("Populated %s from template %s.", arg_directory, arg_template);
+                                }
+                        }
                 }
 
                 if (arg_boot) {
@@ -3455,6 +3523,16 @@ int main(int argc, char *argv[]) {
                 assert(arg_image);
                 assert(!arg_template);
 
+                r = image_path_lock(arg_image, (arg_read_only ? LOCK_SH : LOCK_EX) | LOCK_NB, &tree_global_lock, &tree_local_lock);
+                if (r == -EBUSY) {
+                        r = log_error_errno(r, "Disk image %s is currently busy.", arg_image);
+                        goto finish;
+                }
+                if (r < 0) {
+                        r = log_error_errno(r, "Failed to create image lock: %m");
+                        goto finish;
+                }
+
                 if (!mkdtemp(template)) {
                         log_error_errno(errno, "Failed to create temporary directory: %m");
                         r = -errno;
@@ -3917,9 +3995,10 @@ int main(int argc, char *argv[]) {
                                 _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
                                 char last_char = 0;
 
-                                sd_notify(false,
-                                          "READY=1\n"
-                                          "STATUS=Container running.");
+                                sd_notifyf(false,
+                                           "READY=1\n"
+                                           "STATUS=Container running.\n"
+                                           "X_NSPAWN_LEADER_PID=" PID_FMT, pid);
 
                                 r = sd_event_new(&event);
                                 if (r < 0) {
@@ -4034,7 +4113,7 @@ finish:
         if (arg_machine) {
                 const char *p;
 
-                p = strappenda("/run/systemd/nspawn/propagate", arg_machine);
+                p = strappenda("/run/systemd/nspawn/propagate/", arg_machine);
                 (void) rm_rf(p, false, true, false);
         }