summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--configure.ac1
-rw-r--r--man/NetworkManager.conf.xml20
-rw-r--r--man/common.ent.in1
-rw-r--r--src/dns/nm-dns-manager.c93
4 files changed, 50 insertions, 65 deletions
diff --git a/configure.ac b/configure.ac
index b5ca3b4162..611ef582ed 100644
--- a/configure.ac
+++ b/configure.ac
@@ -89,6 +89,7 @@ AC_SUBST(nmlibdir, '${prefix}'/lib/$PACKAGE, [NetworkManager library directory])
AC_SUBST(nmdatadir, '${datadir}'/$PACKAGE, [NetworkManager shared data directory])
AC_SUBST(nmstatedir, '${localstatedir}'/lib/$PACKAGE, [NetworkManager persistent state directory])
AC_SUBST(nmrundir, '${runstatedir}'/$PACKAGE, [NetworkManager runtime state directory])
+AC_SUBST(nmrundir_, "${runstatedir}/$PACKAGE", [NetworkManager runtime state directory (expanded)])
AC_GNU_SOURCE
AC_CHECK_FUNCS([__secure_getenv secure_getenv])
diff --git a/man/NetworkManager.conf.xml b/man/NetworkManager.conf.xml
index 8c93ac4c06..7ca496bfb0 100644
--- a/man/NetworkManager.conf.xml
+++ b/man/NetworkManager.conf.xml
@@ -321,15 +321,17 @@ no-auto-default=*
options, and this version of NetworkManager was build with a default of
"<literal>&NM_CONFIG_DEFAULT_MAIN_RC_MANAGER;</literal>".
Regardless of this setting, NetworkManager will
- always write resolv.conf to its runtime state directory.</para>
- <para><literal>symlink</literal>: NetworkManager will symlink
- <filename>/etc/resolv.conf</filename> to its private
- resolv.conf file in the runtime state directory. If
- <filename>/etc/resolv.conf</filename>
- already is a symlink pointing to a different location, the file
- will not be modified. This allows the user to disable managing
- by pointing the link <filename>/etc/resolv.conf</filename> to
- somewhere else.</para>
+ always write resolv.conf to its runtime state directory
+ <filename>&nmrundir;/resolv.conf</filename>.</para>
+ <para><literal>symlink</literal>: If <filename>/etc/resolv.conf</filename> is
+ a regular file, NetworkManager will replace the file on update. If
+ <filename>/etc/resolv.conf</filename> is instead a symlink, NetworkManager
+ will leave it alone. Unless the symlink points to the internal file
+ <filename>&nmrundir;/resolv.conf</filename>,
+ in which case the symlink will be updated to emit an inotify notification.
+ This allows the user to conveniently instruct NetworkManager not
+ to manage <filename>/etc/resolv.conf</filename> by replacing it with
+ a symlink.</para>
<para><literal>file</literal>: NetworkManager will write
<filename>/etc/resolv.conf</filename> as file. If it finds
a symlink, it will follow the symlink and update the target
diff --git a/man/common.ent.in b/man/common.ent.in
index 4c3310c44b..b3a4bca8fe 100644
--- a/man/common.ent.in
+++ b/man/common.ent.in
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8" ?>
<!ENTITY NM_VERSION "@NM_VERSION@">
<!ENTITY sysconfdir "@sysconfdir@">
+<!ENTITY nmrundir "@nmrundir_@">
<!ENTITY NM_CONFIG_DEFAULT_MAIN_AUTH_POLKIT_TEXT "@NM_CONFIG_DEFAULT_MAIN_AUTH_POLKIT_TEXT@">
<!ENTITY NM_CONFIG_DEFAULT_LOGGING_BACKEND_TEXT "@NM_CONFIG_DEFAULT_LOGGING_BACKEND_TEXT@">
<!ENTITY NM_CONFIG_DEFAULT_LOGGING_AUDIT_TEXT "@NM_CONFIG_DEFAULT_LOGGING_AUDIT_TEXT@">
diff --git a/src/dns/nm-dns-manager.c b/src/dns/nm-dns-manager.c
index c990243ec7..ab41af9496 100644
--- a/src/dns/nm-dns-manager.c
+++ b/src/dns/nm-dns-manager.c
@@ -668,6 +668,20 @@ dispatch_resolvconf (NMDnsManager *self,
return success ? SR_SUCCESS : SR_ERROR;
}
+static const char *
+_read_link_cached (const char *path, gboolean *is_cached, char **cached)
+{
+ nm_assert (is_cached);
+ nm_assert (cached);
+
+ if (*is_cached)
+ return *cached;
+
+ nm_assert (!*cached);
+ *is_cached = TRUE;
+ return (*cached = g_file_read_link (path, NULL));
+}
+
#define MY_RESOLV_CONF NMRUNDIR "/resolv.conf"
#define MY_RESOLV_CONF_TMP MY_RESOLV_CONF ".tmp"
#define RESOLV_CONF_TMP "/etc/.resolv.conf.NetworkManager"
@@ -681,13 +695,14 @@ update_resolv_conf (NMDnsManager *self,
NMDnsManagerResolvConfManager rc_manager)
{
FILE *f;
- struct stat st;
gboolean success;
gs_free char *content = NULL;
SpawnResult write_file_result = SR_SUCCESS;
int errsv;
const char *rc_path = _PATH_RESCONF;
nm_auto_free char *rc_path_real = NULL;
+ gboolean resconf_link_cached = FALSE;
+ gs_free char *resconf_link = NULL;
/* If we are not managing /etc/resolv.conf and it points to
* MY_RESOLV_CONF, don't write the private DNS configuration to
@@ -697,9 +712,8 @@ update_resolv_conf (NMDnsManager *self,
* 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_UNMANAGED) {
- gs_free char *path = g_file_read_link (_PATH_RESCONF, NULL);
-
- if (g_strcmp0 (path, MY_RESOLV_CONF) == 0) {
+ if (nm_streq0 (_read_link_cached (_PATH_RESCONF, &resconf_link_cached, &resconf_link),
+ MY_RESOLV_CONF)) {
_LOGD ("update-resolv-conf: not updating " _PATH_RESCONF
" since it points to " MY_RESOLV_CONF);
return SR_SUCCESS;
@@ -708,12 +722,16 @@ update_resolv_conf (NMDnsManager *self,
content = create_resolv_conf (searches, nameservers, options);
- if (rc_manager == NM_DNS_MANAGER_RESOLV_CONF_MAN_FILE) {
+ if ( rc_manager == NM_DNS_MANAGER_RESOLV_CONF_MAN_FILE
+ || ( rc_manager == NM_DNS_MANAGER_RESOLV_CONF_MAN_SYMLINK
+ && !_read_link_cached (_PATH_RESCONF, &resconf_link_cached, &resconf_link))) {
GError *local = NULL;
- rc_path_real = realpath (rc_path, NULL);
- if (rc_path_real)
- rc_path = rc_path_real;
+ if (rc_manager == NM_DNS_MANAGER_RESOLV_CONF_MAN_FILE) {
+ rc_path_real = realpath (rc_path, NULL);
+ if (rc_path_real)
+ rc_path = rc_path_real;
+ }
/* we first write to /etc/resolv.conf directly. If that fails,
* we still continue to write to runstatedir but remember the
@@ -788,60 +806,23 @@ update_resolv_conf (NMDnsManager *self,
return write_file_result;
}
- if (rc_manager != NM_DNS_MANAGER_RESOLV_CONF_MAN_SYMLINK) {
+ if ( rc_manager != NM_DNS_MANAGER_RESOLV_CONF_MAN_SYMLINK
+ || !_read_link_cached (_PATH_RESCONF, &resconf_link_cached, &resconf_link)) {
_LOGT ("update-resolv-conf: write internal file %s succeeded", MY_RESOLV_CONF);
return SR_SUCCESS;
}
- /* A symlink pointing to NM's own resolv.conf (MY_RESOLV_CONF) is always
- * overwritten to ensure that changes are indicated with inotify. Symlinks
- * pointing to any other file are never overwritten.
- */
- if (lstat (_PATH_RESCONF, &st) != 0) {
- errsv = errno;
- if (errsv != ENOENT) {
- /* NM cannot read /etc/resolv.conf */
- _LOGT ("update-resolv-conf: write internal file %s succeeded but lstat(%s) failed (%s)",
- MY_RESOLV_CONF, _PATH_RESCONF, g_strerror (errsv));
- g_set_error (error,
- NM_MANAGER_ERROR,
- NM_MANAGER_ERROR_FAILED,
- "Could not lstat %s: %s",
- _PATH_RESCONF,
- g_strerror (errsv));
- return SR_ERROR;
- }
- } else {
- if (S_ISLNK (st.st_mode)) {
- if (stat (_PATH_RESCONF, &st) != -1) {
- gs_free char *path = g_file_read_link (_PATH_RESCONF, NULL);
-
- if (!path || !nm_streq (path, MY_RESOLV_CONF)) {
- /* It's not NM's symlink; do nothing */
- _LOGT ("update-resolv-conf: write internal file %s succeeded "
- "but don't update %s as it points to %s",
- MY_RESOLV_CONF, _PATH_RESCONF, path ?: "");
- return SR_SUCCESS;
- }
-
- /* resolv.conf is a symlink owned by NM and the target is accessible
- */
- } else {
- /* resolv.conf is a symlink but the target is not accessible;
- * some other program is probably managing resolv.conf and
- * NM should not touch it.
- */
- _LOGT ("update-resolv-conf: write internal file %s succeeded "
- "but don't update %s as the symlinks points somewhere else",
- MY_RESOLV_CONF, _PATH_RESCONF);
- return SR_SUCCESS;
- }
- }
+ if (!nm_streq0 (_read_link_cached (_PATH_RESCONF, &resconf_link_cached, &resconf_link),
+ MY_RESOLV_CONF)) {
+ _LOGT ("update-resolv-conf: write internal file %s succeeded (don't touch symlink %s linking to %s)",
+ MY_RESOLV_CONF, _PATH_RESCONF,
+ _read_link_cached (_PATH_RESCONF, &resconf_link_cached, &resconf_link));
+ return SR_SUCCESS;
}
- /* By this point, either /etc/resolv.conf does not exist, is a regular
- * file, or is a symlink already owned by NM. In all cases /etc/resolv.conf
- * is replaced with a symlink pointing to NM's resolv.conf in /var/run/.
+ /* By this point, /etc/resolv.conf exists and is a symlink to our internal
+ * resolv.conf. We update the symlink so that applications get an inotify
+ * notification.
*/
if ( unlink (RESOLV_CONF_TMP) != 0
&& ((errsv = errno) != ENOENT)) {