Problems caused by STDIN set to non-blocking mode

5,291

Solution 1

When this happens, run bash from the command line, then exit (to return the first bash). Should work again. Somewhat interesting details here: https://stackoverflow.com/questions/19895185/bash-shell-read-error-0-resource-temporarily-unavailable.

Solution 2

If you need to script a workaround, you can use

perl -MFcntl -e 'fcntl STDIN, F_SETFL, fcntl(STDIN, F_GETFL, 0) & ~O_NONBLOCK'
Share:
5,291
recvfrom
Author by

recvfrom

Software Engineer working on Google Chrome

Updated on September 18, 2022

Comments

  • recvfrom
    recvfrom almost 2 years

    Certain commands start to consistently fail in a given terminal window:

    $ sudo apt-get install ipython
    ...
    After this operation, 3,826 kB of additional disk space will be used.
    Do you want to continue? [Y/n] Abort.
    $ 
    
    $ kinit -f <username>
    Password for <username>@<domain>: 
    kinit: Pre-authentication failed: Cannot read password while getting initial credentials
    $
    
    $ passwd
    Changing password for <username>.
    (current) UNIX password: 
    passwd: Authentication token manipulation error
    passwd: password unchanged
    $ 
    
    $ crontab -e
    Too many errors from stdincrontab: "/usr/bin/sensible-editor" exited with status 1
    $ 
    
    $ sudo docker run -it ubuntu bash
    (hangs forever)
    

    In searching for the cause, strace revealed that the programs attempt to read from STDIN but receive an error:

    read(0, 0x7fffe1205cc7, 1) = -1 EAGAIN (Resource temporarily unavailable)
    

    From the read(2) man page:

    ERRORS
        EAGAIN The file descriptor fd refers to a file other than a socket and has been marked nonblocking (O_NONBLOCK), and the read would block.
    

    Sure enough, STDIN is marked non-blocking for that terminal window (indicated by the 4 in flags):

    $ cat /proc/self/fdinfo/0 
    pos:    0
    flags:  0104002
    mnt_id: 25
    

    I'm assuming that some program I was using set STDIN to non-blocking mode and then didn't set it back upon exiting (or it was killed before it could.)

    I couldn't figure out how to fix this issue from the command line, so I wrote the following program to do it (and it also lets you change STDIN to non-blocking mode to see what breaks.)

    #include <errno.h>
    #include <fcntl.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    
    int makeStdinNonblocking(int flags) {
        if (fcntl(STDIN_FILENO, F_SETFL, flags | O_NONBLOCK) < 0) { 
            printf("Error calling fcntl in %s: %s\n", __FUNCTION__, strerror(errno));
            return EXIT_FAILURE;
        } 
        return EXIT_SUCCESS;
    }
    int makeStdinBlocking(int flags) {
        if (fcntl(STDIN_FILENO, F_SETFL, flags & ~(O_NONBLOCK)) < 0) { 
            printf("Error calling fcntl in %s: %s\n", __FUNCTION__, strerror(errno));
            return EXIT_FAILURE;
        } 
        return EXIT_SUCCESS;
    }
    int main(int argc, char *argv[]) {
        int flags;
        if (argc != 2) {
            goto usage;
        }
        if ((flags = fcntl(STDIN_FILENO, F_GETFL, 0)) < 0) {
            printf("Error calling fcntl in %s: %s\n", __FUNCTION__, strerror(errno));
            return EXIT_FAILURE;
        }
        if (0 == strncmp(argv[1], "nonblock", 9)) {
            return makeStdinNonblocking(flags);
        }
        else if ( 0 == strncmp(argv[1], "block", 6)) {
            return makeStdinBlocking(flags);
        }
    usage:
        printf("Usage: %s <nonblock|block>\n", argv[0]);
        return EXIT_FAILURE;
    }
    

    Anyway, I was wondering:

    1. Is there a way to make STDIN not non-blocking with standard command line utilities?
    2. Should the shell (in my case, bash) automatically restore the flags on STDIN (and/or STDOUT/STDERR) between commands? Is there a use case for one command relying on STDIN changes made by another program?
    3. Is it a bug for a program to assume that STDIN will be in blocking mode when the program starts, and should each program have to specifically turn-off non-blocking mode if it would cause things to break (see the examples above)?

    For reference, I'm using Ubuntu 17.10 and GNU bash, version 4.4.12(1)-release (x86_64-pc-linux-gnu)

    Update: It looks like this issue was addressed for Fedora with a patch to bash:

    https://bugzilla.redhat.com/show_bug.cgi?id=1068697

    The fix doesn't appear to have been applied upstream, though, at least with version 4.4.18(1)-release (from January 2018.) Also, the bash maintainer mentions that bash shouldn't really be responsible for this:

    https://lists.gnu.org/archive/html/bug-bash/2017-01/msg00043.html

    It sounds like applications should be responsible for restoring the original flags of STDIN if it changes them, so I'm using the following program to check STDIN:

    #include <errno.h>
    #include <fcntl.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    
    int main() {
        int flags;
        if ((flags = fcntl(STDIN_FILENO, F_GETFL, 0)) < 0) {
            printf("Error calling fcntl in %s: %s\n", __FUNCTION__, strerror(errno));
        }
        if (0 != (flags & (O_NONBLOCK))) {
            printf("Warning, STDIN in nonblock mode\n");
        }
        return EXIT_SUCCESS;
    }
    

    I compiled the program (gcc -o checkstdin checkstdin.c) and then put the following in my .bashrc to get it to run after every command:

    PROMPT_COMMAND+="/path/to/checkstdin"
    

    It will print a warning to STDOUT if it detects that STDIN is now in non-blocking mode.