From 692b12944c803ec5bd5dc729ee878dfe50643337 Mon Sep 17 00:00:00 2001 From: Dylan Mikus Date: Mon, 30 Nov 2015 21:39:38 +0000 Subject: [PATCH] Create metadata file and move it to spool directory when call finishes. We create a metadata file for each call. The metadata files will all end up in a spool directory for the rtpengine. Each in-progress file has the format: "rtpengine-meta-$RANDHEX.tmp" and goes in /tmp/. When a call finishes, it is moved to the spool directory in sub-directory /var/spool/rtpengine/metadata/ and we change the file extension to ".txt". The metadata file contains references to all PCAP recording files associated with a call, and it includes generic metadata at the tail of the file. One absolute path for a PCAP file per line, followed by two blank lines, followed by the metadata passed in to the rtpengine through an external command. RTP Engine checks for the spool directory "/var/spool/rtpengine" on startup. If it's not there, it fails. If it's there, it sets up "metadata" and "recordings" inner directories. This is where RTP Engine will write call metadata files and PCAP files. --- daemon/call.c | 74 ++++++++++++++++++++++++++++++++++++++++++++++----- daemon/call.h | 3 +++ daemon/main.c | 57 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 128 insertions(+), 6 deletions(-) diff --git a/daemon/call.c b/daemon/call.c index d096f8a89..eb4fabb8f 100644 --- a/daemon/call.c +++ b/daemon/call.c @@ -133,7 +133,7 @@ static void __monologue_destroy(struct call_monologue *monologue); static int monologue_destroy(struct call_monologue *ml); /* Generate a random PCAP filepath to write recorded RTP stream. */ -void setup_recording_files(struct call *call, struct call_monologue *monologue); +str *setup_recording_file(struct call *call, struct call_monologue *monologue); /* Generates a random string sandwiched between affixes. */ char *rand_affixed_str(int num_bytes, char *prefix, char *suffix); /* Generates a hex string representing n random bytes. len(rand_str) = 2*num_bytes + 1 */ @@ -1532,7 +1532,10 @@ int monologue_offer_answer(struct call_monologue *other_ml, GQueue *streams, ml_media = other_ml_media = NULL; - setup_recording_files(call, monologue); + str *pcap_path = setup_recording_file(call, monologue); + if (pcap_path != NULL) { + fprintf(call->meta_fp, "%s\n", pcap_path->s); + } for (media_iter = streams->head; media_iter; media_iter = media_iter->next) { sp = media_iter->data; @@ -2225,12 +2228,51 @@ void call_destroy(struct call *c) { c->recording_pcaps = g_slist_delete_link(c->recording_pcaps, c->recording_pcaps); } - // TODO - here is where we write out metadata for IPC communication + meta_file_finish(c); free(c->metadata); rwlock_unlock_w(&c->master_lock); } +/** + * Writes metadata to metafile, closes file, and renames it to finished location. + * Returns non-zero for failure. + */ +int meta_file_finish(struct call *call) { + int return_code = 0; + + if (call->meta_fp != NULL) { + fprintf(call->meta_fp, "\n%s\n", call->metadata->s); + fclose(call->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_ext = NULL; + if (meta_filename == NULL) + meta_filename = call->meta_filepath->s; + else + meta_filename = meta_filename + 1; + // We can always expect a file extension + meta_ext = strrchr(meta_filename, '.'); + fn_len = meta_ext - meta_filename; + int prefix_len = 30; // for "/var/spool/rtpengine/metadata/" + int ext_len = 4; // for ".txt" + char new_metapath[prefix_len + fn_len + ext_len + 1]; + snprintf(new_metapath, prefix_len+fn_len+1, "/var/spool/rtpengine/metadata/%s", meta_filename); + snprintf(new_metapath + prefix_len+fn_len, ext_len+1, ".txt"); + return_code = return_code | rename(call->meta_filepath->s, new_metapath); + } + if (call->meta_filepath != NULL) { + free(call->meta_filepath->s); + free(call->meta_filepath); + } + + return return_code; +} + /* XXX move these */ @@ -2334,6 +2376,22 @@ static struct call *call_create(const str *callid, struct callmaster *m) { c->created = poller_now; c->dtls_cert = dtls_cert(); c->tos = m->conf.default_tos; + + int rand_bytes = 16; + str *meta_filepath = malloc(sizeof(str)); + // 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); + c->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(c->meta_filepath->s); + free(c->meta_filepath); + c->meta_filepath = NULL; + } + c->meta_fp = mfp; + return c; } @@ -2867,12 +2925,13 @@ out: /** * Generate a random PCAP filepath to write recorded RTP stream. */ -void setup_recording_files(struct call *call, struct call_monologue *monologue) { +str *setup_recording_file(struct call *call, struct call_monologue *monologue) { + str *recording_path = NULL; if (call->record_call && monologue->recording_pd == NULL && monologue->recording_pdumper == NULL) { int rand_bytes = 16; - str *recording_path = malloc(sizeof(str)); - char *path_chars = rand_affixed_str(rand_bytes, "/tmp/", ".pcap"); + recording_path = malloc(sizeof(str)); + char *path_chars = rand_affixed_str(rand_bytes, "/var/spool/rtpengine/recordings/", ".pcap"); recording_path = str_init(recording_path, path_chars); monologue->recording_path = recording_path; @@ -2882,9 +2941,12 @@ void setup_recording_files(struct call *call, struct call_monologue *monologue) monologue->recording_pd = pcap_open_dead(DLT_RAW, 65535); monologue->recording_pdumper = pcap_dump_open(monologue->recording_pd, recording_path); } else { + monologue->recording_path = NULL; monologue->recording_pd = NULL; monologue->recording_pdumper = NULL; } + + return recording_path; } /** diff --git a/daemon/call.h b/daemon/call.h index 105d34fa1..f1faa8873 100644 --- a/daemon/call.h +++ b/daemon/call.h @@ -437,6 +437,9 @@ struct call { unsigned int redis_hosted_db; unsigned int foreign_call; // created_via_redis_notify call + + str *meta_filepath; + FILE *meta_fp; str *metadata; }; diff --git a/daemon/main.c b/daemon/main.c index 374796df7..caddbbe3c 100644 --- a/daemon/main.c +++ b/daemon/main.c @@ -11,6 +11,8 @@ #include #include #include +#include +#include #include "poller.h" #include "control_tcp.h" @@ -549,6 +551,13 @@ static void create_everything(struct main_context *ctx) { struct timeval redis_start, redis_stop; double redis_diff = 0; + // TODO this should be a configurable path + char spoolpath[] = "/var/spool/rtpengine"; + if (!maybe_create_spool_dir(spoolpath)) { + fprintf(stderr, "Error while setting up spool directory \"%s\".\n", spoolpath); + exit(-1); + } + if (table < 0) goto no_kernel; if (kernel_create_table(table)) { @@ -686,6 +695,54 @@ no_kernel: set_graphite_interval_tv(&tmp_tv); } + +/** + * Sets up the spool directory for RTP Engine. + * If the directory does not exist, return FALSE. + * If the directory exists, but "$dirpath/metadata" or "$dirpath/recordings" + * exist as non-directory files, return FALSE. + * Otherwise, return TRUE. + * + * Create the "metadata" and "recordings" directories if they are not there. + */ +int maybe_create_spool_dir(char *dirpath) { + struct stat info; + int spool_good = TRUE; + + if (stat(dirpath, &info) != 0) { + fprintf(stderr, "Spool directory \"%s\" does not exist.\n", dirpath); + spool_good = FALSE; + } else if (!S_ISDIR(info.st_mode)) { + fprintf(stderr, "Spool file exists, but \"%s\" is not a directory.\n", dirpath); + spool_good = FALSE; + } else { + // Spool directory exists. Make sure it has inner directories. + int path_len = strlen(dirpath); + char meta_path[path_len + 10]; + char rec_path[path_len + 12]; + snprintf(meta_path, path_len + 10, "%s/metadata", dirpath); + snprintf(rec_path, path_len + 12, "%s/recordings", dirpath); + + if (stat(meta_path, &info) != 0) { + fprintf(stdout, "Creating metadata directory \"%s\".\n", meta_path); + mkdir(meta_path, 0700); + } else if(!S_ISDIR(info.st_mode)) { + fprintf(stderr, "Metadata file exists, but \"%s\" is not a directory.\n", meta_path); + spool_good = FALSE; + } + + if (stat(rec_path, &info) != 0) { + fprintf(stdout, "Creating recordings directory \"%s\".\n", rec_path); + mkdir(rec_path, 0700); + } else if(!S_ISDIR(info.st_mode)) { + fprintf(stderr, "Recordings file exists, but \"%s\" is not a directory.\n", rec_path); + spool_good = FALSE; + } + } + + return spool_good; +} + int main(int argc, char **argv) { struct main_context ctx; int idx=0;