From 1c39305fad68b03bf578a77e91d0c20ba8b8145e Mon Sep 17 00:00:00 2001
From: Piotr Gregor <piotrek.gregor@gmail.com>
Date: Sun, 21 Feb 2016 15:16:55 +0000
Subject: [PATCH] FS-8810 fix crash on FS startup

The float-int-float fast arc cosine
mapping is now properly constructed (reused).
---
 src/mod/applications/mod_avmd/fast_acosf.c |  93 ++++++++++++-----
 src/mod/applications/mod_avmd/fast_acosf.h |  42 +++++++-
 src/mod/applications/mod_avmd/mod_avmd.c   | 111 ++++++++++++++++++---
 3 files changed, 204 insertions(+), 42 deletions(-)

diff --git a/src/mod/applications/mod_avmd/fast_acosf.c b/src/mod/applications/mod_avmd/fast_acosf.c
index 6c990d2520..6db0fadad4 100644
--- a/src/mod/applications/mod_avmd/fast_acosf.c
+++ b/src/mod/applications/mod_avmd/fast_acosf.c
@@ -1,3 +1,4 @@
+#include <switch.h>
 #include <stdio.h>
 #include <stdlib.h>
 #ifndef _MSC_VER
@@ -12,6 +13,7 @@
 #include <assert.h>
 #include <errno.h>
 #include <math.h>
+#include <string.h>
 #ifndef _MSC_VER
 #include <unistd.h>
 #endif
@@ -56,57 +58,94 @@ static float strip_float(float f)
 }
 #endif
 
-extern void compute_table(void)
+extern int compute_table(void)
 {
     uint32_t i;
-    float f;
-    FILE *acos_table_file;
-    size_t ret;
+    float   f;
+    FILE    *acos_table_file;
+    size_t  res;
 
     acos_table_file = fopen(ACOS_TABLE_FILENAME, "w");
 
     for (i = 0; i < ACOS_TABLE_LENGTH; i++) {
         f = acosf(float_from_index(i));
-        ret = fwrite(&f, sizeof(f), 1, acos_table_file);
-        assert(ret != 0);
+        res = fwrite(&f, sizeof(f), 1, acos_table_file);
+        if (res != 1) {
+            goto fail;
+        }
     }
 
-    ret = fclose(acos_table_file);
-    assert(ret != EOF);
+    res = fclose(acos_table_file);
+    if (res != 0) {
+        return -2;
+    }
+    return 0;
+
+fail:
+    fclose(acos_table_file);
+    return -1;
 }
 
-
-extern void init_fast_acosf(void)
+extern int init_fast_acosf(void)
 {
-    int ret;
+    int     ret, errsv;
+    FILE    *acos_fp;
+    char    err[150];
 
     if (acos_table == NULL) {
         ret = access(ACOS_TABLE_FILENAME, F_OK);
-        if (ret == 0) compute_table();
+        if (ret == -1) {
+            /* file doesn't exist, bad permissions,
+             * or some other error occured */
+            errsv = errno;
+            strerror_r(errsv, err, 150);
+            if (errsv != ENOENT) return -1;
+            else {
+	            switch_log_printf(
+		            SWITCH_CHANNEL_LOG,
+		            SWITCH_LOG_NOTICE,
+		            "File [%s] doesn't exist. Creating file...\n", ACOS_TABLE_FILENAME
+		        );
+                ret = compute_table();
+                if (ret != 0) return -2;
+            }
+        } else {
+	        switch_log_printf(
+	            SWITCH_CHANNEL_LOG,
+		        SWITCH_LOG_INFO,
+		        "Using previously created file [%s]\n", ACOS_TABLE_FILENAME
+		    );
+        }
+    }
 
-        acos_fd = open(ACOS_TABLE_FILENAME, O_RDONLY);
-        if (acos_fd == -1) perror("Could not open file " ACOS_TABLE_FILENAME);
-        assert(acos_fd != -1);
-        acos_table = (float *)mmap(
-            NULL,
+    acos_fp = fopen(ACOS_TABLE_FILENAME, "r");
+    if (acos_fp == NULL) return -3;
+    /* can't fail */
+    acos_fd = fileno(acos_fp);
+    acos_table = (float *) mmap(
+            NULL,                               /* kernel chooses the address at which to create the mapping */
             ACOS_TABLE_LENGTH * sizeof(float),
             PROT_READ,
-            MAP_SHARED | MAP_POPULATE,
+            MAP_SHARED | MAP_POPULATE,          /* read-ahead on the file.  Later accesses  to  the  mapping
+                                                 * will not be blocked by page faults */
             acos_fd,
             0
-        );
-    }
+            );
+    if (acos_table == MAP_FAILED) return -4;
+
+    return 0;
 }
 
