Sat Aug 6 00:39:29 2011

Asterisk developer's documentation


io.c

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

Generated on Sat Aug 6 00:39:29 2011 for Asterisk - the Open Source PBX by  doxygen 1.4.7