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.