258b82d503c10714559f0e4c83c721b9ec4b591d
[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 && write_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->fd1 = 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_net_set_socket_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_net_set_socket_nonblock(read_fd);
99     if (write_fd < 1)
100       write_fd = read_fd;
101   }
102
103   return stream;
104 }
105
106 /* Return fds */
107
108 bool silc_fd_stream_get_info(SilcStream stream, int *read_fd, int *write_fd)
109 {
110   SilcFDStream fd_stream = stream;
111
112   if (!SILC_IS_FD_STREAM(fd_stream))
113     return FALSE;
114
115   if (read_fd)
116     *read_fd = fd_stream->fd1;
117   if (write_fd)
118     *write_fd = fd_stream->fd2;
119
120   return TRUE;
121 }
122
123 /* Return errno */
124
125 int silc_fd_stream_get_error(SilcStream stream)
126 {
127   SilcFDStream fd_stream = stream;
128
129   if (!SILC_IS_FD_STREAM(fd_stream))
130     return 0;
131
132   return fd_stream->error;
133 }
134
135 /* Read */
136
137 int silc_fd_stream_read(SilcStream stream, unsigned char *buf,
138                         SilcUInt32 buf_len)
139 {
140   SilcFDStream fd_stream = stream;
141   int len = 0;
142
143   if (!SILC_IS_FD_STREAM(fd_stream))
144     return -2;
145
146   SILC_LOG_DEBUG(("Reading data from fd %d", fd_stream->fd1));
147
148   len = silc_file_read(fd_stream->fd1, buf, buf_len);
149   if (len < 0) {
150     if (errno == EAGAIN || errno == EINTR) {
151       SILC_LOG_DEBUG(("Could not read immediately, will do it later"));
152       silc_schedule_set_listen_fd(fd_stream->schedule, fd_stream->fd1,
153                                   SILC_TASK_READ, FALSE);
154       return -1;
155     }
156     SILC_LOG_DEBUG(("Cannot read from fd: %d:%s",
157                     fd_stream->fd1, strerror(errno)));
158     silc_schedule_unset_listen_fd(fd_stream->schedule, fd_stream->fd1);
159     fd_stream->error = errno;
160     return -2;
161   }
162
163   SILC_LOG_DEBUG(("Read %d bytes", len));
164
165   if (!len)
166     silc_schedule_unset_listen_fd(fd_stream->schedule, fd_stream->fd1);
167
168   return len;
169 }
170
171 /* Write */
172
173 int silc_fd_stream_write(SilcStream stream, const unsigned char *data,
174                          SilcUInt32 data_len)
175 {
176   SilcFDStream fd_stream = stream;
177   int ret;
178
179   if (!SILC_IS_FD_STREAM(fd_stream))
180     return -2;
181
182   SILC_LOG_DEBUG(("Writing data to fd %d", fd_stream->fd2));
183
184   ret = silc_file_write(fd_stream->fd2, data, data_len);
185   if (ret < 0) {
186     if (errno == EAGAIN || errno == EINTR) {
187       SILC_LOG_DEBUG(("Could not write immediately, will do it later"));
188       silc_schedule_set_listen_fd(fd_stream->schedule, fd_stream->fd2,
189                                   SILC_TASK_READ | SILC_TASK_WRITE, FALSE);
190       return -1;
191     }
192     SILC_LOG_DEBUG(("Cannot write to fd: %s", strerror(errno)));
193     silc_schedule_unset_listen_fd(fd_stream->schedule, fd_stream->fd2);
194     fd_stream->error = errno;
195     return -2;
196   }
197
198   SILC_LOG_DEBUG(("Wrote data %d bytes", ret));
199
200   if (fd_stream->fd1 == fd_stream->fd2)
201     silc_schedule_set_listen_fd(fd_stream->schedule, fd_stream->fd2,
202                                 SILC_TASK_READ, FALSE);
203   else
204     silc_schedule_unset_listen_fd(fd_stream->schedule, fd_stream->fd2);
205
206   return ret;
207 }
208
209 /* Close stream */
210
211 bool silc_fd_stream_close(SilcStream stream)
212 {
213   SilcFDStream fd_stream = stream;
214
215   if (!SILC_IS_FD_STREAM(fd_stream))
216     return FALSE;
217
218   if (fd_stream->fd1 > 0)
219     silc_file_close(fd_stream->fd1);
220   if (fd_stream->fd2 > 0 && fd_stream->fd2 != fd_stream->fd1)
221     silc_file_close(fd_stream->fd2);
222
223   return TRUE;
224 }
225
226 /* Destroy stream */
227
228 void silc_fd_stream_destroy(SilcStream stream)
229 {
230   SilcFDStream fd_stream = stream;
231
232   if (!SILC_IS_FD_STREAM(fd_stream))
233     return;
234
235   silc_fd_stream_close(stream);
236   silc_free(stream);
237 }
238
239 /* Sets stream notification callback for the stream */
240
241 void silc_fd_stream_notifier(SilcStream stream,
242                              SilcStreamNotifier callback,
243                              void *context)
244 {
245   SilcFDStream fd_stream = stream;
246
247   if (!SILC_IS_FD_STREAM(fd_stream))
248     return;
249
250   SILC_LOG_DEBUG(("Setting stream notifier callback"));
251
252   fd_stream->notifier = callback;
253   fd_stream->notifier_context = context;
254 }
255
256 /* File descriptor stream operations */
257 const SilcStreamOps silc_fd_stream_ops =
258 {
259   silc_fd_stream_read,
260   silc_fd_stream_write,
261   silc_fd_stream_close,
262   silc_fd_stream_destroy,
263   silc_fd_stream_notifier,
264 };