]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
shutdown: Log processes that block umount
authorJan Janssen <medhefgo@web.de>
Wed, 11 May 2022 09:10:11 +0000 (11:10 +0200)
committerJan Janssen <medhefgo@web.de>
Thu, 9 Jun 2022 15:00:46 +0000 (17:00 +0200)
src/shutdown/umount.c

index fc09fde668bf9d57b3621749369483b9de91fc1c..243a1f80efa4f38de71b94837173edd1d0cf0111 100644 (file)
 #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);
         }