The loop in the handler for the "core show locks" could potentially block for
[asterisk-bristuff.git] / channels / chan_local.c
blob1144278f0087270a6960b7a83d8085128e93868d
1 /*
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.
19 /*! \file
21 * \author Mark Spencer <markster@digium.com>
23 * \brief Local Proxy Channel
25 * \ingroup channel_drivers
28 #include "asterisk.h"
30 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
32 #include <stdio.h>
33 #include <string.h>
34 #include <unistd.h>
35 #include <sys/socket.h>
36 #include <errno.h>
37 #include <stdlib.h>
38 #include <fcntl.h>
39 #include <netdb.h>
40 #include <netinet/in.h>
41 #include <arpa/inet.h>
42 #include <sys/signal.h>
44 #include "asterisk/lock.h"
45 #include "asterisk/channel.h"
46 #include "asterisk/config.h"
47 #include "asterisk/logger.h"
48 #include "asterisk/module.h"
49 #include "asterisk/pbx.h"
50 #include "asterisk/options.h"
51 #include "asterisk/lock.h"
52 #include "asterisk/sched.h"
53 #include "asterisk/io.h"
54 #include "asterisk/rtp.h"
55 #include "asterisk/acl.h"
56 #include "asterisk/callerid.h"
57 #include "asterisk/file.h"
58 #include "asterisk/cli.h"
59 #include "asterisk/app.h"
60 #include "asterisk/musiconhold.h"
61 #include "asterisk/manager.h"
62 #include "asterisk/stringfields.h"
63 #include "asterisk/devicestate.h"
65 static const char tdesc[] = "Local Proxy Channel Driver";
67 #define IS_OUTBOUND(a,b) (a == b->chan ? 1 : 0)
69 static struct ast_channel *local_request(const char *type, int format, void *data, int *cause);
70 static int local_digit_begin(struct ast_channel *ast, char digit);
71 static int local_digit_end(struct ast_channel *ast, char digit, unsigned int duration);
72 static int local_call(struct ast_channel *ast, char *dest, int timeout);
73 static int local_hangup(struct ast_channel *ast);
74 static int local_answer(struct ast_channel *ast);
75 static struct ast_frame *local_read(struct ast_channel *ast);
76 static int local_write(struct ast_channel *ast, struct ast_frame *f);
77 static int local_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen);
78 static int local_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
79 static int local_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen);
80 static int local_sendtext(struct ast_channel *ast, const char *text);
81 static int local_devicestate(void *data);
83 /* PBX interface structure for channel registration */
84 static const struct ast_channel_tech local_tech = {
85 .type = "Local",
86 .description = tdesc,
87 .capabilities = -1,
88 .requester = local_request,
89 .send_digit_begin = local_digit_begin,
90 .send_digit_end = local_digit_end,
91 .call = local_call,
92 .hangup = local_hangup,
93 .answer = local_answer,
94 .read = local_read,
95 .write = local_write,
96 .write_video = local_write,
97 .exception = local_read,
98 .indicate = local_indicate,
99 .fixup = local_fixup,
100 .send_html = local_sendhtml,
101 .send_text = local_sendtext,
102 .devicestate = local_devicestate,
105 struct local_pvt {
106 ast_mutex_t lock; /* Channel private lock */
107 unsigned int flags; /* Private flags */
108 char context[AST_MAX_CONTEXT]; /* Context to call */
109 char exten[AST_MAX_EXTENSION]; /* Extension to call */
110 int reqformat; /* Requested format */
111 struct ast_channel *owner; /* Master Channel */
112 struct ast_channel *chan; /* Outbound channel */
113 struct ast_module_user *u_owner; /*! reference to keep the module loaded while in use */
114 struct ast_module_user *u_chan; /*! reference to keep the module loaded while in use */
115 AST_LIST_ENTRY(local_pvt) list; /* Next entity */
118 #define LOCAL_GLARE_DETECT (1 << 0) /*!< Detect glare on hangup */
119 #define LOCAL_CANCEL_QUEUE (1 << 1) /*!< Cancel queue */
120 #define LOCAL_ALREADY_MASQED (1 << 2) /*!< Already masqueraded */
121 #define LOCAL_LAUNCHED_PBX (1 << 3) /*!< PBX was launched */
122 #define LOCAL_NO_OPTIMIZATION (1 << 4) /*!< Do not optimize using masquerading */
124 static AST_LIST_HEAD_STATIC(locals, local_pvt);
126 /*! \brief Adds devicestate to local channels */
127 static int local_devicestate(void *data)
129 char *exten = ast_strdupa(data);
130 char *context = NULL, *opts = NULL;
131 int res;
133 if (!(context = strchr(exten, '@'))) {
134 ast_log(LOG_WARNING, "Someone used Local/%s somewhere without a @context. This is bad.\n", exten);
135 return AST_DEVICE_INVALID;
138 *context++ = '\0';
140 /* Strip options if they exist */
141 if ((opts = strchr(context, '/')))
142 *opts = '\0';
144 if (option_debug > 2)
145 ast_log(LOG_DEBUG, "Checking if extension %s@%s exists (devicestate)\n", exten, context);
146 res = ast_exists_extension(NULL, context, exten, 1, NULL);
147 if (!res)
148 return AST_DEVICE_INVALID;
149 else
150 return AST_DEVICE_UNKNOWN;
153 static int local_queue_frame(struct local_pvt *p, int isoutbound, struct ast_frame *f, struct ast_channel *us)
155 struct ast_channel *other = NULL;
157 retrylock:
159 /* Recalculate outbound channel */
160 other = isoutbound ? p->owner : p->chan;
162 /* Set glare detection */
163 ast_set_flag(p, LOCAL_GLARE_DETECT);
164 if (ast_test_flag(p, LOCAL_CANCEL_QUEUE)) {
165 /* We had a glare on the hangup. Forget all this business,
166 return and destroy p. */
167 ast_mutex_unlock(&p->lock);
168 ast_mutex_destroy(&p->lock);
169 free(p);
170 return -1;
172 if (!other) {
173 ast_clear_flag(p, LOCAL_GLARE_DETECT);
174 return 0;
176 if (ast_mutex_trylock(&other->lock)) {
177 /* Failed to lock. Release main lock and try again */
178 ast_mutex_unlock(&p->lock);
179 if (us) {
180 if (ast_mutex_unlock(&us->lock)) {
181 ast_log(LOG_WARNING, "%s wasn't locked while sending %d/%d\n",
182 us->name, f->frametype, f->subclass);
183 us = NULL;
186 /* Wait just a bit */
187 usleep(1);
188 /* Only we can destroy ourselves, so we can't disappear here */
189 if (us)
190 ast_mutex_lock(&us->lock);
191 ast_mutex_lock(&p->lock);
192 goto retrylock;
194 ast_queue_frame(other, f);
195 ast_mutex_unlock(&other->lock);
196 ast_clear_flag(p, LOCAL_GLARE_DETECT);
197 return 0;
200 static int local_answer(struct ast_channel *ast)
202 struct local_pvt *p = ast->tech_pvt;
203 int isoutbound;
204 int res = -1;
206 if (!p)
207 return -1;
209 ast_mutex_lock(&p->lock);
210 isoutbound = IS_OUTBOUND(ast, p);
211 if (isoutbound) {
212 /* Pass along answer since somebody answered us */
213 struct ast_frame answer = { AST_FRAME_CONTROL, AST_CONTROL_ANSWER };
214 res = local_queue_frame(p, isoutbound, &answer, ast);
215 } else
216 ast_log(LOG_WARNING, "Huh? Local is being asked to answer?\n");
217 if (!res)
218 ast_mutex_unlock(&p->lock);
219 return res;
222 static void check_bridge(struct local_pvt *p, int isoutbound)
224 if (ast_test_flag(p, LOCAL_ALREADY_MASQED) || ast_test_flag(p, LOCAL_NO_OPTIMIZATION) || !p->chan || !p->owner || (p->chan->_bridge != ast_bridged_channel(p->chan)))
225 return;
227 /* only do the masquerade if we are being called on the outbound channel,
228 if it has been bridged to another channel and if there are no pending
229 frames on the owner channel (because they would be transferred to the
230 outbound channel during the masquerade)
232 if (isoutbound && p->chan->_bridge /* Not ast_bridged_channel! Only go one step! */ && AST_LIST_EMPTY(&p->owner->readq)) {
233 /* Masquerade bridged channel into owner */
234 /* Lock everything we need, one by one, and give up if
235 we can't get everything. Remember, we'll get another
236 chance in just a little bit */
237 if (!ast_mutex_trylock(&(p->chan->_bridge)->lock)) {
238 if (!p->chan->_bridge->_softhangup) {
239 if (!ast_mutex_trylock(&p->owner->lock)) {
240 if (!p->owner->_softhangup) {
241 ast_channel_masquerade(p->owner, p->chan->_bridge);
242 ast_set_flag(p, LOCAL_ALREADY_MASQED);
244 ast_mutex_unlock(&p->owner->lock);
246 ast_mutex_unlock(&(p->chan->_bridge)->lock);
249 /* We only allow masquerading in one 'direction'... it's important to preserve the state
250 (group variables, etc.) that live on p->chan->_bridge (and were put there by the dialplan)
251 when the local channels go away.
253 #if 0
254 } else if (!isoutbound && p->owner && p->owner->_bridge && p->chan && AST_LIST_EMPTY(&p->chan->readq)) {
255 /* Masquerade bridged channel into chan */
256 if (!ast_mutex_trylock(&(p->owner->_bridge)->lock)) {
257 if (!p->owner->_bridge->_softhangup) {
258 if (!ast_mutex_trylock(&p->chan->lock)) {
259 if (!p->chan->_softhangup) {
260 ast_channel_masquerade(p->chan, p->owner->_bridge);
261 ast_set_flag(p, LOCAL_ALREADY_MASQED);
263 ast_mutex_unlock(&p->chan->lock);
266 ast_mutex_unlock(&(p->owner->_bridge)->lock);
268 #endif
272 static struct ast_frame *local_read(struct ast_channel *ast)
274 return &ast_null_frame;
277 static int local_write(struct ast_channel *ast, struct ast_frame *f)
279 struct local_pvt *p = ast->tech_pvt;
280 int res = -1;
281 int isoutbound;
283 if (!p)
284 return -1;
286 /* Just queue for delivery to the other side */
287 ast_mutex_lock(&p->lock);
288 isoutbound = IS_OUTBOUND(ast, p);
289 if (f && (f->frametype == AST_FRAME_VOICE || f->frametype == AST_FRAME_VIDEO))
290 check_bridge(p, isoutbound);
291 if (!ast_test_flag(p, LOCAL_ALREADY_MASQED))
292 res = local_queue_frame(p, isoutbound, f, ast);
293 else {
294 if (option_debug)
295 ast_log(LOG_DEBUG, "Not posting to queue since already masked on '%s'\n", ast->name);
296 res = 0;
298 if (!res)
299 ast_mutex_unlock(&p->lock);
300 return res;
303 static int local_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
305 struct local_pvt *p = newchan->tech_pvt;
307 if (!p)
308 return -1;
310 ast_mutex_lock(&p->lock);
312 if ((p->owner != oldchan) && (p->chan != oldchan)) {
313 ast_log(LOG_WARNING, "Old channel wasn't %p but was %p/%p\n", oldchan, p->owner, p->chan);
314 ast_mutex_unlock(&p->lock);
315 return -1;
317 if (p->owner == oldchan)
318 p->owner = newchan;
319 else
320 p->chan = newchan;
321 ast_mutex_unlock(&p->lock);
322 return 0;
325 static int local_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen)
327 struct local_pvt *p = ast->tech_pvt;
328 int res = 0;
329 struct ast_frame f = { AST_FRAME_CONTROL, };
330 int isoutbound;
332 if (!p)
333 return -1;
335 /* If this is an MOH hold or unhold, do it on the Local channel versus real channel */
336 if (condition == AST_CONTROL_HOLD) {
337 ast_moh_start(ast, data, NULL);
338 } else if (condition == AST_CONTROL_UNHOLD) {
339 ast_moh_stop(ast);
340 } else {
341 /* Queue up a frame representing the indication as a control frame */
342 ast_mutex_lock(&p->lock);
343 isoutbound = IS_OUTBOUND(ast, p);
344 f.subclass = condition;
345 f.data = (void*)data;
346 f.datalen = datalen;
347 if (!(res = local_queue_frame(p, isoutbound, &f, ast)))
348 ast_mutex_unlock(&p->lock);
351 return res;
354 static int local_digit_begin(struct ast_channel *ast, char digit)
356 struct local_pvt *p = ast->tech_pvt;
357 int res = -1;
358 struct ast_frame f = { AST_FRAME_DTMF_BEGIN, };
359 int isoutbound;
361 if (!p)
362 return -1;
364 ast_mutex_lock(&p->lock);
365 isoutbound = IS_OUTBOUND(ast, p);
366 f.subclass = digit;
367 if (!(res = local_queue_frame(p, isoutbound, &f, ast)))
368 ast_mutex_unlock(&p->lock);
370 return res;
373 static int local_digit_end(struct ast_channel *ast, char digit, unsigned int duration)
375 struct local_pvt *p = ast->tech_pvt;
376 int res = -1;
377 struct ast_frame f = { AST_FRAME_DTMF_END, };
378 int isoutbound;
380 if (!p)
381 return -1;
383 ast_mutex_lock(&p->lock);
384 isoutbound = IS_OUTBOUND(ast, p);
385 f.subclass = digit;
386 f.len = duration;
387 if (!(res = local_queue_frame(p, isoutbound, &f, ast)))
388 ast_mutex_unlock(&p->lock);
390 return res;
393 static int local_sendtext(struct ast_channel *ast, const char *text)
395 struct local_pvt *p = ast->tech_pvt;
396 int res = -1;
397 struct ast_frame f = { AST_FRAME_TEXT, };
398 int isoutbound;
400 if (!p)
401 return -1;
403 ast_mutex_lock(&p->lock);
404 isoutbound = IS_OUTBOUND(ast, p);
405 f.data = (char *) text;
406 f.datalen = strlen(text) + 1;
407 if (!(res = local_queue_frame(p, isoutbound, &f, ast)))
408 ast_mutex_unlock(&p->lock);
409 return res;
412 static int local_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen)
414 struct local_pvt *p = ast->tech_pvt;
415 int res = -1;
416 struct ast_frame f = { AST_FRAME_HTML, };
417 int isoutbound;
419 if (!p)
420 return -1;
422 ast_mutex_lock(&p->lock);
423 isoutbound = IS_OUTBOUND(ast, p);
424 f.subclass = subclass;
425 f.data = (char *)data;
426 f.datalen = datalen;
427 if (!(res = local_queue_frame(p, isoutbound, &f, ast)))
428 ast_mutex_unlock(&p->lock);
429 return res;
432 /*! \brief Initiate new call, part of PBX interface
433 * dest is the dial string */
434 static int local_call(struct ast_channel *ast, char *dest, int timeout)
436 struct local_pvt *p = ast->tech_pvt;
437 int res;
438 struct ast_var_t *varptr = NULL, *new;
439 size_t len, namelen;
441 if (!p)
442 return -1;
444 ast_mutex_lock(&p->lock);
447 * Note that cid_num and cid_name aren't passed in the ast_channel_alloc
448 * call, so it's done here instead.
450 p->chan->cid.cid_num = ast_strdup(p->owner->cid.cid_num);
451 p->chan->cid.cid_name = ast_strdup(p->owner->cid.cid_name);
452 p->chan->cid.cid_rdnis = ast_strdup(p->owner->cid.cid_rdnis);
453 p->chan->cid.cid_ani = ast_strdup(p->owner->cid.cid_ani);
454 p->chan->cid.cid_pres = p->owner->cid.cid_pres;
455 ast_string_field_set(p->chan, language, p->owner->language);
456 ast_string_field_set(p->chan, accountcode, p->owner->accountcode);
457 p->chan->cdrflags = p->owner->cdrflags;
459 /* copy the channel variables from the incoming channel to the outgoing channel */
460 /* Note that due to certain assumptions, they MUST be in the same order */
461 AST_LIST_TRAVERSE(&p->owner->varshead, varptr, entries) {
462 namelen = strlen(varptr->name);
463 len = sizeof(struct ast_var_t) + namelen + strlen(varptr->value) + 2;
464 if ((new = ast_calloc(1, len))) {
465 memcpy(new, varptr, len);
466 new->value = &(new->name[0]) + namelen + 1;
467 AST_LIST_INSERT_TAIL(&p->chan->varshead, new, entries);
471 /* Start switch on sub channel */
472 if (!(res = ast_pbx_start(p->chan)))
473 ast_set_flag(p, LOCAL_LAUNCHED_PBX);
475 ast_mutex_unlock(&p->lock);
476 return res;
479 /*! \brief Hangup a call through the local proxy channel */
480 static int local_hangup(struct ast_channel *ast)
482 struct local_pvt *p = ast->tech_pvt;
483 int isoutbound;
484 struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_HANGUP };
485 struct ast_channel *ochan = NULL;
486 int glaredetect = 0, res = 0;
488 if (!p)
489 return -1;
491 ast_mutex_lock(&p->lock);
492 isoutbound = IS_OUTBOUND(ast, p);
493 if (isoutbound) {
494 const char *status = pbx_builtin_getvar_helper(p->chan, "DIALSTATUS");
495 if ((status) && (p->owner))
496 pbx_builtin_setvar_helper(p->owner, "CHANLOCALSTATUS", status);
497 p->chan = NULL;
498 ast_clear_flag(p, LOCAL_LAUNCHED_PBX);
499 ast_module_user_remove(p->u_chan);
500 } else {
501 p->owner = NULL;
502 ast_module_user_remove(p->u_owner);
505 ast->tech_pvt = NULL;
507 if (!p->owner && !p->chan) {
508 /* Okay, done with the private part now, too. */
509 glaredetect = ast_test_flag(p, LOCAL_GLARE_DETECT);
510 /* If we have a queue holding, don't actually destroy p yet, but
511 let local_queue do it. */
512 if (glaredetect)
513 ast_set_flag(p, LOCAL_CANCEL_QUEUE);
514 ast_mutex_unlock(&p->lock);
515 /* Remove from list */
516 AST_LIST_LOCK(&locals);
517 AST_LIST_REMOVE(&locals, p, list);
518 AST_LIST_UNLOCK(&locals);
519 /* Grab / release lock just in case */
520 ast_mutex_lock(&p->lock);
521 ast_mutex_unlock(&p->lock);
522 /* And destroy */
523 if (!glaredetect) {
524 ast_mutex_destroy(&p->lock);
525 free(p);
527 return 0;
529 if (p->chan && !ast_test_flag(p, LOCAL_LAUNCHED_PBX))
530 /* Need to actually hangup since there is no PBX */
531 ochan = p->chan;
532 else
533 res = local_queue_frame(p, isoutbound, &f, NULL);
534 if (!res)
535 ast_mutex_unlock(&p->lock);
536 if (ochan)
537 ast_hangup(ochan);
538 return 0;
541 /*! \brief Create a call structure */
542 static struct local_pvt *local_alloc(const char *data, int format)
544 struct local_pvt *tmp = NULL;
545 char *c = NULL, *opts = NULL;
547 if (!(tmp = ast_calloc(1, sizeof(*tmp))))
548 return NULL;
550 /* Initialize private structure information */
551 ast_mutex_init(&tmp->lock);
552 ast_copy_string(tmp->exten, data, sizeof(tmp->exten));
554 /* Look for options */
555 if ((opts = strchr(tmp->exten, '/'))) {
556 *opts++ = '\0';
557 if (strchr(opts, 'n'))
558 ast_set_flag(tmp, LOCAL_NO_OPTIMIZATION);
561 /* Look for a context */
562 if ((c = strchr(tmp->exten, '@')))
563 *c++ = '\0';
565 ast_copy_string(tmp->context, c ? c : "default", sizeof(tmp->context));
567 tmp->reqformat = format;
569 if (!ast_exists_extension(NULL, tmp->context, tmp->exten, 1, NULL)) {
570 ast_log(LOG_NOTICE, "No such extension/context %s@%s creating local channel\n", tmp->exten, tmp->context);
571 ast_mutex_destroy(&tmp->lock);
572 free(tmp);
573 tmp = NULL;
574 } else {
575 /* Add to list */
576 AST_LIST_LOCK(&locals);
577 AST_LIST_INSERT_HEAD(&locals, tmp, list);
578 AST_LIST_UNLOCK(&locals);
581 return tmp;
584 /*! \brief Start new local channel */
585 static struct ast_channel *local_new(struct local_pvt *p, int state)
587 struct ast_channel *tmp = NULL, *tmp2 = NULL;
588 int randnum = ast_random() & 0xffff, fmt = 0;
589 const char *t;
590 int ama;
592 /* Allocate two new Asterisk channels */
593 /* safe accountcode */
594 if (p->owner && p->owner->accountcode)
595 t = p->owner->accountcode;
596 else
597 t = "";
599 if (p->owner)
600 ama = p->owner->amaflags;
601 else
602 ama = 0;
603 if (!(tmp = ast_channel_alloc(1, state, 0, 0, t, p->exten, p->context, ama, "Local/%s@%s-%04x,1", p->exten, p->context, randnum))
604 || !(tmp2 = ast_channel_alloc(1, AST_STATE_RING, 0, 0, t, p->exten, p->context, ama, "Local/%s@%s-%04x,2", p->exten, p->context, randnum))) {
605 if (tmp)
606 ast_channel_free(tmp);
607 if (tmp2)
608 ast_channel_free(tmp2);
609 ast_log(LOG_WARNING, "Unable to allocate channel structure(s)\n");
610 return NULL;
613 tmp2->tech = tmp->tech = &local_tech;
615 tmp->nativeformats = p->reqformat;
616 tmp2->nativeformats = p->reqformat;
618 /* Determine our read/write format and set it on each channel */
619 fmt = ast_best_codec(p->reqformat);
620 tmp->writeformat = fmt;
621 tmp2->writeformat = fmt;
622 tmp->rawwriteformat = fmt;
623 tmp2->rawwriteformat = fmt;
624 tmp->readformat = fmt;
625 tmp2->readformat = fmt;
626 tmp->rawreadformat = fmt;
627 tmp2->rawreadformat = fmt;
629 tmp->tech_pvt = p;
630 tmp2->tech_pvt = p;
632 p->owner = tmp;
633 p->chan = tmp2;
634 p->u_owner = ast_module_user_add(p->owner);
635 p->u_chan = ast_module_user_add(p->chan);
637 ast_copy_string(tmp->context, p->context, sizeof(tmp->context));
638 ast_copy_string(tmp2->context, p->context, sizeof(tmp2->context));
639 ast_copy_string(tmp2->exten, p->exten, sizeof(tmp->exten));
640 tmp->priority = 1;
641 tmp2->priority = 1;
643 return tmp;
647 /*! \brief Part of PBX interface */
648 static struct ast_channel *local_request(const char *type, int format, void *data, int *cause)
650 struct local_pvt *p = NULL;
651 struct ast_channel *chan = NULL;
653 /* Allocate a new private structure and then Asterisk channel */
654 if ((p = local_alloc(data, format)))
655 chan = local_new(p, AST_STATE_DOWN);
657 return chan;
660 /*! \brief CLI command "local show channels" */
661 static int locals_show(int fd, int argc, char **argv)
663 struct local_pvt *p = NULL;
665 if (argc != 3)
666 return RESULT_SHOWUSAGE;
668 AST_LIST_LOCK(&locals);
669 if (!AST_LIST_EMPTY(&locals)) {
670 AST_LIST_TRAVERSE(&locals, p, list) {
671 ast_mutex_lock(&p->lock);
672 ast_cli(fd, "%s -- %s@%s\n", p->owner ? p->owner->name : "<unowned>", p->exten, p->context);
673 ast_mutex_unlock(&p->lock);
675 } else
676 ast_cli(fd, "No local channels in use\n");
677 AST_LIST_UNLOCK(&locals);
679 return RESULT_SUCCESS;
682 static char show_locals_usage[] =
683 "Usage: local show channels\n"
684 " Provides summary information on active local proxy channels.\n";
686 static struct ast_cli_entry cli_local[] = {
687 { { "local", "show", "channels", NULL },
688 locals_show, "List status of local channels",
689 show_locals_usage },
692 /*! \brief Load module into PBX, register channel */
693 static int load_module(void)
695 /* Make sure we can register our channel type */
696 if (ast_channel_register(&local_tech)) {
697 ast_log(LOG_ERROR, "Unable to register channel class 'Local'\n");
698 return -1;
700 ast_cli_register_multiple(cli_local, sizeof(cli_local) / sizeof(struct ast_cli_entry));
701 return 0;
704 /*! \brief Unload the local proxy channel from Asterisk */
705 static int unload_module(void)
707 struct local_pvt *p = NULL;
709 /* First, take us out of the channel loop */
710 ast_cli_unregister_multiple(cli_local, sizeof(cli_local) / sizeof(struct ast_cli_entry));
711 ast_channel_unregister(&local_tech);
712 if (!AST_LIST_LOCK(&locals)) {
713 /* Hangup all interfaces if they have an owner */
714 AST_LIST_TRAVERSE(&locals, p, list) {
715 if (p->owner)
716 ast_softhangup(p->owner, AST_SOFTHANGUP_APPUNLOAD);
718 AST_LIST_UNLOCK(&locals);
719 AST_LIST_HEAD_DESTROY(&locals);
720 } else {
721 ast_log(LOG_WARNING, "Unable to lock the monitor\n");
722 return -1;
724 return 0;
727 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Local Proxy Channel");