policy: move hostname/lookup thread operations into separate file

This commit is contained in:
Dan Williams 2010-04-19 16:59:34 -07:00
parent fbcdc95a21
commit f911307996
6 changed files with 346 additions and 244 deletions

View file

@ -68,9 +68,11 @@ libtest_policy_hosts_la_SOURCES = \
nm-policy-hosts.h
libtest_policy_hosts_la_CPPFLAGS = \
-DSYSCONFDIR=\"$(sysconfdir)\" \
$(GLIB_CFLAGS)
libtest_policy_hosts_la_LIBADD = \
${top_builddir}/src/logging/libnm-logging.la \
$(GLIB_LIBS)
@ -123,6 +125,8 @@ NetworkManager_SOURCES = \
nm-policy.h \
nm-policy-hosts.c \
nm-policy-hosts.h \
nm-policy-hostname.c \
nm-policy-hostname.h \
NetworkManagerUtils.c \
NetworkManagerUtils.h \
nm-system.c \

196
src/nm-policy-hostname.c Normal file
View file

@ -0,0 +1,196 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
/* NetworkManager -- Network link manager
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Copyright (C) 2004 - 2010 Red Hat, Inc.
* Copyright (C) 2007 - 2008 Novell, Inc.
*/
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <netdb.h>
#include <ctype.h>
#include <glib.h>
#include "nm-logging.h"
#include "nm-policy-hostname.h"
#include "nm-policy-hosts.h"
/************************************************************************/
struct HostnameThread {
GThread *thread;
GMutex *lock;
gboolean dead;
int ret;
guint32 ip4_addr;
char hostname[NI_MAXHOST + 1];
HostnameThreadCallback callback;
gpointer user_data;
};
static gboolean
hostname_thread_run_cb (gpointer user_data)
{
HostnameThread *ht = (HostnameThread *) user_data;
(*ht->callback) (ht, ht->ret, ht->hostname, ht->user_data);
return FALSE;
}
static gpointer
hostname_thread_worker (gpointer data)
{
HostnameThread *ht = (HostnameThread *) data;
struct sockaddr_in addr;
int i;
g_mutex_lock (ht->lock);
if (ht->dead) {
g_mutex_unlock (ht->lock);
return (gpointer) NULL;
}
g_mutex_unlock (ht->lock);
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = ht->ip4_addr;
ht->ret = getnameinfo ((struct sockaddr *) &addr, sizeof (struct sockaddr_in),
ht->hostname, NI_MAXHOST, NULL, 0,
NI_NAMEREQD);
if (ht->ret == 0) {
for (i = 0; i < strlen (ht->hostname); i++)
ht->hostname[i] = tolower (ht->hostname[i]);
}
/* Don't track the idle handler ID because by the time the g_idle_add()
* returns the ID, the handler may already have run and freed the
* HostnameThread.
*/
g_idle_add (hostname_thread_run_cb, ht);
return (gpointer) TRUE;
}
void
hostname_thread_free (HostnameThread *ht)
{
g_return_if_fail (ht != NULL);
g_mutex_free (ht->lock);
memset (ht, 0, sizeof (HostnameThread));
g_free (ht);
}
HostnameThread *
hostname_thread_new (guint32 ip4_addr, HostnameThreadCallback callback, gpointer user_data)
{
HostnameThread *ht;
ht = g_malloc0 (sizeof (HostnameThread));
g_assert (ht);
ht->lock = g_mutex_new ();
ht->callback = callback;
ht->user_data = user_data;
ht->ip4_addr = ip4_addr;
ht->thread = g_thread_create (hostname_thread_worker, ht, FALSE, NULL);
if (!ht->thread) {
hostname_thread_free (ht);
ht = NULL;
}
return ht;
}
void
hostname_thread_kill (HostnameThread *ht)
{
g_return_if_fail (ht != NULL);
g_mutex_lock (ht->lock);
ht->dead = TRUE;
g_mutex_unlock (ht->lock);
}
gboolean
hostname_thread_is_dead (HostnameThread *ht)
{
g_return_val_if_fail (ht != NULL, TRUE);
return ht->dead;
}
/************************************************************************/
#define FALLBACK_HOSTNAME "localhost.localdomain"
gboolean
nm_policy_set_system_hostname (const char *new_hostname, const char *msg)
{
char old_hostname[HOST_NAME_MAX + 1];
int ret = 0;
const char *name = new_hostname ? new_hostname : FALLBACK_HOSTNAME;
gboolean set_hostname = TRUE, changed = FALSE;
old_hostname[HOST_NAME_MAX] = '\0';
errno = 0;
ret = gethostname (old_hostname, HOST_NAME_MAX);
if (ret != 0) {
nm_log_warn (LOGD_DNS, "couldn't get the system hostname: (%d) %s",
errno, strerror (errno));
} else {
/* Don't set the hostname if it isn't actually changing */
if ( (new_hostname && !strcmp (old_hostname, new_hostname))
|| (!new_hostname && !strcmp (old_hostname, FALLBACK_HOSTNAME)))
set_hostname = FALSE;
}
if (set_hostname) {
nm_log_info (LOGD_DNS, "Setting system hostname to '%s' (%s)", name, msg);
ret = sethostname (name, strlen (name));
if (ret != 0) {
nm_log_warn (LOGD_DNS, "couldn't set the system hostname to '%s': (%d) %s",
name, errno, strerror (errno));
return FALSE;
}
}
/* But even if the hostname isn't changing, always try updating /etc/hosts
* just in case the hostname changed while NM wasn't running; we need to
* make sure that /etc/hosts has valid mappings for '127.0.0.1' and the
* current system hostname. If those exist,
* nm_policy_hosts_update_etc_hosts() will just return and won't touch
* /etc/hosts at all.
*/
if (!nm_policy_hosts_update_etc_hosts (name, FALLBACK_HOSTNAME, &changed)) {
/* error updating /etc/hosts; fallback to localhost.localdomain */
nm_log_info (LOGD_DNS, "Setting system hostname to '" FALLBACK_HOSTNAME "' (error updating /etc/hosts)");
ret = sethostname (FALLBACK_HOSTNAME, strlen (FALLBACK_HOSTNAME));
if (ret != 0) {
nm_log_warn (LOGD_DNS, "couldn't set the fallback system hostname (%s): (%d) %s",
FALLBACK_HOSTNAME, errno, strerror (errno));
}
}
return changed;
}

