shill: Make nl80211 broadcast callback a list of callbacks.

This will allow multiple callbacks to be called when an nl80211 message
is received.  This allows a user to add a callback without displacing
others (e.g. disconnect metrics).

BUG=chromium-os:35468
TEST=unit tests, manual tests

Change-Id: I19d6cfac5754ea1d2a699de80d4465c49fec888c
Reviewed-on: https://gerrit.chromium.org/gerrit/36061
Reviewed-by: mukesh agrawal <quiche@chromium.org>
Commit-Ready: Wade Guthrie <wdg@google.com>
Tested-by: Wade Guthrie <wdg@google.com>
diff --git a/config80211.cc b/config80211.cc
index f80ad8e..4dc9975 100644
--- a/config80211.cc
+++ b/config80211.cc
@@ -5,7 +5,6 @@
 #include "shill/config80211.h"
 
 #include <ctype.h>
-
 #include <netlink/msg.h>
 
 #include <map>
@@ -14,7 +13,6 @@
 
 #include <base/memory/weak_ptr.h>
 #include <base/stl_util.h>
-#include <base/stringprintf.h>
 
 #include "shill/io_handler.h"
 #include "shill/logging.h"
@@ -24,7 +22,7 @@
 
 using base::Bind;
 using base::LazyInstance;
-using base::StringAppendF;
+using std::list;
 using std::string;
 
 namespace shill {
@@ -58,6 +56,7 @@
 void Config80211::Reset() {
   wifi_state_ = kWifiDown;
   subscribed_events_.clear();
+  ClearBroadcastCallbacks();
 }
 
 bool Config80211::Init(EventDispatcher *dispatcher) {
@@ -93,6 +92,50 @@
   return true;
 }
 
+bool Config80211::AddBroadcastCallback(const Callback &callback) {
+  list<Callback>::iterator i;
+  if (FindBroadcastCallback(callback)) {
+    LOG(WARNING) << "Trying to re-add a callback";
+    return false;  // Should only be one copy in the list.
+  }
+  if (callback.is_null()) {
+    LOG(WARNING) << "Trying to add a NULL callback";
+    return false;
+  }
+  // And add the callback to the list.
+  SLOG(WiFi, 3) << "Config80211::" << __func__ << " - adding callback";
+  broadcast_callbacks_.push_back(callback);
+  return true;
+}
+
+bool Config80211::RemoveBroadcastCallback(const Callback &callback) {
+  list<Callback>::iterator i;
+  for (i = broadcast_callbacks_.begin(); i != broadcast_callbacks_.end(); ++i) {
+    if ((*i).Equals(callback)) {
+      broadcast_callbacks_.erase(i);
+      // Should only be one copy in the list so we don't have to continue
+      // looking for another one.
+      return true;
+    }
+  }
+  LOG(WARNING) << "Callback not found.";
+  return false;
+}
+
+bool Config80211::FindBroadcastCallback(const Callback &callback) const {
+  list<Callback>::const_iterator i;
+  for (i = broadcast_callbacks_.begin(); i != broadcast_callbacks_.end(); ++i) {
+    if ((*i).Equals(callback)) {
+      return true;
+    }
+  }
+  return false;
+}
+
+void Config80211::ClearBroadcastCallbacks() {
+  broadcast_callbacks_.clear();
+}
+
 // static
 bool Config80211::GetEventTypeString(EventType type, string *value) {
   if (!value) {
@@ -159,10 +202,8 @@
 
   // Install the global NetLink Callback for messages along with a parameter.
   // The Netlink Callback's parameter is passed to 'C' as a 'void *'.
-  if (!sock_->SetNetlinkCallback(OnNlMessageReceived,
-                                static_cast<void *>(
-                                    const_cast<Config80211::Callback *>(
-                                        &default_callback_)))) {
+  if (!sock_->SetNetlinkCallback(OnRawNlMessageReceived,
+                                 static_cast<void *>(this))) {
     return false;
   }
   return true;
@@ -174,38 +215,52 @@
 
 // NOTE: the "struct nl_msg" declaration, below, is a complete fabrication
 // (but one consistent with the nl_socket_modify_cb call to which
-// |OnNlMessageReceived| is a parameter).  |raw_message| is actually a
+// |OnRawNlMessageReceived| is a parameter).  |raw_message| is actually a
 // "struct sk_buff" but that data type is only visible in the kernel.  We'll
 // scrub this erroneous datatype with the "nlmsg_hdr" call, below, which
-// extracts an nlmsghdr pointer from |raw_message|.
-int Config80211::OnNlMessageReceived(struct nl_msg *raw_message,
-                                     void *user_callback_object) {
-  SLOG(WiFi, 6) << "\t  Entering " << __func__
-                << "( msg:" << raw_message
-                << ", cb:" << user_callback_object << ")";
+// extracts an nlmsghdr pointer from |raw_message|.  We'll, then, pass this to
+// a separate method, |OnNlMessageReceived|, to make testing easier.
 
-  string output("@");
+// static
+int Config80211::OnRawNlMessageReceived(struct nl_msg *raw_message,
+                                        void *void_config80211) {
+  if (!void_config80211) {
+    LOG(WARNING) << "NULL config80211 parameter";
+    return NL_SKIP;  // Skip current message, continue parsing buffer.
+  }
 
-  nlmsghdr *msg = nlmsg_hdr(raw_message);
+  Config80211 *config80211 = static_cast<Config80211 *>(void_config80211);
+  SLOG(WiFi, 3) << "  " << __func__ << " calling OnNlMessageReceived";
+  return config80211->OnNlMessageReceived(nlmsg_hdr(raw_message));
+}
 
+int Config80211::OnNlMessageReceived(nlmsghdr *msg) {
+  SLOG(WiFi, 3) << "\t  Entering " << __func__
+                << "( msg:" << msg->nlmsg_seq << ")";
   scoped_ptr<UserBoundNlMessage> message(
       UserBoundNlMessageFactory::CreateMessage(msg));
   if (message == NULL) {
-    SLOG(WiFi, 6) << __func__ << "(msg:NULL)";
-    output.append("unknown event");
+    SLOG(WiFi, 3) << __func__ << "(msg:NULL)";
   } else {
-    SLOG(WiFi, 6) << __func__ << "(msg:" << msg->nlmsg_seq << ")";
-    if (user_callback_object) {
-      Config80211::Callback *bound_object =
-          static_cast<Config80211::Callback *> (user_callback_object);
-      if (!bound_object->is_null()) {
-        bound_object->Run(*message);
+    SLOG(WiFi, 3) << __func__ << "(msg:" << msg->nlmsg_seq << ")";
+    list<Callback>::iterator i = broadcast_callbacks_.begin();
+    while (i != broadcast_callbacks_.end()) {
+      SLOG(WiFi, 3) << "    " << __func__ << " - found callback";
+      if (i->is_null()) {
+        // How did this get in here?
+        LOG(WARNING) << "Removing NULL callback from list";
+        list<Callback>::iterator temp = i;
+        ++temp;
+        broadcast_callbacks_.erase(i);
+        i = temp;
+      } else {
+        SLOG(WiFi, 3) << "      " << __func__ << " - calling callback";
+        i->Run(*message);
+        ++i;
       }
     }
-    StringAppendF(&output, "%s", message->ToString().c_str());
   }
 
-  SLOG(WiFi, 5) << output;
   return NL_SKIP;  // Skip current message, continue parsing buffer.
 }