autoservice.c File Reference

Automatic channel service routines. More...

#include "asterisk.h"
#include <sys/time.h>
#include <signal.h>
#include "asterisk/_private.h"
#include "asterisk/pbx.h"
#include "asterisk/frame.h"
#include "asterisk/sched.h"
#include "asterisk/channel.h"
#include "asterisk/file.h"
#include "asterisk/translate.h"
#include "asterisk/manager.h"
#include "asterisk/chanvars.h"
#include "asterisk/linkedlists.h"
#include "asterisk/indications.h"
#include "asterisk/lock.h"
#include "asterisk/utils.h"

Include dependency graph for autoservice.c:

Go to the source code of this file.

Data Structures

struct  asent
struct  aslist

Defines

#define MAX_AUTOMONS   1500

Functions

void ast_autoservice_chan_hangup_peer (struct ast_channel *chan, struct ast_channel *peer)
 Put chan into autoservice while hanging up peer.
int ast_autoservice_ignore (struct ast_channel *chan, enum ast_frame_type ftype)
 Ignore certain frame types.
void ast_autoservice_init (void)
int ast_autoservice_start (struct ast_channel *chan)
 Automatically service a channel for us...
int ast_autoservice_stop (struct ast_channel *chan)
 Stop servicing a channel for us...
static void * autoservice_run (void *ign)
static void autoservice_shutdown (void)

Variables

static int as_chan_list_state
static ast_cond_t as_cond
static volatile int asexit = 0
static pthread_t asthread = AST_PTHREADT_NULL


Detailed Description

Automatic channel service routines.

Author:
Mark Spencer <markster@digium.com>

Russell Bryant <russell@digium.com>

Definition in file autoservice.c.


Define Documentation

#define MAX_AUTOMONS   1500

Definition at line 54 of file autoservice.c.

Referenced by autoservice_run().


Function Documentation

void ast_autoservice_chan_hangup_peer ( struct ast_channel chan,
struct ast_channel peer 
)

Put chan into autoservice while hanging up peer.

Since:
11.0
Parameters:
chan Chan to put into autoservice.
peer Chan to run hangup handlers and hangup.
Returns:
Nothing

Definition at line 318 of file autoservice.c.

References ast_autoservice_start(), ast_autoservice_stop(), and ast_hangup().

Referenced by app_exec(), bridge_failed_peer_goto(), dial_exec_full(), and try_calling().

00319 {
00320    if (chan && !ast_autoservice_start(chan)) {
00321       ast_hangup(peer);
00322       ast_autoservice_stop(chan);
00323    } else {
00324       ast_hangup(peer);
00325    }
00326 }

int ast_autoservice_ignore ( struct ast_channel chan,
enum ast_frame_type  ftype 
)

Ignore certain frame types.

Note:
Normally, we cache DTMF, IMAGE, HTML, TEXT, and CONTROL frames while a channel is in autoservice and queue them up when taken out of autoservice. When this is not desireable, this API may be used to cause the channel to ignore those frametypes after the channel is put into autoservice, but before autoservice is stopped.
Return values:
0 success
-1 channel is not in autoservice

Definition at line 328 of file autoservice.c.

References AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, asent::chan, asent::ignore_frame_types, and asent::list.

00329 {
00330    struct asent *as;
00331    int res = -1;
00332 
00333    AST_LIST_LOCK(&aslist);
00334    AST_LIST_TRAVERSE(&aslist, as, list) {
00335       if (as->chan == chan) {
00336          res = 0;
00337          as->ignore_frame_types |= (1 << ftype);
00338          break;
00339       }
00340    }
00341    AST_LIST_UNLOCK(&aslist);
00342    return res;
00343 }

void ast_autoservice_init ( void   ) 

Provided by autoservice.c

Definition at line 356 of file autoservice.c.

References as_cond, ast_cond_init, ast_register_cleanup(), autoservice_shutdown(), and NULL.

Referenced by main().

int ast_autoservice_start ( struct ast_channel chan  ) 

Automatically service a channel for us...

Return values:
0 success
-1 failure, or the channel is already being autoserviced

Definition at line 190 of file autoservice.c.

References as_cond, ast_calloc, ast_channel_flags(), ast_channel_lock, ast_channel_unlock, ast_cond_signal, AST_FLAG_END_DTMF_ONLY, ast_free, AST_LIST_EMPTY, AST_LIST_INSERT_HEAD, AST_LIST_LOCK, AST_LIST_REMOVE, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log, ast_pthread_create_background, AST_PTHREADT_NULL, ast_set_flag, ast_test_flag, asthread, autoservice_run(), asent::chan, asent::list, LOG_WARNING, NULL, asent::orig_end_dtmf_flag, and asent::use_count.

