Wed Jan 8 2020 09:49:39

Asterisk developer's documentation


app_externalivr.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2005, Digium, Inc.
5  *
6  * Kevin P. Fleming <kpfleming@digium.com>
7  *
8  * Portions taken from the file-based music-on-hold work
9  * created by Anthony Minessale II in res_musiconhold.c
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 External IVR application interface
25  *
26  * \author Kevin P. Fleming <kpfleming@digium.com>
27  *
28  * \note Portions taken from the file-based music-on-hold work
29  * created by Anthony Minessale II in res_musiconhold.c
30  *
31  * \ingroup applications
32  */
33 
34 /*** MODULEINFO
35  <support_level>extended</support_level>
36  ***/
37 
38 #include "asterisk.h"
39 
40 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 362586 $")
41 
42 #include <signal.h>
43 
44 #include "asterisk/lock.h"
45 #include "asterisk/file.h"
46 #include "asterisk/channel.h"
47 #include "asterisk/pbx.h"
48 #include "asterisk/module.h"
49 #include "asterisk/linkedlists.h"
50 #include "asterisk/app.h"
51 #include "asterisk/utils.h"
52 #include "asterisk/tcptls.h"
53 #include "asterisk/astobj2.h"
54 
55 /*** DOCUMENTATION
56  <application name="ExternalIVR" language="en_US">
57  <synopsis>
58  Interfaces with an external IVR application.
59  </synopsis>
60  <syntax>
61  <parameter name="command|ivr://host" required="true" hasparams="true">
62  <argument name="arg1" />
63  <argument name="arg2" multiple="yes" />
64  </parameter>
65  <parameter name="options">
66  <optionlist>
67  <option name="n">
68  <para>Tells ExternalIVR() not to answer the channel.</para>
69  </option>
70  <option name="i">
71  <para>Tells ExternalIVR() not to send a hangup and exit when the
72  channel receives a hangup, instead it sends an <literal>I</literal>
73  informative message meaning that the external application MUST hang
74  up the call with an <literal>H</literal> command.</para>
75  </option>
76  <option name="d">
77  <para>Tells ExternalIVR() to run on a channel that has been hung up
78  and will not look for hangups. The external application must exit with
79  an <literal>E</literal> command.</para>
80  </option>
81  </optionlist>
82  </parameter>
83  </syntax>
84  <description>
85  <para>Either forks a process to run given command or makes a socket to connect
86  to given host and starts a generator on the channel. The generator's play list
87  is controlled by the external application, which can add and clear entries via
88  simple commands issued over its stdout. The external application will receive
89  all DTMF events received on the channel, and notification if the channel is
90  hung up. The received on the channel, and notification if the channel is hung
91  up. The application will not be forcibly terminated when the channel is hung up.
92  For more information see <filename>doc/AST.pdf</filename>.</para>
93  </description>
94  </application>
95  ***/
96 
97 static const char app[] = "ExternalIVR";
98 
99 /* XXX the parser in gcc 2.95 gets confused if you don't put a space between 'name' and the comma */
100 #define ast_chan_log(level, channel, format, ...) ast_log(level, "%s: " format, channel->name , ## __VA_ARGS__)
101 
102 /* Commands */
103 #define EIVR_CMD_APND 'A' /* append to prompt queue */
104 #define EIVR_CMD_DTMF 'D' /* send DTMF */
105 #define EIVR_CMD_EXIT 'E' /* exit */
106 #define EIVR_CMD_GET 'G' /* get channel varable(s) */
107 #define EIVR_CMD_HGUP 'H' /* hangup */
108 #define EIVR_CMD_LOG 'L' /* log message */
109 #define EIVR_CMD_OPT 'O' /* option */
110 #define EIVR_CMD_PARM 'P' /* return supplied params */
111 #define EIVR_CMD_SQUE 'S' /* (re)set prompt queue */
112 #define EIVR_CMD_ANS 'T' /* answer channel */
113 #define EIVR_CMD_SVAR 'V' /* set channel varable(s) */
114 #define EIVR_CMD_XIT 'X' /* exit **depricated** */
115 
117  noanswer = (1 << 0),
118  ignore_hangup = (1 << 1),
119  run_dead = (1 << 2),
120 };
121 
123  AST_APP_OPTION('n', noanswer),
125  AST_APP_OPTION('d', run_dead),
126 });
127 
130  char filename[1];
131 };
132 
134  struct ast_channel *chan;
137  int abort_current_sound;
138  int playing_silence;
139  int option_autoclear;
140  int gen_active;
141 };
142 
143 
144 struct gen_state {
145  struct ivr_localuser *u;
149 };
150 
151 static int eivr_comm(struct ast_channel *chan, struct ivr_localuser *u,
152  int *eivr_events_fd, int *eivr_commands_fd, int *eivr_errors_fd,
153  const struct ast_str *args, const struct ast_flags flags);
154 
155 int eivr_connect_socket(struct ast_channel *chan, const char *host, int port);
156 
157 static void send_eivr_event(FILE *handle, const char event, const char *data,
158  const struct ast_channel *chan)
159 {
160  struct ast_str *tmp = ast_str_create(12);
161 
162  ast_str_append(&tmp, 0, "%c,%10d", event, (int)time(NULL));
163  if (data) {
164  ast_str_append(&tmp, 0, ",%s", data);
165  }
166 
167  fprintf(handle, "%s\n", ast_str_buffer(tmp));
168  ast_debug(1, "sent '%s'\n", ast_str_buffer(tmp));
169  ast_free(tmp);
170 }
171 
172 static void *gen_alloc(struct ast_channel *chan, void *params)
173 {
174  struct ivr_localuser *u = params;
175  struct gen_state *state;
176 
177  if (!(state = ast_calloc(1, sizeof(*state))))
178  return NULL;
179 
180  state->u = u;
181 
182  return state;
183 }
184 
185 static void gen_closestream(struct gen_state *state)
186 {
187  if (!state->stream)
188  return;
189 
190  ast_closestream(state->stream);
191  state->u->chan->stream = NULL;
192  state->stream = NULL;
193 }
194 
195 static void gen_release(struct ast_channel *chan, void *data)
196 {
197  struct gen_state *state = data;
198 
199  gen_closestream(state);
200  ast_free(data);
201 }
202 
203 /* caller has the playlist locked */
204 static int gen_nextfile(struct gen_state *state)
205 {
206  struct ivr_localuser *u = state->u;
207  char *file_to_stream;
208 
209  u->abort_current_sound = 0;
210  u->playing_silence = 0;
211  gen_closestream(state);
212 
213  while (!state->stream) {
214  state->current = AST_LIST_FIRST(&u->playlist);
215  if (state->current) {
216  file_to_stream = state->current->filename;
217  } else {
218  file_to_stream = "silence/10";
219  u->playing_silence = 1;
220  }
221 
222  if (!(state->stream = ast_openstream_full(u->chan, file_to_stream, u->chan->language, 1))) {
223  ast_chan_log(LOG_WARNING, u->chan, "File '%s' could not be opened: %s\n", file_to_stream, strerror(errno));
224  AST_LIST_LOCK(&u->playlist);
225  AST_LIST_REMOVE_HEAD(&u->playlist, list);
227  if (!u->playing_silence) {
228  continue;
229  } else {
230  break;
231  }
232  }
233  }
234 
235  return (!state->stream);
236 }
237 
238 static struct ast_frame *gen_readframe(struct gen_state *state)
239 {
240  struct ast_frame *f = NULL;
241  struct ivr_localuser *u = state->u;
242 
243  if (u->abort_current_sound ||
244  (u->playing_silence && AST_LIST_FIRST(&u->playlist))) {
245  gen_closestream(state);
246  AST_LIST_LOCK(&u->playlist);
247  gen_nextfile(state);
249  }
250 
251  if (!(state->stream && (f = ast_readframe(state->stream)))) {
252  if (state->current) {
253  /* remove finished file from playlist */
254  AST_LIST_LOCK(&u->playlist);
255  AST_LIST_REMOVE_HEAD(&u->playlist, list);
257  /* add finished file to finishlist */
259  AST_LIST_INSERT_TAIL(&u->finishlist, state->current, list);
261  state->current = NULL;
262  }
263  if (!gen_nextfile(state))
264  f = ast_readframe(state->stream);
265  }
266 
267  return f;
268 }
269 
270 static int gen_generate(struct ast_channel *chan, void *data, int len, int samples)
271 {
272  struct gen_state *state = data;
273  struct ast_frame *f = NULL;
274  int res = 0;
275 
276  state->sample_queue += samples;
277 
278  while (state->sample_queue > 0) {
279  if (!(f = gen_readframe(state)))
280  return -1;
281 
282  res = ast_write(chan, f);
283  ast_frfree(f);
284  if (res < 0) {
285  ast_chan_log(LOG_WARNING, chan, "Failed to write frame: %s\n", strerror(errno));
286  return -1;
287  }
288  state->sample_queue -= f->samples;
289  }
290 
291  return res;
292 }
293 
294 static struct ast_generator gen =
295 {
296  .alloc = gen_alloc,
297  .release = gen_release,
298  .generate = gen_generate,
299 };
300 
301 static void ast_eivr_getvariable(struct ast_channel *chan, char *data, char *outbuf, int outbuflen)
302 {
303  /* original input data: "G,var1,var2," */
304  /* data passed as "data": "var1,var2" */
305 
306  char *inbuf, *variable;
307  const char *value;
308  int j;
309  struct ast_str *newstring = ast_str_alloca(outbuflen);
310 
311  outbuf[0] = '\0';
312 
313  for (j = 1, inbuf = data; ; j++) {
314  variable = strsep(&inbuf, ",");
315  if (variable == NULL) {
316  int outstrlen = strlen(outbuf);
317  if (outstrlen && outbuf[outstrlen - 1] == ',') {
318  outbuf[outstrlen - 1] = 0;
319  }
320  break;
321  }
322 
323  ast_channel_lock(chan);
324  if (!(value = pbx_builtin_getvar_helper(chan, variable))) {
325  value = "";
326  }
327 
328  ast_str_append(&newstring, 0, "%s=%s,", variable, value);
329  ast_channel_unlock(chan);
330  ast_copy_string(outbuf, ast_str_buffer(newstring), outbuflen);
331  }
332 }
333 
334 static void ast_eivr_setvariable(struct ast_channel *chan, char *data)
335 {
336  char *value;
337 
338  char *inbuf = ast_strdupa(data), *variable;
339 
340  for (variable = strsep(&inbuf, ","); variable; variable = strsep(&inbuf, ",")) {
341  ast_debug(1, "Setting up a variable: %s\n", variable);
342  /* variable contains "varname=value" */
343  value = strchr(variable, '=');
344  if (!value) {
345  value = "";
346  } else {
347  *value++ = '\0';
348  }
349  pbx_builtin_setvar_helper(chan, variable, value);
350  }
351 }
352 
353 static void ast_eivr_senddtmf(struct ast_channel *chan, char *vdata)
354 {
355 
356  char *data;
357  int dinterval = 0, duration = 0;
359  AST_APP_ARG(digits);
360  AST_APP_ARG(dinterval);
361  AST_APP_ARG(duration);
362  );
363 
364  data = ast_strdupa(vdata);
366 
367  if (!ast_strlen_zero(args.dinterval)) {
368  ast_app_parse_timelen(args.dinterval, &dinterval, TIMELEN_MILLISECONDS);
369  }
370  if (!ast_strlen_zero(args.duration)) {
371  ast_app_parse_timelen(args.duration, &duration, TIMELEN_MILLISECONDS);
372  }
373  ast_verb(4, "Sending DTMF: %s %d %d\n", args.digits, dinterval <= 0 ? 250 : dinterval, duration);
374  ast_dtmf_stream(chan, NULL, args.digits, dinterval <= 0 ? 250 : dinterval, duration);
375 }
376 
377 static struct playlist_entry *make_entry(const char *filename)
378 {
379  struct playlist_entry *entry;
380 
381  if (!(entry = ast_calloc(1, sizeof(*entry) + strlen(filename) + 10))) /* XXX why 10 ? */
382  return NULL;
383 
384  strcpy(entry->filename, filename);
385 
386  return entry;
387 }
388 
389 static int app_exec(struct ast_channel *chan, const char *data)
390 {
391  struct ast_flags flags = { 0, };
392  char *opts[0];
393  struct playlist_entry *entry;
394  int child_stdin[2] = { -1, -1 };
395  int child_stdout[2] = { -1, -1 };
396  int child_stderr[2] = { -1, -1 };
397  int res = -1;
398  int pid;
399 
400  char hostname[1024];
401  char *port_str = NULL;
402  int port = 0;
403  struct ast_tcptls_session_instance *ser = NULL;
404 
405  struct ivr_localuser foo = {
407  .finishlist = AST_LIST_HEAD_INIT_VALUE,
408  .gen_active = 0,
409  };
410  struct ivr_localuser *u = &foo;
411 
412  char *buf;
413  int j;
414  char *s, **app_args, *e;
415  struct ast_str *comma_delim_args = ast_str_alloca(100);
416 
417  AST_DECLARE_APP_ARGS(eivr_args,
418  AST_APP_ARG(application);
419  AST_APP_ARG(options);
420  );
421  AST_DECLARE_APP_ARGS(application_args,
422  AST_APP_ARG(cmd)[32];
423  );
424 
425  u->abort_current_sound = 0;
426  u->chan = chan;
427 
428  if (ast_strlen_zero(data)) {
429  ast_log(LOG_ERROR, "ExternalIVR requires a command to execute\n");
430  goto exit;
431  }
432 
433  buf = ast_strdupa(data);
434  AST_STANDARD_APP_ARGS(eivr_args, buf);
435 
436  ast_verb(4, "ExternalIVR received application and arguments: %s\n", eivr_args.application);
437  ast_verb(4, "ExternalIVR received options: %s\n", eivr_args.options);
438 
439  /* Parse out any application arguments */
440  if ((s = strchr(eivr_args.application, '('))) {
441  s[0] = ',';
442  if ((e = strrchr(s, ')'))) {
443  *e = '\0';
444  } else {
445  ast_log(LOG_ERROR, "Parse error, missing closing parenthesis\n");
446  goto exit;
447  }
448  }
449 
450  AST_STANDARD_APP_ARGS(application_args, eivr_args.application);
451  app_args = application_args.argv;
452 
453  /* Put the application + the arguments in a , delimited list */
454  ast_str_reset(comma_delim_args);
455  for (j = 0; application_args.cmd[j] != NULL; j++) {
456  ast_str_append(&comma_delim_args, 0, "%s%s", j == 0 ? "" : ",", application_args.cmd[j]);
457  }
458 
459  /* Get rid of any extraneous arguments */
460  if (eivr_args.options && (s = strchr(eivr_args.options, ','))) {
461  *s = '\0';
462  }
463 
464  /* Parse the ExternalIVR() arguments */
465  ast_verb(4, "Parsing options from: [%s]\n", eivr_args.options);
466  ast_app_parse_options(app_opts, &flags, opts, eivr_args.options);
467  if (ast_test_flag(&flags, noanswer)) {
468  ast_verb(4, "noanswer is set\n");
469  }
470  if (ast_test_flag(&flags, ignore_hangup)) {
471  ast_verb(4, "ignore_hangup is set\n");
472  }
473  if (ast_test_flag(&flags, run_dead)) {
474  ast_verb(4, "run_dead is set\n");
475  }
476 
477  if (!(ast_test_flag(&flags, noanswer))) {
478  ast_verb(3, "Answering channel and starting generator\n");
479  if (chan->_state != AST_STATE_UP) {
480  if (ast_test_flag(&flags, run_dead)) {
481  ast_chan_log(LOG_ERROR, chan, "Running ExternalIVR with 'd'ead flag on non-hungup channel isn't supported\n");
482  goto exit;
483  }
484  ast_answer(chan);
485  }
486  if (ast_activate_generator(chan, &gen, u) < 0) {
487  ast_chan_log(LOG_ERROR, chan, "Failed to activate generator\n");
488  goto exit;
489  } else {
490  u->gen_active = 1;
491  }
492  }
493 
494  if (!strncmp(app_args[0], "ivr://", 6)) {
495  struct ast_tcptls_session_args ivr_desc = {
496  .accept_fd = -1,
497  .name = "IVR",
498  };
499  struct ast_hostent hp;
500  struct sockaddr_in remote_address_tmp;
501 
502  /*communicate through socket to server*/
503  ast_debug(1, "Parsing hostname:port for socket connect from \"%s\"\n", app_args[0]);
504  ast_copy_string(hostname, app_args[0] + 6, sizeof(hostname));
505  if ((port_str = strchr(hostname, ':')) != NULL) {
506  port_str[0] = 0;
507  port_str += 1;
508  port = atoi(port_str);
509  }
510  if (!port) {
511  port = 2949; /* default port, if one is not provided */
512  }
513 
514  ast_gethostbyname(hostname, &hp);
515  remote_address_tmp.sin_family = AF_INET;
516  remote_address_tmp.sin_port = htons(port);
517  memcpy(&remote_address_tmp.sin_addr.s_addr, hp.hp.h_addr, hp.hp.h_length);
518  ast_sockaddr_from_sin(&ivr_desc.remote_address, &remote_address_tmp);
519  if (!(ser = ast_tcptls_client_create(&ivr_desc)) || !(ser = ast_tcptls_client_start(ser))) {
520  goto exit;
521  }
522  res = eivr_comm(chan, u, &ser->fd, &ser->fd, NULL, comma_delim_args, flags);
523 
524  } else {
525  if (pipe(child_stdin)) {
526  ast_chan_log(LOG_ERROR, chan, "Could not create pipe for child input: %s\n", strerror(errno));
527  goto exit;
528  }
529  if (pipe(child_stdout)) {
530  ast_chan_log(LOG_ERROR, chan, "Could not create pipe for child output: %s\n", strerror(errno));
531  goto exit;
532  }
533  if (pipe(child_stderr)) {
534  ast_chan_log(LOG_ERROR, chan, "Could not create pipe for child errors: %s\n", strerror(errno));
535  goto exit;
536  }
537 
538  pid = ast_safe_fork(0);
539  if (pid < 0) {
540  ast_log(LOG_ERROR, "Failed to fork(): %s\n", strerror(errno));
541  goto exit;
542  }
543 
544  if (!pid) {
545  /* child process */
547  ast_set_priority(0);
548 
549  dup2(child_stdin[0], STDIN_FILENO);
550  dup2(child_stdout[1], STDOUT_FILENO);
551  dup2(child_stderr[1], STDERR_FILENO);
552  ast_close_fds_above_n(STDERR_FILENO);
553  execv(app_args[0], app_args);
554  fprintf(stderr, "Failed to execute '%s': %s\n", app_args[0], strerror(errno));
555  _exit(1);
556  } else {
557  /* parent process */
558  close(child_stdin[0]);
559  child_stdin[0] = -1;
560  close(child_stdout[1]);
561  child_stdout[1] = -1;
562  close(child_stderr[1]);
563  child_stderr[1] = -1;
564  res = eivr_comm(chan, u, &child_stdin[1], &child_stdout[0], &child_stderr[0], comma_delim_args, flags);
565  }
566  }
567 
568  exit:
569  if (u->gen_active) {
571  }
572  if (child_stdin[0] > -1) {
573  close(child_stdin[0]);
574  }
575  if (child_stdin[1] > -1) {
576  close(child_stdin[1]);
577  }
578  if (child_stdout[0] > -1) {
579  close(child_stdout[0]);
580  }
581  if (child_stdout[1] > -1) {
582  close(child_stdout[1]);
583  }
584  if (child_stderr[0] > -1) {
585  close(child_stderr[0]);
586  }
587  if (child_stderr[1] > -1) {
588  close(child_stderr[1]);
589  }
590  if (ser) {
591  ao2_ref(ser, -1);
592  }
593  while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
594  ast_free(entry);
595  }
596  return res;
597 }
598 
599 static int eivr_comm(struct ast_channel *chan, struct ivr_localuser *u,
600  int *eivr_events_fd, int *eivr_commands_fd, int *eivr_errors_fd,
601  const struct ast_str *args, const struct ast_flags flags)
602 {
603  struct playlist_entry *entry;
604  struct ast_frame *f;
605  int ms;
606  int exception;
607  int ready_fd;
608  int waitfds[2] = { *eivr_commands_fd, (eivr_errors_fd) ? *eivr_errors_fd : -1 };
609  struct ast_channel *rchan;
610  int res = -1;
611  int test_available_fd = -1;
612  int hangup_info_sent = 0;
613 
614  FILE *eivr_commands = NULL;
615  FILE *eivr_errors = NULL;
616  FILE *eivr_events = NULL;
617 
618  if (!(eivr_events = fdopen(*eivr_events_fd, "w"))) {
619  ast_chan_log(LOG_ERROR, chan, "Could not open stream to send events\n");
620  goto exit;
621  }
622  if (!(eivr_commands = fdopen(*eivr_commands_fd, "r"))) {
623  ast_chan_log(LOG_ERROR, chan, "Could not open stream to receive commands\n");
624  goto exit;
625  }
626  if (eivr_errors_fd) { /* if opening a socket connection, error stream will not be used */
627  if (!(eivr_errors = fdopen(*eivr_errors_fd, "r"))) {
628  ast_chan_log(LOG_ERROR, chan, "Could not open stream to receive errors\n");
629  goto exit;
630  }
631  }
632 
633  test_available_fd = open("/dev/null", O_RDONLY);
634 
635  setvbuf(eivr_events, NULL, _IONBF, 0);
636  setvbuf(eivr_commands, NULL, _IONBF, 0);
637  if (eivr_errors) {
638  setvbuf(eivr_errors, NULL, _IONBF, 0);
639  }
640 
641  while (1) {
642  if (ast_test_flag(chan, AST_FLAG_ZOMBIE)) {
643  ast_chan_log(LOG_ERROR, chan, "Is a zombie\n");
644  break;
645  }
646  if (!hangup_info_sent && !(ast_test_flag(&flags, run_dead)) && ast_check_hangup(chan)) {
647  if (ast_test_flag(&flags, ignore_hangup)) {
648  ast_verb(3, "Got check_hangup, but ignore_hangup set so sending 'I' command\n");
649  send_eivr_event(eivr_events, 'I', "HANGUP", chan);
650  hangup_info_sent = 1;
651  } else {
652  ast_verb(3, "Got check_hangup\n");
653  send_eivr_event(eivr_events, 'H', NULL, chan);
654  break;
655  }
656  }
657 
658  ready_fd = 0;
659  ms = 100;
660  errno = 0;
661  exception = 0;
662 
663  rchan = ast_waitfor_nandfds(&chan, 1, waitfds, (eivr_errors_fd) ? 2 : 1, &exception, &ready_fd, &ms);
664 
665  if (chan->_state == AST_STATE_UP && !AST_LIST_EMPTY(&u->finishlist)) {
667  while ((entry = AST_LIST_REMOVE_HEAD(&u->finishlist, list))) {
668  send_eivr_event(eivr_events, 'F', entry->filename, chan);
669  ast_free(entry);
670  }
672  }
673 
674  if (chan->_state == AST_STATE_UP && !(ast_check_hangup(chan)) && rchan) {
675  /* the channel has something */
676  f = ast_read(chan);
677  if (!f) {
678  ast_verb(3, "Returned no frame\n");
679  send_eivr_event(eivr_events, 'H', NULL, chan);
680  break;
681  }
682  if (f->frametype == AST_FRAME_DTMF) {
683  send_eivr_event(eivr_events, f->subclass.integer, NULL, chan);
684  if (u->option_autoclear) {
685  AST_LIST_LOCK(&u->playlist);
686  if (!u->abort_current_sound && !u->playing_silence) {
687  /* send interrupted file as T data */
688  if ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
689  send_eivr_event(eivr_events, 'T', entry->filename, chan);
690  ast_free(entry);
691  }
692  }
693  while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
694  send_eivr_event(eivr_events, 'D', entry->filename, chan);
695  ast_free(entry);
696  }
697  if (!u->playing_silence)
698  u->abort_current_sound = 1;
700  }
701  } else if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass.integer == AST_CONTROL_HANGUP)) {
702  ast_verb(3, "Got AST_CONTROL_HANGUP\n");
703  send_eivr_event(eivr_events, 'H', NULL, chan);
704  if (f->data.uint32) {
705  chan->hangupcause = f->data.uint32;
706  }
707  ast_frfree(f);
708  break;
709  }
710  ast_frfree(f);
711  } else if (ready_fd == *eivr_commands_fd) {
712  char input[1024];
713 
714  if (exception || (dup2(*eivr_commands_fd, test_available_fd) == -1) || feof(eivr_commands)) {
715  ast_chan_log(LOG_ERROR, chan, "Child process went away\n");
716  break;
717  }
718 
719  if (!fgets(input, sizeof(input), eivr_commands)) {
720  continue;
721  }
722 
723  ast_strip(input);
724  ast_verb(4, "got command '%s'\n", input);
725 
726  if (strlen(input) < 3) {
727  continue;
728  }
729 
730  if (input[0] == EIVR_CMD_PARM) {
731  struct ast_str *tmp = (struct ast_str *) args;
732  send_eivr_event(eivr_events, 'P', ast_str_buffer(tmp), chan);
733  } else if (input[0] == EIVR_CMD_DTMF) {
734  ast_verb(4, "Sending DTMF: %s\n", &input[2]);
735  ast_eivr_senddtmf(chan, &input[2]);
736  } else if (input[0] == EIVR_CMD_ANS) {
737  ast_verb(3, "Answering channel if needed and starting generator\n");
738  if (chan->_state != AST_STATE_UP) {
739  if (ast_test_flag(&flags, run_dead)) {
740  ast_chan_log(LOG_WARNING, chan, "Running ExternalIVR with 'd'ead flag on non-hungup channel isn't supported\n");
741  send_eivr_event(eivr_events, 'Z', "ANSWER_FAILURE", chan);
742  continue;
743  }
744  if (ast_answer(chan)) {
745  ast_chan_log(LOG_WARNING, chan, "Failed to answer channel\n");
746  send_eivr_event(eivr_events, 'Z', "ANSWER_FAILURE", chan);
747  continue;
748  }
749  }
750  if (!(u->gen_active)) {
751  if (ast_activate_generator(chan, &gen, u) < 0) {
752  ast_chan_log(LOG_WARNING, chan, "Failed to activate generator\n");
753  send_eivr_event(eivr_events, 'Z', "GENERATOR_FAILURE", chan);
754  } else {
755  u->gen_active = 1;
756  }
757  }
758  } else if (input[0] == EIVR_CMD_SQUE) {
759  if (chan->_state != AST_STATE_UP || ast_check_hangup(chan)) {
760  ast_chan_log(LOG_WARNING, chan, "Queue re'S'et called on unanswered channel\n");
761  send_eivr_event(eivr_events, 'Z', NULL, chan);
762  continue;
763  }
764  if (!ast_fileexists(&input[2], NULL, u->chan->language)) {
765  ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]);
766  send_eivr_event(eivr_events, 'Z', &input[2], chan);
767  } else {
768  AST_LIST_LOCK(&u->playlist);
769  if (!u->abort_current_sound && !u->playing_silence) {
770  /* send interrupted file as T data */
771  if ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
772  send_eivr_event(eivr_events, 'T', entry->filename, chan);
773  ast_free(entry);
774  }
775  }
776  while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
777  send_eivr_event(eivr_events, 'D', entry->filename, chan);
778  ast_free(entry);
779  }
780  if (!u->playing_silence) {
781  u->abort_current_sound = 1;
782  }
783  entry = make_entry(&input[2]);
784  if (entry) {
785  AST_LIST_INSERT_TAIL(&u->playlist, entry, list);
786  }
788  }
789  } else if (input[0] == EIVR_CMD_APND) {
790  if (chan->_state != AST_STATE_UP || ast_check_hangup(chan)) {
791  ast_chan_log(LOG_WARNING, chan, "Queue 'A'ppend called on unanswered channel\n");
792  send_eivr_event(eivr_events, 'Z', NULL, chan);
793  continue;
794  }
795  if (!ast_fileexists(&input[2], NULL, u->chan->language)) {
796  ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]);
797  send_eivr_event(eivr_events, 'Z', &input[2], chan);
798  } else {
799  entry = make_entry(&input[2]);
800  if (entry) {
801  AST_LIST_LOCK(&u->playlist);
802  AST_LIST_INSERT_TAIL(&u->playlist, entry, list);
804  }
805  }
806  } else if (input[0] == EIVR_CMD_GET) {
807  char response[2048];
808  ast_verb(4, "Retriving Variables from channel: %s\n", &input[2]);
809  ast_eivr_getvariable(chan, &input[2], response, sizeof(response));
810  send_eivr_event(eivr_events, 'G', response, chan);
811  } else if (input[0] == EIVR_CMD_SVAR) {
812  ast_verb(4, "Setting Variables in channel: %s\n", &input[2]);
813  ast_eivr_setvariable(chan, &input[2]);
814  } else if (input[0] == EIVR_CMD_LOG) {
815  ast_chan_log(LOG_NOTICE, chan, "Log message from EIVR: %s\n", &input[2]);
816  } else if (input[0] == EIVR_CMD_XIT) {
817  ast_chan_log(LOG_NOTICE, chan, "Exiting: %s\n", &input[2]);
818  ast_chan_log(LOG_WARNING, chan, "e'X'it command is depricated, use 'E'xit instead\n");
819  res = 0;
820  break;
821  } else if (input[0] == EIVR_CMD_EXIT) {
822  ast_chan_log(LOG_NOTICE, chan, "Exiting: %s\n", &input[2]);
823  send_eivr_event(eivr_events, 'E', NULL, chan);
824  res = 0;
825  break;
826  } else if (input[0] == EIVR_CMD_HGUP) {
827  ast_chan_log(LOG_NOTICE, chan, "Hanging up: %s\n", &input[2]);
828  send_eivr_event(eivr_events, 'H', NULL, chan);
829  break;
830  } else if (input[0] == EIVR_CMD_OPT) {
831  if (chan->_state != AST_STATE_UP || ast_check_hangup(chan)) {
832  ast_chan_log(LOG_WARNING, chan, "Option called on unanswered channel\n");
833  send_eivr_event(eivr_events, 'Z', NULL, chan);
834  continue;
835  }
836  if (!strcasecmp(&input[2], "autoclear"))
837  u->option_autoclear = 1;
838  else if (!strcasecmp(&input[2], "noautoclear"))
839  u->option_autoclear = 0;
840  else
841  ast_chan_log(LOG_WARNING, chan, "Unknown option requested: %s\n", &input[2]);
842  }
843  } else if (eivr_errors_fd && (ready_fd == *eivr_errors_fd)) {
844  char input[1024];
845 
846  if (exception || feof(eivr_errors)) {
847  ast_chan_log(LOG_ERROR, chan, "Child process went away\n");
848  break;
849  }
850  if (fgets(input, sizeof(input), eivr_errors)) {
851  ast_chan_log(LOG_NOTICE, chan, "stderr: %s\n", ast_strip(input));
852  }
853  } else if ((ready_fd < 0) && ms) {
854  if (errno == 0 || errno == EINTR)
855  continue;
856 
857  ast_chan_log(LOG_ERROR, chan, "Wait failed (%s)\n", strerror(errno));
858  break;
859  }
860  }
861 
862  exit:
863  if (test_available_fd > -1) {
864  close(test_available_fd);
865  }
866  if (eivr_events) {
867  fclose(eivr_events);
868  *eivr_events_fd = -1;
869  }
870  if (eivr_commands) {
871  fclose(eivr_commands);
872  *eivr_commands_fd = -1;
873  }
874  if (eivr_errors) {
875  fclose(eivr_errors);
876  *eivr_errors_fd = -1;
877  }
878  return res;
879 }
880 
881 static int unload_module(void)
882 {
883  return ast_unregister_application(app);
884 }
885 
886 static int load_module(void)
887 {
889 }
890 
891 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "External IVR Interface Application");
union ast_frame_subclass subclass
Definition: frame.h:146
enum sip_cc_notify_state state
Definition: chan_sip.c:842
#define ast_channel_lock(chan)
Definition: channel.h:2466
Main Channel structure associated with a channel.
Definition: channel.h:742
#define AST_MODULE_INFO_STANDARD(keystr, desc)
Definition: module.h:396
#define AST_LIST_LOCK(head)
Locks a list.
Definition: linkedlists.h:39
Asterisk locking-related definitions:
Asterisk main include file. File version handling, generic pbx functions.
#define AST_LIST_FIRST(head)
Returns the first entry contained in a list.
Definition: linkedlists.h:420
#define AST_APP_OPTIONS(holder, options...)
Declares an array of options for an application.
Definition: app.h:712
#define AST_LIST_HEAD(name, type)
Defines a structure to be used to hold a list of specified type.
Definition: linkedlists.h:172
char * strsep(char **str, const char *delims)
static void ast_eivr_getvariable(struct ast_channel *chan, char *data, char *outbuf, int outbuflen)
int ast_safe_fork(int stop_reaper)
Common routine to safely fork without a chance of a signal handler firing badly in the child...
Definition: app.c:2242
static void * gen_alloc(struct ast_channel *chan, void *params)
struct ivr_localuser * u
#define EIVR_CMD_XIT
static int eivr_comm(struct ast_channel *chan, struct ivr_localuser *u, int *eivr_events_fd, int *eivr_commands_fd, int *eivr_errors_fd, const struct ast_str *args, const struct ast_flags flags)
options_flags
struct ivr_localuser::playlist playlist
#define ast_test_flag(p, flag)
Definition: utils.h:63
int ast_activate_generator(struct ast_channel *chan, struct ast_generator *gen, void *params)
Definition: channel.c:3148
static int gen_generate(struct ast_channel *chan, void *data, int len, int samples)
static const char app[]
int ast_app_parse_timelen(const char *timestr, int *result, enum ast_timelen defunit)
Common routine to parse time lengths, with optional time unit specifier.
Definition: app.c:2311
#define LOG_WARNING
Definition: logger.h:144
#define AST_LIST_UNLOCK(head)
Attempts to unlock a list.
Definition: linkedlists.h:139
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:497
#define ast_chan_log(level, channel, format,...)
int ast_app_parse_options(const struct ast_app_option *options, struct ast_flags *flags, char **args, char *optstr)
Parses a string containing application options and sets flags/arguments.
Definition: app.c:2101
#define AST_FRAME_DTMF
Definition: frame.h:128
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application&#39;s arguments.
Definition: app.h:572
#define EIVR_CMD_SVAR
#define EIVR_CMD_PARM
int eivr_connect_socket(struct ast_channel *chan, const char *host, int port)
struct ast_frame * ast_read(struct ast_channel *chan)
Reads a frame.
Definition: channel.c:4383
struct playlist_entry * current
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
Definition: strings.h:900
struct ast_str * ast_str_create(size_t init_len)
Create a malloc&#39;ed dynamic length string.
Definition: strings.h:420
arguments for the accepting thread
Definition: tcptls.h:123
struct ast_filestream * ast_openstream_full(struct ast_channel *chan, const char *filename, const char *preflang, int asis)
Opens stream for use in seeking, playing.
Definition: file.c:641
#define AST_LIST_EMPTY(head)
Checks whether the specified list contains any entries.
Definition: linkedlists.h:449
#define ast_str_alloca(init_len)
Definition: strings.h:608
Generic File Format Support. Should be included by clients of the file handling routines. File service providers should instead include mod_format.h.
static int inbuf(struct baseio *bio, FILE *fi)
utility used by inchar(), for base_encode()
static int load_module(void)
int value
Definition: syslog.c:39
Generic support for tcp/tls servers in Asterisk.
static int input(yyscan_t yyscanner)
Definition: ast_expr2f.c:1575
int ast_unregister_application(const char *app)
Unregister an application.
Definition: pbx.c:7705
struct ast_channel * ast_waitfor_nandfds(struct ast_channel **chan, int n, int *fds, int nfds, int *exception, int *outfd, int *ms)
Waits for activity on a group of channels.
Definition: channel.c:3188
#define ast_verb(level,...)
Definition: logger.h:243
static struct ast_frame * gen_readframe(struct gen_state *state)
const char * pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name)
Return a pointer to the value of the corresponding channel variable.
Definition: pbx.c:10475
static struct ast_app_option app_opts[128]
Utility functions.
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:236
General Asterisk PBX channel definitions.
#define ast_sockaddr_from_sin(addr, sin)
Converts a struct sockaddr_in to a struct ast_sockaddr.
Definition: netsock2.h:642
static struct ast_generator gen
static int gen_nextfile(struct gen_state *state)
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:63
struct playlist_entry::@15 list
char * ast_strip(char *s)
Strip leading/trailing whitespace from a string.
Definition: strings.h:155
static void ast_eivr_senddtmf(struct ast_channel *chan, char *vdata)
struct ast_sockaddr remote_address
Definition: tcptls.h:126
#define ao2_ref(o, delta)
Definition: astobj2.h:472
#define EIVR_CMD_APND
A set of macros to manage forward-linked lists.
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:818
Core PBX routines and definitions.
static void gen_closestream(struct gen_state *state)
int ast_check_hangup(struct ast_channel *chan)
Check to see if a channel is needing hang up.
Definition: channel.c:806
int ast_set_priority(int)
We set ourselves to a high priority, that we might pre-empt everything else. If your PBX has heavy ac...
Definition: asterisk.c:1650
static int app_exec(struct ast_channel *chan, const char *data)
static int unload_module(void)
#define EIVR_CMD_EXIT
static void gen_release(struct ast_channel *chan, void *data)
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: utils.h:663
#define LOG_ERROR
Definition: logger.h:155
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:716
#define EIVR_CMD_OPT
The descriptor of a dynamic string XXX storage will be optimized later if needed We use the ts field ...
Definition: strings.h:364
struct ast_filestream * stream
#define EIVR_CMD_DTMF
static struct @350 args
struct ast_frame * ast_readframe(struct ast_filestream *s)
Read a frame from a filestream.
Definition: file.c:737
#define EIVR_CMD_ANS
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
enum ast_channel_state _state
Definition: channel.h:839
void ast_log(int level, const char *file, int line, const char *function, const char *fmt,...)
Used for sending a log message This is the standard logger function. Probably the only way you will i...
Definition: logger.c:1207
#define LOG_NOTICE
Definition: logger.h:133
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
Definition: linkedlists.h:409
#define ast_channel_unlock(chan)
Definition: channel.h:2467
void *(* alloc)(struct ast_channel *chan, void *params)
Definition: channel.h:180
int errno
#define ast_free(a)
Definition: astmm.h:97
int ast_closestream(struct ast_filestream *f)
Closes a stream.
Definition: file.c:904
void ast_close_fds_above_n(int n)
Common routine for child processes, to close all fds prior to exec(2)
Definition: app.c:2237
static struct ast_format f[]
Definition: format_g726.c:181
int ast_write(struct ast_channel *chan, struct ast_frame *frame)
Write a frame to a channel This function writes the given frame to the indicated channel.
Definition: channel.c:4916
Structure used to handle boolean flags.
Definition: utils.h:200
int pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
Add a variable to the channel variable stack, removing the most recently set value for the same name...
Definition: pbx.c:10546
void ast_str_reset(struct ast_str *buf)
Reset the content of a dynamic string. Useful before a series of ast_str_append.
Definition: strings.h:436
void ast_deactivate_generator(struct ast_channel *chan)
Definition: channel.c:3107
char filename[1]
struct hostent * ast_gethostbyname(const char *host, struct ast_hostent *hp)
Thread-safe gethostbyname function to use in Asterisk.
Definition: utils.c:195
This structure is allocated by file.c in one chunk, together with buf_size and desc_size bytes of mem...
Definition: mod_format.h:100
#define ast_calloc(a, b)
Definition: astmm.h:82
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:223
#define EIVR_CMD_SQUE
struct ast_tcptls_session_instance * ast_tcptls_client_create(struct ast_tcptls_session_args *desc)
Definition: tcptls.c:902
int ast_fileexists(const char *filename, const char *fmt, const char *preflang)
Checks for the existence of a given file.
Definition: file.c:919
int ast_answer(struct ast_channel *chan)
Answer a channel.
Definition: channel.c:3086
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
Data structure associated with a single frame of data.
Definition: frame.h:142
int hangupcause
Definition: channel.h:849
struct ivr_localuser::finishlist finishlist
struct ast_tcptls_session_instance * ast_tcptls_client_start(struct ast_tcptls_session_instance *tcptls_session)
attempts to connect and start tcptls session, on error the tcptls_session&#39;s ref count is decremented...
Definition: tcptls.c:865
#define EIVR_CMD_LOG
uint32_t uint32
Definition: frame.h:160
#define AST_APP_ARG(name)
Define an application argument.
Definition: app.h:555
enum ast_frame_type frametype
Definition: frame.h:144
static void send_eivr_event(FILE *handle, const char event, const char *data, const struct ast_channel *chan)
#define ast_frfree(fr)
Definition: frame.h:583
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the &#39;standard&#39; argument separation process for an application.
Definition: app.h:604
int ast_dtmf_stream(struct ast_channel *chan, struct ast_channel *peer, const char *digits, int between, unsigned int duration)
Send DTMF to a channel.
Definition: app.c:501
#define AST_LIST_HEAD_INIT_VALUE
Defines initial values for a declaration of AST_LIST_HEAD.
Definition: linkedlists.h:233
struct ast_filestream * stream
Definition: channel.h:757
struct hostent hp
Definition: utils.h:211
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:38
Asterisk module definitions.
union ast_frame::@172 data
static void ast_eivr_setvariable(struct ast_channel *chan, char *data)
static struct playlist_entry * make_entry(const char *filename)
#define EIVR_CMD_HGUP
const ast_string_field language
Definition: channel.h:787
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition: module.h:437
#define ASTERISK_FILE_VERSION(file, version)
Register/unregister a source code file with the core.
Definition: asterisk.h:180
int samples
Definition: frame.h:150
#define ast_opt_high_priority
Definition: options.h:108
#define EIVR_CMD_GET
#define AST_APP_OPTION(option, flagno)
Declares an application option that does not accept an argument.
Definition: app.h:721
static char hostname[MAXHOSTNAMELEN]
Definition: logger.c:91
struct ast_channel * chan