I am working on a small libuv-based program. This program should read user-given text from standard input and provide results based off of the input. Below is the source code for the file:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <uv.h>
static void
alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf)
{
static char buffer[1 << 16];
*buf = uv_buf_init(buffer, 1 << 16);
}
static void
read_stdin(uv_stream_t *stream, ssize_t nread, const uv_buf_t* buf)
{
printf("Read STDIN\n");
if (nread < 0) {
// stdin closed
uv_read_stop(stream);
return;
}
printf("Input length: %zu\n", nread);
printf("Buffer length: %zu (%s)\n", buf->len, buf->base);
}
int
main(int argc, char *argv[])
{
/* Initialize loop */
uv_loop_t loop;
uv_loop_init(&loop);
/* Input */
uv_tty_t input;
uv_tty_init(&loop, &input, STDIN_FILENO, 1);
uv_read_start((uv_stream_t *)&input, alloc_buffer, read_stdin);
/* Run loop */
uv_run(&loop, UV_RUN_DEFAULT);
return 0;
}
The above program works fine after being compiled and invoked with no standard input redirection. For example:
> ./test
hello world
Read STDIN
Input length: 12
Buffer length: 65536 (hello world
)
Read STDIN
>>> stdin closed
And piping the result of echo
or some other process yields the correct result as well:
> echo "hello world" | ./test
Read STDIN
Input length: 12
Buffer length: 65536 (hello world
)
Read STDIN
>>> stdin closed
An issue arises when I try to redirect standard input from some regular file or /dev/null
:
> ./test < text.txt
Aborted (core dumped)
Below is the gdb backtrace of the crash:
#0 0x00007ffff6df2d67 in raise () from /usr/lib/libc.so.6
#1 0x00007ffff6df4118 in abort () from /usr/lib/libc.so.6
#2 0x00007ffff79c6c90 in uv.io_poll () from /usr/lib/libuv.so.11
#3 0x00007ffff79ba64f in uv_run () from /usr/lib/libuv.so.11
#4 0x0000000000400b4b in main (argc=1, argv=0x7fffffffe338) at main.c:40
Does anyone have any ideas on why this is happening? Am I using the libuv API incorrectly?
I tried running the failing program through strace and saw the following line right before the program was aborted:
epoll_ctl(5, EPOLL_CTL_ADD, 0, {EPOLLIN, {u32=0, u64=0}}) = -1 EPERM (Operation not permitted)
I did some further reading and it seems using epoll with a regular file descriptor simply does not work. In the first two of the original three tests, file descriptor 0 (standard input) was not a regular file. In the last test, the shell would have closed and replaced file descriptor 0 with the input file text.txt
.
I will most likely have to use the libuv library function uv_guess_handle
, which can be used to determine the type of the STDIN_FILENO
file descriptor:
/* * Used to detect what type of stream should be used with a given file * descriptor. Usually this will be used during initialization to guess the * type of the stdio streams. * For isatty() functionality use this function and test for UV_TTY. */ UV_EXTERN uv_handle_type uv_guess_handle(uv_file file);