-1

The following code, when ran, does not respond to terminal input. I have ran this under Debian 10.5.0 and CentOS 7 with the same results. What am I missing?

#include <sys/epoll.h>
#include <stdio.h>
#include <unistd.h>

int main(void) {

    char buffer[4096];
    int fd = epoll_create1(EPOLL_CLOEXEC);

    struct epoll_event event;
    int ctl_ret;
    event.events = EPOLLIN;
    event.data.fd = STDIN_FILENO;
    ctl_ret = epoll_ctl(fd, EPOLL_CTL_ADD, STDIN_FILENO, &event);
    if (ctl_ret) {
        perror("epoll_ctl");
        return 1;
    }

    int nr_event;
    for (;;) {
        fprintf(stderr, "Starting epoll_wait\n");
        nr_event = epoll_wait(fd, &event, 1, -1);
        if (nr_event < 0) {
            perror("epoll_wait");
            return 1;
        }
        fprintf(stderr, "Reading: %d\n", event.data.fd);
        printf("%ld\n", read(0, buffer, sizeof(buffer)));
    }

}
Mike B
  • 73
  • 8
  • 2
    Were there any errors from epoll_create1 or epoll_ctl? – user253751 Sep 14 '20 at 17:16
  • No. I have updated the code above to also check for epoll_ctl() and epoll_wait() errors. – Mike B Sep 14 '20 at 17:22
  • 1
    Your implementation is fine. Do `for (;;) { long nbytes = 0; ... nbytes = read(0, buffer, sizeof(buffer)); buffer[nbytes-1] = 0; printf("%ld - %s\n", nbytes, buffer); if (strcmp (buffer, "quit") == 0) break;` and you will see `epoll_wait` is happily waiting until data is available on `fd` and then `read` is returning the no. of bytes read (which we overwrite `'\n'` in `buffer` with `0` to treat content as a string) and then check if the entry was `"quit"` to exit. I don't see any problem. – David C. Rankin Sep 14 '20 at 17:51
  • Did you try without epoll to see if your program actually gets any data on standard input? – user253751 Sep 15 '20 at 11:45

1 Answers1

1

As Basile Starynkevitch alluded to, I needed to update the terminal configuration via termios(3).

I have provided the revised code below for reference. For the changes required, please see the code between the // TERMINAL SETUP and // EPOLL SETUP comments.

#include <errno.h>
#include <linux/input.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/epoll.h>
#include <termios.h>
#include <unistd.h>

int main(void) {

    // TERMINAL SETUP
    struct termios tty_cfg, tty_cfg_cache;

    // Check that fd is a TTY
    if (!isatty(STDIN_FILENO)) {
        fprintf(stderr, "Standard input is not a terminal.\n");
        return EXIT_FAILURE;
    }

    // Save old terminal configuration
    if (tcgetattr(STDIN_FILENO, &tty_cfg_cache) == -1 ||
        tcgetattr(STDIN_FILENO, &tty_cfg) == -1) {
        fprintf(stderr, "Cannot get terminal settings: %s.\n", strerror(errno));
        return EXIT_FAILURE;
    }

    // Set new terminal configuration
    tty_cfg.c_iflag &= ~(IGNBRK | BRKINT | PARMRK);
    tty_cfg.c_lflag &= ~(ICANON | ISIG | ECHO | IEXTEN | TOSTOP);
    tty_cfg.c_cc[VMIN] = 0;
    tty_cfg.c_cc[VTIME] = 0;
    tty_cfg.c_cc[VSTART] = 0;
    tty_cfg.c_cc[VSTOP] = 0;
    if (tcsetattr(STDIN_FILENO, TCSANOW, &tty_cfg) == -1) {
        const int  saved_errno = errno;
        tcsetattr(STDIN_FILENO, TCSANOW, &tty_cfg_cache);
        fprintf(stderr, "Cannot set terminal settings: %s.\n", strerror(saved_errno));
        return EXIT_FAILURE;
    }

    // EPOLL SETUP
    // Create epoll
    int epfd = epoll_create1(EPOLL_CLOEXEC);

    // Add stdin control interface to epoll
    struct epoll_event event;
    int ctl_ret;
    event.events = EPOLLIN;
    event.data.fd = STDIN_FILENO;
    ctl_ret = epoll_ctl(epfd, EPOLL_CTL_ADD, STDIN_FILENO, &event);
    if (ctl_ret) {
        perror("epoll_ctl");
        return 1;
    }

    // LOOP AND MONITOR EPOLL
    int nr_event;
    unsigned char keys[16];
    ssize_t n;
    size_t i, done;
    done = 0;
    while (!done) {
        
        // Start epoll_wait
        nr_event = epoll_wait(epfd, &event, 1, -1);
        if (nr_event < 0) {
            perror("epoll_wait");
            return 1;
        }
        
        // Read STDIN_FILENO
        n = read(STDIN_FILENO, keys, sizeof keys);
        if (n > 0) {
            for (i = 0; i < n; i++) {
                // Quit if 'q' or 'Q' is pressed
                if (keys[i] == 'q' || keys[i] == 'Q')
                    done = 1;
                if (keys[i] >= 32 && keys[i] <= 126)
                    printf("Key '%c' = 0x%02x = %u pressed\n", keys[i], keys[i], keys[i]);
                else
                if (keys[i])
                    printf("Key '\\%03o' = 0x%02x = %u pressed\n", keys[i], keys[i], keys[i]);
                else
                    printf("NUL key (0) pressed\n");
            }
            fflush(stdout);
        }
    }
    
    // RESTORE TERMINAL SETTINGS
    tcsetattr(STDIN_FILENO, TCSAFLUSH, &tty_cfg_cache);

    return EXIT_SUCCESS;

}
Mike B
  • 73
  • 8