From 0790d2fc3e44adccfc3495eac8b97a0fade1b5f9 Mon Sep 17 00:00:00 2001 From: Ray Strode Date: Fri, 11 Mar 2005 20:12:57 +0000 Subject: [PATCH] 2005-03-11 Ray Strode Third (unfinished, partially working) cut at porting to dbus 0.30 api and new hal. * info-daemon/NetworkManagerInfoDbus.c: don't free null arrays. * panel-applet/NMWirelessAppletDbus.c: * src/nm-dbus-device.c: * src/nm-dbus-net.c: * src/NetworkManagerDbus.c: more STRING -> OBJECT_PATH fun * src/NetworkManagerDevice.c: * src/NetworkManagerDevice.h: (rename nm_device_get_link_active): rename to nm_device_has_active_link (nm_device_wireless_link_active): rename to nm_device_probe_wireless_link_state (nm_device_wired_link_active): rename to nm_device_probe_wired_link_state. Rewrite to use carrier file since hal doesn't maintain link state anymore. (nm_device_update_link_active): rename to nm_device_update_link_state * src/NetworkManagerPolicy.c (nm_policy_activation_finish): check for NULL MAC address. * src/Makefile.am: * src/NetworkManagerMain.h: * src/NetworkManager.c: * src/nm-netlink-monitor.c: * src/nm-netlink-monitor.h: New class to support monitoring wired ethernet link status, since HAL doesn't export that information anymore. git-svn-id: http://svn-archive.gnome.org/svn/NetworkManager/trunk@495 4912f4e0-d625-0410-9fb7-b9a5a253dbdc --- ChangeLog | 39 +- info-daemon/NetworkManagerInfoDbus.c | 4 +- panel-applet/NMWirelessAppletDbus.c | 20 +- src/Makefile.am | 2 + src/NetworkManager.c | 250 ++++++----- src/NetworkManagerDbus.c | 61 ++- src/NetworkManagerDevice.c | 86 ++-- src/NetworkManagerDevice.h | 4 +- src/NetworkManagerMain.h | 5 +- src/NetworkManagerPolicy.c | 6 +- src/autoip.c | 2 +- src/nm-dbus-device.c | 16 +- src/nm-dbus-net.c | 11 +- src/nm-netlink-monitor.c | 635 +++++++++++++++++++++++++++ src/nm-netlink-monitor.h | 89 ++++ 15 files changed, 1041 insertions(+), 189 deletions(-) create mode 100644 src/nm-netlink-monitor.c create mode 100644 src/nm-netlink-monitor.h diff --git a/ChangeLog b/ChangeLog index de75bd5fa..d57b9ec4b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,9 +1,46 @@ +2005-03-11 Ray Strode + + Third (unfinished, partially working) cut at porting to + dbus 0.30 api and new hal. + + * info-daemon/NetworkManagerInfoDbus.c: + don't free null arrays. + + * panel-applet/NMWirelessAppletDbus.c: + * src/nm-dbus-device.c: + * src/nm-dbus-net.c: + * src/NetworkManagerDbus.c: more + STRING -> OBJECT_PATH fun + * src/NetworkManagerDevice.c: + * src/NetworkManagerDevice.h: + (rename nm_device_get_link_active): rename to + nm_device_has_active_link + (nm_device_wireless_link_active): rename to + nm_device_probe_wireless_link_state + (nm_device_wired_link_active): rename to + nm_device_probe_wired_link_state. Rewrite to + use carrier file since hal doesn't maintain + link state anymore. + (nm_device_update_link_active): rename to + nm_device_update_link_state + * src/NetworkManagerPolicy.c + (nm_policy_activation_finish): check for NULL + MAC address. + + * src/Makefile.am: + * src/NetworkManagerMain.h: + * src/NetworkManager.c: + * src/nm-netlink-monitor.c: + * src/nm-netlink-monitor.h: New class to support + monitoring wired ethernet link status, since HAL + doesn't export that information anymore. + 2005-03-09 Ray Strode Second (unfinished, unworking) cut at porting to dbus 0.30 api. - * dispatcher-daemon/NetworkManagerDispatcher.c + * dispatcher-daemon/NetworkManagerDispatcher.c: * info-daemon/NetworkManagerInfoDbus.c: * panel-applet/NMWirelessAppletDbus.c: * src/NetworkManagerDbusUtils.c: diff --git a/info-daemon/NetworkManagerInfoDbus.c b/info-daemon/NetworkManagerInfoDbus.c index e47ceb59b..031221aa7 100644 --- a/info-daemon/NetworkManagerInfoDbus.c +++ b/info-daemon/NetworkManagerInfoDbus.c @@ -482,7 +482,9 @@ static DBusMessage *nmi_dbus_get_network_properties (NMIAppInfo *info, DBusMessa dbus_message_iter_close_container (&iter, &array_iter); } - gconf_value_free (ap_addrs_value); + + if (ap_addrs_value != NULL) + gconf_value_free (ap_addrs_value); g_free (essid); g_free (key); diff --git a/panel-applet/NMWirelessAppletDbus.c b/panel-applet/NMWirelessAppletDbus.c index 5c37430ad..d61c2e830 100644 --- a/panel-applet/NMWirelessAppletDbus.c +++ b/panel-applet/NMWirelessAppletDbus.c @@ -72,7 +72,7 @@ static int nmwa_dbus_call_nm_method (DBusConnection *con, const char *path, cons g_return_val_if_fail (*arg == NULL, RETURN_FAILURE); if ((arg_type == NM_DBUS_TYPE_STRING_ARRAY) || - (arg_type == NM_DBUS_TYPE_OBJECT_PATH_ARRAY)) + (arg_type == NM_DBUS_TYPE_OBJECT_PATH_ARRAY)) { g_return_val_if_fail (item_count != NULL, RETURN_FAILURE); *item_count = 0; @@ -102,7 +102,7 @@ static int nmwa_dbus_call_nm_method (DBusConnection *con, const char *path, cons ret = RETURN_SUCCESS; if ((ret != RETURN_SUCCESS) && (ret != RETURN_NO_NM)) - fprintf (stderr, "nmwa_dbus_call_nm_method(): %s raised:\n %s\n\n", error.name, error.message); + fprintf (stderr, "nmwa_dbus_call_nm_method(): %s raised on method '%s':\n %s\n\n", error.name, method, error.message); dbus_error_free (&error); return (ret); @@ -155,6 +155,7 @@ static int nmwa_dbus_call_nm_method (DBusConnection *con, const char *path, cons switch (arg_type) { + /* case DBUS_TYPE_OBJECT_PATH: *((char **)(arg)) = nm_dbus_unescape_object_path (dbus_string); break; @@ -170,9 +171,12 @@ static int nmwa_dbus_call_nm_method (DBusConnection *con, const char *path, cons *item_count = num_items; break; } + */ + case DBUS_TYPE_OBJECT_PATH: case DBUS_TYPE_STRING: *((char **)(arg)) = g_strdup (dbus_string); break; + case NM_DBUS_TYPE_OBJECT_PATH_ARRAY: case NM_DBUS_TYPE_STRING_ARRAY: *((char ***)(arg)) = g_strdupv (dbus_array); *item_count = num_items; @@ -234,7 +238,7 @@ static char * nmwa_dbus_get_active_network (NMWirelessApplet *applet, char *dev_ { char *network = NULL; - switch (nmwa_dbus_call_nm_method (applet->connection, dev_path, "getActiveNetwork", DBUS_TYPE_STRING, (void **)(&network), NULL)) + switch (nmwa_dbus_call_nm_method (applet->connection, dev_path, "getActiveNetwork", DBUS_TYPE_OBJECT_PATH, (void **)(&network), NULL)) { case (RETURN_NO_NM): applet->applet_state = APPLET_STATE_NO_NM; @@ -685,7 +689,7 @@ void nmwa_dbus_set_device (DBusConnection *connection, const NetworkDevice *dev, if (passphrase == NULL) passphrase = ""; - dbus_message_append_args (message, DBUS_TYPE_STRING, &dev->nm_device, + dbus_message_append_args (message, DBUS_TYPE_OBJECT_PATH, &dev->nm_device, DBUS_TYPE_STRING, &network->essid, DBUS_TYPE_STRING, &passphrase, DBUS_TYPE_INT32, &key_type, @@ -1261,8 +1265,8 @@ static void nmwa_dbus_device_update_one_network (NMWirelessApplet *applet, DBusM dbus_error_init (&error); /* Try first time with strength, which is only passed for NETWORK_STATUS_STRENGTH_CHANGED */ if (!dbus_message_get_args (message, &error, - DBUS_TYPE_STRING, &dev_path, - DBUS_TYPE_STRING, &net_path, + DBUS_TYPE_OBJECT_PATH, &dev_path, + DBUS_TYPE_OBJECT_PATH, &net_path, DBUS_TYPE_UINT32, &status, DBUS_TYPE_INT32, &strength, DBUS_TYPE_INVALID)) @@ -1273,8 +1277,8 @@ static void nmwa_dbus_device_update_one_network (NMWirelessApplet *applet, DBusM /* Try without strength */ if (!dbus_message_get_args (message, &error, - DBUS_TYPE_STRING, &dev_path, - DBUS_TYPE_STRING, &net_path, + DBUS_TYPE_OBJECT_PATH, &dev_path, + DBUS_TYPE_OBJECT_PATH, &net_path, DBUS_TYPE_UINT32, &status, DBUS_TYPE_INVALID)) { diff --git a/src/Makefile.am b/src/Makefile.am index df0562945..5a3b5cc45 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -38,6 +38,8 @@ NetworkManager_SOURCES = \ NetworkManagerWireless.h \ NetworkManagerSystem.c \ NetworkManagerSystem.h \ + nm-netlink-monitor.c \ + nm-netlink-monitor.h \ autoip.c NetworkManager_CPPFLAGS = \ diff --git a/src/NetworkManager.c b/src/NetworkManager.c index 4a6aafdcd..a126065aa 100644 --- a/src/NetworkManager.c +++ b/src/NetworkManager.c @@ -43,7 +43,9 @@ #include "NetworkManagerAPList.h" #include "NetworkManagerSystem.h" #include "nm-named-manager.h" +#include "nm-netlink-monitor.h" +#define NM_WIRELESS_LINK_STATE_POLL_INTERVAL (5 * 1000) /* * Globals @@ -54,13 +56,9 @@ static gboolean sigterm_pipe_handler (GIOChannel *src, GIOCondition condition, g static void sigterm_handler (int signum); static void nm_data_free (NMData *data); - /* * nm_get_device_interface_from_hal * - * Queries HAL for the "net.interface" property of a device and returns - * it if successful. - * */ static char *nm_get_device_interface_from_hal (LibHalContext *ctx, const char *udi) { @@ -95,7 +93,7 @@ static char *nm_get_device_interface_from_hal (LibHalContext *ctx, const char *u * NULL on failure */ NMDevice * nm_create_device_and_add_to_list (NMData *data, const char *udi, const char *iface, - gboolean test_device, NMDeviceType test_device_type) + gboolean test_device, NMDeviceType test_device_type) { NMDevice *dev = NULL; @@ -110,7 +108,7 @@ NMDevice * nm_create_device_and_add_to_list (NMData *data, const char *udi, cons if (!data->enable_test_devices && test_device) { syslog (LOG_ERR, "nm_create_device_and_add_to_list(): attempt to create a test device," - " but test devices were not enabled on the command line. Will not create the device.\n"); + " but test devices were not enabled on the command line. Will not create the device.\n"); return (NULL); } @@ -131,6 +129,8 @@ NMDevice * nm_create_device_and_add_to_list (NMData *data, const char *udi, cons data->dev_list = g_slist_append (data->dev_list, dev); nm_device_deactivate (dev, TRUE); + nm_device_update_link_state (dev); + nm_unlock_mutex (data->dev_list_mutex, __FUNCTION__); nm_policy_schedule_state_update (data); @@ -139,11 +139,11 @@ NMDevice * nm_create_device_and_add_to_list (NMData *data, const char *udi, cons else { /* If we couldn't add the device to our list, free its data. */ - syslog( LOG_ERR, "nm_create_device_and_add_to_list() could not acquire device list mutex." ); + syslog ( LOG_ERR, "nm_create_device_and_add_to_list() could not acquire device list mutex." ); nm_device_unref (dev); dev = NULL; } - } else syslog( LOG_ERR, "nm_create_device_and_add_to_list() could not allocate device data." ); + } else syslog ( LOG_ERR, "nm_create_device_and_add_to_list() could not allocate device data." ); return (dev); } @@ -194,7 +194,7 @@ void nm_remove_device_from_list (NMData *data, const char *udi) } } nm_unlock_mutex (data->dev_list_mutex, __FUNCTION__); - } else syslog( LOG_ERR, "nm_remove_device_from_list() could not acquire device list mutex." ); + } else syslog ( LOG_ERR, "nm_remove_device_from_list() could not acquire device list mutex." ); } /* Hal doesn't really give us any way to pass a GMainContext to our @@ -212,7 +212,6 @@ static void nm_hal_mainloop_integration (LibHalContext *ctx, DBusConnection * db dbus_connection_setup_with_g_main (dbus_connection, main_context); } - /* * nm_hal_device_added * @@ -224,7 +223,7 @@ static void nm_hal_device_added (LibHalContext *ctx, const char *udi) g_return_if_fail (data != NULL); - syslog( LOG_DEBUG, "New device added (hal udi is '%s').", udi ); + syslog ( LOG_DEBUG, "New device added (hal udi is '%s').", udi ); /* Sometimes the device's properties (like net.interface) are not set up yet, * so this call will fail, and it will actually be added when hal sets the device's @@ -248,7 +247,7 @@ static void nm_hal_device_removed (LibHalContext *ctx, const char *udi) g_return_if_fail (data != NULL); - syslog( LOG_DEBUG, "Device removed (hal udi is '%s').", udi ); + syslog ( LOG_DEBUG, "Device removed (hal udi is '%s').", udi ); nm_remove_device_from_list (data, udi); } @@ -264,7 +263,7 @@ static void nm_hal_device_new_capability (LibHalContext *ctx, const char *udi, c g_return_if_fail (data != NULL); - /*syslog( LOG_DEBUG, "nm_hal_device_new_capability() called with udi = %s, capability = %s", udi, capability );*/ + /*syslog ( LOG_DEBUG, "nm_hal_device_new_capability() called with udi = %s, capability = %s", udi, capability );*/ if (capability && ((strcmp (capability, "net.80203") == 0) || (strcmp (capability, "net.80211") == 0))) { @@ -285,67 +284,9 @@ static void nm_hal_device_new_capability (LibHalContext *ctx, const char *udi, c */ static void nm_hal_device_lost_capability (LibHalContext *ctx, const char *udi, const char *capability) { -/* syslog( LOG_DEBUG, "nm_hal_device_lost_capability() called with udi = %s, capability = %s", udi, capability );*/ +/* syslog ( LOG_DEBUG, "nm_hal_device_lost_capability() called with udi = %s, capability = %s", udi, capability );*/ } - -/* - * nm_hal_device_property_modified - * - */ -static void nm_hal_device_property_modified (LibHalContext *ctx, const char *udi, const char *key, dbus_bool_t is_removed, dbus_bool_t is_added) -{ - NMData *data = (NMData *)libhal_ctx_get_user_data (ctx); - gboolean link = FALSE; - - g_return_if_fail (data != NULL); - g_return_if_fail (udi != NULL); - g_return_if_fail (key != NULL); - - /*syslog (LOG_DEBUG, "nm_hal_device_property_modified() called with udi = %s, key = %s, is_removed = %d, is_added = %d", udi, key, is_removed, is_added);*/ - - /* Only accept wired ethernet link changes for now */ - if (is_removed || (strcmp (key, "net.80203.link") != 0)) - return; - - if (!libhal_device_property_exists (ctx, udi, "net.80203.link", NULL)) - return; - - link = libhal_device_get_property_bool (ctx, udi, "net.80203.link", NULL); - - /* Attempt to acquire mutex for device link updating. If acquire fails ignore the event. */ - if (nm_try_acquire_mutex (data->dev_list_mutex, __FUNCTION__)) - { - NMDevice *dev = NULL; - if ((dev = nm_get_device_by_udi (data, udi)) && nm_device_is_wired (dev)) - { - syslog (LOG_DEBUG, "HAL signaled link state change for device %s.", nm_device_get_iface (dev)); - nm_device_update_link_active (dev); - - /* If the currently active device is locked and wireless, and the wired - * device we just received this property change event for now has a link - * state of TRUE, we want to clear the active device lock so that we switch - * from wireless to wired on the next state update. - * - * This happens when the user has explicitly chosen a wireless network at - * some point, and then comes back and plugs the wired cable in. Due to the - * active device lock we wouldn't switch back to wired automatically, but - * this fixes that behavior. - */ - if ( nm_device_get_link_active (dev) - && data->active_device - && data->active_device_locked - && nm_device_is_wireless (data->active_device)) - { - data->active_device_locked = FALSE; - nm_policy_schedule_state_update (data); - } - } - nm_unlock_mutex (data->dev_list_mutex, __FUNCTION__); - } else syslog( LOG_ERR, "nm_hal_device_property_modified() could not acquire device list mutex." ); -} - - /* * nm_add_initial_devices * @@ -424,16 +365,14 @@ void nm_schedule_status_signal_broadcast (NMData *data) /* - * nm_link_state_monitor + * nm_poll_and_update_wireless_link_state * - * Called every 2s to poll cards and determine if they have a link + * Called every 2s to poll wireless cards and determine if they have a link * or not. * */ -gboolean nm_link_state_monitor (gpointer user_data) +gboolean nm_poll_and_update_wireless_link_state (NMData *data) { - NMData *data = (NMData *)user_data; - g_return_val_if_fail (data != NULL, TRUE); /* Attempt to acquire mutex for device list iteration. @@ -446,39 +385,26 @@ gboolean nm_link_state_monitor (gpointer user_data) { NMDevice *dev = (NMDevice *)(elt->data); - if (dev) + if (dev && nm_device_is_wireless (dev)) { if (!nm_device_is_up (dev)) nm_device_bring_up (dev); - nm_device_update_link_active (dev); - if (dev == data->active_device) - { - if (nm_device_is_wireless (dev) && !nm_device_get_link_active (dev)) - { - /* If we loose a link to the access point, then - * look for another access point to connect to. - */ - nm_device_update_best_ap (dev); - } - } - else - { - /* Ensure that the device has no IP address or routes. This will - * sometimes occur when a card gets inserted, and the system - * initscripts will run to bring the card up, but they get around to - * running _after_ we've been notified of insertion and cleared out - * card info already. - */ - nm_system_device_flush_routes (dev); - if (nm_device_get_ip4_address (dev) != 0) - nm_system_device_flush_addresses (dev); - } + nm_device_update_link_state (dev); + + /* Is this the currently selected device? + * If so, let's make sure it's still has + * an active link. If it lost the link, + * find a better access point. + */ + if ((dev == data->active_device) && + !nm_device_has_active_link (dev)) + nm_device_update_best_ap (dev); } } nm_unlock_mutex (data->dev_list_mutex, __FUNCTION__); - } else syslog( LOG_ERR, "nm_link_state_monitor() could not acquire device list mutex." ); + } else syslog ( LOG_ERR, "nm_poll_and_update_wireless_link_state() could not acquire device list mutex." ); return (TRUE); } @@ -623,6 +549,114 @@ static void nm_print_usage (void) "\n"); } +static void +nm_monitor_wireless_link_state (NMData *data) +{ + GSource *link_source; + link_source = g_timeout_source_new (NM_WIRELESS_LINK_STATE_POLL_INTERVAL); + g_source_set_callback (link_source, + (GSourceFunc) nm_poll_and_update_wireless_link_state, + nm_data, NULL); + g_source_attach (link_source, nm_data->main_context); + g_source_unref (link_source); +} + +static void +nm_wired_link_activated (NmNetlinkMonitor *monitor, + const gchar *interface_name, + NMData *data) +{ + NMDevice *device; + + if (nm_try_acquire_mutex (data->dev_list_mutex, __FUNCTION__)) + { + device = nm_get_device_by_iface (data, interface_name); + + if (device != NULL) + { + nm_device_set_link_active (device, TRUE); + + if (nm_device_has_active_link (device) + && data->active_device + && data->active_device_locked + && nm_device_is_wireless (data->active_device)) + { + data->active_device_locked = FALSE; + nm_policy_schedule_state_update (data); + } + } + else + syslog (LOG_ERR, "unknown wired ethernet interface '%s' activated\n", + interface_name); + nm_unlock_mutex (data->dev_list_mutex, __FUNCTION__); + } +} + +static void +nm_wired_link_deactivated (NmNetlinkMonitor *monitor, + const gchar *interface_name, + NMData *data) +{ + NMDevice *device; + + if (nm_try_acquire_mutex (data->dev_list_mutex, __FUNCTION__)) + { + device = nm_get_device_by_iface (data, interface_name); + + if (device != NULL) + nm_device_set_link_active (device, FALSE); + else + syslog (LOG_ERR, "unknown wired ethernet interface '%s' " + "deactivated\n", interface_name); + nm_unlock_mutex (data->dev_list_mutex, __FUNCTION__); + } +} + +static void +nm_error_monitoring_wired_link_state (NmNetlinkMonitor *monitor, + GError *error, + NMData *data) +{ + /* FIXME: Try to handle the error instead of just printing it. + */ + syslog (LOG_ERR, "error monitoring wired ethernet link state: %s\n", + error->message); +} + +static void +nm_monitor_wired_link_state (NMData *data) +{ + GError *error; + NmNetlinkMonitor *monitor; + + monitor = nm_netlink_monitor_new (); + + error = NULL; + nm_netlink_monitor_open_connection (monitor, &error); + + if (error != NULL) + { + syslog (LOG_ERR, "could not monitor wired ethernet devices: %s", + error->message); + g_error_free (error); + g_object_unref (monitor); + return; + } + + g_signal_connect (G_OBJECT (monitor), "interface-connected", + G_CALLBACK (nm_wired_link_activated), data); + + g_signal_connect (G_OBJECT (monitor), "interface-disconnected", + G_CALLBACK (nm_wired_link_deactivated), data); + + g_signal_connect (G_OBJECT (monitor), "error", + G_CALLBACK (nm_error_monitoring_wired_link_state), + data); + + nm_netlink_monitor_attach (monitor, data->main_context); + + data->netlink_monitor = monitor; +} /* * main @@ -766,8 +800,6 @@ int main( int argc, char *argv[] ) nm_hal_device_new_capability); libhal_ctx_set_device_lost_capability (ctx, nm_hal_device_lost_capability); - libhal_ctx_set_device_property_modified (ctx, - nm_hal_device_property_modified); libhal_device_property_watch_all (nm_data->hal_ctx, &dbus_error); @@ -789,10 +821,9 @@ int main( int argc, char *argv[] ) /* Bring up the loopback interface. */ nm_system_enable_loopback (); - /* Create a watch function that monitors cards for link status. */ - link_source = g_timeout_source_new (5000); - g_source_set_callback (link_source, nm_link_state_monitor, nm_data, NULL); - link_source_id = g_source_attach (link_source, nm_data->main_context); + /* Create watch functions that monitor cards for link status. */ + nm_monitor_wireless_link_state (nm_data); + nm_monitor_wired_link_state (nm_data); if (!nm_named_manager_start (nm_data->named, &error)) { @@ -803,9 +834,6 @@ int main( int argc, char *argv[] ) /* Wheeee!!! */ g_main_loop_run (nm_data->main_loop); - /* Kill the watch functions */ - g_source_remove (link_source_id); - /* Cleanup */ if (libhal_ctx_shutdown (nm_data->hal_ctx, &dbus_error) != 0) { syslog (LOG_NOTICE, "Error: libhal shutdown failed - %s", dbus_error.message); diff --git a/src/NetworkManagerDbus.c b/src/NetworkManagerDbus.c index 8c2b17393..1c028c35e 100644 --- a/src/NetworkManagerDbus.c +++ b/src/NetworkManagerDbus.c @@ -39,6 +39,7 @@ #include "nm-dbus-device.h" #include "nm-dbus-net.h" #include "nm-dbus-dhcp.h" +#include "nm-utils.h" /* @@ -75,9 +76,15 @@ DBusMessage *nm_dbus_create_error_message (DBusMessage *message, const char *exc */ static unsigned char * nm_dbus_get_object_path_from_device (NMDevice *dev) { + char *object_path, *escaped_object_path; + g_return_val_if_fail (dev != NULL, NULL); - return (g_strdup_printf ("%s/%s", NM_DBUS_PATH_DEVICES, nm_device_get_iface (dev))); + object_path = g_strdup_printf ("%s/%s", NM_DBUS_PATH_DEVICES, nm_device_get_iface (dev)); + escaped_object_path = nm_dbus_escape_object_path (object_path); + g_free (object_path); + + return escaped_object_path; } @@ -106,15 +113,21 @@ NMDevice *nm_dbus_get_device_from_object_path (NMData *data, const char *path) { GSList *elt; char compare_path[100]; + char *escaped_compare_path; for (elt = data->dev_list; elt; elt = g_slist_next (elt)) { if ((dev = (NMDevice *)(elt->data))) { snprintf (compare_path, 100, "%s/%s", NM_DBUS_PATH_DEVICES, nm_device_get_iface (dev)); + escaped_compare_path = nm_dbus_escape_object_path (compare_path); /* Compare against our constructed path, but ignore any trailing elements */ - if (strncmp (path, compare_path, strlen (compare_path)) == 0) + if (strncmp (path, compare_path, strlen (escaped_compare_path)) == 0) + { + g_free (escaped_compare_path); break; + } + g_free (escaped_compare_path); dev = NULL; } } @@ -298,11 +311,11 @@ void nm_dbus_signal_device_status_change (DBusConnection *connection, NMDevice * if (ap && nm_ap_get_essid (ap)) { const char *essid; essid = nm_ap_get_essid (ap); - dbus_message_append_args (message, DBUS_TYPE_STRING, &dev_path, + dbus_message_append_args (message, DBUS_TYPE_OBJECT_PATH, &dev_path, DBUS_TYPE_STRING, &essid, DBUS_TYPE_INVALID); } else - dbus_message_append_args (message, DBUS_TYPE_STRING, &dev_path, DBUS_TYPE_INVALID); + dbus_message_append_args (message, DBUS_TYPE_OBJECT_PATH, &dev_path, DBUS_TYPE_INVALID); if (ap) nm_ap_unref (ap); @@ -405,7 +418,7 @@ void nm_dbus_signal_device_ip4_address_change (DBusConnection *connection, NMDev return; } - dbus_message_append_args (message, DBUS_TYPE_STRING, &dev_path, DBUS_TYPE_INVALID); + dbus_message_append_args (message, DBUS_TYPE_OBJECT_PATH, &dev_path, DBUS_TYPE_INVALID); g_free (dev_path); if (!dbus_connection_send (connection, message, NULL)) @@ -451,8 +464,8 @@ void nm_dbus_signal_wireless_network_change (DBusConnection *connection, NMDevic } dbus_message_append_args (message, - DBUS_TYPE_STRING, &dev_path, - DBUS_TYPE_STRING, &ap_path, + DBUS_TYPE_OBJECT_PATH, &dev_path, + DBUS_TYPE_OBJECT_PATH, &ap_path, DBUS_TYPE_UINT32, &status, DBUS_TYPE_INVALID); g_free (ap_path); @@ -548,9 +561,9 @@ NMAccessPoint *nm_dbus_get_network_object (DBusConnection *connection, NMNetwork DBusMessage *reply; NMAccessPoint *ap = NULL; - char *essid = NULL; + const char *essid = NULL; gint timestamp_secs = -1; - char *key = NULL; + const char *key = NULL; NMEncKeyType key_type = -1; gboolean trusted = FALSE; NMDeviceAuthMethod auth_method = NM_DEVICE_AUTH_METHOD_UNKNOWN; @@ -589,6 +602,9 @@ NMAccessPoint *nm_dbus_get_network_object (DBusConnection *connection, NMNetwork goto out; } + /* FIXME: These argument types need to be validated + */ + dbus_message_iter_init (reply, &iter); dbus_message_iter_get_basic (&iter, &essid); dbus_message_iter_get_basic (&iter, ×tamp_secs); @@ -618,6 +634,7 @@ NMAccessPoint *nm_dbus_get_network_object (DBusConnection *connection, NMNetwork nm_ap_set_auth_method (ap, auth_method); /* Get user addresses, form into a GSList, and stuff into the AP */ + if (dbus_message_iter_get_arg_type (&iter) == DBUS_TYPE_ARRAY) { GSList *addr_list = NULL; DBusMessageIter array_iter; @@ -673,7 +690,7 @@ gboolean nm_dbus_update_network_auth_method (DBusConnection *connection, const c } auth_method_as_int32 = (dbus_int32_t) auth_method; - dbus_message_append_args (message, DBUS_TYPE_STRING, network, + dbus_message_append_args (message, DBUS_TYPE_STRING, &network, DBUS_TYPE_INT32, &auth_method, DBUS_TYPE_INVALID); @@ -706,7 +723,7 @@ gboolean nm_dbus_add_network_address (DBusConnection *connection, NMNetworkType DBusMessage *message; DBusError error; gboolean success = FALSE; - char char_addr[20]; + gchar *char_addr; dbus_int32_t type_as_int32; g_return_val_if_fail (connection != NULL, FALSE); @@ -722,14 +739,15 @@ gboolean nm_dbus_add_network_address (DBusConnection *connection, NMNetworkType return (FALSE); } - memset (char_addr, 0, 20); - ether_ntoa_r (addr, &char_addr[0]); + char_addr = g_new0 (gchar, 20); + ether_ntoa_r (addr, char_addr); type_as_int32 = (dbus_int32_t) type; - dbus_message_append_args (message, DBUS_TYPE_STRING, network, - DBUS_TYPE_INT32, &type_as_int32, - DBUS_TYPE_STRING, &char_addr, - DBUS_TYPE_INVALID); + dbus_message_append_args (message, DBUS_TYPE_STRING, &network, + DBUS_TYPE_INT32, &type_as_int32, + DBUS_TYPE_STRING, &char_addr, + DBUS_TYPE_INVALID); + g_free (char_addr); /* Send message and get trusted status back from NetworkManagerInfo */ dbus_error_init (&error); @@ -813,6 +831,8 @@ char ** nm_dbus_get_networks (DBusConnection *connection, NMNetworkType type, in dbus_message_iter_next(&array_iter); } networks = (gchar **)(buffer->data); + if (num_networks != NULL) + *num_networks = buffer->len; g_array_free (buffer, FALSE); } @@ -969,7 +989,7 @@ static DBusHandlerResult nm_dbus_devices_message_handler (DBusConnection *connec } else { - char *object_path; + char *object_path, *escaped_object_path; NMDbusCBData cb_data; cb_data.data = data; @@ -977,10 +997,13 @@ static DBusHandlerResult nm_dbus_devices_message_handler (DBusConnection *connec /* Test whether or not the _networks_ of a device were queried instead of the device itself */ object_path = g_strdup_printf ("%s/%s/Networks/", NM_DBUS_PATH_DEVICES, nm_device_get_iface (dev)); - if (strncmp (path, object_path, strlen (object_path)) == 0) + escaped_object_path = nm_dbus_escape_object_path (object_path); + g_free (object_path); + if (strncmp (path, escaped_object_path, strlen (escaped_object_path)) == 0) handled = nm_dbus_method_dispatch (data->net_methods, connection, message, &cb_data, &reply); else handled = nm_dbus_method_dispatch (data->device_methods, connection, message, &cb_data, &reply); + g_free (escaped_object_path); } if (reply) diff --git a/src/NetworkManagerDevice.c b/src/NetworkManagerDevice.c index e46d83b51..ead8ac28c 100644 --- a/src/NetworkManagerDevice.c +++ b/src/NetworkManagerDevice.c @@ -39,6 +39,8 @@ #include "NetworkManagerSystem.h" #include "NetworkManagerDHCP.h" +#include "nm-utils.h" + /* Local static prototypes */ static gpointer nm_device_worker (gpointer user_data); static gboolean nm_device_activate (gpointer user_data); @@ -307,7 +309,11 @@ NMDevice *nm_device_new (const char *iface, const char *udi, gboolean test_dev, if (nm_device_get_driver_support_level (dev) != NM_DRIVER_UNSUPPORTED) { - nm_device_update_link_active (dev); + if (nm_device_is_wireless (dev)) + { + nm_device_update_link_state (dev); + nm_device_update_signal_strength (dev); + } nm_device_update_ip4_address (dev); nm_device_update_hw_address (dev); @@ -610,7 +616,7 @@ NMDriverSupportLevel nm_device_get_driver_support_level (NMDevice *dev) /* * Get/set functions for link_active */ -gboolean nm_device_get_link_active (NMDevice *dev) +gboolean nm_device_has_active_link (NMDevice *dev) { g_return_val_if_fail (dev != NULL, FALSE); @@ -621,7 +627,13 @@ void nm_device_set_link_active (NMDevice *dev, const gboolean link_active) { g_return_if_fail (dev != NULL); - dev->link_active = link_active; + if (dev->link_active != link_active) + { + dev->link_active = link_active; + + nm_dbus_schedule_device_status_change (dev, DEVICE_STATUS_CHANGE); + nm_policy_schedule_state_update (dev->app_data); + } } @@ -693,7 +705,7 @@ static gboolean nm_device_wireless_is_associated (NMDevice *dev) /* Test devices have their link state set through DBUS */ if (dev->test_device) - return (nm_device_get_link_active (dev)); + return (nm_device_has_active_link (dev)); if ((sk = iw_sockets_open ()) < 0) return (FALSE); @@ -730,12 +742,12 @@ out: } /* - * nm_device_wireless_link_active + * nm_device_probe_wireless_link_state * * Gets the link state of a wireless device * */ -static gboolean nm_device_wireless_link_active (NMDevice *dev) +static gboolean nm_device_probe_wireless_link_state (NMDevice *dev) { gboolean link = FALSE; NMAccessPoint *best_ap; @@ -745,7 +757,7 @@ static gboolean nm_device_wireless_link_active (NMDevice *dev) /* Test devices have their link state set through DBUS */ if (dev->test_device) - return (nm_device_get_link_active (dev)); + return (nm_device_has_active_link (dev)); if (!nm_device_wireless_is_associated (dev)) return (FALSE); @@ -765,14 +777,16 @@ static gboolean nm_device_wireless_link_active (NMDevice *dev) /* - * nm_device_wired_link_active + * nm_device_probe_wired_link_state * * * */ -static gboolean nm_device_wired_link_active (NMDevice *dev) +static gboolean nm_device_probe_wired_link_state (NMDevice *dev) { gboolean link = FALSE; + gchar *contents, *carrier_path; + gsize length; g_return_val_if_fail (dev != NULL, FALSE); g_return_val_if_fail (nm_device_is_wired (dev) == TRUE, FALSE); @@ -780,32 +794,36 @@ static gboolean nm_device_wired_link_active (NMDevice *dev) /* Test devices have their link state set through DBUS */ if (dev->test_device) - return (nm_device_get_link_active (dev)); + return (nm_device_has_active_link (dev)); + + if (dev->removed) + return FALSE; + + carrier_path = g_strdup_printf ("/sys/sys/class/net/%s/carrier", dev->iface); + if (g_file_get_contents (carrier_path, &contents, &length, NULL)) { + link = (gboolean) atoi (contents); + g_free (contents); + } else { + contents = NULL; + } /* We say that non-carrier-detect devices always have a link, because * they never get auto-selected by NM. User has to force them on us, * so we just hope the user knows whether or not the cable's plugged in. */ - if (dev->options.wired.has_carrier_detect != TRUE) + if ((dev->options.wired.has_carrier_detect != TRUE) || (contents == NULL)) link = TRUE; - else - { - /* Device has carrier detect, yay! */ - if (libhal_device_property_exists (dev->app_data->hal_ctx, nm_device_get_udi (dev), "net.80203.link", NULL)) - link = libhal_device_get_property_bool (dev->app_data->hal_ctx, nm_device_get_udi (dev), "net.80203.link", NULL); - } return (link); } - /* - * nm_device_update_link_active + * nm_device_update_link_state * * Updates the link state for a particular device. * */ -void nm_device_update_link_active (NMDevice *dev) +void nm_device_update_link_state (NMDevice *dev) { gboolean link = FALSE; @@ -815,27 +833,16 @@ void nm_device_update_link_active (NMDevice *dev) switch (nm_device_get_type (dev)) { case DEVICE_TYPE_WIRELESS_ETHERNET: - link = nm_device_wireless_link_active (dev); - /* Update our current signal strength too */ - nm_device_update_signal_strength (dev); + nm_device_set_link_active (dev, nm_device_probe_wireless_link_state (dev)); break; case DEVICE_TYPE_WIRED_ETHERNET: - link = nm_device_wired_link_active (dev); + nm_device_set_link_active (dev, nm_device_probe_wired_link_state (dev)); break; default: - link = nm_device_get_link_active (dev); /* Can't get link info for this device, so don't change link status */ break; } - - /* Update device link status and global state variable if the status changed */ - if (link != nm_device_get_link_active (dev)) - { - nm_device_set_link_active (dev, link); - nm_dbus_schedule_device_status_change (dev, DEVICE_STATUS_CHANGE); - nm_policy_schedule_state_update (dev->app_data); - } } @@ -1155,7 +1162,7 @@ void nm_device_get_ap_address (NMDevice *dev, struct ether_addr *addr) { struct ether_addr good_addr = { {0x70, 0x37, 0x03, 0x70, 0x37, 0x03} }; struct ether_addr bad_addr = { {0x00, 0x00, 0x00, 0x00, 0x00, 0x00} }; - gboolean link = nm_device_get_link_active (dev); + gboolean link = nm_device_has_active_link (dev); memcpy ((link ? &good_addr : &bad_addr), &(wrq.u.ap_addr.sa_data), sizeof (struct ether_addr)); return; @@ -2790,8 +2797,15 @@ char * nm_device_get_path_for_ap (NMDevice *dev, NMAccessPoint *ap) g_return_val_if_fail (ap != NULL, NULL); if (nm_ap_get_essid (ap)) - return (g_strdup_printf ("%s/%s/Networks/%s", NM_DBUS_PATH_DEVICES, nm_device_get_iface (dev), nm_ap_get_essid (ap))); - else + { + char *path, *escaped_path; + + path = g_strdup_printf ("%s/%s/Networks/%s", NM_DBUS_PATH_DEVICES, nm_device_get_iface (dev), nm_ap_get_essid (ap)); + escaped_path = nm_dbus_escape_object_path (path); + g_free (path); + + return (escaped_path); + } else return (NULL); } diff --git a/src/NetworkManagerDevice.h b/src/NetworkManagerDevice.h index def2cc93f..9058abf35 100644 --- a/src/NetworkManagerDevice.h +++ b/src/NetworkManagerDevice.h @@ -56,9 +56,9 @@ NMData * nm_device_get_app_data (const NMDevice *dev); gboolean nm_device_get_removed (const NMDevice *dev); void nm_device_set_removed (NMDevice *dev, const gboolean removed); -gboolean nm_device_get_link_active (NMDevice *dev); +gboolean nm_device_has_active_link (NMDevice *dev); void nm_device_set_link_active (NMDevice *dev, const gboolean active); -void nm_device_update_link_active (NMDevice *dev); +void nm_device_update_link_state (NMDevice *dev); char * nm_device_get_essid (NMDevice *dev); void nm_device_set_essid (NMDevice *dev, const char *essid); diff --git a/src/NetworkManagerMain.h b/src/NetworkManagerMain.h index 18b424eb7..fcd756a42 100644 --- a/src/NetworkManagerMain.h +++ b/src/NetworkManagerMain.h @@ -28,6 +28,7 @@ #include #include "NetworkManager.h" #include "NetworkManagerAP.h" +#include "nm-netlink-monitor.h" #include "nm-named-manager.h" typedef struct NMDbusMethodList NMDbusMethodList; @@ -36,10 +37,12 @@ typedef struct NMDbusMethodList NMDbusMethodList; typedef struct NMData { GIOChannel *sigterm_iochannel; - int sigterm_pipe[2]; + int sigterm_pipe[2]; LibHalContext *hal_ctx; + NmNetlinkMonitor *netlink_monitor; + NMNamedManager *named; GList *nameserver_ids; /* For now these are global instead of per-device */ GList *domain_search_ids; diff --git a/src/NetworkManagerPolicy.c b/src/NetworkManagerPolicy.c index 71910fdf6..189c9194f 100644 --- a/src/NetworkManagerPolicy.c +++ b/src/NetworkManagerPolicy.c @@ -65,7 +65,7 @@ static NMDevice * nm_policy_auto_get_best_device (NMData *data) continue; dev_type = nm_device_get_type (dev); - link_active = nm_device_get_link_active (dev); + link_active = nm_device_has_active_link (dev); if (dev_type == DEVICE_TYPE_WIRED_ETHERNET) { @@ -181,7 +181,7 @@ static NMDevice * nm_policy_get_best_device (NMDevice *switch_to_dev, NMData *da { /* Wired devices get unlocked only if they have lost their link */ case (DEVICE_TYPE_WIRED_ETHERNET): - if (nm_device_get_link_active (data->active_device)) + if (nm_device_has_active_link (data->active_device)) best_dev = data->active_device; break; @@ -251,7 +251,7 @@ gboolean nm_policy_activation_finish (gpointer user_data) struct ether_addr addr; nm_device_get_ap_address (dev, &addr); - if (!nm_ethernet_address_is_valid (nm_ap_get_address (ap))) + if (!nm_ap_get_address (ap) || !nm_ethernet_address_is_valid (nm_ap_get_address (ap))) nm_ap_set_address (ap, &addr); /* Don't store MAC addresses for non-infrastructure networks */ diff --git a/src/autoip.c b/src/autoip.c index 92df4a369..4fb8f90d9 100644 --- a/src/autoip.c +++ b/src/autoip.c @@ -124,7 +124,7 @@ static int timeval_subtract (struct timeval *result, struct timeval *x, struct t y->tv_usec += 1000000 * nsec; y->tv_sec -= nsec; } - + /* Compute the time remaining to wait. `tv_usec' is certainly positive. */ result->tv_sec = x->tv_sec - y->tv_sec; diff --git a/src/nm-dbus-device.c b/src/nm-dbus-device.c index 38acd0fd6..41ead4238 100644 --- a/src/nm-dbus-device.c +++ b/src/nm-dbus-device.c @@ -130,7 +130,7 @@ static DBusMessage *nm_dbus_device_get_link_active (DBusConnection *connection, if ((reply = dbus_message_new_method_return (message))) { dbus_bool_t is_active; - is_active = nm_device_get_link_active (dev); + is_active = nm_device_has_active_link (dev); dbus_message_append_args (reply, DBUS_TYPE_BOOLEAN, &is_active, DBUS_TYPE_INVALID); } @@ -186,16 +186,24 @@ static DBusMessage *nm_dbus_device_get_active_network (DBusConnection *connectio if ((best_ap = nm_device_get_best_ap (dev))) { NMAccessPoint *tmp_ap; - char *object_path = g_strdup_printf ("%s/%s/Networks/", NM_DBUS_PATH_DEVICES, nm_device_get_iface (dev)); + char *object_path, *escaped_object_path; + + object_path = g_strdup_printf ("%s/%s/Networks/", NM_DBUS_PATH_DEVICES, nm_device_get_iface (dev)); + escaped_object_path = nm_dbus_escape_object_path (object_path); + g_free (object_path); if ( (tmp_ap = nm_device_ap_list_get_ap_by_essid (dev, nm_ap_get_essid (best_ap))) && (object_path = nm_device_get_path_for_ap (dev, tmp_ap))) { - dbus_message_append_args (reply, DBUS_TYPE_STRING, &object_path, DBUS_TYPE_INVALID); + + escaped_object_path = nm_dbus_escape_object_path (object_path); + g_free (object_path); + + dbus_message_append_args (reply, DBUS_TYPE_OBJECT_PATH, &escaped_object_path, DBUS_TYPE_INVALID); success = TRUE; } nm_ap_unref (best_ap); - g_free (object_path); + g_free (escaped_object_path); } if (!success) { diff --git a/src/nm-dbus-net.c b/src/nm-dbus-net.c index fe1d852cc..623b50f8f 100644 --- a/src/nm-dbus-net.c +++ b/src/nm-dbus-net.c @@ -25,6 +25,7 @@ #include "NetworkManagerAPList.h" #include "NetworkManagerUtils.h" #include "nm-dbus-net.h" +#include "nm-utils.h" /* * nm_dbus_get_ap_from_object_path @@ -37,7 +38,7 @@ static NMAccessPoint *nm_dbus_get_ap_from_object_path (const char *path, NMDevic NMAccessPoint *ap = NULL; NMAccessPointList *ap_list; NMAPListIter *iter; - char compare_path[100]; + char compare_path[100], *escaped_compare_path; g_return_val_if_fail (path != NULL, NULL); g_return_val_if_fail (dev != NULL, NULL); @@ -53,8 +54,14 @@ static NMAccessPoint *nm_dbus_get_ap_from_object_path (const char *path, NMDevic { snprintf (compare_path, 100, "%s/%s/Networks/%s", NM_DBUS_PATH_DEVICES, nm_device_get_iface (dev), nm_ap_get_essid (ap)); - if (strncmp (path, compare_path, strlen (compare_path)) == 0) + escaped_compare_path = nm_dbus_escape_object_path (compare_path); + if (strncmp (path, escaped_compare_path, + strlen (escaped_compare_path)) == 0) + { + g_free (escaped_compare_path); break; + } + g_free (escaped_compare_path); } nm_ap_list_iter_free (iter); diff --git a/src/nm-netlink-monitor.c b/src/nm-netlink-monitor.c new file mode 100644 index 000000000..e7f8849ba --- /dev/null +++ b/src/nm-netlink-monitor.c @@ -0,0 +1,635 @@ +/* nm-netlink-monitor.c - Monitor netlink socket for interface change + * events + * + * Copyright (C) 2005 Ray Strode + * + * Some code borrowed from HAL: + + * Copyright (C) 2003 David Zeuthen, + * Copyright (C) 2004 Novell, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "nm-netlink-monitor.h" + +#define NM_NETLINK_MONITOR_EVENT_CONDITIONS \ + ((GIOCondition) (G_IO_IN | G_IO_PRI)) + +#define NM_NETLINK_MONITOR_ERROR_CONDITIONS \ + ((GIOCondition) (G_IO_ERR | G_IO_NVAL)) + +#define NM_NETLINK_MONITOR_DISCONNECT_CONDITIONS \ + ((GIOCondition) (G_IO_HUP)) + +struct _NmNetlinkMonitorPrivate +{ + GMainContext *context; + GIOChannel *io_channel; + GSource *event_source; +}; + +static void nm_netlink_monitor_finalize (GObject *object); +static void nm_netlink_monitor_class_install_signals (NmNetlinkMonitorClass *service_class); + +static gboolean +nm_netlink_monitor_event_handler (GIOChannel *channel, + GIOCondition io_condition, + NmNetlinkMonitor *monitor); +static gboolean +nm_netlink_monitor_error_handler (GIOChannel *channel, + GIOCondition io_condition, + NmNetlinkMonitor *monitor); +static gboolean +nm_netlink_monitor_disconnect_handler (GIOChannel *channel, + GIOCondition io_condition, + NmNetlinkMonitor *monitor); +enum +{ + INTERFACE_CONNECTED = 0, + INTERFACE_DISCONNECTED, + ERROR, + NUMBER_OF_SIGNALS +}; + +static guint nm_netlink_monitor_signals[NUMBER_OF_SIGNALS]; + +G_DEFINE_TYPE (NmNetlinkMonitor, nm_netlink_monitor, G_TYPE_OBJECT); + +static void +nm_netlink_monitor_class_init (NmNetlinkMonitorClass *monitor_class) +{ + GObjectClass *gobject_class; + + gobject_class = G_OBJECT_CLASS (monitor_class); + + gobject_class->finalize = nm_netlink_monitor_finalize; + + nm_netlink_monitor_class_install_signals (monitor_class); + + g_type_class_add_private (monitor_class, sizeof (NmNetlinkMonitorPrivate)); +} + +static void +nm_netlink_monitor_class_install_signals (NmNetlinkMonitorClass *monitor_class) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (monitor_class); + + nm_netlink_monitor_signals[INTERFACE_CONNECTED] = + g_signal_new ("interface-connected", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (NmNetlinkMonitorClass, interface_connected), + NULL, NULL, g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, 1, G_TYPE_STRING); + monitor_class->interface_connected = NULL; + + nm_netlink_monitor_signals[INTERFACE_DISCONNECTED] = + g_signal_new ("interface-disconnected", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (NmNetlinkMonitorClass, interface_disconnected), + NULL, NULL, g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, 1, G_TYPE_STRING); + monitor_class->interface_disconnected = NULL; + + nm_netlink_monitor_signals[ERROR] = + g_signal_new ("error", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (NmNetlinkMonitorClass, error), + NULL, NULL, g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, G_TYPE_POINTER); + monitor_class->error = NULL; +} + +gboolean +nm_netlink_monitor_open_connection (NmNetlinkMonitor *monitor, + GError **error) +{ + struct sockaddr_nl monitor_address = { 0 }; + int fd, saved_errno; + GError *channel_error; + GIOFlags channel_flags; + + g_return_val_if_fail (monitor->priv->io_channel == NULL, FALSE); + + fd = socket (PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + + if (fd < 0) + { + saved_errno = errno; + + g_set_error (error, NM_NETLINK_MONITOR_ERROR, + NM_NETLINK_MONITOR_ERROR_OPENING_SOCKET, + _("unable to create netlink socket for monitoring " + "wired ethernet devices - %s"), + g_strerror (saved_errno)); + return FALSE; + } + + monitor_address.nl_family = AF_NETLINK; + monitor_address.nl_pid = getpid (); + monitor_address.nl_groups = RTMGRP_LINK; + + if (bind (fd, + (struct sockaddr *) &monitor_address, + sizeof (monitor_address)) < 0) { + + saved_errno = errno; + + g_set_error (error, NM_NETLINK_MONITOR_ERROR, + NM_NETLINK_MONITOR_ERROR_BINDING_TO_SOCKET, + _("unable to bind to netlink socket for monitoring " + "wired ethernet devices - %s"), + g_strerror (saved_errno)); + return FALSE; + } + + monitor->priv->io_channel = g_io_channel_unix_new (fd); + + channel_error = NULL; + g_io_channel_set_encoding (monitor->priv->io_channel, + NULL /* encoding */, + &channel_error); + + /* Encoding is NULL, so no conversion error can possibly + * occur + */ + g_assert (channel_error == NULL); + + g_io_channel_set_close_on_unref (monitor->priv->io_channel, + TRUE); + + channel_flags = g_io_channel_get_flags (monitor->priv->io_channel); + channel_error = NULL; + g_io_channel_set_flags (monitor->priv->io_channel, + channel_flags | G_IO_FLAG_NONBLOCK, + &channel_error); + + if (channel_error != NULL) + { + g_propagate_error (error, channel_error); + return FALSE; + } + + return TRUE; +} + +void +nm_netlink_monitor_close_connection (NmNetlinkMonitor *monitor) +{ + g_return_if_fail (monitor->priv->io_channel != NULL); + + if (monitor->priv->event_source != NULL) + nm_netlink_monitor_detach (monitor); + + g_io_channel_shutdown (monitor->priv->io_channel, + TRUE /* flush pending data */, + NULL); + + g_io_channel_unref (monitor->priv->io_channel); + monitor->priv->io_channel = NULL; +} + +static void +nm_netlink_monitor_init (NmNetlinkMonitor *monitor) +{ + monitor->priv = G_TYPE_INSTANCE_GET_PRIVATE (monitor, + NM_TYPE_NETLINK_MONITOR, + NmNetlinkMonitorPrivate); + + monitor->priv->context = NULL; + monitor->priv->io_channel = NULL; + monitor->priv->event_source = NULL; +} + +static void +nm_netlink_monitor_finalize (GObject *object) +{ + NmNetlinkMonitor *monitor; + GObjectClass *gobject_class; + + monitor = NM_NETLINK_MONITOR (object); + gobject_class = G_OBJECT_CLASS (nm_netlink_monitor_parent_class); + + if (monitor->priv->io_channel != NULL) + nm_netlink_monitor_close_connection (monitor); + + gobject_class->finalize (object); +} + +GQuark +nm_netlink_monitor_error_quark (void) +{ + static GQuark error_quark = 0; + + if (error_quark == 0) + error_quark = g_quark_from_static_string ("nm-netlink-monitor-error-quark"); + + return error_quark; +} + +NmNetlinkMonitor * +nm_netlink_monitor_new (void) +{ + GObject *instance; + + instance = g_object_new (NM_TYPE_NETLINK_MONITOR, NULL); + + return NM_NETLINK_MONITOR (instance); +} + +static void +nm_netlink_monitor_clear_event_source (NmNetlinkMonitor *monitor) +{ + monitor->priv->event_source = NULL; +} + +void +nm_netlink_monitor_attach (NmNetlinkMonitor *monitor, + GMainContext *context) +{ + GSource *event_source; + + g_return_if_fail (NM_IS_NETLINK_MONITOR (monitor)); + g_return_if_fail (monitor->priv->context == NULL); + + if (context == NULL) + context = g_main_context_default (); + + monitor->priv->context = g_main_context_ref (context); + + event_source = g_io_create_watch (monitor->priv->io_channel, + NM_NETLINK_MONITOR_EVENT_CONDITIONS | + NM_NETLINK_MONITOR_ERROR_CONDITIONS | + NM_NETLINK_MONITOR_DISCONNECT_CONDITIONS); + g_source_set_callback (event_source, + (GSourceFunc) nm_netlink_monitor_event_handler, + monitor, + (GDestroyNotify) + nm_netlink_monitor_clear_event_source); + g_source_attach (event_source, context); + monitor->priv->event_source = event_source; +} + +void +nm_netlink_monitor_detach (NmNetlinkMonitor *monitor) +{ + g_return_if_fail (NM_IS_NETLINK_MONITOR (monitor)); + g_return_if_fail (monitor->priv->context != NULL); + + g_source_destroy (monitor->priv->event_source); + monitor->priv->event_source = NULL; + + g_main_context_unref (monitor->priv->context); + monitor->priv->context = NULL; +} + +void +nm_netlink_monitor_request_status (NmNetlinkMonitor *monitor) +{ + +} + +static gboolean +receive_pending_bytes (GIOChannel *channel, + gchar **str_return, + gsize *length, + GError **error) +{ + GString *pending_bytes; + ssize_t num_bytes_read; + gboolean succeeded; + struct sockaddr_nl sender = { 0 }; + gchar buffer[4096]; + static const size_t buffer_capacity = (size_t) sizeof (buffer); + socklen_t sender_size; + int fd, saved_errno; + + fd = g_io_channel_unix_get_fd (channel); + + pending_bytes = g_string_sized_new (4096); + + sender_size = (socklen_t) sizeof (sender); + saved_errno = 0; + succeeded = TRUE; + do + { + num_bytes_read = recvfrom (fd, buffer, buffer_capacity, + MSG_DONTWAIT, + (struct sockaddr *) &sender, + &sender_size); + + if (num_bytes_read < 0) + { + saved_errno = errno; + + /* the kernel doesn't send EOF when it's done, + * so we just have to wait until it wants to + * block and assume that means it's done. + */ + if (saved_errno == EAGAIN) + { + saved_errno = 0; + break; + } + } + else if (saved_errno == EINTR) + saved_errno = 0; + + /* First let's make sure that the sender is actually + * someone legitimate. + * + * There are a few possibilities: + * 1) The size of the sender is less than the + * size of a generic sockaddr structure. + * This means we got sent completely bogus + * data. + * 2) The size of the sender is greater than or + * equal to the size of a generic sockaddr + * structure, but the address family the sender + * belongs to is not AF_NETLINK. In this case + * we were sent some spurious packets that we + * don't care about. + * 3) The address family is AF_NETLINK but the + * the size of the sender is not equal to the + * size of a sockaddr_nl structure. This means + * we can't treat the received data as an + * instance of sockaddr_nl structure. + * + * In any of the above cases, we should discard the data. + */ + if ((sender_size != (socklen_t) sizeof (sender)) || + ((sender_size == (socklen_t) sizeof (sender)) && + sender.nl_family != AF_NETLINK)) + { + g_set_error (error, NM_NETLINK_MONITOR_ERROR, + NM_NETLINK_MONITOR_ERROR_BAD_SENDER, + _("received data from wrong type of sender")); + succeeded = FALSE; + goto out; + } + + /* We only care about messages from the kernel, + * not anywhere else. Only the kernel can have + * nl_pid == 0. + */ + if (sender.nl_pid != 0) + { + g_set_error (error, NM_NETLINK_MONITOR_ERROR, + NM_NETLINK_MONITOR_ERROR_BAD_SENDER, + _("received data from unexpected sender")); + succeeded = FALSE; + goto out; + + } + + /* Okay, the data has passed basic sanity checks, + * let's store it. + */ + if (num_bytes_read > 0) + { + g_string_append_len (pending_bytes, + buffer, (gssize) num_bytes_read); + memset (buffer, 0, num_bytes_read); + } + } + while ((num_bytes_read > 0) || (saved_errno == EINTR)); + + if (saved_errno != 0) + { + g_set_error (error, NM_NETLINK_MONITOR_ERROR, + NM_NETLINK_MONITOR_ERROR_READING_SOCKET, + _("%s"), g_strerror (saved_errno)); + succeeded = FALSE; + goto out; + } + + if (pending_bytes->len > 0) + { + if (str_return) + *str_return = pending_bytes->str; + + if (length) + *length = pending_bytes->len; + + g_string_free (pending_bytes, FALSE); + pending_bytes = NULL; + } + else + { + if (str_return) + *str_return = NULL; + + if (length) + *length = 0; + } + +out: + if (pending_bytes != NULL) + g_string_free (pending_bytes, TRUE); + return succeeded; +} + +static gboolean +nm_netlink_monitor_event_handler (GIOChannel *channel, + GIOCondition io_condition, + NmNetlinkMonitor *monitor) +{ + GError *error; + gchar *received_bytes; + gboolean processing_is_done; + gsize num_received_bytes; + guint num_bytes_to_process; + struct nlmsghdr *header; + + if (io_condition == NM_NETLINK_MONITOR_ERROR_CONDITIONS) + return nm_netlink_monitor_error_handler (channel, io_condition, monitor); + else if (io_condition == NM_NETLINK_MONITOR_DISCONNECT_CONDITIONS) + return nm_netlink_monitor_disconnect_handler (channel, io_condition, monitor); + + g_return_val_if_fail (!(io_condition & + ~(NM_NETLINK_MONITOR_EVENT_CONDITIONS)), + TRUE); + + error = NULL; + + /* Unfortunately, the kernel doesn't return EOF when it's + * done sending packets, so read_to_end () gets confused. + * + * This let's us do sockaddr_nl specific sanity checks anyway. + */ + //status = g_io_channel_read_to_end (channel, &received_bytes, + // &num_received_bytes, + // &error); + receive_pending_bytes (channel, &received_bytes, + &num_received_bytes, + &error); + if (error != NULL) + { + g_signal_emit (G_OBJECT (monitor), + nm_netlink_monitor_signals[ERROR], + 0, error); + g_error_free (error); + return FALSE; + } + + /* Why does NLMSG_* use unsigned ints instead of unsigned longs + * or size_t? + */ + num_bytes_to_process = (guint) num_received_bytes; + + if (num_bytes_to_process < 0) + return TRUE; + + processing_is_done = FALSE; + for (header = (struct nlmsghdr *) received_bytes; + !processing_is_done && + NLMSG_OK (header, (gint) num_bytes_to_process); + header = NLMSG_NEXT (header, num_bytes_to_process)) + { + struct ifinfomsg *interface_info; + struct rtattr *attribute; + int num_attribute_bytes_to_process; + gboolean is_connected; + gchar *interface_name; + + g_assert (num_bytes_to_process <= num_received_bytes); + + switch (header->nlmsg_type) + { + case NLMSG_DONE: + processing_is_done = TRUE; + continue; + + case NLMSG_NOOP: + continue; + + case NLMSG_OVERRUN: + { + error = g_error_new (NM_NETLINK_MONITOR_ERROR, + NM_NETLINK_MONITOR_ERROR_BAD_SOCKET_DATA, + _("too much data was sent " + "over socket and some of " + "it was lost")); + + g_signal_emit (G_OBJECT (monitor), + nm_netlink_monitor_signals[ERROR], + 0, error); + g_error_free (error); + error = NULL; + continue; + } + + case NLMSG_ERROR: + { + struct nlmsgerr *error_message; + + error_message = + (struct nlmsgerr *) NLMSG_DATA (header); + + error = g_error_new (NM_NETLINK_MONITOR_ERROR, + NM_NETLINK_MONITOR_ERROR_BAD_SOCKET_DATA, + "%s", g_strerror (error_message->error)); + + g_signal_emit (G_OBJECT (monitor), + nm_netlink_monitor_signals[ERROR], + 0, error); + g_error_free (error); + error = NULL; + continue; + } + + default: + /* we continue above, so we don't have to stuff + * everything below here in here */ + break; + } + + interface_name = NULL; + interface_info = (struct ifinfomsg *) NLMSG_DATA (header); + + /* The !! weirdness is to cannonicalize the value to 0 or 1. + */ + is_connected = !!((gboolean) (interface_info->ifi_flags & IFF_RUNNING)); + + num_attribute_bytes_to_process = IFLA_PAYLOAD (header); + + for (attribute = IFLA_RTA (interface_info); + RTA_OK (attribute, num_attribute_bytes_to_process); + attribute = RTA_NEXT (attribute, num_attribute_bytes_to_process)) + { + if (attribute->rta_type == IFLA_IFNAME) { + interface_name = + (gchar *) g_strdup (RTA_DATA (attribute)); + } + } + + if (interface_name != NULL) + { + if (is_connected) + g_signal_emit (G_OBJECT (monitor), + nm_netlink_monitor_signals[INTERFACE_CONNECTED], + 0, interface_name); + else + g_signal_emit (G_OBJECT (monitor), + nm_netlink_monitor_signals[INTERFACE_DISCONNECTED], + 0, interface_name); + + g_free (interface_name); + } + } + + return TRUE; +} + +static gboolean +nm_netlink_monitor_error_handler (GIOChannel *channel, + GIOCondition io_condition, + NmNetlinkMonitor *monitor) +{ + g_return_val_if_fail (!(io_condition & + ~(NM_NETLINK_MONITOR_ERROR_CONDITIONS)), + TRUE); + return TRUE; +} + +static gboolean +nm_netlink_monitor_disconnect_handler (GIOChannel *channel, + GIOCondition io_condition, + NmNetlinkMonitor *monitor) +{ + + g_return_val_if_fail (!(io_condition & + ~(NM_NETLINK_MONITOR_DISCONNECT_CONDITIONS)), + TRUE); + return FALSE; +} diff --git a/src/nm-netlink-monitor.h b/src/nm-netlink-monitor.h new file mode 100644 index 000000000..9594fb37f --- /dev/null +++ b/src/nm-netlink-monitor.h @@ -0,0 +1,89 @@ +/* nm-netlink-monitor.h - monitor netlink socket for network + * interface eventss + * + * Copyright (C) 2005 Ray Strode + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ +#ifndef NM_NETLINK_MONITOR_H +#define NM_NETLINK_MONITOR_H + +#include +#include + +G_BEGIN_DECLS + +#define NM_TYPE_NETLINK_MONITOR (nm_netlink_monitor_get_type ()) +#define NM_NETLINK_MONITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_NETLINK_MONITOR, NmNetlinkMonitor)) +#define NM_NETLINK_MONITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_NETLINK_MONITOR, NmNetlinkMonitorClass)) +#define NM_IS_NETLINK_MONITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_NETLINK_MONITOR)) +#define NM_IS_NETLINK_MONITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_NETLINK_MONITOR)) +#define NM_NETLINK_MONITOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_NETLINK_MONITOR, NmNetlinkMonitorClass)) +#define NM_NETLINK_MONITOR_ERROR (nm_netlink_monitor_error_quark ()) + + +typedef struct _NmNetlinkMonitor NmNetlinkMonitor; +typedef struct _NmNetlinkMonitorClass NmNetlinkMonitorClass; +typedef struct _NmNetlinkMonitorPrivate NmNetlinkMonitorPrivate; +typedef enum _NmNetlinkMonitorError NmNetlinkMonitorError; + +struct _NmNetlinkMonitor +{ + GObject parent; + + /*< private >*/ + NmNetlinkMonitorPrivate *priv; +}; + +struct _NmNetlinkMonitorClass +{ + GObjectClass parent_class; + + /* Signals */ + void (* interface_connected) (NmNetlinkMonitor *monitor, + const gchar *interface); + void (* interface_disconnected) (NmNetlinkMonitor *monitor, + const gchar *interface); + void (* error) (NmNetlinkMonitor *monitor, + GError *error); +}; + +enum _NmNetlinkMonitorError +{ + NM_NETLINK_MONITOR_ERROR_GENERIC = 0, + NM_NETLINK_MONITOR_ERROR_OPENING_SOCKET, + NM_NETLINK_MONITOR_ERROR_BINDING_TO_SOCKET, + NM_NETLINK_MONITOR_ERROR_BAD_SENDER, + NM_NETLINK_MONITOR_ERROR_BAD_SOCKET_DATA, + NM_NETLINK_MONITOR_ERROR_READING_SOCKET +}; + +GType nm_netlink_monitor_get_type (void) G_GNUC_CONST; +GQuark nm_netlink_monitor_error_quark (void) G_GNUC_CONST; + +NmNetlinkMonitor *nm_netlink_monitor_new (void); + +gboolean +nm_netlink_monitor_open_connection (NmNetlinkMonitor *monitor, + GError **error); + +void nm_netlink_monitor_attach (NmNetlinkMonitor *monitor, + GMainContext *context); +void nm_netlink_monitor_detach (NmNetlinkMonitor *monitor); + +void nm_netlink_monitor_request_status (NmNetlinkMonitor *monitor); +G_END_DECLS +#endif /* NM_NETLINK_MONITOR_H */