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