summaryrefslogtreecommitdiff
path: root/print-the-process-status-to-console-when-shutdown.patch
diff options
context:
space:
mode:
Diffstat (limited to 'print-the-process-status-to-console-when-shutdown.patch')
-rw-r--r--print-the-process-status-to-console-when-shutdown.patch1280
1 files changed, 1280 insertions, 0 deletions
diff --git a/print-the-process-status-to-console-when-shutdown.patch b/print-the-process-status-to-console-when-shutdown.patch
new file mode 100644
index 0000000..1669236
--- /dev/null
+++ b/print-the-process-status-to-console-when-shutdown.patch
@@ -0,0 +1,1280 @@
+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
+