💾 Archived View for thrig.me › blog › 2023 › 06 › 14 › the-pipes-the-pipes.gmi captured on 2023-07-10 at 13:44:28. Gemini links have been rewritten to link to archived content

View Raw

More Information

⬅️ Previous capture (2023-06-16)

➡️ Next capture (2023-11-14)

-=-=-=-=-=-=-

The pipes, the pipes ...

Let's say you have a tool that makes unsuitable noises on one of the file handles, and the fine manual is silent on how to make the noises go away. Now, some would either patch the software, or at least report an issue to upstream—maybe there could be a quiet or silent option for the tool?

    $ fluidsynth -ni ~/share/sf/genu.sf2 dannyboy.midi
    FluidSynth version 1.1.9
    Copyright (C) 2000-2018 Peter Hanappe and others.
    Distributed under the LGPL license.
    SoundFont(R) is a registered trademark of E-mu Systems, Inc.

    fluidsynth: warning: Found samples with invalid loops, audible glitches possible.
    Type 'help' for help topics.

    > q
    unknown command: q (try help)
    > quit
    cheers!
    $ man fluidsynth | col -bx | egrep -i 'quiet|silent'
    $ 

License spam, warnings, and nothing obvious in the docs on how to turn that stuff off. Also, fluidsynth did not actually play the MIDI file. Luckily it is not too difficult to write your own wrapper program that picks a default soundfont if one is not supplied and plays the given MIDI file.

    $ readlink ~/share/sf/genu.sf2
    /usr/local/share/generaluser-gs/GeneralUser_GS.sf2
    $ midiplay -s ~/share/sf/genu.sf2 dannyboy.midi
    fluidsynth: warning: Found samples with invalid loops, audible glitches possible.
    ...

This still produces noise on, uh,

    $ midiplay -s ~/share/sf/genu.sf2 dannyboy.midi 2>/dev/null
    ...

standard error. This could be thrown away, though sometimes there may actually be an error in addition to the usual noise, in which case you ideally would want to see that error, but not the noise. Some folks aim to remove as many warnings as possible so that the errors when they do happen are easier to spot, but not everyone does that nor is it practical in many cases. Wolf! Look, no really, there's a wolf! Oh what now you ignored an actual problem?

Pipes are one way to wrangle this thing that should not be; standard error could be sent to some pipe, and then if the tool exits with a non-zero status word the error output can be shown. This of course assumes that the tool does actual exit with a non-zero status word, which is not always the case. In that case, there probably should be a patch or bug report to upstream?

    #include <sys/wait.h>

    #include <err.h>
    #include <stdlib.h>
    #include <unistd.h>

    #define IS_CHILD(x) !(x)

    #define PIPE_READER 0
    #define PIPE_WRITER 1

    int
    main(void)
    {
        int epipe[2];
        if (pipe(epipe) == -1) err(1, "pipe");

        pid_t pid = fork();
        if (pid < 0) err(1, "fork");

        if (IS_CHILD(pid)) {
            close(epipe[PIPE_READER]);
            if (epipe[PIPE_WRITER] != STDERR_FILENO) {
                if (dup2(epipe[PIPE_WRITER], STDERR_FILENO) !=
                    STDERR_FILENO)
                    err(1, "dup2");
                close(epipe[PIPE_WRITER]);
            }

            // non-boilerplate child code: emit to standard error
            // and sometimes exit with a non-zero exit status word
            warnx("wolf wolf wolf");
            if (arc4random_uniform(2)) {
                warnx("Wolf!");
                exit(1);
            }
            exit(0);
        }

        close(epipe[PIPE_WRITER]);
        int status;
        wait(&status);
        if (status) {
            char buf[42];
            while (1) {
                ssize_t ret = read(epipe[PIPE_READER], buf, 42);
                if (ret == 0) break; // EOF
                if (ret < 0) err(1, "read");
                write(STDERR_FILENO, buf, (size_t) ret);
            }
            exit(1);
        }
        exit(0);
    }

sometimes.c

Probably this can be done in a shell with less typing, but I've never been very good at shell redirections, and invariably need to write a little test program to figure out the correct order of the simple 2>&1 stuff.

    $ make sometimes; repeat 4 ./sometimes
    cc -O2 -pipe    -o sometimes sometimes.c
    sometimes: wolf wolf wolf
    sometimes: Wolf!

Four runs of the program, but only one wolf! notice. Yes, this was cherry picked—it makes for a good example.

A downside of this method is that standard error may be lost as it is being buffered instead of being printed right away; stderr is unbuffered for a reason. Also there could be problems if the tool being wrapped emits scandalous amounts of data, perhaps the pipe fills up, blocks, and then bad things happen. In that case it might be sensible to redirect the output to a file, or to get the developers to clean up some of their log noise. The good old days when the Java logging velocities would either fill up disks or tickle linux kernel bugs, I so do not miss them.

The pipe has a reader end and a writer end; depending on which way the bytes need to go one side will close the reader or writer, and the other side will close the writer or reader, and then one side might duplicate and then close the remaining end of the pipe so that, here, standard error is wired up to what the parent will read from if need be.

Also do note that some programs will close off "unused" file descriptors, usually for security reasons, e.g. you might see stuff like

        closefrom(STDERR_FILENO + 1);

in the source code for ssh. This may make it difficult to do clever pipe tricks over such programs.

And here's some more pipes.

https://www.youtube.com/watch?v=NqcGbhdfACI

tags #unix

bphflog links

bphflog index

next: The GPL: A Postmortem