d2875f95a69dce8a5e746e28d1533e7d301c6f2f
[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   SilcStack stack;
32   SilcSchedule schedule;
33   SilcStreamNotifier notifier;
34   void *notifier_context;
35   int fd1;
36   int fd2;
37   int error;
38 } *SilcFDStream;
39
40
41 /************************ Static utility functions **************************/
42
43 /* The IO process callback that calls the notifier callback to upper layer. */
44
45 SILC_TASK_CALLBACK(silc_fd_stream_io)
46 {
47   SilcFDStream stream = context;
48
49   if (!stream->notifier)
50     return;
51
52   switch (type) {
53   case SILC_TASK_READ:
54     stream->notifier(stream, SILC_STREAM_CAN_READ, stream->notifier_context);
55     break;
56
57   case SILC_TASK_WRITE:
58     stream->notifier(stream, SILC_STREAM_CAN_WRITE, stream->notifier_context);
59     break;
60
61   default:
62     break;
63   }
64 }
65
66
67 /****************************** Public API **********************************/
68
69 /* Create file descriptor stream */
70
71 SilcStream silc_fd_stream_create(int fd, SilcStack stack)
72 {
73   if (fd < 1)
74     return NULL;
75   return silc_fd_stream_create2(fd, 0, stack);
76 }
77
78 /* Create stream with two file descriptors */
79
80 SilcStream silc_fd_stream_create2(int read_fd, int write_fd, SilcStack stack)
81 {
82   SilcFDStream stream;
83
84   if (stack)
85     stack = silc_stack_alloc(0, stack);
86
87   stream = silc_scalloc(stack, 1, sizeof(*stream));
88   if (!stream) {
89     silc_stack_free(stack);
90     return NULL;
91   }
92
93   SILC_LOG_DEBUG(("Creating new fd stream %p", stream));
94
95   stream->ops = &silc_fd_stream_ops;
96   stream->fd1 = read_fd;
97   stream->fd2 = write_fd;
98   stream->stack = stack;
99
100   return stream;
101 }
102
103 /* Create by opening file */
104
105 SilcStream silc_fd_stream_file(const char *filename, SilcBool reading,
106                                SilcBool writing, SilcStack stack)
107 {
108   const char *read_file = NULL, *write_file = NULL;
109
110   if (!filename)
111     return NULL;
112
113   if (writing)
114     write_file = filename;
115   if (reading)
116     read_file = filename;
117
118   return silc_fd_stream_file2(read_file, write_file, stack);
119 }
120
121 /* Create by opening two files */
122
123 SilcStream silc_fd_stream_file2(const char *read_file, const char *write_file,
124                                 SilcStack stack)
125 {
126   SilcStream stream;
127   int fd1 = 0, fd2 = 0;
128
129   SILC_LOG_DEBUG(("Creating new fd stream for reading `%s' and writing `%s'",
130                   read_file ? read_file : "(none)",
131                   write_file ? write_file : "(none)"));
132
133   if (write_file) {
134     fd2 = silc_file_open(write_file, O_CREAT | O_WRONLY);
135     if (fd2 < 0) {
136       silc_file_close(fd1);
137       return NULL;
138     }
139   }
140
141   if (read_file) {
142     fd1 = silc_file_open(read_file, O_RDONLY);
143     if (fd1 < 0)
144       return NULL;
145   }
146
147   stream = silc_fd_stream_create2(fd1, fd2, stack);
148   if (!stream) {
149     silc_file_close(fd1);
150     silc_file_close(fd2);
151   }
152
153   return stream;
154 }
155
156 /* Return fds */
157
158 SilcBool silc_fd_stream_get_info(SilcStream stream, int *read_fd,
159                                  int *write_fd)
160 {
161   SilcFDStream fd_stream = stream;
162
163   if (!SILC_IS_FD_STREAM(fd_stream))
164     return FALSE;
165
166   if (read_fd)
167     *read_fd = fd_stream->fd1;
168   if (write_fd)
169     *write_fd = fd_stream->fd2;
170
171   return TRUE;
172 }
173
174 /* Return errno */
175
176 int silc_fd_stream_get_error(SilcStream stream)
177 {
178   SilcFDStream fd_stream = stream;
179
180   if (!SILC_IS_FD_STREAM(fd_stream))
181     return 0;
182
183   return fd_stream->error;
184 }
185
186 /* Read */
187
188 int silc_fd_stream_read(SilcStream stream, unsigned char *buf,
189                         SilcUInt32 buf_len)
190 {
191   SilcFDStream fd_stream = stream;
192   int len = 0;
193
194   if (!fd_stream->notifier)
195     return -2;
196
197   SILC_LOG_DEBUG(("Reading data from fd %d", fd_stream->fd1));
198
199   len = silc_file_read(fd_stream->fd1, buf, buf_len);
200   if (len < 0) {
201     if (errno == EAGAIN || errno == EINTR) {
202       SILC_LOG_DEBUG(("Could not read immediately, will do it later"));
203       silc_schedule_set_listen_fd(fd_stream->schedule, fd_stream->fd1,
204                                   SILC_TASK_READ, FALSE);
205       return -1;
206     }
207     SILC_LOG_DEBUG(("Cannot read from fd: %d:%s",
208                     fd_stream->fd1, strerror(errno)));
209     silc_schedule_unset_listen_fd(fd_stream->schedule, fd_stream->fd1);
210     fd_stream->error = errno;
211     return -2;
212   }
213
214   SILC_LOG_DEBUG(("Read %d bytes", len));
215
216   if (!len)
217     silc_schedule_unset_listen_fd(fd_stream->schedule, fd_stream->fd1);
218
219   return len;
220 }
221
222 /* Write */
223
224 int silc_fd_stream_write(SilcStream stream, const unsigned char *data,
225                          SilcUInt32 data_len)
226 {
227   SilcFDStream fd_stream = stream;
228   int ret;
229
230   if (!fd_stream->notifier)
231     return -2;
232
233   SILC_LOG_DEBUG(("Writing data to fd %d", fd_stream->fd2));
234
235   ret = silc_file_write(fd_stream->fd2, data, data_len);
236   if (ret < 0) {
237     if (errno == EAGAIN || errno == EINTR) {
238       SILC_LOG_DEBUG(("Could not write immediately, will do it later"));
239       silc_schedule_set_listen_fd(fd_stream->schedule, fd_stream->fd2,
240                                   SILC_TASK_READ | SILC_TASK_WRITE, FALSE);
241       return -1;
242     }
243     SILC_LOG_DEBUG(("Cannot write to fd: %s", strerror(errno)));
244     silc_schedule_unset_listen_fd(fd_stream->schedule, fd_stream->fd2);
245     fd_stream->error = errno;
246     return -2;
247   }
248
249   SILC_LOG_DEBUG(("Wrote data %d bytes", ret));
250
251   if (fd_stream->fd1 == fd_stream->fd2)
252     silc_schedule_set_listen_fd(fd_stream->schedule, fd_stream->fd2,
253                                 SILC_TASK_READ, FALSE);
254   else
255     silc_schedule_unset_listen_fd(fd_stream->schedule, fd_stream->fd2);
256
257   return ret;
258 }
259
260 /* Close stream */
261
262 SilcBool silc_fd_stream_close(SilcStream stream)
263 {
264   SilcFDStream fd_stream = stream;
265
266   if (fd_stream->fd1 > 0) {
267     silc_file_close(fd_stream->fd1);
268     if (fd_stream->schedule) {
269       silc_schedule_unset_listen_fd(fd_stream->schedule, fd_stream->fd1);
270       silc_schedule_task_del_by_fd(fd_stream->schedule, fd_stream->fd1);
271     }
272   }
273   if (fd_stream->fd2 > 0 && fd_stream->fd2 != fd_stream->fd1) {
274     silc_file_close(fd_stream->fd2);
275     if (fd_stream->schedule) {
276       silc_schedule_unset_listen_fd(fd_stream->schedule, fd_stream->fd2);
277       silc_schedule_task_del_by_fd(fd_stream->schedule, fd_stream->fd2);
278     }
279   }
280
281   return TRUE;
282 }
283
284 /* Destroy stream */
285
286 void silc_fd_stream_destroy(SilcStream stream)
287 {
288   SilcFDStream fd_stream = stream;
289   SilcStack stack = fd_stream->stack;
290
291   silc_fd_stream_close(stream);
292   silc_sfree(stack, stream);
293   silc_stack_free(stack);
294 }
295
296 /* Sets stream notification callback for the stream */
297
298 SilcBool silc_fd_stream_notifier(SilcStream stream,
299                                  SilcSchedule schedule,
300                                  SilcStreamNotifier callback,
301                                  void *context)
302 {
303   SilcFDStream fd_stream = stream;
304
305   SILC_LOG_DEBUG(("Setting stream notifier callback"));
306
307   fd_stream->notifier = callback;
308   fd_stream->notifier_context = context;
309   fd_stream->schedule = schedule;
310
311   /* Schedule the file descriptors */
312   if (schedule) {
313     if (fd_stream->fd2 > 0) {
314       silc_schedule_task_add_fd(schedule, fd_stream->fd2,
315                                 silc_fd_stream_io, stream);
316       silc_file_set_nonblock(fd_stream->fd2);
317     }
318     if (fd_stream->fd1 > 0) {
319       silc_schedule_task_add_fd(schedule, fd_stream->fd1,
320                                 silc_fd_stream_io, stream);
321       silc_schedule_set_listen_fd(schedule, fd_stream->fd1,
322                                   SILC_TASK_READ, FALSE);
323       silc_file_set_nonblock(fd_stream->fd1);
324       if (fd_stream->fd2 < 1)
325         fd_stream->fd2 = fd_stream->fd1;
326     }
327   } else {
328     if (fd_stream->schedule) {
329       silc_schedule_unset_listen_fd(fd_stream->schedule, fd_stream->fd1);
330       silc_schedule_unset_listen_fd(fd_stream->schedule, fd_stream->fd2);
331       silc_schedule_task_del_by_fd(fd_stream->schedule, fd_stream->fd1);
332       silc_schedule_task_del_by_fd(fd_stream->schedule, fd_stream->fd2);
333     }
334   }
335
336   return TRUE;
337 }
338
339 /* Return schedule */
340
341 SilcSchedule silc_fd_stream_get_schedule(SilcStream stream)
342 {
343   SilcFDStream fd_stream = stream;
344   return fd_stream->schedule;
345 }
346
347 /* File descriptor stream operations */
348 const SilcStreamOps silc_fd_stream_ops =
349 {
350   silc_fd_stream_read,
351   silc_fd_stream_write,
352   silc_fd_stream_close,
353   silc_fd_stream_destroy,
354   silc_fd_stream_notifier,
355   silc_fd_stream_get_schedule
356 };