Merge pull request #27638 from YHNdnzj/upheldby-unit-file

unit-file: support UpheldBy= in [Install] settings (adding Upholds= deps from .upholds/)
This commit is contained in:
Mike Yuan 2023-05-16 21:53:24 +08:00 committed by GitHub
commit 0313c41068
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 80 additions and 36 deletions

View File

@ -367,10 +367,9 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
<listitem>
<para>Shows units required and wanted by the specified
units. This recursively lists units following the
<varname>Requires=</varname>,
<varname>Requisite=</varname>,
<varname>ConsistsOf=</varname>,
<varname>Wants=</varname>, <varname>BindsTo=</varname>
<varname>Requires=</varname>, <varname>Requisite=</varname>,
<varname>Wants=</varname>, <varname>ConsistsOf=</varname>,
<varname>BindsTo=</varname>, and <varname>Upholds=</varname>
dependencies. If no units are specified,
<filename>default.target</filename> is implied.</para>
@ -1791,7 +1790,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
<para>Show reverse dependencies between units with
<command>list-dependencies</command>, i.e. follow
dependencies of type <varname>WantedBy=</varname>,
<varname>RequiredBy=</varname>,
<varname>RequiredBy=</varname>, <varname>UpheldBy=</varname>,
<varname>PartOf=</varname>, <varname>BoundBy=</varname>,
instead of <varname>Wants=</varname> and similar.
</para>

View File

@ -749,8 +749,7 @@
<para>When <varname>Upholds=b.service</varname> is used on <filename>a.service</filename>, this
dependency will show as <varname>UpheldBy=a.service</varname> in the property listing of
<filename>b.service</filename>. The <varname>UpheldBy=</varname> dependency cannot be specified
directly.</para>
<filename>b.service</filename>.</para>
</listitem>
</varlistentry>
@ -1848,6 +1847,12 @@
<entry>[Unit] section</entry>
<entry>[Install] section</entry>
</row>
<row>
<entry><varname>Upholds=</varname></entry>
<entry><varname>UpheldBy=</varname></entry>
<entry>[Unit] section</entry>
<entry>[Install] section</entry>
</row>
<row>
<entry><varname>PartOf=</varname></entry>
<entry><varname>ConsistsOf=</varname></entry>
@ -1895,10 +1900,10 @@
</tgroup>
</table>
<para>Note: <varname>WantedBy=</varname> and <varname>RequiredBy=</varname> are
used in the [Install] section to create symlinks in <filename>.wants/</filename>
and <filename>.requires/</filename> directories. They cannot be used directly as a
unit configuration setting.</para>
<para>Note: <varname>WantedBy=</varname>, <varname>RequiredBy=</varname>, and <varname>UpheldBy=</varname>
are used in the [Install] section to create symlinks in <filename>.wants/</filename>,
<filename>.requires/</filename>, and <filename>.upholds/</filename> directories. They cannot be used
directly as a unit configuration setting.</para>
<para>Note: <varname>ConsistsOf=</varname>, <varname>BoundBy=</varname>,
<varname>RequisiteOf=</varname>, <varname>ConflictedBy=</varname> are created
@ -1947,23 +1952,23 @@
<varlistentry>
<term><varname>WantedBy=</varname></term>
<term><varname>RequiredBy=</varname></term>
<term><varname>UpheldBy=</varname></term>
<listitem><para>This option may be used more than once, or a space-separated list of unit names may
be given. A symbolic link is created in the <filename>.wants/</filename> or
<filename>.requires/</filename> directory of each of the listed units when this unit is installed by
<command>systemctl enable</command>. This has the effect of a dependency of type
<varname>Wants=</varname> or <varname>Requires=</varname> being added from the listed unit to the
current unit. The primary result is that the current unit will be started when the listed unit is
started, see the description of <varname>Wants=</varname> and <varname>Requires=</varname> in the
[Unit] section for details.</para>
be given. A symbolic link is created in the <filename>.wants/</filename>, <filename>.requires/</filename>,
or <filename>.upholds/</filename> directory of each of the listed units when this unit is installed
by <command>systemctl enable</command>. This has the effect of a dependency of type
<varname>Wants=</varname>, <varname>Requires=</varname>, or <varname>Upholds=</varname> being added
from the listed unit to the current unit. See the description of the mentioned dependency types
in the [Unit] section for details.</para>
<para>In case of template units listing non template units, the listing unit must have
<varname>DefaultInstance=</varname> set, or <command>systemctl enable</command> must be called with
an instance name. The instance (default or specified) will be added to the
<filename>.wants/</filename> or <filename>.requires/</filename> list of the listed unit. For example,
<command>WantedBy=getty.target</command> in a service <filename>getty@.service</filename> will result
in <command>systemctl enable getty@tty2.service</command> creating a
<filename>getty.target.wants/getty@tty2.service</filename> link to
<filename>.wants/</filename>, <filename>.requires/</filename>, or <filename>.upholds/</filename>
list of the listed unit. For example, <command>WantedBy=getty.target</command> in a service
<filename>getty@.service</filename> will result in <command>systemctl enable getty@tty2.service</command>
creating a <filename>getty.target.wants/getty@tty2.service</filename> link to
<filename>getty@.service</filename>. This also applies to listing specific instances of templated
units: this specific instance will gain the dependency. A template unit may also list a template
unit, in which case a generic dependency will be added where each instance of the listing unit will

