Shell in C to execute pipe - c

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;
};

Related

how make cat and grep work in the first and the second pipe in c writing like heredoc in bash <<

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
}

execl() returning -1 unexepectedly

I'm using execl() to run a certain program off of a forked child. Here's what I've got:
int setup(int* arg1, int* arg2) {
int fd[2], ret, pid;
// create the pipe
ret = pipe(fd);
if (ret == -1) {
fprintf(stderr, "Pipe Failed");
exit(-1);
}
pid = fork();
if (pid < 0) {
fprintf(stderr, "Fork Failed");
exit(-1);
} else if (pid == 0) {
dup2(fd[0],0);
close(fd[0]);
close(fd[1]);
int uhoh = execl("prog", "prog", arg1, arg2, NULL);
fprintf(stderr, "%d %d\n", uhoh, getpid());
} else {
close(fd[0]);
return fd[1];
}
}
The goal here is to create a child, attach the read end of a pipe to it's STDIN, then replace it with the prog program. Stuff wasn't working so I added the fprintf on STDERR to see what the return value was and it's constantly -1.
My current working directory looks like this:
stuff/
main.c
main
prog.c
prog
So should this execl() call be working? Would it be returning -1 if maybe there was a problem with prog?

How to execute arbitrary pipes in c and continue

I'm trying to fork and then execute two or more piped commands in the child process. My idea is to use a while loop to continuously fork and execute the command in one process while continuing the loop in the other. Here's my code:
void
execute_pipe_command(command_t *c)
{
command_t command = *c;
pid_t pid = fork();
if(pid > 0) {
int status;
while(waitpid(pid, &status, 0) < 0)
continue;
if(!WIFEXITED(status))
error(1, errno, "Child exit error");
command->status = WEXITSTATUS(status);
return;
} else if (pid == 0) {
while(command->type == PIPE_COMMAND)
{
int fd[2]; pipe(fd);
pid = fork();
if(pid > 0) {
close(fd[0]);
dup2(fd[1], STDOUT_FILENO);
char **args = command->u.command[1]->u.word;
execvp(args[0], args);
} else if (pid == 0) {
close(fd[1]);
dup2(fd[0], STDIN_FILENO);
command = command->u.command[0];
continue;
} else {
error(1, errno, "forking error");
}
}
char **args = command->u.word;
execvp(args[0], args);
} else {
error(1, errno, "forking error");
}
}
Command is a struct that hold it's type, and if it's a pipe command it holds left and right children commands. Otherwise if it's a simple command it holds an array of strings that make up the command.
When I call this function with a pipe command like ls | cat it should execute the commands, but instead it behaves weirdly. The first two piped commands will run but won't give control back to the program. Instead it'll hang. The subsequent commands are just ignored. So if I give this ls | cat | wc this function will print ls and won't exit until I give a SIGINT.
I'm pretty much confused as to what's going on. I'd appreciate if someone could point out the problem.
while (command->type == PIPE_COMMAND) is always true! This is way it hangs.

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.

Program to pipe output of one process to other process using execv

I am trying to execute "sudo conntrack -E -p udp -e NEW" command and then pipe the output of this command to "logger" command, but this doesnt work. anything obvious that is going wrong?
so parent is the "sudo conntrack...." which forks the child "logger ..."
void main () {
pid_t pid;
int status;
int j=0;
int exe_process;
FILE *prt1;
FILE *prt2;
int fd[2];
char *arg[]={ "sudo", "/usr/sbin/conntrack", "-E", "-p", "udp", "-e", "NEW", NULL };
char *arg1[]={ "/usr/bin/logger", "-t", "log-conntrack", "-p", "daemon.notice", NULL };
if (pipe(fd) < 0)
printf("pipe error\n");
if ((pid = fork()) < 0) /* fork a child process */
{
printf("ERROR: forking child process failed\n");
exit(1);
}
else if (pid > 0) /* for the parent process: */
{
printf("In parent process %d\n",getpid());
close(fd[0]);
if (execvp("/usr/sbin/conntrack", arg) < 0) /* execute the command */
{
printf("ERROR: exec failed\n");
exit(1);
}
prt1=fdopen(fd[1], "ab");
}
else /* for the child: */
{
printf("In parent child %d\n",getpid());
close(fd[1]);
prt2=fdopen(fd[0], "rb");
if (execvp("/usr/bin/logger", arg1) < 0) /* execute the command */
{
printf("ERROR: exec failed\n");
exit(1);
}
}
}
That's pretty much correct but the main problem is that you need dup() instead of what you are trying to do with fdopen. dup will redirect stdin/stdout the way you want.
else if (pid > 0) /* for the parent process: */
{
printf("In parent process %d\n",getpid());
close(fd[0]);
close(1);
dup(fd[1]);
if (execvp("/usr/sbin/conntrack", arg) < 0) /* execute the command */
{
printf("ERROR: exec failed\n");
exit(1);
}
//prt1=fdopen(fd[1], "ab");
}
And in the child:
close(0);
dup(fd[0]);

Resources