-extern void destroy_fast_acosf(void)
+extern int destroy_fast_acosf(void)
 {
-    int ret;
-
-    ret = munmap(acos_table, ACOS_TABLE_LENGTH);
-    assert(ret != -1);
-    ret = close(acos_fd);
-    assert(ret != -1);
+    if (munmap(acos_table, ACOS_TABLE_LENGTH) == -1) return -1;
+    if (acos_fd != -1) {
+        if (close(acos_fd) == -1) return -2;
+    }
+    /* disable use of fast arc cosine file */
     acos_table = NULL;
+
+    return 0;
 }
 
 extern float fast_acosf(float x)
diff --git a/src/mod/applications/mod_avmd/fast_acosf.h b/src/mod/applications/mod_avmd/fast_acosf.h
index d608050da0..a5f2f0649e 100644
--- a/src/mod/applications/mod_avmd/fast_acosf.h
+++ b/src/mod/applications/mod_avmd/fast_acosf.h
@@ -1,10 +1,46 @@
 #ifndef __FAST_ACOSF_H__
 #define __FAST_ACOSF_H__
 
-extern void init_fast_acosf(void);
+
+#define ACOS_TABLE_FILENAME "/tmp/acos_table.dat"
+
+/*! \brief Arc cosine table initialization.
+ *
+ * @author Eric des Courtis
+ * @par    Changes: Piotr Gregor, 07 Feb 2016 (FS-8809, FS-8810)
+ * @return 0 on success, negative value otherwise:
+ *          -1 can't access arc cos table with error != NOENT,
+ *          -2 table creation failed (compute_table)
+ *          -3 can access table but fopen failed
+ *          -4 mmap failed
+ */
+extern int init_fast_acosf(void);
+
+/*! \brief Arc cosine table deinitialization.
+ *
+ * @author Eric des Courtis
+ * @par    Changes: Piotr Gregor, 09 Feb 2016 (FS-8809, FS-8810)
+ * @return 0 on success, negative value otherwise:
+ *          -1 munmap failed,
+ *          -2 close failed
+ */
+extern int destroy_fast_acosf(void);
+
+/*! \brief  Return arc cos for this argument.
+ * @details Uses previously created and mmapped file.
+ * @author  Eric des Courtis
+ */
 extern float fast_acosf(float x);
-extern void destroy_fast_acosf(void);
-extern void compute_table(void);
+
+/*! \brief Arc cosine table creation.
+ *
+ * @author Eric des Courtis
+ * @par    Changes: Piotr Gregor, 07 Feb 2016 (FS-8809, FS-8810)
+ * @return 0 on success, negative value otherwise:
+ *          -1 fwrite failed,
+ *          -2 fclose failed
+ */
+extern int compute_table(void);
 
 #endif
 
diff --git a/src/mod/applications/mod_avmd/mod_avmd.c b/src/mod/applications/mod_avmd/mod_avmd.c
index 14848dcb6b..9d6999d236 100644
--- a/src/mod/applications/mod_avmd/mod_avmd.c
+++ b/src/mod/applications/mod_avmd/mod_avmd.c
@@ -93,7 +93,7 @@ SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_avmd_shutdown);
 SWITCH_STANDARD_API(avmd_api_main);
 
 SWITCH_MODULE_LOAD_FUNCTION(mod_avmd_load);
