(closes issue #12846)
[asterisk-bristuff.git] / apps / app_dahdiscan.c
blobae270e4847c0a5371158020601fe34cca4d970c0
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>dahdi</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>
48 #include "asterisk/lock.h"
49 #include "asterisk/file.h"
50 #include "asterisk/logger.h"
51 #include "asterisk/channel.h"
52 #include "asterisk/pbx.h"
53 #include "asterisk/module.h"
54 #include "asterisk/config.h"
55 #include "asterisk/app.h"
56 #include "asterisk/options.h"
57 #include "asterisk/utils.h"
58 #include "asterisk/cli.h"
59 #include "asterisk/say.h"
61 #include "asterisk/dahdi_compat.h"
63 static char *app = "DAHDIScan";
64 static char *deprecated_app = "ZapScan";
66 static char *synopsis = "Scan Zap channels to monitor calls";
68 static char *descrip =
69 " ZapScan([group]) allows a call center manager to monitor Zap channels in\n"
70 "a convenient way. Use '#' to select the next channel and use '*' to exit\n"
71 "Limit scanning to a channel GROUP by setting the option group argument.\n";
74 #define CONF_SIZE 160
76 static struct ast_channel *get_zap_channel_locked(int num) {
77 char name[80];
79 snprintf(name,sizeof(name),"%s/%d-1", dahdi_chan_name, num);
80 return ast_get_channel_by_name_locked(name);
83 static int careful_write(int fd, unsigned char *data, int len)
85 int res;
86 while(len) {
87 res = write(fd, data, len);
88 if (res < 1) {
89 if (errno != EAGAIN) {
90 ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
91 return -1;
92 } else
93 return 0;
95 len -= res;
96 data += res;
98 return 0;
101 static int conf_run(struct ast_channel *chan, int confno, int confflags)
103 int fd;
104 DAHDI_CONFINFO ztc;
105 struct ast_frame *f;
106 struct ast_channel *c;
107 struct ast_frame fr;
108 int outfd;
109 int ms;
110 int nfds;
111 int res;
112 int flags;
113 int retryzap;
114 int origfd;
115 int ret = -1;
116 char input[4];
117 int ic=0;
119 DAHDI_BUFFERINFO bi;
120 char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
121 char *buf = __buf + AST_FRIENDLY_OFFSET;
123 /* Set it into U-law mode (write) */
124 if (ast_set_write_format(chan, AST_FORMAT_ULAW) < 0) {
125 ast_log(LOG_WARNING, "Unable to set '%s' to write ulaw mode\n", chan->name);
126 goto outrun;
129 /* Set it into U-law mode (read) */
130 if (ast_set_read_format(chan, AST_FORMAT_ULAW) < 0) {
131 ast_log(LOG_WARNING, "Unable to set '%s' to read ulaw mode\n", chan->name);
132 goto outrun;
134 ast_indicate(chan, -1);
135 retryzap = strcasecmp(chan->tech->type, "Zap");
136 zapretry:
137 origfd = chan->fds[0];
138 if (retryzap) {
139 fd = open("/dev/zap/pseudo", O_RDWR);
140 if (fd < 0) {
141 ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
142 goto outrun;
144 /* Make non-blocking */
145 flags = fcntl(fd, F_GETFL);
146 if (flags < 0) {
147 ast_log(LOG_WARNING, "Unable to get flags: %s\n", strerror(errno));
148 close(fd);
149 goto outrun;
151 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
152 ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno));
153 close(fd);
154 goto outrun;
156 /* Setup buffering information */
157 memset(&bi, 0, sizeof(bi));
158 bi.bufsize = CONF_SIZE;
159 bi.txbufpolicy = DAHDI_POLICY_IMMEDIATE;
160 bi.rxbufpolicy = DAHDI_POLICY_IMMEDIATE;
161 bi.numbufs = 4;
162 if (ioctl(fd, DAHDI_SET_BUFINFO, &bi)) {
163 ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
164 close(fd);
165 goto outrun;
167 nfds = 1;
168 } else {
169 /* XXX Make sure we're not running on a pseudo channel XXX */
170 fd = chan->fds[0];
171 nfds = 0;
173 memset(&ztc, 0, sizeof(ztc));
174 /* Check to see if we're in a conference... */
175 ztc.chan = 0;
176 if (ioctl(fd, DAHDI_GETCONF, &ztc)) {
177 ast_log(LOG_WARNING, "Error getting conference\n");
178 close(fd);
179 goto outrun;
181 if (ztc.confmode) {
182 /* Whoa, already in a conference... Retry... */
183 if (!retryzap) {
184 ast_log(LOG_DEBUG, "Zap channel is in a conference already, retrying with pseudo\n");
185 retryzap = 1;
186 goto zapretry;
189 memset(&ztc, 0, sizeof(ztc));
190 /* Add us to the conference */
191 ztc.chan = 0;
192 ztc.confno = confno;
193 ztc.confmode = DAHDI_CONF_MONITORBOTH;
195 if (ioctl(fd, DAHDI_SETCONF, &ztc)) {
196 ast_log(LOG_WARNING, "Error setting conference\n");
197 close(fd);
198 goto outrun;
200 ast_log(LOG_DEBUG, "Placed channel %s in ZAP channel %d monitor\n", chan->name, confno);
202 for(;;) {
203 outfd = -1;
204 ms = -1;
205 c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
206 if (c) {
207 if (c->fds[0] != origfd) {
208 if (retryzap) {
209 /* Kill old pseudo */
210 close(fd);
212 ast_log(LOG_DEBUG, "Ooh, something swapped out under us, starting over\n");
213 retryzap = 0;
214 goto zapretry;
216 f = ast_read(c);
217 if (!f)
218 break;
219 if(f->frametype == AST_FRAME_DTMF) {
220 if(f->subclass == '#') {
221 ret = 0;
222 break;
224 else if (f->subclass == '*') {
225 ret = -1;
226 break;
229 else {
230 input[ic++] = f->subclass;
232 if(ic == 3) {
233 input[ic++] = '\0';
234 ic=0;
235 ret = atoi(input);
236 ast_verbose(VERBOSE_PREFIX_3 "Zapscan: change channel to %d\n",ret);
237 break;
241 if (fd != chan->fds[0]) {
242 if (f->frametype == AST_FRAME_VOICE) {
243 if (f->subclass == AST_FORMAT_ULAW) {
244 /* Carefully write */
245 careful_write(fd, f->data, f->datalen);
246 } else
247 ast_log(LOG_WARNING, "Huh? Got a non-ulaw (%d) frame in the conference\n", f->subclass);
250 ast_frfree(f);
251 } else if (outfd > -1) {
252 res = read(outfd, buf, CONF_SIZE);
253 if (res > 0) {
254 memset(&fr, 0, sizeof(fr));
255 fr.frametype = AST_FRAME_VOICE;
256 fr.subclass = AST_FORMAT_ULAW;
257 fr.datalen = res;
258 fr.samples = res;
259 fr.data = buf;
260 fr.offset = AST_FRIENDLY_OFFSET;
261 if (ast_write(chan, &fr) < 0) {
262 ast_log(LOG_WARNING, "Unable to write frame to channel: %s\n", strerror(errno));
263 /* break; */
265 } else
266 ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
269 if (f)
270 ast_frfree(f);
271 if (fd != chan->fds[0])
272 close(fd);
273 else {
274 /* Take out of conference */
275 /* Add us to the conference */
276 ztc.chan = 0;
277 ztc.confno = 0;
278 ztc.confmode = 0;
279 if (ioctl(fd, DAHDI_SETCONF, &ztc)) {
280 ast_log(LOG_WARNING, "Error setting conference\n");
284 outrun:
286 return ret;
289 static int conf_exec(struct ast_channel *chan, void *data)
291 int res=-1;
292 struct ast_module_user *u;
293 int confflags = 0;
294 int confno = 0;
295 char confstr[80] = "", *tmp = NULL;
296 struct ast_channel *tempchan = NULL, *lastchan = NULL,*ichan = NULL;
297 struct ast_frame *f;
298 char *desired_group;
299 int input=0,search_group=0;
301 u = ast_module_user_add(chan);
303 if (chan->_state != AST_STATE_UP)
304 ast_answer(chan);
306 desired_group = ast_strdupa(data);
307 if(!ast_strlen_zero(desired_group)) {
308 ast_verbose(VERBOSE_PREFIX_3 "Scanning for group %s\n", desired_group);
309 search_group = 1;
312 for (;;) {
313 if (ast_waitfor(chan, 100) < 0)
314 break;
316 f = ast_read(chan);
317 if (!f)
318 break;
319 if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == '*')) {
320 ast_frfree(f);
321 break;
323 ast_frfree(f);
324 ichan = NULL;
325 if(input) {
326 ichan = get_zap_channel_locked(input);
327 input = 0;
330 tempchan = ichan ? ichan : ast_channel_walk_locked(tempchan);
332 if ( !tempchan && !lastchan )
333 break;
335 if (tempchan && search_group) {
336 const char *mygroup;
337 if((mygroup = pbx_builtin_getvar_helper(tempchan, "GROUP")) && (!strcmp(mygroup, desired_group))) {
338 ast_verbose(VERBOSE_PREFIX_3 "Found Matching Channel %s in group %s\n", tempchan->name, desired_group);
339 } else {
340 ast_mutex_unlock(&tempchan->lock);
341 lastchan = tempchan;
342 continue;
345 if (tempchan && (!strcmp(tempchan->tech->type, "Zap")) && (tempchan != chan) ) {
346 ast_verbose(VERBOSE_PREFIX_3 "Zap channel %s is in-use, monitoring...\n", tempchan->name);
347 ast_copy_string(confstr, tempchan->name, sizeof(confstr));
348 ast_mutex_unlock(&tempchan->lock);
349 if ((tmp = strchr(confstr,'-'))) {
350 *tmp = '\0';
352 confno = atoi(strchr(confstr,'/') + 1);
353 ast_stopstream(chan);
354 ast_say_number(chan, confno, AST_DIGIT_ANY, chan->language, (char *) NULL);
355 res = conf_run(chan, confno, confflags);
356 if (res<0) break;
357 input = res;
358 } else if (tempchan)
359 ast_mutex_unlock(&tempchan->lock);
360 lastchan = tempchan;
362 ast_module_user_remove(u);
363 return res;
366 static int conf_exec_warn(struct ast_channel *chan, void *data)
368 ast_log(LOG_WARNING, "Use of the command %s is deprecated, please use %s instead.\n", deprecated_app, app);
369 return conf_exec(chan, data);
372 static int unload_module(void)
374 int res;
376 res = ast_unregister_application(app);
378 ast_module_user_hangup_all();
380 return res;
383 static int load_module(void)
385 ast_register_application(deprecated_app, conf_exec_warn, synopsis, descrip);
386 return ast_register_application(app, conf_exec, synopsis, descrip);
389 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Scan Zap channels application");