ensure revents fields are initialized before calling poll()
[asterisk/asterisk.git] / io.c
1 /*
2  * Asterisk
3  * 
4  * Mark Spencer <markster@marko.net>
5  *
6  * Copyright(C) Mark Spencer
7  * 
8  * Distributed under the terms of the GNU General Public License (GPL) Version 2
9  *
10  * I/O Managment (Derived from Cheops-NG)
11  *
12  */
13
14 #include <stdio.h>
15 #include <unistd.h>
16 #include <stdlib.h>
17 #include <termios.h>
18 #include <string.h> /* for memset */
19 #include <sys/ioctl.h>
20
21 #include "asterisk.h"
22
23 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
24
25 #include "asterisk/io.h"
26 #include "asterisk/logger.h"
27
28 #ifdef DEBUG_IO
29 #define DEBUG DEBUG_M
30 #else
31 #define DEBUG(a) 
32 #endif
33
34 /* 
35  * Kept for each file descriptor
36  */
37 struct io_rec {
38         ast_io_cb callback;             /* What is to be called */
39         void *data;                             /* Data to be passed */
40         int *id;                                        /* ID number */
41 };
42
43 /* These two arrays are keyed with
44    the same index.  it's too bad that
45    pollfd doesn't have a callback field
46    or something like that.  They grow as
47    needed, by GROW_SHRINK_AMOUNT structures
48    at once */
49
50 #define GROW_SHRINK_SIZE 512
51
52 /* Global variables are now in a struct in order to be
53    made threadsafe */
54 struct io_context {
55         /* Poll structure */
56         struct pollfd *fds;
57         /* Associated I/O records */
58         struct io_rec *ior;
59         /* First available fd */
60         unsigned int fdcnt;
61         /* Maximum available fd */
62         unsigned int maxfdcnt;
63         /* Currently used io callback */
64         int current_ioc;
65         /* Whether something has been deleted */
66         int needshrink;
67 };
68
69 struct io_context *io_context_create(void)
70 {
71         /* Create an I/O context */
72         struct io_context *tmp;
73         tmp = malloc(sizeof(struct io_context));
74         if (tmp) {
75                 tmp->needshrink = 0;
76                 tmp->fdcnt = 0;
77                 tmp->maxfdcnt = GROW_SHRINK_SIZE/2;
78                 tmp->current_ioc = -1;
79                 tmp->fds = malloc((GROW_SHRINK_SIZE/2) * sizeof(struct pollfd));
80                 if (!tmp->fds) {
81                         free(tmp);
82                         tmp = NULL;
83                 } else {
84                         memset(tmp->fds, 0, (GROW_SHRINK_SIZE / 2) * sizeof(struct pollfd));
85                         tmp->ior =  malloc((GROW_SHRINK_SIZE / 2) * sizeof(struct io_rec));
86                         if (!tmp->ior) {
87                                 free(tmp->fds);
88                                 free(tmp);
89                                 tmp = NULL;
90                         } else {
91                                 memset(tmp->ior, 0, (GROW_SHRINK_SIZE / 2) * sizeof(struct io_rec));
92                         }
93                 }
94         }
95         return tmp;
96 }
97
98 void io_context_destroy(struct io_context *ioc)
99 {
100         /* Free associated memory with an I/O context */
101         if (ioc->fds)
102                 free(ioc->fds);
103         if (ioc->ior)
104                 free(ioc->ior);
105         free(ioc);
106 }
107
108 static int io_grow(struct io_context *ioc)
109 {
110         /* 
111          * Grow the size of our arrays.  Return 0 on success or
112          * -1 on failure
113          */
114         void *tmp;
115         DEBUG(ast_log(LOG_DEBUG, "io_grow()\n"));
116         ioc->maxfdcnt += GROW_SHRINK_SIZE;
117         tmp = realloc(ioc->ior, (ioc->maxfdcnt + 1) * sizeof(struct io_rec));
118         if (tmp) {
119                 ioc->ior = (struct io_rec *)tmp;
120                 tmp = realloc(ioc->fds, (ioc->maxfdcnt + 1) * sizeof(struct pollfd));
121                 if (tmp) {
122                         ioc->fds = tmp;
123                 } else {
124                         /*
125                          * Not enough memory for the pollfd.  Not really any need
126                          * to shrink back the iorec's as we'll probably want to
127                          * grow them again soon when more memory is available, and
128                          * then they'll already be the right size
129                          */
130                         ioc->maxfdcnt -= GROW_SHRINK_SIZE;
131                         return -1;
132                 }
133         } else {
134                 /*
135                  * Out of memory.  We return to the old size, and return a failure
136                  */
137                 ioc->maxfdcnt -= GROW_SHRINK_SIZE;
138                 return -1;
139         }
140         return 0;
141 }
142
143 int *ast_io_add(struct io_context *ioc, int fd, ast_io_cb callback, short events, void *data)
144 {
145         /*
146          * Add a new I/O entry for this file descriptor
147          * with the given event mask, to call callback with
148          * data as an argument.  Returns NULL on failure.
149          */
150         int *ret;
151         DEBUG(ast_log(LOG_DEBUG, "ast_io_add()\n"));
152         if (ioc->fdcnt >= ioc->maxfdcnt) {
153                 /* 
154                  * We don't have enough space for this entry.  We need to
155                  * reallocate maxfdcnt poll fd's and io_rec's, or back out now.
156                  */
157                 if (io_grow(ioc))
158                         return NULL;
159         }
160
161         /*
162          * At this point, we've got sufficiently large arrays going
163          * and we can make an entry for it in the pollfd and io_r
164          * structures.
165          */
166         ioc->fds[ioc->fdcnt].fd = fd;
167         ioc->fds[ioc->fdcnt].events = events;
168         ioc->fds[ioc->fdcnt].revents = 0;
169         ioc->ior[ioc->fdcnt].callback = callback;
170         ioc->ior[ioc->fdcnt].data = data;
171         ioc->ior[ioc->fdcnt].id = (int *)malloc(sizeof(int));
172         /* Bonk if we couldn't allocate an int */
173         if (!ioc->ior[ioc->fdcnt].id)
174                 return NULL;
175         *(ioc->ior[ioc->fdcnt].id) = ioc->fdcnt;
176         ret = ioc->ior[ioc->fdcnt].id;
177         ioc->fdcnt++;
178         return ret;
179 }
180
181 int *ast_io_change(struct io_context *ioc, int *id, int fd, ast_io_cb callback, short events, void *data)
182 {
183         if (*id < ioc->fdcnt) {
184                 if (fd > -1)
185                         ioc->fds[*id].fd = fd;
186                 if (callback)
187                         ioc->ior[*id].callback = callback;
188                 if (events)
189                         ioc->fds[*id].events = events;
190                 if (data)
191                         ioc->ior[*id].data = data;
192                 return id;
193         }
194         return NULL;
195 }
196
197 static int io_shrink(struct io_context *ioc)
198 {
199         int getfrom;
200         int putto = 0;
201         /* 
202          * Bring the fields from the very last entry to cover over
203          * the entry we are removing, then decrease the size of the 
204          * arrays by one.
205          */
206         for (getfrom = 0; getfrom < ioc->fdcnt; getfrom++) {
207                 if (ioc->ior[getfrom].id) {
208                         /* In use, save it */
209                         if (getfrom != putto) {
210                                 ioc->fds[putto] = ioc->fds[getfrom];
211                                 ioc->ior[putto] = ioc->ior[getfrom];
212                                 *(ioc->ior[putto].id) = putto;
213                         }
214                         putto++;
215                 }
216         }
217         ioc->fdcnt = putto;
218         ioc->needshrink = 0;
219         /* FIXME: We should free some memory if we have lots of unused
220            io structs */
221         return 0;
222 }
223
224 int ast_io_remove(struct io_context *ioc, int *_id)
225 {
226         int x;
227         if (!_id) {
228                 ast_log(LOG_WARNING, "Asked to remove NULL?\n");
229                 return -1;
230         }
231         for (x = 0; x < ioc->fdcnt; x++) {
232                 if (ioc->ior[x].id == _id) {
233                         /* Free the int immediately and set to NULL so we know it's unused now */
234                         free(ioc->ior[x].id);
235                         ioc->ior[x].id = NULL;
236                         ioc->fds[x].events = 0;
237                         ioc->fds[x].revents = 0;
238                         ioc->needshrink = 1;
239                         if (!ioc->current_ioc)
240                                 io_shrink(ioc);
241                         return 0;
242                 }
243         }
244         
245         ast_log(LOG_NOTICE, "Unable to remove unknown id %p\n", _id);
246         return -1;
247 }
248
249 int ast_io_wait(struct io_context *ioc, int howlong)
250 {
251         /*
252          * Make the poll call, and call
253          * the callbacks for anything that needs
254          * to be handled
255          */
256         int res;
257         int x;
258         int origcnt;
259         DEBUG(ast_log(LOG_DEBUG, "ast_io_wait()\n"));
260         res = poll(ioc->fds, ioc->fdcnt, howlong);
261         if (res > 0) {
262                 /*
263                  * At least one event
264                  */
265                 origcnt = ioc->fdcnt;
266                 for(x = 0; x < origcnt; x++) {
267                         /* Yes, it is possible for an entry to be deleted and still have an
268                            event waiting if it occurs after the original calling id */
269                         if (ioc->fds[x].revents && ioc->ior[x].id) {
270                                 /* There's an event waiting */
271                                 ioc->current_ioc = *ioc->ior[x].id;
272                                 if (ioc->ior[x].callback) {
273                                         if (!ioc->ior[x].callback(ioc->ior[x].id, ioc->fds[x].fd, ioc->fds[x].revents, ioc->ior[x].data)) {
274                                                 /* Time to delete them since they returned a 0 */
275                                                 ast_io_remove(ioc, ioc->ior[x].id);
276                                         }
277                                 }
278                                 ioc->current_ioc = -1;
279                         }
280                 }
281                 if (ioc->needshrink)
282                         io_shrink(ioc);
283         }
284         return res;
285 }
286
287 void ast_io_dump(struct io_context *ioc)
288 {
289         /*
290          * Print some debugging information via
291          * the logger interface
292          */
293         int x;
294         ast_log(LOG_DEBUG, "Asterisk IO Dump: %d entries, %d max entries\n", ioc->fdcnt, ioc->maxfdcnt);
295         ast_log(LOG_DEBUG, "================================================\n");
296         ast_log(LOG_DEBUG, "| ID    FD     Callback    Data        Events  |\n");
297         ast_log(LOG_DEBUG, "+------+------+-----------+-----------+--------+\n");
298         for (x = 0; x < ioc->fdcnt; x++) {
299                 ast_log(LOG_DEBUG, "| %.4d | %.4d | %p | %p | %.6x |\n", 
300                                 *ioc->ior[x].id,
301                                 ioc->fds[x].fd,
302                                 ioc->ior[x].callback,
303                                 ioc->ior[x].data,
304                                 ioc->fds[x].events);
305         }
306         ast_log(LOG_DEBUG, "================================================\n");
307 }
308
309 /* Unrelated I/O functions */
310
311 int ast_hide_password(int fd)
312 {
313         struct termios tios;
314         int res;
315         int old;
316         if (!isatty(fd))
317                 return -1;
318         res = tcgetattr(fd, &tios);
319         if (res < 0)
320                 return -1;
321         old = tios.c_lflag & (ECHO | ECHONL);
322         tios.c_lflag &= ~ECHO;
323         tios.c_lflag |= ECHONL;
324         res = tcsetattr(fd, TCSAFLUSH, &tios);
325         if (res < 0)
326                 return -1;
327         return old;
328 }
329
330 int ast_restore_tty(int fd, int oldstate)
331 {
332         int res;
333         struct termios tios;
334         if (oldstate < 0)
335                 return 0;
336         res = tcgetattr(fd, &tios);
337         if (res < 0)
338                 return -1;
339         tios.c_lflag &= ~(ECHO | ECHONL);
340         tios.c_lflag |= oldstate;
341         res = tcsetattr(fd, TCSAFLUSH, &tios);
342         if (res < 0)
343                 return -1;
344         return 0;
345 }
346
347 int ast_get_termcols(int fd)
348 {
349         struct winsize win;
350         int cols = 0;
351
352         if (!isatty(fd))
353                 return -1;
354
355         if ( ioctl(fd, TIOCGWINSZ, &win) != -1 ) {
356                 if ( !cols && win.ws_col > 0 )
357                         cols = (int) win.ws_col;
358         } else {
359                 /* assume 80 characters if the ioctl fails for some reason */
360                 cols = 80;
361         }
362
363         return cols;
364 }
365