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