Referenced by _macro_exec(), acf_curl_helper(), acf_jabberreceive_read(), acf_odbc_read(), acf_odbc_write(), action_toggle_mute_participants(), ast_app_exec_macro(), ast_app_exec_sub(), ast_autoservice_chan_hangup_peer(), ast_dtmf_stream(), ast_get_enum(), ast_get_srv(), ast_get_txt(), begin_dial_prerun(), conf_play(), confbridge_exec(), dial_exec_full(), exec(), findmeexec(), function_realtime_read(), function_realtime_readdestroy(), function_realtime_store(), function_realtime_write(), join_conference_bridge(), leave_marked(), lock_read(), lua_autoservice_start(), lua_get_variable_value(), lua_pbx_exec(), lua_set_variable(), lua_set_variable_value(), originate_exec(), osplookup_exec(), pbx_find_extension(), post_join_play_begin(), realtimefield_read(), shell_helper(), sla_station_exec(), smdi_msg_retrieve_read(), srv_datastore_setup(), system_exec_helper(), try_calling(), and trylock_read().

00191 {
00192    int res = 0;
00193    struct asent *as;
00194 
00195    AST_LIST_LOCK(&aslist);
00196    AST_LIST_TRAVERSE(&aslist, as, list) {
00197       if (as->chan == chan) {
00198          as->use_count++;
00199          break;
00200       }
00201    }
00202    AST_LIST_UNLOCK(&aslist);
00203 
00204    if (as) {
00205       /* Entry exists, autoservice is already handling this channel */
00206       return 0;
00207    }
00208 
00209    if (!(as = ast_calloc(1, sizeof(*as))))
00210       return -1;
00211 
00212    /* New entry created */
00213    as->chan = chan;
00214    as->use_count = 1;
00215 
00216    ast_channel_lock(chan);
00217    as->orig_end_dtmf_flag = ast_test_flag(ast_channel_flags(chan), AST_FLAG_END_DTMF_ONLY) ? 1 : 0;
00218    if (!as->orig_end_dtmf_flag)
00219       ast_set_flag(ast_channel_flags(chan), AST_FLAG_END_DTMF_ONLY);
00220    ast_channel_unlock(chan);
00221 
00222    AST_LIST_LOCK(&aslist);
00223 
00224    if (AST_LIST_EMPTY(&aslist) && asthread != AST_PTHREADT_NULL) {
00225       ast_cond_signal(&as_cond);
00226    }
00227 
00228    AST_LIST_INSERT_HEAD(&aslist, as, list);
00229 
00230    if (asthread == AST_PTHREADT_NULL) { /* need start the thread */
00231       if (ast_pthread_create_background(&asthread, NULL, autoservice_run, NULL)) {
00232          ast_log(LOG_WARNING, "Unable to create autoservice thread :(\n");
00233          /* There will only be a single member in the list at this point,
00234             the one we just added. */
00235          AST_LIST_REMOVE(&aslist, as, list);
00236          ast_free(as);
00237          asthread = AST_PTHREADT_NULL;
00238          res = -1;
00239       } else {
00240          pthread_kill(asthread, SIGURG);
00241       }
00242    }
00243 
00244    AST_LIST_UNLOCK(&aslist);
00245 
00246    return res;
00247 }

int ast_autoservice_stop ( struct ast_channel chan  ) 

Stop servicing a channel for us...

Note:
if chan is locked prior to calling ast_autoservice_stop, it is likely that there will be a deadlock between the thread that calls ast_autoservice_stop and the autoservice thread. It is important that chan is not locked prior to this call
Parameters:
chan 
Return values:
0 success
-1 error, or the channel has been hungup

Definition at line 249 of file autoservice.c.

References as_chan_list_state, ast_channel_flags(), ast_channel_lock, ast_channel_softhangup_internal_flag(), ast_channel_unlock, ast_clear_flag, AST_FLAG_END_DTMF_ONLY, ast_free, ast_frfree, AST_LIST_LOCK, AST_LIST_REMOVE_CURRENT, AST_LIST_REMOVE_HEAD, AST_LIST_TRAVERSE_SAFE_BEGIN, AST_LIST_TRAVERSE_SAFE_END, AST_LIST_UNLOCK, AST_PTHREADT_NULL, ast_queue_frame_head(), asthread, asent::chan, asent::deferred_frames, f, ast_frame::frametype, asent::ignore_frame_types, asent::list, NULL, asent::orig_end_dtmf_flag, and asent::use_count.

