Thu Oct 11 06:42:52 2012

Asterisk developer's documentation


autoservice.c File Reference

Automatic channel service routines. More...

#include "asterisk.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <signal.h>
#include <errno.h>
#include <unistd.h>
#include "asterisk/pbx.h"
#include "asterisk/frame.h"
#include "asterisk/sched.h"
#include "asterisk/options.h"
#include "asterisk/channel.h"
#include "asterisk/logger.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

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)

Variables

static int as_chan_list_state
static ast_cond_t as_cond
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 55 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 308 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_automonitor(), builtin_blindtransfer(), and feature_exec_app().

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

void ast_autoservice_init ( void   ) 

Provided by astobj2.c Provided by autoservice.c

Definition at line 325 of file autoservice.c.

References as_cond, and ast_cond_init().

Referenced by main().

00326 {
00327    ast_cond_init(&as_cond, NULL);
00328 }

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_exec(), acf_odbc_read(), acf_odbc_write(), ast_dtmf_stream(), ast_get_enum(), ast_get_srv(), ast_get_txt(), bridge_playfile(), builtin_atxfer(), builtin_automonitor(), builtin_blindtransfer(), conf_play(), feature_exec_app(), function_realtime_read(), function_realtime_write(), osplookup_exec(), pbx_find_extension(), sla_station_exec(), smdi_msg_retrieve_read(), system_exec_helper(), and try_calling().

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

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
Return values:
0 success
-1 error, or the channel has been hungup

Definition at line 239 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_exec(), acf_odbc_read(), acf_odbc_write(), ast_dtmf_stream(), ast_get_enum(), ast_get_srv(), ast_get_txt(), ast_hangup(), bridge_playfile(), builtin_atxfer(), builtin_automonitor(), conf_play(), feature_exec_app(), finishup(), function_realtime_read(), function_realtime_write(), osplookup_exec(), pbx_find_extension(), sla_station_exec(), smdi_msg_retrieve_read(), system_exec_helper(), and try_calling().

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

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

Definition at line 79 of file autoservice.c.

References ast_channel::_softhangup, 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 = AST_CONTROL_HANGUP,
00084    };
00085 
00086    for (;;) {
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 (!as->chan->_softhangup) {
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 }


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

Referenced by ast_autoservice_init(), and ast_autoservice_start().

pthread_t asthread = AST_PTHREADT_NULL [static]

Definition at line 75 of file autoservice.c.

Referenced by ast_autoservice_start(), ast_autoservice_stop(), and autoservice_run().


Generated on Thu Oct 11 06:42:52 2012 for Asterisk - the Open Source PBX by  doxygen 1.5.6