mirror of
https://github.com/systemd/systemd-stable.git
synced 2025-01-11 05:17:44 +03:00
Merge pull request #7198 from poettering/stdin-stdout
Add StandardInput=data, StandardInput=file:... and more
This commit is contained in:
commit
0133d5553a
37
TODO
37
TODO
@ -32,13 +32,6 @@ Features:
|
||||
|
||||
* Add NetworkNamespacePath= to specify a path to a network namespace
|
||||
|
||||
* Add StandardInputData= and StandardInputText= for putting together data to
|
||||
pass to a service through stdin
|
||||
|
||||
* Add StandardInputPath=, StandardOutputPath=, StandardErrorPath= to connect a
|
||||
service to a specific file. Be smart, and if the specified path refers to an
|
||||
AF_UNIX socket, connect() to it.
|
||||
|
||||
* maybe use SOURCE_DATE_EPOCH (i.e. the env var the reproducible builds folks
|
||||
introduced) as the RTC epoch, instead of the mtime of NEWS.
|
||||
|
||||
@ -51,6 +44,9 @@ Features:
|
||||
|
||||
* document Environment=SYSTEMD_LOG_LEVEL=debug drop-in in debugging document
|
||||
|
||||
* rework ExecOutput and ExecInput enums so that EXEC_OUTPUT_NULL loses its
|
||||
magic meaning and is no longer upgraded to something else if set explicitly.
|
||||
|
||||
* add a way to remove fds from the fdstore by name, and make logind use it
|
||||
|
||||
* in the long run: permit a system with /etc/machine-id linked to /dev/null, to
|
||||
@ -99,9 +95,18 @@ Features:
|
||||
taken if multiple dirs are configured. Maybe avoid setting the env vars in
|
||||
that case?
|
||||
|
||||
* introduce SuccessAction= that permits shutting down the system when a service
|
||||
succeeds. This is useful to replace "ExecPost=/usr/bin/systemctl poweroff" and
|
||||
similar constructs, which are frequently used. This is particularly nice for
|
||||
implementation of a systemd.run= kernel command line option that runs some
|
||||
command and immediately shuts down.
|
||||
|
||||
* expose IO accounting data on the bus, show it in systemd-run --wait and log
|
||||
about it in the resource log message
|
||||
|
||||
* rework unbase64 code to drop whitespace automatically, so that we don't have
|
||||
to drop it first.
|
||||
|
||||
* add "systemctl purge" for flushing out configuration, state, logs, ... of a
|
||||
unit when it is stopped
|
||||
|
||||
@ -110,12 +115,6 @@ Features:
|
||||
|
||||
* replace all uses of fgets() + LINE_MAX by read_line()
|
||||
|
||||
* set IPAddressDeny=any on all services that shouldn't do networking (possibly
|
||||
combined with IPAddressAllow=localhost).
|
||||
|
||||
* dissect: when we discover squashfs, don't claim we had a "writable" partition
|
||||
in systemd-dissect
|
||||
|
||||
* 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.
|
||||
@ -150,15 +149,6 @@ Features:
|
||||
--as-pid2 switch, and sanely proxy sd_notify() messages dropping stuff such
|
||||
as MAINPID.
|
||||
|
||||
* change the dependency Set* objects in Unit structures to become Hashmap*, and
|
||||
then store a bit mask who created a specific dependency: the source unit via
|
||||
fragment configuration, the destination unit via fragment configuration, or
|
||||
the source unit via udev rules (in case of .device units), or any combination
|
||||
thereof. This information can then be used to flush out old udev-created
|
||||
dependencies when the udev properties change, and eventually to implement a
|
||||
"systemctl refresh" operation for reloading the configuration of individual
|
||||
units without reloading the whole set.
|
||||
|
||||
* Add ExecMonitor= setting. May be used multiple times. Forks off a process in
|
||||
the service cgroup, which is supposed to monitor the service, and when it
|
||||
exits the service is considered failed by its monitor.
|
||||
@ -334,8 +324,6 @@ Features:
|
||||
|
||||
* Rework systemctl's GetAll property parsing to use the generic bus_map_all_properties() API
|
||||
|
||||
* implement a per-service firewall based on net_cls
|
||||
|
||||
* Port various tools to make use of verbs.[ch], where applicable: busctl,
|
||||
coredumpctl, hostnamectl, localectl, systemd-analyze, timedatectl
|
||||
|
||||
@ -689,7 +677,6 @@ Features:
|
||||
- document that deps in [Unit] sections ignore Alias= fields in
|
||||
[Install] units of other units, unless those units are disabled
|
||||
- man: clarify that time-sync.target is not only sysv compat but also useful otherwise. Same for similar targets
|
||||
- document the exit codes when services fail before they are exec()ed
|
||||
- document that service reload may be implemented as service reexec
|
||||
- document in wiki how to map ical recurrence events to systemd timer unit calendar specifications
|
||||
- add a man page containing packaging guidelines and recommending usage of things like Documentation=, PrivateTmp=, PrivateNetwork= and ReadOnlyDirectories=/etc /usr.
|
||||
|
@ -484,145 +484,116 @@
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>StandardInput=</varname></term>
|
||||
<listitem><para>Controls where file descriptor 0 (STDIN) of
|
||||
the executed processes is connected to. Takes one of
|
||||
<option>null</option>,
|
||||
<option>tty</option>,
|
||||
<option>tty-force</option>,
|
||||
<option>tty-fail</option>,
|
||||
<option>socket</option> or
|
||||
<option>fd</option>.</para>
|
||||
<listitem><para>Controls where file descriptor 0 (STDIN) of the executed processes is connected to. Takes one
|
||||
of <option>null</option>, <option>tty</option>, <option>tty-force</option>, <option>tty-fail</option>,
|
||||
<option>data</option>, <option>file:<replaceable>path</replaceable></option>, <option>socket</option> or
|
||||
<option>fd:<replaceable>name</replaceable></option>.</para>
|
||||
|
||||
<para>If <option>null</option> is selected, standard input
|
||||
will be connected to <filename>/dev/null</filename>, i.e. all
|
||||
read attempts by the process will result in immediate
|
||||
EOF.</para>
|
||||
<para>If <option>null</option> is selected, standard input will be connected to <filename>/dev/null</filename>,
|
||||
i.e. all read attempts by the process will result in immediate EOF.</para>
|
||||
|
||||
<para>If <option>tty</option> is selected, standard input is
|
||||
connected to a TTY (as configured by
|
||||
<varname>TTYPath=</varname>, see below) and the executed
|
||||
process becomes the controlling process of the terminal. If
|
||||
the terminal is already being controlled by another process,
|
||||
the executed process waits until the current controlling
|
||||
process releases the terminal.</para>
|
||||
<para>If <option>tty</option> is selected, standard input is connected to a TTY (as configured by
|
||||
<varname>TTYPath=</varname>, see below) and the executed process becomes the controlling process of the
|
||||
terminal. If the terminal is already being controlled by another process, the executed process waits until the
|
||||
current controlling process releases the terminal.</para>
|
||||
|
||||
<para><option>tty-force</option> is similar to
|
||||
<option>tty</option>, but the executed process is forcefully
|
||||
and immediately made the controlling process of the terminal,
|
||||
potentially removing previous controlling processes from the
|
||||
terminal.</para>
|
||||
<para><option>tty-force</option> is similar to <option>tty</option>, but the executed process is forcefully and
|
||||
immediately made the controlling process of the terminal, potentially removing previous controlling processes
|
||||
from the terminal.</para>
|
||||
|
||||
<para><option>tty-fail</option> is similar to
|
||||
<option>tty</option> but if the terminal already has a
|
||||
controlling process start-up of the executed process
|
||||
fails.</para>
|
||||
<para><option>tty-fail</option> is similar to <option>tty</option>, but if the terminal already has a
|
||||
controlling process start-up of the executed process fails.</para>
|
||||
|
||||
<para>The <option>socket</option> option is only valid in
|
||||
socket-activated services, and only when the socket
|
||||
configuration file (see
|
||||
<citerefentry><refentrytitle>systemd.socket</refentrytitle><manvolnum>5</manvolnum></citerefentry>
|
||||
for details) specifies a single socket only. If this option is
|
||||
set, standard input will be connected to the socket the
|
||||
service was activated from, which is primarily useful for
|
||||
compatibility with daemons designed for use with the
|
||||
traditional
|
||||
<citerefentry project='freebsd'><refentrytitle>inetd</refentrytitle><manvolnum>8</manvolnum></citerefentry>
|
||||
<para>The <option>data</option> option may be used to configure arbitrary textual or binary data to pass via
|
||||
standard input to the executed process. The data to pass is configured via
|
||||
<varname>StandardInputText=</varname>/<varname>StandardInputData=</varname> (see below). Note that the actual
|
||||
file descriptor type passed (memory file, regular file, UNIX pipe, …) might depend on the kernel and available
|
||||
privileges. In any case, the file descriptor is read-only, and when read returns the specified data
|
||||
followed by EOF.</para>
|
||||
|
||||
<para>The <option>file:<replaceable>path</replaceable></option> option may be used to connect a specific file
|
||||
system object to standard input. An absolute path following the <literal>:</literal> character is expected,
|
||||
which may refer to a regular file, a FIFO or special file. If an <constant>AF_UNIX</constant> socket in the
|
||||
file system is specified, a stream socket is connected to it. The latter is useful for connecting standard
|
||||
input of processes to arbitrary system services.</para>
|
||||
|
||||
<para>The <option>socket</option> option is valid in socket-activated services only, and requires the relevant
|
||||
socket unit file (see
|
||||
<citerefentry><refentrytitle>systemd.socket</refentrytitle><manvolnum>5</manvolnum></citerefentry> for details)
|
||||
to have <varname>Accept=yes</varname> set, or to specify a single socket only. If this option is set, standard
|
||||
input will be connected to the socket the service was activated from, which is primarily useful for
|
||||
compatibility with daemons designed for use with the traditional <citerefentry
|
||||
project='freebsd'><refentrytitle>inetd</refentrytitle><manvolnum>8</manvolnum></citerefentry> socket activation
|
||||
daemon.</para>
|
||||
|
||||
<para>The <option>fd</option> option connects
|
||||
the input stream to a single file descriptor provided by a socket unit.
|
||||
A custom named file descriptor can be specified as part of this option,
|
||||
after a <literal>:</literal> (e.g. <literal>fd:<replaceable>foobar</replaceable></literal>).
|
||||
If no name is specified, <literal>stdin</literal> is assumed
|
||||
(i.e. <literal>fd</literal> is equivalent to <literal>fd:stdin</literal>).
|
||||
At least one socket unit defining such name must be explicitly provided via the
|
||||
<varname>Sockets=</varname> option, and file descriptor name may differ
|
||||
from the name of its containing socket unit.
|
||||
If multiple matches are found, the first one will be used.
|
||||
See <varname>FileDescriptorName=</varname> in
|
||||
<citerefentry><refentrytitle>systemd.socket</refentrytitle><manvolnum>5</manvolnum></citerefentry>
|
||||
for more details about named descriptors and ordering.</para>
|
||||
<para>The <option>fd:<replaceable>name</replaceable></option> option connects standard input to a specific,
|
||||
named file descriptor provided by a socket unit. The name may be specified as part of this option, following a
|
||||
<literal>:</literal> character (e.g. <literal>fd:foobar</literal>). If no name is specified, the name
|
||||
<literal>stdin</literal> is implied (i.e. <literal>fd</literal> is equivalent to <literal>fd:stdin</literal>).
|
||||
At least one socket unit defining the specified name must be provided via the <varname>Sockets=</varname>
|
||||
option, and the file descriptor name may differ from the name of its containing socket unit. If multiple
|
||||
matches are found, the first one will be used. See <varname>FileDescriptorName=</varname> in
|
||||
<citerefentry><refentrytitle>systemd.socket</refentrytitle><manvolnum>5</manvolnum></citerefentry> for more
|
||||
details about named file descriptors and their ordering.</para>
|
||||
|
||||
<para>This setting defaults to
|
||||
<option>null</option>.</para></listitem>
|
||||
<para>This setting defaults to <option>null</option>.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>StandardOutput=</varname></term>
|
||||
<listitem><para>Controls where file descriptor 1 (STDOUT) of
|
||||
the executed processes is connected to. Takes one of
|
||||
<option>inherit</option>,
|
||||
<option>null</option>,
|
||||
<option>tty</option>,
|
||||
<option>journal</option>,
|
||||
<option>syslog</option>,
|
||||
<option>kmsg</option>,
|
||||
<option>journal+console</option>,
|
||||
<option>syslog+console</option>,
|
||||
<option>kmsg+console</option>,
|
||||
<option>socket</option> or
|
||||
<option>fd</option>.</para>
|
||||
<listitem><para>Controls where file descriptor 1 (STDOUT) of the executed processes is connected to. Takes one
|
||||
of <option>inherit</option>, <option>null</option>, <option>tty</option>, <option>journal</option>,
|
||||
<option>syslog</option>, <option>kmsg</option>, <option>journal+console</option>,
|
||||
<option>syslog+console</option>, <option>kmsg+console</option>,
|
||||
<option>file:<replaceable>path</replaceable></option>, <option>socket</option> or
|
||||
<option>fd:<replaceable>name</replaceable></option>.</para>
|
||||
|
||||
<para><option>inherit</option> duplicates the file descriptor
|
||||
of standard input for standard output.</para>
|
||||
<para><option>inherit</option> duplicates the file descriptor of standard input for standard output.</para>
|
||||
|
||||
<para><option>null</option> connects standard output to
|
||||
<filename>/dev/null</filename>, i.e. everything written to it
|
||||
will be lost.</para>
|
||||
<para><option>null</option> connects standard output to <filename>/dev/null</filename>, i.e. everything written
|
||||
to it will be lost.</para>
|
||||
|
||||
<para><option>tty</option> connects standard output to a tty
|
||||
(as configured via <varname>TTYPath=</varname>, see below). If
|
||||
the TTY is used for output only, the executed process will not
|
||||
become the controlling process of the terminal, and will not
|
||||
fail or wait for other processes to release the
|
||||
terminal.</para>
|
||||
<para><option>tty</option> connects standard output to a tty (as configured via <varname>TTYPath=</varname>,
|
||||
see below). If the TTY is used for output only, the executed process will not become the controlling process of
|
||||
the terminal, and will not fail or wait for other processes to release the terminal.</para>
|
||||
|
||||
<para><option>journal</option> connects standard output with
|
||||
the journal which is accessible via
|
||||
<citerefentry><refentrytitle>journalctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>.
|
||||
Note that everything that is written to syslog or kmsg (see
|
||||
below) is implicitly stored in the journal as well, the
|
||||
specific two options listed below are hence supersets of this
|
||||
one.</para>
|
||||
<para><option>journal</option> connects standard output with the journal which is accessible via
|
||||
<citerefentry><refentrytitle>journalctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>. Note that
|
||||
everything that is written to syslog or kmsg (see below) is implicitly stored in the journal as well, the
|
||||
specific two options listed below are hence supersets of this one.</para>
|
||||
|
||||
<para><option>syslog</option> connects standard output to the
|
||||
<citerefentry project='man-pages'><refentrytitle>syslog</refentrytitle><manvolnum>3</manvolnum></citerefentry>
|
||||
system syslog service, in addition to the journal. Note that
|
||||
the journal daemon is usually configured to forward everything
|
||||
it receives to syslog anyway, in which case this option is no
|
||||
different from <option>journal</option>.</para>
|
||||
<para><option>syslog</option> connects standard output to the <citerefentry
|
||||
project='man-pages'><refentrytitle>syslog</refentrytitle><manvolnum>3</manvolnum></citerefentry> system syslog
|
||||
service, in addition to the journal. Note that the journal daemon is usually configured to forward everything
|
||||
it receives to syslog anyway, in which case this option is no different from <option>journal</option>.</para>
|
||||
|
||||
<para><option>kmsg</option> connects standard output with the
|
||||
kernel log buffer which is accessible via
|
||||
<para><option>kmsg</option> connects standard output with the kernel log buffer which is accessible via
|
||||
<citerefentry project='man-pages'><refentrytitle>dmesg</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
|
||||
in addition to the journal. The journal daemon might be
|
||||
configured to send all logs to kmsg anyway, in which case this
|
||||
option is no different from <option>journal</option>.</para>
|
||||
in addition to the journal. The journal daemon might be configured to send all logs to kmsg anyway, in which
|
||||
case this option is no different from <option>journal</option>.</para>
|
||||
|
||||
<para><option>journal+console</option>,
|
||||
<option>syslog+console</option> and
|
||||
<option>kmsg+console</option> work in a similar way as the
|
||||
three options above but copy the output to the system console
|
||||
as well.</para>
|
||||
<para><option>journal+console</option>, <option>syslog+console</option> and <option>kmsg+console</option> work
|
||||
in a similar way as the three options above but copy the output to the system console as well.</para>
|
||||
|
||||
<para><option>socket</option> connects standard output to a
|
||||
socket acquired via socket activation. The semantics are
|
||||
similar to the same option of
|
||||
<varname>StandardInput=</varname>.</para>
|
||||
<para>The <option>file:<replaceable>path</replaceable></option> option may be used to connect a specific file
|
||||
system object to standard output. The semantics are similar to the same option of
|
||||
<varname>StandardInputText=</varname>, see above. If standard input and output are directed to the same file
|
||||
path, it is opened only once, for reading as well as writing and duplicated. This is particular useful when the
|
||||
specified path refers to an <constant>AF_UNIX</constant> socket in the file system, as in that case only a
|
||||
single stream connection is created for both input and output.</para>
|
||||
|
||||
<para>The <option>fd</option> option connects
|
||||
the output stream to a single file descriptor provided by a socket unit.
|
||||
A custom named file descriptor can be specified as part of this option,
|
||||
after a <literal>:</literal> (e.g. <literal>fd:<replaceable>foobar</replaceable></literal>).
|
||||
If no name is specified, <literal>stdout</literal> is assumed
|
||||
(i.e. <literal>fd</literal> is equivalent to <literal>fd:stdout</literal>).
|
||||
At least one socket unit defining such name must be explicitly provided via the
|
||||
<varname>Sockets=</varname> option, and file descriptor name may differ
|
||||
from the name of its containing socket unit.
|
||||
If multiple matches are found, the first one will be used.
|
||||
See <varname>FileDescriptorName=</varname> in
|
||||
<citerefentry><refentrytitle>systemd.socket</refentrytitle><manvolnum>5</manvolnum></citerefentry>
|
||||
for more details about named descriptors and ordering.</para>
|
||||
<para><option>socket</option> connects standard output to a socket acquired via socket activation. The
|
||||
semantics are similar to the same option of <varname>StandardInput=</varname>, see above.</para>
|
||||
|
||||
<para>The <option>fd:<replaceable>name</replaceable></option> option connects standard output to a specific,
|
||||
named file descriptor provided by a socket unit. A name may be specified as part of this option, following a
|
||||
<literal>:</literal> character (e.g. <literal>fd:foobar</literal>). If no name is
|
||||
specified, the name <literal>stdout</literal> is implied (i.e. <literal>fd</literal> is equivalent to
|
||||
<literal>fd:stdout</literal>). At least one socket unit defining the specified name must be provided via the
|
||||
<varname>Sockets=</varname> option, and the file descriptor name may differ from the name of its containing socket
|
||||
unit. If multiple matches are found, the first one will be used. See <varname>FileDescriptorName=</varname>
|
||||
in <citerefentry><refentrytitle>systemd.socket</refentrytitle><manvolnum>5</manvolnum></citerefentry> for more
|
||||
details about named descriptors and their ordering.</para>
|
||||
|
||||
<para>If the standard output (or error output, see below) of a unit is connected to the journal, syslog or the
|
||||
kernel log buffer, the unit will implicitly gain a dependency of type <varname>After=</varname> on
|
||||
@ -632,32 +603,66 @@
|
||||
"hello" > /dev/stderr</command> for writing text to stderr will not work. To mitigate this use the construct
|
||||
<command>echo "hello" >&2</command> instead, which is mostly equivalent and avoids this pitfall.</para>
|
||||
|
||||
<para>This setting defaults to the value set with
|
||||
<varname>DefaultStandardOutput=</varname> in
|
||||
<citerefentry><refentrytitle>systemd-system.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
|
||||
which defaults to <option>journal</option>. Note that setting
|
||||
this parameter might result in additional dependencies to be
|
||||
added to the unit (see above).</para>
|
||||
<para>This setting defaults to the value set with <varname>DefaultStandardOutput=</varname> in
|
||||
<citerefentry><refentrytitle>systemd-system.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>, which
|
||||
defaults to <option>journal</option>. Note that setting this parameter might result in additional dependencies
|
||||
to be added to the unit (see above).</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>StandardError=</varname></term>
|
||||
<listitem><para>Controls where file descriptor 2 (STDERR) of
|
||||
the executed processes is connected to. The available options
|
||||
are identical to those of <varname>StandardOutput=</varname>,
|
||||
with some exceptions: if set to <option>inherit</option> the
|
||||
file descriptor used for standard output is duplicated for
|
||||
standard error, while <option>fd</option> operates on the error
|
||||
stream and will look by default for a descriptor named
|
||||
<listitem><para>Controls where file descriptor 2 (STDERR) of the executed processes is connected to. The
|
||||
available options are identical to those of <varname>StandardOutput=</varname>, with some exceptions: if set to
|
||||
<option>inherit</option> the file descriptor used for standard output is duplicated for standard error, while
|
||||
<option>fd:<replaceable>name</replaceable></option> will use a default file descriptor name of
|
||||
<literal>stderr</literal>.</para>
|
||||
|
||||
<para>This setting defaults to the value set with
|
||||
<varname>DefaultStandardError=</varname> in
|
||||
<citerefentry><refentrytitle>systemd-system.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
|
||||
which defaults to <option>inherit</option>. Note that setting
|
||||
this parameter might result in additional dependencies to be
|
||||
added to the unit (see above).</para></listitem>
|
||||
<para>This setting defaults to the value set with <varname>DefaultStandardError=</varname> in
|
||||
<citerefentry><refentrytitle>systemd-system.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>, which
|
||||
defaults to <option>inherit</option>. Note that setting this parameter might result in additional dependencies
|
||||
to be added to the unit (see above).</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>StandardInputText=</varname></term>
|
||||
<term><varname>StandardInputData=</varname></term>
|
||||
|
||||
<listitem><para>Configures arbitrary textual or binary data to pass via file descriptor 0 (STDIN) to the
|
||||
executed processes. These settings have no effect unless <varname>StandardInput=</varname> is set to
|
||||
<option>data</option>. Use this option to embed process input data directly in the unit file.</para>
|
||||
|
||||
<para><varname>StandardInputText=</varname> accepts arbitrary textual data. C-style escapes for special
|
||||
characters as well as the usual <literal>%</literal>-specifiers are resolved. Each time this setting is used
|
||||
the the specified text is appended to the per-unit data buffer, followed by a newline character (thus every use
|
||||
appends a new line to the end of the buffer). Note that leading and trailing whitespace of lines configured
|
||||
with this option is removed. If an empty line is specified the buffer is cleared (hence, in order to insert an
|
||||
empty line, add an additional <literal>\n</literal> to the end or beginning of a line).</para>
|
||||
|
||||
<para><varname>StandardInputData=</varname> accepts arbitrary binary data, encoded in <ulink
|
||||
url="https://tools.ietf.org/html/rfc2045#section-6.8">Base64</ulink>. No escape sequences or specifiers are
|
||||
resolved. Any whitespace in the encoded version is ignored during decoding.</para>
|
||||
|
||||
<para>Note that <varname>StandardInputText=</varname> and <varname>StandardInputData=</varname> operate on the
|
||||
same data buffer, and may be mixed in order to configure both binary and textual data for the same input
|
||||
stream. The textual or binary data is joined strictly in the order the settings appear in the unit
|
||||
file. Assigning an empty string to either will reset the data buffer.</para>
|
||||
|
||||
<para>Please keep in mind that in order to maintain readability long unit file settings may be split into
|
||||
multiple lines, by suffixing each line (except for the last) with a <literal>\</literal> character (see
|
||||
<citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
|
||||
details). This is particularly useful for large data configured with these two options. Example:</para>
|
||||
|
||||
<programlisting>…
|
||||
StandardInput=data
|
||||
StandardInputData=SWNrIHNpdHplIGRhIHVuJyBlc3NlIEtsb3BzLAp1ZmYgZWVtYWwga2xvcHAncy4KSWNrIGtpZWtl \
|
||||
LCBzdGF1bmUsIHd1bmRyZSBtaXIsCnVmZiBlZW1hbCBqZWh0IHNlIHVmZiBkaWUgVMO8ci4KTmFu \
|
||||
dSwgZGVuayBpY2ssIGljayBkZW5rIG5hbnUhCkpldHogaXNzZSB1ZmYsIGVyc2NodCB3YXIgc2Ug \
|
||||
enUhCkljayBqZWhlIHJhdXMgdW5kIGJsaWNrZSDigJQKdW5kIHdlciBzdGVodCBkcmF1w59lbj8g \
|
||||
SWNrZSEK
|
||||
…
|
||||
</programlisting>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
|
@ -1279,7 +1279,7 @@ int cg_split_spec(const char *spec, char **controller, char **path) {
|
||||
assert(spec);
|
||||
|
||||
if (*spec == '/') {
|
||||
if (!path_is_safe(spec))
|
||||
if (!path_is_normalized(spec))
|
||||
return -EINVAL;
|
||||
|
||||
if (path) {
|
||||
@ -1332,7 +1332,7 @@ int cg_split_spec(const char *spec, char **controller, char **path) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (!path_is_safe(u) ||
|
||||
if (!path_is_normalized(u) ||
|
||||
!path_is_absolute(u)) {
|
||||
free(t);
|
||||
free(u);
|
||||
|
@ -27,8 +27,10 @@
|
||||
|
||||
#include "dirent-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "fs-util.h"
|
||||
#include "macro.h"
|
||||
#include "memfd-util.h"
|
||||
#include "missing.h"
|
||||
#include "parse-util.h"
|
||||
#include "path-util.h"
|
||||
@ -378,3 +380,202 @@ int fd_get_path(int fd, char **ret) {
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int move_fd(int from, int to, int cloexec) {
|
||||
int r;
|
||||
|
||||
/* Move fd 'from' to 'to', make sure FD_CLOEXEC remains equal if requested, and release the old fd. If
|
||||
* 'cloexec' is passed as -1, the original FD_CLOEXEC is inherited for the new fd. If it is 0, it is turned
|
||||
* off, if it is > 0 it is turned on. */
|
||||
|
||||
if (from < 0)
|
||||
return -EBADF;
|
||||
if (to < 0)
|
||||
return -EBADF;
|
||||
|
||||
if (from == to) {
|
||||
|
||||
if (cloexec >= 0) {
|
||||
r = fd_cloexec(to, cloexec);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return to;
|
||||
}
|
||||
|
||||
if (cloexec < 0) {
|
||||
int fl;
|
||||
|
||||
fl = fcntl(from, F_GETFD, 0);
|
||||
if (fl < 0)
|
||||
return -errno;
|
||||
|
||||
cloexec = !!(fl & FD_CLOEXEC);
|
||||
}
|
||||
|
||||
r = dup3(from, to, cloexec ? O_CLOEXEC : 0);
|
||||
if (r < 0)
|
||||
return -errno;
|
||||
|
||||
assert(r == to);
|
||||
|
||||
safe_close(from);
|
||||
|
||||
return to;
|
||||
}
|
||||
|
||||
int acquire_data_fd(const void *data, size_t size, unsigned flags) {
|
||||
|
||||
char procfs_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
|
||||
_cleanup_close_pair_ int pipefds[2] = { -1, -1 };
|
||||
char pattern[] = "/dev/shm/data-fd-XXXXXX";
|
||||
_cleanup_close_ int fd = -1;
|
||||
int isz = 0, r;
|
||||
ssize_t n;
|
||||
off_t f;
|
||||
|
||||
assert(data || size == 0);
|
||||
|
||||
/* Acquire a read-only file descriptor that when read from returns the specified data. This is much more
|
||||
* complex than I wish it was. But here's why:
|
||||
*
|
||||
* a) First we try to use memfds. They are the best option, as we can seal them nicely to make them
|
||||
* read-only. Unfortunately they require kernel 3.17, and – at the time of writing – we still support 3.14.
|
||||
*
|
||||
* b) Then, we try classic pipes. They are the second best options, as we can close the writing side, retaining
|
||||
* a nicely read-only fd in the reading side. However, they are by default quite small, and unprivileged
|
||||
* clients can only bump their size to a system-wide limit, which might be quite low.
|
||||
*
|
||||
* c) Then, we try an O_TMPFILE file in /dev/shm (that dir is the only suitable one known to exist from
|
||||
* earliest boot on). To make it read-only we open the fd a second time with O_RDONLY via
|
||||
* /proc/self/<fd>. Unfortunately O_TMPFILE is not available on older kernels on tmpfs.
|
||||
*
|
||||
* d) Finally, we try creating a regular file in /dev/shm, which we then delete.
|
||||
*
|
||||
* It sucks a bit that depending on the situation we return very different objects here, but that's Linux I
|
||||
* figure. */
|
||||
|
||||
if (size == 0 && ((flags & ACQUIRE_NO_DEV_NULL) == 0)) {
|
||||
/* As a special case, return /dev/null if we have been called for an empty data block */
|
||||
r = open("/dev/null", O_RDONLY|O_CLOEXEC|O_NOCTTY);
|
||||
if (r < 0)
|
||||
return -errno;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
if ((flags & ACQUIRE_NO_MEMFD) == 0) {
|
||||
fd = memfd_new("data-fd");
|
||||
if (fd < 0)
|
||||
goto try_pipe;
|
||||
|
||||
n = write(fd, data, size);
|
||||
if (n < 0)
|
||||
return -errno;
|
||||
if ((size_t) n != size)
|
||||
return -EIO;
|
||||
|
||||
f = lseek(fd, 0, SEEK_SET);
|
||||
if (f != 0)
|
||||
return -errno;
|
||||
|
||||
r = memfd_set_sealed(fd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = fd;
|
||||
fd = -1;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
try_pipe:
|
||||
if ((flags & ACQUIRE_NO_PIPE) == 0) {
|
||||
if (pipe2(pipefds, O_CLOEXEC|O_NONBLOCK) < 0)
|
||||
return -errno;
|
||||
|
||||
isz = fcntl(pipefds[1], F_GETPIPE_SZ, 0);
|
||||
if (isz < 0)
|
||||
return -errno;
|
||||
|
||||
if ((size_t) isz < size) {
|
||||
isz = (int) size;
|
||||
if (isz < 0 || (size_t) isz != size)
|
||||
return -E2BIG;
|
||||
|
||||
/* Try to bump the pipe size */
|
||||
(void) fcntl(pipefds[1], F_SETPIPE_SZ, isz);
|
||||
|
||||
/* See if that worked */
|
||||
isz = fcntl(pipefds[1], F_GETPIPE_SZ, 0);
|
||||
if (isz < 0)
|
||||
return -errno;
|
||||
|
||||
if ((size_t) isz < size)
|
||||
goto try_dev_shm;
|
||||
}
|
||||
|
||||
n = write(pipefds[1], data, size);
|
||||
if (n < 0)
|
||||
return -errno;
|
||||
if ((size_t) n != size)
|
||||
return -EIO;
|
||||
|
||||
(void) fd_nonblock(pipefds[0], false);
|
||||
|
||||
r = pipefds[0];
|
||||
pipefds[0] = -1;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
try_dev_shm:
|
||||
if ((flags & ACQUIRE_NO_TMPFILE) == 0) {
|
||||
fd = open("/dev/shm", O_RDWR|O_TMPFILE|O_CLOEXEC, 0500);
|
||||
if (fd < 0)
|
||||
goto try_dev_shm_without_o_tmpfile;
|
||||
|
||||
n = write(fd, data, size);
|
||||
if (n < 0)
|
||||
return -errno;
|
||||
if ((size_t) n != size)
|
||||
return -EIO;
|
||||
|
||||
/* Let's reopen the thing, in order to get an O_RDONLY fd for the original O_RDWR one */
|
||||
xsprintf(procfs_path, "/proc/self/fd/%i", fd);
|
||||
r = open(procfs_path, O_RDONLY|O_CLOEXEC);
|
||||
if (r < 0)
|
||||
return -errno;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
try_dev_shm_without_o_tmpfile:
|
||||
if ((flags & ACQUIRE_NO_REGULAR) == 0) {
|
||||
fd = mkostemp_safe(pattern);
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
n = write(fd, data, size);
|
||||
if (n < 0) {
|
||||
r = -errno;
|
||||
goto unlink_and_return;
|
||||
}
|
||||
if ((size_t) n != size) {
|
||||
r = -EIO;
|
||||
goto unlink_and_return;
|
||||
}
|
||||
|
||||
/* Let's reopen the thing, in order to get an O_RDONLY fd for the original O_RDWR one */
|
||||
r = open(pattern, O_RDONLY|O_CLOEXEC);
|
||||
if (r < 0)
|
||||
r = -errno;
|
||||
|
||||
unlink_and_return:
|
||||
(void) unlink(pattern);
|
||||
return r;
|
||||
}
|
||||
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
@ -76,6 +76,18 @@ bool fdname_is_valid(const char *s);
|
||||
|
||||
int fd_get_path(int fd, char **ret);
|
||||
|
||||
int move_fd(int from, int to, int cloexec);
|
||||
|
||||
enum {
|
||||
ACQUIRE_NO_DEV_NULL = 1 << 0,
|
||||
ACQUIRE_NO_MEMFD = 1 << 1,
|
||||
ACQUIRE_NO_PIPE = 1 << 2,
|
||||
ACQUIRE_NO_TMPFILE = 1 << 3,
|
||||
ACQUIRE_NO_REGULAR = 1 << 4,
|
||||
};
|
||||
|
||||
int acquire_data_fd(const void *data, size_t size, unsigned flags);
|
||||
|
||||
/* Hint: ENETUNREACH happens if we try to connect to "non-existing" special IP addresses, such as ::5 */
|
||||
#define ERRNO_IS_DISCONNECT(r) \
|
||||
IN_SET(r, ENOTCONN, ECONNRESET, ECONNREFUSED, ECONNABORTED, EPIPE, ENETUNREACH)
|
||||
|
@ -509,7 +509,7 @@ static int getenv_tmp_dir(const char **ret_path) {
|
||||
r = -ENOTDIR;
|
||||
goto next;
|
||||
}
|
||||
if (!path_is_safe(e)) {
|
||||
if (!path_is_normalized(e)) {
|
||||
r = -EPERM;
|
||||
goto next;
|
||||
}
|
||||
@ -707,7 +707,7 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
|
||||
|
||||
if (errno == ENOENT &&
|
||||
(flags & CHASE_NONEXISTENT) &&
|
||||
(isempty(todo) || path_is_safe(todo))) {
|
||||
(isempty(todo) || path_is_normalized(todo))) {
|
||||
|
||||
/* If CHASE_NONEXISTENT is set, and the path does not exist, then that's OK, return
|
||||
* what we got so far. But don't allow this if the remaining path contains "../ or "./"
|
||||
|
@ -97,7 +97,10 @@ int unhexmem(const char *p, size_t l, void **mem, size_t *len) {
|
||||
|
||||
assert(mem);
|
||||
assert(len);
|
||||
assert(p);
|
||||
assert(p || l == 0);
|
||||
|
||||
if (l == (size_t) -1)
|
||||
l = strlen(p);
|
||||
|
||||
if (l % 2 != 0)
|
||||
return -EINVAL;
|
||||
@ -161,6 +164,8 @@ char *base32hexmem(const void *p, size_t l, bool padding) {
|
||||
const uint8_t *x;
|
||||
size_t len;
|
||||
|
||||
assert(p || l == 0);
|
||||
|
||||
if (padding)
|
||||
/* five input bytes makes eight output bytes, padding is added so we must round up */
|
||||
len = 8 * (l + 4) / 5;
|
||||
@ -270,7 +275,12 @@ int unbase32hexmem(const char *p, size_t l, bool padding, void **mem, size_t *_l
|
||||
size_t len;
|
||||
unsigned pad = 0;
|
||||
|
||||
assert(p);
|
||||
assert(p || l == 0);
|
||||
assert(mem);
|
||||
assert(_len);
|
||||
|
||||
if (l == (size_t) -1)
|
||||
l = strlen(p);
|
||||
|
||||
/* padding ensures any base32hex input has input divisible by 8 */
|
||||
if (padding && l % 8 != 0)
|
||||
@ -520,6 +530,9 @@ ssize_t base64mem(const void *p, size_t l, char **out) {
|
||||
char *r, *z;
|
||||
const uint8_t *x;
|
||||
|
||||
assert(p || l == 0);
|
||||
assert(out);
|
||||
|
||||
/* three input bytes makes four output bytes, padding is added so we must round up */
|
||||
z = r = malloc(4 * (l + 2) / 3 + 1);
|
||||
if (!r)
|
||||
@ -555,10 +568,11 @@ ssize_t base64mem(const void *p, size_t l, char **out) {
|
||||
return z - r;
|
||||
}
|
||||
|
||||
static int base64_append_width(char **prefix, int plen,
|
||||
const char *sep, int indent,
|
||||
const void *p, size_t l,
|
||||
int width) {
|
||||
static int base64_append_width(
|
||||
char **prefix, int plen,
|
||||
const char *sep, int indent,
|
||||
const void *p, size_t l,
|
||||
int width) {
|
||||
|
||||
_cleanup_free_ char *x = NULL;
|
||||
char *t, *s;
|
||||
@ -597,17 +611,18 @@ static int base64_append_width(char **prefix, int plen,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int base64_append(char **prefix, int plen,
|
||||
const void *p, size_t l,
|
||||
int indent, int width) {
|
||||
int base64_append(
|
||||
char **prefix, int plen,
|
||||
const void *p, size_t l,
|
||||
int indent, int width) {
|
||||
|
||||
if (plen > width / 2 || plen + indent > width)
|
||||
/* leave indent on the left, keep last column free */
|
||||
return base64_append_width(prefix, plen, "\n", indent, p, l, width - indent - 1);
|
||||
else
|
||||
/* leave plen on the left, keep last column free */
|
||||
return base64_append_width(prefix, plen, NULL, plen, p, l, width - plen - 1);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
int unbase64mem(const char *p, size_t l, void **mem, size_t *_len) {
|
||||
_cleanup_free_ uint8_t *r = NULL;
|
||||
@ -616,7 +631,12 @@ int unbase64mem(const char *p, size_t l, void **mem, size_t *_len) {
|
||||
const char *x;
|
||||
size_t len;
|
||||
|
||||
assert(p);
|
||||
assert(p || l == 0);
|
||||
assert(mem);
|
||||
assert(_len);
|
||||
|
||||
if (l == (size_t) -1)
|
||||
l = strlen(p);
|
||||
|
||||
/* padding ensures any base63 input has input divisible by 4 */
|
||||
if (l % 4 != 0)
|
||||
@ -660,6 +680,7 @@ int unbase64mem(const char *p, size_t l, void **mem, size_t *_len) {
|
||||
}
|
||||
|
||||
switch (l % 4) {
|
||||
|
||||
case 3:
|
||||
a = unbase64char(x[0]);
|
||||
if (a < 0)
|
||||
@ -717,7 +738,10 @@ void hexdump(FILE *f, const void *p, size_t s) {
|
||||
const uint8_t *b = p;
|
||||
unsigned n = 0;
|
||||
|
||||
assert(s == 0 || b);
|
||||
assert(b || s == 0);
|
||||
|
||||
if (!f)
|
||||
f = stdout;
|
||||
|
||||
while (s > 0) {
|
||||
size_t i;
|
||||
|
@ -722,7 +722,7 @@ bool filename_is_valid(const char *p) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool path_is_safe(const char *p) {
|
||||
bool path_is_normalized(const char *p) {
|
||||
|
||||
if (isempty(p))
|
||||
return false;
|
||||
@ -736,7 +736,6 @@ bool path_is_safe(const char *p) {
|
||||
if (strlen(p)+1 > PATH_MAX)
|
||||
return false;
|
||||
|
||||
/* The following two checks are not really dangerous, but hey, they still are confusing */
|
||||
if (startswith(p, "./") || endswith(p, "/.") || strstr(p, "/./"))
|
||||
return false;
|
||||
|
||||
|
@ -132,7 +132,7 @@ int parse_path_argument_and_warn(const char *path, bool suppress_root, char **ar
|
||||
char* dirname_malloc(const char *path);
|
||||
|
||||
bool filename_is_valid(const char *p) _pure_;
|
||||
bool path_is_safe(const char *p) _pure_;
|
||||
bool path_is_normalized(const char *p) _pure_;
|
||||
|
||||
char *file_in_same_dir(const char *path, const char *filename);
|
||||
|
||||
|
@ -271,17 +271,21 @@ static const char * const rlmap_initrd[] = {
|
||||
};
|
||||
|
||||
const char* runlevel_to_target(const char *word) {
|
||||
const char * const *rlmap_ptr;
|
||||
size_t i;
|
||||
const char * const *rlmap_ptr = in_initrd() ? rlmap_initrd
|
||||
: rlmap;
|
||||
|
||||
if (!word)
|
||||
return NULL;
|
||||
|
||||
if (in_initrd() && (word = startswith(word, "rd.")) == NULL)
|
||||
return NULL;
|
||||
if (in_initrd()) {
|
||||
word = startswith(word, "rd.");
|
||||
if (!word)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i = 0; rlmap_ptr[i] != NULL; i += 2)
|
||||
rlmap_ptr = in_initrd() ? rlmap_initrd : rlmap;
|
||||
|
||||
for (i = 0; rlmap_ptr[i]; i += 2)
|
||||
if (streq(word, rlmap_ptr[i]))
|
||||
return rlmap_ptr[i+1];
|
||||
|
||||
|
@ -383,7 +383,7 @@ int unit_name_path_escape(const char *f, char **ret) {
|
||||
if (STR_IN_SET(p, "/", ""))
|
||||
s = strdup("-");
|
||||
else {
|
||||
if (!path_is_safe(p))
|
||||
if (!path_is_normalized(p))
|
||||
return -EINVAL;
|
||||
|
||||
/* Truncate trailing slashes */
|
||||
@ -433,7 +433,7 @@ int unit_name_path_unescape(const char *f, char **ret) {
|
||||
if (!s)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!path_is_safe(s)) {
|
||||
if (!path_is_normalized(s)) {
|
||||
free(s);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -606,7 +606,7 @@ bool valid_home(const char *p) {
|
||||
if (!path_is_absolute(p))
|
||||
return false;
|
||||
|
||||
if (!path_is_safe(p))
|
||||
if (!path_is_normalized(p))
|
||||
return false;
|
||||
|
||||
/* Colons are used as field separators, and hence not OK */
|
||||
|
@ -35,6 +35,7 @@
|
||||
#include "execute.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "hexdecoct.h"
|
||||
#include "io-util.h"
|
||||
#include "ioprio.h"
|
||||
#include "journal-util.h"
|
||||
@ -684,7 +685,7 @@ static int property_get_syslog_facility(
|
||||
return sd_bus_message_append(reply, "i", LOG_FAC(c->syslog_priority));
|
||||
}
|
||||
|
||||
static int property_get_input_fdname(
|
||||
static int property_get_stdio_fdname(
|
||||
sd_bus *bus,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
@ -694,19 +695,26 @@ static int property_get_input_fdname(
|
||||
sd_bus_error *error) {
|
||||
|
||||
ExecContext *c = userdata;
|
||||
const char *name;
|
||||
int fileno;
|
||||
|
||||
assert(bus);
|
||||
assert(c);
|
||||
assert(property);
|
||||
assert(reply);
|
||||
|
||||
name = exec_context_fdname(c, STDIN_FILENO);
|
||||
if (streq(property, "StandardInputFileDescriptorName"))
|
||||
fileno = STDIN_FILENO;
|
||||
else if (streq(property, "StandardOutputFileDescriptorName"))
|
||||
fileno = STDOUT_FILENO;
|
||||
else {
|
||||
assert(streq(property, "StandardErrorFileDescriptorName"));
|
||||
fileno = STDERR_FILENO;
|
||||
}
|
||||
|
||||
return sd_bus_message_append(reply, "s", name);
|
||||
return sd_bus_message_append(reply, "s", exec_context_fdname(c, fileno));
|
||||
}
|
||||
|
||||
static int property_get_output_fdname(
|
||||
static int property_get_input_data(
|
||||
sd_bus *bus,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
@ -716,19 +724,13 @@ static int property_get_output_fdname(
|
||||
sd_bus_error *error) {
|
||||
|
||||
ExecContext *c = userdata;
|
||||
const char *name = NULL;
|
||||
|
||||
assert(bus);
|
||||
assert(c);
|
||||
assert(property);
|
||||
assert(reply);
|
||||
|
||||
if (c->std_output == EXEC_OUTPUT_NAMED_FD && streq(property, "StandardOutputFileDescriptorName"))
|
||||
name = exec_context_fdname(c, STDOUT_FILENO);
|
||||
else if (c->std_error == EXEC_OUTPUT_NAMED_FD && streq(property, "StandardErrorFileDescriptorName"))
|
||||
name = exec_context_fdname(c, STDERR_FILENO);
|
||||
|
||||
return sd_bus_message_append(reply, "s", name);
|
||||
return sd_bus_message_append_array(reply, 'y', c->stdin_data, c->stdin_data_size);
|
||||
}
|
||||
|
||||
static int property_get_bind_paths(
|
||||
@ -858,11 +860,12 @@ const sd_bus_vtable bus_exec_vtable[] = {
|
||||
SD_BUS_PROPERTY("CPUSchedulingResetOnFork", "b", bus_property_get_bool, offsetof(ExecContext, cpu_sched_reset_on_fork), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("NonBlocking", "b", bus_property_get_bool, offsetof(ExecContext, non_blocking), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("StandardInput", "s", property_get_exec_input, offsetof(ExecContext, std_input), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("StandardInputFileDescriptorName", "s", property_get_input_fdname, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("StandardInputFileDescriptorName", "s", property_get_stdio_fdname, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("StandardInputData", "ay", property_get_input_data, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("StandardOutput", "s", bus_property_get_exec_output, offsetof(ExecContext, std_output), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("StandardOutputFileDescriptorName", "s", property_get_output_fdname, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("StandardOutputFileDescriptorName", "s", property_get_stdio_fdname, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("StandardError", "s", bus_property_get_exec_output, offsetof(ExecContext, std_error), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("StandardErrorFileDescriptorName", "s", property_get_output_fdname, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("StandardErrorFileDescriptorName", "s", property_get_stdio_fdname, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("TTYPath", "s", NULL, offsetof(ExecContext, tty_path), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("TTYReset", "b", bus_property_get_bool, offsetof(ExecContext, tty_reset), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("TTYVHangup", "b", bus_property_get_bool, offsetof(ExecContext, tty_vhangup), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
@ -1814,28 +1817,125 @@ int bus_exec_context_set_transient_property(
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!fdname_is_valid(s))
|
||||
if (isempty(s))
|
||||
s = NULL;
|
||||
else if (!fdname_is_valid(s))
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid file descriptor name");
|
||||
|
||||
if (mode != UNIT_CHECK) {
|
||||
|
||||
if (streq(name, "StandardInputFileDescriptorName")) {
|
||||
r = free_and_strdup(c->stdio_fdname + STDIN_FILENO, s);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
c->std_input = EXEC_INPUT_NAMED_FD;
|
||||
r = free_and_strdup(&c->stdio_fdname[STDIN_FILENO], s);
|
||||
if (r < 0)
|
||||
return r;
|
||||
unit_write_drop_in_private_format(u, mode, name, "StandardInput=fd:%s", s);
|
||||
unit_write_drop_in_private_format(u, mode, name, "StandardInput=fd:%s", exec_context_fdname(c, STDIN_FILENO));
|
||||
|
||||
} else if (streq(name, "StandardOutputFileDescriptorName")) {
|
||||
c->std_output = EXEC_OUTPUT_NAMED_FD;
|
||||
r = free_and_strdup(&c->stdio_fdname[STDOUT_FILENO], s);
|
||||
r = free_and_strdup(c->stdio_fdname + STDOUT_FILENO, s);
|
||||
if (r < 0)
|
||||
return r;
|
||||
unit_write_drop_in_private_format(u, mode, name, "StandardOutput=fd:%s", s);
|
||||
} else if (streq(name, "StandardErrorFileDescriptorName")) {
|
||||
c->std_error = EXEC_OUTPUT_NAMED_FD;
|
||||
|
||||
c->std_output = EXEC_OUTPUT_NAMED_FD;
|
||||
unit_write_drop_in_private_format(u, mode, name, "StandardOutput=fd:%s", exec_context_fdname(c, STDOUT_FILENO));
|
||||
|
||||
} else {
|
||||
assert(streq(name, "StandardErrorFileDescriptorName"));
|
||||
|
||||
r = free_and_strdup(&c->stdio_fdname[STDERR_FILENO], s);
|
||||
if (r < 0)
|
||||
return r;
|
||||
unit_write_drop_in_private_format(u, mode, name, "StandardError=fd:%s", s);
|
||||
|
||||
c->std_error = EXEC_OUTPUT_NAMED_FD;
|
||||
unit_write_drop_in_private_format(u, mode, name, "StandardError=fd:%s", exec_context_fdname(c, STDERR_FILENO));
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
||||
} else if (STR_IN_SET(name, "StandardInputFile", "StandardOutputFile", "StandardErrorFile")) {
|
||||
const char *s;
|
||||
|
||||
r = sd_bus_message_read(message, "s", &s);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!path_is_absolute(s))
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path %s is not absolute", s);
|
||||
if (!path_is_normalized(s))
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path %s is not normalized", s);
|
||||
|
||||
if (mode != UNIT_CHECK) {
|
||||
|
||||
if (streq(name, "StandardInputFile")) {
|
||||
r = free_and_strdup(&c->stdio_file[STDIN_FILENO], s);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
c->std_input = EXEC_INPUT_FILE;
|
||||
unit_write_drop_in_private_format(u, mode, name, "StandardInput=file:%s", s);
|
||||
|
||||
} else if (streq(name, "StandardOutputFile")) {
|
||||
r = free_and_strdup(&c->stdio_file[STDOUT_FILENO], s);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
c->std_output = EXEC_OUTPUT_FILE;
|
||||
unit_write_drop_in_private_format(u, mode, name, "StandardOutput=file:%s", s);
|
||||
|
||||
} else {
|
||||
assert(streq(name, "StandardErrorFile"));
|
||||
|
||||
r = free_and_strdup(&c->stdio_file[STDERR_FILENO], s);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
c->std_error = EXEC_OUTPUT_FILE;
|
||||
unit_write_drop_in_private_format(u, mode, name, "StandardError=file:%s", s);
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
||||
} else if (streq(name, "StandardInputData")) {
|
||||
const void *p;
|
||||
size_t sz;
|
||||
|
||||
r = sd_bus_message_read_array(message, 'y', &p, &sz);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (mode != UNIT_CHECK) {
|
||||
_cleanup_free_ char *encoded = NULL;
|
||||
|
||||
if (sz == 0) {
|
||||
c->stdin_data = mfree(c->stdin_data);
|
||||
c->stdin_data_size = 0;
|
||||
|
||||
unit_write_drop_in_private(u, mode, name, "StandardInputData=");
|
||||
} else {
|
||||
void *q;
|
||||
ssize_t n;
|
||||
|
||||
if (c->stdin_data_size + sz < c->stdin_data_size || /* check for overflow */
|
||||
c->stdin_data_size + sz > EXEC_STDIN_DATA_MAX)
|
||||
return -E2BIG;
|
||||
|
||||
n = base64mem(p, sz, &encoded);
|
||||
if (n < 0)
|
||||
return (int) n;
|
||||
|
||||
q = realloc(c->stdin_data, c->stdin_data_size + sz);
|
||||
if (!q)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy((uint8_t*) q + c->stdin_data_size, p, sz);
|
||||
|
||||
c->stdin_data = q;
|
||||
c->stdin_data_size += sz;
|
||||
|
||||
unit_write_drop_in_private_format(u, mode, name, "StandardInputData=%s", encoded);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2363,7 +2463,7 @@ int bus_exec_context_set_transient_property(
|
||||
return r;
|
||||
|
||||
STRV_FOREACH(p, l) {
|
||||
if (!path_is_safe(*p) || path_is_absolute(*p))
|
||||
if (!path_is_normalized(*p) || path_is_absolute(*p))
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "%s= path is not valid: %s", name, *p);
|
||||
}
|
||||
|
||||
|
@ -276,7 +276,7 @@ static bool exec_context_needs_term(const ExecContext *c) {
|
||||
}
|
||||
|
||||
static int open_null_as(int flags, int nfd) {
|
||||
int fd, r;
|
||||
int fd;
|
||||
|
||||
assert(nfd >= 0);
|
||||
|
||||
@ -284,13 +284,7 @@ static int open_null_as(int flags, int nfd) {
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
|
||||
if (fd != nfd) {
|
||||
r = dup2(fd, nfd) < 0 ? -errno : nfd;
|
||||
safe_close(fd);
|
||||
} else
|
||||
r = nfd;
|
||||
|
||||
return r;
|
||||
return move_fd(fd, nfd, false);
|
||||
}
|
||||
|
||||
static int connect_journal_socket(int fd, uid_t uid, gid_t gid) {
|
||||
@ -382,34 +376,78 @@ static int connect_logger_as(
|
||||
is_kmsg_output(output),
|
||||
is_terminal_output(output));
|
||||
|
||||
if (fd == nfd)
|
||||
return nfd;
|
||||
|
||||
r = dup2(fd, nfd) < 0 ? -errno : nfd;
|
||||
safe_close(fd);
|
||||
|
||||
return r;
|
||||
return move_fd(fd, nfd, false);
|
||||
}
|
||||
static int open_terminal_as(const char *path, mode_t mode, int nfd) {
|
||||
int fd, r;
|
||||
static int open_terminal_as(const char *path, int flags, int nfd) {
|
||||
int fd;
|
||||
|
||||
assert(path);
|
||||
assert(nfd >= 0);
|
||||
|
||||
fd = open_terminal(path, mode | O_NOCTTY);
|
||||
fd = open_terminal(path, flags | O_NOCTTY);
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
if (fd != nfd) {
|
||||
r = dup2(fd, nfd) < 0 ? -errno : nfd;
|
||||
safe_close(fd);
|
||||
} else
|
||||
r = nfd;
|
||||
|
||||
return r;
|
||||
return move_fd(fd, nfd, false);
|
||||
}
|
||||
|
||||
static int fixup_input(ExecInput std_input, int socket_fd, bool apply_tty_stdin) {
|
||||
static int acquire_path(const char *path, int flags, mode_t mode) {
|
||||
union sockaddr_union sa = {
|
||||
.sa.sa_family = AF_UNIX,
|
||||
};
|
||||
int fd, r;
|
||||
|
||||
assert(path);
|
||||
|
||||
if (IN_SET(flags & O_ACCMODE, O_WRONLY, O_RDWR))
|
||||
flags |= O_CREAT;
|
||||
|
||||
fd = open(path, flags|O_NOCTTY, mode);
|
||||
if (fd >= 0)
|
||||
return fd;
|
||||
|
||||
if (errno != ENXIO) /* ENXIO is returned when we try to open() an AF_UNIX file system socket on Linux */
|
||||
return -errno;
|
||||
if (strlen(path) > sizeof(sa.un.sun_path)) /* Too long, can't be a UNIX socket */
|
||||
return -ENXIO;
|
||||
|
||||
/* So, it appears the specified path could be an AF_UNIX socket. Let's see if we can connect to it. */
|
||||
|
||||
fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
|
||||
strncpy(sa.un.sun_path, path, sizeof(sa.un.sun_path));
|
||||
if (connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) {
|
||||
safe_close(fd);
|
||||
return errno == EINVAL ? -ENXIO : -errno; /* Propagate initial error if we get EINVAL, i.e. we have
|
||||
* indication that his wasn't an AF_UNIX socket after all */
|
||||
}
|
||||
|
||||
if ((flags & O_ACCMODE) == O_RDONLY)
|
||||
r = shutdown(fd, SHUT_WR);
|
||||
else if ((flags & O_ACCMODE) == O_WRONLY)
|
||||
r = shutdown(fd, SHUT_RD);
|
||||
else
|
||||
return fd;
|
||||
if (r < 0) {
|
||||
safe_close(fd);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
static int fixup_input(
|
||||
const ExecContext *context,
|
||||
int socket_fd,
|
||||
bool apply_tty_stdin) {
|
||||
|
||||
ExecInput std_input;
|
||||
|
||||
assert(context);
|
||||
|
||||
std_input = context->std_input;
|
||||
|
||||
if (is_terminal_input(std_input) && !apply_tty_stdin)
|
||||
return EXEC_INPUT_NULL;
|
||||
@ -417,6 +455,9 @@ static int fixup_input(ExecInput std_input, int socket_fd, bool apply_tty_stdin)
|
||||
if (std_input == EXEC_INPUT_SOCKET && socket_fd < 0)
|
||||
return EXEC_INPUT_NULL;
|
||||
|
||||
if (std_input == EXEC_INPUT_DATA && context->stdin_data_size == 0)
|
||||
return EXEC_INPUT_NULL;
|
||||
|
||||
return std_input;
|
||||
}
|
||||
|
||||
@ -444,13 +485,15 @@ static int setup_input(
|
||||
return -errno;
|
||||
|
||||
/* Try to make this the controlling tty, if it is a tty, and reset it */
|
||||
(void) ioctl(STDIN_FILENO, TIOCSCTTY, context->std_input == EXEC_INPUT_TTY_FORCE);
|
||||
(void) reset_terminal_fd(STDIN_FILENO, true);
|
||||
if (isatty(STDIN_FILENO)) {
|
||||
(void) ioctl(STDIN_FILENO, TIOCSCTTY, context->std_input == EXEC_INPUT_TTY_FORCE);
|
||||
(void) reset_terminal_fd(STDIN_FILENO, true);
|
||||
}
|
||||
|
||||
return STDIN_FILENO;
|
||||
}
|
||||
|
||||
i = fixup_input(context->std_input, socket_fd, params->flags & EXEC_APPLY_TTY_STDIN);
|
||||
i = fixup_input(context, socket_fd, params->flags & EXEC_APPLY_TTY_STDIN);
|
||||
|
||||
switch (i) {
|
||||
|
||||
@ -460,7 +503,7 @@ static int setup_input(
|
||||
case EXEC_INPUT_TTY:
|
||||
case EXEC_INPUT_TTY_FORCE:
|
||||
case EXEC_INPUT_TTY_FAIL: {
|
||||
int fd, r;
|
||||
int fd;
|
||||
|
||||
fd = acquire_terminal(exec_context_tty_path(context),
|
||||
i == EXEC_INPUT_TTY_FAIL,
|
||||
@ -470,22 +513,46 @@ static int setup_input(
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
if (fd != STDIN_FILENO) {
|
||||
r = dup2(fd, STDIN_FILENO) < 0 ? -errno : STDIN_FILENO;
|
||||
safe_close(fd);
|
||||
} else
|
||||
r = STDIN_FILENO;
|
||||
|
||||
return r;
|
||||
return move_fd(fd, STDIN_FILENO, false);
|
||||
}
|
||||
|
||||
case EXEC_INPUT_SOCKET:
|
||||
assert(socket_fd >= 0);
|
||||
|
||||
return dup2(socket_fd, STDIN_FILENO) < 0 ? -errno : STDIN_FILENO;
|
||||
|
||||
case EXEC_INPUT_NAMED_FD:
|
||||
assert(named_iofds[STDIN_FILENO] >= 0);
|
||||
|
||||
(void) fd_nonblock(named_iofds[STDIN_FILENO], false);
|
||||
return dup2(named_iofds[STDIN_FILENO], STDIN_FILENO) < 0 ? -errno : STDIN_FILENO;
|
||||
|
||||
case EXEC_INPUT_DATA: {
|
||||
int fd;
|
||||
|
||||
fd = acquire_data_fd(context->stdin_data, context->stdin_data_size, 0);
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
return move_fd(fd, STDIN_FILENO, false);
|
||||
}
|
||||
|
||||
case EXEC_INPUT_FILE: {
|
||||
bool rw;
|
||||
int fd;
|
||||
|
||||
assert(context->stdio_file[STDIN_FILENO]);
|
||||
|
||||
rw = (context->std_output == EXEC_OUTPUT_FILE && streq_ptr(context->stdio_file[STDIN_FILENO], context->stdio_file[STDOUT_FILENO])) ||
|
||||
(context->std_error == EXEC_OUTPUT_FILE && streq_ptr(context->stdio_file[STDIN_FILENO], context->stdio_file[STDERR_FILENO]));
|
||||
|
||||
fd = acquire_path(context->stdio_file[STDIN_FILENO], rw ? O_RDWR : O_RDONLY, 0666 & ~context->umask);
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
return move_fd(fd, STDIN_FILENO, false);
|
||||
}
|
||||
|
||||
default:
|
||||
assert_not_reached("Unknown input type");
|
||||
}
|
||||
@ -530,7 +597,7 @@ static int setup_output(
|
||||
return STDERR_FILENO;
|
||||
}
|
||||
|
||||
i = fixup_input(context->std_input, socket_fd, params->flags & EXEC_APPLY_TTY_STDIN);
|
||||
i = fixup_input(context, socket_fd, params->flags & EXEC_APPLY_TTY_STDIN);
|
||||
o = fixup_output(context->std_output, socket_fd);
|
||||
|
||||
if (fileno == STDERR_FILENO) {
|
||||
@ -559,8 +626,8 @@ static int setup_output(
|
||||
if (i == EXEC_INPUT_NULL && is_terminal_input(context->std_input))
|
||||
return open_terminal_as(exec_context_tty_path(context), O_WRONLY, fileno);
|
||||
|
||||
/* If the input is connected to anything that's not a /dev/null, inherit that... */
|
||||
if (i != EXEC_INPUT_NULL)
|
||||
/* If the input is connected to anything that's not a /dev/null or a data fd, inherit that... */
|
||||
if (!IN_SET(i, EXEC_INPUT_NULL, EXEC_INPUT_DATA))
|
||||
return dup2(STDIN_FILENO, fileno) < 0 ? -errno : fileno;
|
||||
|
||||
/* If we are not started from PID 1 we just inherit STDOUT from our parent process. */
|
||||
@ -613,12 +680,34 @@ static int setup_output(
|
||||
|
||||
case EXEC_OUTPUT_SOCKET:
|
||||
assert(socket_fd >= 0);
|
||||
|
||||
return dup2(socket_fd, fileno) < 0 ? -errno : fileno;
|
||||
|
||||
case EXEC_OUTPUT_NAMED_FD:
|
||||
assert(named_iofds[fileno] >= 0);
|
||||
|
||||
(void) fd_nonblock(named_iofds[fileno], false);
|
||||
return dup2(named_iofds[fileno], fileno) < 0 ? -errno : fileno;
|
||||
|
||||
case EXEC_OUTPUT_FILE: {
|
||||
bool rw;
|
||||
int fd;
|
||||
|
||||
assert(context->stdio_file[fileno]);
|
||||
|
||||
rw = context->std_input == EXEC_INPUT_FILE &&
|
||||
streq_ptr(context->stdio_file[fileno], context->stdio_file[STDIN_FILENO]);
|
||||
|
||||
if (rw)
|
||||
return dup2(STDIN_FILENO, fileno) < 0 ? -errno : fileno;
|
||||
|
||||
fd = acquire_path(context->stdio_file[fileno], O_WRONLY, 0666 & ~context->umask);
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
return move_fd(fd, fileno, false);
|
||||
}
|
||||
|
||||
default:
|
||||
assert_not_reached("Unknown error type");
|
||||
}
|
||||
@ -3501,8 +3590,10 @@ void exec_context_done(ExecContext *c) {
|
||||
for (l = 0; l < ELEMENTSOF(c->rlimit); l++)
|
||||
c->rlimit[l] = mfree(c->rlimit[l]);
|
||||
|
||||
for (l = 0; l < 3; l++)
|
||||
for (l = 0; l < 3; l++) {
|
||||
c->stdio_fdname[l] = mfree(c->stdio_fdname[l]);
|
||||
c->stdio_file[l] = mfree(c->stdio_file[l]);
|
||||
}
|
||||
|
||||
c->working_directory = mfree(c->working_directory);
|
||||
c->root_directory = mfree(c->root_directory);
|
||||
@ -3540,6 +3631,9 @@ void exec_context_done(ExecContext *c) {
|
||||
c->log_level_max = -1;
|
||||
|
||||
exec_context_free_log_extra_fields(c);
|
||||
|
||||
c->stdin_data = mfree(c->stdin_data);
|
||||
c->stdin_data_size = 0;
|
||||
}
|
||||
|
||||
int exec_context_destroy_runtime_directory(ExecContext *c, const char *runtime_prefix) {
|
||||
@ -3614,18 +3708,25 @@ const char* exec_context_fdname(const ExecContext *c, int fd_index) {
|
||||
assert(c);
|
||||
|
||||
switch (fd_index) {
|
||||
|
||||
case STDIN_FILENO:
|
||||
if (c->std_input != EXEC_INPUT_NAMED_FD)
|
||||
return NULL;
|
||||
|
||||
return c->stdio_fdname[STDIN_FILENO] ?: "stdin";
|
||||
|
||||
case STDOUT_FILENO:
|
||||
if (c->std_output != EXEC_OUTPUT_NAMED_FD)
|
||||
return NULL;
|
||||
|
||||
return c->stdio_fdname[STDOUT_FILENO] ?: "stdout";
|
||||
|
||||
case STDERR_FILENO:
|
||||
if (c->std_error != EXEC_OUTPUT_NAMED_FD)
|
||||
return NULL;
|
||||
|
||||
return c->stdio_fdname[STDERR_FILENO] ?: "stderr";
|
||||
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
@ -3935,6 +4036,20 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
|
||||
prefix, exec_output_to_string(c->std_output),
|
||||
prefix, exec_output_to_string(c->std_error));
|
||||
|
||||
if (c->std_input == EXEC_INPUT_NAMED_FD)
|
||||
fprintf(f, "%sStandardInputFileDescriptorName: %s\n", prefix, c->stdio_fdname[STDIN_FILENO]);
|
||||
if (c->std_output == EXEC_OUTPUT_NAMED_FD)
|
||||
fprintf(f, "%sStandardOutputFileDescriptorName: %s\n", prefix, c->stdio_fdname[STDOUT_FILENO]);
|
||||
if (c->std_error == EXEC_OUTPUT_NAMED_FD)
|
||||
fprintf(f, "%sStandardErrorFileDescriptorName: %s\n", prefix, c->stdio_fdname[STDERR_FILENO]);
|
||||
|
||||
if (c->std_input == EXEC_INPUT_FILE)
|
||||
fprintf(f, "%sStandardInputFile: %s\n", prefix, c->stdio_file[STDIN_FILENO]);
|
||||
if (c->std_output == EXEC_OUTPUT_FILE)
|
||||
fprintf(f, "%sStandardOutputFile: %s\n", prefix, c->stdio_file[STDOUT_FILENO]);
|
||||
if (c->std_error == EXEC_OUTPUT_FILE)
|
||||
fprintf(f, "%sStandardErrorFile: %s\n", prefix, c->stdio_file[STDERR_FILENO]);
|
||||
|
||||
if (c->tty_path)
|
||||
fprintf(f,
|
||||
"%sTTYPath: %s\n"
|
||||
@ -4631,6 +4746,8 @@ static const char* const exec_input_table[_EXEC_INPUT_MAX] = {
|
||||
[EXEC_INPUT_TTY_FAIL] = "tty-fail",
|
||||
[EXEC_INPUT_SOCKET] = "socket",
|
||||
[EXEC_INPUT_NAMED_FD] = "fd",
|
||||
[EXEC_INPUT_DATA] = "data",
|
||||
[EXEC_INPUT_FILE] = "file",
|
||||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP(exec_input, ExecInput);
|
||||
@ -4647,6 +4764,7 @@ static const char* const exec_output_table[_EXEC_OUTPUT_MAX] = {
|
||||
[EXEC_OUTPUT_JOURNAL_AND_CONSOLE] = "journal+console",
|
||||
[EXEC_OUTPUT_SOCKET] = "socket",
|
||||
[EXEC_OUTPUT_NAMED_FD] = "fd",
|
||||
[EXEC_OUTPUT_FILE] = "file",
|
||||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP(exec_output, ExecOutput);
|
||||
|
@ -38,6 +38,8 @@ typedef struct ExecParameters ExecParameters;
|
||||
#include "namespace.h"
|
||||
#include "nsflags.h"
|
||||
|
||||
#define EXEC_STDIN_DATA_MAX (64U*1024U*1024U)
|
||||
|
||||
typedef enum ExecUtmpMode {
|
||||
EXEC_UTMP_INIT,
|
||||
EXEC_UTMP_LOGIN,
|
||||
@ -53,6 +55,8 @@ typedef enum ExecInput {
|
||||
EXEC_INPUT_TTY_FAIL,
|
||||
EXEC_INPUT_SOCKET,
|
||||
EXEC_INPUT_NAMED_FD,
|
||||
EXEC_INPUT_DATA,
|
||||
EXEC_INPUT_FILE,
|
||||
_EXEC_INPUT_MAX,
|
||||
_EXEC_INPUT_INVALID = -1
|
||||
} ExecInput;
|
||||
@ -69,6 +73,7 @@ typedef enum ExecOutput {
|
||||
EXEC_OUTPUT_JOURNAL_AND_CONSOLE,
|
||||
EXEC_OUTPUT_SOCKET,
|
||||
EXEC_OUTPUT_NAMED_FD,
|
||||
EXEC_OUTPUT_FILE,
|
||||
_EXEC_OUTPUT_MAX,
|
||||
_EXEC_OUTPUT_INVALID = -1
|
||||
} ExecOutput;
|
||||
@ -163,6 +168,10 @@ struct ExecContext {
|
||||
ExecOutput std_output;
|
||||
ExecOutput std_error;
|
||||
char *stdio_fdname[3];
|
||||
char *stdio_file[3];
|
||||
|
||||
void *stdin_data;
|
||||
size_t stdin_data_size;
|
||||
|
||||
nsec_t timer_slack_nsec;
|
||||
|
||||
|
@ -41,6 +41,8 @@ $1.RemoveIPC, config_parse_bool, 0,
|
||||
$1.StandardInput, config_parse_exec_input, 0, offsetof($1, exec_context)
|
||||
$1.StandardOutput, config_parse_exec_output, 0, offsetof($1, exec_context)
|
||||
$1.StandardError, config_parse_exec_output, 0, offsetof($1, exec_context)
|
||||
$1.StandardInputText, config_parse_exec_input_text, 0, offsetof($1, exec_context)
|
||||
$1.StandardInputData, config_parse_exec_input_data, 0, offsetof($1, exec_context)
|
||||
$1.TTYPath, config_parse_unit_path_printf, 0, offsetof($1, exec_context.tty_path)
|
||||
$1.TTYReset, config_parse_bool, 0, offsetof($1, exec_context.tty_reset)
|
||||
$1.TTYVHangup, config_parse_bool, 0, offsetof($1, exec_context.tty_vhangup)
|
||||
|
@ -46,6 +46,7 @@
|
||||
#include "escape.h"
|
||||
#include "fd-util.h"
|
||||
#include "fs-util.h"
|
||||
#include "hexdecoct.h"
|
||||
#include "io-util.h"
|
||||
#include "ioprio.h"
|
||||
#include "journal-util.h"
|
||||
@ -832,21 +833,22 @@ int config_parse_socket_bindtodevice(
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_CONFIG_PARSE_ENUM(config_parse_input, exec_input, ExecInput, "Failed to parse input literal specifier");
|
||||
DEFINE_CONFIG_PARSE_ENUM(config_parse_output, exec_output, ExecOutput, "Failed to parse output literal specifier");
|
||||
int config_parse_exec_input(
|
||||
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_exec_input(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) {
|
||||
ExecContext *c = data;
|
||||
const char *name;
|
||||
Unit *u = userdata;
|
||||
const char *n;
|
||||
ExecInput ei;
|
||||
int r;
|
||||
|
||||
assert(data);
|
||||
@ -854,41 +856,194 @@ int config_parse_exec_input(const char *unit,
|
||||
assert(line);
|
||||
assert(rvalue);
|
||||
|
||||
name = startswith(rvalue, "fd:");
|
||||
if (name) {
|
||||
/* Strip prefix and validate fd name */
|
||||
if (!fdname_is_valid(name)) {
|
||||
log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid file descriptor name, ignoring: %s", name);
|
||||
n = startswith(rvalue, "fd:");
|
||||
if (n) {
|
||||
_cleanup_free_ char *resolved = NULL;
|
||||
|
||||
r = unit_full_printf(u, n, &resolved);
|
||||
if (r < 0)
|
||||
return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s: %m", n);
|
||||
|
||||
if (isempty(resolved))
|
||||
resolved = mfree(resolved);
|
||||
else if (!fdname_is_valid(resolved)) {
|
||||
log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid file descriptor name: %s", resolved);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
free_and_replace(c->stdio_fdname[STDIN_FILENO], resolved);
|
||||
|
||||
ei = EXEC_INPUT_NAMED_FD;
|
||||
|
||||
} else if ((n = startswith(rvalue, "file:"))) {
|
||||
_cleanup_free_ char *resolved = NULL;
|
||||
|
||||
r = unit_full_printf(u, n, &resolved);
|
||||
if (r < 0)
|
||||
return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s: %m", n);
|
||||
|
||||
if (!path_is_absolute(resolved)) {
|
||||
log_syntax(unit, LOG_ERR, filename, line, 0, "file: requires an absolute path name: %s", resolved);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!path_is_normalized(resolved)) {
|
||||
log_syntax(unit, LOG_ERR, filename, line, 0, "file: requires a normalized path name: %s", resolved);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
free_and_replace(c->stdio_file[STDIN_FILENO], resolved);
|
||||
|
||||
ei = EXEC_INPUT_FILE;
|
||||
|
||||
} else {
|
||||
ei = exec_input_from_string(rvalue);
|
||||
if (ei < 0) {
|
||||
log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse input specifier, ignoring: %s", rvalue);
|
||||
return 0;
|
||||
}
|
||||
c->std_input = EXEC_INPUT_NAMED_FD;
|
||||
r = free_and_strdup(&c->stdio_fdname[STDIN_FILENO], name);
|
||||
if (r < 0)
|
||||
log_oom();
|
||||
return r;
|
||||
} else {
|
||||
ExecInput ei = exec_input_from_string(rvalue);
|
||||
if (ei == _EXEC_INPUT_INVALID)
|
||||
log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse input specifier, ignoring: %s", rvalue);
|
||||
else
|
||||
c->std_input = ei;
|
||||
return 0;
|
||||
}
|
||||
|
||||
c->std_input = ei;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_exec_output(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_exec_input_text(
|
||||
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_free_ char *unescaped = NULL, *resolved = NULL;
|
||||
ExecContext *c = data;
|
||||
Unit *u = userdata;
|
||||
size_t sz;
|
||||
void *p;
|
||||
int r;
|
||||
|
||||
assert(data);
|
||||
assert(filename);
|
||||
assert(line);
|
||||
assert(rvalue);
|
||||
|
||||
if (isempty(rvalue)) {
|
||||
/* Reset if the empty string is assigned */
|
||||
c->stdin_data = mfree(c->stdin_data);
|
||||
c->stdin_data_size = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = cunescape(rvalue, 0, &unescaped);
|
||||
if (r < 0)
|
||||
return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to decode C escaped text: %s", rvalue);
|
||||
|
||||
r = unit_full_printf(u, unescaped, &resolved);
|
||||
if (r < 0)
|
||||
return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers: %s", unescaped);
|
||||
|
||||
sz = strlen(resolved);
|
||||
if (c->stdin_data_size + sz + 1 < c->stdin_data_size || /* check for overflow */
|
||||
c->stdin_data_size + sz + 1 > EXEC_STDIN_DATA_MAX) {
|
||||
log_syntax(unit, LOG_ERR, filename, line, 0, "Standard input data too large (%zu), maximum of %zu permitted, ignoring.", c->stdin_data_size + sz, (size_t) EXEC_STDIN_DATA_MAX);
|
||||
return -E2BIG;
|
||||
}
|
||||
|
||||
p = realloc(c->stdin_data, c->stdin_data_size + sz + 1);
|
||||
if (!p)
|
||||
return log_oom();
|
||||
|
||||
*((char*) mempcpy((char*) p + c->stdin_data_size, resolved, sz)) = '\n';
|
||||
|
||||
c->stdin_data = p;
|
||||
c->stdin_data_size += sz + 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_exec_input_data(
|
||||
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_free_ char *cleaned = NULL;
|
||||
_cleanup_free_ void *p = NULL;
|
||||
ExecContext *c = data;
|
||||
size_t sz;
|
||||
void *q;
|
||||
int r;
|
||||
|
||||
assert(data);
|
||||
assert(filename);
|
||||
assert(line);
|
||||
assert(rvalue);
|
||||
|
||||
if (isempty(rvalue)) {
|
||||
/* Reset if the empty string is assigned */
|
||||
c->stdin_data = mfree(c->stdin_data);
|
||||
c->stdin_data_size = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Be tolerant to whitespace. Remove it all before decoding */
|
||||
cleaned = strdup(rvalue);
|
||||
if (!cleaned)
|
||||
return log_oom();
|
||||
delete_chars(cleaned, WHITESPACE);
|
||||
|
||||
r = unbase64mem(cleaned, (size_t) -1, &p, &sz);
|
||||
if (r < 0)
|
||||
return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to decode base64 data, ignoring: %s", cleaned);
|
||||
|
||||
assert(sz > 0);
|
||||
|
||||
if (c->stdin_data_size + sz < c->stdin_data_size || /* check for overflow */
|
||||
c->stdin_data_size + sz > EXEC_STDIN_DATA_MAX) {
|
||||
log_syntax(unit, LOG_ERR, filename, line, 0, "Standard input data too large (%zu), maximum of %zu permitted, ignoring.", c->stdin_data_size + sz, (size_t) EXEC_STDIN_DATA_MAX);
|
||||
return -E2BIG;
|
||||
}
|
||||
|
||||
q = realloc(c->stdin_data, c->stdin_data_size + sz);
|
||||
if (!q)
|
||||
return log_oom();
|
||||
|
||||
memcpy((uint8_t*) q + c->stdin_data_size, p, sz);
|
||||
|
||||
c->stdin_data = q;
|
||||
c->stdin_data_size += sz;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_exec_output(
|
||||
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_free_ char *resolved = NULL;
|
||||
const char *n;
|
||||
ExecContext *c = data;
|
||||
Unit *u = userdata;
|
||||
ExecOutput eo;
|
||||
const char *name;
|
||||
int r;
|
||||
|
||||
assert(data);
|
||||
@ -897,38 +1052,67 @@ int config_parse_exec_output(const char *unit,
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
|
||||
name = startswith(rvalue, "fd:");
|
||||
if (name) {
|
||||
/* Strip prefix and validate fd name */
|
||||
if (!fdname_is_valid(name)) {
|
||||
log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid file descriptor name, ignoring: %s", name);
|
||||
return 0;
|
||||
n = startswith(rvalue, "fd:");
|
||||
if (n) {
|
||||
r = unit_full_printf(u, n, &resolved);
|
||||
if (r < 0)
|
||||
return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s: %m", n);
|
||||
|
||||
if (isempty(resolved))
|
||||
resolved = mfree(resolved);
|
||||
else if (!fdname_is_valid(resolved)) {
|
||||
log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid file descriptor name: %s", resolved);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
eo = EXEC_OUTPUT_NAMED_FD;
|
||||
|
||||
} else if ((n = startswith(rvalue, "file:"))) {
|
||||
|
||||
r = unit_full_printf(u, n, &resolved);
|
||||
if (r < 0)
|
||||
return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s: %m", n);
|
||||
|
||||
if (!path_is_absolute(resolved)) {
|
||||
log_syntax(unit, LOG_ERR, filename, line, 0, "file: requires an absolute path name: %s", resolved);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!path_is_normalized(resolved)) {
|
||||
log_syntax(unit, LOG_ERR, filename, line, 0, "file: requires a normalized path name, ignoring: %s", resolved);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
eo = EXEC_OUTPUT_FILE;
|
||||
|
||||
} else {
|
||||
eo = exec_output_from_string(rvalue);
|
||||
if (eo == _EXEC_OUTPUT_INVALID) {
|
||||
if (eo < 0) {
|
||||
log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse output specifier, ignoring: %s", rvalue);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (streq(lvalue, "StandardOutput")) {
|
||||
if (eo == EXEC_OUTPUT_NAMED_FD)
|
||||
free_and_replace(c->stdio_fdname[STDOUT_FILENO], resolved);
|
||||
else
|
||||
free_and_replace(c->stdio_file[STDOUT_FILENO], resolved);
|
||||
|
||||
c->std_output = eo;
|
||||
r = free_and_strdup(&c->stdio_fdname[STDOUT_FILENO], name);
|
||||
if (r < 0)
|
||||
log_oom();
|
||||
return r;
|
||||
} else if (streq(lvalue, "StandardError")) {
|
||||
c->std_error = eo;
|
||||
r = free_and_strdup(&c->stdio_fdname[STDERR_FILENO], name);
|
||||
if (r < 0)
|
||||
log_oom();
|
||||
return r;
|
||||
|
||||
} else {
|
||||
log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse output property, ignoring: %s", lvalue);
|
||||
return 0;
|
||||
assert(streq(lvalue, "StandardError"));
|
||||
|
||||
if (eo == EXEC_OUTPUT_NAMED_FD)
|
||||
free_and_replace(c->stdio_fdname[STDERR_FILENO], resolved);
|
||||
else
|
||||
free_and_replace(c->stdio_file[STDERR_FILENO], resolved);
|
||||
|
||||
c->std_error = eo;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_exec_io_class(const char *unit,
|
||||
@ -3867,7 +4051,7 @@ int config_parse_exec_directories(
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!path_is_safe(k) || path_is_absolute(k)) {
|
||||
if (!path_is_normalized(k) || path_is_absolute(k)) {
|
||||
log_syntax(unit, LOG_ERR, filename, line, 0,
|
||||
"%s= path is not valid, ignoring assignment: %s", lvalue, rvalue);
|
||||
continue;
|
||||
|
@ -47,9 +47,9 @@ int config_parse_service_type(const char *unit, const char *filename, unsigned l
|
||||
int config_parse_service_restart(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_socket_bindtodevice(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_exec_output(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_output(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_exec_input(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_input(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_exec_input_text(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_exec_input_data(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_exec_io_class(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_exec_io_priority(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_exec_cpu_sched_policy(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);
|
||||
|
@ -572,6 +572,40 @@ static int config_parse_show_status(
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int config_parse_output_restricted(
|
||||
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) {
|
||||
|
||||
ExecOutput t, *eo = data;
|
||||
|
||||
assert(filename);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
assert(data);
|
||||
|
||||
t = exec_output_from_string(rvalue);
|
||||
if (t < 0) {
|
||||
log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse output type, ignoring: %s", rvalue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (IN_SET(t, EXEC_OUTPUT_SOCKET, EXEC_OUTPUT_NAMED_FD, EXEC_OUTPUT_FILE)) {
|
||||
log_syntax(unit, LOG_ERR, filename, line, 0, "Standard output types socket, fd:, file: are not supported as defaults, ignoring: %s", rvalue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
*eo = t;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int config_parse_crash_chvt(
|
||||
const char* unit,
|
||||
const char *filename,
|
||||
@ -723,8 +757,8 @@ static int parse_config_file(void) {
|
||||
#endif
|
||||
{ "Manager", "TimerSlackNSec", config_parse_nsec, 0, &arg_timer_slack_nsec },
|
||||
{ "Manager", "DefaultTimerAccuracySec", config_parse_sec, 0, &arg_default_timer_accuracy_usec },
|
||||
{ "Manager", "DefaultStandardOutput", config_parse_output, 0, &arg_default_std_output },
|
||||
{ "Manager", "DefaultStandardError", config_parse_output, 0, &arg_default_std_error },
|
||||
{ "Manager", "DefaultStandardOutput", config_parse_output_restricted,0, &arg_default_std_output },
|
||||
{ "Manager", "DefaultStandardError", config_parse_output_restricted,0, &arg_default_std_error },
|
||||
{ "Manager", "DefaultTimeoutStartSec", config_parse_sec, 0, &arg_default_timeout_start_usec },
|
||||
{ "Manager", "DefaultTimeoutStopSec", config_parse_sec, 0, &arg_default_timeout_stop_usec },
|
||||
{ "Manager", "DefaultRestartSec", config_parse_sec, 0, &arg_default_restart_usec },
|
||||
|
@ -592,10 +592,8 @@ static int service_add_default_dependencies(Service *s) {
|
||||
static void service_fix_output(Service *s) {
|
||||
assert(s);
|
||||
|
||||
/* If nothing has been explicitly configured, patch default
|
||||
* output in. If input is socket/tty we avoid this however,
|
||||
* since in that case we want output to default to the same
|
||||
* place as we read input from. */
|
||||
/* If nothing has been explicitly configured, patch default output in. If input is socket/tty we avoid this
|
||||
* however, since in that case we want output to default to the same place as we read input from. */
|
||||
|
||||
if (s->exec_context.std_error == EXEC_OUTPUT_INHERIT &&
|
||||
s->exec_context.std_output == EXEC_OUTPUT_INHERIT &&
|
||||
@ -605,6 +603,10 @@ static void service_fix_output(Service *s) {
|
||||
if (s->exec_context.std_output == EXEC_OUTPUT_INHERIT &&
|
||||
s->exec_context.std_input == EXEC_INPUT_NULL)
|
||||
s->exec_context.std_output = UNIT(s)->manager->default_std_output;
|
||||
|
||||
if (s->exec_context.std_input == EXEC_INPUT_NULL &&
|
||||
s->exec_context.stdin_data_size > 0)
|
||||
s->exec_context.std_input = EXEC_INPUT_DATA;
|
||||
}
|
||||
|
||||
static int service_setup_bus_name(Service *s) {
|
||||
|
@ -4461,7 +4461,7 @@ int unit_require_mounts_for(Unit *u, const char *path, UnitDependencyMask mask)
|
||||
|
||||
path_kill_slashes(p);
|
||||
|
||||
if (!path_is_safe(p)) {
|
||||
if (!path_is_normalized(p)) {
|
||||
free(p);
|
||||
return -EPERM;
|
||||
}
|
||||
|
@ -104,28 +104,24 @@ int import_fork_tar_x(const char *path, pid_t *ret) {
|
||||
|
||||
pipefd[1] = safe_close(pipefd[1]);
|
||||
|
||||
if (dup2(pipefd[0], STDIN_FILENO) != STDIN_FILENO) {
|
||||
log_error_errno(errno, "Failed to dup2() fd: %m");
|
||||
r = move_fd(pipefd[0], STDIN_FILENO, false);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to move fd: %m");
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (pipefd[0] != STDIN_FILENO)
|
||||
pipefd[0] = safe_close(pipefd[0]);
|
||||
|
||||
null_fd = open("/dev/null", O_WRONLY|O_NOCTTY);
|
||||
if (null_fd < 0) {
|
||||
log_error_errno(errno, "Failed to open /dev/null: %m");
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (dup2(null_fd, STDOUT_FILENO) != STDOUT_FILENO) {
|
||||
log_error_errno(errno, "Failed to dup2() fd: %m");
|
||||
r = move_fd(null_fd, STDOUT_FILENO, false);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to move fd: %m");
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (null_fd != STDOUT_FILENO)
|
||||
null_fd = safe_close(null_fd);
|
||||
|
||||
stdio_unset_cloexec();
|
||||
|
||||
if (unshare(CLONE_NEWNET) < 0)
|
||||
@ -176,28 +172,24 @@ int import_fork_tar_c(const char *path, pid_t *ret) {
|
||||
|
||||
pipefd[0] = safe_close(pipefd[0]);
|
||||
|
||||
if (dup2(pipefd[1], STDOUT_FILENO) != STDOUT_FILENO) {
|
||||
log_error_errno(errno, "Failed to dup2() fd: %m");
|
||||
r = move_fd(pipefd[1], STDOUT_FILENO, false);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to move fd: %m");
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (pipefd[1] != STDOUT_FILENO)
|
||||
pipefd[1] = safe_close(pipefd[1]);
|
||||
|
||||
null_fd = open("/dev/null", O_RDONLY|O_NOCTTY);
|
||||
if (null_fd < 0) {
|
||||
log_error_errno(errno, "Failed to open /dev/null: %m");
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (dup2(null_fd, STDIN_FILENO) != STDIN_FILENO) {
|
||||
log_error_errno(errno, "Failed to dup2() fd: %m");
|
||||
r = move_fd(null_fd, STDIN_FILENO, false);
|
||||
if (r < 0) {
|
||||
log_error_errno(errno, "Failed to move fd: %m");
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (null_fd != STDIN_FILENO)
|
||||
null_fd = safe_close(null_fd);
|
||||
|
||||
stdio_unset_cloexec();
|
||||
|
||||
if (unshare(CLONE_NEWNET) < 0)
|
||||
|
@ -493,28 +493,24 @@ int pull_verify(PullJob *main_job,
|
||||
|
||||
gpg_pipe[1] = safe_close(gpg_pipe[1]);
|
||||
|
||||
if (dup2(gpg_pipe[0], STDIN_FILENO) != STDIN_FILENO) {
|
||||
log_error_errno(errno, "Failed to dup2() fd: %m");
|
||||
r = move_fd(gpg_pipe[0], STDIN_FILENO, false);
|
||||
if (r < 0) {
|
||||
log_error_errno(errno, "Failed to move fd: %m");
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (gpg_pipe[0] != STDIN_FILENO)
|
||||
gpg_pipe[0] = safe_close(gpg_pipe[0]);
|
||||
|
||||
null_fd = open("/dev/null", O_WRONLY|O_NOCTTY);
|
||||
if (null_fd < 0) {
|
||||
log_error_errno(errno, "Failed to open /dev/null: %m");
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (dup2(null_fd, STDOUT_FILENO) != STDOUT_FILENO) {
|
||||
log_error_errno(errno, "Failed to dup2() fd: %m");
|
||||
r = move_fd(null_fd, STDOUT_FILENO, false);
|
||||
if (r < 0) {
|
||||
log_error_errno(errno, "Failed to move fd: %m");
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (null_fd != STDOUT_FILENO)
|
||||
null_fd = safe_close(null_fd);
|
||||
|
||||
cmd[k++] = strjoina("--homedir=", gpg_home);
|
||||
|
||||
/* We add the user keyring only to the command line
|
||||
|
@ -857,12 +857,12 @@ int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bu
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!path_is_absolute(src) || !path_is_safe(src))
|
||||
if (!path_is_absolute(src) || !path_is_normalized(src))
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Source path must be absolute and not contain ../.");
|
||||
|
||||
if (isempty(dest))
|
||||
dest = src;
|
||||
else if (!path_is_absolute(dest) || !path_is_safe(dest))
|
||||
else if (!path_is_absolute(dest) || !path_is_normalized(dest))
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Destination path must be absolute and not contain ../.");
|
||||
|
||||
r = bus_verify_polkit_async(
|
||||
|
@ -887,8 +887,8 @@ static int stop_mounts(
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!path_is_safe(where)) {
|
||||
log_error("Path contains unsafe components: %s", where);
|
||||
if (!path_is_normalized(where)) {
|
||||
log_error("Path contains non-normalized components: %s", where);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -1569,8 +1569,8 @@ int main(int argc, char* argv[]) {
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (!path_is_safe(arg_mount_what)) {
|
||||
log_error("Path contains unsafe components: %s", arg_mount_what);
|
||||
if (!path_is_normalized(arg_mount_what)) {
|
||||
log_error("Path contains non-normalized components: %s", arg_mount_what);
|
||||
r = -EINVAL;
|
||||
goto finish;
|
||||
}
|
||||
@ -1593,8 +1593,8 @@ int main(int argc, char* argv[]) {
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (!path_is_safe(arg_mount_where)) {
|
||||
log_error("Path contains unsafe components: %s", arg_mount_where);
|
||||
if (!path_is_normalized(arg_mount_where)) {
|
||||
log_error("Path contains non-normalized components: %s", arg_mount_where);
|
||||
r = -EINVAL;
|
||||
goto finish;
|
||||
}
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include "errno-list.h"
|
||||
#include "escape.h"
|
||||
#include "hashmap.h"
|
||||
#include "hexdecoct.h"
|
||||
#include "hostname-util.h"
|
||||
#include "in-addr-util.h"
|
||||
#include "list.h"
|
||||
@ -274,6 +275,50 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
|
||||
|
||||
r = sd_bus_message_append(m, "sv", "TasksMax", "t", t);
|
||||
goto finish;
|
||||
|
||||
} else if (STR_IN_SET(field, "StandardInput", "StandardOutput", "StandardError")) {
|
||||
const char *n, *appended;
|
||||
|
||||
n = startswith(eq, "fd:");
|
||||
if (n) {
|
||||
appended = strjoina(field, "FileDescriptorName");
|
||||
r = sd_bus_message_append(m, "sv", appended, "s", n);
|
||||
|
||||
} else if ((n = startswith(eq, "file:"))) {
|
||||
appended = strjoina(field, "File");
|
||||
r = sd_bus_message_append(m, "sv", appended, "s", n);
|
||||
} else
|
||||
r = sd_bus_message_append(m, "sv", field, "s", eq);
|
||||
|
||||
goto finish;
|
||||
|
||||
} else if (streq(field, "StandardInputText")) {
|
||||
_cleanup_free_ char *unescaped = NULL;
|
||||
|
||||
r = cunescape(eq, 0, &unescaped);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to unescape text '%s': %m", eq);
|
||||
|
||||
if (!strextend(&unescaped, "\n", NULL))
|
||||
return log_oom();
|
||||
|
||||
/* Note that we don't expand specifiers here, but that should be OK, as this is a programmatic
|
||||
* interface anyway */
|
||||
|
||||
r = sd_bus_message_append(m, "s", "StandardInputData");
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
r = sd_bus_message_open_container(m, 'v', "ay");
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
r = sd_bus_message_append_array(m, 'y', unescaped, strlen(unescaped));
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
r = sd_bus_message_close_container(m);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
|
||||
@ -324,10 +369,8 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
|
||||
uint64_t u;
|
||||
|
||||
r = cg_weight_parse(eq, &u);
|
||||
if (r < 0) {
|
||||
log_error("Failed to parse %s value %s.", field, eq);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq);
|
||||
|
||||
r = sd_bus_message_append(m, "v", "t", u);
|
||||
|
||||
@ -335,10 +378,8 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
|
||||
uint64_t u;
|
||||
|
||||
r = cg_cpu_shares_parse(eq, &u);
|
||||
if (r < 0) {
|
||||
log_error("Failed to parse %s value %s.", field, eq);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq);
|
||||
|
||||
r = sd_bus_message_append(m, "v", "t", u);
|
||||
|
||||
@ -346,10 +387,8 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
|
||||
uint64_t u;
|
||||
|
||||
r = cg_weight_parse(eq, &u);
|
||||
if (r < 0) {
|
||||
log_error("Failed to parse %s value %s.", field, eq);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq);
|
||||
|
||||
r = sd_bus_message_append(m, "v", "t", u);
|
||||
|
||||
@ -357,17 +396,14 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
|
||||
uint64_t u;
|
||||
|
||||
r = cg_blkio_weight_parse(eq, &u);
|
||||
if (r < 0) {
|
||||
log_error("Failed to parse %s value %s.", field, eq);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq);
|
||||
|
||||
r = sd_bus_message_append(m, "v", "t", u);
|
||||
|
||||
} else if (STR_IN_SET(field,
|
||||
"User", "Group", "DevicePolicy", "KillMode",
|
||||
"UtmpIdentifier", "UtmpMode", "PAMName", "TTYPath",
|
||||
"StandardInput", "StandardOutput", "StandardError",
|
||||
"Description", "Slice", "Type", "WorkingDirectory",
|
||||
"RootDirectory", "SyslogIdentifier", "ProtectSystem",
|
||||
"ProtectHome", "SELinuxContext", "Restart", "RootImage",
|
||||
@ -375,7 +411,32 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
|
||||
"KeyringMode", "CollectMode"))
|
||||
r = sd_bus_message_append(m, "v", "s", eq);
|
||||
|
||||
else if (STR_IN_SET(field, "AppArmorProfile", "SmackProcessLabel")) {
|
||||
else if (streq(field, "StandardInputData")) {
|
||||
_cleanup_free_ char *cleaned = NULL;
|
||||
_cleanup_free_ void *decoded = NULL;
|
||||
size_t sz;
|
||||
|
||||
cleaned = strdup(eq);
|
||||
if (!cleaned)
|
||||
return log_oom();
|
||||
|
||||
delete_chars(cleaned, WHITESPACE);
|
||||
|
||||
r = unbase64mem(cleaned, (size_t) -1, &decoded, &sz);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to decode base64 data '%s': %m", cleaned);
|
||||
|
||||
r = sd_bus_message_open_container(m, 'v', "ay");
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
r = sd_bus_message_append_array(m, 'y', decoded, sz);
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
r = sd_bus_message_close_container(m);
|
||||
|
||||
} else if (STR_IN_SET(field, "AppArmorProfile", "SmackProcessLabel")) {
|
||||
bool ignore;
|
||||
const char *s;
|
||||
|
||||
@ -414,10 +475,8 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
|
||||
} else if (streq(field, "SecureBits")) {
|
||||
|
||||
r = secure_bits_from_string(eq);
|
||||
if (r < 0) {
|
||||
log_error("Failed to parse %s value %s.", field, eq);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq);
|
||||
|
||||
r = sd_bus_message_append(m, "v", "i", r);
|
||||
|
||||
@ -433,10 +492,8 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
|
||||
}
|
||||
|
||||
r = capability_set_from_string(p, &sum);
|
||||
if (r < 0) {
|
||||
log_error("Failed to parse %s value %s.", field, eq);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq);
|
||||
|
||||
sum = invert ? ~sum : sum;
|
||||
|
||||
@ -492,10 +549,8 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
|
||||
bytes = CGROUP_LIMIT_MAX;
|
||||
} else {
|
||||
r = parse_size(bandwidth, 1000, &bytes);
|
||||
if (r < 0) {
|
||||
log_error("Failed to parse byte value %s.", bandwidth);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse byte value %s: %m", bandwidth);
|
||||
}
|
||||
|
||||
r = sd_bus_message_append(m, "v", "a(st)", 1, path, bytes);
|
||||
@ -524,10 +579,9 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
|
||||
}
|
||||
|
||||
r = safe_atou64(weight, &u);
|
||||
if (r < 0) {
|
||||
log_error("Failed to parse %s value %s.", field, weight);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse %s value %s: %m", field, weight);
|
||||
|
||||
r = sd_bus_message_append(m, "v", "a(st)", 1, path, u);
|
||||
}
|
||||
|
||||
@ -669,8 +723,6 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
|
||||
|
||||
r = sd_bus_message_append(m, "v", "i", (int32_t) n);
|
||||
|
||||
#if HAVE_SECCOMP
|
||||
|
||||
} else if (streq(field, "SystemCallFilter")) {
|
||||
int whitelist;
|
||||
_cleanup_strv_free_ char **l = NULL;
|
||||
@ -815,7 +867,7 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
|
||||
r = sd_bus_message_close_container(m);
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
#endif
|
||||
|
||||
} else if (streq(field, "FileDescriptorStoreMax")) {
|
||||
unsigned u;
|
||||
|
||||
@ -858,10 +910,8 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
|
||||
_cleanup_free_ char *word = NULL;
|
||||
|
||||
r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE);
|
||||
if (r < 0) {
|
||||
log_error("Failed to parse Environment value %s", eq);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse Environment value %s: %m", eq);
|
||||
if (r == 0)
|
||||
break;
|
||||
|
||||
@ -908,20 +958,16 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
|
||||
nsec_t n;
|
||||
|
||||
r = parse_nsec(eq, &n);
|
||||
if (r < 0) {
|
||||
log_error("Failed to parse %s value %s", field, eq);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq);
|
||||
|
||||
r = sd_bus_message_append(m, "v", "t", n);
|
||||
} else if (streq(field, "OOMScoreAdjust")) {
|
||||
int oa;
|
||||
|
||||
r = safe_atoi(eq, &oa);
|
||||
if (r < 0) {
|
||||
log_error("Failed to parse %s value %s", field, eq);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq);
|
||||
|
||||
if (!oom_score_adjust_is_valid(oa)) {
|
||||
log_error("OOM score adjust value out of range");
|
||||
@ -946,10 +992,8 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
|
||||
size_t offset;
|
||||
|
||||
r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
|
||||
if (r < 0) {
|
||||
log_error("Failed to parse %s value %s", field, eq);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq);
|
||||
if (r == 0)
|
||||
break;
|
||||
|
||||
@ -994,10 +1038,8 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
|
||||
_cleanup_free_ char *word = NULL;
|
||||
|
||||
r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
|
||||
if (r < 0) {
|
||||
log_error("Failed to parse %s value %s", field, eq);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq);
|
||||
if (r == 0)
|
||||
break;
|
||||
|
||||
|
@ -56,6 +56,7 @@
|
||||
#include "fs-util.h"
|
||||
#include "glob-util.h"
|
||||
#include "hostname-util.h"
|
||||
#include "hexdecoct.h"
|
||||
#include "initreq.h"
|
||||
#include "install.h"
|
||||
#include "io-util.h"
|
||||
@ -4978,6 +4979,24 @@ static int print_property(const char *name, sd_bus_message *m, const char *conte
|
||||
return bus_log_parse_error(r);
|
||||
|
||||
return 0;
|
||||
|
||||
} else if (contents[1] == SD_BUS_TYPE_BYTE && streq(name, "StandardInputData")) {
|
||||
_cleanup_free_ char *h = NULL;
|
||||
const void *p;
|
||||
size_t sz;
|
||||
ssize_t n;
|
||||
|
||||
r = sd_bus_message_read_array(m, 'y', &p, &sz);
|
||||
if (r < 0)
|
||||
return bus_log_parse_error(r);
|
||||
|
||||
n = base64mem(p, sz, &h);
|
||||
if (n < 0)
|
||||
return log_oom();
|
||||
|
||||
print_prop(name, "%s", h);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
break;
|
||||
|
@ -479,6 +479,14 @@ static void test_exec_specifier(Manager *m) {
|
||||
test(m, "exec-specifier.service", 0, CLD_EXITED);
|
||||
}
|
||||
|
||||
static void test_exec_stdin_data(Manager *m) {
|
||||
test(m, "exec-stdin-data.service", 0, CLD_EXITED);
|
||||
}
|
||||
|
||||
static void test_exec_stdio_file(Manager *m) {
|
||||
test(m, "exec-stdio-file.service", 0, CLD_EXITED);
|
||||
}
|
||||
|
||||
static int run_tests(UnitFileScope scope, const test_function_t *tests) {
|
||||
const test_function_t *test = NULL;
|
||||
Manager *m = NULL;
|
||||
@ -535,6 +543,8 @@ int main(int argc, char *argv[]) {
|
||||
test_exec_spec_interpolation,
|
||||
test_exec_read_only_path_suceed,
|
||||
test_exec_unset_environment,
|
||||
test_exec_stdin_data,
|
||||
test_exec_stdio_file,
|
||||
NULL,
|
||||
};
|
||||
static const test_function_t system_tests[] = {
|
||||
|
@ -25,6 +25,9 @@
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "macro.h"
|
||||
#include "random-util.h"
|
||||
#include "string-util.h"
|
||||
#include "util.h"
|
||||
|
||||
static void test_close_many(void) {
|
||||
int fds[3];
|
||||
@ -104,11 +107,60 @@ static void test_open_serialization_fd(void) {
|
||||
write(fd, "test\n", 5);
|
||||
}
|
||||
|
||||
static void test_acquire_data_fd_one(unsigned flags) {
|
||||
char wbuffer[196*1024 - 7];
|
||||
char rbuffer[sizeof(wbuffer)];
|
||||
int fd;
|
||||
|
||||
fd = acquire_data_fd("foo", 3, flags);
|
||||
assert_se(fd >= 0);
|
||||
|
||||
zero(rbuffer);
|
||||
assert_se(read(fd, rbuffer, sizeof(rbuffer)) == 3);
|
||||
assert_se(streq(rbuffer, "foo"));
|
||||
|
||||
fd = safe_close(fd);
|
||||
|
||||
fd = acquire_data_fd("", 0, flags);
|
||||
assert_se(fd >= 0);
|
||||
|
||||
zero(rbuffer);
|
||||
assert_se(read(fd, rbuffer, sizeof(rbuffer)) == 0);
|
||||
assert_se(streq(rbuffer, ""));
|
||||
|
||||
fd = safe_close(fd);
|
||||
|
||||
random_bytes(wbuffer, sizeof(wbuffer));
|
||||
|
||||
fd = acquire_data_fd(wbuffer, sizeof(wbuffer), flags);
|
||||
assert_se(fd >= 0);
|
||||
|
||||
zero(rbuffer);
|
||||
assert_se(read(fd, rbuffer, sizeof(rbuffer)) == sizeof(rbuffer));
|
||||
assert_se(memcmp(rbuffer, wbuffer, sizeof(rbuffer)) == 0);
|
||||
|
||||
fd = safe_close(fd);
|
||||
}
|
||||
|
||||
static void test_acquire_data_fd(void) {
|
||||
|
||||
test_acquire_data_fd_one(0);
|
||||
test_acquire_data_fd_one(ACQUIRE_NO_DEV_NULL);
|
||||
test_acquire_data_fd_one(ACQUIRE_NO_MEMFD);
|
||||
test_acquire_data_fd_one(ACQUIRE_NO_DEV_NULL|ACQUIRE_NO_MEMFD);
|
||||
test_acquire_data_fd_one(ACQUIRE_NO_PIPE);
|
||||
test_acquire_data_fd_one(ACQUIRE_NO_DEV_NULL|ACQUIRE_NO_PIPE);
|
||||
test_acquire_data_fd_one(ACQUIRE_NO_MEMFD|ACQUIRE_NO_PIPE);
|
||||
test_acquire_data_fd_one(ACQUIRE_NO_DEV_NULL|ACQUIRE_NO_MEMFD|ACQUIRE_NO_PIPE);
|
||||
test_acquire_data_fd_one(ACQUIRE_NO_DEV_NULL|ACQUIRE_NO_MEMFD|ACQUIRE_NO_PIPE|ACQUIRE_NO_TMPFILE);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
test_close_many();
|
||||
test_close_nointr();
|
||||
test_same_fd();
|
||||
test_open_serialization_fd();
|
||||
test_acquire_data_fd();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -111,6 +111,8 @@ test_data_files = '''
|
||||
test-execute/exec-runtimedirectory.service
|
||||
test-execute/exec-spec-interpolation.service
|
||||
test-execute/exec-specifier.service
|
||||
test-execute/exec-stdin-data.service
|
||||
test-execute/exec-stdio-file.service
|
||||
test-execute/exec-supplementarygroups-multiple-groups-default-group-user.service
|
||||
test-execute/exec-supplementarygroups-multiple-groups-withgid.service
|
||||
test-execute/exec-supplementarygroups-multiple-groups-withuid.service
|
||||
|
19
test/test-execute/exec-stdin-data.service
Normal file
19
test/test-execute/exec-stdin-data.service
Normal file
@ -0,0 +1,19 @@
|
||||
[Unit]
|
||||
Description=Test for StandardInputText= and StandardInputData=
|
||||
|
||||
[Service]
|
||||
ExecStart=/bin/sh -x -c 'd=$$(mktemp -d -p /tmp); echo -e "this is a test\nand this is more\nsomething encoded!\nsomething in multiple lines\nand some more\nand a more bas64 data\nsomething with strange\nembedded\tcharacters\nand something with a exec-stdin-data.service specifier" > $d/text ; cmp $d/text ; rm -rf $d'
|
||||
Type=oneshot
|
||||
StandardInput=data
|
||||
StandardInputText=this is a test
|
||||
StandardInputText=and this is more
|
||||
StandardInputData=c29tZXRoaW5nIGVuY29kZWQhCg==
|
||||
StandardInputText=something \
|
||||
in multiple lines
|
||||
StandardInputText=\
|
||||
and some more
|
||||
StandardInputData=YW5kIGEgbW9y \
|
||||
ZSBiYXM2NCBk\
|
||||
YXRhCg==
|
||||
StandardInputText=something with strange\nembedded\tcharacters
|
||||
StandardInputText=and something with a %n specifier
|
7
test/test-execute/exec-stdio-file.service
Normal file
7
test/test-execute/exec-stdio-file.service
Normal file
@ -0,0 +1,7 @@
|
||||
[Unit]
|
||||
Description=Test for StandardInput=file:
|
||||
|
||||
[Service]
|
||||
ExecStart=/usr/bin/cmp /usr/bin/cmp
|
||||
Type=oneshot
|
||||
StandardInput=file:/usr/bin/cmp
|
Loading…
Reference in New Issue
Block a user