Handle ERROR event as a CLOSE one and use masks not case to test events.
[cascardo/rnetproxy.git] / hcconn.c
1 /*
2  *  Copyright (C) 2009  Thadeu Lima de Souza Cascardo <cascardo@holoscopio.com>
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License along
15  *  with this program; if not, write to the Free Software Foundation, Inc.,
16  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  */
18
19
20 #include "hcconn.h"
21 #include <unistd.h>
22 #include <fcntl.h>
23 #include <sys/socket.h>
24 #include "hcconn_internal.h"
25
26 /* The server connection watch */
27
28 struct hc_server_cb
29 {
30   GIOChannel *channel;
31   HCServerFunc func;
32   gpointer data;
33 };
34
35 static void
36 hc_server_cb_destroy (gpointer cb)
37 {
38   g_slice_free (struct hc_server_cb, cb);
39 }
40
41 static gboolean
42 hc_server_watch (GIOChannel *channel, GIOCondition cond, gpointer data)
43 {
44   struct hc_server_cb *cb = data;
45   int fd = g_io_channel_unix_get_fd (channel);
46   struct sockaddr addr;
47   socklen_t saddr = sizeof (addr);
48   int client = accept (fd, &addr, &saddr);
49   if (client >= 0 && cb->func)
50     cb->func (client, &addr, saddr, cb->data);
51   return TRUE;
52 }
53
54 void
55 hc_server_add_watch (int fd,
56                      HCServerFunc func,
57                      gpointer data)
58 {
59   struct hc_server_cb *cb;
60   cb = g_slice_new (struct hc_server_cb);
61   cb->channel = g_io_channel_unix_new (fd);
62   cb->func = func;
63   cb->data = data;
64   /* TODO: we should have some way to remove this watch */
65   g_io_add_watch_full (cb->channel, G_PRIORITY_DEFAULT, G_IO_IN,
66                        hc_server_watch, cb, hc_server_cb_destroy);
67 }
68
69
70 /* The IOChannel (simple socket) layer */
71
72 struct channel_layer
73 {
74   GIOChannel *channel;
75   guint watch;
76 };
77
78 ssize_t
79 hc_conn_channel_read (gpointer data, char *buffer, size_t len)
80 {
81   struct channel_layer *layer = data;
82   int fd = g_io_channel_unix_get_fd (layer->channel);
83   return read (fd, buffer, len);
84 }
85
86 ssize_t
87 hc_conn_channel_write (gpointer data, char *buffer, size_t len)
88 {
89   struct channel_layer *layer = data;
90   int fd = g_io_channel_unix_get_fd (layer->channel);
91   return write (fd, buffer, len);
92 }
93
94 void
95 hc_conn_channel_close (gpointer data)
96 {
97   struct channel_layer *layer = data;
98   int fd = g_io_channel_unix_get_fd (layer->channel);
99   g_source_remove (layer->watch);
100   close (fd);
101   g_io_channel_unref (layer->channel);
102   g_slice_free (struct channel_layer, layer);
103 }
104
105 gboolean
106 hc_conn_watch (GIOChannel *channel, GIOCondition cond, gpointer data)
107 {
108   HCConn *conn = data;
109   /* TODO: What about other events, like closing? */
110   HCEvent event;
111   int fd = g_io_channel_unix_get_fd (channel);
112   char buffer;
113   int r;
114   if (cond & G_IO_IN)
115     {
116       event = HC_EVENT_READ;
117       r = recv (fd, &buffer, 1, MSG_PEEK);
118       if (r == 0)
119         {
120           event = HC_EVENT_CLOSE;
121         }
122     }
123   else if (cond & G_IO_HUP)
124     {
125       event = HC_EVENT_CLOSE;
126     }
127   else if (cond & G_IO_ERR)
128     {
129       /* FIXME: create HC_EVENT_ERROR */
130       event = HC_EVENT_CLOSE;
131     }
132   else
133     {
134       /* TODO: handle other conditions and create error event */
135       g_warning ("Received an unexpected IO condition.");
136     }
137   if (conn->func)
138     conn->func (conn, event, conn->data);
139   return TRUE;
140 }
141
142 int
143 hc_conn_set_driver_channel (HCConn *conn, int fd)
144 {
145   struct channel_layer *layer = g_slice_new (struct channel_layer);
146   layer->channel = g_io_channel_unix_new (fd);
147   conn->layer = layer;
148   conn->read = hc_conn_channel_read;
149   conn->write = hc_conn_channel_write;
150   conn->close = hc_conn_channel_close;
151   /* TODO: We must watch other events */
152   layer->watch = g_io_add_watch (layer->channel,
153                                  G_IO_IN | G_IO_HUP | G_IO_ERR,
154                                  hc_conn_watch, conn);
155   /* TODO: connection should be asynchronous so this could make sense */
156   if (conn->func)
157     conn->func (conn, HC_EVENT_CONNECT, conn->data);
158   fcntl (fd, F_SETFL, fcntl (fd, F_GETFL, 0) | O_NONBLOCK);
159   return 0;
160 }
161
162
163 /* The core connection system */
164
165 HCConn *
166 hc_conn_new (HCClientFunc func, gpointer data)
167 {
168   HCConn *conn;
169   conn = g_slice_new (HCConn);
170   conn->func = func;
171   conn->data = data;
172   return conn;
173 }
174
175 void
176 hc_conn_set_callback (HCConn *conn, HCClientFunc func, gpointer data)
177 {
178   conn->func = func;
179   conn->data = data;
180 }
181
182 ssize_t
183 hc_conn_read (HCConn *conn, char *buffer, size_t len)
184 {
185   if (conn->read)
186     return conn->read (conn->layer, buffer, len);
187   return 0;
188 }
189
190 void
191 hc_conn_write (HCConn *conn, char *buffer, size_t len)
192 {
193   /* TODO: Do buffering or something like that */
194   /* Do we really need to? */
195   /* In case of error, we should do something */
196   if (conn->write)
197     conn->write (conn->layer, buffer, len);
198 }
199
200 void
201 hc_conn_close (HCConn *conn)
202 {
203   if (conn->close)
204     conn->close (conn->layer);
205   conn->read = NULL;
206   conn->write = NULL;
207   conn->close = NULL;
208   conn->func = NULL;
209   g_slice_free (HCConn, conn);
210 }