Added silc_file_set_nonblock. Fixed FD Stream. Comment fixes.
[silc.git] / lib / silcutil / silcfdstream.c
1 /*
2
3   silcfdstream.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2005 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 "silcincludes.h"
21
22 #define SILC_IS_FD_STREAM(s) (s->ops == &silc_fd_stream_ops)
23
24 const SilcStreamOps silc_fd_stream_ops;
25
26 /* FD stream context */
27 typedef struct {
28   const SilcStreamOps *ops;
29   SilcSchedule schedule;
30   SilcStreamNotifier notifier;
31   void *notifier_context;
32   int fd1;
33   int fd2;
34   int error;
35 } *SilcFDStream;
36
37 /* The IO process callback that calls the notifier callback to upper layer. */
38
39 SILC_TASK_CALLBACK(silc_fd_stream_io)
40 {
41   SilcFDStream stream = context;
42
43   if (!stream->notifier)
44     return;
45
46   switch (type) {
47   case SILC_TASK_WRITE:
48     stream->notifier(stream, SILC_STREAM_CAN_WRITE, stream->notifier_context);
49     break;
50
51   case SILC_TASK_READ:
52     stream->notifier(stream, SILC_STREAM_CAN_READ, stream->notifier_context);
53     break;
54
55   default:
56     break;
57   }
58 }
59
60 /* Create file descriptor stream */
61
62 SilcStream silc_fd_stream_create(int fd, SilcSchedule schedule)
63 {
64   if (fd < 1)
65     return NULL;
66   return silc_fd_stream_create2(fd, 0, schedule);
67 }
68
69 /* Create stream with two file descriptors */
70
71 SilcStream silc_fd_stream_create2(int read_fd, int write_fd,
72                                   SilcSchedule schedule)
73 {
74   SilcFDStream stream;
75
76   if (read_fd < 1)
77     return NULL;
78
79   stream = silc_calloc(1, sizeof(*stream));
80   if (!stream)
81     return NULL;
82
83   SILC_LOG_DEBUG(("Creating new fd stream %p", stream));
84
85   stream->ops = &silc_fd_stream_ops;
86   stream->schedule = schedule;
87   stream->fd1 = read_fd;
88   stream->fd2 = write_fd;
89
90   /* Schedule the file descriptors */
91   if (write_fd > 0) {
92     silc_schedule_task_add_fd(schedule, write_fd, silc_fd_stream_io, stream);
93     silc_file_set_nonblock(write_fd);
94   }
95   if (read_fd > 0) {
96     silc_schedule_task_add_fd(schedule, read_fd, silc_fd_stream_io, stream);
97     silc_schedule_set_listen_fd(schedule, read_fd, SILC_TASK_READ, FALSE);
98     silc_file_set_nonblock(read_fd);
99     if (write_fd < 1)
100       stream->fd2 = stream->fd1;
101   }
102
103   return stream;
104 }
105
106 /* Return fds */
107
108 SilcBool silc_fd_stream_get_info(SilcStream stream, int *read_fd, int *write_fd)
109 {
110   SilcFDStream fd_stream = stream;
111
112   if (!SILC_IS_FD_STREAM(fd_stream))
113     return FALSE;
114
115   if (read_fd)
116     *read_fd = fd_stream->fd1;
117   if (write_fd)
118     *write_fd = fd_stream->fd2;
119
120   return TRUE;
121 }
122
123 /* Return errno */
124
125 int silc_fd_stream_get_error(SilcStream stream)
126 {
127   SilcFDStream fd_stream = stream;
128
129   if (!SILC_IS_FD_STREAM(fd_stream))
130     return 0;
131
132   return fd_stream->error;
133 }
134
135 /* Read */
136
137 int silc_fd_stream_read(SilcStream stream, unsigned char *buf,
138                         SilcUInt32 buf_len)
139 {
140   SilcFDStream fd_stream = stream;
141   int len = 0;
142
143   if (!SILC_IS_FD_STREAM(fd_stream))
144     return -2;
145
146   SILC_LOG_DEBUG(("Reading data from fd %d", fd_stream->fd1));
147
148   len = silc_file_read(fd_stream->fd1, buf, buf_len);
149   if (len < 0) {
150     if (errno == EAGAIN || errno == EINTR) {
151       SILC_LOG_DEBUG(("Could not read immediately, will do it later"));
152       silc_schedule_set_listen_fd(fd_stream->schedule, fd_stream->fd1,
153                                   SILC_TASK_READ, FALSE);
154       return -1;
155     }
156     SILC_LOG_DEBUG(("Cannot read from fd: %d:%s",
157                     fd_stream->fd1, strerror(errno)));
158     silc_schedule_unset_listen_fd(fd_stream->schedule, fd_stream->fd1);
159     fd_stream->error = errno;
160     return -2;
161   }
162
163   SILC_LOG_DEBUG(("Read %d bytes", len));
164
165   if (!len)
166     silc_schedule_unset_listen_fd(fd_stream->schedule, fd_stream->fd1);
167
168   return len;
169 }
170
171 /* Write */
172
173 int silc_fd_stream_write(SilcStream stream, const unsigned char *data,
174                          SilcUInt32 data_len)
175 {
176   SilcFDStream fd_stream = stream;
177   int ret;
178
179   if (!SILC_IS_FD_STREAM(fd_stream))
180     return -2;
181
182   SILC_LOG_DEBUG(("Writing data to fd %d", fd_stream->fd2));
183
184   ret = silc_file_write(fd_stream->fd2, data, data_len);
185   if (ret < 0) {
186     if (errno == EAGAIN || errno == EINTR) {
187       SILC_LOG_DEBUG(("Could not write immediately, will do it later"));
188       silc_schedule_set_listen_fd(fd_stream->schedule, fd_stream->fd2,
189                                   SILC_TASK_READ | SILC_TASK_WRITE, FALSE);
190       return -1;
191     }
192     SILC_LOG_DEBUG(("Cannot write to fd: %s", strerror(errno)));
193     silc_schedule_unset_listen_fd(fd_stream->schedule, fd_stream->fd2);
194     fd_stream->error = errno;
195     return -2;
196   }
197
198   SILC_LOG_DEBUG(("Wrote data %d bytes", ret));
199
200   if (fd_stream->fd1 == fd_stream->fd2)
201     silc_schedule_set_listen_fd(fd_stream->schedule, fd_stream->fd2,
202                                 SILC_TASK_READ, FALSE);
203   else
204     silc_schedule_unset_listen_fd(fd_stream->schedule, fd_stream->fd2);
205
206   return ret;
207 }
208
209 /* Close stream */
210
211 SilcBool silc_fd_stream_close(SilcStream stream)
212 {
213   SilcFDStream fd_stream = stream;
214
215   if (!SILC_IS_FD_STREAM(fd_stream))
216     return FALSE;
217
218   if (fd_stream->fd1 > 0) {
219     silc_file_close(fd_stream->fd1);
220     silc_schedule_unset_listen_fd(fd_stream->schedule, fd_stream->fd1);
221   }
222   if (fd_stream->fd2 > 0 && fd_stream->fd2 != fd_stream->fd1) {
223     silc_file_close(fd_stream->fd2);
224     silc_schedule_unset_listen_fd(fd_stream->schedule, fd_stream->fd2);
225   }
226
227   return TRUE;
228 }
229
230 /* Destroy stream */
231
232 void silc_fd_stream_destroy(SilcStream stream)
233 {
234   SilcFDStream fd_stream = stream;
235
236   if (!SILC_IS_FD_STREAM(fd_stream))
237     return;
238
239   silc_fd_stream_close(stream);
240   silc_schedule_task_del_by_fd(fd_stream->schedule, fd_stream->fd1);
241   silc_schedule_task_del_by_fd(fd_stream->schedule, fd_stream->fd2);
242   silc_free(stream);
243 }
244
245 /* Sets stream notification callback for the stream */
246
247 void silc_fd_stream_notifier(SilcStream stream,
248                              SilcStreamNotifier callback,
249                              void *context)
250 {
251   SilcFDStream fd_stream = stream;
252
253   if (!SILC_IS_FD_STREAM(fd_stream))
254     return;
255
256   SILC_LOG_DEBUG(("Setting stream notifier callback"));
257
258   fd_stream->notifier = callback;
259   fd_stream->notifier_context = context;
260 }
261
262 /* File descriptor stream operations */
263 const SilcStreamOps silc_fd_stream_ops =
264 {
265   silc_fd_stream_read,
266   silc_fd_stream_write,
267   silc_fd_stream_close,
268   silc_fd_stream_destroy,
269   silc_fd_stream_notifier,
270 };