diff options
author | Thomas Haller <thaller@redhat.com> | 2016-03-23 09:12:04 +0100 |
---|---|---|
committer | Thomas Haller <thaller@redhat.com> | 2016-03-23 09:12:04 +0100 |
commit | 2f6517df432bcbc1df6d74d824daa7c3ecdb3c2e (patch) | |
tree | 949dcf065f64e8ca67e5703381f21b592e400505 | |
parent | 3bcc0e5cea510746c14e80dae3da5e6f7e863706 (diff) | |
parent | a0f796738642b81b07ebc465c267a2da67256525 (diff) | |
download | NetworkManager-2f6517df432bcbc1df6d74d824daa7c3ecdb3c2e.tar.gz |
dns: merge branch 'th/dns-resolv-conf-file-bgo764004'
https://mail.gnome.org/archives/networkmanager-list/2016-March/msg00123.html
https://bugzilla.gnome.org/show_bug.cgi?id=764004
-rw-r--r-- | man/NetworkManager.conf.xml.in | 10 | ||||
-rw-r--r-- | src/dns-manager/nm-dns-manager.c | 238 | ||||
-rw-r--r-- | src/dns-manager/nm-dns-manager.h | 10 |
3 files changed, 175 insertions, 83 deletions
diff --git a/man/NetworkManager.conf.xml.in b/man/NetworkManager.conf.xml.in index cf3421dacd..2a847f8f5e 100644 --- a/man/NetworkManager.conf.xml.in +++ b/man/NetworkManager.conf.xml.in @@ -288,9 +288,13 @@ no-auto-default=* <term><varname>rc-manager</varname></term> <listitem><para>Set the <filename>resolv.conf</filename> management mode. The default value depends on how NetworkManager - was built.</para> - <para><literal>none</literal>: NetworkManager will directly - write changes to <filename>resolv.conf</filename>.</para> + was built. Regardless of this setting, NetworkManager will + always write resolv.conf to its runtime state directory.</para> + <para><literal>none</literal>: NetworkManager will symlink + <filename>/etc/resolv.conf</filename> to its private + resolv.conf file in the runtime state directory.</para> + <para><literal>file</literal>: NetworkManager will write + <filename>/etc/resolv.conf</filename> as file.</para> <para><literal>resolvconf</literal>: NetworkManager will run resolvconf to update the DNS configuration.</para> <para><literal>netconfig</literal>: NetworkManager will run diff --git a/src/dns-manager/nm-dns-manager.c b/src/dns-manager/nm-dns-manager.c index 79d345bed3..ec83e600d5 100644 --- a/src/dns-manager/nm-dns-manager.c +++ b/src/dns-manager/nm-dns-manager.c @@ -100,14 +100,11 @@ NM_DEFINE_SINGLETON_INSTANCE (NMDnsManager); const NMDnsManager *const __self = (self); \ \ _nm_log (__level, _NMLOG_DOMAIN, 0, \ - "%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \ - ((__self == singleton_instance) \ - ? _NMLOG_PREFIX_NAME \ - : ({ \ - g_snprintf (__prefix, sizeof (__prefix), "%s[%p]", _NMLOG_PREFIX_NAME, __self); \ - __prefix; \ - }) \ - ) \ + "%s%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \ + _NMLOG_PREFIX_NAME, \ + ((!__self || __self == singleton_instance) \ + ? "" \ + : nm_sprintf_buf (__prefix, "[%p]", __self)) \ _NM_UTILS_MACRO_REST (__VA_ARGS__)); \ } \ } G_STMT_END @@ -128,6 +125,9 @@ typedef struct { NMDnsManagerResolvConfMode resolv_conf_mode; NMDnsManagerResolvConfManager rc_manager; + char *last_mode; + bool last_immutable:1; + bool mode_initialized:1; NMDnsPlugin *plugin; NMConfig *config; @@ -164,6 +164,15 @@ typedef struct { GPtrArray *nis_servers; } NMResolvConfData; +NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_rc_manager_to_string, NMDnsManagerResolvConfManager, + NM_UTILS_LOOKUP_DEFAULT_WARN (NULL), + NM_UTILS_LOOKUP_STR_ITEM (NM_DNS_MANAGER_RESOLV_CONF_MAN_NONE, "none"), + NM_UTILS_LOOKUP_STR_ITEM (NM_DNS_MANAGER_RESOLV_CONF_MAN_FILE, "file"), + NM_UTILS_LOOKUP_STR_ITEM (NM_DNS_MANAGER_RESOLV_CONF_MAN_RESOLVCONF, "resolvconf"), + NM_UTILS_LOOKUP_STR_ITEM (NM_DNS_MANAGER_RESOLV_CONF_MAN_NETCONFIG, "netconfig"), + NM_UTILS_LOOKUP_ITEM_IGNORE (_NM_DNS_MANAGER_RESOLV_CONF_MAN_INTERNAL_ONLY), +); + static void add_string_item (GPtrArray *array, const char *str) { @@ -406,12 +415,10 @@ dispatch_netconfig (NMDnsManager *self, return SR_SUCCESS; } -static gboolean -write_resolv_conf (FILE *f, - char **searches, - char **nameservers, - char **options, - GError **error) +static char * +create_resolv_conf (char **searches, + char **nameservers, + char **options) { gs_free char *searches_str = NULL; gs_free char *nameservers_str = NULL; @@ -439,9 +446,9 @@ write_resolv_conf (FILE *f, for (i = 0; i < num; i++) { if (i == 3) { g_string_append (str, "# "); - g_string_append (str, _("NOTE: the libc resolver may not support more than 3 nameservers.")); + g_string_append (str, "NOTE: the libc resolver may not support more than 3 nameservers."); g_string_append (str, "\n# "); - g_string_append (str, _("The nameservers listed below may not be recognized.")); + g_string_append (str, "The nameservers listed below may not be recognized."); g_string_append_c (str, '\n'); } @@ -452,10 +459,18 @@ write_resolv_conf (FILE *f, nameservers_str = g_string_free (str, FALSE); } - if (fprintf (f, "# Generated by NetworkManager\n%s%s%s", - searches_str ? searches_str : "", - nameservers_str ? nameservers_str : "", - options_str ? options_str : "") < 0) { + return g_strdup_printf ("# Generated by NetworkManager\n%s%s%s", + searches_str ?: "", + nameservers_str ?: "", + options_str ?: ""); +} + +static gboolean +write_resolv_conf_contents (FILE *f, + const char *content, + GError **error) +{ + if (fprintf (f, "%s", content) < 0) { g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED, @@ -467,6 +482,19 @@ write_resolv_conf (FILE *f, return TRUE; } +static gboolean +write_resolv_conf (FILE *f, + char **searches, + char **nameservers, + char **options, + GError **error) +{ + gs_free char *content = NULL; + + content = create_resolv_conf (searches, nameservers, options); + return write_resolv_conf_contents (f, content, error); +} + static SpawnResult dispatch_resolvconf (NMDnsManager *self, char **searches, @@ -539,18 +567,22 @@ update_resolv_conf (NMDnsManager *self, char **nameservers, char **options, GError **error, - gboolean install_etc) + NMDnsManagerResolvConfManager rc_manager) { FILE *f; struct stat st; gboolean success; + gs_free char *content = NULL; + SpawnResult write_file_result = SR_SUCCESS; /* If we are not managing /etc/resolv.conf and it points to * MY_RESOLV_CONF, don't write the private DNS configuration to * MY_RESOLV_CONF otherwise we would overwrite the changes done by * some external application. - */ - if (!install_etc) { + * + * This is the only situation, where we don't try to update our + * internal resolv.conf file. */ + if (rc_manager == _NM_DNS_MANAGER_RESOLV_CONF_MAN_INTERNAL_ONLY) { gs_free char *path = g_file_read_link (_PATH_RESCONF, NULL); if (g_strcmp0 (path, MY_RESOLV_CONF) == 0) { @@ -560,6 +592,18 @@ update_resolv_conf (NMDnsManager *self, } } + content = create_resolv_conf (searches, nameservers, options); + + if (rc_manager == NM_DNS_MANAGER_RESOLV_CONF_MAN_FILE) { + /* we first write to /etc/resolv.conf directly. If that fails, + * we still continue to write to runstatedir but remember the + * error. */ + if (!g_file_set_contents (_PATH_RESCONF, content, -1, error)) { + write_file_result = SR_ERROR; + error = NULL; + } + } + if ((f = fopen (MY_RESOLV_CONF_TMP, "w")) == NULL) { g_set_error (error, NM_MANAGER_ERROR, @@ -570,7 +614,7 @@ update_resolv_conf (NMDnsManager *self, return SR_ERROR; } - success = write_resolv_conf (f, searches, nameservers, options, error); + success = write_resolv_conf_contents (f, content, error); if (fclose (f) < 0) { if (success) { @@ -598,7 +642,10 @@ update_resolv_conf (NMDnsManager *self, return SR_ERROR; } - if (!install_etc) + if (rc_manager == NM_DNS_MANAGER_RESOLV_CONF_MAN_FILE) + return write_file_result; + + if (rc_manager != NM_DNS_MANAGER_RESOLV_CONF_MAN_NONE) return SR_SUCCESS; /* A symlink pointing to NM's own resolv.conf (MY_RESOLV_CONF) is always @@ -920,7 +967,7 @@ update_dns (NMDnsManager *self, nis_domain = rc.nis_domain; /* Let any plugins do their thing first */ - if (update && priv->plugin) { + if (priv->plugin) { NMDnsPlugin *plugin = priv->plugin; const char *plugin_name = nm_dns_plugin_get_name (plugin); GSList *vpn_configs = NULL, *dev_configs = NULL, *other_configs = NULL; @@ -973,7 +1020,8 @@ update_dns (NMDnsManager *self, if (update) { switch (priv->rc_manager) { case NM_DNS_MANAGER_RESOLV_CONF_MAN_NONE: - result = update_resolv_conf (self, searches, nameservers, options, error, TRUE); + case NM_DNS_MANAGER_RESOLV_CONF_MAN_FILE: + result = update_resolv_conf (self, searches, nameservers, options, error, priv->rc_manager); resolv_conf_updated = TRUE; break; case NM_DNS_MANAGER_RESOLV_CONF_MAN_RESOLVCONF: @@ -990,7 +1038,7 @@ update_dns (NMDnsManager *self, if (result == SR_NOTFOUND) { _LOGD ("update-dns: program not available, writing to resolv.conf"); g_clear_error (error); - result = update_resolv_conf (self, searches, nameservers, options, error, TRUE); + result = update_resolv_conf (self, searches, nameservers, options, error, NM_DNS_MANAGER_RESOLV_CONF_MAN_NONE); resolv_conf_updated = TRUE; } } @@ -998,7 +1046,7 @@ update_dns (NMDnsManager *self, /* Unless we've already done it, update private resolv.conf in NMRUNDIR ignoring any errors */ if (!resolv_conf_updated) - update_resolv_conf (self, searches, nameservers, options, NULL, FALSE); + update_resolv_conf (self, searches, nameservers, options, NULL, _NM_DNS_MANAGER_RESOLV_CONF_MAN_INTERNAL_ONLY); /* signal that resolv.conf was changed */ if (update && result == SR_SUCCESS) @@ -1318,70 +1366,105 @@ nm_dns_manager_end_updates (NMDnsManager *self, const char *func) /******************************************************************/ +static bool +_get_resconf_immutable (int *immutable_cached) +{ + int fd, flags; + int immutable; + + immutable = *immutable_cached; + if (!NM_IN_SET (immutable, FALSE, TRUE)) { + immutable = FALSE; + fd = open (_PATH_RESCONF, O_RDONLY); + if (fd != -1) { + if (ioctl (fd, FS_IOC_GETFLAGS, &flags) != -1) + immutable = NM_FLAGS_HAS (flags, FS_IMMUTABLE_FL); + close (fd); + } + *immutable_cached = immutable; + } + return immutable; +} + NM_DEFINE_SINGLETON_GETTER (NMDnsManager, nm_dns_manager_get, NM_TYPE_DNS_MANAGER); static void init_resolv_conf_mode (NMDnsManager *self) { NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE (self); - const char *mode; - int fd, flags; - - g_clear_object (&priv->plugin); + const char *mode, *mode_unknown; + int immutable = -1; mode = nm_config_data_get_dns_mode (nm_config_get_data (priv->config)); - if (!g_strcmp0 (mode, "none")) { - priv->resolv_conf_mode = NM_DNS_MANAGER_RESOLV_CONF_UNMANAGED; - goto out; + + if ( priv->mode_initialized + && nm_streq0 (mode, priv->last_mode) + && ( nm_streq0 (mode, "none") + || priv->last_immutable == _get_resconf_immutable (&immutable))) { + /* we call init_resolv_conf_mode() on every SIGHUP to possibly reload + * when either "mode" or "immutable" changed. However, we don't want to + * re-create the plugin, when the paramters didn't actually change. So + * detect that we would recreate the same plugin and return early. */ + return; } - fd = open (_PATH_RESCONF, O_RDONLY); - if (fd != -1) { - if (ioctl (fd, FS_IOC_GETFLAGS, &flags) == -1) - flags = 0; - close (fd); + priv->mode_initialized = TRUE; + g_free (priv->last_mode); + priv->last_mode = g_strdup (mode); + priv->last_immutable = FALSE; + g_clear_object (&priv->plugin); + priv->resolv_conf_mode = NM_DNS_MANAGER_RESOLV_CONF_UNMANAGED; - if (flags & FS_IMMUTABLE_FL) { - _LOGI ("set resolv-conf-mode: none -- " _PATH_RESCONF " is immutable"); - priv->resolv_conf_mode = NM_DNS_MANAGER_RESOLV_CONF_UNMANAGED; - return; - } + if (nm_streq0 (mode, "none")) { + _LOGI ("%s%s", "set resolv-conf-mode: ", "none"); + return; } - if (!g_strcmp0 (mode, "dnsmasq")) { - priv->resolv_conf_mode = NM_DNS_MANAGER_RESOLV_CONF_PROXY; - priv->plugin = nm_dns_dnsmasq_new (); - } else if (!g_strcmp0 (mode, "unbound")) { - priv->resolv_conf_mode = NM_DNS_MANAGER_RESOLV_CONF_PROXY; - priv->plugin = nm_dns_unbound_new (); - } else { - priv->resolv_conf_mode = NM_DNS_MANAGER_RESOLV_CONF_EXPLICIT; - if (mode && g_strcmp0 (mode, "default") != 0) { - _LOGW ("set resolve-conf-mode: default -- unknown configuration '%s'", mode); - return; - } - mode = "default"; - } + priv->last_immutable = _get_resconf_immutable (&immutable); + + if (NM_IN_STRSET (mode, "dnsmasq", "unbound")) { + if (!immutable) + priv->resolv_conf_mode = NM_DNS_MANAGER_RESOLV_CONF_PROXY; + if (nm_streq (mode, "dnsmasq")) + priv->plugin = nm_dns_dnsmasq_new (); + else + priv->plugin = nm_dns_unbound_new (); - if (priv->plugin) { g_signal_connect (priv->plugin, NM_DNS_PLUGIN_FAILED, G_CALLBACK (plugin_failed), self); g_signal_connect (priv->plugin, NM_DNS_PLUGIN_CHILD_QUIT, G_CALLBACK (plugin_child_quit), self); + + _NMLOG (immutable ? LOGL_WARN : LOGL_INFO, + "%s%s%s%s%s%s", + "set resolv-conf-mode: ", + immutable ? "none" : mode, + ", plugin=\"", nm_dns_plugin_get_name (priv->plugin), "\"", + immutable ? ", resolv.conf immutable" : ""); + return; } -out: - _LOGI ("set resolv-conf-mode: %s%s%s%s", mode, - NM_PRINT_FMT_QUOTED (priv->plugin, ", plugin=\"", nm_dns_plugin_get_name (priv->plugin), "\"", "")); + if (!immutable) + priv->resolv_conf_mode = NM_DNS_MANAGER_RESOLV_CONF_EXPLICIT; + + mode_unknown = mode && !nm_streq (mode, "default") ? mode : NULL; + _NMLOG (mode_unknown ? LOGL_WARN : LOGL_INFO, + "%s%s%s%s%s%s", + "set resolv-conf-mode: ", + immutable ? "none" : "default", + NM_PRINT_FMT_QUOTED (mode_unknown, " -- unknown configuration '", mode_unknown, "'", ""), + immutable ? ", resolv.conf immutable" : ""); } static void init_resolv_conf_manager (NMDnsManager *self) { NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE (self); - const char *man, *desc = ""; + const char *man; man = nm_config_data_get_rc_manager (nm_config_get_data (priv->config)); if (!g_strcmp0 (man, "none")) priv->rc_manager = NM_DNS_MANAGER_RESOLV_CONF_MAN_NONE; + else if (nm_streq0 (man, "file")) + priv->rc_manager = NM_DNS_MANAGER_RESOLV_CONF_MAN_FILE; else if (!g_strcmp0 (man, "resolvconf")) priv->rc_manager = NM_DNS_MANAGER_RESOLV_CONF_MAN_RESOLVCONF; else if (!g_strcmp0 (man, "netconfig")) @@ -1398,19 +1481,7 @@ init_resolv_conf_manager (NMDnsManager *self) _LOGW ("unknown resolv.conf manager '%s'", man); } - switch (priv->rc_manager) { - case NM_DNS_MANAGER_RESOLV_CONF_MAN_RESOLVCONF: - desc = "resolvconf"; - break; - case NM_DNS_MANAGER_RESOLV_CONF_MAN_NETCONFIG: - desc = "netconfig"; - break; - case NM_DNS_MANAGER_RESOLV_CONF_MAN_NONE: - desc = "none"; - break; - } - - _LOGI ("using resolv.conf manager '%s'", desc); + _LOGI ("using resolv.conf manager '%s'", _rc_manager_to_string (priv->rc_manager)); } static void @@ -1422,8 +1493,15 @@ config_changed_cb (NMConfig *config, { GError *error = NULL; - if (NM_FLAGS_HAS (changes, NM_CONFIG_CHANGE_DNS_MODE)) + if (NM_FLAGS_ANY (changes, NM_CONFIG_CHANGE_DNS_MODE | + NM_CONFIG_CHANGE_SIGHUP)) { + /* reload the resolv-conf mode also on SIGHUP (when DNS_MODE didn't change). + * The reason is, that the configuration also depends on whether resolv.conf + * is immutable, thus, without the configuration changing, we always want to + * re-configure the mode. */ init_resolv_conf_mode (self); + } + if (NM_FLAGS_HAS (changes, NM_CONFIG_CHANGE_RC_MANAGER)) init_resolv_conf_manager (self); @@ -1474,6 +1552,8 @@ dispose (GObject *object) g_clear_object (&priv->plugin); } + g_clear_pointer (&priv->last_mode, g_free); + /* 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 diff --git a/src/dns-manager/nm-dns-manager.h b/src/dns-manager/nm-dns-manager.h index 7a55f1a297..dd5c9e9879 100644 --- a/src/dns-manager/nm-dns-manager.h +++ b/src/dns-manager/nm-dns-manager.h @@ -101,7 +101,13 @@ typedef enum { /** * NMDnsManagerResolvConfManager - * @NM_DNS_MANAGER_RESOLV_CONF_MAN_NONE: NM directly writes resolv.conf + * @_NM_DNS_MANAGER_RESOLV_CONF_MAN_INTERNAL_ONLY: dummy-manager + * to not write resolv.conf at all, only the internal file in + * NM's run state directory. + * @NM_DNS_MANAGER_RESOLV_CONF_MAN_NONE: NM writes resolv.conf + * by symlinking it to the run state directory. + * @NM_DNS_MANAGER_RESOLV_CONF_MAN_FILE: Like NONE, but instead of symlinking + * resolv.conf, write it as a file. * @NM_DNS_MANAGER_RESOLV_CONF_MAN_RESOLVCONF: NM is managing resolv.conf through resolvconf * @NM_DNS_MANAGER_RESOLV_CONF_MAN_NETCONFIG: NM is managing resolv.conf @@ -110,7 +116,9 @@ typedef enum { * NMDnsManager's management of resolv.conf */ typedef enum { + _NM_DNS_MANAGER_RESOLV_CONF_MAN_INTERNAL_ONLY, NM_DNS_MANAGER_RESOLV_CONF_MAN_NONE, + NM_DNS_MANAGER_RESOLV_CONF_MAN_FILE, NM_DNS_MANAGER_RESOLV_CONF_MAN_RESOLVCONF, NM_DNS_MANAGER_RESOLV_CONF_MAN_NETCONFIG, } NMDnsManagerResolvConfManager; |