summaryrefslogtreecommitdiff
path: root/src/dns/nm-dns-manager.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/dns/nm-dns-manager.c')
-rw-r--r--src/dns/nm-dns-manager.c339
1 files changed, 140 insertions, 199 deletions
diff --git a/src/dns/nm-dns-manager.c b/src/dns/nm-dns-manager.c
index e048f9bc78..a4fdf5eb09 100644
--- a/src/dns/nm-dns-manager.c
+++ b/src/dns/nm-dns-manager.c
@@ -120,6 +120,7 @@ typedef struct {
bool need_sort:1;
bool dns_touched:1;
bool is_stopped:1;
+ bool use_local_plugin:1;
char *hostname;
guint updates_queue;
@@ -129,7 +130,7 @@ typedef struct {
NMDnsManagerResolvConfManager rc_manager;
char *mode;
- NMDnsPlugin *plugin;
+ GSList *plugins;
NMConfig *config;
} NMDnsManagerPrivate;
@@ -1074,7 +1075,7 @@ _collect_resolv_conf_data (NMDnsManager *self, /* only for logging context, no o
static gboolean
update_dns (NMDnsManager *self,
- gboolean no_caching,
+ NMDnsPlugin *plugin,
GError **error)
{
NMDnsManagerPrivate *priv;
@@ -1088,6 +1089,7 @@ update_dns (NMDnsManager *self,
SpawnResult result = SR_ERROR;
NMConfigData *data;
NMGlobalDnsConfig *global_config;
+ GSList *iter;
g_return_val_if_fail (!error || !*error, FALSE);
@@ -1121,35 +1123,39 @@ update_dns (NMDnsManager *self,
_collect_resolv_conf_data (self, global_config, priv->configs, priv->hostname,
&searches, &options, &nameservers, &nis_servers, &nis_domain);
- /* Let any plugins do their thing first */
- if (priv->plugin) {
- NMDnsPlugin *plugin = priv->plugin;
- const char *plugin_name = nm_dns_plugin_get_name (plugin);
-
- if (no_caching) {
- _LOGD ("update-dns: plugin %s ignored (caching disabled)",
- plugin_name);
- goto skip;
+ if (plugin) {
+ if (nm_dns_plugin_get_state (plugin) == NM_DNS_PLUGIN_STATE_STOPPED) {
+ /* We're reviving a stopped plugin. The failed plugins have to wait until
+ * the next update. */
+ _LOGD ("update-dns: restarting plugin %s", nm_dns_plugin_get_name (plugin));
+ g_signal_handlers_block_by_func (plugin, plugin_state_changed, self);
+ nm_dns_plugin_update (plugin,
+ priv->configs,
+ global_config,
+ priv->hostname);
+ g_signal_handlers_unblock_by_func (plugin, plugin_state_changed, self);
}
- caching = TRUE;
-
- _LOGD ("update-dns: updating plugin %s", plugin_name);
- g_signal_handlers_block_by_func (plugin, plugin_state_changed, self);
- if (!nm_dns_plugin_update (plugin,
- priv->configs,
- global_config,
- priv->hostname)) {
- _LOGW ("update-dns: plugin %s update failed", plugin_name);
-
- /* If the plugin failed to update, we shouldn't write out a local
- * caching DNS configuration to resolv.conf.
- */
- caching = FALSE;
+ }
+
+ for (iter = priv->plugins; iter; iter = iter->next) {
+ NMDnsPlugin *this_plugin = NM_DNS_PLUGIN (iter->data);
+
+ g_signal_handlers_block_by_func (this_plugin, plugin_state_changed, self);
+ if (!plugin) {
+ /* No specific plugin was passed. Update all. */
+ _LOGD ("update-dns: updating plugin %s", nm_dns_plugin_get_name (this_plugin));
+ g_signal_handlers_block_by_func (this_plugin, plugin_state_changed, self);
+ nm_dns_plugin_update (this_plugin,
+ priv->configs,
+ global_config,
+ priv->hostname);
+ g_signal_handlers_unblock_by_func (this_plugin, plugin_state_changed, self);
}
- g_signal_handlers_unblock_by_func (plugin, plugin_state_changed, self);
+ g_signal_handlers_unblock_by_func (this_plugin, plugin_state_changed, self);
- skip:
- ;
+ if ( priv->use_local_plugin
+ && nm_dns_plugin_get_state (this_plugin) != NM_DNS_PLUGIN_STATE_FAILED)
+ caching = TRUE;
}
/* If caching was successful, we only send 127.0.0.1 to /etc/resolv.conf
@@ -1217,21 +1223,21 @@ plugin_state_changed (NMDnsPlugin *plugin, GParamSpec *pspec, gpointer user_data
switch (nm_dns_plugin_get_state (plugin)) {
case NM_DNS_PLUGIN_STATE_STOPPED:
_LOGI ("dns: plugin %s stopped, restarting it", plugin_name);
- if (!update_dns (self, FALSE, &error)) {
- _LOGW ("could not commit DNS changes: %s", error->message);
- g_clear_error (&error);
- }
break;
case NM_DNS_PLUGIN_STATE_FAILED:
_LOGW ("dns: plugin %s failed", plugin_name);
- if (!update_dns (self, TRUE, &error)) {
- _LOGW ("could not commit DNS changes: %s", error->message);
- g_clear_error (&error);
- }
break;
default:
return;
}
+
+ /* Retry the DNS update, so that a stopped plugin could be restarted or
+ * nameserver configuration reverted to a non-caching mode if no plugins
+ * are non-failed. */
+ if (!update_dns (self, plugin, &error)) {
+ _LOGW ("could not commit DNS changes: %s", error->message);
+ g_clear_error (&error);
+ }
}
static void
@@ -1309,7 +1315,7 @@ nm_dns_manager_add_ip_config (NMDnsManager *self,
}
}
- if (!priv->updates_queue && !update_dns (self, FALSE, &error)) {
+ if (!priv->updates_queue && !update_dns (self, NULL, &error)) {
_LOGW ("could not commit DNS changes: %s", error->message);
g_clear_error (&error);
}
@@ -1355,7 +1361,7 @@ nm_dns_manager_remove_ip_config (NMDnsManager *self, gpointer config)
forget_data (self, data);
g_ptr_array_remove_index (priv->configs, i);
- if (!priv->updates_queue && !update_dns (self, FALSE, &error)) {
+ if (!priv->updates_queue && !update_dns (self, NULL, &error)) {
_LOGW ("could not commit DNS changes: %s", error->message);
g_clear_error (&error);
}
@@ -1414,7 +1420,7 @@ nm_dns_manager_set_hostname (NMDnsManager *self,
if (skip_update)
return;
- if (!priv->updates_queue && !update_dns (self, FALSE, &error)) {
+ if (!priv->updates_queue && !update_dns (self, NULL, &error)) {
_LOGW ("could not commit DNS changes: %s", error->message);
g_clear_error (&error);
}
@@ -1431,7 +1437,7 @@ nm_dns_manager_get_resolv_conf_explicit (NMDnsManager *self)
if ( NM_IN_SET (priv->rc_manager, NM_DNS_MANAGER_RESOLV_CONF_MAN_UNMANAGED,
NM_DNS_MANAGER_RESOLV_CONF_MAN_IMMUTABLE)
- || priv->plugin)
+ || priv->plugins)
return FALSE;
return TRUE;
@@ -1484,7 +1490,7 @@ nm_dns_manager_end_updates (NMDnsManager *self, const char *func)
/* Commit all the outstanding changes */
_LOGD ("(%s): committing DNS changes (%d)", func, priv->updates_queue);
- if (!update_dns (self, FALSE, &error)) {
+ if (!update_dns (self, NULL, &error)) {
_LOGW ("could not commit DNS changes: %s", error->message);
g_clear_error (&error);
}
@@ -1492,49 +1498,23 @@ nm_dns_manager_end_updates (NMDnsManager *self, const char *func)
memset (priv->prev_hash, 0, sizeof (priv->prev_hash));
}
-void
-nm_dns_manager_stop (NMDnsManager *self)
-{
- NMDnsManagerPrivate *priv;
- GError *error = NULL;
-
- priv = NM_DNS_MANAGER_GET_PRIVATE (self);
-
- if (priv->is_stopped)
- g_return_if_reached ();
-
- _LOGT ("stopping...");
-
- /* If we're quitting, leave a valid resolv.conf in place, not one
- * pointing to 127.0.0.1 if any plugins were active. Thus update
- * DNS after disposing of all plugins. But if we haven't done any
- * DNS updates yet, there's no reason to touch resolv.conf on shutdown.
- */
- if (priv->dns_touched) {
- if (!update_dns (self, TRUE, &error)) {
- _LOGW ("could not commit DNS changes on shutdown: %s", error->message);
- g_clear_error (&error);
- }
- priv->dns_touched = FALSE;
- }
-
- priv->is_stopped = TRUE;
-}
-
/*****************************************************************************/
-static gboolean
-_clear_plugin (NMDnsManager *self)
+static void
+_clear_plugins (NMDnsManager *self)
{
NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE (self);
+ GSList *iter;
- if (priv->plugin) {
- g_signal_handlers_disconnect_by_func (priv->plugin, plugin_state_changed, self);
- nm_dns_plugin_stop (priv->plugin);
- g_clear_object (&priv->plugin);
- return TRUE;
+ for (iter = priv->plugins; iter; iter = iter->next) {
+ NMDnsPlugin *plugin = NM_DNS_PLUGIN (iter->data);
+
+ g_signal_handlers_disconnect_by_func (plugin, plugin_state_changed, self);
+ nm_dns_plugin_stop (plugin);
}
- return FALSE;
+ g_slist_free (priv->plugins);
+ priv->plugins = NULL;
+ priv->use_local_plugin = FALSE;
}
static NMDnsManagerResolvConfManager
@@ -1586,86 +1566,53 @@ _check_resconf_immutable (NMDnsManagerResolvConfManager rc_manager)
}
}
-static gboolean
-_resolvconf_resolved_managed (void)
+static void
+init_resolv_conf_mode (NMDnsManager *self, gboolean reload_dns_mode)
{
- static const char *const RESOLVED_PATHS[] = {
- "/run/systemd/resolve/resolv.conf",
- "/lib/systemd/resolv.conf",
- "/usr/lib/systemd/resolv.conf",
- };
- struct stat st, st_test;
- guint i;
+ NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE (self);
+ NMDnsManagerResolvConfManager rc_manager = NM_DNS_MANAGER_RESOLV_CONF_MAN_UNKNOWN;
+ gs_strfreev char **mode = NULL;
+ gboolean rc_manager_changed = FALSE;
+ int i;
- if (lstat (_PATH_RESCONF, &st) != 0)
- return FALSE;
+ if (reload_dns_mode) {
+ mode = nm_config_data_get_dns_mode (nm_config_get_data (priv->config));
+
+ _clear_plugins (self);
+ for (i = 0; mode[i]; i++) {
+ NMDnsPlugin *plugin = NULL;
+
+ if (nm_streq0 (mode[i], "systemd-resolved")) {
+ plugin = nm_dns_systemd_resolved_new ();
+ } else if (nm_streq0 (mode[i], "dnsmasq")) {
+ plugin = nm_dns_dnsmasq_new ();
+ } else if (nm_streq0 (mode[i], "unbound")) {
+ plugin = nm_dns_unbound_new ();
+ } else if (nm_streq0 (mode[i], "none")) {
+ rc_manager = NM_DNS_MANAGER_RESOLV_CONF_MAN_UNMANAGED;
+ } else if (!nm_streq0 (mode[i], "default")) {
+ _LOGW ("init: unknown dns mode '%s'", mode[i]);
+ }
- if (S_ISLNK (st.st_mode)) {
- gs_free char *full_path = NULL;
- nm_auto_free char *real_path = NULL;
-
- /* see if resolv.conf is a symlink with a target that is
- * exactly like one of the candidates.
- *
- * This check will work for symlinks, even if the target
- * does not exist and realpath() cannot resolve anything.
- *
- * We want to handle that, because systemd-resolved might not
- * have started yet. */
- full_path = g_file_read_link (_PATH_RESCONF, NULL);
- if (nm_utils_strv_find_first ((char **) RESOLVED_PATHS,
- G_N_ELEMENTS (RESOLVED_PATHS),
- full_path) >= 0)
- return TRUE;
+ if (plugin) {
+ _LOGI ("init: dns=%s%s%s%s",
+ mode[i], NM_PRINT_FMT_QUOTED (plugin, ", plugin=",
+ nm_dns_plugin_get_name (plugin), "", ""));
- /* see if resolv.conf is a symlink that resolves exactly one
- * of the candidate paths.
- *
- * This check will work for symlinks that can be resolved
- * to a realpath, but the actual file might not exist.
- *
- * We want to handle that, because systemd-resolved might not
- * have started yet. */
- real_path = realpath (_PATH_RESCONF, NULL);
- if (nm_utils_strv_find_first ((char **) RESOLVED_PATHS,
- G_N_ELEMENTS (RESOLVED_PATHS),
- real_path) >= 0)
- return TRUE;
+ g_signal_connect (plugin,
+ "notify::" NM_DNS_PLUGIN_STATE,
+ G_CALLBACK (plugin_state_changed),
+ self);
+ priv->plugins = g_slist_append (priv->plugins, plugin);
+ }
- /* fall-through and resolve the symlink, to check the file
- * it points to (below).
- *
- * This check is the most reliable, but it only works if
- * systemd-resolved already started and created the file. */
- if (stat (_PATH_RESCONF, &st) != 0)
- return FALSE;
- }
-
- /* see if resolv.conf resolves to one of the candidate
- * paths (or whether it is hard-linked). */
- for (i = 0; i < G_N_ELEMENTS (RESOLVED_PATHS); i++) {
- if ( stat (RESOLVED_PATHS[i], &st_test) == 0
- && st.st_dev == st_test.st_dev
- && st.st_ino == st_test.st_ino)
- return TRUE;
+ /* Prefer the local plugin only if it's first in the list. */
+ if (i == 0)
+ priv->use_local_plugin = (plugin != NULL);
+ }
}
- return FALSE;
-}
-
-static void
-init_resolv_conf_mode (NMDnsManager *self, gboolean force_reload_plugin)
-{
- NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE (self);
- NMDnsManagerResolvConfManager rc_manager;
- const char *mode;
- gboolean param_changed = FALSE, plugin_changed = FALSE;
-
- mode = nm_config_data_get_dns_mode (nm_config_get_data (priv->config));
-
- if (nm_streq0 (mode, "none"))
- rc_manager = NM_DNS_MANAGER_RESOLV_CONF_MAN_UNMANAGED;
- else {
+ if (rc_manager == NM_DNS_MANAGER_RESOLV_CONF_MAN_UNKNOWN) {
const char *man;
rc_manager = NM_DNS_MANAGER_RESOLV_CONF_MAN_UNKNOWN;
@@ -1698,61 +1645,23 @@ again:
rc_manager = _check_resconf_immutable (rc_manager);
- if ( (!mode && _resolvconf_resolved_managed ())
- || nm_streq0 (mode, "systemd-resolved")) {
- if ( force_reload_plugin
- || !NM_IS_DNS_SYSTEMD_RESOLVED (priv->plugin)) {
- _clear_plugin (self);
- priv->plugin = nm_dns_systemd_resolved_new ();
- plugin_changed = TRUE;
- }
- mode = "systemd-resolved";
- } else if (nm_streq0 (mode, "dnsmasq")) {
- if (force_reload_plugin || !NM_IS_DNS_DNSMASQ (priv->plugin)) {
- _clear_plugin (self);
- priv->plugin = nm_dns_dnsmasq_new ();
- plugin_changed = TRUE;
- }
- } else if (nm_streq0 (mode, "unbound")) {
- if (force_reload_plugin || !NM_IS_DNS_UNBOUND (priv->plugin)) {
- _clear_plugin (self);
- priv->plugin = nm_dns_unbound_new ();
- plugin_changed = TRUE;
- }
- } else {
- if (!NM_IN_STRSET (mode, "none", "default")) {
- if (mode)
- _LOGW ("init: unknown dns mode '%s'", mode);
- mode = "default";
- }
- if (_clear_plugin (self))
- plugin_changed = TRUE;
- }
-
- if (plugin_changed && priv->plugin)
- g_signal_connect (priv->plugin, "notify::" NM_DNS_PLUGIN_STATE, G_CALLBACK (plugin_state_changed), self);
-
g_object_freeze_notify (G_OBJECT (self));
- if (!nm_streq0 (priv->mode, mode)) {
+ if (!nm_streq0 (priv->mode, mode[0])) {
g_free (priv->mode);
- priv->mode = g_strdup (mode);
- param_changed = TRUE;
+ priv->mode = g_strdup (mode[0]);
+ rc_manager_changed = TRUE;
_notify (self, PROP_MODE);
}
if (priv->rc_manager != rc_manager) {
priv->rc_manager = rc_manager;
- param_changed = TRUE;
+ rc_manager_changed = TRUE;
_notify (self, PROP_RC_MANAGER);
}
- if (param_changed || plugin_changed) {
- _LOGI ("init: dns=%s, rc-manager=%s%s%s%s",
- mode, _rc_manager_to_string (rc_manager),
- NM_PRINT_FMT_QUOTED (priv->plugin, ", plugin=",
- nm_dns_plugin_get_name (priv->plugin), "", ""));
- }
+ if (rc_manager_changed)
+ _LOGI ("init: rc-manager=%s", _rc_manager_to_string (rc_manager));
g_object_thaw_notify (G_OBJECT (self));
}
@@ -1775,7 +1684,8 @@ config_changed_cb (NMConfig *config,
* is immutable, thus, without the configuration changing, we always want to
* re-configure the mode. */
init_resolv_conf_mode (self,
- NM_FLAGS_ANY (changes, NM_CONFIG_CHANGE_CAUSE_SIGHUP
+ NM_FLAGS_ANY (changes, NM_CONFIG_CHANGE_DNS_MODE
+ | NM_CONFIG_CHANGE_CAUSE_SIGHUP
| NM_CONFIG_CHANGE_CAUSE_DNS_FULL));
}
@@ -1786,7 +1696,7 @@ config_changed_cb (NMConfig *config,
NM_CONFIG_CHANGE_DNS_MODE |
NM_CONFIG_CHANGE_RC_MANAGER |
NM_CONFIG_CHANGE_GLOBAL_DNS_CONFIG)) {
- if (!update_dns (self, FALSE, &error)) {
+ if (!update_dns (self, NULL, &error)) {
_LOGW ("could not commit DNS changes: %s", error->message);
g_clear_error (&error);
}
@@ -2028,6 +1938,39 @@ nm_dns_manager_init (NMDnsManager *self)
init_resolv_conf_mode (self, TRUE);
}
+void
+nm_dns_manager_stop (NMDnsManager *self)
+{
+ NMDnsManagerPrivate *priv;
+ GError *error = NULL;
+
+ priv = NM_DNS_MANAGER_GET_PRIVATE (self);
+
+ if (priv->is_stopped)
+ g_return_if_reached ();
+
+ _LOGT ("stopping...");
+
+ /* Clear plugins first so that subsequent update_dns reverts to a
+ * non-caching configuration. */
+ _clear_plugins (self);
+
+ /* If we're quitting, leave a valid resolv.conf in place, not one
+ * pointing to 127.0.0.1 if any plugins were active. Thus update
+ * DNS after disposing of all plugins. But if we haven't done any
+ * DNS updates yet, there's no reason to touch resolv.conf on shutdown.
+ */
+ if (priv->dns_touched) {
+ if (!update_dns (self, NULL, &error)) {
+ _LOGW ("could not commit DNS changes on shutdown: %s", error->message);
+ g_clear_error (&error);
+ }
+ priv->dns_touched = FALSE;
+ }
+
+ priv->is_stopped = TRUE;
+}
+
static void
dispose (GObject *object)
{
@@ -2041,8 +1984,6 @@ dispose (GObject *object)
if (!priv->is_stopped)
nm_dns_manager_stop (self);
- _clear_plugin (self);
-
if (priv->config) {
g_signal_handlers_disconnect_by_func (priv->config, config_changed_cb, self);
g_clear_object (&priv->config);