* systemctl status foo.service should say that it is trigger by foo.timer
* systemctl status should know about 'systemd-analyze calendar ... --iterations='
-* systemctl list-timers foo should use .timer suffix by default
* If timer has just OnInactiveSec=..., it should fire after a specified time
after being started.
* `$SD_EVENT_PROFILE_DELAYS=1` — if set, the sd-event event loop implementation
will print latency information at runtime.
-* `$SYSTEMD_PROC_CMDLINE` — if set, may contain a string that is used as kernel
- command line instead of the actual one readable from /proc/cmdline. This is
- useful for debugging, in order to test generators and other code against
- specific kernel command lines.
+* `$SYSTEMD_PROC_CMDLINE` — if set, the contents are used as the kernel command
+ line instead of the actual one in /proc/cmdline. This is useful for
+ debugging, in order to test generators and other code against specific kernel
+ command lines.
+
+* `$SYSTEMD_EFI_OPTIONS` — if set, used instead of the string in SystemdOptions
+ EFI variable. Analogous to `$SYSTEMD_PROC_CMDLINE`.
* `$SYSTEMD_IN_INITRD` — takes a boolean. If set, overrides initrd detection.
This is useful for debugging and testing initrd-only programs in the main
systemd-nspawn:
-* `$UNIFIED_CGROUP_HIERARCHY=1` — if set, force nspawn into unified cgroup
- hierarchy mode.
+* `$SYSTEMD_NSPAWN_UNIFIED_HIERARCHY=1` — if set, force nspawn into unified
+ cgroup hierarchy mode.
* `$SYSTEMD_NSPAWN_API_VFS_WRITABLE=1` — if set, make /sys and /proc/sys and
friends writable in the container. If set to "network", leave only
# evdev:input:bZZZZvYYYYpXXXXeWWWW-VVVV
# This matches on the kernel modalias of the input-device, mainly:
# ZZZZ is the bus-id (see /usr/include/linux/input.h BUS_*), YYYY, XXXX and
-# WWW are the 4-digit hex uppercase vendor, product and version ID and VVVV
+# WWWW are the 4-digit hex uppercase vendor, product and version ID and VVVV
# is an arbitrary length input-modalias describing the device capabilities.
# The vendor, product and version ID for a device node "eventX" is listed
# in /sys/class/input/eventX/device/id.
KEYBOARD_KEY_67=email # envelope button
KEYBOARD_KEY_69=prog2 # link 2 button
+# Erazer
+evdev:atkbd:dmi:bvn*:bvr*:bd*:svnMedion*:pnErazer*:pvr*
+ KEYBOARD_KEY_a0=!mute
+ KEYBOARD_KEY_ae=!volumedown
+ KEYBOARD_KEY_b0=!volumeup
+
###########################################################
# Microsoft
###########################################################
sensor:modalias:acpi:KIOX000A*:dmi:*:svnEVE*:pnEveV:*
ACCEL_MOUNT_MATRIX=0, 1, 0; -1, 0, 0; 0, 0, 1
+#########################################
+# Google Chromebooks
+#########################################
+sensor:modalias:platform:cros-ec-accel:dmi:*:svnGOOGLE*
+ ACCEL_MOUNT_MATRIX=-1, 0, 0; 0, -1, 0; 0, 0, -1
+
#########################################
# GP-electronic
#########################################
# Lenovo Thinkpad *60 series
evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPad??60:*
evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPad??60?:*
+# Lenovo Thinkpad *70 series
+evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPad??70:*
+evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPad??70?:*
+# Lenovo Thinkpad *80 series
+evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPad??80:*
+evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPad??80?:*
+# Lenovo Thinkpad *90 series
+evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPad??90:*
+evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPad??90?:*
# Lenovo Thinkpad X1 Carbon 3rd gen
evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadX1Carbon3rd:*
# Lenovo Thinkpad X1 Carbon 4th gen
and the firmware's boot loader list.</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>is-installed</option></term>
+
+ <listitem><para>Checks whether <command>systemd-boot</command> is installed in the ESP. Note that a
+ single ESP might host multiple boot loaders; this hence checks whether
+ <command>systemd-boot</command> is one (of possibly many) installed boot loaders — and neither
+ whether it is the default nor whether it is registered in any EFI variables.</para></listitem>
+ </varlistentry>
+
<varlistentry>
<term><option>random-seed</option></term>
</varlistentry>
<varlistentry>
- <term><option>is-installed</option></term>
+ <term><option>system-options</option> <optional><replaceable>VALUE</replaceable></optional></term>
- <listitem><para>Checks whether <command>systemd-boot</command> is installed in the ESP. Note that a
- single ESP might host multiple boot loaders; this hence checks whether
- <command>systemd-boot</command> is one (of possibly many) installed boot loaders — and neither
- whether it is the default nor whether it is registered in any EFI variables.</para></listitem>
+ <listitem><para>When called without the optional argument, prints the current value of the
+ <literal>SystemdOptions</literal> EFI variable. When called with an argument, sets the
+ variable to that value. See
+ <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+ for the meaning of that variable.</para></listitem>
</varlistentry>
<varlistentry>
<refsect1>
<title>Description</title>
- <para>The kernel, the initial RAM disk (initrd) and
- basic userspace functionality may be configured at boot via
- kernel command line arguments.</para>
+ <para>The kernel, the initial RAM disk (initrd) and basic userspace functionality may be configured at
+ boot via kernel command line arguments. In addition, various systemd tools look at the EFI variable
+ <literal>SystemdOptions</literal> (if available). Both sources are combined, but the kernel command line
+ has higher priority. Please note that <emphasis>the EFI variable is only used by systemd tools, and is
+ ignored by the kernel and other user space tools</emphasis>, so it is not a replacement for the kernel
+ command line.</para>
<para>For command line parameters understood by the kernel, please
see
<citerefentry><refentrytitle>systemd-backlight@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-rfkill.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-hibernate-resume-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>systemd-firstboot.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ <citerefentry><refentrytitle>systemd-firstboot.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>bootctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
</para>
</refsect1>
journal. The first argument is a priority value. This is followed by a format string and its parameters, similar to
<citerefentry project='man-pages'><refentrytitle>printf</refentrytitle><manvolnum>3</manvolnum></citerefentry> or
<citerefentry project='man-pages'><refentrytitle>syslog</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
+ Note that currently the resulting message will be trucated to <constant>LINE_MAX - 8</constant>.
The priority value is one of <constant>LOG_EMERG</constant>, <constant>LOG_ALERT</constant>,
<constant>LOG_CRIT</constant>, <constant>LOG_ERR</constant>, <constant>LOG_WARNING</constant>,
<constant>LOG_NOTICE</constant>, <constant>LOG_INFO</constant>, <constant>LOG_DEBUG</constant>, as defined in
variable definitions. The parser strips leading and trailing whitespace from the values of assignments, unless
you use double quotes (").</para>
+ <para><ulink url="https://en.wikipedia.org/wiki/Escape_sequences_in_C#Table_of_escape_sequences">C escapes</ulink>
+ are supported, but not
+ <ulink url="https://en.wikipedia.org/wiki/Control_character#In_ASCII">most control characters</ulink>.
+ <literal>\t</literal> and <literal>\n</literal> can be used to insert tabs and newlines within
+ <varname>EnvironmentFile=</varname>.</para>
+
<para>The argument passed should be an absolute filename or wildcard expression, optionally prefixed with
<literal>-</literal>, which indicates that if the file does not exist, it will not be read and no error or
warning message is logged. This option may be specified more than once in which case all specified files are
<para>Variables set for invoked processes due to this setting are subject to being overridden by those
configured with <varname>Environment=</varname> or <varname>EnvironmentFile=</varname>.</para>
+ <para><ulink url="https://en.wikipedia.org/wiki/Escape_sequences_in_C#Table_of_escape_sequences">C escapes</ulink>
+ are supported, but not
+ <ulink url="https://en.wikipedia.org/wiki/Control_character#In_ASCII">most control characters</ulink>.
+ <literal>\t</literal> and <literal>\n</literal> can be used to insert tabs and newlines within
+ <varname>EnvironmentFile=</varname>.</para>
+
<para>Example:
<programlisting>PassEnvironment=VAR1 VAR2 VAR3</programlisting>
passes three variables <literal>VAR1</literal>,
unless it is empty.</para>
<para>Processes will first be terminated via <constant>SIGTERM</constant> (unless the signal to send
- is changed via <varname>KillSignal=</varname>). Optionally, this is immediately followed by a
- <constant>SIGHUP</constant> (if enabled with <varname>SendSIGHUP=</varname>). If processes still
- remain after the main process of a unit has exited or the delay configured via the
- <varname>TimeoutStopSec=</varname> has passed, the termination request is repeated with the
- <constant>SIGKILL</constant> signal or the signal specified via <varname>FinalKillSignal=</varname>
- (unless this is disabled via the <varname>SendSIGKILL=</varname> option). See
- <citerefentry><refentrytitle>kill</refentrytitle><manvolnum>2</manvolnum></citerefentry> for more
- information.</para>
+ is changed via <varname>KillSignal=</varname> or <varname>RestartKillSignal=</varname>). Optionally,
+ this is immediately followed by a <constant>SIGHUP</constant> (if enabled with
+ <varname>SendSIGHUP=</varname>). If processes still remain after the main process of a unit has
+ exited or the delay configured via the <varname>TimeoutStopSec=</varname> has passed, the termination
+ request is repeated with the <constant>SIGKILL</constant> signal or the signal specified via
+ <varname>FinalKillSignal=</varname> (unless this is disabled via the <varname>SendSIGKILL=</varname>
+ option). See <citerefentry><refentrytitle>kill</refentrytitle><manvolnum>2</manvolnum></citerefentry>
+ for more information.</para>
<para>Defaults to <option>control-group</option>.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>KillSignal=</varname></term>
- <listitem><para>Specifies which signal to use when killing a
- service. This controls the signal that is sent as first step
- of shutting down a unit (see above), and is usually followed
- by <constant>SIGKILL</constant> (see above and below). For a
- list of valid signals, see
+ <listitem><para>Specifies which signal to use when stopping a service. This controls the signal that
+ is sent as first step of shutting down a unit (see above), and is usually followed by
+ <constant>SIGKILL</constant> (see above and below). For a list of valid signals, see
<citerefentry project='man-pages'><refentrytitle>signal</refentrytitle><manvolnum>7</manvolnum></citerefentry>.
- Defaults to <constant>SIGTERM</constant>. </para>
+ Defaults to <constant>SIGTERM</constant>.</para>
- <para>Note that, right after sending the signal specified in
- this setting, systemd will always send
- <constant>SIGCONT</constant>, to ensure that even suspended
- tasks can be terminated cleanly.</para>
+ <para>Note that, right after sending the signal specified in this setting, systemd will always send
+ <constant>SIGCONT</constant>, to ensure that even suspended tasks can be terminated cleanly.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>RestartKillSignal=</varname></term>
+ <listitem><para>Specifies which signal to use when restarting a service. The same as
+ <varname>KillSignal=</varname> described above, with the exception that this setting is used in a
+ restart job. Not set by default, and the value of <varname>KillSignal=</varname> is used.</para>
</listitem>
</varlistentry>
lease expires. This is contrary to the DHCP specification, but may be the best choice if,
e.g., the root filesystem relies on this connection. The setting <literal>dhcp</literal>
implies <literal>dhcp-on-stop</literal>, and <literal>yes</literal> implies
- <literal>dhcp</literal> and <literal>static</literal>. Defaults to
- <literal>dhcp-on-stop</literal>.</para>
+ <literal>dhcp</literal> and <literal>static</literal>. Defaults to <literal>no</literal>.
+ </para>
</listitem>
</varlistentry>
<para>Note that if IPv6 is enabled on the interface, and the MTU is chosen
below 1280 (the minimum MTU for IPv6) it will automatically be increased to this value.</para>
</listitem>
- </varlistentry>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>IPServiceType=</varname></term>
+ <listitem>
+ <para>Takes string; "CS6" or "CS4". Used to set IP service type to CS6 (network control)
+ or CS4 (Realtime). IPServiceType defaults to CS6 if nothing is specified.</para>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect1>
<term><varname>SendRelease=</varname></term>
<listitem>
<para>When true, the DHCPv4 client sends a DHCP release packet when it stops.
- Defaults to false.</para>
+ Defaults to true.</para>
</listitem>
</varlistentry>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>PrefixDelegationHint=</varname></term>
+ <listitem>
+ <para>Takes an IPv6 address with prefix length as <varname>Addresss=</varname> in
+ the "[Network]" section. Specifies the DHCPv6 client for the requesting router to include
+ a prefix-hint in the DHCPv6 solicitation. Prefix ranges 1-128. Defaults to unset.</para>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect1>
<para>Takes a memory size in bytes. If the value is suffixed with K, M, G or T, the specified memory size is
parsed as Kilobytes, Megabytes, Gigabytes, or Terabytes (with the base 1024), respectively. Alternatively, a
percentage value may be specified, which is taken relative to the installed physical memory on the
- system. This controls the <literal>memory.min</literal> control group attribute. For details about this
+ system. If assigned the special value <literal>infinity</literal>, all available memory is protected, which may be
+ useful in order to always inherit all of the protection afforded by ancestors.
+ This controls the <literal>memory.min</literal> control group attribute. For details about this
control group attribute, see <ulink
url="https://www.kernel.org/doc/Documentation/cgroup-v2.txt">cgroup-v2.txt</ulink>.</para>
<para>Takes a memory size in bytes. If the value is suffixed with K, M, G or T, the specified memory size is
parsed as Kilobytes, Megabytes, Gigabytes, or Terabytes (with the base 1024), respectively. Alternatively, a
percentage value may be specified, which is taken relative to the installed physical memory on the
- system. This controls the <literal>memory.low</literal> control group attribute. For details about this
+ system. If assigned the special value <literal>infinity</literal>, all available memory is protected, which may be
+ useful in order to always inherit all of the protection afforded by ancestors.
+ This controls the <literal>memory.low</literal> control group attribute. For details about this
control group attribute, see <ulink
url="https://www.kernel.org/doc/Documentation/cgroup-v2.txt">cgroup-v2.txt</ulink>.</para>
<term><varname>MemoryHigh=<replaceable>bytes</replaceable></varname></term>
<listitem>
- <para>Specify the high limit on memory usage of the executed processes in this unit. Memory usage may go
+ <para>Specify the throttling limit on memory usage of the executed processes in this unit. Memory usage may go
above the limit if unavoidable, but the processes are heavily slowed down and memory is taken away
aggressively in such cases. This is the main mechanism to control memory usage of a unit.</para>
parsed as Kilobytes, Megabytes, Gigabytes, or Terabytes (with the base 1024), respectively. Alternatively, a
percentage value may be specified, which is taken relative to the installed physical memory on the
system. If assigned the
- special value <literal>infinity</literal>, no memory limit is applied. This controls the
+ special value <literal>infinity</literal>, no memory throttling is applied. This controls the
<literal>memory.high</literal> control group attribute. For details about this control group attribute, see
<ulink url="https://www.kernel.org/doc/Documentation/cgroup-v2.txt">cgroup-v2.txt</ulink>.</para>
<varlistentry>
<term><varname>ExecStop=</varname></term>
- <listitem><para>Commands to execute to stop the service
- started via <varname>ExecStart=</varname>. This argument takes
- multiple command lines, following the same scheme as described
- for <varname>ExecStart=</varname> above. Use of this setting
- is optional. After the commands configured in this option are
- run, it is implied that the service is stopped, and any processes
- remaining for it are terminated
- according to the <varname>KillMode=</varname> setting (see
+ <listitem><para>Commands to execute to stop the service started via
+ <varname>ExecStart=</varname>. This argument takes multiple command lines, following the same scheme
+ as described for <varname>ExecStart=</varname> above. Use of this setting is optional. After the
+ commands configured in this option are run, it is implied that the service is stopped, and any
+ processes remaining for it are terminated according to the <varname>KillMode=</varname> setting (see
<citerefentry><refentrytitle>systemd.kill</refentrytitle><manvolnum>5</manvolnum></citerefentry>).
- If this option is not specified, the process is terminated by
- sending the signal specified in <varname>KillSignal=</varname>
- when service stop is requested. Specifier and environment
- variable substitution is supported (including
+ If this option is not specified, the process is terminated by sending the signal specified in
+ <varname>KillSignal=</varname> or <varname>RestartKillSignal=</varname> when service stop is
+ requested. Specifier and environment variable substitution is supported (including
<varname>$MAINPID</varname>, see above).</para>
- <para>Note that it is usually not sufficient to specify a command for this setting that only asks the service
- to terminate (for example, by queuing some form of termination signal for it), but does not wait for it to do
- so. Since the remaining processes of the services are killed according to <varname>KillMode=</varname> and
- <varname>KillSignal=</varname> as described above immediately after the command exited, this may not result in
- a clean stop. The specified command should hence be a synchronous operation, not an asynchronous one.</para>
+ <para>Note that it is usually not sufficient to specify a command for this setting that only asks the
+ service to terminate (for example, by sending some form of termination signal to it), but does not
+ wait for it to do so. Since the remaining processes of the services are killed according to
+ <varname>KillMode=</varname> and <varname>KillSignal=</varname> or
+ <varname>RestartKillSignal=</varname> as described above immediately after the command exited, this
+ may not result in a clean stop. The specified command should hence be a synchronous operation, not an
+ asynchronous one.</para>
<para>Note that the commands specified in <varname>ExecStop=</varname> are only executed when the service
started successfully first. They are not invoked if the service was never started at all, or in case its
<refsect1>
<title>Kernel Command Line</title>
- <para>When run as system instance systemd parses a number of
- kernel command line arguments<footnote><para>If run inside a Linux
- container these arguments may be passed as command line arguments
- to systemd itself, next to any of the command line options listed
- in the Options section above. If run outside of Linux containers,
- these arguments are parsed from <filename>/proc/cmdline</filename>
- instead.</para></footnote>:</para>
+ <para>When run as the system instance systemd parses a number of options listed below. They can be
+ specified as kernel command line arguments<footnote><para>If run inside a Linux container these arguments
+ may be passed as command line arguments to systemd itself, next to any of the command line options listed
+ in the Options section above. If run outside of Linux containers, these arguments are parsed from
+ <filename>/proc/cmdline</filename> instead.</para></footnote>, or through the
+ <literal>SystemdOptions</literal> EFI variable (on EFI systems). The kernel command line has higher
+ priority. Following variables are understood:</para>
<variablelist class='kernel-commandline-options'>
<varlistentry>
possible_link_flags = [
'-Wl,-z,relro',
'-Wl,-z,now',
+ '-fstack-protector',
]
if cc.get_id() == 'clang'
want_libcryptsetup = get_option('libcryptsetup')
if want_libcryptsetup != 'false' and not skip_deps
libcryptsetup = dependency('libcryptsetup',
- version : '>= 1.6.0',
+ version : '>= 2.0.1',
required : want_libcryptsetup == 'true')
have = libcryptsetup.found()
- have_sector = cc.has_member(
- 'struct crypt_params_plain',
- 'sector_size',
- prefix : '#include <libcryptsetup.h>')
else
have = false
- have_sector = false
libcryptsetup = []
endif
conf.set10('HAVE_LIBCRYPTSETUP', have)
-conf.set10('HAVE_LIBCRYPTSETUP_SECTOR_SIZE', have_sector)
want_libcurl = get_option('libcurl')
if want_libcurl != 'false' and not skip_deps
libgcrypt],
c_args : libsystemd_c_args + (static_libsystemd_pic ? [] : ['-fno-PIC']))
+#Generate autosuspend rules
+make_autosuspend_rules_py = find_program('tools/make-autosuspend-rules.py')
+
############################################################
# binaries that have --help and are intended for use by humans,
msgstr ""
"Project-Id-Version: systemd\n"
"Report-Msgid-Bugs-To: https://github.com/systemd/systemd/issues\n"
-"POT-Creation-Date: 2019-09-13 15:30+0000\n"
-"PO-Revision-Date: 2019-09-14 12:44+0200\n"
+"POT-Creation-Date: 2019-09-22 15:30+0000\n"
+"PO-Revision-Date: 2019-09-28 15:18+0200\n"
"Last-Translator: Piotr DrÄ…g <piotrdrag@gmail.com>\n"
"Language-Team: Polish <trans-pl@lists.fedoraproject.org>\n"
"Language: pl\n"
msgid "Authentication is required to reset DNS settings."
msgstr "Wymagane jest uwierzytelnienie, aby przywrócić ustawienia DNS."
+#: src/network/org.freedesktop.network1.policy:143
+msgid "Renew dynamic addresses"
+msgstr "Odnowienie adresów dynamicznych"
+
+#: src/network/org.freedesktop.network1.policy:144
+msgid "Authentication is required to renew dynamic addresses."
+msgstr "Wymagane jest uwierzytelnienie, aby odnowić adresy dynamiczne."
+
#: src/portable/org.freedesktop.portable1.policy:13
msgid "Inspect a portable service image"
msgstr "Badanie obrazu przenośnej usługi"
ACTION=="remove", GOTO="sensor_end"
# device matching the sensor's name and the machine's DMI data for IIO devices
-SUBSYSTEM=="iio", KERNEL=="iio*", SUBSYSTEMS=="usb|i2c", \
+SUBSYSTEM=="iio", KERNEL=="iio*", SUBSYSTEMS=="usb|i2c|platform", \
IMPORT{builtin}="hwdb 'sensor:modalias:$attr{modalias}:$attr{[dmi/id]modalias}'", \
GOTO="sensor_end"
--- /dev/null
+# This udev rule is for any devices that should enter automatic suspend
+# but are not already included in generated rules from ChromeOS via
+# tools/make-autosuspend-rules.py
+#
+
+ACTION!="add", GOTO="autosuspend_manual_end"
+SUBSYSTEM!="usb", GOTO="autosuspend_manual_end"
+
+SUBSYSTEM=="usb", GOTO="autosuspend_manual_usb"
+
+# USB rules
+LABEL="autosuspend_manual_usb"
+GOTO="autosuspend_manual_end"
+
+# Enable autosuspend
+LABEL="autosuspend_manual_enable"
+TEST=="power/control", ATTR{power/control}="auto", GOTO="autosuspend_manual_end"
+
+LABEL="autosuspend_manual_end"
+
60-persistent-v4l.rules
60-sensor.rules
60-serial.rules
+ 61-autosuspend-manual.rules
70-joystick.rules
70-mouse.rules
70-touchpad.rules
install_dir : udevrulesdir)
all_rules += gen
endforeach
+
+auto_suspend_rules = custom_target(
+ '60-autosuspend-chromeos.rules',
+ output : '60-autosuspend-chromeos.rules',
+ command : make_autosuspend_rules_py,
+ capture : true,
+ install : true,
+ install_dir: [udevrulesdir])
SendSIGKILL= MemoryLimit= CPUShares= BlockIOWeight= User= Group=
DevicePolicy= KillMode= DeviceAllow= BlockIOReadBandwidth=
BlockIOWriteBandwidth= BlockIODeviceWeight= Nice= Environment=
- KillSignal= FinalKillSignal= LimitCPU= LimitFSIZE= LimitDATA=
+ KillSignal= RestartKillSignal= FinalKillSignal= LimitCPU= LimitFSIZE= LimitDATA=
LimitSTACK= LimitCORE= LimitRSS= LimitNOFILE= LimitAS= LimitNPROC=
LimitMEMLOCK= LimitLOCKS= LimitSIGPENDING= LimitMSGQUEUE=
LimitNICE= LimitRTPRIO= LimitRTTIME= PrivateTmp= PrivateDevices=
SendSIGKILL= MemoryLimit= CPUShares= BlockIOWeight= User= Group= \
DevicePolicy= KillMode= DeviceAllow= BlockIOReadBandwidth= \
BlockIOWriteBandwidth= BlockIODeviceWeight= Nice= Environment= \
- KillSignal= FinalKillSignal= LimitCPU= LimitFSIZE= LimitDATA= \
+ KillSignal= RestartKillSignal= FinalKillSignal= LimitCPU= LimitFSIZE= LimitDATA= \
LimitSTACK= LimitCORE= LimitRSS= LimitNOFILE= LimitAS= LimitNPROC= \
LimitMEMLOCK= LimitLOCKS= LimitSIGPENDING= LimitMSGQUEUE= \
LimitNICE= LimitRTPRIO= LimitRTTIME= PrivateTmp= PrivateDevices= \
#include "main-func.h"
#include "mkdir.h"
#include "parse-util.h"
-#include "proc-cmdline.h"
+#include "reboot-util.h"
#include "string-util.h"
#include "strv.h"
#include "util.h"
#include "mkdir.h"
#include "parse-util.h"
#include "path-util.h"
-#include "proc-cmdline.h"
#include "process-util.h"
#include "set.h"
#include "special.h"
return ret;
}
-int cg_migrate(
- const char *cfrom,
- const char *pfrom,
- const char *cto,
- const char *pto,
- CGroupFlags flags) {
-
- bool done = false;
- _cleanup_set_free_ Set *s = NULL;
- int r, ret = 0;
- pid_t my_pid;
-
- assert(cfrom);
- assert(pfrom);
- assert(cto);
- assert(pto);
-
- s = set_new(NULL);
- if (!s)
- return -ENOMEM;
-
- my_pid = getpid_cached();
-
- do {
- _cleanup_fclose_ FILE *f = NULL;
- pid_t pid = 0;
- done = true;
-
- r = cg_enumerate_processes(cfrom, pfrom, &f);
- if (r < 0) {
- if (ret >= 0 && r != -ENOENT)
- return r;
-
- return ret;
- }
-
- while ((r = cg_read_pid(f, &pid)) > 0) {
-
- /* This might do weird stuff if we aren't a
- * single-threaded program. However, we
- * luckily know we are not */
- if ((flags & CGROUP_IGNORE_SELF) && pid == my_pid)
- continue;
-
- if (set_get(s, PID_TO_PTR(pid)) == PID_TO_PTR(pid))
- continue;
-
- /* Ignore kernel threads. Since they can only
- * exist in the root cgroup, we only check for
- * them there. */
- if (cfrom &&
- empty_or_root(pfrom) &&
- is_kernel_thread(pid) > 0)
- continue;
-
- r = cg_attach(cto, pto, pid);
- if (r < 0) {
- if (ret >= 0 && r != -ESRCH)
- ret = r;
- } else if (ret == 0)
- ret = 1;
-
- done = false;
-
- r = set_put(s, PID_TO_PTR(pid));
- if (r < 0) {
- if (ret >= 0)
- return r;
-
- return ret;
- }
- }
-
- if (r < 0) {
- if (ret >= 0)
- return r;
-
- return ret;
- }
- } while (!done);
-
- return ret;
-}
-
-int cg_migrate_recursive(
- const char *cfrom,
- const char *pfrom,
- const char *cto,
- const char *pto,
- CGroupFlags flags) {
-
- _cleanup_closedir_ DIR *d = NULL;
- int r, ret = 0;
- char *fn;
-
- assert(cfrom);
- assert(pfrom);
- assert(cto);
- assert(pto);
-
- ret = cg_migrate(cfrom, pfrom, cto, pto, flags);
-
- r = cg_enumerate_subgroups(cfrom, pfrom, &d);
- if (r < 0) {
- if (ret >= 0 && r != -ENOENT)
- return r;
-
- return ret;
- }
-
- while ((r = cg_read_subgroup(d, &fn)) > 0) {
- _cleanup_free_ char *p = NULL;
-
- p = path_join(empty_to_root(pfrom), fn);
- free(fn);
- if (!p)
- return -ENOMEM;
-
- r = cg_migrate_recursive(cfrom, p, cto, pto, flags);
- if (r != 0 && ret >= 0)
- ret = r;
- }
-
- if (r < 0 && ret >= 0)
- ret = r;
-
- if (flags & CGROUP_REMOVE) {
- r = cg_rmdir(cfrom, pfrom);
- if (r < 0 && ret >= 0 && !IN_SET(r, -ENOENT, -EBUSY))
- return r;
- }
-
- return ret;
-}
-
-int cg_migrate_recursive_fallback(
- const char *cfrom,
- const char *pfrom,
- const char *cto,
- const char *pto,
- CGroupFlags flags) {
-
- int r;
-
- assert(cfrom);
- assert(pfrom);
- assert(cto);
- assert(pto);
-
- r = cg_migrate_recursive(cfrom, pfrom, cto, pto, flags);
- if (r < 0) {
- char prefix[strlen(pto) + 1];
-
- /* This didn't work? Then let's try all prefixes of the destination */
-
- PATH_FOREACH_PREFIX(prefix, pto) {
- int q;
-
- q = cg_migrate_recursive(cfrom, pfrom, cto, prefix, flags);
- if (q >= 0)
- return q;
- }
- }
-
- return r;
-}
-
static const char *controller_to_dirname(const char *controller) {
const char *e;
return cg_get_path(controller, path, suffix, fs);
}
-static int trim_cb(const char *path, const struct stat *sb, int typeflag, struct FTW *ftwbuf) {
- assert(path);
- assert(sb);
- assert(ftwbuf);
-
- if (typeflag != FTW_DP)
- return 0;
-
- if (ftwbuf->level < 1)
- return 0;
-
- (void) rmdir(path);
- return 0;
-}
-
-int cg_trim(const char *controller, const char *path, bool delete_root) {
- _cleanup_free_ char *fs = NULL;
- int r = 0, q;
-
- assert(path);
-
- r = cg_get_path(controller, path, NULL, &fs);
- if (r < 0)
- return r;
-
- errno = 0;
- if (nftw(fs, trim_cb, 64, FTW_DEPTH|FTW_MOUNT|FTW_PHYS) != 0) {
- if (errno == ENOENT)
- r = 0;
- else
- r = errno_or_else(EIO);
- }
-
- if (delete_root) {
- if (rmdir(fs) < 0 && errno != ENOENT)
- return -errno;
- }
-
- q = cg_hybrid_unified();
- if (q < 0)
- return q;
- if (q > 0 && streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
- q = cg_trim(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path, delete_root);
- if (q < 0)
- log_warning_errno(q, "Failed to trim compat systemd cgroup %s: %m", path);
- }
-
- return r;
-}
-
-/* Create a cgroup in the hierarchy of controller.
- * Returns 0 if the group already existed, 1 on success, negative otherwise.
- */
-int cg_create(const char *controller, const char *path) {
- _cleanup_free_ char *fs = NULL;
- int r;
-
- r = cg_get_path_and_check(controller, path, NULL, &fs);
- if (r < 0)
- return r;
-
- r = mkdir_parents(fs, 0755);
- if (r < 0)
- return r;
-
- r = mkdir_errno_wrapper(fs, 0755);
- if (r == -EEXIST)
- return 0;
- if (r < 0)
- return r;
-
- r = cg_hybrid_unified();
- if (r < 0)
- return r;
-
- if (r > 0 && streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
- r = cg_create(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path);
- if (r < 0)
- log_warning_errno(r, "Failed to create compat systemd cgroup %s: %m", path);
- }
-
- return 1;
-}
-
-int cg_create_and_attach(const char *controller, const char *path, pid_t pid) {
- int r, q;
-
- assert(pid >= 0);
-
- r = cg_create(controller, path);
- if (r < 0)
- return r;
-
- q = cg_attach(controller, path, pid);
- if (q < 0)
- return q;
-
- /* This does not remove the cgroup on failure */
- return r;
-}
-
-int cg_attach(const char *controller, const char *path, pid_t pid) {
- _cleanup_free_ char *fs = NULL;
- char c[DECIMAL_STR_MAX(pid_t) + 2];
- int r;
-
- assert(path);
- assert(pid >= 0);
-
- r = cg_get_path_and_check(controller, path, "cgroup.procs", &fs);
- if (r < 0)
- return r;
-
- if (pid == 0)
- pid = getpid_cached();
-
- xsprintf(c, PID_FMT "\n", pid);
-
- r = write_string_file(fs, c, WRITE_STRING_FILE_DISABLE_BUFFER);
- if (r < 0)
- return r;
-
- r = cg_hybrid_unified();
- if (r < 0)
- return r;
-
- if (r > 0 && streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
- r = cg_attach(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path, pid);
- if (r < 0)
- log_warning_errno(r, "Failed to attach "PID_FMT" to compat systemd cgroup %s: %m", pid, path);
- }
-
- return 0;
-}
-
-int cg_attach_fallback(const char *controller, const char *path, pid_t pid) {
- int r;
-
- assert(controller);
- assert(path);
- assert(pid >= 0);
-
- r = cg_attach(controller, path, pid);
- if (r < 0) {
- char prefix[strlen(path) + 1];
-
- /* This didn't work? Then let's try all prefixes of
- * the destination */
-
- PATH_FOREACH_PREFIX(prefix, path) {
- int q;
-
- q = cg_attach(controller, prefix, pid);
- if (q >= 0)
- return q;
- }
- }
-
- return r;
-}
-
-int cg_set_access(
- const char *controller,
- const char *path,
- uid_t uid,
- gid_t gid) {
-
- struct Attribute {
- const char *name;
- bool fatal;
- };
-
- /* cgroup v1, aka legacy/non-unified */
- static const struct Attribute legacy_attributes[] = {
- { "cgroup.procs", true },
- { "tasks", false },
- { "cgroup.clone_children", false },
- {},
- };
-
- /* cgroup v2, aka unified */
- static const struct Attribute unified_attributes[] = {
- { "cgroup.procs", true },
- { "cgroup.subtree_control", true },
- { "cgroup.threads", false },
- {},
- };
-
- static const struct Attribute* const attributes[] = {
- [false] = legacy_attributes,
- [true] = unified_attributes,
- };
-
- _cleanup_free_ char *fs = NULL;
- const struct Attribute *i;
- int r, unified;
-
- assert(path);
-
- if (uid == UID_INVALID && gid == GID_INVALID)
- return 0;
-
- unified = cg_unified_controller(controller);
- if (unified < 0)
- return unified;
-
- /* Configure access to the cgroup itself */
- r = cg_get_path(controller, path, NULL, &fs);
- if (r < 0)
- return r;
-
- r = chmod_and_chown(fs, 0755, uid, gid);
- if (r < 0)
- return r;
-
- /* Configure access to the cgroup's attributes */
- for (i = attributes[unified]; i->name; i++) {
- fs = mfree(fs);
-
- r = cg_get_path(controller, path, i->name, &fs);
- if (r < 0)
- return r;
-
- r = chmod_and_chown(fs, 0644, uid, gid);
- if (r < 0) {
- if (i->fatal)
- return r;
-
- log_debug_errno(r, "Failed to set access on cgroup %s, ignoring: %m", fs);
- }
- }
-
- if (streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
- r = cg_hybrid_unified();
- if (r < 0)
- return r;
- if (r > 0) {
- /* Always propagate access mode from unified to legacy controller */
- r = cg_set_access(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path, uid, gid);
- if (r < 0)
- log_debug_errno(r, "Failed to set access on compatibility systemd cgroup %s, ignoring: %m", path);
- }
- }
-
- return 0;
-}
-
int cg_set_xattr(const char *controller, const char *path, const char *name, const void *value, size_t size, int flags) {
_cleanup_free_ char *fs = NULL;
int r;
done:
memcpy(ret_values, v, sizeof(char*) * n);
return 0;
-
-}
-
-int cg_create_everywhere(CGroupMask supported, CGroupMask mask, const char *path) {
- CGroupController c;
- CGroupMask done;
- bool created;
- int r;
-
- /* This one will create a cgroup in our private tree, but also
- * duplicate it in the trees specified in mask, and remove it
- * in all others.
- *
- * Returns 0 if the group already existed in the systemd hierarchy,
- * 1 on success, negative otherwise.
- */
-
- /* First create the cgroup in our own hierarchy. */
- r = cg_create(SYSTEMD_CGROUP_CONTROLLER, path);
- if (r < 0)
- return r;
- created = r;
-
- /* If we are in the unified hierarchy, we are done now */
- r = cg_all_unified();
- if (r < 0)
- return r;
- if (r > 0)
- return created;
-
- supported &= CGROUP_MASK_V1;
- mask = CGROUP_MASK_EXTEND_JOINED(mask);
- done = 0;
-
- /* Otherwise, do the same in the other hierarchies */
- for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
- CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
- const char *n;
-
- if (!FLAGS_SET(supported, bit))
- continue;
-
- if (FLAGS_SET(done, bit))
- continue;
-
- n = cgroup_controller_to_string(c);
- if (FLAGS_SET(mask, bit))
- (void) cg_create(n, path);
- else
- (void) cg_trim(n, path, true);
-
- done |= CGROUP_MASK_EXTEND_JOINED(bit);
- }
-
- return created;
-}
-
-int cg_attach_everywhere(CGroupMask supported, const char *path, pid_t pid, cg_migrate_callback_t path_callback, void *userdata) {
- CGroupController c;
- CGroupMask done;
- int r;
-
- r = cg_attach(SYSTEMD_CGROUP_CONTROLLER, path, pid);
- if (r < 0)
- return r;
-
- r = cg_all_unified();
- if (r < 0)
- return r;
- if (r > 0)
- return 0;
-
- supported &= CGROUP_MASK_V1;
- done = 0;
-
- for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
- CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
- const char *p = NULL;
-
- if (!FLAGS_SET(supported, bit))
- continue;
-
- if (FLAGS_SET(done, bit))
- continue;
-
- if (path_callback)
- p = path_callback(bit, userdata);
- if (!p)
- p = path;
-
- (void) cg_attach_fallback(cgroup_controller_to_string(c), p, pid);
- done |= CGROUP_MASK_EXTEND_JOINED(bit);
- }
-
- return 0;
-}
-
-int cg_attach_many_everywhere(CGroupMask supported, const char *path, Set* pids, cg_migrate_callback_t path_callback, void *userdata) {
- Iterator i;
- void *pidp;
- int r = 0;
-
- SET_FOREACH(pidp, pids, i) {
- pid_t pid = PTR_TO_PID(pidp);
- int q;
-
- q = cg_attach_everywhere(supported, path, pid, path_callback, userdata);
- if (q < 0 && r >= 0)
- r = q;
- }
-
- return r;
-}
-
-int cg_migrate_everywhere(CGroupMask supported, const char *from, const char *to, cg_migrate_callback_t to_callback, void *userdata) {
- CGroupController c;
- CGroupMask done;
- int r = 0, q;
-
- if (!path_equal(from, to)) {
- r = cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, from, SYSTEMD_CGROUP_CONTROLLER, to, CGROUP_REMOVE);
- if (r < 0)
- return r;
- }
-
- q = cg_all_unified();
- if (q < 0)
- return q;
- if (q > 0)
- return r;
-
- supported &= CGROUP_MASK_V1;
- done = 0;
-
- for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
- CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
- const char *p = NULL;
-
- if (!FLAGS_SET(supported, bit))
- continue;
-
- if (FLAGS_SET(done, bit))
- continue;
-
- if (to_callback)
- p = to_callback(bit, userdata);
- if (!p)
- p = to;
-
- (void) cg_migrate_recursive_fallback(SYSTEMD_CGROUP_CONTROLLER, to, cgroup_controller_to_string(c), p, 0);
- done |= CGROUP_MASK_EXTEND_JOINED(bit);
- }
-
- return r;
-}
-
-int cg_trim_everywhere(CGroupMask supported, const char *path, bool delete_root) {
- CGroupController c;
- CGroupMask done;
- int r, q;
-
- r = cg_trim(SYSTEMD_CGROUP_CONTROLLER, path, delete_root);
- if (r < 0)
- return r;
-
- q = cg_all_unified();
- if (q < 0)
- return q;
- if (q > 0)
- return r;
-
- supported &= CGROUP_MASK_V1;
- done = 0;
-
- for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
- CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
-
- if (!FLAGS_SET(supported, bit))
- continue;
-
- if (FLAGS_SET(done, bit))
- continue;
-
- (void) cg_trim(cgroup_controller_to_string(c), path, delete_root);
- done |= CGROUP_MASK_EXTEND_JOINED(bit);
- }
-
- return r;
}
int cg_mask_to_string(CGroupMask mask, char **ret) {
return 0;
}
-static thread_local CGroupUnified unified_cache = CGROUP_UNIFIED_UNKNOWN;
-
-/* The hybrid mode was initially implemented in v232 and simply mounted cgroup2 on /sys/fs/cgroup/systemd. This
- * unfortunately broke other tools (such as docker) which expected the v1 "name=systemd" hierarchy on
- * /sys/fs/cgroup/systemd. From v233 and on, the hybrid mode mountnbs v2 on /sys/fs/cgroup/unified and maintains
- * "name=systemd" hierarchy on /sys/fs/cgroup/systemd for compatibility with other tools.
+/* The hybrid mode was initially implemented in v232 and simply mounted cgroup2 on
+ * /sys/fs/cgroup/systemd. This unfortunately broke other tools (such as docker) which expected the v1
+ * "name=systemd" hierarchy on /sys/fs/cgroup/systemd. From v233 and on, the hybrid mode mounts v2 on
+ * /sys/fs/cgroup/unified and maintains "name=systemd" hierarchy on /sys/fs/cgroup/systemd for compatibility
+ * with other tools.
*
- * To keep live upgrade working, we detect and support v232 layout. When v232 layout is detected, to keep cgroup v2
- * process management but disable the compat dual layout, we return %true on
- * cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER) and %false on cg_hybrid_unified().
+ * To keep live upgrade working, we detect and support v232 layout. When v232 layout is detected, to keep
+ * cgroup v2 process management but disable the compat dual layout, we return true on
+ * cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER) and false on cg_hybrid_unified().
*/
static thread_local bool unified_systemd_v232;
-static int cg_unified_update(void) {
+int cg_unified_cached(bool flush) {
+ static thread_local CGroupUnified unified_cache = CGROUP_UNIFIED_UNKNOWN;
struct statfs fs;
* have any other trouble determining if the unified hierarchy
* is supported. */
- if (unified_cache >= CGROUP_UNIFIED_NONE)
- return 0;
+ if (flush)
+ unified_cache = CGROUP_UNIFIED_UNKNOWN;
+ else if (unified_cache >= CGROUP_UNIFIED_NONE)
+ return unified_cache;
if (statfs("/sys/fs/cgroup/", &fs) < 0)
return log_debug_errno(errno, "statfs(\"/sys/fs/cgroup/\") failed: %m");
"Unknown filesystem type %llx mounted on /sys/fs/cgroup.",
(unsigned long long)fs.f_type);
- return 0;
+ return unified_cache;
}
int cg_unified_controller(const char *controller) {
int r;
- r = cg_unified_update();
+ r = cg_unified_cached(false);
if (r < 0)
return r;
- if (unified_cache == CGROUP_UNIFIED_NONE)
+ if (r == CGROUP_UNIFIED_NONE)
return false;
- if (unified_cache >= CGROUP_UNIFIED_ALL)
+ if (r >= CGROUP_UNIFIED_ALL)
return true;
return streq_ptr(controller, SYSTEMD_CGROUP_CONTROLLER);
int cg_all_unified(void) {
int r;
- r = cg_unified_update();
+ r = cg_unified_cached(false);
if (r < 0)
return r;
- return unified_cache >= CGROUP_UNIFIED_ALL;
+ return r >= CGROUP_UNIFIED_ALL;
}
int cg_hybrid_unified(void) {
int r;
- r = cg_unified_update();
- if (r < 0)
- return r;
-
- return unified_cache == CGROUP_UNIFIED_SYSTEMD && !unified_systemd_v232;
-}
-
-int cg_unified_flush(void) {
- unified_cache = CGROUP_UNIFIED_UNKNOWN;
-
- return cg_unified_update();
-}
-
-int cg_enable_everywhere(
- CGroupMask supported,
- CGroupMask mask,
- const char *p,
- CGroupMask *ret_result_mask) {
-
- _cleanup_fclose_ FILE *f = NULL;
- _cleanup_free_ char *fs = NULL;
- CGroupController c;
- CGroupMask ret = 0;
- int r;
-
- assert(p);
-
- if (supported == 0) {
- if (ret_result_mask)
- *ret_result_mask = 0;
- return 0;
- }
-
- r = cg_all_unified();
- if (r < 0)
- return r;
- if (r == 0) {
- /* On the legacy hierarchy there's no concept of "enabling" controllers in cgroups defined. Let's claim
- * complete success right away. (If you wonder why we return the full mask here, rather than zero: the
- * caller tends to use the returned mask later on to compare if all controllers where properly joined,
- * and if not requeues realization. This use is the primary purpose of the return value, hence let's
- * minimize surprises here and reduce triggers for re-realization by always saying we fully
- * succeeded.) */
- if (ret_result_mask)
- *ret_result_mask = mask & supported & CGROUP_MASK_V2; /* If you wonder why we mask this with
- * CGROUP_MASK_V2: The 'supported' mask
- * might contain pure-V1 or BPF
- * controllers, and we never want to
- * claim that we could enable those with
- * cgroup.subtree_control */
- return 0;
- }
-
- r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, p, "cgroup.subtree_control", &fs);
- if (r < 0)
- return r;
-
- for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
- CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
- const char *n;
-
- if (!FLAGS_SET(CGROUP_MASK_V2, bit))
- continue;
-
- if (!FLAGS_SET(supported, bit))
- continue;
-
- n = cgroup_controller_to_string(c);
- {
- char s[1 + strlen(n) + 1];
-
- s[0] = FLAGS_SET(mask, bit) ? '+' : '-';
- strcpy(s + 1, n);
-
- if (!f) {
- f = fopen(fs, "we");
- if (!f)
- return log_debug_errno(errno, "Failed to open cgroup.subtree_control file of %s: %m", p);
- }
-
- r = write_string_stream(f, s, WRITE_STRING_FILE_DISABLE_BUFFER);
- if (r < 0) {
- log_debug_errno(r, "Failed to %s controller %s for %s (%s): %m",
- FLAGS_SET(mask, bit) ? "enable" : "disable", n, p, fs);
- clearerr(f);
-
- /* If we can't turn off a controller, leave it on in the reported resulting mask. This
- * happens for example when we attempt to turn off a controller up in the tree that is
- * used down in the tree. */
- if (!FLAGS_SET(mask, bit) && r == -EBUSY) /* You might wonder why we check for EBUSY
- * only here, and not follow the same logic
- * for other errors such as EINVAL or
- * EOPNOTSUPP or anything else. That's
- * because EBUSY indicates that the
- * controllers is currently enabled and
- * cannot be disabled because something down
- * the hierarchy is still using it. Any other
- * error most likely means something like "I
- * never heard of this controller" or
- * similar. In the former case it's hence
- * safe to assume the controller is still on
- * after the failed operation, while in the
- * latter case it's safer to assume the
- * controller is unknown and hence certainly
- * not enabled. */
- ret |= bit;
- } else {
- /* Otherwise, if we managed to turn on a controller, set the bit reflecting that. */
- if (FLAGS_SET(mask, bit))
- ret |= bit;
- }
- }
- }
-
- /* Let's return the precise set of controllers now enabled for the cgroup. */
- if (ret_result_mask)
- *ret_result_mask = ret;
-
- return 0;
-}
-
-bool cg_is_unified_wanted(void) {
- static thread_local int wanted = -1;
- int r;
- bool b;
- const bool is_default = DEFAULT_HIERARCHY == CGROUP_UNIFIED_ALL;
- _cleanup_free_ char *c = NULL;
-
- /* If we have a cached value, return that. */
- if (wanted >= 0)
- return wanted;
-
- /* If the hierarchy is already mounted, then follow whatever
- * was chosen for it. */
- if (cg_unified_flush() >= 0)
- return (wanted = unified_cache >= CGROUP_UNIFIED_ALL);
-
- /* If we were explicitly passed systemd.unified_cgroup_hierarchy,
- * respect that. */
- r = proc_cmdline_get_bool("systemd.unified_cgroup_hierarchy", &b);
- if (r > 0)
- return (wanted = b);
-
- /* If we passed cgroup_no_v1=all with no other instructions, it seems
- * highly unlikely that we want to use hybrid or legacy hierarchy. */
- r = proc_cmdline_get_key("cgroup_no_v1", 0, &c);
- if (r > 0 && streq_ptr(c, "all"))
- return (wanted = true);
-
- return (wanted = is_default);
-}
-
-bool cg_is_legacy_wanted(void) {
- static thread_local int wanted = -1;
-
- /* If we have a cached value, return that. */
- if (wanted >= 0)
- return wanted;
-
- /* Check if we have cgroup v2 already mounted. */
- if (cg_unified_flush() >= 0 &&
- unified_cache == CGROUP_UNIFIED_ALL)
- return (wanted = false);
-
- /* Otherwise, assume that at least partial legacy is wanted,
- * since cgroup v2 should already be mounted at this point. */
- return (wanted = true);
-}
-
-bool cg_is_hybrid_wanted(void) {
- static thread_local int wanted = -1;
- int r;
- bool b;
- const bool is_default = DEFAULT_HIERARCHY >= CGROUP_UNIFIED_SYSTEMD;
- /* We default to true if the default is "hybrid", obviously,
- * but also when the default is "unified", because if we get
- * called, it means that unified hierarchy was not mounted. */
-
- /* If we have a cached value, return that. */
- if (wanted >= 0)
- return wanted;
-
- /* If the hierarchy is already mounted, then follow whatever
- * was chosen for it. */
- if (cg_unified_flush() >= 0 &&
- unified_cache == CGROUP_UNIFIED_ALL)
- return (wanted = false);
-
- /* Otherwise, let's see what the kernel command line has to say.
- * Since checking is expensive, cache a non-error result. */
- r = proc_cmdline_get_bool("systemd.legacy_systemd_cgroup_controller", &b);
-
- /* The meaning of the kernel option is reversed wrt. to the return value
- * of this function, hence the negation. */
- return (wanted = r > 0 ? !b : is_default);
-}
-
-int cg_weight_parse(const char *s, uint64_t *ret) {
- uint64_t u;
- int r;
-
- if (isempty(s)) {
- *ret = CGROUP_WEIGHT_INVALID;
- return 0;
- }
-
- r = safe_atou64(s, &u);
+ r = cg_unified_cached(false);
if (r < 0)
return r;
- if (u < CGROUP_WEIGHT_MIN || u > CGROUP_WEIGHT_MAX)
- return -ERANGE;
-
- *ret = u;
- return 0;
+ return r == CGROUP_UNIFIED_SYSTEMD && !unified_systemd_v232;
}
const uint64_t cgroup_io_limit_defaults[_CGROUP_IO_LIMIT_TYPE_MAX] = {
DEFINE_STRING_TABLE_LOOKUP(cgroup_io_limit_type, CGroupIOLimitType);
-int cg_cpu_shares_parse(const char *s, uint64_t *ret) {
- uint64_t u;
- int r;
-
- if (isempty(s)) {
- *ret = CGROUP_CPU_SHARES_INVALID;
- return 0;
- }
-
- r = safe_atou64(s, &u);
- if (r < 0)
- return r;
-
- if (u < CGROUP_CPU_SHARES_MIN || u > CGROUP_CPU_SHARES_MAX)
- return -ERANGE;
-
- *ret = u;
- return 0;
-}
-
-int cg_blkio_weight_parse(const char *s, uint64_t *ret) {
- uint64_t u;
- int r;
-
- if (isempty(s)) {
- *ret = CGROUP_BLKIO_WEIGHT_INVALID;
- return 0;
- }
-
- r = safe_atou64(s, &u);
- if (r < 0)
- return r;
-
- if (u < CGROUP_BLKIO_WEIGHT_MIN || u > CGROUP_BLKIO_WEIGHT_MAX)
- return -ERANGE;
-
- *ret = u;
- return 0;
-}
-
bool is_cgroup_fs(const struct statfs *s) {
return is_fs_type(s, CGROUP_SUPER_MAGIC) ||
is_fs_type(s, CGROUP2_SUPER_MAGIC);
int cg_kill(const char *controller, const char *path, int sig, CGroupFlags flags, Set *s, cg_kill_log_func_t kill_log, void *userdata);
int cg_kill_recursive(const char *controller, const char *path, int sig, CGroupFlags flags, Set *s, cg_kill_log_func_t kill_log, void *userdata);
-int cg_migrate(const char *cfrom, const char *pfrom, const char *cto, const char *pto, CGroupFlags flags);
-int cg_migrate_recursive(const char *cfrom, const char *pfrom, const char *cto, const char *pto, CGroupFlags flags);
-int cg_migrate_recursive_fallback(const char *cfrom, const char *pfrom, const char *cto, const char *pto, CGroupFlags flags);
-
int cg_split_spec(const char *spec, char **controller, char **path);
int cg_mangle_path(const char *path, char **result);
int cg_pid_get_path(const char *controller, pid_t pid, char **path);
-int cg_trim(const char *controller, const char *path, bool delete_root);
-
int cg_rmdir(const char *controller, const char *path);
-int cg_create(const char *controller, const char *path);
-int cg_attach(const char *controller, const char *path, pid_t pid);
-int cg_attach_fallback(const char *controller, const char *path, pid_t pid);
-int cg_create_and_attach(const char *controller, const char *path, pid_t pid);
-
int cg_set_attribute(const char *controller, const char *path, const char *attribute, const char *value);
int cg_get_attribute(const char *controller, const char *path, const char *attribute, char **ret);
int cg_get_keyed_attribute(const char *controller, const char *path, const char *attribute, char **keys, char **values);
typedef const char* (*cg_migrate_callback_t)(CGroupMask mask, void *userdata);
-int cg_create_everywhere(CGroupMask supported, CGroupMask mask, const char *path);
-int cg_attach_everywhere(CGroupMask supported, const char *path, pid_t pid, cg_migrate_callback_t callback, void *userdata);
-int cg_attach_many_everywhere(CGroupMask supported, const char *path, Set* pids, cg_migrate_callback_t callback, void *userdata);
-int cg_migrate_everywhere(CGroupMask supported, const char *from, const char *to, cg_migrate_callback_t callback, void *userdata);
-int cg_trim_everywhere(CGroupMask supported, const char *path, bool delete_root);
-int cg_enable_everywhere(CGroupMask supported, CGroupMask mask, const char *p, CGroupMask *ret_result_mask);
-
int cg_mask_supported(CGroupMask *ret);
int cg_mask_from_string(const char *s, CGroupMask *ret);
int cg_mask_to_string(CGroupMask mask, char **ret);
int cg_all_unified(void);
int cg_hybrid_unified(void);
int cg_unified_controller(const char *controller);
-int cg_unified_flush(void);
-
-bool cg_is_unified_wanted(void);
-bool cg_is_legacy_wanted(void);
-bool cg_is_hybrid_wanted(void);
+int cg_unified_cached(bool flush);
+static inline int cg_unified(void) {
+ return cg_unified_cached(true);
+}
const char* cgroup_controller_to_string(CGroupController c) _const_;
CGroupController cgroup_controller_from_string(const char *s) _pure_;
-int cg_weight_parse(const char *s, uint64_t *ret);
-int cg_cpu_shares_parse(const char *s, uint64_t *ret);
-int cg_blkio_weight_parse(const char *s, uint64_t *ret);
-
bool is_cgroup_fs(const struct statfs *s);
bool fd_is_cgroup_fs(int fd);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <linux/fs.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "sd-id128.h"
+
+#include "alloc-util.h"
+#include "chattr-util.h"
+#include "efivars.h"
+#include "fd-util.h"
+#include "io-util.h"
+#include "macro.h"
+#include "stdio-util.h"
+#include "strv.h"
+#include "time-util.h"
+#include "utf8.h"
+
+#if ENABLE_EFI
+
+char* efi_variable_path(sd_id128_t vendor, const char *name) {
+ char *p;
+
+ if (asprintf(&p,
+ "/sys/firmware/efi/efivars/%s-" SD_ID128_UUID_FORMAT_STR,
+ name, SD_ID128_FORMAT_VAL(vendor)) < 0)
+ return NULL;
+
+ return p;
+}
+
+int efi_get_variable(
+ sd_id128_t vendor,
+ const char *name,
+ uint32_t *ret_attribute,
+ void **ret_value,
+ size_t *ret_size) {
+
+ _cleanup_close_ int fd = -1;
+ _cleanup_free_ char *p = NULL;
+ _cleanup_free_ void *buf = NULL;
+ struct stat st;
+ uint32_t a;
+ ssize_t n;
+
+ assert(name);
+
+ p = efi_variable_path(vendor, name);
+ if (!p)
+ return -ENOMEM;
+
+ if (!ret_value && !ret_size && !ret_attribute) {
+ /* If caller is not interested in anything, just check if the variable exists and is readable
+ * to us. */
+ if (access(p, R_OK) < 0)
+ return -errno;
+
+ return 0;
+ }
+
+ fd = open(p, O_RDONLY|O_NOCTTY|O_CLOEXEC);
+ if (fd < 0)
+ return -errno;
+
+ if (fstat(fd, &st) < 0)
+ return -errno;
+ if (st.st_size < 4)
+ return -ENODATA;
+ if (st.st_size > 4*1024*1024 + 4)
+ return -E2BIG;
+
+ if (ret_value || ret_attribute) {
+ n = read(fd, &a, sizeof(a));
+ if (n < 0)
+ return -errno;
+ if (n != sizeof(a))
+ return -EIO;
+ }
+
+ if (ret_value) {
+ buf = malloc(st.st_size - 4 + 2);
+ if (!buf)
+ return -ENOMEM;
+
+ n = read(fd, buf, (size_t) st.st_size - 4);
+ if (n < 0)
+ return -errno;
+ if (n != st.st_size - 4)
+ return -EIO;
+
+ /* Always NUL terminate (2 bytes, to protect UTF-16) */
+ ((char*) buf)[st.st_size - 4] = 0;
+ ((char*) buf)[st.st_size - 4 + 1] = 0;
+ }
+
+ /* Note that efivarfs interestingly doesn't require ftruncate() to update an existing EFI variable
+ * with a smaller value. */
+
+ if (ret_attribute)
+ *ret_attribute = a;
+
+ if (ret_value)
+ *ret_value = TAKE_PTR(buf);
+
+ if (ret_size)
+ *ret_size = (size_t) st.st_size - 4;
+
+ return 0;
+}
+
+int efi_get_variable_string(sd_id128_t vendor, const char *name, char **p) {
+ _cleanup_free_ void *s = NULL;
+ size_t ss = 0;
+ int r;
+ char *x;
+
+ r = efi_get_variable(vendor, name, NULL, &s, &ss);
+ if (r < 0)
+ return r;
+
+ x = utf16_to_utf8(s, ss);
+ if (!x)
+ return -ENOMEM;
+
+ *p = x;
+ return 0;
+}
+
+int efi_set_variable(
+ sd_id128_t vendor,
+ const char *name,
+ const void *value,
+ size_t size) {
+
+ struct var {
+ uint32_t attr;
+ char buf[];
+ } _packed_ * _cleanup_free_ buf = NULL;
+ _cleanup_free_ char *p = NULL;
+ _cleanup_close_ int fd = -1;
+ bool saved_flags_valid = false;
+ unsigned saved_flags;
+ int r;
+
+ assert(name);
+ assert(value || size == 0);
+
+ p = efi_variable_path(vendor, name);
+ if (!p)
+ return -ENOMEM;
+
+ /* Newer efivarfs protects variables that are not in a whitelist with FS_IMMUTABLE_FL by default, to protect
+ * them for accidental removal and modification. We are not changing these variables accidentally however,
+ * hence let's unset the bit first. */
+
+ r = chattr_path(p, 0, FS_IMMUTABLE_FL, &saved_flags);
+ if (r < 0 && r != -ENOENT)
+ log_debug_errno(r, "Failed to drop FS_IMMUTABLE_FL flag from '%s', ignoring: %m", p);
+
+ saved_flags_valid = r >= 0;
+
+ if (size == 0) {
+ if (unlink(p) < 0) {
+ r = -errno;
+ goto finish;
+ }
+
+ return 0;
+ }
+
+ fd = open(p, O_WRONLY|O_CREAT|O_NOCTTY|O_CLOEXEC, 0644);
+ if (fd < 0) {
+ r = -errno;
+ goto finish;
+ }
+
+ buf = malloc(sizeof(uint32_t) + size);
+ if (!buf) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ buf->attr = EFI_VARIABLE_NON_VOLATILE|EFI_VARIABLE_BOOTSERVICE_ACCESS|EFI_VARIABLE_RUNTIME_ACCESS;
+ memcpy(buf->buf, value, size);
+
+ r = loop_write(fd, buf, sizeof(uint32_t) + size, false);
+ if (r < 0)
+ goto finish;
+
+ r = 0;
+
+finish:
+ if (saved_flags_valid) {
+ int q;
+
+ /* Restore the original flags field, just in case */
+ if (fd < 0)
+ q = chattr_path(p, saved_flags, FS_IMMUTABLE_FL, NULL);
+ else
+ q = chattr_fd(fd, saved_flags, FS_IMMUTABLE_FL, NULL);
+ if (q < 0)
+ log_debug_errno(q, "Failed to restore FS_IMMUTABLE_FL on '%s', ignoring: %m", p);
+ }
+
+ return r;
+}
+
+int efi_set_variable_string(sd_id128_t vendor, const char *name, const char *v) {
+ _cleanup_free_ char16_t *u16 = NULL;
+
+ u16 = utf8_to_utf16(v, strlen(v));
+ if (!u16)
+ return -ENOMEM;
+
+ return efi_set_variable(vendor, name, u16, (char16_strlen(u16) + 1) * sizeof(char16_t));
+}
+
+int efi_systemd_options_variable(char **line) {
+ const char *e;
+ int r;
+
+ assert(line);
+
+ /* For testing purposes it is sometimes useful to be able to override this */
+ e = secure_getenv("SYSTEMD_EFI_OPTIONS");
+ if (e) {
+ char *m;
+
+ m = strdup(e);
+ if (!m)
+ return -ENOMEM;
+
+ *line = m;
+ return 0;
+ }
+
+ r = efi_get_variable_string(EFI_VENDOR_SYSTEMD, "SystemdOptions", line);
+ if (r == -ENOENT)
+ return -ENODATA;
+
+ return r;
+}
+#endif
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#if !ENABLE_EFI
+# include <errno.h>
+#endif
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include "sd-id128.h"
+
+#include "efi/loader-features.h"
+#include "time-util.h"
+
+#define EFI_VENDOR_LOADER SD_ID128_MAKE(4a,67,b0,82,0a,4c,41,cf,b6,c7,44,0b,29,bb,8c,4f)
+#define EFI_VENDOR_GLOBAL SD_ID128_MAKE(8b,e4,df,61,93,ca,11,d2,aa,0d,00,e0,98,03,2b,8c)
+#define EFI_VENDOR_SYSTEMD SD_ID128_MAKE(8c,f2,64,4b,4b,0b,42,8f,93,87,6d,87,60,50,dc,67)
+#define EFI_VARIABLE_NON_VOLATILE 0x0000000000000001
+#define EFI_VARIABLE_BOOTSERVICE_ACCESS 0x0000000000000002
+#define EFI_VARIABLE_RUNTIME_ACCESS 0x0000000000000004
+
+#if ENABLE_EFI
+
+char* efi_variable_path(sd_id128_t vendor, const char *name);
+int efi_get_variable(sd_id128_t vendor, const char *name, uint32_t *attribute, void **value, size_t *size);
+int efi_get_variable_string(sd_id128_t vendor, const char *name, char **p);
+int efi_set_variable(sd_id128_t vendor, const char *name, const void *value, size_t size);
+int efi_set_variable_string(sd_id128_t vendor, const char *name, const char *p);
+
+int efi_systemd_options_variable(char **line);
+
+#else
+
+static inline char* efi_variable_path(sd_id128_t vendor, const char *name) {
+ return NULL;
+}
+
+static inline int efi_get_variable(sd_id128_t vendor, const char *name, uint32_t *attribute, void **value, size_t *size) {
+ return -EOPNOTSUPP;
+}
+
+static inline int efi_get_variable_string(sd_id128_t vendor, const char *name, char **p) {
+ return -EOPNOTSUPP;
+}
+
+static inline int efi_set_variable(sd_id128_t vendor, const char *name, const void *value, size_t size) {
+ return -EOPNOTSUPP;
+}
+
+static inline int efi_set_variable_string(sd_id128_t vendor, const char *name, const char *p) {
+ return -EOPNOTSUPP;
+}
+
+static inline int efi_systemd_options_variable(char **line) {
+ return -ENODATA;
+}
+
+#endif
extern void __coverity_panic__(void);
-static inline int __coverity_check__(int condition) {
+static inline void __coverity_check__(int condition) {
+ if (!condition)
+ __coverity_panic__();
+}
+
+static inline int __coverity_check_and_return__(int condition) {
return condition;
}
-#define assert_message_se(expr, message) \
- do { \
- if (__coverity_check__(!(expr))) \
- __coverity_panic__(); \
- } while (false)
+#define assert_message_se(expr, message) __coverity_check__(!!(expr))
-#define assert_log(expr, message) __coverity_check__(!!(expr))
+#define assert_log(expr, message) __coverity_check_and_return__(!!(expr))
#else /* ! __COVERITY__ */
size_t page_size(void) _pure_;
#define PAGE_ALIGN(l) ALIGN_TO((l), page_size())
+#define PAGE_ALIGN_DOWN(l) (l & ~(page_size() - 1))
/* Normal memcpy requires src to be nonnull. We do nothing if n is 0. */
static inline void memcpy_safe(void *dst, const void *src, size_t n) {
device-nodes.h
dirent-util.c
dirent-util.h
+ efivars.c
+ efivars.h
env-file.c
env-file.h
env-util.c
return STR_IN_SET(fstype,
"afs",
"cifs",
+ "smb3",
"smbfs",
"sshfs",
"ncpfs",
return 0;
}
- last_error = -errno;
+ /* PATH entries which we don't have access to are ignored, as per tradition. */
+ if (errno != EACCES)
+ last_error = -errno;
}
return last_error;
#include <string.h>
#include "alloc-util.h"
+#include "efivars.h"
#include "extract-word.h"
#include "fileio.h"
#include "macro.h"
assert(parse_item);
+ /* We parse the EFI variable first, because later settings have higher priority. */
+
+ r = efi_systemd_options_variable(&line);
+ if (r < 0 && r != -ENODATA)
+ log_debug_errno(r, "Failed to get SystemdOptions EFI variable, ignoring: %m");
+
+ r = proc_cmdline_parse_given(line, parse_item, data, flags);
+ if (r < 0)
+ return r;
+
+ line = mfree(line);
r = proc_cmdline(&line);
if (r < 0)
return r;
return true;
}
-int proc_cmdline_get_key(const char *key, ProcCmdlineFlags flags, char **ret_value) {
- _cleanup_free_ char *line = NULL, *ret = NULL;
+static int cmdline_get_key(const char *line, const char *key, ProcCmdlineFlags flags, char **ret_value) {
+ _cleanup_free_ char *ret = NULL;
bool found = false;
const char *p;
int r;
- /* Looks for a specific key on the kernel command line. Supports three modes:
- *
- * a) The "ret_value" parameter is used. In this case a parameter beginning with the "key" string followed by
- * "=" is searched for, and the value following it is returned in "ret_value".
- *
- * b) as above, but the PROC_CMDLINE_VALUE_OPTIONAL flag is set. In this case if the key is found as a separate
- * word (i.e. not followed by "=" but instead by whitespace or the end of the command line), then this is
- * also accepted, and "value" is returned as NULL.
- *
- * c) The "ret_value" parameter is NULL. In this case a search for the exact "key" parameter is performed.
- *
- * In all three cases, > 0 is returned if the key is found, 0 if not. */
-
- if (isempty(key))
- return -EINVAL;
-
- if (FLAGS_SET(flags, PROC_CMDLINE_VALUE_OPTIONAL) && !ret_value)
- return -EINVAL;
-
- r = proc_cmdline(&line);
- if (r < 0)
- return r;
+ assert(line);
+ assert(key);
p = line;
for (;;) {
return found;
}
+int proc_cmdline_get_key(const char *key, ProcCmdlineFlags flags, char **ret_value) {
+ _cleanup_free_ char *line = NULL;
+ int r;
+
+ /* Looks for a specific key on the kernel command line and (with lower priority) the EFI variable.
+ * Supports three modes:
+ *
+ * a) The "ret_value" parameter is used. In this case a parameter beginning with the "key" string followed by
+ * "=" is searched for, and the value following it is returned in "ret_value".
+ *
+ * b) as above, but the PROC_CMDLINE_VALUE_OPTIONAL flag is set. In this case if the key is found as a separate
+ * word (i.e. not followed by "=" but instead by whitespace or the end of the command line), then this is
+ * also accepted, and "value" is returned as NULL.
+ *
+ * c) The "ret_value" parameter is NULL. In this case a search for the exact "key" parameter is performed.
+ *
+ * In all three cases, > 0 is returned if the key is found, 0 if not. */
+
+ if (isempty(key))
+ return -EINVAL;
+
+ if (FLAGS_SET(flags, PROC_CMDLINE_VALUE_OPTIONAL) && !ret_value)
+ return -EINVAL;
+
+ r = proc_cmdline(&line);
+ if (r < 0)
+ return r;
+
+ r = cmdline_get_key(line, key, flags, ret_value);
+ if (r != 0) /* Either error or true if found. */
+ return r;
+
+ line = mfree(line);
+ r = efi_systemd_options_variable(&line);
+ if (r == -ENODATA)
+ return false; /* Not found */
+ if (r < 0)
+ return r;
+
+ return cmdline_get_key(line, key, flags, ret_value);
+}
+
int proc_cmdline_get_bool(const char *key, bool *ret) {
_cleanup_free_ char *v = NULL;
int r;
return ret;
}
-
-int shall_restore_state(void) {
- bool ret;
- int r;
-
- r = proc_cmdline_get_bool("systemd.restore_state", &ret);
- if (r < 0)
- return r;
-
- return r > 0 ? ret : true;
-}
-
-static const char * const rlmap[] = {
- "emergency", SPECIAL_EMERGENCY_TARGET,
- "-b", SPECIAL_EMERGENCY_TARGET,
- "rescue", SPECIAL_RESCUE_TARGET,
- "single", SPECIAL_RESCUE_TARGET,
- "-s", SPECIAL_RESCUE_TARGET,
- "s", SPECIAL_RESCUE_TARGET,
- "S", SPECIAL_RESCUE_TARGET,
- "1", SPECIAL_RESCUE_TARGET,
- "2", SPECIAL_MULTI_USER_TARGET,
- "3", SPECIAL_MULTI_USER_TARGET,
- "4", SPECIAL_MULTI_USER_TARGET,
- "5", SPECIAL_GRAPHICAL_TARGET,
- NULL
-};
-
-static const char * const rlmap_initrd[] = {
- "emergency", SPECIAL_EMERGENCY_TARGET,
- "rescue", SPECIAL_RESCUE_TARGET,
- NULL
-};
-
-const char* runlevel_to_target(const char *word) {
- const char * const *rlmap_ptr;
- size_t i;
-
- if (!word)
- return NULL;
-
- if (in_initrd()) {
- word = startswith(word, "rd.");
- if (!word)
- return NULL;
- }
-
- rlmap_ptr = in_initrd() ? rlmap_initrd : rlmap;
-
- for (i = 0; rlmap_ptr[i]; i += 2)
- if (streq(word, rlmap_ptr[i]))
- return rlmap_ptr[i+1];
-
- return NULL;
-}
char *proc_cmdline_key_startswith(const char *s, const char *prefix);
bool proc_cmdline_key_streq(const char *x, const char *y);
-int shall_restore_state(void);
-const char* runlevel_to_target(const char *rl);
-
/* A little helper call, to be used in proc_cmdline_parse_t callbacks */
static inline bool proc_cmdline_value_missing(const char *key, const char *value) {
if (!value) {
return s ?: "n/a";
}
+static inline const char* yes_no(bool b) {
+ return b ? "yes" : "no";
+}
+
+static inline const char* true_false(bool b) {
+ return b ? "true" : "false";
+}
+
+static inline const char* one_zero(bool b) {
+ return b ? "1" : "0";
+}
+
+static inline const char* enable_disable(bool b) {
+ return b ? "enable" : "disable";
+}
+
static inline bool isempty(const char *p) {
return !p || !p[0];
}
#include "set.h"
#include "signal-util.h"
#include "stat-util.h"
+#include "static-destruct.h"
#include "string-util.h"
#include "strv.h"
#include "time-util.h"
int saved_argc = 0;
char **saved_argv = NULL;
+char **saved_env = NULL;
static int saved_in_initrd = -1;
+STATIC_DESTRUCTOR_REGISTER(saved_env, strv_freep);
+
bool kexec_loaded(void) {
_cleanup_free_ char *s = NULL;
if (r < 0)
log_debug_errno(r, "Failed to turn off coredumps, ignoring: %m");
}
+
+void save_env(void) {
+ saved_env = strv_copy(environ);
+}
#include "macro.h"
-static inline const char* yes_no(bool b) {
- return b ? "yes" : "no";
-}
-
-static inline const char* true_false(bool b) {
- return b ? "true" : "false";
-}
-
-static inline const char* one_zero(bool b) {
- return b ? "1" : "0";
-}
-
-static inline const char* enable_disable(bool b) {
- return b ? "enable" : "disable";
-}
-
extern int saved_argc;
extern char **saved_argv;
saved_argv = argv;
}
+extern char **saved_env;
+void save_env(void);
+
bool kexec_loaded(void);
int prot_from_flags(int flags) _const_;
#include <sys/stat.h>
#include <unistd.h>
-#include "efivars.h"
+#include "efi-loader.h"
#include "generator.h"
#include "log.h"
#include "mkdir.h"
#include "alloc-util.h"
#include "bootspec.h"
+#include "efi-loader.h"
#include "efivars.h"
#include "fd-util.h"
#include "fs-util.h"
#include "bootspec.h"
#include "copy.h"
#include "dirent-util.h"
+#include "efi-loader.h"
#include "efivars.h"
#include "env-util.h"
#include "escape.h"
" install Install systemd-boot to the ESP and EFI variables\n"
" update Update systemd-boot in the ESP and EFI variables\n"
" remove Remove systemd-boot from the ESP and EFI variables\n"
- " random-seed Initialize random seed in ESP and EFI variables\n"
" is-installed Test whether systemd-boot is installed in the ESP\n"
+ " random-seed Initialize random seed in ESP and EFI variables\n"
+ " system-options Query or set system options string in EFI variable\n"
"\nBoot Loader Entries Commands:\n"
" list List boot loader entries\n"
" set-default ID Set default boot loader entry\n"
return 0;
}
+static int verb_system_options(int argc, char *argv[], void *userdata) {
+ int r;
+
+ if (argc == 1) {
+ _cleanup_free_ char *line = NULL;
+
+ r = efi_systemd_options_variable(&line);
+ if (r < 0)
+ return log_error_errno(r, "Failed to query SystemdOptions EFI variable: %m");
+
+ printf("SystemdOptions: %s\n", line);
+
+ } else {
+ r = efi_set_variable_string(EFI_VENDOR_SYSTEMD, "SystemdOptions", argv[1]);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set SystemdOptions EFI variable: %m");
+ }
+
+ return 0;
+}
+
static int bootctl_main(int argc, char *argv[]) {
static const Verb verbs[] = {
- { "help", VERB_ANY, VERB_ANY, 0, help },
- { "status", VERB_ANY, 1, VERB_DEFAULT, verb_status },
- { "install", VERB_ANY, 1, 0, verb_install },
- { "update", VERB_ANY, 1, 0, verb_install },
- { "remove", VERB_ANY, 1, 0, verb_remove },
- { "random-seed", VERB_ANY, 1, 0, verb_random_seed },
- { "is-installed", VERB_ANY, 1, 0, verb_is_installed },
- { "list", VERB_ANY, 1, 0, verb_list },
- { "set-default", 2, 2, 0, verb_set_default },
- { "set-oneshot", 2, 2, 0, verb_set_default },
+ { "help", VERB_ANY, VERB_ANY, 0, help },
+ { "status", VERB_ANY, 1, VERB_DEFAULT, verb_status },
+ { "install", VERB_ANY, 1, 0, verb_install },
+ { "update", VERB_ANY, 1, 0, verb_install },
+ { "remove", VERB_ANY, 1, 0, verb_remove },
+ { "is-installed", VERB_ANY, 1, 0, verb_is_installed },
+ { "list", VERB_ANY, 1, 0, verb_list },
+ { "set-default", 2, 2, 0, verb_set_default },
+ { "set-oneshot", 2, 2, 0, verb_set_default },
+ { "random-seed", VERB_ANY, 1, 0, verb_random_seed },
+ { "system-options", VERB_ANY, 2, 0, verb_system_options },
{}
};
if (r < 0)
goto fail;
- (void) mkdir_p_label(a->where, 0555);
+ (void) mkdir_p_label(a->where, a->directory_mode);
unit_warn_if_dir_nonempty(UNIT(a), a->where);
#include "bpf-firewall.h"
#include "btrfs-util.h"
#include "bus-error.h"
+#include "cgroup-setup.h"
#include "cgroup-util.h"
#include "cgroup.h"
#include "fd-util.h"
cpu_set_reset(&c->cpuset_mems);
}
-void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) {
+static int unit_get_kernel_memory_limit(Unit *u, const char *file, uint64_t *ret) {
+ _cleanup_free_ char *raw_kval = NULL;
+ uint64_t kval;
+ int r;
+
+ assert(u);
+
+ if (!u->cgroup_realized)
+ return -EOWNERDEAD;
+
+ r = cg_get_attribute("memory", u->cgroup_path, file, &raw_kval);
+ if (r < 0)
+ return r;
+
+ if (streq(raw_kval, "max")) {
+ *ret = CGROUP_LIMIT_MAX;
+ return 0;
+ }
+
+ r = safe_atou64(raw_kval, &kval);
+ if (r < 0)
+ return r;
+
+ *ret = kval;
+
+ return 0;
+}
+
+static int unit_compare_memory_limit(Unit *u, const char *property_name, uint64_t *ret_unit_value, uint64_t *ret_kernel_value) {
+ CGroupContext *c;
+ CGroupMask m;
+ const char *file;
+ uint64_t unit_value;
+ int r;
+
+ /* Compare kernel memcg configuration against our internal systemd state. Unsupported (and will
+ * return -ENODATA) on cgroup v1.
+ *
+ * Returns:
+ *
+ * <0: On error.
+ * 0: If the kernel memory setting doesn't match our configuration.
+ * >0: If the kernel memory setting matches our configuration.
+ *
+ * The following values are only guaranteed to be populated on return >=0:
+ *
+ * - ret_unit_value will contain our internal expected value for the unit, page-aligned.
+ * - ret_kernel_value will contain the actual value presented by the kernel. */
+
+ assert(u);
+
+ r = cg_all_unified();
+ if (r < 0)
+ return log_debug_errno(r, "Failed to determine cgroup hierarchy version: %m");
+
+ /* Unsupported on v1.
+ *
+ * We don't return ENOENT, since that could actually mask a genuine problem where somebody else has
+ * silently masked the controller. */
+ if (r == 0)
+ return -ENODATA;
+
+ /* The root slice doesn't have any controller files, so we can't compare anything. */
+ if (unit_has_name(u, SPECIAL_ROOT_SLICE))
+ return -ENODATA;
+
+ /* It's possible to have MemoryFoo set without systemd wanting to have the memory controller enabled,
+ * for example, in the case of DisableControllers= or cgroup_disable on the kernel command line. To
+ * avoid specious errors in these scenarios, check that we even expect the memory controller to be
+ * enabled at all. */
+ m = unit_get_target_mask(u);
+ if (!FLAGS_SET(m, CGROUP_MASK_MEMORY))
+ return -ENODATA;
+
+ c = unit_get_cgroup_context(u);
+ assert(c);
+
+ if (streq(property_name, "MemoryLow")) {
+ unit_value = unit_get_ancestor_memory_low(u);
+ file = "memory.low";
+ } else if (streq(property_name, "MemoryMin")) {
+ unit_value = unit_get_ancestor_memory_min(u);
+ file = "memory.min";
+ } else if (streq(property_name, "MemoryHigh")) {
+ unit_value = c->memory_high;
+ file = "memory.high";
+ } else if (streq(property_name, "MemoryMax")) {
+ unit_value = c->memory_max;
+ file = "memory.max";
+ } else if (streq(property_name, "MemorySwapMax")) {
+ unit_value = c->memory_swap_max;
+ file = "memory.swap.max";
+ } else
+ return -EINVAL;
+
+ r = unit_get_kernel_memory_limit(u, file, ret_kernel_value);
+ if (r < 0)
+ return log_unit_debug_errno(u, r, "Failed to parse %s: %m", file);
+
+ /* It's intended (soon) in a future kernel to not expose cgroup memory limits rounded to page
+ * boundaries, but instead separate the user-exposed limit, which is whatever userspace told us, from
+ * our internal page-counting. To support those future kernels, just check the value itself first
+ * without any page-alignment. */
+ if (*ret_kernel_value == unit_value) {
+ *ret_unit_value = unit_value;
+ return 1;
+ }
+
+ /* The current kernel behaviour, by comparison, is that even if you write a particular number of
+ * bytes into a cgroup memory file, it always returns that number page-aligned down (since the kernel
+ * internally stores cgroup limits in pages). As such, so long as it aligns properly, everything is
+ * cricket. */
+ if (unit_value != CGROUP_LIMIT_MAX)
+ unit_value = PAGE_ALIGN_DOWN(unit_value);
+
+ *ret_unit_value = unit_value;
+
+ return *ret_kernel_value == *ret_unit_value;
+}
+
+#define FORMAT_CGROUP_DIFF_MAX 128
+
+static char *format_cgroup_memory_limit_comparison(char *buf, size_t l, Unit *u, const char *property_name) {
+ uint64_t kval, sval;
+ int r;
+
+ assert(u);
+ assert(buf);
+ assert(l > 0);
+
+ r = unit_compare_memory_limit(u, property_name, &sval, &kval);
+
+ /* memory.swap.max is special in that it relies on CONFIG_MEMCG_SWAP (and the default swapaccount=1).
+ * In the absence of reliably being able to detect whether memcg swap support is available or not,
+ * only complain if the error is not ENOENT. */
+ if (r > 0 || IN_SET(r, -ENODATA, -EOWNERDEAD) ||
+ (r == -ENOENT && streq(property_name, "MemorySwapMax"))) {
+ buf[0] = 0;
+ return buf;
+ }
+
+ if (r < 0) {
+ snprintf(buf, l, " (error getting kernel value: %s)", strerror_safe(r));
+ return buf;
+ }
+
+ snprintf(buf, l, " (different value in kernel: %" PRIu64 ")", kval);
+
+ return buf;
+}
+
+void cgroup_context_dump(Unit *u, FILE* f, const char *prefix) {
_cleanup_free_ char *disable_controllers_str = NULL;
_cleanup_free_ char *cpuset_cpus = NULL;
_cleanup_free_ char *cpuset_mems = NULL;
CGroupBlockIODeviceBandwidth *b;
CGroupBlockIODeviceWeight *w;
CGroupDeviceAllow *a;
+ CGroupContext *c;
IPAddressAccessItem *iaai;
char **path;
- char u[FORMAT_TIMESPAN_MAX];
+ char q[FORMAT_TIMESPAN_MAX];
char v[FORMAT_TIMESPAN_MAX];
- assert(c);
+ char cda[FORMAT_CGROUP_DIFF_MAX];
+ char cdb[FORMAT_CGROUP_DIFF_MAX];
+ char cdc[FORMAT_CGROUP_DIFF_MAX];
+ char cdd[FORMAT_CGROUP_DIFF_MAX];
+ char cde[FORMAT_CGROUP_DIFF_MAX];
+
+ assert(u);
assert(f);
+ c = unit_get_cgroup_context(u);
+ assert(c);
+
prefix = strempty(prefix);
(void) cg_mask_to_string(c->disable_controllers, &disable_controllers_str);
cpuset_mems = cpu_set_to_range_string(&c->cpuset_mems);
fprintf(f,
- "%sCPUAccounting=%s\n"
- "%sIOAccounting=%s\n"
- "%sBlockIOAccounting=%s\n"
- "%sMemoryAccounting=%s\n"
- "%sTasksAccounting=%s\n"
- "%sIPAccounting=%s\n"
- "%sCPUWeight=%" PRIu64 "\n"
- "%sStartupCPUWeight=%" PRIu64 "\n"
- "%sCPUShares=%" PRIu64 "\n"
- "%sStartupCPUShares=%" PRIu64 "\n"
- "%sCPUQuotaPerSecSec=%s\n"
- "%sCPUQuotaPeriodSec=%s\n"
- "%sAllowedCPUs=%s\n"
- "%sAllowedMemoryNodes=%s\n"
- "%sIOWeight=%" PRIu64 "\n"
- "%sStartupIOWeight=%" PRIu64 "\n"
- "%sBlockIOWeight=%" PRIu64 "\n"
- "%sStartupBlockIOWeight=%" PRIu64 "\n"
- "%sDefaultMemoryMin=%" PRIu64 "\n"
- "%sDefaultMemoryLow=%" PRIu64 "\n"
- "%sMemoryMin=%" PRIu64 "\n"
- "%sMemoryLow=%" PRIu64 "\n"
- "%sMemoryHigh=%" PRIu64 "\n"
- "%sMemoryMax=%" PRIu64 "\n"
- "%sMemorySwapMax=%" PRIu64 "\n"
- "%sMemoryLimit=%" PRIu64 "\n"
- "%sTasksMax=%" PRIu64 "\n"
- "%sDevicePolicy=%s\n"
- "%sDisableControllers=%s\n"
- "%sDelegate=%s\n",
+ "%sCPUAccounting: %s\n"
+ "%sIOAccounting: %s\n"
+ "%sBlockIOAccounting: %s\n"
+ "%sMemoryAccounting: %s\n"
+ "%sTasksAccounting: %s\n"
+ "%sIPAccounting: %s\n"
+ "%sCPUWeight: %" PRIu64 "\n"
+ "%sStartupCPUWeight: %" PRIu64 "\n"
+ "%sCPUShares: %" PRIu64 "\n"
+ "%sStartupCPUShares: %" PRIu64 "\n"
+ "%sCPUQuotaPerSecSec: %s\n"
+ "%sCPUQuotaPeriodSec: %s\n"
+ "%sAllowedCPUs: %s\n"
+ "%sAllowedMemoryNodes: %s\n"
+ "%sIOWeight: %" PRIu64 "\n"
+ "%sStartupIOWeight: %" PRIu64 "\n"
+ "%sBlockIOWeight: %" PRIu64 "\n"
+ "%sStartupBlockIOWeight: %" PRIu64 "\n"
+ "%sDefaultMemoryMin: %" PRIu64 "\n"
+ "%sDefaultMemoryLow: %" PRIu64 "\n"
+ "%sMemoryMin: %" PRIu64 "%s\n"
+ "%sMemoryLow: %" PRIu64 "%s\n"
+ "%sMemoryHigh: %" PRIu64 "%s\n"
+ "%sMemoryMax: %" PRIu64 "%s\n"
+ "%sMemorySwapMax: %" PRIu64 "%s\n"
+ "%sMemoryLimit: %" PRIu64 "\n"
+ "%sTasksMax: %" PRIu64 "\n"
+ "%sDevicePolicy: %s\n"
+ "%sDisableControllers: %s\n"
+ "%sDelegate: %s\n",
prefix, yes_no(c->cpu_accounting),
prefix, yes_no(c->io_accounting),
prefix, yes_no(c->blockio_accounting),
prefix, c->startup_cpu_weight,
prefix, c->cpu_shares,
prefix, c->startup_cpu_shares,
- prefix, format_timespan(u, sizeof(u), c->cpu_quota_per_sec_usec, 1),
+ prefix, format_timespan(q, sizeof(q), c->cpu_quota_per_sec_usec, 1),
prefix, format_timespan(v, sizeof(v), c->cpu_quota_period_usec, 1),
prefix, cpuset_cpus,
prefix, cpuset_mems,
prefix, c->startup_blockio_weight,
prefix, c->default_memory_min,
prefix, c->default_memory_low,
- prefix, c->memory_min,
- prefix, c->memory_low,
- prefix, c->memory_high,
- prefix, c->memory_max,
- prefix, c->memory_swap_max,
+ prefix, c->memory_min, format_cgroup_memory_limit_comparison(cda, sizeof(cda), u, "MemoryMin"),
+ prefix, c->memory_low, format_cgroup_memory_limit_comparison(cdb, sizeof(cdb), u, "MemoryLow"),
+ prefix, c->memory_high, format_cgroup_memory_limit_comparison(cdc, sizeof(cdc), u, "MemoryHigh"),
+ prefix, c->memory_max, format_cgroup_memory_limit_comparison(cdd, sizeof(cdd), u, "MemoryMax"),
+ prefix, c->memory_swap_max, format_cgroup_memory_limit_comparison(cde, sizeof(cde), u, "MemorySwapMax"),
prefix, c->memory_limit,
prefix, c->tasks_max,
prefix, cgroup_device_policy_to_string(c->device_policy),
(void) cg_mask_to_string(c->delegate_controllers, &t);
- fprintf(f, "%sDelegateControllers=%s\n",
+ fprintf(f, "%sDelegateControllers: %s\n",
prefix,
strempty(t));
}
LIST_FOREACH(device_allow, a, c->device_allow)
fprintf(f,
- "%sDeviceAllow=%s %s%s%s\n",
+ "%sDeviceAllow: %s %s%s%s\n",
prefix,
a->path,
a->r ? "r" : "", a->w ? "w" : "", a->m ? "m" : "");
LIST_FOREACH(device_weights, iw, c->io_device_weights)
fprintf(f,
- "%sIODeviceWeight=%s %" PRIu64 "\n",
+ "%sIODeviceWeight: %s %" PRIu64 "\n",
prefix,
iw->path,
iw->weight);
LIST_FOREACH(device_latencies, l, c->io_device_latencies)
fprintf(f,
- "%sIODeviceLatencyTargetSec=%s %s\n",
+ "%sIODeviceLatencyTargetSec: %s %s\n",
prefix,
l->path,
- format_timespan(u, sizeof(u), l->target_usec, 1));
+ format_timespan(q, sizeof(q), l->target_usec, 1));
LIST_FOREACH(device_limits, il, c->io_device_limits) {
char buf[FORMAT_BYTES_MAX];
for (type = 0; type < _CGROUP_IO_LIMIT_TYPE_MAX; type++)
if (il->limits[type] != cgroup_io_limit_defaults[type])
fprintf(f,
- "%s%s=%s %s\n",
+ "%s%s: %s %s\n",
prefix,
cgroup_io_limit_type_to_string(type),
il->path,
LIST_FOREACH(device_weights, w, c->blockio_device_weights)
fprintf(f,
- "%sBlockIODeviceWeight=%s %" PRIu64,
+ "%sBlockIODeviceWeight: %s %" PRIu64,
prefix,
w->path,
w->weight);
if (b->rbps != CGROUP_LIMIT_MAX)
fprintf(f,
- "%sBlockIOReadBandwidth=%s %s\n",
+ "%sBlockIOReadBandwidth: %s %s\n",
prefix,
b->path,
format_bytes(buf, sizeof(buf), b->rbps));
if (b->wbps != CGROUP_LIMIT_MAX)
fprintf(f,
- "%sBlockIOWriteBandwidth=%s %s\n",
+ "%sBlockIOWriteBandwidth: %s %s\n",
prefix,
b->path,
format_bytes(buf, sizeof(buf), b->wbps));
_cleanup_free_ char *k = NULL;
(void) in_addr_to_string(iaai->family, &iaai->address, &k);
- fprintf(f, "%sIPAddressAllow=%s/%u\n", prefix, strnull(k), iaai->prefixlen);
+ fprintf(f, "%sIPAddressAllow: %s/%u\n", prefix, strnull(k), iaai->prefixlen);
}
LIST_FOREACH(items, iaai, c->ip_address_deny) {
_cleanup_free_ char *k = NULL;
(void) in_addr_to_string(iaai->family, &iaai->address, &k);
- fprintf(f, "%sIPAddressDeny=%s/%u\n", prefix, strnull(k), iaai->prefixlen);
+ fprintf(f, "%sIPAddressDeny: %s/%u\n", prefix, strnull(k), iaai->prefixlen);
}
STRV_FOREACH(path, c->ip_filters_ingress)
- fprintf(f, "%sIPIngressFilterPath=%s\n", prefix, *path);
+ fprintf(f, "%sIPIngressFilterPath: %s\n", prefix, *path);
STRV_FOREACH(path, c->ip_filters_egress)
- fprintf(f, "%sIPEgressFilterPath=%s\n", prefix, *path);
+ fprintf(f, "%sIPEgressFilterPath: %s\n", prefix, *path);
}
int cgroup_add_device_allow(CGroupContext *c, const char *dev, const char *mode) {
c = unit_get_cgroup_context(u);
assert(c);
- return c->memory_min > 0 || unit_get_ancestor_memory_low(u) > 0 ||
+ return unit_get_ancestor_memory_min(u) > 0 || unit_get_ancestor_memory_low(u) > 0 ||
c->memory_high != CGROUP_LIMIT_MAX || c->memory_max != CGROUP_LIMIT_MAX ||
c->memory_swap_max != CGROUP_LIMIT_MAX;
}
log_cgroup_compat(u, "Applying MemoryLimit=%" PRIu64 " as MemoryMax=", max);
}
- cgroup_apply_unified_memory_limit(u, "memory.min", c->memory_min);
+ cgroup_apply_unified_memory_limit(u, "memory.min", unit_get_ancestor_memory_min(u));
cgroup_apply_unified_memory_limit(u, "memory.low", unit_get_ancestor_memory_low(u));
cgroup_apply_unified_memory_limit(u, "memory.high", c->memory_high);
cgroup_apply_unified_memory_limit(u, "memory.max", max);
if (r < 0)
return log_error_errno(r, "Cannot find cgroup mount point: %m");
- r = cg_unified_flush();
+ r = cg_unified();
if (r < 0)
return log_error_errno(r, "Couldn't determine if we are running in the unified hierarchy: %m");
void cgroup_context_init(CGroupContext *c);
void cgroup_context_done(CGroupContext *c);
-void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix);
+void cgroup_context_dump(Unit *u, FILE* f, const char *prefix);
void cgroup_context_free_device_allow(CGroupContext *c, CGroupDeviceAllow *a);
void cgroup_context_free_io_device_weight(CGroupContext *c, CGroupIODeviceWeight *w);
static bool warned = false;
log_full(warned ? LOG_DEBUG : LOG_WARNING,
- "Transient unit %s configures an IP firewall with BPF, but the local system does not support BPF/cgroup firewalling with mulitiple filters.\n"
+ "Transient unit %s configures an IP firewall with BPF, but the local system does not support BPF/cgroup firewalling with multiple filters.\n"
"Starting this unit will fail! (This warning is only shown for the first started transient unit using IP firewalling.)", u->id);
warned = true;
}
if (streq(name, "MemoryAccounting"))
return bus_cgroup_set_boolean(u, name, &c->memory_accounting, CGROUP_MASK_MEMORY, message, flags, error);
- if (streq(name, "MemoryMin"))
- return bus_cgroup_set_memory_protection(u, name, &c->memory_min, message, flags, error);
+ if (streq(name, "MemoryMin")) {
+ r = bus_cgroup_set_memory_protection(u, name, &c->memory_min, message, flags, error);
+ if (r > 0)
+ c->memory_min_set = true;
+ return r;
+ }
- if (streq(name, "MemoryLow"))
- return bus_cgroup_set_memory_protection(u, name, &c->memory_low, message, flags, error);
+ if (streq(name, "MemoryLow")) {
+ r = bus_cgroup_set_memory_protection(u, name, &c->memory_low, message, flags, error);
+ if (r > 0)
+ c->memory_low_set = true;
+ return r;
+ }
- if (streq(name, "DefaultMemoryMin"))
- return bus_cgroup_set_memory_protection(u, name, &c->default_memory_min, message, flags, error);
+ if (streq(name, "DefaultMemoryMin")) {
+ r = bus_cgroup_set_memory_protection(u, name, &c->default_memory_min, message, flags, error);
+ if (r > 0)
+ c->default_memory_min_set = true;
+ return r;
+ }
- if (streq(name, "DefaultMemoryLow"))
- return bus_cgroup_set_memory_protection(u, name, &c->default_memory_low, message, flags, error);
+ if (streq(name, "DefaultMemoryLow")) {
+ r = bus_cgroup_set_memory_protection(u, name, &c->default_memory_low, message, flags, error);
+ if (r > 0)
+ c->default_memory_low_set = true;
+ return r;
+ }
if (streq(name, "MemoryHigh"))
return bus_cgroup_set_memory(u, name, &c->memory_high, message, flags, error);
if (streq(name, "MemoryLimit"))
return bus_cgroup_set_memory(u, name, &c->memory_limit, message, flags, error);
- if (streq(name, "MemoryMinScale"))
- return bus_cgroup_set_memory_protection_scale(u, name, &c->memory_min, message, flags, error);
+ if (streq(name, "MemoryMinScale")) {
+ r = bus_cgroup_set_memory_protection_scale(u, name, &c->memory_min, message, flags, error);
+ if (r > 0)
+ c->memory_min_set = true;
+ return r;
+ }
- if (streq(name, "MemoryLowScale"))
- return bus_cgroup_set_memory_protection_scale(u, name, &c->memory_low, message, flags, error);
+ if (streq(name, "MemoryLowScale")) {
+ r = bus_cgroup_set_memory_protection_scale(u, name, &c->memory_low, message, flags, error);
+ if (r > 0)
+ c->memory_low_set = true;
+ return r;
+ }
- if (streq(name, "DefaultMemoryMinScale"))
- return bus_cgroup_set_memory_protection_scale(u, name, &c->default_memory_min, message, flags, error);
+ if (streq(name, "DefaultMemoryMinScale")) {
+ r = bus_cgroup_set_memory_protection_scale(u, name, &c->default_memory_min, message, flags, error);
+ if (r > 0)
+ c->default_memory_min_set = true;
+ return r;
+ }
- if (streq(name, "DefaultMemoryLowScale"))
- return bus_cgroup_set_memory_protection_scale(u, name, &c->default_memory_low, message, flags, error);
+ if (streq(name, "DefaultMemoryLowScale")) {
+ r = bus_cgroup_set_memory_protection_scale(u, name, &c->default_memory_low, message, flags, error);
+ if (r > 0)
+ c->default_memory_low_set = true;
+ return r;
+ }
if (streq(name, "MemoryHighScale"))
return bus_cgroup_set_memory_scale(u, name, &c->memory_high, message, flags, error);
#include "sd-bus.h"
#include "sd-bus-vtable.h"
-#include "job.h"
+#include "unit.h"
extern const sd_bus_vtable bus_job_vtable[];
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_kill_mode, kill_mode, KillMode);
+static int property_get_restart_kill_signal(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+ KillContext *c = userdata;
+ int s;
+
+ assert(c);
+
+ s = restart_kill_signal(c);
+ return sd_bus_message_append_basic(reply, 'i', &s);
+}
+
const sd_bus_vtable bus_kill_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_PROPERTY("KillMode", "s", property_get_kill_mode, offsetof(KillContext, kill_mode), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("KillSignal", "i", bus_property_get_int, offsetof(KillContext, kill_signal), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("RestartKillSignal", "i", property_get_restart_kill_signal, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("FinalKillSignal", "i", bus_property_get_int, offsetof(KillContext, final_kill_signal), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("SendSIGKILL", "b", bus_property_get_bool, offsetof(KillContext, send_sigkill), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("SendSIGHUP", "b", bus_property_get_bool, offsetof(KillContext, send_sighup), SD_BUS_VTABLE_PROPERTY_CONST),
static BUS_DEFINE_SET_TRANSIENT_PARSE(kill_mode, KillMode, kill_mode_from_string);
static BUS_DEFINE_SET_TRANSIENT_TO_STRING(kill_signal, "i", int32_t, int, "%" PRIi32, signal_to_string_with_check);
+static BUS_DEFINE_SET_TRANSIENT_TO_STRING(restart_kill_signal, "i", int32_t, int, "%" PRIi32, signal_to_string_with_check);
static BUS_DEFINE_SET_TRANSIENT_TO_STRING(final_kill_signal, "i", int32_t, int, "%" PRIi32, signal_to_string_with_check);
static BUS_DEFINE_SET_TRANSIENT_TO_STRING(watchdog_signal, "i", int32_t, int, "%" PRIi32, signal_to_string_with_check);
if (streq(name, "KillSignal"))
return bus_set_transient_kill_signal(u, name, &c->kill_signal, message, flags, error);
+ if (streq(name, "RestartKillSignal"))
+ return bus_set_transient_restart_kill_signal(u, name, &c->restart_kill_signal, message, flags, error);
+
if (streq(name, "FinalKillSignal"))
return bus_set_transient_final_kill_signal(u, name, &c->final_kill_signal, message, flags, error);
#include "sd-bus.h"
#include "sd-bus-vtable.h"
-#include "job.h"
#include "unit.h"
extern const sd_bus_vtable bus_unit_vtable[];
#include "cap-list.h"
#include "capability-util.h"
#include "chown-recursive.h"
+#include "cgroup-setup.h"
#include "cpu-set-util.h"
#include "def.h"
#include "env-file.h"
n = write(idle_pipe[3], "x", 1);
if (n > 0)
/* Wait for systemd to react to the signal above. */
- fd_wait_for_event(idle_pipe[0], POLLHUP, IDLE_TIMEOUT2_USEC);
+ (void) fd_wait_for_event(idle_pipe[0], POLLHUP, IDLE_TIMEOUT2_USEC);
}
idle_pipe[0] = safe_close(idle_pipe[0]);
assert(c);
c->kill_signal = SIGTERM;
+ /* restart_kill_signal is unset by default and we fall back to kill_signal */
c->final_kill_signal = SIGKILL;
c->send_sigkill = true;
c->send_sighup = false;
fprintf(f,
"%sKillMode: %s\n"
"%sKillSignal: SIG%s\n"
+ "%sRestartKillSignal: SIG%s\n"
"%sFinalKillSignal: SIG%s\n"
"%sSendSIGKILL: %s\n"
- "%sSendSIGHUP: %s\n",
+ "%sSendSIGHUP: %s\n",
prefix, kill_mode_to_string(c->kill_mode),
prefix, signal_to_string(c->kill_signal),
+ prefix, signal_to_string(restart_kill_signal(c)),
prefix, signal_to_string(c->final_kill_signal),
prefix, yes_no(c->send_sigkill),
prefix, yes_no(c->send_sighup));
static const char* const kill_mode_table[_KILL_MODE_MAX] = {
[KILL_CONTROL_GROUP] = "control-group",
- [KILL_PROCESS] = "process",
- [KILL_MIXED] = "mixed",
- [KILL_NONE] = "none"
+ [KILL_PROCESS] = "process",
+ [KILL_MIXED] = "mixed",
+ [KILL_NONE] = "none",
};
DEFINE_STRING_TABLE_LOOKUP(kill_mode, KillMode);
static const char* const kill_who_table[_KILL_WHO_MAX] = {
- [KILL_MAIN] = "main",
- [KILL_CONTROL] = "control",
- [KILL_ALL] = "all",
- [KILL_MAIN_FAIL] = "main-fail",
+ [KILL_MAIN] = "main",
+ [KILL_CONTROL] = "control",
+ [KILL_ALL] = "all",
+ [KILL_MAIN_FAIL] = "main-fail",
[KILL_CONTROL_FAIL] = "control-fail",
- [KILL_ALL_FAIL] = "all-fail"
+ [KILL_ALL_FAIL] = "all-fail",
};
DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho);
struct KillContext {
KillMode kill_mode;
int kill_signal;
+ int restart_kill_signal;
int final_kill_signal;
int watchdog_signal;
bool send_sigkill;
const char *kill_who_to_string(KillWho k) _const_;
KillWho kill_who_from_string(const char *s) _pure_;
+
+static inline int restart_kill_signal(const KillContext *c) {
+ if (c->restart_kill_signal != 0)
+ return c->restart_kill_signal;
+ return c->kill_signal;
+}
$1.SendSIGHUP, config_parse_bool, 0, offsetof($1, kill_context.send_sighup)
$1.KillMode, config_parse_kill_mode, 0, offsetof($1, kill_context.kill_mode)
$1.KillSignal, config_parse_signal, 0, offsetof($1, kill_context.kill_signal)
+$1.RestartKillSignal, config_parse_signal, 0, offsetof($1, kill_context.restart_kill_signal)
$1.FinalKillSignal, config_parse_signal, 0, offsetof($1, kill_context.final_kill_signal)
$1.WatchdogSignal, config_parse_signal, 0, offsetof($1, kill_context.watchdog_signal)'
)m4_dnl
#include "bus-util.h"
#include "cap-list.h"
#include "capability-util.h"
-#include "cgroup.h"
+#include "cgroup-setup.h"
#include "conf-parser.h"
#include "cpu-set-util.h"
#include "env-util.h"
CONFIG_PARSER_PROTOTYPE(config_parse_exec_cpu_affinity);
CONFIG_PARSER_PROTOTYPE(config_parse_exec_secure_bits);
CONFIG_PARSER_PROTOTYPE(config_parse_capability_set);
-CONFIG_PARSER_PROTOTYPE(config_parse_kill_signal);
-CONFIG_PARSER_PROTOTYPE(config_parse_final_kill_signal);
CONFIG_PARSER_PROTOTYPE(config_parse_exec_mount_flags);
CONFIG_PARSER_PROTOTYPE(config_parse_timer);
CONFIG_PARSER_PROTOTYPE(config_parse_trigger_unit);
if (switch_root_init) {
args[0] = switch_root_init;
- (void) execv(args[0], (char* const*) args);
+ (void) execve(args[0], (char* const*) args, saved_env);
log_warning_errno(errno, "Failed to execute configured init, trying fallback: %m");
}
args[0] = "/bin/sh";
args[1] = NULL;
- (void) execv(args[0], (char* const*) args);
+ (void) execve(args[0], (char* const*) args, saved_env);
log_error_errno(errno, "Failed to execute /bin/sh, giving up: %m");
} else
log_warning_errno(r, "Failed to execute /sbin/init, giving up: %m");
arg_default_blockio_accounting = false;
arg_default_memory_accounting = MEMORY_ACCOUNTING_DEFAULT;
arg_default_tasks_accounting = true;
- arg_default_tasks_max = UINT64_MAX;
+ arg_default_tasks_max = system_tasks_max_scale(DEFAULT_TASKS_MAX_PERCENTAGE, 100U);
arg_machine_id = (sd_id128_t) {};
arg_cad_burst_action = EMERGENCY_ACTION_REBOOT_FORCE;
arg_default_oom_policy = OOM_STOP;
assert(saved_rlimit_nofile);
assert(saved_rlimit_memlock);
- arg_default_tasks_max = system_tasks_max_scale(DEFAULT_TASKS_MAX_PERCENTAGE, 100U);
-
/* Assign configuration defaults */
reset_arguments();
/* Save the original command line */
save_argc_argv(argc, argv);
+ /* Save the original environment as we might need to restore it if we're requested to
+ * execute another system manager later. */
+ save_env();
+
/* Make sure that if the user says "syslog" we actually log to the journal. */
log_set_upgrade_syslog_to_journal(true);
#include "bus-util.h"
#include "cgroup-util.h"
#include "conf-files.h"
+#include "cgroup-setup.h"
#include "dev-setup.h"
-#include "efivars.h"
+#include "dirent-util.h"
+#include "efi-loader.h"
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
exec_context_dump(&m->exec_context, f, prefix);
kill_context_dump(&m->kill_context, f, prefix);
- cgroup_context_dump(&m->cgroup_context, f, prefix);
+ cgroup_context_dump(UNIT(m), f, prefix);
}
static int mount_spawn(Mount *m, ExecCommand *c, pid_t *_pid) {
switch (state) {
case MOUNT_REMOUNTING_SIGTERM:
+ return KILL_RESTART;
+
case MOUNT_UNMOUNTING_SIGTERM:
return KILL_TERMINATE;
prefix, scope_state_to_string(s->state),
prefix, scope_result_to_string(s->result));
- cgroup_context_dump(&s->cgroup_context, f, prefix);
+ cgroup_context_dump(UNIT(s), f, prefix);
kill_context_dump(&s->kill_context, f, prefix);
}
prefix, s->n_fd_store_max,
prefix, s->n_fd_store);
- cgroup_context_dump(&s->cgroup_context, f, prefix);
+ cgroup_context_dump(UNIT(s), f, prefix);
}
static int service_is_suitable_main_pid(Service *s, pid_t pid, int prio) {
service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_RESOURCES);
}
-static int state_to_kill_operation(ServiceState state) {
+static int state_to_kill_operation(Service *s, ServiceState state) {
switch (state) {
case SERVICE_STOP_WATCHDOG:
return KILL_WATCHDOG;
case SERVICE_STOP_SIGTERM:
+ if (unit_has_job_type(UNIT(s), JOB_RESTART))
+ return KILL_RESTART;
+ _fallthrough_;
+
case SERVICE_FINAL_SIGTERM:
return KILL_TERMINATE;
r = unit_kill_context(
UNIT(s),
&s->kill_context,
- state_to_kill_operation(state),
+ state_to_kill_operation(s, state),
s->main_pid,
s->control_pid,
s->main_pid_alien);
assert(s);
- if (UNIT(s)->job && UNIT(s)->job->type == JOB_STOP) {
+ if (unit_has_job_type(UNIT(s), JOB_STOP)) {
/* Don't restart things if we are going down anyway */
log_unit_info(UNIT(s), "Stop job pending for unit, delaying automatic restart.");
"%sSlice State: %s\n",
prefix, slice_state_to_string(t->state));
- cgroup_context_dump(&t->cgroup_context, f, prefix);
+ cgroup_context_dump(UNIT(t), f, prefix);
}
static int slice_start(Unit *u) {
FOREACH_DIRENT(entry, dir, return 0) {
_cleanup_fclose_ FILE *policy = NULL;
+ dirent_ensure_type(dir, entry);
if (!dirent_is_file(entry))
continue;
FOREACH_DIRENT(entry, dir, return 0) {
_cleanup_fclose_ FILE *policy = NULL;
+ dirent_ensure_type(dir, entry);
if (!dirent_is_file(entry))
continue;
exec_command_dump_list(s->exec_command[c], f, prefix2);
}
- cgroup_context_dump(&s->cgroup_context, f, prefix);
+ cgroup_context_dump(UNIT(s), f, prefix);
}
static int instance_from_socket(int fd, unsigned nr, char **instance) {
socket_enter_signal(s, SOCKET_FINAL_SIGTERM, SOCKET_FAILURE_RESOURCES);
}
+static int state_to_kill_operation(Socket *s, SocketState state) {
+ if (state == SOCKET_STOP_PRE_SIGTERM && unit_has_job_type(UNIT(s), JOB_RESTART))
+ return KILL_RESTART;
+
+ if (state == SOCKET_FINAL_SIGTERM)
+ return KILL_TERMINATE;
+
+ return KILL_KILL;
+}
+
static void socket_enter_signal(Socket *s, SocketState state, SocketResult f) {
int r;
r = unit_kill_context(
UNIT(s),
&s->kill_context,
- !IN_SET(state, SOCKET_STOP_PRE_SIGTERM, SOCKET_FINAL_SIGTERM) ?
- KILL_KILL : KILL_TERMINATE,
+ state_to_kill_operation(s, state),
-1,
s->control_pid,
false);
exec_context_dump(&s->exec_context, f, prefix);
kill_context_dump(&s->kill_context, f, prefix);
- cgroup_context_dump(&s->cgroup_context, f, prefix);
+ cgroup_context_dump(UNIT(s), f, prefix);
}
static int swap_spawn(Swap *s, ExecCommand *c, pid_t *_pid) {
swap_enter_dead(s, f);
}
+static int state_to_kill_operation(Swap *s, SwapState state) {
+ if (state == SWAP_DEACTIVATING_SIGTERM) {
+ if (unit_has_job_type(UNIT(s), JOB_RESTART))
+ return KILL_RESTART;
+ else
+ return KILL_TERMINATE;
+ }
+
+ return KILL_KILL;
+}
+
static void swap_enter_signal(Swap *s, SwapState state, SwapResult f) {
int r;
- KillOperation kop;
assert(s);
if (s->result == SWAP_SUCCESS)
s->result = f;
- if (state == SWAP_DEACTIVATING_SIGTERM)
- kop = KILL_TERMINATE;
- else
- kop = KILL_KILL;
- r = unit_kill_context(UNIT(s), &s->kill_context, kop, -1, s->control_pid, false);
+ r = unit_kill_context(UNIT(s),
+ &s->kill_context,
+ state_to_kill_operation(s, state),
+ -1,
+ s->control_pid,
+ false);
if (r < 0)
goto fail;
#include "bpf-firewall.h"
#include "bus-common-errors.h"
#include "bus-util.h"
+#include "cgroup-setup.h"
#include "cgroup-util.h"
#include "dbus-unit.h"
#include "dbus.h"
* different from unit_inactive_or_pending() which checks both
* the current state and for a queued job. */
- return u->job && u->job->type == JOB_STOP;
+ return unit_has_job_type(u, JOB_STOP);
}
bool unit_inactive_or_pending(Unit *u) {
bool unit_will_restart_default(Unit *u) {
assert(u);
- if (!u->job)
- return false;
- if (u->job->type == JOB_START)
- return true;
-
- return false;
+ return unit_has_job_type(u, JOB_START);
}
bool unit_will_restart(Unit *u) {
return 1;
}
-static int operation_to_signal(KillContext *c, KillOperation k) {
+static int operation_to_signal(const KillContext *c, KillOperation k, bool *noteworthy) {
assert(c);
switch (k) {
case KILL_TERMINATE:
case KILL_TERMINATE_AND_LOG:
+ *noteworthy = false;
return c->kill_signal;
+ case KILL_RESTART:
+ *noteworthy = false;
+ return restart_kill_signal(c);
+
case KILL_KILL:
+ *noteworthy = true;
return c->final_kill_signal;
case KILL_WATCHDOG:
+ *noteworthy = true;
return c->watchdog_signal;
default:
if (c->kill_mode == KILL_NONE)
return 0;
- sig = operation_to_signal(c, k);
+ bool noteworthy;
+ sig = operation_to_signal(c, k, ¬eworthy);
+ if (noteworthy)
+ log_func = log_kill;
send_sighup =
c->send_sighup &&
IN_SET(k, KILL_TERMINATE, KILL_TERMINATE_AND_LOG) &&
sig != SIGHUP;
- if (k != KILL_TERMINATE || IN_SET(sig, SIGKILL, SIGABRT))
- log_func = log_kill;
-
if (main_pid > 0) {
if (log_func)
log_func(main_pid, sig, u);
typedef enum KillOperation {
KILL_TERMINATE,
KILL_TERMINATE_AND_LOG,
+ KILL_RESTART,
KILL_KILL,
KILL_WATCHDOG,
_KILL_OPERATION_MAX,
int unit_pid_attachable(Unit *unit, pid_t pid, sd_bus_error *error);
+static inline bool unit_has_job_type(Unit *u, JobType type) {
+ return u && u->job && u->job->type == type;
+}
+
/* unit_log_skip is for cases like ExecCondition= where a unit is considered "done"
* after some execution, rather than succeeded or failed. */
void unit_log_skip(Unit *u, const char *result);
static const char *arg_type = NULL; /* ANY_LUKS, CRYPT_LUKS1, CRYPT_LUKS2, CRYPT_TCRYPT or CRYPT_PLAIN */
static char *arg_cipher = NULL;
static unsigned arg_key_size = 0;
-#if HAVE_LIBCRYPTSETUP_SECTOR_SIZE
static unsigned arg_sector_size = CRYPT_SECTOR_SIZE;
-#endif
static int arg_key_slot = CRYPT_ANY_SLOT;
static unsigned arg_keyfile_size = 0;
static uint64_t arg_keyfile_offset = 0;
static bool arg_submit_from_crypt_cpus = false;
static bool arg_tcrypt_hidden = false;
static bool arg_tcrypt_system = false;
-#ifdef CRYPT_TCRYPT_VERA_MODES
static bool arg_tcrypt_veracrypt = false;
-#endif
static char **arg_tcrypt_keyfiles = NULL;
static uint64_t arg_offset = 0;
static uint64_t arg_skip = 0;
} else if ((val = startswith(option, "sector-size="))) {
-#if HAVE_LIBCRYPTSETUP_SECTOR_SIZE
r = safe_atou(val, &arg_sector_size);
if (r < 0) {
log_error_errno(r, "Failed to parse %s, ignoring: %m", option);
log_error("sector-size= is outside of %u and %u, ignoring.", CRYPT_SECTOR_SIZE, CRYPT_MAX_SECTOR_SIZE);
return 0;
}
-#else
- log_error("sector-size= is not supported, compiled with old libcryptsetup.");
- return 0;
-#endif
} else if ((val = startswith(option, "key-slot="))) {
}
} else if ((val = startswith(option, "keyfile-offset="))) {
- uint64_t off;
- r = safe_atou64(val, &off);
+ r = safe_atou64(val, &arg_keyfile_offset);
if (r < 0) {
log_error_errno(r, "Failed to parse %s, ignoring: %m", option);
return 0;
}
- if ((size_t) off != off) {
- /* https://gitlab.com/cryptsetup/cryptsetup/issues/359 */
- log_error("keyfile-offset= value would truncated to %zu, ignoring.", (size_t) off);
- return 0;
- }
-
- arg_keyfile_offset = off;
-
} else if ((val = startswith(option, "hash="))) {
r = free_and_strdup(&arg_hash, val);
if (r < 0)
arg_type = CRYPT_TCRYPT;
arg_tcrypt_system = true;
} else if (streq(option, "tcrypt-veracrypt")) {
-#ifdef CRYPT_TCRYPT_VERA_MODES
arg_type = CRYPT_TCRYPT;
arg_tcrypt_veracrypt = true;
-#else
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "This version of cryptsetup does not support tcrypt-veracrypt; refusing.");
-#endif
} else if (STR_IN_SET(option, "plain", "swap", "tmp"))
arg_type = CRYPT_PLAIN;
else if ((val = startswith(option, "timeout="))) {
if (arg_tcrypt_system)
params.flags |= CRYPT_TCRYPT_SYSTEM_HEADER;
-#ifdef CRYPT_TCRYPT_VERA_MODES
if (arg_tcrypt_veracrypt)
params.flags |= CRYPT_TCRYPT_VERA_MODES;
-#endif
if (key_file) {
r = read_one_line_file(key_file, &passphrase);
struct crypt_params_plain params = {
.offset = arg_offset,
.skip = arg_skip,
-#if HAVE_LIBCRYPTSETUP_SECTOR_SIZE
.sector_size = arg_sector_size,
-#endif
};
const char *cipher, *cipher_mode;
_cleanup_free_ char *truncated_cipher = NULL;
crypt_get_device_name(cd));
if (key_file) {
- r = crypt_activate_by_keyfile_offset(cd, name, arg_key_slot, key_file, arg_keyfile_size, arg_keyfile_offset, flags);
+ r = crypt_activate_by_keyfile_device_offset(cd, name, arg_key_slot, key_file, arg_keyfile_size, arg_keyfile_offset, flags);
if (r == -EPERM) {
log_error_errno(r, "Failed to activate with key file '%s'. (Key data incorrect?)", key_file);
return -EAGAIN; /* Log actual error, but return EAGAIN */
if (r < 0)
return log_error_errno(r, "Failed to set LUKS data device %s: %m", argv[3]);
}
-#ifdef CRYPT_ANY_TOKEN
+
/* Tokens are available in LUKS2 only, but it is ok to call (and fail) with LUKS1. */
if (!key_file) {
r = crypt_activate_by_token(cd, argv[2], CRYPT_ANY_TOKEN, NULL, flags);
log_debug_errno(r, "Token activation unsuccessful for device %s: %m", crypt_get_device_name(cd));
}
-#endif
}
for (tries = 0; arg_tries == 0 || tries < arg_tries; tries++) {
#include "special.h"
#include "string-util.h"
#include "strv.h"
+#include "unit-file.h"
#include "unit-name.h"
static const char *arg_dest = NULL;
#include "device-util.h"
#include "dirent-util.h"
#include "dissect-image.h"
-#include "efivars.h"
+#include "efi-loader.h"
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
uint32_t xid, const uint8_t *mac_addr,
size_t mac_addr_len, uint16_t arp_type,
uint16_t port);
-int dhcp_network_bind_udp_socket(int ifindex, be32_t address, uint16_t port);
+int dhcp_network_bind_udp_socket(int ifindex, be32_t address, uint16_t port, int ip_service_type);
int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link,
const void *packet, size_t len);
int dhcp_network_send_udp_socket(int s, be32_t address, uint16_t port,
void dhcp_packet_append_ip_headers(DHCPPacket *packet, be32_t source_addr,
uint16_t source, be32_t destination_addr,
- uint16_t destination, uint16_t len);
+ uint16_t destination, uint16_t len, int ip_service_type);
int dhcp_packet_verify_headers(DHCPPacket *packet, size_t len, bool checksum, uint16_t port);
bcast_addr, ð_mac, arp_type, dhcp_hlen, port);
}
-int dhcp_network_bind_udp_socket(int ifindex, be32_t address, uint16_t port) {
+int dhcp_network_bind_udp_socket(int ifindex, be32_t address, uint16_t port, int ip_service_type) {
union sockaddr_union src = {
.in.sin_family = AF_INET,
.in.sin_port = htobe16(port),
if (s < 0)
return -errno;
- r = setsockopt_int(s, IPPROTO_IP, IP_TOS, IPTOS_CLASS_CS6);
+ if (ip_service_type >= 0)
+ r = setsockopt_int(s, IPPROTO_IP, IP_TOS, ip_service_type);
+ else
+ r = setsockopt_int(s, IPPROTO_IP, IP_TOS, IPTOS_CLASS_CS6);
+
if (r < 0)
return r;
void dhcp_packet_append_ip_headers(DHCPPacket *packet, be32_t source_addr,
uint16_t source_port, be32_t destination_addr,
- uint16_t destination_port, uint16_t len) {
+ uint16_t destination_port, uint16_t len, int ip_service_type) {
packet->ip.version = IPVERSION;
packet->ip.ihl = DHCP_IP_SIZE / 4;
packet->ip.tot_len = htobe16(len);
- packet->ip.tos = IPTOS_CLASS_CS6;
+ if (ip_service_type >= 0)
+ packet->ip.tos = ip_service_type;
+ else
+ packet->ip.tos = IPTOS_CLASS_CS6;
packet->ip.protocol = IPPROTO_UDP;
packet->ip.saddr = source_addr;
int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code,
size_t optlen, const void *optval);
int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia);
-int dhcp6_option_append_pd(uint8_t *buf, size_t len, const DHCP6IA *pd);
+int dhcp6_option_append_pd(uint8_t *buf, size_t len, const DHCP6IA *pd, DHCP6Address *hint_pd_prefix);
int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn);
int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode,
size_t *optlen, uint8_t **optvalue);
return r;
}
-int dhcp6_option_append_pd(uint8_t *buf, size_t len, const DHCP6IA *pd) {
+int dhcp6_option_append_pd(uint8_t *buf, size_t len, const DHCP6IA *pd, DHCP6Address *hint_pd_prefix) {
DHCP6Option *option = (DHCP6Option *)buf;
size_t i = sizeof(*option) + sizeof(pd->ia_pd);
+ DHCP6PDPrefixOption *prefix_opt;
DHCP6Address *prefix;
assert_return(buf, -EINVAL);
option->code = htobe16(SD_DHCP6_OPTION_IA_PD);
memcpy(&option->data, &pd->ia_pd, sizeof(pd->ia_pd));
-
LIST_FOREACH(addresses, prefix, pd->addresses) {
- DHCP6PDPrefixOption *prefix_opt;
-
if (len < i + sizeof(*prefix_opt))
return -ENOBUFS;
prefix_opt->option.code = htobe16(SD_DHCP6_OPTION_IA_PD_PREFIX);
prefix_opt->option.len = htobe16(sizeof(prefix_opt->iapdprefix));
- memcpy(&prefix_opt->iapdprefix, &prefix->iapdprefix,
- sizeof(struct iapdprefix));
+ memcpy(&prefix_opt->iapdprefix, &prefix->iapdprefix, sizeof(struct iapdprefix));
+ i += sizeof(*prefix_opt);
+ }
+
+ if (hint_pd_prefix && hint_pd_prefix->iapdprefix.prefixlen > 0) {
+ if (len < i + sizeof(*prefix_opt))
+ return -ENOBUFS;
+
+ prefix_opt = (DHCP6PDPrefixOption *)&buf[i];
+ prefix_opt->option.code = htobe16(SD_DHCP6_OPTION_IA_PD_PREFIX);
+ prefix_opt->option.len = htobe16(sizeof(prefix_opt->iapdprefix));
+ memcpy(&prefix_opt->iapdprefix, &hint_pd_prefix->iapdprefix, sizeof(struct iapdprefix));
i += sizeof(*prefix_opt);
}
void *userdata;
sd_dhcp_lease *lease;
usec_t start_delay;
+ int ip_service_type;
};
static const uint8_t default_req_opts[] = {
return 0;
}
+int sd_dhcp_client_set_service_type(sd_dhcp_client *client, int type) {
+ assert_return(client, -EINVAL);
+
+ client->ip_service_type = type;
+
+ return 0;
+}
+
static int client_notify(sd_dhcp_client *client, int event) {
assert(client);
size_t len) {
dhcp_packet_append_ip_headers(packet, INADDR_ANY, client->port,
- INADDR_BROADCAST, DHCP_PORT_SERVER, len);
+ INADDR_BROADCAST, DHCP_PORT_SERVER, len, client->ip_service_type);
return dhcp_network_send_raw_socket(client->fd, &client->link,
packet, len);
return 0;
}
-static int client_send_release(sd_dhcp_client *client) {
- _cleanup_free_ DHCPPacket *release = NULL;
- size_t optoffset, optlen;
- int r;
-
- assert(client);
- assert(!IN_SET(client->state, DHCP_STATE_STOPPED));
-
- r = client_message_init(client, &release, DHCP_RELEASE,
- &optlen, &optoffset);
- if (r < 0)
- return r;
-
- /* Fill up release IP and MAC */
- release->dhcp.ciaddr = client->lease->address;
- memcpy(&release->dhcp.chaddr, &client->mac_addr, client->mac_addr_len);
-
- r = dhcp_option_append(&release->dhcp, optlen, &optoffset, 0,
- SD_DHCP_OPTION_END, 0, NULL);
- if (r < 0)
- return r;
-
- r = dhcp_network_send_udp_socket(client->fd,
- client->lease->server_address,
- DHCP_PORT_SERVER,
- &release->dhcp,
- sizeof(DHCPMessage) + optoffset);
- if (r < 0)
- return r;
-
- log_dhcp_client(client, "RELEASE");
-
- return 0;
-}
-
static int client_send_request(sd_dhcp_client *client) {
_cleanup_free_ DHCPPacket *request = NULL;
size_t optoffset, optlen;
goto error;
}
- r = dhcp_network_bind_udp_socket(client->ifindex, client->lease->address, client->port);
+ r = dhcp_network_bind_udp_socket(client->ifindex, client->lease->address, client->port, client->ip_service_type);
if (r < 0) {
log_dhcp_client(client, "could not bind UDP socket");
goto error;
int sd_dhcp_client_send_release(sd_dhcp_client *client) {
assert_return(client, -EINVAL);
+ assert_return(client->state != DHCP_STATE_STOPPED, -ESTALE);
+ assert_return(client->lease, -EUNATCH);
+
+ _cleanup_free_ DHCPPacket *release = NULL;
+ size_t optoffset, optlen;
+ int r;
+
+ r = client_message_init(client, &release, DHCP_RELEASE, &optlen, &optoffset);
+ if (r < 0)
+ return r;
- client_send_release(client);
+ /* Fill up release IP and MAC */
+ release->dhcp.ciaddr = client->lease->address;
+ memcpy(&release->dhcp.chaddr, &client->mac_addr, client->mac_addr_len);
+
+ r = dhcp_option_append(&release->dhcp, optlen, &optoffset, 0,
+ SD_DHCP_OPTION_END, 0, NULL);
+ if (r < 0)
+ return r;
+
+ r = dhcp_network_send_udp_socket(client->fd,
+ client->lease->server_address,
+ DHCP_PORT_SERVER,
+ &release->dhcp,
+ sizeof(DHCPMessage) + optoffset);
+ if (r < 0)
+ return r;
+
+ log_dhcp_client(client, "RELEASE");
return 0;
}
.port = DHCP_PORT_CLIENT,
.anonymize = !!anonymize,
.max_attempts = (uint64_t) -1,
+ .ip_service_type = -1,
};
/* NOTE: this could be moved to a function. */
if (anonymize) {
dhcp_packet_append_ip_headers(packet, server->address, DHCP_PORT_SERVER,
packet->dhcp.yiaddr,
- DHCP_PORT_CLIENT, len);
+ DHCP_PORT_CLIENT, len, -1);
return dhcp_network_send_raw_socket(server->fd_raw, &link, packet, len);
}
}
server->fd_raw = r;
- r = dhcp_network_bind_udp_socket(server->ifindex, INADDR_ANY, DHCP_PORT_SERVER);
+ r = dhcp_network_bind_udp_socket(server->ifindex, INADDR_ANY, DHCP_PORT_SERVER, -1);
if (r < 0) {
sd_dhcp_server_stop(server);
return r;
sd_event *event;
int event_priority;
int ifindex;
+ DHCP6Address hint_pd_prefix;
struct in6_addr local_address;
uint8_t mac_addr[MAX_MAC_ADDR_LEN];
size_t mac_addr_len;
return 0;
}
+int sd_dhcp6_client_set_prefix_delegation_hint(
+ sd_dhcp6_client *client,
+ uint8_t prefixlen,
+ const struct in6_addr *pd_address) {
+
+ assert_return(client, -EINVAL);
+ assert_return(pd_address, -EINVAL);
+
+ assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
+
+ client->hint_pd_prefix.iapdprefix.address = *pd_address;
+ client->hint_pd_prefix.iapdprefix.prefixlen = prefixlen;
+
+ return 0;
+}
+
static int client_ensure_duid(sd_dhcp6_client *client) {
if (client->duid_len != 0)
return 0;
}
if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) {
- r = dhcp6_option_append_pd(opt, optlen, &client->ia_pd);
+ r = dhcp6_option_append_pd(opt, optlen, &client->ia_pd, &client->hint_pd_prefix);
if (r < 0)
return r;
}
if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) {
- r = dhcp6_option_append_pd(opt, optlen, &client->lease->pd);
+ r = dhcp6_option_append_pd(opt, optlen, &client->lease->pd, NULL);
if (r < 0)
return r;
}
if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) {
- r = dhcp6_option_append_pd(opt, optlen, &client->lease->pd);
+ r = dhcp6_option_append_pd(opt, optlen, &client->lease->pd, NULL);
if (r < 0)
return r;
.request = DHCP6_REQUEST_IA_NA,
.fd = -1,
.req_opts_len = ELEMENTSOF(default_req_opts),
+ .hint_pd_prefix.iapdprefix.lifetime_preferred = (be32_t) -1,
+ .hint_pd_prefix.iapdprefix.lifetime_valid = (be32_t) -1,
.req_opts = TAKE_PTR(req_opts),
};
return test_fd[0];
}
-int dhcp_network_bind_udp_socket(int ifindex, be32_t address, uint16_t port) {
+int dhcp_network_bind_udp_socket(int ifindex, be32_t address, uint16_t port, int ip_service_type) {
int fd;
fd = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
test_setup_logging(LOG_DEBUG);
- if (cg_unified_flush() == -ENOMEDIUM)
+ if (cg_unified() == -ENOMEDIUM)
return log_tests_skipped("/sys/fs/cgroup/ not available");
r = sd_bus_creds_new_from_pid(&creds, 0, _SD_BUS_CREDS_ALL);
* benefit in leaving it queued */
assert(s->child.options & (WSTOPPED|WCONTINUED));
- waitid(P_PID, s->child.pid, &s->child.siginfo, WNOHANG|(s->child.options & (WSTOPPED|WCONTINUED)));
+ (void) waitid(P_PID, s->child.pid, &s->child.siginfo, WNOHANG|(s->child.options & (WSTOPPED|WCONTINUED)));
}
r = source_set_pending(s, true);
#include "device-util.h"
#include "dirent-util.h"
#include "efivars.h"
+#include "efi-loader.h"
#include "env-util.h"
#include "escape.h"
#include "fd-util.h"
struct dirent *de;
FOREACH_DIRENT_ALL(de, d, break) {
+ dirent_ensure_type(d, de);
if (!dirent_is_file(de))
continue;
FOREACH_DIRENT(de, d, return -errno) {
int k;
+ dirent_ensure_type(d, de);
if (!dirent_is_file(de))
continue;
#include "bus-error.h"
#include "bus-internal.h"
#include "bus-util.h"
-#include "cgroup-util.h"
+#include "cgroup-setup.h"
#include "errno-util.h"
#include "fd-util.h"
#include "fileio.h"
***/
#include <ctype.h>
+#include <netinet/ip.h>
#include "conf-parser.h"
#include "def.h"
#include "networkd-manager.h"
#include "networkd-network.h"
#include "networkd-speed-meter.h"
+#include "networkd-dhcp4.h"
#include "string-table.h"
int manager_parse_config_file(Manager *m) {
ret->raw_data_len = count;
return 0;
}
+
+int config_parse_ip_service_type(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+
+ if (streq(rvalue, "CS4"))
+ *((int *)data) = IPTOS_CLASS_CS4;
+ else if (streq(rvalue, "CS6"))
+ *((int *)data) = IPTOS_CLASS_CS6;
+ else
+ log_syntax(unit, LOG_WARNING, filename, line, 0,
+ "Failed to parse IPServiceType type '%s', ignoring.", rvalue);
+
+ return 0;
+}
CONFIG_PARSER_PROTOTYPE(config_parse_duid_type);
CONFIG_PARSER_PROTOTYPE(config_parse_duid_rawdata);
+CONFIG_PARSER_PROTOTYPE(config_parse_ip_service_type);
/* SPDX-License-Identifier: LGPL-2.1+ */
+#include "in-addr-util.h"
#include "networkd-dhcp-common.h"
#include "networkd-network.h"
#include "parse-util.h"
return 0;
}
+int config_parse_dhcp6_pd_hint(
+ const char* unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ Network *network = data;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = in_addr_prefix_from_string(rvalue, AF_INET6, (union in_addr_union *) &network->dhcp6_pd_address, &network->dhcp6_pd_length);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse PrefixDelegationHint=%s, ignoring assignment", rvalue);
+ return 0;
+ }
+
+ if (network->dhcp6_pd_length < 1 || network->dhcp6_pd_length > 128) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid prefix length='%d', ignoring assignment", network->dhcp6_pd_length);
+ network->dhcp6_pd_length = 0;
+ return 0;
+ }
+
+ return 0;
+}
+
DEFINE_CONFIG_PARSE_ENUM(config_parse_dhcp_use_domains, dhcp_use_domains, DHCPUseDomains,
"Failed to parse DHCP use domains setting");
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_use_sip);
CONFIG_PARSER_PROTOTYPE(config_parse_iaid);
CONFIG_PARSER_PROTOTYPE(config_parse_section_route_table);
+CONFIG_PARSER_PROTOTYPE(config_parse_dhcp6_pd_hint);
static int dhcp_remove_routes(Link *link, sd_dhcp_lease *lease, const struct in_addr *address, bool remove_all);
static int dhcp_remove_router(Link *link, sd_dhcp_lease *lease, const struct in_addr *address, bool remove_all);
static int dhcp_remove_dns_routes(Link *link, sd_dhcp_lease *lease, const struct in_addr *address, bool remove_all);
-static int dhcp_remove_address(Link *link, sd_dhcp_lease *lease, const struct in_addr *address);
+static int dhcp_remove_address(Link *link, sd_dhcp_lease *lease, const struct in_addr *address, link_netlink_message_handler_t callback);
+static int dhcp_remove_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link);
+static int dhcp_lease_renew(sd_dhcp_client *client, Link *link);
void dhcp4_release_old_lease(Link *link) {
struct in_addr address = {}, address_old = {};
(void) dhcp_remove_dns_routes(link, link->dhcp_lease_old, &address_old, false);
if (!in4_addr_equal(&address_old, &address))
- (void) dhcp_remove_address(link, link->dhcp_lease_old, &address_old);
+ (void) dhcp_remove_address(link, link->dhcp_lease_old, &address_old, NULL);
link->dhcp_lease_old = sd_dhcp_lease_unref(link->dhcp_lease_old);
link_dirty(link);
return 1;
r = sd_netlink_message_get_errno(m);
- if (r < 0 && r != -EEXIST) {
+ if (r == -ENETUNREACH && !link->dhcp4_route_retrying) {
+
+ /* It seems kernel does not support that the prefix route cannot be configured with
+ * route table. Let's once drop the config and reconfigure them later. */
+
+ log_link_debug_errno(link, r, "Could not set DHCPv4 route, retrying later: %m");
+ link->dhcp4_route_failed = true;
+ link->manager->dhcp4_prefix_root_cannot_set_table = true;
+ } else if (r < 0 && r != -EEXIST) {
log_link_error_errno(link, r, "Could not set DHCPv4 route: %m");
link_enter_failed(link);
return 1;
}
if (link->dhcp4_messages == 0) {
+ if (link->dhcp4_route_failed) {
+ struct in_addr address = {};
+
+ link->dhcp4_route_failed = false;
+ link->dhcp4_route_retrying = true;
+
+ (void) sd_dhcp_lease_get_address(link->dhcp_lease, &address);
+ (void) dhcp_remove_routes(link, link->dhcp_lease, &address, true);
+ (void) dhcp_remove_router(link, link->dhcp_lease, &address, true);
+ (void) dhcp_remove_dns_routes(link, link->dhcp_lease, &address, true);
+ (void) dhcp_remove_address(link, link->dhcp_lease, &address, dhcp_remove_address_handler);
+
+ return 1;
+ }
link->dhcp4_configured = true;
/* New address and routes are configured now. Let's release old lease. */
dhcp4_release_old_lease(link);
return RT_SCOPE_UNIVERSE;
}
+static bool link_noprefixroute(Link *link) {
+ return link->network->dhcp_route_table_set &&
+ link->network->dhcp_route_table != RT_TABLE_MAIN &&
+ !link->manager->dhcp4_prefix_root_cannot_set_table;
+}
+
static int dhcp_route_configure(Route **route, Link *link) {
int r;
return 0;
}
+static int dhcp_prefix_route_from_lease(
+ const sd_dhcp_lease *lease,
+ uint32_t table,
+ const struct in_addr *address,
+ Route **ret_route) {
+
+ Route *route;
+ struct in_addr netmask;
+ int r;
+
+ r = sd_dhcp_lease_get_netmask((sd_dhcp_lease*) lease, &netmask);
+ if (r < 0)
+ return r;
+
+ r = route_new(&route);
+ if (r < 0)
+ return r;
+
+ route->family = AF_INET;
+ route->dst.in.s_addr = address->s_addr & netmask.s_addr;
+ route->dst_prefixlen = in4_addr_netmask_to_prefixlen(&netmask);
+ route->prefsrc.in = *address;
+ route->scope = RT_SCOPE_LINK;
+ route->protocol = RTPROT_DHCP;
+ route->table = table;
+ *ret_route = route;
+ return 0;
+}
+
static int link_set_dhcp_routes(Link *link) {
_cleanup_free_ sd_dhcp_route **static_routes = NULL;
bool classless_route = false, static_route = false;
if (r < 0)
return log_link_warning_errno(link, r, "DHCP error: could not get address: %m");
+ if (link_noprefixroute(link)) {
+ _cleanup_(route_freep) Route *prefix_route = NULL;
+
+ r = dhcp_prefix_route_from_lease(link->dhcp_lease, table, &address, &prefix_route);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not create prefix route: %m");
+
+ r = dhcp_route_configure(&prefix_route, link);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not set prefix route: %m");
+ }
+
n = sd_dhcp_lease_get_routes(link->dhcp_lease, &static_routes);
if (n == -ENODATA)
log_link_debug_errno(link, n, "DHCP: No routes received from DHCP server: %m");
(void) route_remove(route, link, NULL);
}
+ if (link_noprefixroute(link)) {
+ _cleanup_(route_freep) Route *prefix_route = NULL;
+
+ r = dhcp_prefix_route_from_lease(lease, table, address, &prefix_route);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Could not delete prefix route: %m");
+
+ if (remove_all || !set_contains(link->dhcp_routes, prefix_route))
+ (void) route_remove(prefix_route, link, NULL);
+ }
+
return 0;
}
-static int dhcp_remove_address(Link *link, sd_dhcp_lease *lease, const struct in_addr *address) {
+static int dhcp_remove_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
+ int r;
+
+ assert(link);
+
+ /* This is only used when retrying to assign the address received from DHCPv4 server.
+ * See dhcp4_route_handler(). */
+
+ if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
+ return 1;
+
+ r = sd_netlink_message_get_errno(m);
+ if (r < 0)
+ log_link_debug_errno(link, r, "Failed to remove DHCPv4 address, ignoring: %m");
+ else
+ (void) manager_rtnl_process_address(rtnl, m, link->manager);
+
+ (void) dhcp_lease_renew(link->dhcp_client, link);
+ return 1;
+}
+
+static int dhcp_remove_address(
+ Link *link, sd_dhcp_lease *lease,
+ const struct in_addr *address,
+ link_netlink_message_handler_t callback) {
+
_cleanup_(address_freep) Address *a = NULL;
struct in_addr netmask;
int r;
if (sd_dhcp_lease_get_netmask(lease, &netmask) >= 0)
a->prefixlen = in4_addr_netmask_to_prefixlen(&netmask);
- (void) address_remove(a, link, NULL);
+ (void) address_remove(a, link, callback);
return 0;
}
(void) dhcp_remove_routes(link, link->dhcp_lease, &address, true);
(void) dhcp_remove_router(link, link->dhcp_lease, &address, true);
(void) dhcp_remove_dns_routes(link, link->dhcp_lease, &address, true);
- (void) dhcp_remove_address(link, link->dhcp_lease, &address);
+ (void) dhcp_remove_address(link, link->dhcp_lease, &address, NULL);
(void) dhcp_reset_mtu(link);
(void) dhcp_reset_hostname(link);
addr->cinfo.ifa_valid = lifetime;
addr->prefixlen = prefixlen;
addr->broadcast.s_addr = address->s_addr | ~netmask->s_addr;
+ addr->prefix_route = link_noprefixroute(link);
/* allow reusing an existing address and simply update its lifetime
* in case it already exists */
return 0;
}
- if (link->network->dhcp_send_release)
- (void) sd_dhcp_client_send_release(client);
-
if (link->dhcp_lease) {
+ if (link->network->dhcp_send_release)
+ (void) sd_dhcp_client_send_release(client);
+
r = dhcp_lease_lost(link);
if (r < 0) {
link_enter_failed(link);
return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set max attempts: %m");
}
- return dhcp4_set_client_identifier(link);
+ if (link->network->ip_service_type > 0) {
+ r = sd_dhcp_client_set_service_type(link->dhcp_client, link->network->ip_service_type);
+ if (r < 0)
+ return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set ip service type: %m");
+ }
+ return dhcp4_set_client_identifier(link);
}
int config_parse_dhcp_max_attempts(
return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set prefix delegation: %m");
}
+ if (link->network->dhcp6_pd_length > 0) {
+ r = sd_dhcp6_client_set_prefix_delegation_hint(client, link->network->dhcp6_pd_length, &link->network->dhcp6_pd_address);
+ if (r < 0)
+ return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set prefix hint: %m");
+ }
+
link->dhcp6_client = TAKE_PTR(client);
return 0;
dhcp4_release_old_lease(link);
- if (link->dhcp_client && (!may_keep_dhcp || !link->network ||
- !FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP_ON_STOP))) {
+ bool keep_dhcp = may_keep_dhcp &&
+ link->network &&
+ (link->manager->restarting ||
+ FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP_ON_STOP));
+
+ if (link->dhcp_client && !keep_dhcp) {
k = sd_dhcp_client_stop(link->dhcp_client);
if (k < 0)
r = log_link_warning_errno(link, k, "Could not stop DHCPv4 client: %m");
if (link->state != LINK_STATE_CONFIGURING)
return;
- log_link_info(link, "Configured");
-
link_set_state(link, LINK_STATE_CONFIGURED);
(void) link_join_netdevs_after_configured(link);
char *lease_file;
uint32_t original_mtu;
unsigned dhcp4_messages;
- bool dhcp4_configured;
- bool dhcp6_configured;
+ bool dhcp4_route_failed:1;
+ bool dhcp4_route_retrying:1;
+ bool dhcp4_configured:1;
+ bool dhcp6_configured:1;
unsigned ndisc_messages;
bool ndisc_configured;
sd_ipv4ll *ipv4ll;
bool ipv4ll_address:1;
- bool neighbors_configured;
-
- bool static_routes_configured;
- bool routing_policy_rules_configured;
- bool setting_mtu;
+ bool neighbors_configured:1;
+ bool static_routes_configured:1;
+ bool routing_policy_rules_configured:1;
+ bool setting_mtu:1;
LIST_HEAD(Address, pool_addresses);
#include "ordered-set.h"
#include "path-util.h"
#include "set.h"
+#include "signal-util.h"
#include "strv.h"
#include "sysctl-util.h"
#include "tmpfile-util.h"
return 1;
}
+static int signal_terminate_callback(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
+ Manager *m = userdata;
+
+ assert(m);
+ m->restarting = false;
+
+ log_debug("Terminate operation initiated.");
+
+ return sd_event_exit(sd_event_source_get_event(s), 0);
+}
+
+static int signal_restart_callback(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
+ Manager *m = userdata;
+
+ assert(m);
+ m->restarting = true;
+
+ log_debug("Restart operation initiated.");
+
+ return sd_event_exit(sd_event_source_get_event(s), 0);
+}
+
int manager_new(Manager **ret) {
_cleanup_(manager_freep) Manager *m = NULL;
int r;
if (r < 0)
return r;
+ assert_se(sigprocmask_many(SIG_SETMASK, NULL, SIGINT, SIGTERM, SIGUSR2, -1) >= 0);
+
(void) sd_event_set_watchdog(m->event, true);
- (void) sd_event_add_signal(m->event, NULL, SIGTERM, NULL, NULL);
- (void) sd_event_add_signal(m->event, NULL, SIGINT, NULL, NULL);
+ (void) sd_event_add_signal(m->event, NULL, SIGTERM, signal_terminate_callback, m);
+ (void) sd_event_add_signal(m->event, NULL, SIGINT, signal_terminate_callback, m);
+ (void) sd_event_add_signal(m->event, NULL, SIGUSR2, signal_restart_callback, m);
r = sd_event_add_post(m->event, NULL, manager_dirty_handler, m);
if (r < 0)
bool enumerating:1;
bool dirty:1;
+ bool restarting:1;
Set *dirty_links;
usec_t speed_meter_interval_usec;
usec_t speed_meter_usec_new;
usec_t speed_meter_usec_old;
+
+ bool dhcp4_prefix_root_cannot_set_table;
};
int manager_new(Manager **ret);
DHCPv4.ListenPort, config_parse_uint16, 0, offsetof(Network, dhcp_client_port)
DHCPv4.SendRelease, config_parse_bool, 0, offsetof(Network, dhcp_send_release)
DHCPv4.BlackList, config_parse_dhcp_black_listed_ip_address, 0, 0
+DHCPv4.IPServiceType, config_parse_ip_service_type, 0, offsetof(Network, ip_service_type)
DHCPv6.UseDNS, config_parse_bool, 0, offsetof(Network, dhcp6_use_dns)
DHCPv6.UseNTP, config_parse_bool, 0, offsetof(Network, dhcp6_use_ntp)
DHCPv6.RapidCommit, config_parse_bool, 0, offsetof(Network, rapid_commit)
DHCPv6.ForceDHCPv6PDOtherInformation, config_parse_bool, 0, offsetof(Network, dhcp6_force_pd_other_information)
+DHCPv6.PrefixDelegationHint, config_parse_dhcp6_pd_hint, 0, 0
IPv6AcceptRA.UseAutonomousPrefix, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_autonomous_prefix)
IPv6AcceptRA.UseOnLinkPrefix, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_onlink_prefix)
IPv6AcceptRA.UseDNS, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_dns)
/* CriticalConnection=yes also preserve foreign static configurations. */
network->keep_configuration = KEEP_CONFIGURATION_YES;
else
- /* For backward compatibility, we do not release DHCP addresses on manager stop. */
- network->keep_configuration = KEEP_CONFIGURATION_DHCP_ON_STOP;
+ network->keep_configuration = KEEP_CONFIGURATION_NO;
}
if (network->keep_configuration < 0)
- /* For backward compatibility, we do not release DHCP addresses on manager stop. */
- network->keep_configuration = KEEP_CONFIGURATION_DHCP_ON_STOP;
+ network->keep_configuration = KEEP_CONFIGURATION_NO;
LIST_FOREACH_SAFE(addresses, address, address_next, network->static_addresses)
if (address_section_verify(address) < 0)
.dhcp_use_routes = true,
/* NOTE: this var might be overwritten by network_apply_anonymize_if_set */
.dhcp_send_hostname = true,
+ .dhcp_send_release = true,
/* To enable/disable RFC7844 Anonymity Profiles */
.dhcp_anonymize = false,
.dhcp_route_metric = DHCP_ROUTE_METRIC,
.keep_configuration = _KEEP_CONFIGURATION_INVALID,
.can_triple_sampling = -1,
+ .ip_service_type = -1,
};
r = config_parse_many(filename, NETWORK_DIRS, dropin_dirname,
DHCPUseDomains dhcp_use_domains;
Set *dhcp_black_listed_ip;
Set *dhcp_request_options;
+ int ip_service_type;
/* DHCPv6 Client support*/
bool dhcp6_use_dns;
bool dhcp6_use_ntp;
+ uint8_t dhcp6_pd_length;
+ struct in6_addr dhcp6_pd_address;
/* DHCP Server Support */
bool dhcp_server;
#include <sys/mount.h>
#include "alloc-util.h"
+#include "cgroup-setup.h"
#include "fd-util.h"
#include "fileio.h"
#include "format-util.h"
assert_se(sigfillset(&fullmask) >= 0);
assert_se(sigprocmask(SIG_BLOCK, &fullmask, &oldmask) >= 0);
+ /* Surrender the terminal this stub may control so that child processes can have a controlling terminal
+ * without resorting to setsid hacks. */
+ r = ioctl(STDIN_FILENO, TIOCNOTTY);
+ if (r < 0 && errno != ENOTTY)
+ return log_error_errno(errno, "Failed to surrender controlling terminal: %m");
+
pid = fork();
if (pid < 0)
return log_error_errno(errno, "Failed to fork child pid: %m");
}
static int detect_unified_cgroup_hierarchy_from_environment(void) {
- const char *e;
+ const char *e, *var = "SYSTEMD_NSPAWN_UNIFIED_HIERARCHY";
int r;
/* Allow the user to control whether the unified hierarchy is used */
- e = getenv("UNIFIED_CGROUP_HIERARCHY");
- if (e) {
+
+ e = getenv(var);
+ if (!e) {
+ static bool warned = false;
+
+ var = "UNIFIED_CGROUP_HIERARCHY";
+ e = getenv(var);
+ if (e && !warned) {
+ log_info("$UNIFIED_CGROUP_HIERARCHY has been renamed to $SYSTEMD_NSPAWN_UNIFIED_HIERARCHY.");
+ warned = true;
+ }
+ }
+
+ if (!isempty(e)) {
r = parse_boolean(e);
if (r < 0)
- return log_error_errno(r, "Failed to parse $UNIFIED_CGROUP_HIERARCHY.");
+ return log_error_errno(r, "Failed to parse $%s: %m", var);
if (r > 0)
arg_unified_cgroup_hierarchy = CGROUP_UNIFIED_ALL;
else
static int detect_unified_cgroup_hierarchy_from_image(const char *directory) {
int r;
- /* Let's inherit the mode to use from the host system, but let's take into consideration what systemd in the
- * image actually supports. */
+ /* Let's inherit the mode to use from the host system, but let's take into consideration what systemd
+ * in the image actually supports. */
r = cg_all_unified();
if (r < 0)
return log_error_errno(r, "Failed to determine whether we are in all unified mode.");
return 0;
}
-static void parse_share_ns_env(const char *name, unsigned long ns_flag) {
+static int parse_share_ns_env(const char *name, unsigned long ns_flag) {
int r;
r = getenv_bool(name);
if (r == -ENXIO)
- return;
+ return 0;
if (r < 0)
- log_warning_errno(r, "Failed to parse %s from environment, defaulting to false.", name);
+ return log_error_errno(r, "Failed to parse $%s: %m", name);
arg_clone_ns_flags = (arg_clone_ns_flags & ~ns_flag) | (r > 0 ? 0 : ns_flag);
arg_settings_mask |= SETTING_CLONE_NS_FLAGS;
+ return 0;
}
-static void parse_mount_settings_env(void) {
+static int parse_mount_settings_env(void) {
const char *e;
int r;
r = getenv_bool("SYSTEMD_NSPAWN_TMPFS_TMP");
+ if (r < 0 && r != -ENXIO)
+ return log_error_errno(r, "Failed to parse $SYSTEMD_NSPAWN_TMPFS_TMP: %m");
if (r >= 0)
SET_FLAG(arg_mount_settings, MOUNT_APPLY_TMPFS_TMP, r > 0);
- else if (r != -ENXIO)
- log_warning_errno(r, "Failed to parse $SYSTEMD_NSPAWN_TMPFS_TMP, ignoring: %m");
e = getenv("SYSTEMD_NSPAWN_API_VFS_WRITABLE");
- if (!e)
- return;
-
- if (streq(e, "network")) {
+ if (streq_ptr(e, "network"))
arg_mount_settings |= MOUNT_APPLY_APIVFS_RO|MOUNT_APPLY_APIVFS_NETNS;
- return;
- }
- r = parse_boolean(e);
- if (r < 0) {
- log_warning_errno(r, "Failed to parse SYSTEMD_NSPAWN_API_VFS_WRITABLE from environment, ignoring.");
- return;
+ else if (e) {
+ r = parse_boolean(e);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse $SYSTEMD_NSPAWN_API_VFS_WRITABLE: %m");
+
+ SET_FLAG(arg_mount_settings, MOUNT_APPLY_APIVFS_RO, r == 0);
+ SET_FLAG(arg_mount_settings, MOUNT_APPLY_APIVFS_NETNS, false);
}
- SET_FLAG(arg_mount_settings, MOUNT_APPLY_APIVFS_RO, r == 0);
- SET_FLAG(arg_mount_settings, MOUNT_APPLY_APIVFS_NETNS, false);
+ return 0;
}
-static void parse_environment(void) {
+static int parse_environment(void) {
const char *e;
int r;
- parse_share_ns_env("SYSTEMD_NSPAWN_SHARE_NS_IPC", CLONE_NEWIPC);
- parse_share_ns_env("SYSTEMD_NSPAWN_SHARE_NS_PID", CLONE_NEWPID);
- parse_share_ns_env("SYSTEMD_NSPAWN_SHARE_NS_UTS", CLONE_NEWUTS);
- parse_share_ns_env("SYSTEMD_NSPAWN_SHARE_SYSTEM", CLONE_NEWIPC|CLONE_NEWPID|CLONE_NEWUTS);
+ r = parse_share_ns_env("SYSTEMD_NSPAWN_SHARE_NS_IPC", CLONE_NEWIPC);
+ if (r < 0)
+ return r;
+ r = parse_share_ns_env("SYSTEMD_NSPAWN_SHARE_NS_PID", CLONE_NEWPID);
+ if (r < 0)
+ return r;
+ r = parse_share_ns_env("SYSTEMD_NSPAWN_SHARE_NS_UTS", CLONE_NEWUTS);
+ if (r < 0)
+ return r;
+ r = parse_share_ns_env("SYSTEMD_NSPAWN_SHARE_SYSTEM", CLONE_NEWIPC|CLONE_NEWPID|CLONE_NEWUTS);
+ if (r < 0)
+ return r;
- parse_mount_settings_env();
+ r = parse_mount_settings_env();
+ if (r < 0)
+ return r;
/* SYSTEMD_NSPAWN_USE_CGNS=0 can be used to disable CLONE_NEWCGROUP use,
* even if it is supported. If not supported, it has no effect. */
r = getenv_bool("SYSTEMD_NSPAWN_USE_CGNS");
if (r < 0) {
if (r != -ENXIO)
- log_warning_errno(r, "Failed to parse $SYSTEMD_NSPAWN_USE_CGNS, ignoring: %m");
+ return log_error_errno(r, "Failed to parse $SYSTEMD_NSPAWN_USE_CGNS: %m");
arg_use_cgns = true;
} else {
if (e)
arg_container_service_name = e;
- detect_unified_cgroup_hierarchy_from_environment();
+ return detect_unified_cgroup_hierarchy_from_environment();
}
static int parse_argv(int argc, char *argv[]) {
arg_caps_retain = (arg_caps_retain | plus | (arg_private_network ? UINT64_C(1) << CAP_NET_ADMIN : 0)) & ~minus;
/* Make sure to parse environment before we reset the settings mask below */
- parse_environment();
+ r = parse_environment();
+ if (r < 0)
+ return r;
/* Load all settings from .nspawn files */
if (mask_no_settings)
static int verify_arguments(void) {
int r;
+ if (arg_start_mode == START_PID2 && arg_unified_cgroup_hierarchy == CGROUP_UNIFIED_UNKNOWN) {
+ /* If we are running the stub init in the container, we don't need to look at what the init
+ * in the container supports, because we are not using it. Let's immediately pick the right
+ * setting based on the host system configuration.
+ *
+ * We only do this, if the user didn't use an environment variable to override the detection.
+ */
+
+ r = cg_all_unified();
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine whether we are in all unified mode.");
+ if (r > 0)
+ arg_unified_cgroup_hierarchy = CGROUP_UNIFIED_ALL;
+ else if (cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER) > 0)
+ arg_unified_cgroup_hierarchy = CGROUP_UNIFIED_SYSTEMD;
+ else
+ arg_unified_cgroup_hierarchy = CGROUP_UNIFIED_NONE;
+ }
+
if (arg_userns_mode != USER_NAMESPACE_NO)
arg_mount_settings |= MOUNT_USE_USERNS;
if (r < 0)
goto finish;
- r = cg_unified_flush();
+ r = cg_unified();
if (r < 0) {
log_error_errno(r, "Failed to determine whether the unified cgroups hierarchy is used: %m");
goto finish;
if (r < 0)
goto finish;
- r = detect_unified_cgroup_hierarchy_from_environment();
- if (r < 0)
- goto finish;
+ /* Reapply environment settings. */
+ (void) detect_unified_cgroup_hierarchy_from_environment();
/* Ignore SIGPIPE here, because we use splice() on the ptyfwd stuff and that will generate SIGPIPE if
* the result is closed. Note that the container payload child will reset signal mask+handler anyway,
#include "log.h"
#include "nspawn-patch-uid.h"
#include "user-util.h"
+#include "string-util.h"
#include "tests.h"
-#include "util.h"
int main(int argc, char *argv[]) {
uid_t shift, range;
#include "fd-util.h"
#include "fileio.h"
#include "io-util.h"
+#include "list.h"
#include "main-func.h"
#include "mkdir.h"
#include "parse-util.h"
-#include "proc-cmdline.h"
+#include "reboot-util.h"
#include "string-table.h"
#include "string-util.h"
#include "udev-util.h"
#include "util.h"
-#include "list.h"
/* Note that any write is delayed until exit and the rfkill state will not be
* stored for rfkill indices that disappear after a change. */
#include "acpi-fpdt.h"
#include "boot-timestamps.h"
-#include "efivars.h"
+#include "efi-loader.h"
#include "macro.h"
#include "time-util.h"
#include "device-nodes.h"
#include "dirent-util.h"
#include "efivars.h"
+#include "efi-loader.h"
#include "env-file.h"
#include "env-util.h"
#include "fd-util.h"
_cleanup_free_ char *j = NULL, *osrelease = NULL, *cmdline = NULL;
_cleanup_close_ int fd = -1;
+ dirent_ensure_type(d, de);
if (!dirent_is_file(de))
continue;
#include "bus-unit-util.h"
#include "bus-util.h"
#include "cap-list.h"
+#include "cgroup-setup.h"
#include "cgroup-util.h"
#include "condition.h"
#include "cpu-set-util.h"
static int bus_append_kill_property(sd_bus_message *m, const char *field, const char *eq) {
if (streq(field, "KillMode"))
-
return bus_append_string(m, field, eq);
if (STR_IN_SET(field, "SendSIGHUP", "SendSIGKILL"))
-
return bus_append_parse_boolean(m, field, eq);
- if (STR_IN_SET(field, "KillSignal", "FinalKillSignal", "WatchdogSignal"))
-
+ if (STR_IN_SET(field, "KillSignal", "RestartKillSignal", "FinalKillSignal", "WatchdogSignal"))
return bus_append_signal_from_string(m, field, eq);
return 0;
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <ftw.h>
+#include <unistd.h>
+
+#include "cgroup-setup.h"
+#include "cgroup-util.h"
+#include "errno-util.h"
+#include "parse-util.h"
+#include "path-util.h"
+#include "proc-cmdline.h"
+#include "stdio-util.h"
+#include "string-util.h"
+#include "fs-util.h"
+#include "mkdir.h"
+#include "process-util.h"
+#include "fileio.h"
+#include "user-util.h"
+#include "fd-util.h"
+
+bool cg_is_unified_wanted(void) {
+ static thread_local int wanted = -1;
+ bool b;
+ const bool is_default = DEFAULT_HIERARCHY == CGROUP_UNIFIED_ALL;
+ _cleanup_free_ char *c = NULL;
+ int r;
+
+ /* If we have a cached value, return that. */
+ if (wanted >= 0)
+ return wanted;
+
+ /* If the hierarchy is already mounted, then follow whatever was chosen for it. */
+ r = cg_unified_cached(true);
+ if (r >= 0)
+ return (wanted = r >= CGROUP_UNIFIED_ALL);
+
+ /* If we were explicitly passed systemd.unified_cgroup_hierarchy, respect that. */
+ r = proc_cmdline_get_bool("systemd.unified_cgroup_hierarchy", &b);
+ if (r > 0)
+ return (wanted = b);
+
+ /* If we passed cgroup_no_v1=all with no other instructions, it seems highly unlikely that we want to
+ * use hybrid or legacy hierarchy. */
+ r = proc_cmdline_get_key("cgroup_no_v1", 0, &c);
+ if (r > 0 && streq_ptr(c, "all"))
+ return (wanted = true);
+
+ return (wanted = is_default);
+}
+
+bool cg_is_legacy_wanted(void) {
+ static thread_local int wanted = -1;
+
+ /* If we have a cached value, return that. */
+ if (wanted >= 0)
+ return wanted;
+
+ /* Check if we have cgroup v2 already mounted. */
+ if (cg_unified_cached(true) == CGROUP_UNIFIED_ALL)
+ return (wanted = false);
+
+ /* Otherwise, assume that at least partial legacy is wanted,
+ * since cgroup v2 should already be mounted at this point. */
+ return (wanted = true);
+}
+
+bool cg_is_hybrid_wanted(void) {
+ static thread_local int wanted = -1;
+ int r;
+ bool b;
+ const bool is_default = DEFAULT_HIERARCHY >= CGROUP_UNIFIED_SYSTEMD;
+ /* We default to true if the default is "hybrid", obviously, but also when the default is "unified",
+ * because if we get called, it means that unified hierarchy was not mounted. */
+
+ /* If we have a cached value, return that. */
+ if (wanted >= 0)
+ return wanted;
+
+ /* If the hierarchy is already mounted, then follow whatever was chosen for it. */
+ if (cg_unified_cached(true) == CGROUP_UNIFIED_ALL)
+ return (wanted = false);
+
+ /* Otherwise, let's see what the kernel command line has to say. Since checking is expensive, cache
+ * a non-error result. */
+ r = proc_cmdline_get_bool("systemd.legacy_systemd_cgroup_controller", &b);
+
+ /* The meaning of the kernel option is reversed wrt. to the return value of this function, hence the
+ * negation. */
+ return (wanted = r > 0 ? !b : is_default);
+}
+
+int cg_weight_parse(const char *s, uint64_t *ret) {
+ uint64_t u;
+ int r;
+
+ if (isempty(s)) {
+ *ret = CGROUP_WEIGHT_INVALID;
+ return 0;
+ }
+
+ r = safe_atou64(s, &u);
+ if (r < 0)
+ return r;
+
+ if (u < CGROUP_WEIGHT_MIN || u > CGROUP_WEIGHT_MAX)
+ return -ERANGE;
+
+ *ret = u;
+ return 0;
+}
+
+int cg_cpu_shares_parse(const char *s, uint64_t *ret) {
+ uint64_t u;
+ int r;
+
+ if (isempty(s)) {
+ *ret = CGROUP_CPU_SHARES_INVALID;
+ return 0;
+ }
+
+ r = safe_atou64(s, &u);
+ if (r < 0)
+ return r;
+
+ if (u < CGROUP_CPU_SHARES_MIN || u > CGROUP_CPU_SHARES_MAX)
+ return -ERANGE;
+
+ *ret = u;
+ return 0;
+}
+
+int cg_blkio_weight_parse(const char *s, uint64_t *ret) {
+ uint64_t u;
+ int r;
+
+ if (isempty(s)) {
+ *ret = CGROUP_BLKIO_WEIGHT_INVALID;
+ return 0;
+ }
+
+ r = safe_atou64(s, &u);
+ if (r < 0)
+ return r;
+
+ if (u < CGROUP_BLKIO_WEIGHT_MIN || u > CGROUP_BLKIO_WEIGHT_MAX)
+ return -ERANGE;
+
+ *ret = u;
+ return 0;
+}
+
+
+static int trim_cb(const char *path, const struct stat *sb, int typeflag, struct FTW *ftwbuf) {
+ assert(path);
+ assert(sb);
+ assert(ftwbuf);
+
+ if (typeflag != FTW_DP)
+ return 0;
+
+ if (ftwbuf->level < 1)
+ return 0;
+
+ (void) rmdir(path);
+ return 0;
+}
+
+int cg_trim(const char *controller, const char *path, bool delete_root) {
+ _cleanup_free_ char *fs = NULL;
+ int r = 0, q;
+
+ assert(path);
+
+ r = cg_get_path(controller, path, NULL, &fs);
+ if (r < 0)
+ return r;
+
+ errno = 0;
+ if (nftw(fs, trim_cb, 64, FTW_DEPTH|FTW_MOUNT|FTW_PHYS) != 0) {
+ if (errno == ENOENT)
+ r = 0;
+ else
+ r = errno_or_else(EIO);
+ }
+
+ if (delete_root) {
+ if (rmdir(fs) < 0 && errno != ENOENT)
+ return -errno;
+ }
+
+ q = cg_hybrid_unified();
+ if (q < 0)
+ return q;
+ if (q > 0 && streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
+ q = cg_trim(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path, delete_root);
+ if (q < 0)
+ log_warning_errno(q, "Failed to trim compat systemd cgroup %s: %m", path);
+ }
+
+ return r;
+}
+
+/* Create a cgroup in the hierarchy of controller.
+ * Returns 0 if the group already existed, 1 on success, negative otherwise.
+ */
+int cg_create(const char *controller, const char *path) {
+ _cleanup_free_ char *fs = NULL;
+ int r;
+
+ r = cg_get_path_and_check(controller, path, NULL, &fs);
+ if (r < 0)
+ return r;
+
+ r = mkdir_parents(fs, 0755);
+ if (r < 0)
+ return r;
+
+ r = mkdir_errno_wrapper(fs, 0755);
+ if (r == -EEXIST)
+ return 0;
+ if (r < 0)
+ return r;
+
+ r = cg_hybrid_unified();
+ if (r < 0)
+ return r;
+
+ if (r > 0 && streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
+ r = cg_create(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path);
+ if (r < 0)
+ log_warning_errno(r, "Failed to create compat systemd cgroup %s: %m", path);
+ }
+
+ return 1;
+}
+
+int cg_create_and_attach(const char *controller, const char *path, pid_t pid) {
+ int r, q;
+
+ assert(pid >= 0);
+
+ r = cg_create(controller, path);
+ if (r < 0)
+ return r;
+
+ q = cg_attach(controller, path, pid);
+ if (q < 0)
+ return q;
+
+ /* This does not remove the cgroup on failure */
+ return r;
+}
+
+int cg_attach(const char *controller, const char *path, pid_t pid) {
+ _cleanup_free_ char *fs = NULL;
+ char c[DECIMAL_STR_MAX(pid_t) + 2];
+ int r;
+
+ assert(path);
+ assert(pid >= 0);
+
+ r = cg_get_path_and_check(controller, path, "cgroup.procs", &fs);
+ if (r < 0)
+ return r;
+
+ if (pid == 0)
+ pid = getpid_cached();
+
+ xsprintf(c, PID_FMT "\n", pid);
+
+ r = write_string_file(fs, c, WRITE_STRING_FILE_DISABLE_BUFFER);
+ if (r < 0)
+ return r;
+
+ r = cg_hybrid_unified();
+ if (r < 0)
+ return r;
+
+ if (r > 0 && streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
+ r = cg_attach(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path, pid);
+ if (r < 0)
+ log_warning_errno(r, "Failed to attach "PID_FMT" to compat systemd cgroup %s: %m", pid, path);
+ }
+
+ return 0;
+}
+
+int cg_attach_fallback(const char *controller, const char *path, pid_t pid) {
+ int r;
+
+ assert(controller);
+ assert(path);
+ assert(pid >= 0);
+
+ r = cg_attach(controller, path, pid);
+ if (r < 0) {
+ char prefix[strlen(path) + 1];
+
+ /* This didn't work? Then let's try all prefixes of
+ * the destination */
+
+ PATH_FOREACH_PREFIX(prefix, path) {
+ int q;
+
+ q = cg_attach(controller, prefix, pid);
+ if (q >= 0)
+ return q;
+ }
+ }
+
+ return r;
+}
+
+int cg_set_access(
+ const char *controller,
+ const char *path,
+ uid_t uid,
+ gid_t gid) {
+
+ struct Attribute {
+ const char *name;
+ bool fatal;
+ };
+
+ /* cgroup v1, aka legacy/non-unified */
+ static const struct Attribute legacy_attributes[] = {
+ { "cgroup.procs", true },
+ { "tasks", false },
+ { "cgroup.clone_children", false },
+ {},
+ };
+
+ /* cgroup v2, aka unified */
+ static const struct Attribute unified_attributes[] = {
+ { "cgroup.procs", true },
+ { "cgroup.subtree_control", true },
+ { "cgroup.threads", false },
+ {},
+ };
+
+ static const struct Attribute* const attributes[] = {
+ [false] = legacy_attributes,
+ [true] = unified_attributes,
+ };
+
+ _cleanup_free_ char *fs = NULL;
+ const struct Attribute *i;
+ int r, unified;
+
+ assert(path);
+
+ if (uid == UID_INVALID && gid == GID_INVALID)
+ return 0;
+
+ unified = cg_unified_controller(controller);
+ if (unified < 0)
+ return unified;
+
+ /* Configure access to the cgroup itself */
+ r = cg_get_path(controller, path, NULL, &fs);
+ if (r < 0)
+ return r;
+
+ r = chmod_and_chown(fs, 0755, uid, gid);
+ if (r < 0)
+ return r;
+
+ /* Configure access to the cgroup's attributes */
+ for (i = attributes[unified]; i->name; i++) {
+ fs = mfree(fs);
+
+ r = cg_get_path(controller, path, i->name, &fs);
+ if (r < 0)
+ return r;
+
+ r = chmod_and_chown(fs, 0644, uid, gid);
+ if (r < 0) {
+ if (i->fatal)
+ return r;
+
+ log_debug_errno(r, "Failed to set access on cgroup %s, ignoring: %m", fs);
+ }
+ }
+
+ if (streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
+ r = cg_hybrid_unified();
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ /* Always propagate access mode from unified to legacy controller */
+ r = cg_set_access(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path, uid, gid);
+ if (r < 0)
+ log_debug_errno(r, "Failed to set access on compatibility systemd cgroup %s, ignoring: %m", path);
+ }
+ }
+
+ return 0;
+}
+
+int cg_migrate(
+ const char *cfrom,
+ const char *pfrom,
+ const char *cto,
+ const char *pto,
+ CGroupFlags flags) {
+
+ bool done = false;
+ _cleanup_set_free_ Set *s = NULL;
+ int r, ret = 0;
+ pid_t my_pid;
+
+ assert(cfrom);
+ assert(pfrom);
+ assert(cto);
+ assert(pto);
+
+ s = set_new(NULL);
+ if (!s)
+ return -ENOMEM;
+
+ my_pid = getpid_cached();
+
+ do {
+ _cleanup_fclose_ FILE *f = NULL;
+ pid_t pid = 0;
+ done = true;
+
+ r = cg_enumerate_processes(cfrom, pfrom, &f);
+ if (r < 0) {
+ if (ret >= 0 && r != -ENOENT)
+ return r;
+
+ return ret;
+ }
+
+ while ((r = cg_read_pid(f, &pid)) > 0) {
+
+ /* This might do weird stuff if we aren't a
+ * single-threaded program. However, we
+ * luckily know we are not */
+ if ((flags & CGROUP_IGNORE_SELF) && pid == my_pid)
+ continue;
+
+ if (set_get(s, PID_TO_PTR(pid)) == PID_TO_PTR(pid))
+ continue;
+
+ /* Ignore kernel threads. Since they can only
+ * exist in the root cgroup, we only check for
+ * them there. */
+ if (cfrom &&
+ empty_or_root(pfrom) &&
+ is_kernel_thread(pid) > 0)
+ continue;
+
+ r = cg_attach(cto, pto, pid);
+ if (r < 0) {
+ if (ret >= 0 && r != -ESRCH)
+ ret = r;
+ } else if (ret == 0)
+ ret = 1;
+
+ done = false;
+
+ r = set_put(s, PID_TO_PTR(pid));
+ if (r < 0) {
+ if (ret >= 0)
+ return r;
+
+ return ret;
+ }
+ }
+
+ if (r < 0) {
+ if (ret >= 0)
+ return r;
+
+ return ret;
+ }
+ } while (!done);
+
+ return ret;
+}
+
+int cg_migrate_recursive(
+ const char *cfrom,
+ const char *pfrom,
+ const char *cto,
+ const char *pto,
+ CGroupFlags flags) {
+
+ _cleanup_closedir_ DIR *d = NULL;
+ int r, ret = 0;
+ char *fn;
+
+ assert(cfrom);
+ assert(pfrom);
+ assert(cto);
+ assert(pto);
+
+ ret = cg_migrate(cfrom, pfrom, cto, pto, flags);
+
+ r = cg_enumerate_subgroups(cfrom, pfrom, &d);
+ if (r < 0) {
+ if (ret >= 0 && r != -ENOENT)
+ return r;
+
+ return ret;
+ }
+
+ while ((r = cg_read_subgroup(d, &fn)) > 0) {
+ _cleanup_free_ char *p = NULL;
+
+ p = path_join(empty_to_root(pfrom), fn);
+ free(fn);
+ if (!p)
+ return -ENOMEM;
+
+ r = cg_migrate_recursive(cfrom, p, cto, pto, flags);
+ if (r != 0 && ret >= 0)
+ ret = r;
+ }
+
+ if (r < 0 && ret >= 0)
+ ret = r;
+
+ if (flags & CGROUP_REMOVE) {
+ r = cg_rmdir(cfrom, pfrom);
+ if (r < 0 && ret >= 0 && !IN_SET(r, -ENOENT, -EBUSY))
+ return r;
+ }
+
+ return ret;
+}
+
+int cg_migrate_recursive_fallback(
+ const char *cfrom,
+ const char *pfrom,
+ const char *cto,
+ const char *pto,
+ CGroupFlags flags) {
+
+ int r;
+
+ assert(cfrom);
+ assert(pfrom);
+ assert(cto);
+ assert(pto);
+
+ r = cg_migrate_recursive(cfrom, pfrom, cto, pto, flags);
+ if (r < 0) {
+ char prefix[strlen(pto) + 1];
+
+ /* This didn't work? Then let's try all prefixes of the destination */
+
+ PATH_FOREACH_PREFIX(prefix, pto) {
+ int q;
+
+ q = cg_migrate_recursive(cfrom, pfrom, cto, prefix, flags);
+ if (q >= 0)
+ return q;
+ }
+ }
+
+ return r;
+}
+
+int cg_create_everywhere(CGroupMask supported, CGroupMask mask, const char *path) {
+ CGroupController c;
+ CGroupMask done;
+ bool created;
+ int r;
+
+ /* This one will create a cgroup in our private tree, but also
+ * duplicate it in the trees specified in mask, and remove it
+ * in all others.
+ *
+ * Returns 0 if the group already existed in the systemd hierarchy,
+ * 1 on success, negative otherwise.
+ */
+
+ /* First create the cgroup in our own hierarchy. */
+ r = cg_create(SYSTEMD_CGROUP_CONTROLLER, path);
+ if (r < 0)
+ return r;
+ created = r;
+
+ /* If we are in the unified hierarchy, we are done now */
+ r = cg_all_unified();
+ if (r < 0)
+ return r;
+ if (r > 0)
+ return created;
+
+ supported &= CGROUP_MASK_V1;
+ mask = CGROUP_MASK_EXTEND_JOINED(mask);
+ done = 0;
+
+ /* Otherwise, do the same in the other hierarchies */
+ for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
+ CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
+ const char *n;
+
+ if (!FLAGS_SET(supported, bit))
+ continue;
+
+ if (FLAGS_SET(done, bit))
+ continue;
+
+ n = cgroup_controller_to_string(c);
+ if (FLAGS_SET(mask, bit))
+ (void) cg_create(n, path);
+ else
+ (void) cg_trim(n, path, true);
+
+ done |= CGROUP_MASK_EXTEND_JOINED(bit);
+ }
+
+ return created;
+}
+
+int cg_attach_everywhere(CGroupMask supported, const char *path, pid_t pid, cg_migrate_callback_t path_callback, void *userdata) {
+ CGroupController c;
+ CGroupMask done;
+ int r;
+
+ r = cg_attach(SYSTEMD_CGROUP_CONTROLLER, path, pid);
+ if (r < 0)
+ return r;
+
+ r = cg_all_unified();
+ if (r < 0)
+ return r;
+ if (r > 0)
+ return 0;
+
+ supported &= CGROUP_MASK_V1;
+ done = 0;
+
+ for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
+ CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
+ const char *p = NULL;
+
+ if (!FLAGS_SET(supported, bit))
+ continue;
+
+ if (FLAGS_SET(done, bit))
+ continue;
+
+ if (path_callback)
+ p = path_callback(bit, userdata);
+ if (!p)
+ p = path;
+
+ (void) cg_attach_fallback(cgroup_controller_to_string(c), p, pid);
+ done |= CGROUP_MASK_EXTEND_JOINED(bit);
+ }
+
+ return 0;
+}
+
+int cg_attach_many_everywhere(CGroupMask supported, const char *path, Set* pids, cg_migrate_callback_t path_callback, void *userdata) {
+ Iterator i;
+ void *pidp;
+ int r = 0;
+
+ SET_FOREACH(pidp, pids, i) {
+ pid_t pid = PTR_TO_PID(pidp);
+ int q;
+
+ q = cg_attach_everywhere(supported, path, pid, path_callback, userdata);
+ if (q < 0 && r >= 0)
+ r = q;
+ }
+
+ return r;
+}
+
+int cg_migrate_everywhere(CGroupMask supported, const char *from, const char *to, cg_migrate_callback_t to_callback, void *userdata) {
+ CGroupController c;
+ CGroupMask done;
+ int r = 0, q;
+
+ if (!path_equal(from, to)) {
+ r = cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, from, SYSTEMD_CGROUP_CONTROLLER, to, CGROUP_REMOVE);
+ if (r < 0)
+ return r;
+ }
+
+ q = cg_all_unified();
+ if (q < 0)
+ return q;
+ if (q > 0)
+ return r;
+
+ supported &= CGROUP_MASK_V1;
+ done = 0;
+
+ for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
+ CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
+ const char *p = NULL;
+
+ if (!FLAGS_SET(supported, bit))
+ continue;
+
+ if (FLAGS_SET(done, bit))
+ continue;
+
+ if (to_callback)
+ p = to_callback(bit, userdata);
+ if (!p)
+ p = to;
+
+ (void) cg_migrate_recursive_fallback(SYSTEMD_CGROUP_CONTROLLER, to, cgroup_controller_to_string(c), p, 0);
+ done |= CGROUP_MASK_EXTEND_JOINED(bit);
+ }
+
+ return r;
+}
+
+int cg_trim_everywhere(CGroupMask supported, const char *path, bool delete_root) {
+ CGroupController c;
+ CGroupMask done;
+ int r, q;
+
+ r = cg_trim(SYSTEMD_CGROUP_CONTROLLER, path, delete_root);
+ if (r < 0)
+ return r;
+
+ q = cg_all_unified();
+ if (q < 0)
+ return q;
+ if (q > 0)
+ return r;
+
+ supported &= CGROUP_MASK_V1;
+ done = 0;
+
+ for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
+ CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
+
+ if (!FLAGS_SET(supported, bit))
+ continue;
+
+ if (FLAGS_SET(done, bit))
+ continue;
+
+ (void) cg_trim(cgroup_controller_to_string(c), path, delete_root);
+ done |= CGROUP_MASK_EXTEND_JOINED(bit);
+ }
+
+ return r;
+}
+
+int cg_enable_everywhere(
+ CGroupMask supported,
+ CGroupMask mask,
+ const char *p,
+ CGroupMask *ret_result_mask) {
+
+ _cleanup_fclose_ FILE *f = NULL;
+ _cleanup_free_ char *fs = NULL;
+ CGroupController c;
+ CGroupMask ret = 0;
+ int r;
+
+ assert(p);
+
+ if (supported == 0) {
+ if (ret_result_mask)
+ *ret_result_mask = 0;
+ return 0;
+ }
+
+ r = cg_all_unified();
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ /* On the legacy hierarchy there's no concept of "enabling" controllers in cgroups defined. Let's claim
+ * complete success right away. (If you wonder why we return the full mask here, rather than zero: the
+ * caller tends to use the returned mask later on to compare if all controllers where properly joined,
+ * and if not requeues realization. This use is the primary purpose of the return value, hence let's
+ * minimize surprises here and reduce triggers for re-realization by always saying we fully
+ * succeeded.) */
+ if (ret_result_mask)
+ *ret_result_mask = mask & supported & CGROUP_MASK_V2; /* If you wonder why we mask this with
+ * CGROUP_MASK_V2: The 'supported' mask
+ * might contain pure-V1 or BPF
+ * controllers, and we never want to
+ * claim that we could enable those with
+ * cgroup.subtree_control */
+ return 0;
+ }
+
+ r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, p, "cgroup.subtree_control", &fs);
+ if (r < 0)
+ return r;
+
+ for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
+ CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
+ const char *n;
+
+ if (!FLAGS_SET(CGROUP_MASK_V2, bit))
+ continue;
+
+ if (!FLAGS_SET(supported, bit))
+ continue;
+
+ n = cgroup_controller_to_string(c);
+ {
+ char s[1 + strlen(n) + 1];
+
+ s[0] = FLAGS_SET(mask, bit) ? '+' : '-';
+ strcpy(s + 1, n);
+
+ if (!f) {
+ f = fopen(fs, "we");
+ if (!f)
+ return log_debug_errno(errno, "Failed to open cgroup.subtree_control file of %s: %m", p);
+ }
+
+ r = write_string_stream(f, s, WRITE_STRING_FILE_DISABLE_BUFFER);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to %s controller %s for %s (%s): %m",
+ FLAGS_SET(mask, bit) ? "enable" : "disable", n, p, fs);
+ clearerr(f);
+
+ /* If we can't turn off a controller, leave it on in the reported resulting mask. This
+ * happens for example when we attempt to turn off a controller up in the tree that is
+ * used down in the tree. */
+ if (!FLAGS_SET(mask, bit) && r == -EBUSY) /* You might wonder why we check for EBUSY
+ * only here, and not follow the same logic
+ * for other errors such as EINVAL or
+ * EOPNOTSUPP or anything else. That's
+ * because EBUSY indicates that the
+ * controllers is currently enabled and
+ * cannot be disabled because something down
+ * the hierarchy is still using it. Any other
+ * error most likely means something like "I
+ * never heard of this controller" or
+ * similar. In the former case it's hence
+ * safe to assume the controller is still on
+ * after the failed operation, while in the
+ * latter case it's safer to assume the
+ * controller is unknown and hence certainly
+ * not enabled. */
+ ret |= bit;
+ } else {
+ /* Otherwise, if we managed to turn on a controller, set the bit reflecting that. */
+ if (FLAGS_SET(mask, bit))
+ ret |= bit;
+ }
+ }
+ }
+
+ /* Let's return the precise set of controllers now enabled for the cgroup. */
+ if (ret_result_mask)
+ *ret_result_mask = ret;
+
+ return 0;
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include "cgroup-util.h"
+
+bool cg_is_unified_wanted(void);
+bool cg_is_legacy_wanted(void);
+bool cg_is_hybrid_wanted(void);
+
+int cg_weight_parse(const char *s, uint64_t *ret);
+int cg_cpu_shares_parse(const char *s, uint64_t *ret);
+int cg_blkio_weight_parse(const char *s, uint64_t *ret);
+
+int cg_trim(const char *controller, const char *path, bool delete_root);
+
+int cg_create(const char *controller, const char *path);
+int cg_attach(const char *controller, const char *path, pid_t pid);
+int cg_attach_fallback(const char *controller, const char *path, pid_t pid);
+int cg_create_and_attach(const char *controller, const char *path, pid_t pid);
+
+int cg_migrate(const char *cfrom, const char *pfrom, const char *cto, const char *pto, CGroupFlags flags);
+int cg_migrate_recursive(const char *cfrom, const char *pfrom, const char *cto, const char *pto, CGroupFlags flags);
+int cg_migrate_recursive_fallback(const char *cfrom, const char *pfrom, const char *cto, const char *pto, CGroupFlags flags);
+
+int cg_create_everywhere(CGroupMask supported, CGroupMask mask, const char *path);
+int cg_attach_everywhere(CGroupMask supported, const char *path, pid_t pid, cg_migrate_callback_t callback, void *userdata);
+int cg_attach_many_everywhere(CGroupMask supported, const char *path, Set* pids, cg_migrate_callback_t callback, void *userdata);
+int cg_migrate_everywhere(CGroupMask supported, const char *from, const char *to, cg_migrate_callback_t callback, void *userdata);
+int cg_trim_everywhere(CGroupMask supported, const char *path, bool delete_root);
+int cg_enable_everywhere(CGroupMask supported, CGroupMask mask, const char *p, CGroupMask *ret_result_mask);
#include "cgroup-util.h"
#include "condition.h"
#include "cpu-set-util.h"
-#include "efivars.h"
+#include "efi-loader.h"
#include "env-file.h"
#include "extract-word.h"
#include "fd-util.h"
#include "macro.h"
-/* libcryptsetup define for any LUKS version, compatible with libcryptsetup 1.x */
-#ifndef CRYPT_LUKS
-#define CRYPT_LUKS NULL
-#endif
-
-#ifndef CRYPT_ACTIVATE_SAME_CPU_CRYPT
-#define CRYPT_ACTIVATE_SAME_CPU_CRYPT (1 << 6)
-#endif
-
-#ifndef CRYPT_ACTIVATE_SUBMIT_FROM_CRYPT_CPUS
-#define CRYPT_ACTIVATE_SUBMIT_FROM_CRYPT_CPUS (1 << 7)
-#endif
-
DEFINE_TRIVIAL_CLEANUP_FUNC(struct crypt_device *, crypt_free);
void cryptsetup_log_glue(int level, const char *msg, void *usrptr);
/* SPDX-License-Identifier: LGPL-2.1+ */
-#include <dirent.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <limits.h>
-#include <linux/fs.h>
-#include <stdio.h>
#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
#include <unistd.h>
-#include "sd-id128.h"
-
#include "alloc-util.h"
-#include "chattr-util.h"
#include "dirent-util.h"
+#include "efi-loader.h"
#include "efivars.h"
#include "fd-util.h"
#include "io-util.h"
-#include "macro.h"
#include "parse-util.h"
#include "sort-util.h"
#include "stdio-util.h"
-#include "strv.h"
-#include "time-util.h"
+#include "string-util.h"
#include "utf8.h"
#include "virt.h"
return 0;
}
-char* efi_variable_path(sd_id128_t vendor, const char *name) {
- char *p;
-
- if (asprintf(&p,
- "/sys/firmware/efi/efivars/%s-" SD_ID128_UUID_FORMAT_STR,
- name, SD_ID128_FORMAT_VAL(vendor)) < 0)
- return NULL;
-
- return p;
-}
-
-int efi_get_variable(
- sd_id128_t vendor,
- const char *name,
- uint32_t *ret_attribute,
- void **ret_value,
- size_t *ret_size) {
-
- _cleanup_close_ int fd = -1;
- _cleanup_free_ char *p = NULL;
- _cleanup_free_ void *buf = NULL;
- struct stat st;
- uint32_t a;
- ssize_t n;
-
- assert(name);
-
- p = efi_variable_path(vendor, name);
- if (!p)
- return -ENOMEM;
-
- if (!ret_value && !ret_size && !ret_attribute) {
- /* If caller is not interested in anything, just check if the variable exists and is readable
- * to us. */
- if (access(p, R_OK) < 0)
- return -errno;
-
- return 0;
- }
-
- fd = open(p, O_RDONLY|O_NOCTTY|O_CLOEXEC);
- if (fd < 0)
- return -errno;
-
- if (fstat(fd, &st) < 0)
- return -errno;
- if (st.st_size < 4)
- return -ENODATA;
- if (st.st_size > 4*1024*1024 + 4)
- return -E2BIG;
-
- if (ret_value || ret_attribute) {
- n = read(fd, &a, sizeof(a));
- if (n < 0)
- return -errno;
- if (n != sizeof(a))
- return -EIO;
- }
-
- if (ret_value) {
- buf = malloc(st.st_size - 4 + 2);
- if (!buf)
- return -ENOMEM;
-
- n = read(fd, buf, (size_t) st.st_size - 4);
- if (n < 0)
- return -errno;
- if (n != st.st_size - 4)
- return -EIO;
-
- /* Always NUL terminate (2 bytes, to protect UTF-16) */
- ((char*) buf)[st.st_size - 4] = 0;
- ((char*) buf)[st.st_size - 4 + 1] = 0;
- }
-
- /* Note that efivarfs interestingly doesn't require ftruncate() to update an existing EFI variable
- * with a smaller value. */
-
- if (ret_attribute)
- *ret_attribute = a;
-
- if (ret_value)
- *ret_value = TAKE_PTR(buf);
-
- if (ret_size)
- *ret_size = (size_t) st.st_size - 4;
-
- return 0;
-}
-
-int efi_get_variable_string(sd_id128_t vendor, const char *name, char **p) {
- _cleanup_free_ void *s = NULL;
- size_t ss = 0;
- int r;
- char *x;
-
- r = efi_get_variable(vendor, name, NULL, &s, &ss);
- if (r < 0)
- return r;
-
- x = utf16_to_utf8(s, ss);
- if (!x)
- return -ENOMEM;
-
- *p = x;
- return 0;
-}
-
-int efi_set_variable(
- sd_id128_t vendor,
- const char *name,
- const void *value,
- size_t size) {
-
- struct var {
- uint32_t attr;
- char buf[];
- } _packed_ * _cleanup_free_ buf = NULL;
- _cleanup_free_ char *p = NULL;
- _cleanup_close_ int fd = -1;
- bool saved_flags_valid = false;
- unsigned saved_flags;
- int r;
-
- assert(name);
- assert(value || size == 0);
-
- p = efi_variable_path(vendor, name);
- if (!p)
- return -ENOMEM;
-
- /* Newer efivarfs protects variables that are not in a whitelist with FS_IMMUTABLE_FL by default, to protect
- * them for accidental removal and modification. We are not changing these variables accidentally however,
- * hence let's unset the bit first. */
-
- r = chattr_path(p, 0, FS_IMMUTABLE_FL, &saved_flags);
- if (r < 0 && r != -ENOENT)
- log_debug_errno(r, "Failed to drop FS_IMMUTABLE_FL flag from '%s', ignoring: %m", p);
-
- saved_flags_valid = r >= 0;
-
- if (size == 0) {
- if (unlink(p) < 0) {
- r = -errno;
- goto finish;
- }
-
- return 0;
- }
-
- fd = open(p, O_WRONLY|O_CREAT|O_NOCTTY|O_CLOEXEC, 0644);
- if (fd < 0) {
- r = -errno;
- goto finish;
- }
-
- buf = malloc(sizeof(uint32_t) + size);
- if (!buf) {
- r = -ENOMEM;
- goto finish;
- }
-
- buf->attr = EFI_VARIABLE_NON_VOLATILE|EFI_VARIABLE_BOOTSERVICE_ACCESS|EFI_VARIABLE_RUNTIME_ACCESS;
- memcpy(buf->buf, value, size);
-
- r = loop_write(fd, buf, sizeof(uint32_t) + size, false);
- if (r < 0)
- goto finish;
-
- r = 0;
-
-finish:
- if (saved_flags_valid) {
- int q;
-
- /* Restore the original flags field, just in case */
- if (fd < 0)
- q = chattr_path(p, saved_flags, FS_IMMUTABLE_FL, NULL);
- else
- q = chattr_fd(fd, saved_flags, FS_IMMUTABLE_FL, NULL);
- if (q < 0)
- log_debug_errno(q, "Failed to restore FS_IMMUTABLE_FL on '%s', ignoring: %m", p);
- }
-
- return r;
-}
-
-int efi_set_variable_string(sd_id128_t vendor, const char *name, const char *v) {
- _cleanup_free_ char16_t *u16 = NULL;
-
- u16 = utf8_to_utf16(v, strlen(v));
- if (!u16)
- return -ENOMEM;
-
- return efi_set_variable(vendor, name, u16, (char16_strlen(u16) + 1) * sizeof(char16_t));
-}
static ssize_t utf16_size(const uint16_t *s, size_t buf_len_bytes) {
size_t l = 0;
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-#if ! ENABLE_EFI
-#include <errno.h>
-#endif
-#include <stdbool.h>
-#include <stddef.h>
-#include <stdint.h>
-
-#include "sd-id128.h"
-
-#include "efi/loader-features.h"
-#include "time-util.h"
-
-#define EFI_VENDOR_LOADER SD_ID128_MAKE(4a,67,b0,82,0a,4c,41,cf,b6,c7,44,0b,29,bb,8c,4f)
-#define EFI_VENDOR_GLOBAL SD_ID128_MAKE(8b,e4,df,61,93,ca,11,d2,aa,0d,00,e0,98,03,2b,8c)
-#define EFI_VARIABLE_NON_VOLATILE 0x0000000000000001
-#define EFI_VARIABLE_BOOTSERVICE_ACCESS 0x0000000000000002
-#define EFI_VARIABLE_RUNTIME_ACCESS 0x0000000000000004
+#include "efivars.h"
#if ENABLE_EFI
int efi_get_reboot_to_firmware(void);
int efi_set_reboot_to_firmware(bool value);
-char* efi_variable_path(sd_id128_t vendor, const char *name);
-int efi_get_variable(sd_id128_t vendor, const char *name, uint32_t *attribute, void **value, size_t *size);
-int efi_get_variable_string(sd_id128_t vendor, const char *name, char **p);
-int efi_set_variable(sd_id128_t vendor, const char *name, const void *value, size_t size);
-int efi_set_variable_string(sd_id128_t vendor, const char *name, const char *p);
-
int efi_get_boot_option(uint16_t nr, char **title, sd_id128_t *part_uuid, char **path, bool *active);
int efi_add_boot_option(uint16_t id, const char *title, uint32_t part, uint64_t pstart, uint64_t psize, sd_id128_t part_uuid, const char *path);
int efi_remove_boot_option(uint16_t id);
return -EOPNOTSUPP;
}
-static inline char* efi_variable_path(sd_id128_t vendor, const char *name) {
- return NULL;
-}
-
-static inline int efi_get_variable(sd_id128_t vendor, const char *name, uint32_t *attribute, void **value, size_t *size) {
- return -EOPNOTSUPP;
-}
-
-static inline int efi_get_variable_string(sd_id128_t vendor, const char *name, char **p) {
- return -EOPNOTSUPP;
-}
-
-static inline int efi_set_variable(sd_id128_t vendor, const char *name, const void *value, size_t size) {
- return -EOPNOTSUPP;
-}
-
-static inline int efi_set_variable_string(sd_id128_t vendor, const char *name, const char *p) {
- return -EOPNOTSUPP;
-}
-
static inline int efi_get_boot_option(uint16_t nr, char **title, sd_id128_t *part_uuid, char **path, bool *active) {
return -EOPNOTSUPP;
}
}
if (ring->tx_pending_set) {
- if (ecmd.tx_pending != ring->rx_pending) {
+ if (ecmd.tx_pending != ring->tx_pending) {
ecmd.tx_pending = ring->tx_pending;
need_update = true;
}
bus-wait-for-units.h
calendarspec.c
calendarspec.h
+ cgroup-setup.c
+ cgroup-setup.h
cgroup-show.c
cgroup-show.h
clean-ipc.c
dns-domain.h
dropin.c
dropin.h
- efivars.c
- efivars.h
+ efi-loader.c
+ efi-loader.h
enable-mempool.c
env-file-label.c
env-file-label.h
#include "alloc-util.h"
#include "fileio.h"
#include "log.h"
+#include "proc-cmdline.h"
#include "raw-reboot.h"
#include "reboot-util.h"
#include "string-util.h"
return log_full_errno(flags & REBOOT_LOG ? LOG_ERR : LOG_DEBUG, errno, "Failed to reboot: %m");
}
+
+int shall_restore_state(void) {
+ bool ret;
+ int r;
+
+ r = proc_cmdline_get_bool("systemd.restore_state", &ret);
+ if (r < 0)
+ return r;
+
+ return r > 0 ? ret : true;
+}
int read_reboot_parameter(char **parameter);
int reboot_with_parameter(RebootFlags flags);
+
+int shall_restore_state(void);
#include "fdset.h"
#include "macro.h"
+#include "string-util.h"
#include "time-util.h"
int serialize_item(FILE *f, const char *key, const char *value);
#include "macro.h"
#include "path-lookup.h"
#include "set.h"
+#include "special.h"
#include "stat-util.h"
#include "string-util.h"
#include "strv.h"
if (hashmap_contains(ids, de->d_name))
continue;
+ dirent_ensure_type(d, de);
if (de->d_type == DT_LNK) {
/* We don't explicitly check for alias loops here. unit_ids_map_get() which
* limits the number of hops should be used to access the map. */
// FIXME: if instance, consider any unit names with different template name
return 0;
}
+
+static const char * const rlmap[] = {
+ "emergency", SPECIAL_EMERGENCY_TARGET,
+ "-b", SPECIAL_EMERGENCY_TARGET,
+ "rescue", SPECIAL_RESCUE_TARGET,
+ "single", SPECIAL_RESCUE_TARGET,
+ "-s", SPECIAL_RESCUE_TARGET,
+ "s", SPECIAL_RESCUE_TARGET,
+ "S", SPECIAL_RESCUE_TARGET,
+ "1", SPECIAL_RESCUE_TARGET,
+ "2", SPECIAL_MULTI_USER_TARGET,
+ "3", SPECIAL_MULTI_USER_TARGET,
+ "4", SPECIAL_MULTI_USER_TARGET,
+ "5", SPECIAL_GRAPHICAL_TARGET,
+ NULL
+};
+
+static const char * const rlmap_initrd[] = {
+ "emergency", SPECIAL_EMERGENCY_TARGET,
+ "rescue", SPECIAL_RESCUE_TARGET,
+ NULL
+};
+
+const char* runlevel_to_target(const char *word) {
+ const char * const *rlmap_ptr;
+ size_t i;
+
+ if (!word)
+ return NULL;
+
+ if (in_initrd()) {
+ word = startswith(word, "rd.");
+ if (!word)
+ return NULL;
+ }
+
+ rlmap_ptr = in_initrd() ? rlmap_initrd : rlmap;
+
+ for (i = 0; rlmap_ptr[i]; i += 2)
+ if (streq(word, rlmap_ptr[i]))
+ return rlmap_ptr[i+1];
+
+ return NULL;
+}
const char *unit_name,
const char **ret_fragment_path,
Set **names);
+
+const char* runlevel_to_target(const char *rl);
#include "alloc-util.h"
#include "async.h"
+#include "cgroup-setup.h"
#include "cgroup-util.h"
#include "def.h"
#include "exec-util.h"
#include "proc-cmdline.h"
#include "special.h"
#include "string-util.h"
+#include "unit-file.h"
#include "util.h"
/*
return c;
}
+static int expand_names(sd_bus *bus, char **names, const char* suffix, char ***ret) {
+ _cleanup_strv_free_ char **mangled = NULL, **globs = NULL;
+ char **name;
+ int r, i;
+
+ assert(bus);
+ assert(ret);
+
+ STRV_FOREACH(name, names) {
+ char *t;
+ UnitNameMangle options = UNIT_NAME_MANGLE_GLOB | (arg_quiet ? 0 : UNIT_NAME_MANGLE_WARN);
+
+ if (suffix)
+ r = unit_name_mangle_with_suffix(*name, options, suffix, &t);
+ else
+ r = unit_name_mangle(*name, options, &t);
+ if (r < 0)
+ return log_error_errno(r, "Failed to mangle name: %m");
+
+ if (string_is_glob(t))
+ r = strv_consume(&globs, t);
+ else
+ r = strv_consume(&mangled, t);
+ if (r < 0)
+ return log_oom();
+ }
+
+ /* Query the manager only if any of the names are a glob, since
+ * this is fairly expensive */
+ if (!strv_isempty(globs)) {
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ _cleanup_free_ UnitInfo *unit_infos = NULL;
+ size_t allocated, n;
+
+ r = get_unit_list(bus, NULL, globs, &unit_infos, 0, &reply);
+ if (r < 0)
+ return r;
+
+ n = strv_length(mangled);
+ allocated = n + 1;
+
+ for (i = 0; i < r; i++) {
+ if (!GREEDY_REALLOC(mangled, allocated, n+2))
+ return log_oom();
+
+ mangled[n] = strdup(unit_infos[i].id);
+ if (!mangled[n])
+ return log_oom();
+
+ mangled[++n] = NULL;
+ }
+ }
+
+ *ret = TAKE_PTR(mangled);
+ return 0;
+}
+
+
static int list_units(int argc, char *argv[], void *userdata) {
_cleanup_free_ UnitInfo *unit_infos = NULL;
_cleanup_(message_set_freep) Set *replies = NULL;
static int list_sockets(int argc, char *argv[], void *userdata) {
_cleanup_(message_set_freep) Set *replies = NULL;
_cleanup_strv_free_ char **machines = NULL;
+ _cleanup_strv_free_ char **sockets_with_suffix = NULL;
_cleanup_free_ UnitInfo *unit_infos = NULL;
_cleanup_free_ struct socket_info *socket_infos = NULL;
const UnitInfo *u;
(void) pager_open(arg_pager_flags);
- n = get_unit_list_recursive(bus, strv_skip(argv, 1), &unit_infos, &replies, &machines);
- if (n < 0)
- return n;
+ r = expand_names(bus, strv_skip(argv, 1), ".socket", &sockets_with_suffix);
+ if (r < 0)
+ return r;
- for (u = unit_infos; u < unit_infos + n; u++) {
- _cleanup_strv_free_ char **listening = NULL, **triggered = NULL;
- int i, c;
+ if (argc == 1 || sockets_with_suffix) {
+ n = get_unit_list_recursive(bus, sockets_with_suffix, &unit_infos, &replies, &machines);
+ if (n < 0)
+ return n;
- if (!endswith(u->id, ".socket"))
- continue;
+ for (u = unit_infos; u < unit_infos + n; u++) {
+ _cleanup_strv_free_ char **listening = NULL, **triggered = NULL;
+ int i, c;
- r = get_triggered_units(bus, u->unit_path, &triggered);
- if (r < 0)
- goto cleanup;
+ if (!endswith(u->id, ".socket"))
+ continue;
- c = get_listening(bus, u->unit_path, &listening);
- if (c < 0) {
- r = c;
- goto cleanup;
- }
+ r = get_triggered_units(bus, u->unit_path, &triggered);
+ if (r < 0)
+ goto cleanup;
- if (!GREEDY_REALLOC(socket_infos, size, cs + c)) {
- r = log_oom();
- goto cleanup;
- }
+ c = get_listening(bus, u->unit_path, &listening);
+ if (c < 0) {
+ r = c;
+ goto cleanup;
+ }
- for (i = 0; i < c; i++)
- socket_infos[cs + i] = (struct socket_info) {
- .machine = u->machine,
- .id = u->id,
- .type = listening[i*2],
- .path = listening[i*2 + 1],
- .triggered = triggered,
- .own_triggered = i==0,
- };
+ if (!GREEDY_REALLOC(socket_infos, size, cs + c)) {
+ r = log_oom();
+ goto cleanup;
+ }
- /* from this point on we will cleanup those socket_infos */
- cs += c;
- free(listening);
- listening = triggered = NULL; /* avoid cleanup */
- }
+ for (i = 0; i < c; i++)
+ socket_infos[cs + i] = (struct socket_info) {
+ .machine = u->machine,
+ .id = u->id,
+ .type = listening[i*2],
+ .path = listening[i*2 + 1],
+ .triggered = triggered,
+ .own_triggered = i==0,
+ };
+
+ /* from this point on we will cleanup those socket_infos */
+ cs += c;
+ free(listening);
+ listening = triggered = NULL; /* avoid cleanup */
+ }
- typesafe_qsort(socket_infos, cs, socket_info_compare);
+ typesafe_qsort(socket_infos, cs, socket_info_compare);
+ }
output_sockets_list(socket_infos, cs);
static int list_timers(int argc, char *argv[], void *userdata) {
_cleanup_(message_set_freep) Set *replies = NULL;
_cleanup_strv_free_ char **machines = NULL;
+ _cleanup_strv_free_ char **timers_with_suffix = NULL;
_cleanup_free_ struct timer_info *timer_infos = NULL;
_cleanup_free_ UnitInfo *unit_infos = NULL;
struct timer_info *t;
(void) pager_open(arg_pager_flags);
- n = get_unit_list_recursive(bus, strv_skip(argv, 1), &unit_infos, &replies, &machines);
- if (n < 0)
- return n;
+ r = expand_names(bus, strv_skip(argv, 1), ".timer", &timers_with_suffix);
+ if (r < 0)
+ return r;
+
+ if (argc == 1 || timers_with_suffix) {
+ n = get_unit_list_recursive(bus, timers_with_suffix, &unit_infos, &replies, &machines);
+ if (n < 0)
+ return n;
- dual_timestamp_get(&nw);
+ dual_timestamp_get(&nw);
- for (u = unit_infos; u < unit_infos + n; u++) {
- _cleanup_strv_free_ char **triggered = NULL;
- dual_timestamp next = DUAL_TIMESTAMP_NULL;
- usec_t m, last = 0;
+ for (u = unit_infos; u < unit_infos + n; u++) {
+ _cleanup_strv_free_ char **triggered = NULL;
+ dual_timestamp next = DUAL_TIMESTAMP_NULL;
+ usec_t m, last = 0;
- if (!endswith(u->id, ".timer"))
- continue;
+ if (!endswith(u->id, ".timer"))
+ continue;
- r = get_triggered_units(bus, u->unit_path, &triggered);
- if (r < 0)
- goto cleanup;
+ r = get_triggered_units(bus, u->unit_path, &triggered);
+ if (r < 0)
+ goto cleanup;
- r = get_next_elapse(bus, u->unit_path, &next);
- if (r < 0)
- goto cleanup;
+ r = get_next_elapse(bus, u->unit_path, &next);
+ if (r < 0)
+ goto cleanup;
- get_last_trigger(bus, u->unit_path, &last);
+ get_last_trigger(bus, u->unit_path, &last);
- if (!GREEDY_REALLOC(timer_infos, size, c+1)) {
- r = log_oom();
- goto cleanup;
- }
+ if (!GREEDY_REALLOC(timer_infos, size, c+1)) {
+ r = log_oom();
+ goto cleanup;
+ }
- m = calc_next_elapse(&nw, &next);
+ m = calc_next_elapse(&nw, &next);
- timer_infos[c++] = (struct timer_info) {
- .machine = u->machine,
- .id = u->id,
- .next_elapse = m,
- .last_trigger = last,
- .triggered = TAKE_PTR(triggered),
- };
- }
+ timer_infos[c++] = (struct timer_info) {
+ .machine = u->machine,
+ .id = u->id,
+ .next_elapse = m,
+ .last_trigger = last,
+ .triggered = TAKE_PTR(triggered),
+ };
+ }
- typesafe_qsort(timer_infos, c, timer_info_compare);
+ typesafe_qsort(timer_infos, c, timer_info_compare);
+ }
output_timers_list(timer_infos, c);
return r;
}
-static int expand_names(sd_bus *bus, char **names, const char* suffix, char ***ret) {
- _cleanup_strv_free_ char **mangled = NULL, **globs = NULL;
- char **name;
- int r, i;
-
- assert(bus);
- assert(ret);
-
- STRV_FOREACH(name, names) {
- char *t;
- UnitNameMangle options = UNIT_NAME_MANGLE_GLOB | (arg_quiet ? 0 : UNIT_NAME_MANGLE_WARN);
-
- if (suffix)
- r = unit_name_mangle_with_suffix(*name, options, suffix, &t);
- else
- r = unit_name_mangle(*name, options, &t);
- if (r < 0)
- return log_error_errno(r, "Failed to mangle name: %m");
-
- if (string_is_glob(t))
- r = strv_consume(&globs, t);
- else
- r = strv_consume(&mangled, t);
- if (r < 0)
- return log_oom();
- }
-
- /* Query the manager only if any of the names are a glob, since
- * this is fairly expensive */
- if (!strv_isempty(globs)) {
- _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
- _cleanup_free_ UnitInfo *unit_infos = NULL;
- size_t allocated, n;
-
- r = get_unit_list(bus, NULL, globs, &unit_infos, 0, &reply);
- if (r < 0)
- return r;
-
- n = strv_length(mangled);
- allocated = n + 1;
-
- for (i = 0; i < r; i++) {
- if (!GREEDY_REALLOC(mangled, allocated, n+2))
- return log_oom();
-
- mangled[n] = strdup(unit_infos[i].id);
- if (!mangled[n])
- return log_oom();
-
- mangled[++n] = NULL;
- }
- }
-
- *ret = TAKE_PTR(mangled);
- return 0;
-}
-
static const struct {
const char *target;
const char *verb;
int sd_dhcp_client_get_lease(
sd_dhcp_client *client,
sd_dhcp_lease **ret);
+int sd_dhcp_client_set_service_type(
+ sd_dhcp_client *client,
+ int type);
int sd_dhcp_client_stop(sd_dhcp_client *client);
int sd_dhcp_client_start(sd_dhcp_client *client);
int sd_dhcp6_client_set_request_option(
sd_dhcp6_client *client,
uint16_t option);
+int sd_dhcp6_client_set_prefix_delegation_hint(
+ sd_dhcp6_client *client,
+ uint8_t prefixlen,
+ const struct in6_addr *pd_address);
int sd_dhcp6_client_get_prefix_delegation(sd_dhcp6_client *client,
int *delegation);
int sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client *client,
[],
[]],
+ [['src/test/test-cgroup-setup.c'],
+ [],
+ []],
+
[['src/test/test-env-file.c'],
[],
[]],
#include "acpi-fpdt.h"
#include "boot-timestamps.h"
-#include "efivars.h"
+#include "efi-loader.h"
#include "log.h"
#include "tests.h"
#include "util.h"
#include "macro.h"
#include "missing_prctl.h"
#include "parse-util.h"
+#include "string-util.h"
#include "tests.h"
-#include "util.h"
static uid_t test_uid = -1;
static gid_t test_gid = -1;
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "alloc-util.h"
+#include "build.h"
+#include "cgroup-setup.h"
+#include "log.h"
+#include "proc-cmdline.h"
+#include "string-util.h"
+#include "tests.h"
+
+static void test_is_wanted_print(bool header) {
+ _cleanup_free_ char *cmdline = NULL;
+
+ log_info("-- %s --", __func__);
+ assert_se(proc_cmdline(&cmdline) >= 0);
+ log_info("cmdline: %s", cmdline);
+ if (header) {
+ log_info(_CGROUP_HIERARCHY_);
+ (void) system("findmnt -n /sys/fs/cgroup");
+ }
+
+ log_info("is_unified_wanted() → %s", yes_no(cg_is_unified_wanted()));
+ log_info("is_hybrid_wanted() → %s", yes_no(cg_is_hybrid_wanted()));
+ log_info("is_legacy_wanted() → %s", yes_no(cg_is_legacy_wanted()));
+ log_info(" ");
+}
+
+static void test_is_wanted(void) {
+ assert_se(setenv("SYSTEMD_PROC_CMDLINE",
+ "systemd.unified_cgroup_hierarchy", 1) >= 0);
+ test_is_wanted_print(false);
+
+ assert_se(setenv("SYSTEMD_PROC_CMDLINE",
+ "systemd.unified_cgroup_hierarchy=0", 1) >= 0);
+ test_is_wanted_print(false);
+
+ assert_se(setenv("SYSTEMD_PROC_CMDLINE",
+ "systemd.unified_cgroup_hierarchy=0 "
+ "systemd.legacy_systemd_cgroup_controller", 1) >= 0);
+ test_is_wanted_print(false);
+
+ assert_se(setenv("SYSTEMD_PROC_CMDLINE",
+ "systemd.unified_cgroup_hierarchy=0 "
+ "systemd.legacy_systemd_cgroup_controller=0", 1) >= 0);
+ test_is_wanted_print(false);
+
+ /* cgroup_no_v1=all implies unified cgroup hierarchy, unless otherwise
+ * explicitly specified. */
+ assert_se(setenv("SYSTEMD_PROC_CMDLINE",
+ "cgroup_no_v1=all", 1) >= 0);
+ test_is_wanted_print(false);
+
+ assert_se(setenv("SYSTEMD_PROC_CMDLINE",
+ "cgroup_no_v1=all "
+ "systemd.unified_cgroup_hierarchy=0", 1) >= 0);
+ test_is_wanted_print(false);
+}
+
+int main(void) {
+ test_setup_logging(LOG_DEBUG);
+
+ test_is_wanted_print(true);
+ test_is_wanted_print(false); /* run twice to test caching */
+ test_is_wanted();
+
+ return 0;
+}
fd = safe_close(fd);
}
-static void test_is_wanted_print(bool header) {
- _cleanup_free_ char *cmdline = NULL;
-
- log_info("-- %s --", __func__);
- assert_se(proc_cmdline(&cmdline) >= 0);
- log_info("cmdline: %s", cmdline);
- if (header) {
-
- log_info(_CGROUP_HIERARCHY_);
- (void) system("findmnt -n /sys/fs/cgroup");
- }
-
- log_info("is_unified_wanted() → %s", yes_no(cg_is_unified_wanted()));
- log_info("is_hybrid_wanted() → %s", yes_no(cg_is_hybrid_wanted()));
- log_info("is_legacy_wanted() → %s", yes_no(cg_is_legacy_wanted()));
- log_info(" ");
-}
-
-static void test_is_wanted(void) {
- assert_se(setenv("SYSTEMD_PROC_CMDLINE",
- "systemd.unified_cgroup_hierarchy", 1) >= 0);
- test_is_wanted_print(false);
-
- assert_se(setenv("SYSTEMD_PROC_CMDLINE",
- "systemd.unified_cgroup_hierarchy=0", 1) >= 0);
- test_is_wanted_print(false);
-
- assert_se(setenv("SYSTEMD_PROC_CMDLINE",
- "systemd.unified_cgroup_hierarchy=0 "
- "systemd.legacy_systemd_cgroup_controller", 1) >= 0);
- test_is_wanted_print(false);
-
- assert_se(setenv("SYSTEMD_PROC_CMDLINE",
- "systemd.unified_cgroup_hierarchy=0 "
- "systemd.legacy_systemd_cgroup_controller=0", 1) >= 0);
- test_is_wanted_print(false);
-
- /* cgroup_no_v1=all implies unified cgroup hierarchy, unless otherwise
- * explicitly specified. */
- assert_se(setenv("SYSTEMD_PROC_CMDLINE",
- "cgroup_no_v1=all", 1) >= 0);
- test_is_wanted_print(false);
-
- assert_se(setenv("SYSTEMD_PROC_CMDLINE",
- "cgroup_no_v1=all "
- "systemd.unified_cgroup_hierarchy=0", 1) >= 0);
- test_is_wanted_print(false);
-}
-
static void test_cg_tests(void) {
int all, hybrid, systemd, r;
- r = cg_unified_flush();
+ r = cg_unified();
if (r == -ENOMEDIUM) {
log_notice_errno(r, "Skipping cg hierarchy tests: %m");
return;
}
- assert_se(r == 0);
+ assert_se(r >= 0);
all = cg_all_unified();
assert_se(IN_SET(all, 0, 1));
TEST_REQ_RUNNING_SYSTEMD(test_mask_supported());
TEST_REQ_RUNNING_SYSTEMD(test_is_cgroup_fs());
TEST_REQ_RUNNING_SYSTEMD(test_fd_is_cgroup_fs());
- test_is_wanted_print(true);
- test_is_wanted_print(false); /* run twice to test caching */
- test_is_wanted();
test_cg_tests();
test_cg_get_keyed_attribute();
#include <string.h>
#include <unistd.h>
+#include "cgroup-setup.h"
#include "cgroup-util.h"
#include "path-util.h"
#include "process-util.h"
#include "cgroup-util.h"
#include "condition.h"
#include "cpu-set-util.h"
-#include "efivars.h"
+#include "efi-loader.h"
#include "hostname-util.h"
#include "id128-util.h"
#include "ima-util.h"
_cleanup_free_ char *controller_name = NULL;
int r;
- r = cg_unified_flush();
+ r = cg_unified();
if (r < 0) {
log_notice_errno(r, "Skipping ConditionControlGroupController tests: %m");
return;
printf("Test11: (Start/stop job ordering, execution cycle)\n");
assert_se(manager_add_job(m, JOB_START, i, JOB_FAIL, NULL, NULL, &j) == 0);
- assert_se(a->job && a->job->type == JOB_STOP);
- assert_se(d->job && d->job->type == JOB_STOP);
- assert_se(b->job && b->job->type == JOB_START);
+ assert_se(unit_has_job_type(a, JOB_STOP));
+ assert_se(unit_has_job_type(d, JOB_STOP));
+ assert_se(unit_has_job_type(b, JOB_START));
manager_dump_jobs(m, stdout, "\t");
printf("Load6:\n");
#include "test-helper.h"
#include "random-util.h"
#include "alloc-util.h"
-#include "cgroup-util.h"
+#include "cgroup-setup.h"
#include "string-util.h"
int enter_cgroup_subroot(void) {
#include <stdio.h>
-#include "job.h"
#include "service.h"
#include "unit.h"
#include "proc-cmdline.h"
#include "special.h"
#include "string-util.h"
+#include "tests.h"
#include "util.h"
static int obj;
log_info("/* %s */", __func__);
assert_se(putenv((char*) "SYSTEMD_PROC_CMDLINE=foo_bar=quux wuff-piep=tuet zumm some_arg_with_space='foo bar' and_one_more=\"zzz aaa\"") == 0);
+ assert_se(putenv((char*) "SYSTEMD_EFI_OPTIONS=differnt") == 0);
- /* Test if the override works */
+ /* First test if the overrides for /proc/cmdline still work */
_cleanup_free_ char *line = NULL, *value = NULL;
assert_se(proc_cmdline(&line) >= 0);
assert_se(proc_cmdline_get_key("and_one_more", 0, &value) > 0 && streq_ptr(value, "zzz aaa"));
value = mfree(value);
+
+ assert_se(putenv((char*) "SYSTEMD_PROC_CMDLINE=") == 0);
+ assert_se(putenv((char*) "SYSTEMD_PROC_CMDLINE=foo_bar=quux wuff-piep=tuet zumm some_arg_with_space='foo bar' and_one_more=\"zzz aaa\"") == 0);
+
+ assert_se(streq(line, "foo_bar=quux wuff-piep=tuet zumm some_arg_with_space='foo bar' and_one_more=\"zzz aaa\""));
+ assert_se(proc_cmdline_get_key("foo_bar", 0, &value) > 0 && streq_ptr(value, "quux"));
+ value = mfree(value);
+
+ assert_se(proc_cmdline_get_key("some_arg_with_space", 0, &value) > 0 && streq_ptr(value, "foo bar"));
+ value = mfree(value);
+
+ assert_se(proc_cmdline_get_key("and_one_more", 0, &value) > 0 && streq_ptr(value, "zzz aaa"));
+ value = mfree(value);
}
static int parse_item_given(const char *key, const char *value, void *data) {
log_info("/* %s */", __func__);
assert_se(putenv((char*) "SYSTEMD_PROC_CMDLINE=foo_bar bar-waldo=1 x_y-z=0 quux=miep\nda=yes\nthe=1") == 0);
+ assert_se(putenv((char*) "SYSTEMD_EFI_OPTIONS=") == 0);
+
+ assert_se(proc_cmdline_get_bool("", &value) == -EINVAL);
+ assert_se(proc_cmdline_get_bool("abc", &value) == 0 && value == false);
+ assert_se(proc_cmdline_get_bool("foo_bar", &value) > 0 && value == true);
+ assert_se(proc_cmdline_get_bool("foo-bar", &value) > 0 && value == true);
+ assert_se(proc_cmdline_get_bool("bar-waldo", &value) > 0 && value == true);
+ assert_se(proc_cmdline_get_bool("bar_waldo", &value) > 0 && value == true);
+ assert_se(proc_cmdline_get_bool("x_y-z", &value) > 0 && value == false);
+ assert_se(proc_cmdline_get_bool("x-y-z", &value) > 0 && value == false);
+ assert_se(proc_cmdline_get_bool("x-y_z", &value) > 0 && value == false);
+ assert_se(proc_cmdline_get_bool("x_y_z", &value) > 0 && value == false);
+ assert_se(proc_cmdline_get_bool("quux", &value) == -EINVAL && value == false);
+ assert_se(proc_cmdline_get_bool("da", &value) > 0 && value == true);
+ assert_se(proc_cmdline_get_bool("the", &value) > 0 && value == true);
+
+ assert_se(putenv((char*) "SYSTEMD_PROC_CMDLINE=") == 0);
+ assert_se(putenv((char*) "SYSTEMD_EFI_OPTIONS=foo_bar bar-waldo=1 x_y-z=0 quux=miep\nda=yes\nthe=1") == 0);
assert_se(proc_cmdline_get_bool("", &value) == -EINVAL);
assert_se(proc_cmdline_get_bool("abc", &value) == 0 && value == false);
assert_se(!proc_cmdline_key_startswith("foo-bar", "foo_xx"));
}
-static void test_runlevel_to_target(void) {
- log_info("/* %s */", __func__);
-
- in_initrd_force(false);
- assert_se(streq_ptr(runlevel_to_target(NULL), NULL));
- assert_se(streq_ptr(runlevel_to_target("unknown-runlevel"), NULL));
- assert_se(streq_ptr(runlevel_to_target("rd.unknown-runlevel"), NULL));
- assert_se(streq_ptr(runlevel_to_target("3"), SPECIAL_MULTI_USER_TARGET));
- assert_se(streq_ptr(runlevel_to_target("rd.rescue"), NULL));
-
- in_initrd_force(true);
- assert_se(streq_ptr(runlevel_to_target(NULL), NULL));
- assert_se(streq_ptr(runlevel_to_target("unknown-runlevel"), NULL));
- assert_se(streq_ptr(runlevel_to_target("rd.unknown-runlevel"), NULL));
- assert_se(streq_ptr(runlevel_to_target("3"), NULL));
- assert_se(streq_ptr(runlevel_to_target("rd.rescue"), SPECIAL_RESCUE_TARGET));
-}
-
int main(void) {
- log_parse_environment();
- log_open();
+ test_setup_logging(LOG_INFO);
test_proc_cmdline_parse();
test_proc_cmdline_override();
test_proc_cmdline_get_key();
test_proc_cmdline_get_bool();
test_proc_cmdline_get_key_many();
- test_runlevel_to_target();
return 0;
}
#include "path-lookup.h"
#include "set.h"
+#include "special.h"
#include "strv.h"
#include "tests.h"
#include "unit-file.h"
}
}
+static void test_runlevel_to_target(void) {
+ log_info("/* %s */", __func__);
+
+ in_initrd_force(false);
+ assert_se(streq_ptr(runlevel_to_target(NULL), NULL));
+ assert_se(streq_ptr(runlevel_to_target("unknown-runlevel"), NULL));
+ assert_se(streq_ptr(runlevel_to_target("rd.unknown-runlevel"), NULL));
+ assert_se(streq_ptr(runlevel_to_target("3"), SPECIAL_MULTI_USER_TARGET));
+ assert_se(streq_ptr(runlevel_to_target("rd.rescue"), NULL));
+
+ in_initrd_force(true);
+ assert_se(streq_ptr(runlevel_to_target(NULL), NULL));
+ assert_se(streq_ptr(runlevel_to_target("unknown-runlevel"), NULL));
+ assert_se(streq_ptr(runlevel_to_target("rd.unknown-runlevel"), NULL));
+ assert_se(streq_ptr(runlevel_to_target("3"), NULL));
+ assert_se(streq_ptr(runlevel_to_target("rd.rescue"), SPECIAL_RESCUE_TARGET));
+}
+
int main(int argc, char **argv) {
test_setup_logging(LOG_DEBUG);
test_unit_validate_alias_symlink_and_warn();
test_unit_file_build_name_map(strv_skip(argv, 1));
+ test_runlevel_to_target();
return 0;
}
#include "alloc-util.h"
#include "blkid-util.h"
#include "device-util.h"
-#include "efivars.h"
+#include "efi-loader.h"
#include "errno-util.h"
#include "fd-util.h"
#include "gpt.h"
r = write_string_file(filename, action, WRITE_STRING_FILE_DISABLE_BUFFER);
if (r < 0) {
- log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_ERR, r,
+ bool ignore = IN_SET(r, -ENOENT, -EACCES, -ENODEV);
+
+ log_full_errno(ignore ? LOG_DEBUG : LOG_ERR, r,
"Failed to write '%s' to '%s': %m", action, filename);
- if (ret == 0 && r != -ENOENT)
+ if (ret == 0 && !ignore)
ret = r;
continue;
}
Disable QEMU KVM autodetection (may be necessary when you're trying to run the
*vanilla* QEMU and have both qemu and qemu-kvm installed)
+TEST_NESTED_KVM=1
+ Allow tests to run with nested KVM. By default, the testsuite disables
+ nested KVM if the host machine already runs under KVM. Setting this
+ variable disables such checks
+
QEMU_MEM=512M
Configure amount of memory for QEMU VMs (defaults to 512M)
local _root="/var/lib/machines/unified-$1-cgns-$2-api-vfs-writable-$3"
/create-busybox-container "$_root"
- UNIFIED_CGROUP_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" -b
- UNIFIED_CGROUP_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" --private-network -b
+ SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" -b
+ SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" --private-network -b
- if UNIFIED_CGROUP_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" -U -b; then
+ if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" -U -b; then
[[ "$is_user_ns_supported" = "yes" && "$3" = "network" ]] && return 1
else
[[ "$is_user_ns_supported" = "no" && "$3" = "network" ]] && return 1
fi
- if UNIFIED_CGROUP_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" --private-network -U -b; then
+ if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" --private-network -U -b; then
[[ "$is_user_ns_supported" = "yes" && "$3" = "yes" ]] && return 1
else
[[ "$is_user_ns_supported" = "no" && "$3" = "yes" ]] && return 1
local _netns_opt="--network-namespace-path=/proc/self/ns/net"
# --network-namespace-path and network-related options cannot be used together
- if UNIFIED_CGROUP_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" "$_netns_opt" --network-interface=lo -b; then
+ if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" "$_netns_opt" --network-interface=lo -b; then
return 1
fi
- if UNIFIED_CGROUP_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" "$_netns_opt" --network-macvlan=lo -b; then
+ if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" "$_netns_opt" --network-macvlan=lo -b; then
return 1
fi
- if UNIFIED_CGROUP_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" "$_netns_opt" --network-ipvlan=lo -b; then
+ if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" "$_netns_opt" --network-ipvlan=lo -b; then
return 1
fi
- if UNIFIED_CGROUP_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" "$_netns_opt" --network-veth -b; then
+ if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" "$_netns_opt" --network-veth -b; then
return 1
fi
- if UNIFIED_CGROUP_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" "$_netns_opt" --network-veth-extra=lo -b; then
+ if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" "$_netns_opt" --network-veth-extra=lo -b; then
return 1
fi
- if UNIFIED_CGROUP_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" "$_netns_opt" --network-bridge=lo -b; then
+ if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" "$_netns_opt" --network-bridge=lo -b; then
return 1
fi
- if UNIFIED_CGROUP_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" "$_netns_opt" --network-zone=zone -b; then
+ if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" "$_netns_opt" --network-zone=zone -b; then
return 1
fi
- if UNIFIED_CGROUP_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" "$_netns_opt" --private-network -b; then
+ if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" "$_netns_opt" --private-network -b; then
return 1
fi
# test --network-namespace-path works with a network namespace created by "ip netns"
ip netns add nspawn_test
_netns_opt="--network-namespace-path=/run/netns/nspawn_test"
- UNIFIED_CGROUP_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" "$_netns_opt" /bin/ip a | grep -v -E '^1: lo.*UP'
+ SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" "$_netns_opt" /bin/ip a | grep -v -E '^1: lo.*UP'
local r=$?
ip netns del nspawn_test
! systemd-run --unit=five -p Type=exec -p User=idontexist /bin/sleep infinity
! systemd-run --unit=six -p Type=exec /tmp/brokenbinary
+systemd-run --unit=seven -p KillSignal=SIGTERM -p RestartKillSignal=SIGINT -p Type=exec /bin/sleep infinity
+# Both TERM and SIGINT happen to have the same number on all architectures
+test $(systemctl show --value -p KillSignal seven.service) -eq 15
+test $(systemctl show --value -p RestartKillSignal seven.service) -eq 2
+
+systemctl restart seven.service
+systemctl stop seven.service
+
systemd-analyze log-level info
echo OK > /testok
RequestOptions=
SendRelease=
MaxAttempts=
+IPServiceType=
[DHCPv6]
UseNTP=
UseDNS=
RapidCommit=
ForceDHCPv6PDOtherInformation=
+PrefixDelegationHint=
[Route]
Destination=
Protocol=
Requisite=
Restart=
RestartForceExitStatus=
+RestartKillSignal=
RestartPreventExitStatus=
RestartSec=
ReusePort=
ExecStart=/bin/sh -x -c 'test "$$STATE_DIRECTORY" = "%S/waldo:%S/quux/pief"'
# Make sure that /var/lib/private/waldo is really the only writable directory besides the obvious candidates
-ExecStart=sh -x -c 'test $$(find / \( -path /var/tmp -o -path /tmp -o -path /proc -o -path /dev/mqueue -o -path /dev/shm -o -path /sys/fs/bpf \) -prune -o -type d -writable -print 2>/dev/null | sort -u | tr -d '\\\\n') = /var/lib/private/quux/pief/var/lib/private/waldo'
+ExecStart=sh -x -c 'test $$(find / \( -path /var/tmp -o -path /tmp -o -path /proc -o -path /dev/mqueue -o -path /dev/shm -o -path /sys/fs/bpf -o -path /dev/.lxc \) -prune -o -type d -writable -print 2>/dev/null | sort -u | tr -d '\\\\n') = /var/lib/private/quux/pief/var/lib/private/waldo'
Type=oneshot
DynamicUser=yes
EFI_MOUNT="$(bootctl -x 2>/dev/null || echo /boot)"
QEMU_MEM="${QEMU_MEM:-512M}"
+# Decide if we can (and want to) run QEMU with KVM acceleration.
+# Check if nested KVM is explicitly enabled (TEST_NESTED_KVM). If not,
+# check if it's not explicitly disabled (TEST_NO_KVM) and we're not already
+# running under KVM. If these conditions are met, enable KVM (and possibly
+# nested KVM), otherwise disable it.
+if [[ -n "$TEST_NESTED_KVM" || ( -z "$TEST_NO_KVM" && $(systemd-detect-virt -v) != kvm ) ]]; then
+ QEMU_KVM=yes
+else
+ QEMU_KVM=no
+fi
+
if ! ROOTLIBDIR=$(pkg-config --variable=systemdutildir systemd); then
echo "WARNING! Cannot determine rootlibdir from pkg-config, assuming /usr/lib/systemd" >&2
ROOTLIBDIR=/usr/lib/systemd
function find_qemu_bin() {
# SUSE and Red Hat call the binary qemu-kvm. Debian and Gentoo call it kvm.
- # Either way, only use this version if we aren't running in KVM, because
- # nested KVM is flaky still.
- if [[ $(systemd-detect-virt -v) != kvm && -z $TEST_NO_KVM ]] ; then
+ if [[ $QEMU_KVM == "yes" ]]; then
[ "$QEMU_BIN" ] || QEMU_BIN=$(which -a kvm qemu-kvm 2>/dev/null | grep '^/' -m1)
fi
QEMU_OPTIONS="$QEMU_OPTIONS -initrd $INITRD"
fi
- # Let's use KVM if it is available, but let's avoid using nested KVM as that is still flaky
- if [[ -c /dev/kvm && $(systemd-detect-virt -v) != kvm && -z $TEST_NO_KVM ]] ; then
+ # Let's use KVM if possible
+ if [[ -c /dev/kvm && $QEMU_KVM == "yes" ]]; then
QEMU_OPTIONS="$QEMU_OPTIONS -machine accel=kvm -enable-kvm -cpu host"
fi
fi
if [[ "$UNIFIED_CGROUP_HIERARCHY" = "hybrid" ]]; then
- dwarn "nspawn doesn't support UNIFIED_CGROUP_HIERARCHY=hybrid, skipping"
+ dwarn "nspawn doesn't support SYSTEMD_NSPAWN_UNIFIED_HIERARCHY=hybrid, skipping"
exit
elif [[ "$UNIFIED_CGROUP_HIERARCHY" = "yes" || "$UNIFIED_CGROUP_HIERARCHY" = "no" ]]; then
- _nspawn_cmd="env UNIFIED_CGROUP_HIERARCHY=$UNIFIED_CGROUP_HIERARCHY $_nspawn_cmd"
+ _nspawn_cmd="env SYSTEMD_NSPAWN_UNIFIED_HIERARCHY=$UNIFIED_CGROUP_HIERARCHY $_nspawn_cmd"
elif [[ "$UNIFIED_CGROUP_HIERARCHY" = "default" ]]; then
- _nspawn_cmd="env --unset=UNIFIED_CGROUP_HIERARCHY $_nspawn_cmd"
+ _nspawn_cmd="env --unset=UNIFIED_CGROUP_HIERARCHY --unset=SYSTEMD_NSPAWN_UNIFIED_HIERARCHY $_nspawn_cmd"
else
dfatal "Unknown UNIFIED_CGROUP_HIERARCHY. Got $UNIFIED_CGROUP_HIERARCHY, expected [yes|no|hybrid|default]"
exit 1
self.assertRegex(output, '192.168.5')
self.assertRegex(output, '1492')
- # issue #8726
print('## ip route show table main dev veth99')
output = check_output('ip route show table main dev veth99')
print(output)
- self.assertNotRegex(output, 'proto dhcp')
+ # See issue #8726
+ main_table_is_empty = output == ''
+ if not main_table_is_empty:
+ self.assertNotRegex(output, 'proto dhcp')
print('## ip route show table 211 dev veth99')
output = check_output('ip route show table 211 dev veth99')
print(output)
self.assertRegex(output, 'default via 192.168.5.1 proto dhcp')
+ if main_table_is_empty:
+ self.assertRegex(output, '192.168.5.0/24 proto dhcp')
self.assertRegex(output, '192.168.5.0/24 via 192.168.5.5 proto dhcp')
self.assertRegex(output, '192.168.5.1 proto dhcp scope link')
--- /dev/null
+#!/usr/bin/env python2
+# -*- coding: utf-8 -*-
+
+# Copyright 2017 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Autosuspend udev rule generator
+
+This script is executed at build time to generate udev rules. The
+resulting rules file is installed on the device, the script itself
+is not.
+"""
+
+from __future__ import print_function
+
+# List of USB devices (vendorid:productid) for which it is safe to enable
+# autosuspend.
+USB_IDS = []
+
+# Host Controllers and internal hubs
+USB_IDS += [
+ # Linux Host Controller (UHCI) (most older x86 boards)
+ '1d6b:0001',
+ # Linux Host Controller (EHCI) (all boards)
+ '1d6b:0002',
+ # Linux Host Controller (XHCI) (most newer boards)
+ '1d6b:0003',
+ # SMSC (Internal HSIC Hub) (most Exynos boards)
+ '0424:3503',
+ # Intel (Rate Matching Hub) (all x86 boards)
+ '05e3:0610',
+ # Intel (Internal Hub?) (peppy, falco)
+ '8087:0024',
+ # Genesys Logic (Internal Hub) (rambi)
+ '8087:8000',
+]
+
+# Webcams
+USB_IDS += [
+ # Chicony (zgb)
+ '04f2:b1d8',
+ # Chicony (mario)
+ '04f2:b262',
+ # Chicony (stout)
+ '04f2:b2fe',
+ # Chicony (butterfly)
+ '04f2:b35f',
+ # Chicony (rambi)
+ '04f2:b443',
+ # Chicony (glados)
+ '04f2:b552',
+ # LiteOn (spring)
+ '058f:b001',
+ # Foxlink? (butterfly)
+ '05c8:0351',
+ # Foxlink? (butterfly)
+ '05c8:0355',
+ # Cheng Uei? (falco)
+ '05c8:036e',
+ # SuYin (parrot)
+ '064e:d251',
+ # Realtek (falco)
+ '0bda:571c',
+ # IMC Networks (squawks)
+ '13d3:5657',
+ # Sunplus (parrot)
+ '1bcf:2c17',
+ # (C-13HDO10B39N) (alex)
+ '2232:1013',
+ # (C-10HDP11538N) (lumpy)
+ '2232:1017',
+ # (Namuga) (link)
+ '2232:1033',
+ # (C-03FFM12339N) (daisy)
+ '2232:1037',
+ # (C-10HDO13531N) (peach)
+ '2232:1056',
+ # (NCM-G102) (samus)
+ '2232:6001',
+ # Acer (stout)
+ '5986:0299',
+]
+
+# Bluetooth Host Controller
+USB_IDS += [
+ # Hon-hai (parrot)
+ '0489:e04e',
+ # Hon-hai (peppy)
+ '0489:e056',
+ # Hon-hai (Kahlee)
+ '0489:e09f',
+ # QCA6174A (delan)
+ '0489:e0a2',
+ # LiteOn (parrot)
+ '04ca:3006',
+ # LiteOn (aleena)
+ '04ca:3016',
+ # LiteOn (scarlet)
+ '04ca:301a',
+ # Atheros (stumpy, stout)
+ '0cf3:3004',
+ # Atheros (AR3011) (mario, alex, zgb)
+ '0cf3:3005',
+ # Atheros (stumyp)
+ '0cf3:3007',
+ # Atheros (butterfly)
+ '0cf3:311e',
+ # Atheros (scarlet)
+ '0cf3:e300',
+ # Marvell (rambi)
+ '1286:2046',
+ # Marvell (gru)
+ '1286:204e',
+ # Intel (rambi, samus)
+ '8087:07dc',
+ # Intel (strago, glados)
+ '8087:0a2a',
+ # Intel (octopus)
+ '8087:0aaa',
+ # Intel (hatch)
+ '8087:0026',
+ # Intel (atlas)
+ '8087:0025',
+]
+
+# WWAN (LTE)
+USB_IDS += [
+ # Huawei (ME936) (kip)
+ '12d1:15bb',
+ # Fibocom (L850-GL) (coral, nautilus, sarien)
+ '2cb7:0007',
+]
+
+# Mass Storage
+USB_IDS += [
+ # Genesys (SD card reader) (lumpy, link, peppy)
+ '05e3:0727',
+ # Realtek (SD card reader) (mario, alex)
+ '0bda:0138',
+ # Realtek (SD card reader) (helios)
+ '0bda:0136',
+ # Realtek (SD card reader) (falco)
+ '0bda:0177',
+]
+
+# Security Key
+USB_IDS += [
+ # Yubico.com
+ '1050:0211',
+ # Yubico.com (HID firmware)
+ '1050:0200',
+ # Google Titan key
+ '18d1:5026',
+]
+
+# USB Audio devices
+USB_IDS += [
+ # Google USB-C to 3.5mm Digital Headphone Jack Adapter 'Mir'
+ '18d1:5025',
+ # Google USB-C to 3.5mm Digital Headphone Jack Adapter 'Mir' (HID only)
+ '18d1:5029',
+ # Google USB-C to 3.5mm Digital Headphone Jack Adapter 2018 'Condor'
+ '18d1:5034',
+ # Google Pixel USB-C Earbuds 'Blackbird'
+ '18d1:5033',
+ # Libratone Q Adapt In-Ear USB-C Earphones, Made for Google
+ '03eb:2433',
+ # Moshi USB-C to 3.5 mm Adapter/Charger, Made for Google
+ '282b:48f0',
+ # Moshi USB-C to 3.5 mm Adapter/Charger, Made for Google (HID only)
+ '282b:0026',
+ # AiAiAi TMA-2 C60 Cable, Made for Google
+ '0572:1a08',
+ # Apple USB-C to 3.5mm Headphone Jack Adapter
+ '05ac:110a',
+]
+
+# List of PCI devices (vendorid:deviceid) for which it is safe to enable
+# autosuspend.
+PCI_IDS = []
+
+# Intel
+PCI_IDS += [
+ # Host bridge
+ '8086:590c',
+ # i915
+ '8086:591e',
+ # proc_thermal
+ '8086:1903',
+ # xhci_hcd
+ '8086:9d2f',
+ # intel_pmc_core
+ '8086:9d21',
+ # i801_smbus
+ '8086:9d23',
+ # iwlwifi
+ '8086:095a',
+ # GMM
+ '8086:1911',
+ # Thermal
+ '8086:9d31',
+ # MME
+ '8086:9d3a',
+ # CrOS EC
+ '8086:9d4b',
+ # PCH SPI
+ '8086:9d24',
+ # SATA
+ '8086:02d3',
+ # RAM memory
+ '8086:02ef',
+ # ISA bridge
+ '8086:0284',
+ # Communication controller
+ '8086:02e0',
+ # Network controller
+ '8086:02f0',
+ # Serial bus controller
+ '8086:02a4',
+ # USB controller
+ '8086:02ed',
+ # Graphics
+ '8086:9b41',
+ # DSP
+ '8086:02f9',
+ # Host bridge
+ '8086:9b61',
+ # Host bridge
+ '8086:9b71',
+ # PCI Bridge
+ '8086:02b0',
+ # i915 (atlas)
+ '8086:591c',
+ # iwlwifi (atlas)
+ '8086:2526',
+]
+
+# Samsung
+PCI_IDS += [
+ # NVMe KUS030205M-B001
+ '144d:a806',
+ # NVMe MZVLB256HAHQ
+ '144d:a808',
+]
+
+# Lite-on
+PCI_IDS += [
+ # 3C07110288
+ '14a4:9100',
+]
+
+# Seagate
+PCI_IDS += [
+ # ZP256CM30011
+ '7089:5012',
+]
+
+# Kingston
+PCI_IDS += [
+ # RBUSNS8154P3128GJ3
+ '2646:5008',
+]
+
+################################################################################
+
+UDEV_RULE = """\
+ACTION!="add", GOTO="autosuspend_end"
+SUBSYSTEM!="i2c|pci|usb", GOTO="autosuspend_end"
+
+SUBSYSTEM=="i2c", GOTO="autosuspend_i2c"
+SUBSYSTEM=="pci", GOTO="autosuspend_pci"
+SUBSYSTEM=="usb", GOTO="autosuspend_usb"
+
+# I2C rules
+LABEL="autosuspend_i2c"
+ATTR{name}=="cyapa", ATTR{power/control}="on", GOTO="autosuspend_end"
+GOTO="autosuspend_end"
+
+# PCI rules
+LABEL="autosuspend_pci"
+%(pci_rules)s\
+GOTO="autosuspend_end"
+
+# USB rules
+LABEL="autosuspend_usb"
+%(usb_rules)s\
+GOTO="autosuspend_end"
+
+# Enable autosuspend
+LABEL="autosuspend_enable"
+TEST=="power/control", ATTR{power/control}="auto", GOTO="autosuspend_end"
+
+LABEL="autosuspend_end"
+"""
+
+
+def main():
+ pci_rules = ''
+ for dev_ids in PCI_IDS:
+ vendor, device = dev_ids.split(':')
+ pci_rules += ('ATTR{vendor}=="0x%s", ATTR{device}=="0x%s", '
+ 'GOTO="autosuspend_enable"\n' % (vendor, device))
+
+ usb_rules = ''
+ for dev_ids in USB_IDS:
+ vid, pid = dev_ids.split(':')
+ usb_rules += ('ATTR{idVendor}=="%s", ATTR{idProduct}=="%s", '
+ 'GOTO="autosuspend_enable"\n' % (vid, pid))
+
+ print(UDEV_RULE % {'pci_rules': pci_rules, 'usb_rules': usb_rules})
+
+
+if __name__ == '__main__':
+ main()
--- /dev/null
+#!/usr/bin/env python3
+# SPDX-License-Identifier: LGPL-2.1+
+
+# Generate autosuspend rules for devices that have been whitelisted (IE tested)
+# by the ChromeOS team. Please keep this script in sync with:
+# https://chromium.googlesource.com/chromiumos/platform2/+/master/power_manager/udev/gen_autosuspend_rules.py
+
+import sys
+import chromeos.gen_autosuspend_rules
+
+if __name__ == '__main__':
+ if len(sys.argv) > 1:
+ sys.stdout = open(sys.argv[1], 'w')
+ chromeos.gen_autosuspend_rules.main()
SystemCallErrorNumber=EPERM
SystemCallFilter=@system-service
Type=notify
+RestartKillSignal=SIGUSR2
User=systemd-network
WatchdogSec=3min