From fae84b16f850c8a9d6a5f5c4bee1c162debb8dec Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Thu, 11 May 2017 13:30:26 +0200 Subject: [PATCH] dns: better detect systemd-resolved when checking for resolv.conf symlink We autodetect systemd-resolved based on whether /etc/resolv.conf points to one of the well known files of systemd-resolved. Extend the check by also - follow symlinks and compare the absolute link target - open the file and compare the inodes for hard-linking Note that when NetworkManager starts, systemd-resolved might not have started yet. So, while comparing the inode is the best check, we also compare symlinks (g_file_read_link() and realpath()). Based-on-patch-by: Sam Morris https://github.com/NetworkManager/NetworkManager/pull/16 https://bugzilla.gnome.org/show_bug.cgi?id=779269 --- src/dns/nm-dns-manager.c | 70 ++++++++++++++++++++++++++++++---------- 1 file changed, 53 insertions(+), 17 deletions(-) diff --git a/src/dns/nm-dns-manager.c b/src/dns/nm-dns-manager.c index 1f7eb96412..1f6c2eede4 100644 --- a/src/dns/nm-dns-manager.c +++ b/src/dns/nm-dns-manager.c @@ -1642,32 +1642,68 @@ _check_resconf_immutable (NMDnsManagerResolvConfManager rc_manager) static gboolean _resolvconf_resolved_managed (void) { - static const char *const resolved_paths[] = { + static const char *const RESOLVED_PATHS[] = { "/run/systemd/resolve/resolv.conf", "/lib/systemd/resolv.conf", "/usr/lib/systemd/resolv.conf", }; - GFile *f; - GFileInfo *info; - gboolean ret = FALSE; + struct stat st, st_test; + guint i; - f = g_file_new_for_path (_PATH_RESCONF); - info = g_file_query_info (f, - G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK","\ - G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET, - G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, - NULL, NULL); + if (lstat (_PATH_RESCONF, &st) != 0) + return FALSE; - if (info && g_file_info_get_is_symlink (info)) { - ret = nm_utils_strv_find_first ((gchar **) resolved_paths, - G_N_ELEMENTS (resolved_paths), - g_file_info_get_symlink_target (info)) >= 0; + 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; + + /* 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; + + /* 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; } - g_clear_object(&info); - g_clear_object(&f); + /* 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; + } - return ret; + return FALSE; } static void