47
src/nm-policy-hostname.h Normal file
View file

@ -0,0 +1,47 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
/* NetworkManager -- Network link manager
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Copyright (C) 2004 - 2010 Red Hat, Inc.
* Copyright (C) 2007 - 2008 Novell, Inc.
*/
#ifndef NM_POLICY_HOSTNAME_H
#define NM_POLICY_HOSTNAME_H
#include <glib.h>
gboolean nm_policy_set_system_hostname (const char *new_hostname, const char *msg);
typedef struct HostnameThread HostnameThread;
typedef void (*HostnameThreadCallback) (HostnameThread *ht,
int error,
const char *hostname,
gpointer user_data);
HostnameThread * hostname_thread_new (guint32 ip4_addr,
HostnameThreadCallback callback,
gpointer user_data);
void hostname_thread_free (HostnameThread *ht);
gboolean hostname_thread_is_dead (HostnameThread *ht);
void hostname_thread_kill (HostnameThread *ht);
#endif /* NM_POLICY_HOSTNAME_H */

View file

@ -25,6 +25,7 @@
#include <ctype.h>
#include "nm-policy-hosts.h"
#include "nm-logging.h"
gboolean
nm_policy_hosts_find_token (const char *line, const char *token)
@ -171,3 +172,64 @@ nm_policy_get_etc_hosts (const char **lines,
return contents;
}
gboolean
nm_policy_hosts_update_etc_hosts (const char *hostname,
const char *fallback_hostname,
gboolean *out_changed)
{
char *contents = NULL;
char **lines = NULL;
GError *error = NULL;
GString *new_contents = NULL;
gsize contents_len = 0;
gboolean success = FALSE;
g_return_val_if_fail (hostname != NULL, FALSE);
g_return_val_if_fail (out_changed != NULL, FALSE);
if (!g_file_get_contents (SYSCONFDIR "/hosts", &contents, &contents_len, &error)) {
nm_log_warn (LOGD_DNS, "couldn't read " SYSCONFDIR "/hosts: (%d) %s",
error ? error->code : 0,
(error && error->message) ? error->message : "(unknown)");
g_clear_error (&error);
return FALSE;
}
/* Get the new /etc/hosts contents */
lines = g_strsplit_set (contents, "\n\r", 0);
new_contents = nm_policy_get_etc_hosts ((const char **) lines,
contents_len,
hostname,
fallback_hostname,
&error);
g_strfreev (lines);
g_free (contents);
if (new_contents) {
nm_log_info (LOGD_DNS, "Updating /etc/hosts with new system hostname");
g_clear_error (&error);
/* And actually update /etc/hosts */
if (!g_file_set_contents (SYSCONFDIR "/hosts", new_contents->str, -1, &error)) {
nm_log_warn (LOGD_DNS, "couldn't update " SYSCONFDIR "/hosts: (%d) %s",
error ? error->code : 0,
(error && error->message) ? error->message : "(unknown)");
g_clear_error (&error);
} else {
success = TRUE;
*out_changed = TRUE;
}
g_string_free (new_contents, TRUE);
} else if (!error) {
/* No change required */
success = TRUE;
} else {
nm_log_warn (LOGD_DNS, "couldn't read " SYSCONFDIR "/hosts: (%d) %s",
error->code, error->message ? error->message : "(unknown)");
g_clear_error (&error);
}
return success;
}

