summary refs log tree commit diff
path: root/src/dhcp-manager/nm-dhcp-dhclient.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/dhcp-manager/nm-dhcp-dhclient.c')
-rw-r--r--src/dhcp-manager/nm-dhcp-dhclient.c228
1 files changed, 137 insertions, 91 deletions
diff --git a/src/dhcp-manager/nm-dhcp-dhclient.c b/src/dhcp-manager/nm-dhcp-dhclient.c
index 4232ee0c..504e8d5d 100644
--- a/src/dhcp-manager/nm-dhcp-dhclient.c
+++ b/src/dhcp-manager/nm-dhcp-dhclient.c
@@ -18,6 +18,8 @@
  * Copyright (C) 2005 - 2012 Red Hat, Inc.
  */
 
+#include "config.h"
+
 #define _XOPEN_SOURCE
 #include <time.h>
 #undef _XOPEN_SOURCE
@@ -34,49 +36,35 @@
 #include <arpa/inet.h>
 #include <ctype.h>
 
-#include <config.h>
-
 #include "nm-dhcp-dhclient.h"
 #include "nm-utils.h"
 #include "nm-logging.h"
 #include "nm-dhcp-dhclient-utils.h"
 #include "nm-dhcp-manager.h"
 #include "nm-posix-signals.h"
+#include "NetworkManagerUtils.h"
+#include "nm-dhcp-listener.h"
+#include "gsystem-local-alloc.h"
 
-G_DEFINE_TYPE (NMDHCPDhclient, nm_dhcp_dhclient, NM_TYPE_DHCP_CLIENT)
+G_DEFINE_TYPE (NMDhcpDhclient, nm_dhcp_dhclient, NM_TYPE_DHCP_CLIENT)
 
-#define NM_DHCP_DHCLIENT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DHCP_DHCLIENT, NMDHCPDhclientPrivate))
+#define NM_DHCP_DHCLIENT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DHCP_DHCLIENT, NMDhcpDhclientPrivate))
 
 typedef struct {
-	const char *path;
 	char *conf_file;
 	const char *def_leasefile;
 	char *lease_file;
 	char *pid_file;
-} NMDHCPDhclientPrivate;
+} NMDhcpDhclientPrivate;
 
