3

I reversed statements in if/else, corrected now.


I am reading a code snippet from Advanced Programming in the UNIX® Environment:

The program tests its standard input to see whether it is capable of seeking.

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
int main(void){

    if(lseek(STDIN_FILENO,0, SEEK_CUR) == -1)
        printf("cannot seek\n");
    else{
        printf("seek ok\n");
    }
}

I compile and run it (under Ubuntu 18.04.2 LTS) but don't understand the following behaviors.

//1
$ ./a.out 
cannot seek

//2
$ ./a.out < /etc/passwd
seek OK

//3
$ cat < /etc/passwd | ./a.out
cannot seek

//4
$ ./a.out < /var/spool/cron/FIFO
cannot seek

Why //1 is cannot seek? Empty stdin should be able to seek I think. Is it because stdin has not been opened yet? Because I heard that normally stdin, stdout and stderr are opened when a program starts to run.

Why //2 is OK and //3 is not? I think they are the same.

3
  • I just found out that Unix C API calls is ontopic here :).
    – Rick
    Feb 23, 2019 at 13:22
  • I can't reproduce your seek OK with the given program on OpenBSD (cat file | ./a.out returns seek OK though). There's something wonky about the code. The terminal device (which is attached to the a.out process when no redirection is done) is not seekable.
    – Kusalananda
    Feb 23, 2019 at 13:31
  • //1 doesn't have an "empty stdin", but a stdin connected to the tty. If you want an "empty" stdin, test it with ./a.out <&-. Notice that the if/else are reversed in that code snippet ;-) (and that that's not a reliable way to test whether a file is seekable).
    – user313992
    Feb 23, 2019 at 13:51

1 Answer 1

4

//1 ./a.out:

If you do no redirection of stdin (no pipe and no <), stdin is inherited from the parent process. As you run a.out interactively in a shell, it inherits the terminal device that gets your keyboard input as stdin.

Terminal devices aren't usually seekable because they represent user interaction, but according to the POSIX standard lseek may return success and simply do nothing. On Linux lseek fails with an ESPIPE.

//2 ./a.out < /etc/passwd:

Here stdin is redirected to an open file. As /etc/passwd should be a regular file, it is seekable.

//3 cat < /etc/passwd | ./a.out:

Here you start two processes (cat and ./a.out) and connect them with a pipe.

cat (without other arguments) reads it stdin (/etc/passwd) and copies it to its stdout (the pipe connecting to ./a.out). This is not the same case as //2. From the perspective of ./a.out the stdin cannot seek because it is only a pipe connecting to another process.

//4 ./a.out < /var/spool/cron/FIFO:

Here you have a named pipe or similar special file. This case is similar to //3. You have an unidirectional connection to another process. And these are not seekable.

13
  • Hmm... Is any of this Linux-specific? I get cannot seek on the first two and seek OK on the last two on OpenBSD. Maybe I should ask a separate question about that, but if there's a portability issue with the code, it would be good to point this out.
    – Kusalananda
    Feb 23, 2019 at 13:50
  • AFAIK this is not Linux-specific. There were some proposals for „seekable pipes“ but these don't seem to be implemented anywhere by default. But as @Uncle Billy already mentioned, the if-condition is reversed in the code snipped in the question.
    – cg909
    Feb 23, 2019 at 13:58
  • 2
    @Kusalananda that code is not portable, but in addition to that, it's printing exactly the reverse ("seek OK" when the lseek() failed and returned -1).
    – user313992
    Feb 23, 2019 at 13:59
  • @cg909 it's not about being able to actually seek on a pipe -- lseek is simply allowed to succeed without moving the pointer on devices that don't support seeking. There's no requirement to fail with an error in that case.
    – user313992
    Feb 23, 2019 at 14:03
  • 1
    @UncleBilly You're partly right. unix.com/man-page/posix/3P/lseek »The behavior of lseek() on devices which are incapable of seeking is implementation-defined. The value of the file offset associated with such a device is undefined.« and »Errors: ESPIPE The fildes argument is associated with a pipe, FIFO, or socket.« So lseek on pipes needs to fail with an error, but it isn't defined for terminal devices etc.
    – cg909
    Feb 23, 2019 at 14:13

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .