more ast_copy_string conversions
[asterisk/asterisk.git] / apps / app_zapscan.c
1 /*
2  * Asterisk -- A telephony toolkit for Linux.
3  *
4  * Zap Scanner
5  *
6  * Copyright (C) 2003, Digium
7  *
8  * Modified from app_zapbarge by David Troy <dave@toad.net>
9  *
10  * Mark Spencer <markster@digium.com>
11  *
12  * This program is free software, distributed under the terms of
13  * the GNU General Public License
14  *
15  * Special thanks to comphealth.com for sponsoring this
16  * GPL application.
17  */
18
19 #include <stdlib.h>
20 #include <unistd.h>
21 #include <errno.h>
22 #include <string.h>
23 #include <stdlib.h>
24 #include <sys/ioctl.h>
25
26 #ifdef __linux__
27 #include <linux/zaptel.h>
28 #else
29 #include <zaptel.h>
30 #endif /* __linux__ */
31
32 #include "asterisk.h"
33
34 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
35
36 #include "asterisk/lock.h"
37 #include "asterisk/file.h"
38 #include "asterisk/logger.h"
39 #include "asterisk/channel.h"
40 #include "asterisk/pbx.h"
41 #include "asterisk/module.h"
42 #include "asterisk/config.h"
43 #include "asterisk/app.h"
44 #include "asterisk/options.h"
45 #include "asterisk/utils.h"
46 #include "asterisk/cli.h"
47 #include "asterisk/say.h"
48
49 static char *tdesc = "Scan Zap channels application";
50
51 static char *app = "ZapScan";
52
53 static char *synopsis = "Scan Zap channels to monitor calls";
54
55 static char *descrip =
56 "  ZapScan([group]) allows a call center manager to monitor Zap channels in\n"
57 "a convenient way.  Use '#' to select the next channel and use '*' to exit\n"
58 "Limit scanning to a channel GROUP by setting the option group argument.\n";
59
60 STANDARD_LOCAL_USER;
61
62 LOCAL_USER_DECL;
63
64
65 #define CONF_SIZE 160
66
67 static struct ast_channel *get_zap_channel_locked(int num) {
68         char name[80];
69         
70         snprintf(name,sizeof(name),"Zap/%d-1",num);
71         return ast_get_channel_by_name_locked(name);
72 }
73
74 static int careful_write(int fd, unsigned char *data, int len)
75 {
76         int res;
77         while(len) {
78                 res = write(fd, data, len);
79                 if (res < 1) {
80                         if (errno != EAGAIN) {
81                                 ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
82                                 return -1;
83                         } else
84                                 return 0;
85                 }
86                 len -= res;
87                 data += res;
88         }
89         return 0;
90 }
91
92 static int conf_run(struct ast_channel *chan, int confno, int confflags)
93 {
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;
109         
110         ZT_BUFFERINFO bi;
111         char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
112         char *buf = __buf + AST_FRIENDLY_OFFSET;
113         
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;
118         }
119         
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;
124         }
125         ast_indicate(chan, -1);
126         retryzap = strcasecmp(chan->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;
134                 }
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;
141                 }
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;
146                 }
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;
157                 }
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;
163         }
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;
171         }
172         if (ztc.confmode) {
173                         /* Whoa, already in a conference...  Retry... */
174                         if (!retryzap) {
175                                 ast_log(LOG_DEBUG, "Zap channel is in a conference already, retrying with pseudo\n");
176                                 retryzap = 1;
177                                 goto zapretry;
178                         }
179         }
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;
185                 
186         if (ioctl(fd, ZT_SETCONF, &ztc)) {
187                 ast_log(LOG_WARNING, "Error setting conference\n");
188                 close(fd);
189                 goto outrun;
190         }
191         ast_log(LOG_DEBUG, "Placed channel %s in ZAP channel %d monitor\n", chan->name, confno);
192                 
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);
202                                         }
203                                         ast_log(LOG_DEBUG, "Ooh, something swapped out under us, starting over\n");
204                                         retryzap = 0;
205                                 goto zapretry;
206                                 }
207                                 f = ast_read(c);
208                                 if (!f)
209                                         break;
210                                 if(f->frametype == AST_FRAME_DTMF) {
211                                         if(f->subclass == '#') {
212                                                 ret = 0;
213                                                 break;
214                                         }
215                                         else if (f->subclass == '*') {
216                                                 ret = -1;
217                                                 break;
218                                                 
219                                         }
220                                         else {
221                                                 input[ic++] = f->subclass;
222                                         }
223                                         if(ic == 3) {
224                                                 input[ic++] = '\0';
225                                                 ic=0;
226                                                 ret = atoi(input);
227                                                 ast_verbose(VERBOSE_PREFIX_3 "Zapscan: change channel to %d\n",ret);
228                                                 break;
229                                         }
230                                 }
231                                 
232                                 if (fd != chan->fds[0]) {
233                                         if (f->frametype == AST_FRAME_VOICE) {
234                                                 if (f->subclass == AST_FORMAT_ULAW) {
235                                                         /* Carefully write */
236                                                 careful_write(fd, f->data, f->datalen);
237                                                 } else
238                                                         ast_log(LOG_WARNING, "Huh?  Got a non-ulaw (%d) frame in the conference\n", f->subclass);
239                                         }
240                                 }
241                                 ast_frfree(f);
242                         } else if (outfd > -1) {
243                                 res = read(outfd, buf, CONF_SIZE);
244                                 if (res > 0) {
245                                         memset(&fr, 0, sizeof(fr));
246                                         fr.frametype = AST_FRAME_VOICE;
247                                         fr.subclass = AST_FORMAT_ULAW;
248                                         fr.datalen = res;
249                                         fr.samples = res;
250                                         fr.data = buf;
251                                         fr.offset = AST_FRIENDLY_OFFSET;
252                                         if (ast_write(chan, &fr) < 0) {
253                                                 ast_log(LOG_WARNING, "Unable to write frame to channel: %s\n", strerror(errno));
254                                                 /* break; */
255                                         }
256                                 } else
257                                         ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
258                         }
259         }
260         if (fd != chan->fds[0])
261                         close(fd);
262         else {
263                         /* Take out of conference */
264                         /* Add us to the conference */
265                         ztc.chan = 0;
266                         ztc.confno = 0;
267                         ztc.confmode = 0;
268                         if (ioctl(fd, ZT_SETCONF, &ztc)) {
269                                 ast_log(LOG_WARNING, "Error setting conference\n");
270                 }
271         }
272                 
273  outrun:
274                 
275         return ret;
276 }
277
278 static int conf_exec(struct ast_channel *chan, void *data)
279 {
280         int res=-1;
281         struct localuser *u;
282         int confflags = 0;
283         int confno = 0;
284         char confstr[80] = "", *tmp = NULL;
285         struct ast_channel *tempchan = NULL, *lastchan = NULL,*ichan = NULL;
286         struct ast_frame *f;
287         char *mygroup;
288         char *desired_group;
289         int input=0,search_group=0;
290         
291         LOCAL_USER_ADD(u);
292         
293         if (chan->_state != AST_STATE_UP)
294                 ast_answer(chan);
295         
296         if((desired_group = ast_strdupa((char *) data)) && !ast_strlen_zero(desired_group)) {
297                 ast_verbose(VERBOSE_PREFIX_3 "Scanning for group %s\n", desired_group);
298                 search_group = 1;
299         }
300
301         for (;;) {
302                 if (ast_waitfor(chan, 100) < 0)
303                         break;
304                 
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;
311                 }
312                 ast_frfree(f);
313                 ichan = NULL;
314                 if(input) {
315                         ichan = get_zap_channel_locked(input);
316                         input = 0;
317                 }
318                 
319                 tempchan = ichan ? ichan : ast_channel_walk_locked(tempchan);
320                 
321                 if ( !tempchan && !lastchan )
322                         break;
323                 
324                 if (tempchan && search_group) {
325                         if((mygroup = pbx_builtin_getvar_helper(tempchan, "GROUP")) && (!strcmp(mygroup, desired_group))) {
326                                 ast_verbose(VERBOSE_PREFIX_3 "Found Matching Channel %s in group %s\n", tempchan->name, desired_group);
327                         } else {
328                                 ast_mutex_unlock(&tempchan->lock);
329                                 lastchan = tempchan;
330                                 continue;
331                         }
332                 }
333                 if ( tempchan && tempchan->type && (!strcmp(tempchan->type, "Zap")) && (tempchan != chan) ) {
334                         ast_verbose(VERBOSE_PREFIX_3 "Zap channel %s is in-use, monitoring...\n", tempchan->name);
335                         ast_copy_string(confstr, tempchan->name, sizeof(confstr));
336                         ast_mutex_unlock(&tempchan->lock);
337                         if ((tmp = strchr(confstr,'-'))) {
338                                 *tmp = '\0';
339                         }
340                         confno = atoi(strchr(confstr,'/') + 1);
341                         ast_stopstream(chan);
342                         ast_say_number(chan, confno, AST_DIGIT_ANY, chan->language, (char *) NULL);
343                         res = conf_run(chan, confno, confflags);
344                         if (res<0) break;
345                         input = res;
346                 } else if (tempchan)
347                         ast_mutex_unlock(&tempchan->lock);
348                 lastchan = tempchan;
349         }
350         LOCAL_USER_REMOVE(u);
351         return res;
352 }
353
354 int unload_module(void)
355 {
356         STANDARD_HANGUP_LOCALUSERS;
357         return ast_unregister_application(app);
358 }
359
360 int load_module(void)
361 {
362         return ast_register_application(app, conf_exec, synopsis, descrip);
363 }
364
365 char *description(void)
366 {
367         return tdesc;
368 }
369
370 int usecount(void)
371 {
372         int res;
373         STANDARD_USECOUNT(res);
374         return res;
375 }
376
377 char *key()
378 {
379         return ASTERISK_GPL_KEY;
380 }
381