Stuck on using multiple pipes in C - c

I'm writing a program that just chains together specifically 3 programs, etc "ls | sort | wc".
I looked through the other posts I found on google about multiple pipes but I still can't figure out what I've been doing wrong. Been stuck on this for a while. I believe my program is logically correct, but for some reason it doesn't run. What am I missing?
pipe(pipe1);
pipe(pipe2);
pid = fork();
if(pid > 0){
close(pipe2[1]);
dup2(pipe2[0], 0);
execlp(argv[3], argv[3], NULL);
}
else if(pid == 0){
pid2 = fork();
if(pid2 == 0){
close(pipe1[0]);
dup2(pipe1[1], 1);
execlp(argv[1], argv[1], NULL);
}
else if(pid2 > 0){
close(pipe1[1]);
dup2(pipe1[0], 0);
close(pipe2[0]);
dup2(pipe2[1], 1);
execlp(argv[2], argv[2], NULL);
}
}

You need to fork three times each time in the parent cases, and execute your commands only inside the child cases. Take a look here
Adjusted to your case:
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
int main(int argc, char **argv) {
int pid;
int pipe1[2];
int pipe2[2];
// create pipe1
if (pipe(pipe1) == -1) {
perror("bad pipe1");
exit(1);
}
// fork (ps aux)
if ((pid = fork()) == -1) {
perror("bad fork1");
exit(1);
}
else if (pid == 0) {
// stdin --> ps --> pipe1
// input from stdin (already done), output to pipe1
dup2(pipe1[1], 1);
// close fds
close(pipe1[0]);
close(pipe1[1]);
execlp(argv[1], argv[1], NULL);
// exec didn't work, exit
perror("bad exec ps");
_exit(1);
}
// parent
// create pipe2
if (pipe(pipe2) == -1) {
perror("bad pipe2");
exit(1);
}
// fork (grep root)
if ((pid = fork()) == -1) {
perror("bad fork2");
exit(1);
}
else if (pid == 0) {
// pipe1 --> grep --> pipe2
// input from pipe1
dup2(pipe1[0], 0);
// output to pipe2
dup2(pipe2[1], 1);
// close fds
close(pipe1[0]);
close(pipe1[1]);
close(pipe2[0]);
close(pipe2[1]);
execlp(argv[2], argv[2], NULL);
// exec didn't work, exit
perror("bad exec grep root");
_exit(1);
}
// parent
// close unused fds
close(pipe1[0]);
close(pipe1[1]);
// fork (grep sbin)
if ((pid = fork()) == -1) {
perror("bad fork3");
exit(1);
}
else if (pid == 0) {
// pipe2 --> grep --> stdout
// input from pipe2
dup2(pipe2[0], 0);
// output to stdout (already done). Close fds
close(pipe2[0]);
close(pipe2[1]);
execlp(argv[3], argv[3], NULL);
// exec didn't work, exit
perror("bad exec grep sbin");
_exit(1);
}
// parent
return 0;
}