Referenced by _macro_exec(), acf_curl_helper(), acf_jabberreceive_read(), acf_odbc_read(), acf_odbc_write(), action_toggle_mute_participants(), array(), ast_app_exec_macro(), ast_app_exec_sub(), ast_autoservice_chan_hangup_peer(), ast_dtmf_stream(), ast_get_enum(), ast_get_srv(), ast_get_txt(), ast_hangup(), begin_dial_prerun(), conf_play(), confbridge_exec(), dial_exec_full(), exec(), findmeexec(), function_realtime_read(), function_realtime_readdestroy(), function_realtime_store(), function_realtime_write(), join_conference_bridge(), leave_marked(), lock_read(), lua_autoservice_stop(), lua_get_variable_value(), lua_pbx_exec(), lua_set_variable(), lua_set_variable_value(), originate_exec(), osplookup_exec(), pbx_find_extension(), post_join_play_begin(), realtimefield_read(), shell_helper(), sla_station_exec(), smdi_msg_retrieve_read(), srv_datastore_setup(), system_exec_helper(), try_calling(), and trylock_read().

00250 {
00251    int res = -1;
00252    struct asent *as, *removed = NULL;
00253    struct ast_frame *f;
00254    int chan_list_state;
00255 
00256    AST_LIST_LOCK(&aslist);
00257 
00258    /* Save the autoservice channel list state.  We _must_ verify that the channel
00259     * list has been rebuilt before we return.  Because, after we return, the channel
00260     * could get destroyed and we don't want our poor autoservice thread to step on
00261     * it after its gone! */
00262    chan_list_state = as_chan_list_state;
00263 
00264    /* Find the entry, but do not free it because it still can be in the
00265       autoservice thread array */
00266    AST_LIST_TRAVERSE_SAFE_BEGIN(&aslist, as, list) {
00267       if (as->chan == chan) {
00268          as->use_count--;
00269          if (as->use_count < 1) {
00270             AST_LIST_REMOVE_CURRENT(list);
00271             removed = as;
00272          }
00273          break;
00274       }
00275    }
00276    AST_LIST_TRAVERSE_SAFE_END;
00277 
00278    if (removed && asthread != AST_PTHREADT_NULL) {
00279       pthread_kill(asthread, SIGURG);
00280    }
00281 
00282    AST_LIST_UNLOCK(&aslist);
00283 
00284    if (!removed) {
00285       return 0;
00286    }
00287 
00288    /* Wait while autoservice thread rebuilds its list. */
00289    while (chan_list_state == as_chan_list_state) {
00290       usleep(1000);
00291    }
00292 
00293    /* Now autoservice thread should have no references to our entry
00294       and we can safely destroy it */
00295 
00296    if (!ast_channel_softhangup_internal_flag(chan)) {
00297       res = 0;
00298    }
00299 
00300    if (!as->orig_end_dtmf_flag) {
00301       ast_clear_flag(ast_channel_flags(chan), AST_FLAG_END_DTMF_ONLY);
00302    }
00303 
00304    ast_channel_lock(chan);
00305    while ((f = AST_LIST_REMOVE_HEAD(&as->deferred_frames, frame_list))) {
00306       if (!((1 << f->frametype) & as->ignore_frame_types)) {
00307          ast_queue_frame_head(chan, f);
00308       }
00309       ast_frfree(f);
00310    }
00311    ast_channel_unlock(chan);
00312 
00313    ast_free(as);
00314 
00315    return res;
00316 }

static void* autoservice_run ( void *  ign  )  [static]

Definition at line 79 of file autoservice.c.

References ast_callid_threadassoc_change(), ast_channel_callid(), ast_check_hangup(), ast_cond_wait, AST_CONTROL_HANGUP, AST_FRAME_CONTROL, ast_frdup(), ast_frfree, ast_frisolate(), ast_is_deferrable_frame(), AST_LIST_EMPTY, AST_LIST_INSERT_HEAD, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log, ast_read(), ast_waitfor_n(), asthread, ast_channel::callid, asent::chan, f, ast_frame::frametype, LOG_WARNING, MAX_AUTOMONS, and NULL.

Referenced by ast_autoservice_start().

