when a PRI call must be moved to a different B channel at the request of the other...
[asterisk-bristuff.git] / main / autoservice.c
blob84083c6c5086c7b6a50648b0493e099e6ccbbb61
1 /*
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.
20 /*! \file
22 * \brief Automatic channel service routines
24 * \author Mark Spencer <markster@digium.com>
25 * \author Russell Bryant <russell@digium.com>
28 #include "asterisk.h"
30 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <sys/time.h>
36 #include <signal.h>
37 #include <errno.h>
38 #include <unistd.h>
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
57 struct asent {
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;
78 struct asent *as;
80 AST_LIST_LOCK(&aslist);
81 AST_LIST_TRAVERSE(&aslist, as, list) {
82 if (as->chan != chan)
83 continue;
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)
92 for (;;) {
93 struct ast_channel *mons[MAX_AUTOMONS];
94 struct ast_channel *chan;
95 struct asent *as;
96 int x = 0, ms = 500;
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;
111 else
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);
119 if (chan) {
120 struct ast_frame *f = ast_read(chan);
122 if (!f) {
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);
135 continue;
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:
145 case AST_FRAME_TEXT:
146 case AST_FRAME_IMAGE:
147 case AST_FRAME_HTML:
148 defer_frame(chan, f);
149 break;
151 /* Throw these frames away */
152 case AST_FRAME_DTMF_BEGIN:
153 case AST_FRAME_VOICE:
154 case AST_FRAME_VIDEO:
155 case AST_FRAME_NULL:
156 case AST_FRAME_IAX:
157 case AST_FRAME_CNG:
158 case AST_FRAME_MODEM:
159 break;
162 if (f)
163 ast_frfree(f);
166 asthread = AST_PTHREADT_NULL;
167 return NULL;
170 int ast_autoservice_start(struct ast_channel *chan)
172 int res = 0;
173 struct asent *as;
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) {
179 as->use_count++;
180 break;
183 AST_LIST_UNLOCK(&aslist);
185 if (as) {
186 /* Entry exists, autoservice is already handling this channel */
187 return 0;
190 if (!(as = ast_calloc(1, sizeof(*as))))
191 return -1;
193 /* New entry created */
194 as->chan = chan;
195 as->use_count = 1;
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);
217 free(as);
218 res = -1;
219 } else
220 pthread_kill(asthread, SIGURG);
223 return res;
226 int ast_autoservice_stop(struct ast_channel *chan)
228 int res = -1;
229 struct asent *as;
230 AST_LIST_HEAD_NOLOCK(, ast_frame) dtmf_frames;
231 struct ast_frame *f;
232 int removed = 0;
233 int orig_end_dtmf_flag = 0;
234 int chan_list_state;
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) {
248 as->use_count--;
249 if (as->use_count)
250 break;
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;
254 free(as);
255 removed = 1;
256 if (!chan->_softhangup)
257 res = 0;
258 break;
261 AST_LIST_TRAVERSE_SAFE_END
263 if (removed && asthread != AST_PTHREADT_NULL)
264 pthread_kill(asthread, SIGURG);
266 AST_LIST_UNLOCK(&aslist);
268 if (!removed)
269 return 0;
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))
276 usleep(1000);
278 while ((f = AST_LIST_REMOVE_HEAD(&dtmf_frames, frame_list))) {
279 ast_queue_frame(chan, f);
280 ast_frfree(f);
283 while (chan_list_state == as_chan_list_state)
284 usleep(1000);
286 return res;
289 void ast_autoservice_init(void)
291 ast_cond_init(&as_cond, NULL);