framehook.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 2010, Digium, Inc.
00005  *
00006  * David Vossel <dvossel@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 FrameHooks Architecture
00022  *
00023  * \author David Vossel <dvossel@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: 434089 $")
00033 
00034 #include "asterisk/channel.h"
00035 #include "asterisk/linkedlists.h"
00036 #include "asterisk/framehook.h"
00037 #include "asterisk/frame.h"
00038 
00039 struct ast_framehook {
00040    struct ast_framehook_interface i;
00041    /*! This pointer to ast_channel the framehook is attached to. */
00042    struct ast_channel *chan;
00043    /*! the id representing this framehook on a channel */
00044    unsigned int id;
00045    /*! when set, this signals the read and write function to detach the hook */
00046    int detach_and_destroy_me;
00047    /*! list entry for ast_framehook_list object */
00048    AST_LIST_ENTRY(ast_framehook) list;
00049 };
00050 
00051 struct ast_framehook_list {
00052    /*! the number of hooks currently present */
00053    unsigned int count;
00054    /*! id for next framehook added */
00055    unsigned int id_count;
00056    AST_LIST_HEAD_NOLOCK(, ast_framehook) list;
00057 };
00058 
00059 enum framehook_detachment_mode
00060 {
00061    /*! Destroy the framehook outright. */
00062    FRAMEHOOK_DETACH_DESTROY = 0,
00063    /*! Remove the framehook from the channel, but don't destroy the data since
00064     *  it will be used by a replacement framehook on another channel. */
00065    FRAMEHOOK_DETACH_PRESERVE,
00066 };
00067 
00068 static void framehook_detach(struct ast_framehook *framehook, enum framehook_detachment_mode mode)
00069 {
00070    struct ast_frame *frame;
00071    frame = framehook->i.event_cb(framehook->chan, NULL, AST_FRAMEHOOK_EVENT_DETACHED, framehook->i.data);
00072    /* never assume anything about this function. If you can return a frame during
00073     * the detached event, then assume someone will. */
00074    if (frame) {
00075       ast_frfree(frame);
00076    }
00077    framehook->chan = NULL;
00078 
00079    if (mode == FRAMEHOOK_DETACH_DESTROY && framehook->i.destroy_cb) {
00080       framehook->i.destroy_cb(framehook->i.data);
00081    }
00082    ast_free(framehook);
00083 }
00084 
00085 static struct ast_frame *framehook_list_push_event(struct ast_framehook_list *framehooks, struct ast_frame *frame, enum ast_framehook_event event)
00086 {
00087    struct ast_framehook *framehook;
00088    struct ast_frame *original_frame;
00089    int *skip;
00090    size_t skip_size;
00091 
00092    if (!framehooks) {
00093       return frame;
00094    }
00095 
00096    skip_size = sizeof(int) * framehooks->count;
00097    skip = ast_alloca(skip_size);
00098    memset(skip, 0, skip_size);
00099 
00100    do {
00101       unsigned int num = 0;
00102       original_frame = frame;
00103 
00104       AST_LIST_TRAVERSE_SAFE_BEGIN(&framehooks->list, framehook, list) {
00105          if (framehook->detach_and_destroy_me) {
00106             /* this guy is signaled for destruction */
00107             AST_LIST_REMOVE_CURRENT(list);
00108             framehook_detach(framehook, FRAMEHOOK_DETACH_DESTROY);
00109             continue;
00110          }
00111 
00112          /* If this framehook has been marked as needing to be skipped, do so */
00113          if (skip[num]) {
00114             num++;
00115             continue;
00116          }
00117 
00118          frame = framehook->i.event_cb(framehook->chan, frame, event, framehook->i.data);
00119 
00120          if (frame != original_frame) {
00121             /* To prevent looping we skip any framehooks that have already provided a modified frame */
00122             skip[num] = 1;
00123             break;
00124          }
00125 
00126          num++;
00127       }
00128       AST_LIST_TRAVERSE_SAFE_END;
00129    } while (frame != original_frame);
00130 
00131    return frame;
00132 }
00133 
00134 int ast_framehook_attach(struct ast_channel *chan, struct ast_framehook_interface *i)
00135 {
00136    struct ast_framehook *framehook;
00137    struct ast_framehook_list *fh_list;
00138    struct ast_frame *frame;
00139    if (i->version != AST_FRAMEHOOK_INTERFACE_VERSION) {
00140       ast_log(LOG_ERROR, "Version '%hu' of framehook interface not what we compiled against (%i)\n",
00141          i->version, AST_FRAMEHOOK_INTERFACE_VERSION);
00142       return -1;
00143    }
00144    if (!i->event_cb || !(framehook = ast_calloc(1, sizeof(*framehook)))) {
00145       return -1;
00146    }
00147    framehook->i = *i;
00148    framehook->chan = chan;
00149 
00150    /* create the framehook list if it didn't already exist */
00151    if (!ast_channel_framehooks(chan)) {
00152       if (!(fh_list = ast_calloc(1, sizeof(*ast_channel_framehooks(chan))))) {
00153          ast_free(framehook);
00154          return -1;
00155       }
00156       ast_channel_framehooks_set(chan, fh_list);
00157    }
00158 
00159    ast_channel_framehooks(chan)->count++;
00160    framehook->id = ++ast_channel_framehooks(chan)->id_count;
00161    AST_LIST_INSERT_TAIL(&ast_channel_framehooks(chan)->list, framehook, list);
00162 
00163    /* Tell the event callback we're live and rocking */
00164    frame = framehook->i.event_cb(framehook->chan, NULL, AST_FRAMEHOOK_EVENT_ATTACHED, framehook->i.data);
00165 
00166    /* Never assume anything about this function. If you can return a frame during
00167     * the attached event, then assume someone will. */
00168    if (frame) {
00169       ast_frfree(frame);
00170    }
00171 
00172    if (ast_channel_is_bridged(chan)) {
00173       ast_channel_set_unbridged_nolock(chan, 1);
00174    }
00175 
00176    return framehook->id;
00177 }
00178 
00179 int ast_framehook_detach(struct ast_channel *chan, int id)
00180 {
00181    struct ast_framehook *framehook;
00182    int res = -1;
00183 
00184    if (!ast_channel_framehooks(chan)) {
00185       return res;
00186    }
00187 
00188    AST_LIST_TRAVERSE_SAFE_BEGIN(&ast_channel_framehooks(chan)->list, framehook, list) {
00189       if (framehook->id == id) {
00190          /* we mark for detachment rather than doing explicitly here because
00191           * it needs to be safe for this function to be called within the
00192           * event callback.  If we allowed the hook to actually be destroyed
00193           * immediately here, the event callback would crash on exit. */
00194          framehook->detach_and_destroy_me = 1;
00195          res = 0;
00196          break;
00197       }
00198    }
00199    AST_LIST_TRAVERSE_SAFE_END;
00200 
00201    if (!res && ast_channel_is_bridged(chan)) {
00202       ast_channel_set_unbridged_nolock(chan, 1);
00203    }
00204 
00205    return res;
00206 }
00207 
00208 int ast_framehook_list_destroy(struct ast_channel *chan)
00209 {
00210    struct ast_framehook *framehook;
00211 
00212    if (!ast_channel_framehooks(chan)) {
00213       return 0;
00214    }
00215    AST_LIST_TRAVERSE_SAFE_BEGIN(&ast_channel_framehooks(chan)->list, framehook, list) {
00216       AST_LIST_REMOVE_CURRENT(list);
00217       framehook_detach(framehook, FRAMEHOOK_DETACH_DESTROY);
00218    }
00219    AST_LIST_TRAVERSE_SAFE_END;
00220    ast_free(ast_channel_framehooks(chan));
00221    ast_channel_framehooks_set(chan, NULL);
00222    return 0;
00223 }
00224 
00225 void ast_framehook_list_fixup(struct ast_channel *old_chan, struct ast_channel *new_chan)
00226 {
00227    struct ast_framehook *framehook;
00228    int moved_framehook_id;
00229 
00230    if (ast_channel_framehooks(new_chan)) {
00231       AST_LIST_TRAVERSE_SAFE_BEGIN(&ast_channel_framehooks(new_chan)->list, framehook, list) {
00232          if (framehook->i.disable_inheritance) {
00233             ast_framehook_detach(new_chan, framehook->id);
00234             continue;
00235          }
00236 
00237          if (framehook->i.chan_breakdown_cb) {
00238             framehook->i.chan_breakdown_cb(framehook->i.data, framehook->id,
00239                old_chan, new_chan);
00240          }
00241       }
00242       AST_LIST_TRAVERSE_SAFE_END;
00243    }
00244 
00245    if (!ast_channel_framehooks(old_chan)) {
00246       return;
00247    }
00248 
00249    if (!AST_LIST_EMPTY(&ast_channel_framehooks(old_chan)->list)
00250       && ast_channel_is_bridged(old_chan)) {
00251       ast_channel_set_unbridged_nolock(old_chan, 1);
00252    }
00253    while ((framehook = AST_LIST_REMOVE_HEAD(&ast_channel_framehooks(old_chan)->list, list))) {
00254       /* If inheritance is not allowed for this framehook, just destroy it. */
00255       if (framehook->i.disable_inheritance) {
00256          framehook_detach(framehook, FRAMEHOOK_DETACH_DESTROY);
00257          continue;
00258       }
00259 
00260       /* Otherwise move it to the other channel and perform any fixups set by the framehook interface */
00261       moved_framehook_id = ast_framehook_attach(new_chan, &framehook->i);
00262       if (moved_framehook_id < 0) {
00263          ast_log(LOG_WARNING, "Failed framehook copy during masquerade. Expect loss of features.\n");
00264          framehook_detach(framehook, FRAMEHOOK_DETACH_DESTROY);
00265       } else {
00266          if (framehook->i.chan_fixup_cb) {
00267             framehook->i.chan_fixup_cb(framehook->i.data, moved_framehook_id,
00268                old_chan, new_chan);
00269          }
00270 
00271          framehook_detach(framehook, FRAMEHOOK_DETACH_PRESERVE);
00272       }
00273    }
00274 }
00275 
00276 int ast_framehook_list_is_empty(struct ast_framehook_list *framehooks)
00277 {
00278    if (!framehooks) {
00279       return 1;
00280    }
00281    return AST_LIST_EMPTY(&framehooks->list) ? 1 : 0;
00282 }
00283 
00284 int ast_framehook_list_contains_no_active(struct ast_framehook_list *framehooks)
00285 {
00286    return ast_framehook_list_contains_no_active_of_type(framehooks, 0);
00287 }
00288 
00289 int ast_framehook_list_contains_no_active_of_type(struct ast_framehook_list *framehooks,
00290    enum ast_frame_type type)
00291 {
00292    struct ast_framehook *cur;
00293 
00294    if (!framehooks) {
00295       return 1;
00296    }
00297 
00298    if (AST_LIST_EMPTY(&framehooks->list)) {
00299       return 1;
00300    }
00301 
00302    AST_LIST_TRAVERSE(&framehooks->list, cur, list) {
00303       if (cur->detach_and_destroy_me) {
00304          continue;
00305       }
00306       if (type && cur->i.consume_cb && !cur->i.consume_cb(cur->i.data, type)) {
00307          continue;
00308       }
00309       return 0;
00310    }
00311 
00312    return 1;
00313 }
00314 
00315 struct ast_frame *ast_framehook_list_write_event(struct ast_framehook_list *framehooks, struct ast_frame *frame)
00316 {
00317    return framehook_list_push_event(framehooks, frame, AST_FRAMEHOOK_EVENT_WRITE);
00318 }
00319 
00320 struct ast_frame *ast_framehook_list_read_event(struct ast_framehook_list *framehooks, struct ast_frame *frame)
00321 {
00322    return framehook_list_push_event(framehooks, frame, AST_FRAMEHOOK_EVENT_READ);
00323 }

Generated on Thu Apr 16 06:27:35 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6