5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2005 - 2007 Pekka Riikonen
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.
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.
22 /************************** Types and definitions ***************************/
24 #define SILC_IS_FD_STREAM(s) (s->ops == &silc_fd_stream_ops)
26 const SilcStreamOps silc_fd_stream_ops;
28 /* FD stream context */
30 const SilcStreamOps *ops;
32 SilcSchedule schedule;
33 SilcStreamNotifier notifier;
34 void *notifier_context;
41 /************************ Static utility functions **************************/
43 /* The IO process callback that calls the notifier callback to upper layer. */
45 SILC_TASK_CALLBACK(silc_fd_stream_io)
47 SilcFDStream stream = context;
49 if (!stream->notifier)
54 stream->notifier(stream, SILC_STREAM_CAN_READ, stream->notifier_context);
58 stream->notifier(stream, SILC_STREAM_CAN_WRITE, stream->notifier_context);
67 /****************************** Public API **********************************/
69 /* Create file descriptor stream */
71 SilcStream silc_fd_stream_create(int fd, SilcStack stack)
75 return silc_fd_stream_create2(fd, 0, stack);
78 /* Create stream with two file descriptors */
80 SilcStream silc_fd_stream_create2(int read_fd, int write_fd, SilcStack stack)
85 stack = silc_stack_alloc(0, stack);
87 stream = silc_scalloc(stack, 1, sizeof(*stream));
89 silc_stack_free(stack);
93 SILC_LOG_DEBUG(("Creating new fd stream %p", stream));
95 stream->ops = &silc_fd_stream_ops;
96 stream->fd1 = read_fd;
97 stream->fd2 = write_fd;
98 stream->stack = stack;
103 /* Create by opening file */
105 SilcStream silc_fd_stream_file(const char *filename, SilcBool reading,
106 SilcBool writing, SilcStack stack)
108 const char *read_file = NULL, *write_file = NULL;
114 write_file = filename;
116 read_file = filename;
118 return silc_fd_stream_file2(read_file, write_file, stack);
121 /* Create by opening two files */
123 SilcStream silc_fd_stream_file2(const char *read_file, const char *write_file,
127 int fd1 = 0, fd2 = 0;
129 SILC_LOG_DEBUG(("Creating new fd stream for reading `%s' and writing `%s'",
130 read_file ? read_file : "(none)",
131 write_file ? write_file : "(none)"));
134 fd2 = silc_file_open(write_file, O_CREAT | O_WRONLY);
136 silc_file_close(fd1);
142 fd1 = silc_file_open(read_file, O_RDONLY);
147 stream = silc_fd_stream_create2(fd1, fd2, stack);
149 silc_file_close(fd1);
150 silc_file_close(fd2);
158 SilcBool silc_fd_stream_get_info(SilcStream stream, int *read_fd,
161 SilcFDStream fd_stream = stream;
163 if (!SILC_IS_FD_STREAM(fd_stream))
167 *read_fd = fd_stream->fd1;
169 *write_fd = fd_stream->fd2;
176 int silc_fd_stream_get_error(SilcStream stream)
178 SilcFDStream fd_stream = stream;
180 if (!SILC_IS_FD_STREAM(fd_stream))
183 return fd_stream->error;
188 int silc_fd_stream_read(SilcStream stream, unsigned char *buf,
191 SilcFDStream fd_stream = stream;
194 if (!fd_stream->notifier)
197 SILC_LOG_DEBUG(("Reading data from fd %d", fd_stream->fd1));
199 len = silc_file_read(fd_stream->fd1, buf, buf_len);
201 if (errno == EAGAIN || errno == EINTR) {
202 SILC_LOG_DEBUG(("Could not read immediately, will do it later"));
203 silc_schedule_set_listen_fd(fd_stream->schedule, fd_stream->fd1,
204 SILC_TASK_READ, FALSE);
207 SILC_LOG_DEBUG(("Cannot read from fd: %d:%s",
208 fd_stream->fd1, strerror(errno)));
209 silc_schedule_unset_listen_fd(fd_stream->schedule, fd_stream->fd1);
210 fd_stream->error = errno;
214 SILC_LOG_DEBUG(("Read %d bytes", len));
217 silc_schedule_unset_listen_fd(fd_stream->schedule, fd_stream->fd1);
224 int silc_fd_stream_write(SilcStream stream, const unsigned char *data,
227 SilcFDStream fd_stream = stream;
230 if (!fd_stream->notifier)
233 SILC_LOG_DEBUG(("Writing data to fd %d", fd_stream->fd2));
235 ret = silc_file_write(fd_stream->fd2, data, data_len);
237 if (errno == EAGAIN || errno == EINTR) {
238 SILC_LOG_DEBUG(("Could not write immediately, will do it later"));
239 silc_schedule_set_listen_fd(fd_stream->schedule, fd_stream->fd2,
240 SILC_TASK_READ | SILC_TASK_WRITE, FALSE);
243 SILC_LOG_DEBUG(("Cannot write to fd: %s", strerror(errno)));
244 silc_schedule_unset_listen_fd(fd_stream->schedule, fd_stream->fd2);
245 fd_stream->error = errno;
249 SILC_LOG_DEBUG(("Wrote data %d bytes", ret));
251 if (fd_stream->fd1 == fd_stream->fd2)
252 silc_schedule_set_listen_fd(fd_stream->schedule, fd_stream->fd2,
253 SILC_TASK_READ, FALSE);
255 silc_schedule_unset_listen_fd(fd_stream->schedule, fd_stream->fd2);
262 SilcBool silc_fd_stream_close(SilcStream stream)
264 SilcFDStream fd_stream = stream;
266 if (fd_stream->fd1 > 0) {
267 silc_file_close(fd_stream->fd1);
268 if (fd_stream->schedule) {
269 silc_schedule_unset_listen_fd(fd_stream->schedule, fd_stream->fd1);
270 silc_schedule_task_del_by_fd(fd_stream->schedule, fd_stream->fd1);
273 if (fd_stream->fd2 > 0 && fd_stream->fd2 != fd_stream->fd1) {
274 silc_file_close(fd_stream->fd2);
275 if (fd_stream->schedule) {
276 silc_schedule_unset_listen_fd(fd_stream->schedule, fd_stream->fd2);
277 silc_schedule_task_del_by_fd(fd_stream->schedule, fd_stream->fd2);
286 void silc_fd_stream_destroy(SilcStream stream)
288 SilcFDStream fd_stream = stream;
289 SilcStack stack = fd_stream->stack;
291 silc_fd_stream_close(stream);
292 silc_sfree(stack, stream);
293 silc_stack_free(stack);
296 /* Sets stream notification callback for the stream */
298 SilcBool silc_fd_stream_notifier(SilcStream stream,
299 SilcSchedule schedule,
300 SilcStreamNotifier callback,
303 SilcFDStream fd_stream = stream;
305 SILC_LOG_DEBUG(("Setting stream notifier callback"));
307 fd_stream->notifier = callback;
308 fd_stream->notifier_context = context;
309 fd_stream->schedule = schedule;
311 /* Schedule the file descriptors */
313 if (fd_stream->fd2 > 0) {
314 silc_schedule_task_add_fd(schedule, fd_stream->fd2,
315 silc_fd_stream_io, stream);
316 silc_file_set_nonblock(fd_stream->fd2);
318 if (fd_stream->fd1 > 0) {
319 silc_schedule_task_add_fd(schedule, fd_stream->fd1,
320 silc_fd_stream_io, stream);
321 silc_schedule_set_listen_fd(schedule, fd_stream->fd1,
322 SILC_TASK_READ, FALSE);
323 silc_file_set_nonblock(fd_stream->fd1);
324 if (fd_stream->fd2 < 1)
325 fd_stream->fd2 = fd_stream->fd1;
328 if (fd_stream->schedule) {
329 silc_schedule_unset_listen_fd(fd_stream->schedule, fd_stream->fd1);
330 silc_schedule_unset_listen_fd(fd_stream->schedule, fd_stream->fd2);
331 silc_schedule_task_del_by_fd(fd_stream->schedule, fd_stream->fd1);
332 silc_schedule_task_del_by_fd(fd_stream->schedule, fd_stream->fd2);
339 /* Return schedule */
341 SilcSchedule silc_fd_stream_get_schedule(SilcStream stream)
343 SilcFDStream fd_stream = stream;
344 return fd_stream->schedule;
347 /* File descriptor stream operations */
348 const SilcStreamOps silc_fd_stream_ops =
351 silc_fd_stream_write,
352 silc_fd_stream_close,
353 silc_fd_stream_destroy,
354 silc_fd_stream_notifier,
355 silc_fd_stream_get_schedule