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
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.
28 * \author Mark Spencer <markster@digium.com>
30 * \ingroup applications
34 <depend>dahdi</depend>
39 ASTERISK_FILE_VERSION(__FILE__
, "$Revision$")
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";
76 static struct ast_channel
*get_zap_channel_locked(int num
) {
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
)
87 res
= write(fd
, data
, len
);
89 if (errno
!= EAGAIN
) {
90 ast_log(LOG_WARNING
, "Failed to write audio data to conference: %s\n", strerror(errno
));
101 static int conf_run(struct ast_channel
*chan
, int confno
, int confflags
)
106 struct ast_channel
*c
;
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
);
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
);
134 ast_indicate(chan
, -1);
135 retryzap
= strcasecmp(chan
->tech
->type
, "Zap");
137 origfd
= chan
->fds
[0];
139 fd
= open("/dev/zap/pseudo", O_RDWR
);
141 ast_log(LOG_WARNING
, "Unable to open pseudo channel: %s\n", strerror(errno
));
144 /* Make non-blocking */
145 flags
= fcntl(fd
, F_GETFL
);
147 ast_log(LOG_WARNING
, "Unable to get flags: %s\n", strerror(errno
));
151 if (fcntl(fd
, F_SETFL
, flags
| O_NONBLOCK
)) {
152 ast_log(LOG_WARNING
, "Unable to set flags: %s\n", strerror(errno
));
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
;
162 if (ioctl(fd
, DAHDI_SET_BUFINFO
, &bi
)) {
163 ast_log(LOG_WARNING
, "Unable to set buffering information: %s\n", strerror(errno
));
169 /* XXX Make sure we're not running on a pseudo channel XXX */
173 memset(&ztc
, 0, sizeof(ztc
));
174 /* Check to see if we're in a conference... */
176 if (ioctl(fd
, DAHDI_GETCONF
, &ztc
)) {
177 ast_log(LOG_WARNING
, "Error getting conference\n");
182 /* Whoa, already in a conference... Retry... */
184 ast_log(LOG_DEBUG
, "Zap channel is in a conference already, retrying with pseudo\n");
189 memset(&ztc
, 0, sizeof(ztc
));
190 /* Add us to the conference */
193 ztc
.confmode
= DAHDI_CONF_MONITORBOTH
;
195 if (ioctl(fd
, DAHDI_SETCONF
, &ztc
)) {
196 ast_log(LOG_WARNING
, "Error setting conference\n");
200 ast_log(LOG_DEBUG
, "Placed channel %s in ZAP channel %d monitor\n", chan
->name
, confno
);
205 c
= ast_waitfor_nandfds(&chan
, 1, &fd
, nfds
, NULL
, &outfd
, &ms
);
207 if (c
->fds
[0] != origfd
) {
209 /* Kill old pseudo */
212 ast_log(LOG_DEBUG
, "Ooh, something swapped out under us, starting over\n");
219 if(f
->frametype
== AST_FRAME_DTMF
) {
220 if(f
->subclass
== '#') {
224 else if (f
->subclass
== '*') {
230 input
[ic
++] = f
->subclass
;
236 ast_verbose(VERBOSE_PREFIX_3
"Zapscan: change channel to %d\n",ret
);
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
);
247 ast_log(LOG_WARNING
, "Huh? Got a non-ulaw (%d) frame in the conference\n", f
->subclass
);
251 } else if (outfd
> -1) {
252 res
= read(outfd
, buf
, CONF_SIZE
);
254 memset(&fr
, 0, sizeof(fr
));
255 fr
.frametype
= AST_FRAME_VOICE
;
256 fr
.subclass
= AST_FORMAT_ULAW
;
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
));
266 ast_log(LOG_WARNING
, "Failed to read frame: %s\n", strerror(errno
));
271 if (fd
!= chan
->fds
[0])
274 /* Take out of conference */
275 /* Add us to the conference */
279 if (ioctl(fd
, DAHDI_SETCONF
, &ztc
)) {
280 ast_log(LOG_WARNING
, "Error setting conference\n");
289 static int conf_exec(struct ast_channel
*chan
, void *data
)
292 struct ast_module_user
*u
;
295 char confstr
[80] = "", *tmp
= NULL
;
296 struct ast_channel
*tempchan
= NULL
, *lastchan
= NULL
,*ichan
= NULL
;
299 int input
=0,search_group
=0;
301 u
= ast_module_user_add(chan
);
303 if (chan
->_state
!= AST_STATE_UP
)
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
);
313 if (ast_waitfor(chan
, 100) < 0)
319 if ((f
->frametype
== AST_FRAME_DTMF
) && (f
->subclass
== '*')) {
326 ichan
= get_zap_channel_locked(input
);
330 tempchan
= ichan
? ichan
: ast_channel_walk_locked(tempchan
);
332 if ( !tempchan
&& !lastchan
)
335 if (tempchan
&& search_group
) {
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
);
340 ast_mutex_unlock(&tempchan
->lock
);
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
,'-'))) {
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
);
359 ast_mutex_unlock(&tempchan
->lock
);
362 ast_module_user_remove(u
);
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)
376 res
= ast_unregister_application(app
);
378 ast_module_user_hangup_all();
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");