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>zaptel</depend>
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";
66 static struct ast_channel
*get_zap_channel_locked(int num
) {
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
)
77 res
= write(fd
, data
, len
);
79 if (errno
!= EAGAIN
) {
80 ast_log(LOG_WARNING
, "Failed to write audio data to conference: %s\n", strerror(errno
));
92 static int conf_run(struct ast_channel
*chan
, int confno
, int confflags
)
95 struct zt_confinfo ztc
;
97 struct ast_channel
*c
;
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
);
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
);
125 ast_indicate(chan
, -1);
126 retryzap
= strcasecmp(chan
->tech
->type
, "Zap");
128 origfd
= chan
->fds
[0];
130 fd
= open("/dev/zap/pseudo", O_RDWR
);
132 ast_log(LOG_WARNING
, "Unable to open pseudo channel: %s\n", strerror(errno
));
135 /* Make non-blocking */
136 flags
= fcntl(fd
, F_GETFL
);
138 ast_log(LOG_WARNING
, "Unable to get flags: %s\n", strerror(errno
));
142 if (fcntl(fd
, F_SETFL
, flags
| O_NONBLOCK
)) {
143 ast_log(LOG_WARNING
, "Unable to set flags: %s\n", strerror(errno
));
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
;
153 if (ioctl(fd
, ZT_SET_BUFINFO
, &bi
)) {
154 ast_log(LOG_WARNING
, "Unable to set buffering information: %s\n", strerror(errno
));
160 /* XXX Make sure we're not running on a pseudo channel XXX */
164 memset(&ztc
, 0, sizeof(ztc
));
165 /* Check to see if we're in a conference... */
167 if (ioctl(fd
, ZT_GETCONF
, &ztc
)) {
168 ast_log(LOG_WARNING
, "Error getting conference\n");
173 /* Whoa, already in a conference... Retry... */
175 ast_debug(1, "Zap channel is in a conference already, retrying with pseudo\n");
180 memset(&ztc
, 0, sizeof(ztc
));
181 /* Add us to the conference */
184 ztc
.confmode
= ZT_CONF_MONITORBOTH
;
186 if (ioctl(fd
, ZT_SETCONF
, &ztc
)) {
187 ast_log(LOG_WARNING
, "Error setting conference\n");
191 ast_debug(1, "Placed channel %s in ZAP channel %d monitor\n", chan
->name
, confno
);
196 c
= ast_waitfor_nandfds(&chan
, 1, &fd
, nfds
, NULL
, &outfd
, &ms
);
198 if (c
->fds
[0] != origfd
) {
200 /* Kill old pseudo */
203 ast_debug(1, "Ooh, something swapped out under us, starting over\n");
211 if (f
->frametype
== AST_FRAME_DTMF
) {
212 if (f
->subclass
== '#') {
215 } else if (f
->subclass
== '*') {
219 input
[ic
++] = f
->subclass
;
225 ast_verb(3, "Zapscan: change channel to %d\n", ret
);
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
);
236 ast_log(LOG_WARNING
, "Huh? Got a non-ulaw (%d) frame in the conference\n", f
->subclass
);
241 } else if (outfd
> -1) {
242 res
= read(outfd
, buf
, CONF_SIZE
);
244 memset(&fr
, 0, sizeof(fr
));
245 fr
.frametype
= AST_FRAME_VOICE
;
246 fr
.subclass
= AST_FORMAT_ULAW
;
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
));
256 ast_log(LOG_WARNING
, "Failed to read frame: %s\n", strerror(errno
));
263 if (fd
!= chan
->fds
[0]) {
266 /* Take out of conference */
267 /* Add us to the conference */
271 if (ioctl(fd
, ZT_SETCONF
, &ztc
)) {
272 ast_log(LOG_WARNING
, "Error setting conference\n");
281 static int conf_exec(struct ast_channel
*chan
, void *data
)
286 char confstr
[80] = "", *tmp
= NULL
;
287 struct ast_channel
*tempchan
= NULL
, *lastchan
= NULL
, *ichan
= NULL
;
290 int input
= 0, search_group
= 0;
292 if (chan
->_state
!= AST_STATE_UP
)
295 desired_group
= ast_strdupa(data
);
296 if (!ast_strlen_zero(desired_group
)) {
297 ast_verb(3, "Scanning for group %s\n", desired_group
);
302 if (ast_waitfor(chan
, 100) < 0)
308 if ((f
->frametype
== AST_FRAME_DTMF
) && (f
->subclass
== '*')) {
315 ichan
= get_zap_channel_locked(input
);
319 tempchan
= ichan
? ichan
: ast_channel_walk_locked(tempchan
);
321 if (!tempchan
&& !lastchan
) {
325 if (tempchan
&& search_group
) {
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
);
330 ast_channel_unlock(tempchan
);
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
, '-'))) {
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
);
350 } else if (tempchan
) {
351 ast_channel_unlock(tempchan
);
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");