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.
21 * \brief I/O Managment (Derived from Cheops-NG)
23 * \author Mark Spencer <markster@digium.com>
28 ASTERISK_FILE_VERSION(__FILE__
, "$Revision$")
31 #include <sys/ioctl.h>
33 #include "asterisk/io.h"
34 #include "asterisk/utils.h"
43 * Kept for each file descriptor
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
58 #define GROW_SHRINK_SIZE 512
60 /*! \brief Global IO variables are now in a struct in order to be
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
))))
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
)))) {
88 if (!(tmp
->ior
= ast_calloc(1, (GROW_SHRINK_SIZE
/ 2) * sizeof(*tmp
->ior
)))) {
98 void io_context_destroy(struct io_context
*ioc
)
100 /* Free associated memory with an I/O context */
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
)
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
)))) {
123 if ((tmp
= ast_realloc(ioc
->fds
, (ioc
->maxfdcnt
+ 1) * sizeof(*ioc
->fds
)))) {
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
;
137 * Memory allocation failure. We return to the old size, and
140 ioc
->maxfdcnt
-= GROW_SHRINK_SIZE
;
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
)
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.
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
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 */
184 *(ioc
->ior
[ioc
->fdcnt
].id
) = ioc
->fdcnt
;
185 ret
= ioc
->ior
[ioc
->fdcnt
].id
;
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
)
198 ioc
->fds
[*id
].fd
= fd
;
200 ioc
->ior
[*id
].callback
= callback
;
202 ioc
->fds
[*id
].events
= events
;
204 ioc
->ior
[*id
].data
= data
;
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
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
;
231 /* FIXME: We should free some memory if we have lots of unused
236 int ast_io_remove(struct io_context
*ioc
, int *_id
)
241 ast_log(LOG_WARNING
, "Asked to remove NULL?\n");
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;
253 if (ioc
->current_ioc
== -1)
259 ast_log(LOG_NOTICE
, "Unable to remove unknown id %p\n", _id
);
265 * Make the poll call, and call
266 * the callbacks for anything that needs
269 int ast_io_wait(struct io_context
*ioc
, int howlong
)
273 DEBUG(ast_debug(1, "ast_io_wait()\n"));
275 if ((res
= poll(ioc
->fds
, ioc
->fdcnt
, howlong
)) <= 0)
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;
302 void ast_io_dump(struct io_context
*ioc
)
305 * Print some debugging information via
306 * the logger interface
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",
318 ioc
->ior
[x
].callback
,
322 ast_debug(1, "================================================\n");
325 /* Unrelated I/O functions */
327 int ast_hide_password(int fd
)
334 res
= tcgetattr(fd
, &tios
);
337 old
= tios
.c_lflag
& (ECHO
| ECHONL
);
338 tios
.c_lflag
&= ~ECHO
;
339 tios
.c_lflag
|= ECHONL
;
340 res
= tcsetattr(fd
, TCSAFLUSH
, &tios
);
346 int ast_restore_tty(int fd
, int oldstate
)
352 res
= tcgetattr(fd
, &tios
);
355 tios
.c_lflag
&= ~(ECHO
| ECHONL
);
356 tios
.c_lflag
|= oldstate
;
357 res
= tcsetattr(fd
, TCSAFLUSH
, &tios
);
363 int ast_get_termcols(int fd
)
371 if ( ioctl(fd
, TIOCGWINSZ
, &win
) != -1 ) {
372 if ( !cols
&& win
.ws_col
> 0 )
373 cols
= (int) win
.ws_col
;
375 /* assume 80 characters if the ioctl fails for some reason */