From 5966f7a3b90ee25f23182e9320621a8477a40a51 Mon Sep 17 00:00:00 2001
From: jiangchuangang <jiangchuangang@huawei.com>
Date: Thu, 2 Sep 2021 12:14:19 +0800
Subject: [PATCH] print process status to console when shutdown

---
 src/basic/getopt-defs.h        |   6 +-
 src/basic/process-util.c       |  58 ++++
 src/basic/process-util.h       |   2 +
 src/core/fuser.c               | 506 +++++++++++++++++++++++++++++++++
 src/core/fuser.h               |  55 ++++
 src/core/job.c                 |  36 +++
 src/core/main.c                |  10 +-
 src/core/manager.c             |   4 +
 src/core/manager.h             |   2 +
 src/core/meson.build           |   1 +
 src/core/system.conf.in        |   1 +
 src/shutdown/meson.build       |  13 +
 src/shutdown/process-status.c  | 143 ++++++++++
 src/shutdown/process-status.h  |  24 ++
 src/shutdown/shutdown.c        |  43 +++
 src/shutdown/umount.c          |   5 +
 src/test/meson.build           |  25 ++
 src/test/test-fuser.c          |  14 +
 src/test/test-process-status.c |  10 +
 19 files changed, 953 insertions(+), 5 deletions(-)
 create mode 100644 src/core/fuser.c
 create mode 100644 src/core/fuser.h
 create mode 100644 src/shutdown/process-status.c
 create mode 100644 src/shutdown/process-status.h
 create mode 100644 src/test/test-fuser.c
 create mode 100644 src/test/test-process-status.c

diff --git a/src/basic/getopt-defs.h b/src/basic/getopt-defs.h
index 3efeb6d..dfd17b5 100644
--- a/src/basic/getopt-defs.h
+++ b/src/basic/getopt-defs.h
@@ -37,7 +37,8 @@
 
 #define SHUTDOWN_GETOPT_ARGS                    \
         ARG_EXIT_CODE,                          \
-        ARG_TIMEOUT
+        ARG_TIMEOUT,                            \
+        ARG_DFX_REBOOT
 
 #define COMMON_GETOPT_OPTIONS                                           \
         { "log-level",                required_argument, NULL, ARG_LOG_LEVEL                }, \
@@ -72,4 +73,5 @@
 
 #define SHUTDOWN_GETOPT_OPTIONS                                         \
         { "exit-code",                required_argument, NULL, ARG_EXIT_CODE    }, \
-        { "timeout",                  required_argument, NULL, ARG_TIMEOUT      }
+        { "timeout",                  required_argument, NULL, ARG_TIMEOUT      }, \
+        { "dfx-reboot",               required_argument, NULL, ARG_DFX_REBOOT   }
diff --git a/src/basic/process-util.c b/src/basic/process-util.c
index 201c559..4e93c9b 100644
--- a/src/basic/process-util.c
+++ b/src/basic/process-util.c
@@ -2060,3 +2060,61 @@ static const char* const sched_policy_table[] = {
 };
 
 DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(sched_policy, int, INT_MAX);
+
+unsigned int read_cmdline(char *restrict const dst, unsigned sz, const char* whom, const char *what, char sep) {
+        char path[PATH_MAX];
+        _cleanup_close_ int fd = 0;
+        int len = 0;
+        unsigned n = 0;
+
+        if (sz <= 0)
+                return 0;
+
+        if (sz >= INT_MAX)
+                sz = INT_MAX-1;
+
+        dst[0] = '\0';
+
+        len = snprintf(path, sizeof(path), "%s/%s", whom, what);
+        if (len <= 0 || (size_t)len >= sizeof(path))
+                return 0;
+
+        fd = open(path, O_RDONLY);
+        if (fd == -1)
+                return 0;
+
+        for (;;) {
+                ssize_t r = read(fd, dst+n, sz-n);
+
+                if (r == -1) {
+                        if (errno == EINTR)
+                                continue;
+                        break;
+                }
+
+                if (r <= 0)
+                        break;
+                n += r;
+
+                if (n == sz) {
+                        --n;
+                        break;
+                }
+        }
+
+        if (n) {
+                unsigned i = n;
+
+                while (i && dst[i-1] == '\0')
+                        --i;
+
+                while (i--)
+                        if (dst[i] == '\n' || dst[i] == '\0') dst[i] = sep;
+
+                if (dst[n-1] == ' ')
+                        dst[n-1] = '\0';
+        }
+
+        dst[n] = '\0';
+        return n;
+}
diff --git a/src/basic/process-util.h b/src/basic/process-util.h
index af6cba1..060c0c2 100644
--- a/src/basic/process-util.h
+++ b/src/basic/process-util.h
@@ -218,6 +218,8 @@ int setpriority_closest(int priority);
 
 _noreturn_ void freeze(void);
 
+unsigned int read_cmdline(char *restrict const dst, unsigned sz, const char* whom, const char *what, char sep);
+
 int get_process_threads(pid_t pid);
 
 int is_reaper_process(void);
