Add fd input (such as socket) event to shill_event

It's understood that the main event loop is changing, but
here's a first cut at describing the sort of things we will
need in that respect.  I'm using this facility for my RTNL
prototype.

BUG=n0ne (this will likely be overwritten)
TEST=Unit tests added to unittest.cc and pass

Change-Id: I4ee89fc4119da0871e9f852707cce9341a5018c0
Reviewed-on: http://gerrit.chromium.org/gerrit/605
Reviewed-by: Chris Masone <cmasone@chromium.org>
Tested-by: Paul Stewart <pstew@chromium.org>
diff --git a/shill_event.cc b/shill_event.cc
index 6b1356a..b78680c 100644
--- a/shill_event.cc
+++ b/shill_event.cc
@@ -50,4 +50,60 @@
   }
 }
 
+static gboolean DispatchIOHandler(GIOChannel *chan, GIOCondition cond,
+                                  gpointer data);
+
+class GlibIOInputHandler : public IOInputHandler {
+public:
+  GIOChannel *channel_;
+  Callback<InputData *> *callback_;
+  guint source_id_;
+  EventDispatcher *dispatcher_;
+
+  GlibIOInputHandler(EventDispatcher *dispatcher, int fd,
+                     Callback<InputData *> *callback) :
+    dispatcher_(dispatcher), callback_(callback) {
+    channel_ = g_io_channel_unix_new(fd);
+    g_io_channel_set_close_on_unref(channel_, TRUE);
+    source_id_ = g_io_add_watch(channel_,
+                                (GIOCondition)(G_IO_IN | G_IO_NVAL |
+                                               G_IO_HUP | G_IO_ERR),
+                                DispatchIOHandler, this);
+  }
+
+  ~GlibIOInputHandler() {
+    g_source_remove(source_id_);
+    g_io_channel_shutdown(channel_, TRUE, NULL);
+    g_io_channel_unref(channel_);
+  }
+};
+
+static gboolean DispatchIOHandler(GIOChannel *chan, GIOCondition cond,
+                                  gpointer data) {
+  GlibIOInputHandler *handler = static_cast<GlibIOInputHandler *>(data);
+  unsigned char buf[4096];
+  gsize len;
+  GIOError err;
+
+  if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR))
+    return FALSE;
+
+  err = g_io_channel_read(chan, (gchar *) buf, sizeof(buf), &len);
+  if (err) {
+    if (err == G_IO_ERROR_AGAIN)
+      return TRUE;
+    return FALSE;
+  }
+
+  InputData input_data = { buf, len };
+  handler->callback_->Run(&input_data);
+
+  return TRUE;
+}
+
+IOInputHandler *EventDispatcher::CreateInputHandler(int fd,
+        Callback<InputData *> *callback) {
+  return new GlibIOInputHandler(this, fd, callback);
+}
+
 }  // namespace shill