source: trunk/gtk/io.c @ 1125

Last change on this file since 1125 was 760, checked in by joshe, 15 years ago

Change all my 2-clause BSD licenses to the same MIT/X11 license as libtransmission.

  • Property svn:keywords set to Date Rev Author Id
File size: 8.7 KB
Line 
1/******************************************************************************
2 * $Id: io.c 760 2006-08-13 00:26:52Z livings124 $
3 *
4 * Copyright (c) 2006 Transmission authors and contributors
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 *****************************************************************************/
24
25#include <sys/types.h>
26#include <sys/socket.h>
27#include <sys/uio.h>
28#include <errno.h>
29#include <fcntl.h>
30#include <string.h>
31#include <unistd.h>
32
33#include <glib.h>
34
35#include "io.h"
36#include "util.h"
37
38#define IO_BLOCKSIZE            (1024)
39
40struct iosource {
41  GSource source;
42  GPollFD infd;
43  GPollFD outfd;
44  ioidfunc_t sent;
45  iodatafunc_t received;
46  ionewfunc_t accepted;
47  iofunc_t closed;
48  void *cbdata;
49  char *inbuf;
50  unsigned int inused;
51  unsigned int inmax;
52  GList *outbufs;
53  unsigned int lastid;
54};
55
56struct iooutbuf {
57  char *data;
58  unsigned int len;
59  unsigned int off;
60  unsigned int id;
61};
62
63static gboolean
64nonblock(int fd);
65static struct iosource *
66newsource(void);
67static void
68freeoutbuf(struct iooutbuf *buf);
69static gboolean
70io_prepare(GSource *source, gint *timeout_);
71static gboolean
72io_check(GSource *source);
73static gboolean
74io_dispatch(GSource *source, GSourceFunc callback, gpointer gdata);
75static void
76io_finalize(GSource *source);
77static void
78io_accept(struct iosource *io);
79static void
80io_read(struct iosource *io);
81static void
82io_write(struct iosource *io);
83static void
84io_disconnect(struct iosource *io, int err);
85
86static GSourceFuncs sourcefuncs = {
87  io_prepare,
88  io_check,
89  io_dispatch,
90  io_finalize,
91  NULL,
92  NULL
93};
94
95GSource *
96io_new(int fd, ioidfunc_t sent, iodatafunc_t received,
97       iofunc_t closed, void *cbdata) {
98  struct iosource *io;
99
100  if(!nonblock(fd))
101    return NULL;
102
103  io = newsource();
104  io->sent = sent;
105  io->received = received;
106  io->closed = closed;
107  io->cbdata = cbdata;
108  io->infd.fd = fd;
109  io->infd.events = G_IO_IN | G_IO_HUP | G_IO_ERR;
110  io->infd.revents = 0;
111  io->outfd.fd = fd;
112  io->outfd.events = G_IO_OUT | G_IO_ERR;
113  io->outfd.revents = 0;
114
115  g_source_add_poll((GSource*)io, &io->infd);
116  g_source_attach((GSource*)io, NULL);
117
118  return (GSource*)io;
119}
120
121GSource *
122io_new_listening(int fd, socklen_t len, ionewfunc_t accepted,
123                 iofunc_t closed, void *cbdata) {
124  struct iosource *io;
125
126  g_assert(NULL != accepted);
127
128  if(!nonblock(fd))
129    return NULL;
130
131  io = newsource();
132  io->accepted = accepted;
133  io->closed = closed;
134  io->cbdata = cbdata;
135  io->infd.fd = fd;
136  io->infd.events = G_IO_IN | G_IO_ERR;
137  io->infd.revents = 0;
138  io->inbuf = g_new(char, len);
139  io->inmax = len;
140
141  g_source_add_poll((GSource*)io, &io->infd);
142  g_source_attach((GSource*)io, NULL);
143
144  return (GSource*)io;
145}
146
147static gboolean
148nonblock(int fd) {
149  int flags;
150
151  if(0 > (flags = fcntl(fd, F_GETFL)) ||
152     0 > fcntl(fd, F_SETFL, flags | O_NONBLOCK))
153    return FALSE;
154
155  return TRUE;
156}
157
158static struct iosource *
159newsource(void) {
160  GSource *source = g_source_new(&sourcefuncs, sizeof(struct iosource));
161  struct iosource *io = (struct iosource*)source;
162
163  io->sent = NULL;
164  io->received = NULL;
165  io->accepted = NULL;
166  io->closed = NULL;
167  io->cbdata = NULL;
168  bzero(&io->infd, sizeof(io->infd));
169  io->infd.fd = -1;
170  bzero(&io->outfd, sizeof(io->outfd));
171  io->outfd.fd = -1;
172  io->inbuf = NULL;
173  io->inused = 0;
174  io->inmax = 0;
175  io->outbufs = NULL;
176  io->lastid = 0;
177
178  return io;
179}
180
181unsigned int
182io_send(GSource *source, const char *data, unsigned int len) {
183  char *new = g_new(char, len);
184
185  memcpy(new, data, len);
186
187  return io_send_keepdata(source, new, len);
188}
189
190unsigned int
191io_send_keepdata(GSource *source, char *data, unsigned int len) {
192  struct iosource *io = (struct iosource*)source;
193  struct iooutbuf *buf = g_new(struct iooutbuf, 1);
194
195  buf->data = data;
196  buf->len = len;
197  buf->off = 0;
198  io->lastid++;
199  buf->id = io->lastid;
200
201  if(NULL != io->outbufs)
202    io->outbufs = g_list_append(io->outbufs, buf);
203  else {
204    io->outbufs = g_list_append(io->outbufs, buf);
205    g_source_add_poll(source, &io->outfd);
206  }
207
208  return io->lastid;
209}
210
211static void
212freeoutbuf(struct iooutbuf *buf) {
213  if(NULL != buf->data)
214    g_free(buf->data);
215  g_free(buf);
216}
217
218static gboolean
219io_prepare(GSource *source SHUTUP, gint *timeout_) {
220  *timeout_ = -1;
221  return FALSE;
222}
223
224static gboolean
225io_check(GSource *source) {
226  struct iosource *io = (struct iosource*)source;
227
228  if(io->infd.revents)
229    return TRUE;
230  if(NULL != io->outbufs && io->outfd.revents)
231    return TRUE;
232  else
233    return FALSE;
234}
235
236static gboolean
237io_dispatch(GSource *source, GSourceFunc callback SHUTUP,
238            gpointer gdata SHUTUP) {
239  struct iosource *io = (struct iosource*)source;
240
241  if(io->infd.revents & (G_IO_ERR | G_IO_HUP) ||
242     io->outfd.revents & G_IO_ERR)
243    io_disconnect(io, 0 /* XXX how do I get errors here? */ );
244  else if(io->infd.revents & G_IO_IN)
245    (NULL == io->accepted ? io_read : io_accept)(io);
246  else if(io->outfd.revents & G_IO_OUT)
247    io_write(io);
248  else
249    return FALSE;
250
251  return TRUE;
252}
253
254
255static void
256io_finalize(GSource *source SHUTUP) {
257  struct iosource *io = (struct iosource*)source;
258
259  if(NULL != io->outbufs) {
260    g_list_foreach(io->outbufs, (GFunc)freeoutbuf, NULL);
261    g_list_free(io->outbufs);
262  }
263
264  if(NULL != io->inbuf)
265    g_free(io->inbuf);
266}
267
268static void
269io_biggify(char **buf, unsigned int used, unsigned int *max) {
270  if(used + IO_BLOCKSIZE > *max) {
271    *max += IO_BLOCKSIZE;
272    *buf = g_renew(char, *buf, *max);
273  }
274}
275
276static void
277io_accept(struct iosource *io) {
278  int fd;
279  socklen_t len;
280
281  if(0 > (fd = accept(io->infd.fd, (struct sockaddr*)io->inbuf, &len))) {
282    if(EAGAIN == errno || ECONNABORTED == errno || EWOULDBLOCK == errno)
283      return;
284    io_disconnect(io, errno);
285  }
286
287  io->accepted((GSource*)io, fd, (struct sockaddr*)io->inbuf, len, io->cbdata);
288}
289
290static void
291io_read(struct iosource *io) {
292  ssize_t res = 0;
293  gboolean newdata = FALSE;
294  unsigned int used;
295  int err = 0;
296
297  g_source_ref((GSource*)io);
298
299  do {
300    if(!newdata && 0 < res)
301      newdata = TRUE;
302    io->inused += res;
303    io_biggify(&io->inbuf, io->inused, &io->inmax);
304    errno = 0;
305    res = read(io->infd.fd, io->inbuf + io->inused, io->inmax - io->inused);
306    if(0 > res)
307      err = errno;
308  } while(0 < res);
309
310  if(NULL == io->received)
311    io->inused = 0;
312  else if(newdata) {
313    used = io->received((GSource*)io, io->inbuf, io->inused, io->cbdata);
314    if(used > io->inused)
315      used = io->inused;
316    if(0 < used) {
317      if(used < io->inused)
318        memmove(io->inbuf, io->inbuf + used, io->inused - used);
319      io->inused -= used;
320    }
321  }
322
323  if(0 != err && EAGAIN != err)
324    io_disconnect(io, err);
325  else if(0 == res)
326    io_disconnect(io, 0);
327  g_source_unref((GSource*)io);
328}
329
330static void
331io_write(struct iosource *io) {
332  struct iooutbuf *buf;
333  ssize_t res = 1;
334  int err = 0;
335
336  g_source_ref((GSource*)io);
337
338  while(NULL != io->outbufs && 0 == err) {
339    buf = io->outbufs->data;
340    while(buf->off < buf->len && 0 < res) {
341      errno = 0;
342      res = write(io->outfd.fd, buf->data + buf->off, buf->len - buf->off);
343      if(0 > res)
344        err = errno;
345      else
346        buf->off += res;
347    }
348
349    if(buf->off >= buf->len) {
350      io->outbufs = g_list_remove(io->outbufs, buf);
351      if(NULL == io->outbufs)
352        g_source_remove_poll((GSource*)io, &io->outfd);
353      if(NULL != io->sent)
354        io->sent((GSource*)io, buf->id, io->cbdata);
355      freeoutbuf(buf);
356    }
357  }
358
359  if(0 != err && EAGAIN != err)
360    io_disconnect(io, err);
361
362  g_source_unref((GSource*)io);
363}
364
365static void
366io_disconnect(struct iosource *io, int err) {
367  if(NULL != io->closed) {
368    errno = err;
369    io->closed((GSource*)io, io->cbdata);
370  }
371
372  if(NULL != io->outbufs)
373    g_source_remove_poll((GSource*)io, &io->outfd);
374
375  g_source_remove_poll((GSource*)io, &io->infd);
376  g_source_remove(g_source_get_id((GSource*)io));
377  g_source_unref((GSource*)io);
378}
Note: See TracBrowser for help on using the repository browser.