Compila rnetserver e rnetclient.
[cascardo/rnetproxy.git] / hcconn.c
index 2297c94..c15256f 100644 (file)
--- a/hcconn.c
+++ b/hcconn.c
@@ -1,5 +1,5 @@
 /*
- *  Copyright (C) 2009  Thadeu Lima de Souza Cascardo <cascardo@holoscopio.com>
+ *  Copyright (C) 2009  Thadeu Lima de Souza Cascardo <cascardo@minaslivre.org>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
 #include "hcconn.h"
 #include <unistd.h>
 #include <fcntl.h>
+#include <sys/socket.h>
 #include "hcconn_internal.h"
 
+/* The server connection watch */
+
 struct hc_server_cb
 {
   GIOChannel *channel;
@@ -58,57 +61,119 @@ hc_server_add_watch (int fd,
   cb->channel = g_io_channel_unix_new (fd);
   cb->func = func;
   cb->data = data;
+  /* TODO: we should have some way to remove this watch */
   g_io_add_watch_full (cb->channel, G_PRIORITY_DEFAULT, G_IO_IN,
                        hc_server_watch, cb, hc_server_cb_destroy);
 }
 
+
+/* The IOChannel (simple socket) layer */
+
+struct channel_layer
+{
+  GIOChannel *channel;
+  guint watch;
+};
+
 ssize_t
 hc_conn_channel_read (gpointer data, char *buffer, size_t len)
 {
-  int fd = g_io_channel_unix_get_fd ((GIOChannel *) data);
+  struct channel_layer *layer = data;
+  int fd = g_io_channel_unix_get_fd (layer->channel);
   return read (fd, buffer, len);
 }
 
 ssize_t
 hc_conn_channel_write (gpointer data, char *buffer, size_t len)
 {
-  int fd = g_io_channel_unix_get_fd ((GIOChannel *) data);
+  struct channel_layer *layer = data;
+  int fd = g_io_channel_unix_get_fd (layer->channel);
   return write (fd, buffer, len);
 }
 
 void
 hc_conn_channel_close (gpointer data)
 {
-  int fd = g_io_channel_unix_get_fd ((GIOChannel *) data);
-  shutdown (fd, SHUT_RDWR);
+  struct channel_layer *layer = data;
+  int fd = g_io_channel_unix_get_fd (layer->channel);
+  g_source_remove (layer->watch);
+  close (fd);
+  g_io_channel_unref (layer->channel);
+  g_slice_free (struct channel_layer, layer);
 }
 
 gboolean
 hc_conn_watch (GIOChannel *channel, GIOCondition cond, gpointer data)
 {
   HCConn *conn = data;
-  HCEvent event = HC_EVENT_READ;
+  /* TODO: What about other events, like closing? */
+  HCEvent event;
+  int fd = g_io_channel_unix_get_fd (channel);
+  char buffer;
+  int r;
+  if (cond & G_IO_IN)
+    {
+      event = HC_EVENT_READ;
+      r = recv (fd, &buffer, 1, MSG_PEEK);
+      if (r == 0)
+        {
+          event = HC_EVENT_CLOSE;
+        }
+      else if (r == -1)
+        {
+          /* FIXME: create HC_EVENT_ERROR */
+          event = HC_EVENT_CLOSE;
+        }
+    }
+  else if (cond & G_IO_HUP)
+    {
+      event = HC_EVENT_CLOSE;
+    }
+  else if (cond & G_IO_ERR)
+    {
+      /* FIXME: create HC_EVENT_ERROR */
+      event = HC_EVENT_CLOSE;
+    }
+  else
+    {
+      /* TODO: handle other conditions and create error event */
+      g_warning ("Received an unexpected IO condition.");
+    }
   if (conn->func)
     conn->func (conn, event, conn->data);
   return TRUE;
 }
 
-HCConn *
-hc_conn_new (int fd, HCClientFunc func, gpointer data)
+int
+hc_conn_set_driver_channel (HCConn *conn, int fd)
 {
-  HCConn *conn;
-  conn = g_slice_new (HCConn);
-  conn->channel = g_io_channel_unix_new (fd);
-  conn->func = func;
-  conn->data = data;
-  conn->layer = conn->channel;
+  struct channel_layer *layer = g_slice_new (struct channel_layer);
+  layer->channel = g_io_channel_unix_new (fd);
+  conn->layer = layer;
   conn->read = hc_conn_channel_read;
   conn->write = hc_conn_channel_write;
   conn->close = hc_conn_channel_close;
-  conn->watch = g_io_add_watch (conn->channel, G_IO_IN, hc_conn_watch, conn);
+  /* TODO: We must watch other events */
+  layer->watch = g_io_add_watch (layer->channel,
+                                 G_IO_IN | G_IO_HUP | G_IO_ERR,
+                                 hc_conn_watch, conn);
+  /* TODO: connection should be asynchronous so this could make sense */
   if (conn->func)
     conn->func (conn, HC_EVENT_CONNECT, conn->data);
   fcntl (fd, F_SETFL, fcntl (fd, F_GETFL, 0) | O_NONBLOCK);
+  return 0;
+}
+
+
+/* The core connection system */
+
+HCConn *
+hc_conn_new (HCClientFunc func, gpointer data)
+{
+  HCConn *conn;
+  conn = g_slice_new (HCConn);
+  conn->func = func;
+  conn->data = data;
   return conn;
 }
 
@@ -122,21 +187,29 @@ hc_conn_set_callback (HCConn *conn, HCClientFunc func, gpointer data)
 ssize_t
 hc_conn_read (HCConn *conn, char *buffer, size_t len)
 {
-  return conn->read (conn->layer, buffer, len);
+  if (conn->read)
+    return conn->read (conn->layer, buffer, len);
+  return 0;
 }
 
 void
 hc_conn_write (HCConn *conn, char *buffer, size_t len)
 {
   /* TODO: Do buffering or something like that */
-  conn->write (conn->layer, buffer, len);
+  /* Do we really need to? */
+  /* In case of error, we should do something */
+  if (conn->write)
+    conn->write (conn->layer, buffer, len);
 }
 
 void
 hc_conn_close (HCConn *conn)
 {
-  g_source_remove (conn->watch);
-  conn->close (conn->layer);
-  g_io_channel_unref (conn->channel);
+  if (conn->close)
+    conn->close (conn->layer);
+  conn->read = NULL;
+  conn->write = NULL;
+  conn->close = NULL;
+  conn->func = NULL;
   g_slice_free (HCConn, conn);
 }