Can someone please explain to me why my output has duplicates in it from the ls command. The normal operation of ls -l | sort does not give me a duplicated output so what could be the issue?
Essentially i'm trying to pipe the output from one command and enter it into another command. The program works so far, but the output is displaying duplicate data. Plus and explanation of why I would need to do a close after dup2 would be really helpful :)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
// function declarations
void executeLs(int data_pipe[]);
void executeSort(int data_pipe[]);
int main(){
int data_pipe[2]; // array storing the file descriptors
int childls_pid; // ls child process
int childSort_pid; // sort child process
int rc; // return vaue of the pipe
int child_status1;
int child_status2;
rc = pipe(data_pipe);
if(rc == -1){
perror("pipe");
exit(1);
}
childls_pid = fork();
childSort_pid = fork();
// Ls Child process
switch(childls_pid) {
case -1:
perror("fork childLs Error");
exit(1);
case 0:
// inside of child process
executeLs(data_pipe);
exit(0);
default:
break;
}
// Sort child process
switch(childSort_pid) {
case -1:
perror("fork childSort Error");
exit(1);
case 0:
executeSort(data_pipe);
exit(0);
default:
wait(&child_status2);
}
return 0;
}
void executeLs(int data_pipe[]){
// Closes the read file descriptor
close(data_pipe[0]);
dup2(data_pipe[1], STDOUT_FILENO);
// confused as to why this is necessary
close(data_pipe[1]);
execlp("ls", "ls", "-1", NULL);
}
void executeSort(int data_pipe[]){
// close the write file descriptor
close(data_pipe[1]);
dup2(data_pipe[0], STDIN_FILENO);
close(data_pipe[0]);
execlp("sort","sort", NULL);
}
The reason is that you're forking more processes than you intended. When you do:
childls_pid = fork();
childSort_pid = fork();
you're doing the second fork() in both the original parent process and the process created by the first fork(). So you now have the following process tree:
parent
childls
childSort
childSort
In both childls and childls->childSort, childls_pid is 0, so they both execute the case 0: clause that runs executeLs().
You need to move the second fork into code that only runs in the original parent. You can simply move it to after the first switch statement.
The reason you need to close the pipe FDs is because a pipe isn't really closed until all processes that have it open close it. If the child process has the write end of its pipe open, that will keep it from reading EOF on the pipe.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
// function declarations
void executeLs(int data_pipe[]);
void executeSort(int data_pipe[]);
int main(){
int data_pipe[2]; // array storing the file descriptors
int childls_pid; // ls child process
int childSort_pid; // sort child process
int rc; // return vaue of the pipe
int child_status1;
int child_status2;
rc = pipe(data_pipe);
if(rc == -1){
perror("pipe");
exit(1);
}
childls_pid = fork();
// Ls Child process
switch(childls_pid) {
case -1:
perror("fork childLs Error");
exit(1);
case 0:
// inside of child process
executeLs(data_pipe);
exit(0);
default:
break;
}
childSort_pid = fork();
// Sort child process
switch(childSort_pid) {
case -1:
perror("fork childSort Error");
exit(1);
case 0:
executeSort(data_pipe);
exit(0);
default:
wait(&child_status2);
}
return 0;
}
void executeLs(int data_pipe[]){
// Closes the read file descriptor
close(data_pipe[0]);
dup2(data_pipe[1], STDOUT_FILENO);
// confused as to why this is necessary
close(data_pipe[1]);
execlp("ls", "ls", "-1", NULL);
}
void executeSort(int data_pipe[]){
// close the write file descriptor
close(data_pipe[1]);
dup2(data_pipe[0], STDIN_FILENO);
close(data_pipe[0]);
execlp("sort","sort", NULL);
}
Related
Here's my attempt to try to understand how to do correct piping between two child processes. I'm simply trying to pass the output of one Linux command to another (ls to cat) and have the program return successfully. However, I'm guessing that the second child that is forked is getting stuck and the parent is forever waiting on this child. I have been fiddling with this code for a long time trying to find out why it's getting stuck. I'm kind of a noob when it comes to C system programming, but I am trying to learn.
Does anybody have any idea why the program does not exit, but hangs on cat?
Any help would be greatly appreciated.
Thanks.
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
char *a[2] = {"/bin/ls", NULL};
char *b[2] = {"/bin/cat", NULL};
char *envp[2] = {getenv("PATH"), NULL};
int fd[2], status;
pipe(fd);
int old_std_out = dup(1);
int old_std_in = dup(0);
dup2(fd[1], 1);
int pid = fork();
switch(pid)
{
case -1:
perror("Forkscrew");
exit(1);
break;
case 0:
execve(a[0], a, envp);
exit(0);
break;
default:
waitpid(-1, &status, 0);
dup2(old_std_out, 1);
break;
}
dup2(fd[0], 0);
pid = fork();
switch(pid)
{
case -1:
perror("Forkscrew");
exit(1);
break;
case 0:
execve(b[0], b, envp);
exit(0);
break;
default:
waitpid(-1, &status, 0);
dup2(old_std_in, 0);
break;
}
printf("\n");
return 0;
}
There are two potential deadlocks in your program.
First, the first child (ls) may block when trying to write to the pipe, in which case waitpid() will not return until ls terminates, and ls will not terminate until the second child (cat) starts executing, which can't happen until waitpid() returns. => Deadlock.
Second, cat will read from its stdin until all file descriptors for the write end are closed. Both the parent process and cat have a copy of the write end, cat has it without knowing about it explicitly. Some operating systems will have read() not block if the only copy of the write end is in the same process (to avoid this deadlock), but this is not guaranteed. Either way, since the parent process keeps around a copy of the filedescriptor, and the parent process waitpid()s for the child, which waits for the write-end of the pipe to close, you have deadlock again.
Often, simplifying the program solves such problems:
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
char *a[2] = {"/bin/ls", NULL};
char *b[2] = {"/bin/cat", NULL};
char *envp[2] = {getenv("PATH"), NULL};
int fd[2], status;
pipe(fd);
//int old_std_out = dup(1); /*No need to copy stdout...*/
//int old_std_in = dup(0); /*...or stdin...*/
//dup2(fd[1], 1); /*...if you wait dup2()ing until you need to*/
int pid = fork();
switch(pid)
{
case -1:
perror("Forkscrew");
exit(1);
//break; /*unreachable*/
case 0:
dup2(fd[1], STDOUT_FILENO); /*NOW we dup2()*/
close(fd[0]); /*no need to pass these file descriptors to...*/
close(fd[1]); /*...a program that doesn't expect to have them open*/
execve(a[0], a, envp);
exit(0); /*might want an error message*/
//break; /*unreachable*/
default:
//waitpid(-1, &status, 0); /*don't wait yet*/
//dup2(old_std_out, 1);
close(fd[1]); /*we don't need this in the parent anymore*/
break;
}
//dup2(fd[0], 0); /*not needed anymore*/
pid = fork();
switch(pid)
{
case -1:
perror("Forkscrew");
/*might want to ensure the first child can terminate*/
exit(1);
//break; /*unreachable*/
case 0:
dup2(fd[0], STDIN_FILENO);
close(fd[0]); /*again, cat doesn't expect a fourth fd open*/
execve(b[0], b, envp);
/*again, error message would be nice*/
exit(0);
//break;
default:
//waitpid(-1, &status, 0);
//dup2(old_std_in, 0);
break;
}
waitpid(-1, &status, 0); /*don't wait until both children are created*/
waitpid(-1, &status, 0);
printf("\n");
return 0;
}
As you see, I've left a number of suggestions for improvements, but this should already work fine now, provided the execve()s work alright.
So I am trying to use pipes to cat a file and to sed into a file called newfile.txt Currently the cat command works, using execvp, however it's outputing onto the command display. And then the program goes into an infinite loop when it goes to the sed command.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
char *myargv2[]={"sed", "-e" "s/color/colour/g", NULL};
char *myargv1[]={"cat", "colorfile.txt", NULL};
main()
{
int f_des[2];
int fd[2];
int pipe(int filedes[2]);
int file = open("newfile.txt",O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
if (file < 0)
return 1;
// create a pipe
// Open a pipe and report error if it fails
if (pipe(f_des)==-1)
{
perror("Pipe");
exit(2);
}
//fork the process
// Use switch for fork, because parent doesn't need child's pid.
switch (fork())
{
case -1: // Error
perror("Fork");
exit(2);
case 0: // Child
printf("HERE1\n");
//child will call dup2 to hook standard output to one end of the pipe. Then, execute the cat command using execvp
dup2(fd[1], fileno(stdout));
execvp(myargv1[0], myargv1);
close(fd[1]);
close(fd[0]);
perror(myargv1[0]);
close(fd[1]);
close(fd[0]);
printf("HERE12\n");
exit(3);
default: // Parent
{
printf("HERE13\n");
//parent will call dup2 to hook standard input to the other end of the pipe. Then, execute the sed command using execvp
dup2(fd[0], fileno(stdin));
execvp(myargv2[0], myargv2);
perror(myargv2[0]);
close(fd[1]);
close(fd[0]);
printf("HERE14\n");
//parent will also call dup2 to hook standard output to the file called newfile.txt
if(dup2(file,0 < 0))
return 1;
}
exit(4);
}
return 0;
}
Obviously I'm struggling here. Can anyone point out what I'm doing wrong and/or point me to a good source of information on how to do this?
Thanks!
One primary problem is that you can't make up your mind whether to use f_des or fd for the pipe file descriptors. You have:
int f_des[2];
int fd[2];
int pipe(int filedes[2]);
…
if (pipe(f_des) == -1)
{
perror("Pipe");
exit(2);
}
The declaration of pipe() is not a good idea; that's what the system headers do. But the serious problem is that you create the pipe in f_des and thereafter work with fd.
The other problem is that you don't close the pipe file descriptors accurately. You also have a fair amount of superfluous code. This code works correctly:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
static char *myargv2[]={"sed", "-e" "s/color/colour/g", NULL};
static char *myargv1[]={"cat", "colorfile.txt", NULL};
int main(void)
{
int fd[2];
int pipe(int filedes[2]);
int file = open("newfile.txt",O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
if (file < 0)
return 1;
if (pipe(fd)==-1)
{
perror("Pipe");
exit(2);
}
switch (fork())
{
case -1: // Error
perror("Fork");
exit(2);
case 0: // Child
printf("HERE1\n");
dup2(fd[1], fileno(stdout));
close(fd[0]); // Important (in general)
close(fd[1]); // Important (in general)
execvp(myargv1[0], myargv1);
perror(myargv1[0]);
printf("HERE12\n");
exit(3);
default: // Parent
printf("HERE13\n");
dup2(fd[0], fileno(stdin));
close(fd[0]); // Crucial
close(fd[1]); // Important (in general)
execvp(myargv2[0], myargv2);
perror(myargv2[0]);
exit(4);
}
return 0;
}
A simple rule of thumb is:
If you dup() or dup2() one end of a pipe to standard input or standard output, you should close both of the raw pipe file descriptors.
Given input file colorfile.txt containing:
this is the color of danger
coloration is not important
end of file is.
The program's output is:
HERE13
HERE1
this is the colour of danger
colouration is not important
end of file is.
Interestingly, if the output of the program is piped to another program, the debugging information isn't printed. That's a consequence of default buffering.
I need to write program that have construction like this:
Parent makes fifo, then fork()
child 1 reads message from stdin and writes it to named pipe (FIFO)
then in parent process I need to create pipe (unnamed) and another fork()
child number 2 reades from FIFO, counts length of message and send number to parent via pipe(unnamed).
I created a simple program with one fork where child can communicate with parent:
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#define FIFO "/tmp/my_fifo"
int main()
{
pid_t fork_result;
int pipe_fd;
int res;
char writer[3];
char reader[3];
res = mkfifo(FIFO,0777);
if (res == 0)
{
printf("FIFO created!\n");
fork_result = fork();
if (fork_result == -1)
{
fprintf(stderr, "fork error");
exit(EXIT_FAILURE);
}
if (fork_result == 0)
{
printf("CHILD 1\n");
pipe_fd = open(FIFO, O_WRONLY | O_NONBLOCK);
scanf("%s", writer);
res = write(pipe_fd,writer,3);
if (res == -1)
{
fprintf(stderr,"error writing fifo\n");
exit(EXIT_FAILURE);
}
(void)close(pipe_fd);
exit(EXIT_SUCCESS);
}
else
{
printf("PARENT\n");
pipe_fd = open(FIFO, O_RDONLY);
res = read(pipe_fd, reader, 3);
printf("reader: 0: %c\n",reader[0]);
printf("reader: 1: %c\n",reader[1]);
printf("reader: 2: %c\n",reader[2]);
(void)close(res);
}
}
else
{
printf("deleting fifo... run program again!\n");
unlink(FIFO);
}
exit(EXIT_SUCCESS);
}
and it is working very well. So I created code that have architecture described above:
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#define FIFO "/tmp/my_fifo"
int main()
{
pid_t fork_result;
pid_t fork_result2;
int pipe_fd;
int res;
char writer[3];
char reader[3];
res = mkfifo(FIFO,0777);
if (res == 0)
{
printf("FIFO created!\n");
fork_result = fork();
if (fork_result == -1)
{
fprintf(stderr, "fork error");
exit(EXIT_FAILURE);
}
if (fork_result == 0)
{
printf("CHILD 1\n");
pipe_fd = open(FIFO, O_WRONLY | O_NONBLOCK);
scanf("%s", writer);
res = write(pipe_fd,writer,3);
if (res == -1)
{
fprintf(stderr,"error writing to fifo\n");
exit(EXIT_FAILURE);
}
(void)close(pipe_fd);
exit(EXIT_SUCCESS);
}
else
{
printf("PARENt 1\n");
//don't forget pipe!
fork_result = fork();
pipe_fd = open(FIFO, O_RDONLY);
if (fork_result == 0)
{
printf("CHILD 2\n");
res = read(pipe_fd, reader, 3);
printf("Odczytano: 0: %c\n",reader[0]);
printf("Odczytano: 1: %c\n",reader[1]);
printf("Odczytano: 2: %c\n",reader[2]);
(void)close(res);
}
}
}
else
{
printf("deleting fifo\n");
unlink(FIFO);
}
exit(EXIT_SUCCESS);
}
Running sequence is like this:
PARENT 1
CHILD 1
CHILD 2
so in Parent 1 I'm opening FIFO to read, in child 1 I'm writing to FIFO and child 2 should read it. I mean in code because when I run it I can't even write anything to FIFO. In blocks in scanf("%s", writer); which worked in first program.
Am I using open() correctly? Do I need to use getpid() somewhere? Why it's blocking when I try to write to fifo.
The problem is that CHILD1 is opening the fifo with O_NONBLOCK, which will fail (with EWOULDBLOCK or EAGAIN) if no other process has the fifo open for reading. Now in the first program, the parent continues running after the fork and opens the fifo for reading before the child gets going and opens the write end, so it works. But in the second case, the parent does an extra fork first, which slows it down just enough that CHILD1 gets to its open command before PARENT or CHILD2 has opened the fifo for reading, so the CHILD1 open fails.
Get rid of the O_NONBLOCK and it works just fine (though you do open the fifo for reading in both PARENT and CHILD2, which is probably not what you want).
You have another issue if you want to read from the keyboard. If you run this from the shell, PARENT will exit immediately (more or less), so the shell will go back to reading commands from the keyboard, which means that CHILD1 and the shell will be fighting over the input. If on the other hand, you do what you originally describe and have PARENT wait reading from a pipe from CHILD2, it should do what you want.
Isn't it because you use twice the same variable fork_result? As you created another variable fork_result2, which you don't use, it is probably unintended.
I don't know if this will solve your problem, but at least using fork_result2 at the second fork would make it easier to understand...
I am trying to create a child that calls sort. The parent sends data to the child through a pipe. My code compiles and runs, but there is no output. What am I doing wrong? Am I not closing the pipes correctly, writing the pipes or outputting the data correctly?
[eddit] On my system I need to call /bin/sort NOT /usr/bin/sort!
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <ctype.h>
int main(void){
int pipes[2];
pid_t pid;
FILE *stream;
if(pipe(pipes) == -1)
printf("could not create pipe\n");
switch(fork()){
case -1:
fprintf(stderr, "error forking\n");
break;
case 0:
dup2(pipes[0], STDIN_FILENO);
pid = getpid();
printf("in child, pid=%d\n");
if(close(pipes[1]) == -1)
fprintf(stderr,"err closing write end pid=%d\n", pid);
execl("/usr/bin/sort", "sort", (char*) NULL);
break;
default:
stream = fdopen(pipes[1], "w");
pid = getpid();
printf("in parent, pid=%d\n", pid);
if (stream == NULL)
fprintf(stderr, "could not create file streami\n");
if(close(pipes[0]) == -1)
printf("err closing read end pid=%d\n");
fputs("bob\n",stream);
fputs("cat\n",stream);
fputs("ace\n",stream);
fputs("dog\n",stream);
if(fclose(stream) == EOF)
fprintf(stderr, "error while closing stream\n");
break;
}
return 0;
}
[edit] Here is my working code. Thank you everyone
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
int main(void){
int pipes[2];
pid_t pid;
FILE *stream;
int stat;
if(pipe(pipes) == -1)
printf("could not create pipe\n");
switch(fork()){
case -1:
fprintf(stderr, "error forking\n");
break;
case 0:
dup2(pipes[0], STDIN_FILENO);
pid = getpid();
printf("in child, pid=%d\n", pid);
if(close(pipes[1]) == -1)
fprintf(stderr,"err closing write end pid=%d\n", pid);
if(close(pipes[0]) == -1)
fprintf(stderr,"err closing write end pid=%d\n", pid);
execl("/bin/sort", "sort", (char*) NULL);
exit(EXIT_FAILURE);
break;
default:
stream = fdopen(pipes[1], "w");
pid = getpid();
printf("in parent, pid=%d\n", pid);
if (stream == NULL)
fprintf(stderr, "could not create file streami\n");
if(close(pipes[0]) == -1)
printf("err closing read end pid=%d\n");
fputs("bob\n",stream);
fputs("cat\n",stream);
fputs("ace\n",stream);
fputs("dog\n",stream);
if(fclose(stream) == EOF)
fprintf(stderr, "error while closing stream\n");
break;
}
wait(&stat);
return 0;
}
You most certainly do not have enough close() calls in the code, which will lock the processes up.
Pseudo code:
Create pipe
Fork
In parent:
Close read end of pipe
Write data to be sorted down write end of pipe
Close write end of pipe
Wait for child to die
In child
Close write end of pipe
Duplicate read end of pipe to stdin
Close read end of pipe
Exec the sort program
Exit with an error if the exec returns
Note that the pseudo code ends up closing all four ends of the pipe - the two in the parent and the two in the child. If you don't do that, you will run into deadlock.
The only thing you're really missing is calling wait() or waitpid() at the end of the parent's code, so that it doesn't exit until the child has finished.
No arguments to sort command. Simply running an execl will not work.
A simple program to test would be:
int main(void){
execl("/bin/sort","/bin/sort","filename", (char*) NULL);
}
I will try to create a simple program for you to analyze the situation.
Here you go, try this code:
int main(void){
int pipefd[2];
pid_t pid = 0;
int status;
char data[100]={0};
int fildes[2] ;
int nbytes;
char buf[100]={0};
status = pipe(fildes);
if (status == -1 ) {
// handle eerrror.
}
switch (fork()) {
case -1: /* Handle error */
break;
case 0: /* Child - reads from pipe */
close(fildes[1]); /* Write end is unused */
nbytes = read(fildes[0], buf, 100); /* Get data from pipe */
fprintf(stderr,"Inside child val recieved is %s\n", buf);
/* At this point, a further read would see end of file ... */
execl("/bin/sort", "/bin/sort",buf, (char*) NULL);
close(fildes[0]); /* Finished with pipe */
exit(0);
default: /* Parent - writes to pipe */
close(fildes[0]); /* Read end is unused */
write(fildes[1], "file", strlen("file")); /* Write data on pipe */
close(fildes[1]); /* Child will see EOF */
exit(0);
}
}
Here "file" is a file which need to be sorted.
Hope you can customize it as per your need.
Enjoy..!!!
I'm creating various processes (3 to be precise) and making them do different things.
So far so good. I'm trying to wait in the parent until all children are completed. I've played around with many options (such as the one listed below) but either the parent waits but I have to press enter to return to the shell (meaning that some child completes after the parent?) or the parent never returns to the shell. Any ideas? pointers to where to look for more help? Thanks
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#define READ_END 0
#define WRITE_END 1
int main (int argc, char **argv)
{
pid_t pid;
int fd[2];
int fd2[2];
pipe(fd);
pipe(fd2);
for (int i=0; i<3; i++) {
pid=fork();
if (pid==0 && i==0) {
//never uses fd2, so close both descriptors
close(fd2[READ_END]);
close(fd2[WRITE_END]);
printf("i'm the child used for ls \n");
close(fd[READ_END]); /*close read end since I don't need it */
dup2(fd[WRITE_END], STDOUT_FILENO);
close(fd[WRITE_END]);
execlp("ls", "ls", "-hal", NULL);
break; /*exit for loop to end child's code */
}
else if (pid==0 && i==1) {
printf("i'm in the second child, which will be used to run grep\n");
close(fd[WRITE_END]);
dup2(fd[READ_END], STDIN_FILENO);
close(fd[READ_END]);
close(fd2[READ_END]);
dup2(fd2[WRITE_END], STDOUT_FILENO);
close(fd2[WRITE_END]);
execlp("grep", "grep","p",NULL);
break;
}
else if (pid==0 && i==2) {
//never uses fd so close both descriptors
close(fd[READ_END]);
close(fd[WRITE_END]);
printf("i'm in the original process which will be replaced with wc\n");
close(fd2[WRITE_END]);
dup2(fd2[READ_END], STDIN_FILENO);
close(fd2[READ_END]);
printf("going to exec wc\n");
execlp("wc","wc","-w",NULL);
break;
}
else {
//do parenty things
}
}
wait(NULL);
while (1){
wait(NULL);
if(errno== ECHILD) {
printf("all children ended\n");
break;
}
}
close(fd[READ_END]);
close(fd[WRITE_END]);
close(fd2[READ_END]);
close(fd2[WRITE_END]);
return 0;
}
grep and wc never exit.
Why? They never receive an EOF on stdin.
Why? Because, even though ls has exited and closed the write end of pipe(fd), the main process still has the write end of pipe(fd) open, thus the read end of pipe(fd) is still waiting for more data. Similar thing goes for fd2: even if grep exited, wc wouldn't get an EOF on stdin.
Solution: close all the pipe fds in the main process before you wait.