|
|
From 2f75df5cd6dcd56775fec9e89fc79672e702d826 Mon Sep 17 00:00:00 2001
|
|
|
From: Eric DeVolder <eric.devolder@oracle.com>
|
|
|
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 @@
|
|
|
+<?xml version='1.0'?>
|
|
|
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
|
|
|
+ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
|
|
|
+<!-- SPDX-License-Identifier: LGPL-2.1+ -->
|
|
|
+
|
|
|
+<refentry id="pstore.conf" conditional="ENABLE_PSTORE"
|
|
|
+ xmlns:xi="http://www.w3.org/2001/XInclude">
|
|
|
+ <refentryinfo>
|
|
|
+ <title>pstore.conf</title>
|
|
|
+ <productname>systemd</productname>
|
|
|
+ </refentryinfo>
|
|
|
+
|
|
|
+ <refmeta>
|
|
|
+ <refentrytitle>pstore.conf</refentrytitle>
|
|
|
+ <manvolnum>5</manvolnum>
|
|
|
+ </refmeta>
|
|
|
+
|
|
|
+ <refnamediv>
|
|
|
+ <refname>pstore.conf</refname>
|
|
|
+ <refname>pstore.conf.d</refname>
|
|
|
+ <refpurpose>PStore configuration file</refpurpose>
|
|
|
+ </refnamediv>
|
|
|
+
|
|
|
+ <refsynopsisdiv>
|
|
|
+ <para>
|
|
|
+ <filename>/etc/systemd/pstore.conf</filename>
|
|
|
+ <filename>/etc/systemd/pstore.conf.d/*</filename>
|
|
|
+ </para>
|
|
|
+ </refsynopsisdiv>
|
|
|
+
|
|
|
+ <refsect1>
|
|
|
+ <title>Description</title>
|
|
|
+
|
|
|
+ <para>This file configures the behavior of
|
|
|
+ <citerefentry><refentrytitle>systemd-pstore</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
|
|
|
+ a tool for archiving the contents of the persistent storage filesystem,
|
|
|
+ <ulink url="https://www.kernel.org/doc/Documentation/ABI/testing/pstore">pstore</ulink>.
|
|
|
+ </para>
|
|
|
+ </refsect1>
|
|
|
+
|
|
|
+ <xi:include href="standard-conf.xml" xpointer="main-conf" />
|
|
|
+
|
|
|
+ <refsect1>
|
|
|
+ <title>Options</title>
|
|
|
+
|
|
|
+ <para>All options are configured in the
|
|
|
+ <literal>[PStore]</literal> section:</para>
|
|
|
+
|
|
|
+ <variablelist>
|
|
|
+
|
|
|
+ <varlistentry>
|
|
|
+ <term><varname>Storage=</varname></term>
|
|
|
+
|
|
|
+ <listitem><para>Controls where to archive (i.e. copy) files from the pstore filesystem. One of <literal>none</literal>,
|
|
|
+ <literal>external</literal>, and <literal>journal</literal>. When
|
|
|
+ <literal>none</literal>, the tool exits without processing files in the pstore filesystem.
|
|
|
+ When <literal>external</literal> (the default), files are archived into <filename>/var/lib/systemd/pstore/</filename>,
|
|
|
+ and logged into the journal.
|
|
|
+ When <literal>journal</literal>, pstore file contents are logged only in the journal.</para>
|
|
|
+ </listitem>
|
|
|
+
|
|
|
+ </varlistentry>
|
|
|
+
|
|
|
+ <varlistentry>
|
|
|
+ <term><varname>Unlink=</varname></term>
|
|
|
+
|
|
|
+ <listitem><para>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.
|
|
|
+ </para></listitem>
|
|
|
+ </varlistentry>
|
|
|
+ </variablelist>
|
|
|
+
|
|
|
+ <para>The defaults for all values are listed as comments in the
|
|
|
+ template <filename>/etc/systemd/pstore.conf</filename> file that
|
|
|
+ is installed by default.</para>
|
|
|
+ </refsect1>
|
|
|
+
|
|
|
+ <refsect1>
|
|
|
+ <title>See Also</title>
|
|
|
+ <para>
|
|
|
+ <citerefentry><refentrytitle>systemd-journald.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
|
|
|
+ </para>
|
|
|
+ </refsect1>
|
|
|
+
|
|
|
+</refentry>
|
|
|
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 @@
|
|
|
+<?xml version='1.0'?>
|
|
|
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
|
|
|
+ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
|
|
|
+<!-- SPDX-License-Identifier: LGPL-2.1+ -->
|
|
|
+
|
|
|
+<refentry id="systemd-pstore" conditional='ENABLE_PSTORE'
|
|
|
+ xmlns:xi="http://www.w3.org/2001/XInclude">
|
|
|
+
|
|
|
+ <refentryinfo>
|
|
|
+ <title>systemd-pstore</title>
|
|
|
+ <productname>systemd</productname>
|
|
|
+ </refentryinfo>
|
|
|
+
|
|
|
+ <refmeta>
|
|
|
+ <refentrytitle>systemd-pstore</refentrytitle>
|
|
|
+ <manvolnum>8</manvolnum>
|
|
|
+ </refmeta>
|
|
|
+
|
|
|
+ <refnamediv>
|
|
|
+ <refname>systemd-pstore</refname>
|
|
|
+ <refname>systemd-pstore.service</refname>
|
|
|
+ <refpurpose>Tool to archive contents of the persistent storage filesytem</refpurpose>
|
|
|
+ </refnamediv>
|
|
|
+
|
|
|
+ <refsynopsisdiv>
|
|
|
+ <para><filename>/usr/lib/systemd/systemd-pstore</filename></para>
|
|
|
+ <para><filename>systemd-pstore.service</filename></para>
|
|
|
+ </refsynopsisdiv>
|
|
|
+
|
|
|
+ <refsect1>
|
|
|
+ <title>Description</title>
|
|
|
+ <para><filename>systemd-pstore.service</filename> 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.</para>
|
|
|
+
|
|
|
+ <para>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).</para>
|
|
|
+
|
|
|
+ <para>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.</para>
|
|
|
+
|
|
|
+ <para>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.</para>
|
|
|
+
|
|
|
+ <para>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.</para>
|
|
|
+
|
|
|
+ <para>The <command>systemd-pstore</command> executable does the actual work. Upon starting,
|
|
|
+ the <filename>pstore.conf</filename> 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.</para>
|
|
|
+ </refsect1>
|
|
|
+
|
|
|
+ <refsect1>
|
|
|
+ <title>Configuration</title>
|
|
|
+
|
|
|
+ <para>The behavior of <command>systemd-pstore</command> is configured through the configuration file
|
|
|
+ <filename>/etc/systemd/pstore.conf</filename> and corresponding snippets
|
|
|
+ <filename>/etc/systemd/pstore.conf.d/*.conf</filename>, see
|
|
|
+ <citerefentry><refentrytitle>pstore.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
|
|
|
+ </para>
|
|
|
+
|
|
|
+ <refsect2>
|
|
|
+ <title>Disabling pstore processing</title>
|
|
|
+
|
|
|
+ <para>To disable pstore processing by <command>systemd-pstore</command>,
|
|
|
+ set <programlisting>Storage=none</programlisting> in
|
|
|
+ <citerefentry><refentrytitle>pstore.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
|
|
|
+ </para>
|
|
|
+ </refsect2>
|
|
|
+ </refsect1>
|
|
|
+
|
|
|
+ <refsect1>
|
|
|
+ <title>Usage</title>
|
|
|
+ <para>Data stored in the journal can be viewed with
|
|
|
+ <citerefentry><refentrytitle>journalctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
|
|
|
+ as usual.</para>
|
|
|
+ </refsect1>
|
|
|
+
|
|
|
+ <refsect1>
|
|
|
+ <title>See Also</title>
|
|
|
+ <para>
|
|
|
+ <citerefentry><refentrytitle>pstore.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>
|
|
|
+ </para>
|
|
|
+ </refsect1>
|
|
|
+</refentry>
|
|
|
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 <errno.h>
|
|
|
+#include <stdio.h>
|
|
|
+#include <stdio_ext.h>
|
|
|
+#include <sys/prctl.h>
|
|
|
+#include <sys/xattr.h>
|
|
|
+#include <unistd.h>
|
|
|
+
|
|
|
+#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
|