mirror of
https://github.com/systemd/systemd
synced 2026-03-18 11:04:46 +01:00
Compare commits
No commits in common. "7f7a50dd157b3bfb4282c3cc3a56671121eabe24" and "f75420a43a4dc3c69210f887b38f5df23660ea16" have entirely different histories.
7f7a50dd15
...
f75420a43a
@ -99,7 +99,7 @@
|
||||
<term><varname>ExternalSizeMax=</varname></term>
|
||||
<term><varname>JournalSizeMax=</varname></term>
|
||||
|
||||
<listitem><para>The maximum (compressed or uncompressed) size in bytes of a
|
||||
<listitem><para>The maximum (uncompressed) size in bytes of a
|
||||
core to be saved. Unit suffixes are allowed just as in
|
||||
<option>ProcessSizeMax=</option></para></listitem>.
|
||||
</varlistentry>
|
||||
|
||||
@ -2401,8 +2401,6 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly t MemoryCurrent = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly t MemoryAvailable = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly t CPUUsageNSec = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly ay EffectiveCPUs = [...];
|
||||
@ -3506,8 +3504,6 @@ 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="MemoryAvailable"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="CPUUsageNSec"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="EffectiveCPUs"/>
|
||||
@ -4067,11 +4063,6 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
|
||||
<varname>MountImages</varname>
|
||||
<varname>ExtensionImages</varname>
|
||||
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>
|
||||
</refsect1>
|
||||
|
||||
@ -4205,8 +4196,6 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly t MemoryCurrent = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly t MemoryAvailable = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly t CPUUsageNSec = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly ay EffectiveCPUs = [...];
|
||||
@ -5332,8 +5321,6 @@ 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="MemoryAvailable"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="CPUUsageNSec"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="EffectiveCPUs"/>
|
||||
@ -5928,8 +5915,6 @@ node /org/freedesktop/systemd1/unit/home_2emount {
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly t MemoryCurrent = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly t MemoryAvailable = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly t CPUUsageNSec = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly ay EffectiveCPUs = [...];
|
||||
@ -6901,8 +6886,6 @@ node /org/freedesktop/systemd1/unit/home_2emount {
|
||||
|
||||
<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="EffectiveCPUs"/>
|
||||
@ -7618,8 +7601,6 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly t MemoryCurrent = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly t MemoryAvailable = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly t CPUUsageNSec = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly ay EffectiveCPUs = [...];
|
||||
@ -8563,8 +8544,6 @@ 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="MemoryAvailable"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="CPUUsageNSec"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="EffectiveCPUs"/>
|
||||
@ -9133,8 +9112,6 @@ node /org/freedesktop/systemd1/unit/system_2eslice {
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly t MemoryCurrent = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly t MemoryAvailable = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly t CPUUsageNSec = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly ay EffectiveCPUs = [...];
|
||||
@ -9426,8 +9403,6 @@ node /org/freedesktop/systemd1/unit/system_2eslice {
|
||||
|
||||
<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="EffectiveCPUs"/>
|
||||
@ -9596,8 +9571,6 @@ node /org/freedesktop/systemd1/unit/session_2d1_2escope {
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly t MemoryCurrent = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly t MemoryAvailable = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly t CPUUsageNSec = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly ay EffectiveCPUs = [...];
|
||||
@ -9931,8 +9904,6 @@ 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="MemoryAvailable"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="CPUUsageNSec"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="EffectiveCPUs"/>
|
||||
|
||||
@ -492,9 +492,8 @@
|
||||
<varlistentry>
|
||||
<term><varname>WakeOnLan=</varname></term>
|
||||
<listitem>
|
||||
<para>The Wake-on-LAN policy to set for the device. Takes the special value
|
||||
<literal>off</literal> which disables Wake-on-LAN, or space separated list of the following
|
||||
words:</para>
|
||||
<para>The Wake-on-LAN policy to set for the device. The
|
||||
supported values are:</para>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
@ -541,11 +540,15 @@
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><option>off</option></term>
|
||||
<listitem>
|
||||
<para>Never wake.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
|
||||
<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>
|
||||
<para>Defaults to <option>off</option>.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
|
||||
@ -225,12 +225,10 @@
|
||||
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.
|
||||
The formatting and precedence of applying drop-in configurations follow what is defined above.
|
||||
Files in <filename><replaceable>type</replaceable>.d/</filename> have lower precedence compared
|
||||
to files in name-specific override directories. The usual rules apply: multiple drop-in files
|
||||
with different names are applied in lexicographic order, regardless of which of the directories
|
||||
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>
|
||||
Configurations in <filename><replaceable>type</replaceable>.d/</filename> have the lowest precedence
|
||||
compared to settings in the name specific override directories. So the contents of
|
||||
<filename>foo-.service.d/10-override.conf</filename> would override
|
||||
<filename>service.d/10-override.conf</filename>.</para>
|
||||
|
||||
<para>Note that while systemd offers a flexible dependency system
|
||||
between units it is recommended to use this functionality only
|
||||
@ -2203,62 +2201,6 @@ PrivateTmp=yes</programlisting>
|
||||
to override the entire unit.</para>
|
||||
|
||||
</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>
|
||||
|
||||
@ -161,7 +161,7 @@ L /tmp/foobar - - - - /dev/null</programlisting>
|
||||
<title>Type</title>
|
||||
|
||||
<para>The type consists of a single letter and optionally an exclamation mark (<literal>!</literal>)
|
||||
minus sign (<literal>-</literal>), and/or equals sign (<literal>=</literal>).</para>
|
||||
and/or minus sign (<literal>-</literal>).</para>
|
||||
|
||||
<para>The following line types are understood:</para>
|
||||
|
||||
@ -482,11 +482,6 @@ 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
|
||||
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
|
||||
(i.e. <varname>f</varname>/<varname>F</varname>,
|
||||
<varname>d</varname>/<varname>D</varname>/<varname>v</varname>/<varname>q</varname>/<varname>Q</varname>,
|
||||
@ -590,40 +585,9 @@ w- /proc/sys/vm/swappiness - - - - 10</programlisting></para>
|
||||
<para>The age of a file system entry is determined from its last
|
||||
modification timestamp (mtime), its last access timestamp (atime),
|
||||
and (except for directories) its last status change timestamp
|
||||
(ctime). By default, any of these three (or two) values will
|
||||
prevent cleanup if it is more recent than the current time minus
|
||||
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>
|
||||
(ctime). Any of these three (or two) values will prevent cleanup
|
||||
if it is more recent than the current time minus the age
|
||||
field.</para>
|
||||
|
||||
<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
|
||||
|
||||
36
po/es.po
36
po/es.po
@ -5,14 +5,13 @@
|
||||
# Daniel Mustieles <daniel.mustieles@gmail.com>, 2015.
|
||||
# Álex Puchades <alex94puchades@gmail.com>, 2015.
|
||||
# Adolfo Jayme Barrientos <fitoschido@gmail.com>, 2020.
|
||||
# Emilio Herrera <ehespinosa57@gmail.com>, 2021.
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: systemd master\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2021-01-08 17:48+0100\n"
|
||||
"PO-Revision-Date: 2021-06-08 09:04+0000\n"
|
||||
"Last-Translator: Emilio Herrera <ehespinosa57@gmail.com>\n"
|
||||
"PO-Revision-Date: 2020-08-24 07:29+0000\n"
|
||||
"Last-Translator: Adolfo Jayme Barrientos <fitoschido@gmail.com>\n"
|
||||
"Language-Team: Spanish <https://translate.fedoraproject.org/projects/systemd/"
|
||||
"master/es/>\n"
|
||||
"Language: es\n"
|
||||
@ -20,7 +19,7 @@ msgstr ""
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
||||
"X-Generator: Weblate 4.6.2\n"
|
||||
"X-Generator: Weblate 4.2.1\n"
|
||||
|
||||
#: src/core/org.freedesktop.systemd1.policy.in:22
|
||||
msgid "Send passphrase back to system"
|
||||
@ -74,38 +73,49 @@ msgstr "Necesita autenticarse para recargar el estado de systemd."
|
||||
|
||||
#: src/home/org.freedesktop.home1.policy:13
|
||||
msgid "Create a home area"
|
||||
msgstr "Crear un área home"
|
||||
msgstr ""
|
||||
|
||||
#: 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."
|
||||
msgstr "Se requiere autenticación para crear un área home de usuario."
|
||||
msgstr "Se requiere autenticación para recargar el estado de systemd."
|
||||
|
||||
#: src/home/org.freedesktop.home1.policy:23
|
||||
msgid "Remove a home area"
|
||||
msgstr "Quitar un área home"
|
||||
msgstr ""
|
||||
|
||||
#: 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."
|
||||
msgstr "Se requiere autenticación para quitar un área home de usuario."
|
||||
msgstr "Se requiere autenticación para recargar el estado de systemd."
|
||||
|
||||
#: src/home/org.freedesktop.home1.policy:33
|
||||
msgid "Check credentials of a home area"
|
||||
msgstr "Comprobar las credenciales de un área home"
|
||||
msgstr ""
|
||||
|
||||
#: src/home/org.freedesktop.home1.policy:34
|
||||
#, fuzzy
|
||||
#| msgid ""
|
||||
#| "Authentication is required to manage active sessions, users and seats."
|
||||
msgid ""
|
||||
"Authentication is required to check credentials against a user's home area."
|
||||
msgstr ""
|
||||
"Se requiere autenticación para comprobar las credenciales contra un área "
|
||||
"home de usuario."
|
||||
"Se requiere autenticación para administrar las sesiones activas, usuarios y "
|
||||
"puestos de trabajo."
|
||||
|
||||
#: src/home/org.freedesktop.home1.policy:43
|
||||
msgid "Update a home area"
|
||||
msgstr "Actualizar un área home"
|
||||
msgstr ""
|
||||
|
||||
#: 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."
|
||||
msgstr "Se requiere autenticación para actualizar un área home de usuario."
|
||||
msgstr ""
|
||||
"Se requiere autenticación para conectar un dispositivo a un puesto de "
|
||||
"trabajo."
|
||||
|
||||
#: src/home/org.freedesktop.home1.policy:53
|
||||
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;
|
||||
}
|
||||
|
||||
bool unsafe_transition(const struct stat *a, const struct stat *b) {
|
||||
static 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
|
||||
* 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. */
|
||||
|
||||
@ -94,8 +94,6 @@ enum {
|
||||
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 */
|
||||
#define CHASE_SYMLINKS_MAX 32
|
||||
|
||||
|
||||
@ -48,7 +48,11 @@ static int patch_dirfd_mode(
|
||||
return 0;
|
||||
}
|
||||
|
||||
int unlinkat_harder(int dfd, const char *filename, int unlink_flags, RemoveFlags remove_flags) {
|
||||
static int unlinkat_harder(
|
||||
int dfd,
|
||||
const char *filename,
|
||||
int unlink_flags,
|
||||
RemoveFlags remove_flags) {
|
||||
|
||||
mode_t old_mode;
|
||||
int r;
|
||||
@ -73,15 +77,13 @@ int unlinkat_harder(int dfd, const char *filename, int unlink_flags, RemoveFlags
|
||||
return r;
|
||||
}
|
||||
|
||||
if (FLAGS_SET(remove_flags, REMOVE_CHMOD_RESTORE) && fchmod(dfd, old_mode) < 0)
|
||||
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 */
|
||||
/* If this worked, we won't reset the old mode, since we'll need it for other entries too, and we
|
||||
* should destroy the whole thing */
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fstatat_harder(int dfd,
|
||||
static int fstatat_harder(
|
||||
int dfd,
|
||||
const char *filename,
|
||||
struct stat *ret,
|
||||
int fstatat_flags,
|
||||
@ -107,9 +109,6 @@ int fstatat_harder(int dfd,
|
||||
return r;
|
||||
}
|
||||
|
||||
if (FLAGS_SET(remove_flags, REMOVE_CHMOD_RESTORE) && fchmod(dfd, old_mode) < 0)
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@ -12,17 +12,9 @@ typedef enum RemoveFlags {
|
||||
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_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 or access something */
|
||||
REMOVE_CHMOD_RESTORE = 1 << 6, /* Restore the old mode before returning */
|
||||
REMOVE_CHMOD = 1 << 5, /* chmod() for write access if we cannot delete something */
|
||||
} 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(const char *path, RemoveFlags flags);
|
||||
|
||||
|
||||
@ -1087,7 +1087,6 @@ const UnitVTable automount_vtable = {
|
||||
.can_transient = true,
|
||||
.can_fail = true,
|
||||
.can_trigger = true,
|
||||
.exclude_from_switch_root_serialization = true,
|
||||
|
||||
.init = automount_init,
|
||||
.load = automount_load,
|
||||
|
||||
@ -3402,77 +3402,6 @@ int manager_notify_cgroup_empty(Manager *m, const char *cgroup) {
|
||||
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 r;
|
||||
|
||||
|
||||
@ -282,7 +282,6 @@ int unit_watch_all_pids(Unit *u);
|
||||
int unit_synthesize_cgroup_empty_event(Unit *u);
|
||||
|
||||
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_cpu_usage(Unit *u, nsec_t *ret);
|
||||
int unit_get_io_accounting(Unit *u, CGroupIOAccountingMetric metric, bool allow_cache, uint64_t *ret);
|
||||
|
||||
@ -1097,30 +1097,6 @@ static int property_get_current_memory(
|
||||
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(
|
||||
sd_bus *bus,
|
||||
const char *path,
|
||||
@ -1565,7 +1541,6 @@ const sd_bus_vtable bus_unit_cgroup_vtable[] = {
|
||||
SD_BUS_PROPERTY("Slice", "s", property_get_slice, 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("MemoryAvailable", "t", property_get_available_memory, 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("EffectiveMemoryNodes", "ay", property_get_cpuset_mems, 0, 0),
|
||||
|
||||
@ -3321,7 +3321,11 @@ int manager_serialize(
|
||||
if (u->id != t)
|
||||
continue;
|
||||
|
||||
r = unit_serialize(u, f, fds, switching_root);
|
||||
/* Start marker */
|
||||
fputs(u->id, f);
|
||||
fputc('\n', f);
|
||||
|
||||
r = unit_serialize(u, f, fds, !switching_root);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
@ -2176,7 +2176,6 @@ const UnitVTable mount_vtable = {
|
||||
|
||||
.can_transient = true,
|
||||
.can_fail = true,
|
||||
.exclude_from_switch_root_serialization = true,
|
||||
|
||||
.init = mount_init,
|
||||
.load = mount_load,
|
||||
|
||||
@ -2626,6 +2626,13 @@ static int socket_serialize(Unit *u, FILE *f, FDSet *fds) {
|
||||
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) {
|
||||
Socket *s = SOCKET(u);
|
||||
|
||||
@ -2687,20 +2694,13 @@ 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))
|
||||
log_unit_debug(u, "Failed to parse fifo value: %s", value);
|
||||
else {
|
||||
bool found = false;
|
||||
|
||||
else
|
||||
LIST_FOREACH(port, p, s->ports)
|
||||
if (p->fd < 0 &&
|
||||
p->type == SOCKET_FIFO &&
|
||||
if (p->type == SOCKET_FIFO &&
|
||||
path_equal_or_files_same(p->path, value+skip, 0)) {
|
||||
p->fd = fdset_remove(fds, fd);
|
||||
found = true;
|
||||
socket_port_take_fd(p, fds, fd);
|
||||
break;
|
||||
}
|
||||
if (!found)
|
||||
log_unit_debug(u, "No matching fifo socket found: %s", value+skip);
|
||||
}
|
||||
|
||||
} else if (streq(key, "special")) {
|
||||
int fd, skip = 0;
|
||||
@ -2708,20 +2708,13 @@ 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))
|
||||
log_unit_debug(u, "Failed to parse special value: %s", value);
|
||||
else {
|
||||
bool found = false;
|
||||
|
||||
else
|
||||
LIST_FOREACH(port, p, s->ports)
|
||||
if (p->fd < 0 &&
|
||||
p->type == SOCKET_SPECIAL &&
|
||||
if (p->type == SOCKET_SPECIAL &&
|
||||
path_equal_or_files_same(p->path, value+skip, 0)) {
|
||||
p->fd = fdset_remove(fds, fd);
|
||||
found = true;
|
||||
socket_port_take_fd(p, fds, fd);
|
||||
break;
|
||||
}
|
||||
if (!found)
|
||||
log_unit_debug(u, "No matching special socket found: %s", value+skip);
|
||||
}
|
||||
|
||||
} else if (streq(key, "mqueue")) {
|
||||
int fd, skip = 0;
|
||||
@ -2729,20 +2722,13 @@ 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))
|
||||
log_unit_debug(u, "Failed to parse mqueue value: %s", value);
|
||||
else {
|
||||
bool found = false;
|
||||
|
||||
else
|
||||
LIST_FOREACH(port, p, s->ports)
|
||||
if (p->fd < 0 &&
|
||||
p->type == SOCKET_MQUEUE &&
|
||||
if (p->type == SOCKET_MQUEUE &&
|
||||
streq(p->path, value+skip)) {
|
||||
p->fd = fdset_remove(fds, fd);
|
||||
found = true;
|
||||
socket_port_take_fd(p, fds, fd);
|
||||
break;
|
||||
}
|
||||
if (!found)
|
||||
log_unit_debug(u, "No matching mqueue socket found: %s", value+skip);
|
||||
}
|
||||
|
||||
} else if (streq(key, "socket")) {
|
||||
int fd, type, skip = 0;
|
||||
@ -2750,20 +2736,12 @@ 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))
|
||||
log_unit_debug(u, "Failed to parse socket value: %s", value);
|
||||
else {
|
||||
bool found = false;
|
||||
|
||||
else
|
||||
LIST_FOREACH(port, p, s->ports)
|
||||
if (p->fd < 0 &&
|
||||
socket_address_is(&p->address, value+skip, type)) {
|
||||
p->fd = fdset_remove(fds, fd);
|
||||
found = true;
|
||||
if (socket_address_is(&p->address, value+skip, type)) {
|
||||
socket_port_take_fd(p, fds, fd);
|
||||
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")) {
|
||||
int fd, skip = 0;
|
||||
@ -2771,19 +2749,12 @@ 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))
|
||||
log_unit_debug(u, "Failed to parse socket value: %s", value);
|
||||
else {
|
||||
bool found = false;
|
||||
|
||||
else
|
||||
LIST_FOREACH(port, p, s->ports)
|
||||
if (p->fd < 0 &&
|
||||
socket_address_is_netlink(&p->address, value+skip)) {
|
||||
p->fd = fdset_remove(fds, fd);
|
||||
found = true;
|
||||
if (socket_address_is_netlink(&p->address, value+skip)) {
|
||||
socket_port_take_fd(p, fds, fd);
|
||||
break;
|
||||
}
|
||||
if (!found)
|
||||
log_unit_debug(u, "No matching netlink socket found: %s", value+skip);
|
||||
}
|
||||
|
||||
} else if (streq(key, "ffs")) {
|
||||
int fd, skip = 0;
|
||||
@ -2791,20 +2762,13 @@ 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))
|
||||
log_unit_debug(u, "Failed to parse ffs value: %s", value);
|
||||
else {
|
||||
bool found = false;
|
||||
|
||||
else
|
||||
LIST_FOREACH(port, p, s->ports)
|
||||
if (p->fd < 0 &&
|
||||
p->type == SOCKET_USB_FUNCTION &&
|
||||
if (p->type == SOCKET_USB_FUNCTION &&
|
||||
path_equal_or_files_same(p->path, value+skip, 0)) {
|
||||
p->fd = fdset_remove(fds, fd);
|
||||
found = true;
|
||||
socket_port_take_fd(p, fds, fd);
|
||||
break;
|
||||
}
|
||||
if (!found)
|
||||
log_unit_debug(u, "No matching ffs socket found: %s", value+skip);
|
||||
}
|
||||
|
||||
} else
|
||||
log_unit_debug(UNIT(s), "Unknown serialization key: %s", key);
|
||||
|
||||
@ -89,25 +89,13 @@ static const char *const io_accounting_metric_field_last[_CGROUP_IO_ACCOUNTING_M
|
||||
[CGROUP_IO_WRITE_OPERATIONS] = "io-accounting-write-operations-last",
|
||||
};
|
||||
|
||||
int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool switching_root) {
|
||||
int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) {
|
||||
int r;
|
||||
|
||||
assert(u);
|
||||
assert(f);
|
||||
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)) {
|
||||
r = UNIT_VTABLE(u)->serialize(u, f, fds);
|
||||
if (r < 0)
|
||||
@ -187,7 +175,7 @@ int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool switching_root) {
|
||||
(void) serialize_item_format(f, ip_accounting_metric_field[m], "%" PRIu64, v);
|
||||
}
|
||||
|
||||
if (!switching_root) {
|
||||
if (serialize_jobs) {
|
||||
if (u->job) {
|
||||
fputs("job\n", f);
|
||||
job_serialize(u->job, f);
|
||||
|
||||
@ -543,8 +543,8 @@ typedef struct UnitVTable {
|
||||
|
||||
bool (*can_reload)(Unit *u);
|
||||
|
||||
/* Serialize state and file descriptors that should be carried over into the new
|
||||
* instance after reexecution. */
|
||||
/* Write all data that cannot be restored from other sources
|
||||
* away using unit_serialize_item() */
|
||||
int (*serialize)(Unit *u, FILE *f, FDSet *fds);
|
||||
|
||||
/* Restore one item from the serialization */
|
||||
@ -646,7 +646,7 @@ typedef struct UnitVTable {
|
||||
/* Type specific cleanups. */
|
||||
void (*shutdown)(Manager *m);
|
||||
|
||||
/* If this function is set and returns false all jobs for units
|
||||
/* If this function is set and return false all jobs for units
|
||||
* of this type will immediately fail. */
|
||||
bool (*supported)(void);
|
||||
|
||||
@ -654,28 +654,25 @@ typedef struct UnitVTable {
|
||||
UnitStatusMessageFormats status_message_formats;
|
||||
|
||||
/* True if transient units of this type are OK */
|
||||
bool can_transient;
|
||||
bool can_transient:1;
|
||||
|
||||
/* True if cgroup delegation is permissible */
|
||||
bool can_delegate;
|
||||
bool can_delegate:1;
|
||||
|
||||
/* True if the unit type triggers other units, i.e. can have a UNIT_TRIGGERS dependency */
|
||||
bool can_trigger;
|
||||
bool can_trigger:1;
|
||||
|
||||
/* True if the unit type knows a failure state, and thus can be source of an OnFailure= dependency */
|
||||
bool can_fail;
|
||||
bool can_fail:1;
|
||||
|
||||
/* True if units of this type shall be startable only once and then never again */
|
||||
bool once_only;
|
||||
|
||||
/* Do not serialize this unit when preparing for root switch */
|
||||
bool exclude_from_switch_root_serialization;
|
||||
bool once_only:1;
|
||||
|
||||
/* True if queued jobs of this type should be GC'ed if no other job needs them anymore */
|
||||
bool gc_jobs;
|
||||
bool gc_jobs:1;
|
||||
|
||||
/* True if systemd-oomd can monitor and act on this unit's recursive children's cgroup(s) */
|
||||
bool can_set_managed_oom;
|
||||
bool can_set_managed_oom:1;
|
||||
} UnitVTable;
|
||||
|
||||
extern const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX];
|
||||
|
||||
@ -3,7 +3,6 @@
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/statvfs.h>
|
||||
#include <sys/xattr.h>
|
||||
#include <unistd.h>
|
||||
|
||||
@ -19,7 +18,6 @@
|
||||
|
||||
#include "acl-util.h"
|
||||
#include "alloc-util.h"
|
||||
#include "bus-error.h"
|
||||
#include "capability-util.h"
|
||||
#include "cgroup-util.h"
|
||||
#include "compress.h"
|
||||
@ -44,7 +42,6 @@
|
||||
#include "socket-util.h"
|
||||
#include "special.h"
|
||||
#include "stacktrace.h"
|
||||
#include "stat-util.h"
|
||||
#include "string-table.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
@ -66,10 +63,6 @@
|
||||
#define JOURNAL_SIZE_MAX ((size_t) (10LU*1024LU*1024LU))
|
||||
#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
|
||||
* size. See DATA_SIZE_MAX in journal-importer.h. */
|
||||
assert_cc(JOURNAL_SIZE_MAX <= DATA_SIZE_MAX);
|
||||
@ -336,14 +329,11 @@ static int save_external_coredump(
|
||||
int *ret_node_fd,
|
||||
int *ret_data_fd,
|
||||
uint64_t *ret_size,
|
||||
uint64_t *ret_compressed_size,
|
||||
bool *ret_truncated) {
|
||||
|
||||
_cleanup_(unlink_and_freep) char *tmp = NULL;
|
||||
_cleanup_free_ char *fn = NULL;
|
||||
_cleanup_free_ char *fn = NULL, *tmp = NULL;
|
||||
_cleanup_close_ int fd = -1;
|
||||
uint64_t rlimit, process_limit, max_size;
|
||||
bool truncated, storage_on_tmpfs;
|
||||
struct stat st;
|
||||
uid_t uid;
|
||||
int r;
|
||||
@ -353,8 +343,6 @@ static int save_external_coredump(
|
||||
assert(ret_node_fd);
|
||||
assert(ret_data_fd);
|
||||
assert(ret_size);
|
||||
assert(ret_compressed_size);
|
||||
assert(ret_truncated);
|
||||
|
||||
r = parse_uid(context->meta[META_ARGV_UID], &uid);
|
||||
if (r < 0)
|
||||
@ -391,145 +379,92 @@ static int save_external_coredump(
|
||||
if (fd < 0)
|
||||
return log_error_errno(fd, "Failed to create temporary file for coredump %s: %m", fn);
|
||||
|
||||
/* If storage is on tmpfs, the kernel oomd might kill us if there's MemoryMax set on
|
||||
* the service or the slice it belongs to. This is common on low-resources systems,
|
||||
* to avoid crashing processes to take away too many system resources.
|
||||
* Check the cgroup settings, and set max_size to a bit less than half of the
|
||||
* 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);
|
||||
}
|
||||
|
||||
r = copy_bytes(input_fd, fd, max_size, 0);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Cannot store coredump of %s (%s): %m",
|
||||
if (r < 0) {
|
||||
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;
|
||||
goto fail;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (truncated)
|
||||
*ret_truncated = r == 1;
|
||||
if (*ret_truncated)
|
||||
log_struct(LOG_INFO,
|
||||
LOG_MESSAGE("Core file was truncated to %zu bytes.", max_size),
|
||||
"SIZE_LIMIT=%zu", max_size,
|
||||
"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);
|
||||
if (r < 0)
|
||||
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);
|
||||
goto fail;
|
||||
|
||||
*ret_filename = TAKE_PTR(fn);
|
||||
*ret_data_fd = TAKE_FD(fd);
|
||||
*ret_node_fd = -1;
|
||||
*ret_size = (uint64_t) st.st_size;
|
||||
*ret_truncated = truncated;
|
||||
|
||||
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) {
|
||||
@ -774,7 +709,7 @@ static int submit_coredump(
|
||||
_cleanup_free_ char *stacktrace = NULL;
|
||||
char *core_message;
|
||||
const char *module_name;
|
||||
uint64_t coredump_size = UINT64_MAX, coredump_compressed_size = UINT64_MAX;
|
||||
uint64_t coredump_size = UINT64_MAX;
|
||||
bool truncated = false;
|
||||
JsonVariant *module_json;
|
||||
int r;
|
||||
@ -787,8 +722,7 @@ static int submit_coredump(
|
||||
|
||||
/* Always stream the coredump to disk, if that's possible */
|
||||
r = save_external_coredump(context, input_fd,
|
||||
&filename, &coredump_node_fd, &coredump_fd,
|
||||
&coredump_size, &coredump_compressed_size, &truncated);
|
||||
&filename, &coredump_node_fd, &coredump_fd, &coredump_size, &truncated);
|
||||
if (r < 0)
|
||||
/* Skip whole core dumping part */
|
||||
goto log;
|
||||
@ -796,7 +730,7 @@ static int submit_coredump(
|
||||
/* 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
|
||||
* still process it and log it. */
|
||||
r = maybe_remove_external_coredump(filename, coredump_node_fd >= 0 ? coredump_compressed_size : coredump_size);
|
||||
r = maybe_remove_external_coredump(filename, coredump_size);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0) {
|
||||
@ -804,7 +738,7 @@ static int submit_coredump(
|
||||
|
||||
} else if (arg_storage == COREDUMP_STORAGE_EXTERNAL)
|
||||
log_info("The core will not be stored: size %"PRIu64" is greater than %"PRIu64" (the configured maximum)",
|
||||
coredump_node_fd >= 0 ? coredump_compressed_size : coredump_size, arg_external_size_max);
|
||||
coredump_size, arg_external_size_max);
|
||||
|
||||
/* 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);
|
||||
@ -824,7 +758,7 @@ static int submit_coredump(
|
||||
log_debug("Not generating stack trace: core size %"PRIu64" is greater "
|
||||
"than %"PRIu64" (the configured maximum)",
|
||||
coredump_size, arg_process_size_max);
|
||||
} else if (coredump_fd >= 0)
|
||||
} else
|
||||
coredump_parse_core(coredump_fd, context->meta[META_EXE], &stacktrace, &json_metadata);
|
||||
#endif
|
||||
|
||||
@ -878,7 +812,7 @@ log:
|
||||
}
|
||||
|
||||
/* Optionally store the entire coredump in the journal */
|
||||
if (arg_storage == COREDUMP_STORAGE_JOURNAL && coredump_fd >= 0) {
|
||||
if (arg_storage == COREDUMP_STORAGE_JOURNAL) {
|
||||
if (coredump_size <= arg_journal_size_max) {
|
||||
size_t sz = 0;
|
||||
|
||||
|
||||
@ -550,7 +550,7 @@ int decompress_startswith(
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
int compress_stream_xz(int fdf, int fdt, uint64_t max_bytes, uint64_t *ret_uncompressed_size) {
|
||||
int compress_stream_xz(int fdf, int fdt, uint64_t max_bytes) {
|
||||
#if HAVE_XZ
|
||||
_cleanup_(lzma_end) lzma_stream s = LZMA_STREAM_INIT;
|
||||
lzma_ret ret;
|
||||
@ -611,9 +611,6 @@ int compress_stream_xz(int fdf, int fdt, uint64_t max_bytes, uint64_t *ret_uncom
|
||||
return k;
|
||||
|
||||
if (ret == LZMA_STREAM_END) {
|
||||
if (ret_uncompressed_size)
|
||||
*ret_uncompressed_size = s.total_in;
|
||||
|
||||
log_debug("XZ compression finished (%"PRIu64" -> %"PRIu64" bytes, %.1f%%)",
|
||||
s.total_in, s.total_out,
|
||||
(double) s.total_out / s.total_in * 100);
|
||||
@ -629,15 +626,14 @@ int compress_stream_xz(int fdf, int fdt, uint64_t max_bytes, uint64_t *ret_uncom
|
||||
|
||||
#define LZ4_BUFSIZE (512*1024u)
|
||||
|
||||
int compress_stream_lz4(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) {
|
||||
|
||||
#if HAVE_LZ4
|
||||
LZ4F_errorCode_t c;
|
||||
_cleanup_(LZ4F_freeCompressionContextp) LZ4F_compressionContext_t ctx = NULL;
|
||||
_cleanup_free_ void *in_buff = NULL;
|
||||
_cleanup_free_ char *out_buff = NULL;
|
||||
size_t out_allocsize, n, offset = 0, frame_size;
|
||||
uint64_t total_in = 0, total_out;
|
||||
size_t out_allocsize, n, total_in = 0, total_out, offset = 0, frame_size;
|
||||
int r;
|
||||
static const LZ4F_preferences_t preferences = {
|
||||
.frameInfo.blockSizeID = 5,
|
||||
@ -702,10 +698,7 @@ int compress_stream_lz4(int fdf, int fdt, uint64_t max_bytes, uint64_t *ret_unco
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (ret_uncompressed_size)
|
||||
*ret_uncompressed_size = total_in;
|
||||
|
||||
log_debug("LZ4 compression finished (%" PRIu64 " -> %" PRIu64 " bytes, %.1f%%)",
|
||||
log_debug("LZ4 compression finished (%zu -> %zu bytes, %.1f%%)",
|
||||
total_in, total_out,
|
||||
(double) total_out / total_in * 100);
|
||||
|
||||
@ -851,7 +844,7 @@ int decompress_stream_lz4(int in, int out, uint64_t max_bytes) {
|
||||
#endif
|
||||
}
|
||||
|
||||
int compress_stream_zstd(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) {
|
||||
#if HAVE_ZSTD
|
||||
_cleanup_(ZSTD_freeCCtxp) ZSTD_CCtx *cctx = NULL;
|
||||
_cleanup_free_ void *in_buff = NULL, *out_buff = NULL;
|
||||
@ -940,9 +933,6 @@ int compress_stream_zstd(int fdf, int fdt, uint64_t max_bytes, uint64_t *ret_unc
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret_uncompressed_size)
|
||||
*ret_uncompressed_size = in_bytes;
|
||||
|
||||
if (in_bytes > 0)
|
||||
log_debug("ZSTD compression finished (%" PRIu64 " -> %" PRIu64 " bytes, %.1f%%)",
|
||||
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,
|
||||
uint8_t extra);
|
||||
|
||||
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, uint64_t *ret_uncompressed_size);
|
||||
int compress_stream_zstd(int fdf, int fdt, uint64_t max_bytes, uint64_t *ret_uncompressed_size);
|
||||
int compress_stream_xz(int fdf, int fdt, uint64_t max_bytes);
|
||||
int compress_stream_lz4(int fdf, int fdt, uint64_t max_bytes);
|
||||
int compress_stream_zstd(int fdf, int fdt, uint64_t max_bytes);
|
||||
|
||||
int decompress_stream_xz(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 COMPRESSED_EXT ".xz"
|
||||
#else
|
||||
static inline int compress_stream(int fdf, int fdt, uint64_t max_size, uint64_t *ret_uncompressed_size) {
|
||||
static inline int compress_stream(int fdf, int fdt, uint64_t max_size) {
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
# 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,
|
||||
uint8_t extra);
|
||||
|
||||
typedef int (compress_stream_t)(int fdf, int fdt, uint64_t max_bytes, uint64_t *uncompressed_size);
|
||||
typedef int (compress_stream_t)(int fdf, int fdt, uint64_t max_bytes);
|
||||
typedef int (decompress_stream_t)(int fdf, int fdt, uint64_t max_size);
|
||||
|
||||
#if HAVE_COMPRESSION
|
||||
@ -176,7 +176,6 @@ _unused_ static void test_compress_stream(const char *compression,
|
||||
int r;
|
||||
_cleanup_free_ char *cmd = NULL, *cmd2 = NULL;
|
||||
struct stat st = {};
|
||||
uint64_t uncompressed_size;
|
||||
|
||||
r = find_executable(cat, NULL);
|
||||
if (r < 0) {
|
||||
@ -194,7 +193,7 @@ _unused_ static void test_compress_stream(const char *compression,
|
||||
|
||||
assert_se((dst = mkostemp_safe(pattern)) >= 0);
|
||||
|
||||
assert_se(compress(src, dst, -1, &uncompressed_size) == 0);
|
||||
assert_se(compress(src, dst, -1) == 0);
|
||||
|
||||
if (cat) {
|
||||
assert_se(asprintf(&cmd, "%s %s | diff %s -", cat, pattern, srcfile) > 0);
|
||||
@ -206,7 +205,6 @@ _unused_ static void test_compress_stream(const char *compression,
|
||||
assert_se((dst2 = mkostemp_safe(pattern2)) >= 0);
|
||||
|
||||
assert_se(stat(srcfile, &st) == 0);
|
||||
assert_se((uint64_t)st.st_size == uncompressed_size);
|
||||
|
||||
assert_se(lseek(dst, 0, SEEK_SET) == 0);
|
||||
r = decompress(dst, dst2, st.st_size);
|
||||
|
||||
@ -37,6 +37,7 @@ int main(int argc, char **argv) {
|
||||
test_table(netdev_kind, NETDEV_KIND);
|
||||
test_table(nl_union_link_info_data, NL_UNION_LINK_INFO_DATA);
|
||||
test_table(radv_prefix_delegation, RADV_PREFIX_DELEGATION);
|
||||
test_table(wol, WOL);
|
||||
test_table(lldp_event, SD_LLDP_EVENT);
|
||||
test_table(ndisc_event, SD_NDISC_EVENT);
|
||||
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]");
|
||||
|
||||
else if ((STR_IN_SET(name, "DefaultMemoryLow", "DefaultMemoryMin", "MemoryLow", "MemoryHigh", "MemoryMax", "MemorySwapMax", "MemoryLimit", "MemoryAvailable") && u == CGROUP_LIMIT_MAX) ||
|
||||
else if ((STR_IN_SET(name, "DefaultMemoryLow", "DefaultMemoryMin", "MemoryLow", "MemoryHigh", "MemoryMax", "MemorySwapMax", "MemoryLimit") && u == CGROUP_LIMIT_MAX) ||
|
||||
(STR_IN_SET(name, "TasksMax", "DefaultTasksMax") && u == UINT64_MAX) ||
|
||||
(startswith(name, "Limit") && u == UINT64_MAX) ||
|
||||
(startswith(name, "DefaultLimit") && u == UINT64_MAX))
|
||||
|
||||
@ -24,38 +24,19 @@ static const char* const duplex_table[_DUP_MAX] = {
|
||||
DEFINE_STRING_TABLE_LOOKUP(duplex, Duplex);
|
||||
DEFINE_CONFIG_PARSE_ENUM(config_parse_duplex, duplex, Duplex, "Failed to parse duplex setting");
|
||||
|
||||
static const struct {
|
||||
uint32_t opt;
|
||||
const char *name;
|
||||
} wol_option_map[] = {
|
||||
{ WAKE_PHY, "phy" },
|
||||
{ WAKE_UCAST, "unicast", },
|
||||
{ WAKE_MCAST, "multicast", },
|
||||
{ WAKE_BCAST, "broadcast", },
|
||||
{ WAKE_ARP, "arp", },
|
||||
{ WAKE_MAGIC, "magic", },
|
||||
{ WAKE_MAGICSECURE, "secureon", },
|
||||
static const char* const wol_table[_WOL_MAX] = {
|
||||
[WOL_PHY] = "phy",
|
||||
[WOL_UCAST] = "unicast",
|
||||
[WOL_MCAST] = "multicast",
|
||||
[WOL_BCAST] = "broadcast",
|
||||
[WOL_ARP] = "arp",
|
||||
[WOL_MAGIC] = "magic",
|
||||
[WOL_MAGICSECURE] = "secureon",
|
||||
[WOL_OFF] = "off",
|
||||
};
|
||||
|
||||
int wol_options_to_string_alloc(uint32_t opts, char **ret) {
|
||||
_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;
|
||||
}
|
||||
DEFINE_STRING_TABLE_LOOKUP(wol, WakeOnLan);
|
||||
DEFINE_CONFIG_PARSE_ENUM(config_parse_wol, wol, WakeOnLan, "Failed to parse WakeOnLan setting");
|
||||
|
||||
static const char* const port_table[] = {
|
||||
[NET_DEV_PORT_TP] = "tp",
|
||||
@ -329,7 +310,7 @@ int ethtool_get_permanent_macaddr(int *ethtool_fd, const char *ifname, struct et
|
||||
dest = _v; \
|
||||
} while(false)
|
||||
|
||||
int ethtool_set_wol(int *ethtool_fd, const char *ifname, uint32_t wolopts) {
|
||||
int ethtool_set_wol(int *ethtool_fd, const char *ifname, WakeOnLan wol) {
|
||||
struct ethtool_wolinfo ecmd = {
|
||||
.cmd = ETHTOOL_GWOL,
|
||||
};
|
||||
@ -342,7 +323,7 @@ int ethtool_set_wol(int *ethtool_fd, const char *ifname, uint32_t wolopts) {
|
||||
assert(ethtool_fd);
|
||||
assert(ifname);
|
||||
|
||||
if (wolopts == UINT32_MAX)
|
||||
if (wol == _WOL_INVALID)
|
||||
return 0;
|
||||
|
||||
r = ethtool_connect(ethtool_fd);
|
||||
@ -355,15 +336,66 @@ int ethtool_set_wol(int *ethtool_fd, const char *ifname, uint32_t wolopts) {
|
||||
if (r < 0)
|
||||
return -errno;
|
||||
|
||||
UPDATE(ecmd.wolopts, wolopts, need_update);
|
||||
switch (wol) {
|
||||
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)
|
||||
return 0;
|
||||
if (need_update) {
|
||||
ecmd.cmd = ETHTOOL_SWOL;
|
||||
|
||||
ecmd.cmd = ETHTOOL_SWOL;
|
||||
r = ioctl(*ethtool_fd, SIOCETHTOOL, &ifr);
|
||||
if (r < 0)
|
||||
return -errno;
|
||||
r = ioctl(*ethtool_fd, SIOCETHTOOL, &ifr);
|
||||
if (r < 0)
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -973,6 +1005,7 @@ int config_parse_advertise(
|
||||
void *userdata) {
|
||||
|
||||
uint32_t *advertise = data;
|
||||
const char *p;
|
||||
int r;
|
||||
|
||||
assert(filename);
|
||||
@ -987,7 +1020,7 @@ int config_parse_advertise(
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (const char *p = rvalue;;) {
|
||||
for (p = rvalue;;) {
|
||||
_cleanup_free_ char *w = NULL;
|
||||
enum ethtool_link_mode_bit_indices mode;
|
||||
|
||||
@ -1065,69 +1098,3 @@ int config_parse_nic_buffer_size(
|
||||
|
||||
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,6 +18,19 @@ typedef enum Duplex {
|
||||
_DUP_INVALID = -EINVAL,
|
||||
} 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 {
|
||||
NET_DEV_FEAT_RX,
|
||||
NET_DEV_FEAT_TX,
|
||||
@ -86,7 +99,7 @@ int ethtool_get_link_info(int *ethtool_fd, const char *ifname,
|
||||
int *ret_autonegotiation, uint64_t *ret_speed,
|
||||
Duplex *ret_duplex, NetDevPort *ret_port);
|
||||
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, uint32_t wolopts);
|
||||
int ethtool_set_wol(int *ethtool_fd, const char *ifname, WakeOnLan wol);
|
||||
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_glinksettings(int *ethtool_fd, const char *ifname,
|
||||
@ -98,7 +111,8 @@ int ethtool_set_flow_control(int *fd, const char *ifname, int rx, int tx, int au
|
||||
const char *duplex_to_string(Duplex d) _const_;
|
||||
Duplex duplex_from_string(const char *d) _pure_;
|
||||
|
||||
int wol_options_to_string_alloc(uint32_t opts, char **ret);
|
||||
const char *wol_to_string(WakeOnLan wol) _const_;
|
||||
WakeOnLan wol_from_string(const char *wol) _pure_;
|
||||
|
||||
const char *port_to_string(NetDevPort port) _const_;
|
||||
NetDevPort port_from_string(const char *port) _pure_;
|
||||
|
||||
@ -247,7 +247,6 @@ typedef struct UnitStatusInfo {
|
||||
uint64_t memory_max;
|
||||
uint64_t memory_swap_max;
|
||||
uint64_t memory_limit;
|
||||
uint64_t memory_available;
|
||||
uint64_t cpu_usage_nsec;
|
||||
uint64_t tasks_current;
|
||||
uint64_t tasks_max;
|
||||
@ -683,7 +682,6 @@ static void print_status_info(
|
||||
if (i->memory_min > 0 || i->memory_low > 0 ||
|
||||
i->memory_high != CGROUP_LIMIT_MAX || i->memory_max != CGROUP_LIMIT_MAX ||
|
||||
i->memory_swap_max != CGROUP_LIMIT_MAX ||
|
||||
i->memory_available != CGROUP_LIMIT_MAX ||
|
||||
i->memory_limit != CGROUP_LIMIT_MAX) {
|
||||
const char *prefix = "";
|
||||
|
||||
@ -712,10 +710,6 @@ static void print_status_info(
|
||||
printf("%slimit: %s", prefix, format_bytes(buf, sizeof(buf), i->memory_limit));
|
||||
prefix = " ";
|
||||
}
|
||||
if (i->memory_available != CGROUP_LIMIT_MAX) {
|
||||
printf("%savailable: %s", prefix, format_bytes(buf, sizeof(buf), i->memory_available));
|
||||
prefix = " ";
|
||||
}
|
||||
printf(")");
|
||||
}
|
||||
printf("\n");
|
||||
@ -1833,7 +1827,6 @@ static int show_one(
|
||||
{ "Where", "s", NULL, offsetof(UnitStatusInfo, where) },
|
||||
{ "What", "s", NULL, offsetof(UnitStatusInfo, what) },
|
||||
{ "MemoryCurrent", "t", NULL, offsetof(UnitStatusInfo, memory_current) },
|
||||
{ "MemoryAvailable", "t", NULL, offsetof(UnitStatusInfo, memory_available) },
|
||||
{ "DefaultMemoryMin", "t", NULL, offsetof(UnitStatusInfo, default_memory_min) },
|
||||
{ "DefaultMemoryLow", "t", NULL, offsetof(UnitStatusInfo, default_memory_low) },
|
||||
{ "MemoryMin", "t", NULL, offsetof(UnitStatusInfo, memory_min) },
|
||||
@ -1876,7 +1869,6 @@ static int show_one(
|
||||
.memory_max = CGROUP_LIMIT_MAX,
|
||||
.memory_swap_max = CGROUP_LIMIT_MAX,
|
||||
.memory_limit = UINT64_MAX,
|
||||
.memory_available = CGROUP_LIMIT_MAX,
|
||||
.cpu_usage_nsec = UINT64_MAX,
|
||||
.tasks_current = UINT64_MAX,
|
||||
.tasks_max = UINT64_MAX,
|
||||
|
||||
@ -110,17 +110,6 @@ typedef enum ItemType {
|
||||
ADJUST_MODE = 'm', /* legacy, 'z' is identical to this */
|
||||
} 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 {
|
||||
ItemType type;
|
||||
|
||||
@ -135,7 +124,6 @@ typedef struct Item {
|
||||
gid_t gid;
|
||||
mode_t mode;
|
||||
usec_t age;
|
||||
AgeBy age_by_file, age_by_dir;
|
||||
|
||||
dev_t major_minor;
|
||||
unsigned attribute_value;
|
||||
@ -154,8 +142,6 @@ typedef struct Item {
|
||||
|
||||
bool allow_failure:1;
|
||||
|
||||
bool try_replace:1;
|
||||
|
||||
OperationMask done;
|
||||
} Item;
|
||||
|
||||
@ -517,64 +503,6 @@ static inline nsec_t load_statx_timestamp_nsec(const struct statx_timestamp *ts)
|
||||
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(
|
||||
Item *i,
|
||||
const char *p,
|
||||
@ -586,9 +514,7 @@ static int dir_cleanup(
|
||||
dev_t rootdev_minor,
|
||||
bool mountpoint,
|
||||
int maxdepth,
|
||||
bool keep_this_level,
|
||||
AgeBy age_by_file,
|
||||
AgeBy age_by_dir) {
|
||||
bool keep_this_level) {
|
||||
|
||||
bool deleted = false;
|
||||
struct dirent *dent;
|
||||
@ -713,8 +639,7 @@ static int dir_cleanup(
|
||||
sub_path, sub_dir,
|
||||
atime_nsec, mtime_nsec, cutoff_nsec,
|
||||
rootdev_major, rootdev_minor,
|
||||
false, maxdepth-1, false,
|
||||
age_by_file, age_by_dir);
|
||||
false, maxdepth-1, false);
|
||||
if (q < 0)
|
||||
r = q;
|
||||
}
|
||||
@ -729,13 +654,31 @@ static int dir_cleanup(
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check the file timestamps of an entry against the
|
||||
* given cutoff time; delete if it is older.
|
||||
*/
|
||||
if (!needs_cleanup(atime_nsec, btime_nsec, ctime_nsec, mtime_nsec,
|
||||
cutoff_nsec, sub_path, age_by_dir, true))
|
||||
/* Ignore ctime, we change it when deleting */
|
||||
if (mtime_nsec != NSEC_INFINITY && mtime_nsec >= cutoff_nsec) {
|
||||
char a[FORMAT_TIMESTAMP_MAX];
|
||||
/* Follows spelling in stat(1). */
|
||||
log_debug("Directory \"%s\": modify time %s is too new.",
|
||||
sub_path,
|
||||
format_timestamp_style(a, sizeof(a), mtime_nsec / NSEC_PER_USEC, TIMESTAMP_US));
|
||||
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);
|
||||
if (unlinkat(dirfd(d), dent->d_name, AT_REMOVEDIR) < 0)
|
||||
@ -779,9 +722,38 @@ static int dir_cleanup(
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!needs_cleanup(atime_nsec, btime_nsec, ctime_nsec, mtime_nsec,
|
||||
cutoff_nsec, sub_path, age_by_file, false))
|
||||
if (mtime_nsec != NSEC_INFINITY && mtime_nsec >= cutoff_nsec) {
|
||||
char a[FORMAT_TIMESTAMP_MAX];
|
||||
/* 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;
|
||||
}
|
||||
|
||||
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);
|
||||
if (unlinkat(dirfd(d), dent->d_name, 0) < 0)
|
||||
@ -2030,180 +2002,6 @@ static int glob_item_recursively(Item *i, fdaction_t action) {
|
||||
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) {
|
||||
CreationMode creation;
|
||||
int r = 0;
|
||||
@ -2222,9 +2020,8 @@ static int create_item(Item *i) {
|
||||
|
||||
case TRUNCATE_FILE:
|
||||
case CREATE_FILE:
|
||||
r = mkdir_parents_item(i, S_IFREG);
|
||||
if (r < 0)
|
||||
return r;
|
||||
RUN_WITH_UMASK(0000)
|
||||
(void) mkdir_parents_label(i->path, 0755);
|
||||
|
||||
if ((i->type == CREATE_FILE && i->append_or_force) || i->type == TRUNCATE_FILE)
|
||||
r = truncate_file(i, i->path);
|
||||
@ -2236,9 +2033,8 @@ static int create_item(Item *i) {
|
||||
break;
|
||||
|
||||
case COPY_FILES:
|
||||
r = mkdir_parents_item(i, 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
RUN_WITH_UMASK(0000)
|
||||
(void) mkdir_parents_label(i->path, 0755);
|
||||
|
||||
r = copy_files(i);
|
||||
if (r < 0)
|
||||
@ -2254,9 +2050,8 @@ static int create_item(Item *i) {
|
||||
|
||||
case CREATE_DIRECTORY:
|
||||
case TRUNCATE_DIRECTORY:
|
||||
r = mkdir_parents_item(i, S_IFDIR);
|
||||
if (r < 0)
|
||||
return r;
|
||||
RUN_WITH_UMASK(0000)
|
||||
(void) mkdir_parents_label(i->path, 0755);
|
||||
|
||||
r = create_directory(i, i->path);
|
||||
if (r < 0)
|
||||
@ -2266,9 +2061,8 @@ static int create_item(Item *i) {
|
||||
case CREATE_SUBVOLUME:
|
||||
case CREATE_SUBVOLUME_INHERIT_QUOTA:
|
||||
case CREATE_SUBVOLUME_NEW_QUOTA:
|
||||
r = mkdir_parents_item(i, S_IFDIR);
|
||||
if (r < 0)
|
||||
return r;
|
||||
RUN_WITH_UMASK(0000)
|
||||
(void) mkdir_parents_label(i->path, 0755);
|
||||
|
||||
r = create_subvolume(i, i->path);
|
||||
if (r < 0)
|
||||
@ -2282,9 +2076,8 @@ static int create_item(Item *i) {
|
||||
break;
|
||||
|
||||
case CREATE_FIFO:
|
||||
r = mkdir_parents_item(i, S_IFIFO);
|
||||
if (r < 0)
|
||||
return r;
|
||||
RUN_WITH_UMASK(0000)
|
||||
(void) mkdir_parents_label(i->path, 0755);
|
||||
|
||||
r = create_fifo(i, i->path);
|
||||
if (r < 0)
|
||||
@ -2292,9 +2085,8 @@ static int create_item(Item *i) {
|
||||
break;
|
||||
|
||||
case CREATE_SYMLINK: {
|
||||
r = mkdir_parents_item(i, S_IFLNK);
|
||||
if (r < 0)
|
||||
return r;
|
||||
RUN_WITH_UMASK(0000)
|
||||
(void) mkdir_parents_label(i->path, 0755);
|
||||
|
||||
mac_selinux_create_file_prepare(i->path, S_IFLNK);
|
||||
r = symlink(i->argument, i->path);
|
||||
@ -2350,9 +2142,8 @@ static int create_item(Item *i) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = mkdir_parents_item(i, i->type == CREATE_BLOCK_DEVICE ? S_IFBLK : S_IFCHR);
|
||||
if (r < 0)
|
||||
return r;
|
||||
RUN_WITH_UMASK(0000)
|
||||
(void) mkdir_parents_label(i->path, 0755);
|
||||
|
||||
r = create_device(i, i->type == CREATE_BLOCK_DEVICE ? S_IFBLK : S_IFCHR);
|
||||
if (r < 0)
|
||||
@ -2469,23 +2260,6 @@ 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) {
|
||||
char timestamp[FORMAT_TIMESTAMP_MAX];
|
||||
_cleanup_closedir_ DIR *d = NULL;
|
||||
@ -2532,31 +2306,17 @@ static int clean_item_instance(Item *i, const char* instance) {
|
||||
sx.stx_ino != ps.st_ino;
|
||||
}
|
||||
|
||||
if (DEBUG_LOGGING) {
|
||||
_cleanup_free_ char *ab_f = NULL, *ab_d = NULL;
|
||||
|
||||
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);
|
||||
}
|
||||
log_debug("Cleanup threshold for %s \"%s\" is %s",
|
||||
mountpoint ? "mount point" : "directory",
|
||||
instance,
|
||||
format_timestamp_style(timestamp, sizeof(timestamp), cutoff, TIMESTAMP_US));
|
||||
|
||||
return dir_cleanup(i, instance, d,
|
||||
load_statx_timestamp_nsec(&sx.stx_atime),
|
||||
load_statx_timestamp_nsec(&sx.stx_mtime),
|
||||
cutoff * NSEC_PER_USEC,
|
||||
sx.stx_dev_major, sx.stx_dev_minor, mountpoint,
|
||||
MAX_DEPTH, i->keep_first_level,
|
||||
i->age_by_file, i->age_by_dir);
|
||||
MAX_DEPTH, i->keep_first_level);
|
||||
}
|
||||
|
||||
static int clean_item(Item *i) {
|
||||
@ -2722,9 +2482,6 @@ static bool item_compatible(Item *a, Item *b) {
|
||||
a->age_set == b->age_set &&
|
||||
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->keep_first_level == b->keep_first_level &&
|
||||
@ -2889,58 +2646,6 @@ static int find_gid(const char *group, gid_t *ret_gid, Hashmap **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(
|
||||
const char *fname,
|
||||
unsigned line,
|
||||
@ -2950,15 +2655,11 @@ static int parse_line(
|
||||
Hashmap **gid_cache) {
|
||||
|
||||
_cleanup_free_ char *action = NULL, *mode = NULL, *user = NULL, *group = NULL, *age = NULL, *path = NULL;
|
||||
_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,
|
||||
};
|
||||
_cleanup_(item_free_contents) Item i = {};
|
||||
ItemArray *existing;
|
||||
OrderedHashmap *h;
|
||||
int r, pos;
|
||||
bool append_or_force = false, boot = false, allow_failure = false, try_replace = false;
|
||||
bool append_or_force = false, boot = false, allow_failure = false;
|
||||
|
||||
assert(fname);
|
||||
assert(line >= 1);
|
||||
@ -3003,8 +2704,6 @@ static int parse_line(
|
||||
append_or_force = true;
|
||||
else if (action[pos] == '-' && !allow_failure)
|
||||
allow_failure = true;
|
||||
else if (action[pos] == '=' && !try_replace)
|
||||
try_replace = true;
|
||||
else {
|
||||
*invalid_config = true;
|
||||
return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG), "Unknown modifiers in command '%s'", action);
|
||||
@ -3019,7 +2718,6 @@ static int parse_line(
|
||||
i.type = action[0];
|
||||
i.append_or_force = append_or_force;
|
||||
i.allow_failure = allow_failure;
|
||||
i.try_replace = try_replace;
|
||||
|
||||
r = specifier_printf(path, PATH_MAX-1, specifier_table, NULL, &i.path);
|
||||
if (r == -ENXIO)
|
||||
@ -3228,37 +2926,16 @@ static int parse_line(
|
||||
|
||||
if (!empty_or_dash(age)) {
|
||||
const char *a = age;
|
||||
_cleanup_free_ char *seconds = NULL, *age_by = NULL;
|
||||
|
||||
if (*a == '~') {
|
||||
i.keep_first_level = true;
|
||||
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);
|
||||
if (r < 0) {
|
||||
*invalid_config = true;
|
||||
return log_syntax(NULL, LOG_ERR, fname, line, r, "Invalid age '%s'.", a);
|
||||
return log_syntax(NULL, LOG_ERR, fname, line, r, "Invalid age '%s'.", age);
|
||||
}
|
||||
|
||||
i.age_set = true;
|
||||
|
||||
@ -134,7 +134,7 @@ int link_load_one(LinkConfigContext *ctx, const char *filename) {
|
||||
*link = (LinkConfig) {
|
||||
.filename = TAKE_PTR(name),
|
||||
.mac_address_policy = _MAC_ADDRESS_POLICY_INVALID,
|
||||
.wol = UINT32_MAX, /* UINT32_MAX means do not change WOL setting. */
|
||||
.wol = _WOL_INVALID,
|
||||
.duplex = _DUP_INVALID,
|
||||
.port = _NET_DEV_PORT_INVALID,
|
||||
.autonegotiation = -1,
|
||||
@ -329,13 +329,9 @@ static int link_config_apply_ethtool_settings(int *ethtool_fd, const LinkConfig
|
||||
}
|
||||
|
||||
r = ethtool_set_wol(ethtool_fd, name, config->wol);
|
||||
if (r < 0) {
|
||||
_cleanup_free_ char *str = NULL;
|
||||
|
||||
(void) wol_options_to_string_alloc(config->wol, &str);
|
||||
if (r < 0)
|
||||
log_device_warning_errno(device, r, "Could not set WakeOnLan to %s, ignoring: %m",
|
||||
strna(str));
|
||||
}
|
||||
wol_to_string(config->wol));
|
||||
|
||||
r = ethtool_set_features(ethtool_fd, name, config->features);
|
||||
if (r < 0)
|
||||
|
||||
@ -56,7 +56,7 @@ struct LinkConfig {
|
||||
Duplex duplex;
|
||||
int autonegotiation;
|
||||
uint32_t advertise[N_ADVERTISE];
|
||||
uint32_t wol;
|
||||
WakeOnLan wol;
|
||||
NetDevPort port;
|
||||
int features[_NET_DEV_FEAT_MAX];
|
||||
netdev_channels channels;
|
||||
|
||||
@ -48,7 +48,6 @@ 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/bangs', user=user)
|
||||
test_line('f== /too/many/equals', 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/filename/%Y - - - - "whatever"', user=user)
|
||||
@ -74,11 +73,9 @@ def test_uninitialized_t():
|
||||
test_line('w /foo - - - - "specifier for --user %t"',
|
||||
user=True, returncode=0, extra={'env':{}})
|
||||
|
||||
def test_content(line, expected, *, user, extra={}, subpath='/arg', path_cb=None):
|
||||
def test_content(line, expected, *, user, extra={}):
|
||||
d = tempfile.TemporaryDirectory(prefix='test-systemd-tmpfiles.')
|
||||
if path_cb is not None:
|
||||
path_cb(d.name, subpath)
|
||||
arg = d.name + subpath
|
||||
arg = d.name + '/arg'
|
||||
spec = line.format(arg)
|
||||
test_line(spec, user=user, returncode=0, extra=extra)
|
||||
content = open(arg).read()
|
||||
@ -137,57 +134,6 @@ def test_valid_specifiers(*, 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__':
|
||||
test_invalids(user=False)
|
||||
test_invalids(user=True)
|
||||
@ -195,6 +141,3 @@ if __name__ == '__main__':
|
||||
|
||||
test_valid_specifiers(user=False)
|
||||
test_valid_specifiers(user=True)
|
||||
|
||||
test_hard_cleanup(user=False)
|
||||
test_hard_cleanup(user=True)
|
||||
|
||||
@ -1,196 +0,0 @@
|
||||
#! /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