1 | /****************************************************************************** |
---|
2 | * $Id: io.c 760 2006-08-13 00:26:52Z joshe $ |
---|
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 | |
---|
40 | struct 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 | |
---|
56 | struct iooutbuf { |
---|
57 | char *data; |
---|
58 | unsigned int len; |
---|
59 | unsigned int off; |
---|
60 | unsigned int id; |
---|
61 | }; |
---|
62 | |
---|
63 | static gboolean |
---|
64 | nonblock(int fd); |
---|
65 | static struct iosource * |
---|
66 | newsource(void); |
---|
67 | static void |
---|
68 | freeoutbuf(struct iooutbuf *buf); |
---|
69 | static gboolean |
---|
70 | io_prepare(GSource *source, gint *timeout_); |
---|
71 | static gboolean |
---|
72 | io_check(GSource *source); |
---|
73 | static gboolean |
---|
74 | io_dispatch(GSource *source, GSourceFunc callback, gpointer gdata); |
---|
75 | static void |
---|
76 | io_finalize(GSource *source); |
---|
77 | static void |
---|
78 | io_accept(struct iosource *io); |
---|
79 | static void |
---|
80 | io_read(struct iosource *io); |
---|
81 | static void |
---|
82 | io_write(struct iosource *io); |
---|
83 | static void |
---|
84 | io_disconnect(struct iosource *io, int err); |
---|
85 | |
---|
86 | static GSourceFuncs sourcefuncs = { |
---|
87 | io_prepare, |
---|
88 | io_check, |
---|
89 | io_dispatch, |
---|
90 | io_finalize, |
---|
91 | NULL, |
---|
92 | NULL |
---|
93 | }; |
---|
94 | |
---|
95 | GSource * |
---|
96 | io_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 | |
---|
121 | GSource * |
---|
122 | io_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 | |
---|
147 | static gboolean |
---|
148 | nonblock(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 | |
---|
158 | static struct iosource * |
---|
159 | newsource(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 | |
---|
181 | unsigned int |
---|
182 | io_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 | |
---|
190 | unsigned int |
---|
191 | io_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 | |
---|
211 | static void |
---|
212 | freeoutbuf(struct iooutbuf *buf) { |
---|
213 | if(NULL != buf->data) |
---|
214 | g_free(buf->data); |
---|
215 | g_free(buf); |
---|
216 | } |
---|
217 | |
---|
218 | static gboolean |
---|
219 | io_prepare(GSource *source SHUTUP, gint *timeout_) { |
---|
220 | *timeout_ = -1; |
---|
221 | return FALSE; |
---|
222 | } |
---|
223 | |
---|
224 | static gboolean |
---|
225 | io_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 | |
---|
236 | static gboolean |
---|
237 | io_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 | |
---|
255 | static void |
---|
256 | io_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 | |
---|
268 | static void |
---|
269 | io_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 | |
---|
276 | static void |
---|
277 | io_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 | |
---|
290 | static void |
---|
291 | io_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 | |
---|
330 | static void |
---|
331 | io_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 | |
---|
365 | static void |
---|
366 | io_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 | } |
---|