Compare commits
19 Commits
67d67875b4
...
c0316a84d3
Author | SHA1 | Date |
---|---|---|
James R T | c0316a84d3 | |
Luca Boccassi | 2e5b0412f9 | |
Martin Srebotnjak | 69af4849aa | |
Jiri Grönroos | 18d4e0be89 | |
Dmytro Markevych | 7d7b89a015 | |
Léane GRASSER | 8a92365f79 | |
Yu Watanabe | 2b397d43ab | |
Yu Watanabe | 9ad294efd0 | |
Lennart Poettering | f6793bbcf0 | |
Mike Yuan | f87863a8ff | |
Antonio Alvarez Feijoo | 58c3c2886d | |
Daan De Meyer | dbbe895807 | |
Yu Watanabe | 52b0351a15 | |
Luca Boccassi | fe077a1a58 | |
Mike Yuan | b718b86e1b | |
Mike Yuan | d911778877 | |
Mike Yuan | eea9d3eb10 | |
Mike Yuan | 579ce77ead | |
James Raphael Tiovalen | 1a34e4709f |
|
@ -498,6 +498,7 @@ Most path unit settings are available to transient units.
|
||||||
Unit=
|
Unit=
|
||||||
✓ MakeDirectory=
|
✓ MakeDirectory=
|
||||||
✓ DirectoryMode=
|
✓ DirectoryMode=
|
||||||
|
✓ DeactivationToggle=
|
||||||
```
|
```
|
||||||
|
|
||||||
## Install Section
|
## Install Section
|
||||||
|
|
|
@ -10715,6 +10715,8 @@ node /org/freedesktop/systemd1/unit/cups_2epath {
|
||||||
readonly t TriggerLimitIntervalUSec = ...;
|
readonly t TriggerLimitIntervalUSec = ...;
|
||||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||||
readonly u TriggerLimitBurst = ...;
|
readonly u TriggerLimitBurst = ...;
|
||||||
|
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||||
|
readonly b DeactivationToggle = ...;
|
||||||
};
|
};
|
||||||
interface org.freedesktop.DBus.Peer { ... };
|
interface org.freedesktop.DBus.Peer { ... };
|
||||||
interface org.freedesktop.DBus.Introspectable { ... };
|
interface org.freedesktop.DBus.Introspectable { ... };
|
||||||
|
@ -10731,6 +10733,8 @@ node /org/freedesktop/systemd1/unit/cups_2epath {
|
||||||
|
|
||||||
<!--property TriggerLimitBurst is not documented!-->
|
<!--property TriggerLimitBurst is not documented!-->
|
||||||
|
|
||||||
|
<!--property DeactivationToggle is not documented!-->
|
||||||
|
|
||||||
<!--Autogenerated cross-references for systemd.directives, do not edit-->
|
<!--Autogenerated cross-references for systemd.directives, do not edit-->
|
||||||
|
|
||||||
<variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.systemd1.Unit"/>
|
<variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.systemd1.Unit"/>
|
||||||
|
@ -10755,6 +10759,8 @@ node /org/freedesktop/systemd1/unit/cups_2epath {
|
||||||
|
|
||||||
<variablelist class="dbus-property" generated="True" extra-ref="TriggerLimitBurst"/>
|
<variablelist class="dbus-property" generated="True" extra-ref="TriggerLimitBurst"/>
|
||||||
|
|
||||||
|
<variablelist class="dbus-property" generated="True" extra-ref="DeactivationToggle"/>
|
||||||
|
|
||||||
<!--End of Autogenerated section-->
|
<!--End of Autogenerated section-->
|
||||||
|
|
||||||
<refsect2>
|
<refsect2>
|
||||||
|
@ -12488,6 +12494,10 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
|
||||||
<title>Timer Objects</title>
|
<title>Timer Objects</title>
|
||||||
<para><varname>DeferReactivation</varname> was added in version 257.</para>
|
<para><varname>DeferReactivation</varname> was added in version 257.</para>
|
||||||
</refsect2>
|
</refsect2>
|
||||||
|
<refsect2>
|
||||||
|
<title>Path Objects</title>
|
||||||
|
<para><varname>DeactivationToggle</varname> was added in version 257.</para>
|
||||||
|
</refsect2>
|
||||||
</refsect1>
|
</refsect1>
|
||||||
|
|
||||||
<refsect1>
|
<refsect1>
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
|
|
||||||
<para>A unit configuration file whose name ends in
|
<para>A unit configuration file whose name ends in
|
||||||
<literal>.path</literal> encodes information about a path
|
<literal>.path</literal> encodes information about a path
|
||||||
monitored by systemd, for path-based activation.</para>
|
monitored by systemd, for path-based activation and deactivation.</para>
|
||||||
|
|
||||||
<para>This man page lists the configuration options specific to
|
<para>This man page lists the configuration options specific to
|
||||||
this unit type. See
|
this unit type. See
|
||||||
|
@ -206,6 +206,21 @@
|
||||||
|
|
||||||
<xi:include href="version-info.xml" xpointer="v250"/></listitem>
|
<xi:include href="version-info.xml" xpointer="v250"/></listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term><varname>DeactivationToggle=</varname></term>
|
||||||
|
|
||||||
|
<listitem><para>Takes a boolean argument. If true, the
|
||||||
|
triggered unit is stopped and the path unit goes back to
|
||||||
|
waiting state when the path it watches is removed for
|
||||||
|
<varname>PathExists=</varname>, when there are no matching
|
||||||
|
paths for <varname>PathExistsGlob=</varname>, or when the
|
||||||
|
directory is empty for <varname>DirectoryNotEmpty=</varname>.
|
||||||
|
This option is ignored for <varname>PathChanged=</varname> and
|
||||||
|
<varname>PathModified=</varname> settings. Defaults to
|
||||||
|
<option>false</option>.</para>
|
||||||
|
|
||||||
|
<xi:include href="version-info.xml" xpointer="v257"/></listitem>
|
||||||
|
</varlistentry>
|
||||||
</variablelist>
|
</variablelist>
|
||||||
|
|
||||||
<xi:include href="systemd.service.xml" xpointer="shared-unit-options" />
|
<xi:include href="systemd.service.xml" xpointer="shared-unit-options" />
|
||||||
|
|
18
po/fi.po
18
po/fi.po
|
@ -3,12 +3,13 @@
|
||||||
# Finnish translation of systemd.
|
# Finnish translation of systemd.
|
||||||
# Jan Kuparinen <copper_fin@hotmail.com>, 2021, 2022, 2023.
|
# Jan Kuparinen <copper_fin@hotmail.com>, 2021, 2022, 2023.
|
||||||
# Ricky Tigg <ricky.tigg@gmail.com>, 2022, 2024.
|
# Ricky Tigg <ricky.tigg@gmail.com>, 2022, 2024.
|
||||||
|
# Jiri Grönroos <jiri.gronroos@iki.fi>, 2024.
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2024-11-06 14:42+0000\n"
|
"POT-Creation-Date: 2024-11-06 14:42+0000\n"
|
||||||
"PO-Revision-Date: 2024-09-12 13:43+0000\n"
|
"PO-Revision-Date: 2024-11-20 19:13+0000\n"
|
||||||
"Last-Translator: Ricky Tigg <ricky.tigg@gmail.com>\n"
|
"Last-Translator: Jiri Grönroos <jiri.gronroos@iki.fi>\n"
|
||||||
"Language-Team: Finnish <https://translate.fedoraproject.org/projects/systemd/"
|
"Language-Team: Finnish <https://translate.fedoraproject.org/projects/systemd/"
|
||||||
"main/fi/>\n"
|
"main/fi/>\n"
|
||||||
"Language: fi\n"
|
"Language: fi\n"
|
||||||
|
@ -16,7 +17,7 @@ msgstr ""
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
||||||
"X-Generator: Weblate 5.7.2\n"
|
"X-Generator: Weblate 5.8.2\n"
|
||||||
|
|
||||||
#: src/core/org.freedesktop.systemd1.policy.in:22
|
#: src/core/org.freedesktop.systemd1.policy.in:22
|
||||||
msgid "Send passphrase back to system"
|
msgid "Send passphrase back to system"
|
||||||
|
@ -112,14 +113,12 @@ msgid "Authentication is required to update a user's home area."
|
||||||
msgstr "Todennus vaaditaan käyttäjän kotialueen päivittämiseksi."
|
msgstr "Todennus vaaditaan käyttäjän kotialueen päivittämiseksi."
|
||||||
|
|
||||||
#: src/home/org.freedesktop.home1.policy:53
|
#: src/home/org.freedesktop.home1.policy:53
|
||||||
#, fuzzy
|
|
||||||
msgid "Update your home area"
|
msgid "Update your home area"
|
||||||
msgstr "Päivitä kotialue"
|
msgstr "Päivitä kotialue"
|
||||||
|
|
||||||
#: src/home/org.freedesktop.home1.policy:54
|
#: src/home/org.freedesktop.home1.policy:54
|
||||||
#, fuzzy
|
|
||||||
msgid "Authentication is required to update your home area."
|
msgid "Authentication is required to update your home area."
|
||||||
msgstr "Todennus vaaditaan käyttäjän kotialueen päivittämiseksi."
|
msgstr "Todennus vaaditaan kotialueen päivittämiseksi."
|
||||||
|
|
||||||
#: src/home/org.freedesktop.home1.policy:63
|
#: src/home/org.freedesktop.home1.policy:63
|
||||||
msgid "Resize a home area"
|
msgid "Resize a home area"
|
||||||
|
@ -1174,14 +1173,11 @@ msgstr "Todennus vaaditaan vanhojen järjestelmäpäivitysten puhdistamiseen."
|
||||||
|
|
||||||
#: src/sysupdate/org.freedesktop.sysupdate1.policy:75
|
#: src/sysupdate/org.freedesktop.sysupdate1.policy:75
|
||||||
msgid "Manage optional features"
|
msgid "Manage optional features"
|
||||||
msgstr ""
|
msgstr "Hallitse valinnaisia ominaisuuksia"
|
||||||
|
|
||||||
#: src/sysupdate/org.freedesktop.sysupdate1.policy:76
|
#: src/sysupdate/org.freedesktop.sysupdate1.policy:76
|
||||||
#, fuzzy
|
|
||||||
msgid "Authentication is required to manage optional features"
|
msgid "Authentication is required to manage optional features"
|
||||||
msgstr ""
|
msgstr "Todennus vaaditaan valinnaisten ominaisuuksien hallintaan"
|
||||||
"Todennus vaaditaan aktiivisten istuntojen, käyttäjien ja paikkojen "
|
|
||||||
"hallintaan."
|
|
||||||
|
|
||||||
#: src/timedate/org.freedesktop.timedate1.policy:22
|
#: src/timedate/org.freedesktop.timedate1.policy:22
|
||||||
msgid "Set system time"
|
msgid "Set system time"
|
||||||
|
|
6
po/fr.po
6
po/fr.po
|
@ -12,7 +12,7 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2024-11-06 14:42+0000\n"
|
"POT-Creation-Date: 2024-11-06 14:42+0000\n"
|
||||||
"PO-Revision-Date: 2024-11-07 09:30+0000\n"
|
"PO-Revision-Date: 2024-11-20 19:13+0000\n"
|
||||||
"Last-Translator: Léane GRASSER <leane.grasser@proton.me>\n"
|
"Last-Translator: Léane GRASSER <leane.grasser@proton.me>\n"
|
||||||
"Language-Team: French <https://translate.fedoraproject.org/projects/systemd/"
|
"Language-Team: French <https://translate.fedoraproject.org/projects/systemd/"
|
||||||
"main/fr/>\n"
|
"main/fr/>\n"
|
||||||
|
@ -360,8 +360,8 @@ msgid ""
|
||||||
"Authentication is required to set the statically configured local hostname, "
|
"Authentication is required to set the statically configured local hostname, "
|
||||||
"as well as the pretty hostname."
|
"as well as the pretty hostname."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Une authentification est requise pour définir le nom d'hôte local de manière "
|
"Une authentification est requise pour définir le nom d'hôte local configuré "
|
||||||
"statique, ainsi que le nom d'hôte familier."
|
"de manière statique, ainsi que le nom d'hôte convivial."
|
||||||
|
|
||||||
#: src/hostname/org.freedesktop.hostname1.policy:41
|
#: src/hostname/org.freedesktop.hostname1.policy:41
|
||||||
msgid "Set machine information"
|
msgid "Set machine information"
|
||||||
|
|
15
po/sl.po
15
po/sl.po
|
@ -7,7 +7,7 @@ msgstr ""
|
||||||
"Project-Id-Version: systemd\n"
|
"Project-Id-Version: systemd\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2024-11-06 14:42+0000\n"
|
"POT-Creation-Date: 2024-11-06 14:42+0000\n"
|
||||||
"PO-Revision-Date: 2024-08-26 19:38+0000\n"
|
"PO-Revision-Date: 2024-11-20 19:13+0000\n"
|
||||||
"Last-Translator: Martin Srebotnjak <miles@filmsi.net>\n"
|
"Last-Translator: Martin Srebotnjak <miles@filmsi.net>\n"
|
||||||
"Language-Team: Slovenian <https://translate.fedoraproject.org/projects/"
|
"Language-Team: Slovenian <https://translate.fedoraproject.org/projects/"
|
||||||
"systemd/main/sl/>\n"
|
"systemd/main/sl/>\n"
|
||||||
|
@ -17,7 +17,7 @@ msgstr ""
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"Plural-Forms: nplurals=4; plural=n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || "
|
"Plural-Forms: nplurals=4; plural=n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || "
|
||||||
"n%100==4 ? 2 : 3;\n"
|
"n%100==4 ? 2 : 3;\n"
|
||||||
"X-Generator: Weblate 5.7\n"
|
"X-Generator: Weblate 5.8.2\n"
|
||||||
|
|
||||||
#: src/core/org.freedesktop.systemd1.policy.in:22
|
#: src/core/org.freedesktop.systemd1.policy.in:22
|
||||||
msgid "Send passphrase back to system"
|
msgid "Send passphrase back to system"
|
||||||
|
@ -125,16 +125,13 @@ msgstr ""
|
||||||
"območja."
|
"območja."
|
||||||
|
|
||||||
#: src/home/org.freedesktop.home1.policy:53
|
#: src/home/org.freedesktop.home1.policy:53
|
||||||
#, fuzzy
|
|
||||||
msgid "Update your home area"
|
msgid "Update your home area"
|
||||||
msgstr "Posodobite domače območje"
|
msgstr "Posodobite domače območje"
|
||||||
|
|
||||||
#: src/home/org.freedesktop.home1.policy:54
|
#: src/home/org.freedesktop.home1.policy:54
|
||||||
#, fuzzy
|
|
||||||
msgid "Authentication is required to update your home area."
|
msgid "Authentication is required to update your home area."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Preverjanje pristnosti je potrebno za posodobitev uporabnikovega domačega "
|
"Preverjanje pristnosti je potrebno za posodobitev vašega domačega območja."
|
||||||
"območja."
|
|
||||||
|
|
||||||
#: src/home/org.freedesktop.home1.policy:63
|
#: src/home/org.freedesktop.home1.policy:63
|
||||||
msgid "Resize a home area"
|
msgid "Resize a home area"
|
||||||
|
@ -1234,14 +1231,12 @@ msgstr ""
|
||||||
|
|
||||||
#: src/sysupdate/org.freedesktop.sysupdate1.policy:75
|
#: src/sysupdate/org.freedesktop.sysupdate1.policy:75
|
||||||
msgid "Manage optional features"
|
msgid "Manage optional features"
|
||||||
msgstr ""
|
msgstr "Upravljaj dodatne funkcionalnosti"
|
||||||
|
|
||||||
#: src/sysupdate/org.freedesktop.sysupdate1.policy:76
|
#: src/sysupdate/org.freedesktop.sysupdate1.policy:76
|
||||||
#, fuzzy
|
|
||||||
msgid "Authentication is required to manage optional features"
|
msgid "Authentication is required to manage optional features"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Preverjanje pristnosti je potrebno za upravljanje aktivnih sej, uporabnikov "
|
"Preverjanje pristnosti je potrebno za upravljanje dodatnih funkcionalnosti."
|
||||||
"in delovišč."
|
|
||||||
|
|
||||||
#: src/timedate/org.freedesktop.timedate1.policy:22
|
#: src/timedate/org.freedesktop.timedate1.policy:22
|
||||||
msgid "Set system time"
|
msgid "Set system time"
|
||||||
|
|
20
po/uk.po
20
po/uk.po
|
@ -4,12 +4,13 @@
|
||||||
# Eugene Melnik <jeka7js@gmail.com>, 2014.
|
# Eugene Melnik <jeka7js@gmail.com>, 2014.
|
||||||
# Daniel Korostil <ted.korostiled@gmail.com>, 2014, 2016, 2018.
|
# Daniel Korostil <ted.korostiled@gmail.com>, 2014, 2016, 2018.
|
||||||
# Yuri Chornoivan <yurchor@ukr.net>, 2019, 2020, 2021, 2022, 2023, 2024.
|
# Yuri Chornoivan <yurchor@ukr.net>, 2019, 2020, 2021, 2022, 2023, 2024.
|
||||||
|
# Dmytro Markevych <hotr1pak@gmail.com>, 2024.
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2024-11-06 14:42+0000\n"
|
"POT-Creation-Date: 2024-11-06 14:42+0000\n"
|
||||||
"PO-Revision-Date: 2024-08-24 10:36+0000\n"
|
"PO-Revision-Date: 2024-11-20 19:13+0000\n"
|
||||||
"Last-Translator: Yuri Chornoivan <yurchor@ukr.net>\n"
|
"Last-Translator: Dmytro Markevych <hotr1pak@gmail.com>\n"
|
||||||
"Language-Team: Ukrainian <https://translate.fedoraproject.org/projects/"
|
"Language-Team: Ukrainian <https://translate.fedoraproject.org/projects/"
|
||||||
"systemd/main/uk/>\n"
|
"systemd/main/uk/>\n"
|
||||||
"Language: uk\n"
|
"Language: uk\n"
|
||||||
|
@ -18,7 +19,7 @@ msgstr ""
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
|
"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
|
||||||
"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
|
"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
|
||||||
"X-Generator: Weblate 5.7\n"
|
"X-Generator: Weblate 5.8.2\n"
|
||||||
|
|
||||||
#: src/core/org.freedesktop.systemd1.policy.in:22
|
#: src/core/org.freedesktop.systemd1.policy.in:22
|
||||||
msgid "Send passphrase back to system"
|
msgid "Send passphrase back to system"
|
||||||
|
@ -118,14 +119,12 @@ msgid "Authentication is required to update a user's home area."
|
||||||
msgstr "Для оновлення домашньої теки користувача слід пройти розпізнавання."
|
msgstr "Для оновлення домашньої теки користувача слід пройти розпізнавання."
|
||||||
|
|
||||||
#: src/home/org.freedesktop.home1.policy:53
|
#: src/home/org.freedesktop.home1.policy:53
|
||||||
#, fuzzy
|
|
||||||
msgid "Update your home area"
|
msgid "Update your home area"
|
||||||
msgstr "Оновлення домашньої теки"
|
msgstr "Оновіть свій домашній простір"
|
||||||
|
|
||||||
#: src/home/org.freedesktop.home1.policy:54
|
#: src/home/org.freedesktop.home1.policy:54
|
||||||
#, fuzzy
|
|
||||||
msgid "Authentication is required to update your home area."
|
msgid "Authentication is required to update your home area."
|
||||||
msgstr "Для оновлення домашньої теки користувача слід пройти розпізнавання."
|
msgstr "Для оновлення домашньої області потрібна автентифікація."
|
||||||
|
|
||||||
#: src/home/org.freedesktop.home1.policy:63
|
#: src/home/org.freedesktop.home1.policy:63
|
||||||
msgid "Resize a home area"
|
msgid "Resize a home area"
|
||||||
|
@ -1212,14 +1211,11 @@ msgstr "Для вилучення застарілих оновлень сист
|
||||||
|
|
||||||
#: src/sysupdate/org.freedesktop.sysupdate1.policy:75
|
#: src/sysupdate/org.freedesktop.sysupdate1.policy:75
|
||||||
msgid "Manage optional features"
|
msgid "Manage optional features"
|
||||||
msgstr ""
|
msgstr "Керування додатковими функціями"
|
||||||
|
|
||||||
#: src/sysupdate/org.freedesktop.sysupdate1.policy:76
|
#: src/sysupdate/org.freedesktop.sysupdate1.policy:76
|
||||||
#, fuzzy
|
|
||||||
msgid "Authentication is required to manage optional features"
|
msgid "Authentication is required to manage optional features"
|
||||||
msgstr ""
|
msgstr "Для керування додатковими функціями потрібна автентифікація"
|
||||||
"Для того, щоб керувати сеансами, користувачами і робочими місцями, слід "
|
|
||||||
"пройти розпізнавання."
|
|
||||||
|
|
||||||
#: src/timedate/org.freedesktop.timedate1.policy:22
|
#: src/timedate/org.freedesktop.timedate1.policy:22
|
||||||
msgid "Set system time"
|
msgid "Set system time"
|
||||||
|
|
|
@ -803,6 +803,10 @@ int cg_pid_get_path(const char *controller, pid_t pid, char **ret_path) {
|
||||||
if (!path)
|
if (!path)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
/* Refuse cgroup paths from outside our cgroup namespace */
|
||||||
|
if (startswith(path, "/../"))
|
||||||
|
return -EUNATCH;
|
||||||
|
|
||||||
/* Truncate suffix indicating the process is a zombie */
|
/* Truncate suffix indicating the process is a zombie */
|
||||||
e = endswith(path, " (deleted)");
|
e = endswith(path, " (deleted)");
|
||||||
if (e)
|
if (e)
|
||||||
|
|
|
@ -102,8 +102,8 @@ int pid_get_comm(pid_t pid, char **ret) {
|
||||||
_cleanup_free_ char *escaped = NULL, *comm = NULL;
|
_cleanup_free_ char *escaped = NULL, *comm = NULL;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(ret);
|
|
||||||
assert(pid >= 0);
|
assert(pid >= 0);
|
||||||
|
assert(ret);
|
||||||
|
|
||||||
if (pid == 0 || pid == getpid_cached()) {
|
if (pid == 0 || pid == getpid_cached()) {
|
||||||
comm = new0(char, TASK_COMM_LEN + 1); /* Must fit in 16 byte according to prctl(2) */
|
comm = new0(char, TASK_COMM_LEN + 1); /* Must fit in 16 byte according to prctl(2) */
|
||||||
|
@ -143,6 +143,9 @@ int pidref_get_comm(const PidRef *pid, char **ret) {
|
||||||
if (!pidref_is_set(pid))
|
if (!pidref_is_set(pid))
|
||||||
return -ESRCH;
|
return -ESRCH;
|
||||||
|
|
||||||
|
if (pidref_is_remote(pid))
|
||||||
|
return -EREMOTE;
|
||||||
|
|
||||||
r = pid_get_comm(pid->pid, &comm);
|
r = pid_get_comm(pid->pid, &comm);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
@ -289,6 +292,9 @@ int pidref_get_cmdline(const PidRef *pid, size_t max_columns, ProcessCmdlineFlag
|
||||||
if (!pidref_is_set(pid))
|
if (!pidref_is_set(pid))
|
||||||
return -ESRCH;
|
return -ESRCH;
|
||||||
|
|
||||||
|
if (pidref_is_remote(pid))
|
||||||
|
return -EREMOTE;
|
||||||
|
|
||||||
r = pid_get_cmdline(pid->pid, max_columns, flags, &s);
|
r = pid_get_cmdline(pid->pid, max_columns, flags, &s);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
@ -331,6 +337,9 @@ int pidref_get_cmdline_strv(const PidRef *pid, ProcessCmdlineFlags flags, char *
|
||||||
if (!pidref_is_set(pid))
|
if (!pidref_is_set(pid))
|
||||||
return -ESRCH;
|
return -ESRCH;
|
||||||
|
|
||||||
|
if (pidref_is_remote(pid))
|
||||||
|
return -EREMOTE;
|
||||||
|
|
||||||
r = pid_get_cmdline_strv(pid->pid, flags, &args);
|
r = pid_get_cmdline_strv(pid->pid, flags, &args);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
@ -477,6 +486,9 @@ int pidref_is_kernel_thread(const PidRef *pid) {
|
||||||
if (!pidref_is_set(pid))
|
if (!pidref_is_set(pid))
|
||||||
return -ESRCH;
|
return -ESRCH;
|
||||||
|
|
||||||
|
if (pidref_is_remote(pid))
|
||||||
|
return -EREMOTE;
|
||||||
|
|
||||||
result = pid_is_kernel_thread(pid->pid);
|
result = pid_is_kernel_thread(pid->pid);
|
||||||
if (result < 0)
|
if (result < 0)
|
||||||
return result;
|
return result;
|
||||||
|
@ -594,6 +606,9 @@ int pidref_get_uid(const PidRef *pid, uid_t *ret) {
|
||||||
if (!pidref_is_set(pid))
|
if (!pidref_is_set(pid))
|
||||||
return -ESRCH;
|
return -ESRCH;
|
||||||
|
|
||||||
|
if (pidref_is_remote(pid))
|
||||||
|
return -EREMOTE;
|
||||||
|
|
||||||
r = pid_get_uid(pid->pid, &uid);
|
r = pid_get_uid(pid->pid, &uid);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
@ -794,6 +809,9 @@ int pidref_get_start_time(const PidRef *pid, usec_t *ret) {
|
||||||
if (!pidref_is_set(pid))
|
if (!pidref_is_set(pid))
|
||||||
return -ESRCH;
|
return -ESRCH;
|
||||||
|
|
||||||
|
if (pidref_is_remote(pid))
|
||||||
|
return -EREMOTE;
|
||||||
|
|
||||||
r = pid_get_start_time(pid->pid, ret ? &t : NULL);
|
r = pid_get_start_time(pid->pid, ret ? &t : NULL);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
@ -1093,6 +1111,9 @@ int pidref_is_my_child(const PidRef *pid) {
|
||||||
if (!pidref_is_set(pid))
|
if (!pidref_is_set(pid))
|
||||||
return -ESRCH;
|
return -ESRCH;
|
||||||
|
|
||||||
|
if (pidref_is_remote(pid))
|
||||||
|
return -EREMOTE;
|
||||||
|
|
||||||
result = pid_is_my_child(pid->pid);
|
result = pid_is_my_child(pid->pid);
|
||||||
if (result < 0)
|
if (result < 0)
|
||||||
return result;
|
return result;
|
||||||
|
@ -1128,6 +1149,9 @@ int pidref_is_unwaited(const PidRef *pid) {
|
||||||
if (!pidref_is_set(pid))
|
if (!pidref_is_set(pid))
|
||||||
return -ESRCH;
|
return -ESRCH;
|
||||||
|
|
||||||
|
if (pidref_is_remote(pid))
|
||||||
|
return -EREMOTE;
|
||||||
|
|
||||||
if (pid->pid == 1 || pidref_is_self(pid))
|
if (pid->pid == 1 || pidref_is_self(pid))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
@ -1169,6 +1193,9 @@ int pidref_is_alive(const PidRef *pidref) {
|
||||||
if (!pidref_is_set(pidref))
|
if (!pidref_is_set(pidref))
|
||||||
return -ESRCH;
|
return -ESRCH;
|
||||||
|
|
||||||
|
if (pidref_is_remote(pidref))
|
||||||
|
return -EREMOTE;
|
||||||
|
|
||||||
result = pid_is_alive(pidref->pid);
|
result = pid_is_alive(pidref->pid);
|
||||||
if (result < 0) {
|
if (result < 0) {
|
||||||
assert(result != -ESRCH);
|
assert(result != -ESRCH);
|
||||||
|
|
|
@ -220,9 +220,9 @@ static int synthesize_user_creds(
|
||||||
if (ret_gid)
|
if (ret_gid)
|
||||||
*ret_gid = GID_NOBODY;
|
*ret_gid = GID_NOBODY;
|
||||||
if (ret_home)
|
if (ret_home)
|
||||||
*ret_home = FLAGS_SET(flags, USER_CREDS_CLEAN) ? NULL : "/";
|
*ret_home = FLAGS_SET(flags, USER_CREDS_SUPPRESS_PLACEHOLDER) ? NULL : "/";
|
||||||
if (ret_shell)
|
if (ret_shell)
|
||||||
*ret_shell = FLAGS_SET(flags, USER_CREDS_CLEAN) ? NULL : NOLOGIN;
|
*ret_shell = FLAGS_SET(flags, USER_CREDS_SUPPRESS_PLACEHOLDER) ? NULL : NOLOGIN;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -244,6 +244,7 @@ int get_user_creds(
|
||||||
|
|
||||||
assert(username);
|
assert(username);
|
||||||
assert(*username);
|
assert(*username);
|
||||||
|
assert((ret_home || ret_shell) || !(flags & (USER_CREDS_SUPPRESS_PLACEHOLDER|USER_CREDS_CLEAN)));
|
||||||
|
|
||||||
if (!FLAGS_SET(flags, USER_CREDS_PREFER_NSS) ||
|
if (!FLAGS_SET(flags, USER_CREDS_PREFER_NSS) ||
|
||||||
(!ret_home && !ret_shell)) {
|
(!ret_home && !ret_shell)) {
|
||||||
|
@ -315,17 +316,14 @@ int get_user_creds(
|
||||||
|
|
||||||
if (ret_home)
|
if (ret_home)
|
||||||
/* Note: we don't insist on normalized paths, since there are setups that have /./ in the path */
|
/* Note: we don't insist on normalized paths, since there are setups that have /./ in the path */
|
||||||
*ret_home = (FLAGS_SET(flags, USER_CREDS_CLEAN) &&
|
*ret_home = (FLAGS_SET(flags, USER_CREDS_SUPPRESS_PLACEHOLDER) && empty_or_root(p->pw_dir)) ||
|
||||||
(empty_or_root(p->pw_dir) ||
|
(FLAGS_SET(flags, USER_CREDS_CLEAN) && (!path_is_valid(p->pw_dir) || !path_is_absolute(p->pw_dir)))
|
||||||
!path_is_valid(p->pw_dir) ||
|
? NULL : p->pw_dir;
|
||||||
!path_is_absolute(p->pw_dir))) ? NULL : p->pw_dir;
|
|
||||||
|
|
||||||
if (ret_shell)
|
if (ret_shell)
|
||||||
*ret_shell = (FLAGS_SET(flags, USER_CREDS_CLEAN) &&
|
*ret_shell = (FLAGS_SET(flags, USER_CREDS_SUPPRESS_PLACEHOLDER) && shell_is_placeholder(p->pw_shell)) ||
|
||||||
(isempty(p->pw_shell) ||
|
(FLAGS_SET(flags, USER_CREDS_CLEAN) && (!path_is_valid(p->pw_shell) || !path_is_absolute(p->pw_shell)))
|
||||||
!path_is_valid(p->pw_shell) ||
|
? NULL : p->pw_shell;
|
||||||
!path_is_absolute(p->pw_shell) ||
|
|
||||||
is_nologin_shell(p->pw_shell))) ? NULL : p->pw_shell;
|
|
||||||
|
|
||||||
if (patch_username)
|
if (patch_username)
|
||||||
*username = p->pw_name;
|
*username = p->pw_name;
|
||||||
|
|
|
@ -12,6 +12,8 @@
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "string-util.h"
|
||||||
|
|
||||||
/* Users managed by systemd-homed. See https://systemd.io/UIDS-GIDS for details how this range fits into the rest of the world */
|
/* Users managed by systemd-homed. See https://systemd.io/UIDS-GIDS for details how this range fits into the rest of the world */
|
||||||
#define HOME_UID_MIN ((uid_t) 60001)
|
#define HOME_UID_MIN ((uid_t) 60001)
|
||||||
#define HOME_UID_MAX ((uid_t) 60513)
|
#define HOME_UID_MAX ((uid_t) 60513)
|
||||||
|
@ -36,10 +38,20 @@ static inline int parse_gid(const char *s, gid_t *ret_gid) {
|
||||||
char* getlogname_malloc(void);
|
char* getlogname_malloc(void);
|
||||||
char* getusername_malloc(void);
|
char* getusername_malloc(void);
|
||||||
|
|
||||||
|
const char* default_root_shell_at(int rfd);
|
||||||
|
const char* default_root_shell(const char *root);
|
||||||
|
|
||||||
|
bool is_nologin_shell(const char *shell);
|
||||||
|
|
||||||
|
static inline bool shell_is_placeholder(const char *shell) {
|
||||||
|
return isempty(shell) || is_nologin_shell(shell);
|
||||||
|
}
|
||||||
|
|
||||||
typedef enum UserCredsFlags {
|
typedef enum UserCredsFlags {
|
||||||
USER_CREDS_PREFER_NSS = 1 << 0, /* if set, only synthesize user records if database lacks them. Normally we bypass the userdb entirely for the records we can synthesize */
|
USER_CREDS_PREFER_NSS = 1 << 0, /* if set, only synthesize user records if database lacks them. Normally we bypass the userdb entirely for the records we can synthesize */
|
||||||
USER_CREDS_ALLOW_MISSING = 1 << 1, /* if a numeric UID string is resolved, be OK if there's no record for it */
|
USER_CREDS_ALLOW_MISSING = 1 << 1, /* if a numeric UID string is resolved, be OK if there's no record for it */
|
||||||
USER_CREDS_CLEAN = 1 << 2, /* try to clean up shell and home fields with invalid data */
|
USER_CREDS_CLEAN = 1 << 2, /* try to clean up shell and home fields with invalid data */
|
||||||
|
USER_CREDS_SUPPRESS_PLACEHOLDER = 1 << 3, /* suppress home and/or shell fields if value is placeholder (root/empty/nologin) */
|
||||||
} UserCredsFlags;
|
} UserCredsFlags;
|
||||||
|
|
||||||
int get_user_creds(const char **username, uid_t *ret_uid, gid_t *ret_gid, const char **ret_home, const char **ret_shell, UserCredsFlags flags);
|
int get_user_creds(const char **username, uid_t *ret_uid, gid_t *ret_gid, const char **ret_home, const char **ret_shell, UserCredsFlags flags);
|
||||||
|
@ -125,10 +137,6 @@ int fgetsgent_sane(FILE *stream, struct sgrp **sg);
|
||||||
int putsgent_sane(const struct sgrp *sg, FILE *stream);
|
int putsgent_sane(const struct sgrp *sg, FILE *stream);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool is_nologin_shell(const char *shell);
|
|
||||||
const char* default_root_shell_at(int rfd);
|
|
||||||
const char* default_root_shell(const char *root);
|
|
||||||
|
|
||||||
int is_this_me(const char *username);
|
int is_this_me(const char *username);
|
||||||
|
|
||||||
const char* get_home_root(void);
|
const char* get_home_root(void);
|
||||||
|
|
|
@ -49,6 +49,7 @@ const sd_bus_vtable bus_path_vtable[] = {
|
||||||
SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Path, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Path, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||||||
SD_BUS_PROPERTY("TriggerLimitIntervalUSec", "t", bus_property_get_usec, offsetof(Path, trigger_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST),
|
SD_BUS_PROPERTY("TriggerLimitIntervalUSec", "t", bus_property_get_usec, offsetof(Path, trigger_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||||
SD_BUS_PROPERTY("TriggerLimitBurst", "u", bus_property_get_unsigned, offsetof(Path, trigger_limit.burst), SD_BUS_VTABLE_PROPERTY_CONST),
|
SD_BUS_PROPERTY("TriggerLimitBurst", "u", bus_property_get_unsigned, offsetof(Path, trigger_limit.burst), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||||
|
SD_BUS_PROPERTY("DeactivationToggle", "b", bus_property_get_bool, offsetof(Path, deactivation_toggle), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||||
SD_BUS_VTABLE_END
|
SD_BUS_VTABLE_END
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -141,6 +142,9 @@ static int bus_path_set_transient_property(
|
||||||
if (streq(name, "TriggerLimitIntervalUSec"))
|
if (streq(name, "TriggerLimitIntervalUSec"))
|
||||||
return bus_set_transient_usec(u, name, &p->trigger_limit.interval, message, flags, error);
|
return bus_set_transient_usec(u, name, &p->trigger_limit.interval, message, flags, error);
|
||||||
|
|
||||||
|
if (streq(name, "DeactivationToggle"))
|
||||||
|
return bus_set_transient_bool(u, name, &p->deactivation_toggle, message, flags, error);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -855,9 +855,6 @@ static int get_fixed_user(
|
||||||
assert(user_or_uid);
|
assert(user_or_uid);
|
||||||
assert(ret_username);
|
assert(ret_username);
|
||||||
|
|
||||||
/* Note that we don't set $HOME or $SHELL if they are not particularly enlightening anyway
|
|
||||||
* (i.e. are "/" or "/bin/nologin"). */
|
|
||||||
|
|
||||||
r = get_user_creds(&user_or_uid, ret_uid, ret_gid, ret_home, ret_shell, USER_CREDS_CLEAN);
|
r = get_user_creds(&user_or_uid, ret_uid, ret_gid, ret_home, ret_shell, USER_CREDS_CLEAN);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
@ -1883,7 +1880,10 @@ static int build_environment(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (home && set_user_login_env) {
|
/* Note that we don't set $HOME or $SHELL if they are not particularly enlightening anyway
|
||||||
|
* (i.e. are "/" or "/bin/nologin"). */
|
||||||
|
|
||||||
|
if (home && set_user_login_env && !empty_or_root(home)) {
|
||||||
x = strjoin("HOME=", home);
|
x = strjoin("HOME=", home);
|
||||||
if (!x)
|
if (!x)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
@ -1892,7 +1892,7 @@ static int build_environment(
|
||||||
our_env[n_env++] = x;
|
our_env[n_env++] = x;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (shell && set_user_login_env) {
|
if (shell && set_user_login_env && !shell_is_placeholder(shell)) {
|
||||||
x = strjoin("SHELL=", shell);
|
x = strjoin("SHELL=", shell);
|
||||||
if (!x)
|
if (!x)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
@ -3471,20 +3471,16 @@ static int apply_working_directory(
|
||||||
const ExecContext *context,
|
const ExecContext *context,
|
||||||
const ExecParameters *params,
|
const ExecParameters *params,
|
||||||
ExecRuntime *runtime,
|
ExecRuntime *runtime,
|
||||||
const char *home,
|
const char *home) {
|
||||||
int *exit_status) {
|
|
||||||
|
|
||||||
const char *wd;
|
const char *wd;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(context);
|
assert(context);
|
||||||
assert(exit_status);
|
|
||||||
|
|
||||||
if (context->working_directory_home) {
|
if (context->working_directory_home) {
|
||||||
if (!home) {
|
if (!home)
|
||||||
*exit_status = EXIT_CHDIR;
|
|
||||||
return -ENXIO;
|
return -ENXIO;
|
||||||
}
|
|
||||||
|
|
||||||
wd = home;
|
wd = home;
|
||||||
} else
|
} else
|
||||||
|
@ -3503,13 +3499,7 @@ static int apply_working_directory(
|
||||||
if (r >= 0)
|
if (r >= 0)
|
||||||
r = RET_NERRNO(fchdir(dfd));
|
r = RET_NERRNO(fchdir(dfd));
|
||||||
}
|
}
|
||||||
|
return context->working_directory_missing_ok ? 0 : r;
|
||||||
if (r < 0 && !context->working_directory_missing_ok) {
|
|
||||||
*exit_status = EXIT_CHDIR;
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int apply_root_directory(
|
static int apply_root_directory(
|
||||||
|
@ -3785,7 +3775,7 @@ static int acquire_home(const ExecContext *c, const char **home, char **ret_buf)
|
||||||
if (!c->working_directory_home)
|
if (!c->working_directory_home)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (c->dynamic_user)
|
if (c->dynamic_user || (c->user && is_this_me(c->user) <= 0))
|
||||||
return -EADDRNOTAVAIL;
|
return -EADDRNOTAVAIL;
|
||||||
|
|
||||||
r = get_home_dir(ret_buf);
|
r = get_home_dir(ret_buf);
|
||||||
|
@ -4543,7 +4533,7 @@ int exec_invoke(
|
||||||
r = acquire_home(context, &home, &home_buffer);
|
r = acquire_home(context, &home, &home_buffer);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
*exit_status = EXIT_CHDIR;
|
*exit_status = EXIT_CHDIR;
|
||||||
return log_exec_error_errno(context, params, r, "Failed to determine $HOME for user: %m");
|
return log_exec_error_errno(context, params, r, "Failed to determine $HOME for the invoking user: %m");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If a socket is connected to STDIN/STDOUT/STDERR, we must drop O_NONBLOCK */
|
/* If a socket is connected to STDIN/STDOUT/STDERR, we must drop O_NONBLOCK */
|
||||||
|
@ -5382,9 +5372,11 @@ int exec_invoke(
|
||||||
* running this service might have the correct privilege to change to the working directory. Also, it
|
* running this service might have the correct privilege to change to the working directory. Also, it
|
||||||
* is absolutely 💣 crucial 💣 we applied all mount namespacing rearrangements before this, so that
|
* is absolutely 💣 crucial 💣 we applied all mount namespacing rearrangements before this, so that
|
||||||
* the cwd cannot be used to pin directories outside of the sandbox. */
|
* the cwd cannot be used to pin directories outside of the sandbox. */
|
||||||
r = apply_working_directory(context, params, runtime, home, exit_status);
|
r = apply_working_directory(context, params, runtime, home);
|
||||||
if (r < 0)
|
if (r < 0) {
|
||||||
|
*exit_status = EXIT_CHDIR;
|
||||||
return log_exec_error_errno(context, params, r, "Changing to the requested working directory failed: %m");
|
return log_exec_error_errno(context, params, r, "Changing to the requested working directory failed: %m");
|
||||||
|
}
|
||||||
|
|
||||||
if (needs_sandboxing) {
|
if (needs_sandboxing) {
|
||||||
/* Apply other MAC contexts late, but before seccomp syscall filtering, as those should really be last to
|
/* Apply other MAC contexts late, but before seccomp syscall filtering, as those should really be last to
|
||||||
|
|
|
@ -586,6 +586,7 @@ Path.MakeDirectory, config_parse_bool,
|
||||||
Path.DirectoryMode, config_parse_mode, 0, offsetof(Path, directory_mode)
|
Path.DirectoryMode, config_parse_mode, 0, offsetof(Path, directory_mode)
|
||||||
Path.TriggerLimitIntervalSec, config_parse_sec, 0, offsetof(Path, trigger_limit.interval)
|
Path.TriggerLimitIntervalSec, config_parse_sec, 0, offsetof(Path, trigger_limit.interval)
|
||||||
Path.TriggerLimitBurst, config_parse_unsigned, 0, offsetof(Path, trigger_limit.burst)
|
Path.TriggerLimitBurst, config_parse_unsigned, 0, offsetof(Path, trigger_limit.burst)
|
||||||
|
Path.DeactivationToggle, config_parse_bool, 0, offsetof(Path, deactivation_toggle)
|
||||||
{{ CGROUP_CONTEXT_CONFIG_ITEMS('Slice') }}
|
{{ CGROUP_CONTEXT_CONFIG_ITEMS('Slice') }}
|
||||||
{{ CGROUP_CONTEXT_CONFIG_ITEMS('Scope') }}
|
{{ CGROUP_CONTEXT_CONFIG_ITEMS('Scope') }}
|
||||||
{{ KILL_CONTEXT_CONFIG_ITEMS('Scope') }}
|
{{ KILL_CONTEXT_CONFIG_ITEMS('Scope') }}
|
||||||
|
|
|
@ -47,11 +47,14 @@ int path_spec_watch(PathSpec *s, sd_event_io_handler_t handler) {
|
||||||
bool exists = false;
|
bool exists = false;
|
||||||
char *slash, *oldslash = NULL;
|
char *slash, *oldslash = NULL;
|
||||||
int r;
|
int r;
|
||||||
|
Path *p;
|
||||||
|
|
||||||
assert(s);
|
assert(s);
|
||||||
assert(s->unit);
|
assert(s->unit);
|
||||||
assert(handler);
|
assert(handler);
|
||||||
|
|
||||||
|
p = PATH(s->unit);
|
||||||
|
|
||||||
path_spec_unwatch(s);
|
path_spec_unwatch(s);
|
||||||
|
|
||||||
s->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
|
s->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
|
||||||
|
@ -82,9 +85,11 @@ int path_spec_watch(PathSpec *s, sd_event_io_handler_t handler) {
|
||||||
*cut = '\0';
|
*cut = '\0';
|
||||||
|
|
||||||
flags = IN_MOVE_SELF | IN_DELETE_SELF | IN_CREATE | IN_MOVED_TO;
|
flags = IN_MOVE_SELF | IN_DELETE_SELF | IN_CREATE | IN_MOVED_TO;
|
||||||
|
SET_FLAG(flags, IN_DELETE | IN_MOVED_FROM, p && p->deactivation_toggle);
|
||||||
} else {
|
} else {
|
||||||
cut = NULL;
|
cut = NULL;
|
||||||
flags = flags_table[s->type];
|
flags = flags_table[s->type];
|
||||||
|
SET_FLAG(flags, IN_DELETE | IN_MOVED_FROM, p && p->deactivation_toggle && s->type == PATH_DIRECTORY_NOT_EMPTY);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If this is a symlink watch both the symlink inode and where it points to. If the inode is
|
/* If this is a symlink watch both the symlink inode and where it points to. If the inode is
|
||||||
|
@ -134,7 +139,10 @@ int path_spec_watch(PathSpec *s, sd_event_io_handler_t handler) {
|
||||||
char tmp2 = *cut2;
|
char tmp2 = *cut2;
|
||||||
*cut2 = '\0';
|
*cut2 = '\0';
|
||||||
|
|
||||||
(void) inotify_add_watch(s->inotify_fd, s->path, IN_MOVE_SELF);
|
flags = IN_MOVE_SELF;
|
||||||
|
SET_FLAG(flags, IN_CREATE | IN_DELETE | IN_MOVED_TO | IN_MOVED_FROM, p && p->deactivation_toggle);
|
||||||
|
|
||||||
|
(void) inotify_add_watch(s->inotify_fd, s->path, flags);
|
||||||
/* Error is ignored, the worst can happen is we get spurious events. */
|
/* Error is ignored, the worst can happen is we get spurious events. */
|
||||||
|
|
||||||
*cut2 = tmp2;
|
*cut2 = tmp2;
|
||||||
|
@ -427,14 +435,16 @@ static void path_dump(Unit *u, FILE *f, const char *prefix) {
|
||||||
"%sMakeDirectory: %s\n"
|
"%sMakeDirectory: %s\n"
|
||||||
"%sDirectoryMode: %04o\n"
|
"%sDirectoryMode: %04o\n"
|
||||||
"%sTriggerLimitIntervalSec: %s\n"
|
"%sTriggerLimitIntervalSec: %s\n"
|
||||||
"%sTriggerLimitBurst: %u\n",
|
"%sTriggerLimitBurst: %u\n"
|
||||||
|
"%sDeactivationToggle: %s\n",
|
||||||
prefix, path_state_to_string(p->state),
|
prefix, path_state_to_string(p->state),
|
||||||
prefix, path_result_to_string(p->result),
|
prefix, path_result_to_string(p->result),
|
||||||
prefix, trigger ? trigger->id : "n/a",
|
prefix, trigger ? trigger->id : "n/a",
|
||||||
prefix, yes_no(p->make_directory),
|
prefix, yes_no(p->make_directory),
|
||||||
prefix, p->directory_mode,
|
prefix, p->directory_mode,
|
||||||
prefix, FORMAT_TIMESPAN(p->trigger_limit.interval, USEC_PER_SEC),
|
prefix, FORMAT_TIMESPAN(p->trigger_limit.interval, USEC_PER_SEC),
|
||||||
prefix, p->trigger_limit.burst);
|
prefix, p->trigger_limit.burst,
|
||||||
|
prefix, yes_no(p->deactivation_toggle));
|
||||||
|
|
||||||
LIST_FOREACH(spec, s, p->specs)
|
LIST_FOREACH(spec, s, p->specs)
|
||||||
path_spec_dump(s, f, prefix);
|
path_spec_dump(s, f, prefix);
|
||||||
|
@ -555,7 +565,37 @@ static void path_enter_running(Path *p, char *trigger_path) {
|
||||||
job_set_activation_details(job, details);
|
job_set_activation_details(job, details);
|
||||||
|
|
||||||
path_set_state(p, PATH_RUNNING);
|
path_set_state(p, PATH_RUNNING);
|
||||||
path_unwatch(p);
|
|
||||||
|
if (!p->deactivation_toggle)
|
||||||
|
path_unwatch(p);
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
path_enter_dead(p, PATH_FAILURE_RESOURCES);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void path_deactivate_unit(Path *p) {
|
||||||
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||||
|
Unit *trigger;
|
||||||
|
Job *job;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(p);
|
||||||
|
|
||||||
|
trigger = UNIT_TRIGGER(UNIT(p));
|
||||||
|
if (!trigger) {
|
||||||
|
log_unit_error(UNIT(p), "Unit to trigger vanished.");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = manager_add_job(UNIT(p)->manager, JOB_STOP, trigger, JOB_REPLACE, NULL, &error, &job);
|
||||||
|
if (r < 0) {
|
||||||
|
log_unit_warning(UNIT(p), "Failed to queue unit stop job: %s", bus_error_message(&error, r));
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
path_set_state(p, PATH_WAITING);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -578,19 +618,30 @@ static void path_enter_waiting(Path *p, bool initial, bool from_trigger_notify)
|
||||||
_cleanup_free_ char *trigger_path = NULL;
|
_cleanup_free_ char *trigger_path = NULL;
|
||||||
Unit *trigger;
|
Unit *trigger;
|
||||||
int r;
|
int r;
|
||||||
|
bool watch_not_needed = !p->deactivation_toggle || p->state == PATH_RUNNING;
|
||||||
|
bool trigger_unit_is_running;
|
||||||
|
|
||||||
if (p->trigger_notify_event_source)
|
if (p->trigger_notify_event_source)
|
||||||
(void) event_source_disable(p->trigger_notify_event_source);
|
(void) event_source_disable(p->trigger_notify_event_source);
|
||||||
|
|
||||||
/* If the triggered unit is already running, so are we */
|
/* If the triggered unit is already running, so are we */
|
||||||
trigger = UNIT_TRIGGER(UNIT(p));
|
trigger = UNIT_TRIGGER(UNIT(p));
|
||||||
if (trigger && !UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(trigger))) {
|
trigger_unit_is_running = trigger && !UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(trigger));
|
||||||
|
if (watch_not_needed && trigger_unit_is_running) {
|
||||||
|
if (p->deactivation_toggle && p->state == PATH_RUNNING) {
|
||||||
|
if (!path_check_good(p, true, from_trigger_notify, &trigger_path))
|
||||||
|
/* If the path is removed, stop the unit */
|
||||||
|
path_deactivate_unit(p);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
path_set_state(p, PATH_RUNNING);
|
path_set_state(p, PATH_RUNNING);
|
||||||
path_unwatch(p);
|
path_unwatch(p);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (path_check_good(p, initial, from_trigger_notify, &trigger_path)) {
|
if (watch_not_needed && path_check_good(p, initial, from_trigger_notify, &trigger_path)) {
|
||||||
log_unit_debug(UNIT(p), "Got triggered.");
|
log_unit_debug(UNIT(p), "Got triggered.");
|
||||||
path_enter_running(p, trigger_path);
|
path_enter_running(p, trigger_path);
|
||||||
return;
|
return;
|
||||||
|
@ -608,9 +659,18 @@ static void path_enter_waiting(Path *p, bool initial, bool from_trigger_notify)
|
||||||
* recheck */
|
* recheck */
|
||||||
|
|
||||||
if (path_check_good(p, false, from_trigger_notify, &trigger_path)) {
|
if (path_check_good(p, false, from_trigger_notify, &trigger_path)) {
|
||||||
|
if (p->deactivation_toggle && trigger_unit_is_running) {
|
||||||
|
path_set_state(p, PATH_RUNNING);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
log_unit_debug(UNIT(p), "Got triggered.");
|
log_unit_debug(UNIT(p), "Got triggered.");
|
||||||
path_enter_running(p, trigger_path);
|
path_enter_running(p, trigger_path);
|
||||||
return;
|
return;
|
||||||
|
} else if (p->deactivation_toggle && trigger_unit_is_running) {
|
||||||
|
/* If the path is removed, stop the unit */
|
||||||
|
path_deactivate_unit(p);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
path_set_state(p, PATH_WAITING);
|
path_set_state(p, PATH_WAITING);
|
||||||
|
|
|
@ -67,6 +67,8 @@ struct Path {
|
||||||
RateLimit trigger_limit;
|
RateLimit trigger_limit;
|
||||||
|
|
||||||
sd_event_source *trigger_notify_event_source;
|
sd_event_source *trigger_notify_event_source;
|
||||||
|
|
||||||
|
bool deactivation_toggle;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ActivationDetailsPath {
|
struct ActivationDetailsPath {
|
||||||
|
|
|
@ -193,7 +193,7 @@ int enroll_fido2(
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
|
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"\nPlease save this FIDO2 credential ID. It is required when unloocking the volume\n"
|
"\nPlease save this FIDO2 credential ID. It is required when unlocking the volume\n"
|
||||||
"using the associated FIDO2 keyslot which we just created. To configure automatic\n"
|
"using the associated FIDO2 keyslot which we just created. To configure automatic\n"
|
||||||
"unlocking using this FIDO2 token, add an appropriate entry to your /etc/crypttab\n"
|
"unlocking using this FIDO2 token, add an appropriate entry to your /etc/crypttab\n"
|
||||||
"file, see %s for details.\n", link);
|
"file, see %s for details.\n", link);
|
||||||
|
|
|
@ -1443,6 +1443,7 @@ int link_reconfigure_impl(Link *link, LinkReconfigurationFlag flags) {
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct LinkReconfigurationData {
|
typedef struct LinkReconfigurationData {
|
||||||
|
Manager *manager;
|
||||||
Link *link;
|
Link *link;
|
||||||
LinkReconfigurationFlag flags;
|
LinkReconfigurationFlag flags;
|
||||||
sd_bus_message *message;
|
sd_bus_message *message;
|
||||||
|
@ -1473,6 +1474,12 @@ static void link_reconfiguration_data_destroy_callback(LinkReconfigurationData *
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!data->counter || *data->counter <= 0) {
|
if (!data->counter || *data->counter <= 0) {
|
||||||
|
/* Update the state files before replying the bus method. Otherwise,
|
||||||
|
* systemd-networkd-wait-online following networkctl reload/reconfigure may read an
|
||||||
|
* outdated state file and wrongly handle an interface is already in the configured
|
||||||
|
* state. */
|
||||||
|
(void) manager_clean_all(data->manager);
|
||||||
|
|
||||||
r = sd_bus_reply_method_return(data->message, NULL);
|
r = sd_bus_reply_method_return(data->message, NULL);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
log_warning_errno(r, "Failed to reply for DBus method, ignoring: %m");
|
log_warning_errno(r, "Failed to reply for DBus method, ignoring: %m");
|
||||||
|
@ -1521,6 +1528,7 @@ int link_reconfigure_full(Link *link, LinkReconfigurationFlag flags, sd_bus_mess
|
||||||
}
|
}
|
||||||
|
|
||||||
*data = (LinkReconfigurationData) {
|
*data = (LinkReconfigurationData) {
|
||||||
|
.manager = link->manager,
|
||||||
.link = link_ref(link),
|
.link = link_ref(link),
|
||||||
.flags = flags,
|
.flags = flags,
|
||||||
.message = sd_bus_message_ref(message), /* message may be NULL, but _ref() works fine. */
|
.message = sd_bus_message_ref(message), /* message may be NULL, but _ref() works fine. */
|
||||||
|
|
|
@ -2297,7 +2297,8 @@ static int start_transient_scope(sd_bus *bus) {
|
||||||
uid_t uid;
|
uid_t uid;
|
||||||
gid_t gid;
|
gid_t gid;
|
||||||
|
|
||||||
r = get_user_creds(&arg_exec_user, &uid, &gid, &home, &shell, USER_CREDS_CLEAN|USER_CREDS_PREFER_NSS);
|
r = get_user_creds(&arg_exec_user, &uid, &gid, &home, &shell,
|
||||||
|
USER_CREDS_CLEAN|USER_CREDS_SUPPRESS_PLACEHOLDER|USER_CREDS_PREFER_NSS);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to resolve user %s: %m", arg_exec_user);
|
return log_error_errno(r, "Failed to resolve user %s: %m", arg_exec_user);
|
||||||
|
|
||||||
|
|
|
@ -2315,7 +2315,7 @@ static int bus_append_mount_property(sd_bus_message *m, const char *field, const
|
||||||
static int bus_append_path_property(sd_bus_message *m, const char *field, const char *eq) {
|
static int bus_append_path_property(sd_bus_message *m, const char *field, const char *eq) {
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
if (streq(field, "MakeDirectory"))
|
if (STR_IN_SET(field, "MakeDirectory", "DeactivationToggle"))
|
||||||
return bus_append_parse_boolean(m, field, eq);
|
return bus_append_parse_boolean(m, field, eq);
|
||||||
|
|
||||||
if (streq(field, "DirectoryMode"))
|
if (streq(field, "DirectoryMode"))
|
||||||
|
|
|
@ -46,13 +46,17 @@ static bool argv_has_at(pid_t pid) {
|
||||||
return c == '@';
|
return c == '@';
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool is_survivor_cgroup(const PidRef *pid) {
|
static bool is_in_survivor_cgroup(const PidRef *pid) {
|
||||||
_cleanup_free_ char *cgroup_path = NULL;
|
_cleanup_free_ char *cgroup_path = NULL;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(pidref_is_set(pid));
|
assert(pidref_is_set(pid));
|
||||||
|
|
||||||
r = cg_pidref_get_path(/* root= */ NULL, pid, &cgroup_path);
|
r = cg_pidref_get_path(/* root= */ NULL, pid, &cgroup_path);
|
||||||
|
if (r == -EUNATCH) {
|
||||||
|
log_warning_errno(r, "Process " PID_FMT " appears to originate in foreign namespace, ignoring.", pid->pid);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
log_warning_errno(r, "Failed to get cgroup path of process " PID_FMT ", ignoring: %m", pid->pid);
|
log_warning_errno(r, "Failed to get cgroup path of process " PID_FMT ", ignoring: %m", pid->pid);
|
||||||
return false;
|
return false;
|
||||||
|
@ -86,7 +90,7 @@ static bool ignore_proc(const PidRef *pid, bool warn_rootfs) {
|
||||||
return true; /* also ignore processes where we can't determine this */
|
return true; /* also ignore processes where we can't determine this */
|
||||||
|
|
||||||
/* Ignore processes that are part of a cgroup marked with the user.survive_final_kill_signal xattr */
|
/* Ignore processes that are part of a cgroup marked with the user.survive_final_kill_signal xattr */
|
||||||
if (is_survivor_cgroup(pid))
|
if (is_in_survivor_cgroup(pid))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
r = pidref_get_uid(pid, &uid);
|
r = pidref_get_uid(pid, &uid);
|
||||||
|
|
|
@ -7,24 +7,26 @@ TEST(audit_loginuid_from_pid) {
|
||||||
_cleanup_(pidref_done) PidRef self = PIDREF_NULL, pid1 = PIDREF_NULL;
|
_cleanup_(pidref_done) PidRef self = PIDREF_NULL, pid1 = PIDREF_NULL;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert_se(pidref_set_self(&self) >= 0);
|
ASSERT_OK(pidref_set_self(&self));
|
||||||
assert_se(pidref_set_pid(&pid1, 1) >= 0);
|
ASSERT_OK(pidref_set_pid(&pid1, 1));
|
||||||
|
|
||||||
uid_t uid;
|
uid_t uid;
|
||||||
r = audit_loginuid_from_pid(&self, &uid);
|
r = audit_loginuid_from_pid(&self, &uid);
|
||||||
assert_se(r >= 0 || r == -ENODATA);
|
if (r != -ENODATA)
|
||||||
|
ASSERT_OK(r);
|
||||||
if (r >= 0)
|
if (r >= 0)
|
||||||
log_info("self audit login uid: " UID_FMT, uid);
|
log_info("self audit login uid: " UID_FMT, uid);
|
||||||
|
|
||||||
assert_se(audit_loginuid_from_pid(&pid1, &uid) == -ENODATA);
|
ASSERT_ERROR(audit_loginuid_from_pid(&pid1, &uid), ENODATA);
|
||||||
|
|
||||||
uint32_t sessionid;
|
uint32_t sessionid;
|
||||||
r = audit_session_from_pid(&self, &sessionid);
|
r = audit_session_from_pid(&self, &sessionid);
|
||||||
assert_se(r >= 0 || r == -ENODATA);
|
if (r != -ENODATA)
|
||||||
|
ASSERT_OK(r);
|
||||||
if (r >= 0)
|
if (r >= 0)
|
||||||
log_info("self audit session id: %" PRIu32, sessionid);
|
log_info("self audit session id: %" PRIu32, sessionid);
|
||||||
|
|
||||||
assert_se(audit_session_from_pid(&pid1, &sessionid) == -ENODATA);
|
ASSERT_ERROR(audit_session_from_pid(&pid1, &sessionid), ENODATA);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int intro(void) {
|
static int intro(void) {
|
||||||
|
|
|
@ -22,7 +22,8 @@ typedef void (*test_function_t)(Manager *m);
|
||||||
|
|
||||||
static int setup_test(Manager **m) {
|
static int setup_test(Manager **m) {
|
||||||
char **tests_path = STRV_MAKE("exists", "existsglobFOOBAR", "changed", "modified", "unit",
|
char **tests_path = STRV_MAKE("exists", "existsglobFOOBAR", "changed", "modified", "unit",
|
||||||
"directorynotempty", "makedirectory");
|
"directorynotempty", "makedirectory", "deactivationtoggle",
|
||||||
|
"deactivationtoggle_secondary");
|
||||||
Manager *tmp = NULL;
|
Manager *tmp = NULL;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
|
@ -378,6 +379,371 @@ static void test_path_makedirectory_directorymode(Manager *m) {
|
||||||
(void) rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL);
|
(void) rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_path_deactivationtoggle_exists(Manager *m) {
|
||||||
|
const char *test_file, *test_file2;
|
||||||
|
const char *test_path = "/tmp/test-path_deactivationtoggle/";
|
||||||
|
const char *test_path2 = "/tmp/test-path_deactivationtoggle_secondary/";
|
||||||
|
const char *path_name = "path-deactivationtoggle-exists.path";
|
||||||
|
const char *service_name = "path-deactivationtoggle.service";
|
||||||
|
Unit *unit = NULL;
|
||||||
|
Path *path = NULL;
|
||||||
|
Service *service = NULL;
|
||||||
|
|
||||||
|
assert_se(m);
|
||||||
|
|
||||||
|
assert_se(manager_load_startable_unit_or_warn(m, path_name, NULL, &unit) >= 0);
|
||||||
|
|
||||||
|
path = PATH(unit);
|
||||||
|
service = service_for_path(m, path, service_name);
|
||||||
|
|
||||||
|
assert_se(unit_start(unit, NULL) >= 0);
|
||||||
|
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
test_file = strjoina(test_path, "test_file");
|
||||||
|
test_file2 = strjoina(test_path2, "test_file");
|
||||||
|
|
||||||
|
assert_se(mkdir_p(test_path, 0755) >= 0);
|
||||||
|
assert_se(mkdir_p(test_path2, 0755) >= 0);
|
||||||
|
assert_se(touch(test_file) >= 0);
|
||||||
|
if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
assert_se(rm_rf(test_file, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
|
||||||
|
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
assert_se(touch(test_file2) >= 0);
|
||||||
|
assert_se(rename(test_file2, test_file) >= 0);
|
||||||
|
if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
assert_se(rename(test_file, test_file2) >= 0);
|
||||||
|
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
assert_se(unit_stop(unit) >= 0);
|
||||||
|
assert_se(rm_rf(test_file2, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
|
||||||
|
|
||||||
|
assert_se(touch(test_file) >= 0);
|
||||||
|
assert_se(unit_start(unit, NULL) >= 0);
|
||||||
|
if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* If file exists before path is started, service is stopped when file is removed */
|
||||||
|
assert_se(rm_rf(test_file, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
|
||||||
|
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
assert_se(touch(test_file) >= 0);
|
||||||
|
if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
assert_se(rm_rf(test_file, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
|
||||||
|
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
assert_se(unit_stop(unit) >= 0);
|
||||||
|
|
||||||
|
assert_se(touch(test_file) >= 0);
|
||||||
|
assert_se(unit_start(unit, NULL) >= 0);
|
||||||
|
if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Service is stopped when file is renamed */
|
||||||
|
assert_se(rename(test_file, test_file2) >= 0);
|
||||||
|
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
assert_se(rename(test_file2, test_file) >= 0);
|
||||||
|
if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
assert_se(rename(test_file, test_file2) >= 0);
|
||||||
|
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
assert_se(unit_stop(unit) >= 0);
|
||||||
|
assert_se(rm_rf(test_file2, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
|
||||||
|
|
||||||
|
assert_se(unit_start(UNIT(service), NULL) >= 0);
|
||||||
|
if (check_states(m, path, service, PATH_DEAD, SERVICE_RUNNING) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* If service is running and file does not exist, it is stopped */
|
||||||
|
assert_se(unit_start(unit, NULL) >= 0);
|
||||||
|
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
assert_se(unit_stop(unit) >= 0);
|
||||||
|
|
||||||
|
assert_se(unit_start(UNIT(service), NULL) >= 0);
|
||||||
|
if (check_states(m, path, service, PATH_DEAD, SERVICE_RUNNING) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* If service is running and file exists, path unit is running */
|
||||||
|
assert_se(touch(test_file) >= 0);
|
||||||
|
assert_se(unit_start(unit, NULL) >= 0);
|
||||||
|
if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* If service is running first, service is stopped when file is removed */
|
||||||
|
assert_se(rm_rf(test_file, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
|
||||||
|
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
assert_se(unit_stop(unit) >= 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_path_deactivationtoggle_existsglob(Manager *m) {
|
||||||
|
const char *test_file, *test_file2, *test_file3, *test_file4;
|
||||||
|
const char *test_path = "/tmp/test-path_deactivationtoggle/";
|
||||||
|
const char *test_path2 = "/tmp/test-path_deactivationtoggle_secondary/";
|
||||||
|
const char *path_name = "path-deactivationtoggle-existsglob.path";
|
||||||
|
const char *service_name = "path-deactivationtoggle.service";
|
||||||
|
Unit *unit = NULL;
|
||||||
|
Path *path = NULL;
|
||||||
|
Service *service = NULL;
|
||||||
|
|
||||||
|
assert_se(m);
|
||||||
|
|
||||||
|
assert_se(manager_load_startable_unit_or_warn(m, path_name, NULL, &unit) >= 0);
|
||||||
|
|
||||||
|
path = PATH(unit);
|
||||||
|
service = service_for_path(m, path, service_name);
|
||||||
|
|
||||||
|
assert_se(unit_start(unit, NULL) >= 0);
|
||||||
|
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
test_file = strjoina(test_path, "test_file_globFOO");
|
||||||
|
test_file2 = strjoina(test_path, "test_file_globBAR");
|
||||||
|
test_file3 = strjoina(test_path2, "test_file_globFOO");
|
||||||
|
test_file4 = strjoina(test_path2, "test_file_globBAR");
|
||||||
|
|
||||||
|
assert_se(mkdir_p(test_path, 0755) >= 0);
|
||||||
|
assert_se(mkdir_p(test_path2, 0755) >= 0);
|
||||||
|
assert_se(touch(test_file) >= 0);
|
||||||
|
assert_se(touch(test_file2) >= 0);
|
||||||
|
if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
assert_se(rm_rf(test_file, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
|
||||||
|
assert_se(rm_rf(test_file2, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
|
||||||
|
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
assert_se(touch(test_file3) >= 0);
|
||||||
|
assert_se(touch(test_file4) >= 0);
|
||||||
|
assert_se(rename(test_file3, test_file) >= 0);
|
||||||
|
assert_se(rename(test_file4, test_file2) >= 0);
|
||||||
|
if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
assert_se(rename(test_file, test_file3) >= 0);
|
||||||
|
assert_se(rename(test_file2, test_file4) >= 0);
|
||||||
|
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
assert_se(unit_stop(unit) >= 0);
|
||||||
|
assert_se(rm_rf(test_file3, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
|
||||||
|
assert_se(rm_rf(test_file4, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
|
||||||
|
|
||||||
|
assert_se(touch(test_file) >= 0);
|
||||||
|
assert_se(touch(test_file2) >= 0);
|
||||||
|
assert_se(unit_start(unit, NULL) >= 0);
|
||||||
|
if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* If files exist before path is started, service is stopped when files are removed */
|
||||||
|
assert_se(rm_rf(test_file, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
|
||||||
|
assert_se(rm_rf(test_file2, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
|
||||||
|
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
assert_se(touch(test_file) >= 0);
|
||||||
|
if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
assert_se(rm_rf(test_file, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
|
||||||
|
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
assert_se(unit_stop(unit) >= 0);
|
||||||
|
|
||||||
|
assert_se(touch(test_file) >= 0);
|
||||||
|
assert_se(touch(test_file2) >= 0);
|
||||||
|
assert_se(unit_start(unit, NULL) >= 0);
|
||||||
|
if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Service is stopped when files are renamed */
|
||||||
|
assert_se(rename(test_file, test_file3) >= 0);
|
||||||
|
assert_se(rename(test_file2, test_file4) >= 0);
|
||||||
|
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
assert_se(rename(test_file3, test_file) >= 0);
|
||||||
|
if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
assert_se(rename(test_file, test_file3) >= 0);
|
||||||
|
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
assert_se(unit_stop(unit) >= 0);
|
||||||
|
assert_se(rm_rf(test_file3, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
|
||||||
|
assert_se(rm_rf(test_file4, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
|
||||||
|
|
||||||
|
assert_se(unit_start(UNIT(service), NULL) >= 0);
|
||||||
|
if (check_states(m, path, service, PATH_DEAD, SERVICE_RUNNING) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* If service is running and files do not exist, it is stopped */
|
||||||
|
assert_se(unit_start(unit, NULL) >= 0);
|
||||||
|
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
assert_se(unit_stop(unit) >= 0);
|
||||||
|
|
||||||
|
assert_se(unit_start(UNIT(service), NULL) >= 0);
|
||||||
|
if (check_states(m, path, service, PATH_DEAD, SERVICE_RUNNING) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* If service is running and files exist, path unit is running */
|
||||||
|
assert_se(touch(test_file) >= 0);
|
||||||
|
assert_se(touch(test_file2) >= 0);
|
||||||
|
assert_se(unit_start(unit, NULL) >= 0);
|
||||||
|
if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* If service is running first, service is stopped when files are removed */
|
||||||
|
assert_se(rm_rf(test_file, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
|
||||||
|
assert_se(rm_rf(test_file2, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
|
||||||
|
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
assert_se(unit_stop(unit) >= 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_path_deactivationtoggle_directorynotempty(Manager *m) {
|
||||||
|
const char *test_file, *test_file2;
|
||||||
|
const char *test_path = "/tmp/test-path_deactivationtoggle/";
|
||||||
|
const char *test_path2 = "/tmp/test-path_deactivationtoggle_secondary/";
|
||||||
|
const char *path_name = "path-deactivationtoggle-directorynotempty.path";
|
||||||
|
const char *service_name = "path-deactivationtoggle.service";
|
||||||
|
Unit *unit = NULL;
|
||||||
|
Path *path = NULL;
|
||||||
|
Service *service = NULL;
|
||||||
|
|
||||||
|
assert_se(m);
|
||||||
|
|
||||||
|
assert_se(manager_load_startable_unit_or_warn(m, path_name, NULL, &unit) >= 0);
|
||||||
|
|
||||||
|
path = PATH(unit);
|
||||||
|
service = service_for_path(m, path, service_name);
|
||||||
|
|
||||||
|
assert_se(unit_start(unit, NULL) >= 0);
|
||||||
|
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
test_file = strjoina(test_path, "test_file");
|
||||||
|
test_file2 = strjoina(test_path2, "test_file");
|
||||||
|
|
||||||
|
assert_se(mkdir_p(test_path, 0755) >= 0);
|
||||||
|
assert_se(mkdir_p(test_path2, 0755) >= 0);
|
||||||
|
assert_se(touch(test_file) >= 0);
|
||||||
|
if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
assert_se(rm_rf(test_file, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
|
||||||
|
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
assert_se(touch(test_file2) >= 0);
|
||||||
|
assert_se(rename(test_file2, test_file) >= 0);
|
||||||
|
if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
assert_se(rename(test_file, test_file2) >= 0);
|
||||||
|
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
assert_se(unit_stop(unit) >= 0);
|
||||||
|
assert_se(rm_rf(test_file2, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
|
||||||
|
assert_se(rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
|
||||||
|
|
||||||
|
assert_se(mkdir_p(test_path, 0755) >= 0);
|
||||||
|
assert_se(touch(test_file) >= 0);
|
||||||
|
assert_se(unit_start(unit, NULL) >= 0);
|
||||||
|
if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* If directory is not empty before path is started, service is stopped when file is removed */
|
||||||
|
assert_se(rm_rf(test_file, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
|
||||||
|
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
assert_se(touch(test_file) >= 0);
|
||||||
|
if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
assert_se(rm_rf(test_file, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
|
||||||
|
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
assert_se(unit_stop(unit) >= 0);
|
||||||
|
|
||||||
|
assert_se(touch(test_file) >= 0);
|
||||||
|
assert_se(unit_start(unit, NULL) >= 0);
|
||||||
|
if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Service is stopped when directory is empty due to file rename */
|
||||||
|
assert_se(rename(test_file, test_file2) >= 0);
|
||||||
|
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
assert_se(rename(test_file2, test_file) >= 0);
|
||||||
|
if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
assert_se(rename(test_file, test_file2) >= 0);
|
||||||
|
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
assert_se(unit_stop(unit) >= 0);
|
||||||
|
assert_se(rm_rf(test_file2, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
|
||||||
|
|
||||||
|
assert_se(unit_start(UNIT(service), NULL) >= 0);
|
||||||
|
if (check_states(m, path, service, PATH_DEAD, SERVICE_RUNNING) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* If service is running and directory is empty, it is stopped */
|
||||||
|
assert_se(unit_start(unit, NULL) >= 0);
|
||||||
|
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
assert_se(unit_stop(unit) >= 0);
|
||||||
|
|
||||||
|
assert_se(unit_start(UNIT(service), NULL) >= 0);
|
||||||
|
if (check_states(m, path, service, PATH_DEAD, SERVICE_RUNNING) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* If service is running and directory is not empty, path unit is running */
|
||||||
|
assert_se(touch(test_file) >= 0);
|
||||||
|
assert_se(unit_start(unit, NULL) >= 0);
|
||||||
|
if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* If service is running first, service is stopped when directory is empty */
|
||||||
|
assert_se(rm_rf(test_file, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
|
||||||
|
if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
assert_se(unit_stop(unit) >= 0);
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
static const test_function_t tests[] = {
|
static const test_function_t tests[] = {
|
||||||
test_path_exists,
|
test_path_exists,
|
||||||
|
@ -387,6 +753,9 @@ int main(int argc, char *argv[]) {
|
||||||
test_path_unit,
|
test_path_unit,
|
||||||
test_path_directorynotempty,
|
test_path_directorynotempty,
|
||||||
test_path_makedirectory_directorymode,
|
test_path_makedirectory_directorymode,
|
||||||
|
test_path_deactivationtoggle_exists,
|
||||||
|
test_path_deactivationtoggle_existsglob,
|
||||||
|
test_path_deactivationtoggle_directorynotempty,
|
||||||
NULL,
|
NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -85,6 +85,7 @@ ConditionSecurity=
|
||||||
ConditionUser=
|
ConditionUser=
|
||||||
ConditionVirtualization=
|
ConditionVirtualization=
|
||||||
Conflicts=
|
Conflicts=
|
||||||
|
DeactivationToggle=
|
||||||
DefaultDependencies=
|
DefaultDependencies=
|
||||||
DefaultInstance=
|
DefaultInstance=
|
||||||
DeferAcceptSec=
|
DeferAcceptSec=
|
||||||
|
|
|
@ -6406,11 +6406,11 @@ class NetworkdRATests(unittest.TestCase, Utilities):
|
||||||
|
|
||||||
for i in [100, 200, 300, 512, 1024, 2048]:
|
for i in [100, 200, 300, 512, 1024, 2048]:
|
||||||
if i not in [metric_1, metric_2]:
|
if i not in [metric_1, metric_2]:
|
||||||
self.assertNotIn(f'{i}', output)
|
self.assertNotIn(f'metric {i} ', output)
|
||||||
|
|
||||||
for i in ['low', 'medium', 'high']:
|
for i in ['low', 'medium', 'high']:
|
||||||
if i not in [preference_1, preference_2]:
|
if i not in [preference_1, preference_2]:
|
||||||
self.assertNotIn(f'{i}', output)
|
self.assertNotIn(f'pref {i}', output)
|
||||||
|
|
||||||
def test_router_preference(self):
|
def test_router_preference(self):
|
||||||
copy_network_unit('25-veth-client.netdev',
|
copy_network_unit('25-veth-client.netdev',
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
|
||||||
|
[Unit]
|
||||||
|
Description=Test DeactivationToggle with DirectoryNotEmpty
|
||||||
|
|
||||||
|
[Path]
|
||||||
|
DirectoryNotEmpty=/tmp/test-path_deactivationtoggle/
|
||||||
|
Unit=path-deactivationtoggle.service
|
||||||
|
DeactivationToggle=true
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
|
@ -0,0 +1,12 @@
|
||||||
|
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
|
||||||
|
[Unit]
|
||||||
|
Description=Test DeactivationToggle with PathExists
|
||||||
|
|
||||||
|
[Path]
|
||||||
|
PathExists=/tmp/test-path_deactivationtoggle/test_file
|
||||||
|
Unit=path-deactivationtoggle.service
|
||||||
|
DeactivationToggle=true
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
|
@ -0,0 +1,12 @@
|
||||||
|
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
|
||||||
|
[Unit]
|
||||||
|
Description=Test DeactivationToggle with PathExistsGlob
|
||||||
|
|
||||||
|
[Path]
|
||||||
|
PathExistsGlob=/tmp/test-path_deactivationtoggle/test_file_glob*
|
||||||
|
Unit=path-deactivationtoggle.service
|
||||||
|
DeactivationToggle=true
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
|
@ -0,0 +1,10 @@
|
||||||
|
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
|
||||||
|
[Unit]
|
||||||
|
Description=Service Test for Path units
|
||||||
|
StartLimitIntervalSec=0
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
ExecStart=sleep infinity
|
||||||
|
Type=exec
|
||||||
|
RemainAfterExit=true
|
|
@ -0,0 +1,20 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
|
||||||
|
set -eux
|
||||||
|
set -o pipefail
|
||||||
|
|
||||||
|
# shellcheck source=test/units/util.sh
|
||||||
|
. "$(dirname "$0")"/util.sh
|
||||||
|
|
||||||
|
(! systemd-run --wait -p DynamicUser=yes \
|
||||||
|
-p EnvironmentFile=-/usr/lib/systemd/systemd-asan-env \
|
||||||
|
-p WorkingDirectory='~' true)
|
||||||
|
|
||||||
|
assert_eq "$(systemd-run --pipe --uid=root -p WorkingDirectory='~' pwd)" "/root"
|
||||||
|
assert_eq "$(systemd-run --pipe --uid=nobody -p WorkingDirectory='~' pwd)" "/"
|
||||||
|
assert_eq "$(systemd-run --pipe --uid=testuser -p WorkingDirectory='~' pwd)" "/home/testuser"
|
||||||
|
|
||||||
|
(! systemd-run --wait -p DynamicUser=yes -p User=testuser \
|
||||||
|
-p EnvironmentFile=-/usr/lib/systemd/systemd-asan-env \
|
||||||
|
-p WorkingDirectory='~' true)
|
|
@ -16,6 +16,7 @@ ConditionDirectoryNotEmpty=|/run/confexts
|
||||||
ConditionDirectoryNotEmpty=|/var/lib/confexts
|
ConditionDirectoryNotEmpty=|/var/lib/confexts
|
||||||
ConditionDirectoryNotEmpty=|/usr/local/lib/confexts
|
ConditionDirectoryNotEmpty=|/usr/local/lib/confexts
|
||||||
ConditionDirectoryNotEmpty=|/usr/lib/confexts
|
ConditionDirectoryNotEmpty=|/usr/lib/confexts
|
||||||
|
ConditionDirectoryNotEmpty=|/.extra/confext
|
||||||
|
|
||||||
DefaultDependencies=no
|
DefaultDependencies=no
|
||||||
After=local-fs.target
|
After=local-fs.target
|
||||||
|
|
Loading…
Reference in New Issue