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