shutdown: Log processes that block umount

This commit is contained in:
Jan Janssen 2022-05-11 11:10:11 +02:00
parent 5125b67742
commit 20596876e3

View file

@ -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);
}