I find another solution, the parent process treats the first command, write its output in the first pipe and create a child which will treat the second command and write its outputin the second pipe, meanwhile this child will create a sub-child which will treat the third command.
the command is : ls -l | grep "sthg" `| wc -l
#include<sys/types.h>
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include <fcntl.h>
int main(){
int p[2],p1[2];
int x,y,z;
pipe(p);
x= fork();
if (x>0){//père or parent
close(p[0]);
dup2(p[1],1);
execlp("ls","ls","-l",NULL);
close(p[1]);
}
else{ // fils du père or child of parent
pipe(p1);
z=fork();
if(z>0){ //fils du père or child of parent
close(p[1]);
close(p1[0]);
dup2(p[0],0);
dup2(p1[1],1);
execlp("grep","grep","sthg",NULL);
close(p[0]);
close(p1[1]);
}
else{ //fils du fils or sub-child
close(p[0]);
close(p[1]);
close(p1[1]);
dup2(p1[0],0);
execlp("wc","wc","-l",NULL);
close(p1[0]);
}
}
return 0;
}

Related

C program to pipe multiple commands

I have written the below method to fork and execute commands separated by multiple pipes( test with : ls -lrt | grep "check" | wc -l . However it is not resulting in any output, could any one please spot my mistake. Thanks.
void execCmd (pInfo *info)
{
int i, j, k, m;
struct comType *comm, *comm1, *comm2;
if(info->noOfPipes > 2)
{
// DOES NOT WORK
printf("Start\n");
comm=&(info->cArr[0]);
comm2=&(info->cArr[(info->ppNum)-1]);
int fds[2];
pipe(fds);
pid_t pid = fork();
if(pid == -1)
{
perror("fork failed");
exit(1);
}
if(pid == 0)
{
printf("1st child execution here\n");
close(fds[0]);
dup2(fds[1], STDOUT_FILENO);
close(fds[1]);
execvp(comm->cmd,comm->parms);
}
for (k=1;k<=((info->ppNum)-1);k++)
{
printf("For loop executionn number %d",k);
comm1=&(info->cArr[k]);
printf ("comm 1 : %s\n",comm1->cmd);
pid = fork();
if(pid == -1)
{
perror("fork failed");
exit(1);
}
if(pid == 0)
{
//2nd to n-1 child process
dup2(fds[0], STDIN_FILENO);
close(fds[0]);
dup2(fds[1], STDOUT_FILENO);
close(fds[1]);
execvp(comm1->cmd,comm1->parms);
}
wait(NULL);
}
pid = fork();
if(pid == -1)
{
perror("fork failed");
exit(1);
}
if(pid == 0)
{
//nth child process
printf("Last child execution\n");
close(fds[1]);
dup2(fds[0], STDIN_FILENO);
close(fds[0]);
execvp(comm2->cmd,comm2->parms);
}
close(fds[0]);
close(fds[1]);
wait(NULL);
wait(NULL);
}
}
This following code should give you an idea how to implement the pipelining:
#define STDIN 0
#define STDOUT 1
void exec_cmd(struct comType cmd) {
execvp(cmd->cmd, cmd->params);
}
void pipeCmds(struct comType* cmds) {
int fd[cmds_length * 2] = {0};
pid_t pid = 0;
for (int i = 0; i < cmds_length; i++) {
if (pid = fork() == 0) {
//child: make this cmd's output the other cmd's input
pipe(fd + (2*i) );
close(STDOUT);
dup(fd[i]);
if(i > 0) {
close(STDIN);
dup(fd[i-1]);
}
exec_cmd(cmds[i]);
close(fd[i]);
}
}
}
Note that the main idea is that each command is executed in a separate process (via fork) and the output goes to the next command's input rather than to the default stdout(with file descriptor 1), and the same for the input - stdin (file descriptor 0).

Multi-pipe does not work

I need to implement such IPC-schema:
runtime data -> filter1 -> filter2 -> output. (same as data | filter1 | filter2).
I can pass data to first filter, but to second I can not (maybe because in first child stdout fd is not closed). How to properly implement such schema?
P.S. filter1 and filter2 just read from stdin and write to stdout.
My code:
int main() {
int fd1[2];
pipe(fd1);
pid_t pid1;
if ((pid1 = fork()) > 0) {
char data[] = "Hello world!";
close(fd1[0]);
write(fd1[1], data, sizeof(data));
close(fd1[1]);
wait(NULL);
exit(EXIT_SUCCESS);
} else if (pid1 == 0) {
int fd2[2];
pipe(fd2);
pid_t pid2;
dup2(fd1[0], STDIN_FILENO);
dup2(fd2[1], STDOUT_FILENO);
close(fd1[0]); close(fd1[1]);
close(fd2[0]); close(fd2[1]);
if ((pid2 = fork()) > 0) {
execl("./upcase", "upcase", NULL);
perror("execl");
exit(EXIT_FAILURE);
} else if (pid2 == 0) {
close(fd1[0]); close(fd1[1]);
dup2(fd2[0], STDIN_FILENO);
close(fd2[0]); close(fd2[1]);
execl("./reverse", "reverse", NULL);
perror("execl");
exit(EXIT_FAILURE);
} else {
perror("pid2");
exit(EXIT_FAILURE);
}
} else {
perror("pid1");
exit(EXIT_FAILURE);
}
}
You are closing the pipes too early. Typically, you close fd2[0] before you use it in dup2. And as you redirect FILENO_STDOUT before the second fork, the second filter has no longer access to the original stdout.
Following code works:
int main() {
int fd1[2];
pipe(fd1);
pid_t pid1;
if ((pid1 = fork()) > 0) {
char data[] = "Hello world!";
close(fd1[0]); // OK, will no longer be used
write(fd1[1], data, sizeof(data));
close(fd1[1]);
wait(NULL);
exit(EXIT_SUCCESS);
} else if (pid1 == 0) {
int fd2[2];
pipe(fd2);
pid_t pid2;
close(fd1[1]); // OK, no used from here
if ((pid2 = fork()) > 0) {
dup2(fd1[0], STDIN_FILENO); // redirections for filter1
dup2(fd2[1], STDOUT_FILENO);
close(fd1[0]); // close everything except stdin and stdout
close(fd2[0]); close(fd2[1]);
execl("./upcase", "upcase", NULL);
perror("execl upcase");
exit(EXIT_FAILURE);
} else if (pid2 == 0) {
close(fd1[0]); // not used here
dup2(fd2[0], STDIN_FILENO); // redirection for filter2
close(fd2[0]); close(fd2[1]); // close all what remains
execl("./reverse", "reverse", NULL);
perror("execl reverse");
exit(EXIT_FAILURE);
} else {
...

Can't execute console commands in C with fork and pipe

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!)

C Programming 2 pipes

I want to set up 2 pipes in my program. I have 1 pipe working fine, but I don't know where to place the second pipe.
The pseudo code of my setup is shown below,
Here is it with curly braces sorry about that
//the first pipe:
pipe(pipe1)
//the second pipe:
pipe(pipe2)
pid = fork()
if(pid == 0) {
dup2(pipe1[1], 1)
close(pipe1[0])
execvp(beforepipe)
}
if(pid > 0) { //everything below is in here
pid2 = fork()
if(pid2 == 0){
//pipe1
dup2(pipe1[0],0)
dup2(out,1)
close(pipe1[1])
execvp(afterpipe)
//pipe2 does not work might need to be placed in different area
dup2(pipe1[1],1)
close(pipe1[0])
execvp(beforepipe1)
}
if(pid2 > 0){
close(pipe[0])
close(pipe[1])
wait() //this is an infinite for loop
pid3 = fork()
if(pid3 == 0){
dup2(pipe2[0],0)
dup2(out,1)
close(pipe2[1])
execvp(afterpipe2)
}
if(pid3 > 0) {
close(pipe2[0])
close(pipe2[1])
wait()
}
}
The position of the second pipe is in the wrong place or the code is altogether wrong.
Any suggestions?
Your main problem is that you are not closing anywhere near enough file descriptors. Given a file input1 in the current directory containing your string "eschew obfuscation\", this code works for me (but note how many file descriptors have to be closed!).
Rule of thumb: if a pipe is dup2()d or dup()d to standard input or output, close both file pipe file descriptors.
Example code (with debug tracing in place):
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
/* command pipeline: cat input1 | tr a-z A-Z | tr \\ q */
int main(void)
{
int pipe1[2];
int pipe2[2];
pid_t pid1;
char *cmd1[] = { "cat", "input1", 0 };
char *cmd2[] = { "tr", "a-z", "A-Z", 0 };
char *cmd3[] = { "tr", "\\", "q", 0 };
if (pipe(pipe1) != 0 || pipe(pipe2) != 0)
{
perror("pipe failed");
return 1;
}
pid1 = fork();
if (pid1 < 0)
{
perror("fork 1 failed");
return 1;
}
if (pid1 == 0)
{
/* Child 1 - cat */
dup2(pipe1[1], 1);
close(pipe1[0]);
close(pipe1[1]);
close(pipe2[0]);
close(pipe2[1]);
execvp(cmd1[0], cmd1);
perror("failed to execute cmd1");
return 1;
}
printf("pid 1 = %d\n", pid1);
fflush(stdout);
pid_t pid2 = fork();
if (pid2 < 0)
{
perror("fork 2 failed");
return 1;
}
if (pid2 == 0)
{
/* Child 2 - tr a-z A-Z */
dup2(pipe1[0], 0);
dup2(pipe2[1], 1);
close(pipe1[0]);
close(pipe1[1]);
close(pipe2[0]);
close(pipe2[1]);
execvp(cmd2[0], cmd2);
perror("failed to execute cmd2");
return 1;
}
printf("pid 2 = %d\n", pid2);
fflush(stdout);
pid_t pid3 = fork();
if (pid3 < 0)
{
perror("fork 3 failed");
return 1;
}
if (pid3 == 0)
{
/* Child 3 - tr \\ q */
dup2(pipe2[0], 0);
close(pipe1[0]);
close(pipe1[1]);
close(pipe2[0]);
close(pipe2[1]);
execvp(cmd3[0], cmd3);
perror("failed to execute cmd3");
return 1;
}
printf("pid 3 = %d\n", pid3);
fflush(stdout);
/* Parent - wait for the kids to all die */
close(pipe1[0]);
close(pipe1[1]);
close(pipe2[0]);
close(pipe2[1]);
pid_t corpse;
int status;
while ((corpse = wait(&status)) > 0)
printf("Child %d died status 0x%.4X\n", corpse, status);
return 0;
}
execvp(afterpipe)
//pipe2 does not work might need to be placed in different area
dup2(pipe1[1],1)
close(pipe1[0])
execvp(beforepipe1)
I think the execvp() didnot return. So the code below the execvp() is irrelevent.

piping in shell implementation in C

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.

Resources