diff options
99 files changed, 2402 insertions, 2743 deletions
diff --git a/.gitignore b/.gitignore index 403c16c339..02ba86ef6f 100644 --- a/.gitignore +++ b/.gitignore @@ -249,7 +249,6 @@ /test-pty /test-qcow2 /test-ratelimit -/test-rbtree /test-replace-var /test-resolve /test-resolve-tables diff --git a/CODING_STYLE b/CODING_STYLE index e5ba396368..c2b2e56d5d 100644 --- a/CODING_STYLE +++ b/CODING_STYLE @@ -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). diff --git a/Makefile.am b/Makefile.am index 5c25178aec..34eaefd0bf 100644 --- a/Makefile.am +++ b/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) \ @@ -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. @@ -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) @@ -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 + +* 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. -* machinectl remove --hidden + machinectl remove --all +* 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. diff --git a/man/machinectl.xml b/man/machinectl.xml index cee4bb72ce..a77d2419af 100644 --- a/man/machinectl.xml +++ b/man/machinectl.xml @@ -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> diff --git a/man/systemctl.xml b/man/systemctl.xml index 089fb0f5c3..5f624243f7 100644 --- a/man/systemctl.xml +++ b/man/systemctl.xml @@ -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> @@ -1235,6 +1245,28 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service </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> <term><command>add-requires <replaceable>TARGET</replaceable> diff --git a/man/systemd-nspawn.xml b/man/systemd-nspawn.xml index 3f3bd38900..8a004af1a2 100644 --- a/man/systemd-nspawn.xml +++ b/man/systemd-nspawn.xml @@ -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> diff --git a/man/systemd.automount.xml b/man/systemd.automount.xml index 1b0ae832da..93bae2a6dc 100644 --- a/man/systemd.automount.xml +++ b/man/systemd.automount.xml @@ -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> diff --git a/man/systemd.mount.xml b/man/systemd.mount.xml index 4a8d265fed..f116782b40 100644 --- a/man/systemd.mount.xml +++ b/man/systemd.mount.xml @@ -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 diff --git a/man/systemd.path.xml b/man/systemd.path.xml index 1bd65ce86d..7200c8fe27 100644 --- a/man/systemd.path.xml +++ b/man/systemd.path.xml @@ -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> diff --git a/man/systemd.service.xml b/man/systemd.service.xml index e55534700a..6641dfed4f 100644 --- a/man/systemd.service.xml +++ b/man/systemd.service.xml @@ -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 diff --git a/man/systemd.slice.xml b/man/systemd.slice.xml index 5c87bf0260..16247628af 100644 --- a/man/systemd.slice.xml +++ b/man/systemd.slice.xml @@ -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> diff --git a/man/systemd.socket.xml b/man/systemd.socket.xml index 43841c2399..2d6339680b 100644 --- a/man/systemd.socket.xml +++ b/man/systemd.socket.xml @@ -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 diff --git a/man/systemd.swap.xml b/man/systemd.swap.xml index 69d4be4769..6fc4c7bf13 100644 --- a/man/systemd.swap.xml +++ b/man/systemd.swap.xml @@ -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 diff --git a/man/systemd.target.xml b/man/systemd.target.xml index bd4ab3903e..ab910d75dd 100644 --- a/man/systemd.target.xml +++ b/man/systemd.target.xml @@ -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> diff --git a/man/systemd.timer.xml b/man/systemd.timer.xml index 29e235e2dc..22c83f790f 100644 --- a/man/systemd.timer.xml +++ b/man/systemd.timer.xml @@ -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> diff --git a/man/systemd.unit.xml b/man/systemd.unit.xml index f5022b03cc..7993301167 100644 --- a/man/systemd.unit.xml +++ b/man/systemd.unit.xml @@ -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> diff --git a/src/analyze/analyze-verify.c b/src/analyze/analyze-verify.c index b83f559e7d..5fd3ee49eb 100644 --- a/src/analyze/analyze-verify.c +++ b/src/analyze/analyze-verify.c @@ -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"); diff --git a/src/analyze/analyze-verify.h b/src/analyze/analyze-verify.h index 27c253a562..d8204dc69c 100644 --- a/src/analyze/analyze-verify.h +++ b/src/analyze/analyze-verify.h @@ -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); diff --git a/src/analyze/analyze.c b/src/analyze/analyze.c index 42754a2741..5e03c0c5e0 100644 --- a/src/analyze/analyze.c +++ b/src/analyze/analyze.c @@ -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; diff --git a/src/basic/c-rbtree.c b/src/basic/c-rbtree.c deleted file mode 100644 index cf5a7242df..0000000000 --- a/src/basic/c-rbtree.c +++ /dev/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); -} diff --git a/src/basic/c-rbtree.h b/src/basic/c-rbtree.h deleted file mode 100644 index 20c5515ca1..0000000000 --- a/src/basic/c-rbtree.h +++ /dev/null @@ -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 diff --git a/src/basic/def.h b/src/basic/def.h index 963343eb7d..1a7a0f4928 100644 --- a/src/basic/def.h +++ b/src/basic/def.h @@ -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" \ diff --git a/src/basic/rm-rf.h b/src/basic/rm-rf.h index 6d03268919..40b5b527d5 100644 --- a/src/basic/rm-rf.h +++ b/src/basic/rm-rf.h @@ -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); diff --git a/src/basic/signal-util.c b/src/basic/signal-util.c index e3047b209b..280b5c3251 100644 --- a/src/basic/signal-util.c +++ b/src/basic/signal-util.c @@ -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; diff --git a/src/basic/signal-util.h b/src/basic/signal-util.h index a7322ff26a..dfd6eb564d 100644 --- a/src/basic/signal-util.h +++ b/src/basic/signal-util.h @@ -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; +} diff --git a/src/basic/strv.c b/src/basic/strv.c index 8282298dca..97a96e5762 100644 --- a/src/basic/strv.c +++ b/src/basic/strv.c @@ -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; diff --git a/src/basic/strv.h b/src/basic/strv.h index 7bfa54408d..f61bbb5386 100644 --- a/src/basic/strv.h +++ b/src/basic/strv.h @@ -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); diff --git a/src/basic/util.c b/src/basic/util.c index f1e3bd5b48..957b0e1ff1 100644 --- a/src/basic/util.c +++ b/src/basic/util.c @@ -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; } diff --git a/src/basic/util.h b/src/basic/util.h index 286db05159..1c032c15c9 100644 --- a/src/basic/util.h +++ b/src/basic/util.h @@ -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); diff --git a/src/core/automount.c b/src/core/automount.c index 5dc6fd98e7..7c55d7bc49 100644 --- a/src/core/automount.c +++ b/src/core/automount.c @@ -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); diff --git a/src/core/busname.c b/src/core/busname.c index de2a21ccde..bbe61af4f0 100644 --- a/src/core/busname.c +++ b/src/core/busname.c @@ -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"); diff --git a/src/core/cgroup.c b/src/core/cgroup.c index 9c34928052..25cc6962f9 100644 --- a/src/core/cgroup.c +++ b/src/core/cgroup.c @@ -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 diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c index 973a60187d..9dfca14914 100644 --- a/src/core/dbus-execute.c +++ b/src/core/dbus-execute.c @@ -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); diff --git a/src/core/dbus-kill.c b/src/core/dbus-kill.c index fc50fafaad..0f54c6b84b 100644 --- a/src/core/dbus-kill.c +++ b/src/core/dbus-kill.c @@ -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) { diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c index 00372b92b4..2392ed6c16 100644 --- a/src/core/dbus-manager.c +++ b/src/core/dbus-manager.c @@ -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), diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c index b351f6a2c2..c507265070 100644 --- a/src/core/dbus-unit.c +++ b/src/core/dbus-unit.c @@ -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; diff --git a/src/core/dbus.c b/src/core/dbus.c index 413489373f..263955d874 100644 --- a/src/core/dbus.c +++ b/src/core/dbus.c @@ -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 */ diff --git a/src/core/device.c b/src/core/device.c index 0671620a3e..d01bec53d8 100644 --- a/src/core/device.c +++ b/src/core/device.c @@ -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; diff --git a/src/core/failure-action.c b/src/core/failure-action.c index bb2bc3f399..ddae46190f 100644 --- a/src/core/failure-action.c +++ b/src/core/failure-action.c @@ -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: diff --git a/src/core/job.c b/src/core/job.c index 5557a6a942..d9c5669c9f 100644 --- a/src/core/job.c +++ b/src/core/job.c @@ -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)) diff --git a/src/core/load-dropin.c b/src/core/load-dropin.c index 22b71b6f5e..f83fa09301 100644 --- a/src/core/load-dropin.c +++ b/src/core/load-dropin.c @@ -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, diff --git a/src/core/load-dropin.h b/src/core/load-dropin.h index d8a4aefbb3..942d26724e 100644 --- a/src/core/load-dropin.h +++ b/src/core/load-dropin.h @@ -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); diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index a54f0d764c..c4566f7709 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -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 diff --git a/src/core/machine-id-setup.c b/src/core/machine-id-setup.c index 7b25349c07..86da16c31e 100644 --- a/src/core/machine-id-setup.c +++ b/src/core/machine-id-setup.c @@ -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); diff --git a/src/core/main.c b/src/core/main.c index 56df32426a..2912608435 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -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; diff --git a/src/core/manager.c b/src/core/manager.c index e739795e70..5601770670 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -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); - dual_timestamp_get(&m->generators_start_timestamp); - r = manager_run_generators(m); - dual_timestamp_get(&m->generators_finish_timestamp); + 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; - 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); + dual_timestamp_get(&m->generators_start_timestamp); + r = manager_run_generators(m); + dual_timestamp_get(&m->generators_finish_timestamp); 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); - /* Find new unit paths */ - q = manager_run_generators(m); + q = lookup_paths_init(&m->lookup_paths, m->unit_file_scope, 0, NULL); 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); + /* Find new unit paths */ + q = manager_run_generators(m); 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"); } diff --git a/src/core/manager.h b/src/core/manager.h index 9803f73129..17f84e6963 100644 --- a/src/core/manager.h +++ b/src/core/manager.h @@ -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); diff --git a/src/core/mount.c b/src/core/mount.c index 0fd880df5d..74ab54bfd0 100644 --- a/src/core/mount.c +++ b/src/core/mount.c @@ -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 diff --git a/src/core/org.freedesktop.systemd1.conf b/src/core/org.freedesktop.systemd1.conf index 6a7a37ee92..f78eedbd6e 100644 --- a/src/core/org.freedesktop.systemd1.conf +++ b/src/core/org.freedesktop.systemd1.conf @@ -176,6 +176,10 @@ <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"/> <allow send_destination="org.freedesktop.systemd1" diff --git a/src/core/path.c b/src/core/path.c index 426c4ad299..5e7b3eb234 100644 --- a/src/core/path.c +++ b/src/core/path.c @@ -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; diff --git a/src/core/scope.c b/src/core/scope.c index 361695c3f9..7078d1f7e9 100644 --- a/src/core/scope.c +++ b/src/core/scope.c @@ -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); diff --git a/src/core/service.c b/src/core/service.c index c5cbf0f152..58084e2f82 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -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; diff --git a/src/core/shutdown.c b/src/core/shutdown.c index 96679c920f..e14755d84e 100644 --- a/src/core/shutdown.c +++ b/src/core/shutdown.c @@ -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"); } } diff --git a/src/core/slice.c b/src/core/slice.c index 667f61bde5..63a77c9bca 100644 --- a/src/core/slice.c +++ b/src/core/slice.c @@ -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) diff --git a/src/core/socket.c b/src/core/socket.c index dd515a17a5..65da0e3c5e 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -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; diff --git a/src/core/swap.c b/src/core/swap.c index 11506d9ecb..c6502eb821 100644 --- a/src/core/swap.c +++ b/src/core/swap.c @@ -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) diff --git a/src/core/timer.c b/src/core/timer.c index 3d0bae16e5..a51a67ea11 100644 --- a/src/core/timer.c +++ b/src/core/timer.c @@ -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) diff --git a/src/core/transaction.c b/src/core/transaction.c index c894001cf9..dad387749c 100644 --- a/src/core/transaction.c +++ b/src/core/transaction.c @@ -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", */ diff --git a/src/core/unit-printf.c b/src/core/unit-printf.c index fc057d965c..f11df42af3 100644 --- a/src/core/unit-printf.c +++ b/src/core/unit-printf.c @@ -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; diff --git a/src/core/unit.c b/src/core/unit.c index 70175557f7..fb88260d23 100644 --- a/src/core/unit.c +++ b/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; - (void) unlink(*i); + 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; - p = dirname_malloc(*i); - if (p) - (void) rmdir(p); + /* Only drop transient drop-ins */ + if (!path_equal(u->manager->lookup_paths.transient, pp)) + continue; + + (void) unlink(*i); + (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); + 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 = drop_in_file(dir, u->id, 50, name, &p, &q); + (void) mkdir_p(p, 0755); + r = write_string_file_atomic_label(q, prefixed); if (r < 0) return r; - r = strv_extend(&u->dropin_paths, q); + r = strv_push(&u->dropin_paths, q); if (r < 0) return r; + q = NULL; - strv_sort(u->dropin_paths); 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; } diff --git a/src/core/unit.h b/src/core/unit.h index 601e763ce2..cfdac852a5 100644 --- a/src/core/unit.h +++ b/src/core/unit.h @@ -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; diff --git a/src/import/pull-job.h b/src/import/pull-job.h index 998857035a..3a152a50e3 100644 --- a/src/import/pull-job.h +++ b/src/import/pull-job.h @@ -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; diff --git a/src/libsystemd/sd-bus/bus-common-errors.c b/src/libsystemd/sd-bus/bus-common-errors.c index 6370061daf..02e3bf904c 100644 --- a/src/libsystemd/sd-bus/bus-common-errors.c +++ b/src/libsystemd/sd-bus/bus-common-errors.c @@ -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), diff --git a/src/libsystemd/sd-bus/bus-common-errors.h b/src/libsystemd/sd-bus/bus-common-errors.h index 464834979a..c8f369cb78 100644 --- a/src/libsystemd/sd-bus/bus-common-errors.h +++ b/src/libsystemd/sd-bus/bus-common-errors.h @@ -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" diff --git a/src/libsystemd/sd-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c index 841358ed03..9fd744768d 100644 --- a/src/libsystemd/sd-event/sd-event.c +++ b/src/libsystemd/sd-event/sd-event.c @@ -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; diff --git a/src/locale/localed.c b/src/locale/localed.c index 46405ca68a..3b22a582ac 100644 --- a/src/locale/localed.c +++ b/src/locale/localed.c @@ -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 */ + + 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; - ctx = xkb_context_new(XKB_CONTEXT_NO_ENVIRONMENT_NAMES); + 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 || diff --git a/src/login/logind-session-dbus.c b/src/login/logind-session-dbus.c index ff9170683b..0f8862c0d9 100644 --- a/src/login/logind-session-dbus.c +++ b/src/login/logind-session-dbus.c @@ -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( diff --git a/src/login/logind-user-dbus.c b/src/login/logind-user-dbus.c index fd98c7beca..b73f9ea69e 100644 --- a/src/login/logind-user-dbus.c +++ b/src/login/logind-user-dbus.c @@ -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); diff --git a/src/machine/machine-dbus.c b/src/machine/machine-dbus.c index c5bbf2fbde..c7ff0efac8 100644 --- a/src/machine/machine-dbus.c +++ b/src/machine/machine-dbus.c @@ -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( diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c index 1d3264a1de..35177aa29e 100644 --- a/src/machine/machinectl.c +++ b/src/machine/machinectl.c @@ -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 }, {} }; diff --git a/src/machine/machined-dbus.c b/src/machine/machined-dbus.c index 20894433e7..c9639c3cf2 100644 --- a/src/machine/machined-dbus.c +++ b/src/machine/machined-dbus.c @@ -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), diff --git a/src/network/networkd-lldp-tx.c b/src/network/networkd-lldp-tx.c index c940e63052..6bde04bc32 100644 --- a/src/network/networkd-lldp-tx.c +++ b/src/network/networkd-lldp-tx.c @@ -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; diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c index e065a5a5a9..ab9b777d9a 100644 --- a/src/network/networkd-route.c +++ b/src/network/networkd-route.c @@ -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; diff --git a/src/resolve/test-dnssec.c b/src/resolve/test-dnssec.c index 155be9946f..b3018e8239 100644 --- a/src/resolve/test-dnssec.c +++ b/src/resolve/test-dnssec.c @@ -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); diff --git a/src/shared/firewall-util.c b/src/shared/firewall-util.c index 0d3da2e6d2..ade2de7727 100644 --- a/src/shared/firewall-util.c +++ b/src/shared/firewall-util.c @@ -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> diff --git a/src/shared/install.c b/src/shared/install.c index 0f08137f19..8d9f96553b 100644 --- a/src/shared/install.c +++ b/src/shared/install.c @@ -45,6 +45,7 @@ #include "mkdir.h" #include "path-lookup.h" #include "path-util.h" +#include "rm-rf.h" #include "set.h" #include "special.h" #include "stat-util.h" @@ -65,7 +66,9 @@ typedef struct { OrderedHashmap *have_processed; } InstallContext; -static int in_search_path(const char *path, char **search) { +static int unit_file_lookup_state(UnitFileScope scope, const LookupPaths *paths, const char *name, UnitFileState *ret); + +static int in_search_path(const LookupPaths *p, const char *path) { _cleanup_free_ char *parent = NULL; char **i; @@ -75,141 +78,141 @@ static int in_search_path(const char *path, char **search) { if (!parent) return -ENOMEM; - STRV_FOREACH(i, search) + STRV_FOREACH(i, p->search_path) if (path_equal(parent, *i)) return true; return false; } -static int get_config_path(UnitFileScope scope, bool runtime, const char *root_dir, char **ret) { - char *p = NULL; - int r; - - assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); - assert(ret); +static const char* skip_root(const LookupPaths *p, const char *path) { + char *e; - /* This determines where we shall create or remove our - * installation ("configuration") symlinks */ + assert(p); + assert(path); - switch (scope) { + if (!p->root_dir) + return path; - case UNIT_FILE_SYSTEM: + e = path_startswith(path, p->root_dir); + if (!e) + return NULL; - if (runtime) - p = path_join(root_dir, "/run/systemd/system", NULL); - else - p = path_join(root_dir, SYSTEM_CONFIG_UNIT_PATH, NULL); - break; + /* Make sure the returned path starts with a slash */ + if (e[0] != '/') { + if (e == path || e[-1] != '/') + return NULL; - case UNIT_FILE_GLOBAL: + e--; + } - if (root_dir) - return -EINVAL; + return e; +} - if (runtime) - p = strdup("/run/systemd/user"); - else - p = strdup(USER_CONFIG_UNIT_PATH); - break; +static int path_is_generator(const LookupPaths *p, const char *path) { + _cleanup_free_ char *parent = NULL; - case UNIT_FILE_USER: + assert(p); + assert(path); - if (root_dir) - return -EINVAL; + parent = dirname_malloc(path); + if (!parent) + return -ENOMEM; - if (runtime) - r = user_runtime_dir(&p); - else - r = user_config_home(&p); - if (r < 0) - return r; - if (r == 0) - return -ENOENT; + return path_equal(p->generator, parent) || + path_equal(p->generator_early, parent) || + path_equal(p->generator_late, parent); +} - break; +static int path_is_transient(const LookupPaths *p, const char *path) { + _cleanup_free_ char *parent = NULL; - default: - assert_not_reached("Bad scope"); - } + assert(p); + assert(path); - if (!p) + parent = dirname_malloc(path); + if (!parent) return -ENOMEM; - *ret = p; - return 0; + return path_equal(p->transient, parent); } -static bool is_config_path(UnitFileScope scope, const char *path) { - int r; +static int path_is_control(const LookupPaths *p, const char *path) { + _cleanup_free_ char *parent = NULL; - assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); + assert(p); assert(path); - /* Checks whether the specified path is intended for - * configuration or is outside of it */ + parent = dirname_malloc(path); + if (!parent) + return -ENOMEM; - switch (scope) { + return path_equal(parent, p->persistent_control) || + path_equal(parent, p->runtime_control); +} - case UNIT_FILE_SYSTEM: - case UNIT_FILE_GLOBAL: - return path_startswith(path, "/etc") || - path_startswith(path, SYSTEM_CONFIG_UNIT_PATH) || - path_startswith(path, "/run"); +static int path_is_config(const LookupPaths *p, const char *path) { + _cleanup_free_ char *parent = NULL; + assert(p); + assert(path); - case UNIT_FILE_USER: { - _cleanup_free_ char *p = NULL; + /* Note that we do *not* have generic checks for /etc or /run in place, since with them we couldn't discern + * configuration from transient or generated units */ - r = user_config_home(&p); - if (r < 0) - return r; - if (r > 0 && path_startswith(path, p)) - return true; + parent = dirname_malloc(path); + if (!parent) + return -ENOMEM; - p = mfree(p); + return path_equal(parent, p->persistent_config) || + path_equal(parent, p->runtime_config); +} - r = user_runtime_dir(&p); - if (r < 0) - return r; - if (r > 0 && path_startswith(path, p)) - return true; +static int path_is_runtime(const LookupPaths *p, const char *path) { + _cleanup_free_ char *parent = NULL; + const char *rpath; - return false; - } + assert(p); + assert(path); - default: - assert_not_reached("Bad scope"); - } -} + /* Everything in /run is considered runtime. On top of that we also add explicit checks for the various runtime + * directories, as safety net. */ + rpath = skip_root(p, path); + if (rpath && path_startswith(rpath, "/run")) + return true; -static int verify_root_dir(UnitFileScope scope, const char **root_dir) { - int r; + parent = dirname_malloc(path); + if (!parent) + return -ENOMEM; + + return path_equal(parent, p->runtime_config) || + path_equal(parent, p->generator) || + path_equal(parent, p->generator_early) || + path_equal(parent, p->generator_late) || + path_equal(parent, p->transient) || + path_equal(parent, p->runtime_control); +} - assert(root_dir); +static int path_is_vendor(const LookupPaths *p, const char *path) { + const char *rpath; - /* Verifies that the specified root directory to operate on - * makes sense. Reset it to NULL if it is the root directory - * or set to empty */ + assert(p); + assert(path); - if (isempty(*root_dir) || path_equal(*root_dir, "/")) { - *root_dir = NULL; + rpath = skip_root(p, path); + if (!rpath) return 0; - } - if (scope != UNIT_FILE_SYSTEM) - return -EINVAL; + if (path_startswith(rpath, "/usr")) + return true; - r = is_dir(*root_dir, true); - if (r < 0) - return r; - if (r == 0) - return -ENOTDIR; +#ifdef HAVE_SPLIT_USR + if (path_startswith(rpath, "/lib")) + return true; +#endif - return 0; + return path_equal(rpath, SYSTEM_DATA_UNIT_PATH); } int unit_file_changes_add( @@ -353,6 +356,7 @@ static int remove_marked_symlinks_fd( int fd, const char *path, const char *config_path, + const LookupPaths *lp, bool *restart, UnitFileChange **changes, unsigned *n_changes) { @@ -365,6 +369,7 @@ static int remove_marked_symlinks_fd( assert(fd >= 0); assert(path); assert(config_path); + assert(lp); assert(restart); d = fdopendir(fd); @@ -400,12 +405,13 @@ static int remove_marked_symlinks_fd( } /* This will close nfd, regardless whether it succeeds or not */ - q = remove_marked_symlinks_fd(remove_symlinks_to, nfd, p, config_path, restart, changes, n_changes); + q = remove_marked_symlinks_fd(remove_symlinks_to, nfd, p, config_path, lp, restart, changes, n_changes); if (q < 0 && r == 0) r = q; } else if (de->d_type == DT_LNK) { _cleanup_free_ char *p = NULL, *dest = NULL; + const char *rp; bool found; int q; @@ -417,19 +423,16 @@ static int remove_marked_symlinks_fd( return -ENOMEM; q = readlink_malloc(p, &dest); + if (q == -ENOENT) + continue; if (q < 0) { - if (q == -ENOENT) - continue; - if (r == 0) r = q; continue; } - /* We remove all links pointing to a file or - * path that is marked, as well as all files - * sharing the same name as a file that is - * marked. */ + /* We remove all links pointing to a file or path that is marked, as well as all files sharing + * the same name as a file that is marked. */ found = set_contains(remove_symlinks_to, dest) || @@ -439,7 +442,7 @@ static int remove_marked_symlinks_fd( if (!found) continue; - if (unlink(p) < 0 && errno != ENOENT) { + if (unlinkat(fd, de->d_name, 0) < 0 && errno != ENOENT) { if (r == 0) r = -errno; continue; @@ -450,7 +453,11 @@ static int remove_marked_symlinks_fd( unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, p, NULL); - q = mark_symlink_for_removal(&remove_symlinks_to, p); + /* Now, remember the full path (but with the root prefix removed) of the symlink we just + * removed, and remove any symlinks to it, too */ + + rp = skip_root(lp, p); + q = mark_symlink_for_removal(&remove_symlinks_to, rp ?: p); if (q < 0) return q; if (q > 0) @@ -464,6 +471,7 @@ static int remove_marked_symlinks_fd( static int remove_marked_symlinks( Set *remove_symlinks_to, const char *config_path, + const LookupPaths *lp, UnitFileChange **changes, unsigned *n_changes) { @@ -472,6 +480,7 @@ static int remove_marked_symlinks( int r = 0; assert(config_path); + assert(lp); if (set_size(remove_symlinks_to) <= 0) return 0; @@ -489,7 +498,7 @@ static int remove_marked_symlinks( return -errno; /* This takes possession of cfd and closes it */ - q = remove_marked_symlinks_fd(remove_symlinks_to, cfd, config_path, config_path, &restart, changes, n_changes); + q = remove_marked_symlinks_fd(remove_symlinks_to, cfd, config_path, config_path, lp, &restart, changes, n_changes); if (r == 0) r = q; } while (restart); @@ -503,6 +512,7 @@ static int find_symlinks_fd( int fd, const char *path, const char *config_path, + const LookupPaths *lp, bool *same_name_link) { _cleanup_closedir_ DIR *d = NULL; @@ -513,6 +523,7 @@ static int find_symlinks_fd( assert(fd >= 0); assert(path); assert(config_path); + assert(lp); assert(same_name_link); d = fdopendir(fd); @@ -546,7 +557,7 @@ static int find_symlinks_fd( } /* This will close nfd, regardless whether it succeeds or not */ - q = find_symlinks_fd(root_dir, name, nfd, p, config_path, same_name_link); + q = find_symlinks_fd(root_dir, name, nfd, p, config_path, lp, same_name_link); if (q > 0) return 1; if (r == 0) @@ -624,6 +635,7 @@ static int find_symlinks( const char *root_dir, const char *name, const char *config_path, + const LookupPaths *lp, bool *same_name_link) { int fd; @@ -640,29 +652,25 @@ static int find_symlinks( } /* This takes possession of fd and closes it */ - return find_symlinks_fd(root_dir, name, fd, config_path, config_path, same_name_link); + return find_symlinks_fd(root_dir, name, fd, config_path, config_path, lp, same_name_link); } static int find_symlinks_in_scope( UnitFileScope scope, - const char *root_dir, + const LookupPaths *paths, const char *name, UnitFileState *state) { - _cleanup_free_ char *normal_path = NULL, *runtime_path = NULL; bool same_name_link_runtime = false, same_name_link = false; int r; assert(scope >= 0); assert(scope < _UNIT_FILE_SCOPE_MAX); + assert(paths); assert(name); - /* First look in the normal config path */ - r = get_config_path(scope, false, root_dir, &normal_path); - if (r < 0) - return r; - - r = find_symlinks(root_dir, name, normal_path, &same_name_link); + /* First look in the persistent config path */ + r = find_symlinks(paths->root_dir, name, paths->persistent_config, paths, &same_name_link); if (r < 0) return r; if (r > 0) { @@ -671,11 +679,7 @@ static int find_symlinks_in_scope( } /* Then look in runtime config path */ - r = get_config_path(scope, true, root_dir, &runtime_path); - if (r < 0) - return r; - - r = find_symlinks(root_dir, name, runtime_path, &same_name_link_runtime); + r = find_symlinks(paths->root_dir, name, paths->runtime_config, paths, &same_name_link_runtime); if (r < 0) return r; if (r > 0) { @@ -742,6 +746,23 @@ static UnitFileInstallInfo *install_info_find(InstallContext *c, const char *nam return ordered_hashmap_get(c->will_process, name); } +static int install_info_may_process(UnitFileInstallInfo *i, const LookupPaths *paths) { + assert(i); + assert(paths); + + /* Checks whether the loaded unit file is one we should process, or is masked, transient or generated and thus + * not subject to enable/disable operations. */ + + if (i->type == UNIT_FILE_TYPE_MASKED) + return -ESHUTDOWN; + if (path_is_generator(paths, i->path)) + return -EADDRNOTAVAIL; + if (path_is_transient(paths, i->path)) + return -EADDRNOTAVAIL; + + return 0; +} + static int install_info_add( InstallContext *c, const char *name, @@ -804,20 +825,6 @@ fail: return r; } -static int install_info_add_auto( - InstallContext *c, - const char *name_or_path, - UnitFileInstallInfo **ret) { - - assert(c); - assert(name_or_path); - - if (path_is_absolute(name_or_path)) - return install_info_add(c, NULL, name_or_path, ret); - else - return install_info_add(c, name_or_path, NULL, ret); -} - static int config_parse_also( const char *unit, const char *filename, @@ -900,7 +907,6 @@ static int unit_file_load( InstallContext *c, UnitFileInstallInfo *info, const char *path, - const char *root_dir, SearchFlags flags) { const ConfigTableItem items[] = { @@ -921,8 +927,6 @@ static int unit_file_load( assert(info); assert(path); - path = prefix_roota(root_dir, path); - if (!(flags & SEARCH_LOAD)) { r = lstat(path, &st); if (r < 0) @@ -983,26 +987,26 @@ static int unit_file_load_or_readlink( const char *root_dir, SearchFlags flags) { - _cleanup_free_ char *np = NULL; + _cleanup_free_ char *target = NULL; int r; - r = unit_file_load(c, info, path, root_dir, flags); + r = unit_file_load(c, info, path, flags); if (r != -ELOOP) return r; /* This is a symlink, let's read it. */ - r = readlink_and_make_absolute_root(root_dir, path, &np); + r = readlink_malloc(path, &target); if (r < 0) return r; - if (path_equal(np, "/dev/null")) + if (path_equal(target, "/dev/null")) info->type = UNIT_FILE_TYPE_MASKED; else { const char *bn; UnitType a, b; - bn = basename(np); + bn = basename(target); if (unit_name_is_valid(info->name, UNIT_NAME_PLAIN)) { @@ -1029,9 +1033,16 @@ static int unit_file_load_or_readlink( if (a < 0 || b < 0 || a != b) return -EINVAL; + if (path_is_absolute(target)) + /* This is an absolute path, prefix the root so that we always deal with fully qualified paths */ + info->symlink_target = prefix_root(root_dir, target); + else + /* This is a relative path, take it relative to the dir the symlink is located in. */ + info->symlink_target = file_in_same_dir(path, target); + if (!info->symlink_target) + return -ENOMEM; + info->type = UNIT_FILE_TYPE_SYMLINK; - info->symlink_target = np; - np = NULL; } return 0; @@ -1041,7 +1052,6 @@ static int unit_file_search( InstallContext *c, UnitFileInstallInfo *info, const LookupPaths *paths, - const char *root_dir, SearchFlags flags) { char **p; @@ -1056,18 +1066,18 @@ static int unit_file_search( return 0; if (info->path) - return unit_file_load_or_readlink(c, info, info->path, root_dir, flags); + return unit_file_load_or_readlink(c, info, info->path, paths->root_dir, flags); assert(info->name); - STRV_FOREACH(p, paths->unit_path) { + STRV_FOREACH(p, paths->search_path) { _cleanup_free_ char *path = NULL; path = strjoin(*p, "/", info->name, NULL); if (!path) return -ENOMEM; - r = unit_file_load_or_readlink(c, info, path, root_dir, flags); + r = unit_file_load_or_readlink(c, info, path, paths->root_dir, flags); if (r < 0) { if (r != -ENOENT) return r; @@ -1090,14 +1100,14 @@ static int unit_file_search( if (r < 0) return r; - STRV_FOREACH(p, paths->unit_path) { + STRV_FOREACH(p, paths->search_path) { _cleanup_free_ char *path = NULL; path = strjoin(*p, "/", template, NULL); if (!path) return -ENOMEM; - r = unit_file_load_or_readlink(c, info, path, root_dir, flags); + r = unit_file_load_or_readlink(c, info, path, paths->root_dir, flags); if (r < 0) { if (r != -ENOENT) return r; @@ -1143,7 +1153,6 @@ static int install_info_follow( static int install_info_traverse( UnitFileScope scope, InstallContext *c, - const char *root_dir, const LookupPaths *paths, UnitFileInstallInfo *start, SearchFlags flags, @@ -1157,7 +1166,7 @@ static int install_info_traverse( assert(start); assert(c); - r = unit_file_search(c, start, paths, root_dir, flags); + r = unit_file_search(c, start, paths, flags); if (r < 0) return r; @@ -1168,10 +1177,15 @@ static int install_info_traverse( if (++k > UNIT_FILE_FOLLOW_SYMLINK_MAX) return -ELOOP; - if (!(flags & SEARCH_FOLLOW_CONFIG_SYMLINKS) && is_config_path(scope, i->path)) - return -ELOOP; + if (!(flags & SEARCH_FOLLOW_CONFIG_SYMLINKS)) { + r = path_is_config(paths, i->path); + if (r < 0) + return r; + if (r > 0) + return -ELOOP; + } - r = install_info_follow(c, i, root_dir, flags); + r = install_info_follow(c, i, paths->root_dir, flags); if (r < 0) { _cleanup_free_ char *buffer = NULL; const char *bn; @@ -1205,7 +1219,7 @@ static int install_info_traverse( if (r < 0) return r; - r = unit_file_search(c, i, paths, root_dir, flags); + r = unit_file_search(c, i, paths, flags); if (r < 0) return r; } @@ -1219,10 +1233,28 @@ static int install_info_traverse( return 0; } +static int install_info_add_auto( + InstallContext *c, + const LookupPaths *paths, + const char *name_or_path, + UnitFileInstallInfo **ret) { + + assert(c); + assert(name_or_path); + + if (path_is_absolute(name_or_path)) { + const char *pp; + + pp = prefix_roota(paths->root_dir, name_or_path); + + return install_info_add(c, NULL, pp, ret); + } else + return install_info_add(c, name_or_path, NULL, ret); +} + static int install_info_discover( UnitFileScope scope, InstallContext *c, - const char *root_dir, const LookupPaths *paths, const char *name, SearchFlags flags, @@ -1235,15 +1267,16 @@ static int install_info_discover( assert(paths); assert(name); - r = install_info_add_auto(c, name, &i); + r = install_info_add_auto(c, paths, name, &i); if (r < 0) return r; - return install_info_traverse(scope, c, root_dir, paths, i, flags, ret); + return install_info_traverse(scope, c, paths, i, flags, ret); } static int install_info_symlink_alias( UnitFileInstallInfo *i, + const LookupPaths *paths, const char *config_path, bool force, UnitFileChange **changes, @@ -1253,10 +1286,12 @@ static int install_info_symlink_alias( int r = 0, q; assert(i); + assert(paths); assert(config_path); STRV_FOREACH(s, i->aliases) { _cleanup_free_ char *alias_path = NULL, *dst = NULL; + const char *rp; q = install_full_printf(i, *s, &dst); if (q < 0) @@ -1266,7 +1301,9 @@ static int install_info_symlink_alias( if (!alias_path) return -ENOMEM; - q = create_symlink(i->path, alias_path, force, changes, n_changes); + rp = skip_root(paths, i->path); + + q = create_symlink(rp ?: i->path, alias_path, force, changes, n_changes); if (r == 0) r = q; } @@ -1276,6 +1313,7 @@ static int install_info_symlink_alias( static int install_info_symlink_wants( UnitFileInstallInfo *i, + const LookupPaths *paths, const char *config_path, char **list, const char *suffix, @@ -1289,6 +1327,7 @@ static int install_info_symlink_wants( int r = 0, q; assert(i); + assert(paths); assert(config_path); if (unit_name_is_valid(i->name, UNIT_NAME_TEMPLATE)) { @@ -1309,6 +1348,7 @@ static int install_info_symlink_wants( STRV_FOREACH(s, list) { _cleanup_free_ char *path = NULL, *dst = NULL; + const char *rp; q = install_full_printf(i, *s, &dst); if (q < 0) @@ -1323,7 +1363,9 @@ static int install_info_symlink_wants( if (!path) return -ENOMEM; - q = create_symlink(i->path, path, force, changes, n_changes); + rp = skip_root(paths, i->path); + + q = create_symlink(rp ?: i->path, path, force, changes, n_changes); if (r == 0) r = q; } @@ -1335,12 +1377,12 @@ static int install_info_symlink_link( UnitFileInstallInfo *i, const LookupPaths *paths, const char *config_path, - const char *root_dir, bool force, UnitFileChange **changes, unsigned *n_changes) { _cleanup_free_ char *path = NULL; + const char *rp; int r; assert(i); @@ -1348,7 +1390,7 @@ static int install_info_symlink_link( assert(config_path); assert(i->path); - r = in_search_path(i->path, paths->unit_path); + r = in_search_path(paths, i->path); if (r != 0) return r; @@ -1356,14 +1398,15 @@ static int install_info_symlink_link( if (!path) return -ENOMEM; - return create_symlink(i->path, path, force, changes, n_changes); + rp = skip_root(paths, i->path); + + return create_symlink(rp ?: i->path, path, force, changes, n_changes); } static int install_info_apply( UnitFileInstallInfo *i, const LookupPaths *paths, const char *config_path, - const char *root_dir, bool force, UnitFileChange **changes, unsigned *n_changes) { @@ -1377,17 +1420,17 @@ static int install_info_apply( if (i->type != UNIT_FILE_TYPE_REGULAR) return 0; - r = install_info_symlink_alias(i, config_path, force, changes, n_changes); + r = install_info_symlink_alias(i, paths, config_path, force, changes, n_changes); - q = install_info_symlink_wants(i, config_path, i->wanted_by, ".wants/", force, changes, n_changes); + q = install_info_symlink_wants(i, paths, config_path, i->wanted_by, ".wants/", force, changes, n_changes); if (r == 0) r = q; - q = install_info_symlink_wants(i, config_path, i->required_by, ".requires/", force, changes, n_changes); + q = install_info_symlink_wants(i, paths, config_path, i->required_by, ".requires/", force, changes, n_changes); if (r == 0) r = q; - q = install_info_symlink_link(i, paths, config_path, root_dir, force, changes, n_changes); + q = install_info_symlink_link(i, paths, config_path, force, changes, n_changes); if (r == 0) r = q; @@ -1399,7 +1442,6 @@ static int install_context_apply( InstallContext *c, const LookupPaths *paths, const char *config_path, - const char *root_dir, bool force, SearchFlags flags, UnitFileChange **changes, @@ -1427,14 +1469,14 @@ static int install_context_apply( if (q < 0) return q; - r = install_info_traverse(scope, c, root_dir, paths, i, flags, NULL); + r = install_info_traverse(scope, c, paths, i, flags, NULL); if (r < 0) return r; if (i->type != UNIT_FILE_TYPE_REGULAR) continue; - q = install_info_apply(i, paths, config_path, root_dir, force, changes, n_changes); + q = install_info_apply(i, paths, config_path, force, changes, n_changes); if (r >= 0) { if (q < 0) r = q; @@ -1451,8 +1493,7 @@ static int install_context_mark_for_removal( InstallContext *c, const LookupPaths *paths, Set **remove_symlinks_to, - const char *config_path, - const char *root_dir) { + const char *config_path) { UnitFileInstallInfo *i; int r; @@ -1476,7 +1517,7 @@ static int install_context_mark_for_removal( if (r < 0) return r; - r = install_info_traverse(scope, c, root_dir, paths, i, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, NULL); + r = install_info_traverse(scope, c, paths, i, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, NULL); if (r < 0) return r; @@ -1500,20 +1541,19 @@ int unit_file_mask( UnitFileChange **changes, unsigned *n_changes) { - _cleanup_free_ char *prefix = NULL; + _cleanup_lookup_paths_free_ LookupPaths paths = {}; + const char *config_path; char **i; int r; assert(scope >= 0); assert(scope < _UNIT_FILE_SCOPE_MAX); - r = verify_root_dir(scope, &root_dir); + r = lookup_paths_init(&paths, scope, 0, root_dir); if (r < 0) return r; - r = get_config_path(scope, runtime, root_dir, &prefix); - if (r < 0) - return r; + config_path = runtime ? paths.runtime_config : paths.persistent_config; STRV_FOREACH(i, files) { _cleanup_free_ char *path = NULL; @@ -1525,7 +1565,7 @@ int unit_file_mask( continue; } - path = path_make_absolute(*i, prefix); + path = path_make_absolute(*i, config_path); if (!path) return -ENOMEM; @@ -1545,23 +1585,22 @@ int unit_file_unmask( UnitFileChange **changes, unsigned *n_changes) { + _cleanup_lookup_paths_free_ LookupPaths paths = {}; _cleanup_set_free_free_ Set *remove_symlinks_to = NULL; - _cleanup_free_ char *config_path = NULL; _cleanup_free_ char **todo = NULL; size_t n_todo = 0, n_allocated = 0; + const char *config_path; char **i; int r, q; assert(scope >= 0); assert(scope < _UNIT_FILE_SCOPE_MAX); - r = verify_root_dir(scope, &root_dir); + r = lookup_paths_init(&paths, scope, 0, root_dir); if (r < 0) return r; - r = get_config_path(scope, runtime, root_dir, &config_path); - if (r < 0) - return r; + config_path = runtime ? paths.runtime_config : paths.persistent_config; STRV_FOREACH(i, files) { _cleanup_free_ char *path = NULL; @@ -1592,24 +1631,28 @@ int unit_file_unmask( r = 0; STRV_FOREACH(i, todo) { _cleanup_free_ char *path = NULL; + const char *rp; path = path_make_absolute(*i, config_path); if (!path) return -ENOMEM; if (unlink(path) < 0) { - if (errno != -ENOENT && r >= 0) + if (errno != ENOENT && r >= 0) r = -errno; - } else { - q = mark_symlink_for_removal(&remove_symlinks_to, path); - if (q < 0) - return q; - unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, path, NULL); + continue; } + + unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, path, NULL); + + rp = skip_root(&paths, path); + q = mark_symlink_for_removal(&remove_symlinks_to, rp ?: path); + if (q < 0) + return q; } - q = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes); + q = remove_marked_symlinks(remove_symlinks_to, config_path, &paths, changes, n_changes); if (r >= 0) r = q; @@ -1626,26 +1669,20 @@ int unit_file_link( unsigned *n_changes) { _cleanup_lookup_paths_free_ LookupPaths paths = {}; - _cleanup_free_ char *config_path = NULL; _cleanup_free_ char **todo = NULL; size_t n_todo = 0, n_allocated = 0; + const char *config_path; char **i; int r, q; assert(scope >= 0); assert(scope < _UNIT_FILE_SCOPE_MAX); - r = verify_root_dir(scope, &root_dir); + r = lookup_paths_init(&paths, scope, 0, root_dir); if (r < 0) return r; - r = lookup_paths_init_from_scope(&paths, scope, root_dir); - if (r < 0) - return r; - - r = get_config_path(scope, runtime, root_dir, &config_path); - if (r < 0) - return r; + config_path = runtime ? paths.runtime_config : paths.persistent_config; STRV_FOREACH(i, files) { _cleanup_free_ char *full = NULL; @@ -1659,7 +1696,7 @@ int unit_file_link( if (!unit_name_is_valid(fn, UNIT_NAME_ANY)) return -EINVAL; - full = prefix_root(root_dir, *i); + full = prefix_root(paths.root_dir, *i); if (!full) return -ENOMEM; @@ -1672,7 +1709,7 @@ int unit_file_link( if (!S_ISREG(st.st_mode)) return -ENOTTY; - q = in_search_path(*i, paths.unit_path); + q = in_search_path(&paths, *i); if (q < 0) return q; if (q > 0) @@ -1688,13 +1725,15 @@ int unit_file_link( r = 0; STRV_FOREACH(i, todo) { - _cleanup_free_ char *path = NULL; + _cleanup_free_ char *new_path = NULL; + const char *old_path; - path = path_make_absolute(basename(*i), config_path); - if (!path) + old_path = skip_root(&paths, *i); + new_path = path_make_absolute(basename(*i), config_path); + if (!new_path) return -ENOMEM; - q = create_symlink(*i, path, force, changes, n_changes); + q = create_symlink(old_path ?: *i, new_path, force, changes, n_changes); if (q < 0 && r >= 0) r = q; } @@ -1702,6 +1741,182 @@ int unit_file_link( return r; } +static int path_shall_revert(const LookupPaths *paths, const char *path) { + int r; + + assert(paths); + assert(path); + + /* Checks whether the path is one where the drop-in directories shall be removed. */ + + r = path_is_config(paths, path); + if (r != 0) + return r; + + r = path_is_control(paths, path); + if (r != 0) + return r; + + return path_is_transient(paths, path); +} + +int unit_file_revert( + UnitFileScope scope, + const char *root_dir, + char **files, + UnitFileChange **changes, + unsigned *n_changes) { + + _cleanup_set_free_free_ Set *remove_symlinks_to = NULL; + /* _cleanup_(install_context_done) InstallContext c = {}; */ + _cleanup_lookup_paths_free_ LookupPaths paths = {}; + _cleanup_strv_free_ char **todo = NULL; + size_t n_todo = 0, n_allocated = 0; + char **i; + int r, q; + + /* Puts a unit file back into vendor state. This means: + * + * a) we remove all drop-in snippets added by the user ("config"), add to transient units ("transient"), and + * added via "systemctl set-property" ("control"), but not if the drop-in is generated ("generated"). + * + * c) if there's a vendor unit file (i.e. one in /usr) we remove any configured overriding unit files (i.e. in + * "config", but not in "transient" or "control" or even "generated"). + * + * We remove all that in both the runtime and the persistant directories, if that applies. + */ + + r = lookup_paths_init(&paths, scope, 0, root_dir); + if (r < 0) + return r; + + STRV_FOREACH(i, files) { + bool has_vendor = false; + char **p; + + if (!unit_name_is_valid(*i, UNIT_NAME_ANY)) + return -EINVAL; + + STRV_FOREACH(p, paths.search_path) { + _cleanup_free_ char *path = NULL, *dropin = NULL; + struct stat st; + + path = path_make_absolute(*i, *p); + if (!path) + return -ENOMEM; + + r = lstat(path, &st); + if (r < 0) { + if (errno != ENOENT) + return -errno; + } else if (S_ISREG(st.st_mode)) { + /* Check if there's a vendor version */ + r = path_is_vendor(&paths, path); + if (r < 0) + return r; + if (r > 0) + has_vendor = true; + } + + dropin = strappend(path, ".d"); + if (!dropin) + return -ENOMEM; + + r = lstat(dropin, &st); + if (r < 0) { + if (errno != ENOENT) + return -errno; + } else if (S_ISDIR(st.st_mode)) { + /* Remove the drop-ins */ + r = path_shall_revert(&paths, dropin); + if (r < 0) + return r; + if (r > 0) { + if (!GREEDY_REALLOC0(todo, n_allocated, n_todo + 2)) + return -ENOMEM; + + todo[n_todo++] = dropin; + dropin = NULL; + } + } + } + + if (!has_vendor) + continue; + + /* OK, there's a vendor version, hence drop all configuration versions */ + STRV_FOREACH(p, paths.search_path) { + _cleanup_free_ char *path = NULL; + struct stat st; + + path = path_make_absolute(*i, *p); + if (!path) + return -ENOMEM; + + r = lstat(path, &st); + if (r < 0) { + if (errno != ENOENT) + return -errno; + } else if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) { + r = path_is_config(&paths, path); + if (r < 0) + return r; + if (r > 0) { + if (!GREEDY_REALLOC0(todo, n_allocated, n_todo + 2)) + return -ENOMEM; + + todo[n_todo++] = path; + path = NULL; + } + } + } + } + + strv_uniq(todo); + + r = 0; + STRV_FOREACH(i, todo) { + _cleanup_strv_free_ char **fs = NULL; + const char *rp; + char **j; + + (void) get_files_in_directory(*i, &fs); + + q = rm_rf(*i, REMOVE_ROOT|REMOVE_PHYSICAL); + if (q < 0 && q != -ENOENT && r >= 0) { + r = q; + continue; + } + + STRV_FOREACH(j, fs) { + _cleanup_free_ char *t = NULL; + + t = strjoin(*i, "/", *j, NULL); + if (!t) + return -ENOMEM; + + unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, t, NULL); + } + + unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, *i, NULL); + + rp = skip_root(&paths, *i); + q = mark_symlink_for_removal(&remove_symlinks_to, rp ?: *i); + if (q < 0) + return q; + } + + q = remove_marked_symlinks(remove_symlinks_to, paths.runtime_config, &paths, changes, n_changes); + if (r >= 0) + r = q; + + q = remove_marked_symlinks(remove_symlinks_to, paths.persistent_config, &paths, changes, n_changes); + if (r >= 0) + r = q; + + return r; +} + int unit_file_add_dependency( UnitFileScope scope, bool runtime, @@ -1715,8 +1930,8 @@ int unit_file_add_dependency( _cleanup_lookup_paths_free_ LookupPaths paths = {}; _cleanup_(install_context_done) InstallContext c = {}; - _cleanup_free_ char *config_path = NULL; UnitFileInstallInfo *i, *target_info; + const char *config_path; char **f; int r; @@ -1730,34 +1945,30 @@ int unit_file_add_dependency( if (!unit_name_is_valid(target, UNIT_NAME_ANY)) return -EINVAL; - r = verify_root_dir(scope, &root_dir); + r = lookup_paths_init(&paths, scope, 0, root_dir); if (r < 0) return r; - r = lookup_paths_init_from_scope(&paths, scope, root_dir); - if (r < 0) - return r; + config_path = runtime ? paths.runtime_config : paths.persistent_config; - r = get_config_path(scope, runtime, root_dir, &config_path); + r = install_info_discover(scope, &c, &paths, target, SEARCH_FOLLOW_CONFIG_SYMLINKS, &target_info); if (r < 0) return r; - - r = install_info_discover(scope, &c, root_dir, &paths, target, SEARCH_FOLLOW_CONFIG_SYMLINKS, &target_info); + r = install_info_may_process(target_info, &paths); if (r < 0) return r; - if (target_info->type == UNIT_FILE_TYPE_MASKED) - return -ESHUTDOWN; assert(target_info->type == UNIT_FILE_TYPE_REGULAR); STRV_FOREACH(f, files) { char ***l; - r = install_info_discover(scope, &c, root_dir, &paths, *f, SEARCH_FOLLOW_CONFIG_SYMLINKS, &i); + r = install_info_discover(scope, &c, &paths, *f, SEARCH_FOLLOW_CONFIG_SYMLINKS, &i); + if (r < 0) + return r; + r = install_info_may_process(i, &paths); if (r < 0) return r; - if (i->type == UNIT_FILE_TYPE_MASKED) - return -ESHUTDOWN; assert(i->type == UNIT_FILE_TYPE_REGULAR); @@ -1776,7 +1987,7 @@ int unit_file_add_dependency( return -ENOMEM; } - return install_context_apply(scope, &c, &paths, config_path, root_dir, force, SEARCH_FOLLOW_CONFIG_SYMLINKS, changes, n_changes); + return install_context_apply(scope, &c, &paths, config_path, force, SEARCH_FOLLOW_CONFIG_SYMLINKS, changes, n_changes); } int unit_file_enable( @@ -1790,7 +2001,7 @@ int unit_file_enable( _cleanup_lookup_paths_free_ LookupPaths paths = {}; _cleanup_(install_context_done) InstallContext c = {}; - _cleanup_free_ char *config_path = NULL; + const char *config_path; UnitFileInstallInfo *i; char **f; int r; @@ -1798,24 +2009,19 @@ int unit_file_enable( assert(scope >= 0); assert(scope < _UNIT_FILE_SCOPE_MAX); - r = verify_root_dir(scope, &root_dir); + r = lookup_paths_init(&paths, scope, 0, root_dir); if (r < 0) return r; - r = lookup_paths_init_from_scope(&paths, scope, root_dir); - if (r < 0) - return r; - - r = get_config_path(scope, runtime, root_dir, &config_path); - if (r < 0) - return r; + config_path = runtime ? paths.runtime_config : paths.persistent_config; STRV_FOREACH(f, files) { - r = install_info_discover(scope, &c, root_dir, &paths, *f, SEARCH_LOAD, &i); + r = install_info_discover(scope, &c, &paths, *f, SEARCH_LOAD, &i); + if (r < 0) + return r; + r = install_info_may_process(i, &paths); if (r < 0) return r; - if (i->type == UNIT_FILE_TYPE_MASKED) - return -ESHUTDOWN; assert(i->type == UNIT_FILE_TYPE_REGULAR); } @@ -1825,7 +2031,7 @@ int unit_file_enable( is useful to determine whether the passed files had any installation data at all. */ - return install_context_apply(scope, &c, &paths, config_path, root_dir, force, SEARCH_LOAD, changes, n_changes); + return install_context_apply(scope, &c, &paths, config_path, force, SEARCH_LOAD, changes, n_changes); } int unit_file_disable( @@ -1838,25 +2044,19 @@ int unit_file_disable( _cleanup_lookup_paths_free_ LookupPaths paths = {}; _cleanup_(install_context_done) InstallContext c = {}; - _cleanup_free_ char *config_path = NULL; _cleanup_set_free_free_ Set *remove_symlinks_to = NULL; + const char *config_path; char **i; int r; assert(scope >= 0); assert(scope < _UNIT_FILE_SCOPE_MAX); - r = verify_root_dir(scope, &root_dir); + r = lookup_paths_init(&paths, scope, 0, root_dir); if (r < 0) return r; - r = lookup_paths_init_from_scope(&paths, scope, root_dir); - if (r < 0) - return r; - - r = get_config_path(scope, runtime, root_dir, &config_path); - if (r < 0) - return r; + config_path = runtime ? paths.runtime_config : paths.persistent_config; STRV_FOREACH(i, files) { if (!unit_name_is_valid(*i, UNIT_NAME_ANY)) @@ -1867,11 +2067,11 @@ int unit_file_disable( return r; } - r = install_context_mark_for_removal(scope, &c, &paths, &remove_symlinks_to, config_path, root_dir); + r = install_context_mark_for_removal(scope, &c, &paths, &remove_symlinks_to, config_path); if (r < 0) return r; - return remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes); + return remove_marked_symlinks(remove_symlinks_to, config_path, &paths, changes, n_changes); } int unit_file_reenable( @@ -1912,41 +2112,34 @@ int unit_file_set_default( _cleanup_lookup_paths_free_ LookupPaths paths = {}; _cleanup_(install_context_done) InstallContext c = {}; - _cleanup_free_ char *config_path = NULL; UnitFileInstallInfo *i; - const char *path; + const char *new_path, *old_path; int r; assert(scope >= 0); assert(scope < _UNIT_FILE_SCOPE_MAX); assert(name); - if (unit_name_to_type(name) != UNIT_TARGET) + if (unit_name_to_type(name) != UNIT_TARGET) /* this also validates the name */ return -EINVAL; if (streq(name, SPECIAL_DEFAULT_TARGET)) return -EINVAL; - r = verify_root_dir(scope, &root_dir); + r = lookup_paths_init(&paths, scope, 0, root_dir); if (r < 0) return r; - r = lookup_paths_init_from_scope(&paths, scope, root_dir); + r = install_info_discover(scope, &c, &paths, name, 0, &i); if (r < 0) return r; - - r = get_config_path(scope, false, root_dir, &config_path); + r = install_info_may_process(i, &paths); if (r < 0) return r; - r = install_info_discover(scope, &c, root_dir, &paths, name, 0, &i); - if (r < 0) - return r; - if (i->type == UNIT_FILE_TYPE_MASKED) - return -ESHUTDOWN; - - path = strjoina(config_path, "/" SPECIAL_DEFAULT_TARGET); + old_path = skip_root(&paths, i->path); + new_path = strjoina(paths.persistent_config, "/" SPECIAL_DEFAULT_TARGET); - return create_symlink(i->path, path, force, changes, n_changes); + return create_symlink(old_path ?: i->path, new_path, force, changes, n_changes); } int unit_file_get_default( @@ -1964,19 +2157,16 @@ int unit_file_get_default( assert(scope < _UNIT_FILE_SCOPE_MAX); assert(name); - r = verify_root_dir(scope, &root_dir); + r = lookup_paths_init(&paths, scope, 0, root_dir); if (r < 0) return r; - r = lookup_paths_init_from_scope(&paths, scope, root_dir); + r = install_info_discover(scope, &c, &paths, SPECIAL_DEFAULT_TARGET, SEARCH_FOLLOW_CONFIG_SYMLINKS, &i); if (r < 0) return r; - - r = install_info_discover(scope, &c, root_dir, &paths, SPECIAL_DEFAULT_TARGET, SEARCH_FOLLOW_CONFIG_SYMLINKS, &i); + r = install_info_may_process(i, &paths); if (r < 0) return r; - if (i->type == UNIT_FILE_TYPE_MASKED) - return -ESHUTDOWN; n = strdup(i->name); if (!n) @@ -1986,9 +2176,8 @@ int unit_file_get_default( return 0; } -int unit_file_lookup_state( +static int unit_file_lookup_state( UnitFileScope scope, - const char *root_dir, const LookupPaths *paths, const char *name, UnitFileState *ret) { @@ -2004,11 +2193,7 @@ int unit_file_lookup_state( if (!unit_name_is_valid(name, UNIT_NAME_ANY)) return -EINVAL; - r = verify_root_dir(scope, &root_dir); - if (r < 0) - return r; - - r = install_info_discover(scope, &c, root_dir, paths, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, &i); + r = install_info_discover(scope, &c, paths, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, &i); if (r < 0) return r; @@ -2019,11 +2204,31 @@ int unit_file_lookup_state( switch (i->type) { case UNIT_FILE_TYPE_MASKED: - state = path_startswith(i->path, "/run") ? UNIT_FILE_MASKED_RUNTIME : UNIT_FILE_MASKED; + r = path_is_runtime(paths, i->path); + if (r < 0) + return r; + + state = r > 0 ? UNIT_FILE_MASKED_RUNTIME : UNIT_FILE_MASKED; break; case UNIT_FILE_TYPE_REGULAR: - r = find_symlinks_in_scope(scope, root_dir, i->name, &state); + r = path_is_generator(paths, i->path); + if (r < 0) + return r; + if (r > 0) { + state = UNIT_FILE_GENERATED; + break; + } + + r = path_is_transient(paths, i->path); + if (r < 0) + return r; + if (r > 0) { + state = UNIT_FILE_TRANSIENT; + break; + } + + r = find_symlinks_in_scope(scope, paths, i->name, &state); if (r < 0) return r; if (r == 0) { @@ -2058,15 +2263,30 @@ int unit_file_get_state( assert(scope < _UNIT_FILE_SCOPE_MAX); assert(name); - r = verify_root_dir(scope, &root_dir); + r = lookup_paths_init(&paths, scope, 0, root_dir); if (r < 0) return r; - r = lookup_paths_init_from_scope(&paths, scope, root_dir); + return unit_file_lookup_state(scope, &paths, name, ret); +} + +int unit_file_exists(UnitFileScope scope, const LookupPaths *paths, const char *name) { + _cleanup_(install_context_done) InstallContext c = {}; + int r; + + assert(paths); + assert(name); + + if (!unit_name_is_valid(name, UNIT_NAME_ANY)) + return -EINVAL; + + r = install_info_discover(scope, &c, paths, name, 0, NULL); + if (r == -ENOENT) + return 0; if (r < 0) return r; - return unit_file_lookup_state(scope, root_dir, &paths, name, ret); + return 1; } int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char *name) { @@ -2078,10 +2298,6 @@ int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char assert(scope < _UNIT_FILE_SCOPE_MAX); assert(name); - r = verify_root_dir(scope, &root_dir); - if (r < 0) - return r; - if (!unit_name_is_valid(name, UNIT_NAME_ANY)) return -EINVAL; @@ -2164,7 +2380,6 @@ static int execute_preset( InstallContext *minus, const LookupPaths *paths, const char *config_path, - const char *root_dir, char **files, UnitFilePresetMode mode, bool force, @@ -2181,11 +2396,11 @@ static int execute_preset( if (mode != UNIT_FILE_PRESET_ENABLE_ONLY) { _cleanup_set_free_free_ Set *remove_symlinks_to = NULL; - r = install_context_mark_for_removal(scope, minus, paths, &remove_symlinks_to, config_path, root_dir); + r = install_context_mark_for_removal(scope, minus, paths, &remove_symlinks_to, config_path); if (r < 0) return r; - r = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes); + r = remove_marked_symlinks(remove_symlinks_to, config_path, paths, changes, n_changes); } else r = 0; @@ -2193,7 +2408,7 @@ static int execute_preset( int q; /* Returns number of symlinks that where supposed to be installed. */ - q = install_context_apply(scope, plus, paths, config_path, root_dir, force, SEARCH_LOAD, changes, n_changes); + q = install_context_apply(scope, plus, paths, config_path, force, SEARCH_LOAD, changes, n_changes); if (r >= 0) { if (q < 0) r = q; @@ -2210,7 +2425,6 @@ static int preset_prepare_one( InstallContext *plus, InstallContext *minus, LookupPaths *paths, - const char *root_dir, UnitFilePresetMode mode, const char *name) { @@ -2221,19 +2435,20 @@ static int preset_prepare_one( install_info_find(minus, name)) return 0; - r = unit_file_query_preset(scope, root_dir, name); + r = unit_file_query_preset(scope, paths->root_dir, name); if (r < 0) return r; if (r > 0) { - r = install_info_discover(scope, plus, root_dir, paths, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, &i); + r = install_info_discover(scope, plus, paths, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, &i); if (r < 0) return r; - if (i->type == UNIT_FILE_TYPE_MASKED) - return -ESHUTDOWN; + r = install_info_may_process(i, paths); + if (r < 0) + return r; } else - r = install_info_discover(scope, minus, root_dir, paths, name, SEARCH_FOLLOW_CONFIG_SYMLINKS, &i); + r = install_info_discover(scope, minus, paths, name, SEARCH_FOLLOW_CONFIG_SYMLINKS, &i); return r; } @@ -2250,7 +2465,7 @@ int unit_file_preset( _cleanup_(install_context_done) InstallContext plus = {}, minus = {}; _cleanup_lookup_paths_free_ LookupPaths paths = {}; - _cleanup_free_ char *config_path = NULL; + const char *config_path; char **i; int r; @@ -2258,28 +2473,22 @@ int unit_file_preset( assert(scope < _UNIT_FILE_SCOPE_MAX); assert(mode < _UNIT_FILE_PRESET_MAX); - r = verify_root_dir(scope, &root_dir); + r = lookup_paths_init(&paths, scope, 0, root_dir); if (r < 0) return r; - r = lookup_paths_init_from_scope(&paths, scope, root_dir); - if (r < 0) - return r; - - r = get_config_path(scope, runtime, root_dir, &config_path); - if (r < 0) - return r; + config_path = runtime ? paths.runtime_config : paths.persistent_config; STRV_FOREACH(i, files) { if (!unit_name_is_valid(*i, UNIT_NAME_ANY)) return -EINVAL; - r = preset_prepare_one(scope, &plus, &minus, &paths, root_dir, mode, *i); + r = preset_prepare_one(scope, &plus, &minus, &paths, mode, *i); if (r < 0) return r; } - return execute_preset(scope, &plus, &minus, &paths, config_path, root_dir, files, mode, force, changes, n_changes); + return execute_preset(scope, &plus, &minus, &paths, config_path, files, mode, force, changes, n_changes); } int unit_file_preset_all( @@ -2293,7 +2502,7 @@ int unit_file_preset_all( _cleanup_(install_context_done) InstallContext plus = {}, minus = {}; _cleanup_lookup_paths_free_ LookupPaths paths = {}; - _cleanup_free_ char *config_path = NULL; + const char *config_path = NULL; char **i; int r; @@ -2301,28 +2510,17 @@ int unit_file_preset_all( assert(scope < _UNIT_FILE_SCOPE_MAX); assert(mode < _UNIT_FILE_PRESET_MAX); - r = verify_root_dir(scope, &root_dir); - if (r < 0) - return r; - - r = lookup_paths_init_from_scope(&paths, scope, root_dir); + r = lookup_paths_init(&paths, scope, 0, root_dir); if (r < 0) return r; - r = get_config_path(scope, runtime, root_dir, &config_path); - if (r < 0) - return r; + config_path = runtime ? paths.runtime_config : paths.persistent_config; - STRV_FOREACH(i, paths.unit_path) { + STRV_FOREACH(i, paths.search_path) { _cleanup_closedir_ DIR *d = NULL; - _cleanup_free_ char *units_dir; struct dirent *de; - units_dir = path_join(root_dir, *i, NULL); - if (!units_dir) - return -ENOMEM; - - d = opendir(units_dir); + d = opendir(*i); if (!d) { if (errno == ENOENT) continue; @@ -2340,13 +2538,13 @@ int unit_file_preset_all( if (!IN_SET(de->d_type, DT_LNK, DT_REG)) continue; - r = preset_prepare_one(scope, &plus, &minus, &paths, root_dir, mode, de->d_name); + r = preset_prepare_one(scope, &plus, &minus, &paths, mode, de->d_name); if (r < 0) return r; } } - return execute_preset(scope, &plus, &minus, &paths, config_path, root_dir, NULL, mode, force, changes, n_changes); + return execute_preset(scope, &plus, &minus, &paths, config_path, NULL, mode, force, changes, n_changes); } static void unit_file_list_free_one(UnitFileList *f) { @@ -2381,24 +2579,15 @@ int unit_file_get_list( assert(scope < _UNIT_FILE_SCOPE_MAX); assert(h); - r = verify_root_dir(scope, &root_dir); + r = lookup_paths_init(&paths, scope, 0, root_dir); if (r < 0) return r; - r = lookup_paths_init_from_scope(&paths, scope, root_dir); - if (r < 0) - return r; - - STRV_FOREACH(i, paths.unit_path) { + STRV_FOREACH(i, paths.search_path) { _cleanup_closedir_ DIR *d = NULL; - _cleanup_free_ char *units_dir; struct dirent *de; - units_dir = path_join(root_dir, *i, NULL); - if (!units_dir) - return -ENOMEM; - - d = opendir(units_dir); + d = opendir(*i); if (!d) { if (errno == ENOENT) continue; @@ -2424,11 +2613,11 @@ int unit_file_get_list( if (!f) return -ENOMEM; - f->path = path_make_absolute(de->d_name, units_dir); + f->path = path_make_absolute(de->d_name, *i); if (!f->path) return -ENOMEM; - r = unit_file_lookup_state(scope, root_dir, &paths, basename(f->path), &f->state); + r = unit_file_lookup_state(scope, &paths, de->d_name, &f->state); if (r < 0) f->state = UNIT_FILE_BAD; @@ -2453,6 +2642,8 @@ static const char* const unit_file_state_table[_UNIT_FILE_STATE_MAX] = { [UNIT_FILE_STATIC] = "static", [UNIT_FILE_DISABLED] = "disabled", [UNIT_FILE_INDIRECT] = "indirect", + [UNIT_FILE_GENERATED] = "generated", + [UNIT_FILE_TRANSIENT] = "transient", [UNIT_FILE_BAD] = "bad", }; diff --git a/src/shared/install.h b/src/shared/install.h index c1a43e23e7..6f8b45d4f6 100644 --- a/src/shared/install.h +++ b/src/shared/install.h @@ -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); diff --git a/src/shared/machine-image.c b/src/shared/machine-image.c index d2f1c4a40c..bebfc40efe 100644 --- a/src/shared/machine-image.c +++ b/src/shared/machine-image.c @@ -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) diff --git a/src/shared/machine-image.h b/src/shared/machine-image.h index 31b720d50c..7410168c4f 100644 --- a/src/shared/machine-image.h +++ b/src/shared/machine-image.h @@ -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; +} diff --git a/src/shared/path-lookup.c b/src/shared/path-lookup.c index 5410620725..ca69a0aef2 100644 --- a/src/shared/path-lookup.c +++ b/src/shared/path-lookup.c @@ -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; - e = getenv("XDG_CONFIG_HOME"); - if (e) { - r = strappend(e, "/systemd/user"); - if (!r) - return -ENOMEM; - - *config_home = r; - return 1; - } else { - const char *home; + assert(ret); + assert(suffix); - home = getenv("HOME"); - if (home) { - r = strappend(home, "/.config/systemd/user"); - if (!r) - return -ENOMEM; + e = getenv("XDG_RUNTIME_DIR"); + if (!e) + return -ENXIO; - *config_home = r; - return 1; - } - } + j = strappend(e, suffix); + if (!j) + return -ENOMEM; + *ret = j; return 0; } -int user_runtime_dir(char **runtime_dir) { +static int user_config_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; + assert(ret); + + e = getenv("XDG_CONFIG_HOME"); + if (e) + j = strappend(e, suffix); + else { + const char *home; - *runtime_dir = r; - return 1; + home = getenv("HOME"); + if (!home) + return -ENXIO; + + j = strjoin(home, "/.config", suffix, NULL); } + if (!j) + return -ENOMEM; + + *ret = j; return 0; } -static int user_data_home_dir(char **dir, const char *suffix) { +static int user_data_dir(char **ret, const char *suffix) { const char *e; - char *res; + char *j; + + 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_strv(&res, (char**) config_unit_paths, false) < 0) + if (strv_extend(&res, persistent_config) < 0) return NULL; - if (runtime_dir) - if (strv_extend(&res, runtime_dir) < 0) - return NULL; + if (strv_extend_strv(&res, (char**) config_unit_paths, false) < 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) { - - const char *e; + UnitFileScope scope, + LookupPathsFlags flags, + const char *root_dir) { + + _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); + + 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; - /* First priority is whatever has been passed to us via env - * vars */ + 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; + + case UNIT_FILE_USER: + add = user_dirs(persistent_config, runtime_config, + generator, generator_early, generator_late, + transient, + persistent_config, runtime_control); + break; - if (!unit_path) + 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; + p->root_dir = root; + root = NULL; - if (strv_isempty(p->sysvrcnd_path)) { - strv_free(p->sysvrcnd_path); + return 0; +} - p->sysvrcnd_path = strv_new( - SYSTEM_SYSVRCND_PATH, /* /etc/rcN.d/ */ - NULL); - if (!p->sysvrcnd_path) - return -ENOMEM; +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); + + /* 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. */ + + 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; } - if (!path_strv_resolve_uniq(p->sysvinit_path, root_dir)) - return -ENOMEM; + 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 (!path_strv_resolve_uniq(p->sysvrcnd_path, root_dir)) + if (!GREEDY_REALLOC(stats, allocated, n_stats+1)) 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); - } + stats[n_stats++] = st; + c++; + continue; - if (!strv_isempty(p->sysvrcnd_path)) { - _cleanup_free_ char *t = - strv_join(p->sysvrcnd_path, "\n\t"); - if (!t) - return -ENOMEM; + 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*)); + } - 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 + 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; } -void lookup_paths_free(LookupPaths *p) { +int lookup_paths_mkdir_generator(LookupPaths *p) { + int r, q; + assert(p); - p->unit_path = strv_free(p->unit_path); + if (!p->generator || !p->generator_early || !p->generator_late) + return -EINVAL; -#ifdef HAVE_SYSV_COMPAT - p->sysvinit_path = strv_free(p->sysvinit_path); - p->sysvrcnd_path = strv_free(p->sysvrcnd_path); -#endif + 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; } -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); +void lookup_paths_trim_generator(LookupPaths *p) { + assert(p); - zero(*paths); + /* 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); - return lookup_paths_init(paths, - scope == UNIT_FILE_SYSTEM ? MANAGER_SYSTEM : MANAGER_USER, - scope == UNIT_FILE_USER, - root_dir, - NULL, NULL, NULL); + /* 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."); + } } diff --git a/src/shared/path-lookup.h b/src/shared/path-lookup.h index 26c83d6111..f9bb2fe237 100644 --- a/src/shared/path-lookup.h +++ b/src/shared/path-lookup.h @@ -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" + +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; -int lookup_paths_init_from_scope(LookupPaths *paths, - UnitFileScope scope, - const char *root_dir); + /* 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); diff --git a/src/shared/tests.c b/src/shared/tests.c new file mode 100644 index 0000000000..409116290d --- /dev/null +++ b/src/shared/tests.c @@ -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; +} diff --git a/src/shared/tests.h b/src/shared/tests.h new file mode 100644 index 0000000000..93f09013a1 --- /dev/null +++ b/src/shared/tests.h @@ -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); diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 62cff3a677..f0e788a508 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -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; + siginfo_t status; + const char *name; 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; + 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; - - path = path_join(arg_root, *k, name); - if (!path) - return log_oom(); + 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; - 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."); diff --git a/src/systemd/sd-lldp.h b/src/systemd/sd-lldp.h index fa6ab9ad3b..5772d5794a 100644 --- a/src/systemd/sd-lldp.h +++ b/src/systemd/sd-lldp.h @@ -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 { diff --git a/src/sysv-generator/sysv-generator.c b/src/sysv-generator/sysv-generator.c index 59e1a3e921..9392a3d2b5 100644 --- a/src/sysv-generator/sysv-generator.c +++ b/src/sysv-generator/sysv-generator.c @@ -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; diff --git a/src/test/test-cgroup-mask.c b/src/test/test-cgroup-mask.c index ad15075a5b..4eb8fcd773 100644 --- a/src/test/test-cgroup-mask.c +++ b/src/test/test-cgroup-mask.c @@ -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; } diff --git a/src/test/test-engine.c b/src/test/test-engine.c index ca66f5b684..361d1e7b0b 100644 --- a/src/test/test-engine.c +++ b/src/test/test-engine.c @@ -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; diff --git a/src/test/test-execute.c b/src/test/test-execute.c index 901cc44af6..77ef4e8b2a 100644 --- a/src/test/test-execute.c +++ b/src/test/test-execute.c @@ -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); } diff --git a/src/test/test-install-root.c b/src/test/test-install-root.c index cd250ca7b8..2138655e29 100644 --- a/src/test/test-install-root.c +++ b/src/test/test-install-root.c @@ -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); diff --git a/src/test/test-path-lookup.c b/src/test/test-path-lookup.c index 268da002a9..096326d176 100644 --- a/src/test/test-path-lookup.c +++ b/src/test/test-path-lookup.c @@ -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; } diff --git a/src/test/test-path.c b/src/test/test-path.c index 1e704a03dc..435cafd83a 100644 --- a/src/test/test-path.c +++ b/src/test/test-path.c @@ -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++) { diff --git a/src/test/test-rbtree.c b/src/test/test-rbtree.c deleted file mode 100644 index 8ae416c557..0000000000 --- a/src/test/test-rbtree.c +++ /dev/null @@ -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; -} diff --git a/src/test/test-sched-prio.c b/src/test/test-sched-prio.c index 7f515b53d8..3e9caafc71 100644 --- a/src/test/test-sched-prio.c +++ b/src/test/test-sched-prio.c @@ -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; diff --git a/src/test/test-unit-file.c b/src/test/test-unit-file.c index cc6c61ba63..114ddf8478 100644 --- a/src/test/test-unit-file.c +++ b/src/test/test-unit-file.c @@ -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(); diff --git a/src/test/test-unit-name.c b/src/test/test-unit-name.c index 3de94ef425..2fd83f321c 100644 --- a/src/test/test-unit-name.c +++ b/src/test/test-unit-name.c @@ -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; |