using a child process to read input? - c

I'm trying to use a child process to count words from console input (what I type in UNIX). Here is my code:
int main(){
int pipe1[2];
int child_value;
pipe(pipe1);
child_value= fork();
if(child_value > 0){
/*parent*/
int word_count;
dup2(STDIN_FILENO, pipe1[0]);
close(pipe1[0]);
scanf("%d", &word_count);
printf("%d\n", word_count);
} else if (child_value == 0) {
/*child*/
dup2(pipe1[1], STDOUT_FILENO);
close(pipe1[1]);
execl("/usr/bin/wc", "wc", "-w", NULL);
err(EX_OSERR, "exec error");
} else err(EX_OSERR, "fork error");
return 0;
}
The output displayed on my console is always 0 disregarding what I type into the console, and I always get an error saying:
wc: standard input: Input/output error

As noted in comments:
When you use dup2() or dup() to map one end of a pipe to standard input or standard output, it is almost invariably correct to close both ends of the pipe afterwards. The exceptions are very few and far between; you will know when you need to avoid closing both ends of the pipe. However, that isn't the direct cause of your problem.
Compare: dup2(STDIN_FILENO, pipe1[0]); and dup2(pipe1[1], STDOUT_FILENO);. They should both list the standard file number as the second argument.
#include <err.h>
#include <fcntl.h>
#include <stdio.h>
#include <sysexits.h>
#include <unistd.h>
int main(void)
{
int pipe1[2];
int child_value;
pipe(pipe1);
child_value = fork();
if (child_value > 0)
{
/*parent*/
int word_count;
dup2(pipe1[0], STDIN_FILENO);
close(pipe1[0]);
close(pipe1[1]);
scanf("%d", &word_count);
printf("%d\n", word_count);
}
else if (child_value == 0)
{
/*child*/
dup2(pipe1[1], STDOUT_FILENO);
close(pipe1[1]);
close(pipe1[0]);
execl("/usr/bin/wc", "wc", "-w", NULL);
err(EX_OSERR, "exec error");
}
else
err(EX_OSERR, "fork error");
return 0;
}
Example output (program xx19):
$ ./xx19
So she went into the garden
to cut a cabbage-leaf
to make an apple-pie
and at the same time
a great she-bear coming down the street
pops its head into the shop
What no soap
So he died
and she very imprudently married the Barber
and there were present
the Picninnies
and the Joblillies
and the Garyulies
and the great Panjandrum himself
with the little round button at top
and they all fell to playing the game of catch-as-catch-can
till the gunpowder ran out at the heels of their boots
90
$
(You can search on Google for 'Panjandrum' to find out where that nonsense prose comes from.)

Related

Can I use pipe as read at parent and write in child?

I'm now learning how to use pipes correctly. I found examples only for write in parent and read in child, but I want to know how can I do it reverse. I tried like this:
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <sys/types.h>
int main(){
int p1[2], p2[2];
char original[]="This is a string\n ";
pid_t child2;
pid_t child=fork();
if (child>0)
{
child2 = fork();
if(child2>0){
wait();
printf("I'm the parrent.\n");
close(p1[1]);
close(p2[1]);
printf("Parrent read p1:\n");
if (read(p1[0], original, sizeof(original)) == -1)
perror("read() error in parent p1");
else printf("parent read '%s' from pipe 1\n", original);
printf("Parrent read p2:\n");
if (read(p2[0], original, sizeof(original)) == -1)
perror("read() error in parent p2");
else printf("parent read '%s' from pipe 2\n", original);
}
else{
printf("Child2 \n");
pipe(p2);
close(p2[0]);
if (write(p2[1], original, sizeof(original)+1) == -1)
perror("write() error in child2");
//close(p2[1]);
}
}
else
{
printf("Child1 \n");
pipe(p1);
close(p1[0]);
if (write(p1[1], original, sizeof(original)+1) == -1)
perror("write() error in child1");
//close(p1[1]);
}
return 0;
}
But this way give to me error at reading in parent. read() error in parent p1: Bad file descriptor both times, at p1 and also at p2. So, can I do this this way or not? Or this is just something trivial error?
As already said you just need to invert the indexes for closing the pipes and the read/write.
Your code was almost correct. Two things were wrong: on the parent you were first closing the pipes and then calling pipe(): it should be the other way round: first you create both of the pipes and then close the corresponding element, all in the parent.
Secondly you should call wait after setting the pipes otherwise it does not work. Guessing you want to wait for all the children you should call wait(NULL). I do not know what you meant with wait().
The complete code:
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
int main(){
int p1[2], p2[2];
char original[]="This is a string\n ";
pid_t child2;
pid_t child=fork();
if (child>0)
{
child2 = fork();
if(child2>0){
printf("I'm the parrent.\n");
pipe(p1); //ADDED
pipe(p2); //ADDED
close(p1[1]);
close(p2[1]);
wait(NULL); //DON'T GET IT WHY? if u want to wait all children,
//wait after setting the pipes.
printf("Parrent read p1:\n");
if (read(p1[0], original, sizeof(original)) == -1)
perror("read() error in parent p1");
else printf("parent read '%s' from pipe 1\n", original);
printf("Parrent read p2:\n");
if (read(p2[0], original, sizeof(original)) == -1)
perror("read() error in parent p2");
else printf("parent read '%s' from pipe 2\n", original);
}
else{
printf("Child2 \n");
//pipe(p2); ERROR HERE
close(p2[0]);
if (write(p2[1], original, sizeof(original)+1) == -1)
perror("write() error in child2");
//close(p2[1]);
}
}
}

