I am writing a code to interact with an application involving reading and writing to the application. Here is the code: the first one - namely input.c interacts with the second one - namely app.c
//input.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
#include <errno.h>
#define WRITE 1
#define READ 0
void error(char* msg){
perror(msg);
exit(-1);
}
void got_here(char* msg){
printf("Got_here:%s\n",msg);
}
int main(int argc, char** argv, char** envp){
int fd_parent[2];
int fd_child[2]; // for parent and child to write respectively
if(pipe(fd_parent) < 0 | pipe(fd_child) < 0){
error("Fail to create a pipe"); /// just an error-handle function
}
pid_t child = fork();
if(child < 0){
error("Fail to create a child");
}
else if(child == 0){
dup2(fd_child[WRITE], STDOUT_FILENO);
dup2(fd_parent[READ], STDIN_FILENO);
close(fd_parent[WRITE]);
close(fd_child[READ]);
char str[100] = "./app";
execve(str, argv,envp);
close(fd_parent[READ]);
close(fd_child[WRITE]);
return 0;
}
else{
close(fd_parent[READ]);
close(fd_child[WRITE]);
FILE* stream = fdopen(fd_child[READ], "r");
FILE* stream_write = fdopen(fd_parent[WRITE], "w");
char str[20];
char menu[4] = "10\n";
fread(str,sizeof(char), 20, stream); // Here is where the problem lies
got_here("after read"); // it does not get here
fwrite(menu, sizeof(char), 3, stream_write);
fflush(stream_write);
fclose(stream);
fclose(stream_write);
printf("Parent Done\n");
return 0;
}
}
Here is the application code (I only include the main for shorter code):
int main(int argc, char** argv, char** envp){
char str[10];
printf("- Select Menu -1\n");
printf("1. Play Lotto\n");
scanf("%s", str);
return 0;
}
After running, my program just paused at the fread()
line where it is supposed to finish reading and write to the application. The interesting is if I omit either the scanf()
or printf()
in the second program it works fine. I try change the place of the fwrite
and fread
but the problem is still there. I think it is buffer-related problem, but the application I am trying to interact with is not of my permission to change, so I cannot include fflush
or something.
Is my guess right or there is another explanation for this? And how to get over this problem?
You can use stdbuf command to modify the buffering options of the program at the other end of a pipe. In order to do this in C, you can write:
char str[100] = "./app";
char **new_argv = malloc (sizeof (char *) * (argc + 9));
new_argv[0] = "stdbuf";
new_argv[1] = "-i";
new_argv[2] = "0";
new_argv[3] = "-o";
new_argv[4] = "L";
new_argv[5] = "-e";
new_argv[6] = "L";
new_argv[7] = str;
memcpy (&new_argv[8], &argv[1], argc - 1);
new_argv[argc + 8] = NULL;
execvp ("stdbuf", new_argv);
error ("execvp");
or if you don't really need to pass the arguments of the parent on to the child, then:
execlp ("stdbuf", "stdbuf", "-i", "0", "-o", "L", "-e", "L", "./app", NULL);
error ("execlp");
The stdbuf
command uses LD_PRELOAD to load a library (libstdbuf.so
) into the other program. This library does the trick of modifying the buffering options. You can avoid the use of stdbuf
and set the preload options yourself before exec(). You can also write your own library and preload that instead. But using the stdbuf
is probably the easiest option, if you have this command available.
See also stdbuf source code
Here's the full modified example of your code:
//input.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
#include <errno.h>
#define WRITE 1
#define READ 0
void error (char *msg)
{
perror (msg);
exit (-1);
}
void got_here (char *msg)
{
printf ("Got_here: %s\n", msg);
}
int main (int argc, char **argv, char **envp)
{
int fd_parent[2];
int fd_child[2]; // for parent and child to write respectively
pid_t child;
if (pipe (fd_parent) < 0) {
error ("pipe(fd_parent)");
}
if (pipe (fd_child) < 0) {
error ("pipe(fd_child)");
}
child = fork ();
if (child < 0) {
error ("fork");
} else if (child == 0) {
dup2 (fd_child[WRITE], STDOUT_FILENO);
dup2 (fd_parent[READ], STDIN_FILENO);
close (fd_parent[WRITE]);
close (fd_child[READ]);
char str[100] = "./app";
char **new_argv = malloc (sizeof (char *) * (argc + 9));
new_argv[0] = "stdbuf";
new_argv[1] = "-i";
new_argv[2] = "0";
new_argv[3] = "-o";
new_argv[4] = "L";
new_argv[5] = "-e";
new_argv[6] = "L";
new_argv[7] = str;
memcpy (&new_argv[8], &argv[1], argc - 1);
new_argv[argc + 8] = NULL;
execvp ("stdbuf", new_argv);
error ("execvp");
close (fd_parent[READ]);
close (fd_child[WRITE]);
return 0;
} else {
close (fd_parent[READ]);
close (fd_child[WRITE]);
FILE *stream = fdopen (fd_child[READ], "r");
FILE *stream_write = fdopen (fd_parent[WRITE], "w");
char str[20];
char menu[4] = "10\n";
int res = fread (str, sizeof (char), 20, stream); // Here is where the problem lies
printf ("res = %d\n", res);
got_here ("after read"); // it does not get here
fwrite (menu, sizeof (char), 3, stream_write);
fflush (stream_write);
fclose (stream);
fclose (stream_write);
printf ("Parent Done\n");
return 0;
}
}