Main Page | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals

io.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk
00003  * 
00004  * Mark Spencer <markster@marko.net>
00005  *
00006  * Copyright(C) Mark Spencer
00007  * 
00008  * Distributed under the terms of the GNU General Public License (GPL) Version 2
00009  *
00010  * I/O Managment (Derived from Cheops-NG)
00011  *
00012  */
00013 
00014 #include <stdio.h>
00015 #include <unistd.h>
00016 #include <stdlib.h>
00017 #include <termios.h>
00018 #include <string.h> /* for memset */
00019 #include <sys/ioctl.h>
00020 #include <asterisk/io.h>
00021 #include <asterisk/logger.h>
00022 
00023 #ifdef DEBUG_IO
00024 #define DEBUG DEBUG_M
00025 #else
00026 #define DEBUG(a) 
00027 #endif
00028 
00029 /* 
00030  * Kept for each file descriptor
00031  */
00032 struct io_rec {
00033    ast_io_cb callback;     /* What is to be called */
00034    void *data;             /* Data to be passed */
00035    int *id;                /* ID number */
00036 };
00037 
00038 /* These two arrays are keyed with
00039    the same index.  it's too bad that
00040    pollfd doesn't have a callback field
00041    or something like that.  They grow as
00042    needed, by GROW_SHRINK_AMOUNT structures
00043    at once */
00044 
00045 #define GROW_SHRINK_SIZE 512
00046 
00047 /* Global variables are now in a struct in order to be
00048    made threadsafe */
00049 struct io_context {
00050    /* Poll structure */
00051    struct pollfd *fds;
00052    /* Associated I/O records */
00053    struct io_rec *ior;
00054    /* First available fd */
00055    unsigned int fdcnt;
00056    /* Maximum available fd */
00057    unsigned int maxfdcnt;
00058    /* Currently used io callback */
00059    int current_ioc;
00060    /* Whether something has been deleted */
00061    int needshrink;
00062 };
00063 
00064 
00065 struct io_context *io_context_create(void)
00066 {
00067    /* Create an I/O context */
00068    struct io_context *tmp;
00069    tmp = malloc(sizeof(struct io_context));
00070    if (tmp) {
00071       tmp->needshrink = 0;
00072       tmp->fdcnt = 0;
00073       tmp->maxfdcnt = GROW_SHRINK_SIZE/2;
00074       tmp->current_ioc = -1;
00075       tmp->fds = malloc((GROW_SHRINK_SIZE/2) * sizeof(struct pollfd));
00076       if (!tmp->fds) {
00077          free(tmp);
00078          tmp = NULL;
00079       } else {
00080          memset(tmp->fds, 0, (GROW_SHRINK_SIZE/2) * sizeof(struct pollfd));
00081          tmp->ior =  malloc((GROW_SHRINK_SIZE/2) * sizeof(struct io_rec));
00082          if (!tmp->ior) {
00083             free(tmp->fds);
00084             free(tmp);
00085             tmp = NULL;
00086          } else
00087             memset(tmp->ior, 0, (GROW_SHRINK_SIZE/2) * sizeof(struct io_rec));
00088       }
00089    }
00090    return tmp;
00091 }
00092 
00093 void io_context_destroy(struct io_context *ioc)
00094 {
00095    /* Free associated memory with an I/O context */
00096    if (ioc->fds)
00097       free(ioc->fds);
00098    if (ioc->ior)
00099       free(ioc->ior);
00100    free(ioc);
00101 }
00102 
00103 static int io_grow(struct io_context *ioc)
00104 {
00105    /* 
00106     * Grow the size of our arrays.  Return 0 on success or
00107     * -1 on failure
00108     */
00109    void *tmp;
00110    DEBUG(ast_log(LOG_DEBUG, "io_grow()\n"));
00111    ioc->maxfdcnt += GROW_SHRINK_SIZE;
00112    tmp = realloc(ioc->ior, (ioc->maxfdcnt + 1) * sizeof(struct io_rec));
00113    if (tmp) {
00114       ioc->ior = (struct io_rec *)tmp;
00115       tmp = realloc(ioc->fds, (ioc->maxfdcnt + 1) * sizeof(struct pollfd));
00116       if (tmp) {
00117          ioc->fds = tmp;
00118       } else {
00119          /*
00120           * Not enough memory for the pollfd.  Not really any need
00121           * to shrink back the iorec's as we'll probably want to
00122           * grow them again soon when more memory is available, and
00123           * then they'll already be the right size
00124           */
00125          ioc->maxfdcnt -= GROW_SHRINK_SIZE;
00126          return -1;
00127       }
00128       
00129    } else {
00130       /*
00131        * Out of memory.  We return to the old size, and return a failure
00132        */
00133       ioc->maxfdcnt -= GROW_SHRINK_SIZE;
00134       return -1;
00135    }
00136    return 0;
00137 }
00138 
00139 int *ast_io_add(struct io_context *ioc, int fd, ast_io_cb callback, short events, void *data)
00140 {
00141    /*
00142     * Add a new I/O entry for this file descriptor
00143     * with the given event mask, to call callback with
00144     * data as an argument.  Returns NULL on failure.
00145     */
00146    int *ret;
00147    DEBUG(ast_log(LOG_DEBUG, "ast_io_add()\n"));
00148    if (ioc->fdcnt >= ioc->maxfdcnt) {
00149       /* 
00150        * We don't have enough space for this entry.  We need to
00151        * reallocate maxfdcnt poll fd's and io_rec's, or back out now.
00152        */
00153       if (io_grow(ioc))
00154          return NULL;
00155    }
00156 
00157    /*
00158     * At this point, we've got sufficiently large arrays going
00159     * and we can make an entry for it in the pollfd and io_r
00160     * structures.
00161     */
00162    ioc->fds[ioc->fdcnt].fd = fd;
00163    ioc->fds[ioc->fdcnt].events = events;
00164    ioc->ior[ioc->fdcnt].callback = callback;
00165    ioc->ior[ioc->fdcnt].data = data;
00166    ioc->ior[ioc->fdcnt].id = (int *)malloc(sizeof(int));
00167    /* Bonk if we couldn't allocate an int */
00168    if (!ioc->ior[ioc->fdcnt].id)
00169       return NULL;
00170    *(ioc->ior[ioc->fdcnt].id) = ioc->fdcnt;
00171    ret = ioc->ior[ioc->fdcnt].id;
00172    ioc->fdcnt++;
00173    return ret;
00174 }
00175 
00176 int *ast_io_change(struct io_context *ioc, int *id, int fd, ast_io_cb callback, short events, void *data)
00177 {
00178    if (*id < ioc->fdcnt) {
00179       if (fd > -1)
00180          ioc->fds[*id].fd = fd;
00181       if (callback)
00182          ioc->ior[*id].callback = callback;
00183       if (events)
00184          ioc->fds[*id].events = events;
00185       if (data)
00186          ioc->ior[*id].data = data;
00187       return id;
00188    } else return NULL;
00189 }
00190 
00191 static int io_shrink(struct io_context *ioc)
00192 {
00193    int getfrom;
00194    int putto = 0;
00195    /* 
00196     * Bring the fields from the very last entry to cover over
00197     * the entry we are removing, then decrease the size of the 
00198     * arrays by one.
00199     */
00200    for (getfrom=0;getfrom<ioc->fdcnt;getfrom++) {
00201       if (ioc->ior[getfrom].id) {
00202          /* In use, save it */
00203          if (getfrom != putto) {
00204             ioc->fds[putto] = ioc->fds[getfrom];
00205             ioc->ior[putto] = ioc->ior[getfrom];
00206             *(ioc->ior[putto].id) = putto;
00207          }
00208          putto++;
00209       }
00210    }
00211    ioc->fdcnt = putto;
00212    ioc->needshrink = 0;
00213    /* FIXME: We should free some memory if we have lots of unused
00214       io structs */
00215    return 0;
00216 }
00217 
00218 int ast_io_remove(struct io_context *ioc, int *_id)
00219 {
00220    int x;
00221    if (!_id) {
00222       ast_log(LOG_WARNING, "Asked to remove NULL?\n");
00223       return -1;
00224    }
00225    for (x=0;x<ioc->fdcnt;x++) {
00226       if (ioc->ior[x].id == _id) {
00227          /* Free the int immediately and set to NULL so we know it's unused now */
00228          free(ioc->ior[x].id);
00229          ioc->ior[x].id = NULL;
00230          ioc->fds[x].events = 0;
00231          ioc->fds[x].revents = 0;
00232          ioc->needshrink = 1;
00233          if (!ioc->current_ioc)
00234             io_shrink(ioc);
00235          return 0;
00236       }
00237    }
00238    
00239    ast_log(LOG_NOTICE, "Unable to remove unknown id %p\n", _id);
00240    return -1;
00241 }
00242 
00243 int ast_io_wait(struct io_context *ioc, int howlong)
00244 {
00245    /*
00246     * Make the poll call, and call
00247     * the callbacks for anything that needs
00248     * to be handled
00249     */
00250    int res;
00251    int x;
00252    int origcnt;
00253    DEBUG(ast_log(LOG_DEBUG, "ast_io_wait()\n"));
00254    res = poll(ioc->fds, ioc->fdcnt, howlong);
00255    if (res > 0) {
00256       /*
00257        * At least one event
00258        */
00259       origcnt = ioc->fdcnt;
00260       for(x=0;x<origcnt;x++) {
00261          /* Yes, it is possible for an entry to be deleted and still have an
00262             event waiting if it occurs after the original calling id */
00263          if (ioc->fds[x].revents && ioc->ior[x].id) {
00264             /* There's an event waiting */
00265             ioc->current_ioc = *ioc->ior[x].id;
00266             if (ioc->ior[x].callback) {
00267                if (!ioc->ior[x].callback(ioc->ior[x].id, ioc->fds[x].fd, ioc->fds[x].revents, ioc->ior[x].data)) {
00268                   /* Time to delete them since they returned a 0 */
00269                   ast_io_remove(ioc, ioc->ior[x].id);
00270                }
00271             }
00272             ioc->current_ioc = -1;
00273          }
00274       }
00275       if (ioc->needshrink)
00276          io_shrink(ioc);
00277    }
00278    return res;
00279 }
00280 
00281 void ast_io_dump(struct io_context *ioc)
00282 {
00283    /*
00284     * Print some debugging information via
00285     * the logger interface
00286     */
00287    int x;
00288    ast_log(LOG_DEBUG, "Asterisk IO Dump: %d entries, %d max entries\n", ioc->fdcnt, ioc->maxfdcnt);
00289    ast_log(LOG_DEBUG, "================================================\n");
00290    ast_log(LOG_DEBUG, "| ID    FD     Callback    Data        Events  |\n");
00291    ast_log(LOG_DEBUG, "+------+------+-----------+-----------+--------+\n");
00292    for (x=0;x<ioc->fdcnt;x++) {
00293       ast_log(LOG_DEBUG, "| %.4d | %.4d | %p | %p | %.6x |\n", 
00294             *ioc->ior[x].id,
00295             ioc->fds[x].fd,
00296             ioc->ior[x].callback,
00297             ioc->ior[x].data,
00298             ioc->fds[x].events);
00299    }
00300    ast_log(LOG_DEBUG, "================================================\n");
00301 }
00302 
00303 /* Unrelated I/O functions */
00304 
00305 int ast_hide_password(int fd)
00306 {
00307    struct termios tios;
00308    int res;
00309    int old;
00310    if (!isatty(fd))
00311       return -1;
00312    res = tcgetattr(fd, &tios);
00313    if (res < 0)
00314       return -1;
00315    old = tios.c_lflag & (ECHO | ECHONL);
00316    tios.c_lflag &= ~ECHO;
00317    tios.c_lflag |= ECHONL;
00318    res = tcsetattr(fd, TCSAFLUSH, &tios);
00319    if (res < 0)
00320       return -1;
00321    return old;
00322 }
00323 
00324 int ast_restore_tty(int fd, int oldstate)
00325 {
00326    int res;
00327    struct termios tios;
00328    if (oldstate < 0)
00329       return 0;
00330    res = tcgetattr(fd, &tios);
00331    if (res < 0)
00332       return -1;
00333    tios.c_lflag &= ~(ECHO | ECHONL);
00334    tios.c_lflag |= oldstate;
00335    res = tcsetattr(fd, TCSAFLUSH, &tios);
00336    if (res < 0)
00337       return -1;
00338    return 0;
00339 }
00340 
00341 int ast_get_termcols(int fd)
00342 {
00343    struct winsize win;
00344    int cols = 0;
00345 
00346    if (!isatty(fd))
00347       return -1;
00348 
00349    if ( ioctl(fd, TIOCGWINSZ, &win) != -1 ) {
00350       if ( !cols && win.ws_col > 0 )
00351          cols = (int) win.ws_col;
00352    } else {
00353       /* assume 80 characters if the ioctl fails for some reason */
00354       cols = 80;
00355    }
00356 
00357    return cols;
00358 }
00359 

Generated on Sat Nov 25 19:09:38 2006 for Asterisk by  doxygen 1.4.2