* dissect: when we discover squashfs, don't claim we had a "writable" partition
in systemd-dissect
-* systemd-run should have a way how to connect a pair of pipes to
- stdout/stderr/stdin of the invoked service
-
-* set LockPersonality= on all our services
-
* Add AddUser= setting to unit files, similar to DynamicUser=1 which however
creates a static, persistent user rather than a dynamic, transient user. We
can leverage code from sysusers.d for this.
diffs remain minimal (in particular: the OUI databases we import are not
sorted, and not stable)
-* set SystemCallArchitectures=native on all our services
-
* maybe add call sd_journal_set_block_timeout() or so to set SO_SNDTIMEO for
the sd-journal logging socket, and, if the timeout is set to 0, sets
O_NONBLOCK on it. That way people can control if and when to block for
* tighten sd_notify() MAINPID= checks a bit: don't accept foreign PIDs (i.e.
PIDs not managed by the service manager)
-* journald: when we recv a log datagram via the native or syslog transports,
- search for the PID in the active stream connections, and let's make sure to
- always process the datagrams before the streams. Then, cache client metadata
- per stream in the stream object. This way we can somewhat fix the race with
- quickly exiting processes which log as long as they had their own stream
- connection...
-
* hostnamed: populate form factor data from a new hwdb database, so that old
yogas can be recognized as "convertible" too, even if they predate the DMI
"convertible" form factor
* maybe introduce gpt auto discovery for /var/tmp?
-* set ProtectSystem=strict for all our usual services.
-
* fix PrivateNetwork= so that we fall back gracefully on kernels lacking
namespacing support (similar for the other namespacing options)
* enable LockMLOCK to take a percentage value relative to physical memory
-* switch to ProtectSystem=strict for all our long-running services where that's possible
-
* Permit masking specific netlink APIs with RestrictAddressFamily=
* nspawn: start UID allocation loop from hash of container name
* maybe add a generator that looks for "systemd.run=" on the kernel cmdline for container usercases...
-* cgtop: make cgtop useful in a container
-
* test/:
- add 'set -e' to scripts in test/
- make stuff in test/ work with separate output dir
* cryptsetup:
- cryptsetup-generator: allow specification of passwords in crypttab itself
- - move cryptsetup key caching into kernel keyctl?
- https://bugs.freedesktop.org/show_bug.cgi?id=54982
- support rd.luks.allow-discards= kernel cmdline params in cryptsetup generator
* hw watchdog: optionally try to use the preset watchdog timeout instead of always overriding it
- add trigger --subsystem-match=usb/usb_device device
- reimport udev db after MOVE events for devices without dev_t
-* when a service has the same env var set twice we actually store it twice and return that in systemctl show -p... We should only show the last setting
-
* There's currently no way to cancel fsck (used to be possible via C-c or c on the console)
* add option to sockets to avoid activation. Instead just drop packets/connections, see http://cyberelk.net/tim/2012/02/15/portreserve-systemd-solution/
* dot output for --test showing the 'initial transaction'
-* fingerprint.target, wireless.target, gps.target, netdevice.target
-
* pid1:
- - .timer units should optionally support CLOCK_BOOTTIME in addition to CLOCK_MONOTONIC
- When logging about multiple units (stopping BoundTo units, conflicts, etc.),
log both units as UNIT=, so that journalctl -u triggers on both.
- generate better errors when people try to set transient properties
- load-fragment: when loading a unit file via a chain of symlinks
verify that it is not masked via any of the names traversed.
- introduce Type=pid-file
- - ExecOnFailure=/usr/bin/foo
- introduce mix of BindTo and Requisite
- add a concept of RemainAfterExit= to scope units
- - Set NoNewPrivileges= on all of our own services, where that makes sense
- Allow multiple ExecStart= for all Type= settings, so that we can cover rescue.service nicely
- - consider adding RuntimeDirectoryUser= + RuntimeDirectoryGroup=
* udev-link-config:
- Make sure ID_PATH is always exported and complete for
* dhcp:
- figure out how much we can increase Maximum Message Size
- - support RFC4702 (pass FQDN)
* dhcp6:
- add functions to set previously stored IPv6 addresses on startup and get
* drop accountsservice's StandardOutput=syslog and Type=dbus fields
-* dbus: in fedora, make /var/lib/dbus/machine-id a symlink to /etc/machine-id
-
* /usr/bin/service should actually show the new command line
* fedora: suggest auto-restart on failure, but not on success and not on coredump. also, ask people to think about changing the start limit logic. Also point people to RestartPreventExitStatus=, SuccessExitStatus=
sensor:modalias:acpi:KIOX000A*:dmi:*svnLAMINA:pnT-1016BNORD*
ACCEL_MOUNT_MATRIX=0, 1, 0; 1, 0, 0; 0, 0, 1
+#########################################
+# Lenovo
+#########################################
+sensor:modalias:acpi:NCPE0388*:dmi:*:rnLenovoYOGA510-14IKB:*
+ ACCEL_MOUNT_MATRIX=-1, 0, 0; 0, -1, 0; 0, 0, 1
+
#########################################
# Peaq
#########################################
<filename>/etc/</filename>, with the same filename as the vendor
configuration file. If the vendor configuration file is included in
the initrd image, the image has to be regenerated.</para>
-
</refsection>
<refsection id='main-conf'>
above).</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>--system-call-filter=</option></term>
+
+ <listitem><para>Alter the system call filter applied to containers. Takes a space-separated list of system call
+ names or group names (the latter prefixed with <literal>@</literal>, as listed by the
+ <command>syscall-filter</command> command of <citerefentry
+ project='man-pages'><refentrytitle>systemd-analyze</refentrytitle><manvolnum>1</manvolnum></citerefentry>). Passed
+ system calls will be permitted. The list may optionally be prefixed by <literal>~</literal>, in which case all
+ listed system calls are prohibited. If this command line option is used multiple times the configured lists are
+ combined. If both a positive and a negative list (that is one system call list without and one with the
+ <literal>~</literal> prefix) are configured, the negative list takes precedence over the positive list. Note
+ that <command>systemd-nspawn</command> always implements a system call whitelist (as opposed to a blacklist),
+ and this command line option hence adds or removes entries from the default whitelist, depending on the
+ <literal>~</literal> prefix. Note that the applied system call filter is also altered implicitly if additional
+ capabilities are passed using the <command>--capabilities=</command>.</para></listitem>
+ </varlistentry>
+
<varlistentry>
<term><option>--kill-signal=</option></term>
<term><option>--pty</option></term>
<term><option>-t</option></term>
- <listitem><para>When invoking the command, the transient service connects its standard input and output to the
- terminal <command>systemd-run</command> is invoked on, via a pseudo TTY device. This allows running binaries
- that expect interactive user input as services, such as interactive command shells.</para>
+ <listitem><para>When invoking the command, the transient service connects its standard input, output and error
+ to the terminal <command>systemd-run</command> is invoked on, via a pseudo TTY device. This allows running
+ programs that expect interactive user input/output as services, such as interactive command shells.</para>
<para>Note that
<citerefentry><refentrytitle>machinectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s
<command>shell</command> command is usually a better alternative for requesting a new, interactive login
- session on the local host or a local container.</para></listitem>
+ session on the local host or a local container.</para>
+
+ <para>See below for details on how this switch combines with <option>--pipe</option>.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--pipe</option></term>
+ <term><option>-P</option></term>
+
+ <listitem><para>If specified, standard input, output, and error of the transient service are inherited from the
+ <command>systemd-run</command> command itself. This allows <command>systemd-run</command>
+ to be used within shell pipelines.
+ Note that this mode is not suitable for interactive command shells and similar, as the
+ service process will not become a TTY controller when invoked on a terminal. Use <option>--pty</option> instead
+ in that case.</para>
+
+ <para>When both <option>--pipe</option> and <option>--pty</option> are used in combination the more appropriate
+ option is automatically determined and used. Specifically, when invoked with standard input, output and error
+ connected to a TTY <option>--pty</option> is used, and otherwise <option>--pipe</option>.</para></listitem>
</varlistentry>
<varlistentry>
specified in
<citerefentry><refentrytitle>sysusers.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>
are searched for a matching file. If the string
- <filename>-</filename> is specified as filename, entries from the
+ <literal>-</literal> is specified instead of a filename, entries from the
standard input of the process are read.</para>
</refsect1>
the escaping logic used to convert a file system path to a unit
name see
<citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
+
+ <para>Device units will be reloaded by systemd whenever the
+ corresponding device generates a <literal>changed</literal> event.
+ Other units can use <varname>ReloadPropagatedFrom=</varname> to react
+ to that event</para>
+
</refsect1>
<refsect1>
<varlistentry>
<term><varname>PassEnvironment=</varname></term>
- <listitem><para>Pass environment variables from the systemd system
- manager to executed processes. Takes a space-separated list of variable
- names. This option may be specified more than once, in which case all
- listed variables will be set. If the empty string is assigned to this
- option, the list of environment variables is reset, all prior
- assignments have no effect. Variables that are not set in the system
- manager will not be passed and will be silently ignored.</para>
-
- <para>Variables passed from this setting are overridden by those passed
- from <varname>Environment=</varname> or
- <varname>EnvironmentFile=</varname>.</para>
+ <listitem><para>Pass environment variables set for the system service manager to executed processes. Takes a
+ space-separated list of variable names. This option may be specified more than once, in which case all listed
+ variables will be passed. If the empty string is assigned to this option, the list of environment variables to
+ pass is reset, all prior assignments have no effect. Variables specified that are not set for the system
+ manager will not be passed and will be silently ignored. Note that this option is only relevant for the system
+ service manager, as system services by default do not automatically inherit any environment variables set for
+ the service manager itself. However, in case of the user service manager all environment variables are passed
+ to the executed processes anyway, hence this option is without effect for the user service manager.</para>
+
+ <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>Example:
<programlisting>PassEnvironment=VAR1 VAR2 VAR3</programlisting>
for details about environment variables.</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>UnsetEnvironment=</varname></term>
+
+ <listitem><para>Explicitly unset environment variable assignments that would normally be passed from the
+ service manager to invoked processes of this unit. Takes a space-separated list of variable names or variable
+ assignments. This option may be specified more than once, in which case all listed variables/assignments will
+ be unset. If the empty string is assigned to this option, the list of environment variables/assignments to
+ unset is reset. If a variable assignment is specified (that is: a variable name, followed by
+ <literal>=</literal>, followed by its value), then any environment variable matching this precise assignment is
+ removed. If a variable name is specified (that is a variable name without any following <literal>=</literal> or
+ value), then any assignment matching the variable name, regardless of its value is removed. Note that the
+ effect of <varname>UnsetEnvironment=</varname> is applied as final step when the environment list passed to
+ executed processes is compiled. That means it may undo assignments from any configuration source, including
+ assignments made through <varname>Environment=</varname> or <varname>EnvironmentFile=</varname>, inherited from
+ the system manager's global set of environment variables, inherited via <varname>PassEnvironment=</varname>,
+ set by the service manager itself (such as <varname>$NOTIFY_SOCKET</varname> and such), or set by a PAM module
+ (in case <varname>PAMName=</varname> is used).</para>
+
+ <para>
+ See
+ <citerefentry project='man-pages'><refentrytitle>environ</refentrytitle><manvolnum>7</manvolnum></citerefentry>
+ for details about environment variables.</para></listitem>
+ </varlistentry>
+
<varlistentry>
<term><varname>StandardInput=</varname></term>
<listitem><para>Controls where file descriptor 0 (STDIN) of
<entry>@cpu-emulation</entry>
<entry>System calls for CPU emulation functionality (<citerefentry project='man-pages'><refentrytitle>vm86</refentrytitle><manvolnum>2</manvolnum></citerefentry> and related calls)</entry>
</row>
+ <row>
+ <entry>@credentials</entry>
+ <entry>System calls for querying process credentials (<citerefentry project='man-pages'><refentrytitle>getuid</refentrytitle><manvolnum>2</manvolnum></citerefentry>, <citerefentry project='man-pages'><refentrytitle>capget</refentrytitle><manvolnum>2</manvolnum></citerefentry>, and related calls)</entry>
+ </row>
<row>
<entry>@debug</entry>
<entry>Debugging, performance monitoring and tracing functionality (<citerefentry project='man-pages'><refentrytitle>ptrace</refentrytitle><manvolnum>2</manvolnum></citerefentry>, <citerefentry project='man-pages'><refentrytitle>perf_event_open</refentrytitle><manvolnum>2</manvolnum></citerefentry> and related calls)</entry>
<entry>@keyring</entry>
<entry>Kernel keyring access (<citerefentry project='man-pages'><refentrytitle>keyctl</refentrytitle><manvolnum>2</manvolnum></citerefentry> and related calls)</entry>
</row>
+ <row>
+ <entry>@memlock</entry>
+ <entry>Locking of memory into RAM (<citerefentry project='man-pages'><refentrytitle>mlock</refentrytitle><manvolnum>2</manvolnum></citerefentry>, <citerefentry project='man-pages'><refentrytitle>mlockall</refentrytitle><manvolnum>2</manvolnum></citerefentry> and related calls)</entry>
+ </row>
<row>
<entry>@module</entry>
<entry>Loading and unloading of kernel modules (<citerefentry project='man-pages'><refentrytitle>init_module</refentrytitle><manvolnum>2</manvolnum></citerefentry>, <citerefentry project='man-pages'><refentrytitle>delete_module</refentrytitle><manvolnum>2</manvolnum></citerefentry> and related calls)</entry>
<entry>@setuid</entry>
<entry>System calls for changing user ID and group ID credentials, (<citerefentry project='man-pages'><refentrytitle>setuid</refentrytitle><manvolnum>2</manvolnum></citerefentry>, <citerefentry project='man-pages'><refentrytitle>setgid</refentrytitle><manvolnum>2</manvolnum></citerefentry>, <citerefentry project='man-pages'><refentrytitle>setresuid</refentrytitle><manvolnum>2</manvolnum></citerefentry>, …)</entry>
</row>
+ <row>
+ <entry>@signal</entry>
+ <entry>System calls for manipulating and handling process signals (<citerefentry project='man-pages'><refentrytitle>signal</refentrytitle><manvolnum>2</manvolnum></citerefentry>, <citerefentry project='man-pages'><refentrytitle>sigprocmask</refentrytitle><manvolnum>2</manvolnum></citerefentry>, …)</entry>
+ </row>
<row>
<entry>@swap</entry>
<entry>System calls for enabling/disabling swap devices (<citerefentry project='man-pages'><refentrytitle>swapon</refentrytitle><manvolnum>2</manvolnum></citerefentry>, <citerefentry project='man-pages'><refentrytitle>swapoff</refentrytitle><manvolnum>2</manvolnum></citerefentry>)</entry>
</row>
+ <row>
+ <entry>@timer</entry>
+ <entry>System calls for scheduling operations by time (<citerefentry project='man-pages'><refentrytitle>alarm</refentrytitle><manvolnum>2</manvolnum></citerefentry>, <citerefentry project='man-pages'><refentrytitle>timer_create</refentrytitle><manvolnum>2</manvolnum></citerefentry>, …)</entry>
+ </row>
</tbody>
</tgroup>
</table>
<refsect1>
<title>Environment variables in spawned processes</title>
- <para>Processes started by the system are executed in a clean
- environment in which select variables listed below are set. System
- processes started by systemd do not inherit variables from PID 1,
- but processes started by user systemd instances inherit all
- environment variables from the user systemd instance.
- </para>
+ <para>Processes started by the service manager are executed with an environment variable block assembled from
+ multiple sources. Processes started by the system service manager generally do not inherit environment variables
+ set for the service manager itself (but this may be altered via <varname>PassEnvironment=</varname>), but processes
+ started by the user service manager instances generally do inherit all environment variables set for the service
+ manager itself.</para>
+
+ <para>For each invoked process the list of environment variables set is compiled from the following sources:</para>
+
+ <itemizedlist>
+ <listitem><para>Variables globally configured for the service manager, using the
+ <varname>DefaultEnvironment=</varname> setting in
+ <citerefentry><refentrytitle>systemd-system.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>, the kernel command line option <varname>systemd.setenv=</varname> (see
+ <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>) or via
+ <command>systemctl set-environment</command> (see <citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>).</para></listitem>
+
+ <listitem><para>Variables defined by the service manager itself (see the list below)</para></listitem>
+
+ <listitem><para>Variables set in the service manager's own environment variable block (subject to <varname>PassEnvironment=</varname> for the system service manager)</para></listitem>
+
+ <listitem><para>Variables set via <varname>Environment=</varname> in the unit file</para></listitem>
+
+ <listitem><para>Variables read from files specified via <varname>EnvironmentFiles=</varname> in the unit file</para></listitem>
+
+ <listitem><para>Variables set by any PAM modules in case <varname>PAMName=</varname> is in effect, cf. <citerefentry project='man-pages'><refentrytitle>pam_env</refentrytitle><manvolnum>8</manvolnum></citerefentry></para></listitem>
+ </itemizedlist>
+
+ <para>If the same environment variables are set by multiple of these sources, the later source — according to the
+ order of the list above — wins. Note that as final step all variables listed in
+ <varname>UnsetEnvironment=</varname> are removed again from the compiled environment variable list, immediately
+ before it is passed to the executed process.</para>
+
+ <para>The following select environment variables are set by the service manager itself for each invoked process:</para>
<variablelist class='environment-variables'>
<varlistentry>
<listitem><para>Only defined for the service unit type, this environment variable is passed to all
<varname>ExecStop=</varname> and <varname>ExecStopPost=</varname> processes, and encodes the service
- "result". Currently, the following values are defined: <literal>protocol</literal> (in case of a protocol
- violation; if a service did not take the steps required by its unit configuration), <literal>timeout</literal>
- (in case of an operation timeout), <literal>exit-code</literal> (if a service process exited with a non-zero
- exit code; see <varname>$EXIT_CODE</varname> below for the actual exit code returned), <literal>signal</literal>
- (if a service process was terminated abnormally by a signal; see <varname>$EXIT_CODE</varname> below for the
- actual signal used for the termination), <literal>core-dump</literal> (if a service process terminated
- abnormally and dumped core), <literal>watchdog</literal> (if the watchdog keep-alive ping was enabled for the
- service but it missed the deadline), or <literal>resources</literal> (a catch-all condition in case a system
- operation failed).</para>
+ "result". Currently, the following values are defined:</para>
+
+ <table>
+ <title>Defined <varname>$SERVICE_RESULT</varname> values</title>
+ <tgroup cols='2'>
+ <colspec colname='result'/>
+ <colspec colname='meaning'/>
+ <thead>
+ <row>
+ <entry>Value</entry>
+ <entry>Meaning</entry>
+ </row>
+ </thead>
+
+ <tbody>
+ <row>
+ <entry><literal>success</literal></entry>
+ <entry>The service ran successfully and exited cleanly.</entry>
+ </row>
+ <row>
+ <entry><literal>protocol</literal></entry>
+ <entry>A protocol violation occurred: the service did not take the steps required by its unit configuration (specifically what is configured in its <varname>Type=</varname> setting).</entry>
+ </row>
+ <row>
+ <entry><literal>timeout</literal></entry>
+ <entry>One of the steps timed out.</entry>
+ </row>
+ <row>
+ <entry><literal>exit-code</literal></entry>
+ <entry>Service process exited with a non-zero exit code; see <varname>$EXIT_CODE</varname> below for the actual exit code returned.</entry>
+ </row>
+ <row>
+ <entry><literal>signal</literal></entry>
+ <entry>A service process was terminated abnormally by a signal, without dumping core. See <varname>$EXIT_CODE</varname> below for the actual signal causing the termination.</entry>
+ </row>
+ <row>
+ <entry><literal>core-dump</literal></entry>
+ <entry>A service process terminated abnormally with a signal and dumped core. See <varname>$EXIT_CODE</varname> below for the signal causing the termination.</entry>
+ </row>
+ <row>
+ <entry><literal>watchdog</literal></entry>
+ <entry>Watchdog keep-alive ping was enabled for the service, but the deadline was missed.</entry>
+ </row>
+ <row>
+ <entry><literal>start-limit-hit</literal></entry>
+ <entry>A start limit was defined for the unit and it was hit, causing the unit to fail to start. See <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>'s <varname>StartLimitIntervalSec=</varname> and <varname>StartLimitBurst=</varname> for details.</entry>
+ </row>
+ <row>
+ <entry><literal>resources</literal></entry>
+ <entry>A catch-all condition in case a system operation failed.</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
<para>This environment variable is useful to monitor failure or successful termination of a service. Even
though this variable is available in both <varname>ExecStop=</varname> and <varname>ExecStopPost=</varname>, it
</thead>
<tbody>
+ <row>
+ <entry valign="top"><literal>success</literal></entry>
+ <entry valign="top"><literal>exited</literal></entry>
+ <entry><literal>0</literal></entry>
+ </row>
<row>
<entry morerows="1" valign="top"><literal>protocol</literal></entry>
<entry valign="top">not set</entry>
<entry><literal>exited</literal></entry>
<entry><literal>0</literal></entry>
</row>
-
<row>
<entry morerows="1" valign="top"><literal>timeout</literal></entry>
<entry valign="top"><literal>killed</literal></entry>
<entry><literal>0</literal>, <literal>1</literal>, <literal>2</literal>, <literal
>3</literal>, …, <literal>255</literal></entry>
</row>
-
<row>
<entry valign="top"><literal>exit-code</literal></entry>
<entry valign="top"><literal>exited</literal></entry>
- <entry><literal>0</literal>, <literal>1</literal>, <literal>2</literal>, <literal
+ <entry><literal>1</literal>, <literal>2</literal>, <literal
>3</literal>, …, <literal>255</literal></entry>
</row>
-
<row>
<entry valign="top"><literal>signal</literal></entry>
<entry valign="top"><literal>killed</literal></entry>
<entry><literal>HUP</literal>, <literal>INT</literal>, <literal>KILL</literal>, …</entry>
</row>
-
<row>
<entry valign="top"><literal>core-dump</literal></entry>
<entry valign="top"><literal>dumped</literal></entry>
<entry><literal>ABRT</literal>, <literal>SEGV</literal>, <literal>QUIT</literal>, …</entry>
</row>
-
<row>
<entry morerows="2" valign="top"><literal>watchdog</literal></entry>
<entry><literal>dumped</literal></entry>
<entry><literal>0</literal>, <literal>1</literal>, <literal>2</literal>, <literal
>3</literal>, …, <literal>255</literal></entry>
</row>
-
+ <row>
+ <entry><literal>start-limit-hit</literal></entry>
+ <entry>not set</entry>
+ <entry>not set</entry>
+ </row>
<row>
<entry><literal>resources</literal></entry>
<entry>any of the above</entry>
<entry>any of the above</entry>
</row>
-
<row>
- <entry namest="results" nameend="code">Note: the process may be also terminated by a signal not sent by systemd. In particular the process may send an arbitrary signal to itself in a handler for any of the non-maskable signals. Nevertheless, in the <literal>timeout</literal> and <literal>watchdog</literal> rows above only the signals that systemd sends have been included.</entry>
+ <entry namest="results" nameend="status">Note: the process may be also terminated by a signal not sent by systemd. In particular the process may send an arbitrary signal to itself in a handler for any of the non-maskable signals. Nevertheless, in the <literal>timeout</literal> and <literal>watchdog</literal> rows above only the signals that systemd sends have been included. Moreover, using <varname>SuccessExitStatus=</varname> additional exit statuses may be declared to indicate clean termination, which is not reflected by this table.</entry>
</row>
</tbody>
</tgroup>
</listitem>
</varlistentry>
</variablelist>
-
- <para>Additional variables may be configured by the following
- means: for processes spawned in specific units, use the
- <varname>Environment=</varname>, <varname>EnvironmentFile=</varname>
- and <varname>PassEnvironment=</varname> options above; to specify
- variables globally, use <varname>DefaultEnvironment=</varname>
- (see
- <citerefentry><refentrytitle>systemd-system.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>)
- or the kernel option <varname>systemd.setenv=</varname> (see
- <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>).
- Additional variables may also be set through PAM,
- cf. <citerefentry project='man-pages'><refentrytitle>pam_env</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
</refsect1>
<refsect1>
how the device should be configured. The first (in lexical order)
of the link files that matches a given device is applied. Note
that a default file <filename>99-default.link</filename> is
- shipped by the system, any user-supplied
+ shipped by the system. Any user-supplied
<filename>.link</filename> should hence have a lexically earlier
name to be considered at all.</para>
<varname>NamePolicy=</varname> fail, or in case
<varname>NamePolicy=</varname> is missing or
disabled.</para>
+
+ <para>Note that specifying a name that the kernel might use for another
+ interface (for example <literal>eth0</literal>) is dangerous because the
+ name assignment done by udev will race with the assignment done by the
+ kernel, and only one interface may use the name. Depending on the order of
+ operations, either udev or the kernel will win, making the naming
+ unpredictable. It is best to use some different prefix, for example
+ <literal>internal0</literal>/<literal>external0</literal> or
+ <literal>lan0</literal>/<literal>lan1</literal>/<literal>lan3</literal>.
+ </para>
</listitem>
</varlistentry>
<varlistentry>
<varlistentry>
<term><varname>NotifyReady=</varname></term>
- <listitem><para>Configures support for notifications from the container's init process.
- This is equivalent to use <option>--notify-ready=</option> command line switch,
- and takes the same options. See <citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry>
- for details about the specific options supported.</para></listitem>
+ <listitem><para>Configures support for notifications from the container's init process. This is equivalent to
+ the <option>--notify-ready=</option> command line switch, and takes the same paramaters. See
+ <citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry> for details
+ about the specific options supported.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>SystemCallFilter=</varname></term>
+
+ <listitem><para>Configures the system call filter applied to containers. This is equivalent to the
+ <option>--system-call-filter=</option> command line switch, and takes the same list parameter. See
+ <citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
+ details.</para></listitem>
</varlistentry>
+
</variablelist>
</refsect1>
is used, only one AF_UNIX socket in the file system or one
FIFO may be configured for the socket unit. Use this option to
manage one or more symlinked alias names for a socket, binding
- their lifecycle together. Defaults to the empty
- list.</para></listitem>
+ their lifecycle together. If the empty string is assigned, the
+ list of paths is reset. Defaults to the empty list.</para></listitem>
</varlistentry>
<varlistentry>
</refsect1>
<refsect1>
- <title>Configuration Format</title>
+ <title>Configuration Directories and Precedence</title>
<para>Each configuration file shall be named in the style of
<filename><replaceable>package</replaceable>.conf</filename> or
The second variant should be used when it is desirable to make it
easy to override just this part of configuration.</para>
+ <para>Files in <filename>/etc/sysusers.d</filename> override files
+ with the same name in <filename>/usr/lib/sysusers.d</filename> and
+ <filename>/run/sysusers.d</filename>. Files in
+ <filename>/run/sysusers.d</filename> override files with the same
+ name in <filename>/usr/lib/sysusers.d</filename>. Packages should
+ install their configuration files in
+ <filename>/usr/lib/sysusers.d</filename>. Files in
+ <filename>/etc/sysusers.d</filename> are reserved for the local
+ administrator, who may use this logic to override the
+ configuration files installed by vendor packages. All
+ configuration files are sorted by their filename in lexicographic
+ order, regardless of which of the directories they reside in. If
+ multiple files specify the same path, the entry in the file with
+ the lexicographically earliest name will be applied. All later
+ entries for the same user and group names will be logged as warnings.
+ </para>
+
+ <para>If the administrator wants to disable a configuration file
+ supplied by the vendor, the recommended way is to place a symlink
+ to <filename>/dev/null</filename> in
+ <filename>/etc/sysusers.d/</filename> bearing the same filename.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Configuration File Format</title>
+
<para>The file format is one line per user or group containing
name, ID, GECOS field description and home directory:</para>
- <programlisting># Type Name ID GECOS
-u httpd 440 "HTTP User"
-u authd /usr/bin/authd "Authorization user"
-g input - -
-m authd input
-u root 0 "Superuser" /root</programlisting>
+ <programlisting>#Type Name ID GECOS Home directory
+u httpd 440 "HTTP User"
+u authd /usr/bin/authd "Authorization user"
+g input - -
+m authd input
+u root 0 "Superuser" /root</programlisting>
<para>Empty lines and lines beginning with the <literal>#</literal> character are ignored, and may be used for
commenting.</para>
should otherwise be left unset, or be set to
<literal>-</literal>.</para>
</refsect2>
-
</refsect1>
- <xi:include href="standard-conf.xml" xpointer="confd" />
-
<refsect1>
<title>Idempotence</title>
</refsect1>
<refsect1>
- <title>Configuration Format</title>
+ <title>Configuration Directories and Precedence</title>
<para>Each configuration file shall be named in the style of
<filename><replaceable>package</replaceable>.conf</filename> or
to <filename>/dev/null</filename> in
<filename>/etc/tmpfiles.d/</filename> bearing the same filename.
</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Configuration File Format</title>
<para>The configuration format is one line per path containing
type, path, mode, ownership, age, and argument fields:</para>
ENV{ID_NAME}="$attr{name}", ENV{ID_SERIAL}="$attr{serial}", SYMLINK+="disk/by-id/mmc-$env{ID_NAME}_$env{ID_SERIAL}"
KERNEL=="mmcblk[0-9]p[0-9]*", ENV{ID_NAME}=="?*", ENV{ID_SERIAL}=="?*", SYMLINK+="disk/by-id/mmc-$env{ID_NAME}_$env{ID_SERIAL}-part%n"
+# UBI-MTD
+SUBSYSTEM=="ubi", KERNEL=="ubi*_*", ATTRS{mtd_num}=="*", SYMLINK+="ubi_mtd%s{mtd_num}_%s{name}"
+
# Memstick
KERNEL=="msblk[0-9]|mspblk[0-9]", SUBSYSTEMS=="memstick", ATTRS{name}=="?*", ATTRS{serial}=="?*", \
ENV{ID_NAME}="$attr{name}", ENV{ID_SERIAL}="$attr{serial}", SYMLINK+="disk/by-id/memstick-$env{ID_NAME}_$env{ID_SERIAL}"
# do not edit this file, it will be overwritten on update
-ACTION=="remove", GOTO="drivers_end"
+ACTION!="add", GOTO="drivers_end"
ENV{MODALIAS}=="?*", RUN{builtin}+="kmod load $env{MODALIAS}"
SUBSYSTEM=="tifm", ENV{TIFM_CARD_TYPE}=="SD", RUN{builtin}+="kmod load tifm_sd"
#include "macro.h"
#include "missing.h"
#include "path-util.h"
+#include "stat-util.h"
#include "string-util.h"
#include "strv.h"
#include "util.h"
-static int files_add(Hashmap *h, const char *root, const char *path, const char *suffix) {
+static int files_add(Hashmap *h, const char *suffix, const char *root, unsigned flags, const char *path) {
_cleanup_closedir_ DIR *dir = NULL;
const char *dirpath;
struct dirent *de;
FOREACH_DIRENT(de, dir, return -errno) {
char *p;
- if (!dirent_is_file_with_suffix(de, suffix))
+ if (!dirent_is_file_with_suffix(de, suffix)) {
+ log_debug("Ignoring %s/%s, because it's not a regular file with suffix %s.", dirpath, de->d_name, strna(suffix));
continue;
+ }
+
+ if (flags & CONF_FILES_EXECUTABLE) {
+ struct stat st;
+
+ /* As requested: check if the file is marked exectuable. Note that we don't check access(X_OK)
+ * here, as we care about whether the file is marked executable at all, and not whether it is
+ * executable for us, because if such errors are stuff we should log about. */
+
+ if (fstatat(dirfd(dir), de->d_name, &st, 0) < 0) {
+ log_debug_errno(errno, "Failed to stat %s/%s, ignoring: %m", dirpath, de->d_name);
+ continue;
+ }
+
+ /* We only want executable regular files (or symlinks to them), or symlinks to /dev/null */
+ if (S_ISREG(st.st_mode)) {
+ if ((st.st_mode & 0111) == 0) { /* not executable */
+ log_debug("Ignoring %s/%s, as it is not marked executable.", dirpath, de->d_name);
+ continue;
+ }
+
+ } else if (!null_or_empty(&st)) { /* /dev/null? */
+ log_debug("Ignoring %s/%s, as it is not a regular file (or symlink to /dev/null).", dirpath, de->d_name);
+ continue;
+ }
+ }
p = strjoin(dirpath, "/", de->d_name);
if (!p)
return strcmp(basename(s1), basename(s2));
}
-static int conf_files_list_strv_internal(char ***strv, const char *suffix, const char *root, char **dirs) {
+static int conf_files_list_strv_internal(char ***strv, const char *suffix, const char *root, unsigned flags, char **dirs) {
_cleanup_hashmap_free_ Hashmap *fh = NULL;
char **files, **p;
int r;
return -ENOMEM;
STRV_FOREACH(p, dirs) {
- r = files_add(fh, root, *p, suffix);
+ r = files_add(fh, suffix, root, flags, *p);
if (r == -ENOMEM)
return r;
if (r < 0)
return 0;
}
-int conf_files_list_strv(char ***strv, const char *suffix, const char *root, const char* const* dirs) {
+int conf_files_list_strv(char ***strv, const char *suffix, const char *root, unsigned flags, const char* const* dirs) {
_cleanup_strv_free_ char **copy = NULL;
assert(strv);
if (!copy)
return -ENOMEM;
- return conf_files_list_strv_internal(strv, suffix, root, copy);
+ return conf_files_list_strv_internal(strv, suffix, root, flags, copy);
}
-int conf_files_list(char ***strv, const char *suffix, const char *root, const char *dir, ...) {
+int conf_files_list(char ***strv, const char *suffix, const char *root, unsigned flags, const char *dir, ...) {
_cleanup_strv_free_ char **dirs = NULL;
va_list ap;
if (!dirs)
return -ENOMEM;
- return conf_files_list_strv_internal(strv, suffix, root, dirs);
+ return conf_files_list_strv_internal(strv, suffix, root, flags, dirs);
}
-int conf_files_list_nulstr(char ***strv, const char *suffix, const char *root, const char *d) {
+int conf_files_list_nulstr(char ***strv, const char *suffix, const char *root, unsigned flags, const char *d) {
_cleanup_strv_free_ char **dirs = NULL;
assert(strv);
if (!dirs)
return -ENOMEM;
- return conf_files_list_strv_internal(strv, suffix, root, dirs);
+ return conf_files_list_strv_internal(strv, suffix, root, flags, dirs);
}
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-int conf_files_list(char ***ret, const char *suffix, const char *root, const char *dir, ...);
-int conf_files_list_strv(char ***ret, const char *suffix, const char *root, const char* const* dirs);
-int conf_files_list_nulstr(char ***ret, const char *suffix, const char *root, const char *dirs);
+enum {
+ CONF_FILES_EXECUTABLE = 1,
+};
+
+int conf_files_list(char ***ret, const char *suffix, const char *root, unsigned flags, const char *dir, ...);
+int conf_files_list_strv(char ***ret, const char *suffix, const char *root, unsigned flags, const char* const* dirs);
+int conf_files_list_nulstr(char ***ret, const char *suffix, const char *root, unsigned flags, const char *dirs);
return parse_boolean(e);
}
+int getenv_bool_secure(const char *p) {
+ const char *e;
+
+ e = secure_getenv(p);
+ if (!e)
+ return -ENXIO;
+
+ return parse_boolean(e);
+}
+
int serialize_environment(FILE *f, char **environment) {
char **e;
char *strv_env_get(char **x, const char *n) _pure_;
int getenv_bool(const char *p);
+int getenv_bool_secure(const char *p);
int serialize_environment(FILE *f, char **environment);
int deserialize_environment(char ***environment, const char *line);
assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
- r = conf_files_list_strv(&paths, NULL, NULL, (const char* const*) directories);
+ r = conf_files_list_strv(&paths, NULL, NULL, CONF_FILES_EXECUTABLE, (const char* const*) directories);
if (r < 0)
return r;
#else
#define __O_TMPFILE 020000000
#endif
+#endif
/* a horrid kludge trying to make sure that this will fail on old kernels */
#ifndef O_TMPFILE
#define O_TMPFILE (__O_TMPFILE | O_DIRECTORY)
#endif
-#endif
-
#if !HAVE_DECL_LO_FLAGS_PARTSCAN
#define LO_FLAGS_PARTSCAN 8
#endif
#include <unistd.h>
#include "alloc-util.h"
+#include "btrfs-util.h"
#include "build.h"
#include "cgroup-util.h"
#include "def.h"
SYSTEMD_FEATURES);
return 0;
}
+
+int get_block_device(const char *path, dev_t *dev) {
+ struct stat st;
+ struct statfs sfs;
+
+ assert(path);
+ assert(dev);
+
+ /* Get's the block device directly backing a file system. If
+ * the block device is encrypted, returns the device mapper
+ * block device. */
+
+ if (lstat(path, &st))
+ return -errno;
+
+ if (major(st.st_dev) != 0) {
+ *dev = st.st_dev;
+ return 1;
+ }
+
+ if (statfs(path, &sfs) < 0)
+ return -errno;
+
+ if (F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC))
+ return btrfs_get_block_device(path, dev);
+
+ return 0;
+}
+
+int get_block_device_harder(const char *path, dev_t *dev) {
+ _cleanup_closedir_ DIR *d = NULL;
+ _cleanup_free_ char *p = NULL, *t = NULL;
+ struct dirent *de, *found = NULL;
+ const char *q;
+ unsigned maj, min;
+ dev_t dt;
+ int r;
+
+ assert(path);
+ assert(dev);
+
+ /* Gets the backing block device for a file system, and
+ * handles LUKS encrypted file systems, looking for its
+ * immediate parent, if there is one. */
+
+ r = get_block_device(path, &dt);
+ if (r <= 0)
+ return r;
+
+ if (asprintf(&p, "/sys/dev/block/%u:%u/slaves", major(dt), minor(dt)) < 0)
+ return -ENOMEM;
+
+ d = opendir(p);
+ if (!d) {
+ if (errno == ENOENT)
+ goto fallback;
+
+ return -errno;
+ }
+
+ FOREACH_DIRENT_ALL(de, d, return -errno) {
+
+ if (dot_or_dot_dot(de->d_name))
+ continue;
+
+ if (!IN_SET(de->d_type, DT_LNK, DT_UNKNOWN))
+ continue;
+
+ if (found) {
+ _cleanup_free_ char *u = NULL, *v = NULL, *a = NULL, *b = NULL;
+
+ /* We found a device backed by multiple other devices. We don't really support automatic
+ * discovery on such setups, with the exception of dm-verity partitions. In this case there are
+ * two backing devices: the data partition and the hash partition. We are fine with such
+ * setups, however, only if both partitions are on the same physical device. Hence, let's
+ * verify this. */
+
+ u = strjoin(p, "/", de->d_name, "/../dev");
+ if (!u)
+ return -ENOMEM;
+
+ v = strjoin(p, "/", found->d_name, "/../dev");
+ if (!v)
+ return -ENOMEM;
+
+ r = read_one_line_file(u, &a);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to read %s: %m", u);
+ goto fallback;
+ }
+
+ r = read_one_line_file(v, &b);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to read %s: %m", v);
+ goto fallback;
+ }
+
+ /* Check if the parent device is the same. If not, then the two backing devices are on
+ * different physical devices, and we don't support that. */
+ if (!streq(a, b))
+ goto fallback;
+ }
+
+ found = de;
+ }
+
+ if (!found)
+ goto fallback;
+
+ q = strjoina(p, "/", found->d_name, "/dev");
+
+ r = read_one_line_file(q, &t);
+ if (r == -ENOENT)
+ goto fallback;
+ if (r < 0)
+ return r;
+
+ if (sscanf(t, "%u:%u", &maj, &min) != 2)
+ return -EINVAL;
+
+ if (maj == 0)
+ goto fallback;
+
+ *dev = makedev(maj, min);
+ return 1;
+
+fallback:
+ *dev = dt;
+ return 1;
+}
int update_reboot_parameter_and_warn(const char *param);
int version(void);
+
+int get_block_device(const char *path, dev_t *dev);
+int get_block_device_harder(const char *path, dev_t *dev);
_cleanup_strv_free_ char **files = NULL;
char **f;
- r = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs);
+ r = conf_files_list_nulstr(&files, ".conf", NULL, 0, conf_file_dirs);
if (r < 0) {
log_error_errno(r, "Failed to enumerate binfmt.d files: %m");
goto finish;
SD_BUS_PROPERTY("Environment", "as", NULL, offsetof(ExecContext, environment), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("EnvironmentFiles", "a(sb)", property_get_environment_files, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("PassEnvironment", "as", NULL, offsetof(ExecContext, pass_environment), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("UnsetEnvironment", "as", NULL, offsetof(ExecContext, unset_environment), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("UMask", "u", bus_property_get_mode, offsetof(ExecContext, umask), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("LimitCPU", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_CPU]), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("LimitCPUSoft", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_CPU]), SD_BUS_VTABLE_PROPERTY_CONST),
if (r < 0)
return r;
- if (!strv_env_is_valid(l))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid environment block.");
-
r = unit_full_printf_strv(u, l, &q);
if (r < 0)
return r;
+ if (!strv_env_is_valid(q))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid environment block.");
+
if (mode != UNIT_CHECK) {
if (strv_length(q) == 0) {
c->environment = strv_free(c->environment);
c->environment = e;
/* We write just the new settings out to file, with unresolved specifiers */
- joined = strv_join_quoted(q);
+ joined = strv_join_quoted(l);
if (!joined)
return -ENOMEM;
return 1;
+ } else if (streq(name, "UnsetEnvironment")) {
+
+ _cleanup_strv_free_ char **l = NULL, **q = NULL;
+
+ r = sd_bus_message_read_strv(message, &l);
+ if (r < 0)
+ return r;
+
+ r = unit_full_printf_strv(u, l, &q);
+ if (r < 0)
+ return r;
+
+ if (!strv_env_name_or_assignment_is_valid(q))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid UnsetEnvironment= list.");
+
+ if (mode != UNIT_CHECK) {
+ if (strv_length(q) == 0) {
+ c->unset_environment = strv_free(c->unset_environment);
+ unit_write_drop_in_private_format(u, mode, name, "UnsetEnvironment=");
+ } else {
+ _cleanup_free_ char *joined = NULL;
+ char **e;
+
+ e = strv_env_merge(2, c->unset_environment, q);
+ if (!e)
+ return -ENOMEM;
+
+ strv_free(c->unset_environment);
+ c->unset_environment = e;
+
+ /* We write just the new settings out to file, with unresolved specifiers */
+ joined = strv_join_quoted(l);
+ if (!joined)
+ return -ENOMEM;
+
+ unit_write_drop_in_private_format(u, mode, name, "UnsetEnvironment=%s", joined);
+ }
+ }
+
+ return 1;
+
} else if (streq(name, "TimerSlackNSec")) {
nsec_t n;
} else if (streq(name, "PassEnvironment")) {
- _cleanup_strv_free_ char **l = NULL;
+ _cleanup_strv_free_ char **l = NULL, **q = NULL;
r = sd_bus_message_read_strv(message, &l);
if (r < 0)
return r;
- if (!strv_env_name_is_valid(l))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid PassEnvironment block.");
+ r = unit_full_printf_strv(u, l, &q);
+ if (r < 0)
+ return r;
+
+ if (!strv_env_name_is_valid(q))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid PassEnvironment= block.");
if (mode != UNIT_CHECK) {
if (strv_isempty(l)) {
} else {
_cleanup_free_ char *joined = NULL;
- r = strv_extend_strv(&c->pass_environment, l, true);
+ r = strv_extend_strv(&c->pass_environment, q, true);
if (r < 0)
return r;
- joined = strv_join_quoted(c->pass_environment);
+ /* We write just the new settings out to file, with unresolved specifiers. */
+ joined = strv_join_quoted(l);
if (!joined)
return -ENOMEM;
return 0;
}
- if (streq(action, "remove")) {
+ if (streq(action, "change")) {
+ _cleanup_free_ char *e = NULL;
+ Unit *u;
+
+ r = unit_name_from_path(sysfs, ".device", &e);
+ if (r < 0)
+ return log_error_errno(r, "Failed to generate unit name from device path: %m");
+
+ u = manager_get_unit(m, e);
+ if (!u) {
+ log_error("Failed to get unit from sysfs name.");
+ return 0;
+ }
+
+ r = manager_add_job(m, JOB_RELOAD, u, JOB_REPLACE, NULL, NULL);
+ if (r < 0) {
+ log_error_errno(r, "Failed to add job to manager : %m");
+ return 0;
+ }
+
+ } else if (streq(action, "remove")) {
r = swap_process_device_remove(m, dev);
if (r < 0)
log_error_errno(r, "Failed to process swap device remove event: %m");
x = strjoin(*i, "=", v);
if (!x)
return -ENOMEM;
+
if (!GREEDY_REALLOC(pass_env, n_bufsize, n_env + 2))
return -ENOMEM;
+
pass_env[n_env++] = x;
pass_env[n_env] = NULL;
x = NULL;
#endif
}
+ if (!strv_isempty(context->unset_environment)) {
+ char **ee = NULL;
+
+ ee = strv_env_delete(accum_env, 1, context->unset_environment);
+ if (!ee) {
+ *exit_status = EXIT_MEMORY;
+ return -ENOMEM;
+ }
+
+ strv_free(accum_env);
+ accum_env = ee;
+ }
+
final_argv = replace_env_argv(argv, accum_env);
if (!final_argv) {
*exit_status = EXIT_MEMORY;
c->environment = strv_free(c->environment);
c->environment_files = strv_free(c->environment_files);
c->pass_environment = strv_free(c->pass_environment);
+ c->unset_environment = strv_free(c->unset_environment);
for (l = 0; l < ELEMENTSOF(c->rlimit); l++)
c->rlimit[l] = mfree(c->rlimit[l]);
STRV_FOREACH(e, c->pass_environment)
fprintf(f, "%sPassEnvironment: %s\n", prefix, *e);
+ STRV_FOREACH(e, c->unset_environment)
+ fprintf(f, "%sUnsetEnvironment: %s\n", prefix, *e);
+
fprintf(f, "%sRuntimeDirectoryPreserve: %s\n", prefix, exec_preserve_mode_to_string(c->runtime_directory_preserve_mode));
for (dt = 0; dt < _EXEC_DIRECTORY_MAX; dt++) {
char **environment;
char **environment_files;
char **pass_environment;
+ char **unset_environment;
struct rlimit *rlimit[_RLIMIT_MAX];
char *working_directory, *root_directory, *root_image;
$1.Environment, config_parse_environ, 0, offsetof($1, exec_context.environment)
$1.EnvironmentFile, config_parse_unit_env_file, 0, offsetof($1, exec_context.environment_files)
$1.PassEnvironment, config_parse_pass_environ, 0, offsetof($1, exec_context.pass_environment)
+$1.UnsetEnvironment, config_parse_unset_environ, 0, offsetof($1, exec_context.unset_environment)
$1.DynamicUser, config_parse_bool, true, offsetof($1, exec_context.dynamic_user)
$1.StandardInput, config_parse_exec_input, 0, offsetof($1, exec_context)
$1.StandardOutput, config_parse_exec_output, 0, offsetof($1, exec_context)
assert(rvalue);
assert(u);
+ if (isempty(rvalue)) {
+ char **empty;
+
+ /* Empty assignment resets the list. As a special rule
+ * we actually fill in a real empty array here rather
+ * than NULL, since some code wants to know if
+ * something was set at all... */
+ empty = new0(char*, 1);
+ if (!empty)
+ return log_oom();
+
+ strv_free(*x);
+ *x = empty;
+
+ return 0;
+ }
+
for (p = rvalue;;) {
_cleanup_free_ char *word = NULL, *k = NULL;
return 0;
}
-int config_parse_environ(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) {
+int config_parse_environ(
+ 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) {
Unit *u = userdata;
char ***env = data;
r = unit_full_printf(u, word, &k);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r,
- "Failed to resolve specifiers, ignoring: %s", k);
+ "Failed to resolve specifiers, ignoring: %s", word);
continue;
}
} else {
r = strv_env_replace(env, k);
if (r < 0)
return log_oom();
+
k = NULL;
}
}
-int config_parse_pass_environ(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) {
+int config_parse_pass_environ(
+ 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) {
const char *whole_rvalue = rvalue;
- char*** passenv = data;
_cleanup_strv_free_ char **n = NULL;
size_t nlen = 0, nbufsize = 0;
+ char*** passenv = data;
+ Unit *u = userdata;
int r;
assert(filename);
}
for (;;) {
- _cleanup_free_ char *word = NULL;
+ _cleanup_free_ char *word = NULL, *k = NULL;
r = extract_first_word(&rvalue, &word, NULL, EXTRACT_QUOTES);
if (r == 0)
break;
}
- if (!env_name_is_valid(word)) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Invalid environment name for %s, ignoring: %s", lvalue, word);
+ if (u) {
+ r = unit_full_printf(u, word, &k);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Failed to resolve specifiers, ignoring: %s", word);
+ continue;
+ }
+ } else {
+ k = word;
+ word = NULL;
+ }
+
+ if (!env_name_is_valid(k)) {
+ log_syntax(unit, LOG_ERR, filename, line, 0,
+ "Invalid environment name for %s, ignoring: %s", lvalue, k);
continue;
}
if (!GREEDY_REALLOC(n, nbufsize, nlen + 2))
return log_oom();
- n[nlen++] = word;
+
+ n[nlen++] = k;
n[nlen] = NULL;
- word = NULL;
+ k = NULL;
}
if (n) {
return 0;
}
+int config_parse_unset_environ(
+ 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) {
+
+ _cleanup_strv_free_ char **n = NULL;
+ const char *whole_rvalue = rvalue;
+ size_t nlen = 0, nbufsize = 0;
+ char*** unsetenv = data;
+ Unit *u = userdata;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if (isempty(rvalue)) {
+ /* Empty assignment resets the list */
+ *unsetenv = strv_free(*unsetenv);
+ return 0;
+ }
+
+ for (;;) {
+ _cleanup_free_ char *word = NULL, *k = NULL;
+
+ r = extract_first_word(&rvalue, &word, NULL, EXTRACT_QUOTES);
+ if (r == 0)
+ break;
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Trailing garbage in %s, ignoring: %s", lvalue, whole_rvalue);
+ break;
+ }
+
+ if (u) {
+ r = unit_full_printf(u, word, &k);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Failed to resolve specifiers, ignoring: %s", word);
+ continue;
+ }
+ } else {
+ k = word;
+ word = NULL;
+ }
+
+ if (!env_assignment_is_valid(k) && !env_name_is_valid(k)) {
+ log_syntax(unit, LOG_ERR, filename, line, 0,
+ "Invalid environment name or assignment %s, ignoring: %s", lvalue, k);
+ continue;
+ }
+
+ if (!GREEDY_REALLOC(n, nbufsize, nlen + 2))
+ return log_oom();
+
+ n[nlen++] = k;
+ n[nlen] = NULL;
+ k = NULL;
+ }
+
+ if (n) {
+ r = strv_extend_strv(unsetenv, n, true);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
int config_parse_ip_tos(const char *unit,
const char *filename,
unsigned line,
int config_parse_syscall_errno(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);
int config_parse_environ(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);
int config_parse_pass_environ(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);
+int config_parse_unset_environ(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);
int config_parse_unit_slice(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);
int config_parse_cpu_weight(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);
int config_parse_cpu_shares(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);
log_close();
/* Remember open file descriptors for later deserialization */
- r = fdset_new_fill(&fds);
- if (r < 0) {
- log_emergency_errno(r, "Failed to allocate fd set: %m");
- error_message = "Failed to allocate fd set";
- goto finish;
- } else
- fdset_cloexec(fds, true);
+ if (arg_action == ACTION_RUN) {
+ r = fdset_new_fill(&fds);
+ if (r < 0) {
+ log_emergency_errno(r, "Failed to allocate fd set: %m");
+ error_message = "Failed to allocate fd set";
+ goto finish;
+ } else
+ fdset_cloexec(fds, true);
- if (arg_serialization)
- assert_se(fdset_remove(fds, fileno(arg_serialization)) >= 0);
+ if (arg_serialization)
+ assert_se(fdset_remove(fds, fileno(arg_serialization)) >= 0);
- if (arg_system)
- /* Become a session leader if we aren't one yet. */
- setsid();
+ if (arg_system)
+ /* Become a session leader if we aren't one yet. */
+ setsid();
+ }
/* Move out of the way, so that we won't block unmounts */
assert_se(chdir("/") == 0);
arg_action == ACTION_TEST ? " test" : "", getuid(), t);
}
- if (arg_system && !skip_setup) {
- if (arg_show_status > 0)
- status_welcome();
+ if (arg_action == ACTION_RUN) {
+ if (arg_system && !skip_setup) {
+ if (arg_show_status > 0)
+ status_welcome();
- hostname_setup();
- machine_id_setup(NULL, arg_machine_id, NULL);
- loopback_setup();
- bump_unix_max_dgram_qlen();
+ hostname_setup();
+ machine_id_setup(NULL, arg_machine_id, NULL);
+ loopback_setup();
+ bump_unix_max_dgram_qlen();
- test_usr();
- }
+ test_usr();
+ }
- if (arg_system && arg_runtime_watchdog > 0 && arg_runtime_watchdog != USEC_INFINITY)
- watchdog_set_timeout(&arg_runtime_watchdog);
+ if (arg_system && arg_runtime_watchdog > 0 && arg_runtime_watchdog != USEC_INFINITY)
+ watchdog_set_timeout(&arg_runtime_watchdog);
- if (arg_timer_slack_nsec != NSEC_INFINITY)
- if (prctl(PR_SET_TIMERSLACK, arg_timer_slack_nsec) < 0)
- log_error_errno(errno, "Failed to adjust timer slack: %m");
+ if (arg_timer_slack_nsec != NSEC_INFINITY)
+ if (prctl(PR_SET_TIMERSLACK, arg_timer_slack_nsec) < 0)
+ log_error_errno(errno, "Failed to adjust timer slack: %m");
- if (arg_system && !cap_test_all(arg_capability_bounding_set)) {
- r = capability_bounding_set_drop_usermode(arg_capability_bounding_set);
- if (r < 0) {
- log_emergency_errno(r, "Failed to drop capability bounding set of usermode helpers: %m");
- error_message = "Failed to drop capability bounding set of usermode helpers";
- goto finish;
- }
- r = capability_bounding_set_drop(arg_capability_bounding_set, true);
- if (r < 0) {
- log_emergency_errno(r, "Failed to drop capability bounding set: %m");
- error_message = "Failed to drop capability bounding set";
- goto finish;
+ if (arg_system && !cap_test_all(arg_capability_bounding_set)) {
+ r = capability_bounding_set_drop_usermode(arg_capability_bounding_set);
+ if (r < 0) {
+ log_emergency_errno(r, "Failed to drop capability bounding set of usermode helpers: %m");
+ error_message = "Failed to drop capability bounding set of usermode helpers";
+ goto finish;
+ }
+ r = capability_bounding_set_drop(arg_capability_bounding_set, true);
+ if (r < 0) {
+ log_emergency_errno(r, "Failed to drop capability bounding set: %m");
+ error_message = "Failed to drop capability bounding set";
+ goto finish;
+ }
}
- }
- if (arg_syscall_archs) {
- r = enforce_syscall_archs(arg_syscall_archs);
- if (r < 0) {
- error_message = "Failed to set syscall architectures";
- goto finish;
+ if (arg_syscall_archs) {
+ r = enforce_syscall_archs(arg_syscall_archs);
+ if (r < 0) {
+ error_message = "Failed to set syscall architectures";
+ goto finish;
+ }
}
- }
- if (!arg_system)
- /* Become reaper of our children */
- if (prctl(PR_SET_CHILD_SUBREAPER, 1) < 0)
- log_warning_errno(errno, "Failed to make us a subreaper: %m");
+ if (!arg_system)
+ /* Become reaper of our children */
+ if (prctl(PR_SET_CHILD_SUBREAPER, 1) < 0)
+ log_warning_errno(errno, "Failed to make us a subreaper: %m");
- if (arg_system)
- /* Bump up RLIMIT_NOFILE for systemd itself */
- (void) bump_rlimit_nofile(&saved_rlimit_nofile);
+ if (arg_system)
+ /* Bump up RLIMIT_NOFILE for systemd itself */
+ (void) bump_rlimit_nofile(&saved_rlimit_nofile);
+ }
r = manager_new(arg_system ? UNIT_FILE_SYSTEM : UNIT_FILE_USER, arg_action == ACTION_TEST, &m);
if (r < 0) {
if (t->stamp_path) {
struct stat st;
- if (stat(t->stamp_path, &st) >= 0)
- t->last_trigger.realtime = timespec_load(&st.st_atim);
- else if (errno == ENOENT)
+ if (stat(t->stamp_path, &st) >= 0) {
+ usec_t ft;
+
+ /* Load the file timestamp, but only if it is actually in the past. If it is in the future,
+ * something is wrong with the system clock. */
+
+ ft = timespec_load(&st.st_mtim);
+ if (ft < now(CLOCK_REALTIME))
+ t->last_trigger.realtime = ft;
+ else {
+ char z[FORMAT_TIMESTAMP_MAX];
+
+ log_unit_warning(u, "Not using persistent file timestamp %s as it is in the future.",
+ format_timestamp(z, sizeof(z), ft));
+ }
+
+ } else if (errno == ENOENT)
/* The timer has never run before,
* make sure a stamp file exists.
*/
"%ms" /* (11) mount options */
"%*[^\n]", /* some rubbish at the end */
&path, &type, &options);
- if (k != 2) {
+ if (k != 3) {
if (k == EOF)
break;
static int dm_points_list_detach(MountPoint **head, bool *changed) {
MountPoint *m, *n;
- int n_failed = 0, k;
- struct stat root_st;
+ int n_failed = 0, r;
+ dev_t rootdev;
assert(head);
- k = lstat("/", &root_st);
+ r = get_block_device("/", &rootdev);
+ if (r <= 0)
+ rootdev = 0;
LIST_FOREACH_SAFE(mount_point, m, n, *head) {
- int r;
- if (k >= 0 &&
- major(root_st.st_dev) != 0 &&
- root_st.st_dev == m->devnum) {
- n_failed++;
- continue;
- }
+ if (major(rootdev) != 0)
+ if (rootdev == m->devnum) {
+ n_failed ++;
+ continue;
+ }
log_info("Detaching DM %u:%u.", major(m->devnum), minor(m->devnum));
r = delete_dm(m->devnum);
unit_add_to_dbus_queue(u);
+ if (!UNIT_VTABLE(u)->reload) {
+ /* Unit doesn't have a reload function, but we need to propagate the reload anyway */
+ unit_notify(u, unit_active_state(u), unit_active_state(u), true);
+ return 0;
+ }
+
return UNIT_VTABLE(u)->reload(u);
}
bool unit_can_reload(Unit *u) {
assert(u);
- if (!UNIT_VTABLE(u)->reload)
- return false;
+ if (UNIT_VTABLE(u)->can_reload)
+ return UNIT_VTABLE(u)->can_reload(u);
- if (!UNIT_VTABLE(u)->can_reload)
+ if (!set_isempty(u->dependencies[UNIT_PROPAGATES_RELOAD_TO]))
return true;
- return UNIT_VTABLE(u)->can_reload(u);
+ return UNIT_VTABLE(u)->reload;
}
static void unit_check_unneeded(Unit *u) {
if (r < 0)
return r;
- r = conf_files_list_strv(&files, ".conf", NULL, (const char **) dirs);
+ r = conf_files_list_strv(&files, ".conf", NULL, 0, (const char **) dirs);
if (r < 0)
return r;
return r;
}
-static int get_block_device(const char *path, dev_t *dev) {
- struct stat st;
- struct statfs sfs;
-
- assert(path);
- assert(dev);
-
- /* Get's the block device directly backing a file system. If
- * the block device is encrypted, returns the device mapper
- * block device. */
-
- if (lstat(path, &st))
- return -errno;
-
- if (major(st.st_dev) != 0) {
- *dev = st.st_dev;
- return 1;
- }
-
- if (statfs(path, &sfs) < 0)
- return -errno;
-
- if (F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC))
- return btrfs_get_block_device(path, dev);
-
- return 0;
-}
-
-static int get_block_device_harder(const char *path, dev_t *dev) {
- _cleanup_closedir_ DIR *d = NULL;
- _cleanup_free_ char *p = NULL, *t = NULL;
- struct dirent *de, *found = NULL;
- const char *q;
- unsigned maj, min;
- dev_t dt;
- int r;
-
- assert(path);
- assert(dev);
-
- /* Gets the backing block device for a file system, and
- * handles LUKS encrypted file systems, looking for its
- * immediate parent, if there is one. */
-
- r = get_block_device(path, &dt);
- if (r <= 0)
- return r;
-
- if (asprintf(&p, "/sys/dev/block/%u:%u/slaves", major(dt), minor(dt)) < 0)
- return -ENOMEM;
-
- d = opendir(p);
- if (!d) {
- if (errno == ENOENT)
- goto fallback;
-
- return -errno;
- }
-
- FOREACH_DIRENT_ALL(de, d, return -errno) {
-
- if (dot_or_dot_dot(de->d_name))
- continue;
-
- if (!IN_SET(de->d_type, DT_LNK, DT_UNKNOWN))
- continue;
-
- if (found) {
- _cleanup_free_ char *u = NULL, *v = NULL, *a = NULL, *b = NULL;
-
- /* We found a device backed by multiple other devices. We don't really support automatic
- * discovery on such setups, with the exception of dm-verity partitions. In this case there are
- * two backing devices: the data partition and the hash partition. We are fine with such
- * setups, however, only if both partitions are on the same physical device. Hence, let's
- * verify this. */
-
- u = strjoin(p, "/", de->d_name, "/../dev");
- if (!u)
- return -ENOMEM;
-
- v = strjoin(p, "/", found->d_name, "/../dev");
- if (!v)
- return -ENOMEM;
-
- r = read_one_line_file(u, &a);
- if (r < 0) {
- log_debug_errno(r, "Failed to read %s: %m", u);
- goto fallback;
- }
-
- r = read_one_line_file(v, &b);
- if (r < 0) {
- log_debug_errno(r, "Failed to read %s: %m", v);
- goto fallback;
- }
-
- /* Check if the parent device is the same. If not, then the two backing devices are on
- * different physical devices, and we don't support that. */
- if (!streq(a, b))
- goto fallback;
- }
-
- found = de;
- }
-
- if (!found)
- goto fallback;
-
- q = strjoina(p, "/", found->d_name, "/dev");
-
- r = read_one_line_file(q, &t);
- if (r == -ENOENT)
- goto fallback;
- if (r < 0)
- return r;
-
- if (sscanf(t, "%u:%u", &maj, &min) != 2)
- return -EINVAL;
-
- if (maj == 0)
- goto fallback;
-
- *dev = makedev(maj, min);
- return 1;
-
-fallback:
- *dev = dt;
- return 1;
-}
-
static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
int r;
trie->nodes_count++;
- r = conf_files_list_strv(&files, ".hwdb", arg_root, conf_file_dirs);
+ r = conf_files_list_strv(&files, ".hwdb", arg_root, 0, conf_file_dirs);
if (r < 0)
return log_error_errno(r, "Failed to enumerate hwdb files: %m");
goto finish;
}
- r = conf_files_list_strv(&files, ".catalog", root, dirs);
+ r = conf_files_list_strv(&files, ".catalog", root, 0, dirs);
if (r < 0) {
log_error_errno(r, "Failed to get catalog files: %m");
goto finish;
#include "bus-error.h"
#include "bus-kernel.h"
#include "bus-match.h"
+#include "def.h"
#include "hashmap.h"
#include "list.h"
#include "prioq.h"
LIST_HEAD(sd_bus_track, tracks);
};
+/* For method calls we time-out at 25s, like in the D-Bus reference implementation */
#define BUS_DEFAULT_TIMEOUT ((usec_t) (25 * USEC_PER_SEC))
+/* For the authentication phase we grant 90s, to provide extra room during boot, when RNGs and such are not filled up
+ * with enough entropy yet and might delay the boot */
+#define BUS_AUTH_TIMEOUT ((usec_t) DEFAULT_TIMEOUT_USEC)
+
#define BUS_WQUEUE_MAX (192*1024)
#define BUS_RQUEUE_MAX (192*1024)
bus_get_peercred(b);
b->state = BUS_AUTHENTICATING;
- b->auth_timeout = now(CLOCK_MONOTONIC) + BUS_DEFAULT_TIMEOUT;
+ b->auth_timeout = now(CLOCK_MONOTONIC) + BUS_AUTH_TIMEOUT;
if (sd_is_socket(b->input_fd, AF_UNIX, 0, 0) <= 0)
b->hello_flags &= ~KDBUS_HELLO_ACCEPT_FD;
r = k;
}
- k = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs);
+ k = conf_files_list_nulstr(&files, ".conf", NULL, 0, conf_file_dirs);
if (k < 0) {
log_error_errno(k, "Failed to enumerate modules-load.d files: %m");
if (r == 0)
while ((netdev = hashmap_first(manager->netdevs)))
netdev_unref(netdev);
- r = conf_files_list_strv(&files, ".netdev", NULL, network_dirs);
+ r = conf_files_list_strv(&files, ".netdev", NULL, 0, network_dirs);
if (r < 0)
return log_error_errno(r, "Failed to enumerate netdev files: %m");
while ((network = manager->networks))
network_free(network);
- r = conf_files_list_strv(&files, ".network", NULL, network_dirs);
+ r = conf_files_list_strv(&files, ".network", NULL, 0, network_dirs);
if (r < 0)
return log_error_errno(r, "Failed to enumerate network files: %m");
Exec.PivotRoot, config_parse_pivot_root, 0, 0
Exec.PrivateUsers, config_parse_private_users, 0, 0
Exec.NotifyReady, config_parse_bool, 0, offsetof(Settings, notify_ready)
+Exec.SystemCallFilter, config_parse_syscall_filter,0, 0,
Files.ReadOnly, config_parse_tristate, 0, offsetof(Settings, read_only)
Files.Volatile, config_parse_volatile_mode, 0, offsetof(Settings, volatile_mode)
Files.Bind, config_parse_bind, 0, 0
#include "seccomp-util.h"
#endif
#include "string-util.h"
+#include "strv.h"
#ifdef HAVE_SECCOMP
static int seccomp_add_default_syscall_filter(
scmp_filter_ctx ctx,
uint32_t arch,
- uint64_t cap_list_retain) {
+ uint64_t cap_list_retain,
+ char **syscall_whitelist,
+ char **syscall_blacklist) {
static const struct {
uint64_t capability;
- int syscall_num;
- } blacklist[] = {
- { 0, SCMP_SYS(_sysctl) }, /* obsolete syscall */
- { 0, SCMP_SYS(add_key) }, /* keyring is not namespaced */
- { 0, SCMP_SYS(afs_syscall) }, /* obsolete syscall */
- { 0, SCMP_SYS(bdflush) },
-#ifdef __NR_bpf
- { 0, SCMP_SYS(bpf) },
-#endif
- { 0, SCMP_SYS(break) }, /* obsolete syscall */
- { 0, SCMP_SYS(create_module) }, /* obsolete syscall */
- { 0, SCMP_SYS(ftime) }, /* obsolete syscall */
- { 0, SCMP_SYS(get_kernel_syms) }, /* obsolete syscall */
- { 0, SCMP_SYS(getpmsg) }, /* obsolete syscall */
- { 0, SCMP_SYS(gtty) }, /* obsolete syscall */
-#ifdef __NR_kexec_file_load
- { 0, SCMP_SYS(kexec_file_load) },
-#endif
- { 0, SCMP_SYS(kexec_load) },
- { 0, SCMP_SYS(keyctl) }, /* keyring is not namespaced */
- { 0, SCMP_SYS(lock) }, /* obsolete syscall */
- { 0, SCMP_SYS(lookup_dcookie) },
- { 0, SCMP_SYS(mpx) }, /* obsolete syscall */
- { 0, SCMP_SYS(nfsservctl) }, /* obsolete syscall */
- { 0, SCMP_SYS(open_by_handle_at) },
- { 0, SCMP_SYS(perf_event_open) },
- { 0, SCMP_SYS(prof) }, /* obsolete syscall */
- { 0, SCMP_SYS(profil) }, /* obsolete syscall */
- { 0, SCMP_SYS(putpmsg) }, /* obsolete syscall */
- { 0, SCMP_SYS(query_module) }, /* obsolete syscall */
- { 0, SCMP_SYS(quotactl) },
- { 0, SCMP_SYS(request_key) }, /* keyring is not namespaced */
- { 0, SCMP_SYS(security) }, /* obsolete syscall */
- { 0, SCMP_SYS(sgetmask) }, /* obsolete syscall */
- { 0, SCMP_SYS(ssetmask) }, /* obsolete syscall */
- { 0, SCMP_SYS(stty) }, /* obsolete syscall */
- { 0, SCMP_SYS(swapoff) },
- { 0, SCMP_SYS(swapon) },
- { 0, SCMP_SYS(sysfs) }, /* obsolete syscall */
- { 0, SCMP_SYS(tuxcall) }, /* obsolete syscall */
- { 0, SCMP_SYS(ulimit) }, /* obsolete syscall */
- { 0, SCMP_SYS(uselib) }, /* obsolete syscall */
- { 0, SCMP_SYS(ustat) }, /* obsolete syscall */
- { 0, SCMP_SYS(vserver) }, /* obsolete syscall */
- { CAP_SYSLOG, SCMP_SYS(syslog) },
- { CAP_SYS_MODULE, SCMP_SYS(delete_module) },
- { CAP_SYS_MODULE, SCMP_SYS(finit_module) },
- { CAP_SYS_MODULE, SCMP_SYS(init_module) },
- { CAP_SYS_PACCT, SCMP_SYS(acct) },
- { CAP_SYS_PTRACE, SCMP_SYS(process_vm_readv) },
- { CAP_SYS_PTRACE, SCMP_SYS(process_vm_writev) },
- { CAP_SYS_PTRACE, SCMP_SYS(ptrace) },
- { CAP_SYS_RAWIO, SCMP_SYS(ioperm) },
- { CAP_SYS_RAWIO, SCMP_SYS(iopl) },
- { CAP_SYS_RAWIO, SCMP_SYS(pciconfig_iobase) },
- { CAP_SYS_RAWIO, SCMP_SYS(pciconfig_read) },
- { CAP_SYS_RAWIO, SCMP_SYS(pciconfig_write) },
-#ifdef __NR_s390_pci_mmio_read
- { CAP_SYS_RAWIO, SCMP_SYS(s390_pci_mmio_read) },
-#endif
-#ifdef __NR_s390_pci_mmio_write
- { CAP_SYS_RAWIO, SCMP_SYS(s390_pci_mmio_write) },
-#endif
- { CAP_SYS_TIME, SCMP_SYS(adjtimex) },
- { CAP_SYS_TIME, SCMP_SYS(clock_adjtime) },
- { CAP_SYS_TIME, SCMP_SYS(clock_settime) },
- { CAP_SYS_TIME, SCMP_SYS(settimeofday) },
- { CAP_SYS_TIME, SCMP_SYS(stime) },
+ const char* name;
+ } whitelist[] = {
+ /* Let's use set names where we can */
+ { 0, "@basic-io" },
+ { 0, "@credentials" },
+ { 0, "@default" },
+ { 0, "@file-system" },
+ { 0, "@io-event" },
+ { 0, "@ipc" },
+ { 0, "@mount" },
+ { 0, "@network-io" },
+ { 0, "@process" },
+ { 0, "@resources" },
+ { 0, "@setuid" },
+ { 0, "@signal" },
+ { 0, "@timer" },
+
+ /* The following four are sets we optionally enable, in case the caps have been configured for it */
+ { CAP_SYS_TIME, "@clock" },
+ { CAP_SYS_MODULE, "@module" },
+ { CAP_SYS_RAWIO, "@raw-io" },
+ { CAP_IPC_LOCK, "@memlock" },
+
+ /* Plus a good set of additional syscalls which are not part of any of the groups above */
+ { 0, "brk" },
+ { 0, "capset" },
+ { 0, "chown" },
+ { 0, "chown32" },
+ { 0, "copy_file_range" },
+ { 0, "fadvise64" },
+ { 0, "fadvise64_64" },
+ { 0, "fchown" },
+ { 0, "fchown32" },
+ { 0, "fchownat" },
+ { 0, "fdatasync" },
+ { 0, "flock" },
+ { 0, "fsync" },
+ { 0, "get_mempolicy" },
+ { 0, "getcpu" },
+ { 0, "getpriority" },
+ { 0, "getrandom" },
+ { 0, "io_cancel" },
+ { 0, "io_destroy" },
+ { 0, "io_getevents" },
+ { 0, "io_setup" },
+ { 0, "io_submit" },
+ { 0, "ioctl" },
+ { 0, "ioprio_get" },
+ { 0, "kcmp" },
+ { 0, "lchown" },
+ { 0, "lchown32" },
+ { 0, "madvise" },
+ { 0, "mincore" },
+ { 0, "mprotect" },
+ { 0, "mremap" },
+ { 0, "msync" },
+ { 0, "name_to_handle_at" },
+ { 0, "oldolduname" },
+ { 0, "olduname" },
+ { 0, "personality" },
+ { 0, "preadv2" },
+ { 0, "pwritev2" },
+ { 0, "readahead" },
+ { 0, "readdir" },
+ { 0, "remap_file_pages" },
+ { 0, "sched_get_priority_max" },
+ { 0, "sched_get_priority_min" },
+ { 0, "sched_getaffinity" },
+ { 0, "sched_getattr" },
+ { 0, "sched_getparam" },
+ { 0, "sched_getscheduler" },
+ { 0, "sched_rr_get_interval" },
+ { 0, "sched_yield" },
+ { 0, "seccomp" },
+ { 0, "sendfile" },
+ { 0, "sendfile64" },
+ { 0, "setdomainname" },
+ { 0, "setfsgid" },
+ { 0, "setfsgid32" },
+ { 0, "setfsuid" },
+ { 0, "setfsuid32" },
+ { 0, "sethostname" },
+ { 0, "setpgid" },
+ { 0, "setsid" },
+ { 0, "splice" },
+ { 0, "sync" },
+ { 0, "sync_file_range" },
+ { 0, "syncfs" },
+ { 0, "sysinfo" },
+ { 0, "tee" },
+ { 0, "ugetrlimit" },
+ { 0, "umask" },
+ { 0, "uname" },
+ { 0, "userfaultfd" },
+ { 0, "vmsplice" },
+
+ /* The following individual syscalls are added depending on specified caps */
+ { CAP_SYS_PACCT, "acct" },
+ { CAP_SYS_PTRACE, "process_vm_readv" },
+ { CAP_SYS_PTRACE, "process_vm_writev" },
+ { CAP_SYS_PTRACE, "ptrace" },
+ { CAP_SYS_BOOT, "reboot" },
+ { CAP_SYSLOG, "syslog" },
+ { CAP_SYS_TTY_CONFIG, "vhangup" },
+
+ /*
+ * The following syscalls and groups are knowingly excluded:
+ *
+ * @cpu-emulation
+ * @keyring (NB: keyring is not namespaced!)
+ * @obsolete
+ * @swap
+ *
+ * bpf (NB: bpffs is not namespaced!)
+ * fanotify_init
+ * fanotify_mark
+ * kexec_file_load
+ * kexec_load
+ * lookup_dcookie
+ * nfsservctl
+ * open_by_handle_at
+ * perf_event_open
+ * pkey_alloc
+ * pkey_free
+ * pkey_mprotect
+ * quotactl
+ */
};
- unsigned i;
+
int r, c = 0;
+ size_t i;
+ char **p;
- for (i = 0; i < ELEMENTSOF(blacklist); i++) {
- if (blacklist[i].capability != 0 && (cap_list_retain & (1ULL << blacklist[i].capability)))
+ for (i = 0; i < ELEMENTSOF(whitelist); i++) {
+ if (whitelist[i].capability != 0 && (cap_list_retain & (1ULL << whitelist[i].capability)) == 0)
continue;
- r = seccomp_rule_add_exact(ctx, SCMP_ACT_ERRNO(EPERM), blacklist[i].syscall_num, 0);
- if (r < 0) {
+ r = seccomp_add_syscall_filter_item(ctx, whitelist[i].name, SCMP_ACT_ALLOW, syscall_blacklist);
+ if (r < 0)
/* If the system call is not known on this architecture, then that's fine, let's ignore it */
- _cleanup_free_ char *n = NULL;
+ log_debug_errno(r, "Failed to add rule for system call %s on %s, ignoring: %m", whitelist[i].name, seccomp_arch_to_string(arch));
+ else
+ c++;
+ }
- n = seccomp_syscall_resolve_num_arch(arch, blacklist[i].syscall_num);
- log_debug_errno(r, "Failed to add rule for system call %s, ignoring: %m", strna(n));
- } else
+ STRV_FOREACH(p, syscall_whitelist) {
+ r = seccomp_add_syscall_filter_item(ctx, *p, SCMP_ACT_ALLOW, syscall_blacklist);
+ if (r < 0)
+ log_debug_errno(r, "Failed to add rule for system call %s on %s, ignoring: %m", *p, seccomp_arch_to_string(arch));
+ else
c++;
}
return c;
}
-int setup_seccomp(uint64_t cap_list_retain) {
+int setup_seccomp(uint64_t cap_list_retain, char **syscall_whitelist, char **syscall_blacklist) {
uint32_t arch;
int r;
if (!is_seccomp_available()) {
- log_debug("SECCOMP features not detected in the kernel, disabling SECCOMP audit filter");
+ log_debug("SECCOMP features not detected in the kernel, disabling SECCOMP filterering");
return 0;
}
SECCOMP_FOREACH_LOCAL_ARCH(arch) {
_cleanup_(seccomp_releasep) scmp_filter_ctx seccomp = NULL;
- int n;
- log_debug("Operating on architecture: %s", seccomp_arch_to_string(arch));
+ log_debug("Applying whitelist on architecture: %s", seccomp_arch_to_string(arch));
- r = seccomp_init_for_arch(&seccomp, arch, SCMP_ACT_ALLOW);
+ r = seccomp_init_for_arch(&seccomp, arch, SCMP_ACT_ERRNO(EPERM));
if (r < 0)
return log_error_errno(r, "Failed to allocate seccomp object: %m");
- n = seccomp_add_default_syscall_filter(seccomp, arch, cap_list_retain);
- if (n < 0)
- return n;
+ r = seccomp_add_default_syscall_filter(seccomp, arch, cap_list_retain, syscall_whitelist, syscall_blacklist);
+ if (r < 0)
+ return r;
+
+ r = seccomp_load(seccomp);
+ if (IN_SET(r, -EPERM, -EACCES))
+ return log_error_errno(r, "Failed to install seccomp filter: %m");
+ if (r < 0)
+ log_debug_errno(r, "Failed to install filter set for architecture %s, skipping: %m", seccomp_arch_to_string(arch));
+ }
+
+ SECCOMP_FOREACH_LOCAL_ARCH(arch) {
+ _cleanup_(seccomp_releasep) scmp_filter_ctx seccomp = NULL;
+
+ log_debug("Applying NETLINK_AUDIT mask on architecture: %s", seccomp_arch_to_string(arch));
+
+ r = seccomp_init_for_arch(&seccomp, arch, SCMP_ACT_ALLOW);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate seccomp object: %m");
/*
Audit is broken in containers, much of the userspace audit hookup will fail if running inside a
2,
SCMP_A0(SCMP_CMP_EQ, AF_NETLINK),
SCMP_A2(SCMP_CMP_EQ, NETLINK_AUDIT));
- if (r < 0)
+ if (r < 0) {
log_debug_errno(r, "Failed to add audit seccomp rule, ignoring: %m");
- else
- n++;
-
- if (n <= 0) /* no rule added? then skip this architecture */
continue;
+ }
r = seccomp_load(seccomp);
if (IN_SET(r, -EPERM, -EACCES))
#else
-int setup_seccomp(uint64_t cap_list_retain) {
+int setup_seccomp(uint64_t cap_list_retain, char **syscall_whitelist, char **syscall_blacklist) {
return 0;
}
#include <sys/types.h>
-int setup_seccomp(uint64_t cap_list_retain);
+int setup_seccomp(uint64_t cap_list_retain, char **syscall_whitelist, char **syscall_blacklist);
free(s->pivot_root_new);
free(s->pivot_root_old);
free(s->working_directory);
+ strv_free(s->syscall_whitelist);
+ strv_free(s->syscall_blacklist);
strv_free(s->network_interfaces);
strv_free(s->network_macvlan);
return 0;
}
+
+int config_parse_syscall_filter(
+ 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) {
+
+ Settings *settings = data;
+ bool negative;
+ const char *items;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+
+ negative = rvalue[0] == '~';
+ items = negative ? rvalue + 1 : rvalue;
+
+ for (;;) {
+ _cleanup_free_ char *word = NULL;
+
+ r = extract_first_word(&items, &word, NULL, 0);
+ if (r == 0)
+ break;
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse SystemCallFilter= parameter %s, ignoring: %m", rvalue);
+ return 0;
+ }
+
+ if (negative)
+ r = strv_extend(&settings->syscall_blacklist, word);
+ else
+ r = strv_extend(&settings->syscall_whitelist, word);
+ if (r < 0)
+ return log_oom();
+ }
+
+ return 0;
+}
SETTING_USERNS = 1 << 13,
SETTING_NOTIFY_READY = 1 << 14,
SETTING_PIVOT_ROOT = 1 << 15,
- _SETTINGS_MASK_ALL = (1 << 16) -1
+ SETTING_SYSCALL_FILTER = 1 << 16,
+ _SETTINGS_MASK_ALL = (1 << 17) -1
} SettingsMask;
typedef struct Settings {
UserNamespaceMode userns_mode;
uid_t uid_shift, uid_range;
bool notify_ready;
+ char **syscall_whitelist;
+ char **syscall_blacklist;
/* [Image] */
int read_only;
int config_parse_boot(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);
int config_parse_pid2(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);
int config_parse_private_users(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);
+int config_parse_syscall_filter(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);
static MountSettingsMask arg_mount_settings = MOUNT_APPLY_APIVFS_RO;
static void *arg_root_hash = NULL;
static size_t arg_root_hash_size = 0;
+static char **arg_syscall_whitelist = NULL;
+static char **arg_syscall_blacklist = NULL;
static void help(void) {
printf("%s [OPTIONS...] [PATH] [ARGUMENTS...]\n\n"
" --capability=CAP In addition to the default, retain specified\n"
" capability\n"
" --drop-capability=CAP Drop the specified capability from the default set\n"
+ " --system-call-filter=LIST|~LIST\n"
+ " Permit/prohibit specific system calls\n"
" --kill-signal=SIGNAL Select signal to use for shutting down PID 1\n"
" --link-journal=MODE Link up guest journal, one of no, auto, guest, \n"
" host, try-guest, try-host\n"
ARG_PRIVATE_USERS_CHOWN,
ARG_NOTIFY_READY,
ARG_ROOT_HASH,
+ ARG_SYSTEM_CALL_FILTER,
};
static const struct option options[] = {
{ "pivot-root", required_argument, NULL, ARG_PIVOT_ROOT },
{ "notify-ready", required_argument, NULL, ARG_NOTIFY_READY },
{ "root-hash", required_argument, NULL, ARG_ROOT_HASH },
+ { "system-call-filter", required_argument, NULL, ARG_SYSTEM_CALL_FILTER },
{}
};
break;
}
+ case ARG_SYSTEM_CALL_FILTER: {
+ bool negative;
+ const char *items;
+
+ negative = optarg[0] == '~';
+ items = negative ? optarg + 1 : optarg;
+
+ for (;;) {
+ _cleanup_free_ char *word = NULL;
+
+ r = extract_first_word(&items, &word, NULL, 0);
+ if (r == 0)
+ break;
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse system call filter: %m");
+
+ if (negative)
+ r = strv_extend(&arg_syscall_blacklist, word);
+ else
+ r = strv_extend(&arg_syscall_whitelist, word);
+ if (r < 0)
+ return log_oom();
+ }
+
+ arg_settings_mask |= SETTING_SYSCALL_FILTER;
+ break;
+ }
+
case '?':
return -EINVAL;
if (r < 0)
return r;
- r = setup_seccomp(arg_caps_retain);
+ r = setup_seccomp(arg_caps_retain, arg_syscall_whitelist, arg_syscall_blacklist);
if (r < 0)
return r;
if ((arg_settings_mask & SETTING_NOTIFY_READY) == 0)
arg_notify_ready = settings->notify_ready;
+ if ((arg_settings_mask & SETTING_SYSCALL_FILTER) == 0) {
+
+ if (!arg_settings_trusted && !strv_isempty(arg_syscall_whitelist))
+ log_warning("Ignoring SystemCallFilter= settings, file %s is not trusted.", p);
+ else {
+ strv_free(arg_syscall_whitelist);
+ strv_free(arg_syscall_blacklist);
+
+ arg_syscall_whitelist = settings->syscall_whitelist;
+ arg_syscall_blacklist = settings->syscall_blacklist;
+
+ settings->syscall_whitelist = settings->syscall_blacklist = NULL;
+ }
+ }
+
return 0;
}
if (!machine_name_is_valid(machine))
goto not_found;
- if (getenv_bool("SYSTEMD_NSS_BYPASS_BUS") > 0)
+ if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS") > 0)
/* Make sure we can't deadlock if we are invoked by dbus-daemon. This way, it won't be able to resolve
* these UIDs, but that should be unproblematic as containers should never be able to connect to a bus
* running on the host. */
if (uid < HOST_UID_LIMIT)
goto not_found;
- if (getenv_bool("SYSTEMD_NSS_BYPASS_BUS") > 0)
+ if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS") > 0)
goto not_found;
r = sd_bus_open_system(&bus);
if (!machine_name_is_valid(machine))
goto not_found;
- if (getenv_bool("SYSTEMD_NSS_BYPASS_BUS") > 0)
+ if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS") > 0)
goto not_found;
r = sd_bus_open_system(&bus);
if (gid < HOST_GID_LIMIT)
goto not_found;
- if (getenv_bool("SYSTEMD_NSS_BYPASS_BUS") > 0)
+ if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS") > 0)
goto not_found;
r = sd_bus_open_system(&bus);
goto not_found;
/* Synthesize entries for the root and nobody users, in case they are missing in /etc/passwd */
- if (streq(name, root_passwd.pw_name)) {
- *pwd = root_passwd;
- *errnop = 0;
- return NSS_STATUS_SUCCESS;
- }
- if (streq(name, nobody_passwd.pw_name)) {
- *pwd = nobody_passwd;
- *errnop = 0;
- return NSS_STATUS_SUCCESS;
+ if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
+ if (streq(name, root_passwd.pw_name)) {
+ *pwd = root_passwd;
+ *errnop = 0;
+ return NSS_STATUS_SUCCESS;
+ }
+ if (streq(name, nobody_passwd.pw_name)) {
+ *pwd = nobody_passwd;
+ *errnop = 0;
+ return NSS_STATUS_SUCCESS;
+ }
}
/* Make sure that we don't go in circles when allocating a dynamic UID by checking our own database */
- if (getenv_bool("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
+ if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
goto not_found;
- if (getenv_bool("SYSTEMD_NSS_BYPASS_BUS") > 0) {
+ if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS") > 0) {
/* Access the dynamic UID allocation directly if we are called from dbus-daemon, see above. */
r = direct_lookup_name(name, (uid_t*) &translated);
goto not_found;
/* Synthesize data for the root user and for nobody in case they are missing from /etc/passwd */
- if (uid == root_passwd.pw_uid) {
- *pwd = root_passwd;
- *errnop = 0;
- return NSS_STATUS_SUCCESS;
- }
- if (uid == nobody_passwd.pw_uid) {
- *pwd = nobody_passwd;
- *errnop = 0;
- return NSS_STATUS_SUCCESS;
+ if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
+ if (uid == root_passwd.pw_uid) {
+ *pwd = root_passwd;
+ *errnop = 0;
+ return NSS_STATUS_SUCCESS;
+ }
+ if (uid == nobody_passwd.pw_uid) {
+ *pwd = nobody_passwd;
+ *errnop = 0;
+ return NSS_STATUS_SUCCESS;
+ }
}
if (uid <= SYSTEM_UID_MAX)
goto not_found;
- if (getenv_bool("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
+ if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
goto not_found;
- if (getenv_bool("SYSTEMD_NSS_BYPASS_BUS") > 0) {
+ if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS") > 0) {
r = direct_lookup_uid(uid, &direct);
if (r == -ENOENT)
goto not_found;
/* Synthesize records for root and nobody, in case they are missing form /etc/group */
- if (streq(name, root_group.gr_name)) {
- *gr = root_group;
- *errnop = 0;
- return NSS_STATUS_SUCCESS;
- }
- if (streq(name, nobody_group.gr_name)) {
- *gr = nobody_group;
- *errnop = 0;
- return NSS_STATUS_SUCCESS;
+ if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
+ if (streq(name, root_group.gr_name)) {
+ *gr = root_group;
+ *errnop = 0;
+ return NSS_STATUS_SUCCESS;
+ }
+ if (streq(name, nobody_group.gr_name)) {
+ *gr = nobody_group;
+ *errnop = 0;
+ return NSS_STATUS_SUCCESS;
+ }
}
- if (getenv_bool("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
+ if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
goto not_found;
- if (getenv_bool("SYSTEMD_NSS_BYPASS_BUS") > 0) {
+ if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS") > 0) {
/* Access the dynamic GID allocation directly if we are called from dbus-daemon, see above. */
r = direct_lookup_name(name, (uid_t*) &translated);
goto not_found;
/* Synthesize records for root and nobody, in case they are missing from /etc/group */
- if (gid == root_group.gr_gid) {
- *gr = root_group;
- *errnop = 0;
- return NSS_STATUS_SUCCESS;
- }
- if (gid == nobody_group.gr_gid) {
- *gr = nobody_group;
- *errnop = 0;
- return NSS_STATUS_SUCCESS;
+ if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
+ if (gid == root_group.gr_gid) {
+ *gr = root_group;
+ *errnop = 0;
+ return NSS_STATUS_SUCCESS;
+ }
+ if (gid == nobody_group.gr_gid) {
+ *gr = nobody_group;
+ *errnop = 0;
+ return NSS_STATUS_SUCCESS;
+ }
}
if (gid <= SYSTEM_GID_MAX)
goto not_found;
- if (getenv_bool("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
+ if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
goto not_found;
- if (getenv_bool("SYSTEMD_NSS_BYPASS_BUS") > 0) {
+ if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS") > 0) {
r = direct_lookup_uid(gid, &direct);
if (r == -ENOENT)
assert(suffix);
assert(loader);
- r = conf_files_list_nulstr(&files, suffix, NULL, trust_anchor_dirs);
+ r = conf_files_list_nulstr(&files, suffix, NULL, 0, trust_anchor_dirs);
if (r < 0)
return log_error_errno(r, "Failed to enumerate %s trust anchor files: %m", suffix);
static bool arg_nice_set = false;
static char **arg_environment = NULL;
static char **arg_property = NULL;
-static bool arg_pty = false;
+static enum {
+ ARG_STDIO_NONE, /* The default, as it is for normal services, stdin connected to /dev/null, and stdout+stderr to the journal */
+ ARG_STDIO_PTY, /* Interactive behaviour, requested by --pty: we allocate a pty and connect it to the TTY we are invoked from */
+ ARG_STDIO_DIRECT, /* Directly pass our stdin/stdout/stderr to the activated service, useful for usage in shell pipelines, requested by --pipe */
+ ARG_STDIO_AUTO, /* If --pipe and --pty are used together we use --pty when invoked on a TTY, and --pipe otherwise */
+} arg_stdio = ARG_STDIO_NONE;
static usec_t arg_on_active = 0;
static usec_t arg_on_boot = 0;
static usec_t arg_on_startup = 0;
" --gid=GROUP Run as system group\n"
" --nice=NICE Nice level\n"
" -E --setenv=NAME=VALUE Set environment\n"
- " -t --pty Run service on pseudo tty\n"
+ " -t --pty Run service on pseudo TTY as STDIN/STDOUT/\n"
+ " STDERR\n"
+ " -P --pipe Pass STDIN/STDOUT/STDERR directly to service\n"
" -q --quiet Suppress information messages during runtime\n\n"
"Timer options:\n"
" --on-active=SECONDS Run after SECONDS delay\n"
{ "nice", required_argument, NULL, ARG_NICE },
{ "setenv", required_argument, NULL, 'E' },
{ "property", required_argument, NULL, 'p' },
- { "tty", no_argument, NULL, 't' }, /* deprecated */
+ { "tty", no_argument, NULL, 't' }, /* deprecated alias */
{ "pty", no_argument, NULL, 't' },
+ { "pipe", no_argument, NULL, 'P' },
{ "quiet", no_argument, NULL, 'q' },
{ "on-active", required_argument, NULL, ARG_ON_ACTIVE },
{ "on-boot", required_argument, NULL, ARG_ON_BOOT },
assert(argc >= 0);
assert(argv);
- while ((c = getopt_long(argc, argv, "+hrH:M:E:p:tq", options, NULL)) >= 0)
+ while ((c = getopt_long(argc, argv, "+hrH:M:E:p:tPq", options, NULL)) >= 0)
switch (c) {
break;
- case 't':
- arg_pty = true;
+ case 't': /* --pty */
+ if (IN_SET(arg_stdio, ARG_STDIO_DIRECT, ARG_STDIO_AUTO)) /* if --pipe is already used, upgrade to auto mode */
+ arg_stdio = ARG_STDIO_AUTO;
+ else
+ arg_stdio = ARG_STDIO_PTY;
+ break;
+
+ case 'P': /* --pipe */
+ if (IN_SET(arg_stdio, ARG_STDIO_PTY, ARG_STDIO_AUTO)) /* If --pty is already used, upgrade to auto mode */
+ arg_stdio = ARG_STDIO_AUTO;
+ else
+ arg_stdio = ARG_STDIO_DIRECT;
break;
case 'q':
assert_not_reached("Unhandled option");
}
+
+ if (arg_stdio == ARG_STDIO_AUTO) {
+ /* If we both --pty and --pipe are specified we'll automatically pick --pty if we are connected fully
+ * to a TTY and pick direct fd passing otherwise. This way, we automatically adapt to usage in a shell
+ * pipeline, but we are neatly interactive with tty-level isolation otherwise. */
+ arg_stdio = isatty(STDIN_FILENO) && isatty(STDOUT_FILENO) && isatty(STDERR_FILENO) ?
+ ARG_STDIO_PTY :
+ ARG_STDIO_DIRECT;
+ }
+
if ((optind >= argc) && (!arg_unit || !with_timer())) {
log_error("Command line to execute required.");
return -EINVAL;
return -EINVAL;
}
- if (arg_pty && (with_timer() || arg_scope)) {
- log_error("--pty is not compatible in timer or --scope mode.");
+ if (arg_stdio != ARG_STDIO_NONE && (with_timer() || arg_scope)) {
+ log_error("--pty/--pipe is not compatible in timer or --scope mode.");
return -EINVAL;
}
- if (arg_pty && arg_transport == BUS_TRANSPORT_REMOTE) {
- log_error("--pty is only supported when connecting to the local system or containers.");
+ if (arg_stdio != ARG_STDIO_NONE && arg_transport == BUS_TRANSPORT_REMOTE) {
+ log_error("--pty/--pipe is only supported when connecting to the local system or containers.");
return -EINVAL;
}
- if (arg_pty && arg_no_block) {
- log_error("--pty is not compatible with --no-block.");
+ if (arg_stdio != ARG_STDIO_NONE && arg_no_block) {
+ log_error("--pty/--pipe is not compatible with --no-block.");
return -EINVAL;
}
}
static int transient_service_set_properties(sd_bus_message *m, char **argv, const char *pty_path) {
+ bool send_term = false;
int r;
assert(m);
if (r < 0)
return r;
- if (arg_wait || arg_pty) {
+ if (arg_wait || arg_stdio != ARG_STDIO_NONE) {
r = sd_bus_message_append(m, "(sv)", "AddRef", "b", 1);
if (r < 0)
return r;
}
if (pty_path) {
- const char *e;
-
r = sd_bus_message_append(m,
"(sv)(sv)(sv)(sv)",
"StandardInput", "s", "tty",
if (r < 0)
return r;
+ send_term = true;
+
+ } else if (arg_stdio == ARG_STDIO_DIRECT) {
+ r = sd_bus_message_append(m,
+ "(sv)(sv)(sv)",
+ "StandardInputFileDescriptor", "h", STDIN_FILENO,
+ "StandardOutputFileDescriptor", "h", STDOUT_FILENO,
+ "StandardErrorFileDescriptor", "h", STDERR_FILENO);
+ if (r < 0)
+ return r;
+
+ send_term = isatty(STDIN_FILENO) || isatty(STDOUT_FILENO) || isatty(STDERR_FILENO);
+ }
+
+ if (send_term) {
+ const char *e;
+
e = getenv("TERM");
if (e) {
char *n;
assert(argv);
assert(retval);
- if (arg_pty) {
+ if (arg_stdio == ARG_STDIO_PTY) {
if (arg_transport == BUS_TRANSPORT_LOCAL) {
master = posix_openpt(O_RDWR|O_NOCTTY|O_CLOEXEC|O_NDELAY);
if (!arg_quiet)
log_info("Running as unit: %s", service);
- if (arg_wait || master >= 0) {
+ if (arg_wait || arg_stdio != ARG_STDIO_NONE) {
_cleanup_(run_context_free) RunContext c = {};
_cleanup_free_ char *path = NULL;
const char *mt;
/* If --wait is used connect via the bus, unconditionally, as ref/unref is not supported via the limited direct
* connection */
- if (arg_wait || arg_pty)
+ if (arg_wait || arg_stdio != ARG_STDIO_NONE)
r = bus_connect_transport(arg_transport, arg_host, arg_user, &bus);
else
r = bus_connect_transport_systemd(arg_transport, arg_host, arg_user, &bus);
r = sd_bus_message_append(m, "v", "i", (int32_t) q);
- } else if (STR_IN_SET(field, "Environment", "PassEnvironment")) {
+ } else if (STR_IN_SET(field, "Environment", "UnsetEnvironment", "PassEnvironment")) {
const char *p;
r = sd_bus_message_open_container(m, 'v', "as");
log_error("Invalid environment assignment: %s", word);
return -EINVAL;
}
+ } else if (streq(field, "UnsetEnvironment")) {
+ if (!env_assignment_is_valid(word) && !env_name_is_valid(word)) {
+ log_error("Invalid environment name or assignment: %s", word);
+ return -EINVAL;
+ }
} else { /* PassEnvironment */
if (!env_name_is_valid(word)) {
log_error("Invalid environment variable name: %s", word);
_cleanup_strv_free_ char **files = NULL;
int r;
- r = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs);
+ r = conf_files_list_nulstr(&files, ".conf", NULL, 0, conf_file_dirs);
if (r < 0)
return r;
if (r < 0)
return r;
- r = conf_files_list_strv(&files, ".conf", NULL, (const char* const*) dropin_dirs);
+ r = conf_files_list_strv(&files, ".conf", NULL, 0, (const char* const*) dropin_dirs);
if (r < 0)
return r;
assert(rvalue);
assert(data);
+ if (isempty(rvalue)) {
+ n = NULL;
+ goto finalize;
+ }
+
if (!utf8_is_valid(rvalue)) {
log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue);
return fatal ? -ENOEXEC : 0;
path_kill_slashes(n);
+finalize:
free(*s);
*s = n;
return 0;
}
-int config_parse_strv(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) {
+int config_parse_strv(
+ 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) {
char ***sv = data;
int r;
assert(data);
if (isempty(rvalue)) {
- char **empty;
-
- /* Empty assignment resets the list. As a special rule
- * we actually fill in a real empty array here rather
- * than NULL, since some code wants to know if
- * something was set at all... */
- empty = new0(char*, 1);
- if (!empty)
- return log_oom();
-
- strv_free(*sv);
- *sv = empty;
-
+ *sv = strv_free(*sv);
return 0;
}
free(word);
continue;
}
+
r = strv_consume(sv, word);
if (r < 0)
return log_oom();
assert(rvalue);
assert(personality);
- p = personality_from_string(rvalue);
- if (p == PERSONALITY_INVALID) {
- log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse personality, ignoring: %s", rvalue);
- return 0;
+ if (isempty(rvalue))
+ p = PERSONALITY_INVALID;
+ else {
+ p = personality_from_string(rvalue);
+ if (p == PERSONALITY_INVALID) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse personality, ignoring: %s", rvalue);
+ return 0;
+ }
}
*personality = p;
return 0;
}
- r = conf_files_list_strv(ret, file_suffix, NULL, (const char**) dirs);
+ r = conf_files_list_strv(ret, file_suffix, NULL, 0, (const char**) dirs);
if (r < 0)
return log_warning_errno(r, "Failed to create the list of configuration files: %m");
assert(presets);
if (scope == UNIT_FILE_SYSTEM)
- r = conf_files_list(&files, ".preset", root_dir,
+ r = conf_files_list(&files, ".preset", root_dir, 0,
"/etc/systemd/system-preset",
"/usr/local/lib/systemd/system-preset",
"/usr/lib/systemd/system-preset",
#endif
NULL);
else if (scope == UNIT_FILE_GLOBAL)
- r = conf_files_list(&files, ".preset", root_dir,
+ r = conf_files_list(&files, ".preset", root_dir, 0,
"/etc/systemd/user-preset",
"/usr/local/lib/systemd/user-preset",
"/usr/lib/systemd/user-preset",
* pager so that we get the value from the actual tty */
(void) columns();
- if (pipe(fd) < 0)
+ if (pipe2(fd, O_CLOEXEC) < 0)
return log_error_errno(errno, "Failed to create pager pipe: %m");
parent_pid = getpid_cached();
"execve\0"
"exit\0"
"exit_group\0"
+ "futex\0"
+ "get_robust_list\0"
+ "get_thread_area\0"
"getrlimit\0" /* make sure processes can query stack size and such */
"gettimeofday\0"
+ "membarrier\0"
"nanosleep\0"
"pause\0"
+ "restart_syscall\0"
"rt_sigreturn\0"
+ "set_robust_list\0"
+ "set_thread_area\0"
+ "set_tid_address\0"
"sigreturn\0"
"time\0"
},
.name = "@basic-io",
.help = "Basic IO",
.value =
+ "_llseek\0"
"close\0"
+ "dup\0"
"dup2\0"
"dup3\0"
- "dup\0"
"lseek\0"
"pread64\0"
"preadv\0"
"vm86\0"
"vm86old\0"
},
+ [SYSCALL_FILTER_SET_CREDENTIALS] = {
+ .name = "@credentials",
+ .help = "Query own process credentials",
+ .value =
+ "capget\0"
+ "getegid\0"
+ "getegid32\0"
+ "geteuid\0"
+ "geteuid32\0"
+ "getgid\0"
+ "getgid32\0"
+ "getgroups\0"
+ "getgroups32\0"
+ "getpgid\0"
+ "getpgrp\0"
+ "getpid\0"
+ "getppid\0"
+ "getresgid\0"
+ "getresgid32\0"
+ "getresuid\0"
+ "getresuid32\0"
+ "getsid\0"
+ "gettid\0"
+ "getuid\0"
+ "getuid32\0"
+ },
[SYSCALL_FILTER_SET_DEBUG] = {
.name = "@debug",
.help = "Debugging, performance monitoring and tracing functionality",
"fchdir\0"
"fchmod\0"
"fchmodat\0"
- "fcntl64\0"
"fcntl\0"
+ "fcntl64\0"
"fgetxattr\0"
"flistxattr\0"
+ "fremovexattr\0"
"fsetxattr\0"
- "fstat64\0"
"fstat\0"
+ "fstat64\0"
"fstatat64\0"
- "fstatfs64\0"
"fstatfs\0"
- "ftruncate64\0"
+ "fstatfs64\0"
"ftruncate\0"
+ "ftruncate64\0"
"futimesat\0"
"getcwd\0"
- "getdents64\0"
"getdents\0"
+ "getdents64\0"
"getxattr\0"
"inotify_add_watch\0"
+ "inotify_init\0"
"inotify_init1\0"
"inotify_rm_watch\0"
"lgetxattr\0"
"llistxattr\0"
"lremovexattr\0"
"lsetxattr\0"
- "lstat64\0"
"lstat\0"
+ "lstat64\0"
"mkdir\0"
"mkdirat\0"
"mknod\0"
"mknodat\0"
- "mmap2\0"
"mmap\0"
+ "mmap2\0"
"munmap\0"
"newfstatat\0"
+ "oldfstat\0"
+ "oldlstat\0"
+ "oldstat\0"
"open\0"
"openat\0"
"readlink\0"
"readlinkat\0"
"removexattr\0"
"rename\0"
- "renameat2\0"
"renameat\0"
+ "renameat2\0"
"rmdir\0"
"setxattr\0"
- "stat64\0"
"stat\0"
+ "stat64\0"
"statfs\0"
+ "statfs64\0"
+#ifdef __PNR_statx
"statx\0"
+#endif
"symlink\0"
"symlinkat\0"
- "truncate64\0"
"truncate\0"
+ "truncate64\0"
"unlink\0"
"unlinkat\0"
+ "utime\0"
"utimensat\0"
"utimes\0"
},
.help = "Event loop system calls",
.value =
"_newselect\0"
- "epoll_create1\0"
"epoll_create\0"
+ "epoll_create1\0"
"epoll_ctl\0"
"epoll_ctl_old\0"
"epoll_pwait\0"
"epoll_wait\0"
"epoll_wait_old\0"
- "eventfd2\0"
"eventfd\0"
+ "eventfd2\0"
"poll\0"
"ppoll\0"
"pselect6\0"
"msgget\0"
"msgrcv\0"
"msgsnd\0"
- "pipe2\0"
"pipe\0"
+ "pipe2\0"
"process_vm_readv\0"
"process_vm_writev\0"
"semctl\0"
"keyctl\0"
"request_key\0"
},
+ [SYSCALL_FILTER_SET_MEMLOCK] = {
+ .name = "@memlock",
+ .help = "Memory locking control",
+ .value =
+ "mlock\0"
+ "mlock2\0"
+ "mlockall\0"
+ "munlock\0"
+ "munlockall\0"
+ },
[SYSCALL_FILTER_SET_MODULE] = {
.name = "@module",
.help = "Loading and unloading of kernel modules",
"chroot\0"
"mount\0"
"pivot_root\0"
- "umount2\0"
"umount\0"
+ "umount2\0"
},
[SYSCALL_FILTER_SET_NETWORK_IO] = {
.name = "@network-io",
.help = "Network or Unix socket IO, should not be needed if not network facing",
.value =
- "accept4\0"
"accept\0"
+ "accept4\0"
"bind\0"
"connect\0"
"getpeername\0"
"get_kernel_syms\0"
"getpmsg\0"
"gtty\0"
+ "idle\0"
"lock\0"
"mpx\0"
"prof\0"
"@clock\0"
"@module\0"
"@raw-io\0"
+ "_sysctl\0"
"acct\0"
"bpf\0"
"capset\0"
- "chown32\0"
"chown\0"
+ "chown32\0"
"chroot\0"
- "fchown32\0"
"fchown\0"
+ "fchown32\0"
"fchownat\0"
"kexec_file_load\0"
"kexec_load\0"
- "lchown32\0"
"lchown\0"
+ "lchown32\0"
"nfsservctl\0"
"pivot_root\0"
"quotactl\0"
"reboot\0"
"setdomainname\0"
- "setfsuid32\0"
"setfsuid\0"
- "setgroups32\0"
+ "setfsuid32\0"
"setgroups\0"
+ "setgroups32\0"
"sethostname\0"
- "setresuid32\0"
"setresuid\0"
- "setreuid32\0"
+ "setresuid32\0"
"setreuid\0"
- "setuid32\0"
+ "setreuid32\0"
"setuid\0"
+ "setuid32\0"
"swapoff\0"
"swapon\0"
- "_sysctl\0"
"vhangup\0"
},
[SYSCALL_FILTER_SET_PROCESS] = {
"clone\0"
"execveat\0"
"fork\0"
+ "getpid\0"
+ "getppid\0"
+ "getrusage\0"
+ "gettid\0"
"kill\0"
"prctl\0"
+ "rt_sigqueueinfo\0"
+ "rt_tgsigqueueinfo\0"
"setns\0"
"tgkill\0"
+ "times\0"
"tkill\0"
"unshare\0"
"vfork\0"
+ "wait4\0"
+ "waitid\0"
+ "waitpid\0"
},
[SYSCALL_FILTER_SET_RAW_IO] = {
.name = "@raw-io",
.name = "@resources",
.help = "Alter resource settings",
.value =
+ "ioprio_set\0"
+ "mbind\0"
+ "migrate_pages\0"
+ "move_pages\0"
+ "nice\0"
+ "prlimit64\0"
+ "sched_setaffinity\0"
+ "sched_setattr\0"
"sched_setparam\0"
"sched_setscheduler\0"
- "sched_setaffinity\0"
+ "set_mempolicy\0"
"setpriority\0"
"setrlimit\0"
- "set_mempolicy\0"
- "migrate_pages\0"
- "move_pages\0"
- "mbind\0"
- "sched_setattr\0"
- "prlimit64\0"
},
[SYSCALL_FILTER_SET_SETUID] = {
.name = "@setuid",
.help = "Operations for changing user/group credentials",
.value =
- "setgid32\0"
"setgid\0"
- "setgroups32\0"
+ "setgid32\0"
"setgroups\0"
- "setregid32\0"
+ "setgroups32\0"
"setregid\0"
- "setresgid32\0"
+ "setregid32\0"
"setresgid\0"
- "setresuid32\0"
+ "setresgid32\0"
"setresuid\0"
- "setreuid32\0"
+ "setresuid32\0"
"setreuid\0"
- "setuid32\0"
+ "setreuid32\0"
"setuid\0"
+ "setuid32\0"
+ },
+ [SYSCALL_FILTER_SET_SIGNAL] = {
+ .name = "@signal",
+ .help = "Process signal handling",
+ .value =
+ "rt_sigaction\0"
+ "rt_sigpending\0"
+ "rt_sigprocmask\0"
+ "rt_sigsuspend\0"
+ "rt_sigtimedwait\0"
+ "sigaction\0"
+ "sigaltstack\0"
+ "signal\0"
+ "signalfd\0"
+ "signalfd4\0"
+ "sigpending\0"
+ "sigprocmask\0"
+ "sigsuspend\0"
},
[SYSCALL_FILTER_SET_SWAP] = {
.name = "@swap",
"swapoff\0"
"swapon\0"
},
+ [SYSCALL_FILTER_SET_TIMER] = {
+ .name = "@timer",
+ .help = "Schedule operations by time",
+ .value =
+ "alarm\0"
+ "getitimer\0"
+ "setitimer\0"
+ "timer_create\0"
+ "timer_delete\0"
+ "timer_getoverrun\0"
+ "timer_gettime\0"
+ "timer_settime\0"
+ "timerfd_create\0"
+ "timerfd_gettime\0"
+ "timerfd_settime\0"
+ "times\0"
+ },
};
const SyscallFilterSet *syscall_filter_set_find(const char *name) {
return NULL;
}
+static int seccomp_add_syscall_filter_set(scmp_filter_ctx seccomp, const SyscallFilterSet *set, uint32_t action, char **exclude);
+
+int seccomp_add_syscall_filter_item(scmp_filter_ctx *seccomp, const char *name, uint32_t action, char **exclude) {
+ int r;
+
+ assert(seccomp);
+ assert(name);
+
+ if (strv_contains(exclude, name))
+ return 0;
+
+ if (name[0] == '@') {
+ const SyscallFilterSet *other;
+
+ other = syscall_filter_set_find(name);
+ if (!other) {
+ log_debug("Filter set %s is not known!", name);
+ return -EINVAL;
+ }
+
+ r = seccomp_add_syscall_filter_set(seccomp, other, action, exclude);
+ if (r < 0)
+ return r;
+ } else {
+ int id;
+
+ id = seccomp_syscall_resolve_name(name);
+ if (id == __NR_SCMP_ERROR) {
+ log_debug("System call %s is not known!", name);
+ return -EINVAL; /* Not known at all? Then that's a real error */
+ }
+
+ r = seccomp_rule_add_exact(seccomp, action, id, 0);
+ if (r < 0)
+ /* If the system call is not known on this architecture, then that's fine, let's ignore it */
+ log_debug_errno(r, "Failed to add rule for system call %s() / %d, ignoring: %m", name, id);
+ }
+
+ return 0;
+}
+
static int seccomp_add_syscall_filter_set(
scmp_filter_ctx seccomp,
- uint32_t default_action,
const SyscallFilterSet *set,
- uint32_t action) {
+ uint32_t action,
+ char **exclude) {
const char *sys;
int r;
assert(set);
NULSTR_FOREACH(sys, set->value) {
- int id;
-
- if (sys[0] == '@') {
- const SyscallFilterSet *other;
-
- other = syscall_filter_set_find(sys);
- if (!other)
- return -EINVAL;
-
- r = seccomp_add_syscall_filter_set(seccomp, default_action, other, action);
- if (r < 0)
- return r;
- } else {
- id = seccomp_syscall_resolve_name(sys);
- if (id == __NR_SCMP_ERROR)
- return -EINVAL; /* Not known at all? Then that's a real error */
-
- r = seccomp_rule_add_exact(seccomp, action, id, 0);
- if (r < 0)
- /* If the system call is not known on this architecture, then that's fine, let's ignore it */
- log_debug_errno(r, "Failed to add rule for system call %s() / %d, ignoring: %m", sys, id);
- }
+ r = seccomp_add_syscall_filter_item(seccomp, sys, action, exclude);
+ if (r < 0)
+ return r;
}
return 0;
if (r < 0)
return r;
- r = seccomp_add_syscall_filter_set(seccomp, default_action, set, action);
+ r = seccomp_add_syscall_filter_set(seccomp, set, action, NULL);
if (r < 0) {
log_debug_errno(r, "Failed to add filter set, ignoring: %m");
continue;
SYSCALL_FILTER_SET_BASIC_IO,
SYSCALL_FILTER_SET_CLOCK,
SYSCALL_FILTER_SET_CPU_EMULATION,
+ SYSCALL_FILTER_SET_CREDENTIALS,
SYSCALL_FILTER_SET_DEBUG,
SYSCALL_FILTER_SET_FILE_SYSTEM,
SYSCALL_FILTER_SET_IO_EVENT,
SYSCALL_FILTER_SET_IPC,
SYSCALL_FILTER_SET_KEYRING,
+ SYSCALL_FILTER_SET_MEMLOCK,
SYSCALL_FILTER_SET_MODULE,
SYSCALL_FILTER_SET_MOUNT,
SYSCALL_FILTER_SET_NETWORK_IO,
SYSCALL_FILTER_SET_REBOOT,
SYSCALL_FILTER_SET_RESOURCES,
SYSCALL_FILTER_SET_SETUID,
+ SYSCALL_FILTER_SET_SIGNAL,
SYSCALL_FILTER_SET_SWAP,
+ SYSCALL_FILTER_SET_TIMER,
_SYSCALL_FILTER_SET_MAX
};
int seccomp_filter_set_add(Set *s, bool b, const SyscallFilterSet *set);
+int seccomp_add_syscall_filter_item(scmp_filter_ctx *ctx, const char *name, uint32_t action, char **exclude);
+
int seccomp_load_syscall_filter_set(uint32_t default_action, const SyscallFilterSet *set, uint32_t action);
int seccomp_load_syscall_filter_set_raw(uint32_t default_action, Set* set, uint32_t action);
*(t++) = *f;
}
+ /* if string ended with a stray %, also end with % */
+ if (percent)
+ *(t++) = '%';
+
*t = 0;
*_ret = ret;
return 0;
arg_verb,
NULL
};
- static const char* const dirs[] = {SYSTEM_SLEEP_PATH, NULL};
+ static const char* const dirs[] = {
+ SYSTEM_SLEEP_PATH,
+ NULL
+ };
int r;
_cleanup_fclose_ FILE *f = NULL;
_cleanup_strv_free_ char **files = NULL;
char **f;
- r = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs);
+ r = conf_files_list_nulstr(&files, ".conf", NULL, 0, conf_file_dirs);
if (r < 0) {
log_error_errno(r, "Failed to enumerate sysctl.d files: %m");
goto finish;
_cleanup_strv_free_ char **files = NULL;
char **f;
- r = conf_files_list_nulstr(&files, ".conf", arg_root, conf_file_dirs);
+ r = conf_files_list_nulstr(&files, ".conf", arg_root, 0, conf_file_dirs);
if (r < 0) {
log_error_errno(r, "Failed to enumerate sysusers.d files: %m");
goto finish;
}
}
+ /* Let's tell nss-systemd not to synthesize the "root" and "nobody" entries for it, so that our detection
+ * whether the names or UID/GID area already used otherwise doesn't get confused. After all, even though
+ * nss-systemd synthesizes these users/groups, they should still appear in /etc/passwd and /etc/group, as the
+ * synthesizing logic is merely supposed to be fallback for cases where we run with a completely unpopulated
+ * /etc. */
+ if (setenv("SYSTEMD_NSS_BYPASS_SYNTHETIC", "1", 1) < 0) {
+ r = log_error_errno(errno, "Failed to set SYSTEMD_NSS_BYPASS_SYNTHETIC environment variable: %m");
+ goto finish;
+ }
+
if (!uid_range) {
/* Default to default range of 1..SYSTEMD_UID_MAX */
r = uid_range_add(&uid_range, &n_uid_range, 1, SYSTEM_UID_MAX);
log_debug("/* Check when filtered by suffix */");
- assert_se(conf_files_list(&found_files, ".conf", root_dir, search_1, search_2, NULL) == 0);
+ assert_se(conf_files_list(&found_files, ".conf", root_dir, 0, search_1, search_2, NULL) == 0);
strv_print(found_files);
assert_se(found_files);
assert_se(found_files[2] == NULL);
log_debug("/* Check when unfiltered */");
- assert_se(conf_files_list(&found_files2, NULL, root_dir, search_1, search_2, NULL) == 0);
+ assert_se(conf_files_list(&found_files2, NULL, root_dir, 0, search_1, search_2, NULL) == 0);
strv_print(found_files2);
assert_se(found_files2);
test(m, "exec-read-only-path-succeed.service", 0, CLD_EXITED);
}
+static void test_exec_unset_environment(Manager *m) {
+ test(m, "exec-unset-environment.service", 0, CLD_EXITED);
+}
+
static int run_tests(UnitFileScope scope, const test_function_t *tests) {
const test_function_t *test = NULL;
Manager *m = NULL;
test_exec_ioschedulingclass,
test_exec_spec_interpolation,
test_exec_read_only_path_suceed,
+ test_exec_unset_environment,
NULL,
};
static const test_function_t system_tests[] = {
assert_se(wait_for_terminate_and_warn("lockpersonalityseccomp", pid, true) == EXIT_SUCCESS);
}
+static void test_filter_sets_ordered(void) {
+ size_t i;
+
+ /* Ensure "@default" always remains at the beginning of the list */
+ assert_se(SYSCALL_FILTER_SET_DEFAULT == 0);
+ assert_se(streq(syscall_filter_sets[0].name, "@default"));
+
+ for (i = 0; i < _SYSCALL_FILTER_SET_MAX; i++) {
+ const char *k, *p = NULL;
+
+ /* Make sure each group has a description */
+ assert_se(!isempty(syscall_filter_sets[0].help));
+
+ /* Make sure the groups are ordered alphabetically, except for the first entry */
+ assert_se(i < 2 || strcmp(syscall_filter_sets[i-1].name, syscall_filter_sets[i].name) < 0);
+
+ NULSTR_FOREACH(k, syscall_filter_sets[i].value) {
+
+ /* Ensure each syscall list is in itself ordered, but groups before names */
+ assert_se(!p ||
+ (*p == '@' && *k != '@') ||
+ (((*p == '@' && *k == '@') ||
+ (*p != '@' && *k != '@')) &&
+ strcmp(p, k) < 0));
+
+ p = k;
+ }
+ }
+}
+
int main(int argc, char *argv[]) {
log_set_max_level(LOG_DEBUG);
test_restrict_archs();
test_load_syscall_filter_set_raw();
test_lock_personality();
+ test_filter_sets_ordered();
return 0;
}
/* general tests */
expect(u, "%%", "%");
expect(u, "%%s", "%s");
- expect(u, "%", ""); // REALLY?
+ expect(u, "%,", "%,");
+ expect(u, "%", "%");
/* normal unit */
expect(u, "%n", "blah.service");
_cleanup_strv_free_ char **files = NULL;
char **f;
- r = conf_files_list_nulstr(&files, ".conf", arg_root, conf_file_dirs);
+ r = conf_files_list_nulstr(&files, ".conf", arg_root, 0, conf_file_dirs);
if (r < 0) {
log_error_errno(r, "Failed to enumerate tmpfiles.d files: %m");
goto finish;
/* update timestamp */
paths_check_timestamp(link_dirs, &ctx->link_dirs_ts_usec, true);
- r = conf_files_list_strv(&files, ".link", NULL, link_dirs);
+ r = conf_files_list_strv(&files, ".link", NULL, 0, link_dirs);
if (r < 0)
return log_error_errno(r, "failed to enumerate link files: %m");
udev_rules_check_timestamp(rules);
- r = conf_files_list_strv(&files, ".rules", NULL, rules_dirs);
+ r = conf_files_list_strv(&files, ".rules", NULL, 0, rules_dirs);
if (r < 0) {
log_error_errno(r, "failed to enumerate rules files: %m");
return udev_rules_unref(rules);
}
trie->nodes_count++;
- err = conf_files_list_strv(&files, ".hwdb", root, conf_file_dirs);
+ err = conf_files_list_strv(&files, ".hwdb", root, 0, conf_file_dirs);
if (err < 0) {
log_error_errno(err, "failed to enumerate hwdb files: %m");
rc = EXIT_FAILURE;
test-execute/exec-systemcallfilter-not-failing.service
test-execute/exec-systemcallfilter-system-user.service
test-execute/exec-systemcallfilter-system-user-nfsnobody.service
+ test-execute/exec-unset-environment.service
test-execute/exec-user.service
test-execute/exec-user-nfsnobody.service
test-execute/exec-workingdirectory.service
--- /dev/null
+[Unit]
+Description=Test for UnsetEnvironment
+
+[Service]
+ExecStart=/bin/sh -x -c 'test "$$FOO" = "bar" && test "$${QUUX-X}" = "X" && test "$$VAR3" = "value3" && test "$${VAR4-X}" = "X" && test "$$VAR5" = "value5" && test "$${X%b-X}" = "X"'
+Type=oneshot
+Environment=FOO=bar QUUX=waldo VAR3=value3 VAR4=value4 VAR5=value5 X%b=%U
+UnsetEnvironment=QUUX=waldo VAR3=somethingelse VAR4 X%b=%U
# See tmpfiles.d(5) for details
# Set the NOCOW attribute for directories of journal files. This flag
-# is inheredited by their new files and sub-directories. Matters only
+# is inherited by their new files and sub-directories. Matters only
# for btrfs filesystems.
#
# WARNING: Enabling the NOCOW attribute improves journal performance
# Unset locale for the console getty since the console has problems
# displaying some internationalized messages.
-Environment=LANG= LANGUAGE= LC_CTYPE= LC_NUMERIC= LC_TIME= LC_COLLATE= LC_MONETARY= LC_MESSAGES= LC_PAPER= LC_NAME= LC_ADDRESS= LC_TELEPHONE= LC_MEASUREMENT= LC_IDENTIFICATION=
+UnsetEnvironment=LANG LANGUAGE LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT LC_IDENTIFICATION
[Install]
WantedBy=sysinit.target
# Unset locale for the console getty since the console has problems
# displaying some internationalized messages.
-Environment=LANG= LANGUAGE= LC_CTYPE= LC_NUMERIC= LC_TIME= LC_COLLATE= LC_MONETARY= LC_MESSAGES= LC_PAPER= LC_NAME= LC_ADDRESS= LC_TELEPHONE= LC_MEASUREMENT= LC_IDENTIFICATION=
+UnsetEnvironment=LANG LANGUAGE LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT LC_IDENTIFICATION
[Install]
WantedBy=getty.target
RestrictAddressFamilies=AF_UNIX
SystemCallFilter=~@clock @cpu-emulation @debug @keyring @module @mount @obsolete @raw-io @reboot @swap
SystemCallArchitectures=native
+LockPersonality=yes
StateDirectory=systemd/coredump
RestrictAddressFamilies=AF_UNIX
SystemCallFilter=~@clock @cpu-emulation @debug @keyring @module @mount @obsolete @raw-io @reboot @swap
SystemCallArchitectures=native
+LockPersonality=yes
ReadWritePaths=/etc
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
SystemCallFilter=~@clock @cpu-emulation @debug @keyring @module @obsolete @raw-io @reboot @swap
SystemCallArchitectures=native
+LockPersonality=yes
RestrictNamespaces=yes
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
SystemCallArchitectures=native
+LockPersonality=yes
# If there are many split upjournal files we need a lot of fds to
# access them all and combine
RestrictNamespaces=yes
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
SystemCallArchitectures=native
+LockPersonality=yes
LogsDirectory=journal/remote
[Install]
RestrictNamespaces=yes
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
SystemCallArchitectures=native
+LockPersonality=yes
StateDirectory=systemd/journal-upload
# If there are many split up journal files we need a lot of fds to
RestrictAddressFamilies=AF_UNIX AF_NETLINK
SystemCallFilter=~@clock @cpu-emulation @debug @keyring @module @mount @obsolete @raw-io @reboot @swap
SystemCallArchitectures=native
+LockPersonality=yes
# Increase the default a bit in order to allow many simultaneous
# services being run since we keep one fd open per service. Also, when
RestrictAddressFamilies=AF_UNIX
SystemCallFilter=~@clock @cpu-emulation @debug @keyring @module @mount @obsolete @raw-io @reboot @swap
SystemCallArchitectures=native
+LockPersonality=yes
ReadWritePaths=/etc
RestrictAddressFamilies=AF_UNIX AF_NETLINK AF_INET AF_INET6
SystemCallFilter=~@clock @cpu-emulation @debug @keyring @module @obsolete @raw-io @reboot @swap
SystemCallArchitectures=native
+LockPersonality=yes
FileDescriptorStoreMax=512
# Increase the default a bit in order to allow many simultaneous
RestrictAddressFamilies=AF_UNIX AF_NETLINK AF_INET AF_INET6
SystemCallFilter=~@clock @cpu-emulation @debug @keyring @module @obsolete @raw-io @reboot @swap
SystemCallArchitectures=native
+LockPersonality=yes
# Note that machined cannot be placed in a mount namespace, since it
# needs access to the host's mount namespace in order to implement the
RestrictAddressFamilies=AF_UNIX AF_NETLINK AF_INET AF_INET6 AF_PACKET
SystemCallFilter=~@clock @cpu-emulation @debug @keyring @module @mount @obsolete @raw-io @reboot @swap
SystemCallArchitectures=native
+LockPersonality=yes
RuntimeDirectory=systemd/netif
RuntimeDirectoryPreserve=yes
RestrictAddressFamilies=AF_UNIX AF_NETLINK AF_INET AF_INET6
SystemCallFilter=~@clock @cpu-emulation @debug @keyring @module @mount @obsolete @raw-io @reboot @swap
SystemCallArchitectures=native
+LockPersonality=yes
RuntimeDirectory=systemd/resolve
RuntimeDirectoryPreserve=yes
RestrictAddressFamilies=AF_UNIX
SystemCallFilter=~@cpu-emulation @debug @keyring @module @mount @obsolete @raw-io @reboot @swap
SystemCallArchitectures=native
+LockPersonality=yes
ReadWritePaths=/etc
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
SystemCallFilter=~@cpu-emulation @debug @keyring @module @mount @obsolete @raw-io @reboot @swap
SystemCallArchitectures=native
+LockPersonality=yes
StateDirectory=systemd/timesync
[Install]
RestrictRealtime=yes
RestrictAddressFamilies=AF_UNIX AF_NETLINK AF_INET AF_INET6
SystemCallArchitectures=native
+LockPersonality=yes