-const char *
-nm_dhcp_dhclient_get_path (const char *try_first)
+static const char *
+nm_dhcp_dhclient_get_path (void)
 {
-	static const char *dhclient_paths[] = {
-		"/sbin/dhclient",
-		"/usr/sbin/dhclient",
-		"/usr/pkg/sbin/dhclient",
-		"/usr/local/sbin/dhclient",
-		NULL
-	};
-	const char **path = dhclient_paths;
-
-	if (strlen (try_first) && g_file_test (try_first, G_FILE_TEST_EXISTS))
-		return try_first;
-
-	while (*path != NULL) {
-		if (g_file_test (*path, G_FILE_TEST_EXISTS))
-			break;
-		path++;
-	}
+	const char *path = NULL;
 
-	return *path;
+	if (WITH_DHCLIENT)
+		path = nm_utils_find_helper ("dhclient", DHCLIENT_PATH, NULL);
+	return path;
 }
 
 /**
@@ -136,10 +124,11 @@ get_dhclient_leasefile (const char *iface,
 	return NULL;
 }
 
-GSList *
+static GSList *
 nm_dhcp_dhclient_get_lease_ip_configs (const char *iface,
                                        const char *uuid,
-                                       gboolean ipv6)
+                                       gboolean ipv6,
+                                       guint32 default_route_metric)
 {
 	char *contents = NULL;
 	char *leasefile;
@@ -165,10 +154,11 @@ static gboolean
 merge_dhclient_config (const char *iface,
                        const char *conf_file,
                        gboolean is_ip6,
-                       const char *dhcp_client_id,
-                       GByteArray *anycast_addr,
+                       GBytes *client_id,
+                       const char *anycast_addr,
                        const char *hostname,
                        const char *orig_path,
+                       GBytes **out_new_client_id,
                        GError **error)
 {
 	char *orig = NULL, *new;
@@ -187,7 +177,7 @@ merge_dhclient_config (const char *iface,
 		}
 	}
 
-	new = nm_dhcp_dhclient_create_config (iface, is_ip6, dhcp_client_id, anycast_addr, hostname, orig_path, orig);
+	new = nm_dhcp_dhclient_create_config (iface, is_ip6, client_id, anycast_addr, hostname, orig_path, orig, out_new_client_id);
 	g_assert (new);
 	success = g_file_set_contents (conf_file, new, -1, error);
 	g_free (new);
@@ -271,9 +261,10 @@ static char *
 create_dhclient_config (const char *iface,
                         gboolean is_ip6,
                         const char *uuid,
-                        const char *dhcp_client_id,
-                        GByteArray *dhcp_anycast_addr,
-                        const char *hostname)
+                        GBytes *client_id,
+                        const char *dhcp_anycast_addr,
+                        const char *hostname,
+                        GBytes **out_new_client_id)
 {
 	char *orig = NULL, *new = NULL;
 	GError *error = NULL;
@@ -298,7 +289,7 @@ create_dhclient_config (const char *iface,
 	}
 
 	error = NULL;
-	success = merge_dhclient_config (iface, new, is_ip6, dhcp_client_id, dhcp_anycast_addr, hostname, orig, &error);
+	success = merge_dhclient_config (iface, new, is_ip6, client_id, dhcp_anycast_addr, hostname, orig, out_new_client_id, &error);
 	if (!success) {
 		nm_log_warn (LOGD_DHCP, "(%s): error creating dhclient%s configuration: %s",
 		             iface, is_ip6 ? "6" : "", error->message);
@@ -324,23 +315,24 @@ dhclient_child_setup (gpointer user_data G_GNUC_UNUSED)
 	nm_unblock_posix_signals (NULL);
 }
 
-static GPid
-dhclient_start (NMDHCPClient *client,
+static gboolean
+dhclient_start (NMDhcpClient *client,
                 const char *mode_opt,
                 const GByteArray *duid,
-                gboolean release)
+                gboolean release,
+                pid_t *out_pid)
 {
-	NMDHCPDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (client);
+	NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (client);
 	GPtrArray *argv = NULL;
-	GPid pid = -1;
+	pid_t pid;
 	GError *error = NULL;
-	const char *iface, *uuid, *system_bus_address;
+	const char *iface, *uuid, *system_bus_address, *dhclient_path = NULL;
 	char *binary_name, *cmd_str, *pid_file = NULL, *system_bus_address_env = NULL;
 	gboolean ipv6, success;
 	guint log_domain;
 	char *escaped, *preferred_leasefile_path = NULL;
 
-	g_return_val_if_fail (priv->pid_file == NULL, -1);
+	g_return_val_if_fail (priv->pid_file == NULL, FALSE);
 
 	iface = nm_dhcp_client_get_iface (client);
 	uuid = nm_dhcp_client_get_uuid (client);
@@ -348,9 +340,10 @@ dhclient_start (NMDHCPClient *client,
 
 	log_domain = ipv6 ? LOGD_DHCP6 : LOGD_DHCP4;
 
-	if (!g_file_test (priv->path, G_FILE_TEST_EXISTS)) {
-		nm_log_warn (log_domain, "%s does not exist.", priv->path);
-		return -1;
+	dhclient_path = nm_dhcp_dhclient_get_path ();
+	if (!dhclient_path) {
+		nm_log_warn (log_domain, "dhclient could not be found");
+		return FALSE;
 	}
 
 	pid_file = g_strdup_printf (LOCALSTATEDIR "/run/dhclient%s-%s.pid",
@@ -358,7 +351,7 @@ dhclient_start (NMDHCPClient *client,
 		                        iface);
 
 	/* Kill any existing dhclient from the pidfile */
-	binary_name = g_path_get_basename (priv->path);
+	binary_name = g_path_get_basename (dhclient_path);
 	nm_dhcp_client_stop_existing (pid_file, binary_name);
 	g_free (binary_name);
 
@@ -404,15 +397,21 @@ dhclient_start (NMDHCPClient *client,
 			             iface, priv->lease_file,
 			             error ? error->code : -1,
 			             error && error->message ? error->message : "(unknown)");
-			return -1;
+			g_free (pid_file);
+			return FALSE;
 		}
 	}
 
 	argv = g_ptr_array_new ();
-	g_ptr_array_add (argv, (gpointer) priv->path);
+	g_ptr_array_add (argv, (gpointer) dhclient_path);
 
 	g_ptr_array_add (argv, (gpointer) "-d");
 
+	/* Be quiet. dhclient logs to syslog anyway. And we duplicate the syslog
+	 * to stderr in case of NM running with --debug.
+	 */
+	g_ptr_array_add (argv, (gpointer) "-q");
+
 	if (release)
 		g_ptr_array_add (argv, (gpointer) "-r");
 
