91aec690b4a7ea683e22963527698beeb4f13a26
[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 <sys/poll.h>
16 #include <unistd.h>
17 #include <stdlib.h>
18 #include <termios.h>
19 #include <asterisk/io.h>
20 #include <asterisk/logger.h>
21
22 #ifdef DEBUG_IO
23 #define DEBUG DEBUG_M
24 #else
25 #define DEBUG(a) 
26 #endif
27
28 /* 
29  * Kept for each file descriptor
30  */
31 struct io_rec {
32         ast_io_cb callback;             /* What is to be called */
33         void *data;                             /* Data to be passed */
34         int *id;                                        /* ID number */
35 };
36
37 /* These two arrays are keyed with
38    the same index.  it's too bad that
39    pollfd doesn't have a callback field
40    or something like that.  They grow as
41    needed, by GROW_SHRINK_AMOUNT structures
42    at once */
43
44 #define GROW_SHRINK_SIZE 512
45
46 /* Global variables are now in a struct in order to be
47    made threadsafe */
48 struct io_context {
49         /* Poll structure */
50         struct pollfd *fds;
51         /* Associated I/O records */
52         struct io_rec *ior;
53         /* First available fd */
54         unsigned int fdcnt;
55         /* Maximum available fd */
56         unsigned int maxfdcnt;
57         /* Currently used io callback */
58         int current_ioc;
59 };
60
61
62 struct io_context *io_context_create(void)
63 {
64         /* Create an I/O context */
65         struct io_context *tmp;
66         tmp = malloc(sizeof(struct io_context));
67         if (tmp) {
68                 tmp->fds = NULL;
69                 tmp->ior =  NULL;
70                 tmp->fdcnt = 0;
71                 tmp->maxfdcnt = -1;
72                 tmp->current_ioc = -1;
73         }
74         return tmp;
75 }
76
77 void io_context_destroy(struct io_context *ioc)
78 {
79         /* Free associated memory with an I/O context */
80         if (ioc->fds)
81                 free(ioc->fds);
82         if (ioc->ior)
83                 free(ioc->ior);
84         free(ioc);
85 }
86
87 static int io_grow(struct io_context *ioc)
88 {
89         /* 
90          * Grow the size of our arrays.  Return 0 on success or
91          * -1 on failure
92          */
93         void *tmp;
94         DEBUG(ast_log(LOG_DEBUG, "io_grow()\n"));
95         ioc->maxfdcnt += GROW_SHRINK_SIZE;
96         tmp = realloc(ioc->ior, (ioc->maxfdcnt + 1) * sizeof(struct io_rec));
97         if (tmp) {
98                 ioc->ior = (struct io_rec *)tmp;
99                 tmp = realloc(ioc->fds, (ioc->maxfdcnt + 1) * sizeof(struct pollfd));
100                 if (tmp) {
101                         ioc->fds = tmp;
102                 } else {
103                         /*
104                          * Not enough memory for the pollfd.  Not really any need
105                          * to shrink back the iorec's as we'll probably want to
106                          * grow them again soon when more memory is available, and
107                          * then they'll already be the right size
108                          */
109                         ioc->maxfdcnt -= GROW_SHRINK_SIZE;
110                         return -1;
111                 }
112                 
113         } else {
114                 /*
115                  * Out of memory.  We return to the old size, and return a failure
116                  */
117                 ioc->maxfdcnt -= GROW_SHRINK_SIZE;
118                 return -1;
119         }
120         return 0;
121 }
122
123 int *ast_io_add(struct io_context *ioc, int fd, ast_io_cb callback, short events, void *data)
124 {
125         /*
126          * Add a new I/O entry for this file descriptor
127          * with the given event mask, to call callback with
128          * data as an argument.  Returns NULL on failure.
129          */
130         DEBUG(ast_log(LOG_DEBUG, "ast_io_add()\n"));
131         if (ioc->fdcnt < ioc->maxfdcnt) {
132                 /* 
133                  * We don't have enough space for this entry.  We need to
134                  * reallocate maxfdcnt poll fd's and io_rec's, or back out now.
135                  */
136                 if (io_grow(ioc))
137                         return NULL;
138         }
139
140         /*
141          * At this point, we've got sufficiently large arrays going
142          * and we can make an entry for it in the pollfd and io_r
143          * structures.
144          */
145         ioc->fds[ioc->fdcnt].fd = fd;
146         ioc->fds[ioc->fdcnt].events = events;
147         ioc->ior[ioc->fdcnt].callback = callback;
148         ioc->ior[ioc->fdcnt].data = data;
149         ioc->ior[ioc->fdcnt].id = (int *)malloc(sizeof(int));
150         /* Bonk if we couldn't allocate an int */
151         if (!ioc->ior[ioc->fdcnt].id)
152                 return NULL;
153         *ioc->ior[ioc->fdcnt].id = ioc->fdcnt;
154         return ioc->ior[ioc->fdcnt++].id;
155 }
156
157 int *ast_io_change(struct io_context *ioc, int *id, int fd, ast_io_cb callback, short events, void *data)
158 {
159         if (*id < ioc->fdcnt) {
160                 if (fd > -1)
161                         ioc->fds[*id].fd = fd;
162                 if (callback)
163                         ioc->ior[*id].callback = callback;
164                 if (events)
165                         ioc->fds[*id].events = events;
166                 if (data)
167                         ioc->ior[*id].data = data;
168                 return id;
169         } else return NULL;
170 }
171
172 static int io_shrink(struct io_context *ioc, int which)
173 {
174         /* 
175          * Bring the fields from the very last entry to cover over
176          * the entry we are removing, then decrease the size of the 
177          * arrays by one.
178          */
179         ioc->fdcnt--;
180
181         /* Free the int */
182         free(ioc->ior[which].id);
183         
184         /* If we're not deleting the last one, move the last one to
185            the current position */
186         if (which != ioc->fdcnt) {
187                 ioc->fds[which] = ioc->fds[ioc->fdcnt];
188                 ioc->ior[which] = ioc->ior[ioc->fdcnt];
189                 *ioc->ior[which].id = which;
190         }
191         /* FIXME: We should free some memory if we have lots of unused
192            io structs */
193         return 0;
194 }
195
196 int ast_io_remove(struct io_context *ioc, int *id)
197 {
198         if (ioc->current_ioc == *id) {
199                 ast_log(LOG_NOTICE, "Callback for %d tried to remove itself\n", *id);
200         } else
201         
202         if (*id < ioc->fdcnt) {
203                 return io_shrink(ioc, *id);
204         } else 
205                 ast_log(LOG_NOTICE, "Unable to remove unknown id %d\n", *id);
206
207         return -1;
208 }
209
210 int ast_io_wait(struct io_context *ioc, int howlong)
211 {
212         /*
213          * Make the poll call, and call
214          * the callbacks for anything that needs
215          * to be handled
216          */
217         int res;
218         int x;
219         DEBUG(ast_log(LOG_DEBUG, "ast_io_wait()\n"));
220         res = poll(ioc->fds, ioc->fdcnt, howlong);
221         if (res > 0) {
222                 /*
223                  * At least one event
224                  */
225                 for(x=0;x<ioc->fdcnt;x++) {
226                         if (ioc->fds[x].revents) {
227                                 /* There's an event waiting */
228                                 ioc->current_ioc = *ioc->ior[x].id;
229                                 if (!ioc->ior[x].callback(ioc->ior[x].id, ioc->fds[x].fd, ioc->fds[x].revents, ioc->ior[x].data)) {
230                                         /* Time to delete them since they returned a 0 */
231                                         io_shrink(ioc, x);
232                                 }
233                                 ioc->current_ioc = -1;
234                         }
235                 }
236         }
237         return res;
238 }
239
240 void ast_io_dump(struct io_context *ioc)
241 {
242         /*
243          * Print some debugging information via
244          * the logger interface
245          */
246         int x;
247         ast_log(LOG_DEBUG, "Asterisk IO Dump: %d entries, %d max entries\n", ioc->fdcnt, ioc->maxfdcnt);
248         ast_log(LOG_DEBUG, "================================================\n");
249         ast_log(LOG_DEBUG, "| ID    FD     Callback    Data        Events  |\n");
250         ast_log(LOG_DEBUG, "+------+------+-----------+-----------+--------+\n");
251         for (x=0;x<ioc->fdcnt;x++) {
252                 ast_log(LOG_DEBUG, "| %.4d | %.4d | %p | %p | %.6x |\n", 
253                                 *ioc->ior[x].id,
254                                 ioc->fds[x].fd,
255                                 ioc->ior[x].callback,
256                                 ioc->ior[x].data,
257                                 ioc->fds[x].events);
258         }
259         ast_log(LOG_DEBUG, "================================================\n");
260 }
261
262 /* Unrelated I/O functions */
263
264 int ast_hide_password(int fd)
265 {
266         struct termios tios;
267         int res;
268         int old;
269         if (!isatty(fd))
270                 return -1;
271         res = tcgetattr(fd, &tios);
272         if (res < 0)
273                 return -1;
274         old = tios.c_lflag & (ECHO | ECHONL);
275         tios.c_lflag &= ~ECHO;
276         tios.c_lflag |= ECHONL;
277         res = tcsetattr(fd, TCSAFLUSH, &tios);
278         if (res < 0)
279                 return -1;
280         return old;
281 }
282
283 int ast_restore_tty(int fd, int oldstate)
284 {
285         int res;
286         struct termios tios;
287         if (oldstate < 0)
288                 return 0;
289         res = tcgetattr(fd, &tios);
290         if (res < 0)
291                 return -1;
292         tios.c_lflag &= ~(ECHO | ECHONL);
293         tios.c_lflag |= oldstate;
294         res = tcsetattr(fd, TCSAFLUSH, &tios);
295         if (res < 0)
296                 return -1;
297         return 0;
298 }
299