Asterisk - The Open Source Telephony Project  18.5.0
res_monitor.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  * Mark Spencer <[email protected]>
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 PBX channel monitoring
22  *
23  * \author Mark Spencer <[email protected]>
24  */
25 
26 /*** MODULEINFO
27  <use type="module">func_periodic_hook</use>
28  <support_level>deprecated</support_level>
29  <replacement>app_mixmonitor</replacement>
30  ***/
31 
32 #include "asterisk.h"
33 
34 #include <sys/stat.h>
35 #include <libgen.h>
36 
37 #include "asterisk/paths.h" /* use ast_config_AST_MONITOR_DIR */
38 #include "asterisk/lock.h"
39 #include "asterisk/channel.h"
40 #include "asterisk/file.h"
41 #include "asterisk/pbx.h"
42 #include "asterisk/module.h"
43 #include "asterisk/cli.h"
44 #include "asterisk/manager.h"
45 #include "asterisk/stasis.h"
47 #define AST_API_MODULE
48 #include "asterisk/monitor.h"
49 #undef AST_API_MODULE
50 #include "asterisk/app.h"
51 #include "asterisk/utils.h"
52 #include "asterisk/config.h"
53 #include "asterisk/options.h"
54 #include "asterisk/beep.h"
55 
56 /*** DOCUMENTATION
57  <application name="Monitor" language="en_US">
58  <synopsis>
59  Monitor a channel.
60  </synopsis>
61  <syntax>
62  <parameter name="file_format" argsep=":">
63  <argument name="file_format" required="true">
64  <para>Optional. If not set, defaults to <literal>wav</literal></para>
65  </argument>
66  <argument name="urlbase" />
67  </parameter>
68  <parameter name="fname_base">
69  <para>If set, changes the filename used to the one specified.</para>
70  </parameter>
71  <parameter name="options">
72  <optionlist>
73  <option name="m">
74  <para>When the recording ends mix the two leg files into one and
75  delete the two leg files. If the variable <variable>MONITOR_EXEC</variable>
76  is set, the application referenced in it will be executed instead of
77  soxmix/sox and the raw leg files will NOT be deleted automatically.
78  soxmix/sox or <variable>MONITOR_EXEC</variable> is handed 3 arguments,
79  the two leg files and a target mixed file name which is the same as
80  the leg file names only without the in/out designator.</para>
81  <para>If <variable>MONITOR_EXEC_ARGS</variable> is set, the contents
82  will be passed on as additional arguments to <variable>MONITOR_EXEC</variable>.
83  Both <variable>MONITOR_EXEC</variable> and the Mix flag can be set from the
84  administrator interface.</para>
85  <warning><para>Do not use untrusted strings such as
86  <variable>CALLERID(num)</variable> or <variable>CALLERID(name)</variable>
87  as part of <variable>MONITOR_EXEC</variable> or
88  <variable>MONITOR_EXEC_ARGS</variable>. You risk a command injection
89  attack executing arbitrary commands if the untrusted strings aren't
90  filtered to remove dangerous characters. See function
91  <variable>FILTER()</variable>.</para></warning>
92  </option>
93  <option name="b">
94  <para>Don't begin recording unless a call is bridged to another channel.</para>
95  </option>
96  <option name="B">
97  <para>Play a periodic beep while this call is being recorded.</para>
98  <argument name="interval"><para>Interval, in seconds. Default is 15.</para></argument>
99  </option>
100  <option name="i">
101  <para>Skip recording of input stream (disables <literal>m</literal> option).</para>
102  </option>
103  <option name="o">
104  <para>Skip recording of output stream (disables <literal>m</literal> option).</para>
105  </option>
106  </optionlist>
107  </parameter>
108  </syntax>
109  <description>
110  <para>Used to start monitoring a channel. The channel's input and output
111  voice packets are logged to files until the channel hangs up or
112  monitoring is stopped by the StopMonitor application.</para>
113  <para>By default, files are stored to <filename>/var/spool/asterisk/monitor/</filename>.
114  Returns <literal>-1</literal> if monitor files can't be opened or if the channel is
115  already monitored, otherwise <literal>0</literal>.</para>
116  </description>
117  <see-also>
118  <ref type="application">StopMonitor</ref>
119  </see-also>
120  </application>
121  <application name="StopMonitor" language="en_US">
122  <synopsis>
123  Stop monitoring a channel.
124  </synopsis>
125  <syntax />
126  <description>
127  <para>Stops monitoring a channel. Has no effect if the channel is not monitored.</para>
128  </description>
129  </application>
130  <application name="ChangeMonitor" language="en_US">
131  <synopsis>
132  Change monitoring filename of a channel.
133  </synopsis>
134  <syntax>
135  <parameter name="filename_base" required="true">
136  <para>The new filename base to use for monitoring this channel.</para>
137  </parameter>
138  </syntax>
139  <description>
140  <para>Changes monitoring filename of a channel. Has no effect if the
141  channel is not monitored.</para>
142  </description>
143  </application>
144  <application name="PauseMonitor" language="en_US">
145  <synopsis>
146  Pause monitoring of a channel.
147  </synopsis>
148  <syntax />
149  <description>
150  <para>Pauses monitoring of a channel until it is re-enabled by a call to UnpauseMonitor.</para>
151  </description>
152  <see-also>
153  <ref type="application">UnpauseMonitor</ref>
154  </see-also>
155  </application>
156  <application name="UnpauseMonitor" language="en_US">
157  <synopsis>
158  Unpause monitoring of a channel.
159  </synopsis>
160  <syntax />
161  <description>
162  <para>Unpauses monitoring of a channel on which monitoring had
163  previously been paused with PauseMonitor.</para>
164  </description>
165  <see-also>
166  <ref type="application">PauseMonitor</ref>
167  </see-also>
168  </application>
169  <manager name="Monitor" language="en_US">
170  <synopsis>
171  Monitor a channel.
172  </synopsis>
173  <syntax>
174  <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
175  <parameter name="Channel" required="true">
176  <para>Used to specify the channel to record.</para>
177  </parameter>
178  <parameter name="File">
179  <para>Is the name of the file created in the monitor spool directory.
180  Defaults to the same name as the channel (with slashes replaced with dashes).</para>
181  </parameter>
182  <parameter name="Format">
183  <para>Is the audio recording format. Defaults to <literal>wav</literal>.</para>
184  </parameter>
185  <parameter name="Mix">
186  <para>Boolean parameter as to whether to mix the input and output channels
187  together after the recording is finished.</para>
188  </parameter>
189  </syntax>
190  <description>
191  <para>This action may be used to record the audio on a
192  specified channel.</para>
193  </description>
194  </manager>
195  <manager name="StopMonitor" language="en_US">
196  <synopsis>
197  Stop monitoring a channel.
198  </synopsis>
199  <syntax>
200  <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
201  <parameter name="Channel" required="true">
202  <para>The name of the channel monitored.</para>
203  </parameter>
204  </syntax>
205  <description>
206  <para>This action may be used to end a previously started 'Monitor' action.</para>
207  </description>
208  </manager>
209  <manager name="ChangeMonitor" language="en_US">
210  <synopsis>
211  Change monitoring filename of a channel.
212  </synopsis>
213  <syntax>
214  <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
215  <parameter name="Channel" required="true">
216  <para>Used to specify the channel to record.</para>
217  </parameter>
218  <parameter name="File" required="true">
219  <para>Is the new name of the file created in the
220  monitor spool directory.</para>
221  </parameter>
222  </syntax>
223  <description>
224  <para>This action may be used to change the file
225  started by a previous 'Monitor' action.</para>
226  </description>
227  </manager>
228  <manager name="PauseMonitor" language="en_US">
229  <synopsis>
230  Pause monitoring of a channel.
231  </synopsis>
232  <syntax>
233  <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
234  <parameter name="Channel" required="true">
235  <para>Used to specify the channel to record.</para>
236  </parameter>
237  </syntax>
238  <description>
239  <para>This action may be used to temporarily stop the
240  recording of a channel.</para>
241  </description>
242  </manager>
243  <manager name="UnpauseMonitor" language="en_US">
244  <synopsis>
245  Unpause monitoring of a channel.
246  </synopsis>
247  <syntax>
248  <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
249  <parameter name="Channel" required="true">
250  <para>Used to specify the channel to record.</para>
251  </parameter>
252  </syntax>
253  <description>
254  <para>This action may be used to re-enable recording
255  of a channel after calling PauseMonitor.</para>
256  </description>
257  </manager>
258 
259  ***/
260 
262 
263 #define LOCK_IF_NEEDED(lock, needed) do { \
264  if (needed) \
265  ast_channel_lock(lock); \
266  } while(0)
267 
268 #define UNLOCK_IF_NEEDED(lock, needed) do { \
269  if (needed) \
270  ast_channel_unlock(lock); \
271  } while (0)
272 
273 static unsigned long seq = 0;
274 
275 /*!
276  * \brief Change state of monitored channel
277  * \param chan
278  * \param state monitor state
279  * \retval 0 on success.
280  * \retval -1 on failure.
281 */
282 static int ast_monitor_set_state(struct ast_channel *chan, int state)
283 {
284  LOCK_IF_NEEDED(chan, 1);
285  if (!ast_channel_monitor(chan)) {
286  UNLOCK_IF_NEEDED(chan, 1);
287  return -1;
288  }
290  UNLOCK_IF_NEEDED(chan, 1);
291  return 0;
292 }
293 
294 /*! \brief Start monitoring a channel
295  * \param chan ast_channel struct to record
296  * \param format_spec file format to use for recording
297  * \param fname_base filename base to record to
298  * \param need_lock whether to lock the channel mutex
299  * \param stream_action whether to record the input and/or output streams. X_REC_IN | X_REC_OUT is most often used
300  * Creates the file to record, if no format is specified it assumes WAV
301  * It also sets channel variable __MONITORED=yes
302  * \retval 0 on success
303  * \retval -1 on failure
304  */
305 int AST_OPTIONAL_API_NAME(ast_monitor_start)(struct ast_channel *chan, const char *format_spec,
306  const char *fname_base, int need_lock, int stream_action,
307  const char *beep_id)
308 {
309  int res = 0;
311 
312  LOCK_IF_NEEDED(chan, need_lock);
313 
314  if (!(ast_channel_monitor(chan))) {
316  char *channel_name, *p;
317 
318  /* Create monitoring directory if needed */
320 
321  if (!(monitor = ast_calloc(1, sizeof(*monitor)))) {
322  UNLOCK_IF_NEEDED(chan, need_lock);
323  return -1;
324  }
325 
326  if (!ast_strlen_zero(beep_id)) {
327  ast_copy_string(monitor->beep_id, beep_id, sizeof(monitor->beep_id));
328  }
329 
330  /* Determine file names */
331  if (!ast_strlen_zero(fname_base)) {
332  int directory = strchr(fname_base, '/') ? 1 : 0;
333  const char *absolute = *fname_base == '/' ? "" : ast_config_AST_MONITOR_DIR;
334  const char *absolute_suffix = *fname_base == '/' ? "" : "/";
335 
336  snprintf(monitor->read_filename, FILENAME_MAX, "%s%s%s-in",
337  absolute, absolute_suffix, fname_base);
338  snprintf(monitor->write_filename, FILENAME_MAX, "%s%s%s-out",
339  absolute, absolute_suffix, fname_base);
340  snprintf(monitor->filename_base, FILENAME_MAX, "%s%s%s",
341  absolute, absolute_suffix, fname_base);
342 
343  /* try creating the directory just in case it doesn't exist */
344  if (directory) {
345  char *name = ast_strdupa(monitor->filename_base);
346  ast_mkdir(dirname(name), 0777);
347  }
348  } else {
350  snprintf(monitor->read_filename, FILENAME_MAX, "%s/audio-in-%lu",
352  snprintf(monitor->write_filename, FILENAME_MAX, "%s/audio-out-%lu",
354  seq++;
356 
357  /* Replace all '/' chars from the channel name with '-' chars. */
358  channel_name = ast_strdupa(ast_channel_name(chan));
359  for (p = channel_name; (p = strchr(p, '/')); ) {
360  *p = '-';
361  }
362 
363  snprintf(monitor->filename_base, FILENAME_MAX, "%s/%d-%s",
364  ast_config_AST_MONITOR_DIR, (int)time(NULL), channel_name);
365  monitor->filename_changed = 1;
366  }
367 
368  monitor->stop = ast_monitor_stop;
369 
370  /* Determine file format */
371  if (!ast_strlen_zero(format_spec)) {
372  monitor->format = ast_strdup(format_spec);
373  } else {
374  monitor->format = ast_strdup("wav");
375  }
376 
377  /* open files */
378  if (stream_action & X_REC_IN) {
379  if (ast_fileexists(monitor->read_filename, NULL, NULL) > 0)
380  ast_filedelete(monitor->read_filename, NULL);
381  if (!(monitor->read_stream = ast_writefile(monitor->read_filename,
382  monitor->format, NULL,
383  O_CREAT|O_TRUNC|O_WRONLY, 0, AST_FILE_MODE))) {
384  ast_log(LOG_WARNING, "Could not create file %s\n",
385  monitor->read_filename);
386  ast_free(monitor);
387  UNLOCK_IF_NEEDED(chan, need_lock);
388  return -1;
389  }
390  } else
391  monitor->read_stream = NULL;
392 
393  if (stream_action & X_REC_OUT) {
394  if (ast_fileexists(monitor->write_filename, NULL, NULL) > 0) {
396  }
397  if (!(monitor->write_stream = ast_writefile(monitor->write_filename,
398  monitor->format, NULL,
399  O_CREAT|O_TRUNC|O_WRONLY, 0, AST_FILE_MODE))) {
400  ast_log(LOG_WARNING, "Could not create file %s\n",
401  monitor->write_filename);
402  if (monitor->read_stream) {
403  ast_closestream(monitor->read_stream);
404  }
405  ast_free(monitor);
406  UNLOCK_IF_NEEDED(chan, need_lock);
407  return -1;
408  }
409  } else
410  monitor->write_stream = NULL;
411 
412  ast_channel_insmpl_set(chan, 0);
413  ast_channel_outsmpl_set(chan, 0);
414  ast_channel_monitor_set(chan, monitor);
416  /* so we know this call has been monitored in case we need to bill for it or something */
417  pbx_builtin_setvar_helper(chan, "__MONITORED","true");
418 
421  NULL);
422  if (message) {
424  }
425  } else {
426  ast_debug(1,"Cannot start monitoring %s, already monitored\n", ast_channel_name(chan));
427  res = -1;
428  }
429 
430  UNLOCK_IF_NEEDED(chan, need_lock);
431 
432  return res;
433 }
434 
435 /*!
436  * \brief Get audio format.
437  * \param format recording format.
438  * The file format extensions that Asterisk uses are not all the same as that
439  * which soxmix expects. This function ensures that the format used as the
440  * extension on the filename is something soxmix will understand.
441  */
442 static const char *get_soxmix_format(const char *format)
443 {
444  const char *res = format;
445 
446  if (!strcasecmp(format,"ulaw"))
447  res = "ul";
448  if (!strcasecmp(format,"alaw"))
449  res = "al";
450 
451  return res;
452 }
453 
454 /*!
455  * \brief Stop monitoring channel
456  * \param chan
457  * \param need_lock
458  * Stop the recording, close any open streams, mix in/out channels if required
459  * \return Always 0
460 */
461 int AST_OPTIONAL_API_NAME(ast_monitor_stop)(struct ast_channel *chan, int need_lock)
462 {
463  int delfiles = 0;
465 
466  LOCK_IF_NEEDED(chan, need_lock);
467 
468  if (ast_channel_monitor(chan)) {
469  RAII_VAR(struct ast_str *, tmp, ast_str_create(1024), ast_free);
470 
471  if (ast_channel_monitor(chan)->read_stream) {
472  ast_closestream(ast_channel_monitor(chan)->read_stream);
473  }
474  if (ast_channel_monitor(chan)->write_stream) {
476  }
477 
478  if (tmp && ast_channel_monitor(chan)->filename_changed && !ast_strlen_zero(ast_channel_monitor(chan)->filename_base)) {
479  if (ast_fileexists(ast_channel_monitor(chan)->read_filename,NULL,NULL) > 0) {
480  ast_str_set(&tmp, 0, "%s-in", ast_channel_monitor(chan)->filename_base);
481  if (ast_fileexists(ast_str_buffer(tmp), NULL, NULL) > 0) {
483  }
485  } else {
486  ast_log(LOG_WARNING, "File %s not found\n", ast_channel_monitor(chan)->read_filename);
487  }
488 
489  if (tmp && ast_fileexists(ast_channel_monitor(chan)->write_filename,NULL,NULL) > 0) {
490  ast_str_set(&tmp, 0, "%s-out", ast_channel_monitor(chan)->filename_base);
491  if (ast_fileexists(ast_str_buffer(tmp), NULL, NULL) > 0) {
493  }
495  } else {
496  ast_log(LOG_WARNING, "File %s not found\n", ast_channel_monitor(chan)->write_filename);
497  }
498  }
499 
500  if (tmp && ast_channel_monitor(chan)->joinfiles && !ast_strlen_zero(ast_channel_monitor(chan)->filename_base)) {
501  const char *format = !strcasecmp(ast_channel_monitor(chan)->format,"wav49") ? "WAV" : ast_channel_monitor(chan)->format;
502  char *fname_base = ast_channel_monitor(chan)->filename_base;
503  const char *execute, *execute_args;
504  /* at this point, fname_base really is the full path */
505 
506  /* Set the execute application */
507  execute = pbx_builtin_getvar_helper(chan, "MONITOR_EXEC");
508  if (ast_strlen_zero(execute)) {
509 #ifdef HAVE_SOXMIX
510  execute = "nice -n 19 soxmix";
511 #else
512  execute = "nice -n 19 sox -m";
513 #endif
514  format = get_soxmix_format(format);
515  delfiles = 1;
516  }
517  execute_args = pbx_builtin_getvar_helper(chan, "MONITOR_EXEC_ARGS");
518  if (ast_strlen_zero(execute_args)) {
519  execute_args = "";
520  }
521 
522  ast_str_set(&tmp, 0, delfiles ? "( " : "");
523  ast_str_append(&tmp, 0, "%s \"%s-in.%s\" \"%s-out.%s\" \"%s.%s\" %s &",
524  execute, fname_base, format, fname_base, format, fname_base, format,execute_args);
525  if (delfiles) {
526  /* remove legs when done mixing */
527  ast_str_append(&tmp, 0, "& rm -f \"%s-\"* ) &", fname_base);
528  }
529  ast_debug(1,"monitor executing %s\n", ast_str_buffer(tmp));
530  if (ast_safe_system(ast_str_buffer(tmp)) == -1)
531  ast_log(LOG_WARNING, "Execute of %s failed.\n", ast_str_buffer(tmp));
532  }
533 
534  if (!ast_strlen_zero(ast_channel_monitor(chan)->beep_id)) {
535  ast_beep_stop(chan, ast_channel_monitor(chan)->beep_id);
536  }
537 
541 
544  NULL);
545  if (message) {
547  }
548  pbx_builtin_setvar_helper(chan, "MONITORED", NULL);
549  }
550  pbx_builtin_setvar_helper(chan, "AUTO_MONITOR", NULL);
551 
552  UNLOCK_IF_NEEDED(chan, need_lock);
553 
554  return 0;
555 }
556 
557 
558 /*! \brief Pause monitoring of channel */
560 {
562 }
563 
564 /*! \brief Unpause monitoring of channel */
566 {
568 }
569 
570 /*! \brief Wrapper for ast_monitor_pause */
571 static int pause_monitor_exec(struct ast_channel *chan, const char *data)
572 {
573  return ast_monitor_pause(chan);
574 }
575 
576 /*! \brief Wrapper for ast_monitor_unpause */
577 static int unpause_monitor_exec(struct ast_channel *chan, const char *data)
578 {
579  return ast_monitor_unpause(chan);
580 }
581 
582 /*!
583  * \brief Change monitored filename of channel
584  * \param chan
585  * \param fname_base new filename
586  * \param need_lock
587  * \retval 0 on success.
588  * \retval -1 on failure.
589 */
590 int AST_OPTIONAL_API_NAME(ast_monitor_change_fname)(struct ast_channel *chan, const char *fname_base, int need_lock)
591 {
592  if (ast_strlen_zero(fname_base)) {
593  ast_log(LOG_WARNING, "Cannot change monitor filename of channel %s to null\n", ast_channel_name(chan));
594  return -1;
595  }
596 
597  LOCK_IF_NEEDED(chan, need_lock);
598 
599  if (ast_channel_monitor(chan)) {
600  int directory = strchr(fname_base, '/') ? 1 : 0;
601  const char *absolute = *fname_base == '/' ? "" : ast_config_AST_MONITOR_DIR;
602  const char *absolute_suffix = *fname_base == '/' ? "" : "/";
603  char tmpstring[sizeof(ast_channel_monitor(chan)->filename_base)] = "";
604  int i, fd[2] = { -1, -1 }, doexit = 0;
605 
606  /* before continuing, see if we're trying to rename the file to itself... */
607  snprintf(tmpstring, sizeof(tmpstring), "%s%s%s", absolute, absolute_suffix, fname_base);
608 
609  /* try creating the directory just in case it doesn't exist */
610  if (directory) {
611  char *name = ast_strdupa(tmpstring);
612  ast_mkdir(dirname(name), 0777);
613  }
614 
615  /*!
616  * \note We cannot just compare filenames, due to symlinks, relative
617  * paths, and other possible filesystem issues. We could use
618  * realpath(3), but its use is discouraged. However, if we try to
619  * create the same file from two different paths, the second will
620  * fail, and so we have our notification that the filenames point to
621  * the same path.
622  *
623  * Remember, also, that we're using the basename of the file (i.e.
624  * the file without the format suffix), so it does not already exist
625  * and we aren't interfering with the recording itself.
626  */
627  ast_debug(2, "comparing tmpstring %s to filename_base %s\n", tmpstring, ast_channel_monitor(chan)->filename_base);
628 
629  if ((fd[0] = open(tmpstring, O_CREAT | O_WRONLY, 0644)) < 0 ||
630  (fd[1] = open(ast_channel_monitor(chan)->filename_base, O_CREAT | O_EXCL | O_WRONLY, 0644)) < 0) {
631  if (fd[0] < 0) {
632  ast_log(LOG_ERROR, "Unable to compare filenames: %s\n", strerror(errno));
633  } else {
634  ast_debug(2, "No need to rename monitor filename to itself\n");
635  }
636  doexit = 1;
637  }
638 
639  /* Cleanup temporary files */
640  for (i = 0; i < 2; i++) {
641  if (fd[i] >= 0) {
642  while (close(fd[i]) < 0 && errno == EINTR);
643  }
644  }
645  unlink(tmpstring);
646  /* if previous monitor file existed in a subdirectory, the directory will not be removed */
647  unlink(ast_channel_monitor(chan)->filename_base);
648 
649  if (doexit) {
650  UNLOCK_IF_NEEDED(chan, need_lock);
651  return 0;
652  }
653 
654  ast_copy_string(ast_channel_monitor(chan)->filename_base, tmpstring, sizeof(ast_channel_monitor(chan)->filename_base));
656  } else {
657  ast_log(LOG_WARNING, "Cannot change monitor filename of channel %s to %s, monitoring not started\n", ast_channel_name(chan), fname_base);
658  }
659 
660  UNLOCK_IF_NEEDED(chan, need_lock);
661 
662  return 0;
663 }
664 
665 enum {
666  MON_FLAG_BRIDGED = (1 << 0),
667  MON_FLAG_MIX = (1 << 1),
668  MON_FLAG_DROP_IN = (1 << 2),
669  MON_FLAG_DROP_OUT = (1 << 3),
670  MON_FLAG_BEEP = (1 << 4),
671 };
672 
673 enum {
675  OPT_ARG_ARRAY_SIZE, /* Always last element of the enum */
676 };
677 
684 });
685 
686 /*!
687  * \brief Start monitor
688  * \param chan
689  * \param data arguments passed fname|options
690  * \retval 0 on success.
691  * \retval -1 on failure.
692 */
693 static int start_monitor_exec(struct ast_channel *chan, const char *data)
694 {
695  char *arg;
696  char *options;
697  char *delay;
698  char *urlprefix = NULL;
699  char tmp[256];
700  int stream_action = X_REC_IN | X_REC_OUT;
701  int joinfiles = 0;
702  int res = 0;
703  char *parse;
704  struct ast_flags flags = { 0 };
705  char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, };
706  char beep_id[64] = "";
709  AST_APP_ARG(fname_base);
710  AST_APP_ARG(options);
711  );
712 
713  /* Parse arguments. */
714  if (ast_strlen_zero(data)) {
715  ast_log(LOG_ERROR, "Monitor requires an argument\n");
716  return 0;
717  }
718 
719  parse = ast_strdupa(data);
720  AST_STANDARD_APP_ARGS(args, parse);
721 
722  if (!ast_strlen_zero(args.options)) {
723  ast_app_parse_options(monitor_opts, &flags, opts, args.options);
724 
725  if (ast_test_flag(&flags, MON_FLAG_MIX)) {
726  stream_action |= X_JOIN;
727  }
728  if (ast_test_flag(&flags, MON_FLAG_DROP_IN)) {
729  stream_action &= ~X_REC_IN;
730  }
731  if (ast_test_flag(&flags, MON_FLAG_DROP_OUT)) {
732  stream_action &= ~X_REC_OUT;
733  }
734  if (ast_test_flag(&flags, MON_FLAG_BEEP)) {
735  const char *interval_str = S_OR(opts[OPT_ARG_BEEP_INTERVAL], "15");
736  unsigned int interval = 15;
737 
738  if (sscanf(interval_str, "%30u", &interval) != 1) {
739  ast_log(LOG_WARNING, "Invalid interval '%s' for periodic beep. Using default of %u\n",
740  interval_str, interval);
741  }
742 
743  if (ast_beep_start(chan, interval, beep_id, sizeof(beep_id))) {
744  ast_log(LOG_WARNING, "Unable to enable periodic beep, please ensure func_periodic_hook is loaded.\n");
745  return -1;
746  }
747  }
748  }
749 
750  arg = strchr(args.format, ':');
751  if (arg) {
752  *arg++ = 0;
753  urlprefix = arg;
754  }
755 
756  if (!ast_strlen_zero(urlprefix) && !ast_strlen_zero(args.fname_base)) {
757  snprintf(tmp, sizeof(tmp), "%s/%s.%s", urlprefix, args.fname_base,
758  ((strcmp(args.format, "gsm")) ? "wav" : "gsm"));
759  ast_channel_lock(chan);
761  ast_channel_unlock(chan);
762  }
763  if (ast_test_flag(&flags, MON_FLAG_BRIDGED)) {
764  /* We must remove the "b" option if listed. In principle none of
765  the following could give NULL results, but we check just to
766  be pedantic. Reconstructing with checks for 'm' option does not
767  work if we end up adding more options than 'm' in the future. */
768  delay = ast_strdupa(data);
769  options = strrchr(delay, ',');
770  if (options) {
771  arg = strchr(options, 'b');
772  if (arg) {
773  *arg = 'X';
774  pbx_builtin_setvar_helper(chan,"AUTO_MONITOR", delay);
775  }
776  }
777  return 0;
778  }
779 
780  res = ast_monitor_start(chan, args.format, args.fname_base, 1, stream_action, beep_id);
781  if (res < 0)
782  res = ast_monitor_change_fname(chan, args.fname_base, 1);
783 
784  if (stream_action & X_JOIN) {
785  if ((stream_action & X_REC_IN) && (stream_action & X_REC_OUT))
786  joinfiles = 1;
787  else
788  ast_log(LOG_WARNING, "Won't mix streams unless both input and output streams are recorded\n");
789  }
790  ast_monitor_setjoinfiles(chan, joinfiles);
791 
792  return res;
793 }
794 
795 /*! \brief Wrapper function \see ast_monitor_stop */
796 static int stop_monitor_exec(struct ast_channel *chan, const char *data)
797 {
798  return ast_monitor_stop(chan, 1);
799 }
800 
801 /*! \brief Wrapper function \see ast_monitor_change_fname */
802 static int change_monitor_exec(struct ast_channel *chan, const char *data)
803 {
804  return ast_monitor_change_fname(chan, data, 1);
805 }
806 
807 /*! \brief Start monitoring a channel by manager connection */
808 static int start_monitor_action(struct mansession *s, const struct message *m)
809 {
810  struct ast_channel *c = NULL;
811  const char *name = astman_get_header(m, "Channel");
812  const char *fname = astman_get_header(m, "File");
813  const char *format = astman_get_header(m, "Format");
814  const char *mix = astman_get_header(m, "Mix");
815  char *d;
816 
817  if (ast_strlen_zero(name)) {
818  astman_send_error(s, m, "No channel specified");
819  return AMI_SUCCESS;
820  }
821 
822  if (!(c = ast_channel_get_by_name(name))) {
823  astman_send_error(s, m, "No such channel");
824  return AMI_SUCCESS;
825  }
826 
827  if (ast_strlen_zero(fname)) {
828  /* No filename specified, default to the channel name. */
829  ast_channel_lock(c);
830  fname = ast_strdupa(ast_channel_name(c));
832 
833  /* Replace all '/' chars from the channel name with '-' chars. */
834  for (d = (char *) fname; (d = strchr(d, '/')); ) {
835  *d = '-';
836  }
837  }
838 
839  if (ast_monitor_start(c, format, fname, 1, X_REC_IN | X_REC_OUT, NULL)) {
840  if (ast_monitor_change_fname(c, fname, 1)) {
841  astman_send_error(s, m, "Could not start monitoring channel");
842  c = ast_channel_unref(c);
843  return AMI_SUCCESS;
844  }
845  }
846 
847  if (ast_true(mix)) {
848  ast_channel_lock(c);
851  }
852 
853  c = ast_channel_unref(c);
854 
855  astman_send_ack(s, m, "Started monitoring channel");
856 
857  return AMI_SUCCESS;
858 }
859 
860 /*! \brief Stop monitoring a channel by manager connection */
861 static int stop_monitor_action(struct mansession *s, const struct message *m)
862 {
863  struct ast_channel *c = NULL;
864  const char *name = astman_get_header(m, "Channel");
865  int res;
866 
867  if (ast_strlen_zero(name)) {
868  astman_send_error(s, m, "No channel specified");
869  return AMI_SUCCESS;
870  }
871 
872  if (!(c = ast_channel_get_by_name(name))) {
873  astman_send_error(s, m, "No such channel");
874  return AMI_SUCCESS;
875  }
876 
877  res = ast_monitor_stop(c, 1);
878 
879  c = ast_channel_unref(c);
880 
881  if (res) {
882  astman_send_error(s, m, "Could not stop monitoring channel");
883  return AMI_SUCCESS;
884  }
885 
886  astman_send_ack(s, m, "Stopped monitoring channel");
887 
888  return AMI_SUCCESS;
889 }
890 
891 /*! \brief Change filename of a monitored channel by manager connection */
892 static int change_monitor_action(struct mansession *s, const struct message *m)
893 {
894  struct ast_channel *c = NULL;
895  const char *name = astman_get_header(m, "Channel");
896  const char *fname = astman_get_header(m, "File");
897 
898  if (ast_strlen_zero(name)) {
899  astman_send_error(s, m, "No channel specified");
900  return AMI_SUCCESS;
901  }
902 
903  if (ast_strlen_zero(fname)) {
904  astman_send_error(s, m, "No filename specified");
905  return AMI_SUCCESS;
906  }
907 
908  if (!(c = ast_channel_get_by_name(name))) {
909  astman_send_error(s, m, "No such channel");
910  return AMI_SUCCESS;
911  }
912 
913  if (ast_monitor_change_fname(c, fname, 1)) {
914  c = ast_channel_unref(c);
915  astman_send_error(s, m, "Could not change monitored filename of channel");
916  return AMI_SUCCESS;
917  }
918 
919  c = ast_channel_unref(c);
920 
921  astman_send_ack(s, m, "Changed monitor filename");
922 
923  return AMI_SUCCESS;
924 }
925 
927 {
928  if (ast_channel_monitor(chan))
929  ast_channel_monitor(chan)->joinfiles = turnon;
930 }
931 
933 {
936 };
937 
938 static int do_pause_or_unpause(struct mansession *s, const struct message *m, int action)
939 {
940  struct ast_channel *c = NULL;
941  const char *name = astman_get_header(m, "Channel");
942 
943  if (ast_strlen_zero(name)) {
944  astman_send_error(s, m, "No channel specified");
945  return AMI_SUCCESS;
946  }
947 
948  if (!(c = ast_channel_get_by_name(name))) {
949  astman_send_error(s, m, "No such channel");
950  return AMI_SUCCESS;
951  }
952 
953  if (action == MONITOR_ACTION_PAUSE) {
955  } else {
957  }
958 
959  c = ast_channel_unref(c);
960 
961  astman_send_ack(s, m, (action == MONITOR_ACTION_PAUSE ? "Paused monitoring of the channel" : "Unpaused monitoring of the channel"));
962 
963  return AMI_SUCCESS;
964 }
965 
966 static int pause_monitor_action(struct mansession *s, const struct message *m)
967 {
969 }
970 
971 static int unpause_monitor_action(struct mansession *s, const struct message *m)
972 {
974 }
975 
976 static int load_module(void)
977 {
988 
990 }
991 
992 static int unload_module(void)
993 {
994  ast_unregister_application("Monitor");
995  ast_unregister_application("StopMonitor");
996  ast_unregister_application("ChangeMonitor");
997  ast_unregister_application("PauseMonitor");
998  ast_unregister_application("UnpauseMonitor");
999  ast_manager_unregister("Monitor");
1000  ast_manager_unregister("StopMonitor");
1001  ast_manager_unregister("ChangeMonitor");
1002  ast_manager_unregister("PauseMonitor");
1003  ast_manager_unregister("UnpauseMonitor");
1004 
1005  return 0;
1006 }
1007 
1008 /* usecount semantics need to be defined */
1010  .support_level = AST_MODULE_SUPPORT_DEPRECATED,
1011  .load = load_module,
1012  .unload = unload_module,
1013  .load_pri = AST_MODPRI_CHANNEL_DEPEND,
1014  .optional_modules = "func_periodic_hook",
1015 );
#define FILENAME_MAX
enum sip_cc_notify_state state
Definition: chan_sip.c:959
static int do_pause_or_unpause(struct mansession *s, const struct message *m, int action)
Definition: res_monitor.c:938
#define ast_channel_lock(chan)
Definition: channel.h:2945
#define X_REC_IN
Definition: monitor.h:30
char read_filename[FILENAME_MAX]
Definition: channel.h:4242
Main Channel structure associated with a channel.
Asterisk locking-related definitions:
Asterisk main include file. File version handling, generic pbx functions.
static SQLHSTMT execute(struct odbc_obj *obj, void *data, int silent)
Common execution function for SQL queries.
Definition: func_odbc.c:454
static int ast_monitor_set_state(struct ast_channel *chan, int state)
Change state of monitored channel.
Definition: res_monitor.c:282
void ast_channel_insmpl_set(struct ast_channel *chan, unsigned long value)
static int load_module(void)
Definition: res_monitor.c:976
int(* stop)(struct ast_channel *chan, int need_lock)
Definition: channel.h:4250
#define ast_channel_unref(c)
Decrease channel reference count.
Definition: channel.h:2981
#define ast_test_flag(p, flag)
Definition: utils.h:63
static int unpause_monitor_exec(struct ast_channel *chan, const char *data)
Wrapper for ast_monitor_unpause.
Definition: res_monitor.c:577
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the &#39;standard&#39; argument separation process for an application.
#define UNLOCK_IF_NEEDED(lock, needed)
Definition: res_monitor.c:268
Stasis Message Bus API. See Stasis Message Bus API for detailed documentation.
#define LOG_WARNING
Definition: logger.h:274
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:714
static ast_mutex_t monitorlock
Definition: res_monitor.c:261
static const struct ast_app_option monitor_opts[128]
Definition: res_monitor.c:684
static struct test_val d
static int change_monitor_exec(struct ast_channel *chan, const char *data)
Wrapper function.
Definition: res_monitor.c:802
static int tmp()
Definition: bt_open.c:389
static int start_monitor_action(struct mansession *s, const struct message *m)
Start monitoring a channel by manager connection.
Definition: res_monitor.c:808
char filename_base[FILENAME_MAX]
Definition: channel.h:4244
static int unload_module(void)
Definition: res_monitor.c:992
static int pause_monitor_action(struct mansession *s, const struct message *m)
Definition: res_monitor.c:966
static void write_stream(struct ogg_vorbis_desc *s, FILE *f)
Write out any pending encoded data.
#define EVENT_FLAG_CALL
Definition: manager.h:72
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:1091
int AST_OPTIONAL_API_NAME() ast_beep_stop(struct ast_channel *chan, const char *beep_id)
#define LOCK_IF_NEEDED(lock, needed)
Definition: res_monitor.c:263
void astman_send_ack(struct mansession *s, const struct message *m, char *msg)
Send ack in manager transaction.
Definition: manager.c:3191
#define ast_mutex_lock(a)
Definition: lock.h:187
static struct test_val c
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:243
static int unpause_monitor_action(struct mansession *s, const struct message *m)
Definition: res_monitor.c:971
Generic File Format Support. Should be included by clients of the file handling routines. File service providers should instead include mod_format.h.
const char * args
#define NULL
Definition: resample.c:96
const char * data
struct stasis_message_type * ast_channel_monitor_start_type(void)
Message type for starting monitor on a channel.
int ast_filedelete(const char *filename, const char *fmt)
Deletes a file.
Definition: file.c:1098
char write_filename[FILENAME_MAX]
Definition: channel.h:4243
int ast_unregister_application(const char *app)
Unregister an application.
Definition: pbx_app.c:392
#define AST_FILE_MODE
Definition: asterisk.h:32
static const char * get_soxmix_format(const char *format)
Get audio format.
Definition: res_monitor.c:442
Periodic beeps into the audio of a call.
const char * pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name)
Return a pointer to the value of the corresponding channel variable.
#define X_REC_OUT
Definition: monitor.h:31
Utility functions.
const char * astman_get_header(const struct message *m, char *var)
Get header from mananger transaction.
Definition: manager.c:2820
#define ast_strlen_zero(foo)
Definition: strings.h:52
#define AST_APP_OPTIONS(holder, options...)
Declares an array of options for an application.
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
Definition: strings.h:1065
Configuration File Parser.
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:452
#define ast_log
Definition: astobj2.c:42
General Asterisk PBX channel definitions.
Asterisk file paths, configured in asterisk.conf.
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:911
int AST_OPTIONAL_API_NAME() ast_monitor_start(struct ast_channel *chan, const char *format_spec, const char *fname_base, int need_lock, int stream_action, const char *beep_id)
Start monitoring a channel.
Definition: res_monitor.c:305
struct stasis_message_type * ast_channel_monitor_stop_type(void)
Message type for stopping monitor on a channel.
In case you didn&#39;t read that giant block of text above the mansession_session struct, the struct mansession is named this solely to keep the API the same in Asterisk. This structure really represents data that is different from Manager action to Manager action. The mansession_session pointer contained within points to session-specific data.
Definition: manager.c:1625
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:300
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: main/app.c:2906
Core PBX routines and definitions.
int AST_OPTIONAL_API_NAME() ast_beep_start(struct ast_channel *chan, unsigned int interval, char *beep_id, size_t len)
static int stop_monitor_action(struct mansession *s, const struct message *m)
Stop monitoring a channel by manager connection.
Definition: res_monitor.c:861
const char * ast_channel_uniqueid(const struct ast_channel *chan)
struct stasis_topic * ast_channel_topic(struct ast_channel *chan)
A topic which publishes the events for a particular channel.
int ast_manager_unregister(const char *action)
Unregister a registered manager command.
Definition: manager.c:7258
The AMI - Asterisk Manager Interface - is a TCP protocol created to manage Asterisk with third-party ...
static int change_monitor_action(struct mansession *s, const struct message *m)
Change filename of a monitored channel by manager connection.
Definition: res_monitor.c:892
#define LOG_ERROR
Definition: logger.h:285
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true". This function checks to see whether a string passed to it is an indication of an "true" value. It checks to see if the string is "yes", "true", "y", "t", "on" or "1".
Definition: main/utils.c:1951
The descriptor of a dynamic string XXX storage will be optimized later if needed We use the ts field ...
Definition: strings.h:584
int ast_safe_system(const char *s)
Safely spawn an OS shell command while closing file descriptors.
Definition: extconf.c:829
#define AST_APP_OPTION_ARG(option, flagno, argno)
Declares an application option that accepts an argument.
static unsigned int monitor
Definition: chan_phone.c:116
int errno
struct ast_filestream * ast_writefile(const char *filename, const char *type, const char *comment, int flags, int check, mode_t mode)
Starts writing a file.
Definition: file.c:1361
int AST_OPTIONAL_API_NAME() ast_monitor_change_fname(struct ast_channel *chan, const char *fname_base, int need_lock)
Change monitored filename of channel.
Definition: res_monitor.c:590
MONITOR_PAUSING_ACTION
Definition: res_monitor.c:932
struct ast_filestream * read_stream
Definition: channel.h:4240
#define ast_channel_unlock(chan)
Definition: channel.h:2946
static void parse(struct mgcp_request *req)
Definition: chan_mgcp.c:1872
void stasis_publish(struct stasis_topic *topic, struct stasis_message *message)
Publish a message to a topic&#39;s subscribers.
Definition: stasis.c:1511
static const char name[]
Definition: cdr_mysql.c:74
#define ast_free(a)
Definition: astmm.h:182
const char * ast_config_AST_MONITOR_DIR
Definition: options.c:155
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:204
struct ast_filestream * write_stream
Definition: channel.h:4241
int ast_closestream(struct ast_filestream *f)
Closes a stream.
Definition: file.c:1068
static int doexit
Definition: main/db.c:112
static int stop_monitor_exec(struct ast_channel *chan, const char *data)
Wrapper function.
Definition: res_monitor.c:796
#define X_JOIN
Definition: monitor.h:32
void ast_cdr_setuserfield(const char *channel_name, const char *userfield)
Set CDR user field for channel (stored in CDR)
Definition: cdr.c:3477
Structure used to handle boolean flags.
Definition: utils.h:199
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS|AST_MODFLAG_LOAD_ORDER, "HTTP Phone Provisioning",.support_level=AST_MODULE_SUPPORT_EXTENDED,.load=load_module,.unload=unload_module,.reload=reload,.load_pri=AST_MODPRI_CHANNEL_DEPEND,.requires="http",)
struct stasis_message * ast_channel_blob_create_from_cache(const char *uniqueid, struct stasis_message_type *type, struct ast_json *blob)
Create a ast_channel_blob message, pulling channel state from the cache.
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...
int AST_OPTIONAL_API_NAME() ast_monitor_unpause(struct ast_channel *chan)
Unpause monitoring of channel.
Definition: res_monitor.c:565
int AST_OPTIONAL_API_NAME() ast_monitor_stop(struct ast_channel *chan, int need_lock)
Stop monitoring channel.
Definition: res_monitor.c:461
#define ao2_cleanup(obj)
Definition: astobj2.h:1958
Standard Command Line Interface.
void AST_OPTIONAL_API_NAME() ast_monitor_setjoinfiles(struct ast_channel *chan, int turnon)
Definition: res_monitor.c:926
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:401
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one...
Definition: strings.h:79
const char * ast_channel_name(const struct ast_channel *chan)
int ast_fileexists(const char *filename, const char *fmt, const char *preflang)
Checks for the existence of a given file.
Definition: file.c:1086
Channel monitoring.
int AST_OPTIONAL_API_NAME() ast_monitor_pause(struct ast_channel *chan)
Pause monitoring of channel.
Definition: res_monitor.c:559
Options provided by main asterisk program.
static unsigned long seq
Definition: res_monitor.c:273
int ast_filerename(const char *oldname, const char *newname, const char *fmt)
Renames a file.
Definition: file.c:1103
#define AST_OPTIONAL_API_NAME(name)
Expands to the name of the implementation function.
Definition: optional_api.h:228
static struct test_options options
#define AST_APP_OPTION(option, flagno)
Declares an application option that does not accept an argument.
enum AST_MONITORING_STATE state
Definition: channel.h:4249
#define ast_manager_register_xml(action, authority, func)
Register a manager callback using XML documentation to describe the manager.
Definition: manager.h:186
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
struct ast_channel * ast_channel_get_by_name(const char *name)
Find a channel by name.
Definition: channel.c:1454
void astman_send_error(struct mansession *s, const struct message *m, char *error)
Send error in manager transaction.
Definition: manager.c:3159
Asterisk module definitions.
struct ast_channel_monitor * ast_channel_monitor(const struct ast_channel *chan)
static snd_pcm_format_t format
Definition: chan_alsa.c:102
static int start_monitor_exec(struct ast_channel *chan, const char *data)
Start monitor.
Definition: res_monitor.c:693
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application&#39;s arguments.
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
static int pause_monitor_exec(struct ast_channel *chan, const char *data)
Wrapper for ast_monitor_pause.
Definition: res_monitor.c:571
void ast_channel_outsmpl_set(struct ast_channel *chan, unsigned long value)
#define AST_MUTEX_DEFINE_STATIC(mutex)
Definition: lock.h:518
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition: module.h:626
#define AMI_SUCCESS
Definition: manager.h:65
char beep_id[64]
Definition: channel.h:4245
#define ast_str_create(init_len)
Create a malloc&#39;ed dynamic length string.
Definition: strings.h:620
void ast_channel_monitor_set(struct ast_channel *chan, struct ast_channel_monitor *value)
#define ast_mutex_unlock(a)
Definition: lock.h:188
#define AST_APP_ARG(name)
Define an application argument.
int ast_mkdir(const char *path, int mode)
Recursively create directory path.
Definition: main/utils.c:2231