summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/activate/activate.c44
-rw-r--r--src/basic/fs-util.c3
-rw-r--r--src/basic/hexdecoct.c61
-rw-r--r--src/basic/hexdecoct.h5
-rw-r--r--src/basic/random-util.c15
-rw-r--r--src/basic/rlimit-util.c200
-rw-r--r--src/basic/rlimit-util.h5
-rw-r--r--src/basic/terminal-util.c4
-rw-r--r--src/basic/time-util.h13
-rw-r--r--src/basic/verbs.c6
-rw-r--r--src/basic/verbs.h3
-rw-r--r--src/boot/bootctl.c16
-rw-r--r--src/core/busname.c20
-rw-r--r--src/core/dbus-execute.c59
-rw-r--r--src/core/dbus-service.c16
-rw-r--r--src/core/dbus-timer.c14
-rw-r--r--src/core/dbus-unit.c21
-rw-r--r--src/core/execute.c64
-rw-r--r--src/core/execute.h2
-rw-r--r--src/core/job.c31
-rw-r--r--src/core/job.h5
-rw-r--r--src/core/load-fragment-gperf.gperf.m421
-rw-r--r--src/core/load-fragment.c217
-rw-r--r--src/core/load-fragment.h3
-rw-r--r--src/core/main.c48
-rw-r--r--src/core/manager.c6
-rw-r--r--src/core/mount-setup.c27
-rw-r--r--src/core/mount.c33
-rw-r--r--src/core/scope.c37
-rw-r--r--src/core/service.c217
-rw-r--r--src/core/service.h1
-rw-r--r--src/core/socket.c37
-rw-r--r--src/core/swap.c20
-rw-r--r--src/core/transaction.c8
-rw-r--r--src/core/unit.c37
-rw-r--r--src/core/unit.h4
-rw-r--r--src/gpt-auto-generator/gpt-auto-generator.c11
-rw-r--r--src/journal/cat.c10
-rw-r--r--src/journal/journal-internal.h23
-rw-r--r--src/journal/journalctl.c162
-rw-r--r--src/journal/sd-journal.c190
-rw-r--r--src/libsystemd/libsystemd.sym8
-rw-r--r--src/libsystemd/sd-event/sd-event.c16
-rw-r--r--src/libsystemd/sd-netlink/sd-netlink.c10
-rw-r--r--src/login/logind-dbus.c1
-rw-r--r--src/login/logind.c7
-rw-r--r--src/nspawn/nspawn-register.c8
-rw-r--r--src/nspawn/nspawn.c43
-rw-r--r--src/resolve/dns-type.c84
-rw-r--r--src/resolve/dns-type.h11
-rw-r--r--src/resolve/resolve-tool.c43
-rw-r--r--src/resolve/resolved-bus.c1
-rw-r--r--src/resolve/resolved-dns-dnssec.c201
-rw-r--r--src/resolve/resolved-dns-dnssec.h1
-rw-r--r--src/resolve/resolved-dns-packet.c49
-rw-r--r--src/resolve/resolved-dns-query.c47
-rw-r--r--src/resolve/resolved-dns-query.h4
-rw-r--r--src/resolve/resolved-dns-rr.c184
-rw-r--r--src/resolve/resolved-dns-rr.h11
-rw-r--r--src/resolve/resolved-dns-scope.c27
-rw-r--r--src/resolve/resolved-dns-scope.h2
-rw-r--r--src/resolve/resolved-dns-server.c13
-rw-r--r--src/resolve/resolved-dns-server.h4
-rw-r--r--src/resolve/resolved-dns-transaction.c15
-rw-r--r--src/resolve/resolved-link.c21
-rw-r--r--src/resolve/resolved-link.h4
-rw-r--r--src/resolve/resolved-manager.c17
-rw-r--r--src/resolve/resolved-manager.h2
-rw-r--r--src/resolve/resolved-resolv-conf.c2
-rw-r--r--src/resolve/resolved-resolv-conf.h2
-rw-r--r--src/resolve/resolved.c2
-rw-r--r--src/resolve/test-dnssec-complex.c2
-rw-r--r--src/resolve/test-resolve-tables.c27
-rw-r--r--src/run/run.c8
-rw-r--r--src/shared/bus-util.c117
-rw-r--r--src/shared/logs-show.c27
-rw-r--r--src/shared/test-tables.h15
-rw-r--r--src/systemctl/systemctl.c204
-rw-r--r--src/systemd/sd-journal.h16
-rw-r--r--src/test/test-rlimit-util.c35
-rw-r--r--src/test/test-time.c15
-rw-r--r--src/test/test-unit-file.c29
-rw-r--r--src/test/test-util.c21
-rw-r--r--src/tmpfiles/tmpfiles.c23
-rw-r--r--src/udev/udev-builtin-net_id.c11
85 files changed, 2064 insertions, 1045 deletions
diff --git a/src/activate/activate.c b/src/activate/activate.c
index 95083441ab..558d16824a 100644
--- a/src/activate/activate.c
+++ b/src/activate/activate.c
@@ -151,24 +151,44 @@ static int launch(char* name, char **argv, char **env, int fds) {
return log_oom();
STRV_FOREACH(s, arg_setenv) {
- if (strchr(*s, '='))
- envp[n_env++] = *s;
- else {
+ if (strchr(*s, '=')) {
+ char *k;
+
+ k = strdup(*s);
+ if (!k)
+ return log_oom();
+
+ envp[n_env++] = k;
+ } else {
_cleanup_free_ char *p;
+ const char *n;
p = strappend(*s, "=");
if (!p)
return log_oom();
- envp[n_env] = strv_find_prefix(env, p);
- if (envp[n_env])
- n_env ++;
+
+ n = strv_find_prefix(env, p);
+ if (!n)
+ continue;
+
+ envp[n_env] = strdup(n);
+ if (!envp[n_env])
+ return log_oom();
}
}
for (i = 0; i < ELEMENTSOF(tocopy); i++) {
- envp[n_env] = strv_find_prefix(env, tocopy[i]);
- if (envp[n_env])
- n_env ++;
+ const char *n;
+
+ n = strv_find_prefix(env, tocopy[i]);
+ if (!n)
+ continue;
+
+ envp[n_env] = strdup(n);
+ if (!envp[n_env])
+ return log_oom();
+
+ n_env ++;
}
if ((asprintf((char**)(envp + n_env++), "LISTEN_FDS=%d", fds) < 0) ||
@@ -309,12 +329,12 @@ static void help(void) {
printf("%s [OPTIONS...]\n\n"
"Listen on sockets and launch child on connection.\n\n"
"Options:\n"
- " -d --datagram Datagram sockets\n"
+ " -h --help Show this help and exit\n"
+ " --version Print version string and exit\n"
" -l --listen=ADDR Listen for raw connections at ADDR\n"
+ " -d --datagram Listen on datagram instead of stream socket\n"
" -a --accept Spawn separate child for each connection\n"
- " -h --help Show this help and exit\n"
" -E --setenv=NAME[=VALUE] Pass an environment variable to children\n"
- " --version Print version string and exit\n"
"\n"
"Note: file descriptors from sd_listen_fds() will be passed through.\n"
, program_invocation_short_name);
diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c
index d31bd6e273..61b651b573 100644
--- a/src/basic/fs-util.c
+++ b/src/basic/fs-util.c
@@ -341,7 +341,8 @@ int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gi
if (parents)
mkdir_parents(path, 0755);
- fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, mode > 0 ? mode : 0644);
+ fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY,
+ (mode == 0 || mode == MODE_INVALID) ? 0644 : mode);
if (fd < 0)
return -errno;
diff --git a/src/basic/hexdecoct.c b/src/basic/hexdecoct.c
index 1e907de228..f30e028f45 100644
--- a/src/basic/hexdecoct.c
+++ b/src/basic/hexdecoct.c
@@ -514,14 +514,14 @@ int unbase64char(char c) {
return -EINVAL;
}
-char *base64mem(const void *p, size_t l) {
+ssize_t base64mem(const void *p, size_t l, char **out) {
char *r, *z;
const uint8_t *x;
/* three input bytes makes four output bytes, padding is added so we must round up */
z = r = malloc(4 * (l + 2) / 3 + 1);
if (!r)
- return NULL;
+ return -ENOMEM;
for (x = p; x < (const uint8_t*) p + (l / 3) * 3; x += 3) {
/* x[0] == XXXXXXXX; x[1] == YYYYYYYY; x[2] == ZZZZZZZZ */
@@ -549,9 +549,64 @@ char *base64mem(const void *p, size_t l) {
}
*z = 0;
- return r;
+ *out = r;
+ return z - r;
+}
+
+static int base64_append_width(char **prefix, int plen,
+ const char *sep, int indent,
+ const void *p, size_t l,
+ int width) {
+
+ _cleanup_free_ char *x = NULL;
+ char *t, *s;
+ ssize_t slen, len, avail;
+ int line, lines;
+
+ len = base64mem(p, l, &x);
+ if (len <= 0)
+ return len;
+
+ lines = (len + width - 1) / width;
+
+ slen = sep ? strlen(sep) : 0;
+ t = realloc(*prefix, plen + 1 + slen + (indent + width + 1) * lines);
+ if (!t)
+ return -ENOMEM;
+
+ memcpy(t + plen, sep, slen);
+
+ for (line = 0, s = t + plen + slen, avail = len; line < lines; line++) {
+ int act = MIN(width, avail);
+
+ if (line > 0 || sep) {
+ memset(s, ' ', indent);
+ s += indent;
+ }
+
+ memcpy(s, x + width * line, act);
+ s += act;
+ *(s++) = line < lines - 1 ? '\n' : '\0';
+ avail -= act;
+ }
+ assert(avail == 0);
+
+ *prefix = t;
+ return 0;
}
+int base64_append(char **prefix, int plen,
+ const void *p, size_t l,
+ int indent, int width) {
+ if (plen > width / 2 || plen + indent > width)
+ /* leave indent on the left, keep last column free */
+ return base64_append_width(prefix, plen, "\n", indent, p, l, width - indent - 1);
+ else
+ /* leave plen on the left, keep last column free */
+ return base64_append_width(prefix, plen, NULL, plen, p, l, width - plen - 1);
+};
+
+
int unbase64mem(const char *p, size_t l, void **mem, size_t *_len) {
_cleanup_free_ uint8_t *r = NULL;
int a, b, c, d;
diff --git a/src/basic/hexdecoct.h b/src/basic/hexdecoct.h
index d9eb54a8a1..243c5e921e 100644
--- a/src/basic/hexdecoct.h
+++ b/src/basic/hexdecoct.h
@@ -49,7 +49,10 @@ int unbase64char(char c) _const_;
char *base32hexmem(const void *p, size_t l, bool padding);
int unbase32hexmem(const char *p, size_t l, bool padding, void **mem, size_t *len);
-char *base64mem(const void *p, size_t l);
+ssize_t base64mem(const void *p, size_t l, char **out);
+int base64_append(char **prefix, int plen,
+ const void *p, size_t l,
+ int margin, int width);
int unbase64mem(const char *p, size_t l, void **mem, size_t *len);
void hexdump(FILE *f, const void *p, size_t s);
diff --git a/src/basic/random-util.c b/src/basic/random-util.c
index e1543da5a3..2f468db770 100644
--- a/src/basic/random-util.c
+++ b/src/basic/random-util.c
@@ -95,17 +95,18 @@ void initialize_srand(void) {
if (srand_called)
return;
- x = 0;
-
#ifdef HAVE_SYS_AUXV_H
- /* The kernel provides us with a bit of entropy in auxv, so
- * let's try to make use of that to seed the pseudo-random
- * generator. It's better than nothing... */
+ /* The kernel provides us with 16 bytes of entropy in auxv, so let's try to make use of that to seed the
+ * pseudo-random generator. It's better than nothing... */
auxv = (void*) getauxval(AT_RANDOM);
- if (auxv)
- x ^= *(unsigned*) auxv;
+ if (auxv) {
+ assert_cc(sizeof(x) < 16);
+ memcpy(&x, auxv, sizeof(x));
+ } else
#endif
+ x = 0;
+
x ^= (unsigned) now(CLOCK_REALTIME);
x ^= (unsigned) gettid();
diff --git a/src/basic/rlimit-util.c b/src/basic/rlimit-util.c
index 44f885db16..8a921a27cb 100644
--- a/src/basic/rlimit-util.c
+++ b/src/basic/rlimit-util.c
@@ -22,10 +22,14 @@
#include <errno.h>
#include <sys/resource.h>
+#include "alloc-util.h"
+#include "extract-word.h"
+#include "formats-util.h"
#include "macro.h"
#include "missing.h"
#include "rlimit-util.h"
#include "string-table.h"
+#include "time-util.h"
int setrlimit_closest(int resource, const struct rlimit *rlim) {
struct rlimit highest, fixed;
@@ -51,6 +55,202 @@ int setrlimit_closest(int resource, const struct rlimit *rlim) {
return 0;
}
+static int rlimit_parse_u64(const char *val, rlim_t *ret) {
+ uint64_t u;
+ int r;
+
+ assert(val);
+ assert(ret);
+
+ if (streq(val, "infinity")) {
+ *ret = RLIM_INFINITY;
+ return 0;
+ }
+
+ /* setrlimit(2) suggests rlim_t is always 64bit on Linux. */
+ assert_cc(sizeof(rlim_t) == sizeof(uint64_t));
+
+ r = safe_atou64(val, &u);
+ if (r < 0)
+ return r;
+ if (u >= (uint64_t) RLIM_INFINITY)
+ return -ERANGE;
+
+ *ret = (rlim_t) u;
+ return 0;
+}
+
+static int rlimit_parse_size(const char *val, rlim_t *ret) {
+ uint64_t u;
+ int r;
+
+ assert(val);
+ assert(ret);
+
+ if (streq(val, "infinity")) {
+ *ret = RLIM_INFINITY;
+ return 0;
+ }
+
+ r = parse_size(val, 1024, &u);
+ if (r < 0)
+ return r;
+ if (u >= (uint64_t) RLIM_INFINITY)
+ return -ERANGE;
+
+ *ret = (rlim_t) u;
+ return 0;
+}
+
+static int rlimit_parse_sec(const char *val, rlim_t *ret) {
+ uint64_t u;
+ usec_t t;
+ int r;
+
+ assert(val);
+ assert(ret);
+
+ if (streq(val, "infinity")) {
+ *ret = RLIM_INFINITY;
+ return 0;
+ }
+
+ r = parse_sec(val, &t);
+ if (r < 0)
+ return r;
+ if (t == USEC_INFINITY) {
+ *ret = RLIM_INFINITY;
+ return 0;
+ }
+
+ u = (uint64_t) DIV_ROUND_UP(t, USEC_PER_SEC);
+ if (u >= (uint64_t) RLIM_INFINITY)
+ return -ERANGE;
+
+ *ret = (rlim_t) u;
+ return 0;
+}
+
+static int rlimit_parse_usec(const char *val, rlim_t *ret) {
+ usec_t t;
+ int r;
+
+ assert(val);
+ assert(ret);
+
+ if (streq(val, "infinity")) {
+ *ret = RLIM_INFINITY;
+ return 0;
+ }
+
+ r = parse_time(val, &t, 1);
+ if (r < 0)
+ return r;
+ if (t == USEC_INFINITY) {
+ *ret = RLIM_INFINITY;
+ return 0;
+ }
+
+ *ret = (rlim_t) t;
+ return 0;
+}
+
+static int (*const rlimit_parse_table[_RLIMIT_MAX])(const char *val, rlim_t *ret) = {
+ [RLIMIT_CPU] = rlimit_parse_sec,
+ [RLIMIT_FSIZE] = rlimit_parse_size,
+ [RLIMIT_DATA] = rlimit_parse_size,
+ [RLIMIT_STACK] = rlimit_parse_size,
+ [RLIMIT_CORE] = rlimit_parse_size,
+ [RLIMIT_RSS] = rlimit_parse_size,
+ [RLIMIT_NOFILE] = rlimit_parse_u64,
+ [RLIMIT_AS] = rlimit_parse_size,
+ [RLIMIT_NPROC] = rlimit_parse_u64,
+ [RLIMIT_MEMLOCK] = rlimit_parse_size,
+ [RLIMIT_LOCKS] = rlimit_parse_u64,
+ [RLIMIT_SIGPENDING] = rlimit_parse_u64,
+ [RLIMIT_MSGQUEUE] = rlimit_parse_size,
+ [RLIMIT_NICE] = rlimit_parse_u64,
+ [RLIMIT_RTPRIO] = rlimit_parse_u64,
+ [RLIMIT_RTTIME] = rlimit_parse_usec,
+};
+
+int rlimit_parse_one(int resource, const char *val, rlim_t *ret) {
+ assert(val);
+ assert(ret);
+
+ if (resource < 0)
+ return -EINVAL;
+ if (resource >= _RLIMIT_MAX)
+ return -EINVAL;
+
+ return rlimit_parse_table[resource](val, ret);
+}
+
+int rlimit_parse(int resource, const char *val, struct rlimit *ret) {
+ _cleanup_free_ char *hard = NULL, *soft = NULL;
+ rlim_t hl, sl;
+ int r;
+
+ assert(val);
+ assert(ret);
+
+ r = extract_first_word(&val, &soft, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -EINVAL;
+
+ r = rlimit_parse_one(resource, soft, &sl);
+ if (r < 0)
+ return r;
+
+ r = extract_first_word(&val, &hard, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
+ if (r < 0)
+ return r;
+ if (!isempty(val))
+ return -EINVAL;
+ if (r == 0)
+ hl = sl;
+ else {
+ r = rlimit_parse_one(resource, hard, &hl);
+ if (r < 0)
+ return r;
+ if (sl > hl)
+ return -EILSEQ;
+ }
+
+ *ret = (struct rlimit) {
+ .rlim_cur = sl,
+ .rlim_max = hl,
+ };
+
+ return 0;
+}
+
+int rlimit_format(const struct rlimit *rl, char **ret) {
+ char *s = NULL;
+
+ assert(rl);
+ assert(ret);
+
+ if (rl->rlim_cur >= RLIM_INFINITY && rl->rlim_max >= RLIM_INFINITY)
+ s = strdup("infinity");
+ else if (rl->rlim_cur >= RLIM_INFINITY)
+ (void) asprintf(&s, "infinity:" RLIM_FMT, rl->rlim_max);
+ else if (rl->rlim_max >= RLIM_INFINITY)
+ (void) asprintf(&s, RLIM_FMT ":infinity", rl->rlim_cur);
+ else if (rl->rlim_cur == rl->rlim_max)
+ (void) asprintf(&s, RLIM_FMT, rl->rlim_cur);
+ else
+ (void) asprintf(&s, RLIM_FMT ":" RLIM_FMT, rl->rlim_cur, rl->rlim_max);
+
+ if (!s)
+ return -ENOMEM;
+
+ *ret = s;
+ return 0;
+}
+
static const char* const rlimit_table[_RLIMIT_MAX] = {
[RLIMIT_CPU] = "LimitCPU",
[RLIMIT_FSIZE] = "LimitFSIZE",
diff --git a/src/basic/rlimit-util.h b/src/basic/rlimit-util.h
index 262f86dd04..abf3c57934 100644
--- a/src/basic/rlimit-util.h
+++ b/src/basic/rlimit-util.h
@@ -30,4 +30,9 @@ int rlimit_from_string(const char *s) _pure_;
int setrlimit_closest(int resource, const struct rlimit *rlim);
+int rlimit_parse_one(int resource, const char *val, rlim_t *ret);
+int rlimit_parse(int resource, const char *val, struct rlimit *ret);
+
+int rlimit_format(const struct rlimit *rl, char **ret);
+
#define RLIMIT_MAKE_CONST(lim) ((struct rlimit) { lim, lim })
diff --git a/src/basic/terminal-util.c b/src/basic/terminal-util.c
index fedfc8a5df..0a9d2bbdef 100644
--- a/src/basic/terminal-util.c
+++ b/src/basic/terminal-util.c
@@ -726,9 +726,7 @@ bool tty_is_vc_resolve(const char *tty) {
}
const char *default_term_for_tty(const char *tty) {
- assert(tty);
-
- return tty_is_vc_resolve(tty) ? "TERM=linux" : "TERM=vt220";
+ return tty && tty_is_vc_resolve(tty) ? "TERM=linux" : "TERM=vt220";
}
int fd_columns(int fd) {
diff --git a/src/basic/time-util.h b/src/basic/time-util.h
index 7321e3c670..b37d5ad5dc 100644
--- a/src/basic/time-util.h
+++ b/src/basic/time-util.h
@@ -127,3 +127,16 @@ time_t mktime_or_timegm(struct tm *tm, bool utc);
struct tm *localtime_or_gmtime_r(const time_t *t, struct tm *tm, bool utc);
unsigned long usec_to_jiffies(usec_t usec);
+
+static inline usec_t usec_add(usec_t a, usec_t b) {
+ usec_t c;
+
+ /* Adds two time values, and makes sure USEC_INFINITY as input results as USEC_INFINITY in output, and doesn't
+ * overflow. */
+
+ c = a + b;
+ if (c < a || c < b) /* overflow check */
+ return USEC_INFINITY;
+
+ return c;
+}
diff --git a/src/basic/verbs.c b/src/basic/verbs.c
index 7feb47c48e..6dded9fb77 100644
--- a/src/basic/verbs.c
+++ b/src/basic/verbs.c
@@ -28,6 +28,7 @@
#include "macro.h"
#include "string-util.h"
#include "verbs.h"
+#include "virt.h"
int dispatch_verb(int argc, char *argv[], const Verb verbs[], void *userdata) {
const Verb *verb;
@@ -84,6 +85,11 @@ int dispatch_verb(int argc, char *argv[], const Verb verbs[], void *userdata) {
return -EINVAL;
}
+ if ((verb->flags & VERB_NOCHROOT) && running_in_chroot() > 0) {
+ log_info("Running in chroot, ignoring request.");
+ return 0;
+ }
+
if (name)
return verb->dispatch(left, argv + optind, userdata);
else {
diff --git a/src/basic/verbs.h b/src/basic/verbs.h
index d59e4d59b8..4132cad773 100644
--- a/src/basic/verbs.h
+++ b/src/basic/verbs.h
@@ -22,7 +22,8 @@
***/
#define VERB_ANY ((unsigned) -1)
-#define VERB_DEFAULT 1
+#define VERB_DEFAULT 1U
+#define VERB_NOCHROOT 2U
typedef struct {
const char *verb;
diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c
index 77eea6aada..13cf323bb7 100644
--- a/src/boot/bootctl.c
+++ b/src/boot/bootctl.c
@@ -39,6 +39,7 @@
#include "alloc-util.h"
#include "blkid-util.h"
+#include "dirent-util.h"
#include "efivars.h"
#include "fd-util.h"
#include "fileio.h"
@@ -249,13 +250,10 @@ static int enumerate_binaries(const char *esp_path, const char *path, const char
return log_error_errno(errno, "Failed to read \"%s\": %m", p);
}
- while ((de = readdir(d))) {
+ FOREACH_DIRENT(de, d, break) {
_cleanup_close_ int fd = -1;
_cleanup_free_ char *v = NULL;
- if (de->d_name[0] == '.')
- continue;
-
if (!endswith_no_case(de->d_name, ".efi"))
continue;
@@ -614,12 +612,9 @@ static int install_binaries(const char *esp_path, bool force) {
if (!d)
return log_error_errno(errno, "Failed to open \""BOOTLIBDIR"\": %m");
- while ((de = readdir(d))) {
+ FOREACH_DIRENT(de, d, break) {
int k;
- if (de->d_name[0] == '.')
- continue;
-
if (!endswith_no_case(de->d_name, ".efi"))
continue;
@@ -797,13 +792,10 @@ static int remove_boot_efi(const char *esp_path) {
return log_error_errno(errno, "Failed to open directory \"%s\": %m", p);
}
- while ((de = readdir(d))) {
+ FOREACH_DIRENT(de, d, break) {
_cleanup_close_ int fd = -1;
_cleanup_free_ char *v = NULL;
- if (de->d_name[0] == '.')
- continue;
-
if (!endswith_no_case(de->d_name, ".efi"))
continue;
diff --git a/src/core/busname.c b/src/core/busname.c
index a949cd6d3f..ed083a8412 100644
--- a/src/core/busname.c
+++ b/src/core/busname.c
@@ -112,29 +112,27 @@ static void busname_done(Unit *u) {
n->timer_event_source = sd_event_source_unref(n->timer_event_source);
}
-static int busname_arm_timer(BusName *n) {
+static int busname_arm_timer(BusName *n, usec_t usec) {
int r;
assert(n);
- if (n->timeout_usec <= 0) {
- n->timer_event_source = sd_event_source_unref(n->timer_event_source);
- return 0;
- }
-
if (n->timer_event_source) {
- r = sd_event_source_set_time(n->timer_event_source, now(CLOCK_MONOTONIC) + n->timeout_usec);
+ r = sd_event_source_set_time(n->timer_event_source, usec);
if (r < 0)
return r;
return sd_event_source_set_enabled(n->timer_event_source, SD_EVENT_ONESHOT);
}
+ if (usec == USEC_INFINITY)
+ return 0;
+
r = sd_event_add_time(
UNIT(n)->manager->event,
&n->timer_event_source,
CLOCK_MONOTONIC,
- now(CLOCK_MONOTONIC) + n->timeout_usec, 0,
+ usec, 0,
busname_dispatch_timer, n);
if (r < 0)
return r;
@@ -372,7 +370,7 @@ static int busname_coldplug(Unit *u) {
if (r < 0)
return r;
- r = busname_arm_timer(n);
+ r = busname_arm_timer(n, usec_add(u->state_change_timestamp.monotonic, n->timeout_usec));
if (r < 0)
return r;
}
@@ -397,7 +395,7 @@ static int busname_make_starter(BusName *n, pid_t *_pid) {
pid_t pid;
int r;
- r = busname_arm_timer(n);
+ r = busname_arm_timer(n, usec_add(now(CLOCK_MONOTONIC), n->timeout_usec));
if (r < 0)
goto fail;
@@ -475,7 +473,7 @@ static void busname_enter_signal(BusName *n, BusNameState state, BusNameResult f
}
if (r > 0) {
- r = busname_arm_timer(n);
+ r = busname_arm_timer(n, usec_add(now(CLOCK_MONOTONIC), n->timeout_usec));
if (r < 0) {
log_unit_warning_errno(UNIT(n), r, "Failed to arm timer: %m");
goto fail;
diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c
index eae0808f9e..2de28f43e1 100644
--- a/src/core/dbus-execute.c
+++ b/src/core/dbus-execute.c
@@ -835,7 +835,8 @@ int bus_exec_context_set_transient_property(
UnitSetPropertiesMode mode,
sd_bus_error *error) {
- int r;
+ const char *soft = NULL;
+ int r, ri;
assert(u);
assert(c);
@@ -1492,7 +1493,23 @@ int bus_exec_context_set_transient_property(
return 1;
- } else if (rlimit_from_string(name) >= 0) {
+ }
+
+ ri = rlimit_from_string(name);
+ if (ri < 0) {
+ soft = endswith(name, "Soft");
+ if (soft) {
+ const char *n;
+
+ n = strndupa(name, soft - name);
+ ri = rlimit_from_string(n);
+ if (ri >= 0)
+ name = n;
+
+ }
+ }
+
+ if (ri >= 0) {
uint64_t rl;
rlim_t x;
@@ -1510,22 +1527,36 @@ int bus_exec_context_set_transient_property(
}
if (mode != UNIT_CHECK) {
- int z;
-
- z = rlimit_from_string(name);
+ _cleanup_free_ char *f = NULL;
+ struct rlimit nl;
+
+ if (c->rlimit[ri]) {
+ nl = *c->rlimit[ri];
+
+ if (soft)
+ nl.rlim_cur = x;
+ else
+ nl.rlim_max = x;
+ } else
+ /* When the resource limit is not initialized yet, then assign the value to both fields */
+ nl = (struct rlimit) {
+ .rlim_cur = x,
+ .rlim_max = x,
+ };
+
+ r = rlimit_format(&nl, &f);
+ if (r < 0)
+ return r;
- if (!c->rlimit[z]) {
- c->rlimit[z] = new(struct rlimit, 1);
- if (!c->rlimit[z])
+ if (c->rlimit[ri])
+ *c->rlimit[ri] = nl;
+ else {
+ c->rlimit[ri] = newdup(struct rlimit, &nl, 1);
+ if (!c->rlimit[ri])
return -ENOMEM;
}
- c->rlimit[z]->rlim_cur = c->rlimit[z]->rlim_max = x;
-
- if (x == RLIM_INFINITY)
- unit_write_drop_in_private_format(u, mode, name, "%s=infinity\n", name);
- else
- unit_write_drop_in_private_format(u, mode, name, "%s=%" PRIu64 "\n", name, rl);
+ unit_write_drop_in_private_format(u, mode, name, "%s=%s\n", name, f);
}
return 1;
diff --git a/src/core/dbus-service.c b/src/core/dbus-service.c
index 24f611a593..16f50238a1 100644
--- a/src/core/dbus-service.c
+++ b/src/core/dbus-service.c
@@ -49,6 +49,7 @@ const sd_bus_vtable bus_service_vtable[] = {
SD_BUS_PROPERTY("RestartUSec", "t", bus_property_get_usec, offsetof(Service, restart_usec), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("TimeoutStartUSec", "t", bus_property_get_usec, offsetof(Service, timeout_start_usec), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("TimeoutStopUSec", "t", bus_property_get_usec, offsetof(Service, timeout_stop_usec), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("RuntimeMaxUSec", "t", bus_property_get_usec, offsetof(Service, runtime_max_usec), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("WatchdogUSec", "t", bus_property_get_usec, offsetof(Service, watchdog_usec), SD_BUS_VTABLE_PROPERTY_CONST),
BUS_PROPERTY_DUAL_TIMESTAMP("WatchdogTimestamp", offsetof(Service, watchdog_timestamp), 0),
SD_BUS_PROPERTY("StartLimitInterval", "t", bus_property_get_usec, offsetof(Service, start_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -125,6 +126,19 @@ static int bus_service_set_transient_property(
}
return 1;
+ } else if (streq(name, "RuntimeMaxUSec")) {
+ usec_t u;
+
+ r = sd_bus_message_read(message, "t", &u);
+ if (r < 0)
+ return r;
+
+ if (mode != UNIT_CHECK) {
+ s->runtime_max_usec = u;
+ unit_write_drop_in_private_format(UNIT(s), mode, name, "RuntimeMaxSec=" USEC_FMT "us\n", u);
+ }
+
+ return 1;
} else if (STR_IN_SET(name,
"StandardInputFileDescriptor",
@@ -153,6 +167,8 @@ static int bus_service_set_transient_property(
asynchronous_close(s->stderr_fd);
s->stderr_fd = copy;
}
+
+ s->exec_context.stdio_as_fds = true;
}
return 1;
diff --git a/src/core/dbus-timer.c b/src/core/dbus-timer.c
index ec301df6d7..321ed5da37 100644
--- a/src/core/dbus-timer.c
+++ b/src/core/dbus-timer.c
@@ -267,19 +267,19 @@ static int bus_timer_set_transient_property(
return 1;
- } else if (streq(name, "AccuracySec")) {
-
+ } else if (STR_IN_SET(name, "AccuracyUSec", "AccuracySec")) {
usec_t u = 0;
+ if (streq(name, "AccuracySec"))
+ log_notice("Client is using obsolete AccuracySec= transient property, please use AccuracyUSec= instead.");
+
r = sd_bus_message_read(message, "t", &u);
if (r < 0)
return r;
if (mode != UNIT_CHECK) {
- char time[FORMAT_TIMESPAN_MAX];
-
t->accuracy_usec = u;
- unit_write_drop_in_private_format(UNIT(t), mode, name, "%s=%s\n", name, format_timespan(time, sizeof(time), u, USEC_PER_MSEC));
+ unit_write_drop_in_private_format(UNIT(t), mode, name, "AccuracySec=" USEC_FMT "us\n", u);
}
return 1;
@@ -292,10 +292,8 @@ static int bus_timer_set_transient_property(
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, "RandomizedDelaySec=%s\n", format_timespan(time, sizeof(time), u, USEC_PER_MSEC));
+ unit_write_drop_in_private_format(UNIT(t), mode, name, "RandomizedDelaySec=" USEC_FMT "us\n", u);
}
return 1;
diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c
index e4d2c08972..d7929e5566 100644
--- a/src/core/dbus-unit.c
+++ b/src/core/dbus-unit.c
@@ -458,7 +458,10 @@ int bus_unit_method_start_generic(
assert(u);
assert(job_type >= 0 && job_type < _JOB_TYPE_MAX);
- r = mac_selinux_unit_access_check(u, message, job_type == JOB_STOP ? "stop" : "start", error);
+ r = mac_selinux_unit_access_check(
+ u, message,
+ job_type_to_access_method(job_type),
+ error);
if (r < 0)
return r;
@@ -671,6 +674,7 @@ const sd_bus_vtable bus_unit_vtable[] = {
SD_BUS_PROPERTY("DropInPaths", "as", NULL, offsetof(Unit, dropin_paths), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("UnitFileState", "s", property_get_unit_file_state, 0, 0),
SD_BUS_PROPERTY("UnitFilePreset", "s", property_get_unit_file_preset, 0, 0),
+ BUS_PROPERTY_DUAL_TIMESTAMP("StateChangeTimestamp", offsetof(Unit, state_change_timestamp), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
BUS_PROPERTY_DUAL_TIMESTAMP("InactiveExitTimestamp", offsetof(Unit, inactive_exit_timestamp), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
BUS_PROPERTY_DUAL_TIMESTAMP("ActiveEnterTimestamp", offsetof(Unit, active_enter_timestamp), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
BUS_PROPERTY_DUAL_TIMESTAMP("ActiveExitTimestamp", offsetof(Unit, active_exit_timestamp), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
@@ -983,19 +987,20 @@ int bus_unit_queue_job(
assert(type >= 0 && type < _JOB_TYPE_MAX);
assert(mode >= 0 && mode < _JOB_MODE_MAX);
+ r = mac_selinux_unit_access_check(
+ u, message,
+ job_type_to_access_method(type),
+ error);
+ if (r < 0)
+ return r;
+
if (reload_if_possible && unit_can_reload(u)) {
if (type == JOB_RESTART)
type = JOB_RELOAD_OR_START;
else if (type == JOB_TRY_RESTART)
- type = JOB_RELOAD;
+ type = JOB_TRY_RELOAD;
}
- r = mac_selinux_unit_access_check(
- u, message,
- (type == JOB_START || type == JOB_RESTART || type == JOB_TRY_RESTART) ? "start" :
- type == JOB_STOP ? "stop" : "reload", error);
- if (r < 0)
- return r;
if (type == JOB_STOP &&
(u->load_state == UNIT_NOT_FOUND || u->load_state == UNIT_ERROR) &&
diff --git a/src/core/execute.c b/src/core/execute.c
index b9de2617a9..80db62131c 100644
--- a/src/core/execute.c
+++ b/src/core/execute.c
@@ -183,26 +183,41 @@ static int flags_fds(const int fds[], unsigned n_fds, bool nonblock) {
return 0;
}
-_pure_ static const char *tty_path(const ExecContext *context) {
+static const char *exec_context_tty_path(const ExecContext *context) {
assert(context);
+ if (context->stdio_as_fds)
+ return NULL;
+
if (context->tty_path)
return context->tty_path;
return "/dev/console";
}
-static void exec_context_tty_reset(const ExecContext *context) {
+static void exec_context_tty_reset(const ExecContext *context, const ExecParameters *p) {
+ const char *path;
+
assert(context);
- if (context->tty_vhangup)
- terminal_vhangup(tty_path(context));
+ path = exec_context_tty_path(context);
+
+ if (context->tty_vhangup) {
+ if (p && p->stdin_fd >= 0)
+ (void) terminal_vhangup_fd(p->stdin_fd);
+ else if (path)
+ (void) terminal_vhangup(path);
+ }
- if (context->tty_reset)
- reset_terminal(tty_path(context));
+ if (context->tty_reset) {
+ if (p && p->stdin_fd >= 0)
+ (void) reset_terminal_fd(p->stdin_fd, true);
+ else if (path)
+ (void) reset_terminal(path);
+ }
- if (context->tty_vt_disallocate && context->tty_path)
- vt_disallocate(context->tty_path);
+ if (context->tty_vt_disallocate && path)
+ (void) vt_disallocate(path);
}
static bool is_terminal_output(ExecOutput o) {
@@ -400,7 +415,7 @@ static int setup_input(
case EXEC_INPUT_TTY_FAIL: {
int fd, r;
- fd = acquire_terminal(tty_path(context),
+ fd = acquire_terminal(exec_context_tty_path(context),
i == EXEC_INPUT_TTY_FAIL,
i == EXEC_INPUT_TTY_FORCE,
false,
@@ -485,7 +500,7 @@ static int setup_output(
} else if (o == EXEC_OUTPUT_INHERIT) {
/* If input got downgraded, inherit the original value */
if (i == EXEC_INPUT_NULL && is_terminal_input(context->std_input))
- return open_terminal_as(tty_path(context), O_WRONLY, fileno);
+ return open_terminal_as(exec_context_tty_path(context), O_WRONLY, fileno);
/* If the input is connected to anything that's not a /dev/null, inherit that... */
if (i != EXEC_INPUT_NULL)
@@ -509,7 +524,7 @@ static int setup_output(
return dup2(STDIN_FILENO, fileno) < 0 ? -errno : fileno;
/* We don't reset the terminal if this is just about output */
- return open_terminal_as(tty_path(context), O_WRONLY, fileno);
+ return open_terminal_as(exec_context_tty_path(context), O_WRONLY, fileno);
case EXEC_OUTPUT_SYSLOG:
case EXEC_OUTPUT_SYSLOG_AND_CONSOLE:
@@ -1232,9 +1247,8 @@ static void do_idle_pipe_dance(int idle_pipe[4]) {
static int build_environment(
const ExecContext *c,
+ const ExecParameters *p,
unsigned n_fds,
- char ** fd_names,
- usec_t watchdog_usec,
const char *home,
const char *username,
const char *shell,
@@ -1262,7 +1276,7 @@ static int build_environment(
return -ENOMEM;
our_env[n_env++] = x;
- joined = strv_join(fd_names, ":");
+ joined = strv_join(p->fd_names, ":");
if (!joined)
return -ENOMEM;
@@ -1272,12 +1286,12 @@ static int build_environment(
our_env[n_env++] = x;
}
- if (watchdog_usec > 0) {
+ if (p->watchdog_usec > 0) {
if (asprintf(&x, "WATCHDOG_PID="PID_FMT, getpid()) < 0)
return -ENOMEM;
our_env[n_env++] = x;
- if (asprintf(&x, "WATCHDOG_USEC="USEC_FMT, watchdog_usec) < 0)
+ if (asprintf(&x, "WATCHDOG_USEC="USEC_FMT, p->watchdog_usec) < 0)
return -ENOMEM;
our_env[n_env++] = x;
}
@@ -1313,7 +1327,7 @@ static int build_environment(
c->std_error == EXEC_OUTPUT_TTY ||
c->tty_path) {
- x = strdup(default_term_for_tty(tty_path(c)));
+ x = strdup(default_term_for_tty(exec_context_tty_path(c)));
if (!x)
return -ENOMEM;
our_env[n_env++] = x;
@@ -1490,7 +1504,7 @@ static int exec_child(
return -errno;
}
- exec_context_tty_reset(context);
+ exec_context_tty_reset(context, params);
if (params->confirm_spawn) {
char response;
@@ -1991,7 +2005,7 @@ static int exec_child(
#endif
}
- r = build_environment(context, n_fds, params->fd_names, params->watchdog_usec, home, username, shell, &our_env);
+ r = build_environment(context, params, n_fds, home, username, shell, &our_env);
if (r < 0) {
*exit_status = EXIT_MEMORY;
return r;
@@ -2366,6 +2380,9 @@ static bool tty_may_match_dev_console(const char *tty) {
_cleanup_free_ char *active = NULL;
char *console;
+ if (!tty)
+ return true;
+
if (startswith(tty, "/dev/"))
tty += 5;
@@ -2383,11 +2400,14 @@ static bool tty_may_match_dev_console(const char *tty) {
}
bool exec_context_may_touch_console(ExecContext *ec) {
- return (ec->tty_reset || ec->tty_vhangup || ec->tty_vt_disallocate ||
+
+ return (ec->tty_reset ||
+ ec->tty_vhangup ||
+ ec->tty_vt_disallocate ||
is_terminal_input(ec->std_input) ||
is_terminal_output(ec->std_output) ||
is_terminal_output(ec->std_error)) &&
- tty_may_match_dev_console(tty_path(ec));
+ tty_may_match_dev_console(exec_context_tty_path(ec));
}
static void strv_fprintf(FILE *f, char **l) {
@@ -2726,7 +2746,7 @@ void exec_status_exit(ExecStatus *s, ExecContext *context, pid_t pid, int code,
if (context->utmp_id)
utmp_put_dead_process(context->utmp_id, pid, code, status);
- exec_context_tty_reset(context);
+ exec_context_tty_reset(context, NULL);
}
}
diff --git a/src/core/execute.h b/src/core/execute.h
index 8649620830..e4b93b603d 100644
--- a/src/core/execute.h
+++ b/src/core/execute.h
@@ -122,6 +122,8 @@ struct ExecContext {
nsec_t timer_slack_nsec;
+ bool stdio_as_fds;
+
char *tty_path;
bool tty_reset;
diff --git a/src/core/job.c b/src/core/job.c
index 274c554da9..1dcb872019 100644
--- a/src/core/job.c
+++ b/src/core/job.c
@@ -405,6 +405,13 @@ JobType job_type_collapse(JobType t, Unit *u) {
return JOB_RESTART;
+ case JOB_TRY_RELOAD:
+ s = unit_active_state(u);
+ if (UNIT_IS_INACTIVE_OR_DEACTIVATING(s))
+ return JOB_NOP;
+
+ return JOB_RELOAD;
+
case JOB_RELOAD_OR_START:
s = unit_active_state(u);
if (UNIT_IS_INACTIVE_OR_DEACTIVATING(s))
@@ -925,14 +932,14 @@ int job_start_timer(Job *j) {
j->begin_usec = now(CLOCK_MONOTONIC);
- if (j->unit->job_timeout <= 0)
+ if (j->unit->job_timeout == USEC_INFINITY)
return 0;
r = sd_event_add_time(
j->manager->event,
&j->timer_event_source,
CLOCK_MONOTONIC,
- j->begin_usec + j->unit->job_timeout, 0,
+ usec_add(j->begin_usec, j->unit->job_timeout), 0,
job_dispatch_timer, j);
if (r < 0)
return r;
@@ -1110,17 +1117,16 @@ int job_coldplug(Job *j) {
if (j->state == JOB_WAITING)
job_add_to_run_queue(j);
- if (j->begin_usec == 0 || j->unit->job_timeout == 0)
+ if (j->begin_usec == 0 || j->unit->job_timeout == USEC_INFINITY)
return 0;
- if (j->timer_event_source)
- j->timer_event_source = sd_event_source_unref(j->timer_event_source);
+ j->timer_event_source = sd_event_source_unref(j->timer_event_source);
r = sd_event_add_time(
j->manager->event,
&j->timer_event_source,
CLOCK_MONOTONIC,
- j->begin_usec + j->unit->job_timeout, 0,
+ usec_add(j->begin_usec, j->unit->job_timeout), 0,
job_dispatch_timer, j);
if (r < 0)
log_debug_errno(r, "Failed to restart timeout for job: %m");
@@ -1202,6 +1208,7 @@ static const char* const job_type_table[_JOB_TYPE_MAX] = {
[JOB_RELOAD_OR_START] = "reload-or-start",
[JOB_RESTART] = "restart",
[JOB_TRY_RESTART] = "try-restart",
+ [JOB_TRY_RELOAD] = "try-reload",
[JOB_NOP] = "nop",
};
@@ -1232,3 +1239,15 @@ static const char* const job_result_table[_JOB_RESULT_MAX] = {
};
DEFINE_STRING_TABLE_LOOKUP(job_result, JobResult);
+
+const char* job_type_to_access_method(JobType t) {
+ assert(t >= 0);
+ assert(t < _JOB_TYPE_MAX);
+
+ if (IN_SET(t, JOB_START, JOB_RESTART, JOB_TRY_RESTART))
+ return "start";
+ else if (t == JOB_STOP)
+ return "stop";
+ else
+ return "reload";
+}
diff --git a/src/core/job.h b/src/core/job.h
index 118b24e5b7..bbf5471e8b 100644
--- a/src/core/job.h
+++ b/src/core/job.h
@@ -66,6 +66,9 @@ enum JobType {
* Thus we never need to merge it with anything. */
JOB_TRY_RESTART = _JOB_TYPE_MAX_IN_TRANSACTION, /* if running, stop and then start */
+ /* Similar to JOB_TRY_RESTART but collapses to JOB_RELOAD or JOB_NOP */
+ JOB_TRY_RELOAD,
+
/* JOB_RELOAD_OR_START won't enter into a transaction and cannot result
* from transaction merging (there's no way for JOB_RELOAD and
* JOB_START to meet in one transaction). It can result from a merge
@@ -237,3 +240,5 @@ const char* job_result_to_string(JobResult t) _const_;
JobResult job_result_from_string(const char *s) _pure_;
int job_get_timeout(Job *j, uint64_t *timeout) _pure_;
+
+const char* job_type_to_access_method(JobType t);
diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4
index 29ab1b6b9e..2507db1932 100644
--- a/src/core/load-fragment-gperf.gperf.m4
+++ b/src/core/load-fragment-gperf.gperf.m4
@@ -60,22 +60,22 @@ $1.RestrictAddressFamilies, config_parse_address_families, 0,
$1.SystemCallArchitectures, config_parse_warn_compat, DISABLED_CONFIGURATION, 0
$1.SystemCallErrorNumber, config_parse_warn_compat, DISABLED_CONFIGURATION, 0
$1.RestrictAddressFamilies, config_parse_warn_compat, DISABLED_CONFIGURATION, 0')
-$1.LimitCPU, config_parse_sec_limit, RLIMIT_CPU, offsetof($1, exec_context.rlimit)
-$1.LimitFSIZE, config_parse_bytes_limit, RLIMIT_FSIZE, offsetof($1, exec_context.rlimit)
-$1.LimitDATA, config_parse_bytes_limit, RLIMIT_DATA, offsetof($1, exec_context.rlimit)
-$1.LimitSTACK, config_parse_bytes_limit, RLIMIT_STACK, offsetof($1, exec_context.rlimit)
-$1.LimitCORE, config_parse_bytes_limit, RLIMIT_CORE, offsetof($1, exec_context.rlimit)
-$1.LimitRSS, config_parse_bytes_limit, RLIMIT_RSS, offsetof($1, exec_context.rlimit)
+$1.LimitCPU, config_parse_limit, RLIMIT_CPU, offsetof($1, exec_context.rlimit)
+$1.LimitFSIZE, config_parse_limit, RLIMIT_FSIZE, offsetof($1, exec_context.rlimit)
+$1.LimitDATA, config_parse_limit, RLIMIT_DATA, offsetof($1, exec_context.rlimit)
+$1.LimitSTACK, config_parse_limit, RLIMIT_STACK, offsetof($1, exec_context.rlimit)
+$1.LimitCORE, config_parse_limit, RLIMIT_CORE, offsetof($1, exec_context.rlimit)
+$1.LimitRSS, config_parse_limit, RLIMIT_RSS, offsetof($1, exec_context.rlimit)
$1.LimitNOFILE, config_parse_limit, RLIMIT_NOFILE, offsetof($1, exec_context.rlimit)
-$1.LimitAS, config_parse_bytes_limit, RLIMIT_AS, offsetof($1, exec_context.rlimit)
+$1.LimitAS, config_parse_limit, RLIMIT_AS, offsetof($1, exec_context.rlimit)
$1.LimitNPROC, config_parse_limit, RLIMIT_NPROC, offsetof($1, exec_context.rlimit)
-$1.LimitMEMLOCK, config_parse_bytes_limit, RLIMIT_MEMLOCK, offsetof($1, exec_context.rlimit)
+$1.LimitMEMLOCK, config_parse_limit, RLIMIT_MEMLOCK, offsetof($1, exec_context.rlimit)
$1.LimitLOCKS, config_parse_limit, RLIMIT_LOCKS, offsetof($1, exec_context.rlimit)
$1.LimitSIGPENDING, config_parse_limit, RLIMIT_SIGPENDING, offsetof($1, exec_context.rlimit)
-$1.LimitMSGQUEUE, config_parse_bytes_limit, RLIMIT_MSGQUEUE, offsetof($1, exec_context.rlimit)
+$1.LimitMSGQUEUE, config_parse_limit, RLIMIT_MSGQUEUE, offsetof($1, exec_context.rlimit)
$1.LimitNICE, config_parse_limit, RLIMIT_NICE, offsetof($1, exec_context.rlimit)
$1.LimitRTPRIO, config_parse_limit, RLIMIT_RTPRIO, offsetof($1, exec_context.rlimit)
-$1.LimitRTTIME, config_parse_usec_limit, RLIMIT_RTTIME, offsetof($1, exec_context.rlimit)
+$1.LimitRTTIME, config_parse_limit, RLIMIT_RTTIME, offsetof($1, exec_context.rlimit)
$1.ReadWriteDirectories, config_parse_namespace_path_strv, 0, offsetof($1, exec_context.read_write_dirs)
$1.ReadOnlyDirectories, config_parse_namespace_path_strv, 0, offsetof($1, exec_context.read_only_dirs)
$1.InaccessibleDirectories, config_parse_namespace_path_strv, 0, offsetof($1, exec_context.inaccessible_dirs)
@@ -214,6 +214,7 @@ Service.RestartSec, config_parse_sec, 0,
Service.TimeoutSec, config_parse_service_timeout, 0, offsetof(Service, timeout_start_usec)
Service.TimeoutStartSec, config_parse_service_timeout, 0, offsetof(Service, timeout_start_usec)
Service.TimeoutStopSec, config_parse_service_timeout, 0, offsetof(Service, timeout_stop_usec)
+Service.RuntimeMaxSec, config_parse_sec, 0, offsetof(Service, runtime_max_usec)
Service.WatchdogSec, config_parse_sec, 0, offsetof(Service, watchdog_usec)
Service.StartLimitInterval, config_parse_sec, 0, offsetof(Service, start_limit.interval)
Service.StartLimitBurst, config_parse_unsigned, 0, offsetof(Service, start_limit.burst)
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
index 903e6f0cf6..3b37cc4cda 100644
--- a/src/core/load-fragment.c
+++ b/src/core/load-fragment.c
@@ -54,6 +54,7 @@
#include "parse-util.h"
#include "path-util.h"
#include "process-util.h"
+#include "rlimit-util.h"
#ifdef HAVE_SECCOMP
#include "seccomp-util.h"
#endif
@@ -1101,122 +1102,6 @@ int config_parse_capability_set(
return 0;
}
-static int rlim_parse_u64(const char *val, rlim_t *res) {
- int r = 0;
-
- if (streq(val, "infinity"))
- *res = RLIM_INFINITY;
- else {
- uint64_t u;
-
- /* setrlimit(2) suggests rlim_t is always 64bit on Linux. */
- assert_cc(sizeof(rlim_t) == sizeof(uint64_t));
-
- r = safe_atou64(val, &u);
- if (r >= 0 && u >= (uint64_t) RLIM_INFINITY)
- r = -ERANGE;
- if (r == 0)
- *res = (rlim_t) u;
- }
- return r;
-}
-
-static int rlim_parse_size(const char *val, rlim_t *res) {
- int r = 0;
-
- if (streq(val, "infinity"))
- *res = RLIM_INFINITY;
- else {
- uint64_t u;
-
- r = parse_size(val, 1024, &u);
- if (r >= 0 && u >= (uint64_t) RLIM_INFINITY)
- r = -ERANGE;
- if (r == 0)
- *res = (rlim_t) u;
- }
- return r;
-}
-
-static int rlim_parse_sec(const char *val, rlim_t *res) {
- int r = 0;
-
- if (streq(val, "infinity"))
- *res = RLIM_INFINITY;
- else {
- usec_t t;
-
- r = parse_sec(val, &t);
- if (r < 0)
- return r;
- if (t == USEC_INFINITY)
- *res = RLIM_INFINITY;
- else
- *res = (rlim_t) (DIV_ROUND_UP(t, USEC_PER_SEC));
-
- }
- return r;
-}
-
-static int rlim_parse_usec(const char *val, rlim_t *res) {
- int r = 0;
-
- if (streq(val, "infinity"))
- *res = RLIM_INFINITY;
- else {
- usec_t t;
-
- r = parse_time(val, &t, 1);
- if (r < 0)
- return r;
- if (t == USEC_INFINITY)
- *res = RLIM_INFINITY;
- else
- *res = (rlim_t) t;
- }
- return r;
-}
-
-static int parse_rlimit_range(
- const char *unit,
- const char *filename,
- unsigned line,
- const char *value,
- struct rlimit **rl,
- int (*rlim_parser)(const char *, rlim_t *)) {
-
- const char *whole_value = value;
- rlim_t soft, hard;
- _cleanup_free_ char *sword = NULL, *hword = NULL;
- int nwords, r;
-
- assert(value);
-
- /* <value> or <soft:hard> */
- nwords = extract_many_words(&value, ":", EXTRACT_DONT_COALESCE_SEPARATORS, &sword, &hword, NULL);
- r = nwords < 0 ? nwords : nwords == 0 ? -EINVAL : 0;
-
- if (r == 0)
- r = rlim_parser(sword, &soft);
- if (r == 0 && nwords == 2)
- r = rlim_parser(hword, &hard);
- if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse resource value, ignoring: %s", whole_value);
- return 0;
- }
- if (nwords == 2 && soft > hard)
- return log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid resource value ("RLIM_FMT" > "RLIM_FMT"), ignoring: %s", soft, hard, whole_value);
-
- if (!*rl) {
- *rl = new(struct rlimit, 1);
- if (!*rl)
- return log_oom();
- }
- (*rl)->rlim_cur = soft;
- (*rl)->rlim_max = nwords == 2 ? hard : soft;
- return 0;
-}
-
int config_parse_limit(
const char *unit,
const char *filename,
@@ -1229,88 +1114,35 @@ int config_parse_limit(
void *data,
void *userdata) {
- struct rlimit **rl = data;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- rl += ltype;
- return parse_rlimit_range(unit, filename, line, rvalue, rl, rlim_parse_u64);
-}
-
-int config_parse_bytes_limit(
- 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) {
-
- struct rlimit **rl = data;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- rl += ltype;
- return parse_rlimit_range(unit, filename, line, rvalue, rl, rlim_parse_size);
-}
-
-int config_parse_sec_limit(
- 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) {
-
- struct rlimit **rl = data;
+ struct rlimit **rl = data, d = {};
+ int r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
- rl += ltype;
- return parse_rlimit_range(unit, filename, line, rvalue, rl, rlim_parse_sec);
-}
-
-int config_parse_usec_limit(
- 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) {
-
- struct rlimit **rl = data;
+ r = rlimit_parse(ltype, rvalue, &d);
+ if (r == -EILSEQ) {
+ log_syntax(unit, LOG_WARNING, filename, line, r, "Soft resource limit chosen higher than hard limit, ignoring: %s", rvalue);
+ return 0;
+ }
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse resource value, ignoring: %s", rvalue);
+ return 0;
+ }
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
+ if (rl[ltype])
+ *rl[ltype] = d;
+ else {
+ rl[ltype] = newdup(struct rlimit, &d, 1);
+ if (!rl[ltype])
+ return log_oom();
+ }
- rl += ltype;
- return parse_rlimit_range(unit, filename, line, rvalue, rl, rlim_parse_usec);
+ return 0;
}
-
-
#ifdef HAVE_SYSV_COMPAT
int config_parse_sysv_priority(const char *unit,
const char *filename,
@@ -1911,6 +1743,15 @@ int config_parse_service_timeout(const char *unit,
} else if (streq(lvalue, "TimeoutStartSec"))
s->start_timeout_defined = true;
+ /* Traditionally, these options accepted 0 to disable the timeouts. However, a timeout of 0 suggests it happens
+ * immediately, hence fix this to become USEC_INFINITY instead. This is in-line with how we internally handle
+ * all other timeouts. */
+
+ if (s->timeout_start_usec <= 0)
+ s->timeout_start_usec = USEC_INFINITY;
+ if (s->timeout_stop_usec <= 0)
+ s->timeout_stop_usec = USEC_INFINITY;
+
return 0;
}
diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h
index f0027a6b43..20dd84ba95 100644
--- a/src/core/load-fragment.h
+++ b/src/core/load-fragment.h
@@ -58,9 +58,6 @@ int config_parse_exec_capabilities(const char *unit, const char *filename, unsig
int config_parse_exec_secure_bits(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_capability_set(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_limit(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_bytes_limit(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_sec_limit(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_usec_limit(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_sysv_priority(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_kill_signal(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_mount_flags(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/main.c b/src/core/main.c
index 27ba6af031..99ef723fcb 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -676,22 +676,22 @@ static int parse_config_file(void) {
{ "Manager", "DefaultStartLimitInterval", config_parse_sec, 0, &arg_default_start_limit_interval },
{ "Manager", "DefaultStartLimitBurst", config_parse_unsigned, 0, &arg_default_start_limit_burst },
{ "Manager", "DefaultEnvironment", config_parse_environ, 0, &arg_default_environment },
- { "Manager", "DefaultLimitCPU", config_parse_sec_limit, 0, &arg_default_rlimit[RLIMIT_CPU] },
- { "Manager", "DefaultLimitFSIZE", config_parse_bytes_limit, 0, &arg_default_rlimit[RLIMIT_FSIZE] },
- { "Manager", "DefaultLimitDATA", config_parse_bytes_limit, 0, &arg_default_rlimit[RLIMIT_DATA] },
- { "Manager", "DefaultLimitSTACK", config_parse_bytes_limit, 0, &arg_default_rlimit[RLIMIT_STACK] },
- { "Manager", "DefaultLimitCORE", config_parse_bytes_limit, 0, &arg_default_rlimit[RLIMIT_CORE] },
- { "Manager", "DefaultLimitRSS", config_parse_bytes_limit, 0, &arg_default_rlimit[RLIMIT_RSS] },
- { "Manager", "DefaultLimitNOFILE", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_NOFILE] },
- { "Manager", "DefaultLimitAS", config_parse_bytes_limit, 0, &arg_default_rlimit[RLIMIT_AS] },
- { "Manager", "DefaultLimitNPROC", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_NPROC] },
- { "Manager", "DefaultLimitMEMLOCK", config_parse_bytes_limit, 0, &arg_default_rlimit[RLIMIT_MEMLOCK] },
- { "Manager", "DefaultLimitLOCKS", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_LOCKS] },
- { "Manager", "DefaultLimitSIGPENDING", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_SIGPENDING] },
- { "Manager", "DefaultLimitMSGQUEUE", config_parse_bytes_limit, 0, &arg_default_rlimit[RLIMIT_MSGQUEUE] },
- { "Manager", "DefaultLimitNICE", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_NICE] },
- { "Manager", "DefaultLimitRTPRIO", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_RTPRIO] },
- { "Manager", "DefaultLimitRTTIME", config_parse_usec_limit, 0, &arg_default_rlimit[RLIMIT_RTTIME] },
+ { "Manager", "DefaultLimitCPU", config_parse_limit, RLIMIT_CPU, arg_default_rlimit },
+ { "Manager", "DefaultLimitFSIZE", config_parse_limit, RLIMIT_FSIZE, arg_default_rlimit },
+ { "Manager", "DefaultLimitDATA", config_parse_limit, RLIMIT_DATA, arg_default_rlimit },
+ { "Manager", "DefaultLimitSTACK", config_parse_limit, RLIMIT_STACK, arg_default_rlimit },
+ { "Manager", "DefaultLimitCORE", config_parse_limit, RLIMIT_CORE, arg_default_rlimit },
+ { "Manager", "DefaultLimitRSS", config_parse_limit, RLIMIT_RSS, arg_default_rlimit },
+ { "Manager", "DefaultLimitNOFILE", config_parse_limit, RLIMIT_NOFILE, arg_default_rlimit },
+ { "Manager", "DefaultLimitAS", config_parse_limit, RLIMIT_AS, arg_default_rlimit },
+ { "Manager", "DefaultLimitNPROC", config_parse_limit, RLIMIT_NPROC, arg_default_rlimit },
+ { "Manager", "DefaultLimitMEMLOCK", config_parse_limit, RLIMIT_MEMLOCK, arg_default_rlimit },
+ { "Manager", "DefaultLimitLOCKS", config_parse_limit, RLIMIT_LOCKS, arg_default_rlimit },
+ { "Manager", "DefaultLimitSIGPENDING", config_parse_limit, RLIMIT_SIGPENDING, arg_default_rlimit },
+ { "Manager", "DefaultLimitMSGQUEUE", config_parse_limit, RLIMIT_MSGQUEUE, arg_default_rlimit },
+ { "Manager", "DefaultLimitNICE", config_parse_limit, RLIMIT_NICE, arg_default_rlimit },
+ { "Manager", "DefaultLimitRTPRIO", config_parse_limit, RLIMIT_RTPRIO, arg_default_rlimit },
+ { "Manager", "DefaultLimitRTTIME", config_parse_limit, RLIMIT_RTTIME, arg_default_rlimit },
{ "Manager", "DefaultCPUAccounting", config_parse_bool, 0, &arg_default_cpu_accounting },
{ "Manager", "DefaultBlockIOAccounting", config_parse_bool, 0, &arg_default_blockio_accounting },
{ "Manager", "DefaultMemoryAccounting", config_parse_bool, 0, &arg_default_memory_accounting },
@@ -710,8 +710,14 @@ static int parse_config_file(void) {
CONF_PATHS_NULSTR("systemd/system.conf.d") :
CONF_PATHS_NULSTR("systemd/user.conf.d");
- config_parse_many(fn, conf_dirs_nulstr, "Manager\0",
- config_item_table_lookup, items, false, NULL);
+ config_parse_many(fn, conf_dirs_nulstr, "Manager\0", config_item_table_lookup, items, false, NULL);
+
+ /* Traditionally "0" was used to turn off the default unit timeouts. Fix this up so that we used USEC_INFINITY
+ * like everywhere else. */
+ if (arg_default_timeout_start_usec <= 0)
+ arg_default_timeout_start_usec = USEC_INFINITY;
+ if (arg_default_timeout_stop_usec <= 0)
+ arg_default_timeout_stop_usec = USEC_INFINITY;
return 0;
}
@@ -1363,7 +1369,11 @@ int main(int argc, char *argv[]) {
initrd_timestamp = userspace_timestamp;
if (!skip_setup) {
- mount_setup_early();
+ r = mount_setup_early();
+ if (r < 0) {
+ error_message = "Failed to early mount API filesystems";
+ goto finish;
+ }
dual_timestamp_get(&security_start_timestamp);
if (mac_selinux_setup(&loaded_policy) < 0) {
error_message = "Failed to load SELinux policy";
diff --git a/src/core/manager.c b/src/core/manager.c
index a83a8b013a..e8fea376ff 100644
--- a/src/core/manager.c
+++ b/src/core/manager.c
@@ -3097,18 +3097,18 @@ ManagerState manager_state(Manager *m) {
/* Is the special shutdown target queued? If so, we are in shutdown state */
u = manager_get_unit(m, SPECIAL_SHUTDOWN_TARGET);
- if (u && u->job && IN_SET(u->job->type, JOB_START, JOB_RESTART, JOB_TRY_RESTART, JOB_RELOAD_OR_START))
+ if (u && u->job && IN_SET(u->job->type, JOB_START, JOB_RESTART, JOB_RELOAD_OR_START))
return MANAGER_STOPPING;
/* Are the rescue or emergency targets active or queued? If so we are in maintenance state */
u = manager_get_unit(m, SPECIAL_RESCUE_TARGET);
if (u && (UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(u)) ||
- (u->job && IN_SET(u->job->type, JOB_START, JOB_RESTART, JOB_TRY_RESTART, JOB_RELOAD_OR_START))))
+ (u->job && IN_SET(u->job->type, JOB_START, JOB_RESTART, JOB_RELOAD_OR_START))))
return MANAGER_MAINTENANCE;
u = manager_get_unit(m, SPECIAL_EMERGENCY_TARGET);
if (u && (UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(u)) ||
- (u->job && IN_SET(u->job->type, JOB_START, JOB_RESTART, JOB_TRY_RESTART, JOB_RELOAD_OR_START))))
+ (u->job && IN_SET(u->job->type, JOB_START, JOB_RESTART, JOB_RELOAD_OR_START))))
return MANAGER_MAINTENANCE;
/* Are there any failed units? If so, we are in degraded mode */
diff --git a/src/core/mount-setup.c b/src/core/mount-setup.c
index d73b319c5d..7a06c24016 100644
--- a/src/core/mount-setup.c
+++ b/src/core/mount-setup.c
@@ -199,18 +199,14 @@ static int mount_one(const MountPoint *p, bool relabel) {
return 1;
}
-int mount_setup_early(void) {
+static int mount_points_setup(unsigned n, bool loaded_policy) {
unsigned i;
int r = 0;
- assert_cc(N_EARLY_MOUNT <= ELEMENTSOF(mount_table));
-
- /* Do a minimal mount of /proc and friends to enable the most
- * basic stuff, such as SELinux */
- for (i = 0; i < N_EARLY_MOUNT; i ++) {
+ for (i = 0; i < n; i ++) {
int j;
- j = mount_one(mount_table + i, false);
+ j = mount_one(mount_table + i, loaded_policy);
if (j != 0 && r >= 0)
r = j;
}
@@ -218,6 +214,14 @@ int mount_setup_early(void) {
return r;
}
+int mount_setup_early(void) {
+ assert_cc(N_EARLY_MOUNT <= ELEMENTSOF(mount_table));
+
+ /* Do a minimal mount of /proc and friends to enable the most
+ * basic stuff, such as SELinux */
+ return mount_points_setup(N_EARLY_MOUNT, false);
+}
+
int mount_cgroup_controllers(char ***join_controllers) {
_cleanup_set_free_free_ Set *controllers = NULL;
int r;
@@ -352,16 +356,9 @@ static int nftw_cb(
#endif
int mount_setup(bool loaded_policy) {
- unsigned i;
int r = 0;
- for (i = 0; i < ELEMENTSOF(mount_table); i ++) {
- int j;
-
- j = mount_one(mount_table + i, loaded_policy);
- if (j != 0 && r >= 0)
- r = j;
- }
+ r = mount_points_setup(ELEMENTSOF(mount_table), loaded_policy);
if (r < 0)
return r;
diff --git a/src/core/mount.c b/src/core/mount.c
index 2ad4ad4f42..7e3a6d578f 100644
--- a/src/core/mount.c
+++ b/src/core/mount.c
@@ -152,29 +152,27 @@ static void mount_init(Unit *u) {
u->ignore_on_isolate = true;
}
-static int mount_arm_timer(Mount *m) {
+static int mount_arm_timer(Mount *m, usec_t usec) {
int r;
assert(m);
- if (m->timeout_usec <= 0) {
- m->timer_event_source = sd_event_source_unref(m->timer_event_source);
- return 0;
- }
-
if (m->timer_event_source) {
- r = sd_event_source_set_time(m->timer_event_source, now(CLOCK_MONOTONIC) + m->timeout_usec);
+ r = sd_event_source_set_time(m->timer_event_source, usec);
if (r < 0)
return r;
return sd_event_source_set_enabled(m->timer_event_source, SD_EVENT_ONESHOT);
}
+ if (usec == USEC_INFINITY)
+ return 0;
+
r = sd_event_add_time(
UNIT(m)->manager->event,
&m->timer_event_source,
CLOCK_MONOTONIC,
- now(CLOCK_MONOTONIC) + m->timeout_usec, 0,
+ usec, 0,
mount_dispatch_timer, m);
if (r < 0)
return r;
@@ -653,7 +651,7 @@ static int mount_coldplug(Unit *u) {
if (r < 0)
return r;
- r = mount_arm_timer(m);
+ r = mount_arm_timer(m, usec_add(u->state_change_timestamp.monotonic, m->timeout_usec));
if (r < 0)
return r;
}
@@ -725,11 +723,11 @@ static int mount_spawn(Mount *m, ExecCommand *c, pid_t *_pid) {
r = unit_setup_exec_runtime(UNIT(m));
if (r < 0)
- goto fail;
+ return r;
- r = mount_arm_timer(m);
+ r = mount_arm_timer(m, usec_add(now(CLOCK_MONOTONIC), m->timeout_usec));
if (r < 0)
- goto fail;
+ return r;
exec_params.environment = UNIT(m)->manager->environment;
exec_params.confirm_spawn = UNIT(m)->manager->confirm_spawn;
@@ -745,21 +743,16 @@ static int mount_spawn(Mount *m, ExecCommand *c, pid_t *_pid) {
m->exec_runtime,
&pid);
if (r < 0)
- goto fail;
+ return r;
r = unit_watch_pid(UNIT(m), pid);
if (r < 0)
/* FIXME: we need to do something here */
- goto fail;
+ return r;
*_pid = pid;
return 0;
-
-fail:
- m->timer_event_source = sd_event_source_unref(m->timer_event_source);
-
- return r;
}
static void mount_enter_dead(Mount *m, MountResult f) {
@@ -805,7 +798,7 @@ static void mount_enter_signal(Mount *m, MountState state, MountResult f) {
goto fail;
if (r > 0) {
- r = mount_arm_timer(m);
+ r = mount_arm_timer(m, usec_add(now(CLOCK_MONOTONIC), m->timeout_usec));
if (r < 0)
goto fail;
diff --git a/src/core/scope.c b/src/core/scope.c
index 1953af1f88..7cddee23b8 100644
--- a/src/core/scope.c
+++ b/src/core/scope.c
@@ -66,29 +66,27 @@ static void scope_done(Unit *u) {
s->timer_event_source = sd_event_source_unref(s->timer_event_source);
}
-static int scope_arm_timer(Scope *s) {
+static int scope_arm_timer(Scope *s, usec_t usec) {
int r;
assert(s);
- if (s->timeout_stop_usec <= 0) {
- s->timer_event_source = sd_event_source_unref(s->timer_event_source);
- return 0;
- }
-
if (s->timer_event_source) {
- r = sd_event_source_set_time(s->timer_event_source, now(CLOCK_MONOTONIC) + s->timeout_stop_usec);
+ r = sd_event_source_set_time(s->timer_event_source, usec);
if (r < 0)
return r;
return sd_event_source_set_enabled(s->timer_event_source, SD_EVENT_ONESHOT);
}
+ if (usec == USEC_INFINITY)
+ return 0;
+
r = sd_event_add_time(
UNIT(s)->manager->event,
&s->timer_event_source,
CLOCK_MONOTONIC,
- now(CLOCK_MONOTONIC) + s->timeout_stop_usec, 0,
+ usec, 0,
scope_dispatch_timer, s);
if (r < 0)
return r;
@@ -190,20 +188,19 @@ static int scope_coldplug(Unit *u) {
assert(s);
assert(s->state == SCOPE_DEAD);
- if (s->deserialized_state != s->state) {
-
- if (IN_SET(s->deserialized_state, SCOPE_STOP_SIGKILL, SCOPE_STOP_SIGTERM)) {
- r = scope_arm_timer(s);
- if (r < 0)
- return r;
- }
-
- if (!IN_SET(s->deserialized_state, SCOPE_DEAD, SCOPE_FAILED))
- unit_watch_all_pids(UNIT(s));
+ if (s->deserialized_state == s->state)
+ return 0;
- scope_set_state(s, s->deserialized_state);
+ if (IN_SET(s->deserialized_state, SCOPE_STOP_SIGKILL, SCOPE_STOP_SIGTERM)) {
+ r = scope_arm_timer(s, usec_add(u->state_change_timestamp.monotonic, s->timeout_stop_usec));
+ if (r < 0)
+ return r;
}
+ if (!IN_SET(s->deserialized_state, SCOPE_DEAD, SCOPE_FAILED))
+ unit_watch_all_pids(UNIT(s));
+
+ scope_set_state(s, s->deserialized_state);
return 0;
}
@@ -261,7 +258,7 @@ static void scope_enter_signal(Scope *s, ScopeState state, ScopeResult f) {
r = 1;
if (r > 0) {
- r = scope_arm_timer(s);
+ r = scope_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->timeout_stop_usec));
if (r < 0)
goto fail;
diff --git a/src/core/service.c b/src/core/service.c
index ae84cccbc8..a9345e38b9 100644
--- a/src/core/service.c
+++ b/src/core/service.c
@@ -112,6 +112,7 @@ static void service_init(Unit *u) {
s->timeout_start_usec = u->manager->default_timeout_start_usec;
s->timeout_stop_usec = u->manager->default_timeout_stop_usec;
s->restart_usec = u->manager->default_restart_usec;
+ s->runtime_max_usec = USEC_INFINITY;
s->type = _SERVICE_TYPE_INVALID;
s->socket_fd = -1;
s->bus_endpoint_fd = -1;
@@ -216,7 +217,7 @@ static void service_start_watchdog(Service *s) {
return;
if (s->watchdog_event_source) {
- r = sd_event_source_set_time(s->watchdog_event_source, s->watchdog_timestamp.monotonic + s->watchdog_usec);
+ r = sd_event_source_set_time(s->watchdog_event_source, usec_add(s->watchdog_timestamp.monotonic, s->watchdog_usec));
if (r < 0) {
log_unit_warning_errno(UNIT(s), r, "Failed to reset watchdog timer: %m");
return;
@@ -228,7 +229,7 @@ static void service_start_watchdog(Service *s) {
UNIT(s)->manager->event,
&s->watchdog_event_source,
CLOCK_MONOTONIC,
- s->watchdog_timestamp.monotonic + s->watchdog_usec, 0,
+ usec_add(s->watchdog_timestamp.monotonic, s->watchdog_usec), 0,
service_dispatch_watchdog, s);
if (r < 0) {
log_unit_warning_errno(UNIT(s), r, "Failed to add watchdog timer: %m");
@@ -433,18 +434,21 @@ static int service_arm_timer(Service *s, usec_t usec) {
assert(s);
if (s->timer_event_source) {
- r = sd_event_source_set_time(s->timer_event_source, now(CLOCK_MONOTONIC) + usec);
+ r = sd_event_source_set_time(s->timer_event_source, usec);
if (r < 0)
return r;
return sd_event_source_set_enabled(s->timer_event_source, SD_EVENT_ONESHOT);
}
+ if (usec == USEC_INFINITY)
+ return 0;
+
r = sd_event_add_time(
UNIT(s)->manager->event,
&s->timer_event_source,
CLOCK_MONOTONIC,
- now(CLOCK_MONOTONIC) + usec, 0,
+ usec, 0,
service_dispatch_timer, s);
if (r < 0)
return r;
@@ -509,6 +513,9 @@ static int service_verify(Service *s) {
if (!s->usb_function_descriptors && s->usb_function_strings)
log_unit_warning(UNIT(s), "Service has USBFunctionStrings= setting, but no USBFunctionDescriptors=. Ignoring.");
+ if (s->runtime_max_usec != USEC_INFINITY && s->type == SERVICE_ONESHOT)
+ log_unit_warning(UNIT(s), "MaxRuntimeSec= has no effect in combination with Type=oneshot. Ignoring.");
+
return 0;
}
@@ -624,7 +631,7 @@ static int service_add_extras(Service *s) {
/* Oneshot services have disabled start timeout by default */
if (s->type == SERVICE_ONESHOT && !s->start_timeout_defined)
- s->timeout_start_usec = 0;
+ s->timeout_start_usec = USEC_INFINITY;
service_fix_output(s);
@@ -882,6 +889,7 @@ static void service_set_state(Service *s, ServiceState state) {
if (!IN_SET(state,
SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST,
+ SERVICE_RUNNING,
SERVICE_RELOAD,
SERVICE_STOP, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL,
@@ -954,6 +962,37 @@ static void service_set_state(Service *s, ServiceState state) {
unit_notify(UNIT(s), table[old_state], table[state], s->reload_result == SERVICE_SUCCESS);
}
+static usec_t service_coldplug_timeout(Service *s) {
+ assert(s);
+
+ switch (s->deserialized_state) {
+
+ case SERVICE_START_PRE:
+ case SERVICE_START:
+ case SERVICE_START_POST:
+ case SERVICE_RELOAD:
+ return usec_add(UNIT(s)->state_change_timestamp.monotonic, s->timeout_start_usec);
+
+ case SERVICE_RUNNING:
+ return usec_add(UNIT(s)->active_enter_timestamp.monotonic, s->runtime_max_usec);
+
+ case SERVICE_STOP:
+ case SERVICE_STOP_SIGABRT:
+ case SERVICE_STOP_SIGTERM:
+ case SERVICE_STOP_SIGKILL:
+ case SERVICE_STOP_POST:
+ case SERVICE_FINAL_SIGTERM:
+ case SERVICE_FINAL_SIGKILL:
+ return usec_add(UNIT(s)->state_change_timestamp.monotonic, s->timeout_stop_usec);
+
+ case SERVICE_AUTO_RESTART:
+ return usec_add(UNIT(s)->inactive_enter_timestamp.monotonic, s->restart_usec);
+
+ default:
+ return USEC_INFINITY;
+ }
+}
+
static int service_coldplug(Unit *u) {
Service *s = SERVICE(u);
int r;
@@ -964,31 +1003,9 @@ static int service_coldplug(Unit *u) {
if (s->deserialized_state == s->state)
return 0;
- if (IN_SET(s->deserialized_state,
- SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST,
- SERVICE_RELOAD,
- SERVICE_STOP, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
- SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL)) {
-
- usec_t k;
-
- k = IN_SET(s->deserialized_state, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST, SERVICE_RELOAD) ? s->timeout_start_usec : s->timeout_stop_usec;
-
- /* For the start/stop timeouts 0 means off */
- if (k > 0) {
- r = service_arm_timer(s, k);
- if (r < 0)
- return r;
- }
- }
-
- if (s->deserialized_state == SERVICE_AUTO_RESTART) {
-
- /* The restart timeouts 0 means immediately */
- r = service_arm_timer(s, s->restart_usec);
- if (r < 0)
- return r;
- }
+ r = service_arm_timer(s, service_coldplug_timeout(s));
+ if (r < 0)
+ return r;
if (s->main_pid > 0 &&
pid_is_unwaited(s->main_pid) &&
@@ -1175,7 +1192,7 @@ static int service_spawn(
r = unit_setup_exec_runtime(UNIT(s));
if (r < 0)
- goto fail;
+ return r;
if (pass_fds ||
s->exec_context.std_input == EXEC_INPUT_SOCKET ||
@@ -1184,55 +1201,42 @@ static int service_spawn(
r = service_collect_fds(s, &fds, &fd_names);
if (r < 0)
- goto fail;
+ return r;
n_fds = r;
}
- if (timeout > 0) {
- r = service_arm_timer(s, timeout);
- if (r < 0)
- goto fail;
- } else
- s->timer_event_source = sd_event_source_unref(s->timer_event_source);
+ r = service_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), timeout));
+ if (r < 0)
+ return r;
r = unit_full_printf_strv(UNIT(s), c->argv, &argv);
if (r < 0)
- goto fail;
+ return r;
our_env = new0(char*, 6);
- if (!our_env) {
- r = -ENOMEM;
- goto fail;
- }
+ if (!our_env)
+ return -ENOMEM;
if (is_control ? s->notify_access == NOTIFY_ALL : s->notify_access != NOTIFY_NONE)
- if (asprintf(our_env + n_env++, "NOTIFY_SOCKET=%s", UNIT(s)->manager->notify_socket) < 0) {
- r = -ENOMEM;
- goto fail;
- }
+ if (asprintf(our_env + n_env++, "NOTIFY_SOCKET=%s", UNIT(s)->manager->notify_socket) < 0)
+ return -ENOMEM;
if (s->main_pid > 0)
- if (asprintf(our_env + n_env++, "MAINPID="PID_FMT, s->main_pid) < 0) {
- r = -ENOMEM;
- goto fail;
- }
+ if (asprintf(our_env + n_env++, "MAINPID="PID_FMT, s->main_pid) < 0)
+ return -ENOMEM;
if (UNIT(s)->manager->running_as != MANAGER_SYSTEM)
- if (asprintf(our_env + n_env++, "MANAGERPID="PID_FMT, getpid()) < 0) {
- r = -ENOMEM;
- goto fail;
- }
+ if (asprintf(our_env + n_env++, "MANAGERPID="PID_FMT, getpid()) < 0)
+ return -ENOMEM;
if (s->socket_fd >= 0) {
union sockaddr_union sa;
socklen_t salen = sizeof(sa);
r = getpeername(s->socket_fd, &sa.sa, &salen);
- if (r < 0) {
- r = -errno;
- goto fail;
- }
+ if (r < 0)
+ return -errno;
if (IN_SET(sa.sa.sa_family, AF_INET, AF_INET6)) {
_cleanup_free_ char *addr = NULL;
@@ -1241,34 +1245,26 @@ static int service_spawn(
r = sockaddr_pretty(&sa.sa, salen, true, false, &addr);
if (r < 0)
- goto fail;
+ return r;
t = strappend("REMOTE_ADDR=", addr);
- if (!t) {
- r = -ENOMEM;
- goto fail;
- }
+ if (!t)
+ return -ENOMEM;
our_env[n_env++] = t;
port = sockaddr_port(&sa.sa);
- if (port < 0) {
- r = port;
- goto fail;
- }
+ if (port < 0)
+ return port;
- if (asprintf(&t, "REMOTE_PORT=%u", port) < 0) {
- r = -ENOMEM;
- goto fail;
- }
+ if (asprintf(&t, "REMOTE_PORT=%u", port) < 0)
+ return -ENOMEM;
our_env[n_env++] = t;
}
}
final_env = strv_env_merge(2, UNIT(s)->manager->environment, our_env, NULL);
- if (!final_env) {
- r = -ENOMEM;
- goto fail;
- }
+ if (!final_env)
+ return -ENOMEM;
if (is_control && UNIT(s)->cgroup_path) {
path = strjoina(UNIT(s)->cgroup_path, "/control");
@@ -1280,7 +1276,7 @@ static int service_spawn(
r = bus_kernel_create_endpoint(UNIT(s)->manager->running_as == MANAGER_SYSTEM ? "system" : "user",
UNIT(s)->id, &bus_endpoint_path);
if (r < 0)
- goto fail;
+ return r;
/* Pass the fd to the exec_params so that the child process can upload the policy.
* Keep a reference to the fd in the service, so the endpoint is kept alive as long
@@ -1314,22 +1310,16 @@ static int service_spawn(
s->exec_runtime,
&pid);
if (r < 0)
- goto fail;
+ return r;
r = unit_watch_pid(UNIT(s), pid);
if (r < 0)
/* FIXME: we need to do something here */
- goto fail;
+ return r;
*_pid = pid;
return 0;
-
-fail:
- if (timeout)
- s->timer_event_source = sd_event_source_unref(s->timer_event_source);
-
- return r;
}
static int main_pid_good(Service *s) {
@@ -1437,7 +1427,7 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart)
if (allow_restart && service_shall_restart(s)) {
- r = service_arm_timer(s, s->restart_usec);
+ r = service_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->restart_usec));
if (r < 0)
goto fail;
@@ -1458,7 +1448,7 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart)
* out-of-date, and some software might be confused by it, so
* let's remove it. */
if (s->pid_file)
- unlink_noerrno(s->pid_file);
+ (void) unlink(s->pid_file);
return;
@@ -1545,11 +1535,9 @@ static void service_enter_signal(Service *s, ServiceState state, ServiceResult f
goto fail;
if (r > 0) {
- if (s->timeout_stop_usec > 0) {
- r = service_arm_timer(s, s->timeout_stop_usec);
- if (r < 0)
- goto fail;
- }
+ r = service_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->timeout_stop_usec));
+ if (r < 0)
+ goto fail;
service_set_state(s, state);
} else if (IN_SET(state, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM) && s->kill_context.send_sigkill)
@@ -1577,8 +1565,7 @@ static void service_enter_stop_by_notify(Service *s) {
unit_watch_all_pids(UNIT(s));
- if (s->timeout_stop_usec > 0)
- service_arm_timer(s, s->timeout_stop_usec);
+ service_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->timeout_stop_usec));
/* The service told us it's stopping, so it's as if we SIGTERM'd it. */
service_set_state(s, SERVICE_STOP_SIGTERM);
@@ -1656,8 +1643,10 @@ static void service_enter_running(Service *s, ServiceResult f) {
service_enter_reload_by_notify(s);
else if (s->notify_state == NOTIFY_STOPPING)
service_enter_stop_by_notify(s);
- else
+ else {
service_set_state(s, SERVICE_RUNNING);
+ service_arm_timer(s, usec_add(UNIT(s)->active_enter_timestamp.monotonic, s->runtime_max_usec));
+ }
} else if (s->remain_after_exit)
service_set_state(s, SERVICE_EXITED);
@@ -1711,6 +1700,7 @@ static void service_kill_control_processes(Service *s) {
static void service_enter_start(Service *s) {
ExecCommand *c;
+ usec_t timeout;
pid_t pid;
int r;
@@ -1742,9 +1732,16 @@ static void service_enter_start(Service *s) {
return;
}
+ if (IN_SET(s->type, SERVICE_SIMPLE, SERVICE_IDLE))
+ /* For simple + idle this is the main process. We don't apply any timeout here, but
+ * service_enter_running() will later apply the .runtime_max_usec timeout. */
+ timeout = USEC_INFINITY;
+ else
+ timeout = s->timeout_start_usec;
+
r = service_spawn(s,
c,
- IN_SET(s->type, SERVICE_FORKING, SERVICE_DBUS, SERVICE_NOTIFY, SERVICE_ONESHOT) ? s->timeout_start_usec : 0,
+ timeout,
true,
true,
true,
@@ -1754,7 +1751,7 @@ static void service_enter_start(Service *s) {
if (r < 0)
goto fail;
- if (s->type == SERVICE_SIMPLE || s->type == SERVICE_IDLE) {
+ if (IN_SET(s->type, SERVICE_SIMPLE, SERVICE_IDLE)) {
/* For simple services we immediately start
* the START_POST binaries. */
@@ -1769,9 +1766,7 @@ static void service_enter_start(Service *s) {
s->control_pid = pid;
service_set_state(s, SERVICE_START);
- } else if (s->type == SERVICE_ONESHOT ||
- s->type == SERVICE_DBUS ||
- s->type == SERVICE_NOTIFY) {
+ } else if (IN_SET(s->type, SERVICE_ONESHOT, SERVICE_DBUS, SERVICE_NOTIFY)) {
/* For oneshot services we wait until the start
* process exited, too, but it is our main process. */
@@ -1840,7 +1835,7 @@ static void service_enter_restart(Service *s) {
/* Don't restart things if we are going down anyway */
log_unit_info(UNIT(s), "Stop job pending for unit, delaying automatic restart.");
- r = service_arm_timer(s, s->restart_usec);
+ r = service_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->restart_usec));
if (r < 0)
goto fail;
@@ -1870,9 +1865,7 @@ fail:
static void service_enter_reload_by_notify(Service *s) {
assert(s);
- if (s->timeout_start_usec > 0)
- service_arm_timer(s, s->timeout_start_usec);
-
+ service_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->timeout_start_usec));
service_set_state(s, SERVICE_RELOAD);
}
@@ -1913,6 +1906,7 @@ fail:
}
static void service_run_next_control(Service *s) {
+ usec_t timeout;
int r;
assert(s);
@@ -1924,9 +1918,14 @@ static void service_run_next_control(Service *s) {
s->control_command = s->control_command->command_next;
service_unwatch_control_pid(s);
+ if (IN_SET(s->state, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD))
+ timeout = s->timeout_start_usec;
+ else
+ timeout = s->timeout_stop_usec;
+
r = service_spawn(s,
s->control_command,
- IN_SET(s->state, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD) ? s->timeout_start_usec : s->timeout_stop_usec,
+ timeout,
false,
!s->permissions_start_only,
!s->root_directory_start_only,
@@ -2363,6 +2362,7 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
else {
asynchronous_close(s->stdin_fd);
s->stdin_fd = fdset_remove(fds, fd);
+ s->exec_context.stdio_as_fds = true;
}
} else if (streq(key, "stdout-fd")) {
int fd;
@@ -2372,6 +2372,7 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
else {
asynchronous_close(s->stdout_fd);
s->stdout_fd = fdset_remove(fds, fd);
+ s->exec_context.stdio_as_fds = true;
}
} else if (streq(key, "stderr-fd")) {
int fd;
@@ -2381,6 +2382,7 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
else {
asynchronous_close(s->stderr_fd);
s->stderr_fd = fdset_remove(fds, fd);
+ s->exec_context.stdio_as_fds = true;
}
} else
log_unit_debug(u, "Unknown serialization key: %s", key);
@@ -2879,6 +2881,11 @@ static int service_dispatch_timer(sd_event_source *source, usec_t usec, void *us
service_enter_stop(s, SERVICE_FAILURE_TIMEOUT);
break;
+ case SERVICE_RUNNING:
+ log_unit_warning(UNIT(s), "Service reached runtime time limit. Stopping.");
+ service_enter_stop(s, SERVICE_FAILURE_TIMEOUT);
+ break;
+
case SERVICE_RELOAD:
log_unit_warning(UNIT(s), "Reload operation timed out. Stopping.");
service_unwatch_control_pid(s);
diff --git a/src/core/service.h b/src/core/service.h
index 19efbccfc7..24408940d4 100644
--- a/src/core/service.h
+++ b/src/core/service.h
@@ -118,6 +118,7 @@ struct Service {
usec_t restart_usec;
usec_t timeout_start_usec;
usec_t timeout_stop_usec;
+ usec_t runtime_max_usec;
dual_timestamp watchdog_timestamp;
usec_t watchdog_usec;
diff --git a/src/core/socket.c b/src/core/socket.c
index 2e4173aabc..740b748d65 100644
--- a/src/core/socket.c
+++ b/src/core/socket.c
@@ -170,29 +170,27 @@ static void socket_done(Unit *u) {
s->timer_event_source = sd_event_source_unref(s->timer_event_source);
}
-static int socket_arm_timer(Socket *s) {
+static int socket_arm_timer(Socket *s, usec_t usec) {
int r;
assert(s);
- if (s->timeout_usec <= 0) {
- s->timer_event_source = sd_event_source_unref(s->timer_event_source);
- return 0;
- }
-
if (s->timer_event_source) {
- r = sd_event_source_set_time(s->timer_event_source, now(CLOCK_MONOTONIC) + s->timeout_usec);
+ r = sd_event_source_set_time(s->timer_event_source, usec);
if (r < 0)
return r;
return sd_event_source_set_enabled(s->timer_event_source, SD_EVENT_ONESHOT);
}
+ if (usec == USEC_INFINITY)
+ return 0;
+
r = sd_event_add_time(
UNIT(s)->manager->event,
&s->timer_event_source,
CLOCK_MONOTONIC,
- now(CLOCK_MONOTONIC) + s->timeout_usec, 0,
+ usec, 0,
socket_dispatch_timer, s);
if (r < 0)
return r;
@@ -1494,7 +1492,7 @@ static int socket_coldplug(Unit *u) {
if (r < 0)
return r;
- r = socket_arm_timer(s);
+ r = socket_arm_timer(s, usec_add(u->state_change_timestamp.monotonic, s->timeout_usec));
if (r < 0)
return r;
}
@@ -1507,6 +1505,7 @@ static int socket_coldplug(Unit *u) {
SOCKET_STOP_PRE,
SOCKET_STOP_PRE_SIGTERM,
SOCKET_STOP_PRE_SIGKILL)) {
+
r = socket_open_fds(s);
if (r < 0)
return r;
@@ -1548,15 +1547,15 @@ static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) {
r = unit_setup_exec_runtime(UNIT(s));
if (r < 0)
- goto fail;
+ return r;
- r = socket_arm_timer(s);
+ r = socket_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->timeout_usec));
if (r < 0)
- goto fail;
+ return r;
r = unit_full_printf_strv(UNIT(s), c->argv, &argv);
if (r < 0)
- goto fail;
+ return r;
exec_params.argv = argv;
exec_params.environment = UNIT(s)->manager->environment;
@@ -1573,26 +1572,22 @@ static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) {
s->exec_runtime,
&pid);
if (r < 0)
- goto fail;
+ return r;
r = unit_watch_pid(UNIT(s), pid);
if (r < 0)
/* FIXME: we need to do something here */
- goto fail;
+ return r;
*_pid = pid;
return 0;
-
-fail:
- s->timer_event_source = sd_event_source_unref(s->timer_event_source);
- return r;
}
static int socket_chown(Socket *s, pid_t *_pid) {
pid_t pid;
int r;
- r = socket_arm_timer(s);
+ r = socket_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->timeout_usec));
if (r < 0)
goto fail;
@@ -1735,7 +1730,7 @@ static void socket_enter_signal(Socket *s, SocketState state, SocketResult f) {
goto fail;
if (r > 0) {
- r = socket_arm_timer(s);
+ r = socket_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->timeout_usec));
if (r < 0)
goto fail;
diff --git a/src/core/swap.c b/src/core/swap.c
index 5568898bd7..d895e3ced1 100644
--- a/src/core/swap.c
+++ b/src/core/swap.c
@@ -160,29 +160,27 @@ static void swap_done(Unit *u) {
s->timer_event_source = sd_event_source_unref(s->timer_event_source);
}
-static int swap_arm_timer(Swap *s) {
+static int swap_arm_timer(Swap *s, usec_t usec) {
int r;
assert(s);
- if (s->timeout_usec <= 0) {
- s->timer_event_source = sd_event_source_unref(s->timer_event_source);
- return 0;
- }
-
if (s->timer_event_source) {
- r = sd_event_source_set_time(s->timer_event_source, now(CLOCK_MONOTONIC) + s->timeout_usec);
+ r = sd_event_source_set_time(s->timer_event_source, usec);
if (r < 0)
return r;
return sd_event_source_set_enabled(s->timer_event_source, SD_EVENT_ONESHOT);
}
+ if (usec == USEC_INFINITY)
+ return 0;
+
r = sd_event_add_time(
UNIT(s)->manager->event,
&s->timer_event_source,
CLOCK_MONOTONIC,
- now(CLOCK_MONOTONIC) + s->timeout_usec, 0,
+ usec, 0,
swap_dispatch_timer, s);
if (r < 0)
return r;
@@ -552,7 +550,7 @@ static int swap_coldplug(Unit *u) {
if (r < 0)
return r;
- r = swap_arm_timer(s);
+ r = swap_arm_timer(s, usec_add(u->state_change_timestamp.monotonic, s->timeout_usec));
if (r < 0)
return r;
}
@@ -633,7 +631,7 @@ static int swap_spawn(Swap *s, ExecCommand *c, pid_t *_pid) {
if (r < 0)
goto fail;
- r = swap_arm_timer(s);
+ r = swap_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->timeout_usec));
if (r < 0)
goto fail;
@@ -710,7 +708,7 @@ static void swap_enter_signal(Swap *s, SwapState state, SwapResult f) {
goto fail;
if (r > 0) {
- r = swap_arm_timer(s);
+ r = swap_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->timeout_usec));
if (r < 0)
goto fail;
diff --git a/src/core/transaction.c b/src/core/transaction.c
index 8b0ed74643..0d53e4bac0 100644
--- a/src/core/transaction.c
+++ b/src/core/transaction.c
@@ -1010,7 +1010,13 @@ int transaction_add_job_and_dependencies(
if (type == JOB_RELOAD) {
SET_FOREACH(dep, ret->unit->dependencies[UNIT_PROPAGATES_RELOAD_TO], i) {
- r = transaction_add_job_and_dependencies(tr, JOB_RELOAD, dep, ret, false, false, false, ignore_order, e);
+ JobType nt;
+
+ nt = job_type_collapse(JOB_TRY_RELOAD, dep);
+ if (nt == JOB_NOP)
+ continue;
+
+ r = transaction_add_job_and_dependencies(tr, nt, dep, ret, false, false, false, ignore_order, e);
if (r < 0) {
log_unit_warning(dep,
"Cannot add dependency reload job, ignoring: %s",
diff --git a/src/core/unit.c b/src/core/unit.c
index 32267d95f5..0c1efc0e16 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -99,6 +99,7 @@ Unit *unit_new(Manager *m, size_t size) {
u->unit_file_preset = -1;
u->on_failure_job_mode = JOB_REPLACE;
u->cgroup_inotify_wd = -1;
+ u->job_timeout = USEC_INFINITY;
RATELIMIT_INIT(u->auto_stop_ratelimit, 10 * USEC_PER_SEC, 16);
@@ -869,6 +870,7 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
Iterator i;
const char *prefix2;
char
+ timestamp0[FORMAT_TIMESTAMP_MAX],
timestamp1[FORMAT_TIMESTAMP_MAX],
timestamp2[FORMAT_TIMESTAMP_MAX],
timestamp3[FORMAT_TIMESTAMP_MAX],
@@ -890,6 +892,7 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
"%s\tInstance: %s\n"
"%s\tUnit Load State: %s\n"
"%s\tUnit Active State: %s\n"
+ "%s\nState Change Timestamp: %s\n"
"%s\tInactive Exit Timestamp: %s\n"
"%s\tActive Enter Timestamp: %s\n"
"%s\tActive Exit Timestamp: %s\n"
@@ -907,6 +910,7 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
prefix, strna(u->instance),
prefix, unit_load_state_to_string(u->load_state),
prefix, unit_active_state_to_string(unit_active_state(u)),
+ prefix, strna(format_timestamp(timestamp0, sizeof(timestamp0), u->state_change_timestamp.realtime)),
prefix, strna(format_timestamp(timestamp1, sizeof(timestamp1), u->inactive_exit_timestamp.realtime)),
prefix, strna(format_timestamp(timestamp2, sizeof(timestamp2), u->active_enter_timestamp.realtime)),
prefix, strna(format_timestamp(timestamp3, sizeof(timestamp3), u->active_exit_timestamp.realtime)),
@@ -947,7 +951,7 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
STRV_FOREACH(j, u->dropin_paths)
fprintf(f, "%s\tDropIn Path: %s\n", prefix, *j);
- if (u->job_timeout > 0)
+ if (u->job_timeout != USEC_INFINITY)
fprintf(f, "%s\tJob Timeout: %s\n", prefix, format_timespan(timespan, sizeof(timespan), u->job_timeout, 0));
if (u->job_timeout_action != FAILURE_ACTION_NONE)
@@ -1821,19 +1825,17 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
/* Update timestamps for state changes */
if (m->n_reloading <= 0) {
- dual_timestamp ts;
-
- dual_timestamp_get(&ts);
+ dual_timestamp_get(&u->state_change_timestamp);
if (UNIT_IS_INACTIVE_OR_FAILED(os) && !UNIT_IS_INACTIVE_OR_FAILED(ns))
- u->inactive_exit_timestamp = ts;
+ u->inactive_exit_timestamp = u->state_change_timestamp;
else if (!UNIT_IS_INACTIVE_OR_FAILED(os) && UNIT_IS_INACTIVE_OR_FAILED(ns))
- u->inactive_enter_timestamp = ts;
+ u->inactive_enter_timestamp = u->state_change_timestamp;
if (!UNIT_IS_ACTIVE_OR_RELOADING(os) && UNIT_IS_ACTIVE_OR_RELOADING(ns))
- u->active_enter_timestamp = ts;
+ u->active_enter_timestamp = u->state_change_timestamp;
else if (UNIT_IS_ACTIVE_OR_RELOADING(os) && !UNIT_IS_ACTIVE_OR_RELOADING(ns))
- u->active_exit_timestamp = ts;
+ u->active_exit_timestamp = u->state_change_timestamp;
}
/* Keep track of failed units */
@@ -1894,6 +1896,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
case JOB_RELOAD:
case JOB_RELOAD_OR_START:
+ case JOB_TRY_RELOAD:
if (u->job->state == JOB_RUNNING) {
if (ns == UNIT_ACTIVE)
@@ -2107,6 +2110,7 @@ bool unit_job_is_applicable(Unit *u, JobType j) {
return unit_can_start(u);
case JOB_RELOAD:
+ case JOB_TRY_RELOAD:
return unit_can_reload(u);
case JOB_RELOAD_OR_START:
@@ -2551,10 +2555,13 @@ int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) {
}
}
+ dual_timestamp_serialize(f, "state-change-timestamp", &u->state_change_timestamp);
+
dual_timestamp_serialize(f, "inactive-exit-timestamp", &u->inactive_exit_timestamp);
dual_timestamp_serialize(f, "active-enter-timestamp", &u->active_enter_timestamp);
dual_timestamp_serialize(f, "active-exit-timestamp", &u->active_exit_timestamp);
dual_timestamp_serialize(f, "inactive-enter-timestamp", &u->inactive_enter_timestamp);
+
dual_timestamp_serialize(f, "condition-timestamp", &u->condition_timestamp);
dual_timestamp_serialize(f, "assert-timestamp", &u->assert_timestamp);
@@ -2693,7 +2700,7 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
/* End marker */
if (isempty(l))
- return 0;
+ break;
k = strcspn(l, "=");
@@ -2733,6 +2740,9 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
} else /* legacy for pre-44 */
log_unit_warning(u, "Update from too old systemd versions are unsupported, cannot deserialize job: %s", v);
continue;
+ } else if (streq(l, "state-change-timestamp")) {
+ dual_timestamp_deserialize(v, &u->state_change_timestamp);
+ continue;
} else if (streq(l, "inactive-exit-timestamp")) {
dual_timestamp_deserialize(v, &u->inactive_exit_timestamp);
continue;
@@ -2839,6 +2849,15 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
log_unit_warning(u, "Failed to deserialize unit parameter '%s', ignoring.", l);
}
}
+
+ /* Versions before 228 did not carry a state change timestamp. In this case, take the current time. This is
+ * useful, so that timeouts based on this timestamp don't trigger too early, and is in-line with the logic from
+ * before 228 where the base for timeouts was not peristet across reboots. */
+
+ if (!dual_timestamp_is_set(&u->state_change_timestamp))
+ dual_timestamp_get(&u->state_change_timestamp);
+
+ return 0;
}
int unit_add_node_link(Unit *u, const char *what, bool wants, UnitDependency dep) {
diff --git a/src/core/unit.h b/src/core/unit.h
index 3eb3484fb7..f86a0f687b 100644
--- a/src/core/unit.h
+++ b/src/core/unit.h
@@ -121,6 +121,10 @@ struct Unit {
dual_timestamp condition_timestamp;
dual_timestamp assert_timestamp;
+ /* Updated whenever the low-level state changes */
+ dual_timestamp state_change_timestamp;
+
+ /* Updated whenever the (high-level) active state enters or leaves the active or inactive states */
dual_timestamp inactive_exit_timestamp;
dual_timestamp active_enter_timestamp;
dual_timestamp active_exit_timestamp;
diff --git a/src/gpt-auto-generator/gpt-auto-generator.c b/src/gpt-auto-generator/gpt-auto-generator.c
index ce8cecc5cb..a1bad9fcbe 100644
--- a/src/gpt-auto-generator/gpt-auto-generator.c
+++ b/src/gpt-auto-generator/gpt-auto-generator.c
@@ -635,16 +635,19 @@ static int enumerate_partitions(dev_t devnum) {
if (r == 1)
return 0; /* no results */
else if (r == -2) {
- log_warning("%s: probe gave ambiguous results, ignoring", node);
+ log_warning("%s: probe gave ambiguous results, ignoring.", node);
return 0;
} else if (r != 0)
return log_error_errno(errno ?: EIO, "%s: failed to probe: %m", node);
errno = 0;
r = blkid_probe_lookup_value(b, "PTTYPE", &pttype, NULL);
- if (r != 0)
- return log_error_errno(errno ?: EIO,
- "%s: failed to determine partition table type: %m", node);
+ if (r != 0) {
+ if (errno == 0)
+ return 0; /* No partition table found. */
+
+ return log_error_errno(errno, "%s: failed to determine partition table type: %m", node);
+ }
/* We only do this all for GPT... */
if (!streq_ptr(pttype, "gpt")) {
diff --git a/src/journal/cat.c b/src/journal/cat.c
index 7fd4198df8..07c3df522c 100644
--- a/src/journal/cat.c
+++ b/src/journal/cat.c
@@ -34,7 +34,7 @@
#include "syslog-util.h"
#include "util.h"
-static char *arg_identifier = NULL;
+static const char *arg_identifier = NULL;
static int arg_priority = LOG_INFO;
static bool arg_level_prefix = true;
@@ -82,14 +82,10 @@ static int parse_argv(int argc, char *argv[]) {
return version();
case 't':
- free(arg_identifier);
if (isempty(optarg))
arg_identifier = NULL;
- else {
- arg_identifier = strdup(optarg);
- if (!arg_identifier)
- return log_oom();
- }
+ else
+ arg_identifier = optarg;
break;
case 'p':
diff --git a/src/journal/journal-internal.h b/src/journal/journal-internal.h
index 9ff4fea7fb..a55d1bcc47 100644
--- a/src/journal/journal-internal.h
+++ b/src/journal/journal-internal.h
@@ -103,18 +103,29 @@ struct sd_journal {
unsigned current_invalidate_counter, last_invalidate_counter;
usec_t last_process_usec;
+ /* Iterating through unique fields and their data values */
char *unique_field;
JournalFile *unique_file;
uint64_t unique_offset;
+ /* Iterating through known fields */
+ JournalFile *fields_file;
+ uint64_t fields_offset;
+ uint64_t fields_hash_table_index;
+ char *fields_buffer;
+ size_t fields_buffer_allocated;
+
int flags;
- bool on_network;
- bool no_new_files;
- bool unique_file_lost; /* File we were iterating over got
- removed, and there were no more
- files, so sd_j_enumerate_unique
- will return a value equal to 0. */
+ bool on_network:1;
+ bool no_new_files:1;
+ bool unique_file_lost:1; /* File we were iterating over got
+ removed, and there were no more
+ files, so sd_j_enumerate_unique
+ will return a value equal to 0. */
+ bool fields_file_lost:1;
+ bool has_runtime_files:1;
+ bool has_persistent_files:1;
size_t data_threshold;
diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c
index db11421e7a..20f7082175 100644
--- a/src/journal/journalctl.c
+++ b/src/journal/journalctl.c
@@ -69,6 +69,8 @@
#include "strv.h"
#include "syslog-util.h"
#include "terminal-util.h"
+#include "udev.h"
+#include "udev-util.h"
#include "unit-name.h"
#include "user-util.h"
@@ -136,6 +138,8 @@ static enum {
ACTION_SYNC,
ACTION_ROTATE,
ACTION_VACUUM,
+ ACTION_LIST_FIELDS,
+ ACTION_LIST_FIELD_NAMES,
} arg_action = ACTION_SHOW;
typedef struct BootId {
@@ -145,6 +149,84 @@ typedef struct BootId {
LIST_FIELDS(struct BootId, boot_list);
} BootId;
+static int add_matches_for_device(sd_journal *j, const char *devpath) {
+ int r;
+ _cleanup_udev_unref_ struct udev *udev = NULL;
+ _cleanup_udev_device_unref_ struct udev_device *device = NULL;
+ struct udev_device *d = NULL;
+ struct stat st;
+
+ assert(j);
+ assert(devpath);
+
+ if (!path_startswith(devpath, "/dev/")) {
+ log_error("Devpath does not start with /dev/");
+ return -EINVAL;
+ }
+
+ udev = udev_new();
+ if (!udev)
+ return log_oom();
+
+ r = stat(devpath, &st);
+ if (r < 0)
+ log_error_errno(errno, "Couldn't stat file: %m");
+
+ d = device = udev_device_new_from_devnum(udev, S_ISBLK(st.st_mode) ? 'b' : 'c', st.st_rdev);
+ if (!device)
+ return log_error_errno(errno, "Failed to get udev device from devnum %u:%u: %m", major(st.st_rdev), minor(st.st_rdev));
+
+ while (d) {
+ _cleanup_free_ char *match = NULL;
+ const char *subsys, *sysname, *devnode;
+
+ subsys = udev_device_get_subsystem(d);
+ if (!subsys) {
+ d = udev_device_get_parent(d);
+ continue;
+ }
+
+ sysname = udev_device_get_sysname(d);
+ if (!sysname) {
+ d = udev_device_get_parent(d);
+ continue;
+ }
+
+ match = strjoin("_KERNEL_DEVICE=+", subsys, ":", sysname, NULL);
+ if (!match)
+ return log_oom();
+
+ r = sd_journal_add_match(j, match, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add match: %m");
+
+ devnode = udev_device_get_devnode(d);
+ if (devnode) {
+ _cleanup_free_ char *match1 = NULL;
+
+ r = stat(devnode, &st);
+ if (r < 0)
+ return log_error_errno(r, "Failed to stat() device node \"%s\": %m", devnode);
+
+ r = asprintf(&match1, "_KERNEL_DEVICE=%c%u:%u", S_ISBLK(st.st_mode) ? 'b' : 'c', major(st.st_rdev), minor(st.st_rdev));
+ if (r < 0)
+ return log_oom();
+
+ r = sd_journal_add_match(j, match1, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add match: %m");
+ }
+
+ d = udev_device_get_parent(d);
+ }
+
+ r = add_match_this_boot(j, arg_machine);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add match for the current boot: %m");
+
+ return 0;
+}
+
static void pager_open_if_enabled(void) {
if (arg_no_pager)
@@ -244,6 +326,7 @@ static void help(void) {
"\nCommands:\n"
" -h --help Show this help text\n"
" --version Show package version\n"
+ " -N --fields List all field names currently used\n"
" -F --field=FIELD List all values that a specified field takes\n"
" --disk-usage Show total disk usage of all journal files\n"
" --vacuum-size=BYTES Reduce disk usage below specified size\n"
@@ -340,6 +423,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "unit", required_argument, NULL, 'u' },
{ "user-unit", required_argument, NULL, ARG_USER_UNIT },
{ "field", required_argument, NULL, 'F' },
+ { "fields", no_argument, NULL, 'N' },
{ "catalog", no_argument, NULL, 'x' },
{ "list-catalog", no_argument, NULL, ARG_LIST_CATALOG },
{ "dump-catalog", no_argument, NULL, ARG_DUMP_CATALOG },
@@ -361,7 +445,7 @@ static int parse_argv(int argc, char *argv[]) {
assert(argc >= 0);
assert(argv);
- while ((c = getopt_long(argc, argv, "hefo:aln::qmb::kD:p:c:S:U:t:u:F:xrM:", options, NULL)) >= 0)
+ while ((c = getopt_long(argc, argv, "hefo:aln::qmb::kD:p:c:S:U:t:u:NF:xrM:", options, NULL)) >= 0)
switch (c) {
@@ -698,9 +782,14 @@ static int parse_argv(int argc, char *argv[]) {
break;
case 'F':
+ arg_action = ACTION_LIST_FIELDS;
arg_field = optarg;
break;
+ case 'N':
+ arg_action = ACTION_LIST_FIELD_NAMES;
+ break;
+
case 'x':
arg_catalog = true;
break;
@@ -825,13 +914,12 @@ static int add_matches(sd_journal *j, char **args) {
have_term = false;
} else if (path_is_absolute(*i)) {
- _cleanup_free_ char *p, *t = NULL, *t2 = NULL;
+ _cleanup_free_ char *p, *t = NULL, *t2 = NULL, *interpreter = NULL;
const char *path;
- _cleanup_free_ char *interpreter = NULL;
struct stat st;
p = canonicalize_file_name(*i);
- path = p ? p : *i;
+ path = p ?: *i;
if (lstat(path, &st) < 0)
return log_error_errno(errno, "Couldn't stat file: %m");
@@ -845,34 +933,37 @@ static int add_matches(sd_journal *j, char **args) {
return log_oom();
t = strappend("_COMM=", comm);
+ if (!t)
+ return log_oom();
/* Append _EXE only if the interpreter is not a link.
Otherwise, it might be outdated often. */
- if (lstat(interpreter, &st) == 0 &&
- !S_ISLNK(st.st_mode)) {
+ if (lstat(interpreter, &st) == 0 && !S_ISLNK(st.st_mode)) {
t2 = strappend("_EXE=", interpreter);
if (!t2)
return log_oom();
}
- } else
+ } else {
t = strappend("_EXE=", path);
- } else if (S_ISCHR(st.st_mode))
- (void) asprintf(&t, "_KERNEL_DEVICE=c%u:%u", major(st.st_rdev), minor(st.st_rdev));
- else if (S_ISBLK(st.st_mode))
- (void) asprintf(&t, "_KERNEL_DEVICE=b%u:%u", major(st.st_rdev), minor(st.st_rdev));
- else {
+ if (!t)
+ return log_oom();
+ }
+
+ r = sd_journal_add_match(j, t, 0);
+
+ if (r >=0 && t2)
+ r = sd_journal_add_match(j, t2, 0);
+
+ } else if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode)) {
+ r = add_matches_for_device(j, path);
+ if (r < 0)
+ return r;
+ } else {
log_error("File is neither a device node, nor regular file, nor executable: %s", *i);
return -EINVAL;
}
- if (!t)
- return log_oom();
-
- r = sd_journal_add_match(j, t, 0);
- if (t2)
- r = sd_journal_add_match(j, t2, 0);
have_term = true;
-
} else {
r = sd_journal_add_match(j, *i, 0);
have_term = true;
@@ -1136,10 +1227,11 @@ static int add_boot(sd_journal *j) {
const char *reason = (r == 0) ? "No such boot ID in journal" : strerror(-r);
if (sd_id128_is_null(arg_boot_id))
- log_error("Failed to look up boot %+i: %s", arg_boot_offset, reason);
+ log_error("Data from the specified boot (%+i) is not available: %s",
+ arg_boot_offset, reason);
else
- log_error("Failed to look up boot ID "SD_ID128_FORMAT_STR"%+i: %s",
- SD_ID128_FORMAT_VAL(arg_boot_id), arg_boot_offset, reason);
+ log_error("Data from the specified boot ("SD_ID128_FORMAT_STR") is not available: %s",
+ SD_ID128_FORMAT_VAL(arg_boot_id), reason);
return r == 0 ? -ENODATA : r;
}
@@ -2002,6 +2094,8 @@ int main(int argc, char *argv[]) {
case ACTION_DISK_USAGE:
case ACTION_LIST_BOOTS:
case ACTION_VACUUM:
+ case ACTION_LIST_FIELDS:
+ case ACTION_LIST_FIELD_NAMES:
/* These ones require access to the journal files, continue below. */
break;
@@ -2084,13 +2178,33 @@ int main(int argc, char *argv[]) {
goto finish;
}
+ case ACTION_LIST_FIELD_NAMES: {
+ const char *field;
+
+ SD_JOURNAL_FOREACH_FIELD(j, field) {
+ printf("%s\n", field);
+ n_shown ++;
+ }
+
+ r = 0;
+ goto finish;
+ }
+
case ACTION_SHOW:
+ case ACTION_LIST_FIELDS:
break;
default:
assert_not_reached("Unknown action");
}
+ if (arg_boot_offset != 0 &&
+ sd_journal_has_runtime_files(j) > 0 &&
+ sd_journal_has_persistent_files(j) == 0) {
+ log_info("Specifying boot ID has no effect, no persistent journal was found");
+ r = 0;
+ goto finish;
+ }
/* add_boot() must be called first!
* It may need to seek the journal to find parent boot IDs. */
r = add_boot(j);
@@ -2131,10 +2245,12 @@ int main(int argc, char *argv[]) {
log_debug("Journal filter: %s", filter);
}
- if (arg_field) {
+ if (arg_action == ACTION_LIST_FIELDS) {
const void *data;
size_t size;
+ assert(arg_field);
+
r = sd_journal_set_data_threshold(j, 0);
if (r < 0) {
log_error_errno(r, "Failed to unset data size threshold: %m");
diff --git a/src/journal/sd-journal.c b/src/journal/sd-journal.c
index cd5160154a..7a3aaf0cab 100644
--- a/src/journal/sd-journal.c
+++ b/src/journal/sd-journal.c
@@ -1292,6 +1292,12 @@ static int add_file(sd_journal *j, const char *prefix, const char *filename) {
return 0;
path = strjoina(prefix, "/", filename);
+
+ if (!j->has_runtime_files && path_startswith(path, "/run/log/journal"))
+ j->has_runtime_files = true;
+ else if (!j->has_persistent_files && path_startswith(path, "/var/log/journal"))
+ j->has_persistent_files = true;
+
return add_any_file(j, path);
}
@@ -1332,6 +1338,13 @@ static void remove_file_real(sd_journal *j, JournalFile *f) {
j->unique_file_lost = true;
}
+ if (j->fields_file == f) {
+ j->fields_file = ordered_hashmap_next(j->files, j->fields_file->path);
+ j->fields_offset = 0;
+ if (!j->fields_file)
+ j->fields_file_lost = true;
+ }
+
journal_file_close(f);
j->current_invalidate_counter ++;
@@ -1800,6 +1813,7 @@ _public_ void sd_journal_close(sd_journal *j) {
free(j->path);
free(j->prefix);
free(j->unique_field);
+ free(j->fields_buffer);
free(j);
}
@@ -2506,24 +2520,20 @@ _public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_
* traversed files. */
found = false;
ORDERED_HASHMAP_FOREACH(of, j->files, i) {
- Object *oo;
- uint64_t op;
-
if (of == j->unique_file)
break;
- /* Skip this file it didn't have any fields
- * indexed */
- if (JOURNAL_HEADER_CONTAINS(of->header, n_fields) &&
- le64toh(of->header->n_fields) <= 0)
+ /* Skip this file it didn't have any fields indexed */
+ if (JOURNAL_HEADER_CONTAINS(of->header, n_fields) && le64toh(of->header->n_fields) <= 0)
continue;
- r = journal_file_find_data_object_with_hash(of, odata, ol, le64toh(o->data.hash), &oo, &op);
+ r = journal_file_find_data_object_with_hash(of, odata, ol, le64toh(o->data.hash), NULL, NULL);
if (r < 0)
return r;
-
- if (r > 0)
+ if (r > 0) {
found = true;
+ break;
+ }
}
if (found)
@@ -2546,6 +2556,154 @@ _public_ void sd_journal_restart_unique(sd_journal *j) {
j->unique_file_lost = false;
}
+_public_ int sd_journal_enumerate_fields(sd_journal *j, const char **field) {
+ int r;
+
+ assert_return(j, -EINVAL);
+ assert_return(!journal_pid_changed(j), -ECHILD);
+ assert_return(field, -EINVAL);
+
+ if (!j->fields_file) {
+ if (j->fields_file_lost)
+ return 0;
+
+ j->fields_file = ordered_hashmap_first(j->files);
+ if (!j->fields_file)
+ return 0;
+
+ j->fields_hash_table_index = 0;
+ j->fields_offset = 0;
+ }
+
+ for (;;) {
+ JournalFile *f, *of;
+ Iterator i;
+ uint64_t m;
+ Object *o;
+ size_t sz;
+ bool found;
+
+ f = j->fields_file;
+
+ if (j->fields_offset == 0) {
+ bool eof = false;
+
+ /* We are not yet positioned at any field. Let's pick the first one */
+ r = journal_file_map_field_hash_table(f);
+ if (r < 0)
+ return r;
+
+ m = le64toh(f->header->field_hash_table_size) / sizeof(HashItem);
+ for (;;) {
+ if (j->fields_hash_table_index >= m) {
+ /* Reached the end of the hash table, go to the next file. */
+ eof = true;
+ break;
+ }
+
+ j->fields_offset = le64toh(f->field_hash_table[j->fields_hash_table_index].head_hash_offset);
+
+ if (j->fields_offset != 0)
+ break;
+
+ /* Empty hash table bucket, go to next one */
+ j->fields_hash_table_index++;
+ }
+
+ if (eof) {
+ /* Proceed with next file */
+ j->fields_file = ordered_hashmap_next(j->files, f->path);
+ if (!j->fields_file) {
+ *field = NULL;
+ return 0;
+ }
+
+ j->fields_offset = 0;
+ j->fields_hash_table_index = 0;
+ continue;
+ }
+
+ } else {
+ /* We are already positioned at a field. If so, let's figure out the next field from it */
+
+ r = journal_file_move_to_object(f, OBJECT_FIELD, j->fields_offset, &o);
+ if (r < 0)
+ return r;
+
+ j->fields_offset = le64toh(o->field.next_hash_offset);
+ if (j->fields_offset == 0) {
+ /* Reached the end of the hash table chain */
+ j->fields_hash_table_index++;
+ continue;
+ }
+ }
+
+ /* We use OBJECT_UNUSED here, so that the iteator below doesn't remove our mmap window */
+ r = journal_file_move_to_object(f, OBJECT_UNUSED, j->fields_offset, &o);
+ if (r < 0)
+ return r;
+
+ /* Because we used OBJECT_UNUSED above, we need to do our type check manually */
+ if (o->object.type != OBJECT_FIELD) {
+ log_debug("%s:offset " OFSfmt ": object has type %i, expected %i", f->path, j->fields_offset, o->object.type, OBJECT_FIELD);
+ return -EBADMSG;
+ }
+
+ sz = le64toh(o->object.size) - offsetof(Object, field.payload);
+
+ /* Let's see if we already returned this field name before. */
+ found = false;
+ ORDERED_HASHMAP_FOREACH(of, j->files, i) {
+ if (of == f)
+ break;
+
+ /* Skip this file it didn't have any fields indexed */
+ if (JOURNAL_HEADER_CONTAINS(of->header, n_fields) && le64toh(of->header->n_fields) <= 0)
+ continue;
+
+ r = journal_file_find_field_object_with_hash(of, o->field.payload, sz, le64toh(o->field.hash), NULL, NULL);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ found = true;
+ break;
+ }
+ }
+
+ if (found)
+ continue;
+
+ /* Check if this is really a valid string containing no NUL byte */
+ if (memchr(o->field.payload, 0, sz))
+ return -EBADMSG;
+
+ if (sz > j->data_threshold)
+ sz = j->data_threshold;
+
+ if (!GREEDY_REALLOC(j->fields_buffer, j->fields_buffer_allocated, sz + 1))
+ return -ENOMEM;
+
+ memcpy(j->fields_buffer, o->field.payload, sz);
+ j->fields_buffer[sz] = 0;
+
+ if (!field_is_valid(j->fields_buffer))
+ return -EBADMSG;
+
+ *field = j->fields_buffer;
+ return 1;
+ }
+}
+
+_public_ void sd_journal_restart_fields(sd_journal *j) {
+ if (!j)
+ return;
+
+ j->fields_file = NULL;
+ j->fields_hash_table_index = 0;
+ j->fields_offset = 0;
+ j->fields_file_lost = false;
+}
+
_public_ int sd_journal_reliable_fd(sd_journal *j) {
assert_return(j, -EINVAL);
assert_return(!journal_pid_changed(j), -ECHILD);
@@ -2630,3 +2788,15 @@ _public_ int sd_journal_get_data_threshold(sd_journal *j, size_t *sz) {
*sz = j->data_threshold;
return 0;
}
+
+_public_ int sd_journal_has_runtime_files(sd_journal *j) {
+ assert_return(j, -EINVAL);
+
+ return j->has_runtime_files;
+}
+
+_public_ int sd_journal_has_persistent_files(sd_journal *j) {
+ assert_return(j, -EINVAL);
+
+ return j->has_persistent_files;
+}
diff --git a/src/libsystemd/libsystemd.sym b/src/libsystemd/libsystemd.sym
index 043ff13e6f..4ab637b686 100644
--- a/src/libsystemd/libsystemd.sym
+++ b/src/libsystemd/libsystemd.sym
@@ -481,3 +481,11 @@ global:
sd_bus_path_encode_many;
sd_listen_fds_with_names;
} LIBSYSTEMD_226;
+
+LIBSYSTEMD_229 {
+global:
+ sd_journal_has_runtime_files;
+ sd_journal_has_persistent_files;
+ sd_journal_enumerate_fields;
+ sd_journal_restart_fields;
+} LIBSYSTEMD_227;
diff --git a/src/libsystemd/sd-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c
index 11c7330b9b..eeb3453919 100644
--- a/src/libsystemd/sd-event/sd-event.c
+++ b/src/libsystemd/sd-event/sd-event.c
@@ -327,6 +327,10 @@ static int earliest_time_prioq_compare(const void *a, const void *b) {
return 0;
}
+static usec_t time_event_source_latest(const sd_event_source *s) {
+ return usec_add(s->time.next, s->time.accuracy);
+}
+
static int latest_time_prioq_compare(const void *a, const void *b) {
const sd_event_source *x = a, *y = b;
@@ -346,9 +350,9 @@ static int latest_time_prioq_compare(const void *a, const void *b) {
return 1;
/* Order by time */
- if (x->time.next + x->time.accuracy < y->time.next + y->time.accuracy)
+ if (time_event_source_latest(x) < time_event_source_latest(y))
return -1;
- if (x->time.next + x->time.accuracy > y->time.next + y->time.accuracy)
+ if (time_event_source_latest(x) > time_event_source_latest(y))
return 1;
return 0;
@@ -1066,7 +1070,6 @@ _public_ int sd_event_add_time(
int r;
assert_return(e, -EINVAL);
- assert_return(usec != (uint64_t) -1, -EINVAL);
assert_return(accuracy != (uint64_t) -1, -EINVAL);
assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
assert_return(!event_pid_changed(e), -ECHILD);
@@ -1756,7 +1759,6 @@ _public_ int sd_event_source_set_time(sd_event_source *s, uint64_t usec) {
struct clock_data *d;
assert_return(s, -EINVAL);
- assert_return(usec != (uint64_t) -1, -EINVAL);
assert_return(EVENT_SOURCE_IS_TIME(s->type), -EDOM);
assert_return(s->event->state != SD_EVENT_FINISHED, -ESTALE);
assert_return(!event_pid_changed(s->event), -ECHILD);
@@ -1886,6 +1888,8 @@ static usec_t sleep_between(sd_event *e, usec_t a, usec_t b) {
if (a <= 0)
return 0;
+ if (a >= USEC_INFINITY)
+ return USEC_INFINITY;
if (b <= a + 1)
return a;
@@ -1975,7 +1979,7 @@ static int event_arm_timer(
d->needs_rearm = false;
a = prioq_peek(d->earliest);
- if (!a || a->enabled == SD_EVENT_OFF) {
+ if (!a || a->enabled == SD_EVENT_OFF || a->time.next == USEC_INFINITY) {
if (d->fd < 0)
return 0;
@@ -1995,7 +1999,7 @@ static int event_arm_timer(
b = prioq_peek(d->latest);
assert_se(b && b->enabled != SD_EVENT_OFF);
- t = sleep_between(e, a->time.next, b->time.next + b->time.accuracy);
+ t = sleep_between(e, a->time.next, time_event_source_latest(b));
if (d->next == t)
return 0;
diff --git a/src/libsystemd/sd-netlink/sd-netlink.c b/src/libsystemd/sd-netlink/sd-netlink.c
index 15d387df2c..d3eb379c9a 100644
--- a/src/libsystemd/sd-netlink/sd-netlink.c
+++ b/src/libsystemd/sd-netlink/sd-netlink.c
@@ -44,11 +44,8 @@ static int sd_netlink_new(sd_netlink **ret) {
return -ENOMEM;
rtnl->n_ref = REFCNT_INIT;
-
rtnl->fd = -1;
-
rtnl->sockaddr.nl.nl_family = AF_NETLINK;
-
rtnl->original_pid = getpid();
LIST_HEAD_INIT(rtnl->match_callbacks);
@@ -87,6 +84,9 @@ int sd_netlink_new_from_netlink(sd_netlink **ret, int fd) {
if (r < 0)
return -errno;
+ if (rtnl->sockaddr.nl.nl_family != AF_NETLINK)
+ return -EINVAL;
+
rtnl->fd = fd;
*ret = rtnl;
@@ -118,8 +118,10 @@ int sd_netlink_open_fd(sd_netlink **ret, int fd) {
rtnl->fd = fd;
r = socket_bind(rtnl);
- if (r < 0)
+ if (r < 0) {
+ rtnl->fd = -1; /* on failure, the caller remains owner of the fd, hence don't close it here */
return r;
+ }
*ret = rtnl;
rtnl = NULL;
diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c
index 9eda4638e5..ddfeaa1d87 100644
--- a/src/login/logind-dbus.c
+++ b/src/login/logind-dbus.c
@@ -1228,7 +1228,6 @@ static int attach_device(Manager *m, const char *seat, const char *sysfs) {
return -ENOMEM;
mkdir_p_label("/etc/udev/rules.d", 0755);
- mac_selinux_init("/etc");
r = write_string_file_atomic_label(file, rule);
if (r < 0)
return r;
diff --git a/src/login/logind.c b/src/login/logind.c
index 9723e2f4e0..7e2d114194 100644
--- a/src/login/logind.c
+++ b/src/login/logind.c
@@ -36,6 +36,7 @@
#include "fd-util.h"
#include "formats-util.h"
#include "logind.h"
+#include "selinux-util.h"
#include "signal-util.h"
#include "strv.h"
#include "udev-util.h"
@@ -1127,6 +1128,12 @@ int main(int argc, char *argv[]) {
goto finish;
}
+ r = mac_selinux_init("/run");
+ if (r < 0) {
+ log_error_errno(r, "Could not initialize labelling: %m");
+ goto finish;
+ }
+
/* Always create the directories people can create inotify
* watches in. Note that some applications might check for the
* existence of /run/systemd/seats/ to determine whether
diff --git a/src/nspawn/nspawn-register.c b/src/nspawn/nspawn-register.c
index 65ca9c762b..a89de4b324 100644
--- a/src/nspawn/nspawn-register.c
+++ b/src/nspawn/nspawn-register.c
@@ -166,17 +166,9 @@ int register_machine(
}
STRV_FOREACH(i, properties) {
- r = sd_bus_message_open_container(m, 'r', "sv");
- if (r < 0)
- return bus_log_create_error(r);
-
r = bus_append_unit_property_assignment(m, *i);
if (r < 0)
return r;
-
- r = sd_bus_message_close_container(m);
- if (r < 0)
- return bus_log_create_error(r);
}
r = sd_bus_message_close_container(m);
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
index b183e3aecc..9dd4c051b2 100644
--- a/src/nspawn/nspawn.c
+++ b/src/nspawn/nspawn.c
@@ -1337,6 +1337,7 @@ static int setup_journal(const char *directory) {
sd_id128_t machine_id, this_id;
_cleanup_free_ char *b = NULL, *d = NULL;
const char *etc_machine_id, *p, *q;
+ bool try;
char *id;
int r;
@@ -1344,16 +1345,21 @@ static int setup_journal(const char *directory) {
if (arg_ephemeral)
return 0;
+ if (arg_link_journal == LINK_NO)
+ return 0;
+
+ try = arg_link_journal_try || arg_link_journal == LINK_AUTO;
+
etc_machine_id = prefix_roota(directory, "/etc/machine-id");
r = read_one_line_file(etc_machine_id, &b);
- if (r == -ENOENT && arg_link_journal == LINK_AUTO)
+ if (r == -ENOENT && try)
return 0;
else if (r < 0)
return log_error_errno(r, "Failed to read machine ID from %s: %m", etc_machine_id);
id = strstrip(b);
- if (isempty(id) && arg_link_journal == LINK_AUTO)
+ if (isempty(id) && try)
return 0;
/* Verify validity */
@@ -1366,16 +1372,13 @@ static int setup_journal(const char *directory) {
return log_error_errno(r, "Failed to retrieve machine ID: %m");
if (sd_id128_equal(machine_id, this_id)) {
- log_full(arg_link_journal == LINK_AUTO ? LOG_WARNING : LOG_ERR,
+ log_full(try ? LOG_WARNING : LOG_ERR,
"Host and machine ids are equal (%s): refusing to link journals", id);
- if (arg_link_journal == LINK_AUTO)
+ if (try)
return 0;
return -EEXIST;
}
- if (arg_link_journal == LINK_NO)
- return 0;
-
r = userns_mkdir(directory, "/var", 0755, 0, 0);
if (r < 0)
return log_error_errno(r, "Failed to create /var: %m");
@@ -1392,21 +1395,19 @@ static int setup_journal(const char *directory) {
q = prefix_roota(directory, p);
if (path_is_mount_point(p, 0) > 0) {
- if (arg_link_journal != LINK_AUTO) {
- log_error("%s: already a mount point, refusing to use for journal", p);
- return -EEXIST;
- }
+ if (try)
+ return 0;
- return 0;
+ log_error("%s: already a mount point, refusing to use for journal", p);
+ return -EEXIST;
}
if (path_is_mount_point(q, 0) > 0) {
- if (arg_link_journal != LINK_AUTO) {
- log_error("%s: already a mount point, refusing to use for journal", q);
- return -EEXIST;
- }
+ if (try)
+ return 0;
- return 0;
+ log_error("%s: already a mount point, refusing to use for journal", q);
+ return -EEXIST;
}
r = readlink_and_make_absolute(p, &d);
@@ -1440,7 +1441,7 @@ static int setup_journal(const char *directory) {
if (arg_link_journal == LINK_GUEST) {
if (symlink(q, p) < 0) {
- if (arg_link_journal_try) {
+ if (try) {
log_debug_errno(errno, "Failed to symlink %s to %s, skipping journal setup: %m", q, p);
return 0;
} else
@@ -1456,9 +1457,9 @@ static int setup_journal(const char *directory) {
if (arg_link_journal == LINK_HOST) {
/* don't create parents here -- if the host doesn't have
* permanent journal set up, don't force it here */
- r = mkdir(p, 0755);
- if (r < 0) {
- if (arg_link_journal_try) {
+
+ if (mkdir(p, 0755) < 0 && errno != EEXIST) {
+ if (try) {
log_debug_errno(errno, "Failed to create %s, skipping journal setup: %m", p);
return 0;
} else
diff --git a/src/resolve/dns-type.c b/src/resolve/dns-type.c
index 56720646ca..49210b2ca9 100644
--- a/src/resolve/dns-type.c
+++ b/src/resolve/dns-type.c
@@ -22,6 +22,7 @@
#include <sys/socket.h>
#include "dns-type.h"
+#include "parse-util.h"
#include "string-util.h"
typedef const struct {
@@ -41,10 +42,19 @@ int dns_type_from_string(const char *s) {
assert(s);
sc = lookup_dns_type(s, strlen(s));
- if (!sc)
- return _DNS_TYPE_INVALID;
+ if (sc)
+ return sc->id;
- return sc->id;
+ s = startswith_no_case(s, "TYPE");
+ if (s) {
+ unsigned x;
+
+ if (safe_atou(s, &x) >= 0 &&
+ x <= UINT16_MAX)
+ return (int) x;
+ }
+
+ return _DNS_TYPE_INVALID;
}
bool dns_type_is_pseudo(uint16_t type) {
@@ -228,3 +238,71 @@ int dns_class_from_string(const char *s) {
return _DNS_CLASS_INVALID;
}
+
+const char* tlsa_cert_usage_to_string(uint8_t cert_usage) {
+
+ switch (cert_usage) {
+
+ case 0:
+ return "CA constraint";
+
+ case 1:
+ return "Service certificate constraint";
+
+ case 2:
+ return "Trust anchor assertion";
+
+ case 3:
+ return "Domain-issued certificate";
+
+ case 4 ... 254:
+ return "Unassigned";
+
+ case 255:
+ return "Private use";
+ }
+
+ return NULL; /* clang cannot count that we covered everything */
+}
+
+const char* tlsa_selector_to_string(uint8_t selector) {
+ switch (selector) {
+
+ case 0:
+ return "Full Certificate";
+
+ case 1:
+ return "SubjectPublicKeyInfo";
+
+ case 2 ... 254:
+ return "Unassigned";
+
+ case 255:
+ return "Private use";
+ }
+
+ return NULL;
+}
+
+const char* tlsa_matching_type_to_string(uint8_t selector) {
+
+ switch (selector) {
+
+ case 0:
+ return "No hash used";
+
+ case 1:
+ return "SHA-256";
+
+ case 2:
+ return "SHA-512";
+
+ case 3 ... 254:
+ return "Unassigned";
+
+ case 255:
+ return "Private use";
+ }
+
+ return NULL;
+}
diff --git a/src/resolve/dns-type.h b/src/resolve/dns-type.h
index 60ff160383..d025544bab 100644
--- a/src/resolve/dns-type.h
+++ b/src/resolve/dns-type.h
@@ -87,6 +87,7 @@ enum {
DNS_TYPE_TALINK,
DNS_TYPE_CDS,
DNS_TYPE_CDNSKEY,
+ DNS_TYPE_OPENPGPKEY,
DNS_TYPE_SPF = 0x63,
DNS_TYPE_NID,
@@ -138,8 +139,18 @@ int dns_type_to_af(uint16_t t);
bool dns_class_is_pseudo(uint16_t class);
bool dns_class_is_valid_rr(uint16_t class);
+/* TYPE?? follows http://tools.ietf.org/html/rfc3597#section-5 */
const char *dns_type_to_string(int type);
int dns_type_from_string(const char *s);
const char *dns_class_to_string(uint16_t type);
int dns_class_from_string(const char *name);
+
+/* https://tools.ietf.org/html/draft-ietf-dane-protocol-23#section-7.2 */
+const char *tlsa_cert_usage_to_string(uint8_t cert_usage);
+
+/* https://tools.ietf.org/html/draft-ietf-dane-protocol-23#section-7.3 */
+const char *tlsa_selector_to_string(uint8_t selector);
+
+/* https://tools.ietf.org/html/draft-ietf-dane-protocol-23#section-7.4 */
+const char *tlsa_matching_type_to_string(uint8_t selector);
diff --git a/src/resolve/resolve-tool.c b/src/resolve/resolve-tool.c
index fdaeb8d926..9bee953839 100644
--- a/src/resolve/resolve-tool.c
+++ b/src/resolve/resolve-tool.c
@@ -900,6 +900,12 @@ static int reset_statistics(sd_bus *bus) {
return 0;
}
+static void help_protocol_types(void) {
+ if (arg_legend)
+ puts("Known protocol types:");
+ puts("dns\nllmnr\nllmnr-ipv4\nllmnr-ipv6");
+}
+
static void help_dns_types(void) {
int i;
const char *t;
@@ -930,22 +936,22 @@ static void help(void) {
printf("%s [OPTIONS...] NAME...\n"
"%s [OPTIONS...] --service [[NAME] TYPE] DOMAIN\n\n"
"Resolve domain names, IPv4 and IPv6 addresses, DNS resource records, and services.\n\n"
- " -h --help Show this help\n"
- " --version Show package version\n"
- " -4 Resolve IPv4 addresses\n"
- " -6 Resolve IPv6 addresses\n"
- " -i --interface=INTERFACE Look on interface\n"
- " -p --protocol=PROTOCOL Look via protocol\n"
- " -t --type=TYPE Query RR with DNS type\n"
- " -c --class=CLASS Query RR with DNS class\n"
- " --service Resolve service (SRV)\n"
- " --service-address=BOOL Do [not] resolve address for services\n"
- " --service-txt=BOOL Do [not] resolve TXT records for services\n"
- " --cname=BOOL Do [not] follow CNAME redirects\n"
- " --search=BOOL Do [not] use search domains\n"
- " --legend=BOOL Do [not] print column headers and meta information\n"
- " --statistics Show resolver statistics\n"
- " --reset-statistics Reset resolver statistics\n"
+ " -h --help Show this help\n"
+ " --version Show package version\n"
+ " -4 Resolve IPv4 addresses\n"
+ " -6 Resolve IPv6 addresses\n"
+ " -i --interface=INTERFACE Look on interface\n"
+ " -p --protocol=PROTOCOL|help Look via protocol\n"
+ " -t --type=TYPE|help Query RR with DNS type\n"
+ " -c --class=CLASS|help Query RR with DNS class\n"
+ " --service Resolve service (SRV)\n"
+ " --service-address=BOOL Do [not] resolve address for services\n"
+ " --service-txt=BOOL Do [not] resolve TXT records for services\n"
+ " --cname=BOOL Do [not] follow CNAME redirects\n"
+ " --search=BOOL Do [not] use search domains\n"
+ " --legend=BOOL Do [not] print column headers and meta information\n"
+ " --statistics Show resolver statistics\n"
+ " --reset-statistics Reset resolver statistics\n"
, program_invocation_short_name, program_invocation_short_name);
}
@@ -1061,7 +1067,10 @@ static int parse_argv(int argc, char *argv[]) {
break;
case 'p':
- if (streq(optarg, "dns"))
+ if (streq(optarg, "help")) {
+ help_protocol_types();
+ return 0;
+ } else if (streq(optarg, "dns"))
arg_flags |= SD_RESOLVED_DNS;
else if (streq(optarg, "llmnr"))
arg_flags |= SD_RESOLVED_LLMNR;
diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c
index 834ae837de..251e7a50a4 100644
--- a/src/resolve/resolved-bus.c
+++ b/src/resolve/resolved-bus.c
@@ -281,6 +281,7 @@ static int bus_method_resolve_hostname(sd_bus_message *message, void *userdata,
q->request = sd_bus_message_ref(message);
q->request_family = family;
q->complete = bus_method_resolve_hostname_complete;
+ q->suppress_unroutable_family = family == AF_UNSPEC;
r = dns_query_bus_track(q, message);
if (r < 0)
diff --git a/src/resolve/resolved-dns-dnssec.c b/src/resolve/resolved-dns-dnssec.c
index 76c801cce8..21cf161494 100644
--- a/src/resolve/resolved-dns-dnssec.c
+++ b/src/resolve/resolved-dns-dnssec.c
@@ -19,7 +19,9 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#ifdef HAVE_GCRYPT
#include <gcrypt.h>
+#endif
#include "alloc-util.h"
#include "dns-domain.h"
@@ -48,19 +50,6 @@
* Normal RR → RRSIG/DNSKEY+ → DS → RRSIG/DNSKEY+ → DS → ... → DS → RRSIG/DNSKEY+ → DS
*/
-static void initialize_libgcrypt(void) {
- const char *p;
-
- if (gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P))
- return;
-
- p = gcry_check_version("1.4.5");
- assert(p);
-
- gcry_control(GCRYCTL_DISABLE_SECMEM);
- gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
-}
-
uint16_t dnssec_keytag(DnsResourceRecord *dnskey, bool mask_revoke) {
const uint8_t *p;
uint32_t sum, f;
@@ -88,6 +77,70 @@ uint16_t dnssec_keytag(DnsResourceRecord *dnskey, bool mask_revoke) {
return sum & UINT32_C(0xFFFF);
}
+int dnssec_canonicalize(const char *n, char *buffer, size_t buffer_max) {
+ size_t c = 0;
+ int r;
+
+ /* Converts the specified hostname into DNSSEC canonicalized
+ * form. */
+
+ if (buffer_max < 2)
+ return -ENOBUFS;
+
+ for (;;) {
+ r = dns_label_unescape(&n, buffer, buffer_max);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
+ if (buffer_max < (size_t) r + 2)
+ return -ENOBUFS;
+
+ /* The DNSSEC canonical form is not clear on what to
+ * do with dots appearing in labels, the way DNS-SD
+ * does it. Refuse it for now. */
+
+ if (memchr(buffer, '.', r))
+ return -EINVAL;
+
+ ascii_strlower_n(buffer, (size_t) r);
+ buffer[r] = '.';
+
+ buffer += r + 1;
+ c += r + 1;
+
+ buffer_max -= r + 1;
+ }
+
+ if (c <= 0) {
+ /* Not even a single label: this is the root domain name */
+
+ assert(buffer_max > 2);
+ buffer[0] = '.';
+ buffer[1] = 0;
+
+ return 1;
+ }
+
+ return (int) c;
+}
+
+#ifdef HAVE_GCRYPT
+
+static void initialize_libgcrypt(void) {
+ const char *p;
+
+ if (gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P))
+ return;
+
+ p = gcry_check_version("1.4.5");
+ assert(p);
+
+ gcry_control(GCRYCTL_DISABLE_SECMEM);
+ gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
+}
+
static int rr_compare(const void *a, const void *b) {
DnsResourceRecord **x = (DnsResourceRecord**) a, **y = (DnsResourceRecord**) b;
size_t m;
@@ -971,55 +1024,6 @@ int dnssec_has_rrsig(DnsAnswer *a, const DnsResourceKey *key) {
return 0;
}
-int dnssec_canonicalize(const char *n, char *buffer, size_t buffer_max) {
- size_t c = 0;
- int r;
-
- /* Converts the specified hostname into DNSSEC canonicalized
- * form. */
-
- if (buffer_max < 2)
- return -ENOBUFS;
-
- for (;;) {
- r = dns_label_unescape(&n, buffer, buffer_max);
- if (r < 0)
- return r;
- if (r == 0)
- break;
-
- if (buffer_max < (size_t) r + 2)
- return -ENOBUFS;
-
- /* The DNSSEC canonical form is not clear on what to
- * do with dots appearing in labels, the way DNS-SD
- * does it. Refuse it for now. */
-
- if (memchr(buffer, '.', r))
- return -EINVAL;
-
- ascii_strlower_n(buffer, (size_t) r);
- buffer[r] = '.';
-
- buffer += r + 1;
- c += r + 1;
-
- buffer_max -= r + 1;
- }
-
- if (c <= 0) {
- /* Not even a single label: this is the root domain name */
-
- assert(buffer_max > 2);
- buffer[0] = '.';
- buffer[1] = 0;
-
- return 1;
- }
-
- return (int) c;
-}
-
static int digest_to_gcrypt_md(uint8_t algorithm) {
/* Translates a DNSSEC digest algorithm into a gcrypt digest identifier */
@@ -1882,7 +1886,7 @@ int dnssec_nsec_test(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *r
return 0;
}
-int dnssec_nsec_test_enclosed(DnsAnswer *answer, uint16_t type, const char *name, const char *zone, bool *authenticated) {
+static int dnssec_nsec_test_enclosed(DnsAnswer *answer, uint16_t type, const char *name, const char *zone, bool *authenticated) {
DnsResourceRecord *rr;
DnsAnswerFlags flags;
int r;
@@ -2114,6 +2118,77 @@ int dnssec_test_positive_wildcard(
return dnssec_test_positive_wildcard_nsec(answer, name, source, zone, authenticated);
}
+#else
+
+int dnssec_verify_rrset(
+ DnsAnswer *a,
+ const DnsResourceKey *key,
+ DnsResourceRecord *rrsig,
+ DnsResourceRecord *dnskey,
+ usec_t realtime,
+ DnssecResult *result) {
+
+ return -EOPNOTSUPP;
+}
+
+int dnssec_rrsig_match_dnskey(DnsResourceRecord *rrsig, DnsResourceRecord *dnskey, bool revoked_ok) {
+
+ return -EOPNOTSUPP;
+}
+
+int dnssec_key_match_rrsig(const DnsResourceKey *key, DnsResourceRecord *rrsig) {
+
+ return -EOPNOTSUPP;
+}
+
+int dnssec_verify_rrset_search(
+ DnsAnswer *a,
+ const DnsResourceKey *key,
+ DnsAnswer *validated_dnskeys,
+ usec_t realtime,
+ DnssecResult *result,
+ DnsResourceRecord **ret_rrsig) {
+
+ return -EOPNOTSUPP;
+}
+
+int dnssec_has_rrsig(DnsAnswer *a, const DnsResourceKey *key) {
+
+ return -EOPNOTSUPP;
+}
+
+int dnssec_verify_dnskey_by_ds(DnsResourceRecord *dnskey, DnsResourceRecord *ds, bool mask_revoke) {
+
+ return -EOPNOTSUPP;
+}
+
+int dnssec_verify_dnskey_by_ds_search(DnsResourceRecord *dnskey, DnsAnswer *validated_ds) {
+
+ return -EOPNOTSUPP;
+}
+
+int dnssec_nsec3_hash(DnsResourceRecord *nsec3, const char *name, void *ret) {
+
+ return -EOPNOTSUPP;
+}
+
+int dnssec_nsec_test(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated, uint32_t *ttl) {
+
+ return -EOPNOTSUPP;
+}
+
+int dnssec_test_positive_wildcard(
+ DnsAnswer *answer,
+ const char *name,
+ const char *source,
+ const char *zone,
+ bool *authenticated) {
+
+ return -EOPNOTSUPP;
+}
+
+#endif
+
static const char* const dnssec_result_table[_DNSSEC_RESULT_MAX] = {
[DNSSEC_VALIDATED] = "validated",
[DNSSEC_VALIDATED_WILDCARD] = "validated-wildcard",
diff --git a/src/resolve/resolved-dns-dnssec.h b/src/resolve/resolved-dns-dnssec.h
index c99861b8e5..4542f0aa89 100644
--- a/src/resolve/resolved-dns-dnssec.h
+++ b/src/resolve/resolved-dns-dnssec.h
@@ -94,7 +94,6 @@ typedef enum DnssecNsecResult {
int dnssec_nsec_test(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated, uint32_t *ttl);
-int dnssec_nsec_test_enclosed(DnsAnswer *answer, uint16_t type, const char *name, const char *zone, bool *authenticated);
int dnssec_test_positive_wildcard(DnsAnswer *a, const char *name, const char *source, const char *zone, bool *authenticated);
diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c
index 032e719595..5cbe20832f 100644
--- a/src/resolve/resolved-dns-packet.c
+++ b/src/resolve/resolved-dns-packet.c
@@ -1058,11 +1058,28 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *star
break;
+ case DNS_TYPE_TLSA:
+ r = dns_packet_append_uint8(p, rr->tlsa.cert_usage, NULL);
+ if (r < 0)
+ goto fail;
+
+ r = dns_packet_append_uint8(p, rr->tlsa.selector, NULL);
+ if (r < 0)
+ goto fail;
+
+ r = dns_packet_append_uint8(p, rr->tlsa.matching_type, NULL);
+ if (r < 0)
+ goto fail;
+
+ r = dns_packet_append_blob(p, rr->tlsa.data, rr->tlsa.data_size, NULL);
+ break;
+
case DNS_TYPE_OPT:
+ case DNS_TYPE_OPENPGPKEY:
case _DNS_TYPE_INVALID: /* unparseable */
default:
- r = dns_packet_append_blob(p, rr->generic.data, rr->generic.size, NULL);
+ r = dns_packet_append_blob(p, rr->generic.data, rr->generic.data_size, NULL);
break;
}
if (r < 0)
@@ -1976,10 +1993,36 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl
break;
}
+ case DNS_TYPE_TLSA:
+ r = dns_packet_read_uint8(p, &rr->tlsa.cert_usage, NULL);
+ if (r < 0)
+ goto fail;
+
+ r = dns_packet_read_uint8(p, &rr->tlsa.selector, NULL);
+ if (r < 0)
+ goto fail;
+
+ r = dns_packet_read_uint8(p, &rr->tlsa.matching_type, NULL);
+ if (r < 0)
+ goto fail;
+
+ r = dns_packet_read_memdup(p, rdlength - 3,
+ &rr->tlsa.data, &rr->tlsa.data_size,
+ NULL);
+ if (rr->tlsa.data_size <= 0) {
+ /* the accepted size depends on the algorithm, but for now
+ just ensure that the value is greater than zero */
+ r = -EBADMSG;
+ goto fail;
+ }
+
+ break;
+
case DNS_TYPE_OPT: /* we only care about the header of OPT for now. */
+ case DNS_TYPE_OPENPGPKEY:
default:
unparseable:
- r = dns_packet_read_memdup(p, rdlength, &rr->generic.data, &rr->generic.size, NULL);
+ r = dns_packet_read_memdup(p, rdlength, &rr->generic.data, &rr->generic.data_size, NULL);
if (r < 0)
goto fail;
break;
@@ -2021,7 +2064,7 @@ static bool opt_is_good(DnsResourceRecord *rr, bool *rfc6975) {
return false;
p = rr->opt.data;
- l = rr->opt.size;
+ l = rr->opt.data_size;
while (l > 0) {
uint16_t option_code, option_length;
diff --git a/src/resolve/resolved-dns-query.c b/src/resolve/resolved-dns-query.c
index a00851658e..06d30d7863 100644
--- a/src/resolve/resolved-dns-query.c
+++ b/src/resolve/resolved-dns-query.c
@@ -21,6 +21,7 @@
#include "alloc-util.h"
#include "dns-domain.h"
+#include "dns-type.h"
#include "hostname-util.h"
#include "local-addresses.h"
#include "resolved-dns-query.h"
@@ -161,6 +162,7 @@ static int dns_query_candidate_go(DnsQueryCandidate *c) {
DnsTransaction *t;
Iterator i;
int r;
+ unsigned n = 0;
assert(c);
@@ -172,8 +174,14 @@ static int dns_query_candidate_go(DnsQueryCandidate *c) {
r = dns_transaction_go(t);
if (r < 0)
return r;
+
+ n++;
}
+ /* If there was nothing to start, then let's proceed immediately */
+ if (n == 0)
+ dns_query_candidate_notify(c);
+
return 0;
}
@@ -222,6 +230,31 @@ static DnsTransactionState dns_query_candidate_state(DnsQueryCandidate *c) {
return state;
}
+static bool dns_query_candidate_is_routable(DnsQueryCandidate *c, uint16_t type) {
+ int family;
+
+ assert(c);
+
+ /* Checks whether the specified RR type matches an address family that is routable on the link(s) the scope of
+ * this candidate belongs to. Specifically, whether there's a routable IPv4 address on it if we query an A RR,
+ * or a routable IPv6 address if we query an AAAA RR. */
+
+ if (!c->query->suppress_unroutable_family)
+ return true;
+
+ if (c->scope->protocol != DNS_PROTOCOL_DNS)
+ return true;
+
+ family = dns_type_to_af(type);
+ if (family < 0)
+ return true;
+
+ if (c->scope->link)
+ return link_relevant(c->scope->link, family, false);
+ else
+ return manager_routable(c->scope->manager, family);
+}
+
static int dns_query_candidate_setup_transactions(DnsQueryCandidate *c) {
DnsQuestion *question;
DnsResourceKey *key;
@@ -236,14 +269,24 @@ static int dns_query_candidate_setup_transactions(DnsQueryCandidate *c) {
/* Create one transaction per question key */
DNS_QUESTION_FOREACH(key, question) {
_cleanup_(dns_resource_key_unrefp) DnsResourceKey *new_key = NULL;
+ DnsResourceKey *qkey;
+
+ if (!dns_query_candidate_is_routable(c, key->type))
+ continue;
if (c->search_domain) {
r = dns_resource_key_new_append_suffix(&new_key, key, c->search_domain->name);
if (r < 0)
goto fail;
- }
- r = dns_query_candidate_add_transaction(c, new_key ?: key);
+ qkey = new_key;
+ } else
+ qkey = key;
+
+ if (!dns_scope_good_key(c->scope, qkey))
+ continue;
+
+ r = dns_query_candidate_add_transaction(c, qkey);
if (r < 0)
goto fail;
diff --git a/src/resolve/resolved-dns-query.h b/src/resolve/resolved-dns-query.h
index c7e4ce9a00..75c2c14c1f 100644
--- a/src/resolve/resolved-dns-query.h
+++ b/src/resolve/resolved-dns-query.h
@@ -69,6 +69,10 @@ struct DnsQuery {
uint64_t flags;
int ifindex;
+ /* If true, A or AAAA RR lookups will be suppressed on links with no routable address of the matching address
+ * family */
+ bool suppress_unroutable_family;
+
DnsTransactionState state;
unsigned n_cname_redirects;
diff --git a/src/resolve/resolved-dns-rr.c b/src/resolve/resolved-dns-rr.c
index 7273ef3825..783ec7516c 100644
--- a/src/resolve/resolved-dns-rr.c
+++ b/src/resolve/resolved-dns-rr.c
@@ -25,11 +25,13 @@
#include "dns-domain.h"
#include "dns-type.h"
#include "hexdecoct.h"
+#include "resolved-dns-dnssec.h"
#include "resolved-dns-packet.h"
#include "resolved-dns-rr.h"
#include "string-table.h"
#include "string-util.h"
#include "strv.h"
+#include "terminal-util.h"
DnsResourceKey* dns_resource_key_new(uint16_t class, uint16_t type, const char *name) {
DnsResourceKey *k;
@@ -486,6 +488,11 @@ DnsResourceRecord* dns_resource_record_unref(DnsResourceRecord *rr) {
case DNS_TYPE_AAAA:
break;
+ case DNS_TYPE_TLSA:
+ free(rr->tlsa.data);
+ break;
+
+ case DNS_TYPE_OPENPGPKEY:
default:
free(rr->generic.data);
}
@@ -564,6 +571,10 @@ int dns_resource_record_new_address(DnsResourceRecord **ret, int family, const u
return 0;
}
+#define FIELD_EQUAL(a, b, field) \
+ ((a).field ## _size == (b).field ## _size && \
+ memcmp((a).field, (b).field, (a).field ## _size) == 0)
+
int dns_resource_record_equal(const DnsResourceRecord *a, const DnsResourceRecord *b) {
int r;
@@ -645,36 +656,30 @@ int dns_resource_record_equal(const DnsResourceRecord *a, const DnsResourceRecor
return a->ds.key_tag == b->ds.key_tag &&
a->ds.algorithm == b->ds.algorithm &&
a->ds.digest_type == b->ds.digest_type &&
- a->ds.digest_size == b->ds.digest_size &&
- memcmp(a->ds.digest, b->ds.digest, a->ds.digest_size) == 0;
+ FIELD_EQUAL(a->ds, b->ds, digest);
case DNS_TYPE_SSHFP:
return a->sshfp.algorithm == b->sshfp.algorithm &&
a->sshfp.fptype == b->sshfp.fptype &&
- a->sshfp.fingerprint_size == b->sshfp.fingerprint_size &&
- memcmp(a->sshfp.fingerprint, b->sshfp.fingerprint, a->sshfp.fingerprint_size) == 0;
+ FIELD_EQUAL(a->sshfp, b->sshfp, fingerprint);
case DNS_TYPE_DNSKEY:
return a->dnskey.flags == b->dnskey.flags &&
a->dnskey.protocol == b->dnskey.protocol &&
a->dnskey.algorithm == b->dnskey.algorithm &&
- a->dnskey.key_size == b->dnskey.key_size &&
- memcmp(a->dnskey.key, b->dnskey.key, a->dnskey.key_size) == 0;
+ FIELD_EQUAL(a->dnskey, b->dnskey, key);
case DNS_TYPE_RRSIG:
/* do the fast comparisons first */
- if (a->rrsig.type_covered != b->rrsig.type_covered ||
- a->rrsig.algorithm != b->rrsig.algorithm ||
- a->rrsig.labels != b->rrsig.labels ||
- a->rrsig.original_ttl != b->rrsig.original_ttl ||
- a->rrsig.expiration != b->rrsig.expiration ||
- a->rrsig.inception != b->rrsig.inception ||
- a->rrsig.key_tag != b->rrsig.key_tag ||
- a->rrsig.signature_size != b->rrsig.signature_size ||
- memcmp(a->rrsig.signature, b->rrsig.signature, a->rrsig.signature_size) != 0)
- return false;
-
- return dns_name_equal(a->rrsig.signer, b->rrsig.signer);
+ return a->rrsig.type_covered == b->rrsig.type_covered &&
+ a->rrsig.algorithm == b->rrsig.algorithm &&
+ a->rrsig.labels == b->rrsig.labels &&
+ a->rrsig.original_ttl == b->rrsig.original_ttl &&
+ a->rrsig.expiration == b->rrsig.expiration &&
+ a->rrsig.inception == b->rrsig.inception &&
+ a->rrsig.key_tag == b->rrsig.key_tag &&
+ FIELD_EQUAL(a->rrsig, b->rrsig, signature) &&
+ dns_name_equal(a->rrsig.signer, b->rrsig.signer);
case DNS_TYPE_NSEC:
return dns_name_equal(a->nsec.next_domain_name, b->nsec.next_domain_name) &&
@@ -682,16 +687,20 @@ int dns_resource_record_equal(const DnsResourceRecord *a, const DnsResourceRecor
case DNS_TYPE_NSEC3:
return a->nsec3.algorithm == b->nsec3.algorithm &&
- a->nsec3.flags == b->nsec3.flags &&
- a->nsec3.iterations == b->nsec3.iterations &&
- a->nsec3.salt_size == b->nsec3.salt_size &&
- memcmp(a->nsec3.salt, b->nsec3.salt, a->nsec3.salt_size) == 0 &&
- memcmp(a->nsec3.next_hashed_name, b->nsec3.next_hashed_name, a->nsec3.next_hashed_name_size) == 0 &&
- bitmap_equal(a->nsec3.types, b->nsec3.types);
+ a->nsec3.flags == b->nsec3.flags &&
+ a->nsec3.iterations == b->nsec3.iterations &&
+ FIELD_EQUAL(a->nsec3, b->nsec3, salt) &&
+ FIELD_EQUAL(a->nsec3, b->nsec3, next_hashed_name) &&
+ bitmap_equal(a->nsec3.types, b->nsec3.types);
+
+ case DNS_TYPE_TLSA:
+ return a->tlsa.cert_usage == b->tlsa.cert_usage &&
+ a->tlsa.selector == b->tlsa.selector &&
+ a->tlsa.matching_type == b->tlsa.matching_type &&
+ FIELD_EQUAL(a->tlsa, b->tlsa, data);
default:
- return a->generic.size == b->generic.size &&
- memcmp(a->generic.data, b->generic.data, a->generic.size) == 0;
+ return FIELD_EQUAL(a->generic, b->generic, data);
}
}
@@ -958,23 +967,47 @@ const char *dns_resource_record_to_string(DnsResourceRecord *rr) {
case DNS_TYPE_DNSKEY: {
_cleanup_free_ char *alg = NULL;
+ char *ss;
+ int n, n1;
+ uint16_t key_tag;
+
+ key_tag = dnssec_keytag(rr, true);
r = dnssec_algorithm_to_string_alloc(rr->dnskey.algorithm, &alg);
if (r < 0)
return NULL;
- t = base64mem(rr->dnskey.key, rr->dnskey.key_size);
- if (!t)
- return NULL;
-
- r = asprintf(&s, "%s %u %u %s %s",
+ r = asprintf(&s, "%s %n%u %u %s %n",
k,
+ &n1,
rr->dnskey.flags,
rr->dnskey.protocol,
alg,
- t);
+ &n);
+ if (r < 0)
+ return NULL;
+
+ r = base64_append(&s, n,
+ rr->dnskey.key, rr->dnskey.key_size,
+ 8, columns());
if (r < 0)
return NULL;
+
+ r = asprintf(&ss, "%s\n"
+ "%*s-- Flags:%s%s%s\n"
+ "%*s-- Key tag: %u",
+ s,
+ n1, "",
+ rr->dnskey.flags & DNSKEY_FLAG_SEP ? " SEP" : "",
+ rr->dnskey.flags & DNSKEY_FLAG_REVOKE ? " REVOKE" : "",
+ rr->dnskey.flags & DNSKEY_FLAG_ZONE_KEY ? " ZONE_KEY" : "",
+ n1, "",
+ key_tag);
+ if (r < 0)
+ return NULL;
+ free(s);
+ s = ss;
+
break;
}
@@ -982,6 +1015,7 @@ const char *dns_resource_record_to_string(DnsResourceRecord *rr) {
_cleanup_free_ char *alg = NULL;
char expiration[strlen("YYYYMMDDHHmmSS") + 1], inception[strlen("YYYYMMDDHHmmSS") + 1];
const char *type;
+ int n;
type = dns_type_to_string(rr->rrsig.type_covered);
@@ -989,10 +1023,6 @@ const char *dns_resource_record_to_string(DnsResourceRecord *rr) {
if (r < 0)
return NULL;
- t = base64mem(rr->rrsig.signature, rr->rrsig.signature_size);
- if (!t)
- return NULL;
-
r = format_timestamp_dns(expiration, sizeof(expiration), rr->rrsig.expiration);
if (r < 0)
return NULL;
@@ -1004,7 +1034,7 @@ const char *dns_resource_record_to_string(DnsResourceRecord *rr) {
/* TYPE?? follows
* http://tools.ietf.org/html/rfc3597#section-5 */
- r = asprintf(&s, "%s %s%.*u %s %u %u %s %s %u %s %s",
+ r = asprintf(&s, "%s %s%.*u %s %u %u %s %s %u %s %n",
k,
type ?: "TYPE",
type ? 0 : 1, type ? 0u : (unsigned) rr->rrsig.type_covered,
@@ -1015,9 +1045,16 @@ const char *dns_resource_record_to_string(DnsResourceRecord *rr) {
inception,
rr->rrsig.key_tag,
rr->rrsig.signer,
- t);
+ &n);
if (r < 0)
return NULL;
+
+ r = base64_append(&s, n,
+ rr->rrsig.signature, rr->rrsig.signature_size,
+ 8, columns());
+ if (r < 0)
+ return NULL;
+
break;
}
@@ -1065,13 +1102,70 @@ const char *dns_resource_record_to_string(DnsResourceRecord *rr) {
break;
}
+ case DNS_TYPE_TLSA: {
+ const char *cert_usage, *selector, *matching_type;
+ char *ss;
+ int n;
+
+ cert_usage = tlsa_cert_usage_to_string(rr->tlsa.cert_usage);
+ selector = tlsa_selector_to_string(rr->tlsa.selector);
+ matching_type = tlsa_matching_type_to_string(rr->tlsa.matching_type);
+
+ r = asprintf(&s, "%s %u %u %u %n",
+ k,
+ rr->tlsa.cert_usage,
+ rr->tlsa.selector,
+ rr->tlsa.matching_type,
+ &n);
+ if (r < 0)
+ return NULL;
+
+ r = base64_append(&s, n,
+ rr->tlsa.data, rr->tlsa.data_size,
+ 8, columns());
+ if (r < 0)
+ return NULL;
+
+ r = asprintf(&ss, "%s\n"
+ "%*s-- Cert. usage: %s\n"
+ "%*s-- Selector: %s\n"
+ "%*s-- Matching type: %s",
+ s,
+ n - 6, "", cert_usage,
+ n - 6, "", selector,
+ n - 6, "", matching_type);
+ if (r < 0)
+ return NULL;
+ free(s);
+ s = ss;
+
+ break;
+ }
+
+ case DNS_TYPE_OPENPGPKEY: {
+ int n;
+
+ r = asprintf(&s, "%s %n",
+ k,
+ &n);
+ if (r < 0)
+ return NULL;
+
+ r = base64_append(&s, n,
+ rr->generic.data, rr->generic.data_size,
+ 8, columns());
+ if (r < 0)
+ return NULL;
+ break;
+ }
+
default:
- t = hexmem(rr->generic.data, rr->generic.size);
+ t = hexmem(rr->generic.data, rr->generic.data_size);
if (!t)
return NULL;
/* Format as documented in RFC 3597, Section 5 */
- r = asprintf(&s, "%s \\# %zu %s", k, rr->generic.size, t);
+ r = asprintf(&s, "%s \\# %zu %s", k, rr->generic.data_size, t);
if (r < 0)
return NULL;
break;
@@ -1331,8 +1425,16 @@ static void dns_resource_record_hash_func(const void *i, struct siphash *state)
/* FIXME: We leave the bitmaps out */
break;
+ case DNS_TYPE_TLSA:
+ siphash24_compress(&rr->tlsa.cert_usage, sizeof(rr->tlsa.cert_usage), state);
+ siphash24_compress(&rr->tlsa.selector, sizeof(rr->tlsa.selector), state);
+ siphash24_compress(&rr->tlsa.matching_type, sizeof(rr->tlsa.matching_type), state);
+ siphash24_compress(&rr->tlsa.data, rr->tlsa.data_size, state);
+ break;
+
+ case DNS_TYPE_OPENPGPKEY:
default:
- siphash24_compress(rr->generic.data, rr->generic.size, state);
+ siphash24_compress(rr->generic.data, rr->generic.data_size, state);
break;
}
}
diff --git a/src/resolve/resolved-dns-rr.h b/src/resolve/resolved-dns-rr.h
index d9c31e81c5..37c4487332 100644
--- a/src/resolve/resolved-dns-rr.h
+++ b/src/resolve/resolved-dns-rr.h
@@ -129,7 +129,7 @@ struct DnsResourceRecord {
union {
struct {
void *data;
- size_t size;
+ size_t data_size;
} generic, opt;
struct {
@@ -242,6 +242,15 @@ struct DnsResourceRecord {
size_t next_hashed_name_size;
Bitmap *types;
} nsec3;
+
+ /* https://tools.ietf.org/html/draft-ietf-dane-protocol-23 */
+ struct {
+ uint8_t cert_usage;
+ uint8_t selector;
+ uint8_t matching_type;
+ void *data;
+ size_t data_size;
+ } tlsa;
};
};
diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c
index ac4887abea..03239794ee 100644
--- a/src/resolve/resolved-dns-scope.c
+++ b/src/resolve/resolved-dns-scope.c
@@ -490,7 +490,9 @@ DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, co
}
}
-int dns_scope_good_key(DnsScope *s, DnsResourceKey *key) {
+bool dns_scope_good_key(DnsScope *s, const DnsResourceKey *key) {
+ int key_family;
+
assert(s);
assert(key);
@@ -498,6 +500,9 @@ int dns_scope_good_key(DnsScope *s, DnsResourceKey *key) {
* this scope. Note that this call assumes as fully qualified
* name, i.e. the search suffixes already appended. */
+ if (key->class != DNS_CLASS_IN)
+ return false;
+
if (s->protocol == DNS_PROTOCOL_DNS) {
/* On classic DNS, looking up non-address RRs is always
@@ -519,13 +524,11 @@ int dns_scope_good_key(DnsScope *s, DnsResourceKey *key) {
/* On mDNS and LLMNR, send A and AAAA queries only on the
* respective scopes */
- if (s->family == AF_INET && key->class == DNS_CLASS_IN && key->type == DNS_TYPE_AAAA)
- return false;
-
- if (s->family == AF_INET6 && key->class == DNS_CLASS_IN && key->type == DNS_TYPE_A)
- return false;
+ key_family = dns_type_to_af(key->type);
+ if (key_family < 0)
+ return true;
- return true;
+ return key_family == s->family;
}
static int dns_scope_multicast_membership(DnsScope *s, bool b, struct in_addr in, struct in6_addr in6) {
@@ -1017,9 +1020,6 @@ bool dns_scope_name_needs_search_domain(DnsScope *s, const char *name) {
}
bool dns_scope_network_good(DnsScope *s) {
- Iterator i;
- Link *l;
-
/* Checks whether the network is in good state for lookups on this scope. For mDNS/LLMNR/Classic DNS scopes
* bound to links this is easy, as they don't even exist if the link isn't in a suitable state. For the global
* DNS scope we check whether there are any links that are up and have an address. */
@@ -1027,10 +1027,5 @@ bool dns_scope_network_good(DnsScope *s) {
if (s->link)
return true;
- HASHMAP_FOREACH(l, s->manager->links, i) {
- if (link_relevant(l, AF_UNSPEC, false))
- return true;
- }
-
- return false;
+ return manager_routable(s->manager, AF_UNSPEC);
}
diff --git a/src/resolve/resolved-dns-scope.h b/src/resolve/resolved-dns-scope.h
index f9b63d56d9..05b8d66de0 100644
--- a/src/resolve/resolved-dns-scope.h
+++ b/src/resolve/resolved-dns-scope.h
@@ -87,7 +87,7 @@ int dns_scope_socket_tcp(DnsScope *s, int family, const union in_addr_union *add
int dns_scope_socket_udp(DnsScope *s, DnsServer *server, uint16_t port);
DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, const char *domain);
-int dns_scope_good_key(DnsScope *s, DnsResourceKey *key);
+bool dns_scope_good_key(DnsScope *s, const DnsResourceKey *key);
DnsServer *dns_scope_get_dns_server(DnsScope *s);
void dns_scope_next_dns_server(DnsScope *s);
diff --git a/src/resolve/resolved-dns-server.c b/src/resolve/resolved-dns-server.c
index e1d2025863..43ec92f4f0 100644
--- a/src/resolve/resolved-dns-server.c
+++ b/src/resolve/resolved-dns-server.c
@@ -657,7 +657,9 @@ DnsServer *manager_set_dns_server(Manager *m, DnsServer *s) {
return s;
if (s)
- log_info("Switching to system DNS server %s.", dns_server_string(s));
+ log_info("Switching to %s DNS server %s.",
+ dns_server_type_to_string(s->type),
+ dns_server_string(s));
dns_server_unref(m->current_dns_server);
m->current_dns_server = dns_server_ref(s);
@@ -675,7 +677,7 @@ DnsServer *manager_get_dns_server(Manager *m) {
/* Try to read updates resolv.conf */
manager_read_resolv_conf(m);
- /* If no DNS server was chose so far, pick the first one */
+ /* If no DNS server was chosen so far, pick the first one */
if (!m->current_dns_server)
manager_set_dns_server(m, m->dns_servers);
@@ -723,6 +725,13 @@ void manager_next_dns_server(Manager *m) {
manager_set_dns_server(m, m->dns_servers);
}
+static const char* const dns_server_type_table[_DNS_SERVER_TYPE_MAX] = {
+ [DNS_SERVER_SYSTEM] = "system",
+ [DNS_SERVER_FALLBACK] = "fallback",
+ [DNS_SERVER_LINK] = "link",
+};
+DEFINE_STRING_TABLE_LOOKUP(dns_server_type, DnsServerType);
+
static const char* const dns_server_feature_level_table[_DNS_SERVER_FEATURE_LEVEL_MAX] = {
[DNS_SERVER_FEATURE_LEVEL_TCP] = "TCP",
[DNS_SERVER_FEATURE_LEVEL_UDP] = "UDP",
diff --git a/src/resolve/resolved-dns-server.h b/src/resolve/resolved-dns-server.h
index 2a3c921678..7a885655a4 100644
--- a/src/resolve/resolved-dns-server.h
+++ b/src/resolve/resolved-dns-server.h
@@ -30,6 +30,10 @@ typedef enum DnsServerType {
DNS_SERVER_FALLBACK,
DNS_SERVER_LINK,
} DnsServerType;
+#define _DNS_SERVER_TYPE_MAX (DNS_SERVER_LINK + 1)
+
+const char* dns_server_type_to_string(DnsServerType i) _const_;
+DnsServerType dns_server_type_from_string(const char *s) _pure_;
typedef enum DnsServerFeatureLevel {
DNS_SERVER_FEATURE_LEVEL_TCP,
diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c
index 802ad860a4..501f13063e 100644
--- a/src/resolve/resolved-dns-transaction.c
+++ b/src/resolve/resolved-dns-transaction.c
@@ -1411,12 +1411,6 @@ static int dns_transaction_make_packet(DnsTransaction *t) {
if (r < 0)
return r;
- r = dns_scope_good_key(t->scope, t->key);
- if (r < 0)
- return r;
- if (r == 0)
- return -EDOM;
-
r = dns_packet_append_key(p, t->key, NULL);
if (r < 0)
return r;
@@ -1498,13 +1492,6 @@ int dns_transaction_go(DnsTransaction *t) {
/* Otherwise, we need to ask the network */
r = dns_transaction_make_packet(t);
- if (r == -EDOM) {
- /* Not the right request to make on this network?
- * (i.e. an A request made on IPv6 or an AAAA request
- * made on IPv4, on LLMNR or mDNS.) */
- dns_transaction_complete(t, DNS_TRANSACTION_NO_SERVERS);
- return 0;
- }
if (r < 0)
return r;
@@ -1538,7 +1525,7 @@ int dns_transaction_go(DnsTransaction *t) {
return 0;
}
if (t->scope->protocol == DNS_PROTOCOL_LLMNR && ERRNO_IS_DISCONNECT(-r)) {
- /* On LLMNR, if we cannot connect to a host via TCP when doing revers lookups. This means we cannot
+ /* On LLMNR, if we cannot connect to a host via TCP when doing reverse lookups. This means we cannot
* answer this request with this protocol. */
dns_transaction_complete(t, DNS_TRANSACTION_NOT_FOUND);
return 0;
diff --git a/src/resolve/resolved-link.c b/src/resolve/resolved-link.c
index 37dd4a6e78..7412e64622 100644
--- a/src/resolve/resolved-link.c
+++ b/src/resolve/resolved-link.c
@@ -515,14 +515,17 @@ int link_update_monitor(Link *l) {
return 0;
}
-bool link_relevant(Link *l, int family, bool multicast) {
+bool link_relevant(Link *l, int family, bool local_multicast) {
_cleanup_free_ char *state = NULL;
LinkAddress *a;
assert(l);
- /* A link is relevant for multicast traffic if it isn't a loopback or pointopoint device, has a link beat, can
- * do multicast and has at least one relevant IP address */
+ /* A link is relevant for local multicast traffic if it isn't a loopback or pointopoint device, has a link
+ * beat, can do multicast and has at least one link-local (or better) IP address.
+ *
+ * A link is relevant for non-multicast traffic if it isn't a loopback device, has a link beat, and has at
+ * least one routable address.*/
if (l->flags & (IFF_LOOPBACK|IFF_DORMANT))
return false;
@@ -530,7 +533,7 @@ bool link_relevant(Link *l, int family, bool multicast) {
if ((l->flags & (IFF_UP|IFF_LOWER_UP)) != (IFF_UP|IFF_LOWER_UP))
return false;
- if (multicast) {
+ if (local_multicast) {
if (l->flags & IFF_POINTOPOINT)
return false;
@@ -548,7 +551,7 @@ bool link_relevant(Link *l, int family, bool multicast) {
return false;
LIST_FOREACH(addresses, a, l->addresses)
- if ((family == AF_UNSPEC || a->family == family) && link_address_relevant(a))
+ if ((family == AF_UNSPEC || a->family == family) && link_address_relevant(a, local_multicast))
return true;
return false;
@@ -692,7 +695,7 @@ void link_address_add_rrs(LinkAddress *a, bool force_remove) {
if (a->family == AF_INET) {
if (!force_remove &&
- link_address_relevant(a) &&
+ link_address_relevant(a, true) &&
a->link->llmnr_ipv4_scope &&
a->link->llmnr_support == RESOLVE_SUPPORT_YES &&
a->link->manager->llmnr_support == RESOLVE_SUPPORT_YES) {
@@ -749,7 +752,7 @@ void link_address_add_rrs(LinkAddress *a, bool force_remove) {
if (a->family == AF_INET6) {
if (!force_remove &&
- link_address_relevant(a) &&
+ link_address_relevant(a, true) &&
a->link->llmnr_ipv6_scope &&
a->link->llmnr_support == RESOLVE_SUPPORT_YES &&
a->link->manager->llmnr_support == RESOLVE_SUPPORT_YES) {
@@ -826,13 +829,13 @@ int link_address_update_rtnl(LinkAddress *a, sd_netlink_message *m) {
return 0;
}
-bool link_address_relevant(LinkAddress *a) {
+bool link_address_relevant(LinkAddress *a, bool local_multicast) {
assert(a);
if (a->flags & (IFA_F_DEPRECATED|IFA_F_TENTATIVE))
return false;
- if (IN_SET(a->scope, RT_SCOPE_HOST, RT_SCOPE_NOWHERE))
+ if (a->scope >= (local_multicast ? RT_SCOPE_HOST : RT_SCOPE_LINK))
return false;
return true;
diff --git a/src/resolve/resolved-link.h b/src/resolve/resolved-link.h
index 3b6aafb8f0..29e7b72247 100644
--- a/src/resolve/resolved-link.h
+++ b/src/resolve/resolved-link.h
@@ -89,7 +89,7 @@ int link_new(Manager *m, Link **ret, int ifindex);
Link *link_free(Link *l);
int link_update_rtnl(Link *l, sd_netlink_message *m);
int link_update_monitor(Link *l);
-bool link_relevant(Link *l, int family, bool multicast);
+bool link_relevant(Link *l, int family, bool local_multicast);
LinkAddress* link_find_address(Link *l, int family, const union in_addr_union *in_addr);
void link_add_rrs(Link *l, bool force_remove);
@@ -107,7 +107,7 @@ bool link_dnssec_supported(Link *l);
int link_address_new(Link *l, LinkAddress **ret, int family, const union in_addr_union *in_addr);
LinkAddress *link_address_free(LinkAddress *a);
int link_address_update_rtnl(LinkAddress *a, sd_netlink_message *m);
-bool link_address_relevant(LinkAddress *l);
+bool link_address_relevant(LinkAddress *l, bool local_multicast);
void link_address_add_rrs(LinkAddress *a, bool force_remove);
DEFINE_TRIVIAL_CLEANUP_FUNC(Link*, link_free);
diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c
index 4306403834..f1dbda1a6a 100644
--- a/src/resolve/resolved-manager.c
+++ b/src/resolve/resolved-manager.c
@@ -288,7 +288,7 @@ static int on_network_event(sd_event_source *s, int fd, uint32_t revents, void *
r = manager_write_resolv_conf(m);
if (r < 0)
- log_warning_errno(r, "Could not update resolv.conf: %m");
+ log_warning_errno(r, "Could not update "PRIVATE_RESOLV_CONF": %m");
return 0;
}
@@ -1226,3 +1226,18 @@ void manager_dnssec_verdict(Manager *m, DnssecVerdict verdict, const DnsResource
m->n_dnssec_verdict[verdict]++;
}
+
+bool manager_routable(Manager *m, int family) {
+ Iterator i;
+ Link *l;
+
+ assert(m);
+
+ /* Returns true if the host has at least one interface with a routable address of the specified type */
+
+ HASHMAP_FOREACH(l, m->links, i)
+ if (link_relevant(l, family, false))
+ return true;
+
+ return false;
+}
diff --git a/src/resolve/resolved-manager.h b/src/resolve/resolved-manager.h
index 1af49c8fb9..e2c539d3d2 100644
--- a/src/resolve/resolved-manager.h
+++ b/src/resolve/resolved-manager.h
@@ -169,3 +169,5 @@ DnssecMode manager_get_dnssec_mode(Manager *m);
bool manager_dnssec_supported(Manager *m);
void manager_dnssec_verdict(Manager *m, DnssecVerdict verdict, const DnsResourceKey *key);
+
+bool manager_routable(Manager *m, int family);
diff --git a/src/resolve/resolved-resolv-conf.c b/src/resolve/resolved-resolv-conf.c
index 7567f4c369..c5ce9c4f01 100644
--- a/src/resolve/resolved-resolv-conf.c
+++ b/src/resolve/resolved-resolv-conf.c
@@ -226,8 +226,6 @@ static int write_resolv_conf_contents(FILE *f, OrderedSet *dns, OrderedSet *doma
int manager_write_resolv_conf(Manager *m) {
- #define PRIVATE_RESOLV_CONF "/run/systemd/resolve/resolv.conf"
-
_cleanup_ordered_set_free_ OrderedSet *dns = NULL, *domains = NULL;
_cleanup_free_ char *temp_path = NULL;
_cleanup_fclose_ FILE *f = NULL;
diff --git a/src/resolve/resolved-resolv-conf.h b/src/resolve/resolved-resolv-conf.h
index a3355e994b..7081563965 100644
--- a/src/resolve/resolved-resolv-conf.h
+++ b/src/resolve/resolved-resolv-conf.h
@@ -23,5 +23,7 @@
#include "resolved-manager.h"
+#define PRIVATE_RESOLV_CONF "/run/systemd/resolve/resolv.conf"
+
int manager_read_resolv_conf(Manager *m);
int manager_write_resolv_conf(Manager *m);
diff --git a/src/resolve/resolved.c b/src/resolve/resolved.c
index 472bb32764..eee52da882 100644
--- a/src/resolve/resolved.c
+++ b/src/resolve/resolved.c
@@ -91,7 +91,7 @@ int main(int argc, char *argv[]) {
* symlink */
r = manager_write_resolv_conf(m);
if (r < 0)
- log_warning_errno(r, "Could not create resolv.conf: %m");
+ log_warning_errno(r, "Could not create "PRIVATE_RESOLV_CONF": %m");
sd_notify(false,
"READY=1\n"
diff --git a/src/resolve/test-dnssec-complex.c b/src/resolve/test-dnssec-complex.c
index caac251e83..cde9741866 100644
--- a/src/resolve/test-dnssec-complex.c
+++ b/src/resolve/test-dnssec-complex.c
@@ -220,7 +220,7 @@ int main(int argc, char* argv[]) {
test_hostname_lookup(bus, "poettering.de", AF_INET, NULL);
test_hostname_lookup(bus, "poettering.de", AF_INET6, NULL);
-#if HAVE_LIBIDN
+#ifdef HAVE_LIBIDN
/* Unsigned A with IDNA conversion necessary */
test_hostname_lookup(bus, "pöttering.de", AF_UNSPEC, NULL);
test_hostname_lookup(bus, "pöttering.de", AF_INET, NULL);
diff --git a/src/resolve/test-resolve-tables.c b/src/resolve/test-resolve-tables.c
new file mode 100644
index 0000000000..63660afc87
--- /dev/null
+++ b/src/resolve/test-resolve-tables.c
@@ -0,0 +1,27 @@
+/***
+ This file is part of systemd
+
+ Copyright 2013 Zbigniew Jędrzejewski-Szmek
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "dns-type.h"
+#include "test-tables.h"
+
+int main(int argc, char **argv) {
+ test_table_sparse(dns_type, DNS_TYPE);
+
+ return EXIT_SUCCESS;
+}
diff --git a/src/run/run.c b/src/run/run.c
index 92a1d5373c..080c15466c 100644
--- a/src/run/run.c
+++ b/src/run/run.c
@@ -422,17 +422,9 @@ static int transient_unit_set_properties(sd_bus_message *m, char **properties) {
return r;
STRV_FOREACH(i, properties) {
- r = sd_bus_message_open_container(m, 'r', "sv");
- if (r < 0)
- return r;
-
r = bus_append_unit_property_assignment(m, *i);
if (r < 0)
return r;
-
- r = sd_bus_message_close_container(m);
- if (r < 0)
- return r;
}
return 0;
diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c
index b9a8ee4074..6df73c560a 100644
--- a/src/shared/bus-util.c
+++ b/src/shared/bus-util.c
@@ -1398,7 +1398,7 @@ int bus_parse_unit_info(sd_bus_message *message, UnitInfo *u) {
int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignment) {
const char *eq, *field;
- int r;
+ int r, rl;
assert(m);
assert(assignment);
@@ -1409,20 +1409,18 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
return -EINVAL;
}
+ r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
+ if (r < 0)
+ return bus_log_create_error(r);
+
field = strndupa(assignment, eq - assignment);
eq ++;
if (streq(field, "CPUQuota")) {
- if (isempty(eq)) {
-
- r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, "CPUQuotaPerSecUSec");
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_message_append(m, "v", "t", USEC_INFINITY);
-
- } else if (endswith(eq, "%")) {
+ if (isempty(eq))
+ r = sd_bus_message_append(m, "sv", "CPUQuotaPerSecUSec", "t", USEC_INFINITY);
+ else if (endswith(eq, "%")) {
double percent;
if (sscanf(eq, "%lf%%", &percent) != 1 || percent <= 0) {
@@ -1430,58 +1428,69 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
return -EINVAL;
}
- r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, "CPUQuotaPerSecUSec");
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_message_append(m, "v", "t", (usec_t) percent * USEC_PER_SEC / 100);
+ r = sd_bus_message_append(m, "sv", "CPUQuotaPerSecUSec", "t", (usec_t) percent * USEC_PER_SEC / 100);
} else {
log_error("CPU quota needs to be in percent.");
return -EINVAL;
}
- if (r < 0)
- return bus_log_create_error(r);
-
- return 0;
+ goto finish;
} else if (streq(field, "EnvironmentFile")) {
- r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, "EnvironmentFiles");
- if (r < 0)
- return bus_log_create_error(r);
-
- r = sd_bus_message_append(m, "v", "a(sb)", 1,
+ r = sd_bus_message_append(m, "sv", "EnvironmentFiles", "a(sb)", 1,
eq[0] == '-' ? eq + 1 : eq,
eq[0] == '-');
+ goto finish;
+
+ } else if (STR_IN_SET(field, "AccuracySec", "RandomizedDelaySec", "RuntimeMaxSec")) {
+ char *n;
+ usec_t t;
+ size_t l;
+ r = parse_sec(eq, &t);
if (r < 0)
- return bus_log_create_error(r);
+ return log_error_errno(r, "Failed to parse %s= parameter: %s", field, eq);
- return 0;
+ l = strlen(field);
+ n = newa(char, l + 2);
+ if (!n)
+ return log_oom();
- } else if (streq(field, "RandomizedDelaySec")) {
- usec_t t;
+ /* Change suffix Sec → USec */
+ strcpy(mempcpy(n, field, l - 3), "USec");
+ r = sd_bus_message_append(m, "sv", n, "t", t);
+ goto finish;
+ }
- r = parse_sec(eq, &t);
+ r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ rl = rlimit_from_string(field);
+ if (rl >= 0) {
+ const char *sn;
+ struct rlimit l;
+
+ r = rlimit_parse(rl, eq, &l);
if (r < 0)
- return log_error_errno(r, "Failed to parse RandomizedDelaySec= parameter: %s", eq);
+ return log_error_errno(r, "Failed to parse resource limit: %s", eq);
- r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, "RandomizedDelayUSec");
+ r = sd_bus_message_append(m, "v", "t", l.rlim_max);
if (r < 0)
return bus_log_create_error(r);
- r = sd_bus_message_append(m, "v", "t", t);
+ r = sd_bus_message_close_container(m);
if (r < 0)
return bus_log_create_error(r);
- return 0;
- }
+ r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
+ if (r < 0)
+ return bus_log_create_error(r);
- r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
- if (r < 0)
- return bus_log_create_error(r);
+ sn = strjoina(field, "Soft");
+ r = sd_bus_message_append(m, "sv", sn, "t", l.rlim_cur);
- if (STR_IN_SET(field,
+ } else if (STR_IN_SET(field,
"CPUAccounting", "MemoryAccounting", "BlockIOAccounting", "TasksAccounting",
"SendSIGHUP", "SendSIGKILL", "WakeSystem", "DefaultDependencies",
"IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "RemainAfterExit",
@@ -1662,21 +1671,6 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
r = sd_bus_message_append(m, "v", "a(st)", path, u);
}
- } else if (rlimit_from_string(field) >= 0) {
- uint64_t rl;
-
- if (streq(eq, "infinity"))
- rl = (uint64_t) -1;
- else {
- r = safe_atou64(eq, &rl);
- if (r < 0) {
- log_error("Invalid resource limit: %s", eq);
- return -EINVAL;
- }
- }
-
- r = sd_bus_message_append(m, "v", "t", rl);
-
} else if (streq(field, "Nice")) {
int32_t i;
@@ -1746,16 +1740,6 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
r = sd_bus_message_append(m, "v", "i", sig);
- } else if (streq(field, "AccuracySec")) {
- usec_t u;
-
- r = parse_sec(eq, &u);
- if (r < 0) {
- log_error("Failed to parse %s value %s", field, eq);
- return -EINVAL;
- }
-
- r = sd_bus_message_append(m, "v", "t", u);
} else if (streq(field, "TimerSlackNSec")) {
nsec_t n;
@@ -1869,6 +1853,11 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
return -EINVAL;
}
+finish:
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_close_container(m);
if (r < 0)
return bus_log_create_error(r);
@@ -2100,7 +2089,7 @@ static int check_wait_response(BusWaitForJobs *d, bool quiet, const char *extra_
else if (streq(d->result, "dependency"))
log_error("A dependency job for %s failed. See 'journalctl -xe' for details.", strna(d->name));
else if (streq(d->result, "invalid"))
- log_error("Job for %s invalid.", strna(d->name));
+ log_error("%s is not active, cannot reload.", strna(d->name));
else if (streq(d->result, "assert"))
log_error("Assertion failed on job for %s.", strna(d->name));
else if (streq(d->result, "unsupported"))
diff --git a/src/shared/logs-show.c b/src/shared/logs-show.c
index a1f65d1a88..111f0225d9 100644
--- a/src/shared/logs-show.c
+++ b/src/shared/logs-show.c
@@ -82,12 +82,11 @@ static int print_catalog(FILE *f, sd_journal *j) {
static int parse_field(const void *data, size_t length, const char *field, char **target, size_t *target_size) {
size_t fl, nl;
- void *buf;
+ char *buf;
assert(data);
assert(field);
assert(target);
- assert(target_size);
fl = strlen(field);
if (length < fl)
@@ -97,16 +96,18 @@ static int parse_field(const void *data, size_t length, const char *field, char
return 0;
nl = length - fl;
- buf = malloc(nl+1);
+ buf = new(char, nl+1);
if (!buf)
return log_oom();
memcpy(buf, (const char*) data + fl, nl);
- ((char*)buf)[nl] = 0;
+ buf[nl] = 0;
free(*target);
*target = buf;
- *target_size = nl;
+
+ if (target_size)
+ *target_size = nl;
return 1;
}
@@ -415,7 +416,7 @@ static int output_verbose(
const void *data;
size_t length;
_cleanup_free_ char *cursor = NULL;
- uint64_t realtime;
+ uint64_t realtime = 0;
char ts[FORMAT_TIMESTAMP_MAX + 7];
int r;
@@ -431,17 +432,15 @@ static int output_verbose(
return log_full_errno(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_ERR, r, "Failed to get source realtime timestamp: %m");
else {
_cleanup_free_ char *value = NULL;
- size_t size;
- r = parse_field(data, length, "_SOURCE_REALTIME_TIMESTAMP=", &value, &size);
+ r = parse_field(data, length, "_SOURCE_REALTIME_TIMESTAMP=", &value, NULL);
if (r < 0)
return r;
- else {
- assert(r > 0);
- r = safe_atou64(value, &realtime);
- if (r < 0)
- log_debug_errno(r, "Failed to parse realtime timestamp: %m");
- }
+ assert(r > 0);
+
+ r = safe_atou64(value, &realtime);
+ if (r < 0)
+ log_debug_errno(r, "Failed to parse realtime timestamp: %m");
}
if (r < 0) {
diff --git a/src/shared/test-tables.h b/src/shared/test-tables.h
index 74f1716fe0..228e510104 100644
--- a/src/shared/test-tables.h
+++ b/src/shared/test-tables.h
@@ -28,18 +28,25 @@ static inline void _test_table(const char *name,
reverse_t reverse,
int size,
bool sparse) {
- int i;
+ int i, boring = 0;
for (i = -1; i < size + 1; i++) {
const char* val = lookup(i);
int rev;
- if (val)
+ if (val) {
rev = reverse(val);
- else
+ boring = 0;
+ } else {
rev = reverse("--no-such--value----");
+ boring += i >= 0;
+ }
+
+ if (boring < 1 || i == size)
+ printf("%s: %d → %s → %d\n", name, i, val, rev);
+ else if (boring == 1)
+ printf("%*s ...\n", (int) strlen(name), "");
- printf("%s: %d → %s → %d\n", name, i, val, rev);
assert_se(!(i >= 0 && i < size ?
sparse ? rev != i && rev != -1 : val == NULL || rev != i :
val != NULL || rev != -1));
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index 73f5710b9c..536beb28ce 100644
--- a/src/systemctl/systemctl.c
+++ b/src/systemctl/systemctl.c
@@ -2410,19 +2410,16 @@ static int unit_find_paths(
}
static int check_one_unit(sd_bus *bus, const char *name, const char *good_states, bool quiet) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
- _cleanup_free_ char *n = NULL, *state = NULL;
- const char *path;
+ _cleanup_free_ char *buf = NULL;
+ const char *path, *state;
int r;
assert(name);
- r = unit_name_mangle(name, UNIT_NAME_NOGLOB, &n);
- if (r < 0)
- return log_error_errno(r, "Failed to mangle unit name: %m");
-
- /* We don't use unit_dbus_path_from_name() directly since we
- * don't want to load the unit if it isn't loaded. */
+ /* We don't use unit_dbus_path_from_name() directly since we don't want to load the unit unnecessarily, if it
+ * isn't loaded. */
r = sd_bus_call_method(
bus,
@@ -2430,31 +2427,34 @@ static int check_one_unit(sd_bus *bus, const char *name, const char *good_states
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"GetUnit",
- NULL,
+ &error,
&reply,
- "s", n);
+ "s", name);
if (r < 0) {
- if (!quiet)
- puts("unknown");
- return 0;
- }
+ if (!sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_UNIT))
+ return log_error_errno(r, "Failed to retrieve unit: %s", bus_error_message(&error, r));
- r = sd_bus_message_read(reply, "o", &path);
- if (r < 0)
- return bus_log_parse_error(r);
+ /* The unit is currently not loaded, hence say it's "inactive", since all units that aren't loaded are
+ * considered inactive. */
+ state = "inactive";
- r = sd_bus_get_property_string(
- bus,
- "org.freedesktop.systemd1",
- path,
- "org.freedesktop.systemd1.Unit",
- "ActiveState",
- NULL,
- &state);
- if (r < 0) {
- if (!quiet)
- puts("unknown");
- return 0;
+ } else {
+ r = sd_bus_message_read(reply, "o", &path);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = sd_bus_get_property_string(
+ bus,
+ "org.freedesktop.systemd1",
+ path,
+ "org.freedesktop.systemd1.Unit",
+ "ActiveState",
+ &error,
+ &buf);
+ if (r < 0)
+ return log_error_errno(r, "Failed to retrieve unit state: %s", bus_error_message(&error, r));
+
+ state = buf;
}
if (!quiet)
@@ -2538,6 +2538,7 @@ static const struct {
{ "try-restart", "TryRestartUnit" },
{ "condrestart", "TryRestartUnit" },
{ "reload-or-restart", "ReloadOrRestartUnit" },
+ { "try-reload-or-restart", "ReloadOrTryRestartUnit" },
{ "reload-or-try-restart", "ReloadOrTryRestartUnit" },
{ "condreload", "ReloadOrTryRestartUnit" },
{ "force-reload", "ReloadOrTryRestartUnit" }
@@ -4872,17 +4873,9 @@ static int set_property(int argc, char *argv[], void *userdata) {
return bus_log_create_error(r);
STRV_FOREACH(i, strv_skip(argv, 2)) {
- r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
- if (r < 0)
- return bus_log_create_error(r);
-
r = bus_append_unit_property_assignment(m, *i);
if (r < 0)
return r;
-
- r = sd_bus_message_close_container(m);
- if (r < 0)
- return bus_log_create_error(r);
}
r = sd_bus_message_close_container(m);
@@ -5791,7 +5784,7 @@ static int is_system_running(int argc, char *argv[], void *userdata) {
sd_bus *bus;
int r;
- if (arg_transport == BUS_TRANSPORT_LOCAL && !sd_booted()) {
+ if (running_in_chroot() > 0 || (arg_transport == BUS_TRANSPORT_LOCAL && !sd_booted())) {
if (!arg_quiet)
puts("offline");
return EXIT_FAILURE;
@@ -6234,8 +6227,8 @@ static void systemctl_help(void) {
" try-restart NAME... Restart one or more units if active\n"
" reload-or-restart NAME... Reload one or more units if possible,\n"
" otherwise start or restart\n"
- " reload-or-try-restart NAME... Reload one or more units if possible,\n"
- " otherwise restart if active\n"
+ " try-reload-or-restart NAME... If active, reload one or more units,\n"
+ " if supported, otherwise restart\n"
" isolate NAME Start one unit and stop all others\n"
" kill NAME... Send signal to processes of a unit\n"
" is-active PATTERN... Check whether units are active\n"
@@ -7324,70 +7317,71 @@ static int talk_initctl(void) {
static int systemctl_main(int argc, char *argv[]) {
static const Verb verbs[] = {
- { "list-units", VERB_ANY, VERB_ANY, VERB_DEFAULT, list_units },
- { "list-unit-files", VERB_ANY, VERB_ANY, 0, list_unit_files },
- { "list-sockets", VERB_ANY, VERB_ANY, 0, list_sockets },
- { "list-timers", VERB_ANY, VERB_ANY, 0, list_timers },
- { "list-jobs", VERB_ANY, VERB_ANY, 0, list_jobs },
- { "list-machines", VERB_ANY, VERB_ANY, 0, list_machines },
- { "clear-jobs", VERB_ANY, 1, 0, daemon_reload },
- { "cancel", VERB_ANY, VERB_ANY, 0, cancel_job },
- { "start", 2, VERB_ANY, 0, start_unit },
- { "stop", 2, VERB_ANY, 0, start_unit },
- { "condstop", 2, VERB_ANY, 0, start_unit }, /* For compatibility with ALTLinux */
- { "reload", 2, VERB_ANY, 0, start_unit },
- { "restart", 2, VERB_ANY, 0, start_unit },
- { "try-restart", 2, VERB_ANY, 0, start_unit },
- { "reload-or-restart", 2, VERB_ANY, 0, start_unit },
- { "reload-or-try-restart", 2, VERB_ANY, 0, start_unit },
- { "force-reload", 2, VERB_ANY, 0, start_unit }, /* For compatibility with SysV */
- { "condreload", 2, VERB_ANY, 0, start_unit }, /* For compatibility with ALTLinux */
- { "condrestart", 2, VERB_ANY, 0, start_unit }, /* For compatibility with RH */
- { "isolate", 2, 2, 0, start_unit },
- { "kill", 2, VERB_ANY, 0, kill_unit },
- { "is-active", 2, VERB_ANY, 0, check_unit_active },
- { "check", 2, VERB_ANY, 0, check_unit_active },
- { "is-failed", 2, VERB_ANY, 0, check_unit_failed },
- { "show", VERB_ANY, VERB_ANY, 0, show },
- { "cat", 2, VERB_ANY, 0, cat },
- { "status", VERB_ANY, VERB_ANY, 0, show },
- { "help", VERB_ANY, VERB_ANY, 0, show },
- { "daemon-reload", VERB_ANY, 1, 0, daemon_reload },
- { "daemon-reexec", VERB_ANY, 1, 0, daemon_reload },
- { "show-environment", VERB_ANY, 1, 0, show_environment },
- { "set-environment", 2, VERB_ANY, 0, set_environment },
- { "unset-environment", 2, VERB_ANY, 0, set_environment },
- { "import-environment", VERB_ANY, VERB_ANY, 0, import_environment},
- { "halt", VERB_ANY, 1, 0, start_special },
- { "poweroff", VERB_ANY, 1, 0, start_special },
- { "reboot", VERB_ANY, 2, 0, start_special },
- { "kexec", VERB_ANY, 1, 0, start_special },
- { "suspend", VERB_ANY, 1, 0, start_special },
- { "hibernate", VERB_ANY, 1, 0, start_special },
- { "hybrid-sleep", VERB_ANY, 1, 0, start_special },
- { "default", VERB_ANY, 1, 0, start_special },
- { "rescue", VERB_ANY, 1, 0, start_special },
- { "emergency", VERB_ANY, 1, 0, start_special },
- { "exit", VERB_ANY, 2, 0, start_special },
- { "reset-failed", VERB_ANY, VERB_ANY, 0, reset_failed },
- { "enable", 2, VERB_ANY, 0, enable_unit },
- { "disable", 2, VERB_ANY, 0, enable_unit },
- { "is-enabled", 2, VERB_ANY, 0, unit_is_enabled },
- { "reenable", 2, VERB_ANY, 0, enable_unit },
- { "preset", 2, VERB_ANY, 0, enable_unit },
- { "preset-all", VERB_ANY, 1, 0, preset_all },
- { "mask", 2, VERB_ANY, 0, enable_unit },
- { "unmask", 2, VERB_ANY, 0, enable_unit },
- { "link", 2, VERB_ANY, 0, enable_unit },
- { "switch-root", 2, VERB_ANY, 0, switch_root },
- { "list-dependencies", VERB_ANY, 2, 0, list_dependencies },
- { "set-default", 2, 2, 0, set_default },
- { "get-default", VERB_ANY, 1, 0, get_default, },
- { "set-property", 3, VERB_ANY, 0, set_property },
- { "is-system-running", VERB_ANY, 1, 0, is_system_running },
- { "add-wants", 3, VERB_ANY, 0, add_dependency },
- { "add-requires", 3, VERB_ANY, 0, add_dependency },
- { "edit", 2, VERB_ANY, 0, edit },
+ { "list-units", VERB_ANY, VERB_ANY, VERB_DEFAULT|VERB_NOCHROOT, list_units },
+ { "list-unit-files", VERB_ANY, VERB_ANY, 0, list_unit_files },
+ { "list-sockets", VERB_ANY, VERB_ANY, VERB_NOCHROOT, list_sockets },
+ { "list-timers", VERB_ANY, VERB_ANY, VERB_NOCHROOT, list_timers },
+ { "list-jobs", VERB_ANY, VERB_ANY, VERB_NOCHROOT, list_jobs },
+ { "list-machines", VERB_ANY, VERB_ANY, VERB_NOCHROOT, list_machines },
+ { "clear-jobs", VERB_ANY, 1, VERB_NOCHROOT, daemon_reload },
+ { "cancel", VERB_ANY, VERB_ANY, VERB_NOCHROOT, cancel_job },
+ { "start", 2, VERB_ANY, VERB_NOCHROOT, start_unit },
+ { "stop", 2, VERB_ANY, VERB_NOCHROOT, start_unit },
+ { "condstop", 2, VERB_ANY, VERB_NOCHROOT, start_unit }, /* For compatibility with ALTLinux */
+ { "reload", 2, VERB_ANY, VERB_NOCHROOT, start_unit },
+ { "restart", 2, VERB_ANY, VERB_NOCHROOT, start_unit },
+ { "try-restart", 2, VERB_ANY, VERB_NOCHROOT, start_unit },
+ { "reload-or-restart", 2, VERB_ANY, VERB_NOCHROOT, start_unit },
+ { "reload-or-try-restart", 2, VERB_ANY, VERB_NOCHROOT, start_unit }, /* For compatbility with old systemctl <= 228 */
+ { "try-reload-or-restart", 2, VERB_ANY, VERB_NOCHROOT, start_unit },
+ { "force-reload", 2, VERB_ANY, VERB_NOCHROOT, start_unit }, /* For compatibility with SysV */
+ { "condreload", 2, VERB_ANY, VERB_NOCHROOT, start_unit }, /* For compatibility with ALTLinux */
+ { "condrestart", 2, VERB_ANY, VERB_NOCHROOT, start_unit }, /* For compatibility with RH */
+ { "isolate", 2, 2, VERB_NOCHROOT, start_unit },
+ { "kill", 2, VERB_ANY, VERB_NOCHROOT, kill_unit },
+ { "is-active", 2, VERB_ANY, VERB_NOCHROOT, check_unit_active },
+ { "check", 2, VERB_ANY, VERB_NOCHROOT, check_unit_active },
+ { "is-failed", 2, VERB_ANY, VERB_NOCHROOT, check_unit_failed },
+ { "show", VERB_ANY, VERB_ANY, VERB_NOCHROOT, show },
+ { "cat", 2, VERB_ANY, VERB_NOCHROOT, cat },
+ { "status", VERB_ANY, VERB_ANY, VERB_NOCHROOT, show },
+ { "help", VERB_ANY, VERB_ANY, VERB_NOCHROOT, show },
+ { "daemon-reload", VERB_ANY, 1, VERB_NOCHROOT, daemon_reload },
+ { "daemon-reexec", VERB_ANY, 1, VERB_NOCHROOT, daemon_reload },
+ { "show-environment", VERB_ANY, 1, VERB_NOCHROOT, show_environment },
+ { "set-environment", 2, VERB_ANY, VERB_NOCHROOT, set_environment },
+ { "unset-environment", 2, VERB_ANY, VERB_NOCHROOT, set_environment },
+ { "import-environment", VERB_ANY, VERB_ANY, VERB_NOCHROOT, import_environment},
+ { "halt", VERB_ANY, 1, VERB_NOCHROOT, start_special },
+ { "poweroff", VERB_ANY, 1, VERB_NOCHROOT, start_special },
+ { "reboot", VERB_ANY, 2, VERB_NOCHROOT, start_special },
+ { "kexec", VERB_ANY, 1, VERB_NOCHROOT, start_special },
+ { "suspend", VERB_ANY, 1, VERB_NOCHROOT, start_special },
+ { "hibernate", VERB_ANY, 1, VERB_NOCHROOT, start_special },
+ { "hybrid-sleep", VERB_ANY, 1, VERB_NOCHROOT, start_special },
+ { "default", VERB_ANY, 1, VERB_NOCHROOT, start_special },
+ { "rescue", VERB_ANY, 1, VERB_NOCHROOT, start_special },
+ { "emergency", VERB_ANY, 1, VERB_NOCHROOT, start_special },
+ { "exit", VERB_ANY, 2, VERB_NOCHROOT, start_special },
+ { "reset-failed", VERB_ANY, VERB_ANY, VERB_NOCHROOT, reset_failed },
+ { "enable", 2, VERB_ANY, 0, enable_unit },
+ { "disable", 2, VERB_ANY, 0, enable_unit },
+ { "is-enabled", 2, VERB_ANY, 0, unit_is_enabled },
+ { "reenable", 2, VERB_ANY, 0, enable_unit },
+ { "preset", 2, VERB_ANY, 0, enable_unit },
+ { "preset-all", VERB_ANY, 1, 0, preset_all },
+ { "mask", 2, VERB_ANY, 0, enable_unit },
+ { "unmask", 2, VERB_ANY, 0, enable_unit },
+ { "link", 2, VERB_ANY, 0, enable_unit },
+ { "switch-root", 2, VERB_ANY, VERB_NOCHROOT, switch_root },
+ { "list-dependencies", VERB_ANY, 2, VERB_NOCHROOT, list_dependencies },
+ { "set-default", 2, 2, 0, set_default },
+ { "get-default", VERB_ANY, 1, 0, get_default, },
+ { "set-property", 3, VERB_ANY, VERB_NOCHROOT, set_property },
+ { "is-system-running", VERB_ANY, 1, 0, is_system_running },
+ { "add-wants", 3, VERB_ANY, 0, add_dependency },
+ { "add-requires", 3, VERB_ANY, 0, add_dependency },
+ { "edit", 2, VERB_ANY, VERB_NOCHROOT, edit },
{}
};
@@ -7641,7 +7635,7 @@ int main(int argc, char*argv[]) {
if (r <= 0)
goto finish;
- if (running_in_chroot() > 0 && arg_action != ACTION_SYSTEMCTL) {
+ if (arg_action != ACTION_SYSTEMCTL && running_in_chroot() > 0) {
log_info("Running in chroot, ignoring request.");
r = 0;
goto finish;
diff --git a/src/systemd/sd-journal.h b/src/systemd/sd-journal.h
index 33e36149e9..caf322f062 100644
--- a/src/systemd/sd-journal.h
+++ b/src/systemd/sd-journal.h
@@ -129,6 +129,9 @@ int sd_journal_query_unique(sd_journal *j, const char *field);
int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l);
void sd_journal_restart_unique(sd_journal *j);
+int sd_journal_enumerate_fields(sd_journal *j, const char **field);
+void sd_journal_restart_fields(sd_journal *j);
+
int sd_journal_get_fd(sd_journal *j);
int sd_journal_get_events(sd_journal *j);
int sd_journal_get_timeout(sd_journal *j, uint64_t *timeout_usec);
@@ -139,22 +142,31 @@ int sd_journal_reliable_fd(sd_journal *j);
int sd_journal_get_catalog(sd_journal *j, char **text);
int sd_journal_get_catalog_for_message_id(sd_id128_t id, char **text);
-/* the inverse condition avoids ambiguity of danling 'else' after the macro */
+int sd_journal_has_runtime_files(sd_journal *j);
+int sd_journal_has_persistent_files(sd_journal *j);
+
+/* The inverse condition avoids ambiguity of dangling 'else' after the macro */
#define SD_JOURNAL_FOREACH(j) \
if (sd_journal_seek_head(j) < 0) { } \
else while (sd_journal_next(j) > 0)
-/* the inverse condition avoids ambiguity of danling 'else' after the macro */
+/* The inverse condition avoids ambiguity of dangling 'else' after the macro */
#define SD_JOURNAL_FOREACH_BACKWARDS(j) \
if (sd_journal_seek_tail(j) < 0) { } \
else while (sd_journal_previous(j) > 0)
+/* Iterate through the data fields of the current journal entry */
#define SD_JOURNAL_FOREACH_DATA(j, data, l) \
for (sd_journal_restart_data(j); sd_journal_enumerate_data((j), &(data), &(l)) > 0; )
+/* Iterate through the all known values of a specific field */
#define SD_JOURNAL_FOREACH_UNIQUE(j, data, l) \
for (sd_journal_restart_unique(j); sd_journal_enumerate_unique((j), &(data), &(l)) > 0; )
+/* Iterate through all known field names */
+#define SD_JOURNAL_FOREACH_FIELD(j, field) \
+ for (sd_journal_restart_fields(j); sd_journal_enumerate_fields((j), &(field)) > 0; )
+
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_journal, sd_journal_close);
_SD_END_DECLARATIONS;
diff --git a/src/test/test-rlimit-util.c b/src/test/test-rlimit-util.c
index 24bfe7a60e..d9ac9368cd 100644
--- a/src/test/test-rlimit-util.c
+++ b/src/test/test-rlimit-util.c
@@ -17,12 +17,37 @@
#include <sys/resource.h>
+#include "alloc-util.h"
#include "capability-util.h"
#include "macro.h"
#include "rlimit-util.h"
#include "string-util.h"
#include "util.h"
+static void test_rlimit_parse_format(int resource, const char *string, rlim_t soft, rlim_t hard, int ret, const char *formatted) {
+ _cleanup_free_ char *f = NULL;
+ struct rlimit rl = {
+ .rlim_cur = 4711,
+ .rlim_max = 4712,
+ }, rl2 = {
+ .rlim_cur = 4713,
+ .rlim_max = 4714
+ };
+
+ assert_se(rlimit_parse(resource, string, &rl) == ret);
+ if (ret < 0)
+ return;
+
+ assert_se(rl.rlim_cur == soft);
+ assert_se(rl.rlim_max == hard);
+
+ assert_se(rlimit_format(&rl, &f) >= 0);
+ assert_se(streq(formatted, f));
+
+ assert_se(rlimit_parse(resource, formatted, &rl2) >= 0);
+ assert_se(memcmp(&rl, &rl2, sizeof(struct rlimit)) == 0);
+}
+
int main(int argc, char *argv[]) {
struct rlimit old, new, high;
struct rlimit err = {
@@ -65,5 +90,15 @@ int main(int argc, char *argv[]) {
assert_se(old.rlim_cur == new.rlim_cur);
assert_se(old.rlim_max == new.rlim_max);
+ test_rlimit_parse_format(RLIMIT_NOFILE, "4:5", 4, 5, 0, "4:5");
+ test_rlimit_parse_format(RLIMIT_NOFILE, "6", 6, 6, 0, "6");
+ test_rlimit_parse_format(RLIMIT_NOFILE, "infinity", RLIM_INFINITY, RLIM_INFINITY, 0, "infinity");
+ test_rlimit_parse_format(RLIMIT_NOFILE, "infinity:infinity", RLIM_INFINITY, RLIM_INFINITY, 0, "infinity");
+ test_rlimit_parse_format(RLIMIT_NOFILE, "8:infinity", 8, RLIM_INFINITY, 0, "8:infinity");
+ test_rlimit_parse_format(RLIMIT_CPU, "25min:13h", (25*USEC_PER_MINUTE) / USEC_PER_SEC, (13*USEC_PER_HOUR) / USEC_PER_SEC, 0, "1500:46800");
+ test_rlimit_parse_format(RLIMIT_NOFILE, "", 0, 0, -EINVAL, NULL);
+ test_rlimit_parse_format(RLIMIT_NOFILE, "5:4", 0, 0, -EILSEQ, NULL);
+ test_rlimit_parse_format(RLIMIT_NOFILE, "5:4:3", 0, 0, -EINVAL, NULL);
+
return 0;
}
diff --git a/src/test/test-time.c b/src/test/test-time.c
index 8896b2c92b..ca44f81f9c 100644
--- a/src/test/test-time.c
+++ b/src/test/test-time.c
@@ -176,9 +176,19 @@ static void test_get_timezones(void) {
r = get_timezones(&zones);
assert_se(r == 0);
- STRV_FOREACH(zone, zones) {
+ STRV_FOREACH(zone, zones)
assert_se(timezone_is_valid(*zone));
- }
+}
+
+static void test_usec_add(void) {
+ assert_se(usec_add(0, 0) == 0);
+ assert_se(usec_add(1, 4) == 5);
+ assert_se(usec_add(USEC_INFINITY, 5) == USEC_INFINITY);
+ assert_se(usec_add(5, USEC_INFINITY) == USEC_INFINITY);
+ assert_se(usec_add(USEC_INFINITY-5, 2) == USEC_INFINITY-3);
+ assert_se(usec_add(USEC_INFINITY-2, 2) == USEC_INFINITY);
+ assert_se(usec_add(USEC_INFINITY-1, 2) == USEC_INFINITY);
+ assert_se(usec_add(USEC_INFINITY, 2) == USEC_INFINITY);
}
int main(int argc, char *argv[]) {
@@ -190,6 +200,7 @@ int main(int argc, char *argv[]) {
test_format_timespan(USEC_PER_SEC);
test_timezone_is_valid();
test_get_timezones();
+ test_usec_add();
return 0;
}
diff --git a/src/test/test-unit-file.c b/src/test/test-unit-file.c
index 199623e025..a458870846 100644
--- a/src/test/test-unit-file.c
+++ b/src/test/test-unit-file.c
@@ -712,12 +712,15 @@ static void test_config_parse_rlimit(void) {
assert_se(rl[RLIMIT_NOFILE]->rlim_cur == RLIM_INFINITY);
assert_se(rl[RLIMIT_NOFILE]->rlim_cur == rl[RLIMIT_NOFILE]->rlim_max);
+ rl[RLIMIT_NOFILE]->rlim_cur = 10;
+ rl[RLIMIT_NOFILE]->rlim_max = 20;
+
+ /* Invalid values don't change rl */
assert_se(config_parse_limit(NULL, "fake", 1, "section", 1, "LimitNOFILE", RLIMIT_NOFILE, "10:20:30", rl, NULL) >= 0);
assert_se(rl[RLIMIT_NOFILE]);
assert_se(rl[RLIMIT_NOFILE]->rlim_cur == 10);
assert_se(rl[RLIMIT_NOFILE]->rlim_max == 20);
- /* Invalid values don't change rl */
assert_se(config_parse_limit(NULL, "fake", 1, "section", 1, "LimitNOFILE", RLIMIT_NOFILE, "wat:wat", rl, NULL) >= 0);
assert_se(rl[RLIMIT_NOFILE]);
assert_se(rl[RLIMIT_NOFILE]->rlim_cur == 10);
@@ -735,64 +738,64 @@ static void test_config_parse_rlimit(void) {
rl[RLIMIT_NOFILE] = mfree(rl[RLIMIT_NOFILE]);
- assert_se(config_parse_sec_limit(NULL, "fake", 1, "section", 1, "LimitCPU", RLIMIT_CPU, "56", rl, NULL) >= 0);
+ assert_se(config_parse_limit(NULL, "fake", 1, "section", 1, "LimitCPU", RLIMIT_CPU, "56", rl, NULL) >= 0);
assert_se(rl[RLIMIT_CPU]);
assert_se(rl[RLIMIT_CPU]->rlim_cur == 56);
assert_se(rl[RLIMIT_CPU]->rlim_cur == rl[RLIMIT_CPU]->rlim_max);
- assert_se(config_parse_sec_limit(NULL, "fake", 1, "section", 1, "LimitCPU", RLIMIT_CPU, "57s", rl, NULL) >= 0);
+ assert_se(config_parse_limit(NULL, "fake", 1, "section", 1, "LimitCPU", RLIMIT_CPU, "57s", rl, NULL) >= 0);
assert_se(rl[RLIMIT_CPU]);
assert_se(rl[RLIMIT_CPU]->rlim_cur == 57);
assert_se(rl[RLIMIT_CPU]->rlim_cur == rl[RLIMIT_CPU]->rlim_max);
- assert_se(config_parse_sec_limit(NULL, "fake", 1, "section", 1, "LimitCPU", RLIMIT_CPU, "40s:1m", rl, NULL) >= 0);
+ assert_se(config_parse_limit(NULL, "fake", 1, "section", 1, "LimitCPU", RLIMIT_CPU, "40s:1m", rl, NULL) >= 0);
assert_se(rl[RLIMIT_CPU]);
assert_se(rl[RLIMIT_CPU]->rlim_cur == 40);
assert_se(rl[RLIMIT_CPU]->rlim_max == 60);
- assert_se(config_parse_sec_limit(NULL, "fake", 1, "section", 1, "LimitCPU", RLIMIT_CPU, "infinity", rl, NULL) >= 0);
+ assert_se(config_parse_limit(NULL, "fake", 1, "section", 1, "LimitCPU", RLIMIT_CPU, "infinity", rl, NULL) >= 0);
assert_se(rl[RLIMIT_CPU]);
assert_se(rl[RLIMIT_CPU]->rlim_cur == RLIM_INFINITY);
assert_se(rl[RLIMIT_CPU]->rlim_cur == rl[RLIMIT_CPU]->rlim_max);
- assert_se(config_parse_sec_limit(NULL, "fake", 1, "section", 1, "LimitCPU", RLIMIT_CPU, "1234ms", rl, NULL) >= 0);
+ assert_se(config_parse_limit(NULL, "fake", 1, "section", 1, "LimitCPU", RLIMIT_CPU, "1234ms", rl, NULL) >= 0);
assert_se(rl[RLIMIT_CPU]);
assert_se(rl[RLIMIT_CPU]->rlim_cur == 2);
assert_se(rl[RLIMIT_CPU]->rlim_cur == rl[RLIMIT_CPU]->rlim_max);
rl[RLIMIT_CPU] = mfree(rl[RLIMIT_CPU]);
- assert_se(config_parse_usec_limit(NULL, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME, "58", rl, NULL) >= 0);
+ assert_se(config_parse_limit(NULL, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME, "58", rl, NULL) >= 0);
assert_se(rl[RLIMIT_RTTIME]);
assert_se(rl[RLIMIT_RTTIME]->rlim_cur == 58);
assert_se(rl[RLIMIT_RTTIME]->rlim_cur == rl[RLIMIT_RTTIME]->rlim_max);
- assert_se(config_parse_usec_limit(NULL, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME, "58:60", rl, NULL) >= 0);
+ assert_se(config_parse_limit(NULL, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME, "58:60", rl, NULL) >= 0);
assert_se(rl[RLIMIT_RTTIME]);
assert_se(rl[RLIMIT_RTTIME]->rlim_cur == 58);
assert_se(rl[RLIMIT_RTTIME]->rlim_max == 60);
- assert_se(config_parse_usec_limit(NULL, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME, "59s", rl, NULL) >= 0);
+ assert_se(config_parse_limit(NULL, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME, "59s", rl, NULL) >= 0);
assert_se(rl[RLIMIT_RTTIME]);
assert_se(rl[RLIMIT_RTTIME]->rlim_cur == 59 * USEC_PER_SEC);
assert_se(rl[RLIMIT_RTTIME]->rlim_cur == rl[RLIMIT_RTTIME]->rlim_max);
- assert_se(config_parse_usec_limit(NULL, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME, "59s:123s", rl, NULL) >= 0);
+ assert_se(config_parse_limit(NULL, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME, "59s:123s", rl, NULL) >= 0);
assert_se(rl[RLIMIT_RTTIME]);
assert_se(rl[RLIMIT_RTTIME]->rlim_cur == 59 * USEC_PER_SEC);
assert_se(rl[RLIMIT_RTTIME]->rlim_max == 123 * USEC_PER_SEC);
- assert_se(config_parse_usec_limit(NULL, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME, "infinity", rl, NULL) >= 0);
+ assert_se(config_parse_limit(NULL, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME, "infinity", rl, NULL) >= 0);
assert_se(rl[RLIMIT_RTTIME]);
assert_se(rl[RLIMIT_RTTIME]->rlim_cur == RLIM_INFINITY);
assert_se(rl[RLIMIT_RTTIME]->rlim_cur == rl[RLIMIT_RTTIME]->rlim_max);
- assert_se(config_parse_usec_limit(NULL, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME, "infinity:infinity", rl, NULL) >= 0);
+ assert_se(config_parse_limit(NULL, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME, "infinity:infinity", rl, NULL) >= 0);
assert_se(rl[RLIMIT_RTTIME]);
assert_se(rl[RLIMIT_RTTIME]->rlim_cur == RLIM_INFINITY);
assert_se(rl[RLIMIT_RTTIME]->rlim_cur == rl[RLIMIT_RTTIME]->rlim_max);
- assert_se(config_parse_usec_limit(NULL, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME, "2345ms", rl, NULL) >= 0);
+ assert_se(config_parse_limit(NULL, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME, "2345ms", rl, NULL) >= 0);
assert_se(rl[RLIMIT_RTTIME]);
assert_se(rl[RLIMIT_RTTIME]->rlim_cur == 2345 * USEC_PER_MSEC);
assert_se(rl[RLIMIT_RTTIME]->rlim_cur == rl[RLIMIT_RTTIME]->rlim_max);
diff --git a/src/test/test-util.c b/src/test/test-util.c
index f6ed55878c..e199497818 100644
--- a/src/test/test-util.c
+++ b/src/test/test-util.c
@@ -545,38 +545,31 @@ static void test_unbase32hexmem(void) {
static void test_base64mem(void) {
char *b64;
- b64 = base64mem("", strlen(""));
- assert_se(b64);
+ assert_se(base64mem("", strlen(""), &b64) == 0);
assert_se(streq(b64, ""));
free(b64);
- b64 = base64mem("f", strlen("f"));
- assert_se(b64);
+ assert_se(base64mem("f", strlen("f"), &b64) == 4);
assert_se(streq(b64, "Zg=="));
free(b64);
- b64 = base64mem("fo", strlen("fo"));
- assert_se(b64);
+ assert_se(base64mem("fo", strlen("fo"), &b64) == 4);
assert_se(streq(b64, "Zm8="));
free(b64);
- b64 = base64mem("foo", strlen("foo"));
- assert_se(b64);
+ assert_se(base64mem("foo", strlen("foo"), &b64) == 4);
assert_se(streq(b64, "Zm9v"));
free(b64);
- b64 = base64mem("foob", strlen("foob"));
- assert_se(b64);
+ assert_se(base64mem("foob", strlen("foob"), &b64) == 8);
assert_se(streq(b64, "Zm9vYg=="));
free(b64);
- b64 = base64mem("fooba", strlen("fooba"));
- assert_se(b64);
+ assert_se(base64mem("fooba", strlen("fooba"), &b64) == 8);
assert_se(streq(b64, "Zm9vYmE="));
free(b64);
- b64 = base64mem("foobar", strlen("foobar"));
- assert_se(b64);
+ assert_se(base64mem("foobar", strlen("foobar"), &b64) == 8);
assert_se(streq(b64, "Zm9vYmFy"));
free(b64);
}
diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c
index bb81ff5e3a..59ef940a4d 100644
--- a/src/tmpfiles/tmpfiles.c
+++ b/src/tmpfiles/tmpfiles.c
@@ -1153,6 +1153,7 @@ static int create_item(Item *i) {
_cleanup_free_ char *resolved = NULL;
struct stat st;
int r = 0;
+ int q = 0;
CreationMode creation;
assert(i);
@@ -1279,27 +1280,23 @@ static int create_item(Item *i) {
if (IN_SET(i->type, CREATE_SUBVOLUME_NEW_QUOTA, CREATE_SUBVOLUME_INHERIT_QUOTA)) {
r = btrfs_subvol_auto_qgroup(i->path, 0, i->type == CREATE_SUBVOLUME_NEW_QUOTA);
- if (r == -ENOTTY) {
+ if (r == -ENOTTY)
log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" because of unsupported file system or because directory is not a subvolume: %m", i->path);
- return 0;
- }
- if (r == -EROFS) {
+ else if (r == -EROFS)
log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" because of read-only file system: %m", i->path);
- return 0;
- }
- if (r == -ENOPROTOOPT) {
+ else if (r == -ENOPROTOOPT)
log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" because quota support is disabled: %m", i->path);
- return 0;
- }
- if (r < 0)
- return log_error_errno(r, "Failed to adjust quota for subvolume \"%s\": %m", i->path);
- if (r > 0)
+ else if (r < 0)
+ q = log_error_errno(r, "Failed to adjust quota for subvolume \"%s\": %m", i->path);
+ else if (r > 0)
log_debug("Adjusted quota for subvolume \"%s\".", i->path);
- if (r == 0)
+ else if (r == 0)
log_debug("Quota for subvolume \"%s\" already in place, no change made.", i->path);
}
r = path_set_perms(i, i->path);
+ if (q < 0)
+ return q;
if (r < 0)
return r;
diff --git a/src/udev/udev-builtin-net_id.c b/src/udev/udev-builtin-net_id.c
index 104d5111c5..4f8a759d04 100644
--- a/src/udev/udev-builtin-net_id.c
+++ b/src/udev/udev-builtin-net_id.c
@@ -36,7 +36,7 @@
*
* Type of names:
* b<number> -- BCMA bus core number
- * ccw<name> -- CCW bus group name
+ * c<bus_id> -- CCW bus group name, without leading zeros [s390]
* o<index>[d<dev_port>] -- on-board device index number
* s<slot>[f<function>][d<dev_port>] -- hotplug slot index number
* x<MAC> -- MAC address
@@ -430,8 +430,15 @@ static int names_ccw(struct udev_device *dev, struct netnames *names) {
if (!bus_id_len || bus_id_len < 8 || bus_id_len > 9)
return -EINVAL;
+ /* Strip leading zeros from the bus id for aesthetic purposes. This
+ * keeps the ccw names stable, yet much shorter in general case of
+ * bus_id 0.0.0600 -> 600. This is similar to e.g. how PCI domain is
+ * not prepended when it is zero.
+ */
+ bus_id += strspn(bus_id, ".0");
+
/* Store the CCW bus-ID for use as network device name */
- rc = snprintf(names->ccw_group, sizeof(names->ccw_group), "ccw%s", bus_id);
+ rc = snprintf(names->ccw_group, sizeof(names->ccw_group), "c%s", bus_id);
if (rc >= 0 && rc < (int)sizeof(names->ccw_group))
names->type = NET_CCWGROUP;
return 0;