@@ -456,68 +455,80 @@ dhclient_start (NMDHCPClient *client,
 	nm_log_dbg (log_domain, "running: %s", cmd_str);
 	g_free (cmd_str);
 
-	if (!g_spawn_async (NULL, (char **) argv->pdata, NULL, G_SPAWN_DO_NOT_REAP_CHILD,
-	                    &dhclient_child_setup, NULL, &pid, &error)) {
-		nm_log_warn (log_domain, "dhclient failed to start: '%s'", error->message);
-		g_error_free (error);
-		pid = -1;
-	} else {
+	if (g_spawn_async (NULL, (char **) argv->pdata, NULL,
+	                   G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL,
+	                   &dhclient_child_setup, NULL, &pid, &error)) {
+		g_assert (pid > 0);
 		nm_log_info (log_domain, "dhclient started with pid %d", pid);
+		if (release == FALSE)
+			nm_dhcp_client_watch_child (client, pid);
 		priv->pid_file = pid_file;
+	} else {
+		nm_log_warn (log_domain, "dhclient failed to start: '%s'", error->message);
+		g_error_free (error);
+		g_free (pid_file);
 	}
 
+	if (out_pid)
+		*out_pid = pid;
+
 	g_ptr_array_free (argv, TRUE);
 	g_free (system_bus_address_env);
-	return pid;
+	return pid > 0 ? TRUE : FALSE;
 }
 
-static GPid
-ip4_start (NMDHCPClient *client,
-           const char *dhcp_client_id,
-           GByteArray *dhcp_anycast_addr,
-           const char *hostname)
+static gboolean
+ip4_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const char *last_ip4_address)
 {
-	NMDHCPDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (client);
-	const char *iface, *uuid;
+	NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (client);
+	GBytes *client_id;
+	gs_unref_bytes GBytes *new_client_id = NULL;
+	const char *iface, *uuid, *hostname;
+	gboolean success = FALSE;
 
 	iface = nm_dhcp_client_get_iface (client);
 	uuid = nm_dhcp_client_get_uuid (client);
+	client_id = nm_dhcp_client_get_client_id (client);
+	hostname = nm_dhcp_client_get_hostname (client);
 
-	priv->conf_file = create_dhclient_config (iface, FALSE, uuid, dhcp_client_id, dhcp_anycast_addr, hostname);
-	if (!priv->conf_file) {
+	priv->conf_file = create_dhclient_config (iface, FALSE, uuid, client_id, dhcp_anycast_addr, hostname, &new_client_id);
+	if (priv->conf_file) {
+		if (new_client_id)
+			nm_dhcp_client_set_client_id (client, new_client_id);
+		success = dhclient_start (client, NULL, NULL, FALSE, NULL);
+	} else
 		nm_log_warn (LOGD_DHCP4, "(%s): error creating dhclient configuration file.", iface);
-		return -1;
-	}
 
-	return dhclient_start (client, NULL, NULL, FALSE);
+	return success;
 }
 
