I am trying to write a test program that uses pipes to pass information between 3 linux commands. The bash equivalent of "ls | wc | wc". Below is my code
The only output i am getting is . The program is stuck there without exiting.
./a.out
starting main
creating pipe first
The expected output in bash is something like
ls | wc | wc
1 3 24
Edit:On running strace i could see that main process is on wait4, the two wc processes are stuck on read(0). As i guessed, its because wc is not getting the EOF. Why is this so?.Can someone help me to identify the issue?
#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
int p[2];
int p1[2];
int r=0;
int fork1(void)
{
int pid;
pid = fork();
if(pid == -1) {
perror("fork");
exit(1);
}
return pid;
}
int main (int argc, char const* argv[])
{
printf("starting main\n");
printf("creating pipe first\n");
if(pipe(p)<0) {
perror("pipe");
exit(1);
}
if(pipe(p1)<0) {
perror("pipe");
exit(1);
}
if(fork1()==0) {
//send output to p
close(1);
dup(p[1]);
close(p[0]); close(p[1]);
execlp("ls","ls",NULL);
exit(0);
}
if(fork1() == 0) {
close(1);//write to p1
dup(p1[1]);
close(p1[1]); close(p1[0]);
close(0);//read from p
dup(p[0]);
close(p[0]); close(p[1]);
execlp("wc","wc",NULL);
exit(0);
}
if(fork1() == 0)
{
close(0);//read from p1
dup(p1[0]);
close(p1[0]); close(p1[1]);
execlp("wc","wc",NULL);
exit(0);
}
close(p[0]); close(p[1]); close(p1[0]); close(p1[1]);
wait(&r);wait(&r);wait(&r);
printf("parent done\n");
return 0;
}
The problem was that i was not closing all the pipes in each branch.
I had to close p,p1 in all the three forks and the parent. Once i added those, it was working
Related
For my piping function, I am creating two child processes with fork() and attempting to pipe the contents of the first over to the second child process before calling execvp(). I am doing something wrong, as if I do ls -la | more, the output is a vertical list of the files in the directory. I am very new to piping in c (and c) so chances are that it is an overlooked mistake. Any help is appreciated!
void handle_pipe_cmds(char **args1, char **args2){
char ** word1 = parse_space(*args1);
char ** word2 = parse_space(*args2);
int p[2];
pipe(p);
pid_t pid = fork();
if (pid == -1) {
printf("CANNOT FORK!");
} else if (pid == 0) {
dup2(p[1], STDOUT_FILENO);
close(p[1]);
int status = execvp(word1[0], word1);
if (status != 0) {
printf("%s failed\n", word1[0]);
exit(1);
}
exit(0);
} else {
close(p[1]);
wait(NULL);
}
pid = fork();
if (pid == -1) {
printf("CANNOT FORK!");
} else if (pid == 0) {
dup2(p[0], STDIN_FILENO);
close(p[1]);
close(p[0]);
int status = execvp(word2[0], word2);
if (status != 0) {
printf("%s failed\n", word2[0]);
exit(1);
}
exit(0);
} else {
close(p[1]);
close(p[0]);
wait(NULL);
}
}
If I do ls -la | more, the output is a vertical list of the files in the directory.
That is because the ls program is aware of whether its output stream is a terminal or not, and acts different in both cases. On a terminal, it (may) print colorized entries, and multiple items per line; to a pipe, or a file, it will print just one entry per line with no color. See man ls for details.
I need to create a C program to simulate this command using exec and unnamed pipes:
ls | sort | wc -l
But how can i do this? I have only begin to study pipes and I have tried really bad
int main( int argc, char** argv )
{
int fd1[2], fd2[2],fd3[2], pid;
createPipe(fd1);
createPipe(fd2);
createPipe(fd3);
pid=babyMaker();
if (pid == 0)
{
dup2(fd2[0],0);
dup2(fd3[1],1);
execlp("wc","wc","-l",NULL);
}
pid=babyMaker();
if (pid == 0)
{
dup2(fd2[1], 1);
dup2(fd1[0], STDIN_FILENO);
execlp("sort", "sort", NULL);
}
pid=babyMaker();
if (pid == 0)
{
dup2(fd1[1], 1);
execlp("ls", "ls", "-la", NULL);
}
char string[BUFFER_SIZE];
int bytesReaded=read(fd3[0],string,BUFFER_SIZE);
string[bytesReaded-1]=0;
printf("%s\n",string);
wait(NULL);
return 0;
}
EDIT: Added my code
You need to close the unused file descriptors in each operation using:
close(fd[0]);
I'm trying to make a program that executes the following commands connecting the output of one to the input of the next using pipes and taking two arguments DIR (directory) and ARG (filetype, example: jpg).
ls DIR -laR | grep ARG | sort
Here's the code:
int main(int argc, char *argv[]) {
if (argc != 3) {
printf("Invalid arguments. <dir> <arg>\n");
exit(1);
}
int pipe_fd1[2];
int pipe_fd2[2];
pid_t ls_pid, grep_pid;
int status;
pipe(pipe_fd1);
pipe(pipe_fd2);
ls_pid = fork();
if (ls_pid == 0) { //first child ls DIR -laR
dup2(pipe_fd1[1], STDOUT_FILENO);
close(pipe_fd1[0]);
execlp("ls", "ls", argv[1], "-laR", NULL);
} else if (ls_pid > 0) {
grep_pid = fork();
if (grep_pid == 0) { //second child grep ARG
dup2(pipe_fd1[0], STDIN_FILENO);
dup2(pipe_fd2[1], STDOUT_FILENO);
close(pipe_fd1[1]);
close(pipe_fd2[0]);
waitpid(ls_pid, &status, 0);
execlp("grep", "grep", argv[2], NULL);
} else if (grep_pid > 0) { //parent sort
dup2(pipe_fd2[0], STDIN_FILENO);
close(pipe_fd2[1]);
waitpid(grep_pid, &status, 0);
execlp("sort", "sort", NULL);
}
}
return 0;
}
It seems to be stuck? Not sure why?
You never close pipe_fd1 on the parent, so grep and sort doen't know when to stop reading input: because the pipe read and write ends are never closed on the parent, the reader blocks waiting for more input that will never arrive. You need to close it.
Also, you don't need waitpid(): the way pipes work ensures that input flows linearly and in order throughout the pipe.
Here's the working version with these issues addressed:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc, char *argv[]) {
if (argc != 3) {
printf("Invalid arguments. <dir> <arg>\n");
exit(1);
}
int pipe_fd1[2];
int pipe_fd2[2];
pid_t ls_pid, grep_pid;
pipe(pipe_fd1);
ls_pid = fork();
if (ls_pid == 0) { //first child ls DIR -laR
dup2(pipe_fd1[1], STDOUT_FILENO);
close(pipe_fd1[0]);
execlp("ls", "ls", argv[1], "-laR", NULL);
} else if (ls_pid > 0) {
dup2(pipe_fd1[0], STDIN_FILENO);
close(pipe_fd1[1]);
pipe(pipe_fd2);
grep_pid = fork();
if (grep_pid == 0) { //second child grep ARG
dup2(pipe_fd2[1], STDOUT_FILENO);
close(pipe_fd2[0]);
execlp("grep", "grep", argv[2], NULL);
} else if (grep_pid > 0) { //parent sort
dup2(pipe_fd2[0], STDIN_FILENO);
close(pipe_fd2[1]);
execlp("sort", "sort", NULL);
}
}
return 0;
}
I'm trying to make program on C, which execute console shell command
cat log.txt| awk '{ print $7 }' | head -10
but the third command won't work with 2 present.
Here's what i done
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
int fd[2], status;
pipe(fd);
pid_t pid1 = fork();
if (!pid1)
{
dup2(fd[1], 1);
close(fd[0]);
close(fd[1]);
char* command[3] = {"/bin/cat", "log.txt", 0};
execvp(command[0], command);
exit(EXIT_FAILURE);
}
else if (pid1 == -1)
{
fprintf(stderr, "Can't fork, exiting...\n");
exit(EXIT_FAILURE);
}
pid_t pid2 = fork();
if (!pid2) {
dup2(fd[0], 0);
close(fd[0]);
close(fd[1]);
char* command[3] = {"awk", "{ print $7 }", 0};
execvp(command[0], command);
exit(EXIT_FAILURE);
} else if (pid2 == -1) {
fprintf(stderr, "Can't fork, exiting...\n");
exit(EXIT_FAILURE);
}
pid_t pid3 = fork();
if (!pid3) {
dup2(fd[0], 0);
close(fd[0]);
close(fd[1]);
char* command[3] = {"head", "-10", 0};
execvp(command[0], command);
exit(EXIT_FAILURE);
} else if (pid3 == -1) {
fprintf(stderr, "Can't fork, exiting...\n");
exit(EXIT_FAILURE);
}
close(fd[0]);
close(fd[1]);
waitpid(pid1, NULL, 0);
waitpid(pid2, NULL, 0);
waitpid(pid3, &status, 0);
exit(status);
return 0;
}
pid3 can't execute. I tried to make dup2(fd[1], 1) in pid3, but thats doesn't work. What should be on pid3 to make it's work and how to make more than 3 commands using dup2?
You have created one pipe. One pipe has two ends. Two ends are enough for two processes. If you have three processes all in a single pipeline, you need two pipes. The process in the middle holds on two pipes and two other processes hold on the remaining ends.
Look at this picture:
cat | awk | head
See two pipe symbols? They are the two pipes you need.
You will have to set up two pipes-- one to connect cat to awk and one to connect awk to head.
Also, don't close file descriptors that you actually need (such as fd[0] in your first fork!)
I am implementing a shell in C. This is the function i use for piping. When i put "ls | a" in the code (i.e. pipe a valid command with invalid one),It doesnt exit the child process like it should. How do i make it go back to main function?
same thing happens when i do ps | ls or ps | pwd etc. but ls | ps works the same as in bash. i know ls | ps or ps | ls dont make sense but atleast they should give same output as bash.
void exec3(char **args, char **args2){
int fd[2];
pid_t pid,pid1;
int status;
pipe(fd);
int e=0;
if ((pid = fork()) < 0) {
printf("*** ERROR: forking child process failed\n");
exit(1);
}
else if ((pid1 = fork()) < 0) {
printf("*** ERROR: forking child process failed\n");
exit(1);
}
else if (pid == 0 && pid1!=0){
printf("in 1\n");
close(1);
dup(fd[1]);
close(fd[0]);
close(fd[1]);
if(execvp(args[0],args)<0){
printf("**error in exec");
close(fd[0]);
close(fd[1]);
exit(1);
}
//printf("exiting 1\n");
exit(0);
}
else if (pid1 == 0 && pid!=0) {
printf("in 2\n");
close(0);
dup(fd[0]);
close(fd[1]);
close(fd[0]);
if((e=execvp(args2[0],args2))<0){
printf("**error in exec2 ");
close(fd[0]);
close(fd[1]);
exit(1);
}
exit(0);
}
else {
close(fd[0]);
close(fd[1]);
fflush(stdout) ;
while (wait(&status) != pid);
while (wait(&status) != pid1);
}
}
You are close to the solution. Look at how popen() is implemented, that is what you are trying to do.