x86-64 compile fixes and cleanups
[asterisk/asterisk.git] / io.c
1 /*
2  * Asterisk
3  * 
4  * Mark Spencer <markster@marko.net>
5  *
6  * Copyright(C) Mark Spencer
7  * 
8  * Distributed under the terms of the GNU General Public License (GPL) Version 2
9  *
10  * I/O Managment (Derived from Cheops-NG)
11  *
12  */
13
14 #include <stdio.h>
15 #include <unistd.h>
16 #include <stdlib.h>
17 #include <termios.h>
18 #include <string.h> /* for memset */
19 #include <sys/ioctl.h>
20 #include <asterisk/io.h>
21 #include <asterisk/logger.h>
22
23 #ifdef DEBUG_IO
24 #define DEBUG DEBUG_M
25 #else
26 #define DEBUG(a) 
27 #endif
28
29 /* 
30  * Kept for each file descriptor
31  */
32 struct io_rec {
33         ast_io_cb callback;             /* What is to be called */
34         void *data;                             /* Data to be passed */
35         int *id;                                        /* ID number */
36 };
37
38 /* These two arrays are keyed with
39    the same index.  it's too bad that
40    pollfd doesn't have a callback field
41    or something like that.  They grow as
42    needed, by GROW_SHRINK_AMOUNT structures
43    at once */
44
45 #define GROW_SHRINK_SIZE 512
46
47 /* Global variables are now in a struct in order to be
48    made threadsafe */
49 struct io_context {
50         /* Poll structure */
51         struct pollfd *fds;
52         /* Associated I/O records */
53         struct io_rec *ior;
54         /* First available fd */
55         unsigned int fdcnt;
56         /* Maximum available fd */
57         unsigned int maxfdcnt;
58         /* Currently used io callback */
59         int current_ioc;
60         /* Whether something has been deleted */
61         int needshrink;
62 };
63
64
65 struct io_context *io_context_create(void)
66 {
67         /* Create an I/O context */
68         struct io_context *tmp;
69         tmp = malloc(sizeof(struct io_context));
70         if (tmp) {
71                 tmp->needshrink = 0;
72                 tmp->fdcnt = 0;
73                 tmp->maxfdcnt = GROW_SHRINK_SIZE/2;
74                 tmp->current_ioc = -1;
75                 tmp->fds = malloc((GROW_SHRINK_SIZE/2) * sizeof(struct pollfd));
76                 if (!tmp->fds) {
77                         free(tmp);
78                         tmp = NULL;
79                 } else {
80                         memset(tmp->fds, 0, (GROW_SHRINK_SIZE/2) * sizeof(struct pollfd));
81                         tmp->ior =  malloc((GROW_SHRINK_SIZE/2) * sizeof(struct io_rec));
82                         if (!tmp->ior) {
83                                 free(tmp->fds);
84                                 free(tmp);
85                                 tmp = NULL;
86                         } else
87                                 memset(tmp->ior, 0, (GROW_SHRINK_SIZE/2) * sizeof(struct io_rec));
88                 }
89         }
90         return tmp;
91 }
92
93 void io_context_destroy(struct io_context *ioc)
94 {
95         /* Free associated memory with an I/O context */
96         if (ioc->fds)
97                 free(ioc->fds);
98         if (ioc->ior)
99                 free(ioc->ior);
100         free(ioc);
101 }
102
103 static int io_grow(struct io_context *ioc)
104 {
105         /* 
106          * Grow the size of our arrays.  Return 0 on success or
107          * -1 on failure
108          */
109         void *tmp;
110         DEBUG(ast_log(LOG_DEBUG, "io_grow()\n"));
111         ioc->maxfdcnt += GROW_SHRINK_SIZE;
112         tmp = realloc(ioc->ior, (ioc->maxfdcnt + 1) * sizeof(struct io_rec));
113         if (tmp) {
114                 ioc->ior = (struct io_rec *)tmp;
115                 tmp = realloc(ioc->fds, (ioc->maxfdcnt + 1) * sizeof(struct pollfd));
116                 if (tmp) {
117                         ioc->fds = tmp;
118                 } else {
119                         /*
120                          * Not enough memory for the pollfd.  Not really any need
121                          * to shrink back the iorec's as we'll probably want to
122                          * grow them again soon when more memory is available, and
123                          * then they'll already be the right size
124                          */
125                         ioc->maxfdcnt -= GROW_SHRINK_SIZE;
126                         return -1;
127                 }
128                 
129         } else {
130                 /*
131                  * Out of memory.  We return to the old size, and return a failure
132                  */
133                 ioc->maxfdcnt -= GROW_SHRINK_SIZE;
134                 return -1;
135         }
136         return 0;
137 }
138
139 int *ast_io_add(struct io_context *ioc, int fd, ast_io_cb callback, short events, void *data)
140 {
141         /*
142          * Add a new I/O entry for this file descriptor
143          * with the given event mask, to call callback with
144          * data as an argument.  Returns NULL on failure.
145          */
146         int *ret;
147         DEBUG(ast_log(LOG_DEBUG, "ast_io_add()\n"));
148         if (ioc->fdcnt >= ioc->maxfdcnt) {
149                 /* 
150                  * We don't have enough space for this entry.  We need to
151                  * reallocate maxfdcnt poll fd's and io_rec's, or back out now.
152                  */
153                 if (io_grow(ioc))
154                         return NULL;
155         }
156
157         /*
158          * At this point, we've got sufficiently large arrays going
159          * and we can make an entry for it in the pollfd and io_r
160          * structures.
161          */
162         ioc->fds[ioc->fdcnt].fd = fd;
163         ioc->fds[ioc->fdcnt].events = events;
164         ioc->ior[ioc->fdcnt].callback = callback;
165         ioc->ior[ioc->fdcnt].data = data;
166         ioc->ior[ioc->fdcnt].id = (int *)malloc(sizeof(int));
167         /* Bonk if we couldn't allocate an int */
168         if (!ioc->ior[ioc->fdcnt].id)
169                 return NULL;
170         *(ioc->ior[ioc->fdcnt].id) = ioc->fdcnt;
171         ret = ioc->ior[ioc->fdcnt].id;
172         ioc->fdcnt++;
173         return ret;
174 }
175
176 int *ast_io_change(struct io_context *ioc, int *id, int fd, ast_io_cb callback, short events, void *data)
177 {
178         if (*id < ioc->fdcnt) {
179                 if (fd > -1)
180                         ioc->fds[*id].fd = fd;
181                 if (callback)
182                         ioc->ior[*id].callback = callback;
183                 if (events)
184                         ioc->fds[*id].events = events;
185                 if (data)
186                         ioc->ior[*id].data = data;
187                 return id;
188         } else return NULL;
189 }
190
191 static int io_shrink(struct io_context *ioc)
192 {
193         int getfrom;
194         int putto = 0;
195         /* 
196          * Bring the fields from the very last entry to cover over
197          * the entry we are removing, then decrease the size of the 
198          * arrays by one.
199          */
200         for (getfrom=0;getfrom<ioc->fdcnt;getfrom++) {
201                 if (ioc->ior[getfrom].id) {
202                         /* In use, save it */
203                         if (getfrom != putto) {
204                                 ioc->fds[putto] = ioc->fds[getfrom];
205                                 ioc->ior[putto] = ioc->ior[getfrom];
206                                 *(ioc->ior[putto].id) = putto;
207                         }
208                         putto++;
209                 }
210         }
211         ioc->fdcnt = putto;
212         ioc->needshrink = 0;
213         /* FIXME: We should free some memory if we have lots of unused
214            io structs */
215         return 0;
216 }
217
218 int ast_io_remove(struct io_context *ioc, int *_id)
219 {
220         int x;
221         if (!_id) {
222                 ast_log(LOG_WARNING, "Asked to remove NULL?\n");
223                 return -1;
224         }
225         for (x=0;x<ioc->fdcnt;x++) {
226                 if (ioc->ior[x].id == _id) {
227                         /* Free the int immediately and set to NULL so we know it's unused now */
228                         free(ioc->ior[x].id);
229                         ioc->ior[x].id = NULL;
230                         ioc->fds[x].events = 0;
231                         ioc->fds[x].revents = 0;
232                         ioc->needshrink = 1;
233                         if (!ioc->current_ioc)
234                                 io_shrink(ioc);
235                         return 0;
236                 }
237         }
238         
239         ast_log(LOG_NOTICE, "Unable to remove unknown id %p\n", _id);
240         return -1;
241 }
242
243 int ast_io_wait(struct io_context *ioc, int howlong)
244 {
245         /*
246          * Make the poll call, and call
247          * the callbacks for anything that needs
248          * to be handled
249          */
250         int res;
251         int x;
252         int origcnt;
253         DEBUG(ast_log(LOG_DEBUG, "ast_io_wait()\n"));
254         res = poll(ioc->fds, ioc->fdcnt, howlong);
255         if (res > 0) {
256                 /*
257                  * At least one event
258                  */
259                 origcnt = ioc->fdcnt;
260                 for(x=0;x<origcnt;x++) {
261                         /* Yes, it is possible for an entry to be deleted and still have an
262                            event waiting if it occurs after the original calling id */
263                         if (ioc->fds[x].revents && ioc->ior[x].id) {
264                                 /* There's an event waiting */
265                                 ioc->current_ioc = *ioc->ior[x].id;
266                                 if (ioc->ior[x].callback) {
267                                         if (!ioc->ior[x].callback(ioc->ior[x].id, ioc->fds[x].fd, ioc->fds[x].revents, ioc->ior[x].data)) {
268                                                 /* Time to delete them since they returned a 0 */
269                                                 ast_io_remove(ioc, ioc->ior[x].id);
270                                         }
271                                 }
272                                 ioc->current_ioc = -1;
273                         }
274                 }
275                 if (ioc->needshrink)
276                         io_shrink(ioc);
277         }
278         return res;
279 }
280
281 void ast_io_dump(struct io_context *ioc)
282 {
283         /*
284          * Print some debugging information via
285          * the logger interface
286          */
287         int x;
288         ast_log(LOG_DEBUG, "Asterisk IO Dump: %d entries, %d max entries\n", ioc->fdcnt, ioc->maxfdcnt);
289         ast_log(LOG_DEBUG, "================================================\n");
290         ast_log(LOG_DEBUG, "| ID    FD     Callback    Data        Events  |\n");
291         ast_log(LOG_DEBUG, "+------+------+-----------+-----------+--------+\n");
292         for (x=0;x<ioc->fdcnt;x++) {
293                 ast_log(LOG_DEBUG, "| %.4d | %.4d | %p | %p | %.6x |\n", 
294                                 *ioc->ior[x].id,
295                                 ioc->fds[x].fd,
296                                 ioc->ior[x].callback,
297                                 ioc->ior[x].data,
298                                 ioc->fds[x].events);
299         }
300         ast_log(LOG_DEBUG, "================================================\n");
301 }
302
303 /* Unrelated I/O functions */
304
305 int ast_hide_password(int fd)
306 {
307         struct termios tios;
308         int res;
309         int old;
310         if (!isatty(fd))
311                 return -1;
312         res = tcgetattr(fd, &tios);
313         if (res < 0)
314                 return -1;
315         old = tios.c_lflag & (ECHO | ECHONL);
316         tios.c_lflag &= ~ECHO;
317         tios.c_lflag |= ECHONL;
318         res = tcsetattr(fd, TCSAFLUSH, &tios);
319         if (res < 0)
320                 return -1;
321         return old;
322 }
323
324 int ast_restore_tty(int fd, int oldstate)
325 {
326         int res;
327         struct termios tios;
328         if (oldstate < 0)
329                 return 0;
330         res = tcgetattr(fd, &tios);
331         if (res < 0)
332                 return -1;
333         tios.c_lflag &= ~(ECHO | ECHONL);
334         tios.c_lflag |= oldstate;
335         res = tcsetattr(fd, TCSAFLUSH, &tios);
336         if (res < 0)
337                 return -1;
338         return 0;
339 }
340
341 int ast_get_termcols(int fd)
342 {
343         struct winsize win;
344         int cols = 0;
345
346         if (!isatty(fd))
347                 return -1;
348
349         if ( ioctl(fd, TIOCGWINSZ, &win) != -1 ) {
350                 if ( !cols && win.ws_col > 0 )
351                         cols = (int) win.ws_col;
352         } else {
353                 /* assume 80 characters if the ioctl fails for some reason */
354                 cols = 80;
355         }
356
357         return cols;
358 }
359