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
:
168 for (i
= 0; i
< x
; i
++) {
169 struct ast_frame
*dup_f
;
171 if (mons
[i
] != chan
) {
175 if ((dup_f
= ast_frdup(f
))) {
176 AST_LIST_INSERT_TAIL(&ents
[i
]->deferred_frames
, dup_f
, frame_list
);
185 asthread
= AST_PTHREADT_NULL
;
190 int ast_autoservice_start(struct ast_channel
*chan
)
195 /* Check if the channel already has autoservice */
196 AST_LIST_LOCK(&aslist
);
197 AST_LIST_TRAVERSE(&aslist
, as
, list
) {
198 if (as
->chan
== chan
) {
203 AST_LIST_UNLOCK(&aslist
);
206 /* Entry exists, autoservice is already handling this channel */
210 if (!(as
= ast_calloc(1, sizeof(*as
))))
213 /* New entry created */
217 ast_channel_lock(chan
);
218 as
->orig_end_dtmf_flag
= ast_test_flag(chan
, AST_FLAG_END_DTMF_ONLY
) ? 1 : 0;
219 if (!as
->orig_end_dtmf_flag
)
220 ast_set_flag(chan
, AST_FLAG_END_DTMF_ONLY
);
221 ast_channel_unlock(chan
);
223 AST_LIST_LOCK(&aslist
);
225 if (AST_LIST_EMPTY(&aslist
) && asthread
!= AST_PTHREADT_NULL
) {
226 ast_cond_signal(&as_cond
);
229 AST_LIST_INSERT_HEAD(&aslist
, as
, list
);
231 if (asthread
== AST_PTHREADT_NULL
) { /* need start the thread */
232 if (ast_pthread_create_background(&asthread
, NULL
, autoservice_run
, NULL
)) {
233 ast_log(LOG_WARNING
, "Unable to create autoservice thread :(\n");
234 /* There will only be a single member in the list at this point,
235 the one we just added. */
236 AST_LIST_REMOVE(&aslist
, as
, list
);
238 asthread
= AST_PTHREADT_NULL
;
241 pthread_kill(asthread
, SIGURG
);
245 AST_LIST_UNLOCK(&aslist
);
250 int ast_autoservice_stop(struct ast_channel
*chan
)
253 struct asent
*as
, *removed
= NULL
;
257 AST_LIST_LOCK(&aslist
);
259 /* Save the autoservice channel list state. We _must_ verify that the channel
260 * list has been rebuilt before we return. Because, after we return, the channel
261 * could get destroyed and we don't want our poor autoservice thread to step on
262 * it after its gone! */
263 chan_list_state
= as_chan_list_state
;
265 /* Find the entry, but do not free it because it still can be in the
266 autoservice thread array */
267 AST_LIST_TRAVERSE_SAFE_BEGIN(&aslist
, as
, list
) {
268 if (as
->chan
== chan
) {
270 if (as
->use_count
< 1) {
271 AST_LIST_REMOVE_CURRENT(&aslist
, list
);
277 AST_LIST_TRAVERSE_SAFE_END
279 if (removed
&& asthread
!= AST_PTHREADT_NULL
) {
280 pthread_kill(asthread
, SIGURG
);
283 AST_LIST_UNLOCK(&aslist
);
289 /* Wait while autoservice thread rebuilds its list. */
290 while (chan_list_state
== as_chan_list_state
) {
294 /* Now autoservice thread should have no references to our entry
295 and we can safely destroy it */
297 if (!chan
->_softhangup
) {
301 if (!as
->orig_end_dtmf_flag
) {
302 ast_clear_flag(chan
, AST_FLAG_END_DTMF_ONLY
);
305 while ((f
= AST_LIST_REMOVE_HEAD(&as
->deferred_frames
, frame_list
))) {
306 ast_queue_frame(chan
, f
);
315 void ast_autoservice_init(void)
317 ast_cond_init(&as_cond
, NULL
);