summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--TODO10
-rw-r--r--configure.ac22
-rw-r--r--hwdb/70-pointingstick.hwdb3
-rw-r--r--man/journalctl.xml41
-rw-r--r--man/nss-systemd.xml4
-rw-r--r--man/systemctl.xml4
-rw-r--r--man/systemd-journal-gatewayd.service.xml10
-rw-r--r--man/systemd-nspawn.xml27
-rw-r--r--man/systemd.exec.xml37
-rw-r--r--man/systemd.netdev.xml9
-rw-r--r--man/systemd.network.xml11
-rw-r--r--man/systemd.service.xml8
-rw-r--r--man/systemd.time.xml150
-rw-r--r--shell-completion/bash/journalctl2
-rw-r--r--shell-completion/bash/systemctl.in2
-rw-r--r--shell-completion/zsh/_sd_outputmodes2
-rw-r--r--src/basic/calendarspec.c43
-rw-r--r--src/basic/calendarspec.h1
-rw-r--r--src/basic/fileio.c42
-rw-r--r--src/basic/fs-util.c98
-rw-r--r--src/basic/fs-util.h3
-rw-r--r--src/basic/parse-util.c27
-rw-r--r--src/basic/parse-util.h3
-rw-r--r--src/basic/process-util.h5
-rw-r--r--src/basic/set.h8
-rw-r--r--src/basic/terminal-util.c2
-rw-r--r--src/basic/time-util.c146
-rw-r--r--src/basic/time-util.h4
-rw-r--r--src/core/automount.c5
-rw-r--r--src/core/busname.c6
-rw-r--r--src/core/dbus-automount.c54
-rw-r--r--src/core/dbus-automount.h2
-rw-r--r--src/core/dbus-execute.c2
-rw-r--r--src/core/dbus-mount.c3
-rw-r--r--src/core/execute.c100
-rw-r--r--src/core/execute.h19
-rw-r--r--src/core/load-fragment.c34
-rw-r--r--src/core/main.c3
-rw-r--r--src/core/manager.c1
-rw-r--r--src/core/mount.c54
-rw-r--r--src/core/path.c2
-rw-r--r--src/core/scope.c4
-rw-r--r--src/core/service.c157
-rw-r--r--src/core/socket.c346
-rw-r--r--src/core/socket.h10
-rw-r--r--src/core/swap.c20
-rw-r--r--src/core/timer.c2
-rw-r--r--src/coredump/coredumpctl.c9
-rw-r--r--src/journal-remote/journal-gatewayd.c29
-rw-r--r--src/journal-remote/journal-remote.c4
-rw-r--r--src/journal/journal-verify.c4
-rw-r--r--src/journal/journalctl.c6
-rw-r--r--src/journal/lookup3.c6
-rw-r--r--src/machine/machined-dbus.c2
-rw-r--r--src/network/networkd-brvlan.c20
-rw-r--r--src/network/networkd-brvlan.h1
-rw-r--r--src/network/networkd-link.c103
-rw-r--r--src/network/networkd-netdev-bridge.c7
-rw-r--r--src/network/networkd-netdev-bridge.h1
-rw-r--r--src/network/networkd-netdev-gperf.gperf1
-rw-r--r--src/network/networkd-network-gperf.gperf3
-rw-r--r--src/network/networkd-network.c1
-rw-r--r--src/network/networkd-network.h2
-rw-r--r--src/nspawn/nspawn.c61
-rw-r--r--src/nss-systemd/nss-systemd.c101
-rw-r--r--src/resolve/resolve-tool.c2
-rw-r--r--src/run/run.c8
-rw-r--r--src/shared/bus-unit-util.c14
-rw-r--r--src/shared/logs-show.c183
-rw-r--r--src/shared/output-mode.c1
-rw-r--r--src/shared/output-mode.h1
-rw-r--r--src/systemctl/systemctl.c6
-rw-r--r--src/test/test-calendarspec.c23
-rw-r--r--src/test/test-fs-util.c54
-rw-r--r--src/test/test-id128.c2
-rw-r--r--src/test/test-parse-util.c35
-rw-r--r--src/test/test-sigbus.c3
-rw-r--r--src/test/test-time.c44
78 files changed, 1536 insertions, 749 deletions
diff --git a/TODO b/TODO
index 0199d9d509..33afe39783 100644
--- a/TODO
+++ b/TODO
@@ -35,27 +35,17 @@ Features:
* RemoveIPC= in unit files for removing POSIX/SysV IPC objects
-* Set SERVICE_RESULT= as env var while running ExecStop=
-
* Introduce ProtectSystem=strict for making the entire OS hierarchy read-only
except for a select few
* nspawn: start UID allocation loop from hash of container name
-* in the DynamicUser=1 nss module, also map "nobody" and "root" statically
-
-* pid1: log about all processes we kill with with SIGKILL or in abandoned scopes, as this should normally not happen
-
* nspawn: support that /proc, /sys/, /dev are pre-mounted
-* nspawn: mount esp, so that bootctl can work
-
* define gpt header bits to select volatility mode
* nspawn: mount loopback filesystems with "discard"
-* Make TasksMax= take percentages, taken relative to the pids_max sysctl and pids.max cgroup limit
-
* ProtectKernelLogs= (drops CAP_SYSLOG, add seccomp for syslog() syscall, and DeviceAllow to /dev/kmsg) in service files
* ProtectClock= (drops CAP_SYS_TIMES, adds seecomp filters for settimeofday, adjtimex), sets DeviceAllow o /dev/rtc
diff --git a/configure.ac b/configure.ac
index a86deca471..4d1c96606f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -556,12 +556,30 @@ AC_SUBST(CERTIFICATEROOT)
AC_ARG_WITH([support-url],
AS_HELP_STRING([--with-support-url=URL],
- [Specify the supoport URL to show in catalog entries included in systemd]),
+ [Specify the support URL to show in catalog entries included in systemd]),
[SUPPORT_URL="$withval"],
[SUPPORT_URL=http://lists.freedesktop.org/mailman/listinfo/systemd-devel])
AC_SUBST(SUPPORT_URL)
+AC_ARG_WITH([nobody-user],
+ AS_HELP_STRING([--with-nobody-user=NAME],
+ [Specify the name of the nobody user (the one with UID 65534)]),
+ [NOBODY_USER_NAME="$withval"],
+ [NOBODY_USER_NAME=nobody])
+
+AC_SUBST(NOBODY_USER_NAME)
+AC_DEFINE_UNQUOTED(NOBODY_USER_NAME, ["$NOBODY_USER_NAME"], [The name of the nobody user (the one with UID 65534)])
+
+AC_ARG_WITH([nobody-group],
+ AS_HELP_STRING([--with-nobody-group=NAME],
+ [Specify the name of the nobody group (the one with GID 65534)]),
+ [NOBODY_GROUP_NAME="$withval"],
+ [NOBODY_GROUP_NAME=nobody])
+
+AC_SUBST(NOBODY_GROUP_NAME)
+AC_DEFINE_UNQUOTED(NOBODY_GROUP_NAME, ["$NOBODY_GROUP_NAME"], [The name of the nobody group (the one with GID 65534)])
+
# ------------------------------------------------------------------------------
have_xz=no
AC_ARG_ENABLE(xz, AS_HELP_STRING([--disable-xz], [Disable optional XZ support]))
@@ -1677,6 +1695,8 @@ AC_MSG_RESULT([
Maximum System GID: ${SYSTEM_GID_MAX}
Certificate root: ${CERTIFICATEROOT}
Support URL: ${SUPPORT_URL}
+ Nobody User Name: ${NOBODY_USER_NAME}
+ Nobody Group Name: ${NOBODY_GROUP_NAME}
CFLAGS: ${OUR_CFLAGS} ${CFLAGS}
CPPFLAGS: ${OUR_CPPFLAGS} ${CPPFLAGS}
diff --git a/hwdb/70-pointingstick.hwdb b/hwdb/70-pointingstick.hwdb
index ff52f11e45..c0ec8ffbe0 100644
--- a/hwdb/70-pointingstick.hwdb
+++ b/hwdb/70-pointingstick.hwdb
@@ -96,10 +96,13 @@ evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadX230
# Lenovo Thinkpad X230 tablet
evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadX230Tablet:*
# Lenovo Thinkpad *40 series
+evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPad??40:*
evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPad??40?:*
# Lenovo Thinkpad *50 series
+evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPad??50:*
evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPad??50?:*
# Lenovo Thinkpad *60 series
+evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPad??60:*
evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPad??60?:*
# Lenovo Thinkpad X1 Carbon 3rd gen
evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadX1Carbon3rd:*
diff --git a/man/journalctl.xml b/man/journalctl.xml
index e77621d7b3..c448a29a51 100644
--- a/man/journalctl.xml
+++ b/man/journalctl.xml
@@ -250,6 +250,18 @@
<varlistentry>
<term>
+ <option>short-full</option>
+ </term>
+ <listitem>
+ <para>is very similar, but shows timestamps in the format the <option>--since=</option> and
+ <option>--until=</option> options accept. Unlike the timestamp information shown in
+ <option>short</option> output mode this mode includes weekday, year and timezone information in the
+ output, and is locale-independent.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
<option>short-iso</option>
</term>
<listitem>
@@ -572,24 +584,17 @@
<term><option>-U</option></term>
<term><option>--until=</option></term>
- <listitem><para>Start showing entries on or newer than the
- specified date, or on or older than the specified date,
- respectively. Date specifications should be of the format
- <literal>2012-10-30 18:17:16</literal>. If the time part is
- omitted, <literal>00:00:00</literal> is assumed. If only the
- seconds component is omitted, <literal>:00</literal> is
- assumed. If the date component is omitted, the current day is
- assumed. Alternatively the strings
- <literal>yesterday</literal>, <literal>today</literal>,
- <literal>tomorrow</literal> are understood, which refer to
- 00:00:00 of the day before the current day, the current day,
- or the day after the current day,
- respectively. <literal>now</literal> refers to the current
- time. Finally, relative times may be specified, prefixed with
- <literal>-</literal> or <literal>+</literal>, referring to
- times before or after the current time, respectively. For complete
- time and date specification, see
- <citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>7</manvolnum></citerefentry>.
+ <listitem><para>Start showing entries on or newer than the specified date, or on or older than the specified
+ date, respectively. Date specifications should be of the format <literal>2012-10-30 18:17:16</literal>. If the
+ time part is omitted, <literal>00:00:00</literal> is assumed. If only the seconds component is omitted,
+ <literal>:00</literal> is assumed. If the date component is omitted, the current day is assumed. Alternatively
+ the strings <literal>yesterday</literal>, <literal>today</literal>, <literal>tomorrow</literal> are understood,
+ which refer to 00:00:00 of the day before the current day, the current day, or the day after the current day,
+ respectively. <literal>now</literal> refers to the current time. Finally, relative times may be specified,
+ prefixed with <literal>-</literal> or <literal>+</literal>, referring to times before or after the current
+ time, respectively. For complete time and date specification, see
+ <citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>7</manvolnum></citerefentry>. Note that
+ <option>--output=short-full</option> prints timestamps that follow precisely this format.
</para>
</listitem>
</varlistentry>
diff --git a/man/nss-systemd.xml b/man/nss-systemd.xml
index 4228372e51..56d26e7d1f 100644
--- a/man/nss-systemd.xml
+++ b/man/nss-systemd.xml
@@ -61,6 +61,10 @@
<citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry> for details on
this option.</para>
+ <para>This module also ensures that the root and nobody users and groups (i.e. the users/groups with the UIDs/GIDs
+ 0 and 65534) remain resolvable at all times, even if they aren't listed in <filename>/etc/passwd</filename> or
+ <filename>/etc/group</filename>, or if these files are missing.</para>
+
<para>To activate the NSS module, add <literal>systemd</literal> to the lines starting with
<literal>passwd:</literal> and <literal>group:</literal> in <filename>/etc/nsswitch.conf</filename>.</para>
diff --git a/man/systemctl.xml b/man/systemctl.xml
index e7880d24f7..0ad0ad6d7e 100644
--- a/man/systemctl.xml
+++ b/man/systemctl.xml
@@ -156,6 +156,10 @@
<para>To list all units installed in the file system, use the
<command>list-unit-files</command> command instead.</para>
+
+ <para>When listing units with <command>list-dependencies</command>, recursively show
+ dependencies of all dependent units (by default only dependencies of target units are
+ shown).</para>
</listitem>
</varlistentry>
diff --git a/man/systemd-journal-gatewayd.service.xml b/man/systemd-journal-gatewayd.service.xml
index 9ed85c3950..2cb114f6e3 100644
--- a/man/systemd-journal-gatewayd.service.xml
+++ b/man/systemd-journal-gatewayd.service.xml
@@ -100,6 +100,16 @@
with <option>--cert=</option>.</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>-D <replaceable>DIR</replaceable></option></term>
+ <term><option>--directory=<replaceable>DIR</replaceable></option></term>
+
+ <listitem><para>Takes a directory path as argument. If
+ specified, <command>systemd-journal-gatewayd</command> will serve the
+ specified journal directory <replaceable>DIR</replaceable> instead of
+ the default runtime and system journal paths.</para></listitem>
+ </varlistentry>
+
<xi:include href="standard-options.xml" xpointer="help" />
<xi:include href="standard-options.xml" xpointer="version" />
</variablelist>
diff --git a/man/systemd-nspawn.xml b/man/systemd-nspawn.xml
index 9b623c8353..97b348b565 100644
--- a/man/systemd-nspawn.xml
+++ b/man/systemd-nspawn.xml
@@ -274,8 +274,7 @@
signals. It is recommended to use this mode to invoke arbitrary commands in containers, unless they have been
modified to run correctly as PID 1. Or in other words: this switch should be used for pretty much all commands,
except when the command refers to an init or shell implementation, as these are generally capable of running
- correctly as PID 1. This option may not be combined with <option>--boot</option> or
- <option>--share-system</option>.</para>
+ correctly as PID 1. This option may not be combined with <option>--boot</option>.</para>
</listitem>
</varlistentry>
@@ -285,8 +284,7 @@
<listitem><para>Automatically search for an init binary and invoke it as PID 1, instead of a shell or a user
supplied program. If this option is used, arguments specified on the command line are used as arguments for the
- init binary. This option may not be combined with <option>--as-pid2</option> or
- <option>--share-system</option>.</para>
+ init binary. This option may not be combined with <option>--as-pid2</option>.</para>
<para>The following table explains the different modes of invocation and relationship to
<option>--as-pid2</option> (see above):</para>
@@ -847,23 +845,6 @@
</varlistentry>
<varlistentry>
- <term><option>--share-system</option></term>
-
- <listitem><para>Allows the container to share certain system
- facilities with the host. More specifically, this turns off
- PID namespacing, UTS namespacing and IPC namespacing, and thus
- allows the guest to see and interact more easily with
- processes outside of the container. Note that using this
- option makes it impossible to start up a full Operating System
- in the container, as an init system cannot operate in this
- mode. It is only useful to run specific programs or
- applications this way, without involving an init system in the
- container. This option implies <option>--register=no</option>.
- This option may not be combined with
- <option>--boot</option>.</para></listitem>
- </varlistentry>
-
- <varlistentry>
<term><option>--register=</option></term>
<listitem><para>Controls whether the container is registered
@@ -877,9 +858,7 @@
and shown by tools such as
<citerefentry project='man-pages'><refentrytitle>ps</refentrytitle><manvolnum>1</manvolnum></citerefentry>.
If the container does not run an init system, it is
- recommended to set this option to <literal>no</literal>. Note
- that <option>--share-system</option> implies
- <option>--register=no</option>. </para></listitem>
+ recommended to set this option to <literal>no</literal>.</para></listitem>
</varlistentry>
<varlistentry>
diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml
index 2190da55d4..2495998295 100644
--- a/man/systemd.exec.xml
+++ b/man/systemd.exec.xml
@@ -1617,6 +1617,43 @@
functions) if their standard output or standard error output is connected to the journal anyway, thus enabling
delivery of structured metadata along with logged messages.</para></listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><varname>$SERVICE_RESULT</varname></term>
+
+ <listitem><para>Only defined for the service unit type, this environment variable is passed to all
+ <varname>ExecStop=</varname> and <varname>ExecStopPost=</varname> processes, and encodes the service
+ "result". Currently, the following values are defined: <literal>timeout</literal> (in case of an operation
+ timeout), <literal>exit-code</literal> (if a service process exited with a non-zero exit code; see
+ <varname>$EXIT_STATUS</varname> below for the actual exit status returned), <literal>signal</literal> (if a
+ service process was terminated abnormally by a signal; see <varname>$EXIT_STATUS</varname> below for the actual
+ signal used for the termination), <literal>core-dump</literal> (if a service process terminated abnormally and
+ dumped core), <literal>watchdog</literal> (if the watchdog keep-alive ping was enabled for the service but it
+ missed the deadline), or <literal>resources</literal> (a catch-all condition in case a system operation
+ failed).</para>
+
+ <para>This environment variable is useful to monitor failure or successful termination of a service. Even
+ though this variable is available in both <varname>ExecStop=</varname> and <varname>ExecStopPost=</varname>, it
+ is usually a better choice to place monitoring tools in the latter, as the former is only invoked for services
+ that managed to start up correctly, and the latter covers both services that failed during their start-up and
+ those which failed during their runtime.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>$EXIT_CODE</varname></term>
+ <term><varname>$EXIT_STATUS</varname></term>
+
+ <listitem><para>Only defined for the service unit type, these environment variables are passed to all
+ <varname>ExecStop=</varname>, <varname>ExecStopPost=</varname> processes and contain exit status/code
+ information of the main process of the service. For the precise definition of the exit code and status, see
+ <citerefentry><refentrytitle>wait</refentrytitle><manvolnum>2</manvolnum></citerefentry>. <varname>$EXIT_CODE</varname>
+ is one of <literal>exited</literal>, <literal>killed</literal>,
+ <literal>dumped</literal>. <varname>$EXIT_STATUS</varname> contains the numeric exit code formatted as string
+ if <varname>$EXIT_CODE</varname> is <literal>exited</literal>, and the signal name in all other cases. Note
+ that these environment variables are only set if the service manager succeeded to start and identify the main
+ process of the service.</para></listitem>
+ </varlistentry>
+
</variablelist>
<para>Additional variables may be configured by the following
diff --git a/man/systemd.netdev.xml b/man/systemd.netdev.xml
index a5c6f0fa40..e56708a648 100644
--- a/man/systemd.netdev.xml
+++ b/man/systemd.netdev.xml
@@ -343,8 +343,15 @@
</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>STP=</varname></term>
+ <listitem>
+ <para>A boolean. This enables the bridge's Spanning Tree Protocol (STP). When unset,
+ the kernel's default setting applies.
+ </para>
+ </listitem>
+ </varlistentry>
</variablelist>
-
</refsect1>
<refsect1>
diff --git a/man/systemd.network.xml b/man/systemd.network.xml
index 4541a55490..c332cd7bdc 100644
--- a/man/systemd.network.xml
+++ b/man/systemd.network.xml
@@ -212,6 +212,17 @@
below 1280 (the minimum MTU for IPv6) it will automatically be increased to this value.</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>ARP=</varname></term>
+ <listitem>
+ <para> A boolean. Enables or disables the ARP (low-level Address Resolution Protocol)
+ for this interface. Defaults to unset, which means that the kernel default will be used.</para>
+ <para> For example, disabling ARP is useful when creating multiple MACVLAN or VLAN virtual
+ interfaces atop a single lower-level physical interface, which will then only serve as a
+ link/"bridge" device aggregating traffic to the same physical link and not participate in
+ the network otherwise.</para>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect1>
diff --git a/man/systemd.service.xml b/man/systemd.service.xml
index 875d368fcf..e82edbe93e 100644
--- a/man/systemd.service.xml
+++ b/man/systemd.service.xml
@@ -429,7 +429,13 @@
service failed to start up correctly. Commands configured with this setting need to be able to operate even if
the service failed starting up half-way and left incompletely initialized data around. As the service's
processes have been terminated already when the commands specified with this setting are executed they should
- not attempt to communicate with them.</para></listitem>
+ not attempt to communicate with them.</para>
+
+ <para>Note that all commands that are configured with this setting are invoked with the result code of the
+ service, as well as the main process' exit code and status, set in the <varname>$SERVICE_RESULT</varname>,
+ <varname>$EXIT_CODE</varname> and <varname>$EXIT_STATUS</varname> environment variables, see
+ <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
+ details.</para></listitem>
</varlistentry>
<varlistentry>
diff --git a/man/systemd.time.xml b/man/systemd.time.xml
index aae3accb6c..47229b4a4e 100644
--- a/man/systemd.time.xml
+++ b/man/systemd.time.xml
@@ -57,14 +57,13 @@
<refsect1>
<title>Displaying Time Spans</title>
- <para>Time spans refer to time durations. On display, systemd will
- present time spans as a space-separated series of time values each
- suffixed by a time unit.</para>
+ <para>Time spans refer to time durations. On display, systemd will present time spans as a space-separated series
+ of time values each suffixed by a time unit. Example:</para>
<programlisting>2h 30min</programlisting>
- <para>All specified time values are meant to be added up. The
- above hence refers to 150 minutes.</para>
+ <para>All specified time values are meant to be added up. The above hence refers to 150 minutes. Display is
+ locale-independent, only English names for the time units are used.</para>
</refsect1>
<refsect1>
@@ -83,13 +82,13 @@
<listitem><para>days, day, d</para></listitem>
<listitem><para>weeks, week, w</para></listitem>
<listitem><para>months, month, M (defined as 30.44 days)</para></listitem>
- <listitem><para>years, year, y (define as 365.25 days)</para></listitem>
+ <listitem><para>years, year, y (defined as 365.25 days)</para></listitem>
</itemizedlist>
- <para>If no time unit is specified, generally seconds are assumed,
- but some exceptions exist and are marked as such. In a few cases
- <literal>ns</literal>, <literal>nsec</literal> is accepted too,
- where the granularity of the time span allows for this.</para>
+ <para>If no time unit is specified, generally seconds are assumed, but some exceptions exist and are marked as
+ such. In a few cases <literal>ns</literal>, <literal>nsec</literal> is accepted too, where the granularity of the
+ time span permits this. Parsing is generally locale-independent, non-English names for the time units are not
+ accepted.</para>
<para>Examples for valid time span specifications:</para>
@@ -110,30 +109,29 @@
<programlisting>Fri 2012-11-23 23:02:15 CET</programlisting>
- <para>The weekday is printed according to the locale choice of the
- user.</para>
+ <para>The weekday is printed in the abbreviated English language form. The formatting is locale-independent.</para>
+
+ <para>In some cases timestamps are shown in the UTC timezone instead of the local timezone, which is indicated via
+ the <literal>UTC</literal> timezone specifier in the output.</para>
+
+ <para>In some cases timestamps are shown with microsecond granularity. In this case the sub-second remainder is
+ separated by a full stop from the seconds component.</para>
</refsect1>
<refsect1>
<title>Parsing Timestamps</title>
- <para>When parsing, systemd will accept a similar syntax, but
- expects no timezone specification, unless it is given as the
- literal string "UTC". In this case, the time is considered in UTC,
- otherwise in the local timezone. The weekday specification is
- optional, but when the weekday is specified, it must either be in
- the abbreviated (<literal>Wed</literal>) or non-abbreviated
- (<literal>Wednesday</literal>) English language form (case does
- not matter), and is not subject to the locale choice of the user.
- Either the date, or the time part may be omitted, in which case
- the current date or 00:00:00, respectively, is assumed. The seconds
- component of the time may also be omitted, in which case ":00" is
- assumed. Year numbers may be specified in full or may be
- abbreviated (omitting the century).</para>
-
- <para>A timestamp is considered invalid if a weekday is specified
- and the date does not actually match the specified day of the
- week.</para>
+ <para>When parsing, systemd will accept a similar syntax, but expects no timezone specification, unless it is given
+ as the literal string <literal>UTC</literal> (for the UTC timezone) or is specified to be the locally configured
+ timezone. Other timezones than the local and UTC are not supported. The weekday specification is optional, but when
+ the weekday is specified, it must either be in the abbreviated (<literal>Wed</literal>) or non-abbreviated
+ (<literal>Wednesday</literal>) English language form (case does not matter), and is not subject to the locale
+ choice of the user. Either the date, or the time part may be omitted, in which case the current date or 00:00:00,
+ respectively, is assumed. The seconds component of the time may also be omitted, in which case ":00" is
+ assumed. Year numbers may be specified in full or may be abbreviated (omitting the century).</para>
+
+ <para>A timestamp is considered invalid if a weekday is specified and the date does not match the specified day of
+ the week.</para>
<para>When parsing, systemd will also accept a few special
placeholders instead of timestamps: <literal>now</literal> may be
@@ -167,8 +165,6 @@
2012-11-23 → Fri 2012-11-23 00:00:00
12-11-23 → Fri 2012-11-23 00:00:00
11:12:13 → Fri 2012-11-23 11:12:13
- 11:12:13.9900009 → Fri 2012-11-23 11:12:13
- format_timestamp_us: Fri 2012-11-23 11:12:13.990000
11:12 → Fri 2012-11-23 11:12:00
now → Fri 2012-11-23 18:15:22
today → Fri 2012-11-23 00:00:00
@@ -176,28 +172,25 @@
yesterday → Fri 2012-11-22 00:00:00
tomorrow → Fri 2012-11-24 00:00:00
+3h30min → Fri 2012-11-23 21:45:22
- +3h30min UTC → -EINVAL
-5s → Fri 2012-11-23 18:15:17
11min ago → Fri 2012-11-23 18:04:22
- 11min ago UTC → -EINVAL
@1395716396 → Tue 2014-03-25 03:59:56</programlisting>
- <para>Note that timestamps printed by systemd will not be parsed
- correctly by systemd, as the timezone specification is not
- accepted, and printing timestamps is subject to locale settings
- for the weekday, while parsing only accepts English weekday
- names.</para>
+ <para>Note that timestamps displayed by remote systems with a non-matching timezone are usually not parsable
+ locally, as the timezone component is not understood (unless it happens to be <literal>UTC</literal>).</para>
- <para>In some cases, systemd will display a relative timestamp
- (relative to the current time, or the time of invocation of the
- command) instead or in addition to an absolute timestamp as
- described above. A relative timestamp is formatted as
- follows:</para>
+ <para>Timestamps may also be specified with microsecond granularity. The sub-second remainder is expected separated
+ by a full stop from the seconds component. Example:</para>
+
+ <programlisting>2014-03-25 03:59:56.654563</programlisting>
+
+ <para>In some cases, systemd will display a relative timestamp (relative to the current time, or the time of
+ invocation of the command) instead of or in addition to an absolute timestamp as described above. A relative
+ timestamp is formatted as follows:</para>
- <para>2 months 5 days ago</para>
+ <programlisting>2 months 5 days ago</programlisting>
- <para>Note that any relative timestamp will also parse correctly
- where a timestamp is expected. (see above)</para>
+ <para>Note that a relative timestamp is also accepted where a timestamp is expected (see above).</para>
</refsect1>
<refsect1>
@@ -239,8 +232,9 @@
second component is not specified, <literal>:00</literal> is
assumed.</para>
- <para>A timezone specification is not expected, unless it is given
- as the literal string "UTC", similarly to timestamps.</para>
+ <para>A timezone specification is not expected, unless it is given as the literal string <literal>UTC</literal>, or
+ the local timezone, similar to the supported syntax of timestamps (see above). Non-local timezones except for UTC
+ are not supported.</para>
<para>The special expressions
<literal>minutely</literal>,
@@ -263,38 +257,38 @@
<para>Examples for valid timestamps and their
normalized form:</para>
-<programlisting> Sat,Thu,Mon..Wed,Sat..Sun → Mon..Thu,Sat,Sun *-*-* 00:00:00
- Mon,Sun 12-*-* 2,1:23 → Mon,Sun 2012-*-* 01,02:23:00
- Wed *-1 → Wed *-*-01 00:00:00
+<programlisting> Sat,Thu,Mon..Wed,Sat..Sun → Mon..Thu,Sat,Sun *-*-* 00:00:00
+ Mon,Sun 12-*-* 2,1:23 → Mon,Sun 2012-*-* 01,02:23:00
+ Wed *-1 → Wed *-*-01 00:00:00
Wed..Wed,Wed *-1 → Wed *-*-01 00:00:00
- Wed, 17:48 → Wed *-*-* 17:48:00
+ Wed, 17:48 → Wed *-*-* 17:48:00
Wed..Sat,Tue 12-10-15 1:2:3 → Tue..Sat 2012-10-15 01:02:03
- *-*-7 0:0:0 → *-*-07 00:00:00
- 10-15 → *-10-15 00:00:00
- monday *-12-* 17:00 → Mon *-12-* 17:00:00
- Mon,Fri *-*-3,1,2 *:30:45 → Mon,Fri *-*-01,02,03 *:30:45
- 12,14,13,12:20,10,30 → *-*-* 12,13,14:10,20,30:00
- 12..14:10,20,30 → *-*-* 12,13,14:10,20,30:00
- mon,fri *-1/2-1,3 *:30:45 → Mon,Fri *-01/2-01,03 *:30:45
- 03-05 08:05:40 → *-03-05 08:05:40
- 08:05:40 → *-*-* 08:05:40
- 05:40 → *-*-* 05:40:00
- Sat,Sun 12-05 08:05:40 → Sat,Sun *-12-05 08:05:40
- Sat,Sun 08:05:40 → Sat,Sun *-*-* 08:05:40
- 2003-03-05 05:40 → 2003-03-05 05:40:00
-05:40:23.4200004/3.1700005 → 05:40:23.420000/3.170001
- 2003-02..04-05 → 2003-02,03,04-05 00:00:00
- 2003-03-05 05:40 UTC → 2003-03-05 05:40:00 UTC
- 2003-03-05 → 2003-03-05 00:00:00
- 03-05 → *-03-05 00:00:00
- hourly → *-*-* *:00:00
- daily → *-*-* 00:00:00
- daily UTC → *-*-* 00:00:00 UTC
- monthly → *-*-01 00:00:00
- weekly → Mon *-*-* 00:00:00
- yearly → *-01-01 00:00:00
- annually → *-01-01 00:00:00
- *:2/3 → *-*-* *:02/3:00</programlisting>
+ *-*-7 0:0:0 → *-*-07 00:00:00
+ 10-15 → *-10-15 00:00:00
+ monday *-12-* 17:00 → Mon *-12-* 17:00:00
+ Mon,Fri *-*-3,1,2 *:30:45 → Mon,Fri *-*-01,02,03 *:30:45
+ 12,14,13,12:20,10,30 → *-*-* 12,13,14:10,20,30:00
+ 12..14:10,20,30 → *-*-* 12,13,14:10,20,30:00
+ mon,fri *-1/2-1,3 *:30:45 → Mon,Fri *-01/2-01,03 *:30:45
+ 03-05 08:05:40 → *-03-05 08:05:40
+ 08:05:40 → *-*-* 08:05:40
+ 05:40 → *-*-* 05:40:00
+ Sat,Sun 12-05 08:05:40 → Sat,Sun *-12-05 08:05:40
+ Sat,Sun 08:05:40 → Sat,Sun *-*-* 08:05:40
+ 2003-03-05 05:40 → 2003-03-05 05:40:00
+ 05:40:23.4200004/3.1700005 → 05:40:23.420000/3.170001
+ 2003-02..04-05 → 2003-02,03,04-05 00:00:00
+ 2003-03-05 05:40 UTC → 2003-03-05 05:40:00 UTC
+ 2003-03-05 → 2003-03-05 00:00:00
+ 03-05 → *-03-05 00:00:00
+ hourly → *-*-* *:00:00
+ daily → *-*-* 00:00:00
+ daily UTC → *-*-* 00:00:00 UTC
+ monthly → *-*-01 00:00:00
+ weekly → Mon *-*-* 00:00:00
+ yearly → *-01-01 00:00:00
+ annually → *-01-01 00:00:00
+ *:2/3 → *-*-* *:02/3:00</programlisting>
<para>Calendar events are used by timer units, see
<citerefentry><refentrytitle>systemd.timer</refentrytitle><manvolnum>5</manvolnum></citerefentry>
diff --git a/shell-completion/bash/journalctl b/shell-completion/bash/journalctl
index 53bedcd92e..a999a10df1 100644
--- a/shell-completion/bash/journalctl
+++ b/shell-completion/bash/journalctl
@@ -65,7 +65,7 @@ _journalctl() {
compopt -o filenames
;;
--output|-o)
- comps='short short-iso short-precise short-monotonic verbose export json json-pretty json-sse cat'
+ comps='short short-full short-iso short-precise short-monotonic short-unix verbose export json json-pretty json-sse cat'
;;
--field|-F)
comps=$(journalctl --fields | sort 2>/dev/null)
diff --git a/shell-completion/bash/systemctl.in b/shell-completion/bash/systemctl.in
index 6f2b3f122c..2a45dcbba0 100644
--- a/shell-completion/bash/systemctl.in
+++ b/shell-completion/bash/systemctl.in
@@ -145,7 +145,7 @@ _systemctl () {
comps='full enable-only disable-only'
;;
--output|-o)
- comps='short short-iso short-precise short-monotonic verbose export json
+ comps='short short-full short-iso short-precise short-monotonic short-unix verbose export json
json-pretty json-sse cat'
;;
--machine|-M)
diff --git a/shell-completion/zsh/_sd_outputmodes b/shell-completion/zsh/_sd_outputmodes
index 3836f79b73..52617c6b7a 100644
--- a/shell-completion/zsh/_sd_outputmodes
+++ b/shell-completion/zsh/_sd_outputmodes
@@ -1,5 +1,5 @@
#autoload
local -a _output_opts
-_output_opts=(short short-iso short-precise short-monotonic verbose export json json-pretty json-sse cat)
+_output_opts=(short short-full short-iso short-precise short-monotonic short-unix verbose export json json-pretty json-sse cat)
_describe -t output 'output mode' _output_opts || compadd "$@"
diff --git a/src/basic/calendarspec.c b/src/basic/calendarspec.c
index e4cfab364e..fda293fcb9 100644
--- a/src/basic/calendarspec.c
+++ b/src/basic/calendarspec.c
@@ -302,6 +302,17 @@ int calendar_spec_to_string(const CalendarSpec *c, char **p) {
if (c->utc)
fputs(" UTC", f);
+ else if (IN_SET(c->dst, 0, 1)) {
+
+ /* If daylight saving is explicitly on or off, let's show the used timezone. */
+
+ tzset();
+
+ if (!isempty(tzname[c->dst])) {
+ fputc(' ', f);
+ fputs(tzname[c->dst], f);
+ }
+ }
r = fflush_and_check(f);
if (r < 0) {
@@ -747,9 +758,9 @@ fail:
}
int calendar_spec_from_string(const char *p, CalendarSpec **spec) {
+ const char *utc;
CalendarSpec *c;
int r;
- const char *utc;
assert(p);
assert(spec);
@@ -760,11 +771,39 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) {
c = new0(CalendarSpec, 1);
if (!c)
return -ENOMEM;
+ c->dst = -1;
utc = endswith_no_case(p, " UTC");
if (utc) {
c->utc = true;
p = strndupa(p, utc - p);
+ } else {
+ const char *e = NULL;
+ int j;
+
+ tzset();
+
+ /* Check if the local timezone was specified? */
+ for (j = 0; j <= 1; j++) {
+ if (isempty(tzname[j]))
+ continue;
+
+ e = endswith_no_case(p, tzname[j]);
+ if(!e)
+ continue;
+ if (e == p)
+ continue;
+ if (e[-1] != ' ')
+ continue;
+
+ break;
+ }
+
+ /* Found one of the two timezones specified? */
+ if (IN_SET(j, 0, 1)) {
+ p = strndupa(p, e - p - 1);
+ c->dst = j;
+ }
}
if (strcaseeq(p, "minutely")) {
@@ -1017,7 +1056,7 @@ static int find_next(const CalendarSpec *spec, struct tm *tm, usec_t *usec) {
for (;;) {
/* Normalize the current date */
(void) mktime_or_timegm(&c, spec->utc);
- c.tm_isdst = -1;
+ c.tm_isdst = spec->dst;
c.tm_year += 1900;
r = find_matching_component(spec->year, &c.tm_year);
diff --git a/src/basic/calendarspec.h b/src/basic/calendarspec.h
index f6472c1244..c6087228fd 100644
--- a/src/basic/calendarspec.h
+++ b/src/basic/calendarspec.h
@@ -37,6 +37,7 @@ typedef struct CalendarComponent {
typedef struct CalendarSpec {
int weekdays_bits;
bool utc;
+ int dst;
CalendarComponent *year;
CalendarComponent *month;
diff --git a/src/basic/fileio.c b/src/basic/fileio.c
index f183de4999..d642f3daea 100644
--- a/src/basic/fileio.c
+++ b/src/basic/fileio.c
@@ -47,6 +47,8 @@
#include "umask-util.h"
#include "utf8.h"
+#define READ_FULL_BYTES_MAX (4U*1024U*1024U)
+
int write_string_stream(FILE *f, const char *line, bool enforce_newline) {
assert(f);
@@ -230,7 +232,7 @@ int read_full_stream(FILE *f, char **contents, size_t *size) {
if (S_ISREG(st.st_mode)) {
/* Safety check */
- if (st.st_size > 4*1024*1024)
+ if (st.st_size > READ_FULL_BYTES_MAX)
return -E2BIG;
/* Start with the right file size, but be prepared for
@@ -245,26 +247,31 @@ int read_full_stream(FILE *f, char **contents, size_t *size) {
char *t;
size_t k;
- t = realloc(buf, n+1);
+ t = realloc(buf, n + 1);
if (!t)
return -ENOMEM;
buf = t;
k = fread(buf + l, 1, n - l, f);
+ if (k > 0)
+ l += k;
- if (k <= 0) {
- if (ferror(f))
- return -errno;
+ if (ferror(f))
+ return -errno;
+ if (feof(f))
break;
- }
- l += k;
- n *= 2;
+ /* We aren't expecting fread() to return a short read outside
+ * of (error && eof), assert buffer is full and enlarge buffer.
+ */
+ assert(l == n);
/* Safety check */
- if (n > 4*1024*1024)
+ if (n >= READ_FULL_BYTES_MAX)
return -E2BIG;
+
+ n = MIN(n * 2, READ_FULL_BYTES_MAX);
}
buf[l] = 0;
@@ -1161,8 +1168,8 @@ int tempfn_random_child(const char *p, const char *extra, char **ret) {
char *t, *x;
uint64_t u;
unsigned i;
+ int r;
- assert(p);
assert(ret);
/* Turns this:
@@ -1171,6 +1178,12 @@ int tempfn_random_child(const char *p, const char *extra, char **ret) {
* /foo/bar/waldo/.#<extra>3c2b6219aa75d7d0
*/
+ if (!p) {
+ r = tmp_dir(&p);
+ if (r < 0)
+ return r;
+ }
+
if (!extra)
extra = "";
@@ -1257,10 +1270,13 @@ int fputs_with_space(FILE *f, const char *s, const char *separator, bool *space)
int open_tmpfile_unlinkable(const char *directory, int flags) {
char *p;
- int fd;
+ int fd, r;
- if (!directory)
- directory = "/tmp";
+ if (!directory) {
+ r = tmp_dir(&directory);
+ if (r < 0)
+ return r;
+ }
/* Returns an unlinked temporary file that cannot be linked into the file system anymore */
diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c
index f0c6f3265e..ce87257bc1 100644
--- a/src/basic/fs-util.c
+++ b/src/basic/fs-util.c
@@ -496,34 +496,94 @@ int get_files_in_directory(const char *path, char ***list) {
return n;
}
-int var_tmp(char **ret) {
- const char *tmp_dir = NULL;
- const char *env_tmp_dir = NULL;
- char *c = NULL;
- int r;
+static int getenv_tmp_dir(const char **ret_path) {
+ const char *n;
+ int r, ret = 0;
- assert(ret);
+ assert(ret_path);
- env_tmp_dir = getenv("TMPDIR");
- if (env_tmp_dir != NULL) {
- r = is_dir(env_tmp_dir, true);
- if (r < 0 && r != -ENOENT)
- return r;
- if (r > 0)
- tmp_dir = env_tmp_dir;
+ /* We use the same order of environment variables python uses in tempfile.gettempdir():
+ * https://docs.python.org/3/library/tempfile.html#tempfile.gettempdir */
+ FOREACH_STRING(n, "TMPDIR", "TEMP", "TMP") {
+ const char *e;
+
+ e = secure_getenv(n);
+ if (!e)
+ continue;
+ if (!path_is_absolute(e)) {
+ r = -ENOTDIR;
+ goto next;
+ }
+ if (!path_is_safe(e)) {
+ r = -EPERM;
+ goto next;
+ }
+
+ r = is_dir(e, true);
+ if (r < 0)
+ goto next;
+ if (r == 0) {
+ r = -ENOTDIR;
+ goto next;
+ }
+
+ *ret_path = e;
+ return 1;
+
+ next:
+ /* Remember first error, to make this more debuggable */
+ if (ret >= 0)
+ ret = r;
}
- if (!tmp_dir)
- tmp_dir = "/var/tmp";
+ if (ret < 0)
+ return ret;
- c = strdup(tmp_dir);
- if (!c)
- return -ENOMEM;
- *ret = c;
+ *ret_path = NULL;
+ return ret;
+}
+static int tmp_dir_internal(const char *def, const char **ret) {
+ const char *e;
+ int r, k;
+
+ assert(def);
+ assert(ret);
+
+ r = getenv_tmp_dir(&e);
+ if (r > 0) {
+ *ret = e;
+ return 0;
+ }
+
+ k = is_dir(def, true);
+ if (k == 0)
+ k = -ENOTDIR;
+ if (k < 0)
+ return r < 0 ? r : k;
+
+ *ret = def;
return 0;
}
+int var_tmp_dir(const char **ret) {
+
+ /* Returns the location for "larger" temporary files, that is backed by physical storage if available, and thus
+ * even might survive a boot: /var/tmp. If $TMPDIR (or related environment variables) are set, its value is
+ * returned preferably however. Note that both this function and tmp_dir() below are affected by $TMPDIR,
+ * making it a variable that overrides all temporary file storage locations. */
+
+ return tmp_dir_internal("/var/tmp", ret);
+}
+
+int tmp_dir(const char **ret) {
+
+ /* Similar to var_tmp_dir() above, but returns the location for "smaller" temporary files, which is usually
+ * backed by an in-memory file system: /tmp. */
+
+ return tmp_dir_internal("/tmp", ret);
+}
+
int inotify_add_watch_fd(int fd, int what, uint32_t mask) {
char path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1];
int r;
diff --git a/src/basic/fs-util.h b/src/basic/fs-util.h
index 075e5942b1..2c3b9a1c74 100644
--- a/src/basic/fs-util.h
+++ b/src/basic/fs-util.h
@@ -61,7 +61,8 @@ int mkfifo_atomic(const char *path, mode_t mode);
int get_files_in_directory(const char *path, char ***list);
-int var_tmp(char **ret);
+int tmp_dir(const char **ret);
+int var_tmp_dir(const char **ret);
#define INOTIFY_EVENT_MAX (sizeof(struct inotify_event) + NAME_MAX + 1)
diff --git a/src/basic/parse-util.c b/src/basic/parse-util.c
index 503a895731..c98815b9bc 100644
--- a/src/basic/parse-util.c
+++ b/src/basic/parse-util.c
@@ -29,6 +29,7 @@
#include "extract-word.h"
#include "macro.h"
#include "parse-util.h"
+#include "process-util.h"
#include "string-util.h"
int parse_boolean(const char *v) {
@@ -533,7 +534,7 @@ int parse_fractional_part_u(const char **p, size_t digits, unsigned *res) {
return 0;
}
-int parse_percent(const char *p) {
+int parse_percent_unbounded(const char *p) {
const char *pc, *n;
unsigned v;
int r;
@@ -546,8 +547,30 @@ int parse_percent(const char *p) {
r = safe_atou(n, &v);
if (r < 0)
return r;
+
+ return (int) v;
+}
+
+int parse_percent(const char *p) {
+ int v;
+
+ v = parse_percent_unbounded(p);
if (v > 100)
return -ERANGE;
- return (int) v;
+ return v;
+}
+
+int parse_nice(const char *p, int *ret) {
+ int n, r;
+
+ r = safe_atoi(p, &n);
+ if (r < 0)
+ return r;
+
+ if (!nice_is_valid(n))
+ return -ERANGE;
+
+ *ret = n;
+ return 0;
}
diff --git a/src/basic/parse-util.h b/src/basic/parse-util.h
index 73441bb6fd..461e1cd4d8 100644
--- a/src/basic/parse-util.h
+++ b/src/basic/parse-util.h
@@ -106,4 +106,7 @@ int safe_atod(const char *s, double *ret_d);
int parse_fractional_part_u(const char **s, size_t digits, unsigned *res);
+int parse_percent_unbounded(const char *p);
int parse_percent(const char *p);
+
+int parse_nice(const char *p, int *ret);
diff --git a/src/basic/process-util.h b/src/basic/process-util.h
index 9f75088796..2568e3834f 100644
--- a/src/basic/process-util.h
+++ b/src/basic/process-util.h
@@ -26,6 +26,7 @@
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
+#include <sys/resource.h>
#include "formats-util.h"
#include "macro.h"
@@ -103,3 +104,7 @@ int sched_policy_from_string(const char *s);
void valgrind_summary_hack(void);
int pid_compare_func(const void *a, const void *b);
+
+static inline bool nice_is_valid(int n) {
+ return n >= PRIO_MIN && n < PRIO_MAX;
+}
diff --git a/src/basic/set.h b/src/basic/set.h
index 12f64a8c57..a5f8beb0c4 100644
--- a/src/basic/set.h
+++ b/src/basic/set.h
@@ -23,8 +23,8 @@
#include "hashmap.h"
#include "macro.h"
-Set *internal_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
-#define set_new(ops) internal_set_new(ops HASHMAP_DEBUG_SRC_ARGS)
+Set *internal_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
+#define set_new(ops) internal_set_new(ops HASHMAP_DEBUG_SRC_ARGS)
static inline Set *set_free(Set *s) {
internal_hashmap_free(HASHMAP_BASE(s));
@@ -42,8 +42,8 @@ static inline Set *set_copy(Set *s) {
return (Set*) internal_hashmap_copy(HASHMAP_BASE(s));
}
-int internal_set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
-#define set_ensure_allocated(h, ops) internal_set_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS)
+int internal_set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
+#define set_ensure_allocated(h, ops) internal_set_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS)
int set_put(Set *s, const void *key);
/* no set_update */
diff --git a/src/basic/terminal-util.c b/src/basic/terminal-util.c
index df56d85317..f0a46c48cf 100644
--- a/src/basic/terminal-util.c
+++ b/src/basic/terminal-util.c
@@ -785,7 +785,7 @@ bool tty_is_vc_resolve(const char *tty) {
}
const char *default_term_for_tty(const char *tty) {
- return tty && tty_is_vc_resolve(tty) ? "TERM=linux" : "TERM=vt220";
+ return tty && tty_is_vc_resolve(tty) ? "linux" : "vt220";
}
int fd_columns(int fd) {
diff --git a/src/basic/time-util.c b/src/basic/time-util.c
index 24e681bf85..0ef1f6393e 100644
--- a/src/basic/time-util.c
+++ b/src/basic/time-util.c
@@ -254,32 +254,95 @@ struct timeval *timeval_store(struct timeval *tv, usec_t u) {
return tv;
}
-static char *format_timestamp_internal(char *buf, size_t l, usec_t t,
- bool utc, bool us) {
+static char *format_timestamp_internal(
+ char *buf,
+ size_t l,
+ usec_t t,
+ bool utc,
+ bool us) {
+
+ /* The weekdays in non-localized (English) form. We use this instead of the localized form, so that our
+ * generated timestamps may be parsed with parse_timestamp(), and always read the same. */
+ static const char * const weekdays[] = {
+ [0] = "Sun",
+ [1] = "Mon",
+ [2] = "Tue",
+ [3] = "Wed",
+ [4] = "Thu",
+ [5] = "Fri",
+ [6] = "Sat",
+ };
+
struct tm tm;
time_t sec;
- int k;
+ size_t n;
assert(buf);
- assert(l > 0);
+ if (l <
+ 3 + /* week day */
+ 1 + 10 + /* space and date */
+ 1 + 8 + /* space and time */
+ (us ? 1 + 6 : 0) + /* "." and microsecond part */
+ 1 + 1 + /* space and shortest possible zone */
+ 1)
+ return NULL; /* Not enough space even for the shortest form. */
if (t <= 0 || t == USEC_INFINITY)
+ return NULL; /* Timestamp is unset */
+
+ sec = (time_t) (t / USEC_PER_SEC); /* Round down */
+ if ((usec_t) sec != (t / USEC_PER_SEC))
+ return NULL; /* overflow? */
+
+ if (!localtime_or_gmtime_r(&sec, &tm, utc))
return NULL;
- sec = (time_t) (t / USEC_PER_SEC);
- localtime_or_gmtime_r(&sec, &tm, utc);
+ /* Start with the week day */
+ assert((size_t) tm.tm_wday < ELEMENTSOF(weekdays));
+ memcpy(buf, weekdays[tm.tm_wday], 4);
- if (us)
- k = strftime(buf, l, "%a %Y-%m-%d %H:%M:%S", &tm);
- else
- k = strftime(buf, l, "%a %Y-%m-%d %H:%M:%S %Z", &tm);
+ /* Add the main components */
+ if (strftime(buf + 3, l - 3, " %Y-%m-%d %H:%M:%S", &tm) <= 0)
+ return NULL; /* Doesn't fit */
- if (k <= 0)
- return NULL;
+ /* Append the microseconds part, if that's requested */
if (us) {
- snprintf(buf + strlen(buf), l - strlen(buf), ".%06llu", (unsigned long long) (t % USEC_PER_SEC));
- if (strftime(buf + strlen(buf), l - strlen(buf), " %Z", &tm) <= 0)
- return NULL;
+ n = strlen(buf);
+ if (n + 8 > l)
+ return NULL; /* Microseconds part doesn't fit. */
+
+ sprintf(buf + n, ".%06llu", (unsigned long long) (t % USEC_PER_SEC));
+ }
+
+ /* Append the timezone */
+ n = strlen(buf);
+ if (utc) {
+ /* If this is UTC then let's explicitly use the "UTC" string here, because gmtime_r() normally uses the
+ * obsolete "GMT" instead. */
+ if (n + 5 > l)
+ return NULL; /* "UTC" doesn't fit. */
+
+ strcpy(buf + n, " UTC");
+
+ } else if (!isempty(tm.tm_zone)) {
+ size_t tn;
+
+ /* An explicit timezone is specified, let's use it, if it fits */
+ tn = strlen(tm.tm_zone);
+ if (n + 1 + tn + 1 > l) {
+ /* The full time zone does not fit in. Yuck. */
+
+ if (n + 1 + _POSIX_TZNAME_MAX + 1 > l)
+ return NULL; /* Not even enough space for the POSIX minimum (of 6)? In that case, complain that it doesn't fit */
+
+ /* So the time zone doesn't fit in fully, but the caller passed enough space for the POSIX
+ * minimum time zone length. In this case suppress the timezone entirely, in order not to dump
+ * an overly long, hard to read string on the user. This should be safe, because the user will
+ * assume the local timezone anyway if none is shown. And so does parse_timestamp(). */
+ } else {
+ buf[n++] = ' ';
+ strcpy(buf + n, tm.tm_zone);
+ }
}
return buf;
@@ -539,12 +602,11 @@ int parse_timestamp(const char *t, usec_t *usec) {
{ "Sat", 6 },
};
- const char *k;
- const char *utc;
+ const char *k, *utc, *tzn = NULL;
struct tm tm, copy;
time_t x;
usec_t x_usec, plus = 0, minus = 0, ret;
- int r, weekday = -1;
+ int r, weekday = -1, dst = -1;
unsigned i;
/*
@@ -609,15 +671,55 @@ int parse_timestamp(const char *t, usec_t *usec) {
goto finish;
}
+ /* See if the timestamp is suffixed with UTC */
utc = endswith_no_case(t, " UTC");
if (utc)
t = strndupa(t, utc - t);
+ else {
+ const char *e = NULL;
+ int j;
+
+ tzset();
+
+ /* See if the timestamp is suffixed by either the DST or non-DST local timezone. Note that we only
+ * support the local timezones here, nothing else. Not because we wouldn't want to, but simply because
+ * there are no nice APIs available to cover this. By accepting the local time zone strings, we make
+ * sure that all timestamps written by format_timestamp() can be parsed correctly, even though we don't
+ * support arbitrary timezone specifications. */
+
+ for (j = 0; j <= 1; j++) {
+
+ if (isempty(tzname[j]))
+ continue;
+
+ e = endswith_no_case(t, tzname[j]);
+ if (!e)
+ continue;
+ if (e == t)
+ continue;
+ if (e[-1] != ' ')
+ continue;
+
+ break;
+ }
- x = ret / USEC_PER_SEC;
+ if (IN_SET(j, 0, 1)) {
+ /* Found one of the two timezones specified. */
+ t = strndupa(t, e - t - 1);
+ dst = j;
+ tzn = tzname[j];
+ }
+ }
+
+ x = (time_t) (ret / USEC_PER_SEC);
x_usec = 0;
- assert_se(localtime_or_gmtime_r(&x, &tm, utc));
- tm.tm_isdst = -1;
+ if (!localtime_or_gmtime_r(&x, &tm, utc))
+ return -EINVAL;
+
+ tm.tm_isdst = dst;
+ if (tzn)
+ tm.tm_zone = tzn;
if (streq(t, "today")) {
tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
@@ -634,7 +736,6 @@ int parse_timestamp(const char *t, usec_t *usec) {
goto from_tm;
}
-
for (i = 0; i < ELEMENTSOF(day_nr); i++) {
size_t skip;
@@ -727,7 +828,6 @@ parse_usec:
return -EINVAL;
x_usec = add;
-
}
from_tm:
diff --git a/src/basic/time-util.h b/src/basic/time-util.h
index 1b058f0e49..99be5ce6ee 100644
--- a/src/basic/time-util.h
+++ b/src/basic/time-util.h
@@ -68,7 +68,9 @@ typedef struct triple_timestamp {
#define USEC_PER_YEAR ((usec_t) (31557600ULL*USEC_PER_SEC))
#define NSEC_PER_YEAR ((nsec_t) (31557600ULL*NSEC_PER_SEC))
-#define FORMAT_TIMESTAMP_MAX ((4*4+1)+11+9+4+1) /* weekdays can be unicode */
+/* We assume a maximum timezone length of 6. TZNAME_MAX is not defined on Linux, but glibc internally initializes this
+ * to 6. Let's rely on that. */
+#define FORMAT_TIMESTAMP_MAX (3+1+10+1+8+1+6+1+6+1)
#define FORMAT_TIMESTAMP_WIDTH 28 /* when outputting, assume this width */
#define FORMAT_TIMESTAMP_RELATIVE_MAX 256
#define FORMAT_TIMESPAN_MAX 64
diff --git a/src/core/automount.c b/src/core/automount.c
index 4e9891569c..00295cf769 100644
--- a/src/core/automount.c
+++ b/src/core/automount.c
@@ -301,7 +301,7 @@ static void automount_dump(Unit *u, FILE *f, const char *prefix) {
static void automount_enter_dead(Automount *a, AutomountResult f) {
assert(a);
- if (f != AUTOMOUNT_SUCCESS)
+ if (a->result == AUTOMOUNT_SUCCESS)
a->result = f;
automount_set_state(a, a->result != AUTOMOUNT_SUCCESS ? AUTOMOUNT_FAILED : AUTOMOUNT_DEAD);
@@ -1105,6 +1105,9 @@ const UnitVTable automount_vtable = {
.reset_failed = automount_reset_failed,
.bus_vtable = bus_automount_vtable,
+ .bus_set_property = bus_automount_set_property,
+
+ .can_transient = true,
.shutdown = automount_shutdown,
.supported = automount_supported,
diff --git a/src/core/busname.c b/src/core/busname.c
index 730be2ee14..7952cd31aa 100644
--- a/src/core/busname.c
+++ b/src/core/busname.c
@@ -442,7 +442,7 @@ fail:
static void busname_enter_dead(BusName *n, BusNameResult f) {
assert(n);
- if (f != BUSNAME_SUCCESS)
+ if (n->result == BUSNAME_SUCCESS)
n->result = f;
busname_set_state(n, n->result != BUSNAME_SUCCESS ? BUSNAME_FAILED : BUSNAME_DEAD);
@@ -454,7 +454,7 @@ static void busname_enter_signal(BusName *n, BusNameState state, BusNameResult f
assert(n);
- if (f != BUSNAME_SUCCESS)
+ if (n->result == BUSNAME_SUCCESS)
n->result = f;
kill_context_init(&kill_context);
@@ -882,7 +882,7 @@ static void busname_sigchld_event(Unit *u, pid_t pid, int code, int status) {
log_unit_full(u, f == BUSNAME_SUCCESS ? LOG_DEBUG : LOG_NOTICE, 0,
"Control process exited, code=%s status=%i", sigchld_code_to_string(code), status);
- if (f != BUSNAME_SUCCESS)
+ if (n->result == BUSNAME_SUCCESS)
n->result = f;
switch (n->state) {
diff --git a/src/core/dbus-automount.c b/src/core/dbus-automount.c
index b2806ad86f..26212b3a95 100644
--- a/src/core/dbus-automount.c
+++ b/src/core/dbus-automount.c
@@ -32,3 +32,57 @@ const sd_bus_vtable bus_automount_vtable[] = {
SD_BUS_PROPERTY("TimeoutIdleUSec", "t", bus_property_get_usec, offsetof(Automount, timeout_idle_usec), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_VTABLE_END
};
+
+static int bus_automount_set_transient_property(
+ Automount *a,
+ const char *name,
+ sd_bus_message *message,
+ UnitSetPropertiesMode mode,
+ sd_bus_error *error) {
+
+ int r;
+
+ assert(a);
+ assert(name);
+ assert(message);
+
+ if (streq(name, "TimeoutIdleUSec")) {
+ usec_t timeout_idle_usec;
+ r = sd_bus_message_read(message, "t", &timeout_idle_usec);
+ if (r < 0)
+ return r;
+
+ if (mode != UNIT_CHECK) {
+ char time[FORMAT_TIMESPAN_MAX];
+
+ a->timeout_idle_usec = timeout_idle_usec;
+ unit_write_drop_in_format(UNIT(a), mode, name, "[Automount]\nTimeoutIdleSec=%s\n",
+ format_timespan(time, sizeof(time), timeout_idle_usec, USEC_PER_MSEC));
+ }
+ } else
+ return 0;
+
+ return 1;
+}
+
+int bus_automount_set_property(
+ Unit *u,
+ const char *name,
+ sd_bus_message *message,
+ UnitSetPropertiesMode mode,
+ sd_bus_error *error) {
+
+ Automount *a = AUTOMOUNT(u);
+ int r = 0;
+
+ assert(a);
+ assert(name);
+ assert(message);
+
+ if (u->transient && u->load_state == UNIT_STUB)
+ /* This is a transient unit, let's load a little more */
+
+ r = bus_automount_set_transient_property(a, name, message, mode, error);
+
+ return r;
+}
diff --git a/src/core/dbus-automount.h b/src/core/dbus-automount.h
index 7b51eb973a..f41adda2a6 100644
--- a/src/core/dbus-automount.h
+++ b/src/core/dbus-automount.h
@@ -21,3 +21,5 @@
extern const sd_bus_vtable bus_automount_vtable[];
+
+int bus_automount_set_property(Unit *u, const char *name, sd_bus_message *message, UnitSetPropertiesMode mode, sd_bus_error *error);
diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c
index 4b3bbfbc7d..e35d3ccd2e 100644
--- a/src/core/dbus-execute.c
+++ b/src/core/dbus-execute.c
@@ -936,7 +936,7 @@ int bus_exec_context_set_transient_property(
if (r < 0)
return r;
- if (n < PRIO_MIN || n >= PRIO_MAX)
+ if (!nice_is_valid(n))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Nice value out of range");
if (mode != UNIT_CHECK) {
diff --git a/src/core/dbus-mount.c b/src/core/dbus-mount.c
index 935db7c48b..b4bbee0648 100644
--- a/src/core/dbus-mount.c
+++ b/src/core/dbus-mount.c
@@ -157,6 +157,9 @@ static int bus_mount_set_transient_property(
if (!p)
return -ENOMEM;
+ unit_write_drop_in_format(UNIT(m), mode, name, "[Mount]\n%s=%s\n",
+ name, new_property);
+
free(*property);
*property = p;
}
diff --git a/src/core/execute.c b/src/core/execute.c
index cec3b3cf40..6019df7ea6 100644
--- a/src/core/execute.c
+++ b/src/core/execute.c
@@ -220,12 +220,36 @@ static void exec_context_tty_reset(const ExecContext *context, const ExecParamet
(void) vt_disallocate(path);
}
+static bool is_terminal_input(ExecInput i) {
+ return IN_SET(i,
+ EXEC_INPUT_TTY,
+ EXEC_INPUT_TTY_FORCE,
+ EXEC_INPUT_TTY_FAIL);
+}
+
static bool is_terminal_output(ExecOutput o) {
- return
- o == EXEC_OUTPUT_TTY ||
- o == EXEC_OUTPUT_SYSLOG_AND_CONSOLE ||
- o == EXEC_OUTPUT_KMSG_AND_CONSOLE ||
- o == EXEC_OUTPUT_JOURNAL_AND_CONSOLE;
+ return IN_SET(o,
+ EXEC_OUTPUT_TTY,
+ EXEC_OUTPUT_SYSLOG_AND_CONSOLE,
+ EXEC_OUTPUT_KMSG_AND_CONSOLE,
+ EXEC_OUTPUT_JOURNAL_AND_CONSOLE);
+}
+
+static bool exec_context_needs_term(const ExecContext *c) {
+ assert(c);
+
+ /* Return true if the execution context suggests we should set $TERM to something useful. */
+
+ if (is_terminal_input(c->std_input))
+ return true;
+
+ if (is_terminal_output(c->std_output))
+ return true;
+
+ if (is_terminal_output(c->std_error))
+ return true;
+
+ return !!c->tty_path;
}
static int open_null_as(int flags, int nfd) {
@@ -364,13 +388,6 @@ static int open_terminal_as(const char *path, mode_t mode, int nfd) {
return r;
}
-static bool is_terminal_input(ExecInput i) {
- return
- i == EXEC_INPUT_TTY ||
- i == EXEC_INPUT_TTY_FORCE ||
- i == EXEC_INPUT_TTY_FAIL;
-}
-
static int fixup_input(ExecInput std_input, int socket_fd, bool apply_tty_stdin) {
if (is_terminal_input(std_input) && !apply_tty_stdin)
@@ -411,7 +428,7 @@ static int setup_input(
return STDIN_FILENO;
}
- i = fixup_input(context->std_input, socket_fd, params->apply_tty_stdin);
+ i = fixup_input(context->std_input, socket_fd, params->flags & EXEC_APPLY_TTY_STDIN);
switch (i) {
@@ -486,7 +503,7 @@ static int setup_output(
return STDERR_FILENO;
}
- i = fixup_input(context->std_input, socket_fd, params->apply_tty_stdin);
+ i = fixup_input(context->std_input, socket_fd, params->flags & EXEC_APPLY_TTY_STDIN);
o = fixup_output(context->std_output, socket_fd);
if (fileno == STDERR_FILENO) {
@@ -1409,7 +1426,7 @@ static int build_environment(
our_env[n_env++] = x;
}
- if (p->watchdog_usec > 0) {
+ if ((p->flags & EXEC_SET_WATCHDOG) && p->watchdog_usec > 0) {
if (asprintf(&x, "WATCHDOG_PID="PID_FMT, getpid()) < 0)
return -ENOMEM;
our_env[n_env++] = x;
@@ -1445,12 +1462,21 @@ static int build_environment(
our_env[n_env++] = x;
}
- if (is_terminal_input(c->std_input) ||
- c->std_output == EXEC_OUTPUT_TTY ||
- c->std_error == EXEC_OUTPUT_TTY ||
- c->tty_path) {
+ if (exec_context_needs_term(c)) {
+ const char *tty_path, *term = NULL;
+
+ tty_path = exec_context_tty_path(c);
- x = strdup(default_term_for_tty(exec_context_tty_path(c)));
+ /* If we are forked off PID 1 and we are supposed to operate on /dev/console, then let's try to inherit
+ * the $TERM set for PID 1. This is useful for containers so that the $TERM the container manager
+ * passes to PID 1 ends up all the way in the console login shown. */
+
+ if (path_equal(tty_path, "/dev/console") && getppid() == 1)
+ term = getenv("TERM");
+ if (!term)
+ term = default_term_for_tty(tty_path);
+
+ x = strappend("TERM=", term);
if (!x)
return -ENOMEM;
our_env[n_env++] = x;
@@ -1803,7 +1829,7 @@ static int exec_child(
exec_context_tty_reset(context, params);
- if (params->confirm_spawn) {
+ if (params->flags & EXEC_CONFIRM_SPAWN) {
char response;
r = ask_for_confirmation(&response, argv);
@@ -1852,6 +1878,17 @@ static int exec_child(
*exit_status = EXIT_USER;
return r;
}
+
+ /* Don't set $HOME or $SHELL if they are are not particularly enlightening anyway. */
+ if (isempty(home) || path_equal(home, "/"))
+ home = NULL;
+
+ if (isempty(shell) || PATH_IN_SET(shell,
+ "/bin/nologin",
+ "/sbin/nologin",
+ "/usr/bin/nologin",
+ "/usr/sbin/nologin"))
+ shell = NULL;
}
if (context->group) {
@@ -2057,7 +2094,7 @@ static int exec_child(
umask(context->umask);
- if (params->apply_permissions && !command->privileged) {
+ if ((params->flags & EXEC_APPLY_PERMISSIONS) && !command->privileged) {
r = enforce_groups(context, username, gid);
if (r < 0) {
*exit_status = EXIT_GROUP;
@@ -2127,7 +2164,7 @@ static int exec_child(
}
r = setup_namespace(
- params->apply_chroot ? context->root_directory : NULL,
+ (params->flags & EXEC_APPLY_CHROOT) ? context->root_directory : NULL,
context->read_write_paths,
context->read_only_paths,
context->inaccessible_paths,
@@ -2158,7 +2195,7 @@ static int exec_child(
else
wd = "/";
- if (params->apply_chroot) {
+ if (params->flags & EXEC_APPLY_CHROOT) {
if (!needs_mount_namespace && context->root_directory)
if (chroot(context->root_directory) < 0) {
*exit_status = EXIT_CHROOT;
@@ -2182,7 +2219,12 @@ static int exec_child(
}
#ifdef HAVE_SELINUX
- if (params->apply_permissions && mac_selinux_use() && params->selinux_context_net && socket_fd >= 0 && !command->privileged) {
+ if ((params->flags & EXEC_APPLY_PERMISSIONS) &&
+ mac_selinux_use() &&
+ params->selinux_context_net &&
+ socket_fd >= 0 &&
+ !command->privileged) {
+
r = mac_selinux_get_child_mls_label(socket_fd, command->path, context->selinux_context, &mac_selinux_context_net);
if (r < 0) {
*exit_status = EXIT_SELINUX_CONTEXT;
@@ -2191,7 +2233,7 @@ static int exec_child(
}
#endif
- if (params->apply_permissions && context->private_users) {
+ if ((params->flags & EXEC_APPLY_PERMISSIONS) && context->private_users) {
r = setup_private_users(uid, gid);
if (r < 0) {
*exit_status = EXIT_USER;
@@ -2215,7 +2257,7 @@ static int exec_child(
return r;
}
- if (params->apply_permissions && !command->privileged) {
+ if ((params->flags & EXEC_APPLY_PERMISSIONS) && !command->privileged) {
bool use_address_families = context->address_families_whitelist ||
!set_isempty(context->address_families);
@@ -3091,12 +3133,12 @@ void exec_status_dump(ExecStatus *s, FILE *f, const char *prefix) {
"%sPID: "PID_FMT"\n",
prefix, s->pid);
- if (s->start_timestamp.realtime > 0)
+ if (dual_timestamp_is_set(&s->start_timestamp))
fprintf(f,
"%sStart Timestamp: %s\n",
prefix, format_timestamp(buf, sizeof(buf), s->start_timestamp.realtime));
- if (s->exit_timestamp.realtime > 0)
+ if (dual_timestamp_is_set(&s->exit_timestamp))
fprintf(f,
"%sExit Timestamp: %s\n"
"%sExit Code: %s\n"
diff --git a/src/core/execute.h b/src/core/execute.h
index 5fac3e85e8..106154f81a 100644
--- a/src/core/execute.h
+++ b/src/core/execute.h
@@ -209,6 +209,19 @@ struct ExecContext {
bool no_new_privileges_set:1;
};
+typedef enum ExecFlags {
+ EXEC_CONFIRM_SPAWN = 1U << 0,
+ EXEC_APPLY_PERMISSIONS = 1U << 1,
+ EXEC_APPLY_CHROOT = 1U << 2,
+ EXEC_APPLY_TTY_STDIN = 1U << 3,
+
+ /* The following are not used by execute.c, but by consumers internally */
+ EXEC_PASS_FDS = 1U << 4,
+ EXEC_IS_CONTROL = 1U << 5,
+ EXEC_SETENV_RESULT = 1U << 6,
+ EXEC_SET_WATCHDOG = 1U << 7,
+} ExecFlags;
+
struct ExecParameters {
char **argv;
char **environment;
@@ -217,11 +230,7 @@ struct ExecParameters {
char **fd_names;
unsigned n_fds;
- bool apply_permissions:1;
- bool apply_chroot:1;
- bool apply_tty_stdin:1;
-
- bool confirm_spawn:1;
+ ExecFlags flags;
bool selinux_context_net:1;
bool cgroup_delegate:1;
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
index e8cb3a4249..420f368689 100644
--- a/src/core/load-fragment.c
+++ b/src/core/load-fragment.c
@@ -491,16 +491,17 @@ int config_parse_socket_bind(const char *unit,
return 0;
}
-int config_parse_exec_nice(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_nice(
+ 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;
int priority, r;
@@ -510,14 +511,13 @@ int config_parse_exec_nice(const char *unit,
assert(rvalue);
assert(data);
- r = safe_atoi(rvalue, &priority);
+ r = parse_nice(rvalue, &priority);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse nice priority, ignoring: %s", rvalue);
- return 0;
- }
+ if (r == -ERANGE)
+ log_syntax(unit, LOG_ERR, filename, line, r, "Nice priority out of range, ignoring: %s", rvalue);
+ else
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse nice priority, ignoring: %s", rvalue);
- if (priority < PRIO_MIN || priority >= PRIO_MAX) {
- log_syntax(unit, LOG_ERR, filename, line, 0, "Nice priority out of range, ignoring: %s", rvalue);
return 0;
}
@@ -2903,7 +2903,7 @@ int config_parse_cpu_quota(
return 0;
}
- r = parse_percent(rvalue);
+ r = parse_percent_unbounded(rvalue);
if (r <= 0) {
log_syntax(unit, LOG_ERR, filename, line, r, "CPU quota '%s' invalid. Ignoring.", rvalue);
return 0;
diff --git a/src/core/main.c b/src/core/main.c
index c46d886653..02324d325e 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -1318,7 +1318,7 @@ static int fixup_environment(void) {
return r;
if (r == 0) {
- term = strdup(default_term_for_tty("/dev/console") + 5);
+ term = strdup(default_term_for_tty("/dev/console"));
if (!term)
return -ENOMEM;
}
@@ -1614,6 +1614,7 @@ int main(int argc, char *argv[]) {
retval = version();
goto finish;
} else if (arg_action == ACTION_DUMP_CONFIGURATION_ITEMS) {
+ pager_open(arg_no_pager, false);
unit_dump_config_items(stdout);
retval = EXIT_SUCCESS;
goto finish;
diff --git a/src/core/manager.c b/src/core/manager.c
index e41b65da50..c20e185d78 100644
--- a/src/core/manager.c
+++ b/src/core/manager.c
@@ -553,7 +553,6 @@ static int manager_default_environment(Manager *m) {
return 0;
}
-
int manager_new(UnitFileScope scope, bool test_run, Manager **_m) {
Manager *m;
int r;
diff --git a/src/core/mount.c b/src/core/mount.c
index db5cafcb11..f3ccf6d48a 100644
--- a/src/core/mount.c
+++ b/src/core/mount.c
@@ -484,6 +484,7 @@ static int mount_add_default_dependencies(Mount *m) {
static int mount_verify(Mount *m) {
_cleanup_free_ char *e = NULL;
+ MountParameters *p;
int r;
assert(m);
@@ -508,7 +509,8 @@ static int mount_verify(Mount *m) {
return -EINVAL;
}
- if (UNIT(m)->fragment_path && !m->parameters_fragment.what) {
+ p = get_mount_parameters_fragment(m);
+ if (p && !p->what) {
log_unit_error(UNIT(m), "What= setting is missing. Refusing.");
return -EBADMSG;
}
@@ -699,12 +701,10 @@ static int mount_spawn(Mount *m, ExecCommand *c, pid_t *_pid) {
pid_t pid;
int r;
ExecParameters exec_params = {
- .apply_permissions = true,
- .apply_chroot = true,
- .apply_tty_stdin = true,
- .stdin_fd = -1,
- .stdout_fd = -1,
- .stderr_fd = -1,
+ .flags = EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN,
+ .stdin_fd = -1,
+ .stdout_fd = -1,
+ .stderr_fd = -1,
};
assert(m);
@@ -730,7 +730,7 @@ static int mount_spawn(Mount *m, ExecCommand *c, pid_t *_pid) {
return r;
exec_params.environment = UNIT(m)->manager->environment;
- exec_params.confirm_spawn = UNIT(m)->manager->confirm_spawn;
+ exec_params.flags |= UNIT(m)->manager->confirm_spawn ? EXEC_CONFIRM_SPAWN : 0;
exec_params.cgroup_supported = UNIT(m)->manager->cgroup_supported;
exec_params.cgroup_path = UNIT(m)->cgroup_path;
exec_params.cgroup_delegate = m->cgroup_context.delegate;
@@ -759,7 +759,7 @@ static int mount_spawn(Mount *m, ExecCommand *c, pid_t *_pid) {
static void mount_enter_dead(Mount *m, MountResult f) {
assert(m);
- if (f != MOUNT_SUCCESS)
+ if (m->result == MOUNT_SUCCESS)
m->result = f;
mount_set_state(m, m->result != MOUNT_SUCCESS ? MOUNT_FAILED : MOUNT_DEAD);
@@ -775,7 +775,7 @@ static void mount_enter_dead(Mount *m, MountResult f) {
static void mount_enter_mounted(Mount *m, MountResult f) {
assert(m);
- if (f != MOUNT_SUCCESS)
+ if (m->result == MOUNT_SUCCESS)
m->result = f;
mount_set_state(m, MOUNT_MOUNTED);
@@ -786,7 +786,7 @@ static void mount_enter_signal(Mount *m, MountState state, MountResult f) {
assert(m);
- if (f != MOUNT_SUCCESS)
+ if (m->result == MOUNT_SUCCESS)
m->result = f;
r = unit_kill_context(
@@ -862,11 +862,6 @@ fail:
mount_enter_mounted(m, MOUNT_FAILURE_RESOURCES);
}
-static int mount_get_opts(Mount *m, char **ret) {
- return fstab_filter_options(m->parameters_fragment.options,
- "nofail\0" "noauto\0" "auto\0", NULL, NULL, ret);
-}
-
static void mount_enter_mounting(Mount *m) {
int r;
MountParameters *p;
@@ -889,19 +884,18 @@ static void mount_enter_mounting(Mount *m) {
if (p && mount_is_bind(p))
(void) mkdir_p_label(p->what, m->directory_mode);
- if (m->from_fragment) {
+ if (p) {
_cleanup_free_ char *opts = NULL;
- r = mount_get_opts(m, &opts);
+ r = fstab_filter_options(p->options, "nofail\0" "noauto\0" "auto\0", NULL, NULL, &opts);
if (r < 0)
goto fail;
- r = exec_command_set(m->control_command, MOUNT_PATH,
- m->parameters_fragment.what, m->where, NULL);
+ r = exec_command_set(m->control_command, MOUNT_PATH, p->what, m->where, NULL);
if (r >= 0 && m->sloppy_options)
r = exec_command_append(m->control_command, "-s", NULL);
- if (r >= 0 && m->parameters_fragment.fstype)
- r = exec_command_append(m->control_command, "-t", m->parameters_fragment.fstype, NULL);
+ if (r >= 0 && p->fstype)
+ r = exec_command_append(m->control_command, "-t", p->fstype, NULL);
if (r >= 0 && !isempty(opts))
r = exec_command_append(m->control_command, "-o", opts, NULL);
} else
@@ -927,27 +921,29 @@ fail:
static void mount_enter_remounting(Mount *m) {
int r;
+ MountParameters *p;
assert(m);
m->control_command_id = MOUNT_EXEC_REMOUNT;
m->control_command = m->exec_command + MOUNT_EXEC_REMOUNT;
- if (m->from_fragment) {
+ p = get_mount_parameters_fragment(m);
+ if (p) {
const char *o;
- if (m->parameters_fragment.options)
- o = strjoina("remount,", m->parameters_fragment.options);
+ if (p->options)
+ o = strjoina("remount,", p->options);
else
o = "remount";
r = exec_command_set(m->control_command, MOUNT_PATH,
- m->parameters_fragment.what, m->where,
+ p->what, m->where,
"-o", o, NULL);
if (r >= 0 && m->sloppy_options)
r = exec_command_append(m->control_command, "-s", NULL);
- if (r >= 0 && m->parameters_fragment.fstype)
- r = exec_command_append(m->control_command, "-t", m->parameters_fragment.fstype, NULL);
+ if (r >= 0 && p->fstype)
+ r = exec_command_append(m->control_command, "-t", p->fstype, NULL);
} else
r = -ENOENT;
@@ -1162,7 +1158,7 @@ static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) {
else
assert_not_reached("Unknown code");
- if (f != MOUNT_SUCCESS)
+ if (m->result == MOUNT_SUCCESS)
m->result = f;
if (m->control_command) {
diff --git a/src/core/path.c b/src/core/path.c
index 0dd0d375d8..10f9b06974 100644
--- a/src/core/path.c
+++ b/src/core/path.c
@@ -454,7 +454,7 @@ static int path_coldplug(Unit *u) {
static void path_enter_dead(Path *p, PathResult f) {
assert(p);
- if (f != PATH_SUCCESS)
+ if (p->result == PATH_SUCCESS)
p->result = f;
path_set_state(p, p->result != PATH_SUCCESS ? PATH_FAILED : PATH_DEAD);
diff --git a/src/core/scope.c b/src/core/scope.c
index b45e238974..b278aed3d6 100644
--- a/src/core/scope.c
+++ b/src/core/scope.c
@@ -221,7 +221,7 @@ static void scope_dump(Unit *u, FILE *f, const char *prefix) {
static void scope_enter_dead(Scope *s, ScopeResult f) {
assert(s);
- if (f != SCOPE_SUCCESS)
+ if (s->result == SCOPE_SUCCESS)
s->result = f;
scope_set_state(s, s->result != SCOPE_SUCCESS ? SCOPE_FAILED : SCOPE_DEAD);
@@ -233,7 +233,7 @@ static void scope_enter_signal(Scope *s, ScopeState state, ScopeResult f) {
assert(s);
- if (f != SCOPE_SUCCESS)
+ if (s->result == SCOPE_SUCCESS)
s->result = f;
unit_watch_all_pids(UNIT(s));
diff --git a/src/core/service.c b/src/core/service.c
index eb125cb999..4a37702f52 100644
--- a/src/core/service.c
+++ b/src/core/service.c
@@ -761,6 +761,11 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) {
prefix, s->bus_name,
prefix, yes_no(s->bus_name_good));
+ if (UNIT_ISSET(s->accept_socket))
+ fprintf(f,
+ "%sAccept Socket: %s\n",
+ prefix, UNIT_DEREF(s->accept_socket)->id);
+
kill_context_dump(&s->kill_context, f, prefix);
exec_context_dump(&s->exec_context, f, prefix);
@@ -1036,6 +1041,20 @@ static int service_coldplug(Unit *u) {
if (!IN_SET(s->deserialized_state, SERVICE_DEAD, SERVICE_FAILED, SERVICE_AUTO_RESTART))
(void) unit_setup_dynamic_creds(u);
+ if (UNIT_ISSET(s->accept_socket)) {
+ Socket* socket = SOCKET(UNIT_DEREF(s->accept_socket));
+
+ if (socket->max_connections_per_source > 0) {
+ SocketPeer *peer;
+
+ /* Make a best-effort attempt at bumping the connection count */
+ if (socket_acquire_peer(socket, s->socket_fd, &peer) > 0) {
+ socket_peer_unref(s->peer);
+ s->peer = peer;
+ }
+ }
+ }
+
service_set_state(s, s->deserialized_state);
return 0;
}
@@ -1152,11 +1171,7 @@ static int service_spawn(
Service *s,
ExecCommand *c,
usec_t timeout,
- bool pass_fds,
- bool apply_permissions,
- bool apply_chroot,
- bool apply_tty_stdin,
- bool is_control,
+ ExecFlags flags,
pid_t *_pid) {
_cleanup_strv_free_ char **argv = NULL, **final_env = NULL, **our_env = NULL, **fd_names = NULL;
@@ -1166,12 +1181,10 @@ static int service_spawn(
pid_t pid;
ExecParameters exec_params = {
- .apply_permissions = apply_permissions,
- .apply_chroot = apply_chroot,
- .apply_tty_stdin = apply_tty_stdin,
- .stdin_fd = -1,
- .stdout_fd = -1,
- .stderr_fd = -1,
+ .flags = flags,
+ .stdin_fd = -1,
+ .stdout_fd = -1,
+ .stderr_fd = -1,
};
int r;
@@ -1180,6 +1193,14 @@ static int service_spawn(
assert(c);
assert(_pid);
+ if (flags & EXEC_IS_CONTROL) {
+ /* If this is a control process, mask the permissions/chroot application if this is requested. */
+ if (s->permissions_start_only)
+ exec_params.flags &= ~EXEC_APPLY_PERMISSIONS;
+ if (s->root_directory_start_only)
+ exec_params.flags &= ~EXEC_APPLY_CHROOT;
+ }
+
(void) unit_realize_cgroup(UNIT(s));
if (s->reset_cpu_usage) {
(void) unit_reset_cpu_usage(UNIT(s));
@@ -1194,7 +1215,7 @@ static int service_spawn(
if (r < 0)
return r;
- if (pass_fds ||
+ if ((flags & EXEC_PASS_FDS) ||
s->exec_context.std_input == EXEC_INPUT_SOCKET ||
s->exec_context.std_output == EXEC_OUTPUT_SOCKET ||
s->exec_context.std_error == EXEC_OUTPUT_SOCKET) {
@@ -1214,11 +1235,11 @@ static int service_spawn(
if (r < 0)
return r;
- our_env = new0(char*, 6);
+ our_env = new0(char*, 9);
if (!our_env)
return -ENOMEM;
- if (is_control ? s->notify_access == NOTIFY_ALL : s->notify_access != NOTIFY_NONE)
+ if ((flags & EXEC_IS_CONTROL) ? s->notify_access == NOTIFY_ALL : s->notify_access != NOTIFY_NONE)
if (asprintf(our_env + n_env++, "NOTIFY_SOCKET=%s", UNIT(s)->manager->notify_socket) < 0)
return -ENOMEM;
@@ -1226,7 +1247,7 @@ static int service_spawn(
if (asprintf(our_env + n_env++, "MAINPID="PID_FMT, s->main_pid) < 0)
return -ENOMEM;
- if (!MANAGER_IS_SYSTEM(UNIT(s)->manager))
+ if (MANAGER_IS_USER(UNIT(s)->manager))
if (asprintf(our_env + n_env++, "MANAGERPID="PID_FMT, getpid()) < 0)
return -ENOMEM;
@@ -1262,22 +1283,40 @@ static int service_spawn(
}
}
+ if (flags & EXEC_SETENV_RESULT) {
+ if (asprintf(our_env + n_env++, "SERVICE_RESULT=%s", service_result_to_string(s->result)) < 0)
+ return -ENOMEM;
+
+ if (s->main_exec_status.pid > 0 &&
+ dual_timestamp_is_set(&s->main_exec_status.exit_timestamp)) {
+ if (asprintf(our_env + n_env++, "EXIT_CODE=%s", sigchld_code_to_string(s->main_exec_status.code)) < 0)
+ return -ENOMEM;
+
+ if (s->main_exec_status.code == CLD_EXITED)
+ r = asprintf(our_env + n_env++, "EXIT_STATUS=%i", s->main_exec_status.status);
+ else
+ r = asprintf(our_env + n_env++, "EXIT_STATUS=%s", signal_to_string(s->main_exec_status.status));
+ if (r < 0)
+ return -ENOMEM;
+ }
+ }
+
final_env = strv_env_merge(2, UNIT(s)->manager->environment, our_env, NULL);
if (!final_env)
return -ENOMEM;
- if (is_control && UNIT(s)->cgroup_path) {
+ if ((flags & EXEC_IS_CONTROL) && UNIT(s)->cgroup_path) {
path = strjoina(UNIT(s)->cgroup_path, "/control");
(void) cg_create(SYSTEMD_CGROUP_CONTROLLER, path);
} else
path = UNIT(s)->cgroup_path;
exec_params.argv = argv;
+ exec_params.environment = final_env;
exec_params.fds = fds;
exec_params.fd_names = fd_names;
exec_params.n_fds = n_fds;
- exec_params.environment = final_env;
- exec_params.confirm_spawn = UNIT(s)->manager->confirm_spawn;
+ exec_params.flags |= UNIT(s)->manager->confirm_spawn ? EXEC_CONFIRM_SPAWN : 0;
exec_params.cgroup_supported = UNIT(s)->manager->cgroup_supported;
exec_params.cgroup_path = path;
exec_params.cgroup_delegate = s->cgroup_context.delegate;
@@ -1403,7 +1442,7 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart)
int r;
assert(s);
- if (f != SERVICE_SUCCESS)
+ if (s->result == SERVICE_SUCCESS)
s->result = f;
service_set_state(s, s->result != SERVICE_SUCCESS ? SERVICE_FAILED : SERVICE_DEAD);
@@ -1452,7 +1491,7 @@ static void service_enter_stop_post(Service *s, ServiceResult f) {
int r;
assert(s);
- if (f != SERVICE_SUCCESS)
+ if (s->result == SERVICE_SUCCESS)
s->result = f;
service_unwatch_control_pid(s);
@@ -1465,11 +1504,7 @@ static void service_enter_stop_post(Service *s, ServiceResult f) {
r = service_spawn(s,
s->control_command,
s->timeout_stop_usec,
- false,
- !s->permissions_start_only,
- !s->root_directory_start_only,
- true,
- true,
+ EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN|EXEC_IS_CONTROL|EXEC_SETENV_RESULT,
&s->control_pid);
if (r < 0)
goto fail;
@@ -1509,7 +1544,7 @@ static void service_enter_signal(Service *s, ServiceState state, ServiceResult f
assert(s);
- if (f != SERVICE_SUCCESS)
+ if (s->result == SERVICE_SUCCESS)
s->result = f;
unit_watch_all_pids(UNIT(s));
@@ -1567,7 +1602,7 @@ static void service_enter_stop(Service *s, ServiceResult f) {
assert(s);
- if (f != SERVICE_SUCCESS)
+ if (s->result == SERVICE_SUCCESS)
s->result = f;
service_unwatch_control_pid(s);
@@ -1580,11 +1615,7 @@ static void service_enter_stop(Service *s, ServiceResult f) {
r = service_spawn(s,
s->control_command,
s->timeout_stop_usec,
- false,
- !s->permissions_start_only,
- !s->root_directory_start_only,
- false,
- true,
+ EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_IS_CONTROL|EXEC_SETENV_RESULT,
&s->control_pid);
if (r < 0)
goto fail;
@@ -1623,7 +1654,7 @@ static bool service_good(Service *s) {
static void service_enter_running(Service *s, ServiceResult f) {
assert(s);
- if (f != SERVICE_SUCCESS)
+ if (s->result == SERVICE_SUCCESS)
s->result = f;
service_unwatch_control_pid(s);
@@ -1661,11 +1692,7 @@ static void service_enter_start_post(Service *s) {
r = service_spawn(s,
s->control_command,
s->timeout_start_usec,
- false,
- !s->permissions_start_only,
- !s->root_directory_start_only,
- false,
- true,
+ EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_IS_CONTROL,
&s->control_pid);
if (r < 0)
goto fail;
@@ -1735,11 +1762,7 @@ static void service_enter_start(Service *s) {
r = service_spawn(s,
c,
timeout,
- true,
- true,
- true,
- true,
- false,
+ EXEC_PASS_FDS|EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN|EXEC_SET_WATCHDOG,
&pid);
if (r < 0)
goto fail;
@@ -1798,11 +1821,7 @@ static void service_enter_start_pre(Service *s) {
r = service_spawn(s,
s->control_command,
s->timeout_start_usec,
- false,
- !s->permissions_start_only,
- !s->root_directory_start_only,
- true,
- true,
+ EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_IS_CONTROL|EXEC_APPLY_TTY_STDIN,
&s->control_pid);
if (r < 0)
goto fail;
@@ -1877,11 +1896,7 @@ static void service_enter_reload(Service *s) {
r = service_spawn(s,
s->control_command,
s->timeout_start_usec,
- false,
- !s->permissions_start_only,
- !s->root_directory_start_only,
- false,
- true,
+ EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_IS_CONTROL,
&s->control_pid);
if (r < 0)
goto fail;
@@ -1919,12 +1934,9 @@ static void service_run_next_control(Service *s) {
r = service_spawn(s,
s->control_command,
timeout,
- false,
- !s->permissions_start_only,
- !s->root_directory_start_only,
- s->control_command_id == SERVICE_EXEC_START_PRE ||
- s->control_command_id == SERVICE_EXEC_STOP_POST,
- true,
+ EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_IS_CONTROL|
+ (IN_SET(s->control_command_id, SERVICE_EXEC_START_PRE, SERVICE_EXEC_STOP_POST) ? EXEC_APPLY_TTY_STDIN : 0)|
+ (IN_SET(s->control_command_id, SERVICE_EXEC_STOP, SERVICE_EXEC_STOP_POST) ? EXEC_SETENV_RESULT : 0),
&s->control_pid);
if (r < 0)
goto fail;
@@ -1962,11 +1974,7 @@ static void service_run_next_main(Service *s) {
r = service_spawn(s,
s->main_command,
s->timeout_start_usec,
- true,
- true,
- true,
- true,
- false,
+ EXEC_PASS_FDS|EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN|EXEC_SET_WATCHDOG,
&pid);
if (r < 0)
goto fail;
@@ -2130,6 +2138,12 @@ static int service_serialize(Unit *u, FILE *f, FDSet *fds) {
if (r < 0)
return r;
+ if (UNIT_ISSET(s->accept_socket)) {
+ r = unit_serialize_item(u, f, "accept-socket", UNIT_DEREF(s->accept_socket)->id);
+ if (r < 0)
+ return r;
+ }
+
r = unit_serialize_item_fd(u, f, fds, "socket-fd", s->socket_fd);
if (r < 0)
return r;
@@ -2260,6 +2274,17 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
s->control_command_id = id;
s->control_command = s->exec_command[id];
}
+ } else if (streq(key, "accept-socket")) {
+ Unit *socket;
+
+ r = manager_load_unit(u->manager, value, NULL, NULL, &socket);
+ if (r < 0)
+ log_unit_debug_errno(u, r, "Failed to load accept-socket unit: %s", value);
+ else {
+ unit_ref_set(&s->accept_socket, socket);
+ SOCKET(socket)->n_connections++;
+ }
+
} else if (streq(key, "socket-fd")) {
int fd;
@@ -2620,7 +2645,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
"EXIT_STATUS=%i", status,
NULL);
- if (f != SERVICE_SUCCESS)
+ if (s->result == SERVICE_SUCCESS)
s->result = f;
if (s->main_command &&
@@ -2701,7 +2726,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
"Control process exited, code=%s status=%i",
sigchld_code_to_string(code), status);
- if (f != SERVICE_SUCCESS)
+ if (s->result == SERVICE_SUCCESS)
s->result = f;
/* Immediately get rid of the cgroup, so that the
diff --git a/src/core/socket.c b/src/core/socket.c
index ff55885fb3..50872e8366 100644
--- a/src/core/socket.c
+++ b/src/core/socket.c
@@ -59,6 +59,13 @@
#include "user-util.h"
#include "in-addr-util.h"
+struct SocketPeer {
+ unsigned n_ref;
+
+ Socket *socket;
+ union sockaddr_union peer;
+};
+
static const UnitActiveState state_translation_table[_SOCKET_STATE_MAX] = {
[SOCKET_DEAD] = UNIT_INACTIVE,
[SOCKET_START_PRE] = UNIT_ACTIVATING,
@@ -78,9 +85,6 @@ static const UnitActiveState state_translation_table[_SOCKET_STATE_MAX] = {
static int socket_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata);
static int socket_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata);
-SocketPeer *socket_peer_new(void);
-int socket_find_peer(Socket *s, int fd, SocketPeer **p);
-
static void socket_init(Unit *u) {
Socket *s = SOCKET(u);
@@ -151,10 +155,10 @@ static void socket_done(Unit *u) {
socket_free_ports(s);
- while ((p = hashmap_steal_first(s->peers_by_address)))
+ while ((p = set_steal_first(s->peers_by_address)))
p->socket = NULL;
- s->peers_by_address = hashmap_free(s->peers_by_address);
+ s->peers_by_address = set_free(s->peers_by_address);
s->exec_runtime = exec_runtime_unref(s->exec_runtime);
exec_command_free_array(s->exec_command, _SOCKET_EXEC_COMMAND_MAX);
@@ -482,10 +486,11 @@ static void peer_address_hash_func(const void *p, struct siphash *state) {
const SocketPeer *s = p;
assert(s);
+ assert(IN_SET(s->peer.sa.sa_family, AF_INET, AF_INET6));
if (s->peer.sa.sa_family == AF_INET)
siphash24_compress(&s->peer.in.sin_addr, sizeof(s->peer.in.sin_addr), state);
- else if (s->peer.sa.sa_family == AF_INET6)
+ else
siphash24_compress(&s->peer.in6.sin6_addr, sizeof(s->peer.in6.sin6_addr), state);
}
@@ -503,8 +508,7 @@ static int peer_address_compare_func(const void *a, const void *b) {
case AF_INET6:
return memcmp(&x->peer.in6.sin6_addr, &y->peer.in6.sin6_addr, sizeof(x->peer.in6.sin6_addr));
}
-
- return -1;
+ assert_not_reached("Black sheep in the family!");
}
const struct hash_ops peer_address_hash_ops = {
@@ -519,7 +523,7 @@ static int socket_load(Unit *u) {
assert(u);
assert(u->load_state == UNIT_STUB);
- r = hashmap_ensure_allocated(&s->peers_by_address, &peer_address_hash_ops);
+ r = set_ensure_allocated(&s->peers_by_address, &peer_address_hash_ops);
if (r < 0)
return r;
@@ -537,6 +541,87 @@ static int socket_load(Unit *u) {
return socket_verify(s);
}
+static SocketPeer *socket_peer_new(void) {
+ SocketPeer *p;
+
+ p = new0(SocketPeer, 1);
+ if (!p)
+ return NULL;
+
+ p->n_ref = 1;
+
+ return p;
+}
+
+SocketPeer *socket_peer_ref(SocketPeer *p) {
+ if (!p)
+ return NULL;
+
+ assert(p->n_ref > 0);
+ p->n_ref++;
+
+ return p;
+}
+
+SocketPeer *socket_peer_unref(SocketPeer *p) {
+ if (!p)
+ return NULL;
+
+ assert(p->n_ref > 0);
+
+ p->n_ref--;
+
+ if (p->n_ref > 0)
+ return NULL;
+
+ if (p->socket)
+ set_remove(p->socket->peers_by_address, p);
+
+ return mfree(p);
+}
+
+int socket_acquire_peer(Socket *s, int fd, SocketPeer **p) {
+ _cleanup_(socket_peer_unrefp) SocketPeer *remote = NULL;
+ SocketPeer sa = {}, *i;
+ socklen_t salen = sizeof(sa.peer);
+ int r;
+
+ assert(fd >= 0);
+ assert(s);
+
+ r = getpeername(fd, &sa.peer.sa, &salen);
+ if (r < 0)
+ return log_error_errno(errno, "getpeername failed: %m");
+
+ if (!IN_SET(sa.peer.sa.sa_family, AF_INET, AF_INET6)) {
+ *p = NULL;
+ return 0;
+ }
+
+ i = set_get(s->peers_by_address, &sa);
+ if (i) {
+ *p = socket_peer_ref(i);
+ return 1;
+ }
+
+ remote = socket_peer_new();
+ if (!remote)
+ return log_oom();
+
+ remote->peer = sa.peer;
+
+ r = set_put(s->peers_by_address, remote);
+ if (r < 0)
+ return r;
+
+ remote->socket = s;
+
+ *p = remote;
+ remote = NULL;
+
+ return 1;
+}
+
_const_ static const char* listen_lookup(int family, int type) {
if (family == AF_NETLINK)
@@ -1664,12 +1749,10 @@ static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) {
pid_t pid;
int r;
ExecParameters exec_params = {
- .apply_permissions = true,
- .apply_chroot = true,
- .apply_tty_stdin = true,
- .stdin_fd = -1,
- .stdout_fd = -1,
- .stderr_fd = -1,
+ .flags = EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN,
+ .stdin_fd = -1,
+ .stdout_fd = -1,
+ .stderr_fd = -1,
};
assert(s);
@@ -1700,7 +1783,7 @@ static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) {
exec_params.argv = argv;
exec_params.environment = UNIT(s)->manager->environment;
- exec_params.confirm_spawn = UNIT(s)->manager->confirm_spawn;
+ exec_params.flags |= UNIT(s)->manager->confirm_spawn ? EXEC_CONFIRM_SPAWN : 0;
exec_params.cgroup_supported = UNIT(s)->manager->cgroup_supported;
exec_params.cgroup_path = UNIT(s)->cgroup_path;
exec_params.cgroup_delegate = s->cgroup_context.delegate;
@@ -1812,7 +1895,7 @@ fail:
static void socket_enter_dead(Socket *s, SocketResult f) {
assert(s);
- if (f != SOCKET_SUCCESS)
+ if (s->result == SOCKET_SUCCESS)
s->result = f;
socket_set_state(s, s->result != SOCKET_SUCCESS ? SOCKET_FAILED : SOCKET_DEAD);
@@ -1831,7 +1914,7 @@ static void socket_enter_stop_post(Socket *s, SocketResult f) {
int r;
assert(s);
- if (f != SOCKET_SUCCESS)
+ if (s->result == SOCKET_SUCCESS)
s->result = f;
socket_unwatch_control_pid(s);
@@ -1859,7 +1942,7 @@ static void socket_enter_signal(Socket *s, SocketState state, SocketResult f) {
assert(s);
- if (f != SOCKET_SUCCESS)
+ if (s->result == SOCKET_SUCCESS)
s->result = f;
r = unit_kill_context(
@@ -1903,7 +1986,7 @@ static void socket_enter_stop_pre(Socket *s, SocketResult f) {
int r;
assert(s);
- if (f != SOCKET_SUCCESS)
+ if (s->result == SOCKET_SUCCESS)
s->result = f;
socket_unwatch_control_pid(s);
@@ -2102,22 +2185,26 @@ static void socket_enter_running(Socket *s, int cfd) {
Service *service;
if (s->n_connections >= s->max_connections) {
- log_unit_warning(UNIT(s), "Too many incoming connections (%u), refusing connection attempt.", s->n_connections);
+ log_unit_warning(UNIT(s), "Too many incoming connections (%u), dropping connection.",
+ s->n_connections);
safe_close(cfd);
return;
}
if (s->max_connections_per_source > 0) {
- r = socket_find_peer(s, cfd, &p);
+ r = socket_acquire_peer(s, cfd, &p);
if (r < 0) {
safe_close(cfd);
return;
- }
+ } else if (r > 0 && p->n_ref > s->max_connections_per_source) {
+ _cleanup_free_ char *t = NULL;
+
+ sockaddr_pretty(&p->peer.sa, FAMILY_ADDRESS_SIZE(p->peer.sa.sa_family), true, false, &t);
- if (p->n_ref > s->max_connections_per_source) {
- log_unit_warning(UNIT(s), "Too many incoming connections (%u) from source, refusing connection attempt.", p->n_ref);
+ log_unit_warning(UNIT(s),
+ "Too many incoming connections (%u) from source %s, dropping connection.",
+ p->n_ref, strnull(t));
safe_close(cfd);
- p = NULL;
return;
}
}
@@ -2163,10 +2250,8 @@ static void socket_enter_running(Socket *s, int cfd) {
cfd = -1; /* We passed ownership of the fd to the service now. Forget it here. */
s->n_connections++;
- if (s->max_connections_per_source > 0) {
- service->peer = socket_peer_ref(p);
- p = NULL;
- }
+ service->peer = p; /* Pass ownership of the peer reference */
+ p = NULL;
r = manager_add_job(UNIT(s)->manager, JOB_START, UNIT(service), JOB_REPLACE, &error, NULL);
if (r < 0) {
@@ -2313,9 +2398,7 @@ static int socket_stop(Unit *u) {
static int socket_serialize(Unit *u, FILE *f, FDSet *fds) {
Socket *s = SOCKET(u);
- SocketPeer *k;
SocketPort *p;
- Iterator i;
int r;
assert(u);
@@ -2366,19 +2449,14 @@ static int socket_serialize(Unit *u, FILE *f, FDSet *fds) {
}
}
- HASHMAP_FOREACH(k, s->peers_by_address, i) {
- _cleanup_free_ char *t = NULL;
-
- r = sockaddr_pretty(&k->peer.sa, FAMILY_ADDRESS_SIZE(k->peer.sa.sa_family), true, true, &t);
- if (r < 0)
- return r;
-
- unit_serialize_item_format(u, f, "peer", "%u %s", k->n_ref, t);
- }
-
return 0;
}
+static void socket_port_take_fd(SocketPort *p, FDSet *fds, int fd) {
+ safe_close(p->fd);
+ p->fd = fdset_remove(fds, fd);
+}
+
static int socket_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
Socket *s = SOCKET(u);
@@ -2433,18 +2511,13 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value,
if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd))
log_unit_debug(u, "Failed to parse fifo value: %s", value);
- else {
-
+ else
LIST_FOREACH(port, p, s->ports)
if (p->type == SOCKET_FIFO &&
- path_equal_or_files_same(p->path, value+skip))
+ path_equal_or_files_same(p->path, value+skip)) {
+ socket_port_take_fd(p, fds, fd);
break;
-
- if (p) {
- safe_close(p->fd);
- p->fd = fdset_remove(fds, fd);
- }
- }
+ }
} else if (streq(key, "special")) {
int fd, skip = 0;
@@ -2452,18 +2525,13 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value,
if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd))
log_unit_debug(u, "Failed to parse special value: %s", value);
- else {
-
+ else
LIST_FOREACH(port, p, s->ports)
if (p->type == SOCKET_SPECIAL &&
- path_equal_or_files_same(p->path, value+skip))
+ path_equal_or_files_same(p->path, value+skip)) {
+ socket_port_take_fd(p, fds, fd);
break;
-
- if (p) {
- safe_close(p->fd);
- p->fd = fdset_remove(fds, fd);
- }
- }
+ }
} else if (streq(key, "mqueue")) {
int fd, skip = 0;
@@ -2471,18 +2539,13 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value,
if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd))
log_unit_debug(u, "Failed to parse mqueue value: %s", value);
- else {
-
+ else
LIST_FOREACH(port, p, s->ports)
if (p->type == SOCKET_MQUEUE &&
- streq(p->path, value+skip))
+ streq(p->path, value+skip)) {
+ socket_port_take_fd(p, fds, fd);
break;
-
- if (p) {
- safe_close(p->fd);
- p->fd = fdset_remove(fds, fd);
- }
- }
+ }
} else if (streq(key, "socket")) {
int fd, type, skip = 0;
@@ -2490,17 +2553,12 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value,
if (sscanf(value, "%i %i %n", &fd, &type, &skip) < 2 || fd < 0 || type < 0 || !fdset_contains(fds, fd))
log_unit_debug(u, "Failed to parse socket value: %s", value);
- else {
-
+ else
LIST_FOREACH(port, p, s->ports)
- if (socket_address_is(&p->address, value+skip, type))
+ if (socket_address_is(&p->address, value+skip, type)) {
+ socket_port_take_fd(p, fds, fd);
break;
-
- if (p) {
- safe_close(p->fd);
- p->fd = fdset_remove(fds, fd);
- }
- }
+ }
} else if (streq(key, "netlink")) {
int fd, skip = 0;
@@ -2508,17 +2566,12 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value,
if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd))
log_unit_debug(u, "Failed to parse socket value: %s", value);
- else {
-
+ else
LIST_FOREACH(port, p, s->ports)
- if (socket_address_is_netlink(&p->address, value+skip))
+ if (socket_address_is_netlink(&p->address, value+skip)) {
+ socket_port_take_fd(p, fds, fd);
break;
-
- if (p) {
- safe_close(p->fd);
- p->fd = fdset_remove(fds, fd);
- }
- }
+ }
} else if (streq(key, "ffs")) {
int fd, skip = 0;
@@ -2526,46 +2579,14 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value,
if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd))
log_unit_debug(u, "Failed to parse ffs value: %s", value);
- else {
-
+ else
LIST_FOREACH(port, p, s->ports)
if (p->type == SOCKET_USB_FUNCTION &&
- path_equal_or_files_same(p->path, value+skip))
+ path_equal_or_files_same(p->path, value+skip)) {
+ socket_port_take_fd(p, fds, fd);
break;
+ }
- if (p) {
- safe_close(p->fd);
- p->fd = fdset_remove(fds, fd);
- }
- }
-
- } else if (streq(key, "peer")) {
- _cleanup_(socket_peer_unrefp) SocketPeer *p;
- int n_ref, skip = 0;
- SocketAddress a;
- int r;
-
- if (sscanf(value, "%u %n", &n_ref, &skip) < 1 || n_ref < 1)
- log_unit_debug(u, "Failed to parse socket peer value: %s", value);
- else {
- r = socket_address_parse(&a, value+skip);
- if (r < 0)
- return r;
-
- p = socket_peer_new();
- if (!p)
- return log_oom();
-
- p->n_ref = n_ref;
- memcpy(&p->peer, &a.sockaddr, sizeof(a.sockaddr));
- p->socket = s;
-
- r = hashmap_put(s->peers_by_address, p, p);
- if (r < 0)
- return r;
-
- p = NULL;
- }
} else
log_unit_debug(UNIT(s), "Unknown serialization key: %s", key);
@@ -2662,83 +2683,6 @@ _pure_ static bool socket_check_gc(Unit *u) {
return s->n_connections > 0;
}
-SocketPeer *socket_peer_new(void) {
- SocketPeer *p;
-
- p = new0(SocketPeer, 1);
- if (!p)
- return NULL;
-
- p->n_ref = 1;
-
- return p;
-}
-
-SocketPeer *socket_peer_ref(SocketPeer *p) {
- if (!p)
- return NULL;
-
- assert(p->n_ref > 0);
- p->n_ref++;
-
- return p;
-}
-
-SocketPeer *socket_peer_unref(SocketPeer *p) {
- if (!p)
- return NULL;
-
- assert(p->n_ref > 0);
-
- p->n_ref--;
-
- if (p->n_ref > 0)
- return NULL;
-
- if (p->socket)
- (void) hashmap_remove(p->socket->peers_by_address, p);
-
- free(p);
-
- return NULL;
-}
-
-int socket_find_peer(Socket *s, int fd, SocketPeer **p) {
- _cleanup_free_ SocketPeer *remote = NULL;
- SocketPeer sa, *i;
- socklen_t salen = sizeof(sa.peer);
- int r;
-
- assert(fd >= 0);
- assert(s);
-
- r = getpeername(fd, &sa.peer.sa, &salen);
- if (r < 0)
- return log_error_errno(errno, "getpeername failed: %m");
-
- i = hashmap_get(s->peers_by_address, &sa);
- if (i) {
- *p = i;
- return 1;
- }
-
- remote = socket_peer_new();
- if (!remote)
- return log_oom();
-
- memcpy(&remote->peer, &sa.peer, sizeof(union sockaddr_union));
- remote->socket = s;
-
- r = hashmap_put(s->peers_by_address, remote, remote);
- if (r < 0)
- return r;
-
- *p = remote;
- remote = NULL;
-
- return 0;
-}
-
static int socket_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
SocketPort *p = userdata;
int cfd = -1;
@@ -2824,7 +2768,7 @@ static void socket_sigchld_event(Unit *u, pid_t pid, int code, int status) {
"Control process exited, code=%s status=%i",
sigchld_code_to_string(code), status);
- if (f != SOCKET_SUCCESS)
+ if (s->result == SOCKET_SUCCESS)
s->result = f;
if (s->control_command &&
diff --git a/src/core/socket.h b/src/core/socket.h
index 2fe38ef2aa..89f4664510 100644
--- a/src/core/socket.h
+++ b/src/core/socket.h
@@ -80,7 +80,7 @@ struct Socket {
LIST_HEAD(SocketPort, ports);
- Hashmap *peers_by_address;
+ Set *peers_by_address;
unsigned n_accepted;
unsigned n_connections;
@@ -168,15 +168,9 @@ struct Socket {
RateLimit trigger_limit;
};
-struct SocketPeer {
- unsigned n_ref;
-
- Socket *socket;
- union sockaddr_union peer;
-};
-
SocketPeer *socket_peer_ref(SocketPeer *p);
SocketPeer *socket_peer_unref(SocketPeer *p);
+int socket_acquire_peer(Socket *s, int fd, SocketPeer **p);
DEFINE_TRIVIAL_CLEANUP_FUNC(SocketPeer*, socket_peer_unref);
diff --git a/src/core/swap.c b/src/core/swap.c
index 66a318d01f..2c802da3b5 100644
--- a/src/core/swap.c
+++ b/src/core/swap.c
@@ -611,12 +611,10 @@ static int swap_spawn(Swap *s, ExecCommand *c, pid_t *_pid) {
pid_t pid;
int r;
ExecParameters exec_params = {
- .apply_permissions = true,
- .apply_chroot = true,
- .apply_tty_stdin = true,
- .stdin_fd = -1,
- .stdout_fd = -1,
- .stderr_fd = -1,
+ .flags = EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN,
+ .stdin_fd = -1,
+ .stdout_fd = -1,
+ .stderr_fd = -1,
};
assert(s);
@@ -642,7 +640,7 @@ static int swap_spawn(Swap *s, ExecCommand *c, pid_t *_pid) {
goto fail;
exec_params.environment = UNIT(s)->manager->environment;
- exec_params.confirm_spawn = UNIT(s)->manager->confirm_spawn;
+ exec_params.flags |= UNIT(s)->manager->confirm_spawn ? EXEC_CONFIRM_SPAWN : 0;
exec_params.cgroup_supported = UNIT(s)->manager->cgroup_supported;
exec_params.cgroup_path = UNIT(s)->cgroup_path;
exec_params.cgroup_delegate = s->cgroup_context.delegate;
@@ -675,7 +673,7 @@ fail:
static void swap_enter_dead(Swap *s, SwapResult f) {
assert(s);
- if (f != SWAP_SUCCESS)
+ if (s->result == SWAP_SUCCESS)
s->result = f;
swap_set_state(s, s->result != SWAP_SUCCESS ? SWAP_FAILED : SWAP_DEAD);
@@ -691,7 +689,7 @@ static void swap_enter_dead(Swap *s, SwapResult f) {
static void swap_enter_active(Swap *s, SwapResult f) {
assert(s);
- if (f != SWAP_SUCCESS)
+ if (s->result == SWAP_SUCCESS)
s->result = f;
swap_set_state(s, SWAP_ACTIVE);
@@ -702,7 +700,7 @@ static void swap_enter_signal(Swap *s, SwapState state, SwapResult f) {
assert(s);
- if (f != SWAP_SUCCESS)
+ if (s->result == SWAP_SUCCESS)
s->result = f;
r = unit_kill_context(
@@ -999,7 +997,7 @@ static void swap_sigchld_event(Unit *u, pid_t pid, int code, int status) {
else
assert_not_reached("Unknown code");
- if (f != SWAP_SUCCESS)
+ if (s->result == SWAP_SUCCESS)
s->result = f;
if (s->control_command) {
diff --git a/src/core/timer.c b/src/core/timer.c
index 3206296f09..e2b43f02f8 100644
--- a/src/core/timer.c
+++ b/src/core/timer.c
@@ -291,7 +291,7 @@ static int timer_coldplug(Unit *u) {
static void timer_enter_dead(Timer *t, TimerResult f) {
assert(t);
- if (f != TIMER_SUCCESS)
+ if (t->result == TIMER_SUCCESS)
t->result = f;
timer_set_state(t, t->result != TIMER_SUCCESS ? TIMER_FAILED : TIMER_DEAD);
diff --git a/src/coredump/coredumpctl.c b/src/coredump/coredumpctl.c
index 27b1e0fb3f..bbf8793e57 100644
--- a/src/coredump/coredumpctl.c
+++ b/src/coredump/coredumpctl.c
@@ -30,6 +30,7 @@
#include "compress.h"
#include "fd-util.h"
#include "fileio.h"
+#include "fs-util.h"
#include "journal-internal.h"
#include "log.h"
#include "macro.h"
@@ -609,7 +610,13 @@ static int save_core(sd_journal *j, int fd, char **path, bool *unlink_temp) {
char *temp = NULL;
if (fd < 0) {
- temp = strdup("/var/tmp/coredump-XXXXXX");
+ const char *vt;
+
+ r = var_tmp_dir(&vt);
+ if (r < 0)
+ return log_error_errno(r, "Failed to acquire temporary directory path: %m");
+
+ temp = strjoin(vt, "/coredump-XXXXXX", NULL);
if (!temp)
return log_oom();
diff --git a/src/journal-remote/journal-gatewayd.c b/src/journal-remote/journal-gatewayd.c
index 4ad9184993..05c8698f78 100644
--- a/src/journal-remote/journal-gatewayd.c
+++ b/src/journal-remote/journal-gatewayd.c
@@ -19,9 +19,6 @@
#include <fcntl.h>
#include <getopt.h>
-#ifdef HAVE_GNUTLS
-#include <gnutls/gnutls.h>
-#endif
#include <microhttpd.h>
#include <stdlib.h>
#include <string.h>
@@ -48,6 +45,7 @@
static char *arg_key_pem = NULL;
static char *arg_cert_pem = NULL;
static char *arg_trust_pem = NULL;
+static char *arg_directory = NULL;
typedef struct RequestMeta {
sd_journal *journal;
@@ -118,7 +116,10 @@ static int open_journal(RequestMeta *m) {
if (m->journal)
return 0;
- return sd_journal_open(&m->journal, SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_SYSTEM);
+ if (arg_directory)
+ return sd_journal_open_directory(&m->journal, arg_directory, 0);
+ else
+ return sd_journal_open(&m->journal, SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_SYSTEM);
}
static int request_meta_ensure_tmp(RequestMeta *m) {
@@ -239,6 +240,9 @@ static ssize_t request_reader_entries(
m->size = (uint64_t) sz;
}
+ if (m->tmp == NULL && m->follow)
+ return 0;
+
if (fseeko(m->tmp, pos, SEEK_SET) < 0) {
log_error_errno(errno, "Failed to seek to position: %m");
return MHD_CONTENT_READER_END_WITH_ERROR;
@@ -881,7 +885,8 @@ static void help(void) {
" --version Show package version\n"
" --cert=CERT.PEM Server certificate in PEM format\n"
" --key=KEY.PEM Server key in PEM format\n"
- " --trust=CERT.PEM Certificat authority certificate in PEM format\n",
+ " --trust=CERT.PEM Certificat authority certificate in PEM format\n"
+ " -D --directory=PATH Serve journal files in directory\n",
program_invocation_short_name);
}
@@ -896,11 +901,12 @@ static int parse_argv(int argc, char *argv[]) {
int r, c;
static const struct option options[] = {
- { "help", no_argument, NULL, 'h' },
- { "version", no_argument, NULL, ARG_VERSION },
- { "key", required_argument, NULL, ARG_KEY },
- { "cert", required_argument, NULL, ARG_CERT },
- { "trust", required_argument, NULL, ARG_TRUST },
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, ARG_VERSION },
+ { "key", required_argument, NULL, ARG_KEY },
+ { "cert", required_argument, NULL, ARG_CERT },
+ { "trust", required_argument, NULL, ARG_TRUST },
+ { "directory", required_argument, NULL, 'D' },
{}
};
@@ -954,6 +960,9 @@ static int parse_argv(int argc, char *argv[]) {
#else
log_error("Option --trust is not available.");
#endif
+ case 'D':
+ arg_directory = optarg;
+ break;
case '?':
return -EINVAL;
diff --git a/src/journal-remote/journal-remote.c b/src/journal-remote/journal-remote.c
index f1ef90ed7a..80e2adb100 100644
--- a/src/journal-remote/journal-remote.c
+++ b/src/journal-remote/journal-remote.c
@@ -27,10 +27,6 @@
#include <sys/socket.h>
#include <unistd.h>
-#ifdef HAVE_GNUTLS
-#include <gnutls/gnutls.h>
-#endif
-
#include "sd-daemon.h"
#include "alloc-util.h"
diff --git a/src/journal/journal-verify.c b/src/journal/journal-verify.c
index f61f158e8a..4105abfccc 100644
--- a/src/journal/journal-verify.c
+++ b/src/journal/journal-verify.c
@@ -826,7 +826,7 @@ int journal_file_verify(
int data_fd = -1, entry_fd = -1, entry_array_fd = -1;
unsigned i;
bool found_last = false;
- _cleanup_free_ char *tmp_dir = NULL;
+ const char *tmp_dir = NULL;
#ifdef HAVE_GCRYPT
uint64_t last_tag = 0;
@@ -846,7 +846,7 @@ int journal_file_verify(
} else if (f->seal)
return -ENOKEY;
- r = var_tmp(&tmp_dir);
+ r = var_tmp_dir(&tmp_dir);
if (r < 0) {
log_error_errno(r, "Failed to determine temporary directory: %m");
goto fail;
diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c
index 53c6180864..6f841efb69 100644
--- a/src/journal/journalctl.c
+++ b/src/journal/journalctl.c
@@ -297,9 +297,9 @@ static void help(void) {
" -n --lines[=INTEGER] Number of journal entries to show\n"
" --no-tail Show all lines, even in follow mode\n"
" -r --reverse Show the newest entries first\n"
- " -o --output=STRING Change journal output mode (short, short-iso,\n"
- " short-precise, short-monotonic, verbose,\n"
- " export, json, json-pretty, json-sse, cat)\n"
+ " -o --output=STRING Change journal output mode (short, short-precise,\n"
+ " short-iso, short-full, short-monotonic, short-unix,\n"
+ " verbose, export, json, json-pretty, json-sse, cat)\n"
" --utc Express time in Coordinated Universal Time (UTC)\n"
" -x --catalog Add message explanations where available\n"
" --no-full Ellipsize fields\n"
diff --git a/src/journal/lookup3.c b/src/journal/lookup3.c
index 3d791234f4..d8f1a4977d 100644
--- a/src/journal/lookup3.c
+++ b/src/journal/lookup3.c
@@ -317,7 +317,7 @@ uint32_t jenkins_hashlittle( const void *key, size_t length, uint32_t initval)
* still catch it and complain. The masking trick does make the hash
* noticeably faster for short strings (like English words).
*/
-#ifndef VALGRIND
+#if !defined(VALGRIND) && !defined(__SANITIZE_ADDRESS__)
switch(length)
{
@@ -503,7 +503,7 @@ void jenkins_hashlittle2(
* still catch it and complain. The masking trick does make the hash
* noticeably faster for short strings (like English words).
*/
-#ifndef VALGRIND
+#if !defined(VALGRIND) && !defined(__SANITIZE_ADDRESS__)
switch(length)
{
@@ -681,7 +681,7 @@ uint32_t jenkins_hashbig( const void *key, size_t length, uint32_t initval)
* still catch it and complain. The masking trick does make the hash
* noticeably faster for short strings (like English words).
*/
-#ifndef VALGRIND
+#if !defined(VALGRIND) && !defined(__SANITIZE_ADDRESS__)
switch(length)
{
diff --git a/src/machine/machined-dbus.c b/src/machine/machined-dbus.c
index 1923e8b971..5e2462cba2 100644
--- a/src/machine/machined-dbus.c
+++ b/src/machine/machined-dbus.c
@@ -954,7 +954,7 @@ static int method_clean_pool(sd_bus_message *message, void *userdata, sd_bus_err
/* Create a temporary file we can dump information about deleted images into. We use a temporary file for this
* instead of a pipe or so, since this might grow quit large in theory and we don't want to process this
* continuously */
- result_fd = open_tmpfile_unlinkable("/tmp/", O_RDWR|O_CLOEXEC);
+ result_fd = open_tmpfile_unlinkable(NULL, O_RDWR|O_CLOEXEC);
if (result_fd < 0)
return -errno;
diff --git a/src/network/networkd-brvlan.c b/src/network/networkd-brvlan.c
index 8bc330ebae..18ecd86858 100644
--- a/src/network/networkd-brvlan.c
+++ b/src/network/networkd-brvlan.c
@@ -257,6 +257,24 @@ static int parse_vid_range(const char *rvalue, uint16_t *vid, uint16_t *vid_end)
return r;
}
+int config_parse_brvlan_pvid(const char *unit, const char *filename,
+ unsigned line, const char *section,
+ unsigned section_line, const char *lvalue,
+ int ltype, const char *rvalue, void *data,
+ void *userdata) {
+ Network *network = userdata;
+ int r;
+ uint16_t pvid;
+ r = parse_vlanid(rvalue, &pvid);
+ if (r < 0)
+ return r;
+
+ network->pvid = pvid;
+ network->use_br_vlan = true;
+
+ return 0;
+}
+
int config_parse_brvlan_vlan(const char *unit, const char *filename,
unsigned line, const char *section,
unsigned section_line, const char *lvalue,
@@ -288,6 +306,7 @@ int config_parse_brvlan_vlan(const char *unit, const char *filename,
for (; vid <= vid_end; vid++)
set_bit(vid, network->br_vid_bitmap);
}
+ network->use_br_vlan = true;
return 0;
}
@@ -325,5 +344,6 @@ int config_parse_brvlan_untagged(const char *unit, const char *filename,
set_bit(vid, network->br_untagged_bitmap);
}
}
+ network->use_br_vlan = true;
return 0;
}
diff --git a/src/network/networkd-brvlan.h b/src/network/networkd-brvlan.h
index 6aa6883bfc..b37633f94f 100644
--- a/src/network/networkd-brvlan.h
+++ b/src/network/networkd-brvlan.h
@@ -25,5 +25,6 @@ typedef struct Link Link;
int br_vlan_configure(Link *link, uint16_t pvid, uint32_t *br_vid_bitmap, uint32_t *br_untagged_bitmap);
+int config_parse_brvlan_pvid(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_brvlan_vlan(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_brvlan_untagged(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);
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index a0da697707..69ee7424ce 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -1314,6 +1314,65 @@ int link_set_mtu(Link *link, uint32_t mtu) {
return 0;
}
+static int set_flags_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
+ _cleanup_link_unref_ Link *link = userdata;
+ int r;
+
+ assert(m);
+ assert(link);
+ assert(link->ifname);
+
+ if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
+ return 1;
+
+ r = sd_netlink_message_get_errno(m);
+ if (r < 0)
+ log_link_warning_errno(link, r, "Could not set link flags: %m");
+
+ return 1;
+}
+
+static int link_set_flags(Link *link) {
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
+ unsigned ifi_change = 0;
+ unsigned ifi_flags = 0;
+ int r;
+
+ assert(link);
+ assert(link->manager);
+ assert(link->manager->rtnl);
+
+ if (link->flags & IFF_LOOPBACK)
+ return 0;
+
+ if (!link->network)
+ return 0;
+
+ if (link->network->arp < 0)
+ return 0;
+
+ r = sd_rtnl_message_new_link(link->manager->rtnl, &req, RTM_SETLINK, link->ifindex);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not allocate RTM_SETLINK message: %m");
+
+ if (link->network->arp >= 0) {
+ ifi_change |= IFF_NOARP;
+ ifi_flags |= link->network->arp ? 0 : IFF_NOARP;
+ }
+
+ r = sd_rtnl_message_link_set_flags(req, ifi_flags, ifi_change);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not set link flags: %m");
+
+ r = sd_netlink_call_async(link->manager->rtnl, req, set_flags_handler, link, 0, NULL);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
+
+ link_ref(link);
+
+ return 0;
+}
+
static int link_set_bridge(Link *link) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
int r;
@@ -2001,7 +2060,8 @@ static int link_joined(Link *link) {
log_link_error_errno(link, r, "Could not set bridge message: %m");
}
- if (link->network->bridge || streq_ptr("bridge", link->kind)) {
+ if (link->network->use_br_vlan &&
+ (link->network->bridge || streq_ptr("bridge", link->kind))) {
r = link_set_bridge_vlan(link);
if (r < 0)
log_link_error_errno(link, r, "Could not set bridge vlan: %m");
@@ -2314,6 +2374,35 @@ static int link_drop_foreign_config(Link *link) {
return 0;
}
+static int link_drop_config(Link *link) {
+ Address *address;
+ Route *route;
+ Iterator i;
+ int r;
+
+ SET_FOREACH(address, link->addresses, i) {
+ /* we consider IPv6LL addresses to be managed by the kernel */
+ if (address->family == AF_INET6 && in_addr_is_link_local(AF_INET6, &address->in_addr) == 1)
+ continue;
+
+ r = address_remove(address, link, link_address_remove_handler);
+ if (r < 0)
+ return r;
+ }
+
+ SET_FOREACH(route, link->routes, i) {
+ /* do not touch routes managed by the kernel */
+ if (route->protocol == RTPROT_KERNEL)
+ continue;
+
+ r = route_remove(route, link, link_route_remove_handler);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
static int link_update_lldp(Link *link) {
int r;
@@ -2382,6 +2471,10 @@ static int link_configure(Link *link) {
if (r < 0)
return r;
+ r = link_set_flags(link);
+ if (r < 0)
+ return r;
+
if (link_ipv4ll_enabled(link)) {
r = ipv4ll_configure(link);
if (r < 0)
@@ -2860,6 +2953,14 @@ static int link_carrier_lost(Link *link) {
return r;
}
+ r = link_drop_config(link);
+ if (r < 0)
+ return r;
+
+ r = link_drop_foreign_config(link);
+ if (r < 0)
+ return r;
+
r = link_handle_bound_by_list(link);
if (r < 0)
return r;
diff --git a/src/network/networkd-netdev-bridge.c b/src/network/networkd-netdev-bridge.c
index a5085d2b19..12b0fe972f 100644
--- a/src/network/networkd-netdev-bridge.c
+++ b/src/network/networkd-netdev-bridge.c
@@ -108,6 +108,12 @@ static int netdev_bridge_post_create(NetDev *netdev, Link *link, sd_netlink_mess
return log_netdev_error_errno(netdev, r, "Could not append IFLA_BR_VLAN_FILTERING attribute: %m");
}
+ if (b->stp >= 0) {
+ r = sd_netlink_message_append_u32(req, IFLA_BR_STP_STATE, b->stp);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_BR_STP_STATE attribute: %m");
+ }
+
r = sd_netlink_message_close_container(req);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not append IFLA_LINKINFO attribute: %m");
@@ -135,6 +141,7 @@ static void bridge_init(NetDev *n) {
b->mcast_querier = -1;
b->mcast_snooping = -1;
b->vlan_filtering = -1;
+ b->stp = -1;
}
const NetDevVTable bridge_vtable = {
diff --git a/src/network/networkd-netdev-bridge.h b/src/network/networkd-netdev-bridge.h
index a637aea0a3..4ce0fbb6f9 100644
--- a/src/network/networkd-netdev-bridge.h
+++ b/src/network/networkd-netdev-bridge.h
@@ -27,6 +27,7 @@ typedef struct Bridge {
int mcast_querier;
int mcast_snooping;
int vlan_filtering;
+ int stp;
usec_t forward_delay;
usec_t hello_time;
diff --git a/src/network/networkd-netdev-gperf.gperf b/src/network/networkd-netdev-gperf.gperf
index 9d69f61376..a1ca1a3d4e 100644
--- a/src/network/networkd-netdev-gperf.gperf
+++ b/src/network/networkd-netdev-gperf.gperf
@@ -106,4 +106,5 @@ Bridge.ForwardDelaySec, config_parse_sec, 0,
Bridge.MulticastQuerier, config_parse_tristate, 0, offsetof(Bridge, mcast_querier)
Bridge.MulticastSnooping, config_parse_tristate, 0, offsetof(Bridge, mcast_snooping)
Bridge.VLANFiltering, config_parse_tristate, 0, offsetof(Bridge, vlan_filtering)
+Bridge.STP, config_parse_tristate, 0, offsetof(Bridge, stp)
VRF.TableId, config_parse_uint32, 0, offsetof(Vrf, table_id)
diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf
index 5172a7b5e9..b96f0b7210 100644
--- a/src/network/networkd-network-gperf.gperf
+++ b/src/network/networkd-network-gperf.gperf
@@ -28,6 +28,7 @@ Match.KernelCommandLine, config_parse_net_condition,
Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(Network, match_arch)
Link.MACAddress, config_parse_hwaddr, 0, offsetof(Network, mac)
Link.MTUBytes, config_parse_iec_size, 0, offsetof(Network, mtu)
+Link.ARP, config_parse_tristate, 0, offsetof(Network, arp)
Network.Description, config_parse_string, 0, offsetof(Network, description)
Network.Bridge, config_parse_netdev, 0, offsetof(Network, bridge)
Network.Bond, config_parse_netdev, 0, offsetof(Network, bond)
@@ -114,7 +115,7 @@ Bridge.AllowPortToBeRoot, config_parse_bool,
Bridge.UnicastFlood, config_parse_bool, 0, offsetof(Network, unicast_flood)
BridgeFDB.MACAddress, config_parse_fdb_hwaddr, 0, 0
BridgeFDB.VLANId, config_parse_fdb_vlan_id, 0, 0
-BridgeVLAN.PVID, config_parse_vlanid, 0, offsetof(Network, pvid)
+BridgeVLAN.PVID, config_parse_brvlan_pvid, 0, 0
BridgeVLAN.VLAN, config_parse_brvlan_vlan, 0, 0
BridgeVLAN.EgressUntagged, config_parse_brvlan_untagged, 0, 0
/* backwards compatibility: do not add new entries to this section */
diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c
index 2b764d4f24..17bbe5de9f 100644
--- a/src/network/networkd-network.c
+++ b/src/network/networkd-network.c
@@ -134,6 +134,7 @@ static int network_load_one(Manager *manager, const char *filename) {
network->ipv6_hop_limit = -1;
network->duid.type = _DUID_TYPE_INVALID;
network->proxy_arp = -1;
+ network->arp = -1;
network->ipv6_accept_ra_use_dns = true;
r = config_parse(NULL, filename, file,
diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h
index 08ee939faa..5460eb4d1c 100644
--- a/src/network/networkd-network.h
+++ b/src/network/networkd-network.h
@@ -151,6 +151,7 @@ struct Network {
bool unicast_flood;
unsigned cost;
+ bool use_br_vlan;
uint16_t pvid;
uint32_t br_vid_bitmap[BRIDGE_VLAN_BITMAP_LEN];
uint32_t br_untagged_bitmap[BRIDGE_VLAN_BITMAP_LEN];
@@ -171,6 +172,7 @@ struct Network {
struct ether_addr *mac;
unsigned mtu;
+ int arp;
uint32_t iaid;
DUID duid;
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
index 6cc1b9177d..fcf14bba4c 100644
--- a/src/nspawn/nspawn.c
+++ b/src/nspawn/nspawn.c
@@ -216,10 +216,10 @@ static void help(void) {
" --uuid=UUID Set a specific machine UUID for the container\n"
" -S --slice=SLICE Place the container in the specified slice\n"
" --property=NAME=VALUE Set scope unit property\n"
- " -U --private-users=pick Run within user namespace, pick UID/GID range automatically\n"
+ " -U --private-users=pick Run within user namespace, autoselect UID/GID range\n"
" --private-users[=UIDBASE[:NUIDS]]\n"
- " Run within user namespace, user configured UID/GID range\n"
- " --private-user-chown Adjust OS tree file ownership for private UID/GID range\n"
+ " Similar, but with user configured UID/GID range\n"
+ " --private-user-chown Adjust OS tree ownership to private UID/GID range\n"
" --private-network Disable network in container\n"
" --network-interface=INTERFACE\n"
" Assign an existing network interface to the\n"
@@ -236,11 +236,10 @@ static void help(void) {
" Add an additional virtual Ethernet link between\n"
" host and container\n"
" --network-bridge=INTERFACE\n"
- " Add a virtual Ethernet connection between host\n"
- " and container and add it to an existing bridge on\n"
- " the host\n"
- " --network-zone=NAME Add a virtual Ethernet connection to the container,\n"
- " and add it to an automatically managed bridge interface\n"
+ " Add a virtual Ethernet connection to the container\n"
+ " and attach it to an existing bridge on the host\n"
+ " --network-zone=NAME Similar, but attach the new interface to an\n"
+ " an automatically managed bridge interface\n"
" -p --port=[PROTOCOL:]HOSTPORT[:CONTAINERPORT]\n"
" Expose a container IP port on the host\n"
" -Z --selinux-context=SECLABEL\n"
@@ -269,14 +268,12 @@ static void help(void) {
" --overlay-ro=PATH[:PATH...]:PATH\n"
" Similar, but creates a read-only overlay mount\n"
" -E --setenv=NAME=VALUE Pass an environment variable to PID 1\n"
- " --share-system Share system namespaces with host\n"
" --register=BOOLEAN Register container as machine\n"
" --keep-unit Do not register a scope for the machine, reuse\n"
" the service unit nspawn is running in\n"
" --volatile[=MODE] Run the system in volatile mode\n"
" --settings=BOOLEAN Load additional settings from .nspawn file\n"
- " --notify-ready=BOOLEAN Receive notifications from the container's init process,\n"
- " accepted values: yes and no\n"
+ " --notify-ready=BOOLEAN Receive notifications from the child init process\n"
, program_invocation_short_name);
}
@@ -405,7 +402,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "selinux-context", required_argument, NULL, 'Z' },
{ "selinux-apifs-context", required_argument, NULL, 'L' },
{ "quiet", no_argument, NULL, 'q' },
- { "share-system", no_argument, NULL, ARG_SHARE_SYSTEM },
+ { "share-system", no_argument, NULL, ARG_SHARE_SYSTEM }, /* not documented */
{ "register", required_argument, NULL, ARG_REGISTER },
{ "keep-unit", no_argument, NULL, ARG_KEEP_UNIT },
{ "network-interface", required_argument, NULL, ARG_NETWORK_INTERFACE },
@@ -814,6 +811,8 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_SHARE_SYSTEM:
+ /* We don't officially support this anymore, except for compat reasons. People should use the
+ * $SYSTEMD_NSPAWN_SHARE_SYSTEM environment variable instead. */
arg_share_system = true;
break;
@@ -1018,6 +1017,9 @@ static int parse_argv(int argc, char *argv[]) {
assert_not_reached("Unhandled option");
}
+ if (getenv_bool("SYSTEMD_NSPAWN_SHARE_SYSTEM") > 0)
+ arg_share_system = true;
+
if (arg_share_system)
arg_register = false;
@@ -1025,7 +1027,7 @@ static int parse_argv(int argc, char *argv[]) {
arg_userns_chown = true;
if (arg_start_mode != START_PID1 && arg_share_system) {
- log_error("--boot and --share-system may not be combined.");
+ log_error("--boot and SYSTEMD_NSPAWN_SHARE_SYSTEM=1 may not be combined.");
return -EINVAL;
}
@@ -1254,24 +1256,39 @@ static int setup_resolv_conf(const char *dest) {
/* Fix resolv.conf, if possible */
where = prefix_roota(dest, "/etc/resolv.conf");
+ if (access("/usr/lib/systemd/resolv.conf", F_OK) >= 0) {
+ /* resolved is enabled on the host. In this, case bind mount its static resolv.conf file into the
+ * container, so that the container can use the host's resolver. Given that network namespacing is
+ * disabled it's only natural of the container also uses the host's resolver. It also has the big
+ * advantage that the container will be able to follow the host's DNS server configuration changes
+ * transparently. */
+
+ if (mount("/usr/lib/systemd/resolv.conf", where, NULL, MS_BIND, NULL) < 0)
+ log_warning_errno(errno, "Failed to mount /etc/resolv.conf in the container, ignoring: %m");
+ else {
+ if (mount(NULL, where, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY|MS_NOSUID|MS_NODEV, NULL) < 0)
+ return log_error_errno(errno, "Failed to remount /etc/resolv.conf read-only: %m");
+
+ return 0;
+ }
+ }
+
+ /* If that didn't work, let's copy the file */
r = copy_file("/etc/resolv.conf", where, O_TRUNC|O_NOFOLLOW, 0644, 0);
if (r < 0) {
- /* If the file already exists as symlink, let's
- * suppress the warning, under the assumption that
- * resolved or something similar runs inside and the
- * symlink points there.
+ /* If the file already exists as symlink, let's suppress the warning, under the assumption that
+ * resolved or something similar runs inside and the symlink points there.
*
- * If the disk image is read-only, there's also no
- * point in complaining.
+ * If the disk image is read-only, there's also no point in complaining.
*/
log_full_errno(IN_SET(r, -ELOOP, -EROFS) ? LOG_DEBUG : LOG_WARNING, r,
- "Failed to copy /etc/resolv.conf to %s: %m", where);
+ "Failed to copy /etc/resolv.conf to %s, ignoring: %m", where);
return 0;
}
r = userns_lchown(where, 0, 0);
if (r < 0)
- log_warning_errno(r, "Failed to chown /etc/resolv.conf: %m");
+ log_warning_errno(r, "Failed to chown /etc/resolv.conf, ignoring: %m");
return 0;
}
@@ -1301,7 +1318,7 @@ static int setup_boot_id(const char *dest) {
if (mount(from, to, NULL, MS_BIND, NULL) < 0)
r = log_error_errno(errno, "Failed to bind mount boot id: %m");
else if (mount(NULL, to, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY|MS_NOSUID|MS_NODEV, NULL) < 0)
- log_warning_errno(errno, "Failed to make boot id read-only, ignoring: %m");
+ r = log_error_errno(errno, "Failed to make boot id read-only: %m");
(void) unlink(from);
return r;
diff --git a/src/nss-systemd/nss-systemd.c b/src/nss-systemd/nss-systemd.c
index e7a4393bb0..7078c0c50c 100644
--- a/src/nss-systemd/nss-systemd.c
+++ b/src/nss-systemd/nss-systemd.c
@@ -26,9 +26,52 @@
#include "macro.h"
#include "nss-util.h"
#include "signal-util.h"
+#include "string-util.h"
#include "user-util.h"
#include "util.h"
+#ifndef NOBODY_USER_NAME
+#define NOBODY_USER_NAME "nobody"
+#endif
+
+#ifndef NOBODY_GROUP_NAME
+#define NOBODY_GROUP_NAME "nobody"
+#endif
+
+static const struct passwd root_passwd = {
+ .pw_name = (char*) "root",
+ .pw_passwd = (char*) "x", /* see shadow file */
+ .pw_uid = 0,
+ .pw_gid = 0,
+ .pw_gecos = (char*) "Super User",
+ .pw_dir = (char*) "/root",
+ .pw_shell = (char*) "/bin/sh",
+};
+
+static const struct passwd nobody_passwd = {
+ .pw_name = (char*) NOBODY_USER_NAME,
+ .pw_passwd = (char*) "*", /* locked */
+ .pw_uid = 65534,
+ .pw_gid = 65534,
+ .pw_gecos = (char*) "User Nobody",
+ .pw_dir = (char*) "/",
+ .pw_shell = (char*) "/sbin/nologin",
+};
+
+static const struct group root_group = {
+ .gr_name = (char*) "root",
+ .gr_gid = 0,
+ .gr_passwd = (char*) "x", /* see shadow file */
+ .gr_mem = (char*[]) { NULL },
+};
+
+static const struct group nobody_group = {
+ .gr_name = (char*) NOBODY_GROUP_NAME,
+ .gr_gid = 65534,
+ .gr_passwd = (char*) "*", /* locked */
+ .gr_mem = (char*[]) { NULL },
+};
+
NSS_GETPW_PROTOTYPES(systemd);
NSS_GETGR_PROTOTYPES(systemd);
@@ -50,6 +93,23 @@ enum nss_status _nss_systemd_getpwnam_r(
assert(name);
assert(pwd);
+ if (!valid_user_group_name(name)) {
+ r = -EINVAL;
+ goto fail;
+ }
+
+ /* Synthesize entries for the root and nobody users, in case they are missing in /etc/passwd */
+ if (streq(name, root_passwd.pw_name)) {
+ *pwd = root_passwd;
+ *errnop = 0;
+ return NSS_STATUS_SUCCESS;
+ }
+ if (streq(name, nobody_passwd.pw_name)) {
+ *pwd = nobody_passwd;
+ *errnop = 0;
+ return NSS_STATUS_SUCCESS;
+ }
+
/* Make sure that we don't go in circles when allocating a dynamic UID by checking our own database */
if (getenv_bool("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
goto not_found;
@@ -126,6 +186,18 @@ enum nss_status _nss_systemd_getpwuid_r(
goto fail;
}
+ /* Synthesize data for the root user and for nobody in case they are missing from /etc/passwd */
+ if (uid == root_passwd.pw_uid) {
+ *pwd = root_passwd;
+ *errnop = 0;
+ return NSS_STATUS_SUCCESS;
+ }
+ if (uid == nobody_passwd.pw_uid) {
+ *pwd = nobody_passwd;
+ *errnop = 0;
+ return NSS_STATUS_SUCCESS;
+ }
+
if (uid <= SYSTEM_UID_MAX)
goto not_found;
@@ -202,6 +274,23 @@ enum nss_status _nss_systemd_getgrnam_r(
assert(name);
assert(gr);
+ if (!valid_user_group_name(name)) {
+ r = -EINVAL;
+ goto fail;
+ }
+
+ /* Synthesize records for root and nobody, in case they are missing form /etc/group */
+ if (streq(name, root_group.gr_name)) {
+ *gr = root_group;
+ *errnop = 0;
+ return NSS_STATUS_SUCCESS;
+ }
+ if (streq(name, nobody_group.gr_name)) {
+ *gr = nobody_group;
+ *errnop = 0;
+ return NSS_STATUS_SUCCESS;
+ }
+
if (getenv_bool("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
goto not_found;
@@ -275,6 +364,18 @@ enum nss_status _nss_systemd_getgrgid_r(
goto fail;
}
+ /* Synthesize records for root and nobody, in case they are missing from /etc/group */
+ if (gid == root_group.gr_gid) {
+ *gr = root_group;
+ *errnop = 0;
+ return NSS_STATUS_SUCCESS;
+ }
+ if (gid == nobody_group.gr_gid) {
+ *gr = nobody_group;
+ *errnop = 0;
+ return NSS_STATUS_SUCCESS;
+ }
+
if (gid <= SYSTEM_GID_MAX)
goto not_found;
diff --git a/src/resolve/resolve-tool.c b/src/resolve/resolve-tool.c
index 6ae3750417..07e4cd7d1d 100644
--- a/src/resolve/resolve-tool.c
+++ b/src/resolve/resolve-tool.c
@@ -1542,7 +1542,7 @@ static void help(void) {
"%1$s [OPTIONS...] --statistics\n"
"%1$s [OPTIONS...] --reset-statistics\n"
"\n"
- "Resolve domain names, IPv4 and IPv6 addresses, DNS resource records, and services.\n\n"
+ "Resolve domain names, IPv4 and IPv6 addresses, DNS records, and services.\n\n"
" -h --help Show this help\n"
" --version Show package version\n"
" --no-pager Do not pipe output into a pager\n"
diff --git a/src/run/run.c b/src/run/run.c
index 58fa49a4d1..1917ffd857 100644
--- a/src/run/run.c
+++ b/src/run/run.c
@@ -257,11 +257,9 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_NICE:
- r = safe_atoi(optarg, &arg_nice);
- if (r < 0 || arg_nice < PRIO_MIN || arg_nice >= PRIO_MAX) {
- log_error("Failed to parse nice value");
- return -EINVAL;
- }
+ r = parse_nice(optarg, &arg_nice);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse nice value: %s", optarg);
arg_nice_set = true;
break;
diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c
index 9d8061b539..7774d607c7 100644
--- a/src/shared/bus-unit-util.c
+++ b/src/shared/bus-unit-util.c
@@ -84,7 +84,7 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
if (isempty(eq))
r = sd_bus_message_append(m, "sv", "CPUQuotaPerSecUSec", "t", USEC_INFINITY);
else {
- r = parse_percent(eq);
+ r = parse_percent_unbounded(eq);
if (r <= 0) {
log_error_errno(r, "CPU quota '%s' invalid.", eq);
return -EINVAL;
@@ -366,15 +366,13 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
}
} else if (streq(field, "Nice")) {
- int32_t i;
+ int n;
- r = safe_atoi32(eq, &i);
- if (r < 0) {
- log_error("Failed to parse %s value %s.", field, eq);
- return -EINVAL;
- }
+ r = parse_nice(eq, &n);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse nice value: %s", eq);
- r = sd_bus_message_append(m, "v", "i", i);
+ r = sd_bus_message_append(m, "v", "i", (int32_t) n);
} else if (STR_IN_SET(field, "Environment", "PassEnvironment")) {
const char *p;
diff --git a/src/shared/logs-show.c b/src/shared/logs-show.c
index d04728f505..f9d9c4ed62 100644
--- a/src/shared/logs-show.c
+++ b/src/shared/logs-show.c
@@ -45,6 +45,7 @@
#include "parse-util.h"
#include "process-util.h"
#include "sparse-endian.h"
+#include "stdio-util.h"
#include "string-table.h"
#include "string-util.h"
#include "terminal-util.h"
@@ -206,6 +207,108 @@ static bool print_multiline(FILE *f, unsigned prefix, unsigned n_columns, Output
return ellipsized;
}
+static int output_timestamp_monotonic(FILE *f, sd_journal *j, const char *monotonic) {
+ sd_id128_t boot_id;
+ uint64_t t;
+ int r;
+
+ assert(f);
+ assert(j);
+
+ r = -ENXIO;
+ if (monotonic)
+ r = safe_atou64(monotonic, &t);
+ if (r < 0)
+ r = sd_journal_get_monotonic_usec(j, &t, &boot_id);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get monotonic timestamp: %m");
+
+ fprintf(f, "[%5llu.%06llu]",
+ (unsigned long long) (t / USEC_PER_SEC),
+ (unsigned long long) (t % USEC_PER_SEC));
+
+ return 1 + 5 + 1 + 6 + 1;
+}
+
+static int output_timestamp_realtime(FILE *f, sd_journal *j, OutputMode mode, OutputFlags flags, const char *realtime) {
+ char buf[MAX(FORMAT_TIMESTAMP_MAX, 64)];
+ struct tm *(*gettime_r)(const time_t *, struct tm *);
+ struct tm tm;
+ uint64_t x;
+ time_t t;
+ int r;
+
+ assert(f);
+ assert(j);
+
+ r = -ENXIO;
+ if (realtime)
+ r = safe_atou64(realtime, &x);
+ if (r < 0)
+ r = sd_journal_get_realtime_usec(j, &x);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get realtime timestamp: %m");
+
+ if (mode == OUTPUT_SHORT_FULL) {
+ const char *k;
+
+ if (flags & OUTPUT_UTC)
+ k = format_timestamp_utc(buf, sizeof(buf), x);
+ else
+ k = format_timestamp(buf, sizeof(buf), x);
+ if (!k) {
+ log_error("Failed to format timestamp.");
+ return -EINVAL;
+ }
+
+ } else {
+ gettime_r = (flags & OUTPUT_UTC) ? gmtime_r : localtime_r;
+ t = (time_t) (x / USEC_PER_SEC);
+
+ switch (mode) {
+
+ case OUTPUT_SHORT_UNIX:
+ xsprintf(buf, "%10llu.%06llu", (unsigned long long) t, (unsigned long long) (x % USEC_PER_SEC));
+ break;
+
+ case OUTPUT_SHORT_ISO:
+ if (strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S%z", gettime_r(&t, &tm)) <= 0) {
+ log_error("Failed for format ISO time");
+ return -EINVAL;
+ }
+ break;
+
+ case OUTPUT_SHORT:
+ case OUTPUT_SHORT_PRECISE:
+
+ if (strftime(buf, sizeof(buf), "%b %d %H:%M:%S", gettime_r(&t, &tm)) <= 0) {
+ log_error("Failed to format syslog time");
+ return -EINVAL;
+ }
+
+ if (mode == OUTPUT_SHORT_PRECISE) {
+ size_t k;
+
+ assert(sizeof(buf) > strlen(buf));
+ k = sizeof(buf) - strlen(buf);
+
+ r = snprintf(buf + strlen(buf), k, ".%06llu", (unsigned long long) (x % USEC_PER_SEC));
+ if (r <= 0 || (size_t) r >= k) { /* too long? */
+ log_error("Failed to format precise time");
+ return -EINVAL;
+ }
+ }
+ break;
+
+ default:
+ assert_not_reached("Unknown time format");
+ }
+ }
+
+ fputs(buf, f);
+ return (int) strlen(buf);
+}
+
static int output_short(
FILE *f,
sd_journal *j,
@@ -305,78 +408,15 @@ static int output_short(
if (priority_len == 1 && *priority >= '0' && *priority <= '7')
p = *priority - '0';
- if (mode == OUTPUT_SHORT_MONOTONIC) {
- uint64_t t;
- sd_id128_t boot_id;
-
- r = -ENOENT;
-
- if (monotonic)
- r = safe_atou64(monotonic, &t);
-
- if (r < 0)
- r = sd_journal_get_monotonic_usec(j, &t, &boot_id);
-
- if (r < 0)
- return log_error_errno(r, "Failed to get monotonic timestamp: %m");
-
- fprintf(f, "[%5llu.%06llu]",
- (unsigned long long) (t / USEC_PER_SEC),
- (unsigned long long) (t % USEC_PER_SEC));
-
- n += 1 + 5 + 1 + 6 + 1;
-
- } else {
- char buf[64];
- uint64_t x;
- time_t t;
- struct tm tm;
- struct tm *(*gettime_r)(const time_t *, struct tm *);
-
- r = -ENOENT;
- gettime_r = (flags & OUTPUT_UTC) ? gmtime_r : localtime_r;
-
- if (realtime)
- r = safe_atou64(realtime, &x);
-
- if (r < 0)
- r = sd_journal_get_realtime_usec(j, &x);
-
- if (r < 0)
- return log_error_errno(r, "Failed to get realtime timestamp: %m");
-
- t = (time_t) (x / USEC_PER_SEC);
-
- switch (mode) {
-
- case OUTPUT_SHORT_UNIX:
- r = snprintf(buf, sizeof(buf), "%10llu.%06llu", (unsigned long long) t, (unsigned long long) (x % USEC_PER_SEC));
- break;
-
- case OUTPUT_SHORT_ISO:
- r = strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S%z", gettime_r(&t, &tm));
- break;
-
- case OUTPUT_SHORT_PRECISE:
- r = strftime(buf, sizeof(buf), "%b %d %H:%M:%S", gettime_r(&t, &tm));
- if (r > 0)
- snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), ".%06llu", (unsigned long long) (x % USEC_PER_SEC));
- break;
-
- default:
- r = strftime(buf, sizeof(buf), "%b %d %H:%M:%S", gettime_r(&t, &tm));
- }
-
- if (r <= 0) {
- log_error("Failed to format time.");
- return -EINVAL;
- }
-
- fputs(buf, f);
- n += strlen(buf);
- }
+ if (mode == OUTPUT_SHORT_MONOTONIC)
+ r = output_timestamp_monotonic(f, j, monotonic);
+ else
+ r = output_timestamp_realtime(f, j, mode, flags, realtime);
+ if (r < 0)
+ return r;
+ n += r;
- if (hostname && (flags & OUTPUT_NO_HOSTNAME)) {
+ if (flags & OUTPUT_NO_HOSTNAME) {
/* Suppress display of the hostname if this is requested. */
hostname = NULL;
hostname_len = 0;
@@ -910,6 +950,7 @@ static int (*output_funcs[_OUTPUT_MODE_MAX])(
[OUTPUT_SHORT_PRECISE] = output_short,
[OUTPUT_SHORT_MONOTONIC] = output_short,
[OUTPUT_SHORT_UNIX] = output_short,
+ [OUTPUT_SHORT_FULL] = output_short,
[OUTPUT_VERBOSE] = output_verbose,
[OUTPUT_EXPORT] = output_export,
[OUTPUT_JSON] = output_json,
diff --git a/src/shared/output-mode.c b/src/shared/output-mode.c
index bec53ee0ae..67d8208ad2 100644
--- a/src/shared/output-mode.c
+++ b/src/shared/output-mode.c
@@ -22,6 +22,7 @@
static const char *const output_mode_table[_OUTPUT_MODE_MAX] = {
[OUTPUT_SHORT] = "short",
+ [OUTPUT_SHORT_FULL] = "short-full",
[OUTPUT_SHORT_ISO] = "short-iso",
[OUTPUT_SHORT_PRECISE] = "short-precise",
[OUTPUT_SHORT_MONOTONIC] = "short-monotonic",
diff --git a/src/shared/output-mode.h b/src/shared/output-mode.h
index f37189e57f..ff29dafcb5 100644
--- a/src/shared/output-mode.h
+++ b/src/shared/output-mode.h
@@ -23,6 +23,7 @@
typedef enum OutputMode {
OUTPUT_SHORT,
+ OUTPUT_SHORT_FULL,
OUTPUT_SHORT_ISO,
OUTPUT_SHORT_PRECISE,
OUTPUT_SHORT_MONOTONIC,
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index 4fa7adfc41..b4ce6fba5a 100644
--- a/src/systemctl/systemctl.c
+++ b/src/systemctl/systemctl.c
@@ -6591,9 +6591,9 @@ static void systemctl_help(void) {
" --preset-mode= Apply only enable, only disable, or all presets\n"
" --root=PATH Enable unit files in the specified root directory\n"
" -n --lines=INTEGER Number of journal entries to show\n"
- " -o --output=STRING Change journal output mode (short, short-iso,\n"
- " short-precise, short-monotonic, verbose,\n"
- " export, json, json-pretty, json-sse, cat)\n"
+ " -o --output=STRING Change journal output mode (short, short-precise,\n"
+ " short-iso, short-full, short-monotonic, short-unix,\n"
+ " verbose, export, json, json-pretty, json-sse, cat)\n"
" --firmware-setup Tell the firmware to show the setup menu on next boot\n"
" --plain Print unit dependencies as a list instead of a tree\n\n"
"Unit Commands:\n"
diff --git a/src/test/test-calendarspec.c b/src/test/test-calendarspec.c
index 4a2b93de59..57d9da4855 100644
--- a/src/test/test-calendarspec.c
+++ b/src/test/test-calendarspec.c
@@ -88,6 +88,27 @@ static void test_next(const char *input, const char *new_tz, usec_t after, usec_
tzset();
}
+static void test_timestamp(void) {
+ char buf[FORMAT_TIMESTAMP_MAX];
+ _cleanup_free_ char *t = NULL;
+ CalendarSpec *c;
+ usec_t x, y;
+
+ /* Ensure that a timestamp is also a valid calendar specification. Convert forth and back */
+
+ x = now(CLOCK_REALTIME);
+
+ assert_se(format_timestamp_us(buf, sizeof(buf), x));
+ printf("%s\n", buf);
+ assert_se(calendar_spec_from_string(buf, &c) >= 0);
+ assert_se(calendar_spec_to_string(c, &t) >= 0);
+ calendar_spec_free(c);
+ printf("%s\n", t);
+
+ assert_se(parse_timestamp(t, &y) >= 0);
+ assert_se(y == x);
+}
+
int main(int argc, char* argv[]) {
CalendarSpec *c;
@@ -155,5 +176,7 @@ int main(int argc, char* argv[]) {
assert_se(calendar_spec_from_string("00:00:00/0.00000001", &c) < 0);
assert_se(calendar_spec_from_string("00:00:00.0..00.9", &c) < 0);
+ test_timestamp();
+
return 0;
}
diff --git a/src/test/test-fs-util.c b/src/test/test-fs-util.c
index e0c040f39b..93eec3ef9c 100644
--- a/src/test/test-fs-util.c
+++ b/src/test/test-fs-util.c
@@ -83,47 +83,35 @@ static void test_get_files_in_directory(void) {
}
static void test_var_tmp(void) {
- char *tmp_dir = NULL;
- char *tmpdir_backup = NULL;
- const char *default_var_tmp = NULL;
- const char *var_name;
- bool do_overwrite = true;
-
- default_var_tmp = "/var/tmp";
- var_name = "TMPDIR";
-
- if (getenv(var_name) != NULL) {
- tmpdir_backup = strdup(getenv(var_name));
- assert_se(tmpdir_backup != NULL);
- }
-
- unsetenv(var_name);
+ _cleanup_free_ char *tmpdir_backup = NULL;
+ const char *tmp_dir = NULL, *t;
- var_tmp(&tmp_dir);
- assert_se(!strcmp(tmp_dir, default_var_tmp));
-
- free(tmp_dir);
+ t = getenv("TMPDIR");
+ if (t) {
+ tmpdir_backup = strdup(t);
+ assert_se(tmpdir_backup);
+ }
- setenv(var_name, "/tmp", do_overwrite);
- assert_se(!strcmp(getenv(var_name), "/tmp"));
+ assert(unsetenv("TMPDIR") >= 0);
- var_tmp(&tmp_dir);
- assert_se(!strcmp(tmp_dir, "/tmp"));
+ assert_se(var_tmp_dir(&tmp_dir) >= 0);
+ assert_se(streq(tmp_dir, "/var/tmp"));
- free(tmp_dir);
+ assert_se(setenv("TMPDIR", "/tmp", true) >= 0);
+ assert_se(streq(getenv("TMPDIR"), "/tmp"));
- setenv(var_name, "/88_does_not_exist_88", do_overwrite);
- assert_se(!strcmp(getenv(var_name), "/88_does_not_exist_88"));
+ assert_se(var_tmp_dir(&tmp_dir) >= 0);
+ assert_se(streq(tmp_dir, "/tmp"));
- var_tmp(&tmp_dir);
- assert_se(!strcmp(tmp_dir, default_var_tmp));
+ assert_se(setenv("TMPDIR", "/88_does_not_exist_88", true) >= 0);
+ assert_se(streq(getenv("TMPDIR"), "/88_does_not_exist_88"));
- free(tmp_dir);
+ assert_se(var_tmp_dir(&tmp_dir) >= 0);
+ assert_se(streq(tmp_dir, "/var/tmp"));
- if (tmpdir_backup != NULL) {
- setenv(var_name, tmpdir_backup, do_overwrite);
- assert_se(!strcmp(getenv(var_name), tmpdir_backup));
- free(tmpdir_backup);
+ if (tmpdir_backup) {
+ assert_se(setenv("TMPDIR", tmpdir_backup, true) >= 0);
+ assert_se(streq(getenv("TMPDIR"), tmpdir_backup));
}
}
diff --git a/src/test/test-id128.c b/src/test/test-id128.c
index f01fbdd6b2..1c8e5549da 100644
--- a/src/test/test-id128.c
+++ b/src/test/test-id128.c
@@ -144,7 +144,7 @@ int main(int argc, char *argv[]) {
assert_se(ftruncate(fd, 0) >= 0);
assert_se(sd_id128_randomize(&id) >= 0);
- assert_se(write(fd, id128_to_uuid_string(id, t), 36) == 36);
+ assert_se(write(fd, id128_to_uuid_string(id, q), 36) == 36);
assert_se(lseek(fd, 0, SEEK_SET) == 0);
assert_se(id128_read_fd(fd, ID128_PLAIN, &id2) == -EINVAL);
diff --git a/src/test/test-parse-util.c b/src/test/test-parse-util.c
index 0a76308f72..d08014100b 100644
--- a/src/test/test-parse-util.c
+++ b/src/test/test-parse-util.c
@@ -493,6 +493,39 @@ static void test_parse_percent(void) {
assert_se(parse_percent("1%%") == -EINVAL);
}
+static void test_parse_percent_unbounded(void) {
+ assert_se(parse_percent_unbounded("101%") == 101);
+ assert_se(parse_percent_unbounded("400%") == 400);
+}
+
+static void test_parse_nice(void) {
+ int n;
+
+ assert_se(parse_nice("0", &n) >= 0 && n == 0);
+ assert_se(parse_nice("+0", &n) >= 0 && n == 0);
+ assert_se(parse_nice("-1", &n) >= 0 && n == -1);
+ assert_se(parse_nice("-2", &n) >= 0 && n == -2);
+ assert_se(parse_nice("1", &n) >= 0 && n == 1);
+ assert_se(parse_nice("2", &n) >= 0 && n == 2);
+ assert_se(parse_nice("+1", &n) >= 0 && n == 1);
+ assert_se(parse_nice("+2", &n) >= 0 && n == 2);
+ assert_se(parse_nice("-20", &n) >= 0 && n == -20);
+ assert_se(parse_nice("19", &n) >= 0 && n == 19);
+ assert_se(parse_nice("+19", &n) >= 0 && n == 19);
+
+
+ assert_se(parse_nice("", &n) == -EINVAL);
+ assert_se(parse_nice("-", &n) == -EINVAL);
+ assert_se(parse_nice("+", &n) == -EINVAL);
+ assert_se(parse_nice("xx", &n) == -EINVAL);
+ assert_se(parse_nice("-50", &n) == -ERANGE);
+ assert_se(parse_nice("50", &n) == -ERANGE);
+ assert_se(parse_nice("+50", &n) == -ERANGE);
+ assert_se(parse_nice("-21", &n) == -ERANGE);
+ assert_se(parse_nice("20", &n) == -ERANGE);
+ assert_se(parse_nice("+20", &n) == -ERANGE);
+}
+
int main(int argc, char *argv[]) {
log_parse_environment();
log_open();
@@ -507,6 +540,8 @@ int main(int argc, char *argv[]) {
test_safe_atoi16();
test_safe_atod();
test_parse_percent();
+ test_parse_percent_unbounded();
+ test_parse_nice();
return 0;
}
diff --git a/src/test/test-sigbus.c b/src/test/test-sigbus.c
index 17b81747be..02b8e24308 100644
--- a/src/test/test-sigbus.c
+++ b/src/test/test-sigbus.c
@@ -29,6 +29,9 @@ int main(int argc, char *argv[]) {
void *addr = NULL;
uint8_t *p;
+#ifdef __SANITIZE_ADDRESS__
+ return EXIT_TEST_SKIP;
+#endif
sigbus_install();
assert_se(sigbus_pop(&addr) == 0);
diff --git a/src/test/test-time.c b/src/test/test-time.c
index ee7d55c5ab..7078a0374d 100644
--- a/src/test/test-time.c
+++ b/src/test/test-time.c
@@ -19,6 +19,7 @@
#include "strv.h"
#include "time-util.h"
+#include "random-util.h"
static void test_parse_sec(void) {
usec_t u;
@@ -201,6 +202,48 @@ static void test_usec_sub(void) {
assert_se(usec_sub(USEC_INFINITY, 5) == USEC_INFINITY);
}
+static void test_format_timestamp(void) {
+ unsigned i;
+
+ for (i = 0; i < 100; i++) {
+ char buf[MAX(FORMAT_TIMESTAMP_MAX, FORMAT_TIMESPAN_MAX)];
+ usec_t x, y;
+
+ random_bytes(&x, sizeof(x));
+ x = x % (2147483600 * USEC_PER_SEC) + 1;
+
+ assert_se(format_timestamp(buf, sizeof(buf), x));
+ log_info("%s", buf);
+ assert_se(parse_timestamp(buf, &y) >= 0);
+ assert_se(x / USEC_PER_SEC == y / USEC_PER_SEC);
+
+ assert_se(format_timestamp_utc(buf, sizeof(buf), x));
+ log_info("%s", buf);
+ assert_se(parse_timestamp(buf, &y) >= 0);
+ assert_se(x / USEC_PER_SEC == y / USEC_PER_SEC);
+
+ assert_se(format_timestamp_us(buf, sizeof(buf), x));
+ log_info("%s", buf);
+ assert_se(parse_timestamp(buf, &y) >= 0);
+ assert_se(x == y);
+
+ assert_se(format_timestamp_us_utc(buf, sizeof(buf), x));
+ log_info("%s", buf);
+ assert_se(parse_timestamp(buf, &y) >= 0);
+ assert_se(x == y);
+
+ assert_se(format_timestamp_relative(buf, sizeof(buf), x));
+ log_info("%s", buf);
+ assert_se(parse_timestamp(buf, &y) >= 0);
+
+ /* The two calls above will run with a slightly different local time. Make sure we are in the same
+ * range however, but give enough leeway that this is unlikely to explode. And of course,
+ * format_timestamp_relative() scales the accuracy with the distance from the current time up to one
+ * month, cover for that too. */
+ assert_se(y > x ? y - x : x - y <= USEC_PER_MONTH + USEC_PER_DAY);
+ }
+}
+
int main(int argc, char *argv[]) {
uintmax_t x;
@@ -214,6 +257,7 @@ int main(int argc, char *argv[]) {
test_get_timezones();
test_usec_add();
test_usec_sub();
+ test_format_timestamp();
/* Ensure time_t is signed */
assert_cc((time_t) -1 < (time_t) 1);