Sat Apr 12 07:12:18 2008

Asterisk developer's documentation


autoservice.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2006, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@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 Automatic channel service routines
00022  *
00023  * \author Mark Spencer <markster@digium.com> 
00024  */
00025 
00026 #include "asterisk.h"
00027 
00028 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 94801 $")
00029 
00030 #include <stdio.h>
00031 #include <stdlib.h>
00032 #include <string.h>
00033 #include <sys/time.h>
00034 #include <signal.h>
00035 #include <errno.h>
00036 #include <unistd.h>
00037 
00038 #include "asterisk/pbx.h"
00039 #include "asterisk/frame.h"
00040 #include "asterisk/sched.h"
00041 #include "asterisk/options.h"
00042 #include "asterisk/channel.h"
00043 #include "asterisk/logger.h"
00044 #include "asterisk/file.h"
00045 #include "asterisk/translate.h"
00046 #include "asterisk/manager.h"
00047 #include "asterisk/chanvars.h"
00048 #include "asterisk/linkedlists.h"
00049 #include "asterisk/indications.h"
00050 #include "asterisk/lock.h"
00051 #include "asterisk/utils.h"
00052 
00053 #define MAX_AUTOMONS 256
00054 
00055 struct asent {
00056    struct ast_channel *chan;
00057    /*! This gets incremented each time autoservice gets started on the same
00058     *  channel.  It will ensure that it doesn't actually get stopped until 
00059     *  it gets stopped for the last time. */
00060    unsigned int use_count;
00061    unsigned int orig_end_dtmf_flag:1;
00062    AST_LIST_HEAD_NOLOCK(, ast_frame) dtmf_frames;
00063    AST_LIST_ENTRY(asent) list;
00064 };
00065 
00066 static AST_LIST_HEAD_STATIC(aslist, asent);
00067 
00068 static pthread_t asthread = AST_PTHREADT_NULL;
00069 
00070 static void defer_frame(struct ast_channel *chan, struct ast_frame *f)
00071 {
00072    struct ast_frame *dup_f;
00073    struct asent *as;
00074 
00075    AST_LIST_LOCK(&aslist);
00076    AST_LIST_TRAVERSE(&aslist, as, list) {
00077       if (as->chan != chan)
00078          continue;
00079       if ((dup_f = ast_frdup(f)))
00080          AST_LIST_INSERT_TAIL(&as->dtmf_frames, dup_f, frame_list);
00081    }
00082    AST_LIST_UNLOCK(&aslist);
00083 }
00084 
00085 static void *autoservice_run(void *ign)
00086 {
00087    for (;;) {
00088       struct ast_channel *mons[MAX_AUTOMONS];
00089       struct ast_channel *chan;
00090       struct asent *as;
00091       int x = 0, ms = 500;
00092 
00093       AST_LIST_LOCK(&aslist);
00094       AST_LIST_TRAVERSE(&aslist, as, list) {
00095          if (!as->chan->_softhangup) {
00096             if (x < MAX_AUTOMONS)
00097                mons[x++] = as->chan;
00098             else
00099                ast_log(LOG_WARNING, "Exceeded maximum number of automatic monitoring events.  Fix autoservice.c\n");
00100          }
00101       }
00102       AST_LIST_UNLOCK(&aslist);
00103 
00104       chan = ast_waitfor_n(mons, x, &ms);
00105       if (chan) {
00106          struct ast_frame *f = ast_read(chan);
00107    
00108          if (!f) {
00109             struct ast_frame hangup_frame = { 0, };
00110             /* No frame means the channel has been hung up.
00111              * A hangup frame needs to be queued here as ast_waitfor() may
00112              * never return again for the condition to be detected outside
00113              * of autoservice.  So, we'll leave a HANGUP queued up so the
00114              * thread in charge of this channel will know. */
00115 
00116             hangup_frame.frametype = AST_FRAME_CONTROL;
00117             hangup_frame.subclass = AST_CONTROL_HANGUP;
00118 
00119             defer_frame(chan, &hangup_frame);
00120 
00121             continue;
00122          }
00123          
00124          /* Do not add a default entry in this switch statement.  Each new
00125           * frame type should be addressed directly as to whether it should
00126           * be queued up or not. */
00127          switch (f->frametype) {
00128          /* Save these frames */
00129          case AST_FRAME_DTMF_END:
00130          case AST_FRAME_CONTROL:
00131          case AST_FRAME_TEXT:
00132          case AST_FRAME_IMAGE:
00133          case AST_FRAME_HTML:
00134             defer_frame(chan, f);
00135             break;
00136 
00137          /* Throw these frames away */
00138          case AST_FRAME_DTMF_BEGIN:
00139          case AST_FRAME_VOICE:
00140          case AST_FRAME_VIDEO:
00141          case AST_FRAME_NULL:
00142          case AST_FRAME_IAX:
00143          case AST_FRAME_CNG:
00144          case AST_FRAME_MODEM:
00145             break;
00146          }
00147 
00148          if (f)
00149             ast_frfree(f);
00150       }
00151    }
00152    asthread = AST_PTHREADT_NULL;
00153    return NULL;
00154 }
00155 
00156 int ast_autoservice_start(struct ast_channel *chan)
00157 {
00158    int res = 0;
00159    struct asent *as;
00160 
00161    /* Check if the channel already has autoservice */
00162    AST_LIST_LOCK(&aslist);
00163    AST_LIST_TRAVERSE(&aslist, as, list) {
00164       if (as->chan == chan) {
00165          as->use_count++;
00166          break;
00167       }
00168    }
00169    AST_LIST_UNLOCK(&aslist);
00170 
00171    if (as) {
00172       /* Entry exists, autoservice is already handling this channel */
00173       return 0;
00174    }
00175 
00176    if (!(as = ast_calloc(1, sizeof(*as))))
00177       return -1;
00178    
00179    /* New entry created */
00180    as->chan = chan;
00181    as->use_count = 1;
00182 
00183    ast_channel_lock(chan);
00184    as->orig_end_dtmf_flag = ast_test_flag(chan, AST_FLAG_END_DTMF_ONLY) ? 1 : 0;
00185    if (!as->orig_end_dtmf_flag)
00186       ast_set_flag(chan, AST_FLAG_END_DTMF_ONLY);
00187    ast_channel_unlock(chan);
00188 
00189    AST_LIST_LOCK(&aslist);
00190    AST_LIST_INSERT_HEAD(&aslist, as, list);
00191    AST_LIST_UNLOCK(&aslist);
00192 
00193    if (asthread == AST_PTHREADT_NULL) { /* need start the thread */
00194       if (ast_pthread_create_background(&asthread, NULL, autoservice_run, NULL)) {
00195          ast_log(LOG_WARNING, "Unable to create autoservice thread :(\n");
00196          /* There will only be a single member in the list at this point,
00197             the one we just added. */
00198          AST_LIST_LOCK(&aslist);
00199          AST_LIST_REMOVE(&aslist, as, list);
00200          AST_LIST_UNLOCK(&aslist);
00201          free(as);
00202          res = -1;
00203       } else
00204          pthread_kill(asthread, SIGURG);
00205    }
00206 
00207    return res;
00208 }
00209 
00210 int ast_autoservice_stop(struct ast_channel *chan)
00211 {
00212    int res = -1;
00213    struct asent *as;
00214    AST_LIST_HEAD_NOLOCK(, ast_frame) dtmf_frames;
00215    struct ast_frame *f;
00216    int removed = 0;
00217    int orig_end_dtmf_flag = 0;
00218 
00219    AST_LIST_HEAD_INIT_NOLOCK(&dtmf_frames);
00220 
00221    AST_LIST_LOCK(&aslist);
00222    AST_LIST_TRAVERSE_SAFE_BEGIN(&aslist, as, list) {  
00223       if (as->chan == chan) {
00224          as->use_count--;
00225          if (as->use_count)
00226             break;
00227          AST_LIST_REMOVE_CURRENT(&aslist, list);
00228          AST_LIST_APPEND_LIST(&dtmf_frames, &as->dtmf_frames, frame_list);
00229          orig_end_dtmf_flag = as->orig_end_dtmf_flag;
00230          free(as);
00231          removed = 1;
00232          if (!chan->_softhangup)
00233             res = 0;
00234          break;
00235       }
00236    }
00237    AST_LIST_TRAVERSE_SAFE_END
00238 
00239    if (removed && asthread != AST_PTHREADT_NULL) 
00240       pthread_kill(asthread, SIGURG);
00241 
00242    AST_LIST_UNLOCK(&aslist);
00243 
00244    if (!removed)
00245       return 0;
00246 
00247    if (!orig_end_dtmf_flag)
00248       ast_clear_flag(chan, AST_FLAG_END_DTMF_ONLY);
00249 
00250    /* Wait for it to un-block */
00251    while (ast_test_flag(chan, AST_FLAG_BLOCKING))
00252       usleep(1000);
00253 
00254    while ((f = AST_LIST_REMOVE_HEAD(&dtmf_frames, frame_list))) {
00255       ast_queue_frame(chan, f);
00256       ast_frfree(f);
00257    }
00258 
00259    return res;
00260 }

Generated on Sat Apr 12 07:12:18 2008 for Asterisk - the Open Source PBX by  doxygen 1.5.5