2 pipe, one fork, bc and execlp (C)

I'm trying to get something like this to work in c using piping and fork :
echo "an operation like 10+10" | bc
To be precise, I tried to create 2 pipes, one where the server will write the operation and bc will read and another where the result of the operation (by bc) will go and the server will read it and printf it. For this, I'm changing the output and input of the parent and child process.
Here is my code:
int main(){
int t1, t2;
char resultat[5];
int pipe1[2];
int pipe2[2];
pipe(pipe1);
pipe(pipe2);
int resultat_fork = fork();
if(resultat_fork == -1){
exit(EXIT_FAILURE);
}
if(resultat_fork!=0){
printf("I am the parent\n");
close(pipe1[0]);
close(pipe2[1]);
//We only want to write into pipe1 and read from pipe2, we can close the two other
write(pipe1[1], "20*5\n", 20);
//write on pipe1 the expression
read(pipe2[0], resultat, sizeof(resultat));
//read from pipe2 the answer (written by bc)
printf("resultat : %s\n",resultat);
close(pipe1[1]);
close(pipe2[0]);
}else{
printf("I am the children\n");
close(pipe1[1]);
close(pipe2[0]);
//We only want to write into pipe2 and read from pipe1, we can close the two other
dup2(pipe1[0], 0);
//redirection standard input to pipe1[0]
dup2(pipe2[1], 1);
//redirection standard output to pipe2[1]
execlp("bc", "bc", NULL);
//execute bc, which normaly will read the operation from pipe1 and write the answer into pipe2, but I think it's here the problem come out
close(pipe1[0]);
close(pipe2[1]);
}
return 0;
}
I'm getting the correct answer but with the error :
"(standard_in) 2: illegal character : ^#"
"(standard_in) 2: illegal character : :"
plus I have to CTRL C to quit.
I guess it comes from BC but why...
What am I doing wrong? Thank you!
I've already seen few exemple but only threw one pipe.
This works. Note how it specifies the size of the data to be written, and how it checks the writes and reads, and also how it closes file descriptors. (Remember: sizeof("string literal") counts the null byte, unlike strlen(). It's also a compile time constant. However, more general purpose code would use strlen() on the current expression string.)
Rule of Thumb: If you use dup2() to duplicate a pipe file descriptor to standard input or standard output, close both ends of the pipe.
That also applies if you use dup(), or fcntl() with F_DUPFD or F_DUPFD_CLOEXEC instead.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{
char resultat[5];
int pipe1[2];
int pipe2[2];
pipe(pipe1);
pipe(pipe2);
int resultat_fork = fork();
if (resultat_fork == -1)
{
exit(EXIT_FAILURE);
}
if (resultat_fork != 0)
{
printf("I am the parent\n");
close(pipe1[0]);
close(pipe2[1]);
if (write(pipe1[1], "20*5\n", sizeof("20*5\n") - 1) != sizeof("20*5\n") - 1)
fprintf(stderr, "write to child failed\n");
int nbytes = read(pipe2[0], resultat, sizeof(resultat));
if (nbytes <= 0)
fprintf(stderr, "read from child failed\n");
else
printf("resultat: [%.*s]\n", nbytes, resultat);
close(pipe1[1]);
close(pipe2[0]);
}
else
{
printf("I am the child\n");
close(pipe1[1]);
close(pipe2[0]);
dup2(pipe1[0], 0);
dup2(pipe2[1], 1);
close(pipe1[0]); /* More closes! */
close(pipe2[1]); /* More closes! */
execlp("bc", "bc", NULL);
fprintf(stderr, "Failed to execute bc\n");
exit(EXIT_FAILURE);
}
return 0;
}
There is still room for improving the error handling; the code ploughs on after reporting some of the errors, which is probably not the best behaviour.
Output:
I am the parent
I am the child
resultat: [100
]
(Note: if you pipe the output of the program somewhere, you don't see the I am the child message. For the reasons why, see printf() anomaly after fork().)

How can I redirect stdout back to the terminal in a multi process c program?

I'm trying to write a c program that is the equivalent of the linux command ps -aux | sort -r -n -k 5 but I'm not getting any output
Here's my code
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc, char ** argv){
int pipes[2];
int r;
r = pipe(pipes);
if (r < 0) {
fprintf(stderr, "pipe failed\n\n"); // stderr is a FILE* variable for the standard error file (terminal)
exit(2);
}
int saved_stdout = dup(1);
int pid = fork();
if(pid > 0){
// Parent
pid = fork();
if(pid > 0){
// Parent
wait(NULL);
}else if (pid == 0){
// Child 1
printf("Child 1\n");
dup2(pipes[1], 1);
close(pipes[0]);
close(pipes[1]);
execlp("/bin/ps", "ps", "-aux", (char*) NULL);
exit(0);
}else{
fprintf(stderr, "FORK FAILED\n\n");
return 1;
}
}else if (pid == 0){
// Child 2
printf("Child 2\n");
dup2(pipes[0], 0);
close(pipes[0]);
close(pipes[1]);
dup2(saved_stdout, 1);
close(saved_stdout);
execlp("/bin/sort", "sort", "-r", "-n", "-k", "5", (char*)NULL);
exit(0);
}else{
fprintf(stderr, "FORK FAILED\n\n");
return 1;
}
wait(NULL);
printf("Exiting parent\n");
}
The output I get is this
Child 1
Child 2
Exiting parent
I doesn't actually print the execlp command, I've tried saving stdout to variable saved_stdout which is the solution I found in another answer, but that doesn't seem to work.
How can I redirect stdout back to the terminal?
Strange my output with your code is:
Child 1
Child 2
and the program don't stop. Or you sure that your output is valid ?
Whatever, your problem is that you don't close your pipe in your parents. Just add:
close(pipes[0]);
close(pipes[1]);
In your both parents (before your two call to wait()).
Plus saved_stdout is useless in your case, because you only change stdout in your child1. saved_stdout and 1 describe the same file in your child2.

Create child using fork() inside for loop to run execlp()

I am writing a C program which will run Linux commands, like:
$ cat /etc/passwd | cut -f1 -d: | sort
The idea is to create child process using fork() to run the commands using execlp(). I planned to use two pipes for the communication and direct the input-output using dup().
The output is wrong:
ls -l | wc -c on command returns 1746
the program returns 1761
The code(edited to reflect suggestions):
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <limits.h>
int main()
{
int i,fd1[2],status,listpid[2];
pid_t child;
pipe(fd1);
for(i=0; i< 2; i++)
{
printf("\ncreating child\n");
if((child = fork()) == -1)
{
perror("fork");
exit(EXIT_FAILURE);
}
else if(child == 0)
{
if(i == 0)
{
close(1); dup(fd1[1]);
close(fd1[0]);
close(fd1[1]);
printf("\nrunning ls -l\n");
fflush(stdout);
execlp("ls","ls","-l", (char *)NULL);
exit(EXIT_SUCCESS);
}
else if(i == 1)
{
close(0); dup(fd1[0]);
close(fd1[1]);
close(fd1[0]);
printf("\nrunning wc -c\n");
fflush(stdout);
execlp("wc","wc","-c", (char *)NULL);
exit(EXIT_SUCCESS);
}
}
else
{
listpid[i]=child;
}
}
close(fd1[0]);
close(fd1[1]);
for(i = 0; i < 2; i++)
{
waitpid(listpid[i], &status, 0);
if(WIFEXITED(status))
{
printf("\n[%d] TERMINATED (Status: %d)\n",listpid[i], WEXITSTATUS(status));
}
}
exit(EXIT_SUCCESS);
}
First you can't waitpid in the loop -- if the output of ls is big enough it ill fill the pipe, and so it won't finish until someone reads it: you must wait for both children after the for loop.
Second -- the wc will go on for as long as the other end of the pipe is open, ie you will have to close the pipe in the parent as well.
After your update, the two child processes are behaving correctly. However, you still need to add:
close(fd1[0]);
close(fd1[1]);
between the for loop that launches the children and the for loop that collects the exit statuses.
Because the write end of the pipe is still open, wc does not receive EOF, so it doesn't terminate, so your process is waiting indefinitely.

forking multiple processes and making the parent wait for all of them (in C)

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.

Resources