2

I've got some difficulties with this code. I need to get all the information from the pipe at its end. But, I get a segfault error.

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>

int main(void){

    int tube[2];

    if(pipe(tube) == -1){
        perror("Erreur");
        exit(1);
    }

    dup2(tube[1],1);

    printf("Few lines \n");
    printf("of an undefined size. \n");

    while (!feof(tube[0])) {
        char temp = fgetc(tube[0]);
        printf("chaine : %c\n", temp);
    }

    return 0;
}

If you have an idea of how I can handle this problem, please explain.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
ztarky
  • 53
  • 1
  • 5
  • 2
    How to handle such problems is to start now to learn how to use a debugger. I'm not being glib: that is an essential skill to diagnose and fix your own problems without having to wait for people on the internet to do it for you. ;-) – underscore_d Apr 24 '18 at 14:36
  • The argument of `fgetc()` is a `FILE *`. Your code passes an `int`. That introduces undefined behaviour. Also, `fgetc()` returns `int`, not `char`. Lastly, there are plenty of good explanations of why a `while (!feof())` style loop is bad technique - google will help you find one. – Peter Apr 24 '18 at 15:33

3 Answers3

3

The pipe function returns is pair of int file descriptors, not FILE ones. That means that you can use read, write, or close on them but neither fgetc, nor feof.

In addition, while(!feof(file)) is (almost) always wrong, because the flag is set after an unsuccessfull read has reached the end of the file.

And that is not all. You only get an EOF on the read end of a pipe when all descriptors on the write end are closed. So you must close or flush stdout to ensure that all characters have been written to the pipe, close the file descriptor 1 if you have not closed stdout, and close tube[1] which is still a file descriptor for the write end of the tube.

So you can replace your while loop with:

close(tube[1]);
fclose(stdout);

while (1) {
    char temp;
    if (read(tube[0], &temp, 1) < 1) break;
    fprintf(stderr, "chaine : %c\n", temp);
}

It fixes the SEGFAULT caused by using feof and fgetc on something that is not a FILE, and ensure a proper close of the write end of the file before reading its content to obtain a nice end of file condition.

Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
  • 1
    In case the pipe is still empty, the `read()` function will block waiting for content to be written to the pipe and the process will hang typically. This answer discuses that: https://stackoverflow.com/a/8130971/4365678 Also you can read about it in the man page for pipe: https://linux.die.net/man/7/pipe – Ibrahim Mohamed Dec 29 '19 at 18:42
1

But, I get a segfault error. ? that means you didn't read the compiler warning properly. when you do feof(tube[0]) it says feof() expects FILE* but you have provided of int(tube[0] is inetger) type.

/usr/include/stdio.h:828:12: note: expected ‘struct FILE *’ but argument is of type ‘int’

So first thing is always read the compiler warnings & compile code with -Wall flags.

This fgetc(tube[0]); is not the way to read data from file descriptor, use read() system call to read data from file descriptors not fgetc(). fgetc() you can use if you had opened the file with fopen().

Also

dup2(tube[1],1); /* this is not expected one which you want*/

use like this

dup2(1,tube[1]);/*stdout get duplicated with tube[1] i.e whatever you
                        write on stdout will be written into write end of pipe*/

Here is the simple example.

char temp[100];
        int ret = 0;
        ret = read(tube[0],temp,sizeof(temp));/*reading from pipe*/
        if(ret == -1)   {
                perror("read");
                return 0;
        }
        else {
                temp[ret] = '\0';/*read returns no of items read, so put null
                                        at last otherwise you may get some junk data  */
                printf("%s",temp);
        }

Read man 2 read & man 2 dup2 to know how these system calls works..

Achal
  • 11,821
  • 2
  • 15
  • 37
0

You're using a function that returns a single character (fgetc) then treating that value as a pointer to a string in your printf call. You're also storing that character as the address of the pointer to a character rather than as the actually character, so when printf goes to read your string it's reading some low memory that it doesn't own. fgetc returns the character itself, you you need a char variable, not a char*.

Try:

while (!feof(tube[0])) {
    char temp = fgetc(tube[0]);
    printf("chaine : %c\n", temp);
}
user2686305
  • 1
  • 1
  • 2
  • I always get a segfault. But, yes that was a misconception (I update the main post in consequence). Thx – ztarky Apr 24 '18 at 14:39
  • 1
    feof() and fgetc() both expect pointers to streams, not file descriptors. Try creating a stream with fpopen(). – user2686305 Apr 24 '18 at 14:51