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
) dtmf_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 defer_frame(struct ast_channel
*chan
, struct ast_frame
*f
)
77 struct ast_frame
*dup_f
;
80 AST_LIST_LOCK(&aslist
);
81 AST_LIST_TRAVERSE(&aslist
, as
, list
) {
84 if ((dup_f
= ast_frdup(f
)))
85 AST_LIST_INSERT_TAIL(&as
->dtmf_frames
, dup_f
, frame_list
);
87 AST_LIST_UNLOCK(&aslist
);
90 static void *autoservice_run(void *ign
)
93 struct ast_channel
*mons
[MAX_AUTOMONS
];
94 struct ast_channel
*chan
;
98 AST_LIST_LOCK(&aslist
);
100 /* At this point, we know that no channels that have been removed are going
101 * to get used again. */
102 as_chan_list_state
++;
104 if (AST_LIST_EMPTY(&aslist
))
105 ast_cond_wait(&as_cond
, &aslist
.lock
);
107 AST_LIST_TRAVERSE(&aslist
, as
, list
) {
108 if (!as
->chan
->_softhangup
) {
109 if (x
< MAX_AUTOMONS
)
110 mons
[x
++] = as
->chan
;
112 ast_log(LOG_WARNING
, "Exceeded maximum number of automatic monitoring events. Fix autoservice.c\n");
116 AST_LIST_UNLOCK(&aslist
);
118 chan
= ast_waitfor_n(mons
, x
, &ms
);
120 struct ast_frame
*f
= ast_read(chan
);
123 struct ast_frame hangup_frame
= { 0, };
124 /* No frame means the channel has been hung up.
125 * A hangup frame needs to be queued here as ast_waitfor() may
126 * never return again for the condition to be detected outside
127 * of autoservice. So, we'll leave a HANGUP queued up so the
128 * thread in charge of this channel will know. */
130 hangup_frame
.frametype
= AST_FRAME_CONTROL
;
131 hangup_frame
.subclass
= AST_CONTROL_HANGUP
;
133 defer_frame(chan
, &hangup_frame
);
138 /* Do not add a default entry in this switch statement. Each new
139 * frame type should be addressed directly as to whether it should
140 * be queued up or not. */
141 switch (f
->frametype
) {
142 /* Save these frames */
143 case AST_FRAME_DTMF_END
:
144 case AST_FRAME_CONTROL
:
146 case AST_FRAME_IMAGE
:
148 defer_frame(chan
, f
);
151 /* Throw these frames away */
152 case AST_FRAME_DTMF_BEGIN
:
153 case AST_FRAME_VOICE
:
154 case AST_FRAME_VIDEO
:
158 case AST_FRAME_MODEM
:
166 asthread
= AST_PTHREADT_NULL
;
170 int ast_autoservice_start(struct ast_channel
*chan
)
175 /* Check if the channel already has autoservice */
176 AST_LIST_LOCK(&aslist
);
177 AST_LIST_TRAVERSE(&aslist
, as
, list
) {
178 if (as
->chan
== chan
) {
183 AST_LIST_UNLOCK(&aslist
);
186 /* Entry exists, autoservice is already handling this channel */
190 if (!(as
= ast_calloc(1, sizeof(*as
))))
193 /* New entry created */
197 ast_channel_lock(chan
);
198 as
->orig_end_dtmf_flag
= ast_test_flag(chan
, AST_FLAG_END_DTMF_ONLY
) ? 1 : 0;
199 if (!as
->orig_end_dtmf_flag
)
200 ast_set_flag(chan
, AST_FLAG_END_DTMF_ONLY
);
201 ast_channel_unlock(chan
);
203 AST_LIST_LOCK(&aslist
);
204 if (AST_LIST_EMPTY(&aslist
))
205 ast_cond_signal(&as_cond
);
206 AST_LIST_INSERT_HEAD(&aslist
, as
, list
);
207 AST_LIST_UNLOCK(&aslist
);
209 if (asthread
== AST_PTHREADT_NULL
) { /* need start the thread */
210 if (ast_pthread_create_background(&asthread
, NULL
, autoservice_run
, NULL
)) {
211 ast_log(LOG_WARNING
, "Unable to create autoservice thread :(\n");
212 /* There will only be a single member in the list at this point,
213 the one we just added. */
214 AST_LIST_LOCK(&aslist
);
215 AST_LIST_REMOVE(&aslist
, as
, list
);
216 AST_LIST_UNLOCK(&aslist
);
220 pthread_kill(asthread
, SIGURG
);
226 int ast_autoservice_stop(struct ast_channel
*chan
)
230 AST_LIST_HEAD_NOLOCK(, ast_frame
) dtmf_frames
;
233 int orig_end_dtmf_flag
= 0;
236 AST_LIST_HEAD_INIT_NOLOCK(&dtmf_frames
);
238 AST_LIST_LOCK(&aslist
);
240 /* Save the autoservice channel list state. We _must_ verify that the channel
241 * list has been rebuilt before we return. Because, after we return, the channel
242 * could get destroyed and we don't want our poor autoservice thread to step on
243 * it after its gone! */
244 chan_list_state
= as_chan_list_state
;
246 AST_LIST_TRAVERSE_SAFE_BEGIN(&aslist
, as
, list
) {
247 if (as
->chan
== chan
) {
251 AST_LIST_REMOVE_CURRENT(&aslist
, list
);
252 AST_LIST_APPEND_LIST(&dtmf_frames
, &as
->dtmf_frames
, frame_list
);
253 orig_end_dtmf_flag
= as
->orig_end_dtmf_flag
;
256 if (!chan
->_softhangup
)
261 AST_LIST_TRAVERSE_SAFE_END
263 if (removed
&& asthread
!= AST_PTHREADT_NULL
)
264 pthread_kill(asthread
, SIGURG
);
266 AST_LIST_UNLOCK(&aslist
);
271 if (!orig_end_dtmf_flag
)
272 ast_clear_flag(chan
, AST_FLAG_END_DTMF_ONLY
);
274 /* Wait for it to un-block */
275 while (ast_test_flag(chan
, AST_FLAG_BLOCKING
))
278 while ((f
= AST_LIST_REMOVE_HEAD(&dtmf_frames
, frame_list
))) {
279 ast_queue_frame(chan
, f
);
283 while (chan_list_state
== as_chan_list_state
)
289 void ast_autoservice_init(void)
291 ast_cond_init(&as_cond
, NULL
);