-SWITCH_MODULE_DEFINITION(mod_avmd, mod_avmd_load, NULL, NULL);
+SWITCH_MODULE_DEFINITION(mod_avmd, mod_avmd_load, mod_avmd_shutdown, NULL);
 SWITCH_STANDARD_APP(avmd_start_function);
 
 /*! Status of the beep detection */
@@ -206,10 +206,16 @@ static switch_bool_t avmd_callback(switch_media_bug_t * bug, void *user_data, sw
 /*! \brief FreeSWITCH module loading function.
  *
  * @author Eric des Courtis
- * @return Load success or failure.
+ * @par    Changes: Piotr Gregor, 07 Feb 2016 (FS-8809, FS-8810)
+ * @return On success SWITCH_STATUS_SUCCES,
+ *         on failure SWITCH_STATUS_TERM.
  */
 SWITCH_MODULE_LOAD_FUNCTION(mod_avmd_load)
 {
+#ifdef FASTMATH
+    char    err[150];
+    int     ret;
+#endif
 
 	switch_application_interface_t *app_interface;
 	switch_api_interface_t *api_interface;
@@ -218,7 +224,8 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_avmd_load)
 
 
 	if (switch_event_reserve_subclass(AVMD_EVENT_BEEP) != SWITCH_STATUS_SUCCESS) {
-		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't register subclass %s!\n", AVMD_EVENT_BEEP);
+		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR,
+                "Couldn't register subclass [%s]!\n", AVMD_EVENT_BEEP);
 		return SWITCH_STATUS_TERM;
 	}
 
@@ -227,14 +234,66 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_avmd_load)
 		SWITCH_CHANNEL_LOG,
 		SWITCH_LOG_NOTICE,
 		"Advanced Voicemail detection enabled\n"
-		);
+	);
 
 #ifdef FASTMATH
-	init_fast_acosf();
+    ret = init_fast_acosf();
+    if (ret != 0) {
+        strerror_r(errno, err, 150);
+        switch (ret) {
+
+            case -1:
+	            switch_log_printf(
+		            SWITCH_CHANNEL_LOG,
+		            SWITCH_LOG_ERROR,
+		            "Can't access file [%s], error [%s]\n",
+                    ACOS_TABLE_FILENAME, err
+		        );
+                break;
+
+            case -2:
+	            switch_log_printf(
+		            SWITCH_CHANNEL_LOG,
+		            SWITCH_LOG_ERROR,
+		            "Error creating file [%s], error [%s]\n",
+                    ACOS_TABLE_FILENAME, err
+		        );
+                break;
+
+            case -3:
+	            switch_log_printf(
+		            SWITCH_CHANNEL_LOG,
+		            SWITCH_LOG_ERROR,
+		            "Access rights are OK but can't open file [%s], error [%s]\n",
+                    ACOS_TABLE_FILENAME, err
+		        );
+                break;
+
+            case -4:
+	            switch_log_printf(
+		            SWITCH_CHANNEL_LOG,
+		            SWITCH_LOG_ERROR,
+		            "Access rights are OK but can't mmap file [%s], error [%s]\n",
+                    ACOS_TABLE_FILENAME, err
+		        );
+                break;
+
+            default:
+	            switch_log_printf(
+		            SWITCH_CHANNEL_LOG,
+		            SWITCH_LOG_ERROR,
+		            "Unknown error [%d] while initializing fast cos table [%s], "
+                    "errno [%s]\n", ret, ACOS_TABLE_FILENAME, err
+		        );
+                return SWITCH_STATUS_TERM;
+        }
+        return SWITCH_STATUS_TERM;
+    } else
 	switch_log_printf(
 		SWITCH_CHANNEL_LOG,
 		SWITCH_LOG_NOTICE,
-		"Advanced Voicemail detection: fast math enabled\n"
+		"Advanced Voicemail detection: fast math enabled, arc cosine table "
+        "is [%s]\n", ACOS_TABLE_FILENAME
 		);
 #endif
 
