5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2005 - 2008 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 && s->ops == &silc_fd_stream_ops)
26 const SilcStreamOps silc_fd_stream_ops;
28 /* FD stream context */
30 const SilcStreamOps *ops;
31 SilcSchedule schedule;
32 SilcStreamNotifier notifier;
33 void *notifier_context;
40 /************************ Static utility functions **************************/
42 /* The IO process callback that calls the notifier callback to upper layer. */
44 SILC_TASK_CALLBACK(silc_fd_stream_io)
46 SilcFDStream stream = context;
48 if (!stream->notifier)
53 stream->notifier(stream, SILC_STREAM_CAN_READ, stream->notifier_context);
57 stream->notifier(stream, SILC_STREAM_CAN_WRITE, stream->notifier_context);
66 /****************************** Public API **********************************/
68 /* Create file descriptor stream */
70 SilcStream silc_fd_stream_create(int fd)
74 return silc_fd_stream_create2(fd, 0);
77 /* Create stream with two file descriptors */
79 SilcStream silc_fd_stream_create2(int read_fd, int write_fd)
83 stream = silc_calloc(1, sizeof(*stream));
87 SILC_LOG_DEBUG(("Creating new fd stream %p", stream));
89 stream->ops = &silc_fd_stream_ops;
90 stream->fd1 = read_fd;
91 stream->fd2 = write_fd;
96 /* Create by opening file */
98 SilcStream silc_fd_stream_file(const char *filename, SilcBool reading,
101 const char *read_file = NULL, *write_file = NULL;
107 write_file = filename;
109 read_file = filename;
111 return silc_fd_stream_file2(read_file, write_file);
114 /* Create by opening two files */
116 SilcStream silc_fd_stream_file2(const char *read_file, const char *write_file)
119 int fd1 = 0, fd2 = 0;
121 SILC_LOG_DEBUG(("Creating new fd stream for reading `%s' and writing `%s'",
122 read_file ? read_file : "(none)",
123 write_file ? write_file : "(none)"));
126 fd2 = silc_file_open(write_file, O_CREAT | O_WRONLY);
128 silc_file_close(fd1);
134 fd1 = silc_file_open(read_file, O_RDONLY);
139 stream = silc_fd_stream_create2(fd1, fd2);
141 silc_file_close(fd1);
142 silc_file_close(fd2);
150 SilcBool silc_fd_stream_get_info(SilcStream stream, int *read_fd,
153 SilcFDStream fd_stream = stream;
155 if (!SILC_IS_FD_STREAM(fd_stream))
159 *read_fd = fd_stream->fd1;
161 *write_fd = fd_stream->fd2;
168 int silc_fd_stream_get_error(SilcStream stream)
170 SilcFDStream fd_stream = stream;
172 if (!SILC_IS_FD_STREAM(fd_stream))
175 return fd_stream->error;
180 int silc_fd_stream_read(SilcStream stream, unsigned char *buf,
183 SilcFDStream fd_stream = stream;
186 if (!fd_stream->notifier)
189 SILC_LOG_DEBUG(("Reading data from fd %d", fd_stream->fd1));
191 len = silc_file_read(fd_stream->fd1, buf, buf_len);
193 if (errno == EAGAIN || errno == EINTR) {
194 SILC_LOG_DEBUG(("Could not read immediately, will do it later"));
195 silc_schedule_set_listen_fd(fd_stream->schedule, fd_stream->fd1,
196 SILC_TASK_READ, FALSE);
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 fd_stream->error = errno;
206 SILC_LOG_DEBUG(("Read %d bytes", len));
209 silc_schedule_unset_listen_fd(fd_stream->schedule, fd_stream->fd1);
216 int silc_fd_stream_write(SilcStream stream, const unsigned char *data,
219 SilcFDStream fd_stream = stream;
222 if (!fd_stream->notifier)
225 SILC_LOG_DEBUG(("Writing data to fd %d", fd_stream->fd2));
227 ret = silc_file_write(fd_stream->fd2, data, data_len);
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);
235 SILC_LOG_DEBUG(("Cannot write to fd: %s", strerror(errno)));
236 silc_schedule_unset_listen_fd(fd_stream->schedule, fd_stream->fd2);
237 fd_stream->error = errno;
241 SILC_LOG_DEBUG(("Wrote data %d bytes", ret));
243 if (fd_stream->fd1 == fd_stream->fd2)
244 silc_schedule_set_listen_fd(fd_stream->schedule, fd_stream->fd2,
245 SILC_TASK_READ, FALSE);
247 silc_schedule_unset_listen_fd(fd_stream->schedule, fd_stream->fd2);
254 SilcBool silc_fd_stream_close(SilcStream stream)
256 SilcFDStream fd_stream = stream;
258 if (fd_stream->fd1 > 0) {
259 silc_file_close(fd_stream->fd1);
260 if (fd_stream->schedule) {
261 silc_schedule_unset_listen_fd(fd_stream->schedule, fd_stream->fd1);
262 silc_schedule_task_del_by_fd(fd_stream->schedule, fd_stream->fd1);
265 if (fd_stream->fd2 > 0 && fd_stream->fd2 != fd_stream->fd1) {
266 silc_file_close(fd_stream->fd2);
267 if (fd_stream->schedule) {
268 silc_schedule_unset_listen_fd(fd_stream->schedule, fd_stream->fd2);
269 silc_schedule_task_del_by_fd(fd_stream->schedule, fd_stream->fd2);
278 void silc_fd_stream_destroy(SilcStream stream)
280 silc_fd_stream_close(stream);
284 /* Sets stream notification callback for the stream */
286 SilcBool silc_fd_stream_notifier(SilcStream stream,
287 SilcSchedule schedule,
288 SilcStreamNotifier callback,
291 SilcFDStream fd_stream = stream;
293 SILC_LOG_DEBUG(("Setting stream notifier callback"));
295 fd_stream->notifier = callback;
296 fd_stream->notifier_context = context;
297 fd_stream->schedule = schedule;
299 /* Schedule the file descriptors */
301 if (fd_stream->fd2 > 0) {
302 silc_schedule_task_add_fd(schedule, fd_stream->fd2,
303 silc_fd_stream_io, stream);
304 silc_file_set_nonblock(fd_stream->fd2);
306 if (fd_stream->fd1 > 0) {
307 silc_schedule_task_add_fd(schedule, fd_stream->fd1,
308 silc_fd_stream_io, stream);
309 silc_schedule_set_listen_fd(schedule, fd_stream->fd1,
310 SILC_TASK_READ, FALSE);
311 silc_file_set_nonblock(fd_stream->fd1);
312 if (fd_stream->fd2 < 1)
313 fd_stream->fd2 = fd_stream->fd1;
316 if (fd_stream->schedule) {
317 silc_schedule_unset_listen_fd(fd_stream->schedule, fd_stream->fd1);
318 silc_schedule_unset_listen_fd(fd_stream->schedule, fd_stream->fd2);
319 silc_schedule_task_del_by_fd(fd_stream->schedule, fd_stream->fd1);
320 silc_schedule_task_del_by_fd(fd_stream->schedule, fd_stream->fd2);
327 /* Return schedule */
329 SilcSchedule silc_fd_stream_get_schedule(SilcStream stream)
331 SilcFDStream fd_stream = stream;
332 return fd_stream->schedule;
335 /* File descriptor stream operations */
336 const SilcStreamOps silc_fd_stream_ops =
339 silc_fd_stream_write,
340 silc_fd_stream_close,
341 silc_fd_stream_destroy,
342 silc_fd_stream_notifier,
343 silc_fd_stream_get_schedule