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