-static GPid
-ip6_start (NMDHCPClient *client,
-           GByteArray *dhcp_anycast_addr,
-           const char *hostname,
+static gboolean
+ip6_start (NMDhcpClient *client,
+           const char *dhcp_anycast_addr,
            gboolean info_only,
+           NMSettingIP6ConfigPrivacy privacy,
            const GByteArray *duid)
 {
-	NMDHCPDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (client);
-	const char *iface, *uuid;
+	NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (client);
+	const char *iface, *uuid, *hostname;
 
 	iface = nm_dhcp_client_get_iface (client);
 	uuid = nm_dhcp_client_get_uuid (client);
+	hostname = nm_dhcp_client_get_hostname (client);
 
-	priv->conf_file = create_dhclient_config (iface, TRUE, uuid, NULL, dhcp_anycast_addr, hostname);
+	priv->conf_file = create_dhclient_config (iface, TRUE, uuid, NULL, dhcp_anycast_addr, hostname, NULL);
 	if (!priv->conf_file) {
 		nm_log_warn (LOGD_DHCP6, "(%s): error creating dhclient6 configuration file.", iface);
-		return -1;
+		return FALSE;
 	}
 
-	return dhclient_start (client, info_only ? "-S" : "-N", duid, FALSE);
+	return dhclient_start (client, info_only ? "-S" : "-N", duid, FALSE, NULL);
 }
 
 static void
-stop (NMDHCPClient *client, gboolean release, const GByteArray *duid)
+stop (NMDhcpClient *client, gboolean release, const GByteArray *duid)
 {
-	NMDHCPDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (client);
+	NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (client);
 
 	/* Chain up to parent */
 	NM_DHCP_CLIENT_CLASS (nm_dhcp_dhclient_parent_class)->stop (client, release, duid);
@@ -533,20 +544,37 @@ stop (NMDHCPClient *client, gboolean release, const GByteArray *duid)
 	}
 
 	if (release) {
-		GPid rpid;
+		pid_t rpid = -1;
 
-		rpid = dhclient_start (client, NULL, duid, TRUE);
-		if (rpid > 0) {
+		if (dhclient_start (client, NULL, duid, TRUE, &rpid)) {
 			/* Wait a few seconds for the release to happen */
 			nm_dhcp_client_stop_pid (rpid, nm_dhcp_client_get_iface (client));
 		}
 	}
 }
 
+static void
+state_changed (NMDhcpClient *client,
+	           NMDhcpState state,
+	           GObject *ip_config,
+	           GHashTable *options)
+{
+	NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (client);
+	gs_unref_bytes GBytes *client_id = NULL;
+
+	if (nm_dhcp_client_get_client_id (client))
+		return;
+	if (state != NM_DHCP_STATE_BOUND)
+		return;
+
+	client_id = nm_dhcp_dhclient_get_client_id_from_config_file (priv->conf_file);
+	nm_dhcp_client_set_client_id (client, client_id);
+}
+
 static GByteArray *
-get_duid (NMDHCPClient *client)
+get_duid (NMDhcpClient *client)
 {
-	NMDHCPDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (client);
+	NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (client);
 	GByteArray *duid = NULL;
 	char *leasefile;
 	GError *error = NULL;
@@ -595,13 +623,11 @@ static const char *def_leasefiles[] = {
 };
 
 static void
-nm_dhcp_dhclient_init (NMDHCPDhclient *self)
+nm_dhcp_dhclient_init (NMDhcpDhclient *self)
 {
-	NMDHCPDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (self);
+	NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (self);
 	const char **iter = &def_leasefiles[0];
 
-	priv->path = nm_dhcp_dhclient_get_path (DHCLIENT_PATH);
-
 	while (iter && *iter) {
 		if (g_file_test (*iter, G_FILE_TEST_EXISTS)) {
 			priv->def_leasefile = *iter;
@@ -613,12 +639,21 @@ nm_dhcp_dhclient_init (NMDHCPDhclient *self)
 	/* Fallback option */
 	if (!priv->def_leasefile)
 		priv->def_leasefile = SYSCONFDIR "/dhclient6.leases";
+
+	g_signal_connect (nm_dhcp_listener_get (),
+	                  NM_DHCP_LISTENER_EVENT,
+	                  G_CALLBACK (nm_dhcp_client_handle_event),
+	                  self);
 }
 
 static void
 dispose (GObject *object)
 {
-	NMDHCPDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (object);
+	NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (object);
+
+	g_signal_handlers_disconnect_by_func (nm_dhcp_listener_get (),
+	                                      G_CALLBACK (nm_dhcp_client_handle_event),
+	                                      NM_DHCP_DHCLIENT (object));
 
 	g_free (priv->pid_file);
 	g_free (priv->conf_file);
@@ -628,12 +663,12 @@ dispose (GObject *object)
 }
 
 static void
-nm_dhcp_dhclient_class_init (NMDHCPDhclientClass *dhclient_class)
+nm_dhcp_dhclient_class_init (NMDhcpDhclientClass *dhclient_class)
 {
-	NMDHCPClientClass *client_class = NM_DHCP_CLIENT_CLASS (dhclient_class);
+	NMDhcpClientClass *client_class = NM_DHCP_CLIENT_CLASS (dhclient_class);
 	GObjectClass *object_class = G_OBJECT_CLASS (dhclient_class);
 
-	g_type_class_add_private (dhclient_class, sizeof (NMDHCPDhclientPrivate));
+	g_type_class_add_private (dhclient_class, sizeof (NMDhcpDhclientPrivate));
 
 	/* virtual methods */
 	object_class->dispose = dispose;
@@ -642,5 +677,16 @@ nm_dhcp_dhclient_class_init (NMDHCPDhclientClass *dhclient_class)
 	client_class->ip6_start = ip6_start;
 	client_class->stop = stop;
 	client_class->get_duid = get_duid;
+	client_class->state_changed = state_changed;
+}
+
+static void __attribute__((constructor))
+register_dhcp_dhclient (void)
+{
+	g_type_init ();
+	_nm_dhcp_client_register (NM_TYPE_DHCP_DHCLIENT,
+	                          "dhclient",
+	                          nm_dhcp_dhclient_get_path,
+	                          nm_dhcp_dhclient_get_lease_ip_configs);
 }