Asterisk - The Open Source Telephony Project  18.5.0
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 <[email protected]>
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 <[email protected]>
24  */
25 
26 /*** MODULEINFO
27  <support_level>core</support_level>
28  ***/
29 
30 #include "asterisk.h"
31 
32 #include <termios.h>
33 #include <sys/ioctl.h>
34 
35 #include "asterisk/io.h"
36 #include "asterisk/utils.h"
37 #ifdef HAVE_SYSTEMD
38 #include <systemd/sd-daemon.h>
39 
40 #ifndef SD_LISTEN_FDS_START
41 #define SD_LISTEN_FDS_START 3
42 #endif
43 #endif
44 
45 #ifdef DEBUG_IO
46 #define DEBUG DEBUG_M
47 #else
48 #define DEBUG(a)
49 #endif
50 
51 /*! \brief
52  * Kept for each file descriptor
53  */
54 struct io_rec {
55  ast_io_cb callback; /*!< What is to be called */
56  void *data; /*!< Data to be passed */
57  int *id; /*!< ID number */
58 };
59 
60 /* These two arrays are keyed with
61  the same index. it's too bad that
62  pollfd doesn't have a callback field
63  or something like that. They grow as
64  needed, by GROW_SHRINK_SIZE structures
65  at once */
66 
67 #define GROW_SHRINK_SIZE 512
68 
69 /*! \brief Global IO variables are now in a struct in order to be
70  made threadsafe */
71 struct io_context {
72  struct pollfd *fds; /*!< Poll structure */
73  struct io_rec *ior; /*!< Associated I/O records */
74  unsigned int fdcnt; /*!< First available fd */
75  unsigned int maxfdcnt; /*!< Maximum available fd */
76  int current_ioc; /*!< Currently used io callback */
77  int needshrink; /*!< Whether something has been deleted */
78 };
79 
80 /*! \brief Create an I/O context */
82 {
83  struct io_context *tmp = NULL;
84 
85  if (!(tmp = ast_malloc(sizeof(*tmp))))
86  return NULL;
87 
88  tmp->needshrink = 0;
89  tmp->fdcnt = 0;
90  tmp->maxfdcnt = GROW_SHRINK_SIZE/2;
91  tmp->current_ioc = -1;
92 
93  if (!(tmp->fds = ast_calloc(1, (GROW_SHRINK_SIZE / 2) * sizeof(*tmp->fds)))) {
94  ast_free(tmp);
95  tmp = NULL;
96  } else {
97  if (!(tmp->ior = ast_calloc(1, (GROW_SHRINK_SIZE / 2) * sizeof(*tmp->ior)))) {
98  ast_free(tmp->fds);
99  ast_free(tmp);
100  tmp = NULL;
101  }
102  }
103 
104  return tmp;
105 }
106 
108 {
109  /* Free associated memory with an I/O context */
110  if (ioc->fds)
111  ast_free(ioc->fds);
112  if (ioc->ior)
113  ast_free(ioc->ior);
114 
115  ast_free(ioc);
116 }
117 
118 /*! \brief
119  * Grow the size of our arrays.
120  * \return 0 on success or -1 on failure
121  */
122 static int io_grow(struct io_context *ioc)
123 {
124  void *tmp;
125 
126  DEBUG(ast_debug(1, "io_grow()\n"));
127 
128  ioc->maxfdcnt += GROW_SHRINK_SIZE;
129 
130  if ((tmp = ast_realloc(ioc->ior, (ioc->maxfdcnt + 1) * sizeof(*ioc->ior)))) {
131  ioc->ior = tmp;
132  if ((tmp = ast_realloc(ioc->fds, (ioc->maxfdcnt + 1) * sizeof(*ioc->fds)))) {
133  ioc->fds = tmp;
134  } else {
135  /*
136  * Failed to allocate enough memory for the pollfd. Not
137  * really any need to shrink back the iorec's as we'll
138  * probably want to grow them again soon when more memory
139  * is available, and then they'll already be the right size
140  */
141  ioc->maxfdcnt -= GROW_SHRINK_SIZE;
142  return -1;
143  }
144  } else {
145  /*
146  * Memory allocation failure. We return to the old size, and
147  * return a failure
148  */
149  ioc->maxfdcnt -= GROW_SHRINK_SIZE;
150  return -1;
151  }
152 
153  return 0;
154 }
155 
156 /*! \brief
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.
160  * \return Returns NULL on failure.
161  */
162 int *ast_io_add(struct io_context *ioc, int fd, ast_io_cb callback, short events, void *data)
163 {
164  int *ret;
165 
166  DEBUG(ast_debug(1, "ast_io_add()\n"));
167 
168  if (ioc->fdcnt >= ioc->maxfdcnt) {
169  /*
170  * We don't have enough space for this entry. We need to
171  * reallocate maxfdcnt poll fd's and io_rec's, or back out now.
172  */
173  if (io_grow(ioc))
174  return NULL;
175  }
176 
177  /*
178  * At this point, we've got sufficiently large arrays going
179  * and we can make an entry for it in the pollfd and io_r
180  * structures.
181  */
182  ioc->fds[ioc->fdcnt].fd = fd;
183  ioc->fds[ioc->fdcnt].events = events;
184  ioc->fds[ioc->fdcnt].revents = 0;
185  ioc->ior[ioc->fdcnt].callback = callback;
186  ioc->ior[ioc->fdcnt].data = data;
187 
188  if (!(ioc->ior[ioc->fdcnt].id = ast_malloc(sizeof(*ioc->ior[ioc->fdcnt].id)))) {
189  /* Bonk if we couldn't allocate an int */
190  return NULL;
191  }
192 
193  *(ioc->ior[ioc->fdcnt].id) = ioc->fdcnt;
194  ret = ioc->ior[ioc->fdcnt].id;
195  ioc->fdcnt++;
196 
197  return ret;
198 }
199 
200 int *ast_io_change(struct io_context *ioc, int *id, int fd, ast_io_cb callback, short events, void *data)
201 {
202  /* If this id exceeds our file descriptor count it doesn't exist here */
203  if (*id > ioc->fdcnt)
204  return NULL;
205 
206  if (fd > -1)
207  ioc->fds[*id].fd = fd;
208  if (callback)
209  ioc->ior[*id].callback = callback;
210  if (events)
211  ioc->fds[*id].events = events;
212  if (data)
213  ioc->ior[*id].data = data;
214 
215  return id;
216 }
217 
218 static int io_shrink(struct io_context *ioc)
219 {
220  int getfrom, putto = 0;
221 
222  /*
223  * Bring the fields from the very last entry to cover over
224  * the entry we are removing, then decrease the size of the
225  * arrays by one.
226  */
227  for (getfrom = 0; getfrom < ioc->fdcnt; getfrom++) {
228  if (ioc->ior[getfrom].id) {
229  /* In use, save it */
230  if (getfrom != putto) {
231  ioc->fds[putto] = ioc->fds[getfrom];
232  ioc->ior[putto] = ioc->ior[getfrom];
233  *(ioc->ior[putto].id) = putto;
234  }
235  putto++;
236  }
237  }
238  ioc->fdcnt = putto;
239  ioc->needshrink = 0;
240  /* FIXME: We should free some memory if we have lots of unused
241  io structs */
242  return 0;
243 }
244 
245 int ast_io_remove(struct io_context *ioc, int *_id)
246 {
247  int x;
248 
249  if (!_id) {
250  ast_log(LOG_WARNING, "Asked to remove NULL?\n");
251  return -1;
252  }
253 
254  for (x = 0; x < ioc->fdcnt; x++) {
255  if (ioc->ior[x].id == _id) {
256  /* Free the int immediately and set to NULL so we know it's unused now */
257  ast_free(ioc->ior[x].id);
258  ioc->ior[x].id = NULL;
259  ioc->fds[x].events = 0;
260  ioc->fds[x].revents = 0;
261  ioc->needshrink = 1;
262  if (ioc->current_ioc == -1)
263  io_shrink(ioc);
264  return 0;
265  }
266  }
267 
268  ast_log(LOG_NOTICE, "Unable to remove unknown id %p\n", _id);
269 
270  return -1;
271 }
272 
273 /*! \brief
274  * Make the poll call, and call
275  * the callbacks for anything that needs
276  * to be handled
277  */
278 int ast_io_wait(struct io_context *ioc, int howlong)
279 {
280  int res, x, origcnt;
281 
282  DEBUG(ast_debug(1, "ast_io_wait()\n"));
283 
284  if ((res = ast_poll(ioc->fds, ioc->fdcnt, howlong)) <= 0) {
285  return res;
286  }
287 
288  /* At least one event tripped */
289  origcnt = ioc->fdcnt;
290  for (x = 0; x < origcnt; x++) {
291  /* Yes, it is possible for an entry to be deleted and still have an
292  event waiting if it occurs after the original calling id */
293  if (ioc->fds[x].revents && ioc->ior[x].id) {
294  /* There's an event waiting */
295  ioc->current_ioc = *ioc->ior[x].id;
296  if (ioc->ior[x].callback) {
297  if (!ioc->ior[x].callback(ioc->ior[x].id, ioc->fds[x].fd, ioc->fds[x].revents, ioc->ior[x].data)) {
298  /* Time to delete them since they returned a 0 */
299  ast_io_remove(ioc, ioc->ior[x].id);
300  }
301  }
302  ioc->current_ioc = -1;
303  }
304  }
305 
306  if (ioc->needshrink)
307  io_shrink(ioc);
308 
309  return res;
310 }
311 
312 void ast_io_dump(struct io_context *ioc)
313 {
314  /*
315  * Print some debugging information via
316  * the logger interface
317  */
318  int x;
319 
320  ast_debug(1, "Asterisk IO Dump: %u entries, %u max entries\n", ioc->fdcnt, ioc->maxfdcnt);
321  ast_debug(1, "================================================\n");
322  ast_debug(1, "| ID FD Callback Data Events |\n");
323  ast_debug(1, "+------+------+-----------+-----------+--------+\n");
324  for (x = 0; x < ioc->fdcnt; x++) {
325  ast_debug(1, "| %.4d | %.4d | %p | %p | %.6x |\n",
326  *ioc->ior[x].id,
327  ioc->fds[x].fd,
328  ioc->ior[x].callback,
329  ioc->ior[x].data,
330  (unsigned)ioc->fds[x].events);
331  }
332  ast_debug(1, "================================================\n");
333 }
334 
335 /* Unrelated I/O functions */
336 
337 int ast_hide_password(int fd)
338 {
339  struct termios tios;
340  int res;
341  int old;
342  if (!isatty(fd))
343  return -1;
344  res = tcgetattr(fd, &tios);
345  if (res < 0)
346  return -1;
347  old = tios.c_lflag & (ECHO | ECHONL);
348  tios.c_lflag &= ~ECHO;
349  tios.c_lflag |= ECHONL;
350  res = tcsetattr(fd, TCSAFLUSH, &tios);
351  if (res < 0)
352  return -1;
353  return old;
354 }
355 
356 int ast_restore_tty(int fd, int oldstate)
357 {
358  int res;
359  struct termios tios;
360  if (oldstate < 0)
361  return 0;
362  res = tcgetattr(fd, &tios);
363  if (res < 0)
364  return -1;
365  tios.c_lflag &= ~(ECHO | ECHONL);
366  tios.c_lflag |= oldstate;
367  res = tcsetattr(fd, TCSAFLUSH, &tios);
368  if (res < 0)
369  return -1;
370  return 0;
371 }
372 
373 int ast_get_termcols(int fd)
374 {
375  struct winsize win;
376  int cols = 0;
377 
378  if (!isatty(fd))
379  return -1;
380 
381  if ( ioctl(fd, TIOCGWINSZ, &win) != -1 ) {
382  if ( !cols && win.ws_col > 0 )
383  cols = (int) win.ws_col;
384  } else {
385  /* assume 80 characters if the ioctl fails for some reason */
386  cols = 80;
387  }
388 
389  return cols;
390 }
391 
392 int ast_sd_notify(const char *state) {
393 #ifdef HAVE_SYSTEMD
394  return sd_notify(0, state);
395 #else
396  return 0;
397 #endif
398 }
399 
400 /*!
401  * \internal \brief Check the type and sockaddr of a file descriptor.
402  * \param fd File Descriptor to check.
403  * \param type SOCK_STREAM or SOCK_DGRAM
404  * \param addr The socket address to match.
405  * \retval 0 if matching
406  * \retval -1 if not matching
407  */
408 #ifdef HAVE_SYSTEMD
409 static int ast_sd_is_socket_sockaddr(int fd, int type, const struct ast_sockaddr* addr)
410 {
411  int canretry = 1;
412  struct ast_sockaddr fd_addr;
413  struct sockaddr ss;
414  socklen_t ss_len;
415 
416  if (sd_is_socket(fd, AF_UNSPEC, type, 1) <= 0) {
417  return -1;
418  }
419 
420 doretry:
421  if (getsockname(fd, &ss, &ss_len) != 0) {
422  return -1;
423  }
424 
425  if (ss.sa_family == AF_UNSPEC && canretry) {
426  /* An unknown bug can cause silent failure from
427  * the first call to getsockname. */
428  canretry = 0;
429  goto doretry;
430  }
431 
432  ast_sockaddr_copy_sockaddr(&fd_addr, &ss, ss_len);
433 
434  return ast_sockaddr_cmp(addr, &fd_addr);
435 }
436 #endif
437 
438 int ast_sd_get_fd(int type, const struct ast_sockaddr *addr)
439 {
440 #ifdef HAVE_SYSTEMD
441  int count = sd_listen_fds(0);
442  int idx;
443 
444  for (idx = 0; idx < count; idx++) {
445  if (!ast_sd_is_socket_sockaddr(idx + SD_LISTEN_FDS_START, type, addr)) {
446  return idx + SD_LISTEN_FDS_START;
447  }
448  }
449 #endif
450 
451  return -1;
452 }
453 
454 int ast_sd_get_fd_un(int type, const char *path)
455 {
456 #ifdef HAVE_SYSTEMD
457  int count = sd_listen_fds(0);
458  int idx;
459 
460  for (idx = 0; idx < count; idx++) {
461  if (sd_is_socket_unix(idx + SD_LISTEN_FDS_START, type, 1, path, 0) > 0) {
462  return idx + SD_LISTEN_FDS_START;
463  }
464  }
465 #endif
466 
467  return -1;
468 }
static const char type[]
Definition: chan_ooh323.c:109
Asterisk main include file. File version handling, generic pbx functions.
#define ast_realloc(p, len)
A wrapper for realloc()
Definition: astmm.h:228
static int io_shrink(struct io_context *ioc)
Definition: io.c:218
void * data
Definition: io.c:56
struct pollfd * fds
Definition: io.c:72
#define LOG_WARNING
Definition: logger.h:274
#define GROW_SHRINK_SIZE
Definition: io.c:67
static int tmp()
Definition: bt_open.c:389
int * ast_io_add(struct io_context *ioc, int fd, ast_io_cb callback, short events, void *data)
Add a new I/O entry for this file descriptor with the given event mask, to call callback with data as...
Definition: io.c:162
Kept for each file descriptor.
Definition: io.c:54
int ast_sd_notify(const char *state)
a wrapper for sd_notify(): notify systemd of any state changes.
Definition: io.c:392
static const struct adsi_event events[]
Definition: app_adsiprog.c:85
int ast_sd_get_fd_un(int type, const char *path)
Find a listening AF_LOCAL file descriptor provided by socket activation.
Definition: io.c:454
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:200
void ast_io_dump(struct io_context *ioc)
Dumps the IO array. Debugging: Dump everything in the I/O array.
Definition: io.c:312
#define NULL
Definition: resample.c:96
int ast_sockaddr_cmp(const struct ast_sockaddr *a, const struct ast_sockaddr *b)
Compares two ast_sockaddr structures.
Definition: netsock2.c:388
I/O Management (derived from Cheops-NG)
Socket address structure.
Definition: netsock2.h:97
Utility functions.
#define DEBUG(a)
Definition: io.c:48
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:452
#define ast_log
Definition: astobj2.c:42
int current_ioc
Definition: io.c:76
int(* ast_io_cb)(int *id, int fd, short events, void *cbdata)
Definition: io.h:72
static void ast_sockaddr_copy_sockaddr(struct ast_sockaddr *dst, struct sockaddr *src, socklen_t len)
Copies the data from a sockaddr to an ast_sockaddr.
Definition: netsock2.h:154
#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:71
static int io_grow(struct io_context *ioc)
Grow the size of our arrays.
Definition: io.c:122
int ast_get_termcols(int fd)
Definition: io.c:373
#define ast_malloc(len)
A wrapper for malloc()
Definition: astmm.h:193
#define LOG_NOTICE
Definition: logger.h:263
#define ast_free(a)
Definition: astmm.h:182
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:204
ast_io_cb callback
Definition: io.c:55
int ast_hide_password(int fd)
Definition: io.c:337
int ast_sd_get_fd(int type, const struct ast_sockaddr *addr)
Find a listening file descriptor provided by socket activation.
Definition: io.c:438
unsigned int fdcnt
Definition: io.c:74
struct io_rec * ior
Definition: io.c:73
int ast_io_wait(struct io_context *ioc, int howlong)
Make the poll call, and call the callbacks for anything that needs to be handled. ...
Definition: io.c:278
int * id
Definition: io.c:57
int needshrink
Definition: io.c:77
void io_context_destroy(struct io_context *ioc)
Destroys a context.
Definition: io.c:107
int isatty(int)
int ast_io_remove(struct io_context *ioc, int *_id)
Removes an IO context.
Definition: io.c:245
#define ECHO
Definition: ast_expr2f.c:563
unsigned int maxfdcnt
Definition: io.c:75
struct io_context * io_context_create(void)
Create an I/O context.
Definition: io.c:81
int ast_restore_tty(int fd, int oldstate)
Restores TTY mode. Call with result from previous ast_hide_password.
Definition: io.c:356