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>
30 ASTERISK_FILE_VERSION(__FILE__
, "$Revision$")
40 #include "asterisk/pbx.h"
41 #include "asterisk/frame.h"
42 #include "asterisk/sched.h"
43 #include "asterisk/options.h"
44 #include "asterisk/channel.h"
45 #include "asterisk/logger.h"
46 #include "asterisk/file.h"
47 #include "asterisk/translate.h"
48 #include "asterisk/manager.h"
49 #include "asterisk/chanvars.h"
50 #include "asterisk/linkedlists.h"
51 #include "asterisk/indications.h"
52 #include "asterisk/lock.h"
53 #include "asterisk/utils.h"
55 #define MAX_AUTOMONS 1500
58 struct ast_channel
*chan
;
59 /*! This gets incremented each time autoservice gets started on the same
60 * channel. It will ensure that it doesn't actually get stopped until
61 * it gets stopped for the last time. */
62 unsigned int use_count
;
63 unsigned int orig_end_dtmf_flag
:1;
64 AST_LIST_HEAD_NOLOCK(, ast_frame
) deferred_frames
;
65 AST_LIST_ENTRY(asent
) list
;
68 static AST_LIST_HEAD_STATIC(aslist
, asent
);
69 static ast_cond_t as_cond
;
71 static pthread_t asthread
= AST_PTHREADT_NULL
;
73 static int as_chan_list_state
;
75 static void *autoservice_run(void *ign
)
78 struct ast_channel
*mons
[MAX_AUTOMONS
];
79 struct asent
*ents
[MAX_AUTOMONS
];
80 struct ast_channel
*chan
;
82 int i
, x
= 0, ms
= 50;
83 struct ast_frame
*f
= NULL
;
84 struct ast_frame
*defer_frame
= NULL
;
86 AST_LIST_LOCK(&aslist
);
88 /* At this point, we know that no channels that have been removed are going
89 * to get used again. */
92 if (AST_LIST_EMPTY(&aslist
)) {
93 ast_cond_wait(&as_cond
, &aslist
.lock
);
96 AST_LIST_TRAVERSE(&aslist
, as
, list
) {
97 if (!as
->chan
->_softhangup
) {
98 if (x
< MAX_AUTOMONS
) {
100 mons
[x
++] = as
->chan
;
102 ast_log(LOG_WARNING
, "Exceeded maximum number of automatic monitoring events. Fix autoservice.c\n");
107 AST_LIST_UNLOCK(&aslist
);
113 chan
= ast_waitfor_n(mons
, x
, &ms
);
121 struct ast_frame hangup_frame
= { 0, };
122 /* No frame means the channel has been hung up.
123 * A hangup frame needs to be queued here as ast_waitfor() may
124 * never return again for the condition to be detected outside
125 * of autoservice. So, we'll leave a HANGUP queued up so the
126 * thread in charge of this channel will know. */
128 hangup_frame
.frametype
= AST_FRAME_CONTROL
;
129 hangup_frame
.subclass
= AST_CONTROL_HANGUP
;
131 defer_frame
= &hangup_frame
;
134 /* Do not add a default entry in this switch statement. Each new
135 * frame type should be addressed directly as to whether it should
136 * be queued up or not. */
138 switch (f
->frametype
) {
139 /* Save these frames */
140 case AST_FRAME_DTMF_END
:
141 case AST_FRAME_CONTROL
:
143 case AST_FRAME_IMAGE
:
148 /* Throw these frames away */
149 case AST_FRAME_DTMF_BEGIN
:
150 case AST_FRAME_VOICE
:
151 case AST_FRAME_VIDEO
:
155 case AST_FRAME_MODEM
:
161 for (i
= 0; i
< x
; i
++) {
162 struct ast_frame
*dup_f
;
164 if (mons
[i
] != chan
) {
168 if ((dup_f
= ast_frdup(defer_frame
))) {
169 AST_LIST_INSERT_TAIL(&ents
[i
]->deferred_frames
, dup_f
, frame_list
);
181 asthread
= AST_PTHREADT_NULL
;
186 int ast_autoservice_start(struct ast_channel
*chan
)
191 /* Check if the channel already has autoservice */
192 AST_LIST_LOCK(&aslist
);
193 AST_LIST_TRAVERSE(&aslist
, as
, list
) {
194 if (as
->chan
== chan
) {
199 AST_LIST_UNLOCK(&aslist
);
202 /* Entry exists, autoservice is already handling this channel */
206 if (!(as
= ast_calloc(1, sizeof(*as
))))
209 /* New entry created */
213 ast_channel_lock(chan
);
214 as
->orig_end_dtmf_flag
= ast_test_flag(chan
, AST_FLAG_END_DTMF_ONLY
) ? 1 : 0;
215 if (!as
->orig_end_dtmf_flag
)
216 ast_set_flag(chan
, AST_FLAG_END_DTMF_ONLY
);
217 ast_channel_unlock(chan
);
219 AST_LIST_LOCK(&aslist
);
221 if (AST_LIST_EMPTY(&aslist
) && asthread
!= AST_PTHREADT_NULL
) {
222 ast_cond_signal(&as_cond
);
225 AST_LIST_INSERT_HEAD(&aslist
, as
, list
);
227 if (asthread
== AST_PTHREADT_NULL
) { /* need start the thread */
228 if (ast_pthread_create_background(&asthread
, NULL
, autoservice_run
, NULL
)) {
229 ast_log(LOG_WARNING
, "Unable to create autoservice thread :(\n");
230 /* There will only be a single member in the list at this point,
231 the one we just added. */
232 AST_LIST_REMOVE(&aslist
, as
, list
);
234 asthread
= AST_PTHREADT_NULL
;
237 pthread_kill(asthread
, SIGURG
);
241 AST_LIST_UNLOCK(&aslist
);
246 int ast_autoservice_stop(struct ast_channel
*chan
)
249 struct asent
*as
, *removed
= NULL
;
253 AST_LIST_LOCK(&aslist
);
255 /* Save the autoservice channel list state. We _must_ verify that the channel
256 * list has been rebuilt before we return. Because, after we return, the channel
257 * could get destroyed and we don't want our poor autoservice thread to step on
258 * it after its gone! */
259 chan_list_state
= as_chan_list_state
;
261 /* Find the entry, but do not free it because it still can be in the
262 autoservice thread array */
263 AST_LIST_TRAVERSE_SAFE_BEGIN(&aslist
, as
, list
) {
264 if (as
->chan
== chan
) {
266 if (as
->use_count
< 1) {
267 AST_LIST_REMOVE_CURRENT(&aslist
, list
);
273 AST_LIST_TRAVERSE_SAFE_END
275 if (removed
&& asthread
!= AST_PTHREADT_NULL
) {
276 pthread_kill(asthread
, SIGURG
);
279 AST_LIST_UNLOCK(&aslist
);
285 /* Wait while autoservice thread rebuilds its list. */
286 while (chan_list_state
== as_chan_list_state
) {
290 /* Now autoservice thread should have no references to our entry
291 and we can safely destroy it */
293 if (!chan
->_softhangup
) {
297 if (!as
->orig_end_dtmf_flag
) {
298 ast_clear_flag(chan
, AST_FLAG_END_DTMF_ONLY
);
301 while ((f
= AST_LIST_REMOVE_HEAD(&as
->deferred_frames
, frame_list
))) {
302 ast_queue_frame(chan
, f
);
311 void ast_autoservice_init(void)
313 ast_cond_init(&as_cond
, NULL
);