Fixed mode setting for file read&write.
[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_WRITE:
53     stream->notifier(stream, SILC_STREAM_CAN_WRITE, stream->notifier_context);
54     break;
55
56   case SILC_TASK_READ:
57     stream->notifier(stream, SILC_STREAM_CAN_READ, 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 (!SILC_IS_FD_STREAM(fd_stream))
162     return -2;
163   if (!fd_stream->notifier)
164     return -2;
165
166   SILC_LOG_DEBUG(("Reading data from fd %d", fd_stream->fd1));
167
168   len = silc_file_read(fd_stream->fd1, buf, buf_len);
169   if (len < 0) {
170     if (errno == EAGAIN || errno == EINTR) {
171       SILC_LOG_DEBUG(("Could not read immediately, will do it later"));
172       silc_schedule_set_listen_fd(fd_stream->schedule, fd_stream->fd1,
173                                   SILC_TASK_READ, FALSE);
174       return -1;
175     }
176     SILC_LOG_DEBUG(("Cannot read from fd: %d:%s",
177                     fd_stream->fd1, strerror(errno)));
178     silc_schedule_unset_listen_fd(fd_stream->schedule, fd_stream->fd1);
179     fd_stream->error = errno;
180     return -2;
181   }
182
183   SILC_LOG_DEBUG(("Read %d bytes", len));
184
185   if (!len)
186     silc_schedule_unset_listen_fd(fd_stream->schedule, fd_stream->fd1);
187
188   return len;
189 }
190
191 /* Write */
192
193 int silc_fd_stream_write(SilcStream stream, const unsigned char *data,
194                          SilcUInt32 data_len)
195 {
196   SilcFDStream fd_stream = stream;
197   int ret;
198
199   if (!SILC_IS_FD_STREAM(fd_stream))
200     return -2;
201   if (!fd_stream->notifier)
202     return -2;
203
204   SILC_LOG_DEBUG(("Writing data to fd %d", fd_stream->fd2));
205
206   ret = silc_file_write(fd_stream->fd2, data, data_len);
207   if (ret < 0) {
208     if (errno == EAGAIN || errno == EINTR) {
209       SILC_LOG_DEBUG(("Could not write immediately, will do it later"));
210       silc_schedule_set_listen_fd(fd_stream->schedule, fd_stream->fd2,
211                                   SILC_TASK_READ | SILC_TASK_WRITE, FALSE);
212       return -1;
213     }
214     SILC_LOG_DEBUG(("Cannot write to fd: %s", strerror(errno)));
215     silc_schedule_unset_listen_fd(fd_stream->schedule, fd_stream->fd2);
216     fd_stream->error = errno;
217     return -2;
218   }
219
220   SILC_LOG_DEBUG(("Wrote data %d bytes", ret));
221
222   if (fd_stream->fd1 == fd_stream->fd2)
223     silc_schedule_set_listen_fd(fd_stream->schedule, fd_stream->fd2,
224                                 SILC_TASK_READ, FALSE);
225   else
226     silc_schedule_unset_listen_fd(fd_stream->schedule, fd_stream->fd2);
227
228   return ret;
229 }
230
231 /* Close stream */
232
233 SilcBool silc_fd_stream_close(SilcStream stream)
234 {
235   SilcFDStream fd_stream = stream;
236
237   if (!SILC_IS_FD_STREAM(fd_stream))
238     return FALSE;
239
240   if (fd_stream->fd1 > 0) {
241     silc_file_close(fd_stream->fd1);
242     silc_schedule_unset_listen_fd(fd_stream->schedule, fd_stream->fd1);
243   }
244   if (fd_stream->fd2 > 0 && fd_stream->fd2 != fd_stream->fd1) {
245     silc_file_close(fd_stream->fd2);
246     silc_schedule_unset_listen_fd(fd_stream->schedule, fd_stream->fd2);
247   }
248
249   return TRUE;
250 }
251
252 /* Destroy stream */
253
254 void silc_fd_stream_destroy(SilcStream stream)
255 {
256   SilcFDStream fd_stream = stream;
257
258   if (!SILC_IS_FD_STREAM(fd_stream))
259     return;
260
261   silc_fd_stream_close(stream);
262   silc_schedule_task_del_by_fd(fd_stream->schedule, fd_stream->fd1);
263   silc_schedule_task_del_by_fd(fd_stream->schedule, fd_stream->fd2);
264   silc_free(stream);
265 }
266
267 /* Sets stream notification callback for the stream */
268
269 void silc_fd_stream_notifier(SilcStream stream,
270                              SilcSchedule schedule,
271                              SilcStreamNotifier callback,
272                              void *context)
273 {
274   SilcFDStream fd_stream = stream;
275
276   if (!SILC_IS_FD_STREAM(fd_stream))
277     return;
278
279   SILC_LOG_DEBUG(("Setting stream notifier callback"));
280
281   fd_stream->notifier = callback;
282   fd_stream->notifier_context = context;
283   fd_stream->schedule = schedule;
284
285   /* Schedule the file descriptors */
286   if (schedule) {
287     if (fd_stream->fd2 > 0) {
288       silc_schedule_task_add_fd(schedule, fd_stream->fd2,
289                                 silc_fd_stream_io, stream);
290       silc_file_set_nonblock(fd_stream->fd2);
291     }
292     if (fd_stream->fd1 > 0) {
293       silc_schedule_task_add_fd(schedule, fd_stream->fd1,
294                                 silc_fd_stream_io, stream);
295       silc_schedule_set_listen_fd(schedule, fd_stream->fd1,
296                                   SILC_TASK_READ, FALSE);
297       silc_file_set_nonblock(fd_stream->fd1);;
298       if (fd_stream->fd2 < 1)
299         fd_stream->fd2 = fd_stream->fd1;
300     }
301   } else {
302     silc_schedule_unset_listen_fd(fd_stream->schedule, fd_stream->fd1);
303     silc_schedule_unset_listen_fd(fd_stream->schedule, fd_stream->fd2);
304   }
305 }
306
307 /* Return schedule */
308
309 SilcSchedule silc_fd_stream_get_schedule(SilcStream stream)
310 {
311   SilcFDStream fd_stream = stream;
312
313   if (!SILC_IS_FD_STREAM(fd_stream))
314     return NULL;
315
316   return fd_stream->schedule;
317 }
318
319 /* File descriptor stream operations */
320 const SilcStreamOps silc_fd_stream_ops =
321 {
322   silc_fd_stream_read,
323   silc_fd_stream_write,
324   silc_fd_stream_close,
325   silc_fd_stream_destroy,
326   silc_fd_stream_notifier,
327   silc_fd_stream_get_schedule
328 };