SILC_IS_FD_STREAM and SILC_IS_SOCKET_STREAM now checks for NULL stream.
[silc.git] / lib / silcutil / silcfdstream.c
1 /*
2
3   silcfdstream.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2005 - 2008 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 && 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   stream = silc_calloc(1, sizeof(*stream));
84   if (!stream)
85     return NULL;
86
87   SILC_LOG_DEBUG(("Creating new fd stream %p", stream));
88
89   stream->ops = &silc_fd_stream_ops;
90   stream->fd1 = read_fd;
91   stream->fd2 = write_fd;
92
93   return stream;
94 }
95
96 /* Create by opening file */
97
98 SilcStream silc_fd_stream_file(const char *filename, SilcBool reading,
99                                SilcBool writing)
100 {
101   const char *read_file = NULL, *write_file = NULL;
102
103   if (!filename)
104     return NULL;
105
106   if (writing)
107     write_file = filename;
108   if (reading)
109     read_file = filename;
110
111   return silc_fd_stream_file2(read_file, write_file);
112 }
113
114 /* Create by opening two files */
115
116 SilcStream silc_fd_stream_file2(const char *read_file, const char *write_file)
117 {
118   SilcStream stream;
119   int fd1 = 0, fd2 = 0;
120
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)"));
124
125   if (write_file) {
126     fd2 = silc_file_open(write_file, O_CREAT | O_WRONLY);
127     if (fd2 < 0) {
128       silc_file_close(fd1);
129       return NULL;
130     }
131   }
132
133   if (read_file) {
134     fd1 = silc_file_open(read_file, O_RDONLY);
135     if (fd1 < 0)
136       return NULL;
137   }
138
139   stream = silc_fd_stream_create2(fd1, fd2);
140   if (!stream) {
141     silc_file_close(fd1);
142     silc_file_close(fd2);
143   }
144
145   return stream;
146 }
147
148 /* Return fds */
149
150 SilcBool silc_fd_stream_get_info(SilcStream stream, int *read_fd,
151                                  int *write_fd)
152 {
153   SilcFDStream fd_stream = stream;
154
155   if (!SILC_IS_FD_STREAM(fd_stream))
156     return FALSE;
157
158   if (read_fd)
159     *read_fd = fd_stream->fd1;
160   if (write_fd)
161     *write_fd = fd_stream->fd2;
162
163   return TRUE;
164 }
165
166 /* Return errno */
167
168 int silc_fd_stream_get_error(SilcStream stream)
169 {
170   SilcFDStream fd_stream = stream;
171
172   if (!SILC_IS_FD_STREAM(fd_stream))
173     return 0;
174
175   return fd_stream->error;
176 }
177
178 /* Read */
179
180 int silc_fd_stream_read(SilcStream stream, unsigned char *buf,
181                         SilcUInt32 buf_len)
182 {
183   SilcFDStream fd_stream = stream;
184   int len = 0;
185
186   if (!fd_stream->notifier)
187     return -2;
188
189   SILC_LOG_DEBUG(("Reading data from fd %d", fd_stream->fd1));
190
191   len = silc_file_read(fd_stream->fd1, buf, buf_len);
192   if (len < 0) {
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);
197       return -1;
198     }
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;
203     return -2;
204   }
205
206   SILC_LOG_DEBUG(("Read %d bytes", len));
207
208   if (!len)
209     silc_schedule_unset_listen_fd(fd_stream->schedule, fd_stream->fd1);
210
211   return len;
212 }
213
214 /* Write */
215
216 int silc_fd_stream_write(SilcStream stream, const unsigned char *data,
217                          SilcUInt32 data_len)
218 {
219   SilcFDStream fd_stream = stream;
220   int ret;
221
222   if (!fd_stream->notifier)
223     return -2;
224
225   SILC_LOG_DEBUG(("Writing data to fd %d", fd_stream->fd2));
226
227   ret = silc_file_write(fd_stream->fd2, data, data_len);
228   if (ret < 0) {
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       return -1;
234     }
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;
238     return -2;
239   }
240
241   SILC_LOG_DEBUG(("Wrote data %d bytes", ret));
242
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);
246   else
247     silc_schedule_unset_listen_fd(fd_stream->schedule, fd_stream->fd2);
248
249   return ret;
250 }
251
252 /* Close stream */
253
254 SilcBool silc_fd_stream_close(SilcStream stream)
255 {
256   SilcFDStream fd_stream = stream;
257
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);
263     }
264   }
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);
270     }
271   }
272
273   return TRUE;
274 }
275
276 /* Destroy stream */
277
278 void silc_fd_stream_destroy(SilcStream stream)
279 {
280   silc_fd_stream_close(stream);
281   silc_free(stream);
282 }
283
284 /* Sets stream notification callback for the stream */
285
286 SilcBool silc_fd_stream_notifier(SilcStream stream,
287                                  SilcSchedule schedule,
288                                  SilcStreamNotifier callback,
289                                  void *context)
290 {
291   SilcFDStream fd_stream = stream;
292
293   SILC_LOG_DEBUG(("Setting stream notifier callback"));
294
295   fd_stream->notifier = callback;
296   fd_stream->notifier_context = context;
297   fd_stream->schedule = schedule;
298
299   /* Schedule the file descriptors */
300   if (schedule) {
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);
305     }
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;
314     }
315   } else {
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);
321     }
322   }
323
324   return TRUE;
325 }
326
327 /* Return schedule */
328
329 SilcSchedule silc_fd_stream_get_schedule(SilcStream stream)
330 {
331   SilcFDStream fd_stream = stream;
332   return fd_stream->schedule;
333 }
334
335 /* File descriptor stream operations */
336 const SilcStreamOps silc_fd_stream_ops =
337 {
338   silc_fd_stream_read,
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
344 };