00080 {
00081    ast_callid callid = 0;
00082    struct ast_frame hangup_frame = {
00083       .frametype = AST_FRAME_CONTROL,
00084       .subclass.integer = AST_CONTROL_HANGUP,
00085    };
00086 
00087    while (!asexit) {
00088       struct ast_channel *mons[MAX_AUTOMONS];
00089       struct asent *ents[MAX_AUTOMONS];
00090       struct ast_channel *chan;
00091       struct asent *as;
00092       int i, x = 0, ms = 50;
00093       struct ast_frame *f = NULL;
00094       struct ast_frame *defer_frame = NULL;
00095 
00096       AST_LIST_LOCK(&aslist);
00097 
00098       /* At this point, we know that no channels that have been removed are going
00099        * to get used again. */
00100       as_chan_list_state++;
00101 
00102       if (AST_LIST_EMPTY(&aslist)) {
00103          ast_cond_wait(&as_cond, &aslist.lock);
00104       }
00105 
00106       AST_LIST_TRAVERSE(&aslist, as, list) {
00107          if (!ast_check_hangup(as->chan)) {
00108             if (x < MAX_AUTOMONS) {
00109                ents[x] = as;
00110                mons[x++] = as->chan;
00111             } else {
00112                ast_log(LOG_WARNING, "Exceeded maximum number of automatic monitoring events.  Fix autoservice.c\n");
00113             }
00114          }
00115       }
00116 
00117       AST_LIST_UNLOCK(&aslist);
00118 
00119       if (!x) {
00120          /* If we don't sleep, this becomes a busy loop, which causes
00121           * problems when Asterisk runs at a different priority than other
00122           * user processes.  As long as we check for new channels at least
00123           * once every 10ms, we should be fine. */
00124          usleep(10000);
00125          continue;
00126       }
00127 
00128       chan = ast_waitfor_n(mons, x, &ms);
00129       if (!chan) {
00130          continue;
00131       }
00132 
00133       callid = ast_channel_callid(chan);
00134       ast_callid_threadassoc_change(callid);
00135 
00136       f = ast_read(chan);
00137 
00138       if (!f) {
00139          /* No frame means the channel has been hung up.
00140           * A hangup frame needs to be queued here as ast_waitfor() may
00141           * never return again for the condition to be detected outside
00142           * of autoservice.  So, we'll leave a HANGUP queued up so the
00143           * thread in charge of this channel will know. */
00144 
00145          defer_frame = &hangup_frame;
00146       } else if (ast_is_deferrable_frame(f)) {
00147          defer_frame = f;
00148       } else {
00149          /* Can't defer. Discard and continue with next. */
00150          ast_frfree(f);
00151          continue;
00152       }
00153 
00154       for (i = 0; i < x; i++) {
00155          struct ast_frame *dup_f;
00156 
00157          if (mons[i] != chan) {
00158             continue;
00159          }
00160 
00161          if (!f) { /* defer_frame == &hangup_frame */
00162             if ((dup_f = ast_frdup(defer_frame))) {
00163                AST_LIST_INSERT_HEAD(&ents[i]->deferred_frames, dup_f, frame_list);
00164             }
00165          } else {
00166             if ((dup_f = ast_frisolate(defer_frame))) {
00167                AST_LIST_INSERT_HEAD(&ents[i]->deferred_frames, dup_f, frame_list);
00168             }
00169             if (dup_f != defer_frame) {
00170                ast_frfree(defer_frame);
00171             }
00172          }
00173 
00174          break;
00175       }
00176       /* The ast_waitfor_n() call will only read frames from
00177        * the channels' file descriptors. If ast_waitfor_n()
00178        * returns non-NULL, then one of the channels in the
00179        * mons array must have triggered the return. It's
00180        * therefore impossible that we got here while (i >= x).
00181        * If we did, we'd need to ast_frfree(f) if (f). */
00182    }
00183 
00184    ast_callid_threadassoc_change(0);
00185    asthread = AST_PTHREADT_NULL;
00186 
00187    return NULL;
00188 }

static void autoservice_shutdown ( void   )  [static]

Definition at line 345 of file autoservice.c.

References as_cond, asexit, ast_cond_signal, AST_PTHREADT_NULL, asthread, and NULL.

Referenced by ast_autoservice_init().

00346 {
00347    pthread_t th = asthread;
00348    asexit = 1;
00349    if (th != AST_PTHREADT_NULL) {
00350       ast_cond_signal(&as_cond);
00351       pthread_kill(th, SIGURG);
00352       pthread_join(th, NULL);
00353    }
00354 }


Variable Documentation

int as_chan_list_state [static]

Definition at line 77 of file autoservice.c.

Referenced by ast_autoservice_stop().

ast_cond_t as_cond [static]

Definition at line 72 of file autoservice.c.

Referenced by ast_autoservice_init(), ast_autoservice_start(), and autoservice_shutdown().

volatile int asexit = 0 [static]

Definition at line 75 of file autoservice.c.

Referenced by autoservice_shutdown().

pthread_t asthread = AST_PTHREADT_NULL [static]


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