View file

@ -23,14 +23,18 @@
#include <glib.h>
gboolean nm_policy_hosts_update_etc_hosts (const char *hostname,
const char *fallback_hostname,
gboolean *out_changed);
/* Only for testcases; don't use outside of nm-policy-hosts.c */
gboolean nm_policy_hosts_find_token (const char *line, const char *token);
GString *nm_policy_get_etc_hosts (const char **lines,
gsize existing_len,
const char *hostname,
const char *fallback_hostname,
GError **error);
/* Only for testcases; don't use outside of nm-policy-hosts.c */
gboolean nm_policy_hosts_find_token (const char *line, const char *token);
#endif /* NM_POLICY_HOSTS_H */

View file

@ -42,24 +42,7 @@
#include "nm-named-manager.h"
#include "nm-vpn-manager.h"
#include "nm-policy-hosts.h"
typedef struct LookupThread LookupThread;
typedef void (*LookupCallback) (LookupThread *thread, gpointer user_data);
struct LookupThread {
GThread *thread;
GMutex *lock;
gboolean die;
int ret;
guint32 ip4_addr;
char hostname[NI_MAXHOST + 1];
LookupCallback callback;
gpointer user_data;
};
#include "nm-policy-hostname.h"
struct NMPolicy {
NMManager *manager;
@ -74,97 +57,11 @@ struct NMPolicy {
NMDevice *default_device;
LookupThread *lookup;
HostnameThread *lookup;
char *orig_hostname; /* hostname at NM start time */
};
static gboolean
lookup_thread_run_cb (gpointer user_data)
{
LookupThread *thread = (LookupThread *) user_data;
(*thread->callback) (thread, thread->user_data);
return FALSE;
}
static gpointer
lookup_thread_worker (gpointer data)
{
LookupThread *thread = (LookupThread *) data;
struct sockaddr_in addr;
g_mutex_lock (thread->lock);
if (thread->die) {
g_mutex_unlock (thread->lock);
return (gpointer) NULL;
}
g_mutex_unlock (thread->lock);
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = thread->ip4_addr;
thread->ret = getnameinfo ((struct sockaddr *) &addr, sizeof (struct sockaddr_in),
thread->hostname, NI_MAXHOST, NULL, 0,
NI_NAMEREQD);
if (thread->ret == 0) {
int i;
for (i = 0; i < strlen (thread->hostname); i++)
thread->hostname[i] = tolower (thread->hostname[i]);
}
/* Don't track the idle handler ID because by the time the g_idle_add()
* returns the ID, the handler may already have run and freed the
* LookupThread.
*/
g_idle_add (lookup_thread_run_cb, thread);
return (gpointer) TRUE;
}
static void
lookup_thread_free (LookupThread *thread)
{
g_return_if_fail (thread != NULL);
g_mutex_free (thread->lock);
memset (thread, 0, sizeof (LookupThread));
g_free (thread);
}
static LookupThread *
lookup_thread_new (guint32 ip4_addr, LookupCallback callback, gpointer user_data)
{
LookupThread *thread;
thread = g_malloc0 (sizeof (LookupThread));
if (!thread)
return NULL;
thread->lock = g_mutex_new ();
thread->callback = callback;
thread->user_data = user_data;
thread->ip4_addr = ip4_addr;
thread->thread = g_thread_create (lookup_thread_worker, thread, FALSE, NULL);
if (!thread->thread) {
lookup_thread_free (thread);
return NULL;
}
return thread;
}
static void
lookup_thread_die (LookupThread *thread)
{
g_return_if_fail (thread != NULL);
g_mutex_lock (thread->lock);
thread->die = TRUE;
g_mutex_unlock (thread->lock);
}
#define INVALID_TAG "invalid"
static const char *
@ -253,142 +150,34 @@ get_best_device (NMManager *manager, NMActRequest **out_req)
return best;
}
#define FALLBACK_HOSTNAME "localhost.localdomain"
static gboolean
update_etc_hosts (const char *hostname, gboolean *out_changed)
{
char *contents = NULL;
char **lines = NULL;
GError *error = NULL;
GString *new_contents = NULL;
gsize contents_len = 0;
gboolean success = FALSE;
g_return_val_if_fail (hostname != NULL, FALSE);
g_return_val_if_fail (out_changed != NULL, FALSE);
if (!g_file_get_contents (SYSCONFDIR "/hosts", &contents, &contents_len, &error)) {
nm_log_warn (LOGD_DNS, "couldn't read " SYSCONFDIR "/hosts: (%d) %s",
error ? error->code : 0,
(error && error->message) ? error->message : "(unknown)");
g_clear_error (&error);
return FALSE;
}
/* Get the new /etc/hosts contents */
lines = g_strsplit_set (contents, "\n\r", 0);
new_contents = nm_policy_get_etc_hosts ((const char **) lines,
contents_len,
hostname,
FALLBACK_HOSTNAME,
&error);
g_strfreev (lines);
g_free (contents);
if (new_contents) {
nm_log_info (LOGD_DNS, "Updating /etc/hosts with new system hostname");
g_clear_error (&error);
/* And actually update /etc/hosts */
if (!g_file_set_contents (SYSCONFDIR "/hosts", new_contents->str, -1, &error)) {
nm_log_warn (LOGD_DNS, "couldn't update " SYSCONFDIR "/hosts: (%d) %s",
error ? error->code : 0,
(error && error->message) ? error->message : "(unknown)");
g_clear_error (&error);
} else {
success = TRUE;
*out_changed = TRUE;
}
g_string_free (new_contents, TRUE);
} else if (!error) {
/* No change required */
success = TRUE;
} else {
nm_log_warn (LOGD_DNS, "couldn't read " SYSCONFDIR "/hosts: (%d) %s",
error->code, error->message ? error->message : "(unknown)");
g_clear_error (&error);
}
return success;
}
static void
set_system_hostname (const char *new_hostname, const char *msg)
_set_hostname (const char *new_hostname, const char *msg)
{
char old_hostname[HOST_NAME_MAX + 1];
int ret = 0;
const char *name = new_hostname ? new_hostname : FALLBACK_HOSTNAME;
gboolean set_hostname = TRUE, changed = FALSE;
old_hostname[HOST_NAME_MAX] = '\0';
errno = 0;
ret = gethostname (old_hostname, HOST_NAME_MAX);
if (ret != 0) {
nm_log_warn (LOGD_DNS, "couldn't get the system hostname: (%d) %s",
errno, strerror (errno));
} else {
/* Don't set the hostname if it isn't actually changing */
if ( (new_hostname && !strcmp (old_hostname, new_hostname))
|| (!new_hostname && !strcmp (old_hostname, FALLBACK_HOSTNAME)))
set_hostname = FALSE;
}
if (set_hostname) {
nm_log_info (LOGD_DNS, "Setting system hostname to '%s' (%s)", name, msg);
ret = sethostname (name, strlen (name));
if (ret != 0) {
nm_log_warn (LOGD_DNS, "couldn't set the system hostname to '%s': (%d) %s",
name, errno, strerror (errno));
return;
}
}
/* But still always try updating /etc/hosts just in case the hostname
* changed while NM wasn't running; we need to make sure that /etc/hosts
* has valid mappings for '127.0.0.1' and the current system hostname. If
* those exist, update_etc_hosts() will just return and won't touch
* /etc/hosts at all.
*/
if (!update_etc_hosts (name, &changed)) {
/* error updating /etc/hosts; fallback to localhost.localdomain */
nm_log_info (LOGD_DNS, "Setting system hostname to '" FALLBACK_HOSTNAME "' (error updating /etc/hosts)");
ret = sethostname (FALLBACK_HOSTNAME, strlen (FALLBACK_HOSTNAME));
if (ret != 0) {
nm_log_warn (LOGD_DNS, "couldn't set the fallback system hostname (%s): (%d) %s",
FALLBACK_HOSTNAME, errno, strerror (errno));
}
}
if (changed)
if (nm_policy_set_system_hostname (new_hostname, msg))
nm_utils_call_dispatcher ("hostname", NULL, NULL, NULL);
}
static void
lookup_callback (LookupThread *thread, gpointer user_data)
lookup_callback (HostnameThread *thread,
int result,
const char *hostname,
gpointer user_data)
{
NMPolicy *policy = (NMPolicy *) user_data;
char *msg;
/* If the thread was told to die or it's not the current in-progress
* hostname lookup, nothing to do.
*/
if (thread->die || (thread != policy->lookup))
goto done;
policy->lookup = NULL;
if (!strlen (thread->hostname)) {
char *msg;
/* No valid IP4 config (!!); fall back to localhost.localdomain */
msg = g_strdup_printf ("address lookup failed: %d", thread->ret);
set_system_hostname (NULL, msg);
g_free (msg);
} else
set_system_hostname (thread->hostname, "from address lookup");
done:
lookup_thread_free (thread);
/* Update the hostname if the calling lookup thread is the in-progress one */
if (!hostname_thread_is_dead (thread) && (thread == policy->lookup)) {
policy->lookup = NULL;
if (!hostname) {
/* No valid IP4 config (!!); fall back to localhost.localdomain */
msg = g_strdup_printf ("address lookup failed: %d", result);
_set_hostname (NULL, msg);
g_free (msg);
} else
_set_hostname (hostname, "from address lookup");
}
hostname_thread_free (thread);
}
static void
@ -403,7 +192,7 @@ update_system_hostname (NMPolicy *policy, NMDevice *best)
g_return_if_fail (policy != NULL);
if (policy->lookup) {
lookup_thread_die (policy->lookup);
hostname_thread_kill (policy->lookup);
policy->lookup = NULL;
}
@ -419,7 +208,7 @@ update_system_hostname (NMPolicy *policy, NMDevice *best)
/* Try a persistent hostname first */
g_object_get (G_OBJECT (policy->manager), NM_MANAGER_HOSTNAME, &configured_hostname, NULL);
if (configured_hostname) {
set_system_hostname (configured_hostname, "from system configuration");
_set_hostname (configured_hostname, "from system configuration");
g_free (configured_hostname);
return;
}
@ -432,7 +221,7 @@ update_system_hostname (NMPolicy *policy, NMDevice *best)
/* No best device; fall back to original hostname or if there wasn't
* one, 'localhost.localdomain'
*/
set_system_hostname (policy->orig_hostname, "no default device");
_set_hostname (policy->orig_hostname, "no default device");
return;
}
@ -446,7 +235,7 @@ update_system_hostname (NMPolicy *policy, NMDevice *best)
/* Sanity check */
while (*p) {
if (!isblank (*p++)) {
set_system_hostname (dhcp4_hostname, "from DHCP");
_set_hostname (dhcp4_hostname, "from DHCP");
return;
}
}
@ -459,7 +248,7 @@ update_system_hostname (NMPolicy *policy, NMDevice *best)
* when NM started up.
*/
if (policy->orig_hostname) {
set_system_hostname (policy->orig_hostname, "from system startup");
_set_hostname (policy->orig_hostname, "from system startup");
return;
}
@ -471,7 +260,7 @@ update_system_hostname (NMPolicy *policy, NMDevice *best)
|| (nm_ip4_config_get_num_nameservers (ip4_config) == 0)
|| (nm_ip4_config_get_num_addresses (ip4_config) == 0)) {
/* No valid IP4 config (!!); fall back to localhost.localdomain */
set_system_hostname (NULL, "no IPv4 config");
_set_hostname (NULL, "no IPv4 config");
return;
}
@ -479,10 +268,10 @@ update_system_hostname (NMPolicy *policy, NMDevice *best)
g_assert (addr); /* checked for > 1 address above */
/* Start the hostname lookup thread */
policy->lookup = lookup_thread_new (nm_ip4_address_get_address (addr), lookup_callback, policy);
policy->lookup = hostname_thread_new (nm_ip4_address_get_address (addr), lookup_callback, policy);
if (!policy->lookup) {
/* Fall back to 'localhost.localdomain' */
set_system_hostname (NULL, "error starting hostname thread");
_set_hostname (NULL, "error starting hostname thread");
}
}
@ -1088,7 +877,7 @@ nm_policy_destroy (NMPolicy *policy)
* by the lookup thread callback.
*/
if (policy->lookup) {
lookup_thread_die (policy->lookup);
hostname_thread_kill (policy->lookup);
policy->lookup = NULL;
}