Skip to content

mod_shell_stream: flush inherited stdio buffers in child before dup2 to stop console log leaking into the audio pipe - #3121

Open
asdf3309 wants to merge 1 commit into
signalwire:masterfrom
asdf3309:fix/shell-stream-stdout-leak
Open

asdf3309 wants to merge 1 commit into
signalwire:masterfrom
asdf3309:fix/shell-stream-stdout-leak

Conversation

@asdf3309

Copy link
Copy Markdown

Description

Fixes pop/clip noise at the end of every shell_stream:// playback (see #3120).

The child branch forks from the parent while FreeSWITCH console logs (e.g. sofia's hash(insert/...last_dial/...) notices) are still sitting in the block-buffered stdout FILE* stream. The sequence:

  1. fprintf(runtime.console = stdout) buffers console log text, not flushed yet
  2. switch_fork() — the child inherits a copy of the buffered data
  3. dup2(fds[1], STDOUT_FILENO) points stdout at the audio pipe
  4. switch_system() forks a grandchild that runs system(dcmd) (the audio source); when it exit(0)s, stdio flushes the inherited stdout buffer into the audio pipe
  5. The playback pump plays those log bytes as audio → tail pop/clip

This patch flushes stdout/stderr in the child before the dup2, so there is nothing buffered to leak into the pipe. The flush must happen before dup2, otherwise the logs get flushed into the pipe.

Type of Change

  • Bug fix

Related Issues

Fixes #3120

Testing

  • Tested manually (verified on 1.10.12 in a Docker container, 24 kHz L16 → 8 kHz alaw)

With a pure-silence repro (shell_stream://sh -c "dd if=/dev/zero bs=24000 count=4"), the pipe previously contained 1,145 bytes of log text after the 96,000 bytes of stream data, e.g.:

pipe read=1145 first=5d20736f6669612f last=0a1b5b6d   ("] sofia/..." ... "\n\x1b[m")

After the fix the pipe contains exactly the stream data, RTP capture shows a clean silence tail, and the audible pop is gone. Note this is a different failure mode than #3104 (blocking wait() in file_open): that one is addressed separately.

Note: master's printf("EOF") sentinel and 1.10.12's read()==0 end-of-stream both leave the leak path intact; flushing in the child fixes the leak for both.

The child branch forks from the parent while FreeSWITCH console logs are
still sitting in the block-buffered stdout FILE* stream. After
dup2(fds[1], STDOUT_FILENO), switch_system() forks a grandchild that runs
the shell command; when that grandchild exits, stdio flushes the
inherited buffered console log text into the audio pipe, and the playback
pump plays those log bytes as audio -- a pop/clip noise at the end of
every shell_stream:// stream.

Flush stdout/stderr in the child before dup2 so there is nothing buffered
to leak into the pipe. Verified on 1.10.12: pipe no longer contains log
text after stream data, RTP tail is silence.

Fixes signalwire#3120
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant