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
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]))
fi
echo " polkit agent: ${enable_polkit_agent}"
echo " selinux: $have_selinux"
+echo " hostname persist: ${hostname_persist}"
echo
echo "Features:"
#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>
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);
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))
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;
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
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)
{
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),
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,
NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
NMAuthCallResult result;
GError *error = NULL;
- GSList *iter;
const char *hostname;
g_assert (context);
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);
- }
}
}
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,
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)
{
{
NMSettings *self;
NMSettingsPrivate *priv;
+ GFile *file;
+ GFileMonitor *monitor;
self = g_object_new (NM_TYPE_SETTINGS, NULL);
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;
}
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);
}
nm_settings_class_init (NMSettingsClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
-
+
g_type_class_add_private (class, sizeof (NMSettingsPrivate));
/* virtual methods */