diff options
-rw-r--r-- | Makefile.am | 4 | ||||
-rw-r--r-- | NEWS | 2 | ||||
-rw-r--r-- | configure.ac | 2 | ||||
-rw-r--r-- | man/systemd.socket.xml | 9 | ||||
-rw-r--r-- | man/systemd.time.xml | 5 | ||||
-rw-r--r-- | man/systemd.timer.xml | 9 | ||||
-rw-r--r-- | src/basic/calendarspec.c | 236 | ||||
-rw-r--r-- | src/basic/calendarspec.h | 2 | ||||
-rw-r--r-- | src/basic/parse-util.c | 36 | ||||
-rw-r--r-- | src/basic/parse-util.h | 2 | ||||
-rw-r--r-- | src/basic/time-util.c | 22 | ||||
-rw-r--r-- | src/core/dbus-manager.c | 53 | ||||
-rw-r--r-- | src/core/dbus-socket.c | 1 | ||||
-rw-r--r-- | src/core/dbus-timer.c | 17 | ||||
-rw-r--r-- | src/core/job.c | 73 | ||||
-rw-r--r-- | src/core/load-fragment-gperf.gperf.m4 | 2 | ||||
-rw-r--r-- | src/core/load-fragment.c | 31 | ||||
-rw-r--r-- | src/core/load-fragment.h | 1 | ||||
-rw-r--r-- | src/core/socket.c | 13 | ||||
-rw-r--r-- | src/core/socket.h | 2 | ||||
-rw-r--r-- | src/core/timer.c | 47 | ||||
-rw-r--r-- | src/core/timer.h | 1 | ||||
-rw-r--r-- | src/core/unit.c | 55 | ||||
-rw-r--r-- | src/core/unit.h | 2 | ||||
-rw-r--r-- | src/run/run.c | 76 | ||||
-rw-r--r-- | src/test/test-calendarspec.c | 13 | ||||
-rw-r--r-- | sysctl.d/50-coredump.conf.in | 2 |
27 files changed, 513 insertions, 205 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 @@ -188,7 +188,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/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.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..3841588820 100644 --- a/man/systemd.timer.xml +++ b/man/systemd.timer.xml @@ -243,6 +243,15 @@ again after any work that is to be done is finished. Defaults to <varname>false</varname>.</para></listitem> </varlistentry> + + <varlistentry> + <term><varname>RemainAfterExit=</varname></term> + + <listitem><para>Takes a boolean argument. If true, an elapsed + timer will stay loaded, and its state remains + queriable. 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..2e6c479c3a 100644 --- a/src/core/dbus-timer.c +++ b/src/core/dbus-timer.c @@ -182,6 +182,7 @@ const sd_bus_vtable bus_timer_vtable[] = { SD_BUS_PROPERTY("AccuracyUSec", "t", bus_property_get_usec, offsetof(Timer, accuracy_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 }; @@ -283,7 +284,6 @@ static int bus_timer_set_transient_property( return 1; } else if (streq(name, "WakeSystem")) { - int b; r = sd_bus_message_read(message, "b", &b); @@ -292,11 +292,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..0dbe38d7e5 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,6 +345,7 @@ 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.Unit, config_parse_trigger_unit, 0, 0 m4_dnl 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..0587452cfb 100644 --- a/src/core/timer.c +++ b/src/core/timer.c @@ -55,6 +55,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 +218,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 +278,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 +298,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; @@ -314,6 +334,7 @@ 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 +395,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 +407,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 +444,16 @@ 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)); + 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); diff --git a/src/core/timer.h b/src/core/timer.h index ac5af6a93c..6bc9fbed3d 100644 --- a/src/core/timer.h +++ b/src/core/timer.h @@ -73,6 +73,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/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/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 |