Wed Jan 8 2020 09:49:48

Asterisk developer's documentation


io.c
Go to the documentation of this file.
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 /*** MODULEINFO
27  <support_level>core</support_level>
28  ***/
29 
30 #include "asterisk.h"
31 
32 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 413586 $")
33 
34 #include <termios.h>
35 #include <sys/ioctl.h>
36 
37 #include "asterisk/io.h"
38 #include "asterisk/utils.h"
39 
40 #ifdef DEBUG_IO
41 #define DEBUG DEBUG_M
42 #else
43 #define DEBUG(a)
44 #endif
45 
46 /*! \brief
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_SIZE structures
60  at once */
61 
62 #define GROW_SHRINK_SIZE 512
63 
64 /*! \brief Global IO variables are now in a struct in order to be
65  made threadsafe */
66 struct io_context {
67  struct pollfd *fds; /*!< Poll structure */
68  struct io_rec *ior; /*!< Associated I/O records */
69  unsigned int fdcnt; /*!< First available fd */
70  unsigned int maxfdcnt; /*!< Maximum available fd */
71  int current_ioc; /*!< Currently used io callback */
72  int needshrink; /*!< Whether something has been deleted */
73 };
74 
75 /*! \brief Create an I/O context */
77 {
78  struct io_context *tmp = NULL;
79 
80  if (!(tmp = ast_malloc(sizeof(*tmp))))
81  return NULL;
82 
83  tmp->needshrink = 0;
84  tmp->fdcnt = 0;
85  tmp->maxfdcnt = GROW_SHRINK_SIZE/2;
86  tmp->current_ioc = -1;
87 
88  if (!(tmp->fds = ast_calloc(1, (GROW_SHRINK_SIZE / 2) * sizeof(*tmp->fds)))) {
89  ast_free(tmp);
90  tmp = NULL;
91  } else {
92  if (!(tmp->ior = ast_calloc(1, (GROW_SHRINK_SIZE / 2) * sizeof(*tmp->ior)))) {
93  ast_free(tmp->fds);
94  ast_free(tmp);
95  tmp = NULL;
96  }
97  }
98 
99  return tmp;
100 }
101 
103 {
104  /* Free associated memory with an I/O context */
105  if (ioc->fds)
106  ast_free(ioc->fds);
107  if (ioc->ior)
108  ast_free(ioc->ior);
109 
110  ast_free(ioc);
111 }
112 
113 /*! \brief
114  * Grow the size of our arrays.
115  * \return 0 on success or -1 on failure
116  */
117 static int io_grow(struct io_context *ioc)
118 {
119  void *tmp;
120 
121  DEBUG(ast_debug(1, "io_grow()\n"));
122 
123  ioc->maxfdcnt += GROW_SHRINK_SIZE;
124 
125  if ((tmp = ast_realloc(ioc->ior, (ioc->maxfdcnt + 1) * sizeof(*ioc->ior)))) {
126  ioc->ior = tmp;
127  if ((tmp = ast_realloc(ioc->fds, (ioc->maxfdcnt + 1) * sizeof(*ioc->fds)))) {
128  ioc->fds = tmp;
129  } else {
130  /*
131  * Failed to allocate enough memory for the pollfd. Not
132  * really any need to shrink back the iorec's as we'll
133  * probably want to grow them again soon when more memory
134  * is available, and then they'll already be the right size
135  */
136  ioc->maxfdcnt -= GROW_SHRINK_SIZE;
137  return -1;
138  }
139  } else {
140  /*
141  * Memory allocation failure. We return to the old size, and
142  * return a failure
143  */
144  ioc->maxfdcnt -= GROW_SHRINK_SIZE;
145  return -1;
146  }
147 
148  return 0;
149 }
150 
151 /*! \brief
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.
155  * \return Returns NULL on failure.
156  */
157 int *ast_io_add(struct io_context *ioc, int fd, ast_io_cb callback, short events, void *data)
158 {
159  int *ret;
160 
161  DEBUG(ast_debug(1, "ast_io_add()\n"));
162 
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 
183  if (!(ioc->ior[ioc->fdcnt].id = ast_malloc(sizeof(*ioc->ior[ioc->fdcnt].id)))) {
184  /* Bonk if we couldn't allocate an int */
185  return NULL;
186  }
187 
188  *(ioc->ior[ioc->fdcnt].id) = ioc->fdcnt;
189  ret = ioc->ior[ioc->fdcnt].id;
190  ioc->fdcnt++;
191 
192  return ret;
193 }
194 
195 int *ast_io_change(struct io_context *ioc, int *id, int fd, ast_io_cb callback, short events, void *data)
196 {
197  /* If this id exceeds our file descriptor count it doesn't exist here */
198  if (*id > ioc->fdcnt)
199  return NULL;
200 
201  if (fd > -1)
202  ioc->fds[*id].fd = fd;
203  if (callback)
204  ioc->ior[*id].callback = callback;
205  if (events)
206  ioc->fds[*id].events = events;
207  if (data)
208  ioc->ior[*id].data = data;
209 
210  return id;
211 }
212 
213 static int io_shrink(struct io_context *ioc)
214 {
215  int getfrom, putto = 0;
216 
217  /*
218  * Bring the fields from the very last entry to cover over
219  * the entry we are removing, then decrease the size of the
220  * arrays by one.
221  */
222  for (getfrom = 0; getfrom < ioc->fdcnt; getfrom++) {
223  if (ioc->ior[getfrom].id) {
224  /* In use, save it */
225  if (getfrom != putto) {
226  ioc->fds[putto] = ioc->fds[getfrom];
227  ioc->ior[putto] = ioc->ior[getfrom];
228  *(ioc->ior[putto].id) = putto;
229  }
230  putto++;
231  }
232  }
233  ioc->fdcnt = putto;
234  ioc->needshrink = 0;
235  /* FIXME: We should free some memory if we have lots of unused
236  io structs */
237  return 0;
238 }
239 
240 int ast_io_remove(struct io_context *ioc, int *_id)
241 {
242  int x;
243 
244  if (!_id) {
245  ast_log(LOG_WARNING, "Asked to remove NULL?\n");
246  return -1;
247  }
248 
249  for (x = 0; x < ioc->fdcnt; x++) {
250  if (ioc->ior[x].id == _id) {
251  /* Free the int immediately and set to NULL so we know it's unused now */
252  ast_free(ioc->ior[x].id);
253  ioc->ior[x].id = NULL;
254  ioc->fds[x].events = 0;
255  ioc->fds[x].revents = 0;
256  ioc->needshrink = 1;
257  if (ioc->current_ioc == -1)
258  io_shrink(ioc);
259  return 0;
260  }
261  }
262 
263  ast_log(LOG_NOTICE, "Unable to remove unknown id %p\n", _id);
264 
265  return -1;
266 }
267 
268 /*! \brief
269  * Make the poll call, and call
270  * the callbacks for anything that needs
271  * to be handled
272  */
273 int ast_io_wait(struct io_context *ioc, int howlong)
274 {
275  int res, x, origcnt;
276 
277  DEBUG(ast_debug(1, "ast_io_wait()\n"));
278 
279  if ((res = ast_poll(ioc->fds, ioc->fdcnt, howlong)) <= 0) {
280  return res;
281  }
282 
283  /* At least one event tripped */
284  origcnt = ioc->fdcnt;
285  for (x = 0; x < origcnt; x++) {
286  /* Yes, it is possible for an entry to be deleted and still have an
287  event waiting if it occurs after the original calling id */
288  if (ioc->fds[x].revents && ioc->ior[x].id) {
289  /* There's an event waiting */
290  ioc->current_ioc = *ioc->ior[x].id;
291  if (ioc->ior[x].callback) {
292  if (!ioc->ior[x].callback(ioc->ior[x].id, ioc->fds[x].fd, ioc->fds[x].revents, ioc->ior[x].data)) {
293  /* Time to delete them since they returned a 0 */
294  ast_io_remove(ioc, ioc->ior[x].id);
295  }
296  }
297  ioc->current_ioc = -1;
298  }
299  }
300 
301  if (ioc->needshrink)
302  io_shrink(ioc);
303 
304  return res;
305 }
306 
307 void ast_io_dump(struct io_context *ioc)
308 {
309  /*
310  * Print some debugging information via
311  * the logger interface
312  */
313  int x;
314 
315  ast_debug(1, "Asterisk IO Dump: %u entries, %u max entries\n", ioc->fdcnt, ioc->maxfdcnt);
316  ast_debug(1, "================================================\n");
317  ast_debug(1, "| ID FD Callback Data Events |\n");
318  ast_debug(1, "+------+------+-----------+-----------+--------+\n");
319  for (x = 0; x < ioc->fdcnt; x++) {
320  ast_debug(1, "| %.4d | %.4d | %p | %p | %.6x |\n",
321  *ioc->ior[x].id,
322  ioc->fds[x].fd,
323  ioc->ior[x].callback,
324  ioc->ior[x].data,
325  (unsigned)ioc->fds[x].events);
326  }
327  ast_debug(1, "================================================\n");
328 }
329 
330 /* Unrelated I/O functions */
331 
332 int ast_hide_password(int fd)
333 {
334  struct termios tios;
335  int res;
336  int old;
337  if (!isatty(fd))
338  return -1;
339  res = tcgetattr(fd, &tios);
340  if (res < 0)
341  return -1;
342  old = tios.c_lflag & (ECHO | ECHONL);
343  tios.c_lflag &= ~ECHO;
344  tios.c_lflag |= ECHONL;
345  res = tcsetattr(fd, TCSAFLUSH, &tios);
346  if (res < 0)
347  return -1;
348  return old;
349 }
350 
351 int ast_restore_tty(int fd, int oldstate)
352 {
353  int res;
354  struct termios tios;
355  if (oldstate < 0)
356  return 0;
357  res = tcgetattr(fd, &tios);
358  if (res < 0)
359  return -1;
360  tios.c_lflag &= ~(ECHO | ECHONL);
361  tios.c_lflag |= oldstate;
362  res = tcsetattr(fd, TCSAFLUSH, &tios);
363  if (res < 0)
364  return -1;
365  return 0;
366 }
367 
368 int ast_get_termcols(int fd)
369 {
370  struct winsize win;
371  int cols = 0;
372 
373  if (!isatty(fd))
374  return -1;
375 
376  if ( ioctl(fd, TIOCGWINSZ, &win) != -1 ) {
377  if ( !cols && win.ws_col > 0 )
378  cols = (int) win.ws_col;
379  } else {
380  /* assume 80 characters if the ioctl fails for some reason */
381  cols = 80;
382  }
383 
384  return cols;
385 }
386 
int ast_io_wait(struct io_context *ioc, int howlong)
Waits for IO.
Definition: io.c:273
Asterisk main include file. File version handling, generic pbx functions.
static int io_shrink(struct io_context *ioc)
Definition: io.c:213
void * data
Definition: io.c:51
struct pollfd * fds
Definition: io.c:67
#define LOG_WARNING
Definition: logger.h:144
int ast_hide_password(int fd)
Definition: io.c:332
#define GROW_SHRINK_SIZE
Definition: io.c:62
Kept for each file descriptor.
Definition: io.c:49
void ast_io_dump(struct io_context *ioc)
Dumps the IO array. Debugging: Dump everything in the I/O array.
Definition: io.c:307
int * ast_io_add(struct io_context *ioc, int fd, ast_io_cb callback, short events, void *data)
Adds an IO context.
Definition: io.c:157
I/O Management (derived from Cheops-NG)
int ast_get_termcols(int fd)
Definition: io.c:368
Utility functions.
int ast_restore_tty(int fd, int oldstatus)
Restores TTY mode. Call with result from previous ast_hide_password.
Definition: io.c:351
#define DEBUG(a)
Definition: io.c:43
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:236
int current_ioc
Definition: io.c:71
void io_context_destroy(struct io_context *ioc)
Destroys a context.
Definition: io.c:102
#define ast_poll(a, b, c)
Definition: poll-compat.h:88
Global IO variables are now in a struct in order to be made threadsafe.
Definition: io.c:66
static int io_grow(struct io_context *ioc)
Grow the size of our arrays.
Definition: io.c:117
void ast_log(int level, const char *file, int line, const char *function, const char *fmt,...)
Used for sending a log message This is the standard logger function. Probably the only way you will i...
Definition: logger.c:1207
#define LOG_NOTICE
Definition: logger.h:133
#define ast_free(a)
Definition: astmm.h:97
ast_io_cb callback
Definition: io.c:50
int ast_io_remove(struct io_context *ioc, int *id)
Removes an IO context.
Definition: io.c:240
unsigned int fdcnt
Definition: io.c:69
struct io_rec * ior
Definition: io.c:68
int(* ast_io_cb)(int *id, int fd, short events, void *cbdata)
Definition: io.h:71
#define ast_calloc(a, b)
Definition: astmm.h:82
#define ast_realloc(a, b)
Definition: astmm.h:103
int * id
Definition: io.c:52
int needshrink
Definition: io.c:72
int * ast_io_change(struct io_context *ioc, int *id, int fd, ast_io_cb callback, short events, void *data)
Changes an IO handler.
Definition: io.c:195
int isatty(int)
enum queue_result id
Definition: app_queue.c:1090
static struct adsi_event events[]
Definition: app_adsiprog.c:78
#define ast_malloc(a)
Definition: astmm.h:91
#define ECHO
Definition: ast_expr2f.c:555
unsigned int maxfdcnt
Definition: io.c:70
#define ASTERISK_FILE_VERSION(file, version)
Register/unregister a source code file with the core.
Definition: asterisk.h:180
struct io_context * io_context_create(void)
Creates a context Create a context for I/O operations Basically mallocs an IO structure and sets up s...
Definition: io.c:76