Dillo v3.1.1-98-g318d1f14
Loading...
Searching...
No Matches
IO.c
Go to the documentation of this file.
1/*
2 * File: IO.c
3 *
4 * Copyright (C) 2000-2007 Jorge Arellano Cid <jcid@dillo.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 */
11
16#include <errno.h>
17#include <fcntl.h>
18#include <unistd.h>
19#include "../msg.h"
20#include "../chain.h"
21#include "../klist.h"
22#include "IO.h"
23#include "iowatch.hh"
24#include "tls.h"
25
26/*
27 * Symbolic defines for shutdown() function
28 * (Not defined in the same header file, for all distros --Jcid)
29 */
30#define IO_StopRd 1
31#define IO_StopWr 2
32#define IO_StopRdWr (IO_StopRd | IO_StopWr)
33
34
35typedef struct {
36 int Key; /* Primary Key (for klist) */
37 int Op; /* IORead | IOWrite */
38 int FD; /* Current File Descriptor */
39 int Status; /* nonzero upon IO failure */
40 Dstr *Buf; /* Internal buffer */
41
42 void *Info; /* CCC Info structure for this IO */
43} IOData_t;
44
45
46/*
47 * Local data
48 */
49static Klist_t *ValidIOs = NULL; /* Active IOs list. It holds pointers to
50 * IOData_t structures. */
51
52/*
53 * Forward declarations
54 */
55void a_IO_ccc(int Op, int Branch, int Dir, ChainLink *Info,
56 void *Data1, void *Data2);
57
58
59/* IO API - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
60
64static IOData_t *IO_new(int op)
65{
66 IOData_t *io = dNew0(IOData_t, 1);
67 io->Op = op;
68 io->FD = -1;
69 io->Key = 0;
70 io->Buf = dStr_sized_new(IOBufLen);
71
72 return io;
73}
74
75/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
76
80static void IO_ins(IOData_t *io)
81{
82 if (io->Key == 0) {
83 io->Key = a_Klist_insert(&ValidIOs, io);
84 }
85 _MSG("IO_ins: io->Key=%d, Klist_length=%d\n",
86 io->Key, a_Klist_length(ValidIOs));
87}
88
92static void IO_del(IOData_t *io)
93{
94 if (io->Key != 0) {
95 a_Klist_remove(ValidIOs, io->Key);
96 }
97 io->Key = 0;
98 _MSG(" -->ValidIOs: %d\n", a_Klist_length(ValidIOs));
99}
100
104static IOData_t *IO_get(int Key)
105{
106 return (IOData_t *)a_Klist_get_data(ValidIOs, Key);
107}
108
112static void IO_free(IOData_t *io)
113{
114 dStr_free(io->Buf, 1);
115 dFree(io);
116}
117
125static void IO_close_fd(IOData_t *io, int CloseCode)
126{
127 int events = 0;
128
129 _MSG("====> begin IO_close_fd (%d) Key=%d CloseCode=%d ",
130 io->FD, io->Key, CloseCode);
131
132 /* With HTTP, if we close the writing part, the reading one also gets
133 * closed! */
134 if ((CloseCode == IO_StopRdWr) && io->FD != -1) {
135 dClose(io->FD);
136 } else {
137 _MSG(" NOT CLOSING ");
138 }
139 /* Remove this IOData_t reference, from our ValidIOs list
140 * We don't deallocate it here, just remove from the list.*/
141 IO_del(io);
142
143 /* Stop the polling on this FD */
144 if (CloseCode & IO_StopRd) {
145 events |= DIO_READ;
146 }
147 if (CloseCode & IO_StopWr) {
148 events |= DIO_WRITE;
149 }
150 a_IOwatch_remove_fd(io->FD, events);
151 _MSG(" end IO close (%d) <=====\n", io->FD);
152}
153
157static bool_t IO_read(IOData_t *io)
158{
159 char Buf[IOBufLen];
160 ssize_t St;
161 bool_t ret = FALSE;
162 int io_key = io->Key;
163 void *conn = a_Tls_connection(io->FD);
164
165 _MSG(" IO_read\n");
166
167 /* this is a new read-buffer */
168 dStr_truncate(io->Buf, 0);
169 io->Status = 0;
170
171 while (1) {
172 St = conn ? a_Tls_read(conn, Buf, IOBufLen)
173 : read(io->FD, Buf, IOBufLen);
174 if (St > 0) {
175 dStr_append_l(io->Buf, Buf, St);
176 continue;
177 } else if (St < 0) {
178 if (errno == EINTR) {
179 continue;
180 } else if (errno == EAGAIN) {
181 ret = TRUE;
182 break;
183 } else {
184 if (conn) {
185 io->Status = St;
186 break;
187 } else {
188 io->Status = errno;
189 MSG("READ Failed with %d: %s\n", (int)St, strerror(errno));
190 break;
191 }
192 }
193 } else { /* St == 0 */
194 break;
195 }
196 }
197
198 if (io->Buf->len > 0) {
199 /* send what we've got so far */
200 a_IO_ccc(OpSend, 2, FWD, io->Info, io, NULL);
201 }
202 if (St == 0) {
203 /* TODO: design a general way to avoid reentrancy problems with CCC. */
204
205 /* The following check is necessary because the above CCC operation
206 * may abort the whole Chain. */
207 if ((io = IO_get(io_key))) {
208 /* All data read (EOF) */
209 _MSG("IO_read: io->Key=%d io_key=%d\n", io->Key, io_key);
210 a_IO_ccc(OpEnd, 2, FWD, io->Info, io, NULL);
211 }
212 }
213 return ret;
214}
215
219static bool_t IO_write(IOData_t *io)
220{
221 ssize_t St;
222 bool_t ret = FALSE;
223 void *conn = a_Tls_connection(io->FD);
224
225 _MSG(" IO_write\n");
226 io->Status = 0;
227
228 while (1) {
229 St = conn ? a_Tls_write(conn, io->Buf->str, io->Buf->len)
230 : write(io->FD, io->Buf->str, io->Buf->len);
231 if (St < 0) {
232 /* Error */
233 if (errno == EINTR) {
234 continue;
235 } else if (errno == EAGAIN) {
236 ret = TRUE;
237 break;
238 } else {
239 if (conn) {
240 io->Status = St;
241 break;
242 } else {
243 io->Status = errno;
244 MSG("WRITE Failed with %d: %s\n", (int)St, strerror(errno));
245 break;
246 }
247 }
248 } else if (St < io->Buf->len) {
249 /* Not all data written */
250 dStr_erase (io->Buf, 0, St);
251 } else {
252 /* All data in buffer written */
253 dStr_truncate(io->Buf, 0);
254 break;
255 }
256 }
257
258 return ret;
259}
260
265static int IO_callback(IOData_t *io)
266{
267 bool_t ret = FALSE;
268
269 _MSG("IO_callback:: (%s) FD = %d\n",
270 (io->Op == IORead) ? "IORead" : "IOWrite", io->FD);
271
272 if (io->Op == IORead) { /* Read */
273 ret = IO_read(io);
274 } else if (io->Op == IOWrite) { /* Write */
275 ret = IO_write(io);
276 }
277 return (ret) ? 1 : 0;
278}
279
283static void IO_fd_read_cb(int fd, void *data)
284{
285 int io_key = VOIDP2INT(data);
286 IOData_t *io = IO_get(io_key);
287
288 /* There should be no more events on already closed FDs --Jcid */
289 if (io == NULL) {
290 MSG_ERR("IO_fd_read_cb: call on already closed io!\n");
292
293 } else {
294 if (IO_callback(io) == 0)
296 if ((io = IO_get(io_key)) && io->Status) {
297 /* check io because IO_read OpSend could trigger abort */
298 a_IO_ccc(OpAbort, 2, FWD, io->Info, io, NULL);
299 }
300 }
301}
302
306static void IO_fd_write_cb(int fd, void *data)
307{
308 int io_key = VOIDP2INT(data);
309 IOData_t *io = IO_get(io_key);
310
311 if (io == NULL) {
312 /* There must be no more events on already closed FDs --Jcid */
313 MSG_ERR("IO_fd_write_cb: call on already closed io!\n");
315
316 } else {
317 if (IO_callback(io) == 0)
319 if (io->Status)
320 a_IO_ccc(OpAbort, 1, FWD, io->Info, NULL, NULL);
321 }
322}
323
328static void IO_submit(IOData_t *r_io)
329{
330 if (r_io->FD < 0) {
331 MSG_ERR("IO_submit: FD not initialized\n");
332 return;
333 }
334
335 /* Insert this IO in ValidIOs */
336 IO_ins(r_io);
337
338 _MSG("IO_submit:: (%s) FD = %d\n",
339 (r_io->Op == IORead) ? "IORead" : "IOWrite", r_io->FD);
340
341 /* Set FD to background and to close on exec. */
342 fcntl(r_io->FD, F_SETFL, O_NONBLOCK | fcntl(r_io->FD, F_GETFL));
343 fcntl(r_io->FD, F_SETFD, FD_CLOEXEC | fcntl(r_io->FD, F_GETFD));
344
345 if (r_io->Op == IORead) {
346 a_IOwatch_add_fd(r_io->FD, DIO_READ,
347 IO_fd_read_cb, INT2VOIDP(r_io->Key));
348
349 } else if (r_io->Op == IOWrite) {
350 a_IOwatch_add_fd(r_io->FD, DIO_WRITE,
351 IO_fd_write_cb, INT2VOIDP(r_io->Key));
352 }
353}
354
359void a_IO_ccc(int Op, int Branch, int Dir, ChainLink *Info,
360 void *Data1, void *Data2)
361{
362 IOData_t *io;
363 DataBuf *dbuf;
364
365 dReturn_if_fail( a_Chain_check("a_IO_ccc", Op, Branch, Dir, Info) );
366
367 if (Branch == 1) {
368 if (Dir == BCK) {
369 /* Write data using select */
370 switch (Op) {
371 case OpStart:
372 io = IO_new(IOWrite);
373 io->Info = Info;
374 Info->LocalKey = io;
375 break;
376 case OpSend:
377 io = Info->LocalKey;
378 if (Data2 && !strcmp(Data2, "FD")) {
379 io->FD = *(int*)Data1; /* SockFD */
380 } else {
381 dbuf = Data1;
382 dStr_append_l(io->Buf, dbuf->Buf, dbuf->Size);
383 IO_submit(io);
384 }
385 break;
386 case OpEnd:
387 case OpAbort:
388 io = Info->LocalKey;
389 if (io->Buf->len > 0) {
390 char *newline = memchr(io->Buf->str, '\n', io->Buf->len);
391 int msglen = newline ? newline - io->Buf->str : 2048;
392
393 MSG("IO_write, closing with pending data not sent: \"%s\"\n",
394 dStr_printable(io->Buf, msglen));
395 }
396 /* close FD, remove from ValidIOs and remove its watch */
397 IO_close_fd(io, Op == OpEnd ? IO_StopWr : IO_StopRdWr);
398 IO_free(io);
399 dFree(Info);
400 break;
401 default:
402 MSG_WARN("Unused CCC IO 1B\n");
403 break;
404 }
405 } else { /* 1 FWD */
406 /* Write-data status */
407 switch (Op) {
408 case OpAbort:
409 io = Info->LocalKey;
411 IO_free(io);
412 a_Chain_fcb(OpAbort, Info, NULL, NULL);
413 dFree(Info);
414 break;
415 default:
416 MSG_WARN("Unused CCC IO 1F\n");
417 break;
418 }
419 }
420
421 } else if (Branch == 2) {
422 if (Dir == BCK) {
423 /* This part catches the reader's messages */
424 switch (Op) {
425 case OpStart:
426 io = IO_new(IORead);
427 Info->LocalKey = io;
428 io->Info = Info;
429 break;
430 case OpSend:
431 io = Info->LocalKey;
432 if (Data2 && !strcmp(Data2, "FD")) {
433 io->FD = *(int*)Data1; /* SockFD */
434 IO_submit(io);
435 }
436 break;
437 case OpEnd:
438 case OpAbort:
439 io = Info->LocalKey;
440 IO_close_fd(io, Op == OpEnd ? IO_StopRd : IO_StopRdWr);
441 IO_free(io);
442 dFree(Info);
443 break;
444 default:
445 MSG_WARN("Unused CCC IO 2B\n");
446 break;
447 }
448 } else { /* 2 FWD */
449 /* Send read-data */
450 io = Data1;
451 switch (Op) {
452 case OpSend:
453 dbuf = a_Chain_dbuf_new(io->Buf->str, io->Buf->len, 0);
454 a_Chain_fcb(OpSend, Info, dbuf, NULL);
455 dFree(dbuf);
456 break;
457 case OpEnd:
458 case OpAbort:
459 a_Chain_fcb(Op, Info, NULL, NULL);
460 IO_close_fd(io, IO_StopRdWr); /* IO_StopRd would leak FDs */
461 IO_free(io);
462 dFree(Info);
463 break;
464 default:
465 MSG_WARN("Unused CCC IO 2F\n");
466 break;
467 }
468 }
469 }
470}
471
static void IO_del(IOData_t *io)
Remove an IO from ValidIOs.
Definition IO.c:92
static void IO_submit(IOData_t *r_io)
Receive an IO request (IORead | IOWrite), Set a watch for it, and let it flow!
Definition IO.c:328
static IOData_t * IO_get(int Key)
Return a io by its Key (NULL if not found)
Definition IO.c:104
static bool_t IO_write(IOData_t *io)
Write data, from a specific buffer, into a file descriptor.
Definition IO.c:219
#define IO_StopRd
Definition IO.c:30
static void IO_fd_read_cb(int fd, void *data)
Handle the READ event of a FD.
Definition IO.c:283
void a_IO_ccc(int Op, int Branch, int Dir, ChainLink *Info, void *Data1, void *Data2)
CCC function for the IO module.
Definition IO.c:359
static void IO_fd_write_cb(int fd, void *data)
Handle the WRITE event of a FD.
Definition IO.c:306
static void IO_close_fd(IOData_t *io, int CloseCode)
Close an open FD, and remove io controls.
Definition IO.c:125
#define IO_StopWr
Definition IO.c:31
#define IO_StopRdWr
Definition IO.c:32
static IOData_t * IO_new(int op)
Return a new, initialized, 'io' struct.
Definition IO.c:64
static bool_t IO_read(IOData_t *io)
Read data from a file descriptor into a specific buffer.
Definition IO.c:157
static Klist_t * ValidIOs
Definition IO.c:49
static void IO_ins(IOData_t *io)
Register an IO in ValidIOs.
Definition IO.c:80
static void IO_free(IOData_t *io)
Free an 'io' struct.
Definition IO.c:112
static int IO_callback(IOData_t *io)
Handle background IO for a given FD (reads | writes).
Definition IO.c:265
#define IOWrite
Definition IO.h:12
#define IORead
Definition IO.h:11
#define IOBufLen
Definition IO.h:19
#define _MSG(...)
Definition bookmarks.c:45
#define MSG(...)
Definition bookmarks.c:46
DataBuf * a_Chain_dbuf_new(void *buf, int size, int code)
Allocate and initialize a new DataBuf structure.
Definition chain.c:171
int a_Chain_check(char *FuncStr, int Op, int Branch, int Dir, ChainLink *Info)
Check whether the CCC is operative.
Definition chain.c:186
int a_Chain_fcb(int Op, ChainLink *Info, void *Data1, void *Data2)
Issue the forward callback of the 'Info' link.
Definition chain.c:103
#define OpAbort
Definition chain.h:17
#define FWD
Definition chain.h:29
#define OpStart
Definition chain.h:13
#define BCK
Definition chain.h:30
#define OpSend
Definition chain.h:14
#define OpEnd
Definition chain.h:16
unsigned char bool_t
Definition d_size.h:21
void dFree(void *mem)
Definition dlib.c:68
Dstr * dStr_sized_new(int sz)
Create a new string with a given size.
Definition dlib.c:254
void dStr_erase(Dstr *ds, int pos_0, int len)
Erase a substring.
Definition dlib.c:388
void dStr_free(Dstr *ds, int all)
Free a dillo string.
Definition dlib.c:337
int dClose(int fd)
Close a FD handling EINTR.
Definition dlib.c:951
void dStr_append_l(Dstr *ds, const char *s, int l)
Append a C string to a Dstr (providing length).
Definition dlib.c:308
const char * dStr_printable(Dstr *in, int maxlen)
Return a printable representation of the provided Dstr, limited to a length of roughly maxlen.
Definition dlib.c:513
void dStr_truncate(Dstr *ds, int len)
Truncate a Dstr to be 'len' bytes long.
Definition dlib.c:368
#define dReturn_if_fail(expr)
Definition dlib.h:72
#define dNew0(type, count)
Definition dlib.h:51
#define VOIDP2INT(p)
Definition dlib.h:43
#define TRUE
Definition dlib.h:23
#define FALSE
Definition dlib.h:19
#define INT2VOIDP(i)
Definition dlib.h:44
#define MSG_ERR(...)
Definition dpid_common.h:23
void a_IOwatch_add_fd(int fd, int when, Fl_FD_Handler Callback, void *usr_data=0)
Hook a Callback for a certain activities in a FD.
Definition iowatch.cc:22
void a_IOwatch_remove_fd(int fd, int when)
Remove a Callback for a given FD (or just remove some events)
Definition iowatch.cc:32
#define DIO_READ
Definition iowatch.hh:7
#define DIO_WRITE
Definition iowatch.hh:8
void a_Klist_remove(Klist_t *Klist, int Key)
Remove data by Key.
Definition klist.c:86
void * a_Klist_get_data(Klist_t *Klist, int Key)
Return the data pointer for a given Key (or NULL if not found)
Definition klist.c:43
int a_Klist_insert(Klist_t **Klist, void *Data)
Insert a data pointer and return a key for it.
Definition klist.c:56
int a_Klist_length(Klist_t *Klist)
Return the number of elements in the Klist.
Definition klist.c:102
#define MSG_WARN(...)
Definition msg.h:26
A convenience data structure for passing data chunks between nodes.
Definition chain.h:52
char * Buf
Definition chain.h:53
int Size
Definition chain.h:54
Definition dlib.h:102
int a_Tls_read(void *conn, void *buf, size_t len)
Definition tls.c:154
int a_Tls_write(void *conn, void *buf, size_t len)
Definition tls.c:167
void * a_Tls_connection(int fd)
Return TLS connection information for a given file descriptor, or NULL if no TLS connection was found...
Definition tls.c:48