piping in shell implementation in C - 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.

Related

Am I piping correctly?

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.

Shell in C to execute pipe

Hi I'm having a bit of trouble with my pipe execute function, where I want a shell in C to be able to execute a pipe. arg1 is the input before the pipe and arg2 is the command after the pipe. I want the program to terminate after ctr -d but it seems to quit without it, the moment the code is executed. An example of my input is ls | wc, where arg1 = ls and arg2 = wc. Any help/ pointers will be greatly appreciated, thank you.
int executepipe (char ** arg1, char ** arg2) {
int fds[2];
int child=-1;
int status = pipe(fds);
if (status < 0)
{
printf("\npipe error");
return -1;
}
int pid =-1;
pid= fork();
while(1){
if (pid < 0) { //error!
perror("fork");
exit(1);
}
//child
if (pid == 0){// child process (command after the pipe)
//signal(SIGINT, SIG_DFL);
//signal(SIGQUIT, SIG_DFL);
close(fds[1]);//nothing more to be written
dup2(fds[0], 0);
execvp(arg2[0], arg2);
//if errors exist execv wouldn't have been invoked
perror("cannot execute command");
exit(1);
}
else { // parent process (command before the pipe)
close(fds[0]);
signal(SIGINT, SIG_DFL);
signal(SIGQUIT, SIG_DFL);
dup2(fds[1], 1);
close(fds[1]);
execvp(arg1[0], arg1);
//if errors exist execv wouldn't have been invoked
perror("cannot execute command");
exit(1);
}
if ( wait(&child) == -1 ){
perror("wait");}
}
return 0;
};

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

Pipe not getting EOF

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

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

Resources