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