Wed Aug 7 17:15:42 2019

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

Generated on 7 Aug 2019 for Asterisk - The Open Source Telephony Project by  doxygen 1.6.1