Projects
openEuler:24.03:SP1:Everything
systemd
_service:tar_scm:print-the-process-status-to-co...
Sign Up
Log In
Username
Password
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File _service:tar_scm:print-the-process-status-to-console-when-shutdown.patch of Package systemd
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
Locations
Projects
Search
Status Monitor
Help
Open Build Service
OBS Manuals
API Documentation
OBS Portal
Reporting a Bug
Contact
Mailing List
Forums
Chat (IRC)
Twitter
Open Build Service (OBS)
is an
openSUSE project
.
浙ICP备2022010568号-2