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 */