Fix a few things I missed to ensure zt_chan_conf structure is not modified in mkintf
[asterisk-bristuff.git] / apps / app_zapscan.c
blob690d9a5cc0a5c2eff971e5eb18e12193625c57f4
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 <stdlib.h>
42 #include <stdio.h>
43 #include <string.h>
44 #include <unistd.h>
45 #include <errno.h>
46 #include <sys/ioctl.h>
47 #include <zaptel/zaptel.h>
49 #include "asterisk/lock.h"
50 #include "asterisk/file.h"
51 #include "asterisk/logger.h"
52 #include "asterisk/channel.h"
53 #include "asterisk/pbx.h"
54 #include "asterisk/module.h"
55 #include "asterisk/config.h"
56 #include "asterisk/app.h"
57 #include "asterisk/options.h"
58 #include "asterisk/utils.h"
59 #include "asterisk/cli.h"
60 #include "asterisk/say.h"
62 static char *app = "ZapScan";
64 static char *synopsis = "Scan Zap channels to monitor calls";
66 static char *descrip =
67 " ZapScan([group]) allows a call center manager to monitor Zap channels in\n"
68 "a convenient way. Use '#' to select the next channel and use '*' to exit\n"
69 "Limit scanning to a channel GROUP by setting the option group argument.\n";
72 #define CONF_SIZE 160
74 static struct ast_channel *get_zap_channel_locked(int num) {
75 char name[80];
77 snprintf(name,sizeof(name),"Zap/%d-1",num);
78 return ast_get_channel_by_name_locked(name);
81 static int careful_write(int fd, unsigned char *data, int len)
83 int res;
84 while(len) {
85 res = write(fd, data, len);
86 if (res < 1) {
87 if (errno != EAGAIN) {
88 ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
89 return -1;
90 } else
91 return 0;
93 len -= res;
94 data += res;
96 return 0;
99 static int conf_run(struct ast_channel *chan, int confno, int confflags)
101 int fd;
102 struct zt_confinfo ztc;
103 struct ast_frame *f;
104 struct ast_channel *c;
105 struct ast_frame fr;
106 int outfd;
107 int ms;
108 int nfds;
109 int res;
110 int flags;
111 int retryzap;
112 int origfd;
113 int ret = -1;
114 char input[4];
115 int ic=0;
117 ZT_BUFFERINFO bi;
118 char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
119 char *buf = __buf + AST_FRIENDLY_OFFSET;
121 /* Set it into U-law mode (write) */
122 if (ast_set_write_format(chan, AST_FORMAT_ULAW) < 0) {
123 ast_log(LOG_WARNING, "Unable to set '%s' to write ulaw mode\n", chan->name);
124 goto outrun;
127 /* Set it into U-law mode (read) */
128 if (ast_set_read_format(chan, AST_FORMAT_ULAW) < 0) {
129 ast_log(LOG_WARNING, "Unable to set '%s' to read ulaw mode\n", chan->name);
130 goto outrun;
132 ast_indicate(chan, -1);
133 retryzap = strcasecmp(chan->tech->type, "Zap");
134 zapretry:
135 origfd = chan->fds[0];
136 if (retryzap) {
137 fd = open("/dev/zap/pseudo", O_RDWR);
138 if (fd < 0) {
139 ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
140 goto outrun;
142 /* Make non-blocking */
143 flags = fcntl(fd, F_GETFL);
144 if (flags < 0) {
145 ast_log(LOG_WARNING, "Unable to get flags: %s\n", strerror(errno));
146 close(fd);
147 goto outrun;
149 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
150 ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno));
151 close(fd);
152 goto outrun;
154 /* Setup buffering information */
155 memset(&bi, 0, sizeof(bi));
156 bi.bufsize = CONF_SIZE;
157 bi.txbufpolicy = ZT_POLICY_IMMEDIATE;
158 bi.rxbufpolicy = ZT_POLICY_IMMEDIATE;
159 bi.numbufs = 4;
160 if (ioctl(fd, ZT_SET_BUFINFO, &bi)) {
161 ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
162 close(fd);
163 goto outrun;
165 nfds = 1;
166 } else {
167 /* XXX Make sure we're not running on a pseudo channel XXX */
168 fd = chan->fds[0];
169 nfds = 0;
171 memset(&ztc, 0, sizeof(ztc));
172 /* Check to see if we're in a conference... */
173 ztc.chan = 0;
174 if (ioctl(fd, ZT_GETCONF, &ztc)) {
175 ast_log(LOG_WARNING, "Error getting conference\n");
176 close(fd);
177 goto outrun;
179 if (ztc.confmode) {
180 /* Whoa, already in a conference... Retry... */
181 if (!retryzap) {
182 ast_log(LOG_DEBUG, "Zap channel is in a conference already, retrying with pseudo\n");
183 retryzap = 1;
184 goto zapretry;
187 memset(&ztc, 0, sizeof(ztc));
188 /* Add us to the conference */
189 ztc.chan = 0;
190 ztc.confno = confno;
191 ztc.confmode = ZT_CONF_MONITORBOTH;
193 if (ioctl(fd, ZT_SETCONF, &ztc)) {
194 ast_log(LOG_WARNING, "Error setting conference\n");
195 close(fd);
196 goto outrun;
198 ast_log(LOG_DEBUG, "Placed channel %s in ZAP channel %d monitor\n", chan->name, confno);
200 for(;;) {
201 outfd = -1;
202 ms = -1;
203 c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
204 if (c) {
205 if (c->fds[0] != origfd) {
206 if (retryzap) {
207 /* Kill old pseudo */
208 close(fd);
210 ast_log(LOG_DEBUG, "Ooh, something swapped out under us, starting over\n");
211 retryzap = 0;
212 goto zapretry;
214 f = ast_read(c);
215 if (!f)
216 break;
217 if(f->frametype == AST_FRAME_DTMF) {
218 if(f->subclass == '#') {
219 ret = 0;
220 break;
222 else if (f->subclass == '*') {
223 ret = -1;
224 break;
227 else {
228 input[ic++] = f->subclass;
230 if(ic == 3) {
231 input[ic++] = '\0';
232 ic=0;
233 ret = atoi(input);
234 ast_verbose(VERBOSE_PREFIX_3 "Zapscan: change channel to %d\n",ret);
235 break;
239 if (fd != chan->fds[0]) {
240 if (f->frametype == AST_FRAME_VOICE) {
241 if (f->subclass == AST_FORMAT_ULAW) {
242 /* Carefully write */
243 careful_write(fd, f->data, f->datalen);
244 } else
245 ast_log(LOG_WARNING, "Huh? Got a non-ulaw (%d) frame in the conference\n", f->subclass);
248 ast_frfree(f);
249 } else if (outfd > -1) {
250 res = read(outfd, buf, CONF_SIZE);
251 if (res > 0) {
252 memset(&fr, 0, sizeof(fr));
253 fr.frametype = AST_FRAME_VOICE;
254 fr.subclass = AST_FORMAT_ULAW;
255 fr.datalen = res;
256 fr.samples = res;
257 fr.data = buf;
258 fr.offset = AST_FRIENDLY_OFFSET;
259 if (ast_write(chan, &fr) < 0) {
260 ast_log(LOG_WARNING, "Unable to write frame to channel: %s\n", strerror(errno));
261 /* break; */
263 } else
264 ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
267 if (f)
268 ast_frfree(f);
269 if (fd != chan->fds[0])
270 close(fd);
271 else {
272 /* Take out of conference */
273 /* Add us to the conference */
274 ztc.chan = 0;
275 ztc.confno = 0;
276 ztc.confmode = 0;
277 if (ioctl(fd, ZT_SETCONF, &ztc)) {
278 ast_log(LOG_WARNING, "Error setting conference\n");
282 outrun:
284 return ret;
287 static int conf_exec(struct ast_channel *chan, void *data)
289 int res=-1;
290 struct ast_module_user *u;
291 int confflags = 0;
292 int confno = 0;
293 char confstr[80] = "", *tmp = NULL;
294 struct ast_channel *tempchan = NULL, *lastchan = NULL,*ichan = NULL;
295 struct ast_frame *f;
296 char *desired_group;
297 int input=0,search_group=0;
299 u = ast_module_user_add(chan);
301 if (chan->_state != AST_STATE_UP)
302 ast_answer(chan);
304 desired_group = ast_strdupa(data);
305 if(!ast_strlen_zero(desired_group)) {
306 ast_verbose(VERBOSE_PREFIX_3 "Scanning for group %s\n", desired_group);
307 search_group = 1;
310 for (;;) {
311 if (ast_waitfor(chan, 100) < 0)
312 break;
314 f = ast_read(chan);
315 if (!f)
316 break;
317 if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == '*')) {
318 ast_frfree(f);
319 break;
321 ast_frfree(f);
322 ichan = NULL;
323 if(input) {
324 ichan = get_zap_channel_locked(input);
325 input = 0;
328 tempchan = ichan ? ichan : ast_channel_walk_locked(tempchan);
330 if ( !tempchan && !lastchan )
331 break;
333 if (tempchan && search_group) {
334 const char *mygroup;
335 if((mygroup = pbx_builtin_getvar_helper(tempchan, "GROUP")) && (!strcmp(mygroup, desired_group))) {
336 ast_verbose(VERBOSE_PREFIX_3 "Found Matching Channel %s in group %s\n", tempchan->name, desired_group);
337 } else {
338 ast_mutex_unlock(&tempchan->lock);
339 lastchan = tempchan;
340 continue;
343 if (tempchan && (!strcmp(tempchan->tech->type, "Zap")) && (tempchan != chan) ) {
344 ast_verbose(VERBOSE_PREFIX_3 "Zap channel %s is in-use, monitoring...\n", tempchan->name);
345 ast_copy_string(confstr, tempchan->name, sizeof(confstr));
346 ast_mutex_unlock(&tempchan->lock);
347 if ((tmp = strchr(confstr,'-'))) {
348 *tmp = '\0';
350 confno = atoi(strchr(confstr,'/') + 1);
351 ast_stopstream(chan);
352 ast_say_number(chan, confno, AST_DIGIT_ANY, chan->language, (char *) NULL);
353 res = conf_run(chan, confno, confflags);
354 if (res<0) break;
355 input = res;
356 } else if (tempchan)
357 ast_mutex_unlock(&tempchan->lock);
358 lastchan = tempchan;
360 ast_module_user_remove(u);
361 return res;
364 static int unload_module(void)
366 int res;
368 res = ast_unregister_application(app);
370 ast_module_user_hangup_all();
372 return res;
375 static int load_module(void)
377 return ast_register_application(app, conf_exec, synopsis, descrip);
380 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Scan Zap channels application");