res/res_monitor: Reset in/out sample counts on Monitor start
[asterisk/asterisk.git] / res / res_monitor.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2005, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*! \file
20  *
21  * \brief 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$")
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"
46 #include "asterisk/stasis_channels.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                                         </option>
86                                         <option name="b">
87                                                 <para>Don't begin recording unless a call is bridged to another channel.</para>
88                                         </option>
89                                         <option name="B">
90                                                 <para>Play a periodic beep while this call is being recorded.</para>
91                                                 <argument name="interval"><para>Interval, in seconds. Default is 15.</para></argument>
92                                         </option>
93                                         <option name="i">
94                                                 <para>Skip recording of input stream (disables <literal>m</literal> option).</para>
95                                         </option>
96                                         <option name="o">
97                                                 <para>Skip recording of output stream (disables <literal>m</literal> option).</para>
98                                         </option>
99                                 </optionlist>
100                         </parameter>
101                 </syntax>
102                 <description>
103                         <para>Used to start monitoring a channel. The channel's input and output
104                         voice packets are logged to files until the channel hangs up or
105                         monitoring is stopped by the StopMonitor application.</para>
106                         <para>By default, files are stored to <filename>/var/spool/asterisk/monitor/</filename>.
107                         Returns <literal>-1</literal> if monitor files can't be opened or if the channel is
108                         already monitored, otherwise <literal>0</literal>.</para>
109                 </description>
110                 <see-also>
111                         <ref type="application">StopMonitor</ref>
112                 </see-also>
113         </application>
114         <application name="StopMonitor" language="en_US">
115                 <synopsis>
116                         Stop monitoring a channel.
117                 </synopsis>
118                 <syntax />
119                 <description>
120                         <para>Stops monitoring a channel. Has no effect if the channel is not monitored.</para>
121                 </description>
122         </application>
123         <application name="ChangeMonitor" language="en_US">
124                 <synopsis>
125                         Change monitoring filename of a channel.
126                 </synopsis>
127                 <syntax>
128                         <parameter name="filename_base" required="true">
129                                 <para>The new filename base to use for monitoring this channel.</para>
130                         </parameter>
131                 </syntax>
132                 <description>
133                         <para>Changes monitoring filename of a channel. Has no effect if the
134                         channel is not monitored.</para>
135                 </description>
136         </application>
137         <application name="PauseMonitor" language="en_US">
138                 <synopsis>
139                         Pause monitoring of a channel.
140                 </synopsis>
141                 <syntax />
142                 <description>
143                         <para>Pauses monitoring of a channel until it is re-enabled by a call to UnpauseMonitor.</para>
144                 </description>
145                 <see-also>
146                         <ref type="application">UnpauseMonitor</ref>
147                 </see-also>
148         </application>
149         <application name="UnpauseMonitor" language="en_US">
150                 <synopsis>
151                         Unpause monitoring of a channel.
152                 </synopsis>
153                 <syntax />
154                 <description>
155                         <para>Unpauses monitoring of a channel on which monitoring had
156                         previously been paused with PauseMonitor.</para>
157                 </description>
158                 <see-also>
159                         <ref type="application">PauseMonitor</ref>
160                 </see-also>
161         </application>
162         <manager name="Monitor" language="en_US">
163                 <synopsis>
164                         Monitor a channel.
165                 </synopsis>
166                 <syntax>
167                         <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
168                         <parameter name="Channel" required="true">
169                                 <para>Used to specify the channel to record.</para>
170                         </parameter>
171                         <parameter name="File">
172                                 <para>Is the name of the file created in the monitor spool directory.
173                                 Defaults to the same name as the channel (with slashes replaced with dashes).</para>
174                         </parameter>
175                         <parameter name="Format">
176                                 <para>Is the audio recording format. Defaults to <literal>wav</literal>.</para>
177                         </parameter>
178                         <parameter name="Mix">
179                                 <para>Boolean parameter as to whether to mix the input and output channels
180                                 together after the recording is finished.</para>
181                         </parameter>
182                 </syntax>
183                 <description>
184                         <para>This action may be used to record the audio on a
185                         specified channel.</para>
186                 </description>
187         </manager>
188         <manager name="StopMonitor" language="en_US">
189                 <synopsis>
190                         Stop monitoring a channel.
191                 </synopsis>
192                 <syntax>
193                         <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
194                         <parameter name="Channel" required="true">
195                                 <para>The name of the channel monitored.</para>
196                         </parameter>
197                 </syntax>
198                 <description>
199                         <para>This action may be used to end a previously started 'Monitor' action.</para>
200                 </description>
201         </manager>
202         <manager name="ChangeMonitor" language="en_US">
203                 <synopsis>
204                         Change monitoring filename of a channel.
205                 </synopsis>
206                 <syntax>
207                         <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
208                         <parameter name="Channel" required="true">
209                                 <para>Used to specify the channel to record.</para>
210                         </parameter>
211                         <parameter name="File" required="true">
212                                 <para>Is the new name of the file created in the
213                                 monitor spool directory.</para>
214                         </parameter>
215                 </syntax>
216                 <description>
217                         <para>This action may be used to change the file
218                         started by a previous 'Monitor' action.</para>
219                 </description>
220         </manager>
221         <manager name="PauseMonitor" language="en_US">
222                 <synopsis>
223                         Pause monitoring of a channel.
224                 </synopsis>
225                 <syntax>
226                         <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
227                         <parameter name="Channel" required="true">
228                                 <para>Used to specify the channel to record.</para>
229                         </parameter>
230                 </syntax>
231                 <description>
232                         <para>This action may be used to temporarily stop the
233                         recording of a channel.</para>
234                 </description>
235         </manager>
236         <manager name="UnpauseMonitor" language="en_US">
237                 <synopsis>
238                         Unpause monitoring of a channel.
239                 </synopsis>
240                 <syntax>
241                         <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
242                         <parameter name="Channel" required="true">
243                                 <para>Used to specify the channel to record.</para>
244                         </parameter>
245                 </syntax>
246                 <description>
247                         <para>This action may be used to re-enable recording
248                         of a channel after calling PauseMonitor.</para>
249                 </description>
250         </manager>
251
252  ***/
253
254 AST_MUTEX_DEFINE_STATIC(monitorlock);
255
256 #define LOCK_IF_NEEDED(lock, needed) do { \
257         if (needed) \
258                 ast_channel_lock(lock); \
259         } while(0)
260
261 #define UNLOCK_IF_NEEDED(lock, needed) do { \
262         if (needed) \
263                 ast_channel_unlock(lock); \
264         } while (0)
265
266 static unsigned long seq = 0;
267
268 /*! 
269  * \brief Change state of monitored channel 
270  * \param chan 
271  * \param state monitor state
272  * \retval 0 on success.
273  * \retval -1 on failure.
274 */
275 static int ast_monitor_set_state(struct ast_channel *chan, int state)
276 {
277         LOCK_IF_NEEDED(chan, 1);
278         if (!ast_channel_monitor(chan)) {
279                 UNLOCK_IF_NEEDED(chan, 1);
280                 return -1;
281         }
282         ast_channel_monitor(chan)->state = state;
283         UNLOCK_IF_NEEDED(chan, 1);
284         return 0;
285 }
286
287 /*! \brief Start monitoring a channel
288  * \param chan ast_channel struct to record
289  * \param format_spec file format to use for recording
290  * \param fname_base filename base to record to
291  * \param need_lock whether to lock the channel mutex
292  * \param stream_action whether to record the input and/or output streams.  X_REC_IN | X_REC_OUT is most often used
293  * Creates the file to record, if no format is specified it assumes WAV
294  * It also sets channel variable __MONITORED=yes
295  * \retval 0 on success
296  * \retval -1 on failure
297  */
298 int AST_OPTIONAL_API_NAME(ast_monitor_start)(struct ast_channel *chan, const char *format_spec,
299                                              const char *fname_base, int need_lock, int stream_action,
300                                              const char *beep_id)
301 {
302         int res = 0;
303         RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);
304
305         LOCK_IF_NEEDED(chan, need_lock);
306
307         if (!(ast_channel_monitor(chan))) {
308                 struct ast_channel_monitor *monitor;
309                 char *channel_name, *p;
310
311                 /* Create monitoring directory if needed */
312                 ast_mkdir(ast_config_AST_MONITOR_DIR, 0777);
313
314                 if (!(monitor = ast_calloc(1, sizeof(*monitor)))) {
315                         UNLOCK_IF_NEEDED(chan, need_lock);
316                         return -1;
317                 }
318
319                 if (!ast_strlen_zero(beep_id)) {
320                         ast_copy_string(monitor->beep_id, beep_id, sizeof(monitor->beep_id));
321                 }
322
323                 /* Determine file names */
324                 if (!ast_strlen_zero(fname_base)) {
325                         int directory = strchr(fname_base, '/') ? 1 : 0;
326                         const char *absolute = *fname_base == '/' ? "" : ast_config_AST_MONITOR_DIR;
327                         const char *absolute_suffix = *fname_base == '/' ? "" : "/";
328
329                         snprintf(monitor->read_filename, FILENAME_MAX, "%s%s%s-in",
330                                                 absolute, absolute_suffix, fname_base);
331                         snprintf(monitor->write_filename, FILENAME_MAX, "%s%s%s-out",
332                                                 absolute, absolute_suffix, fname_base);
333                         snprintf(monitor->filename_base, FILENAME_MAX, "%s%s%s",
334                                                 absolute, absolute_suffix, fname_base);
335
336                         /* try creating the directory just in case it doesn't exist */
337                         if (directory) {
338                                 char *name = ast_strdupa(monitor->filename_base);
339                                 ast_mkdir(dirname(name), 0777);
340                         }
341                 } else {
342                         ast_mutex_lock(&monitorlock);
343                         snprintf(monitor->read_filename, FILENAME_MAX, "%s/audio-in-%lu",
344                                                 ast_config_AST_MONITOR_DIR, seq);
345                         snprintf(monitor->write_filename, FILENAME_MAX, "%s/audio-out-%lu",
346                                                 ast_config_AST_MONITOR_DIR, seq);
347                         seq++;
348                         ast_mutex_unlock(&monitorlock);
349
350                         /* Replace all '/' chars from the channel name with '-' chars. */
351                         channel_name = ast_strdupa(ast_channel_name(chan));
352                         for (p = channel_name; (p = strchr(p, '/')); ) {
353                                 *p = '-';
354                         }
355
356                         snprintf(monitor->filename_base, FILENAME_MAX, "%s/%d-%s",
357                                          ast_config_AST_MONITOR_DIR, (int)time(NULL), channel_name);
358                         monitor->filename_changed = 1;
359                 }
360
361                 monitor->stop = ast_monitor_stop;
362
363                 /* Determine file format */
364                 if (!ast_strlen_zero(format_spec)) {
365                         monitor->format = ast_strdup(format_spec);
366                 } else {
367                         monitor->format = ast_strdup("wav");
368                 }
369                 
370                 /* open files */
371                 if (stream_action & X_REC_IN) {
372                         if (ast_fileexists(monitor->read_filename, NULL, NULL) > 0)
373                                 ast_filedelete(monitor->read_filename, NULL);
374                         if (!(monitor->read_stream = ast_writefile(monitor->read_filename,
375                                                         monitor->format, NULL,
376                                                         O_CREAT|O_TRUNC|O_WRONLY, 0, AST_FILE_MODE))) {
377                                 ast_log(LOG_WARNING, "Could not create file %s\n",
378                                                         monitor->read_filename);
379                                 ast_free(monitor);
380                                 UNLOCK_IF_NEEDED(chan, need_lock);
381                                 return -1;
382                         }
383                 } else
384                         monitor->read_stream = NULL;
385
386                 if (stream_action & X_REC_OUT) {
387                         if (ast_fileexists(monitor->write_filename, NULL, NULL) > 0) {
388                                 ast_filedelete(monitor->write_filename, NULL);
389                         }
390                         if (!(monitor->write_stream = ast_writefile(monitor->write_filename,
391                                                         monitor->format, NULL,
392                                                         O_CREAT|O_TRUNC|O_WRONLY, 0, AST_FILE_MODE))) {
393                                 ast_log(LOG_WARNING, "Could not create file %s\n",
394                                                         monitor->write_filename);
395                                 if (monitor->read_stream) {
396                                         ast_closestream(monitor->read_stream);
397                                 }
398                                 ast_free(monitor);
399                                 UNLOCK_IF_NEEDED(chan, need_lock);
400                                 return -1;
401                         }
402                 } else
403                         monitor->write_stream = NULL;
404
405                 ast_channel_insmpl_set(chan, 0);
406                 ast_channel_outsmpl_set(chan, 0);
407                 ast_channel_monitor_set(chan, monitor);
408                 ast_monitor_set_state(chan, AST_MONITOR_RUNNING);
409                 /* so we know this call has been monitored in case we need to bill for it or something */
410                 pbx_builtin_setvar_helper(chan, "__MONITORED","true");
411
412                 message = ast_channel_blob_create_from_cache(ast_channel_uniqueid(chan),
413                                 ast_channel_monitor_start_type(),
414                                 NULL);
415                 if (message) {
416                         stasis_publish(ast_channel_topic(chan), message);
417                 }
418         } else {
419                 ast_debug(1,"Cannot start monitoring %s, already monitored\n", ast_channel_name(chan));
420                 res = -1;
421         }
422
423         UNLOCK_IF_NEEDED(chan, need_lock);
424
425         return res;
426 }
427
428 /*!
429  * \brief Get audio format.
430  * \param format recording format.
431  * The file format extensions that Asterisk uses are not all the same as that
432  * which soxmix expects.  This function ensures that the format used as the
433  * extension on the filename is something soxmix will understand.
434  */
435 static const char *get_soxmix_format(const char *format)
436 {
437         const char *res = format;
438
439         if (!strcasecmp(format,"ulaw"))
440                 res = "ul";
441         if (!strcasecmp(format,"alaw"))
442                 res = "al";
443         
444         return res;
445 }
446
447 /*! 
448  * \brief Stop monitoring channel 
449  * \param chan 
450  * \param need_lock
451  * Stop the recording, close any open streams, mix in/out channels if required
452  * \return Always 0
453 */
454 int AST_OPTIONAL_API_NAME(ast_monitor_stop)(struct ast_channel *chan, int need_lock)
455 {
456         int delfiles = 0;
457         RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);
458
459         LOCK_IF_NEEDED(chan, need_lock);
460
461         if (ast_channel_monitor(chan)) {
462                 char filename[ FILENAME_MAX ];
463
464                 if (ast_channel_monitor(chan)->read_stream) {
465                         ast_closestream(ast_channel_monitor(chan)->read_stream);
466                 }
467                 if (ast_channel_monitor(chan)->write_stream) {
468                         ast_closestream(ast_channel_monitor(chan)->write_stream);
469                 }
470
471                 if (ast_channel_monitor(chan)->filename_changed && !ast_strlen_zero(ast_channel_monitor(chan)->filename_base)) {
472                         if (ast_fileexists(ast_channel_monitor(chan)->read_filename,NULL,NULL) > 0) {
473                                 snprintf(filename, FILENAME_MAX, "%s-in", ast_channel_monitor(chan)->filename_base);
474                                 if (ast_fileexists(filename, NULL, NULL) > 0) {
475                                         ast_filedelete(filename, NULL);
476                                 }
477                                 ast_filerename(ast_channel_monitor(chan)->read_filename, filename, ast_channel_monitor(chan)->format);
478                         } else {
479                                 ast_log(LOG_WARNING, "File %s not found\n", ast_channel_monitor(chan)->read_filename);
480                         }
481
482                         if (ast_fileexists(ast_channel_monitor(chan)->write_filename,NULL,NULL) > 0) {
483                                 snprintf(filename, FILENAME_MAX, "%s-out", ast_channel_monitor(chan)->filename_base);
484                                 if (ast_fileexists(filename, NULL, NULL) > 0) {
485                                         ast_filedelete(filename, NULL);
486                                 }
487                                 ast_filerename(ast_channel_monitor(chan)->write_filename, filename, ast_channel_monitor(chan)->format);
488                         } else {
489                                 ast_log(LOG_WARNING, "File %s not found\n", ast_channel_monitor(chan)->write_filename);
490                         }
491                 }
492
493                 if (ast_channel_monitor(chan)->joinfiles && !ast_strlen_zero(ast_channel_monitor(chan)->filename_base)) {
494                         char tmp[1024];
495                         char tmp2[1024];
496                         const char *format = !strcasecmp(ast_channel_monitor(chan)->format,"wav49") ? "WAV" : ast_channel_monitor(chan)->format;
497                         char *fname_base = ast_channel_monitor(chan)->filename_base;
498                         const char *execute, *execute_args;
499                         /* at this point, fname_base really is the full path */
500
501                         /* Set the execute application */
502                         execute = pbx_builtin_getvar_helper(chan, "MONITOR_EXEC");
503                         if (ast_strlen_zero(execute)) {
504 #ifdef HAVE_SOXMIX
505                                 execute = "nice -n 19 soxmix";
506 #else
507                                 execute = "nice -n 19 sox -m";
508 #endif
509                                 format = get_soxmix_format(format);
510                                 delfiles = 1;
511                         } 
512                         execute_args = pbx_builtin_getvar_helper(chan, "MONITOR_EXEC_ARGS");
513                         if (ast_strlen_zero(execute_args)) {
514                                 execute_args = "";
515                         }
516                         
517                         snprintf(tmp, sizeof(tmp), "%s \"%s-in.%s\" \"%s-out.%s\" \"%s.%s\" %s &",
518                                 execute, fname_base, format, fname_base, format, fname_base, format,execute_args);
519                         if (delfiles) {
520                                 snprintf(tmp2,sizeof(tmp2), "( %s& rm -f \"%s-\"* ) &",tmp, fname_base); /* remove legs when done mixing */
521                                 ast_copy_string(tmp, tmp2, sizeof(tmp));
522                         }
523                         ast_debug(1,"monitor executing %s\n",tmp);
524                         if (ast_safe_system(tmp) == -1)
525                                 ast_log(LOG_WARNING, "Execute of %s failed.\n",tmp);
526                 }
527
528                 if (!ast_strlen_zero(ast_channel_monitor(chan)->beep_id)) {
529                         ast_beep_stop(chan, ast_channel_monitor(chan)->beep_id);
530                 }
531
532                 ast_free(ast_channel_monitor(chan)->format);
533                 ast_free(ast_channel_monitor(chan));
534                 ast_channel_monitor_set(chan, NULL);
535
536                 message = ast_channel_blob_create_from_cache(ast_channel_uniqueid(chan),
537                                 ast_channel_monitor_stop_type(),
538                                 NULL);
539                 if (message) {
540                         stasis_publish(ast_channel_topic(chan), message);
541                 }
542                 pbx_builtin_setvar_helper(chan, "MONITORED", NULL);
543         }
544         pbx_builtin_setvar_helper(chan, "AUTO_MONITOR", NULL);
545
546         UNLOCK_IF_NEEDED(chan, need_lock);
547
548         return 0;
549 }
550
551
552 /*! \brief Pause monitoring of channel */
553 int AST_OPTIONAL_API_NAME(ast_monitor_pause)(struct ast_channel *chan)
554 {
555         return ast_monitor_set_state(chan, AST_MONITOR_PAUSED);
556 }
557
558 /*! \brief Unpause monitoring of channel */
559 int AST_OPTIONAL_API_NAME(ast_monitor_unpause)(struct ast_channel *chan)
560 {
561         return ast_monitor_set_state(chan, AST_MONITOR_RUNNING);
562 }
563
564 /*! \brief Wrapper for ast_monitor_pause */
565 static int pause_monitor_exec(struct ast_channel *chan, const char *data)
566 {
567         return ast_monitor_pause(chan);
568 }
569
570 /*! \brief Wrapper for ast_monitor_unpause */
571 static int unpause_monitor_exec(struct ast_channel *chan, const char *data)
572 {
573         return ast_monitor_unpause(chan);
574 }
575
576 /*! 
577  * \brief Change monitored filename of channel 
578  * \param chan
579  * \param fname_base new filename
580  * \param need_lock
581  * \retval 0 on success.
582  * \retval -1 on failure.
583 */
584 int AST_OPTIONAL_API_NAME(ast_monitor_change_fname)(struct ast_channel *chan, const char *fname_base, int need_lock)
585 {
586         if (ast_strlen_zero(fname_base)) {
587                 ast_log(LOG_WARNING, "Cannot change monitor filename of channel %s to null\n", ast_channel_name(chan));
588                 return -1;
589         }
590
591         LOCK_IF_NEEDED(chan, need_lock);
592
593         if (ast_channel_monitor(chan)) {
594                 int directory = strchr(fname_base, '/') ? 1 : 0;
595                 const char *absolute = *fname_base == '/' ? "" : ast_config_AST_MONITOR_DIR;
596                 const char *absolute_suffix = *fname_base == '/' ? "" : "/";
597                 char tmpstring[sizeof(ast_channel_monitor(chan)->filename_base)] = "";
598                 int i, fd[2] = { -1, -1 }, doexit = 0;
599
600                 /* before continuing, see if we're trying to rename the file to itself... */
601                 snprintf(tmpstring, sizeof(tmpstring), "%s%s%s", absolute, absolute_suffix, fname_base);
602
603                 /* try creating the directory just in case it doesn't exist */
604                 if (directory) {
605                         char *name = ast_strdupa(tmpstring);
606                         ast_mkdir(dirname(name), 0777);
607                 }
608
609                 /*!
610                  * \note We cannot just compare filenames, due to symlinks, relative
611                  * paths, and other possible filesystem issues.  We could use
612                  * realpath(3), but its use is discouraged.  However, if we try to
613                  * create the same file from two different paths, the second will
614                  * fail, and so we have our notification that the filenames point to
615                  * the same path.
616                  *
617                  * Remember, also, that we're using the basename of the file (i.e.
618                  * the file without the format suffix), so it does not already exist
619                  * and we aren't interfering with the recording itself.
620                  */
621                 ast_debug(2, "comparing tmpstring %s to filename_base %s\n", tmpstring, ast_channel_monitor(chan)->filename_base);
622                 
623                 if ((fd[0] = open(tmpstring, O_CREAT | O_WRONLY, 0644)) < 0 ||
624                         (fd[1] = open(ast_channel_monitor(chan)->filename_base, O_CREAT | O_EXCL | O_WRONLY, 0644)) < 0) {
625                         if (fd[0] < 0) {
626                                 ast_log(LOG_ERROR, "Unable to compare filenames: %s\n", strerror(errno));
627                         } else {
628                                 ast_debug(2, "No need to rename monitor filename to itself\n");
629                         }
630                         doexit = 1;
631                 }
632
633                 /* Cleanup temporary files */
634                 for (i = 0; i < 2; i++) {
635                         if (fd[i] >= 0) {
636                                 while (close(fd[i]) < 0 && errno == EINTR);
637                         }
638                 }
639                 unlink(tmpstring);
640                 /* if previous monitor file existed in a subdirectory, the directory will not be removed */
641                 unlink(ast_channel_monitor(chan)->filename_base);
642
643                 if (doexit) {
644                         UNLOCK_IF_NEEDED(chan, need_lock);
645                         return 0;
646                 }
647
648                 ast_copy_string(ast_channel_monitor(chan)->filename_base, tmpstring, sizeof(ast_channel_monitor(chan)->filename_base));
649                 ast_channel_monitor(chan)->filename_changed = 1;
650         } else {
651                 ast_log(LOG_WARNING, "Cannot change monitor filename of channel %s to %s, monitoring not started\n", ast_channel_name(chan), fname_base);
652         }
653
654         UNLOCK_IF_NEEDED(chan, need_lock);
655
656         return 0;
657 }
658
659 enum {
660         MON_FLAG_BRIDGED =  (1 << 0),
661         MON_FLAG_MIX =      (1 << 1),
662         MON_FLAG_DROP_IN =  (1 << 2),
663         MON_FLAG_DROP_OUT = (1 << 3),
664         MON_FLAG_BEEP =     (1 << 4),
665 };
666
667 enum {
668         OPT_ARG_BEEP_INTERVAL,
669         OPT_ARG_ARRAY_SIZE,     /* Always last element of the enum */
670 };
671
672 AST_APP_OPTIONS(monitor_opts, {
673         AST_APP_OPTION('b', MON_FLAG_BRIDGED),
674         AST_APP_OPTION('m', MON_FLAG_MIX),
675         AST_APP_OPTION('i', MON_FLAG_DROP_IN),
676         AST_APP_OPTION('o', MON_FLAG_DROP_OUT),
677         AST_APP_OPTION_ARG('B', MON_FLAG_BEEP, OPT_ARG_BEEP_INTERVAL),
678 });
679
680 /*!
681  * \brief Start monitor
682  * \param chan
683  * \param data arguments passed fname|options
684  * \retval 0 on success.
685  * \retval -1 on failure.
686 */
687 static int start_monitor_exec(struct ast_channel *chan, const char *data)
688 {
689         char *arg;
690         char *options;
691         char *delay;
692         char *urlprefix = NULL;
693         char tmp[256];
694         int stream_action = X_REC_IN | X_REC_OUT;
695         int joinfiles = 0;
696         int res = 0;
697         char *parse;
698         struct ast_flags flags = { 0 };
699         char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, };
700         char beep_id[64] = "";
701         AST_DECLARE_APP_ARGS(args,
702                 AST_APP_ARG(format);
703                 AST_APP_ARG(fname_base);
704                 AST_APP_ARG(options);
705         );
706         
707         /* Parse arguments. */
708         if (ast_strlen_zero(data)) {
709                 ast_log(LOG_ERROR, "Monitor requires an argument\n");
710                 return 0;
711         }
712
713         parse = ast_strdupa(data);
714         AST_STANDARD_APP_ARGS(args, parse);
715
716         if (!ast_strlen_zero(args.options)) {
717                 ast_app_parse_options(monitor_opts, &flags, opts, args.options);
718
719                 if (ast_test_flag(&flags, MON_FLAG_MIX)) {
720                         stream_action |= X_JOIN;
721                 }
722                 if (ast_test_flag(&flags, MON_FLAG_DROP_IN)) {
723                         stream_action &= ~X_REC_IN;
724                 }
725                 if (ast_test_flag(&flags, MON_FLAG_DROP_OUT)) {
726                         stream_action &= ~X_REC_OUT;
727                 }
728                 if (ast_test_flag(&flags, MON_FLAG_BEEP)) {
729                         const char *interval_str = S_OR(opts[OPT_ARG_BEEP_INTERVAL], "15");
730                         unsigned int interval = 15;
731
732                         if (sscanf(interval_str, "%30u", &interval) != 1) {
733                                 ast_log(LOG_WARNING, "Invalid interval '%s' for periodic beep. Using default of %u\n",
734                                                 interval_str, interval);
735                         }
736
737                         if (ast_beep_start(chan, interval, beep_id, sizeof(beep_id))) {
738                                 ast_log(LOG_WARNING, "Unable to enable periodic beep, please ensure func_periodic_hook is loaded.\n");
739                                 return -1;
740                         }
741                 }
742         }
743
744         arg = strchr(args.format, ':');
745         if (arg) {
746                 *arg++ = 0;
747                 urlprefix = arg;
748         }
749
750         if (!ast_strlen_zero(urlprefix) && !ast_strlen_zero(args.fname_base)) {
751                 snprintf(tmp, sizeof(tmp), "%s/%s.%s", urlprefix, args.fname_base,
752                         ((strcmp(args.format, "gsm")) ? "wav" : "gsm"));
753                 ast_channel_lock(chan);
754                 ast_cdr_setuserfield(ast_channel_name(chan), tmp);
755                 ast_channel_unlock(chan);
756         }
757         if (ast_test_flag(&flags, MON_FLAG_BRIDGED)) {
758                 /* We must remove the "b" option if listed.  In principle none of
759                    the following could give NULL results, but we check just to
760                    be pedantic. Reconstructing with checks for 'm' option does not
761                    work if we end up adding more options than 'm' in the future. */
762                 delay = ast_strdupa(data);
763                 options = strrchr(delay, ',');
764                 if (options) {
765                         arg = strchr(options, 'b');
766                         if (arg) {
767                                 *arg = 'X';
768                                 pbx_builtin_setvar_helper(chan,"AUTO_MONITOR", delay);
769                         }
770                 }
771                 return 0;
772         }
773
774         res = ast_monitor_start(chan, args.format, args.fname_base, 1, stream_action, beep_id);
775         if (res < 0)
776                 res = ast_monitor_change_fname(chan, args.fname_base, 1);
777
778         if (stream_action & X_JOIN) {
779                 if ((stream_action & X_REC_IN) && (stream_action & X_REC_OUT))
780                         joinfiles = 1;
781                 else
782                         ast_log(LOG_WARNING, "Won't mix streams unless both input and output streams are recorded\n");
783         }
784         ast_monitor_setjoinfiles(chan, joinfiles);
785
786         return res;
787 }
788
789 /*! \brief Wrapper function \see ast_monitor_stop */
790 static int stop_monitor_exec(struct ast_channel *chan, const char *data)
791 {
792         return ast_monitor_stop(chan, 1);
793 }
794
795 /*! \brief Wrapper function \see ast_monitor_change_fname */
796 static int change_monitor_exec(struct ast_channel *chan, const char *data)
797 {
798         return ast_monitor_change_fname(chan, data, 1);
799 }
800
801 /*! \brief Start monitoring a channel by manager connection */
802 static int start_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         const char *fname = astman_get_header(m, "File");
807         const char *format = astman_get_header(m, "Format");
808         const char *mix = astman_get_header(m, "Mix");
809         char *d;
810
811         if (ast_strlen_zero(name)) {
812                 astman_send_error(s, m, "No channel specified");
813                 return AMI_SUCCESS;
814         }
815
816         if (!(c = ast_channel_get_by_name(name))) {
817                 astman_send_error(s, m, "No such channel");
818                 return AMI_SUCCESS;
819         }
820
821         if (ast_strlen_zero(fname)) {
822                 /* No filename specified, default to the channel name. */
823                 ast_channel_lock(c);
824                 fname = ast_strdupa(ast_channel_name(c));
825                 ast_channel_unlock(c);
826
827                 /* Replace all '/' chars from the channel name with '-' chars. */
828                 for (d = (char *) fname; (d = strchr(d, '/')); ) {
829                         *d = '-';
830                 }
831         }
832
833         if (ast_monitor_start(c, format, fname, 1, X_REC_IN | X_REC_OUT, NULL)) {
834                 if (ast_monitor_change_fname(c, fname, 1)) {
835                         astman_send_error(s, m, "Could not start monitoring channel");
836                         c = ast_channel_unref(c);
837                         return AMI_SUCCESS;
838                 }
839         }
840
841         if (ast_true(mix)) {
842                 ast_channel_lock(c);
843                 ast_monitor_setjoinfiles(c, 1);
844                 ast_channel_unlock(c);
845         }
846
847         c = ast_channel_unref(c);
848
849         astman_send_ack(s, m, "Started monitoring channel");
850
851         return AMI_SUCCESS;
852 }
853
854 /*! \brief Stop monitoring a channel by manager connection */
855 static int stop_monitor_action(struct mansession *s, const struct message *m)
856 {
857         struct ast_channel *c = NULL;
858         const char *name = astman_get_header(m, "Channel");
859         int res;
860
861         if (ast_strlen_zero(name)) {
862                 astman_send_error(s, m, "No channel specified");
863                 return AMI_SUCCESS;
864         }
865
866         if (!(c = ast_channel_get_by_name(name))) {
867                 astman_send_error(s, m, "No such channel");
868                 return AMI_SUCCESS;
869         }
870
871         res = ast_monitor_stop(c, 1);
872
873         c = ast_channel_unref(c);
874
875         if (res) {
876                 astman_send_error(s, m, "Could not stop monitoring channel");
877                 return AMI_SUCCESS;
878         }
879
880         astman_send_ack(s, m, "Stopped monitoring channel");
881
882         return AMI_SUCCESS;
883 }
884
885 /*! \brief Change filename of a monitored channel by manager connection */
886 static int change_monitor_action(struct mansession *s, const struct message *m)
887 {
888         struct ast_channel *c = NULL;
889         const char *name = astman_get_header(m, "Channel");
890         const char *fname = astman_get_header(m, "File");
891
892         if (ast_strlen_zero(name)) {
893                 astman_send_error(s, m, "No channel specified");
894                 return AMI_SUCCESS;
895         }
896
897         if (ast_strlen_zero(fname)) {
898                 astman_send_error(s, m, "No filename specified");
899                 return AMI_SUCCESS;
900         }
901
902         if (!(c = ast_channel_get_by_name(name))) {
903                 astman_send_error(s, m, "No such channel");
904                 return AMI_SUCCESS;
905         }
906
907         if (ast_monitor_change_fname(c, fname, 1)) {
908                 c = ast_channel_unref(c);
909                 astman_send_error(s, m, "Could not change monitored filename of channel");
910                 return AMI_SUCCESS;
911         }
912
913         c = ast_channel_unref(c);
914
915         astman_send_ack(s, m, "Changed monitor filename");
916
917         return AMI_SUCCESS;
918 }
919
920 void AST_OPTIONAL_API_NAME(ast_monitor_setjoinfiles)(struct ast_channel *chan, int turnon)
921 {
922         if (ast_channel_monitor(chan))
923                 ast_channel_monitor(chan)->joinfiles = turnon;
924 }
925
926 enum MONITOR_PAUSING_ACTION
927 {
928         MONITOR_ACTION_PAUSE,
929         MONITOR_ACTION_UNPAUSE
930 };
931
932 static int do_pause_or_unpause(struct mansession *s, const struct message *m, int action)
933 {
934         struct ast_channel *c = NULL;
935         const char *name = astman_get_header(m, "Channel");
936
937         if (ast_strlen_zero(name)) {
938                 astman_send_error(s, m, "No channel specified");
939                 return AMI_SUCCESS;
940         }
941
942         if (!(c = ast_channel_get_by_name(name))) {
943                 astman_send_error(s, m, "No such channel");
944                 return AMI_SUCCESS;
945         }
946
947         if (action == MONITOR_ACTION_PAUSE) {
948                 ast_monitor_pause(c);
949         } else {
950                 ast_monitor_unpause(c);
951         }
952
953         c = ast_channel_unref(c);
954
955         astman_send_ack(s, m, (action == MONITOR_ACTION_PAUSE ? "Paused monitoring of the channel" : "Unpaused monitoring of the channel"));
956
957         return AMI_SUCCESS;
958 }
959
960 static int pause_monitor_action(struct mansession *s, const struct message *m)
961 {
962         return do_pause_or_unpause(s, m, MONITOR_ACTION_PAUSE);
963 }
964
965 static int unpause_monitor_action(struct mansession *s, const struct message *m)
966 {
967         return do_pause_or_unpause(s, m, MONITOR_ACTION_UNPAUSE);
968 }
969
970 static int load_module(void)
971 {
972         ast_register_application_xml("Monitor", start_monitor_exec);
973         ast_register_application_xml("StopMonitor", stop_monitor_exec);
974         ast_register_application_xml("ChangeMonitor", change_monitor_exec);
975         ast_register_application_xml("PauseMonitor", pause_monitor_exec);
976         ast_register_application_xml("UnpauseMonitor", unpause_monitor_exec);
977         ast_manager_register_xml("Monitor", EVENT_FLAG_CALL, start_monitor_action);
978         ast_manager_register_xml("StopMonitor", EVENT_FLAG_CALL, stop_monitor_action);
979         ast_manager_register_xml("ChangeMonitor", EVENT_FLAG_CALL, change_monitor_action);
980         ast_manager_register_xml("PauseMonitor", EVENT_FLAG_CALL, pause_monitor_action);
981         ast_manager_register_xml("UnpauseMonitor", EVENT_FLAG_CALL, unpause_monitor_action);
982
983         return AST_MODULE_LOAD_SUCCESS;
984 }
985
986 static int unload_module(void)
987 {
988         ast_unregister_application("Monitor");
989         ast_unregister_application("StopMonitor");
990         ast_unregister_application("ChangeMonitor");
991         ast_unregister_application("PauseMonitor");
992         ast_unregister_application("UnpauseMonitor");
993         ast_manager_unregister("Monitor");
994         ast_manager_unregister("StopMonitor");
995         ast_manager_unregister("ChangeMonitor");
996         ast_manager_unregister("PauseMonitor");
997         ast_manager_unregister("UnpauseMonitor");
998
999         return 0;
1000 }
1001
1002 /* usecount semantics need to be defined */
1003 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "Call Monitoring Resource",
1004                 .support_level = AST_MODULE_SUPPORT_CORE,
1005                 .load = load_module,
1006                 .unload = unload_module,
1007                 .load_pri = AST_MODPRI_CHANNEL_DEPEND,
1008                 );