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
32 ASTERISK_FILE_VERSION(__FILE__
, "$Revision$")
37 #include <sys/socket.h>
42 #include <netinet/in.h>
43 #include <arpa/inet.h>
44 #include <sys/signal.h>
46 #include "asterisk/lock.h"
47 #include "asterisk/channel.h"
48 #include "asterisk/config.h"
49 #include "asterisk/logger.h"
50 #include "asterisk/module.h"
51 #include "asterisk/pbx.h"
52 #include "asterisk/options.h"
53 #include "asterisk/lock.h"
54 #include "asterisk/sched.h"
55 #include "asterisk/io.h"
56 #include "asterisk/rtp.h"
57 #include "asterisk/acl.h"
58 #include "asterisk/callerid.h"
59 #include "asterisk/file.h"
60 #include "asterisk/cli.h"
61 #include "asterisk/app.h"
62 #include "asterisk/musiconhold.h"
63 #include "asterisk/manager.h"
64 #include "asterisk/stringfields.h"
66 static const char tdesc
[] = "Feature Proxy Channel Driver";
68 #define IS_OUTBOUND(a,b) (a == b->chan ? 1 : 0)
71 struct ast_channel
*owner
;
75 int alertpipebackup
[2];
79 ast_mutex_t lock
; /* Channel private lock */
80 char tech
[AST_MAX_EXTENSION
]; /* Technology to abstract */
81 char dest
[AST_MAX_EXTENSION
]; /* Destination to abstract */
82 struct ast_channel
*subchan
;
83 struct feature_sub subs
[3]; /* Subs */
84 struct ast_channel
*owner
; /* Current Master Channel */
85 AST_LIST_ENTRY(feature_pvt
) list
; /* Next entity */
88 static AST_LIST_HEAD_STATIC(features
, feature_pvt
);
90 #define SUB_REAL 0 /* Active call */
91 #define SUB_CALLWAIT 1 /* Call-Waiting call on hold */
92 #define SUB_THREEWAY 2 /* Three-way call */
94 static struct ast_channel
*features_request(const char *type
, int format
, void *data
, int *cause
);
95 static int features_digit_begin(struct ast_channel
*ast
, char digit
);
96 static int features_digit_end(struct ast_channel
*ast
, char digit
, unsigned int duration
);
97 static int features_call(struct ast_channel
*ast
, char *dest
, int timeout
);
98 static int features_hangup(struct ast_channel
*ast
);
99 static int features_answer(struct ast_channel
*ast
);
100 static struct ast_frame
*features_read(struct ast_channel
*ast
);
101 static int features_write(struct ast_channel
*ast
, struct ast_frame
*f
);
102 static int features_indicate(struct ast_channel
*ast
, int condition
, const void *data
, size_t datalen
);
103 static int features_fixup(struct ast_channel
*oldchan
, struct ast_channel
*newchan
);
105 static const struct ast_channel_tech features_tech
= {
107 .description
= tdesc
,
109 .requester
= features_request
,
110 .send_digit_begin
= features_digit_begin
,
111 .send_digit_end
= features_digit_end
,
112 .call
= features_call
,
113 .hangup
= features_hangup
,
114 .answer
= features_answer
,
115 .read
= features_read
,
116 .write
= features_write
,
117 .exception
= features_read
,
118 .indicate
= features_indicate
,
119 .fixup
= features_fixup
,
122 static inline void init_sub(struct feature_sub
*sub
)
126 sub
->timingfdbackup
= -1;
127 sub
->alertpipebackup
[0] = sub
->alertpipebackup
[1] = -1;
130 static inline int indexof(struct feature_pvt
*p
, struct ast_channel
*owner
, int nullok
)
134 ast_log(LOG_WARNING
, "indexof called on NULL owner??\n");
137 for (x
=0; x
<3; x
++) {
138 if (owner
== p
->subs
[x
].owner
)
145 static void wakeup_sub(struct feature_pvt
*p
, int a
)
147 struct ast_frame null
= { AST_FRAME_NULL
, };
149 if (p
->subs
[a
].owner
) {
150 if (ast_mutex_trylock(&p
->subs
[a
].owner
->lock
)) {
151 ast_mutex_unlock(&p
->lock
);
153 ast_mutex_lock(&p
->lock
);
155 ast_queue_frame(p
->subs
[a
].owner
, &null
);
156 ast_mutex_unlock(&p
->subs
[a
].owner
->lock
);
165 static void restore_channel(struct feature_pvt
*p
, int index
)
167 /* Restore timing/alertpipe */
168 p
->subs
[index
].owner
->timingfd
= p
->subs
[index
].timingfdbackup
;
169 p
->subs
[index
].owner
->alertpipe
[0] = p
->subs
[index
].alertpipebackup
[0];
170 p
->subs
[index
].owner
->alertpipe
[1] = p
->subs
[index
].alertpipebackup
[1];
171 p
->subs
[index
].owner
->fds
[AST_ALERT_FD
] = p
->subs
[index
].alertpipebackup
[0];
172 p
->subs
[index
].owner
->fds
[AST_TIMING_FD
] = p
->subs
[index
].timingfdbackup
;
175 static void update_features(struct feature_pvt
*p
, int index
)
178 if (p
->subs
[index
].owner
) {
179 for (x
=0; x
<AST_MAX_FDS
; x
++) {
181 p
->subs
[index
].owner
->fds
[x
] = -1;
183 p
->subs
[index
].owner
->fds
[x
] = p
->subchan
->fds
[x
];
186 /* Copy timings from master channel */
187 p
->subs
[index
].owner
->timingfd
= p
->subchan
->timingfd
;
188 p
->subs
[index
].owner
->alertpipe
[0] = p
->subchan
->alertpipe
[0];
189 p
->subs
[index
].owner
->alertpipe
[1] = p
->subchan
->alertpipe
[1];
190 if (p
->subs
[index
].owner
->nativeformats
!= p
->subchan
->readformat
) {
191 p
->subs
[index
].owner
->nativeformats
= p
->subchan
->readformat
;
192 if (p
->subs
[index
].owner
->readformat
)
193 ast_set_read_format(p
->subs
[index
].owner
, p
->subs
[index
].owner
->readformat
);
194 if (p
->subs
[index
].owner
->writeformat
)
195 ast_set_write_format(p
->subs
[index
].owner
, p
->subs
[index
].owner
->writeformat
);
198 restore_channel(p
, index
);
204 static void swap_subs(struct feature_pvt
*p
, int a
, int b
)
207 struct ast_channel
*towner
;
209 ast_log(LOG_DEBUG
, "Swapping %d and %d\n", a
, b
);
211 towner
= p
->subs
[a
].owner
;
212 tinthreeway
= p
->subs
[a
].inthreeway
;
214 p
->subs
[a
].owner
= p
->subs
[b
].owner
;
215 p
->subs
[a
].inthreeway
= p
->subs
[b
].inthreeway
;
217 p
->subs
[b
].owner
= towner
;
218 p
->subs
[b
].inthreeway
= tinthreeway
;
219 update_features(p
,a
);
220 update_features(p
,b
);
226 static int features_answer(struct ast_channel
*ast
)
228 struct feature_pvt
*p
= ast
->tech_pvt
;
232 ast_mutex_lock(&p
->lock
);
233 x
= indexof(p
, ast
, 0);
234 if (!x
&& p
->subchan
)
235 res
= ast_answer(p
->subchan
);
236 ast_mutex_unlock(&p
->lock
);
240 static struct ast_frame
*features_read(struct ast_channel
*ast
)
242 struct feature_pvt
*p
= ast
->tech_pvt
;
247 ast_mutex_lock(&p
->lock
);
248 x
= indexof(p
, ast
, 0);
249 if (!x
&& p
->subchan
) {
250 update_features(p
, x
);
251 f
= ast_read(p
->subchan
);
253 ast_mutex_unlock(&p
->lock
);
257 static int features_write(struct ast_channel
*ast
, struct ast_frame
*f
)
259 struct feature_pvt
*p
= ast
->tech_pvt
;
263 ast_mutex_lock(&p
->lock
);
264 x
= indexof(p
, ast
, 0);
265 if (!x
&& p
->subchan
)
266 res
= ast_write(p
->subchan
, f
);
267 ast_mutex_unlock(&p
->lock
);
271 static int features_fixup(struct ast_channel
*oldchan
, struct ast_channel
*newchan
)
273 struct feature_pvt
*p
= newchan
->tech_pvt
;
276 ast_mutex_lock(&p
->lock
);
277 if (p
->owner
== oldchan
)
279 for (x
= 0; x
< 3; x
++) {
280 if (p
->subs
[x
].owner
== oldchan
)
281 p
->subs
[x
].owner
= newchan
;
283 ast_mutex_unlock(&p
->lock
);
287 static int features_indicate(struct ast_channel
*ast
, int condition
, const void *data
, size_t datalen
)
289 struct feature_pvt
*p
= ast
->tech_pvt
;
293 /* Queue up a frame representing the indication as a control frame */
294 ast_mutex_lock(&p
->lock
);
295 x
= indexof(p
, ast
, 0);
296 if (!x
&& p
->subchan
)
297 res
= ast_indicate(p
->subchan
, condition
);
298 ast_mutex_unlock(&p
->lock
);
302 static int features_digit_begin(struct ast_channel
*ast
, char digit
)
304 struct feature_pvt
*p
= ast
->tech_pvt
;
308 /* Queue up a frame representing the indication as a control frame */
309 ast_mutex_lock(&p
->lock
);
310 x
= indexof(p
, ast
, 0);
311 if (!x
&& p
->subchan
)
312 res
= ast_senddigit_begin(p
->subchan
, digit
);
313 ast_mutex_unlock(&p
->lock
);
318 static int features_digit_end(struct ast_channel
*ast
, char digit
, unsigned int duration
)
320 struct feature_pvt
*p
= ast
->tech_pvt
;
324 /* Queue up a frame representing the indication as a control frame */
325 ast_mutex_lock(&p
->lock
);
326 x
= indexof(p
, ast
, 0);
327 if (!x
&& p
->subchan
)
328 res
= ast_senddigit_end(p
->subchan
, digit
, duration
);
329 ast_mutex_unlock(&p
->lock
);
333 static int features_call(struct ast_channel
*ast
, char *dest
, int timeout
)
335 struct feature_pvt
*p
= ast
->tech_pvt
;
340 dest2
= strchr(dest
, '/');
342 ast_mutex_lock(&p
->lock
);
343 x
= indexof(p
, ast
, 0);
344 if (!x
&& p
->subchan
) {
345 p
->subchan
->cid
.cid_num
= ast_strdup(p
->owner
->cid
.cid_num
);
346 p
->subchan
->cid
.cid_name
= ast_strdup(p
->owner
->cid
.cid_name
);
347 p
->subchan
->cid
.cid_rdnis
= ast_strdup(p
->owner
->cid
.cid_rdnis
);
348 p
->subchan
->cid
.cid_ani
= ast_strdup(p
->owner
->cid
.cid_ani
);
350 p
->subchan
->cid
.cid_pres
= p
->owner
->cid
.cid_pres
;
351 ast_string_field_set(p
->subchan
, language
, p
->owner
->language
);
352 ast_string_field_set(p
->subchan
, accountcode
, p
->owner
->accountcode
);
353 p
->subchan
->cdrflags
= p
->owner
->cdrflags
;
354 res
= ast_call(p
->subchan
, dest2
, timeout
);
355 update_features(p
, x
);
357 ast_log(LOG_NOTICE
, "Uhm yah, not quite there with the call waiting...\n");
358 ast_mutex_unlock(&p
->lock
);
363 static int features_hangup(struct ast_channel
*ast
)
365 struct feature_pvt
*p
= ast
->tech_pvt
;
368 ast_mutex_lock(&p
->lock
);
369 x
= indexof(p
, ast
, 0);
371 restore_channel(p
, x
);
372 p
->subs
[x
].owner
= NULL
;
373 /* XXX Re-arrange, unconference, etc XXX */
375 ast
->tech_pvt
= NULL
;
377 if (!p
->subs
[SUB_REAL
].owner
&& !p
->subs
[SUB_CALLWAIT
].owner
&& !p
->subs
[SUB_THREEWAY
].owner
) {
378 ast_mutex_unlock(&p
->lock
);
379 /* Remove from list */
380 AST_LIST_LOCK(&features
);
381 AST_LIST_REMOVE(&features
, p
, list
);
382 AST_LIST_UNLOCK(&features
);
383 ast_mutex_lock(&p
->lock
);
386 ast_hangup(p
->subchan
);
387 ast_mutex_unlock(&p
->lock
);
388 ast_mutex_destroy(&p
->lock
);
392 ast_mutex_unlock(&p
->lock
);
396 static struct feature_pvt
*features_alloc(char *data
, int format
)
398 struct feature_pvt
*tmp
;
403 struct ast_channel
*chan
;
405 tech
= ast_strdupa(data
);
407 dest
= strchr(tech
, '/');
413 if (!tech
|| !dest
) {
414 ast_log(LOG_NOTICE
, "Format for feature channel is Feature/Tech/Dest ('%s' not valid)!\n",
418 AST_LIST_LOCK(&features
);
419 AST_LIST_TRAVERSE(&features
, tmp
, list
) {
420 if (!strcasecmp(tmp
->tech
, tech
) && !strcmp(tmp
->dest
, dest
))
423 AST_LIST_UNLOCK(&features
);
425 chan
= ast_request(tech
, format
, dest
, &status
);
427 ast_log(LOG_NOTICE
, "Unable to allocate subchannel '%s/%s'\n", tech
, dest
);
430 tmp
= malloc(sizeof(struct feature_pvt
));
432 memset(tmp
, 0, sizeof(struct feature_pvt
));
434 init_sub(tmp
->subs
+ x
);
435 ast_mutex_init(&tmp
->lock
);
436 ast_copy_string(tmp
->tech
, tech
, sizeof(tmp
->tech
));
437 ast_copy_string(tmp
->dest
, dest
, sizeof(tmp
->dest
));
439 AST_LIST_LOCK(&features
);
440 AST_LIST_INSERT_HEAD(&features
, tmp
, list
);
441 AST_LIST_UNLOCK(&features
);
447 static struct ast_channel
*features_new(struct feature_pvt
*p
, int state
, int index
)
449 struct ast_channel
*tmp
;
453 ast_log(LOG_WARNING
, "Called upon channel with no subchan:(\n");
456 if (p
->subs
[index
].owner
) {
457 ast_log(LOG_WARNING
, "Called to put index %d already there!\n", index
);
460 /* figure out what you want the name to be */
464 b2
= ast_safe_string_alloc("Feature/%s/%s-%d", p
->tech
, p
->dest
, x
);
468 if (p
->subs
[y
].owner
&& !strcasecmp(p
->subs
[y
].owner
->name
, b2
))
474 tmp
= ast_channel_alloc(0, state
, 0,0, b2
);
475 /* free up the name, it was copied into the channel name */
479 ast_log(LOG_WARNING
, "Unable to allocate channel structure\n");
482 tmp
->tech
= &features_tech
;
483 tmp
->writeformat
= p
->subchan
->writeformat
;
484 tmp
->rawwriteformat
= p
->subchan
->rawwriteformat
;
485 tmp
->readformat
= p
->subchan
->readformat
;
486 tmp
->rawreadformat
= p
->subchan
->rawreadformat
;
487 tmp
->nativeformats
= p
->subchan
->readformat
;
489 p
->subs
[index
].owner
= tmp
;
492 ast_module_ref(ast_module_info
->self
);
497 static struct ast_channel
*features_request(const char *type
, int format
, void *data
, int *cause
)
499 struct feature_pvt
*p
;
500 struct ast_channel
*chan
= NULL
;
502 p
= features_alloc(data
, format
);
503 if (p
&& !p
->subs
[SUB_REAL
].owner
)
504 chan
= features_new(p
, AST_STATE_DOWN
, SUB_REAL
);
506 update_features(p
,SUB_REAL
);
510 static int features_show(int fd
, int argc
, char **argv
)
512 struct feature_pvt
*p
;
515 return RESULT_SHOWUSAGE
;
517 if (AST_LIST_EMPTY(&features
)) {
518 ast_cli(fd
, "No feature channels in use\n");
519 return RESULT_SUCCESS
;
522 AST_LIST_LOCK(&features
);
523 AST_LIST_TRAVERSE(&features
, p
, list
) {
524 ast_mutex_lock(&p
->lock
);
525 ast_cli(fd
, "%s -- %s/%s\n", p
->owner
? p
->owner
->name
: "<unowned>", p
->tech
, p
->dest
);
526 ast_mutex_unlock(&p
->lock
);
528 AST_LIST_UNLOCK(&features
);
529 return RESULT_SUCCESS
;
532 static char show_features_usage
[] =
533 "Usage: feature show channels\n"
534 " Provides summary information on feature channels.\n";
536 static struct ast_cli_entry cli_features
[] = {
537 { { "feature", "show", "channels", NULL
},
538 features_show
, "List status of feature channels",
539 show_features_usage
},
542 static int load_module(void)
544 /* Make sure we can register our sip channel type */
545 if (ast_channel_register(&features_tech
)) {
546 ast_log(LOG_ERROR
, "Unable to register channel class 'Feature'\n");
549 ast_cli_register_multiple(cli_features
, sizeof(cli_features
) / sizeof(struct ast_cli_entry
));
553 static int unload_module(void)
555 struct feature_pvt
*p
;
557 /* First, take us out of the channel loop */
558 ast_cli_unregister_multiple(cli_features
, sizeof(cli_features
) / sizeof(struct ast_cli_entry
));
559 ast_channel_unregister(&features_tech
);
561 if (!AST_LIST_LOCK(&features
))
563 /* Hangup all interfaces if they have an owner */
564 AST_LIST_TRAVERSE_SAFE_BEGIN(&features
, p
, list
) {
566 ast_softhangup(p
->owner
, AST_SOFTHANGUP_APPUNLOAD
);
567 AST_LIST_REMOVE_CURRENT(&features
, list
);
570 AST_LIST_TRAVERSE_SAFE_END
571 AST_LIST_UNLOCK(&features
);
576 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY
, "Feature Proxy Channel");