2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2008, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
7 * Russell Bryant <russell@digium.com>
9 * See http://www.asterisk.org for more information about
10 * the Asterisk project. Please do not directly contact
11 * any of the maintainers of this project for assistance;
12 * the project provides a web site, mailing lists and IRC
13 * channels for your use.
15 * This program is free software, distributed under the terms of
16 * the GNU General Public License Version 2. See the LICENSE file
17 * at the top of the source tree.
22 * \brief Automatic channel service routines
24 * \author Mark Spencer <markster@digium.com>
25 * \author Russell Bryant <russell@digium.com>
29 <support_level>core</support_level>
34 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
39 #include "asterisk/_private.h" /* prototype for ast_autoservice_init() */
41 #include "asterisk/pbx.h"
42 #include "asterisk/frame.h"
43 #include "asterisk/sched.h"
44 #include "asterisk/channel.h"
45 #include "asterisk/file.h"
46 #include "asterisk/translate.h"
47 #include "asterisk/manager.h"
48 #include "asterisk/chanvars.h"
49 #include "asterisk/linkedlists.h"
50 #include "asterisk/indications.h"
51 #include "asterisk/lock.h"
52 #include "asterisk/utils.h"
54 #define MAX_AUTOMONS 1500
57 struct ast_channel *chan;
58 /*! This gets incremented each time autoservice gets started on the same
59 * channel. It will ensure that it doesn't actually get stopped until
60 * it gets stopped for the last time. */
61 unsigned int use_count;
62 unsigned int orig_end_dtmf_flag:1;
63 unsigned int ignore_frame_types;
64 /*! Frames go on at the head of deferred_frames, so we have the frames
65 * from newest to oldest. As we put them at the head of the readq, we'll
66 * end up with them in the right order for the channel's readq. */
67 AST_LIST_HEAD_NOLOCK(, ast_frame) deferred_frames;
68 AST_LIST_ENTRY(asent) list;
71 static AST_LIST_HEAD_STATIC(aslist, asent);
72 static ast_cond_t as_cond;
74 static pthread_t asthread = AST_PTHREADT_NULL;
76 static int as_chan_list_state;
78 static void *autoservice_run(void *ign)
80 struct ast_callid *callid = NULL;
81 struct ast_frame hangup_frame = {
82 .frametype = AST_FRAME_CONTROL,
83 .subclass.integer = AST_CONTROL_HANGUP,
87 struct ast_channel *mons[MAX_AUTOMONS];
88 struct asent *ents[MAX_AUTOMONS];
89 struct ast_channel *chan;
91 int i, x = 0, ms = 50;
92 struct ast_frame *f = NULL;
93 struct ast_frame *defer_frame = NULL;
95 AST_LIST_LOCK(&aslist);
97 /* At this point, we know that no channels that have been removed are going
98 * to get used again. */
101 if (AST_LIST_EMPTY(&aslist)) {
102 ast_cond_wait(&as_cond, &aslist.lock);
105 AST_LIST_TRAVERSE(&aslist, as, list) {
106 if (!ast_check_hangup(as->chan)) {
107 if (x < MAX_AUTOMONS) {
109 mons[x++] = as->chan;
111 ast_log(LOG_WARNING, "Exceeded maximum number of automatic monitoring events. Fix autoservice.c\n");
116 AST_LIST_UNLOCK(&aslist);
119 /* If we don't sleep, this becomes a busy loop, which causes
120 * problems when Asterisk runs at a different priority than other
121 * user processes. As long as we check for new channels at least
122 * once every 10ms, we should be fine. */
127 chan = ast_waitfor_n(mons, x, &ms);
132 callid = ast_channel_callid(chan);
133 ast_callid_threadassoc_change(callid);
138 /* No frame means the channel has been hung up.
139 * A hangup frame needs to be queued here as ast_waitfor() may
140 * never return again for the condition to be detected outside
141 * of autoservice. So, we'll leave a HANGUP queued up so the
142 * thread in charge of this channel will know. */
144 defer_frame = &hangup_frame;
145 } else if (ast_is_deferrable_frame(f)) {
148 /* Can't defer. Discard and continue with next. */
153 for (i = 0; i < x; i++) {
154 struct ast_frame *dup_f;
156 if (mons[i] != chan) {
160 if (!f) { /* defer_frame == &hangup_frame */
161 if ((dup_f = ast_frdup(defer_frame))) {
162 AST_LIST_INSERT_HEAD(&ents[i]->deferred_frames, dup_f, frame_list);
165 if ((dup_f = ast_frisolate(defer_frame))) {
166 AST_LIST_INSERT_HEAD(&ents[i]->deferred_frames, dup_f, frame_list);
168 if (dup_f != defer_frame) {
169 ast_frfree(defer_frame);
175 /* The ast_waitfor_n() call will only read frames from
176 * the channels' file descriptors. If ast_waitfor_n()
177 * returns non-NULL, then one of the channels in the
178 * mons array must have triggered the return. It's
179 * therefore impossible that we got here while (i >= x).
180 * If we did, we'd need to ast_frfree(f) if (f). */
184 ast_callid_threadassoc_remove();
185 callid = ast_callid_unref(callid);
188 asthread = AST_PTHREADT_NULL;
193 int ast_autoservice_start(struct ast_channel *chan)
198 AST_LIST_LOCK(&aslist);
199 AST_LIST_TRAVERSE(&aslist, as, list) {
200 if (as->chan == chan) {
205 AST_LIST_UNLOCK(&aslist);
208 /* Entry exists, autoservice is already handling this channel */
212 if (!(as = ast_calloc(1, sizeof(*as))))
215 /* New entry created */
219 ast_channel_lock(chan);
220 as->orig_end_dtmf_flag = ast_test_flag(ast_channel_flags(chan), AST_FLAG_END_DTMF_ONLY) ? 1 : 0;
221 if (!as->orig_end_dtmf_flag)
222 ast_set_flag(ast_channel_flags(chan), AST_FLAG_END_DTMF_ONLY);
223 ast_channel_unlock(chan);
225 AST_LIST_LOCK(&aslist);
227 if (AST_LIST_EMPTY(&aslist) && asthread != AST_PTHREADT_NULL) {
228 ast_cond_signal(&as_cond);
231 AST_LIST_INSERT_HEAD(&aslist, as, list);
233 if (asthread == AST_PTHREADT_NULL) { /* need start the thread */
234 if (ast_pthread_create_background(&asthread, NULL, autoservice_run, NULL)) {
235 ast_log(LOG_WARNING, "Unable to create autoservice thread :(\n");
236 /* There will only be a single member in the list at this point,
237 the one we just added. */
238 AST_LIST_REMOVE(&aslist, as, list);
240 asthread = AST_PTHREADT_NULL;
243 pthread_kill(asthread, SIGURG);
247 AST_LIST_UNLOCK(&aslist);
252 int ast_autoservice_stop(struct ast_channel *chan)
255 struct asent *as, *removed = NULL;
259 AST_LIST_LOCK(&aslist);
261 /* Save the autoservice channel list state. We _must_ verify that the channel
262 * list has been rebuilt before we return. Because, after we return, the channel
263 * could get destroyed and we don't want our poor autoservice thread to step on
264 * it after its gone! */
265 chan_list_state = as_chan_list_state;
267 /* Find the entry, but do not free it because it still can be in the
268 autoservice thread array */
269 AST_LIST_TRAVERSE_SAFE_BEGIN(&aslist, as, list) {
270 if (as->chan == chan) {
272 if (as->use_count < 1) {
273 AST_LIST_REMOVE_CURRENT(list);
279 AST_LIST_TRAVERSE_SAFE_END;
281 if (removed && asthread != AST_PTHREADT_NULL) {
282 pthread_kill(asthread, SIGURG);
285 AST_LIST_UNLOCK(&aslist);
291 /* Wait while autoservice thread rebuilds its list. */
292 while (chan_list_state == as_chan_list_state) {
296 /* Now autoservice thread should have no references to our entry
297 and we can safely destroy it */
299 if (!ast_channel_softhangup_internal_flag(chan)) {
303 if (!as->orig_end_dtmf_flag) {
304 ast_clear_flag(ast_channel_flags(chan), AST_FLAG_END_DTMF_ONLY);
307 ast_channel_lock(chan);
308 while ((f = AST_LIST_REMOVE_HEAD(&as->deferred_frames, frame_list))) {
309 if (!((1 << f->frametype) & as->ignore_frame_types)) {
310 ast_queue_frame_head(chan, f);
314 ast_channel_unlock(chan);
321 void ast_autoservice_chan_hangup_peer(struct ast_channel *chan, struct ast_channel *peer)
323 if (chan && !ast_autoservice_start(chan)) {
325 ast_autoservice_stop(chan);
331 int ast_autoservice_ignore(struct ast_channel *chan, enum ast_frame_type ftype)
336 AST_LIST_LOCK(&aslist);
337 AST_LIST_TRAVERSE(&aslist, as, list) {
338 if (as->chan == chan) {
340 as->ignore_frame_types |= (1 << ftype);
344 AST_LIST_UNLOCK(&aslist);
348 void ast_autoservice_init(void)
350 ast_cond_init(&as_cond, NULL);