View File

@ -250,9 +250,10 @@ bool lookup_paths_timestamp_hash_same(const LookupPaths *lp, uint64_t timestamp_
static int directory_name_is_valid(const char *name) {
/* Accept a directory whose name is a valid unit file name ending in .wants/, .requires/ or .d/ */
/* Accept a directory whose name is a valid unit file name ending in .wants/, .requires/,
* .upholds/ or .d/ */
FOREACH_STRING(suffix, ".wants", ".requires", ".d") {
FOREACH_STRING(suffix, ".wants", ".requires", ".upholds", ".d") {
_cleanup_free_ char *chopped = NULL;
const char *e;

View File

@ -88,7 +88,7 @@ int unit_load_dropin(Unit *u) {
assert(u);
/* Load dependencies from .wants and .requires directories */
/* Load dependencies from .wants, .requires and .upholds directories */
r = process_deps(u, UNIT_WANTS, ".wants");
if (r < 0)
return r;
@ -97,6 +97,10 @@ int unit_load_dropin(Unit *u) {
if (r < 0)
return r;
r = process_deps(u, UNIT_UPHOLDS, ".upholds");
if (r < 0)
return r;
/* Load .conf dropins */
r = unit_find_dropin_paths(u, &l);
if (r <= 0)

View File

@ -580,5 +580,6 @@ Scope.OOMPolicy, config_parse_oom_policy,
Install.Alias, NULL, 0, 0
Install.WantedBy, NULL, 0, 0
Install.RequiredBy, NULL, 0, 0
Install.UpheldBy, NULL, 0, 0
Install.Also, NULL, 0, 0
Install.DefaultInstance, NULL, 0, 0

View File

@ -73,7 +73,8 @@ static bool install_info_has_rules(const InstallInfo *i) {
return !strv_isempty(i->aliases) ||
!strv_isempty(i->wanted_by) ||
!strv_isempty(i->required_by);
!strv_isempty(i->required_by) ||
!strv_isempty(i->upheld_by);
}
static bool install_info_has_also(const InstallInfo *i) {
@ -942,7 +943,7 @@ static int find_symlinks(
continue;
suffix = strrchr(de->d_name, '.');
if (!STRPTR_IN_SET(suffix, ".wants", ".requires"))
if (!STRPTR_IN_SET(suffix, ".wants", ".requires", ".upholds"))
continue;
path = path_join(config_path, de->d_name);
@ -967,7 +968,8 @@ static int find_symlinks(
log_debug_errno(r, "Failed to look up symlinks in \"%s\": %m", path);
}
/* We didn't find any suitable symlinks in .wants or .requires directories, let's look for linked unit files in this directory. */
/* We didn't find any suitable symlinks in .wants, .requires or .upholds directories,
* let's look for linked unit files in this directory. */
rewinddir(config_dir);
return find_symlinks_in_directory(config_dir, config_path, root_dir, i,
/* ignore_destination= */ false,
@ -1081,6 +1083,7 @@ static void install_info_clear(InstallInfo *i) {
i->aliases = strv_free(i->aliases);
i->wanted_by = strv_free(i->wanted_by);
i->required_by = strv_free(i->required_by);
i->upheld_by = strv_free(i->upheld_by);
i->also = strv_free(i->also);
i->default_instance = mfree(i->default_instance);
i->symlink_target = mfree(i->symlink_target);
@ -1337,6 +1340,7 @@ static int unit_file_load(
{ "Install", "Alias", config_parse_alias, 0, &info->aliases },
{ "Install", "WantedBy", config_parse_strv, 0, &info->wanted_by },
{ "Install", "RequiredBy", config_parse_strv, 0, &info->required_by },
{ "Install", "UpheldBy", config_parse_strv, 0, &info->upheld_by },
{ "Install", "DefaultInstance", config_parse_default_instance, 0, info },
{ "Install", "Also", config_parse_also, 0, ctx },
{}
@ -1440,7 +1444,8 @@ static int unit_file_load(
return
(int) strv_length(info->aliases) +
(int) strv_length(info->wanted_by) +
(int) strv_length(info->required_by);
(int) strv_length(info->required_by) +
(int) strv_length(info->upheld_by);
}
static int unit_file_load_or_readlink(
@ -2113,6 +2118,10 @@ static int install_info_apply(
if (r == 0)
r = q;
q = install_info_symlink_wants(scope, file_flags, info, lp, config_path, info->upheld_by, ".upholds/", changes, n_changes);
if (r == 0)
r = q;
return r;
}
@ -2733,8 +2742,10 @@ int unit_file_add_dependency(
if (dep == UNIT_WANTS)
l = &info->wanted_by;
else
else if (dep == UNIT_REQUIRES)
l = &info->required_by;
else
l = &info->upheld_by;
strv_free(*l);
*l = strv_new(target_info->name);

View File

@ -92,6 +92,7 @@ struct InstallInfo {
char **aliases;
char **wanted_by;
char **required_by;
char **upheld_by;
char **also;
char *default_instance;

View File

@ -248,9 +248,9 @@ int verb_enable(int argc, char *argv[], void *userdata) {
}
if (carries_install_info == 0 && !ignore_carries_install_info)
log_notice("The unit files have no installation config (WantedBy=, RequiredBy=, Also=,\n"
"Alias= settings in the [Install] section, and DefaultInstance= for template\n"
"units). This means they are not meant to be enabled or disabled using systemctl.\n"
log_notice("The unit files have no installation config (WantedBy=, RequiredBy=, UpheldBy=,\n"
"Also=, or Alias= settings in the [Install] section, and DefaultInstance= for\n"
"template units). This means they are not meant to be enabled or disabled using systemctl.\n"
" \n" /* trick: the space is needed so that the line does not get stripped from output */
"Possible reasons for having this kind of units are:\n"
"%1$s A unit may be statically enabled by being symlinked from another unit's\n"

View File

@ -1135,6 +1135,9 @@ TEST(verify_alias) {
verify_one(&plain_service, "alias.socket", -EXDEV, NULL);
verify_one(&plain_service, "alias@.service", -EXDEV, NULL);
verify_one(&plain_service, "alias@inst.service", -EXDEV, NULL);
/* Setting WantedBy= and RequiredBy= through Alias= is supported for the sake of backwards
* compatibility. */
verify_one(&plain_service, "foo.target.wants/plain.service", 0, NULL);
verify_one(&plain_service, "foo.target.wants/plain.socket", -EXDEV, NULL);
verify_one(&plain_service, "foo.target.wants/plain@.service", -EXDEV, NULL);
@ -1143,9 +1146,14 @@ TEST(verify_alias) {
verify_one(&plain_service, "foo.target.requires/plain.socket", -EXDEV, NULL);
verify_one(&plain_service, "foo.target.requires/plain@.service", -EXDEV, NULL);
verify_one(&plain_service, "foo.target.requires/service", -EXDEV, NULL);
verify_one(&plain_service, "foo.target.conf/plain.service", -EXDEV, NULL);
verify_one(&plain_service, "foo.service/plain.service", -EXDEV, NULL); /* missing dir suffix */
verify_one(&plain_service, "asdf.requires/plain.service", -EXDEV, NULL); /* invalid unit name component */
/* The newly-added UpheldBy= (.upholds/) and other suffixes should be rejected */
verify_one(&plain_service, "foo.target.upholds/plain.service", -EXDEV, NULL);
verify_one(&plain_service, "foo.target.upholds/plain.socket", -EXDEV, NULL);
verify_one(&plain_service, "foo.target.upholds/plain@.service", -EXDEV, NULL);
verify_one(&plain_service, "foo.target.upholds/service", -EXDEV, NULL);
verify_one(&plain_service, "foo.service/plain.service", -EXDEV, NULL); /* missing dir suffix */
verify_one(&plain_service, "foo.target.conf/plain.service", -EXDEV, NULL);
verify_one(&bare_template, "alias.service", -EXDEV, NULL);
verify_one(&bare_template, "alias.socket", -EXDEV, NULL);

View File

@ -269,6 +269,8 @@ Type=
USBFunctionDescriptors=
USBFunctionStrings=
Unit=
UpheldBy=
Upholds=
User=
WakeSystem=
WantedBy=

View File

@ -0,0 +1,9 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Unit that sets UpheldBy= through [Install]
[Service]
ExecStart=/bin/sleep infinity
[Install]
UpheldBy=testsuite-23-retry-uphold.service

View File

@ -30,6 +30,8 @@ done
systemctl stop testsuite-23-uphold.service
systemctl enable testsuite-23-upheldby-install.service
# Idea is this:
# 1. we start testsuite-23-retry-uphold.service
# 2. which through Uphold= starts testsuite-23-retry-upheld.service
@ -42,12 +44,13 @@ systemctl stop testsuite-23-uphold.service
rm -f /tmp/testsuite-23-retry-fail
systemctl start testsuite-23-retry-uphold.service
systemctl is-active testsuite-23-upheldby-install.service
while ! systemctl is-failed testsuite-23-retry-fail.service ; do
sleep .5
done
systemctl is-active testsuite-23-retry-upheld.service && { echo 'unexpected success'; exit 1; }
(! systemctl is-active testsuite-23-retry-upheld.service)
touch /tmp/testsuite-23-retry-fail