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