bb0120cbc866a1bdb5044c6be9dba03710ee64be
[asterisk/asterisk.git] / apps / app_dahdibarge.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2005, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
8  * Special thanks to comphealth.com for sponsoring this
9  * GPL application.
10  *
11  * See http://www.asterisk.org for more information about
12  * the Asterisk project. Please do not directly contact
13  * any of the maintainers of this project for assistance;
14  * the project provides a web site, mailing lists and IRC
15  * channels for your use.
16  *
17  * This program is free software, distributed under the terms of
18  * the GNU General Public License Version 2. See the LICENSE file
19  * at the top of the source tree.
20  */
21
22 /*! \file
23  *
24  * \brief DAHDI Barge support
25  *
26  * \author Mark Spencer <markster@digium.com>
27  *
28  * \note Special thanks to comphealth.com for sponsoring this
29  * GPL application.
30  * 
31  * \ingroup applications
32  */
33
34 /*** MODULEINFO
35         <depend>dahdi</depend>
36         <defaultenabled>no</defaultenabled>
37         <support_level>deprecated</support_level>
38         <replacement>app_chanspy</replacement>
39  ***/
40
41 #include "asterisk.h"
42
43 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
44
45 #include <dahdi/user.h>
46
47 #include "asterisk/lock.h"
48 #include "asterisk/file.h"
49 #include "asterisk/channel.h"
50 #include "asterisk/pbx.h"
51 #include "asterisk/module.h"
52 #include "asterisk/config.h"
53 #include "asterisk/app.h"
54 #include "asterisk/cli.h"
55 #include "asterisk/say.h"
56 #include "asterisk/utils.h"
57
58 /*** DOCUMENTATION
59         <application name="DAHDIBarge" language="en_US">
60                 <synopsis>
61                         Barge in (monitor) DAHDI channel.
62                 </synopsis>
63                 <syntax>
64                         <parameter name="channel">
65                                 <para>Channel to barge.</para>
66                         </parameter>
67                 </syntax>
68                 <description>
69                         <para>Barges in on a specified DAHDI <replaceable>channel</replaceable> or prompts
70                         if one is not specified. Returns <literal>-1</literal> when caller user hangs
71                         up and is independent of the state of the channel being monitored.
72                         </para>
73                 </description>
74         </application>
75  ***/
76 static const char app[] = "DAHDIBarge";
77
78 #define CONF_SIZE 160
79
80 static int careful_write(int fd, unsigned char *data, int len)
81 {
82         int res;
83         while(len) {
84                 res = write(fd, data, len);
85                 if (res < 1) {
86                         if (errno != EAGAIN) {
87                                 ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
88                                 return -1;
89                         } else
90                                 return 0;
91                 }
92                 len -= res;
93                 data += res;
94         }
95         return 0;
96 }
97
98 static int conf_run(struct ast_channel *chan, int confno, int confflags)
99 {
100         int fd;
101         struct dahdi_confinfo dahdic;
102         struct ast_frame *f;
103         struct ast_channel *c;
104         struct ast_frame fr;
105         int outfd;
106         int ms;
107         int nfds;
108         int res;
109         int flags;
110         int retrydahdi;
111         int origfd;
112         int ret = -1;
113
114         struct dahdi_bufferinfo bi;
115         char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
116         char *buf = __buf + AST_FRIENDLY_OFFSET;
117
118         /* Set it into U-law mode (write) */
119         if (ast_set_write_format_by_id(chan, AST_FORMAT_ULAW) < 0) {
120                 ast_log(LOG_WARNING, "Unable to set '%s' to write ulaw mode\n", chan->name);
121                 goto outrun;
122         }
123
124         /* Set it into U-law mode (read) */
125         if (ast_set_read_format_by_id(chan, AST_FORMAT_ULAW) < 0) {
126                 ast_log(LOG_WARNING, "Unable to set '%s' to read ulaw mode\n", chan->name);
127                 goto outrun;
128         }
129         ast_indicate(chan, -1);
130         retrydahdi = strcasecmp(chan->tech->type, "DAHDI");
131 dahdiretry:
132         origfd = chan->fds[0];
133         if (retrydahdi) {
134                 fd = open("/dev/dahdi/pseudo", O_RDWR);
135                 if (fd < 0) {
136                         ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
137                         goto outrun;
138                 }
139                 /* Make non-blocking */
140                 flags = fcntl(fd, F_GETFL);
141                 if (flags < 0) {
142                         ast_log(LOG_WARNING, "Unable to get flags: %s\n", strerror(errno));
143                         close(fd);
144                         goto outrun;
145                 }
146                 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
147                         ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno));
148                         close(fd);
149                         goto outrun;
150                 }
151                 /* Setup buffering information */
152                 memset(&bi, 0, sizeof(bi));
153                 bi.bufsize = CONF_SIZE;
154                 bi.txbufpolicy = DAHDI_POLICY_IMMEDIATE;
155                 bi.rxbufpolicy = DAHDI_POLICY_IMMEDIATE;
156                 bi.numbufs = 4;
157                 if (ioctl(fd, DAHDI_SET_BUFINFO, &bi)) {
158                         ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
159                         close(fd);
160                         goto outrun;
161                 }
162                 nfds = 1;
163         } else {
164                 /* XXX Make sure we're not running on a pseudo channel XXX */
165                 fd = chan->fds[0];
166                 nfds = 0;
167         }
168         memset(&dahdic, 0, sizeof(dahdic));
169         /* Check to see if we're in a conference... */
170         dahdic.chan = 0;        
171         if (ioctl(fd, DAHDI_GETCONF, &dahdic)) {
172                 ast_log(LOG_WARNING, "Error getting conference\n");
173                 close(fd);
174                 goto outrun;
175         }
176         if (dahdic.confmode) {
177                 /* Whoa, already in a conference...  Retry... */
178                 if (!retrydahdi) {
179                         ast_debug(1, "DAHDI channel is in a conference already, retrying with pseudo\n");
180                         retrydahdi = 1;
181                         goto dahdiretry;
182                 }
183         }
184         memset(&dahdic, 0, sizeof(dahdic));
185         /* Add us to the conference */
186         dahdic.chan = 0;        
187         dahdic.confno = confno;
188         dahdic.confmode = DAHDI_CONF_MONITORBOTH;
189
190         if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
191                 ast_log(LOG_WARNING, "Error setting conference\n");
192                 close(fd);
193                 goto outrun;
194         }
195         ast_debug(1, "Placed channel %s in DAHDI channel %d monitor\n", chan->name, confno);
196
197         for(;;) {
198                 outfd = -1;
199                 ms = -1;
200                 c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
201                 if (c) {
202                         if (c->fds[0] != origfd) {
203                                 if (retrydahdi) {
204                                         /* Kill old pseudo */
205                                         close(fd);
206                                 }
207                                 ast_debug(1, "Ooh, something swapped out under us, starting over\n");
208                                 retrydahdi = 0;
209                                 goto dahdiretry;
210                         }
211                         f = ast_read(c);
212                         if (!f) 
213                                 break;
214                         if ((f->frametype == AST_FRAME_DTMF) && (f->subclass.integer == '#')) {
215                                 ret = 0;
216                                 ast_frfree(f);
217                                 break;
218                         } else if (fd != chan->fds[0]) {
219                                 if (f->frametype == AST_FRAME_VOICE) {
220                                         if (f->subclass.format.id == AST_FORMAT_ULAW) {
221                                                 /* Carefully write */
222                                                 careful_write(fd, f->data.ptr, f->datalen);
223                                         } else
224                                                 ast_log(LOG_WARNING, "Huh?  Got a non-ulaw (%s) frame in the conference\n", ast_getformatname(&f->subclass.format));
225                                 }
226                         }
227                         ast_frfree(f);
228                 } else if (outfd > -1) {
229                         res = read(outfd, buf, CONF_SIZE);
230                         if (res > 0) {
231                                 memset(&fr, 0, sizeof(fr));
232                                 fr.frametype = AST_FRAME_VOICE;
233                                 ast_format_set(&fr.subclass.format, AST_FORMAT_ULAW, 0);
234                                 fr.datalen = res;
235                                 fr.samples = res;
236                                 fr.data.ptr = buf;
237                                 fr.offset = AST_FRIENDLY_OFFSET;
238                                 if (ast_write(chan, &fr) < 0) {
239                                         ast_log(LOG_WARNING, "Unable to write frame to channel: %s\n", strerror(errno));
240                                         /* break; */
241                                 }
242                         } else 
243                                 ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
244                 }
245         }
246         if (fd != chan->fds[0])
247                 close(fd);
248         else {
249                 /* Take out of conference */
250                 /* Add us to the conference */
251                 dahdic.chan = 0;        
252                 dahdic.confno = 0;
253                 dahdic.confmode = 0;
254                 if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
255                         ast_log(LOG_WARNING, "Error setting conference\n");
256                 }
257         }
258
259 outrun:
260
261         return ret;
262 }
263
264 static int conf_exec(struct ast_channel *chan, const char *data)
265 {
266         int res = -1;
267         int retrycnt = 0;
268         int confflags = 0;
269         int confno = 0;
270         char confnostr[80] = "";
271         
272         if (!ast_strlen_zero(data)) {
273                 if ((sscanf(data, "DAHDI/%30d", &confno) != 1) &&
274                     (sscanf(data, "%30d", &confno) != 1)) {
275                         ast_log(LOG_WARNING, "DAHDIBarge Argument (if specified) must be a channel number, not '%s'\n", (char *)data);
276                         return 0;
277                 }
278         }
279         
280         if (chan->_state != AST_STATE_UP)
281                 ast_answer(chan);
282
283         while(!confno && (++retrycnt < 4)) {
284                 /* Prompt user for conference number */
285                 confnostr[0] = '\0';
286                 res = ast_app_getdata(chan, "conf-getchannel",confnostr, sizeof(confnostr) - 1, 0);
287                 if (res <0) goto out;
288                 if (sscanf(confnostr, "%30d", &confno) != 1)
289                         confno = 0;
290         }
291         if (confno) {
292                 /* XXX Should prompt user for pin if pin is required XXX */
293                 /* Run the conference */
294                 res = conf_run(chan, confno, confflags);
295         }
296 out:
297         /* Do the conference */
298         return res;
299 }
300
301 static int unload_module(void)
302 {
303         return ast_unregister_application(app);
304 }
305
306 static int load_module(void)
307 {
308         return ((ast_register_application_xml(app, conf_exec)) ? AST_MODULE_LOAD_FAILURE : AST_MODULE_LOAD_SUCCESS);
309 }
310
311 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Barge in on DAHDI channel application");