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