2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2005, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
21 * \brief feature Proxy Channel
23 * \author Mark Spencer <markster@digium.com>
25 * \note *** Experimental code ****
27 * \ingroup channel_drivers
30 <defaultenabled>no</defaultenabled>
35 ASTERISK_FILE_VERSION(__FILE__
, "$Revision$")
40 #include <sys/socket.h>
45 #include <netinet/in.h>
46 #include <arpa/inet.h>
47 #include <sys/signal.h>
49 #include "asterisk/lock.h"
50 #include "asterisk/channel.h"
51 #include "asterisk/config.h"
52 #include "asterisk/logger.h"
53 #include "asterisk/module.h"
54 #include "asterisk/pbx.h"
55 #include "asterisk/options.h"
56 #include "asterisk/lock.h"
57 #include "asterisk/sched.h"
58 #include "asterisk/io.h"
59 #include "asterisk/rtp.h"
60 #include "asterisk/acl.h"
61 #include "asterisk/callerid.h"
62 #include "asterisk/file.h"
63 #include "asterisk/cli.h"
64 #include "asterisk/app.h"
65 #include "asterisk/musiconhold.h"
66 #include "asterisk/manager.h"
67 #include "asterisk/stringfields.h"
69 static const char tdesc
[] = "Feature Proxy Channel Driver";
71 #define IS_OUTBOUND(a,b) (a == b->chan ? 1 : 0)
74 struct ast_channel
*owner
;
78 int alertpipebackup
[2];
82 ast_mutex_t lock
; /* Channel private lock */
83 char tech
[AST_MAX_EXTENSION
]; /* Technology to abstract */
84 char dest
[AST_MAX_EXTENSION
]; /* Destination to abstract */
85 struct ast_channel
*subchan
;
86 struct feature_sub subs
[3]; /* Subs */
87 struct ast_channel
*owner
; /* Current Master Channel */
88 AST_LIST_ENTRY(feature_pvt
) list
; /* Next entity */
91 static AST_LIST_HEAD_STATIC(features
, feature_pvt
);
93 #define SUB_REAL 0 /* Active call */
94 #define SUB_CALLWAIT 1 /* Call-Waiting call on hold */
95 #define SUB_THREEWAY 2 /* Three-way call */
97 static struct ast_channel
*features_request(const char *type
, int format
, void *data
, int *cause
);
98 static int features_digit_begin(struct ast_channel
*ast
, char digit
);
99 static int features_digit_end(struct ast_channel
*ast
, char digit
, unsigned int duration
);
100 static int features_call(struct ast_channel
*ast
, char *dest
, int timeout
);
101 static int features_hangup(struct ast_channel
*ast
);
102 static int features_answer(struct ast_channel
*ast
);
103 static struct ast_frame
*features_read(struct ast_channel
*ast
);
104 static int features_write(struct ast_channel
*ast
, struct ast_frame
*f
);
105 static int features_indicate(struct ast_channel
*ast
, int condition
, const void *data
, size_t datalen
);
106 static int features_fixup(struct ast_channel
*oldchan
, struct ast_channel
*newchan
);
108 static const struct ast_channel_tech features_tech
= {
110 .description
= tdesc
,
112 .requester
= features_request
,
113 .send_digit_begin
= features_digit_begin
,
114 .send_digit_end
= features_digit_end
,
115 .call
= features_call
,
116 .hangup
= features_hangup
,
117 .answer
= features_answer
,
118 .read
= features_read
,
119 .write
= features_write
,
120 .exception
= features_read
,
121 .indicate
= features_indicate
,
122 .fixup
= features_fixup
,
125 static inline void init_sub(struct feature_sub
*sub
)
129 sub
->timingfdbackup
= -1;
130 sub
->alertpipebackup
[0] = sub
->alertpipebackup
[1] = -1;
133 static inline int indexof(struct feature_pvt
*p
, struct ast_channel
*owner
, int nullok
)
137 ast_log(LOG_WARNING
, "indexof called on NULL owner??\n");
140 for (x
=0; x
<3; x
++) {
141 if (owner
== p
->subs
[x
].owner
)
148 static void wakeup_sub(struct feature_pvt
*p
, int a
)
150 struct ast_frame null
= { AST_FRAME_NULL
, };
152 if (p
->subs
[a
].owner
) {
153 if (ast_mutex_trylock(&p
->subs
[a
].owner
->lock
)) {
154 ast_mutex_unlock(&p
->lock
);
156 ast_mutex_lock(&p
->lock
);
158 ast_queue_frame(p
->subs
[a
].owner
, &null
);
159 ast_mutex_unlock(&p
->subs
[a
].owner
->lock
);
168 static void restore_channel(struct feature_pvt
*p
, int index
)
170 /* Restore timing/alertpipe */
171 p
->subs
[index
].owner
->timingfd
= p
->subs
[index
].timingfdbackup
;
172 p
->subs
[index
].owner
->alertpipe
[0] = p
->subs
[index
].alertpipebackup
[0];
173 p
->subs
[index
].owner
->alertpipe
[1] = p
->subs
[index
].alertpipebackup
[1];
174 p
->subs
[index
].owner
->fds
[AST_ALERT_FD
] = p
->subs
[index
].alertpipebackup
[0];
175 p
->subs
[index
].owner
->fds
[AST_TIMING_FD
] = p
->subs
[index
].timingfdbackup
;
178 static void update_features(struct feature_pvt
*p
, int index
)
181 if (p
->subs
[index
].owner
) {
182 for (x
=0; x
<AST_MAX_FDS
; x
++) {
184 p
->subs
[index
].owner
->fds
[x
] = -1;
186 p
->subs
[index
].owner
->fds
[x
] = p
->subchan
->fds
[x
];
189 /* Copy timings from master channel */
190 p
->subs
[index
].owner
->timingfd
= p
->subchan
->timingfd
;
191 p
->subs
[index
].owner
->alertpipe
[0] = p
->subchan
->alertpipe
[0];
192 p
->subs
[index
].owner
->alertpipe
[1] = p
->subchan
->alertpipe
[1];
193 if (p
->subs
[index
].owner
->nativeformats
!= p
->subchan
->readformat
) {
194 p
->subs
[index
].owner
->nativeformats
= p
->subchan
->readformat
;
195 if (p
->subs
[index
].owner
->readformat
)
196 ast_set_read_format(p
->subs
[index
].owner
, p
->subs
[index
].owner
->readformat
);
197 if (p
->subs
[index
].owner
->writeformat
)
198 ast_set_write_format(p
->subs
[index
].owner
, p
->subs
[index
].owner
->writeformat
);
201 restore_channel(p
, index
);
207 static void swap_subs(struct feature_pvt
*p
, int a
, int b
)
210 struct ast_channel
*towner
;
212 ast_log(LOG_DEBUG
, "Swapping %d and %d\n", a
, b
);
214 towner
= p
->subs
[a
].owner
;
215 tinthreeway
= p
->subs
[a
].inthreeway
;
217 p
->subs
[a
].owner
= p
->subs
[b
].owner
;
218 p
->subs
[a
].inthreeway
= p
->subs
[b
].inthreeway
;
220 p
->subs
[b
].owner
= towner
;
221 p
->subs
[b
].inthreeway
= tinthreeway
;
222 update_features(p
,a
);
223 update_features(p
,b
);
229 static int features_answer(struct ast_channel
*ast
)
231 struct feature_pvt
*p
= ast
->tech_pvt
;
235 ast_mutex_lock(&p
->lock
);
236 x
= indexof(p
, ast
, 0);
237 if (!x
&& p
->subchan
)
238 res
= ast_answer(p
->subchan
);
239 ast_mutex_unlock(&p
->lock
);
243 static struct ast_frame
*features_read(struct ast_channel
*ast
)
245 struct feature_pvt
*p
= ast
->tech_pvt
;
250 ast_mutex_lock(&p
->lock
);
251 x
= indexof(p
, ast
, 0);
252 if (!x
&& p
->subchan
) {
253 update_features(p
, x
);
254 f
= ast_read(p
->subchan
);
256 ast_mutex_unlock(&p
->lock
);
260 static int features_write(struct ast_channel
*ast
, struct ast_frame
*f
)
262 struct feature_pvt
*p
= ast
->tech_pvt
;
266 ast_mutex_lock(&p
->lock
);
267 x
= indexof(p
, ast
, 0);
268 if (!x
&& p
->subchan
)
269 res
= ast_write(p
->subchan
, f
);
270 ast_mutex_unlock(&p
->lock
);
274 static int features_fixup(struct ast_channel
*oldchan
, struct ast_channel
*newchan
)
276 struct feature_pvt
*p
= newchan
->tech_pvt
;
279 ast_mutex_lock(&p
->lock
);
280 if (p
->owner
== oldchan
)
282 for (x
= 0; x
< 3; x
++) {
283 if (p
->subs
[x
].owner
== oldchan
)
284 p
->subs
[x
].owner
= newchan
;
286 ast_mutex_unlock(&p
->lock
);
290 static int features_indicate(struct ast_channel
*ast
, int condition
, const void *data
, size_t datalen
)
292 struct feature_pvt
*p
= ast
->tech_pvt
;
296 /* Queue up a frame representing the indication as a control frame */
297 ast_mutex_lock(&p
->lock
);
298 x
= indexof(p
, ast
, 0);
299 if (!x
&& p
->subchan
)
300 res
= ast_indicate(p
->subchan
, condition
);
301 ast_mutex_unlock(&p
->lock
);
305 static int features_digit_begin(struct ast_channel
*ast
, char digit
)
307 struct feature_pvt
*p
= ast
->tech_pvt
;
311 /* Queue up a frame representing the indication as a control frame */
312 ast_mutex_lock(&p
->lock
);
313 x
= indexof(p
, ast
, 0);
314 if (!x
&& p
->subchan
)
315 res
= ast_senddigit_begin(p
->subchan
, digit
);
316 ast_mutex_unlock(&p
->lock
);
321 static int features_digit_end(struct ast_channel
*ast
, char digit
, unsigned int duration
)
323 struct feature_pvt
*p
= ast
->tech_pvt
;
327 /* Queue up a frame representing the indication as a control frame */
328 ast_mutex_lock(&p
->lock
);
329 x
= indexof(p
, ast
, 0);
330 if (!x
&& p
->subchan
)
331 res
= ast_senddigit_end(p
->subchan
, digit
, duration
);
332 ast_mutex_unlock(&p
->lock
);
336 static int features_call(struct ast_channel
*ast
, char *dest
, int timeout
)
338 struct feature_pvt
*p
= ast
->tech_pvt
;
343 dest2
= strchr(dest
, '/');
345 ast_mutex_lock(&p
->lock
);
346 x
= indexof(p
, ast
, 0);
347 if (!x
&& p
->subchan
) {
348 p
->subchan
->cid
.cid_num
= ast_strdup(p
->owner
->cid
.cid_num
);
349 p
->subchan
->cid
.cid_name
= ast_strdup(p
->owner
->cid
.cid_name
);
350 p
->subchan
->cid
.cid_rdnis
= ast_strdup(p
->owner
->cid
.cid_rdnis
);
351 p
->subchan
->cid
.cid_ani
= ast_strdup(p
->owner
->cid
.cid_ani
);
353 p
->subchan
->cid
.cid_pres
= p
->owner
->cid
.cid_pres
;
354 ast_string_field_set(p
->subchan
, language
, p
->owner
->language
);
355 ast_string_field_set(p
->subchan
, accountcode
, p
->owner
->accountcode
);
356 p
->subchan
->cdrflags
= p
->owner
->cdrflags
;
357 res
= ast_call(p
->subchan
, dest2
, timeout
);
358 update_features(p
, x
);
360 ast_log(LOG_NOTICE
, "Uhm yah, not quite there with the call waiting...\n");
361 ast_mutex_unlock(&p
->lock
);
366 static int features_hangup(struct ast_channel
*ast
)
368 struct feature_pvt
*p
= ast
->tech_pvt
;
371 ast_mutex_lock(&p
->lock
);
372 x
= indexof(p
, ast
, 0);
374 restore_channel(p
, x
);
375 p
->subs
[x
].owner
= NULL
;
376 /* XXX Re-arrange, unconference, etc XXX */
378 ast
->tech_pvt
= NULL
;
380 if (!p
->subs
[SUB_REAL
].owner
&& !p
->subs
[SUB_CALLWAIT
].owner
&& !p
->subs
[SUB_THREEWAY
].owner
) {
381 ast_mutex_unlock(&p
->lock
);
382 /* Remove from list */
383 AST_LIST_LOCK(&features
);
384 AST_LIST_REMOVE(&features
, p
, list
);
385 AST_LIST_UNLOCK(&features
);
386 ast_mutex_lock(&p
->lock
);
389 ast_hangup(p
->subchan
);
390 ast_mutex_unlock(&p
->lock
);
391 ast_mutex_destroy(&p
->lock
);
395 ast_mutex_unlock(&p
->lock
);
399 static struct feature_pvt
*features_alloc(char *data
, int format
)
401 struct feature_pvt
*tmp
;
406 struct ast_channel
*chan
;
408 tech
= ast_strdupa(data
);
410 dest
= strchr(tech
, '/');
416 if (!tech
|| !dest
) {
417 ast_log(LOG_NOTICE
, "Format for feature channel is Feature/Tech/Dest ('%s' not valid)!\n",
421 AST_LIST_LOCK(&features
);
422 AST_LIST_TRAVERSE(&features
, tmp
, list
) {
423 if (!strcasecmp(tmp
->tech
, tech
) && !strcmp(tmp
->dest
, dest
))
426 AST_LIST_UNLOCK(&features
);
428 chan
= ast_request(tech
, format
, dest
, &status
);
430 ast_log(LOG_NOTICE
, "Unable to allocate subchannel '%s/%s'\n", tech
, dest
);
433 tmp
= malloc(sizeof(struct feature_pvt
));
435 memset(tmp
, 0, sizeof(struct feature_pvt
));
437 init_sub(tmp
->subs
+ x
);
438 ast_mutex_init(&tmp
->lock
);
439 ast_copy_string(tmp
->tech
, tech
, sizeof(tmp
->tech
));
440 ast_copy_string(tmp
->dest
, dest
, sizeof(tmp
->dest
));
442 AST_LIST_LOCK(&features
);
443 AST_LIST_INSERT_HEAD(&features
, tmp
, list
);
444 AST_LIST_UNLOCK(&features
);
450 static struct ast_channel
*features_new(struct feature_pvt
*p
, int state
, int index
)
452 struct ast_channel
*tmp
;
456 ast_log(LOG_WARNING
, "Called upon channel with no subchan:(\n");
459 if (p
->subs
[index
].owner
) {
460 ast_log(LOG_WARNING
, "Called to put index %d already there!\n", index
);
463 /* figure out what you want the name to be */
467 b2
= ast_safe_string_alloc("%s/%s-%d", p
->tech
, p
->dest
, x
);
471 if (p
->subs
[y
].owner
&& !strcasecmp(p
->subs
[y
].owner
->name
, b2
))
477 tmp
= ast_channel_alloc(0, state
, 0,0, "", "", "", 0, "Feature/%s", b2
);
478 /* free up the name, it was copied into the channel name */
482 ast_log(LOG_WARNING
, "Unable to allocate channel structure\n");
485 tmp
->tech
= &features_tech
;
486 tmp
->writeformat
= p
->subchan
->writeformat
;
487 tmp
->rawwriteformat
= p
->subchan
->rawwriteformat
;
488 tmp
->readformat
= p
->subchan
->readformat
;
489 tmp
->rawreadformat
= p
->subchan
->rawreadformat
;
490 tmp
->nativeformats
= p
->subchan
->readformat
;
492 p
->subs
[index
].owner
= tmp
;
495 ast_module_ref(ast_module_info
->self
);
500 static struct ast_channel
*features_request(const char *type
, int format
, void *data
, int *cause
)
502 struct feature_pvt
*p
;
503 struct ast_channel
*chan
= NULL
;
505 p
= features_alloc(data
, format
);
506 if (p
&& !p
->subs
[SUB_REAL
].owner
)
507 chan
= features_new(p
, AST_STATE_DOWN
, SUB_REAL
);
509 update_features(p
,SUB_REAL
);
513 static int features_show(int fd
, int argc
, char **argv
)
515 struct feature_pvt
*p
;
518 return RESULT_SHOWUSAGE
;
520 if (AST_LIST_EMPTY(&features
)) {
521 ast_cli(fd
, "No feature channels in use\n");
522 return RESULT_SUCCESS
;
525 AST_LIST_LOCK(&features
);
526 AST_LIST_TRAVERSE(&features
, p
, list
) {
527 ast_mutex_lock(&p
->lock
);
528 ast_cli(fd
, "%s -- %s/%s\n", p
->owner
? p
->owner
->name
: "<unowned>", p
->tech
, p
->dest
);
529 ast_mutex_unlock(&p
->lock
);
531 AST_LIST_UNLOCK(&features
);
532 return RESULT_SUCCESS
;
535 static char show_features_usage
[] =
536 "Usage: feature show channels\n"
537 " Provides summary information on feature channels.\n";
539 static struct ast_cli_entry cli_features
[] = {
540 { { "feature", "show", "channels", NULL
},
541 features_show
, "List status of feature channels",
542 show_features_usage
},
545 static int load_module(void)
547 /* Make sure we can register our sip channel type */
548 if (ast_channel_register(&features_tech
)) {
549 ast_log(LOG_ERROR
, "Unable to register channel class 'Feature'\n");
552 ast_cli_register_multiple(cli_features
, sizeof(cli_features
) / sizeof(struct ast_cli_entry
));
556 static int unload_module(void)
558 struct feature_pvt
*p
;
560 /* First, take us out of the channel loop */
561 ast_cli_unregister_multiple(cli_features
, sizeof(cli_features
) / sizeof(struct ast_cli_entry
));
562 ast_channel_unregister(&features_tech
);
564 if (!AST_LIST_LOCK(&features
))
566 /* Hangup all interfaces if they have an owner */
567 AST_LIST_TRAVERSE_SAFE_BEGIN(&features
, p
, list
) {
569 ast_softhangup(p
->owner
, AST_SOFTHANGUP_APPUNLOAD
);
570 AST_LIST_REMOVE_CURRENT(&features
, list
);
573 AST_LIST_TRAVERSE_SAFE_END
574 AST_LIST_UNLOCK(&features
);
579 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY
, "Feature Proxy Channel");