d707e59a326029d073e220b62a4f11dd1c8fd97c
[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 /* Create by opening file */
107
108 SilcStream silc_fd_stream_file(const char *filename,
109                                SilcBool reading, SilcBool writing,
110                                SilcSchedule schedule)
111 {
112   int fd, flags = 0;
113
114   if (!filename)
115     return NULL;
116
117   if (reading)
118     flags |= O_RDONLY;
119   if (writing)
120     flags |= O_WRONLY;
121   if (reading && writing)
122     flags |= O_RDWR;
123
124   fd = silc_file_open(filename, flags);
125   if (fd < 0)
126     return NULL;
127
128   return silc_fd_stream_create(fd, schedule);
129 }
130
131 /* Return fds */
132
133 SilcBool silc_fd_stream_get_info(SilcStream stream, int *read_fd, 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 (!SILC_IS_FD_STREAM(fd_stream))
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 (!SILC_IS_FD_STREAM(fd_stream))
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 (!SILC_IS_FD_STREAM(fd_stream))
241     return FALSE;
242
243   if (fd_stream->fd1 > 0) {
244     silc_file_close(fd_stream->fd1);
245     silc_schedule_unset_listen_fd(fd_stream->schedule, fd_stream->fd1);
246   }
247   if (fd_stream->fd2 > 0 && fd_stream->fd2 != fd_stream->fd1) {
248     silc_file_close(fd_stream->fd2);
249     silc_schedule_unset_listen_fd(fd_stream->schedule, fd_stream->fd2);
250   }
251
252   return TRUE;
253 }
254
255 /* Destroy stream */
256
257 void silc_fd_stream_destroy(SilcStream stream)
258 {
259   SilcFDStream fd_stream = stream;
260
261   if (!SILC_IS_FD_STREAM(fd_stream))
262     return;
263
264   silc_fd_stream_close(stream);
265   silc_schedule_task_del_by_fd(fd_stream->schedule, fd_stream->fd1);
266   silc_schedule_task_del_by_fd(fd_stream->schedule, fd_stream->fd2);
267   silc_free(stream);
268 }
269
270 /* Sets stream notification callback for the stream */
271
272 void silc_fd_stream_notifier(SilcStream stream,
273                              SilcStreamNotifier callback,
274                              void *context)
275 {
276   SilcFDStream fd_stream = stream;
277
278   if (!SILC_IS_FD_STREAM(fd_stream))
279     return;
280
281   SILC_LOG_DEBUG(("Setting stream notifier callback"));
282
283   fd_stream->notifier = callback;
284   fd_stream->notifier_context = context;
285 }
286
287 /* File descriptor stream operations */
288 const SilcStreamOps silc_fd_stream_ops =
289 {
290   silc_fd_stream_read,
291   silc_fd_stream_write,
292   silc_fd_stream_close,
293   silc_fd_stream_destroy,
294   silc_fd_stream_notifier,
295 };