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