queue device state changes and handle them serially in a background thread
[asterisk/asterisk.git] / apps / app_chanisavail.c
1 /*
2  * Asterisk -- A telephony toolkit for Linux.
3  *
4  * Check if Channel is Available
5  * 
6  * Copyright (C) 2003 - 2005, Digium, Inc.
7  *
8  * Mark Spencer <markster@digium.com>
9  * James Golovich <james@gnuinter.net>
10  *
11  * This program is free software, distributed under the terms of
12  * the GNU General Public License
13  *
14  */
15
16 #include <stdlib.h>
17 #include <unistd.h>
18 #include <errno.h>
19 #include <string.h>
20 #include <stdlib.h>
21 #include <sys/ioctl.h>
22
23 #include "asterisk.h"
24
25 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
26
27 #include "asterisk/lock.h"
28 #include "asterisk/file.h"
29 #include "asterisk/logger.h"
30 #include "asterisk/channel.h"
31 #include "asterisk/pbx.h"
32 #include "asterisk/module.h"
33 #include "asterisk/app.h"
34 #include "asterisk/devicestate.h"
35
36 static char *tdesc = "Check if channel is available";
37
38 static char *app = "ChanIsAvail";
39
40 static char *synopsis = "Check if channel is available";
41
42 static char *descrip = 
43 "  ChanIsAvail(Technology/resource[&Technology2/resource2...][|option]): \n"
44 "Checks is any of the requested channels are available.  If none\n"
45 "of the requested channels are available the new priority will be\n"
46 "n+101 (unless such a priority does not exist or on error, in which\n"
47 "case ChanIsAvail will return -1).\n"
48 "If any of the requested channels are available, the next priority will be n+1,\n"
49 "the channel variable ${AVAILCHAN} will be set to the name of the available channel\n"
50 "and the ChanIsAvail app will return 0.\n"
51 "${AVAILORIGCHAN} is the canonical channel name that was used to create the channel.\n"
52 "${AVAILSTATUS} is the status code for the channel.\n"
53 "If the option 's' is specified (state), will consider channel unavailable\n"
54 "when the channel is in use at all, even if it can take another call.\n";
55
56 STANDARD_LOCAL_USER;
57
58 LOCAL_USER_DECL;
59
60 static int chanavail_exec(struct ast_channel *chan, void *data)
61 {
62         int res=-1, inuse=-1, option_state=0;
63         int status;
64         struct localuser *u;
65         char info[512], tmp[512], trychan[512], *peers, *tech, *number, *rest, *cur, *options, *stringp;
66         struct ast_channel *tempchan;
67
68         if (!data) {
69                 ast_log(LOG_WARNING, "ChanIsAvail requires an argument (Zap/1&Zap/2)\n");
70                 return -1;
71         }
72         LOCAL_USER_ADD(u);
73
74         strncpy(info, (char *)data, sizeof(info)-1);
75         stringp = info;
76         strsep(&stringp, "|");
77         options = strsep(&stringp, "|");
78         if (options && *options == 's')
79                 option_state = 1;
80         peers = info;
81         if (peers) {
82                 cur = peers;
83                 do {
84                         /* remember where to start next time */
85                         rest = strchr(cur, '&');
86                         if (rest) {
87                                 *rest = 0;
88                                 rest++;
89                         }
90                         tech = cur;
91                         number = strchr(tech, '/');
92                         if (!number) {
93                                 ast_log(LOG_WARNING, "ChanIsAvail argument takes format ([technology]/[device])\n");
94                                 return -1;
95                         }
96                         *number = '\0';
97                         number++;
98                         
99                         if (option_state) {
100                                 /* If the pbx says in use then don't bother trying further.
101                                    This is to permit testing if someone's on a call, even if the 
102                                    channel can permit more calls (ie callwaiting, sip calls, etc).  */
103                                
104                                 snprintf(trychan, sizeof(trychan), "%s/%s",cur,number);
105                                 status = inuse = ast_device_state(trychan);
106                         }
107                         if ((inuse < 1) && (tempchan = ast_request(tech, chan->nativeformats, number, &status))) {
108                                         pbx_builtin_setvar_helper(chan, "AVAILCHAN", tempchan->name);
109                                         /* Store the originally used channel too */
110                                         snprintf(tmp, sizeof(tmp), "%s/%s", tech, number);
111                                         pbx_builtin_setvar_helper(chan, "AVAILORIGCHAN", tmp);
112                                         snprintf(tmp, sizeof(tmp), "%d", status);
113                                         pbx_builtin_setvar_helper(chan, "AVAILSTATUS", tmp);
114                                         ast_hangup(tempchan);
115                                         tempchan = NULL;
116                                         res = 1;
117                                         break;
118                         } else {
119                                 snprintf(tmp, sizeof(tmp), "%d", status);
120                                 pbx_builtin_setvar_helper(chan, "AVAILSTATUS", tmp);
121                         }
122                         cur = rest;
123                 } while (cur);
124         }
125         if (res < 1) {
126                 pbx_builtin_setvar_helper(chan, "AVAILCHAN", "");
127                 pbx_builtin_setvar_helper(chan, "AVAILORIGCHAN", "");
128                 if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->cid.cid_num))
129                         chan->priority+=100;
130                 else
131                         return -1;
132         }
133
134         LOCAL_USER_REMOVE(u);
135         return 0;
136 }
137
138 int unload_module(void)
139 {
140         STANDARD_HANGUP_LOCALUSERS;
141         return ast_unregister_application(app);
142 }
143
144 int load_module(void)
145 {
146         return ast_register_application(app, chanavail_exec, synopsis, descrip);
147 }
148
149 char *description(void)
150 {
151         return tdesc;
152 }
153
154 int usecount(void)
155 {
156         int res;
157         STANDARD_USECOUNT(res);
158         return res;
159 }
160
161 char *key()
162 {
163         return ASTERISK_GPL_KEY;
164 }