}
int main(int argc, char *argv[]) {
- bool need_umount, need_swapoff, need_loop_detach, need_dm_detach, in_container, use_watchdog = false, can_initrd;
+ bool need_umount, need_swapoff, need_loop_detach, need_dm_detach, need_md_detach, in_container, use_watchdog = false, can_initrd;
_cleanup_free_ char *cgroup = NULL;
char *arguments[3], *watchdog_device;
int cmd, r, umount_log_level = LOG_INFO;
need_swapoff = !in_container;
need_loop_detach = !in_container;
need_dm_detach = !in_container;
+ need_md_detach = !in_container;
can_initrd = !in_container && !in_initrd() && access("/run/initramfs/shutdown", X_OK) == 0;
/* Unmount all mountpoints, swaps, and loopback devices */
log_error_errno(r, "Failed to detach loop devices: %m");
}
+ if (need_md_detach) {
+ log_info("Stopping MD devices.");
+ r = md_detach_all(&changed, umount_log_level);
+ if (r == 0) {
+ need_md_detach = false;
+ log_info("All MD devices stopped.");
+ } else if (r > 0)
+ log_info("Not all MD devices stopped, %d left.", r);
+ else
+ log_error_errno(r, "Failed to stop MD devices: %m");
+ }
+
if (need_dm_detach) {
log_info("Detaching DM devices.");
r = dm_detach_all(&changed, umount_log_level);
log_error_errno(r, "Failed to detach DM devices: %m");
}
- if (!need_umount && !need_swapoff && !need_loop_detach && !need_dm_detach) {
- log_info("All filesystems, swaps, loop devices and DM devices detached.");
+ if (!need_umount && !need_swapoff && !need_loop_detach && !need_dm_detach
+ && !need_md_detach) {
+ log_info("All filesystems, swaps, loop devices, MD devices and DM devices detached.");
/* Yay, done */
break;
}
/* If in this iteration we didn't manage to
* unmount/deactivate anything, we simply give up */
if (!changed) {
- log_info("Cannot finalize remaining%s%s%s%s continuing.",
+ log_info("Cannot finalize remaining%s%s%s%s%s continuing.",
need_umount ? " file systems," : "",
need_swapoff ? " swap devices," : "",
need_loop_detach ? " loop devices," : "",
- need_dm_detach ? " DM devices," : "");
+ need_dm_detach ? " DM devices," : "",
+ need_md_detach ? " MD devices," : "");
break;
}
- log_debug("Couldn't finalize remaining %s%s%s%s trying again.",
+ log_debug("Couldn't finalize remaining %s%s%s%s%s trying again.",
need_umount ? " file systems," : "",
need_swapoff ? " swap devices," : "",
need_loop_detach ? " loop devices," : "",
- need_dm_detach ? " DM devices," : "");
+ need_dm_detach ? " DM devices," : "",
+ need_md_detach ? " MD devices," : "");
}
/* We're done with the watchdog. */
log_error_errno(r, "Failed to switch root to \"/run/initramfs\": %m");
}
- if (need_umount || need_swapoff || need_loop_detach || need_dm_detach)
- log_error("Failed to finalize%s%s%s%s ignoring.",
+ if (need_umount || need_swapoff || need_loop_detach || need_dm_detach || need_md_detach)
+ log_error("Failed to finalize%s%s%s%s%s ignoring.",
need_umount ? " file systems," : "",
need_swapoff ? " swap devices," : "",
need_loop_detach ? " loop devices," : "",
- need_dm_detach ? " DM devices," : "");
+ need_dm_detach ? " DM devices," : "",
+ need_md_detach ? " MD devices," : "");
/* The kernel will automatically flush ATA disks and suchlike on reboot(), but the file systems need to be
* sync'ed explicitly in advance. So let's do this here, but not needlessly slow down containers. Note that we
#include <errno.h>
#include <fcntl.h>
#include <linux/dm-ioctl.h>
+#include <linux/major.h>
+#include <linux/raid/md_u.h>
#include <linux/loop.h>
#include <sys/mount.h>
#include <sys/swap.h>
return 0;
}
+static int md_list_get(MountPoint **head) {
+ _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
+ sd_device *d;
+ int r;
+
+ assert(head);
+
+ r = sd_device_enumerator_new(&e);
+ if (r < 0)
+ return r;
+
+ r = sd_device_enumerator_allow_uninitialized(e);
+ if (r < 0)
+ return r;
+
+ r = sd_device_enumerator_add_match_subsystem(e, "block", true);
+ if (r < 0)
+ return r;
+
+ r = sd_device_enumerator_add_match_sysname(e, "md*");
+ if (r < 0)
+ return r;
+
+ FOREACH_DEVICE(e, d) {
+ _cleanup_free_ char *p = NULL;
+ const char *dn;
+ MountPoint *m;
+ dev_t devnum;
+
+ if (sd_device_get_devnum(d, &devnum) < 0 ||
+ sd_device_get_devname(d, &dn) < 0)
+ continue;
+
+ p = strdup(dn);
+ if (!p)
+ return -ENOMEM;
+
+ m = new(MountPoint, 1);
+ if (!m)
+ return -ENOMEM;
+
+ *m = (MountPoint) {
+ .path = TAKE_PTR(p),
+ .devnum = devnum,
+ };
+
+ LIST_PREPEND(mount_point, *head, m);
+ }
+
+ return 0;
+}
+
static int delete_loopback(const char *device) {
_cleanup_close_ int fd = -1;
struct loop_info64 info;
return 0;
}
+static int delete_md(MountPoint *m) {
+
+ _cleanup_close_ int fd = -1;
+
+ assert(major(m->devnum) != 0);
+ assert(m->path != 0);
+
+ fd = open(m->path, O_RDONLY|O_CLOEXEC|O_EXCL);
+ if (fd < 0)
+ return -errno;
+
+ if (ioctl(fd, STOP_ARRAY, NULL) < 0)
+ return -errno;
+
+ return 0;
+}
+
static bool nonunmountable_path(const char *path) {
return path_equal(path, "/")
#if ! HAVE_SPLIT_USR
return n_failed;
}
+static int md_points_list_detach(MountPoint **head, bool *changed, int umount_log_level) {
+ MountPoint *m, *n;
+ int n_failed = 0, r;
+ dev_t rootdev = 0;
+
+ assert(head);
+ assert(changed);
+
+ (void) get_block_device("/", &rootdev);
+
+ LIST_FOREACH_SAFE(mount_point, m, n, *head) {
+ if (major(rootdev) != 0 && rootdev == m->devnum) {
+ n_failed ++;
+ continue;
+ }
+
+ log_info("Stopping MD %s (%u:%u).", m->path, major(m->devnum), minor(m->devnum));
+ r = delete_md(m);
+ if (r < 0) {
+ log_full_errno(umount_log_level, r, "Could not stop MD %s: %m", m->path);
+ n_failed++;
+ continue;
+ }
+
+ *changed = true;
+ mount_point_free(head, m);
+ }
+
+ return n_failed;
+}
+
static int umount_all_once(bool *changed, int umount_log_level) {
_cleanup_(mount_points_list_free) LIST_HEAD(MountPoint, mp_list_head);
int r;
return dm_points_list_detach(&dm_list_head, changed, umount_log_level);
}
+
+int md_detach_all(bool *changed, int umount_log_level) {
+ _cleanup_(mount_points_list_free) LIST_HEAD(MountPoint, md_list_head);
+ int r;
+
+ assert(changed);
+
+ LIST_HEAD_INIT(md_list_head);
+
+ r = md_list_get(&md_list_head);
+ if (r < 0)
+ return r;
+
+ return md_points_list_detach(&md_list_head, changed, umount_log_level);
+}