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;
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, SilcStack stack)
73 silc_set_errno_reason(SILC_ERR_BAD_FD, "Bad file descriptor %d", fd);
76 return silc_fd_stream_create2(fd, 0, stack);
79 /* Create stream with two file descriptors */
81 SilcStream silc_fd_stream_create2(int read_fd, int write_fd, SilcStack stack)
86 stack = silc_stack_alloc(0, stack);
88 stream = silc_scalloc(stack, 1, sizeof(*stream));
90 silc_stack_free(stack);
94 SILC_LOG_DEBUG(("Creating new fd stream %p", stream));
96 stream->ops = &silc_fd_stream_ops;
97 stream->fd1 = read_fd;
98 stream->fd2 = write_fd;
99 stream->stack = stack;
104 /* Create by opening file */
106 SilcStream silc_fd_stream_file(const char *filename, SilcBool reading,
107 SilcBool writing, SilcStack stack)
109 const char *read_file = NULL, *write_file = NULL;
112 silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
117 write_file = filename;
119 read_file = filename;
121 return silc_fd_stream_file2(read_file, write_file, stack);
124 /* Create by opening two files */
126 SilcStream silc_fd_stream_file2(const char *read_file, const char *write_file,
130 int fd1 = 0, fd2 = 0;
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)"));
137 fd2 = silc_file_open(write_file, O_CREAT | O_WRONLY);
139 silc_file_close(fd1);
145 fd1 = silc_file_open(read_file, O_RDONLY);
150 stream = silc_fd_stream_create2(fd1, fd2, stack);
152 silc_file_close(fd1);
153 silc_file_close(fd2);
161 SilcBool silc_fd_stream_get_info(SilcStream stream, int *read_fd,
164 SilcFDStream fd_stream = stream;
166 if (!SILC_IS_FD_STREAM(fd_stream))
170 *read_fd = fd_stream->fd1;
172 *write_fd = fd_stream->fd2;
179 int silc_fd_stream_read(SilcStream stream, unsigned char *buf,
182 SilcFDStream fd_stream = stream;
185 if (!fd_stream->notifier)
188 SILC_LOG_DEBUG(("Reading data from fd %d", fd_stream->fd1));
190 len = silc_file_read(fd_stream->fd1, buf, buf_len);
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);
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);
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);
233 silc_set_errno_posix(errno);
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);
242 SILC_LOG_DEBUG(("Wrote data %d bytes", ret));
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);
248 silc_schedule_unset_listen_fd(fd_stream->schedule, fd_stream->fd2);
255 SilcBool silc_fd_stream_close(SilcStream stream)
257 SilcFDStream fd_stream = stream;
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);
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);
279 void silc_fd_stream_destroy(SilcStream stream)
281 SilcFDStream fd_stream = stream;
282 SilcStack stack = fd_stream->stack;
284 silc_fd_stream_close(stream);
285 silc_sfree(stack, stream);
286 silc_stack_free(stack);
289 /* Sets stream notification callback for the stream */
291 SilcBool silc_fd_stream_notifier(SilcStream stream,
292 SilcSchedule schedule,
293 SilcStreamNotifier callback,
296 SilcFDStream fd_stream = stream;
298 SILC_LOG_DEBUG(("Setting stream notifier callback"));
300 fd_stream->notifier = callback;
301 fd_stream->notifier_context = context;
302 fd_stream->schedule = schedule;
304 /* Schedule the file descriptors */
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);
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;
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);
332 /* Return schedule */
334 SilcSchedule silc_fd_stream_get_schedule(SilcStream stream)
336 SilcFDStream fd_stream = stream;
337 return fd_stream->schedule;
340 /* File descriptor stream operations */
341 const SilcStreamOps silc_fd_stream_ops =
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