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 */
302 if (DHCLIENT_PATH && strlen (DHCLIENT_PATH))
303 dhclient_path = nm_dhcp_dhclient_get_path (DHCLIENT_PATH);
(1) Event array_null: |
Comparing an array to null is not useful: """". |
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