core: move handling of hostname from plugins to core

How to write and read the machine hostname is something that has been
handled until now by plugins; this is questionable since the method
using for storing the hostname should depend only on the distro used
and not on which plugins are enabled.

This commit moves all hostname-related functions from plugins to the
core and allows to specify the method used to load and store the
hostname at build time with the

  --with-hostname-persist=default|suse|gentoo

configure option.

'default' method stores the hostname to /etc/hostname and monitors it
to detect runtime changes.

When the selected method is 'suse', the hostname gets read from and
written to /etc/HOSTNAME; the file /etc/sysconfig/network/dhcp is also
read to detect if the hostname is dynamic and thus invalid. Both files
are monitored for changes.

'gentoo' method relies on /etc/conf.d/hostname for storing the
hostname.
This commit is contained in:
Beniamino Galvani 2015-03-23 09:15:47 +01:00
parent fb304bc6b9
commit 6c3d71c431
2 changed files with 258 additions and 46 deletions

View file

@ -119,6 +119,13 @@ if test -z "$config_plugins_default" -o "$config_plugins_default" = no; then
test "$enable_config_plugin_ibft" = "yes" && config_plugins_default="$config_plugins_default,ibft"
config_plugins_default="${config_plugins_default#,}"
fi
test "$enable_ifcfg_rh" = "yes" && distro_plugins="$distro_plugins,ifcfg-rh"
test "$enable_ifcfg_suse" = "yes" && distro_plugins="$distro_plugins,ifcfg-suse"
test "$enable_ifupdown" = "yes" && distro_plugins="$distro_plugins,ifupdown"
test "$enable_ifnet" = "yes" && distro_plugins="$distro_plugins,ifnet"
distro_plugins="${distro_plugins#,}"
AC_DEFINE_UNQUOTED(CONFIG_PLUGINS_DEFAULT, "$config_plugins_default", [Default configuration option for main.plugins setting])
if test "$enable_ifcfg_rh" = "yes"; then
@ -347,6 +354,24 @@ fi
PKG_CHECK_MODULES(SYSTEMD_200, [systemd >= 200], [have_systemd_200=yes],[have_systemd_200=no])
AM_CONDITIONAL(HAVE_SYSTEMD_200, test "${have_systemd_200}" = "yes")
# Hostname persist mode
AC_ARG_WITH(hostname-persist, AS_HELP_STRING([--with-hostname-persist=default|suse|gentoo],
[Hostname persist method]))
AS_IF([test "$with_hostname_persist" = "suse"], hostname_persist=suse)
AS_IF([test "$with_hostname_persist" = "gentoo"], hostname_persist=gentoo)
AS_IF([test "$with_hostname_persist" = "default"], hostname_persist=default)
# if the method was not explicitly set, try to guess it from the enabled plugins
AS_IF([test -z "$hostname_persist" -a "$distro_plugins" = "ifcfg-suse"], hostname_persist=suse)
AS_IF([test -z "$hostname_persist" -a "$distro_plugins" = "ifnet"], hostname_persist=gentoo)
AS_IF([test -z "$hostname_persist"], hostname_persist=default)
if test "$hostname_persist" = suse; then
AC_DEFINE(HOSTNAME_PERSIST_SUSE, 1, [Enable SuSE hostname persist method])
elif test "$hostname_persist" = gentoo; then
AC_DEFINE(HOSTNAME_PERSIST_GENTOO, 1, [Enable Gentoo hostname persist method])
fi
# Session tracking support
AC_ARG_WITH(systemd-logind, AS_HELP_STRING([--with-systemd-logind=yes|no],
[Support systemd session tracking]))
@ -1063,6 +1088,7 @@ else
fi
echo " polkit agent: ${enable_polkit_agent}"
echo " selinux: $have_selinux"
echo " hostname persist: ${hostname_persist}"
echo
echo "Features:"

View file

