Wed Aug 7 17:15:52 2019

Asterisk developer's documentation


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"

Go to the source code of this file.

Data Structures

struct  asent
struct  aslist

Defines

#define MAX_AUTOMONS   1500

Functions

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

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 307 of file autoservice.c.

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

Referenced by builtin_automixmonitor(), feature_exec_app(), and play_message_on_chan().

00308 {
00309    struct asent *as;
00310    int res = -1;
00311 
00312    AST_LIST_LOCK(&aslist);
00313    AST_LIST_TRAVERSE(&aslist, as, list) {
00314       if (as->chan == chan) {
00315          res = 0;
00316          as->ignore_frame_types |= (1 << ftype);
00317          break;
00318       }
00319    }
00320    AST_LIST_UNLOCK(&aslist);
00321    return res;
00322 }

void ast_autoservice_init ( void   ) 

Provided by autoservice.c

Definition at line 335 of file autoservice.c.

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

Referenced by main().

00336 {
00337    ast_register_cleanup(autoservice_shutdown);
00338    ast_cond_init(&as_cond, NULL);
00339 }

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 179 of file autoservice.c.

References as_cond, ast_calloc, ast_channel_lock, ast_channel_unlock, ast_cond_signal, AST_FLAG_END_DTMF_ONLY, 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, free, asent::list, LOG_WARNING, 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(), ast_app_exec_macro(), ast_app_exec_sub(), ast_dtmf_stream(), ast_get_enum(), ast_get_srv(), ast_get_txt(), ast_masq_park_call_exten(), ast_park_call_exten(), bridge_playfile(), builtin_atxfer(), builtin_automixmonitor(), builtin_blindtransfer(), conf_play(), confbridge_exec(), dial_exec_full(), exec(), feature_exec_app(), feature_request_and_dial(), function_realtime_read(), function_realtime_readdestroy(), function_realtime_store(), function_realtime_write(), leave_conference_bridge(), 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(), play_message_on_chan(), realtimefield_read(), shell_helper(), sla_station_exec(), smdi_msg_retrieve_read(), srv_datastore_setup(), system_exec_helper(), try_calling(), and trylock_read().

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

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 238 of file autoservice.c.

References ast_channel::_softhangup, as_chan_list_state, ast_channel_lock, ast_channel_unlock, ast_clear_flag, AST_FLAG_END_DTMF_ONLY, 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, free, asent::ignore_frame_types, asent::list, 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(), array(), ast_app_exec_macro(), ast_app_exec_sub(), ast_dtmf_stream(), ast_get_enum(), ast_get_srv(), ast_get_txt(), ast_hangup(), ast_masq_park_call_exten(), ast_park_call_exten(), bridge_playfile(), builtin_atxfer(), builtin_automixmonitor(), conf_play(), confbridge_exec(), dial_exec_full(), exec(), feature_exec_app(), feature_request_and_dial(), finishup(), function_realtime_read(), function_realtime_readdestroy(), function_realtime_store(), function_realtime_write(), leave_conference_bridge(), 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(), play_message_on_chan(), realtimefield_read(), shell_helper(), sla_station_exec(), smdi_msg_retrieve_read(), srv_datastore_setup(), system_exec_helper(), try_calling(), and trylock_read().

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

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

Definition at line 79 of file autoservice.c.

References 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, asent::chan, f, ast_frame::frametype, LOG_WARNING, and MAX_AUTOMONS.

Referenced by ast_autoservice_start().

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

static void autoservice_shutdown ( void   )  [static]

Definition at line 324 of file autoservice.c.

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

Referenced by ast_autoservice_init().

00325 {
00326    pthread_t th = asthread;
00327    asexit = 1;
00328    if (th != AST_PTHREADT_NULL) {
00329       ast_cond_signal(&as_cond);
00330       pthread_kill(th, SIGURG);
00331       pthread_join(th, NULL);
00332    }
00333 }


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 7 Aug 2019 for Asterisk - The Open Source Telephony Project by  doxygen 1.6.1