Find the answer to your Linux question:
Results 1 to 1 of 1
Hello, The included code fails on Linux but not on OS X. I'm struggling to understand why. I get: Code: line 60, errno != EAGAIN && errno != EINTR: Input/output ...
  1. #1
    Just Joined!
    Join Date
    Jul 2008
    Posts
    1

    pseudo-terminals: read(2) gives EIO

    Hello,

    The included code fails on Linux but not on OS X. I'm struggling to
    understand why. I get:
    Code:
        line 60, errno != EAGAIN && errno != EINTR: Input/output error (errno = 5)
        Aborted
    An errno value of 5 is EIO on my Linux installation. The code invokes the program you give it on the command line and this error appears to occur right at the end, as the child is finishing.

    If I use a pipe instead of a pseudo-terminal (a small change in
    make_pt), it also seems to work fine.

    Here's the code (also uploaded to http://www.mr-edd.co.uk/files/guff/invoke.c).
    It aims to run the program specified by argv[1], ... , argv[argc-1]
    and forward the output to the parent stdout and stderr.

    Code:
    #ifndef _XOPEN_SOURCE
    #define _XOPEN_SOURCE 600
    #endif
    
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    #include <sys/time.h>
    #include <sys/uio.h>
    #include <sys/select.h>
    #include <sys/param.h>
    #include <fcntl.h>
    
    #include <string.h>
    #include <stdio.h>
    #include <errno.h>
    #include <stdlib.h>
    
    #define BAIL_IF(cond) \
        do \
        { \
            if ((cond)) \
            { \
                int e = errno; \
                fprintf(stderr, "line &#37;d, %s: %s (errno = %d)\n", \
                        (int)__LINE__, \
                        #cond, \
                        strerror(e), \
                        e); \
                abort(); \
            } \
        } \
        while (0)
    
    void make_pt(int *m, int *s)
    {
        const char *name;
        BAIL_IF((*m = posix_openpt(O_RDWR | O_NOCTTY)) < 0);
        BAIL_IF(grantpt(*m) < 0);
        BAIL_IF(unlockpt(*m) < 0);
        BAIL_IF((name = ptsname(*m)) == NULL);
        BAIL_IF((*s = open(name, O_RDWR | O_NOCTTY)) < 0);
    
    }
    
    void set_non_blocking(int fd)
    {
        int flags;
        BAIL_IF((flags = fcntl(fd, F_GETFL)) < 0);
        fcntl(fd, F_SETFL, flags | O_NONBLOCK);
    
    }
    
    void forward_child_output(fd_set fds, int *fd, FILE *target)
    {
        if (*fd >= 0 && FD_ISSET(*fd, &fds))
        {
            char buff[1024 > SSIZE_MAX ? SSIZE_MAX : 1024];
            int r = read(*fd, buff, sizeof buff);
            if (r > 0) BAIL_IF(fwrite(buff, 1, r, target) != (unsigned)r);
            else if (r == 0) *fd = -1;
            else BAIL_IF(errno != EAGAIN && errno != EINTR);
        }
    
    }
    
    void select_loop(int iomaster, int emaster, pid_t child_id)
    {
        pid_t pid = 0;
        fd_set fds;
        int waitstat = 0;
    
        while (1)
        {
            int n = 0;
            FD_ZERO(&fds);
            if (iomaster != -1) FD_SET(iomaster, &fds);
            if (emaster != -1) FD_SET(emaster, &fds);
            n = (iomaster > n) ? (iomaster > emaster ? iomaster :emaster) : n;
    
            /* TODO: forward input to child. */
    
            if (n > 0)
            {
                struct timeval tv = { 0, 10000 };
                int s = select(n + 1, &fds, NULL, NULL, &tv);
                BAIL_IF(s < 0);
                forward_child_output(fds, &iomaster, stdout);
                forward_child_output(fds, &emaster, stderr);
            }
    
            if (pid == 0) BAIL_IF((pid = waitpid(child_id, &waitstat, WNOHANG)) < 0);
    
            /* If communications channels are closed and the child has finished,
             * we're done
             */
            if (pid != 0 && iomaster == -1 && emaster == -1) break;
        }
    
    }
    
    int main(int argc, char **argv)
    {
        int iomaster, ioslave, emaster, eslave;
        pid_t pid;
    
        BAIL_IF(argc < 2);
    
        make_pt(&iomaster, &ioslave);
        make_pt(&emaster, &eslave);
    
        BAIL_IF((pid = fork()) < 0);
    
        if (pid == 0)
        {
            /* in the child */
            close(iomaster);
            close(emaster);
            BAIL_IF(dup2(ioslave, STDIN_FILENO) < 0);
            BAIL_IF(dup2(ioslave, STDOUT_FILENO) < 0);
            BAIL_IF(dup2(eslave, STDERR_FILENO) < 0);
            BAIL_IF(execvp(argv[1], argv + 1) < 0);
        }
        else
        {
            /* in the parent */
            close(ioslave);
            close(eslave);
            set_non_blocking(iomaster);
            set_non_blocking(emaster);
            select_loop(iomaster, emaster, pid);
            close(iomaster);
            close(emaster);
        }
    
        return 0;
    
    }
    Any insights and corrections are greatly appreciated!
    Last edited by edd____; 07-22-2008 at 07:35 PM. Reason: Added code tags

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
...