Added SILC Thread Queue API
[silc.git] / lib / silcutil / silcfdstream.c
1 /*
2
3   silcfdstream.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2005 - 2007 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; version 2 of the License.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18 */
19
20 #include "silc.h"
21
22 /************************** Types and definitions ***************************/
23
24 #define SILC_IS_FD_STREAM(s) (s->ops == &silc_fd_stream_ops)
25
26 const SilcStreamOps silc_fd_stream_ops;
27
28 /* FD stream context */
29 typedef struct {
30   const SilcStreamOps *ops;
31   SilcStack stack;
32   SilcSchedule schedule;
33   SilcStreamNotifier notifier;
34   void *notifier_context;
35   int fd1;
36   int fd2;
37 } *SilcFDStream;
38
39
40 /************************ Static utility functions **************************/
41
42 /* The IO process callback that calls the notifier callback to upper layer. */
43
44 SILC_TASK_CALLBACK(silc_fd_stream_io)
45 {
46   SilcFDStream stream = context;
47
48   if (!stream->notifier)
49     return;
50
51   switch (type) {
52   case SILC_TASK_READ:
53     stream->notifier(stream, SILC_STREAM_CAN_READ, stream->notifier_context);
54     break;
55
56   case SILC_TASK_WRITE:
57     stream->notifier(stream, SILC_STREAM_CAN_WRITE, stream->notifier_context);
58     break;
59
60   default:
61     break;
62   }
63 }
64
65
66 /****************************** Public API **********************************/
67
68 /* Create file descriptor stream */
69
70 SilcStream silc_fd_stream_create(int fd, SilcStack stack)
71 {
72   if (fd < 1) {
73     silc_set_errno_reason(SILC_ERR_BAD_FD, "Bad file descriptor %d", fd);
74     return NULL;
75   }
76   return silc_fd_stream_create2(fd, 0, stack);
77 }
78
79 /* Create stream with two file descriptors */
80
81 SilcStream silc_fd_stream_create2(int read_fd, int write_fd, SilcStack stack)
82 {
83   SilcFDStream stream;
84
85   if (stack)
86     stack = silc_stack_alloc(0, stack);
87
88   stream = silc_scalloc(stack, 1, sizeof(*stream));
89   if (!stream) {
90     silc_stack_free(stack);
91     return NULL;
92   }
93
94   SILC_LOG_DEBUG(("Creating new fd stream %p", stream));
95
96   stream->ops = &silc_fd_stream_ops;
97   stream->fd1 = read_fd;
98   stream->fd2 = write_fd;
99   stream->stack = stack;
100
101   return stream;
102 }
103
104 /* Create by opening file */
105
106 SilcStream silc_fd_stream_file(const char *filename, SilcBool reading,
107                                SilcBool writing, SilcStack stack)
108 {
109   const char *read_file = NULL, *write_file = NULL;
110
111   if (!filename) {
112     silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
113     return NULL;
114   }
115
116   if (writing)
117     write_file = filename;
118   if (reading)
119     read_file = filename;
120
121   return silc_fd_stream_file2(read_file, write_file, stack);
122 }
123
124 /* Create by opening two files */
125
126 SilcStream silc_fd_stream_file2(const char *read_file, const char *write_file,
127                                 SilcStack stack)
128 {
129   SilcStream stream;
130   int fd1 = 0, fd2 = 0;
131
132   SILC_LOG_DEBUG(("Creating new fd stream for reading `%s' and writing `%s'",
133                   read_file ? read_file : "(none)",
134                   write_file ? write_file : "(none)"));
135
136   if (write_file) {
137     fd2 = silc_file_open(write_file, O_CREAT | O_WRONLY);
138     if (fd2 < 0) {
139       silc_file_close(fd1);
140       return NULL;
141     }
142   }
143
144   if (read_file) {
145     fd1 = silc_file_open(read_file, O_RDONLY);
146     if (fd1 < 0)
147       return NULL;
148   }
149
150   stream = silc_fd_stream_create2(fd1, fd2, stack);
151   if (!stream) {
152     silc_file_close(fd1);
153     silc_file_close(fd2);
154   }
155
156   return stream;
157 }
158
159 /* Return fds */
160
161 SilcBool silc_fd_stream_get_info(SilcStream stream, int *read_fd,
162                                  int *write_fd)
163 {
164   SilcFDStream fd_stream = stream;
165
166   if (!SILC_IS_FD_STREAM(fd_stream))
167     return FALSE;
168
169   if (read_fd)
170     *read_fd = fd_stream->fd1;
171   if (write_fd)
172     *write_fd = fd_stream->fd2;
173
174   return TRUE;
175 }
176
177 /* Read */
178
179 int silc_fd_stream_read(SilcStream stream, unsigned char *buf,
180                         SilcUInt32 buf_len)
181 {
182   SilcFDStream fd_stream = stream;
183   int len = 0;
184
185   if (!fd_stream->notifier)
186     return -2;
187
188   SILC_LOG_DEBUG(("Reading data from fd %d", fd_stream->fd1));
189
190   len = silc_file_read(fd_stream->fd1, buf, buf_len);
191   if (len < 0) {
192     if (errno == EAGAIN || errno == EINTR) {
193       SILC_LOG_DEBUG(("Could not read immediately, will do it later"));
194       silc_schedule_set_listen_fd(fd_stream->schedule, fd_stream->fd1,
195                                   SILC_TASK_READ, FALSE);
196       silc_set_errno_posix(errno);
197       return -1;
198     }
199     SILC_LOG_DEBUG(("Cannot read from fd: %d:%s",
200                     fd_stream->fd1, strerror(errno)));
201     silc_schedule_unset_listen_fd(fd_stream->schedule, fd_stream->fd1);
202     silc_set_errno_posix(errno);
203     return -2;
204   }
205
206   SILC_LOG_DEBUG(("Read %d bytes", len));
207
208   if (!len)
209     silc_schedule_unset_listen_fd(fd_stream->schedule, fd_stream->fd1);
210
211   return len;
212 }
213
214 /* Write */
215
216 int silc_fd_stream_write(SilcStream stream, const unsigned char *data,
217                          SilcUInt32 data_len)
218 {
219   SilcFDStream fd_stream = stream;
220   int ret;
221
222   if (!fd_stream->notifier)
223     return -2;
224
225   SILC_LOG_DEBUG(("Writing data to fd %d", fd_stream->fd2));
226
227   ret = silc_file_write(fd_stream->fd2, data, data_len);
228   if (ret < 0) {
229     if (errno == EAGAIN || errno == EINTR) {
230       SILC_LOG_DEBUG(("Could not write immediately, will do it later"));
231       silc_schedule_set_listen_fd(fd_stream->schedule, fd_stream->fd2,
232                                   SILC_TASK_READ | SILC_TASK_WRITE, FALSE);
233       silc_set_errno_posix(errno);
234       return -1;
235     }
236     SILC_LOG_DEBUG(("Cannot write to fd: %s", strerror(errno)));
237     silc_schedule_unset_listen_fd(fd_stream->schedule, fd_stream->fd2);
238     silc_set_errno_posix(errno);
239     return -2;
240   }
241
242   SILC_LOG_DEBUG(("Wrote data %d bytes", ret));
243
244   if (fd_stream->fd1 == fd_stream->fd2)
245     silc_schedule_set_listen_fd(fd_stream->schedule, fd_stream->fd2,
246                                 SILC_TASK_READ, FALSE);
247   else
248     silc_schedule_unset_listen_fd(fd_stream->schedule, fd_stream->fd2);
249
250   return ret;
251 }
252
253 /* Close stream */
254
255 SilcBool silc_fd_stream_close(SilcStream stream)
256 {
257   SilcFDStream fd_stream = stream;
258
259   if (fd_stream->fd1 > 0) {
260     silc_file_close(fd_stream->fd1);
261     if (fd_stream->schedule) {
262       silc_schedule_unset_listen_fd(fd_stream->schedule, fd_stream->fd1);
263       silc_schedule_task_del_by_fd(fd_stream->schedule, fd_stream->fd1);
264     }
265   }
266   if (fd_stream->fd2 > 0 && fd_stream->fd2 != fd_stream->fd1) {
267     silc_file_close(fd_stream->fd2);
268     if (fd_stream->schedule) {
269       silc_schedule_unset_listen_fd(fd_stream->schedule, fd_stream->fd2);
270       silc_schedule_task_del_by_fd(fd_stream->schedule, fd_stream->fd2);
271     }
272   }
273
274   return TRUE;
275 }
276
277 /* Destroy stream */
278
279 void silc_fd_stream_destroy(SilcStream stream)
280 {
281   SilcFDStream fd_stream = stream;
282   SilcStack stack = fd_stream->stack;
283
284   silc_fd_stream_close(stream);
285   silc_sfree(stack, stream);
286   silc_stack_free(stack);
287 }
288
289 /* Sets stream notification callback for the stream */
290
291 SilcBool silc_fd_stream_notifier(SilcStream stream,
292                                  SilcSchedule schedule,
293                                  SilcStreamNotifier callback,
294                                  void *context)
295 {
296   SilcFDStream fd_stream = stream;
297
298   SILC_LOG_DEBUG(("Setting stream notifier callback"));
299
300   fd_stream->notifier = callback;
301   fd_stream->notifier_context = context;
302   fd_stream->schedule = schedule;
303
304   /* Schedule the file descriptors */
305   if (schedule) {
306     if (fd_stream->fd2 > 0) {
307       silc_schedule_task_add_fd(schedule, fd_stream->fd2,
308                                 silc_fd_stream_io, stream);
309       silc_file_set_nonblock(fd_stream->fd2);
310     }
311     if (fd_stream->fd1 > 0) {
312       silc_schedule_task_add_fd(schedule, fd_stream->fd1,
313                                 silc_fd_stream_io, stream);
314       silc_schedule_set_listen_fd(schedule, fd_stream->fd1,
315                                   SILC_TASK_READ, FALSE);
316       silc_file_set_nonblock(fd_stream->fd1);
317       if (fd_stream->fd2 < 1)
318         fd_stream->fd2 = fd_stream->fd1;
319     }
320   } else {
321     if (fd_stream->schedule) {
322       silc_schedule_unset_listen_fd(fd_stream->schedule, fd_stream->fd1);
323       silc_schedule_unset_listen_fd(fd_stream->schedule, fd_stream->fd2);
324       silc_schedule_task_del_by_fd(fd_stream->schedule, fd_stream->fd1);
325       silc_schedule_task_del_by_fd(fd_stream->schedule, fd_stream->fd2);
326     }
327   }
328
329   return TRUE;
330 }
331
332 /* Return schedule */
333
334 SilcSchedule silc_fd_stream_get_schedule(SilcStream stream)
335 {
336   SilcFDStream fd_stream = stream;
337   return fd_stream->schedule;
338 }
339
340 /* File descriptor stream operations */
341 const SilcStreamOps silc_fd_stream_ops =
342 {
343   silc_fd_stream_read,
344   silc_fd_stream_write,
345   silc_fd_stream_close,
346   silc_fd_stream_destroy,
347   silc_fd_stream_notifier,
348   silc_fd_stream_get_schedule
349 };