Merged revisions 47287 via svnmerge from
[asterisk/asterisk.git] / channels / chan_local.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2005, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
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.
13  *
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.
17  */
18
19 /*! \file
20  *
21  * \author Mark Spencer <markster@digium.com>
22  *
23  * \brief Local Proxy Channel
24  * 
25  * \ingroup channel_drivers
26  */
27
28 #include "asterisk.h"
29
30 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
31
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>
43
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"
64
65 static const char tdesc[] = "Local Proxy Channel Driver";
66
67 #define IS_OUTBOUND(a,b) (a == b->chan ? 1 : 0)
68
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);
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);
82
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,
103 };
104
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 */
116 };
117
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 */
123
124 static AST_LIST_HEAD_STATIC(locals, local_pvt);
125
126 /*! \brief Adds devicestate to local channels */
127 static int local_devicestate(void *data)
128 {
129         char *exten = ast_strdupa(data);
130         char *context = NULL, *opts = NULL;
131         int res;
132
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;      
136         }
137
138         *context++ = '\0';
139
140         /* Strip options if they exist */
141         if ((opts = strchr(context, '/')))
142                 *opts = '\0';
143
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;
151 }
152
153 static int local_queue_frame(struct local_pvt *p, int isoutbound, struct ast_frame *f, struct ast_channel *us)
154 {
155         struct ast_channel *other = NULL;
156
157 retrylock:              
158
159         /* Recalculate outbound channel */
160         other = isoutbound ? p->owner : p->chan;
161
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;
171         }
172         if (!other) {
173                 ast_clear_flag(p, LOCAL_GLARE_DETECT);
174                 return 0;
175         }
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;
184                         }
185                 }
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;
193         }
194         ast_queue_frame(other, f);
195         ast_mutex_unlock(&other->lock);
196         ast_clear_flag(p, LOCAL_GLARE_DETECT);
197         return 0;
198 }
199
200 static int local_answer(struct ast_channel *ast)
201 {
202         struct local_pvt *p = ast->tech_pvt;
203         int isoutbound;
204         int res = -1;
205
206         ast_mutex_lock(&p->lock);
207         isoutbound = IS_OUTBOUND(ast, p);
208         if (isoutbound) {
209                 /* Pass along answer since somebody answered us */
210                 struct ast_frame answer = { AST_FRAME_CONTROL, AST_CONTROL_ANSWER };
211                 res = local_queue_frame(p, isoutbound, &answer, ast);
212         } else
213                 ast_log(LOG_WARNING, "Huh?  Local is being asked to answer?\n");
214         ast_mutex_unlock(&p->lock);
215         return res;
216 }
217
218 static void check_bridge(struct local_pvt *p, int isoutbound)
219 {
220         if (ast_test_flag(p, LOCAL_ALREADY_MASQED) || ast_test_flag(p, LOCAL_NO_OPTIMIZATION) || !p->chan || !p->owner)
221                 return;
222
223         /* only do the masquerade if we are being called on the outbound channel,
224            if it has been bridged to another channel and if there are no pending
225            frames on the owner channel (because they would be transferred to the
226            outbound channel during the masquerade)
227         */
228         if (isoutbound && p->chan->_bridge /* Not ast_bridged_channel!  Only go one step! */ && AST_LIST_EMPTY(&p->owner->readq)) {
229                 /* Masquerade bridged channel into owner */
230                 /* Lock everything we need, one by one, and give up if
231                    we can't get everything.  Remember, we'll get another
232                    chance in just a little bit */
233                 if (!ast_mutex_trylock(&(p->chan->_bridge)->lock)) {
234                         if (!p->chan->_bridge->_softhangup) {
235                                 if (!ast_mutex_trylock(&p->owner->lock)) {
236                                         if (!p->owner->_softhangup) {
237                                                 ast_channel_masquerade(p->owner, p->chan->_bridge);
238                                                 ast_set_flag(p, LOCAL_ALREADY_MASQED);
239                                         }
240                                         ast_mutex_unlock(&p->owner->lock);
241                                 }
242                                 ast_mutex_unlock(&(p->chan->_bridge)->lock);
243                         }
244                 }
245         /* We only allow masquerading in one 'direction'... it's important to preserve the state
246            (group variables, etc.) that live on p->chan->_bridge (and were put there by the dialplan)
247            when the local channels go away.
248         */
249 #if 0
250         } else if (!isoutbound && p->owner && p->owner->_bridge && p->chan && AST_LIST_EMPTY(&p->chan->readq)) {
251                 /* Masquerade bridged channel into chan */
252                 if (!ast_mutex_trylock(&(p->owner->_bridge)->lock)) {
253                         if (!p->owner->_bridge->_softhangup) {
254                                 if (!ast_mutex_trylock(&p->chan->lock)) {
255                                         if (!p->chan->_softhangup) {
256                                                 ast_channel_masquerade(p->chan, p->owner->_bridge);
257                                                 ast_set_flag(p, LOCAL_ALREADY_MASQED);
258                                         }
259                                         ast_mutex_unlock(&p->chan->lock);
260                                 }
261                         }
262                         ast_mutex_unlock(&(p->owner->_bridge)->lock);
263                 }
264 #endif
265         }
266 }
267
268 static struct ast_frame  *local_read(struct ast_channel *ast)
269 {
270         return &ast_null_frame;
271 }
272
273 static int local_write(struct ast_channel *ast, struct ast_frame *f)
274 {
275         struct local_pvt *p = ast->tech_pvt;
276         int res = -1;
277         int isoutbound;
278
279         /* Just queue for delivery to the other side */
280         ast_mutex_lock(&p->lock);
281         isoutbound = IS_OUTBOUND(ast, p);
282         if (f && (f->frametype == AST_FRAME_VOICE || f->frametype == AST_FRAME_VIDEO))
283                 check_bridge(p, isoutbound);
284         if (!ast_test_flag(p, LOCAL_ALREADY_MASQED))
285                 res = local_queue_frame(p, isoutbound, f, ast);
286         else {
287                 if (option_debug)
288                         ast_log(LOG_DEBUG, "Not posting to queue since already masked on '%s'\n", ast->name);
289                 res = 0;
290         }
291         ast_mutex_unlock(&p->lock);
292         return res;
293 }
294
295 static int local_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
296 {
297         struct local_pvt *p = newchan->tech_pvt;
298         ast_mutex_lock(&p->lock);
299
300         if ((p->owner != oldchan) && (p->chan != oldchan)) {
301                 ast_log(LOG_WARNING, "Old channel wasn't %p but was %p/%p\n", oldchan, p->owner, p->chan);
302                 ast_mutex_unlock(&p->lock);
303                 return -1;
304         }
305         if (p->owner == oldchan)
306                 p->owner = newchan;
307         else
308                 p->chan = newchan;
309         ast_mutex_unlock(&p->lock);
310         return 0;
311 }
312
313 static int local_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen)
314 {
315         struct local_pvt *p = ast->tech_pvt;
316         int res = 0;
317         struct ast_frame f = { AST_FRAME_CONTROL, };
318         int isoutbound;
319
320         /* If this is an MOH hold or unhold, do it on the Local channel versus real channel */
321         if (condition == AST_CONTROL_HOLD) {
322                 ast_moh_start(ast, data, NULL);
323         } else if (condition == AST_CONTROL_UNHOLD) {
324                 ast_moh_stop(ast);
325         } else {
326                 /* Queue up a frame representing the indication as a control frame */
327                 ast_mutex_lock(&p->lock);
328                 isoutbound = IS_OUTBOUND(ast, p);
329                 f.subclass = condition;
330                 f.data = (void*)data;
331                 f.datalen = datalen;
332                 res = local_queue_frame(p, isoutbound, &f, ast);
333                 ast_mutex_unlock(&p->lock);
334         }
335
336         return res;
337 }
338
339 static int local_digit_begin(struct ast_channel *ast, char digit)
340 {
341         struct local_pvt *p = ast->tech_pvt;
342         int res = -1;
343         struct ast_frame f = { AST_FRAME_DTMF_BEGIN, };
344         int isoutbound;
345
346         ast_mutex_lock(&p->lock);
347         isoutbound = IS_OUTBOUND(ast, p);
348         f.subclass = digit;
349         res = local_queue_frame(p, isoutbound, &f, ast);
350         ast_mutex_unlock(&p->lock);
351
352         return res;
353 }
354
355 static int local_digit_end(struct ast_channel *ast, char digit)
356 {
357         struct local_pvt *p = ast->tech_pvt;
358         int res = -1;
359         struct ast_frame f = { AST_FRAME_DTMF_END, };
360         int isoutbound;
361
362         ast_mutex_lock(&p->lock);
363         isoutbound = IS_OUTBOUND(ast, p);
364         f.subclass = digit;
365         res = local_queue_frame(p, isoutbound, &f, ast);
366         ast_mutex_unlock(&p->lock);
367         
368         return res;
369 }
370
371 static int local_sendtext(struct ast_channel *ast, const char *text)
372 {
373         struct local_pvt *p = ast->tech_pvt;
374         int res = -1;
375         struct ast_frame f = { AST_FRAME_TEXT, };
376         int isoutbound;
377
378         ast_mutex_lock(&p->lock);
379         isoutbound = IS_OUTBOUND(ast, p);
380         f.data = (char *) text;
381         f.datalen = strlen(text) + 1;
382         res = local_queue_frame(p, isoutbound, &f, ast);
383         ast_mutex_unlock(&p->lock);
384         return res;
385 }
386
387 static int local_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen)
388 {
389         struct local_pvt *p = ast->tech_pvt;
390         int res = -1;
391         struct ast_frame f = { AST_FRAME_HTML, };
392         int isoutbound;
393
394         ast_mutex_lock(&p->lock);
395         isoutbound = IS_OUTBOUND(ast, p);
396         f.subclass = subclass;
397         f.data = (char *)data;
398         f.datalen = datalen;
399         res = local_queue_frame(p, isoutbound, &f, ast);
400         ast_mutex_unlock(&p->lock);
401         return res;
402 }
403
404 /*! \brief Initiate new call, part of PBX interface 
405  *      dest is the dial string */
406 static int local_call(struct ast_channel *ast, char *dest, int timeout)
407 {
408         struct local_pvt *p = ast->tech_pvt;
409         int res;
410         struct ast_var_t *varptr = NULL, *new;
411         size_t len, namelen;
412         
413         ast_mutex_lock(&p->lock);
414
415         p->chan->cid.cid_num = ast_strdup(p->owner->cid.cid_num);
416         p->chan->cid.cid_name = ast_strdup(p->owner->cid.cid_name);
417         p->chan->cid.cid_rdnis = ast_strdup(p->owner->cid.cid_rdnis);
418         p->chan->cid.cid_ani = ast_strdup(p->owner->cid.cid_ani);
419         p->chan->cid.cid_pres = p->owner->cid.cid_pres;
420         ast_string_field_set(p->chan, language, p->owner->language);
421         ast_string_field_set(p->chan, accountcode, p->owner->accountcode);
422         p->chan->cdrflags = p->owner->cdrflags;
423
424         /* copy the channel variables from the incoming channel to the outgoing channel */
425         /* Note that due to certain assumptions, they MUST be in the same order */
426         AST_LIST_TRAVERSE(&p->owner->varshead, varptr, entries) {
427                 namelen = strlen(varptr->name);
428                 len = sizeof(struct ast_var_t) + namelen + strlen(varptr->value) + 2;
429                 if ((new = ast_calloc(1, len))) {
430                         memcpy(new, varptr, len);
431                         new->value = &(new->name[0]) + namelen + 1;
432                         AST_LIST_INSERT_TAIL(&p->chan->varshead, new, entries);
433                 }
434         }
435
436         ast_set_flag(p, LOCAL_LAUNCHED_PBX);
437
438         /* Start switch on sub channel */
439         res = ast_pbx_start(p->chan);
440         ast_mutex_unlock(&p->lock);
441         return res;
442 }
443
444 /*! \brief Hangup a call through the local proxy channel */
445 static int local_hangup(struct ast_channel *ast)
446 {
447         struct local_pvt *p = ast->tech_pvt;
448         int isoutbound;
449         struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_HANGUP };
450         struct ast_channel *ochan = NULL;
451         int glaredetect = 0;
452
453         ast_mutex_lock(&p->lock);
454         isoutbound = IS_OUTBOUND(ast, p);
455         if (isoutbound) {
456                 const char *status = pbx_builtin_getvar_helper(p->chan, "DIALSTATUS");
457                 if ((status) && (p->owner))
458                         pbx_builtin_setvar_helper(p->owner, "CHANLOCALSTATUS", status);
459                 p->chan = NULL;
460                 ast_clear_flag(p, LOCAL_LAUNCHED_PBX);
461                 ast_module_user_remove(p->u_chan);
462         } else {
463                 p->owner = NULL;
464                 ast_module_user_remove(p->u_owner);
465         }
466         
467         ast->tech_pvt = NULL;
468         
469         if (!p->owner && !p->chan) {
470                 /* Okay, done with the private part now, too. */
471                 glaredetect = ast_test_flag(p, LOCAL_GLARE_DETECT);
472                 /* If we have a queue holding, don't actually destroy p yet, but
473                    let local_queue do it. */
474                 if (glaredetect)
475                         ast_set_flag(p, LOCAL_CANCEL_QUEUE);
476                 ast_mutex_unlock(&p->lock);
477                 /* Remove from list */
478                 AST_LIST_LOCK(&locals);
479                 AST_LIST_REMOVE(&locals, p, list);
480                 AST_LIST_UNLOCK(&locals);
481                 /* Grab / release lock just in case */
482                 ast_mutex_lock(&p->lock);
483                 ast_mutex_unlock(&p->lock);
484                 /* And destroy */
485                 if (!glaredetect) {
486                         ast_mutex_destroy(&p->lock);
487                         free(p);
488                 }
489                 return 0;
490         }
491         if (p->chan && !ast_test_flag(p, LOCAL_LAUNCHED_PBX))
492                 /* Need to actually hangup since there is no PBX */
493                 ochan = p->chan;
494         else
495                 local_queue_frame(p, isoutbound, &f, NULL);
496         ast_mutex_unlock(&p->lock);
497         if (ochan)
498                 ast_hangup(ochan);
499         return 0;
500 }
501
502 /*! \brief Create a call structure */
503 static struct local_pvt *local_alloc(const char *data, int format)
504 {
505         struct local_pvt *tmp = NULL;
506         char *c = NULL, *opts = NULL;
507
508         if (!(tmp = ast_calloc(1, sizeof(*tmp))))
509                 return NULL;
510
511         /* Initialize private structure information */
512         ast_mutex_init(&tmp->lock);
513         ast_copy_string(tmp->exten, data, sizeof(tmp->exten));
514
515         /* Look for options */
516         if ((opts = strchr(tmp->exten, '/'))) {
517                 *opts++ = '\0';
518                 if (strchr(opts, 'n'))
519                         ast_set_flag(tmp, LOCAL_NO_OPTIMIZATION);
520         }
521
522         /* Look for a context */
523         if ((c = strchr(tmp->exten, '@')))
524                 *c++ = '\0';
525
526         ast_copy_string(tmp->context, c ? c : "default", sizeof(tmp->context));
527
528         tmp->reqformat = format;
529
530         if (!ast_exists_extension(NULL, tmp->context, tmp->exten, 1, NULL)) {
531                 ast_log(LOG_NOTICE, "No such extension/context %s@%s creating local channel\n", tmp->exten, tmp->context);
532                 ast_mutex_destroy(&tmp->lock);
533                 free(tmp);
534                 tmp = NULL;
535         } else {
536                 /* Add to list */
537                 AST_LIST_LOCK(&locals);
538                 AST_LIST_INSERT_HEAD(&locals, tmp, list);
539                 AST_LIST_UNLOCK(&locals);
540         }
541         
542         return tmp;
543 }
544
545 /*! \brief Start new local channel */
546 static struct ast_channel *local_new(struct local_pvt *p, int state)
547 {
548         struct ast_channel *tmp = NULL, *tmp2 = NULL;
549         int randnum = ast_random() & 0xffff, fmt = 0;
550
551         /* Allocate two new Asterisk channels */
552         if (!(tmp = ast_channel_alloc(1)) || !(tmp2 = ast_channel_alloc(1))) {
553                 if (tmp)
554                         ast_channel_free(tmp);
555                 if (tmp2)
556                         ast_channel_free(tmp2);
557                 ast_log(LOG_WARNING, "Unable to allocate channel structure(s)\n");
558                 return NULL;
559         } 
560
561         tmp2->tech = tmp->tech = &local_tech;
562
563         tmp->nativeformats = p->reqformat;
564         tmp2->nativeformats = p->reqformat;
565
566         ast_string_field_build(tmp, name, "Local/%s@%s-%04x,1", p->exten, p->context, randnum);
567         ast_string_field_build(tmp2, name, "Local/%s@%s-%04x,2", p->exten, p->context, randnum);
568
569         ast_setstate(tmp, state);
570         ast_setstate(tmp2, AST_STATE_RING);
571
572         /* Determine our read/write format and set it on each channel */
573         fmt = ast_best_codec(p->reqformat);
574         tmp->writeformat = fmt;
575         tmp2->writeformat = fmt;
576         tmp->rawwriteformat = fmt;
577         tmp2->rawwriteformat = fmt;
578         tmp->readformat = fmt;
579         tmp2->readformat = fmt;
580         tmp->rawreadformat = fmt;
581         tmp2->rawreadformat = fmt;
582
583         tmp->tech_pvt = p;
584         tmp2->tech_pvt = p;
585
586         p->owner = tmp;
587         p->chan = tmp2;
588         p->u_owner = ast_module_user_add(p->owner);
589         p->u_chan = ast_module_user_add(p->chan);
590
591         ast_copy_string(tmp->context, p->context, sizeof(tmp->context));
592         ast_copy_string(tmp2->context, p->context, sizeof(tmp2->context));
593         ast_copy_string(tmp2->exten, p->exten, sizeof(tmp->exten));
594         tmp->priority = 1;
595         tmp2->priority = 1;
596
597         return tmp;
598 }
599
600
601 /*! \brief Part of PBX interface */
602 static struct ast_channel *local_request(const char *type, int format, void *data, int *cause)
603 {
604         struct local_pvt *p = NULL;
605         struct ast_channel *chan = NULL;
606
607         /* Allocate a new private structure and then Asterisk channel */
608         if ((p = local_alloc(data, format)))
609                 chan = local_new(p, AST_STATE_DOWN);
610
611         return chan;
612 }
613
614 /*! \brief CLI command "local show channels" */
615 static int locals_show(int fd, int argc, char **argv)
616 {
617         struct local_pvt *p = NULL;
618
619         if (argc != 3)
620                 return RESULT_SHOWUSAGE;
621
622         AST_LIST_LOCK(&locals);
623         if (!AST_LIST_EMPTY(&locals)) {
624                 AST_LIST_TRAVERSE(&locals, p, list) {
625                         ast_mutex_lock(&p->lock);
626                         ast_cli(fd, "%s -- %s@%s\n", p->owner ? p->owner->name : "<unowned>", p->exten, p->context);
627                         ast_mutex_unlock(&p->lock);
628                 }
629         } else
630                 ast_cli(fd, "No local channels in use\n");
631         AST_LIST_UNLOCK(&locals);
632
633         return RESULT_SUCCESS;
634 }
635
636 static char show_locals_usage[] = 
637 "Usage: local show channels\n"
638 "       Provides summary information on active local proxy channels.\n";
639
640 static struct ast_cli_entry cli_local[] = {
641         { { "local", "show", "channels", NULL },
642         locals_show, "List status of local channels",
643         show_locals_usage },
644 };
645
646 /*! \brief Load module into PBX, register channel */
647 static int load_module(void)
648 {
649         /* Make sure we can register our channel type */
650         if (ast_channel_register(&local_tech)) {
651                 ast_log(LOG_ERROR, "Unable to register channel class 'Local'\n");
652                 return -1;
653         }
654         ast_cli_register_multiple(cli_local, sizeof(cli_local) / sizeof(struct ast_cli_entry));
655         return 0;
656 }
657
658 /*! \brief Unload the local proxy channel from Asterisk */
659 static int unload_module(void)
660 {
661         struct local_pvt *p = NULL;
662
663         /* First, take us out of the channel loop */
664         ast_cli_unregister_multiple(cli_local, sizeof(cli_local) / sizeof(struct ast_cli_entry));
665         ast_channel_unregister(&local_tech);
666         if (!AST_LIST_LOCK(&locals)) {
667                 /* Hangup all interfaces if they have an owner */
668                 AST_LIST_TRAVERSE(&locals, p, list) {
669                         if (p->owner)
670                                 ast_softhangup(p->owner, AST_SOFTHANGUP_APPUNLOAD);
671                 }
672                 AST_LIST_UNLOCK(&locals);
673                 AST_LIST_HEAD_DESTROY(&locals);
674         } else {
675                 ast_log(LOG_WARNING, "Unable to lock the monitor\n");
676                 return -1;
677         }               
678         return 0;
679 }
680
681 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Local Proxy Channel");