[alsa-devel] alsa-lib Plugin: File deadlock when piping to shell command

GitHub issues - edited github at alsa-project.org
Thu Nov 21 22:26:36 CET 2019


alsa-project/alsa-lib issue #14 was edited from bharris6:

When using the [ALSA File plugin](https://www.alsa-project.org/alsa-doc/alsa-lib/pcm_plugins.html) you can tell it to pipe playback data to either a file or a program/shell command.

>Output filename (or shell command the stream will be piped to if STR starts with the pipe char).  

Piping to a shell command (as shown below) causes a deadlock where alsa waits for the shell command to return at the same time that the shell command is waiting for alsa to write to its STDIN.

>From the [source code](https://github.com/alsa-project/alsa-lib/blob/master/src/pcm/pcm_file.c), it appears that [popen](https://pubs.opengroup.org/onlinepubs/009695399/functions/popen.html) is being used to initialize/open the output file specified in configuration:  

    static int snd_pcm_file_open_output_file(snd_pcm_file_t *file)
    {
        int err, fd;
    
        /* fname can contain keys, generating final_fname */
        err = snd_pcm_file_replace_fname(file, &(file->final_fname));
        if (err < 0)
            return err;
        /*printf("DEBUG - original fname: %s, final fname: %s\n",
          file->fname, file->final_fname);*/
    
        if (file->final_fname[0] == '|') {
            /* pipe mode */
            FILE *pipe;
            /* clearing */
            pipe = popen(file->final_fname + 1, "w");
            if (!pipe) {
                SYSERR("running %s for writing failed",
                        file->final_fname);
                return -errno;
            }
            fd = dup(fileno(pipe));
            err = -errno;
            pclose(pipe);
            if (fd < 0) {
                SYSERR("unable to dup pipe file handle for command %s",
                        file->final_fname);
                return err;  

Although I can confirm the file I pass in my ALSA configuration gets started fine, it never receives anything on its `stdin` like ALSA's (and popen's) documentation states it should:  

>If mode is w, *when the child process is started its file descriptor STDIN_FILENO shall be the readable end of the pipe*, and the file descriptor fileno(stream) in the calling process, where stream is the stream pointer returned by popen(), shall be the writable end of the pipe.

My /etc/asound.conf: 

    pcm.!default {
        type plug
        slave.pcm "duplicator
    }
    
    pcm.default {
        type plug
        slave.pcm "duplicator"
    }
    
    pcm.dmixer {
        type dmix
        ipc_key 1024
        ipc_key_add_uid false
        ipc_perm 0666
        slave {
            pcm "hw:0,0"
            period_time 0
            period_size 1024
            buffer_size 8192
            rate 44100
        }
    }    
    
    pcm.duplicator {
        type plug
        slave.pcm {
            type multi
            slave {
                a { pcm "dmixer" channels 2 }
                b { pcm "fileout" channels 2 }
            }
            bindings [
                { slave a channel 0 }
                { slave a channel 1 }
                { slave b channel 0 }
                { slave b channel 1 }
            ]
        }
        ttable [
            [ 1 0 1 0 ]
            [ 0 1 0 1 ]
        ]
    }

    pcm.fileout {
        type file
        slave.pcm "null"
        file "|safe_fifo_alt"
        format raw
        perm 0666
    }  

safe_fifo_alt:  

    #!/usr/bin/python3
    import os, sys
    
    BUFFER_SIZE = 16384
    
    path = "/tmp/audio"
    if not os.path.exists(path):
        os.mkfifo(path)
        
    _fifo_in = os.open(path, os.O_RDONLY | os.O_NONBLOCK)
    _fifo_out = os.open(path, os.WR_ONLY | os.O_NONBLOCK)
    
    for chunk in iter(lambda: sys.stdin.buffer.read(BUFFER_SIZE), b''):
        os.write(_fifo_out, chunk)
        
    os.close(_fifo_in)
    os.close(_fifo_out)  

I based `safe_fifo_alt` on [safe_fifo](https://github.com/dpayne/cli-visualizer/blob/master/bin/safe_fifo.c) from `cli-visualizer`.  They both hang indefinitely on their STDIN read because alsa is hanging on its `pclose()`.  

I've tested an alternative method to simulate the popen and pipe into their STDIN to make sure it worked, specifically:  

    head -c 500 /dev/urandom | sh -c safe_fifo_alt  

It appears this was changed in revision 22ade9b8c150240a960ca683ee6d8f53ce8bc6ea, where the `dup()` and `pclose()` calls were added and introduced the deadlock.

Issue URL     : https://github.com/alsa-project/alsa-lib/issues/14
Repository URL: https://github.com/alsa-project/alsa-lib


More information about the Alsa-devel mailing list