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