diff --git a/src/core/fuser.c b/src/core/fuser.c
new file mode 100644
index 0000000..e943469
--- /dev/null
+++ b/src/core/fuser.c
@@ -0,0 +1,506 @@
+#include "fuser.h"
+#include "process-util.h"
+
+static int parse_dir(struct name *this_name, struct inode *match_inode) {
+        if ((this_name == NULL) || (match_inode == NULL)) {
+                manager_status_printf(NULL, STATUS_TYPE_NORMAL, NULL,
+                        "Can't parse dir.");
+                return -1;
+        }
+
+        if (stat(this_name->filename, &this_name->st) != 0) {
+                manager_status_printf(NULL, STATUS_TYPE_NORMAL, NULL,
+                        "Can't stat dir %s.", this_name->filename);
+                return -1;
+        }
+
+        match_inode->name = this_name;
+        match_inode->device = this_name->st.st_dev;
+        match_inode->inode = this_name->st.st_ino;
+
+        return 0;
+}
+
+static int parse_mounts(struct name *this_name, struct device *match_device) {
+        if ((this_name == NULL) && (match_device == NULL)) {
+                manager_status_printf(NULL, STATUS_TYPE_NORMAL, NULL,
+                        "Can't parse mounts.");
+                return -1;
+        }
+
+        match_device->name = this_name;
+
+        if (S_ISBLK(this_name->st.st_mode))
+                match_device->device = this_name->st.st_rdev;
+        else
+                match_device->device = this_name->st.st_dev;
+
+        return 0;
+}
+
+static uid_t getpiduid(const pid_t pid) {
+        char pathname[PATH_MAX];
+        struct stat st;
+        int r = 0;
+
+        r = snprintf(pathname, sizeof(pathname), "/proc/%d", pid);
+        if (r < 0) {
+                manager_status_printf(NULL, STATUS_TYPE_NORMAL, NULL,
+                        "Snprintf run failed in getpiduid.");
+                return 0;
+        }
+
+        if (stat(pathname, &st) != 0)
+                return 0;
+
+        return st.st_uid;
+}
+
+static struct stat *get_pidstat(const pid_t pid) {
+        char pathname[PATH_MAX];
+        struct stat *st = NULL;
+        int r = 0;
+
+        st = (struct stat *)malloc(sizeof(struct stat));
+        if (st == NULL) {
+                manager_status_printf(NULL, STATUS_TYPE_NORMAL, NULL,
+                        "Malloc failed in get_pidstat.");
+                return NULL;
+        }
+
+        r = snprintf(pathname, sizeof(pathname), "/proc/%d/cwd", pid);
+        if (r < 0) {
+                manager_status_printf(NULL, STATUS_TYPE_NORMAL, NULL,
+                        "Snprintf run failed in get_pidstat.");
+                return NULL;
+        }
+
+        if (stat(pathname, st) != 0) {
+                free(st);
+                return NULL;
+        }
+
+        return st;
+}
+
+static void add_matched_proc(struct name *name, const pid_t pid, const uid_t uid) {
+        struct procs *pptr = NULL;
+        struct procs *last_proc = NULL;
+        char pathname[PATH_MAX];
+        char cmdname[CMD_NAME_LEN + 1];
+        char *cptr = NULL;
+        int cmdlen = 0;
+        FILE *fp = NULL;
+
+        if (name == NULL) {
+                manager_status_printf(NULL, STATUS_TYPE_NORMAL, NULL,
+                        "Name should not be NULL.");
+                return;
+        }
+
+        //find out wheather the pid already in pptr->pid
+        for (pptr = name->matched_procs; pptr != NULL; pptr = pptr->next) {
+                last_proc = pptr;
+
+                if (pptr->pid == pid)
+                            return;
+        }
+
+        pptr = (struct procs *)malloc(sizeof(struct procs));
+        if (pptr == NULL) {
+                manager_status_printf(NULL, STATUS_TYPE_NORMAL, NULL,
+                        "Can't malloc in add_matched_proc.");
+                return;
+        }
+
+        pptr->pid = pid;
+        pptr->uid = uid;
+        pptr->username = NULL;
+        pptr->next = NULL;
+        pptr->command = NULL;
+
+        if ((snprintf(pathname, sizeof(pathname), "/proc/%d/stat", pid) > 0) &&
+                ((fp = fopen(pathname, "r")) != NULL) && (fscanf(fp, "%*d (%100[^)]", cmdname) == 1)) {
+                pptr->command = (char *)malloc(COMM_LEN + 1);
+
+                if (pptr->command != NULL) {
+                       cmdlen = 0;
+
+                       for (cptr = cmdname; cmdlen < COMM_LEN && *cptr; cptr++) {
+                               if (isprint(*cptr)) {
+                                       pptr->command[cmdlen++] = *cptr;
+                               } else if (cmdlen < (COMM_LEN - 4)) {
+                                       cmdlen += sprintf(&(pptr->command[cmdlen]), "\\%03o", (unsigned int)*cptr);
+                               }
+                       }
+
+                       pptr->command[cmdlen] = '\0';
+                }
+        }
+
+        if (last_proc == NULL)
+                name->matched_procs = pptr;
+        else
+                last_proc->next = pptr;
+
+        if (fp)
+                fclose(fp);
+}
+
+static void check_dir(const pid_t pid, const char *dirname, const struct device *dev,
+        const struct inode *ino, const uid_t uid) {
+        DIR *dirp = NULL;
+        dev_t thedev;
+        struct dirent *direntry = NULL;
+        struct stat st;
+        char dirpath[PATH_MAX];
+        char filepath[PATH_MAX];
+        int r = 0;
+
+        if (dirname == NULL) {
+                manager_status_printf(NULL, STATUS_TYPE_NORMAL, NULL,
+                        "Dirname is NULL.");
+                return;
+        }
+
+        r = snprintf(dirpath, sizeof(dirpath), "/proc/%d/%s", pid, dirname);
+        if (r < 0) {
+                manager_status_printf(NULL, STATUS_TYPE_NORMAL, NULL,
+                        "Snprintf run failed in check_dir.");
+               return;
+        }
+
+        dirp = opendir(dirpath);
+        if (dirp == NULL)
+                return;
+
+        while ((direntry = readdir(dirp)) != NULL) {
+                if (direntry->d_name[0] < '0' || direntry->d_name[0] > '9')
+                        continue;
+
+                snprintf(filepath, sizeof(filepath), "/proc/%d/%s/%s",
+                        pid, dirname, direntry->d_name);
+
+                if (stat(filepath, &st) != 0)
+                        continue;
+
+                thedev = st.st_dev;
+
+                if ((dev != NULL) && (thedev == dev->device)) {
+                        add_matched_proc(dev->name, pid, uid);
+                }
+
+                if ((ino != NULL) && (thedev == ino->device)) {
+                        if (st.st_ino == ino->inode) {
+                                add_matched_proc(ino->name, pid, uid);
+                        }
+                }
+        } //end while
+
+        closedir(dirp);
+}
+
+static int scan_procs(const struct name *name, const struct inode *ino, const struct device *dev) {
+        DIR *topproc_dir = NULL;
+        struct dirent *topproc_dent = NULL;
+        pid_t pid;
+        pid_t my_pid;
+        uid_t uid;
+
+        if (name == NULL) {
+                manager_status_printf(NULL, STATUS_TYPE_NORMAL, NULL,
+                         "Name should not be null in scan_procs.");
+                return -1;
+        }
+
+        if ((ino == NULL) && (dev == NULL)) {
+                manager_status_printf(NULL, STATUS_TYPE_NORMAL, NULL,
+                        "Ino and dev should not be NULL in scan_procs.");
+                return -1;
+        }
+
+        topproc_dir = opendir("/proc");
+        if (topproc_dir == NULL) {
+                manager_status_printf(NULL, STATUS_TYPE_NORMAL, NULL,
+                        "Can't open dir proc.");
+                return -1;
+        }
+
+       my_pid = getpid();
+
+        while ((topproc_dent = readdir(topproc_dir)) != NULL) {
+                dev_t scan_dev;
+                struct stat *st = NULL;
+
+                /* Not a process */
+                if ((topproc_dent->d_name[0] < '0') || (topproc_dent->d_name[0] > '9'))
+                        continue;
+
+                pid = atoi(topproc_dent->d_name);
+                if (pid == my_pid)
+                        continue;
+
+                uid = getpiduid(pid);
+
+                st = get_pidstat(pid);
+                scan_dev = st ? st->st_dev : 0;
+
+                if ((dev != NULL) && (scan_dev == dev->device))
+                        add_matched_proc(dev->name, pid, uid);
+
+                if ((ino != NULL) && (scan_dev == ino->device)) {
+                        if (!st)
+                                st = get_pidstat(pid);
+
+                        if (st && (st->st_dev == ino->device) && (st->st_ino == ino->inode))
+                                add_matched_proc(ino->name, pid, uid);
+                }
+
+                if (st)
+                        free(st);
+
+                check_dir(pid, "fd", dev, ino, uid);
+        } // end while
+
+        closedir(topproc_dir);
+        return 0;
+}
+
+static void add_special_proc(struct name *name, const uid_t uid, const char *command) {
+        struct procs *pptr = NULL;
+
+        if (name == NULL) {
+                manager_status_printf(NULL, STATUS_TYPE_NORMAL, NULL,
+                        "Name should not be null in add_special_proc.");
+                return;
+        }
+
+        for (pptr = name->matched_procs; pptr != NULL; pptr = pptr->next) {
+                if (pptr->command != NULL && strcmp(pptr->command, command) == 0)
+                        return;
+        }
+
+        if ((pptr = malloc(sizeof(struct procs))) == NULL) {
+                manager_status_printf(NULL, STATUS_TYPE_NORMAL, NULL,
+                        "Can't allocate memory for add_special_proc() proc");
+                return;
+        }
+
+        pptr->pid = 0;
+        pptr->uid = uid;
+        pptr->next = name->matched_procs;
+        pptr->command = strdup(command);
+
+        name->matched_procs = pptr;
+}
+
+static void scan_mounts_and_swaps(const struct name *name, const struct inode *ino,
+            const struct device *dev, const char *file) {
+        FILE *fp = NULL;
+        char line[PATH_MAX];
+        char *find_mountp = NULL;
+        char *find_space_mounts = NULL;
+        char *find_space_swaps = NULL;
+        struct stat st;
+
+        if (name == NULL) {
+                manager_status_printf(NULL, STATUS_TYPE_NORMAL, NULL,
+                        "Name should not be null in scan_mounts_and_swaps.");
+                return;
+        }
+
+        if ((ino == NULL) && (dev == NULL)) {
+                manager_status_printf(NULL, STATUS_TYPE_NORMAL, NULL,
+                        "Ino and dev should not be null in scan_mounts_and_swaps.");
+                return;
+        }
+
+        fp = fopen(file, "r");
+        if (fp == NULL) {
+                manager_status_printf(NULL, STATUS_TYPE_NORMAL, NULL,
+                        "Can't open file %s", file);
+                return;
+        }
+
+        while (fgets(line, PATH_MAX, fp) != NULL) {
+                if (strcmp(file, PROC_MOUNTS) == 0) {
+                        if ((find_mountp = strchr(line, ' ')) == NULL)
+                                continue;
+
+                        find_mountp++;
+
+                        find_space_mounts = strchr(find_mountp, ' ');
+                        if (find_space_mounts == NULL)
+                                continue;
+
+                        *find_space_mounts = '\0';
+
+                        if (stat(find_mountp, &st) != 0)
+                                continue;
+                } else {
+                        find_space_swaps = strchr(line, ' ');
+                        if (find_space_swaps == NULL)
+                                continue;
+
+                        *find_space_swaps = '\0';
+                        find_space_swaps++;
+
+                        while (*find_space_swaps == ' ') {
+                                find_space_swaps++;
+
+                                if (*find_space_swaps == '\0')
+                                        continue;
+                        }
+
+                        if (stat(line, &st) != 0) {
+                                continue;
+                        }
+                }
+
+                if ((dev != NULL) && (st.st_dev == dev->device)) {
+                        if (strcmp(file, PROC_MOUNTS) == 0)
+                                add_special_proc(dev->name, 0, find_mountp);
+
+                        if (strcmp(file, PROC_SWAPS) == 0)
+                                add_special_proc(dev->name, 0, line);
+                }
+
+                if ((ino != NULL) && (st.st_dev == ino->device) && (st.st_ino == ino->inode)) {
+                        if (strcmp(file, PROC_MOUNTS) == 0)
+                                add_special_proc(ino->name, 0, find_mountp);
+
+                        if (strcmp(file, PROC_SWAPS) == 0)
+                                add_special_proc(ino->name, 0, line);
+                }
+        } // end while
+
+        fclose(fp);
+}
+
+static void print_matches(const struct name *name) {
+        struct procs *pptr = NULL;
+        struct passwd *pwent = NULL;
+        static char P_cmd_long[MAX_COMM_LEN];
+        char cmd_path[PATH_MAX];
+        int r = 0;
+
+        if (name == NULL) {
+                manager_status_printf(NULL, STATUS_TYPE_NORMAL, NULL,
+                        "Name should not be null in print_matches.");
+                return;
+        }
+
+        manager_status_printf(NULL, STATUS_TYPE_NORMAL, NULL,
+                "\t\tUSER\t\tPID\tCOMMAND");
+        manager_status_printf(NULL, STATUS_TYPE_NORMAL, NULL,
+                "%s:", name->filename);
+
+        for (pptr = name->matched_procs; pptr != NULL; pptr = pptr->next) {
+                if (pwent == NULL || pwent->pw_uid != pptr->uid)
+                        pwent = getpwuid(pptr->uid); //get username
+
+                r = snprintf(cmd_path, sizeof(cmd_path), "/proc/%d", pptr->pid);
+                if (r <= 0) {
+                        manager_status_printf(NULL, STATUS_TYPE_NORMAL, NULL, "Can't snprintf /proc/%d.", pptr->pid);
+                        return;
+                }
+
+                read_cmdline(P_cmd_long, sizeof(P_cmd_long), cmd_path, "cmdline", ' ');
+
+                if (strlen(P_cmd_long) != 0){
+                        free(pptr->command);
+                        pptr->command = strdup(P_cmd_long);
+                }
+
+                if (pptr->command == NULL)
+                        continue;
+
+                if (pwent != NULL) {
+                        if (pptr->pid != 0)
+                                manager_status_printf(NULL, STATUS_TYPE_NORMAL, NULL,
+                                        "\t\t%-s\t\t%-d\t%-s", pwent->pw_name, pptr->pid, pptr->command);
+                        else
+                                manager_status_printf(NULL, STATUS_TYPE_NORMAL, NULL,
+                                        "\t\t%-s\t\t%-s\t%-s", pwent->pw_name, "kernel", pptr->command);
+                } else {
+                        if (pptr->pid != 0)
+                                manager_status_printf(NULL, STATUS_TYPE_NORMAL, NULL,
+                                        "\t\t%-u\t\t%-d\t%-s", pptr->uid, pptr->pid, pptr->command);
+                        else
+                                manager_status_printf(NULL, STATUS_TYPE_NORMAL, NULL,
+                                      "\t\t%-u\t\t%-s\t%-s", pptr->uid, "kernel", pptr->command);
+                }
+        }
+}
+
+static void free_matched_procs(struct procs *matched_procs) {
+        struct procs *procs_tmp = NULL;
+        struct procs *procs_next = NULL;
+
+        procs_tmp = matched_procs;
+
+        while (procs_tmp != NULL) {
+                procs_next = procs_tmp->next;
+
+                if (procs_tmp->command)
+                        free(procs_tmp->command);
+
+                free(procs_tmp);
+
+                procs_tmp = procs_next;
+        }
+}
+
+int fuser(const char *dir) {
+        struct name this_name;
+        struct inode match_inode;
+        struct device match_device;
+        int r = 0;
+
+        if (dir == NULL) {
+                manager_status_printf(NULL, STATUS_TYPE_NORMAL, NULL,
+                        "Dir should not be NULL.");
+                return -1;
+        }
+
+        this_name.matched_procs = NULL;
+
+        this_name.filename = strdup(dir); //need to free
+        if (this_name.filename == NULL) {
+                manager_status_printf(NULL, STATUS_TYPE_NORMAL, NULL,
+                      "Can't allocate memory for fuser() this_name->filename.");
+                return -1;
+        }
+
+        r = parse_dir(&this_name, &match_inode);
+        if (r < 0) {
+                manager_status_printf(NULL, STATUS_TYPE_NORMAL, NULL,
+                        "%s", "Failed to parse file.");
+                free(this_name.filename);
+                return -1;
+        }
+
+        r = parse_mounts(&this_name, &match_device);
+        if (r < 0) {
+                manager_status_printf(NULL, STATUS_TYPE_NORMAL, NULL,
+                        "%s", "Failed to parse mounts.");
+                free(this_name.filename);
+                return -1;
+        }
+
+        r = scan_procs(&this_name, &match_inode, &match_device);
+        if (r < 0) {
+                manager_status_printf(NULL, STATUS_TYPE_NORMAL, NULL,
+                        "%s", "Failed to scan_procs.");
+                free(this_name.filename);
+                return -1;
+        }
+
+        scan_mounts_and_swaps(&this_name, &match_inode, &match_device, PROC_MOUNTS);
+        scan_mounts_and_swaps(&this_name, &match_inode, &match_device, PROC_SWAPS);
+        print_matches(&this_name);
+
+        free_matched_procs(this_name.matched_procs);
+        free(this_name.filename);
+        return 0;
+}
diff --git a/src/core/fuser.h b/src/core/fuser.h
new file mode 100644
index 0000000..b74b879
--- /dev/null
+++ b/src/core/fuser.h
@@ -0,0 +1,55 @@
+#pragma once
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <string.h>
+#include <limits.h>
+#include <errno.h>
+
+#include "manager.h"
+
+struct procs {
+        pid_t pid;
+        uid_t uid;
+        char *username;
+        char *command;
+        struct procs *next;
+};
+
+struct name {
+        char *filename;
+        struct stat st;
+        struct procs *matched_procs;
+};
+
+struct inode {
+        struct name *name;
+        dev_t device;
+        ino_t inode;
+};
+
+struct device {
+        struct name *name;
+        dev_t device;
+};
+
+#ifndef PATH_MAX
+#define PATH_MAX 4096
+#endif /* PATH_MAX */
+
+#define CMD_NAME_LEN 100
+#define COMM_LEN 64
+#define MAX_COMM_LEN 1024
+#define PROC_MOUNTS "/proc/mounts"
+#define PROC_SWAPS "/proc/swaps"
+
+int fuser(const char *dir);
diff --git a/src/core/job.c b/src/core/job.c
index e7d1f65..b86aadd 100644
--- a/src/core/job.c
+++ b/src/core/job.c
@@ -27,6 +27,9 @@
 #include "terminal-util.h"
 #include "unit.h"
 #include "virt.h"
