diff options
author | Dan Williams <dcbw@redhat.com> | 2007-06-28 19:19:54 +0000 |
---|---|---|
committer | Dan Williams <dcbw@redhat.com> | 2007-06-28 19:19:54 +0000 |
commit | 0e9f10f311af3b7603a2eaa00136140bb45cb9a1 (patch) | |
tree | 659010efc253c5165ec7397cc21380a943dd8407 | |
parent | f235c3b3a35bd6cd0d2eeb3f1ee35ad3268788a2 (diff) | |
download | NetworkManager-0e9f10f311af3b7603a2eaa00136140bb45cb9a1.tar.gz |
2007-06-28 Dan Williams <dcbw@redhat.com>
Get rid of dhcdbd, based on a patch from Robert Frank. Drive dhclient
directly. Big refactor to make the DHCP manager a GObject subclass with
signals on DHCP events.
git-svn-id: http://svn-archive.gnome.org/svn/NetworkManager/branches/nm-0-6-olpc@2621 4912f4e0-d625-0410-9fb7-b9a5a253dbdc
-rw-r--r-- | ChangeLog | 6 | ||||
-rw-r--r-- | callouts/Makefile.am | 4 | ||||
-rwxr-xr-x | callouts/nm-dhcp-client.action | 12 | ||||
-rw-r--r-- | callouts/nm-dhcp-client.conf | 14 | ||||
-rw-r--r-- | configure.in | 13 | ||||
-rw-r--r-- | src/NetworkManager.c | 4 | ||||
-rw-r--r-- | src/NetworkManagerDbus.c | 19 | ||||
-rw-r--r-- | src/NetworkManagerMain.h | 2 | ||||
-rw-r--r-- | src/NetworkManagerPolicy.c | 38 | ||||
-rw-r--r-- | src/NetworkManagerPolicy.h | 2 | ||||
-rw-r--r-- | src/dhcp-manager/Makefile.am | 28 | ||||
-rw-r--r-- | src/dhcp-manager/nm-dhcp-manager.c | 1312 | ||||
-rw-r--r-- | src/dhcp-manager/nm-dhcp-manager.h | 97 | ||||
-rw-r--r-- | src/dhcp-manager/nm-dhcp-marshal-main.c | 3 | ||||
-rw-r--r-- | src/dhcp-manager/nm-dhcp-marshal.list | 2 | ||||
-rw-r--r-- | src/nm-activation-request.c | 37 | ||||
-rw-r--r-- | src/nm-activation-request.h | 6 | ||||
-rw-r--r-- | src/nm-device-802-11-mesh-olpc.c | 23 | ||||
-rw-r--r-- | src/nm-device.c | 158 |
19 files changed, 1056 insertions, 724 deletions
@@ -1,3 +1,9 @@ +2007-06-28 Dan Williams <dcbw@redhat.com> + + Get rid of dhcdbd, based on a patch from Robert Frank. Drive dhclient + directly. Big refactor to make the DHCP manager a GObject subclass with + signals on DHCP events. + 2007-06-22 Dan Williams <dcbw@redhat.com> * callouts/nm-avahi-autoipd.action diff --git a/callouts/Makefile.am b/callouts/Makefile.am index 2d681bb1b9..6241e2c757 100644 --- a/callouts/Makefile.am +++ b/callouts/Makefile.am @@ -1,8 +1,8 @@ dbusservicedir = $(DBUS_SYS_DIR) -dbusservice_DATA = nm-avahi-autoipd.conf +dbusservice_DATA = nm-avahi-autoipd.conf nm-dhcp-client.conf calloutdir = $(sysconfdir)/NetworkManager/callouts -callout_DATA = nm-avahi-autoipd.action +callout_DATA = nm-avahi-autoipd.action nm-dhcp-client.action EXTRA_DIST = \ $(dbusservice_DATA) \ diff --git a/callouts/nm-dhcp-client.action b/callouts/nm-dhcp-client.action new file mode 100755 index 0000000000..73725711b0 --- /dev/null +++ b/callouts/nm-dhcp-client.action @@ -0,0 +1,12 @@ +#!/bin/bash +# nm-dhcp-client.action +# sends a dbus message to the dhcp-client in nm with environmental variables +# from the dhcp server response + +#Network Manager's way of doing this +/bin/dbus-send \ + --system \ + --type=signal \ + / \ + org.freedesktop.nm_dhcp_client.Event \ + 'string:[dhclient]'"`echo && env | /bin/egrep -v '^(PATH|SHLVL|_|PWD|dhc_dbus)\='`"; diff --git a/callouts/nm-dhcp-client.conf b/callouts/nm-dhcp-client.conf new file mode 100644 index 0000000000..515a1106c6 --- /dev/null +++ b/callouts/nm-dhcp-client.conf @@ -0,0 +1,14 @@ +<!DOCTYPE busconfig PUBLIC + "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN" + "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd"> +<busconfig> + <policy user="root"> + <allow own="org.freedesktop.nm_dhcp_client"/> + <allow send_interface="org.freedesktop.nm_dhcp_client"/> + </policy> + <policy context="default"> + <deny own="org.freedesktop.nm_dhcp_client"/> + <deny send_interface="org.freedesktop.nm_dhcp_client"/> + </policy> +</busconfig> + diff --git a/configure.in b/configure.in index d86b47180f..68c359fa43 100644 --- a/configure.in +++ b/configure.in @@ -200,19 +200,6 @@ fi AC_SUBST(DBUS_SYS_DIR) AC_DEFINE_UNQUOTED(DBUS_SYSTEMD_DIR, "$DBUS_SYS_DIR", [Where system.d dir for DBUS is]) -# dhcdbd binary path -AC_ARG_WITH(dhcdbd, AC_HELP_STRING([--with-dhcdbd=/path/to/dhcdbd], [path to dhcdbd])) -if test "x${with_dhcdbd}" = x; then - AC_PATH_PROG(DHCDBD_BINARY_PATH, dhcdbd, [], $PATH:/sbin:/usr/sbin) - if ! test -x "$DHCDBD_BINARY_PATH"; then - AC_MSG_ERROR(dhcdbd was not installed. See http://people.redhat.com/jvdias/dhcdbd) - fi -else - DHCDBD_BINARY_PATH="$with_dhcdbd" -fi -AC_DEFINE_UNQUOTED(DHCDBD_BINARY_PATH, "$DHCDBD_BINARY_PATH", [Define to path of dhcdbd binary]) -AC_SUBST(DHCDBD_BINARY_PATH) - # wpa_supplicant binary path AC_ARG_WITH(wpa_supplicant, AC_HELP_STRING([--with-wpa_supplicant=/path/to/wpa_supplicant], [path to wpa_supplicant])) if test "x${with_wpa_supplicant}" = x; then diff --git a/src/NetworkManager.c b/src/NetworkManager.c index 613ba59572..1f05b1fe7f 100644 --- a/src/NetworkManager.c +++ b/src/NetworkManager.c @@ -734,7 +734,6 @@ static void nm_data_free (NMData *data) nm_ap_list_unref (data->invalid_ap_list); nm_vpn_manager_dispose (data->vpn_manager); - nm_dhcp_manager_dispose (data->dhcp_manager); g_object_unref (data->named_manager); g_main_loop_unref (data->main_loop); @@ -891,6 +890,7 @@ int main( int argc, char *argv[] ) char * owner; char * pidfile = NULL; char * user_pidfile = NULL; + NMDHCPManager * dhcp_mgr; if (getuid () != 0) { @@ -981,8 +981,8 @@ int main( int argc, char *argv[] ) /* Need to happen after DBUS is initialized */ nm_data->vpn_manager = nm_vpn_manager_new (nm_data); - nm_data->dhcp_manager = nm_dhcp_manager_new (nm_data); nm_data->named_manager = nm_named_manager_new (nm_data->dbus_connection); + dhcp_mgr = nm_dhcp_manager_get (nm_data); /* If NMI is running, grab allowed wireless network lists from it ASAP */ if (nm_dbus_is_info_daemon_running (nm_data->dbus_connection)) diff --git a/src/NetworkManagerDbus.c b/src/NetworkManagerDbus.c index 43e419b16a..3631011c94 100644 --- a/src/NetworkManagerDbus.c +++ b/src/NetworkManagerDbus.c @@ -311,7 +311,6 @@ nm_dbus_signal_mesh_device_change (gpointer user_data) char * mesh_dev_path = NULL; char * primary_dev_path = NULL; const char * sig; - int i = 0; g_return_val_if_fail (cb_data->data, FALSE); g_return_val_if_fail (cb_data->data->dbus_connection, FALSE); @@ -615,6 +614,7 @@ static DBusHandlerResult nm_dbus_signal_filter (DBusConnection *connection, DBus const char * method; gboolean handled = FALSE; DBusError error; + NMDHCPManager * dhcp_manager = nm_dhcp_manager_get (NULL); g_return_val_if_fail (data != NULL, DBUS_HANDLER_RESULT_NOT_YET_HANDLED); g_return_val_if_fail (connection != NULL, DBUS_HANDLER_RESULT_NOT_YET_HANDLED); @@ -673,7 +673,6 @@ static DBusHandlerResult nm_dbus_signal_filter (DBusConnection *connection, DBus nm_hal_deinit (data); dbus_connection_unref (data->dbus_connection); data->dbus_connection = NULL; - nm_dhcp_manager_dispose (data->dhcp_manager); g_thread_create ((GThreadFunc) nm_dbus_reinit, (gpointer) data, FALSE, NULL); handled = TRUE; } @@ -720,18 +719,15 @@ static DBusHandlerResult nm_dbus_signal_filter (DBusConnection *connection, DBus handled = TRUE; } } - else if (nm_dhcp_manager_process_name_owner_changed (data->dhcp_manager, service, old_owner, new_owner) == TRUE) - handled = TRUE; else if (nm_vpn_manager_process_name_owner_changed (data->vpn_manager, service, old_owner, new_owner) == TRUE) handled = TRUE; else if (nm_named_manager_process_name_owner_changed (data->named_manager, service, old_owner, new_owner) == TRUE) handled = TRUE; } - } - else if (dbus_message_is_signal (message, AUTOIPD_CALLOUT_INTERFACE, "AutoIP4Event")) { + } else if (dbus_message_is_signal (message, AUTOIPD_CALLOUT_INTERFACE, "AutoIP4Event")) { handled = nm_dbus_autoip_process_signal (data, message); - } else if (nm_dhcp_manager_process_signal (data->dhcp_manager, message) == TRUE) { - handled = TRUE; + } else if (dbus_message_is_signal (message, DHCP_CALLOUT_INTERFACE, "Event")) { + handled = nm_dhcp_manager_process_signal (dhcp_manager, message); } else if (nm_vpn_manager_process_signal (data->vpn_manager, message) == TRUE) { handled = TRUE; } @@ -935,8 +931,6 @@ static gpointer nm_dbus_reinit (gpointer user_data) if ((owner = get_name_owner (data->dbus_connection, "org.freedesktop.Hal"))) nm_hal_init (data); - data->dhcp_manager = nm_dhcp_manager_new (data); - nm_info ("Successfully reconnected to the system bus."); return NULL; @@ -1005,6 +999,11 @@ DBusConnection *nm_dbus_init (NMData *data) "interface='" AUTOIPD_CALLOUT_INTERFACE "'", NULL); + dbus_bus_add_match (connection, + "type='signal'," + "interface='" DHCP_CALLOUT_INTERFACE "'", + NULL); + if ((owner = get_name_owner (connection, NMI_DBUS_SERVICE))) { char *match = get_nmi_match_string (owner); diff --git a/src/NetworkManagerMain.h b/src/NetworkManagerMain.h index be550b6ba2..e44cad7c6a 100644 --- a/src/NetworkManagerMain.h +++ b/src/NetworkManagerMain.h @@ -49,7 +49,6 @@ typedef struct NMDbusMethodList NMDbusMethodList; typedef struct NMActRequest NMActRequest; typedef struct NMVPNActRequest NMVPNActRequest; typedef struct NMVPNManager NMVPNManager; -typedef struct NMDHCPManager NMDHCPManager; #define DHCP_SERVICE_NAME "com.redhat.dhcp" #define DHCP_OBJECT_PATH "/com/redhat/dhcp" @@ -65,7 +64,6 @@ typedef struct NMData NMNamedManager * named_manager; NMVPNManager * vpn_manager; - NMDHCPManager * dhcp_manager; DBusConnection * dbus_connection; NMDbusMethodList * nm_methods; diff --git a/src/NetworkManagerPolicy.c b/src/NetworkManagerPolicy.c index 542cb7fb79..9f67b831bb 100644 --- a/src/NetworkManagerPolicy.c +++ b/src/NetworkManagerPolicy.c @@ -188,6 +188,44 @@ void nm_policy_schedule_activation_failed (NMActRequest *req) } +static gboolean nm_policy_deactivate (gpointer user_data) +{ + NMDevice * dev = NM_DEVICE (user_data); + NMData * data = NULL; + + g_return_val_if_fail (dev != NULL, FALSE); + + data = nm_device_get_app_data (dev); + g_assert (data); + + nm_device_deactivate (dev); + g_object_unref (dev); + + nm_schedule_state_change_signal_broadcast (data); + nm_policy_schedule_device_change_check (data); + return FALSE; +} + +void nm_policy_schedule_deactivate (NMDevice * dev) +{ + GSource * source; + NMData * data; + + g_return_if_fail (dev != NULL); + + data = nm_device_get_app_data (dev); + g_assert (data); + + source = g_idle_source_new (); + g_source_set_priority (source, G_PRIORITY_HIGH_IDLE); + g_object_ref (dev); + g_source_set_callback (source, (GSourceFunc) nm_policy_deactivate, dev, NULL); + g_source_attach (source, data->main_context); + g_source_unref (source); + nm_info ("Activation (%s) deactivation scheduled...", nm_device_get_iface (dev)); +} + + /* * nm_policy_auto_get_best_device * diff --git a/src/NetworkManagerPolicy.h b/src/NetworkManagerPolicy.h index d927e1d6a3..cbe33a4971 100644 --- a/src/NetworkManagerPolicy.h +++ b/src/NetworkManagerPolicy.h @@ -37,6 +37,8 @@ void nm_policy_schedule_device_ap_lists_update_from_allowed (NMData *app_data) void nm_policy_schedule_activation_finish (NMActRequest *req); void nm_policy_schedule_activation_failed (NMActRequest *req); +void nm_policy_schedule_deactivate (NMDevice * dev); + void nm_policy_add_nbd_notifier (NMDevice *dev); #endif diff --git a/src/dhcp-manager/Makefile.am b/src/dhcp-manager/Makefile.am index 500c9b32dd..c66943c7f5 100644 --- a/src/dhcp-manager/Makefile.am +++ b/src/dhcp-manager/Makefile.am @@ -6,8 +6,14 @@ INCLUDES = -I${top_srcdir} \ noinst_LTLIBRARIES = libdhcp-manager.la -libdhcp_manager_la_SOURCES = nm-dhcp-manager.c \ - nm-dhcp-manager.h +BUILT_SOURCES = \ + nm-dhcp-marshal.h \ + nm-dhcp-marshal.c + +libdhcp_manager_la_SOURCES = \ + nm-dhcp-manager.c \ + nm-dhcp-manager.h \ + nm-dhcp-marshal-main.c libdhcp_manager_la_CPPFLAGS = $(DBUS_CFLAGS) \ $(GTHREAD_CFLAGS) \ @@ -22,3 +28,21 @@ libdhcp_manager_la_CPPFLAGS = $(DBUS_CFLAGS) \ libdhcp_manager_la_LIBADD = $(DBUS_LIBS) $(GTHREAD_LIBS) + +EXTRA_DIST = nm-dhcp-marshal.list +CLEANFILES = $(BUILT_SOURCES) + +nm-dhcp-marshal.h: nm-dhcp-marshal.list + $(GLIB_GENMARSHAL) --prefix=nm_dhcp_marshal $(srcdir)/nm-dhcp-marshal.list --header > \ + xgen-gmh \ + && (cmp -s xgen-gmh nm-dhcp-marshal.h || cp xgen-gmh nm-dhcp-marshal.h) \ + && rm -f xgen-gmh xgen-gmh~ + +nm-dhcp-marshal.c: nm-dhcp-marshal.list + $(GLIB_GENMARSHAL) --prefix=nm_dhcp_marshal $(srcdir)/nm-dhcp-marshal.list --body > \ + xgen-gmc \ + && cp xgen-gmc nm-dhcp-marshal.c \ + && rm -f xgen-gmc xgen-gmc~ + +nm-dhcp-marshal-main.c: nm-dhcp-marshal.c nm-dhcp-marshal.h + diff --git a/src/dhcp-manager/nm-dhcp-manager.c b/src/dhcp-manager/nm-dhcp-manager.c index 3bf49f2a0f..751d214f27 100644 --- a/src/dhcp-manager/nm-dhcp-manager.c +++ b/src/dhcp-manager/nm-dhcp-manager.c @@ -24,758 +24,912 @@ #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> +#include <sys/wait.h> +#include <signal.h> +#include <string.h> #include "nm-dhcp-manager.h" -#include "nm-device.h" -#include "NetworkManagerPolicy.h" -#include "NetworkManagerUtils.h" -#include "NetworkManagerSystem.h" -#include "nm-activation-request.h" +#include "nm-dhcp-marshal.h" #include "nm-utils.h" +#include "NetworkManagerUtils.h" -#define NM_DHCP_TIMEOUT 45 /* DHCP timeout, in seconds */ +#define NM_DHCP_TIMEOUT 45 /* DHCP timeout, in seconds */ -struct NMDHCPManager +static const char *dhclient_binary_paths[] = { + "/sbin/dhclient", + "/usr/sbin/dhclient", + "/usr/local/sbin/dhclient", + NULL +}; + +typedef struct { + char * iface; + guchar state; + GPid dhclient_pid; + GSource * timeout_source; + GSource * cancel_source; + GSource * watch_source; + NMDHCPManager * manager; + GHashTable * options; +} NMDHCPDevice; + +typedef struct { + GHashTable * devices; NMData * data; - gboolean running; - size_t dhcp_sn_len; +} NMDHCPManagerPrivate; + + +#define NM_DHCP_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DHCP_MANAGER, NMDHCPManagerPrivate)) + +G_DEFINE_TYPE (NMDHCPManager, nm_dhcp_manager, G_TYPE_OBJECT) + +enum { + STATE_CHANGED, + TIMEOUT, + + LAST_SIGNAL }; +static guint signals[LAST_SIGNAL] = { 0 }; + +static NMDHCPManager *nm_dhcp_manager_new (NMData * data); + +static void nm_dhcp_manager_cancel_transaction_real (NMDHCPDevice *device, gboolean blocking); + +static char * +get_pidfile_for_iface (const char * iface) +{ + return g_strdup_printf ("%s/%s-%s.%s", + NM_DHCP_MANAGER_PID_DIR, + NM_DHCP_MANAGER_PID_FILENAME, + iface, + NM_DHCP_MANAGER_PID_FILE_EXT); +} + + +NMDHCPManager * +nm_dhcp_manager_get (NMData * data) +{ + static GStaticMutex mutex = G_STATIC_MUTEX_INIT; + static NMDHCPManager *singleton = NULL; + + g_static_mutex_lock (&mutex); + if (!singleton) { + if (!data) { + nm_error ("Initial creation must pass NMData"); + g_assert_not_reached (); + } + singleton = nm_dhcp_manager_new (data); + } + g_object_ref (singleton); + g_static_mutex_unlock (&mutex); + + return singleton; +} -char *get_dhcp_match_string (const char *owner) +static void +nm_dhcp_manager_init (NMDHCPManager *msg) { - g_return_val_if_fail (owner != NULL, NULL); +} - return g_strdup_printf ("type='signal',interface='" DHCP_SERVICE_NAME ".state',sender='%s'", owner); +static void +finalize (GObject *object) +{ + G_OBJECT_CLASS (nm_dhcp_manager_parent_class)->finalize (object); } +static void +nm_dhcp_manager_class_init (NMDHCPManagerClass *manager_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (manager_class); + + g_type_class_add_private (manager_class, sizeof (NMDHCPManagerPrivate)); + + /* virtual methods */ + object_class->finalize = finalize; + + /* signals */ + signals[STATE_CHANGED] = + g_signal_new ("state-changed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMDHCPManagerClass, state_changed), + NULL, NULL, + nm_dhcp_marshal_VOID__STRING_UCHAR, + G_TYPE_NONE, 2, + G_TYPE_STRING, + G_TYPE_UCHAR); + + signals[TIMEOUT] = + g_signal_new ("timeout", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMDHCPManagerClass, timeout), + NULL, NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, 1, + G_TYPE_STRING); +} static gboolean state_is_bound (guint8 state) { - if ( (state == 2) /* BOUND */ - || (state == 3) /* RENEW */ - || (state == 4) /* REBOOT */ - || (state == 5)) /* REBIND */ + if ((state == DHC_BOUND) + || (state == DHC_RENEW) + || (state == DHC_REBOOT) + || (state == DHC_REBIND) + || (state == DHC_START)) return TRUE; return FALSE; } -static gboolean state_is_down (guint8 state) +static void +nm_dhcp_device_timeout_cleanup (NMDHCPDevice * device) { - if ( (state == 0) /* NBI */ - || (state == 11) /* RELEASE */ - || (state == 13) /* ABEND */ - || (state == 14)) /* END */ - return TRUE; - - return FALSE; + if (!device->timeout_source) + return; + g_source_destroy (device->timeout_source); + g_source_unref (device->timeout_source); + device->timeout_source = NULL; } +static void +nm_dhcp_device_cancel_cleanup (NMDHCPDevice * device) +{ + if (!device->cancel_source) + return; +fprintf (stderr, "%s: cleaning up cancel source %p\n", __func__, device->cancel_source); + g_source_destroy (device->cancel_source); + g_source_unref (device->cancel_source); + device->cancel_source = NULL; +} -NMDHCPManager * nm_dhcp_manager_new (NMData *data) +static void +nm_dhcp_device_watch_cleanup (NMDHCPDevice * device) { - NMDHCPManager * manager; - char * owner; + if (!device->watch_source) + return; + g_source_destroy (device->watch_source); + g_source_unref (device->watch_source); + device->watch_source = NULL; +} - g_return_val_if_fail (data != NULL, NULL); - g_return_val_if_fail (data->dbus_connection != NULL, NULL); +static void +nm_dhcp_device_destroy (NMDHCPDevice *device) +{ + nm_dhcp_device_timeout_cleanup (device); +fprintf (stderr, "%s: calling cancel_cleanup\n", __func__); + nm_dhcp_device_cancel_cleanup (device); + nm_dhcp_device_watch_cleanup (device); + g_hash_table_remove_all (device->options); + g_free (device->iface); + g_slice_free (NMDHCPDevice, device); +} - manager = g_malloc0 (sizeof (NMDHCPManager)); - manager->data = data; - manager->running = dbus_bus_name_has_owner (manager->data->dbus_connection, DHCP_SERVICE_NAME, NULL); - manager->dhcp_sn_len = strlen (DHCP_SERVICE_NAME); - if (manager->running && (owner = get_name_owner (data->dbus_connection, DHCP_SERVICE_NAME))) +static inline const char * +state_to_string (guint32 state) +{ + switch (state) { - char *match = get_dhcp_match_string (owner); - dbus_bus_add_match (data->dbus_connection, match, NULL); - g_free (match); - g_free (owner); + case DHC_PREINIT: + return "preinit"; + case DHC_BOUND: + return "bound"; + case DHC_RENEW: + return "renew"; + case DHC_REBOOT: + return "reboot"; + case DHC_REBIND: + return "rebind"; + case DHC_STOP: + return "stop"; + case DHC_MEDIUM: + return "medium"; + case DHC_TIMEOUT: + return "timeout"; + case DHC_FAIL: + return "fail"; + case DHC_EXPIRE: + return "expire"; + case DHC_RELEASE: + return "release"; + case DHC_START: + return "successfully started"; + case DHC_ABEND: + return "abnormal exit"; + case DHC_END: + return "normal exit"; + default: + break; } - - return manager; + return NULL; } - -void nm_dhcp_manager_dispose (NMDHCPManager *manager) +static inline guint32 +string_to_state (const char *state) { - g_return_if_fail (manager != NULL); - - memset (manager, 0, sizeof (NMDHCPManager)); - g_free (manager); + if (strcmp("PREINIT", state) == 0) + return DHC_PREINIT; + else if (strcmp("BOUND", state) == 0) + return DHC_BOUND; + else if (strcmp("RENEW", state) == 0) + return DHC_RENEW; + else if (strcmp("REBOOT", state) == 0) + return DHC_REBOOT; + else if (strcmp("REBIND", state) == 0) + return DHC_REBIND; + else if (strcmp("STOP", state) == 0) + return DHC_STOP; + else if (strcmp("MEDIUM", state) == 0) + return DHC_MEDIUM; + else if (strcmp("TIMEOUT", state) == 0) + return DHC_TIMEOUT; + else if (strcmp("FAIL", state) == 0) + return DHC_FAIL; + else if (strcmp("EXPIRE", state) == 0) + return DHC_EXPIRE; + else if (strcmp("RELEASE", state) == 0) + return DHC_RELEASE; + else if (strcmp("START", state) == 0) + return DHC_START; + else if (strcmp("ABEND", state) == 0) + return DHC_ABEND; + else if (strcmp("END", state) == 0) + return DHC_END; + else + return 255; } -guint32 nm_dhcp_manager_get_state_for_device (NMDHCPManager *manager, NMDevice *dev) +/* + * nm_dhcp_dbus_set_state + * + * Set the dhcp state for the interface (and store the data that dhclient gave us) + * + */ +static gboolean +nm_dhcp_dbus_set_state (NMDHCPManager *manager, + DBusMessage *message) { - DBusMessage * message; - DBusMessage * reply; - char * path; - guint32 state = 0; + NMDHCPManagerPrivate * priv; + NMDHCPDevice * device; + const char * data = NULL; DBusError error; + gboolean success = FALSE; + char * iface; + gchar ** keys; + GKeyFile * keyfile = NULL; + GError * gerror = NULL; + gsize length; + int i; - g_return_val_if_fail (manager != NULL, 0); - g_return_val_if_fail (dev != NULL, 0); + g_return_val_if_fail (manager != NULL, FALSE); + g_return_val_if_fail (message != NULL, FALSE); + + priv = NM_DHCP_MANAGER_GET_PRIVATE (manager); - if (!manager->running) + dbus_error_init (&error); + if (!dbus_message_get_args (message, &error, + DBUS_TYPE_STRING, &data, + DBUS_TYPE_INVALID)) { - nm_warning ("dhcdbd not running!"); - return 0; + nm_warning ("Could not process the request because its arguments were " + " invalid. dbus said: '%s'", + error.message); + dbus_error_free (&error); + goto out; } - path = g_strdup_printf (DHCP_OBJECT_PATH"/%s", nm_device_get_iface (dev)); - message = dbus_message_new_method_call (DHCP_SERVICE_NAME, path, DHCP_SERVICE_NAME".dbus.get", "reason"); - g_free (path); - if (message == NULL) - { - nm_warning ("nm_dhcp_manager_get_state_for_device(): Couldn't allocate the dbus message"); - return 0; + keyfile = g_key_file_new (); + if (!g_key_file_load_from_data (keyfile, + (gchar *) data, + strlen (data), + 0, + &gerror)) { + nm_warning ("Unable to parse data from dhclient: '%s'", gerror->message); + g_error_free (gerror); + goto out; } - dbus_error_init (&error); - reply = dbus_connection_send_with_reply_and_block (manager->data->dbus_connection, message, -1, &error); - dbus_message_unref (message); - if (dbus_error_is_set (&error)) - { - if (strcmp (error.name, "org.freedesktop.DBus.Error.UnknownMethod") != 0) - nm_info ("Error from dhcdbd on 'reason' request because: name '%s', message '%s'.", error.name, error.message); - dbus_error_free (&error); + /* Grab the device, if it exists... and set options for it (since as of + * now all interfaces are through the same dbus object) + */ + iface = (char *) g_key_file_get_string (keyfile, "dhclient", "interface", NULL); + if (!iface) { + nm_warning ("Couldn't get dhclient interface from options."); + goto out; } - else if (reply) - { - if (!dbus_message_get_args (reply, NULL, DBUS_TYPE_UINT32, &state, DBUS_TYPE_INVALID)) - state = 0; - dbus_message_unref (reply); + + device = (NMDHCPDevice *) g_hash_table_lookup (priv->devices, iface); + if (!device) { + nm_warning ("Interface %s not registered for DHCP", iface); + goto out; + } + + nm_dhcp_device_timeout_cleanup (device); + + keys = g_key_file_get_keys (keyfile, "dhclient", &length, NULL); + if (!keys) { + nm_warning ("Not enough memory for parsing dhclient options."); + goto out; } - return state; + for (i = 0; i < length; i++) { + char * key, * value; + + key = g_strdup (keys[i]); + if (!key) { + nm_warning ("Not enough memory to parse dhclient option %s", keys[i]); + goto done_parse; + } + + value = g_strdup (g_key_file_get_string (keyfile, "dhclient", key, NULL)); + if (!key) { + nm_warning ("Not enough memory to parse dhclient option %s value.", keys[i]); + goto done_parse; + } + + g_hash_table_insert (device->options, key, value); + if (strcmp (keys[i], "reason") == 0) { + guint32 old_state = device->state; + device->state = string_to_state (value); + nm_info ("DHCP: device %s state changed %s -> %s\n", + device->iface, + state_to_string (old_state), + state_to_string (device->state)); + } + } + +done_parse: + g_strfreev (keys); + + g_signal_emit (G_OBJECT (device->manager), signals[STATE_CHANGED], 0, device->iface, device->state); + success = TRUE; + +out: + if (keyfile) + g_key_file_free (keyfile); + return success; } -static guint32 -get_timeout_secs (NMActRequest *req) +gboolean +nm_dhcp_manager_process_signal (NMDHCPManager * manager, + DBusMessage *message) { - guint req_secs = nm_act_request_get_dhcp_timeout_wait (req); - if (!req_secs) - return NM_DHCP_TIMEOUT; - return req_secs; + const char * method; + const char * path; + + g_return_val_if_fail (message != NULL, FALSE); + + method = dbus_message_get_member (message); + path = dbus_message_get_path (message); + + if (strcmp ("Event", method) || strcmp (path, "/")) + return FALSE; + + nm_dhcp_dbus_set_state (manager, message); + return TRUE; } + +static NMDHCPManager * +nm_dhcp_manager_new (NMData * data) +{ + NMDHCPManager *manager; + NMDHCPManagerPrivate *priv; + + manager = g_object_new (NM_TYPE_DHCP_MANAGER, NULL); + priv = NM_DHCP_MANAGER_GET_PRIVATE (manager); + + priv->devices = g_hash_table_new_full (g_str_hash, g_str_equal, + NULL, + (GDestroyNotify) nm_dhcp_device_destroy); + priv->data = data; + + return manager; +} + + /* * nm_dhcp_manager_handle_timeout * * Called after timeout of a DHCP transaction to notify device of the failure. * */ -static gboolean nm_dhcp_manager_handle_timeout (NMActRequest *req) +static gboolean +nm_dhcp_manager_handle_timeout (gpointer user_data) { - NMData * data; - NMDevice * dev; - - g_return_val_if_fail (req != NULL, FALSE); - - data = nm_act_request_get_data (req); - g_assert (data); - - dev = nm_act_request_get_dev (req); - g_assert (dev); + NMDHCPDevice *device = (NMDHCPDevice *) user_data; nm_info ("Device '%s' DHCP transaction took too long (>%ds), stopping it.", - nm_device_get_iface (dev), get_timeout_secs (req)); + device->iface, NM_DHCP_TIMEOUT); - if (nm_act_request_get_stage (req) == NM_ACT_STAGE_IP_CONFIG_START) - { - nm_act_request_set_dhcp_timeout (req, 0); - nm_dhcp_manager_cancel_transaction (data->dhcp_manager, req); - nm_device_activate_schedule_stage4_ip_config_timeout (req); - } + g_signal_emit (G_OBJECT (device->manager), signals[TIMEOUT], 0, device->iface); + + nm_dhcp_manager_cancel_transaction (device->manager, device->iface, FALSE); return FALSE; } - -gboolean nm_dhcp_manager_begin_transaction (NMDHCPManager *manager, NMActRequest *req) +static NMDHCPDevice * +nm_dhcp_device_new (NMDHCPManager *manager, const char *iface) { - DBusError error; - DBusMessage * message; - DBusMessage * reply; - NMDevice * dev; - char * path; - const guint32 opt1 = 31; /* turns off ALL actions and dhclient-script just writes options to dhcdbd */ - const guint32 opt2 = 2; /* dhclient is run in ONE SHOT mode and releases existing leases when brought down */ - GSource * source; + NMDHCPDevice *device; + GHashTable * hash = NM_DHCP_MANAGER_GET_PRIVATE (manager)->devices; - g_return_val_if_fail (manager != NULL, FALSE); - g_return_val_if_fail (req != NULL, FALSE); - - if (!manager->running) - { - nm_warning ("dhcdbd not running!"); - return FALSE; - } - else - { - /* Cancel any DHCP transaction already in progress */ - nm_dhcp_manager_cancel_transaction (manager, req); - /* FIXME don't sleep */ - sleep (1); + device = g_slice_new0 (NMDHCPDevice); + if (!device) { + nm_warning ("%s: Out of memory creating DHCP transaction object.", iface); + return NULL; } - dev = nm_act_request_get_dev (req); - g_assert (dev); - - nm_info ("Activation (%s) Beginning DHCP transaction.", nm_device_get_iface (dev)); - - path = g_strdup_printf (DHCP_OBJECT_PATH"/%s", nm_device_get_iface (dev)); - message = dbus_message_new_method_call (DHCP_SERVICE_NAME, path, DHCP_SERVICE_NAME, "up"); - g_free (path); - if (message == NULL) - { - nm_warning ("nm_dhcp_manager_begin_transaction(): Couldn't allocate the dbus message"); - return FALSE; + device->iface = g_strdup (iface); + if (!device) { + nm_warning ("%s: Out of memory creating DHCP transaction object " + "property 'iface'.", + iface); + goto error; } - - dbus_message_append_args (message, DBUS_TYPE_UINT32, &opt1, DBUS_TYPE_UINT32, &opt2, DBUS_TYPE_INVALID); - dbus_error_init (&error); - if ((reply = dbus_connection_send_with_reply_and_block (manager->data->dbus_connection, message, -1, &error))) - dbus_message_unref (reply); - dbus_message_unref (message); - if (dbus_error_is_set (&error)) - { - nm_info ("Couldn't send DHCP 'up' message because: name '%s', message '%s'.", error.name, error.message); - dbus_error_free (&error); - return FALSE; + + device->manager = manager; + + nm_dhcp_manager_cancel_transaction_real (device, FALSE); + + /* Do this after the transaction cancel since that clears options out */ + device->options = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + g_free); + if (!device->options) { + nm_warning ("%s: Out of memory creating DHCP transaction object " + "property 'options'.", + iface); + goto error; } - /* Set up a timeout on the transaction to kill it after NM_DHCP_TIMEOUT seconds */ - source = g_timeout_source_new (get_timeout_secs (req) * 1000); - g_source_set_callback (source, (GSourceFunc) nm_dhcp_manager_handle_timeout, req, NULL); - nm_act_request_set_dhcp_timeout (req, g_source_attach (source, manager->data->main_context)); - g_source_unref (source); + g_hash_table_insert (hash, device->iface, device); + return device; - return TRUE; +error: + g_hash_table_destroy (device->options); + g_free (device->iface); + g_slice_free (NMDHCPDevice, device); + return NULL; } -static void remove_timeout (NMDHCPManager *manager, NMActRequest *req) +/* + * nm_dhcp_manager_get_option + * + * Return the requested dhcp item for the given interface + * + */ +static gpointer +nm_dhcp_manager_get_option(NMDHCPDevice *device, const char *key) { - guint id; - - g_return_if_fail (manager != NULL); - g_return_if_fail (req != NULL); - - /* Remove any pending timeouts on the request */ - if ((id = nm_act_request_get_dhcp_timeout (req)) > 0) - { - GSource * source = g_main_context_find_source_by_id (manager->data->main_context, id); - nm_act_request_set_dhcp_timeout (req, 0); - g_source_destroy (source); - } + return g_hash_table_lookup (device->options, key); } + /* - * nm_dhcp_manager_cancel_transaction + * dhclient_watch_cb * - * Stop any in-progress DHCP transaction on a particular device. + * Watch our child dhclient process and get notified of events from it. * */ -void nm_dhcp_manager_cancel_transaction (NMDHCPManager *manager, NMActRequest *req) +static void dhclient_watch_cb (GPid pid, gint status, gpointer user_data) { - NMDevice *dev; + NMDHCPDevice *device = (NMDHCPDevice *)user_data; - g_return_if_fail (manager != NULL); - g_return_if_fail (req != NULL); - - dev = nm_act_request_get_dev (req); - g_assert (dev); + if (!WIFEXITED (status)) { + device->state = DHC_ABEND; + nm_warning ("dhclient died abnormally"); + } + device->dhclient_pid = 0; - if (manager->running && !state_is_down (nm_act_request_get_dhcp_state (req))) - { - DBusMessage * message; - char * path = g_strdup_printf (DHCP_OBJECT_PATH"/%s", nm_device_get_iface (dev)); - - if ((message = dbus_message_new_method_call (DHCP_SERVICE_NAME, path, DHCP_SERVICE_NAME, "down"))) - { - dbus_connection_send (manager->data->dbus_connection, message, NULL); - dbus_message_unref (message); - - /* Give dhcdbd/dhclient some time to send out a RELEASE if they like */ - /* FIXME: we should really monitor the interface's DHCP state by waiting - * for dhcdbd to tell us the device is "down" rather than sleeping here. - */ - if (!manager->data->asleep) - sleep (1); - } - g_free (path); + nm_dhcp_device_watch_cleanup (device); + nm_dhcp_device_timeout_cleanup (device); - remove_timeout (manager, req); - } + g_signal_emit (G_OBJECT (device->manager), signals[STATE_CHANGED], 0, device->iface, device->state); } - -static gboolean get_ip4_uint32s (NMDHCPManager *manager, NMDevice *dev, const char *item, - guint32 **ip4_uint32, guint32 *num_items, gboolean ignore_error) +static gboolean +dhclient_run (NMDHCPDevice *device, gchar *xtra_arg) { - DBusMessage * message = NULL; - DBusMessage * reply = NULL; - char * path; + const char ** dhclient_binary = NULL; + GPtrArray * dhclient_argv = NULL; + GPid pid; + GError * error = NULL; + char * pidfile = NULL; gboolean success = FALSE; + NMDHCPManagerPrivate * priv; + + /* Find dhclient */ + dhclient_binary = dhclient_binary_paths; + while (*dhclient_binary != NULL) { + if (g_file_test (*dhclient_binary, G_FILE_TEST_EXISTS)) + break; + dhclient_binary++; + } - g_return_val_if_fail (manager != NULL, FALSE); - g_return_val_if_fail (dev != NULL, FALSE); - g_return_val_if_fail (ip4_uint32 != NULL, FALSE); - g_return_val_if_fail (num_items != NULL, FALSE); - - *ip4_uint32 = NULL; - *num_items = 0; - path = g_strdup_printf (DHCP_OBJECT_PATH"/%s", nm_device_get_iface (dev)); - if ((message = dbus_message_new_method_call (DHCP_SERVICE_NAME, path, DHCP_SERVICE_NAME".dbus.get", item))) - { - DBusError error; - - dbus_error_init (&error); - reply = dbus_connection_send_with_reply_and_block (manager->data->dbus_connection, message, -1, &error); - if (reply) - { - GArray *buffer; - DBusMessageIter iter; - - dbus_message_iter_init (reply, &iter); - - buffer = g_array_new (TRUE, TRUE, sizeof (guint32)); - while (dbus_message_iter_get_arg_type (&iter) == DBUS_TYPE_UINT32) - { - guint32 value; - - dbus_message_iter_get_basic (&iter, &value); - g_array_append_val (buffer, value); - dbus_message_iter_next (&iter); - success = TRUE; - } - - if (success) - { - *ip4_uint32 = (guint32 *)(buffer->data); - *num_items = buffer->len; - } - g_array_free (buffer, FALSE); - dbus_message_unref (reply); - } + if (!*dhclient_binary) { + nm_warning ("Could not find dhclient binary."); + goto out; + } - if (dbus_error_is_set (&error)) - { - if (!ignore_error) - nm_warning ("get_ip4_uint32s(): error calling '%s', DHCP daemon returned error '%s', message '%s'.", - item, error.name, error.message); - dbus_error_free (&error); - } - dbus_message_unref (message); + pidfile = get_pidfile_for_iface (device->iface); + if (!pidfile) { + nm_warning ("%s: not enough memory for dhclient options.", device->iface); + goto out; } - g_free (path); - return success; -} + // FIXME: look for existing pidfile and kill dhclient + dhclient_argv = g_ptr_array_new (); + g_ptr_array_add (dhclient_argv, (gpointer) (*dhclient_binary)); -static gboolean get_ip4_string (NMDHCPManager *manager, NMDevice *dev, const char *item, - char **string, gboolean ignore_error) -{ - DBusMessage * message = NULL; - DBusMessage * reply = NULL; - char * path; - gboolean success = FALSE; + g_ptr_array_add (dhclient_argv, (gpointer) "-d"); + g_ptr_array_add (dhclient_argv, (gpointer) "-x"); - g_return_val_if_fail (manager != NULL, FALSE); - g_return_val_if_fail (dev != NULL, FALSE); - g_return_val_if_fail (string != NULL, FALSE); + if (xtra_arg != NULL) + g_ptr_array_add (dhclient_argv, (gpointer) xtra_arg); - *string = NULL; - path = g_strdup_printf (DHCP_OBJECT_PATH"/%s", nm_device_get_iface (dev)); - if ((message = dbus_message_new_method_call (DHCP_SERVICE_NAME, path, DHCP_SERVICE_NAME".dbus.get", item))) - { - DBusError error; - - dbus_error_init (&error); - if ((reply = dbus_connection_send_with_reply_and_block (manager->data->dbus_connection, message, -1, &error))) - { - DBusMessageIter iter; - - dbus_message_iter_init (reply, &iter); - if (dbus_message_iter_get_arg_type (&iter) == DBUS_TYPE_STRING) - { - char *dbus_string; - - dbus_error_init (&error); - if (dbus_message_get_args (reply, &error, DBUS_TYPE_STRING, &dbus_string, DBUS_TYPE_INVALID)) - { - *string = g_strdup (dbus_string); - success = TRUE; - } - } - else if (dbus_message_iter_get_arg_type (&iter) == DBUS_TYPE_ARRAY) - { - char *byte_array = NULL; - int len = 0; - - dbus_error_init (&error); - if (dbus_message_get_args (reply, &error, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &byte_array, &len, DBUS_TYPE_INVALID)) - { - byte_array[len] = '\0'; - *string = g_strdup (byte_array); - success = TRUE; - } - } - } + g_ptr_array_add (dhclient_argv, (gpointer) "-sf"); /* Set script file */ + g_ptr_array_add (dhclient_argv, (gpointer) SYSCONFDIR "/NetworkManager/callouts/nm-dhcp-client.action" ); - if (dbus_error_is_set (&error)) - { - if (!ignore_error) - nm_warning ("get_ip4_string(): error calling '%s', DHCP daemon returned error '%s', message '%s'.", - item, error.name, error.message); - dbus_error_free (&error); - *string = NULL; - } - dbus_message_unref (message); + g_ptr_array_add (dhclient_argv, (gpointer) "-pf"); /* Set pid file */ + g_ptr_array_add (dhclient_argv, (gpointer) pidfile); + + g_ptr_array_add (dhclient_argv, (gpointer) device->iface); + g_ptr_array_add (dhclient_argv, NULL); + + if (!g_spawn_async (NULL, (char **) dhclient_argv->pdata, NULL, + G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, &pid, &error)) { + nm_warning ("dhclient failed to start. error: '%s'", error->message); + g_error_free (error); + goto out; } - g_free (path); - return success; -} + nm_info ("dhclient started with pid %d", pid); + device->dhclient_pid = pid; + device->watch_source = g_child_watch_source_new (pid); + g_source_set_callback (device->watch_source, + (GSourceFunc) dhclient_watch_cb, + device, + NULL); -static gboolean nm_completion_dhcp_bound_test(int tries, - nm_completion_args args) -{ - NMActRequest * req = args[0]; - NMDevice * dev = args[1]; + priv = NM_DHCP_MANAGER_GET_PRIVATE (device->manager); + g_source_attach (device->watch_source, priv->data->main_context); + success = TRUE; - if (state_is_bound (nm_act_request_get_dhcp_state (req))) - return TRUE; - if (nm_device_activation_should_cancel (dev)) - return TRUE; - return FALSE; +out: + g_free (pidfile); + g_ptr_array_free (dhclient_argv, TRUE); + return success; } -/* - * nm_dhcp_manager_get_ip4_config - * - * Get IP4 configuration values from the DHCP daemon - * - */ -NMIP4Config * nm_dhcp_manager_get_ip4_config (NMDHCPManager *manager, NMActRequest *req) +gboolean +nm_dhcp_manager_begin_transaction (NMDHCPManager *manager, + const char *iface, + guint32 timeout) { - NMDevice * dev; - NMIP4Config * ip4_config = NULL; - int i; - guint32 count = 0; - guint32 * ip4_address = NULL; - guint32 * ip4_netmask = NULL; - guint32 * ip4_broadcast = NULL; - guint32 * ip4_nameservers = NULL; - guint32 * ip4_gateway = NULL; - guint32 num_ip4_nameservers = 0; - guint32 num_ip4_nis_servers = 0; - char * hostname = NULL; - char * domain_names = NULL; - char * nis_domain = NULL; - guint32 * ip4_nis_servers = NULL; - struct in_addr temp_addr; - nm_completion_args args; - - g_return_val_if_fail (manager != NULL, NULL); - g_return_val_if_fail (req != NULL, NULL); - - if (!manager->running) - return NULL; + NMDHCPManagerPrivate *priv; + NMDHCPDevice *device; - dev = nm_act_request_get_dev (req); - g_assert (dev); + g_return_val_if_fail (NM_IS_DHCP_MANAGER (manager), FALSE); + g_return_val_if_fail (iface != NULL, FALSE); - args[0] = req; - args[1] = dev; - nm_wait_for_completion (30, G_USEC_PER_SEC / 10, - nm_completion_dhcp_bound_test, NULL, args); - if (nm_device_activation_should_cancel (dev)) - return NULL; + priv = NM_DHCP_MANAGER_GET_PRIVATE (manager); - if (!state_is_bound (nm_act_request_get_dhcp_state (req))) - { - nm_warning ("Tried to get IP4 Config for a device when dhcdbd wasn't in a BOUND state!"); - return NULL; - } + device = (NMDHCPDevice *) g_hash_table_lookup (priv->devices, iface); + if (!device) + device = nm_dhcp_device_new (manager, iface); - if (!get_ip4_uint32s (manager, dev, "ip_address", &ip4_address, &count, FALSE) || !count) - goto out; + if (state_is_bound (device->state)) { + /* Cancel any DHCP transaction already in progress */ + nm_dhcp_manager_cancel_transaction_real (device, TRUE); + } - if (!get_ip4_uint32s (manager, dev, "subnet_mask", &ip4_netmask, &count, FALSE) || !count) - goto out; + nm_info ("Activation (%s) Beginning DHCP transaction.", iface); - if (!get_ip4_uint32s (manager, dev, "broadcast_address", &ip4_broadcast, &count, FALSE) || !count) - goto out; + if (timeout == 0) + timeout = NM_DHCP_TIMEOUT; - if (!get_ip4_uint32s (manager, dev, "routers", &ip4_gateway, &count, TRUE) || !count) - { - /* If DHCP doesn't have a 'routers', just use the DHCP server's address as our gateway for now */ - if (!get_ip4_uint32s (manager, dev, "dhcp_server_identifier", &ip4_gateway, &count, FALSE) || !count) - goto out; - } + /* Set up a timeout on the transaction to kill it after the timeout */ + device->timeout_source = g_timeout_source_new (timeout * 1000); + g_source_set_callback (device->timeout_source, + nm_dhcp_manager_handle_timeout, + device, + NULL); + g_source_attach (device->timeout_source, + priv->data->main_context); - get_ip4_string (manager, dev, "host_name", &hostname, TRUE); - get_ip4_uint32s (manager, dev, "domain_name_servers", &ip4_nameservers, &num_ip4_nameservers, FALSE); - get_ip4_string (manager, dev, "domain_name", &domain_names, TRUE); - get_ip4_string (manager, dev, "nis_domain", &nis_domain, TRUE); - get_ip4_uint32s (manager, dev, "nis_servers", &ip4_nis_servers, &num_ip4_nis_servers, TRUE); + dhclient_run (device, NULL); - nm_info ("Retrieved the following IP4 configuration from the DHCP daemon:"); + return TRUE; +} - ip4_config = nm_ip4_config_new (); - nm_ip4_config_set_address (ip4_config, ip4_address[0]); - temp_addr.s_addr = ip4_address[0]; - nm_info (" address %s", inet_ntoa (temp_addr)); +static void +nm_dhcp_manager_cancel_transaction_real (NMDHCPDevice *device, gboolean blocking) +{ + int i = 20; /* 4 seconds */ + char * pidfile; - nm_ip4_config_set_netmask (ip4_config, ip4_netmask[0]); - temp_addr.s_addr = ip4_netmask[0]; - nm_info (" netmask %s", inet_ntoa (temp_addr)); + if (!device->dhclient_pid) + return; - nm_ip4_config_set_broadcast (ip4_config, ip4_broadcast[0]); - temp_addr.s_addr = ip4_broadcast[0]; - nm_info (" broadcast %s", inet_ntoa (temp_addr)); + kill (device->dhclient_pid, SIGTERM); - nm_ip4_config_set_gateway (ip4_config, ip4_gateway[0]); - temp_addr.s_addr = ip4_gateway[0]; - nm_info (" gateway %s", inet_ntoa (temp_addr)); + /* Yes, the state has to reach DHC_END. */ + while (blocking && i-- > 0) { + gint child_status; + if (waitpid (device->dhclient_pid, &child_status, WNOHANG) > 0) + break; + g_usleep (G_USEC_PER_SEC / 5); + } - for (i = 0; i < num_ip4_nameservers; i++) - { - nm_ip4_config_add_nameserver (ip4_config, ip4_nameservers[i]); - temp_addr.s_addr = ip4_nameservers[i]; - nm_info (" nameserver %s", inet_ntoa (temp_addr)); + if (i <= 0) { + nm_warning ("%s: dhclient pid %d didn't exit, will kill it.", + device->iface, device->dhclient_pid); + kill (device->dhclient_pid, SIGKILL); } - if (hostname) - { - nm_ip4_config_set_hostname (ip4_config, hostname); - nm_info (" hostname '%s'", hostname); + /* Clean up the pidfile if it got left around */ + pidfile = get_pidfile_for_iface (device->iface); + if (pidfile) { + remove (pidfile); + g_free (pidfile); } + device->dhclient_pid = 0; + device->state = DHC_END; - if (domain_names) - { - char **searches = g_strsplit (domain_names, " ", 0); - char **s; + nm_dhcp_device_watch_cleanup (device); + nm_dhcp_device_timeout_cleanup (device); + g_hash_table_remove_all (device->options); +} - for (s = searches; *s; s++) - { - nm_info (" domain name '%s'", *s); - nm_ip4_config_add_domain (ip4_config, *s); - } - g_strfreev (searches); - } - if (nis_domain) - { - nm_ip4_config_set_nis_domain (ip4_config, nis_domain); - nm_info (" nis domain '%s'", nis_domain); - } +/* + * nm_dhcp_manager_cancel_transaction + * + * Stop any in-progress DHCP transaction on a particular device. + * + */ +void +nm_dhcp_manager_cancel_transaction (NMDHCPManager *manager, + const char *iface, + gboolean blocking) +{ + NMDHCPDevice *device; + NMDHCPManagerPrivate *priv; - for (i = 0; i < num_ip4_nis_servers; i++) - { - nm_ip4_config_add_nis_server (ip4_config, ip4_nis_servers[i]); - temp_addr.s_addr = ip4_nis_servers[i]; - nm_info (" nis server %s", inet_ntoa (temp_addr)); - } + g_return_if_fail (NM_IS_DHCP_MANAGER (manager)); + g_return_if_fail (iface != NULL); - /* - * Grab the MTU from the backend. If DHCP servers can send recommended MTU's, - * should set that here if the backend returns zero. - */ - nm_ip4_config_set_mtu (ip4_config, nm_system_get_mtu (dev)); + priv = NM_DHCP_MANAGER_GET_PRIVATE (manager); -out: - g_free (hostname); - g_free (domain_names); - g_free (nis_domain); + device = (NMDHCPDevice *) g_hash_table_lookup (priv->devices, iface); - g_free (ip4_address); - g_free (ip4_netmask); - g_free (ip4_broadcast); - g_free (ip4_gateway); - g_free (ip4_nameservers); - g_free (ip4_nis_servers); + if (!device || !device->dhclient_pid) + return; - return ip4_config; + nm_dhcp_manager_cancel_transaction_real (device, blocking); } -static inline const char * state_to_string (guint state) + +static gboolean +handle_request_cancel (gpointer user_data) { - switch (state) - { - case DHCDBD_PREINIT: - return "starting"; - case DHCDBD_BOUND: - return "bound"; - case DHCDBD_RENEW: - return "renew"; - case DHCDBD_REBOOT: - return "reboot"; - case DHCDBD_REBIND: - return "rebind"; - case DHCDBD_TIMEOUT: - return "timeout"; - case DHCDBD_FAIL: - return "fail"; - case DHCDBD_START: - return "successfully started"; - case DHCDBD_ABEND: - return "abnormal exit"; - case DHCDBD_END: - return "normal exit"; - default: - return "unknown"; - } + NMDHCPDevice *device = (NMDHCPDevice *) user_data; + +fprintf (stderr, "request_handle_cancel started...\n"); + nm_dhcp_manager_cancel_transaction_real (device, TRUE); +fprintf (stderr, "request_handle_cancel done.\n"); +fprintf (stderr, "%s: calling cancel_cleanup\n", __func__); + nm_dhcp_device_cancel_cleanup (device); + return FALSE; } + /* - * nm_dhcp_manager_process_signal + * nm_dhcp_manager_request_cancel_transaction * - * Possibly process a signal from the bus, if it comes from the currently - * active DHCP daemon, if any. Return TRUE if processed, FALSE if not. + * Request that any in-progress transaction be canceled. * */ -gboolean nm_dhcp_manager_process_signal (NMDHCPManager *manager, DBusMessage *message) +void +nm_dhcp_manager_request_cancel_transaction (NMDHCPManager *manager, + const char *iface, + gboolean blocking) { - const char * object_path; - const char * member; - const char * interface; - gboolean handled = FALSE; - NMDevice * dev; - NMActRequest * req = NULL; + NMDHCPDevice *device; + NMDHCPManagerPrivate *priv; - g_return_val_if_fail (manager != NULL, FALSE); - g_return_val_if_fail (message != NULL, FALSE); + g_return_if_fail (NM_IS_DHCP_MANAGER (manager)); + g_return_if_fail (iface != NULL); - if (!(object_path = dbus_message_get_path (message))) - return FALSE; - if (!(member = dbus_message_get_member (message))) - return FALSE; - if (!(interface = dbus_message_get_interface (message))) - return FALSE; - /* Ignore non-DHCP related messages */ - if (strncmp (interface, DHCP_SERVICE_NAME, manager->dhcp_sn_len)) - return FALSE; + priv = NM_DHCP_MANAGER_GET_PRIVATE (manager); -#if 0 - { - const char *signature = dbus_message_get_signature (message); - nm_info ("nm_dhcp_manager_process_signal(): got signal op='%s' member='%s' interface='%s' sig='%s'", object_path, member, interface, signature); - } -#endif + device = (NMDHCPDevice *) g_hash_table_lookup (priv->devices, iface); - dev = nm_get_device_by_iface (manager->data, member); - if (dev && (req = nm_device_get_act_request (dev))) - { - const char *iface = nm_device_get_iface (dev); - - if (dbus_message_is_signal (message, DHCP_SERVICE_NAME".state", iface)) - { - guint8 state; - - if (dbus_message_get_args (message, NULL, DBUS_TYPE_BYTE, &state, DBUS_TYPE_INVALID)) - { - const char *desc = state_to_string (state); - - nm_info ("DHCP daemon state is now %d (%s) for interface %s", state, desc, iface); - switch (state) - { - case DHCDBD_BOUND: /* lease obtained */ - case DHCDBD_RENEW: /* lease renewed */ - case DHCDBD_REBOOT: /* have valid lease, but now obtained a different one */ - case DHCDBD_REBIND: /* new, different lease */ - if (nm_act_request_get_stage (req) == NM_ACT_STAGE_IP_CONFIG_START) - { - nm_device_activate_schedule_stage4_ip_config_get (req); - remove_timeout (manager, req); - } - break; - - case DHCDBD_TIMEOUT: /* timed out contacting DHCP server */ - if (nm_act_request_get_stage (req) == NM_ACT_STAGE_IP_CONFIG_START) - { - nm_device_activate_schedule_stage4_ip_config_timeout (req); - remove_timeout (manager, req); - } - break; - - case DHCDBD_FAIL: /* all attempts to contact server timed out, sleeping */ - case DHCDBD_ABEND: /* dhclient exited abnormally */ -// case DHCDBD_END: /* dhclient exited normally */ - if (nm_act_request_get_stage (req) == NM_ACT_STAGE_IP_CONFIG_START) - { - nm_policy_schedule_activation_failed (req); - remove_timeout (manager, req); - } - break; - - default: - break; - } - nm_act_request_set_dhcp_state (req, state); - } - - handled = TRUE; - } + if (!device || !device->dhclient_pid) + return; + + if (!device->cancel_source) { + device->cancel_source = g_idle_source_new (); +fprintf (stderr, "%s: created cancel source %p\n", __func__, device->cancel_source); + g_source_set_priority (device->cancel_source, G_PRIORITY_HIGH_IDLE); + g_source_set_callback (device->cancel_source, + handle_request_cancel, + device, + NULL); + g_source_attach (device->cancel_source, + priv->data->main_context); } - return handled; + while (blocking && device->cancel_source) + g_usleep (G_USEC_PER_SEC / 10); } /* - * nm_dhcp_manager_process_name_owner_changed + * nm_dhcp_manager_get_ip4_config * - * Respond to "service created"/"service deleted" signals from dbus for the active DHCP daemon. + * Get IP4 configuration values from the DHCP daemon * */ -gboolean nm_dhcp_manager_process_name_owner_changed (NMDHCPManager *manager, const char *changed_service_name, const char *old_owner, const char *new_owner) +NMIP4Config * +nm_dhcp_manager_get_ip4_config (NMDHCPManager *manager, + const char *iface) { - gboolean handled = FALSE; - gboolean old_owner_good = (old_owner && strlen (old_owner)); - gboolean new_owner_good = (new_owner && strlen (new_owner)); + NMDHCPManagerPrivate *priv; + NMDHCPDevice *device; + NMIP4Config * ip4_config = NULL; + guint32 ip4_address = 0; + guint32 ip4_netmask = 0; + guint32 ip4_broadcast = 0; + guint32 ip4_gateway = 0; + char * hostname = NULL; + char * domain_names = NULL; + char * nameservers = NULL; + char * nis_domain = NULL; + char * nis_servers = NULL; + char * ip = NULL; //this is a general string that is used as a temporary place for ip(s) + + g_return_val_if_fail (NM_IS_DHCP_MANAGER (manager), NULL); + g_return_val_if_fail (iface != NULL, NULL); + + priv = NM_DHCP_MANAGER_GET_PRIVATE (manager); + + device = (NMDHCPDevice *) g_hash_table_lookup (priv->devices, iface); + if (!device) { + nm_warning ("Device '%s' transaction not started.", iface); + return NULL; + } - g_return_val_if_fail (manager != NULL, FALSE); - g_return_val_if_fail (changed_service_name != NULL, FALSE); + if (!state_is_bound (device->state)) { + nm_warning ("%s: dhclient didn't bind to a lease.", device->iface); + return NULL; + } - /* Can't handle the signal if its not from the DHCP service */ - if (strcmp (DHCP_SERVICE_NAME, changed_service_name) != 0) - return FALSE; + if (!state_is_bound (device->state)) { + nm_warning ("Tried to get IP4 Config for a device when DHCP " + "wasn't in a BOUND state!"); + return NULL; + } - if (!old_owner_good && new_owner_good) - { - char *match = get_dhcp_match_string (new_owner); + ip = g_hash_table_lookup (device->options, "new_ip_address"); + if (ip != NULL) { + ip4_address = inet_addr (ip); + nm_info(" address %s", ip); + } + else { + return NULL; + } - /* DHCP service got created */ - dbus_bus_add_match (manager->data->dbus_connection, match, NULL); - g_free (match); + ip = g_hash_table_lookup (device->options, "new_subnet_mask"); + if (ip != NULL) { + ip4_netmask = inet_addr (ip); + nm_info(" netmask %s", ip); + } + else { + return NULL; + } - manager->running = TRUE; - handled = TRUE; + ip = g_hash_table_lookup (device->options, "new_broadcast_address"); + if (ip != NULL) { + ip4_broadcast = inet_addr (ip); + nm_info(" broadcast %s", ip); } - else if (old_owner_good && !new_owner_good) - { - char *match = get_dhcp_match_string (old_owner); + else { + return NULL; + } + + ip = g_hash_table_lookup (device->options, "new_routers"); + if (ip != NULL) { + ip4_gateway = inet_addr (ip); + } + else { /* If DHCP doesn't have a 'routers', just use the DHCP server's address as our gateway for now */ + ip = g_hash_table_lookup (device->options, "new_dhcp_server_identifier"); + if (ip != NULL) + ip4_gateway = inet_addr (ip); + else + return NULL; + } + + nm_info(" gateway %s", ip); - /* DHCP service went away */ - dbus_bus_remove_match (manager->data->dbus_connection, match, NULL); - g_free (match); + ip4_config = nm_ip4_config_new (); + nm_ip4_config_set_address (ip4_config, ip4_address); + nm_ip4_config_set_netmask (ip4_config, ip4_netmask); + nm_ip4_config_set_broadcast (ip4_config, ip4_broadcast); + nm_ip4_config_set_gateway (ip4_config, ip4_gateway); + + hostname = g_hash_table_lookup (device->options, "new_host_name"); + nameservers = g_hash_table_lookup (device->options, "new_domain_name_servers"); + domain_names = g_hash_table_lookup (device->options, "new_domain_name"); + nis_domain = g_hash_table_lookup (device->options, "new_nis_domain"); + nis_servers = g_hash_table_lookup (device->options, "new_nis_servers"); + + + if (nameservers) { + char **searches = g_strsplit (nameservers, " ", 0); + char **s; + int ip4_nameserver; - manager->running = FALSE; - handled = TRUE; + for (s = searches; *s; s++) { + ip4_nameserver = inet_addr (*s); + nm_ip4_config_add_nameserver (ip4_config, ip4_nameserver); + nm_info (" nameserver '%s'", *s); + } + g_strfreev (searches); } - return handled; -} + if (hostname) { + nm_ip4_config_set_hostname (ip4_config, hostname); + nm_info (" hostname '%s'", hostname); + } + if (domain_names) { + char **searches = g_strsplit (domain_names, " ", 0); + char **s; + for (s = searches; *s; s++) { + nm_info (" domain name '%s'", *s); + nm_ip4_config_add_domain (ip4_config, *s); + } + g_strfreev (searches); + } + + if (nis_domain) { + nm_ip4_config_set_nis_domain (ip4_config, nis_domain); + nm_info (" nis domain '%s'", nis_domain); + } + + if (nis_servers) { + char **searches = g_strsplit (nis_servers, " ", 0); + char **s; + int ip4_nis_server; + + for (s = searches; *s; s++) { + ip4_nis_server = inet_addr (*s); + nm_ip4_config_add_nis_server (ip4_config, ip4_nis_server); + nm_info (" nis server '%s'", *s); + } + g_strfreev (searches); + } + + /* + * FIXME: + * Grab the MTU from the backend. If DHCP servers can send recommended + * MTU's, should set that here. + */ + + return ip4_config; +} diff --git a/src/dhcp-manager/nm-dhcp-manager.h b/src/dhcp-manager/nm-dhcp-manager.h index 2746713bed..1c002bae2d 100644 --- a/src/dhcp-manager/nm-dhcp-manager.h +++ b/src/dhcp-manager/nm-dhcp-manager.h @@ -21,47 +21,72 @@ #ifndef NM_DHCP_MANAGER_H #define NM_DHCP_MANAGER_H -#include "NetworkManagerMain.h" -#include "nm-device.h" +#include <glib/gtypes.h> +#include <glib-object.h> +#include "nm-ip4-config.h" -/* - * FIXME: These should go in a header shared by NetworkManager and dhcdbd, - * but right now NetworkManager and dhcdbd do not share any header. The - * following is copied (and cleaned up) from dhcdbd.h. - */ -enum dhcdbd_state -{ - DHCDBD_NBI=0, /* no broadcast interfaces found */ - DHCDBD_PREINIT, /* configuration started */ - DHCDBD_BOUND, /* lease obtained */ - DHCDBD_RENEW, /* lease renewed */ - DHCDBD_REBOOT, /* have valid lease, but now obtained a different one */ - DHCDBD_REBIND, /* new, different lease */ - DHCDBD_STOP, /* remove old lease */ - DHCDBD_MEDIUM, /* media selection begun */ - DHCDBD_TIMEOUT, /* timed out contacting DHCP server */ - DHCDBD_FAIL, /* all attempts to contact server timed out, sleeping */ - DHCDBD_EXPIRE, /* lease has expired, renewing */ - DHCDBD_RELEASE, /* releasing lease */ - DHCDBD_START, /* sent when dhclient started OK */ - DHCDBD_ABEND, /* dhclient exited abnormally */ - DHCDBD_END, /* dhclient exited normally */ - DHCDBD_END_OPTIONS, /* last option in subscription sent */ -}; +#define NM_TYPE_DHCP_MANAGER (nm_dhcp_manager_get_type ()) +#define NM_DHCP_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DHCP_MANAGER, NMDHCPManager)) +#define NM_DHCP_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DHCP_MANAGER, NMDHCPManagerClass)) +#define NM_IS_DHCP_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DHCP_MANAGER)) +#define NM_IS_DHCP_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_DHCP_MANAGER)) +#define NM_DHCP_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DHCP_MANAGER, NMDHCPManagerClass)) + +#define NM_DHCP_MANAGER_PID_DIR "/var/run" +#define NM_DHCP_MANAGER_PID_FILENAME "dhclient" +#define NM_DHCP_MANAGER_PID_FILE_EXT "pid" + +#define DHCP_CALLOUT_INTERFACE "org.freedesktop.nm_dhcp_client" + +typedef enum { + DHC_NBI=0, /* no broadcast interfaces found */ + DHC_PREINIT, /* configuration started */ + DHC_BOUND, /* lease obtained */ + DHC_RENEW, /* lease renewed */ + DHC_REBOOT, /* have valid lease, but now obtained a different one */ + DHC_REBIND, /* new, different lease */ + DHC_STOP, /* remove old lease */ + DHC_MEDIUM, /* media selection begun */ + DHC_TIMEOUT, /* timed out contacting DHCP server */ + DHC_FAIL, /* all attempts to contact server timed out, sleeping */ + DHC_EXPIRE, /* lease has expired, renewing */ + DHC_RELEASE, /* releasing lease */ + DHC_START, /* sent when dhclient started OK */ + DHC_ABEND, /* dhclient exited abnormally */ + DHC_END, /* dhclient exited normally */ + DHC_END_OPTIONS, /* last option in subscription sent */ +} NMDHCPState; + +typedef struct { + GObject parent; +} NMDHCPManager; + +typedef struct { + GObjectClass parent; -char * get_dhcp_match_string (const char *owner); + /* Signals */ + void (*state_changed) (NMDHCPManager *manager, char *iface, NMDHCPState state); + void (*timeout) (NMDHCPManager *manager, char *iface); +} NMDHCPManagerClass; -NMDHCPManager * nm_dhcp_manager_new (NMData *data); -void nm_dhcp_manager_dispose (NMDHCPManager *manager); +struct NMData; -gboolean nm_dhcp_manager_begin_transaction (NMDHCPManager *manager, NMActRequest *req); -void nm_dhcp_manager_cancel_transaction (NMDHCPManager *manager, NMActRequest *req); +GType nm_dhcp_manager_get_type (void); -NMIP4Config * nm_dhcp_manager_get_ip4_config (NMDHCPManager *manager, NMActRequest *req); +NMDHCPManager *nm_dhcp_manager_get (struct NMData * data); +gboolean nm_dhcp_manager_begin_transaction (NMDHCPManager *manager, + const char *iface, + guint32 timeout); +void nm_dhcp_manager_cancel_transaction (NMDHCPManager *manager, + const char *iface, + gboolean blocking); +NMIP4Config * nm_dhcp_manager_get_ip4_config (NMDHCPManager *manager, const char *iface); +NMDHCPState nm_dhcp_manager_get_state_for_device (NMDHCPManager *manager, const char *iface); -gboolean nm_dhcp_manager_process_signal (NMDHCPManager *manager, DBusMessage *message); -gboolean nm_dhcp_manager_process_name_owner_changed (NMDHCPManager *manager, const char *changed_service_name, const char *old_owner, const char *new_owner); +gboolean nm_dhcp_manager_process_signal (NMDHCPManager *manager, DBusMessage *message); -guint32 nm_dhcp_manager_get_state_for_device (NMDHCPManager *manager, NMDevice *dev); +void nm_dhcp_manager_request_cancel_transaction (NMDHCPManager *manager, + const char *iface, + gboolean blocking); -#endif +#endif /* NM_DHCP_MANAGER_H */ diff --git a/src/dhcp-manager/nm-dhcp-marshal-main.c b/src/dhcp-manager/nm-dhcp-marshal-main.c new file mode 100644 index 0000000000..eba68d7ea1 --- /dev/null +++ b/src/dhcp-manager/nm-dhcp-marshal-main.c @@ -0,0 +1,3 @@ +#include "nm-dhcp-marshal.h" +#include "nm-dhcp-marshal.c" + diff --git a/src/dhcp-manager/nm-dhcp-marshal.list b/src/dhcp-manager/nm-dhcp-marshal.list new file mode 100644 index 0000000000..8147399d30 --- /dev/null +++ b/src/dhcp-manager/nm-dhcp-marshal.list @@ -0,0 +1,2 @@ +VOID:STRING,UCHAR + diff --git a/src/nm-activation-request.c b/src/nm-activation-request.c index 4f25b07c29..e563d2b3f4 100644 --- a/src/nm-activation-request.c +++ b/src/nm-activation-request.c @@ -43,8 +43,6 @@ struct NMActRequest NMActStage stage; DBusPendingCall * user_key_pcall; - guint32 dhcp_state; - guint dhcp_timeout; /* the timeout source itself */ guint dhcp_timeout_wait; /* in seconds */ }; @@ -71,7 +69,6 @@ NMActRequest * nm_act_request_new (NMData *data, NMDevice *dev, NMAccessPoint *a req->ap = ap; req->user_requested = user_requested; - req->dhcp_state = nm_dhcp_manager_get_state_for_device (data->dhcp_manager, dev); return req; } @@ -98,12 +95,6 @@ void nm_act_request_unref (NMActRequest *req) if (req->ip4_config) nm_ip4_config_unref (req->ip4_config); - if (req->dhcp_timeout > 0) - { - GSource * source = g_main_context_find_source_by_id (req->data->main_context, req->dhcp_timeout); - g_source_destroy (source); - } - memset (req, 0, sizeof (NMActRequest)); g_free (req); } @@ -216,34 +207,6 @@ void nm_act_request_set_user_key_pending_call (NMActRequest *req, DBusPendingCal req->user_key_pcall = pcall; } -guint8 nm_act_request_get_dhcp_state (NMActRequest *req) -{ - g_return_val_if_fail (req != NULL, 0); - - return req->dhcp_state; -} - -void nm_act_request_set_dhcp_state (NMActRequest *req, guint8 dhcp_state) -{ - g_return_if_fail (req != NULL); - - req->dhcp_state = dhcp_state; -} - -guint nm_act_request_get_dhcp_timeout (NMActRequest *req) -{ - g_return_val_if_fail (req != NULL, 0); - - return req->dhcp_timeout; -} - -void nm_act_request_set_dhcp_timeout (NMActRequest *req, guint dhcp_timeout) -{ - g_return_if_fail (req != NULL); - - req->dhcp_timeout = dhcp_timeout; -} - guint nm_act_request_get_dhcp_timeout_wait (NMActRequest *req) { g_return_val_if_fail (req != NULL, 0); diff --git a/src/nm-activation-request.h b/src/nm-activation-request.h index c5579fa432..5f9f747238 100644 --- a/src/nm-activation-request.h +++ b/src/nm-activation-request.h @@ -50,12 +50,6 @@ void nm_act_request_set_stage (NMActRequest *req, NMActStage stage); DBusPendingCall * nm_act_request_get_user_key_pending_call (NMActRequest *req); void nm_act_request_set_user_key_pending_call (NMActRequest *req, DBusPendingCall *pcall); -guint8 nm_act_request_get_dhcp_state (NMActRequest *req); -void nm_act_request_set_dhcp_state (NMActRequest *req, guint8 dhcp_state); - -guint nm_act_request_get_dhcp_timeout (NMActRequest *req); -void nm_act_request_set_dhcp_timeout (NMActRequest *req, guint dhcp_timeout); - guint nm_act_request_get_dhcp_timeout_wait (NMActRequest *req); void nm_act_request_set_dhcp_timeout_wait (NMActRequest *req, guint dhcp_timeout); diff --git a/src/nm-device-802-11-mesh-olpc.c b/src/nm-device-802-11-mesh-olpc.c index 077067888c..6a80b5e6a2 100644 --- a/src/nm-device-802-11-mesh-olpc.c +++ b/src/nm-device-802-11-mesh-olpc.c @@ -38,6 +38,7 @@ #include "nm-activation-request.h" #include "NetworkManagerSystem.h" #include "NetworkManagerPolicy.h" +#include "nm-dhcp-manager.h" #define MESH_SSID "olpc-mesh" #define MPP_DEFAULT_CHANNEL 1 @@ -349,7 +350,8 @@ real_init (NMDevice *dev) g_direct_equal); if ( !self->priv->mpp.activated_ids || !self->priv->mpp.deactivated_ids) { - nm_warning ("%s: couldn't allocate MPP tables."); + nm_warning ("%s: couldn't allocate MPP tables.", + nm_device_get_iface (NM_DEVICE (self))); mpp_clear_hash_tables (self); } @@ -504,7 +506,7 @@ cleanup_ethdev (NMDevice80211MeshOLPC *self) static void connect_to_device_signals (NMDevice80211MeshOLPC *self, NMDevice *dev) { - guint32 act_id, deact_id, act_fail_id; + guint32 act_id, deact_id; g_return_if_fail (self != NULL); g_return_if_fail (dev != NULL); @@ -668,7 +670,7 @@ real_notify_device_removed (NMDevice *dev, /* If we are an MPP and the removed device was the one providing * the primary connection, stop being an MPP. */ - if (removed_dev = self->priv->mpp.primary) + if (removed_dev == self->priv->mpp.primary) mpp_cleanup (self); } @@ -1001,8 +1003,6 @@ is_mpp_active (NMDevice80211MeshOLPC *self) static void mpp_cleanup (NMDevice80211MeshOLPC *self) { - NMIP4Config * config; - if (self->priv->mpp.primary) { NMData * data = nm_device_get_app_data (NM_DEVICE (self)); if (self->priv->mpp.associated) { @@ -1205,7 +1205,6 @@ aipd_watch_cb (GPid pid, gpointer user_data) { NMDevice80211MeshOLPC * self = NM_DEVICE_802_11_MESH_OLPC (user_data); - NMDevice * dev = NM_DEVICE (user_data); g_assert (self); @@ -1319,7 +1318,6 @@ static gboolean aipd_monitor_start (NMDevice80211MeshOLPC *self) { gboolean success = FALSE; - GIOChannel * channel; GMainContext * context; g_return_val_if_fail (self != NULL, FALSE); @@ -1640,7 +1638,7 @@ real_act_stage4_get_ip4_config (NMDevice *dev, NMDevice80211MeshOLPCClass * klass; NMDeviceClass * parent_class; const char * iface = nm_device_get_iface (dev); - NMData * data = nm_device_get_app_data (dev); + NMDHCPManager * dhcp_manager = nm_dhcp_manager_get (NULL); g_return_val_if_fail (config != NULL, NM_ACT_STAGE_RETURN_FAILURE); g_return_val_if_fail (*config == NULL, NM_ACT_STAGE_RETURN_FAILURE); @@ -1662,13 +1660,18 @@ real_act_stage4_get_ip4_config (NMDevice *dev, /* Kill dhclient; we don't need it anymore after MPP discovery here * because we're ignoring the returned lease. */ - nm_dhcp_manager_cancel_transaction (data->dhcp_manager, req); - sleep (1); + nm_dhcp_manager_request_cancel_transaction (dhcp_manager, + nm_device_get_iface (NM_DEVICE (self)), + TRUE); if (ret != NM_ACT_STAGE_RETURN_SUCCESS) goto out; break; case MESH_S4_P2P_MESH: real_config = nm_ip4_config_new (); + if (!real_config) { + nm_warning ("Not enough memory to create ip4 config."); + goto out; + } nm_ip4_config_set_address (real_config, self->priv->aipd.ip4_addr); nm_ip4_config_set_netmask (real_config, (guint32)(ntohl (IPV4LL_NETMASK))); nm_ip4_config_set_broadcast (real_config, (guint32)(ntohl (IPV4LL_BROADCAST))); diff --git a/src/nm-device.c b/src/nm-device.c index 2518910749..3e912229a4 100644 --- a/src/nm-device.c +++ b/src/nm-device.c @@ -77,13 +77,16 @@ struct _NMDevicePrivate /* IP configuration info */ void * system_config_data; /* Distro-specific config data (parsed config file, etc) */ - gboolean use_dhcp; NMIP4Config * ip4_config; /* Config from DHCP, PPP, or system config files */ GMainContext * context; GMainLoop * loop; GThread * worker; gboolean worker_started; + + NMDHCPManager * dhcp_manager; + gulong dhcp_state_sigid; + gulong dhcp_timeout_sigid; }; static gpointer nm_device_worker (gpointer user_data); @@ -214,7 +217,6 @@ nm_device_new (const char *iface, /* Grab IP config data for this device from the system configuration files */ dev->priv->system_config_data = nm_system_device_get_system_config (dev, app_data); - dev->priv->use_dhcp = nm_system_device_get_use_dhcp (dev); /* Allow distributions to flag devices as disabled */ if (nm_system_device_get_disabled (dev)) @@ -271,7 +273,6 @@ nm_device_init (NMDevice * self) self->priv->quit_activation = FALSE; self->priv->system_config_data = NULL; - self->priv->use_dhcp = TRUE; self->priv->ip4_config = NULL; self->priv->context = NULL; @@ -911,22 +912,34 @@ real_act_stage3_ip_config_start (NMDevice *self, nm_device_bring_up (self); /* DHCP devices try DHCP, non-DHCP default to SUCCESS */ - if (nm_device_get_use_dhcp (self)) - { - /* Begin a DHCP transaction on the interface */ - if (!nm_dhcp_manager_begin_transaction (data->dhcp_manager, req)) - { + if (nm_system_device_get_use_dhcp (self)) { + gboolean success; + guint32 timeout; + + nm_device_set_use_dhcp (self, TRUE); + + /* DHCP manager will cancel any transaction already in progress and we do not + want to cancel this activation if we get "down" state from that. */ + g_signal_handler_block (self->priv->dhcp_manager, + self->priv->dhcp_state_sigid); + + timeout = nm_act_request_get_dhcp_timeout_wait (req); + success = nm_dhcp_manager_begin_transaction (self->priv->dhcp_manager, + nm_device_get_iface (self), + timeout); + + g_signal_handler_unblock (self->priv->dhcp_manager, + self->priv->dhcp_state_sigid); + + if (success) { + /* DHCP devices will be notified by the DHCP manager when + * stuff happens. + */ + ret = NM_ACT_STAGE_RETURN_POSTPONE; + } else ret = NM_ACT_STAGE_RETURN_FAILURE; - goto out; - } - - /* DHCP devices will be notified by the DHCP manager when - * stuff happens. - */ - ret = NM_ACT_STAGE_RETURN_POSTPONE; } -out: return ret; } @@ -1053,10 +1066,16 @@ real_act_stage4_get_ip4_config (NMDevice *self, data = nm_act_request_get_data (req); g_assert (data); - if (nm_device_get_use_dhcp (self)) - real_config = nm_dhcp_manager_get_ip4_config (data->dhcp_manager, req); - else + if (nm_device_get_use_dhcp (self)) { + real_config = nm_dhcp_manager_get_ip4_config (NM_DEVICE_GET_PRIVATE (self)->dhcp_manager, + nm_device_get_iface (self)); + + if (real_config && nm_ip4_config_get_mtu (real_config) == 0) + /* If the DHCP server doesn't set the MTU, get it from backend. */ + nm_ip4_config_set_mtu (real_config, nm_system_get_mtu (self)); + } else { real_config = nm_system_device_new_ip4_system_config (self); + } if (real_config) { @@ -1340,9 +1359,6 @@ real_activation_cancel_handler (NMDevice *self, { g_return_if_fail (self != NULL); g_return_if_fail (req != NULL); - - if (nm_act_request_get_stage (req) == NM_ACT_STAGE_IP_CONFIG_START) - nm_dhcp_manager_cancel_transaction (self->priv->app_data->dhcp_manager, req); } /* @@ -1495,12 +1511,19 @@ nm_device_deactivate_quickly (NMDevice *self) else if (nm_device_is_activating (self)) nm_device_activation_cancel (self); + if (nm_device_get_use_dhcp (self)) { + fprintf (stderr, "will cancel DHCP\n"); + nm_dhcp_manager_request_cancel_transaction (NM_DEVICE_GET_PRIVATE (self)->dhcp_manager, + nm_device_get_iface (self), + FALSE); + fprintf (stderr, "... done\n"); + } + /* Tear down an existing activation request, which may not have happened * in nm_device_activation_cancel() above, for various reasons. */ if ((act_request = nm_device_get_act_request (self))) { - nm_dhcp_manager_cancel_transaction (app_data->dhcp_manager, act_request); nm_act_request_unref (act_request); self->priv->act_request = NULL; } @@ -1694,12 +1717,74 @@ nm_device_can_interrupt_activation (NMDevice *self) /* IP Configuration stuff */ +static void +dhcp_state_changed (NMDHCPManager *dhcp_manager, + const char *iface, + NMDHCPState state, + gpointer user_data) +{ + NMDevice * self = NM_DEVICE (user_data); + NMActRequest * req; + + if (strcmp (nm_device_get_iface (self), iface) != 0) + return; + + req = nm_device_get_act_request (self); + g_return_if_fail (req != NULL); + + switch (state) { + case DHC_BOUND: /* lease obtained */ + case DHC_RENEW: /* lease renewed */ + case DHC_REBOOT: /* have valid lease, but now obtained a different one */ + case DHC_REBIND: /* new, different lease */ + if (nm_act_request_get_stage (req) == NM_ACT_STAGE_IP_CONFIG_START) + nm_device_activate_schedule_stage4_ip_config_get (req); + break; + case DHC_TIMEOUT: /* timed out contacting DHCP server */ + if (nm_act_request_get_stage (req) == NM_ACT_STAGE_IP_CONFIG_START) + nm_device_activate_schedule_stage4_ip_config_timeout (req); + break; + case DHC_FAIL: /* all attempts to contact server timed out, sleeping */ + case DHC_ABEND: /* dhclient exited abnormally */ + case DHC_END: /* dhclient exited normally */ + if (nm_act_request_get_stage (req) == NM_ACT_STAGE_IP_CONFIG_START) { + nm_policy_schedule_activation_failed (req); + } else if (nm_device_is_activated (self)) { + if (nm_device_get_use_dhcp (self)) { + /* dhclient quit and therefore can't renew our lease, kill the conneciton */ + nm_policy_schedule_deactivate (self); + } + } + break; + default: + break; + } +} + +static void +dhcp_timeout (NMDHCPManager *dhcp_manager, + const char *iface, + gpointer user_data) +{ + NMDevice * self = NM_DEVICE (user_data); + NMActRequest * req; + + if (strcmp (nm_device_get_iface (self), iface) != 0) + return; + + req = nm_device_get_act_request (self); + g_return_if_fail (req != NULL); + + if (nm_act_request_get_stage (req) == NM_ACT_STAGE_IP_CONFIG_START) + nm_device_activate_schedule_stage4_ip_config_timeout (req); +} + gboolean nm_device_get_use_dhcp (NMDevice *self) { g_return_val_if_fail (self != NULL, FALSE); - return self->priv->use_dhcp; + return self->priv->dhcp_manager ? TRUE : FALSE; } void @@ -1708,7 +1793,30 @@ nm_device_set_use_dhcp (NMDevice *self, { g_return_if_fail (self != NULL); - self->priv->use_dhcp = use_dhcp; + if (use_dhcp) { + if (!self->priv->dhcp_manager) { + self->priv->dhcp_manager = nm_dhcp_manager_get (NULL); + self->priv->dhcp_state_sigid = g_signal_connect (self->priv->dhcp_manager, + "state-changed", + G_CALLBACK (dhcp_state_changed), + self); + self->priv->dhcp_timeout_sigid = g_signal_connect (self->priv->dhcp_manager, + "timeout", + G_CALLBACK (dhcp_timeout), + self); + } + } else if (self->priv->dhcp_manager) { + g_signal_handler_disconnect (self->priv->dhcp_manager, + self->priv->dhcp_state_sigid); + self->priv->dhcp_state_sigid = 0; + + g_signal_handler_disconnect (self->priv->dhcp_manager, + self->priv->dhcp_timeout_sigid); + self->priv->dhcp_timeout_sigid = 0; + + g_object_unref (self->priv->dhcp_manager); + self->priv->dhcp_manager = NULL; + } } |