silc_stream_set_notifier and silc_schedule_set_listen_fd now
[silc.git] / lib / silcutil / silcfdstream.c
1 /*
2
3   silcfdstream.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2005 - 2006 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
106   if (!filename)
107     return NULL;
108
109   if (reading)
110     flags |= O_RDONLY;
111   if (writing)
112     flags |= O_CREAT | O_WRONLY;
113   if (reading && writing)
114     flags = O_CREAT | O_RDWR;
115
116   fd = silc_file_open(filename, flags);
117   if (fd < 0)
118     return NULL;
119
120   return silc_fd_stream_create(fd);
121 }
122
123 /* Return fds */
124
125 SilcBool silc_fd_stream_get_info(SilcStream stream, int *read_fd,
126                                  int *write_fd)
127 {
128   SilcFDStream fd_stream = stream;
129
130   if (!SILC_IS_FD_STREAM(fd_stream))
131     return FALSE;
132
133   if (read_fd)
134     *read_fd = fd_stream->fd1;
135   if (write_fd)
136     *write_fd = fd_stream->fd2;
137
138   return TRUE;
139 }
140
141 /* Return errno */
142
143 int silc_fd_stream_get_error(SilcStream stream)
144 {
145   SilcFDStream fd_stream = stream;
146
147   if (!SILC_IS_FD_STREAM(fd_stream))
148     return 0;
149
150   return fd_stream->error;
151 }
152
153 /* Read */
154
155 int silc_fd_stream_read(SilcStream stream, unsigned char *buf,
156                         SilcUInt32 buf_len)
157 {
158   SilcFDStream fd_stream = stream;
159   int len = 0;
160
161   if (!fd_stream->notifier)
162     return -2;
163
164   SILC_LOG_DEBUG(("Reading data from fd %d", fd_stream->fd1));
165
166   len = silc_file_read(fd_stream->fd1, buf, buf_len);
167   if (len < 0) {
168     if (errno == EAGAIN || errno == EINTR) {
169       SILC_LOG_DEBUG(("Could not read immediately, will do it later"));
170       silc_schedule_set_listen_fd(fd_stream->schedule, fd_stream->fd1,
171                                   SILC_TASK_READ, FALSE);
172       return -1;
173     }
174     SILC_LOG_DEBUG(("Cannot read from fd: %d:%s",
175                     fd_stream->fd1, strerror(errno)));
176     silc_schedule_unset_listen_fd(fd_stream->schedule, fd_stream->fd1);
177     fd_stream->error = errno;
178     return -2;
179   }
180
181   SILC_LOG_DEBUG(("Read %d bytes", len));
182
183   if (!len)
184     silc_schedule_unset_listen_fd(fd_stream->schedule, fd_stream->fd1);
185
186   return len;
187 }
188
189 /* Write */
190
191 int silc_fd_stream_write(SilcStream stream, const unsigned char *data,
192                          SilcUInt32 data_len)
193 {
194   SilcFDStream fd_stream = stream;
195   int ret;
196
197   if (!fd_stream->notifier)
198     return -2;
199
200   SILC_LOG_DEBUG(("Writing data to fd %d", fd_stream->fd2));
201
202   ret = silc_file_write(fd_stream->fd2, data, data_len);
203   if (ret < 0) {
204     if (errno == EAGAIN || errno == EINTR) {
205       SILC_LOG_DEBUG(("Could not write immediately, will do it later"));
206       silc_schedule_set_listen_fd(fd_stream->schedule, fd_stream->fd2,
207                                   SILC_TASK_READ | SILC_TASK_WRITE, FALSE);
208       return -1;
209     }
210     SILC_LOG_DEBUG(("Cannot write to fd: %s", strerror(errno)));
211     silc_schedule_unset_listen_fd(fd_stream->schedule, fd_stream->fd2);
212     fd_stream->error = errno;
213     return -2;
214   }
215
216   SILC_LOG_DEBUG(("Wrote data %d bytes", ret));
217
218   if (fd_stream->fd1 == fd_stream->fd2)
219     silc_schedule_set_listen_fd(fd_stream->schedule, fd_stream->fd2,
220                                 SILC_TASK_READ, FALSE);
221   else
222     silc_schedule_unset_listen_fd(fd_stream->schedule, fd_stream->fd2);
223
224   return ret;
225 }
226
227 /* Close stream */
228
229 SilcBool silc_fd_stream_close(SilcStream stream)
230 {
231   SilcFDStream fd_stream = stream;
232
233   if (fd_stream->fd1 > 0) {
234     silc_file_close(fd_stream->fd1);
235     silc_schedule_unset_listen_fd(fd_stream->schedule, fd_stream->fd1);
236     silc_schedule_task_del_by_fd(fd_stream->schedule, fd_stream->fd1);
237   }
238   if (fd_stream->fd2 > 0 && fd_stream->fd2 != fd_stream->fd1) {
239     silc_file_close(fd_stream->fd2);
240     silc_schedule_unset_listen_fd(fd_stream->schedule, fd_stream->fd2);
241     silc_schedule_task_del_by_fd(fd_stream->schedule, fd_stream->fd2);
242   }
243
244   return TRUE;
245 }
246
247 /* Destroy stream */
248
249 void silc_fd_stream_destroy(SilcStream stream)
250 {
251   silc_fd_stream_close(stream);
252   silc_free(stream);
253 }
254
255 /* Sets stream notification callback for the stream */
256
257 SilcBool silc_fd_stream_notifier(SilcStream stream,
258                                  SilcSchedule schedule,
259                                  SilcStreamNotifier callback,
260                                  void *context)
261 {
262   SilcFDStream fd_stream = stream;
263
264   SILC_LOG_DEBUG(("Setting stream notifier callback"));
265
266   fd_stream->notifier = callback;
267   fd_stream->notifier_context = context;
268   fd_stream->schedule = schedule;
269
270   /* Schedule the file descriptors */
271   if (schedule) {
272     if (fd_stream->fd2 > 0) {
273       silc_schedule_task_add_fd(schedule, fd_stream->fd2,
274                                 silc_fd_stream_io, stream);
275       silc_file_set_nonblock(fd_stream->fd2);
276     }
277     if (fd_stream->fd1 > 0) {
278       silc_schedule_task_add_fd(schedule, fd_stream->fd1,
279                                 silc_fd_stream_io, stream);
280       silc_schedule_set_listen_fd(schedule, fd_stream->fd1,
281                                   SILC_TASK_READ, FALSE);
282       silc_file_set_nonblock(fd_stream->fd1);;
283       if (fd_stream->fd2 < 1)
284         fd_stream->fd2 = fd_stream->fd1;
285     }
286   } else {
287     silc_schedule_unset_listen_fd(fd_stream->schedule, fd_stream->fd1);
288     silc_schedule_unset_listen_fd(fd_stream->schedule, fd_stream->fd2);
289     silc_schedule_task_del_by_fd(fd_stream->schedule, fd_stream->fd1);
290     silc_schedule_task_del_by_fd(fd_stream->schedule, fd_stream->fd2);
291   }
292
293   return TRUE;
294 }
295
296 /* Return schedule */
297
298 SilcSchedule silc_fd_stream_get_schedule(SilcStream stream)
299 {
300   SilcFDStream fd_stream = stream;
301   return fd_stream->schedule;
302 }
303
304 /* File descriptor stream operations */
305 const SilcStreamOps silc_fd_stream_ops =
306 {
307   silc_fd_stream_read,
308   silc_fd_stream_write,
309   silc_fd_stream_close,
310   silc_fd_stream_destroy,
311   silc_fd_stream_notifier,
312   silc_fd_stream_get_schedule
313 };