I have a C program that should emulate the same thing as calling:
popen("ls | grep som")
Right now I have two processes that each execute one part of this command and the firsts' output is the second ones' input. When I execute the program I see the correct line being prompted in the terminal but I can't seem to save the output to a string. I always end up with the first thing ls command prints out.
Example:
if ls prints out:
one
two
three
the string is always equal to "one".
This is what the code looks like:
int fd[2];
pipe(fd);
pid_t pid1, pid2;
FILE *f;
int pid1 = fork();
if (pid1 == 0) {
dup2(fd[1], STDOUT_FILENO);
close(fd[1]);
close(fd[0]);
execlp("ls", "ls", NULL);
}
int pid2 = fork();
if (pid2 == 0) {
dup2(fd[0], STDIN_FILENO);
close(fd[1]);
close(fd[0]);
execlp("grep", "grep", "som",NULL);
}
f = fdopen(fd[0] ,"r");
// then I read the output with snprintf
//and once again I close fd's
close(fd[0]);
close(fd[1]);
waitpid(// first process)
waitpid(// second process)
Add another pipe for grep's stdout and read from it from you main process.
Related
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc, char *argv[])
{
int fd[2];
pid_t pid;
if (pipe(fd) == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}
pid = fork();
if (pid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (pid == 0) {
dup2(fd[1], STDOUT_FILENO);
close(fd[0]);
close(fd[1]);
execlp("ls", "ls", NULL);
perror("execlp");
exit(EXIT_FAILURE);
} else {
dup2(fd[0], STDIN_FILENO);
close(fd[0]);
close(fd[1]);
execlp("more", "more", "-3", NULL);
perror("execlp");
exit(EXIT_FAILURE);
}
}
I dont understand, why does my program when we say if (pid == 0) works like intended where more program display 3 lines and that again 3 lines after the space and so on, and when i say if (pid!=0) ls display everything and more command doesnt work? Why is this?
Why does it matter if parent process does ls and child more command or parent does more and child ls? Either way more wont print anything until ls writes data into the pipe.
Here is the code i tried versus the one above, i tried having parent process calling "ls" command and child process call "more" command. I don't see why this would be an issue? I just changed "responsobilities", i don't see how this changes anything? child process with "more" command comes first, it will attempt to read pipe and it will block since it's empty, parent process with "ls" command will eventually write into the pipe and the child process with "more" will get unblocked, read from the pipe and print it out on the user screen. So i don't understand why is it only displaying the contents of the directory, instead of showing me three piece's of content and then after i click space another three, and so on..
if (pid != 0) {
dup2(fd[1], STDOUT_FILENO);
close(fd[0]);
close(fd[1]);
execlp("ls", "ls", NULL);
perror("execlp");
exit(EXIT_FAILURE);
}
else {
dup2(fd[0], STDIN_FILENO);
close(fd[0]);
close(fd[1]);
execlp("more", "more", "-3", NULL);
perror("execlp");
exit(EXIT_FAILURE);
}
I'd like to know if alternating pipes is possible in C.
The following is the code I thought about, minus error checking and the program setter.
As you can see I only use two pipes in alternating order, which is basically the question I'm asking: Would this be possible or do I have to create a new pipe when the pipes before have been used? (In this example you could remove the fourth fork and let it end in the third fork which would work flawless from my experience).
int main(int argc, char *argv[]) {
int pip[2], pop[2];
pid_t pid1, pid2, pid3, pid4;
pipe(pip);
pipe(pop);
//Program execute
pid1 = fork();
if(pid1 == 0) {
dup2(pip[WRITE], STDOUT_FILENO);
//close all ends here
execvp(prog1A[0], prog1A);
}
pid2 = fork();
if(pid2 == 0) {
dup2(pip[READ], STDIN_FILENO);
dup2(pop[WRITE], STDOUT_FILENO);
//close all ends here
execvp(prog2A[0], prog2A);
}
pid3 = fork();
if(pid3 == 0) {
dup2(pop[READ], STDIN_FILENO);
dup2(pip[WRITE], STDOUT_FILENO);
//close all ends here
execvp(prog3A[0], prog3A);
}
pid4 = fork();
if(pid4 == 0) {
dup2(pip[READ], STDIN_FILENO);
//close all ends here
execvp(prog4A[0], prog4A);
}
//close all ends here
//wait for all pids here
}
Would this be possible in C?
You need separate pipes. The way you wrote it, both pid2 and pid4 would have the same pipe connected to their STDIN and steal the incoming byted from each other. Similar of the writing end, several processes writing into the same pipe will get their outputs coming out (mixed) on the reading end.
Here is my pipe to pipe function:
int *pip is the pipe that I want to read from
char **cmd is the command that I want to execute
char **envp points to the environment variables
I want to take the pipe (pip) as STDIN with dup2
and take the new pipe (fd) as STDOUT also with dup2
The problem is that when it comes to execve the command is executed but the program is stuck in an infinite loop
int *pipe_to_pipe(int *pip, char **cmd, char **envp) {
int *fd;
int pid;
fd = (int *)malloc(sizeof(int) * 2);
if (!fd || pipe(fd) == -1)
error(1);
pid = fork();
if (pid == -1)
error(1);
if (pid == 0)
{
dup2(pip[0], STDIN_FILENO);
dup2(fd[1], STDOUT_FILENO);
close(fd[0]);
close(pip[1]);
execve((command_path), cmd, envp);
exit(0);
}
wait(NULL);
free(pip);
return (fd);
}
As an example, if I have "hello world" in my int *pip
as a char **cmd I have cmd[0] = "cat", cmd[1] = "-e".
When I make execve(path, cmd, envp);
the program cat "hello world" and infinite loop here.
How can I fix this?
The problem comes from the fact that I didn't close the pipe "pip" in the parent process adding those lines that would be executed by the parent process fix everything
if (pid != 0){
close(pip[0]);
close(pip[1]);
wait(NULL);
}
Otherwise it fixes my problem I don't understand why if someone can explain to me I will be gratefull
Thanks to everyone that answered me <3
I am working to make a shell like bash, but i have trouble solving heredoc << so i made a test code as simple as possible for this question.
void pipeline()
{
int i = 0;
int fd[2];
pid_t pid;
int fdd = 0;
while (i < 2)
{
pipe(fd);
pid = fork();
if (pid == 0)
{
//dup2(fd[1],1); if i dup in the first pipe cat dont finalize
if (i == 0)
dup2(fd[0],0);
write(fd[1], "hello\nhow\nare\nyou\n", 17);
close(fd[0]);
close(fd[1]);
dup2(fdd, 0);
if (i == 0)
execlp("cat", "cat", NULL);
else
execlp("grep", "grep", "you" , NULL);
perror("error");
exit(1);
}
else
{
close(fd[1]);
fdd = fd[0];
wait(NULL);
i++;
}
}
}
int main(int *argc, char **argv, char **env)
{
pipeline();
}
I know that cat and grep need an EOF to run; what I'm doing is writing in stdin and running cat, but my question is: how do I save stdout for grep without duping stdout on the first pipe?
If I dup on dup2(fd[1],1) cat does not work in the first pipe, could someone help me out to make this code work? And make it as similar to bash heredoc as well if possible.
how do I save stdout for grep without duping stdout on the first pipe?
I'd rearrange the creation of the child processes from rightmost to leftmost - then grep is created first and can output to the initial output descriptor. A necessary change is to run all child processes before waiting on one as well as before writing, so that there's no deadlock even if the pipe buffer wouldn't suffice for the heredoc.
void pipeline()
{
int i = 2; // create children from last to first
int fd[2];
pid_t pid;
int fdd = 1; // output of last child is STDOUT
while (i--)
{
pipe(fd);
pid = fork();
if (pid == 0)
{
dup2(fdd, 1); // child's output
dup2(fd[0], 0);
close(fd[0]);
close(fd[1]);
if (i == 0)
execlp("cat", "cat", "-A", NULL);
else
execlp("grep", "grep", "you" , NULL);
perror("error");
exit(1);
}
if (fdd != 1) close(fdd); // close if a pipe write end
fdd = fd[1]; // preceding child's output is pipe write end
close(fd[0]);
}
write(fd[1], "hello\nhow\nare\nyou\n", 17);
close(fd[1]); // signal EOF to child
while (wait(NULL) > 0) ; // wait for all children
}
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
}