mirror of
https://github.com/systemd/systemd
synced 2026-03-18 11:04:46 +01:00
Compare commits
14 Commits
f75420a43a
...
7f7a50dd15
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7f7a50dd15 | ||
|
|
66973219c0 | ||
|
|
c50404aecc | ||
|
|
3da0caf5bb | ||
|
|
fd5f48af5c | ||
|
|
c46c323385 | ||
|
|
90a404f5d4 | ||
|
|
755021d434 | ||
|
|
587f2a5e56 | ||
|
|
5b6f8e13ad | ||
|
|
93ff34e44a | ||
|
|
d477a094e8 | ||
|
|
cbd4a8df5f | ||
|
|
1ece068e83 |
@ -99,7 +99,7 @@
|
|||||||
<term><varname>ExternalSizeMax=</varname></term>
|
<term><varname>ExternalSizeMax=</varname></term>
|
||||||
<term><varname>JournalSizeMax=</varname></term>
|
<term><varname>JournalSizeMax=</varname></term>
|
||||||
|
|
||||||
<listitem><para>The maximum (uncompressed) size in bytes of a
|
<listitem><para>The maximum (compressed or uncompressed) size in bytes of a
|
||||||
core to be saved. Unit suffixes are allowed just as in
|
core to be saved. Unit suffixes are allowed just as in
|
||||||
<option>ProcessSizeMax=</option></para></listitem>.
|
<option>ProcessSizeMax=</option></para></listitem>.
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|||||||
@ -2401,6 +2401,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
|
|||||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||||
readonly t MemoryCurrent = ...;
|
readonly t MemoryCurrent = ...;
|
||||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||||
|
readonly t MemoryAvailable = ...;
|
||||||
|
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||||
readonly t CPUUsageNSec = ...;
|
readonly t CPUUsageNSec = ...;
|
||||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||||
readonly ay EffectiveCPUs = [...];
|
readonly ay EffectiveCPUs = [...];
|
||||||
@ -3504,6 +3506,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
|
|||||||
|
|
||||||
<variablelist class="dbus-property" generated="True" extra-ref="MemoryCurrent"/>
|
<variablelist class="dbus-property" generated="True" extra-ref="MemoryCurrent"/>
|
||||||
|
|
||||||
|
<variablelist class="dbus-property" generated="True" extra-ref="MemoryAvailable"/>
|
||||||
|
|
||||||
<variablelist class="dbus-property" generated="True" extra-ref="CPUUsageNSec"/>
|
<variablelist class="dbus-property" generated="True" extra-ref="CPUUsageNSec"/>
|
||||||
|
|
||||||
<variablelist class="dbus-property" generated="True" extra-ref="EffectiveCPUs"/>
|
<variablelist class="dbus-property" generated="True" extra-ref="EffectiveCPUs"/>
|
||||||
@ -4063,6 +4067,11 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
|
|||||||
<varname>MountImages</varname>
|
<varname>MountImages</varname>
|
||||||
<varname>ExtensionImages</varname>
|
<varname>ExtensionImages</varname>
|
||||||
see systemd.exec(5) for their meaning.</para>
|
see systemd.exec(5) for their meaning.</para>
|
||||||
|
|
||||||
|
<para><varname>MemoryAvailable</varname> indicates how much unused memory is available to the unit before
|
||||||
|
the <literal>MemoryMax</literal> or <literal>MemoryHigh</literal> (whichever is lower) limit set by the cgroup
|
||||||
|
memory controller is reached. It will take into consideration limits on all parent slices, other than the
|
||||||
|
limits set on the unit itself.</para>
|
||||||
</refsect2>
|
</refsect2>
|
||||||
</refsect1>
|
</refsect1>
|
||||||
|
|
||||||
@ -4196,6 +4205,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
|
|||||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||||
readonly t MemoryCurrent = ...;
|
readonly t MemoryCurrent = ...;
|
||||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||||
|
readonly t MemoryAvailable = ...;
|
||||||
|
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||||
readonly t CPUUsageNSec = ...;
|
readonly t CPUUsageNSec = ...;
|
||||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||||
readonly ay EffectiveCPUs = [...];
|
readonly ay EffectiveCPUs = [...];
|
||||||
@ -5321,6 +5332,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
|
|||||||
|
|
||||||
<variablelist class="dbus-property" generated="True" extra-ref="MemoryCurrent"/>
|
<variablelist class="dbus-property" generated="True" extra-ref="MemoryCurrent"/>
|
||||||
|
|
||||||
|
<variablelist class="dbus-property" generated="True" extra-ref="MemoryAvailable"/>
|
||||||
|
|
||||||
<variablelist class="dbus-property" generated="True" extra-ref="CPUUsageNSec"/>
|
<variablelist class="dbus-property" generated="True" extra-ref="CPUUsageNSec"/>
|
||||||
|
|
||||||
<variablelist class="dbus-property" generated="True" extra-ref="EffectiveCPUs"/>
|
<variablelist class="dbus-property" generated="True" extra-ref="EffectiveCPUs"/>
|
||||||
@ -5915,6 +5928,8 @@ node /org/freedesktop/systemd1/unit/home_2emount {
|
|||||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||||
readonly t MemoryCurrent = ...;
|
readonly t MemoryCurrent = ...;
|
||||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||||
|
readonly t MemoryAvailable = ...;
|
||||||
|
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||||
readonly t CPUUsageNSec = ...;
|
readonly t CPUUsageNSec = ...;
|
||||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||||
readonly ay EffectiveCPUs = [...];
|
readonly ay EffectiveCPUs = [...];
|
||||||
@ -6886,6 +6901,8 @@ node /org/freedesktop/systemd1/unit/home_2emount {
|
|||||||
|
|
||||||
<variablelist class="dbus-property" generated="True" extra-ref="MemoryCurrent"/>
|
<variablelist class="dbus-property" generated="True" extra-ref="MemoryCurrent"/>
|
||||||
|
|
||||||
|
<variablelist class="dbus-property" generated="True" extra-ref="MemoryAvailable"/>
|
||||||
|
|
||||||
<variablelist class="dbus-property" generated="True" extra-ref="CPUUsageNSec"/>
|
<variablelist class="dbus-property" generated="True" extra-ref="CPUUsageNSec"/>
|
||||||
|
|
||||||
<variablelist class="dbus-property" generated="True" extra-ref="EffectiveCPUs"/>
|
<variablelist class="dbus-property" generated="True" extra-ref="EffectiveCPUs"/>
|
||||||
@ -7601,6 +7618,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
|
|||||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||||
readonly t MemoryCurrent = ...;
|
readonly t MemoryCurrent = ...;
|
||||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||||
|
readonly t MemoryAvailable = ...;
|
||||||
|
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||||
readonly t CPUUsageNSec = ...;
|
readonly t CPUUsageNSec = ...;
|
||||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||||
readonly ay EffectiveCPUs = [...];
|
readonly ay EffectiveCPUs = [...];
|
||||||
@ -8544,6 +8563,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
|
|||||||
|
|
||||||
<variablelist class="dbus-property" generated="True" extra-ref="MemoryCurrent"/>
|
<variablelist class="dbus-property" generated="True" extra-ref="MemoryCurrent"/>
|
||||||
|
|
||||||
|
<variablelist class="dbus-property" generated="True" extra-ref="MemoryAvailable"/>
|
||||||
|
|
||||||
<variablelist class="dbus-property" generated="True" extra-ref="CPUUsageNSec"/>
|
<variablelist class="dbus-property" generated="True" extra-ref="CPUUsageNSec"/>
|
||||||
|
|
||||||
<variablelist class="dbus-property" generated="True" extra-ref="EffectiveCPUs"/>
|
<variablelist class="dbus-property" generated="True" extra-ref="EffectiveCPUs"/>
|
||||||
@ -9112,6 +9133,8 @@ node /org/freedesktop/systemd1/unit/system_2eslice {
|
|||||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||||
readonly t MemoryCurrent = ...;
|
readonly t MemoryCurrent = ...;
|
||||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||||
|
readonly t MemoryAvailable = ...;
|
||||||
|
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||||
readonly t CPUUsageNSec = ...;
|
readonly t CPUUsageNSec = ...;
|
||||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||||
readonly ay EffectiveCPUs = [...];
|
readonly ay EffectiveCPUs = [...];
|
||||||
@ -9403,6 +9426,8 @@ node /org/freedesktop/systemd1/unit/system_2eslice {
|
|||||||
|
|
||||||
<variablelist class="dbus-property" generated="True" extra-ref="MemoryCurrent"/>
|
<variablelist class="dbus-property" generated="True" extra-ref="MemoryCurrent"/>
|
||||||
|
|
||||||
|
<variablelist class="dbus-property" generated="True" extra-ref="MemoryAvailable"/>
|
||||||
|
|
||||||
<variablelist class="dbus-property" generated="True" extra-ref="CPUUsageNSec"/>
|
<variablelist class="dbus-property" generated="True" extra-ref="CPUUsageNSec"/>
|
||||||
|
|
||||||
<variablelist class="dbus-property" generated="True" extra-ref="EffectiveCPUs"/>
|
<variablelist class="dbus-property" generated="True" extra-ref="EffectiveCPUs"/>
|
||||||
@ -9571,6 +9596,8 @@ node /org/freedesktop/systemd1/unit/session_2d1_2escope {
|
|||||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||||
readonly t MemoryCurrent = ...;
|
readonly t MemoryCurrent = ...;
|
||||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||||
|
readonly t MemoryAvailable = ...;
|
||||||
|
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||||
readonly t CPUUsageNSec = ...;
|
readonly t CPUUsageNSec = ...;
|
||||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||||
readonly ay EffectiveCPUs = [...];
|
readonly ay EffectiveCPUs = [...];
|
||||||
@ -9904,6 +9931,8 @@ node /org/freedesktop/systemd1/unit/session_2d1_2escope {
|
|||||||
|
|
||||||
<variablelist class="dbus-property" generated="True" extra-ref="MemoryCurrent"/>
|
<variablelist class="dbus-property" generated="True" extra-ref="MemoryCurrent"/>
|
||||||
|
|
||||||
|
<variablelist class="dbus-property" generated="True" extra-ref="MemoryAvailable"/>
|
||||||
|
|
||||||
<variablelist class="dbus-property" generated="True" extra-ref="CPUUsageNSec"/>
|
<variablelist class="dbus-property" generated="True" extra-ref="CPUUsageNSec"/>
|
||||||
|
|
||||||
<variablelist class="dbus-property" generated="True" extra-ref="EffectiveCPUs"/>
|
<variablelist class="dbus-property" generated="True" extra-ref="EffectiveCPUs"/>
|
||||||
|
|||||||
@ -492,8 +492,9 @@
|
|||||||
<varlistentry>
|
<varlistentry>
|
||||||
<term><varname>WakeOnLan=</varname></term>
|
<term><varname>WakeOnLan=</varname></term>
|
||||||
<listitem>
|
<listitem>
|
||||||
<para>The Wake-on-LAN policy to set for the device. The
|
<para>The Wake-on-LAN policy to set for the device. Takes the special value
|
||||||
supported values are:</para>
|
<literal>off</literal> which disables Wake-on-LAN, or space separated list of the following
|
||||||
|
words:</para>
|
||||||
|
|
||||||
<variablelist>
|
<variablelist>
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
@ -540,15 +541,11 @@
|
|||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
<varlistentry>
|
|
||||||
<term><option>off</option></term>
|
|
||||||
<listitem>
|
|
||||||
<para>Never wake.</para>
|
|
||||||
</listitem>
|
|
||||||
</varlistentry>
|
|
||||||
</variablelist>
|
</variablelist>
|
||||||
|
|
||||||
<para>Defaults to <option>off</option>.</para>
|
<para>Defaults to unset, and the device's default will be used. This setting can be specified
|
||||||
|
multiple times. If an empty string is assigned, then the all previous assignments are
|
||||||
|
cleared.</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
|
|||||||
@ -225,10 +225,12 @@
|
|||||||
where <replaceable>type</replaceable> may be e.g. <literal>service</literal> or <literal>socket</literal>,
|
where <replaceable>type</replaceable> may be e.g. <literal>service</literal> or <literal>socket</literal>,
|
||||||
that allows altering or adding to the settings of all corresponding unit files on the system.
|
that allows altering or adding to the settings of all corresponding unit files on the system.
|
||||||
The formatting and precedence of applying drop-in configurations follow what is defined above.
|
The formatting and precedence of applying drop-in configurations follow what is defined above.
|
||||||
Configurations in <filename><replaceable>type</replaceable>.d/</filename> have the lowest precedence
|
Files in <filename><replaceable>type</replaceable>.d/</filename> have lower precedence compared
|
||||||
compared to settings in the name specific override directories. So the contents of
|
to files in name-specific override directories. The usual rules apply: multiple drop-in files
|
||||||
<filename>foo-.service.d/10-override.conf</filename> would override
|
with different names are applied in lexicographic order, regardless of which of the directories
|
||||||
<filename>service.d/10-override.conf</filename>.</para>
|
they reside in, so a file in <filename><replaceable>type</replaceable>.d/</filename> applies
|
||||||
|
to a unit only if there are no drop-ins or masks with that name in directories with higher
|
||||||
|
precedence. See Examples.</para>
|
||||||
|
|
||||||
<para>Note that while systemd offers a flexible dependency system
|
<para>Note that while systemd offers a flexible dependency system
|
||||||
between units it is recommended to use this functionality only
|
between units it is recommended to use this functionality only
|
||||||
@ -2201,6 +2203,62 @@ PrivateTmp=yes</programlisting>
|
|||||||
to override the entire unit.</para>
|
to override the entire unit.</para>
|
||||||
|
|
||||||
</example>
|
</example>
|
||||||
|
|
||||||
|
<example>
|
||||||
|
<title>Top level drop-ins with template units</title>
|
||||||
|
|
||||||
|
<para>Top level drop-ins can be used to change some aspect of all units
|
||||||
|
of a particular type. For example by creating the
|
||||||
|
<filename index='false'>/etc/systemd/system/service.d</filename>
|
||||||
|
directory with a drop-in file, the contents of the drop-in file can be
|
||||||
|
applied to all service units. We can take this further by having the
|
||||||
|
top-level drop-in instantiate a secondary helper unit. Consider for
|
||||||
|
example the following set of units and drop-in files where we install
|
||||||
|
an OnFailure dependency for all service units.</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
<filename index='false'>/etc/systemd/system/failure-handler@.service</filename>:</para>
|
||||||
|
|
||||||
|
<programlisting>[Unit]
|
||||||
|
Description=My failure handler for %i
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
# Perform some special action for when %i exits unexpectedly.
|
||||||
|
ExecStart=/usr/sbin/myfailurehandler %i
|
||||||
|
|
||||||
|
</programlisting>
|
||||||
|
|
||||||
|
<para>We can then add an instance of the
|
||||||
|
<filename index='false'>failure-handler@.service</filename> as an
|
||||||
|
<varname>OnFailure=</varname> dependency for all service units.</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
<filename index='false'>/etc/systemd/system/service.d/10-all.conf</filename>:</para>
|
||||||
|
|
||||||
|
<programlisting>[Unit]
|
||||||
|
OnFailure=failure-handler@%n.service
|
||||||
|
</programlisting>
|
||||||
|
|
||||||
|
<para>Now, after running <command>systemctl daemon-reload</command> all
|
||||||
|
services will have acquired an <varname>OnFailure=</varname> dependency on
|
||||||
|
<filename index='false'>failure-handler@%n.service</filename>. The
|
||||||
|
template instance units will also have gained the dependency which results
|
||||||
|
in the creation of a recursive dependency chain. We can break the chain by
|
||||||
|
disabling the drop-in for the template instance units via a symlink to
|
||||||
|
<filename index='false'>/dev/null</filename>:</para>
|
||||||
|
|
||||||
|
<programlisting>
|
||||||
|
<command>mkdir /etc/systemd/system/failure-handler@.service.d</command>
|
||||||
|
<command>ln -s /dev/null /etc/systemd/system/failure-handler@.service.d/10-all.conf</command>
|
||||||
|
<command>systemctl daemon-reload</command>
|
||||||
|
</programlisting>
|
||||||
|
|
||||||
|
<para>This ensures that if a <filename index='false'>failure-handler@.service</filename> instance fails it will not trigger an instance named
|
||||||
|
<filename index='false'>failure-handler@failure-handler.service.service</filename>.</para>
|
||||||
|
|
||||||
|
</example>
|
||||||
|
|
||||||
</refsect1>
|
</refsect1>
|
||||||
|
|
||||||
<refsect1>
|
<refsect1>
|
||||||
|
|||||||
@ -161,7 +161,7 @@ L /tmp/foobar - - - - /dev/null</programlisting>
|
|||||||
<title>Type</title>
|
<title>Type</title>
|
||||||
|
|
||||||
<para>The type consists of a single letter and optionally an exclamation mark (<literal>!</literal>)
|
<para>The type consists of a single letter and optionally an exclamation mark (<literal>!</literal>)
|
||||||
and/or minus sign (<literal>-</literal>).</para>
|
minus sign (<literal>-</literal>), and/or equals sign (<literal>=</literal>).</para>
|
||||||
|
|
||||||
<para>The following line types are understood:</para>
|
<para>The following line types are understood:</para>
|
||||||
|
|
||||||
@ -482,6 +482,11 @@ r! /tmp/.X[0-9]*-lock</programlisting>
|
|||||||
<programlisting># Modify sysfs but don't fail if we are in a container with a read-only /proc
|
<programlisting># Modify sysfs but don't fail if we are in a container with a read-only /proc
|
||||||
w- /proc/sys/vm/swappiness - - - - 10</programlisting></para>
|
w- /proc/sys/vm/swappiness - - - - 10</programlisting></para>
|
||||||
|
|
||||||
|
<para>If the equals sign (<literal>=</literal>) is used, the file types of existing objects in the specified path
|
||||||
|
are checked, and removed if they do not match. This includes any implicitly created parent directories (which can
|
||||||
|
be either directories or directory symlinks). For example, if there is a FIFO in place of one of the parent path
|
||||||
|
components it will be replaced with a directory.</para>
|
||||||
|
|
||||||
<para>Note that for all line types that result in creation of any kind of file node
|
<para>Note that for all line types that result in creation of any kind of file node
|
||||||
(i.e. <varname>f</varname>/<varname>F</varname>,
|
(i.e. <varname>f</varname>/<varname>F</varname>,
|
||||||
<varname>d</varname>/<varname>D</varname>/<varname>v</varname>/<varname>q</varname>/<varname>Q</varname>,
|
<varname>d</varname>/<varname>D</varname>/<varname>v</varname>/<varname>q</varname>/<varname>Q</varname>,
|
||||||
@ -585,9 +590,40 @@ w- /proc/sys/vm/swappiness - - - - 10</programlisting></para>
|
|||||||
<para>The age of a file system entry is determined from its last
|
<para>The age of a file system entry is determined from its last
|
||||||
modification timestamp (mtime), its last access timestamp (atime),
|
modification timestamp (mtime), its last access timestamp (atime),
|
||||||
and (except for directories) its last status change timestamp
|
and (except for directories) its last status change timestamp
|
||||||
(ctime). Any of these three (or two) values will prevent cleanup
|
(ctime). By default, any of these three (or two) values will
|
||||||
if it is more recent than the current time minus the age
|
prevent cleanup if it is more recent than the current time minus
|
||||||
field.</para>
|
the age field. To restrict the deletion based on particular type
|
||||||
|
of file timestamps, the age-by argument can be used.</para>
|
||||||
|
|
||||||
|
<para>The age-by argument, when (optionally) specified along
|
||||||
|
with age will check if the file system entry has aged by the
|
||||||
|
type of file timestamp(s) provided. It can be specified by
|
||||||
|
prefixing the age argument with a set of file timestamp types
|
||||||
|
followed by a colon character <literal>:</literal>, i.e.,
|
||||||
|
<literal><replaceable>age-by</replaceable>:<replaceable>cleanup-age</replaceable></literal>.
|
||||||
|
The argument can be a set of:
|
||||||
|
<constant>a</constant> (<constant>A</constant> for directories),
|
||||||
|
<constant>b</constant> (<constant>B</constant> for directories),
|
||||||
|
<constant>c</constant> (<constant>C</constant> for directories; ignored by default), or
|
||||||
|
<constant>m</constant> (<constant>M</constant> for directories),
|
||||||
|
indicating access, creation, last status change, and last
|
||||||
|
modification times of a file system entry respectively. See
|
||||||
|
<citerefentry project='man-pages'><refentrytitle>statx</refentrytitle><manvolnum>2</manvolnum></citerefentry>
|
||||||
|
file timestamp fields for more details.</para>
|
||||||
|
|
||||||
|
<para>If unspecified, the age-by field defaults to
|
||||||
|
<constant>abcmABM</constant>,
|
||||||
|
i.e., by default all file timestamps are taken into consideration,
|
||||||
|
with the exception of the last status change timestamp (ctime) for
|
||||||
|
directories. This is because the aging logic itself will alter the
|
||||||
|
ctime whenever it deletes a file inside it. To ensure that running
|
||||||
|
the aging logic does not feed back into the next iteration of it,
|
||||||
|
ctime for directories is ignored by default.</para>
|
||||||
|
|
||||||
|
<para>For example:<programlisting>
|
||||||
|
# Files created and modified, and directories accessed more than
|
||||||
|
# an hour ago in "/tmp/foo/bar", are subject to time-based cleanup.
|
||||||
|
d /tmp/foo/bar - - - - bmA:1h -</programlisting></para>
|
||||||
|
|
||||||
<para>Note that while the aging algorithm is run a 'shared' BSD file lock (see <citerefentry
|
<para>Note that while the aging algorithm is run a 'shared' BSD file lock (see <citerefentry
|
||||||
project='man-pages'><refentrytitle>flock</refentrytitle><manvolnum>2</manvolnum></citerefentry>) is
|
project='man-pages'><refentrytitle>flock</refentrytitle><manvolnum>2</manvolnum></citerefentry>) is
|
||||||
|
|||||||
36
po/es.po
36
po/es.po
@ -5,13 +5,14 @@
|
|||||||
# Daniel Mustieles <daniel.mustieles@gmail.com>, 2015.
|
# Daniel Mustieles <daniel.mustieles@gmail.com>, 2015.
|
||||||
# Álex Puchades <alex94puchades@gmail.com>, 2015.
|
# Álex Puchades <alex94puchades@gmail.com>, 2015.
|
||||||
# Adolfo Jayme Barrientos <fitoschido@gmail.com>, 2020.
|
# Adolfo Jayme Barrientos <fitoschido@gmail.com>, 2020.
|
||||||
|
# Emilio Herrera <ehespinosa57@gmail.com>, 2021.
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: systemd master\n"
|
"Project-Id-Version: systemd master\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2021-01-08 17:48+0100\n"
|
"POT-Creation-Date: 2021-01-08 17:48+0100\n"
|
||||||
"PO-Revision-Date: 2020-08-24 07:29+0000\n"
|
"PO-Revision-Date: 2021-06-08 09:04+0000\n"
|
||||||
"Last-Translator: Adolfo Jayme Barrientos <fitoschido@gmail.com>\n"
|
"Last-Translator: Emilio Herrera <ehespinosa57@gmail.com>\n"
|
||||||
"Language-Team: Spanish <https://translate.fedoraproject.org/projects/systemd/"
|
"Language-Team: Spanish <https://translate.fedoraproject.org/projects/systemd/"
|
||||||
"master/es/>\n"
|
"master/es/>\n"
|
||||||
"Language: es\n"
|
"Language: es\n"
|
||||||
@ -19,7 +20,7 @@ msgstr ""
|
|||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
||||||
"X-Generator: Weblate 4.2.1\n"
|
"X-Generator: Weblate 4.6.2\n"
|
||||||
|
|
||||||
#: src/core/org.freedesktop.systemd1.policy.in:22
|
#: src/core/org.freedesktop.systemd1.policy.in:22
|
||||||
msgid "Send passphrase back to system"
|
msgid "Send passphrase back to system"
|
||||||
@ -73,49 +74,38 @@ msgstr "Necesita autenticarse para recargar el estado de systemd."
|
|||||||
|
|
||||||
#: src/home/org.freedesktop.home1.policy:13
|
#: src/home/org.freedesktop.home1.policy:13
|
||||||
msgid "Create a home area"
|
msgid "Create a home area"
|
||||||
msgstr ""
|
msgstr "Crear un área home"
|
||||||
|
|
||||||
#: src/home/org.freedesktop.home1.policy:14
|
#: src/home/org.freedesktop.home1.policy:14
|
||||||
#, fuzzy
|
|
||||||
#| msgid "Authentication is required to reload the systemd state."
|
|
||||||
msgid "Authentication is required to create a user's home area."
|
msgid "Authentication is required to create a user's home area."
|
||||||
msgstr "Se requiere autenticación para recargar el estado de systemd."
|
msgstr "Se requiere autenticación para crear un área home de usuario."
|
||||||
|
|
||||||
#: src/home/org.freedesktop.home1.policy:23
|
#: src/home/org.freedesktop.home1.policy:23
|
||||||
msgid "Remove a home area"
|
msgid "Remove a home area"
|
||||||
msgstr ""
|
msgstr "Quitar un área home"
|
||||||
|
|
||||||
#: src/home/org.freedesktop.home1.policy:24
|
#: src/home/org.freedesktop.home1.policy:24
|
||||||
#, fuzzy
|
|
||||||
#| msgid "Authentication is required to reload the systemd state."
|
|
||||||
msgid "Authentication is required to remove a user's home area."
|
msgid "Authentication is required to remove a user's home area."
|
||||||
msgstr "Se requiere autenticación para recargar el estado de systemd."
|
msgstr "Se requiere autenticación para quitar un área home de usuario."
|
||||||
|
|
||||||
#: src/home/org.freedesktop.home1.policy:33
|
#: src/home/org.freedesktop.home1.policy:33
|
||||||
msgid "Check credentials of a home area"
|
msgid "Check credentials of a home area"
|
||||||
msgstr ""
|
msgstr "Comprobar las credenciales de un área home"
|
||||||
|
|
||||||
#: src/home/org.freedesktop.home1.policy:34
|
#: src/home/org.freedesktop.home1.policy:34
|
||||||
#, fuzzy
|
|
||||||
#| msgid ""
|
|
||||||
#| "Authentication is required to manage active sessions, users and seats."
|
|
||||||
msgid ""
|
msgid ""
|
||||||
"Authentication is required to check credentials against a user's home area."
|
"Authentication is required to check credentials against a user's home area."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Se requiere autenticación para administrar las sesiones activas, usuarios y "
|
"Se requiere autenticación para comprobar las credenciales contra un área "
|
||||||
"puestos de trabajo."
|
"home de usuario."
|
||||||
|
|
||||||
#: src/home/org.freedesktop.home1.policy:43
|
#: src/home/org.freedesktop.home1.policy:43
|
||||||
msgid "Update a home area"
|
msgid "Update a home area"
|
||||||
msgstr ""
|
msgstr "Actualizar un área home"
|
||||||
|
|
||||||
#: src/home/org.freedesktop.home1.policy:44
|
#: src/home/org.freedesktop.home1.policy:44
|
||||||
#, fuzzy
|
|
||||||
#| msgid "Authentication is required to attach a device to a seat."
|
|
||||||
msgid "Authentication is required to update a user's home area."
|
msgid "Authentication is required to update a user's home area."
|
||||||
msgstr ""
|
msgstr "Se requiere autenticación para actualizar un área home de usuario."
|
||||||
"Se requiere autenticación para conectar un dispositivo a un puesto de "
|
|
||||||
"trabajo."
|
|
||||||
|
|
||||||
#: src/home/org.freedesktop.home1.policy:53
|
#: src/home/org.freedesktop.home1.policy:53
|
||||||
msgid "Resize a home area"
|
msgid "Resize a home area"
|
||||||
|
|||||||
@ -733,7 +733,7 @@ int inotify_add_watch_and_warn(int fd, const char *pathname, uint32_t mask) {
|
|||||||
return wd;
|
return wd;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool unsafe_transition(const struct stat *a, const struct stat *b) {
|
bool unsafe_transition(const struct stat *a, const struct stat *b) {
|
||||||
/* Returns true if the transition from a to b is safe, i.e. that we never transition from unprivileged to
|
/* Returns true if the transition from a to b is safe, i.e. that we never transition from unprivileged to
|
||||||
* privileged files or directories. Why bother? So that unprivileged code can't symlink to privileged files
|
* privileged files or directories. Why bother? So that unprivileged code can't symlink to privileged files
|
||||||
* making us believe we read something safe even though it isn't safe in the specific context we open it in. */
|
* making us believe we read something safe even though it isn't safe in the specific context we open it in. */
|
||||||
|
|||||||
@ -94,6 +94,8 @@ enum {
|
|||||||
CHASE_WARN = 1 << 7, /* Emit an appropriate warning when an error is encountered */
|
CHASE_WARN = 1 << 7, /* Emit an appropriate warning when an error is encountered */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
bool unsafe_transition(const struct stat *a, const struct stat *b);
|
||||||
|
|
||||||
/* How many iterations to execute before returning -ELOOP */
|
/* How many iterations to execute before returning -ELOOP */
|
||||||
#define CHASE_SYMLINKS_MAX 32
|
#define CHASE_SYMLINKS_MAX 32
|
||||||
|
|
||||||
|
|||||||
@ -48,11 +48,7 @@ static int patch_dirfd_mode(
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int unlinkat_harder(
|
int unlinkat_harder(int dfd, const char *filename, int unlink_flags, RemoveFlags remove_flags) {
|
||||||
int dfd,
|
|
||||||
const char *filename,
|
|
||||||
int unlink_flags,
|
|
||||||
RemoveFlags remove_flags) {
|
|
||||||
|
|
||||||
mode_t old_mode;
|
mode_t old_mode;
|
||||||
int r;
|
int r;
|
||||||
@ -77,13 +73,15 @@ static int unlinkat_harder(
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If this worked, we won't reset the old mode, since we'll need it for other entries too, and we
|
if (FLAGS_SET(remove_flags, REMOVE_CHMOD_RESTORE) && fchmod(dfd, old_mode) < 0)
|
||||||
* should destroy the whole thing */
|
return -errno;
|
||||||
|
|
||||||
|
/* If this worked, we won't reset the old mode by default, since we'll need it for other entries too,
|
||||||
|
* and we should destroy the whole thing */
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int fstatat_harder(
|
int fstatat_harder(int dfd,
|
||||||
int dfd,
|
|
||||||
const char *filename,
|
const char *filename,
|
||||||
struct stat *ret,
|
struct stat *ret,
|
||||||
int fstatat_flags,
|
int fstatat_flags,
|
||||||
@ -109,6 +107,9 @@ static int fstatat_harder(
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (FLAGS_SET(remove_flags, REMOVE_CHMOD_RESTORE) && fchmod(dfd, old_mode) < 0)
|
||||||
|
return -errno;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -12,9 +12,17 @@ typedef enum RemoveFlags {
|
|||||||
REMOVE_PHYSICAL = 1 << 2, /* If not set, only removes files on tmpfs, never physical file systems */
|
REMOVE_PHYSICAL = 1 << 2, /* If not set, only removes files on tmpfs, never physical file systems */
|
||||||
REMOVE_SUBVOLUME = 1 << 3, /* Drop btrfs subvolumes in the tree too */
|
REMOVE_SUBVOLUME = 1 << 3, /* Drop btrfs subvolumes in the tree too */
|
||||||
REMOVE_MISSING_OK = 1 << 4, /* If the top-level directory is missing, ignore the ENOENT for it */
|
REMOVE_MISSING_OK = 1 << 4, /* If the top-level directory is missing, ignore the ENOENT for it */
|
||||||
REMOVE_CHMOD = 1 << 5, /* chmod() for write access if we cannot delete something */
|
REMOVE_CHMOD = 1 << 5, /* chmod() for write access if we cannot delete or access something */
|
||||||
|
REMOVE_CHMOD_RESTORE = 1 << 6, /* Restore the old mode before returning */
|
||||||
} RemoveFlags;
|
} RemoveFlags;
|
||||||
|
|
||||||
|
int unlinkat_harder(int dfd, const char *filename, int unlink_flags, RemoveFlags remove_flags);
|
||||||
|
int fstatat_harder(int dfd,
|
||||||
|
const char *filename,
|
||||||
|
struct stat *ret,
|
||||||
|
int fstatat_flags,
|
||||||
|
RemoveFlags remove_flags);
|
||||||
|
|
||||||
int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev);
|
int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev);
|
||||||
int rm_rf(const char *path, RemoveFlags flags);
|
int rm_rf(const char *path, RemoveFlags flags);
|
||||||
|
|
||||||
|
|||||||
@ -1087,6 +1087,7 @@ const UnitVTable automount_vtable = {
|
|||||||
.can_transient = true,
|
.can_transient = true,
|
||||||
.can_fail = true,
|
.can_fail = true,
|
||||||
.can_trigger = true,
|
.can_trigger = true,
|
||||||
|
.exclude_from_switch_root_serialization = true,
|
||||||
|
|
||||||
.init = automount_init,
|
.init = automount_init,
|
||||||
.load = automount_load,
|
.load = automount_load,
|
||||||
|
|||||||
@ -3402,6 +3402,77 @@ int manager_notify_cgroup_empty(Manager *m, const char *cgroup) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int unit_get_memory_available(Unit *u, uint64_t *ret) {
|
||||||
|
uint64_t unit_current, available = UINT64_MAX;
|
||||||
|
CGroupContext *unit_context;
|
||||||
|
const char *memory_file;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(u);
|
||||||
|
assert(ret);
|
||||||
|
|
||||||
|
/* If data from cgroups can be accessed, try to find out how much more memory a unit can
|
||||||
|
* claim before hitting the configured cgroup limits (if any). Consider both MemoryHigh
|
||||||
|
* and MemoryMax, and also any slice the unit might be nested below. */
|
||||||
|
|
||||||
|
if (!UNIT_CGROUP_BOOL(u, memory_accounting))
|
||||||
|
return -ENODATA;
|
||||||
|
|
||||||
|
if (!u->cgroup_path)
|
||||||
|
return -ENODATA;
|
||||||
|
|
||||||
|
/* The root cgroup doesn't expose this information */
|
||||||
|
if (unit_has_host_root_cgroup(u))
|
||||||
|
return -ENODATA;
|
||||||
|
|
||||||
|
if ((u->cgroup_realized_mask & CGROUP_MASK_MEMORY) == 0)
|
||||||
|
return -ENODATA;
|
||||||
|
|
||||||
|
r = cg_all_unified();
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
memory_file = r > 0 ? "memory.current" : "memory.usage_in_bytes";
|
||||||
|
|
||||||
|
r = cg_get_attribute_as_uint64("memory", u->cgroup_path, memory_file, &unit_current);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
assert_se(unit_context = unit_get_cgroup_context(u));
|
||||||
|
|
||||||
|
if (unit_context->memory_max != UINT64_MAX || unit_context->memory_high != UINT64_MAX)
|
||||||
|
available = LESS_BY(MIN(unit_context->memory_max, unit_context->memory_high), unit_current);
|
||||||
|
|
||||||
|
for (Unit *slice = UNIT_GET_SLICE(u); slice; slice = UNIT_GET_SLICE(slice)) {
|
||||||
|
uint64_t slice_current, slice_available = UINT64_MAX;
|
||||||
|
CGroupContext *slice_context;
|
||||||
|
|
||||||
|
/* No point in continuing if we can't go any lower */
|
||||||
|
if (available == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (!slice->cgroup_path)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
slice_context = unit_get_cgroup_context(slice);
|
||||||
|
if (!slice_context)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (slice_context->memory_max == UINT64_MAX && slice_context->memory_high == UINT64_MAX)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
r = cg_get_attribute_as_uint64("memory", slice->cgroup_path, memory_file, &slice_current);
|
||||||
|
if (r < 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
slice_available = LESS_BY(MIN(slice_context->memory_max, slice_context->memory_high), slice_current);
|
||||||
|
available = MIN(slice_available, available);
|
||||||
|
}
|
||||||
|
|
||||||
|
*ret = available;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int unit_get_memory_current(Unit *u, uint64_t *ret) {
|
int unit_get_memory_current(Unit *u, uint64_t *ret) {
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
|
|||||||
@ -282,6 +282,7 @@ int unit_watch_all_pids(Unit *u);
|
|||||||
int unit_synthesize_cgroup_empty_event(Unit *u);
|
int unit_synthesize_cgroup_empty_event(Unit *u);
|
||||||
|
|
||||||
int unit_get_memory_current(Unit *u, uint64_t *ret);
|
int unit_get_memory_current(Unit *u, uint64_t *ret);
|
||||||
|
int unit_get_memory_available(Unit *u, uint64_t *ret);
|
||||||
int unit_get_tasks_current(Unit *u, uint64_t *ret);
|
int unit_get_tasks_current(Unit *u, uint64_t *ret);
|
||||||
int unit_get_cpu_usage(Unit *u, nsec_t *ret);
|
int unit_get_cpu_usage(Unit *u, nsec_t *ret);
|
||||||
int unit_get_io_accounting(Unit *u, CGroupIOAccountingMetric metric, bool allow_cache, uint64_t *ret);
|
int unit_get_io_accounting(Unit *u, CGroupIOAccountingMetric metric, bool allow_cache, uint64_t *ret);
|
||||||
|
|||||||
@ -1097,6 +1097,30 @@ static int property_get_current_memory(
|
|||||||
return sd_bus_message_append(reply, "t", sz);
|
return sd_bus_message_append(reply, "t", sz);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int property_get_available_memory(
|
||||||
|
sd_bus *bus,
|
||||||
|
const char *path,
|
||||||
|
const char *interface,
|
||||||
|
const char *property,
|
||||||
|
sd_bus_message *reply,
|
||||||
|
void *userdata,
|
||||||
|
sd_bus_error *error) {
|
||||||
|
|
||||||
|
uint64_t sz = UINT64_MAX;
|
||||||
|
Unit *u = userdata;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(bus);
|
||||||
|
assert(reply);
|
||||||
|
assert(u);
|
||||||
|
|
||||||
|
r = unit_get_memory_available(u, &sz);
|
||||||
|
if (r < 0 && r != -ENODATA)
|
||||||
|
log_unit_warning_errno(u, r, "Failed to get total available memory from cgroup: %m");
|
||||||
|
|
||||||
|
return sd_bus_message_append(reply, "t", sz);
|
||||||
|
}
|
||||||
|
|
||||||
static int property_get_current_tasks(
|
static int property_get_current_tasks(
|
||||||
sd_bus *bus,
|
sd_bus *bus,
|
||||||
const char *path,
|
const char *path,
|
||||||
@ -1541,6 +1565,7 @@ const sd_bus_vtable bus_unit_cgroup_vtable[] = {
|
|||||||
SD_BUS_PROPERTY("Slice", "s", property_get_slice, 0, 0),
|
SD_BUS_PROPERTY("Slice", "s", property_get_slice, 0, 0),
|
||||||
SD_BUS_PROPERTY("ControlGroup", "s", property_get_cgroup, 0, 0),
|
SD_BUS_PROPERTY("ControlGroup", "s", property_get_cgroup, 0, 0),
|
||||||
SD_BUS_PROPERTY("MemoryCurrent", "t", property_get_current_memory, 0, 0),
|
SD_BUS_PROPERTY("MemoryCurrent", "t", property_get_current_memory, 0, 0),
|
||||||
|
SD_BUS_PROPERTY("MemoryAvailable", "t", property_get_available_memory, 0, 0),
|
||||||
SD_BUS_PROPERTY("CPUUsageNSec", "t", property_get_cpu_usage, 0, 0),
|
SD_BUS_PROPERTY("CPUUsageNSec", "t", property_get_cpu_usage, 0, 0),
|
||||||
SD_BUS_PROPERTY("EffectiveCPUs", "ay", property_get_cpuset_cpus, 0, 0),
|
SD_BUS_PROPERTY("EffectiveCPUs", "ay", property_get_cpuset_cpus, 0, 0),
|
||||||
SD_BUS_PROPERTY("EffectiveMemoryNodes", "ay", property_get_cpuset_mems, 0, 0),
|
SD_BUS_PROPERTY("EffectiveMemoryNodes", "ay", property_get_cpuset_mems, 0, 0),
|
||||||
|
|||||||
@ -3321,11 +3321,7 @@ int manager_serialize(
|
|||||||
if (u->id != t)
|
if (u->id != t)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* Start marker */
|
r = unit_serialize(u, f, fds, switching_root);
|
||||||
fputs(u->id, f);
|
|
||||||
fputc('\n', f);
|
|
||||||
|
|
||||||
r = unit_serialize(u, f, fds, !switching_root);
|
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2176,6 +2176,7 @@ const UnitVTable mount_vtable = {
|
|||||||
|
|
||||||
.can_transient = true,
|
.can_transient = true,
|
||||||
.can_fail = true,
|
.can_fail = true,
|
||||||
|
.exclude_from_switch_root_serialization = true,
|
||||||
|
|
||||||
.init = mount_init,
|
.init = mount_init,
|
||||||
.load = mount_load,
|
.load = mount_load,
|
||||||
|
|||||||
@ -2626,13 +2626,6 @@ static int socket_serialize(Unit *u, FILE *f, FDSet *fds) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void socket_port_take_fd(SocketPort *p, FDSet *fds, int fd) {
|
|
||||||
assert(p);
|
|
||||||
|
|
||||||
safe_close(p->fd);
|
|
||||||
p->fd = fdset_remove(fds, fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int socket_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
|
static int socket_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
|
||||||
Socket *s = SOCKET(u);
|
Socket *s = SOCKET(u);
|
||||||
|
|
||||||
@ -2694,13 +2687,20 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value,
|
|||||||
|
|
||||||
if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd))
|
if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd))
|
||||||
log_unit_debug(u, "Failed to parse fifo value: %s", value);
|
log_unit_debug(u, "Failed to parse fifo value: %s", value);
|
||||||
else
|
else {
|
||||||
|
bool found = false;
|
||||||
|
|
||||||
LIST_FOREACH(port, p, s->ports)
|
LIST_FOREACH(port, p, s->ports)
|
||||||
if (p->type == SOCKET_FIFO &&
|
if (p->fd < 0 &&
|
||||||
|
p->type == SOCKET_FIFO &&
|
||||||
path_equal_or_files_same(p->path, value+skip, 0)) {
|
path_equal_or_files_same(p->path, value+skip, 0)) {
|
||||||
socket_port_take_fd(p, fds, fd);
|
p->fd = fdset_remove(fds, fd);
|
||||||
|
found = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (!found)
|
||||||
|
log_unit_debug(u, "No matching fifo socket found: %s", value+skip);
|
||||||
|
}
|
||||||
|
|
||||||
} else if (streq(key, "special")) {
|
} else if (streq(key, "special")) {
|
||||||
int fd, skip = 0;
|
int fd, skip = 0;
|
||||||
@ -2708,13 +2708,20 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value,
|
|||||||
|
|
||||||
if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd))
|
if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd))
|
||||||
log_unit_debug(u, "Failed to parse special value: %s", value);
|
log_unit_debug(u, "Failed to parse special value: %s", value);
|
||||||
else
|
else {
|
||||||
|
bool found = false;
|
||||||
|
|
||||||
LIST_FOREACH(port, p, s->ports)
|
LIST_FOREACH(port, p, s->ports)
|
||||||
if (p->type == SOCKET_SPECIAL &&
|
if (p->fd < 0 &&
|
||||||
|
p->type == SOCKET_SPECIAL &&
|
||||||
path_equal_or_files_same(p->path, value+skip, 0)) {
|
path_equal_or_files_same(p->path, value+skip, 0)) {
|
||||||
socket_port_take_fd(p, fds, fd);
|
p->fd = fdset_remove(fds, fd);
|
||||||
|
found = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (!found)
|
||||||
|
log_unit_debug(u, "No matching special socket found: %s", value+skip);
|
||||||
|
}
|
||||||
|
|
||||||
} else if (streq(key, "mqueue")) {
|
} else if (streq(key, "mqueue")) {
|
||||||
int fd, skip = 0;
|
int fd, skip = 0;
|
||||||
@ -2722,13 +2729,20 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value,
|
|||||||
|
|
||||||
if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd))
|
if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd))
|
||||||
log_unit_debug(u, "Failed to parse mqueue value: %s", value);
|
log_unit_debug(u, "Failed to parse mqueue value: %s", value);
|
||||||
else
|
else {
|
||||||
|
bool found = false;
|
||||||
|
|
||||||
LIST_FOREACH(port, p, s->ports)
|
LIST_FOREACH(port, p, s->ports)
|
||||||
if (p->type == SOCKET_MQUEUE &&
|
if (p->fd < 0 &&
|
||||||
|
p->type == SOCKET_MQUEUE &&
|
||||||
streq(p->path, value+skip)) {
|
streq(p->path, value+skip)) {
|
||||||
socket_port_take_fd(p, fds, fd);
|
p->fd = fdset_remove(fds, fd);
|
||||||
|
found = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (!found)
|
||||||
|
log_unit_debug(u, "No matching mqueue socket found: %s", value+skip);
|
||||||
|
}
|
||||||
|
|
||||||
} else if (streq(key, "socket")) {
|
} else if (streq(key, "socket")) {
|
||||||
int fd, type, skip = 0;
|
int fd, type, skip = 0;
|
||||||
@ -2736,12 +2750,20 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value,
|
|||||||
|
|
||||||
if (sscanf(value, "%i %i %n", &fd, &type, &skip) < 2 || fd < 0 || type < 0 || !fdset_contains(fds, fd))
|
if (sscanf(value, "%i %i %n", &fd, &type, &skip) < 2 || fd < 0 || type < 0 || !fdset_contains(fds, fd))
|
||||||
log_unit_debug(u, "Failed to parse socket value: %s", value);
|
log_unit_debug(u, "Failed to parse socket value: %s", value);
|
||||||
else
|
else {
|
||||||
|
bool found = false;
|
||||||
|
|
||||||
LIST_FOREACH(port, p, s->ports)
|
LIST_FOREACH(port, p, s->ports)
|
||||||
if (socket_address_is(&p->address, value+skip, type)) {
|
if (p->fd < 0 &&
|
||||||
socket_port_take_fd(p, fds, fd);
|
socket_address_is(&p->address, value+skip, type)) {
|
||||||
|
p->fd = fdset_remove(fds, fd);
|
||||||
|
found = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (!found)
|
||||||
|
log_unit_debug(u, "No matching %s socket found: %s",
|
||||||
|
socket_address_type_to_string(type), value+skip);
|
||||||
|
}
|
||||||
|
|
||||||
} else if (streq(key, "netlink")) {
|
} else if (streq(key, "netlink")) {
|
||||||
int fd, skip = 0;
|
int fd, skip = 0;
|
||||||
@ -2749,12 +2771,19 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value,
|
|||||||
|
|
||||||
if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd))
|
if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd))
|
||||||
log_unit_debug(u, "Failed to parse socket value: %s", value);
|
log_unit_debug(u, "Failed to parse socket value: %s", value);
|
||||||
else
|
else {
|
||||||
|
bool found = false;
|
||||||
|
|
||||||
LIST_FOREACH(port, p, s->ports)
|
LIST_FOREACH(port, p, s->ports)
|
||||||
if (socket_address_is_netlink(&p->address, value+skip)) {
|
if (p->fd < 0 &&
|
||||||
socket_port_take_fd(p, fds, fd);
|
socket_address_is_netlink(&p->address, value+skip)) {
|
||||||
|
p->fd = fdset_remove(fds, fd);
|
||||||
|
found = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (!found)
|
||||||
|
log_unit_debug(u, "No matching netlink socket found: %s", value+skip);
|
||||||
|
}
|
||||||
|
|
||||||
} else if (streq(key, "ffs")) {
|
} else if (streq(key, "ffs")) {
|
||||||
int fd, skip = 0;
|
int fd, skip = 0;
|
||||||
@ -2762,13 +2791,20 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value,
|
|||||||
|
|
||||||
if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd))
|
if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd))
|
||||||
log_unit_debug(u, "Failed to parse ffs value: %s", value);
|
log_unit_debug(u, "Failed to parse ffs value: %s", value);
|
||||||
else
|
else {
|
||||||
|
bool found = false;
|
||||||
|
|
||||||
LIST_FOREACH(port, p, s->ports)
|
LIST_FOREACH(port, p, s->ports)
|
||||||
if (p->type == SOCKET_USB_FUNCTION &&
|
if (p->fd < 0 &&
|
||||||
|
p->type == SOCKET_USB_FUNCTION &&
|
||||||
path_equal_or_files_same(p->path, value+skip, 0)) {
|
path_equal_or_files_same(p->path, value+skip, 0)) {
|
||||||
socket_port_take_fd(p, fds, fd);
|
p->fd = fdset_remove(fds, fd);
|
||||||
|
found = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (!found)
|
||||||
|
log_unit_debug(u, "No matching ffs socket found: %s", value+skip);
|
||||||
|
}
|
||||||
|
|
||||||
} else
|
} else
|
||||||
log_unit_debug(UNIT(s), "Unknown serialization key: %s", key);
|
log_unit_debug(UNIT(s), "Unknown serialization key: %s", key);
|
||||||
|
|||||||
@ -89,13 +89,25 @@ static const char *const io_accounting_metric_field_last[_CGROUP_IO_ACCOUNTING_M
|
|||||||
[CGROUP_IO_WRITE_OPERATIONS] = "io-accounting-write-operations-last",
|
[CGROUP_IO_WRITE_OPERATIONS] = "io-accounting-write-operations-last",
|
||||||
};
|
};
|
||||||
|
|
||||||
int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) {
|
int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool switching_root) {
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(u);
|
assert(u);
|
||||||
assert(f);
|
assert(f);
|
||||||
assert(fds);
|
assert(fds);
|
||||||
|
|
||||||
|
if (switching_root && UNIT_VTABLE(u)->exclude_from_switch_root_serialization) {
|
||||||
|
/* In the new root, paths for mounts and automounts will be different, so it doesn't make
|
||||||
|
* much sense to serialize things. API file systems will be moved to the new root, but we
|
||||||
|
* don't have mount units for those. */
|
||||||
|
log_unit_debug(u, "not serializing before switch-root");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Start marker */
|
||||||
|
fputs(u->id, f);
|
||||||
|
fputc('\n', f);
|
||||||
|
|
||||||
if (unit_can_serialize(u)) {
|
if (unit_can_serialize(u)) {
|
||||||
r = UNIT_VTABLE(u)->serialize(u, f, fds);
|
r = UNIT_VTABLE(u)->serialize(u, f, fds);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
@ -175,7 +187,7 @@ int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) {
|
|||||||
(void) serialize_item_format(f, ip_accounting_metric_field[m], "%" PRIu64, v);
|
(void) serialize_item_format(f, ip_accounting_metric_field[m], "%" PRIu64, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (serialize_jobs) {
|
if (!switching_root) {
|
||||||
if (u->job) {
|
if (u->job) {
|
||||||
fputs("job\n", f);
|
fputs("job\n", f);
|
||||||
job_serialize(u->job, f);
|
job_serialize(u->job, f);
|
||||||
|
|||||||
@ -543,8 +543,8 @@ typedef struct UnitVTable {
|
|||||||
|
|
||||||
bool (*can_reload)(Unit *u);
|
bool (*can_reload)(Unit *u);
|
||||||
|
|
||||||
/* Write all data that cannot be restored from other sources
|
/* Serialize state and file descriptors that should be carried over into the new
|
||||||
* away using unit_serialize_item() */
|
* instance after reexecution. */
|
||||||
int (*serialize)(Unit *u, FILE *f, FDSet *fds);
|
int (*serialize)(Unit *u, FILE *f, FDSet *fds);
|
||||||
|
|
||||||
/* Restore one item from the serialization */
|
/* Restore one item from the serialization */
|
||||||
@ -646,7 +646,7 @@ typedef struct UnitVTable {
|
|||||||
/* Type specific cleanups. */
|
/* Type specific cleanups. */
|
||||||
void (*shutdown)(Manager *m);
|
void (*shutdown)(Manager *m);
|
||||||
|
|
||||||
/* If this function is set and return false all jobs for units
|
/* If this function is set and returns false all jobs for units
|
||||||
* of this type will immediately fail. */
|
* of this type will immediately fail. */
|
||||||
bool (*supported)(void);
|
bool (*supported)(void);
|
||||||
|
|
||||||
@ -654,25 +654,28 @@ typedef struct UnitVTable {
|
|||||||
UnitStatusMessageFormats status_message_formats;
|
UnitStatusMessageFormats status_message_formats;
|
||||||
|
|
||||||
/* True if transient units of this type are OK */
|
/* True if transient units of this type are OK */
|
||||||
bool can_transient:1;
|
bool can_transient;
|
||||||
|
|
||||||
/* True if cgroup delegation is permissible */
|
/* True if cgroup delegation is permissible */
|
||||||
bool can_delegate:1;
|
bool can_delegate;
|
||||||
|
|
||||||
/* True if the unit type triggers other units, i.e. can have a UNIT_TRIGGERS dependency */
|
/* True if the unit type triggers other units, i.e. can have a UNIT_TRIGGERS dependency */
|
||||||
bool can_trigger:1;
|
bool can_trigger;
|
||||||
|
|
||||||
/* True if the unit type knows a failure state, and thus can be source of an OnFailure= dependency */
|
/* True if the unit type knows a failure state, and thus can be source of an OnFailure= dependency */
|
||||||
bool can_fail:1;
|
bool can_fail;
|
||||||
|
|
||||||
/* True if units of this type shall be startable only once and then never again */
|
/* True if units of this type shall be startable only once and then never again */
|
||||||
bool once_only:1;
|
bool once_only;
|
||||||
|
|
||||||
|
/* Do not serialize this unit when preparing for root switch */
|
||||||
|
bool exclude_from_switch_root_serialization;
|
||||||
|
|
||||||
/* True if queued jobs of this type should be GC'ed if no other job needs them anymore */
|
/* True if queued jobs of this type should be GC'ed if no other job needs them anymore */
|
||||||
bool gc_jobs:1;
|
bool gc_jobs;
|
||||||
|
|
||||||
/* True if systemd-oomd can monitor and act on this unit's recursive children's cgroup(s) */
|
/* True if systemd-oomd can monitor and act on this unit's recursive children's cgroup(s) */
|
||||||
bool can_set_managed_oom:1;
|
bool can_set_managed_oom;
|
||||||
} UnitVTable;
|
} UnitVTable;
|
||||||
|
|
||||||
extern const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX];
|
extern const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX];
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <sys/prctl.h>
|
#include <sys/prctl.h>
|
||||||
|
#include <sys/statvfs.h>
|
||||||
#include <sys/xattr.h>
|
#include <sys/xattr.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
@ -18,6 +19,7 @@
|
|||||||
|
|
||||||
#include "acl-util.h"
|
#include "acl-util.h"
|
||||||
#include "alloc-util.h"
|
#include "alloc-util.h"
|
||||||
|
#include "bus-error.h"
|
||||||
#include "capability-util.h"
|
#include "capability-util.h"
|
||||||
#include "cgroup-util.h"
|
#include "cgroup-util.h"
|
||||||
#include "compress.h"
|
#include "compress.h"
|
||||||
@ -42,6 +44,7 @@
|
|||||||
#include "socket-util.h"
|
#include "socket-util.h"
|
||||||
#include "special.h"
|
#include "special.h"
|
||||||
#include "stacktrace.h"
|
#include "stacktrace.h"
|
||||||
|
#include "stat-util.h"
|
||||||
#include "string-table.h"
|
#include "string-table.h"
|
||||||
#include "string-util.h"
|
#include "string-util.h"
|
||||||
#include "strv.h"
|
#include "strv.h"
|
||||||
@ -63,6 +66,10 @@
|
|||||||
#define JOURNAL_SIZE_MAX ((size_t) (10LU*1024LU*1024LU))
|
#define JOURNAL_SIZE_MAX ((size_t) (10LU*1024LU*1024LU))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* When checking for available memory and setting lower limits, don't
|
||||||
|
* go below 4MB for writing core files to storage. */
|
||||||
|
#define PROCESS_SIZE_MIN (4U*1024U*1024U)
|
||||||
|
|
||||||
/* Make sure to not make this larger than the maximum journal entry
|
/* Make sure to not make this larger than the maximum journal entry
|
||||||
* size. See DATA_SIZE_MAX in journal-importer.h. */
|
* size. See DATA_SIZE_MAX in journal-importer.h. */
|
||||||
assert_cc(JOURNAL_SIZE_MAX <= DATA_SIZE_MAX);
|
assert_cc(JOURNAL_SIZE_MAX <= DATA_SIZE_MAX);
|
||||||
@ -329,11 +336,14 @@ static int save_external_coredump(
|
|||||||
int *ret_node_fd,
|
int *ret_node_fd,
|
||||||
int *ret_data_fd,
|
int *ret_data_fd,
|
||||||
uint64_t *ret_size,
|
uint64_t *ret_size,
|
||||||
|
uint64_t *ret_compressed_size,
|
||||||
bool *ret_truncated) {
|
bool *ret_truncated) {
|
||||||
|
|
||||||
_cleanup_free_ char *fn = NULL, *tmp = NULL;
|
_cleanup_(unlink_and_freep) char *tmp = NULL;
|
||||||
|
_cleanup_free_ char *fn = NULL;
|
||||||
_cleanup_close_ int fd = -1;
|
_cleanup_close_ int fd = -1;
|
||||||
uint64_t rlimit, process_limit, max_size;
|
uint64_t rlimit, process_limit, max_size;
|
||||||
|
bool truncated, storage_on_tmpfs;
|
||||||
struct stat st;
|
struct stat st;
|
||||||
uid_t uid;
|
uid_t uid;
|
||||||
int r;
|
int r;
|
||||||
@ -343,6 +353,8 @@ static int save_external_coredump(
|
|||||||
assert(ret_node_fd);
|
assert(ret_node_fd);
|
||||||
assert(ret_data_fd);
|
assert(ret_data_fd);
|
||||||
assert(ret_size);
|
assert(ret_size);
|
||||||
|
assert(ret_compressed_size);
|
||||||
|
assert(ret_truncated);
|
||||||
|
|
||||||
r = parse_uid(context->meta[META_ARGV_UID], &uid);
|
r = parse_uid(context->meta[META_ARGV_UID], &uid);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
@ -379,92 +391,145 @@ static int save_external_coredump(
|
|||||||
if (fd < 0)
|
if (fd < 0)
|
||||||
return log_error_errno(fd, "Failed to create temporary file for coredump %s: %m", fn);
|
return log_error_errno(fd, "Failed to create temporary file for coredump %s: %m", fn);
|
||||||
|
|
||||||
r = copy_bytes(input_fd, fd, max_size, 0);
|
/* If storage is on tmpfs, the kernel oomd might kill us if there's MemoryMax set on
|
||||||
if (r < 0) {
|
* the service or the slice it belongs to. This is common on low-resources systems,
|
||||||
log_error_errno(r, "Cannot store coredump of %s (%s): %m",
|
* to avoid crashing processes to take away too many system resources.
|
||||||
context->meta[META_ARGV_PID], context->meta[META_COMM]);
|
* Check the cgroup settings, and set max_size to a bit less than half of the
|
||||||
goto fail;
|
* available memory left to the process.
|
||||||
|
* Then, attempt to write the core file uncompressed first - if the write gets
|
||||||
|
* interrupted, we know we won't be able to write it all, so instead compress what
|
||||||
|
* was written so far, delete the uncompressed truncated core, and then continue
|
||||||
|
* compressing from STDIN. Given the compressed core cannot be larger than the
|
||||||
|
* uncompressed one, and 1KB for metadata is accounted for in the calculation, we
|
||||||
|
* should be able to at least store the full compressed core file. */
|
||||||
|
|
||||||
|
storage_on_tmpfs = fd_is_temporary_fs(fd) > 0;
|
||||||
|
if (storage_on_tmpfs && arg_compress) {
|
||||||
|
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
||||||
|
uint64_t cgroup_limit = UINT64_MAX;
|
||||||
|
struct statvfs sv;
|
||||||
|
|
||||||
|
/* If we can't get the cgroup limit, just ignore it, but don't fail,
|
||||||
|
* try anyway with the config settings. */
|
||||||
|
r = sd_bus_default_system(&bus);
|
||||||
|
if (r < 0)
|
||||||
|
log_info_errno(r, "Failed to connect to system bus, skipping MemoryAvailable check: %m");
|
||||||
|
else {
|
||||||
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||||
|
|
||||||
|
r = sd_bus_get_property_trivial(
|
||||||
|
bus,
|
||||||
|
"org.freedesktop.systemd1",
|
||||||
|
"/org/freedesktop/systemd1/unit/self",
|
||||||
|
"org.freedesktop.systemd1.Service",
|
||||||
|
"MemoryAvailable",
|
||||||
|
&error,
|
||||||
|
't', &cgroup_limit);
|
||||||
|
if (r < 0)
|
||||||
|
log_warning_errno(r,
|
||||||
|
"Failed to query MemoryAvailable for current unit, "
|
||||||
|
"falling back to static config settings: %s",
|
||||||
|
bus_error_message(&error, r));
|
||||||
|
}
|
||||||
|
|
||||||
|
max_size = MIN(cgroup_limit, max_size);
|
||||||
|
max_size = LESS_BY(max_size, 1024U) / 2; /* Account for 1KB metadata overhead for compressing */
|
||||||
|
max_size = MAX(PROCESS_SIZE_MIN, max_size); /* Impose a lower minimum */
|
||||||
|
|
||||||
|
/* tmpfs might get full quickly, so check the available space too.
|
||||||
|
* But don't worry about errors here, failing to access the storage
|
||||||
|
* location will be better logged when writing to it. */
|
||||||
|
if (statvfs("/var/lib/systemd/coredump/", &sv) >= 0)
|
||||||
|
max_size = MIN((uint64_t)sv.f_frsize * (uint64_t)sv.f_bfree, max_size);
|
||||||
|
|
||||||
|
log_debug("Limiting core file size to %" PRIu64 " bytes due to cgroup memory limits.", max_size);
|
||||||
}
|
}
|
||||||
*ret_truncated = r == 1;
|
|
||||||
if (*ret_truncated)
|
r = copy_bytes(input_fd, fd, max_size, 0);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Cannot store coredump of %s (%s): %m",
|
||||||
|
context->meta[META_ARGV_PID], context->meta[META_COMM]);
|
||||||
|
truncated = r == 1;
|
||||||
|
|
||||||
|
#if HAVE_COMPRESSION
|
||||||
|
if (arg_compress) {
|
||||||
|
_cleanup_(unlink_and_freep) char *tmp_compressed = NULL;
|
||||||
|
_cleanup_free_ char *fn_compressed = NULL;
|
||||||
|
_cleanup_close_ int fd_compressed = -1;
|
||||||
|
uint64_t uncompressed_size = 0;
|
||||||
|
|
||||||
|
if (lseek(fd, 0, SEEK_SET) == (off_t) -1)
|
||||||
|
return log_error_errno(errno, "Failed to seek on coredump %s: %m", fn);
|
||||||
|
|
||||||
|
fn_compressed = strjoin(fn, COMPRESSED_EXT);
|
||||||
|
if (!fn_compressed)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
|
fd_compressed = open_tmpfile_linkable(fn_compressed, O_RDWR|O_CLOEXEC, &tmp_compressed);
|
||||||
|
if (fd_compressed < 0)
|
||||||
|
return log_error_errno(fd_compressed, "Failed to create temporary file for coredump %s: %m", fn_compressed);
|
||||||
|
|
||||||
|
r = compress_stream(fd, fd_compressed, max_size, &uncompressed_size);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to compress %s: %m", coredump_tmpfile_name(tmp_compressed));
|
||||||
|
|
||||||
|
if (truncated && storage_on_tmpfs) {
|
||||||
|
uint64_t partial_uncompressed_size = 0;
|
||||||
|
|
||||||
|
/* Uncompressed write was truncated and we are writing to tmpfs: delete
|
||||||
|
* the uncompressed core, and compress the remaining part from STDIN. */
|
||||||
|
|
||||||
|
tmp = unlink_and_free(tmp);
|
||||||
|
fd = safe_close(fd);
|
||||||
|
|
||||||
|
r = compress_stream(input_fd, fd_compressed, max_size, &partial_uncompressed_size);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to compress %s: %m", coredump_tmpfile_name(tmp_compressed));
|
||||||
|
uncompressed_size += partial_uncompressed_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = fix_permissions(fd_compressed, tmp_compressed, fn_compressed, context, uid);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
if (fstat(fd_compressed, &st) < 0)
|
||||||
|
return log_error_errno(errno,
|
||||||
|
"Failed to fstat core file %s: %m",
|
||||||
|
coredump_tmpfile_name(tmp_compressed));
|
||||||
|
|
||||||
|
*ret_filename = TAKE_PTR(fn_compressed); /* compressed */
|
||||||
|
*ret_node_fd = TAKE_FD(fd_compressed); /* compressed */
|
||||||
|
*ret_compressed_size = (uint64_t) st.st_size; /* compressed */
|
||||||
|
*ret_data_fd = TAKE_FD(fd);
|
||||||
|
*ret_size = uncompressed_size;
|
||||||
|
*ret_truncated = truncated;
|
||||||
|
tmp_compressed = mfree(tmp_compressed);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (truncated)
|
||||||
log_struct(LOG_INFO,
|
log_struct(LOG_INFO,
|
||||||
LOG_MESSAGE("Core file was truncated to %zu bytes.", max_size),
|
LOG_MESSAGE("Core file was truncated to %zu bytes.", max_size),
|
||||||
"SIZE_LIMIT=%zu", max_size,
|
"SIZE_LIMIT=%zu", max_size,
|
||||||
"MESSAGE_ID=" SD_MESSAGE_TRUNCATED_CORE_STR);
|
"MESSAGE_ID=" SD_MESSAGE_TRUNCATED_CORE_STR);
|
||||||
|
|
||||||
if (fstat(fd, &st) < 0) {
|
|
||||||
log_error_errno(errno, "Failed to fstat core file %s: %m", coredump_tmpfile_name(tmp));
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lseek(fd, 0, SEEK_SET) == (off_t) -1) {
|
|
||||||
log_error_errno(errno, "Failed to seek on %s: %m", coredump_tmpfile_name(tmp));
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if HAVE_COMPRESSION
|
|
||||||
/* If we will remove the coredump anyway, do not compress. */
|
|
||||||
if (arg_compress && !maybe_remove_external_coredump(NULL, st.st_size)) {
|
|
||||||
|
|
||||||
_cleanup_free_ char *fn_compressed = NULL, *tmp_compressed = NULL;
|
|
||||||
_cleanup_close_ int fd_compressed = -1;
|
|
||||||
|
|
||||||
fn_compressed = strjoin(fn, COMPRESSED_EXT);
|
|
||||||
if (!fn_compressed) {
|
|
||||||
log_oom();
|
|
||||||
goto uncompressed;
|
|
||||||
}
|
|
||||||
|
|
||||||
fd_compressed = open_tmpfile_linkable(fn_compressed, O_RDWR|O_CLOEXEC, &tmp_compressed);
|
|
||||||
if (fd_compressed < 0) {
|
|
||||||
log_error_errno(fd_compressed, "Failed to create temporary file for coredump %s: %m", fn_compressed);
|
|
||||||
goto uncompressed;
|
|
||||||
}
|
|
||||||
|
|
||||||
r = compress_stream(fd, fd_compressed, -1);
|
|
||||||
if (r < 0) {
|
|
||||||
log_error_errno(r, "Failed to compress %s: %m", coredump_tmpfile_name(tmp_compressed));
|
|
||||||
goto fail_compressed;
|
|
||||||
}
|
|
||||||
|
|
||||||
r = fix_permissions(fd_compressed, tmp_compressed, fn_compressed, context, uid);
|
|
||||||
if (r < 0)
|
|
||||||
goto fail_compressed;
|
|
||||||
|
|
||||||
/* OK, this worked, we can get rid of the uncompressed version now */
|
|
||||||
if (tmp)
|
|
||||||
unlink_noerrno(tmp);
|
|
||||||
|
|
||||||
*ret_filename = TAKE_PTR(fn_compressed); /* compressed */
|
|
||||||
*ret_node_fd = TAKE_FD(fd_compressed); /* compressed */
|
|
||||||
*ret_data_fd = TAKE_FD(fd); /* uncompressed */
|
|
||||||
*ret_size = (uint64_t) st.st_size; /* uncompressed */
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
fail_compressed:
|
|
||||||
if (tmp_compressed)
|
|
||||||
(void) unlink(tmp_compressed);
|
|
||||||
}
|
|
||||||
|
|
||||||
uncompressed:
|
|
||||||
#endif
|
|
||||||
|
|
||||||
r = fix_permissions(fd, tmp, fn, context, uid);
|
r = fix_permissions(fd, tmp, fn, context, uid);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
goto fail;
|
return log_error_errno(r, "Failed to fix permissions and finalize coredump %s into %s: %m", coredump_tmpfile_name(tmp), fn);
|
||||||
|
|
||||||
|
if (fstat(fd, &st) < 0)
|
||||||
|
return log_error_errno(errno, "Failed to fstat core file %s: %m", coredump_tmpfile_name(tmp));
|
||||||
|
|
||||||
|
if (lseek(fd, 0, SEEK_SET) == (off_t) -1)
|
||||||
|
return log_error_errno(errno, "Failed to seek on coredump %s: %m", fn);
|
||||||
|
|
||||||
*ret_filename = TAKE_PTR(fn);
|
|
||||||
*ret_data_fd = TAKE_FD(fd);
|
*ret_data_fd = TAKE_FD(fd);
|
||||||
*ret_node_fd = -1;
|
|
||||||
*ret_size = (uint64_t) st.st_size;
|
*ret_size = (uint64_t) st.st_size;
|
||||||
|
*ret_truncated = truncated;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
fail:
|
|
||||||
if (tmp)
|
|
||||||
(void) unlink(tmp);
|
|
||||||
return r;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int allocate_journal_field(int fd, size_t size, char **ret, size_t *ret_size) {
|
static int allocate_journal_field(int fd, size_t size, char **ret, size_t *ret_size) {
|
||||||
@ -709,7 +774,7 @@ static int submit_coredump(
|
|||||||
_cleanup_free_ char *stacktrace = NULL;
|
_cleanup_free_ char *stacktrace = NULL;
|
||||||
char *core_message;
|
char *core_message;
|
||||||
const char *module_name;
|
const char *module_name;
|
||||||
uint64_t coredump_size = UINT64_MAX;
|
uint64_t coredump_size = UINT64_MAX, coredump_compressed_size = UINT64_MAX;
|
||||||
bool truncated = false;
|
bool truncated = false;
|
||||||
JsonVariant *module_json;
|
JsonVariant *module_json;
|
||||||
int r;
|
int r;
|
||||||
@ -722,7 +787,8 @@ static int submit_coredump(
|
|||||||
|
|
||||||
/* Always stream the coredump to disk, if that's possible */
|
/* Always stream the coredump to disk, if that's possible */
|
||||||
r = save_external_coredump(context, input_fd,
|
r = save_external_coredump(context, input_fd,
|
||||||
&filename, &coredump_node_fd, &coredump_fd, &coredump_size, &truncated);
|
&filename, &coredump_node_fd, &coredump_fd,
|
||||||
|
&coredump_size, &coredump_compressed_size, &truncated);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
/* Skip whole core dumping part */
|
/* Skip whole core dumping part */
|
||||||
goto log;
|
goto log;
|
||||||
@ -730,7 +796,7 @@ static int submit_coredump(
|
|||||||
/* If we don't want to keep the coredump on disk, remove it now, as later on we
|
/* If we don't want to keep the coredump on disk, remove it now, as later on we
|
||||||
* will lack the privileges for it. However, we keep the fd to it, so that we can
|
* will lack the privileges for it. However, we keep the fd to it, so that we can
|
||||||
* still process it and log it. */
|
* still process it and log it. */
|
||||||
r = maybe_remove_external_coredump(filename, coredump_size);
|
r = maybe_remove_external_coredump(filename, coredump_node_fd >= 0 ? coredump_compressed_size : coredump_size);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
if (r == 0) {
|
if (r == 0) {
|
||||||
@ -738,7 +804,7 @@ static int submit_coredump(
|
|||||||
|
|
||||||
} else if (arg_storage == COREDUMP_STORAGE_EXTERNAL)
|
} else if (arg_storage == COREDUMP_STORAGE_EXTERNAL)
|
||||||
log_info("The core will not be stored: size %"PRIu64" is greater than %"PRIu64" (the configured maximum)",
|
log_info("The core will not be stored: size %"PRIu64" is greater than %"PRIu64" (the configured maximum)",
|
||||||
coredump_size, arg_external_size_max);
|
coredump_node_fd >= 0 ? coredump_compressed_size : coredump_size, arg_external_size_max);
|
||||||
|
|
||||||
/* Vacuum again, but exclude the coredump we just created */
|
/* Vacuum again, but exclude the coredump we just created */
|
||||||
(void) coredump_vacuum(coredump_node_fd >= 0 ? coredump_node_fd : coredump_fd, arg_keep_free, arg_max_use);
|
(void) coredump_vacuum(coredump_node_fd >= 0 ? coredump_node_fd : coredump_fd, arg_keep_free, arg_max_use);
|
||||||
@ -758,7 +824,7 @@ static int submit_coredump(
|
|||||||
log_debug("Not generating stack trace: core size %"PRIu64" is greater "
|
log_debug("Not generating stack trace: core size %"PRIu64" is greater "
|
||||||
"than %"PRIu64" (the configured maximum)",
|
"than %"PRIu64" (the configured maximum)",
|
||||||
coredump_size, arg_process_size_max);
|
coredump_size, arg_process_size_max);
|
||||||
} else
|
} else if (coredump_fd >= 0)
|
||||||
coredump_parse_core(coredump_fd, context->meta[META_EXE], &stacktrace, &json_metadata);
|
coredump_parse_core(coredump_fd, context->meta[META_EXE], &stacktrace, &json_metadata);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -812,7 +878,7 @@ log:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Optionally store the entire coredump in the journal */
|
/* Optionally store the entire coredump in the journal */
|
||||||
if (arg_storage == COREDUMP_STORAGE_JOURNAL) {
|
if (arg_storage == COREDUMP_STORAGE_JOURNAL && coredump_fd >= 0) {
|
||||||
if (coredump_size <= arg_journal_size_max) {
|
if (coredump_size <= arg_journal_size_max) {
|
||||||
size_t sz = 0;
|
size_t sz = 0;
|
||||||
|
|
||||||
|
|||||||
@ -550,7 +550,7 @@ int decompress_startswith(
|
|||||||
return -EBADMSG;
|
return -EBADMSG;
|
||||||
}
|
}
|
||||||
|
|
||||||
int compress_stream_xz(int fdf, int fdt, uint64_t max_bytes) {
|
int compress_stream_xz(int fdf, int fdt, uint64_t max_bytes, uint64_t *ret_uncompressed_size) {
|
||||||
#if HAVE_XZ
|
#if HAVE_XZ
|
||||||
_cleanup_(lzma_end) lzma_stream s = LZMA_STREAM_INIT;
|
_cleanup_(lzma_end) lzma_stream s = LZMA_STREAM_INIT;
|
||||||
lzma_ret ret;
|
lzma_ret ret;
|
||||||
@ -611,6 +611,9 @@ int compress_stream_xz(int fdf, int fdt, uint64_t max_bytes) {
|
|||||||
return k;
|
return k;
|
||||||
|
|
||||||
if (ret == LZMA_STREAM_END) {
|
if (ret == LZMA_STREAM_END) {
|
||||||
|
if (ret_uncompressed_size)
|
||||||
|
*ret_uncompressed_size = s.total_in;
|
||||||
|
|
||||||
log_debug("XZ compression finished (%"PRIu64" -> %"PRIu64" bytes, %.1f%%)",
|
log_debug("XZ compression finished (%"PRIu64" -> %"PRIu64" bytes, %.1f%%)",
|
||||||
s.total_in, s.total_out,
|
s.total_in, s.total_out,
|
||||||
(double) s.total_out / s.total_in * 100);
|
(double) s.total_out / s.total_in * 100);
|
||||||
@ -626,14 +629,15 @@ int compress_stream_xz(int fdf, int fdt, uint64_t max_bytes) {
|
|||||||
|
|
||||||
#define LZ4_BUFSIZE (512*1024u)
|
#define LZ4_BUFSIZE (512*1024u)
|
||||||
|
|
||||||
int compress_stream_lz4(int fdf, int fdt, uint64_t max_bytes) {
|
int compress_stream_lz4(int fdf, int fdt, uint64_t max_bytes, uint64_t *ret_uncompressed_size) {
|
||||||
|
|
||||||
#if HAVE_LZ4
|
#if HAVE_LZ4
|
||||||
LZ4F_errorCode_t c;
|
LZ4F_errorCode_t c;
|
||||||
_cleanup_(LZ4F_freeCompressionContextp) LZ4F_compressionContext_t ctx = NULL;
|
_cleanup_(LZ4F_freeCompressionContextp) LZ4F_compressionContext_t ctx = NULL;
|
||||||
_cleanup_free_ void *in_buff = NULL;
|
_cleanup_free_ void *in_buff = NULL;
|
||||||
_cleanup_free_ char *out_buff = NULL;
|
_cleanup_free_ char *out_buff = NULL;
|
||||||
size_t out_allocsize, n, total_in = 0, total_out, offset = 0, frame_size;
|
size_t out_allocsize, n, offset = 0, frame_size;
|
||||||
|
uint64_t total_in = 0, total_out;
|
||||||
int r;
|
int r;
|
||||||
static const LZ4F_preferences_t preferences = {
|
static const LZ4F_preferences_t preferences = {
|
||||||
.frameInfo.blockSizeID = 5,
|
.frameInfo.blockSizeID = 5,
|
||||||
@ -698,7 +702,10 @@ int compress_stream_lz4(int fdf, int fdt, uint64_t max_bytes) {
|
|||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
log_debug("LZ4 compression finished (%zu -> %zu bytes, %.1f%%)",
|
if (ret_uncompressed_size)
|
||||||
|
*ret_uncompressed_size = total_in;
|
||||||
|
|
||||||
|
log_debug("LZ4 compression finished (%" PRIu64 " -> %" PRIu64 " bytes, %.1f%%)",
|
||||||
total_in, total_out,
|
total_in, total_out,
|
||||||
(double) total_out / total_in * 100);
|
(double) total_out / total_in * 100);
|
||||||
|
|
||||||
@ -844,7 +851,7 @@ int decompress_stream_lz4(int in, int out, uint64_t max_bytes) {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
int compress_stream_zstd(int fdf, int fdt, uint64_t max_bytes) {
|
int compress_stream_zstd(int fdf, int fdt, uint64_t max_bytes, uint64_t *ret_uncompressed_size) {
|
||||||
#if HAVE_ZSTD
|
#if HAVE_ZSTD
|
||||||
_cleanup_(ZSTD_freeCCtxp) ZSTD_CCtx *cctx = NULL;
|
_cleanup_(ZSTD_freeCCtxp) ZSTD_CCtx *cctx = NULL;
|
||||||
_cleanup_free_ void *in_buff = NULL, *out_buff = NULL;
|
_cleanup_free_ void *in_buff = NULL, *out_buff = NULL;
|
||||||
@ -933,6 +940,9 @@ int compress_stream_zstd(int fdf, int fdt, uint64_t max_bytes) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ret_uncompressed_size)
|
||||||
|
*ret_uncompressed_size = in_bytes;
|
||||||
|
|
||||||
if (in_bytes > 0)
|
if (in_bytes > 0)
|
||||||
log_debug("ZSTD compression finished (%" PRIu64 " -> %" PRIu64 " bytes, %.1f%%)",
|
log_debug("ZSTD compression finished (%" PRIu64 " -> %" PRIu64 " bytes, %.1f%%)",
|
||||||
in_bytes, max_bytes - left, (double) (max_bytes - left) / in_bytes * 100);
|
in_bytes, max_bytes - left, (double) (max_bytes - left) / in_bytes * 100);
|
||||||
|
|||||||
@ -64,9 +64,9 @@ int decompress_startswith(int compression,
|
|||||||
const void *prefix, size_t prefix_len,
|
const void *prefix, size_t prefix_len,
|
||||||
uint8_t extra);
|
uint8_t extra);
|
||||||
|
|
||||||
int compress_stream_xz(int fdf, int fdt, uint64_t max_bytes);
|
int compress_stream_xz(int fdf, int fdt, uint64_t max_bytes, uint64_t *ret_uncompressed_size);
|
||||||
int compress_stream_lz4(int fdf, int fdt, uint64_t max_bytes);
|
int compress_stream_lz4(int fdf, int fdt, uint64_t max_bytes, uint64_t *ret_uncompressed_size);
|
||||||
int compress_stream_zstd(int fdf, int fdt, uint64_t max_bytes);
|
int compress_stream_zstd(int fdf, int fdt, uint64_t max_bytes, uint64_t *ret_uncompressed_size);
|
||||||
|
|
||||||
int decompress_stream_xz(int fdf, int fdt, uint64_t max_size);
|
int decompress_stream_xz(int fdf, int fdt, uint64_t max_size);
|
||||||
int decompress_stream_lz4(int fdf, int fdt, uint64_t max_size);
|
int decompress_stream_lz4(int fdf, int fdt, uint64_t max_size);
|
||||||
@ -82,7 +82,7 @@ int decompress_stream_zstd(int fdf, int fdt, uint64_t max_size);
|
|||||||
# define compress_stream compress_stream_xz
|
# define compress_stream compress_stream_xz
|
||||||
# define COMPRESSED_EXT ".xz"
|
# define COMPRESSED_EXT ".xz"
|
||||||
#else
|
#else
|
||||||
static inline int compress_stream(int fdf, int fdt, uint64_t max_size) {
|
static inline int compress_stream(int fdf, int fdt, uint64_t max_size, uint64_t *ret_uncompressed_size) {
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
}
|
}
|
||||||
# define COMPRESSED_EXT ""
|
# define COMPRESSED_EXT ""
|
||||||
|
|||||||
@ -41,7 +41,7 @@ typedef int (decompress_sw_t)(const void *src, uint64_t src_size,
|
|||||||
const void *prefix, size_t prefix_len,
|
const void *prefix, size_t prefix_len,
|
||||||
uint8_t extra);
|
uint8_t extra);
|
||||||
|
|
||||||
typedef int (compress_stream_t)(int fdf, int fdt, uint64_t max_bytes);
|
typedef int (compress_stream_t)(int fdf, int fdt, uint64_t max_bytes, uint64_t *uncompressed_size);
|
||||||
typedef int (decompress_stream_t)(int fdf, int fdt, uint64_t max_size);
|
typedef int (decompress_stream_t)(int fdf, int fdt, uint64_t max_size);
|
||||||
|
|
||||||
#if HAVE_COMPRESSION
|
#if HAVE_COMPRESSION
|
||||||
@ -176,6 +176,7 @@ _unused_ static void test_compress_stream(const char *compression,
|
|||||||
int r;
|
int r;
|
||||||
_cleanup_free_ char *cmd = NULL, *cmd2 = NULL;
|
_cleanup_free_ char *cmd = NULL, *cmd2 = NULL;
|
||||||
struct stat st = {};
|
struct stat st = {};
|
||||||
|
uint64_t uncompressed_size;
|
||||||
|
|
||||||
r = find_executable(cat, NULL);
|
r = find_executable(cat, NULL);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
@ -193,7 +194,7 @@ _unused_ static void test_compress_stream(const char *compression,
|
|||||||
|
|
||||||
assert_se((dst = mkostemp_safe(pattern)) >= 0);
|
assert_se((dst = mkostemp_safe(pattern)) >= 0);
|
||||||
|
|
||||||
assert_se(compress(src, dst, -1) == 0);
|
assert_se(compress(src, dst, -1, &uncompressed_size) == 0);
|
||||||
|
|
||||||
if (cat) {
|
if (cat) {
|
||||||
assert_se(asprintf(&cmd, "%s %s | diff %s -", cat, pattern, srcfile) > 0);
|
assert_se(asprintf(&cmd, "%s %s | diff %s -", cat, pattern, srcfile) > 0);
|
||||||
@ -205,6 +206,7 @@ _unused_ static void test_compress_stream(const char *compression,
|
|||||||
assert_se((dst2 = mkostemp_safe(pattern2)) >= 0);
|
assert_se((dst2 = mkostemp_safe(pattern2)) >= 0);
|
||||||
|
|
||||||
assert_se(stat(srcfile, &st) == 0);
|
assert_se(stat(srcfile, &st) == 0);
|
||||||
|
assert_se((uint64_t)st.st_size == uncompressed_size);
|
||||||
|
|
||||||
assert_se(lseek(dst, 0, SEEK_SET) == 0);
|
assert_se(lseek(dst, 0, SEEK_SET) == 0);
|
||||||
r = decompress(dst, dst2, st.st_size);
|
r = decompress(dst, dst2, st.st_size);
|
||||||
|
|||||||
@ -37,7 +37,6 @@ int main(int argc, char **argv) {
|
|||||||
test_table(netdev_kind, NETDEV_KIND);
|
test_table(netdev_kind, NETDEV_KIND);
|
||||||
test_table(nl_union_link_info_data, NL_UNION_LINK_INFO_DATA);
|
test_table(nl_union_link_info_data, NL_UNION_LINK_INFO_DATA);
|
||||||
test_table(radv_prefix_delegation, RADV_PREFIX_DELEGATION);
|
test_table(radv_prefix_delegation, RADV_PREFIX_DELEGATION);
|
||||||
test_table(wol, WOL);
|
|
||||||
test_table(lldp_event, SD_LLDP_EVENT);
|
test_table(lldp_event, SD_LLDP_EVENT);
|
||||||
test_table(ndisc_event, SD_NDISC_EVENT);
|
test_table(ndisc_event, SD_NDISC_EVENT);
|
||||||
test_table(dhcp_lease_server_type, SD_DHCP_LEASE_SERVER_TYPE);
|
test_table(dhcp_lease_server_type, SD_DHCP_LEASE_SERVER_TYPE);
|
||||||
|
|||||||
@ -165,7 +165,7 @@ static int bus_print_property(const char *name, const char *expected_value, sd_b
|
|||||||
|
|
||||||
bus_print_property_value(name, expected_value, flags, "[not set]");
|
bus_print_property_value(name, expected_value, flags, "[not set]");
|
||||||
|
|
||||||
else if ((STR_IN_SET(name, "DefaultMemoryLow", "DefaultMemoryMin", "MemoryLow", "MemoryHigh", "MemoryMax", "MemorySwapMax", "MemoryLimit") && u == CGROUP_LIMIT_MAX) ||
|
else if ((STR_IN_SET(name, "DefaultMemoryLow", "DefaultMemoryMin", "MemoryLow", "MemoryHigh", "MemoryMax", "MemorySwapMax", "MemoryLimit", "MemoryAvailable") && u == CGROUP_LIMIT_MAX) ||
|
||||||
(STR_IN_SET(name, "TasksMax", "DefaultTasksMax") && u == UINT64_MAX) ||
|
(STR_IN_SET(name, "TasksMax", "DefaultTasksMax") && u == UINT64_MAX) ||
|
||||||
(startswith(name, "Limit") && u == UINT64_MAX) ||
|
(startswith(name, "Limit") && u == UINT64_MAX) ||
|
||||||
(startswith(name, "DefaultLimit") && u == UINT64_MAX))
|
(startswith(name, "DefaultLimit") && u == UINT64_MAX))
|
||||||
|
|||||||
@ -24,19 +24,38 @@ static const char* const duplex_table[_DUP_MAX] = {
|
|||||||
DEFINE_STRING_TABLE_LOOKUP(duplex, Duplex);
|
DEFINE_STRING_TABLE_LOOKUP(duplex, Duplex);
|
||||||
DEFINE_CONFIG_PARSE_ENUM(config_parse_duplex, duplex, Duplex, "Failed to parse duplex setting");
|
DEFINE_CONFIG_PARSE_ENUM(config_parse_duplex, duplex, Duplex, "Failed to parse duplex setting");
|
||||||
|
|
||||||
static const char* const wol_table[_WOL_MAX] = {
|
static const struct {
|
||||||
[WOL_PHY] = "phy",
|
uint32_t opt;
|
||||||
[WOL_UCAST] = "unicast",
|
const char *name;
|
||||||
[WOL_MCAST] = "multicast",
|
} wol_option_map[] = {
|
||||||
[WOL_BCAST] = "broadcast",
|
{ WAKE_PHY, "phy" },
|
||||||
[WOL_ARP] = "arp",
|
{ WAKE_UCAST, "unicast", },
|
||||||
[WOL_MAGIC] = "magic",
|
{ WAKE_MCAST, "multicast", },
|
||||||
[WOL_MAGICSECURE] = "secureon",
|
{ WAKE_BCAST, "broadcast", },
|
||||||
[WOL_OFF] = "off",
|
{ WAKE_ARP, "arp", },
|
||||||
|
{ WAKE_MAGIC, "magic", },
|
||||||
|
{ WAKE_MAGICSECURE, "secureon", },
|
||||||
};
|
};
|
||||||
|
|
||||||
DEFINE_STRING_TABLE_LOOKUP(wol, WakeOnLan);
|
int wol_options_to_string_alloc(uint32_t opts, char **ret) {
|
||||||
DEFINE_CONFIG_PARSE_ENUM(config_parse_wol, wol, WakeOnLan, "Failed to parse WakeOnLan setting");
|
_cleanup_free_ char *str = NULL;
|
||||||
|
|
||||||
|
assert(ret);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < ELEMENTSOF(wol_option_map); i++)
|
||||||
|
if (opts & wol_option_map[i].opt &&
|
||||||
|
!strextend_with_separator(&str, ",", wol_option_map[i].name))
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
if (!str) {
|
||||||
|
str = strdup("off");
|
||||||
|
if (!str)
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
*ret = TAKE_PTR(str);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static const char* const port_table[] = {
|
static const char* const port_table[] = {
|
||||||
[NET_DEV_PORT_TP] = "tp",
|
[NET_DEV_PORT_TP] = "tp",
|
||||||
@ -310,7 +329,7 @@ int ethtool_get_permanent_macaddr(int *ethtool_fd, const char *ifname, struct et
|
|||||||
dest = _v; \
|
dest = _v; \
|
||||||
} while(false)
|
} while(false)
|
||||||
|
|
||||||
int ethtool_set_wol(int *ethtool_fd, const char *ifname, WakeOnLan wol) {
|
int ethtool_set_wol(int *ethtool_fd, const char *ifname, uint32_t wolopts) {
|
||||||
struct ethtool_wolinfo ecmd = {
|
struct ethtool_wolinfo ecmd = {
|
||||||
.cmd = ETHTOOL_GWOL,
|
.cmd = ETHTOOL_GWOL,
|
||||||
};
|
};
|
||||||
@ -323,7 +342,7 @@ int ethtool_set_wol(int *ethtool_fd, const char *ifname, WakeOnLan wol) {
|
|||||||
assert(ethtool_fd);
|
assert(ethtool_fd);
|
||||||
assert(ifname);
|
assert(ifname);
|
||||||
|
|
||||||
if (wol == _WOL_INVALID)
|
if (wolopts == UINT32_MAX)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
r = ethtool_connect(ethtool_fd);
|
r = ethtool_connect(ethtool_fd);
|
||||||
@ -336,66 +355,15 @@ int ethtool_set_wol(int *ethtool_fd, const char *ifname, WakeOnLan wol) {
|
|||||||
if (r < 0)
|
if (r < 0)
|
||||||
return -errno;
|
return -errno;
|
||||||
|
|
||||||
switch (wol) {
|
UPDATE(ecmd.wolopts, wolopts, need_update);
|
||||||
case WOL_PHY:
|
|
||||||
if (ecmd.wolopts != WAKE_PHY) {
|
|
||||||
ecmd.wolopts = WAKE_PHY;
|
|
||||||
need_update = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case WOL_UCAST:
|
|
||||||
if (ecmd.wolopts != WAKE_UCAST) {
|
|
||||||
ecmd.wolopts = WAKE_UCAST;
|
|
||||||
need_update = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case WOL_MCAST:
|
|
||||||
if (ecmd.wolopts != WAKE_MCAST) {
|
|
||||||
ecmd.wolopts = WAKE_MCAST;
|
|
||||||
need_update = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case WOL_BCAST:
|
|
||||||
if (ecmd.wolopts != WAKE_BCAST) {
|
|
||||||
ecmd.wolopts = WAKE_BCAST;
|
|
||||||
need_update = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case WOL_ARP:
|
|
||||||
if (ecmd.wolopts != WAKE_ARP) {
|
|
||||||
ecmd.wolopts = WAKE_ARP;
|
|
||||||
need_update = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case WOL_MAGIC:
|
|
||||||
if (ecmd.wolopts != WAKE_MAGIC) {
|
|
||||||
ecmd.wolopts = WAKE_MAGIC;
|
|
||||||
need_update = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case WOL_MAGICSECURE:
|
|
||||||
if (ecmd.wolopts != WAKE_MAGICSECURE) {
|
|
||||||
ecmd.wolopts = WAKE_MAGICSECURE;
|
|
||||||
need_update = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case WOL_OFF:
|
|
||||||
if (ecmd.wolopts != 0) {
|
|
||||||
ecmd.wolopts = 0;
|
|
||||||
need_update = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (need_update) {
|
if (!need_update)
|
||||||
ecmd.cmd = ETHTOOL_SWOL;
|
return 0;
|
||||||
|
|
||||||
r = ioctl(*ethtool_fd, SIOCETHTOOL, &ifr);
|
ecmd.cmd = ETHTOOL_SWOL;
|
||||||
if (r < 0)
|
r = ioctl(*ethtool_fd, SIOCETHTOOL, &ifr);
|
||||||
return -errno;
|
if (r < 0)
|
||||||
}
|
return -errno;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -1005,7 +973,6 @@ int config_parse_advertise(
|
|||||||
void *userdata) {
|
void *userdata) {
|
||||||
|
|
||||||
uint32_t *advertise = data;
|
uint32_t *advertise = data;
|
||||||
const char *p;
|
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(filename);
|
assert(filename);
|
||||||
@ -1020,7 +987,7 @@ int config_parse_advertise(
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (p = rvalue;;) {
|
for (const char *p = rvalue;;) {
|
||||||
_cleanup_free_ char *w = NULL;
|
_cleanup_free_ char *w = NULL;
|
||||||
enum ethtool_link_mode_bit_indices mode;
|
enum ethtool_link_mode_bit_indices mode;
|
||||||
|
|
||||||
@ -1098,3 +1065,69 @@ int config_parse_nic_buffer_size(
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int config_parse_wol(
|
||||||
|
const char *unit,
|
||||||
|
const char *filename,
|
||||||
|
unsigned line,
|
||||||
|
const char *section,
|
||||||
|
unsigned section_line,
|
||||||
|
const char *lvalue,
|
||||||
|
int ltype,
|
||||||
|
const char *rvalue,
|
||||||
|
void *data,
|
||||||
|
void *userdata) {
|
||||||
|
|
||||||
|
uint32_t new_opts = 0, *opts = data;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(filename);
|
||||||
|
assert(section);
|
||||||
|
assert(lvalue);
|
||||||
|
assert(rvalue);
|
||||||
|
assert(data);
|
||||||
|
|
||||||
|
if (isempty(rvalue)) {
|
||||||
|
*opts = UINT32_MAX; /* Do not update WOL option. */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (streq(rvalue, "off")) {
|
||||||
|
*opts = 0; /* Disable WOL. */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const char *p = rvalue;;) {
|
||||||
|
_cleanup_free_ char *w = NULL;
|
||||||
|
bool found = false;
|
||||||
|
|
||||||
|
r = extract_first_word(&p, &w, NULL, 0);
|
||||||
|
if (r == -ENOMEM)
|
||||||
|
return log_oom();
|
||||||
|
if (r < 0) {
|
||||||
|
log_syntax(unit, LOG_WARNING, filename, line, r,
|
||||||
|
"Failed to split wake-on-lan modes '%s', ignoring assignment: %m", rvalue);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (r == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < ELEMENTSOF(wol_option_map); i++)
|
||||||
|
if (streq(w, wol_option_map[i].name)) {
|
||||||
|
new_opts |= wol_option_map[i].opt;
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found)
|
||||||
|
log_syntax(unit, LOG_WARNING, filename, line, 0,
|
||||||
|
"Unknown wake-on-lan mode '%s', ignoring.", w);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*opts == UINT32_MAX)
|
||||||
|
*opts = new_opts;
|
||||||
|
else
|
||||||
|
*opts |= new_opts;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|||||||
@ -18,19 +18,6 @@ typedef enum Duplex {
|
|||||||
_DUP_INVALID = -EINVAL,
|
_DUP_INVALID = -EINVAL,
|
||||||
} Duplex;
|
} Duplex;
|
||||||
|
|
||||||
typedef enum WakeOnLan {
|
|
||||||
WOL_PHY,
|
|
||||||
WOL_UCAST,
|
|
||||||
WOL_MCAST,
|
|
||||||
WOL_BCAST,
|
|
||||||
WOL_ARP,
|
|
||||||
WOL_MAGIC,
|
|
||||||
WOL_MAGICSECURE,
|
|
||||||
WOL_OFF,
|
|
||||||
_WOL_MAX,
|
|
||||||
_WOL_INVALID = -EINVAL,
|
|
||||||
} WakeOnLan;
|
|
||||||
|
|
||||||
typedef enum NetDevFeature {
|
typedef enum NetDevFeature {
|
||||||
NET_DEV_FEAT_RX,
|
NET_DEV_FEAT_RX,
|
||||||
NET_DEV_FEAT_TX,
|
NET_DEV_FEAT_TX,
|
||||||
@ -99,7 +86,7 @@ int ethtool_get_link_info(int *ethtool_fd, const char *ifname,
|
|||||||
int *ret_autonegotiation, uint64_t *ret_speed,
|
int *ret_autonegotiation, uint64_t *ret_speed,
|
||||||
Duplex *ret_duplex, NetDevPort *ret_port);
|
Duplex *ret_duplex, NetDevPort *ret_port);
|
||||||
int ethtool_get_permanent_macaddr(int *ethtool_fd, const char *ifname, struct ether_addr *ret);
|
int ethtool_get_permanent_macaddr(int *ethtool_fd, const char *ifname, struct ether_addr *ret);
|
||||||
int ethtool_set_wol(int *ethtool_fd, const char *ifname, WakeOnLan wol);
|
int ethtool_set_wol(int *ethtool_fd, const char *ifname, uint32_t wolopts);
|
||||||
int ethtool_set_nic_buffer_size(int *ethtool_fd, const char *ifname, const netdev_ring_param *ring);
|
int ethtool_set_nic_buffer_size(int *ethtool_fd, const char *ifname, const netdev_ring_param *ring);
|
||||||
int ethtool_set_features(int *ethtool_fd, const char *ifname, const int *features);
|
int ethtool_set_features(int *ethtool_fd, const char *ifname, const int *features);
|
||||||
int ethtool_set_glinksettings(int *ethtool_fd, const char *ifname,
|
int ethtool_set_glinksettings(int *ethtool_fd, const char *ifname,
|
||||||
@ -111,8 +98,7 @@ int ethtool_set_flow_control(int *fd, const char *ifname, int rx, int tx, int au
|
|||||||
const char *duplex_to_string(Duplex d) _const_;
|
const char *duplex_to_string(Duplex d) _const_;
|
||||||
Duplex duplex_from_string(const char *d) _pure_;
|
Duplex duplex_from_string(const char *d) _pure_;
|
||||||
|
|
||||||
const char *wol_to_string(WakeOnLan wol) _const_;
|
int wol_options_to_string_alloc(uint32_t opts, char **ret);
|
||||||
WakeOnLan wol_from_string(const char *wol) _pure_;
|
|
||||||
|
|
||||||
const char *port_to_string(NetDevPort port) _const_;
|
const char *port_to_string(NetDevPort port) _const_;
|
||||||
NetDevPort port_from_string(const char *port) _pure_;
|
NetDevPort port_from_string(const char *port) _pure_;
|
||||||
|
|||||||
@ -247,6 +247,7 @@ typedef struct UnitStatusInfo {
|
|||||||
uint64_t memory_max;
|
uint64_t memory_max;
|
||||||
uint64_t memory_swap_max;
|
uint64_t memory_swap_max;
|
||||||
uint64_t memory_limit;
|
uint64_t memory_limit;
|
||||||
|
uint64_t memory_available;
|
||||||
uint64_t cpu_usage_nsec;
|
uint64_t cpu_usage_nsec;
|
||||||
uint64_t tasks_current;
|
uint64_t tasks_current;
|
||||||
uint64_t tasks_max;
|
uint64_t tasks_max;
|
||||||
@ -682,6 +683,7 @@ static void print_status_info(
|
|||||||
if (i->memory_min > 0 || i->memory_low > 0 ||
|
if (i->memory_min > 0 || i->memory_low > 0 ||
|
||||||
i->memory_high != CGROUP_LIMIT_MAX || i->memory_max != CGROUP_LIMIT_MAX ||
|
i->memory_high != CGROUP_LIMIT_MAX || i->memory_max != CGROUP_LIMIT_MAX ||
|
||||||
i->memory_swap_max != CGROUP_LIMIT_MAX ||
|
i->memory_swap_max != CGROUP_LIMIT_MAX ||
|
||||||
|
i->memory_available != CGROUP_LIMIT_MAX ||
|
||||||
i->memory_limit != CGROUP_LIMIT_MAX) {
|
i->memory_limit != CGROUP_LIMIT_MAX) {
|
||||||
const char *prefix = "";
|
const char *prefix = "";
|
||||||
|
|
||||||
@ -710,6 +712,10 @@ static void print_status_info(
|
|||||||
printf("%slimit: %s", prefix, format_bytes(buf, sizeof(buf), i->memory_limit));
|
printf("%slimit: %s", prefix, format_bytes(buf, sizeof(buf), i->memory_limit));
|
||||||
prefix = " ";
|
prefix = " ";
|
||||||
}
|
}
|
||||||
|
if (i->memory_available != CGROUP_LIMIT_MAX) {
|
||||||
|
printf("%savailable: %s", prefix, format_bytes(buf, sizeof(buf), i->memory_available));
|
||||||
|
prefix = " ";
|
||||||
|
}
|
||||||
printf(")");
|
printf(")");
|
||||||
}
|
}
|
||||||
printf("\n");
|
printf("\n");
|
||||||
@ -1827,6 +1833,7 @@ static int show_one(
|
|||||||
{ "Where", "s", NULL, offsetof(UnitStatusInfo, where) },
|
{ "Where", "s", NULL, offsetof(UnitStatusInfo, where) },
|
||||||
{ "What", "s", NULL, offsetof(UnitStatusInfo, what) },
|
{ "What", "s", NULL, offsetof(UnitStatusInfo, what) },
|
||||||
{ "MemoryCurrent", "t", NULL, offsetof(UnitStatusInfo, memory_current) },
|
{ "MemoryCurrent", "t", NULL, offsetof(UnitStatusInfo, memory_current) },
|
||||||
|
{ "MemoryAvailable", "t", NULL, offsetof(UnitStatusInfo, memory_available) },
|
||||||
{ "DefaultMemoryMin", "t", NULL, offsetof(UnitStatusInfo, default_memory_min) },
|
{ "DefaultMemoryMin", "t", NULL, offsetof(UnitStatusInfo, default_memory_min) },
|
||||||
{ "DefaultMemoryLow", "t", NULL, offsetof(UnitStatusInfo, default_memory_low) },
|
{ "DefaultMemoryLow", "t", NULL, offsetof(UnitStatusInfo, default_memory_low) },
|
||||||
{ "MemoryMin", "t", NULL, offsetof(UnitStatusInfo, memory_min) },
|
{ "MemoryMin", "t", NULL, offsetof(UnitStatusInfo, memory_min) },
|
||||||
@ -1869,6 +1876,7 @@ static int show_one(
|
|||||||
.memory_max = CGROUP_LIMIT_MAX,
|
.memory_max = CGROUP_LIMIT_MAX,
|
||||||
.memory_swap_max = CGROUP_LIMIT_MAX,
|
.memory_swap_max = CGROUP_LIMIT_MAX,
|
||||||
.memory_limit = UINT64_MAX,
|
.memory_limit = UINT64_MAX,
|
||||||
|
.memory_available = CGROUP_LIMIT_MAX,
|
||||||
.cpu_usage_nsec = UINT64_MAX,
|
.cpu_usage_nsec = UINT64_MAX,
|
||||||
.tasks_current = UINT64_MAX,
|
.tasks_current = UINT64_MAX,
|
||||||
.tasks_max = UINT64_MAX,
|
.tasks_max = UINT64_MAX,
|
||||||
|
|||||||
@ -110,6 +110,17 @@ typedef enum ItemType {
|
|||||||
ADJUST_MODE = 'm', /* legacy, 'z' is identical to this */
|
ADJUST_MODE = 'm', /* legacy, 'z' is identical to this */
|
||||||
} ItemType;
|
} ItemType;
|
||||||
|
|
||||||
|
typedef enum AgeBy {
|
||||||
|
AGE_BY_ATIME = 1 << 0,
|
||||||
|
AGE_BY_BTIME = 1 << 1,
|
||||||
|
AGE_BY_CTIME = 1 << 2,
|
||||||
|
AGE_BY_MTIME = 1 << 3,
|
||||||
|
|
||||||
|
/* All file timestamp types are checked by default. */
|
||||||
|
AGE_BY_DEFAULT_FILE = AGE_BY_ATIME | AGE_BY_BTIME | AGE_BY_CTIME | AGE_BY_MTIME,
|
||||||
|
AGE_BY_DEFAULT_DIR = AGE_BY_ATIME | AGE_BY_BTIME | AGE_BY_MTIME
|
||||||
|
} AgeBy;
|
||||||
|
|
||||||
typedef struct Item {
|
typedef struct Item {
|
||||||
ItemType type;
|
ItemType type;
|
||||||
|
|
||||||
@ -124,6 +135,7 @@ typedef struct Item {
|
|||||||
gid_t gid;
|
gid_t gid;
|
||||||
mode_t mode;
|
mode_t mode;
|
||||||
usec_t age;
|
usec_t age;
|
||||||
|
AgeBy age_by_file, age_by_dir;
|
||||||
|
|
||||||
dev_t major_minor;
|
dev_t major_minor;
|
||||||
unsigned attribute_value;
|
unsigned attribute_value;
|
||||||
@ -142,6 +154,8 @@ typedef struct Item {
|
|||||||
|
|
||||||
bool allow_failure:1;
|
bool allow_failure:1;
|
||||||
|
|
||||||
|
bool try_replace:1;
|
||||||
|
|
||||||
OperationMask done;
|
OperationMask done;
|
||||||
} Item;
|
} Item;
|
||||||
|
|
||||||
@ -503,6 +517,64 @@ static inline nsec_t load_statx_timestamp_nsec(const struct statx_timestamp *ts)
|
|||||||
return ts->tv_sec * NSEC_PER_SEC + ts->tv_nsec;
|
return ts->tv_sec * NSEC_PER_SEC + ts->tv_nsec;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool needs_cleanup(
|
||||||
|
nsec_t atime,
|
||||||
|
nsec_t btime,
|
||||||
|
nsec_t ctime,
|
||||||
|
nsec_t mtime,
|
||||||
|
nsec_t cutoff,
|
||||||
|
const char *sub_path,
|
||||||
|
AgeBy age_by,
|
||||||
|
bool is_dir) {
|
||||||
|
|
||||||
|
if (FLAGS_SET(age_by, AGE_BY_MTIME) && mtime != NSEC_INFINITY && mtime >= cutoff) {
|
||||||
|
char a[FORMAT_TIMESTAMP_MAX];
|
||||||
|
/* Follows spelling in stat(1). */
|
||||||
|
log_debug("%s \"%s\": modify time %s is too new.",
|
||||||
|
is_dir ? "Directory" : "File",
|
||||||
|
sub_path,
|
||||||
|
format_timestamp_style(a, sizeof(a), mtime / NSEC_PER_USEC, TIMESTAMP_US));
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FLAGS_SET(age_by, AGE_BY_ATIME) && atime != NSEC_INFINITY && atime >= cutoff) {
|
||||||
|
char a[FORMAT_TIMESTAMP_MAX];
|
||||||
|
log_debug("%s \"%s\": access time %s is too new.",
|
||||||
|
is_dir ? "Directory" : "File",
|
||||||
|
sub_path,
|
||||||
|
format_timestamp_style(a, sizeof(a), atime / NSEC_PER_USEC, TIMESTAMP_US));
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Note: Unless explicitly specified by the user, "ctime" is ignored
|
||||||
|
* by default for directories, because we change it when deleting.
|
||||||
|
*/
|
||||||
|
if (FLAGS_SET(age_by, AGE_BY_CTIME) && ctime != NSEC_INFINITY && ctime >= cutoff) {
|
||||||
|
char a[FORMAT_TIMESTAMP_MAX];
|
||||||
|
log_debug("%s \"%s\": change time %s is too new.",
|
||||||
|
is_dir ? "Directory" : "File",
|
||||||
|
sub_path,
|
||||||
|
format_timestamp_style(a, sizeof(a), ctime / NSEC_PER_USEC, TIMESTAMP_US));
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FLAGS_SET(age_by, AGE_BY_BTIME) && btime != NSEC_INFINITY && btime >= cutoff) {
|
||||||
|
char a[FORMAT_TIMESTAMP_MAX];
|
||||||
|
log_debug("%s \"%s\": birth time %s is too new.",
|
||||||
|
is_dir ? "Directory" : "File",
|
||||||
|
sub_path,
|
||||||
|
format_timestamp_style(a, sizeof(a), btime / NSEC_PER_USEC, TIMESTAMP_US));
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static int dir_cleanup(
|
static int dir_cleanup(
|
||||||
Item *i,
|
Item *i,
|
||||||
const char *p,
|
const char *p,
|
||||||
@ -514,7 +586,9 @@ static int dir_cleanup(
|
|||||||
dev_t rootdev_minor,
|
dev_t rootdev_minor,
|
||||||
bool mountpoint,
|
bool mountpoint,
|
||||||
int maxdepth,
|
int maxdepth,
|
||||||
bool keep_this_level) {
|
bool keep_this_level,
|
||||||
|
AgeBy age_by_file,
|
||||||
|
AgeBy age_by_dir) {
|
||||||
|
|
||||||
bool deleted = false;
|
bool deleted = false;
|
||||||
struct dirent *dent;
|
struct dirent *dent;
|
||||||
@ -639,7 +713,8 @@ static int dir_cleanup(
|
|||||||
sub_path, sub_dir,
|
sub_path, sub_dir,
|
||||||
atime_nsec, mtime_nsec, cutoff_nsec,
|
atime_nsec, mtime_nsec, cutoff_nsec,
|
||||||
rootdev_major, rootdev_minor,
|
rootdev_major, rootdev_minor,
|
||||||
false, maxdepth-1, false);
|
false, maxdepth-1, false,
|
||||||
|
age_by_file, age_by_dir);
|
||||||
if (q < 0)
|
if (q < 0)
|
||||||
r = q;
|
r = q;
|
||||||
}
|
}
|
||||||
@ -654,31 +729,13 @@ static int dir_cleanup(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Ignore ctime, we change it when deleting */
|
/*
|
||||||
if (mtime_nsec != NSEC_INFINITY && mtime_nsec >= cutoff_nsec) {
|
* Check the file timestamps of an entry against the
|
||||||
char a[FORMAT_TIMESTAMP_MAX];
|
* given cutoff time; delete if it is older.
|
||||||
/* Follows spelling in stat(1). */
|
*/
|
||||||
log_debug("Directory \"%s\": modify time %s is too new.",
|
if (!needs_cleanup(atime_nsec, btime_nsec, ctime_nsec, mtime_nsec,
|
||||||
sub_path,
|
cutoff_nsec, sub_path, age_by_dir, true))
|
||||||
format_timestamp_style(a, sizeof(a), mtime_nsec / NSEC_PER_USEC, TIMESTAMP_US));
|
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
if (atime_nsec != NSEC_INFINITY && atime_nsec >= cutoff_nsec) {
|
|
||||||
char a[FORMAT_TIMESTAMP_MAX];
|
|
||||||
log_debug("Directory \"%s\": access time %s is too new.",
|
|
||||||
sub_path,
|
|
||||||
format_timestamp_style(a, sizeof(a), atime_nsec / NSEC_PER_USEC, TIMESTAMP_US));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (btime_nsec != NSEC_INFINITY && btime_nsec >= cutoff_nsec) {
|
|
||||||
char a[FORMAT_TIMESTAMP_MAX];
|
|
||||||
log_debug("Directory \"%s\": birth time %s is too new.",
|
|
||||||
sub_path,
|
|
||||||
format_timestamp_style(a, sizeof(a), btime_nsec / NSEC_PER_USEC, TIMESTAMP_US));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
log_debug("Removing directory \"%s\".", sub_path);
|
log_debug("Removing directory \"%s\".", sub_path);
|
||||||
if (unlinkat(dirfd(d), dent->d_name, AT_REMOVEDIR) < 0)
|
if (unlinkat(dirfd(d), dent->d_name, AT_REMOVEDIR) < 0)
|
||||||
@ -722,38 +779,9 @@ static int dir_cleanup(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mtime_nsec != NSEC_INFINITY && mtime_nsec >= cutoff_nsec) {
|
if (!needs_cleanup(atime_nsec, btime_nsec, ctime_nsec, mtime_nsec,
|
||||||
char a[FORMAT_TIMESTAMP_MAX];
|
cutoff_nsec, sub_path, age_by_file, false))
|
||||||
/* Follows spelling in stat(1). */
|
|
||||||
log_debug("File \"%s\": modify time %s is too new.",
|
|
||||||
sub_path,
|
|
||||||
format_timestamp_style(a, sizeof(a), mtime_nsec / NSEC_PER_USEC, TIMESTAMP_US));
|
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
if (atime_nsec != NSEC_INFINITY && atime_nsec >= cutoff_nsec) {
|
|
||||||
char a[FORMAT_TIMESTAMP_MAX];
|
|
||||||
log_debug("File \"%s\": access time %s is too new.",
|
|
||||||
sub_path,
|
|
||||||
format_timestamp_style(a, sizeof(a), atime_nsec / NSEC_PER_USEC, TIMESTAMP_US));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ctime_nsec != NSEC_INFINITY && ctime_nsec >= cutoff_nsec) {
|
|
||||||
char a[FORMAT_TIMESTAMP_MAX];
|
|
||||||
log_debug("File \"%s\": change time %s is too new.",
|
|
||||||
sub_path,
|
|
||||||
format_timestamp_style(a, sizeof(a), ctime_nsec / NSEC_PER_USEC, TIMESTAMP_US));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (btime_nsec != NSEC_INFINITY && btime_nsec >= cutoff_nsec) {
|
|
||||||
char a[FORMAT_TIMESTAMP_MAX];
|
|
||||||
log_debug("File \"%s\": birth time %s is too new.",
|
|
||||||
sub_path,
|
|
||||||
format_timestamp_style(a, sizeof(a), btime_nsec / NSEC_PER_USEC, TIMESTAMP_US));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
log_debug("Removing \"%s\".", sub_path);
|
log_debug("Removing \"%s\".", sub_path);
|
||||||
if (unlinkat(dirfd(d), dent->d_name, 0) < 0)
|
if (unlinkat(dirfd(d), dent->d_name, 0) < 0)
|
||||||
@ -2002,6 +2030,180 @@ static int glob_item_recursively(Item *i, fdaction_t action) {
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int rm_if_wrong_type_safe(
|
||||||
|
mode_t mode,
|
||||||
|
int parent_fd,
|
||||||
|
const struct stat *parent_st /* Only used if follow is true. */,
|
||||||
|
const char *name,
|
||||||
|
int flags) {
|
||||||
|
_cleanup_free_ char *parent_name = NULL;
|
||||||
|
bool follow_links = !FLAGS_SET(flags, AT_SYMLINK_NOFOLLOW);
|
||||||
|
struct stat st;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(name);
|
||||||
|
assert((mode & ~S_IFMT) == 0);
|
||||||
|
assert(!follow_links || parent_st);
|
||||||
|
assert((flags & ~AT_SYMLINK_NOFOLLOW) == 0);
|
||||||
|
|
||||||
|
if (!filename_is_valid(name))
|
||||||
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "\"%s\" is not a valid filename.", name);
|
||||||
|
|
||||||
|
r = fstatat_harder(parent_fd, name, &st, flags, REMOVE_CHMOD | REMOVE_CHMOD_RESTORE);
|
||||||
|
if (r < 0) {
|
||||||
|
(void) fd_get_path(parent_fd, &parent_name);
|
||||||
|
return log_full_errno(r == -ENOENT? LOG_DEBUG : LOG_ERR, r,
|
||||||
|
"Failed to stat \"%s\" at \"%s\": %m", name, strna(parent_name));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fail before removing anything if this is an unsafe transition. */
|
||||||
|
if (follow_links && unsafe_transition(parent_st, &st)) {
|
||||||
|
(void) fd_get_path(parent_fd, &parent_name);
|
||||||
|
return log_error_errno(SYNTHETIC_ERRNO(ENOLINK),
|
||||||
|
"Unsafe transition from \"%s\" to \"%s\".", parent_name, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((st.st_mode & S_IFMT) == mode)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
(void) fd_get_path(parent_fd, &parent_name);
|
||||||
|
log_notice("Wrong file type 0x%x; rm -rf \"%s/%s\"", st.st_mode & S_IFMT, strna(parent_name), name);
|
||||||
|
|
||||||
|
/* If the target of the symlink was the wrong type, the link needs to be removed instead of the
|
||||||
|
* target, so make sure it is identified as a link and not a directory. */
|
||||||
|
if (follow_links) {
|
||||||
|
r = fstatat_harder(parent_fd, name, &st, AT_SYMLINK_NOFOLLOW, REMOVE_CHMOD | REMOVE_CHMOD_RESTORE);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to stat \"%s\" at \"%s\": %m", name, strna(parent_name));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Do not remove mount points. */
|
||||||
|
r = fd_is_mount_point(parent_fd, name, follow_links ? AT_SYMLINK_FOLLOW : 0);
|
||||||
|
if (r < 0)
|
||||||
|
(void) log_warning_errno(r, "Failed to check if \"%s/%s\" is a mount point: %m; Continuing",
|
||||||
|
strna(parent_name), name);
|
||||||
|
else if (r > 0)
|
||||||
|
return log_error_errno(SYNTHETIC_ERRNO(EBUSY),
|
||||||
|
"Not removing \"%s/%s\" because it is a mount point.", strna(parent_name), name);
|
||||||
|
|
||||||
|
if ((st.st_mode & S_IFMT) == S_IFDIR) {
|
||||||
|
_cleanup_close_ int child_fd = -1;
|
||||||
|
|
||||||
|
child_fd = openat(parent_fd, name, O_NOCTTY | O_CLOEXEC | O_DIRECTORY);
|
||||||
|
if (child_fd < 0)
|
||||||
|
return log_error_errno(errno, "Failed to open \"%s\" at \"%s\": %m", name, strna(parent_name));
|
||||||
|
|
||||||
|
r = rm_rf_children(TAKE_FD(child_fd), REMOVE_ROOT|REMOVE_SUBVOLUME|REMOVE_PHYSICAL, &st);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to remove contents of \"%s\" at \"%s\": %m", name, strna(parent_name));
|
||||||
|
|
||||||
|
r = unlinkat_harder(parent_fd, name, AT_REMOVEDIR, REMOVE_CHMOD | REMOVE_CHMOD_RESTORE);
|
||||||
|
} else
|
||||||
|
r = unlinkat_harder(parent_fd, name, 0, REMOVE_CHMOD | REMOVE_CHMOD_RESTORE);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to remove \"%s\" at \"%s\": %m", name, strna(parent_name));
|
||||||
|
|
||||||
|
/* This is covered by the log_notice "Wrong file type..." It is logged earlier because it gives
|
||||||
|
* context to other error messages that might follow. */
|
||||||
|
return -ENOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If child_mode is non-zero, rm_if_wrong_type_safe will be executed for the last path component. */
|
||||||
|
static int mkdir_parents_rm_if_wrong_type(mode_t child_mode, const char *path) {
|
||||||
|
_cleanup_close_ int parent_fd = -1, next_fd = -1;
|
||||||
|
_cleanup_free_ char *parent_name = NULL;
|
||||||
|
struct stat parent_st;
|
||||||
|
const char *s, *e;
|
||||||
|
int r;
|
||||||
|
size_t path_len;
|
||||||
|
|
||||||
|
assert(path);
|
||||||
|
assert((child_mode & ~S_IFMT) == 0);
|
||||||
|
|
||||||
|
path_len = strlen(path);
|
||||||
|
|
||||||
|
if (!is_path(path))
|
||||||
|
/* rm_if_wrong_type_safe already logs errors. */
|
||||||
|
return child_mode != 0 ? rm_if_wrong_type_safe(child_mode, AT_FDCWD, NULL, path, AT_SYMLINK_NOFOLLOW) : 0;
|
||||||
|
|
||||||
|
if (child_mode != 0 && endswith(path, "/"))
|
||||||
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||||
|
"Trailing path separators are only allowed if child_mode is not set; got \"%s\"", path);
|
||||||
|
|
||||||
|
/* Get the parent_fd and stat. */
|
||||||
|
parent_fd = AT_FDCWD;
|
||||||
|
if (path_is_absolute(path)) {
|
||||||
|
parent_fd = open("/", O_NOCTTY | O_CLOEXEC | O_DIRECTORY);
|
||||||
|
if (parent_fd < 0)
|
||||||
|
return log_error_errno(errno, "Failed to open root: %m");
|
||||||
|
}
|
||||||
|
if (fstat(parent_fd, &parent_st) < 0)
|
||||||
|
return log_error_errno(errno, "Failed to stat root: %m");
|
||||||
|
|
||||||
|
/* Check every parent directory in the path, except the last component */
|
||||||
|
e = path;
|
||||||
|
for (;;) {
|
||||||
|
char t[path_len + 1];
|
||||||
|
|
||||||
|
/* Find the start of the next path component. */
|
||||||
|
s = e + strspn(e, "/");
|
||||||
|
/* Find the end of the next path component. */
|
||||||
|
e = s + strcspn(s, "/");
|
||||||
|
|
||||||
|
/* Copy the path component to t so it can be a null terminated string. */
|
||||||
|
*((char*) mempcpy(t, s, e - s)) = 0;
|
||||||
|
|
||||||
|
/* Is this the last component? If so, then check the type */
|
||||||
|
if (*e == 0)
|
||||||
|
return child_mode != 0 ? rm_if_wrong_type_safe(child_mode, parent_fd, &parent_st, t, AT_SYMLINK_NOFOLLOW) : 0;
|
||||||
|
else {
|
||||||
|
r = rm_if_wrong_type_safe(S_IFDIR, parent_fd, &parent_st, t, 0);
|
||||||
|
/* Remove dangling symlinks. */
|
||||||
|
if (r == -ENOENT)
|
||||||
|
r = rm_if_wrong_type_safe(S_IFDIR, parent_fd, &parent_st, t, AT_SYMLINK_NOFOLLOW);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (r == -ENOENT) {
|
||||||
|
RUN_WITH_UMASK(0000)
|
||||||
|
r = mkdirat_label(parent_fd, t, 0755);
|
||||||
|
if (r < 0) {
|
||||||
|
(void) fd_get_path(parent_fd, &parent_name);
|
||||||
|
return log_error_errno(r, "Failed to mkdir \"%s\" at \"%s\": %m", t, parent_name);
|
||||||
|
}
|
||||||
|
} else if (r < 0)
|
||||||
|
/* rm_if_wrong_type_safe already logs errors. */
|
||||||
|
return r;
|
||||||
|
|
||||||
|
next_fd = openat(parent_fd, t, O_NOCTTY | O_CLOEXEC | O_DIRECTORY);
|
||||||
|
if (next_fd < 0) {
|
||||||
|
r = -errno;
|
||||||
|
(void) fd_get_path(parent_fd, &parent_name);
|
||||||
|
return log_error_errno(r, "Failed to open \"%s\" at \"%s\": %m", t, parent_name);
|
||||||
|
}
|
||||||
|
if (fstat(next_fd, &parent_st) < 0) {
|
||||||
|
r = -errno;
|
||||||
|
(void) fd_get_path(parent_fd, &parent_name);
|
||||||
|
return log_error_errno(r, "Failed to stat \"%s\" at \"%s\": %m", t, parent_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
safe_close(parent_fd);
|
||||||
|
parent_fd = TAKE_FD(next_fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mkdir_parents_item(Item *i, mode_t child_mode) {
|
||||||
|
int r;
|
||||||
|
if (i->try_replace) {
|
||||||
|
r = mkdir_parents_rm_if_wrong_type(child_mode, i->path);
|
||||||
|
if (r < 0 && r != -ENOENT)
|
||||||
|
return r;
|
||||||
|
} else
|
||||||
|
RUN_WITH_UMASK(0000)
|
||||||
|
(void) mkdir_parents_label(i->path, 0755);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int create_item(Item *i) {
|
static int create_item(Item *i) {
|
||||||
CreationMode creation;
|
CreationMode creation;
|
||||||
int r = 0;
|
int r = 0;
|
||||||
@ -2020,8 +2222,9 @@ static int create_item(Item *i) {
|
|||||||
|
|
||||||
case TRUNCATE_FILE:
|
case TRUNCATE_FILE:
|
||||||
case CREATE_FILE:
|
case CREATE_FILE:
|
||||||
RUN_WITH_UMASK(0000)
|
r = mkdir_parents_item(i, S_IFREG);
|
||||||
(void) mkdir_parents_label(i->path, 0755);
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
if ((i->type == CREATE_FILE && i->append_or_force) || i->type == TRUNCATE_FILE)
|
if ((i->type == CREATE_FILE && i->append_or_force) || i->type == TRUNCATE_FILE)
|
||||||
r = truncate_file(i, i->path);
|
r = truncate_file(i, i->path);
|
||||||
@ -2033,8 +2236,9 @@ static int create_item(Item *i) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case COPY_FILES:
|
case COPY_FILES:
|
||||||
RUN_WITH_UMASK(0000)
|
r = mkdir_parents_item(i, 0);
|
||||||
(void) mkdir_parents_label(i->path, 0755);
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
r = copy_files(i);
|
r = copy_files(i);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
@ -2050,8 +2254,9 @@ static int create_item(Item *i) {
|
|||||||
|
|
||||||
case CREATE_DIRECTORY:
|
case CREATE_DIRECTORY:
|
||||||
case TRUNCATE_DIRECTORY:
|
case TRUNCATE_DIRECTORY:
|
||||||
RUN_WITH_UMASK(0000)
|
r = mkdir_parents_item(i, S_IFDIR);
|
||||||
(void) mkdir_parents_label(i->path, 0755);
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
r = create_directory(i, i->path);
|
r = create_directory(i, i->path);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
@ -2061,8 +2266,9 @@ static int create_item(Item *i) {
|
|||||||
case CREATE_SUBVOLUME:
|
case CREATE_SUBVOLUME:
|
||||||
case CREATE_SUBVOLUME_INHERIT_QUOTA:
|
case CREATE_SUBVOLUME_INHERIT_QUOTA:
|
||||||
case CREATE_SUBVOLUME_NEW_QUOTA:
|
case CREATE_SUBVOLUME_NEW_QUOTA:
|
||||||
RUN_WITH_UMASK(0000)
|
r = mkdir_parents_item(i, S_IFDIR);
|
||||||
(void) mkdir_parents_label(i->path, 0755);
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
r = create_subvolume(i, i->path);
|
r = create_subvolume(i, i->path);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
@ -2076,8 +2282,9 @@ static int create_item(Item *i) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case CREATE_FIFO:
|
case CREATE_FIFO:
|
||||||
RUN_WITH_UMASK(0000)
|
r = mkdir_parents_item(i, S_IFIFO);
|
||||||
(void) mkdir_parents_label(i->path, 0755);
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
r = create_fifo(i, i->path);
|
r = create_fifo(i, i->path);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
@ -2085,8 +2292,9 @@ static int create_item(Item *i) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case CREATE_SYMLINK: {
|
case CREATE_SYMLINK: {
|
||||||
RUN_WITH_UMASK(0000)
|
r = mkdir_parents_item(i, S_IFLNK);
|
||||||
(void) mkdir_parents_label(i->path, 0755);
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
mac_selinux_create_file_prepare(i->path, S_IFLNK);
|
mac_selinux_create_file_prepare(i->path, S_IFLNK);
|
||||||
r = symlink(i->argument, i->path);
|
r = symlink(i->argument, i->path);
|
||||||
@ -2142,8 +2350,9 @@ static int create_item(Item *i) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
RUN_WITH_UMASK(0000)
|
r = mkdir_parents_item(i, i->type == CREATE_BLOCK_DEVICE ? S_IFBLK : S_IFCHR);
|
||||||
(void) mkdir_parents_label(i->path, 0755);
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
r = create_device(i, i->type == CREATE_BLOCK_DEVICE ? S_IFBLK : S_IFCHR);
|
r = create_device(i, i->type == CREATE_BLOCK_DEVICE ? S_IFBLK : S_IFCHR);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
@ -2260,6 +2469,23 @@ static int remove_item(Item *i) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char *age_by_to_string(AgeBy ab, bool is_dir) {
|
||||||
|
static const char ab_map[] = { 'a', 'b', 'c', 'm' };
|
||||||
|
size_t j = 0;
|
||||||
|
char *ret;
|
||||||
|
|
||||||
|
ret = new(char, ELEMENTSOF(ab_map) + 1);
|
||||||
|
if (!ret)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < ELEMENTSOF(ab_map); i++)
|
||||||
|
if (FLAGS_SET(ab, 1U << i))
|
||||||
|
ret[j++] = is_dir ? ascii_toupper(ab_map[i]) : ab_map[i];
|
||||||
|
|
||||||
|
ret[j] = 0;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static int clean_item_instance(Item *i, const char* instance) {
|
static int clean_item_instance(Item *i, const char* instance) {
|
||||||
char timestamp[FORMAT_TIMESTAMP_MAX];
|
char timestamp[FORMAT_TIMESTAMP_MAX];
|
||||||
_cleanup_closedir_ DIR *d = NULL;
|
_cleanup_closedir_ DIR *d = NULL;
|
||||||
@ -2306,17 +2532,31 @@ static int clean_item_instance(Item *i, const char* instance) {
|
|||||||
sx.stx_ino != ps.st_ino;
|
sx.stx_ino != ps.st_ino;
|
||||||
}
|
}
|
||||||
|
|
||||||
log_debug("Cleanup threshold for %s \"%s\" is %s",
|
if (DEBUG_LOGGING) {
|
||||||
mountpoint ? "mount point" : "directory",
|
_cleanup_free_ char *ab_f = NULL, *ab_d = NULL;
|
||||||
instance,
|
|
||||||
format_timestamp_style(timestamp, sizeof(timestamp), cutoff, TIMESTAMP_US));
|
ab_f = age_by_to_string(i->age_by_file, false);
|
||||||
|
if (!ab_f)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
|
ab_d = age_by_to_string(i->age_by_dir, true);
|
||||||
|
if (!ab_d)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
|
log_debug("Cleanup threshold for %s \"%s\" is %s; age-by: %s%s",
|
||||||
|
mountpoint ? "mount point" : "directory",
|
||||||
|
instance,
|
||||||
|
format_timestamp_style(timestamp, sizeof(timestamp), cutoff, TIMESTAMP_US),
|
||||||
|
ab_f, ab_d);
|
||||||
|
}
|
||||||
|
|
||||||
return dir_cleanup(i, instance, d,
|
return dir_cleanup(i, instance, d,
|
||||||
load_statx_timestamp_nsec(&sx.stx_atime),
|
load_statx_timestamp_nsec(&sx.stx_atime),
|
||||||
load_statx_timestamp_nsec(&sx.stx_mtime),
|
load_statx_timestamp_nsec(&sx.stx_mtime),
|
||||||
cutoff * NSEC_PER_USEC,
|
cutoff * NSEC_PER_USEC,
|
||||||
sx.stx_dev_major, sx.stx_dev_minor, mountpoint,
|
sx.stx_dev_major, sx.stx_dev_minor, mountpoint,
|
||||||
MAX_DEPTH, i->keep_first_level);
|
MAX_DEPTH, i->keep_first_level,
|
||||||
|
i->age_by_file, i->age_by_dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int clean_item(Item *i) {
|
static int clean_item(Item *i) {
|
||||||
@ -2482,6 +2722,9 @@ static bool item_compatible(Item *a, Item *b) {
|
|||||||
a->age_set == b->age_set &&
|
a->age_set == b->age_set &&
|
||||||
a->age == b->age &&
|
a->age == b->age &&
|
||||||
|
|
||||||
|
a->age_by_file == b->age_by_file &&
|
||||||
|
a->age_by_dir == b->age_by_dir &&
|
||||||
|
|
||||||
a->mask_perms == b->mask_perms &&
|
a->mask_perms == b->mask_perms &&
|
||||||
|
|
||||||
a->keep_first_level == b->keep_first_level &&
|
a->keep_first_level == b->keep_first_level &&
|
||||||
@ -2646,6 +2889,58 @@ static int find_gid(const char *group, gid_t *ret_gid, Hashmap **cache) {
|
|||||||
return name_to_gid_offline(arg_root, group, ret_gid, cache);
|
return name_to_gid_offline(arg_root, group, ret_gid, cache);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int parse_age_by_from_arg(const char *age_by_str, Item *item) {
|
||||||
|
AgeBy ab_f = 0, ab_d = 0;
|
||||||
|
|
||||||
|
static const struct {
|
||||||
|
char age_by_chr;
|
||||||
|
AgeBy age_by_flag;
|
||||||
|
} age_by_types[] = {
|
||||||
|
{ 'a', AGE_BY_ATIME },
|
||||||
|
{ 'b', AGE_BY_BTIME },
|
||||||
|
{ 'c', AGE_BY_CTIME },
|
||||||
|
{ 'm', AGE_BY_MTIME },
|
||||||
|
};
|
||||||
|
|
||||||
|
assert(age_by_str);
|
||||||
|
assert(item);
|
||||||
|
|
||||||
|
if (isempty(age_by_str))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
for (const char *s = age_by_str; *s != 0; s++) {
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
/* Ignore whitespace. */
|
||||||
|
if (strchr(WHITESPACE, *s))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for (i = 0; i < ELEMENTSOF(age_by_types); i++) {
|
||||||
|
/* Check lower-case for files, upper-case for directories. */
|
||||||
|
if (*s == age_by_types[i].age_by_chr) {
|
||||||
|
ab_f |= age_by_types[i].age_by_flag;
|
||||||
|
break;
|
||||||
|
} else if (*s == ascii_toupper(age_by_types[i].age_by_chr)) {
|
||||||
|
ab_d |= age_by_types[i].age_by_flag;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Invalid character. */
|
||||||
|
if (i >= ELEMENTSOF(age_by_types))
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* No match. */
|
||||||
|
if (ab_f == 0 && ab_d == 0)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
item->age_by_file = ab_f > 0 ? ab_f : AGE_BY_DEFAULT_FILE;
|
||||||
|
item->age_by_dir = ab_d > 0 ? ab_d : AGE_BY_DEFAULT_DIR;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int parse_line(
|
static int parse_line(
|
||||||
const char *fname,
|
const char *fname,
|
||||||
unsigned line,
|
unsigned line,
|
||||||
@ -2655,11 +2950,15 @@ static int parse_line(
|
|||||||
Hashmap **gid_cache) {
|
Hashmap **gid_cache) {
|
||||||
|
|
||||||
_cleanup_free_ char *action = NULL, *mode = NULL, *user = NULL, *group = NULL, *age = NULL, *path = NULL;
|
_cleanup_free_ char *action = NULL, *mode = NULL, *user = NULL, *group = NULL, *age = NULL, *path = NULL;
|
||||||
_cleanup_(item_free_contents) Item i = {};
|
_cleanup_(item_free_contents) Item i = {
|
||||||
|
/* The "age-by" argument considers all file timestamp types by default. */
|
||||||
|
.age_by_file = AGE_BY_DEFAULT_FILE,
|
||||||
|
.age_by_dir = AGE_BY_DEFAULT_DIR,
|
||||||
|
};
|
||||||
ItemArray *existing;
|
ItemArray *existing;
|
||||||
OrderedHashmap *h;
|
OrderedHashmap *h;
|
||||||
int r, pos;
|
int r, pos;
|
||||||
bool append_or_force = false, boot = false, allow_failure = false;
|
bool append_or_force = false, boot = false, allow_failure = false, try_replace = false;
|
||||||
|
|
||||||
assert(fname);
|
assert(fname);
|
||||||
assert(line >= 1);
|
assert(line >= 1);
|
||||||
@ -2704,6 +3003,8 @@ static int parse_line(
|
|||||||
append_or_force = true;
|
append_or_force = true;
|
||||||
else if (action[pos] == '-' && !allow_failure)
|
else if (action[pos] == '-' && !allow_failure)
|
||||||
allow_failure = true;
|
allow_failure = true;
|
||||||
|
else if (action[pos] == '=' && !try_replace)
|
||||||
|
try_replace = true;
|
||||||
else {
|
else {
|
||||||
*invalid_config = true;
|
*invalid_config = true;
|
||||||
return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG), "Unknown modifiers in command '%s'", action);
|
return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG), "Unknown modifiers in command '%s'", action);
|
||||||
@ -2718,6 +3019,7 @@ static int parse_line(
|
|||||||
i.type = action[0];
|
i.type = action[0];
|
||||||
i.append_or_force = append_or_force;
|
i.append_or_force = append_or_force;
|
||||||
i.allow_failure = allow_failure;
|
i.allow_failure = allow_failure;
|
||||||
|
i.try_replace = try_replace;
|
||||||
|
|
||||||
r = specifier_printf(path, PATH_MAX-1, specifier_table, NULL, &i.path);
|
r = specifier_printf(path, PATH_MAX-1, specifier_table, NULL, &i.path);
|
||||||
if (r == -ENXIO)
|
if (r == -ENXIO)
|
||||||
@ -2926,16 +3228,37 @@ static int parse_line(
|
|||||||
|
|
||||||
if (!empty_or_dash(age)) {
|
if (!empty_or_dash(age)) {
|
||||||
const char *a = age;
|
const char *a = age;
|
||||||
|
_cleanup_free_ char *seconds = NULL, *age_by = NULL;
|
||||||
|
|
||||||
if (*a == '~') {
|
if (*a == '~') {
|
||||||
i.keep_first_level = true;
|
i.keep_first_level = true;
|
||||||
a++;
|
a++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Format: "age-by:age"; where age-by is "[abcmABCM]+". */
|
||||||
|
r = split_pair(a, ":", &age_by, &seconds);
|
||||||
|
if (r == -ENOMEM)
|
||||||
|
return log_oom();
|
||||||
|
if (r < 0 && r != -EINVAL)
|
||||||
|
return log_error_errno(r, "Failed to parse age-by for '%s': %m", age);
|
||||||
|
if (r >= 0) {
|
||||||
|
/* We found a ":", parse the "age-by" part. */
|
||||||
|
r = parse_age_by_from_arg(age_by, &i);
|
||||||
|
if (r == -ENOMEM)
|
||||||
|
return log_oom();
|
||||||
|
if (r < 0) {
|
||||||
|
*invalid_config = true;
|
||||||
|
return log_syntax(NULL, LOG_ERR, fname, line, r, "Invalid age-by '%s'.", age_by);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* For parsing the "age" part, after the ":". */
|
||||||
|
a = seconds;
|
||||||
|
}
|
||||||
|
|
||||||
r = parse_sec(a, &i.age);
|
r = parse_sec(a, &i.age);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
*invalid_config = true;
|
*invalid_config = true;
|
||||||
return log_syntax(NULL, LOG_ERR, fname, line, r, "Invalid age '%s'.", age);
|
return log_syntax(NULL, LOG_ERR, fname, line, r, "Invalid age '%s'.", a);
|
||||||
}
|
}
|
||||||
|
|
||||||
i.age_set = true;
|
i.age_set = true;
|
||||||
|
|||||||
@ -134,7 +134,7 @@ int link_load_one(LinkConfigContext *ctx, const char *filename) {
|
|||||||
*link = (LinkConfig) {
|
*link = (LinkConfig) {
|
||||||
.filename = TAKE_PTR(name),
|
.filename = TAKE_PTR(name),
|
||||||
.mac_address_policy = _MAC_ADDRESS_POLICY_INVALID,
|
.mac_address_policy = _MAC_ADDRESS_POLICY_INVALID,
|
||||||
.wol = _WOL_INVALID,
|
.wol = UINT32_MAX, /* UINT32_MAX means do not change WOL setting. */
|
||||||
.duplex = _DUP_INVALID,
|
.duplex = _DUP_INVALID,
|
||||||
.port = _NET_DEV_PORT_INVALID,
|
.port = _NET_DEV_PORT_INVALID,
|
||||||
.autonegotiation = -1,
|
.autonegotiation = -1,
|
||||||
@ -329,9 +329,13 @@ static int link_config_apply_ethtool_settings(int *ethtool_fd, const LinkConfig
|
|||||||
}
|
}
|
||||||
|
|
||||||
r = ethtool_set_wol(ethtool_fd, name, config->wol);
|
r = ethtool_set_wol(ethtool_fd, name, config->wol);
|
||||||
if (r < 0)
|
if (r < 0) {
|
||||||
|
_cleanup_free_ char *str = NULL;
|
||||||
|
|
||||||
|
(void) wol_options_to_string_alloc(config->wol, &str);
|
||||||
log_device_warning_errno(device, r, "Could not set WakeOnLan to %s, ignoring: %m",
|
log_device_warning_errno(device, r, "Could not set WakeOnLan to %s, ignoring: %m",
|
||||||
wol_to_string(config->wol));
|
strna(str));
|
||||||
|
}
|
||||||
|
|
||||||
r = ethtool_set_features(ethtool_fd, name, config->features);
|
r = ethtool_set_features(ethtool_fd, name, config->features);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
|
|||||||
@ -56,7 +56,7 @@ struct LinkConfig {
|
|||||||
Duplex duplex;
|
Duplex duplex;
|
||||||
int autonegotiation;
|
int autonegotiation;
|
||||||
uint32_t advertise[N_ADVERTISE];
|
uint32_t advertise[N_ADVERTISE];
|
||||||
WakeOnLan wol;
|
uint32_t wol;
|
||||||
NetDevPort port;
|
NetDevPort port;
|
||||||
int features[_NET_DEV_FEAT_MAX];
|
int features[_NET_DEV_FEAT_MAX];
|
||||||
netdev_channels channels;
|
netdev_channels channels;
|
||||||
|
|||||||
@ -48,6 +48,7 @@ def test_invalids(*, user):
|
|||||||
test_line('f++ /too/many/plusses', user=user)
|
test_line('f++ /too/many/plusses', user=user)
|
||||||
test_line('f+!+ /too/many/plusses', user=user)
|
test_line('f+!+ /too/many/plusses', user=user)
|
||||||
test_line('f!+! /too/many/bangs', user=user)
|
test_line('f!+! /too/many/bangs', user=user)
|
||||||
|
test_line('f== /too/many/equals', user=user)
|
||||||
test_line('w /unresolved/argument - - - - "%Y"', user=user)
|
test_line('w /unresolved/argument - - - - "%Y"', user=user)
|
||||||
test_line('w /unresolved/argument/sandwich - - - - "%v%Y%v"', user=user)
|
test_line('w /unresolved/argument/sandwich - - - - "%v%Y%v"', user=user)
|
||||||
test_line('w /unresolved/filename/%Y - - - - "whatever"', user=user)
|
test_line('w /unresolved/filename/%Y - - - - "whatever"', user=user)
|
||||||
@ -73,9 +74,11 @@ def test_uninitialized_t():
|
|||||||
test_line('w /foo - - - - "specifier for --user %t"',
|
test_line('w /foo - - - - "specifier for --user %t"',
|
||||||
user=True, returncode=0, extra={'env':{}})
|
user=True, returncode=0, extra={'env':{}})
|
||||||
|
|
||||||
def test_content(line, expected, *, user, extra={}):
|
def test_content(line, expected, *, user, extra={}, subpath='/arg', path_cb=None):
|
||||||
d = tempfile.TemporaryDirectory(prefix='test-systemd-tmpfiles.')
|
d = tempfile.TemporaryDirectory(prefix='test-systemd-tmpfiles.')
|
||||||
arg = d.name + '/arg'
|
if path_cb is not None:
|
||||||
|
path_cb(d.name, subpath)
|
||||||
|
arg = d.name + subpath
|
||||||
spec = line.format(arg)
|
spec = line.format(arg)
|
||||||
test_line(spec, user=user, returncode=0, extra=extra)
|
test_line(spec, user=user, returncode=0, extra=extra)
|
||||||
content = open(arg).read()
|
content = open(arg).read()
|
||||||
@ -134,6 +137,57 @@ def test_valid_specifiers(*, user):
|
|||||||
|
|
||||||
test_content('f {} - - - - %%', '%', user=user)
|
test_content('f {} - - - - %%', '%', user=user)
|
||||||
|
|
||||||
|
def mkfifo(parent, subpath):
|
||||||
|
os.makedirs(parent, mode=0o755, exist_ok=True)
|
||||||
|
first_component = subpath.split('/')[1]
|
||||||
|
path = parent + '/' + first_component
|
||||||
|
print('path: {}'.format(path))
|
||||||
|
os.mkfifo(path)
|
||||||
|
|
||||||
|
def mkdir(parent, subpath):
|
||||||
|
first_component = subpath.split('/')[1]
|
||||||
|
path = parent + '/' + first_component
|
||||||
|
os.makedirs(path, mode=0o755, exist_ok=True)
|
||||||
|
os.symlink(path, path + '/self', target_is_directory=True)
|
||||||
|
|
||||||
|
def symlink(parent, subpath):
|
||||||
|
link_path = parent + '/link-target'
|
||||||
|
os.makedirs(parent, mode=0o755, exist_ok=True)
|
||||||
|
with open(link_path, 'wb') as f:
|
||||||
|
f.write(b'target')
|
||||||
|
first_component = subpath.split('/')[1]
|
||||||
|
path = parent + '/' + first_component
|
||||||
|
os.symlink(link_path, path, target_is_directory=True)
|
||||||
|
|
||||||
|
def file(parent, subpath):
|
||||||
|
content = 'file-' + subpath.split('/')[1]
|
||||||
|
path = parent + subpath
|
||||||
|
os.makedirs(os.path.dirname(path), mode=0o755, exist_ok=True)
|
||||||
|
with open(path, 'wb') as f:
|
||||||
|
f.write(content.encode())
|
||||||
|
|
||||||
|
def valid_symlink(parent, subpath):
|
||||||
|
target = 'link-target'
|
||||||
|
link_path = parent + target
|
||||||
|
os.makedirs(link_path, mode=0o755, exist_ok=True)
|
||||||
|
first_component = subpath.split('/')[1]
|
||||||
|
path = parent + '/' + first_component
|
||||||
|
os.symlink(target, path, target_is_directory=True)
|
||||||
|
|
||||||
|
def test_hard_cleanup(*, user):
|
||||||
|
type_cbs = [None, file, mkdir, symlink]
|
||||||
|
if 'mkfifo' in dir(os):
|
||||||
|
type_cbs.append(mkfifo)
|
||||||
|
|
||||||
|
for type_cb in type_cbs:
|
||||||
|
for subpath in ['/shallow', '/deep/1/2']:
|
||||||
|
label = '{}-{}'.format('None' if type_cb is None else type_cb.__name__, subpath.split('/')[1])
|
||||||
|
test_content('f= {} - - - - ' + label, label, user=user, subpath=subpath, path_cb=type_cb)
|
||||||
|
|
||||||
|
# Test the case that a valid symlink is in the path.
|
||||||
|
label = 'valid_symlink-deep'
|
||||||
|
test_content('f= {} - - - - ' + label, label, user=user, subpath='/deep/1/2', path_cb=valid_symlink)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
test_invalids(user=False)
|
test_invalids(user=False)
|
||||||
test_invalids(user=True)
|
test_invalids(user=True)
|
||||||
@ -141,3 +195,6 @@ if __name__ == '__main__':
|
|||||||
|
|
||||||
test_valid_specifiers(user=False)
|
test_valid_specifiers(user=False)
|
||||||
test_valid_specifiers(user=True)
|
test_valid_specifiers(user=True)
|
||||||
|
|
||||||
|
test_hard_cleanup(user=False)
|
||||||
|
test_hard_cleanup(user=True)
|
||||||
|
|||||||
196
test/units/testsuite-22.12.sh
Executable file
196
test/units/testsuite-22.12.sh
Executable file
@ -0,0 +1,196 @@
|
|||||||
|
#! /bin/bash
|
||||||
|
|
||||||
|
# Test the "Age" parameter (with age-by) for systemd-tmpfiles.
|
||||||
|
|
||||||
|
set -e
|
||||||
|
set -x
|
||||||
|
|
||||||
|
# Test directory structure looks like this:
|
||||||
|
# /tmp/ageby/
|
||||||
|
# ├── d1
|
||||||
|
# │ ├── f1
|
||||||
|
# │ ├── f2
|
||||||
|
# │ ├── f3
|
||||||
|
# │ └── f4
|
||||||
|
# ├── d2
|
||||||
|
# │ ├── f1
|
||||||
|
# │ ├── f2
|
||||||
|
# ...
|
||||||
|
|
||||||
|
export SYSTEMD_LOG_LEVEL="debug"
|
||||||
|
|
||||||
|
rm -rf /tmp/ageby
|
||||||
|
mkdir -p /tmp/ageby/d{1..4}
|
||||||
|
|
||||||
|
# TODO: There is probably a better way to figure this out.
|
||||||
|
# Test for [bB] age-by arguments only on filesystems that expose
|
||||||
|
# the creation time. Note that this is _not_ an accurate way to
|
||||||
|
# check if the filesystem or kernel version don't provide the
|
||||||
|
# timestamp. But, if the timestamp is visible in "stat" it is a
|
||||||
|
# good indicator that the test can be run.
|
||||||
|
TEST_TMPFILES_AGEBY_BTIME=${TEST_TMPFILES_AGEBY_BTIME:-0}
|
||||||
|
if stat --format "%w" /tmp/ageby 2>/dev/null | grep -qv '^[\?\-]$'; then
|
||||||
|
TEST_TMPFILES_AGEBY_BTIME=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
touch -a --date "2 minutes ago" /tmp/ageby/d1/f1
|
||||||
|
touch -m --date "4 minutes ago" /tmp/ageby/d2/f1
|
||||||
|
|
||||||
|
# Create a bunch of other files.
|
||||||
|
touch /tmp/ageby/d{1,2}/f{2..4}
|
||||||
|
|
||||||
|
# For "ctime".
|
||||||
|
touch /tmp/ageby/d3/f1
|
||||||
|
chmod +x /tmp/ageby/d3/f1
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
# For "btime".
|
||||||
|
touch /tmp/ageby/d4/f1
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
# More files with recent "{a,b}time" values.
|
||||||
|
touch /tmp/ageby/d{3,4}/f{2..4}
|
||||||
|
|
||||||
|
# Check for cleanup of "f1" in each of "/tmp/d{1..4}".
|
||||||
|
systemd-tmpfiles --clean - <<-EOF
|
||||||
|
d /tmp/ageby/d1 - - - a:1m -
|
||||||
|
e /tmp/ageby/d2 - - - m:3m -
|
||||||
|
D /tmp/ageby/d3 - - - c:2s -
|
||||||
|
EOF
|
||||||
|
|
||||||
|
for d in d{1..3}; do
|
||||||
|
test ! -f "/tmp/ageby/${d}/f1"
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ $TEST_TMPFILES_AGEBY_BTIME -gt 0 ]]; then
|
||||||
|
systemd-tmpfiles --clean - <<-EOF
|
||||||
|
d /tmp/ageby/d4 - - - b:1s -
|
||||||
|
EOF
|
||||||
|
|
||||||
|
test ! -f "/tmp/ageby/d4/f1"
|
||||||
|
else
|
||||||
|
# Remove the file manually.
|
||||||
|
rm "/tmp/ageby/d4/f1"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check for an invalid "age" and "age-by" arguments.
|
||||||
|
for a in ':' ':1s' '2:1h' 'nope:42h' '" :7m"' 'm:' '::' '"+r^w-x:2/h"' 'b ar::64'; do
|
||||||
|
systemd-tmpfiles --clean - <<EOF 2>&1 | grep -q -F 'Invalid age'
|
||||||
|
d /tmp/ageby - - - ${a} -
|
||||||
|
EOF
|
||||||
|
done
|
||||||
|
|
||||||
|
for d in d{1..4}; do
|
||||||
|
for f in f{2..4}; do
|
||||||
|
test -f "/tmp/ageby/${d}/${f}"
|
||||||
|
done
|
||||||
|
done
|
||||||
|
|
||||||
|
# Check for parsing with whitespace, repeated values
|
||||||
|
# for "age-by" (valid arguments).
|
||||||
|
for a in '" a:24h"' 'cccaab:2h' '" aa : 4h"' '" a A B C c:1h"'; do
|
||||||
|
systemd-tmpfiles --clean - <<EOF
|
||||||
|
d /tmp/ageby - - - ${a} -
|
||||||
|
EOF
|
||||||
|
done
|
||||||
|
|
||||||
|
for d in d{1..4}; do
|
||||||
|
for f in f{2..4}; do
|
||||||
|
test -f "/tmp/ageby/${d}/${f}"
|
||||||
|
done
|
||||||
|
done
|
||||||
|
|
||||||
|
# Check that all files are removed if the "Age" is
|
||||||
|
# set to "0" (regardless of "age-by" argument).
|
||||||
|
systemd-tmpfiles --clean - <<-EOF
|
||||||
|
d /tmp/ageby/d1 - - - abc:0 -
|
||||||
|
e /tmp/ageby/d2 - - - cmb:0 -
|
||||||
|
EOF
|
||||||
|
|
||||||
|
for d in d{1,2}; do
|
||||||
|
for f in f{2..4}; do
|
||||||
|
test ! -f "/tmp/ageby/${d}/${f}"
|
||||||
|
done
|
||||||
|
done
|
||||||
|
|
||||||
|
# Check for combinations:
|
||||||
|
# - "/tmp/ageby/d3/f2" has file timestamps that
|
||||||
|
# are older than the specified age, it will be
|
||||||
|
# removed
|
||||||
|
# - "/tmp/ageby/d4/f2", has not aged for the given
|
||||||
|
# timestamp combination, it will not be removed
|
||||||
|
touch -a -m --date "4 minutes ago" /tmp/ageby/d3/f2
|
||||||
|
touch -a -m --date "8 minutes ago" /tmp/ageby/d4/f2
|
||||||
|
systemd-tmpfiles --clean - <<-EOF
|
||||||
|
e /tmp/ageby/d3 - - - am:3m -
|
||||||
|
D /tmp/ageby/d4 - - - mc:7m -
|
||||||
|
EOF
|
||||||
|
|
||||||
|
test ! -f "/tmp/ageby/d3/f2"
|
||||||
|
test -f "/tmp/ageby/d4/f2"
|
||||||
|
|
||||||
|
# Check that all files are removed if only "Age" is set to 0.
|
||||||
|
systemd-tmpfiles --clean - <<-EOF
|
||||||
|
e /tmp/ageby/d3 - - - 0s
|
||||||
|
d /tmp/ageby/d4 - - - 0s
|
||||||
|
EOF
|
||||||
|
|
||||||
|
for d in d{3,4}; do
|
||||||
|
for f in f{2..4}; do
|
||||||
|
test ! -f "/tmp/ageby/$d/${f}"
|
||||||
|
done
|
||||||
|
done
|
||||||
|
|
||||||
|
# Check "age-by" argument for sub-directories in "/tmp/ageby".
|
||||||
|
systemd-tmpfiles --clean - <<-EOF
|
||||||
|
d /tmp/ageby/ - - - A:1m -
|
||||||
|
EOF
|
||||||
|
|
||||||
|
for d in d{1..4}; do
|
||||||
|
test -d "/tmp/ageby/${d}"
|
||||||
|
done
|
||||||
|
|
||||||
|
# Check for combinations.
|
||||||
|
touch -a -m --date "5 seconds ago" /tmp/ageby/d{1,2}
|
||||||
|
systemd-tmpfiles --clean - <<-EOF
|
||||||
|
e /tmp/ageby/ - - - AM:4s -
|
||||||
|
EOF
|
||||||
|
|
||||||
|
for d in d{1,2}; do
|
||||||
|
test ! -d "/tmp/ageby/${d}"
|
||||||
|
done
|
||||||
|
|
||||||
|
for d in d{3,4}; do
|
||||||
|
test -d "/tmp/ageby/${d}"
|
||||||
|
done
|
||||||
|
|
||||||
|
# Check "btime" for directories.
|
||||||
|
if [[ $TEST_TMPFILES_AGEBY_BTIME -gt 0 ]]; then
|
||||||
|
systemd-tmpfiles --clean - <<-EOF
|
||||||
|
d /tmp/ageby/ - - - B:8s -
|
||||||
|
EOF
|
||||||
|
|
||||||
|
for d in d{3,4}; do
|
||||||
|
test -d "/tmp/ageby/${d}"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# To bump "atime".
|
||||||
|
touch -a --date "1 second ago" /tmp/ageby/d3
|
||||||
|
systemd-tmpfiles --clean - <<-EOF
|
||||||
|
d /tmp/ageby/ - - - A:2s -
|
||||||
|
EOF
|
||||||
|
|
||||||
|
test -d /tmp/ageby/d3
|
||||||
|
test ! -d /tmp/ageby/d4
|
||||||
|
|
||||||
|
# Check if sub-directories are removed regardless
|
||||||
|
# of "age-by", when "Age" is set to "0".
|
||||||
|
systemd-tmpfiles --clean - <<-EOF
|
||||||
|
D /tmp/ageby/ - - - AM:0 -
|
||||||
|
EOF
|
||||||
|
|
||||||
|
test ! -d /tmp/ageby/d3
|
||||||
|
|
||||||
|
# Cleanup the test directory (fail if not empty).
|
||||||
|
rmdir /tmp/ageby
|
||||||
Loading…
x
Reference in New Issue
Block a user