cttyioctlstrace

ENOTTY: Inappropriate ioctl for device: program works, but not when executed inside <()


I have simple program that reads file line by line, and prints each line. The core of the program is:

while ((size = getline(&line, &len, f)) != -1)
    printf("%s", line);

Now I have following problem:

this works:

./test zz

this also works:

/bin/cat <(./test zz)

but when I pipe the output to other program, it does not work:

/bin/cat <(./test zz) | /bin/cat

there is no output. When I compare with strace the one that works, and the one that doesn't:

/bin/cat <(strace ./test zz)

/bin/cat <(strace ./test zz) | /bin/cat

I see this error: -1 ENOTTY (Inappropriate ioctl for device)

ioctl(0, TCGETS, 0x7ffc302104d0)        = -1 ENOTTY (Inappropriate ioctl for device)
brk(NULL)                               = 0x55b4e1fd3000
brk(0x55b4e1ff4000)                     = 0x55b4e1ff4000
fstat(0, {st_mode=S_IFCHR|0666, st_rdev=makedev(0x1, 0x3), ...}) = 0
ioctl(0, TCGETS, 0x7ffc30210350)        = -1 ENOTTY (Inappropriate ioctl for device)
read(0, "", 4096)                       = 0
close(0)                                = 0
exit_group(0)                           = ?
+++ exited with 0 +++

EDIT:

here is my proof-of-concept code:

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <stdbool.h>
#include <unistd.h>


int main(int argc, char **argv) {

    FILE *f;
    char *line = NULL;
    size_t len = 0;
    ssize_t size, cut;


    if (!isatty(STDIN_FILENO) || argc < 2 || (argv[1] == "-"))
        f = stdin;
    else
        if (!(f = fopen(argv[1], "r"))) {
            perror(argv[1]);
            return 1;
        }

    while ((size = getline(&line, &len, f)) != -1)
    printf("%s", line);

    fflush(stdout);
    fclose(f);
    if (line)
        free(line);
    exit(EXIT_SUCCESS);
}

EDIT2:

I have removed the part: (!isatty(STDIN_FILENO) and that fixed the problem. But I don't understand what is happening.

I originally added this, so that my program can take filename as argument ./test zz or take input from pipe cat zz | ./test.


Solution

  • If any of the following conditions are true, the program uses stdin for input:

    In the scenario you presented, stdin isn't connected to a terminal[1], so the test reads from stdin. You don't get the desired result since you want to read from the file zz.

    The first condition (!isatty(STDIN_FILENO)) makes no sense. You were correct to remove it. The remaining two conditions are sufficient to make cat zz | ./test work.


    1. I don't know why. But it's not important. There are plenty of times stdin isn't a terminal. For example, the stdin of daemons aren't terminals, and thus the stdin of program launched by deamons aren't terminals unless the daemon takes steps to make it so.