From 091a364c802e34a58f3260c9cb5db9b75c62215c Mon Sep 17 00:00:00 2001 From: Tom Gundersen Date: Sun, 18 May 2014 22:10:48 +0200 Subject: [PATCH] resolved: add daemon to manage resolv.conf Also remove the equivalent functionality from networkd. --- .gitignore | 1 + Makefile-man.am | 14 + Makefile.am | 59 +++- configure.ac | 27 +- man/resolved.conf.xml | 91 +++++ man/systemd-networkd.service.xml | 5 - man/systemd-resolved.service.xml | 85 +++++ src/network/.gitignore | 2 - src/network/networkd-link.c | 11 - src/network/networkd-manager.c | 201 +---------- src/network/networkd-network.c | 2 +- src/network/networkd.c | 8 - src/network/networkd.h | 8 - src/resolve/.gitignore | 2 + src/resolve/Makefile | 1 + .../resolved-gperf.gperf} | 8 +- src/resolve/resolved-manager.c | 320 ++++++++++++++++++ src/resolve/resolved.c | 86 +++++ .../resolved.conf.in} | 4 +- src/resolve/resolved.h | 69 ++++ units/.gitignore | 1 + units/systemd-resolved.service.in | 21 ++ 22 files changed, 765 insertions(+), 261 deletions(-) create mode 100644 man/resolved.conf.xml create mode 100644 man/systemd-resolved.service.xml create mode 100644 src/resolve/.gitignore create mode 120000 src/resolve/Makefile rename src/{network/networkd-gperf.gperf => resolve/resolved-gperf.gperf} (55%) create mode 100644 src/resolve/resolved-manager.c create mode 100644 src/resolve/resolved.c rename src/{network/networkd.conf.in => resolve/resolved.conf.in} (87%) create mode 100644 src/resolve/resolved.h create mode 100644 units/systemd-resolved.service.in diff --git a/.gitignore b/.gitignore index 3577c2a89..908c563f4 100644 --- a/.gitignore +++ b/.gitignore @@ -92,6 +92,7 @@ /systemd-remount-api-vfs /systemd-remount-fs /systemd-reply-password +/systemd-resolved /systemd-rfkill /systemd-run /systemd-shutdown diff --git a/Makefile-man.am b/Makefile-man.am index ca7d209bd..6d57b7513 100644 --- a/Makefile-man.am +++ b/Makefile-man.am @@ -1131,6 +1131,18 @@ man/systemd-readahead.html: man/systemd-readahead-replay.service.html endif +if ENABLE_RESOLVED +MANPAGES += \ + man/resolved.conf.5 \ + man/systemd-resolved.service.8 +MANPAGES_ALIAS += \ + man/systemd-resolved.8 +man/systemd-resolved.8: man/systemd-resolved.service.8 +man/systemd-resolved.html: man/systemd-resolved.service.html + $(html-alias) + +endif + if ENABLE_RFKILL MANPAGES += \ man/systemd-rfkill@.service.8 @@ -1483,6 +1495,7 @@ EXTRA_DIST += \ man/nss-myhostname.xml \ man/os-release.xml \ man/pam_systemd.xml \ + man/resolved.conf.xml \ man/runlevel.xml \ man/sd-daemon.xml \ man/sd-id128.xml \ @@ -1581,6 +1594,7 @@ EXTRA_DIST += \ man/systemd-random-seed.service.xml \ man/systemd-readahead-replay.service.xml \ man/systemd-remount-fs.service.xml \ + man/systemd-resolved.service.xml \ man/systemd-rfkill@.service.xml \ man/systemd-run.xml \ man/systemd-shutdownd.service.xml \ diff --git a/Makefile.am b/Makefile.am index 6e0118841..f2a3bbd02 100644 --- a/Makefile.am +++ b/Makefile.am @@ -4193,6 +4193,51 @@ EXTRA_DIST += \ endif +# ------------------------------------------------------------------------------ +if ENABLE_RESOLVED +systemd_resolved_SOURCES = \ + src/resolve/resolved.h \ + src/resolve/resolved.c \ + src/resolve/resolved-manager.c + +nodist_systemd_resolved_SOURCES = \ + src/resolve/resolved-gperf.c + +EXTRA_DIST += \ + src/resolve/resolved-gperf.gperf + +CLEANFILES += \ + src/resolve/resolved-gperf.c + +systemd_resolved_LDADD = \ + libsystemd-label.la \ + libsystemd-internal.la \ + libsystemd-shared.la \ + libsystemd-network.la + +rootlibexec_PROGRAMS += \ + systemd-resolved + +nodist_systemunit_DATA += \ + units/systemd-resolved.service + +EXTRA_DIST += \ + units/systemd-resolved.service.in + +GENERAL_ALIASES += \ + $(systemunitdir)/systemd-resolved.service $(pkgsysconfdir)/system/multi-user.target.wants/systemd-resolved.service + +nodist_pkgsysconf_DATA += \ + src/resolve/resolved.conf + +EXTRA_DIST += \ + src/resolve/resolved.conf.in + +CLEANFILES += \ + src/resolve/resolved.conf + +endif + # ------------------------------------------------------------------------------ if ENABLE_NETWORKD rootlibexec_PROGRAMS += \ @@ -4225,8 +4270,7 @@ libsystemd_networkd_core_la_SOURCES = \ nodist_libsystemd_networkd_core_la_SOURCES = \ src/network/networkd-network-gperf.c \ - src/network/networkd-netdev-gperf.c \ - src/network/networkd-gperf.c + src/network/networkd-netdev-gperf.c libsystemd_networkd_core_la_LIBADD = \ libudev-internal.la \ @@ -4272,22 +4316,15 @@ GENERAL_ALIASES += \ $(systemunitdir)/systemd-networkd.service $(pkgsysconfdir)/system/multi-user.target.wants/systemd-networkd.service \ $(systemunitdir)/systemd-networkd.service $(pkgsysconfdir)/system/network-online.target.wants/systemd-networkd-wait-online.service -nodist_pkgsysconf_DATA += \ - src/network/networkd.conf - EXTRA_DIST += \ src/network/networkd-network-gperf.gperf \ src/network/networkd-netdev-gperf.gperf \ - src/network/networkd-gperf.gperf \ units/systemd-networkd.service.in \ - units/systemd-networkd-wait-online.service.in \ - src/network/networkd.conf.in + units/systemd-networkd-wait-online.service.in CLEANFILES += \ src/network/networkd-network-gperf.c \ - src/network/networkd-netdev-gperf.c \ - src/network/networkd-gperf.c \ - src/network/networkd.conf + src/network/networkd-netdev-gperf.c endif # ------------------------------------------------------------------------------ diff --git a/configure.ac b/configure.ac index 469fc2d94..9a849ffe7 100644 --- a/configure.ac +++ b/configure.ac @@ -879,25 +879,33 @@ fi AM_CONDITIONAL(ENABLE_POLKIT, [test "x$have_polkit" = "xyes"]) # ------------------------------------------------------------------------------ -have_networkd=no -AC_ARG_ENABLE(networkd, AS_HELP_STRING([--disable-networkd], [disable networkd])) -if test "x$enable_networkd" != "xno"; then - AC_DEFINE(ENABLE_NETWORKD, 1, [Define if networkd support is to be enabled]) - have_networkd=yes +have_resolved=no +AC_ARG_ENABLE(resolved, AS_HELP_STRING([--disable-resolved], [disable resolve daemon])) +if test "x$enable_resolved" != "xno"; then + have_resolved=yes fi -AS_IF([test "x$have_networkd" = "xyes" -a "x$have_kmod" != "xyes"], - [AC_MSG_ERROR([networkd requires kmod])]) -AM_CONDITIONAL(ENABLE_NETWORKD, [test "x$have_networkd" = "xyes"]) +AM_CONDITIONAL(ENABLE_RESOLVED, [test "$have_resolved" = "yes"]) AC_ARG_WITH(dns-servers, AS_HELP_STRING([--with-dns-servers=DNSSERVERS], [Space-separated list of default DNS servers]), - [DNS_SERVERS="$withval"], + [NTP_SERVERS="$withval"], [DNS_SERVERS="8.8.8.8 8.8.4.4 2001:4860:4860::8888 2001:4860:4860::8844"]) AC_DEFINE_UNQUOTED(DNS_SERVERS, ["$DNS_SERVERS"], [Default DNS Servers]) AC_SUBST(DNS_SERVERS) +# ------------------------------------------------------------------------------ +have_networkd=no +AC_ARG_ENABLE(networkd, AS_HELP_STRING([--disable-networkd], [disable networkd])) +if test "x$enable_networkd" != "xno"; then + AC_DEFINE(ENABLE_NETWORKD, 1, [Define if networkd support is to be enabled]) + have_networkd=yes +fi +AS_IF([test "x$have_networkd" = "xyes" -a "x$have_kmod" != "xyes"], + [AC_MSG_ERROR([networkd requires kmod])]) +AM_CONDITIONAL(ENABLE_NETWORKD, [test "x$have_networkd" = "xyes"]) + # ------------------------------------------------------------------------------ have_efi=no AC_ARG_ENABLE(efi, AS_HELP_STRING([--disable-efi], [disable EFI support])) @@ -1201,6 +1209,7 @@ AC_MSG_RESULT([ time epoch: ${TIME_EPOCH} localed: ${have_localed} networkd: ${have_networkd} + resolved: ${have_resolved} default DNS servers: ${DNS_SERVERS} coredump: ${have_coredump} polkit: ${have_polkit} diff --git a/man/resolved.conf.xml b/man/resolved.conf.xml new file mode 100644 index 000000000..04e510fbe --- /dev/null +++ b/man/resolved.conf.xml @@ -0,0 +1,91 @@ + + + + + + + + + resolved.conf + systemd + + + + Developer + Tom + Gundersen + teg@jklm.no + + + + + + resolved.conf + 5 + + + + resolved.conf + Network Name Resolution configuration file + + + + /etc/systemd/resolved.conf + + + + Description + + When starting, systemd-resolved will read the + configuration file resolved.conf. + This configuration file determines the fallback DNS + servers. + + + + + Options + + + + + DNS= + A space separated list of IPv4 and IPv6 + addresses to be used as the fallback DNS servers. Note that + the servers obtained from + systemd-networkd.service8 + take precedence. If this option is not given, a compiled-in + list of DNS servers is used instead. + + + + + + + See Also + + systemd1, + systemd-resolved.service8, + systemd-networkd.service8 + + + + diff --git a/man/systemd-networkd.service.xml b/man/systemd-networkd.service.xml index 75ea1a4b4..057079887 100644 --- a/man/systemd-networkd.service.xml +++ b/man/systemd-networkd.service.xml @@ -72,11 +72,6 @@ restarting networkd does not cut the network connection, and, in particular, that it is safe to transition between the initrd and the real root, and back. - - Nameservers configured in networkd, or received over DHCP - are exposed in /run/systemd/network/resolv.conf. - This file should not be used directly, but only through a symlink - from /etc/resolv.conf. Configuration Files diff --git a/man/systemd-resolved.service.xml b/man/systemd-resolved.service.xml new file mode 100644 index 000000000..cd73cb73c --- /dev/null +++ b/man/systemd-resolved.service.xml @@ -0,0 +1,85 @@ + + + + + + + + + systemd-resolved.service + systemd + + + + Developer + Tom + Gundersen + teg@jklm.no + + + + + + systemd-resolved.service + 8 + + + + systemd-resolved.service + systemd-resolved + Network Name Resolution manager + + + + systemd-resolved.service + /usr/lib/systemd/systemd-resolved + + + + Description + + systemd-networkd is a system + service that manages network name resolution. It does so by + generating /run/systemd/network/resolv.conf, + which may be symlinked from /etc/resolv.conf. + The contents is generated from the global settings in + resolved.conf5, + the per-link static settings in .network files, + and the per-link dynamic settings received over DHCP. See + systemd.network5 + for more details. + + Note that /run/systemd/network/resolv.conf + should not be used directly, but only through a symlink from + /etc/resolv.conf. + + + + See Also + + resolved.conf5, + systemd1, + systemd.network5, + systemd-networkd.service8 + + + + diff --git a/src/network/.gitignore b/src/network/.gitignore index 04bce2e8b..885859648 100644 --- a/src/network/.gitignore +++ b/src/network/.gitignore @@ -1,4 +1,2 @@ /networkd-network-gperf.c /networkd-netdev-gperf.c -/networkd-gperf.c -/networkd.conf diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 8ce2dbd39..5a7472ba2 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -854,8 +854,6 @@ static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) { struct in_addr netmask; struct in_addr gateway; unsigned prefixlen; - struct in_addr *nameservers; - size_t nameservers_size; int r; assert(client); @@ -920,15 +918,6 @@ static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) { link->dhcp_lease = lease; - if (link->network->dhcp_dns) { - r = sd_dhcp_lease_get_dns(lease, &nameservers, &nameservers_size); - if (r >= 0) { - r = manager_update_resolv_conf(link->manager); - if (r < 0) - log_error_link(link, "Failed to update resolv.conf"); - } - } - if (link->network->dhcp_mtu) { uint16_t mtu; diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c index ad36553f2..2e3b4bb88 100644 --- a/src/network/networkd-manager.c +++ b/src/network/networkd-manager.c @@ -19,7 +19,7 @@ along with systemd; If not, see . ***/ -#include +#include #include #include @@ -76,95 +76,6 @@ static int setup_signals(Manager *m) { return 0; } -static int set_fallback_dns(Manager *m, const char *string) { - char *word, *state; - size_t length; - int r; - - assert(m); - assert(string); - - FOREACH_WORD_QUOTED(word, length, string, state) { - _cleanup_address_free_ Address *address = NULL; - Address *tail; - _cleanup_free_ char *addrstr = NULL; - - r = address_new_dynamic(&address); - if (r < 0) - return r; - - addrstr = strndup(word, length); - if (!addrstr) - return -ENOMEM; - - r = net_parse_inaddr(addrstr, &address->family, &address->in_addr); - if (r < 0) { - log_debug("Ignoring invalid DNS address '%s'", addrstr); - continue; - } - - LIST_FIND_TAIL(addresses, m->fallback_dns, tail); - LIST_INSERT_AFTER(addresses, m->fallback_dns, tail, address); - address = NULL; - } - - return 0; -} - -int config_parse_dnsv( - const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - Manager *m = userdata; - Address *address; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(m); - - while ((address = m->fallback_dns)) { - LIST_REMOVE(addresses, m->fallback_dns, address); - address_free(address); - } - - set_fallback_dns(m, rvalue); - - return 0; -} - -static int manager_parse_config_file(Manager *m) { - static const char fn[] = "/etc/systemd/networkd.conf"; - _cleanup_fclose_ FILE *f = NULL; - int r; - - assert(m); - - f = fopen(fn, "re"); - if (!f) { - if (errno == ENOENT) - return 0; - - log_warning("Failed to open configuration file %s: %m", fn); - return -errno; - } - - r = config_parse(NULL, fn, f, "Network\0", config_item_perf_lookup, - (void*) networkd_gperf_lookup, false, false, m); - if (r < 0) - log_warning("Failed to parse configuration file: %s", strerror(-r)); - - return r; -} - int manager_new(Manager **ret) { _cleanup_manager_free_ Manager *m = NULL; int r; @@ -177,14 +88,6 @@ int manager_new(Manager **ret) { if (!m->state_file) return -ENOMEM; - r = set_fallback_dns(m, DNS_SERVERS); - if (r < 0) - return r; - - r = manager_parse_config_file(m); - if (r < 0) - return r; - r = sd_event_default(&m->event); if (r < 0) return r; @@ -241,7 +144,6 @@ void manager_free(Manager *m) { Network *network; NetDev *netdev; Link *link; - Address *address; if (!m) return; @@ -257,11 +159,6 @@ void manager_free(Manager *m) { sd_event_source_unref(m->sigint_event_source); sd_event_unref(m->event); - while ((address = m->fallback_dns)) { - LIST_REMOVE(addresses, m->fallback_dns, address); - address_free(address); - } - while ((link = hashmap_first(m->links))) link_unref(link); hashmap_free(m->links); @@ -522,102 +419,6 @@ int manager_bus_listen(Manager *m) { return 0; } -static void append_dns(FILE *f, struct in_addr *dns, unsigned char family, unsigned *count) { - char buf[INET6_ADDRSTRLEN]; - const char *address; - - address = inet_ntop(family, dns, buf, INET6_ADDRSTRLEN); - if (!address) { - log_warning("Invalid DNS address. Ignoring."); - return; - } - - if (*count == MAXNS) - fputs("# Too many DNS servers configured, the following entries " - "will be ignored\n", f); - - fprintf(f, "nameserver %s\n", address); - - (*count) ++; -} - -int manager_update_resolv_conf(Manager *m) { - _cleanup_free_ char *temp_path = NULL; - _cleanup_fclose_ FILE *f = NULL; - Link *link; - Iterator i; - unsigned count = 0; - const char *domainname = NULL; - int r; - - assert(m); - - r = fopen_temporary("/run/systemd/network/resolv.conf", &f, &temp_path); - if (r < 0) - return r; - - fchmod(fileno(f), 0644); - - fputs("# This file is managed by systemd-networkd(8). Do not edit.\n#\n" - "# Third party programs must not access this file directly, but\n" - "# only through the symlink at /etc/resolv.conf. To manage\n" - "# resolv.conf(5) in a different way, replace the symlink by a\n" - "# static file or a different symlink.\n\n", f); - - HASHMAP_FOREACH(link, m->links, i) { - if (link->dhcp_lease) { - struct in_addr *nameservers; - size_t nameservers_size; - - if (link->network->dhcp_dns) { - r = sd_dhcp_lease_get_dns(link->dhcp_lease, &nameservers, &nameservers_size); - if (r >= 0) { - unsigned j; - - for (j = 0; j < nameservers_size; j++) - append_dns(f, &nameservers[j], AF_INET, &count); - } - } - - if (link->network->dhcp_domainname && !domainname) { - r = sd_dhcp_lease_get_domainname(link->dhcp_lease, &domainname); - if (r >= 0) - fprintf(f, "domain %s\n", domainname); - } - } - } - - HASHMAP_FOREACH(link, m->links, i) { - if (link->network && link->network->dns) { - Address *address; - - LIST_FOREACH(addresses, address, link->network->dns) { - append_dns(f, &address->in_addr.in, - address->family, &count); - } - } - } - - if (!count) { - Address *address; - - LIST_FOREACH(addresses, address, m->fallback_dns) - append_dns(f, &address->in_addr.in, - address->family, &count); - } - - fflush(f); - - if (ferror(f) || rename(temp_path, "/run/systemd/network/resolv.conf") < 0) { - r = -errno; - unlink("/run/systemd/network/resolv.conf"); - unlink(temp_path); - return r; - } - - return 0; -} - int manager_save(Manager *m) { Link *link; Iterator i; diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index 12107c940..1b8856afe 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -235,7 +235,7 @@ int network_apply(Manager *manager, Network *network, Link *link) { link->network = network; if (network->dns) { - r = manager_update_resolv_conf(manager); + r = link_save(link); if (r < 0) return r; } diff --git a/src/network/networkd.c b/src/network/networkd.c index 6b3bf12a4..6985dcaa4 100644 --- a/src/network/networkd.c +++ b/src/network/networkd.c @@ -93,14 +93,6 @@ int main(int argc, char *argv[]) { goto out; } - /* write out empty resolv.conf to avoid a - * dangling symlink */ - r = manager_update_resolv_conf(m); - if (r < 0) { - log_error("Could not create resolv.conf: %s", strerror(-r)); - goto out; - } - sd_notify(false, "READY=1\n" "STATUS=Processing requests..."); diff --git a/src/network/networkd.h b/src/network/networkd.h index 30a29c7b6..3e4e1f2ba 100644 --- a/src/network/networkd.h +++ b/src/network/networkd.h @@ -256,7 +256,6 @@ struct Manager { Hashmap *links; Hashmap *netdevs; LIST_HEAD(Network, networks); - LIST_HEAD(Address, fallback_dns); usec_t network_dirs_ts_usec; struct kmod_ctx *kmod_ctx; @@ -278,18 +277,11 @@ int manager_rtnl_listen(Manager *m); int manager_udev_listen(Manager *m); int manager_bus_listen(Manager *m); -int manager_update_resolv_conf(Manager *m); int manager_save(Manager *m); DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free); #define _cleanup_manager_free_ _cleanup_(manager_freep) -const struct ConfigPerfItem* networkd_gperf_lookup(const char *key, unsigned length); - -int config_parse_dnsv(const char *unit, const char *filename, unsigned line, - const char *section, unsigned section_line, const char *lvalue, - int ltype, const char *rvalue, void *data, void *userdata); - /* NetDev */ int netdev_load(Manager *manager); diff --git a/src/resolve/.gitignore b/src/resolve/.gitignore new file mode 100644 index 000000000..ca3016e6a --- /dev/null +++ b/src/resolve/.gitignore @@ -0,0 +1,2 @@ +/resolved-gperf.c +/resolved.conf diff --git a/src/resolve/Makefile b/src/resolve/Makefile new file mode 120000 index 000000000..d0b0e8e00 --- /dev/null +++ b/src/resolve/Makefile @@ -0,0 +1 @@ +../Makefile \ No newline at end of file diff --git a/src/network/networkd-gperf.gperf b/src/resolve/resolved-gperf.gperf similarity index 55% rename from src/network/networkd-gperf.gperf rename to src/resolve/resolved-gperf.gperf index a5dfc400e..71e998051 100644 --- a/src/network/networkd-gperf.gperf +++ b/src/resolve/resolved-gperf.gperf @@ -1,17 +1,17 @@ %{ #include #include "conf-parser.h" -#include "networkd.h" +#include "resolved.h" %} struct ConfigPerfItem; %null_strings %language=ANSI-C %define slot-name section_and_lvalue -%define hash-function-name networkd_gperf_hash -%define lookup-function-name networkd_gperf_lookup +%define hash-function-name resolved_gperf_hash +%define lookup-function-name resolved_gperf_lookup %readonly-tables %omit-struct-type %struct-type %includes %% -Network.DNS, config_parse_dnsv, 0, offsetof(Manager, fallback_dns) +Resolve.DNS, config_parse_dnsv, 0, offsetof(Manager, fallback_dns) diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c new file mode 100644 index 000000000..ae173991d --- /dev/null +++ b/src/resolve/resolved-manager.c @@ -0,0 +1,320 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2014 Tom Gundersen + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . + ***/ + +#include +#include +#include + +#include "resolved.h" +#include "event-util.h" +#include "network-util.h" +#include "sd-dhcp-lease.h" +#include "dhcp-lease-internal.h" +#include "network-internal.h" +#include "conf-parser.h" +#include "mkdir.h" + +static int set_fallback_dns(Manager *m, const char *string) { + char *word, *state; + size_t length; + int r; + + assert(m); + assert(string); + + FOREACH_WORD_QUOTED(word, length, string, state) { + _cleanup_free_ Address *address = NULL; + Address *tail; + _cleanup_free_ char *addrstr = NULL; + + address = new0(Address, 1); + if (!address) + return -ENOMEM; + + addrstr = strndup(word, length); + if (!addrstr) + return -ENOMEM; + + r = net_parse_inaddr(addrstr, &address->family, &address->in_addr); + if (r < 0) { + log_debug("Ignoring invalid DNS address '%s'", addrstr); + continue; + } + + LIST_FIND_TAIL(addresses, m->fallback_dns, tail); + LIST_INSERT_AFTER(addresses, m->fallback_dns, tail, address); + address = NULL; + } + + return 0; +} + +int config_parse_dnsv( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + Manager *m = userdata; + Address *address; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(m); + + while ((address = m->fallback_dns)) { + LIST_REMOVE(addresses, m->fallback_dns, address); + free(address); + } + + set_fallback_dns(m, rvalue); + + return 0; +} + +static int manager_parse_config_file(Manager *m) { + static const char fn[] = "/etc/systemd/resolved.conf"; + _cleanup_fclose_ FILE *f = NULL; + int r; + + assert(m); + + f = fopen(fn, "re"); + if (!f) { + if (errno == ENOENT) + return 0; + + log_warning("Failed to open configuration file %s: %m", fn); + return -errno; + } + + r = config_parse(NULL, fn, f, "Resolve\0", config_item_perf_lookup, + (void*) resolved_gperf_lookup, false, false, m); + if (r < 0) + log_warning("Failed to parse configuration file: %s", strerror(-r)); + + return r; +} + +int manager_new(Manager **ret) { + _cleanup_manager_free_ Manager *m = NULL; + int r; + + m = new0(Manager, 1); + if (!m) + return -ENOMEM; + + r = set_fallback_dns(m, DNS_SERVERS); + if (r < 0) + return r; + + r = manager_parse_config_file(m); + if (r < 0) + return r; + + r = sd_event_default(&m->event); + if (r < 0) + return r; + + sd_event_add_signal(m->event, NULL, SIGTERM, NULL, NULL); + sd_event_add_signal(m->event, NULL, SIGINT, NULL, NULL); + + sd_event_set_watchdog(m->event, true); + + *ret = m; + m = NULL; + + return 0; +} + +void manager_free(Manager *m) { + Address *address; + + if (!m) + return; + + sd_event_unref(m->event); + + while ((address = m->fallback_dns)) { + LIST_REMOVE(addresses, m->fallback_dns, address); + free(address); + } + + free(m); +} + +static void append_dns(FILE *f, void *dns, unsigned char family, unsigned *count) { + char buf[INET6_ADDRSTRLEN]; + const char *address; + + assert(f); + assert(dns); + assert(count); + + address = inet_ntop(family, dns, buf, INET6_ADDRSTRLEN); + if (!address) { + log_warning("Invalid DNS address. Ignoring."); + return; + } + + if (*count == MAXNS) + fputs("# Too many DNS servers configured, the following entries " + "may be ignored\n", f); + + fprintf(f, "nameserver %s\n", address); + + (*count) ++; +} + +int manager_update_resolv_conf(Manager *m) { + _cleanup_free_ char *temp_path = NULL; + _cleanup_fclose_ FILE *f = NULL; + _cleanup_free_ unsigned *indices = NULL; + Address *address; + unsigned count = 0; + int n, r, i; + + assert(m); + + r = fopen_temporary("/run/systemd/network/resolv.conf", &f, &temp_path); + if (r < 0) + return r; + + fchmod(fileno(f), 0644); + + fputs("# This file is managed by systemd-resolved(8). Do not edit.\n#\n" + "# Third party programs must not access this file directly, but\n" + "# only through the symlink at /etc/resolv.conf. To manage\n" + "# resolv.conf(5) in a different way, replace the symlink by a\n" + "# static file or a different symlink.\n\n", f); + + n = sd_network_get_ifindices(&indices); + if (n < 0) + n = 0; + + for (i = 0; i < n; i++) { + _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL; + struct in_addr *nameservers; + struct in6_addr *nameservers6; + size_t nameservers_size; + + r = sd_network_dhcp_use_dns(indices[i]); + if (r > 0) { + r = sd_network_get_dhcp_lease(indices[i], &lease); + if (r >= 0) { + r = sd_dhcp_lease_get_dns(lease, &nameservers, &nameservers_size); + if (r >= 0) { + unsigned j; + + for (j = 0; j < nameservers_size; j++) + append_dns(f, &nameservers[j], AF_INET, &count); + } + } + } + + r = sd_network_get_dns(indices[i], &nameservers, &nameservers_size); + if (r >= 0) { + unsigned j; + + for (j = 0; j < nameservers_size; j++) + append_dns(f, &nameservers[j], AF_INET, &count); + + free(nameservers); + } + + r = sd_network_get_dns6(indices[i], &nameservers6, &nameservers_size); + if (r >= 0) { + unsigned j; + + for (j = 0; j < nameservers_size; j++) + append_dns(f, &nameservers6[j], AF_INET6, &count); + + free(nameservers6); + } + } + + LIST_FOREACH(addresses, address, m->fallback_dns) + append_dns(f, &address->in_addr, address->family, &count); + + fflush(f); + + if (ferror(f) || rename(temp_path, "/run/systemd/network/resolv.conf") < 0) { + r = -errno; + unlink("/run/systemd/network/resolv.conf"); + unlink(temp_path); + return r; + } + + return 0; +} + +static int manager_network_event_handler(sd_event_source *s, int fd, uint32_t revents, + void *userdata) { + Manager *m = userdata; + int r; + + assert(m); + + r = manager_update_resolv_conf(m); + if (r < 0) + log_warning("Could not update resolv.conf: %s", strerror(-r)); + + sd_network_monitor_flush(m->network_monitor); + + return 0; +} + +int manager_network_monitor_listen(Manager *m) { + _cleanup_event_source_unref_ sd_event_source *event_source = NULL; + _cleanup_network_monitor_unref_ sd_network_monitor *monitor = NULL; + int r, fd, events; + + r = sd_network_monitor_new(NULL, &monitor); + if (r < 0) + return r; + + fd = sd_network_monitor_get_fd(monitor); + if (fd < 0) + return fd; + + events = sd_network_monitor_get_events(monitor); + if (events < 0) + return events; + + r = sd_event_add_io(m->event, &event_source, fd, events, + &manager_network_event_handler, m); + if (r < 0) + return r; + + m->network_monitor = monitor; + m->network_event_source = event_source; + monitor = NULL; + event_source = NULL; + + return 0; +} diff --git a/src/resolve/resolved.c b/src/resolve/resolved.c new file mode 100644 index 000000000..82f43d680 --- /dev/null +++ b/src/resolve/resolved.c @@ -0,0 +1,86 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2014 Tom Gundersen + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "sd-event.h" +#include "sd-daemon.h" + +#include "resolved.h" + +#include "mkdir.h" + +int main(int argc, char *argv[]) { + _cleanup_manager_free_ Manager *m = NULL; + int r; + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + umask(0022); + + if (argc != 1) { + log_error("This program takes no arguments."); + r = -EINVAL; + goto out; + } + + /* Always create the directory where resolv.conf will live */ + r = mkdir_label("/run/systemd/network", 0755); + if (r < 0) + log_error("Could not create runtime directory: %s", + strerror(-r)); + + r = manager_new(&m); + if (r < 0) { + log_error("Could not create manager: %s", strerror(-r)); + goto out; + } + + r = manager_network_monitor_listen(m); + if (r < 0) { + log_error("Could not listen for network events: %s", strerror(-r)); + goto out; + } + + /* write out default resolv.conf to avoid a + * dangling symlink */ + r = manager_update_resolv_conf(m); + if (r < 0) { + log_error("Could not create resolv.conf: %s", strerror(-r)); + goto out; + } + + sd_notify(false, + "READY=1\n" + "STATUS=Processing requests..."); + + r = sd_event_loop(m->event); + if (r < 0) { + log_error("Event loop failed: %s", strerror(-r)); + goto out; + } + +out: + sd_notify(false, + "STATUS=Shutting down..."); + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/network/networkd.conf.in b/src/resolve/resolved.conf.in similarity index 87% rename from src/network/networkd.conf.in rename to src/resolve/resolved.conf.in index efe889a44..a2391954a 100644 --- a/src/network/networkd.conf.in +++ b/src/resolve/resolved.conf.in @@ -5,7 +5,7 @@ # the Free Software Foundation; either version 2.1 of the License, or # (at your option) any later version. # -# See networkd.conf(5) for details +# See resolved.conf(5) for details -[Network] +[Resolve] #DNS=@DNS_SERVERS@ diff --git a/src/resolve/resolved.h b/src/resolve/resolved.h new file mode 100644 index 000000000..984edc76c --- /dev/null +++ b/src/resolve/resolved.h @@ -0,0 +1,69 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2013 Tom Gundersen + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#pragma once + +#include "sd-event.h" +#include "sd-network.h" + +#include "util.h" +#include "list.h" + +typedef struct Address Address; +typedef struct Manager Manager; + +struct Address { + unsigned char family; + + union { + struct in_addr in; + struct in6_addr in6; + } in_addr; + + LIST_FIELDS(Address, addresses); +}; + +struct Manager { + sd_event *event; + + LIST_HEAD(Address, fallback_dns); + + /* network */ + sd_event_source *network_event_source; + sd_network_monitor *network_monitor; +}; + +/* Manager */ + +int manager_new(Manager **ret); +void manager_free(Manager *m); + +int manager_update_resolv_conf(Manager *m); +int manager_network_monitor_listen(Manager *m); + +DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free); +#define _cleanup_manager_free_ _cleanup_(manager_freep) + +const struct ConfigPerfItem* resolved_gperf_lookup(const char *key, unsigned length); + +int config_parse_dnsv(const char *unit, const char *filename, unsigned line, + const char *section, unsigned section_line, const char *lvalue, + int ltype, const char *rvalue, void *data, void *userdata); diff --git a/units/.gitignore b/units/.gitignore index fca17c974..b8c084595 100644 --- a/units/.gitignore +++ b/units/.gitignore @@ -49,6 +49,7 @@ /systemd-readahead-replay.service /systemd-reboot.service /systemd-remount-fs.service +/systemd-resolved.service /systemd-rfkill@.service /systemd-shutdownd.service /systemd-suspend.service diff --git a/units/systemd-resolved.service.in b/units/systemd-resolved.service.in new file mode 100644 index 000000000..f4bbb7c16 --- /dev/null +++ b/units/systemd-resolved.service.in @@ -0,0 +1,21 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Network Name Resolution +Documentation=man:systemd-resolved.service(8) +After=systemd-networkd.service network.service + +[Service] +Type=notify +Restart=always +RestartSec=0 +ExecStart=@rootlibexecdir@/systemd-resolved +CapabilityBoundingSet= + +[Install] +WantedBy=multi-user.target -- 2.30.2