diff --git a/man/org.freedesktop.resolve1.xml b/man/org.freedesktop.resolve1.xml index 5b8acbbd9f..6a6d9de6b9 100644 --- a/man/org.freedesktop.resolve1.xml +++ b/man/org.freedesktop.resolve1.xml @@ -147,6 +147,8 @@ node /org/freedesktop/resolve1 { readonly as DNSSECNegativeTrustAnchors = ['...', ...]; @org.freedesktop.DBus.Property.EmitsChangedSignal("false") readonly s DNSStubListener = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly s ResolvConfMode = '...'; }; interface org.freedesktop.DBus.Peer { ... }; interface org.freedesktop.DBus.Introspectable { ... }; @@ -272,6 +274,8 @@ node /org/freedesktop/resolve1 { + + @@ -555,9 +559,12 @@ node /org/freedesktop/resolve1 { DNSSEC is supported by DNS servers until it verifies that this is not the case. Thus, the reported value may initially be true, until the first transactions are executed. - The LogLevel property shows the (maximum) log level of the manager, with the - same values as the option described in - systemd1. + The ResolvConfMode property exposes how /etc/resolv.conf + is managed on the host. Currently, the values uplink, stub, + static (these three correspond to the three different files + systemd-resolved.service provides), foreign (the file is + managed by admin or another service, systemd-resolved.service just consumes it), + missing (/etc/resolv.conf is missing). diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c index afe67d9e6c..724c3d4a6a 100644 --- a/src/resolve/resolved-bus.c +++ b/src/resolve/resolved-bus.c @@ -15,6 +15,7 @@ #include "resolved-dnssd-bus.h" #include "resolved-dnssd.h" #include "resolved-link-bus.h" +#include "resolved-resolv-conf.h" #include "socket-netlink.h" #include "stdio-util.h" #include "strv.h" @@ -1620,6 +1621,28 @@ static BUS_DEFINE_PROPERTY_GET(bus_property_get_dnssec_supported, "b", Manager, static BUS_DEFINE_PROPERTY_GET2(bus_property_get_dnssec_mode, "s", Manager, manager_get_dnssec_mode, dnssec_mode_to_string); static BUS_DEFINE_PROPERTY_GET2(bus_property_get_dns_over_tls_mode, "s", Manager, manager_get_dns_over_tls_mode, dns_over_tls_mode_to_string); +static int bus_property_get_resolv_conf_mode( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + int r; + + assert(reply); + + r = resolv_conf_mode(); + if (r < 0) { + log_warning_errno(r, "Failed to test /etc/resolv.conf mode, ignoring: %m"); + return sd_bus_message_append(reply, "s", NULL); + } + + return sd_bus_message_append(reply, "s", resolv_conf_mode_to_string(r)); +} + static int bus_method_reset_statistics(sd_bus_message *message, void *userdata, sd_bus_error *error) { Manager *m = userdata; DnsScope *s; @@ -2000,6 +2023,7 @@ static const sd_bus_vtable resolve_vtable[] = { SD_BUS_PROPERTY("DNSSECSupported", "b", bus_property_get_dnssec_supported, 0, 0), SD_BUS_PROPERTY("DNSSECNegativeTrustAnchors", "as", bus_property_get_ntas, 0, 0), SD_BUS_PROPERTY("DNSStubListener", "s", bus_property_get_dns_stub_listener_mode, offsetof(Manager, dns_stub_listener_mode), 0), + SD_BUS_PROPERTY("ResolvConfMode", "s", bus_property_get_resolv_conf_mode, 0, 0), SD_BUS_METHOD_WITH_ARGS("ResolveHostname", SD_BUS_ARGS("i", ifindex, "s", name, "i", family, "t", flags), diff --git a/src/resolve/resolved-resolv-conf.c b/src/resolve/resolved-resolv-conf.c index 0de5046367..05d98e518d 100644 --- a/src/resolve/resolved-resolv-conf.c +++ b/src/resolve/resolved-resolv-conf.c @@ -15,6 +15,7 @@ #include "resolved-dns-server.h" #include "resolved-resolv-conf.h" #include "stat-util.h" +#include "string-table.h" #include "string-util.h" #include "strv.h" #include "tmpfile-util-label.h" @@ -371,3 +372,49 @@ int manager_write_resolv_conf(Manager *m) { return r; } + +int resolv_conf_mode(void) { + static const char * const table[_RESOLV_CONF_MODE_MAX] = { + [RESOLV_CONF_UPLINK] = PRIVATE_UPLINK_RESOLV_CONF, + [RESOLV_CONF_STUB] = PRIVATE_STUB_RESOLV_CONF, + [RESOLV_CONF_STATIC] = PRIVATE_STATIC_RESOLV_CONF, + }; + + struct stat system_st; + + if (stat("/etc/resolv.conf", &system_st) < 0) { + if (errno == ENOENT) + return RESOLV_CONF_MISSING; + + return -errno; + } + + for (ResolvConfMode m = 0; m < _RESOLV_CONF_MODE_MAX; m++) { + struct stat our_st; + + if (!table[m]) + continue; + + if (stat(table[m], &our_st) < 0) { + if (errno != ENOENT) + log_debug_errno(errno, "Failed to stat() %s, ignoring: %m", table[m]); + + continue; + } + + if (system_st.st_dev == our_st.st_dev && + system_st.st_ino == our_st.st_ino) + return m; + } + + return RESOLV_CONF_FOREIGN; +} + +static const char* const resolv_conf_mode_table[_RESOLV_CONF_MODE_MAX] = { + [RESOLV_CONF_UPLINK] = "uplink", + [RESOLV_CONF_STUB] = "stub", + [RESOLV_CONF_STATIC] = "static", + [RESOLV_CONF_MISSING] = "missing", + [RESOLV_CONF_FOREIGN] = "foreign", +}; +DEFINE_STRING_TABLE_LOOKUP(resolv_conf_mode, ResolvConfMode); diff --git a/src/resolve/resolved-resolv-conf.h b/src/resolve/resolved-resolv-conf.h index f69cf2a441..584d25c0f7 100644 --- a/src/resolve/resolved-resolv-conf.h +++ b/src/resolve/resolved-resolv-conf.h @@ -6,3 +6,18 @@ int manager_check_resolv_conf(const Manager *m); int manager_read_resolv_conf(Manager *m); int manager_write_resolv_conf(Manager *m); + +typedef enum ResolvConfMode { + RESOLV_CONF_UPLINK, + RESOLV_CONF_STUB, + RESOLV_CONF_STATIC, + RESOLV_CONF_FOREIGN, + RESOLV_CONF_MISSING, + _RESOLV_CONF_MODE_MAX, + _RESOLV_CONF_MODE_INVALID = -1, +} ResolvConfMode; + +int resolv_conf_mode(void); + +const char* resolv_conf_mode_to_string(ResolvConfMode m) _const_; +ResolvConfMode resolv_conf_mode_from_string(const char *s) _pure_;