Blocked revisions 117809 via svnmerge
[asterisk-bristuff.git] / apps / app_zapscan.c
blob372467572d8206b4af51824c8667e30ffee441bb
1 /*
2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2005, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
8 * Modified from app_zapbarge by David Troy <dave@toad.net>
10 * Special thanks to comphealth.com for sponsoring this
11 * GPL application.
13 * See http://www.asterisk.org for more information about
14 * the Asterisk project. Please do not directly contact
15 * any of the maintainers of this project for assistance;
16 * the project provides a web site, mailing lists and IRC
17 * channels for your use.
19 * This program is free software, distributed under the terms of
20 * the GNU General Public License Version 2. See the LICENSE file
21 * at the top of the source tree.
24 /*! \file
26 * \brief Zap Scanner
28 * \author Mark Spencer <markster@digium.com>
30 * \ingroup applications
33 /*** MODULEINFO
34 <depend>zaptel</depend>
35 ***/
37 #include "asterisk.h"
39 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
41 #include "asterisk/zapata.h"
43 #include "asterisk/lock.h"
44 #include "asterisk/file.h"
45 #include "asterisk/channel.h"
46 #include "asterisk/pbx.h"
47 #include "asterisk/module.h"
48 #include "asterisk/config.h"
49 #include "asterisk/app.h"
50 #include "asterisk/utils.h"
51 #include "asterisk/cli.h"
52 #include "asterisk/say.h"
54 static char *app = "ZapScan";
56 static char *synopsis = "Scan Zap channels to monitor calls";
58 static char *descrip =
59 " ZapScan([group]) allows a call center manager to monitor Zap channels in\n"
60 "a convenient way. Use '#' to select the next channel and use '*' to exit\n"
61 "Limit scanning to a channel GROUP by setting the option group argument.\n";
64 #define CONF_SIZE 160
66 static struct ast_channel *get_zap_channel_locked(int num) {
67 char name[80];
69 snprintf(name, sizeof(name), "Zap/%d-1", num);
70 return ast_get_channel_by_name_locked(name);
73 static int careful_write(int fd, unsigned char *data, int len)
75 int res;
76 while (len) {
77 res = write(fd, data, len);
78 if (res < 1) {
79 if (errno != EAGAIN) {
80 ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
81 return -1;
82 } else {
83 return 0;
86 len -= res;
87 data += res;
89 return 0;
92 static int conf_run(struct ast_channel *chan, int confno, int confflags)
94 int fd;
95 struct zt_confinfo ztc;
96 struct ast_frame *f;
97 struct ast_channel *c;
98 struct ast_frame fr;
99 int outfd;
100 int ms;
101 int nfds;
102 int res;
103 int flags;
104 int retryzap;
105 int origfd;
106 int ret = -1;
107 char input[4];
108 int ic = 0;
110 ZT_BUFFERINFO bi;
111 char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
112 char *buf = __buf + AST_FRIENDLY_OFFSET;
114 /* Set it into U-law mode (write) */
115 if (ast_set_write_format(chan, AST_FORMAT_ULAW) < 0) {
116 ast_log(LOG_WARNING, "Unable to set '%s' to write ulaw mode\n", chan->name);
117 goto outrun;
120 /* Set it into U-law mode (read) */
121 if (ast_set_read_format(chan, AST_FORMAT_ULAW) < 0) {
122 ast_log(LOG_WARNING, "Unable to set '%s' to read ulaw mode\n", chan->name);
123 goto outrun;
125 ast_indicate(chan, -1);
126 retryzap = strcasecmp(chan->tech->type, "Zap");
127 zapretry:
128 origfd = chan->fds[0];
129 if (retryzap) {
130 fd = open("/dev/zap/pseudo", O_RDWR);
131 if (fd < 0) {
132 ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
133 goto outrun;
135 /* Make non-blocking */
136 flags = fcntl(fd, F_GETFL);
137 if (flags < 0) {
138 ast_log(LOG_WARNING, "Unable to get flags: %s\n", strerror(errno));
139 close(fd);
140 goto outrun;
142 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
143 ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno));
144 close(fd);
145 goto outrun;
147 /* Setup buffering information */
148 memset(&bi, 0, sizeof(bi));
149 bi.bufsize = CONF_SIZE;
150 bi.txbufpolicy = ZT_POLICY_IMMEDIATE;
151 bi.rxbufpolicy = ZT_POLICY_IMMEDIATE;
152 bi.numbufs = 4;
153 if (ioctl(fd, ZT_SET_BUFINFO, &bi)) {
154 ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
155 close(fd);
156 goto outrun;
158 nfds = 1;
159 } else {
160 /* XXX Make sure we're not running on a pseudo channel XXX */
161 fd = chan->fds[0];
162 nfds = 0;
164 memset(&ztc, 0, sizeof(ztc));
165 /* Check to see if we're in a conference... */
166 ztc.chan = 0;
167 if (ioctl(fd, ZT_GETCONF, &ztc)) {
168 ast_log(LOG_WARNING, "Error getting conference\n");
169 close(fd);
170 goto outrun;
172 if (ztc.confmode) {
173 /* Whoa, already in a conference... Retry... */
174 if (!retryzap) {
175 ast_debug(1, "Zap channel is in a conference already, retrying with pseudo\n");
176 retryzap = 1;
177 goto zapretry;
180 memset(&ztc, 0, sizeof(ztc));
181 /* Add us to the conference */
182 ztc.chan = 0;
183 ztc.confno = confno;
184 ztc.confmode = ZT_CONF_MONITORBOTH;
186 if (ioctl(fd, ZT_SETCONF, &ztc)) {
187 ast_log(LOG_WARNING, "Error setting conference\n");
188 close(fd);
189 goto outrun;
191 ast_debug(1, "Placed channel %s in ZAP channel %d monitor\n", chan->name, confno);
193 for (;;) {
194 outfd = -1;
195 ms = -1;
196 c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
197 if (c) {
198 if (c->fds[0] != origfd) {
199 if (retryzap) {
200 /* Kill old pseudo */
201 close(fd);
203 ast_debug(1, "Ooh, something swapped out under us, starting over\n");
204 retryzap = 0;
205 goto zapretry;
207 f = ast_read(c);
208 if (!f) {
209 break;
211 if (f->frametype == AST_FRAME_DTMF) {
212 if (f->subclass == '#') {
213 ret = 0;
214 break;
215 } else if (f->subclass == '*') {
216 ret = -1;
217 break;
218 } else {
219 input[ic++] = f->subclass;
221 if (ic == 3) {
222 input[ic++] = '\0';
223 ic = 0;
224 ret = atoi(input);
225 ast_verb(3, "Zapscan: change channel to %d\n", ret);
226 break;
230 if (fd != chan->fds[0]) {
231 if (f->frametype == AST_FRAME_VOICE) {
232 if (f->subclass == AST_FORMAT_ULAW) {
233 /* Carefully write */
234 careful_write(fd, f->data.ptr, f->datalen);
235 } else {
236 ast_log(LOG_WARNING, "Huh? Got a non-ulaw (%d) frame in the conference\n", f->subclass);
240 ast_frfree(f);
241 } else if (outfd > -1) {
242 res = read(outfd, buf, CONF_SIZE);
243 if (res > 0) {
244 memset(&fr, 0, sizeof(fr));
245 fr.frametype = AST_FRAME_VOICE;
246 fr.subclass = AST_FORMAT_ULAW;
247 fr.datalen = res;
248 fr.samples = res;
249 fr.data.ptr = buf;
250 fr.offset = AST_FRIENDLY_OFFSET;
251 if (ast_write(chan, &fr) < 0) {
252 ast_log(LOG_WARNING, "Unable to write frame to channel: %s\n", strerror(errno));
253 /* break; */
255 } else {
256 ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
260 if (f) {
261 ast_frfree(f);
263 if (fd != chan->fds[0]) {
264 close(fd);
265 } else {
266 /* Take out of conference */
267 /* Add us to the conference */
268 ztc.chan = 0;
269 ztc.confno = 0;
270 ztc.confmode = 0;
271 if (ioctl(fd, ZT_SETCONF, &ztc)) {
272 ast_log(LOG_WARNING, "Error setting conference\n");
276 outrun:
278 return ret;
281 static int conf_exec(struct ast_channel *chan, void *data)
283 int res=-1;
284 int confflags = 0;
285 int confno = 0;
286 char confstr[80] = "", *tmp = NULL;
287 struct ast_channel *tempchan = NULL, *lastchan = NULL, *ichan = NULL;
288 struct ast_frame *f;
289 char *desired_group;
290 int input = 0, search_group = 0;
292 if (chan->_state != AST_STATE_UP)
293 ast_answer(chan);
295 desired_group = ast_strdupa(data);
296 if (!ast_strlen_zero(desired_group)) {
297 ast_verb(3, "Scanning for group %s\n", desired_group);
298 search_group = 1;
301 for (;;) {
302 if (ast_waitfor(chan, 100) < 0)
303 break;
305 f = ast_read(chan);
306 if (!f)
307 break;
308 if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == '*')) {
309 ast_frfree(f);
310 break;
312 ast_frfree(f);
313 ichan = NULL;
314 if(input) {
315 ichan = get_zap_channel_locked(input);
316 input = 0;
319 tempchan = ichan ? ichan : ast_channel_walk_locked(tempchan);
321 if (!tempchan && !lastchan) {
322 break;
325 if (tempchan && search_group) {
326 const char *mygroup;
327 if ((mygroup = pbx_builtin_getvar_helper(tempchan, "GROUP")) && (!strcmp(mygroup, desired_group))) {
328 ast_verb(3, "Found Matching Channel %s in group %s\n", tempchan->name, desired_group);
329 } else {
330 ast_channel_unlock(tempchan);
331 lastchan = tempchan;
332 continue;
335 if (tempchan && (!strcmp(tempchan->tech->type, "Zap")) && (tempchan != chan)) {
336 ast_verb(3, "Zap channel %s is in-use, monitoring...\n", tempchan->name);
337 ast_copy_string(confstr, tempchan->name, sizeof(confstr));
338 ast_channel_unlock(tempchan);
339 if ((tmp = strchr(confstr, '-'))) {
340 *tmp = '\0';
342 confno = atoi(strchr(confstr, '/') + 1);
343 ast_stopstream(chan);
344 ast_say_number(chan, confno, AST_DIGIT_ANY, chan->language, (char *) NULL);
345 res = conf_run(chan, confno, confflags);
346 if (res < 0) {
347 break;
349 input = res;
350 } else if (tempchan) {
351 ast_channel_unlock(tempchan);
353 lastchan = tempchan;
355 return res;
358 static int unload_module(void)
360 return ast_unregister_application(app);
363 static int load_module(void)
365 return ((ast_register_application(app, conf_exec, synopsis, descrip)) ? AST_MODULE_LOAD_FAILURE : AST_MODULE_LOAD_SUCCESS);
368 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Scan Zap channels application");