Browse Source

MT#56759 support discarding call recordings

When the `discard-recording` flag is given in one of the commands to
rtpengine (e.g. in the `delete` command), the metafile is renamed to a
.DISCARD suffix and then deleted.

The recording daemon then, seeing the .DISCARD suffix, proceeds to
immediately close all recordings, delete the files if any, and delete
the entries from the DB.

Change-Id: I3f0cac129f2d56cbccd770d43bf434dea6c0a0db
pull/1642/head
Richard Fuchs 3 years ago
parent
commit
eea05c878f
11 changed files with 130 additions and 35 deletions
  1. +1
    -1
      daemon/call.c
  2. +25
    -5
      daemon/call_interfaces.c
  3. +60
    -14
      daemon/recording.c
  4. +5
    -0
      docs/ng_control_protocol.md
  5. +1
    -0
      include/call_interfaces.h
  6. +3
    -2
      include/recording.h
  7. +18
    -6
      recording-daemon/metafile.c
  8. +14
    -5
      recording-daemon/output.c
  9. +1
    -1
      recording-daemon/output.h
  10. +1
    -1
      recording-daemon/packet.c
  11. +1
    -0
      recording-daemon/types.h

+ 1
- 1
daemon/call.c View File

@ -3637,7 +3637,7 @@ static void __call_cleanup(struct call *c) {
obj_put(sfd);
}
recording_finish(c);
recording_finish(c, false);
}
/* called lock-free, but must hold a reference to the call */


+ 25
- 5
daemon/call_interfaces.c View File

@ -1031,6 +1031,9 @@ static void call_ng_flags_flags(struct sdp_ng_flags *out, str *s, void *dummy) {
case CSH_LOOKUP("record-call"):
out->record_call = 1;
break;
case CSH_LOOKUP("discard-recording"):
out->discard_recording = 1;
break;
case CSH_LOOKUP("inactive"):
out->inactive = 1;
break;
@ -2119,6 +2122,7 @@ const char *call_delete_ng(bencode_item_t *input, bencode_item_t *output) {
str fromtag, totag, viabranch, callid;
bencode_item_t *flags, *it;
bool fatal = false;
bool discard = false;
int delete_delay;
if (!bencode_dictionary_get_str(input, "call-id", &callid))
@ -2132,6 +2136,8 @@ const char *call_delete_ng(bencode_item_t *input, bencode_item_t *output) {
for (it = flags->child; it; it = it->sibling) {
if (!bencode_strcmp(it, "fatal"))
fatal = true;
else if (!bencode_strcmp(it, "discard-recording"))
discard = true;
}
}
delete_delay = bencode_dictionary_get_int_str(input, "delete-delay", -1);
@ -2146,13 +2152,23 @@ const char *call_delete_ng(bencode_item_t *input, bencode_item_t *output) {
}
}
if (call_delete_branch_by_id(&callid, &viabranch, &fromtag, &totag, output, delete_delay)) {
if (fatal)
return "Call-ID not found or tags didn't match";
bencode_dictionary_add_string(output, "warning", "Call-ID not found or tags didn't match");
}
struct call *c = call_get(&callid);
if (!c)
goto err;
if (discard)
recording_discard(c);
if (call_delete_branch(c, &viabranch, &fromtag, &totag, output, delete_delay))
goto err;
return NULL;
err:
if (fatal)
return "Call-ID not found or tags didn't match";
bencode_dictionary_add_string(output, "warning", "Call-ID not found or tags didn't match");
return NULL;
}
static void ng_stats(bencode_item_t *d, const struct stream_stats *s, struct stream_stats *totals) {
@ -2593,6 +2609,10 @@ static void stop_recording_fn(bencode_item_t *input, struct call *call) {
pause_recording_fn(input, call);
return;
}
if (bencode_strcmp(child, "discard-recording") == 0) {
recording_discard(call);
return;
}
}
}


+ 60
- 14
daemon/recording.c View File