@ -34,6 +34,10 @@
#include <dbus/dbus.h>
#include <dbus/dbus-glib-lowlevel.h>
#if HAVE_SELINUX
#include <selinux/selinux.h>
#endif
#include "gsystem-local-alloc.h"
#include <nm-dbus-interface.h>
#include <nm-connection.h>
@ -94,6 +98,20 @@ EXPORT(nm_settings_connection_replace_settings)
EXPORT(nm_settings_connection_replace_and_commit)
/* END LINKER CRACKROCK */
#define HOSTNAME_FILE_DEFAULT "/etc/hostname"
#define HOSTNAME_FILE_SUSE "/etc/HOSTNAME"
#define HOSTNAME_FILE_GENTOO "/etc/conf.d/hostname"
#define IFCFG_DIR SYSCONFDIR "/sysconfig/network"
#define CONF_DHCP IFCFG_DIR "/dhcp"
#if defined(HOSTNAME_PERSIST_SUSE)
#define HOSTNAME_FILE HOSTNAME_FILE_SUSE
#elif defined(HOSTNAME_PERSIST_GENTOO)
#define HOSTNAME_FILE HOSTNAME_FILE_GENTOO
#else
#define HOSTNAME_FILE HOSTNAME_FILE_DEFAULT
#endif
static void claim_connection (NMSettings *self,
NMSettingsConnection *connection);
@ -152,6 +170,15 @@ typedef struct {
GSList *get_connections_cache;
gboolean startup_complete;
struct {
char *value;
char *file;
GFileMonitor *monitor;
GFileMonitor *dhcp_monitor;
guint monitor_id;
guint dhcp_monitor_id;
} hostname;
} NMSettingsPrivate;
#define NM_SETTINGS_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTINGS, NMSettingsPrivate))
@ -497,7 +524,7 @@ get_plugin (NMSettings *self, guint32 capability)
g_return_val_if_fail (self != NULL, NULL);
/* Do any of the plugins support setting the hostname? */
/* Do any of the plugins support the given capability? */
for (iter = priv->plugins; iter; iter = iter->next) {
NMSystemConfigInterfaceCapabilities caps = NM_SYSTEM_CONFIG_INTERFACE_CAP_NONE;
@ -509,30 +536,88 @@ get_plugin (NMSettings *self, guint32 capability)
return NULL;
}
#if defined(HOSTNAME_PERSIST_GENTOO)
static gchar *
read_hostname_gentoo (const char *path)
{
gchar *contents = NULL, *result = NULL, *tmp;
gchar **all_lines = NULL;
guint line_num, i;
if (!g_file_get_contents (path, &contents, NULL, NULL))
return NULL;
all_lines = g_strsplit (contents, "\n", 0);
line_num = g_strv_length (all_lines);
for (i = 0; i < line_num; i++) {
g_strstrip (all_lines[i]);
if (all_lines[i][0] == '#' || all_lines[i][0] == '\0')
continue;
if (g_str_has_prefix (all_lines[i], "hostname=")) {
tmp = &all_lines[i][STRLEN ("hostname=")];
result = g_shell_unquote (tmp, NULL);
break;
}
}
g_strfreev (all_lines);
g_free (contents);
return result;
}
#endif
#if defined(HOSTNAME_PERSIST_SUSE)
static gboolean
hostname_is_dynamic (void)
{
GIOChannel *channel;
char *str = NULL;
gboolean dynamic = FALSE;
channel = g_io_channel_new_file (CONF_DHCP, "r", NULL);
if (!channel)
return dynamic;
while (g_io_channel_read_line (channel, &str, NULL, NULL, NULL) != G_IO_STATUS_EOF) {
if (str) {
g_strstrip (str);
if (g_str_has_prefix (str, "DHCLIENT_SET_HOSTNAME="))
dynamic = strcmp (&str[STRLEN ("DHCLIENT_SET_HOSTNAME=")], "\"yes\"") == 0;
g_free (str);
}
}
g_io_channel_shutdown (channel, FALSE, NULL);
g_io_channel_unref (channel);
return dynamic;
}
#endif
/* Returns an allocated string which the caller owns and must eventually free */
char *
nm_settings_get_hostname (NMSettings *self)
{
NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
GSList *iter;
char *hostname = NULL;
/* Hostname returned is the hostname returned from the first plugin
* that provides one.
*/
for (iter = priv->plugins; iter; iter = iter->next) {
NMSystemConfigInterfaceCapabilities caps = NM_SYSTEM_CONFIG_INTERFACE_CAP_NONE;
#if defined(HOSTNAME_PERSIST_GENTOO)
hostname = read_hostname_gentoo (priv->hostname.file);
#else
g_object_get (G_OBJECT (iter->data), NM_SYSTEM_CONFIG_INTERFACE_CAPABILITIES, &caps, NULL);
if (caps & NM_SYSTEM_CONFIG_INTERFACE_CAP_MODIFY_HOSTNAME) {
g_object_get (G_OBJECT (iter->data), NM_SYSTEM_CONFIG_INTERFACE_HOSTNAME, &hostname, NULL);
if (hostname && strlen (hostname))
return hostname;
g_free (hostname);
}
#if defined(HOSTNAME_PERSIST_SUSE)
if (priv->hostname.dhcp_monitor_id && hostname_is_dynamic ())
return NULL;
#endif
if (g_file_get_contents (priv->hostname.file, &hostname, NULL, NULL))
g_strchomp (hostname);
#endif /* HOSTNAME_PERSIST_GENTOO */
if (hostname && !hostname[0]) {
g_free (hostname);
hostname = NULL;
}
return NULL;
return hostname;
}
static gboolean
@ -595,14 +680,6 @@ unrecognized_specs_changed (NMSystemConfigInterface *config,
nm_system_config_interface_get_unrecognized_specs);
}
static void
hostname_changed (NMSystemConfigInterface *config,
GParamSpec *pspec,
gpointer user_data)
{
g_object_notify (G_OBJECT (user_data), NM_SETTINGS_HOSTNAME);
}
static void
add_plugin (NMSettings *self, NMSystemConfigInterface *plugin)
{
@ -616,9 +693,6 @@ add_plugin (NMSettings *self, NMSystemConfigInterface *plugin)
priv = NM_SETTINGS_GET_PRIVATE (self);
priv->plugins = g_slist_append (priv->plugins, g_object_ref (plugin));
g_signal_connect (plugin, "notify::"NM_SYSTEM_CONFIG_INTERFACE_HOSTNAME, G_CALLBACK (hostname_changed), self);
nm_system_config_interface_init (plugin, NULL);
g_object_get (G_OBJECT (plugin),
@ -1442,6 +1516,56 @@ impl_settings_reload_connections (NMSettings *self,
dbus_g_method_return (context, TRUE);
}
static gboolean
write_hostname (NMSettingsPrivate *priv, const char *hostname)
{
char *hostname_eol;
gboolean ret;
gs_free_error GError *error = NULL;
const char *file = priv->hostname.file;
#if HAVE_SELINUX
security_context_t se_ctx_prev = NULL, se_ctx = NULL;
struct stat file_stat = { .st_mode = 0 };
mode_t st_mode = 0;
/* Get default context for hostname file and set it for fscreate */
if (stat (file, &file_stat) == 0)
st_mode = file_stat.st_mode;
matchpathcon (file, st_mode, &se_ctx);
matchpathcon_fini ();
getfscreatecon (&se_ctx_prev);
setfscreatecon (se_ctx);
#endif
#if defined (HOSTNAME_PERSIST_GENTOO)
hostname_eol = g_strdup_printf ("#Generated by NetworkManager\n"
"hostname=\"%s\"\n", hostname);
#else
hostname_eol = g_strdup_printf ("%s\n", hostname);
#endif
/* FIXME: g_file_set_contents() writes first to a temporary file
* and renames it atomically. We should hack g_file_set_contents()
* to set the SELINUX labels before renaming the file. */
ret = g_file_set_contents (file, hostname_eol, -1, &error);
#if HAVE_SELINUX
/* Restore previous context and cleanup */
setfscreatecon (se_ctx_prev);
freecon (se_ctx);
freecon (se_ctx_prev);
#endif
g_free (hostname_eol);
if (!ret) {
nm_log_warn (LOGD_SETTINGS, "Could not save hostname to %s: %s", file, error->message);
return FALSE;
}
return TRUE;
}
static void
pk_hostname_cb (NMAuthChain *chain,
GError *chain_error,
@ -1452,7 +1576,6 @@ pk_hostname_cb (NMAuthChain *chain,
NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
NMAuthCallResult result;
GError *error = NULL;
GSList *iter;
const char *hostname;
g_assert (context);
@ -1472,21 +1595,12 @@ pk_hostname_cb (NMAuthChain *chain,
NM_SETTINGS_ERROR_PERMISSION_DENIED,
"Insufficient privileges.");
} else {
/* Set the hostname in all plugins */
hostname = nm_auth_chain_get_data (chain, "hostname");
for (iter = priv->plugins; iter; iter = iter->next) {
NMSystemConfigInterfaceCapabilities caps = NM_SYSTEM_CONFIG_INTERFACE_CAP_NONE;
/* error will be cleared if any plugin supports saving the hostname */
if (!write_hostname (priv, hostname)) {
error = g_error_new_literal (NM_SETTINGS_ERROR,
NM_SETTINGS_ERROR_FAILED,
"Saving the hostname failed.");
g_object_get (G_OBJECT (iter->data), NM_SYSTEM_CONFIG_INTERFACE_CAPABILITIES, &caps, NULL);
if (caps & NM_SYSTEM_CONFIG_INTERFACE_CAP_MODIFY_HOSTNAME) {
g_object_set (G_OBJECT (iter->data), NM_SYSTEM_CONFIG_INTERFACE_HOSTNAME, hostname, NULL);
g_clear_error (&error);
}
}
}
@ -1543,14 +1657,6 @@ impl_settings_save_hostname (NMSettings *self,
goto done;
}
/* Do any of the plugins support setting the hostname? */
if (!get_plugin (self, NM_SYSTEM_CONFIG_INTERFACE_CAP_MODIFY_HOSTNAME)) {
error = g_error_new_literal (NM_SETTINGS_ERROR,
NM_SETTINGS_ERROR_NOT_SUPPORTED,
"None of the registered plugins support setting the hostname.");
goto done;
}
chain = nm_auth_chain_new_context (context, pk_hostname_cb, self);
if (!chain) {
error = g_error_new_literal (NM_SETTINGS_ERROR,
@ -1569,6 +1675,37 @@ done:
g_clear_error (&error);
}
static void
hostname_maybe_changed (NMSettings *settings)
{
NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (settings);
char *new_hostname;
new_hostname = nm_settings_get_hostname (settings);
if ( (new_hostname && !priv->hostname.value)
|| (!new_hostname && priv->hostname.value)
|| (priv->hostname.value && new_hostname && strcmp (priv->hostname.value, new_hostname))) {
nm_log_info (LOGD_SETTINGS, "hostname changed from '%s' to '%s'",
priv->hostname.value, new_hostname);
g_free (priv->hostname.value);
priv->hostname.value = new_hostname;
g_object_notify (G_OBJECT (settings), NM_SETTINGS_HOSTNAME);
} else
g_free (new_hostname);
}
static void
hostname_file_changed_cb (GFileMonitor *monitor,
GFile *file,
GFile *other_file,
GFileMonitorEvent event_type,
gpointer user_data)
{
hostname_maybe_changed (user_data);
}
static gboolean
have_connection_for_device (NMSettings *self, NMDevice *device)
{
@ -1872,6 +2009,8 @@ nm_settings_new (GError **error)
{
NMSettings *self;
NMSettingsPrivate *priv;
GFile *file;
GFileMonitor *monitor;
self = g_object_new (NM_TYPE_SETTINGS, NULL);
@ -1889,6 +2028,34 @@ nm_settings_new (GError **error)
load_connections (self);
check_startup_complete (self);
priv->hostname.file = HOSTNAME_FILE;
priv->hostname.value = nm_settings_get_hostname (self);
/* monitor changes to hostname file */
file = g_file_new_for_path (priv->hostname.file);
monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE, NULL, NULL);
g_object_unref (file);
if (monitor) {
priv->hostname.monitor_id = g_signal_connect (monitor, "changed",
G_CALLBACK (hostname_file_changed_cb),
self);
priv->hostname.monitor = monitor;
}
#if defined (HOSTNAME_PERSIST_SUSE)
/* monitor changes to dhcp file to know whether the hostname is valid */
file = g_file_new_for_path (CONF_DHCP);
monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE, NULL, NULL);
g_object_unref (file);
if (monitor) {
priv->hostname.dhcp_monitor_id = g_signal_connect (monitor, "changed",
G_CALLBACK (hostname_file_changed_cb),
self);
priv->hostname.dhcp_monitor = monitor;
}
#endif
hostname_maybe_changed (self);
nm_dbus_manager_register_object (priv->dbus_mgr, NM_DBUS_PATH_SETTINGS, self);
return self;
}
@ -1932,6 +2099,25 @@ dispose (GObject *object)
g_object_unref (priv->agent_mgr);
if (priv->hostname.monitor) {
if (priv->hostname.monitor_id)
g_signal_handler_disconnect (priv->hostname.monitor, priv->hostname.monitor_id);
g_file_monitor_cancel (priv->hostname.monitor);
g_clear_object (&priv->hostname.monitor);
}
if (priv->hostname.dhcp_monitor) {
if (priv->hostname.dhcp_monitor_id)
g_signal_handler_disconnect (priv->hostname.dhcp_monitor,
priv->hostname.dhcp_monitor_id);
g_file_monitor_cancel (priv->hostname.dhcp_monitor);
g_clear_object (&priv->hostname.dhcp_monitor);
}
g_clear_pointer (&priv->hostname.value, g_free);
G_OBJECT_CLASS (nm_settings_parent_class)->dispose (object);
}
@ -2001,7 +2187,7 @@ static void
nm_settings_class_init (NMSettingsClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
g_type_class_add_private (class, sizeof (NMSettingsPrivate));
/* virtual methods */