From 20596876e3031e05304061ec1fb080646fcef5ba Mon Sep 17 00:00:00 2001 From: Jan Janssen Date: Wed, 11 May 2022 11:10:11 +0200 Subject: shutdown: Log processes that block umount --- src/shutdown/umount.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 63 insertions(+), 3 deletions(-) (limited to 'src/shutdown') diff --git a/src/shutdown/umount.c b/src/shutdown/umount.c index fc09fde668..243a1f80ef 100644 --- a/src/shutdown/umount.c +++ b/src/shutdown/umount.c @@ -25,13 +25,17 @@ #include "blockdev-util.h" #include "def.h" #include "device-util.h" +#include "dirent-util.h" #include "escape.h" #include "fd-util.h" +#include "fileio.h" +#include "fs-util.h" #include "fstab-util.h" #include "libmount-util.h" #include "mount-setup.h" #include "mount-util.h" #include "mountpoint-util.h" +#include "parse-util.h" #include "path-util.h" #include "process-util.h" #include "signal-util.h" @@ -524,6 +528,58 @@ static bool nonunmountable_path(const char *path) { || path_startswith(path, "/run/initramfs"); } +static void log_umount_blockers(const char *mnt) { + _cleanup_closedir_ DIR *dir = opendir("/proc"); + if (!dir) + return (void) log_warning_errno(errno, "opendir(/proc) failed: %m"); + + _cleanup_free_ char *blockers = NULL; + + FOREACH_DIRENT_ALL(de, dir, break) { + if (!IN_SET(de->d_type, DT_DIR, DT_UNKNOWN)) + continue; + + pid_t pid; + if (parse_pid(de->d_name, &pid) < 0) + continue; + + _cleanup_closedir_ DIR *pid_dir = xopendirat(dirfd(dir), de->d_name, 0); + if (!pid_dir) + continue; + + _cleanup_closedir_ DIR *fd_dir = xopendirat(dirfd(pid_dir), "fd", 0); + if (!fd_dir) + continue; + + FOREACH_DIRENT(fd_de, fd_dir, break) { + _cleanup_free_ char *open_file = NULL, *comm = NULL; + + if (readlinkat_malloc(dirfd(fd_dir), fd_de->d_name, &open_file) < 0) + continue; + + if (!path_startswith(open_file, mnt)) + continue; + + if (PATH_STARTSWITH_SET(open_file, "/dev", "/sys", "/proc")) + continue; + + if (get_process_comm(pid, &comm) < 0) + continue; + + if (!strextend_with_separator(&blockers, ", ", comm)) + return (void) log_oom(); + + if (!strextend(&blockers, "(", de->d_name, ")")) + return (void) log_oom(); + + break; + } + } + + if (blockers) + log_warning("Unmounting '%s' blocked by: %s", mnt, blockers); +} + static int remount_with_timeout(MountPoint *m, bool last_try) { pid_t pid; int r; @@ -586,9 +642,13 @@ static int umount_with_timeout(MountPoint *m, bool last_try) { * "busy", this may allow processes to die, thus making the * filesystem less busy so the unmount might succeed (rather * than return EBUSY). */ - r = umount2(m->path, MNT_FORCE); - if (r < 0) - log_full_errno(last_try ? LOG_ERR : LOG_INFO, errno, "Failed to unmount %s: %m", m->path); + r = RET_NERRNO(umount2(m->path, MNT_FORCE)); + if (r < 0) { + log_full_errno(last_try ? LOG_ERR : LOG_INFO, r, "Failed to unmount %s: %m", m->path); + + if (r == -EBUSY && last_try) + log_umount_blockers(m->path); + } _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS); } -- cgit v1.2.1