I want to simulate bash in my Linux C program using pipes and execvp function. e.g
ls -l | wc -l
There is my program:
if(pipe(des_p) == -1) {perror("Failed to create pipe");}
if(fork() == 0) { //first fork
close(1); //closing stdout
dup(des_p[1]); //replacing stdout with pipe write
close(des_p[0]); //closing pipe read
close(des_p[1]); //closing pipe write
if(execvp(bash_args[0], bash_args)) // contains ls -l
/* error checking */
}
else {
if(fork() == 0) { //creating 2nd child
close(0); //closing stdin
dup(des_p[0]); //replacing stdin with pipe read
close(des_p[1]); //closing pipe write
close(des_p[0]); //closing pipe read
if(execvp(bash_args[another_place], bash_args)) //contains wc -l
/* error checking */
}
close(des_p[0]);
close(des_p[1]);
wait(0);
wait(0);
}
This code actually runs, but doesn't do the right thing.
What's wrong with this code? That's not working and I don't have a clue why.
You need to close the pipe fds in the parent, or the child won't receive EOF, because the pipe's still open for writing in the parent. This would cause the second wait() to hang. Works for me:
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char** argv)
{
int des_p[2];
if(pipe(des_p) == -1) {
perror("Pipe failed");
exit(1);
}
if(fork() == 0) //first fork
{
close(STDOUT_FILENO); //closing stdout
dup(des_p[1]); //replacing stdout with pipe write
close(des_p[0]); //closing pipe read
close(des_p[1]);
const char* prog1[] = { "ls", "-l", 0};
execvp(prog1[0], prog1);
perror("execvp of ls failed");
exit(1);
}
if(fork() == 0) //creating 2nd child
{
close(STDIN_FILENO); //closing stdin
dup(des_p[0]); //replacing stdin with pipe read
close(des_p[1]); //closing pipe write
close(des_p[0]);
const char* prog2[] = { "wc", "-l", 0};
execvp(prog2[0], prog2);
perror("execvp of wc failed");
exit(1);
}
close(des_p[0]);
close(des_p[1]);
wait(0);
wait(0);
return 0;
}
Read up on what the wait function does. It will wait until one child process exists. You're waiting for the first child to exit before you start the second child. The first child probably won't exit until there's some process that reads from the other end of the pipe.
Related
I've been trying to write a really simple program in which the parent process passes 100 lines to a child process through a pipe. The child should then use the generated lines and execute the command line program more over those lines.
However, when I try to run the program, it just freezes. I was careful to close all descriptors not being used by both processes but I don't really understand what may be causing it.
Code:
int main(void){
int fd[2];
if (pipe(fd) == -1){
perror("Error creating pipe");
return 1;
}
dup2(fd[1], STDOUT_FILENO);
int i;
for (i = 1; i <= 100; i++){
printf("Line %d\n", i);
}
close(fd[1]);
pid_t pid = fork();
if(pid == 0) {
dup2(fd[0], STDIN_FILENO);
close(fd[0]);
execlp("more", "more",(char*) NULL);
fprintf(stderr, "Failed to execute 'more'\n");
exit(1);
}
wait(NULL);
return 0;
}
I was careful to close all descriptors not being used by both processes
Not really.
dup2(fd[1], STDOUT_FILENO);
Here you make stdout a copy of fd[1].
close(fd[1]);
Here you close fd[1], but stdout is still open.
Then you fork. At this point both processes have access to the write end of the pipe via stdout.
dup2(fd[0], STDIN_FILENO);
close(fd[0]);
In the child process you copy fd[0] to stdin and close fd[0].
Then, when you exec more, it still has access to both ends of the pipe (via stdin / stdout).
At the same time your parent process has access to both ends of the pipe (via fd[0] / stdout).
In effect you've closed nothing.
There's a second issue: Your parent process writes to stdout, which is bound to the write end of the pipe, without anyone reading it. Depending on how much you write, whether stdout is line buffered or block buffered, how big the stdout buffer is, and how much your pipe itself can store, this itself can deadlock. If the pipe runs full and there's no one around to read from it, printf will just block.
To fix this, don't dup2 in the parent process and don't write to the pipe before the child process has started.
int main(void){
int fd[2];
if (pipe(fd) == -1){
perror("Error creating pipe");
return 1;
}
pid_t pid = fork();
if (pid == -1) {
perror("Error spawning process");
return 2;
}
if (pid == 0) {
close(fd[1]); /* close write end of the pipe in the child */
dup2(fd[0], STDIN_FILENO);
close(fd[0]);
execlp("more", "more", (char*)NULL);
fprintf(stderr, "Failed to execute 'more'\n");
exit(1);
}
close(fd[0]); /* close read end of the pipe in the parent */
FILE *fp = fdopen(fd[1], "w");
if (!fp) {
perror("Error opening file handle");
return 3;
}
for (int i = 1; i <= 100; i++){
fprintf(fp, "Line %d\n", i);
}
fclose(fp); /* flush and close write end of the pipe in the parent */
wait(NULL);
return 0;
}
I have a follow-up question to this one: Classic C. Using pipes in execvp function, stdin and stdout redirection
I need to do the same, but with more than two commands.
If I execute ls | head | wc it doesnt work.
My approach is, that all the commands between the first and the last one need to get input from the pipe and output it to the pipe.
My code looks like this:
if(i != 0 && i < notoken - 1) {
close(STDOUT_FILENO); //closing stdout
dup(pipefd[1]); //replacing stdout with pipe write
close(STDIN_FILENO); //closing stdin
dup(pipefd[0]); //replacing stdin with pipe read
close(pipefd[0]); //closing pipe read
close(pipefd[1]);
argv[0] = token[i];
argv[1] = NULL;
execvp(argv[0], argv);
perror("failed");
exit(1);
} else if(i == 0 && notoken > 1) {
if(fork() == 0) { //first fork
close(STDOUT_FILENO); //closing stdout
dup(pipefd[1]); //replacing stdout with pipe write
close(pipefd[0]); //closing pipe read
close(pipefd[1]);
argv[0] = token[i];
argv[1] = NULL;
execvp(argv[0], argv);
perror("failed");
exit(1);
}
} else {
if(fork() ==0) {
close(STDIN_FILENO); //closing stdin
dup(pipefd[0]); //replacing stdin with pipe read
close(pipefd[1]); //closing pipe write
close(pipefd[0]);
argv[0] = token[i];
argv[1] = NULL;
execvp(argv[0], argv);
perror("failed");
exit(1);
}
}
I've been trying to write a program that will send and receive commands to a bash shell (/bin/sh). Like a wrapper program around a bash shell. So, I could write to stdin "cd ~/Desktop", then write again "ls" and I will receive a listing of the files on the desktop. I can't get it working though. On the second write command in this code, it will echo back whatever I wrote to stdin. I've also tried using popen() but that only provides output, not allowing me to write to stdin. Could someone please help solve this problem? Thanks
void main()
{
// Create a pipe and fork
//
int fd[2];
int p = pipe(fd);
pid_t pid = fork();
if (pid > 0)
{
// Read from the pipe and output the result
//
//close(fd[1]);
char buf[1024] = { 0 };
read(fd[0], buf, sizeof(buf));
printf("1 - %s\n", buf);
write (fd[1], "ifconfig", strlen ("ifconfig") );
// problem is here, read is returning echo'd bytes from write()
read(fd[0], buf, sizeof(buf));
printf("2 - %s\n", buf);
// Wait for child to terminate
int status;
wait(&status);
}
else if (pid == 0)
{
// Redirect stdout and stderr to the pipe and execute the shell
// command
//
dup2(fd[0], STDIN_FILENO);
dup2(fd[1], STDOUT_FILENO);
dup2(fd[1], STDERR_FILENO);
//close(fd[0]);
execl("/bin/sh", "exec sh", "-c", "ls", (char*) NULL );
}
}
EDIT - Updated code per 1st answer, now there is no output from the 2nd read() call
void main()
{
// Create a pipe and fork
//
int fd[2];
int ChildToParent[2], ParentToChild[2];
pipe (ParentToChild);
pipe (ChildToParent);
pid_t pid = fork();
if (pid > 0)
{
// In parent process
// Read the output of the child from child_to_parent[0]
// We don't need child_to_parent[1] so close it
close(ChildToParent[1]);
// Write output to the child using parent_to_child[1]
// We don't need parent_to_child[0] so close it
close(ParentToChild[0]);
// Read from and write to the child process...
char buf[1024] = { 0 };
read(ChildToParent[0], buf, sizeof(buf));
printf("1 - %s\n", buf);
write(ParentToChild[1], "whoami", strlen ("whoami") );
memset (buf, 0, 1024);
// this call to read returns nothing
read(ChildToParent[0], buf, sizeof(buf));
printf("2 - %s\n", buf);
}
else if (pid == 0)
{
// Redirect stdout and stderr to the pipe and execute the shell
// command
//
// child_to_parent[1] is were we write output, it's the
// new standard output, child_to_parent[0] can be closed
dup2 (ChildToParent[1], STDOUT_FILENO);
close(ChildToParent[0]);
// parent_to_child[0] is where we read input from, it's the
// new standard input, parent_to_child[1] can be closed
dup2 (ParentToChild[0], STDIN_FILENO);
close(ParentToChild[1]);
//close(fd[0]);
execl("/bin/sh", "exec sh", "-c", "ls", (char*) NULL );
}
}
Remember that pipes are a one-way communication stream. You can't use it for two-way communication between two processes. For that you need two pipes, one in each direction.
Perhaps something like this simple example:
// Pipe for the child process to write to the parent process
int child_to_parent[2];
// Pipe for the parent process to write to the child process
int parent_to_child[2];
// Create the TWO pipes
pipe(child_to_parent);
pipe(parent_to_child);
pid_t pid = fork();
if (pid > 0)
{
// In parent process
// Read the output of the child from child_to_parent[0]
// We don't need child_to_parent[1] so close it
close(child_to_parent[1]);
// Write output to the child using parent_to_child[1]
// We don't need parent_to_child[0] so close it
close(parent_to_child[0]);
// Read from and write to the child process...
}
else if (pid == 0)
{
// In child process
// child_to_parent[1] is were we write output, it's the
// new standard output, child_to_parent[0] can be closed
dup2(child_to_parent[1], STDOUT_FILENO);
close(child_to_parent[0]);
// parent_to_child[0] is where we read input from, it's the
// new standard input, parent_to_child[1] can be closed
dup2(parent_to_child[0], STDIN_FILENO);
close(parent_to_child[1]);
// Do whatever the child is supposed to do
}
If I create a process with fork(), such as grep, how do I pass it data to process? When I use write I get the error $ grep: (standard input): Bad file descriptor;
Command I'm running ps aux | grep notepad
child = fork();
//C1 execute first line of command line
if(child == 0)
{
close(1); //close stdout
dup(pfds[1]); // make stdout pfds[1]
close(pfds[0]);
//execute the args
execvp(args[0], args);
perror("FAILED TO EXECUTE!!!");
exit(-1);
}
//Parent assume execution control
else
{
int in = dup(0); //duplicate in
int out = dup(1); //duplicate out
//Close the parents in and redirect to pipe
close(0);
dup(pfds[0]);
close(pfds[1]);
waitpid(pid, NULL, 0); // wait for child to die
read(pfds[0], pbuff, P_BUFSIZE); //read from pipe
//Close the parents out and redirect to pipe
close(1);
dup(pfds[1]);
close(pfds[0]);
write(pfds[0], pbuff, sizeof(pbuff)-1);
pid = fork();
//C2 process use the data of the old C1 process
if(pid == 0)
{
//Close childs in and redirect to pipe;
close(0);
dup(pfds[0]);
execvp(args2[0], args2);
perror("Execution failure);
exit(-1)
}
I want to simulate bash in my Linux C program using pipes and execvp function. e.g
ls -l | wc -l
There is my program:
if(pipe(des_p) == -1) {perror("Failed to create pipe");}
if(fork() == 0) { //first fork
close(1); //closing stdout
dup(des_p[1]); //replacing stdout with pipe write
close(des_p[0]); //closing pipe read
close(des_p[1]); //closing pipe write
if(execvp(bash_args[0], bash_args)) // contains ls -l
/* error checking */
}
else {
if(fork() == 0) { //creating 2nd child
close(0); //closing stdin
dup(des_p[0]); //replacing stdin with pipe read
close(des_p[1]); //closing pipe write
close(des_p[0]); //closing pipe read
if(execvp(bash_args[another_place], bash_args)) //contains wc -l
/* error checking */
}
close(des_p[0]);
close(des_p[1]);
wait(0);
wait(0);
}
This code actually runs, but doesn't do the right thing.
What's wrong with this code? That's not working and I don't have a clue why.
You need to close the pipe fds in the parent, or the child won't receive EOF, because the pipe's still open for writing in the parent. This would cause the second wait() to hang. Works for me:
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char** argv)
{
int des_p[2];
if(pipe(des_p) == -1) {
perror("Pipe failed");
exit(1);
}
if(fork() == 0) //first fork
{
close(STDOUT_FILENO); //closing stdout
dup(des_p[1]); //replacing stdout with pipe write
close(des_p[0]); //closing pipe read
close(des_p[1]);
const char* prog1[] = { "ls", "-l", 0};
execvp(prog1[0], prog1);
perror("execvp of ls failed");
exit(1);
}
if(fork() == 0) //creating 2nd child
{
close(STDIN_FILENO); //closing stdin
dup(des_p[0]); //replacing stdin with pipe read
close(des_p[1]); //closing pipe write
close(des_p[0]);
const char* prog2[] = { "wc", "-l", 0};
execvp(prog2[0], prog2);
perror("execvp of wc failed");
exit(1);
}
close(des_p[0]);
close(des_p[1]);
wait(0);
wait(0);
return 0;
}
Read up on what the wait function does. It will wait until one child process exists. You're waiting for the first child to exit before you start the second child. The first child probably won't exit until there's some process that reads from the other end of the pipe.