summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/analyze/analyze-fdstore.c2
-rw-r--r--src/analyze/analyze-inspect-elf.c26
-rw-r--r--src/analyze/analyze-plot.c8
-rw-r--r--src/analyze/analyze-security.c4
-rw-r--r--src/analyze/analyze.c6
-rw-r--r--src/basic/env-util.c32
-rw-r--r--src/basic/env-util.h5
-rw-r--r--src/basic/iovec-wrapper.c21
-rw-r--r--src/basic/iovec-wrapper.h3
-rw-r--r--src/basic/process-util.c2
-rw-r--r--src/basic/signal-util.c16
-rw-r--r--src/basic/signal-util.h2
-rw-r--r--src/basic/string-util.c35
-rw-r--r--src/basic/terminal-util.c155
-rw-r--r--src/basic/terminal-util.h13
-rw-r--r--src/boot/bootctl-status.c2
-rw-r--r--src/boot/efi/boot.c4
-rw-r--r--src/boot/measure.c8
-rw-r--r--src/busctl/busctl.c79
-rw-r--r--src/core/dbus-execute.c112
-rw-r--r--src/core/exec-invoke.c54
-rw-r--r--src/core/execute-serialize.c27
-rw-r--r--src/core/execute.c73
-rw-r--r--src/core/execute.h18
-rw-r--r--src/core/load-fragment-gperf.gperf.in2
-rw-r--r--src/core/load-fragment.c28
-rw-r--r--src/core/load-fragment.h1
-rw-r--r--src/core/mount.c3
-rw-r--r--src/core/namespace.c112
-rw-r--r--src/core/namespace.h14
-rw-r--r--src/core/service.c116
-rw-r--r--src/core/socket.c8
-rw-r--r--src/core/swap.c3
-rw-r--r--src/coredump/coredump.c473
-rw-r--r--src/coredump/coredump.conf2
-rw-r--r--src/creds/creds.c4
-rw-r--r--src/cryptenroll/cryptenroll-recovery.c2
-rw-r--r--src/dissect/dissect.c10
-rw-r--r--src/firstboot/firstboot.c159
-rw-r--r--src/fundamental/macro-fundamental.h1
-rw-r--r--src/home/homectl-recovery-key.c2
-rw-r--r--src/home/homectl.c6
-rw-r--r--src/home/homed-home-bus.c34
-rw-r--r--src/home/homed-home-bus.h1
-rw-r--r--src/home/homed-home.c9
-rw-r--r--src/home/org.freedesktop.home1.policy10
-rw-r--r--src/hostname/hostnamectl.c2
-rw-r--r--src/import/export-raw.c16
-rw-r--r--src/import/export-tar.c15
-rw-r--r--src/import/import-raw.c15
-rw-r--r--src/import/import-tar.c15
-rw-r--r--src/import/importctl.c2
-rw-r--r--src/journal/bsod.c10
-rw-r--r--src/journal/journalctl-authenticate.c4
-rw-r--r--src/journal/journalctl-misc.c2
-rw-r--r--src/kernel-install/kernel-install.c2
-rw-r--r--src/libsystemd-network/ndisc-option.c5
-rw-r--r--src/libsystemd-network/sd-dhcp-lease.c8
-rw-r--r--src/libsystemd-network/sd-dhcp6-lease.c6
-rw-r--r--src/libsystemd/sd-bus/bus-common-errors.c3
-rw-r--r--src/libsystemd/sd-json/json-util.h2
-rw-r--r--src/libsystemd/sd-json/sd-json.c38
-rw-r--r--src/libsystemd/sd-netlink/netlink-message-rtnl.c2
-rw-r--r--src/libsystemd/sd-varlink/sd-varlink.c43
-rw-r--r--src/libsystemd/sd-varlink/varlink-internal.h7
-rw-r--r--src/login/inhibit.c2
-rw-r--r--src/login/loginctl.c2
-rw-r--r--src/login/logind-dbus.c22
-rw-r--r--src/login/logind-inhibit.c11
-rw-r--r--src/login/logind-inhibit.h3
-rw-r--r--src/machine/machine-dbus.c13
-rw-r--r--src/machine/machine-varlink.c4
-rw-r--r--src/machine/machined-varlink.c56
-rw-r--r--src/network/fuzz-netdev-parser.c3
-rw-r--r--src/network/netdev/batadv.c3
-rw-r--r--src/network/netdev/bridge.c7
-rw-r--r--src/network/netdev/fou-tunnel.c4
-rw-r--r--src/network/netdev/ipoib.c4
-rw-r--r--src/network/netdev/ipvlan.c9
-rw-r--r--src/network/netdev/l2tp-tunnel.c11
-rw-r--r--src/network/netdev/macsec.c30
-rw-r--r--src/network/netdev/netdev.c221
-rw-r--r--src/network/netdev/netdev.h22
-rw-r--r--src/network/netdev/tunnel.c147
-rw-r--r--src/network/netdev/tunnel.h1
-rw-r--r--src/network/netdev/tuntap.c1
-rw-r--r--src/network/netdev/vxlan.c79
-rw-r--r--src/network/netdev/wireguard.c6
-rw-r--r--src/network/netdev/wlan.c3
-rw-r--r--src/network/networkctl-description.c2
-rw-r--r--src/network/networkctl-lldp.c2
-rw-r--r--src/network/networkd-can.c4
-rw-r--r--src/network/networkd-dhcp-prefix-delegation.c25
-rw-r--r--src/network/networkd-dhcp4.c4
-rw-r--r--src/network/networkd-dhcp6.c4
-rw-r--r--src/network/networkd-ipv4ll.c4
-rw-r--r--src/network/networkd-link.c24
-rw-r--r--src/network/networkd-link.h8
-rw-r--r--src/network/networkd-manager.c78
-rw-r--r--src/network/networkd-manager.h11
-rw-r--r--src/network/networkd-ndisc.c2
-rw-r--r--src/network/networkd-network.c41
-rw-r--r--src/network/networkd-network.h2
-rw-r--r--src/network/networkd-queue.c1
-rw-r--r--src/network/networkd-setlink.c4
-rw-r--r--src/notify/notify.c74
-rw-r--r--src/partition/repart.c34
-rw-r--r--src/pcrlock/pcrlock.c18
-rw-r--r--src/resolve/resolvectl.c24
-rw-r--r--src/resolve/resolved-dns-packet.c34
-rw-r--r--src/resolve/resolved-varlink.c120
-rw-r--r--src/resolve/test-dns-packet-append.c30
-rw-r--r--src/resolve/test-dns-packet-extract.c93
-rw-r--r--src/run/run.c52
-rw-r--r--src/shared/ask-password-api.c32
-rw-r--r--src/shared/bootspec.c2
-rw-r--r--src/shared/bus-unit-util.c50
-rw-r--r--src/shared/bus-unit-util.h10
-rw-r--r--src/shared/dns-domain.c12
-rw-r--r--src/shared/fdset.c40
-rw-r--r--src/shared/format-table.c2
-rw-r--r--src/shared/journal-importer.c4
-rw-r--r--src/shared/pretty-print.c44
-rw-r--r--src/shared/pretty-print.h5
-rw-r--r--src/shared/ptyfwd.c19
-rw-r--r--src/shared/qrcode-util.c35
-rw-r--r--src/shared/qrcode-util.h31
-rw-r--r--src/shared/user-record-show.c29
-rw-r--r--src/shared/user-record.c252
-rw-r--r--src/shared/user-record.h9
-rw-r--r--src/sysext/sysext.c2
-rw-r--r--src/systemctl/systemctl-compat-halt.c7
-rw-r--r--src/systemctl/systemctl-logind.c4
-rw-r--r--src/systemctl/systemctl-start-special.c15
-rw-r--r--src/systemd/_sd-common.h4
-rw-r--r--src/systemd/sd-id128.h10
-rw-r--r--src/systemd/sd-json.h4
-rw-r--r--src/systemd/sd-varlink-idl.h3
-rw-r--r--src/systemd/sd-varlink.h2
-rw-r--r--src/sysupdate/meson.build6
-rw-r--r--src/sysupdate/sysupdate.c12
-rw-r--r--src/sysupdate/updatectl.c14
-rw-r--r--src/sysusers/sysusers.c32
-rw-r--r--src/sysv-generator/sysv-generator.c9
-rw-r--r--src/test/meson.build2
-rw-r--r--src/test/test-env-util.c11
-rw-r--r--src/test/test-progress-bar.c6
-rw-r--r--src/test/test-sbat.c19
-rw-r--r--src/test/test-strip-tab-ansi.c9
-rw-r--r--src/test/test-terminal-util.c29
-rw-r--r--src/test/test-user-record.c101
-rw-r--r--src/test/test-varlink.c28
-rw-r--r--src/udev/udevadm-info.c22
-rw-r--r--src/update-utmp/update-utmp.c11
-rw-r--r--src/userdb/userdbctl.c2
-rw-r--r--src/varlinkctl/varlinkctl.c8
156 files changed, 2958 insertions, 1253 deletions
diff --git a/src/analyze/analyze-fdstore.c b/src/analyze/analyze-fdstore.c
index 4cf015ab44..e7e273e218 100644
--- a/src/analyze/analyze-fdstore.c
+++ b/src/analyze/analyze-fdstore.c
@@ -81,7 +81,7 @@ static int dump_fdstore(sd_bus *bus, const char *arg) {
if (r < 0)
return r;
- if (FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF) && table_isempty(table))
+ if (table_isempty(table) && !sd_json_format_enabled(arg_json_format_flags))
log_info("No file descriptors in fdstore of '%s'.", unit);
else {
r = table_print_with_pager(table, arg_json_format_flags, arg_pager_flags, /* show_header= */true);
diff --git a/src/analyze/analyze-inspect-elf.c b/src/analyze/analyze-inspect-elf.c
index 1ae7304163..0f78729c3a 100644
--- a/src/analyze/analyze-inspect-elf.c
+++ b/src/analyze/analyze-inspect-elf.c
@@ -4,6 +4,7 @@
#include "analyze.h"
#include "analyze-inspect-elf.h"
+#include "chase.h"
#include "elf-util.h"
#include "errno-util.h"
#include "fd-util.h"
@@ -19,23 +20,13 @@ static int analyze_elf(char **filenames, sd_json_format_flags_t json_flags) {
STRV_FOREACH(filename, filenames) {
_cleanup_(sd_json_variant_unrefp) sd_json_variant *package_metadata = NULL;
_cleanup_(table_unrefp) Table *t = NULL;
- _cleanup_free_ char *abspath = NULL, *path = NULL, *stacktrace = NULL;
+ _cleanup_free_ char *abspath = NULL, *stacktrace = NULL;
_cleanup_close_ int fd = -EBADF;
bool coredump = false;
- r = path_make_absolute_cwd(*filename, &abspath);
- if (r < 0)
- return log_error_errno(r, "Could not make an absolute path out of \"%s\": %m", *filename);
-
- path = path_join(empty_to_root(arg_root), abspath);
- if (!path)
- return log_oom();
-
- path_simplify(path);
-
- fd = RET_NERRNO(open(path, O_RDONLY|O_CLOEXEC));
+ fd = chase_and_open(*filename, arg_root, CHASE_PREFIX_ROOT, O_RDONLY|O_CLOEXEC, &abspath);
if (fd < 0)
- return log_error_errno(fd, "Could not open \"%s\": %m", path);
+ return log_error_errno(fd, "Could not open \"%s\": %m", *filename);
r = parse_elf_object(fd, abspath, arg_root, /* fork_disable_dump= */false, &stacktrace, &package_metadata);
if (r < 0)
@@ -65,7 +56,7 @@ static int analyze_elf(char **filenames, sd_json_format_flags_t json_flags) {
* metadata is parsed recursively in core files, so there might be
* multiple modules. */
if (STR_IN_SET(module_name, "elfType", "elfArchitecture")) {
- if (streq(module_name, "elfType") && streq("coredump", sd_json_variant_string(module_json)))
+ if (streq(module_name, "elfType") && streq(sd_json_variant_string(module_json), "coredump"))
coredump = true;
r = table_add_many(
@@ -118,12 +109,13 @@ static int analyze_elf(char **filenames, sd_json_format_flags_t json_flags) {
return table_log_add_error(r);
}
- if (json_flags & SD_JSON_FORMAT_OFF) {
+ if (sd_json_format_enabled(json_flags))
+ sd_json_variant_dump(package_metadata, json_flags, stdout, NULL);
+ else {
r = table_print(t, NULL);
if (r < 0)
return table_log_print_error(r);
- } else
- sd_json_variant_dump(package_metadata, json_flags, stdout, NULL);
+ }
}
return 0;
diff --git a/src/analyze/analyze-plot.c b/src/analyze/analyze-plot.c
index 78aeff1b62..c50343d71c 100644
--- a/src/analyze/analyze-plot.c
+++ b/src/analyze/analyze-plot.c
@@ -168,7 +168,9 @@ static void plot_tooltip(const UnitTimes *ut) {
assert(ut->name);
svg("%s:\n", ut->name);
-
+ svg("Activating: %"PRI_USEC".%.3"PRI_USEC"\n", ut->activating / USEC_PER_SEC, ut->activating % USEC_PER_SEC);
+ svg("Activated: %"PRI_USEC".%.3"PRI_USEC"\n", ut->activated / USEC_PER_SEC, ut->activated % USEC_PER_SEC);
+
UnitDependency i;
FOREACH_ARGUMENT(i, UNIT_AFTER, UNIT_BEFORE, UNIT_REQUIRES, UNIT_REQUISITE, UNIT_WANTS, UNIT_CONFLICTS, UNIT_UPHOLDS)
if (!strv_isempty(ut->deps[i])) {
@@ -417,7 +419,7 @@ static int show_table(Table *table, const char *word) {
if (!table_isempty(table)) {
table_set_header(table, arg_legend);
- if (!FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF))
+ if (sd_json_format_enabled(arg_json_format_flags))
r = table_print_json(table, NULL, arg_json_format_flags | SD_JSON_FORMAT_COLOR_AUTO);
else
r = table_print(table, NULL);
@@ -494,7 +496,7 @@ int verb_plot(int argc, char *argv[], void *userdata) {
typesafe_qsort(times, n, compare_unit_start);
- if (!FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF) || arg_table)
+ if (sd_json_format_enabled(arg_json_format_flags) || arg_table)
r = produce_plot_as_text(times, boot);
else
r = produce_plot_as_svg(times, host, boot, pretty_times);
diff --git a/src/analyze/analyze-security.c b/src/analyze/analyze-security.c
index 874fff14b4..aefd4f6122 100644
--- a/src/analyze/analyze-security.c
+++ b/src/analyze/analyze-security.c
@@ -1872,7 +1872,7 @@ static int assess(const SecurityInfo *info,
return log_error_errno(r, "Failed to update cell in table: %m");
}
- if (json_format_flags & SD_JSON_FORMAT_OFF) {
+ if (!sd_json_format_enabled(json_format_flags)) {
r = table_hide_column_from_display(details_table, (size_t) 2);
if (r < 0)
return log_error_errno(r, "Failed to set columns to display: %m");
@@ -1891,7 +1891,7 @@ static int assess(const SecurityInfo *info,
assert(i < ELEMENTSOF(badness_table));
- if (details_table && (json_format_flags & SD_JSON_FORMAT_OFF)) {
+ if (details_table && !sd_json_format_enabled(json_format_flags)) {
_cleanup_free_ char *clickable = NULL;
const char *name;
diff --git a/src/analyze/analyze.c b/src/analyze/analyze.c
index 5aba273ca6..0db3547a49 100644
--- a/src/analyze/analyze.c
+++ b/src/analyze/analyze.c
@@ -592,7 +592,7 @@ static int parse_argv(int argc, char *argv[]) {
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Option --offline= requires one or more units to perform a security review.");
- if (arg_json_format_flags != SD_JSON_FORMAT_OFF && !STRPTR_IN_SET(argv[optind], "security", "inspect-elf", "plot", "fdstore", "pcrs", "architectures", "capability", "exit-status"))
+ if (sd_json_format_enabled(arg_json_format_flags) && !STRPTR_IN_SET(argv[optind], "security", "inspect-elf", "plot", "fdstore", "pcrs", "architectures", "capability", "exit-status"))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Option --json= is only supported for security, inspect-elf, plot, fdstore, pcrs, architectures, capability, exit-status right now.");
@@ -631,13 +631,13 @@ static int parse_argv(int argc, char *argv[]) {
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No conditions can be passed if --unit= is used.");
if ((!arg_legend && !STRPTR_IN_SET(argv[optind], "plot", "architectures")) ||
- (streq_ptr(argv[optind], "plot") && !arg_legend && !arg_table && FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF)))
+ (streq_ptr(argv[optind], "plot") && !arg_legend && !arg_table && !sd_json_format_enabled(arg_json_format_flags)))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Option --no-legend is only supported for plot with either --table or --json=.");
if (arg_table && !streq_ptr(argv[optind], "plot"))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Option --table is only supported for plot right now.");
- if (arg_table && !FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF))
+ if (arg_table && sd_json_format_enabled(arg_json_format_flags))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--table and --json= are mutually exclusive.");
if (arg_capability != CAPABILITY_LITERAL && !streq_ptr(argv[optind], "capability"))
diff --git a/src/basic/env-util.c b/src/basic/env-util.c
index 4c06d1a165..99ce1a1842 100644
--- a/src/basic/env-util.c
+++ b/src/basic/env-util.c
@@ -266,7 +266,7 @@ static bool env_entry_has_name(const char *entry, const char *name) {
return *t == '=';
}
-char **strv_env_delete(char **x, size_t n_lists, ...) {
+char** strv_env_delete(char **x, size_t n_lists, ...) {
size_t n, i = 0;
_cleanup_strv_free_ char **t = NULL;
va_list ap;
@@ -564,7 +564,35 @@ char* strv_env_pairs_get(char **l, const char *name) {
return result;
}
-char **strv_env_clean_with_callback(char **e, void (*invalid_callback)(const char *p, void *userdata), void *userdata) {
+int strv_env_get_merged(char **l, char ***ret) {
+ _cleanup_strv_free_ char **v = NULL;
+ size_t n = 0;
+ int r;
+
+ assert(ret);
+
+ /* This converts a strv with pairs of environment variable name + value into a strv of name and
+ * value concatenated with a "=" separator. E.g.
+ * input : { "NAME", "value", "FOO", "var" }
+ * output : { "NAME=value", "FOO=var" } */
+
+ STRV_FOREACH_PAIR(key, value, l) {
+ char *s;
+
+ s = strjoin(*key, "=", *value);
+ if (!s)
+ return -ENOMEM;
+
+ r = strv_consume_with_size(&v, &n, s);
+ if (r < 0)
+ return r;
+ }
+
+ *ret = TAKE_PTR(v);
+ return 0;
+}
+
+char** strv_env_clean_with_callback(char **e, void (*invalid_callback)(const char *p, void *userdata), void *userdata) {
int k = 0;
STRV_FOREACH(p, e) {
diff --git a/src/basic/env-util.h b/src/basic/env-util.h
index 5f20f1d69c..203ed65bd1 100644
--- a/src/basic/env-util.h
+++ b/src/basic/env-util.h
@@ -34,14 +34,14 @@ int replace_env_argv(char **argv, char **env, char ***ret, char ***ret_unset_var
bool strv_env_is_valid(char **e);
#define strv_env_clean(l) strv_env_clean_with_callback(l, NULL, NULL)
-char **strv_env_clean_with_callback(char **l, void (*invalid_callback)(const char *p, void *userdata), void *userdata);
+char** strv_env_clean_with_callback(char **l, void (*invalid_callback)(const char *p, void *userdata), void *userdata);
bool strv_env_name_is_valid(char **l);
bool strv_env_name_or_assignment_is_valid(char **l);
char** _strv_env_merge(char **first, ...);
#define strv_env_merge(first, ...) _strv_env_merge(first, __VA_ARGS__, POINTER_MAX)
-char **strv_env_delete(char **x, size_t n_lists, ...); /* New copy */
+char** strv_env_delete(char **x, size_t n_lists, ...); /* New copy */
char** strv_env_unset(char **l, const char *p); /* In place ... */
char** strv_env_unset_many_internal(char **l, ...) _sentinel_;
@@ -60,6 +60,7 @@ static inline char* strv_env_get(char * const *x, const char *n) {
}
char* strv_env_pairs_get(char **l, const char *name) _pure_;
+int strv_env_get_merged(char **l, char ***ret);
int getenv_bool(const char *p);
int secure_getenv_bool(const char *p);
diff --git a/src/basic/iovec-wrapper.c b/src/basic/iovec-wrapper.c
index b335acd108..5cc3cc2f93 100644
--- a/src/basic/iovec-wrapper.c
+++ b/src/basic/iovec-wrapper.c
@@ -9,22 +9,27 @@ struct iovec_wrapper *iovw_new(void) {
return new0(struct iovec_wrapper, 1);
}
-void iovw_free_contents(struct iovec_wrapper *iovw, bool free_vectors) {
+void iovw_done(struct iovec_wrapper *iovw) {
assert(iovw);
- if (free_vectors)
- for (size_t i = 0; i < iovw->count; i++)
- free(iovw->iovec[i].iov_base);
-
iovw->iovec = mfree(iovw->iovec);
iovw->count = 0;
}
+void iovw_done_free(struct iovec_wrapper *iovw) {
+ assert(iovw);
+
+ FOREACH_ARRAY(i, iovw->iovec, iovw->count)
+ iovec_done(i);
+
+ iovw_done(iovw);
+}
+
struct iovec_wrapper *iovw_free_free(struct iovec_wrapper *iovw) {
if (!iovw)
return NULL;
- iovw_free_contents(iovw, /* free_vectors= */ true);
+ iovw_done_free(iovw);
return mfree(iovw);
}
@@ -32,7 +37,7 @@ struct iovec_wrapper *iovw_free(struct iovec_wrapper *iovw) {
if (!iovw)
return NULL;
- iovw_free_contents(iovw, /* free_vectors= */ false);
+ iovw_done(iovw);
return mfree(iovw);
}
@@ -124,7 +129,7 @@ int iovw_append(struct iovec_wrapper *target, const struct iovec_wrapper *source
rollback:
for (size_t i = original_count; i < target->count; i++)
- free(target->iovec[i].iov_base);
+ iovec_done(target->iovec + i);
target->count = original_count;
return r;
diff --git a/src/basic/iovec-wrapper.h b/src/basic/iovec-wrapper.h
index 05e220c1d0..4778fa6b27 100644
--- a/src/basic/iovec-wrapper.h
+++ b/src/basic/iovec-wrapper.h
@@ -17,7 +17,8 @@ struct iovec_wrapper *iovw_free_free(struct iovec_wrapper *iovw);
DEFINE_TRIVIAL_CLEANUP_FUNC(struct iovec_wrapper*, iovw_free_free);
-void iovw_free_contents(struct iovec_wrapper *iovw, bool free_vectors);
+void iovw_done_free(struct iovec_wrapper *iovw);
+void iovw_done(struct iovec_wrapper *iovw);
int iovw_put(struct iovec_wrapper *iovw, void *data, size_t len);
static inline int iovw_consume(struct iovec_wrapper *iovw, void *data, size_t len) {
diff --git a/src/basic/process-util.c b/src/basic/process-util.c
index f30d9117a7..a85a1b35f0 100644
--- a/src/basic/process-util.c
+++ b/src/basic/process-util.c
@@ -2029,7 +2029,7 @@ int posix_spawn_wrapper(
const char *cgroup,
PidRef *ret_pidref) {
- short flags = POSIX_SPAWN_SETSIGMASK|POSIX_SPAWN_SETSIGDEF;
+ short flags = POSIX_SPAWN_SETSIGMASK;
posix_spawnattr_t attr;
sigset_t mask;
int r;
diff --git a/src/basic/signal-util.c b/src/basic/signal-util.c
index 670040f1ad..ad9ed049f1 100644
--- a/src/basic/signal-util.c
+++ b/src/basic/signal-util.c
@@ -301,3 +301,19 @@ const struct sigaction sigaction_default = {
.sa_handler = SIG_DFL,
.sa_flags = SA_RESTART,
};
+
+int parse_signo(const char *s, int *ret) {
+ int sig, r;
+
+ r = safe_atoi(s, &sig);
+ if (r < 0)
+ return r;
+
+ if (!SIGNAL_VALID(sig))
+ return -EINVAL;
+
+ if (ret)
+ *ret = sig;
+
+ return 0;
+}
diff --git a/src/basic/signal-util.h b/src/basic/signal-util.h
index 5739fe0559..559ce42949 100644
--- a/src/basic/signal-util.h
+++ b/src/basic/signal-util.h
@@ -68,3 +68,5 @@ void propagate_signal(int sig, siginfo_t *siginfo);
extern const struct sigaction sigaction_ignore;
extern const struct sigaction sigaction_default;
+
+int parse_signo(const char *s, int *ret);
diff --git a/src/basic/string-util.c b/src/basic/string-util.c
index 1715d62a39..171a368059 100644
--- a/src/basic/string-util.c
+++ b/src/basic/string-util.c
@@ -698,7 +698,8 @@ char* strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]) {
STATE_OTHER,
STATE_ESCAPE,
STATE_CSI,
- STATE_CSO,
+ STATE_OSC,
+ STATE_OSC_CLOSING,
} state = STATE_OTHER;
_cleanup_(memstream_done) MemStream m = {};
size_t isz, shift[2] = {}, n_carriage_returns = 0;
@@ -711,7 +712,7 @@ char* strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]) {
*
* 1. Replaces TABs by 8 spaces
* 2. Strips ANSI color sequences (a subset of CSI), i.e. ESC '[' … 'm' sequences
- * 3. Strips ANSI operating system sequences (CSO), i.e. ESC ']' … BEL sequences
+ * 3. Strips ANSI operating system sequences (OSC), i.e. ESC ']' … ST sequences
* 4. Strip trailing \r characters (since they would "move the cursor", but have no
* other effect).
*
@@ -719,7 +720,7 @@ char* strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]) {
* are any other special characters. Truncated ANSI sequences are left-as is too. This call is
* supposed to suppress the most basic formatting noise, but nothing else.
*
- * Why care for CSO sequences? Well, to undo what terminal_urlify() and friends generate. */
+ * Why care for OSC sequences? Well, to undo what terminal_urlify() and friends generate. */
isz = _isz ? *_isz : strlen(*ibuf);
@@ -766,8 +767,8 @@ char* strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]) {
} else if (*i == '[') { /* ANSI CSI */
state = STATE_CSI;
begin = i + 1;
- } else if (*i == ']') { /* ANSI CSO */
- state = STATE_CSO;
+ } else if (*i == ']') { /* ANSI OSC */
+ state = STATE_OSC;
begin = i + 1;
} else {
fputc('\x1B', f);
@@ -793,17 +794,35 @@ char* strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]) {
break;
- case STATE_CSO:
+ case STATE_OSC:
assert(n_carriage_returns == 0);
+ /* There are three kinds of OSC terminators: \x07, \x1b\x5c or \x9c. We only support
+ * the first two, because the last one is a valid UTF-8 codepoint and hence creates
+ * an ambiguity (many Terminal emulators refuse to support it as well). */
if (i >= *ibuf + isz || /* EOT … */
- (*i != '\a' && (uint8_t) *i < 32U) || (uint8_t) *i > 126U) { /* … or invalid chars in sequence */
+ (!IN_SET(*i, '\x07', '\x1b') && (uint8_t) *i < 32U) || (uint8_t) *i > 126U) { /* … or invalid chars in sequence */
fputc('\x1B', f);
fputc(']', f);
advance_offsets(i - *ibuf, highlight, shift, 2);
state = STATE_OTHER;
i = begin-1;
- } else if (*i == '\a')
+ } else if (*i == '\x07') /* Single character ST */
+ state = STATE_OTHER;
+ else if (*i == '\x1B')
+ state = STATE_OSC_CLOSING;
+
+ break;
+
+ case STATE_OSC_CLOSING:
+ if (i >= *ibuf + isz || /* EOT … */
+ *i != '\x5c') { /* … or incomplete two-byte ST in sequence */
+ fputc('\x1B', f);
+ fputc(']', f);
+ advance_offsets(i - *ibuf, highlight, shift, 2);
+ state = STATE_OTHER;
+ i = begin-1;
+ } else if (*i == '\x5c')
state = STATE_OTHER;
break;
diff --git a/src/basic/terminal-util.c b/src/basic/terminal-util.c
index 8ded46f493..878c1ec06a 100644
--- a/src/basic/terminal-util.c
+++ b/src/basic/terminal-util.c
@@ -323,7 +323,6 @@ int show_menu(char **x, unsigned n_columns, unsigned width, unsigned percentage)
int open_terminal(const char *name, int mode) {
_cleanup_close_ int fd = -EBADF;
- unsigned c = 0;
/*
* If a TTY is in the process of being closed opening it might cause EIO. This is horribly awful, but
@@ -333,10 +332,9 @@ int open_terminal(const char *name, int mode) {
* https://bugs.launchpad.net/ubuntu/+source/linux/+bug/554172/comments/245
*/
- if ((mode & (O_CREAT|O_PATH|O_DIRECTORY|O_TMPFILE)) != 0)
- return -EINVAL;
+ assert((mode & (O_CREAT|O_PATH|O_DIRECTORY|O_TMPFILE)) == 0);
- for (;;) {
+ for (unsigned c = 0;; c++) {
fd = open(name, mode, 0);
if (fd >= 0)
break;
@@ -346,10 +344,9 @@ int open_terminal(const char *name, int mode) {
/* Max 1s in total */
if (c >= 20)
- return -errno;
+ return -EIO;
(void) usleep_safe(50 * USEC_PER_MSEC);
- c++;
}
if (!isatty_safe(fd))
@@ -1296,16 +1293,16 @@ int ptsname_malloc(int fd, char **ret) {
}
}
-int openpt_allocate(int flags, char **ret_slave) {
+int openpt_allocate(int flags, char **ret_peer_path) {
_cleanup_close_ int fd = -EBADF;
- _cleanup_free_ char *p = NULL;
int r;
fd = posix_openpt(flags|O_NOCTTY|O_CLOEXEC);
if (fd < 0)
return -errno;
- if (ret_slave) {
+ _cleanup_free_ char *p = NULL;
+ if (ret_peer_path) {
r = ptsname_malloc(fd, &p);
if (r < 0)
return r;
@@ -1317,20 +1314,22 @@ int openpt_allocate(int flags, char **ret_slave) {
if (unlockpt(fd) < 0)
return -errno;
- if (ret_slave)
- *ret_slave = TAKE_PTR(p);
+ if (ret_peer_path)
+ *ret_peer_path = TAKE_PTR(p);
return TAKE_FD(fd);
}
static int ptsname_namespace(int pty, char **ret) {
- int no = -1, r;
+ int no = -1;
+
+ assert(pty >= 0);
+ assert(ret);
/* Like ptsname(), but doesn't assume that the path is
* accessible in the local namespace. */
- r = ioctl(pty, TIOCGPTN, &no);
- if (r < 0)
+ if (ioctl(pty, TIOCGPTN, &no) < 0)
return -errno;
if (no < 0)
@@ -1342,10 +1341,9 @@ static int ptsname_namespace(int pty, char **ret) {
return 0;
}
-int openpt_allocate_in_namespace(pid_t pid, int flags, char **ret_slave) {
+int openpt_allocate_in_namespace(pid_t pid, int flags, char **ret_peer_path) {
_cleanup_close_ int pidnsfd = -EBADF, mntnsfd = -EBADF, usernsfd = -EBADF, rootfd = -EBADF, fd = -EBADF;
_cleanup_close_pair_ int pair[2] = EBADF_PAIR;
- pid_t child;
int r;
assert(pid > 0);
@@ -1354,17 +1352,27 @@ int openpt_allocate_in_namespace(pid_t pid, int flags, char **ret_slave) {
if (r < 0)
return r;
- if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
+ if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, pair) < 0)
return -errno;
- r = namespace_fork("(sd-openptns)", "(sd-openpt)", NULL, 0, FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGKILL,
- pidnsfd, mntnsfd, -1, usernsfd, rootfd, &child);
+ r = namespace_fork(
+ "(sd-openptns)",
+ "(sd-openpt)",
+ /* except_fds= */ NULL,
+ /* n_except_fds= */ 0,
+ FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGKILL|FORK_WAIT,
+ pidnsfd,
+ mntnsfd,
+ /* netns_fd= */ -EBADF,
+ usernsfd,
+ rootfd,
+ /* ret_pid= */ NULL);
if (r < 0)
return r;
if (r == 0) {
pair[0] = safe_close(pair[0]);
- fd = openpt_allocate(flags, NULL);
+ fd = openpt_allocate(flags, /* ret_peer_path= */ NULL);
if (fd < 0)
_exit(EXIT_FAILURE);
@@ -1376,18 +1384,12 @@ int openpt_allocate_in_namespace(pid_t pid, int flags, char **ret_slave) {
pair[1] = safe_close(pair[1]);
- r = wait_for_terminate_and_check("(sd-openptns)", child, 0);
- if (r < 0)
- return r;
- if (r != EXIT_SUCCESS)
- return -EIO;
-
fd = receive_one_fd(pair[0], 0);
if (fd < 0)
return fd;
- if (ret_slave) {
- r = ptsname_namespace(fd, ret_slave);
+ if (ret_peer_path) {
+ r = ptsname_namespace(fd, ret_peer_path);
if (r < 0)
return r;
}
@@ -1398,30 +1400,40 @@ int openpt_allocate_in_namespace(pid_t pid, int flags, char **ret_slave) {
int open_terminal_in_namespace(pid_t pid, const char *name, int mode) {
_cleanup_close_ int pidnsfd = -EBADF, mntnsfd = -EBADF, usernsfd = -EBADF, rootfd = -EBADF;
_cleanup_close_pair_ int pair[2] = EBADF_PAIR;
- pid_t child;
int r;
- r = namespace_open(pid, &pidnsfd, &mntnsfd, /* ret_netns_fd = */ NULL, &usernsfd, &rootfd);
+ assert(pid > 0);
+ assert(name);
+
+ r = namespace_open(pid, &pidnsfd, &mntnsfd, /* ret_netns_fd= */ NULL, &usernsfd, &rootfd);
if (r < 0)
return r;
- if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
+ if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, pair) < 0)
return -errno;
- r = namespace_fork("(sd-terminalns)", "(sd-terminal)", NULL, 0, FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGKILL,
- pidnsfd, mntnsfd, -1, usernsfd, rootfd, &child);
+ r = namespace_fork(
+ "(sd-terminalns)",
+ "(sd-terminal)",
+ /* except_fds= */ NULL,
+ /* n_except_fds= */ 0,
+ FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGKILL|FORK_WAIT,
+ pidnsfd,
+ mntnsfd,
+ /* netnsd_fd= */ -EBADF,
+ usernsfd,
+ rootfd,
+ /* ret_pid= */ NULL);
if (r < 0)
return r;
if (r == 0) {
- int master;
-
pair[0] = safe_close(pair[0]);
- master = open_terminal(name, mode|O_NOCTTY|O_CLOEXEC);
- if (master < 0)
+ int pty_fd = open_terminal(name, mode|O_NOCTTY|O_CLOEXEC);
+ if (pty_fd < 0)
_exit(EXIT_FAILURE);
- if (send_one_fd(pair[1], master, 0) < 0)
+ if (send_one_fd(pair[1], pty_fd, 0) < 0)
_exit(EXIT_FAILURE);
_exit(EXIT_SUCCESS);
@@ -1429,12 +1441,6 @@ int open_terminal_in_namespace(pid_t pid, const char *name, int mode) {
pair[1] = safe_close(pair[1]);
- r = wait_for_terminate_and_check("(sd-terminalns)", child, 0);
- if (r < 0)
- return r;
- if (r != EXIT_SUCCESS)
- return -EIO;
-
return receive_one_fd(pair[0], 0);
}
@@ -1923,12 +1929,12 @@ int get_default_background_color(double *ret_red, double *ret_green, double *ret
if (tcsetattr(STDIN_FILENO, TCSADRAIN, &new_termios) < 0)
return -errno;
- r = loop_write(STDOUT_FILENO, "\x1B]11;?\x07", SIZE_MAX);
+ r = loop_write(STDOUT_FILENO, ANSI_OSC "11;?" ANSI_ST, SIZE_MAX);
if (r < 0)
goto finish;
usec_t end = usec_add(now(CLOCK_MONOTONIC), 333 * USEC_PER_MSEC);
- char buf[STRLEN("\x1B]11;rgb:0/0/0\x07")]; /* shortest possible reply */
+ char buf[STRLEN(ANSI_OSC "11;rgb:0/0/0" ANSI_ST)]; /* shortest possible reply */
size_t buf_full = 0;
BackgroundColorContext context = {};
@@ -2278,3 +2284,60 @@ int terminal_is_pty_fd(int fd) {
return true;
}
+
+int pty_open_peer_racefree(int fd, int mode) {
+ assert(fd >= 0);
+
+ /* Opens the peer PTY using the new race-free TIOCGPTPEER ioctl() (kernel 4.13).
+ *
+ * This is safe to be called on TTYs from other namespaces. */
+
+ assert((mode & (O_CREAT|O_PATH|O_DIRECTORY|O_TMPFILE)) == 0);
+
+ /* This replicates the EIO retry logic of open_terminal() in a modified way. */
+ for (unsigned c = 0;; c++) {
+ int peer_fd = ioctl(fd, TIOCGPTPEER, mode);
+ if (peer_fd >= 0)
+ return peer_fd;
+
+ if (ERRNO_IS_NOT_SUPPORTED(errno) || errno == EINVAL) /* new ioctl() is not supported, return a clear error */
+ return -EOPNOTSUPP;
+
+ if (errno != EIO)
+ return -errno;
+
+ /* Max 1s in total */
+ if (c >= 20)
+ return -EIO;
+
+ (void) usleep_safe(50 * USEC_PER_MSEC);
+ }
+}
+
+int pty_open_peer(int fd, int mode) {
+ int r;
+
+ assert(fd >= 0);
+
+ /* Opens the peer PTY using the new race-free TIOCGPTPEER ioctl() (kernel 4.13) if it is
+ * available. Otherwise falls back to the POSIX ptsname() + open() logic.
+ *
+ * Because of the fallback path this is not safe to be called on PTYs from other namespaces. (Because
+ * we open the peer PTY name there via a path in the file system.) */
+
+ // TODO: Remove fallback path once baseline is updated to >= 4.13, i.e. systemd v258
+
+ int peer_fd = pty_open_peer_racefree(fd, mode);
+ if (peer_fd >= 0)
+ return peer_fd;
+ if (!ERRNO_IS_NEG_NOT_SUPPORTED(peer_fd))
+ return peer_fd;
+
+ /* The racy fallback path */
+ _cleanup_free_ char *peer_path = NULL;
+ r = ptsname_malloc(fd, &peer_path);
+ if (r < 0)
+ return r;
+
+ return open_terminal(peer_path, mode);
+}
diff --git a/src/basic/terminal-util.h b/src/basic/terminal-util.h
index b446e547d6..c30faf168c 100644
--- a/src/basic/terminal-util.h
+++ b/src/basic/terminal-util.h
@@ -27,6 +27,16 @@
#define ANSI_WINDOW_TITLE_PUSH "\x1b[22;2t"
#define ANSI_WINDOW_TITLE_POP "\x1b[23;2t"
+/* ANSI "string terminator" character ("ST"). Terminal emulators typically allow three different ones: 0x07,
+ * 0x9c, and 0x1B 0x5C. We'll avoid 0x07 (BEL, aka ^G) since it might trigger unexpected TTY signal
+ * handling. And we'll avoid 0x9c since that's also valid regular codepoint in UTF-8 and elsewhere, and
+ * creates ambiguities. Because of that some terminal emulators explicitly choose not to support it. Hence we
+ * use 0x1B 0x5c */
+#define ANSI_ST "\e\\"
+
+/* The "operating system command" ("OSC") start sequence */
+#define ANSI_OSC "\e]"
+
bool isatty_safe(int fd);
int terminal_reset_defensive(int fd, bool switch_to_text);
@@ -144,3 +154,6 @@ int terminal_get_size_by_dsr(int input_fd, int output_fd, unsigned *ret_rows, un
int terminal_fix_size(int input_fd, int output_fd);
int terminal_is_pty_fd(int fd);
+
+int pty_open_peer_racefree(int fd, int mode);
+int pty_open_peer(int fd, int mode);
diff --git a/src/boot/bootctl-status.c b/src/boot/bootctl-status.c
index 61d76dd679..c548ddcee5 100644
--- a/src/boot/bootctl-status.c
+++ b/src/boot/bootctl-status.c
@@ -843,7 +843,7 @@ int verb_list(int argc, char *argv[], void *userdata) {
if (r < 0)
return r;
- if (config.n_entries == 0 && FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF)) {
+ if (config.n_entries == 0 && !sd_json_format_enabled(arg_json_format_flags)) {
log_info("No boot loader entries found.");
return 0;
}
diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c
index a8c7b6881f..87b0232860 100644
--- a/src/boot/efi/boot.c
+++ b/src/boot/efi/boot.c
@@ -1926,14 +1926,14 @@ static bool is_sd_boot(EFI_FILE *root_dir, const char16_t *loader_path) {
/* profile= */ UINT_MAX,
/* validate_base= */ 0,
&vector);
- if (vector.memory_size != sizeof(SD_MAGIC))
+ if (vector.memory_size != STRLEN(SD_MAGIC))
return false;
err = file_handle_read(handle, vector.file_offset, vector.file_size, &content, &read);
if (err != EFI_SUCCESS || vector.file_size != read)
return false;
- return memcmp(content, SD_MAGIC, sizeof(SD_MAGIC)) == 0;
+ return memcmp(content, SD_MAGIC, STRLEN(SD_MAGIC)) == 0;
}
static BootEntry* config_add_entry_loader_auto(
diff --git a/src/boot/measure.c b/src/boot/measure.c
index 38bc2afda5..3ad49e9d74 100644
--- a/src/boot/measure.c
+++ b/src/boot/measure.c
@@ -748,7 +748,7 @@ static int verb_calculate(int argc, char *argv[], void *userdata) {
return r;
for (size_t i = 0; i < n; i++) {
- if (arg_json_format_flags & SD_JSON_FORMAT_OFF) {
+ if (!sd_json_format_enabled(arg_json_format_flags)) {
_cleanup_free_ char *hd = NULL;
if (i == 0) {
@@ -789,7 +789,7 @@ static int verb_calculate(int argc, char *argv[], void *userdata) {
pcr_states_restore(pcr_states, n);
}
- if (!FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF)) {
+ if (sd_json_format_enabled(arg_json_format_flags)) {
if (arg_json_format_flags & (SD_JSON_FORMAT_PRETTY|SD_JSON_FORMAT_PRETTY_AUTO))
pager_open(arg_pager_flags);
@@ -1106,7 +1106,7 @@ static int verb_status(int argc, char *argv[], void *userdata) {
if (r < 0)
return log_error_errno(r, "Failed to decode PCR value '%s': %m", s);
- if (arg_json_format_flags & SD_JSON_FORMAT_OFF) {
+ if (!sd_json_format_enabled(arg_json_format_flags)) {
_cleanup_free_ char *f = NULL;
f = hexmem(h, l);
@@ -1150,7 +1150,7 @@ static int verb_status(int argc, char *argv[], void *userdata) {
}
}
- if (!FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF)) {
+ if (sd_json_format_enabled(arg_json_format_flags)) {
if (arg_json_format_flags & (SD_JSON_FORMAT_PRETTY|SD_JSON_FORMAT_PRETTY_AUTO))
pager_open(arg_pager_flags);
diff --git a/src/busctl/busctl.c b/src/busctl/busctl.c
index 1e352a3729..8971d8c28f 100644
--- a/src/busctl/busctl.c
+++ b/src/busctl/busctl.c
@@ -65,7 +65,7 @@ static bool arg_augment_creds = true;
static bool arg_watch_bind = false;
static usec_t arg_timeout = 0;
static const char *arg_destination = NULL;
-static uint64_t arg_num_matches = UINT64_MAX;
+static uint64_t arg_limit_messages = UINT64_MAX;
STATIC_DESTRUCTOR_REGISTER(arg_matches, strv_freep);
@@ -1268,6 +1268,9 @@ static int monitor(int argc, char **argv, int (*dump)(sd_bus_message *m, FILE *f
if (r < 0)
return r;
+ usec_t end = arg_timeout > 0 ?
+ usec_add(now(CLOCK_MONOTONIC), arg_timeout) : USEC_INFINITY;
+
/* upgrade connection; it's not used for anything else after this call */
r = sd_bus_message_new_method_call(bus,
&message,
@@ -1331,7 +1334,7 @@ static int monitor(int argc, char **argv, int (*dump)(sd_bus_message *m, FILE *f
if (r < 0)
return log_error_errno(r, "Failed to get unique name: %m");
- if (!arg_quiet && arg_json_format_flags == SD_JSON_FORMAT_OFF)
+ if (!arg_quiet && !sd_json_format_enabled(arg_json_format_flags))
log_info("Monitoring bus message stream.");
(void) sd_notify(/* unset_environment=false */ false, "READY=1");
@@ -1364,14 +1367,18 @@ static int monitor(int argc, char **argv, int (*dump)(sd_bus_message *m, FILE *f
dump(m, stdout);
fflush(stdout);
- if (arg_num_matches != UINT64_MAX && --arg_num_matches == 0) {
- if (!arg_quiet && arg_json_format_flags == SD_JSON_FORMAT_OFF)
- log_info("Received requested number of matching messages, exiting.");
- return 0;
+ if (arg_limit_messages != UINT64_MAX) {
+ arg_limit_messages--;
+
+ if (arg_limit_messages == 0) {
+ if (!arg_quiet && !sd_json_format_enabled(arg_json_format_flags))
+ log_info("Received requested maximum number of messages, exiting.");
+ return 0;
+ }
}
if (sd_bus_message_is_signal(m, "org.freedesktop.DBus.Local", "Disconnected") > 0) {
- if (!arg_quiet && arg_json_format_flags == SD_JSON_FORMAT_OFF)
+ if (!arg_quiet && !sd_json_format_enabled(arg_json_format_flags))
log_info("Connection terminated, exiting.");
return 0;
}
@@ -1382,9 +1389,9 @@ static int monitor(int argc, char **argv, int (*dump)(sd_bus_message *m, FILE *f
if (r > 0)
continue;
- r = sd_bus_wait(bus, arg_timeout > 0 ? arg_timeout : UINT64_MAX);
- if (r == 0 && arg_timeout > 0) {
- if (!arg_quiet && arg_json_format_flags == SD_JSON_FORMAT_OFF)
+ r = sd_bus_wait(bus, arg_timeout > 0 ? usec_sub_unsigned(end, now(CLOCK_MONOTONIC)) : UINT64_MAX);
+ if (r == 0 && arg_timeout > 0 && now(CLOCK_MONOTONIC) >= end) {
+ if (!arg_quiet && !sd_json_format_enabled(arg_json_format_flags))
log_info("Timed out waiting for messages, exiting.");
return 0;
}
@@ -1394,7 +1401,7 @@ static int monitor(int argc, char **argv, int (*dump)(sd_bus_message *m, FILE *f
}
static int verb_monitor(int argc, char **argv, void *userdata) {
- return monitor(argc, argv, (arg_json_format_flags & SD_JSON_FORMAT_OFF) ? message_dump : message_json);
+ return monitor(argc, argv, sd_json_format_enabled(arg_json_format_flags) ? message_json : message_dump);
}
static int verb_capture(int argc, char **argv, void *userdata) {
@@ -2146,7 +2153,7 @@ static int call(int argc, char **argv, void *userdata) {
if (r > 0 || arg_quiet)
return 0;
- if (!FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF)) {
+ if (sd_json_format_enabled(arg_json_format_flags)) {
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
if (arg_json_format_flags & (SD_JSON_FORMAT_PRETTY|SD_JSON_FORMAT_PRETTY_AUTO))
@@ -2256,7 +2263,7 @@ static int get_property(int argc, char **argv, void *userdata) {
if (r < 0)
return bus_log_parse_error(r);
- if (!FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF)) {
+ if (sd_json_format_enabled(arg_json_format_flags)) {
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
if (arg_json_format_flags & (SD_JSON_FORMAT_PRETTY|SD_JSON_FORMAT_PRETTY_AUTO))
@@ -2448,9 +2455,9 @@ static int help(void) {
pager_open(arg_pager_flags);
- printf("%s [OPTIONS...] COMMAND ...\n\n"
- "%sIntrospect the D-Bus IPC bus.%s\n"
- "\nCommands:\n"
+ printf("%1$s [OPTIONS...] COMMAND ...\n\n"
+ "%5$sIntrospect the D-Bus IPC bus.%6$s\n"
+ "\n%3$sCommands%4$s:\n"
" list List bus names\n"
" status [SERVICE] Show bus service, process or bus owner credentials\n"
" monitor [SERVICE...] Show bus traffic\n"
@@ -2468,7 +2475,7 @@ static int help(void) {
" set-property SERVICE OBJECT INTERFACE PROPERTY SIGNATURE ARGUMENT...\n"
" Set property value\n"
" help Show this help\n"
- "\nOptions:\n"
+ "\n%3$sOptions:%4$s\n"
" -h --help Show this help\n"
" --version Show package version\n"
" --no-pager Do not pipe output into a pager\n"
@@ -2500,13 +2507,16 @@ static int help(void) {
" --watch-bind=BOOL Wait for bus AF_UNIX socket to be bound in the file\n"
" system\n"
" --destination=SERVICE Destination service of a signal\n"
- " --num-matches=NUMBER Exit after receiving a number of matches while\n"
- " monitoring\n"
- "\nSee the %s for details.\n",
+ " -N --limit-messages=NUMBER\n"
+ " Stop monitoring after receiving the specified number\n"
+ " of messages\n"
+ "\nSee the %2$s for details.\n",
program_invocation_short_name,
- ansi_highlight(),
+ link,
+ ansi_underline(),
ansi_normal(),
- link);
+ ansi_highlight(),
+ ansi_normal());
return 0;
}
@@ -2541,7 +2551,6 @@ static int parse_argv(int argc, char *argv[]) {
ARG_WATCH_BIND,
ARG_JSON,
ARG_DESTINATION,
- ARG_NUM_MATCHES,
};
static const struct option options[] = {
@@ -2574,7 +2583,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "watch-bind", required_argument, NULL, ARG_WATCH_BIND },
{ "json", required_argument, NULL, ARG_JSON },
{ "destination", required_argument, NULL, ARG_DESTINATION },
- { "num-matches", required_argument, NULL, ARG_NUM_MATCHES },
+ { "limit-messages", required_argument, NULL, 'N' },
{},
};
@@ -2583,7 +2592,7 @@ static int parse_argv(int argc, char *argv[]) {
assert(argc >= 0);
assert(argv);
- while ((c = getopt_long(argc, argv, "hH:M:C:J:qjl", options, NULL)) >= 0)
+ while ((c = getopt_long(argc, argv, "hH:M:C:J:qjlN:", options, NULL)) >= 0)
switch (c) {
@@ -2710,6 +2719,11 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_TIMEOUT:
+ if (isempty(optarg)) {
+ arg_timeout = 0; /* Reset to default */
+ break;
+ }
+
r = parse_sec(optarg, &arg_timeout);
if (r < 0)
return log_error_errno(r, "Failed to parse --timeout= parameter '%s': %m", optarg);
@@ -2743,12 +2757,17 @@ static int parse_argv(int argc, char *argv[]) {
arg_destination = optarg;
break;
- case ARG_NUM_MATCHES:
- r = safe_atou64(optarg, &arg_num_matches);
+ case 'N':
+ if (isempty(optarg)) {
+ arg_limit_messages = UINT64_MAX; /* Reset to default */
+ break;
+ }
+
+ r = safe_atou64(optarg, &arg_limit_messages);
if (r < 0)
- return log_error_errno(r, "Failed to parse --num-matches= parameter '%s': %m", optarg);
- if (arg_num_matches == 0)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--num-matches= parameter cannot be 0");
+ return log_error_errno(r, "Failed to parse --limit-messages= parameter: %s", optarg);
+ if (arg_limit_messages == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--limit-messages= parameter cannot be 0");
break;
diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c
index 9e06785428..a9a73b599b 100644
--- a/src/core/dbus-execute.c
+++ b/src/core/dbus-execute.c
@@ -5,6 +5,7 @@
#include "af-list.h"
#include "alloc-util.h"
#include "bus-get-properties.h"
+#include "bus-unit-util.h"
#include "bus-util.h"
#include "cap-list.h"
#include "capability-util.h"
@@ -61,6 +62,7 @@ static BUS_DEFINE_PROPERTY_GET2(property_get_ioprio_priority, "i", ExecContext,
static BUS_DEFINE_PROPERTY_GET_GLOBAL(property_get_empty_string, "s", NULL);
static BUS_DEFINE_PROPERTY_GET_REF(property_get_private_tmp_ex, "s", PrivateTmp, private_tmp_to_string);
static BUS_DEFINE_PROPERTY_GET_REF(property_get_private_users_ex, "s", PrivateUsers, private_users_to_string);
+static BUS_DEFINE_PROPERTY_GET_REF(property_get_protect_control_groups_ex, "s", ProtectControlGroups, protect_control_groups_to_string);
static BUS_DEFINE_PROPERTY_GET_REF(property_get_syslog_level, "i", int, LOG_PRI);
static BUS_DEFINE_PROPERTY_GET_REF(property_get_syslog_facility, "i", int, LOG_FAC);
static BUS_DEFINE_PROPERTY_GET(property_get_cpu_affinity_from_numa, "b", ExecContext, exec_context_get_cpu_affinity_from_numa);
@@ -980,11 +982,18 @@ static int property_get_exec_dir_symlink(
return r;
FOREACH_ARRAY(i, d->items, d->n_items)
- STRV_FOREACH(dst, i->symlinks) {
- r = sd_bus_message_append(reply, "(sst)", i->path, *dst, UINT64_C(0) /* flags, unused for now */);
+ if (strv_isempty(i->symlinks)) {
+ /* The old exec directory properties cannot represent flags, so list them here with no
+ * destination */
+ r = sd_bus_message_append(reply, "(sst)", i->path, "", (uint64_t) (i->flags & _EXEC_DIRECTORY_FLAGS_PUBLIC));
if (r < 0)
return r;
- }
+ } else
+ STRV_FOREACH(dst, i->symlinks) {
+ r = sd_bus_message_append(reply, "(sst)", i->path, *dst, (uint64_t) (i->flags & _EXEC_DIRECTORY_FLAGS_PUBLIC));
+ if (r < 0)
+ return r;
+ }
return sd_bus_message_close_container(reply);
}
@@ -1043,6 +1052,21 @@ static int property_get_private_users(
return sd_bus_message_append_basic(reply, 'b', &b);
}
+static int property_get_protect_control_groups(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ ProtectControlGroups *p = ASSERT_PTR(userdata);
+ int b = *p != PROTECT_CONTROL_GROUPS_NO;
+
+ return sd_bus_message_append_basic(reply, 'b', &b);
+}
+
const sd_bus_vtable bus_exec_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_PROPERTY("Environment", "as", NULL, offsetof(ExecContext, environment), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -1163,7 +1187,8 @@ const sd_bus_vtable bus_exec_vtable[] = {
SD_BUS_PROPERTY("ProtectKernelTunables", "b", bus_property_get_bool, offsetof(ExecContext, protect_kernel_tunables), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("ProtectKernelModules", "b", bus_property_get_bool, offsetof(ExecContext, protect_kernel_modules), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("ProtectKernelLogs", "b", bus_property_get_bool, offsetof(ExecContext, protect_kernel_logs), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("ProtectControlGroups", "b", bus_property_get_bool, offsetof(ExecContext, protect_control_groups), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("ProtectControlGroups", "b", property_get_protect_control_groups, offsetof(ExecContext, protect_control_groups), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("ProtectControlGroupsEx", "s", property_get_protect_control_groups_ex, offsetof(ExecContext, protect_control_groups), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("PrivateNetwork", "b", bus_property_get_bool, offsetof(ExecContext, private_network), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("PrivateUsers", "b", property_get_private_users, offsetof(ExecContext, private_users), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("PrivateUsersEx", "s", property_get_private_users_ex, offsetof(ExecContext, private_users), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -1909,6 +1934,42 @@ int bus_exec_context_set_transient_property(
return 1;
}
+ if (streq(name, "ProtectControlGroups")) {
+ int v;
+
+ r = sd_bus_message_read(message, "b", &v);
+ if (r < 0)
+ return r;
+
+ if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
+ c->protect_control_groups = v ? PROTECT_CONTROL_GROUPS_YES : PROTECT_CONTROL_GROUPS_NO;
+ (void) unit_write_settingf(u, flags, name, "%s=%s", name, yes_no(v));
+ }
+
+ return 1;
+ }
+
+ if (streq(name, "ProtectControlGroupsEx")) {
+ const char *s;
+ ProtectControlGroups t;
+
+ r = sd_bus_message_read(message, "s", &s);
+ if (r < 0)
+ return r;
+
+ t = protect_control_groups_from_string(s);
+ if (t < 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid %s setting: %s", name, s);
+
+ if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
+ c->protect_control_groups = t;
+ (void) unit_write_settingf(u, flags, name, "ProtectControlGroups=%s",
+ protect_control_groups_to_string(c->protect_control_groups));
+ }
+
+ return 1;
+ }
+
if (streq(name, "PrivateDevices"))
return bus_set_transient_bool(u, name, &c->private_devices, message, flags, error);
@@ -1960,9 +2021,6 @@ int bus_exec_context_set_transient_property(
if (streq(name, "ProtectClock"))
return bus_set_transient_bool(u, name, &c->protect_clock, message, flags, error);
- if (streq(name, "ProtectControlGroups"))
- return bus_set_transient_bool(u, name, &c->protect_control_groups, message, flags, error);
-
if (streq(name, "CPUSchedulingResetOnFork"))
return bus_set_transient_bool(u, name, &c->cpu_sched_reset_on_fork, message, flags, error);
@@ -3394,7 +3452,7 @@ int bus_exec_context_set_transient_property(
_cleanup_free_ char *joined = NULL;
STRV_FOREACH(source, l) {
- r = exec_directory_add(d, *source, NULL);
+ r = exec_directory_add(d, *source, /* symlink= */ NULL, /* flags= */ 0);
if (r < 0)
return log_oom();
}
@@ -3812,7 +3870,7 @@ int bus_exec_context_set_transient_property(
} else if (STR_IN_SET(name, "StateDirectorySymlink", "RuntimeDirectorySymlink", "CacheDirectorySymlink", "LogsDirectorySymlink")) {
char *source, *destination;
ExecDirectory *directory;
- uint64_t symlink_flags; /* No flags for now, reserved for future uses. */
+ uint64_t symlink_flags;
ExecDirectoryType i;
assert_se((i = exec_directory_type_symlink_from_string(name)) >= 0);
@@ -3823,40 +3881,50 @@ int bus_exec_context_set_transient_property(
return r;
while ((r = sd_bus_message_read(message, "(sst)", &source, &destination, &symlink_flags)) > 0) {
+ if ((symlink_flags & ~_EXEC_DIRECTORY_FLAGS_PUBLIC) != 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid 'flags' parameter '%" PRIu64 "'", symlink_flags);
if (!path_is_valid(source))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Source path %s is not valid.", source);
if (path_is_absolute(source))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Source path %s is absolute.", source);
if (!path_is_normalized(source))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Source path %s is not normalized.", source);
- if (!path_is_valid(destination))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Destination path %s is not valid.", destination);
- if (path_is_absolute(destination))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Destination path %s is absolute.", destination);
- if (!path_is_normalized(destination))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Destination path %s is not normalized.", destination);
- if (symlink_flags != 0)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Flags must be zero.");
+ if (isempty(destination))
+ destination = NULL;
+ else {
+ if (!path_is_valid(destination))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Destination path %s is not valid.", destination);
+ if (path_is_absolute(destination))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Destination path %s is absolute.", destination);
+ if (!path_is_normalized(destination))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Destination path %s is not normalized.", destination);
+ }
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
_cleanup_free_ char *destination_escaped = NULL, *source_escaped = NULL;
- r = exec_directory_add(directory, source, destination);
+ r = exec_directory_add(directory, source, destination, symlink_flags);
if (r < 0)
return r;
/* Need to store them in the unit with the escapes, so that they can be parsed again */
source_escaped = xescape(source, ":");
- destination_escaped = xescape(destination, ":");
- if (!source_escaped || !destination_escaped)
+ if (!source_escaped)
return -ENOMEM;
+ if (destination) {
+ destination_escaped = xescape(destination, ":");
+ if (!destination_escaped)
+ return -ENOMEM;
+ }
unit_write_settingf(
u, flags|UNIT_ESCAPE_SPECIFIERS, exec_directory_type_to_string(i),
- "%s=%s:%s",
+ "%s=%s%s%s%s",
exec_directory_type_to_string(i),
source_escaped,
- destination_escaped);
+ destination_escaped || FLAGS_SET(symlink_flags, EXEC_DIRECTORY_READ_ONLY) ? ":" : "",
+ destination_escaped,
+ FLAGS_SET(symlink_flags, EXEC_DIRECTORY_READ_ONLY) ? ":ro" : "");
}
}
if (r < 0)
diff --git a/src/core/exec-invoke.c b/src/core/exec-invoke.c
index c71d6b4c2a..968e9bd7c4 100644
--- a/src/core/exec-invoke.c
+++ b/src/core/exec-invoke.c
@@ -2452,7 +2452,7 @@ static int setup_exec_directory(
goto fail;
}
- if (!i->only_create) {
+ if (!FLAGS_SET(i->flags, EXEC_DIRECTORY_ONLY_CREATE)) {
/* And link it up from the original place.
* Notes
* 1) If a mount namespace is going to be used, then this symlink remains on
@@ -2474,7 +2474,7 @@ static int setup_exec_directory(
} else {
_cleanup_free_ char *target = NULL;
- if (type != EXEC_DIRECTORY_CONFIGURATION &&
+ if (EXEC_DIRECTORY_TYPE_SHALL_CHOWN(type) &&
readlink_and_make_absolute(p, &target) >= 0) {
_cleanup_free_ char *q = NULL, *q_resolved = NULL, *target_resolved = NULL;
@@ -2526,7 +2526,7 @@ static int setup_exec_directory(
if (r != -EEXIST)
goto fail;
- if (type == EXEC_DIRECTORY_CONFIGURATION) {
+ if (!EXEC_DIRECTORY_TYPE_SHALL_CHOWN(type)) {
struct stat st;
/* Don't change the owner/access mode of the configuration directory,
@@ -2643,7 +2643,7 @@ static int compile_bind_mounts(
continue;
FOREACH_ARRAY(i, context->directories[t].items, context->directories[t].n_items)
- n += !i->only_create;
+ n += !FLAGS_SET(i->flags, EXEC_DIRECTORY_ONLY_CREATE) || FLAGS_SET(i->flags, EXEC_DIRECTORY_READ_ONLY);
}
if (n <= 0) {
@@ -2691,8 +2691,10 @@ static int compile_bind_mounts(
_cleanup_free_ char *s = NULL, *d = NULL;
/* When one of the parent directories is in the list, we cannot create the symlink
- * for the child directory. See also the comments in setup_exec_directory(). */
- if (i->only_create)
+ * for the child directory. See also the comments in setup_exec_directory().
+ * But if it needs to be read only, then we have to create a bind mount anyway to
+ * make it so. */
+ if (FLAGS_SET(i->flags, EXEC_DIRECTORY_ONLY_CREATE) && !FLAGS_SET(i->flags, EXEC_DIRECTORY_READ_ONLY))
continue;
if (exec_directory_is_private(context, t))
@@ -2718,6 +2720,7 @@ static int compile_bind_mounts(
.destination = TAKE_PTR(d),
.nosuid = context->dynamic_user, /* don't allow suid/sgid when DynamicUser= is on */
.recursive = true,
+ .read_only = FLAGS_SET(i->flags, EXEC_DIRECTORY_READ_ONLY),
};
}
}
@@ -2766,7 +2769,7 @@ static int compile_symlinks(
if (!exec_directory_is_private(context, dt) ||
exec_context_with_rootfs(context) ||
- i->only_create)
+ FLAGS_SET(i->flags, EXEC_DIRECTORY_ONLY_CREATE))
continue;
private_path = path_join(params->prefix[dt], "private", i->path);
@@ -3098,7 +3101,7 @@ static int apply_mount_namespace(
/* We need to make the pressure path writable even if /sys/fs/cgroups is made read-only, as the
* service will need to write to it in order to start the notifications. */
- if (context->protect_control_groups && memory_pressure_path && !streq(memory_pressure_path, "/dev/null")) {
+ if (exec_is_cgroup_mount_read_only(context, params) && memory_pressure_path && !streq(memory_pressure_path, "/dev/null")) {
read_write_paths_cleanup = strv_copy(context->read_write_paths);
if (!read_write_paths_cleanup)
return -ENOMEM;
@@ -3242,7 +3245,7 @@ static int apply_mount_namespace(
* sandbox inside the mount namespace. */
.ignore_protect_paths = !needs_sandboxing && !context->dynamic_user && root_dir,
- .protect_control_groups = needs_sandboxing && context->protect_control_groups,
+ .protect_control_groups = needs_sandboxing ? exec_get_protect_control_groups(context, params) : PROTECT_CONTROL_GROUPS_NO,
.protect_kernel_tunables = needs_sandboxing && context->protect_kernel_tunables,
.protect_kernel_modules = needs_sandboxing && context->protect_kernel_modules,
.protect_kernel_logs = needs_sandboxing && context->protect_kernel_logs,
@@ -3636,7 +3639,8 @@ static int compile_suggested_paths(const ExecContext *c, const ExecParameters *p
* directories. */
for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++) {
- if (t == EXEC_DIRECTORY_CONFIGURATION)
+
+ if (!EXEC_DIRECTORY_TYPE_SHALL_CHOWN(t))
continue;
if (!p->prefix[t])
@@ -3886,7 +3890,7 @@ static bool exec_context_need_unprivileged_private_users(
context->protect_kernel_tunables ||
context->protect_kernel_modules ||
context->protect_kernel_logs ||
- context->protect_control_groups ||
+ exec_needs_cgroup_mount(context, params) ||
context->protect_clock ||
context->protect_hostname ||
!strv_isempty(context->read_write_paths) ||
@@ -4580,6 +4584,10 @@ int exec_invoke(
}
}
+ /* We need sandboxing if the caller asked us to apply it and the command isn't explicitly excepted
+ * from it. */
+ needs_sandboxing = (params->flags & EXEC_APPLY_SANDBOXING) && !(command->flags & EXEC_COMMAND_FULLY_PRIVILEGED);
+
if (params->cgroup_path) {
/* If delegation is enabled we'll pass ownership of the cgroup to the user of the new process. On cgroup v1
* this is only about systemd's own hierarchy, i.e. not the controller hierarchies, simply because that's not
@@ -4623,6 +4631,18 @@ int exec_invoke(
"Failed to adjust ownership of '%s', ignoring: %m", memory_pressure_path);
memory_pressure_path = mfree(memory_pressure_path);
}
+ /* First we use the current cgroup path to chmod and chown the memory pressure path, then pass the path relative
+ * to the cgroup namespace to environment variables and mounts. If chown/chmod fails, we should not pass memory
+ * pressure path environment variable or read-write mount to the unit. This is why we check if
+ * memory_pressure_path != NULL in the conditional below. */
+ if (memory_pressure_path && needs_sandboxing && exec_needs_cgroup_namespace(context, params)) {
+ memory_pressure_path = mfree(memory_pressure_path);
+ r = cg_get_path("memory", "", "memory.pressure", &memory_pressure_path);
+ if (r < 0) {
+ *exit_status = EXIT_MEMORY;
+ return log_oom();
+ }
+ }
} else if (cgroup_context->memory_pressure_watch == CGROUP_PRESSURE_WATCH_NO) {
memory_pressure_path = strdup("/dev/null"); /* /dev/null is explicit indicator for turning of memory pressure watch */
if (!memory_pressure_path) {
@@ -4709,10 +4729,6 @@ int exec_invoke(
return log_exec_error_errno(context, params, r, "Failed to set up kernel keyring: %m");
}
- /* We need sandboxing if the caller asked us to apply it and the command isn't explicitly excepted
- * from it. */
- needs_sandboxing = (params->flags & EXEC_APPLY_SANDBOXING) && !(command->flags & EXEC_COMMAND_FULLY_PRIVILEGED);
-
/* We need the ambient capability hack, if the caller asked us to apply it and the command is marked
* for it, and the kernel doesn't actually support ambient caps. */
needs_ambient_hack = (params->flags & EXEC_APPLY_SANDBOXING) && (command->flags & EXEC_COMMAND_AMBIENT_MAGIC) && !ambient_capabilities_supported();
@@ -4853,6 +4869,14 @@ int exec_invoke(
log_exec_warning(context, params, "PrivateIPC=yes is configured, but the kernel does not support IPC namespaces, ignoring.");
}
+ if (needs_sandboxing && exec_needs_cgroup_namespace(context, params)) {
+ r = unshare(CLONE_NEWCGROUP);
+ if (r < 0) {
+ *exit_status = EXIT_NAMESPACE;
+ return log_exec_error_errno(context, params, r, "Failed to set up cgroup namespacing: %m");
+ }
+ }
+
if (needs_mount_namespace) {
_cleanup_free_ char *error_path = NULL;
diff --git a/src/core/execute-serialize.c b/src/core/execute-serialize.c
index 1b44c49238..6fa0b21968 100644
--- a/src/core/execute-serialize.c
+++ b/src/core/execute-serialize.c
@@ -1910,7 +1910,7 @@ static int exec_context_serialize(const ExecContext *c, FILE *f) {
if (r < 0)
return r;
- r = serialize_bool_elide(f, "exec-context-protect-control-groups", c->protect_control_groups);
+ r = serialize_item(f, "exec-context-protect-control-groups", protect_control_groups_to_string(c->protect_control_groups));
if (r < 0)
return r;
@@ -1998,7 +1998,10 @@ static int exec_context_serialize(const ExecContext *c, FILE *f) {
if (!strextend(&value, " ", path_escaped))
return log_oom_debug();
- if (!strextend(&value, ":", yes_no(i->only_create)))
+ if (!strextend(&value, ":", yes_no(FLAGS_SET(i->flags, EXEC_DIRECTORY_ONLY_CREATE))))
+ return log_oom_debug();
+
+ if (!strextend(&value, ":", yes_no(FLAGS_SET(i->flags, EXEC_DIRECTORY_READ_ONLY))))
return log_oom_debug();
STRV_FOREACH(d, i->symlinks) {
@@ -2792,7 +2795,7 @@ static int exec_context_deserialize(ExecContext *c, FILE *f) {
return r;
c->protect_clock = r;
} else if ((val = startswith(l, "exec-context-protect-control-groups="))) {
- r = parse_boolean(val);
+ r = protect_control_groups_from_string(val);
if (r < 0)
return r;
c->protect_control_groups = r;
@@ -2893,7 +2896,8 @@ static int exec_context_deserialize(ExecContext *c, FILE *f) {
return r;
for (;;) {
- _cleanup_free_ char *tuple = NULL, *path = NULL, *only_create = NULL;
+ _cleanup_free_ char *tuple = NULL, *path = NULL, *only_create = NULL, *read_only = NULL;
+ ExecDirectoryFlags exec_directory_flags = 0;
const char *p;
/* Use EXTRACT_UNESCAPE_RELAX here, as we unescape the colons in subsequent calls */
@@ -2904,20 +2908,27 @@ static int exec_context_deserialize(ExecContext *c, FILE *f) {
break;
p = tuple;
- r = extract_many_words(&p, ":", EXTRACT_UNESCAPE_SEPARATORS, &path, &only_create);
+ r = extract_many_words(&p, ":", EXTRACT_UNESCAPE_SEPARATORS, &path, &only_create, &read_only);
if (r < 0)
return r;
if (r < 2)
continue;
- r = exec_directory_add(&c->directories[dt], path, NULL);
+ r = parse_boolean(only_create);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ exec_directory_flags |= EXEC_DIRECTORY_ONLY_CREATE;
+
+ r = parse_boolean(read_only);
if (r < 0)
return r;
+ if (r > 0)
+ exec_directory_flags |= EXEC_DIRECTORY_READ_ONLY;
- r = parse_boolean(only_create);
+ r = exec_directory_add(&c->directories[dt], path, /* symlink= */ NULL, exec_directory_flags);
if (r < 0)
return r;
- c->directories[dt].items[c->directories[dt].n_items - 1].only_create = r;
if (isempty(p))
continue;
diff --git a/src/core/execute.c b/src/core/execute.c
index 0c2c278d69..1c41b39a2f 100644
--- a/src/core/execute.c
+++ b/src/core/execute.c
@@ -210,6 +210,50 @@ bool exec_needs_ipc_namespace(const ExecContext *context) {
return context->private_ipc || context->ipc_namespace_path;
}
+static bool can_apply_cgroup_namespace(const ExecContext *context, const ExecParameters *params) {
+ return cg_all_unified() > 0 && ns_type_supported(NAMESPACE_CGROUP);
+}
+
+static bool needs_cgroup_namespace(ProtectControlGroups i) {
+ return IN_SET(i, PROTECT_CONTROL_GROUPS_PRIVATE, PROTECT_CONTROL_GROUPS_STRICT);
+}
+
+ProtectControlGroups exec_get_protect_control_groups(const ExecContext *context, const ExecParameters *params) {
+ assert(context);
+
+ /* If cgroup namespace is configured via ProtectControlGroups=private or strict but we can't actually
+ * use cgroup namespace, either from not having unified hierarchy or kernel support, we ignore the
+ * setting and do not unshare the namespace. ProtectControlGroups=private and strict get downgraded
+ * to no and yes respectively. This ensures that strict always gets a read-only mount of /sys/fs/cgroup.
+ *
+ * TODO: Remove fallback once cgroupv1 support is removed in v258. */
+ if (needs_cgroup_namespace(context->protect_control_groups) && !can_apply_cgroup_namespace(context, params)) {
+ if (context->protect_control_groups == PROTECT_CONTROL_GROUPS_PRIVATE)
+ return PROTECT_CONTROL_GROUPS_NO;
+ if (context->protect_control_groups == PROTECT_CONTROL_GROUPS_STRICT)
+ return PROTECT_CONTROL_GROUPS_YES;
+ }
+ return context->protect_control_groups;
+}
+
+bool exec_needs_cgroup_namespace(const ExecContext *context, const ExecParameters *params) {
+ assert(context);
+
+ return needs_cgroup_namespace(exec_get_protect_control_groups(context, params));
+}
+
+bool exec_needs_cgroup_mount(const ExecContext *context, const ExecParameters *params) {
+ assert(context);
+
+ return exec_get_protect_control_groups(context, params) != PROTECT_CONTROL_GROUPS_NO;
+}
+
+bool exec_is_cgroup_mount_read_only(const ExecContext *context, const ExecParameters *params) {
+ assert(context);
+
+ return IN_SET(exec_get_protect_control_groups(context, params), PROTECT_CONTROL_GROUPS_YES, PROTECT_CONTROL_GROUPS_STRICT);
+}
+
bool exec_needs_mount_namespace(
const ExecContext *context,
const ExecParameters *params,
@@ -259,7 +303,7 @@ bool exec_needs_mount_namespace(
context->protect_kernel_tunables ||
context->protect_kernel_modules ||
context->protect_kernel_logs ||
- context->protect_control_groups ||
+ exec_needs_cgroup_mount(context, params) ||
context->protect_proc != PROTECT_PROC_DEFAULT ||
context->proc_subset != PROC_SUBSET_ALL ||
exec_needs_ipc_namespace(context))
@@ -287,6 +331,11 @@ bool exec_needs_mount_namespace(
if (exec_context_get_effective_bind_log_sockets(context))
return true;
+ for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++)
+ FOREACH_ARRAY(i, context->directories[t].items, context->directories[t].n_items)
+ if (FLAGS_SET(i->flags, EXEC_DIRECTORY_READ_ONLY))
+ return true;
+
return false;
}
@@ -296,7 +345,7 @@ bool exec_directory_is_private(const ExecContext *context, ExecDirectoryType typ
if (!context->dynamic_user)
return false;
- if (type == EXEC_DIRECTORY_CONFIGURATION)
+ if (!EXEC_DIRECTORY_TYPE_SHALL_CHOWN(type))
return false;
if (type == EXEC_DIRECTORY_RUNTIME && context->runtime_directory_preserve_mode == EXEC_PRESERVE_NO)
@@ -1000,7 +1049,7 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
prefix, yes_no(c->protect_kernel_modules),
prefix, yes_no(c->protect_kernel_logs),
prefix, yes_no(c->protect_clock),
- prefix, yes_no(c->protect_control_groups),
+ prefix, protect_control_groups_to_string(c->protect_control_groups),
prefix, yes_no(c->private_network),
prefix, private_users_to_string(c->private_users),
prefix, protect_home_to_string(c->protect_home),
@@ -1074,7 +1123,12 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
fprintf(f, "%s%sMode: %04o\n", prefix, exec_directory_type_to_string(dt), c->directories[dt].mode);
for (size_t i = 0; i < c->directories[dt].n_items; i++) {
- fprintf(f, "%s%s: %s\n", prefix, exec_directory_type_to_string(dt), c->directories[dt].items[i].path);
+ fprintf(f,
+ "%s%s: %s%s\n",
+ prefix,
+ exec_directory_type_to_string(dt),
+ c->directories[dt].items[i].path,
+ FLAGS_SET(c->directories[dt].items[i].flags, EXEC_DIRECTORY_READ_ONLY) ? " (ro)" : "");
STRV_FOREACH(d, c->directories[dt].items[i].symlinks)
fprintf(f, "%s%s: %s:%s\n", prefix, exec_directory_type_symlink_to_string(dt), c->directories[dt].items[i].path, *d);
@@ -1595,7 +1649,7 @@ int exec_context_get_clean_directories(
return r;
/* Also remove private directories unconditionally. */
- if (t != EXEC_DIRECTORY_CONFIGURATION) {
+ if (EXEC_DIRECTORY_TYPE_SHALL_CHOWN(t)) {
j = path_join(prefix[t], "private", i->path);
if (!j)
return -ENOMEM;
@@ -2687,7 +2741,7 @@ static ExecDirectoryItem *exec_directory_find(ExecDirectory *d, const char *path
return NULL;
}
-int exec_directory_add(ExecDirectory *d, const char *path, const char *symlink) {
+int exec_directory_add(ExecDirectory *d, const char *path, const char *symlink, ExecDirectoryFlags flags) {
_cleanup_strv_free_ char **s = NULL;
_cleanup_free_ char *p = NULL;
ExecDirectoryItem *existing;
@@ -2702,6 +2756,8 @@ int exec_directory_add(ExecDirectory *d, const char *path, const char *symlink)
if (r < 0)
return r;
+ existing->flags |= flags;
+
return 0; /* existing item is updated */
}
@@ -2721,6 +2777,7 @@ int exec_directory_add(ExecDirectory *d, const char *path, const char *symlink)
d->items[d->n_items++] = (ExecDirectoryItem) {
.path = TAKE_PTR(p),
.symlinks = TAKE_PTR(s),
+ .flags = flags,
};
return 1; /* new item is added */
@@ -2738,7 +2795,7 @@ void exec_directory_sort(ExecDirectory *d) {
/* Sort the exec directories to make always parent directories processed at first in
* setup_exec_directory(), e.g., even if StateDirectory=foo/bar foo, we need to create foo at first,
- * then foo/bar. Also, set .only_create flag if one of the parent directories is contained in the
+ * then foo/bar. Also, set the ONLY_CREATE flag if one of the parent directories is contained in the
* list. See also comments in setup_exec_directory() and issue #24783. */
if (d->n_items <= 1)
@@ -2749,7 +2806,7 @@ void exec_directory_sort(ExecDirectory *d) {
for (size_t i = 1; i < d->n_items; i++)
for (size_t j = 0; j < i; j++)
if (path_startswith(d->items[i].path, d->items[j].path)) {
- d->items[i].only_create = true;
+ d->items[i].flags |= EXEC_DIRECTORY_ONLY_CREATE;
break;
}
}
diff --git a/src/core/execute.h b/src/core/execute.h
index 29c08d48b9..2f5abc9785 100644
--- a/src/core/execute.h
+++ b/src/core/execute.h
@@ -15,6 +15,7 @@ typedef struct Manager Manager;
#include <stdio.h>
#include <sys/capability.h>
+#include "bus-unit-util.h"
#include "cgroup-util.h"
#include "coredump-util.h"
#include "cpu-set-util.h"
@@ -152,10 +153,16 @@ typedef enum ExecDirectoryType {
_EXEC_DIRECTORY_TYPE_INVALID = -EINVAL,
} ExecDirectoryType;
+static inline bool EXEC_DIRECTORY_TYPE_SHALL_CHOWN(ExecDirectoryType t) {
+ /* Returns true for the ExecDirectoryTypes that we shall chown()ing for the user to. We do this for
+ * all of them, except for configuration */
+ return t >= 0 && t < _EXEC_DIRECTORY_TYPE_MAX && t != EXEC_DIRECTORY_CONFIGURATION;
+}
+
typedef struct ExecDirectoryItem {
char *path;
char **symlinks;
- bool only_create;
+ ExecDirectoryFlags flags;
} ExecDirectoryItem;
typedef struct ExecDirectory {
@@ -324,7 +331,7 @@ struct ExecContext {
bool protect_kernel_modules;
bool protect_kernel_logs;
bool protect_clock;
- bool protect_control_groups;
+ ProtectControlGroups protect_control_groups;
ProtectSystem protect_system;
ProtectHome protect_home;
bool protect_hostname;
@@ -579,7 +586,7 @@ void exec_params_deep_clear(ExecParameters *p);
bool exec_context_get_cpu_affinity_from_numa(const ExecContext *c);
void exec_directory_done(ExecDirectory *d);
-int exec_directory_add(ExecDirectory *d, const char *path, const char *symlink);
+int exec_directory_add(ExecDirectory *d, const char *path, const char *symlink, ExecDirectoryFlags flags);
void exec_directory_sort(ExecDirectory *d);
bool exec_directory_is_private(const ExecContext *context, ExecDirectoryType type);
@@ -616,6 +623,11 @@ bool exec_needs_mount_namespace(const ExecContext *context, const ExecParameters
bool exec_needs_network_namespace(const ExecContext *context);
bool exec_needs_ipc_namespace(const ExecContext *context);
+ProtectControlGroups exec_get_protect_control_groups(const ExecContext *context, const ExecParameters *params);
+bool exec_needs_cgroup_namespace(const ExecContext *context, const ExecParameters *params);
+bool exec_needs_cgroup_mount(const ExecContext *context, const ExecParameters *params);
+bool exec_is_cgroup_mount_read_only(const ExecContext *context, const ExecParameters *params);
+
/* These logging macros do the same logging as those in unit.h, but using ExecContext and ExecParameters
* instead of the unit object, so that it can be used in the sd-executor context (where the unit object is
* not available). */
diff --git a/src/core/load-fragment-gperf.gperf.in b/src/core/load-fragment-gperf.gperf.in
index ea82a578ba..f5cbb319d7 100644
--- a/src/core/load-fragment-gperf.gperf.in
+++ b/src/core/load-fragment-gperf.gperf.in
@@ -125,7 +125,7 @@
{{type}}.ProtectKernelModules, config_parse_bool, 0, offsetof({{type}}, exec_context.protect_kernel_modules)
{{type}}.ProtectKernelLogs, config_parse_bool, 0, offsetof({{type}}, exec_context.protect_kernel_logs)
{{type}}.ProtectClock, config_parse_bool, 0, offsetof({{type}}, exec_context.protect_clock)
-{{type}}.ProtectControlGroups, config_parse_bool, 0, offsetof({{type}}, exec_context.protect_control_groups)
+{{type}}.ProtectControlGroups, config_parse_protect_control_groups, 0, offsetof({{type}}, exec_context.protect_control_groups)
{{type}}.NetworkNamespacePath, config_parse_unit_path_printf, 0, offsetof({{type}}, exec_context.network_namespace_path)
{{type}}.IPCNamespacePath, config_parse_unit_path_printf, 0, offsetof({{type}}, exec_context.ipc_namespace_path)
{{type}}.LogNamespace, config_parse_log_namespace, 0, offsetof({{type}}, exec_context)
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
index fa8909e1d1..1d813332b1 100644
--- a/src/core/load-fragment.c
+++ b/src/core/load-fragment.c
@@ -135,6 +135,7 @@ DEFINE_CONFIG_PARSE_ENUM(config_parse_protect_proc, protect_proc, ProtectProc);
DEFINE_CONFIG_PARSE_ENUM(config_parse_proc_subset, proc_subset, ProcSubset);
DEFINE_CONFIG_PARSE_ENUM(config_parse_private_tmp, private_tmp, PrivateTmp);
DEFINE_CONFIG_PARSE_ENUM(config_parse_private_users, private_users, PrivateUsers);
+DEFINE_CONFIG_PARSE_ENUM(config_parse_protect_control_groups, protect_control_groups, ProtectControlGroups);
DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_utmp_mode, exec_utmp_mode, ExecUtmpMode);
DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode, job_mode, JobMode);
DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access, notify_access, NotifyAccess);
@@ -4697,9 +4698,9 @@ int config_parse_exec_directories(
if (r == 0)
return 0;
- _cleanup_free_ char *src = NULL, *dest = NULL;
+ _cleanup_free_ char *src = NULL, *dest = NULL, *flags = NULL;
const char *q = tuple;
- r = extract_many_words(&q, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS, &src, &dest);
+ r = extract_many_words(&q, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS|EXTRACT_DONT_COALESCE_SEPARATORS, &src, &dest, &flags);
if (r == -ENOMEM)
return log_oom();
if (r <= 0) {
@@ -4726,20 +4727,20 @@ int config_parse_exec_directories(
continue;
}
+ if (!isempty(dest) && streq(lvalue, "ConfigurationDirectory")) {
+ log_syntax(unit, LOG_WARNING, filename, line, 0,
+ "Additional parameter is not supported for ConfigurationDirectory, ignoring: %s", tuple);
+ continue;
+ }
+
/* For State and Runtime directories we support an optional destination parameter, which
* will be used to create a symlink to the source. */
_cleanup_free_ char *dresolved = NULL;
if (!isempty(dest)) {
- if (streq(lvalue, "ConfigurationDirectory")) {
- log_syntax(unit, LOG_WARNING, filename, line, 0,
- "Destination parameter is not supported for ConfigurationDirectory, ignoring: %s", tuple);
- continue;
- }
-
r = unit_path_printf(u, dest, &dresolved);
if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r,
- "Failed to resolve unit specifiers in \"%s\", ignoring: %m", dest);
+ "Failed to resolve unit specifiers in \"%s\", ignoring: %m", dest);
continue;
}
@@ -4748,7 +4749,14 @@ int config_parse_exec_directories(
continue;
}
- r = exec_directory_add(ed, sresolved, dresolved);
+ ExecDirectoryFlags exec_directory_flags = exec_directory_flags_from_string(flags);
+ if (exec_directory_flags < 0 || (exec_directory_flags & ~_EXEC_DIRECTORY_FLAGS_PUBLIC) != 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, 0,
+ "Invalid flags for %s=, ignoring: %s", lvalue, flags);
+ continue;
+ }
+
+ r = exec_directory_add(ed, sresolved, dresolved, exec_directory_flags);
if (r < 0)
return log_oom();
}
diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h
index e8b2eaee52..9b95f0c24e 100644
--- a/src/core/load-fragment.h
+++ b/src/core/load-fragment.h
@@ -114,6 +114,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_namespace_path_strv);
CONFIG_PARSER_PROTOTYPE(config_parse_temporary_filesystems);
CONFIG_PARSER_PROTOTYPE(config_parse_private_tmp);
CONFIG_PARSER_PROTOTYPE(config_parse_private_users);
+CONFIG_PARSER_PROTOTYPE(config_parse_protect_control_groups);
CONFIG_PARSER_PROTOTYPE(config_parse_cpu_quota);
CONFIG_PARSER_PROTOTYPE(config_parse_allowed_cpuset);
CONFIG_PARSER_PROTOTYPE(config_parse_protect_home);
diff --git a/src/core/mount.c b/src/core/mount.c
index 19dd09c58f..92e0d7737a 100644
--- a/src/core/mount.c
+++ b/src/core/mount.c
@@ -876,6 +876,9 @@ static int mount_spawn(Mount *m, ExecCommand *c, PidRef *ret_pid) {
if (r < 0)
return r;
+ /* Assume the label inherited from systemd as the fallback */
+ exec_params.fallback_smack_process_label = NULL;
+
r = exec_spawn(UNIT(m),
c,
&m->exec_context,
diff --git a/src/core/namespace.c b/src/core/namespace.c
index c45be457a8..a0d3dc0cbb 100644
--- a/src/core/namespace.c
+++ b/src/core/namespace.c
@@ -65,6 +65,7 @@ typedef enum MountMode {
MOUNT_PRIVATE_SYSFS,
MOUNT_BIND_SYSFS,
MOUNT_PROCFS,
+ MOUNT_PRIVATE_CGROUP2FS,
MOUNT_READ_ONLY,
MOUNT_READ_WRITE,
MOUNT_NOEXEC,
@@ -199,6 +200,22 @@ static const MountEntry protect_home_yes_table[] = {
{ "/root", MOUNT_INACCESSIBLE, true },
};
+/* ProtectControlGroups=yes table */
+static const MountEntry protect_control_groups_yes_table[] = {
+ { "/sys/fs/cgroup", MOUNT_READ_ONLY, false },
+};
+
+/* ProtectControlGroups=private table. Note mount_private_apivfs() always use MS_NOSUID|MS_NOEXEC|MS_NODEV so
+ * flags is not set here. nsdelegate has been supported since kernels >= 4.13 so it is safe to use. */
+static const MountEntry protect_control_groups_private_table[] = {
+ { "/sys/fs/cgroup", MOUNT_PRIVATE_CGROUP2FS, false, .read_only = false, .nosuid = true, .noexec = true, .options_const = "nsdelegate" },
+};
+
+/* ProtectControlGroups=strict table */
+static const MountEntry protect_control_groups_strict_table[] = {
+ { "/sys/fs/cgroup", MOUNT_PRIVATE_CGROUP2FS, false, .read_only = true, .nosuid = true, .noexec = true, .options_const = "nsdelegate" },
+};
+
/* ProtectSystem=yes table */
static const MountEntry protect_system_yes_table[] = {
{ "/usr", MOUNT_READ_ONLY, false },
@@ -247,6 +264,7 @@ static const char * const mount_mode_table[_MOUNT_MODE_MAX] = {
[MOUNT_EMPTY_DIR] = "empty-dir",
[MOUNT_PRIVATE_SYSFS] = "private-sysfs",
[MOUNT_BIND_SYSFS] = "bind-sysfs",
+ [MOUNT_PRIVATE_CGROUP2FS] = "private-cgroup2fs",
[MOUNT_PROCFS] = "procfs",
[MOUNT_READ_ONLY] = "read-only",
[MOUNT_READ_WRITE] = "read-write",
@@ -727,6 +745,28 @@ static int append_static_mounts(MountList *ml, const MountEntry *mounts, size_t
return 0;
}
+static int append_protect_control_groups(MountList *ml, ProtectControlGroups protect_control_groups, bool ignore_protect) {
+ assert(ml);
+
+ switch (protect_control_groups) {
+
+ case PROTECT_CONTROL_GROUPS_NO:
+ return 0;
+
+ case PROTECT_CONTROL_GROUPS_YES:
+ return append_static_mounts(ml, protect_control_groups_yes_table, ELEMENTSOF(protect_control_groups_yes_table), ignore_protect);
+
+ case PROTECT_CONTROL_GROUPS_PRIVATE:
+ return append_static_mounts(ml, protect_control_groups_private_table, ELEMENTSOF(protect_control_groups_private_table), ignore_protect);
+
+ case PROTECT_CONTROL_GROUPS_STRICT:
+ return append_static_mounts(ml, protect_control_groups_strict_table, ELEMENTSOF(protect_control_groups_strict_table), ignore_protect);
+
+ default:
+ assert_not_reached();
+ }
+}
+
static int append_protect_home(MountList *ml, ProtectHome protect_home, bool ignore_protect) {
assert(ml);
@@ -1269,10 +1309,14 @@ static int mount_private_apivfs(
r = mount_nofollow_verbose(LOG_DEBUG, fstype, temporary_mount, fstype, MS_NOSUID|MS_NOEXEC|MS_NODEV, opts);
if (r == -EINVAL && opts)
- /* If this failed with EINVAL then this likely means the textual hidepid= stuff for procfs is
- * not supported by the kernel, and thus the per-instance hidepid= neither, which means we
- * really don't want to use it, since it would affect our host's /proc mount. Hence let's
- * gracefully fallback to a classic, unrestricted version. */
+ /* If this failed with EINVAL then this likely means either:
+ * 1. the textual hidepid= stuff for procfs is not supported by the kernel, and thus the
+ * per-instance hidepid= neither, which means we really don't want to use it, since it
+ * would affect our host's /proc mount.
+ * 2. nsdelegate for cgroup2 is not supported by the kernel even though CLONE_NEWCGROUP
+ * is supported.
+ *
+ * Hence let's gracefully fallback to a classic, unrestricted version. */
r = mount_nofollow_verbose(LOG_DEBUG, fstype, temporary_mount, fstype, MS_NOSUID|MS_NOEXEC|MS_NODEV, /* opts = */ NULL);
if (ERRNO_IS_NEG_PRIVILEGE(r)) {
/* When we do not have enough privileges to mount a new instance, fall back to use an
@@ -1318,6 +1362,39 @@ static int mount_private_sysfs(const MountEntry *m, const NamespaceParameters *p
return mount_private_apivfs("sysfs", mount_entry_path(m), "/sys", /* opts = */ NULL, p->runtime_scope);
}
+static bool check_recursiveprot_supported(void) {
+ int r;
+
+ /* memory_recursiveprot is only supported for kernels >= 5.7. Note mount_option_supported uses fsopen()
+ * and fsconfig() which are supported for kernels >= 5.2. So if mount_option_supported() returns an
+ * error, we can assume memory_recursiveprot is not supported. */
+ r = mount_option_supported("cgroup2", "memory_recursiveprot", NULL);
+ if (r < 0)
+ log_debug_errno(r, "Failed to determine whether the 'memory_recursiveprot' mount option is supported, assuming not: %m");
+ else if (r == 0)
+ log_debug("This kernel version does not support 'memory_recursiveprot', not using mount option.");
+
+ return r > 0;
+}
+
+static int mount_private_cgroup2fs(const MountEntry *m, const NamespaceParameters *p) {
+ _cleanup_free_ char *opts = NULL;
+
+ assert(m);
+ assert(p);
+
+ if (check_recursiveprot_supported()) {
+ opts = strdup(strempty(mount_entry_options(m)));
+ if (!opts)
+ return -ENOMEM;
+
+ if (!strextend_with_separator(&opts, ",", "memory_recursiveprot"))
+ return -ENOMEM;
+ }
+
+ return mount_private_apivfs("cgroup2", mount_entry_path(m), "/sys/fs/cgroup", opts ?: mount_entry_options(m), p->runtime_scope);
+}
+
static int mount_procfs(const MountEntry *m, const NamespaceParameters *p) {
_cleanup_free_ char *opts = NULL;
@@ -1763,6 +1840,9 @@ static int apply_one_mount(
case MOUNT_PROCFS:
return mount_procfs(m, p);
+ case MOUNT_PRIVATE_CGROUP2FS:
+ return mount_private_cgroup2fs(m, p);
+
case MOUNT_RUN:
return mount_run(m);
@@ -1933,7 +2013,7 @@ static bool namespace_parameters_mount_apivfs(const NamespaceParameters *p) {
*/
return p->mount_apivfs ||
- p->protect_control_groups ||
+ p->protect_control_groups != PROTECT_CONTROL_GROUPS_NO ||
p->protect_kernel_tunables ||
p->protect_proc != PROTECT_PROC_DEFAULT ||
p->proc_subset != PROC_SUBSET_ALL;
@@ -2490,16 +2570,9 @@ int setup_namespace(const NamespaceParameters *p, char **reterr_path) {
return r;
}
- if (p->protect_control_groups) {
- MountEntry *me = mount_list_extend(&ml);
- if (!me)
- return log_oom_debug();
-
- *me = (MountEntry) {
- .path_const = "/sys/fs/cgroup",
- .mode = MOUNT_READ_ONLY,
- };
- }
+ r = append_protect_control_groups(&ml, p->protect_control_groups, false);
+ if (r < 0)
+ return r;
r = append_protect_home(&ml, p->protect_home, p->ignore_protect_paths);
if (r < 0)
@@ -3195,6 +3268,15 @@ static const char *const protect_system_table[_PROTECT_SYSTEM_MAX] = {
DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(protect_system, ProtectSystem, PROTECT_SYSTEM_YES);
+static const char *const protect_control_groups_table[_PROTECT_CONTROL_GROUPS_MAX] = {
+ [PROTECT_CONTROL_GROUPS_NO] = "no",
+ [PROTECT_CONTROL_GROUPS_YES] = "yes",
+ [PROTECT_CONTROL_GROUPS_PRIVATE] = "private",
+ [PROTECT_CONTROL_GROUPS_STRICT] = "strict",
+};
+
+DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(protect_control_groups, ProtectControlGroups, PROTECT_CONTROL_GROUPS_YES);
+
static const char* const namespace_type_table[] = {
[NAMESPACE_MOUNT] = "mnt",
[NAMESPACE_CGROUP] = "cgroup",
diff --git a/src/core/namespace.h b/src/core/namespace.h
index 2c8f7987fe..fa4f7c7cf6 100644
--- a/src/core/namespace.h
+++ b/src/core/namespace.h
@@ -69,6 +69,15 @@ typedef enum PrivateUsers {
_PRIVATE_USERS_INVALID = -EINVAL,
} PrivateUsers;
+typedef enum ProtectControlGroups {
+ PROTECT_CONTROL_GROUPS_NO,
+ PROTECT_CONTROL_GROUPS_YES,
+ PROTECT_CONTROL_GROUPS_PRIVATE,
+ PROTECT_CONTROL_GROUPS_STRICT,
+ _PROTECT_CONTROL_GROUPS_MAX,
+ _PROTECT_CONTROL_GROUPS_INVALID = -EINVAL,
+} ProtectControlGroups;
+
struct BindMount {
char *source;
char *destination;
@@ -151,7 +160,6 @@ struct NamespaceParameters {
bool ignore_protect_paths;
- bool protect_control_groups;
bool protect_kernel_tunables;
bool protect_kernel_modules;
bool protect_kernel_logs;
@@ -165,6 +173,7 @@ struct NamespaceParameters {
bool bind_log_sockets;
bool mount_nosuid;
+ ProtectControlGroups protect_control_groups;
ProtectHome protect_home;
ProtectSystem protect_system;
ProtectProc protect_proc;
@@ -210,6 +219,9 @@ PrivateTmp private_tmp_from_string(const char *s) _pure_;
const char* private_users_to_string(PrivateUsers i) _const_;
PrivateUsers private_users_from_string(const char *s) _pure_;
+const char* protect_control_groups_to_string(ProtectControlGroups i) _const_;
+ProtectControlGroups protect_control_groups_from_string(const char *s) _pure_;
+
void bind_mount_free_many(BindMount *b, size_t n);
int bind_mount_add(BindMount **b, size_t *n, const BindMount *item);
diff --git a/src/core/service.c b/src/core/service.c
index 8cea17f853..737dc9905a 100644
--- a/src/core/service.c
+++ b/src/core/service.c
@@ -4551,6 +4551,72 @@ static bool service_notify_message_authorized(Service *s, PidRef *pid) {
}
}
+static int service_notify_message_parse_new_pid(
+ Unit *u,
+ char * const *tags,
+ FDSet *fds,
+ PidRef *ret) {
+
+ _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
+ const char *e;
+ int r;
+
+ assert(u);
+ assert(ret);
+
+ /* MAINPIDFD=1 always takes precedence */
+ if (strv_contains(tags, "MAINPIDFD=1")) {
+ unsigned n_fds = fdset_size(fds);
+ if (n_fds != 1)
+ return log_unit_warning_errno(u, SYNTHETIC_ERRNO(EINVAL),
+ "Got MAINPIDFD=1 with %s fd, ignoring.", n_fds == 0 ? "no" : "more than one");
+
+ r = pidref_set_pidfd_consume(&pidref, ASSERT_FD(fdset_steal_first(fds)));
+ if (r < 0)
+ return log_unit_warning_errno(u, r, "Failed to create reference to received new main pidfd: %m");
+
+ goto finish;
+ }
+
+ e = strv_find_startswith(tags, "MAINPID=");
+ if (!e) {
+ *ret = PIDREF_NULL;
+ return 0;
+ }
+
+ r = pidref_set_pidstr(&pidref, e);
+ if (r < 0)
+ return log_unit_warning_errno(u, r, "Failed to parse MAINPID=%s field in notification message, ignoring: %m", e);
+
+ e = strv_find_startswith(tags, "MAINPIDFDID=");
+ if (!e)
+ goto finish;
+
+ uint64_t pidfd_id;
+
+ r = safe_atou64(e, &pidfd_id);
+ if (r < 0)
+ return log_unit_warning_errno(u, r, "Failed to parse MAINPIDFDID= in notification message, refusing: %s", e);
+
+ r = pidref_acquire_pidfd_id(&pidref);
+ if (r < 0) {
+ if (!ERRNO_IS_NEG_NOT_SUPPORTED(r))
+ log_unit_warning_errno(u, r,
+ "Failed to acquire pidfd id of process " PID_FMT ", not validating MAINPIDFDID=%" PRIu64 ": %m",
+ pidref.pid, pidfd_id);
+ goto finish;
+ }
+
+ if (pidref.fd_id != pidfd_id)
+ return log_unit_warning_errno(u, SYNTHETIC_ERRNO(ESRCH),
+ "PIDFD ID of process " PID_FMT " (%" PRIu64 ") mismatches with received MAINPIDFDID=%" PRIu64 ", not changing main PID.",
+ pidref.pid, pidref.fd_id, pidfd_id);
+
+finish:
+ *ret = TAKE_PIDREF(pidref);
+ return 1;
+}
+
static void service_notify_message(
Unit *u,
PidRef *pidref,
@@ -4576,38 +4642,34 @@ static void service_notify_message(
bool notify_dbus = false;
const char *e;
- /* Interpret MAINPID= */
- e = strv_find_startswith(tags, "MAINPID=");
- if (e && IN_SET(s->state, SERVICE_START, SERVICE_START_POST, SERVICE_RUNNING,
- SERVICE_RELOAD, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY,
- SERVICE_STOP, SERVICE_STOP_SIGTERM)) {
-
- _cleanup_(pidref_done) PidRef new_main_pid = PIDREF_NULL;
+ /* Interpret MAINPID= (+ MAINPIDFDID=) / MAINPIDFD=1 */
+ _cleanup_(pidref_done) PidRef new_main_pid = PIDREF_NULL;
- r = pidref_set_pidstr(&new_main_pid, e);
- if (r < 0)
- log_unit_warning_errno(u, r, "Failed to parse MAINPID=%s field in notification message, ignoring: %m", e);
- else if (!s->main_pid_known || !pidref_equal(&new_main_pid, &s->main_pid)) {
+ r = service_notify_message_parse_new_pid(u, tags, fds, &new_main_pid);
+ if (r > 0 &&
+ IN_SET(s->state, SERVICE_START, SERVICE_START_POST, SERVICE_RUNNING,
+ SERVICE_RELOAD, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY,
+ SERVICE_STOP, SERVICE_STOP_SIGTERM) &&
+ (!s->main_pid_known || !pidref_equal(&new_main_pid, &s->main_pid))) {
- r = service_is_suitable_main_pid(s, &new_main_pid, LOG_WARNING);
- if (r == 0) {
- /* The new main PID is a bit suspicious, which is OK if the sender is privileged. */
+ r = service_is_suitable_main_pid(s, &new_main_pid, LOG_WARNING);
+ if (r == 0) {
+ /* The new main PID is a bit suspicious, which is OK if the sender is privileged. */
- if (ucred->uid == 0) {
- log_unit_debug(u, "New main PID "PID_FMT" does not belong to service, but we'll accept it as the request to change it came from a privileged process.", new_main_pid.pid);
- r = 1;
- } else
- log_unit_warning(u, "New main PID "PID_FMT" does not belong to service, refusing.", new_main_pid.pid);
- }
- if (r > 0) {
- (void) service_set_main_pidref(s, TAKE_PIDREF(new_main_pid), /* start_timestamp = */ NULL);
+ if (ucred->uid == 0) {
+ log_unit_debug(u, "New main PID "PID_FMT" does not belong to service, but we'll accept it as the request to change it came from a privileged process.", new_main_pid.pid);
+ r = 1;
+ } else
+ log_unit_warning(u, "New main PID "PID_FMT" does not belong to service, refusing.", new_main_pid.pid);
+ }
+ if (r > 0) {
+ (void) service_set_main_pidref(s, TAKE_PIDREF(new_main_pid), /* start_timestamp = */ NULL);
- r = unit_watch_pidref(UNIT(s), &s->main_pid, /* exclusive= */ false);
- if (r < 0)
- log_unit_warning_errno(UNIT(s), r, "Failed to watch new main PID "PID_FMT" for service: %m", s->main_pid.pid);
+ r = unit_watch_pidref(UNIT(s), &s->main_pid, /* exclusive= */ false);
+ if (r < 0)
+ log_unit_warning_errno(UNIT(s), r, "Failed to watch new main PID "PID_FMT" for service: %m", s->main_pid.pid);
- notify_dbus = true;
- }
+ notify_dbus = true;
}
}
diff --git a/src/core/socket.c b/src/core/socket.c
index 9b99ab2c35..e4a19285c9 100644
--- a/src/core/socket.c
+++ b/src/core/socket.c
@@ -2024,6 +2024,14 @@ static int socket_chown(Socket *s, PidRef *ret_pid) {
path = socket_address_get_path(&p->address);
else if (p->type == SOCKET_FIFO)
path = p->path;
+ else if (p->type == SOCKET_MQUEUE) {
+ /* Use fchown on the fd since /dev/mqueue might not be mounted. */
+ if (fchown(p->fd, uid, gid) < 0) {
+ log_unit_error_errno(UNIT(s), errno, "Failed to fchown(): %m");
+ _exit(EXIT_CHOWN);
+ }
+ continue;
+ }
if (!path)
continue;
diff --git a/src/core/swap.c b/src/core/swap.c
index ff6c4255ab..312db05db5 100644
--- a/src/core/swap.c
+++ b/src/core/swap.c
@@ -642,6 +642,9 @@ static int swap_spawn(Swap *s, ExecCommand *c, PidRef *ret_pid) {
if (r < 0)
return r;
+ /* Assume the label inherited from systemd as the fallback */
+ exec_params.fallback_smack_process_label = NULL;
+
r = exec_spawn(UNIT(s),
c,
&s->exec_context,
diff --git a/src/coredump/coredump.c b/src/coredump/coredump.c
index 722b5b7529..6970a6a898 100644
--- a/src/coredump/coredump.c
+++ b/src/coredump/coredump.c
@@ -2,11 +2,15 @@
#include <errno.h>
#include <stdio.h>
+#include <sys/mount.h>
#include <sys/prctl.h>
#include <sys/statvfs.h>
#include <sys/auxv.h>
#include <sys/xattr.h>
#include <unistd.h>
+#if WANT_LINUX_FS_H
+# include <linux/fs.h>
+#endif
#include "sd-daemon.h"
#include "sd-journal.h"
@@ -86,6 +90,8 @@
* size. See DATA_SIZE_MAX in journal-importer.h. */
assert_cc(JOURNAL_SIZE_MAX <= DATA_SIZE_MAX);
+#define MOUNT_TREE_ROOT "/run/systemd/mount-rootfs"
+
enum {
/* We use these as array indexes for our process metadata cache.
*
@@ -134,15 +140,28 @@ static const char * const meta_field_names[_META_MAX] = {
};
typedef struct Context {
- const char *meta[_META_MAX];
- size_t meta_size[_META_MAX];
- pid_t pid;
+ PidRef pidref;
uid_t uid;
gid_t gid;
+ int signo;
+ uint64_t rlimit;
bool is_pid1;
bool is_journald;
+ int mount_tree_fd;
+
+ /* These point into external memory, are not owned by this object */
+ const char *meta[_META_MAX];
+ size_t meta_size[_META_MAX];
} Context;
+#define CONTEXT_NULL \
+ (Context) { \
+ .pidref = PIDREF_NULL, \
+ .uid = UID_INVALID, \
+ .gid = GID_INVALID, \
+ .mount_tree_fd = -EBADF, \
+ }
+
typedef enum CoredumpStorage {
COREDUMP_STORAGE_NONE,
COREDUMP_STORAGE_EXTERNAL,
@@ -167,7 +186,16 @@ static uint64_t arg_external_size_max = EXTERNAL_SIZE_MAX;
static uint64_t arg_journal_size_max = JOURNAL_SIZE_MAX;
static uint64_t arg_keep_free = UINT64_MAX;
static uint64_t arg_max_use = UINT64_MAX;
-static bool arg_access_container = false;
+#if HAVE_DWFL_SET_SYSROOT
+static bool arg_enter_namespace = false;
+#endif
+
+static void context_done(Context *c) {
+ assert(c);
+
+ pidref_done(&c->pidref);
+ c->mount_tree_fd = safe_close(c->mount_tree_fd);
+}
static int parse_config(void) {
static const ConfigTableItem items[] = {
@@ -179,9 +207,9 @@ static int parse_config(void) {
{ "Coredump", "KeepFree", config_parse_iec_uint64, 0, &arg_keep_free },
{ "Coredump", "MaxUse", config_parse_iec_uint64, 0, &arg_max_use },
#if HAVE_DWFL_SET_SYSROOT
- { "Coredump", "AccessContainer", config_parse_bool, 0, &arg_access_container },
+ { "Coredump", "EnterNamespace", config_parse_bool, 0, &arg_enter_namespace },
#else
- { "Coredump", "AccessContainer", config_parse_warn_compat, DISABLED_CONFIGURATION, 0 },
+ { "Coredump", "EnterNamespace", config_parse_warn_compat, DISABLED_CONFIGURATION, 0 },
#endif
{}
};
@@ -428,7 +456,7 @@ static int save_external_coredump(
_cleanup_(unlink_and_freep) char *tmp = NULL;
_cleanup_free_ char *fn = NULL;
_cleanup_close_ int fd = -EBADF;
- uint64_t rlimit, process_limit, max_size;
+ uint64_t process_limit, max_size;
bool truncated, storage_on_tmpfs;
struct stat st;
int r;
@@ -441,11 +469,7 @@ static int save_external_coredump(
assert(ret_compressed_size);
assert(ret_truncated);
- r = safe_atou64(context->meta[META_ARGV_RLIMIT], &rlimit);
- if (r < 0)
- return log_error_errno(r, "Failed to parse resource limit '%s': %m",
- context->meta[META_ARGV_RLIMIT]);
- if (rlimit < page_size())
+ if (context->rlimit < page_size())
/* Is coredumping disabled? Then don't bother saving/processing the
* coredump. Anything below PAGE_SIZE cannot give a readable coredump
* (the kernel uses ELF_EXEC_PAGESIZE which is not easily accessible, but
@@ -460,7 +484,7 @@ static int save_external_coredump(
"Limits for coredump processing and storage are both 0, not dumping core.");
/* Never store more than the process configured, or than we actually shall keep or process */
- max_size = MIN(rlimit, process_limit);
+ max_size = MIN(context->rlimit, process_limit);
r = make_filename(context, &fn);
if (r < 0)
@@ -765,9 +789,12 @@ static int get_process_container_parent_cmdline(pid_t pid, char** cmdline) {
}
static int change_uid_gid(const Context *context) {
+ int r;
+
+ assert(context);
+
uid_t uid = context->uid;
gid_t gid = context->gid;
- int r;
if (uid_is_system(uid)) {
const char *user = "systemd-coredump";
@@ -782,45 +809,43 @@ static int change_uid_gid(const Context *context) {
return drop_privileges(uid, gid, 0);
}
-static int setup_container_mount_tree(int mount_tree_fd, char **container_root) {
- _cleanup_free_ char *root = NULL;
+static int attach_mount_tree(int mount_tree_fd) {
int r;
assert(mount_tree_fd >= 0);
- assert(container_root);
- r = unshare(CLONE_NEWNS);
+ r = detach_mount_namespace();
if (r < 0)
- return log_warning_errno(errno, "Failed to unshare mount namespace: %m");
+ return log_warning_errno(r, "Failed to detach mount namespace: %m");
- r = mount(NULL, "/", NULL, MS_REC|MS_PRIVATE, NULL);
+ r = mkdir_p_label(MOUNT_TREE_ROOT, 0555);
if (r < 0)
- return log_warning_errno(errno, "Failed to disable mount propagation: %m");
+ return log_warning_errno(r, "Failed to create directory: %m");
- r = mkdtemp_malloc("/tmp/systemd-coredump-root-XXXXXX", &root);
+ r = mount_setattr(mount_tree_fd, "", AT_EMPTY_PATH,
+ &(struct mount_attr) {
+ .attr_set = MOUNT_ATTR_RDONLY|MOUNT_ATTR_NOSUID|MOUNT_ATTR_NODEV|MOUNT_ATTR_NOEXEC|MOUNT_ATTR_NOSYMFOLLOW,
+ .propagation = MS_SLAVE,
+ }, sizeof(struct mount_attr));
if (r < 0)
- return log_warning_errno(r, "Failed to create temporary directory: %m");
+ return log_warning_errno(errno, "Failed to change properties of mount tree: %m");
- r = move_mount(mount_tree_fd, "", -EBADF, root, MOVE_MOUNT_F_EMPTY_PATH);
+ r = move_mount(mount_tree_fd, "", -EBADF, MOUNT_TREE_ROOT, MOVE_MOUNT_F_EMPTY_PATH);
if (r < 0)
- return log_warning_errno(errno, "Failed to move mount tree: %m");
+ return log_warning_errno(errno, "Failed to attach mount tree: %m");
- *container_root = TAKE_PTR(root);
return 0;
}
static int submit_coredump(
const Context *context,
struct iovec_wrapper *iovw,
- int input_fd,
- int mount_tree_fd) {
+ int input_fd) {
_cleanup_(sd_json_variant_unrefp) sd_json_variant *json_metadata = NULL;
_cleanup_close_ int coredump_fd = -EBADF, coredump_node_fd = -EBADF;
- _cleanup_free_ char *filename = NULL, *coredump_data = NULL;
- _cleanup_free_ char *stacktrace = NULL;
- _cleanup_free_ char *root = NULL;
- const char *module_name;
+ _cleanup_free_ char *filename = NULL, *coredump_data = NULL, *stacktrace = NULL;
+ const char *module_name, *root = NULL;
uint64_t coredump_size = UINT64_MAX, coredump_compressed_size = UINT64_MAX;
bool truncated = false, written = false;
sd_json_variant *module_json;
@@ -856,11 +881,8 @@ static int submit_coredump(
(void) coredump_vacuum(coredump_node_fd >= 0 ? coredump_node_fd : coredump_fd, arg_keep_free, arg_max_use);
}
- if (mount_tree_fd >= 0 && arg_access_container) {
- r = setup_container_mount_tree(mount_tree_fd, &root);
- if (r < 0)
- log_warning_errno(r, "Failed to setup container mount tree, ignoring: %m");
- }
+ if (context->mount_tree_fd >= 0 && attach_mount_tree(context->mount_tree_fd) >= 0)
+ root = MOUNT_TREE_ROOT;
/* Now, let's drop privileges to become the user who owns the segfaulted process and allocate the
* coredump memory under the user's uid. This also ensures that the credentials journald will see are
@@ -869,6 +891,7 @@ static int submit_coredump(
r = change_uid_gid(context);
if (r < 0)
return log_error_errno(r, "Failed to drop privileges: %m");
+
if (written) {
/* Try to get a stack trace if we can */
if (coredump_size > arg_process_size_max)
@@ -991,7 +1014,7 @@ static int submit_coredump(
return 0;
}
-static int save_context(Context *context, const struct iovec_wrapper *iovw) {
+static int context_parse_iovw(Context *context, struct iovec_wrapper *iovw) {
const char *unit;
int r;
@@ -999,33 +1022,46 @@ static int save_context(Context *context, const struct iovec_wrapper *iovw) {
assert(iovw);
assert(iovw->count >= _META_ARGV_MAX);
- /* The context does not allocate any memory on its own */
-
- for (size_t n = 0; n < iovw->count; n++) {
- struct iovec *iovec = iovw->iovec + n;
+ /* Converts the data in the iovec array iovw into separate fields. Fills in context->meta[] (for
+ * which no memory is allocated, it just contains direct pointers into the iovec array memory). */
+ bool have_signal_name = false;
+ FOREACH_ARRAY(iovec, iovw->iovec, iovw->count) {
for (size_t i = 0; i < ELEMENTSOF(meta_field_names); i++) {
/* Note that these strings are NUL terminated, because we made sure that a
* trailing NUL byte is in the buffer, though not included in the iov_len
* count (see process_socket() and gather_pid_metadata_*()) */
assert(((char*) iovec->iov_base)[iovec->iov_len] == 0);
- const char *p = startswith(iovec->iov_base, meta_field_names[i]);
+ const char *p = memory_startswith(iovec->iov_base, iovec->iov_len, meta_field_names[i]);
if (p) {
context->meta[i] = p;
context->meta_size[i] = iovec->iov_len - strlen(meta_field_names[i]);
break;
}
}
+
+ have_signal_name = have_signal_name ||
+ memory_startswith(iovec->iov_base, iovec->iov_len, "COREDUMP_SIGNAL_NAME=");
}
- if (!context->meta[META_ARGV_PID])
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "Failed to find the PID of crashing process");
+ /* The basic fields from argv[] should always be there, refuse early if not */
+ for (int i = 0; i < _META_ARGV_MAX; i++)
+ if (!context->meta[i])
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "A required (%s) has not been sent, aborting.", meta_field_names[i]);
- r = parse_pid(context->meta[META_ARGV_PID], &context->pid);
+ pid_t parsed_pid;
+ r = parse_pid(context->meta[META_ARGV_PID], &parsed_pid);
if (r < 0)
return log_error_errno(r, "Failed to parse PID \"%s\": %m", context->meta[META_ARGV_PID]);
+ if (pidref_is_set(&context->pidref)) {
+ if (context->pidref.pid != parsed_pid)
+ return log_error_errno(r, "Passed PID " PID_FMT " does not match passed " PID_FMT ": %m", parsed_pid, context->pidref.pid);
+ } else {
+ r = pidref_set_pid(&context->pidref, parsed_pid);
+ if (r < 0)
+ return log_error_errno(r, "Failed to initialize pidref from pid " PID_FMT ": %m", parsed_pid);
+ }
r = parse_uid(context->meta[META_ARGV_UID], &context->uid);
if (r < 0)
@@ -1035,25 +1071,42 @@ static int save_context(Context *context, const struct iovec_wrapper *iovw) {
if (r < 0)
return log_error_errno(r, "Failed to parse GID \"%s\": %m", context->meta[META_ARGV_GID]);
+ r = parse_signo(context->meta[META_ARGV_SIGNAL], &context->signo);
+ if (r < 0)
+ log_warning_errno(r, "Failed to parse signal number \"%s\", ignoring: %m", context->meta[META_ARGV_SIGNAL]);
+
+ r = safe_atou64(context->meta[META_ARGV_RLIMIT], &context->rlimit);
+ if (r < 0)
+ log_warning_errno(r, "Failed to parse resource limit \"%s\", ignoring: %m", context->meta[META_ARGV_RLIMIT]);
+
unit = context->meta[META_UNIT];
context->is_pid1 = streq(context->meta[META_ARGV_PID], "1") || streq_ptr(unit, SPECIAL_INIT_SCOPE);
context->is_journald = streq_ptr(unit, SPECIAL_JOURNALD_SERVICE);
+ /* After parsing everything, let's also synthesize a new iovw field for the textual signal name if it
+ * isn't already set. */
+ if (SIGNAL_VALID(context->signo) && !have_signal_name)
+ (void) iovw_put_string_field(iovw, "COREDUMP_SIGNAL_NAME=SIG", signal_to_string(context->signo));
+
return 0;
}
static int process_socket(int fd) {
- _cleanup_close_ int input_fd = -EBADF, mount_tree_fd = -EBADF;
- Context context = {};
- struct iovec_wrapper iovw = {};
- bool first = true;
+ _cleanup_(iovw_done_free) struct iovec_wrapper iovw = {};
+ _cleanup_(context_done) Context context = CONTEXT_NULL;
+ _cleanup_close_ int input_fd = -EBADF;
+ enum {
+ STATE_PAYLOAD,
+ STATE_INPUT_FD_DONE,
+ STATE_PID_FD_DONE,
+ } state = STATE_PAYLOAD;
int r;
assert(fd >= 0);
log_setup();
- log_debug("Processing coredump received on stdin...");
+ log_debug("Processing coredump received via socket...");
for (;;) {
CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(int))) control;
@@ -1065,72 +1118,95 @@ static int process_socket(int fd) {
ssize_t n, l;
l = next_datagram_size_fd(fd);
- if (l < 0) {
- r = log_error_errno(l, "Failed to determine datagram size to read: %m");
- goto finish;
- }
+ if (l < 0)
+ return log_error_errno(l, "Failed to determine datagram size to read: %m");
_cleanup_(iovec_done) struct iovec iovec = {
.iov_len = l,
.iov_base = malloc(l + 1),
};
- if (!iovec.iov_base) {
- r = log_oom();
- goto finish;
- }
+ if (!iovec.iov_base)
+ return log_oom();
mh.msg_iov = &iovec;
n = recvmsg_safe(fd, &mh, MSG_CMSG_CLOEXEC);
- if (n < 0) {
- r = log_error_errno(n, "Failed to receive datagram: %m");
- goto finish;
- }
-
- /* The final zero-length datagram carries the file descriptor and tells us
- * that we're done. */
+ if (n < 0)
+ return log_error_errno(n, "Failed to receive datagram: %m");
+
+ /* The final zero-length datagrams ("sentinels") carry file descriptors and tell us that
+ * we're done. There are three sentinels: one with just the coredump fd, followed by one with
+ * the pidfd, and finally one with the mount tree fd. The latter two or the last one may be
+ * omitted (which is supported for compatibility with older systemd version, in particular to
+ * facilitate cross-container coredumping). */
if (n == 0) {
struct cmsghdr *found;
- found = cmsg_find(&mh, SOL_SOCKET, SCM_RIGHTS, CMSG_LEN(sizeof(int) * 2));
- if (found) {
- int fds[2] = EBADF_PAIR;
-
- memcpy(fds, CMSG_TYPED_DATA(found, int), sizeof(int) * 2);
+ found = cmsg_find(&mh, SOL_SOCKET, SCM_RIGHTS, CMSG_LEN(sizeof(int)));
+ if (!found) {
+ /* This is zero length message but it either doesn't carry a single
+ * descriptor, or it has more than one. This is a protocol violation so let's
+ * bail out.
+ *
+ * Well, not quite! In practice there's one more complication: EOF on
+ * SOCK_SEQPACKET is not distinguishable from a zero length datagram. Hence
+ * if we get a zero length datagram without fds we consider it EOF, and
+ * that's permissible for the final two fds. Hence let's be strict on the
+ * first fd, but lenient on the other two. */
+
+ if (!cmsg_find(&mh, SOL_SOCKET, SCM_RIGHTS, (socklen_t) -1) && state != STATE_PAYLOAD) /* no fds, and already got the first fd → we are done */
+ break;
+
+ cmsg_close_all(&mh);
+ return log_error_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "Received zero length message with zero or more than one file descriptor(s), expected one.");
+ }
- assert(mount_tree_fd < 0);
+ switch (state) {
- /* Maybe we already got coredump FD in previous iteration? */
- safe_close(input_fd);
+ case STATE_PAYLOAD:
+ assert(input_fd < 0);
+ input_fd = *CMSG_TYPED_DATA(found, int);
+ state = STATE_INPUT_FD_DONE;
+ continue;
- input_fd = fds[0];
- mount_tree_fd = fds[1];
+ case STATE_INPUT_FD_DONE:
+ assert(!pidref_is_set(&context.pidref));
- /* We have all FDs we need let's take a shortcut here. */
- break;
- } else {
- found = cmsg_find(&mh, SOL_SOCKET, SCM_RIGHTS, CMSG_LEN(sizeof(int)));
- if (found)
- input_fd = *CMSG_TYPED_DATA(found, int);
- }
+ r = pidref_set_pidfd_consume(&context.pidref, *CMSG_TYPED_DATA(found, int));
+ if (r < 0)
+ return log_error_errno(r, "Failed to initialize pidref: %m");
- /* This is the first message that carries file descriptors, maybe there will be one more that actually contains array of descriptors. */
- if (first) {
- first = false;
+ state = STATE_PID_FD_DONE;
continue;
+
+ case STATE_PID_FD_DONE:
+ assert(context.mount_tree_fd < 0);
+ context.mount_tree_fd = *CMSG_TYPED_DATA(found, int);
+ /* We have all FDs we need so we are done. */
+ break;
}
break;
- } else
- cmsg_close_all(&mh);
+ }
+
+ cmsg_close_all(&mh);
+
+ /* Only zero length messages are allowed after the first message that carried a file descriptor. */
+ if (state != STATE_PAYLOAD)
+ return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Received unexpected message with non-zero length.");
+
+ /* Payload messages should not carry fds */
+ if (cmsg_find(&mh, SOL_SOCKET, SCM_RIGHTS, (socklen_t) -1))
+ return log_error_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "Received payload message with file descriptor(s), expected none.");
/* Add trailing NUL byte, in case these are strings */
((char*) iovec.iov_base)[n] = 0;
iovec.iov_len = (size_t) n;
- r = iovw_put(&iovw, iovec.iov_base, iovec.iov_len);
- if (r < 0)
- goto finish;
+ if (iovw_put(&iovw, iovec.iov_base, iovec.iov_len) < 0)
+ return log_oom();
TAKE_STRUCT(iovec);
}
@@ -1138,27 +1214,19 @@ static int process_socket(int fd) {
/* Make sure we got all data we really need */
assert(input_fd >= 0);
- r = save_context(&context, &iovw);
+ r = context_parse_iovw(&context, &iovw);
if (r < 0)
- goto finish;
+ return r;
/* Make sure we received at least all fields we need. */
for (int i = 0; i < _META_MANDATORY_MAX; i++)
- if (!context.meta[i]) {
- r = log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "A mandatory argument (%i) has not been sent, aborting.",
- i);
- goto finish;
- }
-
- r = submit_coredump(&context, &iovw, input_fd, mount_tree_fd);
+ if (!context.meta[i])
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "A mandatory argument (%i) has not been sent, aborting.", i);
-finish:
- iovw_free_contents(&iovw, true);
- return r;
+ return submit_coredump(&context, &iovw, input_fd);
}
-static int send_iovec(const struct iovec_wrapper *iovw, int input_fd, int mounts_fd) {
+static int send_iovec(const struct iovec_wrapper *iovw, int input_fd, PidRef *pidref, int mount_tree_fd) {
_cleanup_close_ int fd = -EBADF;
int r;
@@ -1197,7 +1265,7 @@ static int send_iovec(const struct iovec_wrapper *iovw, int input_fd, int mounts
* what we want to send, and the second one contains
* the trailing dots. */
copy[0] = iovw->iovec[i];
- copy[1] = IOVEC_MAKE(((char[]){'.', '.', '.'}), 3);
+ copy[1] = IOVEC_MAKE(((const char[]){'.', '.', '.'}), 3);
mh.msg_iov = copy;
mh.msg_iovlen = 2;
@@ -1211,15 +1279,26 @@ static int send_iovec(const struct iovec_wrapper *iovw, int input_fd, int mounts
}
}
+ /* First sentinel: the coredump fd */
r = send_one_fd(fd, input_fd, 0);
if (r < 0)
return log_error_errno(r, "Failed to send coredump fd: %m");
- if (mounts_fd >= 0) {
- r = send_many_fds(fd, (int[]) { input_fd, mounts_fd }, 2, 0);
- if (r < 0)
- return log_error_errno(r, "Failed to send coredump fds: %m");
- }
+ /* The optional second sentinel: the pidfd */
+ if (!pidref_is_set(pidref) || pidref->fd < 0) /* If we have no pidfd, stop now */
+ return 0;
+
+ r = send_one_fd(fd, pidref->fd, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to send pidfd: %m");
+
+ /* The optional third sentinel: the mount tree fd */
+ if (mount_tree_fd < 0) /* If we have no mount tree, stop now */
+ return 0;
+
+ r = send_one_fd(fd, mount_tree_fd, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to send mount tree fd: %m");
return 0;
}
@@ -1229,9 +1308,7 @@ static int gather_pid_metadata_from_argv(
Context *context,
int argc, char **argv) {
- _cleanup_free_ char *free_timestamp = NULL;
- int r, signo;
- char *t;
+ int r;
assert(iovw);
assert(context);
@@ -1245,30 +1322,19 @@ static int gather_pid_metadata_from_argv(
argc, _META_ARGV_MAX);
for (int i = 0; i < _META_ARGV_MAX; i++) {
+ _cleanup_free_ char *buf = NULL;
+ const char *t = argv[i];
- t = argv[i];
-
- switch (i) {
-
- case META_ARGV_TIMESTAMP:
+ if (i == META_ARGV_TIMESTAMP) {
/* The journal fields contain the timestamp padded with six
* zeroes, so that the kernel-supplied 1s granularity timestamps
* becomes 1μs granularity, i.e. the granularity systemd usually
* operates in. */
- t = free_timestamp = strjoin(argv[i], "000000");
- if (!t)
+ buf = strjoin(argv[i], "000000");
+ if (!buf)
return log_oom();
- break;
-
- case META_ARGV_SIGNAL:
- /* For signal, record its pretty name too */
- if (safe_atoi(argv[i], &signo) >= 0 && SIGNAL_VALID(signo))
- (void) iovw_put_string_field(iovw, "COREDUMP_SIGNAL_NAME=SIG",
- signal_to_string(signo));
- break;
- default:
- break;
+ t = buf;
}
r = iovw_put_string_field(iovw, meta_field_names[i], t);
@@ -1278,7 +1344,7 @@ static int gather_pid_metadata_from_argv(
/* Cache some of the process metadata we collected so far and that we'll need to
* access soon */
- return save_context(context, iovw);
+ return context_parse_iovw(context, iovw);
}
static int gather_pid_metadata_from_procfs(struct iovec_wrapper *iovw, Context *context) {
@@ -1295,10 +1361,10 @@ static int gather_pid_metadata_from_procfs(struct iovec_wrapper *iovw, Context *
/* Note that if we fail on oom later on, we do not roll-back changes to the iovec
* structure. (It remains valid, with the first iovec fields initialized.) */
- pid = context->pid;
+ pid = context->pidref.pid;
/* The following is mandatory */
- r = pid_get_comm(pid, &t);
+ r = pidref_get_comm(&context->pidref, &t);
if (r < 0)
return log_error_errno(r, "Failed to get COMM: %m");
@@ -1313,7 +1379,7 @@ static int gather_pid_metadata_from_procfs(struct iovec_wrapper *iovw, Context *
if (r < 0)
log_warning_errno(r, "Failed to get EXE, ignoring: %m");
- if (cg_pid_get_unit(pid, &t) >= 0)
+ if (cg_pidref_get_unit(&context->pidref, &t) >= 0)
(void) iovw_put_string_field_free(iovw, "COREDUMP_UNIT=", t);
if (cg_pid_get_user_unit(pid, &t) >= 0)
@@ -1331,7 +1397,7 @@ static int gather_pid_metadata_from_procfs(struct iovec_wrapper *iovw, Context *
if (sd_pid_get_slice(pid, &t) >= 0)
(void) iovw_put_string_field_free(iovw, "COREDUMP_SLICE=", t);
- if (pid_get_cmdline(pid, SIZE_MAX, PROCESS_CMDLINE_QUOTE_POSIX, &t) >= 0)
+ if (pidref_get_cmdline(&context->pidref, SIZE_MAX, PROCESS_CMDLINE_QUOTE_POSIX, &t) >= 0)
(void) iovw_put_string_field_free(iovw, "COREDUMP_CMDLINE=", t);
if (cg_pid_get_path_shifted(pid, NULL, &t) >= 0)
@@ -1365,7 +1431,7 @@ static int gather_pid_metadata_from_procfs(struct iovec_wrapper *iovw, Context *
if (read_full_virtual_file(p, &t, &size) >= 0) {
char *buf = malloc(strlen("COREDUMP_PROC_AUXV=") + size + 1);
if (buf) {
- /* Add a dummy terminator to make save_context() happy. */
+ /* Add a dummy terminator to make context_parse_iovw() happy. */
*mempcpy_typesafe(stpcpy(buf, "COREDUMP_PROC_AUXV="), t, size) = '\0';
(void) iovw_consume(iovw, buf, size + strlen("COREDUMP_PROC_AUXV="));
}
@@ -1393,10 +1459,10 @@ static int gather_pid_metadata_from_procfs(struct iovec_wrapper *iovw, Context *
(void) iovw_put_string_field_free(iovw, "COREDUMP_ENVIRON=", t);
/* we successfully acquired all metadata */
- return save_context(context, iovw);
+ return context_parse_iovw(context, iovw);
}
-static int send_ucred(int transport_fd, struct ucred *ucred) {
+static int send_ucred(int transport_fd, const struct ucred *ucred) {
CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct ucred))) control = {};
struct msghdr mh = {
.msg_control = &control,
@@ -1405,6 +1471,7 @@ static int send_ucred(int transport_fd, struct ucred *ucred) {
struct cmsghdr *cmsg;
assert(transport_fd >= 0);
+ assert(ucred);
cmsg = CMSG_FIRSTHDR(&mh);
*cmsg = (struct cmsghdr) {
@@ -1427,6 +1494,7 @@ static int receive_ucred(int transport_fd, struct ucred *ret_ucred) {
struct ucred *ucred = NULL;
ssize_t n;
+ assert(transport_fd >= 0);
assert(ret_ucred);
n = recvmsg_safe(transport_fd, &mh, 0);
@@ -1483,19 +1551,21 @@ static int can_forward_coredump(pid_t pid) {
static int forward_coredump_to_container(Context *context) {
_cleanup_close_ int pidnsfd = -EBADF, mntnsfd = -EBADF, netnsfd = -EBADF, usernsfd = -EBADF, rootfd = -EBADF;
_cleanup_close_pair_ int pair[2] = EBADF_PAIR;
- pid_t pid, child;
+ pid_t leader_pid, child;
struct ucred ucred = {
- .pid = context->pid,
+ .pid = context->pidref.pid,
.uid = context->uid,
.gid = context->gid,
};
int r;
- r = namespace_get_leader(context->pid, NAMESPACE_PID, &pid);
+ assert(context);
+
+ r = namespace_get_leader(context->pidref.pid, NAMESPACE_PID, &leader_pid);
if (r < 0)
return log_debug_errno(r, "Failed to get namespace leader: %m");
- r = can_forward_coredump(pid);
+ r = can_forward_coredump(leader_pid);
if (r < 0)
return log_debug_errno(r, "Failed to check if coredump can be forwarded: %m");
if (r == 0)
@@ -1510,19 +1580,16 @@ static int forward_coredump_to_container(Context *context) {
if (r < 0)
return log_debug_errno(r, "Failed to set SO_PASSCRED: %m");
- r = namespace_open(pid, &pidnsfd, &mntnsfd, &netnsfd, &usernsfd, &rootfd);
+ r = namespace_open(leader_pid, &pidnsfd, &mntnsfd, &netnsfd, &usernsfd, &rootfd);
if (r < 0)
- return log_debug_errno(r, "Failed to join namespaces of PID " PID_FMT ": %m", pid);
+ return log_debug_errno(r, "Failed to join namespaces of PID " PID_FMT ": %m", leader_pid);
r = namespace_fork("(sd-coredumpns)", "(sd-coredump)", NULL, 0,
FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGTERM,
pidnsfd, mntnsfd, netnsfd, usernsfd, rootfd, &child);
if (r < 0)
- return log_debug_errno(r, "Failed to fork into namespaces of PID " PID_FMT ": %m", pid);
+ return log_debug_errno(r, "Failed to fork into namespaces of PID " PID_FMT ": %m", leader_pid);
if (r == 0) {
- _cleanup_(iovw_free_freep) struct iovec_wrapper *iovw = NULL;
- Context child_context = {};
-
pair[0] = safe_close(pair[0]);
r = access_nofollow("/run/systemd/coredump", W_OK);
@@ -1537,7 +1604,7 @@ static int forward_coredump_to_container(Context *context) {
_exit(EXIT_FAILURE);
}
- iovw = iovw_new();
+ _cleanup_(iovw_free_freep) struct iovec_wrapper *iovw = iovw_new();
if (!iovw) {
log_oom();
_exit(EXIT_FAILURE);
@@ -1548,16 +1615,15 @@ static int forward_coredump_to_container(Context *context) {
(void) iovw_put_string_field(iovw, "COREDUMP_FORWARDED=", "1");
for (int i = 0; i < _META_ARGV_MAX; i++) {
- int signo;
char buf[DECIMAL_STR_MAX(pid_t)];
const char *t = context->meta[i];
+ /* Patch some of the fields with the translated ucred data */
switch (i) {
case META_ARGV_PID:
xsprintf(buf, PID_FMT, ucred.pid);
t = buf;
-
break;
case META_ARGV_UID:
@@ -1570,13 +1636,6 @@ static int forward_coredump_to_container(Context *context) {
t = buf;
break;
- case META_ARGV_SIGNAL:
- if (safe_atoi(t, &signo) >= 0 && SIGNAL_VALID(signo))
- (void) iovw_put_string_field(iovw,
- "COREDUMP_SIGNAL_NAME=SIG",
- signal_to_string(signo));
- break;
-
default:
break;
}
@@ -1588,7 +1647,8 @@ static int forward_coredump_to_container(Context *context) {
}
}
- r = save_context(&child_context, iovw);
+ _cleanup_(context_done) Context child_context = CONTEXT_NULL;
+ r = context_parse_iovw(&child_context, iovw);
if (r < 0) {
log_debug_errno(r, "Failed to save context: %m");
_exit(EXIT_FAILURE);
@@ -1600,7 +1660,7 @@ static int forward_coredump_to_container(Context *context) {
_exit(EXIT_FAILURE);
}
- r = send_iovec(iovw, STDIN_FILENO, -EBADF);
+ r = send_iovec(iovw, STDIN_FILENO, &context->pidref, /* mount_tree_fd= */ -EBADF);
if (r < 0) {
log_debug_errno(r, "Failed to send iovec to coredump socket: %m");
_exit(EXIT_FAILURE);
@@ -1628,42 +1688,60 @@ static int forward_coredump_to_container(Context *context) {
return 0;
}
-static int gather_pid_mount_tree_fd(const Context *context) {
- _cleanup_close_ int mntns_fd = -EBADF, root_fd = -EBADF;
+static int acquire_pid_mount_tree_fd(const Context *context, int *ret_fd) {
+ /* Don't bother preparing environment if we can't pass it to libdwfl. */
+#if !HAVE_DWFL_SET_SYSROOT
+ *ret_fd = -EOPNOTSUPP;
+ log_debug("dwfl_set_sysroot() is not supported.");
+#else
+ _cleanup_close_ int mntns_fd = -EBADF, root_fd = -EBADF, fd = -EBADF;
_cleanup_close_pair_ int pair[2] = EBADF_PAIR;
- int fd = -EBADF, r;
- pid_t child;
+ int r;
assert(context);
+ assert(ret_fd);
- /* Don't bother preparing environment if we can't pass it to libdwfl. */
-#if !HAVE_DWFL_SET_SYSROOT
- return -EBADF;
-#endif
-
- if (!arg_access_container)
- return -EBADF;
+ if (!arg_enter_namespace) {
+ *ret_fd = -EHOSTDOWN;
+ log_debug("EnterNamespace=no so we won't use mount tree of the crashed process for generating backtrace.");
+ return 0;
+ }
if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, pair) < 0)
return log_error_errno(errno, "Failed to create socket pair: %m");
- r = namespace_open(context->pid, NULL, &mntns_fd, NULL, NULL, &root_fd);
+ r = namespace_open(context->pidref.pid,
+ /* ret_pidns_fd= */ NULL,
+ &mntns_fd,
+ /* ret_netns_fd= */ NULL,
+ /* ret_userns_fd= */ NULL,
+ &root_fd);
if (r < 0)
return log_error_errno(r, "Failed to open mount namespace of crashing process: %m");
- r = namespace_fork("(sd-mount-tree-ns)", "(sd-mount-tree)", NULL, 0, FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGKILL, -1, mntns_fd, -1, -1, root_fd, &child);
+ r = namespace_fork("(sd-mount-tree-ns)",
+ "(sd-mount-tree)",
+ /* except_fds= */ NULL,
+ /* n_except_fds= */ 0,
+ FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGKILL|FORK_LOG|FORK_WAIT,
+ /* pidns_fd= */ -EBADF,
+ mntns_fd,
+ /* netns_fd= */ -EBADF,
+ /* userns_fd= */ -EBADF,
+ root_fd,
+ NULL);
if (r < 0)
- return log_error_errno(r, "Failed to fork(): %m");
+ return r;
if (r == 0) {
pair[0] = safe_close(pair[0]);
- r = open_tree(-EBADF, "/", AT_NO_AUTOMOUNT | AT_RECURSIVE | AT_SYMLINK_NOFOLLOW | OPEN_TREE_CLOEXEC | OPEN_TREE_CLONE);
- if (r < 0) {
+ fd = open_tree(-EBADF, "/", AT_NO_AUTOMOUNT | AT_RECURSIVE | AT_SYMLINK_NOFOLLOW | OPEN_TREE_CLOEXEC | OPEN_TREE_CLONE);
+ if (fd < 0) {
log_error_errno(errno, "Failed to clone mount tree: %m");
_exit(EXIT_FAILURE);
}
- r = send_one_fd(pair[1], r, 0);
+ r = send_one_fd(pair[1], fd, 0);
if (r < 0) {
log_error_errno(r, "Failed to send mount tree to parent: %m");
_exit(EXIT_FAILURE);
@@ -1674,24 +1752,19 @@ static int gather_pid_mount_tree_fd(const Context *context) {
pair[1] = safe_close(pair[1]);
- r = wait_for_terminate_and_check("(sd-mount-tree-ns)", child, 0);
- if (r < 0)
- return log_error_errno(r, "Failed to wait for child: %m");
- if (r != EXIT_SUCCESS)
- return log_error_errno(SYNTHETIC_ERRNO(ECHILD), "Child died abnormally.");
-
fd = receive_one_fd(pair[0], MSG_DONTWAIT);
if (fd < 0)
return log_error_errno(fd, "Failed to receive mount tree: %m");
- return fd;
+ *ret_fd = TAKE_FD(fd);
+#endif
+ return 0;
}
static int process_kernel(int argc, char* argv[]) {
_cleanup_(iovw_free_freep) struct iovec_wrapper *iovw = NULL;
- _cleanup_close_ int mount_tree_fd = -EBADF;
- Context context = {};
- int r, signo;
+ _cleanup_(context_done) Context context = CONTEXT_NULL;
+ int r;
/* When we're invoked by the kernel, stdout/stderr are closed which is dangerous because the fds
* could get reallocated. To avoid hard to debug issues, let's instead bind stdout/stderr to
@@ -1722,11 +1795,11 @@ static int process_kernel(int argc, char* argv[]) {
/* Log minimal metadata now, so it is not lost if the system is about to shut down. */
log_info("Process %s (%s) of user %s terminated abnormally with signal %s/%s, processing...",
- context.meta[META_ARGV_PID], context.meta[META_COMM],
- context.meta[META_ARGV_UID], context.meta[META_ARGV_SIGNAL],
- strna(safe_atoi(context.meta[META_ARGV_SIGNAL], &signo) >= 0 ? signal_to_string(signo) : NULL));
+ context.meta[META_ARGV_PID], context.meta[META_COMM],
+ context.meta[META_ARGV_UID], context.meta[META_ARGV_SIGNAL],
+ signal_to_string(context.signo));
- r = in_same_namespace(getpid_cached(), context.pid, NAMESPACE_PID);
+ r = in_same_namespace(getpid_cached(), context.pidref.pid, NAMESPACE_PID);
if (r < 0)
log_debug_errno(r, "Failed to check pidns of crashing process, ignoring: %m");
if (r == 0) {
@@ -1736,11 +1809,9 @@ static int process_kernel(int argc, char* argv[]) {
if (r >= 0)
return 0;
- r = gather_pid_mount_tree_fd(&context);
- if (r < 0 && r != -EBADF)
+ r = acquire_pid_mount_tree_fd(&context, &context.mount_tree_fd);
+ if (r < 0)
log_warning_errno(r, "Failed to access the mount tree of a container, ignoring: %m");
- else
- mount_tree_fd = r;
}
/* If this is PID 1 disable coredump collection, we'll unlikely be able to process
@@ -1758,18 +1829,20 @@ static int process_kernel(int argc, char* argv[]) {
(void) iovw_put_string_field(iovw, "PRIORITY=", STRINGIFY(LOG_CRIT));
if (context.is_journald || context.is_pid1)
- return submit_coredump(&context, iovw, STDIN_FILENO, mount_tree_fd);
+ return submit_coredump(&context, iovw, STDIN_FILENO);
- return send_iovec(iovw, STDIN_FILENO, mount_tree_fd);
+ return send_iovec(iovw, STDIN_FILENO, &context.pidref, context.mount_tree_fd);
}
static int process_backtrace(int argc, char *argv[]) {
_cleanup_(journal_importer_cleanup) JournalImporter importer = JOURNAL_IMPORTER_INIT(STDIN_FILENO);
_cleanup_(iovw_free_freep) struct iovec_wrapper *iovw = NULL;
- Context context = {};
+ _cleanup_(context_done) Context context = CONTEXT_NULL;
char *message;
int r;
+ assert(argc >= 2);
+
log_debug("Processing backtrace on stdin...");
iovw = iovw_new();
diff --git a/src/coredump/coredump.conf b/src/coredump/coredump.conf
index 2790bf1be6..181aede9da 100644
--- a/src/coredump/coredump.conf
+++ b/src/coredump/coredump.conf
@@ -25,4 +25,4 @@
#JournalSizeMax=767M
#MaxUse=
#KeepFree=
-#AccessContainer=no
+#EnterNamespace=no
diff --git a/src/creds/creds.c b/src/creds/creds.c
index bb59db37fc..b24a84eaa6 100644
--- a/src/creds/creds.c
+++ b/src/creds/creds.c
@@ -273,7 +273,7 @@ static int verb_list(int argc, char **argv, void *userdata) {
return log_error_errno(SYNTHETIC_ERRNO(ENXIO), "No credentials passed. (i.e. $CREDENTIALS_DIRECTORY not set.)");
}
- if (FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF) && table_isempty(t)) {
+ if (table_isempty(t) && !sd_json_format_enabled(arg_json_format_flags)) {
log_info("No credentials");
return 0;
}
@@ -370,7 +370,7 @@ static int write_blob(FILE *f, const void *data, size_t size) {
int r;
if (arg_transcode == TRANSCODE_OFF &&
- arg_json_format_flags != SD_JSON_FORMAT_OFF) {
+ sd_json_format_enabled(arg_json_format_flags)) {
_cleanup_(erase_and_freep) char *suffixed = NULL;
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
diff --git a/src/cryptenroll/cryptenroll-recovery.c b/src/cryptenroll/cryptenroll-recovery.c
index 0a5e9cdcbe..33b58eeb37 100644
--- a/src/cryptenroll/cryptenroll-recovery.c
+++ b/src/cryptenroll/cryptenroll-recovery.c
@@ -67,7 +67,7 @@ int enroll_recovery(
"whenever authentication is requested.\n", stderr);
fflush(stderr);
- (void) print_qrcode(stderr, "You may optionally scan the recovery key off screen", password);
+ (void) print_qrcode(stderr, "Optionally scan the recovery key for safekeeping", password);
if (asprintf(&keyslot_as_string, "%i", keyslot) < 0) {
r = log_oom();
diff --git a/src/dissect/dissect.c b/src/dissect/dissect.c
index 1cad45c8ff..78a830d574 100644
--- a/src/dissect/dissect.c
+++ b/src/dissect/dissect.c
@@ -877,7 +877,7 @@ static int action_dissect(
if (arg_json_format_flags & (SD_JSON_FORMAT_OFF|SD_JSON_FORMAT_PRETTY|SD_JSON_FORMAT_PRETTY_AUTO))
pager_open(arg_pager_flags);
- if (arg_json_format_flags & SD_JSON_FORMAT_OFF) {
+ if (!sd_json_format_enabled(arg_json_format_flags)) {
printf(" File Name: %s%s%s\n",
ansi_highlight(), bn, ansi_normal());
@@ -907,7 +907,7 @@ static int action_dissect(
log_warning_errno(r, "OS image is currently in use, proceeding without showing OS image metadata.");
else if (r < 0)
return log_error_errno(r, "Failed to acquire image metadata: %m");
- else if (arg_json_format_flags & SD_JSON_FORMAT_OFF) {
+ else if (!sd_json_format_enabled(arg_json_format_flags)) {
if (m->image_name && !streq(m->image_name, bn))
printf("Image Name: %s\n", m->image_name);
@@ -1017,7 +1017,7 @@ static int action_dissect(
/* Hide the device path if this is a loopback device that is not relinquished, since that means the
* device node is not going to be useful the instant our command exits */
- if ((!d || d->created) && (arg_json_format_flags & SD_JSON_FORMAT_OFF))
+ if ((!d || d->created) && !sd_json_format_enabled(arg_json_format_flags))
table_hide_column_from_display(t, 8);
for (PartitionDesignator i = 0; i < _PARTITION_DESIGNATOR_MAX; i++) {
@@ -1080,7 +1080,7 @@ static int action_dissect(
return table_log_add_error(r);
}
- if (arg_json_format_flags & SD_JSON_FORMAT_OFF) {
+ if (!sd_json_format_enabled(arg_json_format_flags)) {
(void) table_set_header(t, arg_legend);
r = table_print(t, NULL);
@@ -1856,7 +1856,7 @@ static int action_discover(void) {
return log_error_errno(r, "Failed to discover images: %m");
}
- if ((arg_json_format_flags & SD_JSON_FORMAT_OFF) && hashmap_isempty(images)) {
+ if (hashmap_isempty(images) && !sd_json_format_enabled(arg_json_format_flags)) {
log_info("No images found.");
return 0;
}
diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c
index 5bdeecc96c..9be62b8df3 100644
--- a/src/firstboot/firstboot.c
+++ b/src/firstboot/firstboot.c
@@ -104,8 +104,10 @@ static void print_welcome(int rfd) {
if (!arg_welcome)
return;
- if (done)
+ if (done) {
+ putchar('\n'); /* Add some breathing room between multiple prompts */
return;
+ }
r = parse_os_release_at(rfd,
"PRETTY_NAME", &pretty_name,
@@ -132,7 +134,7 @@ static void print_welcome(int rfd) {
done = true;
}
-static int prompt_loop(const char *text, char **l, unsigned percentage, bool (*is_valid)(const char *name), char **ret) {
+static int prompt_loop(int rfd, const char *text, char **l, unsigned percentage, bool (*is_valid)(int rfd, const char *name), char **ret) {
int r;
assert(text);
@@ -143,7 +145,8 @@ static int prompt_loop(const char *text, char **l, unsigned percentage, bool (*i
_cleanup_free_ char *p = NULL;
unsigned u;
- r = ask_string(&p, "%s %s (empty to skip, \"list\" to list options): ",
+ r = ask_string(&p, strv_isempty(l) ? "%s %s (empty to skip): "
+ : "%s %s (empty to skip, \"list\" to list options): ",
special_glyph(SPECIAL_GLYPH_TRIANGULAR_BULLET), text);
if (r < 0)
return log_error_errno(r, "Failed to query user: %m");
@@ -153,27 +156,29 @@ static int prompt_loop(const char *text, char **l, unsigned percentage, bool (*i
return 0;
}
- if (streq(p, "list")) {
- r = show_menu(l, 3, 20, percentage);
- if (r < 0)
- return r;
+ if (!strv_isempty(l)) {
+ if (streq(p, "list")) {
+ r = show_menu(l, 3, 20, percentage);
+ if (r < 0)
+ return r;
- putchar('\n');
- continue;
- };
-
- r = safe_atou(p, &u);
- if (r >= 0) {
- if (u <= 0 || u > strv_length(l)) {
- log_error("Specified entry number out of range.");
+ putchar('\n');
continue;
}
- log_info("Selected '%s'.", l[u-1]);
- return free_and_strdup_warn(ret, l[u-1]);
+ r = safe_atou(p, &u);
+ if (r >= 0) {
+ if (u <= 0 || u > strv_length(l)) {
+ log_error("Specified entry number out of range.");
+ continue;
+ }
+
+ log_info("Selected '%s'.", l[u-1]);
+ return free_and_strdup_warn(ret, l[u-1]);
+ }
}
- if (is_valid(p))
+ if (is_valid(rfd, p))
return free_and_replace(*ret, p);
/* Be more helpful to the user, and give a hint what the user might have wanted to type. */
@@ -259,9 +264,15 @@ static bool locale_is_installed_bool(const char *name) {
}
static bool locale_is_ok(int rfd, const char *name) {
+ int r;
+
assert(rfd >= 0);
- return dir_fd_is_root(rfd) ? locale_is_installed_bool(name) : locale_is_valid(name);
+ r = dir_fd_is_root(rfd);
+ if (r < 0)
+ log_debug_errno(r, "Unable to determine if operating on host root directory, assuming we are: %m");
+
+ return r != 0 ? locale_is_installed_bool(name) : locale_is_valid(name);
}
static int prompt_locale(int rfd) {
@@ -316,21 +327,18 @@ static int prompt_locale(int rfd) {
/* Not setting arg_locale_message here, since it defaults to LANG anyway */
}
} else {
- bool (*is_valid)(const char *name) = dir_fd_is_root(rfd) ? locale_is_installed_bool
- : locale_is_valid;
-
print_welcome(rfd);
- r = prompt_loop("Please enter system locale name or number",
- locales, 60, is_valid, &arg_locale);
+ r = prompt_loop(rfd, "Please enter the new system locale name or number",
+ locales, 60, locale_is_ok, &arg_locale);
if (r < 0)
return r;
if (isempty(arg_locale))
return 0;
- r = prompt_loop("Please enter system message locale name or number",
- locales, 60, is_valid, &arg_locale_messages);
+ r = prompt_loop(rfd, "Please enter the new system message locale name or number",
+ locales, 60, locale_is_ok, &arg_locale_messages);
if (r < 0)
return r;
@@ -404,14 +412,16 @@ static bool keymap_exists_bool(const char *name) {
return keymap_exists(name) > 0;
}
-static typeof(&keymap_is_valid) determine_keymap_validity_func(int rfd) {
+static bool keymap_is_ok(int rfd, const char* name) {
int r;
+ assert(rfd >= 0);
+
r = dir_fd_is_root(rfd);
if (r < 0)
log_debug_errno(r, "Unable to determine if operating on host root directory, assuming we are: %m");
- return r != 0 ? keymap_exists_bool : keymap_is_valid;
+ return r != 0 ? keymap_exists_bool(name) : keymap_is_valid(name);
}
static int prompt_keymap(int rfd) {
@@ -444,8 +454,8 @@ static int prompt_keymap(int rfd) {
print_welcome(rfd);
- return prompt_loop("Please enter system keymap name or number",
- kmaps, 60, determine_keymap_validity_func(rfd), &arg_keymap);
+ return prompt_loop(rfd, "Please enter the new keymap name or number",
+ kmaps, 60, keymap_is_ok, &arg_keymap);
}
static int process_keymap(int rfd) {
@@ -502,7 +512,9 @@ static int process_keymap(int rfd) {
return 1;
}
-static bool timezone_is_valid_log_debug(const char *name) {
+static bool timezone_is_ok(int rfd, const char *name) {
+ assert(rfd >= 0);
+
return timezone_is_valid(name, LOG_DEBUG);
}
@@ -534,12 +546,8 @@ static int prompt_timezone(int rfd) {
print_welcome(rfd);
- r = prompt_loop("Please enter timezone name or number",
- zones, 30, timezone_is_valid_log_debug, &arg_timezone);
- if (r < 0)
- return r;
-
- return 0;
+ return prompt_loop(rfd, "Please enter the new timezone name or number",
+ zones, 30, timezone_is_ok, &arg_timezone);
}
static int process_timezone(int rfd) {
@@ -600,6 +608,12 @@ static int process_timezone(int rfd) {
return 0;
}
+static bool hostname_is_ok(int rfd, const char *name) {
+ assert(rfd >= 0);
+
+ return hostname_is_valid(name, VALID_HOSTNAME_TRAILING_DOT);
+}
+
static int prompt_hostname(int rfd) {
int r;
@@ -614,31 +628,13 @@ static int prompt_hostname(int rfd) {
}
print_welcome(rfd);
- putchar('\n');
-
- for (;;) {
- _cleanup_free_ char *h = NULL;
- r = ask_string(&h, "%s Please enter hostname for new system (empty to skip): ", special_glyph(SPECIAL_GLYPH_TRIANGULAR_BULLET));
- if (r < 0)
- return log_error_errno(r, "Failed to query hostname: %m");
-
- if (isempty(h)) {
- log_info("No hostname entered, skipping.");
- break;
- }
-
- if (!hostname_is_valid(h, VALID_HOSTNAME_TRAILING_DOT)) {
- log_error("Specified hostname invalid.");
- continue;
- }
-
- /* Get rid of the trailing dot that we allow, but don't want to see */
- arg_hostname = hostname_cleanup(h);
- h = NULL;
- break;
- }
+ r = prompt_loop(rfd, "Please enter the new hostname",
+ NULL, 0, hostname_is_ok, &arg_hostname);
+ if (r < 0)
+ return r;
+ hostname_cleanup(arg_hostname);
return 0;
}
@@ -728,10 +724,9 @@ static int prompt_root_password(int rfd) {
}
print_welcome(rfd);
- putchar('\n');
- msg1 = strjoina(special_glyph(SPECIAL_GLYPH_TRIANGULAR_BULLET), " Please enter a new root password (empty to skip):");
- msg2 = strjoina(special_glyph(SPECIAL_GLYPH_TRIANGULAR_BULLET), " Please enter new root password again:");
+ msg1 = strjoina(special_glyph(SPECIAL_GLYPH_TRIANGULAR_BULLET), " Please enter the new root password (empty to skip):");
+ msg2 = strjoina(special_glyph(SPECIAL_GLYPH_TRIANGULAR_BULLET), " Please enter the new root password again:");
suggest_passwords();
@@ -799,6 +794,12 @@ static int find_shell(int rfd, const char *path) {
return 0;
}
+static bool shell_is_ok(int rfd, const char *path) {
+ assert(rfd >= 0);
+
+ return find_shell(rfd, path) >= 0;
+}
+
static int prompt_root_shell(int rfd) {
int r;
@@ -821,29 +822,9 @@ static int prompt_root_shell(int rfd) {
}
print_welcome(rfd);
- putchar('\n');
-
- for (;;) {
- _cleanup_free_ char *s = NULL;
-
- r = ask_string(&s, "%s Please enter root shell for new system (empty to skip): ", special_glyph(SPECIAL_GLYPH_TRIANGULAR_BULLET));
- if (r < 0)
- return log_error_errno(r, "Failed to query root shell: %m");
- if (isempty(s)) {
- log_info("No shell entered, skipping.");
- break;
- }
-
- r = find_shell(rfd, s);
- if (r < 0)
- continue;
-
- arg_root_shell = TAKE_PTR(s);
- break;
- }
-
- return 0;
+ return prompt_loop(rfd, "Please enter the new root shell",
+ NULL, 0, shell_is_ok, &arg_root_shell);
}
static int write_root_passwd(int rfd, int etc_fd, const char *password, const char *shell) {
@@ -1668,7 +1649,7 @@ static int run(int argc, char *argv[]) {
/* We check these conditions here instead of in parse_argv() so that we can take the root directory
* into account. */
- if (arg_keymap && !determine_keymap_validity_func(rfd)(arg_keymap))
+ if (arg_keymap && !keymap_is_ok(rfd, arg_keymap))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Keymap %s is not installed.", arg_keymap);
if (arg_locale && !locale_is_ok(rfd, arg_locale))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Locale %s is not installed.", arg_locale);
@@ -1705,15 +1686,15 @@ static int run(int argc, char *argv[]) {
if (r < 0)
return r;
- r = process_machine_id(rfd);
+ r = process_root_account(rfd);
if (r < 0)
return r;
- r = process_root_account(rfd);
+ r = process_kernel_cmdline(rfd);
if (r < 0)
return r;
- r = process_kernel_cmdline(rfd);
+ r = process_machine_id(rfd);
if (r < 0)
return r;
diff --git a/src/fundamental/macro-fundamental.h b/src/fundamental/macro-fundamental.h
index 7e604b8283..35758b5b18 100644
--- a/src/fundamental/macro-fundamental.h
+++ b/src/fundamental/macro-fundamental.h
@@ -542,7 +542,6 @@ static inline uint64_t ALIGN_OFFSET_U64(uint64_t l, uint64_t ali) {
#define DECLARE_NOALLOC_SECTION(name, text) \
asm(".pushsection " name ",\"S\"\n\t" \
".ascii " STRINGIFY(text) "\n\t" \
- ".zero 1\n\t" \
".popsection\n")
#ifdef SBAT_DISTRO
diff --git a/src/home/homectl-recovery-key.c b/src/home/homectl-recovery-key.c
index b5c57e14a8..2b76303edd 100644
--- a/src/home/homectl-recovery-key.c
+++ b/src/home/homectl-recovery-key.c
@@ -160,7 +160,7 @@ int identity_add_recovery_key(sd_json_variant **v) {
"whenever authentication is requested.\n", stderr);
fflush(stderr);
- (void) print_qrcode(stderr, "You may optionally scan the recovery key off screen", password);
+ (void) print_qrcode(stderr, "Optionally scan the recovery key for safekeeping", password);
return 0;
}
diff --git a/src/home/homectl.c b/src/home/homectl.c
index 11270d7edb..b3aacbcbcf 100644
--- a/src/home/homectl.c
+++ b/src/home/homectl.c
@@ -203,7 +203,7 @@ static int list_homes(int argc, char *argv[], void *userdata) {
if (r < 0)
return bus_log_parse_error(r);
- if (!table_isempty(table) || !FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF)) {
+ if (!table_isempty(table) || sd_json_format_enabled(arg_json_format_flags)) {
r = table_set_sort(table, (size_t) 0);
if (r < 0)
return table_log_sort_error(r);
@@ -213,7 +213,7 @@ static int list_homes(int argc, char *argv[], void *userdata) {
return r;
}
- if (arg_legend && FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF)) {
+ if (arg_legend && !sd_json_format_enabled(arg_json_format_flags)) {
if (table_isempty(table))
printf("No home areas.\n");
else
@@ -671,7 +671,7 @@ static void dump_home_record(UserRecord *hr) {
log_warning("Warning: lacking rights to acquire privileged fields of user record of '%s', output incomplete.", hr->user_name);
}
- if (arg_json_format_flags & SD_JSON_FORMAT_OFF)
+ if (!sd_json_format_enabled(arg_json_format_flags))
user_record_show(hr, true);
else {
_cleanup_(user_record_unrefp) UserRecord *stripped = NULL;
diff --git a/src/home/homed-home-bus.c b/src/home/homed-home-bus.c
index 290e05ac6b..80e2773447 100644
--- a/src/home/homed-home-bus.c
+++ b/src/home/homed-home-bus.c
@@ -55,7 +55,7 @@ static int property_get_state(
return sd_bus_message_append(reply, "s", home_state_to_string(home_get_state(h)));
}
-int bus_home_client_is_trusted(Home *h, sd_bus_message *message) {
+static int bus_home_client_is_trusted(Home *h, sd_bus_message *message, bool strict) {
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
uid_t euid;
int r;
@@ -73,7 +73,7 @@ int bus_home_client_is_trusted(Home *h, sd_bus_message *message) {
if (r < 0)
return r;
- return euid == 0 || h->uid == euid;
+ return (!strict && euid == 0) || h->uid == euid;
}
static int home_verify_polkit_async(
@@ -117,7 +117,7 @@ int bus_home_get_record_json(
assert(h);
assert(ret);
- trusted = bus_home_client_is_trusted(h, message);
+ trusted = bus_home_client_is_trusted(h, message, /* strict= */ false);
if (trusted < 0) {
log_warning_errno(trusted, "Failed to determine whether client is trusted, assuming untrusted.");
trusted = false;
@@ -423,7 +423,7 @@ int bus_home_update_record(
Hashmap *blobs,
uint64_t flags,
sd_bus_error *error) {
- int r;
+ int r, relax_access;
assert(h);
assert(message);
@@ -436,10 +436,32 @@ int bus_home_update_record(
if ((flags & ~SD_HOMED_UPDATE_FLAGS_ALL) != 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid flags provided.");
+ if (blobs) {
+ const char *failed = NULL;
+ r = user_record_ensure_blob_manifest(hr, blobs, &failed);
+ if (r == -EINVAL)
+ return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Provided blob files do not correspond to blob manifest.");
+ if (r < 0)
+ return sd_bus_error_set_errnof(error, r, "Failed to generate hash for blob %s: %m", strnull(failed));
+ }
+
+ relax_access = user_record_self_changes_allowed(h->record, hr);
+ if (relax_access < 0) {
+ log_warning_errno(relax_access, "Failed to determine if changes to user record are permitted, assuming not: %m");
+ relax_access = false;
+ } else if (relax_access) {
+ relax_access = bus_home_client_is_trusted(h, message, /* strict= */ true);
+ if (relax_access < 0) {
+ log_warning_errno(relax_access, "Failed to determine whether client is trusted, assuming not: %m");
+ relax_access = false;
+ }
+ }
+
r = home_verify_polkit_async(
h,
message,
- "org.freedesktop.home1.update-home",
+ relax_access ? "org.freedesktop.home1.update-home-by-owner"
+ : "org.freedesktop.home1.update-home",
UID_INVALID,
error);
if (r < 0)
@@ -561,7 +583,7 @@ int bus_home_method_change_password(
h,
message,
"org.freedesktop.home1.passwd-home",
- h->uid,
+ h->uid, /* Always let a user change their own password. Safe b/c homework will always re-check password */
error);
if (r < 0)
return r;
diff --git a/src/home/homed-home-bus.h b/src/home/homed-home-bus.h
index 1644bc8066..8d4ddf909f 100644
--- a/src/home/homed-home-bus.h
+++ b/src/home/homed-home-bus.h
@@ -6,7 +6,6 @@
#include "bus-object.h"
#include "homed-home.h"
-int bus_home_client_is_trusted(Home *h, sd_bus_message *message);
int bus_home_get_record_json(Home *h, sd_bus_message *message, char **ret, bool *ret_incomplete);
int bus_home_method_activate(sd_bus_message *message, void *userdata, sd_bus_error *error);
diff --git a/src/home/homed-home.c b/src/home/homed-home.c
index 664397e55e..32691e4f81 100644
--- a/src/home/homed-home.c
+++ b/src/home/homed-home.c
@@ -1727,15 +1727,6 @@ static int home_update_internal(
secret = saved_secret;
}
- if (blobs) {
- const char *failed = NULL;
- r = user_record_ensure_blob_manifest(hr, blobs, &failed);
- if (r == -EINVAL)
- return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Provided blob files do not correspond to blob manifest.");
- if (r < 0)
- return sd_bus_error_set_errnof(error, r, "Failed to generate hash for blob %s: %m", strnull(failed));
- }
-
r = manager_verify_user_record(h->manager, hr);
switch (r) {
diff --git a/src/home/org.freedesktop.home1.policy b/src/home/org.freedesktop.home1.policy
index 3b19ed3ed5..d3317772ac 100644
--- a/src/home/org.freedesktop.home1.policy
+++ b/src/home/org.freedesktop.home1.policy
@@ -49,6 +49,16 @@
</defaults>
</action>
+ <action id="org.freedesktop.home1.update-home-by-owner">
+ <description gettext-domain="systemd">Update your home area</description>
+ <message gettext-domain="systemd">Authentication is required to update your home area.</message>
+ <defaults>
+ <allow_any>auth_admin_keep</allow_any>
+ <allow_inactive>auth_admin_keep</allow_inactive>
+ <allow_active>yes</allow_active>
+ </defaults>
+ </action>
+
<action id="org.freedesktop.home1.resize-home">
<description gettext-domain="systemd">Resize a home area</description>
<message gettext-domain="systemd">Authentication is required to resize a user's home area.</message>
diff --git a/src/hostname/hostnamectl.c b/src/hostname/hostnamectl.c
index 4bbfa670b7..a537312d25 100644
--- a/src/hostname/hostnamectl.c
+++ b/src/hostname/hostnamectl.c
@@ -491,7 +491,7 @@ static int show_status(int argc, char **argv, void *userdata) {
sd_bus *bus = userdata;
int r;
- if (arg_json_format_flags != SD_JSON_FORMAT_OFF) {
+ if (sd_json_format_enabled(arg_json_format_flags)) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
diff --git a/src/import/export-raw.c b/src/import/export-raw.c
index f425396261..5e34fd372e 100644
--- a/src/import/export-raw.c
+++ b/src/import/export-raw.c
@@ -9,9 +9,11 @@
#include "copy.h"
#include "export-raw.h"
#include "fd-util.h"
+#include "format-util.h"
#include "fs-util.h"
#include "import-common.h"
#include "missing_fcntl.h"
+#include "pretty-print.h"
#include "ratelimit.h"
#include "stat-util.h"
#include "string-util.h"
@@ -121,7 +123,16 @@ static void raw_export_report_progress(RawExport *e) {
return;
sd_notifyf(false, "X_IMPORT_PROGRESS=%u%%", percent);
- log_info("Exported %u%%.", percent);
+
+ if (isatty_safe(STDERR_FILENO))
+ (void) draw_progress_barf(
+ percent,
+ "%s %s/%s",
+ special_glyph(SPECIAL_GLYPH_ARROW_RIGHT),
+ FORMAT_BYTES(e->written_uncompressed),
+ FORMAT_BYTES(e->st.st_size));
+ else
+ log_info("Exported %u%%.", percent);
e->last_percent = percent;
}
@@ -215,6 +226,9 @@ static int raw_export_process(RawExport *e) {
finish:
if (r >= 0) {
+ if (isatty_safe(STDERR_FILENO))
+ clear_progress_bar(/* prefix= */ NULL);
+
(void) copy_times(e->input_fd, e->output_fd, COPY_CRTIME);
(void) copy_xattr(e->input_fd, NULL, e->output_fd, NULL, 0);
}
diff --git a/src/import/export-tar.c b/src/import/export-tar.c
index 9e92badfef..e025efe411 100644
--- a/src/import/export-tar.c
+++ b/src/import/export-tar.c
@@ -7,6 +7,7 @@
#include "export-tar.h"
#include "fd-util.h"
#include "import-common.h"
+#include "pretty-print.h"
#include "process-util.h"
#include "ratelimit.h"
#include "string-util.h"
@@ -132,7 +133,16 @@ static void tar_export_report_progress(TarExport *e) {
return;
sd_notifyf(false, "X_IMPORT_PROGRESS=%u%%", percent);
- log_info("Exported %u%%.", percent);
+
+ if (isatty_safe(STDERR_FILENO))
+ (void) draw_progress_barf(
+ percent,
+ "%s %s/%s",
+ special_glyph(SPECIAL_GLYPH_ARROW_RIGHT),
+ FORMAT_BYTES(e->written_uncompressed),
+ FORMAT_BYTES(e->quota_referenced));
+ else
+ log_info("Exported %u%%.", percent);
e->last_percent = percent;
}
@@ -229,6 +239,9 @@ static int tar_export_process(TarExport *e) {
return 0;
finish:
+ if (r >= 0 && isatty_safe(STDERR_FILENO))
+ clear_progress_bar(/* prefix= */ NULL);
+
if (e->on_finished)
e->on_finished(e, r, e->userdata);
else
diff --git a/src/import/import-raw.c b/src/import/import-raw.c
index 78775b96d6..602d1f1ac3 100644
--- a/src/import/import-raw.c
+++ b/src/import/import-raw.c
@@ -20,6 +20,7 @@
#include "machine-pool.h"
#include "mkdir-label.h"
#include "path-util.h"
+#include "pretty-print.h"
#include "qcow2-util.h"
#include "ratelimit.h"
#include "rm-rf.h"
@@ -149,7 +150,16 @@ static void raw_import_report_progress(RawImport *i) {
return;
sd_notifyf(false, "X_IMPORT_PROGRESS=%u%%", percent);
- log_info("Imported %u%%.", percent);
+
+ if (isatty_safe(STDERR_FILENO))
+ (void) draw_progress_barf(
+ percent,
+ "%s %s/%s",
+ special_glyph(SPECIAL_GLYPH_ARROW_RIGHT),
+ FORMAT_BYTES(i->written_compressed),
+ FORMAT_BYTES(i->input_stat.st_size));
+ else
+ log_info("Imported %u%%.", percent);
i->last_percent = percent;
}
@@ -459,6 +469,9 @@ static int raw_import_process(RawImport *i) {
return 0;
complete:
+ if (isatty_safe(STDERR_FILENO))
+ clear_progress_bar(/* prefix= */ NULL);
+
r = raw_import_finish(i);
finish:
diff --git a/src/import/import-tar.c b/src/import/import-tar.c
index 976c918246..e82159cb52 100644
--- a/src/import/import-tar.c
+++ b/src/import/import-tar.c
@@ -20,6 +20,7 @@
#include "machine-pool.h"
#include "mkdir-label.h"
#include "path-util.h"
+#include "pretty-print.h"
#include "process-util.h"
#include "qcow2-util.h"
#include "ratelimit.h"
@@ -150,7 +151,16 @@ static void tar_import_report_progress(TarImport *i) {
return;
sd_notifyf(false, "X_IMPORT_PROGRESS=%u%%", percent);
- log_info("Imported %u%%.", percent);
+
+ if (isatty_safe(STDERR_FILENO))
+ (void) draw_progress_barf(
+ percent,
+ "%s %s/%s",
+ special_glyph(SPECIAL_GLYPH_ARROW_RIGHT),
+ FORMAT_BYTES(i->written_compressed),
+ FORMAT_BYTES(i->input_stat.st_size));
+ else
+ log_info("Imported %u%%.", percent);
i->last_percent = percent;
}
@@ -322,6 +332,9 @@ static int tar_import_process(TarImport *i) {
return 0;
finish:
+ if (r >= 0 && isatty_safe(STDERR_FILENO))
+ clear_progress_bar(/* prefix= */ NULL);
+
if (i->on_finished)
i->on_finished(i, r, i->userdata);
else
diff --git a/src/import/importctl.c b/src/import/importctl.c
index 0ddfa988a6..1ddba76b09 100644
--- a/src/import/importctl.c
+++ b/src/import/importctl.c
@@ -936,7 +936,7 @@ static int list_images(int argc, char *argv[], void *userdata) {
if (r < 0)
return table_log_add_error(r);
- if (FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF))
+ if (!sd_json_format_enabled(arg_json_format_flags))
r = table_add_many(
t,
TABLE_STRING, read_only ? "ro" : "rw",
diff --git a/src/journal/bsod.c b/src/journal/bsod.c
index cdf9e4874c..2f06808cd4 100644
--- a/src/journal/bsod.c
+++ b/src/journal/bsod.c
@@ -143,7 +143,7 @@ static int display_emergency_message_fullscreen(const char *message) {
unsigned qr_code_start_row = 1, qr_code_start_column = 1;
char ttybuf[STRLEN("/dev/tty") + DECIMAL_STR_MAX(int) + 1];
_cleanup_close_ int fd = -EBADF;
- _cleanup_fclose_ FILE *stream = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
char read_character_buffer = '\0';
struct winsize w = {
.ws_col = 80,
@@ -206,13 +206,15 @@ static int display_emergency_message_fullscreen(const char *message) {
goto cleanup;
}
- r = fdopen_independent(fd, "r+", &stream);
+ r = fdopen_independent(fd, "r+", &f);
if (r < 0) {
r = log_error_errno(errno, "Failed to open output file: %m");
goto cleanup;
}
- r = print_qrcode_full(stream, "Scan the QR code", message, qr_code_start_row, qr_code_start_column, w.ws_col, w.ws_row);
+ r = print_qrcode_full(f, "Scan the error message",
+ message, qr_code_start_row, qr_code_start_column, w.ws_col, w.ws_row,
+ /* check_tty= */ false);
if (r < 0)
log_warning_errno(r, "QR code could not be printed, ignoring: %m");
@@ -226,7 +228,7 @@ static int display_emergency_message_fullscreen(const char *message) {
goto cleanup;
}
- r = read_one_char(stream, &read_character_buffer, USEC_INFINITY, NULL);
+ r = read_one_char(f, &read_character_buffer, USEC_INFINITY, NULL);
if (r < 0 && r != -EINTR)
log_error_errno(r, "Failed to read character: %m");
diff --git a/src/journal/journalctl-authenticate.c b/src/journal/journalctl-authenticate.c
index 8167aef7f5..865814cd03 100644
--- a/src/journal/journalctl-authenticate.c
+++ b/src/journal/journalctl-authenticate.c
@@ -199,9 +199,7 @@ int action_setup_keys(void) {
if (!url)
return log_oom();
- (void) print_qrcode(stderr,
- "To transfer the verification key to your phone scan the QR code below",
- url);
+ (void) print_qrcode(stderr, "Scan the verification key to transfer it to another device", url);
#endif
return 0;
diff --git a/src/journal/journalctl-misc.c b/src/journal/journalctl-misc.c
index 5a1f311221..3dea69d9fb 100644
--- a/src/journal/journalctl-misc.c
+++ b/src/journal/journalctl-misc.c
@@ -333,7 +333,7 @@ int action_list_namespaces(void) {
}
}
- if (table_isempty(table) && FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF)) {
+ if (table_isempty(table) && !sd_json_format_enabled(arg_json_format_flags)) {
if (!arg_quiet)
log_notice("No namespaces found.");
} else {
diff --git a/src/kernel-install/kernel-install.c b/src/kernel-install/kernel-install.c
index f4f30df07d..f059f5dc76 100644
--- a/src/kernel-install/kernel-install.c
+++ b/src/kernel-install/kernel-install.c
@@ -1370,7 +1370,7 @@ static int verb_inspect(int argc, char *argv[], void *userdata) {
if (r < 0)
return table_log_add_error(r);
- if (arg_json_format_flags & SD_JSON_FORMAT_OFF) {
+ if (!sd_json_format_enabled(arg_json_format_flags)) {
r = table_add_many(t,
TABLE_FIELD, "Plugin Arguments",
TABLE_STRV, strv_skip(c->argv, 1));
diff --git a/src/libsystemd-network/ndisc-option.c b/src/libsystemd-network/ndisc-option.c
index 1071d98b19..d784ffb3ff 100644
--- a/src/libsystemd-network/ndisc-option.c
+++ b/src/libsystemd-network/ndisc-option.c
@@ -1358,6 +1358,11 @@ static int ndisc_option_parse_encrypted_dns(Set **options, size_t offset, size_t
r = ndisc_get_dns_name(opt + off, ilen, &res.auth_name);
if (r < 0)
return r;
+ r = dns_name_is_valid_ldh(res.auth_name);
+ if (r < 0)
+ return r;
+ if (!r)
+ return -EBADMSG;
if (dns_name_is_root(res.auth_name))
return -EBADMSG;
off += ilen;
diff --git a/src/libsystemd-network/sd-dhcp-lease.c b/src/libsystemd-network/sd-dhcp-lease.c
index 1c0cd6829b..fc891a0b04 100644
--- a/src/libsystemd-network/sd-dhcp-lease.c
+++ b/src/libsystemd-network/sd-dhcp-lease.c
@@ -628,6 +628,11 @@ static int lease_parse_dnr(const uint8_t *option, size_t len, sd_dns_resolver **
r = lease_parse_dns_name(option + offset, ilen, &res.auth_name);
if (r < 0)
return r;
+ r = dns_name_is_valid_ldh(res.auth_name);
+ if (r < 0)
+ return r;
+ if (!r)
+ return -EBADMSG;
if (dns_name_is_root(res.auth_name))
return -EBADMSG;
offset += ilen;
@@ -1551,7 +1556,8 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
r = deserialize_dnr(&lease->dnr, dnr);
if (r < 0)
log_debug_errno(r, "Failed to deserialize DNR servers %s, ignoring: %m", dnr);
- lease->n_dnr = r;
+ else
+ lease->n_dnr = r;
}
if (ntp) {
diff --git a/src/libsystemd-network/sd-dhcp6-lease.c b/src/libsystemd-network/sd-dhcp6-lease.c
index bc054c42b2..2ff1e87a2e 100644
--- a/src/libsystemd-network/sd-dhcp6-lease.c
+++ b/src/libsystemd-network/sd-dhcp6-lease.c
@@ -8,6 +8,7 @@
#include "alloc-util.h"
#include "dhcp6-internal.h"
#include "dhcp6-lease-internal.h"
+#include "dns-domain.h"
#include "network-common.h"
#include "sort-util.h"
#include "strv.h"
@@ -465,6 +466,11 @@ static int dhcp6_lease_add_dnr(sd_dhcp6_lease *lease, const uint8_t *optval, siz
r = dhcp6_option_parse_domainname(optval + offset, ilen, &res.auth_name);
if (r < 0)
return r;
+ r = dns_name_is_valid_ldh(res.auth_name);
+ if (r < 0)
+ return r;
+ if (!r)
+ return -EBADMSG;
offset += ilen;
/* RFC9463 § 3.1.6: adn only mode */
diff --git a/src/libsystemd/sd-bus/bus-common-errors.c b/src/libsystemd/sd-bus/bus-common-errors.c
index f765238ba7..03423bdcd3 100644
--- a/src/libsystemd/sd-bus/bus-common-errors.c
+++ b/src/libsystemd/sd-bus/bus-common-errors.c
@@ -60,8 +60,7 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = {
SD_BUS_ERROR_MAP(BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED, EOPNOTSUPP),
SD_BUS_ERROR_MAP(BUS_ERROR_SESSION_BUSY, EBUSY),
SD_BUS_ERROR_MAP(BUS_ERROR_NOT_YOUR_DEVICE, EPERM),
- /* needs to be EOPNOTSUPP for proper handling in callers of logind_schedule_shutdown() */
- SD_BUS_ERROR_MAP(BUS_ERROR_DESIGNATED_MAINTENANCE_TIME_NOT_SCHEDULED, EOPNOTSUPP),
+ SD_BUS_ERROR_MAP(BUS_ERROR_DESIGNATED_MAINTENANCE_TIME_NOT_SCHEDULED, EBADSLT),
SD_BUS_ERROR_MAP(BUS_ERROR_AUTOMATIC_TIME_SYNC_ENABLED, EALREADY),
SD_BUS_ERROR_MAP(BUS_ERROR_NO_NTP_SUPPORT, EOPNOTSUPP),
diff --git a/src/libsystemd/sd-json/json-util.h b/src/libsystemd/sd-json/json-util.h
index 2aeb076823..81c0fd02a6 100644
--- a/src/libsystemd/sd-json/json-util.h
+++ b/src/libsystemd/sd-json/json-util.h
@@ -157,6 +157,7 @@ enum {
_JSON_BUILD_PAIR_FINITE_USEC,
_JSON_BUILD_PAIR_STRING_NON_EMPTY,
_JSON_BUILD_PAIR_STRV_NON_EMPTY,
+ _JSON_BUILD_PAIR_STRV_ENV_PAIR_NON_EMPTY,
_JSON_BUILD_PAIR_VARIANT_NON_NULL,
/* _SD_JSON_BUILD_PAIR_VARIANT_ARRAY_NON_EMPTY, */
_JSON_BUILD_PAIR_BYTE_ARRAY_NON_EMPTY,
@@ -204,6 +205,7 @@ enum {
#define JSON_BUILD_PAIR_FINITE_USEC(name, u) _JSON_BUILD_PAIR_FINITE_USEC, (const char*) { name }, (usec_t) { u }
#define JSON_BUILD_PAIR_STRING_NON_EMPTY(name, s) _JSON_BUILD_PAIR_STRING_NON_EMPTY, (const char*) { name }, (const char*) { s }
#define JSON_BUILD_PAIR_STRV_NON_EMPTY(name, l) _JSON_BUILD_PAIR_STRV_NON_EMPTY, (const char*) { name }, (char**) { l }
+#define JSON_BUILD_PAIR_STRV_ENV_PAIR_NON_EMPTY(name, l) _JSON_BUILD_PAIR_STRV_ENV_PAIR_NON_EMPTY, (const char*) { name }, (char**) { l }
#define JSON_BUILD_PAIR_VARIANT_NON_NULL(name, v) _JSON_BUILD_PAIR_VARIANT_NON_NULL, (const char*) { name }, (sd_json_variant*) { v }
#define JSON_BUILD_PAIR_BYTE_ARRAY_NON_EMPTY(name, v, n) _JSON_BUILD_PAIR_BYTE_ARRAY_NON_EMPTY, (const char*) { name }, (const void*) { v }, (size_t) { n }
#define JSON_BUILD_PAIR_IN4_ADDR_NON_NULL(name, v) _JSON_BUILD_PAIR_IN4_ADDR_NON_NULL, (const char*) { name }, (const struct in_addr*) { v }
diff --git a/src/libsystemd/sd-json/sd-json.c b/src/libsystemd/sd-json/sd-json.c
index 297052bdb4..a8a5e47761 100644
--- a/src/libsystemd/sd-json/sd-json.c
+++ b/src/libsystemd/sd-json/sd-json.c
@@ -11,6 +11,7 @@
#include "alloc-util.h"
#include "ansi-color.h"
+#include "env-util.h"
#include "errno-util.h"
#include "escape.h"
#include "ether-addr-util.h"
@@ -1877,7 +1878,7 @@ _public_ int sd_json_variant_format(sd_json_variant *v, sd_json_format_flags_t f
assert_return(v, -EINVAL);
assert_return(ret, -EINVAL);
- if (flags & SD_JSON_FORMAT_OFF)
+ if (!sd_json_format_enabled(flags))
return -ENOEXEC;
f = memstream_init(&m);
@@ -3867,22 +3868,13 @@ _public_ int sd_json_buildv(sd_json_variant **ret, va_list ap) {
l = va_arg(ap, char **);
- _cleanup_strv_free_ char **el = NULL;
- STRV_FOREACH_PAIR(x, y, l) {
- char *n = NULL;
-
- n = strjoin(*x, "=", *y);
- if (!n) {
- r = -ENOMEM;
- goto finish;
- }
+ if (current->n_suppress == 0) {
+ _cleanup_strv_free_ char **el = NULL;
- r = strv_consume(&el, n);
+ r = strv_env_get_merged(l, &el);
if (r < 0)
goto finish;
- }
- if (current->n_suppress == 0) {
r = sd_json_variant_new_array_strv(&add, el);
if (r < 0)
goto finish;
@@ -4541,7 +4533,8 @@ _public_ int sd_json_buildv(sd_json_variant **ret, va_list ap) {
break;
}
- case _JSON_BUILD_PAIR_STRV_NON_EMPTY: {
+ case _JSON_BUILD_PAIR_STRV_NON_EMPTY:
+ case _JSON_BUILD_PAIR_STRV_ENV_PAIR_NON_EMPTY: {
const char *n;
char **l;
@@ -4554,11 +4547,19 @@ _public_ int sd_json_buildv(sd_json_variant **ret, va_list ap) {
l = va_arg(ap, char **);
if (!strv_isempty(l) && current->n_suppress == 0) {
+ _cleanup_strv_free_ char **el = NULL;
+
+ if (command == _JSON_BUILD_PAIR_STRV_ENV_PAIR_NON_EMPTY) {
+ r = strv_env_get_merged(l, &el);
+ if (r < 0)
+ goto finish;
+ }
+
r = sd_json_variant_new_string(&add, n);
if (r < 0)
goto finish;
- r = sd_json_variant_new_array_strv(&add_more, l);
+ r = sd_json_variant_new_array_strv(&add_more, el ?: l);
if (r < 0)
goto finish;
}
@@ -5614,9 +5615,7 @@ _public_ int sd_json_dispatch_id128(const char *name, sd_json_variant *variant,
}
_public_ int sd_json_dispatch_signal(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
- int *signo = userdata;
- uint32_t k;
- int r;
+ int *signo = ASSERT_PTR(userdata), r;
assert_return(variant, -EINVAL);
@@ -5625,7 +5624,8 @@ _public_ int sd_json_dispatch_signal(const char *name, sd_json_variant *variant,
return 0;
}
- r = sd_json_dispatch_uint32(name, variant, flags, &k);
+ int k;
+ r = sd_json_dispatch_int(name, variant, flags, &k);
if (r < 0)
return r;
diff --git a/src/libsystemd/sd-netlink/netlink-message-rtnl.c b/src/libsystemd/sd-netlink/netlink-message-rtnl.c
index 29c3af1fec..986e10c623 100644
--- a/src/libsystemd/sd-netlink/netlink-message-rtnl.c
+++ b/src/libsystemd/sd-netlink/netlink-message-rtnl.c
@@ -342,7 +342,7 @@ int sd_rtnl_message_new_link(sd_netlink *rtnl, sd_netlink_message **ret, uint16_
if (r < 0)
return r;
- if (nlmsg_type == RTM_NEWLINK)
+ if (nlmsg_type == RTM_NEWLINK && ifindex == 0)
(*ret)->hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_EXCL;
else if (nlmsg_type == RTM_NEWLINKPROP)
(*ret)->hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_EXCL | NLM_F_APPEND;
diff --git a/src/libsystemd/sd-varlink/sd-varlink.c b/src/libsystemd/sd-varlink/sd-varlink.c
index fb42e3205d..9c9160fd4e 100644
--- a/src/libsystemd/sd-varlink/sd-varlink.c
+++ b/src/libsystemd/sd-varlink/sd-varlink.c
@@ -1356,23 +1356,28 @@ static int varlink_dispatch_method(sd_varlink *v) {
r = varlink_idl_validate_method_call(v->current_method, parameters, flags, &bad_field);
if (r == -EBADE) {
- varlink_log_errno(v, r, "Method %s() called without 'more' flag, but flag needs to be set: %m",
+ varlink_log_errno(v, r, "Method %s() called without 'more' flag, but flag needs to be set.",
method);
if (v->state == VARLINK_PROCESSING_METHOD) {
r = sd_varlink_error(v, SD_VARLINK_ERROR_EXPECTED_MORE, NULL);
- if (r < 0)
- return r;
+ /* If we didn't manage to enqueue an error response, then fail the
+ * connection completely. Otherwise ignore the error from
+ * sd_varlink_error() here, as it is synthesized from the function's
+ * parameters. */
+ if (r < 0 && VARLINK_STATE_WANTS_REPLY(v->state))
+ goto fail;
}
} else if (r < 0) {
/* Please adjust test/units/end.sh when updating the log message. */
varlink_log_errno(v, r, "Parameters for method %s() didn't pass validation on field '%s': %m",
method, strna(bad_field));
- if (IN_SET(v->state, VARLINK_PROCESSING_METHOD, VARLINK_PROCESSING_METHOD_MORE)) {
+ if (VARLINK_STATE_WANTS_REPLY(v->state)) {
r = sd_varlink_error_invalid_parameter_name(v, bad_field);
- if (r < 0)
- return r;
+ /* If we didn't manage to enqueue an error response, then fail the connection completely. */
+ if (r < 0 && VARLINK_STATE_WANTS_REPLY(v->state))
+ goto fail;
}
}
@@ -1381,21 +1386,22 @@ static int varlink_dispatch_method(sd_varlink *v) {
if (!invalid) {
r = callback(v, parameters, flags, v->userdata);
- if (r < 0) {
+ if (r < 0 && VARLINK_STATE_WANTS_REPLY(v->state)) {
varlink_log_errno(v, r, "Callback for %s returned error: %m", method);
- /* We got an error back from the callback. Propagate it to the client if the method call remains unanswered. */
- if (IN_SET(v->state, VARLINK_PROCESSING_METHOD, VARLINK_PROCESSING_METHOD_MORE)) {
- r = sd_varlink_error_errno(v, r);
- if (r < 0)
- return r;
- }
+ /* We got an error back from the callback. Propagate it to the client if the
+ * method call remains unanswered. */
+ r = sd_varlink_error_errno(v, r);
+ /* If we didn't manage to enqueue an error response, then fail the connection completely. */
+ if (r < 0 && VARLINK_STATE_WANTS_REPLY(v->state))
+ goto fail;
}
}
- } else if (IN_SET(v->state, VARLINK_PROCESSING_METHOD, VARLINK_PROCESSING_METHOD_MORE)) {
+ } else if (VARLINK_STATE_WANTS_REPLY(v->state)) {
r = sd_varlink_errorbo(v, SD_VARLINK_ERROR_METHOD_NOT_FOUND, SD_JSON_BUILD_PAIR("method", SD_JSON_BUILD_STRING(method)));
- if (r < 0)
- return r;
+ /* If we didn't manage to enqueue an error response, then fail the connection completely. */
+ if (r < 0 && VARLINK_STATE_WANTS_REPLY(v->state))
+ goto fail;
}
switch (v->state) {
@@ -2558,7 +2564,10 @@ _public_ int sd_varlink_error(sd_varlink *v, const char *error_id, sd_json_varia
} else
varlink_set_state(v, VARLINK_PROCESSED_METHOD);
- return 1;
+ /* Everything worked. Let's now return the error we got passed as input as negative errno, so that
+ * programs can just do "return sd_varlink_error();" and get both: a friendly error reply to clients,
+ * and an error return from the current stack frame. */
+ return sd_varlink_error_to_errno(error_id, parameters);
}
_public_ int sd_varlink_errorb(sd_varlink *v, const char *error_id, ...) {
diff --git a/src/libsystemd/sd-varlink/varlink-internal.h b/src/libsystemd/sd-varlink/varlink-internal.h
index 5469d9e345..b184ac7754 100644
--- a/src/libsystemd/sd-varlink/varlink-internal.h
+++ b/src/libsystemd/sd-varlink/varlink-internal.h
@@ -63,6 +63,13 @@ typedef enum VarlinkState {
VARLINK_PENDING_METHOD, \
VARLINK_PENDING_METHOD_MORE)
+/* Tests whether we are expected to generate a method call reply, i.e. are processing a method call, except
+ * one with the ONEWAY flag set. */
+#define VARLINK_STATE_WANTS_REPLY(state) \
+ IN_SET(state, \
+ VARLINK_PROCESSING_METHOD, \
+ VARLINK_PROCESSING_METHOD_MORE)
+
typedef struct VarlinkJsonQueueItem VarlinkJsonQueueItem;
/* A queued message we shall write into the socket, along with the file descriptors to send at the same
diff --git a/src/login/inhibit.c b/src/login/inhibit.c
index 70950438d8..bb99ae9c6d 100644
--- a/src/login/inhibit.c
+++ b/src/login/inhibit.c
@@ -157,7 +157,7 @@ static int help(void) {
" handle-lid-switch\n"
" --who=STRING A descriptive string who is inhibiting\n"
" --why=STRING A descriptive string why is being inhibited\n"
- " --mode=MODE One of block, block-weak, delay, or delay-weak\n"
+ " --mode=MODE One of block, block-weak, or delay\n"
" --list List active inhibitors\n"
"\nSee the %s for details.\n",
program_invocation_short_name,
diff --git a/src/login/loginctl.c b/src/login/loginctl.c
index cdb017a931..bda69de076 100644
--- a/src/login/loginctl.c
+++ b/src/login/loginctl.c
@@ -1628,7 +1628,7 @@ static int parse_argv(int argc, char *argv[]) {
if (r <= 0)
return r;
- if (!FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF))
+ if (sd_json_format_enabled(arg_json_format_flags))
arg_legend = false;
break;
diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c
index 5de882e42b..7557ab2d93 100644
--- a/src/login/logind-dbus.c
+++ b/src/login/logind-dbus.c
@@ -310,7 +310,14 @@ static int property_get_inhibited(
assert(bus);
assert(reply);
- w = manager_inhibit_what(m, /* block= */ streq(property, "BlockInhibited"));
+ if (streq(property, "BlockInhibited"))
+ w = manager_inhibit_what(m, INHIBIT_BLOCK);
+ else if (streq(property, "BlockWeakInhibited"))
+ w = manager_inhibit_what(m, INHIBIT_BLOCK_WEAK);
+ else if (streq(property, "DelayInhibited"))
+ w = manager_inhibit_what(m, INHIBIT_DELAY);
+ else
+ assert_not_reached();
return sd_bus_message_append(reply, "s", inhibit_what_to_string(w));
}
@@ -2656,11 +2663,11 @@ static int method_schedule_shutdown(sd_bus_message *message, void *userdata, sd_
if (r < 0) {
if (r == -ENOENT)
return sd_bus_error_set(error,
- BUS_ERROR_DESIGNATED_MAINTENANCE_TIME_NOT_SCHEDULED,
- "No upcoming maintenance window scheduled");
- return sd_bus_error_setf(error,
- BUS_ERROR_DESIGNATED_MAINTENANCE_TIME_NOT_SCHEDULED,
- "Failed to determine next maintenance window");
+ BUS_ERROR_DESIGNATED_MAINTENANCE_TIME_NOT_SCHEDULED,
+ "No upcoming maintenance window scheduled");
+
+ return sd_bus_error_set_errnof(error, r,
+ "Failed to determine next maintenance window: %m");
}
log_info("Scheduled %s at maintenance window %s", type, FORMAT_TIMESTAMP(elapse));
@@ -3640,7 +3647,7 @@ static int method_inhibit(sd_bus_message *message, void *userdata, sd_bus_error
"Invalid mode specification %s", mode);
/* Delay is only supported for shutdown/sleep */
- if (IN_SET(mm, INHIBIT_DELAY, INHIBIT_DELAY_WEAK) && (w & ~(INHIBIT_SHUTDOWN|INHIBIT_SLEEP)))
+ if (mm == INHIBIT_DELAY && (w & ~(INHIBIT_SHUTDOWN|INHIBIT_SLEEP)))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Delay inhibitors only supported for shutdown and sleep");
@@ -3745,6 +3752,7 @@ static const sd_bus_vtable manager_vtable[] = {
SD_BUS_PROPERTY("IdleSinceHint", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("IdleSinceHintMonotonic", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("BlockInhibited", "s", property_get_inhibited, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+ SD_BUS_PROPERTY("BlockWeakInhibited", "s", property_get_inhibited, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("DelayInhibited", "s", property_get_inhibited, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("InhibitDelayMaxUSec", "t", NULL, offsetof(Manager, inhibit_delay_max), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("UserStopDelayUSec", "t", NULL, offsetof(Manager, user_stop_delay), SD_BUS_VTABLE_PROPERTY_CONST),
diff --git a/src/login/logind-inhibit.c b/src/login/logind-inhibit.c
index c1798f927c..27ad8f1652 100644
--- a/src/login/logind-inhibit.c
+++ b/src/login/logind-inhibit.c
@@ -363,16 +363,14 @@ bool inhibitor_is_orphan(Inhibitor *i) {
return false;
}
-InhibitWhat manager_inhibit_what(Manager *m, bool block) {
+InhibitWhat manager_inhibit_what(Manager *m, InhibitMode mode) {
Inhibitor *i;
InhibitWhat what = 0;
assert(m);
HASHMAP_FOREACH(i, m->inhibitors)
- if (i->started &&
- ((!block && IN_SET(i->mode, INHIBIT_DELAY, INHIBIT_DELAY_WEAK)) ||
- (block && IN_SET(i->mode, INHIBIT_BLOCK, INHIBIT_BLOCK_WEAK))))
+ if (i->started && i->mode == mode)
what |= i->what;
return what;
@@ -424,13 +422,13 @@ bool manager_is_inhibited(
continue;
if ((block && !IN_SET(i->mode, INHIBIT_BLOCK, INHIBIT_BLOCK_WEAK)) ||
- (!block && !IN_SET(i->mode, INHIBIT_DELAY, INHIBIT_DELAY_WEAK)))
+ (!block && i->mode != INHIBIT_DELAY))
continue;
if (ignore_inactive && pidref_is_active_session(m, &i->pid) <= 0)
continue;
- if (IN_SET(i->mode, INHIBIT_BLOCK_WEAK, INHIBIT_DELAY_WEAK) && ignore_uid && i->uid == uid)
+ if (i->mode == INHIBIT_BLOCK_WEAK && ignore_uid && i->uid == uid)
continue;
if (!inhibited ||
@@ -531,7 +529,6 @@ static const char* const inhibit_mode_table[_INHIBIT_MODE_MAX] = {
[INHIBIT_BLOCK] = "block",
[INHIBIT_BLOCK_WEAK] = "block-weak",
[INHIBIT_DELAY] = "delay",
- [INHIBIT_DELAY_WEAK] = "delay-weak"
};
DEFINE_STRING_TABLE_LOOKUP(inhibit_mode, InhibitMode);
diff --git a/src/login/logind-inhibit.h b/src/login/logind-inhibit.h
index b5167974af..16abd6958c 100644
--- a/src/login/logind-inhibit.h
+++ b/src/login/logind-inhibit.h
@@ -22,7 +22,6 @@ typedef enum InhibitMode {
INHIBIT_BLOCK,
INHIBIT_BLOCK_WEAK,
INHIBIT_DELAY,
- INHIBIT_DELAY_WEAK,
_INHIBIT_MODE_MAX,
_INHIBIT_MODE_INVALID = -EINVAL,
} InhibitMode;
@@ -67,7 +66,7 @@ int inhibitor_create_fifo(Inhibitor *i);
bool inhibitor_is_orphan(Inhibitor *i);
-InhibitWhat manager_inhibit_what(Manager *m, bool block);
+InhibitWhat manager_inhibit_what(Manager *m, InhibitMode mode);
bool manager_is_inhibited(Manager *m, InhibitWhat w, bool block, dual_timestamp *since, bool ignore_inactive, bool ignore_uid, uid_t uid, Inhibitor **offending);
static inline bool inhibit_what_is_valid(InhibitWhat w) {
diff --git a/src/machine/machine-dbus.c b/src/machine/machine-dbus.c
index 30abc659f8..de70c0a188 100644
--- a/src/machine/machine-dbus.c
+++ b/src/machine/machine-dbus.c
@@ -407,7 +407,7 @@ int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bu
_cleanup_strv_free_ char **env = NULL, **args_wire = NULL, **args = NULL;
_cleanup_free_ char *command_line = NULL;
Machine *m = ASSERT_PTR(userdata);
- const char *p, *unit, *user, *path, *description, *utmp_id;
+ const char *unit, *user, *path, *description, *utmp_id;
int r;
assert(message);
@@ -484,10 +484,11 @@ int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bu
if (master < 0)
return master;
- p = path_startswith(pty_name, "/dev/pts/");
- assert(p);
-
- slave = machine_open_terminal(m, pty_name, O_RDWR|O_NOCTTY|O_CLOEXEC);
+ /* First try to get an fd for the PTY peer via the new racefree ioctl(), directly. Otherwise go via
+ * joining the namespace, because it goes by path */
+ slave = pty_open_peer_racefree(master, O_RDWR|O_NOCTTY|O_CLOEXEC);
+ if (ERRNO_IS_NEG_NOT_SUPPORTED(slave))
+ slave = machine_open_terminal(m, pty_name, O_RDWR|O_NOCTTY|O_CLOEXEC);
if (slave < 0)
return slave;
@@ -505,6 +506,8 @@ int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bu
return r;
/* Name and mode */
+ const char *p = ASSERT_PTR(path_startswith(pty_name, "/dev/pts/"));
+
unit = strjoina("container-shell@", p, ".service");
r = sd_bus_message_append(tm, "ss", unit, "fail");
if (r < 0)
diff --git a/src/machine/machine-varlink.c b/src/machine/machine-varlink.c
index c9f44f20ab..bfa4095a3b 100644
--- a/src/machine/machine-varlink.c
+++ b/src/machine/machine-varlink.c
@@ -236,8 +236,6 @@ int lookup_machine_by_name_or_pidref(sd_varlink *link, Manager *manager, const c
assert(manager);
assert(ret_machine);
- /* This returns 0 on success, 1 on error and it is replied, and a negative errno otherwise. */
-
if (machine_name) {
r = lookup_machine_by_name(link, manager, machine_name, &machine);
if (r == -EINVAL)
@@ -350,7 +348,7 @@ int vl_method_kill(sd_varlink *link, sd_json_variant *parameters, sd_varlink_met
r = lookup_machine_by_name_or_pidref(link, manager, p.name, &p.pidref, &machine);
if (r == -ESRCH)
return sd_varlink_error(link, "io.systemd.Machine.NoSuchMachine", NULL);
- if (r != 0)
+ if (r < 0)
return r;
if (isempty(p.swhom))
diff --git a/src/machine/machined-varlink.c b/src/machine/machined-varlink.c
index 2c2eb2d918..151d06e5f4 100644
--- a/src/machine/machined-varlink.c
+++ b/src/machine/machined-varlink.c
@@ -392,56 +392,49 @@ static int vl_method_get_memberships(sd_varlink *link, sd_json_variant *paramete
}
static int json_build_local_addresses(const struct local_address *addresses, size_t n_addresses, sd_json_variant **ret) {
+ _cleanup_(sd_json_variant_unrefp) sd_json_variant *array = NULL;
int r;
- if (n_addresses == 0)
- return 0;
-
- assert(addresses);
+ assert(addresses || n_addresses == 0);
assert(ret);
FOREACH_ARRAY(a, addresses, n_addresses) {
- _cleanup_(sd_json_variant_unrefp) sd_json_variant *entry = NULL;
- r = sd_json_buildo(
- &entry,
+ r = sd_json_variant_append_arraybo(
+ &array,
JSON_BUILD_PAIR_UNSIGNED_NON_ZERO("ifindex", a->ifindex),
SD_JSON_BUILD_PAIR_INTEGER("family", a->family),
SD_JSON_BUILD_PAIR_BYTE_ARRAY("address", &a->address.bytes, FAMILY_ADDRESS_SIZE(a->family)));
if (r < 0)
return r;
-
- r = sd_json_variant_append_array(ret, entry);
- if (r < 0)
- return r;
}
+ *ret = TAKE_PTR(array);
return 0;
}
static int list_machine_one_and_maybe_read_metadata(sd_varlink *link, Machine *m, bool more, AcquireMetadata am) {
- _cleanup_(sd_json_variant_unrefp) sd_json_variant *addr_array = NULL;
+ _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL, *addr_array = NULL;
_cleanup_strv_free_ char **os_release = NULL;
uid_t shift = UID_INVALID;
- int r, n = 0;
+ int r;
assert(link);
assert(m);
- _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
-
if (should_acquire_metadata(am)) {
_cleanup_free_ struct local_address *addresses = NULL;
- n = machine_get_addresses(m, &addresses);
- if (n < 0 && am == ACQUIRE_METADATA_GRACEFUL)
- log_debug_errno(n, "Failed to get address (graceful mode), ignoring: %m");
- else if (n == -ENONET)
+
+ r = machine_get_addresses(m, &addresses);
+ if (r < 0 && am == ACQUIRE_METADATA_GRACEFUL)
+ log_debug_errno(r, "Failed to get address (graceful mode), ignoring: %m");
+ else if (r == -ENONET)
return sd_varlink_error(link, "io.systemd.Machine.NoPrivateNetworking", NULL);
- else if (ERRNO_IS_NEG_NOT_SUPPORTED(n))
+ else if (ERRNO_IS_NEG_NOT_SUPPORTED(r))
return sd_varlink_error(link, "io.systemd.Machine.NotAvailable", NULL);
- else if (n < 0)
- return log_debug_errno(n, "Failed to get addresses: %m");
+ else if (r < 0)
+ return log_debug_errno(r, "Failed to get addresses: %m");
else {
- r = json_build_local_addresses(addresses, n, &addr_array);
+ r = json_build_local_addresses(addresses, r, &addr_array);
if (r < 0)
return r;
}
@@ -477,11 +470,11 @@ static int list_machine_one_and_maybe_read_metadata(sd_varlink *link, Machine *m
JSON_BUILD_PAIR_STRING_NON_EMPTY("unit", m->unit),
SD_JSON_BUILD_PAIR_CONDITION(pidref_is_set(&m->leader), "leader", JSON_BUILD_PIDREF(&m->leader)),
SD_JSON_BUILD_PAIR_CONDITION(dual_timestamp_is_set(&m->timestamp), "timestamp", JSON_BUILD_DUAL_TIMESTAMP(&m->timestamp)),
- SD_JSON_BUILD_PAIR_CONDITION(m->vsock_cid != VMADDR_CID_ANY, "vSockCid", SD_JSON_BUILD_UNSIGNED(m->vsock_cid)),
+ JSON_BUILD_PAIR_UNSIGNED_NOT_EQUAL("vSockCid", m->vsock_cid, VMADDR_CID_ANY),
JSON_BUILD_PAIR_STRING_NON_EMPTY("sshAddress", m->ssh_address),
JSON_BUILD_PAIR_STRING_NON_EMPTY("sshPrivateKeyPath", m->ssh_private_key_path),
- SD_JSON_BUILD_PAIR_CONDITION(n > 0, "addresses", SD_JSON_BUILD_VARIANT(addr_array)),
- SD_JSON_BUILD_PAIR_CONDITION(!strv_isempty(os_release), "OSRelease", JSON_BUILD_STRV_ENV_PAIR(os_release)),
+ JSON_BUILD_PAIR_VARIANT_NON_NULL("addresses", addr_array),
+ JSON_BUILD_PAIR_STRV_ENV_PAIR_NON_EMPTY("OSRelease", os_release),
JSON_BUILD_PAIR_UNSIGNED_NOT_EQUAL("UIDShift", shift, UID_INVALID));
if (r < 0)
return r;
@@ -517,7 +510,6 @@ static int vl_method_list(sd_varlink *link, sd_json_variant *parameters, sd_varl
Manager *m = ASSERT_PTR(userdata);
_cleanup_(machine_lookup_parameters_done) MachineLookupParameters p = {
.pidref = PIDREF_NULL,
- .acquire_metadata = ACQUIRE_METADATA_NO,
};
Machine *machine;
@@ -534,7 +526,7 @@ static int vl_method_list(sd_varlink *link, sd_json_variant *parameters, sd_varl
r = lookup_machine_by_name_or_pidref(link, m, p.name, &p.pidref, &machine);
if (r == -ESRCH)
return sd_varlink_error(link, "io.systemd.Machine.NoSuchMachine", NULL);
- if (r != 0)
+ if (r < 0)
return r;
return list_machine_one_and_maybe_read_metadata(link, machine, /* more = */ false, p.acquire_metadata);
@@ -584,7 +576,7 @@ static int lookup_machine_and_call_method(sd_varlink *link, sd_json_variant *par
r = lookup_machine_by_name_or_pidref(link, manager, p.name, &p.pidref, &machine);
if (r == -ESRCH)
return sd_varlink_error(link, "io.systemd.Machine.NoSuchMachine", NULL);
- if (r != 0)
+ if (r < 0)
return r;
return method(link, parameters, flags, machine);
@@ -635,8 +627,8 @@ static int list_image_one_and_maybe_read_metadata(sd_varlink *link, Image *image
&v,
JSON_BUILD_PAIR_STRING_NON_EMPTY("hostname", image->hostname),
SD_JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(image->machine_id), "machineId", SD_JSON_BUILD_ID128(image->machine_id)),
- SD_JSON_BUILD_PAIR_CONDITION(!strv_isempty(image->machine_info), "machineInfo", JSON_BUILD_STRV_ENV_PAIR(image->machine_info)),
- SD_JSON_BUILD_PAIR_CONDITION(!strv_isempty(image->os_release), "OSRelease", JSON_BUILD_STRV_ENV_PAIR(image->os_release)));
+ JSON_BUILD_PAIR_STRV_ENV_PAIR_NON_EMPTY("machineInfo", image->machine_info),
+ JSON_BUILD_PAIR_STRV_ENV_PAIR_NON_EMPTY("OSRelease", image->os_release));
if (r < 0)
return r;
}
@@ -651,7 +643,7 @@ static int vl_method_list_images(sd_varlink *link, sd_json_variant *parameters,
struct params {
const char *image_name;
AcquireMetadata acquire_metadata;
- } p = { .acquire_metadata = ACQUIRE_METADATA_NO };
+ } p = {};
int r;
static const sd_json_dispatch_field dispatch_table[] = {
diff --git a/src/network/fuzz-netdev-parser.c b/src/network/fuzz-netdev-parser.c
index f0988bd4cc..7e29ba9b8e 100644
--- a/src/network/fuzz-netdev-parser.c
+++ b/src/network/fuzz-netdev-parser.c
@@ -10,6 +10,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
_cleanup_(manager_freep) Manager *manager = NULL;
_cleanup_fclose_ FILE *f = NULL;
_cleanup_(unlink_tempfilep) char netdev_config[] = "/tmp/fuzz-networkd.XXXXXX";
+ _cleanup_(netdev_unrefp) NetDev *netdev = NULL;
if (outside_size_range(size, 0, 65536))
return 0;
@@ -22,6 +23,6 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
fflush(f);
assert_se(manager_new(&manager, /* test_mode = */ true) >= 0);
- (void) netdev_load_one(manager, netdev_config);
+ (void) netdev_load_one(manager, netdev_config, &netdev);
return 0;
}
diff --git a/src/network/netdev/batadv.c b/src/network/netdev/batadv.c
index e600727c20..9806d8eb7c 100644
--- a/src/network/netdev/batadv.c
+++ b/src/network/netdev/batadv.c
@@ -163,6 +163,9 @@ static int netdev_batadv_post_create(NetDev *netdev, Link *link) {
assert(netdev);
+ if (!netdev_is_managed(netdev))
+ return 0; /* Already detached, due to e.g. reloading .netdev files. */
+
r = sd_genl_message_new(netdev->manager->genl, BATADV_NL_NAME, BATADV_CMD_SET_MESH, &message);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not allocate netlink message: %m");
diff --git a/src/network/netdev/bridge.c b/src/network/netdev/bridge.c
index 25b0f81aa0..da5b332277 100644
--- a/src/network/netdev/bridge.c
+++ b/src/network/netdev/bridge.c
@@ -159,14 +159,13 @@ static int netdev_bridge_post_create(NetDev *netdev, Link *link) {
assert(netdev);
+ if (!netdev_is_managed(netdev))
+ return 0; /* Already detached, due to e.g. reloading .netdev files. */
+
r = sd_rtnl_message_new_link(netdev->manager->rtnl, &req, RTM_NEWLINK, netdev->ifindex);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not allocate netlink message: %m");
- r = sd_netlink_message_set_flags(req, NLM_F_REQUEST | NLM_F_ACK);
- if (r < 0)
- return log_link_error_errno(link, r, "Could not set netlink message flags: %m");
-
r = netdev_bridge_post_create_message(netdev, req);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not create netlink message: %m");
diff --git a/src/network/netdev/fou-tunnel.c b/src/network/netdev/fou-tunnel.c
index e962c6c64b..a85aff2e6d 100644
--- a/src/network/netdev/fou-tunnel.c
+++ b/src/network/netdev/fou-tunnel.c
@@ -89,6 +89,7 @@ static int netdev_create_fou_tunnel_message(NetDev *netdev, sd_netlink_message *
int r;
assert(netdev);
+ assert(netdev->manager);
r = sd_genl_message_new(netdev->manager->genl, FOU_GENL_NAME, FOU_CMD_ADD, &m);
if (r < 0)
@@ -128,6 +129,9 @@ static int netdev_fou_tunnel_create(NetDev *netdev) {
assert(FOU(netdev));
+ if (!netdev_is_managed(netdev))
+ return 0; /* Already detached, due to e.g. reloading .netdev files. */
+
r = netdev_create_fou_tunnel_message(netdev, &m);
if (r < 0)
return r;
diff --git a/src/network/netdev/ipoib.c b/src/network/netdev/ipoib.c
index 0065a5452e..6932c62e2a 100644
--- a/src/network/netdev/ipoib.c
+++ b/src/network/netdev/ipoib.c
@@ -53,10 +53,6 @@ int ipoib_set_netlink_message(Link *link, sd_netlink_message *m) {
assert(link->network);
assert(m);
- r = sd_netlink_message_set_flags(m, NLM_F_REQUEST | NLM_F_ACK);
- if (r < 0)
- return r;
-
r = sd_netlink_message_open_container(m, IFLA_LINKINFO);
if (r < 0)
return r;
diff --git a/src/network/netdev/ipvlan.c b/src/network/netdev/ipvlan.c
index 892678b4ad..5cfae1eb64 100644
--- a/src/network/netdev/ipvlan.c
+++ b/src/network/netdev/ipvlan.c
@@ -37,6 +37,13 @@ static int netdev_ipvlan_fill_message_create(NetDev *netdev, Link *link, sd_netl
return 0;
}
+static bool ipvlan_can_set_mac(NetDev *netdev, const struct hw_addr_data *hw_addr) {
+ assert(netdev);
+
+ /* MAC address cannot be updated. Even unchanged, IFLA_ADDRESS attribute cannot be set in the message. */
+ return netdev->ifindex <= 0;
+}
+
static void ipvlan_init(NetDev *netdev) {
IPVlan *m = ASSERT_PTR(netdev)->kind == NETDEV_KIND_IPVLAN ? IPVLAN(netdev) : IPVTAP(netdev);
@@ -50,6 +57,7 @@ const NetDevVTable ipvlan_vtable = {
.sections = NETDEV_COMMON_SECTIONS "IPVLAN\0",
.fill_message_create = netdev_ipvlan_fill_message_create,
.create_type = NETDEV_CREATE_STACKED,
+ .can_set_mac = ipvlan_can_set_mac,
.iftype = ARPHRD_ETHER,
.generate_mac = true,
};
@@ -60,6 +68,7 @@ const NetDevVTable ipvtap_vtable = {
.sections = NETDEV_COMMON_SECTIONS "IPVTAP\0",
.fill_message_create = netdev_ipvlan_fill_message_create,
.create_type = NETDEV_CREATE_STACKED,
+ .can_set_mac = ipvlan_can_set_mac,
.iftype = ARPHRD_ETHER,
.generate_mac = true,
};
diff --git a/src/network/netdev/l2tp-tunnel.c b/src/network/netdev/l2tp-tunnel.c
index c1372bb1e0..c87e44797b 100644
--- a/src/network/netdev/l2tp-tunnel.c
+++ b/src/network/netdev/l2tp-tunnel.c
@@ -94,6 +94,8 @@ static int l2tp_session_new_static(L2tpTunnel *t, const char *filename, unsigned
static int netdev_l2tp_create_message_tunnel(NetDev *netdev, union in_addr_union *local_address, sd_netlink_message **ret) {
assert(local_address);
+ assert(netdev);
+ assert(netdev->manager);
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
uint16_t encap_type;
@@ -188,6 +190,7 @@ static int netdev_l2tp_create_message_session(NetDev *netdev, L2tpSession *sessi
int r;
assert(netdev);
+ assert(netdev->manager);
assert(session);
assert(session->tunnel);
@@ -385,6 +388,11 @@ static int l2tp_create_session(NetDev *netdev, L2tpSession *session) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *n = NULL;
int r;
+ assert(netdev);
+
+ if (!netdev_is_managed(netdev))
+ return 0; /* Already detached, due to e.g. reloading .netdev files. */
+
r = netdev_l2tp_create_message_session(netdev, session, &n);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Failed to create netlink message: %m");
@@ -429,6 +437,9 @@ static int l2tp_create_tunnel(NetDev *netdev) {
L2tpTunnel *t = L2TP(netdev);
int r;
+ if (!netdev_is_managed(netdev))
+ return 0; /* Already detached, due to e.g. reloading .netdev files. */
+
r = l2tp_get_local_address(netdev, &local_address);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not find local address.");
diff --git a/src/network/netdev/macsec.c b/src/network/netdev/macsec.c
index 187da41344..6dd434f803 100644
--- a/src/network/netdev/macsec.c
+++ b/src/network/netdev/macsec.c
@@ -224,6 +224,7 @@ static int netdev_macsec_create_message(NetDev *netdev, int command, sd_netlink_
assert(netdev);
assert(netdev->ifindex > 0);
+ assert(netdev->manager);
r = sd_genl_message_new(netdev->manager->genl, MACSEC_GENL_NAME, command, &m);
if (r < 0)
@@ -334,6 +335,9 @@ static int netdev_macsec_configure_receive_association(NetDev *netdev, ReceiveAs
assert(netdev);
assert(a);
+ if (!netdev_is_managed(netdev))
+ return 0; /* Already detached, due to e.g. reloading .netdev files. */
+
r = netdev_macsec_create_message(netdev, MACSEC_CMD_ADD_RXSA, &m);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Failed to create netlink message: %m");
@@ -406,6 +410,9 @@ static int netdev_macsec_configure_receive_channel(NetDev *netdev, ReceiveChanne
assert(netdev);
assert(c);
+ if (!netdev_is_managed(netdev))
+ return 0; /* Already detached, due to e.g. reloading .netdev files. */
+
r = netdev_macsec_create_message(netdev, MACSEC_CMD_ADD_RXSC, &m);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Failed to create netlink message: %m");
@@ -454,6 +461,9 @@ static int netdev_macsec_configure_transmit_association(NetDev *netdev, Transmit
assert(netdev);
assert(a);
+ if (!netdev_is_managed(netdev))
+ return 0; /* Already detached, due to e.g. reloading .netdev files. */
+
r = netdev_macsec_create_message(netdev, MACSEC_CMD_ADD_TXSA, &m);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Failed to create netlink message: %m");
@@ -499,12 +509,6 @@ static int netdev_macsec_fill_message_create(NetDev *netdev, Link *link, sd_netl
MACsec *v = MACSEC(netdev);
int r;
- if (v->port > 0) {
- r = sd_netlink_message_append_u16(m, IFLA_MACSEC_PORT, v->port);
- if (r < 0)
- return r;
- }
-
if (v->encrypt >= 0) {
r = sd_netlink_message_append_u8(m, IFLA_MACSEC_ENCRYPT, v->encrypt);
if (r < 0)
@@ -515,6 +519,20 @@ static int netdev_macsec_fill_message_create(NetDev *netdev, Link *link, sd_netl
if (r < 0)
return r;
+ /* The properties below cannot be updated, and the kernel refuses the whole request if one of the
+ * following attributes is set for an existing interface. */
+ if (netdev->ifindex > 0)
+ return 0;
+
+ if (v->port > 0) {
+ r = sd_netlink_message_append_u16(m, IFLA_MACSEC_PORT, v->port);
+ if (r < 0)
+ return r;
+ }
+
+ /* Currently not supported by networkd, but IFLA_MACSEC_CIPHER_SUITE, IFLA_MACSEC_ICV_LEN, and
+ * IFLA_MACSEC_SCI can neither set for an existing interface. */
+
return 0;
}
diff --git a/src/network/netdev/netdev.c b/src/network/netdev/netdev.c
index 8e58a1ae12..85760c741d 100644
--- a/src/network/netdev/netdev.c
+++ b/src/network/netdev/netdev.c
@@ -34,6 +34,7 @@
#include "networkd-queue.h"
#include "networkd-setlink.h"
#include "networkd-sriov.h"
+#include "networkd-state-file.h"
#include "nlmon.h"
#include "path-lookup.h"
#include "siphash24.h"
@@ -241,6 +242,7 @@ static NetDev* netdev_free(NetDev *netdev) {
condition_free_list(netdev->conditions);
free(netdev->filename);
strv_free(netdev->dropins);
+ hashmap_free(netdev->stats_by_path);
free(netdev->description);
free(netdev->ifname);
@@ -273,18 +275,17 @@ void netdev_drop(NetDev *netdev) {
netdev_detach(netdev);
}
-int netdev_attach_name(NetDev *netdev, const char *name) {
+static int netdev_attach_name_full(NetDev *netdev, const char *name, Hashmap **netdevs) {
int r;
assert(netdev);
- assert(netdev->manager);
assert(name);
- r = hashmap_ensure_put(&netdev->manager->netdevs, &string_hash_ops, name, netdev);
+ r = hashmap_ensure_put(netdevs, &string_hash_ops, name, netdev);
if (r == -ENOMEM)
return log_oom();
if (r == -EEXIST) {
- NetDev *n = hashmap_get(netdev->manager->netdevs, name);
+ NetDev *n = hashmap_get(*netdevs, name);
assert(n);
if (!streq(netdev->filename, n->filename))
@@ -299,6 +300,13 @@ int netdev_attach_name(NetDev *netdev, const char *name) {
return 0;
}
+int netdev_attach_name(NetDev *netdev, const char *name) {
+ assert(netdev);
+ assert(netdev->manager);
+
+ return netdev_attach_name_full(netdev, name, &netdev->manager->netdevs);
+}
+
static int netdev_attach(NetDev *netdev) {
int r;
@@ -345,16 +353,16 @@ void link_assign_netdev(Link *link) {
old = TAKE_PTR(link->netdev);
if (netdev_get(link->manager, link->ifname, &netdev) < 0)
- return;
+ goto not_found;
int ifindex = NETDEV_VTABLE(netdev)->get_ifindex ?
NETDEV_VTABLE(netdev)->get_ifindex(netdev, link->ifname) :
netdev->ifindex;
if (ifindex != link->ifindex)
- return;
+ goto not_found;
if (NETDEV_VTABLE(netdev)->iftype != link->iftype)
- return;
+ goto not_found;
if (!NETDEV_VTABLE(netdev)->skip_netdev_kind_check) {
const char *kind;
@@ -365,13 +373,23 @@ void link_assign_netdev(Link *link) {
kind = netdev_kind_to_string(netdev->kind);
if (!streq_ptr(kind, link->kind))
- return;
+ goto not_found;
}
link->netdev = netdev_ref(netdev);
- if (netdev != old)
- log_link_debug(link, "Found matching .netdev file: %s", netdev->filename);
+ if (netdev == old)
+ return; /* The same NetDev found. */
+
+ log_link_debug(link, "Found matching .netdev file: %s", netdev->filename);
+ link_dirty(link);
+ return;
+
+not_found:
+
+ if (old)
+ /* Previously assigned NetDev is detached from Manager? Update the state file. */
+ link_dirty(link);
}
void netdev_enter_failed(NetDev *netdev) {
@@ -395,6 +413,17 @@ int netdev_enter_ready(NetDev *netdev) {
return 0;
}
+bool netdev_needs_reconfigure(NetDev *netdev, NetDevLocalAddressType type) {
+ assert(netdev);
+ assert(type < _NETDEV_LOCAL_ADDRESS_TYPE_MAX);
+
+ if (type < 0)
+ return true;
+
+ return NETDEV_VTABLE(netdev)->needs_reconfigure &&
+ NETDEV_VTABLE(netdev)->needs_reconfigure(netdev, type);
+}
+
/* callback for netdev's created without a backing Link */
static int netdev_create_handler(sd_netlink *rtnl, sd_netlink_message *m, NetDev *netdev) {
int r;
@@ -605,6 +634,31 @@ finalize:
return 0;
}
+static bool netdev_can_set_mac(NetDev *netdev, const struct hw_addr_data *hw_addr) {
+ assert(netdev);
+ assert(hw_addr);
+
+ if (hw_addr->length <= 0)
+ return false;
+
+ if (!NETDEV_VTABLE(netdev)->can_set_mac)
+ return true;
+
+ return NETDEV_VTABLE(netdev)->can_set_mac(netdev, hw_addr);
+}
+
+static bool netdev_can_set_mtu(NetDev *netdev, uint32_t mtu) {
+ assert(netdev);
+
+ if (mtu <= 0)
+ return false;
+
+ if (!NETDEV_VTABLE(netdev)->can_set_mtu)
+ return true;
+
+ return NETDEV_VTABLE(netdev)->can_set_mtu(netdev, mtu);
+}
+
static int netdev_create_message(NetDev *netdev, Link *link, sd_netlink_message *m) {
int r;
@@ -617,14 +671,14 @@ static int netdev_create_message(NetDev *netdev, Link *link, sd_netlink_message
if (r < 0)
return r;
- if (hw_addr.length > 0) {
+ if (netdev_can_set_mac(netdev, &hw_addr)) {
log_netdev_debug(netdev, "Using MAC address: %s", HW_ADDR_TO_STR(&hw_addr));
r = netlink_message_append_hw_addr(m, IFLA_ADDRESS, &hw_addr);
if (r < 0)
return r;
}
- if (netdev->mtu != 0) {
+ if (netdev_can_set_mtu(netdev, netdev->mtu)) {
r = sd_netlink_message_append_u32(m, IFLA_MTU, netdev->mtu);
if (r < 0)
return r;
@@ -670,6 +724,7 @@ static int independent_netdev_create(NetDev *netdev) {
int r;
assert(netdev);
+ assert(netdev->manager);
/* create netdev */
if (NETDEV_VTABLE(netdev)->create) {
@@ -681,7 +736,7 @@ static int independent_netdev_create(NetDev *netdev) {
return 0;
}
- r = sd_rtnl_message_new_link(netdev->manager->rtnl, &m, RTM_NEWLINK, 0);
+ r = sd_rtnl_message_new_link(netdev->manager->rtnl, &m, RTM_NEWLINK, netdev->ifindex);
if (r < 0)
return r;
@@ -710,7 +765,7 @@ static int stacked_netdev_create(NetDev *netdev, Link *link, Request *req) {
assert(link);
assert(req);
- r = sd_rtnl_message_new_link(netdev->manager->rtnl, &m, RTM_NEWLINK, 0);
+ r = sd_rtnl_message_new_link(netdev->manager->rtnl, &m, RTM_NEWLINK, netdev->ifindex);
if (r < 0)
return r;
@@ -755,9 +810,6 @@ static bool link_is_ready_to_create_stacked_netdev(Link *link) {
static int netdev_is_ready_to_create(NetDev *netdev, Link *link) {
assert(netdev);
- if (netdev->state != NETDEV_STATE_LOADING)
- return false;
-
if (link && !link_is_ready_to_create_stacked_netdev(link))
return false;
@@ -774,6 +826,9 @@ static int stacked_netdev_process_request(Request *req, Link *link, void *userda
assert(req);
assert(link);
+ if (!netdev_is_managed(netdev))
+ return 1; /* Already detached, due to e.g. reloading .netdev files, cancelling the request. */
+
r = netdev_is_ready_to_create(netdev, link);
if (r <= 0)
return r;
@@ -816,8 +871,8 @@ int link_request_stacked_netdev(Link *link, NetDev *netdev) {
if (!netdev_is_stacked(netdev))
return -EINVAL;
- if (!IN_SET(netdev->state, NETDEV_STATE_LOADING, NETDEV_STATE_FAILED) || netdev->ifindex > 0)
- return 0; /* Already created. */
+ if (!netdev_is_managed(netdev))
+ return 0; /* Already detached, due to e.g. reloading .netdev files. */
link->stacked_netdevs_created = false;
r = link_queue_request_full(link, REQUEST_TYPE_NETDEV_STACKED,
@@ -843,6 +898,9 @@ static int independent_netdev_process_request(Request *req, Link *link, void *us
assert(!link);
+ if (!netdev_is_managed(netdev))
+ return 1; /* Already detached, due to e.g. reloading .netdev files, cancelling the request. */
+
r = netdev_is_ready_to_create(netdev, NULL);
if (r <= 0)
return r;
@@ -866,6 +924,12 @@ static int netdev_request_to_create(NetDev *netdev) {
if (netdev_is_stacked(netdev))
return 0;
+ if (!netdev_is_managed(netdev))
+ return 0; /* Already detached, due to e.g. reloading .netdev files. */
+
+ if (netdev->state != NETDEV_STATE_LOADING)
+ return 0; /* Already configured (at least tried previously). Not necessary to reconfigure. */
+
r = netdev_is_ready_to_create(netdev, NULL);
if (r < 0)
return r;
@@ -885,21 +949,20 @@ static int netdev_request_to_create(NetDev *netdev) {
return 0;
}
-int netdev_load_one(Manager *manager, const char *filename) {
+int netdev_load_one(Manager *manager, const char *filename, NetDev **ret) {
_cleanup_(netdev_unrefp) NetDev *netdev_raw = NULL, *netdev = NULL;
const char *dropin_dirname;
int r;
assert(manager);
assert(filename);
+ assert(ret);
r = null_or_empty_path(filename);
if (r < 0)
return log_warning_errno(r, "Failed to check if \"%s\" is empty: %m", filename);
- if (r > 0) {
- log_debug("Skipping empty file: %s", filename);
- return 0;
- }
+ if (r > 0)
+ return log_debug_errno(SYNTHETIC_ERRNO(ENOENT), "Skipping empty file: %s", filename);
netdev_raw = new(NetDev, 1);
if (!netdev_raw)
@@ -924,10 +987,8 @@ int netdev_load_one(Manager *manager, const char *filename) {
return r; /* config_parse_many() logs internally. */
/* skip out early if configuration does not match the environment */
- if (!condition_test_list(netdev_raw->conditions, environ, NULL, NULL, NULL)) {
- log_debug("%s: Conditions in the file do not match the system environment, skipping.", filename);
- return 0;
- }
+ if (!condition_test_list(netdev_raw->conditions, environ, NULL, NULL, NULL))
+ return log_debug_errno(SYNTHETIC_ERRNO(ESTALE), "%s: Conditions in the file do not match the system environment, skipping.", filename);
if (netdev_raw->kind == _NETDEV_KIND_INVALID)
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "NetDev has no Kind= configured in \"%s\", ignoring.", filename);
@@ -954,7 +1015,7 @@ int netdev_load_one(Manager *manager, const char *filename) {
config_item_perf_lookup, network_netdev_gperf_lookup,
CONFIG_PARSE_WARN,
netdev,
- NULL,
+ &netdev->stats_by_path,
&netdev->dropins);
if (r < 0)
return r; /* config_parse_many() logs internally. */
@@ -970,35 +1031,111 @@ int netdev_load_one(Manager *manager, const char *filename) {
if (!netdev->filename)
return log_oom();
- r = netdev_attach(netdev);
- if (r < 0)
- return r;
-
log_syntax(/* unit = */ NULL, LOG_DEBUG, filename, /* config_line = */ 0, /* error = */ 0, "Successfully loaded.");
- r = netdev_request_to_create(netdev);
+ *ret = TAKE_PTR(netdev);
+ return 0;
+}
+
+int netdev_load(Manager *manager) {
+ _cleanup_strv_free_ char **files = NULL;
+ int r;
+
+ assert(manager);
+
+ r = conf_files_list_strv(&files, ".netdev", NULL, 0, NETWORK_DIRS);
if (r < 0)
- return r; /* netdev_request_to_create() logs internally. */
+ return log_error_errno(r, "Failed to enumerate netdev files: %m");
+
+ STRV_FOREACH(f, files) {
+ _cleanup_(netdev_unrefp) NetDev *netdev = NULL;
+
+ if (netdev_load_one(manager, *f, &netdev) < 0)
+ continue;
+
+ if (netdev_attach(netdev) < 0)
+ continue;
+
+ if (netdev_request_to_create(netdev) < 0)
+ continue;
+
+ TAKE_PTR(netdev);
+ }
- TAKE_PTR(netdev);
return 0;
}
-int netdev_load(Manager *manager, bool reload) {
+int netdev_reload(Manager *manager) {
+ _cleanup_hashmap_free_ Hashmap *new_netdevs = NULL;
_cleanup_strv_free_ char **files = NULL;
int r;
assert(manager);
- if (!reload)
- hashmap_clear_with_destructor(manager->netdevs, netdev_unref);
-
r = conf_files_list_strv(&files, ".netdev", NULL, 0, NETWORK_DIRS);
if (r < 0)
return log_error_errno(r, "Failed to enumerate netdev files: %m");
- STRV_FOREACH(f, files)
- (void) netdev_load_one(manager, *f);
+ STRV_FOREACH(f, files) {
+ _cleanup_(netdev_unrefp) NetDev *netdev = NULL;
+ NetDev *old;
+
+ if (netdev_load_one(manager, *f, &netdev) < 0)
+ continue;
+
+ if (netdev_get(manager, netdev->ifname, &old) < 0) {
+ log_netdev_debug(netdev, "Found new .netdev file: %s", netdev->filename);
+
+ if (netdev_attach_name_full(netdev, netdev->ifname, &new_netdevs) >= 0)
+ TAKE_PTR(netdev);
+
+ continue;
+ }
+
+ if (!stats_by_path_equal(netdev->stats_by_path, old->stats_by_path)) {
+ log_netdev_debug(netdev, "Found updated .netdev file: %s", netdev->filename);
+
+ /* Copy ifindex. */
+ netdev->ifindex = old->ifindex;
+
+ if (netdev_attach_name_full(netdev, netdev->ifname, &new_netdevs) >= 0)
+ TAKE_PTR(netdev);
+
+ continue;
+ }
+
+ /* Keep the original object, and drop the new one. */
+ if (netdev_attach_name_full(old, old->ifname, &new_netdevs) >= 0)
+ netdev_ref(old);
+ }
+
+ /* Detach old NetDev objects from Manager.
+ * Note, the same object may be registered with multiple names, and netdev_detach() may drop multiple
+ * entries. Hence, hashmap_free_with_destructor() cannot be used. */
+ for (NetDev *n; (n = hashmap_first(manager->netdevs)); )
+ netdev_detach(n);
+
+ /* Attach new NetDev objects to Manager. */
+ for (;;) {
+ _cleanup_(netdev_unrefp) NetDev *netdev = hashmap_steal_first(new_netdevs);
+ if (!netdev)
+ break;
+
+ netdev->manager = manager;
+ if (netdev_attach(netdev) < 0)
+ continue;
+
+ /* Create a new netdev or update existing netdev, */
+ if (netdev_request_to_create(netdev) < 0)
+ continue;
+
+ TAKE_PTR(netdev);
+ }
+
+ /* Reassign NetDev objects to Link object. */
+ Link *link;
+ HASHMAP_FOREACH(link, manager->links_by_index)
+ link_assign_netdev(link);
return 0;
}
diff --git a/src/network/netdev/netdev.h b/src/network/netdev/netdev.h
index 10b9d0dd77..765496d044 100644
--- a/src/network/netdev/netdev.h
+++ b/src/network/netdev/netdev.h
@@ -8,6 +8,7 @@
#include "hash-funcs.h"
#include "list.h"
#include "log-link.h"
+#include "netdev-util.h"
#include "networkd-link.h"
#include "time-util.h"
@@ -118,6 +119,7 @@ typedef struct NetDev {
char *filename;
char **dropins;
+ Hashmap *stats_by_path;
LIST_HEAD(Condition, conditions);
@@ -179,6 +181,16 @@ typedef struct NetDevVTable {
/* get ifindex of the netdev. */
int (*get_ifindex)(NetDev *netdev, const char *name);
+ /* provides if MAC address can be set. If this is not set, assumed to be yes. */
+ bool (*can_set_mac)(NetDev *netdev, const struct hw_addr_data *hw_addr);
+
+ /* provides if MTU can be set. If this is not set, assumed to be yes. */
+ bool (*can_set_mtu)(NetDev *netdev, uint32_t mtu);
+
+ /* provides if the netdev needs to be reconfigured when a specified type of address on the underlying
+ * interface is updated. */
+ bool (*needs_reconfigure)(NetDev *netdev, NetDevLocalAddressType type);
+
/* expected iftype, e.g. ARPHRD_ETHER. */
uint16_t iftype;
@@ -211,14 +223,15 @@ NetDev* netdev_detach_name(NetDev *netdev, const char *name);
void netdev_detach(NetDev *netdev);
int netdev_set_ifindex_internal(NetDev *netdev, int ifindex);
-int netdev_load(Manager *manager, bool reload);
-int netdev_load_one(Manager *manager, const char *filename);
+int netdev_load(Manager *manager);
+int netdev_reload(Manager *manager);
+int netdev_load_one(Manager *manager, const char *filename, NetDev **ret);
void netdev_drop(NetDev *netdev);
void netdev_enter_failed(NetDev *netdev);
int netdev_enter_ready(NetDev *netdev);
-NetDev *netdev_unref(NetDev *netdev);
-NetDev *netdev_ref(NetDev *netdev);
+NetDev* netdev_unref(NetDev *netdev);
+NetDev* netdev_ref(NetDev *netdev);
DEFINE_TRIVIAL_DESTRUCTOR(netdev_destroy_callback, NetDev, netdev_unref);
DEFINE_TRIVIAL_CLEANUP_FUNC(NetDev*, netdev_unref);
@@ -229,6 +242,7 @@ int netdev_set_ifindex(NetDev *netdev, sd_netlink_message *newlink);
int netdev_generate_hw_addr(NetDev *netdev, Link *link, const char *name,
const struct hw_addr_data *hw_addr, struct hw_addr_data *ret);
+bool netdev_needs_reconfigure(NetDev *netdev, NetDevLocalAddressType type);
int link_request_stacked_netdev(Link *link, NetDev *netdev);
const char* netdev_kind_to_string(NetDevKind d) _const_;
diff --git a/src/network/netdev/tunnel.c b/src/network/netdev/tunnel.c
index 2bf58086b2..0339c49c8f 100644
--- a/src/network/netdev/tunnel.c
+++ b/src/network/netdev/tunnel.c
@@ -34,7 +34,7 @@ DEFINE_CONFIG_PARSE_ENUM(config_parse_ip6tnl_mode, ip6tnl_mode, Ip6TnlMode);
#define HASH_KEY SD_ID128_MAKE(74,c4,de,12,f3,d9,41,34,bb,3d,c1,a4,42,93,50,87)
-int dhcp4_pd_create_6rd_tunnel_name(Link *link, char **ret) {
+static int dhcp4_pd_create_6rd_tunnel_name(Link *link) {
_cleanup_free_ char *ifname_alloc = NULL;
uint8_t ipv4masklen, sixrd_prefixlen, *buf, *p;
struct in_addr ipv4address;
@@ -47,13 +47,16 @@ int dhcp4_pd_create_6rd_tunnel_name(Link *link, char **ret) {
assert(link);
assert(link->dhcp_lease);
+ if (link->dhcp4_6rd_tunnel_name)
+ return 0; /* Already set. Do not change even if the 6rd option is changed. */
+
r = sd_dhcp_lease_get_address(link->dhcp_lease, &ipv4address);
if (r < 0)
- return log_link_debug_errno(link, r, "Failed to get DHCPv4 address: %m");
+ return r;
r = sd_dhcp_lease_get_6rd(link->dhcp_lease, &ipv4masklen, &sixrd_prefixlen, &sixrd_prefix, NULL, NULL);
if (r < 0)
- return log_link_debug_errno(link, r, "Failed to get 6rd option: %m");
+ return r;
sz = sizeof(uint8_t) * 2 + sizeof(struct in6_addr) + sizeof(struct in_addr);
buf = newa(uint8_t, sz);
@@ -80,21 +83,44 @@ int dhcp4_pd_create_6rd_tunnel_name(Link *link, char **ret) {
ifname_alloc = strdup(ifname);
if (!ifname_alloc)
- return log_oom_debug();
+ return -ENOMEM;
- *ret = TAKE_PTR(ifname_alloc);
+ link->dhcp4_6rd_tunnel_name = TAKE_PTR(ifname_alloc);
return 0;
}
-static int dhcp4_pd_create_6rd_tunnel_message(
- Link *link,
- sd_netlink_message *m,
- const struct in_addr *ipv4address,
- uint8_t ipv4masklen,
- const struct in6_addr *sixrd_prefix,
- uint8_t sixrd_prefixlen) {
+int dhcp4_pd_create_6rd_tunnel(Link *link, link_netlink_message_handler_t callback) {
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
+ uint8_t ipv4masklen, sixrd_prefixlen;
+ struct in_addr ipv4address;
+ struct in6_addr sixrd_prefix;
+ Link *sit = NULL;
int r;
+ assert(link);
+ assert(link->manager);
+ assert(link->manager->rtnl);
+ assert(link->dhcp_lease);
+ assert(callback);
+
+ r = sd_dhcp_lease_get_address(link->dhcp_lease, &ipv4address);
+ if (r < 0)
+ return r;
+
+ r = sd_dhcp_lease_get_6rd(link->dhcp_lease, &ipv4masklen, &sixrd_prefixlen, &sixrd_prefix, NULL, NULL);
+ if (r < 0)
+ return r;
+
+ r = dhcp4_pd_create_6rd_tunnel_name(link);
+ if (r < 0)
+ return r;
+
+ (void) link_get_by_name(link->manager, link->dhcp4_6rd_tunnel_name, &sit);
+
+ r = sd_rtnl_message_new_link(link->manager->rtnl, &m, RTM_NEWLINK, sit ? sit->ifindex : 0);
+ if (r < 0)
+ return r;
+
r = sd_netlink_message_append_string(m, IFLA_IFNAME, link->dhcp4_6rd_tunnel_name);
if (r < 0)
return r;
@@ -107,7 +133,7 @@ static int dhcp4_pd_create_6rd_tunnel_message(
if (r < 0)
return r;
- r = sd_netlink_message_append_in_addr(m, IFLA_IPTUN_LOCAL, ipv4address);
+ r = sd_netlink_message_append_in_addr(m, IFLA_IPTUN_LOCAL, &ipv4address);
if (r < 0)
return r;
@@ -115,7 +141,7 @@ static int dhcp4_pd_create_6rd_tunnel_message(
if (r < 0)
return r;
- r = sd_netlink_message_append_in6_addr(m, IFLA_IPTUN_6RD_PREFIX, sixrd_prefix);
+ r = sd_netlink_message_append_in6_addr(m, IFLA_IPTUN_6RD_PREFIX, &sixrd_prefix);
if (r < 0)
return r;
@@ -123,7 +149,7 @@ static int dhcp4_pd_create_6rd_tunnel_message(
if (r < 0)
return r;
- struct in_addr relay_prefix = *ipv4address;
+ struct in_addr relay_prefix = ipv4address;
(void) in4_addr_mask(&relay_prefix, ipv4masklen);
r = sd_netlink_message_append_u32(m, IFLA_IPTUN_6RD_RELAY_PREFIX, relay_prefix.s_addr);
if (r < 0)
@@ -141,48 +167,12 @@ static int dhcp4_pd_create_6rd_tunnel_message(
if (r < 0)
return r;
- return 0;
-}
-
-int dhcp4_pd_create_6rd_tunnel(Link *link, link_netlink_message_handler_t callback) {
- _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
- uint8_t ipv4masklen, sixrd_prefixlen;
- struct in_addr ipv4address;
- struct in6_addr sixrd_prefix;
- int r;
-
- assert(link);
- assert(link->ifindex > 0);
- assert(link->manager);
- assert(link->dhcp_lease);
- assert(link->dhcp4_6rd_tunnel_name);
- assert(callback);
-
- r = sd_dhcp_lease_get_address(link->dhcp_lease, &ipv4address);
- if (r < 0)
- return log_link_debug_errno(link, r, "Failed to get DHCPv4 address: %m");
-
- r = sd_dhcp_lease_get_6rd(link->dhcp_lease, &ipv4masklen, &sixrd_prefixlen, &sixrd_prefix, NULL, NULL);
- if (r < 0)
- return log_link_debug_errno(link, r, "Failed to get 6rd option: %m");
-
- r = sd_rtnl_message_new_link(link->manager->rtnl, &m, RTM_NEWLINK, 0);
- if (r < 0)
- return log_link_debug_errno(link, r, "Failed to create netlink message: %m");
-
- r = dhcp4_pd_create_6rd_tunnel_message(link, m,
- &ipv4address, ipv4masklen,
- &sixrd_prefix, sixrd_prefixlen);
- if (r < 0)
- return log_link_debug_errno(link, r, "Failed to fill netlink message: %m");
-
r = netlink_call_async(link->manager->rtnl, NULL, m, callback,
link_netlink_destroy_callback, link);
if (r < 0)
- return log_link_debug_errno(link, r, "Could not send netlink message: %m");
+ return r;
link_ref(link);
-
return 0;
}
@@ -681,34 +671,27 @@ static int netdev_tunnel_verify(NetDev *netdev, const char *filename) {
}
}
- if (IN_SET(netdev->kind, NETDEV_KIND_VTI, NETDEV_KIND_IPIP, NETDEV_KIND_SIT, NETDEV_KIND_GRE) &&
- !IN_SET(t->family, AF_UNSPEC, AF_INET))
- return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
- "vti/ipip/sit/gre tunnel without a local/remote IPv4 address configured in %s. Ignoring", filename);
+ if (IN_SET(netdev->kind, NETDEV_KIND_VTI, NETDEV_KIND_IPIP, NETDEV_KIND_SIT, NETDEV_KIND_GRE, NETDEV_KIND_GRETAP, NETDEV_KIND_ERSPAN)) {
+ if (!IN_SET(t->family, AF_UNSPEC, AF_INET))
+ return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
+ "%s tunnel without a local/remote IPv4 address configured in %s, ignoring.",
+ netdev_kind_to_string(netdev->kind), filename);
- if (IN_SET(netdev->kind, NETDEV_KIND_GRETAP, NETDEV_KIND_ERSPAN) &&
- (t->family != AF_INET || !in_addr_is_set(t->family, &t->remote)))
- return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
- "gretap/erspan tunnel without a remote IPv4 address configured in %s. Ignoring", filename);
-
- if ((IN_SET(netdev->kind, NETDEV_KIND_VTI6, NETDEV_KIND_IP6TNL) && t->family != AF_INET6) ||
- (netdev->kind == NETDEV_KIND_IP6GRE && !IN_SET(t->family, AF_UNSPEC, AF_INET6)))
- return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
- "vti6/ip6tnl/ip6gre tunnel without a local/remote IPv6 address configured in %s. Ignoring", filename);
+ t->family = AF_INET; /* For netlink_message_append_in_addr_union(). */
+ }
- if (netdev->kind == NETDEV_KIND_IP6GRETAP &&
- (t->family != AF_INET6 || !in_addr_is_set(t->family, &t->remote)))
- return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
- "ip6gretap tunnel without a remote IPv6 address configured in %s. Ignoring", filename);
+ if (IN_SET(netdev->kind, NETDEV_KIND_VTI6, NETDEV_KIND_IP6TNL, NETDEV_KIND_IP6GRE, NETDEV_KIND_IP6GRETAP)) {
+ if (!IN_SET(t->family, AF_UNSPEC, AF_INET6))
+ return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
+ "%s tunnel without a local/remote IPv6 address configured in %s, ignoring,",
+ netdev_kind_to_string(netdev->kind), filename);
+ t->family = AF_INET6; /* For netlink_message_append_in_addr_union(). */
+ }
if (t->fou_tunnel && t->fou_destination_port <= 0)
return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
"FooOverUDP missing port configured in %s. Ignoring", filename);
- /* netlink_message_append_in_addr_union() is used for vti/vti6. So, t->family cannot be AF_UNSPEC. */
- if (netdev->kind == NETDEV_KIND_VTI)
- t->family = AF_INET;
-
if (t->assign_to_loopback)
t->independent = true;
@@ -725,6 +708,14 @@ static int netdev_tunnel_verify(NetDev *netdev, const char *filename) {
return 0;
}
+static bool tunnel_needs_reconfigure(NetDev *netdev, NetDevLocalAddressType type) {
+ assert(type >= 0 && type < _NETDEV_LOCAL_ADDRESS_TYPE_MAX);
+
+ Tunnel *t = ASSERT_PTR(TUNNEL(netdev));
+
+ return t->local_type == type;
+}
+
static int unset_local(Tunnel *t) {
assert(t);
@@ -1136,6 +1127,7 @@ const NetDevVTable ipip_vtable = {
.create_type = NETDEV_CREATE_STACKED,
.is_ready_to_create = netdev_tunnel_is_ready_to_create,
.config_verify = netdev_tunnel_verify,
+ .needs_reconfigure = tunnel_needs_reconfigure,
.iftype = ARPHRD_TUNNEL,
};
@@ -1147,6 +1139,7 @@ const NetDevVTable sit_vtable = {
.create_type = NETDEV_CREATE_STACKED,
.is_ready_to_create = netdev_tunnel_is_ready_to_create,
.config_verify = netdev_tunnel_verify,
+ .needs_reconfigure = tunnel_needs_reconfigure,
.iftype = ARPHRD_SIT,
};
@@ -1158,6 +1151,7 @@ const NetDevVTable vti_vtable = {
.create_type = NETDEV_CREATE_STACKED,
.is_ready_to_create = netdev_tunnel_is_ready_to_create,
.config_verify = netdev_tunnel_verify,
+ .needs_reconfigure = tunnel_needs_reconfigure,
.iftype = ARPHRD_TUNNEL,
};
@@ -1169,6 +1163,7 @@ const NetDevVTable vti6_vtable = {
.create_type = NETDEV_CREATE_STACKED,
.is_ready_to_create = netdev_tunnel_is_ready_to_create,
.config_verify = netdev_tunnel_verify,
+ .needs_reconfigure = tunnel_needs_reconfigure,
.iftype = ARPHRD_TUNNEL6,
};
@@ -1180,6 +1175,7 @@ const NetDevVTable gre_vtable = {
.create_type = NETDEV_CREATE_STACKED,
.is_ready_to_create = netdev_tunnel_is_ready_to_create,
.config_verify = netdev_tunnel_verify,
+ .needs_reconfigure = tunnel_needs_reconfigure,
.iftype = ARPHRD_IPGRE,
};
@@ -1191,6 +1187,7 @@ const NetDevVTable gretap_vtable = {
.create_type = NETDEV_CREATE_STACKED,
.is_ready_to_create = netdev_tunnel_is_ready_to_create,
.config_verify = netdev_tunnel_verify,
+ .needs_reconfigure = tunnel_needs_reconfigure,
.iftype = ARPHRD_ETHER,
.generate_mac = true,
};
@@ -1203,6 +1200,7 @@ const NetDevVTable ip6gre_vtable = {
.create_type = NETDEV_CREATE_STACKED,
.is_ready_to_create = netdev_tunnel_is_ready_to_create,
.config_verify = netdev_tunnel_verify,
+ .needs_reconfigure = tunnel_needs_reconfigure,
.iftype = ARPHRD_IP6GRE,
};
@@ -1214,6 +1212,7 @@ const NetDevVTable ip6gretap_vtable = {
.create_type = NETDEV_CREATE_STACKED,
.is_ready_to_create = netdev_tunnel_is_ready_to_create,
.config_verify = netdev_tunnel_verify,
+ .needs_reconfigure = tunnel_needs_reconfigure,
.iftype = ARPHRD_ETHER,
.generate_mac = true,
};
@@ -1226,6 +1225,7 @@ const NetDevVTable ip6tnl_vtable = {
.create_type = NETDEV_CREATE_STACKED,
.is_ready_to_create = netdev_tunnel_is_ready_to_create,
.config_verify = netdev_tunnel_verify,
+ .needs_reconfigure = tunnel_needs_reconfigure,
.iftype = ARPHRD_TUNNEL6,
};
@@ -1237,6 +1237,7 @@ const NetDevVTable erspan_vtable = {
.create_type = NETDEV_CREATE_STACKED,
.is_ready_to_create = netdev_tunnel_is_ready_to_create,
.config_verify = netdev_tunnel_verify,
+ .needs_reconfigure = tunnel_needs_reconfigure,
.iftype = ARPHRD_ETHER,
.generate_mac = true,
};
diff --git a/src/network/netdev/tunnel.h b/src/network/netdev/tunnel.h
index cf26cfad98..6cd03b032d 100644
--- a/src/network/netdev/tunnel.h
+++ b/src/network/netdev/tunnel.h
@@ -69,7 +69,6 @@ typedef struct Tunnel {
uint8_t sixrd_prefixlen;
} Tunnel;
-int dhcp4_pd_create_6rd_tunnel_name(Link *link, char **ret);
int dhcp4_pd_create_6rd_tunnel(Link *link, link_netlink_message_handler_t callback);
DEFINE_NETDEV_CAST(IPIP, Tunnel);
diff --git a/src/network/netdev/tuntap.c b/src/network/netdev/tuntap.c
index f5be31ed94..c012bc1591 100644
--- a/src/network/netdev/tuntap.c
+++ b/src/network/netdev/tuntap.c
@@ -103,6 +103,7 @@ static int netdev_create_tuntap(NetDev *netdev) {
int r;
assert(netdev);
+ assert(netdev->manager);
t = TUNTAP(netdev);
assert(t);
diff --git a/src/network/netdev/vxlan.c b/src/network/netdev/vxlan.c
index e928b20d83..9f22794d34 100644
--- a/src/network/netdev/vxlan.c
+++ b/src/network/netdev/vxlan.c
@@ -44,12 +44,6 @@ static int netdev_vxlan_fill_message_create(NetDev *netdev, Link *link, sd_netli
int local_family, r;
VxLan *v = VXLAN(netdev);
- if (v->vni <= VXLAN_VID_MAX) {
- r = sd_netlink_message_append_u32(m, IFLA_VXLAN_ID, v->vni);
- if (r < 0)
- return r;
- }
-
if (in_addr_is_set(v->group_family, &v->group)) {
if (v->group_family == AF_INET)
r = sd_netlink_message_append_in_addr(m, IFLA_VXLAN_GROUP, &v->group.in);
@@ -83,12 +77,12 @@ static int netdev_vxlan_fill_message_create(NetDev *netdev, Link *link, sd_netli
if (r < 0)
return r;
- if (v->inherit) {
- r = sd_netlink_message_append_flag(m, IFLA_VXLAN_TTL_INHERIT);
- if (r < 0)
- return r;
- } else {
- r = sd_netlink_message_append_u8(m, IFLA_VXLAN_TTL, v->ttl);
+ r = sd_netlink_message_append_u8(m, IFLA_VXLAN_TTL, v->ttl);
+ if (r < 0)
+ return r;
+
+ if (v->fdb_ageing != 0) {
+ r = sd_netlink_message_append_u32(m, IFLA_VXLAN_AGEING, v->fdb_ageing / USEC_PER_SEC);
if (r < 0)
return r;
}
@@ -99,6 +93,34 @@ static int netdev_vxlan_fill_message_create(NetDev *netdev, Link *link, sd_netli
return r;
}
+ r = sd_netlink_message_append_u32(m, IFLA_VXLAN_LABEL, htobe32(v->flow_label));
+ if (r < 0)
+ return r;
+
+ if (v->df != _NETDEV_VXLAN_DF_INVALID) {
+ r = sd_netlink_message_append_u8(m, IFLA_VXLAN_DF, v->df);
+ if (r < 0)
+ return r;
+ }
+
+ if (netdev->ifindex > 0)
+ return 0;
+
+ /* The properties below cannot be updated, and the kernel refuses the whole request if one of the
+ * following attributes is set for an existing interface. */
+
+ if (v->vni <= VXLAN_VID_MAX) {
+ r = sd_netlink_message_append_u32(m, IFLA_VXLAN_ID, v->vni);
+ if (r < 0)
+ return r;
+ }
+
+ if (v->inherit) {
+ r = sd_netlink_message_append_flag(m, IFLA_VXLAN_TTL_INHERIT);
+ if (r < 0)
+ return r;
+ }
+
r = sd_netlink_message_append_u8(m, IFLA_VXLAN_LEARNING, v->learning);
if (r < 0)
return r;
@@ -119,12 +141,6 @@ static int netdev_vxlan_fill_message_create(NetDev *netdev, Link *link, sd_netli
if (r < 0)
return r;
- if (v->fdb_ageing != 0) {
- r = sd_netlink_message_append_u32(m, IFLA_VXLAN_AGEING, v->fdb_ageing / USEC_PER_SEC);
- if (r < 0)
- return r;
- }
-
if (v->max_fdb != 0) {
r = sd_netlink_message_append_u32(m, IFLA_VXLAN_LIMIT, v->max_fdb);
if (r < 0)
@@ -166,10 +182,6 @@ static int netdev_vxlan_fill_message_create(NetDev *netdev, Link *link, sd_netli
return r;
}
- r = sd_netlink_message_append_u32(m, IFLA_VXLAN_LABEL, htobe32(v->flow_label));
- if (r < 0)
- return r;
-
if (v->group_policy) {
r = sd_netlink_message_append_flag(m, IFLA_VXLAN_GBP);
if (r < 0)
@@ -182,15 +194,16 @@ static int netdev_vxlan_fill_message_create(NetDev *netdev, Link *link, sd_netli
return r;
}
- if (v->df != _NETDEV_VXLAN_DF_INVALID) {
- r = sd_netlink_message_append_u8(m, IFLA_VXLAN_DF, v->df);
- if (r < 0)
- return r;
- }
-
return 0;
}
+static bool vxlan_can_set_mtu(NetDev *netdev, uint32_t mtu) {
+ assert(netdev);
+
+ /* MTU cannot be updated. Even unchanged, IFLA_MTU attribute cannot be set in the message. */
+ return netdev->ifindex <= 0;
+}
+
int config_parse_vxlan_address(
const char *unit,
const char *filename,
@@ -402,6 +415,14 @@ static int netdev_vxlan_verify(NetDev *netdev, const char *filename) {
return 0;
}
+static bool vxlan_needs_reconfigure(NetDev *netdev, NetDevLocalAddressType type) {
+ assert(type >= 0 && type < _NETDEV_LOCAL_ADDRESS_TYPE_MAX);
+
+ VxLan *v = VXLAN(netdev);
+
+ return v->local_type == type;
+}
+
static int netdev_vxlan_is_ready_to_create(NetDev *netdev, Link *link) {
VxLan *v = VXLAN(netdev);
@@ -431,6 +452,8 @@ const NetDevVTable vxlan_vtable = {
.create_type = NETDEV_CREATE_STACKED,
.is_ready_to_create = netdev_vxlan_is_ready_to_create,
.config_verify = netdev_vxlan_verify,
+ .can_set_mtu = vxlan_can_set_mtu,
+ .needs_reconfigure = vxlan_needs_reconfigure,
.iftype = ARPHRD_ETHER,
.generate_mac = true,
};
diff --git a/src/network/netdev/wireguard.c b/src/network/netdev/wireguard.c
index 9715cf4034..9e8dfb259a 100644
--- a/src/network/netdev/wireguard.c
+++ b/src/network/netdev/wireguard.c
@@ -234,6 +234,9 @@ static int wireguard_set_interface(NetDev *netdev) {
Wireguard *w = WIREGUARD(netdev);
int r;
+ if (!netdev_is_managed(netdev))
+ return 0; /* Already detached, due to e.g. reloading .netdev files. */
+
for (WireguardPeer *peer_start = w->peers; peer_start || !sent_once; ) {
uint16_t i = 0;
@@ -399,6 +402,9 @@ static int peer_resolve_endpoint(WireguardPeer *peer) {
netdev = NETDEV(peer->wireguard);
+ if (!netdev_is_managed(netdev))
+ return 0; /* Already detached, due to e.g. reloading .netdev files. */
+
if (!peer->endpoint_host || !peer->endpoint_port)
/* Not necessary to resolve the endpoint. */
return 0;
diff --git a/src/network/netdev/wlan.c b/src/network/netdev/wlan.c
index 904e40fe81..5b9db8b219 100644
--- a/src/network/netdev/wlan.c
+++ b/src/network/netdev/wlan.c
@@ -27,6 +27,9 @@ static void wlan_init(NetDev *netdev) {
static int wlan_get_wiphy(NetDev *netdev, Wiphy **ret) {
WLan *w = WLAN(netdev);
+ if (!netdev_is_managed(netdev))
+ return -ENOENT; /* Already detached, due to e.g. reloading .netdev files. */
+
if (w->wiphy_name)
return wiphy_get_by_name(netdev->manager, w->wiphy_name, ret);
diff --git a/src/network/networkctl-description.c b/src/network/networkctl-description.c
index 14ce55bfae..410751f9e7 100644
--- a/src/network/networkctl-description.c
+++ b/src/network/networkctl-description.c
@@ -122,7 +122,7 @@ int dump_description(int argc, char *argv[]) {
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
int r;
- if (arg_json_format_flags == SD_JSON_FORMAT_OFF)
+ if (!sd_json_format_enabled(arg_json_format_flags))
return 0;
r = acquire_bus(&bus);
diff --git a/src/network/networkctl-lldp.c b/src/network/networkctl-lldp.c
index 7836cb5ccf..8a10562b0d 100644
--- a/src/network/networkctl-lldp.c
+++ b/src/network/networkctl-lldp.c
@@ -231,7 +231,7 @@ int link_lldp_status(int argc, char *argv[], void *userdata) {
if (r < 0)
return r;
- if (arg_json_format_flags != SD_JSON_FORMAT_OFF)
+ if (sd_json_format_enabled(arg_json_format_flags))
return dump_lldp_neighbors_json(reply, strv_skip(argv, 1));
pager_open(arg_pager_flags);
diff --git a/src/network/networkd-can.c b/src/network/networkd-can.c
index a5b003ad22..9457104a0e 100644
--- a/src/network/networkd-can.c
+++ b/src/network/networkd-can.c
@@ -20,10 +20,6 @@ int can_set_netlink_message(Link *link, sd_netlink_message *m) {
assert(link->network);
assert(m);
- r = sd_netlink_message_set_flags(m, NLM_F_REQUEST | NLM_F_ACK);
- if (r < 0)
- return r;
-
r = sd_netlink_message_open_container(m, IFLA_LINKINFO);
if (r < 0)
return r;
diff --git a/src/network/networkd-dhcp-prefix-delegation.c b/src/network/networkd-dhcp-prefix-delegation.c
index 56555df62c..1f80513007 100644
--- a/src/network/networkd-dhcp-prefix-delegation.c
+++ b/src/network/networkd-dhcp-prefix-delegation.c
@@ -961,7 +961,6 @@ static int dhcp4_pd_6rd_tunnel_create_handler(sd_netlink *rtnl, sd_netlink_messa
}
int dhcp4_pd_prefix_acquired(Link *uplink) {
- _cleanup_free_ char *tunnel_name = NULL;
uint8_t ipv4masklen, sixrd_prefixlen, pd_prefixlen;
struct in6_addr sixrd_prefix, pd_prefix;
struct in_addr ipv4address;
@@ -1010,28 +1009,10 @@ int dhcp4_pd_prefix_acquired(Link *uplink) {
if (r < 0)
return r;
- /* Generate 6rd SIT tunnel device name. */
- r = dhcp4_pd_create_6rd_tunnel_name(uplink, &tunnel_name);
+ /* Create or update 6rd SIT tunnel device. */
+ r = dhcp4_pd_create_6rd_tunnel(uplink, dhcp4_pd_6rd_tunnel_create_handler);
if (r < 0)
- return r;
-
- /* Remove old tunnel device if exists. */
- if (!streq_ptr(uplink->dhcp4_6rd_tunnel_name, tunnel_name)) {
- Link *old_tunnel;
-
- if (uplink->dhcp4_6rd_tunnel_name &&
- link_get_by_name(uplink->manager, uplink->dhcp4_6rd_tunnel_name, &old_tunnel) >= 0)
- (void) link_remove(old_tunnel);
-
- free_and_replace(uplink->dhcp4_6rd_tunnel_name, tunnel_name);
- }
-
- /* Create 6rd SIT tunnel device if it does not exist yet. */
- if (link_get_by_name(uplink->manager, uplink->dhcp4_6rd_tunnel_name, NULL) < 0) {
- r = dhcp4_pd_create_6rd_tunnel(uplink, dhcp4_pd_6rd_tunnel_create_handler);
- if (r < 0)
- return r;
- }
+ return log_link_warning_errno(uplink, r, "Failed to create or update 6rd SIT tunnel: %m");
/* Then, assign subnet prefixes to downstream interfaces. */
HASHMAP_FOREACH(link, uplink->manager->links_by_index) {
diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c
index 2dd29bca94..bda3a561d9 100644
--- a/src/network/networkd-dhcp4.c
+++ b/src/network/networkd-dhcp4.c
@@ -326,6 +326,10 @@ int dhcp4_check_ready(Link *link) {
if (r < 0)
return r;
+ r = link_request_stacked_netdevs(link, NETDEV_LOCAL_ADDRESS_DHCP4);
+ if (r < 0)
+ return r;
+
r = sd_ipv4ll_stop(link->ipv4ll);
if (r < 0)
return log_link_warning_errno(link, r, "Failed to drop IPv4 link-local address: %m");
diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c
index b49f51f684..1eb5138d5e 100644
--- a/src/network/networkd-dhcp6.c
+++ b/src/network/networkd-dhcp6.c
@@ -122,6 +122,10 @@ int dhcp6_check_ready(Link *link) {
if (r < 0)
return r;
+ r = link_request_stacked_netdevs(link, NETDEV_LOCAL_ADDRESS_DHCP6);
+ if (r < 0)
+ return r;
+
link_check_ready(link);
return 0;
}
diff --git a/src/network/networkd-ipv4ll.c b/src/network/networkd-ipv4ll.c
index 299aaedd07..ea960593bb 100644
--- a/src/network/networkd-ipv4ll.c
+++ b/src/network/networkd-ipv4ll.c
@@ -109,6 +109,10 @@ static int ipv4ll_address_claimed(sd_ipv4ll *ll, Link *link) {
log_link_debug(link, "IPv4 link-local claim "IPV4_ADDRESS_FMT_STR,
IPV4_ADDRESS_FMT_VAL(address->in_addr.in));
+ r = link_request_stacked_netdevs(link, NETDEV_LOCAL_ADDRESS_IPV4LL);
+ if (r < 0)
+ return r;
+
return link_request_address(link, address, NULL, ipv4ll_address_handler, NULL);
}
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index 59240bbc36..be8826b49c 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -222,7 +222,7 @@ void link_dns_settings_clear(Link *link) {
link->dnssec_negative_trust_anchors = set_free_free(link->dnssec_negative_trust_anchors);
}
-static void link_free_engines(Link *link) {
+void link_free_engines(Link *link) {
if (!link)
return;
@@ -379,7 +379,7 @@ int link_stop_engines(Link *link, bool may_keep_dhcp) {
bool keep_dhcp = may_keep_dhcp &&
link->network &&
!link->network->dhcp_send_decline && /* IPv4 ACD for the DHCPv4 address is running. */
- (link->manager->restarting ||
+ (link->manager->state == MANAGER_RESTARTING ||
FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP_ON_STOP));
if (!keep_dhcp) {
@@ -640,15 +640,23 @@ static int link_request_static_configs(Link *link) {
return 0;
}
-static int link_request_stacked_netdevs(Link *link) {
+int link_request_stacked_netdevs(Link *link, NetDevLocalAddressType type) {
NetDev *netdev;
int r;
assert(link);
+ if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
+ return 0;
+
+ assert(link->network);
+
link->stacked_netdevs_created = false;
HASHMAP_FOREACH(netdev, link->network->stacked_netdevs) {
+ if (!netdev_needs_reconfigure(netdev, type))
+ continue;
+
r = link_request_stacked_netdev(link, netdev);
if (r < 0)
return r;
@@ -776,6 +784,10 @@ int link_ipv6ll_gained(Link *link) {
if (r < 0)
return r;
+ r = link_request_stacked_netdevs(link, NETDEV_LOCAL_ADDRESS_IPV6LL);
+ if (r < 0)
+ return r;
+
link_check_ready(link);
return 0;
}
@@ -1188,7 +1200,7 @@ static int link_configure(Link *link) {
if (r < 0)
return r;
- r = link_request_stacked_netdevs(link);
+ r = link_request_stacked_netdevs(link, _NETDEV_LOCAL_ADDRESS_TYPE_INVALID);
if (r < 0)
return r;
@@ -1313,9 +1325,13 @@ int link_reconfigure_impl(Link *link, bool force) {
int r;
assert(link);
+ assert(link->manager);
link_assign_netdev(link);
+ if (link->manager->state != MANAGER_RUNNING)
+ return 0;
+
if (IN_SET(link->state, LINK_STATE_PENDING, LINK_STATE_LINGER))
return 0;
diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h
index 89278f3a43..86aba9556b 100644
--- a/src/network/networkd-link.h
+++ b/src/network/networkd-link.h
@@ -19,6 +19,7 @@
#include "ether-addr-util.h"
#include "log-link.h"
+#include "netdev.h"
#include "netif-util.h"
#include "network-util.h"
#include "networkd-bridge-vlan.h"
@@ -222,8 +223,8 @@ bool link_is_ready_to_configure(Link *link, bool allow_unmanaged);
void link_ntp_settings_clear(Link *link);
void link_dns_settings_clear(Link *link);
-Link *link_unref(Link *link);
-Link *link_ref(Link *link);
+Link* link_unref(Link *link);
+Link* link_ref(Link *link);
DEFINE_TRIVIAL_CLEANUP_FUNC(Link*, link_unref);
DEFINE_TRIVIAL_DESTRUCTOR(link_netlink_destroy_callback, Link, link_unref);
@@ -252,10 +253,13 @@ int link_ipv6ll_gained(Link *link);
bool link_has_ipv6_connectivity(Link *link);
int link_stop_engines(Link *link, bool may_keep_dhcp);
+void link_free_engines(Link *link);
const char* link_state_to_string(LinkState s) _const_;
LinkState link_state_from_string(const char *s) _pure_;
+int link_request_stacked_netdevs(Link *link, NetDevLocalAddressType type);
+
int link_reconfigure_impl(Link *link, bool force);
int link_reconfigure(Link *link, bool force);
int link_reconfigure_on_bus_method_reload(Link *link, sd_bus_message *message);
diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c
index 2813fa1f28..6639d59b32 100644
--- a/src/network/networkd-manager.c
+++ b/src/network/networkd-manager.c
@@ -422,30 +422,73 @@ static int manager_connect_rtnl(Manager *m, int fd) {
static int manager_post_handler(sd_event_source *s, void *userdata) {
Manager *manager = ASSERT_PTR(userdata);
+ /* To release dynamic leases, we need to process queued remove requests before stopping networkd.
+ * This is especially important when KeepConfiguration=no. See issue #34837. */
(void) manager_process_remove_requests(manager);
- (void) manager_process_requests(manager);
- (void) manager_clean_all(manager);
+
+ switch (manager->state) {
+ case MANAGER_RUNNING:
+ (void) manager_process_requests(manager);
+ (void) manager_clean_all(manager);
+ return 0;
+
+ case MANAGER_TERMINATING:
+ case MANAGER_RESTARTING:
+ if (!ordered_set_isempty(manager->remove_request_queue))
+ return 0; /* There are some unissued remove requests. */
+
+ if (netlink_get_reply_callback_count(manager->rtnl) > 0 ||
+ netlink_get_reply_callback_count(manager->genl) > 0 ||
+ fw_ctx_get_reply_callback_count(manager->fw_ctx) > 0)
+ return 0; /* There are some message calls waiting for their replies. */
+
+ manager->state = MANAGER_STOPPED;
+ return sd_event_exit(sd_event_source_get_event(s), 0);
+
+ default:
+ assert_not_reached();
+ }
+
return 0;
}
-static int signal_terminate_callback(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
- Manager *m = ASSERT_PTR(userdata);
+static int manager_stop(Manager *manager, ManagerState state) {
+ assert(manager);
+ assert(IN_SET(state, MANAGER_TERMINATING, MANAGER_RESTARTING));
- m->restarting = false;
+ if (manager->state != MANAGER_RUNNING) {
+ log_debug("Already terminating or restarting systemd-networkd, refusing further operation request.");
+ return 0;
+ }
- log_debug("Terminate operation initiated.");
+ switch (state) {
+ case MANAGER_TERMINATING:
+ log_debug("Terminate operation initiated.");
+ break;
+ case MANAGER_RESTARTING:
+ log_debug("Restart operation initiated.");
+ break;
+ default:
+ assert_not_reached();
+ }
- return sd_event_exit(sd_event_source_get_event(s), 0);
-}
+ manager->state = state;
-static int signal_restart_callback(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
- Manager *m = ASSERT_PTR(userdata);
+ Link *link;
+ HASHMAP_FOREACH(link, manager->links_by_index) {
+ (void) link_stop_engines(link, /* may_keep_dhcp = */ true);
+ link_free_engines(link);
+ }
- m->restarting = true;
+ return 0;
+}
- log_debug("Restart operation initiated.");
+static int signal_terminate_callback(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
+ return manager_stop(userdata, MANAGER_TERMINATING);
+}
- return sd_event_exit(sd_event_source_get_event(s), 0);
+static int signal_restart_callback(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
+ return manager_stop(userdata, MANAGER_RESTARTING);
}
static int signal_reload_callback(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
@@ -614,8 +657,6 @@ int manager_new(Manager **ret, bool test_mode) {
}
Manager* manager_free(Manager *m) {
- Link *link;
-
if (!m)
return NULL;
@@ -623,9 +664,6 @@ Manager* manager_free(Manager *m) {
free(m->state_file);
- HASHMAP_FOREACH(link, m->links_by_index)
- (void) link_stop_engines(link, true);
-
m->request_queue = ordered_set_free(m->request_queue);
m->remove_request_queue = ordered_set_free(m->remove_request_queue);
@@ -739,7 +777,7 @@ int manager_start(Manager *m) {
int manager_load_config(Manager *m) {
int r;
- r = netdev_load(m, false);
+ r = netdev_load(m);
if (r < 0)
return r;
@@ -1150,7 +1188,7 @@ int manager_reload(Manager *m, sd_bus_message *message) {
(void) notify_reloading();
- r = netdev_load(m, /* reload= */ true);
+ r = netdev_reload(m);
if (r < 0)
goto finish;
diff --git a/src/network/networkd-manager.h b/src/network/networkd-manager.h
index 05a86b6b58..5f4508ea6f 100644
--- a/src/network/networkd-manager.h
+++ b/src/network/networkd-manager.h
@@ -19,6 +19,15 @@
#include "set.h"
#include "time-util.h"
+typedef enum ManagerState {
+ MANAGER_RUNNING,
+ MANAGER_TERMINATING,
+ MANAGER_RESTARTING,
+ MANAGER_STOPPED,
+ _MANAGER_STATE_MAX,
+ _MANAGER_STATE_INVALID = -EINVAL,
+} ManagerState;
+
struct Manager {
sd_netlink *rtnl;
/* lazy initialized */
@@ -35,10 +44,10 @@ struct Manager {
KeepConfiguration keep_configuration;
IPv6PrivacyExtensions ipv6_privacy_extensions;
+ ManagerState state;
bool test_mode;
bool enumerating;
bool dirty;
- bool restarting;
bool manage_foreign_routes;
bool manage_foreign_rules;
bool manage_foreign_nexthops;
diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c
index 46f3954725..0773e9e8ca 100644
--- a/src/network/networkd-ndisc.c
+++ b/src/network/networkd-ndisc.c
@@ -2141,6 +2141,8 @@ static int ndisc_drop_outdated(Link *link, const struct in6_addr *router, usec_t
updated = true;
}
+ RET_GATHER(ret, link_request_stacked_netdevs(link, NETDEV_LOCAL_ADDRESS_SLAAC));
+
if (updated)
link_dirty(link);
diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c
index 3d399fe876..9cd683e9bc 100644
--- a/src/network/networkd-network.c
+++ b/src/network/networkd-network.c
@@ -598,24 +598,47 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
return 0;
}
-int network_load(Manager *manager, OrderedHashmap **networks) {
+int network_load(Manager *manager, OrderedHashmap **ret) {
_cleanup_strv_free_ char **files = NULL;
+ OrderedHashmap *networks = NULL;
int r;
assert(manager);
-
- ordered_hashmap_clear_with_destructor(*networks, network_unref);
+ assert(ret);
r = conf_files_list_strv(&files, ".network", NULL, 0, NETWORK_DIRS);
if (r < 0)
return log_error_errno(r, "Failed to enumerate network files: %m");
STRV_FOREACH(f, files)
- (void) network_load_one(manager, networks, *f);
+ (void) network_load_one(manager, &networks, *f);
+ *ret = TAKE_PTR(networks);
return 0;
}
+static bool network_netdev_equal(Network *a, Network *b) {
+ assert(a);
+ assert(b);
+
+ if (a->batadv != b->batadv ||
+ a->bridge != b->bridge ||
+ a->bond != b->bond ||
+ a->vrf != b->vrf ||
+ a->xfrm != b->xfrm)
+ return false;
+
+ if (hashmap_size(a->stacked_netdevs) != hashmap_size(b->stacked_netdevs))
+ return false;
+
+ NetDev *n;
+ HASHMAP_FOREACH(n, a->stacked_netdevs)
+ if (hashmap_get(b->stacked_netdevs, n->ifname) != n)
+ return false;
+
+ return true;
+}
+
int network_reload(Manager *manager) {
OrderedHashmap *new_networks = NULL;
Network *n, *old;
@@ -630,15 +653,21 @@ int network_reload(Manager *manager) {
ORDERED_HASHMAP_FOREACH(n, new_networks) {
r = network_get_by_name(manager, n->name, &old);
if (r < 0) {
- log_debug("Found new .network file: %s", n->filename);
+ log_debug("%s: Found new .network file.", n->filename);
continue;
}
if (!stats_by_path_equal(n->stats_by_path, old->stats_by_path)) {
- log_debug("Found updated .network file: %s", n->filename);
+ log_debug("%s: Found updated .network file.", n->filename);
+ continue;
+ }
+
+ if (!network_netdev_equal(n, old)) {
+ log_debug("%s: Detected update of referenced .netdev file(s).", n->filename);
continue;
}
+ /* Nothing updated, use the existing Network object, and drop the new one. */
r = ordered_hashmap_replace(new_networks, old->name, old);
if (r < 0)
goto failure;
diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h
index b4ab117928..30ea9ac352 100644
--- a/src/network/networkd-network.h
+++ b/src/network/networkd-network.h
@@ -419,7 +419,7 @@ Network *network_ref(Network *network);
Network *network_unref(Network *network);
DEFINE_TRIVIAL_CLEANUP_FUNC(Network*, network_unref);
-int network_load(Manager *manager, OrderedHashmap **networks);
+int network_load(Manager *manager, OrderedHashmap **ret);
int network_reload(Manager *manager);
int network_load_one(Manager *manager, OrderedHashmap **networks, const char *filename);
int network_verify(Network *network);
diff --git a/src/network/networkd-queue.c b/src/network/networkd-queue.c
index dcb9bd0549..e898ea6e85 100644
--- a/src/network/networkd-queue.c
+++ b/src/network/networkd-queue.c
@@ -193,6 +193,7 @@ int netdev_queue_request(
int r;
assert(netdev);
+ assert(netdev->manager);
r = request_new(netdev->manager, NULL, REQUEST_TYPE_NETDEV_INDEPENDENT,
netdev, (mfree_func_t) netdev_unref,
diff --git a/src/network/networkd-setlink.c b/src/network/networkd-setlink.c
index b1a2623dcd..8519e6e7a0 100644
--- a/src/network/networkd-setlink.c
+++ b/src/network/networkd-setlink.c
@@ -191,10 +191,6 @@ static int link_configure_fill_message(
return r;
break;
case REQUEST_TYPE_SET_LINK_BOND:
- r = sd_netlink_message_set_flags(req, NLM_F_REQUEST | NLM_F_ACK);
- if (r < 0)
- return r;
-
r = sd_netlink_message_open_container(req, IFLA_LINKINFO);
if (r < 0)
return r;
diff --git a/src/notify/notify.c b/src/notify/notify.c
index 32bd6e6a7a..8937457ec9 100644
--- a/src/notify/notify.c
+++ b/src/notify/notify.c
@@ -28,7 +28,7 @@
static bool arg_ready = false;
static bool arg_reloading = false;
static bool arg_stopping = false;
-static pid_t arg_pid = 0;
+static PidRef arg_pid = PIDREF_NULL;
static const char *arg_status = NULL;
static bool arg_booted = false;
static uid_t arg_uid = UID_INVALID;
@@ -39,6 +39,7 @@ static char **arg_exec = NULL;
static FDSet *arg_fds = NULL;
static char *arg_fdname = NULL;
+STATIC_DESTRUCTOR_REGISTER(arg_pid, pidref_done);
STATIC_DESTRUCTOR_REGISTER(arg_env, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_exec, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_fds, fdset_freep);
@@ -99,16 +100,24 @@ static pid_t manager_pid(void) {
return pid;
}
-static pid_t pid_parent_if_possible(void) {
- pid_t parent_pid = getppid();
+static int pidref_parent_if_applicable(PidRef *ret) {
+ _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
+ int r;
+
+ assert(ret);
+
+ r = pidref_set_parent(&pidref);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to create reference to our parent process: %m");
/* Don't send from PID 1 or the service manager's PID (which might be distinct from 1, if we are a
* --user service). That'd just be confusing for the service manager. */
- if (parent_pid <= 1 ||
- parent_pid == manager_pid())
- return getpid_cached();
+ if (pidref.pid <= 1 ||
+ pidref.pid == manager_pid())
+ return pidref_set_self(ret);
- return parent_pid;
+ *ret = TAKE_PIDREF(pidref);
+ return 0;
}
static int parse_argv(int argc, char *argv[]) {
@@ -175,17 +184,18 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_PID:
+ pidref_done(&arg_pid);
+
if (isempty(optarg) || streq(optarg, "auto"))
- arg_pid = pid_parent_if_possible();
+ r = pidref_parent_if_applicable(&arg_pid);
else if (streq(optarg, "parent"))
- arg_pid = getppid();
+ r = pidref_set_parent(&arg_pid);
else if (streq(optarg, "self"))
- arg_pid = getpid_cached();
- else {
- r = parse_pid(optarg, &arg_pid);
- if (r < 0)
- return log_error_errno(r, "Failed to parse PID %s.", optarg);
- }
+ r = pidref_set_self(&arg_pid);
+ else
+ r = pidref_set_pidstr(&arg_pid, optarg);
+ if (r < 0)
+ return log_error_errno(r, "Failed to refer to --pid='%s': %m", optarg);
break;
@@ -276,7 +286,7 @@ static int parse_argv(int argc, char *argv[]) {
if (arg_fdname && fdset_isempty(arg_fds))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No file descriptors passed, but --fdname= set, refusing.");
- bool have_env = arg_ready || arg_stopping || arg_reloading || arg_status || arg_pid > 0 || !fdset_isempty(arg_fds);
+ bool have_env = arg_ready || arg_stopping || arg_reloading || arg_status || pidref_is_set(&arg_pid) || !fdset_isempty(arg_fds);
size_t n_arg_env;
if (do_exec) {
@@ -326,9 +336,10 @@ static int parse_argv(int argc, char *argv[]) {
}
static int run(int argc, char* argv[]) {
- _cleanup_free_ char *status = NULL, *cpid = NULL, *msg = NULL, *monotonic_usec = NULL, *fdn = NULL;
+ _cleanup_free_ char *status = NULL, *main_pid = NULL, *main_pidfd_id = NULL, *msg = NULL,
+ *monotonic_usec = NULL, *fdn = NULL;
_cleanup_strv_free_ char **final_env = NULL;
- const char *our_env[9];
+ const char *our_env[10];
size_t i = 0;
int r;
@@ -371,11 +382,22 @@ static int run(int argc, char* argv[]) {
our_env[i++] = status;
}
- if (arg_pid > 0) {
- if (asprintf(&cpid, "MAINPID="PID_FMT, arg_pid) < 0)
+ if (pidref_is_set(&arg_pid)) {
+ if (asprintf(&main_pid, "MAINPID="PID_FMT, arg_pid.pid) < 0)
return log_oom();
- our_env[i++] = cpid;
+ our_env[i++] = main_pid;
+
+ r = pidref_acquire_pidfd_id(&arg_pid);
+ if (r < 0)
+ log_debug_errno(r, "Unable to acquire pidfd id of new main pid " PID_FMT ", ignoring: %m",
+ arg_pid.pid);
+ else {
+ if (asprintf(&main_pidfd_id, "MAINPIDFDID=%" PRIu64, arg_pid.fd_id) < 0)
+ return log_oom();
+
+ our_env[i++] = main_pidfd_id;
+ }
}
if (!fdset_isempty(arg_fds)) {
@@ -415,11 +437,11 @@ static int run(int argc, char* argv[]) {
/* If --pid= is explicitly specified, use it as source pid. Otherwise, pretend the message originates
* from our parent, i.e. --pid=auto */
- if (arg_pid <= 0)
- arg_pid = pid_parent_if_possible();
+ if (!pidref_is_set(&arg_pid))
+ (void) pidref_parent_if_applicable(&arg_pid);
if (fdset_isempty(arg_fds))
- r = sd_pid_notify(arg_pid, /* unset_environment= */ false, msg);
+ r = sd_pid_notify(arg_pid.pid, /* unset_environment= */ false, msg);
else {
_cleanup_free_ int *a = NULL;
int k;
@@ -428,7 +450,7 @@ static int run(int argc, char* argv[]) {
if (k < 0)
return log_error_errno(k, "Failed to convert file descriptor set to array: %m");
- r = sd_pid_notify_with_fds(arg_pid, /* unset_environment= */ false, msg, a, k);
+ r = sd_pid_notify_with_fds(arg_pid.pid, /* unset_environment= */ false, msg, a, k);
}
if (r < 0)
@@ -440,7 +462,7 @@ static int run(int argc, char* argv[]) {
arg_fds = fdset_free(arg_fds); /* Close before we execute anything */
if (!arg_no_block) {
- r = sd_pid_notify_barrier(arg_pid, /* unset_environment= */ false, 5 * USEC_PER_SEC);
+ r = sd_pid_notify_barrier(arg_pid.pid, /* unset_environment= */ false, 5 * USEC_PER_SEC);
if (r < 0)
return log_error_errno(r, "Failed to invoke barrier: %m");
if (r == 0)
diff --git a/src/partition/repart.c b/src/partition/repart.c
index 65b50e346f..5427e0a69f 100644
--- a/src/partition/repart.c
+++ b/src/partition/repart.c
@@ -3511,7 +3511,7 @@ static int context_dump_partitions(Context *context) {
const size_t roothash_col = 14, dropin_files_col = 15, split_path_col = 16;
bool has_roothash = false, has_dropin_files = false, has_split_path = false;
- if ((arg_json_format_flags & SD_JSON_FORMAT_OFF) && context->n_partitions == 0) {
+ if (context->n_partitions == 0 && !sd_json_format_enabled(arg_json_format_flags)) {
log_info("Empty partition table.");
return 0;
}
@@ -3541,7 +3541,7 @@ static int context_dump_partitions(Context *context) {
table_set_json_field_name(t, 15, "drop-in_files");
if (!DEBUG_LOGGING) {
- if (arg_json_format_flags & SD_JSON_FORMAT_OFF)
+ if (!sd_json_format_enabled(arg_json_format_flags))
(void) table_set_display(t, (size_t) 0, (size_t) 1, (size_t) 2, (size_t) 3, (size_t) 4,
(size_t) 8, (size_t) 9, (size_t) 12, roothash_col, dropin_files_col,
split_path_col);
@@ -3624,7 +3624,7 @@ static int context_dump_partitions(Context *context) {
has_split_path = has_split_path || !isempty(p->split_path);
}
- if ((arg_json_format_flags & SD_JSON_FORMAT_OFF) && (sum_padding > 0 || sum_size > 0)) {
+ if (!sd_json_format_enabled(arg_json_format_flags) && (sum_padding > 0 || sum_size > 0)) {
const char *a, *b;
a = strjoina(special_glyph(SPECIAL_GLYPH_SIGMA), " = ", FORMAT_BYTES(sum_size));
@@ -3891,17 +3891,17 @@ static int context_dump(Context *context, bool late) {
assert(context);
- if (arg_pretty == 0 && FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF))
+ if (arg_pretty == 0 && !sd_json_format_enabled(arg_json_format_flags))
return 0;
/* If we're outputting JSON, only dump after doing all operations so we can include the roothashes
* in the output. */
- if (!late && !FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF))
+ if (!late && sd_json_format_enabled(arg_json_format_flags))
return 0;
/* If we're not outputting JSON, only dump again after doing all operations if there are any
* roothashes that we need to communicate to the user. */
- if (late && FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF) && !context_has_roothash(context))
+ if (late && !sd_json_format_enabled(arg_json_format_flags) && !context_has_roothash(context))
return 0;
r = context_dump_partitions(context);
@@ -3910,7 +3910,7 @@ static int context_dump(Context *context, bool late) {
/* Only write the partition bar once, even if we're writing the partition table twice to communicate
* roothashes. */
- if (FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF) && !late) {
+ if (!sd_json_format_enabled(arg_json_format_flags) && !late) {
putc('\n', stdout);
r = context_dump_partition_bar(context);
@@ -5085,21 +5085,19 @@ static int partition_format_verity_sig(Context *context, Partition *p) {
static int progress_bytes(uint64_t n_bytes, void *userdata) {
Partition *p = ASSERT_PTR(userdata);
- _cleanup_free_ char *s = NULL;
p->copy_blocks_done += n_bytes;
- if (asprintf(&s, "%s %s %s %s/%s",
- strna(p->copy_blocks_path),
- special_glyph(SPECIAL_GLYPH_ARROW_RIGHT),
- strna(p->definition_path),
- FORMAT_BYTES(p->copy_blocks_done),
- FORMAT_BYTES(p->copy_blocks_size)) < 0)
- return log_oom();
+ (void) draw_progress_barf(
+ p->copy_blocks_done >= p->copy_blocks_size ? 100.0 : /* catch division be zero */
+ 100.0 * (double) p->copy_blocks_done / (double) p->copy_blocks_size,
+ "%s %s %s %s/%s",
+ strna(p->copy_blocks_path),
+ special_glyph(SPECIAL_GLYPH_ARROW_RIGHT),
+ strna(p->definition_path),
+ FORMAT_BYTES(p->copy_blocks_done),
+ FORMAT_BYTES(p->copy_blocks_size));
- draw_progress_bar(s,
- p->copy_blocks_done >= p->copy_blocks_size ? 100.0 : /* catch division be zero */
- 100.0 * (double) p->copy_blocks_done / (double) p->copy_blocks_size);
return 0;
}
diff --git a/src/pcrlock/pcrlock.c b/src/pcrlock/pcrlock.c
index 46f285d54a..c1915761ee 100644
--- a/src/pcrlock/pcrlock.c
+++ b/src/pcrlock/pcrlock.c
@@ -2045,7 +2045,7 @@ static int add_algorithm_columns(
if (r < 0)
return table_log_add_error(r);
- if (FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF) &&
+ if (!sd_json_format_enabled(arg_json_format_flags) &&
el->primary_algorithm != UINT16_MAX &&
*alg != el->primary_algorithm)
(void) table_hide_column_from_display(table, c);
@@ -2106,7 +2106,7 @@ static int show_log_table(EventLog *el, sd_json_variant **ret_variant) {
(void) table_hide_column_from_display(table, table_get_columns(table) - 3); /* hide source */
- if (!FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF))
+ if (sd_json_format_enabled(arg_json_format_flags))
(void) table_hide_column_from_display(table, (size_t) 1); /* hide color block column */
(void) table_set_json_field_name(table, phase_column, "phase");
@@ -2242,7 +2242,7 @@ static int show_pcr_table(EventLog *el, sd_json_variant **ret_variant) {
if (r < 0)
return r;
- if (!FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF))
+ if (sd_json_format_enabled(arg_json_format_flags))
(void) table_hide_column_from_display(table, (size_t) 1, (size_t) 2); /* hide color block and emoji column */
else if (!emoji_enabled())
(void) table_hide_column_from_display(table, (size_t) 2);
@@ -2347,7 +2347,7 @@ static int show_pcr_table(EventLog *el, sd_json_variant **ret_variant) {
if (r < 0)
return log_error_errno(r, "Failed to output table: %m");
- if (FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF))
+ if (!sd_json_format_enabled(arg_json_format_flags))
printf("\n"
"%sLegend: H → PCR hash value matches event log%s\n"
"%s R → All event log records for this PCR have a matching component%s\n"
@@ -2431,7 +2431,7 @@ static int event_log_load_and_process(EventLog **ret) {
static int verb_show_log(int argc, char *argv[], void *userdata) {
_cleanup_(sd_json_variant_unrefp) sd_json_variant *log_table = NULL, *pcr_table = NULL;
_cleanup_(event_log_freep) EventLog *el = NULL;
- bool want_json = !FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF);
+ bool want_json = sd_json_format_enabled(arg_json_format_flags);
int r;
r = event_log_load_and_process(&el);
@@ -2607,7 +2607,7 @@ static int verb_list_components(int argc, char *argv[], void *userdata) {
FOREACH_ARRAY(c, el->components, el->n_components) {
- if (FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF)) {
+ if (!sd_json_format_enabled(arg_json_format_flags)) {
_cleanup_free_ char *marker = NULL;
switch (loc) {
@@ -2653,13 +2653,13 @@ static int verb_list_components(int argc, char *argv[], void *userdata) {
}
}
- if (!table_isempty(table) || !FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF)) {
+ if (!table_isempty(table) || sd_json_format_enabled(arg_json_format_flags)) {
r = table_print_with_pager(table, arg_json_format_flags, arg_pager_flags, /* show_header= */ true);
if (r < 0)
return log_error_errno(r, "Failed to output table: %m");
}
- if (FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF)) {
+ if (!sd_json_format_enabled(arg_json_format_flags)) {
if (table_isempty(table))
printf("No components defined.\n");
else
@@ -4154,7 +4154,7 @@ static int event_log_show_predictions(Tpm2PCRPrediction *context, uint16_t alg)
pager_open(arg_pager_flags);
- if (!FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF)) {
+ if (sd_json_format_enabled(arg_json_format_flags)) {
_cleanup_(sd_json_variant_unrefp) sd_json_variant *j = NULL;
for (size_t i = 0; i < TPM2_N_HASH_ALGORITHMS; i++) {
diff --git a/src/resolve/resolvectl.c b/src/resolve/resolvectl.c
index 0204fdf16f..62354b4784 100644
--- a/src/resolve/resolvectl.c
+++ b/src/resolve/resolvectl.c
@@ -204,7 +204,7 @@ static void print_source(uint64_t flags, usec_t rtt) {
if (!arg_legend)
return;
- if (!FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF))
+ if (sd_json_format_enabled(arg_json_format_flags))
return;
if (flags == 0)
@@ -273,7 +273,7 @@ static int resolve_host(sd_bus *bus, const char *name) {
assert(name);
- if (!FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF))
+ if (sd_json_format_enabled(arg_json_format_flags))
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Use --json=pretty with --type=A or --type=AAAA to acquire address record information in JSON format.");
log_debug("Resolving %s (family %s, interface %s).", name, af_to_name(arg_family) ?: "*", isempty(arg_ifname) ? "*" : arg_ifname);
@@ -374,7 +374,7 @@ static int resolve_address(sd_bus *bus, int family, const union in_addr_union *a
assert(IN_SET(family, AF_INET, AF_INET6));
assert(address);
- if (!FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF))
+ if (sd_json_format_enabled(arg_json_format_flags))
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Use --json=pretty with --type= to acquire resource record information in JSON format.");
if (ifindex <= 0)
@@ -468,7 +468,7 @@ static int output_rr_packet(const void *d, size_t l, int ifindex) {
if (r < 0)
return log_error_errno(r, "Failed to parse RR: %m");
- if (!FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF)) {
+ if (sd_json_format_enabled(arg_json_format_flags)) {
_cleanup_(sd_json_variant_unrefp) sd_json_variant *j = NULL;
r = dns_resource_record_to_json(rr, &j);
if (r < 0)
@@ -994,7 +994,7 @@ static int verb_service(int argc, char **argv, void *userdata) {
if (r < 0)
return r;
- if (!FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF))
+ if (sd_json_format_enabled(arg_json_format_flags))
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Use --json=pretty with --type= to acquire resource record information in JSON format.");
if (argc == 2)
@@ -1060,7 +1060,7 @@ static int verb_openpgp(int argc, char **argv, void *userdata) {
if (r < 0)
return r;
- if (!FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF))
+ if (sd_json_format_enabled(arg_json_format_flags))
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Use --json=pretty with --type= to acquire resource record information in JSON format.");
STRV_FOREACH(p, strv_skip(argv, 1))
@@ -1117,7 +1117,7 @@ static int verb_tlsa(int argc, char **argv, void *userdata) {
if (r < 0)
return r;
- if (!FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF))
+ if (sd_json_format_enabled(arg_json_format_flags))
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Use --json=pretty with --type= to acquire resource record information in JSON format.");
if (service_family_is_valid(argv[1])) {
@@ -1152,7 +1152,7 @@ static int show_statistics(int argc, char **argv, void *userdata) {
if (r < 0)
return r;
- if (!FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF))
+ if (sd_json_format_enabled(arg_json_format_flags))
return sd_json_variant_dump(reply, arg_json_format_flags, NULL, NULL);
struct statistics {
@@ -1316,7 +1316,7 @@ static int reset_statistics(int argc, char **argv, void *userdata) {
if (r < 0)
return r;
- if (!FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF))
+ if (sd_json_format_enabled(arg_json_format_flags))
return sd_json_variant_dump(reply, arg_json_format_flags, NULL, NULL);
return 0;
@@ -2978,7 +2978,7 @@ static int monitor_reply(
return 0;
}
- if (arg_json_format_flags & SD_JSON_FORMAT_OFF) {
+ if (!sd_json_format_enabled(arg_json_format_flags)) {
monitor_query_dump(parameters);
printf("\n");
} else
@@ -3180,7 +3180,7 @@ static int verb_show_cache(int argc, char *argv[], void *userdata) {
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
"DumpCache() response 'dump' field not an array");
- if (FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF)) {
+ if (!sd_json_format_enabled(arg_json_format_flags)) {
sd_json_variant *i;
JSON_VARIANT_ARRAY_FOREACH(i, d) {
@@ -3360,7 +3360,7 @@ static int verb_show_server_state(int argc, char *argv[], void *userdata) {
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
"DumpCache() response 'dump' field not an array");
- if (FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF)) {
+ if (!sd_json_format_enabled(arg_json_format_flags)) {
sd_json_variant *i;
JSON_VARIANT_ARRAY_FOREACH(i, d) {
diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c
index f9991d86ab..c414ca800c 100644
--- a/src/resolve/resolved-dns-packet.c
+++ b/src/resolve/resolved-dns-packet.c
@@ -557,12 +557,19 @@ int dns_packet_append_name(
bool canonical_candidate,
size_t *start) {
- size_t saved_size;
+ _cleanup_free_ char **added_entries = NULL; /* doesn't own the strings! this is just regular pointer array, not a NULL-terminated strv! */
+ size_t n_added_entries = 0, saved_size;
int r;
assert(p);
assert(name);
+ r = dns_name_is_valid(name);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -EINVAL;
+
if (p->refuse_compression)
allow_compression = false;
@@ -598,6 +605,11 @@ int dns_packet_append_name(
if (allow_compression) {
_cleanup_free_ char *s = NULL;
+ if (!GREEDY_REALLOC(added_entries, n_added_entries + 1)) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
s = strdup(z);
if (!s) {
r = -ENOMEM;
@@ -608,7 +620,8 @@ int dns_packet_append_name(
if (r < 0)
goto fail;
- TAKE_PTR(s);
+ /* Keep track of the entries we just added (note that the string is owned by the hashtable, not this array!) */
+ added_entries[n_added_entries++] = TAKE_PTR(s);
}
}
@@ -623,6 +636,12 @@ done:
return 0;
fail:
+ /* Remove all label compression names we added again */
+ FOREACH_ARRAY(s, added_entries, n_added_entries) {
+ hashmap_remove(p->names, *s);
+ free(*s);
+ }
+
dns_packet_truncate(p, saved_size);
return r;
}
@@ -1506,7 +1525,7 @@ int dns_packet_read_name(
size_t after_rindex = 0, jump_barrier = p->rindex;
_cleanup_free_ char *name = NULL;
bool first = true;
- size_t n = 0;
+ size_t n = 0, m = 0;
int r;
if (p->refuse_compression)
@@ -1535,14 +1554,21 @@ int dns_packet_read_name(
if (first)
first = false;
- else
+ else {
name[n++] = '.';
+ m++;
+ }
r = dns_label_escape(label, c, name + n, DNS_LABEL_ESCAPED_MAX);
if (r < 0)
return r;
n += r;
+ m += c;
+
+ if (m > DNS_HOSTNAME_MAX)
+ return -EBADMSG;
+
continue;
} else if (allow_compression && FLAGS_SET(c, 0xc0)) {
uint16_t ptr;
diff --git a/src/resolve/resolved-varlink.c b/src/resolve/resolved-varlink.c
index 6a8e80128c..168923e6b5 100644
--- a/src/resolve/resolved-varlink.c
+++ b/src/resolve/resolved-varlink.c
@@ -241,16 +241,12 @@ static void vl_method_resolve_hostname_complete(DnsQuery *query) {
assert(q);
- if (q->state != DNS_TRANSACTION_SUCCESS) {
- r = reply_query_state(q);
- goto finish;
- }
+ if (q->state != DNS_TRANSACTION_SUCCESS)
+ return (void) reply_query_state(q);
r = dns_query_process_cname_many(q);
- if (r == -ELOOP) {
- r = sd_varlink_error(q->varlink_request, "io.systemd.Resolve.CNAMELoop", NULL);
- goto finish;
- }
+ if (r == -ELOOP)
+ return (void) sd_varlink_error(q->varlink_request, "io.systemd.Resolve.CNAMELoop", NULL);
if (r < 0)
goto finish;
if (r == DNS_QUERY_CNAME) {
@@ -265,10 +261,8 @@ static void vl_method_resolve_hostname_complete(DnsQuery *query) {
if (r < 0)
goto finish;
- if (sd_json_variant_is_blank_object(array)) {
- r = sd_varlink_error(q->varlink_request, "io.systemd.Resolve.NoSuchResourceRecord", NULL);
- goto finish;
- }
+ if (sd_json_variant_is_blank_object(array))
+ return (void) sd_varlink_error(q->varlink_request, "io.systemd.Resolve.NoSuchResourceRecord", NULL);
assert(canonical);
r = dns_name_normalize(dns_resource_key_name(canonical->key), 0, &normalized);
@@ -443,16 +437,12 @@ static void vl_method_resolve_address_complete(DnsQuery *query) {
assert(q);
- if (q->state != DNS_TRANSACTION_SUCCESS) {
- r = reply_query_state(q);
- goto finish;
- }
+ if (q->state != DNS_TRANSACTION_SUCCESS)
+ return (void) reply_query_state(q);
r = dns_query_process_cname_many(q);
- if (r == -ELOOP) {
- r = sd_varlink_error(q->varlink_request, "io.systemd.Resolve.CNAMELoop", NULL);
- goto finish;
- }
+ if (r == -ELOOP)
+ return (void) sd_varlink_error(q->varlink_request, "io.systemd.Resolve.CNAMELoop", NULL);
if (r < 0)
goto finish;
if (r == DNS_QUERY_CNAME) {
@@ -484,10 +474,8 @@ static void vl_method_resolve_address_complete(DnsQuery *query) {
goto finish;
}
- if (sd_json_variant_is_blank_object(array)) {
- r = sd_varlink_error(q->varlink_request, "io.systemd.Resolve.NoSuchResourceRecord", NULL);
- goto finish;
- }
+ if (sd_json_variant_is_blank_object(array))
+ return (void) sd_varlink_error(q->varlink_request, "io.systemd.Resolve.NoSuchResourceRecord", NULL);
r = sd_varlink_replybo(
q->varlink_request,
@@ -711,14 +699,14 @@ static int append_srv(
return 1; /* added */
}
-static sd_varlink *get_vl_link_aux_query(DnsQuery *aux) {
+static sd_varlink* take_vl_link_aux_query(DnsQuery *aux) {
assert(aux);
/* Find the main query */
while (aux->auxiliary_for)
aux = aux->auxiliary_for;
- return aux->varlink_request;
+ return TAKE_PTR(aux->varlink_request);
}
static void resolve_service_all_complete(DnsQuery *query) {
@@ -768,20 +756,16 @@ static void resolve_service_all_complete(DnsQuery *query) {
if (bad->state == DNS_TRANSACTION_SUCCESS) {
assert(bad->auxiliary_result != 0);
- if (bad->auxiliary_result == -ELOOP) {
- r = sd_varlink_error(query->varlink_request, "io.systemd.Resolve.CNAMELoop", NULL);
- goto finish;
- }
+ if (bad->auxiliary_result == -ELOOP)
+ return (void) sd_varlink_error(query->varlink_request, "io.systemd.Resolve.CNAMELoop", NULL);
assert(bad->auxiliary_result < 0);
r = bad->auxiliary_result;
goto finish;
}
- bad->varlink_request = get_vl_link_aux_query(bad);
- r = reply_query_state(bad);
- bad->varlink_request = NULL;
- goto finish;
+ bad->varlink_request = take_vl_link_aux_query(bad);
+ return (void) reply_query_state(bad);
}
}
@@ -804,10 +788,8 @@ static void resolve_service_all_complete(DnsQuery *query) {
canonical = dns_resource_record_ref(rr);
}
- if (sd_json_variant_is_blank_object(srv)) {
- r = sd_varlink_error(query->varlink_request, "io.systemd.Resolve.NoSuchResourceRecord", NULL);
- goto finish;
- }
+ if (sd_json_variant_is_blank_object(srv))
+ return (void) sd_varlink_error(query->varlink_request, "io.systemd.Resolve.NoSuchResourceRecord", NULL);
DNS_ANSWER_FOREACH(rr, q->answer) {
r = dns_question_matches_rr(question, rr, NULL);
@@ -829,10 +811,8 @@ static void resolve_service_all_complete(DnsQuery *query) {
if (r < 0)
goto finish;
- if (isempty(type)) {
- r = sd_varlink_error(q->varlink_request, "io.systemd.Resolve.InconsistentServiceRecords", NULL);
- goto finish;
- }
+ if (isempty(type))
+ return (void) sd_varlink_error(q->varlink_request, "io.systemd.Resolve.InconsistentServiceRecords", NULL);
r = sd_varlink_replybo(
query->varlink_request,
@@ -925,18 +905,14 @@ static void vl_method_resolve_service_complete(DnsQuery *query) {
assert(q);
- if (q->state != DNS_TRANSACTION_SUCCESS) {
- r = reply_query_state(q);
- goto finish;
- }
+ if (q->state != DNS_TRANSACTION_SUCCESS)
+ return (void) reply_query_state(q);
r = dns_query_process_cname_many(q);
- if (r == -ELOOP) {
- r = sd_varlink_error(q->varlink_request, "io.systemd.Resolve.CNAMELoop", NULL);
- goto finish;
- }
+ if (r == -ELOOP)
+ return (void) sd_varlink_error(q->varlink_request, "io.systemd.Resolve.CNAMELoop", NULL);
if (r < 0)
- goto finish;
+ goto fail;
if (r == DNS_QUERY_CNAME) {
/* This was a cname, and the query was restarted. */
TAKE_PTR(q);
@@ -948,7 +924,7 @@ static void vl_method_resolve_service_complete(DnsQuery *query) {
DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, q->answer) {
r = dns_question_matches_rr(question, rr, NULL);
if (r < 0)
- goto finish;
+ goto fail;
if (r == 0)
continue;
@@ -966,34 +942,28 @@ static void vl_method_resolve_service_complete(DnsQuery *query) {
q->block_all_complete--;
if (r < 0)
- goto finish;
+ goto fail;
}
found++;
}
- if (has_root_domain && found <= 0) {
+ if (has_root_domain && found <= 0)
/* If there's exactly one SRV RR and it uses the root domain as hostname, then the service is
* explicitly not offered on the domain. Report this as a recognizable error. See RFC 2782,
* Section "Usage Rules". */
- r = sd_varlink_error(q->varlink_request, "io.systemd.Resolve.ServiceNotProvided", NULL);
- goto finish;
- }
+ return (void) sd_varlink_error(q->varlink_request, "io.systemd.Resolve.ServiceNotProvided", NULL);
- if (found <= 0) {
- r = sd_varlink_error(q->varlink_request, "io.systemd.Resolve.NoSuchResourceRecord", NULL);
- goto finish;
- }
+ if (found <= 0)
+ return (void) sd_varlink_error(q->varlink_request, "io.systemd.Resolve.NoSuchResourceRecord", NULL);
/* Maybe we are already finished? check now... */
resolve_service_all_complete(TAKE_PTR(q));
return;
-finish:
- if (r < 0) {
- log_error_errno(r, "Failed to send address reply: %m");
- (void) sd_varlink_error_errno(q->varlink_request, r);
- }
+fail:
+ log_error_errno(r, "Failed to send address reply: %m");
+ (void) sd_varlink_error_errno(q->varlink_request, r);
}
static int vl_method_resolve_service(sd_varlink* link, sd_json_variant* parameters, sd_varlink_method_flags_t flags, void* userdata) {
@@ -1090,16 +1060,12 @@ static void vl_method_resolve_record_complete(DnsQuery *query) {
assert(q);
- if (q->state != DNS_TRANSACTION_SUCCESS) {
- r = reply_query_state(q);
- goto finish;
- }
+ if (q->state != DNS_TRANSACTION_SUCCESS)
+ return (void) reply_query_state(q);
r = dns_query_process_cname_many(q);
- if (r == -ELOOP) {
- r = sd_varlink_error(q->varlink_request, "io.systemd.Resolve.CNAMELoop", NULL);
- goto finish;
- }
+ if (r == -ELOOP)
+ return (void) sd_varlink_error(q->varlink_request, "io.systemd.Resolve.CNAMELoop", NULL);
if (r < 0)
goto finish;
if (r == DNS_QUERY_CNAME) {
@@ -1141,10 +1107,8 @@ static void vl_method_resolve_record_complete(DnsQuery *query) {
added++;
}
- if (added <= 0) {
- r = sd_varlink_error(q->varlink_request, "io.systemd.Resolve.NoSuchResourceRecord", NULL);
- goto finish;
- }
+ if (added <= 0)
+ return (void) sd_varlink_error(q->varlink_request, "io.systemd.Resolve.NoSuchResourceRecord", NULL);
r = sd_varlink_replybo(
q->varlink_request,
diff --git a/src/resolve/test-dns-packet-append.c b/src/resolve/test-dns-packet-append.c
index 9ef77b3b98..8942050927 100644
--- a/src/resolve/test-dns-packet-append.c
+++ b/src/resolve/test-dns-packet-append.c
@@ -1267,4 +1267,34 @@ TEST(packet_append_answer_single_svcb) {
ASSERT_EQ(memcmp(DNS_PACKET_DATA(packet), data, sizeof(data)), 0);
}
+static void dump_packet_data(DnsPacket *packet) {
+ assert(packet);
+ fprintf(stderr, "packet bytes:");
+ for (size_t i = 0; i < packet->size; i++)
+ fprintf(stderr, " %x", DNS_PACKET_DATA(packet)[i]);
+ fprintf(stderr, "\n");
+}
+
+TEST(packet_append_key_name_too_long) {
+ _cleanup_(dns_packet_unrefp) DnsPacket *packet = NULL;
+ _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
+ int r;
+
+ ASSERT_OK(dns_packet_new(&packet, DNS_PROTOCOL_DNS, 0, DNS_PACKET_SIZE_MAX));
+
+ DNS_PACKET_ID(packet) = htobe16(42);
+ DNS_PACKET_HEADER(packet)->flags = htobe16(DNS_PACKET_MAKE_FLAGS(0, 0, 0, 0, 1, 0, 0, 0, 0));
+ DNS_PACKET_HEADER(packet)->qdcount = htobe16(1);
+
+ key = ASSERT_PTR(dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, "www.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.com"));
+ r = dns_packet_append_key(packet, key, 0, NULL);
+
+ log_debug("r = %d, size = %zu", r, packet->size);
+ log_debug("key name = <%s>", dns_resource_key_name(key));
+ dump_packet_data(packet);
+
+ ASSERT_EQ(r, -EINVAL);
+ ASSERT_EQ(packet->size, 12U);
+}
+
DEFINE_TEST_MAIN(LOG_DEBUG)
diff --git a/src/resolve/test-dns-packet-extract.c b/src/resolve/test-dns-packet-extract.c
index 5cd5a83d49..0bded09077 100644
--- a/src/resolve/test-dns-packet-extract.c
+++ b/src/resolve/test-dns-packet-extract.c
@@ -806,12 +806,7 @@ TEST(packet_query_single_long_domain) {
0x10, 'n', 'i', 't', 'r', 'o', 's', 'y', 'l', 's', 'u', 'l', 'f', 'u', 'r', 'i', 'c',
0x10, 'o', 'b', 'j', 'e', 'c', 't', 'l', 'e', 's', 's', 'n', 'e', 's', 's', 'e', 's',
0x10, 'p', 'a', 'r', 't', 'r', 'i', 'd', 'g', 'e', 'b', 'e', 'r', 'r', 'i', 'e', 's',
- 0x10, 'r', 'e', 'a', 's', 'o', 'n', 'l', 'e', 's', 's', 'n', 'e', 's', 's', 'e', 's',
- 0x10, 's', 'e', 'm', 'i', 'p', 'a', 't', 'h', 'o', 'l', 'o', 'g', 'i', 'c', 'a', 'l',
- 0x10, 't', 'o', 'm', 'f', 'o', 'o', 'l', 'i', 's', 'h', 'n', 'e', 's', 's', 'e', 's',
- 0x10, 'u', 'n', 'd', 'e', 'r', 'c', 'a', 'p', 'i', 't', 'a', 'l', 'i', 'z', 'e', 'd',
- 0x10, 'v', 'e', 'c', 't', 'o', 'r', 'c', 'a', 'r', 'd', 'i', 'o', 'g', 'r', 'a', 'm',
- 0x10, 'w', 'e', 'a', 't', 'h', 'e', 'r', 'p', 'r', 'o', 'o', 'f', 'n', 'e', 's', 's',
+ 0x0F, 'r', 'e', 'a', 's', 'o', 'n', 'l', 'e', 's', 's', 'n', 'e', 's', 's', 'e',
0x00,
/* A */ 0x00, 0x01,
/* IN */ 0x00, 0x01
@@ -823,12 +818,12 @@ TEST(packet_query_single_long_domain) {
ASSERT_EQ(dns_question_size(packet->question), 1u);
ASSERT_EQ(dns_answer_size(packet->answer), 0u);
- key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A,
- "absorptivenesses.calligraphically.deacidifications.ecophysiological."
- "falsifiabilities.heterochromatism.icositetrahedron.journalistically."
- "kinaesthetically.lactovegetarians.misinterpretable.nitrosylsulfuric."
- "objectlessnesses.partridgeberries.reasonlessnesses.semipathological."
- "tomfoolishnesses.undercapitalized.vectorcardiogram.weatherproofness");
+ key = dns_resource_key_new(
+ DNS_CLASS_IN, DNS_TYPE_A,
+ "absorptivenesses.calligraphically.deacidifications.ecophysiological."
+ "falsifiabilities.heterochromatism.icositetrahedron.journalistically."
+ "kinaesthetically.lactovegetarians.misinterpretable.nitrosylsulfuric."
+ "objectlessnesses.partridgeberries.reasonlessnesse");
ASSERT_NOT_NULL(key);
ASSERT_TRUE(dns_question_contains_key(packet->question, key));
@@ -4777,4 +4772,78 @@ TEST(format_dns_svc_param_key) {
ASSERT_STREQ(str, "ohttp");
}
+TEST(overlong_domain) {
+ _cleanup_(dns_packet_unrefp) DnsPacket *packet = NULL;
+
+ ASSERT_OK(dns_packet_new(&packet, DNS_PROTOCOL_DNS, 0, DNS_PACKET_SIZE_MAX));
+ ASSERT_NOT_NULL(packet);
+ dns_packet_truncate(packet, 0);
+
+ const uint8_t data[] = {
+ 0x00, 0x42, 0x00, 0x00,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+ 63, '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2',
+ 63, '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2',
+ 63, '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2',
+ 63, '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2',
+ 0x00,
+ 0x00, DNS_TYPE_A,
+ 0x00, DNS_CLASS_IN,
+ };
+
+ ASSERT_OK(dns_packet_append_blob(packet, data, sizeof(data), NULL));
+ ASSERT_OK(dns_packet_validate_query(packet));
+ _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
+ ASSERT_ERROR(dns_packet_read_key(packet, &key, NULL, NULL), EBADMSG);
+
+ const uint8_t data2[] = {
+ 0x00, 0x42, 0x00, 0x00,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+ 63, '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2',
+ 63, '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2',
+ 63, '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2',
+ 62, '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1',
+ 0x00,
+ 0x00, DNS_TYPE_A,
+ 0x00, DNS_CLASS_IN,
+ };
+
+ packet = dns_packet_unref(packet);
+ ASSERT_OK(dns_packet_new(&packet, DNS_PROTOCOL_DNS, 0, DNS_PACKET_SIZE_MAX));
+ ASSERT_NOT_NULL(packet);
+ dns_packet_truncate(packet, 0);
+ ASSERT_OK(dns_packet_append_blob(packet, data2, sizeof(data2), NULL));
+ ASSERT_OK(dns_packet_validate_query(packet));
+ ASSERT_ERROR(dns_packet_read_key(packet, &key, NULL, NULL), EBADMSG);
+
+ const uint8_t data3[] = {
+ 0x00, 0x42, 0x00, 0x00,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+ 63, '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2',
+ 63, '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2',
+ 63, '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2',
+ 61, '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
+ 0x00,
+ 0x00, DNS_TYPE_A,
+ 0x00, DNS_CLASS_IN,
+ };
+
+ packet = dns_packet_unref(packet);
+ ASSERT_OK(dns_packet_new(&packet, DNS_PROTOCOL_DNS, 0, DNS_PACKET_SIZE_MAX));
+ ASSERT_NOT_NULL(packet);
+ dns_packet_truncate(packet, 0);
+ ASSERT_OK(dns_packet_append_blob(packet, data3, sizeof(data3), NULL));
+ ASSERT_OK(dns_packet_validate_query(packet));
+ ASSERT_OK(dns_packet_read_key(packet, &key, NULL, NULL));
+
+ ASSERT_STREQ(dns_resource_key_name(key),
+ "012345678901234567890123456789012345678901234567890123456789012."
+ "012345678901234567890123456789012345678901234567890123456789012."
+ "012345678901234567890123456789012345678901234567890123456789012."
+ "0123456789012345678901234567890123456789012345678901234567890");
+}
+
DEFINE_TEST_MAIN(LOG_DEBUG)
diff --git a/src/run/run.c b/src/run/run.c
index b73e292514..e4bcc5d599 100644
--- a/src/run/run.c
+++ b/src/run/run.c
@@ -1858,11 +1858,11 @@ static void set_window_title(PTYForward *f) {
(void) pty_forward_set_title_prefix(f, dot);
}
-static int chown_to_capsule(const char *path, const char *capsule) {
+static int fchown_to_capsule(int fd, const char *capsule) {
_cleanup_free_ char *p = NULL;
int r;
- assert(path);
+ assert(fd >= 0);
assert(capsule);
p = path_join("/run/capsules/", capsule);
@@ -1877,7 +1877,7 @@ static int chown_to_capsule(const char *path, const char *capsule) {
if (uid_is_system(st.st_uid) || gid_is_system(st.st_gid)) /* paranoid safety check */
return -EPERM;
- return chmod_and_chown(path, 0600, st.st_uid, st.st_gid);
+ return fchmod_and_chown(fd, 0600, st.st_uid, st.st_gid);
}
static int print_unit_invocation(const char *unit, sd_id128_t invocation_id) {
@@ -1886,7 +1886,7 @@ static int print_unit_invocation(const char *unit, sd_id128_t invocation_id) {
assert(unit);
- if (FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF)) {
+ if (!sd_json_format_enabled(arg_json_format_flags)) {
if (sd_id128_is_null(invocation_id))
log_info("Running as unit: %s", unit);
else
@@ -1912,7 +1912,7 @@ static int start_transient_service(sd_bus *bus) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
_cleanup_free_ char *service = NULL, *pty_path = NULL;
- _cleanup_close_ int master = -EBADF, slave = -EBADF;
+ _cleanup_close_ int pty_fd = -EBADF, peer_fd = -EBADF;
int r;
assert(bus);
@@ -1922,29 +1922,22 @@ static int start_transient_service(sd_bus *bus) {
if (arg_stdio == ARG_STDIO_PTY) {
if (IN_SET(arg_transport, BUS_TRANSPORT_LOCAL, BUS_TRANSPORT_CAPSULE)) {
- master = posix_openpt(O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK);
- if (master < 0)
- return log_error_errno(errno, "Failed to acquire pseudo tty: %m");
+ pty_fd = openpt_allocate(O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK, &pty_path);
+ if (pty_fd < 0)
+ return log_error_errno(pty_fd, "Failed to acquire pseudo tty: %m");
- r = ptsname_malloc(master, &pty_path);
- if (r < 0)
- return log_error_errno(r, "Failed to determine tty name: %m");
+ peer_fd = pty_open_peer(pty_fd, O_RDWR|O_NOCTTY|O_CLOEXEC);
+ if (peer_fd < 0)
+ return log_error_errno(peer_fd, "Failed to open pty peer: %m");
if (arg_transport == BUS_TRANSPORT_CAPSULE) {
/* If we are in capsule mode, we must give the capsule UID/GID access to the PTY we just allocated first. */
- r = chown_to_capsule(pty_path, arg_host);
+ r = fchown_to_capsule(peer_fd, arg_host);
if (r < 0)
return log_error_errno(r, "Failed to chown tty to capsule UID/GID: %m");
}
- if (unlockpt(master) < 0)
- return log_error_errno(errno, "Failed to unlock tty: %m");
-
- slave = open_terminal(pty_path, O_RDWR|O_NOCTTY|O_CLOEXEC);
- if (slave < 0)
- return log_error_errno(slave, "Failed to open pty slave: %m");
-
} else if (arg_transport == BUS_TRANSPORT_MACHINE) {
_cleanup_(sd_bus_unrefp) sd_bus *system_bus = NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *pty_reply = NULL;
@@ -1965,18 +1958,25 @@ static int start_transient_service(sd_bus *bus) {
if (r < 0)
return log_error_errno(r, "Failed to get machine PTY: %s", bus_error_message(&error, r));
- r = sd_bus_message_read(pty_reply, "hs", &master, &s);
+ r = sd_bus_message_read(pty_reply, "hs", &pty_fd, &s);
if (r < 0)
return bus_log_parse_error(r);
- master = fcntl(master, F_DUPFD_CLOEXEC, 3);
- if (master < 0)
+ pty_fd = fcntl(pty_fd, F_DUPFD_CLOEXEC, 3);
+ if (pty_fd < 0)
return log_error_errno(errno, "Failed to duplicate master fd: %m");
pty_path = strdup(s);
if (!pty_path)
return log_oom();
+ peer_fd = pty_open_peer_racefree(pty_fd, O_RDWR|O_NOCTTY|O_CLOEXEC);
+ if (ERRNO_IS_NEG_NOT_SUPPORTED(peer_fd))
+ log_debug_errno(r, "TIOCGPTPEER ioctl not available, falling back to race-ful PTY peer opening: %m");
+ /* We do not open the peer_fd in this case, we let systemd on the remote side open it instead */
+ else if (peer_fd < 0)
+ return log_debug_errno(peer_fd, "Failed to open PTY peer: %m");
+
// FIXME: Introduce OpenMachinePTYEx() that accepts ownership/permission as param
// and additionally returns the pty fd, for #33216 and #32999
} else
@@ -2005,10 +2005,10 @@ static int start_transient_service(sd_bus *bus) {
return r;
}
- r = make_transient_service_unit(bus, &m, service, pty_path, slave);
+ r = make_transient_service_unit(bus, &m, service, pty_path, peer_fd);
if (r < 0)
return r;
- slave = safe_close(slave);
+ peer_fd = safe_close(peer_fd);
r = bus_call_with_hint(bus, m, "service", &reply);
if (r < 0)
@@ -2066,13 +2066,13 @@ static int start_transient_service(sd_bus *bus) {
if (!c.bus_path)
return log_oom();
- if (master >= 0) {
+ if (pty_fd >= 0) {
(void) sd_event_set_signal_exit(c.event, true);
if (!arg_quiet)
log_info("Press ^] three times within 1s to disconnect TTY.");
- r = pty_forward_new(c.event, master, PTY_FORWARD_IGNORE_INITIAL_VHANGUP, &c.forward);
+ r = pty_forward_new(c.event, pty_fd, PTY_FORWARD_IGNORE_INITIAL_VHANGUP, &c.forward);
if (r < 0)
return log_error_errno(r, "Failed to create PTY forwarder: %m");
diff --git a/src/shared/ask-password-api.c b/src/shared/ask-password-api.c
index 7b9cdadc54..0c3156cd27 100644
--- a/src/shared/ask-password-api.c
+++ b/src/shared/ask-password-api.c
@@ -22,6 +22,7 @@
#include "ansi-color.h"
#include "ask-password-api.h"
#include "creds-util.h"
+#include "env-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "format-util.h"
@@ -113,6 +114,28 @@ static int touch_ask_password_directory(AskPasswordFlags flags) {
return 1; /* did something */
}
+static usec_t keyring_cache_timeout(void) {
+ static usec_t saved_timeout = USEC_INFINITY;
+ static bool saved_timeout_set = false;
+ int r;
+
+ if (saved_timeout_set)
+ return saved_timeout;
+
+ const char *e = secure_getenv("SYSTEMD_ASK_PASSWORD_KEYRING_TIMEOUT_SEC");
+ if (streq_ptr(e, "default"))
+ saved_timeout = KEYRING_TIMEOUT_USEC;
+ else if (e) {
+ r = parse_sec(e, &saved_timeout);
+ if (r < 0)
+ log_debug_errno(r, "Invalid value in $SYSTEMD_ASK_PASSWORD_KEYRING_TIMEOUT_SEC, ignoring: %s", e);
+ }
+
+ saved_timeout_set = true;
+
+ return saved_timeout;
+}
+
static int add_to_keyring(const char *keyname, AskPasswordFlags flags, char **passwords) {
_cleanup_strv_free_erase_ char **l = NULL;
_cleanup_(erase_and_freep) char *p = NULL;
@@ -122,7 +145,7 @@ static int add_to_keyring(const char *keyname, AskPasswordFlags flags, char **pa
assert(keyname);
- if (!FLAGS_SET(flags, ASK_PASSWORD_PUSH_CACHE))
+ if (!FLAGS_SET(flags, ASK_PASSWORD_PUSH_CACHE) || keyring_cache_timeout() == 0)
return 0;
if (strv_isempty(passwords))
return 0;
@@ -151,9 +174,10 @@ static int add_to_keyring(const char *keyname, AskPasswordFlags flags, char **pa
if (serial == -1)
return -errno;
- if (keyctl(KEYCTL_SET_TIMEOUT,
- (unsigned long) serial,
- (unsigned long) DIV_ROUND_UP(KEYRING_TIMEOUT_USEC, USEC_PER_SEC), 0, 0) < 0)
+ if (keyring_cache_timeout() != USEC_INFINITY &&
+ keyctl(KEYCTL_SET_TIMEOUT,
+ (unsigned long) serial,
+ (unsigned long) DIV_ROUND_UP(keyring_cache_timeout(), USEC_PER_SEC), 0, 0) < 0)
log_debug_errno(errno, "Failed to adjust kernel keyring key timeout: %m");
/* Tell everyone to check the keyring */
diff --git a/src/shared/bootspec.c b/src/shared/bootspec.c
index 87aa11ccdf..4c0195a41e 100644
--- a/src/shared/bootspec.c
+++ b/src/shared/bootspec.c
@@ -1936,7 +1936,7 @@ int show_boot_entries(const BootConfig *config, sd_json_format_flags_t json_form
assert(config);
- if (!FLAGS_SET(json_format, SD_JSON_FORMAT_OFF)) {
+ if (sd_json_format_enabled(json_format)) {
_cleanup_(sd_json_variant_unrefp) sd_json_variant *array = NULL;
for (size_t i = 0; i < config->n_entries; i++) {
diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c
index 59e4901878..90b6f233e2 100644
--- a/src/shared/bus-unit-util.c
+++ b/src/shared/bus-unit-util.c
@@ -1047,6 +1047,7 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
"ProtectHome",
"PrivateTmpEx",
"PrivateUsersEx",
+ "ProtectControlGroupsEx",
"SELinuxContext",
"RootImage",
"RootVerity",
@@ -2126,7 +2127,7 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
}
if (STR_IN_SET(field, "StateDirectory", "RuntimeDirectory", "CacheDirectory", "LogsDirectory")) {
- _cleanup_strv_free_ char **symlinks = NULL, **sources = NULL;
+ _cleanup_strv_free_ char **symlinks = NULL, **symlinks_ro = NULL, **sources = NULL, **sources_ro = NULL;
const char *p = eq;
/* Adding new directories is supported from both *DirectorySymlink methods and the
@@ -2134,7 +2135,7 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
* tuple use the new method, else use the old one. */
for (;;) {
- _cleanup_free_ char *tuple = NULL, *source = NULL, *destination = NULL;
+ _cleanup_free_ char *tuple = NULL, *source = NULL, *dest = NULL, *flags = NULL;
r = extract_first_word(&p, &tuple, NULL, EXTRACT_UNQUOTE);
if (r < 0)
@@ -2143,20 +2144,31 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
break;
const char *t = tuple;
- r = extract_many_words(&t, ":", EXTRACT_UNQUOTE|EXTRACT_DONT_COALESCE_SEPARATORS, &source, &destination);
+ r = extract_many_words(&t, ":", EXTRACT_UNQUOTE|EXTRACT_DONT_COALESCE_SEPARATORS, &source, &dest, &flags);
if (r <= 0)
return log_error_errno(r ?: SYNTHETIC_ERRNO(EINVAL), "Failed to parse argument: %m");
path_simplify(source);
- if (isempty(destination)) {
+ if (isempty(dest) && isempty(flags)) {
r = strv_consume(&sources, TAKE_PTR(source));
if (r < 0)
return bus_log_create_error(r);
+ } else if (isempty(flags)) {
+ path_simplify(dest);
+ r = strv_consume_pair(&symlinks, TAKE_PTR(source), TAKE_PTR(dest));
+ if (r < 0)
+ return log_oom();
} else {
- path_simplify(destination);
-
- r = strv_consume_pair(&symlinks, TAKE_PTR(source), TAKE_PTR(destination));
+ ExecDirectoryFlags exec_directory_flags = exec_directory_flags_from_string(flags);
+ if (exec_directory_flags < 0 || (exec_directory_flags & ~_EXEC_DIRECTORY_FLAGS_PUBLIC) != 0)
+ return log_error_errno(r, "Failed to parse flags: %s", flags);
+
+ if (!isempty(dest)) {
+ path_simplify(dest);
+ r = strv_consume_pair(&symlinks_ro, TAKE_PTR(source), TAKE_PTR(dest));
+ } else
+ r = strv_consume(&sources_ro, TAKE_PTR(source));
if (r < 0)
return log_oom();
}
@@ -2191,7 +2203,7 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
/* For State and Runtime directories we support an optional destination parameter, which
* will be used to create a symlink to the source. But it is new so we cannot change the
* old DBUS signatures, so append a new message type. */
- if (!strv_isempty(symlinks)) {
+ if (!strv_isempty(symlinks) || !strv_isempty(symlinks_ro) || !strv_isempty(sources_ro)) {
const char *symlink_field;
r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
@@ -2227,6 +2239,18 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
return bus_log_create_error(r);
}
+ STRV_FOREACH_PAIR(source, destination, symlinks_ro) {
+ r = sd_bus_message_append(m, "(sst)", *source, *destination, (uint64_t) EXEC_DIRECTORY_READ_ONLY);
+ if (r < 0)
+ return bus_log_create_error(r);
+ }
+
+ STRV_FOREACH(source, sources_ro) {
+ r = sd_bus_message_append(m, "(sst)", *source, "", (uint64_t) EXEC_DIRECTORY_READ_ONLY);
+ 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);
@@ -3077,3 +3101,13 @@ int unit_freezer_freeze(UnitFreezer *f) {
int unit_freezer_thaw(UnitFreezer *f) {
return unit_freezer_action(f, false);
}
+
+ExecDirectoryFlags exec_directory_flags_from_string(const char *s) {
+ if (isempty(s))
+ return 0;
+
+ if (streq(s, "ro"))
+ return EXEC_DIRECTORY_READ_ONLY;
+
+ return _EXEC_DIRECTORY_FLAGS_INVALID;
+}
diff --git a/src/shared/bus-unit-util.h b/src/shared/bus-unit-util.h
index 6f0d55971c..8090b36e3d 100644
--- a/src/shared/bus-unit-util.h
+++ b/src/shared/bus-unit-util.h
@@ -7,6 +7,16 @@
#include "pidref.h"
#include "unit-def.h"
+typedef enum ExecDirectoryFlags {
+ EXEC_DIRECTORY_READ_ONLY = 1 << 0, /* Public API via DBUS, do not change */
+ EXEC_DIRECTORY_ONLY_CREATE = 1 << 1, /* Only the private directory will be created, not the symlink to it */
+ _EXEC_DIRECTORY_FLAGS_MAX,
+ _EXEC_DIRECTORY_FLAGS_PUBLIC = EXEC_DIRECTORY_READ_ONLY,
+ _EXEC_DIRECTORY_FLAGS_INVALID = -EINVAL,
+} ExecDirectoryFlags;
+
+ExecDirectoryFlags exec_directory_flags_from_string(const char *s) _pure_;
+
typedef struct UnitInfo {
const char *machine;
const char *id;
diff --git a/src/shared/dns-domain.c b/src/shared/dns-domain.c
index 31f6db5137..e91284177c 100644
--- a/src/shared/dns-domain.c
+++ b/src/shared/dns-domain.c
@@ -480,17 +480,17 @@ finish:
return 0;
}
-void dns_name_hash_func(const char *p, struct siphash *state) {
+void dns_name_hash_func(const char *name, struct siphash *state) {
int r;
- assert(p);
+ assert(name);
- for (;;) {
+ for (const char *p = name;;) {
char label[DNS_LABEL_MAX+1];
r = dns_label_unescape(&p, label, sizeof label, 0);
if (r < 0)
- break;
+ return string_hash_func(p, state); /* fallback for invalid DNS names */
if (r == 0)
break;
@@ -516,13 +516,13 @@ int dns_name_compare_func(const char *a, const char *b) {
for (;;) {
char la[DNS_LABEL_MAX+1], lb[DNS_LABEL_MAX+1];
- if (x == NULL && y == NULL)
+ if (!x && !y)
return 0;
r = dns_label_unescape_suffix(a, &x, la, sizeof(la));
q = dns_label_unescape_suffix(b, &y, lb, sizeof(lb));
if (r < 0 || q < 0)
- return CMP(r, q);
+ return strcmp(a, b); /* if not valid DNS labels, then let's compare the whole strings as is */
r = ascii_strcasecmp_nn(la, r, lb, q);
if (r != 0)
diff --git a/src/shared/fdset.c b/src/shared/fdset.c
index b3d3cfa784..42092ada25 100644
--- a/src/shared/fdset.c
+++ b/src/shared/fdset.c
@@ -22,7 +22,7 @@
#define MAKE_SET(s) ((Set*) s)
#define MAKE_FDSET(s) ((FDSet*) s)
-FDSet *fdset_new(void) {
+FDSet* fdset_new(void) {
return MAKE_FDSET(set_new(NULL));
}
@@ -52,12 +52,20 @@ int fdset_new_array(FDSet **ret, const int fds[], size_t n_fds) {
return 0;
}
-void fdset_close(FDSet *s, bool async) {
+int fdset_steal_first(FDSet *fds) {
void *p;
- while ((p = set_steal_first(MAKE_SET(s)))) {
- int fd = PTR_TO_FD(p);
+ p = set_steal_first(MAKE_SET(fds));
+ if (!p)
+ return -ENOENT;
+
+ return PTR_TO_FD(p);
+}
+
+void fdset_close(FDSet *fds, bool async) {
+ int fd;
+ while ((fd = fdset_steal_first(fds)) >= 0) {
/* Valgrind's fd might have ended up in this set here, due to fdset_new_fill(). We'll ignore
* all failures here, so that the EBADFD that valgrind will return us on close() doesn't
* influence us */
@@ -144,7 +152,7 @@ bool fdset_contains(FDSet *s, int fd) {
return false;
}
- return !!set_get(MAKE_SET(s), FD_TO_PTR(fd));
+ return set_contains(MAKE_SET(s), FD_TO_PTR(fd));
}
int fdset_remove(FDSet *s, int fd) {
@@ -230,13 +238,13 @@ int fdset_new_fill(
}
int fdset_cloexec(FDSet *fds, bool b) {
- void *p;
int r;
assert(fds);
- SET_FOREACH(p, MAKE_SET(fds)) {
- r = fd_cloexec(PTR_TO_FD(p), b);
+ int fd;
+ FDSET_FOREACH(fd, fds) {
+ r = fd_cloexec(fd, b);
if (r < 0)
return r;
}
@@ -269,7 +277,6 @@ int fdset_new_listen_fds(FDSet **ret, bool unset) {
int fdset_to_array(FDSet *fds, int **ret) {
unsigned j = 0, m;
- void *e;
int *a;
assert(ret);
@@ -286,8 +293,9 @@ int fdset_to_array(FDSet *fds, int **ret) {
if (!a)
return -ENOMEM;
- SET_FOREACH(e, MAKE_SET(fds))
- a[j++] = PTR_TO_FD(e);
+ int fd;
+ FDSET_FOREACH(fd, fds)
+ a[j++] = fd;
assert(j == m);
@@ -322,13 +330,3 @@ int fdset_iterate(FDSet *s, Iterator *i) {
return PTR_TO_FD(p);
}
-
-int fdset_steal_first(FDSet *fds) {
- void *p;
-
- p = set_steal_first(MAKE_SET(fds));
- if (!p)
- return -ENOENT;
-
- return PTR_TO_FD(p);
-}
diff --git a/src/shared/format-table.c b/src/shared/format-table.c
index f5ef7175a3..5f05247438 100644
--- a/src/shared/format-table.c
+++ b/src/shared/format-table.c
@@ -3124,7 +3124,7 @@ int table_print_json(Table *t, FILE *f, sd_json_format_flags_t flags) {
assert(t);
- if (flags & SD_JSON_FORMAT_OFF) /* If JSON output is turned off, use regular output */
+ if (!sd_json_format_enabled(flags)) /* If JSON output is turned off, use regular output */
return table_print(t, f);
if (!f)
diff --git a/src/shared/journal-importer.c b/src/shared/journal-importer.c
index 4dc0b8f662..9264638303 100644
--- a/src/shared/journal-importer.c
+++ b/src/shared/journal-importer.c
@@ -33,7 +33,7 @@ void journal_importer_cleanup(JournalImporter *imp) {
free(imp->name);
free(imp->buf);
- iovw_free_contents(&imp->iovw, false);
+ iovw_done(&imp->iovw);
}
static char* realloc_buffer(JournalImporter *imp, size_t size) {
@@ -452,7 +452,7 @@ void journal_importer_drop_iovw(JournalImporter *imp) {
/* This function drops processed data that along with the iovw that points at it */
- iovw_free_contents(&imp->iovw, false);
+ iovw_done(&imp->iovw);
/* possibly reset buffer position */
remain = imp->filled - imp->offset;
diff --git a/src/shared/pretty-print.c b/src/shared/pretty-print.c
index 0360fa9d72..4e38e60775 100644
--- a/src/shared/pretty-print.c
+++ b/src/shared/pretty-print.c
@@ -1,8 +1,9 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#include <sys/utsname.h>
#include <errno.h>
+#include <math.h>
#include <stdio.h>
+#include <sys/utsname.h>
#include "alloc-util.h"
#include "color-util.h"
@@ -87,7 +88,9 @@ int terminal_urlify(const char *url, const char *text, char **ret) {
text = url;
if (urlify_enabled())
- n = strjoin("\x1B]8;;", url, "\a", text, "\x1B]8;;\a");
+ n = strjoin(ANSI_OSC "8;;", url, ANSI_ST,
+ text,
+ ANSI_OSC "8;;" ANSI_ST);
else
n = strdup(text);
if (!n)
@@ -460,7 +463,7 @@ bool shall_tint_background(void) {
return cache != 0;
}
-void draw_progress_bar_impl(const char *prefix, double percentage) {
+void draw_progress_bar_unbuffered(const char *prefix, double percentage) {
fputc('\r', stderr);
if (prefix) {
fputs(prefix, stderr);
@@ -468,6 +471,14 @@ void draw_progress_bar_impl(const char *prefix, double percentage) {
}
if (!terminal_is_dumb()) {
+ /* Generate the Windows Terminal progress indication OSC sequence here. Most Linux terminals currently
+ * ignore this. But let's hope this changes one day. For details about this OSC sequence, see:
+ *
+ * https://conemu.github.io/en/AnsiEscapeCodes.html#ConEmu_specific_OSC
+ * https://github.com/microsoft/terminal/pull/8055
+ */
+ fprintf(stderr, ANSI_OSC "9;4;1;%u" ANSI_ST, (unsigned) ceil(percentage));
+
size_t cols = columns();
size_t prefix_width = utf8_console_width(prefix) + 1 /* space */;
size_t length = cols > prefix_width + 6 ? cols - prefix_width - 6 : 0;
@@ -513,10 +524,9 @@ void draw_progress_bar_impl(const char *prefix, double percentage) {
fputs(ANSI_ERASE_TO_END_OF_LINE, stderr);
fputc('\r', stderr);
-
}
-void clear_progress_bar_impl(const char *prefix) {
+void clear_progress_bar_unbuffered(const char *prefix) {
fputc('\r', stderr);
if (terminal_is_dumb())
@@ -525,7 +535,9 @@ void clear_progress_bar_impl(const char *prefix) {
LESS_BY(columns(), 1U)),
stderr);
else
- fputs(ANSI_ERASE_TO_END_OF_LINE, stderr);
+ /* Undo Windows Terminal progress indication again. */
+ fputs(ANSI_OSC "9;4;0;;" ANSI_ST
+ ANSI_ERASE_TO_END_OF_LINE, stderr);
fputc('\r', stderr);
}
@@ -535,10 +547,26 @@ void draw_progress_bar(const char *prefix, double percentage) {
* unbuffered by default. Let's temporarily turn on full buffering, so that this is passed to the tty
* as a single buffer, to make things more efficient. */
WITH_BUFFERED_STDERR;
- draw_progress_bar_impl(prefix, percentage);
+ draw_progress_bar_unbuffered(prefix, percentage);
+}
+
+int draw_progress_barf(double percentage, const char *prefixf, ...) {
+ _cleanup_free_ char *s = NULL;
+ va_list ap;
+ int r;
+
+ va_start(ap, prefixf);
+ r = vasprintf(&s, prefixf, ap);
+ va_end(ap);
+
+ if (r < 0)
+ return -ENOMEM;
+
+ draw_progress_bar(s, percentage);
+ return 0;
}
void clear_progress_bar(const char *prefix) {
WITH_BUFFERED_STDERR;
- clear_progress_bar_impl(prefix);
+ clear_progress_bar_unbuffered(prefix);
}
diff --git a/src/shared/pretty-print.h b/src/shared/pretty-print.h
index 446f305b73..8ea1e964a3 100644
--- a/src/shared/pretty-print.h
+++ b/src/shared/pretty-print.h
@@ -54,9 +54,10 @@ int terminal_tint_color(double hue, char **ret);
bool shall_tint_background(void);
void draw_progress_bar(const char *prefix, double percentage);
+int draw_progress_barf(double percentage, const char *prefixf, ...) _printf_(2, 3);
void clear_progress_bar(const char *prefix);
-void draw_progress_bar_impl(const char *prefix, double percentage);
-void clear_progress_bar_impl(const char *prefix);
+void draw_progress_bar_unbuffered(const char *prefix, double percentage);
+void clear_progress_bar_unbuffered(const char *prefix);
static inline FILE* enable_buffering(FILE *f, char *buffer, size_t size) {
assert(f);
diff --git a/src/shared/ptyfwd.c b/src/shared/ptyfwd.c
index c06c279d87..c1e3b7180b 100644
--- a/src/shared/ptyfwd.c
+++ b/src/shared/ptyfwd.c
@@ -397,7 +397,7 @@ static int insert_window_title_fix(PTYForward *f, size_t offset) {
if (!t)
return 0;
- _cleanup_free_ char *joined = strjoin("\x1b]0;", f->title_prefix, t, "\a");
+ _cleanup_free_ char *joined = strjoin(ANSI_OSC "0;", f->title_prefix, t, ANSI_ST);
if (!joined)
return -ENOMEM;
@@ -507,11 +507,16 @@ static int pty_forward_ansi_process(PTYForward *f, size_t offset) {
} else {
/* Otherwise, the OSC sequence is over
*
- * There are two allowed ways to end an OSC sequence:
- * BEL '\x07'
- * String Terminator (ST): <Esc>\ - "\x1b\x5c"
- * since we cannot lookahead to see if the Esc is followed by a \
- * we cut a corner here and assume it will be \. */
+ * There are three documented ways to end an OSC sequence:
+ * 1. BEL aka ^G aka \x07
+ * 2. \x9c
+ * 3. \x1b\x5c
+ * since we cannot look ahead to see if the Esc is followed by a "\"
+ * we cut a corner here and assume it will be "\"e.
+ *
+ * Note that we do not support \x9c here, because that's also a valid UTF8
+ * codepoint, and that would create ambiguity. Various terminal emulators
+ * similar do not support it. */
if (IN_SET(c, '\x07', '\x1b')) {
r = insert_window_title_fix(f, i+1);
@@ -567,7 +572,7 @@ static int do_shovel(PTYForward *f) {
if (f->title) {
if (!strextend(&f->out_buffer,
ANSI_WINDOW_TITLE_PUSH
- "\x1b]2;", f->title, "\a"))
+ ANSI_OSC "2;", f->title, ANSI_ST))
return log_oom();
}
diff --git a/src/shared/qrcode-util.c b/src/shared/qrcode-util.c
index 36cd5dcd7c..da88d80d03 100644
--- a/src/shared/qrcode-util.c
+++ b/src/shared/qrcode-util.c
@@ -173,36 +173,49 @@ static void write_qrcode(FILE *output, QRcode *qr, unsigned int row, unsigned in
fflush(output);
}
-int print_qrcode_full(FILE *out, const char *header, const char *string, unsigned row, unsigned column, unsigned tty_width, unsigned tty_height) {
- QRcode* qr;
+DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(QRcode*, sym_QRcode_free, NULL);
+
+int print_qrcode_full(
+ FILE *out,
+ const char *header,
+ const char *string,
+ unsigned row,
+ unsigned column,
+ unsigned tty_width,
+ unsigned tty_height,
+ bool check_tty) {
+
int r;
/* If this is not a UTF-8 system or ANSI colors aren't supported/disabled don't print any QR
* codes */
- if (!is_locale_utf8() || !colors_enabled())
- return -EOPNOTSUPP;
+ if (!is_locale_utf8())
+ return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Not an UTF-8 system, cannot print qrcode");
+ if (check_tty && !colors_enabled())
+ return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Colors are disabled, cannot print qrcode");
r = dlopen_qrencode();
if (r < 0)
return r;
- qr = sym_QRcode_encodeString(string, 0, QR_ECLEVEL_L, QR_MODE_8, 1);
+ _cleanup_(sym_QRcode_freep) QRcode *qr =
+ sym_QRcode_encodeString(string, 0, QR_ECLEVEL_L, QR_MODE_8, 1);
if (!qr)
- return -ENOMEM;
+ return log_oom_debug();
if (row != UINT_MAX && column != UINT_MAX) {
- int fd;
unsigned qr_code_width, qr_code_height;
- fd = fileno(out);
+
+ int fd = fileno(out);
if (fd < 0)
return log_debug_errno(errno, "Failed to get file descriptor from the file stream: %m");
- qr_code_width = qr_code_height = qr->width + 8;
+ qr_code_width = qr_code_height = qr->width + 8;
if (column + qr_code_width > tty_width)
column = tty_width - qr_code_width;
/* Terminal characters are twice as high as they are wide so it's qr_code_height / 2,
- * our QR code prints an extra new line, so we have -1 as well */
+ * our QR code prints an extra new line, so we have -1 as well */
if (row + qr_code_height > tty_height)
row = tty_height - (qr_code_height / 2 ) - 1;
@@ -218,10 +231,8 @@ int print_qrcode_full(FILE *out, const char *header, const char *string, unsigne
fprintf(out, "\n%s:\n\n", header);
write_qrcode(out, qr, row, column);
-
fputc('\n', out);
- sym_QRcode_free(qr);
return 0;
}
#endif
diff --git a/src/shared/qrcode-util.h b/src/shared/qrcode-util.h
index ee58294436..89a15bb3f5 100644
--- a/src/shared/qrcode-util.h
+++ b/src/shared/qrcode-util.h
@@ -1,6 +1,7 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
-
#pragma once
+
+#include <stdbool.h>
#include <stdio.h>
#include <errno.h>
#include <limits.h>
@@ -8,15 +9,29 @@
#if HAVE_QRENCODE
int dlopen_qrencode(void);
-int print_qrcode_full(FILE *out, const char *header, const char *string, unsigned row, unsigned column, unsigned tty_width, unsigned tty_height);
-static inline int print_qrcode(FILE *out, const char *header, const char *string) {
- return print_qrcode_full(out, header, string, UINT_MAX, UINT_MAX, UINT_MAX, UINT_MAX);
-}
+int print_qrcode_full(
+ FILE *out,
+ const char *header,
+ const char *string,
+ unsigned row,
+ unsigned column,
+ unsigned tty_width,
+ unsigned tty_height,
+ bool check_tty);
#else
-static inline int print_qrcode_full(FILE *out, const char *header, const char *string, unsigned row, unsigned column, unsigned tty_width, unsigned tty_height) {
+static inline int print_qrcode_full(
+ FILE *out,
+ const char *header,
+ const char *string,
+ unsigned row,
+ unsigned column,
+ unsigned tty_width,
+ unsigned tty_height,
+ bool check_tty) {
return -EOPNOTSUPP;
}
+#endif
+
static inline int print_qrcode(FILE *out, const char *header, const char *string) {
- return -EOPNOTSUPP;
+ return print_qrcode_full(out, header, string, UINT_MAX, UINT_MAX, UINT_MAX, UINT_MAX, true);
}
-#endif
diff --git a/src/shared/user-record-show.c b/src/shared/user-record-show.c
index 45ef48bab6..e6de0cd002 100644
--- a/src/shared/user-record-show.c
+++ b/src/shared/user-record-show.c
@@ -28,6 +28,25 @@ const char* user_record_state_color(const char *state) {
return NULL;
}
+static void dump_self_modifiable(const char *heading, char **field, const char **value) {
+ assert(heading);
+
+ /* Helper function for printing the various self_modifiable_* fields from the user record */
+
+ if (strv_isempty((char**) value))
+ /* Case 1: the array is explicitly set to be empty by the administrator */
+ printf("%13s %sDisabled by Administrator%s\n", heading, ansi_highlight_red(), ansi_normal());
+ else if (!field)
+ /* Case 2: we have values, but the field is NULL. This means that we're using the defaults.
+ * We list them anyways, because they're security-sensitive to the administrator */
+ STRV_FOREACH(i, value)
+ printf("%13s %s%s%s\n", i == value ? heading : "", ansi_grey(), *i, ansi_normal());
+ else
+ /* Case 3: we have a list provided by the administrator */
+ STRV_FOREACH(i, value)
+ printf("%13s %s\n", i == value ? heading : "", *i);
+}
+
void user_record_show(UserRecord *hr, bool show_full_group_info) {
_cleanup_strv_free_ char **langs = NULL;
const char *hd, *ip, *shell;
@@ -585,6 +604,16 @@ void user_record_show(UserRecord *hr, bool show_full_group_info) {
if (hr->service)
printf(" Service: %s\n", hr->service);
+
+ dump_self_modifiable("Self Modify:",
+ hr->self_modifiable_fields,
+ user_record_self_modifiable_fields(hr));
+ dump_self_modifiable("(Blobs)",
+ hr->self_modifiable_blobs,
+ user_record_self_modifiable_blobs(hr));
+ dump_self_modifiable("(Privileged)",
+ hr->self_modifiable_privileged,
+ user_record_self_modifiable_privileged(hr));
}
void group_record_show(GroupRecord *gr, bool show_full_user_info) {
diff --git a/src/shared/user-record.c b/src/shared/user-record.c
index b03a38eb18..47fd5cc311 100644
--- a/src/shared/user-record.c
+++ b/src/shared/user-record.c
@@ -207,6 +207,10 @@ static UserRecord* user_record_free(UserRecord *h) {
for (size_t i = 0; i < h->n_recovery_key; i++)
recovery_key_done(h->recovery_key + i);
+ strv_free(h->self_modifiable_fields);
+ strv_free(h->self_modifiable_blobs);
+ strv_free(h->self_modifiable_privileged);
+
sd_json_variant_unref(h->json);
return mfree(h);
@@ -1300,6 +1304,9 @@ static int dispatch_per_machine(const char *name, sd_json_variant *variant, sd_j
{ "passwordChangeNow", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_tristate, offsetof(UserRecord, password_change_now), 0 },
{ "pkcs11TokenUri", SD_JSON_VARIANT_ARRAY, dispatch_pkcs11_uri_array, offsetof(UserRecord, pkcs11_token_uri), 0 },
{ "fido2HmacCredential", SD_JSON_VARIANT_ARRAY, dispatch_fido2_hmac_credential_array, 0, 0 },
+ { "selfModifiableFields", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_strv, offsetof(UserRecord, self_modifiable_fields), SD_JSON_STRICT },
+ { "selfModifiableBlobs", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_strv, offsetof(UserRecord, self_modifiable_blobs), SD_JSON_STRICT },
+ { "selfModifiablePrivileged", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_strv, offsetof(UserRecord, self_modifiable_privileged), SD_JSON_STRICT },
{},
};
@@ -1468,7 +1475,6 @@ int user_group_record_mangle(
assert(v);
assert(ret_variant);
- assert(ret_mask);
/* Note that this function is shared with the group record parser, hence we try to be generic in our
* log message wording here, to cover both cases. */
@@ -1556,7 +1562,8 @@ int user_group_record_mangle(
else
*ret_variant = sd_json_variant_ref(v);
- *ret_mask = m;
+ if (ret_mask)
+ *ret_mask = m;
return 0;
}
@@ -1646,6 +1653,9 @@ int user_record_load(UserRecord *h, sd_json_variant *v, UserRecordLoadFlags load
{ "pkcs11TokenUri", SD_JSON_VARIANT_ARRAY, dispatch_pkcs11_uri_array, offsetof(UserRecord, pkcs11_token_uri), 0 },
{ "fido2HmacCredential", SD_JSON_VARIANT_ARRAY, dispatch_fido2_hmac_credential_array, 0, 0 },
{ "recoveryKeyType", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_strv, offsetof(UserRecord, recovery_key_type), 0 },
+ { "selfModifiableFields", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_strv, offsetof(UserRecord, self_modifiable_fields), SD_JSON_STRICT },
+ { "selfModifiableBlobs", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_strv, offsetof(UserRecord, self_modifiable_blobs), SD_JSON_STRICT },
+ { "selfModifiablePrivileged", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_strv, offsetof(UserRecord, self_modifiable_privileged), SD_JSON_STRICT },
{ "secret", SD_JSON_VARIANT_OBJECT, dispatch_secret, 0, 0 },
{ "privileged", SD_JSON_VARIANT_OBJECT, dispatch_privileged, 0, 0 },
@@ -2156,6 +2166,244 @@ int user_record_languages(UserRecord *h, char ***ret) {
return 0;
}
+const char** user_record_self_modifiable_fields(UserRecord *h) {
+ /* As a rule of thumb: a setting is safe if it cannot be used by a
+ * user to give themselves some unfair advantage over other users on
+ * a given system. */
+ static const char *const default_fields[] = {
+ /* For display purposes */
+ "realName",
+ "emailAddress", /* Just the $EMAIL env var */
+ "iconName",
+ "location",
+
+ /* Basic account settings */
+ "shell",
+ "umask",
+ "environment",
+ "timeZone",
+ "preferredLanguage",
+ "additionalLanguages",
+ "preferredSessionLauncher",
+ "preferredSessionType",
+
+ /* Authentication methods */
+ "pkcs11TokenUri",
+ "fido2HmacCredential",
+ "recoveryKeyType",
+
+ "lastChangeUSec", /* Necessary to be able to change record at all */
+ "lastPasswordChangeUSec", /* Ditto, but for authentication methods */
+ NULL
+ };
+
+ assert(h);
+
+ /* Note that we intentionally distinguish between NULL and an empty array here */
+ return (const char**) h->self_modifiable_fields ?: (const char**) default_fields;
+}
+
+const char** user_record_self_modifiable_blobs(UserRecord *h) {
+ static const char *const default_blobs[] = {
+ /* For display purposes */
+ "avatar",
+ "login-background",
+ NULL
+ };
+
+ assert(h);
+
+ /* Note that we intentionally distinguish between NULL and an empty array here */
+ return (const char**) h->self_modifiable_blobs ?: (const char**) default_blobs;
+}
+
+const char** user_record_self_modifiable_privileged(UserRecord *h) {
+ static const char *const default_fields[] = {
+ /* For display purposes */
+ "passwordHint",
+
+ /* Authentication methods */
+ "hashedPassword"
+ "pkcs11EncryptedKey",
+ "fido2HmacSalt",
+ "recoveryKey",
+
+ "sshAuthorizedKeys", /* Basically just ~/.ssh/authorized_keys */
+ NULL
+ };
+
+ assert(h);
+
+ /* Note that we intentionally distinguish between NULL and an empty array here */
+ return (const char**) h->self_modifiable_privileged ?: (const char**) default_fields;
+}
+
+static int remove_self_modifiable_json_fields_common(UserRecord *current, sd_json_variant **target) {
+ _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL, *blobs = NULL;
+ char **allowed;
+ int r;
+
+ assert(current);
+ assert(target);
+
+ if (!sd_json_variant_is_object(*target))
+ return -EINVAL;
+
+ v = sd_json_variant_ref(*target);
+
+ /* Handle basic fields */
+ allowed = (char**) user_record_self_modifiable_fields(current);
+ r = sd_json_variant_filter(&v, allowed);
+ if (r < 0)
+ return r;
+
+ /* Handle blobs */
+ blobs = sd_json_variant_ref(sd_json_variant_by_key(v, "blobManifest"));
+ if (blobs) {
+ /* The blobManifest contains the sha256 hashes of the blobs,
+ * which are enforced by the service managing the user. So, by
+ * comparing the blob manifests like this, we're actually comparing
+ * the contents of the blob directories & files */
+
+ allowed = (char**) user_record_self_modifiable_blobs(current);
+ r = sd_json_variant_filter(&blobs, allowed);
+ if (r < 0)
+ return r;
+
+ if (sd_json_variant_is_blank_object(blobs))
+ r = sd_json_variant_filter(&v, STRV_MAKE("blobManifest"));
+ else
+ r = sd_json_variant_set_field(&v, "blobManifest", blobs);
+ if (r < 0)
+ return r;
+ }
+
+ JSON_VARIANT_REPLACE(*target, TAKE_PTR(v));
+ return 0;
+}
+
+static int remove_self_modifiable_json_fields(UserRecord *current, UserRecord *h, sd_json_variant **ret) {
+ _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL, *privileged = NULL;
+ sd_json_variant *per_machine;
+ char **allowed;
+ int r;
+
+ assert(current);
+ assert(h);
+ assert(ret);
+
+ r = user_group_record_mangle(h->json, USER_RECORD_EXTRACT_SIGNABLE|USER_RECORD_PERMISSIVE, &v, NULL);
+ if (r < 0)
+ return r;
+
+ /* Handle the regular section */
+ r = remove_self_modifiable_json_fields_common(current, &v);
+ if (r < 0)
+ return r;
+
+ /* Handle the perMachine section */
+ per_machine = sd_json_variant_by_key(v, "perMachine");
+ if (per_machine) {
+ _cleanup_(sd_json_variant_unrefp) sd_json_variant *new_per_machine = NULL;
+ sd_json_variant *e;
+
+ if (!sd_json_variant_is_array(per_machine))
+ return -EINVAL;
+
+ JSON_VARIANT_ARRAY_FOREACH(e, per_machine) {
+ _cleanup_(sd_json_variant_unrefp) sd_json_variant *z = NULL;
+
+ if (!sd_json_variant_is_object(e))
+ return -EINVAL;
+
+ r = per_machine_match(e, 0);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ /* It's only permissible to change anything inside of matching perMachine sections */
+ r = sd_json_variant_append_array(&new_per_machine, e);
+ if (r < 0)
+ return r;
+ continue;
+ }
+
+ z = sd_json_variant_ref(e);
+
+ r = remove_self_modifiable_json_fields_common(current, &z);
+ if (r < 0)
+ return r;
+
+ if (!sd_json_variant_is_blank_object(z)) {
+ r = sd_json_variant_append_array(&new_per_machine, z);
+ if (r < 0)
+ return r;
+ }
+ }
+
+ if (sd_json_variant_is_blank_array(new_per_machine))
+ r = sd_json_variant_filter(&v, STRV_MAKE("perMachine"));
+ else
+ r = sd_json_variant_set_field(&v, "perMachine", new_per_machine);
+ if (r < 0)
+ return r;
+ }
+
+ /* Handle the privileged section */
+ privileged = sd_json_variant_ref(sd_json_variant_by_key(v, "privileged"));
+ if (privileged) {
+ allowed = (char**) user_record_self_modifiable_privileged(current);
+ r = sd_json_variant_filter(&privileged, allowed);
+ if (r < 0)
+ return r;
+
+ if (sd_json_variant_is_blank_object(privileged))
+ r = sd_json_variant_filter(&v, STRV_MAKE("privileged"));
+ else
+ r = sd_json_variant_set_field(&v, "privileged", privileged);
+ if (r < 0)
+ return r;
+ }
+
+ JSON_VARIANT_REPLACE(*ret, TAKE_PTR(v));
+ return 0;
+}
+
+int user_record_self_changes_allowed(UserRecord *current, UserRecord *incoming) {
+ _cleanup_(sd_json_variant_unrefp) sd_json_variant *vc = NULL, *vi = NULL;
+ int r;
+
+ assert(current);
+ assert(incoming);
+
+ /* We remove the fields that the user is allowed to change and then
+ * compare the resulting JSON records. If they are not equal, that
+ * means a disallowed field has been changed and thus we should
+ * require administrator permission to apply the changes. */
+
+ r = remove_self_modifiable_json_fields(current, current, &vc);
+ if (r < 0)
+ return r;
+
+ /* Note that we use `current` as the source of the allowlist, and not
+ * `incoming`. This prevents the user from adding fields. Consider a
+ * scenario that would've been possible if we had messed up this check:
+ *
+ * 1) A user starts out with no group memberships and no custom allowlist.
+ * Thus, this user is not an administrator, and the `memberOf` and
+ * `selfModifiableFields` fields are unset in their record.
+ * 2) This user crafts a request to add the following to their record:
+ * { "memberOf": ["wheel"], "selfModifiableFields": ["memberOf", "selfModifiableFields"] }
+ * 3) We remove the `mebmerOf` and `selfModifiabileFields` fields from `incoming`
+ * 4) `current` and `incoming` compare as equal, so we let the change happen
+ * 5) the user has granted themselves administrator privileges
+ */
+ r = remove_self_modifiable_json_fields(current, incoming, &vi);
+ if (r < 0)
+ return r;
+
+ return sd_json_variant_equal(vc, vi);
+}
+
uint64_t user_record_ratelimit_next_try(UserRecord *h) {
assert(h);
diff --git a/src/shared/user-record.h b/src/shared/user-record.h
index 0443820890..b539b3f55e 100644
--- a/src/shared/user-record.h
+++ b/src/shared/user-record.h
@@ -383,6 +383,10 @@ typedef struct UserRecord {
char **capability_bounding_set;
char **capability_ambient_set;
+ char **self_modifiable_fields; /* fields a user can change about themself w/o auth */
+ char **self_modifiable_blobs;
+ char **self_modifiable_privileged;
+
sd_json_variant *json;
} UserRecord;
@@ -431,6 +435,11 @@ uint64_t user_record_capability_bounding_set(UserRecord *h);
uint64_t user_record_capability_ambient_set(UserRecord *h);
int user_record_languages(UserRecord *h, char ***ret);
+const char **user_record_self_modifiable_fields(UserRecord *h);
+const char **user_record_self_modifiable_blobs(UserRecord *h);
+const char **user_record_self_modifiable_privileged(UserRecord *h);
+int user_record_self_changes_allowed(UserRecord *current, UserRecord *new);
+
int user_record_build_image_path(UserStorage storage, const char *user_name_and_realm, char **ret);
bool user_record_equal(UserRecord *a, UserRecord *b);
diff --git a/src/sysext/sysext.c b/src/sysext/sysext.c
index fcf29a99d3..0adfab253c 100644
--- a/src/sysext/sysext.c
+++ b/src/sysext/sysext.c
@@ -2222,7 +2222,7 @@ static int verb_list(int argc, char **argv, void *userdata) {
if (r < 0)
return log_error_errno(r, "Failed to discover images: %m");
- if ((arg_json_format_flags & SD_JSON_FORMAT_OFF) && hashmap_isempty(images)) {
+ if (hashmap_isempty(images) && !sd_json_format_enabled(arg_json_format_flags)) {
log_info("No OS extensions found.");
return 0;
}
diff --git a/src/systemctl/systemctl-compat-halt.c b/src/systemctl/systemctl-compat-halt.c
index 4f6e304816..9d69230097 100644
--- a/src/systemctl/systemctl-compat-halt.c
+++ b/src/systemctl/systemctl-compat-halt.c
@@ -180,10 +180,9 @@ int halt_main(void) {
return start_with_fallback();
}
- if (geteuid() != 0) {
- (void) must_be_root();
- return -EPERM;
- }
+ r = must_be_root();
+ if (r < 0)
+ return r;
if (!arg_no_wtmp) {
if (sd_booted() > 0)
diff --git a/src/systemctl/systemctl-logind.c b/src/systemctl/systemctl-logind.c
index e4ef7cf915..fc64e60c64 100644
--- a/src/systemctl/systemctl-logind.c
+++ b/src/systemctl/systemctl-logind.c
@@ -204,6 +204,10 @@ int logind_check_inhibitors(enum action a) {
if (r < 0)
return bus_log_parse_error(r);
+ /* root respects inhibitors since v257 but keeps ignoring sessions by default */
+ if (arg_check_inhibitors < 0 && c == 0 && geteuid() == 0)
+ return 0;
+
/* Check for current sessions */
sd_get_sessions(&sessions);
STRV_FOREACH(s, sessions) {
diff --git a/src/systemctl/systemctl-start-special.c b/src/systemctl/systemctl-start-special.c
index 00dd05bff7..5fff5d4f18 100644
--- a/src/systemctl/systemctl-start-special.c
+++ b/src/systemctl/systemctl-start-special.c
@@ -201,14 +201,17 @@ int verb_start_special(int argc, char *argv[], void *userdata) {
case ACTION_KEXEC:
case ACTION_HALT:
case ACTION_SOFT_REBOOT:
- if (arg_when == 0)
+ if (arg_when == 0) {
r = logind_reboot(a);
- else
+ if (r >= 0 || IN_SET(r, -EACCES, -EOPNOTSUPP, -EINPROGRESS))
+ /* The latter indicates that the requested operation requires auth,
+ * is not supported or already in progress, in which cases we ignore the error. */
+ return r;
+ } else {
r = logind_schedule_shutdown(a);
- if (r >= 0 || IN_SET(r, -EACCES, -EOPNOTSUPP, -EINPROGRESS))
- /* The latter indicates that the requested operation requires auth,
- * is not supported or already in progress, in which cases we ignore the error. */
- return r;
+ if (r != -ENOSYS)
+ return r;
+ }
/* On all other errors, try low-level operation. In order to minimize the difference
* between operation with and without logind, we explicitly enable non-blocking mode
diff --git a/src/systemd/_sd-common.h b/src/systemd/_sd-common.h
index dbe9fa035e..5792dd8106 100644
--- a/src/systemd/_sd-common.h
+++ b/src/systemd/_sd-common.h
@@ -45,6 +45,10 @@ typedef void (*_sd_destroy_t)(void *userdata);
# define _sd_pure_ __attribute__((__pure__))
#endif
+#ifndef _sd_const_
+# define _sd_const_ __attribute__((__const__))
+#endif
+
/* Note that strictly speaking __deprecated__ has been available before GCC 6. However, starting with GCC 6
* it also works on enum values, which we are interested in. Since this is a developer-facing feature anyway
* (as opposed to build engineer-facing), let's hence conditionalize this to gcc 6, given that the developers
diff --git a/src/systemd/sd-id128.h b/src/systemd/sd-id128.h
index 7573991c20..7be690400d 100644
--- a/src/systemd/sd-id128.h
+++ b/src/systemd/sd-id128.h
@@ -117,24 +117,24 @@ int sd_id128_get_invocation_app_specific(sd_id128_t app_id, sd_id128_t *ret);
#define SD_ID128_MAKE_UUID_STR(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) \
#a #b #c #d "-" #e #f "-" #g #h "-" #i #j "-" #k #l #m #n #o #p
-_sd_pure_ static __inline__ int sd_id128_equal(sd_id128_t a, sd_id128_t b) {
+_sd_const_ static __inline__ int sd_id128_equal(sd_id128_t a, sd_id128_t b) {
return a.qwords[0] == b.qwords[0] && a.qwords[1] == b.qwords[1];
}
int sd_id128_string_equal(const char *s, sd_id128_t id);
-_sd_pure_ static __inline__ int sd_id128_is_null(sd_id128_t a) {
+_sd_const_ static __inline__ int sd_id128_is_null(sd_id128_t a) {
return a.qwords[0] == 0 && a.qwords[1] == 0;
}
-_sd_pure_ static __inline__ int sd_id128_is_allf(sd_id128_t a) {
+_sd_const_ static __inline__ int sd_id128_is_allf(sd_id128_t a) {
return a.qwords[0] == UINT64_C(0xFFFFFFFFFFFFFFFF) && a.qwords[1] == UINT64_C(0xFFFFFFFFFFFFFFFF);
}
#define SD_ID128_NULL ((const sd_id128_t) { .qwords = { 0, 0 }})
#define SD_ID128_ALLF ((const sd_id128_t) { .qwords = { UINT64_C(0xFFFFFFFFFFFFFFFF), UINT64_C(0xFFFFFFFFFFFFFFFF) }})
-_sd_pure_ static __inline__ int sd_id128_in_setv(sd_id128_t a, va_list ap) {
+_sd_const_ static __inline__ int sd_id128_in_setv(sd_id128_t a, va_list ap) {
for (;;) {
sd_id128_t b = va_arg(ap, sd_id128_t);
@@ -146,7 +146,7 @@ _sd_pure_ static __inline__ int sd_id128_in_setv(sd_id128_t a, va_list ap) {
}
}
-_sd_pure_ static __inline__ int sd_id128_in_set_sentinel(sd_id128_t a, ...) {
+_sd_const_ static __inline__ int sd_id128_in_set_sentinel(sd_id128_t a, ...) {
va_list ap;
int r;
diff --git a/src/systemd/sd-json.h b/src/systemd/sd-json.h
index f3c9a27257..3930d82b0d 100644
--- a/src/systemd/sd-json.h
+++ b/src/systemd/sd-json.h
@@ -342,6 +342,10 @@ int sd_json_variant_unhex(sd_json_variant *v, void **ret, size_t *ret_size);
const char* sd_json_variant_type_to_string(sd_json_variant_type_t t);
sd_json_variant_type_t sd_json_variant_type_from_string(const char *s);
+_sd_const_ static __inline__ int sd_json_format_enabled(sd_json_format_flags_t flags) {
+ return !(flags & SD_JSON_FORMAT_OFF);
+}
+
_SD_END_DECLARATIONS;
#endif
diff --git a/src/systemd/sd-varlink-idl.h b/src/systemd/sd-varlink-idl.h
index e5e8a57498..fe65c00eb6 100644
--- a/src/systemd/sd-varlink-idl.h
+++ b/src/systemd/sd-varlink-idl.h
@@ -18,7 +18,6 @@
***/
#include <errno.h>
-#include <stdbool.h>
#include <stdio.h>
#include "sd-json.h"
@@ -61,7 +60,7 @@ __extension__ typedef enum _SD_ENUM_TYPE_S64(sd_varlink_symbol_flags_t) {
} sd_varlink_symbol_flags_t;
__extension__ typedef enum _SD_ENUM_TYPE_S64(sd_varlink_field_type_t) {
- _SD_VARLINK_FIELD_TYPE_END_MARKER = 0, /* zero type means: this is the last entry in the fields[] array of VarlinkSymbol */
+ _SD_VARLINK_FIELD_TYPE_END_MARKER = 0, /* zero type means: this is the last entry in the fields[] array of sd_varlink_symbol */
SD_VARLINK_STRUCT,
SD_VARLINK_ENUM,
SD_VARLINK_NAMED_TYPE,
diff --git a/src/systemd/sd-varlink.h b/src/systemd/sd-varlink.h
index 4596561ed3..fbf575ffdf 100644
--- a/src/systemd/sd-varlink.h
+++ b/src/systemd/sd-varlink.h
@@ -68,7 +68,7 @@ __extension__ typedef enum _SD_ENUM_TYPE_S64(sd_varlink_server_flags_t) {
SD_VARLINK_SERVER_ROOT_ONLY = 1 << 0, /* Only accessible by root */
SD_VARLINK_SERVER_MYSELF_ONLY = 1 << 1, /* Only accessible by our own UID */
SD_VARLINK_SERVER_ACCOUNT_UID = 1 << 2, /* Do per user accounting */
- SD_VARLINK_SERVER_INHERIT_USERDATA = 1 << 3, /* Initialize Varlink connection userdata from VarlinkServer userdata */
+ SD_VARLINK_SERVER_INHERIT_USERDATA = 1 << 3, /* Initialize Varlink connection userdata from sd_varlink_server userdata */
SD_VARLINK_SERVER_INPUT_SENSITIVE = 1 << 4, /* Automatically mark all connection input as sensitive */
_SD_ENUM_FORCE_S64(SD_VARLINK_SERVER)
} sd_varlink_server_flags_t;
diff --git a/src/sysupdate/meson.build b/src/sysupdate/meson.build
index 0c7c469a62..baf3e93a8d 100644
--- a/src/sysupdate/meson.build
+++ b/src/sysupdate/meson.build
@@ -40,7 +40,7 @@ executables += [
libexec_template + {
'name' : 'systemd-sysupdated',
'dbus' : true,
- 'conditions' : ['ENABLE_SYSUPDATE'],
+ 'conditions' : ['ENABLE_SYSUPDATED'],
'sources' : files('sysupdated.c'),
'dependencies' : threads,
},
@@ -48,11 +48,11 @@ executables += [
'name' : 'updatectl',
'public' : true,
'sources' : systemd_updatectl_sources,
- 'conditions' : ['ENABLE_SYSUPDATE'],
+ 'conditions' : ['ENABLE_SYSUPDATED'],
},
]
-if conf.get('ENABLE_SYSUPDATE') == 1
+if conf.get('ENABLE_SYSUPDATED') == 1
install_data('org.freedesktop.sysupdate1.conf',
install_dir : dbuspolicydir)
install_data('org.freedesktop.sysupdate1.service',
diff --git a/src/sysupdate/sysupdate.c b/src/sysupdate/sysupdate.c
index 40a274b879..2bdd71e742 100644
--- a/src/sysupdate/sysupdate.c
+++ b/src/sysupdate/sysupdate.c
@@ -585,7 +585,7 @@ static int context_show_version(Context *c, const char *version) {
if (arg_json_format_flags & (SD_JSON_FORMAT_OFF|SD_JSON_FORMAT_PRETTY|SD_JSON_FORMAT_PRETTY_AUTO))
(void) pager_open(arg_pager_flags);
- if (FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF))
+ if (!sd_json_format_enabled(arg_json_format_flags))
printf("%s%s%s Version: %s\n"
" State: %s%s%s\n"
"Installed: %s%s\n"
@@ -784,7 +784,7 @@ static int context_show_version(Context *c, const char *version) {
if (!have_sha256)
(void) table_hide_column_from_display(t, 12);
- if (FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF)) {
+ if (!sd_json_format_enabled(arg_json_format_flags)) {
printf("%s%s%s Version: %s\n"
" State: %s%s%s\n"
"Installed: %s%s%s%s%s\n"
@@ -873,7 +873,7 @@ static int context_vacuum(
disabled_count++;
}
- if (FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF)) {
+ if (!sd_json_format_enabled(arg_json_format_flags)) {
if (count > 0 && disabled_count > 0)
log_info("Removed %i instances, and %zu disabled transfers.", count, disabled_count);
else if (count > 0)
@@ -1157,7 +1157,7 @@ static int verb_list(int argc, char **argv, void *userdata) {
if (version)
return context_show_version(context, version);
- else if (FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF))
+ else if (!sd_json_format_enabled(arg_json_format_flags))
return context_show_table(context);
else {
_cleanup_(sd_json_variant_unrefp) sd_json_variant *json = NULL;
@@ -1351,7 +1351,7 @@ static int verb_check_new(int argc, char **argv, void *userdata) {
if (r < 0)
return r;
- if (FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF)) {
+ if (!sd_json_format_enabled(arg_json_format_flags)) {
if (!context->candidate) {
log_debug("No candidate found.");
return EXIT_FAILURE;
@@ -1611,7 +1611,7 @@ static int verb_components(int argc, char **argv, void *userdata) {
strv_sort(z);
- if (FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF)) {
+ if (!sd_json_format_enabled(arg_json_format_flags)) {
if (!has_default_component && set_isempty(names)) {
log_info("No components defined.");
return 0;
diff --git a/src/sysupdate/updatectl.c b/src/sysupdate/updatectl.c
index e7e4b374da..7cfccb66f4 100644
--- a/src/sysupdate/updatectl.c
+++ b/src/sysupdate/updatectl.c
@@ -843,23 +843,23 @@ static int update_render_progress(sd_event_source *source, void *userdata) {
int progress = PTR_TO_INT(p);
if (progress == UPDATE_PROGRESS_FAILED) {
- clear_progress_bar_impl(target);
+ clear_progress_bar_unbuffered(target);
fprintf(stderr, "%s: %s Unknown failure\n", target, RED_CROSS_MARK());
total += 100;
} else if (progress == -EALREADY) {
- clear_progress_bar_impl(target);
+ clear_progress_bar_unbuffered(target);
fprintf(stderr, "%s: %s Already up-to-date\n", target, GREEN_CHECK_MARK());
n--; /* Don't consider this target in the total */
} else if (progress < 0) {
- clear_progress_bar_impl(target);
+ clear_progress_bar_unbuffered(target);
fprintf(stderr, "%s: %s %s\n", target, RED_CROSS_MARK(), STRERROR(progress));
total += 100;
} else if (progress == UPDATE_PROGRESS_DONE) {
- clear_progress_bar_impl(target);
+ clear_progress_bar_unbuffered(target);
fprintf(stderr, "%s: %s Done\n", target, GREEN_CHECK_MARK());
total += 100;
} else {
- draw_progress_bar_impl(target, progress);
+ draw_progress_bar_unbuffered(target, progress);
fputs("\n", stderr);
total += progress;
}
@@ -867,9 +867,9 @@ static int update_render_progress(sd_event_source *source, void *userdata) {
if (n > 1) {
if (exiting)
- clear_progress_bar_impl(target);
+ clear_progress_bar_unbuffered(target);
else {
- draw_progress_bar_impl("Total", (double) total / n);
+ draw_progress_bar_unbuffered("Total", (double) total / n);
if (terminal_is_dumb())
fputs("\n", stderr);
}
diff --git a/src/sysusers/sysusers.c b/src/sysusers/sysusers.c
index 8ec1373479..44253483db 100644
--- a/src/sysusers/sysusers.c
+++ b/src/sysusers/sysusers.c
@@ -86,6 +86,8 @@ typedef struct Item {
bool uid_set;
+ bool locked;
+
bool todo_user;
bool todo_group;
} Item;
@@ -654,7 +656,7 @@ static int write_temporary_shadow(
.sp_max = -1,
.sp_warn = -1,
.sp_inact = -1,
- .sp_expire = -1,
+ .sp_expire = i->locked ? 1 : -1, /* Negative expiration means "unset". Expiration 0 or 1 means "locked" */
.sp_flag = ULONG_MAX, /* this appears to be what everybody does ... */
};
@@ -1707,10 +1709,17 @@ static int parse_line(
return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EINVAL),
"Trailing garbage.");
- /* Verify action */
- if (strlen(action) != 1)
- return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EINVAL),
- "Unknown modifier '%s'.", action);
+ if (isempty(action))
+ return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG),
+ "Empty command specification.");
+
+ bool locked = false;
+ for (int pos = 1; action[pos]; pos++)
+ if (action[pos] == '!' && !locked)
+ locked = true;
+ else
+ return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG),
+ "Unknown modifiers in command '%s'.", action);
if (!IN_SET(action[0], ADD_USER, ADD_GROUP, ADD_MEMBER, ADD_RANGE))
return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG),
@@ -1793,6 +1802,10 @@ static int parse_line(
switch (action[0]) {
case ADD_RANGE:
+ if (locked)
+ return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EINVAL),
+ "Flag '!' not permitted on lines of type 'r'.");
+
if (resolved_name)
return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EINVAL),
"Lines of type 'r' don't take a name field.");
@@ -1820,6 +1833,10 @@ static int parse_line(
return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EINVAL),
"Lines of type 'm' require a user name in the second field.");
+ if (locked)
+ return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EINVAL),
+ "Flag '!' not permitted on lines of type 'm'.");
+
if (!resolved_id)
return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EINVAL),
"Lines of type 'm' require a group name in the third field.");
@@ -1886,6 +1903,7 @@ static int parse_line(
i->description = TAKE_PTR(resolved_description);
i->home = TAKE_PTR(resolved_home);
i->shell = TAKE_PTR(resolved_shell);
+ i->locked = locked;
h = c->users;
break;
@@ -1895,6 +1913,10 @@ static int parse_line(
return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EINVAL),
"Lines of type 'g' require a user name in the second field.");
+ if (locked)
+ return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EINVAL),
+ "Flag '!' not permitted on lines of type 'g'.");
+
if (description || home || shell)
return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EINVAL),
"Lines of type '%c' don't take a %s field.",
diff --git a/src/sysv-generator/sysv-generator.c b/src/sysv-generator/sysv-generator.c
index f3b447095a..fde8c057e3 100644
--- a/src/sysv-generator/sysv-generator.c
+++ b/src/sysv-generator/sysv-generator.c
@@ -763,12 +763,13 @@ static int enumerate_sysv(const LookupPaths *lp, Hashmap *all_services) {
return log_oom();
log_struct(LOG_WARNING,
- LOG_MESSAGE("SysV service '%s' lacks a native systemd unit file. "
- "%s Automatically generating a unit file for compatibility. Please update package to include a native systemd unit file, in order to make it safe, robust and future-proof. "
+ LOG_MESSAGE("SysV service '%s' lacks a native systemd unit file, "
+ "automatically generating a unit file for compatibility.\n"
+ "Please update package to include a native systemd unit file.\n"
"%s This compatibility logic is deprecated, expect removal soon. %s",
fpath,
- special_glyph(SPECIAL_GLYPH_RECYCLING),
- special_glyph(SPECIAL_GLYPH_WARNING_SIGN), special_glyph(SPECIAL_GLYPH_WARNING_SIGN)),
+ special_glyph(SPECIAL_GLYPH_WARNING_SIGN),
+ special_glyph(SPECIAL_GLYPH_WARNING_SIGN)),
"MESSAGE_ID=" SD_MESSAGE_SYSV_GENERATOR_DEPRECATED_STR,
"SYSVSCRIPT=%s", fpath,
"UNIT=%s", name);
diff --git a/src/test/meson.build b/src/test/meson.build
index b8699a016b..9f74a7b56a 100644
--- a/src/test/meson.build
+++ b/src/test/meson.build
@@ -183,6 +183,7 @@ simple_tests += files(
'test-umask-util.c',
'test-unaligned.c',
'test-unit-file.c',
+ 'test-user-record.c',
'test-user-util.c',
'test-utf8.c',
'test-verbs.c',
@@ -378,7 +379,6 @@ executables += [
},
test_template + {
'sources' : files('test-progress-bar.c'),
- 'type' : 'manual',
},
test_template + {
'sources' : files('test-qrcode-util.c'),
diff --git a/src/test/test-env-util.c b/src/test/test-env-util.c
index e2c009dc9c..7eda66bd3e 100644
--- a/src/test/test-env-util.c
+++ b/src/test/test-env-util.c
@@ -581,4 +581,15 @@ TEST(getenv_path_list) {
assert_se(unsetenv("TEST_GETENV_PATH_LIST") >= 0);
}
+TEST(strv_env_get_merged) {
+ char **l = STRV_MAKE("ONE", "1", "TWO", "2", "THREE", "3", "FOUR", "4", "FIVE", "5"),
+ **expected = STRV_MAKE("ONE=1", "TWO=2", "THREE=3", "FOUR=4", "FIVE=5");
+ _cleanup_strv_free_ char **m = NULL;
+
+ ASSERT_OK(strv_env_get_merged(NULL, &m));
+ ASSERT_NULL(m);
+ ASSERT_OK(strv_env_get_merged(l, &m));
+ ASSERT_TRUE(strv_equal(m, expected));
+}
+
DEFINE_TEST_MAIN(LOG_DEBUG);
diff --git a/src/test/test-progress-bar.c b/src/test/test-progress-bar.c
index abc1d292dc..b199741cf5 100644
--- a/src/test/test-progress-bar.c
+++ b/src/test/test-progress-bar.c
@@ -14,7 +14,7 @@ TEST(progress_bar) {
for (double d = 0; d <= 100; d += 0.5) {
usleep_safe(random_u64_range(20 * USEC_PER_MSEC));
- draw_progress_bar(PROGRESS_PREFIX, d);
+ draw_progress_barf(d, PROGRESS_PREFIX "[" PID_FMT "]", getpid_cached());
if (!paused && d >= 50) {
clear_progress_bar(PROGRESS_PREFIX);
@@ -25,9 +25,9 @@ TEST(progress_bar) {
}
}
- draw_progress_bar(PROGRESS_PREFIX, 100);
+ draw_progress_barf(100, PROGRESS_PREFIX "[" PID_FMT "]", getpid_cached());
usleep_safe(300 * MSEC_PER_SEC);
- clear_progress_bar(PROGRESS_PREFIX);
+ clear_progress_bar(PROGRESS_PREFIX "[0123456789]" );
fputs("Done.\n", stdout);
}
diff --git a/src/test/test-sbat.c b/src/test/test-sbat.c
index d8546b1ad9..cf9c155c9d 100644
--- a/src/test/test-sbat.c
+++ b/src/test/test-sbat.c
@@ -8,17 +8,26 @@
#include "sbat.h"
#include "tests.h"
-TEST(sbat_section_text) {
- log_info("---SBAT-----------&<----------------------------------------\n"
+TEST(BOOT_SBAT) {
+ log_info("---SBAT-----------&<-----------------------------------------\n"
"%s"
+ "------------------>&-----------------------------------------",
+#ifdef SBAT_DISTRO
+ SBAT_BOOT_SECTION_TEXT
+#else
+ "(not defined)"
+#endif
+ );
+}
+
+TEST(STUB_SBAT) {
+ log_info("---SBAT-----------&<-----------------------------------------\n"
"%s"
"------------------>&-----------------------------------------",
#ifdef SBAT_DISTRO
- SBAT_BOOT_SECTION_TEXT,
SBAT_STUB_SECTION_TEXT
#else
- "(not defined)",
- ""
+ "(not defined)"
#endif
);
}
diff --git a/src/test/test-strip-tab-ansi.c b/src/test/test-strip-tab-ansi.c
index 3ad0fcd90e..00bd7cdaf1 100644
--- a/src/test/test-strip-tab-ansi.c
+++ b/src/test/test-strip-tab-ansi.c
@@ -67,6 +67,15 @@ TEST(strip_tab_ansi) {
assert_se(strip_tab_ansi(&q, NULL, NULL));
ASSERT_STREQ(q, qq);
}
+
+ /* Test that both kinds of ST are recognized after OSC */
+ assert_se(p = strdup("before" ANSI_OSC "inside1" ANSI_ST
+ "between1" ANSI_OSC "inside2\a"
+ "between2" ANSI_OSC "inside3\x1b\x5c"
+ "after"));
+ assert_se(strip_tab_ansi(&p, NULL, NULL));
+ ASSERT_STREQ(p, "beforebetween1between2after");
+ free(p);
}
DEFINE_TEST_MAIN(LOG_INFO);
diff --git a/src/test/test-terminal-util.c b/src/test/test-terminal-util.c
index ad800a4111..38c9f7e34a 100644
--- a/src/test/test-terminal-util.c
+++ b/src/test/test-terminal-util.c
@@ -25,6 +25,10 @@
"in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat " \
"non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
+TEST(colors_enabled) {
+ log_info("colors_enabled: %s", yes_no(colors_enabled()));
+}
+
TEST(default_term_for_tty) {
puts(default_term_for_tty("/dev/tty23"));
puts(default_term_for_tty("/dev/ttyS23"));
@@ -212,14 +216,13 @@ TEST(terminal_fix_size) {
TEST(terminal_is_pty_fd) {
_cleanup_close_ int fd1 = -EBADF, fd2 = -EBADF;
- _cleanup_free_ char *peer = NULL;
int r;
- fd1 = openpt_allocate(O_RDWR, &peer);
+ fd1 = openpt_allocate(O_RDWR, /* ret_peer_path= */ NULL);
assert_se(fd1 >= 0);
assert_se(terminal_is_pty_fd(fd1) > 0);
- fd2 = open_terminal(peer, O_RDWR|O_CLOEXEC|O_NOCTTY);
+ fd2 = pty_open_peer(fd1, O_RDWR|O_CLOEXEC|O_NOCTTY);
assert_se(fd2 >= 0);
assert_se(terminal_is_pty_fd(fd2) > 0);
@@ -291,4 +294,24 @@ TEST(terminal_reset_defensive) {
log_notice_errno(r, "Failed to reset terminal: %m");
}
+TEST(pty_open_peer) {
+ _cleanup_close_ int pty_fd = -EBADF, peer_fd = -EBADF;
+ _cleanup_free_ char *pty_path = NULL;
+
+ pty_fd = openpt_allocate(O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK, &pty_path);
+ assert(pty_fd >= 0);
+ assert(pty_path);
+
+ peer_fd = pty_open_peer(pty_fd, O_RDWR|O_NOCTTY|O_CLOEXEC);
+ assert(peer_fd >= 0);
+
+ static const char x[] = { 'x', '\n' };
+ assert(write(pty_fd, x, sizeof(x)) == 2);
+
+ char buf[3];
+ assert(read(peer_fd, &buf, sizeof(buf)) == sizeof(x));
+ assert(buf[0] == x[0]);
+ assert(buf[1] == x[1]);
+}
+
DEFINE_TEST_MAIN(LOG_INFO);
diff --git a/src/test/test-user-record.c b/src/test/test-user-record.c
new file mode 100644
index 0000000000..3a7e8e28af
--- /dev/null
+++ b/src/test/test-user-record.c
@@ -0,0 +1,101 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "json-util.h"
+#include "macro.h"
+#include "tests.h"
+#include "user-record.h"
+
+#define USER(ret, ...) \
+ ({ \
+ typeof(ret) _r = (ret); \
+ user_record_unref(*_r); \
+ assert_se(user_record_build((ret), SD_JSON_BUILD_OBJECT(__VA_ARGS__)) >= 0); \
+ 0; \
+ })
+
+TEST(self_changes) {
+ _cleanup_(user_record_unrefp) UserRecord *curr = NULL, *new = NULL;
+
+ /* not allowlisted */
+ USER(&curr,
+ SD_JSON_BUILD_PAIR_STRING("userName", "test"),
+ SD_JSON_BUILD_PAIR_UNSIGNED("notInHardCodedList", 11111));
+ USER(&new,
+ SD_JSON_BUILD_PAIR_STRING("userName", "test"),
+ SD_JSON_BUILD_PAIR_UNSIGNED("notInHardCodedList", 99999));
+ assert_se(!user_record_self_changes_allowed(curr, new));
+
+ /* manually allowlisted */
+ USER(&curr,
+ SD_JSON_BUILD_PAIR_STRING("userName", "test"),
+ SD_JSON_BUILD_PAIR_UNSIGNED("notInHardCodedList", 11111),
+ SD_JSON_BUILD_PAIR_ARRAY("selfModifiableFields", SD_JSON_BUILD_STRING("notInHardCodedList")));
+ USER(&new,
+ SD_JSON_BUILD_PAIR_STRING("userName", "test"),
+ SD_JSON_BUILD_PAIR_ARRAY("selfModifiableFields", SD_JSON_BUILD_STRING("notInHardCodedList")),
+ /* change in order shouldn't affect things */
+ SD_JSON_BUILD_PAIR_UNSIGNED("notInHardCodedList", 99999));
+ assert_se(user_record_self_changes_allowed(curr, new));
+
+ /* default allowlisted */
+ USER(&curr,
+ SD_JSON_BUILD_PAIR_STRING("userName", "test"),
+ SD_JSON_BUILD_PAIR_STRING("realName", "Old Name"));
+ USER(&new,
+ SD_JSON_BUILD_PAIR_STRING("userName", "test"),
+ SD_JSON_BUILD_PAIR_STRING("realName", "New Name"));
+ assert_se(user_record_self_changes_allowed(curr, new));
+
+ /* introduced new default allowlisted */
+ USER(&curr,
+ SD_JSON_BUILD_PAIR_STRING("userName", "test"));
+ USER(&new,
+ SD_JSON_BUILD_PAIR_STRING("userName", "test"),
+ SD_JSON_BUILD_PAIR_STRING("realName", "New Name"));
+ assert_se(user_record_self_changes_allowed(curr, new));
+
+ /* introduced new not allowlisted */
+ USER(&curr,
+ SD_JSON_BUILD_PAIR_STRING("userName", "test"));
+ USER(&new,
+ SD_JSON_BUILD_PAIR_STRING("userName", "test"),
+ SD_JSON_BUILD_PAIR_UNSIGNED("notInHardCodedList", 99999));
+ assert_se(!user_record_self_changes_allowed(curr, new));
+
+ /* privileged section: default allowlisted */
+ USER(&curr,
+ SD_JSON_BUILD_PAIR_STRING("userName", "test"),
+ SD_JSON_BUILD_PAIR_OBJECT("privileged",
+ SD_JSON_BUILD_PAIR_STRING("passwordHint", "Old Hint")));
+ USER(&new,
+ SD_JSON_BUILD_PAIR_STRING("userName", "test"),
+ SD_JSON_BUILD_PAIR_OBJECT("privileged",
+ SD_JSON_BUILD_PAIR_STRING("passwordHint", "New Hint")));
+ assert_se(user_record_self_changes_allowed(curr, new));
+
+ /* privileged section: not allowlisted */
+ USER(&curr,
+ SD_JSON_BUILD_PAIR_STRING("userName", "test"),
+ SD_JSON_BUILD_PAIR_OBJECT("privileged",
+ SD_JSON_BUILD_PAIR_UNSIGNED("notInHardCodedList", 11111)));
+ USER(&new,
+ SD_JSON_BUILD_PAIR_STRING("userName", "test"),
+ SD_JSON_BUILD_PAIR_OBJECT("privileged",
+ SD_JSON_BUILD_PAIR_UNSIGNED("notInHardCodedList", 99999)));
+ assert_se(!user_record_self_changes_allowed(curr, new));
+
+ /* privileged section: manually allowlisted */
+ USER(&curr,
+ SD_JSON_BUILD_PAIR_STRING("userName", "test"),
+ SD_JSON_BUILD_PAIR_ARRAY("selfModifiablePrivileged", SD_JSON_BUILD_STRING("notInHardCodedList")),
+ SD_JSON_BUILD_PAIR_OBJECT("privileged",
+ SD_JSON_BUILD_PAIR_UNSIGNED("notInHardCodedList", 11111)));
+ USER(&new,
+ SD_JSON_BUILD_PAIR_STRING("userName", "test"),
+ SD_JSON_BUILD_PAIR_ARRAY("selfModifiablePrivileged", SD_JSON_BUILD_STRING("notInHardCodedList")),
+ SD_JSON_BUILD_PAIR_OBJECT("privileged",
+ SD_JSON_BUILD_PAIR_UNSIGNED("notInHardCodedList", 99999)));
+ assert_se(user_record_self_changes_allowed(curr, new));
+}
+
+DEFINE_TEST_MAIN(LOG_INFO);
diff --git a/src/test/test-varlink.c b/src/test/test-varlink.c
index cafe98871b..40d972b6b6 100644
--- a/src/test/test-varlink.c
+++ b/src/test/test-varlink.c
@@ -33,14 +33,20 @@ static int method_something(sd_varlink *link, sd_json_variant *parameters, sd_va
int r;
a = sd_json_variant_by_key(parameters, "a");
- if (!a)
- return sd_varlink_error(link, "io.test.BadParameters", NULL);
+ if (!a) {
+ r = sd_varlink_error(link, "io.test.BadParameters", NULL);
+ assert_se(r == -EBADR);
+ return r;
+ }
x = sd_json_variant_integer(a);
b = sd_json_variant_by_key(parameters, "b");
- if (!b)
- return sd_varlink_error(link, "io.test.BadParameters", NULL);
+ if (!b) {
+ r = sd_varlink_error(link, "io.test.BadParameters", NULL);
+ assert_se(r == -EBADR);
+ return r;
+ }
y = sd_json_variant_integer(b);
@@ -105,8 +111,11 @@ static int method_passfd(sd_varlink *link, sd_json_variant *parameters, sd_varli
int r;
a = sd_json_variant_by_key(parameters, "fd");
- if (!a)
- return sd_varlink_error(link, "io.test.BadParameters", NULL);
+ if (!a) {
+ r = sd_varlink_error_invalid_parameter_name(link, "fd");
+ assert_se(r == -EINVAL);
+ return r;
+ }
ASSERT_STREQ(sd_json_variant_string(a), "whoop");
@@ -242,8 +251,7 @@ static void *thread(void *arg) {
_cleanup_(sd_json_variant_unrefp) sd_json_variant *i = NULL;
_cleanup_(sd_json_variant_unrefp) sd_json_variant *wrong = NULL;
sd_json_variant *o = NULL, *k = NULL, *j = NULL;
- const char *error_id;
- const char *e;
+ const char *error_id, *e;
int x = 0;
assert_se(sd_json_build(&i, SD_JSON_BUILD_OBJECT(SD_JSON_BUILD_PAIR("a", SD_JSON_BUILD_INTEGER(88)),
@@ -288,6 +296,7 @@ static void *thread(void *arg) {
assert_se(sd_varlink_push_fd(c, fd3) == 2);
assert_se(sd_varlink_callb(c, "io.test.PassFD", &o, &e, SD_JSON_BUILD_OBJECT(SD_JSON_BUILD_PAIR("fd", SD_JSON_BUILD_STRING("whoop")))) >= 0);
+ assert_se(!e);
int fd4 = sd_varlink_peek_fd(c, 0);
int fd5 = sd_varlink_peek_fd(c, 1);
@@ -298,6 +307,9 @@ static void *thread(void *arg) {
test_fd(fd4, "miau", 4);
test_fd(fd5, "wuff", 4);
+ assert_se(sd_varlink_callb(c, "io.test.PassFD", &o, &e, SD_JSON_BUILD_OBJECT(SD_JSON_BUILD_PAIR("fdx", SD_JSON_BUILD_STRING("whoopx")))) >= 0);
+ ASSERT_TRUE(sd_varlink_error_is_invalid_parameter(e, o, "fd"));
+
assert_se(sd_varlink_callb(c, "io.test.IDontExist", &o, &e, SD_JSON_BUILD_OBJECT(SD_JSON_BUILD_PAIR("x", SD_JSON_BUILD_REAL(5.5)))) >= 0);
ASSERT_STREQ(sd_json_variant_string(sd_json_variant_by_key(o, "method")), "io.test.IDontExist");
ASSERT_STREQ(e, SD_VARLINK_ERROR_METHOD_NOT_FOUND);
diff --git a/src/udev/udevadm-info.c b/src/udev/udevadm-info.c
index 0a1da3ed31..57f063b010 100644
--- a/src/udev/udevadm-info.c
+++ b/src/udev/udevadm-info.c
@@ -274,6 +274,8 @@ static int print_device_chain_in_json(sd_device *device) {
assert(device);
+ arg_json_format_flags |=SD_JSON_FORMAT_SEQ;
+
r = print_all_attributes_in_json(device, /* is_parent = */ false);
if (r < 0)
return r;
@@ -453,9 +455,7 @@ static int export_devices(sd_device_enumerator *e) {
pager_open(arg_pager_flags);
FOREACH_DEVICE_AND_SUBSYSTEM(e, d)
- if (arg_json_format_flags & SD_JSON_FORMAT_OFF)
- (void) print_record(d, NULL);
- else {
+ if (sd_json_format_enabled(arg_json_format_flags)) {
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
r = record_to_json(d, &v);
@@ -463,7 +463,8 @@ static int export_devices(sd_device_enumerator *e) {
return r;
(void) sd_json_variant_dump(v, arg_json_format_flags, stdout, NULL);
- }
+ } else
+ (void) print_record(d, NULL);
return 0;
}
@@ -629,9 +630,7 @@ static int query_device(QueryType query, sd_device* device) {
return 0;
case QUERY_ALL:
- if (arg_json_format_flags & SD_JSON_FORMAT_OFF)
- return print_record(device, NULL);
- else {
+ if (sd_json_format_enabled(arg_json_format_flags)) {
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
r = record_to_json(device, &v);
@@ -639,7 +638,8 @@ static int query_device(QueryType query, sd_device* device) {
return r;
(void) sd_json_variant_dump(v, arg_json_format_flags, stdout, NULL);
- }
+ } else
+ return print_record(device, NULL);
return 0;
@@ -1229,10 +1229,10 @@ int info_main(int argc, char *argv[], void *userdata) {
if (action == ACTION_QUERY)
r = query_device(query, device);
else if (action == ACTION_ATTRIBUTE_WALK) {
- if (arg_json_format_flags & SD_JSON_FORMAT_OFF)
- r = print_device_chain(device);
- else
+ if (sd_json_format_enabled(arg_json_format_flags))
r = print_device_chain_in_json(device);
+ else
+ r = print_device_chain(device);
} else if (action == ACTION_TREE)
r = print_tree(device);
else
diff --git a/src/update-utmp/update-utmp.c b/src/update-utmp/update-utmp.c
index 7a8a53f7e8..e40843cf35 100644
--- a/src/update-utmp/update-utmp.c
+++ b/src/update-utmp/update-utmp.c
@@ -65,6 +65,8 @@ static int get_startup_monotonic_time(Context *c, usec_t *ret) {
return 0;
}
+#define MAX_ATTEMPTS 64u
+
static int get_current_runlevel(Context *c) {
static const struct {
const int runlevel;
@@ -84,12 +86,13 @@ static int get_current_runlevel(Context *c) {
for (unsigned n_attempts = 0;;) {
if (n_attempts++ > 0) {
/* systemd might have dropped off momentarily, let's not make this an error,
- * and wait some random time. Let's pick a random time in the range 0ms…250ms,
+ * and wait some random time. Let's pick a random time in the range 100ms…2000ms,
* linearly scaled by the number of failed attempts. */
c->bus = sd_bus_flush_close_unref(c->bus);
- usec_t usec = random_u64_range(UINT64_C(10) * USEC_PER_MSEC +
- UINT64_C(240) * USEC_PER_MSEC * n_attempts/64);
+ usec_t usec =
+ UINT64_C(100) * USEC_PER_MSEC +
+ random_u64_range(UINT64_C(1900) * USEC_PER_MSEC * n_attempts / MAX_ATTEMPTS);
(void) usleep_safe(usec);
r = bus_connect_system_systemd(&c->bus);
@@ -121,7 +124,7 @@ static int get_current_runlevel(Context *c) {
sd_bus_error_has_names(&error,
SD_BUS_ERROR_NO_REPLY,
SD_BUS_ERROR_DISCONNECTED)) &&
- n_attempts < 64) {
+ n_attempts < MAX_ATTEMPTS) {
log_debug_errno(r, "Failed to get state of %s, retrying after a slight delay: %s",
e->special, bus_error_message(&error, r));
break;
diff --git a/src/userdb/userdbctl.c b/src/userdb/userdbctl.c
index d74251fd86..16c8f385d6 100644
--- a/src/userdb/userdbctl.c
+++ b/src/userdb/userdbctl.c
@@ -1287,7 +1287,7 @@ static int parse_argv(int argc, char *argv[]) {
if (r <= 0)
return r;
- arg_output = FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF) ? _OUTPUT_INVALID : OUTPUT_JSON;
+ arg_output = sd_json_format_enabled(arg_json_format_flags) ? OUTPUT_JSON : _OUTPUT_INVALID;
break;
case 'j':
diff --git a/src/varlinkctl/varlinkctl.c b/src/varlinkctl/varlinkctl.c
index b6c6c0da42..76c9e4850a 100644
--- a/src/varlinkctl/varlinkctl.c
+++ b/src/varlinkctl/varlinkctl.c
@@ -287,7 +287,7 @@ static int verb_info(int argc, char *argv[], void *userdata) {
pager_open(arg_pager_flags);
- if (FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF)) {
+ if (!sd_json_format_enabled(arg_json_format_flags)) {
static const sd_json_dispatch_field dispatch_table[] = {
{ "vendor", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(GetInfoData, vendor), SD_JSON_MANDATORY },
{ "product", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(GetInfoData, product), SD_JSON_MANDATORY },
@@ -426,7 +426,7 @@ static int verb_introspect(int argc, char *argv[], void *userdata) {
if (r < 0)
return r;
- if (FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF) || list_methods) {
+ if (!sd_json_format_enabled(arg_json_format_flags) || list_methods) {
static const sd_json_dispatch_field dispatch_table[] = {
{ "description", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, 0, SD_JSON_MANDATORY },
{}
@@ -478,7 +478,7 @@ static int verb_introspect(int argc, char *argv[], void *userdata) {
strv_sort_uniq(methods);
- if (FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF))
+ if (!sd_json_format_enabled(arg_json_format_flags))
strv_print(methods);
else {
_cleanup_(sd_json_variant_unrefp) sd_json_variant *j = NULL;
@@ -539,7 +539,7 @@ static int verb_call(int argc, char *argv[], void *userdata) {
parameter = argc > 3 && !streq(argv[3], "-") ? argv[3] : NULL;
/* No JSON mode explicitly configured? Then default to the same as -j */
- if (FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF)) {
+ if (!sd_json_format_enabled(arg_json_format_flags)) {
arg_json_format_flags &= ~SD_JSON_FORMAT_OFF;
arg_json_format_flags |= SD_JSON_FORMAT_PRETTY_AUTO|SD_JSON_FORMAT_COLOR_AUTO;
}