Thu Apr 28 2011 16:56:55

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"
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 struct aslist aslist
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 50 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:
0success
-1channel is not in autoservice

Definition at line 302 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(), builtin_blindtransfer(), feature_exec_app(), and play_message_in_bridged_call().

{
   struct asent *as;
   int res = -1;

   AST_LIST_LOCK(&aslist);
   AST_LIST_TRAVERSE(&aslist, as, list) {
      if (as->chan == chan) {
         res = 0;
         as->ignore_frame_types |= (1 << ftype);
         break;
      }
   }
   AST_LIST_UNLOCK(&aslist);
   return res;
}
void ast_autoservice_init ( void  )

Provided by autoservice.c

Definition at line 319 of file autoservice.c.

References as_cond, and ast_cond_init().

Referenced by main().

{
   ast_cond_init(&as_cond, NULL);
}
int ast_autoservice_start ( struct ast_channel chan)

Automatically service a channel for us...

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

Definition at line 174 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_automixmonitor(), builtin_blindtransfer(), conf_play(), confbridge_exec(), dial_exec_full(), feature_exec_app(), 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_in_bridged_call(), post_join_marked(), realtimefield_read(), shell_helper(), sla_station_exec(), smdi_msg_retrieve_read(), system_exec_helper(), try_calling(), and trylock_read().

{
   int res = 0;
   struct asent *as;

   AST_LIST_LOCK(&aslist);
   AST_LIST_TRAVERSE(&aslist, as, list) {
      if (as->chan == chan) {
         as->use_count++;
         break;
      }
   }
   AST_LIST_UNLOCK(&aslist);

   if (as) {
      /* Entry exists, autoservice is already handling this channel */
      return 0;
   }

   if (!(as = ast_calloc(1, sizeof(*as))))
      return -1;
   
   /* New entry created */
   as->chan = chan;
   as->use_count = 1;

   ast_channel_lock(chan);
   as->orig_end_dtmf_flag = ast_test_flag(chan, AST_FLAG_END_DTMF_ONLY) ? 1 : 0;
   if (!as->orig_end_dtmf_flag)
      ast_set_flag(chan, AST_FLAG_END_DTMF_ONLY);
   ast_channel_unlock(chan);

   AST_LIST_LOCK(&aslist);

   if (AST_LIST_EMPTY(&aslist) && asthread != AST_PTHREADT_NULL) {
      ast_cond_signal(&as_cond);
   }

   AST_LIST_INSERT_HEAD(&aslist, as, list);

   if (asthread == AST_PTHREADT_NULL) { /* need start the thread */
      if (ast_pthread_create_background(&asthread, NULL, autoservice_run, NULL)) {
         ast_log(LOG_WARNING, "Unable to create autoservice thread :(\n");
         /* There will only be a single member in the list at this point,
            the one we just added. */
         AST_LIST_REMOVE(&aslist, as, list);
         free(as);
         asthread = AST_PTHREADT_NULL;
         res = -1;
      } else {
         pthread_kill(asthread, SIGURG);
      }
   }

   AST_LIST_UNLOCK(&aslist);

   return res;
}
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:
0success
-1error, or the channel has been hungup

Definition at line 233 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(), array(), ast_dtmf_stream(), ast_get_enum(), ast_get_srv(), ast_get_txt(), ast_hangup(), bridge_playfile(), builtin_atxfer(), builtin_automixmonitor(), conf_play(), confbridge_exec(), dial_exec_full(), feature_exec_app(), 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_in_bridged_call(), post_join_marked(), realtimefield_read(), shell_helper(), sla_station_exec(), smdi_msg_retrieve_read(), system_exec_helper(), try_calling(), and trylock_read().