+#include "fuser.h"
+#include "mount.h"
+#include "process-util.h"
 
 Job* job_new_raw(Unit *unit) {
         Job *j;
@@ -729,6 +732,8 @@ static const char* job_done_mid(JobType type, JobResult result) {
 static void job_emit_done_message(Unit *u, uint32_t job_id, JobType t, JobResult result) {
         _cleanup_free_ char *free_ident = NULL;
         const char *ident, *format;
+        int r = 0;
+        pid_t pid;
 
         assert(u);
         assert(t >= 0);
@@ -825,6 +830,37 @@ static void job_emit_done_message(Unit *u, uint32_t job_id, JobType t, JobResult
                                                       "See 'systemctl status %s' for details.", quoted);
                 }
         }
+
+        if (FLAGS_SET(manager_state(u->manager), MANAGER_STOPPING) && u->manager->defaults.dfx_reboot &&
+                ((u->type == UNIT_MOUNT || u->type == UNIT_AUTOMOUNT) && t == JOB_STOP && result == JOB_FAILED)) {
+
+                Mount *m = MOUNT(u);
+
+                r = safe_fork("(fuser-shutdown)", FORK_RESET_SIGNALS, &pid);
+                if (r < 0) {
+                        manager_status_printf(NULL, STATUS_TYPE_NORMAL, NULL,
+                                "Failed to fork for fuser!");
+                       return;
+                }
+                if (r == 0) {
+                        manager_status_printf(NULL, STATUS_TYPE_NORMAL, NULL,
+                                "-------------fuser -mv %s----------------", m->where);
+
+                        r = fuser(m->where);
+                        if (r < 0)
+                                manager_status_printf(NULL, STATUS_TYPE_NORMAL, NULL, "Can't run fuser.");
+
+                        manager_status_printf(NULL, STATUS_TYPE_NORMAL, NULL,
+                                "%s","----------------------------------------------------------------------");
+                        _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
+                }
+
+                r = wait_for_terminate_with_timeout(pid, 3 * USEC_PER_SEC);
+                if (r == -ETIMEDOUT) {
+                        manager_status_printf(NULL, STATUS_TYPE_NORMAL, NULL, "Timeout to run (fuser-shutdown).");
+                        (void) kill(pid, SIGKILL);
+                }
+        }
 }
 
 static int job_perform_on_unit(Job **j) {
diff --git a/src/core/main.c b/src/core/main.c
index 96b0a11..ddbabaa 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -622,6 +622,7 @@ static int parse_config_file(void) {
                 { "Manager", "CrashChangeVT",                config_parse_crash_chvt,            0,                        &arg_crash_chvt                   },
                 { "Manager", "CrashShell",                   config_parse_bool,                  0,                        &arg_crash_shell                  },
                 { "Manager", "CrashReboot",                  config_parse_bool,                  0,                        &arg_crash_reboot                 },
+                { "Manager", "DefaultDFXReboot",             config_parse_bool,                  0,                        &arg_defaults.dfx_reboot          },
                 { "Manager", "ShowStatus",                   config_parse_show_status,           0,                        &arg_show_status                  },
                 { "Manager", "StatusUnitFormat",             config_parse_status_unit_format,    0,                        &arg_status_unit_format           },
                 { "Manager", "CPUAffinity",                  config_parse_cpu_affinity2,         0,                        &arg_cpu_affinity                 },
@@ -1471,7 +1472,8 @@ static int become_shutdown(int objective, int retval) {
 
         char log_level[STRLEN("--log-level=") + DECIMAL_STR_MAX(int)],
              timeout[STRLEN("--timeout=") + DECIMAL_STR_MAX(usec_t) + STRLEN("us")],
-             exit_code[STRLEN("--exit-code=") + DECIMAL_STR_MAX(uint8_t)];
+             exit_code[STRLEN("--exit-code=") + DECIMAL_STR_MAX(uint8_t)],
+             dfx_reboot[STRLEN("--dfx-reboot=") + DECIMAL_STR_MAX(bool)];
 
         _cleanup_strv_free_ char **env_block = NULL;
         usec_t watchdog_timer = 0;
@@ -1482,15 +1484,17 @@ static int become_shutdown(int objective, int retval) {
 
         xsprintf(log_level, "--log-level=%d", log_get_max_level());
         xsprintf(timeout, "--timeout=%" PRI_USEC "us", arg_defaults.timeout_stop_usec);
+        xsprintf(dfx_reboot, "--dfx-reboot=%d", arg_defaults.dfx_reboot);
 
-        const char* command_line[10] = {
+        const char* command_line[11] = {
                 SYSTEMD_SHUTDOWN_BINARY_PATH,
                 table[objective],
                 log_level,
                 timeout,
+                dfx_reboot,
                 /* Note that the last position is a terminator and must contain NULL. */
         };
-        size_t pos = 4;
+        size_t pos = 5;
 
         assert(command_line[pos-1]);
         assert(!command_line[pos]);
diff --git a/src/core/manager.c b/src/core/manager.c
index b29d4e1..53fd07d 100644
--- a/src/core/manager.c
+++ b/src/core/manager.c
@@ -4206,6 +4206,8 @@ int manager_set_unit_defaults(Manager *m, const UnitDefaults *defaults) {
         m->defaults.oom_score_adjust = defaults->oom_score_adjust;
         m->defaults.oom_score_adjust_set = defaults->oom_score_adjust_set;
 
+        m->defaults.dfx_reboot = defaults->dfx_reboot;
+
         m->defaults.memory_pressure_watch = defaults->memory_pressure_watch;
         m->defaults.memory_pressure_threshold_usec = defaults->memory_pressure_threshold_usec;
 
@@ -4978,6 +4980,8 @@ void unit_defaults_init(UnitDefaults *defaults, RuntimeScope scope) {
 
                 .oom_policy = OOM_STOP,
                 .oom_score_adjust_set = false,
+
+                .dfx_reboot = false,
         };
 }
 
diff --git a/src/core/manager.h b/src/core/manager.h
index 93e9d2a..19fb33b 100644
--- a/src/core/manager.h
+++ b/src/core/manager.h
@@ -177,6 +177,8 @@ typedef struct UnitDefaults {
         int oom_score_adjust;
         bool oom_score_adjust_set;
 
+        bool dfx_reboot;
+
         CGroupPressureWatch memory_pressure_watch;
         usec_t memory_pressure_threshold_usec;
 
diff --git a/src/core/meson.build b/src/core/meson.build
index 7701d3d..83103ef 100644
--- a/src/core/meson.build
+++ b/src/core/meson.build
@@ -68,6 +68,7 @@ libcore_sources = files(
         'unit-printf.c',
         'unit-serialize.c',
         'unit.c',
+        'fuser.c',
 )
 
 if conf.get('BPF_FRAMEWORK') == 1
diff --git a/src/core/system.conf.in b/src/core/system.conf.in
index dbdc47c..3495b8e 100644
--- a/src/core/system.conf.in
+++ b/src/core/system.conf.in
@@ -80,6 +80,7 @@ DefaultLimitMEMLOCK=64M
 #DefaultMemoryPressureThresholdSec=200ms
 #DefaultMemoryPressureWatch=auto
 #DefaultOOMPolicy=stop
+#DefaultDFXReboot=no
 #DefaultSmackProcessLabel=
 #ReloadLimitIntervalSec=
 #ReloadLimitBurst=
diff --git a/src/shutdown/meson.build b/src/shutdown/meson.build
index 219f9fd..c932e28 100644
--- a/src/shutdown/meson.build
+++ b/src/shutdown/meson.build
@@ -1,5 +1,7 @@
 # SPDX-License-Identifier: LGPL-2.1-or-later
 
+shutdown_includes = [includes, include_directories('.')]
+
 systemd_shutdown_sources = files(
         'detach-dm.c',
         'detach-loopback.c',
@@ -7,12 +9,18 @@ systemd_shutdown_sources = files(
         'detach-swap.c',
         'shutdown.c',
         'umount.c',
+        'process-status.c',
 )
 
 executables += [
         libexec_template + {
                 'name' : 'systemd-shutdown',
                 'sources' : systemd_shutdown_sources,
+                'include_directories' : core_includes,
+                'link_with' : [
+                        libcore,
+                        libshared
+                ],
                 'dependencies' : libmount,
         },
         libexec_template + {
@@ -34,6 +42,11 @@ executables += [
                         'detach-swap.c',
                         'umount.c',
                 ),
+                'include_directories' : core_includes,
+                'link_with' : [
+                        libcore,
+                        libshared
+                ],
                 'dependencies' : libmount,
         },
 ]
diff --git a/src/shutdown/process-status.c b/src/shutdown/process-status.c
new file mode 100644
index 0000000..11837a2
--- /dev/null
+++ b/src/shutdown/process-status.c
@@ -0,0 +1,143 @@
+#include "process-status.h"
+#include "process-util.h"
+
+static uid_t P_uid;
+static int P_pid;
+static int P_ppid;
+static char P_stat[COMM_LEN];
+static char P_cmd_short[COMM_LEN];
+static char P_user[COMM_LEN];
+static char P_cmd_long[COMM_LEN];
+
+static int read_from_stat(int pid) {
+        char buf[PATH_MAX];
+        char cmd_path[PATH_MAX];
+        char pathname[PATH_MAX];
+        int fd = 0;
+        struct stat st;
+        int r = 0;
+
+        memset(buf, 0, sizeof(buf));
+        memset(cmd_path, 0, sizeof(cmd_path));
+        memset(pathname, 0, sizeof(pathname));
+
+        r = snprintf(pathname, sizeof(pathname), "/proc/%d", pid);
+        if (r <= 0) {
+                manager_status_printf(NULL, STATUS_TYPE_NORMAL, NULL,
+                        "Can't snprintf /proc/%d.", pid);
+                return -1;
+        }
+
+        if (stat(pathname, &st) != 0) {
+                manager_status_printf(NULL, STATUS_TYPE_NORMAL, NULL,
+                        "Can't stat %s.", pathname);
+                return -1;
+        }
+
+        P_uid = st.st_uid;
+
+        r = snprintf(buf, sizeof(buf), "/proc/%d/stat", pid);
+        if (r <= 0) {
+                manager_status_printf(NULL, STATUS_TYPE_NORMAL, NULL,
+                        "Can't snprintf /proc/%d/stat.", pid);
+                return -1;
+        }
+
+        fd = open(buf, O_RDONLY, 0);
+        if (fd == -1) {
+                manager_status_printf(NULL, STATUS_TYPE_NORMAL, NULL,
+                        "Can't open %s.", buf);
+                return -1;
+        }
+
+        r = read(fd, buf, sizeof(buf) - 1);
+        if (r < 0) {
+                manager_status_printf(NULL, STATUS_TYPE_NORMAL, NULL,
+                        "Can't read /proc/%d/stat.", pid);
+                close(fd);
+                return -1;
+        }
+
+        r = sscanf(buf, "%d %s %s %d", &P_pid, P_cmd_short, P_stat, &P_ppid);
+        if (r < 0) {
+                manager_status_printf(NULL, STATUS_TYPE_NORMAL, NULL,
+                        "Can't run sscanf.");
+                close(fd);
+                return -1;
+        }
+
+        close(fd);
+
+        if(P_pid != pid)
+                return -1;
+
+        r = snprintf(cmd_path, sizeof(cmd_path), "/proc/%d", pid);
+        if (r <= 0) {
+            manager_status_printf(NULL, STATUS_TYPE_NORMAL, NULL, "Can't snprintf /proc/%d.", pid);
+             return -1;
+        }
+
+        /* read from /proc/$pid/cmdline */
+        read_cmdline(P_cmd_long, sizeof(P_cmd_long), cmd_path, "cmdline", ' ');
+
+        return 0;
+}
+
+static void do_user(void) {
+        struct passwd *p = NULL;
+
+        p = getpwuid(P_uid);
+        if (p) {
+                snprintf(P_user, sizeof(P_user), "%s", p->pw_name);
+        } else {
+                snprintf(P_user, sizeof(P_user), "%u", P_uid);
+        }
+}
+
+static void print_proc(void) {
+        if ((P_ppid != KTHREADD) && (strcmp(P_cmd_short, "(kthreadd)") != 0)) {
+                if (strlen(P_cmd_long) != 0) {
+                        manager_status_printf(NULL, STATUS_TYPE_NORMAL,"systemd-shutdown",
+                                "%-s\t%-d\t%-d\t%-s", P_user, P_pid, P_ppid, P_cmd_long);
+                } else {
+                        manager_status_printf(NULL, STATUS_TYPE_NORMAL,"systemd-shutdown",
+                                "%-s\t%-d\t%-d\t%-s", P_user, P_pid, P_ppid, P_cmd_short);
+                }
+        }
+}
+
+int process_status(void) {
+        manager_status_printf(NULL, STATUS_TYPE_NORMAL,"systemd-shutdown",
+                "%s", "-----------------------------------------------------------------");
+        manager_status_printf(NULL, STATUS_TYPE_NORMAL,"systemd-shutdown",
+                "%s", "USER\tPID\tPPID\tCMD");
+
+        struct dirent *ent = NULL;
+        DIR *dir = NULL;
+
+        dir = opendir("/proc");
+        if (dir == NULL) {
+                manager_status_printf(NULL, STATUS_TYPE_NORMAL,"systemd-shutdown",
+                        "%s", "can't open /proc");
+                return -1;
+        }
+
+        while((ent = readdir(dir))){
+                if (*ent->d_name < '0' || *ent->d_name > '9')
+                        continue;
+
+                if (read_from_stat(atoi(ent->d_name)) != 0)
+                        continue;
+
+                do_user();
+
+                print_proc();
+        }
+
+        closedir(dir);
+
+        manager_status_printf(NULL, STATUS_TYPE_NORMAL,"systemd-shutdown",
+                "%s", "------------------------------------------------------------------");
+
+        return 0;
+}
diff --git a/src/shutdown/process-status.h b/src/shutdown/process-status.h
new file mode 100644
index 0000000..2f4333d
--- /dev/null
+++ b/src/shutdown/process-status.h
@@ -0,0 +1,24 @@
+#pragma once
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <dirent.h>
+#include <string.h>
+#include <pwd.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <limits.h>
+#include <errno.h>
+
+#include "manager.h"
+
+#define COMM_LEN 512
+
+#ifndef PATH_MAX
+#define PATH_MAX 4096
+#endif
+
+#define KTHREADD 2
+
+int process_status(void);
diff --git a/src/shutdown/shutdown.c b/src/shutdown/shutdown.c
index b976b7d..d6beb2d 100644
--- a/src/shutdown/shutdown.c
+++ b/src/shutdown/shutdown.c
@@ -48,13 +48,17 @@
 #include "umount.h"
 #include "virt.h"
 #include "watchdog.h"
+#include "process-status.h"
 
 #define SYNC_PROGRESS_ATTEMPTS 3
 #define SYNC_TIMEOUT_USEC (10*USEC_PER_SEC)
+#define SHUTDOWN_TIMEOUT_MIN (0*USEC_PER_SEC)
+#define SHUTDOWN_TIMEOUT_INTERVAL (30*USEC_PER_SEC)
 
 static char* arg_verb;
 static uint8_t arg_exit_code;
 static usec_t arg_timeout = DEFAULT_TIMEOUT_USEC;
+static bool dfx_reboot = false;
 
 static int parse_argv(int argc, char *argv[]) {
         enum {
@@ -82,6 +86,13 @@ static int parse_argv(int argc, char *argv[]) {
         while ((c = getopt_long(argc, argv, "-", options, NULL)) >= 0)
                 switch (c) {
 
+                case ARG_DFX_REBOOT:
+                        if (streq(optarg, "1")) {
+                                dfx_reboot = true;
+                        }
+
+                        break;
+
                 case ARG_LOG_LEVEL:
                         r = log_set_max_level_from_string(optarg);
                         if (r < 0)
@@ -341,6 +352,9 @@ int main(int argc, char *argv[]) {
         _cleanup_free_ char *cgroup = NULL;
         char *arguments[3];
         int cmd, r;
+        usec_t now_time, time_interval;
+        pid_t pid;
+        bool fork_failed = false;
 
         /* Close random fds we might have get passed, just for paranoia, before we open any new fds, for
          * example for logging. After all this tool's purpose is about detaching any pinned resources, and
@@ -432,8 +446,37 @@ int main(int argc, char *argv[]) {
              need_dm_detach = !in_container, need_md_detach = !in_container, can_initrd, last_try = false;
         can_initrd = !in_container && !in_initrd() && access("/run/initramfs/shutdown", X_OK) == 0;
 
+        now_time = now(CLOCK_MONOTONIC);
+        time_interval = SHUTDOWN_TIMEOUT_MIN;
         /* Unmount all mountpoints, swaps, and loopback devices */
         for (;;) {
+                if (dfx_reboot && (now(CLOCK_MONOTONIC) >= now_time + time_interval)) {
+                        r = safe_fork("(process_status)", FORK_RESET_SIGNALS, &pid);
+                        if (r < 0) {
+                               manager_status_printf(NULL, STATUS_TYPE_NORMAL, NULL,
+                                "Failed to fork for process_status!");
+                                fork_failed = true;
+                        }
+                        if (r == 0) {
+                                r = process_status();
+                                if (r < 0)
+                                        manager_status_printf(NULL, STATUS_TYPE_NORMAL, NULL, "Can't run ps.");
+
+                               _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
+                        }
+
+                        now_time = now(CLOCK_MONOTONIC);
+                        time_interval = SHUTDOWN_TIMEOUT_INTERVAL;
+
+                        if (!fork_failed) {
+                                r = wait_for_terminate_with_timeout(pid, 3 * USEC_PER_SEC);
+                                if (r == -ETIMEDOUT) {
+                                        manager_status_printf(NULL, STATUS_TYPE_NORMAL, NULL, "Timeout to run (process_status).");
+                                        (void) kill(pid, SIGKILL);
+                                }
+                        }
+                }
+
                 bool changed = false;
 
                 (void) watchdog_ping();
diff --git a/src/shutdown/umount.c b/src/shutdown/umount.c
index 1a9b99d..220ae2e 100644
--- a/src/shutdown/umount.c
+++ b/src/shutdown/umount.c
@@ -28,6 +28,7 @@
 #include "signal-util.h"
 #include "umount.h"
 #include "virt.h"
+#include "manager.h"
 
 static void mount_point_free(MountPoint **head, MountPoint *m) {
         assert(head);
@@ -321,6 +322,7 @@ static int umount_with_timeout(MountPoint *m, bool last_try) {
                 pfd[0] = safe_close(pfd[0]);
 
                 log_info("Unmounting '%s'.", m->path);
+                manager_status_printf(NULL, STATUS_TYPE_NORMAL, NULL, "Unmounting '%s'.", m->path);
 
                 /* Start the mount operation here in the child Using MNT_FORCE causes some filesystems
                  * (e.g. FUSE and NFS and other network filesystems) to abort any pending requests and return
@@ -332,9 +334,12 @@ static int umount_with_timeout(MountPoint *m, bool last_try) {
                                        (m->umount_lazily ? MNT_DETACH : MNT_FORCE)));
                 if (r < 0) {
                         log_full_errno(last_try ? LOG_ERR : LOG_INFO, r, "Failed to unmount %s: %m", m->path);
+                        manager_status_printf(NULL, STATUS_TYPE_NORMAL, NULL, "Failed to unmount '%s'.", m->path);
 
                         if (r == -EBUSY && last_try)
                                 log_umount_blockers(m->path);
+                } else {
+                        manager_status_printf(NULL, STATUS_TYPE_NORMAL, NULL, "Unmounted '%s'.", m->path);
                 }
 
                 (void) write(pfd[1], &r, sizeof(r)); /* try to send errno up */
diff --git a/src/test/meson.build b/src/test/meson.build
index a7ca76e..f9e1974 100644
--- a/src/test/meson.build
+++ b/src/test/meson.build
@@ -596,4 +596,29 @@ executables += [
                         libudev_basic,
                 ],
         },
+        test_template + {
+                'sources' : files(
+                        'test-process-status.c',
+                        '../shutdown/process-status.c'
+                ),
+                'link_with' : [
+                        libcore,
+                        libshared,
+                ],
+                'include_directories' : [
+                        shutdown_includes,
+                        core_includes,
+                ]
+        },
+        test_template + {
+                'sources' : files(
+                        'test-fuser.c',
+                        '../core/fuser.c'
+                ),
+                'link_with' : [
+                        libcore,
+                        libshared,
+                ],
+                'include_directories' : core_includes,
+        },
 ]
diff --git a/src/test/test-fuser.c b/src/test/test-fuser.c
new file mode 100644
index 0000000..1527b5b
--- /dev/null
+++ b/src/test/test-fuser.c
@@ -0,0 +1,14 @@
+#include "fuser.h"
+#include "tests.h"
+
+int main(int argc, char *argv[]){
+        test_setup_logging(LOG_DEBUG);
+
+        assert_se(fuser("/") == 0);
+        assert_se(fuser(NULL) < 0);
+        assert_se(fuser("/dev") == 0);
+        assert_se(fuser("/dev/empty/mountpoint") < 0);
+        assert_se(fuser("") < 0);
+
+        return 0;
+}
diff --git a/src/test/test-process-status.c b/src/test/test-process-status.c
new file mode 100644
index 0000000..4a4c3da
--- /dev/null
+++ b/src/test/test-process-status.c
@@ -0,0 +1,10 @@
+#include "process-status.h"
+#include "tests.h"
+
+int main(int argc, char *argv[]){
+
+      assert_se(process_status() == 0);
+
+      return 0;
+
+}
-- 
2.33.0

