From f516684aded8608fc9267f9b1625ce3e39d0f824 Mon Sep 17 00:00:00 2001 From: Dylan Mikus Date: Tue, 15 Dec 2015 19:52:39 +0000 Subject: [PATCH] All RTP packet data for a call goes to one PCAP file. This involved moving all code from fs.(c|h) to recording.(c|h). We still spoof packets, so the UDP will look like all monologues are coming over the same port and will probably look like they are all one stream if you look at the PCAP file. --- daemon/Makefile | 2 +- daemon/call.c | 27 ++++++----- daemon/call.h | 10 +---- daemon/call_interfaces.c | 29 +++++++----- daemon/main.c | 4 +- daemon/media_socket.c | 6 +-- daemon/{fs.c => recording.c} | 86 ++++++++++++++++++------------------ daemon/{fs.h => recording.h} | 30 ++++++++++--- 8 files changed, 106 insertions(+), 88 deletions(-) rename daemon/{fs.c => recording.c} (80%) rename daemon/{fs.h => recording.h} (67%) diff --git a/daemon/Makefile b/daemon/Makefile index 6bec78fc1..898839877 100644 --- a/daemon/Makefile +++ b/daemon/Makefile @@ -69,7 +69,7 @@ endif SRCS= main.c kernel.c poller.c aux.c control_tcp.c streambuf.c call.c control_udp.c redis.c \ bencode.c cookie_cache.c udp_listener.c control_ng.c sdp.c str.c stun.c rtcp.c \ crypto.c rtp.c call_interfaces.c dtls.c log.c cli.c graphite.c ice.c socket.c \ - media_socket.c rtcp_xr.c homer.c fs.c + media_socket.c rtcp_xr.c homer.c recording.c OBJS= $(SRCS:.c=.o) diff --git a/daemon/call.c b/daemon/call.c index 3dbd240a3..6e265d047 100644 --- a/daemon/call.c +++ b/daemon/call.c @@ -36,7 +36,7 @@ #include "ice.h" #include "rtpengine_config.h" #include "log_funcs.h" -#include "fs.h" +#include "recording.h" @@ -1523,11 +1523,13 @@ int monologue_offer_answer(struct call_monologue *other_ml, GQueue *streams, ml_media = other_ml_media = NULL; - str *pcap_path = recording_setup_file(call, monologue); - if (pcap_path != NULL != NULL && monologue->recording_pdumper != NULL - && call->meta_fp) { - // Write the location of the PCAP file to the metadata file - fprintf(call->meta_fp, "%s\n", pcap_path->s); + if (call->recording != NULL && call->recording->recording_pdumper == NULL) { + str *pcap_path = recording_setup_file(call->recording); + if (pcap_path != NULL && call->recording->recording_pdumper != NULL + && call->recording->meta_fp) { + // Write the location of the PCAP file to the metadata file + fprintf(call->recording->meta_fp, "%s\n", pcap_path->s); + } } for (media_iter = streams->head; media_iter; media_iter = media_iter->next) { @@ -2216,13 +2218,12 @@ void call_destroy(struct call *c) { obj_put(sfd); } - while (c->recording_pcaps) { - free(c->recording_pcaps->data); - c->recording_pcaps = g_slist_delete_link(c->recording_pcaps, c->recording_pcaps); + if (c->recording != NULL) { + recording_finish_file(c->recording); + free(c->recording->metadata); } - meta_finish_file(c); - free(c->metadata); + rwlock_unlock_w(&c->master_lock); } @@ -2319,6 +2320,7 @@ static struct call *call_create(const str *callid, struct callmaster *m) { ilog(LOG_NOTICE, "Creating new call"); c = obj_alloc0("call", sizeof(*c), __call_free); + c->recording = NULL; c->callmaster = m; mutex_init(&c->buffer_lock); call_buffer_init(&c->buffer); @@ -2415,8 +2417,6 @@ struct call_monologue *__monologue_create(struct call *call) { ret->call = call; ret->created = poller_now; ret->other_tags = g_hash_table_new(str_hash, str_equal); - ret->recording_pd = NULL; - ret->recording_pdumper = NULL; g_queue_init(&ret->medias); gettimeofday(&ret->started, NULL); @@ -2491,7 +2491,6 @@ static void __monologue_destroy(struct call_monologue *monologue) { GList *l; call = monologue->call; - recording_finish_file(monologue); g_hash_table_remove(call->tags, &monologue->tag); diff --git a/daemon/call.h b/daemon/call.h index 375fa75f1..dd81303c1 100644 --- a/daemon/call.h +++ b/daemon/call.h @@ -399,9 +399,6 @@ struct call_monologue { GHashTable *other_tags; struct call_monologue *active_dialogue; GQueue medias; - pcap_t *recording_pd; - pcap_dumper_t *recording_pdumper; - str *recording_path; }; struct call { @@ -423,9 +420,6 @@ struct call { GQueue endpoint_maps; struct dtls_cert *dtls_cert; /* for outgoing */ - unsigned int record_call; - GSList *recording_pcaps; - str callid; time_t created; time_t last_signal; @@ -438,9 +432,7 @@ struct call { unsigned int redis_hosted_db; unsigned int foreign_call; // created_via_redis_notify call - str *meta_filepath; - FILE *meta_fp; - str *metadata; + struct recording *recording; }; struct callmaster_config { diff --git a/daemon/call_interfaces.c b/daemon/call_interfaces.c index 288cda3cb..449779707 100644 --- a/daemon/call_interfaces.c +++ b/daemon/call_interfaces.c @@ -18,6 +18,7 @@ #include "control_udp.h" #include "rtp.h" #include "ice.h" +#include "recording.h" @@ -689,17 +690,26 @@ static const char *call_offer_answer_ng(bencode_item_t *input, struct callmaster goto out; if (recordcall.s && !str_cmp(&recordcall, "yes")) { - if (!call->record_call) { - meta_setup_file(call); - call->record_call = 1; + if (call->recording == NULL) { + call->recording = g_slice_alloc0(sizeof(struct recording)); + call->recording->recording_pd = NULL; + call->recording->recording_pdumper = NULL; + meta_setup_file(call->recording); } bencode_dictionary_get_str(input, "metadata", &metadata); if (metadata.len > 0) { - free(call->metadata); - call->metadata = str_dup(&metadata); + free(call->recording->metadata); + call->recording->metadata = str_dup(&metadata); } } else { - call->record_call = 0; + if (call->recording != NULL) { + g_slice_free1(sizeof(*(call->recording)), call->recording); + str *rec_metadata = call->recording->metadata; + if (rec_metadata != NULL) { + free(rec_metadata); + } + } + call->recording = NULL; } if (!call->created_from && addr) { @@ -750,10 +760,9 @@ static const char *call_offer_answer_ng(bencode_item_t *input, struct callmaster chopper->iov_num, chopper->str_len); bencode_dictionary_add_string(output, "result", "ok"); bencode_item_t *recordings = bencode_dictionary_add_list(output, "recordings"); - GList *l; - char *recording_path; - for (l = call->recording_pcaps; l; l = l->next) { - bencode_list_add_string(recordings, l->data); + if (call->recording != NULL && call->recording->recording_path != NULL) { + char *recording_path = call->recording->recording_path->s; + bencode_list_add_string(recordings, recording_path); } errstr = NULL; diff --git a/daemon/main.c b/daemon/main.c index 0648849c8..6fc74270f 100644 --- a/daemon/main.c +++ b/daemon/main.c @@ -30,7 +30,7 @@ #include "socket.h" #include "media_socket.h" #include "homer.h" -#include "fs.h" +#include "recording.h" @@ -521,7 +521,7 @@ static void init_everything() { socket_init(); log_init(); - fs_init(spooldir); + recording_fs_init(spooldir); clock_gettime(CLOCK_REALTIME, &ts); srandom(ts.tv_sec ^ ts.tv_nsec); SSL_library_init(); diff --git a/daemon/media_socket.c b/daemon/media_socket.c index ff958c41e..8fc73bdfa 100644 --- a/daemon/media_socket.c +++ b/daemon/media_socket.c @@ -18,7 +18,7 @@ #include "aux.h" #include "log_funcs.h" #include "poller.h" -#include "fs.h" +#include "recording.h" @@ -834,7 +834,7 @@ void kernelize(struct packet_stream *stream) { struct packet_stream *sink = NULL; const char *nk_warn_msg; - if (PS_ISSET(stream, KERNELIZED) || call->record_call) + if (PS_ISSET(stream, KERNELIZED) || call->recording != NULL) return; if (cm->conf.kernelid < 0) goto no_kernel; @@ -1035,8 +1035,8 @@ static int stream_packet(struct stream_fd *sfd, str *s, const endpoint_t *fsin, struct rtp_header *rtp_h; struct rtp_stats *rtp_s; - recording_pdumper = sfd->stream->media->monologue->recording_pdumper; call = sfd->call; + recording_pdumper = call->recording != NULL ? call->recording->recording_pdumper : NULL; cm = call->callmaster; rwlock_lock_r(&call->master_lock); diff --git a/daemon/fs.c b/daemon/recording.c similarity index 80% rename from daemon/fs.c rename to daemon/recording.c index c76972a93..25fe8cf6b 100644 --- a/daemon/fs.c +++ b/daemon/recording.c @@ -1,4 +1,4 @@ -#include "fs.h" +#include "recording.h" #include #include #include @@ -21,7 +21,7 @@ static char *spooldir = NULL; * Initialize RTP Engine filesystem settings and structure. * Check for or create the RTP Engine spool directory. */ -void fs_init(char *spoolpath) { +void recording_fs_init(char *spoolpath) { // Whether or not to fail if the spool directory does not exist. int dne_fail; if (spoolpath == NULL || spoolpath[0] == '\0') { @@ -97,7 +97,7 @@ int maybe_create_spool_dir(char *spoolpath) { * Create a call metadata file in a temporary location. * Attaches the filepath and the file pointer to the call struct. */ -str *meta_setup_file(struct call *call) { +str *meta_setup_file(struct recording *recording) { if (spooldir == NULL) { // No spool directory was created, so we cannot have metadata files. return NULL; @@ -108,15 +108,15 @@ str *meta_setup_file(struct call *call) { // Initially file extension is ".tmp". When call is over, it changes to ".txt". char *path_chars = rand_affixed_str(rand_bytes, "/tmp/rtpengine-meta-", ".tmp"); meta_filepath = str_init(meta_filepath, path_chars); - call->meta_filepath = meta_filepath; + recording->meta_filepath = meta_filepath; FILE *mfp = fopen(meta_filepath->s, "w"); if (mfp == NULL) { ilog(LOG_ERROR, "Could not open metadata file: %s", meta_filepath->s); - free(call->meta_filepath->s); - free(call->meta_filepath); - call->meta_filepath = NULL; + free(recording->meta_filepath->s); + free(recording->meta_filepath); + recording->meta_filepath = NULL; } - call->meta_fp = mfp; + recording->meta_fp = mfp; ilog(LOG_INFO, "Wrote metadata file to temporary path: %s", meta_filepath->s); return meta_filepath; } @@ -127,9 +127,10 @@ str *meta_setup_file(struct call *call) { * Returns non-zero for failure. */ int meta_finish_file(struct call *call) { + struct recording *recording = call->recording; int return_code = 0; - if (call->meta_fp != NULL) { + if (recording != NULL && recording->meta_fp != NULL) { // Print start timestamp and end timestamp // YYYY-MM-DDThh:mm:ss time_t start = 0, end = 0; @@ -143,23 +144,23 @@ int meta_finish_file(struct call *call) { } timeinfo = localtime(&start); strftime(timebuffer, 20, "%FT%T", timeinfo); - fprintf(call->meta_fp, "\n%s\n", timebuffer); + fprintf(recording->meta_fp, "\n%s\n", timebuffer); timeinfo = localtime(&end); strftime(timebuffer, 20, "%FT%T", timeinfo); - fprintf(call->meta_fp, "%s\n", timebuffer); + fprintf(recording->meta_fp, "%s\n", timebuffer); // Print metadata - fprintf(call->meta_fp, "\n%s\n", call->metadata->s); - fclose(call->meta_fp); + fprintf(recording->meta_fp, "\n%s\n", recording->metadata->s); + fclose(recording->meta_fp); // Get the filename (in between its directory and the file extension) // and move it to the finished file location. // Rename extension to ".txt". int fn_len; - char *meta_filename = strrchr(call->meta_filepath->s, '/'); + char *meta_filename = strrchr(recording->meta_filepath->s, '/'); char *meta_ext = NULL; if (meta_filename == NULL) { - meta_filename = call->meta_filepath->s; + meta_filename = recording->meta_filepath->s; } else { meta_filename = meta_filename + 1; @@ -172,18 +173,18 @@ int meta_finish_file(struct call *call) { char new_metapath[prefix_len + fn_len + ext_len + 1]; snprintf(new_metapath, prefix_len+fn_len+1, "%s/metadata/%s", spooldir, meta_filename); snprintf(new_metapath + prefix_len+fn_len, ext_len+1, ".txt"); - return_code = return_code || rename(call->meta_filepath->s, new_metapath); + return_code = return_code || rename(recording->meta_filepath->s, new_metapath); if (return_code != 0) { ilog(LOG_ERROR, "Could not move metadata file \"%s\" to \"%s/metadata/\"", - call->meta_filepath->s, spooldir); + recording->meta_filepath->s, spooldir); } else { ilog(LOG_INFO, "Moved metadata file \"%s\" to \"%s/metadata\"", - call->meta_filepath->s, spooldir); + recording->meta_filepath->s, spooldir); } } - if (call->meta_filepath != NULL) { - free(call->meta_filepath->s); - free(call->meta_filepath); + if (recording != NULL && recording->meta_filepath != NULL) { + free(recording->meta_filepath->s); + free(recording->meta_filepath); } return return_code; @@ -192,11 +193,11 @@ int meta_finish_file(struct call *call) { /** * Generate a random PCAP filepath to write recorded RTP stream. */ -str *recording_setup_file(struct call *call, struct call_monologue *monologue) { +str *recording_setup_file(struct recording *recording) { str *recording_path = NULL; if (spooldir != NULL - && call->record_call - && monologue->recording_pd == NULL && monologue->recording_pdumper == NULL) { + && recording != NULL + && recording->recording_pd == NULL && recording->recording_pdumper == NULL) { int rand_bytes = 16; int rec_path_len = strlen(spooldir) + 8; // spool directory path + "/pcaps/" char rec_path[rec_path_len]; @@ -205,22 +206,21 @@ str *recording_setup_file(struct call *call, struct call_monologue *monologue) { recording_path = malloc(sizeof(str)); recording_path = str_init(recording_path, path_chars); - monologue->recording_path = recording_path; + recording->recording_path = recording_path; - call->recording_pcaps = g_slist_prepend(call->recording_pcaps, g_strdup(path_chars)); - monologue->recording_pd = pcap_open_dead(DLT_RAW, 65535); - monologue->recording_pdumper = pcap_dump_open(monologue->recording_pd, path_chars); - if (monologue->recording_pdumper == NULL) { - pcap_close(monologue->recording_pd); - monologue->recording_pd = NULL; + recording->recording_pd = pcap_open_dead(DLT_RAW, 65535); + recording->recording_pdumper = pcap_dump_open(recording->recording_pd, path_chars); + if (recording->recording_pdumper == NULL) { + pcap_close(recording->recording_pd); + recording->recording_pd = NULL; ilog(LOG_INFO, "Failed to write recording file: %s", recording_path->s); } else { ilog(LOG_INFO, "Writing recording file: %s", recording_path->s); } - } else { - monologue->recording_path = NULL; - monologue->recording_pd = NULL; - monologue->recording_pdumper = NULL; + } else if (recording != NULL) { + recording->recording_path = NULL; + recording->recording_pd = NULL; + recording->recording_pdumper = NULL; } return recording_path; @@ -229,15 +229,15 @@ str *recording_setup_file(struct call *call, struct call_monologue *monologue) { /** * Flushes PCAP file, closes the dumper and descriptors, and frees object memory. */ -void recording_finish_file(struct call_monologue *monologue) { - if (monologue->recording_pdumper != NULL) { - pcap_dump_flush(monologue->recording_pdumper); - pcap_dump_close(monologue->recording_pdumper); - free(monologue->recording_path->s); - free(monologue->recording_path); +void recording_finish_file(struct recording *recording) { + if (recording->recording_pdumper != NULL) { + pcap_dump_flush(recording->recording_pdumper); + pcap_dump_close(recording->recording_pdumper); + free(recording->recording_path->s); + free(recording->recording_path); } - if (monologue->recording_pd != NULL) { - pcap_close(monologue->recording_pd); + if (recording->recording_pd != NULL) { + pcap_close(recording->recording_pd); } } diff --git a/daemon/fs.h b/daemon/recording.h similarity index 67% rename from daemon/fs.h rename to daemon/recording.h index a2a93659c..182f1ad93 100644 --- a/daemon/fs.h +++ b/daemon/recording.h @@ -1,16 +1,34 @@ -#ifndef __FS_H__ -#define __FS_H__ +/** + * recording.h + * + * Handles call recording to PCAP files and recording metadata. + * Mostly filesystem operations + */ + +#ifndef __RECORDING_H__ +#define __RECORDING_H__ #include #include #include #include "call.h" + +struct recording { + str *meta_filepath; + FILE *meta_fp; + str *metadata; + pcap_t *recording_pd; + pcap_dumper_t *recording_pdumper; + str *recording_path; +}; + + /** * Initialize RTP Engine filesystem settings and structure. * Check for or create the RTP Engine spool directory. */ -void fs_init(char *spooldir); +void recording_fs_init(char *spooldir); /** * Create a call metadata file in a temporary location. @@ -30,7 +48,7 @@ void fs_init(char *spooldir); * generic metadata * */ -str *meta_setup_file(struct call *call); +str *meta_setup_file(struct recording *recording); /** * Writes metadata to metafile, closes file, and renames it to finished location. @@ -42,12 +60,12 @@ int meta_finish_file(struct call *call); * Generate a random PCAP filepath to write recorded RTP stream. * Returns path to created file. */ -str *recording_setup_file(struct call *call, struct call_monologue *monologue); +str *recording_setup_file(struct recording *recording); /** * Flushes PCAP file, closes the dumper and descriptors, and frees object memory. */ -void recording_finish_file(struct call_monologue *monologue); +void recording_finish_file(struct recording *recording); /** * Write out a PCAP packet with payload string.