Commit d2cf322856 for asterisk.org

commit d2cf32285669b18cfcba0edcf3efbf8f91e4a63d
Author: Naveen Albert <asterisk@phreaknet.org>
Date:   Sun Oct 4 14:58:09 2026 -0400

    app_mixmonitor: Abort MixMonitor if ast_writefile fails

    An error flag is set if ast_writefile fails, but this flag
    is never used later. If the flag is set, clean up the MixMonitor
    invocation and abort.

    This complements #2149, which causes ast_writefile to fail
    if attempting to record to a file already being written to.

    Resolves: #2198

    UserNote: If MixMonitor fails to open a file for writing
    audio, it will now abort, rather than silently failing.

diff --git a/apps/app_mixmonitor.c b/apps/app_mixmonitor.c
index ef2caaad68..4ec689b6ec 100644
--- a/apps/app_mixmonitor.c
+++ b/apps/app_mixmonitor.c
@@ -521,6 +521,7 @@ struct mixmonitor_ds {
 	struct ast_filestream *fs_write;

 	struct ast_audiohook *audiohook;
+	char *datastore_id;

 	unsigned int samp_rate;
 	char *filename;
@@ -570,6 +571,7 @@ static void mixmonitor_ds_destroy(void *data)
 	mixmonitor_ds->destruction_ok = 1;
 	ast_free(mixmonitor_ds->filename);
 	ast_free(mixmonitor_ds->beep_id);
+	ast_free(mixmonitor_ds->datastore_id);
 	ast_cond_signal(&mixmonitor_ds->destruction_condition);
 	ast_mutex_unlock(&mixmonitor_ds->lock);
 }
@@ -848,6 +850,9 @@ static int mixmonitor_autochan_is_bridged(struct ast_autochan *autochan)
 	return is_bridged;
 }

+/* Forward declaration */
+static void mixmonitor_ds_remove_and_free(struct ast_channel *chan, const char *datastore_id);
+
 static void *mixmonitor_thread(void *obj)
 {
 	struct mixmonitor *mixmonitor = obj;
@@ -875,8 +880,6 @@ static void *mixmonitor_thread(void *obj)
 		ast_callid_threadassoc_add(mixmonitor->callid);
 	}

-	ast_verb(2, "Begin MixMonitor Recording %s\n", mixmonitor->name);
-
 	fs = &mixmonitor->mixmonitor_ds->fs;
 	fs_read = &mixmonitor->mixmonitor_ds->fs_read;
 	fs_write = &mixmonitor->mixmonitor_ds->fs_write;
@@ -888,6 +891,35 @@ static void *mixmonitor_thread(void *obj)

 	format_slin = ast_format_cache_get_slin_by_rate(mixmonitor->mixmonitor_ds->samp_rate);

+	if (errflag) {
+		/* Tear everything down and exit. */
+		mixmonitor_ds_close_fs(mixmonitor->mixmonitor_ds);
+		mixmonitor->mixmonitor_ds->audiohook = NULL;
+
+		ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);
+
+		ast_autochan_channel_lock(mixmonitor->autochan);
+		ast_audiohook_remove(mixmonitor->autochan->chan, &mixmonitor->audiohook);
+
+		mixmonitor_ds_remove_and_free(mixmonitor->autochan->chan, mixmonitor->mixmonitor_ds->datastore_id);
+		ast_autochan_channel_unlock(mixmonitor->autochan);
+		ast_autochan_destroy(mixmonitor->autochan);
+
+		ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock);
+		if (!mixmonitor->mixmonitor_ds->destruction_ok) {
+			ast_cond_wait(&mixmonitor->mixmonitor_ds->destruction_condition, &mixmonitor->mixmonitor_ds->lock);
+		}
+		ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);
+
+		ast_audiohook_destroy(&mixmonitor->audiohook);
+		mixmonitor_free(mixmonitor);
+
+		ast_module_unref(ast_module_info->self);
+		return NULL;
+	}
+
+	ast_verb(2, "Begin MixMonitor Recording %s\n", mixmonitor->name);
+
 	ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);

 	if (mixmonitor->skip_seconds > 0.0) {
@@ -1042,6 +1074,7 @@ frame_cleanup:
 	if (!mixmonitor->mixmonitor_ds->destruction_ok) {
 		ast_cond_wait(&mixmonitor->mixmonitor_ds->destruction_condition, &mixmonitor->mixmonitor_ds->lock);
 	}
+
 	ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);

 	/* Free the translate paths */
@@ -1316,7 +1349,7 @@ static int launch_monitor_thread(struct ast_channel *chan, const char *filename,
 		return -1;
 	}

-	ast_free(datastore_id);
+	mixmonitor->mixmonitor_ds->datastore_id = datastore_id; /* Steal the reference */

 	/* reference be released at mixmonitor destruction */
 	mixmonitor->callid = ast_read_threadstorage_callid();