Wed Jan 8 2020 09:49:50

Asterisk developer's documentation


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 <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 PBX channel monitoring
22  *
23  * \author Mark Spencer <markster@digium.com>
24  */
25 
26 /*** MODULEINFO
27  <support_level>core</support_level>
28  ***/
29 
30 #include "asterisk.h"
31 
32 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 413586 $")
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/manager.h"
44 #include "asterisk/cli.h"
45 #define AST_API_MODULE
46 #include "asterisk/monitor.h"
47 #include "asterisk/app.h"
48 #include "asterisk/utils.h"
49 #include "asterisk/config.h"
50 #include "asterisk/options.h"
51 
52 /*** DOCUMENTATION
53  <application name="Monitor" language="en_US">
54  <synopsis>
55  Monitor a channel.
56  </synopsis>
57  <syntax>
58  <parameter name="file_format" argsep=":">
59  <argument name="file_format" required="true">
60  <para>optional, if not set, defaults to <literal>wav</literal></para>
61  </argument>
62  <argument name="urlbase" />
63  </parameter>
64  <parameter name="fname_base">
65  <para>if set, changes the filename used to the one specified.</para>
66  </parameter>
67  <parameter name="options">
68  <optionlist>
69  <option name="m">
70  <para>when the recording ends mix the two leg files into one and
71  delete the two leg files. If the variable <variable>MONITOR_EXEC</variable>
72  is set, the application referenced in it will be executed instead of
73  soxmix/sox and the raw leg files will NOT be deleted automatically.
74  soxmix/sox or <variable>MONITOR_EXEC</variable> is handed 3 arguments,
75  the two leg files and a target mixed file name which is the same as
76  the leg file names only without the in/out designator.</para>
77  <para>If <variable>MONITOR_EXEC_ARGS</variable> is set, the contents
78  will be passed on as additional arguments to <variable>MONITOR_EXEC</variable>.
79  Both <variable>MONITOR_EXEC</variable> and the Mix flag can be set from the
80  administrator interface.</para>
81  </option>
82  <option name="b">
83  <para>Don't begin recording unless a call is bridged to another channel.</para>
84  </option>
85  <option name="i">
86  <para>Skip recording of input stream (disables <literal>m</literal> option).</para>
87  </option>
88  <option name="o">
89  <para>Skip recording of output stream (disables <literal>m</literal> option).</para>
90  </option>
91  </optionlist>
92  </parameter>
93  </syntax>
94  <description>
95  <para>Used to start monitoring a channel. The channel's input and output
96  voice packets are logged to files until the channel hangs up or
97  monitoring is stopped by the StopMonitor application.</para>
98  <para>By default, files are stored to <filename>/var/spool/asterisk/monitor/</filename>.
99  Returns <literal>-1</literal> if monitor files can't be opened or if the channel is
100  already monitored, otherwise <literal>0</literal>.</para>
101  </description>
102  <see-also>
103  <ref type="application">StopMonitor</ref>
104  </see-also>
105  </application>
106  <application name="StopMonitor" language="en_US">
107  <synopsis>
108  Stop monitoring a channel.
109  </synopsis>
110  <syntax />
111  <description>
112  <para>Stops monitoring a channel. Has no effect if the channel is not monitored.</para>
113  </description>
114  </application>
115  <application name="ChangeMonitor" language="en_US">
116  <synopsis>
117  Change monitoring filename of a channel.
118  </synopsis>
119  <syntax>
120  <parameter name="filename_base" required="true">
121  <para>The new filename base to use for monitoring this channel.</para>
122  </parameter>
123  </syntax>
124  <description>
125  <para>Changes monitoring filename of a channel. Has no effect if the
126  channel is not monitored.</para>
127  </description>
128  </application>
129  <application name="PauseMonitor" language="en_US">
130  <synopsis>
131  Pause monitoring of a channel.
132  </synopsis>
133  <syntax />
134  <description>
135  <para>Pauses monitoring of a channel until it is re-enabled by a call to UnpauseMonitor.</para>
136  </description>
137  <see-also>
138  <ref type="application">UnpauseMonitor</ref>
139  </see-also>
140  </application>
141  <application name="UnpauseMonitor" language="en_US">
142  <synopsis>
143  Unpause monitoring of a channel.
144  </synopsis>
145  <syntax />
146  <description>
147  <para>Unpauses monitoring of a channel on which monitoring had
148  previously been paused with PauseMonitor.</para>
149  </description>
150  <see-also>
151  <ref type="application">PauseMonitor</ref>
152  </see-also>
153  </application>
154  <manager name="Monitor" language="en_US">
155  <synopsis>
156  Monitor a channel.
157  </synopsis>
158  <syntax>
159  <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
160  <parameter name="Channel" required="true">
161  <para>Used to specify the channel to record.</para>
162  </parameter>
163  <parameter name="File">
164  <para>Is the name of the file created in the monitor spool directory.
165  Defaults to the same name as the channel (with slashes replaced with dashes).</para>
166  </parameter>
167  <parameter name="Format">
168  <para>Is the audio recording format. Defaults to <literal>wav</literal>.</para>
169  </parameter>
170  <parameter name="Mix">
171  <para>Boolean parameter as to whether to mix the input and output channels
172  together after the recording is finished.</para>
173  </parameter>
174  </syntax>
175  <description>
176  <para>This action may be used to record the audio on a
177  specified channel.</para>
178  </description>
179  </manager>
180  <manager name="StopMonitor" language="en_US">
181  <synopsis>
182  Stop monitoring a channel.
183  </synopsis>
184  <syntax>
185  <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
186  <parameter name="Channel" required="true">
187  <para>The name of the channel monitored.</para>
188  </parameter>
189  </syntax>
190  <description>
191  <para>This action may be used to end a previously started 'Monitor' action.</para>
192  </description>
193  </manager>
194  <manager name="ChangeMonitor" language="en_US">
195  <synopsis>
196  Change monitoring filename of a channel.
197  </synopsis>
198  <syntax>
199  <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
200  <parameter name="Channel" required="true">
201  <para>Used to specify the channel to record.</para>
202  </parameter>
203  <parameter name="File" required="true">
204  <para>Is the new name of the file created in the
205  monitor spool directory.</para>
206  </parameter>
207  </syntax>
208  <description>
209  <para>This action may be used to change the file
210  started by a previous 'Monitor' action.</para>
211  </description>
212  </manager>
213  <manager name="PauseMonitor" language="en_US">
214  <synopsis>
215  Pause monitoring of a channel.
216  </synopsis>
217  <syntax>
218  <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
219  <parameter name="Channel" required="true">
220  <para>Used to specify the channel to record.</para>
221  </parameter>
222  </syntax>
223  <description>
224  <para>This action may be used to temporarily stop the
225  recording of a channel.</para>
226  </description>
227  </manager>
228  <manager name="UnpauseMonitor" language="en_US">
229  <synopsis>
230  Unpause 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 re-enable recording
240  of a channel after calling PauseMonitor.</para>
241  </description>
242  </manager>
243 
244  ***/
245 
247 
248 #define LOCK_IF_NEEDED(lock, needed) do { \
249  if (needed) \
250  ast_channel_lock(lock); \
251  } while(0)
252 
253 #define UNLOCK_IF_NEEDED(lock, needed) do { \
254  if (needed) \
255  ast_channel_unlock(lock); \
256  } while (0)
257 
258 static unsigned long seq = 0;
259 
260 /*!
261  * \brief Change state of monitored channel
262  * \param chan
263  * \param state monitor state
264  * \retval 0 on success.
265  * \retval -1 on failure.
266 */
267 static int ast_monitor_set_state(struct ast_channel *chan, int state)
268 {
269  LOCK_IF_NEEDED(chan, 1);
270  if (!chan->monitor) {
271  UNLOCK_IF_NEEDED(chan, 1);
272  return -1;
273  }
274  chan->monitor->state = state;
275  UNLOCK_IF_NEEDED(chan, 1);
276  return 0;
277 }
278 
279 /*! \brief Start monitoring a channel
280  * \param chan ast_channel struct to record
281  * \param format_spec file format to use for recording
282  * \param fname_base filename base to record to
283  * \param need_lock whether to lock the channel mutex
284  * \param stream_action whether to record the input and/or output streams. X_REC_IN | X_REC_OUT is most often used
285  * Creates the file to record, if no format is specified it assumes WAV
286  * It also sets channel variable __MONITORED=yes
287  * \retval 0 on success
288  * \retval -1 on failure
289  */
290 int AST_OPTIONAL_API_NAME(ast_monitor_start)(struct ast_channel *chan, const char *format_spec,
291  const char *fname_base, int need_lock, int stream_action)
292 {
293  int res = 0;
294 
295  LOCK_IF_NEEDED(chan, need_lock);
296 
297  if (!(chan->monitor)) {
299  char *channel_name, *p;
300 
301  /* Create monitoring directory if needed */
303 
304  if (!(monitor = ast_calloc(1, sizeof(*monitor)))) {
305  UNLOCK_IF_NEEDED(chan, need_lock);
306  return -1;
307  }
308 
309  /* Determine file names */
310  if (!ast_strlen_zero(fname_base)) {
311  int directory = strchr(fname_base, '/') ? 1 : 0;
312  const char *absolute = *fname_base == '/' ? "" : ast_config_AST_MONITOR_DIR;
313  const char *absolute_suffix = *fname_base == '/' ? "" : "/";
314 
315  snprintf(monitor->read_filename, FILENAME_MAX, "%s%s%s-in",
316  absolute, absolute_suffix, fname_base);
317  snprintf(monitor->write_filename, FILENAME_MAX, "%s%s%s-out",
318  absolute, absolute_suffix, fname_base);
319  snprintf(monitor->filename_base, FILENAME_MAX, "%s%s%s",
320  absolute, absolute_suffix, fname_base);
321 
322  /* try creating the directory just in case it doesn't exist */
323  if (directory) {
324  char *name = ast_strdupa(monitor->filename_base);
325  ast_mkdir(dirname(name), 0777);
326  }
327  } else {
329  snprintf(monitor->read_filename, FILENAME_MAX, "%s/audio-in-%lu",
331  snprintf(monitor->write_filename, FILENAME_MAX, "%s/audio-out-%lu",
333  seq++;
335 
336  /* Replace all '/' chars from the channel name with '-' chars. */
337  channel_name = ast_strdupa(chan->name);
338  for (p = channel_name; (p = strchr(p, '/')); ) {
339  *p = '-';
340  }
341 
342  snprintf(monitor->filename_base, FILENAME_MAX, "%s/%d-%s",
343  ast_config_AST_MONITOR_DIR, (int)time(NULL), channel_name);
344  monitor->filename_changed = 1;
345  }
346 
347  monitor->stop = ast_monitor_stop;
348 
349  /* Determine file format */
350  if (!ast_strlen_zero(format_spec)) {
351  monitor->format = ast_strdup(format_spec);
352  } else {
353  monitor->format = ast_strdup("wav");
354  }
355 
356  /* open files */
357  if (stream_action & X_REC_IN) {
358  if (ast_fileexists(monitor->read_filename, NULL, NULL) > 0)
359  ast_filedelete(monitor->read_filename, NULL);
360  if (!(monitor->read_stream = ast_writefile(monitor->read_filename,
361  monitor->format, NULL,
362  O_CREAT|O_TRUNC|O_WRONLY, 0, AST_FILE_MODE))) {
363  ast_log(LOG_WARNING, "Could not create file %s\n",
364  monitor->read_filename);
365  ast_free(monitor);
366  UNLOCK_IF_NEEDED(chan, need_lock);
367  return -1;
368  }
369  } else
370  monitor->read_stream = NULL;
371 
372  if (stream_action & X_REC_OUT) {
373  if (ast_fileexists(monitor->write_filename, NULL, NULL) > 0) {
374  ast_filedelete(monitor->write_filename, NULL);
375  }
376  if (!(monitor->write_stream = ast_writefile(monitor->write_filename,
377  monitor->format, NULL,
378  O_CREAT|O_TRUNC|O_WRONLY, 0, AST_FILE_MODE))) {
379  ast_log(LOG_WARNING, "Could not create file %s\n",
380  monitor->write_filename);
381  if (monitor->read_stream) {
382  ast_closestream(monitor->read_stream);
383  }
384  ast_free(monitor);
385  UNLOCK_IF_NEEDED(chan, need_lock);
386  return -1;
387  }
388  } else
389  monitor->write_stream = NULL;
390 
391  chan->monitor = monitor;
393  /* so we know this call has been monitored in case we need to bill for it or something */
394  pbx_builtin_setvar_helper(chan, "__MONITORED","true");
395 
396  ast_manager_event(chan, EVENT_FLAG_CALL, "MonitorStart",
397  "Channel: %s\r\n"
398  "Uniqueid: %s\r\n",
399  chan->name,
400  chan->uniqueid);
401  } else {
402  ast_debug(1,"Cannot start monitoring %s, already monitored\n", chan->name);
403  res = -1;
404  }
405 
406  UNLOCK_IF_NEEDED(chan, need_lock);
407 
408  return res;
409 }
410 
411 /*!
412  * \brief Get audio format.
413  * \param format recording format.
414  * The file format extensions that Asterisk uses are not all the same as that
415  * which soxmix expects. This function ensures that the format used as the
416  * extension on the filename is something soxmix will understand.
417  */
418 static const char *get_soxmix_format(const char *format)
419 {
420  const char *res = format;
421 
422  if (!strcasecmp(format,"ulaw"))
423  res = "ul";
424  if (!strcasecmp(format,"alaw"))
425  res = "al";
426 
427  return res;
428 }
429 
430 /*!
431  * \brief Stop monitoring channel
432  * \param chan
433  * \param need_lock
434  * Stop the recording, close any open streams, mix in/out channels if required
435  * \return Always 0
436 */
437 int AST_OPTIONAL_API_NAME(ast_monitor_stop)(struct ast_channel *chan, int need_lock)
438 {
439  int delfiles = 0;
440 
441  LOCK_IF_NEEDED(chan, need_lock);
442 
443  if (chan->monitor) {
444  char filename[ FILENAME_MAX ];
445 
446  if (chan->monitor->read_stream) {
448  }
449  if (chan->monitor->write_stream) {
451  }
452 
454  if (chan->monitor->read_stream) {
455  if (ast_fileexists(chan->monitor->read_filename,NULL,NULL) > 0) {
456  snprintf(filename, FILENAME_MAX, "%s-in", chan->monitor->filename_base);
457  if (ast_fileexists(filename, NULL, NULL) > 0) {
458  ast_filedelete(filename, NULL);
459  }
460  ast_filerename(chan->monitor->read_filename, filename, chan->monitor->format);
461  } else {
462  ast_log(LOG_WARNING, "File %s not found\n", chan->monitor->read_filename);
463  }
464  }
465 
466  if (chan->monitor->write_stream) {
467  if (ast_fileexists(chan->monitor->write_filename,NULL,NULL) > 0) {
468  snprintf(filename, FILENAME_MAX, "%s-out", chan->monitor->filename_base);
469  if (ast_fileexists(filename, NULL, NULL) > 0) {
470  ast_filedelete(filename, NULL);
471  }
472  ast_filerename(chan->monitor->write_filename, filename, chan->monitor->format);
473  } else {
474  ast_log(LOG_WARNING, "File %s not found\n", chan->monitor->write_filename);
475  }
476  }
477  }
478 
479  if (chan->monitor->joinfiles && !ast_strlen_zero(chan->monitor->filename_base)) {
480  char tmp[1024];
481  char tmp2[1024];
482  const char *format = !strcasecmp(chan->monitor->format,"wav49") ? "WAV" : chan->monitor->format;
483  char *fname_base = chan->monitor->filename_base;
484  const char *execute, *execute_args;
485  /* at this point, fname_base really is the full path */
486 
487  /* Set the execute application */
488  execute = pbx_builtin_getvar_helper(chan, "MONITOR_EXEC");
489  if (ast_strlen_zero(execute)) {
490 #ifdef HAVE_SOXMIX
491  execute = "nice -n 19 soxmix";
492 #else
493  execute = "nice -n 19 sox -m";
494 #endif
495  format = get_soxmix_format(format);
496  delfiles = 1;
497  }
498  execute_args = pbx_builtin_getvar_helper(chan, "MONITOR_EXEC_ARGS");
499  if (ast_strlen_zero(execute_args)) {
500  execute_args = "";
501  }
502 
503  snprintf(tmp, sizeof(tmp), "%s \"%s-in.%s\" \"%s-out.%s\" \"%s.%s\" %s &",
504  execute, fname_base, format, fname_base, format, fname_base, format,execute_args);
505  if (delfiles) {
506  snprintf(tmp2,sizeof(tmp2), "( %s& rm -f \"%s-\"* ) &",tmp, fname_base); /* remove legs when done mixing */
507  ast_copy_string(tmp, tmp2, sizeof(tmp));
508  }
509  ast_debug(1,"monitor executing %s\n",tmp);
510  if (ast_safe_system(tmp) == -1)
511  ast_log(LOG_WARNING, "Execute of %s failed.\n",tmp);
512  }
513 
514  ast_free(chan->monitor->format);
515  ast_free(chan->monitor);
516  chan->monitor = NULL;
517 
518  ast_manager_event(chan, EVENT_FLAG_CALL, "MonitorStop",
519  "Channel: %s\r\n"
520  "Uniqueid: %s\r\n",
521  chan->name,
522  chan->uniqueid
523  );
524  pbx_builtin_setvar_helper(chan, "MONITORED", NULL);
525  }
526  pbx_builtin_setvar_helper(chan, "AUTO_MONITOR", NULL);
527 
528  UNLOCK_IF_NEEDED(chan, need_lock);
529 
530  return 0;
531 }
532 
533 
534 /*! \brief Pause monitoring of channel */
536 {
538 }
539 
540 /*! \brief Unpause monitoring of channel */
542 {
544 }
545 
546 /*! \brief Wrapper for ast_monitor_pause */
547 static int pause_monitor_exec(struct ast_channel *chan, const char *data)
548 {
549  return ast_monitor_pause(chan);
550 }
551 
552 /*! \brief Wrapper for ast_monitor_unpause */
553 static int unpause_monitor_exec(struct ast_channel *chan, const char *data)
554 {
555  return ast_monitor_unpause(chan);
556 }
557 
558 /*!
559  * \brief Change monitored filename of channel
560  * \param chan
561  * \param fname_base new filename
562  * \param need_lock
563  * \retval 0 on success.
564  * \retval -1 on failure.
565 */
566 int AST_OPTIONAL_API_NAME(ast_monitor_change_fname)(struct ast_channel *chan, const char *fname_base, int need_lock)
567 {
568  if (ast_strlen_zero(fname_base)) {
569  ast_log(LOG_WARNING, "Cannot change monitor filename of channel %s to null\n", chan->name);
570  return -1;
571  }
572 
573  LOCK_IF_NEEDED(chan, need_lock);
574 
575  if (chan->monitor) {
576  int directory = strchr(fname_base, '/') ? 1 : 0;
577  const char *absolute = *fname_base == '/' ? "" : ast_config_AST_MONITOR_DIR;
578  const char *absolute_suffix = *fname_base == '/' ? "" : "/";
579  char tmpstring[sizeof(chan->monitor->filename_base)] = "";
580  int i, fd[2] = { -1, -1 }, doexit = 0;
581 
582  /* before continuing, see if we're trying to rename the file to itself... */
583  snprintf(tmpstring, sizeof(tmpstring), "%s%s%s", absolute, absolute_suffix, fname_base);
584 
585  /* try creating the directory just in case it doesn't exist */
586  if (directory) {
587  char *name = ast_strdupa(tmpstring);
588  ast_mkdir(dirname(name), 0777);
589  }
590 
591  /*!
592  * \note We cannot just compare filenames, due to symlinks, relative
593  * paths, and other possible filesystem issues. We could use
594  * realpath(3), but its use is discouraged. However, if we try to
595  * create the same file from two different paths, the second will
596  * fail, and so we have our notification that the filenames point to
597  * the same path.
598  *
599  * Remember, also, that we're using the basename of the file (i.e.
600  * the file without the format suffix), so it does not already exist
601  * and we aren't interfering with the recording itself.
602  */
603  ast_debug(2, "comparing tmpstring %s to filename_base %s\n", tmpstring, chan->monitor->filename_base);
604 
605  if ((fd[0] = open(tmpstring, O_CREAT | O_WRONLY, 0644)) < 0 ||
606  (fd[1] = open(chan->monitor->filename_base, O_CREAT | O_EXCL | O_WRONLY, 0644)) < 0) {
607  if (fd[0] < 0) {
608  ast_log(LOG_ERROR, "Unable to compare filenames: %s\n", strerror(errno));
609  } else {
610  ast_debug(2, "No need to rename monitor filename to itself\n");
611  }
612  doexit = 1;
613  }
614 
615  /* Cleanup temporary files */
616  for (i = 0; i < 2; i++) {
617  if (fd[i] >= 0) {
618  while (close(fd[i]) < 0 && errno == EINTR);
619  }
620  }
621  unlink(tmpstring);
622  /* if previous monitor file existed in a subdirectory, the directory will not be removed */
623  unlink(chan->monitor->filename_base);
624 
625  if (doexit) {
626  UNLOCK_IF_NEEDED(chan, need_lock);
627  return 0;
628  }
629 
630  ast_copy_string(chan->monitor->filename_base, tmpstring, sizeof(chan->monitor->filename_base));
631  chan->monitor->filename_changed = 1;
632  } else {
633  ast_log(LOG_WARNING, "Cannot change monitor filename of channel %s to %s, monitoring not started\n", chan->name, fname_base);
634  }
635 
636  UNLOCK_IF_NEEDED(chan, need_lock);
637 
638  return 0;
639 }
640 
641 
642 /*!
643  * \brief Start monitor
644  * \param chan
645  * \param data arguments passed fname|options
646  * \retval 0 on success.
647  * \retval -1 on failure.
648 */
649 static int start_monitor_exec(struct ast_channel *chan, const char *data)
650 {
651  char *arg;
652  char *options;
653  char *delay;
654  char *urlprefix = NULL;
655  char tmp[256];
656  int stream_action = X_REC_IN | X_REC_OUT;
657  int joinfiles = 0;
658  int waitforbridge = 0;
659  int res = 0;
660  char *parse;
663  AST_APP_ARG(fname_base);
664  AST_APP_ARG(options);
665  );
666 
667  /* Parse arguments. */
668  if (ast_strlen_zero(data)) {
669  ast_log(LOG_ERROR, "Monitor requires an argument\n");
670  return 0;
671  }
672 
673  parse = ast_strdupa(data);
674  AST_STANDARD_APP_ARGS(args, parse);
675 
676  if (!ast_strlen_zero(args.options)) {
677  if (strchr(args.options, 'm'))
678  stream_action |= X_JOIN;
679  if (strchr(args.options, 'b'))
680  waitforbridge = 1;
681  if (strchr(args.options, 'i'))
682  stream_action &= ~X_REC_IN;
683  if (strchr(args.options, 'o'))
684  stream_action &= ~X_REC_OUT;
685  }
686 
687  arg = strchr(args.format, ':');
688  if (arg) {
689  *arg++ = 0;
690  urlprefix = arg;
691  }
692 
693  if (!ast_strlen_zero(urlprefix) && !ast_strlen_zero(args.fname_base)) {
694  snprintf(tmp, sizeof(tmp), "%s/%s.%s", urlprefix, args.fname_base,
695  ((strcmp(args.format, "gsm")) ? "wav" : "gsm"));
696  ast_channel_lock(chan);
697  if (!chan->cdr && !(chan->cdr = ast_cdr_alloc())) {
698  ast_channel_unlock(chan);
699  return -1;
700  }
701  ast_cdr_setuserfield(chan, tmp);
702  ast_channel_unlock(chan);
703  }
704  if (waitforbridge) {
705  /* We must remove the "b" option if listed. In principle none of
706  the following could give NULL results, but we check just to
707  be pedantic. Reconstructing with checks for 'm' option does not
708  work if we end up adding more options than 'm' in the future. */
709  delay = ast_strdupa(data);
710  options = strrchr(delay, ',');
711  if (options) {
712  arg = strchr(options, 'b');
713  if (arg) {
714  *arg = 'X';
715  pbx_builtin_setvar_helper(chan,"AUTO_MONITOR", delay);
716  }
717  }
718  return 0;
719  }
720 
721  res = ast_monitor_start(chan, args.format, args.fname_base, 1, stream_action);
722  if (res < 0)
723  res = ast_monitor_change_fname(chan, args.fname_base, 1);
724 
725  if (stream_action & X_JOIN) {
726  if ((stream_action & X_REC_IN) && (stream_action & X_REC_OUT))
727  joinfiles = 1;
728  else
729  ast_log(LOG_WARNING, "Won't mix streams unless both input and output streams are recorded\n");
730  }
731  ast_monitor_setjoinfiles(chan, joinfiles);
732 
733  return res;
734 }
735 
736 /*! \brief Wrapper function \see ast_monitor_stop */
737 static int stop_monitor_exec(struct ast_channel *chan, const char *data)
738 {
739  return ast_monitor_stop(chan, 1);
740 }
741 
742 /*! \brief Wrapper function \see ast_monitor_change_fname */
743 static int change_monitor_exec(struct ast_channel *chan, const char *data)
744 {
745  return ast_monitor_change_fname(chan, data, 1);
746 }
747 
748 /*! \brief Start monitoring a channel by manager connection */
749 static int start_monitor_action(struct mansession *s, const struct message *m)
750 {
751  struct ast_channel *c = NULL;
752  const char *name = astman_get_header(m, "Channel");
753  const char *fname = astman_get_header(m, "File");
754  const char *format = astman_get_header(m, "Format");
755  const char *mix = astman_get_header(m, "Mix");
756  char *d;
757 
758  if (ast_strlen_zero(name)) {
759  astman_send_error(s, m, "No channel specified");
760  return AMI_SUCCESS;
761  }
762 
763  if (!(c = ast_channel_get_by_name(name))) {
764  astman_send_error(s, m, "No such channel");
765  return AMI_SUCCESS;
766  }
767 
768  if (ast_strlen_zero(fname)) {
769  /* No filename specified, default to the channel name. */
770  ast_channel_lock(c);
771  fname = ast_strdupa(c->name);
773 
774  /* Replace all '/' chars from the channel name with '-' chars. */
775  for (d = (char *) fname; (d = strchr(d, '/')); ) {
776  *d = '-';
777  }
778  }
779 
780  if (ast_monitor_start(c, format, fname, 1, X_REC_IN | X_REC_OUT)) {
781  if (ast_monitor_change_fname(c, fname, 1)) {
782  astman_send_error(s, m, "Could not start monitoring channel");
783  c = ast_channel_unref(c);
784  return AMI_SUCCESS;
785  }
786  }
787 
788  if (ast_true(mix)) {
789  ast_channel_lock(c);
792  }
793 
794  c = ast_channel_unref(c);
795 
796  astman_send_ack(s, m, "Started monitoring channel");
797 
798  return AMI_SUCCESS;
799 }
800 
801 /*! \brief Stop monitoring a channel by manager connection */
802 static int stop_monitor_action(struct mansession *s, const struct message *m)
803 {
804  struct ast_channel *c = NULL;
805  const char *name = astman_get_header(m, "Channel");
806  int res;
807 
808  if (ast_strlen_zero(name)) {
809  astman_send_error(s, m, "No channel specified");
810  return AMI_SUCCESS;
811  }
812 
813  if (!(c = ast_channel_get_by_name(name))) {
814  astman_send_error(s, m, "No such channel");
815  return AMI_SUCCESS;
816  }
817 
818  res = ast_monitor_stop(c, 1);
819 
820  c = ast_channel_unref(c);
821 
822  if (res) {
823  astman_send_error(s, m, "Could not stop monitoring channel");
824  return AMI_SUCCESS;
825  }
826 
827  astman_send_ack(s, m, "Stopped monitoring channel");
828 
829  return AMI_SUCCESS;
830 }
831 
832 /*! \brief Change filename of a monitored channel by manager connection */
833 static int change_monitor_action(struct mansession *s, const struct message *m)
834 {
835  struct ast_channel *c = NULL;
836  const char *name = astman_get_header(m, "Channel");
837  const char *fname = astman_get_header(m, "File");
838 
839  if (ast_strlen_zero(name)) {
840  astman_send_error(s, m, "No channel specified");
841  return AMI_SUCCESS;
842  }
843 
844  if (ast_strlen_zero(fname)) {
845  astman_send_error(s, m, "No filename specified");
846  return AMI_SUCCESS;
847  }
848 
849  if (!(c = ast_channel_get_by_name(name))) {
850  astman_send_error(s, m, "No such channel");
851  return AMI_SUCCESS;
852  }
853 
854  if (ast_monitor_change_fname(c, fname, 1)) {
855  c = ast_channel_unref(c);
856  astman_send_error(s, m, "Could not change monitored filename of channel");
857  return AMI_SUCCESS;
858  }
859 
860  c = ast_channel_unref(c);
861 
862  astman_send_ack(s, m, "Changed monitor filename");
863 
864  return AMI_SUCCESS;
865 }
866 
868 {
869  if (chan->monitor)
870  chan->monitor->joinfiles = turnon;
871 }
872 
874 {
877 };
878 
879 static int do_pause_or_unpause(struct mansession *s, const struct message *m, int action)
880 {
881  struct ast_channel *c = NULL;
882  const char *name = astman_get_header(m, "Channel");
883 
884  if (ast_strlen_zero(name)) {
885  astman_send_error(s, m, "No channel specified");
886  return AMI_SUCCESS;
887  }
888 
889  if (!(c = ast_channel_get_by_name(name))) {
890  astman_send_error(s, m, "No such channel");
891  return AMI_SUCCESS;
892  }
893 
894  if (action == MONITOR_ACTION_PAUSE) {
896  } else {
898  }
899 
900  c = ast_channel_unref(c);
901 
902  astman_send_ack(s, m, (action == MONITOR_ACTION_PAUSE ? "Paused monitoring of the channel" : "Unpaused monitoring of the channel"));
903 
904  return AMI_SUCCESS;
905 }
906 
907 static int pause_monitor_action(struct mansession *s, const struct message *m)
908 {
910 }
911 
912 static int unpause_monitor_action(struct mansession *s, const struct message *m)
913 {
915 }
916 
917 static int load_module(void)
918 {
929 
931 }
932 
933 static int unload_module(void)
934 {
935  ast_unregister_application("Monitor");
936  ast_unregister_application("StopMonitor");
937  ast_unregister_application("ChangeMonitor");
938  ast_unregister_application("PauseMonitor");
939  ast_unregister_application("UnpauseMonitor");
940  ast_manager_unregister("Monitor");
941  ast_manager_unregister("StopMonitor");
942  ast_manager_unregister("ChangeMonitor");
943  ast_manager_unregister("PauseMonitor");
944  ast_manager_unregister("UnpauseMonitor");
945 
946  return 0;
947 }
948 
949 /* usecount semantics need to be defined */
951  .load = load_module,
952  .unload = unload_module,
953  .load_pri = AST_MODPRI_CHANNEL_DEPEND,
954  );
enum sip_cc_notify_state state
Definition: chan_sip.c:842
static int do_pause_or_unpause(struct mansession *s, const struct message *m, int action)
Definition: res_monitor.c:879
#define ast_channel_lock(chan)
Definition: channel.h:2466
#define X_REC_IN
Definition: monitor.h:35
char read_filename[FILENAME_MAX]
Definition: monitor.h:43
Main Channel structure associated with a channel.
Definition: channel.h:742
int ast_safe_system(const char *s)
Safely spawn an external program while closing file descriptors.
Definition: asterisk.c:1077
Asterisk locking-related definitions:
Asterisk main include file. File version handling, generic pbx functions.
static int ast_monitor_set_state(struct ast_channel *chan, int state)
Change state of monitored channel.
Definition: res_monitor.c:267
const ast_string_field uniqueid
Definition: channel.h:787
static int load_module(void)
Definition: res_monitor.c:917
#define ast_strdup(a)
Definition: astmm.h:109
#define ast_channel_unref(c)
Decrease channel reference count.
Definition: channel.h:2502
static int unpause_monitor_exec(struct ast_channel *chan, const char *data)
Wrapper for ast_monitor_unpause.
Definition: res_monitor.c:553
#define UNLOCK_IF_NEEDED(lock, needed)
Definition: res_monitor.c:253
#define LOG_WARNING
Definition: logger.h:144
static ast_mutex_t monitorlock
Definition: res_monitor.c:246
static int change_monitor_exec(struct ast_channel *chan, const char *data)
Wrapper function.
Definition: res_monitor.c:743
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application&#39;s arguments.
Definition: app.h:572
static int start_monitor_action(struct mansession *s, const struct message *m)
Start monitoring a channel by manager connection.
Definition: res_monitor.c:749
char filename_base[FILENAME_MAX]
Definition: monitor.h:45
static int unload_module(void)
Definition: res_monitor.c:933
static int pause_monitor_action(struct mansession *s, const struct message *m)
Definition: res_monitor.c:907
#define EVENT_FLAG_CALL
Definition: manager.h:72
Configuration File Parser.
int(* stop)(struct ast_channel *chan, int need_lock)
Definition: monitor.h:50
#define LOCK_IF_NEEDED(lock, needed)
Definition: res_monitor.c:248
void astman_send_ack(struct mansession *s, const struct message *m, char *msg)
Send ack in manager transaction.
Definition: manager.c:2135
#define ast_mutex_lock(a)
Definition: lock.h:155
struct ast_cdr * cdr
Definition: channel.h:766
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:374
static int unpause_monitor_action(struct mansession *s, const struct message *m)
Definition: res_monitor.c:912
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 * data
Definition: channel.h:755
int ast_filedelete(const char *filename, const char *fmt)
Deletes a file.
Definition: file.c:931
void ast_monitor_setjoinfiles(struct ast_channel *chan, int turnon)
Definition: res_monitor.c:867
char write_filename[FILENAME_MAX]
Definition: monitor.h:44
int ast_unregister_application(const char *app)
Unregister an application.
Definition: pbx.c:7705
#define AST_FILE_MODE
Definition: asterisk.h:36
static const char * get_soxmix_format(const char *format)
Get audio format.
Definition: res_monitor.c:418
static char urlprefix[AST_MAX_BUF]
Definition: chan_agent.c:236
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
#define X_REC_OUT
Definition: monitor.h:36
Utility functions.
const char * astman_get_header(const struct message *m, char *var)
Get header from mananger transaction.
Definition: manager.c:1860
#define ast_manager_event(chan, category, event, contents,...)
Definition: manager.h:221
int ast_cdr_setuserfield(struct ast_channel *chan, const char *userfield)
Set CDR user field for channel (stored in CDR)
Definition: cdr.c:1057
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:236
General Asterisk PBX channel definitions.
Asterisk file paths, configured in asterisk.conf.
int ast_monitor_unpause(struct ast_channel *chan)
Unpause monitoring of channel.
Definition: res_monitor.c:541
#define ast_manager_register_xml(a, b, c)
Register a manager callback using XML documentation to describe the manager.
Definition: manager.h:172
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:63
static int doexit
Definition: db.c:113
#define FILENAME_MAX
Definition: private.h:207
int ast_monitor_stop(struct ast_channel *chan, int need_lock)
Stop monitoring channel.
Definition: res_monitor.c:437
int ast_monitor_start(struct ast_channel *chan, const char *format_spec, const char *fname_base, int need_lock, int stream_action)
Start monitoring a channel.
Definition: res_monitor.c:290
Core PBX routines and definitions.
static int stop_monitor_action(struct mansession *s, const struct message *m)
Stop monitoring a channel by manager connection.
Definition: res_monitor.c:802
int ast_monitor_change_fname(struct ast_channel *chan, const char *fname_base, int need_lock)
Change monitored filename of channel.
Definition: res_monitor.c:566
The AMI - Asterisk Manager Interface - is a TCP protocol created to manage Asterisk with third-party ...
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: utils.h:663
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:833
#define LOG_ERROR
Definition: logger.h:155
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is &quot;true&quot;. This function checks to see whether a string passed to it is an indication of an &quot;true&quot; value. It checks to see if the string is &quot;yes&quot;, &quot;true&quot;, &quot;y&quot;, &quot;t&quot;, &quot;on&quot; or &quot;1&quot;.
Definition: utils.c:1533
static struct @350 args
static unsigned int monitor
Definition: chan_phone.c:108
const ast_string_field name
Definition: channel.h:787
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:1049
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
MONITOR_PAUSING_ACTION
Definition: res_monitor.c:873
struct ast_filestream * read_stream
Definition: monitor.h:41
int ast_monitor_pause(struct ast_channel *chan)
Pause monitoring of channel.
Definition: res_monitor.c:535
#define ast_channel_unlock(chan)
Definition: channel.h:2467
int errno
static void parse(struct mgcp_request *req)
Definition: chan_mgcp.c:1858
static const char name[]
#define ast_free(a)
Definition: astmm.h:97
const char * ast_config_AST_MONITOR_DIR
Definition: asterisk.c:260
struct ast_filestream * write_stream
Definition: monitor.h:42
int ast_closestream(struct ast_filestream *f)
Closes a stream.
Definition: file.c:904
static int stop_monitor_exec(struct ast_channel *chan, const char *data)
Wrapper function.
Definition: res_monitor.c:737
#define X_JOIN
Definition: monitor.h:37
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
Standard Command Line Interface.
#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
int ast_fileexists(const char *filename, const char *fmt, const char *preflang)
Checks for the existence of a given file.
Definition: file.c:919
Channel monitoring.
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
Options provided by main asterisk program.
static unsigned long seq
Definition: res_monitor.c:258
int ast_filerename(const char *oldname, const char *newname, const char *fmt)
Renames a file.
Definition: file.c:936
#define AST_APP_ARG(name)
Define an application argument.
Definition: app.h:555
#define AST_OPTIONAL_API_NAME(name)
Definition: optional_api.h:217
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the &#39;standard&#39; argument separation process for an application.
Definition: app.h:604
struct ast_channel_monitor * monitor
Definition: channel.h:769
enum AST_MONITORING_STATE state
Definition: monitor.h:49
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:38
struct ast_channel * ast_channel_get_by_name(const char *name)
Find a channel by name.
Definition: channel.c:1803
void astman_send_error(struct mansession *s, const struct message *m, char *error)
Send error in manager transaction.
Definition: manager.c:2130
Asterisk module definitions.
static snd_pcm_format_t format
Definition: chan_alsa.c:93
static int start_monitor_exec(struct ast_channel *chan, const char *data)
Start monitor.
Definition: res_monitor.c:649
static int pause_monitor_exec(struct ast_channel *chan, const char *data)
Wrapper for ast_monitor_pause.
Definition: res_monitor.c:547
struct ast_cdr * ast_cdr_alloc(void)
Allocate a CDR record.
Definition: cdr.c:499
#define AST_MUTEX_DEFINE_STATIC(mutex)
Definition: lock.h:526
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition: module.h:437
#define AMI_SUCCESS
Definition: manager.h:65
int ast_manager_unregister(char *action)
Unregister a registered manager command.
Definition: manager.c:5355
#define ASTERISK_FILE_VERSION(file, version)
Register/unregister a source code file with the core.
Definition: asterisk.h:180
#define ast_mutex_unlock(a)
Definition: lock.h:156
int ast_mkdir(const char *path, int mode)
Recursively create directory path.
Definition: utils.c:2151