From 5808e71b99e330f7608dd1133de7e3b9c599e285 Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: Mar 01 2023 06:10:27 +0000 Subject: import systemd-239-73.el8 --- diff --git a/SOURCES/0873-journald-add-API-to-move-logging-from-var-to-run-aga.patch b/SOURCES/0873-journald-add-API-to-move-logging-from-var-to-run-aga.patch new file mode 100644 index 0000000..f44f50b --- /dev/null +++ b/SOURCES/0873-journald-add-API-to-move-logging-from-var-to-run-aga.patch @@ -0,0 +1,176 @@ +From 8f0a91b5192b72eb8b0f06e04ef3515d0397fcb8 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Fri, 5 Apr 2019 18:20:25 +0200 +Subject: [PATCH] journald: add API to move logging from /var to /run again + +We now have this nice little Varlink API, let's beef it up a bit. + +[dtardon: This diverges from the upstream commit in two ways: One is +that the new action is bound to a signal, as varlink is not available. +The other is that this introduces a new "flag" file +/run/systemd/journal/relinquished, which is essentially the opposite of +/run/systemd/journal/flushed (hence at most one of them may exist at any +time).] + +(cherry picked from commit b4e26d1d8e582d78e67ed766177f10e8e194404c) + +Related: #1873540 +--- + src/journal/journald-server.c | 64 ++++++++++++++++++++++++++++------- + 1 file changed, 51 insertions(+), 13 deletions(-) + +diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c +index 7632e2d9d0..279a32768c 100644 +--- a/src/journal/journald-server.c ++++ b/src/journal/journald-server.c +@@ -283,13 +283,14 @@ static bool flushed_flag_is_set(void) { + return access("/run/systemd/journal/flushed", F_OK) >= 0; + } + +-static int system_journal_open(Server *s, bool flush_requested) { ++static int system_journal_open(Server *s, bool flush_requested, bool relinquish_requested) { + const char *fn; + int r = 0; + + if (!s->system_journal && + IN_SET(s->storage, STORAGE_PERSISTENT, STORAGE_AUTO) && +- (flush_requested || flushed_flag_is_set())) { ++ (flush_requested || flushed_flag_is_set()) && ++ !relinquish_requested) { + + /* If in auto mode: first try to create the machine + * path, but not the prefix. +@@ -331,7 +332,7 @@ static int system_journal_open(Server *s, bool flush_requested) { + + fn = strjoina(s->runtime_storage.path, "/system.journal"); + +- if (s->system_journal) { ++ if (s->system_journal && !relinquish_requested) { + + /* Try to open the runtime journal, but only + * if it already exists, so that we can flush +@@ -386,7 +387,7 @@ static JournalFile* find_journal(Server *s, uid_t uid) { + * else that's left the journals as NULL). + * + * Fixes https://github.com/systemd/systemd/issues/3968 */ +- (void) system_journal_open(s, false); ++ (void) system_journal_open(s, false, false); + + /* We split up user logs only on /var, not on /run. If the + * runtime file is open, we write to it exclusively, in order +@@ -964,7 +965,7 @@ int server_flush_to_var(Server *s, bool require_flag_file) { + char ts[FORMAT_TIMESPAN_MAX]; + usec_t start; + unsigned n = 0; +- int r; ++ int r, k; + + assert(s); + +@@ -977,7 +978,7 @@ int server_flush_to_var(Server *s, bool require_flag_file) { + if (require_flag_file && !flushed_flag_is_set()) + return 0; + +- (void) system_journal_open(s, true); ++ (void) system_journal_open(s, true, false); + + if (!s->system_journal) + return 0; +@@ -1056,6 +1057,13 @@ finish: + n), + NULL); + ++ if (unlink("/run/systemd/journal/relinquished") < 0 && errno != ENOENT) ++ log_warning_errno(errno, "Failed to unlink /run/systemd/journal/relinquished, ignoring: %m"); ++ ++ k = touch("/run/systemd/journal/flushed"); ++ if (k < 0) ++ log_warning_errno(k, "Failed to touch /run/systemd/journal/flushed, ignoring: %m"); ++ + return r; + } + +@@ -1180,7 +1188,6 @@ int server_process_datagram(sd_event_source *es, int fd, uint32_t revents, void + + static int dispatch_sigusr1(sd_event_source *es, const struct signalfd_siginfo *si, void *userdata) { + Server *s = userdata; +- int r; + + assert(s); + +@@ -1190,10 +1197,6 @@ static int dispatch_sigusr1(sd_event_source *es, const struct signalfd_siginfo * + server_sync(s); + server_vacuum(s, false); + +- r = touch("/run/systemd/journal/flushed"); +- if (r < 0) +- log_warning_errno(r, "Failed to touch /run/systemd/journal/flushed, ignoring: %m"); +- + server_space_usage_message(s, NULL); + return 0; + } +@@ -1250,12 +1253,43 @@ static int dispatch_sigrtmin1(sd_event_source *es, const struct signalfd_siginfo + return 0; + } + ++ ++static int dispatch_sigrtmin2(sd_event_source *es, const struct signalfd_siginfo *si, void *userdata) { ++ Server *s = userdata; ++ int r; ++ ++ assert(s); ++ ++ if (s->storage == STORAGE_NONE) ++ return 0; ++ ++ if (s->runtime_journal && !s->system_journal) ++ return 0; ++ ++ log_debug("Received request to relinquish /var from PID " PID_FMT, si->ssi_pid); ++ ++ (void) system_journal_open(s, false, true); ++ ++ s->system_journal = journal_file_close(s->system_journal); ++ ordered_hashmap_clear_with_destructor(s->user_journals, journal_file_close); ++ set_clear_with_destructor(s->deferred_closes, journal_file_close); ++ ++ if (unlink("/run/systemd/journal/flushed") < 0 && errno != ENOENT) ++ log_warning_errno(errno, "Failed to unlink /run/systemd/journal/flushed, ignoring: %m") ; ++ ++ r = write_timestamp_file_atomic("/run/systemd/journal/relinquished", now(CLOCK_MONOTONIC)); ++ if (r < 0) ++ log_warning_errno(r, "Failed to write /run/systemd/journal/relinquished, ignoring: %m"); ++ ++ return 0; ++} ++ + static int setup_signals(Server *s) { + int r; + + assert(s); + +- assert_se(sigprocmask_many(SIG_SETMASK, NULL, SIGINT, SIGTERM, SIGUSR1, SIGUSR2, SIGRTMIN+1, -1) >= 0); ++ assert_se(sigprocmask_many(SIG_SETMASK, NULL, SIGINT, SIGTERM, SIGUSR1, SIGUSR2, SIGRTMIN+1, SIGRTMIN+2, -1) >= 0); + + r = sd_event_add_signal(s->event, &s->sigusr1_event_source, SIGUSR1, dispatch_sigusr1, s); + if (r < 0) +@@ -1294,6 +1328,10 @@ static int setup_signals(Server *s) { + if (r < 0) + return r; + ++ r = sd_event_add_signal(s->event, &s->sigrtmin1_event_source, SIGRTMIN+2, dispatch_sigrtmin2, s); ++ if (r < 0) ++ return r; ++ + r = sd_event_source_set_priority(s->sigrtmin1_event_source, SD_EVENT_PRIORITY_NORMAL+15); + if (r < 0) + return r; +@@ -1876,7 +1914,7 @@ int server_init(Server *s) { + + (void) client_context_acquire_default(s); + +- return system_journal_open(s, false); ++ return system_journal_open(s, false, false); + } + + void server_maybe_append_tags(Server *s) { diff --git a/SOURCES/0874-journalctl-add-new-relinquish-and-smart-relinquish-o.patch b/SOURCES/0874-journalctl-add-new-relinquish-and-smart-relinquish-o.patch new file mode 100644 index 0000000..65b8ac3 --- /dev/null +++ b/SOURCES/0874-journalctl-add-new-relinquish-and-smart-relinquish-o.patch @@ -0,0 +1,242 @@ +From fa93a97bdf18906d9517f4183802456986490c89 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Fri, 5 Apr 2019 18:21:02 +0200 +Subject: [PATCH] journalctl: add new --relinquish and --smart-relinquish + options + +The latter is identical to the former, but becomes a NOP if +/var/log/journal is on the same mount as /, and thus during shutdown +unmounting /var is not necessary and hence we can keep logging until the +very end. + +[dtardon: The only divergence from the upstream commit is the impl. of +relinquish_var().] + +(cherry picked from commit c0dfcb318c28d87e1176a8ad87ac7cc4ecc50305) + +Related: #1873540 +--- + src/journal/journalctl.c | 161 +++++++++++++++++++++++++-------------- + 1 file changed, 103 insertions(+), 58 deletions(-) + +diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c +index 228cfe7e49..6928c79a06 100644 +--- a/src/journal/journalctl.c ++++ b/src/journal/journalctl.c +@@ -47,6 +47,7 @@ + #include "log.h" + #include "logs-show.h" + #include "mkdir.h" ++#include "mount-util.h" + #include "pager.h" + #include "parse-util.h" + #include "path-util.h" +@@ -162,6 +163,7 @@ static enum { + ACTION_UPDATE_CATALOG, + ACTION_LIST_BOOTS, + ACTION_FLUSH, ++ ACTION_RELINQUISH_VAR, + ACTION_SYNC, + ACTION_ROTATE, + ACTION_VACUUM, +@@ -358,6 +360,8 @@ static void help(void) { + " --vacuum-time=TIME Remove journal files older than specified time\n" + " --verify Verify journal file consistency\n" + " --sync Synchronize unwritten journal messages to disk\n" ++ " --relinquish-var Stop logging to disk, log to temporary file system\n" ++ " --smart-relinquish-var Similar, but NOP if log directory is on root mount\n" + " --flush Flush all journal data from /run into /var\n" + " --rotate Request immediate rotation of the journal files\n" + " --header Show journal header information\n" +@@ -402,6 +406,8 @@ static int parse_argv(int argc, char *argv[]) { + ARG_UTC, + ARG_SYNC, + ARG_FLUSH, ++ ARG_RELINQUISH_VAR, ++ ARG_SMART_RELINQUISH_VAR, + ARG_ROTATE, + ARG_VACUUM_SIZE, + ARG_VACUUM_FILES, +@@ -411,64 +417,66 @@ static int parse_argv(int argc, char *argv[]) { + }; + + static const struct option options[] = { +- { "help", no_argument, NULL, 'h' }, +- { "version" , no_argument, NULL, ARG_VERSION }, +- { "no-pager", no_argument, NULL, ARG_NO_PAGER }, +- { "pager-end", no_argument, NULL, 'e' }, +- { "follow", no_argument, NULL, 'f' }, +- { "force", no_argument, NULL, ARG_FORCE }, +- { "output", required_argument, NULL, 'o' }, +- { "all", no_argument, NULL, 'a' }, +- { "full", no_argument, NULL, 'l' }, +- { "no-full", no_argument, NULL, ARG_NO_FULL }, +- { "lines", optional_argument, NULL, 'n' }, +- { "no-tail", no_argument, NULL, ARG_NO_TAIL }, +- { "new-id128", no_argument, NULL, ARG_NEW_ID128 }, +- { "quiet", no_argument, NULL, 'q' }, +- { "merge", no_argument, NULL, 'm' }, +- { "this-boot", no_argument, NULL, ARG_THIS_BOOT }, /* deprecated */ +- { "boot", optional_argument, NULL, 'b' }, +- { "list-boots", no_argument, NULL, ARG_LIST_BOOTS }, +- { "dmesg", no_argument, NULL, 'k' }, +- { "system", no_argument, NULL, ARG_SYSTEM }, +- { "user", no_argument, NULL, ARG_USER }, +- { "directory", required_argument, NULL, 'D' }, +- { "file", required_argument, NULL, ARG_FILE }, +- { "root", required_argument, NULL, ARG_ROOT }, +- { "header", no_argument, NULL, ARG_HEADER }, +- { "identifier", required_argument, NULL, 't' }, +- { "priority", required_argument, NULL, 'p' }, +- { "grep", required_argument, NULL, 'g' }, +- { "case-sensitive", optional_argument, NULL, ARG_CASE_SENSITIVE }, +- { "setup-keys", no_argument, NULL, ARG_SETUP_KEYS }, +- { "interval", required_argument, NULL, ARG_INTERVAL }, +- { "verify", no_argument, NULL, ARG_VERIFY }, +- { "verify-key", required_argument, NULL, ARG_VERIFY_KEY }, +- { "disk-usage", no_argument, NULL, ARG_DISK_USAGE }, +- { "cursor", required_argument, NULL, 'c' }, +- { "after-cursor", required_argument, NULL, ARG_AFTER_CURSOR }, +- { "show-cursor", no_argument, NULL, ARG_SHOW_CURSOR }, +- { "since", required_argument, NULL, 'S' }, +- { "until", required_argument, NULL, 'U' }, +- { "unit", required_argument, NULL, 'u' }, +- { "user-unit", required_argument, NULL, ARG_USER_UNIT }, +- { "field", required_argument, NULL, 'F' }, +- { "fields", no_argument, NULL, 'N' }, +- { "catalog", no_argument, NULL, 'x' }, +- { "list-catalog", no_argument, NULL, ARG_LIST_CATALOG }, +- { "dump-catalog", no_argument, NULL, ARG_DUMP_CATALOG }, +- { "update-catalog", no_argument, NULL, ARG_UPDATE_CATALOG }, +- { "reverse", no_argument, NULL, 'r' }, +- { "machine", required_argument, NULL, 'M' }, +- { "utc", no_argument, NULL, ARG_UTC }, +- { "flush", no_argument, NULL, ARG_FLUSH }, +- { "sync", no_argument, NULL, ARG_SYNC }, +- { "rotate", no_argument, NULL, ARG_ROTATE }, +- { "vacuum-size", required_argument, NULL, ARG_VACUUM_SIZE }, +- { "vacuum-files", required_argument, NULL, ARG_VACUUM_FILES }, +- { "vacuum-time", required_argument, NULL, ARG_VACUUM_TIME }, +- { "no-hostname", no_argument, NULL, ARG_NO_HOSTNAME }, +- { "output-fields", required_argument, NULL, ARG_OUTPUT_FIELDS }, ++ { "help", no_argument, NULL, 'h' }, ++ { "version" , no_argument, NULL, ARG_VERSION }, ++ { "no-pager", no_argument, NULL, ARG_NO_PAGER }, ++ { "pager-end", no_argument, NULL, 'e' }, ++ { "follow", no_argument, NULL, 'f' }, ++ { "force", no_argument, NULL, ARG_FORCE }, ++ { "output", required_argument, NULL, 'o' }, ++ { "all", no_argument, NULL, 'a' }, ++ { "full", no_argument, NULL, 'l' }, ++ { "no-full", no_argument, NULL, ARG_NO_FULL }, ++ { "lines", optional_argument, NULL, 'n' }, ++ { "no-tail", no_argument, NULL, ARG_NO_TAIL }, ++ { "new-id128", no_argument, NULL, ARG_NEW_ID128 }, /* deprecated */ ++ { "quiet", no_argument, NULL, 'q' }, ++ { "merge", no_argument, NULL, 'm' }, ++ { "this-boot", no_argument, NULL, ARG_THIS_BOOT }, /* deprecated */ ++ { "boot", optional_argument, NULL, 'b' }, ++ { "list-boots", no_argument, NULL, ARG_LIST_BOOTS }, ++ { "dmesg", no_argument, NULL, 'k' }, ++ { "system", no_argument, NULL, ARG_SYSTEM }, ++ { "user", no_argument, NULL, ARG_USER }, ++ { "directory", required_argument, NULL, 'D' }, ++ { "file", required_argument, NULL, ARG_FILE }, ++ { "root", required_argument, NULL, ARG_ROOT }, ++ { "header", no_argument, NULL, ARG_HEADER }, ++ { "identifier", required_argument, NULL, 't' }, ++ { "priority", required_argument, NULL, 'p' }, ++ { "grep", required_argument, NULL, 'g' }, ++ { "case-sensitive", optional_argument, NULL, ARG_CASE_SENSITIVE }, ++ { "setup-keys", no_argument, NULL, ARG_SETUP_KEYS }, ++ { "interval", required_argument, NULL, ARG_INTERVAL }, ++ { "verify", no_argument, NULL, ARG_VERIFY }, ++ { "verify-key", required_argument, NULL, ARG_VERIFY_KEY }, ++ { "disk-usage", no_argument, NULL, ARG_DISK_USAGE }, ++ { "cursor", required_argument, NULL, 'c' }, ++ { "after-cursor", required_argument, NULL, ARG_AFTER_CURSOR }, ++ { "show-cursor", no_argument, NULL, ARG_SHOW_CURSOR }, ++ { "since", required_argument, NULL, 'S' }, ++ { "until", required_argument, NULL, 'U' }, ++ { "unit", required_argument, NULL, 'u' }, ++ { "user-unit", required_argument, NULL, ARG_USER_UNIT }, ++ { "field", required_argument, NULL, 'F' }, ++ { "fields", no_argument, NULL, 'N' }, ++ { "catalog", no_argument, NULL, 'x' }, ++ { "list-catalog", no_argument, NULL, ARG_LIST_CATALOG }, ++ { "dump-catalog", no_argument, NULL, ARG_DUMP_CATALOG }, ++ { "update-catalog", no_argument, NULL, ARG_UPDATE_CATALOG }, ++ { "reverse", no_argument, NULL, 'r' }, ++ { "machine", required_argument, NULL, 'M' }, ++ { "utc", no_argument, NULL, ARG_UTC }, ++ { "flush", no_argument, NULL, ARG_FLUSH }, ++ { "relinquish-var", no_argument, NULL, ARG_RELINQUISH_VAR }, ++ { "smart-relinquish-var", no_argument, NULL, ARG_SMART_RELINQUISH_VAR }, ++ { "sync", no_argument, NULL, ARG_SYNC }, ++ { "rotate", no_argument, NULL, ARG_ROTATE }, ++ { "vacuum-size", required_argument, NULL, ARG_VACUUM_SIZE }, ++ { "vacuum-files", required_argument, NULL, ARG_VACUUM_FILES }, ++ { "vacuum-time", required_argument, NULL, ARG_VACUUM_TIME }, ++ { "no-hostname", no_argument, NULL, ARG_NO_HOSTNAME }, ++ { "output-fields", required_argument, NULL, ARG_OUTPUT_FIELDS }, + {} + }; + +@@ -893,6 +901,35 @@ static int parse_argv(int argc, char *argv[]) { + arg_action = ACTION_FLUSH; + break; + ++ case ARG_SMART_RELINQUISH_VAR: { ++ int root_mnt_id, log_mnt_id; ++ ++ /* Try to be smart about relinquishing access to /var/log/journal/ during shutdown: ++ * if it's on the same mount as the root file system there's no point in ++ * relinquishing access and we can leave journald write to it until the very last ++ * moment. */ ++ ++ r = path_get_mnt_id("/", &root_mnt_id); ++ if (r < 0) ++ log_debug_errno(r, "Failed to get root mount ID, ignoring: %m"); ++ else { ++ r = path_get_mnt_id("/var/log/journal/", &log_mnt_id); ++ if (r < 0) ++ log_debug_errno(r, "Failed to get journal directory mount ID, ignoring: %m"); ++ else if (root_mnt_id == log_mnt_id) { ++ log_debug("/var/log/journal/ is on root file system, not relinquishing access to /var."); ++ return 0; ++ } else ++ log_debug("/var/log/journal/ is not on the root file system, relinquishing access to it."); ++ } ++ ++ _fallthrough_; ++ } ++ ++ case ARG_RELINQUISH_VAR: ++ arg_action = ACTION_RELINQUISH_VAR; ++ break; ++ + case ARG_ROTATE: + arg_action = ACTION_ROTATE; + break; +@@ -2056,6 +2093,10 @@ static int send_signal_and_wait(int sig, const char *watch_path) { + return 0; + } + ++static int relinquish_var(void) { ++ return send_signal_and_wait(SIGRTMIN+2, "/run/systemd/journal/relinquished"); ++} ++ + static int rotate(void) { + return send_signal_and_wait(SIGUSR2, "/run/systemd/journal/rotated"); + } +@@ -2171,6 +2212,10 @@ int main(int argc, char *argv[]) { + r = flush_to_var(); + goto finish; + ++ case ACTION_RELINQUISH_VAR: ++ r = relinquish_var(); ++ goto finish; ++ + case ACTION_SYNC: + r = sync_journal(); + goto finish; diff --git a/SOURCES/0875-units-automatically-revert-to-run-logging-on-shutdow.patch b/SOURCES/0875-units-automatically-revert-to-run-logging-on-shutdow.patch new file mode 100644 index 0000000..e5b3898 --- /dev/null +++ b/SOURCES/0875-units-automatically-revert-to-run-logging-on-shutdow.patch @@ -0,0 +1,26 @@ +From ce6531045b337c3f793d1d74f1e5641e658805bb Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Fri, 5 Apr 2019 18:22:31 +0200 +Subject: [PATCH] units: automatically revert to /run logging on shutdown if + necessary + +Fixes: #867 +(cherry picked from commit 1e187d2dd52cbb4f0bb30e4d96acf7f72a145b91) + +Resolves: #1873540 +--- + units/systemd-journal-flush.service.in | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/units/systemd-journal-flush.service.in b/units/systemd-journal-flush.service.in +index 439f5f3f76..653dcebe0e 100644 +--- a/units/systemd-journal-flush.service.in ++++ b/units/systemd-journal-flush.service.in +@@ -19,6 +19,7 @@ RequiresMountsFor=/var/log/journal + + [Service] + ExecStart=@rootbindir@/journalctl --flush ++ExecStop=@rootbindir@/journalctl --smart-relinquish-var + Type=oneshot + RemainAfterExit=yes + TimeoutSec=90s diff --git a/SOURCES/0876-pstore-Tool-to-archive-contents-of-pstore.patch b/SOURCES/0876-pstore-Tool-to-archive-contents-of-pstore.patch new file mode 100644 index 0000000..be1c25a --- /dev/null +++ b/SOURCES/0876-pstore-Tool-to-archive-contents-of-pstore.patch @@ -0,0 +1,929 @@ +From 2f75df5cd6dcd56775fec9e89fc79672e702d826 Mon Sep 17 00:00:00 2001 +From: Eric DeVolder +Date: Thu, 16 May 2019 08:59:01 -0500 +Subject: [PATCH] pstore: Tool to archive contents of pstore +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This patch introduces the systemd pstore service which will archive the +contents of the Linux persistent storage filesystem, pstore, to other storage, +thus preserving the existing information contained in the pstore, and clearing +pstore storage for future error events. + +Linux provides a persistent storage file system, pstore[1], that can store +error records when the kernel dies (or reboots or powers-off). These records in +turn can be referenced to debug kernel problems (currently the kernel stuffs +the tail of the dmesg, which also contains a stack backtrace, into pstore). + +The pstore file system supports a variety of backends that map onto persistent +storage, such as the ACPI ERST[2, Section 18.5 Error Serialization] and UEFI +variables[3 Appendix N Common Platform Error Record]. The pstore backends +typically offer a relatively small amount of persistent storage, e.g. 64KiB, +which can quickly fill up and thus prevent subsequent kernel crashes from +recording errors. Thus there is a need to monitor and extract the pstore +contents so that future kernel problems can also record information in the +pstore. + +The pstore service is independent of the kdump service. In cloud environments +specifically, host and guest filesystems are on remote filesystems (eg. iSCSI +or NFS), thus kdump relies [implicitly and/or explicitly] upon proper operation +of networking software *and* hardware *and* infrastructure. Thus it may not be +possible to capture a kernel coredump to a file since writes over the network +may not be possible. + +The pstore backend, on the other hand, is completely local and provides a path +to store error records which will survive a reboot and aid in post-mortem +debugging. + +Usage Notes: +This tool moves files from /sys/fs/pstore into /var/lib/systemd/pstore. + +To enable kernel recording of error records into pstore, one must either pass +crash_kexec_post_notifiers[4] to the kernel command line or enable via 'echo Y + > /sys/module/kernel/parameters/crash_kexec_post_notifiers'. This option +invokes the recording of errors into pstore *before* an attempt to kexec/kdump +on a kernel crash. + +Optionally, to record reboots and shutdowns in the pstore, one can either pass +the printk.always_kmsg_dump[4] to the kernel command line or enable via 'echo Y > +/sys/module/printk/parameters/always_kmsg_dump'. This option enables code on the +shutdown path to record information via pstore. + +This pstore service is a oneshot service. When run, the service invokes +systemd-pstore which is a tool that performs the following: + - reads the pstore.conf configuration file + - collects the lists of files in the pstore (eg. /sys/fs/pstore) + - for certain file types (eg. dmesg) a handler is invoked + - for all other files, the file is moved from pstore + + - In the case of dmesg handler, final processing occurs as such: + - files processed in reverse lexigraphical order to faciliate + reconstruction of original dmesg + - the filename is examined to determine which dmesg it is a part + - the file is appended to the reconstructed dmesg + +For example, the following pstore contents: + + root@vm356:~# ls -al /sys/fs/pstore + total 0 + drwxr-x--- 2 root root 0 May 9 09:50 . + drwxr-xr-x 7 root root 0 May 9 09:50 .. + -r--r--r-- 1 root root 1610 May 9 09:49 dmesg-efi-155741337601001 + -r--r--r-- 1 root root 1778 May 9 09:49 dmesg-efi-155741337602001 + -r--r--r-- 1 root root 1726 May 9 09:49 dmesg-efi-155741337603001 + -r--r--r-- 1 root root 1746 May 9 09:49 dmesg-efi-155741337604001 + -r--r--r-- 1 root root 1686 May 9 09:49 dmesg-efi-155741337605001 + -r--r--r-- 1 root root 1690 May 9 09:49 dmesg-efi-155741337606001 + -r--r--r-- 1 root root 1775 May 9 09:49 dmesg-efi-155741337607001 + -r--r--r-- 1 root root 1811 May 9 09:49 dmesg-efi-155741337608001 + -r--r--r-- 1 root root 1817 May 9 09:49 dmesg-efi-155741337609001 + -r--r--r-- 1 root root 1795 May 9 09:49 dmesg-efi-155741337710001 + -r--r--r-- 1 root root 1770 May 9 09:49 dmesg-efi-155741337711001 + -r--r--r-- 1 root root 1796 May 9 09:49 dmesg-efi-155741337712001 + -r--r--r-- 1 root root 1787 May 9 09:49 dmesg-efi-155741337713001 + -r--r--r-- 1 root root 1808 May 9 09:49 dmesg-efi-155741337714001 + -r--r--r-- 1 root root 1754 May 9 09:49 dmesg-efi-155741337715001 + +results in the following: + + root@vm356:~# ls -al /var/lib/systemd/pstore/155741337/ + total 92 + drwxr-xr-x 2 root root 4096 May 9 09:50 . + drwxr-xr-x 4 root root 40 May 9 09:50 .. + -rw-r--r-- 1 root root 1610 May 9 09:50 dmesg-efi-155741337601001 + -rw-r--r-- 1 root root 1778 May 9 09:50 dmesg-efi-155741337602001 + -rw-r--r-- 1 root root 1726 May 9 09:50 dmesg-efi-155741337603001 + -rw-r--r-- 1 root root 1746 May 9 09:50 dmesg-efi-155741337604001 + -rw-r--r-- 1 root root 1686 May 9 09:50 dmesg-efi-155741337605001 + -rw-r--r-- 1 root root 1690 May 9 09:50 dmesg-efi-155741337606001 + -rw-r--r-- 1 root root 1775 May 9 09:50 dmesg-efi-155741337607001 + -rw-r--r-- 1 root root 1811 May 9 09:50 dmesg-efi-155741337608001 + -rw-r--r-- 1 root root 1817 May 9 09:50 dmesg-efi-155741337609001 + -rw-r--r-- 1 root root 1795 May 9 09:50 dmesg-efi-155741337710001 + -rw-r--r-- 1 root root 1770 May 9 09:50 dmesg-efi-155741337711001 + -rw-r--r-- 1 root root 1796 May 9 09:50 dmesg-efi-155741337712001 + -rw-r--r-- 1 root root 1787 May 9 09:50 dmesg-efi-155741337713001 + -rw-r--r-- 1 root root 1808 May 9 09:50 dmesg-efi-155741337714001 + -rw-r--r-- 1 root root 1754 May 9 09:50 dmesg-efi-155741337715001 + -rw-r--r-- 1 root root 26754 May 9 09:50 dmesg.txt + +where dmesg.txt is reconstructed from the group of related +dmesg-efi-155741337* files. + +Configuration file: +The pstore.conf configuration file has four settings, described below. + - Storage : one of "none", "external", or "journal". With "none", this + tool leaves the contents of pstore untouched. With "external", the + contents of the pstore are moved into the /var/lib/systemd/pstore, + as well as logged into the journal. With "journal", the contents of + the pstore are recorded only in the systemd journal. The default is + "external". + - Unlink : is a boolean. When "true", the default, then files in the + pstore are removed once processed. When "false", processing of the + pstore occurs normally, but the pstore files remain. + +References: +[1] "Persistent storage for a kernel's dying breath", + March 23, 2011. + https://lwn.net/Articles/434821/ + +[2] "Advanced Configuration and Power Interface Specification", + version 6.2, May 2017. + https://www.uefi.org/sites/default/files/resources/ACPI_6_2.pdf + +[3] "Unified Extensible Firmware Interface Specification", + version 2.8, March 2019. + https://uefi.org/sites/default/files/resources/UEFI_Spec_2_8_final.pdf + +[4] "The kernel’s command-line parameters", + https://static.lwn.net/kerneldoc/admin-guide/kernel-parameters.html + +(cherry picked from commit 9b4abc69b201e5d7295e1b0762883659f053e747) + +Resolves: #2158832 +--- + man/pstore.conf.xml | 89 +++++++ + man/rules/meson.build | 2 + + man/systemd-pstore.xml | 99 ++++++++ + meson.build | 20 ++ + meson_options.txt | 2 + + src/pstore/meson.build | 10 + + src/pstore/pstore.c | 395 ++++++++++++++++++++++++++++++++ + src/pstore/pstore.conf | 16 ++ + units/meson.build | 1 + + units/systemd-pstore.service.in | 24 ++ + 10 files changed, 658 insertions(+) + create mode 100644 man/pstore.conf.xml + create mode 100644 man/systemd-pstore.xml + create mode 100644 src/pstore/meson.build + create mode 100644 src/pstore/pstore.c + create mode 100644 src/pstore/pstore.conf + create mode 100644 units/systemd-pstore.service.in + +diff --git a/man/pstore.conf.xml b/man/pstore.conf.xml +new file mode 100644 +index 0000000000..b5cda47d02 +--- /dev/null ++++ b/man/pstore.conf.xml +@@ -0,0 +1,89 @@ ++ ++ ++ ++ ++ ++ ++ pstore.conf ++ systemd ++ ++ ++ ++ pstore.conf ++ 5 ++ ++ ++ ++ pstore.conf ++ pstore.conf.d ++ PStore configuration file ++ ++ ++ ++ ++ /etc/systemd/pstore.conf ++ /etc/systemd/pstore.conf.d/* ++ ++ ++ ++ ++ Description ++ ++ This file configures the behavior of ++ systemd-pstore8, ++ a tool for archiving the contents of the persistent storage filesystem, ++ pstore. ++ ++ ++ ++ ++ ++ ++ Options ++ ++ All options are configured in the ++ [PStore] section: ++ ++ ++ ++ ++ Storage= ++ ++ Controls where to archive (i.e. copy) files from the pstore filesystem. One of none, ++ external, and journal. When ++ none, the tool exits without processing files in the pstore filesystem. ++ When external (the default), files are archived into /var/lib/systemd/pstore/, ++ and logged into the journal. ++ When journal, pstore file contents are logged only in the journal. ++ ++ ++ ++ ++ ++ Unlink= ++ ++ Controls whether or not files are removed from pstore after processing. ++ Takes a boolean value. When true, a pstore file is removed from the pstore once it has been ++ archived (either to disk or into the journal). When false, processing of pstore files occurs ++ normally, but the files remain in the pstore. ++ The default is true in order to maintain the pstore in a nearly empty state, so that the pstore ++ has storage available for the next kernel error event. ++ ++ ++ ++ ++ The defaults for all values are listed as comments in the ++ template /etc/systemd/pstore.conf file that ++ is installed by default. ++ ++ ++ ++ See Also ++ ++ systemd-journald.service8, ++ ++ ++ ++ +diff --git a/man/rules/meson.build b/man/rules/meson.build +index e6c0a99bbd..6295330c5e 100644 +--- a/man/rules/meson.build ++++ b/man/rules/meson.build +@@ -44,6 +44,7 @@ manpages = [ + ['os-release', '5', [], ''], + ['pam_systemd', '8', [], 'HAVE_PAM'], + ['portablectl', '1', [], 'ENABLE_PORTABLED'], ++ ['pstore.conf', '5', ['pstore.conf.d'], 'ENABLE_PSTORE'], + ['resolvectl', '1', ['resolvconf'], 'ENABLE_RESOLVE'], + ['resolved.conf', '5', ['resolved.conf.d'], 'ENABLE_RESOLVE'], + ['runlevel', '8', [], 'ENABLE_UTMP'], +@@ -633,6 +634,7 @@ manpages = [ + ['systemd-nspawn', '1', [], ''], + ['systemd-path', '1', [], ''], + ['systemd-portabled.service', '8', ['systemd-portabled'], 'ENABLE_PORTABLED'], ++ ['systemd-pstore', '8', ['systemd-pstore.service'], 'ENABLE_PSTORE'], + ['systemd-quotacheck.service', + '8', + ['systemd-quotacheck'], +diff --git a/man/systemd-pstore.xml b/man/systemd-pstore.xml +new file mode 100644 +index 0000000000..dd1aa5e83b +--- /dev/null ++++ b/man/systemd-pstore.xml +@@ -0,0 +1,99 @@ ++ ++ ++ ++ ++ ++ ++ ++ systemd-pstore ++ systemd ++ ++ ++ ++ systemd-pstore ++ 8 ++ ++ ++ ++ systemd-pstore ++ systemd-pstore.service ++ Tool to archive contents of the persistent storage filesytem ++ ++ ++ ++ /usr/lib/systemd/systemd-pstore ++ systemd-pstore.service ++ ++ ++ ++ Description ++ systemd-pstore.service is a system service that archives the ++ contents of the Linux persistent storage filesystem, pstore, to other storage, ++ thus preserving the existing information contained in the pstore, and clearing ++ pstore storage for future error events. ++ ++ Linux provides a persistent storage file system, pstore, that can store ++ error records when the kernel dies (or reboots or powers-off). These records in ++ turn can be referenced to debug kernel problems (currently the kernel stuffs ++ the tail of the dmesg, which also contains a stack backtrace, into pstore). ++ ++ The pstore file system supports a variety of backends that map onto persistent ++ storage, such as the ACPI ERST and UEFI variables. The pstore backends ++ typically offer a relatively small amount of persistent storage, e.g. 64KiB, ++ which can quickly fill up and thus prevent subsequent kernel crashes from ++ recording errors. Thus there is a need to monitor and extract the pstore ++ contents so that future kernel problems can also record information in the ++ pstore. ++ ++ The pstore service is independent of the kdump service. In cloud environments ++ specifically, host and guest filesystems are on remote filesystems (eg. iSCSI ++ or NFS), thus kdump relies [implicitly and/or explicitly] upon proper operation ++ of networking software *and* hardware *and* infrastructure. Thus it may not be ++ possible to capture a kernel coredump to a file since writes over the network ++ may not be possible. ++ ++ The pstore backend, on the other hand, is completely local and provides a path ++ to store error records which will survive a reboot and aid in post-mortem ++ debugging. ++ ++ The systemd-pstore executable does the actual work. Upon starting, ++ the pstore.conf is read to obtain options, then the /sys/fs/pstore ++ directory contents are processed according to the options. Pstore files are written to the ++ journal, and optionally saved into /var/lib/systemd/pstore. ++ ++ ++ ++ Configuration ++ ++ The behavior of systemd-pstore is configured through the configuration file ++ /etc/systemd/pstore.conf and corresponding snippets ++ /etc/systemd/pstore.conf.d/*.conf, see ++ pstore.conf5. ++ ++ ++ ++ Disabling pstore processing ++ ++ To disable pstore processing by systemd-pstore, ++ set Storage=none in ++ pstore.conf5. ++ ++ ++ ++ ++ ++ Usage ++ Data stored in the journal can be viewed with ++ journalctl1 ++ as usual. ++ ++ ++ ++ See Also ++ ++ pstore.conf5 ++ ++ ++ +diff --git a/meson.build b/meson.build +index af4cf331da..972a8fb6f7 100644 +--- a/meson.build ++++ b/meson.build +@@ -1224,6 +1224,7 @@ foreach term : ['utmp', + 'environment-d', + 'binfmt', + 'coredump', ++ 'pstore', + 'resolve', + 'logind', + 'hostnamed', +@@ -1439,6 +1440,7 @@ subdir('src/network') + subdir('src/analyze') + subdir('src/journal-remote') + subdir('src/coredump') ++subdir('src/pstore') + subdir('src/hostname') + subdir('src/import') + subdir('src/kernel-install') +@@ -2151,6 +2153,23 @@ if conf.get('ENABLE_COREDUMP') == 1 + public_programs += [exe] + endif + ++if conf.get('ENABLE_PSTORE') == 1 ++ executable('systemd-pstore', ++ systemd_pstore_sources, ++ include_directories : includes, ++ link_with : [libshared], ++ dependencies : [threads, ++ libacl, ++ libdw, ++ libxz, ++ liblz4], ++ install_rpath : rootlibexecdir, ++ install : true, ++ install_dir : rootlibexecdir) ++ ++ public_programs += exe ++endif ++ + if conf.get('ENABLE_BINFMT') == 1 + exe = executable('systemd-binfmt', + 'src/binfmt/binfmt.c', +@@ -3014,6 +3033,7 @@ foreach tuple : [ + ['resolve'], + ['DNS-over-TLS'], + ['coredump'], ++ ['pstore'], + ['polkit'], + ['legacy pkla', install_polkit_pkla], + ['efi'], +diff --git a/meson_options.txt b/meson_options.txt +index 213079ac15..5624304bf4 100644 +--- a/meson_options.txt ++++ b/meson_options.txt +@@ -76,6 +76,8 @@ option('binfmt', type : 'boolean', + description : 'support for custom binary formats') + option('coredump', type : 'boolean', + description : 'install the coredump handler') ++option('pstore', type : 'boolean', ++ description : 'install the pstore archival tool') + option('logind', type : 'boolean', + description : 'install the systemd-logind stack') + option('hostnamed', type : 'boolean', +diff --git a/src/pstore/meson.build b/src/pstore/meson.build +new file mode 100644 +index 0000000000..adbac24b54 +--- /dev/null ++++ b/src/pstore/meson.build +@@ -0,0 +1,10 @@ ++# SPDX-License-Identifier: LGPL-2.1+ ++ ++systemd_pstore_sources = files(''' ++ pstore.c ++'''.split()) ++ ++if conf.get('ENABLE_PSTORE') == 1 ++ install_data('pstore.conf', ++ install_dir : pkgsysconfdir) ++endif +diff --git a/src/pstore/pstore.c b/src/pstore/pstore.c +new file mode 100644 +index 0000000000..f95e016eb6 +--- /dev/null ++++ b/src/pstore/pstore.c +@@ -0,0 +1,395 @@ ++/* SPDX-License-Identifier: LGPL-2.1+ */ ++ ++/* Copyright © 2019 Oracle and/or its affiliates. */ ++ ++/* Generally speaking, the pstore contains a small number of files ++ * that in turn contain a small amount of data. */ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "sd-daemon.h" ++#include "sd-journal.h" ++#include "sd-login.h" ++#include "sd-messages.h" ++ ++#include "acl-util.h" ++#include "alloc-util.h" ++#include "capability-util.h" ++#include "cgroup-util.h" ++#include "compress.h" ++#include "conf-parser.h" ++#include "copy.h" ++#include "dirent-util.h" ++#include "escape.h" ++#include "fd-util.h" ++#include "fileio.h" ++#include "fs-util.h" ++#include "io-util.h" ++#include "journal-importer.h" ++#include "log.h" ++#include "macro.h" ++#include "missing.h" ++#include "mkdir.h" ++#include "parse-util.h" ++#include "process-util.h" ++#include "signal-util.h" ++#include "socket-util.h" ++#include "special.h" ++#include "string-table.h" ++#include "string-util.h" ++#include "strv.h" ++#include "user-util.h" ++#include "util.h" ++ ++/* Command line argument handling */ ++typedef enum PStoreStorage { ++ PSTORE_STORAGE_NONE, ++ PSTORE_STORAGE_EXTERNAL, ++ PSTORE_STORAGE_JOURNAL, ++ _PSTORE_STORAGE_MAX, ++ _PSTORE_STORAGE_INVALID = -1 ++} PStoreStorage; ++ ++static const char* const pstore_storage_table[_PSTORE_STORAGE_MAX] = { ++ [PSTORE_STORAGE_NONE] = "none", ++ [PSTORE_STORAGE_EXTERNAL] = "external", ++ [PSTORE_STORAGE_JOURNAL] = "journal", ++}; ++ ++DEFINE_PRIVATE_STRING_TABLE_LOOKUP(pstore_storage, PStoreStorage); ++static DEFINE_CONFIG_PARSE_ENUM(config_parse_pstore_storage, pstore_storage, PStoreStorage, "Failed to parse storage setting"); ++ ++static PStoreStorage arg_storage = PSTORE_STORAGE_EXTERNAL; ++ ++static bool arg_unlink = true; ++static const char *arg_sourcedir = "/sys/fs/pstore"; ++static const char *arg_archivedir = "/var/lib/systemd/pstore"; ++ ++static int parse_config(void) { ++ static const ConfigTableItem items[] = { ++ { "PStore", "Unlink", config_parse_bool, 0, &arg_unlink }, ++ { "PStore", "Storage", config_parse_pstore_storage, 0, &arg_storage }, ++ {} ++ }; ++ ++ return config_parse_many_nulstr(PKGSYSCONFDIR "/pstore.conf", ++ CONF_PATHS_NULSTR("systemd/pstore.conf.d"), ++ "PStore\0", ++ config_item_table_lookup, items, ++ CONFIG_PARSE_WARN, NULL); ++} ++ ++/* File list handling - PStoreEntry is the struct and ++ * and PStoreEntry is the type that contains all info ++ * about a pstore entry. */ ++typedef struct PStoreEntry { ++ struct dirent dirent; ++ bool is_binary; ++ bool handled; ++ char *content; ++ size_t content_size; ++} PStoreEntry; ++ ++typedef struct PStoreList { ++ PStoreEntry *entries; ++ size_t n_entries; ++ size_t n_entries_allocated; ++} PStoreList; ++ ++static void pstore_entries_reset(PStoreList *list) { ++ for (size_t i = 0; i < list->n_entries; i++) ++ free(list->entries[i].content); ++ free(list->entries); ++ list->n_entries = 0; ++} ++ ++static int compare_pstore_entries(const void *_a, const void *_b) { ++ PStoreEntry *a = (PStoreEntry *)_a, *b = (PStoreEntry *)_b; ++ return strcmp(a->dirent.d_name, b->dirent.d_name); ++} ++ ++static int move_file(PStoreEntry *pe, const char *subdir) { ++ _cleanup_free_ char *ifd_path = NULL; ++ _cleanup_free_ char *ofd_path = NULL; ++ int r = 0; ++ struct iovec iovec[2] = {}; ++ int n_iovec = 0; ++ _cleanup_free_ void *field = NULL; ++ const char *suffix = NULL; ++ size_t field_size; ++ ++ if (pe->handled) ++ return 0; ++ ++ ifd_path = path_join(NULL, arg_sourcedir, pe->dirent.d_name); ++ if (!ifd_path) ++ return log_oom(); ++ ++ ofd_path = path_join(arg_archivedir, subdir, pe->dirent.d_name); ++ if (!ofd_path) ++ return log_oom(); ++ ++ /* Always log to the journal */ ++ suffix = arg_storage == PSTORE_STORAGE_EXTERNAL ? strjoina(" moved to ", ofd_path) : (char *)"."; ++ field = strjoina("MESSAGE=PStore ", pe->dirent.d_name, suffix); ++ iovec[n_iovec++] = IOVEC_MAKE_STRING(field); ++ ++ field_size = strlen("FILE=") + pe->content_size; ++ field = malloc(field_size); ++ if (!field) ++ return log_oom(); ++ memcpy(stpcpy(field, "FILE="), pe->content, pe->content_size); ++ iovec[n_iovec++] = IOVEC_MAKE(field, field_size); ++ ++ r = sd_journal_sendv(iovec, n_iovec); ++ if (r < 0) ++ return log_error_errno(r, "Failed to log pstore entry: %m"); ++ ++ if (arg_storage == PSTORE_STORAGE_EXTERNAL) { ++ /* Move file from pstore to external storage */ ++ r = mkdir_parents(ofd_path, 0755); ++ if (r < 0) ++ return log_error_errno(r, "Failed to create directoy %s: %m", ofd_path); ++ r = copy_file_atomic(ifd_path, ofd_path, 0600, 0, COPY_REPLACE); ++ if (r < 0) ++ return log_error_errno(r, "Failed to copy_file_atomic: %s to %s", ifd_path, ofd_path); ++ } ++ ++ /* If file copied properly, remove it from pstore */ ++ if (arg_unlink) ++ (void) unlink(ifd_path); ++ ++ pe->handled = true; ++ ++ return 0; ++} ++ ++static int write_dmesg(const char *dmesg, size_t size, const char *id) { ++ _cleanup_(unlink_and_freep) char *ofd_path = NULL; ++ _cleanup_free_ char *tmp_path = NULL; ++ _cleanup_close_ int ofd = -1; ++ ssize_t wr; ++ int r; ++ ++ if (isempty(dmesg) || size == 0) ++ return 0; ++ ++ /* log_info("Record ID %s", id); */ ++ ++ ofd_path = path_join(arg_archivedir, id, "dmesg.txt"); ++ if (!ofd_path) ++ return log_oom(); ++ ++ ofd = open_tmpfile_linkable(ofd_path, O_CLOEXEC|O_CREAT|O_TRUNC|O_WRONLY, &tmp_path); ++ if (ofd < 0) ++ return log_error_errno(ofd, "Failed to open temporary file %s: %m", ofd_path); ++ wr = write(ofd, dmesg, size); ++ if (wr < 0) ++ return log_error_errno(errno, "Failed to store dmesg to %s: %m", ofd_path); ++ if (wr != (ssize_t)size) ++ return log_error_errno(-EIO, "Failed to store dmesg to %s. %zu bytes are lost.", ofd_path, size - wr); ++ r = link_tmpfile(ofd, tmp_path, ofd_path); ++ if (r < 0) ++ return log_error_errno(r, "Failed to write temporary file %s: %m", ofd_path); ++ ofd_path = mfree(ofd_path); ++ ++ return 0; ++} ++ ++static void process_dmesg_files(PStoreList *list) { ++ /* Move files, reconstruct dmesg.txt */ ++ PStoreEntry *pe; ++ _cleanup_free_ char *dmesg = NULL; ++ size_t dmesg_size = 0; ++ _cleanup_free_ char *dmesg_id = NULL; ++ ++ /* Handle each dmesg file: files processed in reverse ++ * order so as to properly reconstruct original dmesg */ ++ for (size_t n = list->n_entries; n > 0; n--) { ++ bool move_file_and_continue = false; ++ _cleanup_free_ char *pe_id = NULL; ++ char *p; ++ size_t plen; ++ ++ pe = &list->entries[n-1]; ++ ++ if (pe->handled) ++ continue; ++ if (!startswith(pe->dirent.d_name, "dmesg-")) ++ continue; ++ ++ if (endswith(pe->dirent.d_name, ".enc.z")) /* indicates a problem */ ++ move_file_and_continue = true; ++ p = strrchr(pe->dirent.d_name, '-'); ++ if (!p) ++ move_file_and_continue = true; ++ ++ if (move_file_and_continue) { ++ /* A dmesg file on which we do NO additional processing */ ++ (void) move_file(pe, NULL); ++ continue; ++ } ++ ++ /* See if this file is one of a related group of files ++ * in order to reconstruct dmesg */ ++ ++ /* When dmesg is written into pstore, it is done so in ++ * small chunks, whatever the exchange buffer size is ++ * with the underlying pstore backend (ie. EFI may be ++ * ~2KiB), which means an example pstore with approximately ++ * 64KB of storage may have up to roughly 32 dmesg files ++ * that could be related, depending upon the size of the ++ * original dmesg. ++ * ++ * Here we look at the dmesg filename and try to discern ++ * if files are part of a related group, meaning the same ++ * original dmesg. ++ * ++ * The two known pstore backends are EFI and ERST. These ++ * backends store data in the Common Platform Error ++ * Record, CPER, format. The dmesg- filename contains the ++ * CPER record id, a 64bit number (in decimal notation). ++ * In Linux, the record id is encoded with two digits for ++ * the dmesg part (chunk) number and 3 digits for the ++ * count number. So allowing an additional digit to ++ * compensate for advancing time, this code ignores the ++ * last six digits of the filename in determining the ++ * record id. ++ * ++ * For the EFI backend, the record id encodes an id in the ++ * upper 32 bits, and a timestamp in the lower 32-bits. ++ * So ignoring the least significant 6 digits has proven ++ * to generally identify related dmesg entries. */ ++#define PSTORE_FILENAME_IGNORE 6 ++ ++ /* determine common portion of record id */ ++ ++p; /* move beyond dmesg- */ ++ plen = strlen(p); ++ if (plen > PSTORE_FILENAME_IGNORE) { ++ pe_id = memdup_suffix0(p, plen - PSTORE_FILENAME_IGNORE); ++ if (!pe_id) { ++ log_oom(); ++ return; ++ } ++ } else ++ pe_id = mfree(pe_id); ++ ++ /* Now move file from pstore to archive storage */ ++ move_file(pe, pe_id); ++ ++ /* If the current record id is NOT the same as the ++ * previous record id, then start a new dmesg.txt file */ ++ if (!pe_id || !dmesg_id || !streq(pe_id, dmesg_id)) { ++ /* Encountered a new dmesg group, close out old one, open new one */ ++ if (dmesg) { ++ (void) write_dmesg(dmesg, dmesg_size, dmesg_id); ++ dmesg = mfree(dmesg); ++ dmesg_size = 0; ++ } ++ ++ /* now point dmesg_id to storage of pe_id */ ++ free_and_replace(dmesg_id, pe_id); ++ } ++ ++ /* Reconstruction of dmesg is done as a useful courtesy, do not log errors */ ++ dmesg = realloc(dmesg, dmesg_size + strlen(pe->dirent.d_name) + strlen(":\n") + pe->content_size + 1); ++ if (dmesg) { ++ dmesg_size += sprintf(&dmesg[dmesg_size], "%s:\n", pe->dirent.d_name); ++ if (pe->content) { ++ memcpy(&dmesg[dmesg_size], pe->content, pe->content_size); ++ dmesg_size += pe->content_size; ++ } ++ } ++ ++ pe_id = mfree(pe_id); ++ } ++ if (dmesg) ++ (void) write_dmesg(dmesg, dmesg_size, dmesg_id); ++} ++ ++static int list_files(PStoreList *list, const char *sourcepath) { ++ _cleanup_(closedirp) DIR *dirp = NULL; ++ struct dirent *de; ++ int r = 0; ++ ++ dirp = opendir(sourcepath); ++ if (!dirp) ++ return log_error_errno(errno, "Failed to opendir %s: %m", sourcepath); ++ ++ FOREACH_DIRENT(de, dirp, return log_error_errno(errno, "Failed to iterate through %s: %m", sourcepath)) { ++ _cleanup_free_ char *ifd_path = NULL; ++ ++ ifd_path = path_join(NULL, sourcepath, de->d_name); ++ if (!ifd_path) ++ return log_oom(); ++ ++ _cleanup_free_ char *buf = NULL; ++ size_t buf_size; ++ ++ /* Now read contents of pstore file */ ++ r = read_full_file(ifd_path, &buf, &buf_size); ++ if (r < 0) { ++ log_warning_errno(r, "Failed to read file %s: %m", ifd_path); ++ continue; ++ } ++ ++ if (!GREEDY_REALLOC(list->entries, list->n_entries_allocated, list->n_entries + 1)) ++ return log_oom(); ++ ++ list->entries[list->n_entries++] = (PStoreEntry) { ++ .dirent = *de, ++ .content = TAKE_PTR(buf), ++ .content_size = buf_size, ++ .is_binary = true, ++ .handled = false, ++ }; ++ } ++ ++ return r; ++} ++ ++static int run(int argc, char *argv[]) { ++ _cleanup_(pstore_entries_reset) PStoreList list = {}; ++ int r; ++ ++ log_open(); ++ ++ /* Ignore all parse errors */ ++ (void) parse_config(); ++ ++ log_debug("Selected storage '%s'.", pstore_storage_to_string(arg_storage)); ++ log_debug("Selected Unlink '%d'.", arg_unlink); ++ ++ if (arg_storage == PSTORE_STORAGE_NONE) ++ /* Do nothing, intentionally, leaving pstore untouched */ ++ return 0; ++ ++ /* Obtain list of files in pstore */ ++ r = list_files(&list, arg_sourcedir); ++ if (r < 0) ++ return r; ++ ++ /* Handle each pstore file */ ++ /* Sort files lexigraphically ascending, generally needed by all */ ++ qsort_safe(list.entries, list.n_entries, sizeof(PStoreEntry), compare_pstore_entries); ++ ++ /* Process known file types */ ++ process_dmesg_files(&list); ++ ++ /* Move left over files out of pstore */ ++ for (size_t n = 0; n < list.n_entries; n++) ++ move_file(&list.entries[n], NULL); ++ ++ return 0; ++} ++ ++int main(int argc, char *argv[]) { ++ int r; ++ ++ r = run(argc, argv); ++ return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; ++} +diff --git a/src/pstore/pstore.conf b/src/pstore/pstore.conf +new file mode 100644 +index 0000000000..93a8b6707c +--- /dev/null ++++ b/src/pstore/pstore.conf +@@ -0,0 +1,16 @@ ++# This file is part of systemd. ++# ++# systemd is free software; you can redistribute it and/or modify it ++# under the terms of the GNU Lesser General Public License as published by ++# the Free Software Foundation; either version 2.1 of the License, or ++# (at your option) any later version. ++# ++# Entries in this file show the compile time defaults. ++# You can change settings by editing this file. ++# Defaults can be restored by simply deleting this file. ++# ++# See pstore.conf(5) for details. ++ ++[PStore] ++#Storage=external ++#Unlink=yes +diff --git a/units/meson.build b/units/meson.build +index a74fa95195..e8e64eb30a 100644 +--- a/units/meson.build ++++ b/units/meson.build +@@ -136,6 +136,7 @@ in_units = [ + ['systemd-binfmt.service', 'ENABLE_BINFMT', + 'sysinit.target.wants/'], + ['systemd-coredump@.service', 'ENABLE_COREDUMP'], ++ ['systemd-pstore.service', 'ENABLE_PSTORE'], + ['systemd-firstboot.service', 'ENABLE_FIRSTBOOT', + 'sysinit.target.wants/'], + ['systemd-fsck-root.service', ''], +diff --git a/units/systemd-pstore.service.in b/units/systemd-pstore.service.in +new file mode 100644 +index 0000000000..fec2b1aebf +--- /dev/null ++++ b/units/systemd-pstore.service.in +@@ -0,0 +1,24 @@ ++# SPDX-License-Identifier: LGPL-2.1+ ++# ++# This file is part of systemd. ++# ++# systemd is free software; you can redistribute it and/or modify it ++# under the terms of the GNU Lesser General Public License as published by ++# the Free Software Foundation; either version 2.1 of the License, or ++# (at your option) any later version. ++ ++[Unit] ++Description=Platform Persistent Storage Archival ++Documentation=man:systemd-pstore(8) ++DefaultDependencies=no ++Wants=systemd-remount-fs.service ++After=systemd-remount-fs.service ++ ++[Service] ++Type=oneshot ++ExecStart=@rootlibexecdir@/systemd-pstore ++RemainAfterExit=yes ++StateDirectory=systemd/pstore ++ ++[Install] ++WantedBy=systemd-remount-fs.service diff --git a/SOURCES/0877-meson-drop-redundant-line.patch b/SOURCES/0877-meson-drop-redundant-line.patch new file mode 100644 index 0000000..ed13771 --- /dev/null +++ b/SOURCES/0877-meson-drop-redundant-line.patch @@ -0,0 +1,27 @@ +From c95ba53ab720dfbd7f692e0a87d7f5d4f89ea36b Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Mon, 22 Jul 2019 10:46:53 +0900 +Subject: [PATCH] meson: drop redundant line + +Found by @mattiasb. + +(cherry picked from commit 3f708e7f6909faad307bdb60ed0f8d68e84f6584) + +Related: #2158832 +--- + meson.build | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/meson.build b/meson.build +index 972a8fb6f7..673800a1a7 100644 +--- a/meson.build ++++ b/meson.build +@@ -2166,8 +2166,6 @@ if conf.get('ENABLE_PSTORE') == 1 + install_rpath : rootlibexecdir, + install : true, + install_dir : rootlibexecdir) +- +- public_programs += exe + endif + + if conf.get('ENABLE_BINFMT') == 1 diff --git a/SOURCES/0878-pstore-drop-unnecessary-initializations.patch b/SOURCES/0878-pstore-drop-unnecessary-initializations.patch new file mode 100644 index 0000000..6bc6613 --- /dev/null +++ b/SOURCES/0878-pstore-drop-unnecessary-initializations.patch @@ -0,0 +1,48 @@ +From 7e4b7cc35af0e3b3afbf32fa0fd9961cd01ad9a9 Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Mon, 22 Jul 2019 10:52:12 +0900 +Subject: [PATCH] pstore: drop unnecessary initializations + +(cherry picked from commit 2e4effd129343d22bfed34e94810d3f87c8f0e85) + +Related: #2158832 +--- + src/pstore/pstore.c | 15 ++++++--------- + 1 file changed, 6 insertions(+), 9 deletions(-) + +diff --git a/src/pstore/pstore.c b/src/pstore/pstore.c +index f95e016eb6..e6a342fc50 100644 +--- a/src/pstore/pstore.c ++++ b/src/pstore/pstore.c +@@ -113,14 +113,12 @@ static int compare_pstore_entries(const void *_a, const void *_b) { + } + + static int move_file(PStoreEntry *pe, const char *subdir) { +- _cleanup_free_ char *ifd_path = NULL; +- _cleanup_free_ char *ofd_path = NULL; +- int r = 0; +- struct iovec iovec[2] = {}; +- int n_iovec = 0; ++ _cleanup_free_ char *ifd_path = NULL, *ofd_path = NULL; + _cleanup_free_ void *field = NULL; +- const char *suffix = NULL; ++ struct iovec iovec[2]; ++ const char *suffix; + size_t field_size; ++ int n_iovec = 0, r; + + if (pe->handled) + return 0; +@@ -202,10 +200,9 @@ static int write_dmesg(const char *dmesg, size_t size, const char *id) { + + static void process_dmesg_files(PStoreList *list) { + /* Move files, reconstruct dmesg.txt */ +- PStoreEntry *pe; +- _cleanup_free_ char *dmesg = NULL; ++ _cleanup_free_ char *dmesg = NULL, *dmesg_id = NULL; + size_t dmesg_size = 0; +- _cleanup_free_ char *dmesg_id = NULL; ++ PStoreEntry *pe; + + /* Handle each dmesg file: files processed in reverse + * order so as to properly reconstruct original dmesg */ diff --git a/SOURCES/0879-pstopre-fix-return-value-of-list_files.patch b/SOURCES/0879-pstopre-fix-return-value-of-list_files.patch new file mode 100644 index 0000000..72e8698 --- /dev/null +++ b/SOURCES/0879-pstopre-fix-return-value-of-list_files.patch @@ -0,0 +1,46 @@ +From a0485b96118d3d2ac439f510e404ffb3db03e23f Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Mon, 22 Jul 2019 10:55:10 +0900 +Subject: [PATCH] pstopre: fix return value of list_files() + +Previously, the return value of the last read_full_file() is returned. +This makes the error in read_full_file() is always ignored. + +(cherry picked from commit 337874a45fff46a80e4974c681a5e651f3a0fac9) + +Related: #2158832 +--- + src/pstore/pstore.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/src/pstore/pstore.c b/src/pstore/pstore.c +index e6a342fc50..2fbef48543 100644 +--- a/src/pstore/pstore.c ++++ b/src/pstore/pstore.c +@@ -311,7 +311,7 @@ static void process_dmesg_files(PStoreList *list) { + static int list_files(PStoreList *list, const char *sourcepath) { + _cleanup_(closedirp) DIR *dirp = NULL; + struct dirent *de; +- int r = 0; ++ int r; + + dirp = opendir(sourcepath); + if (!dirp) +@@ -330,7 +330,7 @@ static int list_files(PStoreList *list, const char *sourcepath) { + /* Now read contents of pstore file */ + r = read_full_file(ifd_path, &buf, &buf_size); + if (r < 0) { +- log_warning_errno(r, "Failed to read file %s: %m", ifd_path); ++ log_warning_errno(r, "Failed to read file %s, skipping: %m", ifd_path); + continue; + } + +@@ -346,7 +346,7 @@ static int list_files(PStoreList *list, const char *sourcepath) { + }; + } + +- return r; ++ return 0; + } + + static int run(int argc, char *argv[]) { diff --git a/SOURCES/0880-pstore-remove-temporary-file-on-failure.patch b/SOURCES/0880-pstore-remove-temporary-file-on-failure.patch new file mode 100644 index 0000000..33bf5a0 --- /dev/null +++ b/SOURCES/0880-pstore-remove-temporary-file-on-failure.patch @@ -0,0 +1,36 @@ +From 58000dc7dd93ff6e8357de64154b0849d3c17c5d Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Mon, 22 Jul 2019 11:01:43 +0900 +Subject: [PATCH] pstore: remove temporary file on failure + +(cherry picked from commit 03c5f6cc02648eeff3179b2b762d46b9e1889bb1) + +Related: #2158832 +--- + src/pstore/pstore.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/src/pstore/pstore.c b/src/pstore/pstore.c +index 2fbef48543..ce8080ceed 100644 +--- a/src/pstore/pstore.c ++++ b/src/pstore/pstore.c +@@ -167,8 +167,8 @@ static int move_file(PStoreEntry *pe, const char *subdir) { + } + + static int write_dmesg(const char *dmesg, size_t size, const char *id) { +- _cleanup_(unlink_and_freep) char *ofd_path = NULL; +- _cleanup_free_ char *tmp_path = NULL; ++ _cleanup_(unlink_and_freep) char *tmp_path = NULL; ++ _cleanup_free_ char *ofd_path = NULL; + _cleanup_close_ int ofd = -1; + ssize_t wr; + int r; +@@ -193,7 +193,7 @@ static int write_dmesg(const char *dmesg, size_t size, const char *id) { + r = link_tmpfile(ofd, tmp_path, ofd_path); + if (r < 0) + return log_error_errno(r, "Failed to write temporary file %s: %m", ofd_path); +- ofd_path = mfree(ofd_path); ++ tmp_path = mfree(tmp_path); + + return 0; + } diff --git a/SOURCES/0881-pstore-do-not-add-FILE-journal-entry-if-content_size.patch b/SOURCES/0881-pstore-do-not-add-FILE-journal-entry-if-content_size.patch new file mode 100644 index 0000000..400dae5 --- /dev/null +++ b/SOURCES/0881-pstore-do-not-add-FILE-journal-entry-if-content_size.patch @@ -0,0 +1,57 @@ +From 5006e4bd9aecea40ca3d907adc692c4c8001a6c1 Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Mon, 22 Jul 2019 11:08:06 +0900 +Subject: [PATCH] pstore: do not add FILE= journal entry if content_size == 0 + +(cherry picked from commit 6bf18debddbe1b231f783617e054cc194bb36d1e) + +Related: #2158832 +--- + src/pstore/pstore.c | 25 ++++++++++++++----------- + 1 file changed, 14 insertions(+), 11 deletions(-) + +diff --git a/src/pstore/pstore.c b/src/pstore/pstore.c +index ce8080ceed..eb251d61c8 100644 +--- a/src/pstore/pstore.c ++++ b/src/pstore/pstore.c +@@ -114,10 +114,8 @@ static int compare_pstore_entries(const void *_a, const void *_b) { + + static int move_file(PStoreEntry *pe, const char *subdir) { + _cleanup_free_ char *ifd_path = NULL, *ofd_path = NULL; +- _cleanup_free_ void *field = NULL; ++ const char *suffix, *message; + struct iovec iovec[2]; +- const char *suffix; +- size_t field_size; + int n_iovec = 0, r; + + if (pe->handled) +@@ -133,15 +131,20 @@ static int move_file(PStoreEntry *pe, const char *subdir) { + + /* Always log to the journal */ + suffix = arg_storage == PSTORE_STORAGE_EXTERNAL ? strjoina(" moved to ", ofd_path) : (char *)"."; +- field = strjoina("MESSAGE=PStore ", pe->dirent.d_name, suffix); +- iovec[n_iovec++] = IOVEC_MAKE_STRING(field); ++ message = strjoina("MESSAGE=PStore ", pe->dirent.d_name, suffix); ++ iovec[n_iovec++] = IOVEC_MAKE_STRING(message); + +- field_size = strlen("FILE=") + pe->content_size; +- field = malloc(field_size); +- if (!field) +- return log_oom(); +- memcpy(stpcpy(field, "FILE="), pe->content, pe->content_size); +- iovec[n_iovec++] = IOVEC_MAKE(field, field_size); ++ if (pe->content_size > 0) { ++ _cleanup_free_ void *field = NULL; ++ size_t field_size; ++ ++ field_size = strlen("FILE=") + pe->content_size; ++ field = malloc(field_size); ++ if (!field) ++ return log_oom(); ++ memcpy(stpcpy(field, "FILE="), pe->content, pe->content_size); ++ iovec[n_iovec++] = IOVEC_MAKE(field, field_size); ++ } + + r = sd_journal_sendv(iovec, n_iovec); + if (r < 0) diff --git a/SOURCES/0882-pstore-run-only-when-sys-fs-pstore-is-not-empty.patch b/SOURCES/0882-pstore-run-only-when-sys-fs-pstore-is-not-empty.patch new file mode 100644 index 0000000..ea535a5 --- /dev/null +++ b/SOURCES/0882-pstore-run-only-when-sys-fs-pstore-is-not-empty.patch @@ -0,0 +1,24 @@ +From a7247899f156761934bcb4b380861b3d3ec5449f Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Mon, 22 Jul 2019 14:09:12 +0900 +Subject: [PATCH] pstore: run only when /sys/fs/pstore is not empty + +(cherry picked from commit 6d4f213b1f6afb2901f0d97cec0e28e20809b713) + +Related: #2158832 +--- + units/systemd-pstore.service.in | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/units/systemd-pstore.service.in b/units/systemd-pstore.service.in +index fec2b1aebf..dde21bc33e 100644 +--- a/units/systemd-pstore.service.in ++++ b/units/systemd-pstore.service.in +@@ -10,6 +10,7 @@ + [Unit] + Description=Platform Persistent Storage Archival + Documentation=man:systemd-pstore(8) ++ConditionDirectoryNotEmpty=/sys/fs/pstore + DefaultDependencies=no + Wants=systemd-remount-fs.service + After=systemd-remount-fs.service diff --git a/SOURCES/0883-pstore-fix-use-after-free.patch b/SOURCES/0883-pstore-fix-use-after-free.patch new file mode 100644 index 0000000..3546fb3 --- /dev/null +++ b/SOURCES/0883-pstore-fix-use-after-free.patch @@ -0,0 +1,34 @@ +From 7f5bfbd5485e1cb779d7568cabb5783651fd9da3 Mon Sep 17 00:00:00 2001 +From: Michael Olbrich +Date: Fri, 6 Sep 2019 15:04:01 +0200 +Subject: [PATCH] pstore: fix use after free + +The memory is still needed in the sd_journal_sendv() after the 'if' block. + +(cherry picked from commit 1e19f5ac0d680a63eccae7ef1fc6ce225dca0bbf) + +Related: #2158832 +--- + src/pstore/pstore.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/pstore/pstore.c b/src/pstore/pstore.c +index eb251d61c8..cafb1804c6 100644 +--- a/src/pstore/pstore.c ++++ b/src/pstore/pstore.c +@@ -114,6 +114,7 @@ static int compare_pstore_entries(const void *_a, const void *_b) { + + static int move_file(PStoreEntry *pe, const char *subdir) { + _cleanup_free_ char *ifd_path = NULL, *ofd_path = NULL; ++ _cleanup_free_ void *field = NULL; + const char *suffix, *message; + struct iovec iovec[2]; + int n_iovec = 0, r; +@@ -135,7 +136,6 @@ static int move_file(PStoreEntry *pe, const char *subdir) { + iovec[n_iovec++] = IOVEC_MAKE_STRING(message); + + if (pe->content_size > 0) { +- _cleanup_free_ void *field = NULL; + size_t field_size; + + field_size = strlen("FILE=") + pe->content_size; diff --git a/SOURCES/0884-pstore-refuse-to-run-if-arguments-are-specified.patch b/SOURCES/0884-pstore-refuse-to-run-if-arguments-are-specified.patch new file mode 100644 index 0000000..de27c4c --- /dev/null +++ b/SOURCES/0884-pstore-refuse-to-run-if-arguments-are-specified.patch @@ -0,0 +1,29 @@ +From a35a90322f8587f650aeb72bfbe1ebcc93e503aa Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Mon, 22 Jul 2019 10:43:19 +0200 +Subject: [PATCH] pstore: refuse to run if arguments are specified + +(This is why the --help chech passed.) + +(cherry picked from commit 22d6bea8820612e6a1483d8b6cfd820f1417ae6b) + +Related: #2158832 +--- + src/pstore/pstore.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/src/pstore/pstore.c b/src/pstore/pstore.c +index cafb1804c6..b0b21dedd4 100644 +--- a/src/pstore/pstore.c ++++ b/src/pstore/pstore.c +@@ -358,6 +358,10 @@ static int run(int argc, char *argv[]) { + + log_open(); + ++ if (argc > 1) ++ return log_error_errno(-EINVAL, ++ "This program takes no arguments."); ++ + /* Ignore all parse errors */ + (void) parse_config(); + diff --git a/SOURCES/0885-pstore-allow-specifying-src-and-dst-dirs-are-argumen.patch b/SOURCES/0885-pstore-allow-specifying-src-and-dst-dirs-are-argumen.patch new file mode 100644 index 0000000..00779b3 --- /dev/null +++ b/SOURCES/0885-pstore-allow-specifying-src-and-dst-dirs-are-argumen.patch @@ -0,0 +1,44 @@ +From 76fe0974f62f0a2beb2d3d8e224e80a57c0ebd09 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Fri, 4 Oct 2019 16:14:47 +0200 +Subject: [PATCH] pstore: allow specifying src and dst dirs are arguments + +This makes it much easier to debug the program as a normal user, since we +don't need to set up fake input under /sys/fs/pstore/. + +Also, let's make the debug output a bit nicer. + +(cherry picked from commit e05a72d7587ff916a983588f8393af624d330dd0) + +Related: #2158832 +--- + src/pstore/pstore.c | 11 +++++++---- + 1 file changed, 7 insertions(+), 4 deletions(-) + +diff --git a/src/pstore/pstore.c b/src/pstore/pstore.c +index b0b21dedd4..7353e83a81 100644 +--- a/src/pstore/pstore.c ++++ b/src/pstore/pstore.c +@@ -358,15 +358,18 @@ static int run(int argc, char *argv[]) { + + log_open(); + +- if (argc > 1) ++ if (argc == 3) { ++ arg_sourcedir = argv[1]; ++ arg_archivedir = argv[2]; ++ } else if (argc > 1) + return log_error_errno(-EINVAL, +- "This program takes no arguments."); ++ "This program takes zero or two arguments."); + + /* Ignore all parse errors */ + (void) parse_config(); + +- log_debug("Selected storage '%s'.", pstore_storage_to_string(arg_storage)); +- log_debug("Selected Unlink '%d'.", arg_unlink); ++ log_debug("Selected storage: %s.", pstore_storage_to_string(arg_storage)); ++ log_debug("Selected unlink: %s.", yes_no(arg_unlink)); + + if (arg_storage == PSTORE_STORAGE_NONE) + /* Do nothing, intentionally, leaving pstore untouched */ diff --git a/SOURCES/0886-pstore-rework-memory-handling-for-dmesg.patch b/SOURCES/0886-pstore-rework-memory-handling-for-dmesg.patch new file mode 100644 index 0000000..8c0adda --- /dev/null +++ b/SOURCES/0886-pstore-rework-memory-handling-for-dmesg.patch @@ -0,0 +1,133 @@ +From a2ba34a79de3748f51d57541c54dbe22e1d03a9e Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Fri, 4 Oct 2019 16:17:27 +0200 +Subject: [PATCH] pstore: rework memory handling for dmesg + +Semmle Security Reports report: +> The problem occurs on the way realloc is being used. When a size +> bigger than the chunk that wants to be reallocated is passed, realloc +> try to malloc a bigger size, however in the case that malloc fails +> (for example, by forcing a big allocation) realloc will return NULL. +> +> According to the man page: +> "The realloc() function returns a pointer to the newly allocated +> memory, which is suitably aligned for any built-in type and may be +> different from ptr, or NULL if the request fails. If size was +> equal to 0, either NULL or a pointer suitable to be passed to free() +> is returned. If realloc() fails, the original block is left +> untouched; it is not freed or moved." +> +> The problem occurs when the memory ptr passed to the first argument of +> realloc is the same as the one used for the result, for example in +> this case: +> +> dmesg = realloc(dmesg, dmesg_size + strlen(pe->dirent.d_name) + +> strlen(":\n") + pe->content_size + 1); +> +> https://lgtm.com/projects/g/systemd/systemd/snapshot/f8bcb81955f9e93a4787627e28f43fffb2a84836/files/src/pstore/pstore.c?sort=name&dir=A +> SC&mode=heatmap#L300 +> +> If the malloc inside that realloc fails, then the original memory +> chunk will never be free but since realloc will return NULL, the +> pointer to that memory chunk will be lost and a memory leak will +> occur. +> +> In case you are curious, this is the query we used to find this problem: +> https://lgtm.com/query/8650323308193591473/ + +Let's use a more standard pattern: allocate memory using greedy_realloc, and +instead of freeing it when we wrote out a chunk, let's just move the cursor +back to the beginning and reuse the memory we allocated previously. + +If we fail to allocate the memory for dmesg contents, don't write the dmesg +entry, but let's still process the files to move them out of pstore. + +(cherry picked from commit 8198c3e42b0614b6bd1db6f38813b842c8577304) + +Related: #2158832 +--- + src/pstore/pstore.c | 43 ++++++++++++++++++++++++++----------------- + 1 file changed, 26 insertions(+), 17 deletions(-) + +diff --git a/src/pstore/pstore.c b/src/pstore/pstore.c +index 7353e83a81..d70e142b4d 100644 +--- a/src/pstore/pstore.c ++++ b/src/pstore/pstore.c +@@ -176,9 +176,11 @@ static int write_dmesg(const char *dmesg, size_t size, const char *id) { + ssize_t wr; + int r; + +- if (isempty(dmesg) || size == 0) ++ if (size == 0) + return 0; + ++ assert(dmesg); ++ + /* log_info("Record ID %s", id); */ + + ofd_path = path_join(arg_archivedir, id, "dmesg.txt"); +@@ -204,7 +206,8 @@ static int write_dmesg(const char *dmesg, size_t size, const char *id) { + static void process_dmesg_files(PStoreList *list) { + /* Move files, reconstruct dmesg.txt */ + _cleanup_free_ char *dmesg = NULL, *dmesg_id = NULL; +- size_t dmesg_size = 0; ++ size_t dmesg_size = 0, dmesg_allocated = 0; ++ bool dmesg_bad = false; + PStoreEntry *pe; + + /* Handle each dmesg file: files processed in reverse +@@ -281,33 +284,39 @@ static void process_dmesg_files(PStoreList *list) { + /* Now move file from pstore to archive storage */ + move_file(pe, pe_id); + ++ if (dmesg_bad) ++ continue; ++ + /* If the current record id is NOT the same as the + * previous record id, then start a new dmesg.txt file */ +- if (!pe_id || !dmesg_id || !streq(pe_id, dmesg_id)) { ++ if (!streq_ptr(pe_id, dmesg_id)) { + /* Encountered a new dmesg group, close out old one, open new one */ +- if (dmesg) { +- (void) write_dmesg(dmesg, dmesg_size, dmesg_id); +- dmesg = mfree(dmesg); +- dmesg_size = 0; +- } ++ (void) write_dmesg(dmesg, dmesg_size, dmesg_id); ++ dmesg_size = 0; + + /* now point dmesg_id to storage of pe_id */ + free_and_replace(dmesg_id, pe_id); + } + +- /* Reconstruction of dmesg is done as a useful courtesy, do not log errors */ +- dmesg = realloc(dmesg, dmesg_size + strlen(pe->dirent.d_name) + strlen(":\n") + pe->content_size + 1); +- if (dmesg) { +- dmesg_size += sprintf(&dmesg[dmesg_size], "%s:\n", pe->dirent.d_name); +- if (pe->content) { +- memcpy(&dmesg[dmesg_size], pe->content, pe->content_size); +- dmesg_size += pe->content_size; +- } ++ /* Reconstruction of dmesg is done as a useful courtesy: do not fail, but don't write garbled ++ * output either. */ ++ size_t needed = strlen(pe->dirent.d_name) + strlen(":\n") + pe->content_size + 1; ++ if (!GREEDY_REALLOC(dmesg, dmesg_allocated, dmesg_size + needed)) { ++ log_warning_errno(ENOMEM, "Failed to write dmesg file: %m"); ++ dmesg_bad = true; ++ continue; ++ } ++ ++ dmesg_size += sprintf(dmesg + dmesg_size, "%s:\n", pe->dirent.d_name); ++ if (pe->content) { ++ memcpy(dmesg + dmesg_size, pe->content, pe->content_size); ++ dmesg_size += pe->content_size; + } + + pe_id = mfree(pe_id); + } +- if (dmesg) ++ ++ if (!dmesg_bad) + (void) write_dmesg(dmesg, dmesg_size, dmesg_id); + } + diff --git a/SOURCES/0887-pstore-fixes-for-dmesg.txt-reconstruction.patch b/SOURCES/0887-pstore-fixes-for-dmesg.txt-reconstruction.patch new file mode 100644 index 0000000..cbfb074 --- /dev/null +++ b/SOURCES/0887-pstore-fixes-for-dmesg.txt-reconstruction.patch @@ -0,0 +1,504 @@ +From 5ac15c7dc49476e7cd7cc3a4b507282c9f78d528 Mon Sep 17 00:00:00 2001 +From: Eric DeVolder +Date: Mon, 21 Nov 2022 11:27:27 -0500 +Subject: [PATCH] pstore: fixes for dmesg.txt reconstruction + +This patch fixes problems with the re-assembly of the dmesg +from the records stored in pstore. + +The current code simply ignores the last 6 characters of the +file name to form a base record id, which then groups any +pstore files with this base id into the reconstructed dmesg.txt. +This approach fails when the following oops generated the +following in pstore: + + -rw-------. 1 root root 1808 Oct 27 22:07 dmesg-efi-166692286101001 + -rw-------. 1 root root 1341 Oct 27 22:07 dmesg-efi-166692286101002 + -rw-------. 1 root root 1812 Oct 27 22:07 dmesg-efi-166692286102001 + -rw-------. 1 root root 1820 Oct 27 22:07 dmesg-efi-166692286102002 + -rw-------. 1 root root 1807 Oct 27 22:07 dmesg-efi-166692286103001 + -rw-------. 1 root root 1791 Oct 27 22:07 dmesg-efi-166692286103002 + -rw-------. 1 root root 1773 Oct 27 22:07 dmesg-efi-166692286104001 + -rw-------. 1 root root 1801 Oct 27 22:07 dmesg-efi-166692286104002 + -rw-------. 1 root root 1821 Oct 27 22:07 dmesg-efi-166692286105001 + -rw-------. 1 root root 1809 Oct 27 22:07 dmesg-efi-166692286105002 + -rw-------. 1 root root 1804 Oct 27 22:07 dmesg-efi-166692286106001 + -rw-------. 1 root root 1817 Oct 27 22:07 dmesg-efi-166692286106002 + -rw-------. 1 root root 1792 Oct 27 22:07 dmesg-efi-166692286107001 + -rw-------. 1 root root 1810 Oct 27 22:07 dmesg-efi-166692286107002 + -rw-------. 1 root root 1717 Oct 27 22:07 dmesg-efi-166692286108001 + -rw-------. 1 root root 1808 Oct 27 22:07 dmesg-efi-166692286108002 + -rw-------. 1 root root 1764 Oct 27 22:07 dmesg-efi-166692286109001 + -rw-------. 1 root root 1765 Oct 27 22:07 dmesg-efi-166692286109002 + -rw-------. 1 root root 1796 Oct 27 22:07 dmesg-efi-166692286110001 + -rw-------. 1 root root 1816 Oct 27 22:07 dmesg-efi-166692286110002 + -rw-------. 1 root root 1793 Oct 27 22:07 dmesg-efi-166692286111001 + -rw-------. 1 root root 1751 Oct 27 22:07 dmesg-efi-166692286111002 + -rw-------. 1 root root 1813 Oct 27 22:07 dmesg-efi-166692286112001 + -rw-------. 1 root root 1786 Oct 27 22:07 dmesg-efi-166692286112002 + -rw-------. 1 root root 1754 Oct 27 22:07 dmesg-efi-166692286113001 + -rw-------. 1 root root 1752 Oct 27 22:07 dmesg-efi-166692286113002 + -rw-------. 1 root root 1803 Oct 27 22:07 dmesg-efi-166692286114001 + -rw-------. 1 root root 1759 Oct 27 22:07 dmesg-efi-166692286114002 + -rw-------. 1 root root 1805 Oct 27 22:07 dmesg-efi-166692286115001 + -rw-------. 1 root root 1787 Oct 27 22:07 dmesg-efi-166692286115002 + -rw-------. 1 root root 1815 Oct 27 22:07 dmesg-efi-166692286116001 + -rw-------. 1 root root 1771 Oct 27 22:07 dmesg-efi-166692286116002 + -rw-------. 1 root root 1816 Oct 27 22:07 dmesg-efi-166692286117002 + -rw-------. 1 root root 1388 Oct 27 22:07 dmesg-efi-166692286701003 + -rw-------. 1 root root 1824 Oct 27 22:07 dmesg-efi-166692286702003 + -rw-------. 1 root root 1795 Oct 27 22:07 dmesg-efi-166692286703003 + -rw-------. 1 root root 1805 Oct 27 22:07 dmesg-efi-166692286704003 + -rw-------. 1 root root 1813 Oct 27 22:07 dmesg-efi-166692286705003 + -rw-------. 1 root root 1821 Oct 27 22:07 dmesg-efi-166692286706003 + -rw-------. 1 root root 1814 Oct 27 22:07 dmesg-efi-166692286707003 + -rw-------. 1 root root 1812 Oct 27 22:07 dmesg-efi-166692286708003 + -rw-------. 1 root root 1769 Oct 27 22:07 dmesg-efi-166692286709003 + -rw-------. 1 root root 1820 Oct 27 22:07 dmesg-efi-166692286710003 + -rw-------. 1 root root 1755 Oct 27 22:07 dmesg-efi-166692286711003 + -rw-------. 1 root root 1790 Oct 27 22:07 dmesg-efi-166692286712003 + -rw-------. 1 root root 1756 Oct 27 22:07 dmesg-efi-166692286713003 + -rw-------. 1 root root 1763 Oct 27 22:07 dmesg-efi-166692286714003 + -rw-------. 1 root root 1791 Oct 27 22:07 dmesg-efi-166692286715003 + -rw-------. 1 root root 1775 Oct 27 22:07 dmesg-efi-166692286716003 + -rw-------. 1 root root 1820 Oct 27 22:07 dmesg-efi-166692286717003 + +The "reconstructed" dmesg.txt that resulted from the above contained +the following (ignoring actual contents, just providing the Part info): + + Emergency#3 Part17 + Emergency#3 Part16 + Emergency#3 Part15 + Emergency#3 Part14 + Emergency#3 Part13 + Emergency#3 Part12 + Emergency#3 Part11 + Emergency#3 Part10 + Emergency#3 Part9 + Emergency#3 Part8 + Emergency#3 Part7 + Emergency#3 Part6 + Emergency#3 Part5 + Emergency#3 Part4 + Emergency#3 Part3 + Emergency#3 Part2 + Emergency#3 Part1 + Panic#2 Part17 + Panic#2 Part16 + Oops#1 Part16 + Panic#2 Part15 + Oops#1 Part15 + Panic#2 Part14 + Oops#1 Part14 + Panic#2 Part13 + Oops#1 Part13 + Panic#2 Part12 + Oops#1 Part12 + Panic#2 Part11 + Oops#1 Part11 + Panic#2 Part10 + Oops#1 Part10 + Panic#2 Part9 + Oops#1 Part9 + Panic#2 Part8 + Oops#1 Part8 + Panic#2 Part7 + Oops#1 Part7 + Panic#2 Part6 + Oops#1 Part6 + Panic#2 Part5 + Oops#1 Part5 + Panic#2 Part4 + Oops#1 Part4 + Panic#2 Part3 + Oops#1 Part3 + Panic#2 Part2 + Oops#1 Part2 + Panic#2 Part1 + Oops#1 Part1 + +The above is a interleaved mess of three dmesg dumps. + +This patch fixes the above problems, and simplifies the dmesg +reconstruction process. The code now distinguishes between +records on EFI vs ERST, which have differently formatted +record identifiers. Using knowledge of the format of the +record ids allows vastly improved reconstruction process. + +With this change in place, the above pstore records now +result in the following: + + # ls -alR /var/lib/systemd/pstore + 1666922861: + total 8 + drwxr-xr-x. 4 root root 28 Nov 18 14:58 . + drwxr-xr-x. 7 root root 144 Nov 18 14:58 .. + drwxr-xr-x. 2 root root 4096 Nov 18 14:58 001 + drwxr-xr-x. 2 root root 4096 Nov 18 14:58 002 + + 1666922861/001: + total 100 + drwxr-xr-x. 2 root root 4096 Nov 18 14:58 . + drwxr-xr-x. 4 root root 28 Nov 18 14:58 .. + -rw-------. 1 root root 1808 Oct 27 22:07 dmesg-efi-166692286101001 + -rw-------. 1 root root 1812 Oct 27 22:07 dmesg-efi-166692286102001 + -rw-------. 1 root root 1807 Oct 27 22:07 dmesg-efi-166692286103001 + -rw-------. 1 root root 1773 Oct 27 22:07 dmesg-efi-166692286104001 + -rw-------. 1 root root 1821 Oct 27 22:07 dmesg-efi-166692286105001 + -rw-------. 1 root root 1804 Oct 27 22:07 dmesg-efi-166692286106001 + -rw-------. 1 root root 1792 Oct 27 22:07 dmesg-efi-166692286107001 + -rw-------. 1 root root 1717 Oct 27 22:07 dmesg-efi-166692286108001 + -rw-------. 1 root root 1764 Oct 27 22:07 dmesg-efi-166692286109001 + -rw-------. 1 root root 1796 Oct 27 22:07 dmesg-efi-166692286110001 + -rw-------. 1 root root 1793 Oct 27 22:07 dmesg-efi-166692286111001 + -rw-------. 1 root root 1813 Oct 27 22:07 dmesg-efi-166692286112001 + -rw-------. 1 root root 1754 Oct 27 22:07 dmesg-efi-166692286113001 + -rw-------. 1 root root 1803 Oct 27 22:07 dmesg-efi-166692286114001 + -rw-------. 1 root root 1805 Oct 27 22:07 dmesg-efi-166692286115001 + -rw-------. 1 root root 1815 Oct 27 22:07 dmesg-efi-166692286116001 + -rw-r-----. 1 root root 28677 Nov 18 14:58 dmesg.txt + + 1666922861/002: + total 104 + drwxr-xr-x. 2 root root 4096 Nov 18 14:58 . + drwxr-xr-x. 4 root root 28 Nov 18 14:58 .. + -rw-------. 1 root root 1341 Oct 27 22:07 dmesg-efi-166692286101002 + -rw-------. 1 root root 1820 Oct 27 22:07 dmesg-efi-166692286102002 + -rw-------. 1 root root 1791 Oct 27 22:07 dmesg-efi-166692286103002 + -rw-------. 1 root root 1801 Oct 27 22:07 dmesg-efi-166692286104002 + -rw-------. 1 root root 1809 Oct 27 22:07 dmesg-efi-166692286105002 + -rw-------. 1 root root 1817 Oct 27 22:07 dmesg-efi-166692286106002 + -rw-------. 1 root root 1810 Oct 27 22:07 dmesg-efi-166692286107002 + -rw-------. 1 root root 1808 Oct 27 22:07 dmesg-efi-166692286108002 + -rw-------. 1 root root 1765 Oct 27 22:07 dmesg-efi-166692286109002 + -rw-------. 1 root root 1816 Oct 27 22:07 dmesg-efi-166692286110002 + -rw-------. 1 root root 1751 Oct 27 22:07 dmesg-efi-166692286111002 + -rw-------. 1 root root 1786 Oct 27 22:07 dmesg-efi-166692286112002 + -rw-------. 1 root root 1752 Oct 27 22:07 dmesg-efi-166692286113002 + -rw-------. 1 root root 1759 Oct 27 22:07 dmesg-efi-166692286114002 + -rw-------. 1 root root 1787 Oct 27 22:07 dmesg-efi-166692286115002 + -rw-------. 1 root root 1771 Oct 27 22:07 dmesg-efi-166692286116002 + -rw-------. 1 root root 1816 Oct 27 22:07 dmesg-efi-166692286117002 + -rw-r-----. 1 root root 30000 Nov 18 14:58 dmesg.txt + + 1666922867: + total 4 + drwxr-xr-x. 3 root root 17 Nov 18 14:58 . + drwxr-xr-x. 7 root root 144 Nov 18 14:58 .. + drwxr-xr-x. 2 root root 4096 Nov 18 14:58 003 + + 1666922867/003: + total 104 + drwxr-xr-x. 2 root root 4096 Nov 18 14:58 . + drwxr-xr-x. 3 root root 17 Nov 18 14:58 .. + -rw-------. 1 root root 1388 Oct 27 22:07 dmesg-efi-166692286701003 + -rw-------. 1 root root 1824 Oct 27 22:07 dmesg-efi-166692286702003 + -rw-------. 1 root root 1795 Oct 27 22:07 dmesg-efi-166692286703003 + -rw-------. 1 root root 1805 Oct 27 22:07 dmesg-efi-166692286704003 + -rw-------. 1 root root 1813 Oct 27 22:07 dmesg-efi-166692286705003 + -rw-------. 1 root root 1821 Oct 27 22:07 dmesg-efi-166692286706003 + -rw-------. 1 root root 1814 Oct 27 22:07 dmesg-efi-166692286707003 + -rw-------. 1 root root 1812 Oct 27 22:07 dmesg-efi-166692286708003 + -rw-------. 1 root root 1769 Oct 27 22:07 dmesg-efi-166692286709003 + -rw-------. 1 root root 1820 Oct 27 22:07 dmesg-efi-166692286710003 + -rw-------. 1 root root 1755 Oct 27 22:07 dmesg-efi-166692286711003 + -rw-------. 1 root root 1790 Oct 27 22:07 dmesg-efi-166692286712003 + -rw-------. 1 root root 1756 Oct 27 22:07 dmesg-efi-166692286713003 + -rw-------. 1 root root 1763 Oct 27 22:07 dmesg-efi-166692286714003 + -rw-------. 1 root root 1791 Oct 27 22:07 dmesg-efi-166692286715003 + -rw-------. 1 root root 1775 Oct 27 22:07 dmesg-efi-166692286716003 + -rw-------. 1 root root 1820 Oct 27 22:07 dmesg-efi-166692286717003 + -rw-r-----. 1 root root 30111 Nov 18 14:58 dmesg.txt + +Furthemore, pstore records on ERST are now able to accurately +identify the change in timestamp sequence in order to start a +new dmesg.txt, as needed. + +(cherry picked from commit 5fbaa757077bde2db8d33b1c358518c41b990339) + +Related: #2158832 +--- + src/pstore/pstore.c | 216 +++++++++++++++++++------------------------- + 1 file changed, 92 insertions(+), 124 deletions(-) + +diff --git a/src/pstore/pstore.c b/src/pstore/pstore.c +index d70e142b4d..9f61e8f7f8 100644 +--- a/src/pstore/pstore.c ++++ b/src/pstore/pstore.c +@@ -112,8 +112,8 @@ static int compare_pstore_entries(const void *_a, const void *_b) { + return strcmp(a->dirent.d_name, b->dirent.d_name); + } + +-static int move_file(PStoreEntry *pe, const char *subdir) { +- _cleanup_free_ char *ifd_path = NULL, *ofd_path = NULL; ++static int move_file(PStoreEntry *pe, const char *subdir1, const char *subdir2) { ++ _cleanup_free_ char *ifd_path = NULL, *ofd_path = NULL, *ofd_path_base = NULL; + _cleanup_free_ void *field = NULL; + const char *suffix, *message; + struct iovec iovec[2]; +@@ -126,7 +126,11 @@ static int move_file(PStoreEntry *pe, const char *subdir) { + if (!ifd_path) + return log_oom(); + +- ofd_path = path_join(arg_archivedir, subdir, pe->dirent.d_name); ++ ofd_path_base = path_join(arg_archivedir, subdir1, subdir2); ++ if (!ofd_path_base) ++ return log_oom(); ++ ++ ofd_path = path_join(NULL, ofd_path_base, pe->dirent.d_name); + if (!ofd_path) + return log_oom(); + +@@ -169,155 +173,119 @@ static int move_file(PStoreEntry *pe, const char *subdir) { + return 0; + } + +-static int write_dmesg(const char *dmesg, size_t size, const char *id) { +- _cleanup_(unlink_and_freep) char *tmp_path = NULL; +- _cleanup_free_ char *ofd_path = NULL; ++static int append_dmesg(PStoreEntry *pe, const char *subdir1, const char *subdir2) { ++ /* Append dmesg chunk to end, create if needed */ ++ _cleanup_free_ char *ofd_path = NULL, *ofd_path_base = NULL; + _cleanup_close_ int ofd = -1; + ssize_t wr; +- int r; + +- if (size == 0) +- return 0; ++ assert(pe); + +- assert(dmesg); ++ if (pe->content_size == 0) ++ return 0; + +- /* log_info("Record ID %s", id); */ ++ ofd_path_base = path_join(arg_archivedir, subdir1, subdir2); ++ if (!ofd_path_base) ++ return log_oom(); + +- ofd_path = path_join(arg_archivedir, id, "dmesg.txt"); ++ ofd_path = path_join(NULL, ofd_path_base, "dmesg.txt"); + if (!ofd_path) + return log_oom(); + +- ofd = open_tmpfile_linkable(ofd_path, O_CLOEXEC|O_CREAT|O_TRUNC|O_WRONLY, &tmp_path); ++ ofd = open(ofd_path, O_CREAT|O_NOFOLLOW|O_NOCTTY|O_CLOEXEC|O_APPEND|O_WRONLY, 0640); + if (ofd < 0) +- return log_error_errno(ofd, "Failed to open temporary file %s: %m", ofd_path); +- wr = write(ofd, dmesg, size); ++ return log_error_errno(ofd, "Failed to open file %s: %m", ofd_path); ++ wr = write(ofd, pe->content, pe->content_size); + if (wr < 0) + return log_error_errno(errno, "Failed to store dmesg to %s: %m", ofd_path); +- if (wr != (ssize_t)size) +- return log_error_errno(-EIO, "Failed to store dmesg to %s. %zu bytes are lost.", ofd_path, size - wr); +- r = link_tmpfile(ofd, tmp_path, ofd_path); +- if (r < 0) +- return log_error_errno(r, "Failed to write temporary file %s: %m", ofd_path); +- tmp_path = mfree(tmp_path); ++ if ((size_t)wr != pe->content_size) ++ return log_error_errno(-EIO, "Failed to store dmesg to %s. %zu bytes are lost.", ofd_path, pe->content_size - wr); + + return 0; + } + +-static void process_dmesg_files(PStoreList *list) { ++static int process_dmesg_files(PStoreList *list) { + /* Move files, reconstruct dmesg.txt */ +- _cleanup_free_ char *dmesg = NULL, *dmesg_id = NULL; +- size_t dmesg_size = 0, dmesg_allocated = 0; +- bool dmesg_bad = false; +- PStoreEntry *pe; ++ _cleanup_free_ char *erst_subdir = NULL; ++ uint64_t last_record_id = 0; ++ ++ /* When dmesg is written into pstore, it is done so in small chunks, whatever the exchange buffer ++ * size is with the underlying pstore backend (ie. EFI may be ~2KiB), which means an example ++ * pstore with approximately 64KB of storage may have up to roughly 32 dmesg files, some likely ++ * related. ++ * ++ * Here we look at the dmesg filename and try to discern if files are part of a related group, ++ * meaning the same original dmesg. ++ * ++ * The dmesg- filename contains the backend-type and the Common Platform Error Record, CPER, ++ * record id, a 64-bit number. ++ * ++ * Files are processed in reverse lexigraphical order so as to properly reconstruct original dmesg.*/ + +- /* Handle each dmesg file: files processed in reverse +- * order so as to properly reconstruct original dmesg */ + for (size_t n = list->n_entries; n > 0; n--) { +- bool move_file_and_continue = false; +- _cleanup_free_ char *pe_id = NULL; ++ PStoreEntry *pe; + char *p; +- size_t plen; + + pe = &list->entries[n-1]; + + if (pe->handled) + continue; +- if (!startswith(pe->dirent.d_name, "dmesg-")) +- continue; +- + if (endswith(pe->dirent.d_name, ".enc.z")) /* indicates a problem */ +- move_file_and_continue = true; +- p = strrchr(pe->dirent.d_name, '-'); +- if (!p) +- move_file_and_continue = true; +- +- if (move_file_and_continue) { +- /* A dmesg file on which we do NO additional processing */ +- (void) move_file(pe, NULL); + continue; +- } +- +- /* See if this file is one of a related group of files +- * in order to reconstruct dmesg */ +- +- /* When dmesg is written into pstore, it is done so in +- * small chunks, whatever the exchange buffer size is +- * with the underlying pstore backend (ie. EFI may be +- * ~2KiB), which means an example pstore with approximately +- * 64KB of storage may have up to roughly 32 dmesg files +- * that could be related, depending upon the size of the +- * original dmesg. +- * +- * Here we look at the dmesg filename and try to discern +- * if files are part of a related group, meaning the same +- * original dmesg. +- * +- * The two known pstore backends are EFI and ERST. These +- * backends store data in the Common Platform Error +- * Record, CPER, format. The dmesg- filename contains the +- * CPER record id, a 64bit number (in decimal notation). +- * In Linux, the record id is encoded with two digits for +- * the dmesg part (chunk) number and 3 digits for the +- * count number. So allowing an additional digit to +- * compensate for advancing time, this code ignores the +- * last six digits of the filename in determining the +- * record id. +- * +- * For the EFI backend, the record id encodes an id in the +- * upper 32 bits, and a timestamp in the lower 32-bits. +- * So ignoring the least significant 6 digits has proven +- * to generally identify related dmesg entries. */ +-#define PSTORE_FILENAME_IGNORE 6 +- +- /* determine common portion of record id */ +- ++p; /* move beyond dmesg- */ +- plen = strlen(p); +- if (plen > PSTORE_FILENAME_IGNORE) { +- pe_id = memdup_suffix0(p, plen - PSTORE_FILENAME_IGNORE); +- if (!pe_id) { +- log_oom(); +- return; +- } +- } else +- pe_id = mfree(pe_id); +- +- /* Now move file from pstore to archive storage */ +- move_file(pe, pe_id); +- +- if (dmesg_bad) ++ if (!startswith(pe->dirent.d_name, "dmesg-")) + continue; + +- /* If the current record id is NOT the same as the +- * previous record id, then start a new dmesg.txt file */ +- if (!streq_ptr(pe_id, dmesg_id)) { +- /* Encountered a new dmesg group, close out old one, open new one */ +- (void) write_dmesg(dmesg, dmesg_size, dmesg_id); +- dmesg_size = 0; +- +- /* now point dmesg_id to storage of pe_id */ +- free_and_replace(dmesg_id, pe_id); +- } +- +- /* Reconstruction of dmesg is done as a useful courtesy: do not fail, but don't write garbled +- * output either. */ +- size_t needed = strlen(pe->dirent.d_name) + strlen(":\n") + pe->content_size + 1; +- if (!GREEDY_REALLOC(dmesg, dmesg_allocated, dmesg_size + needed)) { +- log_warning_errno(ENOMEM, "Failed to write dmesg file: %m"); +- dmesg_bad = true; +- continue; +- } +- +- dmesg_size += sprintf(dmesg + dmesg_size, "%s:\n", pe->dirent.d_name); +- if (pe->content) { +- memcpy(dmesg + dmesg_size, pe->content, pe->content_size); +- dmesg_size += pe->content_size; +- } +- +- pe_id = mfree(pe_id); ++ if ((p = startswith(pe->dirent.d_name, "dmesg-efi-"))) { ++ /* For the EFI backend, the 3 least significant digits of record id encodes a ++ * "count" number, the next 2 least significant digits for the dmesg part ++ * (chunk) number, and the remaining digits as the timestamp. See ++ * linux/drivers/firmware/efi/efi-pstore.c in efi_pstore_write(). */ ++ _cleanup_free_ char *subdir1 = NULL, *subdir2 = NULL; ++ size_t plen = strlen(p); ++ ++ if (plen < 6) ++ continue; ++ ++ /* Extract base record id */ ++ subdir1 = strndup(p, plen - 5); ++ if (!subdir1) ++ return log_oom(); ++ /* Extract "count" field */ ++ subdir2 = strndup(p + plen - 3, 3); ++ if (!subdir2) ++ return log_oom(); ++ ++ /* Now move file from pstore to archive storage */ ++ (void) move_file(pe, subdir1, subdir2); ++ ++ /* Append to the dmesg */ ++ (void) append_dmesg(pe, subdir1, subdir2); ++ } else if ((p = startswith(pe->dirent.d_name, "dmesg-erst-"))) { ++ /* For the ERST backend, the record is a monotonically increasing number, seeded as ++ * a timestamp. See linux/drivers/acpi/apei/erst.c in erst_writer(). */ ++ uint64_t record_id; ++ ++ if (safe_atou64(p, &record_id) < 0) ++ continue; ++ if (last_record_id - 1 != record_id) ++ /* A discontinuity in the number has been detected, this current record id ++ * will become the directory name for all pieces of the dmesg in this ++ * series. */ ++ if (free_and_strdup(&erst_subdir, p) < 0) ++ return log_oom(); ++ ++ /* Now move file from pstore to archive storage */ ++ (void) move_file(pe, erst_subdir, NULL); ++ ++ /* Append to the dmesg */ ++ (void) append_dmesg(pe, erst_subdir, NULL); ++ ++ /* Update, but keep erst_subdir for next file */ ++ last_record_id = record_id; ++ } else ++ log_debug("Unknown backend, ignoring \"%s\".", pe->dirent.d_name); + } +- +- if (!dmesg_bad) +- (void) write_dmesg(dmesg, dmesg_size, dmesg_id); ++ return 0; + } + + static int list_files(PStoreList *list, const char *sourcepath) { +@@ -394,11 +362,11 @@ static int run(int argc, char *argv[]) { + qsort_safe(list.entries, list.n_entries, sizeof(PStoreEntry), compare_pstore_entries); + + /* Process known file types */ +- process_dmesg_files(&list); ++ (void) process_dmesg_files(&list); + + /* Move left over files out of pstore */ + for (size_t n = 0; n < list.n_entries; n++) +- move_file(&list.entries[n], NULL); ++ (void) move_file(&list.entries[n], NULL, NULL); + + return 0; + } diff --git a/SOURCES/0888-pstore-Don-t-start-systemd-pstore.service-in-contain.patch b/SOURCES/0888-pstore-Don-t-start-systemd-pstore.service-in-contain.patch new file mode 100644 index 0000000..10bb2fc --- /dev/null +++ b/SOURCES/0888-pstore-Don-t-start-systemd-pstore.service-in-contain.patch @@ -0,0 +1,27 @@ +From 653a635086cfeaf0af12da3a722b0ebe2029b927 Mon Sep 17 00:00:00 2001 +From: Balint Reczey +Date: Mon, 16 Dec 2019 19:03:19 +0100 +Subject: [PATCH] pstore: Don't start systemd-pstore.service in containers + +Usually it is not useful and can also fail making +boot-and-services autopkgtest fail. + +(cherry picked from commit 287f506c32f3f4a48ba020408f964cb0f964d752) + +Related: #2158832 +--- + units/systemd-pstore.service.in | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/units/systemd-pstore.service.in b/units/systemd-pstore.service.in +index dde21bc33e..89f34afe34 100644 +--- a/units/systemd-pstore.service.in ++++ b/units/systemd-pstore.service.in +@@ -11,6 +11,7 @@ + Description=Platform Persistent Storage Archival + Documentation=man:systemd-pstore(8) + ConditionDirectoryNotEmpty=/sys/fs/pstore ++ConditionVirtualization=!container + DefaultDependencies=no + Wants=systemd-remount-fs.service + After=systemd-remount-fs.service diff --git a/SOURCES/0889-units-pull-in-systemd-pstore.service-from-sysinit.ta.patch b/SOURCES/0889-units-pull-in-systemd-pstore.service-from-sysinit.ta.patch new file mode 100644 index 0000000..3682e6f --- /dev/null +++ b/SOURCES/0889-units-pull-in-systemd-pstore.service-from-sysinit.ta.patch @@ -0,0 +1,36 @@ +From c7e65774a4ccc8a431f63c5a12ab776b24ee1190 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Wed, 8 Apr 2020 16:12:00 +0200 +Subject: [PATCH] units: pull in systemd-pstore.service from sysinit.target + +sysinit.target is the target our early boot services are generally +pulled in from, make systemd-pstore.service not an exception of that. + +Effectively this doesn't mean much, either way our unit is part of the +initial transaction. + +(cherry picked from commit 167241912f51fbc0d7d0869b9af34c15b5ecc4b6) + +Related: #2158832 +--- + units/systemd-pstore.service.in | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/units/systemd-pstore.service.in b/units/systemd-pstore.service.in +index 89f34afe34..37fcf878f0 100644 +--- a/units/systemd-pstore.service.in ++++ b/units/systemd-pstore.service.in +@@ -15,6 +15,7 @@ ConditionVirtualization=!container + DefaultDependencies=no + Wants=systemd-remount-fs.service + After=systemd-remount-fs.service ++Before=sysinit.target + + [Service] + Type=oneshot +@@ -23,4 +24,4 @@ RemainAfterExit=yes + StateDirectory=systemd/pstore + + [Install] +-WantedBy=systemd-remount-fs.service ++WantedBy=sysinit.target diff --git a/SOURCES/0890-units-drop-dependency-on-systemd-remount-fs.service-.patch b/SOURCES/0890-units-drop-dependency-on-systemd-remount-fs.service-.patch new file mode 100644 index 0000000..cb60e2b --- /dev/null +++ b/SOURCES/0890-units-drop-dependency-on-systemd-remount-fs.service-.patch @@ -0,0 +1,36 @@ +From bc6f273a0475a1fa7ab56bc1e498ee62c96aa660 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Wed, 8 Apr 2020 16:10:38 +0200 +Subject: [PATCH] units: drop dependency on systemd-remount-fs.service from + systemd-pstore.service + +This dependency is now generated automatically given we use +StateDirectory=. Moreover the combination of Wants= and After= was too +strong anway, as whether remount-fs is pulled in or not should not be up +to systemd-pstore.service, and in fact is part of the initial +transaction anyway. + +[dtardon: This only removes Wants=, not After=, because I haven't +backported the auto-generation code the description talks about. The +code is simple, but it's just an optimisation allowing for slightly +shorter unit files, hence I don't think we really need it.] + +(cherry picked from commit 0c978faa16fa9ecf92f0bbb5c7cc709dc472d115) + +Related: #2158832 +--- + units/systemd-pstore.service.in | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/units/systemd-pstore.service.in b/units/systemd-pstore.service.in +index 37fcf878f0..9a86f3145c 100644 +--- a/units/systemd-pstore.service.in ++++ b/units/systemd-pstore.service.in +@@ -13,7 +13,6 @@ Documentation=man:systemd-pstore(8) + ConditionDirectoryNotEmpty=/sys/fs/pstore + ConditionVirtualization=!container + DefaultDependencies=no +-Wants=systemd-remount-fs.service + After=systemd-remount-fs.service + Before=sysinit.target + diff --git a/SOURCES/0891-units-make-sure-systemd-pstore-stops-at-shutdown.patch b/SOURCES/0891-units-make-sure-systemd-pstore-stops-at-shutdown.patch new file mode 100644 index 0000000..6fdbf34 --- /dev/null +++ b/SOURCES/0891-units-make-sure-systemd-pstore-stops-at-shutdown.patch @@ -0,0 +1,29 @@ +From 818ddd1efd751ef50f9960920284465befe9d704 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Wed, 8 Apr 2020 16:25:03 +0200 +Subject: [PATCH] units: make sure systemd-pstore stops at shutdown + +This doesn't matter too much given that the service doesn't do anything +on shutdown, but let's still stop it to make things cleaner. + +(cherry picked from commit b0c1a07654c80d3cbbbcc52f860d4206707c0b08) + +Related: #2158832 +--- + units/systemd-pstore.service.in | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/units/systemd-pstore.service.in b/units/systemd-pstore.service.in +index 9a86f3145c..8cbf264a99 100644 +--- a/units/systemd-pstore.service.in ++++ b/units/systemd-pstore.service.in +@@ -14,7 +14,8 @@ ConditionDirectoryNotEmpty=/sys/fs/pstore + ConditionVirtualization=!container + DefaultDependencies=no + After=systemd-remount-fs.service +-Before=sysinit.target ++Conflicts=shutdown.target ++Before=sysinit.target shutdown.target + + [Service] + Type=oneshot diff --git a/SOURCES/0892-pstore-Run-after-modules-are-loaded.patch b/SOURCES/0892-pstore-Run-after-modules-are-loaded.patch new file mode 100644 index 0000000..491e363 --- /dev/null +++ b/SOURCES/0892-pstore-Run-after-modules-are-loaded.patch @@ -0,0 +1,45 @@ +From 9cc6ee46e0083bc36b53d19e14fb637f7a1542dd Mon Sep 17 00:00:00 2001 +From: Alexander Graf +Date: Thu, 9 Jun 2022 16:20:43 +0200 +Subject: [PATCH] pstore: Run after modules are loaded + +The systemd-pstore service takes pstore files on boot and transfers them +to disk. It only does it once on boot and only if it finds any. The typical +location of the pstore on modern systems is the UEFI variable store. + +Most distributions ship with CONFIG_EFI_VARS_PSTORE=m. That means, the +UEFI variable store is only available on boot after the respective module +is loaded. + +In most situations, the pstore service gets loaded before the UEFI pstore, +so we don't get to transfer logs. Instead, they accumulate, filling up the +pstore over time, potentially breaking the UEFI variable store. + +Let's add a service dependency on any kernel module that can provide a +pstore to ensure we only scan for pstate after we can actually see pstate. + +I have seen live occurences of systems breaking because we did not erase +the pstates and ran out of UEFI nvram space. + +Fixes https://github.com/systemd/systemd/issues/18540 + +(cherry picked from commit 70e74a5997ae2ce7ba72a74ac949c3b2dad1a1d6) + +Related: #2158832 +--- + units/systemd-pstore.service.in | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/units/systemd-pstore.service.in b/units/systemd-pstore.service.in +index 8cbf264a99..1983a9738f 100644 +--- a/units/systemd-pstore.service.in ++++ b/units/systemd-pstore.service.in +@@ -16,6 +16,8 @@ DefaultDependencies=no + After=systemd-remount-fs.service + Conflicts=shutdown.target + Before=sysinit.target shutdown.target ++After=modprobe@efi_pstore.service modprobe@mtdpstore.service modprobe@chromeos_pstore.service modprobe@ramoops.service modprobe@pstore_zone.service modprobe@pstore_blk.service ++Wants=modprobe@efi_pstore.service modprobe@mtdpstore.service modprobe@chromeos_pstore.service modprobe@ramoops.service modprobe@pstore_zone.service modprobe@pstore_blk.service + + [Service] + Type=oneshot diff --git a/SOURCES/0893-pstore-do-not-try-to-load-all-known-pstore-modules.patch b/SOURCES/0893-pstore-do-not-try-to-load-all-known-pstore-modules.patch new file mode 100644 index 0000000..2108fd0 --- /dev/null +++ b/SOURCES/0893-pstore-do-not-try-to-load-all-known-pstore-modules.patch @@ -0,0 +1,48 @@ +From 6a6f108b59e47581d93cbc6bdc604ee84f1bb791 Mon Sep 17 00:00:00 2001 +From: Nick Rosbrook +Date: Wed, 7 Sep 2022 13:25:13 -0400 +Subject: [PATCH] pstore: do not try to load all known pstore modules + +Commit 70e74a5997 ("pstore: Run after modules are loaded") added After= +and Wants= entries for all known kernel modules providing a pstore. + +While adding these dependencies on systems where one of the modules is +not present, or not configured, should not have a real affect on the +system, it can produce annoying error messages in the kernel log. E.g. +"mtd device must be supplied (device name is empty)" when the mtdpstore +module is not configured correctly. + +Since dependencies cannot be removed with drop-ins, if a distro wants to +remove some of these modules from systemd-pstore.service, they need to +patch units/systemd-pstore.service.in. On the other hand, if they want +to append to the dependencies this can be done by shipping a drop-in. + +Since the original intent of the previous commit was to fix [1], which +only requires the efi_pstore module, remove all other kernel module +dependencies from systemd-pstore.service, and let distros ship drop-ins +to add dependencies if needed. + +[1] https://github.com/systemd/systemd/issues/18540 + +(cherry picked from commit 8b8bd621e1d16808678fc3afed257df1fa03a281) + +Related: #2158832 +--- + units/systemd-pstore.service.in | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/units/systemd-pstore.service.in b/units/systemd-pstore.service.in +index 1983a9738f..19ffa8d4e8 100644 +--- a/units/systemd-pstore.service.in ++++ b/units/systemd-pstore.service.in +@@ -16,8 +16,8 @@ DefaultDependencies=no + After=systemd-remount-fs.service + Conflicts=shutdown.target + Before=sysinit.target shutdown.target +-After=modprobe@efi_pstore.service modprobe@mtdpstore.service modprobe@chromeos_pstore.service modprobe@ramoops.service modprobe@pstore_zone.service modprobe@pstore_blk.service +-Wants=modprobe@efi_pstore.service modprobe@mtdpstore.service modprobe@chromeos_pstore.service modprobe@ramoops.service modprobe@pstore_zone.service modprobe@pstore_blk.service ++After=modprobe@efi_pstore.service ++Wants=modprobe@efi_pstore.service + + [Service] + Type=oneshot diff --git a/SOURCES/0894-logind-session-make-stopping-of-idle-session-visible.patch b/SOURCES/0894-logind-session-make-stopping-of-idle-session-visible.patch new file mode 100644 index 0000000..df27c3b --- /dev/null +++ b/SOURCES/0894-logind-session-make-stopping-of-idle-session-visible.patch @@ -0,0 +1,26 @@ +From b18e19f2262e7ed95c25d53268d12427fe77102d Mon Sep 17 00:00:00 2001 +From: David Tardon +Date: Tue, 21 Feb 2023 10:41:47 +0100 +Subject: [PATCH] logind-session: make stopping of idle session visible to + admins + +(cherry picked from commit 6269ffe7ee8a659df7336a2582054ecd9eecf4b1) + +Resolves: #2156780 +--- + src/login/logind-session.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/login/logind-session.c b/src/login/logind-session.c +index 18a07efcdb..916202a65a 100644 +--- a/src/login/logind-session.c ++++ b/src/login/logind-session.c +@@ -673,7 +673,7 @@ static int session_dispatch_stop_on_idle(sd_event_source *source, uint64_t t, vo + + idle = session_get_idle_hint(s, &ts); + if (idle) { +- log_debug("Session \"%s\" of user \"%s\" is idle, stopping.", s->id, s->user->name); ++ log_info("Session \"%s\" of user \"%s\" is idle, stopping.", s->id, s->user->name); + + return session_stop(s, /* force */ true); + } diff --git a/SOURCES/0895-journald-Increase-stdout-buffer-size-sooner-when-alm.patch b/SOURCES/0895-journald-Increase-stdout-buffer-size-sooner-when-alm.patch new file mode 100644 index 0000000..f03ea88 --- /dev/null +++ b/SOURCES/0895-journald-Increase-stdout-buffer-size-sooner-when-alm.patch @@ -0,0 +1,32 @@ +From a0b52398692f3e4bda18520db9e2397f7b2c80dd Mon Sep 17 00:00:00 2001 +From: Benjamin Robin +Date: Sun, 3 May 2020 18:37:21 +0200 +Subject: [PATCH] journald: Increase stdout buffer size sooner, when almost + full + +If the previous received buffer length is almost equal to the allocated +buffer size, before this change the next read can only receive a couple +of bytes (in the worst case only 1 byte), which is not efficient. + +(cherry picked from commit 034e9719ac1ba88a36b05da38c7aa98761d42c77) + +Related: #2029426 +--- + src/journal/journald-stream.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/journal/journald-stream.c b/src/journal/journald-stream.c +index 302a82d3d7..c8de984335 100644 +--- a/src/journal/journald-stream.c ++++ b/src/journal/journald-stream.c +@@ -507,8 +507,8 @@ static int stdout_stream_process(sd_event_source *es, int fd, uint32_t revents, + goto terminate; + } + +- /* If the buffer is full already (discounting the extra NUL we need), add room for another 1K */ +- if (s->length + 1 >= s->allocated) { ++ /* If the buffer is almost full, add room for another 1K */ ++ if (s->length + 512 >= s->allocated) { + if (!GREEDY_REALLOC(s->buffer, s->allocated, s->length + 1 + 1024)) { + log_oom(); + goto terminate; diff --git a/SOURCES/0896-journald-rework-end-of-line-marker-handling-to-use-a.patch b/SOURCES/0896-journald-rework-end-of-line-marker-handling-to-use-a.patch new file mode 100644 index 0000000..a655502 --- /dev/null +++ b/SOURCES/0896-journald-rework-end-of-line-marker-handling-to-use-a.patch @@ -0,0 +1,77 @@ +From d8fabe7a6839eeb0d5d0504471f2d18b07545238 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Tue, 12 May 2020 18:53:35 +0200 +Subject: [PATCH] journald: rework end of line marker handling to use a field + table + +(cherry picked from commit 549b7379ba404c33fd448d2bca46a57f6529b00b) + +Related: #2029426 +--- + src/journal/journald-stream.c | 29 ++++++++++++++++++++--------- + 1 file changed, 20 insertions(+), 9 deletions(-) + +diff --git a/src/journal/journald-stream.c b/src/journal/journald-stream.c +index c8de984335..58752a5a24 100644 +--- a/src/journal/journald-stream.c ++++ b/src/journal/journald-stream.c +@@ -54,6 +54,8 @@ typedef enum LineBreak { + LINE_BREAK_NUL, + LINE_BREAK_LINE_MAX, + LINE_BREAK_EOF, ++ _LINE_BREAK_MAX, ++ _LINE_BREAK_INVALID = -1, + } LineBreak; + + struct StdoutStream { +@@ -233,7 +235,11 @@ fail: + return log_error_errno(r, "Failed to save stream data %s: %m", s->state_file); + } + +-static int stdout_stream_log(StdoutStream *s, const char *p, LineBreak line_break) { ++static int stdout_stream_log( ++ StdoutStream *s, ++ const char *p, ++ LineBreak line_break) { ++ + struct iovec *iovec; + int priority; + char syslog_priority[] = "PRIORITY=\0"; +@@ -245,6 +251,9 @@ static int stdout_stream_log(StdoutStream *s, const char *p, LineBreak line_brea + assert(s); + assert(p); + ++ assert(line_break >= 0); ++ assert(line_break < _LINE_BREAK_MAX); ++ + if (s->context) + (void) client_context_maybe_refresh(s->server, s->context, NULL, NULL, 0, NULL, USEC_INFINITY); + else if (pid_is_valid(s->ucred.pid)) { +@@ -296,17 +305,19 @@ static int stdout_stream_log(StdoutStream *s, const char *p, LineBreak line_brea + iovec[n++] = IOVEC_MAKE_STRING(syslog_identifier); + } + +- if (line_break != LINE_BREAK_NEWLINE) { +- const char *c; ++ static const char * const line_break_field_table[_LINE_BREAK_MAX] = { ++ [LINE_BREAK_NEWLINE] = NULL, /* Do not add field if traditional newline */ ++ [LINE_BREAK_NUL] = "_LINE_BREAK=nul", ++ [LINE_BREAK_LINE_MAX] = "_LINE_BREAK=line-max", ++ [LINE_BREAK_EOF] = "_LINE_BREAK=eof", ++ }; + +- /* If this log message was generated due to an uncommon line break then mention this in the log +- * entry */ ++ const char *c = line_break_field_table[line_break]; + +- c = line_break == LINE_BREAK_NUL ? "_LINE_BREAK=nul" : +- line_break == LINE_BREAK_LINE_MAX ? "_LINE_BREAK=line-max" : +- "_LINE_BREAK=eof"; ++ /* If this log message was generated due to an uncommon line break then mention this in the log ++ * entry */ ++ if (c) + iovec[n++] = IOVEC_MAKE_STRING(c); +- } + + message = strappend("MESSAGE=", p); + if (message) diff --git a/SOURCES/0897-journald-use-the-fact-that-client_context_release-re.patch b/SOURCES/0897-journald-use-the-fact-that-client_context_release-re.patch new file mode 100644 index 0000000..7d4ab74 --- /dev/null +++ b/SOURCES/0897-journald-use-the-fact-that-client_context_release-re.patch @@ -0,0 +1,27 @@ +From cd85a657c932725ac7c1b506dc6dd4270d1dc068 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Tue, 12 May 2020 19:15:38 +0200 +Subject: [PATCH] journald: use the fact that client_context_release() returns + NULL + +(cherry picked from commit 020b4a023c2c6dda83afb9a82a62e640569c40c1) + +Related: #2029426 +--- + src/journal/journald-stream.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/src/journal/journald-stream.c b/src/journal/journald-stream.c +index 58752a5a24..ab1a855943 100644 +--- a/src/journal/journald-stream.c ++++ b/src/journal/journald-stream.c +@@ -570,8 +570,7 @@ static int stdout_stream_process(sd_event_source *es, int fd, uint32_t revents, + goto terminate; + + s->ucred = *ucred; +- client_context_release(s->server, s->context); +- s->context = NULL; ++ s->context = client_context_release(s->server, s->context); + } + + s->length += l; diff --git a/SOURCES/0898-journald-rework-pid-change-handling.patch b/SOURCES/0898-journald-rework-pid-change-handling.patch new file mode 100644 index 0000000..09fb251 --- /dev/null +++ b/SOURCES/0898-journald-rework-pid-change-handling.patch @@ -0,0 +1,219 @@ +From 538bd9b42dabf1145cf7ab77f32347d516b01e88 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Tue, 12 May 2020 18:56:34 +0200 +Subject: [PATCH] journald: rework pid change handling + +Let's introduce an explicit line ending marker for line endings due to +pid change. + +Let's also make sure we don't get confused with buffer management. + +Fixes: #15654 +(cherry picked from commit 45ba1ea5e9264d385fa565328fe957ef1d78caa1) + +Resolves: #2029426 +--- + src/journal/journald-stream.c | 99 +++++++++++++++++++++++------------ + 1 file changed, 66 insertions(+), 33 deletions(-) + +diff --git a/src/journal/journald-stream.c b/src/journal/journald-stream.c +index ab1a855943..5be5b0939c 100644 +--- a/src/journal/journald-stream.c ++++ b/src/journal/journald-stream.c +@@ -54,6 +54,7 @@ typedef enum LineBreak { + LINE_BREAK_NUL, + LINE_BREAK_LINE_MAX, + LINE_BREAK_EOF, ++ LINE_BREAK_PID_CHANGE, + _LINE_BREAK_MAX, + _LINE_BREAK_INVALID = -1, + } LineBreak; +@@ -310,6 +311,7 @@ static int stdout_stream_log( + [LINE_BREAK_NUL] = "_LINE_BREAK=nul", + [LINE_BREAK_LINE_MAX] = "_LINE_BREAK=line-max", + [LINE_BREAK_EOF] = "_LINE_BREAK=eof", ++ [LINE_BREAK_PID_CHANGE] = "_LINE_BREAK=pid-change", + }; + + const char *c = line_break_field_table[line_break]; +@@ -431,21 +433,43 @@ static int stdout_stream_line(StdoutStream *s, char *p, LineBreak line_break) { + assert_not_reached("Unknown stream state"); + } + +-static int stdout_stream_scan(StdoutStream *s, bool force_flush) { +- char *p; +- size_t remaining; ++static int stdout_stream_found( ++ StdoutStream *s, ++ char *p, ++ size_t l, ++ LineBreak line_break) { ++ ++ char saved; + int r; + + assert(s); ++ assert(p); ++ ++ /* Let's NUL terminate the specified buffer for this call, and revert back afterwards */ ++ saved = p[l]; ++ p[l] = 0; ++ r = stdout_stream_line(s, p, line_break); ++ p[l] = saved; + +- p = s->buffer; +- remaining = s->length; ++ return r; ++} ++ ++static int stdout_stream_scan( ++ StdoutStream *s, ++ char *p, ++ size_t remaining, ++ LineBreak force_flush, ++ size_t *ret_consumed) { + +- /* XXX: This function does nothing if (s->length == 0) */ ++ size_t consumed = 0; ++ int r; ++ ++ assert(s); ++ assert(p); + + for (;;) { + LineBreak line_break; +- size_t skip; ++ size_t skip, found; + char *end1, *end2; + + end1 = memchr(p, '\n', remaining); +@@ -453,43 +477,40 @@ static int stdout_stream_scan(StdoutStream *s, bool force_flush) { + + if (end2) { + /* We found a NUL terminator */ +- skip = end2 - p + 1; ++ found = end2 - p; ++ skip = found + 1; + line_break = LINE_BREAK_NUL; + } else if (end1) { + /* We found a \n terminator */ +- *end1 = 0; +- skip = end1 - p + 1; ++ found = end1 - p; ++ skip = found + 1; + line_break = LINE_BREAK_NEWLINE; + } else if (remaining >= s->server->line_max) { + /* Force a line break after the maximum line length */ +- *(p + s->server->line_max) = 0; +- skip = remaining; ++ found = skip = s->server->line_max; + line_break = LINE_BREAK_LINE_MAX; + } else + break; + +- r = stdout_stream_line(s, p, line_break); ++ r = stdout_stream_found(s, p, found, line_break); + if (r < 0) + return r; + +- remaining -= skip; + p += skip; ++ consumed += skip; ++ remaining -= skip; + } + +- if (force_flush && remaining > 0) { +- p[remaining] = 0; +- r = stdout_stream_line(s, p, LINE_BREAK_EOF); ++ if (force_flush >= 0 && remaining > 0) { ++ r = stdout_stream_found(s, p, remaining, force_flush); + if (r < 0) + return r; + +- p += remaining; +- remaining = 0; ++ consumed += remaining; + } + +- if (p > s->buffer) { +- memmove(s->buffer, p, remaining); +- s->length = remaining; +- } ++ if (ret_consumed) ++ *ret_consumed = consumed; + + return 0; + } +@@ -497,11 +518,12 @@ static int stdout_stream_scan(StdoutStream *s, bool force_flush) { + static int stdout_stream_process(sd_event_source *es, int fd, uint32_t revents, void *userdata) { + uint8_t buf[CMSG_SPACE(sizeof(struct ucred))]; + StdoutStream *s = userdata; ++ size_t limit, consumed; + struct ucred *ucred = NULL; + struct cmsghdr *cmsg; + struct iovec iovec; +- size_t limit; + ssize_t l; ++ char *p; + int r; + + struct msghdr msghdr = { +@@ -529,7 +551,7 @@ static int stdout_stream_process(sd_event_source *es, int fd, uint32_t revents, + /* Try to make use of the allocated buffer in full, but never read more than the configured line size. Also, + * always leave room for a terminating NUL we might need to add. */ + limit = MIN(s->allocated - 1, s->server->line_max); +- ++ assert(s->length <= limit); + iovec = IOVEC_MAKE(s->buffer + s->length, limit - s->length); + + l = recvmsg(s->fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC); +@@ -543,7 +565,7 @@ static int stdout_stream_process(sd_event_source *es, int fd, uint32_t revents, + cmsg_close_all(&msghdr); + + if (l == 0) { +- stdout_stream_scan(s, true); ++ (void) stdout_stream_scan(s, s->buffer, s->length, /* force_flush = */ LINE_BREAK_EOF, NULL); + goto terminate; + } + +@@ -562,22 +584,33 @@ static int stdout_stream_process(sd_event_source *es, int fd, uint32_t revents, + * in the meantime. + */ + if (ucred && ucred->pid != s->ucred.pid) { +- /* force out any previously half-written lines from a +- * different process, before we switch to the new ucred +- * structure for everything we just added */ +- r = stdout_stream_scan(s, true); ++ /* Force out any previously half-written lines from a different process, before we switch to ++ * the new ucred structure for everything we just added */ ++ r = stdout_stream_scan(s, s->buffer, s->length, /* force_flush = */ LINE_BREAK_PID_CHANGE, NULL); + if (r < 0) + goto terminate; + +- s->ucred = *ucred; + s->context = client_context_release(s->server, s->context); ++ ++ p = s->buffer + s->length; ++ } else { ++ p = s->buffer; ++ l += s->length; + } + +- s->length += l; +- r = stdout_stream_scan(s, false); ++ /* Always copy in the new credentials */ ++ if (ucred) ++ s->ucred = *ucred; ++ ++ r = stdout_stream_scan(s, p, l, _LINE_BREAK_INVALID, &consumed); + if (r < 0) + goto terminate; + ++ /* Move what wasn't consumed to the front of the buffer */ ++ assert(consumed <= (size_t) l); ++ s->length = l - consumed; ++ memmove(s->buffer, p + consumed, s->length); ++ + return 1; + + terminate: diff --git a/SOURCES/0899-test-Add-a-test-case-for-15654.patch b/SOURCES/0899-test-Add-a-test-case-for-15654.patch new file mode 100644 index 0000000..24d7ee7 --- /dev/null +++ b/SOURCES/0899-test-Add-a-test-case-for-15654.patch @@ -0,0 +1,34 @@ +From e019afeefb396ea42d03f4c3f9713e262aff6450 Mon Sep 17 00:00:00 2001 +From: Benjamin Robin +Date: Wed, 6 May 2020 23:28:02 +0200 +Subject: [PATCH] test: Add a test case for #15654 + +(cherry picked from commit c11d8fd1dab3bc3f0abbc861ba5eb34518cec1da) + +Related: #2029426 +--- + test/TEST-04-JOURNAL/test-journal.sh | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/test/TEST-04-JOURNAL/test-journal.sh b/test/TEST-04-JOURNAL/test-journal.sh +index a3db1a7472..bdf137cd69 100755 +--- a/test/TEST-04-JOURNAL/test-journal.sh ++++ b/test/TEST-04-JOURNAL/test-journal.sh +@@ -76,6 +76,17 @@ journalctl -b -o export -t "$ID" --output-fields=_PID | grep '^_PID=' >/output + grep -q "^_PID=$PID" /output + grep -vq "^_PID=$PID" /output + ++# https://github.com/systemd/systemd/issues/15654 ++ID=$(journalctl --new-id128 | sed -n 2p) ++printf "This will\nusually fail\nand be truncated\n">/expected ++systemd-cat -t "$ID" /bin/sh -c 'env echo -n "This will";echo;env echo -n "usually fail";echo;env echo -n "and be truncated";echo;' ++journalctl --sync ++journalctl -b -o cat -t "$ID" >/output ++cmp /expected /output ++ ++# Add new tests before here, the journald restarts below ++# may make tests flappy. ++ + # Don't lose streams on restart + systemctl start forever-print-hola + sleep 3 diff --git a/SOURCES/0900-test-Stricter-test-case-for-15654-Add-more-checks.patch b/SOURCES/0900-test-Stricter-test-case-for-15654-Add-more-checks.patch new file mode 100644 index 0000000..8ebaec2 --- /dev/null +++ b/SOURCES/0900-test-Stricter-test-case-for-15654-Add-more-checks.patch @@ -0,0 +1,32 @@ +From afcfb65ce7514bf32e59e0b9d212ae18d023a4b5 Mon Sep 17 00:00:00 2001 +From: Benjamin Robin +Date: Sat, 9 May 2020 12:01:07 +0200 +Subject: [PATCH] test: Stricter test case for #15654 (Add more checks) + +Check: + - There is only 3 messages logged with type stdout + - Check all messages logged does not have new line: LINE_BREAK=eof + - Check that the 3 messages are logged from a different PID + - Check the 3 MESSAGE= content +(cherry picked from commit d38b3b74dbde2d65b23e5963354c3db96acd3420) + +Related: #2029426 +--- + test/TEST-04-JOURNAL/test-journal.sh | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/test/TEST-04-JOURNAL/test-journal.sh b/test/TEST-04-JOURNAL/test-journal.sh +index bdf137cd69..641259ce24 100755 +--- a/test/TEST-04-JOURNAL/test-journal.sh ++++ b/test/TEST-04-JOURNAL/test-journal.sh +@@ -83,6 +83,10 @@ systemd-cat -t "$ID" /bin/sh -c 'env echo -n "This will";echo;env echo -n "usual + journalctl --sync + journalctl -b -o cat -t "$ID" >/output + cmp /expected /output ++[[ $(journalctl -b -o export -t "$ID" --output-fields=_TRANSPORT | grep -Pc "^_TRANSPORT=stdout$") -eq 3 ]] ++[[ $(journalctl -b -o export -t "$ID" --output-fields=_LINE_BREAK | grep -Pc "^_LINE_BREAK=pid-change$") -eq 3 ]] ++[[ $(journalctl -b -o export -t "$ID" --output-fields=_PID | sort -u | grep -c "^_PID=.*$") -eq 3 ]] ++[[ $(journalctl -b -o export -t "$ID" --output-fields=MESSAGE | grep -Pc "^MESSAGE=(This will|usually fail|and be truncated)$") -eq 3 ]] + + # Add new tests before here, the journald restarts below + # may make tests flappy. diff --git a/SOURCES/0901-man-document-the-new-_LINE_BREAK-type.patch b/SOURCES/0901-man-document-the-new-_LINE_BREAK-type.patch new file mode 100644 index 0000000..75c0e85 --- /dev/null +++ b/SOURCES/0901-man-document-the-new-_LINE_BREAK-type.patch @@ -0,0 +1,42 @@ +From 2f55aeadef3dcdf65c61f24a41178148c3544ac3 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Wed, 13 May 2020 00:09:43 +0200 +Subject: [PATCH] man: document the new _LINE_BREAK= type + +(cherry picked from commit a3d9aee14fa2f7df429dc401582877176206b7fd) + +Related: #2029426 +--- + man/systemd.journal-fields.xml | 19 ++++++++++--------- + 1 file changed, 10 insertions(+), 9 deletions(-) + +diff --git a/man/systemd.journal-fields.xml b/man/systemd.journal-fields.xml +index 0c95c4cd95..ad2b94dbd5 100644 +--- a/man/systemd.journal-fields.xml ++++ b/man/systemd.journal-fields.xml +@@ -326,15 +326,16 @@ + + _LINE_BREAK= + +- Only applies to _TRANSPORT=stdout records: indicates that the log message in the +- standard output/error stream was not terminated with a normal newline character (\n, +- i.e. ASCII 10). Specifically, when set this field is one of (in case the line was +- terminated by a NUL byte), (in case the maximum log line length was reached, as +- configured with LineMax= in +- journald.conf5) or +- (if this was the last log record of a stream and the stream ended without a final +- newline character). Note that this record is not generated when a normal newline character was used for +- marking the log line end. ++ Only applies to _TRANSPORT=stdout records: indicates that the log message ++ in the standard output/error stream was not terminated with a normal newline character ++ (\n, i.e. ASCII 10). Specifically, when set this field is one of ++ (in case the line was terminated by a NUL byte), (in ++ case the maximum log line length was reached, as configured with LineMax= in ++ journald.conf5), ++ (if this was the last log record of a stream and the stream ended without a ++ final newline character), or (if the process which generated the log ++ output changed in the middle of a line). Note that this record is not generated when a normal ++ newline character was used for marking the log line end. + + + diff --git a/SPECS/systemd.spec b/SPECS/systemd.spec index 48f77b6..5f644f6 100644 --- a/SPECS/systemd.spec +++ b/SPECS/systemd.spec @@ -13,7 +13,7 @@ Name: systemd Url: http://www.freedesktop.org/wiki/Software/systemd Version: 239 -Release: 72%{?dist} +Release: 73%{?dist} # For a breakdown of the licensing, see README License: LGPLv2+ and MIT and GPLv2+ Summary: System and Service Manager @@ -922,6 +922,35 @@ Patch0869: 0869-systemctl-reintroduce-the-original-halt_main.patch Patch0870: 0870-systemctl-preserve-old-behavior-unless-requested.patch Patch0871: 0871-pam_systemd-suppress-LOG_DEBUG-log-messages-if-debug.patch Patch0872: 0872-udev-net_id-introduce-naming-scheme-for-RHEL-8.8.patch +Patch0873: 0873-journald-add-API-to-move-logging-from-var-to-run-aga.patch +Patch0874: 0874-journalctl-add-new-relinquish-and-smart-relinquish-o.patch +Patch0875: 0875-units-automatically-revert-to-run-logging-on-shutdow.patch +Patch0876: 0876-pstore-Tool-to-archive-contents-of-pstore.patch +Patch0877: 0877-meson-drop-redundant-line.patch +Patch0878: 0878-pstore-drop-unnecessary-initializations.patch +Patch0879: 0879-pstopre-fix-return-value-of-list_files.patch +Patch0880: 0880-pstore-remove-temporary-file-on-failure.patch +Patch0881: 0881-pstore-do-not-add-FILE-journal-entry-if-content_size.patch +Patch0882: 0882-pstore-run-only-when-sys-fs-pstore-is-not-empty.patch +Patch0883: 0883-pstore-fix-use-after-free.patch +Patch0884: 0884-pstore-refuse-to-run-if-arguments-are-specified.patch +Patch0885: 0885-pstore-allow-specifying-src-and-dst-dirs-are-argumen.patch +Patch0886: 0886-pstore-rework-memory-handling-for-dmesg.patch +Patch0887: 0887-pstore-fixes-for-dmesg.txt-reconstruction.patch +Patch0888: 0888-pstore-Don-t-start-systemd-pstore.service-in-contain.patch +Patch0889: 0889-units-pull-in-systemd-pstore.service-from-sysinit.ta.patch +Patch0890: 0890-units-drop-dependency-on-systemd-remount-fs.service-.patch +Patch0891: 0891-units-make-sure-systemd-pstore-stops-at-shutdown.patch +Patch0892: 0892-pstore-Run-after-modules-are-loaded.patch +Patch0893: 0893-pstore-do-not-try-to-load-all-known-pstore-modules.patch +Patch0894: 0894-logind-session-make-stopping-of-idle-session-visible.patch +Patch0895: 0895-journald-Increase-stdout-buffer-size-sooner-when-alm.patch +Patch0896: 0896-journald-rework-end-of-line-marker-handling-to-use-a.patch +Patch0897: 0897-journald-use-the-fact-that-client_context_release-re.patch +Patch0898: 0898-journald-rework-pid-change-handling.patch +Patch0899: 0899-test-Add-a-test-case-for-15654.patch +Patch0900: 0900-test-Stricter-test-case-for-15654-Add-more-checks.patch +Patch0901: 0901-man-document-the-new-_LINE_BREAK-type.patch %ifarch %{ix86} x86_64 aarch64 %global have_gnu_efi 1 @@ -1339,7 +1368,8 @@ python3 %{SOURCE2} %buildroot < - 239-73 +- journald: add API to move logging from /var to /run again (#1873540) +- journalctl: add new --relinquish and --smart-relinquish options (#1873540) +- units: automatically revert to /run logging on shutdown if necessary (#1873540) +- pstore: Tool to archive contents of pstore (#2158832) +- meson: drop redundant line (#2158832) +- pstore: drop unnecessary initializations (#2158832) +- pstopre: fix return value of list_files() (#2158832) +- pstore: remove temporary file on failure (#2158832) +- pstore: do not add FILE= journal entry if content_size == 0 (#2158832) +- pstore: run only when /sys/fs/pstore is not empty (#2158832) +- pstore: fix use after free (#2158832) +- pstore: refuse to run if arguments are specified (#2158832) +- pstore: allow specifying src and dst dirs are arguments (#2158832) +- pstore: rework memory handling for dmesg (#2158832) +- pstore: fixes for dmesg.txt reconstruction (#2158832) +- pstore: Don't start systemd-pstore.service in containers (#2158832) +- units: pull in systemd-pstore.service from sysinit.target (#2158832) +- units: drop dependency on systemd-remount-fs.service from systemd-pstore.service (#2158832) +- units: make sure systemd-pstore stops at shutdown (#2158832) +- pstore: Run after modules are loaded (#2158832) +- pstore: do not try to load all known pstore modules (#2158832) +- logind-session: make stopping of idle session visible to admins (#2156780) +- journald: Increase stdout buffer size sooner, when almost full (#2029426) +- journald: rework end of line marker handling to use a field table (#2029426) +- journald: use the fact that client_context_release() returns NULL (#2029426) +- journald: rework pid change handling (#2029426) +- test: Add a test case for #15654 (#2029426) +- test: Stricter test case for #15654 (Add more checks) (#2029426) +- man: document the new _LINE_BREAK= type (#2029426) + * Fri Feb 17 2023 systemd maintenance team - 239-72 - test: import logind test from debian/ubuntu test suite (#1866955) - test: introduce inst_recursive() helper function (#1866955)