2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 2013 Digium, Inc.
6 * Richard Mudgett <rmudgett@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 Local proxy channel driver.
23 * \author Richard Mudgett <rmudgett@digium.com>
26 * \arg \ref AstCREDITS
30 <support_level>core</support_level>
36 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
38 /* ------------------------------------------------------------------- */
40 #include "asterisk/channel.h"
41 #include "asterisk/pbx.h"
42 #include "asterisk/cli.h"
43 #include "asterisk/manager.h"
44 #include "asterisk/devicestate.h"
45 #include "asterisk/astobj2.h"
46 #include "asterisk/bridge.h"
47 #include "asterisk/core_unreal.h"
48 #include "asterisk/core_local.h"
49 #include "asterisk/stasis.h"
50 #include "asterisk/stasis_channels.h"
51 #include "asterisk/_private.h"
52 #include "asterisk/stasis_channels.h"
55 <manager name="LocalOptimizeAway" language="en_US">
57 Optimize away a local channel when possible.
60 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
61 <parameter name="Channel" required="true">
62 <para>The channel name to optimize away.</para>
66 <para>A local channel created with "/n" will not automatically optimize away.
67 Calling this command on the local channel will clear that flag and allow
68 it to optimize away if it's bridged or when it becomes bridged.</para>
71 <managerEvent language="en_US" name="LocalBridge">
72 <managerEventInstance class="EVENT_FLAG_CALL">
73 <synopsis>Raised when two halves of a Local Channel form a bridge.</synopsis>
75 <channel_snapshot prefix="LocalOne"/>
76 <channel_snapshot prefix="LocalTwo"/>
77 <parameter name="Context">
78 <para>The context in the dialplan that Channel2 starts in.</para>
80 <parameter name="Exten">
81 <para>The extension in the dialplan that Channel2 starts in.</para>
83 <parameter name="LocalOptimization">
90 </managerEventInstance>
92 <managerEvent language="en_US" name="LocalOptimizationBegin">
93 <managerEventInstance class="EVENT_FLAG_CALL">
94 <synopsis>Raised when two halves of a Local Channel begin to optimize
95 themselves out of the media path.</synopsis>
97 <channel_snapshot prefix="LocalOne"/>
98 <channel_snapshot prefix="LocalTwo"/>
99 <channel_snapshot prefix="Source"/>
100 <parameter name="DestUniqueId">
101 <para>The unique ID of the bridge into which the local channel is optimizing.</para>
103 <parameter name="Id">
104 <para>Identification for the optimization operation.</para>
108 <ref type="managerEvent">LocalOptimizationEnd</ref>
109 <ref type="manager">LocalOptimizeAway</ref>
111 </managerEventInstance>
113 <managerEvent language="en_US" name="LocalOptimizationEnd">
114 <managerEventInstance class="EVENT_FLAG_CALL">
115 <synopsis>Raised when two halves of a Local Channel have finished optimizing
116 themselves out of the media path.</synopsis>
118 <channel_snapshot prefix="LocalOne"/>
119 <channel_snapshot prefix="LocalTwo"/>
120 <parameter name="Success">
121 <para>Indicates whether the local optimization succeeded.</para>
123 <parameter name="Id">
124 <para>Identification for the optimization operation. Matches the <replaceable>Id</replaceable>
125 from a previous <literal>LocalOptimizationBegin</literal></para>
129 <ref type="managerEvent">LocalOptimizationBegin</ref>
130 <ref type="manager">LocalOptimizeAway</ref>
132 </managerEventInstance>
136 static const char tdesc[] = "Local Proxy Channel Driver";
138 static struct ao2_container *locals;
140 static struct ast_channel *local_request(const char *type, struct ast_format_cap *cap, const struct ast_channel *requestor, const char *data, int *cause);
141 static int local_call(struct ast_channel *ast, const char *dest, int timeout);
142 static int local_hangup(struct ast_channel *ast);
143 static int local_devicestate(const char *data);
144 static void local_optimization_started_cb(struct ast_unreal_pvt *base, struct ast_channel *source,
145 enum ast_unreal_channel_indicator dest, unsigned int id);
146 static void local_optimization_finished_cb(struct ast_unreal_pvt *base, int success, unsigned int id);
148 static struct ast_manager_event_blob *local_message_to_ami(struct stasis_message *msg);
151 * @{ \brief Define local channel message types.
153 STASIS_MESSAGE_TYPE_DEFN(ast_local_bridge_type,
154 .to_ami = local_message_to_ami,
156 STASIS_MESSAGE_TYPE_DEFN(ast_local_optimization_begin_type,
157 .to_ami = local_message_to_ami,
159 STASIS_MESSAGE_TYPE_DEFN(ast_local_optimization_end_type,
160 .to_ami = local_message_to_ami,
164 /*! \brief Callbacks from the unreal core when channel optimization occurs */
165 struct ast_unreal_pvt_callbacks local_unreal_callbacks = {
166 .optimization_started = local_optimization_started_cb,
167 .optimization_finished = local_optimization_finished_cb,
170 /* PBX interface structure for channel registration */
171 static struct ast_channel_tech local_tech = {
173 .description = tdesc,
174 .requester = local_request,
175 .send_digit_begin = ast_unreal_digit_begin,
176 .send_digit_end = ast_unreal_digit_end,
178 .hangup = local_hangup,
179 .answer = ast_unreal_answer,
180 .read = ast_unreal_read,
181 .write = ast_unreal_write,
182 .write_video = ast_unreal_write,
183 .exception = ast_unreal_read,
184 .indicate = ast_unreal_indicate,
185 .fixup = ast_unreal_fixup,
186 .send_html = ast_unreal_sendhtml,
187 .send_text = ast_unreal_sendtext,
188 .devicestate = local_devicestate,
189 .queryoption = ast_unreal_queryoption,
190 .setoption = ast_unreal_setoption,
193 /*! What to do with the ;2 channel when ast_call() happens. */
194 enum local_call_action {
195 /* The ast_call() will run dialplan on the ;2 channel. */
196 LOCAL_CALL_ACTION_DIALPLAN,
197 /* The ast_call() will impart the ;2 channel into a bridge. */
198 LOCAL_CALL_ACTION_BRIDGE,
199 /* The ast_call() will masquerade the ;2 channel into a channel. */
200 LOCAL_CALL_ACTION_MASQUERADE,
203 /*! Join a bridge on ast_call() parameters. */
204 struct local_bridge {
205 /*! Bridge to join. */
206 struct ast_bridge *join;
207 /*! Channel to swap with when joining bridge. */
208 struct ast_channel *swap;
209 /*! Features that are specific to this channel when pushed into the bridge. */
210 struct ast_bridge_features *features;
214 * \brief the local pvt structure for all channels
216 * The local channel pvt has two ast_chan objects - the "owner" and the "next channel", the outbound channel
218 * ast_chan owner -> local_pvt -> ast_chan chan
221 /*! Unreal channel driver base class values. */
222 struct ast_unreal_pvt base;
223 /*! Additional action arguments */
225 /*! Make ;2 join a bridge on ast_call(). */
226 struct local_bridge bridge;
227 /*! Make ;2 masquerade into this channel on ast_call(). */
228 struct ast_channel *masq;
230 /*! What to do with the ;2 channel on ast_call(). */
231 enum local_call_action type;
232 /*! Context to call */
233 char context[AST_MAX_CONTEXT];
234 /*! Extension to call */
235 char exten[AST_MAX_EXTENSION];
238 struct ast_channel *ast_local_get_peer(struct ast_channel *ast)
240 struct local_pvt *p = ast_channel_tech_pvt(ast);
241 struct local_pvt *found;
242 struct ast_channel *peer;
248 found = p ? ao2_find(locals, p, 0) : NULL;
250 /* ast is either not a local channel or it has alredy been hungup */
254 if (ast == p->base.owner) {
256 } else if (ast == p->base.chan) {
257 peer = p->base.owner;
262 ast_channel_ref(peer);
269 /*! \brief Adds devicestate to local channels */
270 static int local_devicestate(const char *data)
273 int res = AST_DEVICE_INVALID;
274 char *exten = ast_strdupa(data);
277 struct local_pvt *lp;
278 struct ao2_iterator it;
280 /* Strip options if they exist */
281 opts = strchr(exten, '/');
286 context = strchr(exten, '@');
289 "Someone used Local/%s somewhere without a @context. This is bad.\n", data);
290 return AST_DEVICE_INVALID;
294 it = ao2_iterator_init(locals, 0);
295 for (; (lp = ao2_iterator_next(&it)); ao2_ref(lp, -1)) {
297 if (!strcmp(exten, lp->exten)
298 && !strcmp(context, lp->context)) {
299 res = AST_DEVICE_NOT_INUSE;
301 && ast_test_flag(&lp->base, AST_UNREAL_CARETAKER_THREAD)) {
307 res = AST_DEVICE_INUSE;
312 ao2_iterator_destroy(&it);
314 if (res == AST_DEVICE_INVALID) {
315 ast_debug(3, "Checking if extension %s@%s exists (devicestate)\n", exten, context);
316 if (ast_exists_extension(NULL, context, exten, 1, NULL)) {
317 res = AST_DEVICE_NOT_INUSE;
324 static struct ast_multi_channel_blob *local_channel_optimization_blob(struct local_pvt *p,
325 struct ast_json *json_object)
327 struct ast_multi_channel_blob *payload;
328 RAII_VAR(struct ast_channel_snapshot *, local_one_snapshot, NULL, ao2_cleanup);
329 RAII_VAR(struct ast_channel_snapshot *, local_two_snapshot, NULL, ao2_cleanup);
331 local_one_snapshot = ast_channel_snapshot_create(p->base.owner);
332 if (!local_one_snapshot) {
336 local_two_snapshot = ast_channel_snapshot_create(p->base.chan);
337 if (!local_two_snapshot) {
341 payload = ast_multi_channel_blob_create(json_object);
345 ast_multi_channel_blob_add_channel(payload, "1", local_one_snapshot);
346 ast_multi_channel_blob_add_channel(payload, "2", local_two_snapshot);
351 /*! \brief Callback for \ref ast_unreal_pvt_callbacks \ref optimization_started_cb */
352 static void local_optimization_started_cb(struct ast_unreal_pvt *base, struct ast_channel *source,
353 enum ast_unreal_channel_indicator dest, unsigned int id)
355 RAII_VAR(struct ast_json *, json_object, ast_json_null(), ast_json_unref);
356 RAII_VAR(struct ast_multi_channel_blob *, payload, NULL, ao2_cleanup);
357 RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
358 struct local_pvt *p = (struct local_pvt *)base;
360 json_object = ast_json_pack("{s: i, s: i}",
361 "dest", dest, "id", id);
367 payload = local_channel_optimization_blob(p, json_object);
373 RAII_VAR(struct ast_channel_snapshot *, source_snapshot, NULL, ao2_cleanup);
374 source_snapshot = ast_channel_snapshot_create(source);
375 if (!source_snapshot) {
379 ast_multi_channel_blob_add_channel(payload, "source", source_snapshot);
382 msg = stasis_message_create(ast_local_optimization_begin_type(), payload);
387 stasis_publish(ast_channel_topic(p->base.owner), msg);
390 /*! \brief Callback for \ref ast_unreal_pvt_callbacks \ref optimization_finished_cb */
391 static void local_optimization_finished_cb(struct ast_unreal_pvt *base, int success, unsigned int id)
393 RAII_VAR(struct ast_json *, json_object, ast_json_null(), ast_json_unref);
394 RAII_VAR(struct ast_multi_channel_blob *, payload, NULL, ao2_cleanup);
395 RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
396 struct local_pvt *p = (struct local_pvt *)base;
398 json_object = ast_json_pack("{s: i, s: i}", "success", success, "id", id);
404 payload = local_channel_optimization_blob(p, json_object);
409 msg = stasis_message_create(ast_local_optimization_end_type(), payload);
414 stasis_publish(ast_channel_topic(p->base.owner), msg);
417 static struct ast_manager_event_blob *local_message_to_ami(struct stasis_message *message)
419 struct ast_multi_channel_blob *obj = stasis_message_data(message);
420 struct ast_json *blob = ast_multi_channel_blob_get_json(obj);
421 struct ast_channel_snapshot *local_snapshot_one;
422 struct ast_channel_snapshot *local_snapshot_two;
423 RAII_VAR(struct ast_str *, local_channel_one, NULL, ast_free);
424 RAII_VAR(struct ast_str *, local_channel_two, NULL, ast_free);
425 RAII_VAR(struct ast_str *, event_buffer, NULL, ast_free);
428 local_snapshot_one = ast_multi_channel_blob_get_channel(obj, "1");
429 local_snapshot_two = ast_multi_channel_blob_get_channel(obj, "2");
430 if (!local_snapshot_one || !local_snapshot_two) {
434 event_buffer = ast_str_create(1024);
435 local_channel_one = ast_manager_build_channel_state_string_prefix(local_snapshot_one, "LocalOne");
436 local_channel_two = ast_manager_build_channel_state_string_prefix(local_snapshot_two, "LocalTwo");
437 if (!event_buffer || !local_channel_one || !local_channel_two) {
441 if (stasis_message_type(message) == ast_local_optimization_begin_type()) {
442 struct ast_channel_snapshot *source_snapshot;
443 RAII_VAR(struct ast_str *, source_str, NULL, ast_free);
444 const char *dest_uniqueid;
446 source_snapshot = ast_multi_channel_blob_get_channel(obj, "source");
447 if (source_snapshot) {
448 source_str = ast_manager_build_channel_state_string_prefix(source_snapshot, "Source");
454 dest_uniqueid = ast_json_object_get(blob, "dest") == AST_UNREAL_OWNER ?
455 local_snapshot_one->uniqueid : local_snapshot_two->uniqueid;
457 event = "LocalOptimizationBegin";
459 ast_str_append(&event_buffer, 0, "%s", ast_str_buffer(source_str));
461 ast_str_append(&event_buffer, 0, "DestUniqueId: %s\r\n", dest_uniqueid);
462 ast_str_append(&event_buffer, 0, "Id: %u\r\n", (unsigned int) ast_json_integer_get(ast_json_object_get(blob, "id")));
463 } else if (stasis_message_type(message) == ast_local_optimization_end_type()) {
464 event = "LocalOptimizationEnd";
465 ast_str_append(&event_buffer, 0, "Success: %s\r\n", ast_json_integer_get(ast_json_object_get(blob, "success")) ? "Yes" : "No");
466 ast_str_append(&event_buffer, 0, "Id: %u\r\n", (unsigned int) ast_json_integer_get(ast_json_object_get(blob, "id")));
467 } else if (stasis_message_type(message) == ast_local_bridge_type()) {
468 event = "LocalBridge";
469 ast_str_append(&event_buffer, 0, "Context: %s\r\n", ast_json_string_get(ast_json_object_get(blob, "context")));
470 ast_str_append(&event_buffer, 0, "Exten: %s\r\n", ast_json_string_get(ast_json_object_get(blob, "exten")));
471 ast_str_append(&event_buffer, 0, "LocalOptimization: %s\r\n", ast_json_is_true(ast_json_object_get(blob, "can_optimize")) ? "Yes" : "No");
476 return ast_manager_event_blob_create(EVENT_FLAG_CALL, event,
480 ast_str_buffer(local_channel_one),
481 ast_str_buffer(local_channel_two),
482 ast_str_buffer(event_buffer));
487 * \brief Post the \ref ast_local_bridge_type \ref stasis message
490 * \param p local_pvt to raise the local bridge message
494 static void publish_local_bridge_message(struct local_pvt *p)
496 RAII_VAR(struct ast_multi_channel_blob *, multi_blob, NULL, ao2_cleanup);
497 RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
498 RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
499 RAII_VAR(struct ast_channel_snapshot *, one_snapshot, NULL, ao2_cleanup);
500 RAII_VAR(struct ast_channel_snapshot *, two_snapshot, NULL, ao2_cleanup);
501 SCOPED_AO2LOCK(lock, p);
503 blob = ast_json_pack("{s: s, s: s, s: b}",
504 "context", p->context,
506 "can_optimize", !ast_test_flag(&p->base, AST_UNREAL_NO_OPTIMIZATION));
511 multi_blob = ast_multi_channel_blob_create(blob);
516 one_snapshot = ast_channel_snapshot_create(p->base.owner);
521 two_snapshot = ast_channel_snapshot_create(p->base.chan);
526 ast_multi_channel_blob_add_channel(multi_blob, "1", one_snapshot);
527 ast_multi_channel_blob_add_channel(multi_blob, "2", two_snapshot);
529 msg = stasis_message_create(ast_local_bridge_type(), multi_blob);
534 stasis_publish(ast_channel_topic(p->base.owner), msg);
537 int ast_local_setup_bridge(struct ast_channel *ast, struct ast_bridge *bridge, struct ast_channel *swap, struct ast_bridge_features *features)
540 struct local_pvt *found;
544 if (!ast || !bridge) {
545 ast_bridge_features_destroy(features);
549 ast_channel_lock(ast);
550 p = ast_channel_tech_pvt(ast);
551 ast_channel_unlock(ast);
553 found = p ? ao2_find(locals, p, 0) : NULL;
556 if (found->type == LOCAL_CALL_ACTION_DIALPLAN
559 && !ast_test_flag(&found->base, AST_UNREAL_CARETAKER_THREAD)) {
562 ast_channel_ref(swap);
564 found->type = LOCAL_CALL_ACTION_BRIDGE;
565 found->action.bridge.join = bridge;
566 found->action.bridge.swap = swap;
567 found->action.bridge.features = features;
570 ast_bridge_features_destroy(features);
579 int ast_local_setup_masquerade(struct ast_channel *ast, struct ast_channel *masq)
582 struct local_pvt *found;
590 ast_channel_lock(ast);
591 p = ast_channel_tech_pvt(ast);
592 ast_channel_unlock(ast);
594 found = p ? ao2_find(locals, p, 0) : NULL;
597 if (found->type == LOCAL_CALL_ACTION_DIALPLAN
600 && !ast_test_flag(&found->base, AST_UNREAL_CARETAKER_THREAD)) {
601 ast_channel_ref(masq);
602 found->type = LOCAL_CALL_ACTION_MASQUERADE;
603 found->action.masq = masq;
613 /*! \brief Initiate new call, part of PBX interface
614 * dest is the dial string */
615 static int local_call(struct ast_channel *ast, const char *dest, int timeout)
617 struct local_pvt *p = ast_channel_tech_pvt(ast);
620 struct ast_channel *owner = NULL;
621 struct ast_channel *chan = NULL;
623 char *reduced_dest = ast_strdupa(dest);
625 const char *chan_cid;
631 /* since we are letting go of channel locks that were locked coming into
632 * this function, then we need to give the tech pvt a ref */
634 ast_channel_unlock(ast);
636 ast_unreal_lock_all(&p->base, &chan, &owner);
644 if (!owner || !chan) {
649 ast_unreal_call_setup(owner, chan);
652 * If the local channel has /n on the end of it, we need to lop
653 * that off for our argument to setting up the CC_INTERFACES
656 if ((slash = strrchr(reduced_dest, '/'))) {
659 ast_set_cc_interfaces_chanvar(chan, reduced_dest);
664 ast_channel_unlock(owner);
666 chan_cid = S_COR(ast_channel_caller(chan)->id.number.valid,
667 ast_channel_caller(chan)->id.number.str, NULL);
669 chan_cid = ast_strdupa(chan_cid);
671 ast_channel_unlock(chan);
675 case LOCAL_CALL_ACTION_DIALPLAN:
676 if (!ast_exists_extension(NULL, p->context, p->exten, 1, chan_cid)) {
677 ast_log(LOG_NOTICE, "No such extension/context %s@%s while calling Local channel\n",
678 p->exten, p->context);
680 publish_local_bridge_message(p);
682 /* Start switch on sub channel */
683 res = ast_pbx_start(chan);
686 case LOCAL_CALL_ACTION_BRIDGE:
687 publish_local_bridge_message(p);
689 res = ast_bridge_impart(p->action.bridge.join, chan, p->action.bridge.swap,
690 p->action.bridge.features, 1);
691 ao2_ref(p->action.bridge.join, -1);
692 p->action.bridge.join = NULL;
693 ao2_cleanup(p->action.bridge.swap);
694 p->action.bridge.swap = NULL;
695 p->action.bridge.features = NULL;
697 case LOCAL_CALL_ACTION_MASQUERADE:
698 publish_local_bridge_message(p);
700 res = ast_channel_move(p->action.masq, chan);
702 /* Chan is now an orphaned zombie. Destroy it. */
705 p->action.masq = ast_channel_unref(p->action.masq);
710 ast_set_flag(&p->base, AST_UNREAL_CARETAKER_THREAD);
714 /* we already unlocked them, clear them here so the cleanup label won't touch them. */
715 owner = ast_channel_unref(owner);
716 chan = ast_channel_unref(chan);
726 ast_channel_unlock(chan);
727 ast_channel_unref(chan);
731 * owner is supposed to be == to ast, if it is, don't unlock it
732 * because ast must exit locked
736 ast_channel_unlock(owner);
737 ast_channel_lock(ast);
739 ast_channel_unref(owner);
741 /* we have to exit with ast locked */
742 ast_channel_lock(ast);
748 /*! \brief Hangup a call through the local proxy channel */
749 static int local_hangup(struct ast_channel *ast)
751 struct local_pvt *p = ast_channel_tech_pvt(ast);
758 /* give the pvt a ref to fulfill calling requirements. */
760 res = ast_unreal_hangup(&p->base, ast);
765 unlink = !p->base.owner && !p->base.chan;
768 ao2_unlink(locals, p);
778 * \brief struct local_pvt destructor.
780 * \param vdoomed Object to destroy.
784 static void local_pvt_destructor(void *vdoomed)
786 struct local_pvt *doomed = vdoomed;
788 switch (doomed->type) {
789 case LOCAL_CALL_ACTION_DIALPLAN:
791 case LOCAL_CALL_ACTION_BRIDGE:
792 ao2_cleanup(doomed->action.bridge.join);
793 ao2_cleanup(doomed->action.bridge.swap);
794 ast_bridge_features_destroy(doomed->action.bridge.features);
796 case LOCAL_CALL_ACTION_MASQUERADE:
797 ao2_cleanup(doomed->action.masq);
800 ast_unreal_destructor(&doomed->base);
803 /*! \brief Create a call structure */
804 static struct local_pvt *local_alloc(const char *data, struct ast_format_cap *cap)
806 struct local_pvt *pvt;
811 pvt = (struct local_pvt *) ast_unreal_alloc(sizeof(*pvt), local_pvt_destructor, cap);
815 pvt->base.callbacks = &local_unreal_callbacks;
817 parse = ast_strdupa(data);
820 * Local channels intercept MOH by default.
822 * This is a silly default because it represents state held by
823 * the local channels. Unless local channel optimization is
824 * disabled, the state will dissapear when the local channels
827 ast_set_flag(&pvt->base, AST_UNREAL_MOH_INTERCEPT);
829 /* Look for options */
830 if ((opts = strchr(parse, '/'))) {
832 if (strchr(opts, 'n')) {
833 ast_set_flag(&pvt->base, AST_UNREAL_NO_OPTIMIZATION);
835 if (strchr(opts, 'j')) {
836 if (ast_test_flag(&pvt->base, AST_UNREAL_NO_OPTIMIZATION)) {
837 ast_set_flag(&pvt->base.jb_conf, AST_JB_ENABLED);
839 ast_log(LOG_ERROR, "You must use the 'n' option with the 'j' option to enable the jitter buffer\n");
842 if (strchr(opts, 'm')) {
843 ast_clear_flag(&pvt->base, AST_UNREAL_MOH_INTERCEPT);
847 /* Look for a context */
848 if ((context = strchr(parse, '@'))) {
852 ast_copy_string(pvt->context, S_OR(context, "default"), sizeof(pvt->context));
853 ast_copy_string(pvt->exten, parse, sizeof(pvt->exten));
854 snprintf(pvt->base.name, sizeof(pvt->base.name), "%s@%s", pvt->exten, pvt->context);
856 return pvt; /* this is returned with a ref */
859 /*! \brief Part of PBX interface */
860 static struct ast_channel *local_request(const char *type, struct ast_format_cap *cap, const struct ast_channel *requestor, const char *data, int *cause)
863 struct ast_channel *chan;
864 struct ast_callid *callid;
866 /* Allocate a new private structure and then Asterisk channels */
867 p = local_alloc(data, cap);
871 callid = ast_read_threadstorage_callid();
872 chan = ast_unreal_new_channels(&p->base, &local_tech, AST_STATE_DOWN, AST_STATE_RING,
873 p->exten, p->context, requestor, callid);
878 ast_callid_unref(callid);
880 ao2_ref(p, -1); /* kill the ref from the alloc */
885 /*! \brief CLI command "local show channels" */
886 static char *locals_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
889 struct ao2_iterator it;
893 e->command = "local show channels";
895 "Usage: local show channels\n"
896 " Provides summary information on active local proxy channels.\n";
903 return CLI_SHOWUSAGE;
906 if (ao2_container_count(locals) == 0) {
907 ast_cli(a->fd, "No local channels in use\n");
908 return RESULT_SUCCESS;
911 it = ao2_iterator_init(locals, 0);
912 while ((p = ao2_iterator_next(&it))) {
914 ast_cli(a->fd, "%s -- %s\n",
915 p->base.owner ? ast_channel_name(p->base.owner) : "<unowned>",
920 ao2_iterator_destroy(&it);
925 static struct ast_cli_entry cli_local[] = {
926 AST_CLI_DEFINE(locals_show, "List status of local channels"),
929 static int manager_optimize_away(struct mansession *s, const struct message *m)
933 struct local_pvt *found;
934 struct ast_channel *chan;
936 channel = astman_get_header(m, "Channel");
937 if (ast_strlen_zero(channel)) {
938 astman_send_error(s, m, "'Channel' not specified.");
942 chan = ast_channel_get_by_name(channel);
944 astman_send_error(s, m, "Channel does not exist.");
948 p = ast_channel_tech_pvt(chan);
949 ast_channel_unref(chan);
951 found = p ? ao2_find(locals, p, 0) : NULL;
954 ast_clear_flag(&found->base, AST_UNREAL_NO_OPTIMIZATION);
957 astman_send_ack(s, m, "Queued channel to be optimized away");
959 astman_send_error(s, m, "Unable to find channel");
966 static int locals_cmp_cb(void *obj, void *arg, int flags)
968 return (obj == arg) ? CMP_MATCH : 0;
973 * \brief Shutdown the local proxy channel.
978 static void local_shutdown(void)
981 struct ao2_iterator it;
983 /* First, take us out of the channel loop */
984 ast_cli_unregister_multiple(cli_local, ARRAY_LEN(cli_local));
985 ast_manager_unregister("LocalOptimizeAway");
986 ast_channel_unregister(&local_tech);
988 it = ao2_iterator_init(locals, 0);
989 while ((p = ao2_iterator_next(&it))) {
991 ast_softhangup(p->base.owner, AST_SOFTHANGUP_APPUNLOAD);
995 ao2_iterator_destroy(&it);
999 ast_format_cap_destroy(local_tech.capabilities);
1001 STASIS_MESSAGE_TYPE_CLEANUP(ast_local_optimization_begin_type);
1002 STASIS_MESSAGE_TYPE_CLEANUP(ast_local_optimization_end_type);
1003 STASIS_MESSAGE_TYPE_CLEANUP(ast_local_bridge_type);
1006 int ast_local_init(void)
1009 if (STASIS_MESSAGE_TYPE_INIT(ast_local_optimization_begin_type)) {
1013 if (STASIS_MESSAGE_TYPE_INIT(ast_local_optimization_end_type)) {
1017 if (STASIS_MESSAGE_TYPE_INIT(ast_local_bridge_type)) {
1021 if (!(local_tech.capabilities = ast_format_cap_alloc())) {
1024 ast_format_cap_add_all(local_tech.capabilities);
1026 locals = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_MUTEX, 0, NULL, locals_cmp_cb);
1028 ast_format_cap_destroy(local_tech.capabilities);
1032 /* Make sure we can register our channel type */
1033 if (ast_channel_register(&local_tech)) {
1034 ast_log(LOG_ERROR, "Unable to register channel class 'Local'\n");
1035 ao2_ref(locals, -1);
1036 ast_format_cap_destroy(local_tech.capabilities);
1039 ast_cli_register_multiple(cli_local, ARRAY_LEN(cli_local));
1040 ast_manager_register_xml_core("LocalOptimizeAway", EVENT_FLAG_SYSTEM|EVENT_FLAG_CALL, manager_optimize_away);
1042 ast_register_atexit(local_shutdown);