I'm trying to create a shell wherein i need to implement pipelining..
The pipes are working fine untill all commands are correct else it either blocks or goes into infinite loop(when last cmd is wrong)..here's my pipelines function....
Code explanation:- I'm using two pipes for alternate pipes declared in command.Initially pipe1(fd1) in declared and when second pipe is found another pipe(fd2) is declared. When the third pipe is found pipe1(fd1 ) is redeclared and when fourth pipe is found pipe(fd2) is redeclared and this goes as long as new pipes are found.
num received by function is number of commands (actually number-1) separated by pipes.
Inside the loop then I have divided commands on five conditions
1>>(odd numbered cmd as i%2==0)starting command(i==0 and i%2==0)
2>>ending command(for I%2==0 and i==num)
3>>mid in pipe(i%2==0 and i!=num)
4>>(even numbered cmd as i%2!=0) mid in pipe(i%2!=0 and i!=num)
5>>ending even numbered cmd(i%2!=0 and i==num)
Heres my code :
void call_piping(int num){
int i,fd1[2],fd2[2],status;
pid_t pid;
//pipe(fd1);
//pipe(fd2);
for(i=0;i<=num;i++){
//printf("HH\n");
if(i%2==0){
if(pipe(fd1)==-1){
printf("Error creating pipe fd1\n");
return;
}
}
else{
if(pipe(fd2)==-1){
printf("Error creating pipe fd2\n");
return;
}
}
/*else{
close(fd1[1]);
}*/
if(i==0){
pid=fork();
if(pid==0){
close(fd1[0]);
close(1);
dup2(fd1[1],1);
if((execvp(argv[i][0],argv[i]))==-1){
printf("%s: Invalid command or Command not found\n",argv[i][0]);
close(fd1[1]);
return;
}
}
else{
waitpid(pid,&status,0);
close(fd1[1]);
//printf("Hello1");
}
}
else if(i%2==0 && i==num){
pid=fork();
if(pid==0){
close(0);
//close(1);
dup2(fd2[0],0);
if((execvp(argv[i][0],argv[i]))==-1){
printf("%s: Invalid command or Command not found\n",argv[i][0]);
close(fd2[0]);
return;
}
}
else{
waitpid(pid,&status,0);
//printf("Hello");
close(fd2[0]);
}
}
else if(i%2==0 && i!=num){
pid=fork();
if(pid==0){
close(0);
close(1);
dup2(fd2[0],0);
dup2(fd1[1],1);
if((execvp(argv[i][0],argv[i]))==-1){
printf("%s: Invalid command or Command not found\n",argv[i][0]);
close(fd1[1]);
//printf("Hell");
close(fd2[0]);
return;
}
}
else{
waitpid(pid,&status,0);
close(fd1[1]);
//printf("Hell");
close(fd2[0]);
}
}
else if(i%2!=0 && i==num){
pid=fork();
if(pid==0){
close(0);
dup2(fd1[0],0);
if((execvp(argv[i][0],argv[i]))==-1){
printf("%s: Invalid command or Command not found\n",argv[i][0]);
close(fd1[0]);
return;
}
}
else{
waitpid(pid,&status,0);
//printf("Hel");
close(fd1[0]);
}
}
else if(i%2!=0 && i!=num){
pid=fork();
if(pid==0){
close(0);
close(1);
dup2(fd1[0],0);
dup2(fd2[1],1);
if((execvp(argv[i][0],argv[i]))==-1){
printf("%s: Invalid command or Command not found\n",argv[i][0]);
close(fd2[1]);
close(fd1[0]);
return;
}
}
else{
waitpid(pid,&status,0);
//printf("He");
close(fd2[1]);
close(fd1[0]);
}
}
}
}
so basically it is error handling i'm having problem with...one solution that i thought was to all descendants processes created but dont know how to do that....
That is an extremely complex rat's nest of if statements. I've certainly not understood all the code; I've not even attempted to read it all. However, I don't think you're closing enough file descriptors quick enough.
For example, at the end, you have:
else{
waitpid(pid,&status,0);
//printf("He");
close(fd2[1]);
close(fd1[0]);
}
Diagnostic printing should always have a newline at the end; otherwise, you won't see the output in a timely manner. The two close() calls should be before the waitpid(); processes that wait for EOF on the pipe won't get EOF if the parent process still has it open for writing.
Early on in the code, you have:
if(pid==0){
close(fd1[0]);
close(1);
dup2(fd1[1],1);
if((execvp(argv[i][0],argv[i]))==-1){
printf("%s: Invalid command or Command not found\n",argv[i][0]);
close(fd1[1]);
return;
}
}
As a rule of thumb, if you use dup2() to associate a pipe file descriptor with either standard input or standard output, you should close both of the pipe file descriptors in that process. You also do not need to do close(1) before doing dup(fd[1], 1) — dup2() closes the target file descriptor anyway.
You should report errors on standard error, not standard output. The name should be self-explanatory.
You don't need to test the return value from execvp(); the function won't return unless it failed, so the test is redundant. It is also usually a good idea to exit from a failed exec*() call rather than return. If the exec*() fails and the code returns, you'll have two processes competing for standard input; this never leads to happiness.
The second fragment might be written like this (where, amongst other changes, I've added some spaces for improved readability; spaces are cheap!):
if (pid == 0)
{
dup2(fd1[1], 1);
close(fd1[0]);
close(fd1[1]);
execvp(argv[i][0], argv[i]);
fprintf(stderr, "%s: Invalid command or Command not found\n", argv[i][0]);
exit(1);
}
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 the following code:
pid_t childProcessID;
childProcessID = fork();
if (childProcessID == -1) {
printf("fork failed");
exit(EXIT_FAILURE);
}
if (childProcessID == 0) {
printf("reached child\n");
close(STDOUT_FILENO);
dup2(fd[1], STDOUT_FILENO);
close(fd[1]);
close(fd[0]);
printf("about to execute child command\n");
if (fork() == 0) {
execve(argv1[0], argv1, NULL);
printf("Command not found.");
exit(1);
}
else {
wait(NULL);
}
}
else {
printf("reached parent, but wait for child to finish\n");
waitpid(childProcessID, &child_status, NULL);
printf("child finished\n");
close(STDIN_FILENO);
dup2(fd[0], 0);
close(fd[1]);
printf("About to executed piped command\n");
if (fork() == 0) {
execve(argv2[0], argv2, NULL);
printf("Command not found");
exit(1);
}
else {
wait(NULL);
}
}
When I run my minishell and give it the input nl parse.c | wc -l, My minishell prints out:
reached parent, but wait for child to finish
reached child
And nothing underneath. The program is clearly still running but nothing is being printed. Why is my child not executing the rest of the code? argv1[] and argv[2] are initialized and work fine.
When you do :
dup2(fd[1], STDOUT_FILENO)
This code tells the process to redirect its STDOUT: Standard Output, i.e. the print commands and everything that gets printed on terminal to file descriptor fd[1].
So your child process executes and prints everything as it is supposed to,but not on the terminal. To see the output on terminal, comment the line :
//dup2(fd[1], STDOUT_FILENO)
For some unknown reason, when I'm executing piped commands in my shell program, they're only outputting once I exit the program, anyone see why?
Code:
int execCmdsPiped(char **cmds, char **pipedCmds){
// 0 is read end, 1 is write end
int pipefd[2];
pid_t pid1, pid2;
if (pipe(pipefd) == -1) {
fprintf(stderr,"Pipe failed");
return 1;
}
pid1 = fork();
if (pid1 < 0) {
fprintf(stderr, "Fork Failure");
}
if (pid1 == 0) {
// Child 1 executing..
// It only needs to write at the write end
close(pipefd[0]);
dup2(pipefd[1], STDOUT_FILENO);
close(pipefd[1]);
if (execvp(pipedCmds[0], pipedCmds) < 0) {
printf("\nCouldn't execute command 1: %s\n", *pipedCmds);
exit(0);
}
} else {
// Parent executing
pid2 = fork();
if (pid2 < 0) {
fprintf(stderr, "Fork Failure");
exit(0);
}
// Child 2 executing..
// It only needs to read at the read end
if (pid2 == 0) {
close(pipefd[1]);
dup2(pipefd[0], STDIN_FILENO);
close(pipefd[0]);
if (execvp(cmds[0], cmds) < 0) {
//printf("\nCouldn't execute command 2...");
printf("\nCouldn't execute command 2: %s\n", *cmds);
exit(0);
}
} else {
// parent executing, waiting for two children
wait(NULL);
}
}
}
Output:
In this example of the output, I have used "ls | sort -r" as the example, another important note is that my program is designed to only handle one pipe, I'm not supporting multi-piped commands. But with all that in mind, where am I going wrong, and what should I do to fix it so that it's outputting within the shell, not outside it. Many thanks in advance for any and all advice and help given.
The reason would be your parent process file descriptors are not closed yet. When you wait for the second command to terminate, it hangs because the writing end is not closed so it wait until either the writing end is closed, or new data is available to read.
Try closing both pipefd[0] and pipefd[1] before waiting for process to terminate.
Also note that wait(NULL); will immediately return when one process has terminated, you would need a second one as to not generate zombies if your process still runs after that.
I am using pipe fork and exec, to implement a generic pipe for any two shell programs. I am specifically using ls | grep to test it. It works, the data gets copied over to grep, grep searches for matches and then outputs them to stdout. However after that the program just hangs.
This is my code that is executed when a pipe is detected. I fork, and then fork again because I wish to have the parent process of the first fork continue to run after the exec calls. I believe due to debug code that after the exec() call that executes grep is made that nothing is happening.
if(pipeFlag == 1){
pipe(fd);
PID = fork();
if (PID == 0){//child process
fPID = fork();
if(fPID == 0){//child of child
printf("in child of child\n");
dup2(fd[1], 1);
execvp(command, argv);//needs error checking
printf("mysh: %s: command not found\n", argv[0]);
exit(EXIT_FAILURE);
}
if(fPID > 0){//parent of 2nd child
printf("in parent of 2nd child\n");
dup2(fd[0], 0);
execvp(command1, argv1);//needs error checking
printf("mysh: %s: command not found\n", argv[0]);
exit(EXIT_FAILURE);
}
if(PID == -1){
printf("ERROR:\n");
switch (errno){
case EAGAIN:
printf("Cannot fork process: System Process Limit Reached\n");
case ENOMEM:
printf("Cannot fork process: Out of memory\n");
}
return 1;
}
}
if(PID > 0){//parent
wait(PID, 0, 0);
printf("in outer parent\n");
}
if(PID == -1){
printf("ERROR:\n");
switch (errno){
case EAGAIN:
printf("Cannot fork process: System Process Limit Reached\n");
case ENOMEM:
printf("Cannot fork process: Out of memory\n");
}
return 1;
}
}
Below is my solution to the problem. I'm not sure if it's a permanent solution. I'm not even 100% sure if my reasoning for why this works and the previous code did not isn't. All I did was switch the command that is waiting for input from the pipe(grep) to the parent process, and the command writing output to the pipe(ls) to the child process.
My reasoning for why this works is thus: I was testing with ls | grep, ls was finished writing to the pipe before grep's child process ever got set up, and therefore never closed the pipe and grep never received EOF. By changing their position grep was ready and waiting for ls to write by the time the process running ls is set up. I believe that this is a very imperfect fix, so for anyone who reads this in the future, I hope you can provide a better answer. There are a number of conditions I can think of where this could still mess up, if my reasoning for why it works is correct.
if(pipeFlag == 1){
pipe(fd);
PID = fork();
if (PID == 0){//child process
fPID = fork();
if(fPID == 0){//child of child
printf("in child of child\n");
dup2(fd[0], 0);
execvp(command1, argv1);//needs error checking
printf("mysh: %s: command not found\n", argv[0]);
exit(EXIT_FAILURE);
}
if(fPID > 0){//parent of 2nd child
printf("in parent of 2nd child\n");
dup2(fd[1], 1);
execvp(command, argv);//needs error checking
printf("mysh: %s: command not found\n", argv[0]);
exit(EXIT_FAILURE);
}
if(PID == -1){
printf("ERROR:\n");
switch (errno){
case EAGAIN:
printf("Cannot fork process: System Process Limit Reached\n");
case ENOMEM:
printf("Cannot fork process: Out of memory\n");
}
return 1;
}
}
if(PID > 0){//parent
wait(PID, 0, 0);
printf("in outer parent\n");
}
if(PID == -1){
printf("ERROR:\n");
switch (errno){
case EAGAIN:
printf("Cannot fork process: System Process Limit Reached\n");
case ENOMEM:
printf("Cannot fork process: Out of memory\n");
}
return 1;
}
}
Currently am doing two forks to pipeline two process, but I think am doing my wait(&status) wrong because after the command my shell just hangs and does not return to my prompt. I know my pipe is working because I can see the result if I remove the wait.
Any tips?
pipe(mypipe);
pid1=fork();
if(pid1==0)
{
pid2=fork();
if(pid2==0)
{
close(0);
dup(mypipe[0]);
close(mypipe[1]);
execv(foundnode2->path_dir,arv2);
exit(0);
}
close(1);
dup(mypipe[1]);
close(mypipe[0]);
pid2 = wait(&status2);
execv(foundnode1->path_dir,arv1);
exit(0);
}
pid1 = wait(&status2);
Rule of Thumb: if you use dup() or dup2() to map one end of a pipe to standard input or standard output, you should close() both ends of the pipe itself. You're not doing that; your waits are waiting for the programs to finish but the programs will not finish because there is still a proess with the pipe open that could write to the pipe. Also, the process which created the pipe needs to close both ends of the pipe since it is not, itself, using the pipe (the child processes are using it). See also C MiniShell — Adding Pipelines.
Also, you should not be waiting for the first child to finish before launching the second (so the pid2 = wait(&status2); line is a bad idea). Pipes have a fairly small capacity; if the total data to be transferred is too large, the writing child may block waiting for the reading child to read, but the reading child hasn't started yet because it is waiting for the writing child to exit (and it takes a long time for this deadlock to resolve itself). You're seeing the output appear without the wait() calls because the second part of the pipeline executes and processes the data from the first part of the pipeline, but it is still waiting for more data to come from the shell.
Taking those tips into account, you might end up with:
pipe(mypipe);
pid1 = fork();
if (pid1 == 0)
{
pid2 = fork();
if (pid2 == 0)
{
close(0);
dup(mypipe[0]);
close(mypipe[1]);
close(mypipe[0]);
execv(foundnode2->path_dir, arv2);
fprintf(stderr, "Failed to exec %s\n", foundnode2->path_dir);
exit(1);
}
close(1);
dup(mypipe[1]);
close(mypipe[0]);
close(mypipe[1]);
execv(foundnode1->path_dir, arv1);
fprintf(stderr, "Failed to exec %s\n", foundnode1->path_dir);
exit(1);
}
close(mypipe[0]);
close(mypipe[1]);
pid1 = wait(&status1);
Notice the error reporting to standard error when the commands fail to execv(). Also, the exit status of 0 should be reserved for success; 1 is a convenient error exit status, or you can use EXIT_FAILURE from <stdlib.h>.
There is a lot of error checking omitted still; the fork() operations could fail; the pipe() might fail. One consequence is that if the second fork() fails, you still launch the second child (identified by foundnode1->path_dir).
And I note that you could save yourself a little work by moving the pipe creation into the first child process (the parent then does not need to — indeed, cannot — close the pipe):
int pid1 = fork();
if (pid1 == 0)
{
int mypipe[2];
pipe(mypipe);
int pid2 = fork();
if (pid2 == 0)
{
close(0);
dup(mypipe[0]);
close(mypipe[1]);
close(mypipe[0]);
execv(foundnode2->path_dir, arv2);
fprintf(stderr, "Failed to exec %s\n", foundnode2->path_dir);
exit(1);
}
close(1);
dup(mypipe[1]);
close(mypipe[0]);
close(mypipe[1]);
execv(foundnode1->path_dir, arv1);
fprintf(stderr, "Failed to exec %s\n", foundnode1->path_dir);
exit(1);
}
pid1 = wait(&status1);
If it's just a pipe with two processes, I wouldn't wait at all. Just fork and do an exec in parent and child.
int fd[2];
pipe(fd);
int pid = fork();
if (pid == -1) {
/* error handling */
} else if (pid == 0) {
dup2(fd[0], 0);
close(fd[1]);
execv(foundnode2->path_dir,arv2);
/* error handling for failed exec */
exit(1);
} else {
dup2(fd[1], 1);
close(fd[0]);
execv(foundnode1->path_dir,arv1);
/* error handling for failed exec */
exit(1);
}