Added silc_thread_yield.
[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   SilcSchedule schedule;
32   SilcStreamNotifier notifier;
33   void *notifier_context;
34   int fd1;
35   int fd2;
36   int error;
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)
71 {
72   if (fd < 1)
73     return NULL;
74   return silc_fd_stream_create2(fd, 0);
75 }
76
77 /* Create stream with two file descriptors */
78
79 SilcStream silc_fd_stream_create2(int read_fd, int write_fd)
80 {
81   SilcFDStream stream;
82
83   if (read_fd < 1)
84     return NULL;
85
86   stream = silc_calloc(1, sizeof(*stream));
87   if (!stream)
88     return NULL;
89
90   SILC_LOG_DEBUG(("Creating new fd stream %p", stream));
91
92   stream->ops = &silc_fd_stream_ops;
93   stream->fd1 = read_fd;
94   stream->fd2 = write_fd;
95
96   return stream;
97 }
98
99 /* Create by opening file */
100
101 SilcStream silc_fd_stream_file(const char *filename,
102                                SilcBool reading, SilcBool writing)
103 {
104   int fd, flags = 0;
105   SilcStream stream;
106
107   if (!filename)
108     return NULL;
109
110   SILC_LOG_DEBUG(("Creating new fd stream for file `%s'", filename));
111
112   if (reading)
113     flags |= O_RDONLY;
114   if (writing)
115     flags |= O_CREAT | O_WRONLY;
116   if (reading && writing)
117     flags = O_CREAT | O_RDWR;
118
119   fd = silc_file_open(filename, flags);
120   if (fd < 0)
121     return NULL;
122
123   stream = silc_fd_stream_create(fd);
124   if (!stream)
125     silc_file_close(fd);
126
127   return stream;
128 }
129
130 /* Return fds */
131
132 SilcBool silc_fd_stream_get_info(SilcStream stream, int *read_fd,
133                                  int *write_fd)
134 {
135   SilcFDStream fd_stream = stream;
136
137   if (!SILC_IS_FD_STREAM(fd_stream))
138     return FALSE;
139
140   if (read_fd)
141     *read_fd = fd_stream->fd1;
142   if (write_fd)
143     *write_fd = fd_stream->fd2;
144
145   return TRUE;
146 }
147
148 /* Return errno */
149
150 int silc_fd_stream_get_error(SilcStream stream)
151 {
152   SilcFDStream fd_stream = stream;
153
154   if (!SILC_IS_FD_STREAM(fd_stream))
155     return 0;
156
157   return fd_stream->error;
158 }
159
160 /* Read */
161
162 int silc_fd_stream_read(SilcStream stream, unsigned char *buf,
163                         SilcUInt32 buf_len)
164 {
165   SilcFDStream fd_stream = stream;
166   int len = 0;
167
168   if (!fd_stream->notifier)
169     return -2;
170
171   SILC_LOG_DEBUG(("Reading data from fd %d", fd_stream->fd1));
172
173   len = silc_file_read(fd_stream->fd1, buf, buf_len);
174   if (len < 0) {
175     if (errno == EAGAIN || errno == EINTR) {
176       SILC_LOG_DEBUG(("Could not read immediately, will do it later"));
177       silc_schedule_set_listen_fd(fd_stream->schedule, fd_stream->fd1,
178                                   SILC_TASK_READ, FALSE);
179       return -1;
180     }
181     SILC_LOG_DEBUG(("Cannot read from fd: %d:%s",
182                     fd_stream->fd1, strerror(errno)));
183     silc_schedule_unset_listen_fd(fd_stream->schedule, fd_stream->fd1);
184     fd_stream->error = errno;
185     return -2;
186   }
187
188   SILC_LOG_DEBUG(("Read %d bytes", len));
189
190   if (!len)
191     silc_schedule_unset_listen_fd(fd_stream->schedule, fd_stream->fd1);
192
193   return len;
194 }
195
196 /* Write */
197
198 int silc_fd_stream_write(SilcStream stream, const unsigned char *data,
199                          SilcUInt32 data_len)
200 {
201   SilcFDStream fd_stream = stream;
202   int ret;
203
204   if (!fd_stream->notifier)
205     return -2;
206
207   SILC_LOG_DEBUG(("Writing data to fd %d", fd_stream->fd2));
208
209   ret = silc_file_write(fd_stream->fd2, data, data_len);
210   if (ret < 0) {
211     if (errno == EAGAIN || errno == EINTR) {
212       SILC_LOG_DEBUG(("Could not write immediately, will do it later"));
213       silc_schedule_set_listen_fd(fd_stream->schedule, fd_stream->fd2,
214                                   SILC_TASK_READ | SILC_TASK_WRITE, FALSE);
215       return -1;
216     }
217     SILC_LOG_DEBUG(("Cannot write to fd: %s", strerror(errno)));
218     silc_schedule_unset_listen_fd(fd_stream->schedule, fd_stream->fd2);
219     fd_stream->error = errno;
220     return -2;
221   }
222
223   SILC_LOG_DEBUG(("Wrote data %d bytes", ret));
224
225   if (fd_stream->fd1 == fd_stream->fd2)
226     silc_schedule_set_listen_fd(fd_stream->schedule, fd_stream->fd2,
227                                 SILC_TASK_READ, FALSE);
228   else
229     silc_schedule_unset_listen_fd(fd_stream->schedule, fd_stream->fd2);
230
231   return ret;
232 }
233
234 /* Close stream */
235
236 SilcBool silc_fd_stream_close(SilcStream stream)
237 {
238   SilcFDStream fd_stream = stream;
239
240   if (fd_stream->fd1 > 0) {
241     silc_file_close(fd_stream->fd1);
242     if (fd_stream->schedule) {
243       silc_schedule_unset_listen_fd(fd_stream->schedule, fd_stream->fd1);
244       silc_schedule_task_del_by_fd(fd_stream->schedule, fd_stream->fd1);
245     }
246   }
247   if (fd_stream->fd2 > 0 && fd_stream->fd2 != fd_stream->fd1) {
248     silc_file_close(fd_stream->fd2);
249     if (fd_stream->schedule) {
250       silc_schedule_unset_listen_fd(fd_stream->schedule, fd_stream->fd2);
251       silc_schedule_task_del_by_fd(fd_stream->schedule, fd_stream->fd2);
252     }
253   }
254
255   return TRUE;
256 }
257
258 /* Destroy stream */
259
260 void silc_fd_stream_destroy(SilcStream stream)
261 {
262   silc_fd_stream_close(stream);
263   silc_free(stream);
264 }
265
266 /* Sets stream notification callback for the stream */
267
268 SilcBool silc_fd_stream_notifier(SilcStream stream,
269                                  SilcSchedule schedule,
270                                  SilcStreamNotifier callback,
271                                  void *context)
272 {
273   SilcFDStream fd_stream = stream;
274
275   SILC_LOG_DEBUG(("Setting stream notifier callback"));
276
277   fd_stream->notifier = callback;
278   fd_stream->notifier_context = context;
279   fd_stream->schedule = schedule;
280
281   /* Schedule the file descriptors */
282   if (schedule) {
283     if (fd_stream->fd2 > 0) {
284       silc_schedule_task_add_fd(schedule, fd_stream->fd2,
285                                 silc_fd_stream_io, stream);
286       silc_file_set_nonblock(fd_stream->fd2);
287     }
288     if (fd_stream->fd1 > 0) {
289       silc_schedule_task_add_fd(schedule, fd_stream->fd1,
290                                 silc_fd_stream_io, stream);
291       silc_schedule_set_listen_fd(schedule, fd_stream->fd1,
292                                   SILC_TASK_READ, FALSE);
293       silc_file_set_nonblock(fd_stream->fd1);;
294       if (fd_stream->fd2 < 1)
295         fd_stream->fd2 = fd_stream->fd1;
296     }
297   } else {
298     if (fd_stream->schedule) {
299       silc_schedule_unset_listen_fd(fd_stream->schedule, fd_stream->fd1);
300       silc_schedule_unset_listen_fd(fd_stream->schedule, fd_stream->fd2);
301       silc_schedule_task_del_by_fd(fd_stream->schedule, fd_stream->fd1);
302       silc_schedule_task_del_by_fd(fd_stream->schedule, fd_stream->fd2);
303     }
304   }
305
306   return TRUE;
307 }
308
309 /* Return schedule */
310
311 SilcSchedule silc_fd_stream_get_schedule(SilcStream stream)
312 {
313   SilcFDStream fd_stream = stream;
314   return fd_stream->schedule;
315 }
316
317 /* File descriptor stream operations */
318 const SilcStreamOps silc_fd_stream_ops =
319 {
320   silc_fd_stream_read,
321   silc_fd_stream_write,
322   silc_fd_stream_close,
323   silc_fd_stream_destroy,
324   silc_fd_stream_notifier,
325   silc_fd_stream_get_schedule
326 };