0

FINAL UPDATE: Code updated with final working version, got everything working thanks to the code found on: How to flush stdin without requiring user input?

Im programming C and having some trouble redirecting output from a parent process to a child process using file descriptors.

The idea of all of this, is for program A to be the middleman between all the data that goes from two programs. I have a process A that creates a child process B which executes a execlp to a program C. There is also a program D that communicates with process A by named pipes and the idea is to redirect this communication to program C using unnamed pipes.

Right now my programs redirect the inicial communication from C to A to D correctly and from D to A correctly but fails when the redirection from A to C is supposed to happen.

I think the problem is that fgets() does not seem to retrieve the input unless two enters are given. I've tried using scanf, fscanf, getchar() and others aswell as flushing in multiple ways, nothing worked. The only problem really is the fact that two inputs seem to be required for the communication of A to C to occur. There are a million posts about this, and i've tried a lot of them to no sucess. Can anyone help? Sorry if it sounds confusing.

Process A:(middleman)

#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>

int main(void) 
{  //0->read 1->write
    int fd_out[2];
    int fd_in[2];

    pipe(fd_out);
    pipe(fd_in);
    
    pid_t pid = fork(); //Create process B
      
    char s[BUFSIZ];
    char s2[BUFSIZ];

    if ( pid == 0 ) 
    { 
        close(STDOUT_FILENO);
        dup(fd_out[1]);
        close(fd_out[0]); 
        
        close(STDIN_FILENO);
        dup(fd_in[0]); 
        close(fd_in[1]);
        
        if(execlp("./test_pipe_game","./test_pipe_game",(char*)NULL) == -1){
            printf("Error EXECL\n");  //Program C
        }
    }else{
        int fifo;
        char * myfifo = "/tmp/fifo123"; 
  
        mkfifo(myfifo, 0666); 


        close (fd_out[1]);
        close(fd_in[0]);         
        while(1){ 
            
            read(fd_out[0], s, BUFSIZ);
            printf("Game said:\n%s \nsending to client...\n", s);
            
            fifo = open(myfifo, O_WRONLY);
            write(fifo, s, BUFSIZ);
            close(fifo);

            fifo = open(myfifo, O_RDONLY);
            read(fifo, s2, BUFSIZ); 
            close(fifo); 

            printf("Client said:\n%s \nsending to game...\n", s2);
            
            write(fd_in[1], s2, BUFSIZ);

            fflush(stdout);

            printf("Sent!\n");
        }
    }

    int status; 
      
    waitpid(pid, &status, 0); 
  
    if ( WIFEXITED(status) ) 
    { 
        int exit_status = WEXITSTATUS(status);   
        printf("Exit status of the child was %d, my pid = %d\n",  
                                     exit_status, getpid()); 
    } 
    return 0; 
}

Program C:(game)

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

//https://stackoverflow.com/questions/54299405/how-to-flush-stdin-without-requiring-user-input
int flush_in(FILE *file)
{
    int ch;
    int flags;
    int fd;

    fd = fileno(file);
    flags = fcntl(fd, F_GETFL, 0);
    if (flags < 0) {
        return -1;
    }
    if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
        return -1;
    }
    do {
        ch = fgetc(file);
    } while (ch != EOF);
    clearerr(file);
    if (fcntl(fd, F_SETFL, flags)) {
        return -1;
    }
    return 0;
}
    
int main(){
    char s[BUFSIZ];
    char aux;
    int c;
    int i = 0;


    while(1){
        

        printf("Say something client!\n");

        fflush(stdout);

        //fgets(s, BUFSIZ, stdin);
        scanf("%s",s);
        
        printf("I received %s from the client!\n", s);

        flush_in(stdin);
        
    }

}

Program D(client)

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <string.h>

int main() 
{ 
    int fd1; 

    char * myfifo = "/tmp/fifo123"; 

    char str1[BUFSIZ];
    char str2[BUFSIZ];
    while (1) 
    { 
        system("clear");
        fd1 = open(myfifo,O_RDONLY); 
        read(fd1, str1, BUFSIZ); 
        close(fd1); 
        printf("The game said:\n%s\n", str1);

        printf("Say something:\n");
        //fgets(str2, BUFSIZ, stdin);
        scanf("%s",str2);

        fd1 = open(myfifo,O_WRONLY); 
        write(fd1, str2, BUFSIZ); 
        close(fd1); 
    } 
    return 0; 
} 

UPDATE: Like @CraigEstey and @thebusybee said my problem was using only one pipe, when i added the second pipe it solved the issue. I now have another problem, which seems to be related to reading from stdin without getting trash, i get no trash from the first 2 or 3 communications but after that fgets only reads trash. Flushing stdin does not seem to solve much. I've updated the codes if anyone wants to help!

Mooshie
  • 1
  • 1
  • Mixing `pipe` and `mkfifo` is usually a bad idea. Pipes/fifos are _unidirectional_. So, you need _two_. The parent can _not_ write to a _single_ fifo [to send data to a child/client] and then open it for reading to get back the response. Likewise for `D`. You might consider a `socket`. – Craig Estey Jan 21 '21 at 23:47
  • And, having `D` do a [second] `mkfifo` [after the parent has already done one], is not necessary [and may even be harmful]. – Craig Estey Jan 21 '21 at 23:55
  • As @CraigEstey mentions, you need to provide **two** pipes in A to communicate with C over the **two** channels `stdin` and `stdout`. – the busybee Jan 22 '21 at 10:16
  • Thank you @CraigEstey for the help! I implemented two pipes and it did work! The only weird thing that is happening is that in the communication from C to A, after the first time, is being doubled for some reason, even though i flush stdin. A quick summary of the communication that is happening right now: C->A->D->A->C->A(A receives doubled data here)->.... This is exactly what i want, the only problem is the doubling, any idea what might be happening? I suspect that it is an error in the fgets from program C. – Mooshie Jan 22 '21 at 19:30
  • Thank you @thebusybee, posted an update on the comments, if you can help me feel free to look at it! :) – Mooshie Jan 22 '21 at 19:31
  • It's difficult to comment on your latest improvements without being able to see them. Don't change the _original_ code--it would make the comments/answers you've received non-sensical. Rather, edit your question and post the updated code at the bottom. That's what most OPs do, particularly if they've made _some_ progress but still need more help. – Craig Estey Jan 22 '21 at 23:21
  • 1
    Or, it's fine to post your own _answer_. This is usually done if nobody posts a full answer and you figured it out on your own. You're a good candidate for that. You could post the answer along with text explaining the remaining issue. When you fix it, edit the answer and post the final code. You can even _accept_ your own answer. SO isn't just for you, but also for others, who may find the question/answer solves _their_ problem. In either case, post a comment to me [to alert me] and I can comment/answer appropriately. – Craig Estey Jan 22 '21 at 23:25

0 Answers0