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$")
35 #include "asterisk/_private.h" /* prototype for ast_autoservice_init() */
37 #include "asterisk/pbx.h"
38 #include "asterisk/frame.h"
39 #include "asterisk/sched.h"
40 #include "asterisk/channel.h"
41 #include "asterisk/file.h"
42 #include "asterisk/translate.h"
43 #include "asterisk/manager.h"
44 #include "asterisk/chanvars.h"
45 #include "asterisk/linkedlists.h"
46 #include "asterisk/indications.h"
47 #include "asterisk/lock.h"
48 #include "asterisk/utils.h"
50 #define MAX_AUTOMONS 1500
53 struct ast_channel
*chan
;
54 /*! This gets incremented each time autoservice gets started on the same
55 * channel. It will ensure that it doesn't actually get stopped until
56 * it gets stopped for the last time. */
57 unsigned int use_count
;
58 unsigned int orig_end_dtmf_flag
:1;
59 AST_LIST_HEAD_NOLOCK(, ast_frame
) deferred_frames
;
60 AST_LIST_ENTRY(asent
) list
;
63 static AST_LIST_HEAD_STATIC(aslist
, asent
);
64 static ast_cond_t as_cond
;
66 static pthread_t asthread
= AST_PTHREADT_NULL
;
68 static int as_chan_list_state
;
70 static void *autoservice_run(void *ign
)
73 struct ast_channel
*mons
[MAX_AUTOMONS
];
74 struct asent
*ents
[MAX_AUTOMONS
];
75 struct ast_channel
*chan
;
77 int i
, x
= 0, ms
= 50;
78 struct ast_frame
*f
= NULL
;
79 struct ast_frame
*defer_frame
= NULL
;
81 AST_LIST_LOCK(&aslist
);
83 /* At this point, we know that no channels that have been removed are going
84 * to get used again. */
87 if (AST_LIST_EMPTY(&aslist
)) {
88 ast_cond_wait(&as_cond
, &aslist
.lock
);
91 AST_LIST_TRAVERSE(&aslist
, as
, list
) {
92 if (!ast_check_hangup(as
->chan
)) {
93 if (x
< MAX_AUTOMONS
) {
97 ast_log(LOG_WARNING
, "Exceeded maximum number of automatic monitoring events. Fix autoservice.c\n");
102 AST_LIST_UNLOCK(&aslist
);
108 chan
= ast_waitfor_n(mons
, x
, &ms
);
116 struct ast_frame hangup_frame
= { 0, };
117 /* No frame means the channel has been hung up.
118 * A hangup frame needs to be queued here as ast_waitfor() may
119 * never return again for the condition to be detected outside
120 * of autoservice. So, we'll leave a HANGUP queued up so the
121 * thread in charge of this channel will know. */
123 hangup_frame
.frametype
= AST_FRAME_CONTROL
;
124 hangup_frame
.subclass
= AST_CONTROL_HANGUP
;
126 defer_frame
= &hangup_frame
;
129 /* Do not add a default entry in this switch statement. Each new
130 * frame type should be addressed directly as to whether it should
131 * be queued up or not. */
133 switch (f
->frametype
) {
134 /* Save these frames */
135 case AST_FRAME_DTMF_END
:
136 case AST_FRAME_CONTROL
:
138 case AST_FRAME_IMAGE
:
143 /* Throw these frames away */
144 case AST_FRAME_DTMF_BEGIN
:
145 case AST_FRAME_VOICE
:
146 case AST_FRAME_VIDEO
:
150 case AST_FRAME_MODEM
:
156 for (i
= 0; i
< x
; i
++) {
157 struct ast_frame
*dup_f
;
159 if (mons
[i
] != chan
) {
163 if ((dup_f
= ast_frdup(defer_frame
))) {
164 AST_LIST_INSERT_TAIL(&ents
[i
]->deferred_frames
, dup_f
, frame_list
);
176 asthread
= AST_PTHREADT_NULL
;
181 int ast_autoservice_start(struct ast_channel
*chan
)
186 AST_LIST_LOCK(&aslist
);
187 AST_LIST_TRAVERSE(&aslist
, as
, list
) {
188 if (as
->chan
== chan
) {
193 AST_LIST_UNLOCK(&aslist
);
196 /* Entry exists, autoservice is already handling this channel */
200 if (!(as
= ast_calloc(1, sizeof(*as
))))
203 /* New entry created */
207 ast_channel_lock(chan
);
208 as
->orig_end_dtmf_flag
= ast_test_flag(chan
, AST_FLAG_END_DTMF_ONLY
) ? 1 : 0;
209 if (!as
->orig_end_dtmf_flag
)
210 ast_set_flag(chan
, AST_FLAG_END_DTMF_ONLY
);
211 ast_channel_unlock(chan
);
213 AST_LIST_LOCK(&aslist
);
215 if (AST_LIST_EMPTY(&aslist
) && asthread
!= AST_PTHREADT_NULL
) {
216 ast_cond_signal(&as_cond
);
219 AST_LIST_INSERT_HEAD(&aslist
, as
, list
);
221 if (asthread
== AST_PTHREADT_NULL
) { /* need start the thread */
222 if (ast_pthread_create_background(&asthread
, NULL
, autoservice_run
, NULL
)) {
223 ast_log(LOG_WARNING
, "Unable to create autoservice thread :(\n");
224 /* There will only be a single member in the list at this point,
225 the one we just added. */
226 AST_LIST_REMOVE(&aslist
, as
, list
);
228 asthread
= AST_PTHREADT_NULL
;
231 pthread_kill(asthread
, SIGURG
);
235 AST_LIST_UNLOCK(&aslist
);
240 int ast_autoservice_stop(struct ast_channel
*chan
)
243 struct asent
*as
, *removed
= NULL
;
247 AST_LIST_LOCK(&aslist
);
249 /* Save the autoservice channel list state. We _must_ verify that the channel
250 * list has been rebuilt before we return. Because, after we return, the channel
251 * could get destroyed and we don't want our poor autoservice thread to step on
252 * it after its gone! */
253 chan_list_state
= as_chan_list_state
;
255 /* Find the entry, but do not free it because it still can be in the
256 autoservice thread array */
257 AST_LIST_TRAVERSE_SAFE_BEGIN(&aslist
, as
, list
) {
258 if (as
->chan
== chan
) {
260 if (as
->use_count
< 1) {
261 AST_LIST_REMOVE_CURRENT(list
);
267 AST_LIST_TRAVERSE_SAFE_END
;
269 if (removed
&& asthread
!= AST_PTHREADT_NULL
) {
270 pthread_kill(asthread
, SIGURG
);
273 AST_LIST_UNLOCK(&aslist
);
279 /* Wait while autoservice thread rebuilds its list. */
280 while (chan_list_state
== as_chan_list_state
) {
284 /* Now autoservice thread should have no references to our entry
285 and we can safely destroy it */
287 if (!chan
->_softhangup
) {
291 if (!as
->orig_end_dtmf_flag
) {
292 ast_clear_flag(chan
, AST_FLAG_END_DTMF_ONLY
);
295 while ((f
= AST_LIST_REMOVE_HEAD(&as
->deferred_frames
, frame_list
))) {
296 ast_queue_frame(chan
, f
);
305 void ast_autoservice_init(void)
307 ast_cond_init(&as_cond
, NULL
);