@@ -246,7 +305,7 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_avmd_load)
 		avmd_start_function,
 		"[start] [stop]",
 		SAF_NONE
-		);
+	);
 
 	SWITCH_ADD_API(api_interface, "avmd", "Voicemail beep detection", avmd_api_main, AVMD_SYNTAX);
 
@@ -328,11 +387,34 @@ SWITCH_STANDARD_APP(avmd_start_function)
  */
 SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_avmd_shutdown)
 {
+#ifdef FASTMATH
+	int res;
+#endif
 
 	switch_event_free_subclass(AVMD_EVENT_BEEP);
 	
 #ifdef FASTMATH
-	destroy_fast_acosf();
+	res = destroy_fast_acosf();
+    if (res != 0) {
+        switch (res) {
+            case -1:
+	            switch_log_printf(
+		            SWITCH_CHANNEL_LOG,
+		            SWITCH_LOG_ERROR,
+		            "Failed unmap arc cosine table\n"
+		        );
+                break;
+            case -2:
+	            switch_log_printf(
+		            SWITCH_CHANNEL_LOG,
+		            SWITCH_LOG_ERROR,
+		            "Failed closing arc cosine table\n"
+		        );
+                break;
+            default:
+            break;
+        }
+    }
 #endif
 
 	switch_log_printf(
@@ -537,13 +619,16 @@ static void avmd_process(avmd_session_t *session, switch_frame_t *frame)
 				/* calculate variance */
 				v = session->sqa_b.sma - (session->sma_b.sma * session->sma_b.sma);
 
-				switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session->session), SWITCH_LOG_DEBUG, "<<< AVMD v=%f f=%f %fHz sma=%f sqa=%f >>>\n", v, f, TO_HZ(session->rate, f), session->sma_b.sma, session->sqa_b.sma);
+				switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session->session), SWITCH_LOG_DEBUG,
+                    "<<< AVMD v=[%f] f=[%f] [%f]Hz sma=[%f] sqa=[%f] >>>\n", v, f, TO_HZ(session->rate, f),
+                    session->sma_b.sma, session->sqa_b.sma);
 			}
 
 			/*! If variance is less than threshold then we have detection */
 			if (v < VARIANCE_THRESHOLD) {
 
-				switch_channel_set_variable_printf(channel, "avmd_total_time", "%d", (int)(switch_micro_time_now() - session->start_time) / 1000);
+				switch_channel_set_variable_printf(channel, "avmd_total_time",
+                    "[%d]", (int)(switch_micro_time_now() - session->start_time) / 1000);
 				switch_channel_execute_on(channel, "execute_on_avmd_beep");
 
 				/*! Throw an event to FreeSWITCH */
@@ -551,7 +636,8 @@ static void avmd_process(avmd_session_t *session, switch_frame_t *frame)
 				if (status != SWITCH_STATUS_SUCCESS) return;
 
 				switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Beep-Status", "stop");
-				switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Unique-ID", switch_core_session_get_uuid(session->session));
+				switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Unique-ID",
+                    switch_core_session_get_uuid(session->session));
 				switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "call-command", "avmd");
 
 				if ((switch_event_dup(&event_copy, event)) != SWITCH_STATUS_SUCCESS) return;
@@ -559,7 +645,8 @@ static void avmd_process(avmd_session_t *session, switch_frame_t *frame)
 				switch_core_session_queue_event(session->session, &event);
 				switch_event_fire(&event_copy);
 
-				switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session->session), SWITCH_LOG_DEBUG, "<<< AVMD - Beep Detected >>>\n");
+				switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session->session), SWITCH_LOG_DEBUG,
+                    "<<< AVMD - Beep Detected >>>\n");
 				switch_channel_set_variable(channel, "avmd_detect", "TRUE");
 				RESET_SMA_BUFFER(&session->sma_b);
 				RESET_SMA_BUFFER(&session->sqa_b);