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