mirror of
https://github.com/systemd/systemd.git
synced 2025-03-29 06:50:16 +03:00
Merge pull request #2973 from poettering/search-path
Many fixes, in particular to the install logic
This commit is contained in:
commit
025ef1d226
1
.gitignore
vendored
1
.gitignore
vendored
@ -249,7 +249,6 @@
|
||||
/test-pty
|
||||
/test-qcow2
|
||||
/test-ratelimit
|
||||
/test-rbtree
|
||||
/test-replace-var
|
||||
/test-resolve
|
||||
/test-resolve-tables
|
||||
|
@ -213,6 +213,7 @@
|
||||
b) socket() and socketpair() must get SOCK_CLOEXEC passed
|
||||
c) recvmsg() must get MSG_CMSG_CLOEXEC set
|
||||
d) F_DUPFD_CLOEXEC should be used instead of F_DUPFD, and so on
|
||||
f) invocations of fopen() should take "e"
|
||||
|
||||
- We never use the POSIX version of basename() (which glibc defines it in
|
||||
libgen.h), only the GNU version (which glibc defines in string.h).
|
||||
|
16
Makefile.am
16
Makefile.am
@ -747,8 +747,6 @@ libbasic_la_SOURCES = \
|
||||
src/basic/missing_syscall.h \
|
||||
src/basic/capability-util.c \
|
||||
src/basic/capability-util.h \
|
||||
src/basic/c-rbtree.c \
|
||||
src/basic/c-rbtree.h \
|
||||
src/basic/conf-files.c \
|
||||
src/basic/conf-files.h \
|
||||
src/basic/stdio-util.h \
|
||||
@ -1038,7 +1036,9 @@ libshared_la_SOURCES = \
|
||||
src/shared/machine-pool.c \
|
||||
src/shared/machine-pool.h \
|
||||
src/shared/resolve-util.c \
|
||||
src/shared/resolve-util.h
|
||||
src/shared/resolve-util.h \
|
||||
src/shared/tests.h \
|
||||
src/shared/tests.c
|
||||
|
||||
if HAVE_UTMP
|
||||
libshared_la_SOURCES += \
|
||||
@ -1494,7 +1494,6 @@ tests += \
|
||||
test-copy \
|
||||
test-cap-list \
|
||||
test-sigbus \
|
||||
test-rbtree \
|
||||
test-verbs \
|
||||
test-af-list \
|
||||
test-arphrd-list \
|
||||
@ -1748,12 +1747,6 @@ test_sigbus_SOURCES = \
|
||||
test_sigbus_LDADD = \
|
||||
libshared.la
|
||||
|
||||
test_rbtree_SOURCES = \
|
||||
src/test/test-rbtree.c
|
||||
|
||||
test_rbtree_LDADD = \
|
||||
libshared.la
|
||||
|
||||
test_condition_SOURCES = \
|
||||
src/test/test-condition.c
|
||||
|
||||
@ -4729,8 +4722,7 @@ systemd_localed_SOURCES = \
|
||||
src/locale/localed.c
|
||||
|
||||
systemd_localed_LDADD = \
|
||||
libshared.la \
|
||||
$(XKBCOMMON_LIBS)
|
||||
libshared.la
|
||||
|
||||
systemd_localed_CFLAGS = \
|
||||
$(AM_CFLAGS) \
|
||||
|
3
NEWS
3
NEWS
@ -417,6 +417,9 @@ CHANGES WITH 228:
|
||||
|
||||
https://sourceware.org/bugzilla/show_bug.cgi?id=19108
|
||||
|
||||
Note that only util-linux versions built with
|
||||
--enable-libmount-force-mountinfo are supported.
|
||||
|
||||
* Support for the ".snapshot" unit type has been removed. This
|
||||
feature turned out to be little useful and little used, and
|
||||
has now been removed from the core and from systemctl.
|
||||
|
1
README
1
README
@ -118,6 +118,7 @@ REQUIREMENTS:
|
||||
glibc >= 2.16
|
||||
libcap
|
||||
libmount >= 2.27.1 (from util-linux)
|
||||
(util-linux *must* be built with --enable-libmount-force-mountinfo)
|
||||
libseccomp >= 1.0.0 (optional)
|
||||
libblkid >= 2.24 (from util-linux) (optional)
|
||||
libkmod >= 15 (optional)
|
||||
|
25
TODO
25
TODO
@ -33,16 +33,29 @@ Janitorial Clean-ups:
|
||||
|
||||
Features:
|
||||
|
||||
* when using UTF8, ellipsize with "…" rather than "...", so that we can show more contents before truncating
|
||||
* transient units: don't bother with actually setting unit properties, we
|
||||
reload the unit file anyway
|
||||
|
||||
* machinectl remove --hidden + machinectl remove --all
|
||||
* https://github.com/systemd/systemd/pull/2886 is fucked
|
||||
|
||||
* make sure resolved can be restarted without losing pushed-in dns config
|
||||
|
||||
* fix https://github.com/systemd/systemd/pull/2890, this shouldn't be exported
|
||||
like this.
|
||||
|
||||
* journald: sigbus API via a signal-handler safe function that people may call
|
||||
from the SIGBUS handler
|
||||
|
||||
* resolved: cefmz.x.incapdns.net fails to authenticate
|
||||
|
||||
* when using UTF8, ellipsize with "…" rather than "...", so that we can show more contents before truncating
|
||||
|
||||
* move specifier expansion from service_spawn() into load-fragment.c
|
||||
|
||||
* optionally, also require WATCHDOG=1 notifications during service start-up and shutdown
|
||||
|
||||
* resolved: maybe, after all, implement local listening for DNS packets on port
|
||||
53.
|
||||
127.0.0.53:53.
|
||||
|
||||
* delay activation of logind until somebody logs in, or when /dev/tty0 pulls it
|
||||
in or lingering is on (so that containers don't bother with it until PAM is used). also exit-on-idle
|
||||
@ -68,12 +81,6 @@ Features:
|
||||
|
||||
* push CPUAffinity= also into the "cpuset" cgroup controller (only after the cpuset controller got ported to the unified hierarchy)
|
||||
|
||||
* add a new command "systemctl revert" or so, that removes all dropin
|
||||
snippets in /run and /etc, and all unit files with counterparts in
|
||||
/usr, and thus undoes what "systemctl set-property" and "systemctl
|
||||
edit" create. Maybe even add "systemctl revert -a" to do this for
|
||||
all units.
|
||||
|
||||
* PID 1 should send out sd_notify("WATCHDOG=1") messages (for usage in the --user mode, and when run via nspawn)
|
||||
|
||||
* consider throwing a warning if a service declares it wants to be "Before=" a .device unit.
|
||||
|
@ -133,7 +133,9 @@
|
||||
|
||||
<para>When listing VM or container images, do not suppress
|
||||
images beginning in a dot character
|
||||
(<literal>.</literal>).</para></listitem>
|
||||
(<literal>.</literal>).</para>
|
||||
|
||||
<para>When cleaning VM or container images, remove all images, not just hidden ones.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
@ -217,9 +219,11 @@
|
||||
<term><option>--read-only</option></term>
|
||||
|
||||
<listitem><para>When used with <command>bind</command>, applies
|
||||
a read-only bind mount.</para></listitem>
|
||||
</varlistentry>
|
||||
a read-only bind mount.</para>
|
||||
|
||||
<para>When used with <command>clone</command>, <command>import-raw</command> or <command>import-tar</command> a
|
||||
read-only container or VM image is created.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>-n</option></term>
|
||||
@ -599,7 +603,10 @@
|
||||
all other settings that could identify the instance
|
||||
unmodified. The original image and the cloned copy will hence
|
||||
share these credentials, and it might be necessary to manually
|
||||
change them in the copy.</para></listitem>
|
||||
change them in the copy.</para>
|
||||
|
||||
<para>If combined with the <option>--read-only</option> switch a read-only cloned image is
|
||||
created.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
@ -660,6 +667,23 @@
|
||||
itself.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><command>clean</command></term>
|
||||
|
||||
<listitem><para>Remove hidden VM or container images (or all). This command removes all hidden machine images
|
||||
from <filename>/var/lib/machines</filename>, i.e. those whose name begins with a dot. Use <command>machinectl
|
||||
list-images --all</command> to see a list of all machine images, including the hidden ones.</para>
|
||||
|
||||
<para>When combined with the <option>--all</option> switch removes all images, not just hidden ones. This
|
||||
command effectively empties <filename>/var/lib/machines</filename>.</para>
|
||||
|
||||
<para>Note that commands such as <command>machinectl pull-tar</command> or <command>machinectl
|
||||
pull-raw</command> usually create hidden, read-only, unmodified machine images from the downloaded image first,
|
||||
before cloning a writable working copy of it, in order to avoid duplicate downloads in case of images that are
|
||||
reused multiple times. Use <command>machinectl clean</command> to remove old, hidden images created this
|
||||
way.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
</variablelist></refsect2>
|
||||
|
||||
<refsect2><title>Image Transfer Commands</title><variablelist>
|
||||
|
@ -1168,22 +1168,32 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service
|
||||
</row>
|
||||
<row>
|
||||
<entry><literal>static</literal></entry>
|
||||
<entry>The unit file is not enabled, and has no provisions for enabling in the <literal>[Install]</literal> section.</entry>
|
||||
<entry>The unit file is not enabled, and has no provisions for enabling in the <literal>[Install]</literal> unit file section.</entry>
|
||||
<entry>0</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><literal>indirect</literal></entry>
|
||||
<entry>The unit file itself is not enabled, but it has a non-empty <varname>Also=</varname> setting in the <literal>[Install]</literal> section, listing other unit files that might be enabled.</entry>
|
||||
<entry>The unit file itself is not enabled, but it has a non-empty <varname>Also=</varname> setting in the <literal>[Install]</literal> unit file section, listing other unit files that might be enabled.</entry>
|
||||
<entry>0</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><literal>disabled</literal></entry>
|
||||
<entry>Unit file is not enabled, but contains an <literal>[Install]</literal> section with installation instructions.</entry>
|
||||
<entry>The unit file is not enabled, but contains an <literal>[Install]</literal> section with installation instructions.</entry>
|
||||
<entry>> 0</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><literal>generated</literal></entry>
|
||||
<entry>The unit file was generated dynamically via a generator tool. See <citerefentry><refentrytitle>systemd.generator</refentrytitle><manvolnum>7</manvolnum></citerefentry>. Generated unit files may not be enabled, they are enabled implicitly by their generator.</entry>
|
||||
<entry>0</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><literal>transient</literal></entry>
|
||||
<entry>The unit file has been created dynamically with the runtime API. Transient units may not be enabled.</entry>
|
||||
<entry>0</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><literal>bad</literal></entry>
|
||||
<entry>Unit file is invalid or another error occurred. Note that <command>is-enabled</command> will not actually return this state, but print an error message instead. However the unit file listing printed by <command>list-unit-files</command> might show it.</entry>
|
||||
<entry>The unit file is invalid or another error occurred. Note that <command>is-enabled</command> will not actually return this state, but print an error message instead. However the unit file listing printed by <command>list-unit-files</command> might show it.</entry>
|
||||
<entry>> 0</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
@ -1234,6 +1244,28 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><command>revert <replaceable>NAME</replaceable>...</command></term>
|
||||
|
||||
<listitem>
|
||||
<para>Revert one or more unit files to their vendor versions. This command removes drop-in configuration
|
||||
files that modify the specified units, as well as any user-configured unit file that overrides a matching
|
||||
vendor supplied unit file. Specifically, for a unit <literal>foo.service</literal> the matching directories
|
||||
<literal>foo.service.d/</literal> with all their contained files are removed, both below the persistent and
|
||||
runtime configuration directories (i.e. below <filename>/etc/systemd/system</filename> and
|
||||
<filename>/run/systemd/system</filename>); if the unit file has a vendor-supplied version (i.e. a unit file
|
||||
located below <filename>/usr</filename>) any matching peristent or runtime unit file that overrides it is
|
||||
removed, too. Note that if a unit file has no vendor-supplied version (i.e. is only defined below
|
||||
<filename>/etc/systemd/system</filename> or <filename>/run/systemd/system</filename>, but not in a unit
|
||||
file stored below <filename>/usr</filename>), then it is not removed. Also, if a unit is masked, it is
|
||||
unmasked.</para>
|
||||
|
||||
<para>Effectively, this command may be used to undo all changes made with <command>systemctl
|
||||
edit</command>, <command>systemctl set-property</command> and <command>systemctl mask</command> and puts
|
||||
the original unit file with its settings back in effect.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><command>add-wants <replaceable>TARGET</replaceable>
|
||||
<replaceable>NAME</replaceable>...</command></term>
|
||||
|
@ -58,7 +58,7 @@
|
||||
</cmdsynopsis>
|
||||
<cmdsynopsis>
|
||||
<command>systemd-nspawn</command>
|
||||
<arg choice="plain">-b</arg>
|
||||
<arg choice="plain">--boot</arg>
|
||||
<arg choice="opt" rep="repeat">OPTIONS</arg>
|
||||
<arg choice="opt" rep="repeat">ARGS</arg>
|
||||
</cmdsynopsis>
|
||||
@ -263,7 +263,7 @@
|
||||
signals. It is recommended to use this mode to invoke arbitrary commands in containers, unless they have been
|
||||
modified to run correctly as PID 1. Or in other words: this switch should be used for pretty much all commands,
|
||||
except when the command refers to an init or shell implementation, as these are generally capable of running
|
||||
correctly as PID 1). This option may not be combined with <option>--boot</option> or
|
||||
correctly as PID 1. This option may not be combined with <option>--boot</option> or
|
||||
<option>--share-system</option>.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
@ -294,12 +294,12 @@
|
||||
<tbody>
|
||||
<row>
|
||||
<entry>Neither <option>--as-pid2</option> nor <option>--boot</option> specified</entry>
|
||||
<entry>The passed parameters are interpreted as command line, which is executed as PID 1 in the container.</entry>
|
||||
<entry>The passed parameters are interpreted as the command line, which is executed as PID 1 in the container.</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry><option>--as-pid2</option> specified</entry>
|
||||
<entry>The passed parameters are interpreted as command line, which are executed as PID 2 in the container. A stub init process is run as PID 1.</entry>
|
||||
<entry>The passed parameters are interpreted as the command line, which is executed as PID 2 in the container. A stub init process is run as PID 1.</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
|
@ -97,11 +97,9 @@
|
||||
<para>An implicit <varname>Before=</varname> dependency is created
|
||||
between an automount unit and the mount unit it activates.</para>
|
||||
|
||||
<para>Automount units acquire automatic <varname>Before=</varname>
|
||||
and <varname>Conflicts=</varname> on
|
||||
<filename>umount.target</filename> in order to be stopped during
|
||||
shutdown, unless <varname>DefaultDependencies=no</varname> is
|
||||
set.</para>
|
||||
<para>Automount units acquire automatic <varname>Before=</varname> and <varname>Conflicts=</varname> on
|
||||
<filename>umount.target</filename> in order to be stopped during shutdown, unless
|
||||
<varname>DefaultDependencies=no</varname> is set in the <literal>[Unit]</literal> section.</para>
|
||||
|
||||
</refsect1>
|
||||
|
||||
|
@ -128,26 +128,17 @@
|
||||
<filename>systemd-quotacheck.service</filename> and
|
||||
<filename>quotaon.service</filename> are added.</para>
|
||||
|
||||
<para>For mount units with
|
||||
<varname>DefaultDependencies=yes</varname> (the default) a couple
|
||||
additional dependencies are added. Mount units referring to local
|
||||
file systems automatically gain an <varname>After=</varname>
|
||||
dependency on <filename>local-fs-pre.target</filename>. Network
|
||||
mount units automatically acquire <varname>After=</varname>
|
||||
dependencies on <filename>remote-fs-pre.target</filename>,
|
||||
<filename>network.target</filename> and
|
||||
<filename>network-online.target</filename>. Towards the latter a
|
||||
<varname>Wants=</varname> unit is added as well. Mount units
|
||||
referring to local and network file systems are distinguished by
|
||||
their file system type specification. In some cases this is not
|
||||
sufficient (for example network block device based mounts, such as
|
||||
iSCSI), in which case <option>_netdev</option> may be added to the
|
||||
mount option string of the unit, which forces systemd to consider the
|
||||
mount unit a network mount. Mount units (regardless if local or
|
||||
network) also acquire automatic <varname>Before=</varname> and
|
||||
<varname>Conflicts=</varname> on
|
||||
<filename>umount.target</filename> in order to be stopped
|
||||
during shutdown.</para>
|
||||
<para>For mount units with <varname>DefaultDependencies=yes</varname> in the <literal>[Unit]</literal> section (the
|
||||
default) a couple additional dependencies are added. Mount units referring to local file systems automatically gain
|
||||
an <varname>After=</varname> dependency on <filename>local-fs-pre.target</filename>. Network mount units
|
||||
automatically acquire <varname>After=</varname> dependencies on <filename>remote-fs-pre.target</filename>,
|
||||
<filename>network.target</filename> and <filename>network-online.target</filename>. Towards the latter a
|
||||
<varname>Wants=</varname> unit is added as well. Mount units referring to local and network file systems are
|
||||
distinguished by their file system type specification. In some cases this is not sufficient (for example network
|
||||
block device based mounts, such as iSCSI), in which case <option>_netdev</option> may be added to the mount option
|
||||
string of the unit, which forces systemd to consider the mount unit a network mount. Mount units (regardless if
|
||||
local or network) also acquire automatic <varname>Before=</varname> and <varname>Conflicts=</varname> on
|
||||
<filename>umount.target</filename> in order to be stopped during shutdown.</para>
|
||||
|
||||
<para>Additional implicit dependencies may be added as result of
|
||||
execution and resource control parameters as documented in
|
||||
|
@ -91,16 +91,12 @@
|
||||
<para>An implicit <varname>Before=</varname> dependency is added
|
||||
between a path unit and the unit it is supposed to activate.</para>
|
||||
|
||||
<para>Unless <varname>DefaultDependencies=false</varname> is used,
|
||||
path units will implicitly have dependencies of type
|
||||
<varname>Before=</varname> on <filename>paths.target</filename>,
|
||||
dependencies of type <varname>After=</varname> and
|
||||
<varname>Requires=</varname> on
|
||||
<filename>sysinit.target</filename>, and have dependencies of type
|
||||
<varname>Conflicts=</varname> and <varname>Before=</varname> on
|
||||
<filename>shutdown.target</filename>. These ensure that path units
|
||||
are terminated cleanly prior to system shutdown. Only path units
|
||||
involved with early boot or late system shutdown should disable
|
||||
<para>Unless <varname>DefaultDependencies=false</varname> in the <literal>[Unit]</literal> section is used, path
|
||||
units will implicitly have dependencies of type <varname>Before=</varname> on <filename>paths.target</filename>,
|
||||
dependencies of type <varname>After=</varname> and <varname>Requires=</varname> on
|
||||
<filename>sysinit.target</filename>, and have dependencies of type <varname>Conflicts=</varname> and
|
||||
<varname>Before=</varname> on <filename>shutdown.target</filename>. These ensure that path units are terminated
|
||||
cleanly prior to system shutdown. Only path units involved with early boot or late system shutdown should disable
|
||||
this option.
|
||||
</para>
|
||||
</refsect1>
|
||||
|
@ -100,18 +100,13 @@
|
||||
their activated <filename>.socket</filename> units via an
|
||||
automatic <varname>After=</varname> dependency.</para>
|
||||
|
||||
<para>Unless <varname>DefaultDependencies=</varname> is set to
|
||||
<option>false</option>, service units will implicitly have
|
||||
dependencies of type <varname>Requires=</varname> and
|
||||
<varname>After=</varname> on <filename>sysinit.target</filename>,
|
||||
a dependency of type <varname>After=</varname> on
|
||||
<filename>basic.target</filename> as well as dependencies of
|
||||
type <varname>Conflicts=</varname> and <varname>Before=</varname>
|
||||
on <filename>shutdown.target</filename>. These ensure that normal
|
||||
service units pull in basic system initialization, and are
|
||||
terminated cleanly prior to system shutdown. Only services
|
||||
involved with early boot or late system shutdown should disable
|
||||
this option.</para>
|
||||
<para>Unless <varname>DefaultDependencies=</varname> in the <literal>[Unit]</literal> is set to
|
||||
<option>false</option>, service units will implicitly have dependencies of type <varname>Requires=</varname> and
|
||||
<varname>After=</varname> on <filename>sysinit.target</filename>, a dependency of type <varname>After=</varname> on
|
||||
<filename>basic.target</filename> as well as dependencies of type <varname>Conflicts=</varname> and
|
||||
<varname>Before=</varname> on <filename>shutdown.target</filename>. These ensure that normal service units pull in
|
||||
basic system initialization, and are terminated cleanly prior to system shutdown. Only services involved with early
|
||||
boot or late system shutdown should disable this option.</para>
|
||||
|
||||
<para>Instanced service units (i.e. service units with an <literal>@</literal> in their name) are assigned by
|
||||
default a per-template slice unit (see
|
||||
|
@ -106,14 +106,10 @@
|
||||
<varname>After=</varname> and <varname>Requires=</varname> on
|
||||
their immediate parent slice unit.</para>
|
||||
|
||||
<para>Unless <varname>DefaultDependencies=false</varname>
|
||||
is used, slice units will implicitly have dependencies of
|
||||
type <varname>Conflicts=</varname> and
|
||||
<varname>Before=</varname> on
|
||||
<filename>shutdown.target</filename>. These ensure
|
||||
that slice units are removed prior to system
|
||||
shutdown. Only slice units involved with early boot or
|
||||
late system shutdown should disable this option.
|
||||
<para>Unless <varname>DefaultDependencies=false</varname> is used in the <literal>[Unit]</literal> section, slice
|
||||
units will implicitly have dependencies of type <varname>Conflicts=</varname> and <varname>Before=</varname> on
|
||||
<filename>shutdown.target</filename>. These ensure that slice units are removed prior to system shutdown. Only
|
||||
slice units involved with early boot or late system shutdown should disable this option.
|
||||
</para>
|
||||
</refsect1>
|
||||
|
||||
|
@ -97,16 +97,12 @@
|
||||
<filename>foo@.service</filename> must exist from which services
|
||||
are instantiated for each incoming connection.</para>
|
||||
|
||||
<para>Unless <varname>DefaultDependencies=</varname> is set to
|
||||
<option>false</option>, socket units will implicitly have
|
||||
dependencies of type <varname>Requires=</varname> and
|
||||
<varname>After=</varname> on <filename>sysinit.target</filename>
|
||||
as well as dependencies of type <varname>Conflicts=</varname> and
|
||||
<varname>Before=</varname> on
|
||||
<filename>shutdown.target</filename>. These ensure that socket
|
||||
units pull in basic system initialization, and are terminated
|
||||
cleanly prior to system shutdown. Only sockets involved with early
|
||||
boot or late system shutdown should disable this option.</para>
|
||||
<para>Unless <varname>DefaultDependencies=</varname> in the <literal>[Unit]</literal> section is set to
|
||||
<option>false</option>, socket units will implicitly have dependencies of type <varname>Requires=</varname> and
|
||||
<varname>After=</varname> on <filename>sysinit.target</filename> as well as dependencies of type
|
||||
<varname>Conflicts=</varname> and <varname>Before=</varname> on <filename>shutdown.target</filename>. These ensure
|
||||
that socket units pull in basic system initialization, and are terminated cleanly prior to system shutdown. Only
|
||||
sockets involved with early boot or late system shutdown should disable this option.</para>
|
||||
|
||||
<para>Socket units will have a <varname>Before=</varname>
|
||||
dependency on the service which they trigger added implicitly. No
|
||||
|
@ -95,12 +95,10 @@
|
||||
dependencies on the device units or the mount units of the files
|
||||
they are activated from.</para>
|
||||
|
||||
<para>Swap units with <varname>DefaultDependencies=</varname>
|
||||
enabled implicitly acquire a <varname>Conflicts=</varname> and an
|
||||
<varname>After=</varname> dependency on
|
||||
<filename>umount.target</filename> so that they are deactivated at
|
||||
shutdown, unless <varname>DefaultDependencies=no</varname> is
|
||||
specified.</para>
|
||||
<para>Swap units with <varname>DefaultDependencies=</varname> in the <literal>[Unit]</literal> section enabled
|
||||
implicitly acquire a <varname>Conflicts=</varname> and an <varname>After=</varname> dependency on
|
||||
<filename>umount.target</filename> so that they are deactivated at shutdown, unless
|
||||
<varname>DefaultDependencies=no</varname> is specified.</para>
|
||||
|
||||
<para>Additional implicit dependencies may be added as result of
|
||||
execution and resource control parameters as documented in
|
||||
|
@ -82,14 +82,11 @@
|
||||
<refsect1>
|
||||
<title>Automatic Dependencies</title>
|
||||
|
||||
<para>Unless <varname>DefaultDependencies=</varname> is set to
|
||||
<option>no</option>, target units will implicitly complement all
|
||||
configured dependencies of type <varname>Wants=</varname>,
|
||||
<varname>Requires=</varname> with dependencies of type
|
||||
<varname>After=</varname>, unless an ordering dependency of any
|
||||
kind between the target and the respective other unit is already
|
||||
in place. Note that this behaviour is disabled if either unit has
|
||||
<varname>DefaultDependencies=no</varname>.</para>
|
||||
<para>Unless <varname>DefaultDependencies=</varname> in the <literal>[Unit]</literal> section is set to
|
||||
<option>no</option>, target units will implicitly complement all configured dependencies of type
|
||||
<varname>Wants=</varname>, <varname>Requires=</varname> with dependencies of type <varname>After=</varname>, unless
|
||||
an ordering dependency of any kind between the target and the respective other unit is already in place. Note that
|
||||
this behaviour is disabled if either unit has <varname>DefaultDependencies=no</varname>.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
|
@ -81,21 +81,15 @@
|
||||
<para>Timer units automatically gain a <varname>Before=</varname>
|
||||
dependency on the service they are supposed to activate.</para>
|
||||
|
||||
<para>Unless <varname>DefaultDependencies=</varname> is set to
|
||||
<option>false</option>, all timer units will implicitly have
|
||||
dependencies of type <varname>Requires=</varname> and
|
||||
<varname>After=</varname> on <filename>sysinit.target</filename>,
|
||||
a dependency of type <varname>Before=</varname> on
|
||||
<filename>timers.target</filename>, as well as
|
||||
<varname>Conflicts=</varname> and <varname>Before=</varname> on
|
||||
<filename>shutdown.target</filename> to ensure that they are
|
||||
stopped cleanly prior to system shutdown. Timer units with at
|
||||
least one <varname>OnCalendar=</varname> directive will have an
|
||||
additional <varname>After=</varname> dependency on
|
||||
<filename>timer-sync.target</filename> to avoid being started
|
||||
before the system clock has been correctly set. Only timer units
|
||||
involved with early boot or late system shutdown should disable
|
||||
the <varname>DefaultDependencies=</varname> option.</para>
|
||||
<para>Unless <varname>DefaultDependencies=</varname> in the <literal>[Unit]</literal> section is set to
|
||||
<option>false</option>, all timer units will implicitly have dependencies of type <varname>Requires=</varname> and
|
||||
<varname>After=</varname> on <filename>sysinit.target</filename>, a dependency of type <varname>Before=</varname>
|
||||
on <filename>timers.target</filename>, as well as <varname>Conflicts=</varname> and <varname>Before=</varname> on
|
||||
<filename>shutdown.target</filename> to ensure that they are stopped cleanly prior to system shutdown. Timer units
|
||||
with at least one <varname>OnCalendar=</varname> directive will have an additional <varname>After=</varname>
|
||||
dependency on <filename>timer-sync.target</filename> to avoid being started before the system clock has been
|
||||
correctly set. Only timer units involved with early boot or late system shutdown should disable the
|
||||
<varname>DefaultDependencies=</varname> option.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
|
@ -66,18 +66,16 @@
|
||||
<para><literallayout><filename>/etc/systemd/system/*</filename>
|
||||
<filename>/run/systemd/system/*</filename>
|
||||
<filename>/usr/lib/systemd/system/*</filename>
|
||||
<filename>...</filename>
|
||||
<filename>…</filename>
|
||||
</literallayout></para>
|
||||
|
||||
<para><literallayout><filename>$XDG_CONFIG_HOME/systemd/user/*</filename>
|
||||
<filename>$HOME/.config/systemd/user/*</filename>
|
||||
<para><literallayout><filename>~/.config/systemd/user/*</filename>
|
||||
<filename>/etc/systemd/user/*</filename>
|
||||
<filename>$XDG_RUNTIME_DIR/systemd/user/*</filename>
|
||||
<filename>/run/systemd/user/*</filename>
|
||||
<filename>$XDG_DATA_HOME/systemd/user/*</filename>
|
||||
<filename>$HOME/.local/share/systemd/user/*</filename>
|
||||
<filename>~/.local/share/systemd/user/*</filename>
|
||||
<filename>/usr/lib/systemd/user/*</filename>
|
||||
<filename>...</filename>
|
||||
<filename>…</filename>
|
||||
</literallayout></para>
|
||||
</refsynopsisdiv>
|
||||
|
||||
|
@ -231,14 +231,12 @@ static int verify_unit(Unit *u, bool check_man) {
|
||||
return r;
|
||||
}
|
||||
|
||||
int verify_units(char **filenames, ManagerRunningAs running_as, bool check_man) {
|
||||
int verify_units(char **filenames, UnitFileScope scope, bool check_man) {
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error err = SD_BUS_ERROR_NULL;
|
||||
_cleanup_free_ char *var = NULL;
|
||||
Manager *m = NULL;
|
||||
FILE *serial = NULL;
|
||||
FDSet *fdset = NULL;
|
||||
|
||||
_cleanup_free_ char *var = NULL;
|
||||
|
||||
char **filename;
|
||||
int r = 0, k;
|
||||
|
||||
@ -255,7 +253,7 @@ int verify_units(char **filenames, ManagerRunningAs running_as, bool check_man)
|
||||
|
||||
assert_se(set_unit_path(var) >= 0);
|
||||
|
||||
r = manager_new(running_as, true, &m);
|
||||
r = manager_new(scope, true, &m);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to initialize manager: %m");
|
||||
|
||||
|
@ -23,4 +23,4 @@
|
||||
|
||||
#include "path-lookup.h"
|
||||
|
||||
int verify_units(char **filenames, ManagerRunningAs running_as, bool check_man);
|
||||
int verify_units(char **filenames, UnitFileScope scope, bool check_man);
|
||||
|
@ -1443,7 +1443,7 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
if (streq_ptr(argv[optind], "verify"))
|
||||
r = verify_units(argv+optind+1,
|
||||
arg_user ? MANAGER_USER : MANAGER_SYSTEM,
|
||||
arg_user ? UNIT_FILE_USER : UNIT_FILE_SYSTEM,
|
||||
arg_man);
|
||||
else {
|
||||
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
||||
|
@ -1,674 +0,0 @@
|
||||
/***
|
||||
This file is part of systemd. See COPYING for details.
|
||||
|
||||
systemd is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
systemd is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
/*
|
||||
* RB-Tree Implementation
|
||||
* This implements the insertion/removal of elements in RB-Trees. You're highly
|
||||
* recommended to have an RB-Tree documentation at hand when reading this. Both
|
||||
* insertion and removal can be split into a handful of situations that can
|
||||
* occur. Those situations are enumerated as "Case 1" to "Case n" here, and
|
||||
* follow closely the cases described in most RB-Tree documentations. This file
|
||||
* does not explain why it is enough to handle just those cases, nor does it
|
||||
* provide a proof of correctness. Dig out your algorithm 101 handbook if
|
||||
* you're interested.
|
||||
*
|
||||
* This implementation is *not* straightforward. Usually, a handful of
|
||||
* rotation, reparent, swap and link helpers can be used to implement the
|
||||
* rebalance operations. However, those often perform unnecessary writes.
|
||||
* Therefore, this implementation hard-codes all the operations. You're highly
|
||||
* recommended to look at the two basic helpers before reading the code:
|
||||
* c_rbtree_swap_child()
|
||||
* c_rbtree_set_parent_and_color()
|
||||
* Those are the only helpers used, hence, you should really know what they do
|
||||
* before digging into the code.
|
||||
*
|
||||
* For a highlevel documentation of the API, see the header file and docbook
|
||||
* comments.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include "c-rbtree.h"
|
||||
|
||||
enum {
|
||||
C_RBNODE_RED = 0,
|
||||
C_RBNODE_BLACK = 1,
|
||||
};
|
||||
|
||||
static inline unsigned long c_rbnode_color(CRBNode *n) {
|
||||
return (unsigned long)n->__parent_and_color & 1UL;
|
||||
}
|
||||
|
||||
static inline _Bool c_rbnode_is_red(CRBNode *n) {
|
||||
return c_rbnode_color(n) == C_RBNODE_RED;
|
||||
}
|
||||
|
||||
static inline _Bool c_rbnode_is_black(CRBNode *n) {
|
||||
return c_rbnode_color(n) == C_RBNODE_BLACK;
|
||||
}
|
||||
|
||||
/**
|
||||
* c_rbnode_leftmost() - return leftmost child
|
||||
* @n: current node, or NULL
|
||||
*
|
||||
* This returns the leftmost child of @n. If @n is NULL, this will return NULL.
|
||||
* In all other cases, this function returns a valid pointer. That is, if @n
|
||||
* does not have any left children, this returns @n.
|
||||
*
|
||||
* Worst case runtime (n: number of elements in tree): O(log(n))
|
||||
*
|
||||
* Return: Pointer to leftmost child, or NULL.
|
||||
*/
|
||||
CRBNode *c_rbnode_leftmost(CRBNode *n) {
|
||||
if (n)
|
||||
while (n->left)
|
||||
n = n->left;
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* c_rbnode_rightmost() - return rightmost child
|
||||
* @n: current node, or NULL
|
||||
*
|
||||
* This returns the rightmost child of @n. If @n is NULL, this will return
|
||||
* NULL. In all other cases, this function returns a valid pointer. That is, if
|
||||
* @n does not have any right children, this returns @n.
|
||||
*
|
||||
* Worst case runtime (n: number of elements in tree): O(log(n))
|
||||
*
|
||||
* Return: Pointer to rightmost child, or NULL.
|
||||
*/
|
||||
CRBNode *c_rbnode_rightmost(CRBNode *n) {
|
||||
if (n)
|
||||
while (n->right)
|
||||
n = n->right;
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* c_rbnode_next() - return next node
|
||||
* @n: current node, or NULL
|
||||
*
|
||||
* An RB-Tree always defines a linear order of its elements. This function
|
||||
* returns the logically next node to @n. If @n is NULL, the last node or
|
||||
* unlinked, this returns NULL.
|
||||
*
|
||||
* Worst case runtime (n: number of elements in tree): O(log(n))
|
||||
*
|
||||
* Return: Pointer to next node, or NULL.
|
||||
*/
|
||||
CRBNode *c_rbnode_next(CRBNode *n) {
|
||||
CRBNode *p;
|
||||
|
||||
if (!c_rbnode_is_linked(n))
|
||||
return NULL;
|
||||
if (n->right)
|
||||
return c_rbnode_leftmost(n->right);
|
||||
|
||||
while ((p = c_rbnode_parent(n)) && n == p->right)
|
||||
n = p;
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
/**
|
||||
* c_rbnode_prev() - return previous node
|
||||
* @n: current node, or NULL
|
||||
*
|
||||
* An RB-Tree always defines a linear order of its elements. This function
|
||||
* returns the logically previous node to @n. If @n is NULL, the first node or
|
||||
* unlinked, this returns NULL.
|
||||
*
|
||||
* Worst case runtime (n: number of elements in tree): O(log(n))
|
||||
*
|
||||
* Return: Pointer to previous node, or NULL.
|
||||
*/
|
||||
CRBNode *c_rbnode_prev(CRBNode *n) {
|
||||
CRBNode *p;
|
||||
|
||||
if (!c_rbnode_is_linked(n))
|
||||
return NULL;
|
||||
if (n->left)
|
||||
return c_rbnode_rightmost(n->left);
|
||||
|
||||
while ((p = c_rbnode_parent(n)) && n == p->left)
|
||||
n = p;
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
/**
|
||||
* c_rbtree_first() - return first node
|
||||
* @t: tree to operate on
|
||||
*
|
||||
* An RB-Tree always defines a linear order of its elements. This function
|
||||
* returns the logically first node in @t. If @t is empty, NULL is returned.
|
||||
*
|
||||
* Fixed runtime (n: number of elements in tree): O(log(n))
|
||||
*
|
||||
* Return: Pointer to first node, or NULL.
|
||||
*/
|
||||
CRBNode *c_rbtree_first(CRBTree *t) {
|
||||
assert(t);
|
||||
return c_rbnode_leftmost(t->root);
|
||||
}
|
||||
|
||||
/**
|
||||
* c_rbtree_last() - return last node
|
||||
* @t: tree to operate on
|
||||
*
|
||||
* An RB-Tree always defines a linear order of its elements. This function
|
||||
* returns the logically last node in @t. If @t is empty, NULL is returned.
|
||||
*
|
||||
* Fixed runtime (n: number of elements in tree): O(log(n))
|
||||
*
|
||||
* Return: Pointer to last node, or NULL.
|
||||
*/
|
||||
CRBNode *c_rbtree_last(CRBTree *t) {
|
||||
assert(t);
|
||||
return c_rbnode_rightmost(t->root);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the color and parent of a node. This should be treated as a simple
|
||||
* assignment of the 'color' and 'parent' fields of the node. No other magic is
|
||||
* applied. But since both fields share its backing memory, this helper
|
||||
* function is provided.
|
||||
*/
|
||||
static inline void c_rbnode_set_parent_and_color(CRBNode *n, CRBNode *p, unsigned long c) {
|
||||
assert(!((unsigned long)p & 1));
|
||||
assert(c < 2);
|
||||
n->__parent_and_color = (CRBNode*)((unsigned long)p | c);
|
||||
}
|
||||
|
||||
/* same as c_rbnode_set_parent_and_color(), but keeps the current color */
|
||||
static inline void c_rbnode_set_parent(CRBNode *n, CRBNode *p) {
|
||||
c_rbnode_set_parent_and_color(n, p, c_rbnode_color(n));
|
||||
}
|
||||
|
||||
/*
|
||||
* This function partially replaces an existing child pointer to a new one. The
|
||||
* existing child must be given as @old, the new child as @new. @p must be the
|
||||
* parent of @old (or NULL if it has no parent).
|
||||
* This function ensures that the parent of @old now points to @new. However,
|
||||
* it does *NOT* change the parent pointer of @new. The caller must ensure
|
||||
* this.
|
||||
* If @p is NULL, this function ensures that the root-pointer is adjusted
|
||||
* instead (given as @t).
|
||||
*/
|
||||
static inline void c_rbtree_swap_child(CRBTree *t, CRBNode *p, CRBNode *old, CRBNode *new) {
|
||||
if (p) {
|
||||
if (p->left == old)
|
||||
p->left = new;
|
||||
else
|
||||
p->right = new;
|
||||
} else {
|
||||
t->root = new;
|
||||
}
|
||||
}
|
||||
|
||||
static inline CRBNode *c_rbtree_paint_one(CRBTree *t, CRBNode *n) {
|
||||
CRBNode *p, *g, *gg, *u, *x;
|
||||
|
||||
/*
|
||||
* Paint a single node according to RB-Tree rules. The node must
|
||||
* already be linked into the tree and painted red.
|
||||
* We repaint the node or rotate the tree, if required. In case a
|
||||
* recursive repaint is required, the next node to be re-painted
|
||||
* is returned.
|
||||
* p: parent
|
||||
* g: grandparent
|
||||
* gg: grandgrandparent
|
||||
* u: uncle
|
||||
* x: temporary
|
||||
*/
|
||||
|
||||
/* node is red, so we can access the parent directly */
|
||||
p = n->__parent_and_color;
|
||||
|
||||
if (!p) {
|
||||
/* Case 1:
|
||||
* We reached the root. Mark it black and be done. As all
|
||||
* leaf-paths share the root, the ratio of black nodes on each
|
||||
* path stays the same. */
|
||||
c_rbnode_set_parent_and_color(n, p, C_RBNODE_BLACK);
|
||||
n = NULL;
|
||||
} else if (c_rbnode_is_black(p)) {
|
||||
/* Case 2:
|
||||
* The parent is already black. As our node is red, we did not
|
||||
* change the number of black nodes on any path, nor do we have
|
||||
* multiple consecutive red nodes. */
|
||||
n = NULL;
|
||||
} else if (p == p->__parent_and_color->left) { /* parent is red, so grandparent exists */
|
||||
g = p->__parent_and_color;
|
||||
gg = c_rbnode_parent(g);
|
||||
u = g->right;
|
||||
|
||||
if (u && c_rbnode_is_red(u)) {
|
||||
/* Case 3:
|
||||
* Parent and uncle are both red. We know the
|
||||
* grandparent must be black then. Repaint parent and
|
||||
* uncle black, the grandparent red and recurse into
|
||||
* the grandparent. */
|
||||
c_rbnode_set_parent_and_color(p, g, C_RBNODE_BLACK);
|
||||
c_rbnode_set_parent_and_color(u, g, C_RBNODE_BLACK);
|
||||
c_rbnode_set_parent_and_color(g, gg, C_RBNODE_RED);
|
||||
n = g;
|
||||
} else {
|
||||
/* parent is red, uncle is black */
|
||||
|
||||
if (n == p->right) {
|
||||
/* Case 4:
|
||||
* We're the right child. Rotate on parent to
|
||||
* become left child, so we can handle it the
|
||||
* same as case 5. */
|
||||
x = n->left;
|
||||
p->right = n->left;
|
||||
n->left = p;
|
||||
if (x)
|
||||
c_rbnode_set_parent_and_color(x, p, C_RBNODE_BLACK);
|
||||
c_rbnode_set_parent_and_color(p, n, C_RBNODE_RED);
|
||||
p = n;
|
||||
}
|
||||
|
||||
/* 'n' is invalid from here on! */
|
||||
n = NULL;
|
||||
|
||||
/* Case 5:
|
||||
* We're the red left child or a red parent, black
|
||||
* grandparent and uncle. Rotate on grandparent and
|
||||
* switch color with parent. Number of black nodes on
|
||||
* each path stays the same, but we got rid of the
|
||||
* double red path. As the grandparent is still black,
|
||||
* we're done. */
|
||||
x = p->right;
|
||||
g->left = x;
|
||||
p->right = g;
|
||||
if (x)
|
||||
c_rbnode_set_parent_and_color(x, g, C_RBNODE_BLACK);
|
||||
c_rbnode_set_parent_and_color(p, gg, C_RBNODE_BLACK);
|
||||
c_rbnode_set_parent_and_color(g, p, C_RBNODE_RED);
|
||||
c_rbtree_swap_child(t, gg, g, p);
|
||||
}
|
||||
} else /* if (p == p->__parent_and_color->left) */ { /* same as above, but mirrored */
|
||||
g = p->__parent_and_color;
|
||||
gg = c_rbnode_parent(g);
|
||||
u = g->left;
|
||||
|
||||
if (u && c_rbnode_is_red(u)) {
|
||||
c_rbnode_set_parent_and_color(p, g, C_RBNODE_BLACK);
|
||||
c_rbnode_set_parent_and_color(u, g, C_RBNODE_BLACK);
|
||||
c_rbnode_set_parent_and_color(g, gg, C_RBNODE_RED);
|
||||
n = g;
|
||||
} else {
|
||||
if (n == p->left) {
|
||||
x = n->right;
|
||||
p->left = n->right;
|
||||
n->right = p;
|
||||
if (x)
|
||||
c_rbnode_set_parent_and_color(x, p, C_RBNODE_BLACK);
|
||||
c_rbnode_set_parent_and_color(p, n, C_RBNODE_RED);
|
||||
p = n;
|
||||
}
|
||||
|
||||
n = NULL;
|
||||
|
||||
x = p->left;
|
||||
g->right = x;
|
||||
p->left = g;
|
||||
if (x)
|
||||
c_rbnode_set_parent_and_color(x, g, C_RBNODE_BLACK);
|
||||
c_rbnode_set_parent_and_color(p, gg, C_RBNODE_BLACK);
|
||||
c_rbnode_set_parent_and_color(g, p, C_RBNODE_RED);
|
||||
c_rbtree_swap_child(t, gg, g, p);
|
||||
}
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
static inline void c_rbtree_paint(CRBTree *t, CRBNode *n) {
|
||||
assert(t);
|
||||
assert(n);
|
||||
|
||||
while (n)
|
||||
n = c_rbtree_paint_one(t, n);
|
||||
}
|
||||
|
||||
/**
|
||||
* c_rbtree_add() - add node to tree
|
||||
* @t: tree to operate one
|
||||
* @p: parent node to link under, or NULL
|
||||
* @l: left/right slot of @p (or root) to link at
|
||||
* @n: node to add
|
||||
*
|
||||
* This links @n into the tree given as @t. The caller must provide the exact
|
||||
* spot where to link the node. That is, the caller must traverse the tree
|
||||
* based on their search order. Once they hit a leaf where to insert the node,
|
||||
* call this function to link it and rebalance the tree.
|
||||
*
|
||||
* A typical insertion would look like this (@t is your tree, @n is your node):
|
||||
*
|
||||
* CRBNode **i, *p;
|
||||
*
|
||||
* i = &t->root;
|
||||
* p = NULL;
|
||||
* while (*i) {
|
||||
* p = *i;
|
||||
* if (compare(n, *i) < 0)
|
||||
* i = &(*i)->left;
|
||||
* else
|
||||
* i = &(*i)->right;
|
||||
* }
|
||||
*
|
||||
* c_rbtree_add(t, p, i, n);
|
||||
*
|
||||
* Once the node is linked into the tree, a simple lookup on the same tree can
|
||||
* be coded like this:
|
||||
*
|
||||
* CRBNode *i;
|
||||
*
|
||||
* i = t->root;
|
||||
* while (i) {
|
||||
* int v = compare(n, i);
|
||||
* if (v < 0)
|
||||
* i = (*i)->left;
|
||||
* else if (v > 0)
|
||||
* i = (*i)->right;
|
||||
* else
|
||||
* break;
|
||||
* }
|
||||
*
|
||||
* When you add nodes to a tree, the memory contents of the node do not matter.
|
||||
* That is, there is no need to initialize the node via c_rbnode_init().
|
||||
* However, if you relink nodes multiple times during their lifetime, it is
|
||||
* usually very convenient to use c_rbnode_init() and c_rbtree_remove_init().
|
||||
* In those cases, you should validate that a node is unlinked before you call
|
||||
* c_rbtree_add().
|
||||
*/
|
||||
void c_rbtree_add(CRBTree *t, CRBNode *p, CRBNode **l, CRBNode *n) {
|
||||
assert(t);
|
||||
assert(l);
|
||||
assert(n);
|
||||
assert(!p || l == &p->left || l == &p->right);
|
||||
assert(p || l == &t->root);
|
||||
|
||||
c_rbnode_set_parent_and_color(n, p, C_RBNODE_RED);
|
||||
n->left = n->right = NULL;
|
||||
*l = n;
|
||||
|
||||
c_rbtree_paint(t, n);
|
||||
}
|
||||
|
||||
static inline CRBNode *c_rbtree_rebalance_one(CRBTree *t, CRBNode *p, CRBNode *n) {
|
||||
CRBNode *s, *x, *y, *g;
|
||||
|
||||
/*
|
||||
* Rebalance tree after a node was removed. This happens only if you
|
||||
* remove a black node and one path is now left with an unbalanced
|
||||
* number or black nodes.
|
||||
* This function assumes all paths through p and n have one black node
|
||||
* less than all other paths. If recursive fixup is required, the
|
||||
* current node is returned.
|
||||
*/
|
||||
|
||||
if (n == p->left) {
|
||||
s = p->right;
|
||||
if (c_rbnode_is_red(s)) {
|
||||
/* Case 3:
|
||||
* We have a red node as sibling. Rotate it onto our
|
||||
* side so we can later on turn it black. This way, we
|
||||
* gain the additional black node in our path. */
|
||||
g = c_rbnode_parent(p);
|
||||
x = s->left;
|
||||
p->right = x;
|
||||
s->left = p;
|
||||
c_rbnode_set_parent_and_color(x, p, C_RBNODE_BLACK);
|
||||
c_rbnode_set_parent_and_color(s, g, c_rbnode_color(p));
|
||||
c_rbnode_set_parent_and_color(p, s, C_RBNODE_RED);
|
||||
c_rbtree_swap_child(t, g, p, s);
|
||||
s = x;
|
||||
}
|
||||
|
||||
x = s->right;
|
||||
if (!x || c_rbnode_is_black(x)) {
|
||||
y = s->left;
|
||||
if (!y || c_rbnode_is_black(y)) {
|
||||
/* Case 4:
|
||||
* Our sibling is black and has only black
|
||||
* children. Flip it red and turn parent black.
|
||||
* This way we gained a black node in our path,
|
||||
* or we fix it recursively one layer up, which
|
||||
* will rotate the red sibling as parent. */
|
||||
c_rbnode_set_parent_and_color(s, p, C_RBNODE_RED);
|
||||
if (c_rbnode_is_black(p))
|
||||
return p;
|
||||
|
||||
c_rbnode_set_parent_and_color(p, c_rbnode_parent(p), C_RBNODE_BLACK);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Case 5:
|
||||
* Left child of our sibling is red, right one is black.
|
||||
* Rotate on parent so the right child of our sibling is
|
||||
* now red, and we can fall through to case 6. */
|
||||
x = y->right;
|
||||
s->left = y->right;
|
||||
y->right = s;
|
||||
p->right = y;
|
||||
if (x)
|
||||
c_rbnode_set_parent_and_color(x, s, C_RBNODE_BLACK);
|
||||
x = s;
|
||||
s = y;
|
||||
}
|
||||
|
||||
/* Case 6:
|
||||
* The right child of our sibling is red. Rotate left and flip
|
||||
* colors, which gains us an additional black node in our path,
|
||||
* that was previously on our sibling. */
|
||||
g = c_rbnode_parent(p);
|
||||
y = s->left;
|
||||
p->right = y;
|
||||
s->left = p;
|
||||
c_rbnode_set_parent_and_color(x, s, C_RBNODE_BLACK);
|
||||
if (y)
|
||||
c_rbnode_set_parent_and_color(y, p, c_rbnode_color(y));
|
||||
c_rbnode_set_parent_and_color(s, g, c_rbnode_color(p));
|
||||
c_rbnode_set_parent_and_color(p, s, C_RBNODE_BLACK);
|
||||
c_rbtree_swap_child(t, g, p, s);
|
||||
} else /* if (!n || n == p->right) */ { /* same as above, but mirrored */
|
||||
s = p->left;
|
||||
if (c_rbnode_is_red(s)) {
|
||||
g = c_rbnode_parent(p);
|
||||
x = s->right;
|
||||
p->left = x;
|
||||
s->right = p;
|
||||
c_rbnode_set_parent_and_color(x, p, C_RBNODE_BLACK);
|
||||
c_rbnode_set_parent_and_color(s, g, C_RBNODE_BLACK);
|
||||
c_rbnode_set_parent_and_color(p, s, C_RBNODE_RED);
|
||||
c_rbtree_swap_child(t, g, p, s);
|
||||
s = x;
|
||||
}
|
||||
|
||||
x = s->left;
|
||||
if (!x || c_rbnode_is_black(x)) {
|
||||
y = s->right;
|
||||
if (!y || c_rbnode_is_black(y)) {
|
||||
c_rbnode_set_parent_and_color(s, p, C_RBNODE_RED);
|
||||
if (c_rbnode_is_black(p))
|
||||
return p;
|
||||
|
||||
c_rbnode_set_parent_and_color(p, c_rbnode_parent(p), C_RBNODE_BLACK);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
x = y->left;
|
||||
s->right = y->left;
|
||||
y->left = s;
|
||||
p->left = y;
|
||||
if (x)
|
||||
c_rbnode_set_parent_and_color(x, s, C_RBNODE_BLACK);
|
||||
x = s;
|
||||
s = y;
|
||||
}
|
||||
|
||||
g = c_rbnode_parent(p);
|
||||
y = s->right;
|
||||
p->left = y;
|
||||
s->right = p;
|
||||
c_rbnode_set_parent_and_color(x, s, C_RBNODE_BLACK);
|
||||
if (y)
|
||||
c_rbnode_set_parent_and_color(y, p, c_rbnode_color(y));
|
||||
c_rbnode_set_parent_and_color(s, g, c_rbnode_color(p));
|
||||
c_rbnode_set_parent_and_color(p, s, C_RBNODE_BLACK);
|
||||
c_rbtree_swap_child(t, g, p, s);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline void c_rbtree_rebalance(CRBTree *t, CRBNode *p) {
|
||||
CRBNode *n = NULL;
|
||||
|
||||
assert(t);
|
||||
assert(p);
|
||||
|
||||
do {
|
||||
n = c_rbtree_rebalance_one(t, p, n);
|
||||
p = n ? c_rbnode_parent(n) : NULL;
|
||||
} while (p);
|
||||
}
|
||||
|
||||
/**
|
||||
* c_rbtree_remove() - remove node from tree
|
||||
* @t: tree to operate one
|
||||
* @n: node to remove
|
||||
*
|
||||
* This removes the given node from its tree. Once unlinked, the tree is
|
||||
* rebalanced.
|
||||
* The caller *must* ensure that the given tree is actually the tree it is
|
||||
* linked on. Otherwise, behavior is undefined.
|
||||
*
|
||||
* This does *NOT* reset @n to being unlinked (for performance reason, this
|
||||
* function *never* modifies @n at all). If you need this, use
|
||||
* c_rbtree_remove_init().
|
||||
*/
|
||||
void c_rbtree_remove(CRBTree *t, CRBNode *n) {
|
||||
CRBNode *p, *s, *gc, *x, *next = NULL;
|
||||
unsigned long c;
|
||||
|
||||
assert(t);
|
||||
assert(n);
|
||||
assert(c_rbnode_is_linked(n));
|
||||
|
||||
/*
|
||||
* There are three distinct cases during node removal of a tree:
|
||||
* * The node has no children, in which case it can simply be removed.
|
||||
* * The node has exactly one child, in which case the child displaces
|
||||
* its parent.
|
||||
* * The node has two children, in which case there is guaranteed to
|
||||
* be a successor to the node (successor being the node ordered
|
||||
* directly after it). This successor cannot have two children by
|
||||
* itself (two interior nodes can never be successive). Therefore,
|
||||
* we can simply swap the node with its successor (including color)
|
||||
* and have reduced this case to either of the first two.
|
||||
*
|
||||
* Whenever the node we removed was black, we have to rebalance the
|
||||
* tree. Note that this affects the actual node we _remove_, not @n (in
|
||||
* case we swap it).
|
||||
*
|
||||
* p: parent
|
||||
* s: successor
|
||||
* gc: grand-...-child
|
||||
* x: temporary
|
||||
* next: next node to rebalance on
|
||||
*/
|
||||
|
||||
if (!n->left) {
|
||||
/*
|
||||
* Case 1:
|
||||
* The node has no left child. If it neither has a right child,
|
||||
* it is a leaf-node and we can simply unlink it. If it also
|
||||
* was black, we have to rebalance, as always if we remove a
|
||||
* black node.
|
||||
* But if the node has a right child, the child *must* be red
|
||||
* (otherwise, the right path has more black nodes as the
|
||||
* non-existing left path), and the node to be removed must
|
||||
* hence be black. We simply replace the node with its child,
|
||||
* turning the red child black, and thus no rebalancing is
|
||||
* required.
|
||||
*/
|
||||
p = c_rbnode_parent(n);
|
||||
c = c_rbnode_color(n);
|
||||
c_rbtree_swap_child(t, p, n, n->right);
|
||||
if (n->right)
|
||||
c_rbnode_set_parent_and_color(n->right, p, c);
|
||||
else
|
||||
next = (c == C_RBNODE_BLACK) ? p : NULL;
|
||||
} else if (!n->right) {
|
||||
/*
|
||||
* Case 1.1:
|
||||
* The node has exactly one child, and it is on the left. Treat
|
||||
* it as mirrored case of Case 1 (i.e., replace the node by its
|
||||
* child).
|
||||
*/
|
||||
p = c_rbnode_parent(n);
|
||||
c = c_rbnode_color(n);
|
||||
c_rbtree_swap_child(t, p, n, n->left);
|
||||
c_rbnode_set_parent_and_color(n->left, p, c);
|
||||
} else {
|
||||
/*
|
||||
* Case 2:
|
||||
* We are dealing with a full interior node with a child not on
|
||||
* both sides. Find its successor and swap it. Then remove the
|
||||
* node similar to Case 1. For performance reasons we don't
|
||||
* perform the full swap, but skip links that are about to be
|
||||
* removed, anyway.
|
||||
*/
|
||||
s = n->right;
|
||||
if (!s->left) {
|
||||
/* right child is next, no need to touch grandchild */
|
||||
p = s;
|
||||
gc = s->right;
|
||||
} else {
|
||||
/* find successor and swap partially */
|
||||
s = c_rbnode_leftmost(s);
|
||||
p = c_rbnode_parent(s);
|
||||
|
||||
gc = s->right;
|
||||
p->left = s->right;
|
||||
s->right = n->right;
|
||||
c_rbnode_set_parent(n->right, s);
|
||||
}
|
||||
|
||||
/* node is partially swapped, now remove as in Case 1 */
|
||||
s->left = n->left;
|
||||
c_rbnode_set_parent(n->left, s);
|
||||
|
||||
x = c_rbnode_parent(n);
|
||||
c = c_rbnode_color(n);
|
||||
c_rbtree_swap_child(t, x, n, s);
|
||||
if (gc)
|
||||
c_rbnode_set_parent_and_color(gc, p, C_RBNODE_BLACK);
|
||||
else
|
||||
next = c_rbnode_is_black(s) ? p : NULL;
|
||||
c_rbnode_set_parent_and_color(s, x, c);
|
||||
}
|
||||
|
||||
if (next)
|
||||
c_rbtree_rebalance(t, next);
|
||||
}
|
@ -1,297 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
/***
|
||||
This file is part of systemd. See COPYING for details.
|
||||
|
||||
systemd is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
systemd is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
/*
|
||||
* Standalone Red-Black-Tree Implementation in Standard ISO-C11
|
||||
*
|
||||
* This header provides an RB-Tree API, that is fully implemented in ISO-C11
|
||||
* and has no external dependencies. Furthermore, tree traversal, memory
|
||||
* allocations, and key comparisons a fully in control of the API user. The
|
||||
* implementation only provides the RB-Tree specific rebalancing and coloring.
|
||||
*
|
||||
* A tree is represented by the "CRBTree" structure. It contains a *singly*
|
||||
* field, which is a pointer to the root node. If NULL, the tree is empty. If
|
||||
* non-NULL, there is at least a single element in the tree.
|
||||
*
|
||||
* Each node of the tree is represented by the "CRBNode" structure. It has
|
||||
* three fields. The @left and @right members can be accessed by the API user
|
||||
* directly to traverse the tree. The third member is an implementation detail
|
||||
* and encodes the parent pointer and color of the node.
|
||||
* API users are required to embed the CRBNode object into their own objects
|
||||
* and then use offsetof() (i.e., container_of() and friends) to turn CRBNode
|
||||
* pointers into pointers to their own structure.
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct CRBNode CRBNode;
|
||||
typedef struct CRBTree CRBTree;
|
||||
|
||||
/**
|
||||
* struct CRBNode - Node of a Red-Black Tree
|
||||
* @__parent_and_color: internal state
|
||||
* @left: left child, or NULL
|
||||
* @right: right child, or NULL
|
||||
*
|
||||
* Each node in an RB-Tree must embed an CRBNode object. This object contains
|
||||
* pointers to its left and right child, which can be freely accessed by the
|
||||
* API user at any time. They are NULL, if the node does not have a left/right
|
||||
* child.
|
||||
*
|
||||
* The @__parent_and_color field must never be accessed directly. It encodes
|
||||
* the pointer to the parent node, and the color of the node. Use the accessor
|
||||
* functions instead.
|
||||
*
|
||||
* There is no reason to initialize a CRBNode object before linking it.
|
||||
* However, if you need a boolean state that tells you whether the node is
|
||||
* linked or not, you should initialize the node via c_rbnode_init() or
|
||||
* C_RBNODE_INIT.
|
||||
*/
|
||||
struct CRBNode {
|
||||
CRBNode *__parent_and_color;
|
||||
CRBNode *left;
|
||||
CRBNode *right;
|
||||
};
|
||||
|
||||
#define C_RBNODE_INIT(_var) { .__parent_and_color = &(_var) }
|
||||
|
||||
CRBNode *c_rbnode_leftmost(CRBNode *n);
|
||||
CRBNode *c_rbnode_rightmost(CRBNode *n);
|
||||
CRBNode *c_rbnode_next(CRBNode *n);
|
||||
CRBNode *c_rbnode_prev(CRBNode *n);
|
||||
|
||||
/**
|
||||
* struct CRBTree - Red-Black Tree
|
||||
* @root: pointer to the root node, or NULL
|
||||
*
|
||||
* Each Red-Black Tree is rooted in an CRBTree object. This object contains a
|
||||
* pointer to the root node of the tree. The API user is free to access the
|
||||
* @root member at any time, and use it to traverse the tree.
|
||||
*
|
||||
* To initialize an RB-Tree, set it to NULL / all zero.
|
||||
*/
|
||||
struct CRBTree {
|
||||
CRBNode *root;
|
||||
};
|
||||
|
||||
CRBNode *c_rbtree_first(CRBTree *t);
|
||||
CRBNode *c_rbtree_last(CRBTree *t);
|
||||
|
||||
void c_rbtree_add(CRBTree *t, CRBNode *p, CRBNode **l, CRBNode *n);
|
||||
void c_rbtree_remove(CRBTree *t, CRBNode *n);
|
||||
|
||||
/**
|
||||
* c_rbnode_init() - mark a node as unlinked
|
||||
* @n: node to operate on
|
||||
*
|
||||
* This marks the node @n as unlinked. The node will be set to a valid state
|
||||
* that can never happen if the node is linked in a tree. Furthermore, this
|
||||
* state is fully known to the implementation, and as such handled gracefully
|
||||
* in all cases.
|
||||
*
|
||||
* You are *NOT* required to call this on your node. c_rbtree_add() can handle
|
||||
* uninitialized nodes just fine. However, calling this allows to use
|
||||
* c_rbnode_is_linked() to check for the state of a node. Furthermore,
|
||||
* iterators and accessors can be called on initialized (yet unlinked) nodes.
|
||||
*
|
||||
* Use the C_RBNODE_INIT macro if you want to initialize static variables.
|
||||
*/
|
||||
static inline void c_rbnode_init(CRBNode *n) {
|
||||
*n = (CRBNode)C_RBNODE_INIT(*n);
|
||||
}
|
||||
|
||||
/**
|
||||
* c_rbnode_is_linked() - check whether a node is linked
|
||||
* @n: node to check, or NULL
|
||||
*
|
||||
* This checks whether the passed node is linked. If you pass NULL, or if the
|
||||
* node is not linked into a tree, this will return false. Otherwise, this
|
||||
* returns true.
|
||||
*
|
||||
* Note that you must have either linked the node or initialized it, before
|
||||
* calling this function. Never call this function on uninitialized nodes.
|
||||
* Furthermore, removing a node via c_rbtree_remove() does *NOT* mark the node
|
||||
* as unlinked. You have to call c_rbnode_init() yourself after removal, or use
|
||||
* the c_rbtree_remove_init() helper.
|
||||
*
|
||||
* Return: true if the node is linked, false if not.
|
||||
*/
|
||||
static inline _Bool c_rbnode_is_linked(CRBNode *n) {
|
||||
return n && n->__parent_and_color != n;
|
||||
}
|
||||
|
||||
/**
|
||||
* c_rbnode_parent() - return parent pointer
|
||||
* @n node to access
|
||||
*
|
||||
* This returns a pointer to the parent of the given node @n. If @n does not
|
||||
* have a parent, NULL is returned. If @n is not linked, @n itself is returned.
|
||||
*
|
||||
* You should not call this on unlinked or uninitialized nodes! If you do, you
|
||||
* better know how its semantics.
|
||||
*
|
||||
* Return: Pointer to parent.
|
||||
*/
|
||||
static inline CRBNode *c_rbnode_parent(CRBNode *n) {
|
||||
return (CRBNode*)((unsigned long)n->__parent_and_color & ~1UL);
|
||||
}
|
||||
|
||||
/**
|
||||
* c_rbtree_remove_init() - safely remove node from tree and reinitialize it
|
||||
* @t: tree to operate on
|
||||
* @n: node to remove, or NULL
|
||||
*
|
||||
* This is almost the same as c_rbtree_remove(), but extends it slightly, to be
|
||||
* more convenient to use in many cases:
|
||||
* - if @n is unlinked or NULL, this is a no-op
|
||||
* - @n is reinitialized after being removed
|
||||
*/
|
||||
static inline void c_rbtree_remove_init(CRBTree *t, CRBNode *n) {
|
||||
if (c_rbnode_is_linked(n)) {
|
||||
c_rbtree_remove(t, n);
|
||||
c_rbnode_init(n);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* CRBCompareFunc - compare a node to a key
|
||||
* @t: tree where the node is linked to
|
||||
* @k: key to compare
|
||||
* @n: node to compare
|
||||
*
|
||||
* If you use the tree-traversal helpers (which are optional), you need to
|
||||
* provide this callback so they can compare nodes in a tree to the key you
|
||||
* look for.
|
||||
*
|
||||
* The tree @t is provided as optional context to this callback. The key you
|
||||
* look for is provided as @k, the current node that should be compared to is
|
||||
* provided as @n. This function should work like strcmp(), that is, return -1
|
||||
* if @key orders before @n, 0 if both compare equal, and 1 if it orders after
|
||||
* @n.
|
||||
*/
|
||||
typedef int (*CRBCompareFunc) (CRBTree *t, void *k, CRBNode *n);
|
||||
|
||||
/**
|
||||
* c_rbtree_find_node() - find node
|
||||
* @t: tree to search through
|
||||
* @f: comparison function
|
||||
* @k: key to search for
|
||||
*
|
||||
* This searches through @t for a node that compares equal to @k. The function
|
||||
* @f must be provided by the caller, which is used to compare nodes to @k. See
|
||||
* the documentation of CRBCompareFunc for details.
|
||||
*
|
||||
* If there are multiple entries that compare equal to @k, this will return a
|
||||
* pseudo-randomly picked node. If you need stable lookup functions for trees
|
||||
* where duplicate entries are allowed, you better code your own lookup.
|
||||
*
|
||||
* Return: Pointer to matching node, or NULL.
|
||||
*/
|
||||
static inline CRBNode *c_rbtree_find_node(CRBTree *t, CRBCompareFunc f, const void *k) {
|
||||
CRBNode *i;
|
||||
|
||||
assert(t);
|
||||
assert(f);
|
||||
|
||||
i = t->root;
|
||||
while (i) {
|
||||
int v = f(t, (void *)k, i);
|
||||
if (v < 0)
|
||||
i = i->left;
|
||||
else if (v > 0)
|
||||
i = i->right;
|
||||
else
|
||||
return i;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* c_rbtree_find_entry() - find entry
|
||||
* @_t: tree to search through
|
||||
* @_f: comparison function
|
||||
* @_k: key to search for
|
||||
* @_t: type of the structure that embeds the nodes
|
||||
* @_o: name of the node-member in type @_t
|
||||
*
|
||||
* This is very similar to c_rbtree_find_node(), but instead of returning a
|
||||
* pointer to the CRBNode, it returns a pointer to the surrounding object. This
|
||||
* object must embed the CRBNode object. The type of the surrounding object
|
||||
* must be given as @_t, and the name of the embedded CRBNode member as @_o.
|
||||
*
|
||||
* See c_rbtree_find_node() for more details.
|
||||
*
|
||||
* Return: Pointer to found entry, NULL if not found.
|
||||
*/
|
||||
#define c_rbtree_find_entry(_m, _f, _k, _t, _o) \
|
||||
((_t *)(((char *)c_rbtree_find_node((_m), (_f), (_k)) ?: \
|
||||
(char *)NULL + offsetof(_t, _o)) - offsetof(_t, _o)))
|
||||
|
||||
/**
|
||||
* c_rbtree_find_slot() - find slot to insert new node
|
||||
* @t: tree to search through
|
||||
* @f: comparison function
|
||||
* @k: key to search for
|
||||
* @p: output storage for parent pointer
|
||||
*
|
||||
* This searches through @t just like c_rbtree_find_node() does. However,
|
||||
* instead of returning a pointer to a node that compares equal to @k, this
|
||||
* searches for a slot to insert a node with key @k. A pointer to the slot is
|
||||
* returned, and a pointer to the parent of the slot is stored in @p. Both
|
||||
* can be passed directly to c_rbtree_add(), together with your node to insert.
|
||||
*
|
||||
* If there already is a node in the tree, that compares equal to @k, this will
|
||||
* return NULL and store the conflicting node in @p. In all other cases,
|
||||
* this will return a pointer (non-NULL) to the empty slot to insert the node
|
||||
* at. @p will point to the parent node of that slot.
|
||||
*
|
||||
* If you want trees that allow duplicate nodes, you better code your own
|
||||
* insertion function.
|
||||
*
|
||||
* Return: Pointer to slot to insert node, or NULL on conflicts.
|
||||
*/
|
||||
static inline CRBNode **c_rbtree_find_slot(CRBTree *t, CRBCompareFunc f, const void *k, CRBNode **p) {
|
||||
CRBNode **i;
|
||||
|
||||
assert(t);
|
||||
assert(f);
|
||||
assert(p);
|
||||
|
||||
i = &t->root;
|
||||
*p = NULL;
|
||||
while (*i) {
|
||||
int v = f(t, (void *)k, *i);
|
||||
*p = *i;
|
||||
if (v < 0)
|
||||
i = &(*i)->left;
|
||||
else if (v > 0)
|
||||
i = &(*i)->right;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -41,8 +41,6 @@
|
||||
#define SIGNALS_CRASH_HANDLER SIGSEGV,SIGILL,SIGFPE,SIGBUS,SIGQUIT,SIGABRT
|
||||
#define SIGNALS_IGNORE SIGPIPE
|
||||
|
||||
#define REBOOT_PARAM_FILE "/run/systemd/reboot-param"
|
||||
|
||||
#ifdef HAVE_SPLIT_USR
|
||||
#define KBD_KEYMAP_DIRS \
|
||||
"/usr/share/keymaps/\0" \
|
||||
|
@ -30,3 +30,12 @@ typedef enum RemoveFlags {
|
||||
|
||||
int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev);
|
||||
int rm_rf(const char *path, RemoveFlags flags);
|
||||
|
||||
/* Useful for usage with _cleanup_(), destroys a directory and frees the pointer */
|
||||
static inline void rm_rf_and_free(char *p) {
|
||||
if (!p)
|
||||
return;
|
||||
(void) rm_rf(p, REMOVE_ROOT);
|
||||
free(p);
|
||||
}
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(char*, rm_rf_and_free);
|
||||
|
@ -255,7 +255,7 @@ int signal_from_string(const char *s) {
|
||||
}
|
||||
if (safe_atou(s, &u) >= 0) {
|
||||
signo = (int) u + offset;
|
||||
if (signo > 0 && signo < _NSIG)
|
||||
if (SIGNAL_VALID(signo))
|
||||
return signo;
|
||||
}
|
||||
return -EINVAL;
|
||||
|
@ -50,3 +50,7 @@ static inline void block_signals_reset(sigset_t *ss) {
|
||||
assert_se(sigprocmask_many(SIG_BLOCK, &t, __VA_ARGS__, -1) >= 0); \
|
||||
t; \
|
||||
})
|
||||
|
||||
static inline bool SIGNAL_VALID(int signo) {
|
||||
return signo > 0 && signo < _NSIG;
|
||||
}
|
||||
|
@ -558,6 +558,42 @@ int strv_extend(char ***l, const char *value) {
|
||||
return strv_consume(l, v);
|
||||
}
|
||||
|
||||
int strv_extend_front(char ***l, const char *value) {
|
||||
size_t n, m;
|
||||
char *v, **c;
|
||||
|
||||
assert(l);
|
||||
|
||||
/* Like strv_extend(), but prepends rather than appends the new entry */
|
||||
|
||||
if (!value)
|
||||
return 0;
|
||||
|
||||
n = strv_length(*l);
|
||||
|
||||
/* Increase and overflow check. */
|
||||
m = n + 2;
|
||||
if (m < n)
|
||||
return -ENOMEM;
|
||||
|
||||
v = strdup(value);
|
||||
if (!v)
|
||||
return -ENOMEM;
|
||||
|
||||
c = realloc_multiply(*l, sizeof(char*), m);
|
||||
if (!c) {
|
||||
free(v);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
memmove(c+1, c, n * sizeof(char*));
|
||||
c[0] = v;
|
||||
c[n+1] = NULL;
|
||||
|
||||
*l = c;
|
||||
return 0;
|
||||
}
|
||||
|
||||
char **strv_uniq(char **l) {
|
||||
char **i;
|
||||
|
||||
|
@ -50,6 +50,7 @@ int strv_extend_strv(char ***a, char **b, bool filter_duplicates);
|
||||
int strv_extend_strv_concat(char ***a, char **b, const char *suffix);
|
||||
int strv_extend(char ***l, const char *value);
|
||||
int strv_extendf(char ***l, const char *format, ...) _printf_(2,0);
|
||||
int strv_extend_front(char ***l, const char *value);
|
||||
int strv_push(char ***l, char *value);
|
||||
int strv_push_pair(char ***l, char *a, char *b);
|
||||
int strv_push_prepend(char ***l, char *value);
|
||||
|
@ -55,6 +55,7 @@
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
#include "time-util.h"
|
||||
#include "umask-util.h"
|
||||
#include "user-util.h"
|
||||
#include "util.h"
|
||||
|
||||
@ -777,15 +778,24 @@ uint64_t physical_memory(void) {
|
||||
return (uint64_t) mem * (uint64_t) page_size();
|
||||
}
|
||||
|
||||
int update_reboot_param_file(const char *param) {
|
||||
int r = 0;
|
||||
int update_reboot_parameter_and_warn(const char *param) {
|
||||
int r;
|
||||
|
||||
if (param) {
|
||||
r = write_string_file(REBOOT_PARAM_FILE, param, WRITE_STRING_FILE_CREATE);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to write reboot param to "REBOOT_PARAM_FILE": %m");
|
||||
} else
|
||||
(void) unlink(REBOOT_PARAM_FILE);
|
||||
if (isempty(param)) {
|
||||
if (unlink("/run/systemd/reboot-param") < 0) {
|
||||
if (errno == ENOENT)
|
||||
return 0;
|
||||
|
||||
return log_warning_errno(errno, "Failed to unlink reboot parameter file: %m");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
RUN_WITH_UMASK(0022)
|
||||
r = write_string_file("/run/systemd/reboot-param", param, WRITE_STRING_FILE_CREATE);
|
||||
if (r < 0)
|
||||
return log_warning_errno(r, "Failed to write reboot parameter file: %m");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -184,6 +184,6 @@ int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int
|
||||
|
||||
uint64_t physical_memory(void);
|
||||
|
||||
int update_reboot_param_file(const char *param);
|
||||
int update_reboot_parameter_and_warn(const char *param);
|
||||
|
||||
int version(void);
|
||||
|
@ -149,7 +149,7 @@ static int automount_add_default_dependencies(Automount *a) {
|
||||
if (!UNIT(a)->default_dependencies)
|
||||
return 0;
|
||||
|
||||
if (UNIT(a)->manager->running_as != MANAGER_SYSTEM)
|
||||
if (!MANAGER_IS_SYSTEM(UNIT(a)->manager))
|
||||
return 0;
|
||||
|
||||
r = unit_add_two_dependencies_by_name(UNIT(a), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true);
|
||||
|
@ -149,7 +149,7 @@ static int busname_add_default_default_dependencies(BusName *n) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (UNIT(n)->manager->running_as == MANAGER_SYSTEM) {
|
||||
if (MANAGER_IS_SYSTEM(UNIT(n)->manager)) {
|
||||
r = unit_add_two_dependencies_by_name(UNIT(n), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@ -318,7 +318,7 @@ static int busname_open_fd(BusName *n) {
|
||||
if (n->starter_fd >= 0)
|
||||
return 0;
|
||||
|
||||
mode = UNIT(n)->manager->running_as == MANAGER_SYSTEM ? "system" : "user";
|
||||
mode = MANAGER_IS_SYSTEM(UNIT(n)->manager) ? "system" : "user";
|
||||
n->starter_fd = bus_kernel_open_bus_fd(mode, &path);
|
||||
if (n->starter_fd < 0)
|
||||
return log_unit_warning_errno(UNIT(n), n->starter_fd, "Failed to open %s: %m", path ?: "kdbus");
|
||||
|
@ -1265,7 +1265,7 @@ int manager_setup_cgroup(Manager *m) {
|
||||
* it. This is to support live upgrades from older systemd
|
||||
* versions where PID 1 was moved there. Also see
|
||||
* cg_get_root_path(). */
|
||||
if (!e && m->running_as == MANAGER_SYSTEM) {
|
||||
if (!e && MANAGER_IS_SYSTEM(m)) {
|
||||
e = endswith(m->cgroup_root, "/" SPECIAL_SYSTEM_SLICE);
|
||||
if (!e)
|
||||
e = endswith(m->cgroup_root, "/system"); /* even more legacy */
|
||||
@ -1318,7 +1318,7 @@ int manager_setup_cgroup(Manager *m) {
|
||||
|
||||
(void) sd_event_source_set_description(m->cgroup_inotify_event_source, "cgroup-inotify");
|
||||
|
||||
} else if (m->running_as == MANAGER_SYSTEM) {
|
||||
} else if (MANAGER_IS_SYSTEM(m)) {
|
||||
|
||||
/* On the legacy hierarchy we only get
|
||||
* notifications via cgroup agents. (Which
|
||||
|
@ -837,9 +837,9 @@ int bus_exec_context_set_transient_property(
|
||||
|
||||
if (mode != UNIT_CHECK) {
|
||||
|
||||
if (isempty(uu)) {
|
||||
if (isempty(uu))
|
||||
c->user = mfree(c->user);
|
||||
} else {
|
||||
else {
|
||||
char *t;
|
||||
|
||||
t = strdup(uu);
|
||||
@ -864,9 +864,9 @@ int bus_exec_context_set_transient_property(
|
||||
|
||||
if (mode != UNIT_CHECK) {
|
||||
|
||||
if (isempty(gg)) {
|
||||
if (isempty(gg))
|
||||
c->group = mfree(c->group);
|
||||
} else {
|
||||
else {
|
||||
char *t;
|
||||
|
||||
t = strdup(gg);
|
||||
|
@ -58,7 +58,7 @@ int bus_kill_context_set_transient_property(
|
||||
|
||||
k = kill_mode_from_string(m);
|
||||
if (k < 0)
|
||||
return -EINVAL;
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Kill mode '%s' not known.", m);
|
||||
|
||||
if (mode != UNIT_CHECK) {
|
||||
c->kill_mode = k;
|
||||
@ -75,7 +75,7 @@ int bus_kill_context_set_transient_property(
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (sig <= 0 || sig >= _NSIG)
|
||||
if (!SIGNAL_VALID(sig))
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Signal %i out of range", sig);
|
||||
|
||||
if (mode != UNIT_CHECK) {
|
||||
|
@ -1187,7 +1187,7 @@ static int method_reboot(sd_bus_message *message, void *userdata, sd_bus_error *
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (m->running_as != MANAGER_SYSTEM)
|
||||
if (!MANAGER_IS_SYSTEM(m))
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Reboot is only supported for system managers.");
|
||||
|
||||
m->exit_code = MANAGER_REBOOT;
|
||||
@ -1206,7 +1206,7 @@ static int method_poweroff(sd_bus_message *message, void *userdata, sd_bus_error
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (m->running_as != MANAGER_SYSTEM)
|
||||
if (!MANAGER_IS_SYSTEM(m))
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Powering off is only supported for system managers.");
|
||||
|
||||
m->exit_code = MANAGER_POWEROFF;
|
||||
@ -1225,7 +1225,7 @@ static int method_halt(sd_bus_message *message, void *userdata, sd_bus_error *er
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (m->running_as != MANAGER_SYSTEM)
|
||||
if (!MANAGER_IS_SYSTEM(m))
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Halt is only supported for system managers.");
|
||||
|
||||
m->exit_code = MANAGER_HALT;
|
||||
@ -1244,7 +1244,7 @@ static int method_kexec(sd_bus_message *message, void *userdata, sd_bus_error *e
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (m->running_as != MANAGER_SYSTEM)
|
||||
if (!MANAGER_IS_SYSTEM(m))
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "KExec is only supported for system managers.");
|
||||
|
||||
m->exit_code = MANAGER_KEXEC;
|
||||
@ -1265,7 +1265,7 @@ static int method_switch_root(sd_bus_message *message, void *userdata, sd_bus_er
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (m->running_as != MANAGER_SYSTEM)
|
||||
if (!MANAGER_IS_SYSTEM(m))
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Root switching is only supported by system manager.");
|
||||
|
||||
r = sd_bus_message_read(message, "ss", &root, &init);
|
||||
@ -1433,7 +1433,7 @@ static int method_set_exit_code(sd_bus_message *message, void *userdata, sd_bus_
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (m->running_as == MANAGER_SYSTEM && detect_container() <= 0)
|
||||
if (MANAGER_IS_SYSTEM(m) && detect_container() <= 0)
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "ExitCode can only be set for user service managers or in containers.");
|
||||
|
||||
m->return_value = code;
|
||||
@ -1466,7 +1466,7 @@ static int method_list_unit_files(sd_bus_message *message, void *userdata, sd_bu
|
||||
if (!h)
|
||||
return -ENOMEM;
|
||||
|
||||
r = unit_file_get_list(m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER, NULL, h);
|
||||
r = unit_file_get_list(m->unit_file_scope, NULL, h);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
@ -1498,7 +1498,6 @@ static int method_get_unit_file_state(sd_bus_message *message, void *userdata, s
|
||||
Manager *m = userdata;
|
||||
const char *name;
|
||||
UnitFileState state;
|
||||
UnitFileScope scope;
|
||||
int r;
|
||||
|
||||
assert(message);
|
||||
@ -1514,9 +1513,7 @@ static int method_get_unit_file_state(sd_bus_message *message, void *userdata, s
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
|
||||
|
||||
r = unit_file_get_state(scope, NULL, name, &state);
|
||||
r = unit_file_get_state(m->unit_file_scope, NULL, name, &state);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -1526,7 +1523,6 @@ static int method_get_unit_file_state(sd_bus_message *message, void *userdata, s
|
||||
static int method_get_default_target(sd_bus_message *message, void *userdata, sd_bus_error *error) {
|
||||
_cleanup_free_ char *default_target = NULL;
|
||||
Manager *m = userdata;
|
||||
UnitFileScope scope;
|
||||
int r;
|
||||
|
||||
assert(message);
|
||||
@ -1538,9 +1534,7 @@ static int method_get_default_target(sd_bus_message *message, void *userdata, sd
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
|
||||
|
||||
r = unit_file_get_default(scope, NULL, &default_target);
|
||||
r = unit_file_get_default(m->unit_file_scope, NULL, &default_target);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -1613,6 +1607,19 @@ fail:
|
||||
return r;
|
||||
}
|
||||
|
||||
static int install_error(sd_bus_error *error, int c) {
|
||||
assert(c < 0);
|
||||
|
||||
if (c == -ESHUTDOWN)
|
||||
return sd_bus_error_setf(error, BUS_ERROR_UNIT_MASKED, "Unit file is masked.");
|
||||
if (c == -EADDRNOTAVAIL)
|
||||
return sd_bus_error_setf(error, BUS_ERROR_UNIT_GENERATED, "Unit file is transient or generated.");
|
||||
if (c == -ELOOP)
|
||||
return sd_bus_error_setf(error, BUS_ERROR_UNIT_LINKED, "Refusing to operate on linked unit file.");
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
static int method_enable_unit_files_generic(
|
||||
sd_bus_message *message,
|
||||
Manager *m,
|
||||
@ -1624,7 +1631,6 @@ static int method_enable_unit_files_generic(
|
||||
_cleanup_strv_free_ char **l = NULL;
|
||||
UnitFileChange *changes = NULL;
|
||||
unsigned n_changes = 0;
|
||||
UnitFileScope scope;
|
||||
int runtime, force, r;
|
||||
|
||||
assert(message);
|
||||
@ -1644,13 +1650,11 @@ static int method_enable_unit_files_generic(
|
||||
if (r == 0)
|
||||
return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
|
||||
|
||||
scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
|
||||
|
||||
r = call(scope, runtime, NULL, l, force, &changes, &n_changes);
|
||||
if (r == -ESHUTDOWN)
|
||||
return sd_bus_error_setf(error, BUS_ERROR_UNIT_MASKED, "Unit file is masked");
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = call(m->unit_file_scope, runtime, NULL, l, force, &changes, &n_changes);
|
||||
if (r < 0) {
|
||||
unit_file_changes_free(changes, n_changes);
|
||||
return install_error(error, r);
|
||||
}
|
||||
|
||||
return reply_unit_file_changes_and_free(m, message, carries_install_info ? r : -1, changes, n_changes);
|
||||
}
|
||||
@ -1686,7 +1690,6 @@ static int method_preset_unit_files_with_mode(sd_bus_message *message, void *use
|
||||
unsigned n_changes = 0;
|
||||
Manager *m = userdata;
|
||||
UnitFilePresetMode mm;
|
||||
UnitFileScope scope;
|
||||
int runtime, force, r;
|
||||
const char *mode;
|
||||
|
||||
@ -1715,11 +1718,11 @@ static int method_preset_unit_files_with_mode(sd_bus_message *message, void *use
|
||||
if (r == 0)
|
||||
return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
|
||||
|
||||
scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
|
||||
|
||||
r = unit_file_preset(scope, runtime, NULL, l, mm, force, &changes, &n_changes);
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = unit_file_preset(m->unit_file_scope, runtime, NULL, l, mm, force, &changes, &n_changes);
|
||||
if (r < 0) {
|
||||
unit_file_changes_free(changes, n_changes);
|
||||
return install_error(error, r);
|
||||
}
|
||||
|
||||
return reply_unit_file_changes_and_free(m, message, r, changes, n_changes);
|
||||
}
|
||||
@ -1734,7 +1737,6 @@ static int method_disable_unit_files_generic(
|
||||
_cleanup_strv_free_ char **l = NULL;
|
||||
UnitFileChange *changes = NULL;
|
||||
unsigned n_changes = 0;
|
||||
UnitFileScope scope;
|
||||
int r, runtime;
|
||||
|
||||
assert(message);
|
||||
@ -1748,17 +1750,17 @@ static int method_disable_unit_files_generic(
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
|
||||
|
||||
r = bus_verify_manage_unit_files_async(m, message, error);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
|
||||
|
||||
r = call(scope, runtime, NULL, l, &changes, &n_changes);
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = call(m->unit_file_scope, runtime, NULL, l, &changes, &n_changes);
|
||||
if (r < 0) {
|
||||
unit_file_changes_free(changes, n_changes);
|
||||
return install_error(error, r);
|
||||
}
|
||||
|
||||
return reply_unit_file_changes_and_free(m, message, -1, changes, n_changes);
|
||||
}
|
||||
@ -1771,11 +1773,39 @@ static int method_unmask_unit_files(sd_bus_message *message, void *userdata, sd_
|
||||
return method_disable_unit_files_generic(message, userdata, "enable", unit_file_unmask, error);
|
||||
}
|
||||
|
||||
static int method_revert_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) {
|
||||
_cleanup_strv_free_ char **l = NULL;
|
||||
UnitFileChange *changes = NULL;
|
||||
unsigned n_changes = 0;
|
||||
Manager *m = userdata;
|
||||
int r;
|
||||
|
||||
assert(message);
|
||||
assert(m);
|
||||
|
||||
r = sd_bus_message_read_strv(message, &l);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = bus_verify_manage_unit_files_async(m, message, error);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
|
||||
|
||||
r = unit_file_revert(m->unit_file_scope, NULL, l, &changes, &n_changes);
|
||||
if (r < 0) {
|
||||
unit_file_changes_free(changes, n_changes);
|
||||
return install_error(error, r);
|
||||
}
|
||||
|
||||
return reply_unit_file_changes_and_free(m, message, -1, changes, n_changes);
|
||||
}
|
||||
|
||||
static int method_set_default_target(sd_bus_message *message, void *userdata, sd_bus_error *error) {
|
||||
UnitFileChange *changes = NULL;
|
||||
unsigned n_changes = 0;
|
||||
Manager *m = userdata;
|
||||
UnitFileScope scope;
|
||||
const char *name;
|
||||
int force, r;
|
||||
|
||||
@ -1796,11 +1826,11 @@ static int method_set_default_target(sd_bus_message *message, void *userdata, sd
|
||||
if (r == 0)
|
||||
return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
|
||||
|
||||
scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
|
||||
|
||||
r = unit_file_set_default(scope, NULL, name, force, &changes, &n_changes);
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = unit_file_set_default(m->unit_file_scope, NULL, name, force, &changes, &n_changes);
|
||||
if (r < 0) {
|
||||
unit_file_changes_free(changes, n_changes);
|
||||
return install_error(error, r);
|
||||
}
|
||||
|
||||
return reply_unit_file_changes_and_free(m, message, -1, changes, n_changes);
|
||||
}
|
||||
@ -1810,7 +1840,6 @@ static int method_preset_all_unit_files(sd_bus_message *message, void *userdata,
|
||||
unsigned n_changes = 0;
|
||||
Manager *m = userdata;
|
||||
UnitFilePresetMode mm;
|
||||
UnitFileScope scope;
|
||||
const char *mode;
|
||||
int force, runtime, r;
|
||||
|
||||
@ -1839,12 +1868,10 @@ static int method_preset_all_unit_files(sd_bus_message *message, void *userdata,
|
||||
if (r == 0)
|
||||
return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
|
||||
|
||||
scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
|
||||
|
||||
r = unit_file_preset_all(scope, runtime, NULL, mm, force, &changes, &n_changes);
|
||||
r = unit_file_preset_all(m->unit_file_scope, runtime, NULL, mm, force, &changes, &n_changes);
|
||||
if (r < 0) {
|
||||
unit_file_changes_free(changes, n_changes);
|
||||
return r;
|
||||
return install_error(error, r);
|
||||
}
|
||||
|
||||
return reply_unit_file_changes_and_free(m, message, -1, changes, n_changes);
|
||||
@ -1855,7 +1882,6 @@ static int method_add_dependency_unit_files(sd_bus_message *message, void *userd
|
||||
Manager *m = userdata;
|
||||
UnitFileChange *changes = NULL;
|
||||
unsigned n_changes = 0;
|
||||
UnitFileScope scope;
|
||||
int runtime, force, r;
|
||||
char *target;
|
||||
char *type;
|
||||
@ -1882,13 +1908,11 @@ static int method_add_dependency_unit_files(sd_bus_message *message, void *userd
|
||||
if (dep < 0)
|
||||
return -EINVAL;
|
||||
|
||||
scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
|
||||
|
||||
r = unit_file_add_dependency(scope, runtime, NULL, l, target, dep, force, &changes, &n_changes);
|
||||
if (r == -ESHUTDOWN)
|
||||
return sd_bus_error_setf(error, BUS_ERROR_UNIT_MASKED, "Unit file is masked");
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = unit_file_add_dependency(m->unit_file_scope, runtime, NULL, l, target, dep, force, &changes, &n_changes);
|
||||
if (r < 0) {
|
||||
unit_file_changes_free(changes, n_changes);
|
||||
return install_error(error, r);
|
||||
}
|
||||
|
||||
return reply_unit_file_changes_and_free(m, message, -1, changes, n_changes);
|
||||
}
|
||||
@ -1924,7 +1948,7 @@ const sd_bus_vtable bus_manager_vtable[] = {
|
||||
SD_BUS_PROPERTY("Environment", "as", NULL, offsetof(Manager, environment), 0),
|
||||
SD_BUS_PROPERTY("ConfirmSpawn", "b", bus_property_get_bool, offsetof(Manager, confirm_spawn), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("ShowStatus", "b", bus_property_get_bool, offsetof(Manager, show_status), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("UnitPath", "as", NULL, offsetof(Manager, lookup_paths.unit_path), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("UnitPath", "as", NULL, offsetof(Manager, lookup_paths.search_path), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("DefaultStandardOutput", "s", bus_property_get_exec_output, offsetof(Manager, default_std_output), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("DefaultStandardError", "s", bus_property_get_exec_output, offsetof(Manager, default_std_output), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_WRITABLE_PROPERTY("RuntimeWatchdogUSec", "t", bus_property_get_usec, property_set_runtime_watchdog, offsetof(Manager, runtime_watchdog), 0),
|
||||
@ -2025,6 +2049,7 @@ const sd_bus_vtable bus_manager_vtable[] = {
|
||||
SD_BUS_METHOD("PresetUnitFilesWithMode", "assbb", "ba(sss)", method_preset_unit_files_with_mode, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("MaskUnitFiles", "asbb", "a(sss)", method_mask_unit_files, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("UnmaskUnitFiles", "asb", "a(sss)", method_unmask_unit_files, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("RevertUnitFiles", "as", "a(sss)", method_revert_unit_files, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("SetDefaultTarget", "sb", "a(sss)", method_set_default_target, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("GetDefaultTarget", NULL, "s", method_get_default_target, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("PresetAllUnitFiles", "sbb", "a(sss)", method_preset_all_unit_files, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include "locale-util.h"
|
||||
#include "log.h"
|
||||
#include "selinux-access.h"
|
||||
#include "signal-util.h"
|
||||
#include "special.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
@ -547,7 +548,7 @@ int bus_unit_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid who argument %s", swho);
|
||||
}
|
||||
|
||||
if (signo <= 0 || signo >= _NSIG)
|
||||
if (!SIGNAL_VALID(signo))
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Signal number out of range.");
|
||||
|
||||
r = bus_verify_manage_units_async_full(
|
||||
@ -1002,7 +1003,6 @@ int bus_unit_queue_job(
|
||||
type = JOB_TRY_RELOAD;
|
||||
}
|
||||
|
||||
|
||||
if (type == JOB_STOP &&
|
||||
(u->load_state == UNIT_NOT_FOUND || u->load_state == UNIT_ERROR) &&
|
||||
unit_active_state(u) == UNIT_INACTIVE)
|
||||
@ -1259,6 +1259,7 @@ int bus_unit_set_properties(
|
||||
}
|
||||
|
||||
int bus_unit_check_load_state(Unit *u, sd_bus_error *error) {
|
||||
assert(u);
|
||||
|
||||
if (u->load_state == UNIT_LOADED)
|
||||
return 0;
|
||||
|
@ -112,7 +112,7 @@ static int signal_agent_released(sd_bus_message *message, void *userdata, sd_bus
|
||||
manager_notify_cgroup_empty(m, cgroup);
|
||||
|
||||
/* if running as system-instance, forward under our name */
|
||||
if (m->running_as == MANAGER_SYSTEM && m->system_bus) {
|
||||
if (MANAGER_IS_SYSTEM(m) && m->system_bus) {
|
||||
r = sd_bus_message_rewind(message, 1);
|
||||
if (r >= 0)
|
||||
r = sd_bus_send(m->system_bus, message, NULL);
|
||||
@ -690,7 +690,7 @@ static int bus_on_connection(sd_event_source *s, int fd, uint32_t revents, void
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (m->running_as == MANAGER_SYSTEM) {
|
||||
if (MANAGER_IS_SYSTEM(m)) {
|
||||
/* When we run as system instance we get the Released
|
||||
* signal via a direct connection */
|
||||
|
||||
@ -864,10 +864,10 @@ static int bus_init_api(Manager *m) {
|
||||
return 0;
|
||||
|
||||
/* The API and system bus is the same if we are running in system mode */
|
||||
if (m->running_as == MANAGER_SYSTEM && m->system_bus)
|
||||
if (MANAGER_IS_SYSTEM(m) && m->system_bus)
|
||||
bus = sd_bus_ref(m->system_bus);
|
||||
else {
|
||||
if (m->running_as == MANAGER_SYSTEM)
|
||||
if (MANAGER_IS_SYSTEM(m))
|
||||
r = sd_bus_open_system(&bus);
|
||||
else
|
||||
r = sd_bus_open_user(&bus);
|
||||
@ -907,7 +907,7 @@ static int bus_setup_system(Manager *m, sd_bus *bus) {
|
||||
assert(bus);
|
||||
|
||||
/* On kdbus or if we are a user instance we get the Released message via the system bus */
|
||||
if (m->running_as == MANAGER_USER || m->kdbus_fd >= 0) {
|
||||
if (MANAGER_IS_USER(m) || m->kdbus_fd >= 0) {
|
||||
r = sd_bus_add_match(
|
||||
bus,
|
||||
NULL,
|
||||
@ -932,7 +932,7 @@ static int bus_init_system(Manager *m) {
|
||||
return 0;
|
||||
|
||||
/* The API and system bus is the same if we are running in system mode */
|
||||
if (m->running_as == MANAGER_SYSTEM && m->api_bus) {
|
||||
if (MANAGER_IS_SYSTEM(m) && m->api_bus) {
|
||||
m->system_bus = sd_bus_ref(m->api_bus);
|
||||
return 0;
|
||||
}
|
||||
@ -983,7 +983,7 @@ static int bus_init_private(Manager *m) {
|
||||
if (m->kdbus_fd >= 0)
|
||||
return 0;
|
||||
|
||||
if (m->running_as == MANAGER_SYSTEM) {
|
||||
if (MANAGER_IS_SYSTEM(m)) {
|
||||
|
||||
/* We want the private bus only when running as init */
|
||||
if (getpid() != 1)
|
||||
@ -1082,7 +1082,7 @@ static void destroy_bus(Manager *m, sd_bus **bus) {
|
||||
|
||||
/* Possibly flush unwritten data, but only if we are
|
||||
* unprivileged, since we don't want to sync here */
|
||||
if (m->running_as != MANAGER_SYSTEM)
|
||||
if (!MANAGER_IS_SYSTEM(m))
|
||||
sd_bus_flush(*bus);
|
||||
|
||||
/* And destroy the object */
|
||||
|
@ -265,7 +265,7 @@ static int device_add_udev_wants(Unit *u, struct udev_device *dev) {
|
||||
assert(u);
|
||||
assert(dev);
|
||||
|
||||
property = u->manager->running_as == MANAGER_USER ? "SYSTEMD_USER_WANTS" : "SYSTEMD_WANTS";
|
||||
property = MANAGER_IS_USER(u->manager) ? "SYSTEMD_USER_WANTS" : "SYSTEMD_WANTS";
|
||||
wants = udev_device_get_property_value(dev, property);
|
||||
if (!wants)
|
||||
return 0;
|
||||
|
@ -47,7 +47,7 @@ int failure_action(
|
||||
if (action == FAILURE_ACTION_NONE)
|
||||
return -ECANCELED;
|
||||
|
||||
if (m->running_as == MANAGER_USER) {
|
||||
if (!MANAGER_IS_SYSTEM(m)) {
|
||||
/* Downgrade all options to simply exiting if we run
|
||||
* in user mode */
|
||||
|
||||
@ -61,17 +61,17 @@ int failure_action(
|
||||
case FAILURE_ACTION_REBOOT:
|
||||
log_and_status(m, "Rebooting as result of failure.");
|
||||
|
||||
update_reboot_param_file(reboot_arg);
|
||||
(void) manager_add_job_by_name_and_warn(m, JOB_START, SPECIAL_REBOOT_TARGET,
|
||||
JOB_REPLACE_IRREVERSIBLY, NULL);
|
||||
(void) update_reboot_parameter_and_warn(reboot_arg);
|
||||
(void) manager_add_job_by_name_and_warn(m, JOB_START, SPECIAL_REBOOT_TARGET, JOB_REPLACE_IRREVERSIBLY, NULL);
|
||||
|
||||
break;
|
||||
|
||||
case FAILURE_ACTION_REBOOT_FORCE:
|
||||
log_and_status(m, "Forcibly rebooting as result of failure.");
|
||||
|
||||
update_reboot_param_file(reboot_arg);
|
||||
(void) update_reboot_parameter_and_warn(reboot_arg);
|
||||
m->exit_code = MANAGER_REBOOT;
|
||||
|
||||
break;
|
||||
|
||||
case FAILURE_ACTION_REBOOT_IMMEDIATE:
|
||||
@ -79,9 +79,10 @@ int failure_action(
|
||||
|
||||
sync();
|
||||
|
||||
if (reboot_arg) {
|
||||
if (!isempty(reboot_arg)) {
|
||||
log_info("Rebooting with argument '%s'.", reboot_arg);
|
||||
syscall(SYS_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART2, reboot_arg);
|
||||
log_warning_errno(errno, "Failed to reboot with parameter, retrying without: %m");
|
||||
}
|
||||
|
||||
log_info("Rebooting.");
|
||||
@ -90,8 +91,7 @@ int failure_action(
|
||||
|
||||
case FAILURE_ACTION_POWEROFF:
|
||||
log_and_status(m, "Powering off as result of failure.");
|
||||
(void) manager_add_job_by_name_and_warn(m, JOB_START, SPECIAL_POWEROFF_TARGET,
|
||||
JOB_REPLACE_IRREVERSIBLY, NULL);
|
||||
(void) manager_add_job_by_name_and_warn(m, JOB_START, SPECIAL_POWEROFF_TARGET, JOB_REPLACE_IRREVERSIBLY, NULL);
|
||||
break;
|
||||
|
||||
case FAILURE_ACTION_POWEROFF_FORCE:
|
||||
|
@ -137,7 +137,7 @@ void job_uninstall(Job *j) {
|
||||
/* Detach from next 'bigger' objects */
|
||||
|
||||
/* daemon-reload should be transparent to job observers */
|
||||
if (j->manager->n_reloading <= 0)
|
||||
if (!MANAGER_IS_RELOADING(j->manager))
|
||||
bus_job_send_removed_signal(j);
|
||||
|
||||
*pj = NULL;
|
||||
@ -1156,7 +1156,7 @@ void job_shutdown_magic(Job *j) {
|
||||
if (j->type != JOB_START)
|
||||
return;
|
||||
|
||||
if (j->unit->manager->running_as != MANAGER_SYSTEM)
|
||||
if (!MANAGER_IS_SYSTEM(j->unit->manager))
|
||||
return;
|
||||
|
||||
if (!unit_has_name(j->unit, SPECIAL_SHUTDOWN_TARGET))
|
||||
|
@ -44,6 +44,7 @@ static int add_dependency_consumer(
|
||||
}
|
||||
|
||||
int unit_load_dropin(Unit *u) {
|
||||
_cleanup_strv_free_ char **l = NULL;
|
||||
Iterator i;
|
||||
char *t, **f;
|
||||
int r;
|
||||
@ -55,7 +56,7 @@ int unit_load_dropin(Unit *u) {
|
||||
SET_FOREACH(t, u->names, i) {
|
||||
char **p;
|
||||
|
||||
STRV_FOREACH(p, u->manager->lookup_paths.unit_path) {
|
||||
STRV_FOREACH(p, u->manager->lookup_paths.search_path) {
|
||||
unit_file_process_dir(u->manager->unit_path_cache, *p, t, ".wants", UNIT_WANTS,
|
||||
add_dependency_consumer, u, NULL);
|
||||
unit_file_process_dir(u->manager->unit_path_cache, *p, t, ".requires", UNIT_REQUIRES,
|
||||
@ -63,11 +64,19 @@ int unit_load_dropin(Unit *u) {
|
||||
}
|
||||
}
|
||||
|
||||
u->dropin_paths = strv_free(u->dropin_paths);
|
||||
r = unit_find_dropin_paths(u, &u->dropin_paths);
|
||||
r = unit_find_dropin_paths(u, &l);
|
||||
if (r <= 0)
|
||||
return 0;
|
||||
|
||||
if (!u->dropin_paths) {
|
||||
u->dropin_paths = l;
|
||||
l = NULL;
|
||||
} else {
|
||||
r = strv_extend_strv(&u->dropin_paths, l, true);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
}
|
||||
|
||||
STRV_FOREACH(f, u->dropin_paths) {
|
||||
config_parse(u->id, *f, NULL,
|
||||
UNIT_VTABLE(u)->sections,
|
||||
|
@ -25,7 +25,7 @@
|
||||
/* Read service data supplementary drop-in directories */
|
||||
|
||||
static inline int unit_find_dropin_paths(Unit *u, char ***paths) {
|
||||
return unit_file_find_dropin_paths(u->manager->lookup_paths.unit_path,
|
||||
return unit_file_find_dropin_paths(u->manager->lookup_paths.search_path,
|
||||
u->manager->unit_path_cache,
|
||||
u->names,
|
||||
paths);
|
||||
|
@ -2495,7 +2495,7 @@ int config_parse_syscall_filter(
|
||||
|
||||
/* Turn on NNP, but only if it wasn't configured explicitly
|
||||
* before, and only if we are in user mode. */
|
||||
if (!c->no_new_privileges_set && u->manager->running_as == MANAGER_USER)
|
||||
if (!c->no_new_privileges_set && MANAGER_IS_USER(u->manager))
|
||||
c->no_new_privileges = true;
|
||||
|
||||
return 0;
|
||||
@ -3583,7 +3583,7 @@ static int load_from_path(Unit *u, const char *path) {
|
||||
} else {
|
||||
char **p;
|
||||
|
||||
STRV_FOREACH(p, u->manager->lookup_paths.unit_path) {
|
||||
STRV_FOREACH(p, u->manager->lookup_paths.search_path) {
|
||||
|
||||
/* Instead of opening the path right away, we manually
|
||||
* follow all symlinks and add their name to our unit
|
||||
|
@ -259,9 +259,8 @@ int machine_id_setup(const char *root, sd_id128_t machine_id) {
|
||||
/* Hmm, we couldn't write it? So let's write it to
|
||||
* /run/machine-id as a replacement */
|
||||
|
||||
RUN_WITH_UMASK(0022) {
|
||||
RUN_WITH_UMASK(0022)
|
||||
r = write_string_file(run_machine_id, id, WRITE_STRING_FILE_CREATE);
|
||||
}
|
||||
if (r < 0) {
|
||||
(void) unlink(run_machine_id);
|
||||
return log_error_errno(r, "Cannot write %s: %m", run_machine_id);
|
||||
|
@ -81,6 +81,7 @@
|
||||
#include "strv.h"
|
||||
#include "switch-root.h"
|
||||
#include "terminal-util.h"
|
||||
#include "umask-util.h"
|
||||
#include "user-util.h"
|
||||
#include "virt.h"
|
||||
#include "watchdog.h"
|
||||
@ -94,7 +95,7 @@ static enum {
|
||||
ACTION_DONE
|
||||
} arg_action = ACTION_RUN;
|
||||
static char *arg_default_unit = NULL;
|
||||
static ManagerRunningAs arg_running_as = _MANAGER_RUNNING_AS_INVALID;
|
||||
static bool arg_system = false;
|
||||
static bool arg_dump_core = true;
|
||||
static int arg_crash_chvt = -1;
|
||||
static bool arg_crash_shell = false;
|
||||
@ -688,11 +689,11 @@ static int parse_config_file(void) {
|
||||
|
||||
const char *fn, *conf_dirs_nulstr;
|
||||
|
||||
fn = arg_running_as == MANAGER_SYSTEM ?
|
||||
fn = arg_system ?
|
||||
PKGSYSCONFDIR "/system.conf" :
|
||||
PKGSYSCONFDIR "/user.conf";
|
||||
|
||||
conf_dirs_nulstr = arg_running_as == MANAGER_SYSTEM ?
|
||||
conf_dirs_nulstr = arg_system ?
|
||||
CONF_PATHS_NULSTR("systemd/system.conf.d") :
|
||||
CONF_PATHS_NULSTR("systemd/user.conf.d");
|
||||
|
||||
@ -866,11 +867,11 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
break;
|
||||
|
||||
case ARG_SYSTEM:
|
||||
arg_running_as = MANAGER_SYSTEM;
|
||||
arg_system = true;
|
||||
break;
|
||||
|
||||
case ARG_USER:
|
||||
arg_running_as = MANAGER_USER;
|
||||
arg_system = false;
|
||||
break;
|
||||
|
||||
case ARG_TEST:
|
||||
@ -1237,7 +1238,8 @@ static int write_container_id(void) {
|
||||
if (isempty(c))
|
||||
return 0;
|
||||
|
||||
r = write_string_file("/run/systemd/container", c, WRITE_STRING_FILE_CREATE);
|
||||
RUN_WITH_UMASK(0022)
|
||||
r = write_string_file("/run/systemd/container", c, WRITE_STRING_FILE_CREATE);
|
||||
if (r < 0)
|
||||
return log_warning_errno(r, "Failed to write /run/systemd/container, ignoring: %m");
|
||||
|
||||
@ -1346,7 +1348,7 @@ int main(int argc, char *argv[]) {
|
||||
if (getpid() == 1 && detect_container() <= 0) {
|
||||
|
||||
/* Running outside of a container as PID 1 */
|
||||
arg_running_as = MANAGER_SYSTEM;
|
||||
arg_system = true;
|
||||
make_null_stdio();
|
||||
log_set_target(LOG_TARGET_KMSG);
|
||||
log_open();
|
||||
@ -1430,7 +1432,7 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
} else if (getpid() == 1) {
|
||||
/* Running inside a container, as PID 1 */
|
||||
arg_running_as = MANAGER_SYSTEM;
|
||||
arg_system = true;
|
||||
log_set_target(LOG_TARGET_CONSOLE);
|
||||
log_close_console(); /* force reopen of /dev/console */
|
||||
log_open();
|
||||
@ -1443,7 +1445,7 @@ int main(int argc, char *argv[]) {
|
||||
kernel_timestamp = DUAL_TIMESTAMP_NULL;
|
||||
} else {
|
||||
/* Running as user instance */
|
||||
arg_running_as = MANAGER_USER;
|
||||
arg_system = false;
|
||||
log_set_target(LOG_TARGET_AUTO);
|
||||
log_open();
|
||||
|
||||
@ -1501,7 +1503,7 @@ int main(int argc, char *argv[]) {
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (arg_running_as == MANAGER_SYSTEM) {
|
||||
if (arg_system) {
|
||||
r = parse_proc_cmdline(parse_proc_cmdline_item);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
|
||||
@ -1522,14 +1524,14 @@ int main(int argc, char *argv[]) {
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (arg_running_as == MANAGER_USER &&
|
||||
if (!arg_system &&
|
||||
arg_action == ACTION_RUN &&
|
||||
sd_booted() <= 0) {
|
||||
log_error("Trying to run as user instance, but the system has not been booted with systemd.");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (arg_running_as == MANAGER_SYSTEM &&
|
||||
if (arg_system &&
|
||||
arg_action == ACTION_RUN &&
|
||||
running_in_chroot() > 0) {
|
||||
log_error("Cannot be run in a chroot() environment.");
|
||||
@ -1557,7 +1559,7 @@ int main(int argc, char *argv[]) {
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (arg_running_as == MANAGER_USER &&
|
||||
if (!arg_system &&
|
||||
!getenv("XDG_RUNTIME_DIR")) {
|
||||
log_error("Trying to run as user instance, but $XDG_RUNTIME_DIR is not set.");
|
||||
goto finish;
|
||||
@ -1580,7 +1582,7 @@ int main(int argc, char *argv[]) {
|
||||
if (arg_serialization)
|
||||
assert_se(fdset_remove(fds, fileno(arg_serialization)) >= 0);
|
||||
|
||||
if (arg_running_as == MANAGER_SYSTEM)
|
||||
if (arg_system)
|
||||
/* Become a session leader if we aren't one yet. */
|
||||
setsid();
|
||||
|
||||
@ -1589,7 +1591,7 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
/* Reset the console, but only if this is really init and we
|
||||
* are freshly booted */
|
||||
if (arg_running_as == MANAGER_SYSTEM && arg_action == ACTION_RUN) {
|
||||
if (arg_system && arg_action == ACTION_RUN) {
|
||||
|
||||
/* If we are init, we connect stdin/stdout/stderr to
|
||||
* /dev/null and make sure we don't have a controlling
|
||||
@ -1616,7 +1618,7 @@ int main(int argc, char *argv[]) {
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (arg_running_as == MANAGER_SYSTEM) {
|
||||
if (arg_system) {
|
||||
int v;
|
||||
|
||||
log_info(PACKAGE_STRING " running in %ssystem mode. (" SYSTEMD_FEATURES ")",
|
||||
@ -1652,7 +1654,7 @@ int main(int argc, char *argv[]) {
|
||||
arg_action == ACTION_TEST ? " test" : "", getuid(), t);
|
||||
}
|
||||
|
||||
if (arg_running_as == MANAGER_SYSTEM && !skip_setup) {
|
||||
if (arg_system && !skip_setup) {
|
||||
if (arg_show_status > 0)
|
||||
status_welcome();
|
||||
|
||||
@ -1664,7 +1666,7 @@ int main(int argc, char *argv[]) {
|
||||
test_usr();
|
||||
}
|
||||
|
||||
if (arg_running_as == MANAGER_SYSTEM && arg_runtime_watchdog > 0 && arg_runtime_watchdog != USEC_INFINITY)
|
||||
if (arg_system && arg_runtime_watchdog > 0 && arg_runtime_watchdog != USEC_INFINITY)
|
||||
watchdog_set_timeout(&arg_runtime_watchdog);
|
||||
|
||||
if (arg_timer_slack_nsec != NSEC_INFINITY)
|
||||
@ -1694,12 +1696,12 @@ int main(int argc, char *argv[]) {
|
||||
}
|
||||
}
|
||||
|
||||
if (arg_running_as == MANAGER_USER)
|
||||
if (!arg_system)
|
||||
/* Become reaper of our children */
|
||||
if (prctl(PR_SET_CHILD_SUBREAPER, 1) < 0)
|
||||
log_warning_errno(errno, "Failed to make us a subreaper: %m");
|
||||
|
||||
if (arg_running_as == MANAGER_SYSTEM) {
|
||||
if (arg_system) {
|
||||
bump_rlimit_nofile(&saved_rlimit_nofile);
|
||||
|
||||
if (empty_etc) {
|
||||
@ -1711,7 +1713,7 @@ int main(int argc, char *argv[]) {
|
||||
}
|
||||
}
|
||||
|
||||
r = manager_new(arg_running_as, arg_action == ACTION_TEST, &m);
|
||||
r = manager_new(arg_system ? UNIT_FILE_SYSTEM : UNIT_FILE_USER, arg_action == ACTION_TEST, &m);
|
||||
if (r < 0) {
|
||||
log_emergency_errno(r, "Failed to allocate manager object: %m");
|
||||
error_message = "Failed to allocate manager object";
|
||||
@ -1874,7 +1876,7 @@ int main(int argc, char *argv[]) {
|
||||
case MANAGER_EXIT:
|
||||
retval = m->return_value;
|
||||
|
||||
if (m->running_as == MANAGER_USER) {
|
||||
if (MANAGER_IS_USER(m)) {
|
||||
log_debug("Exit.");
|
||||
goto finish;
|
||||
}
|
||||
@ -1970,7 +1972,7 @@ finish:
|
||||
args[i++] = SYSTEMD_BINARY_PATH;
|
||||
if (switch_root_dir)
|
||||
args[i++] = "--switched-root";
|
||||
args[i++] = arg_running_as == MANAGER_SYSTEM ? "--system" : "--user";
|
||||
args[i++] = arg_system ? "--system" : "--user";
|
||||
args[i++] = "--deserialize";
|
||||
args[i++] = sfd;
|
||||
args[i++] = NULL;
|
||||
|
@ -49,6 +49,7 @@
|
||||
#include "dbus-manager.h"
|
||||
#include "dbus-unit.h"
|
||||
#include "dbus.h"
|
||||
#include "dirent-util.h"
|
||||
#include "env-util.h"
|
||||
#include "escape.h"
|
||||
#include "exit-status.h"
|
||||
@ -63,6 +64,7 @@
|
||||
#include "manager.h"
|
||||
#include "missing.h"
|
||||
#include "mkdir.h"
|
||||
#include "mkdir.h"
|
||||
#include "parse-util.h"
|
||||
#include "path-lookup.h"
|
||||
#include "path-util.h"
|
||||
@ -98,7 +100,6 @@ static int manager_dispatch_idle_pipe_fd(sd_event_source *source, int fd, uint32
|
||||
static int manager_dispatch_jobs_in_progress(sd_event_source *source, usec_t usec, void *userdata);
|
||||
static int manager_dispatch_run_queue(sd_event_source *source, void *userdata);
|
||||
static int manager_run_generators(Manager *m);
|
||||
static void manager_undo_generators(Manager *m);
|
||||
|
||||
static void manager_watch_jobs_in_progress(Manager *m) {
|
||||
usec_t next;
|
||||
@ -491,7 +492,7 @@ static int manager_setup_signals(Manager *m) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (m->running_as == MANAGER_SYSTEM)
|
||||
if (MANAGER_IS_SYSTEM(m))
|
||||
return enable_special_signals(m);
|
||||
|
||||
return 0;
|
||||
@ -518,7 +519,7 @@ static void manager_clean_environment(Manager *m) {
|
||||
static int manager_default_environment(Manager *m) {
|
||||
assert(m);
|
||||
|
||||
if (m->running_as == MANAGER_SYSTEM) {
|
||||
if (MANAGER_IS_SYSTEM(m)) {
|
||||
/* The system manager always starts with a clean
|
||||
* environment for its children. It does not import
|
||||
* the kernel or the parents exported variables.
|
||||
@ -547,43 +548,36 @@ static int manager_default_environment(Manager *m) {
|
||||
}
|
||||
|
||||
|
||||
int manager_new(ManagerRunningAs running_as, bool test_run, Manager **_m) {
|
||||
|
||||
static const char * const unit_log_fields[_MANAGER_RUNNING_AS_MAX] = {
|
||||
[MANAGER_SYSTEM] = "UNIT=",
|
||||
[MANAGER_USER] = "USER_UNIT=",
|
||||
};
|
||||
|
||||
static const char * const unit_log_format_strings[_MANAGER_RUNNING_AS_MAX] = {
|
||||
[MANAGER_SYSTEM] = "UNIT=%s",
|
||||
[MANAGER_USER] = "USER_UNIT=%s",
|
||||
};
|
||||
|
||||
int manager_new(UnitFileScope scope, bool test_run, Manager **_m) {
|
||||
Manager *m;
|
||||
int r;
|
||||
|
||||
assert(_m);
|
||||
assert(running_as >= 0);
|
||||
assert(running_as < _MANAGER_RUNNING_AS_MAX);
|
||||
assert(IN_SET(scope, UNIT_FILE_SYSTEM, UNIT_FILE_USER));
|
||||
|
||||
m = new0(Manager, 1);
|
||||
if (!m)
|
||||
return -ENOMEM;
|
||||
|
||||
#ifdef ENABLE_EFI
|
||||
if (running_as == MANAGER_SYSTEM && detect_container() <= 0)
|
||||
boot_timestamps(&m->userspace_timestamp, &m->firmware_timestamp, &m->loader_timestamp);
|
||||
#endif
|
||||
|
||||
m->running_as = running_as;
|
||||
m->unit_file_scope = scope;
|
||||
m->exit_code = _MANAGER_EXIT_CODE_INVALID;
|
||||
m->default_timer_accuracy_usec = USEC_PER_MINUTE;
|
||||
m->default_tasks_accounting = true;
|
||||
m->default_tasks_max = UINT64_C(512);
|
||||
|
||||
#ifdef ENABLE_EFI
|
||||
if (MANAGER_IS_SYSTEM(m) && detect_container() <= 0)
|
||||
boot_timestamps(&m->userspace_timestamp, &m->firmware_timestamp, &m->loader_timestamp);
|
||||
#endif
|
||||
|
||||
/* Prepare log fields we can use for structured logging */
|
||||
m->unit_log_field = unit_log_fields[running_as];
|
||||
m->unit_log_format_string = unit_log_format_strings[running_as];
|
||||
if (MANAGER_IS_SYSTEM(m)) {
|
||||
m->unit_log_field = "UNIT=";
|
||||
m->unit_log_format_string = "UNIT=%s";
|
||||
} else {
|
||||
m->unit_log_field = "USER_UNIT=";
|
||||
m->unit_log_format_string = "USER_UNIT=%s";
|
||||
}
|
||||
|
||||
m->idle_pipe[0] = m->idle_pipe[1] = m->idle_pipe[2] = m->idle_pipe[3] = -1;
|
||||
|
||||
@ -683,6 +677,7 @@ static int manager_setup_notify(Manager *m) {
|
||||
.sa.sa_family = AF_UNIX,
|
||||
};
|
||||
static const int one = 1;
|
||||
const char *e;
|
||||
|
||||
/* First free all secondary fields */
|
||||
m->notify_socket = mfree(m->notify_socket);
|
||||
@ -694,19 +689,13 @@ static int manager_setup_notify(Manager *m) {
|
||||
|
||||
fd_inc_rcvbuf(fd, NOTIFY_RCVBUF_SIZE);
|
||||
|
||||
if (m->running_as == MANAGER_SYSTEM)
|
||||
m->notify_socket = strdup("/run/systemd/notify");
|
||||
else {
|
||||
const char *e;
|
||||
|
||||
e = getenv("XDG_RUNTIME_DIR");
|
||||
if (!e) {
|
||||
log_error_errno(errno, "XDG_RUNTIME_DIR is not set: %m");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
m->notify_socket = strappend(e, "/systemd/notify");
|
||||
e = manager_get_runtime_prefix(m);
|
||||
if (!e) {
|
||||
log_error("Failed to determine runtime prefix.");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
m->notify_socket = strappend(e, "/systemd/notify");
|
||||
if (!m->notify_socket)
|
||||
return log_oom();
|
||||
|
||||
@ -756,8 +745,8 @@ static int manager_setup_kdbus(Manager *m) {
|
||||
return -ESOCKTNOSUPPORT;
|
||||
|
||||
m->kdbus_fd = bus_kernel_create_bus(
|
||||
m->running_as == MANAGER_SYSTEM ? "system" : "user",
|
||||
m->running_as == MANAGER_SYSTEM, &p);
|
||||
MANAGER_IS_SYSTEM(m) ? "system" : "user",
|
||||
MANAGER_IS_SYSTEM(m), &p);
|
||||
|
||||
if (m->kdbus_fd < 0)
|
||||
return log_debug_errno(m->kdbus_fd, "Failed to set up kdbus: %m");
|
||||
@ -778,7 +767,7 @@ static int manager_connect_bus(Manager *m, bool reexecuting) {
|
||||
try_bus_connect =
|
||||
m->kdbus_fd >= 0 ||
|
||||
reexecuting ||
|
||||
(m->running_as == MANAGER_USER && getenv("DBUS_SESSION_BUS_ADDRESS"));
|
||||
(MANAGER_IS_USER(m) && getenv("DBUS_SESSION_BUS_ADDRESS"));
|
||||
|
||||
/* Try to connect to the buses, if possible. */
|
||||
return bus_init(m, try_bus_connect);
|
||||
@ -940,7 +929,7 @@ Manager* manager_free(Manager *m) {
|
||||
* around */
|
||||
manager_shutdown_cgroup(m, m->exit_code != MANAGER_REEXECUTE);
|
||||
|
||||
manager_undo_generators(m);
|
||||
lookup_paths_flush_generator(&m->lookup_paths);
|
||||
|
||||
bus_done(m);
|
||||
|
||||
@ -1037,7 +1026,6 @@ static void manager_coldplug(Manager *m) {
|
||||
|
||||
static void manager_build_unit_path_cache(Manager *m) {
|
||||
char **i;
|
||||
_cleanup_closedir_ DIR *d = NULL;
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
@ -1046,29 +1034,27 @@ static void manager_build_unit_path_cache(Manager *m) {
|
||||
|
||||
m->unit_path_cache = set_new(&string_hash_ops);
|
||||
if (!m->unit_path_cache) {
|
||||
log_error("Failed to allocate unit path cache.");
|
||||
return;
|
||||
r = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* This simply builds a list of files we know exist, so that
|
||||
* we don't always have to go to disk */
|
||||
|
||||
STRV_FOREACH(i, m->lookup_paths.unit_path) {
|
||||
STRV_FOREACH(i, m->lookup_paths.search_path) {
|
||||
_cleanup_closedir_ DIR *d = NULL;
|
||||
struct dirent *de;
|
||||
|
||||
d = opendir(*i);
|
||||
if (!d) {
|
||||
if (errno != ENOENT)
|
||||
log_error_errno(errno, "Failed to open directory %s: %m", *i);
|
||||
log_warning_errno(errno, "Failed to open directory %s, ignoring: %m", *i);
|
||||
continue;
|
||||
}
|
||||
|
||||
while ((de = readdir(d))) {
|
||||
FOREACH_DIRENT(de, d, r = -errno; goto fail) {
|
||||
char *p;
|
||||
|
||||
if (hidden_file(de->d_name))
|
||||
continue;
|
||||
|
||||
p = strjoin(streq(*i, "/") ? "" : *i, "/", de->d_name, NULL);
|
||||
if (!p) {
|
||||
r = -ENOMEM;
|
||||
@ -1079,20 +1065,15 @@ static void manager_build_unit_path_cache(Manager *m) {
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
d = safe_closedir(d);
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
fail:
|
||||
log_error_errno(r, "Failed to build unit path cache: %m");
|
||||
|
||||
set_free_free(m->unit_path_cache);
|
||||
m->unit_path_cache = NULL;
|
||||
log_warning_errno(r, "Failed to build unit path cache, proceeding without: %m");
|
||||
m->unit_path_cache = set_free_free(m->unit_path_cache);
|
||||
}
|
||||
|
||||
|
||||
static void manager_distribute_fds(Manager *m, FDSet *fds) {
|
||||
Iterator i;
|
||||
Unit *u;
|
||||
@ -1116,21 +1097,22 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
|
||||
|
||||
assert(m);
|
||||
|
||||
r = lookup_paths_init(&m->lookup_paths, m->unit_file_scope, 0, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Make sure the transient directory always exists, so that it remains in the search path */
|
||||
r = mkdir_p_label(m->lookup_paths.transient, 0755);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
dual_timestamp_get(&m->generators_start_timestamp);
|
||||
r = manager_run_generators(m);
|
||||
dual_timestamp_get(&m->generators_finish_timestamp);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = lookup_paths_init(
|
||||
&m->lookup_paths, m->running_as, true,
|
||||
NULL,
|
||||
m->generator_unit_path,
|
||||
m->generator_unit_path_early,
|
||||
m->generator_unit_path_late);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
lookup_paths_reduce(&m->lookup_paths);
|
||||
manager_build_unit_path_cache(m);
|
||||
|
||||
/* If we will deserialize make sure that during enumeration
|
||||
@ -1744,7 +1726,7 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t
|
||||
}
|
||||
|
||||
log_received_signal(sfsi.ssi_signo == SIGCHLD ||
|
||||
(sfsi.ssi_signo == SIGTERM && m->running_as == MANAGER_USER)
|
||||
(sfsi.ssi_signo == SIGTERM && MANAGER_IS_USER(m))
|
||||
? LOG_DEBUG : LOG_INFO,
|
||||
&sfsi);
|
||||
|
||||
@ -1755,7 +1737,7 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t
|
||||
break;
|
||||
|
||||
case SIGTERM:
|
||||
if (m->running_as == MANAGER_SYSTEM) {
|
||||
if (MANAGER_IS_SYSTEM(m)) {
|
||||
/* This is for compatibility with the
|
||||
* original sysvinit */
|
||||
m->exit_code = MANAGER_REEXECUTE;
|
||||
@ -1765,7 +1747,7 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t
|
||||
/* Fall through */
|
||||
|
||||
case SIGINT:
|
||||
if (m->running_as == MANAGER_SYSTEM) {
|
||||
if (MANAGER_IS_SYSTEM(m)) {
|
||||
|
||||
/* If the user presses C-A-D more than
|
||||
* 7 times within 2s, we reboot
|
||||
@ -1791,14 +1773,14 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t
|
||||
break;
|
||||
|
||||
case SIGWINCH:
|
||||
if (m->running_as == MANAGER_SYSTEM)
|
||||
if (MANAGER_IS_SYSTEM(m))
|
||||
manager_start_target(m, SPECIAL_KBREQUEST_TARGET, JOB_REPLACE);
|
||||
|
||||
/* This is a nop on non-init */
|
||||
break;
|
||||
|
||||
case SIGPWR:
|
||||
if (m->running_as == MANAGER_SYSTEM)
|
||||
if (MANAGER_IS_SYSTEM(m))
|
||||
manager_start_target(m, SPECIAL_SIGPWR_TARGET, JOB_REPLACE);
|
||||
|
||||
/* This is a nop on non-init */
|
||||
@ -1906,7 +1888,7 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t
|
||||
break;
|
||||
|
||||
case 24:
|
||||
if (m->running_as == MANAGER_USER) {
|
||||
if (MANAGER_IS_USER(m)) {
|
||||
m->exit_code = MANAGER_EXIT;
|
||||
return 0;
|
||||
}
|
||||
@ -2022,7 +2004,7 @@ int manager_loop(Manager *m) {
|
||||
while (m->exit_code == MANAGER_OK) {
|
||||
usec_t wait_usec;
|
||||
|
||||
if (m->runtime_watchdog > 0 && m->runtime_watchdog != USEC_INFINITY && m->running_as == MANAGER_SYSTEM)
|
||||
if (m->runtime_watchdog > 0 && m->runtime_watchdog != USEC_INFINITY && MANAGER_IS_SYSTEM(m))
|
||||
watchdog_ping();
|
||||
|
||||
if (!ratelimit_test(&rl)) {
|
||||
@ -2047,7 +2029,7 @@ int manager_loop(Manager *m) {
|
||||
continue;
|
||||
|
||||
/* Sleep for half the watchdog time */
|
||||
if (m->runtime_watchdog > 0 && m->runtime_watchdog != USEC_INFINITY && m->running_as == MANAGER_SYSTEM) {
|
||||
if (m->runtime_watchdog > 0 && m->runtime_watchdog != USEC_INFINITY && MANAGER_IS_SYSTEM(m)) {
|
||||
wait_usec = m->runtime_watchdog / 2;
|
||||
if (wait_usec <= 0)
|
||||
wait_usec = 1;
|
||||
@ -2118,7 +2100,7 @@ void manager_send_unit_audit(Manager *m, Unit *u, int type, bool success) {
|
||||
const char *msg;
|
||||
int audit_fd, r;
|
||||
|
||||
if (m->running_as != MANAGER_SYSTEM)
|
||||
if (!MANAGER_IS_SYSTEM(m))
|
||||
return;
|
||||
|
||||
audit_fd = get_audit_fd();
|
||||
@ -2127,7 +2109,7 @@ void manager_send_unit_audit(Manager *m, Unit *u, int type, bool success) {
|
||||
|
||||
/* Don't generate audit events if the service was already
|
||||
* started and we're just deserializing */
|
||||
if (m->n_reloading > 0)
|
||||
if (MANAGER_IS_RELOADING(m))
|
||||
return;
|
||||
|
||||
if (u->type != UNIT_SERVICE)
|
||||
@ -2161,10 +2143,10 @@ void manager_send_unit_plymouth(Manager *m, Unit *u) {
|
||||
|
||||
/* Don't generate plymouth events if the service was already
|
||||
* started and we're just deserializing */
|
||||
if (m->n_reloading > 0)
|
||||
if (MANAGER_IS_RELOADING(m))
|
||||
return;
|
||||
|
||||
if (m->running_as != MANAGER_SYSTEM)
|
||||
if (!MANAGER_IS_SYSTEM(m))
|
||||
return;
|
||||
|
||||
if (detect_container() > 0)
|
||||
@ -2208,7 +2190,7 @@ int manager_open_serialization(Manager *m, FILE **_f) {
|
||||
|
||||
assert(_f);
|
||||
|
||||
path = m->running_as == MANAGER_SYSTEM ? "/run/systemd" : "/tmp";
|
||||
path = MANAGER_IS_SYSTEM(m) ? "/run/systemd" : "/tmp";
|
||||
fd = open_tmpfile(path, O_RDWR|O_CLOEXEC);
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
@ -2539,23 +2521,19 @@ int manager_reload(Manager *m) {
|
||||
|
||||
/* From here on there is no way back. */
|
||||
manager_clear_jobs_and_units(m);
|
||||
manager_undo_generators(m);
|
||||
lookup_paths_flush_generator(&m->lookup_paths);
|
||||
lookup_paths_free(&m->lookup_paths);
|
||||
|
||||
q = lookup_paths_init(&m->lookup_paths, m->unit_file_scope, 0, NULL);
|
||||
if (q < 0 && r >= 0)
|
||||
r = q;
|
||||
|
||||
/* Find new unit paths */
|
||||
q = manager_run_generators(m);
|
||||
if (q < 0 && r >= 0)
|
||||
r = q;
|
||||
|
||||
q = lookup_paths_init(
|
||||
&m->lookup_paths, m->running_as, true,
|
||||
NULL,
|
||||
m->generator_unit_path,
|
||||
m->generator_unit_path_early,
|
||||
m->generator_unit_path_late);
|
||||
if (q < 0 && r >= 0)
|
||||
r = q;
|
||||
|
||||
lookup_paths_reduce(&m->lookup_paths);
|
||||
manager_build_unit_path_cache(m);
|
||||
|
||||
/* First, enumerate what we can from all config files */
|
||||
@ -2589,12 +2567,6 @@ int manager_reload(Manager *m) {
|
||||
return r;
|
||||
}
|
||||
|
||||
bool manager_is_reloading_or_reexecuting(Manager *m) {
|
||||
assert(m);
|
||||
|
||||
return m->n_reloading != 0;
|
||||
}
|
||||
|
||||
void manager_reset_failed(Manager *m) {
|
||||
Unit *u;
|
||||
Iterator i;
|
||||
@ -2626,7 +2598,7 @@ static void manager_notify_finished(Manager *m) {
|
||||
if (m->test_run)
|
||||
return;
|
||||
|
||||
if (m->running_as == MANAGER_SYSTEM && detect_container() <= 0) {
|
||||
if (MANAGER_IS_SYSTEM(m) && detect_container() <= 0) {
|
||||
|
||||
/* Note that m->kernel_usec.monotonic is always at 0,
|
||||
* and m->firmware_usec.monotonic and
|
||||
@ -2691,7 +2663,7 @@ static void manager_notify_finished(Manager *m) {
|
||||
void manager_check_finished(Manager *m) {
|
||||
assert(m);
|
||||
|
||||
if (m->n_reloading > 0)
|
||||
if (MANAGER_IS_RELOADING(m))
|
||||
return;
|
||||
|
||||
/* Verify that we are actually running currently. Initially
|
||||
@ -2732,77 +2704,6 @@ void manager_check_finished(Manager *m) {
|
||||
manager_invalidate_startup_units(m);
|
||||
}
|
||||
|
||||
static int create_generator_dir(Manager *m, char **generator, const char *name) {
|
||||
char *p;
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
assert(generator);
|
||||
assert(name);
|
||||
|
||||
if (*generator)
|
||||
return 0;
|
||||
|
||||
if (m->running_as == MANAGER_SYSTEM && getpid() == 1) {
|
||||
/* systemd --system, not running --test */
|
||||
|
||||
p = strappend("/run/systemd/", name);
|
||||
if (!p)
|
||||
return log_oom();
|
||||
|
||||
r = mkdir_p_label(p, 0755);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to create generator directory %s: %m", p);
|
||||
free(p);
|
||||
return r;
|
||||
}
|
||||
} else if (m->running_as == MANAGER_USER) {
|
||||
const char *s = NULL;
|
||||
|
||||
s = getenv("XDG_RUNTIME_DIR");
|
||||
if (!s)
|
||||
return -EINVAL;
|
||||
p = strjoin(s, "/systemd/", name, NULL);
|
||||
if (!p)
|
||||
return log_oom();
|
||||
|
||||
r = mkdir_p_label(p, 0755);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to create generator directory %s: %m", p);
|
||||
free(p);
|
||||
return r;
|
||||
}
|
||||
} else {
|
||||
/* systemd --system --test */
|
||||
|
||||
p = strjoin("/tmp/systemd-", name, ".XXXXXX", NULL);
|
||||
if (!p)
|
||||
return log_oom();
|
||||
|
||||
if (!mkdtemp(p)) {
|
||||
log_error_errno(errno, "Failed to create generator directory %s: %m", p);
|
||||
free(p);
|
||||
return -errno;
|
||||
}
|
||||
}
|
||||
|
||||
*generator = p;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void trim_generator_dir(Manager *m, char **generator) {
|
||||
assert(m);
|
||||
assert(generator);
|
||||
|
||||
if (!*generator)
|
||||
return;
|
||||
|
||||
if (rmdir(*generator) >= 0)
|
||||
*generator = mfree(*generator);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static int manager_run_generators(Manager *m) {
|
||||
_cleanup_strv_free_ char **paths = NULL;
|
||||
const char *argv[5];
|
||||
@ -2814,71 +2715,40 @@ static int manager_run_generators(Manager *m) {
|
||||
if (m->test_run)
|
||||
return 0;
|
||||
|
||||
paths = generator_paths(m->running_as);
|
||||
paths = generator_binary_paths(m->unit_file_scope);
|
||||
if (!paths)
|
||||
return log_oom();
|
||||
|
||||
/* Optimize by skipping the whole process by not creating output directories
|
||||
* if no generators are found. */
|
||||
STRV_FOREACH(path, paths) {
|
||||
r = access(*path, F_OK);
|
||||
if (r == 0)
|
||||
if (access(*path, F_OK) >= 0)
|
||||
goto found;
|
||||
if (errno != ENOENT)
|
||||
log_warning_errno(errno, "Failed to open generator directory %s: %m", *path);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
found:
|
||||
r = create_generator_dir(m, &m->generator_unit_path, "generator");
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
|
||||
r = create_generator_dir(m, &m->generator_unit_path_early, "generator.early");
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
|
||||
r = create_generator_dir(m, &m->generator_unit_path_late, "generator.late");
|
||||
r = lookup_paths_mkdir_generator(&m->lookup_paths);
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
|
||||
argv[0] = NULL; /* Leave this empty, execute_directory() will fill something in */
|
||||
argv[1] = m->generator_unit_path;
|
||||
argv[2] = m->generator_unit_path_early;
|
||||
argv[3] = m->generator_unit_path_late;
|
||||
argv[1] = m->lookup_paths.generator;
|
||||
argv[2] = m->lookup_paths.generator_early;
|
||||
argv[3] = m->lookup_paths.generator_late;
|
||||
argv[4] = NULL;
|
||||
|
||||
RUN_WITH_UMASK(0022)
|
||||
execute_directories((const char* const*) paths, DEFAULT_TIMEOUT_USEC, (char**) argv);
|
||||
|
||||
finish:
|
||||
trim_generator_dir(m, &m->generator_unit_path);
|
||||
trim_generator_dir(m, &m->generator_unit_path_early);
|
||||
trim_generator_dir(m, &m->generator_unit_path_late);
|
||||
lookup_paths_trim_generator(&m->lookup_paths);
|
||||
return r;
|
||||
}
|
||||
|
||||
static void remove_generator_dir(Manager *m, char **generator) {
|
||||
assert(m);
|
||||
assert(generator);
|
||||
|
||||
if (!*generator)
|
||||
return;
|
||||
|
||||
strv_remove(m->lookup_paths.unit_path, *generator);
|
||||
(void) rm_rf(*generator, REMOVE_ROOT);
|
||||
|
||||
*generator = mfree(*generator);
|
||||
}
|
||||
|
||||
static void manager_undo_generators(Manager *m) {
|
||||
assert(m);
|
||||
|
||||
remove_generator_dir(m, &m->generator_unit_path);
|
||||
remove_generator_dir(m, &m->generator_unit_path_early);
|
||||
remove_generator_dir(m, &m->generator_unit_path_late);
|
||||
}
|
||||
|
||||
int manager_environment_add(Manager *m, char **minus, char **plus) {
|
||||
char **a = NULL, **b = NULL, **l;
|
||||
assert(m);
|
||||
@ -2941,7 +2811,7 @@ void manager_recheck_journal(Manager *m) {
|
||||
|
||||
assert(m);
|
||||
|
||||
if (m->running_as != MANAGER_SYSTEM)
|
||||
if (!MANAGER_IS_SYSTEM(m))
|
||||
return;
|
||||
|
||||
u = manager_get_unit(m, SPECIAL_JOURNALD_SOCKET);
|
||||
@ -2965,7 +2835,7 @@ void manager_set_show_status(Manager *m, ShowStatus mode) {
|
||||
assert(m);
|
||||
assert(IN_SET(mode, SHOW_STATUS_AUTO, SHOW_STATUS_NO, SHOW_STATUS_YES, SHOW_STATUS_TEMPORARY));
|
||||
|
||||
if (m->running_as != MANAGER_SYSTEM)
|
||||
if (!MANAGER_IS_SYSTEM(m))
|
||||
return;
|
||||
|
||||
if (m->show_status != mode)
|
||||
@ -2982,7 +2852,7 @@ void manager_set_show_status(Manager *m, ShowStatus mode) {
|
||||
static bool manager_get_show_status(Manager *m, StatusType type) {
|
||||
assert(m);
|
||||
|
||||
if (m->running_as != MANAGER_SYSTEM)
|
||||
if (!MANAGER_IS_SYSTEM(m))
|
||||
return false;
|
||||
|
||||
if (m->no_console_output)
|
||||
@ -3004,7 +2874,7 @@ static bool manager_get_show_status(Manager *m, StatusType type) {
|
||||
void manager_set_first_boot(Manager *m, bool b) {
|
||||
assert(m);
|
||||
|
||||
if (m->running_as != MANAGER_SYSTEM)
|
||||
if (!MANAGER_IS_SYSTEM(m))
|
||||
return;
|
||||
|
||||
if (m->first_boot != (int) b) {
|
||||
@ -3050,7 +2920,7 @@ Set *manager_get_units_requiring_mounts_for(Manager *m, const char *path) {
|
||||
const char *manager_get_runtime_prefix(Manager *m) {
|
||||
assert(m);
|
||||
|
||||
return m->running_as == MANAGER_SYSTEM ?
|
||||
return MANAGER_IS_SYSTEM(m) ?
|
||||
"/run" :
|
||||
getenv("XDG_RUNTIME_DIR");
|
||||
}
|
||||
|
@ -140,6 +140,7 @@ struct Manager {
|
||||
|
||||
sd_event_source *jobs_in_progress_event_source;
|
||||
|
||||
UnitFileScope unit_file_scope;
|
||||
LookupPaths lookup_paths;
|
||||
Set *unit_path_cache;
|
||||
|
||||
@ -162,10 +163,6 @@ struct Manager {
|
||||
dual_timestamp units_load_start_timestamp;
|
||||
dual_timestamp units_load_finish_timestamp;
|
||||
|
||||
char *generator_unit_path;
|
||||
char *generator_unit_path_early;
|
||||
char *generator_unit_path_late;
|
||||
|
||||
struct udev* udev;
|
||||
|
||||
/* Data specific to the device subsystem */
|
||||
@ -228,7 +225,6 @@ struct Manager {
|
||||
unsigned n_in_gc_queue;
|
||||
|
||||
/* Flags */
|
||||
ManagerRunningAs running_as;
|
||||
ManagerExitCode exit_code:5;
|
||||
|
||||
bool dispatching_load_queue:1;
|
||||
@ -304,10 +300,15 @@ struct Manager {
|
||||
const char *unit_log_field;
|
||||
const char *unit_log_format_string;
|
||||
|
||||
int first_boot;
|
||||
int first_boot; /* tri-state */
|
||||
};
|
||||
|
||||
int manager_new(ManagerRunningAs running_as, bool test_run, Manager **m);
|
||||
#define MANAGER_IS_SYSTEM(m) ((m)->unit_file_scope == UNIT_FILE_SYSTEM)
|
||||
#define MANAGER_IS_USER(m) ((m)->unit_file_scope != UNIT_FILE_SYSTEM)
|
||||
|
||||
#define MANAGER_IS_RELOADING(m) ((m)->n_reloading > 0)
|
||||
|
||||
int manager_new(UnitFileScope scope, bool test_run, Manager **m);
|
||||
Manager* manager_free(Manager *m);
|
||||
|
||||
void manager_enumerate(Manager *m);
|
||||
@ -345,8 +346,6 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds);
|
||||
|
||||
int manager_reload(Manager *m);
|
||||
|
||||
bool manager_is_reloading_or_reexecuting(Manager *m) _pure_;
|
||||
|
||||
void manager_reset_failed(Manager *m);
|
||||
|
||||
void manager_send_unit_audit(Manager *m, Unit *u, int type, bool success);
|
||||
|
@ -336,8 +336,7 @@ static int mount_add_device_links(Mount *m) {
|
||||
if (path_equal(m->where, "/"))
|
||||
return 0;
|
||||
|
||||
if (mount_is_auto(p) && !mount_is_automount(p) &&
|
||||
UNIT(m)->manager->running_as == MANAGER_SYSTEM)
|
||||
if (mount_is_auto(p) && !mount_is_automount(p) && MANAGER_IS_SYSTEM(UNIT(m)->manager))
|
||||
device_wants_mount = true;
|
||||
|
||||
r = unit_add_node_link(UNIT(m), p->what, device_wants_mount, m->from_fragment ? UNIT_BINDS_TO : UNIT_REQUIRES);
|
||||
@ -353,7 +352,7 @@ static int mount_add_quota_links(Mount *m) {
|
||||
|
||||
assert(m);
|
||||
|
||||
if (UNIT(m)->manager->running_as != MANAGER_SYSTEM)
|
||||
if (!MANAGER_IS_SYSTEM(UNIT(m)->manager))
|
||||
return 0;
|
||||
|
||||
p = get_mount_parameters_fragment(m);
|
||||
@ -400,7 +399,7 @@ static int mount_add_default_dependencies(Mount *m) {
|
||||
if (!UNIT(m)->default_dependencies)
|
||||
return 0;
|
||||
|
||||
if (UNIT(m)->manager->running_as != MANAGER_SYSTEM)
|
||||
if (!MANAGER_IS_SYSTEM(UNIT(m)->manager))
|
||||
return 0;
|
||||
|
||||
/* We do not add any default dependencies to /, /usr or
|
||||
@ -1396,7 +1395,7 @@ static int mount_setup_unit(
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (m->running_as == MANAGER_SYSTEM) {
|
||||
if (MANAGER_IS_SYSTEM(m)) {
|
||||
const char* target;
|
||||
|
||||
target = mount_needs_network(options, fstype) ? SPECIAL_REMOTE_FS_TARGET : SPECIAL_LOCAL_FS_TARGET;
|
||||
@ -1424,7 +1423,7 @@ static int mount_setup_unit(
|
||||
}
|
||||
}
|
||||
|
||||
if (m->running_as == MANAGER_SYSTEM &&
|
||||
if (MANAGER_IS_SYSTEM(m) &&
|
||||
mount_needs_network(options, fstype)) {
|
||||
/* _netdev option may have shown up late, or on a
|
||||
* remount. Add remote-fs dependencies, even though
|
||||
|
@ -174,6 +174,10 @@
|
||||
send_interface="org.freedesktop.systemd1.Manager"
|
||||
send_member="LinkUnitFiles"/>
|
||||
|
||||
<allow send_destination="org.freedesktop.systemd1"
|
||||
send_interface="org.freedesktop.systemd1.Manager"
|
||||
send_member="RevertUnitFiles"/>
|
||||
|
||||
<allow send_destination="org.freedesktop.systemd1"
|
||||
send_interface="org.freedesktop.systemd1.Manager"
|
||||
send_member="PresetUnitFiles"/>
|
||||
|
@ -318,7 +318,7 @@ static int path_add_default_dependencies(Path *p) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (UNIT(p)->manager->running_as == MANAGER_SYSTEM) {
|
||||
if (MANAGER_IS_SYSTEM(UNIT(p)->manager)) {
|
||||
r = unit_add_two_dependencies_by_name(UNIT(p), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
@ -138,7 +138,7 @@ static int scope_verify(Scope *s) {
|
||||
return 0;
|
||||
|
||||
if (set_isempty(UNIT(s)->pids) &&
|
||||
!manager_is_reloading_or_reexecuting(UNIT(s)->manager) &&
|
||||
!MANAGER_IS_RELOADING(UNIT(s)->manager) &&
|
||||
!unit_has_name(UNIT(s), SPECIAL_INIT_SCOPE)) {
|
||||
log_unit_error(UNIT(s), "Scope has no PIDs. Refusing.");
|
||||
return -EINVAL;
|
||||
@ -154,26 +154,27 @@ static int scope_load(Unit *u) {
|
||||
assert(s);
|
||||
assert(u->load_state == UNIT_STUB);
|
||||
|
||||
if (!u->transient && !manager_is_reloading_or_reexecuting(u->manager))
|
||||
if (!u->transient && !MANAGER_IS_RELOADING(u->manager))
|
||||
/* Refuse to load non-transient scope units, but allow them while reloading. */
|
||||
return -ENOENT;
|
||||
|
||||
u->load_state = UNIT_LOADED;
|
||||
|
||||
r = unit_load_dropin(u);
|
||||
r = unit_load_fragment_and_dropin_optional(u);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = unit_patch_contexts(u);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (u->load_state == UNIT_LOADED) {
|
||||
r = unit_patch_contexts(u);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = unit_set_default_slice(u);
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = unit_set_default_slice(u);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = scope_add_default_dependencies(s);
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = scope_add_default_dependencies(s);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return scope_verify(s);
|
||||
}
|
||||
@ -292,7 +293,7 @@ static int scope_start(Unit *u) {
|
||||
|
||||
assert(s->state == SCOPE_DEAD);
|
||||
|
||||
if (!u->transient && !manager_is_reloading_or_reexecuting(u->manager))
|
||||
if (!u->transient && !MANAGER_IS_RELOADING(u->manager))
|
||||
return -ENOENT;
|
||||
|
||||
(void) unit_realize_cgroup(u);
|
||||
|
@ -523,7 +523,7 @@ static int service_add_default_dependencies(Service *s) {
|
||||
/* Add a number of automatic dependencies useful for the
|
||||
* majority of services. */
|
||||
|
||||
if (UNIT(s)->manager->running_as == MANAGER_SYSTEM) {
|
||||
if (MANAGER_IS_SYSTEM(UNIT(s)->manager)) {
|
||||
/* First, pull in the really early boot stuff, and
|
||||
* require it, so that we fail if we can't acquire
|
||||
* it. */
|
||||
@ -920,7 +920,7 @@ static void service_set_state(Service *s, ServiceState state) {
|
||||
|
||||
/* For the inactive states unit_notify() will trim the cgroup,
|
||||
* but for exit we have to do that ourselves... */
|
||||
if (state == SERVICE_EXITED && UNIT(s)->manager->n_reloading <= 0)
|
||||
if (state == SERVICE_EXITED && !MANAGER_IS_RELOADING(UNIT(s)->manager))
|
||||
unit_prune_cgroup(UNIT(s));
|
||||
|
||||
/* For remain_after_exit services, let's see if we can "release" the
|
||||
@ -1211,7 +1211,7 @@ static int service_spawn(
|
||||
if (asprintf(our_env + n_env++, "MAINPID="PID_FMT, s->main_pid) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
if (UNIT(s)->manager->running_as != MANAGER_SYSTEM)
|
||||
if (!MANAGER_IS_SYSTEM(UNIT(s)->manager))
|
||||
if (asprintf(our_env + n_env++, "MANAGERPID="PID_FMT, getpid()) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -397,9 +397,14 @@ int main(int argc, char *argv[]) {
|
||||
if (!in_container) {
|
||||
_cleanup_free_ char *param = NULL;
|
||||
|
||||
if (read_one_line_file(REBOOT_PARAM_FILE, ¶m) >= 0) {
|
||||
r = read_one_line_file("/run/systemd/reboot-param", ¶m);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to read reboot parameter file: %m");
|
||||
|
||||
if (!isempty(param)) {
|
||||
log_info("Rebooting with argument '%s'.", param);
|
||||
syscall(SYS_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART2, param);
|
||||
log_warning_errno(errno, "Failed to reboot with parameter, retrying without: %m");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -135,6 +135,7 @@ static int slice_load(Unit *u) {
|
||||
int r;
|
||||
|
||||
assert(s);
|
||||
assert(u->load_state == UNIT_STUB);
|
||||
|
||||
r = unit_load_fragment_and_dropin_optional(u);
|
||||
if (r < 0)
|
||||
|
@ -301,7 +301,7 @@ static int socket_add_default_dependencies(Socket *s) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (UNIT(s)->manager->running_as == MANAGER_SYSTEM) {
|
||||
if (MANAGER_IS_SYSTEM(UNIT(s)->manager)) {
|
||||
r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
@ -198,7 +198,7 @@ static int swap_add_device_links(Swap *s) {
|
||||
return 0;
|
||||
|
||||
if (is_device_path(s->what))
|
||||
return unit_add_node_link(UNIT(s), s->what, UNIT(s)->manager->running_as == MANAGER_SYSTEM, UNIT_BINDS_TO);
|
||||
return unit_add_node_link(UNIT(s), s->what, MANAGER_IS_SYSTEM(UNIT(s)->manager), UNIT_BINDS_TO);
|
||||
else
|
||||
/* File based swap devices need to be ordered after
|
||||
* systemd-remount-fs.service, since they might need a
|
||||
@ -214,7 +214,7 @@ static int swap_add_default_dependencies(Swap *s) {
|
||||
if (!UNIT(s)->default_dependencies)
|
||||
return 0;
|
||||
|
||||
if (UNIT(s)->manager->running_as != MANAGER_SYSTEM)
|
||||
if (!MANAGER_IS_SYSTEM(UNIT(s)->manager))
|
||||
return 0;
|
||||
|
||||
if (detect_container() > 0)
|
||||
|
@ -109,7 +109,7 @@ static int timer_add_default_dependencies(Timer *t) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (UNIT(t)->manager->running_as == MANAGER_SYSTEM) {
|
||||
if (MANAGER_IS_SYSTEM(UNIT(t)->manager)) {
|
||||
r = unit_add_two_dependencies_by_name(UNIT(t), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@ -135,7 +135,7 @@ static int timer_setup_persistent(Timer *t) {
|
||||
if (!t->persistent)
|
||||
return 0;
|
||||
|
||||
if (UNIT(t)->manager->running_as == MANAGER_SYSTEM) {
|
||||
if (MANAGER_IS_SYSTEM(UNIT(t)->manager)) {
|
||||
|
||||
r = unit_require_mounts_for(UNIT(t), "/var/lib/systemd/timers");
|
||||
if (r < 0)
|
||||
|
@ -855,7 +855,7 @@ int transaction_add_job_and_dependencies(
|
||||
* This matters when jobs are spawned as part of coldplugging itself (see e. g. path_coldplug()).
|
||||
* This way, we "recursively" coldplug units, ensuring that we do not look at state of
|
||||
* not-yet-coldplugged units. */
|
||||
if (unit->manager->n_reloading > 0)
|
||||
if (MANAGER_IS_RELOADING(unit->manager))
|
||||
unit_coldplug(unit);
|
||||
|
||||
/* log_debug("Pulling in %s/%s from %s/%s", */
|
||||
|
@ -140,14 +140,9 @@ static int specifier_runtime(char specifier, void *data, void *userdata, char **
|
||||
|
||||
assert(u);
|
||||
|
||||
if (u->manager->running_as == MANAGER_SYSTEM)
|
||||
e = "/run";
|
||||
else {
|
||||
e = getenv("XDG_RUNTIME_DIR");
|
||||
if (!e)
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
e = manager_get_runtime_prefix(u->manager);
|
||||
if (!e)
|
||||
return -EOPNOTSUPP;
|
||||
n = strdup(e);
|
||||
if (!n)
|
||||
return -ENOMEM;
|
||||
|
165
src/core/unit.c
165
src/core/unit.c
@ -47,11 +47,13 @@
|
||||
#include "path-util.h"
|
||||
#include "process-util.h"
|
||||
#include "set.h"
|
||||
#include "signal-util.h"
|
||||
#include "special.h"
|
||||
#include "stat-util.h"
|
||||
#include "stdio-util.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
#include "umask-util.h"
|
||||
#include "unit-name.h"
|
||||
#include "unit.h"
|
||||
#include "user-util.h"
|
||||
@ -418,13 +420,22 @@ static void unit_remove_transient(Unit *u) {
|
||||
(void) unlink(u->fragment_path);
|
||||
|
||||
STRV_FOREACH(i, u->dropin_paths) {
|
||||
_cleanup_free_ char *p = NULL;
|
||||
_cleanup_free_ char *p = NULL, *pp = NULL;
|
||||
|
||||
p = dirname_malloc(*i); /* Get the drop-in directory from the drop-in file */
|
||||
if (!p)
|
||||
continue;
|
||||
|
||||
pp = dirname_malloc(p); /* Get the config directory from the drop-in directory */
|
||||
if (!pp)
|
||||
continue;
|
||||
|
||||
/* Only drop transient drop-ins */
|
||||
if (!path_equal(u->manager->lookup_paths.transient, pp))
|
||||
continue;
|
||||
|
||||
(void) unlink(*i);
|
||||
|
||||
p = dirname_malloc(*i);
|
||||
if (p)
|
||||
(void) rmdir(p);
|
||||
(void) rmdir(p);
|
||||
}
|
||||
}
|
||||
|
||||
@ -483,7 +494,10 @@ void unit_free(Unit *u) {
|
||||
|
||||
assert(u);
|
||||
|
||||
if (u->manager->n_reloading <= 0)
|
||||
if (u->transient_file)
|
||||
fclose(u->transient_file);
|
||||
|
||||
if (!MANAGER_IS_RELOADING(u->manager))
|
||||
unit_remove_transient(u);
|
||||
|
||||
bus_unit_send_removed_signal(u);
|
||||
@ -814,7 +828,7 @@ int unit_add_exec_dependencies(Unit *u, ExecContext *c) {
|
||||
return r;
|
||||
}
|
||||
|
||||
if (u->manager->running_as != MANAGER_SYSTEM)
|
||||
if (!MANAGER_IS_SYSTEM(u->manager))
|
||||
return 0;
|
||||
|
||||
if (c->private_tmp) {
|
||||
@ -1222,6 +1236,17 @@ int unit_load(Unit *u) {
|
||||
if (u->load_state != UNIT_STUB)
|
||||
return 0;
|
||||
|
||||
if (u->transient_file) {
|
||||
r = fflush_and_check(u->transient_file);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
fclose(u->transient_file);
|
||||
u->transient_file = NULL;
|
||||
|
||||
u->dropin_mtime = now(CLOCK_REALTIME);
|
||||
}
|
||||
|
||||
if (UNIT_VTABLE(u)->load) {
|
||||
r = UNIT_VTABLE(u)->load(u);
|
||||
if (r < 0)
|
||||
@ -1834,7 +1859,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
|
||||
m = u->manager;
|
||||
|
||||
/* Update timestamps for state changes */
|
||||
if (m->n_reloading <= 0) {
|
||||
if (!MANAGER_IS_RELOADING(m)) {
|
||||
dual_timestamp_get(&u->state_change_timestamp);
|
||||
|
||||
if (UNIT_IS_INACTIVE_OR_FAILED(os) && !UNIT_IS_INACTIVE_OR_FAILED(ns))
|
||||
@ -1941,7 +1966,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
|
||||
} else
|
||||
unexpected = true;
|
||||
|
||||
if (m->n_reloading <= 0) {
|
||||
if (!MANAGER_IS_RELOADING(m)) {
|
||||
|
||||
/* If this state change happened without being
|
||||
* requested by a job, then let's retroactively start
|
||||
@ -1978,7 +2003,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
|
||||
|
||||
if (u->type == UNIT_SERVICE &&
|
||||
!UNIT_IS_ACTIVE_OR_RELOADING(os) &&
|
||||
m->n_reloading <= 0) {
|
||||
!MANAGER_IS_RELOADING(m)) {
|
||||
/* Write audit record if we have just finished starting up */
|
||||
manager_send_unit_audit(m, u, AUDIT_SERVICE_START, true);
|
||||
u->in_audit = true;
|
||||
@ -1995,7 +2020,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
|
||||
if (u->type == UNIT_SERVICE &&
|
||||
UNIT_IS_INACTIVE_OR_FAILED(ns) &&
|
||||
!UNIT_IS_INACTIVE_OR_FAILED(os) &&
|
||||
m->n_reloading <= 0) {
|
||||
!MANAGER_IS_RELOADING(m)) {
|
||||
|
||||
/* Hmm, if there was no start record written
|
||||
* write it now, so that we always have a nice
|
||||
@ -2016,7 +2041,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
|
||||
manager_recheck_journal(m);
|
||||
unit_trigger_notify(u);
|
||||
|
||||
if (u->manager->n_reloading <= 0) {
|
||||
if (!MANAGER_IS_RELOADING(u->manager)) {
|
||||
/* Maybe we finished startup and are now ready for
|
||||
* being stopped because unneeded? */
|
||||
unit_check_unneeded(u);
|
||||
@ -2413,7 +2438,7 @@ int unit_set_default_slice(Unit *u) {
|
||||
if (!escaped)
|
||||
return -ENOMEM;
|
||||
|
||||
if (u->manager->running_as == MANAGER_SYSTEM)
|
||||
if (MANAGER_IS_SYSTEM(u->manager))
|
||||
b = strjoin("system-", escaped, ".slice", NULL);
|
||||
else
|
||||
b = strappend(escaped, ".slice");
|
||||
@ -2423,7 +2448,7 @@ int unit_set_default_slice(Unit *u) {
|
||||
slice_name = b;
|
||||
} else
|
||||
slice_name =
|
||||
u->manager->running_as == MANAGER_SYSTEM && !unit_has_name(u, SPECIAL_INIT_SCOPE)
|
||||
MANAGER_IS_SYSTEM(u->manager) && !unit_has_name(u, SPECIAL_INIT_SCOPE)
|
||||
? SPECIAL_SYSTEM_SLICE
|
||||
: SPECIAL_ROOT_SLICE;
|
||||
|
||||
@ -2884,7 +2909,7 @@ int unit_add_node_link(Unit *u, const char *what, bool wants, UnitDependency dep
|
||||
return r;
|
||||
|
||||
r = unit_add_two_dependencies(u, UNIT_AFTER,
|
||||
u->manager->running_as == MANAGER_SYSTEM ? dep : UNIT_WANTS,
|
||||
MANAGER_IS_SYSTEM(u->manager) ? dep : UNIT_WANTS,
|
||||
device, true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@ -3040,8 +3065,7 @@ bool unit_active_or_pending(Unit *u) {
|
||||
int unit_kill(Unit *u, KillWho w, int signo, sd_bus_error *error) {
|
||||
assert(u);
|
||||
assert(w >= 0 && w < _KILL_WHO_MAX);
|
||||
assert(signo > 0);
|
||||
assert(signo < _NSIG);
|
||||
assert(SIGNAL_VALID(signo));
|
||||
|
||||
if (!UNIT_VTABLE(u)->kill)
|
||||
return -EOPNOTSUPP;
|
||||
@ -3158,7 +3182,7 @@ UnitFileState unit_get_unit_file_state(Unit *u) {
|
||||
|
||||
if (u->unit_file_state < 0 && u->fragment_path) {
|
||||
r = unit_file_get_state(
|
||||
u->manager->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER,
|
||||
u->manager->unit_file_scope,
|
||||
NULL,
|
||||
basename(u->fragment_path),
|
||||
&u->unit_file_state);
|
||||
@ -3174,7 +3198,7 @@ int unit_get_unit_file_preset(Unit *u) {
|
||||
|
||||
if (u->unit_file_preset < 0 && u->fragment_path)
|
||||
u->unit_file_preset = unit_file_query_preset(
|
||||
u->manager->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER,
|
||||
u->manager->unit_file_scope,
|
||||
NULL,
|
||||
basename(u->fragment_path));
|
||||
|
||||
@ -3225,7 +3249,7 @@ int unit_patch_contexts(Unit *u) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (u->manager->running_as == MANAGER_USER &&
|
||||
if (MANAGER_IS_USER(u->manager) &&
|
||||
!ec->working_directory) {
|
||||
|
||||
r = get_home_dir(&ec->working_directory);
|
||||
@ -3237,7 +3261,7 @@ int unit_patch_contexts(Unit *u) {
|
||||
ec->working_directory_missing_ok = true;
|
||||
}
|
||||
|
||||
if (u->manager->running_as == MANAGER_USER &&
|
||||
if (MANAGER_IS_USER(u->manager) &&
|
||||
(ec->syscall_whitelist ||
|
||||
!set_isempty(ec->syscall_filter) ||
|
||||
!set_isempty(ec->syscall_archs) ||
|
||||
@ -3315,59 +3339,62 @@ ExecRuntime *unit_get_exec_runtime(Unit *u) {
|
||||
return *(ExecRuntime**) ((uint8_t*) u + offset);
|
||||
}
|
||||
|
||||
static int unit_drop_in_dir(Unit *u, UnitSetPropertiesMode mode, bool transient, char **dir) {
|
||||
static const char* unit_drop_in_dir(Unit *u, UnitSetPropertiesMode mode) {
|
||||
assert(u);
|
||||
|
||||
if (u->manager->running_as == MANAGER_USER) {
|
||||
int r;
|
||||
if (!IN_SET(mode, UNIT_RUNTIME, UNIT_PERSISTENT))
|
||||
return NULL;
|
||||
|
||||
if (mode == UNIT_PERSISTENT && !transient)
|
||||
r = user_config_home(dir);
|
||||
else
|
||||
r = user_runtime_dir(dir);
|
||||
if (r == 0)
|
||||
return -ENOENT;
|
||||
if (u->transient) /* Redirect drop-ins for transient units always into the transient directory. */
|
||||
return u->manager->lookup_paths.transient;
|
||||
|
||||
return r;
|
||||
}
|
||||
if (mode == UNIT_RUNTIME)
|
||||
return u->manager->lookup_paths.runtime_control;
|
||||
|
||||
if (mode == UNIT_PERSISTENT && !transient)
|
||||
*dir = strdup("/etc/systemd/system");
|
||||
else
|
||||
*dir = strdup("/run/systemd/system");
|
||||
if (!*dir)
|
||||
return -ENOMEM;
|
||||
if (mode == UNIT_PERSISTENT)
|
||||
return u->manager->lookup_paths.persistent_control;
|
||||
|
||||
return 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int unit_write_drop_in(Unit *u, UnitSetPropertiesMode mode, const char *name, const char *data) {
|
||||
|
||||
_cleanup_free_ char *dir = NULL, *p = NULL, *q = NULL;
|
||||
_cleanup_free_ char *p = NULL, *q = NULL;
|
||||
const char *dir, *prefixed;
|
||||
int r;
|
||||
|
||||
assert(u);
|
||||
|
||||
if (u->transient_file) {
|
||||
/* When this is a transient unit file in creation, then let's not create a new drop-in but instead
|
||||
* write to the transient unit file. */
|
||||
fputs(data, u->transient_file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!IN_SET(mode, UNIT_PERSISTENT, UNIT_RUNTIME))
|
||||
return 0;
|
||||
|
||||
r = unit_drop_in_dir(u, mode, u->transient, &dir);
|
||||
if (r < 0)
|
||||
return r;
|
||||
dir = unit_drop_in_dir(u, mode);
|
||||
if (!dir)
|
||||
return -EINVAL;
|
||||
|
||||
r = write_drop_in(dir, u->id, 50, name, data);
|
||||
if (r < 0)
|
||||
return r;
|
||||
prefixed = strjoina("# This is a drop-in unit file extension, created via \"systemctl set-property\" or an equivalent operation. Do not edit.\n",
|
||||
data);
|
||||
|
||||
r = drop_in_file(dir, u->id, 50, name, &p, &q);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = strv_extend(&u->dropin_paths, q);
|
||||
(void) mkdir_p(p, 0755);
|
||||
r = write_string_file_atomic_label(q, prefixed);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
strv_sort(u->dropin_paths);
|
||||
r = strv_push(&u->dropin_paths, q);
|
||||
if (r < 0)
|
||||
return r;
|
||||
q = NULL;
|
||||
|
||||
strv_uniq(u->dropin_paths);
|
||||
|
||||
u->dropin_mtime = now(CLOCK_REALTIME);
|
||||
@ -3398,7 +3425,7 @@ int unit_write_drop_in_format(Unit *u, UnitSetPropertiesMode mode, const char *n
|
||||
}
|
||||
|
||||
int unit_write_drop_in_private(Unit *u, UnitSetPropertiesMode mode, const char *name, const char *data) {
|
||||
_cleanup_free_ char *ndata = NULL;
|
||||
const char *ndata;
|
||||
|
||||
assert(u);
|
||||
assert(name);
|
||||
@ -3410,9 +3437,7 @@ int unit_write_drop_in_private(Unit *u, UnitSetPropertiesMode mode, const char *
|
||||
if (!IN_SET(mode, UNIT_PERSISTENT, UNIT_RUNTIME))
|
||||
return 0;
|
||||
|
||||
ndata = strjoin("[", UNIT_VTABLE(u)->private_section, "]\n", data, NULL);
|
||||
if (!ndata)
|
||||
return -ENOMEM;
|
||||
ndata = strjoina("[", UNIT_VTABLE(u)->private_section, "]\n", data, NULL);
|
||||
|
||||
return unit_write_drop_in(u, mode, name, ndata);
|
||||
}
|
||||
@ -3440,24 +3465,50 @@ int unit_write_drop_in_private_format(Unit *u, UnitSetPropertiesMode mode, const
|
||||
}
|
||||
|
||||
int unit_make_transient(Unit *u) {
|
||||
FILE *f;
|
||||
char *path;
|
||||
|
||||
assert(u);
|
||||
|
||||
if (!UNIT_VTABLE(u)->can_transient)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
u->load_state = UNIT_STUB;
|
||||
u->load_error = 0;
|
||||
u->transient = true;
|
||||
path = strjoin(u->manager->lookup_paths.transient, "/", u->id, NULL);
|
||||
if (!path)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Let's open the file we'll write the transient settings into. This file is kept open as long as we are
|
||||
* creating the transient, and is closed in unit_load(), as soon as we start loading the file. */
|
||||
|
||||
RUN_WITH_UMASK(0022)
|
||||
f = fopen(path, "we");
|
||||
if (!f) {
|
||||
free(path);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if (u->transient_file)
|
||||
fclose(u->transient_file);
|
||||
u->transient_file = f;
|
||||
|
||||
free(u->fragment_path);
|
||||
u->fragment_path = path;
|
||||
|
||||
u->fragment_path = mfree(u->fragment_path);
|
||||
u->source_path = mfree(u->source_path);
|
||||
u->dropin_paths = strv_free(u->dropin_paths);
|
||||
u->fragment_mtime = u->source_mtime = u->dropin_mtime = 0;
|
||||
|
||||
u->load_state = UNIT_STUB;
|
||||
u->load_error = 0;
|
||||
u->transient = true;
|
||||
|
||||
unit_add_to_dbus_queue(u);
|
||||
unit_add_to_gc_queue(u);
|
||||
unit_add_to_load_queue(u);
|
||||
|
||||
fputs("# This is a transient unit file, created programmatically via the systemd API. Do not edit.\n",
|
||||
u->transient_file);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -95,6 +95,9 @@ struct Unit {
|
||||
usec_t source_mtime;
|
||||
usec_t dropin_mtime;
|
||||
|
||||
/* If this is a transient unit we are currently writing, this is where we are writing it to */
|
||||
FILE *transient_file;
|
||||
|
||||
/* If there is something to do with this unit, then this is the installed job for it */
|
||||
Job *job;
|
||||
|
||||
|
@ -44,15 +44,6 @@ typedef enum PullJobState {
|
||||
|
||||
#define PULL_JOB_IS_COMPLETE(j) (IN_SET((j)->state, PULL_JOB_DONE, PULL_JOB_FAILED))
|
||||
|
||||
typedef enum PullJobCompression {
|
||||
PULL_JOB_UNCOMPRESSED,
|
||||
PULL_JOB_XZ,
|
||||
PULL_JOB_GZIP,
|
||||
PULL_JOB_BZIP2,
|
||||
_PULL_JOB_COMPRESSION_MAX,
|
||||
_PULL_JOB_COMPRESSION_INVALID = -1,
|
||||
} PullJobCompression;
|
||||
|
||||
struct PullJob {
|
||||
PullJobState state;
|
||||
int error;
|
||||
|
@ -38,6 +38,8 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = {
|
||||
SD_BUS_ERROR_MAP(BUS_ERROR_TRANSACTION_ORDER_IS_CYCLIC, EDEADLK),
|
||||
SD_BUS_ERROR_MAP(BUS_ERROR_TRANSACTION_IS_DESTRUCTIVE, EDEADLK),
|
||||
SD_BUS_ERROR_MAP(BUS_ERROR_UNIT_MASKED, ESHUTDOWN),
|
||||
SD_BUS_ERROR_MAP(BUS_ERROR_UNIT_GENERATED, EADDRNOTAVAIL),
|
||||
SD_BUS_ERROR_MAP(BUS_ERROR_UNIT_LINKED, ELOOP),
|
||||
SD_BUS_ERROR_MAP(BUS_ERROR_JOB_TYPE_NOT_APPLICABLE, EBADR),
|
||||
SD_BUS_ERROR_MAP(BUS_ERROR_NO_ISOLATION, EPERM),
|
||||
SD_BUS_ERROR_MAP(BUS_ERROR_SHUTTING_DOWN, ECANCELED),
|
||||
|
@ -34,6 +34,8 @@
|
||||
#define BUS_ERROR_TRANSACTION_ORDER_IS_CYCLIC "org.freedesktop.systemd1.TransactionOrderIsCyclic"
|
||||
#define BUS_ERROR_TRANSACTION_IS_DESTRUCTIVE "org.freedesktop.systemd1.TransactionIsDestructive"
|
||||
#define BUS_ERROR_UNIT_MASKED "org.freedesktop.systemd1.UnitMasked"
|
||||
#define BUS_ERROR_UNIT_GENERATED "org.freedesktop.systemd1.UnitGenerated"
|
||||
#define BUS_ERROR_UNIT_LINKED "org.freedesktop.systemd1.UnitLinked"
|
||||
#define BUS_ERROR_JOB_TYPE_NOT_APPLICABLE "org.freedesktop.systemd1.JobTypeNotApplicable"
|
||||
#define BUS_ERROR_NO_ISOLATION "org.freedesktop.systemd1.NoIsolation"
|
||||
#define BUS_ERROR_SHUTTING_DOWN "org.freedesktop.systemd1.ShuttingDown"
|
||||
|
@ -1145,8 +1145,7 @@ _public_ int sd_event_add_signal(
|
||||
int r;
|
||||
|
||||
assert_return(e, -EINVAL);
|
||||
assert_return(sig > 0, -EINVAL);
|
||||
assert_return(sig < _NSIG, -EINVAL);
|
||||
assert_return(SIGNAL_VALID(sig), -EINVAL);
|
||||
assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
|
||||
assert_return(!event_pid_changed(e), -ECHILD);
|
||||
|
||||
@ -2200,7 +2199,7 @@ static int process_signal(sd_event *e, struct signal_data *d, uint32_t events) {
|
||||
if (_unlikely_(n != sizeof(si)))
|
||||
return -EIO;
|
||||
|
||||
assert(si.ssi_signo < _NSIG);
|
||||
assert(SIGNAL_VALID(si.ssi_signo));
|
||||
|
||||
read_one = true;
|
||||
|
||||
|
@ -24,6 +24,7 @@
|
||||
|
||||
#ifdef HAVE_XKBCOMMON
|
||||
#include <xkbcommon/xkbcommon.h>
|
||||
#include <dlfcn.h>
|
||||
#endif
|
||||
|
||||
#include "sd-bus.h"
|
||||
@ -1101,6 +1102,7 @@ static int method_set_vc_keyboard(sd_bus_message *m, void *userdata, sd_bus_erro
|
||||
}
|
||||
|
||||
#ifdef HAVE_XKBCOMMON
|
||||
|
||||
_printf_(3, 0)
|
||||
static void log_xkb(struct xkb_context *ctx, enum xkb_log_level lvl, const char *format, va_list args) {
|
||||
const char *fmt;
|
||||
@ -1109,7 +1111,24 @@ static void log_xkb(struct xkb_context *ctx, enum xkb_log_level lvl, const char
|
||||
log_internalv(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, fmt, args);
|
||||
}
|
||||
|
||||
#define LOAD_SYMBOL(symbol, dl, name) \
|
||||
({ \
|
||||
(symbol) = (typeof(symbol)) dlvsym((dl), (name), "V_0.5.0"); \
|
||||
(symbol) ? 0 : -EOPNOTSUPP; \
|
||||
})
|
||||
|
||||
static int verify_xkb_rmlvo(const char *model, const char *layout, const char *variant, const char *options) {
|
||||
|
||||
/* We dlopen() the library in order to make the dependency soft. The library (and what it pulls in) is huge
|
||||
* after all, hence let's support XKB maps when the library is around, and refuse otherwise. The function
|
||||
* pointers to the shared library are below: */
|
||||
|
||||
struct xkb_context* (*symbol_xkb_context_new)(enum xkb_context_flags flags) = NULL;
|
||||
void (*symbol_xkb_context_unref)(struct xkb_context *context) = NULL;
|
||||
void (*symbol_xkb_context_set_log_fn)(struct xkb_context *context, void (*log_fn)(struct xkb_context *context, enum xkb_log_level level, const char *format, va_list args)) = NULL;
|
||||
struct xkb_keymap* (*symbol_xkb_keymap_new_from_names)(struct xkb_context *context, const struct xkb_rule_names *names, enum xkb_keymap_compile_flags flags) = NULL;
|
||||
void (*symbol_xkb_keymap_unref)(struct xkb_keymap *keymap) = NULL;
|
||||
|
||||
const struct xkb_rule_names rmlvo = {
|
||||
.model = model,
|
||||
.layout = layout,
|
||||
@ -1118,35 +1137,68 @@ static int verify_xkb_rmlvo(const char *model, const char *layout, const char *v
|
||||
};
|
||||
struct xkb_context *ctx = NULL;
|
||||
struct xkb_keymap *km = NULL;
|
||||
void *dl;
|
||||
int r;
|
||||
|
||||
/* compile keymap from RMLVO information to check out its validity */
|
||||
/* Compile keymap from RMLVO information to check out its validity */
|
||||
|
||||
ctx = xkb_context_new(XKB_CONTEXT_NO_ENVIRONMENT_NAMES);
|
||||
dl = dlopen("libxkbcommon.so.0", RTLD_LAZY);
|
||||
if (!dl)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
r = LOAD_SYMBOL(symbol_xkb_context_new, dl, "xkb_context_new");
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
|
||||
r = LOAD_SYMBOL(symbol_xkb_context_unref, dl, "xkb_context_unref");
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
|
||||
r = LOAD_SYMBOL(symbol_xkb_context_set_log_fn, dl, "xkb_context_set_log_fn");
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
|
||||
r = LOAD_SYMBOL(symbol_xkb_keymap_new_from_names, dl, "xkb_keymap_new_from_names");
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
|
||||
r = LOAD_SYMBOL(symbol_xkb_keymap_unref, dl, "xkb_keymap_unref");
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
|
||||
ctx = symbol_xkb_context_new(XKB_CONTEXT_NO_ENVIRONMENT_NAMES);
|
||||
if (!ctx) {
|
||||
r = -ENOMEM;
|
||||
goto exit;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
xkb_context_set_log_fn(ctx, log_xkb);
|
||||
symbol_xkb_context_set_log_fn(ctx, log_xkb);
|
||||
|
||||
km = xkb_keymap_new_from_names(ctx, &rmlvo, XKB_KEYMAP_COMPILE_NO_FLAGS);
|
||||
km = symbol_xkb_keymap_new_from_names(ctx, &rmlvo, XKB_KEYMAP_COMPILE_NO_FLAGS);
|
||||
if (!km) {
|
||||
r = -EINVAL;
|
||||
goto exit;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
r = 0;
|
||||
|
||||
exit:
|
||||
xkb_keymap_unref(km);
|
||||
xkb_context_unref(ctx);
|
||||
finish:
|
||||
if (symbol_xkb_keymap_unref && km)
|
||||
symbol_xkb_keymap_unref(km);
|
||||
|
||||
if (symbol_xkb_context_unref && ctx)
|
||||
symbol_xkb_context_unref(ctx);
|
||||
|
||||
(void) dlclose(dl);
|
||||
return r;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static int verify_xkb_rmlvo(const char *model, const char *layout, const char *variant, const char *options) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static int method_set_x11_keyboard(sd_bus_message *m, void *userdata, sd_bus_error *error) {
|
||||
@ -1203,7 +1255,11 @@ static int method_set_x11_keyboard(sd_bus_message *m, void *userdata, sd_bus_err
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Cannot compile XKB keymap for new x11 keyboard layout ('%s' / '%s' / '%s' / '%s'): %m",
|
||||
strempty(model), strempty(layout), strempty(variant), strempty(options));
|
||||
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Cannot compile XKB keymap, refusing");
|
||||
|
||||
if (r == -EOPNOTSUPP)
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Local keyboard configuration not supported on this system.");
|
||||
|
||||
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Specified keymap cannot be compiled, refusing as invalid.");
|
||||
}
|
||||
|
||||
if (free_and_strdup(&c->x11_layout, layout) < 0 ||
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include "logind-session-device.h"
|
||||
#include "logind-session.h"
|
||||
#include "logind.h"
|
||||
#include "signal-util.h"
|
||||
#include "strv.h"
|
||||
#include "util.h"
|
||||
|
||||
@ -300,7 +301,7 @@ int bus_session_method_kill(sd_bus_message *message, void *userdata, sd_bus_erro
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid kill parameter '%s'", swho);
|
||||
}
|
||||
|
||||
if (signo <= 0 || signo >= _NSIG)
|
||||
if (!SIGNAL_VALID(signo))
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid signal %i", signo);
|
||||
|
||||
r = bus_verify_polkit_async(
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include "formats-util.h"
|
||||
#include "logind-user.h"
|
||||
#include "logind.h"
|
||||
#include "signal-util.h"
|
||||
#include "strv.h"
|
||||
#include "user-util.h"
|
||||
|
||||
@ -222,7 +223,7 @@ int bus_user_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (signo <= 0 || signo >= _NSIG)
|
||||
if (!SIGNAL_VALID(signo))
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid signal %i", signo);
|
||||
|
||||
r = user_kill(u, signo);
|
||||
|
@ -46,6 +46,7 @@
|
||||
#include "mkdir.h"
|
||||
#include "path-util.h"
|
||||
#include "process-util.h"
|
||||
#include "signal-util.h"
|
||||
#include "strv.h"
|
||||
#include "terminal-util.h"
|
||||
#include "user-util.h"
|
||||
@ -166,7 +167,7 @@ int bus_machine_method_kill(sd_bus_message *message, void *userdata, sd_bus_erro
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid kill parameter '%s'", swho);
|
||||
}
|
||||
|
||||
if (signo <= 0 || signo >= _NSIG)
|
||||
if (!SIGNAL_VALID(signo))
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid signal %i", signo);
|
||||
|
||||
r = bus_verify_polkit_async(
|
||||
|
@ -2338,6 +2338,50 @@ static int set_limit(int argc, char *argv[], void *userdata) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int clean_images(int argc, char *argv[], void *userdata) {
|
||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
uint64_t usage, total = 0;
|
||||
char fb[FORMAT_BYTES_MAX];
|
||||
sd_bus *bus = userdata;
|
||||
const char *name;
|
||||
unsigned c = 0;
|
||||
int r;
|
||||
|
||||
r = sd_bus_call_method(
|
||||
bus,
|
||||
"org.freedesktop.machine1",
|
||||
"/org/freedesktop/machine1",
|
||||
"org.freedesktop.machine1.Manager",
|
||||
"CleanPool",
|
||||
&error,
|
||||
&reply,
|
||||
"s", arg_all ? "all" : "hidden");
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Could not clean pool: %s", bus_error_message(&error, r));
|
||||
|
||||
r = sd_bus_message_enter_container(reply, 'a', "(st)");
|
||||
if (r < 0)
|
||||
return bus_log_parse_error(r);
|
||||
|
||||
while ((r = sd_bus_message_read(reply, "(st)", &name, &usage)) > 0) {
|
||||
log_info("Removed image '%s'. Freed exclusive disk space: %s",
|
||||
name, format_bytes(fb, sizeof(fb), usage));
|
||||
|
||||
total += usage;
|
||||
c++;
|
||||
}
|
||||
|
||||
r = sd_bus_message_exit_container(reply);
|
||||
if (r < 0)
|
||||
return bus_log_parse_error(r);
|
||||
|
||||
log_info("Removed %u images in total. Total freed exclusive disk space %s.",
|
||||
c, format_bytes(fb, sizeof(fb), total));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int help(int argc, char *argv[], void *userdata) {
|
||||
|
||||
printf("%s [OPTIONS...] {COMMAND} ...\n\n"
|
||||
@ -2396,6 +2440,7 @@ static int help(int argc, char *argv[], void *userdata) {
|
||||
" read-only NAME [BOOL] Mark or unmark image read-only\n"
|
||||
" remove NAME... Remove an image\n"
|
||||
" set-limit [NAME] BYTES Set image or pool size limit (disk quota)\n\n"
|
||||
" clean Remove hidden (or all) images\n"
|
||||
"Image Transfer Commands:\n"
|
||||
" pull-tar URL [NAME] Download a TAR container image\n"
|
||||
" pull-raw URL [NAME] Download a RAW container or VM image\n"
|
||||
@ -2635,6 +2680,7 @@ static int machinectl_main(int argc, char *argv[], sd_bus *bus) {
|
||||
{ "list-transfers", VERB_ANY, 1, 0, list_transfers },
|
||||
{ "cancel-transfer", 2, VERB_ANY, 0, cancel_transfer },
|
||||
{ "set-limit", 2, 3, 0, set_limit },
|
||||
{ "clean", VERB_ANY, 1, 0, clean_images },
|
||||
{}
|
||||
};
|
||||
|
||||
|
@ -802,6 +802,93 @@ static int method_mark_image_read_only(sd_bus_message *message, void *userdata,
|
||||
return bus_image_method_mark_read_only(message, i, error);
|
||||
}
|
||||
|
||||
static int method_clean_pool(sd_bus_message *message, void *userdata, sd_bus_error *error) {
|
||||
enum {
|
||||
REMOVE_ALL,
|
||||
REMOVE_HIDDEN,
|
||||
} mode;
|
||||
|
||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
|
||||
_cleanup_(image_hashmap_freep) Hashmap *images = NULL;
|
||||
Manager *m = userdata;
|
||||
Image *image;
|
||||
const char *mm;
|
||||
Iterator i;
|
||||
int r;
|
||||
|
||||
assert(message);
|
||||
|
||||
r = sd_bus_message_read(message, "s", &mm);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (streq(mm, "all"))
|
||||
mode = REMOVE_ALL;
|
||||
else if (streq(mm, "hidden"))
|
||||
mode = REMOVE_HIDDEN;
|
||||
else
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown mode '%s'.", mm);
|
||||
|
||||
r = bus_verify_polkit_async(
|
||||
message,
|
||||
CAP_SYS_ADMIN,
|
||||
"org.freedesktop.machine1.manage-machines",
|
||||
NULL,
|
||||
false,
|
||||
UID_INVALID,
|
||||
&m->polkit_registry,
|
||||
error);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
return 1; /* Will call us back */
|
||||
|
||||
images = hashmap_new(&string_hash_ops);
|
||||
if (!images)
|
||||
return -ENOMEM;
|
||||
|
||||
r = image_discover(images);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_new_method_return(message, &reply);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_open_container(reply, 'a', "(st)");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
HASHMAP_FOREACH(image, images, i) {
|
||||
|
||||
/* We can't remove vendor images (i.e. those in /usr) */
|
||||
if (IMAGE_IS_VENDOR(image))
|
||||
continue;
|
||||
|
||||
if (IMAGE_IS_HOST(image))
|
||||
continue;
|
||||
|
||||
if (mode == REMOVE_HIDDEN && !IMAGE_IS_HIDDEN(image))
|
||||
continue;
|
||||
|
||||
r = image_remove(image);
|
||||
if (r == -EBUSY) /* keep images that are currently being used. */
|
||||
continue;
|
||||
if (r < 0)
|
||||
return sd_bus_error_set_errnof(error, r, "Failed to remove image %s: %m", image->name);
|
||||
|
||||
r = sd_bus_message_append(reply, "(st)", image->name, image->usage_exclusive);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = sd_bus_message_close_container(reply);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return sd_bus_send(NULL, reply, NULL);
|
||||
}
|
||||
|
||||
static int method_set_pool_limit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
|
||||
Manager *m = userdata;
|
||||
uint64_t limit;
|
||||
@ -1144,6 +1231,7 @@ const sd_bus_vtable manager_vtable[] = {
|
||||
SD_BUS_METHOD("MarkImageReadOnly", "sb", NULL, method_mark_image_read_only, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("SetPoolLimit", "t", NULL, method_set_pool_limit, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("SetImageLimit", "st", NULL, method_set_image_limit, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("CleanPool", "s", "a(st)", method_clean_pool, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("MapFromMachineUser", "su", "u", method_map_from_machine_user, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("MapToMachineUser", "u", "sou", method_map_to_machine_user, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("MapFromMachineGroup", "su", "u", method_map_from_machine_group, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
|
@ -30,6 +30,8 @@
|
||||
#include "string-util.h"
|
||||
#include "unaligned.h"
|
||||
|
||||
#define LLDP_MULTICAST_ADDR { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e }
|
||||
|
||||
/* The LLDP spec calls this "txFastInit", see 9.2.5.19 */
|
||||
#define LLDP_TX_FAST_INIT 4U
|
||||
|
||||
@ -127,7 +129,7 @@ static int lldp_make_packet(
|
||||
|
||||
h = (struct ether_header*) packet;
|
||||
h->ether_type = htobe16(ETHERTYPE_LLDP);
|
||||
memcpy(h->ether_dhost, &(struct ether_addr) { SD_LLDP_MULTICAST_ADDR }, ETH_ALEN);
|
||||
memcpy(h->ether_dhost, &(struct ether_addr) { LLDP_MULTICAST_ADDR }, ETH_ALEN);
|
||||
memcpy(h->ether_shost, hwaddr, ETH_ALEN);
|
||||
|
||||
p = (uint8_t*) packet + sizeof(struct ether_header);
|
||||
@ -199,7 +201,7 @@ static int lldp_send_packet(int ifindex, const void *packet, size_t packet_size)
|
||||
.ll.sll_protocol = htobe16(ETHERTYPE_LLDP),
|
||||
.ll.sll_ifindex = ifindex,
|
||||
.ll.sll_halen = ETH_ALEN,
|
||||
.ll.sll_addr = SD_LLDP_MULTICAST_ADDR,
|
||||
.ll.sll_addr = LLDP_MULTICAST_ADDR,
|
||||
};
|
||||
|
||||
_cleanup_close_ int fd = -1;
|
||||
|
@ -52,8 +52,7 @@ int route_new_static(Network *network, unsigned section, Route **ret) {
|
||||
int r;
|
||||
|
||||
if (section) {
|
||||
route = hashmap_get(network->routes_by_section,
|
||||
UINT_TO_PTR(section));
|
||||
route = hashmap_get(network->routes_by_section, UINT_TO_PTR(section));
|
||||
if (route) {
|
||||
*ret = route;
|
||||
route = NULL;
|
||||
@ -67,16 +66,18 @@ int route_new_static(Network *network, unsigned section, Route **ret) {
|
||||
return r;
|
||||
|
||||
route->protocol = RTPROT_STATIC;
|
||||
route->network = network;
|
||||
|
||||
LIST_PREPEND(routes, network->static_routes, route);
|
||||
|
||||
if (section) {
|
||||
r = hashmap_put(network->routes_by_section, UINT_TO_PTR(route->section), route);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
route->section = section;
|
||||
hashmap_put(network->routes_by_section,
|
||||
UINT_TO_PTR(route->section), route);
|
||||
}
|
||||
|
||||
LIST_PREPEND(routes, network->static_routes, route);
|
||||
route->network = network;
|
||||
|
||||
*ret = route;
|
||||
route = NULL;
|
||||
|
||||
|
@ -230,7 +230,6 @@ static void test_dnssec_verify_rrset2(void) {
|
||||
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *nsec = NULL, *rrsig = NULL, *dnskey = NULL;
|
||||
_cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
|
||||
DnssecResult result;
|
||||
int r;
|
||||
|
||||
nsec = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_NSEC, "nasa.gov");
|
||||
assert_se(nsec);
|
||||
|
@ -17,14 +17,24 @@
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#warning "Temporary work-around for broken glibc vs. linux kernel header definitions"
|
||||
#warning "This really should be removed sooner rather than later, when this is fixed upstream"
|
||||
#define _NET_IF_H 1
|
||||
|
||||
#include <alloca.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <endian.h>
|
||||
#include <errno.h>
|
||||
#include <net/if.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <net/if.h>
|
||||
#include <linux/if.h>
|
||||
#ifndef IFNAMSIZ
|
||||
#undef _NET_IF_H
|
||||
/* Let's make sure to include this one, too, if IFNAMSIZ isn't defined yet, as it is for kernels <= 4.2 */
|
||||
#include <net/if.h>
|
||||
#endif
|
||||
#include <linux/netfilter_ipv4/ip_tables.h>
|
||||
#include <linux/netfilter/nf_nat.h>
|
||||
#include <linux/netfilter/xt_addrtype.h>
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -54,6 +54,8 @@ enum UnitFileState {
|
||||
UNIT_FILE_STATIC,
|
||||
UNIT_FILE_DISABLED,
|
||||
UNIT_FILE_INDIRECT,
|
||||
UNIT_FILE_GENERATED,
|
||||
UNIT_FILE_TRANSIENT,
|
||||
UNIT_FILE_BAD,
|
||||
_UNIT_FILE_STATE_MAX,
|
||||
_UNIT_FILE_STATE_INVALID = -1
|
||||
@ -126,17 +128,18 @@ static inline bool UNIT_FILE_INSTALL_INFO_HAS_ALSO(UnitFileInstallInfo *i) {
|
||||
int unit_file_enable(UnitFileScope scope, bool runtime, const char *root_dir, char **files, bool force, UnitFileChange **changes, unsigned *n_changes);
|
||||
int unit_file_disable(UnitFileScope scope, bool runtime, const char *root_dir, char **files, UnitFileChange **changes, unsigned *n_changes);
|
||||
int unit_file_reenable(UnitFileScope scope, bool runtime, const char *root_dir, char **files, bool force, UnitFileChange **changes, unsigned *n_changes);
|
||||
int unit_file_link(UnitFileScope scope, bool runtime, const char *root_dir, char **files, bool force, UnitFileChange **changes, unsigned *n_changes);
|
||||
int unit_file_preset(UnitFileScope scope, bool runtime, const char *root_dir, char **files, UnitFilePresetMode mode, bool force, UnitFileChange **changes, unsigned *n_changes);
|
||||
int unit_file_preset_all(UnitFileScope scope, bool runtime, const char *root_dir, UnitFilePresetMode mode, bool force, UnitFileChange **changes, unsigned *n_changes);
|
||||
int unit_file_mask(UnitFileScope scope, bool runtime, const char *root_dir, char **files, bool force, UnitFileChange **changes, unsigned *n_changes);
|
||||
int unit_file_unmask(UnitFileScope scope, bool runtime, const char *root_dir, char **files, UnitFileChange **changes, unsigned *n_changes);
|
||||
int unit_file_link(UnitFileScope scope, bool runtime, const char *root_dir, char **files, bool force, UnitFileChange **changes, unsigned *n_changes);
|
||||
int unit_file_revert(UnitFileScope scope, const char *root_dir, char **files, UnitFileChange **changes, unsigned *n_changes);
|
||||
int unit_file_set_default(UnitFileScope scope, const char *root_dir, const char *file, bool force, UnitFileChange **changes, unsigned *n_changes);
|
||||
int unit_file_get_default(UnitFileScope scope, const char *root_dir, char **name);
|
||||
int unit_file_add_dependency(UnitFileScope scope, bool runtime, const char *root_dir, char **files, const char *target, UnitDependency dep, bool force, UnitFileChange **changes, unsigned *n_changes);
|
||||
|
||||
int unit_file_lookup_state(UnitFileScope scope, const char *root_dir,const LookupPaths *paths, const char *name, UnitFileState *ret);
|
||||
int unit_file_get_state(UnitFileScope scope, const char *root_dir, const char *filename, UnitFileState *ret);
|
||||
int unit_file_exists(UnitFileScope scope, const LookupPaths *paths, const char *name);
|
||||
|
||||
int unit_file_get_list(UnitFileScope scope, const char *root_dir, Hashmap *h);
|
||||
Hashmap* unit_file_list_free(Hashmap *h);
|
||||
|
@ -401,8 +401,7 @@ int image_remove(Image *i) {
|
||||
|
||||
assert(i);
|
||||
|
||||
if (path_equal(i->path, "/") ||
|
||||
path_startswith(i->path, "/usr"))
|
||||
if (IMAGE_IS_VENDOR(i) || IMAGE_IS_HOST(i))
|
||||
return -EROFS;
|
||||
|
||||
settings = image_settings_path(i);
|
||||
@ -474,8 +473,7 @@ int image_rename(Image *i, const char *new_name) {
|
||||
if (!image_name_is_valid(new_name))
|
||||
return -EINVAL;
|
||||
|
||||
if (path_equal(i->path, "/") ||
|
||||
path_startswith(i->path, "/usr"))
|
||||
if (IMAGE_IS_VENDOR(i) || IMAGE_IS_HOST(i))
|
||||
return -EROFS;
|
||||
|
||||
settings = image_settings_path(i);
|
||||
@ -642,8 +640,7 @@ int image_read_only(Image *i, bool b) {
|
||||
int r;
|
||||
assert(i);
|
||||
|
||||
if (path_equal(i->path, "/") ||
|
||||
path_startswith(i->path, "/usr"))
|
||||
if (IMAGE_IS_VENDOR(i) || IMAGE_IS_HOST(i))
|
||||
return -EROFS;
|
||||
|
||||
/* Make sure we don't interfere with a running nspawn */
|
||||
@ -751,8 +748,7 @@ int image_path_lock(const char *path, int operation, LockFile *global, LockFile
|
||||
int image_set_limit(Image *i, uint64_t referenced_max) {
|
||||
assert(i);
|
||||
|
||||
if (path_equal(i->path, "/") ||
|
||||
path_startswith(i->path, "/usr"))
|
||||
if (IMAGE_IS_VENDOR(i) || IMAGE_IS_HOST(i))
|
||||
return -EROFS;
|
||||
|
||||
if (i->type != IMAGE_SUBVOLUME)
|
||||
|
@ -25,6 +25,8 @@
|
||||
#include "hashmap.h"
|
||||
#include "lockfile-util.h"
|
||||
#include "macro.h"
|
||||
#include "path-util.h"
|
||||
#include "string-util.h"
|
||||
#include "time-util.h"
|
||||
|
||||
typedef enum ImageType {
|
||||
@ -75,3 +77,27 @@ int image_path_lock(const char *path, int operation, LockFile *global, LockFile
|
||||
int image_name_lock(const char *name, int operation, LockFile *ret);
|
||||
|
||||
int image_set_limit(Image *i, uint64_t referenced_max);
|
||||
|
||||
static inline bool IMAGE_IS_HIDDEN(const struct Image *i) {
|
||||
assert(i);
|
||||
|
||||
return i->name && i->name[0] == '.';
|
||||
}
|
||||
|
||||
static inline bool IMAGE_IS_VENDOR(const struct Image *i) {
|
||||
assert(i);
|
||||
|
||||
return i->path && path_startswith(i->path, "/usr");
|
||||
}
|
||||
|
||||
static inline bool IMAGE_IS_HOST(const struct Image *i) {
|
||||
assert(i);
|
||||
|
||||
if (i->name && streq(i->name, ".host"))
|
||||
return true;
|
||||
|
||||
if (i->path && path_equal(i->path, "/"))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -26,61 +26,66 @@
|
||||
#include "install.h"
|
||||
#include "log.h"
|
||||
#include "macro.h"
|
||||
#include "mkdir.h"
|
||||
#include "path-lookup.h"
|
||||
#include "path-util.h"
|
||||
#include "rm-rf.h"
|
||||
#include "stat-util.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
#include "util.h"
|
||||
|
||||
int user_config_home(char **config_home) {
|
||||
static int user_runtime_dir(char **ret, const char *suffix) {
|
||||
const char *e;
|
||||
char *r;
|
||||
char *j;
|
||||
|
||||
assert(ret);
|
||||
assert(suffix);
|
||||
|
||||
e = getenv("XDG_RUNTIME_DIR");
|
||||
if (!e)
|
||||
return -ENXIO;
|
||||
|
||||
j = strappend(e, suffix);
|
||||
if (!j)
|
||||
return -ENOMEM;
|
||||
|
||||
*ret = j;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int user_config_dir(char **ret, const char *suffix) {
|
||||
const char *e;
|
||||
char *j;
|
||||
|
||||
assert(ret);
|
||||
|
||||
e = getenv("XDG_CONFIG_HOME");
|
||||
if (e) {
|
||||
r = strappend(e, "/systemd/user");
|
||||
if (!r)
|
||||
return -ENOMEM;
|
||||
|
||||
*config_home = r;
|
||||
return 1;
|
||||
} else {
|
||||
if (e)
|
||||
j = strappend(e, suffix);
|
||||
else {
|
||||
const char *home;
|
||||
|
||||
home = getenv("HOME");
|
||||
if (home) {
|
||||
r = strappend(home, "/.config/systemd/user");
|
||||
if (!r)
|
||||
return -ENOMEM;
|
||||
if (!home)
|
||||
return -ENXIO;
|
||||
|
||||
*config_home = r;
|
||||
return 1;
|
||||
}
|
||||
j = strjoin(home, "/.config", suffix, NULL);
|
||||
}
|
||||
|
||||
if (!j)
|
||||
return -ENOMEM;
|
||||
|
||||
*ret = j;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int user_runtime_dir(char **runtime_dir) {
|
||||
static int user_data_dir(char **ret, const char *suffix) {
|
||||
const char *e;
|
||||
char *r;
|
||||
char *j;
|
||||
|
||||
e = getenv("XDG_RUNTIME_DIR");
|
||||
if (e) {
|
||||
r = strappend(e, "/systemd/user");
|
||||
if (!r)
|
||||
return -ENOMEM;
|
||||
|
||||
*runtime_dir = r;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int user_data_home_dir(char **dir, const char *suffix) {
|
||||
const char *e;
|
||||
char *res;
|
||||
assert(ret);
|
||||
assert(suffix);
|
||||
|
||||
/* We don't treat /etc/xdg/systemd here as the spec
|
||||
* suggests because we assume that that is a link to
|
||||
@ -88,27 +93,33 @@ static int user_data_home_dir(char **dir, const char *suffix) {
|
||||
|
||||
e = getenv("XDG_DATA_HOME");
|
||||
if (e)
|
||||
res = strappend(e, suffix);
|
||||
j = strappend(e, suffix);
|
||||
else {
|
||||
const char *home;
|
||||
|
||||
home = getenv("HOME");
|
||||
if (home)
|
||||
res = strjoin(home, "/.local/share", suffix, NULL);
|
||||
else
|
||||
return 0;
|
||||
if (!home)
|
||||
return -ENXIO;
|
||||
|
||||
|
||||
j = strjoin(home, "/.local/share", suffix, NULL);
|
||||
}
|
||||
if (!res)
|
||||
if (!j)
|
||||
return -ENOMEM;
|
||||
|
||||
*dir = res;
|
||||
return 0;
|
||||
*ret = j;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char** user_dirs(
|
||||
const char *persistent_config,
|
||||
const char *runtime_config,
|
||||
const char *generator,
|
||||
const char *generator_early,
|
||||
const char *generator_late) {
|
||||
const char *generator_late,
|
||||
const char *transient,
|
||||
const char *persistent_control,
|
||||
const char *runtime_control) {
|
||||
|
||||
const char * const config_unit_paths[] = {
|
||||
USER_CONFIG_UNIT_PATH,
|
||||
@ -116,8 +127,6 @@ static char** user_dirs(
|
||||
NULL
|
||||
};
|
||||
|
||||
const char * const runtime_unit_path = "/run/systemd/user";
|
||||
|
||||
const char * const data_unit_paths[] = {
|
||||
"/usr/local/lib/systemd/user",
|
||||
"/usr/local/share/systemd/user",
|
||||
@ -128,8 +137,8 @@ static char** user_dirs(
|
||||
};
|
||||
|
||||
const char *e;
|
||||
_cleanup_free_ char *config_home = NULL, *runtime_dir = NULL, *data_home = NULL;
|
||||
_cleanup_strv_free_ char **config_dirs = NULL, **data_dirs = NULL;
|
||||
_cleanup_free_ char *data_home = NULL;
|
||||
_cleanup_free_ char **res = NULL;
|
||||
char **tmp;
|
||||
int r;
|
||||
@ -143,12 +152,6 @@ static char** user_dirs(
|
||||
* as data, and allow overriding as configuration.
|
||||
*/
|
||||
|
||||
if (user_config_home(&config_home) < 0)
|
||||
return NULL;
|
||||
|
||||
if (user_runtime_dir(&runtime_dir) < 0)
|
||||
return NULL;
|
||||
|
||||
e = getenv("XDG_CONFIG_DIRS");
|
||||
if (e) {
|
||||
config_dirs = strv_split(e, ":");
|
||||
@ -156,8 +159,8 @@ static char** user_dirs(
|
||||
return NULL;
|
||||
}
|
||||
|
||||
r = user_data_home_dir(&data_home, "/systemd/user");
|
||||
if (r < 0)
|
||||
r = user_data_dir(&data_home, "/systemd/user");
|
||||
if (r < 0 && r != -ENXIO)
|
||||
return NULL;
|
||||
|
||||
e = getenv("XDG_DATA_DIRS");
|
||||
@ -171,35 +174,36 @@ static char** user_dirs(
|
||||
return NULL;
|
||||
|
||||
/* Now merge everything we found. */
|
||||
if (generator_early)
|
||||
if (strv_extend(&res, generator_early) < 0)
|
||||
return NULL;
|
||||
if (strv_extend(&res, persistent_control) < 0)
|
||||
return NULL;
|
||||
|
||||
if (config_home)
|
||||
if (strv_extend(&res, config_home) < 0)
|
||||
return NULL;
|
||||
if (strv_extend(&res, runtime_control) < 0)
|
||||
return NULL;
|
||||
|
||||
if (strv_extend(&res, transient) < 0)
|
||||
return NULL;
|
||||
|
||||
if (strv_extend(&res, generator_early) < 0)
|
||||
return NULL;
|
||||
|
||||
if (!strv_isempty(config_dirs))
|
||||
if (strv_extend_strv_concat(&res, config_dirs, "/systemd/user") < 0)
|
||||
return NULL;
|
||||
|
||||
if (strv_extend(&res, persistent_config) < 0)
|
||||
return NULL;
|
||||
|
||||
if (strv_extend_strv(&res, (char**) config_unit_paths, false) < 0)
|
||||
return NULL;
|
||||
|
||||
if (runtime_dir)
|
||||
if (strv_extend(&res, runtime_dir) < 0)
|
||||
return NULL;
|
||||
|
||||
if (strv_extend(&res, runtime_unit_path) < 0)
|
||||
if (strv_extend(&res, runtime_config) < 0)
|
||||
return NULL;
|
||||
|
||||
if (generator)
|
||||
if (strv_extend(&res, generator) < 0)
|
||||
return NULL;
|
||||
if (strv_extend(&res, generator) < 0)
|
||||
return NULL;
|
||||
|
||||
if (data_home)
|
||||
if (strv_extend(&res, data_home) < 0)
|
||||
return NULL;
|
||||
if (strv_extend(&res, data_home) < 0)
|
||||
return NULL;
|
||||
|
||||
if (!strv_isempty(data_dirs))
|
||||
if (strv_extend_strv_concat(&res, data_dirs, "/systemd/user") < 0)
|
||||
@ -208,9 +212,8 @@ static char** user_dirs(
|
||||
if (strv_extend_strv(&res, (char**) data_unit_paths, false) < 0)
|
||||
return NULL;
|
||||
|
||||
if (generator_late)
|
||||
if (strv_extend(&res, generator_late) < 0)
|
||||
return NULL;
|
||||
if (strv_extend(&res, generator_late) < 0)
|
||||
return NULL;
|
||||
|
||||
if (path_strv_make_absolute_cwd(res) < 0)
|
||||
return NULL;
|
||||
@ -220,58 +223,299 @@ static char** user_dirs(
|
||||
return tmp;
|
||||
}
|
||||
|
||||
char **generator_paths(ManagerRunningAs running_as) {
|
||||
if (running_as == MANAGER_USER)
|
||||
return strv_new("/run/systemd/user-generators",
|
||||
"/etc/systemd/user-generators",
|
||||
"/usr/local/lib/systemd/user-generators",
|
||||
USER_GENERATOR_PATH,
|
||||
NULL);
|
||||
else
|
||||
return strv_new("/run/systemd/system-generators",
|
||||
"/etc/systemd/system-generators",
|
||||
"/usr/local/lib/systemd/system-generators",
|
||||
SYSTEM_GENERATOR_PATH,
|
||||
NULL);
|
||||
static int acquire_generator_dirs(
|
||||
UnitFileScope scope,
|
||||
char **generator,
|
||||
char **generator_early,
|
||||
char **generator_late) {
|
||||
|
||||
_cleanup_free_ char *x = NULL, *y = NULL, *z = NULL;
|
||||
const char *prefix;
|
||||
|
||||
assert(generator);
|
||||
assert(generator_early);
|
||||
assert(generator_late);
|
||||
|
||||
switch (scope) {
|
||||
|
||||
case UNIT_FILE_SYSTEM:
|
||||
prefix = "/run/systemd/";
|
||||
break;
|
||||
|
||||
case UNIT_FILE_USER: {
|
||||
const char *e;
|
||||
|
||||
e = getenv("XDG_RUNTIME_DIR");
|
||||
if (!e)
|
||||
return -ENXIO;
|
||||
|
||||
prefix = strjoina(e, "/systemd/", NULL);
|
||||
break;
|
||||
}
|
||||
|
||||
case UNIT_FILE_GLOBAL:
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
default:
|
||||
assert_not_reached("Hmm, unexpected scope value.");
|
||||
}
|
||||
|
||||
x = strappend(prefix, "generator");
|
||||
if (!x)
|
||||
return -ENOMEM;
|
||||
|
||||
y = strappend(prefix, "generator.early");
|
||||
if (!y)
|
||||
return -ENOMEM;
|
||||
|
||||
z = strappend(prefix, "generator.late");
|
||||
if (!z)
|
||||
return -ENOMEM;
|
||||
|
||||
*generator = x;
|
||||
*generator_early = y;
|
||||
*generator_late = z;
|
||||
|
||||
x = y = z = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acquire_transient_dir(UnitFileScope scope, char **ret) {
|
||||
assert(ret);
|
||||
|
||||
switch (scope) {
|
||||
|
||||
case UNIT_FILE_SYSTEM: {
|
||||
char *transient;
|
||||
|
||||
transient = strdup("/run/systemd/transient");
|
||||
if (!transient)
|
||||
return -ENOMEM;
|
||||
|
||||
*ret = transient;
|
||||
return 0;
|
||||
}
|
||||
|
||||
case UNIT_FILE_USER:
|
||||
return user_runtime_dir(ret, "/systemd/transient");
|
||||
|
||||
case UNIT_FILE_GLOBAL:
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
default:
|
||||
assert_not_reached("Hmm, unexpected scope value.");
|
||||
}
|
||||
}
|
||||
|
||||
static int acquire_config_dirs(UnitFileScope scope, char **persistent, char **runtime) {
|
||||
_cleanup_free_ char *a = NULL, *b = NULL;
|
||||
int r;
|
||||
|
||||
assert(persistent);
|
||||
assert(runtime);
|
||||
|
||||
switch (scope) {
|
||||
|
||||
case UNIT_FILE_SYSTEM:
|
||||
a = strdup(SYSTEM_CONFIG_UNIT_PATH);
|
||||
b = strdup("/run/systemd/system");
|
||||
break;
|
||||
|
||||
case UNIT_FILE_GLOBAL:
|
||||
a = strdup(USER_CONFIG_UNIT_PATH);
|
||||
b = strdup("/run/systemd/user");
|
||||
break;
|
||||
|
||||
case UNIT_FILE_USER:
|
||||
r = user_config_dir(&a, "/systemd/user");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = user_runtime_dir(runtime, "/systemd/user");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*persistent = a;
|
||||
a = NULL;
|
||||
|
||||
return 0;
|
||||
|
||||
default:
|
||||
assert_not_reached("Hmm, unexpected scope value.");
|
||||
}
|
||||
|
||||
if (!a || !b)
|
||||
return -ENOMEM;
|
||||
|
||||
*persistent = a;
|
||||
*runtime = b;
|
||||
a = b = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acquire_control_dirs(UnitFileScope scope, char **persistent, char **runtime) {
|
||||
_cleanup_free_ char *a = NULL;
|
||||
int r;
|
||||
|
||||
assert(persistent);
|
||||
assert(runtime);
|
||||
|
||||
switch (scope) {
|
||||
|
||||
case UNIT_FILE_SYSTEM: {
|
||||
_cleanup_free_ char *b = NULL;
|
||||
|
||||
a = strdup("/etc/systemd/system.control");
|
||||
if (!a)
|
||||
return -ENOMEM;
|
||||
|
||||
b = strdup("/run/systemd/system.control");
|
||||
if (!b)
|
||||
return -ENOMEM;
|
||||
|
||||
*runtime = b;
|
||||
b = NULL;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case UNIT_FILE_USER:
|
||||
r = user_config_dir(&a, "/systemd/system.control");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = user_runtime_dir(runtime, "/systemd/system.control");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
break;
|
||||
|
||||
case UNIT_FILE_GLOBAL:
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
default:
|
||||
assert_not_reached("Hmm, unexpected scope value.");
|
||||
}
|
||||
|
||||
*persistent = a;
|
||||
a = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int patch_root_prefix(char **p, const char *root_dir) {
|
||||
char *c;
|
||||
|
||||
assert(p);
|
||||
|
||||
if (!*p)
|
||||
return 0;
|
||||
|
||||
c = prefix_root(root_dir, *p);
|
||||
if (!c)
|
||||
return -ENOMEM;
|
||||
|
||||
free(*p);
|
||||
*p = c;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int patch_root_prefix_strv(char **l, const char *root_dir) {
|
||||
char **i;
|
||||
int r;
|
||||
|
||||
if (!root_dir)
|
||||
return 0;
|
||||
|
||||
STRV_FOREACH(i, l) {
|
||||
r = patch_root_prefix(i, root_dir);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lookup_paths_init(
|
||||
LookupPaths *p,
|
||||
ManagerRunningAs running_as,
|
||||
bool personal,
|
||||
const char *root_dir,
|
||||
const char *generator,
|
||||
const char *generator_early,
|
||||
const char *generator_late) {
|
||||
UnitFileScope scope,
|
||||
LookupPathsFlags flags,
|
||||
const char *root_dir) {
|
||||
|
||||
const char *e;
|
||||
_cleanup_free_ char
|
||||
*root = NULL,
|
||||
*persistent_config = NULL, *runtime_config = NULL,
|
||||
*generator = NULL, *generator_early = NULL, *generator_late = NULL,
|
||||
*transient = NULL,
|
||||
*persistent_control = NULL, *runtime_control = NULL;
|
||||
bool append = false; /* Add items from SYSTEMD_UNIT_PATH before normal directories */
|
||||
char **l = NULL;
|
||||
const char *e;
|
||||
int r;
|
||||
|
||||
assert(p);
|
||||
assert(scope >= 0);
|
||||
assert(scope < _UNIT_FILE_SCOPE_MAX);
|
||||
|
||||
/* First priority is whatever has been passed to us via env
|
||||
* vars */
|
||||
if (!isempty(root_dir) && !path_equal(root_dir, "/")) {
|
||||
if (scope == UNIT_FILE_USER)
|
||||
return -EINVAL;
|
||||
|
||||
r = is_dir(root_dir, true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
return -ENOTDIR;
|
||||
|
||||
root = strdup(root_dir);
|
||||
if (!root)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
r = acquire_config_dirs(scope, &persistent_config, &runtime_config);
|
||||
if (r < 0 && r != -ENXIO)
|
||||
return r;
|
||||
|
||||
if ((flags & LOOKUP_PATHS_EXCLUDE_GENERATED) == 0) {
|
||||
r = acquire_generator_dirs(scope, &generator, &generator_early, &generator_late);
|
||||
if (r < 0 && r != -EOPNOTSUPP && r != -ENXIO)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = acquire_transient_dir(scope, &transient);
|
||||
if (r < 0 && r != -EOPNOTSUPP && r != -ENXIO)
|
||||
return r;
|
||||
|
||||
r = acquire_control_dirs(scope, &persistent_control, &runtime_control);
|
||||
if (r < 0 && r != -EOPNOTSUPP && r != -ENXIO)
|
||||
return r;
|
||||
|
||||
/* First priority is whatever has been passed to us via env vars */
|
||||
e = getenv("SYSTEMD_UNIT_PATH");
|
||||
if (e) {
|
||||
if (endswith(e, ":")) {
|
||||
e = strndupa(e, strlen(e) - 1);
|
||||
const char *k;
|
||||
|
||||
k = endswith(e, ":");
|
||||
if (k) {
|
||||
e = strndupa(e, k - e);
|
||||
append = true;
|
||||
}
|
||||
|
||||
/* FIXME: empty components in other places should be
|
||||
* rejected. */
|
||||
|
||||
r = path_split_and_make_absolute(e, &p->unit_path);
|
||||
r = path_split_and_make_absolute(e, &l);
|
||||
if (r < 0)
|
||||
return r;
|
||||
} else
|
||||
p->unit_path = NULL;
|
||||
l = NULL;
|
||||
|
||||
if (!p->unit_path || append) {
|
||||
if (!l || append) {
|
||||
/* Let's figure something out. */
|
||||
|
||||
_cleanup_strv_free_ char **unit_path;
|
||||
_cleanup_strv_free_ char **add = NULL;
|
||||
|
||||
/* For the user units we include share/ in the search
|
||||
* path in order to comply with the XDG basedir spec.
|
||||
@ -279,17 +523,45 @@ int lookup_paths_init(
|
||||
* we include /lib in the search path for the system
|
||||
* stuff but avoid it for user stuff. */
|
||||
|
||||
if (running_as == MANAGER_USER) {
|
||||
if (personal)
|
||||
unit_path = user_dirs(generator, generator_early, generator_late);
|
||||
else
|
||||
unit_path = strv_new(
|
||||
switch (scope) {
|
||||
|
||||
case UNIT_FILE_SYSTEM:
|
||||
add = strv_new(
|
||||
/* If you modify this you also want to modify
|
||||
* systemdsystemunitpath= in systemd.pc.in! */
|
||||
STRV_IFNOTNULL(persistent_control),
|
||||
STRV_IFNOTNULL(runtime_control),
|
||||
STRV_IFNOTNULL(transient),
|
||||
STRV_IFNOTNULL(generator_early),
|
||||
persistent_config,
|
||||
SYSTEM_CONFIG_UNIT_PATH,
|
||||
"/etc/systemd/system",
|
||||
runtime_config,
|
||||
"/run/systemd/system",
|
||||
STRV_IFNOTNULL(generator),
|
||||
"/usr/local/lib/systemd/system",
|
||||
SYSTEM_DATA_UNIT_PATH,
|
||||
"/usr/lib/systemd/system",
|
||||
#ifdef HAVE_SPLIT_USR
|
||||
"/lib/systemd/system",
|
||||
#endif
|
||||
STRV_IFNOTNULL(generator_late),
|
||||
NULL);
|
||||
break;
|
||||
|
||||
case UNIT_FILE_GLOBAL:
|
||||
add = strv_new(
|
||||
/* If you modify this you also want to modify
|
||||
* systemduserunitpath= in systemd.pc.in, and
|
||||
* the arrays in user_dirs() above! */
|
||||
STRV_IFNOTNULL(persistent_control),
|
||||
STRV_IFNOTNULL(runtime_control),
|
||||
STRV_IFNOTNULL(transient),
|
||||
STRV_IFNOTNULL(generator_early),
|
||||
persistent_config,
|
||||
USER_CONFIG_UNIT_PATH,
|
||||
"/etc/systemd/user",
|
||||
runtime_config,
|
||||
"/run/systemd/user",
|
||||
STRV_IFNOTNULL(generator),
|
||||
"/usr/local/lib/systemd/user",
|
||||
@ -299,143 +571,251 @@ int lookup_paths_init(
|
||||
"/usr/share/systemd/user",
|
||||
STRV_IFNOTNULL(generator_late),
|
||||
NULL);
|
||||
} else
|
||||
unit_path = strv_new(
|
||||
/* If you modify this you also want to modify
|
||||
* systemdsystemunitpath= in systemd.pc.in! */
|
||||
STRV_IFNOTNULL(generator_early),
|
||||
SYSTEM_CONFIG_UNIT_PATH,
|
||||
"/etc/systemd/system",
|
||||
"/run/systemd/system",
|
||||
STRV_IFNOTNULL(generator),
|
||||
"/usr/local/lib/systemd/system",
|
||||
SYSTEM_DATA_UNIT_PATH,
|
||||
"/usr/lib/systemd/system",
|
||||
#ifdef HAVE_SPLIT_USR
|
||||
"/lib/systemd/system",
|
||||
#endif
|
||||
STRV_IFNOTNULL(generator_late),
|
||||
NULL);
|
||||
break;
|
||||
|
||||
if (!unit_path)
|
||||
case UNIT_FILE_USER:
|
||||
add = user_dirs(persistent_config, runtime_config,
|
||||
generator, generator_early, generator_late,
|
||||
transient,
|
||||
persistent_config, runtime_control);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert_not_reached("Hmm, unexpected scope?");
|
||||
}
|
||||
|
||||
if (!add)
|
||||
return -ENOMEM;
|
||||
|
||||
r = strv_extend_strv(&p->unit_path, unit_path, false);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (l) {
|
||||
r = strv_extend_strv(&l, add, false);
|
||||
if (r < 0)
|
||||
return r;
|
||||
} else {
|
||||
l = add;
|
||||
add = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!path_strv_resolve_uniq(p->unit_path, root_dir))
|
||||
r = patch_root_prefix(&persistent_config, root);
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = patch_root_prefix(&runtime_config, root);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = patch_root_prefix(&generator, root);
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = patch_root_prefix(&generator_early, root);
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = patch_root_prefix(&generator_late, root);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = patch_root_prefix(&transient, root);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = patch_root_prefix(&persistent_control, root);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = patch_root_prefix(&runtime_control, root);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = patch_root_prefix_strv(l, root);
|
||||
if (r < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!strv_isempty(p->unit_path)) {
|
||||
_cleanup_free_ char *t = strv_join(p->unit_path, "\n\t");
|
||||
if (!t)
|
||||
return -ENOMEM;
|
||||
log_debug("Looking for unit files in (higher priority first):\n\t%s", t);
|
||||
} else {
|
||||
log_debug("Ignoring unit files.");
|
||||
p->unit_path = strv_free(p->unit_path);
|
||||
}
|
||||
p->search_path = strv_uniq(l);
|
||||
l = NULL;
|
||||
|
||||
if (running_as == MANAGER_SYSTEM) {
|
||||
#ifdef HAVE_SYSV_COMPAT
|
||||
/* /etc/init.d/ compatibility does not matter to users */
|
||||
p->persistent_config = persistent_config;
|
||||
p->runtime_config = runtime_config;
|
||||
persistent_config = runtime_config = NULL;
|
||||
|
||||
e = getenv("SYSTEMD_SYSVINIT_PATH");
|
||||
if (e) {
|
||||
r = path_split_and_make_absolute(e, &p->sysvinit_path);
|
||||
if (r < 0)
|
||||
return r;
|
||||
} else
|
||||
p->sysvinit_path = NULL;
|
||||
p->generator = generator;
|
||||
p->generator_early = generator_early;
|
||||
p->generator_late = generator_late;
|
||||
generator = generator_early = generator_late = NULL;
|
||||
|
||||
if (strv_isempty(p->sysvinit_path)) {
|
||||
strv_free(p->sysvinit_path);
|
||||
p->transient = transient;
|
||||
transient = NULL;
|
||||
|
||||
p->sysvinit_path = strv_new(
|
||||
SYSTEM_SYSVINIT_PATH, /* /etc/init.d/ */
|
||||
NULL);
|
||||
if (!p->sysvinit_path)
|
||||
return -ENOMEM;
|
||||
}
|
||||
p->persistent_control = persistent_control;
|
||||
p->runtime_control = runtime_control;
|
||||
persistent_control = runtime_control = NULL;
|
||||
|
||||
e = getenv("SYSTEMD_SYSVRCND_PATH");
|
||||
if (e) {
|
||||
r = path_split_and_make_absolute(e, &p->sysvrcnd_path);
|
||||
if (r < 0)
|
||||
return r;
|
||||
} else
|
||||
p->sysvrcnd_path = NULL;
|
||||
|
||||
if (strv_isempty(p->sysvrcnd_path)) {
|
||||
strv_free(p->sysvrcnd_path);
|
||||
|
||||
p->sysvrcnd_path = strv_new(
|
||||
SYSTEM_SYSVRCND_PATH, /* /etc/rcN.d/ */
|
||||
NULL);
|
||||
if (!p->sysvrcnd_path)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (!path_strv_resolve_uniq(p->sysvinit_path, root_dir))
|
||||
return -ENOMEM;
|
||||
|
||||
if (!path_strv_resolve_uniq(p->sysvrcnd_path, root_dir))
|
||||
return -ENOMEM;
|
||||
|
||||
if (!strv_isempty(p->sysvinit_path)) {
|
||||
_cleanup_free_ char *t = strv_join(p->sysvinit_path, "\n\t");
|
||||
if (!t)
|
||||
return -ENOMEM;
|
||||
log_debug("Looking for SysV init scripts in:\n\t%s", t);
|
||||
} else {
|
||||
log_debug("Ignoring SysV init scripts.");
|
||||
p->sysvinit_path = strv_free(p->sysvinit_path);
|
||||
}
|
||||
|
||||
if (!strv_isempty(p->sysvrcnd_path)) {
|
||||
_cleanup_free_ char *t =
|
||||
strv_join(p->sysvrcnd_path, "\n\t");
|
||||
if (!t)
|
||||
return -ENOMEM;
|
||||
|
||||
log_debug("Looking for SysV rcN.d links in:\n\t%s", t);
|
||||
} else {
|
||||
log_debug("Ignoring SysV rcN.d links.");
|
||||
p->sysvrcnd_path = strv_free(p->sysvrcnd_path);
|
||||
}
|
||||
#else
|
||||
log_debug("SysV init scripts and rcN.d links support disabled");
|
||||
#endif
|
||||
}
|
||||
p->root_dir = root;
|
||||
root = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void lookup_paths_free(LookupPaths *p) {
|
||||
if (!p)
|
||||
return;
|
||||
|
||||
p->search_path = strv_free(p->search_path);
|
||||
|
||||
p->persistent_config = mfree(p->persistent_config);
|
||||
p->runtime_config = mfree(p->runtime_config);
|
||||
|
||||
p->generator = mfree(p->generator);
|
||||
p->generator_early = mfree(p->generator_early);
|
||||
p->generator_late = mfree(p->generator_late);
|
||||
|
||||
p->transient = mfree(p->transient);
|
||||
|
||||
p->persistent_control = mfree(p->persistent_control);
|
||||
p->runtime_control = mfree(p->runtime_control);
|
||||
|
||||
p->root_dir = mfree(p->root_dir);
|
||||
}
|
||||
|
||||
int lookup_paths_reduce(LookupPaths *p) {
|
||||
_cleanup_free_ struct stat *stats = NULL;
|
||||
size_t n_stats = 0, allocated = 0;
|
||||
unsigned c = 0;
|
||||
int r;
|
||||
|
||||
assert(p);
|
||||
|
||||
p->unit_path = strv_free(p->unit_path);
|
||||
/* Drop duplicates and non-existing directories from the search path. We figure out whether two directories are
|
||||
* the same by comparing their device and inode numbers. Note one special tweak: when we have a root path set,
|
||||
* we do not follow symlinks when retrieving them, because the kernel wouldn't take the root prefix into
|
||||
* account when following symlinks. When we have no root path set this restriction does not apply however. */
|
||||
|
||||
#ifdef HAVE_SYSV_COMPAT
|
||||
p->sysvinit_path = strv_free(p->sysvinit_path);
|
||||
p->sysvrcnd_path = strv_free(p->sysvrcnd_path);
|
||||
#endif
|
||||
if (!p->search_path)
|
||||
return 0;
|
||||
|
||||
while (p->search_path[c]) {
|
||||
struct stat st;
|
||||
unsigned k;
|
||||
|
||||
if (p->root_dir)
|
||||
r = lstat(p->search_path[c], &st);
|
||||
else
|
||||
r = stat(p->search_path[c], &st);
|
||||
if (r < 0) {
|
||||
if (errno == ENOENT)
|
||||
goto remove_item;
|
||||
|
||||
/* If something we don't grok happened, let's better leave it in. */
|
||||
log_debug_errno(errno, "Failed to stat %s: %m", p->search_path[c]);
|
||||
c++;
|
||||
continue;
|
||||
}
|
||||
|
||||
for (k = 0; k < n_stats; k++) {
|
||||
if (stats[k].st_dev == st.st_dev &&
|
||||
stats[k].st_ino == st.st_ino)
|
||||
break;
|
||||
}
|
||||
|
||||
if (k < n_stats) /* Is there already an entry with the same device/inode? */
|
||||
goto remove_item;
|
||||
|
||||
if (!GREEDY_REALLOC(stats, allocated, n_stats+1))
|
||||
return -ENOMEM;
|
||||
|
||||
stats[n_stats++] = st;
|
||||
c++;
|
||||
continue;
|
||||
|
||||
remove_item:
|
||||
free(p->search_path[c]);
|
||||
memmove(p->search_path + c,
|
||||
p->search_path + c + 1,
|
||||
(strv_length(p->search_path + c + 1) + 1) * sizeof(char*));
|
||||
}
|
||||
|
||||
if (strv_isempty(p->search_path)) {
|
||||
log_debug("Ignoring unit files.");
|
||||
p->search_path = strv_free(p->search_path);
|
||||
} else {
|
||||
_cleanup_free_ char *t;
|
||||
|
||||
t = strv_join(p->search_path, "\n\t");
|
||||
if (!t)
|
||||
return -ENOMEM;
|
||||
|
||||
log_debug("Looking for unit files in (higher priority first):\n\t%s", t);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lookup_paths_init_from_scope(LookupPaths *paths,
|
||||
UnitFileScope scope,
|
||||
const char *root_dir) {
|
||||
assert(paths);
|
||||
assert(scope >= 0);
|
||||
assert(scope < _UNIT_FILE_SCOPE_MAX);
|
||||
int lookup_paths_mkdir_generator(LookupPaths *p) {
|
||||
int r, q;
|
||||
|
||||
zero(*paths);
|
||||
assert(p);
|
||||
|
||||
return lookup_paths_init(paths,
|
||||
scope == UNIT_FILE_SYSTEM ? MANAGER_SYSTEM : MANAGER_USER,
|
||||
scope == UNIT_FILE_USER,
|
||||
root_dir,
|
||||
NULL, NULL, NULL);
|
||||
if (!p->generator || !p->generator_early || !p->generator_late)
|
||||
return -EINVAL;
|
||||
|
||||
r = mkdir_p_label(p->generator, 0755);
|
||||
|
||||
q = mkdir_p_label(p->generator_early, 0755);
|
||||
if (q < 0 && r >= 0)
|
||||
r = q;
|
||||
|
||||
q = mkdir_p_label(p->generator_late, 0755);
|
||||
if (q < 0 && r >= 0)
|
||||
r = q;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
void lookup_paths_trim_generator(LookupPaths *p) {
|
||||
assert(p);
|
||||
|
||||
/* Trim empty dirs */
|
||||
|
||||
if (p->generator)
|
||||
(void) rmdir(p->generator);
|
||||
if (p->generator_early)
|
||||
(void) rmdir(p->generator_early);
|
||||
if (p->generator_late)
|
||||
(void) rmdir(p->generator_late);
|
||||
}
|
||||
|
||||
void lookup_paths_flush_generator(LookupPaths *p) {
|
||||
assert(p);
|
||||
|
||||
/* Flush the generated unit files in full */
|
||||
|
||||
if (p->generator)
|
||||
(void) rm_rf(p->generator, REMOVE_ROOT);
|
||||
if (p->generator_early)
|
||||
(void) rm_rf(p->generator_early, REMOVE_ROOT);
|
||||
if (p->generator_late)
|
||||
(void) rm_rf(p->generator_late, REMOVE_ROOT);
|
||||
}
|
||||
|
||||
char **generator_binary_paths(UnitFileScope scope) {
|
||||
|
||||
switch (scope) {
|
||||
|
||||
case UNIT_FILE_SYSTEM:
|
||||
return strv_new("/run/systemd/system-generators",
|
||||
"/etc/systemd/system-generators",
|
||||
"/usr/local/lib/systemd/system-generators",
|
||||
SYSTEM_GENERATOR_PATH,
|
||||
NULL);
|
||||
|
||||
case UNIT_FILE_GLOBAL:
|
||||
case UNIT_FILE_USER:
|
||||
return strv_new("/run/systemd/user-generators",
|
||||
"/etc/systemd/user-generators",
|
||||
"/usr/local/lib/systemd/user-generators",
|
||||
USER_GENERATOR_PATH,
|
||||
NULL);
|
||||
|
||||
default:
|
||||
assert_not_reached("Hmm, unexpected scope.");
|
||||
}
|
||||
}
|
||||
|
@ -20,41 +20,57 @@
|
||||
***/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "macro.h"
|
||||
|
||||
typedef struct LookupPaths {
|
||||
char **unit_path;
|
||||
#ifdef HAVE_SYSV_COMPAT
|
||||
char **sysvinit_path;
|
||||
char **sysvrcnd_path;
|
||||
#endif
|
||||
} LookupPaths;
|
||||
|
||||
typedef enum ManagerRunningAs {
|
||||
MANAGER_SYSTEM,
|
||||
MANAGER_USER,
|
||||
_MANAGER_RUNNING_AS_MAX,
|
||||
_MANAGER_RUNNING_AS_INVALID = -1
|
||||
} ManagerRunningAs;
|
||||
|
||||
int user_config_home(char **config_home);
|
||||
int user_runtime_dir(char **runtime_dir);
|
||||
|
||||
char **generator_paths(ManagerRunningAs running_as);
|
||||
|
||||
int lookup_paths_init(LookupPaths *p,
|
||||
ManagerRunningAs running_as,
|
||||
bool personal,
|
||||
const char *root_dir,
|
||||
const char *generator,
|
||||
const char *generator_early,
|
||||
const char *generator_late);
|
||||
typedef struct LookupPaths LookupPaths;
|
||||
|
||||
#include "install.h"
|
||||
#include "macro.h"
|
||||
|
||||
int lookup_paths_init_from_scope(LookupPaths *paths,
|
||||
UnitFileScope scope,
|
||||
const char *root_dir);
|
||||
typedef enum LookupPathsFlags {
|
||||
LOOKUP_PATHS_EXCLUDE_GENERATED = 1,
|
||||
} LookupPathsFlags;
|
||||
|
||||
struct LookupPaths {
|
||||
/* Where we look for unit files. This includes the individual special paths below, but also any vendor
|
||||
* supplied, static unit file paths. */
|
||||
char **search_path;
|
||||
|
||||
/* Where we shall create or remove our installation symlinks, aka "configuration", and where the user/admin
|
||||
* shall place his own unit files. */
|
||||
char *persistent_config;
|
||||
char *runtime_config;
|
||||
|
||||
/* Where to place generated unit files (i.e. those a "generator" tool generated). Note the special semantics of
|
||||
* this directory: the generators are flushed each time a "systemctl daemon-reload" is issued. The user should
|
||||
* not alter these directories directly. */
|
||||
char *generator;
|
||||
char *generator_early;
|
||||
char *generator_late;
|
||||
|
||||
/* Where to place transient unit files (i.e. those created dynamically via the bus API). Note the special
|
||||
* semantics of this directory: all units created transiently have their unit files removed as the transient
|
||||
* unit is unloaded. The user should not alter this directory directly. */
|
||||
char *transient;
|
||||
|
||||
/* Where the snippets created by "systemctl set-property" are placed. Note that for transient units, the
|
||||
* snippets are placed in the transient directory though (see above). The user should not alter this directory
|
||||
* directly. */
|
||||
char *persistent_control;
|
||||
char *runtime_control;
|
||||
|
||||
/* The root directory prepended to all items above, or NULL */
|
||||
char *root_dir;
|
||||
};
|
||||
|
||||
int lookup_paths_init(LookupPaths *p, UnitFileScope scope, LookupPathsFlags flags, const char *root_dir);
|
||||
|
||||
int lookup_paths_reduce(LookupPaths *p);
|
||||
|
||||
int lookup_paths_mkdir_generator(LookupPaths *p);
|
||||
void lookup_paths_trim_generator(LookupPaths *p);
|
||||
void lookup_paths_flush_generator(LookupPaths *p);
|
||||
|
||||
void lookup_paths_free(LookupPaths *p);
|
||||
#define _cleanup_lookup_paths_free_ _cleanup_(lookup_paths_free)
|
||||
|
||||
char **generator_binary_paths(UnitFileScope scope);
|
||||
|
33
src/shared/tests.c
Normal file
33
src/shared/tests.c
Normal file
@ -0,0 +1,33 @@
|
||||
/***
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright 2016 Lennart Poettering
|
||||
|
||||
systemd is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
systemd is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <util.h>
|
||||
|
||||
#include "tests.h"
|
||||
|
||||
char* setup_fake_runtime_dir(void) {
|
||||
char t[] = "/tmp/fake-xdg-runtime-XXXXXX", *p;
|
||||
|
||||
assert_se(mkdtemp(t));
|
||||
assert_se(setenv("XDG_RUNTIME_DIR", t, 1) >= 0);
|
||||
assert_se(p = strdup(t));
|
||||
|
||||
return p;
|
||||
}
|
22
src/shared/tests.h
Normal file
22
src/shared/tests.h
Normal file
@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
/***
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright 2016 Lennart Poettering
|
||||
|
||||
systemd is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
systemd is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
char* setup_fake_runtime_dir(void);
|
@ -1993,7 +1993,7 @@ static void dump_unit_file_changes(const UnitFileChange *changes, unsigned n_cha
|
||||
if (changes[i].type == UNIT_FILE_SYMLINK)
|
||||
log_info("Created symlink %s, pointing to %s.", changes[i].path, changes[i].source);
|
||||
else
|
||||
log_info("Removed symlink %s.", changes[i].path);
|
||||
log_info("Removed %s.", changes[i].path);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2295,7 +2295,7 @@ static int unit_file_find_path(LookupPaths *lp, const char *unit_name, char **un
|
||||
assert(unit_name);
|
||||
assert(unit_path);
|
||||
|
||||
STRV_FOREACH(p, lp->unit_path) {
|
||||
STRV_FOREACH(p, lp->search_path) {
|
||||
_cleanup_free_ char *path;
|
||||
|
||||
path = path_join(arg_root, *p, unit_name);
|
||||
@ -2395,7 +2395,7 @@ static int unit_find_paths(
|
||||
}
|
||||
|
||||
if (dropin_paths) {
|
||||
r = unit_file_find_dropin_paths(lp->unit_path, NULL, names, &dropins);
|
||||
r = unit_file_find_dropin_paths(lp->search_path, NULL, names, &dropins);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
@ -3136,7 +3136,7 @@ static int start_special(int argc, char *argv[], void *userdata) {
|
||||
return r;
|
||||
|
||||
if (a == ACTION_REBOOT && argc > 1) {
|
||||
r = update_reboot_param_file(argv[1]);
|
||||
r = update_reboot_parameter_and_warn(argv[1]);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -4780,34 +4780,6 @@ static int show(int argc, char *argv[], void *userdata) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int init_home_and_lookup_paths(char **user_home, char **user_runtime, LookupPaths *lp) {
|
||||
int r;
|
||||
|
||||
assert(user_home);
|
||||
assert(user_runtime);
|
||||
assert(lp);
|
||||
|
||||
if (arg_scope == UNIT_FILE_USER) {
|
||||
r = user_config_home(user_home);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to query XDG_CONFIG_HOME: %m");
|
||||
else if (r == 0)
|
||||
return log_error_errno(ENOTDIR, "Cannot find units: $XDG_CONFIG_HOME and $HOME are not set.");
|
||||
|
||||
r = user_runtime_dir(user_runtime);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to query XDG_CONFIG_HOME: %m");
|
||||
else if (r == 0)
|
||||
return log_error_errno(ENOTDIR, "Cannot find units: $XDG_RUNTIME_DIR is not set.");
|
||||
}
|
||||
|
||||
r = lookup_paths_init_from_scope(lp, arg_scope, arg_root);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to query unit lookup paths: %m");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cat_file(const char *filename, bool newline) {
|
||||
_cleanup_close_ int fd;
|
||||
|
||||
@ -4826,8 +4798,6 @@ static int cat_file(const char *filename, bool newline) {
|
||||
}
|
||||
|
||||
static int cat(int argc, char *argv[], void *userdata) {
|
||||
_cleanup_free_ char *user_home = NULL;
|
||||
_cleanup_free_ char *user_runtime = NULL;
|
||||
_cleanup_lookup_paths_free_ LookupPaths lp = {};
|
||||
_cleanup_strv_free_ char **names = NULL;
|
||||
char **name;
|
||||
@ -4840,9 +4810,9 @@ static int cat(int argc, char *argv[], void *userdata) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
r = init_home_and_lookup_paths(&user_home, &user_runtime, &lp);
|
||||
r = lookup_paths_init(&lp, arg_scope, 0, arg_root);
|
||||
if (r < 0)
|
||||
return r;
|
||||
return log_error_errno(r, "Failed to determine unit paths: %m");
|
||||
|
||||
r = acquire_bus(BUS_MANAGER, &bus);
|
||||
if (r < 0)
|
||||
@ -5253,8 +5223,10 @@ static int enable_sysv_units(const char *verb, char **args) {
|
||||
int r = 0;
|
||||
|
||||
#if defined(HAVE_SYSV_COMPAT)
|
||||
unsigned f = 0;
|
||||
_cleanup_lookup_paths_free_ LookupPaths paths = {};
|
||||
unsigned f = 0;
|
||||
|
||||
/* Processes all SysV units, and reshuffles the array so that afterwards only the native units remain */
|
||||
|
||||
if (arg_scope != UNIT_FILE_SYSTEM)
|
||||
return 0;
|
||||
@ -5268,24 +5240,28 @@ static int enable_sysv_units(const char *verb, char **args) {
|
||||
"is-enabled"))
|
||||
return 0;
|
||||
|
||||
/* Processes all SysV units, and reshuffles the array so that
|
||||
* afterwards only the native units remain */
|
||||
|
||||
r = lookup_paths_init(&paths, MANAGER_SYSTEM, false, arg_root, NULL, NULL, NULL);
|
||||
r = lookup_paths_init(&paths, arg_scope, LOOKUP_PATHS_EXCLUDE_GENERATED, arg_root);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = 0;
|
||||
while (args[f]) {
|
||||
const char *name;
|
||||
|
||||
const char *argv[] = {
|
||||
ROOTLIBEXECDIR "/systemd-sysv-install",
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
};
|
||||
|
||||
_cleanup_free_ char *p = NULL, *q = NULL, *l = NULL;
|
||||
bool found_native = false, found_sysv;
|
||||
unsigned c = 1;
|
||||
const char *argv[6] = { ROOTLIBEXECDIR "/systemd-sysv-install", NULL, NULL, NULL, NULL };
|
||||
char **k;
|
||||
int j;
|
||||
pid_t pid;
|
||||
siginfo_t status;
|
||||
const char *name;
|
||||
unsigned c = 1;
|
||||
pid_t pid;
|
||||
int j;
|
||||
|
||||
name = args[f++];
|
||||
|
||||
@ -5295,21 +5271,13 @@ static int enable_sysv_units(const char *verb, char **args) {
|
||||
if (path_is_absolute(name))
|
||||
continue;
|
||||
|
||||
STRV_FOREACH(k, paths.unit_path) {
|
||||
_cleanup_free_ char *path = NULL;
|
||||
j = unit_file_exists(arg_scope, &paths, name);
|
||||
if (j < 0 && !IN_SET(j, -ELOOP, -ESHUTDOWN, -EADDRNOTAVAIL))
|
||||
return log_error_errno(j, "Failed to lookup unit file state: %m");
|
||||
found_native = j != 0;
|
||||
|
||||
path = path_join(arg_root, *k, name);
|
||||
if (!path)
|
||||
return log_oom();
|
||||
|
||||
found_native = access(path, F_OK) >= 0;
|
||||
if (found_native)
|
||||
break;
|
||||
}
|
||||
|
||||
/* If we have both a native unit and a SysV script,
|
||||
* enable/disable them both (below); for is-enabled, prefer the
|
||||
* native unit */
|
||||
/* If we have both a native unit and a SysV script, enable/disable them both (below); for is-enabled,
|
||||
* prefer the native unit */
|
||||
if (found_native && streq(verb, "is-enabled"))
|
||||
continue;
|
||||
|
||||
@ -5323,9 +5291,9 @@ static int enable_sysv_units(const char *verb, char **args) {
|
||||
continue;
|
||||
|
||||
if (found_native)
|
||||
log_info("Synchronizing state of %s with SysV init with %s...", name, argv[0]);
|
||||
log_info("Synchronizing state of %s with SysV service script with %s.", name, argv[0]);
|
||||
else
|
||||
log_info("%s is not a native service, redirecting to systemd-sysv-install", name);
|
||||
log_info("%s is not a native service, redirecting to systemd-sysv-install.", name);
|
||||
|
||||
if (!isempty(arg_root))
|
||||
argv[c++] = q = strappend("--root=", arg_root);
|
||||
@ -5338,7 +5306,7 @@ static int enable_sysv_units(const char *verb, char **args) {
|
||||
if (!l)
|
||||
return log_oom();
|
||||
|
||||
log_info("Executing %s", l);
|
||||
log_info("Executing: %s", l);
|
||||
|
||||
pid = fork();
|
||||
if (pid < 0)
|
||||
@ -5350,7 +5318,7 @@ static int enable_sysv_units(const char *verb, char **args) {
|
||||
(void) reset_signal_mask();
|
||||
|
||||
execv(argv[0], (char**) argv);
|
||||
log_error_errno(r, "Failed to execute %s: %m", argv[0]);
|
||||
log_error_errno(errno, "Failed to execute %s: %m", argv[0]);
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
@ -5372,9 +5340,11 @@ static int enable_sysv_units(const char *verb, char **args) {
|
||||
}
|
||||
|
||||
} else if (status.si_status != 0)
|
||||
return -EINVAL;
|
||||
} else
|
||||
return -EBADE; /* We don't warn here, under the assumption the script already showed an explanation */
|
||||
} else {
|
||||
log_error("Unexpected waitid() result.");
|
||||
return -EPROTO;
|
||||
}
|
||||
|
||||
if (found_native)
|
||||
continue;
|
||||
@ -5445,8 +5415,7 @@ static int enable_unit(int argc, char *argv[], void *userdata) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* If the operation was fully executed by the SysV compat,
|
||||
* let's finish early */
|
||||
/* If the operation was fully executed by the SysV compat, let's finish early */
|
||||
if (strv_isempty(names))
|
||||
return 0;
|
||||
|
||||
@ -5468,11 +5437,17 @@ static int enable_unit(int argc, char *argv[], void *userdata) {
|
||||
r = unit_file_mask(arg_scope, arg_runtime, arg_root, names, arg_force, &changes, &n_changes);
|
||||
else if (streq(verb, "unmask"))
|
||||
r = unit_file_unmask(arg_scope, arg_runtime, arg_root, names, &changes, &n_changes);
|
||||
else if (streq(verb, "revert"))
|
||||
r = unit_file_revert(arg_scope, arg_root, names, &changes, &n_changes);
|
||||
else
|
||||
assert_not_reached("Unknown verb");
|
||||
|
||||
if (r == -ESHUTDOWN)
|
||||
return log_error_errno(r, "Unit file is masked.");
|
||||
if (r == -EADDRNOTAVAIL)
|
||||
return log_error_errno(r, "Unit file is transient or generated.");
|
||||
if (r == -ELOOP)
|
||||
return log_error_errno(r, "Refusing to operate on linked unit file.");
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Operation failed: %m");
|
||||
|
||||
@ -5484,7 +5459,7 @@ static int enable_unit(int argc, char *argv[], void *userdata) {
|
||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL, *m = NULL;
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
int expect_carries_install_info = false;
|
||||
bool send_force = true, send_preset_mode = false;
|
||||
bool send_runtime = true, send_force = true, send_preset_mode = false;
|
||||
const char *method;
|
||||
sd_bus *bus;
|
||||
|
||||
@ -5519,6 +5494,9 @@ static int enable_unit(int argc, char *argv[], void *userdata) {
|
||||
else if (streq(verb, "unmask")) {
|
||||
method = "UnmaskUnitFiles";
|
||||
send_force = false;
|
||||
} else if (streq(verb, "revert")) {
|
||||
method = "RevertUnitFiles";
|
||||
send_runtime = send_force = false;
|
||||
} else
|
||||
assert_not_reached("Unknown verb");
|
||||
|
||||
@ -5542,9 +5520,11 @@ static int enable_unit(int argc, char *argv[], void *userdata) {
|
||||
return bus_log_create_error(r);
|
||||
}
|
||||
|
||||
r = sd_bus_message_append(m, "b", arg_runtime);
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
if (send_runtime) {
|
||||
r = sd_bus_message_append(m, "b", arg_runtime);
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
}
|
||||
|
||||
if (send_force) {
|
||||
r = sd_bus_message_append(m, "b", arg_force);
|
||||
@ -5639,6 +5619,8 @@ static int add_dependency(int argc, char *argv[], void *userdata) {
|
||||
r = unit_file_add_dependency(arg_scope, arg_runtime, arg_root, names, target, dep, arg_force, &changes, &n_changes);
|
||||
if (r == -ESHUTDOWN)
|
||||
return log_error_errno(r, "Unit file is masked.");
|
||||
if (r == -EADDRNOTAVAIL)
|
||||
return log_error_errno(r, "Unit file is transient or generated.");
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Can't add dependency: %m");
|
||||
|
||||
@ -5783,7 +5765,8 @@ static int unit_is_enabled(int argc, char *argv[], void *userdata) {
|
||||
UNIT_FILE_ENABLED,
|
||||
UNIT_FILE_ENABLED_RUNTIME,
|
||||
UNIT_FILE_STATIC,
|
||||
UNIT_FILE_INDIRECT))
|
||||
UNIT_FILE_INDIRECT,
|
||||
UNIT_FILE_GENERATED))
|
||||
enabled = true;
|
||||
|
||||
if (!arg_quiet)
|
||||
@ -5818,7 +5801,7 @@ static int unit_is_enabled(int argc, char *argv[], void *userdata) {
|
||||
if (r < 0)
|
||||
return bus_log_parse_error(r);
|
||||
|
||||
if (STR_IN_SET(s, "enabled", "enabled-runtime", "static", "indirect"))
|
||||
if (STR_IN_SET(s, "enabled", "enabled-runtime", "static", "indirect", "generated"))
|
||||
enabled = true;
|
||||
|
||||
if (!arg_quiet)
|
||||
@ -5826,7 +5809,7 @@ static int unit_is_enabled(int argc, char *argv[], void *userdata) {
|
||||
}
|
||||
}
|
||||
|
||||
return !enabled;
|
||||
return enabled ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
|
||||
static int is_system_running(int argc, char *argv[], void *userdata) {
|
||||
@ -5896,52 +5879,32 @@ static int create_edit_temp_file(const char *new_path, const char *original_path
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_file_to_edit(const char *name, const char *user_home, const char *user_runtime, char **ret_path) {
|
||||
_cleanup_free_ char *path = NULL, *path2 = NULL, *run = NULL;
|
||||
static int get_file_to_edit(
|
||||
const LookupPaths *paths,
|
||||
const char *name,
|
||||
char **ret_path) {
|
||||
|
||||
_cleanup_free_ char *path = NULL, *run = NULL;
|
||||
|
||||
assert(name);
|
||||
assert(ret_path);
|
||||
|
||||
switch (arg_scope) {
|
||||
case UNIT_FILE_SYSTEM:
|
||||
path = path_join(arg_root, SYSTEM_CONFIG_UNIT_PATH, name);
|
||||
if (arg_runtime)
|
||||
run = path_join(arg_root, "/run/systemd/system/", name);
|
||||
break;
|
||||
case UNIT_FILE_GLOBAL:
|
||||
path = path_join(arg_root, USER_CONFIG_UNIT_PATH, name);
|
||||
if (arg_runtime)
|
||||
run = path_join(arg_root, "/run/systemd/user/", name);
|
||||
break;
|
||||
case UNIT_FILE_USER:
|
||||
assert(user_home);
|
||||
assert(user_runtime);
|
||||
|
||||
path = path_join(arg_root, user_home, name);
|
||||
if (arg_runtime) {
|
||||
path2 = path_join(arg_root, USER_CONFIG_UNIT_PATH, name);
|
||||
if (!path2)
|
||||
return log_oom();
|
||||
run = path_join(arg_root, user_runtime, name);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
assert_not_reached("Invalid scope");
|
||||
}
|
||||
if (!path || (arg_runtime && !run))
|
||||
path = strjoin(paths->persistent_config, "/", name, NULL);
|
||||
if (!path)
|
||||
return log_oom();
|
||||
|
||||
if (arg_runtime) {
|
||||
run = strjoin(paths->runtime_config, name, NULL);
|
||||
if (!run)
|
||||
return log_oom();
|
||||
}
|
||||
|
||||
if (arg_runtime) {
|
||||
if (access(path, F_OK) >= 0) {
|
||||
log_error("Refusing to create \"%s\" because it would be overridden by \"%s\" anyway.", run, path);
|
||||
return -EEXIST;
|
||||
}
|
||||
|
||||
if (path2 && access(path2, F_OK) >= 0) {
|
||||
log_error("Refusing to create \"%s\" because it would be overridden by \"%s\" anyway.", run, path2);
|
||||
return -EEXIST;
|
||||
}
|
||||
|
||||
*ret_path = run;
|
||||
run = NULL;
|
||||
} else {
|
||||
@ -5952,7 +5915,12 @@ static int get_file_to_edit(const char *name, const char *user_home, const char
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int unit_file_create_dropin(const char *unit_name, const char *user_home, const char *user_runtime, char **ret_new_path, char **ret_tmp_path) {
|
||||
static int unit_file_create_dropin(
|
||||
const LookupPaths *paths,
|
||||
const char *unit_name,
|
||||
char **ret_new_path,
|
||||
char **ret_tmp_path) {
|
||||
|
||||
char *tmp_new_path, *tmp_tmp_path, *ending;
|
||||
int r;
|
||||
|
||||
@ -5961,7 +5929,7 @@ static int unit_file_create_dropin(const char *unit_name, const char *user_home,
|
||||
assert(ret_tmp_path);
|
||||
|
||||
ending = strjoina(unit_name, ".d/override.conf");
|
||||
r = get_file_to_edit(ending, user_home, user_runtime, &tmp_new_path);
|
||||
r = get_file_to_edit(paths, ending, &tmp_new_path);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -5978,10 +5946,9 @@ static int unit_file_create_dropin(const char *unit_name, const char *user_home,
|
||||
}
|
||||
|
||||
static int unit_file_create_copy(
|
||||
const LookupPaths *paths,
|
||||
const char *unit_name,
|
||||
const char *fragment_path,
|
||||
const char *user_home,
|
||||
const char *user_runtime,
|
||||
char **ret_new_path,
|
||||
char **ret_tmp_path) {
|
||||
|
||||
@ -5993,7 +5960,7 @@ static int unit_file_create_copy(
|
||||
assert(ret_new_path);
|
||||
assert(ret_tmp_path);
|
||||
|
||||
r = get_file_to_edit(unit_name, user_home, user_runtime, &tmp_new_path);
|
||||
r = get_file_to_edit(paths, unit_name, &tmp_new_path);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -6108,8 +6075,6 @@ static int run_editor(char **paths) {
|
||||
}
|
||||
|
||||
static int find_paths_to_edit(sd_bus *bus, char **names, char ***paths) {
|
||||
_cleanup_free_ char *user_home = NULL;
|
||||
_cleanup_free_ char *user_runtime = NULL;
|
||||
_cleanup_lookup_paths_free_ LookupPaths lp = {};
|
||||
char **name;
|
||||
int r;
|
||||
@ -6117,7 +6082,7 @@ static int find_paths_to_edit(sd_bus *bus, char **names, char ***paths) {
|
||||
assert(names);
|
||||
assert(paths);
|
||||
|
||||
r = init_home_and_lookup_paths(&user_home, &user_runtime, &lp);
|
||||
r = lookup_paths_init(&lp, arg_scope, 0, arg_root);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -6137,9 +6102,9 @@ static int find_paths_to_edit(sd_bus *bus, char **names, char ***paths) {
|
||||
}
|
||||
|
||||
if (arg_full)
|
||||
r = unit_file_create_copy(*name, path, user_home, user_runtime, &new_path, &tmp_path);
|
||||
r = unit_file_create_copy(&lp, *name, path, &new_path, &tmp_path);
|
||||
else
|
||||
r = unit_file_create_dropin(*name, user_home, user_runtime, &new_path, &tmp_path);
|
||||
r = unit_file_create_dropin(&lp, *name, &new_path, &tmp_path);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -6324,6 +6289,8 @@ static void systemctl_help(void) {
|
||||
" unmask NAME... Unmask one or more units\n"
|
||||
" link PATH... Link one or more units files into\n"
|
||||
" the search path\n"
|
||||
" revert NAME... Revert one or more unit files to vendor\n"
|
||||
" version\n"
|
||||
" add-wants TARGET NAME... Add 'Wants' dependency for the target\n"
|
||||
" on specified one or more units\n"
|
||||
" add-requires TARGET NAME... Add 'Requires' dependency for the target\n"
|
||||
@ -6992,7 +6959,7 @@ static int halt_parse_argv(int argc, char *argv[]) {
|
||||
}
|
||||
|
||||
if (arg_action == ACTION_REBOOT && (argc == optind || argc == optind + 1)) {
|
||||
r = update_reboot_param_file(argc == optind + 1 ? argv[optind] : NULL);
|
||||
r = update_reboot_parameter_and_warn(argc == optind + 1 ? argv[optind] : NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
} else if (optind < argc) {
|
||||
@ -7447,6 +7414,7 @@ static int systemctl_main(int argc, char *argv[]) {
|
||||
{ "mask", 2, VERB_ANY, 0, enable_unit },
|
||||
{ "unmask", 2, VERB_ANY, 0, enable_unit },
|
||||
{ "link", 2, VERB_ANY, 0, enable_unit },
|
||||
{ "revert", 2, VERB_ANY, 0, enable_unit },
|
||||
{ "switch-root", 2, VERB_ANY, VERB_NOCHROOT, switch_root },
|
||||
{ "list-dependencies", VERB_ANY, 2, VERB_NOCHROOT, list_dependencies },
|
||||
{ "set-default", 2, 2, 0, set_default },
|
||||
@ -7493,6 +7461,7 @@ static int start_with_fallback(void) {
|
||||
}
|
||||
|
||||
static int halt_now(enum action a) {
|
||||
int r;
|
||||
|
||||
/* The kernel will automaticall flush ATA disks and suchlike
|
||||
* on reboot(), but the file systems need to be synce'd
|
||||
@ -7519,9 +7488,14 @@ static int halt_now(enum action a) {
|
||||
case ACTION_REBOOT: {
|
||||
_cleanup_free_ char *param = NULL;
|
||||
|
||||
if (read_one_line_file(REBOOT_PARAM_FILE, ¶m) >= 0) {
|
||||
r = read_one_line_file("/run/systemd/reboot-param", ¶m);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to read reboot parameter file: %m");
|
||||
|
||||
if (!isempty(param)) {
|
||||
log_info("Rebooting with argument '%s'.", param);
|
||||
(void) syscall(SYS_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART2, param);
|
||||
log_warning_errno(errno, "Failed to reboot with parameter, retrying without: %m");
|
||||
}
|
||||
|
||||
log_info("Rebooting.");
|
||||
|
@ -33,20 +33,18 @@ _SD_BEGIN_DECLARATIONS;
|
||||
typedef struct sd_lldp sd_lldp;
|
||||
typedef struct sd_lldp_neighbor sd_lldp_neighbor;
|
||||
|
||||
#define SD_LLDP_MULTICAST_ADDR { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e }
|
||||
|
||||
/* IEEE 802.3AB Clause 9: TLV Types */
|
||||
enum {
|
||||
SD_LLDP_TYPE_END = 0,
|
||||
SD_LLDP_TYPE_CHASSIS_ID = 1,
|
||||
SD_LLDP_TYPE_PORT_ID = 2,
|
||||
SD_LLDP_TYPE_TTL = 3,
|
||||
SD_LLDP_TYPE_PORT_DESCRIPTION = 4,
|
||||
SD_LLDP_TYPE_SYSTEM_NAME = 5,
|
||||
SD_LLDP_TYPE_SYSTEM_DESCRIPTION = 6,
|
||||
SD_LLDP_TYPE_SYSTEM_CAPABILITIES = 7,
|
||||
SD_LLDP_TYPE_MGMT_ADDRESS = 8,
|
||||
SD_LLDP_TYPE_PRIVATE = 127,
|
||||
SD_LLDP_TYPE_END = 0,
|
||||
SD_LLDP_TYPE_CHASSIS_ID = 1,
|
||||
SD_LLDP_TYPE_PORT_ID = 2,
|
||||
SD_LLDP_TYPE_TTL = 3,
|
||||
SD_LLDP_TYPE_PORT_DESCRIPTION = 4,
|
||||
SD_LLDP_TYPE_SYSTEM_NAME = 5,
|
||||
SD_LLDP_TYPE_SYSTEM_DESCRIPTION = 6,
|
||||
SD_LLDP_TYPE_SYSTEM_CAPABILITIES = 7,
|
||||
SD_LLDP_TYPE_MGMT_ADDRESS = 8,
|
||||
SD_LLDP_TYPE_PRIVATE = 127,
|
||||
};
|
||||
|
||||
/* IEEE 802.3AB Clause 9.5.2: Chassis subtypes */
|
||||
@ -63,28 +61,28 @@ enum {
|
||||
|
||||
/* IEEE 802.3AB Clause 9.5.3: Port subtype */
|
||||
enum {
|
||||
SD_LLDP_PORT_SUBTYPE_RESERVED = 0,
|
||||
SD_LLDP_PORT_SUBTYPE_INTERFACE_ALIAS = 1,
|
||||
SD_LLDP_PORT_SUBTYPE_PORT_COMPONENT = 2,
|
||||
SD_LLDP_PORT_SUBTYPE_MAC_ADDRESS = 3,
|
||||
SD_LLDP_PORT_SUBTYPE_NETWORK_ADDRESS = 4,
|
||||
SD_LLDP_PORT_SUBTYPE_INTERFACE_NAME = 5,
|
||||
SD_LLDP_PORT_SUBTYPE_AGENT_CIRCUIT_ID = 6,
|
||||
SD_LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED = 7,
|
||||
SD_LLDP_PORT_SUBTYPE_RESERVED = 0,
|
||||
SD_LLDP_PORT_SUBTYPE_INTERFACE_ALIAS = 1,
|
||||
SD_LLDP_PORT_SUBTYPE_PORT_COMPONENT = 2,
|
||||
SD_LLDP_PORT_SUBTYPE_MAC_ADDRESS = 3,
|
||||
SD_LLDP_PORT_SUBTYPE_NETWORK_ADDRESS = 4,
|
||||
SD_LLDP_PORT_SUBTYPE_INTERFACE_NAME = 5,
|
||||
SD_LLDP_PORT_SUBTYPE_AGENT_CIRCUIT_ID = 6,
|
||||
SD_LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED = 7,
|
||||
};
|
||||
|
||||
enum {
|
||||
SD_LLDP_SYSTEM_CAPABILITIES_OTHER = 1 << 0,
|
||||
SD_LLDP_SYSTEM_CAPABILITIES_REPEATER = 1 << 1,
|
||||
SD_LLDP_SYSTEM_CAPABILITIES_BRIDGE = 1 << 2,
|
||||
SD_LLDP_SYSTEM_CAPABILITIES_WLAN_AP = 1 << 3,
|
||||
SD_LLDP_SYSTEM_CAPABILITIES_ROUTER = 1 << 4,
|
||||
SD_LLDP_SYSTEM_CAPABILITIES_PHONE = 1 << 5,
|
||||
SD_LLDP_SYSTEM_CAPABILITIES_DOCSIS = 1 << 6,
|
||||
SD_LLDP_SYSTEM_CAPABILITIES_STATION = 1 << 7,
|
||||
SD_LLDP_SYSTEM_CAPABILITIES_CVLAN = 1 << 8,
|
||||
SD_LLDP_SYSTEM_CAPABILITIES_SVLAN = 1 << 9,
|
||||
SD_LLDP_SYSTEM_CAPABILITIES_TPMR = 1 << 10,
|
||||
SD_LLDP_SYSTEM_CAPABILITIES_OTHER = 1 << 0,
|
||||
SD_LLDP_SYSTEM_CAPABILITIES_REPEATER = 1 << 1,
|
||||
SD_LLDP_SYSTEM_CAPABILITIES_BRIDGE = 1 << 2,
|
||||
SD_LLDP_SYSTEM_CAPABILITIES_WLAN_AP = 1 << 3,
|
||||
SD_LLDP_SYSTEM_CAPABILITIES_ROUTER = 1 << 4,
|
||||
SD_LLDP_SYSTEM_CAPABILITIES_PHONE = 1 << 5,
|
||||
SD_LLDP_SYSTEM_CAPABILITIES_DOCSIS = 1 << 6,
|
||||
SD_LLDP_SYSTEM_CAPABILITIES_STATION = 1 << 7,
|
||||
SD_LLDP_SYSTEM_CAPABILITIES_CVLAN = 1 << 8,
|
||||
SD_LLDP_SYSTEM_CAPABILITIES_SVLAN = 1 << 9,
|
||||
SD_LLDP_SYSTEM_CAPABILITIES_TPMR = 1 << 10,
|
||||
};
|
||||
|
||||
#define SD_LLDP_SYSTEM_CAPABILITIES_ALL ((uint16_t) -1)
|
||||
@ -104,13 +102,13 @@ enum {
|
||||
#define SD_LLDP_OUI_802_3 (uint8_t[]) { 0x00, 0x12, 0x0f }
|
||||
|
||||
enum {
|
||||
SD_LLDP_OUI_802_1_SUBTYPE_PORT_VLAN_ID = 1,
|
||||
SD_LLDP_OUI_802_1_SUBTYPE_PORT_PROTOCOL_VLAN_ID = 2,
|
||||
SD_LLDP_OUI_802_1_SUBTYPE_VLAN_NAME = 3,
|
||||
SD_LLDP_OUI_802_1_SUBTYPE_PROTOCOL_IDENTITY = 4,
|
||||
SD_LLDP_OUI_802_1_SUBTYPE_VID_USAGE_DIGEST = 5,
|
||||
SD_LLDP_OUI_802_1_SUBTYPE_MANAGEMENT_VID = 6,
|
||||
SD_LLDP_OUI_802_1_SUBTYPE_LINK_AGGREGATION = 7,
|
||||
SD_LLDP_OUI_802_1_SUBTYPE_PORT_VLAN_ID = 1,
|
||||
SD_LLDP_OUI_802_1_SUBTYPE_PORT_PROTOCOL_VLAN_ID = 2,
|
||||
SD_LLDP_OUI_802_1_SUBTYPE_VLAN_NAME = 3,
|
||||
SD_LLDP_OUI_802_1_SUBTYPE_PROTOCOL_IDENTITY = 4,
|
||||
SD_LLDP_OUI_802_1_SUBTYPE_VID_USAGE_DIGEST = 5,
|
||||
SD_LLDP_OUI_802_1_SUBTYPE_MANAGEMENT_VID = 6,
|
||||
SD_LLDP_OUI_802_1_SUBTYPE_LINK_AGGREGATION = 7,
|
||||
};
|
||||
|
||||
typedef enum sd_lldp_event {
|
||||
|
@ -729,14 +729,50 @@ static int fix_order(SysvStub *s, Hashmap *all_services) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acquire_search_path(const char *def, const char *envvar, char ***ret) {
|
||||
_cleanup_strv_free_ char **l = NULL;
|
||||
const char *e;
|
||||
int r;
|
||||
|
||||
assert(def);
|
||||
assert(envvar);
|
||||
|
||||
e = getenv(envvar);
|
||||
if (e) {
|
||||
r = path_split_and_make_absolute(e, &l);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to make $%s search path absolute: %m", envvar);
|
||||
}
|
||||
|
||||
if (strv_isempty(l)) {
|
||||
strv_free(l);
|
||||
|
||||
l = strv_new(def, NULL);
|
||||
if (!l)
|
||||
return log_oom();
|
||||
}
|
||||
|
||||
if (!path_strv_resolve_uniq(l, NULL))
|
||||
return log_oom();
|
||||
|
||||
*ret = l;
|
||||
l = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int enumerate_sysv(const LookupPaths *lp, Hashmap *all_services) {
|
||||
_cleanup_strv_free_ char **sysvinit_path = NULL;
|
||||
char **path;
|
||||
int r;
|
||||
|
||||
assert(lp);
|
||||
assert(all_services);
|
||||
|
||||
STRV_FOREACH(path, lp->sysvinit_path) {
|
||||
r = acquire_search_path(SYSTEM_SYSVINIT_PATH, "SYSTEMD_SYSVINIT_PATH", &sysvinit_path);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
STRV_FOREACH(path, sysvinit_path) {
|
||||
_cleanup_closedir_ DIR *d = NULL;
|
||||
struct dirent *de;
|
||||
|
||||
@ -770,11 +806,11 @@ static int enumerate_sysv(const LookupPaths *lp, Hashmap *all_services) {
|
||||
if (hashmap_contains(all_services, name))
|
||||
continue;
|
||||
|
||||
r = unit_file_lookup_state(UNIT_FILE_SYSTEM, NULL, lp, name, NULL);
|
||||
if (r < 0 && r != -ENOENT) {
|
||||
r = unit_file_exists(UNIT_FILE_SYSTEM, lp, name);
|
||||
if (r < 0 && !IN_SET(r, -ELOOP, -ESHUTDOWN, -EADDRNOTAVAIL)) {
|
||||
log_debug_errno(r, "Failed to detect whether %s exists, skipping: %m", name);
|
||||
continue;
|
||||
} else if (r >= 0) {
|
||||
} else if (r != 0) {
|
||||
log_debug("Native unit for %s already exists, skipping.", name);
|
||||
continue;
|
||||
}
|
||||
@ -806,6 +842,7 @@ static int enumerate_sysv(const LookupPaths *lp, Hashmap *all_services) {
|
||||
static int set_dependencies_from_rcnd(const LookupPaths *lp, Hashmap *all_services) {
|
||||
Set *runlevel_services[ELEMENTSOF(rcnd_table)] = {};
|
||||
_cleanup_set_free_ Set *shutdown_services = NULL;
|
||||
_cleanup_strv_free_ char **sysvrcnd_path = NULL;
|
||||
SysvStub *service;
|
||||
unsigned i;
|
||||
Iterator j;
|
||||
@ -814,7 +851,11 @@ static int set_dependencies_from_rcnd(const LookupPaths *lp, Hashmap *all_servic
|
||||
|
||||
assert(lp);
|
||||
|
||||
STRV_FOREACH(p, lp->sysvrcnd_path) {
|
||||
r = acquire_search_path(SYSTEM_SYSVRCND_PATH, "SYSTEMD_SYSVRCND_PATH", &sysvrcnd_path);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
STRV_FOREACH(p, sysvrcnd_path) {
|
||||
for (i = 0; i < ELEMENTSOF(rcnd_table); i ++) {
|
||||
|
||||
_cleanup_closedir_ DIR *d = NULL;
|
||||
@ -963,7 +1004,7 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
umask(0022);
|
||||
|
||||
r = lookup_paths_init(&lp, MANAGER_SYSTEM, true, NULL, NULL, NULL, NULL);
|
||||
r = lookup_paths_init(&lp, UNIT_FILE_SYSTEM, LOOKUP_PATHS_EXCLUDE_GENERATED, NULL);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to find lookup paths: %m");
|
||||
goto finish;
|
||||
|
@ -21,7 +21,9 @@
|
||||
|
||||
#include "macro.h"
|
||||
#include "manager.h"
|
||||
#include "rm-rf.h"
|
||||
#include "test-helper.h"
|
||||
#include "tests.h"
|
||||
#include "unit.h"
|
||||
|
||||
static int test_cgroup_mask(void) {
|
||||
@ -33,7 +35,7 @@ static int test_cgroup_mask(void) {
|
||||
|
||||
/* Prepare the manager. */
|
||||
assert_se(set_unit_path(TEST_DIR) >= 0);
|
||||
r = manager_new(MANAGER_USER, true, &m);
|
||||
r = manager_new(UNIT_FILE_USER, true, &m);
|
||||
if (r == -EPERM || r == -EACCES) {
|
||||
puts("manager_new: Permission denied. Skipping test.");
|
||||
return EXIT_TEST_SKIP;
|
||||
@ -107,7 +109,11 @@ static int test_cgroup_mask(void) {
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
_cleanup_(rm_rf_and_freep) char *runtime_dir = NULL;
|
||||
int rc = 0;
|
||||
|
||||
assert_se(runtime_dir = setup_fake_runtime_dir());
|
||||
TEST_REQ_RUNNING_SYSTEMD(rc = test_cgroup_mask());
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
@ -23,9 +23,12 @@
|
||||
|
||||
#include "bus-util.h"
|
||||
#include "manager.h"
|
||||
#include "rm-rf.h"
|
||||
#include "test-helper.h"
|
||||
#include "tests.h"
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
_cleanup_(rm_rf_and_freep) char *runtime_dir = NULL;
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error err = SD_BUS_ERROR_NULL;
|
||||
Manager *m = NULL;
|
||||
Unit *a = NULL, *b = NULL, *c = NULL, *d = NULL, *e = NULL, *g = NULL, *h = NULL;
|
||||
@ -34,9 +37,11 @@ int main(int argc, char *argv[]) {
|
||||
Job *j;
|
||||
int r;
|
||||
|
||||
assert_se(runtime_dir = setup_fake_runtime_dir());
|
||||
|
||||
/* prepare the test */
|
||||
assert_se(set_unit_path(TEST_DIR) >= 0);
|
||||
r = manager_new(MANAGER_USER, true, &m);
|
||||
r = manager_new(UNIT_FILE_USER, true, &m);
|
||||
if (MANAGER_SKIP_TEST(r)) {
|
||||
printf("Skipping test: manager_new: %s\n", strerror(-r));
|
||||
return EXIT_TEST_SKIP;
|
||||
|
@ -291,14 +291,14 @@ static void test_exec_spec_interpolation(Manager *m) {
|
||||
test(m, "exec-spec-interpolation.service", 0, CLD_EXITED);
|
||||
}
|
||||
|
||||
static int run_tests(ManagerRunningAs running_as, test_function_t *tests) {
|
||||
static int run_tests(UnitFileScope scope, test_function_t *tests) {
|
||||
test_function_t *test = NULL;
|
||||
Manager *m = NULL;
|
||||
int r;
|
||||
|
||||
assert_se(tests);
|
||||
|
||||
r = manager_new(running_as, true, &m);
|
||||
r = manager_new(scope, true, &m);
|
||||
if (MANAGER_SKIP_TEST(r)) {
|
||||
printf("Skipping test: manager_new: %s\n", strerror(-r));
|
||||
return EXIT_TEST_SKIP;
|
||||
@ -366,9 +366,9 @@ int main(int argc, char *argv[]) {
|
||||
assert_se(unsetenv("VAR2") == 0);
|
||||
assert_se(unsetenv("VAR3") == 0);
|
||||
|
||||
r = run_tests(MANAGER_USER, user_tests);
|
||||
r = run_tests(UNIT_FILE_USER, user_tests);
|
||||
if (r != 0)
|
||||
return r;
|
||||
|
||||
return run_tests(MANAGER_SYSTEM, system_tests);
|
||||
return run_tests(UNIT_FILE_SYSTEM, system_tests);
|
||||
}
|
||||
|
@ -30,6 +30,8 @@ static void test_basic_mask_and_enable(const char *root) {
|
||||
UnitFileChange *changes = NULL;
|
||||
unsigned n_changes = 0;
|
||||
|
||||
log_set_max_level(LOG_DEBUG);
|
||||
|
||||
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "a.service", NULL) == -ENOENT);
|
||||
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", NULL) == -ENOENT);
|
||||
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", NULL) == -ENOENT);
|
||||
@ -628,6 +630,57 @@ static void test_preset_and_list(const char *root) {
|
||||
assert_se(got_yes && got_no);
|
||||
}
|
||||
|
||||
static void test_revert(const char *root) {
|
||||
const char *p;
|
||||
UnitFileState state;
|
||||
UnitFileChange *changes = NULL;
|
||||
unsigned n_changes = 0;
|
||||
|
||||
assert(root);
|
||||
|
||||
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "xx.service", NULL) == -ENOENT);
|
||||
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "yy.service", NULL) == -ENOENT);
|
||||
|
||||
p = strjoina(root, "/usr/lib/systemd/system/xx.service");
|
||||
assert_se(write_string_file(p, "# Empty\n", WRITE_STRING_FILE_CREATE) >= 0);
|
||||
|
||||
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "xx.service", NULL) >= 0);
|
||||
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "xx.service", &state) >= 0 && state == UNIT_FILE_STATIC);
|
||||
|
||||
/* Initially there's nothing to revert */
|
||||
assert_se(unit_file_revert(UNIT_FILE_SYSTEM, root, STRV_MAKE("xx.service"), &changes, &n_changes) >= 0);
|
||||
assert_se(n_changes == 0);
|
||||
unit_file_changes_free(changes, n_changes);
|
||||
changes = NULL; n_changes = 0;
|
||||
|
||||
p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/xx.service");
|
||||
assert_se(write_string_file(p, "# Empty override\n", WRITE_STRING_FILE_CREATE) >= 0);
|
||||
|
||||
/* Revert the override file */
|
||||
assert_se(unit_file_revert(UNIT_FILE_SYSTEM, root, STRV_MAKE("xx.service"), &changes, &n_changes) >= 0);
|
||||
assert_se(n_changes == 1);
|
||||
assert_se(changes[0].type == UNIT_FILE_UNLINK);
|
||||
assert_se(streq(changes[0].path, p));
|
||||
unit_file_changes_free(changes, n_changes);
|
||||
changes = NULL; n_changes = 0;
|
||||
|
||||
p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/xx.service.d/dropin.conf");
|
||||
assert_se(mkdir_parents(p, 0755) >= 0);
|
||||
assert_se(write_string_file(p, "# Empty dropin\n", WRITE_STRING_FILE_CREATE) >= 0);
|
||||
|
||||
/* Revert the dropin file */
|
||||
assert_se(unit_file_revert(UNIT_FILE_SYSTEM, root, STRV_MAKE("xx.service"), &changes, &n_changes) >= 0);
|
||||
assert_se(n_changes == 2);
|
||||
assert_se(changes[0].type == UNIT_FILE_UNLINK);
|
||||
assert_se(streq(changes[0].path, p));
|
||||
|
||||
p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/xx.service.d");
|
||||
assert_se(changes[1].type == UNIT_FILE_UNLINK);
|
||||
assert_se(streq(changes[1].path, p));
|
||||
unit_file_changes_free(changes, n_changes);
|
||||
changes = NULL; n_changes = 0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
char root[] = "/tmp/rootXXXXXX";
|
||||
const char *p;
|
||||
@ -656,6 +709,7 @@ int main(int argc, char *argv[]) {
|
||||
test_template_enable(root);
|
||||
test_indirect(root);
|
||||
test_preset_and_list(root);
|
||||
test_revert(root);
|
||||
|
||||
assert_se(rm_rf(root, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
|
||||
|
||||
|
@ -26,41 +26,38 @@
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
|
||||
static void test_paths(ManagerRunningAs running_as, bool personal) {
|
||||
static void test_paths(UnitFileScope scope) {
|
||||
char template[] = "/tmp/test-path-lookup.XXXXXXX";
|
||||
|
||||
_cleanup_lookup_paths_free_ LookupPaths lp_without_env = {};
|
||||
_cleanup_lookup_paths_free_ LookupPaths lp_with_env = {};
|
||||
char *exists, *not, *systemd_unit_path;
|
||||
char *systemd_unit_path;
|
||||
|
||||
assert_se(mkdtemp(template));
|
||||
exists = strjoina(template, "/exists");
|
||||
assert_se(mkdir(exists, 0755) == 0);
|
||||
not = strjoina(template, "/not");
|
||||
|
||||
assert_se(unsetenv("SYSTEMD_UNIT_PATH") == 0);
|
||||
assert_se(lookup_paths_init(&lp_without_env, running_as, personal, NULL, exists, not, not) == 0);
|
||||
|
||||
assert_se(!strv_isempty(lp_without_env.unit_path));
|
||||
assert_se(strv_contains(lp_without_env.unit_path, exists));
|
||||
assert_se(strv_contains(lp_without_env.unit_path, not));
|
||||
assert_se(lookup_paths_init(&lp_without_env, scope, 0, NULL) >= 0);
|
||||
assert_se(!strv_isempty(lp_without_env.search_path));
|
||||
assert_se(lookup_paths_reduce(&lp_without_env) >= 0);
|
||||
|
||||
systemd_unit_path = strjoina(template, "/systemd-unit-path");
|
||||
assert_se(setenv("SYSTEMD_UNIT_PATH", systemd_unit_path, 1) == 0);
|
||||
assert_se(lookup_paths_init(&lp_with_env, running_as, personal, NULL, exists, not, not) == 0);
|
||||
assert_se(strv_length(lp_with_env.unit_path) == 1);
|
||||
assert_se(streq(lp_with_env.unit_path[0], systemd_unit_path));
|
||||
assert_se(lookup_paths_init(&lp_with_env, scope, 0, NULL) == 0);
|
||||
assert_se(strv_length(lp_with_env.search_path) == 1);
|
||||
assert_se(streq(lp_with_env.search_path[0], systemd_unit_path));
|
||||
assert_se(lookup_paths_reduce(&lp_with_env) >= 0);
|
||||
assert_se(strv_length(lp_with_env.search_path) == 0);
|
||||
|
||||
assert_se(rm_rf(template, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
|
||||
}
|
||||
|
||||
static void print_generator_paths(ManagerRunningAs running_as) {
|
||||
static void print_generator_binary_paths(UnitFileScope scope) {
|
||||
_cleanup_strv_free_ char **paths;
|
||||
char **dir;
|
||||
|
||||
log_info("Generators dirs (%s):", running_as == MANAGER_SYSTEM ? "system" : "user");
|
||||
log_info("Generators dirs (%s):", scope == UNIT_FILE_SYSTEM ? "system" : "user");
|
||||
|
||||
paths = generator_paths(running_as);
|
||||
paths = generator_binary_paths(scope);
|
||||
STRV_FOREACH(dir, paths)
|
||||
log_info(" %s", *dir);
|
||||
}
|
||||
@ -70,13 +67,12 @@ int main(int argc, char **argv) {
|
||||
log_parse_environment();
|
||||
log_open();
|
||||
|
||||
test_paths(MANAGER_SYSTEM, false);
|
||||
test_paths(MANAGER_SYSTEM, true);
|
||||
test_paths(MANAGER_USER, false);
|
||||
test_paths(MANAGER_USER, true);
|
||||
test_paths(UNIT_FILE_SYSTEM);
|
||||
test_paths(UNIT_FILE_USER);
|
||||
test_paths(UNIT_FILE_GLOBAL);
|
||||
|
||||
print_generator_paths(MANAGER_SYSTEM);
|
||||
print_generator_paths(MANAGER_USER);
|
||||
print_generator_binary_paths(UNIT_FILE_SYSTEM);
|
||||
print_generator_binary_paths(UNIT_FILE_USER);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
#include "test-helper.h"
|
||||
#include "tests.h"
|
||||
#include "unit.h"
|
||||
#include "util.h"
|
||||
|
||||
@ -44,7 +45,7 @@ static int setup_test(Manager **m) {
|
||||
|
||||
assert_se(m);
|
||||
|
||||
r = manager_new(MANAGER_USER, true, &tmp);
|
||||
r = manager_new(UNIT_FILE_USER, true, &tmp);
|
||||
if (MANAGER_SKIP_TEST(r)) {
|
||||
printf("Skipping test: manager_new: %s\n", strerror(-r));
|
||||
return -EXIT_TEST_SKIP;
|
||||
@ -243,7 +244,7 @@ static void test_path_makedirectory_directorymode(Manager *m) {
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
test_function_t tests[] = {
|
||||
static const test_function_t tests[] = {
|
||||
test_path_exists,
|
||||
test_path_existsglob,
|
||||
test_path_changed,
|
||||
@ -253,12 +254,15 @@ int main(int argc, char *argv[]) {
|
||||
test_path_makedirectory_directorymode,
|
||||
NULL,
|
||||
};
|
||||
test_function_t *test = NULL;
|
||||
|
||||
_cleanup_(rm_rf_and_freep) char *runtime_dir = NULL;
|
||||
const test_function_t *test = NULL;
|
||||
Manager *m = NULL;
|
||||
|
||||
log_parse_environment();
|
||||
log_open();
|
||||
|
||||
assert_se(runtime_dir = setup_fake_runtime_dir());
|
||||
assert_se(set_unit_path(TEST_DIR "/test-path/") >= 0);
|
||||
|
||||
for (test = tests; test && *test; test++) {
|
||||
|
@ -1,362 +0,0 @@
|
||||
/***
|
||||
This file is part of systemd. See COPYING for details.
|
||||
|
||||
systemd is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
systemd is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
/*
|
||||
* Tests for RB-Tree
|
||||
*/
|
||||
|
||||
#undef NDEBUG
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include "c-rbtree.h"
|
||||
|
||||
/* verify that all API calls are exported */
|
||||
static void test_api(void) {
|
||||
CRBTree t = {};
|
||||
CRBNode n = C_RBNODE_INIT(n);
|
||||
|
||||
assert(!c_rbnode_is_linked(&n));
|
||||
|
||||
/* init, is_linked, add, remove, remove_init */
|
||||
|
||||
c_rbtree_add(&t, NULL, &t.root, &n);
|
||||
assert(c_rbnode_is_linked(&n));
|
||||
|
||||
c_rbtree_remove_init(&t, &n);
|
||||
assert(!c_rbnode_is_linked(&n));
|
||||
|
||||
c_rbtree_add(&t, NULL, &t.root, &n);
|
||||
assert(c_rbnode_is_linked(&n));
|
||||
|
||||
c_rbtree_remove(&t, &n);
|
||||
assert(c_rbnode_is_linked(&n)); /* @n wasn't touched */
|
||||
|
||||
c_rbnode_init(&n);
|
||||
assert(!c_rbnode_is_linked(&n));
|
||||
|
||||
/* first, last, leftmost, rightmost, next, prev */
|
||||
|
||||
assert(!c_rbtree_first(&t));
|
||||
assert(!c_rbtree_last(&t));
|
||||
assert(&n == c_rbnode_leftmost(&n));
|
||||
assert(&n == c_rbnode_rightmost(&n));
|
||||
assert(!c_rbnode_next(&n));
|
||||
assert(!c_rbnode_prev(&n));
|
||||
}
|
||||
|
||||
/* copied from c-rbtree.c, relies on internal representation */
|
||||
static inline _Bool c_rbnode_is_red(CRBNode *n) {
|
||||
return !((unsigned long)n->__parent_and_color & 1UL);
|
||||
}
|
||||
|
||||
/* copied from c-rbtree.c, relies on internal representation */
|
||||
static inline _Bool c_rbnode_is_black(CRBNode *n) {
|
||||
return !!((unsigned long)n->__parent_and_color & 1UL);
|
||||
}
|
||||
|
||||
static size_t validate(CRBTree *t) {
|
||||
unsigned int i_black, n_black;
|
||||
CRBNode *n, *p, *o;
|
||||
size_t count = 0;
|
||||
|
||||
assert(t);
|
||||
assert(!t->root || c_rbnode_is_black(t->root));
|
||||
|
||||
/* traverse to left-most child, count black nodes */
|
||||
i_black = 0;
|
||||
n = t->root;
|
||||
while (n && n->left) {
|
||||
if (c_rbnode_is_black(n))
|
||||
++i_black;
|
||||
n = n->left;
|
||||
}
|
||||
n_black = i_black;
|
||||
|
||||
/*
|
||||
* Traverse tree and verify correctness:
|
||||
* 1) A node is either red or black
|
||||
* 2) The root is black
|
||||
* 3) All leaves are black
|
||||
* 4) Every red node must have two black child nodes
|
||||
* 5) Every path to a leaf contains the same number of black nodes
|
||||
*
|
||||
* Note that NULL nodes are considered black, which is why we don't
|
||||
* check for 3).
|
||||
*/
|
||||
o = NULL;
|
||||
while (n) {
|
||||
++count;
|
||||
|
||||
/* verify natural order */
|
||||
assert(n > o);
|
||||
o = n;
|
||||
|
||||
/* verify consistency */
|
||||
assert(!n->right || c_rbnode_parent(n->right) == n);
|
||||
assert(!n->left || c_rbnode_parent(n->left) == n);
|
||||
|
||||
/* verify 2) */
|
||||
if (!c_rbnode_parent(n))
|
||||
assert(c_rbnode_is_black(n));
|
||||
|
||||
if (c_rbnode_is_red(n)) {
|
||||
/* verify 4) */
|
||||
assert(!n->left || c_rbnode_is_black(n->left));
|
||||
assert(!n->right || c_rbnode_is_black(n->right));
|
||||
} else {
|
||||
/* verify 1) */
|
||||
assert(c_rbnode_is_black(n));
|
||||
}
|
||||
|
||||
/* verify 5) */
|
||||
if (!n->left && !n->right)
|
||||
assert(i_black == n_black);
|
||||
|
||||
/* get next node */
|
||||
if (n->right) {
|
||||
n = n->right;
|
||||
if (c_rbnode_is_black(n))
|
||||
++i_black;
|
||||
|
||||
while (n->left) {
|
||||
n = n->left;
|
||||
if (c_rbnode_is_black(n))
|
||||
++i_black;
|
||||
}
|
||||
} else {
|
||||
while ((p = c_rbnode_parent(n)) && n == p->right) {
|
||||
n = p;
|
||||
if (c_rbnode_is_black(p->right))
|
||||
--i_black;
|
||||
}
|
||||
|
||||
n = p;
|
||||
if (p && c_rbnode_is_black(p->left))
|
||||
--i_black;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static void insert(CRBTree *t, CRBNode *n) {
|
||||
CRBNode **i, *p;
|
||||
|
||||
assert(t);
|
||||
assert(n);
|
||||
assert(!c_rbnode_is_linked(n));
|
||||
|
||||
i = &t->root;
|
||||
p = NULL;
|
||||
while (*i) {
|
||||
p = *i;
|
||||
if (n < *i) {
|
||||
i = &(*i)->left;
|
||||
} else {
|
||||
assert(n > *i);
|
||||
i = &(*i)->right;
|
||||
}
|
||||
}
|
||||
|
||||
c_rbtree_add(t, p, i, n);
|
||||
}
|
||||
|
||||
static void shuffle(void **nodes, size_t n_memb) {
|
||||
unsigned int i, j;
|
||||
void *t;
|
||||
|
||||
for (i = 0; i < n_memb; ++i) {
|
||||
j = rand() % n_memb;
|
||||
t = nodes[j];
|
||||
nodes[j] = nodes[i];
|
||||
nodes[i] = t;
|
||||
}
|
||||
}
|
||||
|
||||
/* run some pseudo-random tests on the tree */
|
||||
static void test_shuffle(void) {
|
||||
CRBNode *nodes[256];
|
||||
CRBTree t = {};
|
||||
unsigned int i, j;
|
||||
size_t n;
|
||||
|
||||
/* allocate and initialize all nodes */
|
||||
for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) {
|
||||
nodes[i] = malloc(sizeof(*nodes[i]));
|
||||
assert(nodes[i]);
|
||||
c_rbnode_init(nodes[i]);
|
||||
}
|
||||
|
||||
/* shuffle nodes and validate *empty* tree */
|
||||
shuffle((void **)nodes, sizeof(nodes) / sizeof(*nodes));
|
||||
n = validate(&t);
|
||||
assert(n == 0);
|
||||
|
||||
/* add all nodes and validate after each insertion */
|
||||
for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) {
|
||||
insert(&t, nodes[i]);
|
||||
n = validate(&t);
|
||||
assert(n == i + 1);
|
||||
}
|
||||
|
||||
/* shuffle nodes again */
|
||||
shuffle((void **)nodes, sizeof(nodes) / sizeof(*nodes));
|
||||
|
||||
/* remove all nodes (in different order) and validate on each round */
|
||||
for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) {
|
||||
c_rbtree_remove(&t, nodes[i]);
|
||||
n = validate(&t);
|
||||
assert(n == sizeof(nodes) / sizeof(*nodes) - i - 1);
|
||||
c_rbnode_init(nodes[i]);
|
||||
}
|
||||
|
||||
/* shuffle nodes and validate *empty* tree again */
|
||||
shuffle((void **)nodes, sizeof(nodes) / sizeof(*nodes));
|
||||
n = validate(&t);
|
||||
assert(n == 0);
|
||||
|
||||
/* add all nodes again */
|
||||
for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) {
|
||||
insert(&t, nodes[i]);
|
||||
n = validate(&t);
|
||||
assert(n == i + 1);
|
||||
}
|
||||
|
||||
/* 4 times, remove half of the nodes and add them again */
|
||||
for (j = 0; j < 4; ++j) {
|
||||
/* shuffle nodes again */
|
||||
shuffle((void **)nodes, sizeof(nodes) / sizeof(*nodes));
|
||||
|
||||
/* remove half of the nodes */
|
||||
for (i = 0; i < sizeof(nodes) / sizeof(*nodes) / 2; ++i) {
|
||||
c_rbtree_remove(&t, nodes[i]);
|
||||
n = validate(&t);
|
||||
assert(n == sizeof(nodes) / sizeof(*nodes) - i - 1);
|
||||
c_rbnode_init(nodes[i]);
|
||||
}
|
||||
|
||||
/* shuffle the removed half */
|
||||
shuffle((void **)nodes, sizeof(nodes) / sizeof(*nodes) / 2);
|
||||
|
||||
/* add the removed half again */
|
||||
for (i = 0; i < sizeof(nodes) / sizeof(*nodes) / 2; ++i) {
|
||||
insert(&t, nodes[i]);
|
||||
n = validate(&t);
|
||||
assert(n == sizeof(nodes) / sizeof(*nodes) / 2 + i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
/* shuffle nodes again */
|
||||
shuffle((void **)nodes, sizeof(nodes) / sizeof(*nodes));
|
||||
|
||||
/* remove all */
|
||||
for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) {
|
||||
c_rbtree_remove(&t, nodes[i]);
|
||||
n = validate(&t);
|
||||
assert(n == sizeof(nodes) / sizeof(*nodes) - i - 1);
|
||||
c_rbnode_init(nodes[i]);
|
||||
}
|
||||
|
||||
/* free nodes again */
|
||||
for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i)
|
||||
free(nodes[i]);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
unsigned long key;
|
||||
CRBNode rb;
|
||||
} Node;
|
||||
|
||||
#define node_from_rb(_rb) ((Node *)((char *)(_rb) - offsetof(Node, rb)))
|
||||
|
||||
static int compare(CRBTree *t, void *k, CRBNode *n) {
|
||||
unsigned long key = (unsigned long)k;
|
||||
Node *node = node_from_rb(n);
|
||||
|
||||
return (key < node->key) ? -1 : (key > node->key) ? 1 : 0;
|
||||
}
|
||||
|
||||
/* run tests against the c_rbtree_find*() helpers */
|
||||
static void test_map(void) {
|
||||
CRBNode **slot, *p;
|
||||
CRBTree t = {};
|
||||
Node *nodes[2048];
|
||||
unsigned long i;
|
||||
|
||||
/* allocate and initialize all nodes */
|
||||
for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) {
|
||||
nodes[i] = malloc(sizeof(*nodes[i]));
|
||||
assert(nodes[i]);
|
||||
nodes[i]->key = i;
|
||||
c_rbnode_init(&nodes[i]->rb);
|
||||
}
|
||||
|
||||
/* shuffle nodes */
|
||||
shuffle((void **)nodes, sizeof(nodes) / sizeof(*nodes));
|
||||
|
||||
/* add all nodes, and verify that each node is linked */
|
||||
for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) {
|
||||
assert(!c_rbnode_is_linked(&nodes[i]->rb));
|
||||
assert(!c_rbtree_find_entry(&t, compare, (void *)nodes[i]->key, Node, rb));
|
||||
|
||||
slot = c_rbtree_find_slot(&t, compare, (void *)nodes[i]->key, &p);
|
||||
assert(slot);
|
||||
c_rbtree_add(&t, p, slot, &nodes[i]->rb);
|
||||
|
||||
assert(c_rbnode_is_linked(&nodes[i]->rb));
|
||||
assert(nodes[i] == c_rbtree_find_entry(&t, compare, (void *)nodes[i]->key, Node, rb));
|
||||
}
|
||||
|
||||
/* shuffle nodes again */
|
||||
shuffle((void **)nodes, sizeof(nodes) / sizeof(*nodes));
|
||||
|
||||
/* remove all nodes (in different order) */
|
||||
for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) {
|
||||
assert(c_rbnode_is_linked(&nodes[i]->rb));
|
||||
assert(nodes[i] == c_rbtree_find_entry(&t, compare, (void *)nodes[i]->key, Node, rb));
|
||||
|
||||
c_rbtree_remove_init(&t, &nodes[i]->rb);
|
||||
|
||||
assert(!c_rbnode_is_linked(&nodes[i]->rb));
|
||||
assert(!c_rbtree_find_entry(&t, compare, (void *)nodes[i]->key, Node, rb));
|
||||
}
|
||||
|
||||
/* free nodes again */
|
||||
for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i)
|
||||
free(nodes[i]);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
unsigned int i;
|
||||
|
||||
/* we want stable tests, so use fixed seed */
|
||||
srand(0xdeadbeef);
|
||||
|
||||
test_api();
|
||||
|
||||
/*
|
||||
* The tests are pseudo random; run them multiple times, each run will
|
||||
* have different orders and thus different results.
|
||||
*/
|
||||
for (i = 0; i < 4; ++i) {
|
||||
test_shuffle();
|
||||
test_map();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -21,9 +21,12 @@
|
||||
|
||||
#include "macro.h"
|
||||
#include "manager.h"
|
||||
#include "rm-rf.h"
|
||||
#include "test-helper.h"
|
||||
#include "tests.h"
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
_cleanup_(rm_rf_and_freep) char *runtime_dir = NULL;
|
||||
Manager *m = NULL;
|
||||
Unit *idle_ok, *idle_bad, *rr_ok, *rr_bad, *rr_sched;
|
||||
Service *ser;
|
||||
@ -31,9 +34,11 @@ int main(int argc, char *argv[]) {
|
||||
FDSet *fdset = NULL;
|
||||
int r;
|
||||
|
||||
assert_se(runtime_dir = setup_fake_runtime_dir());
|
||||
|
||||
/* prepare the test */
|
||||
assert_se(set_unit_path(TEST_DIR) >= 0);
|
||||
r = manager_new(MANAGER_USER, true, &m);
|
||||
r = manager_new(UNIT_FILE_USER, true, &m);
|
||||
if (MANAGER_SKIP_TEST(r)) {
|
||||
printf("Skipping test: manager_new: %s\n", strerror(-r));
|
||||
return EXIT_TEST_SKIP;
|
||||
|
@ -35,10 +35,12 @@
|
||||
#include "install.h"
|
||||
#include "load-fragment.h"
|
||||
#include "macro.h"
|
||||
#include "rm-rf.h"
|
||||
#include "specifier.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
#include "test-helper.h"
|
||||
#include "tests.h"
|
||||
#include "user-util.h"
|
||||
#include "util.h"
|
||||
|
||||
@ -113,7 +115,7 @@ static void test_config_parse_exec(void) {
|
||||
Manager *m = NULL;
|
||||
Unit *u = NULL;
|
||||
|
||||
r = manager_new(MANAGER_USER, true, &m);
|
||||
r = manager_new(UNIT_FILE_USER, true, &m);
|
||||
if (MANAGER_SKIP_TEST(r)) {
|
||||
printf("Skipping test: manager_new: %s\n", strerror(-r));
|
||||
return;
|
||||
@ -840,11 +842,14 @@ static void test_config_parse_pass_environ(void) {
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
_cleanup_(rm_rf_and_freep) char *runtime_dir = NULL;
|
||||
int r;
|
||||
|
||||
log_parse_environment();
|
||||
log_open();
|
||||
|
||||
assert_se(runtime_dir = setup_fake_runtime_dir());
|
||||
|
||||
r = test_unit_file_get_set();
|
||||
test_config_parse_exec();
|
||||
test_config_parse_capability_set();
|
||||
|
@ -209,7 +209,7 @@ static int test_unit_printf(void) {
|
||||
assert_se(get_home_dir(&home) >= 0);
|
||||
assert_se(get_shell(&shell) >= 0);
|
||||
|
||||
r = manager_new(MANAGER_USER, true, &m);
|
||||
r = manager_new(UNIT_FILE_USER, true, &m);
|
||||
if (r == -EPERM || r == -EACCES || r == -EADDRINUSE) {
|
||||
puts("manager_new: Permission denied. Skipping test.");
|
||||
return EXIT_TEST_SKIP;
|
||||
|
Loading…
x
Reference in New Issue
Block a user