summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am4
-rw-r--r--NEWS10
-rw-r--r--TODO9
-rw-r--r--configure.ac2
-rw-r--r--man/systemd-journald.service.xml7
-rw-r--r--man/systemd.socket.xml9
-rw-r--r--man/systemd.time.xml5
-rw-r--r--man/systemd.timer.xml61
-rw-r--r--src/basic/calendarspec.c236
-rw-r--r--src/basic/calendarspec.h2
-rw-r--r--src/basic/parse-util.c36
-rw-r--r--src/basic/parse-util.h2
-rw-r--r--src/basic/time-util.c22
-rw-r--r--src/core/dbus-manager.c53
-rw-r--r--src/core/dbus-socket.c1
-rw-r--r--src/core/dbus-timer.c34
-rw-r--r--src/core/job.c73
-rw-r--r--src/core/load-fragment-gperf.gperf.m43
-rw-r--r--src/core/load-fragment.c31
-rw-r--r--src/core/load-fragment.h1
-rw-r--r--src/core/socket.c13
-rw-r--r--src/core/socket.h2
-rw-r--r--src/core/timer.c75
-rw-r--r--src/core/timer.h2
-rw-r--r--src/core/unit.c55
-rw-r--r--src/core/unit.h2
-rw-r--r--src/libsystemd-network/dhcp6-network.c22
-rw-r--r--src/libsystemd-network/sd-dhcp6-client.c19
-rw-r--r--src/libsystemd-network/sd-ndisc.c5
-rw-r--r--src/libsystemd-network/test-dhcp6-client.c6
-rw-r--r--src/network/networkd-address.c6
-rw-r--r--src/network/networkd-dhcp4.c3
-rw-r--r--src/network/networkd-dhcp6.c3
-rw-r--r--src/network/networkd-link.c26
-rw-r--r--src/network/networkd-link.h4
-rw-r--r--src/network/networkd-ndisc.c10
-rw-r--r--src/resolve/resolved-dns-scope.c6
-rw-r--r--src/resolve/resolved-dns-transaction.c14
-rw-r--r--src/resolve/resolved-dns-zone.c245
-rw-r--r--src/resolve/resolved-dns-zone.h2
-rw-r--r--src/run/run.c76
-rw-r--r--src/shared/bus-util.c32
-rw-r--r--src/systemctl/systemctl.c11
-rw-r--r--src/systemd/sd-dhcp6-client.h1
-rw-r--r--src/test/test-calendarspec.c13
-rw-r--r--sysctl.d/50-coredump.conf.in2
46 files changed, 859 insertions, 397 deletions
diff --git a/Makefile.am b/Makefile.am
index 5652a899a1..437905ce04 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -42,9 +42,9 @@ LIBUDEV_CURRENT=7
LIBUDEV_REVISION=4
LIBUDEV_AGE=6
-LIBSYSTEMD_CURRENT=12
+LIBSYSTEMD_CURRENT=13
LIBSYSTEMD_REVISION=0
-LIBSYSTEMD_AGE=12
+LIBSYSTEMD_AGE=13
# The following four libraries only exist for compatibility reasons,
# their version info should not be bumped anymore
diff --git a/NEWS b/NEWS
index 49862b352d..539093616e 100644
--- a/NEWS
+++ b/NEWS
@@ -88,6 +88,14 @@ CHANGES WITH 228:
from PID1's environment block into the environment block of
the service.
+ * Timer units gained support for a new RemainAfterElapse=
+ setting which takes a boolean argument. It defaults on on,
+ exposing behaviour unchanged to previous releases. If set to
+ off, timer units are unloaded after they elapsed if they
+ cannot elapse again. This is particularly useful for
+ transient timer units, which shall not stay around longer
+ than until they first elapse.
+
* systemd will now bump the net.unix.max_dgram_qlen to 512 by
default now (the kernel default is 16). This is beneficial
for avoiding blocking on AF_UNIX/SOCK_DGRAM sockets since it
@@ -188,7 +196,7 @@ CHANGES WITH 228:
Tom Gundersen, Torstein Husebø, Vito Caputo, Zbigniew
Jędrzejewski-Szmek
- -- Berlin, 2015-11-XX
+ -- Berlin, 2015-11-18
CHANGES WITH 227:
diff --git a/TODO b/TODO
index 10a20758ba..4a68fc5d60 100644
--- a/TODO
+++ b/TODO
@@ -71,8 +71,6 @@ Features:
* install: include generator dirs in unit file search paths
-* invent a better systemd-run scheme for naming scopes, that works with remoting
-
* rework C11 utf8.[ch] to use char32_t instead of uint32_t when referring
to unicode chars, to make things more expressive.
@@ -103,7 +101,7 @@ Features:
* Rework systemctl's GetAll property parsing to use the generic bus_map_all_properties() API
-* core/cgroup: support net_cls modules, and support automatically allocating class ids, then add support for making firewall changes depending on it, to implement a per-service firewall
+* implement a per-service firewall based on net_cls
* Port various tools to make use of verbs.[ch], where applicable
@@ -155,8 +153,6 @@ Features:
* maybe provide an API to allow migration of foreign PIDs into existing scopes.
-* maybe support a new very "soft" reboot mode, that simply kills all processes, disassembles everything, flushes /run and sysvipc, and then reexecs systemd again
-
* man: maybe use the word "inspect" rather than "introspect"?
* systemctl: if some operation fails, show log output?
@@ -213,8 +209,6 @@ Features:
* generator that automatically discovers btrfs subvolumes, identifies their purpose based on some xattr on them.
-* timer units: actually add extra delays to timer units with high AccuracySec values, don't start them already when we are awake...
-
* a way for container managers to turn off getty starting via $container_headless= or so...
* figure out a nice way how we can let the admin know what child/sibling unit causes cgroup membership for a specific unit
@@ -736,7 +730,6 @@ Features:
- Support --test based on current system state
- If we show an error about a unit (such as not showing up) and it has no Description string, then show a description string generated form the reverse of unit_name_mangle().
- after deserializing sockets in socket.c we should reapply sockopts and things
- - make timer units go away after they elapsed
- drop PID 1 reloading, only do reexecing (difficult: Reload()
currently is properly synchronous, Reexec() is weird, because we
cannot delay the response properly until we are back, so instead of
diff --git a/configure.ac b/configure.ac
index c96b9fb1d9..ec30ff12ae 100644
--- a/configure.ac
+++ b/configure.ac
@@ -20,7 +20,7 @@
AC_PREREQ([2.64])
AC_INIT([systemd],
- [227],
+ [228],
[http://github.com/systemd/systemd/issues],
[systemd],
[http://www.freedesktop.org/wiki/Software/systemd])
diff --git a/man/systemd-journald.service.xml b/man/systemd-journald.service.xml
index f1054b03bb..2810638bc2 100644
--- a/man/systemd-journald.service.xml
+++ b/man/systemd-journald.service.xml
@@ -106,13 +106,6 @@
<programlisting>mkdir -p /var/log/journal
systemd-tmpfiles --create --prefix /var/log/journal</programlisting>
- <para><filename>systemd-journald</filename> will forward all
- received log messages to the
- <constant>AF_UNIX</constant>/<constant>SOCK_DGRAM</constant>
- socket <filename>/run/systemd/journal/syslog</filename>, if it
- exists, which may be used by Unix syslog daemons to process the
- data further.</para>
-
<para>See
<citerefentry><refentrytitle>journald.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>
for information about the configuration of this service.</para>
diff --git a/man/systemd.socket.xml b/man/systemd.socket.xml
index beac053bf0..43841c2399 100644
--- a/man/systemd.socket.xml
+++ b/man/systemd.socket.xml
@@ -311,6 +311,15 @@
</varlistentry>
<varlistentry>
+ <term><varname>SocketProtocol=</varname></term>
+ <listitem><para>Takes a one of <option>udplite</option>
+ or <option>sctp</option>. Specifies a socket protocol
+ (<constant>IPPROTO_UDPLITE</constant>) UDP-Lite
+ (<constant>IPPROTO_SCTP</constant>) SCTP socket respectively. </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><varname>BindIPv6Only=</varname></term>
<listitem><para>Takes a one of <option>default</option>,
<option>both</option> or <option>ipv6-only</option>. Controls
diff --git a/man/systemd.time.xml b/man/systemd.time.xml
index a6fcc95e4c..ffcac82263 100644
--- a/man/systemd.time.xml
+++ b/man/systemd.time.xml
@@ -229,6 +229,10 @@
the value and all values plus multiples of the repetition value
are matched.</para>
+ <para>The seconds component may contain decimal fractions both in
+ the value and the repetition. All fractions are rounded to 6
+ decimal places.</para>
+
<para>Either time or date specification may be omitted, in which
case the current day and 00:00:00 is implied, respectively. If the
second component is not specified, <literal>:00</literal> is
@@ -276,6 +280,7 @@ Wed-Sat,Tue 12-10-15 1:2:3 → Tue-Sat 2012-10-15 01:02:03
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-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
diff --git a/man/systemd.timer.xml b/man/systemd.timer.xml
index 8cf6c4683b..fd03bda9cd 100644
--- a/man/systemd.timer.xml
+++ b/man/systemd.timer.xml
@@ -190,13 +190,12 @@
<varname>OnUnitInactiveSec=</varname> and ending the time
configured with <varname>AccuracySec=</varname> later. Within
this time window, the expiry time will be placed at a
- host-specific, randomized but stable position that is
+ host-specific, randomized, but stable position that is
synchronized between all local timer units. This is done in
- order to distribute the wake-up time in networked
- installations, as well as optimizing power consumption to
- suppress unnecessary CPU wake-ups. To get best accuracy, set
- this option to 1us. Note that the timer is still subject to
- the timer slack configured via
+ order to optimize power consumption to suppress unnecessary
+ CPU wake-ups. To get best accuracy, set this option to
+ 1us. Note that the timer is still subject to the timer slack
+ configured via
<citerefentry><refentrytitle>systemd-system.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>'s
<varname>TimerSlackNSec=</varname> setting. See
<citerefentry><refentrytitle>prctl</refentrytitle><manvolnum>2</manvolnum></citerefentry>
@@ -204,6 +203,38 @@
this value as high as possible and as low as
necessary.</para></listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><varname>RandomSec=</varname></term>
+
+ <listitem><para>Delay the timer by a randomly selected, evenly
+ distributed amount of time between 0 and the specified time
+ value. Defaults to 0, indicating that no randomized delay
+ shall be applied. Each timer unit will determine this delay
+ randomly each time it is started, and the delay will simply be
+ added on top of the next determined elapsing time. This is
+ useful to stretch dispatching of similarly configured timer
+ events over a certain amount time, to avoid that they all fire
+ at the same time, possibly resulting in resource
+ congestion. Note the relation to
+ <varname>AccuracySec=</varname> above: the latter allows the
+ service manager to coalesce timer events within a specified
+ time range in order to minimize wakeups, the former does the
+ opposite: it stretches timer events over a time range, to make
+ it unlikely that they fire simultaneously. If
+ <varname>RandomSec=</varname> and
+ <varname>AccuracySec=</varname> are used in conjunction, first
+ the a randomized time is added, and the result is then
+ possibly shifted further to coalesce it with other timer
+ events possibly happening on the system. As mentioned above
+ <varname>AccuracySec=</varname> defaults to 1min and
+ <varname>RandomSec=</varname> to 0, thus encouraging
+ coalescing of timer events. In order to optimally stretch
+ timer events over a certain range of time, make sure to set
+ <varname>RandomSec=</varname> to a higher value, and
+ <varname>AccuracySec=1us</varname>.</para></listitem>
+ </varlistentry>
+
<varlistentry>
<term><varname>Unit=</varname></term>
@@ -243,6 +274,24 @@
again after any work that is to be done is finished. Defaults
to <varname>false</varname>.</para></listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><varname>RemainAfterElapse=</varname></term>
+
+ <listitem><para>Takes a boolean argument. If true, an elapsed
+ timer will stay loaded, and its state remains queriable. If
+ false, an elapsed timer unit that cannot elapse anymore is
+ unloaded. Turning this off is particularly useful for
+ transient timer units that shall disappear after they first
+ elapse. Note that this setting has an effect on repeatedly
+ starting the a timer unit that only elapses once: if
+ <varname>RemainAfterElapse=</varname> is on, it will not be
+ started again, and is guaranteed to elapse only once. However,
+ if <varname>RemainAfterLeapse=</varname> is off, it might be
+ started again if it is already elapsed, and thus be triggered
+ multiple times. Defaults to
+ <varname>yes</varname>.</para></listitem>
+ </varlistentry>
</variablelist>
</refsect1>
diff --git a/src/basic/calendarspec.c b/src/basic/calendarspec.c
index 157ae1fb74..8f60561ede 100644
--- a/src/basic/calendarspec.c
+++ b/src/basic/calendarspec.c
@@ -25,6 +25,7 @@
#include "alloc-util.h"
#include "calendarspec.h"
#include "fileio.h"
+#include "parse-util.h"
#include "string-util.h"
#define BITS_WEEKDAYS 127
@@ -49,7 +50,7 @@ void calendar_spec_free(CalendarSpec *c) {
free_chain(c->day);
free_chain(c->hour);
free_chain(c->minute);
- free_chain(c->second);
+ free_chain(c->microsecond);
free(c);
}
@@ -135,7 +136,7 @@ int calendar_spec_normalize(CalendarSpec *c) {
sort_chain(&c->day);
sort_chain(&c->hour);
sort_chain(&c->minute);
- sort_chain(&c->second);
+ sort_chain(&c->microsecond);
return 0;
}
@@ -177,7 +178,7 @@ _pure_ bool calendar_spec_valid(CalendarSpec *c) {
if (!chain_valid(c->minute, 0, 59))
return false;
- if (!chain_valid(c->second, 0, 59))
+ if (!chain_valid(c->microsecond, 0, 60*USEC_PER_SEC-1))
return false;
return true;
@@ -232,7 +233,7 @@ static void format_weekdays(FILE *f, const CalendarSpec *c) {
}
}
-static void format_chain(FILE *f, int space, const CalendarComponent *c) {
+static void format_chain(FILE *f, int space, const CalendarComponent *c, bool usec) {
assert(f);
if (!c) {
@@ -241,14 +242,25 @@ static void format_chain(FILE *f, int space, const CalendarComponent *c) {
}
assert(c->value >= 0);
- fprintf(f, "%0*i", space, c->value);
-
- if (c->repeat > 0)
- fprintf(f, "/%i", c->repeat);
+ if (!usec)
+ fprintf(f, "%0*i", space, c->value);
+ else if (c->value % USEC_PER_SEC == 0)
+ fprintf(f, "%0*i", space, (int) (c->value / USEC_PER_SEC));
+ else
+ fprintf(f, "%0*i.%06i", space, (int) (c->value / USEC_PER_SEC), (int) (c->value % USEC_PER_SEC));
+
+ if (c->repeat > 0) {
+ if (!usec)
+ fprintf(f, "/%i", c->repeat);
+ else if (c->repeat % USEC_PER_SEC == 0)
+ fprintf(f, "/%i", (int) (c->repeat / USEC_PER_SEC));
+ else
+ fprintf(f, "/%i.%06i", (int) (c->repeat / USEC_PER_SEC), (int) (c->repeat % USEC_PER_SEC));
+ }
if (c->next) {
fputc(',', f);
- format_chain(f, space, c->next);
+ format_chain(f, space, c->next, usec);
}
}
@@ -270,17 +282,17 @@ int calendar_spec_to_string(const CalendarSpec *c, char **p) {
fputc(' ', f);
}
- format_chain(f, 4, c->year);
+ format_chain(f, 4, c->year, false);
fputc('-', f);
- format_chain(f, 2, c->month);
+ format_chain(f, 2, c->month, false);
fputc('-', f);
- format_chain(f, 2, c->day);
+ format_chain(f, 2, c->day, false);
fputc(' ', f);
- format_chain(f, 2, c->hour);
+ format_chain(f, 2, c->hour, false);
fputc(':', f);
- format_chain(f, 2, c->minute);
+ format_chain(f, 2, c->minute, false);
fputc(':', f);
- format_chain(f, 2, c->second);
+ format_chain(f, 2, c->microsecond, true);
if (c->utc)
fputs(" UTC", f);
@@ -391,35 +403,70 @@ static int parse_weekdays(const char **p, CalendarSpec *c) {
}
}
-static int prepend_component(const char **p, CalendarComponent **c) {
- unsigned long value, repeat = 0;
- char *e = NULL, *ee = NULL;
- CalendarComponent *cc;
-
- assert(p);
- assert(c);
+static int parse_component_decimal(const char **p, bool usec, unsigned long *res) {
+ unsigned long value;
+ const char *e = NULL;
+ char *ee = NULL;
+ int r;
errno = 0;
- value = strtoul(*p, &e, 10);
+ value = strtoul(*p, &ee, 10);
if (errno > 0)
return -errno;
- if (e == *p)
+ if (ee == *p)
return -EINVAL;
if ((unsigned long) (int) value != value)
return -ERANGE;
+ e = ee;
- if (*e == '/') {
- repeat = strtoul(e+1, &ee, 10);
- if (errno > 0)
- return -errno;
- if (ee == e+1)
- return -EINVAL;
- if ((unsigned long) (int) repeat != repeat)
- return -ERANGE;
- if (repeat <= 0)
+ if (usec) {
+ if (value * USEC_PER_SEC / USEC_PER_SEC != value)
return -ERANGE;
- e = ee;
+ value *= USEC_PER_SEC;
+ if (*e == '.') {
+ unsigned add;
+
+ e++;
+ r = parse_fractional_part_u(&e, 6, &add);
+ if (r < 0)
+ return r;
+
+ if (add + value < value)
+ return -ERANGE;
+ value += add;
+ }
+ }
+
+ *p = e;
+ *res = value;
+
+ return 0;
+}
+
+static int prepend_component(const char **p, bool usec, CalendarComponent **c) {
+ unsigned long value, repeat = 0;
+ CalendarComponent *cc;
+ int r;
+ const char *e;
+
+ assert(p);
+ assert(c);
+
+ e = *p;
+
+ r = parse_component_decimal(&e, usec, &value);
+ if (r < 0)
+ return r;
+
+ if (*e == '/') {
+ e++;
+ r = parse_component_decimal(&e, usec, &repeat);
+ if (r < 0)
+ return r;
+
+ if (repeat == 0)
+ return -ERANGE;
}
if (*e != 0 && *e != ' ' && *e != ',' && *e != '-' && *e != ':')
@@ -438,13 +485,31 @@ static int prepend_component(const char **p, CalendarComponent **c) {
if (*e ==',') {
*p += 1;
- return prepend_component(p, c);
+ return prepend_component(p, usec, c);
}
return 0;
}
-static int parse_chain(const char **p, CalendarComponent **c) {
+static int const_chain(int value, CalendarComponent **c) {
+ CalendarComponent *cc = NULL;
+
+ assert(c);
+
+ cc = new0(CalendarComponent, 1);
+ if (!cc)
+ return -ENOMEM;
+
+ cc->value = value;
+ cc->repeat = 0;
+ cc->next = *c;
+
+ *c = cc;
+
+ return 0;
+}
+
+static int parse_chain(const char **p, bool usec, CalendarComponent **c) {
const char *t;
CalendarComponent *cc = NULL;
int r;
@@ -455,12 +520,19 @@ static int parse_chain(const char **p, CalendarComponent **c) {
t = *p;
if (t[0] == '*') {
+ if (usec) {
+ r = const_chain(0, c);
+ if (r < 0)
+ return r;
+ (*c)->repeat = USEC_PER_SEC;
+ } else
+ *c = NULL;
+
*p = t + 1;
- *c = NULL;
return 0;
}
- r = prepend_component(&t, &cc);
+ r = prepend_component(&t, usec, &cc);
if (r < 0) {
free_chain(cc);
return r;
@@ -471,24 +543,6 @@ static int parse_chain(const char **p, CalendarComponent **c) {
return 0;
}
-static int const_chain(int value, CalendarComponent **c) {
- CalendarComponent *cc = NULL;
-
- assert(c);
-
- cc = new0(CalendarComponent, 1);
- if (!cc)
- return -ENOMEM;
-
- cc->value = value;
- cc->repeat = 0;
- cc->next = *c;
-
- *c = cc;
-
- return 0;
-}
-
static int parse_date(const char **p, CalendarSpec *c) {
const char *t;
int r;
@@ -503,7 +557,7 @@ static int parse_date(const char **p, CalendarSpec *c) {
if (*t == 0)
return 0;
- r = parse_chain(&t, &first);
+ r = parse_chain(&t, false, &first);
if (r < 0)
return r;
@@ -519,7 +573,7 @@ static int parse_date(const char **p, CalendarSpec *c) {
}
t++;
- r = parse_chain(&t, &second);
+ r = parse_chain(&t, false, &second);
if (r < 0) {
free_chain(first);
return r;
@@ -540,7 +594,7 @@ static int parse_date(const char **p, CalendarSpec *c) {
}
t++;
- r = parse_chain(&t, &third);
+ r = parse_chain(&t, false, &third);
if (r < 0) {
free_chain(first);
free_chain(second);
@@ -582,7 +636,7 @@ static int parse_calendar_time(const char **p, CalendarSpec *c) {
goto finish;
}
- r = parse_chain(&t, &h);
+ r = parse_chain(&t, false, &h);
if (r < 0)
goto fail;
@@ -592,7 +646,7 @@ static int parse_calendar_time(const char **p, CalendarSpec *c) {
}
t++;
- r = parse_chain(&t, &m);
+ r = parse_chain(&t, false, &m);
if (r < 0)
goto fail;
@@ -610,7 +664,7 @@ static int parse_calendar_time(const char **p, CalendarSpec *c) {
}
t++;
- r = parse_chain(&t, &s);
+ r = parse_chain(&t, true, &s);
if (r < 0)
goto fail;
@@ -639,7 +693,8 @@ finish:
*p = t;
c->hour = h;
c->minute = m;
- c->second = s;
+ c->microsecond = s;
+
return 0;
fail:
@@ -671,7 +726,7 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) {
}
if (strcaseeq(p, "minutely")) {
- r = const_chain(0, &c->second);
+ r = const_chain(0, &c->microsecond);
if (r < 0)
goto fail;
@@ -679,7 +734,7 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) {
r = const_chain(0, &c->minute);
if (r < 0)
goto fail;
- r = const_chain(0, &c->second);
+ r = const_chain(0, &c->microsecond);
if (r < 0)
goto fail;
@@ -690,7 +745,7 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) {
r = const_chain(0, &c->minute);
if (r < 0)
goto fail;
- r = const_chain(0, &c->second);
+ r = const_chain(0, &c->microsecond);
if (r < 0)
goto fail;
@@ -704,7 +759,7 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) {
r = const_chain(0, &c->minute);
if (r < 0)
goto fail;
- r = const_chain(0, &c->second);
+ r = const_chain(0, &c->microsecond);
if (r < 0)
goto fail;
@@ -724,7 +779,7 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) {
r = const_chain(0, &c->minute);
if (r < 0)
goto fail;
- r = const_chain(0, &c->second);
+ r = const_chain(0, &c->microsecond);
if (r < 0)
goto fail;
@@ -738,7 +793,7 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) {
r = const_chain(0, &c->minute);
if (r < 0)
goto fail;
- r = const_chain(0, &c->second);
+ r = const_chain(0, &c->microsecond);
if (r < 0)
goto fail;
@@ -765,7 +820,7 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) {
r = const_chain(0, &c->minute);
if (r < 0)
goto fail;
- r = const_chain(0, &c->second);
+ r = const_chain(0, &c->microsecond);
if (r < 0)
goto fail;
@@ -789,7 +844,7 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) {
r = const_chain(0, &c->minute);
if (r < 0)
goto fail;
- r = const_chain(0, &c->second);
+ r = const_chain(0, &c->microsecond);
if (r < 0)
goto fail;
@@ -906,14 +961,16 @@ static bool matches_weekday(int weekdays_bits, const struct tm *tm, bool utc) {
return (weekdays_bits & (1 << k));
}
-static int find_next(const CalendarSpec *spec, struct tm *tm) {
+static int find_next(const CalendarSpec *spec, struct tm *tm, usec_t *usec) {
struct tm c;
+ int tm_usec;
int r;
assert(spec);
assert(tm);
c = *tm;
+ tm_usec = *usec;
for (;;) {
/* Normalize the current date */
@@ -927,7 +984,7 @@ static int find_next(const CalendarSpec *spec, struct tm *tm) {
if (r > 0) {
c.tm_mon = 0;
c.tm_mday = 1;
- c.tm_hour = c.tm_min = c.tm_sec = 0;
+ c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0;
}
if (r < 0 || tm_out_of_bounds(&c, spec->utc))
return r;
@@ -938,29 +995,29 @@ static int find_next(const CalendarSpec *spec, struct tm *tm) {
if (r > 0) {
c.tm_mday = 1;
- c.tm_hour = c.tm_min = c.tm_sec = 0;
+ c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0;
}
if (r < 0 || tm_out_of_bounds(&c, spec->utc)) {
c.tm_year ++;
c.tm_mon = 0;
c.tm_mday = 1;
- c.tm_hour = c.tm_min = c.tm_sec = 0;
+ c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0;
continue;
}
r = find_matching_component(spec->day, &c.tm_mday);
if (r > 0)
- c.tm_hour = c.tm_min = c.tm_sec = 0;
+ c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0;
if (r < 0 || tm_out_of_bounds(&c, spec->utc)) {
c.tm_mon ++;
c.tm_mday = 1;
- c.tm_hour = c.tm_min = c.tm_sec = 0;
+ c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0;
continue;
}
if (!matches_weekday(spec->weekdays_bits, &c, spec->utc)) {
c.tm_mday++;
- c.tm_hour = c.tm_min = c.tm_sec = 0;
+ c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0;
continue;
}
@@ -969,7 +1026,7 @@ static int find_next(const CalendarSpec *spec, struct tm *tm) {
c.tm_min = c.tm_sec = 0;
if (r < 0 || tm_out_of_bounds(&c, spec->utc)) {
c.tm_mday ++;
- c.tm_hour = c.tm_min = c.tm_sec = 0;
+ c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0;
continue;
}
@@ -978,19 +1035,23 @@ static int find_next(const CalendarSpec *spec, struct tm *tm) {
c.tm_sec = 0;
if (r < 0 || tm_out_of_bounds(&c, spec->utc)) {
c.tm_hour ++;
- c.tm_min = c.tm_sec = 0;
+ c.tm_min = c.tm_sec = tm_usec = 0;
continue;
}
- r = find_matching_component(spec->second, &c.tm_sec);
+ c.tm_sec = c.tm_sec * USEC_PER_SEC + tm_usec;
+ r = find_matching_component(spec->microsecond, &c.tm_sec);
+ tm_usec = c.tm_sec % USEC_PER_SEC;
+ c.tm_sec /= USEC_PER_SEC;
+
if (r < 0 || tm_out_of_bounds(&c, spec->utc)) {
c.tm_min ++;
- c.tm_sec = 0;
+ c.tm_sec = tm_usec = 0;
continue;
}
-
*tm = c;
+ *usec = tm_usec;
return 0;
}
}
@@ -999,14 +1060,17 @@ int calendar_spec_next_usec(const CalendarSpec *spec, usec_t usec, usec_t *next)
struct tm tm;
time_t t;
int r;
+ usec_t tm_usec;
assert(spec);
assert(next);
- t = (time_t) (usec / USEC_PER_SEC) + 1;
+ usec++;
+ t = (time_t) (usec / USEC_PER_SEC);
assert_se(localtime_or_gmtime_r(&t, &tm, spec->utc));
+ tm_usec = usec % USEC_PER_SEC;
- r = find_next(spec, &tm);
+ r = find_next(spec, &tm, &tm_usec);
if (r < 0)
return r;
@@ -1014,6 +1078,6 @@ int calendar_spec_next_usec(const CalendarSpec *spec, usec_t usec, usec_t *next)
if (t == (time_t) -1)
return -EINVAL;
- *next = (usec_t) t * USEC_PER_SEC;
+ *next = (usec_t) t * USEC_PER_SEC + tm_usec;
return 0;
}
diff --git a/src/basic/calendarspec.h b/src/basic/calendarspec.h
index 56dc02f391..d703a1b67b 100644
--- a/src/basic/calendarspec.h
+++ b/src/basic/calendarspec.h
@@ -44,7 +44,7 @@ typedef struct CalendarSpec {
CalendarComponent *hour;
CalendarComponent *minute;
- CalendarComponent *second;
+ CalendarComponent *microsecond;
} CalendarSpec;
void calendar_spec_free(CalendarSpec *c);
diff --git a/src/basic/parse-util.c b/src/basic/parse-util.c
index 151067e916..3ae99d9334 100644
--- a/src/basic/parse-util.c
+++ b/src/basic/parse-util.c
@@ -490,3 +490,39 @@ int safe_atod(const char *s, double *ret_d) {
*ret_d = (double) d;
return 0;
}
+
+int parse_fractional_part_u(const char **p, size_t digits, unsigned *res) {
+ size_t i;
+ unsigned val = 0;
+ const char *s;
+
+ s = *p;
+
+ /* accept any number of digits, strtoull is limted to 19 */
+ for(i=0; i < digits; i++,s++) {
+ if (*s < '0' || *s > '9') {
+ if (i == 0)
+ return -EINVAL;
+
+ /* too few digits, pad with 0 */
+ for (; i < digits; i++)
+ val *= 10;
+
+ break;
+ }
+
+ val *= 10;
+ val += *s - '0';
+ }
+
+ /* maybe round up */
+ if (*s >= '5' && *s <= '9')
+ val++;
+
+ s += strspn(s, DIGITS);
+
+ *p = s;
+ *res = val;
+
+ return 0;
+}
diff --git a/src/basic/parse-util.h b/src/basic/parse-util.h
index 408690d0b3..125de53d7a 100644
--- a/src/basic/parse-util.h
+++ b/src/basic/parse-util.h
@@ -90,3 +90,5 @@ static inline int safe_atoli(const char *s, long int *ret_u) {
#endif
int safe_atod(const char *s, double *ret_d);
+
+int parse_fractional_part_u(const char **s, size_t digits, unsigned *res);
diff --git a/src/basic/time-util.c b/src/basic/time-util.c
index 647763a230..b9da6991da 100644
--- a/src/basic/time-util.c
+++ b/src/basic/time-util.c
@@ -27,6 +27,7 @@
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
+#include "parse-util.h"
#include "path-util.h"
#include "string-util.h"
#include "strv.h"
@@ -658,29 +659,18 @@ int parse_timestamp(const char *t, usec_t *usec) {
parse_usec:
{
- char *end;
- unsigned long long val;
- size_t l;
+ unsigned add;
k++;
- if (*k < '0' || *k > '9')
+ r = parse_fractional_part_u(&k, 6, &add);
+ if (r < 0)
return -EINVAL;
- /* base 10 instead of base 0, .09 is not base 8 */
- errno = 0;
- val = strtoull(k, &end, 10);
- if (*end || errno)
+ if (*k)
return -EINVAL;
- l = end-k;
-
- /* val has l digits, make them 6 */
- for (; l < 6; l++)
- val *= 10;
- for (; l > 6; l--)
- val /= 10;
+ x_usec = add;
- x_usec = val;
}
from_tm:
diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c
index 67e4e8b218..4d730290b2 100644
--- a/src/core/dbus-manager.c
+++ b/src/core/dbus-manager.c
@@ -644,6 +644,7 @@ static int transient_unit_from_message(
Unit **unit,
sd_bus_error *error) {
+ UnitType t;
Unit *u;
int r;
@@ -651,23 +652,18 @@ static int transient_unit_from_message(
assert(message);
assert(name);
+ t = unit_name_to_type(name);
+ if (t < 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid unit name or type.");
+
+ if (!unit_vtable[t]->can_transient)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unit type %s does not support transient units.", unit_type_to_string(t));
+
r = manager_load_unit(m, name, NULL, error, &u);
if (r < 0)
return r;
- /* Check if the unit already exists or is already referenced,
- * in a number of different ways. Note that to cater for unit
- * types such as slice, we are generally fine with units that
- * are marked UNIT_LOADED even even though nothing was
- * actually loaded, as those unit types don't require a file
- * on disk to validly load. */
-
- if (!IN_SET(u->load_state, UNIT_NOT_FOUND, UNIT_LOADED) ||
- u->fragment_path ||
- u->source_path ||
- !strv_isempty(u->dropin_paths) ||
- u->refs ||
- set_size(u->dependencies[UNIT_REFERENCED_BY]) > 0)
+ if (!unit_is_pristine(u))
return sd_bus_error_setf(error, BUS_ERROR_UNIT_EXISTS, "Unit %s already exists.", name);
/* OK, the unit failed to load and is unreferenced, now let's
@@ -681,6 +677,9 @@ static int transient_unit_from_message(
if (r < 0)
return r;
+ /* Now load the missing bits of the unit we just created */
+ manager_dispatch_load_queue(m);
+
*unit = u;
return 0;
@@ -691,8 +690,6 @@ static int transient_aux_units_from_message(
sd_bus_message *message,
sd_bus_error *error) {
- Unit *u;
- char *name = NULL;
int r;
assert(m);
@@ -703,20 +700,17 @@ static int transient_aux_units_from_message(
return r;
while ((r = sd_bus_message_enter_container(message, 'r', "sa(sv)")) > 0) {
+ const char *name = NULL;
+ Unit *u;
+
r = sd_bus_message_read(message, "s", &name);
if (r < 0)
return r;
r = transient_unit_from_message(m, message, name, &u, error);
- if (r < 0 && r != -EEXIST)
+ if (r < 0)
return r;
- if (r != -EEXIST) {
- r = unit_load(u);
- if (r < 0)
- return r;
- }
-
r = sd_bus_message_exit_container(message);
if (r < 0)
return r;
@@ -735,7 +729,6 @@ static int method_start_transient_unit(sd_bus_message *message, void *userdata,
const char *name, *smode;
Manager *m = userdata;
JobMode mode;
- UnitType t;
Unit *u;
int r;
@@ -750,13 +743,6 @@ static int method_start_transient_unit(sd_bus_message *message, void *userdata,
if (r < 0)
return r;
- t = unit_name_to_type(name);
- if (t < 0)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid unit type.");
-
- if (!unit_vtable[t]->can_transient)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unit type %s does not support transient units.", unit_type_to_string(t));
-
mode = job_mode_from_string(smode);
if (mode < 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Job mode %s is invalid.", smode);
@@ -775,13 +761,6 @@ static int method_start_transient_unit(sd_bus_message *message, void *userdata,
if (r < 0)
return r;
- /* And load this stub fully */
- r = unit_load(u);
- if (r < 0)
- return r;
-
- manager_dispatch_load_queue(m);
-
/* Finally, start it */
return bus_unit_queue_job(message, u, JOB_START, mode, false, error);
}
diff --git a/src/core/dbus-socket.c b/src/core/dbus-socket.c
index be5ef261a6..895dd07753 100644
--- a/src/core/dbus-socket.c
+++ b/src/core/dbus-socket.c
@@ -150,6 +150,7 @@ const sd_bus_vtable bus_socket_vtable[] = {
SD_BUS_PROPERTY("NConnections", "u", bus_property_get_unsigned, offsetof(Socket, n_connections), 0),
SD_BUS_PROPERTY("NAccepted", "u", bus_property_get_unsigned, offsetof(Socket, n_accepted), 0),
SD_BUS_PROPERTY("FileDescriptorName", "s", property_get_fdname, 0, 0),
+ SD_BUS_PROPERTY("SocketProtocol", "i", bus_property_get_int, offsetof(Socket, socket_protocol), SD_BUS_VTABLE_PROPERTY_CONST),
BUS_EXEC_COMMAND_LIST_VTABLE("ExecStartPre", offsetof(Socket, exec_command[SOCKET_EXEC_START_PRE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
BUS_EXEC_COMMAND_LIST_VTABLE("ExecStartPost", offsetof(Socket, exec_command[SOCKET_EXEC_START_POST]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
BUS_EXEC_COMMAND_LIST_VTABLE("ExecStopPre", offsetof(Socket, exec_command[SOCKET_EXEC_STOP_PRE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
diff --git a/src/core/dbus-timer.c b/src/core/dbus-timer.c
index a8a280d961..4bee82df07 100644
--- a/src/core/dbus-timer.c
+++ b/src/core/dbus-timer.c
@@ -180,8 +180,10 @@ const sd_bus_vtable bus_timer_vtable[] = {
BUS_PROPERTY_DUAL_TIMESTAMP("LastTriggerUSec", offsetof(Timer, last_trigger), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Timer, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("AccuracyUSec", "t", bus_property_get_usec, offsetof(Timer, accuracy_usec), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("RandomUSec", "t", bus_property_get_usec, offsetof(Timer, random_usec), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Persistent", "b", bus_property_get_bool, offsetof(Timer, persistent), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("WakeSystem", "b", bus_property_get_bool, offsetof(Timer, wake_system), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("RemainAfterElapse", "b", bus_property_get_bool, offsetof(Timer, remain_after_elapse), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_VTABLE_END
};
@@ -282,8 +284,23 @@ static int bus_timer_set_transient_property(
return 1;
- } else if (streq(name, "WakeSystem")) {
+ } else if (streq(name, "RandomUSec")) {
+ usec_t u = 0;
+
+ r = sd_bus_message_read(message, "t", &u);
+ if (r < 0)
+ return r;
+
+ if (mode != UNIT_CHECK) {
+ char time[FORMAT_TIMESPAN_MAX];
+
+ t->random_usec = u;
+ unit_write_drop_in_private_format(UNIT(t), mode, name, "RandomSec=%s\n", format_timespan(time, sizeof(time), u, USEC_PER_MSEC));
+ }
+
+ return 1;
+ } else if (streq(name, "WakeSystem")) {
int b;
r = sd_bus_message_read(message, "b", &b);
@@ -292,11 +309,24 @@ static int bus_timer_set_transient_property(
if (mode != UNIT_CHECK) {
t->wake_system = b;
- unit_write_drop_in_private_format(UNIT(t), mode, name, "%s=%s\n", name, yes_no(t->wake_system));
+ unit_write_drop_in_private_format(UNIT(t), mode, name, "%s=%s\n", name, yes_no(b));
}
return 1;
+ } else if (streq(name, "RemainAfterElapse")) {
+ int b;
+
+ r = sd_bus_message_read(message, "b", &b);
+ if (r < 0)
+ return r;
+
+ if (mode != UNIT_CHECK) {
+ t->remain_after_elapse = b;
+ unit_write_drop_in_private_format(UNIT(t), mode, name, "%s=%s\n", name, yes_no(b));
+ }
+
+ return 1;
}
return 0;
diff --git a/src/core/job.c b/src/core/job.c
index 53e0947215..9654590635 100644
--- a/src/core/job.c
+++ b/src/core/job.c
@@ -500,17 +500,26 @@ static void job_change_type(Job *j, JobType newtype) {
}
static int job_perform_on_unit(Job **j) {
- /* While we execute this operation the job might go away (for
- * example: because it finishes immediately or is replaced by a new,
- * conflicting job.) To make sure we don't access a freed job later on
- * we store the id here, so that we can verify the job is still
- * valid. */
- Manager *m = (*j)->manager;
- Unit *u = (*j)->unit;
- JobType t = (*j)->type;
- uint32_t id = (*j)->id;
+ uint32_t id;
+ Manager *m;
+ JobType t;
+ Unit *u;
int r;
+ /* While we execute this operation the job might go away (for
+ * example: because it finishes immediately or is replaced by
+ * a new, conflicting job.) To make sure we don't access a
+ * freed job later on we store the id here, so that we can
+ * verify the job is still valid. */
+
+ assert(j);
+ assert(*j);
+
+ m = (*j)->manager;
+ u = (*j)->unit;
+ t = (*j)->type;
+ id = (*j)->id;
+
switch (t) {
case JOB_START:
r = unit_start(u);
@@ -518,6 +527,7 @@ static int job_perform_on_unit(Job **j) {
case JOB_RESTART:
t = JOB_STOP;
+ /* fall through */
case JOB_STOP:
r = unit_stop(u);
break;
@@ -617,8 +627,7 @@ int job_run_and_invalidate(Job *j) {
}
_pure_ static const char *job_get_status_message_format(Unit *u, JobType t, JobResult result) {
- const char *format;
- const UnitStatusMessageFormats *format_table;
+
static const char *const generic_finished_start_job[_JOB_RESULT_MAX] = {
[JOB_DONE] = "Started %s.",
[JOB_TIMEOUT] = "Timed out starting %s.",
@@ -644,11 +653,14 @@ _pure_ static const char *job_get_status_message_format(Unit *u, JobType t, JobR
[JOB_SKIPPED] = "%s is not active.",
};
+ const UnitStatusMessageFormats *format_table;
+ const char *format;
+
assert(u);
assert(t >= 0);
assert(t < _JOB_TYPE_MAX);
- if (t == JOB_START || t == JOB_STOP || t == JOB_RESTART) {
+ if (IN_SET(t, JOB_START, JOB_STOP, JOB_RESTART)) {
format_table = &UNIT_VTABLE(u)->status_message_formats;
if (format_table) {
format = t == JOB_START ? format_table->finished_start_job[result] :
@@ -672,7 +684,6 @@ _pure_ static const char *job_get_status_message_format(Unit *u, JobType t, JobR
}
static void job_print_status_message(Unit *u, JobType t, JobResult result) {
- const char *format;
static const char* const job_result_status_table[_JOB_RESULT_MAX] = {
[JOB_DONE] = ANSI_GREEN " OK " ANSI_NORMAL,
[JOB_TIMEOUT] = ANSI_HIGHLIGHT_RED " TIME " ANSI_NORMAL,
@@ -683,10 +694,16 @@ static void job_print_status_message(Unit *u, JobType t, JobResult result) {
[JOB_UNSUPPORTED] = ANSI_HIGHLIGHT_YELLOW "UNSUPP" ANSI_NORMAL,
};
+ const char *format;
+
assert(u);
assert(t >= 0);
assert(t < _JOB_TYPE_MAX);
+ /* Reload status messages have traditionally not been printed to console. */
+ if (t == JOB_RELOAD)
+ return;
+
format = job_get_status_message_format(u, t, result);
if (!format)
return;
@@ -699,10 +716,10 @@ static void job_print_status_message(Unit *u, JobType t, JobResult result) {
REENABLE_WARNING;
if (t == JOB_START && result == JOB_FAILED) {
- _cleanup_free_ char *quoted = shell_maybe_quote(u->id);
+ _cleanup_free_ char *quoted;
- manager_status_printf(u->manager, STATUS_TYPE_NORMAL, NULL,
- "See 'systemctl status %s' for details.", strna(quoted));
+ quoted = shell_maybe_quote(u->id);
+ manager_status_printf(u->manager, STATUS_TYPE_NORMAL, NULL, "See 'systemctl status %s' for details.", strna(quoted));
}
}
@@ -740,13 +757,22 @@ static void job_log_status_message(Unit *u, JobType t, JobResult result) {
snprintf(buf, sizeof(buf), format, unit_description(u));
REENABLE_WARNING;
- if (t == JOB_START)
+ switch (t) {
+
+ case JOB_START:
mid = result == JOB_DONE ? SD_MESSAGE_UNIT_STARTED : SD_MESSAGE_UNIT_FAILED;
- else if (t == JOB_STOP || t == JOB_RESTART)
- mid = SD_MESSAGE_UNIT_STOPPED;
- else if (t == JOB_RELOAD)
+ break;
+
+ case JOB_RELOAD:
mid = SD_MESSAGE_UNIT_RELOADED;
- else {
+ break;
+
+ case JOB_STOP:
+ case JOB_RESTART:
+ mid = SD_MESSAGE_UNIT_STOPPED;
+ break;
+
+ default:
log_struct(job_result_log_level[result],
LOG_UNIT_ID(u),
LOG_MESSAGE("%s", buf),
@@ -770,10 +796,7 @@ static void job_emit_status_message(Unit *u, JobType t, JobResult result) {
return;
job_log_status_message(u, t, result);
-
- /* Reload status messages have traditionally not been printed to console. */
- if (t != JOB_RELOAD)
- job_print_status_message(u, t, result);
+ job_print_status_message(u, t, result);
}
static void job_fail_dependencies(Unit *u, UnitDependency d) {
diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4
index 799418033d..c64850802e 100644
--- a/src/core/load-fragment-gperf.gperf.m4
+++ b/src/core/load-fragment-gperf.gperf.m4
@@ -249,6 +249,7 @@ Socket.ListenNetlink, config_parse_socket_listen, SOCKET_SOCK
Socket.ListenSpecial, config_parse_socket_listen, SOCKET_SPECIAL, 0
Socket.ListenMessageQueue, config_parse_socket_listen, SOCKET_MQUEUE, 0
Socket.ListenUSBFunction, config_parse_socket_listen, SOCKET_USB_FUNCTION, 0
+Socket.SocketProtocol, config_parse_socket_protocol, 0, 0
Socket.BindIPv6Only, config_parse_socket_bind, 0, 0,
Socket.Backlog, config_parse_unsigned, 0, offsetof(Socket, backlog)
Socket.BindToDevice, config_parse_socket_bindtodevice, 0, 0
@@ -344,7 +345,9 @@ Timer.OnUnitActiveSec, config_parse_timer, 0,
Timer.OnUnitInactiveSec, config_parse_timer, 0, 0
Timer.Persistent, config_parse_bool, 0, offsetof(Timer, persistent)
Timer.WakeSystem, config_parse_bool, 0, offsetof(Timer, wake_system)
+Timer.RemainAfterElapse, config_parse_bool, 0, offsetof(Timer, remain_after_elapse)
Timer.AccuracySec, config_parse_sec, 0, offsetof(Timer, accuracy_usec)
+Timer.RandomSec, config_parse_sec, 0, offsetof(Timer, random_usec)
Timer.Unit, config_parse_trigger_unit, 0, 0
m4_dnl
Path.PathExists, config_parse_path_spec, 0, 0
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
index dda79267f7..d5cf476fda 100644
--- a/src/core/load-fragment.c
+++ b/src/core/load-fragment.c
@@ -421,6 +421,37 @@ int config_parse_socket_listen(const char *unit,
return 0;
}
+int config_parse_socket_protocol(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) {
+ Socket *s;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ s = SOCKET(data);
+
+ if (streq(rvalue, "udplite"))
+ s->socket_protocol = IPPROTO_UDPLITE;
+ else if (streq(rvalue, "sctp"))
+ s->socket_protocol = IPPROTO_SCTP;
+ else {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Socket protocol not supported, ignoring: %s", rvalue);
+ return 0;
+ }
+
+ return 0;
+}
+
int config_parse_socket_bind(const char *unit,
const char *filename,
unsigned line,
diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h
index 62300c10f9..a451fc164a 100644
--- a/src/core/load-fragment.h
+++ b/src/core/load-fragment.h
@@ -38,6 +38,7 @@ int config_parse_unit_path_printf(const char *unit, const char *filename, unsign
int config_parse_unit_path_strv_printf(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_documentation(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_socket_listen(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_socket_protocol(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_socket_bind(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);
int config_parse_exec_oom_score_adjust(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/core/socket.c b/src/core/socket.c
index 5b9e32ce9d..687675b24e 100644
--- a/src/core/socket.c
+++ b/src/core/socket.c
@@ -1266,6 +1266,19 @@ static int socket_open_fds(Socket *s) {
know_label = true;
}
+ /* Apply the socket protocol */
+ switch(p->address.type) {
+ case SOCK_STREAM:
+ case SOCK_SEQPACKET:
+ if (p->socket->socket_protocol == IPPROTO_SCTP)
+ p->address.protocol = p->socket->socket_protocol;
+ break;
+ case SOCK_DGRAM:
+ if (p->socket->socket_protocol == IPPROTO_UDPLITE)
+ p->address.protocol = p->socket->socket_protocol;
+ break;
+ }
+
r = socket_address_listen(
&p->address,
SOCK_CLOEXEC|SOCK_NONBLOCK,
diff --git a/src/core/socket.h b/src/core/socket.h
index 94cda8a90d..fb3948130f 100644
--- a/src/core/socket.h
+++ b/src/core/socket.h
@@ -120,6 +120,8 @@ struct Socket {
bool remove_on_stop;
bool writable;
+ int socket_protocol;
+
/* Socket options */
bool keep_alive;
bool no_delay;
diff --git a/src/core/timer.c b/src/core/timer.c
index 51b1d875be..6b0f8e8616 100644
--- a/src/core/timer.c
+++ b/src/core/timer.c
@@ -27,6 +27,7 @@
#include "dbus-timer.h"
#include "fs-util.h"
#include "parse-util.h"
+#include "random-util.h"
#include "special.h"
#include "string-table.h"
#include "string-util.h"
@@ -55,6 +56,7 @@ static void timer_init(Unit *u) {
t->next_elapse_monotonic_or_boottime = USEC_INFINITY;
t->next_elapse_realtime = USEC_INFINITY;
t->accuracy_usec = u->manager->default_timer_accuracy_usec;
+ t->remain_after_elapse = true;
}
void timer_free_values(Timer *t) {
@@ -217,13 +219,15 @@ static void timer_dump(Unit *u, FILE *f, const char *prefix) {
"%sUnit: %s\n"
"%sPersistent: %s\n"
"%sWakeSystem: %s\n"
- "%sAccuracy: %s\n",
+ "%sAccuracy: %s\n"
+ "%sRemainAfterElapse: %s\n",
prefix, timer_state_to_string(t->state),
prefix, timer_result_to_string(t->result),
prefix, trigger ? trigger->id : "n/a",
prefix, yes_no(t->persistent),
prefix, yes_no(t->wake_system),
- prefix, format_timespan(buf, sizeof(buf), t->accuracy_usec, 1));
+ prefix, format_timespan(buf, sizeof(buf), t->accuracy_usec, 1),
+ prefix, yes_no(t->remain_after_elapse));
LIST_FOREACH(value, v, t->values) {
@@ -275,13 +279,13 @@ static int timer_coldplug(Unit *u) {
assert(t);
assert(t->state == TIMER_DEAD);
- if (t->deserialized_state != t->state) {
+ if (t->deserialized_state == t->state)
+ return 0;
- if (t->deserialized_state == TIMER_WAITING)
- timer_enter_waiting(t, false);
- else
- timer_set_state(t, t->deserialized_state);
- }
+ if (t->deserialized_state == TIMER_WAITING)
+ timer_enter_waiting(t, false);
+ else
+ timer_set_state(t, t->deserialized_state);
return 0;
}
@@ -295,6 +299,23 @@ static void timer_enter_dead(Timer *t, TimerResult f) {
timer_set_state(t, t->result != TIMER_SUCCESS ? TIMER_FAILED : TIMER_DEAD);
}
+static void timer_enter_elapsed(Timer *t, bool leave_around) {
+ assert(t);
+
+ /* If a unit is marked with RemainAfterElapse=yes we leave it
+ * around even after it elapsed once, so that starting it
+ * later again does not necessarily mean immediate
+ * retriggering. We unconditionally leave units with
+ * TIMER_UNIT_ACTIVE or TIMER_UNIT_INACTIVE triggers around,
+ * since they might be restarted automatically at any time
+ * later on. */
+
+ if (t->remain_after_elapse || leave_around)
+ timer_set_state(t, TIMER_ELAPSED);
+ else
+ timer_enter_dead(t, TIMER_SUCCESS);
+}
+
static usec_t monotonic_to_boottime(usec_t t) {
usec_t a, b;
@@ -310,10 +331,33 @@ static usec_t monotonic_to_boottime(usec_t t) {
return 0;
}
+static void add_random(Timer *t, usec_t *v) {
+ char s[FORMAT_TIMESPAN_MAX];
+ usec_t add;
+
+ assert(t);
+ assert(*v);
+
+ if (t->random_usec == 0)
+ return;
+ if (*v == USEC_INFINITY)
+ return;
+
+ add = random_u64() % t->random_usec;
+
+ if (*v + add < *v) /* overflow */
+ *v = (usec_t) -2; /* Highest possible value, that is not USEC_INFINITY */
+ else
+ *v += add;
+
+ log_unit_info(UNIT(t), "Adding %s random time.", format_timespan(s, sizeof(s), add, 0));
+}
+
static void timer_enter_waiting(Timer *t, bool initial) {
bool found_monotonic = false, found_realtime = false;
usec_t ts_realtime, ts_monotonic;
usec_t base = 0;
+ bool leave_around = false;
TimerValue *v;
int r;
@@ -374,7 +418,7 @@ static void timer_enter_waiting(Timer *t, bool initial) {
break;
case TIMER_UNIT_ACTIVE:
-
+ leave_around = true;
base = UNIT_TRIGGER(UNIT(t))->inactive_exit_timestamp.monotonic;
if (base <= 0)
@@ -386,7 +430,7 @@ static void timer_enter_waiting(Timer *t, bool initial) {
break;
case TIMER_UNIT_INACTIVE:
-
+ leave_around = true;
base = UNIT_TRIGGER(UNIT(t))->inactive_enter_timestamp.monotonic;
if (base <= 0)
@@ -423,14 +467,18 @@ static void timer_enter_waiting(Timer *t, bool initial) {
if (!found_monotonic && !found_realtime) {
log_unit_debug(UNIT(t), "Timer is elapsed.");
- timer_set_state(t, TIMER_ELAPSED);
+ timer_enter_elapsed(t, leave_around);
return;
}
if (found_monotonic) {
char buf[FORMAT_TIMESPAN_MAX];
+ usec_t left;
- log_unit_debug(UNIT(t), "Monotonic timer elapses in %s.", format_timespan(buf, sizeof(buf), t->next_elapse_monotonic_or_boottime > ts_monotonic ? t->next_elapse_monotonic_or_boottime - ts_monotonic : 0, 0));
+ add_random(t, &t->next_elapse_monotonic_or_boottime);
+
+ left = t->next_elapse_monotonic_or_boottime > ts_monotonic ? t->next_elapse_monotonic_or_boottime - ts_monotonic : 0;
+ log_unit_debug(UNIT(t), "Monotonic timer elapses in %s.", format_timespan(buf, sizeof(buf), left, 0));
if (t->monotonic_event_source) {
r = sd_event_source_set_time(t->monotonic_event_source, t->next_elapse_monotonic_or_boottime);
@@ -463,6 +511,9 @@ static void timer_enter_waiting(Timer *t, bool initial) {
if (found_realtime) {
char buf[FORMAT_TIMESTAMP_MAX];
+
+ add_random(t, &t->next_elapse_realtime);
+
log_unit_debug(UNIT(t), "Realtime timer elapses at %s.", format_timestamp(buf, sizeof(buf), t->next_elapse_realtime));
if (t->realtime_event_source) {
diff --git a/src/core/timer.h b/src/core/timer.h
index ac5af6a93c..0599f07818 100644
--- a/src/core/timer.h
+++ b/src/core/timer.h
@@ -58,6 +58,7 @@ struct Timer {
Unit meta;
usec_t accuracy_usec;
+ usec_t random_usec;
LIST_HEAD(TimerValue, values);
usec_t next_elapse_realtime;
@@ -73,6 +74,7 @@ struct Timer {
bool persistent;
bool wake_system;
+ bool remain_after_elapse;
char *stamp_path;
};
diff --git a/src/core/unit.c b/src/core/unit.c
index d199d87bf8..0a02e38aa8 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -1347,12 +1347,18 @@ static bool unit_assert_test(Unit *u) {
return u->assert_result;
}
+void unit_status_printf(Unit *u, const char *status, const char *unit_status_msg_format) {
+ DISABLE_WARNING_FORMAT_NONLITERAL;
+ manager_status_printf(u->manager, STATUS_TYPE_NORMAL, status, unit_status_msg_format, unit_description(u));
+ REENABLE_WARNING;
+}
+
_pure_ static const char* unit_get_status_message_format(Unit *u, JobType t) {
const char *format;
const UnitStatusMessageFormats *format_table;
assert(u);
- assert(t == JOB_START || t == JOB_STOP || t == JOB_RELOAD);
+ assert(IN_SET(t, JOB_START, JOB_STOP, JOB_RELOAD));
if (t != JOB_RELOAD) {
format_table = &UNIT_VTABLE(u)->status_message_formats;
@@ -1377,6 +1383,10 @@ static void unit_status_print_starting_stopping(Unit *u, JobType t) {
assert(u);
+ /* Reload status messages have traditionally not been printed to console. */
+ if (!IN_SET(t, JOB_START, JOB_STOP))
+ return;
+
format = unit_get_status_message_format(u, t);
DISABLE_WARNING_FORMAT_NONLITERAL;
@@ -1391,7 +1401,7 @@ static void unit_status_log_starting_stopping_reloading(Unit *u, JobType t) {
assert(u);
- if (t != JOB_START && t != JOB_STOP && t != JOB_RELOAD)
+ if (!IN_SET(t, JOB_START, JOB_STOP, JOB_RELOAD))
return;
if (log_on_console())
@@ -1423,12 +1433,12 @@ static void unit_status_log_starting_stopping_reloading(Unit *u, JobType t) {
}
void unit_status_emit_starting_stopping_reloading(Unit *u, JobType t) {
+ assert(u);
+ assert(t >= 0);
+ assert(t < _JOB_TYPE_MAX);
unit_status_log_starting_stopping_reloading(u, t);
-
- /* Reload status messages have traditionally not been printed to console. */
- if (t != JOB_RELOAD)
- unit_status_print_starting_stopping(u, t);
+ unit_status_print_starting_stopping(u, t);
}
/* Errors:
@@ -2896,13 +2906,6 @@ int unit_coldplug(Unit *u) {
return 0;
}
-void unit_status_printf(Unit *u, const char *status, const char *unit_status_msg_format) {
- DISABLE_WARNING_FORMAT_NONLITERAL;
- manager_status_printf(u->manager, STATUS_TYPE_NORMAL,
- status, unit_status_msg_format, unit_description(u));
- REENABLE_WARNING;
-}
-
bool unit_need_daemon_reload(Unit *u) {
_cleanup_strv_free_ char **t = NULL;
char **path;
@@ -3428,7 +3431,15 @@ int unit_make_transient(Unit *u) {
u->load_state = UNIT_STUB;
u->load_error = 0;
u->transient = true;
+
u->fragment_path = mfree(u->fragment_path);
+ u->source_path = mfree(u->source_path);
+ u->dropin_paths = strv_free(u->dropin_paths);
+ u->fragment_mtime = u->source_mtime = u->dropin_mtime = 0;
+
+ unit_add_to_dbus_queue(u);
+ unit_add_to_gc_queue(u);
+ unit_add_to_load_queue(u);
return 0;
}
@@ -3704,3 +3715,21 @@ int unit_fail_if_symlink(Unit *u, const char* where) {
return -ELOOP;
}
+
+bool unit_is_pristine(Unit *u) {
+ assert(u);
+
+ /* Check if the unit already exists or is already around,
+ * in a number of different ways. Note that to cater for unit
+ * types such as slice, we are generally fine with units that
+ * are marked UNIT_LOADED even even though nothing was
+ * actually loaded, as those unit types don't require a file
+ * on disk to validly load. */
+
+ return !(!IN_SET(u->load_state, UNIT_NOT_FOUND, UNIT_LOADED) ||
+ u->fragment_path ||
+ u->source_path ||
+ !strv_isempty(u->dropin_paths) ||
+ u->job ||
+ u->merged_into);
+}
diff --git a/src/core/unit.h b/src/core/unit.h
index bcf41d2348..a69d624fad 100644
--- a/src/core/unit.h
+++ b/src/core/unit.h
@@ -586,6 +586,8 @@ int unit_require_mounts_for(Unit *u, const char *path);
bool unit_type_supported(UnitType t);
+bool unit_is_pristine(Unit *u);
+
static inline bool unit_supported(Unit *u) {
return unit_type_supported(u->type);
}
diff --git a/src/libsystemd-network/dhcp6-network.c b/src/libsystemd-network/dhcp6-network.c
index 318ee9c4b4..fd2d60c9d5 100644
--- a/src/libsystemd-network/dhcp6-network.c
+++ b/src/libsystemd-network/dhcp6-network.c
@@ -33,36 +33,32 @@
#include "socket-util.h"
int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) {
- struct in6_pktinfo pktinfo = {
- .ipi6_ifindex = index,
- };
union sockaddr_union src = {
.in6.sin6_family = AF_INET6,
.in6.sin6_port = htobe16(DHCP6_PORT_CLIENT),
- .in6.sin6_addr = IN6ADDR_ANY_INIT,
+ .in6.sin6_scope_id = index,
};
_cleanup_close_ int s = -1;
int r, off = 0, on = 1;
- if (local_address)
- memcpy(&src.in6.sin6_addr, local_address,
- sizeof(src.in6.sin6_addr));
+ assert(index > 0);
+ assert(local_address);
+
+ src.in6.sin6_addr = *local_address;
- s = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
- IPPROTO_UDP);
+ s = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, IPPROTO_UDP);
if (s < 0)
return -errno;
- r = setsockopt(s, IPPROTO_IPV6, IPV6_PKTINFO, &pktinfo,
- sizeof(pktinfo));
+ r = setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on));
if (r < 0)
return -errno;
- r = setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on));
+ r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &off, sizeof(off));
if (r < 0)
return -errno;
- r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &off, sizeof(off));
+ r = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
if (r < 0)
return -errno;
diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c
index a443bb3a7a..801331d270 100644
--- a/src/libsystemd-network/sd-dhcp6-client.c
+++ b/src/libsystemd-network/sd-dhcp6-client.c
@@ -32,6 +32,7 @@
#include "dhcp6-lease-internal.h"
#include "dhcp6-protocol.h"
#include "fd-util.h"
+#include "in-addr-util.h"
#include "network-internal.h"
#include "random-util.h"
#include "string-table.h"
@@ -46,6 +47,7 @@ struct sd_dhcp6_client {
sd_event *event;
int event_priority;
int index;
+ struct in6_addr local_address;
uint8_t mac_addr[MAX_MAC_ADDR_LEN];
size_t mac_addr_len;
uint16_t arp_type;
@@ -133,6 +135,18 @@ int sd_dhcp6_client_set_index(sd_dhcp6_client *client, int interface_index) {
return 0;
}
+int sd_dhcp6_client_set_local_address(sd_dhcp6_client *client, const struct in6_addr *local_address) {
+ assert_return(client, -EINVAL);
+ assert_return(local_address, -EINVAL);
+ assert_return(in_addr_is_link_local(AF_INET6, (const union in_addr_union *) local_address) > 0, -EINVAL);
+
+ assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
+
+ client->local_address = *local_address;
+
+ return 0;
+}
+
int sd_dhcp6_client_set_mac(
sd_dhcp6_client *client,
const uint8_t *addr, size_t addr_len,
@@ -1135,9 +1149,10 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) {
assert_return(client, -EINVAL);
assert_return(client->event, -EINVAL);
assert_return(client->index > 0, -EINVAL);
+ assert_return(in_addr_is_link_local(AF_INET6, (const union in_addr_union *) &client->local_address) > 0, -EINVAL);
if (!IN_SET(client->state, DHCP6_STATE_STOPPED))
- return -EALREADY;
+ return -EBUSY;
r = client_reset(client);
if (r < 0)
@@ -1151,7 +1166,7 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) {
if (r < 0)
return r;
- r = dhcp6_network_bind_udp_socket(client->index, NULL);
+ r = dhcp6_network_bind_udp_socket(client->index, &client->local_address);
if (r < 0)
return r;
diff --git a/src/libsystemd-network/sd-ndisc.c b/src/libsystemd-network/sd-ndisc.c
index 0881973e7f..3bb06f6892 100644
--- a/src/libsystemd-network/sd-ndisc.c
+++ b/src/libsystemd-network/sd-ndisc.c
@@ -508,6 +508,9 @@ static int ndisc_router_advertisment_recv(sd_event_source *s, int fd, uint32_t r
return 0;
}
+ if (!in_addr_is_link_local(AF_INET6, (const union in_addr_union*) &router.in6.sin6_addr))
+ return 0;
+
if (ra->nd_ra_type != ND_ROUTER_ADVERT)
return 0;
@@ -628,7 +631,7 @@ int sd_ndisc_router_discovery_start(sd_ndisc *nd) {
assert(nd->event);
if (nd->state != NDISC_STATE_IDLE)
- return -EINVAL;
+ return -EBUSY;
if (nd->index < 1)
return -EINVAL;
diff --git a/src/libsystemd-network/test-dhcp6-client.c b/src/libsystemd-network/test-dhcp6-client.c
index 17ed6d58f3..9e05fde8f6 100644
--- a/src/libsystemd-network/test-dhcp6-client.c
+++ b/src/libsystemd-network/test-dhcp6-client.c
@@ -562,6 +562,7 @@ static void test_client_information_cb(sd_dhcp6_client *client, int event,
sd_event *e = userdata;
sd_dhcp6_lease *lease;
struct in6_addr *addrs;
+ struct in6_addr address = { { { 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01 } } };
char **domains;
assert_se(e);
@@ -590,6 +591,8 @@ static void test_client_information_cb(sd_dhcp6_client *client, int event,
assert_se(sd_dhcp6_client_set_callback(client,
test_client_solicit_cb, e) >= 0);
+ assert_se(sd_dhcp6_client_set_local_address(client, &address) >= 0);
+
assert_se(sd_dhcp6_client_start(client) >= 0);
}
@@ -701,6 +704,7 @@ int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) {
static int test_client_solicit(sd_event *e) {
sd_dhcp6_client *client;
usec_t time_now = now(clock_boottime_or_monotonic());
+ struct in6_addr address = { { { 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01 } } };
int val = true;
if (verbose)
@@ -729,6 +733,8 @@ static int test_client_solicit(sd_event *e) {
time_now + 2 * USEC_PER_SEC, 0,
test_hangcheck, NULL) >= 0);
+ assert_se(sd_dhcp6_client_set_local_address(client, &address) >= 0);
+
assert_se(sd_dhcp6_client_start(client) >= 0);
sd_event_loop(e);
diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c
index c0562e5788..1ce1f4d8d6 100644
--- a/src/network/networkd-address.c
+++ b/src/network/networkd-address.c
@@ -347,9 +347,9 @@ int address_update(Address *address, unsigned char flags, unsigned char scope, s
link_check_ready(address->link);
if (address->family == AF_INET6 &&
- in_addr_is_link_local(AF_INET6, &address->in_addr) &&
- !address->link->ipv6ll_address) {
- r = link_ipv6ll_gained(address->link);
+ in_addr_is_link_local(AF_INET6, &address->in_addr) > 0 &&
+ in_addr_is_null(AF_INET6, (const union in_addr_union*) &address->link->ipv6ll_address) > 0) {
+ r = link_ipv6ll_gained(address->link, &address->in_addr.in6);
if (r < 0)
return r;
}
diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c
index b9c60a3c77..48e3d84055 100644
--- a/src/network/networkd-dhcp4.c
+++ b/src/network/networkd-dhcp4.c
@@ -255,6 +255,7 @@ static int dhcp_lease_lost(Link *link) {
}
link->dhcp_lease = sd_dhcp_lease_unref(link->dhcp_lease);
+ link_dirty(link);
link->dhcp4_configured = false;
return 0;
@@ -331,6 +332,7 @@ static int dhcp_lease_renew(sd_dhcp_client *client, Link *link) {
sd_dhcp_lease_unref(link->dhcp_lease);
link->dhcp4_configured = false;
link->dhcp_lease = sd_dhcp_lease_ref(lease);
+ link_dirty(link);
r = sd_dhcp_lease_get_address(lease, &address);
if (r < 0)
@@ -408,6 +410,7 @@ static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) {
NULL);
link->dhcp_lease = sd_dhcp_lease_ref(lease);
+ link_dirty(link);
if (link->network->dhcp_mtu) {
uint16_t mtu;
diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c
index f83ff54369..e67e51f7ef 100644
--- a/src/network/networkd-dhcp6.c
+++ b/src/network/networkd-dhcp6.c
@@ -211,6 +211,9 @@ int dhcp6_configure(Link *link) {
assert(link);
+ if (link->dhcp6_client)
+ return 0;
+
r = sd_dhcp6_client_new(&client);
if (r < 0)
return r;
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index a415035887..4af895a6fb 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -615,7 +615,7 @@ void link_check_ready(Link *link) {
return;
if (link_ipv6ll_enabled(link))
- if (!link->ipv6ll_address)
+ if (in_addr_is_null(AF_INET6, (const union in_addr_union*) &link->ipv6ll_address) > 0)
return;
if ((link_dhcp4_enabled(link) && !link_dhcp6_enabled(link) &&
@@ -1260,11 +1260,16 @@ static int link_acquire_ipv6_conf(Link *link) {
if (link_dhcp6_enabled(link)) {
assert(link->dhcp6_client);
+ assert(in_addr_is_link_local(AF_INET6, (const union in_addr_union*)&link->ipv6ll_address) > 0);
log_link_debug(link, "Acquiring DHCPv6 lease");
+ r = sd_dhcp6_client_set_local_address(link->dhcp6_client, &link->ipv6ll_address);
+ if (r < 0 && r != -EBUSY)
+ return log_link_warning_errno(link, r, "Could not set IPv6LL address in DHCP client: %m");
+
r = sd_dhcp6_client_start(link->dhcp6_client);
- if (r < 0)
+ if (r < 0 && r != -EBUSY)
return log_link_warning_errno(link, r, "Could not acquire DHCPv6 lease: %m");
}
@@ -1274,7 +1279,7 @@ static int link_acquire_ipv6_conf(Link *link) {
log_link_debug(link, "Discovering IPv6 routers");
r = sd_ndisc_router_discovery_start(link->ndisc_router_discovery);
- if (r < 0)
+ if (r < 0 && r != -EBUSY)
return log_link_warning_errno(link, r, "Could not start IPv6 Router Discovery: %m");
}
@@ -2089,7 +2094,8 @@ static int link_configure(Link *link) {
return r;
}
- if (link_dhcp6_enabled(link)) {
+ if (link_dhcp6_enabled(link) ||
+ link_ipv6_accept_ra_enabled(link)) {
r = dhcp6_configure(link);
if (r < 0)
return r;
@@ -2121,7 +2127,7 @@ static int link_configure(Link *link) {
if (r < 0)
return r;
- if (link->ipv6ll_address) {
+ if (in_addr_is_null(AF_INET6, (const union in_addr_union*) &link->ipv6ll_address) == 0) {
r = link_acquire_ipv6_conf(link);
if (r < 0)
return r;
@@ -2290,7 +2296,8 @@ network_file_fail:
if (r < 0) {
log_link_debug_errno(link, r, "Failed to extract next address string: %m");
continue;
- } if (r == 0)
+ }
+ if (r == 0)
break;
prefixlen_str = strchr(address_str, '/');
@@ -2334,7 +2341,8 @@ network_file_fail:
if (r < 0) {
log_link_debug_errno(link, r, "Failed to extract next route string: %m");
continue;
- } if (r == 0)
+ }
+ if (r == 0)
break;
prefixlen_str = strchr(route_str, '/');
@@ -2472,14 +2480,14 @@ failed:
return r;
}
-int link_ipv6ll_gained(Link *link) {
+int link_ipv6ll_gained(Link *link, const struct in6_addr *address) {
int r;
assert(link);
log_link_info(link, "Gained IPv6LL");
- link->ipv6ll_address = true;
+ link->ipv6ll_address = *address;
link_check_ready(link);
if (!IN_SET(link->state, LINK_STATE_PENDING, LINK_STATE_PENDING, LINK_STATE_UNMANAGED, LINK_STATE_FAILED)) {
diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h
index b564bcbca0..aa2235b11d 100644
--- a/src/network/networkd-link.h
+++ b/src/network/networkd-link.h
@@ -69,6 +69,7 @@ struct Link {
char *ifname;
char *state_file;
struct ether_addr mac;
+ struct in6_addr ipv6ll_address;
uint32_t mtu;
struct udev_device *udev_device;
@@ -101,7 +102,6 @@ struct Link {
sd_ipv4ll *ipv4ll;
bool ipv4ll_address:1;
bool ipv4ll_route:1;
- bool ipv6ll_address:1;
bool static_configured;
@@ -144,7 +144,7 @@ int link_save(Link *link);
int link_carrier_reset(Link *link);
bool link_has_carrier(Link *link);
-int link_ipv6ll_gained(Link *link);
+int link_ipv6ll_gained(Link *link, const struct in6_addr *address);
int link_set_mtu(Link *link, uint32_t mtu);
int link_set_hostname(Link *link, const char *hostname);
diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c
index 126f9c0fe9..ce9e513ceb 100644
--- a/src/network/networkd-ndisc.c
+++ b/src/network/networkd-ndisc.c
@@ -75,7 +75,7 @@ static void ndisc_prefix_autonomous_handler(sd_ndisc *nd, const struct in6_addr
address->family = AF_INET6;
address->in_addr.in6 = *prefix;
if (in_addr_is_null(AF_INET6, (const union in_addr_union *) &link->network->ipv6_token) == 0)
- memcpy(&address->in_addr.in6 + 8, &link->network->ipv6_token + 8, 8);
+ memcpy(((char *)&address->in_addr.in6) + 8, ((char *)&link->network->ipv6_token) + 8, 8);
else {
/* see RFC4291 section 2.5.1 */
address->in_addr.in6.__in6_u.__u6_addr8[8] = link->mac.ether_addr_octet[0];
@@ -159,7 +159,7 @@ static void ndisc_router_handler(sd_ndisc *nd, uint8_t flags, const struct in6_a
dhcp6_request_address(link);
r = sd_dhcp6_client_start(link->dhcp6_client);
- if (r < 0 && r != -EALREADY)
+ if (r < 0 && r != -EBUSY)
log_link_warning_errno(link, r, "Starting DHCPv6 client on NDisc request failed: %m");
}
@@ -205,8 +205,12 @@ static void ndisc_handler(sd_ndisc *nd, int event, void *userdata) {
dhcp6_request_address(link);
r = sd_dhcp6_client_start(link->dhcp6_client);
- if (r < 0 && r != -EALREADY)
+ if (r < 0 && r != -EBUSY)
log_link_warning_errno(link, r, "Starting DHCPv6 client after NDisc timeout failed: %m");
+
+ link->ndisc_configured = true;
+ link_check_ready(link);
+
break;
case SD_NDISC_EVENT_STOP:
break;
diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c
index b15370b017..873d76e40c 100644
--- a/src/resolve/resolved-dns-scope.c
+++ b/src/resolve/resolved-dns-scope.c
@@ -543,6 +543,7 @@ static void dns_scope_verify_conflicts(DnsScope *s, DnsPacket *p) {
void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p) {
_cleanup_(dns_packet_unrefp) DnsPacket *reply = NULL;
_cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL, *soa = NULL;
+ DnsResourceKey *key = NULL;
bool tentative = false;
int r, fd;
@@ -576,7 +577,10 @@ void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p) {
return;
}
- r = dns_zone_lookup(&s->zone, p->question, &answer, &soa, &tentative);
+ assert(p->question->n_keys == 1);
+ key = p->question->keys[0];
+
+ r = dns_zone_lookup(&s->zone, key, &answer, &soa, &tentative);
if (r < 0) {
log_debug_errno(r, "Failed to lookup key: %m");
return;
diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c
index 6545f6cd8a..37f47c47c0 100644
--- a/src/resolve/resolved-dns-transaction.c
+++ b/src/resolve/resolved-dns-transaction.c
@@ -626,6 +626,20 @@ int dns_transaction_go(DnsTransaction *t) {
t->cached = dns_answer_unref(t->cached);
t->cached_rcode = 0;
+ /* Check the zone, but obly if this transaction is not used
+ * for probing or verifying a zone item. */
+ if (set_isempty(t->zone_items)) {
+
+ r = dns_zone_lookup(&t->scope->zone, t->key, &t->cached, NULL, NULL);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ t->cached_rcode = DNS_RCODE_SUCCESS;
+ dns_transaction_complete(t, DNS_TRANSACTION_SUCCESS);
+ return 0;
+ }
+ }
+
/* Check the cache, but only if this transaction is not used
* for probing or verifying a zone item. */
if (set_isempty(t->zone_items)) {
diff --git a/src/resolve/resolved-dns-zone.c b/src/resolve/resolved-dns-zone.c
index 48dcf76daa..a021ecb93d 100644
--- a/src/resolve/resolved-dns-zone.c
+++ b/src/resolve/resolved-dns-zone.c
@@ -283,97 +283,76 @@ int dns_zone_put(DnsZone *z, DnsScope *s, DnsResourceRecord *rr, bool probe) {
return 0;
}
-int dns_zone_lookup(DnsZone *z, DnsQuestion *q, DnsAnswer **ret_answer, DnsAnswer **ret_soa, bool *ret_tentative) {
+int dns_zone_lookup(DnsZone *z, DnsResourceKey *key, DnsAnswer **ret_answer, DnsAnswer **ret_soa, bool *ret_tentative) {
_cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL, *soa = NULL;
- unsigned i, n_answer = 0, n_soa = 0;
- bool tentative = true;
+ unsigned n_answer = 0;
+ DnsZoneItem *j, *first;
+ bool tentative = true, need_soa = false;
int r;
assert(z);
- assert(q);
+ assert(key);
assert(ret_answer);
- assert(ret_soa);
- if (q->n_keys <= 0) {
- *ret_answer = NULL;
- *ret_soa = NULL;
-
- if (ret_tentative)
- *ret_tentative = false;
+ /* First iteration, count what we have */
- return 0;
- }
+ if (key->type == DNS_TYPE_ANY || key->class == DNS_CLASS_ANY) {
+ bool found = false, added = false;
+ int k;
- /* First iteration, count what we have */
- for (i = 0; i < q->n_keys; i++) {
- DnsZoneItem *j, *first;
+ /* If this is a generic match, then we have to
+ * go through the list by the name and look
+ * for everything manually */
- if (q->keys[i]->type == DNS_TYPE_ANY ||
- q->keys[i]->class == DNS_CLASS_ANY) {
- bool found = false, added = false;
- int k;
+ first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(key));
+ LIST_FOREACH(by_name, j, first) {
+ if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING))
+ continue;
- /* If this is a generic match, then we have to
- * go through the list by the name and look
- * for everything manually */
+ found = true;
- first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(q->keys[i]));
- LIST_FOREACH(by_name, j, first) {
- if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING))
- continue;
+ k = dns_resource_key_match_rr(key, j->rr);
+ if (k < 0)
+ return k;
+ if (k > 0) {
+ n_answer++;
+ added = true;
+ }
- found = true;
+ }
- k = dns_resource_key_match_rr(q->keys[i], j->rr);
- if (k < 0)
- return k;
- if (k > 0) {
- n_answer++;
- added = true;
- }
+ if (found && !added)
+ need_soa = true;
- }
+ } else {
+ bool found = false;
- if (found && !added)
- n_soa++;
+ /* If this is a specific match, then look for
+ * the right key immediately */
- } else {
- bool found = false;
+ first = hashmap_get(z->by_key, key);
+ LIST_FOREACH(by_key, j, first) {
+ if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING))
+ continue;
- /* If this is a specific match, then look for
- * the right key immediately */
+ found = true;
+ n_answer++;
+ }
- first = hashmap_get(z->by_key, q->keys[i]);
- LIST_FOREACH(by_key, j, first) {
+ if (!found) {
+ first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(key));
+ LIST_FOREACH(by_name, j, first) {
if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING))
continue;
- found = true;
- n_answer++;
- }
-
- if (!found) {
- first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(q->keys[i]));
- LIST_FOREACH(by_name, j, first) {
- if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING))
- continue;
-
- n_soa++;
- break;
- }
+ need_soa = true;
+ break;
}
}
}
- if (n_answer <= 0 && n_soa <= 0) {
- *ret_answer = NULL;
- *ret_soa = NULL;
-
- if (ret_tentative)
- *ret_tentative = false;
-
- return 0;
- }
+ if (n_answer <= 0 && !need_soa)
+ goto return_empty;
if (n_answer > 0) {
answer = dns_answer_new(n_answer);
@@ -381,99 +360,113 @@ int dns_zone_lookup(DnsZone *z, DnsQuestion *q, DnsAnswer **ret_answer, DnsAnswe
return -ENOMEM;
}
- if (n_soa > 0) {
- soa = dns_answer_new(n_soa);
+ if (need_soa) {
+ soa = dns_answer_new(1);
if (!soa)
return -ENOMEM;
}
/* Second iteration, actually add the RRs to the answers */
- for (i = 0; i < q->n_keys; i++) {
- DnsZoneItem *j, *first;
-
- if (q->keys[i]->type == DNS_TYPE_ANY ||
- q->keys[i]->class == DNS_CLASS_ANY) {
- bool found = false, added = false;
- int k;
+ if (key->type == DNS_TYPE_ANY || key->class == DNS_CLASS_ANY) {
+ bool found = false, added = false;
+ int k;
- first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(q->keys[i]));
- LIST_FOREACH(by_name, j, first) {
- if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING))
- continue;
-
- found = true;
+ first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(key));
+ LIST_FOREACH(by_name, j, first) {
+ if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING))
+ continue;
- if (j->state != DNS_ZONE_ITEM_PROBING)
- tentative = false;
+ found = true;
- k = dns_resource_key_match_rr(q->keys[i], j->rr);
- if (k < 0)
- return k;
- if (k > 0) {
- r = dns_answer_add(answer, j->rr, 0);
- if (r < 0)
- return r;
+ if (j->state != DNS_ZONE_ITEM_PROBING)
+ tentative = false;
- added = true;
- }
- }
-
- if (found && !added) {
- r = dns_answer_add_soa(soa, DNS_RESOURCE_KEY_NAME(q->keys[i]), LLMNR_DEFAULT_TTL);
+ k = dns_resource_key_match_rr(key, j->rr);
+ if (k < 0)
+ return k;
+ if (k > 0) {
+ r = dns_answer_add(answer, j->rr, 0);
if (r < 0)
return r;
+
+ added = true;
}
- } else {
- bool found = false;
+ }
- first = hashmap_get(z->by_key, q->keys[i]);
- LIST_FOREACH(by_key, j, first) {
- if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING))
- continue;
+ if (found && !added) {
+ r = dns_answer_add_soa(soa, DNS_RESOURCE_KEY_NAME(key), LLMNR_DEFAULT_TTL);
+ if (r < 0)
+ return r;
+ }
+ } else {
+ bool found = false;
- found = true;
+ first = hashmap_get(z->by_key, key);
+ LIST_FOREACH(by_key, j, first) {
+ if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING))
+ continue;
- if (j->state != DNS_ZONE_ITEM_PROBING)
- tentative = false;
+ found = true;
- r = dns_answer_add(answer, j->rr, 0);
- if (r < 0)
- return r;
- }
+ if (j->state != DNS_ZONE_ITEM_PROBING)
+ tentative = false;
+
+ r = dns_answer_add(answer, j->rr, 0);
+ if (r < 0)
+ return r;
+ }
- if (!found) {
- bool add_soa = false;
+ if (!found) {
+ bool add_soa = false;
- first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(q->keys[i]));
- LIST_FOREACH(by_name, j, first) {
- if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING))
- continue;
+ first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(key));
+ LIST_FOREACH(by_name, j, first) {
+ if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING))
+ continue;
- if (j->state != DNS_ZONE_ITEM_PROBING)
- tentative = false;
+ if (j->state != DNS_ZONE_ITEM_PROBING)
+ tentative = false;
- add_soa = true;
- }
+ add_soa = true;
+ }
- if (add_soa) {
- r = dns_answer_add_soa(soa, DNS_RESOURCE_KEY_NAME(q->keys[i]), LLMNR_DEFAULT_TTL);
- if (r < 0)
- return r;
- }
+ if (add_soa) {
+ r = dns_answer_add_soa(soa, DNS_RESOURCE_KEY_NAME(key), LLMNR_DEFAULT_TTL);
+ if (r < 0)
+ return r;
}
}
}
+ /* If the caller sets ret_tentative to NULL, then use this as
+ * indication to not return tentative entries */
+
+ if (!ret_tentative && tentative)
+ goto return_empty;
+
*ret_answer = answer;
answer = NULL;
- *ret_soa = soa;
- soa = NULL;
+ if (ret_soa) {
+ *ret_soa = soa;
+ soa = NULL;
+ }
if (ret_tentative)
*ret_tentative = tentative;
return 1;
+
+return_empty:
+ *ret_answer = NULL;
+
+ if (ret_soa)
+ *ret_soa = NULL;
+
+ if (ret_tentative)
+ *ret_tentative = false;
+
+ return 0;
}
void dns_zone_item_conflict(DnsZoneItem *i) {
diff --git a/src/resolve/resolved-dns-zone.h b/src/resolve/resolved-dns-zone.h
index 495d17cdb1..db92113a36 100644
--- a/src/resolve/resolved-dns-zone.h
+++ b/src/resolve/resolved-dns-zone.h
@@ -67,7 +67,7 @@ void dns_zone_flush(DnsZone *z);
int dns_zone_put(DnsZone *z, DnsScope *s, DnsResourceRecord *rr, bool probe);
void dns_zone_remove_rr(DnsZone *z, DnsResourceRecord *rr);
-int dns_zone_lookup(DnsZone *z, DnsQuestion *q, DnsAnswer **answer, DnsAnswer **soa, bool *tentative);
+int dns_zone_lookup(DnsZone *z, DnsResourceKey *key, DnsAnswer **answer, DnsAnswer **soa, bool *tentative);
void dns_zone_item_conflict(DnsZoneItem *i);
void dns_zone_item_ready(DnsZoneItem *i);
diff --git a/src/run/run.c b/src/run/run.c
index df6a4f0074..e1accc467b 100644
--- a/src/run/run.c
+++ b/src/run/run.c
@@ -648,6 +648,11 @@ static int transient_timer_set_properties(sd_bus_message *m) {
if (r < 0)
return r;
+ /* Automatically clean up our transient timers */
+ r = sd_bus_message_append(m, "(sv)", "RemainAfterElapse", "b", false);
+ if (r < 0)
+ return r;
+
if (arg_on_active) {
r = sd_bus_message_append(m, "(sv)", "OnActiveSec", "t", arg_on_active);
if (r < 0)
@@ -687,6 +692,51 @@ static int transient_timer_set_properties(sd_bus_message *m) {
return 0;
}
+static int make_unit_name(sd_bus *bus, UnitType t, char **ret) {
+ const char *unique, *id;
+ char *p;
+ int r;
+
+ assert(bus);
+ assert(t >= 0);
+ assert(t < _UNIT_TYPE_MAX);
+
+ r = sd_bus_get_unique_name(bus, &unique);
+ if (r < 0) {
+ sd_id128_t rnd;
+
+ /* We couldn't get the unique name, which is a pretty
+ * common case if we are connected to systemd
+ * directly. In that case, just pick a random uuid as
+ * name */
+
+ r = sd_id128_randomize(&rnd);
+ if (r < 0)
+ return log_error_errno(r, "Failed to generate random run unit name: %m");
+
+ if (asprintf(ret, "run-r" SD_ID128_FORMAT_STR ".%s", SD_ID128_FORMAT_VAL(rnd), unit_type_to_string(t)) < 0)
+ return log_oom();
+
+ return 0;
+ }
+
+ /* We managed to get the unique name, then let's use that to
+ * name our transient units. */
+
+ id = startswith(unique, ":1.");
+ if (!id) {
+ log_error("Unique name %s has unexpected format.", unique);
+ return -EINVAL;
+ }
+
+ p = strjoin("run-u", id, ".", unit_type_to_string(t), NULL);
+ if (!p)
+ return log_oom();
+
+ *ret = p;
+ return 0;
+}
+
static int start_transient_service(
sd_bus *bus,
char **argv) {
@@ -763,8 +813,11 @@ static int start_transient_service(
r = unit_name_mangle_with_suffix(arg_unit, UNIT_NAME_NOGLOB, ".service", &service);
if (r < 0)
return log_error_errno(r, "Failed to mangle unit name: %m");
- } else if (asprintf(&service, "run-"PID_FMT".service", getpid()) < 0)
- return log_oom();
+ } else {
+ r = make_unit_name(bus, UNIT_SERVICE, &service);
+ if (r < 0)
+ return r;
+ }
r = sd_bus_message_new_method_call(
bus,
@@ -882,8 +935,11 @@ static int start_transient_scope(
r = unit_name_mangle_with_suffix(arg_unit, UNIT_NAME_NOGLOB, ".scope", &scope);
if (r < 0)
return log_error_errno(r, "Failed to mangle scope name: %m");
- } else if (asprintf(&scope, "run-"PID_FMT".scope", getpid()) < 0)
- return log_oom();
+ } else {
+ r = make_unit_name(bus, UNIT_SCOPE, &scope);
+ if (r < 0)
+ return r;
+ }
r = sd_bus_message_new_method_call(
bus,
@@ -1052,9 +1108,15 @@ static int start_transient_timer(
break;
}
- } else if ((asprintf(&service, "run-"PID_FMT".service", getpid()) < 0) ||
- (asprintf(&timer, "run-"PID_FMT".timer", getpid()) < 0))
- return log_oom();
+ } else {
+ r = make_unit_name(bus, UNIT_SERVICE, &service);
+ if (r < 0)
+ return r;
+
+ r = unit_name_change_suffix(service, ".timer", &timer);
+ if (r < 0)
+ return log_error_errno(r, "Failed to change unit suffix: %m");
+ }
r = sd_bus_message_new_method_call(
bus,
diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c
index 73ceeba18f..38281045b8 100644
--- a/src/shared/bus-util.c
+++ b/src/shared/bus-util.c
@@ -1428,16 +1428,36 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
return bus_log_create_error(r);
return 0;
+
} else if (streq(field, "EnvironmentFile")) {
+
r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, "EnvironmentFiles");
if (r < 0)
- return r;
+ return bus_log_create_error(r);
r = sd_bus_message_append(m, "v", "a(sb)", 1,
eq[0] == '-' ? eq + 1 : eq,
eq[0] == '-');
if (r < 0)
- return r;
+ return bus_log_create_error(r);
+
+ return 0;
+
+ } else if (streq(field, "RandomSec")) {
+ usec_t t;
+
+ r = parse_sec(eq, &t);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse RandomSec= parameter: %s", eq);
+
+ r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, "RandomUSec");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append(m, "v", "t", t);
+ if (r < 0)
+ return bus_log_create_error(r);
+
return 0;
}
@@ -1450,13 +1470,11 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
"SendSIGHUP", "SendSIGKILL", "WakeSystem", "DefaultDependencies",
"IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "RemainAfterExit",
"PrivateTmp", "PrivateDevices", "PrivateNetwork", "NoNewPrivileges",
- "SyslogLevelPrefix", "Delegate")) {
+ "SyslogLevelPrefix", "Delegate", "RemainAfterElapse")) {
r = parse_boolean(eq);
- if (r < 0) {
- log_error("Failed to parse boolean assignment %s.", assignment);
- return -EINVAL;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse boolean assignment %s.", assignment);
r = sd_bus_message_append(m, "v", "b", r);
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index 51b82d57db..240fa2c551 100644
--- a/src/systemctl/systemctl.c
+++ b/src/systemctl/systemctl.c
@@ -3360,6 +3360,7 @@ typedef struct UnitStatusInfo {
usec_t inactive_enter_timestamp;
bool need_daemon_reload;
+ bool transient;
/* Service */
pid_t main_pid;
@@ -3459,7 +3460,7 @@ static void print_status_info(
path = i->source_path ? i->source_path : i->fragment_path;
- if (i->load_error)
+ if (i->load_error != 0)
printf(" Loaded: %s%s%s (Reason: %s)\n",
on, strna(i->load_state), off, i->load_error);
else if (path && !isempty(i->unit_file_state) && !isempty(i->unit_file_preset))
@@ -3475,6 +3476,9 @@ static void print_status_info(
printf(" Loaded: %s%s%s\n",
on, strna(i->load_state), off);
+ if (i->transient)
+ printf("Transient: yes\n");
+
if (!strv_isempty(i->dropin_paths)) {
_cleanup_free_ char *dir = NULL;
bool last = false;
@@ -3839,6 +3843,8 @@ static int status_property(const char *name, sd_bus_message *m, UnitStatusInfo *
i->condition_result = b;
else if (streq(name, "AssertResult"))
i->assert_result = b;
+ else if (streq(name, "Transient"))
+ i->transient = b;
break;
}
@@ -4646,8 +4652,7 @@ static int show(int argc, char *argv[], void *userdata) {
return -EINVAL;
}
- if (show_properties)
- pager_open_if_enabled();
+ pager_open_if_enabled();
if (show_status)
/* Increase max number of open files to 16K if we can, we
diff --git a/src/systemd/sd-dhcp6-client.h b/src/systemd/sd-dhcp6-client.h
index 9f0e92806e..0ca6c07de4 100644
--- a/src/systemd/sd-dhcp6-client.h
+++ b/src/systemd/sd-dhcp6-client.h
@@ -49,6 +49,7 @@ int sd_dhcp6_client_set_callback(sd_dhcp6_client *client,
sd_dhcp6_client_cb_t cb, void *userdata);
int sd_dhcp6_client_set_index(sd_dhcp6_client *client, int interface_index);
+int sd_dhcp6_client_set_local_address(sd_dhcp6_client *client, const struct in6_addr *local_address);
int sd_dhcp6_client_set_mac(sd_dhcp6_client *client, const uint8_t *addr,
size_t addr_len, uint16_t arp_type);
int sd_dhcp6_client_set_duid(sd_dhcp6_client *client, uint16_t type, uint8_t *duid,
diff --git a/src/test/test-calendarspec.c b/src/test/test-calendarspec.c
index 70819b0371..9cef7154c6 100644
--- a/src/test/test-calendarspec.c
+++ b/src/test/test-calendarspec.c
@@ -75,7 +75,7 @@ static void test_next(const char *input, const char *new_tz, usec_t after, usec_
u = after;
r = calendar_spec_next_usec(c, after, &u);
- printf("At: %s\n", r < 0 ? strerror(-r) : format_timestamp(buf, sizeof(buf), u));
+ printf("At: %s\n", r < 0 ? strerror(-r) : format_timestamp_us(buf, sizeof(buf), u));
if (expect != (usec_t)-1)
assert_se(r >= 0 && u == expect);
else
@@ -123,6 +123,9 @@ int main(int argc, char* argv[]) {
test_one("annually", "*-01-01 00:00:00");
test_one("*:2/3", "*-*-* *:02/3:00");
test_one("2015-10-25 01:00:00 uTc", "2015-10-25 01:00:00 UTC");
+ test_one("2016-03-27 03:17:00.4200005", "2016-03-27 03:17:00.420001");
+ test_one("2016-03-27 03:17:00/0.42", "2016-03-27 03:17:00/0.420000");
+ test_one("2016-03-27 03:17:00/0.42", "2016-03-27 03:17:00/0.420000");
test_next("2016-03-27 03:17:00", "", 12345, 1459048620000000);
test_next("2016-03-27 03:17:00", "CET", 12345, 1459041420000000);
@@ -131,11 +134,19 @@ int main(int argc, char* argv[]) {
test_next("2016-03-27 03:17:00 UTC", "", 12345, 1459048620000000);
test_next("2016-03-27 03:17:00 UTC", "CET", 12345, 1459048620000000);
test_next("2016-03-27 03:17:00 UTC", "EET", 12345, 1459048620000000);
+ test_next("2016-03-27 03:17:00.420000001 UTC", "EET", 12345, 1459048620420000);
+ test_next("2016-03-27 03:17:00.4200005 UTC", "EET", 12345, 1459048620420001);
+ test_next("2015-11-13 09:11:23.42", "EET", 12345, 1447398683420000);
+ test_next("2015-11-13 09:11:23.42/1.77", "EET", 1447398683420000, 1447398685190000);
+ test_next("2015-11-13 09:11:23.42/1.77", "EET", 1447398683419999, 1447398683420000);
assert_se(calendar_spec_from_string("test", &c) < 0);
assert_se(calendar_spec_from_string("", &c) < 0);
assert_se(calendar_spec_from_string("7", &c) < 0);
assert_se(calendar_spec_from_string("121212:1:2", &c) < 0);
+ assert_se(calendar_spec_from_string("2000-03-05.23 00:00:00", &c) < 0);
+ assert_se(calendar_spec_from_string("2000-03-05 00:00.1:00", &c) < 0);
+ assert_se(calendar_spec_from_string("00:00:00/0.00000001", &c) < 0);
return 0;
}
diff --git a/sysctl.d/50-coredump.conf.in b/sysctl.d/50-coredump.conf.in
index d5f600ef45..5e04c821b6 100644
--- a/sysctl.d/50-coredump.conf.in
+++ b/sysctl.d/50-coredump.conf.in
@@ -9,4 +9,4 @@
# and systemd-coredump(8) and core(5) for the explanation of the
# setting below.
-kernel.core_pattern=|@rootlibexecdir@/systemd-coredump %p %u %g %s %t %e
+kernel.core_pattern=|@rootlibexecdir@/systemd-coredump %P %u %g %s %t %e