From ff6dbffeb1cc1bd23cf5595fdc0c98567ff22360 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 7 Jan 2005 18:07:06 +0000 Subject: [PATCH] 2005-01-07 Dan Williams * dhcpcd/client.c - Rework the DHCP client code to be much less chatty when it receives non-DHCP UDP packets during the DHCP run (reported by and preliminary patches from Bill Moss) * Move wireless scanning to a separate thread. This thread forwards the results to the main thread when done where they are integrated into the device's access point lists. This keeps the main thread (which does all the DBUS communication) from being blocked for long periods of time by wireless scanning. * Make state modification an idle routine in the main loop, and trigger state changes rather than polling for them. * src/backends/NetworkManagerGentoo.c - Fix up invalid C90 code (reported by Christoph Ruessler) * src/NetworkManagerDevice.c - Revert IPv6 patch for wired devices from 2004-12-22 for router advertisements, causing problems and infinite loop during "best" device determination due to link going up/down (reported by Bill Moss) Apply patch from Peter Jones * src/NetworkManagerDevice.c - Shortcut for link-checking for ipw2x00 cards - Split out association check into separate routine git-svn-id: http://svn-archive.gnome.org/svn/NetworkManager/trunk@360 4912f4e0-d625-0410-9fb7-b9a5a253dbdc --- ChangeLog | 30 ++ dhcpcd/client.c | 226 +++++---- src/Makefile.am | 1 + src/NetworkManager.c | 78 +-- src/NetworkManagerDbus.c | 2 - src/NetworkManagerDevice.c | 745 ++++++++++++++-------------- src/NetworkManagerDevice.h | 11 +- src/NetworkManagerDevicePrivate.h | 2 - src/NetworkManagerMain.h | 15 +- src/NetworkManagerPolicy.c | 237 ++++----- src/NetworkManagerPolicy.h | 11 +- src/NetworkManagerSystem.c | 1 - src/NetworkManagerWireless.c | 113 ++++- src/NetworkManagerWireless.h | 16 +- src/autoip.c | 1 - src/backends/NetworkManagerGentoo.c | 8 +- 16 files changed, 831 insertions(+), 666 deletions(-) diff --git a/ChangeLog b/ChangeLog index e0ba93d839..62afbb58c2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,33 @@ +2005-01-07 Dan Williams + + * dhcpcd/client.c + - Rework the DHCP client code to be much less chatty when + it receives non-DHCP UDP packets during the DHCP run + (reported by and preliminary patches from Bill Moss) + + * Move wireless scanning to a separate thread. This thread forwards the + results to the main thread when done where they are integrated into + the device's access point lists. This keeps the main thread (which + does all the DBUS communication) from being blocked for long periods + of time by wireless scanning. + + * Make state modification an idle routine in the main loop, and trigger + state changes rather than polling for them. + + * src/backends/NetworkManagerGentoo.c + - Fix up invalid C90 code (reported by Christoph Ruessler) + + * src/NetworkManagerDevice.c + - Revert IPv6 patch for wired devices from 2004-12-22 for + router advertisements, causing problems and infinite loop + during "best" device determination due to link going up/down + (reported by Bill Moss) + + Apply patch from Peter Jones + * src/NetworkManagerDevice.c + - Shortcut for link-checking for ipw2x00 cards + - Split out association check into separate routine + 2004-01-05 Colin Walters * named/named.conf: Add PID_FILE. diff --git a/dhcpcd/client.c b/dhcpcd/client.c index ba050934ff..8bbda17214 100644 --- a/dhcpcd/client.c +++ b/dhcpcd/client.c @@ -408,21 +408,26 @@ int verify_checksum(void * buf, int length, void * buf2, int length2) /* "timeout" should be the future point in time when we wish to stop * checking for data on the socket. */ -int peekfd (dhcp_interface *iface, int sk, struct timeval *timeout) +int peekfd (dhcp_interface *iface, int sk, int min_data, struct timeval *end_time) { struct timeval diff; struct timeval now; + int recv_data_len = 0; + char ethPacket[ETH_FRAME_LEN]; + + if (min_data < 1) + return RET_DHCP_ERROR; /* Wake up each second to check whether or not we've been told - * to stop with iface->cease and check our timeout. + * to stop with iface->cease and check our end time. */ gettimeofday (&now, NULL); - syslog (LOG_INFO, "DHCP waiting for data, overall timeout = {%ds, %dus}\n", (int)timeout->tv_sec, (int)timeout->tv_usec); - while (timeval_subtract (&diff, timeout, &now) == 0) + syslog (LOG_INFO, "DHCP waiting for data, overall end_time = {%ds, %dus}\n", (int)end_time->tv_sec, (int)end_time->tv_usec); + while ((timeval_subtract (&diff, end_time, &now) == 0) && !iface->cease && (recv_data_len < min_data)) { fd_set fs; struct timeval wait = {1, 0}; - syslog (LOG_INFO, "DHCP waiting for data, remaining timeout = {%ds, %dus}\n", (int)diff.tv_sec, (int)diff.tv_usec); + syslog (LOG_INFO, "DHCP waiting for data of minimum size %d, remaining timeout = {%ds, %dus}\n", min_data, (int)diff.tv_sec, (int)diff.tv_usec); FD_ZERO (&fs); FD_SET (sk, &fs); @@ -430,11 +435,20 @@ int peekfd (dhcp_interface *iface, int sk, struct timeval *timeout) if (select (sk+1, &fs, NULL, NULL, &wait) == -1) return RET_DHCP_ERROR; if (FD_ISSET(sk, &fs)) - return RET_DHCP_SUCCESS; - if (iface->cease) - return RET_DHCP_CEASED; + { + /* Get length of data waiting on the socket */ + recv_data_len = recvfrom (sk, ethPacket, sizeof (ethPacket), MSG_DONTWAIT | MSG_PEEK, 0, NULL); + if ((recv_data_len == -1) && (errno != EAGAIN)) + return RET_DHCP_ERROR; /* Return on fatal errors */ + } gettimeofday (&now, NULL); }; + + if (iface->cease) + return RET_DHCP_CEASED; + else if (recv_data_len >= min_data) + return RET_DHCP_SUCCESS; + return RET_DHCP_TIMEOUT; } /*****************************************************************************/ @@ -446,7 +460,7 @@ int dhcp_handle_transaction (dhcp_interface *iface, unsigned int expected_reply_ struct sockaddr_in addr; int tries = 0; int err = RET_DHCP_TIMEOUT; - struct timeval recv_timeout, overall_end, diff, current; + struct timeval recv_end, overall_end, diff, current; udpipMessage *udp_send = NULL; if (!dhcp_return) @@ -477,16 +491,16 @@ int dhcp_handle_transaction (dhcp_interface *iface, unsigned int expected_reply_ do { udpipMessage *udp_msg_recv = NULL; - struct iphdr *ip_hdr; + struct iphdr *ip_hdr = NULL; struct udphdr *udp_hdr; char *tmp_ip; dhcpMessage *dhcp_msg_recv = NULL; int reply_type = -1; char foobuf[512]; struct sockaddr_ll server_hw_addr; - int o; - char ethPacket[ETH_FRAME_LEN]; - int len; + int data_good = 0; + int min_data_len = (sizeof (struct iphdr) + sizeof (struct udphdr)); + if (iface->cease) goto out; @@ -532,114 +546,106 @@ int dhcp_handle_transaction (dhcp_interface *iface, unsigned int expected_reply_ * clamp the receive timeout to overall_end. */ tries++; - gettimeofday (&recv_timeout, NULL); - recv_timeout.tv_sec += (tries * DHCP_INITIAL_RTO); - recv_timeout.tv_usec += (random () % 200000); - if (timeval_subtract (&diff, &overall_end, &recv_timeout) != 0) - memcpy (&recv_timeout, &overall_end, sizeof (struct timeval)); + gettimeofday (&recv_end, NULL); + recv_end.tv_sec += (tries * DHCP_INITIAL_RTO); + recv_end.tv_usec += (random () % 200000); + /* Clamp recv_end to overall_end if its greater than overall_end */ + if (timeval_subtract (&diff, &overall_end, &recv_end) != 0) + memcpy (&recv_end, &overall_end, sizeof (struct timeval)); - /* Wait for some kind of data to appear on the socket */ - syslog (LOG_INFO, "DHCP: Waiting for reply..."); - if ((err = peekfd (iface, recv_sk, &recv_timeout)) != RET_DHCP_SUCCESS) + /* Packet receive loop */ + data_good = 0; + while ((timeval_subtract (&diff, &overall_end, &recv_end) == 0) && !data_good) { - if (err == RET_DHCP_TIMEOUT) + int len; + int o; + char ethPacket[ETH_FRAME_LEN]; + + /* Wait for some kind of data to appear on the socket */ + syslog (LOG_INFO, "DHCP: Waiting for reply..."); + if ((err = peekfd (iface, recv_sk, min_data_len, &recv_end)) != RET_DHCP_SUCCESS) + { + if (err == RET_DHCP_TIMEOUT) + break; + goto out; + } + syslog (LOG_INFO, "DHCP: Got some data to check for reply packet."); + + /* Ok, we allegedly have the data we need, so grab it from the queue */ + o = sizeof (struct sockaddr_ll); + len = recvfrom (recv_sk, pkt_recv, ETH_FRAME_LEN, 0, (struct sockaddr *)&server_hw_addr, &o); + syslog (LOG_INFO, "DHCP: actual data length was %d", len); + if (len < (sizeof (struct iphdr) + sizeof (struct udphdr))) + { + syslog (LOG_INFO, "DHCP: Data length failed minimum length check (should be %d, got %d)", (sizeof (struct iphdr) + sizeof (struct udphdr)), len); continue; - goto out; - } - syslog (LOG_INFO, "DHCP: Got some data to check for reply packet."); - - /* Peek from the data until we get a full amount or a timeout has occurred */ - memset (pkt_recv, 0, ETH_FRAME_LEN); - o = sizeof (struct sockaddr_ll); - do - { - o = sizeof (server_hw_addr); - len = recvfrom (recv_sk, ethPacket, sizeof (ethPacket), MSG_DONTWAIT | MSG_PEEK, (struct sockaddr *)&server_hw_addr, &o); - if (iface->cease || ((len == -1) && (errno != EAGAIN)) || (len == 0)) - { - err = iface->cease ? RET_DHCP_CEASED : RET_DHCP_ERROR; - goto out; } - /* Return if we've exceeded our timeout */ - gettimeofday (¤t, NULL); - if (timeval_subtract (&diff, &overall_end, ¤t) != 0) + ip_hdr = (struct iphdr *) pkt_recv; + if (!verify_checksum (NULL, 0, ip_hdr, sizeof (struct iphdr))) { - err = RET_DHCP_TIMEOUT; - goto out; + syslog (LOG_INFO, "DHCP: Reply message had bad IP checksum, won't use it."); + continue; } - syslog (LOG_INFO, "DHCP: Received data of len %d, looking for at least %d", len, (sizeof (struct iphdr) + sizeof (struct udphdr))); - } while (len < (sizeof (struct iphdr) + sizeof (struct udphdr))); - /* Ok, we allegedly have the data we need, so grab it from the queue */ - o = sizeof (struct sockaddr_ll); - len = recvfrom (recv_sk, pkt_recv, ETH_FRAME_LEN, 0, (struct sockaddr *)&server_hw_addr, &o); - syslog (LOG_INFO, "DHCP: actual data length was %d", len); - if (len < (sizeof (struct iphdr) + sizeof (struct udphdr))) - { - syslog (LOG_INFO, "DHCP: Data length failed minimum length check (should be %d, got %d)", (sizeof (struct iphdr) + sizeof (struct udphdr)), len); - continue; + if (ntohs (ip_hdr->tot_len) > len) + { + syslog (LOG_INFO, "DHCP: Reply message had mismatch in length (IP header said %d, packet was really %d), won't use it.", ntohs (ip_hdr->tot_len), len); + continue; + } + len = ntohs (ip_hdr->tot_len); + + if (ip_hdr->protocol != IPPROTO_UDP) + { + syslog (LOG_INFO, "DHCP: Reply message was not not UDP (ip_hdr->protocol = %d, IPPROTO_UDP = %d), won't use it.", ip_hdr->protocol, IPPROTO_UDP); + continue; + } + + udp_hdr = (struct udphdr *) (pkt_recv + sizeof (struct iphdr)); + if (ntohs (udp_hdr->source) != DHCP_SERVER_PORT) + { + syslog (LOG_INFO, "DHCP: Reply message's source port was not the DHCP server port number, won't use it."); + continue; + } + if (ntohs (udp_hdr->dest) != DHCP_CLIENT_PORT) + { + syslog (LOG_INFO, "DHCP: Reply message's destination port was not the DHCP client port number, won't use it."); + continue; + } + + /* Ok, packet appears to be OK */ + /* Ensure DHCP packet is 0xFF terminated, which isn't the case on Cisco 800 series ISDN router */ + dhcp_msg_recv = malloc (sizeof (dhcpMessage)); + memset (dhcp_msg_recv, 0xFF, sizeof (dhcpMessage)); + memcpy (dhcp_msg_recv, (char *) udp_hdr + sizeof (struct udphdr), len - sizeof (struct iphdr) - sizeof (struct udphdr)); + + if (dhcp_msg_recv->xid != iface->xid) + { + syslog (LOG_INFO, "DHCP: Reply message's XID does not match expected XID (message %d, expected %d), won't use it.", dhcp_msg_recv->xid, iface->xid); + free (dhcp_msg_recv); + continue; + } + + if (dhcp_msg_recv->htype != ARPHRD_ETHER) + { + if (DebugFlag) + syslog (LOG_DEBUG, "DHCP: Reply message's header type was not ARPHRD_ETHER (messgae %d, expected %d), won't use it.", dhcp_msg_recv->htype, ARPHRD_ETHER); + free (dhcp_msg_recv); + continue; + } + + if (dhcp_msg_recv->op != DHCP_BOOTREPLY) + { + syslog (LOG_INFO, "DHCP: Reply message was not a bootp/DHCP reply, won't use it."); + free (dhcp_msg_recv); + continue; + } + + data_good = 1; } - ip_hdr = (struct iphdr *) pkt_recv; - if (!verify_checksum (NULL, 0, ip_hdr, sizeof (struct iphdr))) - { - syslog (LOG_INFO, "DHCP: Reply message had bad IP checksum, won't use it."); + if (!data_good) continue; - } - - if (ntohs (ip_hdr->tot_len) > len) - { - syslog (LOG_INFO, "DHCP: Reply message had mismatch in length (IP header said %d, packet was really %d), won't use it.", ntohs (ip_hdr->tot_len), len); - continue; - } - len = ntohs (ip_hdr->tot_len); - - if (ip_hdr->protocol != IPPROTO_UDP) - { - syslog (LOG_INFO, "DHCP: Reply message was not not UDP (ip_hdr->protocol = %d, IPPROTO_UDP = %d), won't use it.", ip_hdr->protocol, IPPROTO_UDP); - continue; - } - - udp_hdr = (struct udphdr *) (pkt_recv + sizeof (struct iphdr)); - if (ntohs (udp_hdr->source) != DHCP_SERVER_PORT) - { - syslog (LOG_INFO, "DHCP: Reply message's source port was not the DHCP server port number, won't use it."); - continue; - } - if (ntohs (udp_hdr->dest) != DHCP_CLIENT_PORT) - { - syslog (LOG_INFO, "DHCP: Reply message's destination port was not the DHCP client port number, won't use it."); - continue; - } - - /* Ok, packet appears to be OK */ - /* Ensure DHCP packet is 0xFF terminated, which isn't the case on Cisco 800 series ISDN router */ - dhcp_msg_recv = malloc (sizeof (dhcpMessage)); - memset (dhcp_msg_recv, 0xFF, sizeof (dhcpMessage)); - memcpy (dhcp_msg_recv, (char *) udp_hdr + sizeof (struct udphdr), len - sizeof (struct iphdr) - sizeof (struct udphdr)); - - if (dhcp_msg_recv->xid != iface->xid) - { - syslog (LOG_INFO, "DHCP: Reply message's XID does not match expected XID (message %d, expected %d), won't use it.", dhcp_msg_recv->xid, iface->xid); - free (dhcp_msg_recv); - continue; - } - - if (dhcp_msg_recv->htype != ARPHRD_ETHER) - { - if (DebugFlag) - syslog (LOG_DEBUG, "DHCP: Reply message's header type was not ARPHRD_ETHER (messgae %d, expected %d), won't use it.", dhcp_msg_recv->htype, ARPHRD_ETHER); - free (dhcp_msg_recv); - continue; - } - - if (dhcp_msg_recv->op != DHCP_BOOTREPLY) - { - syslog (LOG_INFO, "DHCP: Reply message was not a bootp/DHCP reply, won't use it."); - free (dhcp_msg_recv); - continue; - } /* Clear out all data remaining on the interface in preparation for another broadcast if needed */ while ((iface->foo_sk > 0) && recvfrom (iface->foo_sk, (void *)foobuf, sizeof (foobuf), 0, NULL, NULL) != -1); diff --git a/src/Makefile.am b/src/Makefile.am index 7d67fc21b4..88dbd7d580 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -3,6 +3,7 @@ INCLUDES = -I${top_srcdir} -I../named AM_CPPFLAGS = \ $(NM_CFLAGS) \ $(OPENSSL_CFLAGS) \ + -g \ -Wall \ -DDBUS_API_SUBJECT_TO_CHANGE \ -DG_DISABLE_DEPRECATED \ diff --git a/src/NetworkManager.c b/src/NetworkManager.c index 571a64f714..40d1ac974b 100644 --- a/src/NetworkManager.c +++ b/src/NetworkManager.c @@ -316,7 +316,7 @@ static void nm_hal_device_property_modified (LibHalContext *ctx, const char *udi 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 ); + //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"))) @@ -333,6 +333,7 @@ static void nm_hal_device_property_modified (LibHalContext *ctx, const char *udi 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, FALSE); /* If the currently active device is locked and wireless, and the wired @@ -496,6 +497,10 @@ static NMData *nm_data_new (gboolean enable_test_devices) data->main_context = g_main_context_new (); data->main_loop = g_main_loop_new (data->main_context, FALSE); + data->wscan_ctx = g_main_context_new (); + data->wscan_loop = g_main_loop_new (data->wscan_ctx, FALSE); + data->wscan_thread_done = FALSE; + if (pipe(data->sigterm_pipe) < 0) { syslog (LOG_CRIT, "Couldn't create pipe: %s", g_strerror (errno)); @@ -525,8 +530,7 @@ static NMData *nm_data_new (gboolean enable_test_devices) /* Initialize the device list mutex to protect additions/deletions to it. */ data->dev_list_mutex = g_mutex_new (); data->user_device_mutex = g_mutex_new (); - data->state_modified_mutex = g_mutex_new (); - if (!data->dev_list_mutex || !data->user_device_mutex || !data->state_modified_mutex) + if (!data->dev_list_mutex || !data->user_device_mutex) { nm_data_free (data); syslog (LOG_ERR, "Could not initialize data structure locks."); @@ -536,7 +540,6 @@ static NMData *nm_data_new (gboolean enable_test_devices) /* Initialize the access point lists */ data->allowed_ap_list = nm_ap_list_new (NETWORK_TYPE_ALLOWED); data->invalid_ap_list = nm_ap_list_new (NETWORK_TYPE_INVALID); - if (!data->allowed_ap_list || !data->invalid_ap_list) { nm_data_free (data); @@ -544,10 +547,13 @@ static NMData *nm_data_new (gboolean enable_test_devices) return (NULL); } - data->state_modified = TRUE; + data->state_modified_idle_id = 0; + data->enable_test_devices = enable_test_devices; data->starting_up = TRUE; + nm_data_mark_state_changed (data); + return (data); } @@ -570,13 +576,12 @@ static void nm_data_free (NMData *data) g_mutex_free (data->dev_list_mutex); g_mutex_free (data->user_device_mutex); - g_mutex_free (data->state_modified_mutex); nm_ap_list_unref (data->allowed_ap_list); nm_ap_list_unref (data->invalid_ap_list); - g_object_unref (data->main_loop); - g_object_unref (data->main_context); + g_main_loop_unref (data->main_loop); + g_main_context_unref (data->main_context); memset (data, 0, sizeof (NMData)); } @@ -585,16 +590,25 @@ static void nm_data_free (NMData *data) /* * nm_data_mark_state_changed * - * Notify our timeout that the networking state has changed in some way. + * Queue up an idle handler to deal with state changes. * */ void nm_data_mark_state_changed (NMData *data) { + static GStaticMutex mutex = G_STATIC_MUTEX_INIT; + g_return_if_fail (data != NULL); - g_mutex_lock (data->state_modified_mutex); - data->state_modified = TRUE; - g_mutex_unlock (data->state_modified_mutex); + g_static_mutex_lock (&mutex); + if (data->state_modified_idle_id == 0) + { + GSource *source = g_idle_source_new (); + + g_source_set_callback (source, nm_state_modification_monitor, data, NULL); + data->state_modified_idle_id = g_source_attach (source, data->main_context); + g_source_unref (source); + } + g_static_mutex_unlock (&mutex); } static void sigterm_handler (int signum) @@ -641,10 +655,11 @@ static void nm_print_usage (void) int main( int argc, char *argv[] ) { LibHalContext *ctx = NULL; - guint link_source_id, policy_source_id, wscan_source_id; - GSource *link_source, *policy_source, *wscan_source; + guint link_source_id; + GSource *link_source; gboolean become_daemon = TRUE; gboolean enable_test_devices = FALSE; + GError *error = NULL; if ((int)getuid() != 0) { @@ -762,34 +777,33 @@ int main( int argc, char *argv[] ) 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); - /* Another watch function which handles networking state changes and applies - * the correct policy on a change. - */ - policy_source = g_timeout_source_new (500); - g_source_set_callback (policy_source, nm_state_modification_monitor, nm_data, NULL); - policy_source_id = g_source_attach (policy_source, nm_data->main_context); - - /* Keep a current list of access points */ - wscan_source = g_timeout_source_new (10000); - g_source_set_callback (wscan_source, nm_wireless_scan_monitor, nm_data, NULL); - wscan_source_id = g_source_attach (wscan_source, nm_data->main_context); - if (become_daemon && daemon (0, 0) < 0) { - syslog( LOG_ERR, "NetworkManager could not daemonize. errno = %d", errno ); + syslog (LOG_ERR, "NetworkManager could not daemonize. errno = %d", errno); exit (1); } - syslog (LOG_NOTICE, "running mainloop..."); - /* Wheeee!!! */ - g_main_loop_run (nm_data->main_loop); + /* Start the wireless scanning thread and timeout */ + if (!g_thread_create (nm_wireless_scan_worker, nm_data, FALSE, &error)) + { + syslog (LOG_CRIT, "Could not start wireless scan worker thread. Exiting. (error: %s)", error ? error->message : "unknown"); + if (error) + g_error_free (error); + exit (1); + } + /* Wheeee!!! */ + syslog (LOG_NOTICE, "running mainloop..."); + g_main_loop_run (nm_data->main_loop); syslog (LOG_NOTICE, "exiting..."); /* Kill the watch functions */ g_source_remove (link_source_id); - g_source_remove (policy_source_id); - g_source_remove (wscan_source_id); + + /* Quit and wait for the scan thread */ + g_main_loop_quit (nm_data->wscan_loop); + while (nm_data->wscan_thread_done == FALSE) + g_usleep (100); /* Cleanup */ if (hal_shutdown (nm_data->hal_ctx) != 0) diff --git a/src/NetworkManagerDbus.c b/src/NetworkManagerDbus.c index b62129df8b..2301755837 100644 --- a/src/NetworkManagerDbus.c +++ b/src/NetworkManagerDbus.c @@ -1270,7 +1270,6 @@ static DBusHandlerResult nm_dbus_nmi_filter (DBusConnection *connection, DBusMes { data->update_ap_lists = TRUE; data->info_daemon_avail = TRUE; - data->notify_device_support = TRUE; nm_data_mark_state_changed (data); } /* Don't set handled = TRUE since other filter functions on this dbus connection @@ -1288,7 +1287,6 @@ static DBusHandlerResult nm_dbus_nmi_filter (DBusConnection *connection, DBusMes { data->update_ap_lists = TRUE; data->info_daemon_avail = FALSE; - data->notify_device_support = TRUE; nm_data_mark_state_changed (data); } /* Don't set handled = TRUE since other filter functions on this dbus connection diff --git a/src/NetworkManagerDevice.c b/src/NetworkManagerDevice.c index 18a4ac7596..ac44cb4826 100644 --- a/src/NetworkManagerDevice.c +++ b/src/NetworkManagerDevice.c @@ -271,10 +271,7 @@ NMDevice *nm_device_new (const char *iface, const char *udi, gboolean test_dev, } dev->options.wireless.supports_wireless_scan = nm_device_supports_wireless_scan (dev); - /* Perform an initial wireless scan */ nm_device_set_mode (dev, NETWORK_MODE_INFRA); - nm_device_do_wireless_scan (dev); - nm_device_update_best_ap (dev); if ((sk = iw_sockets_open ()) >= 0) { @@ -311,22 +308,32 @@ void nm_device_ref (NMDevice *dev) dev->refcount++; } -void nm_device_unref (NMDevice *dev) +/* + * nm_device_unref + * + * Decreases the refcount on a device by 1, and if the refcount reaches 0, + * deallocates memory used by the device. + * + * Returns: FALSE if device was not deallocated + * TRUE if device was deallocated + */ +gboolean nm_device_unref (NMDevice *dev) { - g_return_if_fail (dev != NULL); + gboolean deleted = FALSE; + + g_return_val_if_fail (dev != NULL, TRUE); dev->refcount--; if (dev->refcount <= 0) { - g_main_loop_quit (dev->loop); - - nm_device_ap_list_clear (dev); - dev->options.wireless.ap_list = NULL; + if (dev->loop) + g_main_loop_quit (dev->loop); - g_free (dev->udi); - g_free (dev->iface); if (nm_device_is_wireless (dev)) { + nm_device_ap_list_clear (dev); + dev->options.wireless.ap_list = NULL; + g_mutex_free (dev->options.wireless.scan_mutex); if (dev->options.wireless.ap_list) nm_ap_list_unref (dev->options.wireless.ap_list); @@ -340,10 +347,27 @@ void nm_device_unref (NMDevice *dev) g_mutex_free (dev->options.wireless.best_ap_mutex); } + g_free (dev->udi); dev->udi = NULL; + g_free (dev->iface); dev->iface = NULL; g_free (dev); + deleted = TRUE; } + + return deleted; +} + + +/* + * nm_device_get_app_data + * + */ +NMData *nm_device_get_app_data (const NMDevice *dev) +{ + g_return_val_if_fail (dev != NULL, FALSE); + + return (dev->app_data); } @@ -497,16 +521,15 @@ gboolean nm_device_get_supports_wireless_scan (NMDevice *dev) /* - * nm_device_wireless_link_active - * - * Gets the link state of a wireless device + * nm_device_wireless_is_associated * + * Figure out whether or not we're associated to an access point */ -static gboolean nm_device_wireless_link_active (NMDevice *dev) +static gboolean nm_device_wireless_is_associated (NMDevice *dev) { - struct iwreq wrq; - int iwlib_socket; - gboolean link = FALSE; + struct iwreq wrq; + int sk; + gboolean associated = FALSE; g_return_val_if_fail (dev != NULL, FALSE); g_return_val_if_fail (dev->app_data != NULL, FALSE); @@ -515,27 +538,62 @@ static gboolean nm_device_wireless_link_active (NMDevice *dev) if (dev->test_device) return (nm_device_get_link_active (dev)); - /* - * For wireless cards, the best indicator of a "link" at this time - * seems to be whether the card has a valid access point MAC address. - * Is there a better way? - */ - if ((iwlib_socket = iw_sockets_open ()) < 0) + if ((sk = iw_sockets_open ()) < 0) return (FALSE); - if (iw_get_ext (iwlib_socket, nm_device_get_iface (dev), SIOCGIWAP, &wrq) >= 0) - { - NMAccessPoint *best_ap; + /* Some cards, for example ipw2x00 cards, can short-circuit the MAC + * address check using this check on IWNAME. Its faster. + */ + if (iw_get_ext (sk, nm_device_get_iface (dev), SIOCGIWNAME, &wrq) >= 0) + if (strcmp(wrq.u.name, "unassociated")) + associated = TRUE; - if ((best_ap = nm_device_get_best_ap (dev))) - { - if ( nm_ethernet_address_is_valid ((struct ether_addr *)(&(wrq.u.ap_addr.sa_data))) - && !nm_device_need_ap_switch (dev)) - link = TRUE; - nm_ap_unref (best_ap); - } + /* + * For all other wireless cards, the best indicator of a "link" at this time + * seems to be whether the card has a valid access point MAC address. + * Is there a better way? Some cards don't work too well with this check, ie + * Lucent WaveLAN. + */ + if (!associated) + if (iw_get_ext (sk, nm_device_get_iface (dev), SIOCGIWAP, &wrq) >= 0) + if (nm_ethernet_address_is_valid ((struct ether_addr *)(&(wrq.u.ap_addr.sa_data)))) + associated = TRUE; + + close (sk); + + return (associated); +} + +/* + * nm_device_wireless_link_active + * + * Gets the link state of a wireless device + * + */ +static gboolean nm_device_wireless_link_active (NMDevice *dev) +{ + gboolean link = FALSE; + NMAccessPoint *best_ap; + + g_return_val_if_fail (dev != NULL, FALSE); + g_return_val_if_fail (dev->app_data != NULL, FALSE); + + /* Test devices have their link state set through DBUS */ + if (dev->test_device) + return (nm_device_get_link_active (dev)); + + if (!nm_device_wireless_is_associated (dev)) + return (FALSE); + + /* If we don't have a "best" ap, we can't logically have a valid link + * that we want to use. + */ + if ((best_ap = nm_device_get_best_ap (dev))) + { + if (!nm_device_need_ap_switch (dev)) + link = TRUE; + nm_ap_unref (best_ap); } - close (iwlib_socket); return (link); } @@ -1429,6 +1487,33 @@ gboolean nm_device_set_mode (NMDevice *dev, const NMNetworkMode mode) } +/* + * nm_device_activation_schedule_finish + * + * Schedule an idle routine in the main thread to finish the activation. + * + */ +void nm_device_activation_schedule_finish (NMDevice *dev, gboolean success) +{ + GSource *source = NULL; + guint source_id = 0; + NMActivationResult *result = NULL; + + g_return_if_fail (dev != NULL); + g_return_if_fail (dev->app_data != NULL); + + result = g_malloc0 (sizeof (NMActivationResult)); + nm_device_ref (dev); /* Ref device for idle handler */ + result->dev = dev; + result->success = success; + + source = g_idle_source_new (); + g_source_set_callback (source, nm_policy_activation_finish, (gpointer)result, NULL); + g_source_attach (source, dev->app_data->main_context); + g_source_unref (source); +} + + /* * nm_device_activation_begin * @@ -1447,21 +1532,13 @@ gboolean nm_device_activation_begin (NMDevice *dev) g_return_val_if_fail (!dev->activating, TRUE); /* Return if activation has already begun */ g_return_val_if_fail (data != NULL, FALSE); - /* Ref the device so it doesn't go away while worker function is active */ - nm_device_ref (dev); - - /* Don't attempt to actually activate if we are just starting NetworkManager and - * we are about to activate a wired device that's already configured. Plays nicer - * with the system when NM is started after a network is already set up. - * - * FIXME: IPv6 here too, and this really should not be here, it should be part of - * the policy, not the device code itself. - */ - if (data->starting_up && nm_device_is_wired (data->active_device) && nm_device_get_ip4_address (data->active_device)) + /* Reset communication flags between worker and main thread */ + dev->activating = TRUE; + dev->quit_activation = FALSE; + if (nm_device_is_wireless (dev)) { - dev->activating = FALSE; - dev->just_activated = TRUE; - return (TRUE); + dev->options.wireless.now_scanning = FALSE; + dev->options.wireless.user_key_received = FALSE; } if (nm_device_get_driver_support_level (dev) == NM_DRIVER_UNSUPPORTED) @@ -1470,21 +1547,28 @@ gboolean nm_device_activation_begin (NMDevice *dev) return (FALSE); } - /* Reset communication flags between worker and main thread */ - dev->activating = TRUE; - dev->just_activated = FALSE; - dev->quit_activation = FALSE; - dev->activation_failed = FALSE; - if (nm_device_is_wireless (dev)) + /* Don't attempt to actually activate if we are just starting NetworkManager and + * we are about to activate a wired device that's already configured. Plays nicer + * with the system when NM is started after a network is already set up. + * + * FIXME: IPv6 here too, and this really should not be here, it should be part of + * the policy, not the device code itself. + */ + if (data->starting_up && nm_device_is_wired (dev) && nm_device_get_ip4_address (dev)) { - dev->options.wireless.now_scanning = FALSE; - dev->options.wireless.user_key_received = FALSE; + dev->activating = FALSE; + nm_device_activation_schedule_finish (dev, TRUE); + return (TRUE); } + /* Ref the device so it doesn't go away while worker function is active */ + nm_device_ref (dev); + if (!g_thread_create (nm_device_activation_worker, dev, FALSE, &error)) { syslog (LOG_CRIT, "nm_device_activation_begin(): could not create activation worker thread."); dev->activating = FALSE; + nm_device_unref (dev); return (FALSE); } @@ -1510,8 +1594,6 @@ static gboolean nm_device_activation_handle_cancel (NMDevice *dev) { syslog (LOG_DEBUG, "nm_device_activation_worker(%s): activation canceled.", nm_device_get_iface (dev)); dev->activating = FALSE; - dev->just_activated = FALSE; - dev->activation_failed = TRUE; dev->quit_activation = FALSE; return (TRUE); } @@ -1683,8 +1765,15 @@ static gboolean AP_NEED_KEY (NMAccessPoint *ap) if ((s = nm_ap_get_enc_key_source (ap))) len = strlen (s); - syslog (LOG_NOTICE, "AP_NEED_KEY: is_enc=%d && (!(key_source=%d) || !(strlen=%d))\n", nm_ap_get_encrypted (ap), - !!nm_ap_get_enc_key_source (ap), len); + if (!nm_ap_get_encrypted (ap)) + syslog (LOG_NOTICE, "AP_NEED_KEY: access point is unencrypted, no key needed."); + else + { + if (!!nm_ap_get_enc_key_source (ap) && len) + syslog (LOG_NOTICE, "AP_NEED_KEY: access point is encrypted, and a key exists. No new key needed."); + else + syslog (LOG_NOTICE, "AP_NEED_KEY: access point is encrypted, but NO valid key exists. New key needed."); + } if (nm_ap_get_encrypted (ap) && (!nm_ap_get_enc_key_source (ap) || !len)) return (TRUE); @@ -1696,7 +1785,7 @@ static gboolean HAVE_LINK (NMDevice *dev) g_return_val_if_fail (dev != NULL, FALSE); g_return_val_if_fail (nm_device_is_wireless (dev), FALSE); - syslog (LOG_NOTICE, "HAVELINK: act=%d\n", nm_device_get_link_active (dev)); + syslog (LOG_NOTICE, "HAVELINK: card appears %s a link to the access point.", nm_device_get_link_active (dev) ? "to have" : "NOT to have"); return (nm_device_get_link_active (dev)); } @@ -1898,13 +1987,6 @@ static gboolean nm_device_activation_configure_ip (NMDevice *dev, gboolean do_on g_return_val_if_fail (dev != NULL, FALSE); nm_system_delete_default_route (); - /* This will assigne an IPv6 address, if a Router ADVertisement Daemon is pressent */ - if (!nm_device_is_wireless (dev)) - { - if (nm_device_is_up (dev)) - nm_device_bring_down (dev); - nm_device_bring_up (dev); - } if (do_only_autoip) { success = nm_device_do_autoip (dev); @@ -1956,9 +2038,9 @@ static gboolean nm_device_activation_configure_ip (NMDevice *dev, gboolean do_on */ static gpointer nm_device_activation_worker (gpointer user_data) { - NMDevice *dev = (NMDevice *)user_data; - gboolean success = FALSE; - GMainContext *context = NULL; + NMDevice *dev = (NMDevice *)user_data; + gboolean success = FALSE; + GMainContext *context = NULL; g_return_val_if_fail (dev != NULL, NULL); g_return_val_if_fail (dev->app_data != NULL, NULL); @@ -2001,27 +2083,23 @@ static gpointer nm_device_activation_worker (gpointer user_data) { syslog (LOG_DEBUG, "Activation (%s) IP configuration/DHCP unsuccessful! Ending activation...\n", nm_device_get_iface (dev)); dev->activating = FALSE; - dev->just_activated = FALSE; - dev->activation_failed = TRUE; dev->quit_activation = FALSE; goto out; } - dev->just_activated = TRUE; dev->activating = FALSE; - dev->activation_failed = FALSE; dev->quit_activation = FALSE; syslog (LOG_DEBUG, "Activation (%s) IP configuration/DHCP successful!\n", nm_device_get_iface (dev)); /* If we were told to quit activation, stop the thread and return */ if (nm_device_activation_handle_cancel (dev)) - { - syslog (LOG_DEBUG, "Activation (%s) told to cancel. Ending activation...\n", nm_device_get_iface (dev)); goto out; - } + + nm_device_activation_schedule_finish (dev, success); syslog (LOG_INFO, "nm_device_activation_worker(%s): device activated", nm_device_get_iface (dev)); + /* Don't need to stick around for devices that use Static IP */ if (!nm_device_config_get_use_dhcp (dev) || !dev->dhcp_iface) goto out; @@ -2053,27 +2131,6 @@ out: } -/* - * nm_device_is_just_activated - * - * Check if the device was just activated successfully or not. If so, clear - * its just_activated flag and return TRUE. If its not activated yet, return FALSE. - * - */ -gboolean nm_device_is_just_activated (NMDevice *dev) -{ - g_return_val_if_fail (dev != NULL, FALSE); - - if (dev->just_activated) - { - dev->just_activated = FALSE; - return (TRUE); - } - - return (FALSE); -} - - /* * nm_device_is_activating * @@ -2102,29 +2159,6 @@ gboolean nm_device_activation_should_cancel (NMDevice *dev) } -/* - * nm_device_did_activation_fail - * - * Return whether or not activation on the device failed (was cancelled or - * activation process encountered and error) - * - */ -gboolean nm_device_did_activation_fail (NMDevice *dev) -{ - g_return_val_if_fail (dev != NULL, FALSE); - - return (dev->activation_failed); -} - - -void nm_device_clear_activation_fail (NMDevice *dev) -{ - g_return_if_fail (dev != NULL); - - dev->activation_failed = FALSE; -} - - /* * nm_device_activation_cancel * @@ -2720,8 +2754,7 @@ gboolean nm_device_wireless_network_exists (NMDevice *dev, const char *network, g_usleep (G_USEC_PER_SEC * nm_device_get_association_pause_value (dev)); nm_device_update_link_active (dev, FALSE); - nm_device_get_ap_address (dev, &addr); - if (nm_ethernet_address_is_valid (&addr) && nm_device_get_essid (dev)) + if (nm_device_wireless_is_associated (dev) && nm_device_get_essid (dev)) { nm_device_get_ap_address (dev, ap_addr); success = TRUE; @@ -2819,221 +2852,7 @@ gboolean nm_device_find_and_use_essid (NMDevice *dev, const char *essid, const c success = TRUE; } - return (success); -} - - -/* - * nm_device_do_normal_scan - * - * Scan for access points on cards that support wireless scanning. - * - */ -static void nm_device_do_normal_scan (NMDevice *dev) -{ - int iwlib_socket; - NMData *data; - - g_return_if_fail (dev != NULL); - g_return_if_fail (dev->app_data != NULL); - - /* Test devices shouldn't get here since we fake the AP list earlier */ - g_return_if_fail (!dev->test_device); - - data = (NMData *)dev->app_data; - - /* Device must be up before we can scan */ - if (!nm_device_is_up (dev)) - nm_device_bring_up (dev); - g_usleep (G_USEC_PER_SEC); - - iwlib_socket = iw_sockets_open (); - if (iwlib_socket >= 0) - { - wireless_scan_head scan_results = { NULL, 0 }; - wireless_scan *tmp_ap; - int err; - NMAccessPointList *old_ap_list = NULL; - NMAccessPointList *new_scan_list = NULL; - NMAccessPointList *earliest_scan = NULL; - gboolean have_blank_essids = FALSE; - NMAPListIter *iter; - NMAccessPoint *artificial_ap; - NMNetworkMode orig_mode = NETWORK_MODE_INFRA; - double orig_freq; - int orig_rate; - - orig_mode = nm_device_get_mode (dev); - orig_freq = nm_device_get_frequency (dev); - orig_rate = nm_device_get_bitrate (dev); - nm_device_set_mode (dev, NETWORK_MODE_INFRA); - - err = iw_scan (iwlib_socket, (char *)nm_device_get_iface (dev), WIRELESS_EXT, &scan_results); - if ((err == -1) && (errno == ENODATA)) - { - /* Card hasn't had time yet to compile full access point list. - * Give it some more time and scan again. If that doesn't work - * give up. Cards that need to scan more channels (Atheros 5212 - * based cards, for example) need more time here. - */ - g_usleep ((G_USEC_PER_SEC * nm_device_get_association_pause_value (dev)) / 2); - err = iw_scan (iwlib_socket, (char *)nm_device_get_iface (dev), WIRELESS_EXT, &scan_results); - if (err == -1) - { - nm_device_set_mode (dev, orig_mode); - nm_device_set_frequency (dev, orig_freq); - nm_device_set_bitrate (dev, orig_rate); - close (iwlib_socket); - return; - } - } - nm_device_set_mode (dev, orig_mode); - nm_device_set_frequency (dev, orig_freq); - nm_device_set_bitrate (dev, orig_rate); - - /* New list for current scan data */ - new_scan_list = nm_ap_list_new (NETWORK_TYPE_DEVICE); - if (!new_scan_list) - { - nm_dispose_scan_results (scan_results.result); - close (iwlib_socket); - return; - } - - /* Shift all previous cached scan results and dispose of the oldest one. */ - earliest_scan = dev->options.wireless.cached_ap_list3; - dev->options.wireless.cached_ap_list3 = dev->options.wireless.cached_ap_list2; - dev->options.wireless.cached_ap_list2 = dev->options.wireless.cached_ap_list1; - dev->options.wireless.cached_ap_list1 = new_scan_list; - - /* Iterate over scan results and pick a "most" preferred access point. */ - tmp_ap = scan_results.result; - while (tmp_ap) - { - /* We need at least an ESSID or a MAC address for each access point */ - if (tmp_ap->b.has_essid || tmp_ap->has_ap_addr) - { - NMAccessPoint *nm_ap = nm_ap_new (); - - /* Copy over info from scan to local structure */ - - /* NOTE: some Cisco products actually broadcast "" as their ESSID when they - * are set to not broadcast it, rather than just broadcasting a blank ESSID. - */ - if ( !tmp_ap->b.has_essid - || (tmp_ap->b.essid && !strlen (tmp_ap->b.essid)) - || (tmp_ap->b.essid && !strcmp (tmp_ap->b.essid, ""))) - { - nm_ap_set_essid (nm_ap, NULL); - have_blank_essids = TRUE; - } - else - nm_ap_set_essid (nm_ap, tmp_ap->b.essid); - - if (tmp_ap->b.has_key && (tmp_ap->b.key_flags & IW_ENCODE_DISABLED)) - nm_ap_set_encrypted (nm_ap, FALSE); - else - nm_ap_set_encrypted (nm_ap, TRUE); - - if (tmp_ap->has_ap_addr) - nm_ap_set_address (nm_ap, (const struct ether_addr *)(tmp_ap->ap_addr.sa_data)); - - if (tmp_ap->b.has_mode) - { - NMNetworkMode mode = NETWORK_MODE_INFRA; - switch (tmp_ap->b.mode) - { - case IW_MODE_INFRA: - mode = NETWORK_MODE_INFRA; - break; - case IW_MODE_ADHOC: - mode = NETWORK_MODE_ADHOC; - break; - default: - mode = NETWORK_MODE_INFRA; - break; - } - nm_ap_set_mode (nm_ap, mode); - } - else - nm_ap_set_mode (nm_ap, NETWORK_MODE_INFRA); - - nm_ap_set_strength (nm_ap, nm_wireless_qual_to_percent (dev, &(tmp_ap->stats.qual))); - - if (tmp_ap->b.has_freq) - nm_ap_set_freq (nm_ap, tmp_ap->b.freq); - - /* Add the AP to the device's AP list */ - nm_ap_list_append_ap (dev->options.wireless.cached_ap_list1, nm_ap); - nm_ap_unref (nm_ap); - } - tmp_ap = tmp_ap->next; - } - nm_dispose_scan_results (scan_results.result); - close (iwlib_socket); - - /* Compose the current access point list for the card based on the past two scans. This - * is to achieve some stability in the list, since cards don't necessarily return the same - * access point list each scan even if you are standing in the same place. - */ - old_ap_list = nm_device_ap_list_get (dev); - dev->options.wireless.ap_list = nm_ap_list_combine (dev->options.wireless.cached_ap_list1, dev->options.wireless.cached_ap_list2); - - /* If any blank ESSID networks were detected in the current scan, try to match their - * AP MAC address with existing ones in previous scans, and if we get a match, copy the - * ESSID over to the newest scan list. This enures that we keep the known ESSID for that - * base station around as long as possible, which allows nm_device_update_best_ap() to do - * its job when the user wanted us to connect to a non-broadcasting network. - */ - if (have_blank_essids) - { - nm_ap_list_copy_essids_by_address (nm_device_ap_list_get (dev), old_ap_list); - nm_ap_list_copy_essids_by_address (nm_device_ap_list_get (dev), dev->app_data->allowed_ap_list); - } - - /* Once we have the list, copy in any relevant information from our Allowed list. */ - nm_ap_list_copy_properties (nm_device_ap_list_get (dev), dev->app_data->allowed_ap_list); - - /* Furthermore, if we have any "artificial" access points, ie ones that exist but don't show up in - * the scan for some reason, copy those over if we are associated with that access point right now. - * Some Cisco cards don't report non-ESSID-broadcasting access points in their scans even though - * the card associates with that AP just fine. - */ - if (old_ap_list && (iter = nm_ap_list_iter_new (old_ap_list))) - { - char *essid = nm_device_get_essid (dev); - - while (essid && (artificial_ap = nm_ap_list_iter_next (iter))) - { - /* Copy over the artificial AP from the old list to the new one if - * its the AP the card is currently associated with. - */ - if ( nm_ap_get_essid (artificial_ap) - && !strcmp (essid, nm_ap_get_essid (artificial_ap)) - && nm_ap_get_artificial (artificial_ap)) - nm_ap_list_append_ap (nm_device_ap_list_get (dev), artificial_ap); - } - nm_ap_list_iter_free (iter); - } - if (old_ap_list) - nm_ap_list_unref (old_ap_list); - - /* Generate the "old" list from the 3rd and 4th oldest scans we've done */ - old_ap_list = nm_ap_list_combine (dev->options.wireless.cached_ap_list3, earliest_scan); - - /* Don't need the 4th scan around any more */ - if (earliest_scan) - nm_ap_list_unref (earliest_scan); - - /* Now do a diff of the old and new networks that we can see, and - * signal any changes over dbus. - */ - nm_ap_list_diff (dev->app_data, dev, old_ap_list, nm_device_ap_list_get (dev)); - if (old_ap_list) - nm_ap_list_unref (old_ap_list); - } - else - syslog (LOG_ERR, "nm_device_do_normal_scan() could not get a control socket for the wireless card %s.", nm_device_get_iface (dev) ); + return success; } @@ -3176,7 +2995,175 @@ static void nm_device_fake_ap_list (NMDevice *dev) nm_ap_list_diff (dev->app_data, dev, old_ap_list, nm_device_ap_list_get (dev)); if (old_ap_list) nm_ap_list_unref (old_ap_list); +} + +/* + * nm_device_process_scan_results + * + * Process results of an iwscan() into our own AP lists + * + */ +void nm_device_process_scan_results (NMDevice *dev, wireless_scan_head *results) +{ + wireless_scan *tmp_ap; + NMAccessPointList *old_ap_list = NULL; + NMAccessPointList *new_scan_list = NULL; + NMAccessPointList *earliest_scan = NULL; + gboolean have_blank_essids = FALSE; + NMAPListIter *iter; + NMAccessPoint *artificial_ap; + + g_return_if_fail (dev != NULL); + + if (!results) + return; + + /* Test devices get their info faked */ + if (dev->test_device) + { + nm_device_fake_ap_list (dev); + return; + } + + /* Devices that don't support scanning have their pseudo-scanning done in + * the main thread anyway. + */ + if (!nm_device_supports_wireless_scan (dev)) + { + nm_device_do_pseudo_scan (dev); + return; + } + + /* Shift all previous cached scan results and dispose of the oldest one. */ + earliest_scan = dev->options.wireless.cached_ap_list3; + dev->options.wireless.cached_ap_list3 = dev->options.wireless.cached_ap_list2; + dev->options.wireless.cached_ap_list2 = dev->options.wireless.cached_ap_list1; + dev->options.wireless.cached_ap_list1 = nm_ap_list_new (NETWORK_TYPE_DEVICE); + + /* Iterate over scan results and pick a "most" preferred access point. */ + tmp_ap = results->result; + while (tmp_ap) + { + /* We need at least an ESSID or a MAC address for each access point */ + if (tmp_ap->b.has_essid || tmp_ap->has_ap_addr) + { + NMAccessPoint *nm_ap = nm_ap_new (); + + /* Copy over info from scan to local structure */ + + /* ipw2x00 drivers fill in an essid of "" if they think the access point + * is hiding its MAC address. Sigh. + */ + if ( !tmp_ap->b.has_essid + || (tmp_ap->b.essid && !strlen (tmp_ap->b.essid)) + || (tmp_ap->b.essid && !strcmp (tmp_ap->b.essid, ""))) + { + nm_ap_set_essid (nm_ap, NULL); + have_blank_essids = TRUE; + } + else + nm_ap_set_essid (nm_ap, tmp_ap->b.essid); + + if (tmp_ap->b.has_key && (tmp_ap->b.key_flags & IW_ENCODE_DISABLED)) + nm_ap_set_encrypted (nm_ap, FALSE); + else + nm_ap_set_encrypted (nm_ap, TRUE); + + if (tmp_ap->has_ap_addr) + nm_ap_set_address (nm_ap, (const struct ether_addr *)(tmp_ap->ap_addr.sa_data)); + + if (tmp_ap->b.has_mode) + { + NMNetworkMode mode = NETWORK_MODE_INFRA; + switch (tmp_ap->b.mode) + { + case IW_MODE_INFRA: + mode = NETWORK_MODE_INFRA; + break; + case IW_MODE_ADHOC: + mode = NETWORK_MODE_ADHOC; + break; + default: + mode = NETWORK_MODE_INFRA; + break; + } + nm_ap_set_mode (nm_ap, mode); + } + else + nm_ap_set_mode (nm_ap, NETWORK_MODE_INFRA); + + nm_ap_set_strength (nm_ap, nm_wireless_qual_to_percent (dev, &(tmp_ap->stats.qual))); + + if (tmp_ap->b.has_freq) + nm_ap_set_freq (nm_ap, tmp_ap->b.freq); + + /* Add the AP to the device's AP list */ + nm_ap_list_append_ap (dev->options.wireless.cached_ap_list1, nm_ap); + nm_ap_unref (nm_ap); + } + tmp_ap = tmp_ap->next; + } + + /* Compose the current access point list for the card based on the past two scans. This + * is to achieve some stability in the list, since cards don't necessarily return the same + * access point list each scan even if you are standing in the same place. + */ + old_ap_list = nm_device_ap_list_get (dev); + dev->options.wireless.ap_list = nm_ap_list_combine (dev->options.wireless.cached_ap_list1, dev->options.wireless.cached_ap_list2); + + /* If any blank ESSID networks were detected in the current scan, try to match their + * AP MAC address with existing ones in previous scans, and if we get a match, copy the + * ESSID over to the newest scan list. This enures that we keep the known ESSID for that + * base station around as long as possible, which allows nm_device_update_best_ap() to do + * its job when the user wanted us to connect to a non-broadcasting network. + */ + if (have_blank_essids) + { + nm_ap_list_copy_essids_by_address (nm_device_ap_list_get (dev), old_ap_list); + nm_ap_list_copy_essids_by_address (nm_device_ap_list_get (dev), dev->app_data->allowed_ap_list); + } + + /* Once we have the list, copy in any relevant information from our Allowed list. */ + nm_ap_list_copy_properties (nm_device_ap_list_get (dev), dev->app_data->allowed_ap_list); + + /* Furthermore, if we have any "artificial" access points, ie ones that exist but don't show up in + * the scan for some reason, copy those over if we are associated with that access point right now. + * Some Cisco cards don't report non-ESSID-broadcasting access points in their scans even though + * the card associates with that AP just fine. + */ + if (old_ap_list && (iter = nm_ap_list_iter_new (old_ap_list))) + { + char *essid = nm_device_get_essid (dev); + + while (essid && (artificial_ap = nm_ap_list_iter_next (iter))) + { + /* Copy over the artificial AP from the old list to the new one if + * its the AP the card is currently associated with. + */ + if ( nm_ap_get_essid (artificial_ap) + && !strcmp (essid, nm_ap_get_essid (artificial_ap)) + && nm_ap_get_artificial (artificial_ap)) + nm_ap_list_append_ap (nm_device_ap_list_get (dev), artificial_ap); + } + nm_ap_list_iter_free (iter); + } + if (old_ap_list) + nm_ap_list_unref (old_ap_list); + + /* Generate the "old" list from the 3rd and 4th oldest scans we've done */ + old_ap_list = nm_ap_list_combine (dev->options.wireless.cached_ap_list3, earliest_scan); + + /* Don't need the 4th scan around any more */ + if (earliest_scan) + nm_ap_list_unref (earliest_scan); + + /* Now do a diff of the old and new networks that we can see, and + * signal any changes over dbus. + */ + nm_ap_list_diff (dev->app_data, dev, old_ap_list, nm_device_ap_list_get (dev)); + if (old_ap_list) + nm_ap_list_unref (old_ap_list); } @@ -3186,54 +3173,62 @@ static void nm_device_fake_ap_list (NMDevice *dev) * Get a list of access points this device can see. * */ -void nm_device_do_wireless_scan (NMDevice *dev) +void nm_device_do_wireless_scan (NMDevice *dev, wireless_scan_head *results) { - NMAccessPoint *best_ap = NULL; - gboolean do_scan = TRUE; + int sk; g_return_if_fail (dev != NULL); - g_return_if_fail (dev->app_data != NULL); - g_return_if_fail (nm_device_is_wireless (dev)); - if (!nm_try_acquire_mutex (dev->options.wireless.scan_mutex, __FUNCTION__)) + /* We don't really scan on test devices or devices that don't have scanning support */ + if (dev->test_device || !nm_device_supports_wireless_scan (dev)) return; - /* Compose a fake list of access points */ - if (dev->test_device) + /* Grab the scan mutex */ + if (nm_try_acquire_mutex (dev->options.wireless.scan_mutex, __FUNCTION__)) { - nm_device_fake_ap_list (dev); - nm_unlock_mutex (dev->options.wireless.scan_mutex, __FUNCTION__); - return; - } + /* Device must be up before we can scan */ + if (!nm_device_is_up (dev)) + nm_device_bring_up (dev); + g_usleep (G_USEC_PER_SEC); - if ((best_ap = nm_device_get_best_ap (dev))) - { - /* Don't pseudo-scan when we already have a connection. */ - if (!nm_device_get_supports_wireless_scan (dev)) + if ((sk = iw_sockets_open ()) >= 0) { - struct ether_addr ap_addr; + wireless_scan *tmp_ap; + int err; + NMNetworkMode orig_mode = NETWORK_MODE_INFRA; + double orig_freq; + int orig_rate; - /* We can't pseudo-scan without switching APs, therefore - * if the card has a valid access point and its an allowed - * access point, don't pseudo-scan for others. + orig_mode = nm_device_get_mode (dev); + orig_freq = nm_device_get_frequency (dev); + orig_rate = nm_device_get_bitrate (dev); + + /* Must be in infrastructure mode during scan, otherwise we don't get a full + * list of scan results. Scanning doesn't work well in Ad-Hoc mode :( */ - nm_device_get_ap_address (dev, &ap_addr); - if ( nm_ethernet_address_is_valid (&ap_addr) - && nm_ap_list_get_ap_by_essid (dev->app_data->allowed_ap_list, nm_device_get_essid (dev))) - do_scan = FALSE; - } - nm_ap_unref (best_ap); - } + nm_device_set_mode (dev, NETWORK_MODE_INFRA); - if (do_scan) - { - if (nm_device_get_supports_wireless_scan (dev)) - nm_device_do_normal_scan (dev); - else - nm_device_do_pseudo_scan (dev); - } + err = iw_scan (sk, (char *)nm_device_get_iface (dev), WIRELESS_EXT, results); + if ((err == -1) && (errno == ENODATA)) + { + /* Card hasn't had time yet to compile full access point list. + * Give it some more time and scan again. If that doesn't work + * give up. Cards that need to scan more channels (Atheros 5212 + * based cards, for example) need more time here. + */ + g_usleep ((G_USEC_PER_SEC * nm_device_get_association_pause_value (dev)) / 2); + err = iw_scan (sk, (char *)nm_device_get_iface (dev), WIRELESS_EXT, results); + if (err == -1) + results->result = NULL; + } + nm_device_set_mode (dev, orig_mode); + nm_device_set_frequency (dev, orig_freq); + nm_device_set_bitrate (dev, orig_rate); - nm_unlock_mutex (dev->options.wireless.scan_mutex, __FUNCTION__); + close (sk); + } + nm_unlock_mutex (dev->options.wireless.scan_mutex, __FUNCTION__); + } } diff --git a/src/NetworkManagerDevice.h b/src/NetworkManagerDevice.h index 4a52263176..8c4cb0db5c 100644 --- a/src/NetworkManagerDevice.h +++ b/src/NetworkManagerDevice.h @@ -23,6 +23,7 @@ #define NETWORK_MANAGER_DEVICE_H #include +#include #include "NetworkManager.h" #include "NetworkManagerMain.h" @@ -41,7 +42,7 @@ NMDevice * nm_device_new (const char *iface, const char *udi, gboolean test_ NMDeviceType test_dev_type, NMData *app_data); void nm_device_ref (NMDevice *dev); -void nm_device_unref (NMDevice *dev); +gboolean nm_device_unref (NMDevice *dev); int nm_device_open_sock (void); @@ -57,6 +58,8 @@ gboolean nm_device_is_wireless (NMDevice *dev); gboolean nm_device_is_wired (NMDevice *dev); /* There is no nm_device_set_iface_type() because that's determined when you set the device's iface */ +NMData * nm_device_get_app_data (const NMDevice *dev); + gboolean nm_device_get_link_active (NMDevice *dev); void nm_device_set_link_active (NMDevice *dev, const gboolean active); void nm_device_update_link_active (NMDevice *dev, gboolean check_mii); @@ -77,7 +80,8 @@ void nm_device_update_hw_address (NMDevice *dev); void nm_device_get_ip6_address (NMDevice *dev); gboolean nm_device_get_supports_wireless_scan (NMDevice *dev); -void nm_device_do_wireless_scan (NMDevice *dev); +void nm_device_process_scan_results (NMDevice *dev, struct wireless_scan_head *results); +void nm_device_do_wireless_scan (NMDevice *dev, struct wireless_scan_head *results); gboolean nm_device_wireless_network_exists (NMDevice *dev, const char *network, const char *key, NMEncKeyType key_type, struct ether_addr *addr, gboolean *encrypted); @@ -106,10 +110,7 @@ void nm_device_set_enc_key (NMDevice *dev, const char *key, NMDeviceAuthMeth gboolean nm_device_activation_begin (NMDevice *dev); void nm_device_activation_cancel (NMDevice *dev); gboolean nm_device_activation_should_cancel (NMDevice *dev); -gboolean nm_device_is_just_activated (NMDevice *dev); gboolean nm_device_is_activating (NMDevice *dev); -gboolean nm_device_did_activation_fail (NMDevice *dev); -void nm_device_clear_activation_fail (NMDevice *dev); gboolean nm_device_deactivate (NMDevice *dev, gboolean just_added); gboolean nm_device_is_scanning (NMDevice *dev); diff --git a/src/NetworkManagerDevicePrivate.h b/src/NetworkManagerDevicePrivate.h index 7936773464..8a6201b672 100644 --- a/src/NetworkManagerDevicePrivate.h +++ b/src/NetworkManagerDevicePrivate.h @@ -114,9 +114,7 @@ struct NMDevice guint rebind_timeout; gboolean activating; /* Set by main thread before beginning activation */ - gboolean just_activated; /* Set by activation thread after successful activation */ gboolean quit_activation; /* Flag to signal activation thread to stop activating */ - gboolean activation_failed; /* Did the activation fail? */ gboolean test_device; gboolean test_device_up; diff --git a/src/NetworkManagerMain.h b/src/NetworkManagerMain.h index 8d7fc76f40..0e5e70a3db 100644 --- a/src/NetworkManagerMain.h +++ b/src/NetworkManagerMain.h @@ -33,13 +33,13 @@ typedef struct NMData { GIOChannel *sigterm_iochannel; - int sigterm_pipe[2]; + int sigterm_pipe[2]; LibHalContext *hal_ctx; NMNamedManager *named; GList *nameserver_ids; /* For now these are global instead of per-device */ - guint domain_search_id; + guint domain_search_id; DBusConnection *dbus_connection; GMainContext *main_context; @@ -48,7 +48,13 @@ typedef struct NMData gboolean enable_test_devices; gboolean starting_up; /* Hack for not taking down an already-set-up wired device when we launch */ - gboolean notify_device_support; + /* Main loop for wireless scanning thread */ + GMainContext *wscan_ctx; + GMainLoop *wscan_loop; + gboolean wscan_thread_done; + + guint state_modified_idle_id; + GSList *dev_list; GMutex *dev_list_mutex; @@ -58,9 +64,6 @@ typedef struct NMData struct NMDevice *user_device; /* Holds a device that the user requests NM to use. */ GMutex *user_device_mutex; - gboolean state_modified; - GMutex *state_modified_mutex; - gboolean update_ap_lists; struct NMAccessPointList *allowed_ap_list; struct NMAccessPointList *invalid_ap_list; diff --git a/src/NetworkManagerPolicy.c b/src/NetworkManagerPolicy.c index 4672cbf543..3c75c35813 100644 --- a/src/NetworkManagerPolicy.c +++ b/src/NetworkManagerPolicy.c @@ -226,154 +226,163 @@ static NMDevice * nm_policy_get_best_device (NMData *data, gboolean *should_lock } +/* + * nm_policy_activation_finish + * + * Finishes up activation by sending out dbus signals, which has to happen + * on the main thread. + * + */ +gboolean nm_policy_activation_finish (gpointer user_data) +{ + NMActivationResult *result = (NMActivationResult *)user_data; + NMDevice *dev = NULL; + NMData *data = NULL; + + g_return_val_if_fail (result != NULL, FALSE); + +fprintf (stderr, "Activation Finish called\n"); + if (!(dev = result->dev)) + goto out; + + if (!(data = nm_device_get_app_data (dev))) + goto out; + + if (result->success) + { + nm_dbus_signal_device_status_change (data->dbus_connection, dev, DEVICE_NOW_ACTIVE); + /* Tell NetworkManagerInfo to store the MAC address of the active device's AP */ + if (nm_device_is_wireless (dev)) + { + struct ether_addr addr; + + nm_device_get_ap_address (dev, &addr); + nm_dbus_add_network_address (data->dbus_connection, NETWORK_TYPE_ALLOWED, nm_device_get_essid (dev), &addr); + } + syslog (LOG_INFO, "nm_state_modification_monitor() activated device %s", nm_device_get_iface (data->active_device)); + } + else + { + nm_dbus_signal_device_status_change (data->dbus_connection, dev, DEVICE_ACTIVATION_FAILED); + if (nm_device_is_wireless (dev)) + { + NMAccessPoint *ap = nm_device_get_best_ap (dev); + if (ap) + { + /* Add the AP to the invalid list and force a best ap update (list takes ownership of ap) */ + nm_ap_list_append_ap (data->invalid_ap_list, ap); + nm_ap_unref (ap); + + nm_device_update_best_ap (dev); + + /* Unref because nm_device_get_best_ap() refs it before returning. */ + nm_ap_unref (ap); + } + syslog (LOG_INFO, "nm_state_modification_monitor() failed to activate device %s (%s)", nm_device_get_iface (dev), ap ? nm_ap_get_essid (ap) : "(none)"); + } + else + syslog (LOG_INFO, "nm_state_modification_monitor() failed to activate device %s", nm_device_get_iface (dev)); + nm_data_mark_state_changed (data); + } + +out: + g_free (result); + nm_device_unref (dev); + return FALSE; +} + + /* * nm_state_modification_monitor * - * Called every 2s and figures out which interface to switch the active + * Figures out which interface to switch the active * network connection to if our global network state has changed. * Global network state changes are triggered by: * 1) insertion/deletion of interfaces * 2) link state change of an interface - * 3) appearance/disappearance of an allowed wireless access point * */ gboolean nm_state_modification_monitor (gpointer user_data) { NMData *data = (NMData *)user_data; - gboolean modified = FALSE; - g_return_val_if_fail (data != NULL, TRUE); + g_return_val_if_fail (data != NULL, FALSE); + +fprintf (stderr, "State modification monitor called\n"); + data->state_modified_idle_id = 0; /* If the info daemon is now running, get our trusted/preferred ap lists from it */ - if (data->info_daemon_avail) + if (data->info_daemon_avail && data->update_ap_lists) { - if (data->update_ap_lists) - { - /* Query info daemon for network lists if its now running */ - if (data->allowed_ap_list) - nm_ap_list_unref (data->allowed_ap_list); - data->allowed_ap_list = nm_ap_list_new (NETWORK_TYPE_ALLOWED); - if (data->allowed_ap_list) - nm_ap_list_populate (data->allowed_ap_list, data); + /* Query info daemon for network lists if its now running */ + if (data->allowed_ap_list) + nm_ap_list_unref (data->allowed_ap_list); + data->allowed_ap_list = nm_ap_list_new (NETWORK_TYPE_ALLOWED); + if (data->allowed_ap_list) + nm_ap_list_populate (data->allowed_ap_list, data); - data->update_ap_lists = FALSE; - } + data->update_ap_lists = FALSE; } - /* Check global state modified variable, and reset it with - * appropriate locking. - */ - g_mutex_lock (data->state_modified_mutex); - modified = data->state_modified; - if (data->state_modified) - data->state_modified = FALSE; - g_mutex_unlock (data->state_modified_mutex); - - /* If any modifications to the data model were made, update - * network state based on policy applied to the data model. - */ - if (modified) + if (nm_try_acquire_mutex (data->dev_list_mutex, __FUNCTION__)) { - if (nm_try_acquire_mutex (data->dev_list_mutex, __FUNCTION__)) + gboolean should_lock_on_activate = FALSE; + gboolean do_switch = FALSE; + NMDevice *best_dev = NULL; + + if ((best_dev = nm_policy_get_best_device (data, &should_lock_on_activate))) + nm_device_ref (best_dev); + + /* Figure out if we need to change devices or wireless networks */ + if (best_dev != data->active_device) { - gboolean should_lock_on_activate = FALSE; - gboolean do_switch = FALSE; - NMDevice *best_dev = NULL; - - if ((best_dev = nm_policy_get_best_device (data, &should_lock_on_activate))) - nm_device_ref (best_dev); - - /* Figure out if we need to change devices or wireless networks */ - if (best_dev != data->active_device) + syslog (LOG_INFO, " SWITCH: best device changed"); + do_switch = TRUE; /* Device changed */ + } + else if (best_dev) + { + if (nm_device_is_wireless (best_dev) && !nm_device_is_activating (best_dev) && nm_device_need_ap_switch (best_dev)) { - syslog (LOG_INFO, " SWITCH: best device changed"); - do_switch = TRUE; /* Device changed */ + syslog (LOG_INFO, " SWITCH: need to associate with new access point or create a wireless network."); + do_switch = TRUE; } - else if (best_dev) + else if (!nm_device_is_activating (best_dev) && !nm_device_get_ip4_address (best_dev)) { - if (nm_device_is_wireless (best_dev) && !nm_device_is_activating (best_dev) && nm_device_need_ap_switch (best_dev)) - { - syslog (LOG_INFO, " SWITCH: need to associate with new access point or create a wireless network."); - do_switch = TRUE; - } - else if (!nm_device_is_activating (best_dev) && !nm_device_get_ip4_address (best_dev)) - { - syslog (LOG_INFO, " SWITCH: need to get an IP address."); - do_switch = TRUE; - } + syslog (LOG_INFO, " SWITCH: need to get an IP address."); + do_switch = TRUE; } + } - if (do_switch) + if (do_switch) + { + /* Deactivate the old device */ + if (data->active_device) { - /* Deactivate the old device */ - if (data->active_device) - { - nm_device_deactivate (data->active_device, FALSE); - nm_device_unref (data->active_device); - data->active_device = NULL; - } - - if (best_dev) - { - /* Begin activation on the new device */ - syslog (LOG_INFO, "nm_state_modification_monitor(): beginning activation for device '%s'", nm_device_get_iface (best_dev)); - nm_device_ref (best_dev); - data->active_device = best_dev; - nm_device_activation_begin (data->active_device); - - /* nm_policy_get_best_device() signals us that the user forced - * a device upon us and that we should lock the active device. - */ - if (should_lock_on_activate) - data->active_device_locked = TRUE; - } + nm_device_deactivate (data->active_device, FALSE); + nm_device_unref (data->active_device); + data->active_device = NULL; } if (best_dev) - nm_device_unref (best_dev); - - nm_unlock_mutex (data->dev_list_mutex, __FUNCTION__); - } - else - syslog (LOG_ERR, "nm_state_modification_monitor() could not get device list mutex"); - } - else if (data->active_device && nm_device_is_just_activated (data->active_device)) - { - nm_dbus_signal_device_status_change (data->dbus_connection, data->active_device, DEVICE_NOW_ACTIVE); - /* Tell NetworkManagerInfo to store the MAC address of the active device's AP */ - if (nm_device_is_wireless (data->active_device)) - { - struct ether_addr addr; - - nm_device_get_ap_address (data->active_device, &addr); - nm_dbus_add_network_address (data->dbus_connection, NETWORK_TYPE_ALLOWED, nm_device_get_essid (data->active_device), &addr); - } - syslog (LOG_INFO, "nm_state_modification_monitor() activated device %s", nm_device_get_iface (data->active_device)); - } - else if (data->active_device && nm_device_did_activation_fail (data->active_device)) - { - nm_device_clear_activation_fail (data->active_device); - nm_dbus_signal_device_status_change (data->dbus_connection, data->active_device, DEVICE_ACTIVATION_FAILED); - if (nm_device_is_wireless (data->active_device)) - { - NMAccessPoint *ap = nm_device_get_best_ap (data->active_device); - if (ap) { - /* Add the AP to the invalid list and force a best ap update */ - nm_ap_list_append_ap (data->invalid_ap_list, ap); - nm_device_update_best_ap (data->active_device); + /* Begin activation on the new device */ + syslog (LOG_INFO, "nm_state_modification_monitor(): beginning activation for device '%s'", nm_device_get_iface (best_dev)); + nm_device_ref (best_dev); + data->active_device = best_dev; + nm_device_activation_begin (data->active_device); - /* Unref once because the list takes ownership, and unref a second time because - * nm_device_get_best_ap() refs it before returning. + /* nm_policy_get_best_device() signals us that the user forced + * a device upon us and that we should lock the active device. */ - nm_ap_unref (ap); - nm_ap_unref (ap); + if (should_lock_on_activate) + data->active_device_locked = TRUE; } - syslog (LOG_INFO, "nm_state_modification_monitor() failed to activate device %s (%s)", nm_device_get_iface (data->active_device), ap ? nm_ap_get_essid (ap) : "(none)"); } - else - syslog (LOG_INFO, "nm_state_modification_monitor() failed to activate device %s", nm_device_get_iface (data->active_device)); - nm_data_mark_state_changed (data); + + if (best_dev) + nm_device_unref (best_dev); + + nm_unlock_mutex (data->dev_list_mutex, __FUNCTION__); } /* Clear the starting up flag, so we will now take over and have our way with @@ -382,5 +391,5 @@ gboolean nm_state_modification_monitor (gpointer user_data) if (data->starting_up) data->starting_up = FALSE; - return (TRUE); + return (FALSE); } diff --git a/src/NetworkManagerPolicy.h b/src/NetworkManagerPolicy.h index 84ecb1f66c..02be93f356 100644 --- a/src/NetworkManagerPolicy.h +++ b/src/NetworkManagerPolicy.h @@ -25,10 +25,15 @@ #include "NetworkManager.h" #include "NetworkManagerDevice.h" +typedef struct +{ + NMDevice *dev; + gboolean success; +} NMActivationResult; + + gboolean nm_state_modification_monitor (gpointer user_data); -void nm_policy_update_allowed_access_points (NMData *data); - -gpointer nm_policy_allowed_ap_refresh_worker (gpointer user_data); +gboolean nm_policy_activation_finish (gpointer user_data); #endif diff --git a/src/NetworkManagerSystem.c b/src/NetworkManagerSystem.c index b05bd0b1ad..cca0364e50 100644 --- a/src/NetworkManagerSystem.c +++ b/src/NetworkManagerSystem.c @@ -28,7 +28,6 @@ #include #include #include -#include #include #include #include diff --git a/src/NetworkManagerWireless.c b/src/NetworkManagerWireless.c index 5ede287644..4eb260dae8 100644 --- a/src/NetworkManagerWireless.c +++ b/src/NetworkManagerWireless.c @@ -194,22 +194,64 @@ int nm_wireless_qual_to_percent (NMDevice *dev, const struct iw_quality *qual) /* - * nm_wireless_scan_monitor + * nm_wireless_process_scan_results * - * Called every 10s to get a list of access points. + * Run from main thread to hand scan results off to each device + * for processing. * */ -gboolean nm_wireless_scan_monitor (gpointer user_data) +static gboolean nm_wireless_process_scan_results (gpointer user_data) +{ + GSList *results = (GSList *)user_data; + GSList *elem = NULL; + + if (!results) + return FALSE; + + elem = results; + while (elem) + { + NMWirelessScanResults *res = (NMWirelessScanResults *)(elem->data); + + nm_device_process_scan_results (res->dev, &(res->results)); + + /* Release the scan results */ + nm_dispose_scan_results (res->results.result); + nm_device_unref (res->dev); + g_free (res); + elem->data = NULL; + + elem = g_slist_next (elem); + } + g_slist_free (results); + + return FALSE; +} + + +/* + * nm_wireless_scan_monitor + * + * Called every 10s to get a list of access points from the hardware. When its got + * the list, it schedules an idle handler in the main thread's event loop to actually + * integrate the scan results into the NMDevice's access point list. + * + */ +static gboolean nm_wireless_scan_monitor (gpointer user_data) { NMData *data = (NMData *)user_data; GSList *element; NMDevice *dev; + GSList *scan_results = NULL; g_return_val_if_fail (data != NULL, TRUE); - /* Attempt to acquire mutex so that data->active_device sticks around. - * If the acquire fails, just ignore the scan completely. + /* We don't want to lock the device list for the entire duration of the scanning process + * for all cards. Scanning can take quite a while. Therefore, we grab a list of the devices + * and ref each one, then release the device list lock, perform scanning, and pass that list + * to the idle handler in the main thread, along iwth the scanning results. */ + if (!nm_try_acquire_mutex (data->dev_list_mutex, __FUNCTION__)) { syslog (LOG_ERR, "nm_wireless_scan_monitor() could not acquire device list mutex." ); @@ -220,11 +262,68 @@ gboolean nm_wireless_scan_monitor (gpointer user_data) while (element) { if ((dev = (NMDevice *)(element->data)) && nm_device_is_wireless (dev)) - nm_device_do_wireless_scan (dev); + { + NMWirelessScanResults *scan_res = g_malloc0 (sizeof (NMWirelessScanResults)); + + nm_device_ref (dev); + scan_res->dev = dev; + scan_results = g_slist_append (scan_results, scan_res); + } + element = g_slist_next (element); + } + nm_unlock_mutex (data->dev_list_mutex, __FUNCTION__); + + /* Okay, do the actual scanning now. */ + element = scan_results; + while (element) + { + NMWirelessScanResults *res = (NMWirelessScanResults *)(element->data); + nm_device_do_wireless_scan (res->dev, &(res->results)); element = g_slist_next (element); } - nm_unlock_mutex (data->dev_list_mutex, __FUNCTION__); + /* Schedule an idle handler in the main thread to process the scan results */ + if (scan_results) + { + guint scan_process_source_id = 0; + GSource *scan_process_source = g_idle_source_new (); + + g_source_set_callback (scan_process_source, nm_wireless_process_scan_results, scan_results, NULL); + scan_process_source_id = g_source_attach (scan_process_source, data->main_context); + g_source_unref (scan_process_source); + } return (TRUE); } + + +/* + * nm_wireless_scan_worker + * + * Worker thread main function to handle wireless scanning. + * + */ +gpointer nm_wireless_scan_worker (gpointer user_data) +{ + NMData *data = (NMData *)user_data; + guint wscan_source_id = 0; + GSource *wscan_source = NULL; + + if (!data) + return NULL; + + wscan_source = g_timeout_source_new (14000); + g_source_set_callback (wscan_source, nm_wireless_scan_monitor, data, NULL); + wscan_source_id = g_source_attach (wscan_source, data->wscan_ctx); + g_source_unref (wscan_source); + + /* Do an initial scan */ + nm_wireless_scan_monitor (user_data); + + g_main_loop_run (data->wscan_loop); + + g_source_remove (wscan_source_id); + data->wscan_thread_done = TRUE; + return NULL; +} + diff --git a/src/NetworkManagerWireless.h b/src/NetworkManagerWireless.h index 8bad0f0473..f260401013 100644 --- a/src/NetworkManagerWireless.h +++ b/src/NetworkManagerWireless.h @@ -27,12 +27,20 @@ #include "NetworkManagerDevice.h" #include "NetworkManagerAPList.h" -char * nm_wireless_64bit_ascii_to_hex (const unsigned char *ascii); -char * nm_wireless_128bit_ascii_to_hex (const unsigned char *ascii); -char * nm_wireless_128bit_key_from_passphrase (const char *passphrase); -gboolean nm_wireless_scan_monitor (gpointer user_data); +typedef struct +{ + NMDevice *dev; + struct wireless_scan_head results; +} NMWirelessScanResults; + + +char * nm_wireless_64bit_ascii_to_hex (const unsigned char *ascii); +char * nm_wireless_128bit_ascii_to_hex (const unsigned char *ascii); +char * nm_wireless_128bit_key_from_passphrase (const char *passphrase); int nm_wireless_qual_to_percent (NMDevice *dev, const struct iw_quality *qual); +gpointer nm_wireless_scan_worker (gpointer user_data); + #endif diff --git a/src/autoip.c b/src/autoip.c index 86e4ba43a5..218c2c05ef 100644 --- a/src/autoip.c +++ b/src/autoip.c @@ -23,7 +23,6 @@ #include #include #include -#include #include #include #include diff --git a/src/backends/NetworkManagerGentoo.c b/src/backends/NetworkManagerGentoo.c index 1532c39a1e..4e7db78056 100644 --- a/src/backends/NetworkManagerGentoo.c +++ b/src/backends/NetworkManagerGentoo.c @@ -168,15 +168,15 @@ void nm_system_device_flush_addresses (NMDevice *dev) */ gboolean nm_system_device_setup_static_ip4_config (NMDevice *dev) { - syslog (LOG_WARNING, "nm_system_device_setup_static_ip4_config() is not implemented yet for this distribution.\n"); #define IPBITS (sizeof (guint32) * 8) struct in_addr ip_addr, net_addr, broad_addr, gate_addr; int i, err; guint32 prefix = IPBITS; - const char *iface; - char *buf; - char *addr, *netmask, *broadcast, *gateway; + const char *iface; + char *buf; + char *addr, *netmask, *broadcast, *gateway; + syslog (LOG_WARNING, "nm_system_device_setup_static_ip4_config() is not implemented yet for this distribution.\n"); /* Extract the addresses back into strings */