diff options
Diffstat (limited to 'src')
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; |