@ -46,14 +46,14 @@ static void init_all(struct call *call);
static void sdp_after_all(struct recording *recording, GString *str, struct call_monologue *ml,
enum call_opmode opmode);
static void dump_packet_all(struct media_packet *mp, const str *s);
static void finish_all(struct call *call);
static void finish_all(struct call *call, bool discard);
// pcap methods
static int rec_pcap_create_spool_dir(const char *dirpath);
static void rec_pcap_init(struct call *);
static void sdp_after_pcap(struct recording *, GString *str, struct call_monologue *, enum call_opmode opmode);
static void dump_packet_pcap(struct media_packet *mp, const str *s);
static void finish_pcap(struct call *);
static void finish_pcap(struct call *, bool discard);
static void response_pcap(struct recording *, bencode_item_t *);
// proc methods
@ -62,7 +62,7 @@ static void sdp_before_proc(struct recording *, const str *, struct call_monolog
static void sdp_after_proc(struct recording *, GString *str, struct call_monologue *, enum call_opmode opmode);
static void meta_chunk_proc(struct recording *, const char *, const str *);
static void update_flags_proc(struct call *call, bool streams);
static void finish_proc(struct call *);
static void finish_proc(struct call *, bool discard);
static void dump_packet_proc(struct media_packet *mp, const str *s);
static void init_stream_proc(struct packet_stream *);
static void setup_stream_proc(struct packet_stream *);
@ -393,12 +393,20 @@ void recording_stop(struct call *call) {
}
ilog(LOG_NOTICE, "Turning off call recording.");
recording_finish(call);
recording_finish(call, false);
}
void recording_pause(struct call *call) {
ilog(LOG_NOTICE, "Pausing call recording.");
recording_update_flags(call, true);
}
void recording_discard(struct call *call) {
call->recording_on = 0;
if (!call->recording)
return;
ilog(LOG_NOTICE, "Turning off call recording and discarding outputs.");
recording_finish(call, true);
}
/**
*
@ -424,6 +432,10 @@ void detect_setup_recording(struct call *call, const struct sdp_ng_flags *flags)
call->recording_on = 0;
recording_stop(call);
}
else if (!str_cmp(recordcall, "discard") || flags->discard_recording) {
call->recording_on = 0;
recording_discard(call);
}
else if (recordcall->len != 0)
ilog(LOG_INFO, "\"record-call\" flag "STR_FORMAT" is invalid flag.", STR_FMT(recordcall));
}
@ -572,7 +584,23 @@ static void rec_pcap_meta_finish_file(struct call *call) {
mutex_destroy(&recording->u.pcap.recording_lock);
g_clear_pointer(&recording->u.pcap.meta_filepath, g_free);
}
/**
* Closes and discards all output files.
*/
static void rec_pcap_meta_discard_file(struct call *call) {
struct recording *recording = call->recording;
if (recording == NULL || recording->u.pcap.meta_fp == NULL)
return;
fclose(recording->u.pcap.meta_fp);
recording->u.pcap.meta_fp = NULL;
unlink(recording->u.pcap.recording_path);
unlink(recording->u.pcap.meta_filepath);
g_clear_pointer(&recording->u.pcap.meta_filepath, free);
}
/**
@ -673,9 +701,12 @@ static void dump_packet_pcap(struct media_packet *mp, const str *s) {
mutex_unlock(&recording->u.pcap.recording_lock);
}
static void finish_pcap(struct call *call) {
static void finish_pcap(struct call *call, bool discard) {
rec_pcap_recording_finish_file(call->recording);
rec_pcap_meta_finish_file(call);
if (!discard)
rec_pcap_meta_finish_file(call);
else
rec_pcap_meta_discard_file(call);
}
static void response_pcap(struct recording *recording, bencode_item_t *output) {
@ -692,7 +723,7 @@ static void response_pcap(struct recording *recording, bencode_item_t *output) {
void recording_finish(struct call *call) {
void recording_finish(struct call *call, bool discard) {
if (!call || !call->recording)
return;
@ -700,7 +731,7 @@ void recording_finish(struct call *call) {
struct recording *recording = call->recording;
_rm(finish, call);
_rm(finish, call, discard);
g_clear_pointer(&recording->meta_prefix, g_free);
g_clear_pointer(&recording->escaped_callid, free);
@ -810,7 +841,7 @@ static void sdp_after_proc(struct recording *recording, GString *str, struct cal
"SDP from %u after %s", ml->unique_id, get_opmode_text(opmode));
}
static void finish_proc(struct call *call) {
static void finish_proc(struct call *call, bool discard) {
struct recording *recording = call->recording;
if (!kernel.is_open)
return;
@ -822,10 +853,25 @@ static void finish_proc(struct call *call) {
struct packet_stream *ps = l->data;
ps->recording.u.proc.stream_idx = UNINIT_IDX;
}
int ret = unlink(recording->u.proc.meta_filepath);
const char *unlink_fn = recording->u.proc.meta_filepath;
AUTO_CLEANUP_GBUF(discard_fn);
if (discard) {
discard_fn = g_strdup_printf("%s.DISCARD", recording->u.proc.meta_filepath);
int ret = rename(recording->u.proc.meta_filepath, discard_fn);
if (ret)
ilog(LOG_ERR, "Failed to rename metadata file \"%s\" to \"%s\": %s",
recording->u.proc.meta_filepath,
discard_fn,
strerror(errno));
unlink_fn = discard_fn;
}
int ret = unlink(unlink_fn);
if (ret)
ilog(LOG_ERR, "Failed to delete metadata file \"%s\": %s",
recording->u.proc.meta_filepath, strerror(errno));
unlink_fn, strerror(errno));
g_clear_pointer(&recording->u.proc.meta_filepath, free);
}
@ -969,7 +1015,7 @@ static void dump_packet_all(struct media_packet *mp, const str *s) {
dump_packet_proc(mp, s);
}
static void finish_all(struct call *call) {
finish_pcap(call);
finish_proc(call);
static void finish_all(struct call *call, bool discard) {
finish_pcap(call, discard);
finish_proc(call, discard);
}

+ 5
- 0
docs/ng_control_protocol.md View File

@ -813,6 +813,11 @@ Spaces in each string may be replaced by hyphens.
the DSP to detect in-band DTMF audio tones even when it
wouldn't otherwise be necessary.
* `discard recording`
When file recording is in use, instructs the recording daemon to discard
(delete) the recording files, as well as the database entries if present.
* `early media`
Used in conjunction with the audio player. If set, audio playback is


+ 1
- 0
include/call_interfaces.h View File

@ -157,6 +157,7 @@ struct sdp_ng_flags {
siprec:1,
fragment:1,
record_call:1,
discard_recording:1,
debug:1,
inactive:1,
loop_protect:1,


+ 3
- 2
include/recording.h View File

@ -76,7 +76,7 @@ struct recording_method {
void (*update_flags)(struct call *call, bool streams);
void (*dump_packet)(struct media_packet *, const str *s);
void (*finish)(struct call *);
void (*finish)(struct call *, bool discard);
void (*response)(struct recording *, bencode_item_t *);
void (*init_stream_struct)(struct packet_stream *);
@ -121,12 +121,13 @@ void update_metadata_monologue(struct call_monologue *ml, str *metadata);
void recording_start(struct call *call, const char *prefix, str *output_dest);
void recording_pause(struct call *call);
void recording_stop(struct call *call);
void recording_discard(struct call *call);
#define meta_write_sdp_before(args...) _rm(sdp_before, args)
#define meta_write_sdp_after(args...) _rm(sdp_after, args)
void recording_finish(struct call *);
void recording_finish(struct call *, bool discard);


+ 18
- 6
recording-daemon/metafile.c View File

@ -26,7 +26,7 @@ static void meta_free(void *ptr) {
metafile_t *mf = ptr;
dbg("freeing metafile info for %s%s%s", FMT_M(mf->name));
output_close(mf, mf->mix_out, NULL);
output_close(mf, mf->mix_out, NULL, mf->discard);
mix_destroy(mf->mix);
db_close_call(mf);
g_string_chunk_free(mf->gsc);
@ -363,16 +363,28 @@ void metafile_delete(char *name) {
pthread_mutex_lock(&metafiles_lock);
metafile_t *mf = g_hash_table_lookup(metafiles, name);
if (!mf) {
// nothing to do
pthread_mutex_unlock(&metafiles_lock);
return;
// has it been renamed?
size_t len = strlen(name);
char *suffix = name + len - strlen(".DISCARD");
if (suffix > name && strcmp(suffix, ".DISCARD") == 0) {
*suffix = '\0';
mf = g_hash_table_lookup(metafiles, name);
if (mf)
mf->discard = 1;
*suffix = '.';
}
if (!mf) {
// nothing to do
pthread_mutex_unlock(&metafiles_lock);
return;
}
}
// switch locks and remove entry
pthread_mutex_lock(&mf->lock);
g_hash_table_remove(metafiles, name);
g_hash_table_remove(metafiles, mf->name);
pthread_mutex_unlock(&metafiles_lock);
ilog(LOG_INFO, "Recording for call '%s%s%s' finished", FMT_M(name));
ilog(LOG_INFO, "Recording for call '%s%s%s' finished", FMT_M(mf->name));
meta_destroy(mf);


+ 14
- 5
recording-daemon/output.c View File

@ -339,15 +339,24 @@ static bool output_shutdown(output_t *output) {
}
void output_close(metafile_t *mf, output_t *output, tag_t *tag) {
void output_close(metafile_t *mf, output_t *output, tag_t *tag, bool discard) {
if (!output)
return;
if (output_shutdown(output)) {
db_close_stream(output);
notify_push_output(output, mf, tag);
if (!discard) {
if (output_shutdown(output)) {
db_close_stream(output);
notify_push_output(output, mf, tag);
}
else
db_delete_stream(mf, output);
}
else
else {
output_shutdown(output);
if (unlink(output->filename))
ilog(LOG_WARN, "Failed to unlink '%s%s%s': %s",
FMT_M(output->filename), strerror(errno));
db_delete_stream(mf, output);
}
encoder_free(output->encoder);
g_clear_pointer(&output->full_filename, g_free);
g_clear_pointer(&output->file_path, g_free);


+ 1
- 1
recording-daemon/output.h View File

@ -12,7 +12,7 @@ void output_init(const char *format);
output_t *output_new(const char *path, const char *call, const char *type, const char *kind, const char *label);
output_t *output_new_from_full_path(const char *path, char *name, const char *kind);
void output_close(metafile_t *, output_t *, tag_t *);
void output_close(metafile_t *, output_t *, tag_t *, bool discard);
int output_config(output_t *output, const format_t *requested_format, format_t *actual_format);
int output_add(output_t *output, AVFrame *frame);


+ 1
- 1
recording-daemon/packet.c View File

@ -145,7 +145,7 @@ void ssrc_tls_state(ssrc_t *ssrc) {
void ssrc_close(ssrc_t *s) {
output_close(s->metafile, s->output, tag_get(s->metafile, s->stream->tag));
output_close(s->metafile, s->output, tag_get(s->metafile, s->stream->tag), s->metafile->discard);
s->output = NULL;
for (int i = 0; i < G_N_ELEMENTS(s->decoders); i++) {
decoder_free(s->decoders[i]);


+ 1
- 0
recording-daemon/types.h View File

@ -141,6 +141,7 @@ struct metafile_s {
unsigned int recording_on:1;
unsigned int forwarding_on:1;
unsigned int discard:1;
};


Loading…
Cancel
Save