Merged revisions 140817 via svnmerge from
[asterisk-bristuff.git] / main / io.c
blob68b73d26cd16f463cee00496733165aeec1b9a58
1 /*
2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2005, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
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.
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.
19 /*! \file
21 * \brief I/O Managment (Derived from Cheops-NG)
23 * \author Mark Spencer <markster@digium.com>
26 #include "asterisk.h"
28 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
30 #include <termios.h>
31 #include <sys/ioctl.h>
33 #include "asterisk/io.h"
34 #include "asterisk/utils.h"
36 #ifdef DEBUG_IO
37 #define DEBUG DEBUG_M
38 #else
39 #define DEBUG(a)
40 #endif
42 /*! \brief
43 * Kept for each file descriptor
45 struct io_rec {
46 ast_io_cb callback; /*!< What is to be called */
47 void *data; /*!< Data to be passed */
48 int *id; /*!< ID number */
51 /* These two arrays are keyed with
52 the same index. it's too bad that
53 pollfd doesn't have a callback field
54 or something like that. They grow as
55 needed, by GROW_SHRINK_SIZE structures
56 at once */
58 #define GROW_SHRINK_SIZE 512
60 /*! \brief Global IO variables are now in a struct in order to be
61 made threadsafe */
62 struct io_context {
63 struct pollfd *fds; /*!< Poll structure */
64 struct io_rec *ior; /*!< Associated I/O records */
65 unsigned int fdcnt; /*!< First available fd */
66 unsigned int maxfdcnt; /*!< Maximum available fd */
67 int current_ioc; /*!< Currently used io callback */
68 int needshrink; /*!< Whether something has been deleted */
71 /*! \brief Create an I/O context */
72 struct io_context *io_context_create(void)
74 struct io_context *tmp = NULL;
76 if (!(tmp = ast_malloc(sizeof(*tmp))))
77 return NULL;
79 tmp->needshrink = 0;
80 tmp->fdcnt = 0;
81 tmp->maxfdcnt = GROW_SHRINK_SIZE/2;
82 tmp->current_ioc = -1;
84 if (!(tmp->fds = ast_calloc(1, (GROW_SHRINK_SIZE / 2) * sizeof(*tmp->fds)))) {
85 ast_free(tmp);
86 tmp = NULL;
87 } else {
88 if (!(tmp->ior = ast_calloc(1, (GROW_SHRINK_SIZE / 2) * sizeof(*tmp->ior)))) {
89 ast_free(tmp->fds);
90 ast_free(tmp);
91 tmp = NULL;
95 return tmp;
98 void io_context_destroy(struct io_context *ioc)
100 /* Free associated memory with an I/O context */
101 if (ioc->fds)
102 ast_free(ioc->fds);
103 if (ioc->ior)
104 ast_free(ioc->ior);
106 ast_free(ioc);
109 /*! \brief
110 * Grow the size of our arrays.
111 * \return 0 on success or -1 on failure
113 static int io_grow(struct io_context *ioc)
115 void *tmp;
117 DEBUG(ast_debug(1, "io_grow()\n"));
119 ioc->maxfdcnt += GROW_SHRINK_SIZE;
121 if ((tmp = ast_realloc(ioc->ior, (ioc->maxfdcnt + 1) * sizeof(*ioc->ior)))) {
122 ioc->ior = tmp;
123 if ((tmp = ast_realloc(ioc->fds, (ioc->maxfdcnt + 1) * sizeof(*ioc->fds)))) {
124 ioc->fds = tmp;
125 } else {
127 * Failed to allocate enough memory for the pollfd. Not
128 * really any need to shrink back the iorec's as we'll
129 * probably want to grow them again soon when more memory
130 * is available, and then they'll already be the right size
132 ioc->maxfdcnt -= GROW_SHRINK_SIZE;
133 return -1;
135 } else {
137 * Memory allocation failure. We return to the old size, and
138 * return a failure
140 ioc->maxfdcnt -= GROW_SHRINK_SIZE;
141 return -1;
144 return 0;
147 /*! \brief
148 * Add a new I/O entry for this file descriptor
149 * with the given event mask, to call callback with
150 * data as an argument.
151 * \return Returns NULL on failure.
153 int *ast_io_add(struct io_context *ioc, int fd, ast_io_cb callback, short events, void *data)
155 int *ret;
157 DEBUG(ast_debug(1, "ast_io_add()\n"));
159 if (ioc->fdcnt >= ioc->maxfdcnt) {
161 * We don't have enough space for this entry. We need to
162 * reallocate maxfdcnt poll fd's and io_rec's, or back out now.
164 if (io_grow(ioc))
165 return NULL;
169 * At this point, we've got sufficiently large arrays going
170 * and we can make an entry for it in the pollfd and io_r
171 * structures.
173 ioc->fds[ioc->fdcnt].fd = fd;
174 ioc->fds[ioc->fdcnt].events = events;
175 ioc->fds[ioc->fdcnt].revents = 0;
176 ioc->ior[ioc->fdcnt].callback = callback;
177 ioc->ior[ioc->fdcnt].data = data;
179 if (!(ioc->ior[ioc->fdcnt].id = ast_malloc(sizeof(*ioc->ior[ioc->fdcnt].id)))) {
180 /* Bonk if we couldn't allocate an int */
181 return NULL;
184 *(ioc->ior[ioc->fdcnt].id) = ioc->fdcnt;
185 ret = ioc->ior[ioc->fdcnt].id;
186 ioc->fdcnt++;
188 return ret;
191 int *ast_io_change(struct io_context *ioc, int *id, int fd, ast_io_cb callback, short events, void *data)
193 /* If this id exceeds our file descriptor count it doesn't exist here */
194 if (*id > ioc->fdcnt)
195 return NULL;
197 if (fd > -1)
198 ioc->fds[*id].fd = fd;
199 if (callback)
200 ioc->ior[*id].callback = callback;
201 if (events)
202 ioc->fds[*id].events = events;
203 if (data)
204 ioc->ior[*id].data = data;
206 return id;
209 static int io_shrink(struct io_context *ioc)
211 int getfrom, putto = 0;
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.
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;
226 putto++;
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;
236 int ast_io_remove(struct io_context *ioc, int *_id)
238 int x;
240 if (!_id) {
241 ast_log(LOG_WARNING, "Asked to remove NULL?\n");
242 return -1;
245 for (x = 0; x < ioc->fdcnt; x++) {
246 if (ioc->ior[x].id == _id) {
247 /* Free the int immediately and set to NULL so we know it's unused now */
248 ast_free(ioc->ior[x].id);
249 ioc->ior[x].id = NULL;
250 ioc->fds[x].events = 0;
251 ioc->fds[x].revents = 0;
252 ioc->needshrink = 1;
253 if (ioc->current_ioc == -1)
254 io_shrink(ioc);
255 return 0;
259 ast_log(LOG_NOTICE, "Unable to remove unknown id %p\n", _id);
261 return -1;
264 /*! \brief
265 * Make the poll call, and call
266 * the callbacks for anything that needs
267 * to be handled
269 int ast_io_wait(struct io_context *ioc, int howlong)
271 int res, x, origcnt;
273 DEBUG(ast_debug(1, "ast_io_wait()\n"));
275 if ((res = poll(ioc->fds, ioc->fdcnt, howlong)) <= 0)
276 return res;
278 /* At least one event tripped */
279 origcnt = ioc->fdcnt;
280 for (x = 0; x < origcnt; x++) {
281 /* Yes, it is possible for an entry to be deleted and still have an
282 event waiting if it occurs after the original calling id */
283 if (ioc->fds[x].revents && ioc->ior[x].id) {
284 /* There's an event waiting */
285 ioc->current_ioc = *ioc->ior[x].id;
286 if (ioc->ior[x].callback) {
287 if (!ioc->ior[x].callback(ioc->ior[x].id, ioc->fds[x].fd, ioc->fds[x].revents, ioc->ior[x].data)) {
288 /* Time to delete them since they returned a 0 */
289 ast_io_remove(ioc, ioc->ior[x].id);
292 ioc->current_ioc = -1;
296 if (ioc->needshrink)
297 io_shrink(ioc);
299 return res;
302 void ast_io_dump(struct io_context *ioc)
305 * Print some debugging information via
306 * the logger interface
308 int x;
310 ast_debug(1, "Asterisk IO Dump: %d entries, %d max entries\n", ioc->fdcnt, ioc->maxfdcnt);
311 ast_debug(1, "================================================\n");
312 ast_debug(1, "| ID FD Callback Data Events |\n");
313 ast_debug(1, "+------+------+-----------+-----------+--------+\n");
314 for (x = 0; x < ioc->fdcnt; x++) {
315 ast_debug(1, "| %.4d | %.4d | %p | %p | %.6x |\n",
316 *ioc->ior[x].id,
317 ioc->fds[x].fd,
318 ioc->ior[x].callback,
319 ioc->ior[x].data,
320 ioc->fds[x].events);
322 ast_debug(1, "================================================\n");
325 /* Unrelated I/O functions */
327 int ast_hide_password(int fd)
329 struct termios tios;
330 int res;
331 int old;
332 if (!isatty(fd))
333 return -1;
334 res = tcgetattr(fd, &tios);
335 if (res < 0)
336 return -1;
337 old = tios.c_lflag & (ECHO | ECHONL);
338 tios.c_lflag &= ~ECHO;
339 tios.c_lflag |= ECHONL;
340 res = tcsetattr(fd, TCSAFLUSH, &tios);
341 if (res < 0)
342 return -1;
343 return old;
346 int ast_restore_tty(int fd, int oldstate)
348 int res;
349 struct termios tios;
350 if (oldstate < 0)
351 return 0;
352 res = tcgetattr(fd, &tios);
353 if (res < 0)
354 return -1;
355 tios.c_lflag &= ~(ECHO | ECHONL);
356 tios.c_lflag |= oldstate;
357 res = tcsetattr(fd, TCSAFLUSH, &tios);
358 if (res < 0)
359 return -1;
360 return 0;
363 int ast_get_termcols(int fd)
365 struct winsize win;
366 int cols = 0;
368 if (!isatty(fd))
369 return -1;
371 if ( ioctl(fd, TIOCGWINSZ, &win) != -1 ) {
372 if ( !cols && win.ws_col > 0 )
373 cols = (int) win.ws_col;
374 } else {
375 /* assume 80 characters if the ioctl fails for some reason */
376 cols = 80;
379 return cols;