int cpipe[2];
if(-1==pipe(cpipe)) {
printf("Pipe failed");
exit(1);
}
// Fork printenv
childPID=fork();
if(-1==childPID) {
printf("Fork failed");
exit(1);
}
else if (0==childPID) {
// if(-1==dup2(cpipe[1],1)); {
// printf("Pipe failed");
// exit(1);
// }
// close(cpipe[0]);
// close(cpipe[1]);
execl("/usr/bin/printenv","printenv",0);
printf("Execl failed");
_exit(1);
}
wait(&childExitStatus);
printf("\n%d \n",childExitStatus);
if(childExitStatus!=0) {exit(1);}
The above code works; gives childExitStatus = 0, and everyone is happy. Since i want the output of printenv to write to a pipe instead of being printed on screen, i put in the currently commented section. Decommenting that section, however gives the exit code 256, and everyone gets sad. What did i do wrong?
Your line:
if(-1==dup2(cpipe[1],1)); {
has a misplaced semicolon. Change the above line to:
if(-1==dup2(cpipe[1],1)){
and you should see the correct return value.
exit(1) was always getting called inside the if statement because the if statement was ended with the semicolon and the code inside the braces was always getting executed.
I managed to make it work by using dup instead of dup2 in the child process. The original code always failed at dup2 (I changed the exit status for each specific error). But I don't know why.
} else if (childPID == 0) {
close(1);
dup(cpipe[1]);
...
}
I also added reading from cpipe[0] in the parent process, just to test:
char c[4096];
write(1, c, read(cpipe[0], c, 4096));
close(cpipe[0]);
int childExitStatus;
wait(&childExitStatus);
printf("\n%d \n", childExitStatus);
if (childExitStatus != 0)
_exit(5);
Try not write:
close(cpipe[1]);
because printenv closes the cpipe[1]. If I am not mistaken.
Related
I'm trying to implement piping in a simple shell program that I'm writing in C.
But for some reason, I'm not getting output when I try to run ls | wc -l.
I'm really not sure why this is happening since I'm basically putting the child process's output to pipe[1] which does the command before pipe indicator and I'm putting parent's input to pipe[0] which does the command after pipe indicator and it should be printing to the terminal since the output of parent's never been changed, my approach right now is if piping is flagged the call fork in child and do the piping.
code below
int pipe1[2];
int pipepid;
int piping; /*flag for piping*/
int pipeposition;/*index of pipe indicator*/
//* code... */
if(pipe(pipe1)!= 0){
perror("pipe");
exit(1);
};
/* split commands to before pipe indicator and after */
for(int p = 0;p<pipeposition;p++){
argsbefore[p]=args[p];
}
/* after */
int e=0;
for(int h = pipeposition+1; h<cnt;h++){
argsafter[e]=args[h];
e++;
}
/* code ... */
if(piping){
pipepid = fork();
if(pid == 0){
/* do child */
if(dup2(pipe1[1],1)==-1){
perror("dup2 child");
exit(1);
}
close(pipe1[1]);
if (execvp(argsbefore[0], argsbefore) < 0) {
printf("exec failed\n");
exit(1);
}
exit(0);
}/* else if error */
else if(pid == -1){
printf("ERROR: fork failed\n");
exit(1);
}/* parent */
else{
if(dup2(pipe1[0],0)==-1){
perror("dup2 parent");
exit(1);
}
close(pipe1[0]);
if (execvp(argsafter[0], argsafter) < 0) {
printf("exec failed\n");
exit(1);
}
}
}
you seem to be doing that on a unix-like system. If you're lucky, your system might have a tool that reports every system call your program perform (strace -f my_program my_ar gu_ments would do that on Linux).
That would give you a list of what process did what and when, and whether there have been error code for some operations. That usually helps a lot with these multi-process setups.
It turns out I didn't close all the pipes so the second command wasn't able to finish, after putting close for both ends in the main parent process it was fixed
I have this function that executes a command
int cmd2(char * const *cmd, char * std_out)
{
char tmp[4096];
int pipefds[2], r, status, x;
pid_t pid;
if (pipe(pipefds) == -1){
return -1;
}
if ( (pid = fork()) == -1){
return -1;
}
if (pid == 0)
{
dup2(pipefds[1], STDOUT_FILENO);
dup2(piepfds[1], STDERR_FILENO);
close(pipefds[0]);
close(pipefds[1]);
execvp(cmd[0] , cmd);
}
else
{
close(pipefds[1]);
x = read(pipefds[0], tmp, 4096);
printf("Got %d bytes\n",x);
wait(NULL);
}
return 0;
}
the error message that should be outputted
┌──(kali㉿kali)-[~/]
└─$ ./wow
zsh: no such file or directory: ./wow
when running another c code that fputs a buffer into stderr it is display by the cmd2 without any problems
I tried to redirect stderr to stdout using 2>&1 but this does not seem to have an effect
how to read any/all results from executing the command
If you try running a non-existent command with cmd2, execvp will set errno and return. It will not print anything anywhere. You need to check errno and print the error. Something as simple as:
execvp(cmd[0], cmd);
perror(cmd[0]);
exit(EXIT_FAILURE);
should usually suffice. zsh does something like that when you try to run a non-existent command with it, that's why you see an error message.
Note however that it is the child process that prints the message. You discard child process output and only print its length. You need to do something about it if you want to see the message.
So I'm trying to read a line with multiple commands and creating a child process for each command to handle it. I'm having some trouble once I try to uncomment the if(fd2) statement for stdout redirection. Instead of the desired output appearing once, it appears multiple times. I'm not exactly sure what is causing this, I'm closing all of the file descriptors and having the child exit.
The commands I'm dealing with may have both stdout and stdin redirection, and I'm successful in setting up the redirection for stdin, but I cant seem to find my bug. I don't get a perror() warning on display. It's not so much as a problem with dup2(), but rather with open(), if I just uncomment that part I get the error.
int concCmd(Cmd cmdM[], int iCmdCnt, Token tokenM[], int iTokenCnt){
long pid, wpid;
int i, status=0, fd = 0, fd2=0;
fflush(0);
for(i=0; i<iCmdCnt; i++){ //create a process for every command
pid=fork();
switch(pid){
case -1:
perror("Fork\n");
return 1;
case 0:
if(cmdM[i].iStdinRedirectIdx != 0){ //does command need stdin redirection
if((fd = open(tokenM[cmdM[i].iStdinRedirectIdx], O_RDONLY)) <0){
perror("open stdin");
return 1;
}
if((dup2(fd,STDIN_FILENO))<0){
perror("dup2");
return 1;
}
close(fd); //close in parent process
}
if(cmdM[i].iStdoutRedirectIdx != 0){ //command needs stdout redirection
// if((fd2 = open(tokenM[cmdM[i].iStdoutRedirectIdx], O_WRONLY|O_CREAT|O_EXCL)) < 0){
// perror("open stdout");
// return 1;
// }
// if((dup2(fd, STDOUT_FILENO))<0){
// perror("dup2");
// return 1;
// }
close(fd2);
}
//EXECVP HERE!!!
//execvp (cmdM[i].szCmdNm, tokenM[
exit(0);
default:
wpid = wait(&status);
close(fd);
close(fd2);
fprintf(stderr, "%ld %ld\n", (long)getpid(), wpid);
}
}
return 0;
}
I went ahead an added a fflush(0) before I started forking, to clear out the buffer, as suggested in another question, which should reasonably clear the buffer for when I call open for stdout, but I still get the same errors.
I was going to make a comment, because what I want to say is very simple, but I need to reference some code, so maybe posting an answer is easier.
// if((fd2 = open(tokenM[cmdM[i].iStdoutRedirectIdx], O_WRONLY|O_CREAT|O_EXCL)) < 0){
// perror("open stdout");
// return 1;
// }
// if((dup2(fd, STDOUT_FILENO))<0){
// perror("dup2");
// return 1;
// }
In your code, the part about fd2 which you comment in your question has a typo.
dup2(fd, STDOUT_FILENO) should be dup2(fd2, STDOUT_FILENO)
I don't know whether it is really a typo in your source code, or you just made a mistake when you posting your question.
im currently implementing a shell in C.
My problem arises when i try to run a command like this:
SHELL$: sort < txtFile | grep key
im running sort < txtFile in a process (child), and in the parent i.e else if(pid > 0) im running the other command to the right of the pipe.
The program runs fine, but it exits the infinite loop that i set up in main to keep receiving input from the user.
How could i solve this problem?
this is the code i have so far to deal with the pipe, i didnt include the code that i have to deal with the redirects:
c2p is the pipe i setup for this.
if(pid == 0)
{
if( PIPE_FLAG )
{
close(c2p[0]);
if(dup2(c2p[1], STDOUT_FILENO) == -1){
perror("dup2() failed");
exit(2);
}
}
/* Execute command */
execvp(cmd_args[0], cmd_args);
perror("exec failed 1. "); /* return only when exec fails */
exit(-1);
}
else if(pid > 0)
{
if(PIPE_FLAG)
{
close(c2p[1]);
if(dup2(c2p[0], STDIN_FILENO) == -1){
perror("dup2() failed");
exit(-1);
}
execvp(nxt_args[0], nxt_args);
perror("exec failed 2. ");
exit(-1);
}
}
else
{
/* error occurred */
perror("fork failed");
exit(1);
}
I'm running sort < txtFile in the child process, and in the parent I'm running the command to the right of the pipe.
What happens to your shell process, then? The parent process is the shell. By running the right-side command in the parent process you're having it take over the shell's process. Remember that exec() replaces the current process.
You'll need to fork() twice, and execute the two sides of the pipe in the child processes. The parent must remain the shell, which will then wait() for the children to exit before presenting the next command prompt.
/* How shell works */
#include<stdio.h>
#include<unistd.h>
main (int argc, char **argv)
{
if (argc < 2)
{
fprintf (stderr, "\nUsage: ./a.out cmd [options]...\n");
}
if (!fork ())
{
argv++;
execvp (argv[0], argv);
}
}
I'm using the execl function to run a Linux process from C. When I do, for example:
int cmd_quem() {
int result;
result = fork();
if(result < 0) {
exit(-1);
}
if (result == 0) {
execl("/usr/bin/who", "who", NULL);
sleep(4); //checking if father is being polite
exit(1);
}
else {
// father's time
wait();
}
return 0;
}
I get on the console the result of doing "who" on the terminal. What I'd like to know is if there is any function to "catch" the output result from a command. What I mean is, if there is anyway to catch this:
feuplive tty5 2009-11-21 18:20
Which is one of the lines resulting from the who command.
To do this, you need to open a pipe. You then replace the child's stdout with the writing end of the pipe, and read from the reading end of the pipe in the parent. Like this modified version of your code:
int cmd_quem(void) {
int result;
int pipefd[2];
FILE *cmd_output;
char buf[1024];
int status;
result = pipe(pipefd);
if (result < 0) {
perror("pipe");
exit(-1);
}
result = fork();
if(result < 0) {
exit(-1);
}
if (result == 0) {
dup2(pipefd[1], STDOUT_FILENO); /* Duplicate writing end to stdout */
close(pipefd[0]);
close(pipefd[1]);
execl("/usr/bin/who", "who", NULL);
_exit(1);
}
/* Parent process */
close(pipefd[1]); /* Close writing end of pipe */
cmd_output = fdopen(pipefd[0], "r");
if (fgets(buf, sizeof buf, cmd_output)) {
printf("Data from who command: %s\n", buf);
} else {
printf("No data received.\n");
}
wait(&status);
printf("Child exit status = %d\n", status);
return 0;
}
First, execl does not return unless there's a problem like the executable is not found. That sleep(4) is probably never executed.
As for redirecting and getting the output, check out the Unix Programming FAQ. Look for spawn_background_command.
The exec() family of functions creates a new process image from a regular, executable file. This file is either an executable object file, or an interpreter script. There is no return from a successful call to an exec() function, because the calling process is functionally replaced by the new process.
So any code after exec() is never executed unless it is failed.
If you want to capture output of a shell command you need popen.