1    	/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2    	/* nm-dhcp-manager.c - Handle the DHCP daemon for NetworkManager
3    	 *
4    	 * This program is free software; you can redistribute it and/or modify
5    	 * it under the terms of the GNU General Public License as published by
6    	 * the Free Software Foundation; either version 2, or (at your option)
7    	 * any later version.
8    	 *
9    	 * This program is distributed in the hope that it will be useful,
10   	 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11   	 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12   	 * GNU General Public License for more details.
13   	 *
14   	 * You should have received a copy of the GNU General Public License along
15   	 * with this program; if not, write to the Free Software Foundation, Inc.,
16   	 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17   	 *
18   	 * Copyright (C) 2005 - 2013 Red Hat, Inc.
19   	 * Copyright (C) 2006 - 2008 Novell, Inc.
20   	 *
21   	 */
22   	
23   	#include "config.h"
24   	#include <glib.h>
25   	#include <glib/gi18n.h>
26   	#include <dbus/dbus.h>
27   	#include <sys/socket.h>
28   	#include <sys/wait.h>
29   	#include <signal.h>
30   	#include <string.h>
31   	#include <stdlib.h>
32   	#include <errno.h>
33   	#include <unistd.h>
34   	#include <fcntl.h>
35   	#include <stdio.h>
36   	
37   	#include "nm-dhcp-manager.h"
38   	#include "nm-dhcp-dhclient.h"
39   	#include "nm-dhcp-dhcpcd.h"
40   	#include "nm-logging.h"
41   	#include "nm-dbus-manager.h"
42   	#include "nm-hostname-provider.h"
43   	#include "nm-config.h"
44   	#include "nm-dbus-glib-types.h"
45   	#include "nm-glib-compat.h"
46   	
47   	GQuark
48   	nm_dhcp_manager_error_quark (void)
49   	{
50   	    static GQuark ret = 0;
51   	
52   	    if (ret == 0)
53   	        ret = g_quark_from_static_string ("nm_dhcp_manager_error");
54   	
55   	    return ret;
56   	}
57   	
58   	#define NM_DHCP_CLIENT_DBUS_IFACE   "org.freedesktop.nm_dhcp_client"
59   	
60   	#define DHCP_TIMEOUT 45 /* default DHCP timeout, in seconds */
61   	
62   	#define PRIV_SOCK_PATH NMRUNDIR "/private-dhcp"
63   	#define PRIV_SOCK_TAG  "dhcp"
64   	
65   	static NMDHCPManager *singleton = NULL;
66   	
67   	/* default to installed helper, but can be modified for testing */
68   	const char *nm_dhcp_helper_path = LIBEXECDIR "/nm-dhcp-helper";
69   	
70   	typedef GSList * (*GetLeaseConfigFunc) (const char *iface, const char *uuid, gboolean ipv6);
71   	
72   	typedef struct {
73   		GType               client_type;
74   		GetLeaseConfigFunc  get_lease_config_func;
75   	
76   		NMDBusManager *     dbus_mgr;
77   		guint               new_conn_id;
78   		guint               dis_conn_id;
79   		GHashTable *        proxies;
80   	
81   		GHashTable *        clients;
82   		DBusGProxy *        proxy;
83   		NMHostnameProvider *hostname_provider;
84   	} NMDHCPManagerPrivate;
85   	
86   	
87   	#define NM_DHCP_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DHCP_MANAGER, NMDHCPManagerPrivate))
88   	
89   	G_DEFINE_TYPE (NMDHCPManager, nm_dhcp_manager, G_TYPE_OBJECT)
90   	
91   	static char *
92   	garray_to_string (GArray *array, const char *key)
93   	{
94   		GString *str;
95   		int i;
96   		unsigned char c;
97   		char *converted = NULL;
98   	
99   		g_return_val_if_fail (array != NULL, NULL);
100  	
101  		/* Since the DHCP options come through environment variables, they should
102  		 * already be UTF-8 safe, but just make sure.
103  		 */
104  		str = g_string_sized_new (array->len);
105  		for (i = 0; i < array->len; i++) {
106  			c = array->data[i];
107  	
108  			/* Convert NULLs to spaces and non-ASCII characters to ? */
109  			if (c == '\0')
110  				c = ' ';
111  			else if (c > 127)
112  				c = '?';
113  			str = g_string_append_c (str, c);
114  		}
115  		str = g_string_append_c (str, '\0');
116  	
117  		converted = str->str;
118  		if (!g_utf8_validate (converted, -1, NULL))
119  			nm_log_warn (LOGD_DHCP, "DHCP option '%s' couldn't be converted to UTF-8", key);
120  		g_string_free (str, FALSE);
121  		return converted;
122  	}
123  	
124  	static NMDHCPClient *
125  	get_client_for_pid (NMDHCPManager *manager, GPid pid)
126  	{
127  		NMDHCPManagerPrivate *priv;
128  		GHashTableIter iter;
129  		gpointer value;
130  	
131  		g_return_val_if_fail (NM_IS_DHCP_MANAGER (manager), NULL);
132  	
133  		priv = NM_DHCP_MANAGER_GET_PRIVATE (manager);
134  	
135  		g_hash_table_iter_init (&iter, priv->clients);
136  		while (g_hash_table_iter_next (&iter, NULL, &value)) {
137  			NMDHCPClient *candidate = NM_DHCP_CLIENT (value);
138  	
139  			if (nm_dhcp_client_get_pid (candidate) == pid)
140  				return candidate;
141  		}
142  	
143  		return NULL;
144  	}
145  	
146  	static NMDHCPClient *
147  	get_client_for_iface (NMDHCPManager *manager,
148  	                      const char *iface,
149  	                      gboolean ip6)
150  	{
151  		NMDHCPManagerPrivate *priv;
152  		GHashTableIter iter;
153  		gpointer value;
154  	
155  		g_return_val_if_fail (NM_IS_DHCP_MANAGER (manager), NULL);
156  		g_return_val_if_fail (iface, NULL);
157  	
158  		priv = NM_DHCP_MANAGER_GET_PRIVATE (manager);
159  	
160  		g_hash_table_iter_init (&iter, priv->clients);
161  		while (g_hash_table_iter_next (&iter, NULL, &value)) {
162  			NMDHCPClient *candidate = NM_DHCP_CLIENT (value);
163  	
164  			if (   !strcmp (iface, nm_dhcp_client_get_iface (candidate))
165  			    && (nm_dhcp_client_get_ipv6 (candidate) == ip6))
166  				return candidate;
167  		}
168  	
169  		return NULL;
170  	}
171  	
172  	static char *
173  	get_option (GHashTable *hash, const char *key)
174  	{
175  		GValue *value;
176  	
177  		value = g_hash_table_lookup (hash, key);
178  		if (value == NULL)
179  			return NULL;
180  	
181  		if (G_VALUE_TYPE (value) != DBUS_TYPE_G_UCHAR_ARRAY) {
182  			nm_log_warn (LOGD_DHCP, "unexpected key %s value type was not "
183  			             "DBUS_TYPE_G_UCHAR_ARRAY",
184  			             (char *) key);
185  			return NULL;
186  		}
187  	
188  		return garray_to_string ((GArray *) g_value_get_boxed (value), key);
189  	}
190  	
191  	static void
192  	nm_dhcp_manager_handle_event (DBusGProxy *proxy,
193  	                              GHashTable *options,
194  	                              gpointer user_data)
195  	{
196  		NMDHCPManager *manager;
197  		NMDHCPManagerPrivate *priv;
198  		NMDHCPClient *client;
199  		char *iface = NULL;
200  		char *pid_str = NULL;
201  		char *reason = NULL;
202  		unsigned long temp;
203  	
204  		manager = NM_DHCP_MANAGER (user_data);
205  		priv = NM_DHCP_MANAGER_GET_PRIVATE (manager);
206  	
207  		iface = get_option (options, "interface");
208  		if (iface == NULL) {
209  			nm_log_warn (LOGD_DHCP, "DHCP event didn't have associated interface.");
210  			goto out;
211  		}
212  	
213  		pid_str = get_option (options, "pid");
214  		if (pid_str == NULL) {
215  			nm_log_warn (LOGD_DHCP, "DHCP event didn't have associated PID.");
216  			goto out;
217  		}
218  	
219  		temp = strtoul (pid_str, NULL, 10);
220  		if ((temp == ULONG_MAX) && (errno == ERANGE)) {
221  			nm_log_warn (LOGD_DHCP, "couldn't convert PID");
222  			goto out;
223  		}
224  	
225  		client = get_client_for_pid (manager, (GPid) temp);
226  		if (client == NULL) {
227  			nm_log_warn (LOGD_DHCP, "(pid %ld) unhandled DHCP event for interface %s", temp, iface);
228  			goto out;
229  		}
230  	
231  		if (strcmp (iface, nm_dhcp_client_get_iface (client))) {
232  			nm_log_warn (LOGD_DHCP, "(pid %ld) received DHCP event from unexpected interface '%s' (expected '%s')",
233  			             temp, iface, nm_dhcp_client_get_iface (client));
234  			goto out;
235  		}
236  	
237  		reason = get_option (options, "reason");
238  		if (reason == NULL) {
239  			nm_log_warn (LOGD_DHCP, "(pid %ld) DHCP event didn't have a reason", temp);
240  			goto out;
241  		}
242  	
243  		nm_dhcp_client_new_options (client, options, reason);
244  	
245  	out:
246  		g_free (iface);
247  		g_free (pid_str);
248  		g_free (reason);
249  	}
250  	
251  	#if HAVE_DBUS_GLIB_100
252  	static void
253  	new_connection_cb (NMDBusManager *mgr,
254  	                   DBusGConnection *connection,
255  	                   NMDHCPManager *self)
256  	{
257  		NMDHCPManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE (self);
258  		DBusGProxy *proxy;
259  	
260  		/* Create a new proxy for the client */
261  		proxy = dbus_g_proxy_new_for_peer (connection, "/", NM_DHCP_CLIENT_DBUS_IFACE);
262  		dbus_g_proxy_add_signal (proxy,
263  		                         "Event",
264  		                         DBUS_TYPE_G_MAP_OF_VARIANT,
265  		                         G_TYPE_INVALID);
266  		dbus_g_proxy_connect_signal (proxy,
267  		                             "Event",
268  		                             G_CALLBACK (nm_dhcp_manager_handle_event),
269  		                             self,
270  		                             NULL);
271  		g_hash_table_insert (priv->proxies, connection, proxy);
272  	}
273  	
274  	static void
275  	dis_connection_cb (NMDBusManager *mgr,
276  	                   DBusGConnection *connection,
277  	                   NMDHCPManager *self)
278  	{
279  		NMDHCPManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE (self);
280  		DBusGProxy *proxy;
281  	
282  		proxy = g_hash_table_lookup (priv->proxies, connection);
283  		if (proxy) {
284  			dbus_g_proxy_disconnect_signal (proxy,
285  			                                "Event",
286  			                                G_CALLBACK (nm_dhcp_manager_handle_event),
287  			                                self);
288  			g_hash_table_remove (priv->proxies, connection);
289  		}
290  	}
291  	#endif
292  	
293  	static GType
294  	get_client_type (const char *client, GError **error)
295  	{
296  		const char *dhclient_path = NULL;
297  		const char *dhcpcd_path = NULL;
298  	
299  		/* If a client was disabled at build-time, its *_PATH define will be
300  		 * an empty string.
301  		 */
(1) Event array_null: Comparing an array to null is not useful: ""/sbin/dhclient"".
302  		if (DHCLIENT_PATH && strlen (DHCLIENT_PATH))
303  			dhclient_path = nm_dhcp_dhclient_get_path (DHCLIENT_PATH);
304  		if (DHCPCD_PATH && strlen (DHCPCD_PATH))
305  			dhcpcd_path = nm_dhcp_dhcpcd_get_path (DHCPCD_PATH);
306  	
307  		if (!client) {
308  			if (dhclient_path)
309  				return NM_TYPE_DHCP_DHCLIENT;
310  			else if (dhcpcd_path)
311  				return NM_TYPE_DHCP_DHCPCD;
312  			else {
313  				g_set_error_literal (error,
314  				                     NM_DHCP_MANAGER_ERROR, NM_DHCP_MANAGER_ERROR_BAD_CLIENT,
315  				                     _("no usable DHCP client could be found."));
316  				return 0;
317  			}
318  		}
319  	
320  		if (!strcmp (client, "dhclient")) {
321  			if (!dhclient_path) {
322  				g_set_error_literal (error,
323  				                     NM_DHCP_MANAGER_ERROR, NM_DHCP_MANAGER_ERROR_BAD_CLIENT,
324  				                     _("'dhclient' could be found."));
325  				return 0;
326  			}
327  			return NM_TYPE_DHCP_DHCLIENT;
328  		}
329  	
330  		if (!strcmp (client, "dhcpcd")) {
331  			if (!dhcpcd_path) {
332  				g_set_error_literal (error,
333  				                     NM_DHCP_MANAGER_ERROR, NM_DHCP_MANAGER_ERROR_BAD_CLIENT,
334  				                     _("'dhcpcd' could be found."));
335  				return 0;
336  			}
337  			return NM_TYPE_DHCP_DHCPCD;
338  		}
339  	
340  		g_set_error (error,
341  		             NM_DHCP_MANAGER_ERROR, NM_DHCP_MANAGER_ERROR_BAD_CLIENT,
342  		             _("unsupported DHCP client '%s'"), client);
343  		return 0;
344  	}
345  	
346  	NMDHCPManager *
347  	nm_dhcp_manager_get (void)
348  	{
349  		NMDHCPManagerPrivate *priv;
350  		const char *client;
351  		GError *error = NULL;
352  	#if !HAVE_DBUS_GLIB_100
353  		DBusGConnection *g_connection;
354  	#endif
355  	
356  		if (singleton)
357  			return g_object_ref (singleton);
358  	
359  		singleton = g_object_new (NM_TYPE_DHCP_MANAGER, NULL);
360  		priv = NM_DHCP_MANAGER_GET_PRIVATE (singleton);
361  	
362  		/* Client-specific setup */
363  		client = nm_config_get_dhcp_client (nm_config_get ());
364  		priv->client_type = get_client_type (client, &error);
365  		if (priv->client_type == NM_TYPE_DHCP_DHCLIENT)
366  			priv->get_lease_config_func = nm_dhcp_dhclient_get_lease_config;
367  		else if (priv->client_type == NM_TYPE_DHCP_DHCPCD)
368  			priv->get_lease_config_func = nm_dhcp_dhcpcd_get_lease_config;
369  		else {
370  			nm_log_warn (LOGD_DHCP, "No usable DHCP client found (%s)! DHCP configurations will fail.",
371  			             error->message);
372  			g_error_free (error);
373  		}
374  	
375  		priv->clients = g_hash_table_new_full (g_direct_hash, g_direct_equal,
376  		                                       NULL,
377  		                                       (GDestroyNotify) g_object_unref);
378  		g_assert (priv->clients);
379  	
380  		priv->dbus_mgr = nm_dbus_manager_get ();
381  	
382  	#if HAVE_DBUS_GLIB_100
383  		/* Register the socket our DHCP clients will return lease info on */
384  		nm_dbus_manager_private_server_register (priv->dbus_mgr, PRIV_SOCK_PATH, PRIV_SOCK_TAG);
385  		priv->new_conn_id = g_signal_connect (priv->dbus_mgr,
386  		                                      NM_DBUS_MANAGER_PRIVATE_CONNECTION_NEW "::" PRIV_SOCK_TAG,
387  		                                      (GCallback) new_connection_cb,
388  		                                      singleton);
389  		priv->dis_conn_id = g_signal_connect (priv->dbus_mgr,
390  		                                      NM_DBUS_MANAGER_PRIVATE_CONNECTION_DISCONNECTED "::" PRIV_SOCK_TAG,
391  		                                      (GCallback) dis_connection_cb,
392  		                                      singleton);
393  	#else
394  		g_connection = nm_dbus_manager_get_connection (priv->dbus_mgr);
395  		priv->proxy = dbus_g_proxy_new_for_name (g_connection,
396  		                                         "org.freedesktop.nm_dhcp_client",
397  		                                         "/",
398  		                                         NM_DHCP_CLIENT_DBUS_IFACE);
399  		g_assert (priv->proxy);
400  		dbus_g_proxy_add_signal (priv->proxy,
401  		                         "Event",
402  		                         DBUS_TYPE_G_MAP_OF_VARIANT,
403  		                         G_TYPE_INVALID);
404  		dbus_g_proxy_connect_signal (priv->proxy, "Event",
405  		                             G_CALLBACK (nm_dhcp_manager_handle_event),
406  		                             singleton,
407  		                             NULL);
408  	#endif
409  		return singleton;
410  	}
411  	
412  	#define REMOVE_ID_TAG "remove-id"
413  	#define TIMEOUT_ID_TAG "timeout-id"
414  	
415  	static void
416  	remove_client (NMDHCPManager *self, NMDHCPClient *client)
417  	{
418  		NMDHCPManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE (self);
419  		guint id;
420  	
421  		id = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (client), REMOVE_ID_TAG));
422  		if (id)
423  			g_signal_handler_disconnect (client, id);
424  	
425  		id = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (client), TIMEOUT_ID_TAG));
426  		if (id)
427  			g_signal_handler_disconnect (client, id);
428  	
429  		/* Stopping the client is left up to the controlling device
430  		 * explicitly since we may want to quit NetworkManager but not terminate
431  		 * the DHCP client.
432  		 */
433  	
434  		g_hash_table_remove (priv->clients, client);
435  	}
436  	
437  	static void
438  	add_client (NMDHCPManager *self, NMDHCPClient *client)
439  	{
440  		NMDHCPManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE (self);
441  		guint id;
442  	
443  		id = g_signal_connect_swapped (client, "remove", G_CALLBACK (remove_client), self);
444  		g_object_set_data (G_OBJECT (client), REMOVE_ID_TAG, GUINT_TO_POINTER (id));
445  	
446  		id = g_signal_connect_swapped (client, "timeout", G_CALLBACK (remove_client), self);
447  		g_object_set_data (G_OBJECT (client), TIMEOUT_ID_TAG, GUINT_TO_POINTER (id));
448  	
449  		g_hash_table_insert (priv->clients, client, g_object_ref (client));
450  	}
451  	
452  	static NMDHCPClient *
453  	client_start (NMDHCPManager *self,
454  	              const char *iface,
455  	              const GByteArray *hwaddr,
456  	              const char *uuid,
457  	              gboolean ipv6,
458  	              NMSettingIP4Config *s_ip4,
459  	              NMSettingIP6Config *s_ip6,
460  	              guint32 timeout,
461  	              guint8 *dhcp_anycast_addr,
462  	              const char *hostname,
463  	              gboolean info_only)
464  	{
465  		NMDHCPManagerPrivate *priv;
466  		NMDHCPClient *client;
467  		gboolean success = FALSE;
468  	
469  		g_return_val_if_fail (self, NULL);
470  		g_return_val_if_fail (NM_IS_DHCP_MANAGER (self), NULL);
471  		g_return_val_if_fail (iface != NULL, NULL);
472  		g_return_val_if_fail (uuid != NULL, NULL);
473  	
474  		priv = NM_DHCP_MANAGER_GET_PRIVATE (self);
475  	
476  		/* Ensure we have a usable DHCP client */
477  		g_return_val_if_fail (priv->client_type != 0, NULL);
478  	
479  		/* Kill any old client instance */
480  		client = get_client_for_iface (self, iface, ipv6);
481  		if (client) {
482  			nm_dhcp_client_stop (client, FALSE);
483  			remove_client (self, client);
484  		}
485  	
486  		/* And make a new one */
487  		client = g_object_new (priv->client_type,
488  		                       NM_DHCP_CLIENT_INTERFACE, iface,
489  		                       NM_DHCP_CLIENT_HWADDR, hwaddr,
490  		                       NM_DHCP_CLIENT_IPV6, ipv6,
491  		                       NM_DHCP_CLIENT_UUID, uuid,
492  		                       NM_DHCP_CLIENT_TIMEOUT, timeout ? timeout : DHCP_TIMEOUT,
493  		                       NULL);
494  		g_return_val_if_fail (client != NULL, NULL);
495  		add_client (self, client);
496  	
497  		if (ipv6)
498  			success = nm_dhcp_client_start_ip6 (client, s_ip6, dhcp_anycast_addr, hostname, info_only);
499  		else
500  			success = nm_dhcp_client_start_ip4 (client, s_ip4, dhcp_anycast_addr, hostname);
501  	
502  		if (!success) {
503  			remove_client (self, client);
504  			g_object_unref (client);
505  			client = NULL;
506  		}
507  	
508  		return client;
509  	}
510  	
511  	/* Caller owns a reference to the NMDHCPClient on return */
512  	NMDHCPClient *
513  	nm_dhcp_manager_start_ip4 (NMDHCPManager *self,
514  	                           const char *iface,
515  	                           const GByteArray *hwaddr,
516  	                           const char *uuid,
517  	                           NMSettingIP4Config *s_ip4,
518  	                           guint32 timeout,
519  	                           guint8 *dhcp_anycast_addr)
520  	{
521  		NMDHCPManagerPrivate *priv;
522  		const char *hostname, *method;
523  		gboolean send_hostname;
524  	
525  		g_return_val_if_fail (self, NULL);
526  		g_return_val_if_fail (NM_IS_DHCP_MANAGER (self), NULL);
527  	
528  		priv = NM_DHCP_MANAGER_GET_PRIVATE (self);
529  	
530  		method = nm_setting_ip4_config_get_method (s_ip4);
531  		g_return_val_if_fail (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) == 0, NULL);
532  	
533  		send_hostname = nm_setting_ip4_config_get_dhcp_send_hostname (s_ip4);
534  		if (send_hostname) {
535  			hostname = nm_setting_ip4_config_get_dhcp_hostname (s_ip4);
536  	
537  			/* If we're supposed to send the hostname to the DHCP server but
538  			 * the user didn't specify one, then use the hostname from the
539  			 * hostname provider if there is one, otherwise use the persistent
540  			 * hostname.
541  			 */
542  			if (!hostname && priv->hostname_provider) {
543  				hostname = nm_hostname_provider_get_hostname (priv->hostname_provider);
544  				if (   hostname
545  				    && (!strcmp (hostname, "localhost.localdomain") ||
546  				        !strcmp (hostname, "localhost6.localdomain6")))
547  					hostname = NULL;
548  			}
549  		} else
550  			hostname = NULL;
551  	
552  		return client_start (self, iface, hwaddr, uuid, FALSE, s_ip4, NULL, timeout, dhcp_anycast_addr, hostname, FALSE);
553  	}
554  	
555  	/* Caller owns a reference to the NMDHCPClient on return */
556  	NMDHCPClient *
557  	nm_dhcp_manager_start_ip6 (NMDHCPManager *self,
558  	                           const char *iface,
559  	                           const GByteArray *hwaddr,
560  	                           const char *uuid,
561  	                           NMSettingIP6Config *s_ip6,
562  	                           guint32 timeout,
563  	                           guint8 *dhcp_anycast_addr,
564  	                           gboolean info_only)
565  	{
566  		NMDHCPManagerPrivate *priv;
567  		const char *hostname;
568  	
569  		g_return_val_if_fail (self, NULL);
570  		g_return_val_if_fail (NM_IS_DHCP_MANAGER (self), NULL);
571  	
572  		priv = NM_DHCP_MANAGER_GET_PRIVATE (self);
573  	
574  		hostname = nm_setting_ip6_config_get_dhcp_hostname (s_ip6);
575  		if (!hostname && priv->hostname_provider) {
576  			hostname = nm_hostname_provider_get_hostname (priv->hostname_provider);
577  			if (   g_strcmp0 (hostname, "localhost.localdomain") == 0
578  			    || g_strcmp0 (hostname, "localhost6.localdomain6") == 0)
579  				hostname = NULL;
580  		}
581  	
582  		return client_start (self, iface, hwaddr, uuid, TRUE, NULL, s_ip6, timeout, dhcp_anycast_addr, hostname, info_only);
583  	}
584  	
585  	static void
586  	hostname_provider_destroyed (gpointer data, GObject *destroyed_object)
587  	{
588  		NM_DHCP_MANAGER_GET_PRIVATE (data)->hostname_provider = NULL;
589  	}
590  	
591  	void
592  	nm_dhcp_manager_set_hostname_provider (NMDHCPManager *manager,
593  										   NMHostnameProvider *provider)
594  	{
595  		NMDHCPManagerPrivate *priv;
596  	
597  		g_return_if_fail (NM_IS_DHCP_MANAGER (manager));
598  	
599  		priv = NM_DHCP_MANAGER_GET_PRIVATE (manager);
600  	
601  		if (priv->hostname_provider) {
602  			g_object_weak_unref (G_OBJECT (priv->hostname_provider), hostname_provider_destroyed, manager);
603  			priv->hostname_provider = NULL;
604  		}
605  	
606  		if (provider) {
607  			priv->hostname_provider = provider;
608  			g_object_weak_ref (G_OBJECT (provider), hostname_provider_destroyed, manager);
609  		}
610  	}
611  	
612  	GSList *
613  	nm_dhcp_manager_get_lease_config (NMDHCPManager *self,
614  	                                  const char *iface,
615  	                                  const char *uuid,
616  	                                  gboolean ipv6)
617  	{
618  		NMDHCPManagerPrivate *priv;
619  	
620  		g_return_val_if_fail (NM_IS_DHCP_MANAGER (self), NULL);
621  		g_return_val_if_fail (iface != NULL, NULL);
622  		g_return_val_if_fail (uuid != NULL, NULL);
623  	
624  		priv = NM_DHCP_MANAGER_GET_PRIVATE (self);
625  	
626  		if (priv->get_lease_config_func)
627  			return priv->get_lease_config_func (iface, uuid, ipv6);
628  	
629  		nm_log_warn (LOGD_DHCP, "Cannot get a DHCP lease config (no usable DHCP client was found!)");
630  		return NULL;
631  	}
632  	
633  	NMIP4Config *
634  	nm_dhcp_manager_test_ip4_options_to_config (const char *dhcp_client,
635  	                                            const char *iface,
636  	                                            GHashTable *options,
637  	                                            const char *reason)
638  	{
639  		NMDHCPClient *client;
640  		NMIP4Config *config;
641  		GType client_type;
642  		GError *error = NULL;
643  	
644  		client_type = get_client_type (dhcp_client, &error);
645  		if (!client_type) {
646  			nm_log_err (LOGD_DHCP4, "error: %s", error ? error->message : "(unknown)");
647  			g_clear_error (&error);
648  			return NULL;
649  		}
650  	
651  		client = (NMDHCPClient *) g_object_new (client_type,
652  		                                        NM_DHCP_CLIENT_INTERFACE, iface,
653  		                                        NULL);
654  		g_return_val_if_fail (client != NULL, NULL);
655  		nm_dhcp_client_new_options (client, options, reason);
656  		config = nm_dhcp_client_get_ip4_config (client, TRUE);
657  		g_object_unref (client);
658  	
659  		return config;
660  	}
661  	
662  	/***************************************************/
663  	
664  	static void
665  	nm_dhcp_manager_init (NMDHCPManager *manager)
666  	{
667  		NMDHCPManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE (manager);
668  	
669  		/* Maps DBusGConnection :: DBusGProxy */
670  		priv->proxies = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref);
671  	}
672  	
673  	static void
674  	dispose (GObject *object)
675  	{
676  		NMDHCPManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE (object);
677  		GList *values, *iter;
678  	
679  		if (priv->clients) {
680  			values = g_hash_table_get_values (priv->clients);
681  			for (iter = values; iter; iter = g_list_next (iter))
682  				remove_client (NM_DHCP_MANAGER (object), NM_DHCP_CLIENT (iter->data));
683  			g_list_free (values);
684  		}
685  	
686  		if (priv->new_conn_id) {
687  			g_signal_handler_disconnect (priv->dbus_mgr, priv->new_conn_id);
688  			priv->new_conn_id = 0;
689  		}
690  		if (priv->dis_conn_id) {
691  			g_signal_handler_disconnect (priv->dbus_mgr, priv->dis_conn_id);
692  			priv->dis_conn_id = 0;
693  		}
694  		priv->dbus_mgr = NULL;
695  	
696  		if (priv->proxies) {
697  			g_hash_table_destroy (priv->proxies);
698  			priv->proxies = NULL;
699  		}
700  		if (priv->proxy)
701  			g_object_unref (priv->proxy);
702  	
703  		G_OBJECT_CLASS (nm_dhcp_manager_parent_class)->dispose (object);
704  	}
705  	
706  	static void
707  	finalize (GObject *object)
708  	{
709  		NMDHCPManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE (object);
710  	
711  		if (priv->hostname_provider) {
712  			g_object_weak_unref (G_OBJECT (priv->hostname_provider), hostname_provider_destroyed, object);
713  			priv->hostname_provider = NULL;
714  		}
715  	
716  		if (priv->clients)
717  			g_hash_table_destroy (priv->clients);
718  	
719  		G_OBJECT_CLASS (nm_dhcp_manager_parent_class)->finalize (object);
720  	}
721  	
722  	static void
723  	nm_dhcp_manager_class_init (NMDHCPManagerClass *manager_class)
724  	{
725  		GObjectClass *object_class = G_OBJECT_CLASS (manager_class);
726  	
727  		g_type_class_add_private (manager_class, sizeof (NMDHCPManagerPrivate));
728  	
729  		/* virtual methods */
730  		object_class->finalize = finalize;
731  		object_class->dispose = dispose;
732  	}
733