ARI: Fix a crash caused by hanging during playback to a channel in a bridge
[asterisk/asterisk.git] / res / res_stasis_recording.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2013, Digium, Inc.
5  *
6  * David M. Lee, II <dlee@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 res_stasis recording support.
22  *
23  * \author David M. Lee, II <dlee@digium.com>
24  */
25
26 /*** MODULEINFO
27         <depend type="module">res_stasis</depend>
28         <support_level>core</support_level>
29  ***/
30
31 #include "asterisk.h"
32
33 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
34
35 #include "asterisk/dsp.h"
36 #include "asterisk/file.h"
37 #include "asterisk/module.h"
38 #include "asterisk/paths.h"
39 #include "asterisk/stasis_app_impl.h"
40 #include "asterisk/stasis_app_recording.h"
41 #include "asterisk/stasis_channels.h"
42
43 /*! Number of hash buckets for recording container. Keep it prime! */
44 #define RECORDING_BUCKETS 127
45
46 /*! Comment is ignored by most formats, so we will ignore it, too. */
47 #define RECORDING_COMMENT NULL
48
49 /*! Recording check is unimplemented. le sigh */
50 #define RECORDING_CHECK 0
51
52 /*! Container of all current recordings */
53 static struct ao2_container *recordings;
54
55 struct stasis_app_recording {
56         /*! Recording options. */
57         struct stasis_app_recording_options *options;
58         /*! Absolute path (minus extension) of the recording */
59         char *absolute_name;
60         /*! Control object for the channel we're recording */
61         struct stasis_app_control *control;
62         /*! Current state of the recording. */
63         enum stasis_app_recording_state state;
64         /*! Duration calculations */
65         struct {
66                 /*! Total duration */
67                 int total;
68                 /*! Duration minus any silence */
69                 int energy_only;
70         } duration;
71         /*! Indicates whether the recording is currently muted */
72         int muted:1;
73 };
74
75 static struct ast_json *recording_to_json(struct stasis_message *message,
76         const struct stasis_message_sanitizer *sanitize)
77 {
78         struct ast_channel_blob *channel_blob = stasis_message_data(message);
79         struct ast_json *blob = channel_blob->blob;
80         const char *state =
81                 ast_json_string_get(ast_json_object_get(blob, "state"));
82         const char *type;
83
84         if (!strcmp(state, "recording")) {
85                 type = "RecordingStarted";
86         } else if (!strcmp(state, "done") || !strcasecmp(state, "canceled")) {
87                 type = "RecordingFinished";
88         } else if (!strcmp(state, "failed")) {
89                 type = "RecordingFailed";
90         } else {
91                 return NULL;
92         }
93
94         return ast_json_pack("{s: s, s: O}",
95                 "type", type,
96                 "recording", blob);
97 }
98
99 STASIS_MESSAGE_TYPE_DEFN(stasis_app_recording_snapshot_type,
100         .to_json = recording_to_json,
101 );
102
103 static int recording_hash(const void *obj, int flags)
104 {
105         const struct stasis_app_recording *recording = obj;
106         const char *id = flags & OBJ_KEY ? obj : recording->options->name;
107         return ast_str_hash(id);
108 }
109
110 static int recording_cmp(void *obj, void *arg, int flags)
111 {
112         struct stasis_app_recording *lhs = obj;
113         struct stasis_app_recording *rhs = arg;
114         const char *rhs_id = flags & OBJ_KEY ? arg : rhs->options->name;
115
116         if (strcmp(lhs->options->name, rhs_id) == 0) {
117                 return CMP_MATCH | CMP_STOP;
118         } else {
119                 return 0;
120         }
121 }
122
123 static const char *state_to_string(enum stasis_app_recording_state state)
124 {
125         switch (state) {
126         case STASIS_APP_RECORDING_STATE_QUEUED:
127                 return "queued";
128         case STASIS_APP_RECORDING_STATE_RECORDING:
129                 return "recording";
130         case STASIS_APP_RECORDING_STATE_PAUSED:
131                 return "paused";
132         case STASIS_APP_RECORDING_STATE_COMPLETE:
133                 return "done";
134         case STASIS_APP_RECORDING_STATE_FAILED:
135                 return "failed";
136         case STASIS_APP_RECORDING_STATE_CANCELED:
137                 return "canceled";
138         case STASIS_APP_RECORDING_STATE_MAX:
139                 return "?";
140         }
141
142         return "?";
143 }
144
145 static void recording_options_dtor(void *obj)
146 {
147         struct stasis_app_recording_options *options = obj;
148
149         ast_string_field_free_memory(options);
150 }
151
152 struct stasis_app_recording_options *stasis_app_recording_options_create(
153         const char *name, const char *format)
154 {
155         RAII_VAR(struct stasis_app_recording_options *, options, NULL,
156                 ao2_cleanup);
157
158         options = ao2_alloc(sizeof(*options), recording_options_dtor);
159
160         if (!options || ast_string_field_init(options, 128)) {
161                 return NULL;
162         }
163         ast_string_field_set(options, name, name);
164         ast_string_field_set(options, format, format);
165
166         ao2_ref(options, +1);
167         return options;
168 }
169
170 char stasis_app_recording_termination_parse(const char *str)
171 {
172         if (ast_strlen_zero(str)) {
173                 return STASIS_APP_RECORDING_TERMINATE_NONE;
174         }
175
176         if (strcasecmp(str, "none") == 0) {
177                 return STASIS_APP_RECORDING_TERMINATE_NONE;
178         }
179
180         if (strcasecmp(str, "any") == 0) {
181                 return STASIS_APP_RECORDING_TERMINATE_ANY;
182         }
183
184         if (strcasecmp(str, "#") == 0) {
185                 return '#';
186         }
187
188         if (strcasecmp(str, "*") == 0) {
189                 return '*';
190         }
191
192         return STASIS_APP_RECORDING_TERMINATE_INVALID;
193 }
194
195 enum ast_record_if_exists stasis_app_recording_if_exists_parse(
196         const char *str)
197 {
198         if (ast_strlen_zero(str)) {
199                 /* Default value */
200                 return AST_RECORD_IF_EXISTS_FAIL;
201         }
202
203         if (strcasecmp(str, "fail") == 0) {
204                 return AST_RECORD_IF_EXISTS_FAIL;
205         }
206
207         if (strcasecmp(str, "overwrite") == 0) {
208                 return AST_RECORD_IF_EXISTS_OVERWRITE;
209         }
210
211         if (strcasecmp(str, "append") == 0) {
212                 return AST_RECORD_IF_EXISTS_APPEND;
213         }
214
215         return -1;
216 }
217
218 static void recording_publish(struct stasis_app_recording *recording, const char *cause)
219 {
220         RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
221         RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);
222
223         ast_assert(recording != NULL);
224
225         json = stasis_app_recording_to_json(recording);
226         if (json == NULL) {
227                 return;
228         }
229
230         if (!ast_strlen_zero(cause)) {
231                 struct ast_json *failure_cause = ast_json_string_create(cause);
232
233                 if (!failure_cause) {
234                         return;
235                 }
236
237                 if (ast_json_object_set(json, "cause", failure_cause)) {
238                         return;
239                 }
240         }
241
242         message = ast_channel_blob_create_from_cache(
243                 stasis_app_control_get_channel_id(recording->control),
244                 stasis_app_recording_snapshot_type(), json);
245         if (message == NULL) {
246                 return;
247         }
248
249         stasis_app_control_publish(recording->control, message);
250 }
251
252
253 static void recording_set_state(struct stasis_app_recording *recording,
254                                 enum stasis_app_recording_state state,
255                                 const char *cause)
256 {
257         SCOPED_AO2LOCK(lock, recording);
258         recording->state = state;
259         recording_publish(recording, cause);
260 }
261
262 static enum stasis_app_control_channel_result check_rule_recording(
263         const struct stasis_app_control *control)
264 {
265         return STASIS_APP_CHANNEL_RECORDING;
266 }
267
268 struct stasis_app_control_rule rule_recording = {
269         .check_rule = check_rule_recording
270 };
271
272 static void recording_fail(struct stasis_app_control *control,
273                            struct stasis_app_recording *recording,
274                            const char *cause)
275 {
276         stasis_app_control_unregister_add_rule(control, &rule_recording);
277
278         recording_set_state(
279                 recording, STASIS_APP_RECORDING_STATE_FAILED, cause);
280 }
281
282 static void recording_cleanup(void *data)
283 {
284         struct stasis_app_recording *recording = data;
285
286         ao2_unlink_flags(recordings, recording,
287                 OBJ_POINTER | OBJ_UNLINK | OBJ_NODATA);
288         ao2_ref(recording, -1);
289 }
290
291 static int record_file(struct stasis_app_control *control,
292         struct ast_channel *chan, void *data)
293 {
294         struct stasis_app_recording *recording = data;
295         char *acceptdtmf;
296         int res;
297
298         ast_assert(recording != NULL);
299
300         if (stasis_app_get_bridge(control)) {
301                 ast_log(LOG_ERROR, "Cannot record channel while in bridge\n");
302                 recording_fail(control, recording, "Cannot record channel while in bridge");
303                 return -1;
304         }
305
306         switch (recording->options->terminate_on) {
307         case STASIS_APP_RECORDING_TERMINATE_NONE:
308         case STASIS_APP_RECORDING_TERMINATE_INVALID:
309                 acceptdtmf = "";
310                 break;
311         case STASIS_APP_RECORDING_TERMINATE_ANY:
312                 acceptdtmf = "#*0123456789abcd";
313                 break;
314         default:
315                 acceptdtmf = ast_alloca(2);
316                 acceptdtmf[0] = recording->options->terminate_on;
317                 acceptdtmf[1] = '\0';
318         }
319
320         res = ast_auto_answer(chan);
321         if (res != 0) {
322                 ast_debug(3, "%s: Failed to answer\n",
323                         ast_channel_uniqueid(chan));
324                 recording_fail(control, recording, "Failed to answer channel");
325                 return -1;
326         }
327
328         recording_set_state(
329                 recording, STASIS_APP_RECORDING_STATE_RECORDING, NULL);
330         ast_play_and_record_full(chan,
331                 NULL, /* playfile */
332                 recording->absolute_name,
333                 recording->options->max_duration_seconds,
334                 recording->options->format,
335                 &recording->duration.total,
336                 recording->options->max_silence_seconds ? &recording->duration.energy_only : NULL,
337                 recording->options->beep,
338                 -1, /* silencethreshold */
339                 recording->options->max_silence_seconds * 1000,
340                 NULL, /* path */
341                 acceptdtmf,
342                 NULL, /* canceldtmf */
343                 1, /* skip_confirmation_sound */
344                 recording->options->if_exists);
345
346         ast_debug(3, "%s: Recording complete\n", ast_channel_uniqueid(chan));
347
348         recording_set_state(
349                 recording, STASIS_APP_RECORDING_STATE_COMPLETE, NULL);
350
351         stasis_app_control_unregister_add_rule(control, &rule_recording);
352
353         return 0;
354 }
355
356 static void recording_dtor(void *obj)
357 {
358         struct stasis_app_recording *recording = obj;
359
360         ast_free(recording->absolute_name);
361         ao2_cleanup(recording->options);
362 }
363
364 struct stasis_app_recording *stasis_app_control_record(
365         struct stasis_app_control *control,
366         struct stasis_app_recording_options *options)
367 {
368         struct stasis_app_recording *recording;
369         char *last_slash;
370
371         errno = 0;
372
373         if (options == NULL ||
374                 ast_strlen_zero(options->name) ||
375                 ast_strlen_zero(options->format) ||
376                 options->max_silence_seconds < 0 ||
377                 options->max_duration_seconds < 0) {
378                 errno = EINVAL;
379                 return NULL;
380         }
381
382         ast_debug(3, "%s: Sending record(%s.%s) command\n",
383                 stasis_app_control_get_channel_id(control), options->name,
384                 options->format);
385
386         recording = ao2_alloc(sizeof(*recording), recording_dtor);
387         if (!recording) {
388                 errno = ENOMEM;
389                 return NULL;
390         }
391         recording->duration.total = -1;
392         recording->duration.energy_only = -1;
393
394         ast_asprintf(&recording->absolute_name, "%s/%s",
395                 ast_config_AST_RECORDING_DIR, options->name);
396
397         if (recording->absolute_name == NULL) {
398                 errno = ENOMEM;
399                 ao2_ref(recording, -1);
400                 return NULL;
401         }
402
403         if ((last_slash = strrchr(recording->absolute_name, '/'))) {
404                 *last_slash = '\0';
405                 if (ast_safe_mkdir(ast_config_AST_RECORDING_DIR,
406                                 recording->absolute_name, 0777) != 0) {
407                         /* errno set by ast_mkdir */
408                         ao2_ref(recording, -1);
409                         return NULL;
410                 }
411                 *last_slash = '/';
412         }
413
414         ao2_ref(options, +1);
415         recording->options = options;
416         recording->control = control;
417         recording->state = STASIS_APP_RECORDING_STATE_QUEUED;
418
419         if ((recording->options->if_exists == AST_RECORD_IF_EXISTS_FAIL) &&
420                         (ast_fileexists(recording->absolute_name, NULL, NULL))) {
421                 ast_log(LOG_WARNING, "Recording file '%s' already exists and ifExists option is failure.\n",
422                         recording->absolute_name);
423                 errno = EEXIST;
424                 ao2_ref(recording, -1);
425                 return NULL;
426         }
427
428         {
429                 RAII_VAR(struct stasis_app_recording *, old_recording, NULL,
430                         ao2_cleanup);
431
432                 SCOPED_AO2LOCK(lock, recordings);
433
434                 old_recording = ao2_find(recordings, options->name,
435                         OBJ_KEY | OBJ_NOLOCK);
436                 if (old_recording) {
437                         ast_log(LOG_WARNING,
438                                 "Recording %s already in progress\n",
439                                 recording->options->name);
440                         errno = EEXIST;
441                         ao2_ref(recording, -1);
442                         return NULL;
443                 }
444                 ao2_link(recordings, recording);
445         }
446
447         stasis_app_control_register_add_rule(control, &rule_recording);
448
449         stasis_app_send_command_async(control, record_file, ao2_bump(recording), recording_cleanup);
450
451         return recording;
452 }
453
454 enum stasis_app_recording_state stasis_app_recording_get_state(
455         struct stasis_app_recording *recording)
456 {
457         return recording->state;
458 }
459
460 const char *stasis_app_recording_get_name(
461         struct stasis_app_recording *recording)
462 {
463         return recording->options->name;
464 }
465
466 struct stasis_app_recording *stasis_app_recording_find_by_name(const char *name)
467 {
468         RAII_VAR(struct stasis_app_recording *, recording, NULL, ao2_cleanup);
469
470         recording = ao2_find(recordings, name, OBJ_KEY);
471         if (recording == NULL) {
472                 return NULL;
473         }
474
475         ao2_ref(recording, +1);
476         return recording;
477 }
478
479 struct ast_json *stasis_app_recording_to_json(
480         const struct stasis_app_recording *recording)
481 {
482         RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
483
484         if (recording == NULL) {
485                 return NULL;
486         }
487
488         json = ast_json_pack("{s: s, s: s, s: s, s: s}",
489                 "name", recording->options->name,
490                 "format", recording->options->format,
491                 "state", state_to_string(recording->state),
492                 "target_uri", recording->options->target);
493         if (json && recording->duration.total > -1) {
494                 ast_json_object_set(json, "duration",
495                         ast_json_integer_create(recording->duration.total));
496         }
497         if (json && recording->duration.energy_only > -1) {
498                 ast_json_object_set(json, "talking_duration",
499                         ast_json_integer_create(recording->duration.energy_only));
500                 ast_json_object_set(json, "silence_duration",
501                         ast_json_integer_create(recording->duration.total - recording->duration.energy_only));
502         }
503
504         return ast_json_ref(json);
505 }
506
507 typedef int (*recording_operation_cb)(struct stasis_app_recording *recording);
508
509 static int recording_noop(struct stasis_app_recording *recording)
510 {
511         return 0;
512 }
513
514 static int recording_disregard(struct stasis_app_recording *recording)
515 {
516         recording->state = STASIS_APP_RECORDING_STATE_CANCELED;
517         return 0;
518 }
519
520 static int recording_cancel(struct stasis_app_recording *recording)
521 {
522         int res = 0;
523         recording->state = STASIS_APP_RECORDING_STATE_CANCELED;
524         res |= stasis_app_control_queue_control(recording->control,
525                 AST_CONTROL_RECORD_CANCEL);
526         res |= ast_filedelete(recording->absolute_name, NULL);
527         return res;
528 }
529
530 static int recording_stop(struct stasis_app_recording *recording)
531 {
532         recording->state = STASIS_APP_RECORDING_STATE_COMPLETE;
533         return stasis_app_control_queue_control(recording->control,
534                 AST_CONTROL_RECORD_STOP);
535 }
536
537 static int recording_pause(struct stasis_app_recording *recording)
538 {
539         recording->state = STASIS_APP_RECORDING_STATE_PAUSED;
540         return stasis_app_control_queue_control(recording->control,
541                 AST_CONTROL_RECORD_SUSPEND);
542 }
543
544 static int recording_unpause(struct stasis_app_recording *recording)
545 {
546         recording->state = STASIS_APP_RECORDING_STATE_RECORDING;
547         return stasis_app_control_queue_control(recording->control,
548                 AST_CONTROL_RECORD_SUSPEND);
549 }
550
551 static int recording_mute(struct stasis_app_recording *recording)
552 {
553         if (recording->muted) {
554                 /* already muted */
555                 return 0;
556         }
557
558         recording->muted = 1;
559         return stasis_app_control_queue_control(recording->control,
560                 AST_CONTROL_RECORD_MUTE);
561 }
562
563 static int recording_unmute(struct stasis_app_recording *recording)
564 {
565         if (!recording->muted) {
566                 /* already unmuted */
567                 return 0;
568         }
569
570         return stasis_app_control_queue_control(recording->control,
571                 AST_CONTROL_RECORD_MUTE);
572 }
573
574 recording_operation_cb operations[STASIS_APP_RECORDING_STATE_MAX][STASIS_APP_RECORDING_OPER_MAX] = {
575         [STASIS_APP_RECORDING_STATE_QUEUED][STASIS_APP_RECORDING_CANCEL] = recording_disregard,
576         [STASIS_APP_RECORDING_STATE_QUEUED][STASIS_APP_RECORDING_STOP] = recording_disregard,
577         [STASIS_APP_RECORDING_STATE_RECORDING][STASIS_APP_RECORDING_CANCEL] = recording_cancel,
578         [STASIS_APP_RECORDING_STATE_RECORDING][STASIS_APP_RECORDING_STOP] = recording_stop,
579         [STASIS_APP_RECORDING_STATE_RECORDING][STASIS_APP_RECORDING_PAUSE] = recording_pause,
580         [STASIS_APP_RECORDING_STATE_RECORDING][STASIS_APP_RECORDING_UNPAUSE] = recording_noop,
581         [STASIS_APP_RECORDING_STATE_RECORDING][STASIS_APP_RECORDING_MUTE] = recording_mute,
582         [STASIS_APP_RECORDING_STATE_RECORDING][STASIS_APP_RECORDING_UNMUTE] = recording_unmute,
583         [STASIS_APP_RECORDING_STATE_PAUSED][STASIS_APP_RECORDING_CANCEL] = recording_cancel,
584         [STASIS_APP_RECORDING_STATE_PAUSED][STASIS_APP_RECORDING_STOP] = recording_stop,
585         [STASIS_APP_RECORDING_STATE_PAUSED][STASIS_APP_RECORDING_PAUSE] = recording_noop,
586         [STASIS_APP_RECORDING_STATE_PAUSED][STASIS_APP_RECORDING_UNPAUSE] = recording_unpause,
587         [STASIS_APP_RECORDING_STATE_PAUSED][STASIS_APP_RECORDING_MUTE] = recording_mute,
588         [STASIS_APP_RECORDING_STATE_PAUSED][STASIS_APP_RECORDING_UNMUTE] = recording_unmute,
589 };
590
591 enum stasis_app_recording_oper_results stasis_app_recording_operation(
592         struct stasis_app_recording *recording,
593         enum stasis_app_recording_media_operation operation)
594 {
595         recording_operation_cb cb;
596         SCOPED_AO2LOCK(lock, recording);
597
598         if (recording->state < 0 || recording->state >= STASIS_APP_RECORDING_STATE_MAX) {
599                 ast_log(LOG_WARNING, "Invalid recording state %u\n",
600                         recording->state);
601                 return -1;
602         }
603
604         if (operation < 0 || operation >= STASIS_APP_RECORDING_OPER_MAX) {
605                 ast_log(LOG_WARNING, "Invalid recording operation %u\n",
606                         operation);
607                 return -1;
608         }
609
610         cb = operations[recording->state][operation];
611
612         if (!cb) {
613                 if (recording->state != STASIS_APP_RECORDING_STATE_RECORDING) {
614                         /* So we can be specific in our error message. */
615                         return STASIS_APP_RECORDING_OPER_NOT_RECORDING;
616                 } else {
617                         /* And, really, all operations should be valid during
618                          * recording */
619                         ast_log(LOG_ERROR,
620                                 "Unhandled operation during recording: %u\n",
621                                 operation);
622                         return STASIS_APP_RECORDING_OPER_FAILED;
623                 }
624         }
625
626         return cb(recording) ?
627                 STASIS_APP_RECORDING_OPER_FAILED : STASIS_APP_RECORDING_OPER_OK;
628 }
629
630 static int load_module(void)
631 {
632         int r;
633
634         r = STASIS_MESSAGE_TYPE_INIT(stasis_app_recording_snapshot_type);
635         if (r != 0) {
636                 return AST_MODULE_LOAD_FAILURE;
637         }
638
639         recordings = ao2_container_alloc(RECORDING_BUCKETS, recording_hash,
640                 recording_cmp);
641         if (!recordings) {
642                 return AST_MODULE_LOAD_FAILURE;
643         }
644         return AST_MODULE_LOAD_SUCCESS;
645 }
646
647 static int unload_module(void)
648 {
649         ao2_cleanup(recordings);
650         recordings = NULL;
651         STASIS_MESSAGE_TYPE_CLEANUP(stasis_app_recording_snapshot_type);
652         return 0;
653 }
654
655 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "Stasis application recording support",
656         .support_level = AST_MODULE_SUPPORT_CORE,
657         .load = load_module,
658         .unload = unload_module,
659         .nonoptreq = "res_stasis",
660         .load_pri = AST_MODPRI_APP_DEPEND);