{
   int res = -1;
   struct asent *as, *removed = NULL;
   struct ast_frame *f;
   int chan_list_state;

   AST_LIST_LOCK(&aslist);

   /* Save the autoservice channel list state.  We _must_ verify that the channel
    * list has been rebuilt before we return.  Because, after we return, the channel
    * could get destroyed and we don't want our poor autoservice thread to step on
    * it after its gone! */
   chan_list_state = as_chan_list_state;

   /* Find the entry, but do not free it because it still can be in the
      autoservice thread array */
   AST_LIST_TRAVERSE_SAFE_BEGIN(&aslist, as, list) {  
      if (as->chan == chan) {
         as->use_count--;
         if (as->use_count < 1) {
            AST_LIST_REMOVE_CURRENT(list);
            removed = as;
         }
         break;
      }
   }
   AST_LIST_TRAVERSE_SAFE_END;

   if (removed && asthread != AST_PTHREADT_NULL) {
      pthread_kill(asthread, SIGURG);
   }

   AST_LIST_UNLOCK(&aslist);

   if (!removed) {
      return 0;
   }

   /* Wait while autoservice thread rebuilds its list. */
   while (chan_list_state == as_chan_list_state) {
      usleep(1000);
   }

   /* Now autoservice thread should have no references to our entry
      and we can safely destroy it */

   if (!chan->_softhangup) {
      res = 0;
   }

   if (!as->orig_end_dtmf_flag) {
      ast_clear_flag(chan, AST_FLAG_END_DTMF_ONLY);
   }

   ast_channel_lock(chan);
   while ((f = AST_LIST_REMOVE_HEAD(&as->deferred_frames, frame_list))) {
      if (!((1 << f->frametype) & as->ignore_frame_types)) {
         ast_queue_frame_head(chan, f);
      }
      ast_frfree(f);
   }
   ast_channel_unlock(chan);

   free(as);

   return res;
}
static void* autoservice_run ( void *  ign) [static]

Definition at line 74 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_PTHREADT_NULL, ast_read(), ast_waitfor_n(), asthread, asent::chan, f, ast_frame::frametype, LOG_WARNING, and MAX_AUTOMONS.

Referenced by ast_autoservice_start().

{
   struct ast_frame hangup_frame = {
      .frametype = AST_FRAME_CONTROL,
      .subclass = AST_CONTROL_HANGUP,
   };

   for (;;) {
      struct ast_channel *mons[MAX_AUTOMONS];
      struct asent *ents[MAX_AUTOMONS];
      struct ast_channel *chan;
      struct asent *as;
      int i, x = 0, ms = 50;
      struct ast_frame *f = NULL;
      struct ast_frame *defer_frame = NULL;

      AST_LIST_LOCK(&aslist);

      /* At this point, we know that no channels that have been removed are going
       * to get used again. */
      as_chan_list_state++;

      if (AST_LIST_EMPTY(&aslist)) {
         ast_cond_wait(&as_cond, &aslist.lock);
      }

      AST_LIST_TRAVERSE(&aslist, as, list) {
         if (!ast_check_hangup(as->chan)) {
            if (x < MAX_AUTOMONS) {
               ents[x] = as;
               mons[x++] = as->chan;
            } else {
               ast_log(LOG_WARNING, "Exceeded maximum number of automatic monitoring events.  Fix autoservice.c\n");
            }
         }
      }

      AST_LIST_UNLOCK(&aslist);

      if (!x) {
         /* If we don't sleep, this becomes a busy loop, which causes
          * problems when Asterisk runs at a different priority than other
          * user processes.  As long as we check for new channels at least
          * once every 10ms, we should be fine. */
         usleep(10000);
         continue;
      }

      chan = ast_waitfor_n(mons, x, &ms);
      if (!chan) {
         continue;
      }

      f = ast_read(chan);

      if (!f) {
         /* No frame means the channel has been hung up.
          * A hangup frame needs to be queued here as ast_waitfor() may
          * never return again for the condition to be detected outside
          * of autoservice.  So, we'll leave a HANGUP queued up so the
          * thread in charge of this channel will know. */

         defer_frame = &hangup_frame;
      } else if (ast_is_deferrable_frame(f)) {
         defer_frame = f;
      }

      if (defer_frame) {
         for (i = 0; i < x; i++) {
            struct ast_frame *dup_f;
            
            if (mons[i] != chan) {
               continue;
            }
            
            if (defer_frame != f) {
               if ((dup_f = ast_frdup(defer_frame))) {
                  AST_LIST_INSERT_HEAD(&ents[i]->deferred_frames, dup_f, frame_list);
               }
            } else {
               if ((dup_f = ast_frisolate(defer_frame))) {
                  if (dup_f != defer_frame) {
                     ast_frfree(defer_frame);
                  }
                  AST_LIST_INSERT_HEAD(&ents[i]->deferred_frames, dup_f, frame_list);
               }
            }
            
            break;
         }
      } else if (f) {
         ast_frfree(f);
      }
   }

   asthread = AST_PTHREADT_NULL;

   return NULL;
}

Variable Documentation

int as_chan_list_state [static]

Definition at line 72 of file autoservice.c.

Referenced by ast_autoservice_stop().

ast_cond_t as_cond [static]

Definition at line 68 of file autoservice.c.

Referenced by ast_autoservice_init(), and ast_autoservice_start().

struct aslist aslist [static]
pthread_t asthread = AST_PTHREADT_NULL [static]

Definition at line 70 of file autoservice.c.

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