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 
12 /*
13  * Dillo's event driven IO engine
14  */
15 
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 
25 /*
26  * Symbolic defines for shutdown() function
27  * (Not defined in the same header file, for all distros --Jcid)
28  */
29 #define IO_StopRd   1
30 #define IO_StopWr   2
31 #define IO_StopRdWr (IO_StopRd | IO_StopWr)
32 
33 
34 typedef struct {
35    int Key;               /* Primary Key (for klist) */
36    int Op;                /* IORead | IOWrite */
37    int FD;                /* Current File Descriptor */
38    int Flags;             /* Flag array (look definitions above) */
39    int Status;            /* errno code */
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  */
49 static Klist_t *ValidIOs = NULL; /* Active IOs list. It holds pointers to
50                                   * IOData_t structures. */
51 
52 /*
53  *  Forward declarations
54  */
55 void a_IO_ccc(int Op, int Branch, int Dir, ChainLink *Info,
56               void *Data1, void *Data2);
57 
58 
59 /* IO API  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
60 
61 /*
62  * Return a new, initialized, 'io' struct
63  */
IO_new(int op)64 static 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->Flags = 0;
70    io->Key = 0;
71    io->Buf = dStr_sized_new(IOBufLen);
72 
73    return io;
74 }
75 
76 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
77 
78 /*
79  * Register an IO in ValidIOs
80  */
IO_ins(IOData_t * io)81 static void IO_ins(IOData_t *io)
82 {
83    if (io->Key == 0) {
84       io->Key = a_Klist_insert(&ValidIOs, io);
85    }
86    _MSG("IO_ins: io->Key=%d, Klist_length=%d\n",
87        io->Key, a_Klist_length(ValidIOs));
88 }
89 
90 /*
91  * Remove an IO from ValidIOs
92  */
IO_del(IOData_t * io)93 static void IO_del(IOData_t *io)
94 {
95    if (io->Key != 0) {
96       a_Klist_remove(ValidIOs, io->Key);
97    }
98    io->Key = 0;
99    _MSG(" -->ValidIOs: %d\n", a_Klist_length(ValidIOs));
100 }
101 
102 /*
103  * Return a io by its Key (NULL if not found)
104  */
IO_get(int Key)105 static IOData_t *IO_get(int Key)
106 {
107    return (IOData_t *)a_Klist_get_data(ValidIOs, Key);
108 }
109 
110 /*
111  * Free an 'io' struct
112  */
IO_free(IOData_t * io)113 static void IO_free(IOData_t *io)
114 {
115    dStr_free(io->Buf, 1);
116    dFree(io);
117 }
118 
119 /*
120  * Close an open FD, and remove io controls.
121  * (This function can be used for Close and Abort operations)
122  * BUG: there's a race condition for Abort. The file descriptor is closed
123  * twice, and it could be reused for something else in between. It's simple
124  * to fix, but it'd be better to design a canonical way to Abort the CCC.
125  */
IO_close_fd(IOData_t * io,int CloseCode)126 static void IO_close_fd(IOData_t *io, int CloseCode)
127 {
128    int events = 0;
129 
130    _MSG("====> begin IO_close_fd (%d) Key=%d CloseCode=%d Flags=%d ",
131        io->FD, io->Key, CloseCode, io->Flags);
132 
133    /* With HTTP, if we close the writing part, the reading one also gets
134     * closed! (other clients may set 'IOFlag_ForceClose') */
135    if (((io->Flags & IOFlag_ForceClose) || (CloseCode == IO_StopRdWr)) &&
136        io->FD != -1) {
137       dClose(io->FD);
138    } else {
139       _MSG(" NOT CLOSING ");
140    }
141    /* Remove this IOData_t reference, from our ValidIOs list
142     * We don't deallocate it here, just remove from the list.*/
143    IO_del(io);
144 
145    /* Stop the polling on this FD */
146    if (CloseCode & IO_StopRd) {
147      events |= DIO_READ;
148    }
149    if (CloseCode & IO_StopWr) {
150      events |= DIO_WRITE;
151    }
152    a_IOwatch_remove_fd(io->FD, events);
153    _MSG(" end IO close (%d) <=====\n", io->FD);
154 }
155 
156 /*
157  * Read data from a file descriptor into a specific buffer
158  */
IO_read(IOData_t * io)159 static bool_t IO_read(IOData_t *io)
160 {
161    char Buf[IOBufLen];
162    ssize_t St;
163    bool_t ret = FALSE;
164    int io_key = io->Key;
165 
166    _MSG("  IO_read\n");
167 
168    /* this is a new read-buffer */
169    dStr_truncate(io->Buf, 0);
170    io->Status = 0;
171 
172    while (1) {
173       St = 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             io->Status = errno;
185             break;
186          }
187       } else { /* St == 0 */
188          break;
189       }
190    }
191 
192    if (io->Buf->len > 0) {
193       /* send what we've got so far */
194       a_IO_ccc(OpSend, 2, FWD, io->Info, io, NULL);
195    }
196    if (St == 0) {
197       /* TODO: design a general way to avoid reentrancy problems with CCC. */
198 
199       /* The following check is necessary because the above CCC operation
200        * may abort the whole Chain. */
201       if ((io = IO_get(io_key))) {
202          /* All data read (EOF) */
203          _MSG("IO_read: io->Key=%d io_key=%d\n", io->Key, io_key);
204          a_IO_ccc(OpEnd, 2, FWD, io->Info, io, NULL);
205       }
206    }
207    return ret;
208 }
209 
210 /*
211  * Write data, from a specific buffer, into a file descriptor
212  */
IO_write(IOData_t * io)213 static bool_t IO_write(IOData_t *io)
214 {
215    ssize_t St;
216    bool_t ret = FALSE;
217 
218    _MSG("  IO_write\n");
219    io->Status = 0;
220 
221    while (1) {
222       St = write(io->FD, io->Buf->str, io->Buf->len);
223       if (St < 0) {
224          /* Error */
225          if (errno == EINTR) {
226             continue;
227          } else if (errno == EAGAIN) {
228             ret = TRUE;
229             break;
230          } else {
231             io->Status = errno;
232             break;
233          }
234       } else if (St < io->Buf->len) {
235          /* Not all data written */
236          dStr_erase (io->Buf, 0, St);
237       } else {
238          /* All data in buffer written */
239          dStr_truncate(io->Buf, 0);
240          break;
241       }
242    }
243 
244    return ret;
245 }
246 
247 /*
248  * Handle background IO for a given FD (reads | writes)
249  * (This function gets called when there's activity in the FD)
250  */
IO_callback(IOData_t * io)251 static int IO_callback(IOData_t *io)
252 {
253    bool_t ret = FALSE;
254 
255    _MSG("IO_callback:: (%s) FD = %d\n",
256         (io->Op == IORead) ? "IORead" : "IOWrite", io->FD);
257 
258    if (io->Op == IORead) {          /* Read */
259       ret = IO_read(io);
260    } else if (io->Op == IOWrite) {  /* Write */
261       ret = IO_write(io);
262    }
263    return (ret) ? 1 : 0;
264 }
265 
266 /*
267  * Handle the READ event of a FD.
268  */
IO_fd_read_cb(int fd,void * data)269 static void IO_fd_read_cb(int fd, void *data)
270 {
271    int io_key = VOIDP2INT(data);
272    IOData_t *io = IO_get(io_key);
273 
274    /* There should be no more events on already closed FDs  --Jcid */
275    if (io == NULL) {
276       MSG_ERR("IO_fd_read_cb: call on already closed io!\n");
277       a_IOwatch_remove_fd(fd, DIO_READ);
278 
279    } else {
280       if (IO_callback(io) == 0)
281          a_IOwatch_remove_fd(fd, DIO_READ);
282    }
283 }
284 
285 /*
286  * Handle the WRITE event of a FD.
287  */
IO_fd_write_cb(int fd,void * data)288 static void IO_fd_write_cb(int fd, void *data)
289 {
290    int io_key = VOIDP2INT(data);
291    IOData_t *io = IO_get(io_key);
292 
293    if (io == NULL) {
294       /* There must be no more events on already closed FDs  --Jcid */
295       MSG_ERR("IO_fd_write_cb: call on already closed io!\n");
296       a_IOwatch_remove_fd(fd, DIO_WRITE);
297 
298    } else {
299       if (IO_callback(io) == 0)
300          a_IOwatch_remove_fd(fd, DIO_WRITE);
301    }
302 }
303 
304 /*
305  * Receive an IO request (IORead | IOWrite),
306  * Set a watch for it, and let it flow!
307  */
IO_submit(IOData_t * r_io)308 static void IO_submit(IOData_t *r_io)
309 {
310    if (r_io->FD < 0) {
311       MSG_ERR("IO_submit: FD not initialized\n");
312       return;
313    }
314 
315    /* Insert this IO in ValidIOs */
316    IO_ins(r_io);
317 
318    _MSG("IO_submit:: (%s) FD = %d\n",
319         (r_io->Op == IORead) ? "IORead" : "IOWrite", r_io->FD);
320 
321    /* Set FD to background and to close on exec. */
322    fcntl(r_io->FD, F_SETFL, O_NONBLOCK | fcntl(r_io->FD, F_GETFL));
323    fcntl(r_io->FD, F_SETFD, FD_CLOEXEC | fcntl(r_io->FD, F_GETFD));
324 
325    if (r_io->Op == IORead) {
326       a_IOwatch_add_fd(r_io->FD, DIO_READ,
327                        IO_fd_read_cb, INT2VOIDP(r_io->Key));
328 
329    } else if (r_io->Op == IOWrite) {
330       a_IOwatch_add_fd(r_io->FD, DIO_WRITE,
331                        IO_fd_write_cb, INT2VOIDP(r_io->Key));
332    }
333 }
334 
335 /*
336  * CCC function for the IO module
337  * ( Data1 = IOData_t* ; Data2 = NULL )
338  */
a_IO_ccc(int Op,int Branch,int Dir,ChainLink * Info,void * Data1,void * Data2)339 void a_IO_ccc(int Op, int Branch, int Dir, ChainLink *Info,
340               void *Data1, void *Data2)
341 {
342    IOData_t *io;
343    DataBuf *dbuf;
344 
345    dReturn_if_fail( a_Chain_check("a_IO_ccc", Op, Branch, Dir, Info) );
346 
347    if (Branch == 1) {
348       if (Dir == BCK) {
349          /* Write data using select */
350          switch (Op) {
351          case OpStart:
352             io = IO_new(IOWrite);
353             Info->LocalKey = io;
354             break;
355          case OpSend:
356             io = Info->LocalKey;
357             if (Data2 && !strcmp(Data2, "FD")) {
358                io->FD = *(int*)Data1; /* SockFD */
359             } else {
360                dbuf = Data1;
361                dStr_append_l(io->Buf, dbuf->Buf, dbuf->Size);
362                IO_submit(io);
363             }
364             break;
365          case OpEnd:
366          case OpAbort:
367             io = Info->LocalKey;
368             if (io->Buf->len > 0) {
369                char *newline = memchr(io->Buf->str, '\n', io->Buf->len);
370                int msglen = newline ? newline - io->Buf->str : 2048;
371 
372                MSG_WARN("IO_write, closing with pending data not sent: "
373                         "\"%s\"\n", dStr_printable(io->Buf, msglen));
374             }
375             /* close FD, remove from ValidIOs and remove its watch */
376             IO_close_fd(io, Op == OpEnd ? IO_StopWr : IO_StopRdWr);
377             IO_free(io);
378             dFree(Info);
379             break;
380          default:
381             MSG_WARN("Unused CCC\n");
382             break;
383          }
384       } else {  /* 1 FWD */
385          /* Write-data status */
386          switch (Op) {
387          default:
388             MSG_WARN("Unused CCC\n");
389             break;
390          }
391       }
392 
393    } else if (Branch == 2) {
394       if (Dir == BCK) {
395          /* This part catches the reader's messages */
396          switch (Op) {
397          case OpStart:
398             io = IO_new(IORead);
399             Info->LocalKey = io;
400             io->Info = Info;
401             break;
402          case OpSend:
403             io = Info->LocalKey;
404             if (Data2 && !strcmp(Data2, "FD")) {
405                io->FD = *(int*)Data1; /* SockFD */
406                IO_submit(io);
407             }
408             break;
409          case OpAbort:
410             io = Info->LocalKey;
411             IO_close_fd(io, IO_StopRdWr);
412             IO_free(io);
413             dFree(Info);
414             break;
415          default:
416             MSG_WARN("Unused CCC\n");
417             break;
418          }
419       } else {  /* 2 FWD */
420          /* Send read-data */
421          io = Data1;
422          switch (Op) {
423          case OpSend:
424             dbuf = a_Chain_dbuf_new(io->Buf->str, io->Buf->len, 0);
425             a_Chain_fcb(OpSend, Info, dbuf, NULL);
426             dFree(dbuf);
427             break;
428          case OpEnd:
429             a_Chain_fcb(OpEnd, Info, NULL, NULL);
430             IO_close_fd(io, IO_StopRdWr); /* IO_StopRd would leak FDs */
431             IO_free(io);
432             dFree(Info);
433             break;
434          default:
435             MSG_WARN("Unused CCC\n");
436             break;
437          }
438       }
439    }
440 }
441 
442