Merge "Revert "AGI: Only defer frames when in an interception routine.""
[asterisk/asterisk.git] / apps / app_dahdiras.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  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*! \file
20  *
21  * \brief Execute an ISDN RAS
22  *
23  * \author Mark Spencer <markster@digium.com>
24  * 
25  * \ingroup applications
26  */
27
28 /*** MODULEINFO
29         <depend>dahdi</depend>
30         <support_level>extended</support_level>
31  ***/
32
33 #include "asterisk.h"
34
35 #include <sys/ioctl.h>
36 #include <sys/wait.h>
37 #include <signal.h>
38 #include <fcntl.h>
39
40 #include <dahdi/user.h>
41
42 #include "asterisk/lock.h"
43 #include "asterisk/file.h"
44 #include "asterisk/channel.h"
45 #include "asterisk/pbx.h"
46 #include "asterisk/module.h"
47 #include "asterisk/app.h"
48
49 /*** DOCUMENTATION
50         <application name="DAHDIRAS" language="en_US">
51                 <synopsis>
52                         Executes DAHDI ISDN RAS application.
53                 </synopsis>
54                 <syntax>
55                         <parameter name="args" required="true">
56                                 <para>A list of parameters to pass to the pppd daemon,
57                                 separated by <literal>,</literal> characters.</para>
58                         </parameter>
59                 </syntax>
60                 <description>
61                         <para>Executes a RAS server using pppd on the given channel.
62                         The channel must be a clear channel (i.e. PRI source) and a DAHDI
63                         channel to be able to use this function (No modem emulation is included).</para>
64                         <para>Your pppd must be patched to be DAHDI aware.</para>
65                 </description>
66         </application>
67
68  ***/
69
70 static const char app[] = "DAHDIRAS";
71
72 #define PPP_MAX_ARGS    32
73 #define PPP_EXEC        "/usr/sbin/pppd"
74
75 static pid_t spawn_ras(struct ast_channel *chan, char *args)
76 {
77         pid_t pid;
78         char *c;
79
80         char *argv[PPP_MAX_ARGS];
81         int argc = 0;
82         char *stringp=NULL;
83
84         /* Start by forking */
85         pid = ast_safe_fork(1);
86         if (pid) {
87                 return pid;
88         }
89
90         /* Execute RAS on File handles */
91         dup2(ast_channel_fd(chan, 0), STDIN_FILENO);
92
93         /* Drop high priority */
94         if (ast_opt_high_priority)
95                 ast_set_priority(0);
96
97         /* Close other file descriptors */
98         ast_close_fds_above_n(STDERR_FILENO);
99
100         /* Reset all arguments */
101         memset(argv, 0, sizeof(argv));
102
103         /* First argument is executable, followed by standard
104            arguments for DAHDI PPP */
105         argv[argc++] = PPP_EXEC;
106         argv[argc++] = "nodetach";
107
108         /* And all the other arguments */
109         stringp=args;
110         c = strsep(&stringp, ",");
111         while(c && strlen(c) && (argc < (PPP_MAX_ARGS - 4))) {
112                 argv[argc++] = c;
113                 c = strsep(&stringp, ",");
114         }
115
116         argv[argc++] = "plugin";
117         argv[argc++] = "dahdi.so";
118         argv[argc++] = "stdin";
119
120         /* Finally launch PPP */
121         execv(PPP_EXEC, argv);
122         fprintf(stderr, "Failed to exec PPPD!\n");
123         exit(1);
124 }
125
126 static void run_ras(struct ast_channel *chan, char *args)
127 {
128         pid_t pid;
129         int status;
130         int res;
131         int signalled = 0;
132         struct dahdi_bufferinfo savebi;
133         int x;
134         
135         res = ioctl(ast_channel_fd(chan, 0), DAHDI_GET_BUFINFO, &savebi);
136         if(res) {
137                 ast_log(LOG_WARNING, "Unable to check buffer policy on channel %s\n", ast_channel_name(chan));
138                 return;
139         }
140
141         pid = spawn_ras(chan, args);
142         if (pid < 0) {
143                 ast_log(LOG_WARNING, "Failed to spawn RAS\n");
144         } else {
145                 for (;;) {
146                         res = waitpid(pid, &status, WNOHANG);
147                         if (!res) {
148                                 /* Check for hangup */
149                                 if (ast_check_hangup(chan) && !signalled) {
150                                         ast_debug(1, "Channel '%s' hungup.  Signalling RAS at %d to die...\n", ast_channel_name(chan), pid);
151                                         kill(pid, SIGTERM);
152                                         signalled=1;
153                                 }
154                                 /* Try again */
155                                 sleep(1);
156                                 continue;
157                         }
158                         if (res < 0) {
159                                 ast_log(LOG_WARNING, "waitpid returned %d: %s\n", res, strerror(errno));
160                         }
161                         if (WIFEXITED(status)) {
162                                 ast_verb(3, "RAS on %s terminated with status %d\n", ast_channel_name(chan), WEXITSTATUS(status));
163                         } else if (WIFSIGNALED(status)) {
164                                 ast_verb(3, "RAS on %s terminated with signal %d\n", 
165                                          ast_channel_name(chan), WTERMSIG(status));
166                         } else {
167                                 ast_verb(3, "RAS on %s terminated weirdly.\n", ast_channel_name(chan));
168                         }
169                         /* Throw back into audio mode */
170                         x = 1;
171                         ioctl(ast_channel_fd(chan, 0), DAHDI_AUDIOMODE, &x);
172
173                         /* Restore saved values */
174                         res = ioctl(ast_channel_fd(chan, 0), DAHDI_SET_BUFINFO, &savebi);
175                         if (res < 0) {
176                                 ast_log(LOG_WARNING, "Unable to set buffer policy on channel %s\n", ast_channel_name(chan));
177                         }
178                         break;
179                 }
180         }
181         ast_safe_fork_cleanup();
182 }
183
184 static int dahdiras_exec(struct ast_channel *chan, const char *data)
185 {
186         int res=-1;
187         char *args;
188         struct dahdi_params dahdip;
189
190         if (!data) 
191                 data = "";
192
193         args = ast_strdupa(data);
194         
195         /* Answer the channel if it's not up */
196         if (ast_channel_state(chan) != AST_STATE_UP)
197                 ast_answer(chan);
198         if (strcasecmp(ast_channel_tech(chan)->type, "DAHDI")) {
199                 /* If it's not a DAHDI channel, we're done.  Wait a couple of
200                    seconds and then hangup... */
201                 ast_verb(2, "Channel %s is not a DAHDI channel\n", ast_channel_name(chan));
202                 sleep(2);
203         } else {
204                 memset(&dahdip, 0, sizeof(dahdip));
205                 if (ioctl(ast_channel_fd(chan, 0), DAHDI_GET_PARAMS, &dahdip)) {
206                         ast_log(LOG_WARNING, "Unable to get DAHDI parameters\n");
207                 } else if (dahdip.sigtype != DAHDI_SIG_CLEAR) {
208                         ast_verb(2, "Channel %s is not a clear channel\n", ast_channel_name(chan));
209                 } else {
210                         /* Everything should be okay.  Run PPP. */
211                         ast_verb(3, "Starting RAS on %s\n", ast_channel_name(chan));
212                         /* Execute RAS */
213                         run_ras(chan, args);
214                 }
215         }
216         return res;
217 }
218
219 static int unload_module(void) 
220 {
221         return ast_unregister_application(app);
222 }
223
224 static int load_module(void)
225 {
226         return ((ast_register_application_xml(app, dahdiras_exec)) ? AST_MODULE_LOAD_FAILURE : AST_MODULE_LOAD_SUCCESS);
227 }
228
229 AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "DAHDI ISDN Remote Access Server");
230
231