I'm trying to write a program in C that creates 2 child processes with each one of them executing an execvp.
My problem is that the first child writes too much input into the pipe from which the other child reads.
int main(int argc, char* argv[]){
//unnamed pipe
int pipeFd[2], statusFirst,statusSecond;
pid_t childPidOne,childPidTwo;
if(pipe(pipeFd) < 0){
perror("Pipe error:\n");
exit(EXIT_FAILURE);
}
switch(childPidOne = fork()){
case -1:
perror("First Fork error:\n");
exit(EXIT_FAILURE);
case 0:
printf("First child\n");
close(pipeFd[1]);
if( (execvp(argv[1], &argv[1])) < 0){
perror("First execvp error:\n");
}
printf("End First cild\n");
exit(0);
default:
//Do nothing
break;
}
switch(childPidTwo = fork()){
case -1:
perror("Second Fork error:\n");
exit(EXIT_FAILURE);
case 0:
printf("Second cild\n");
close(pipeFd[0]);
if( (execvp(argv[3], &argv[3])) < 0){
perror("Second execvp error:\n");
}
printf("End Second cild\n");
exit(0);
default:
//Do nothing
break;
}
close(pipeFd[0]);
close(pipeFd[1]);
if( (waitpid(childPidOne,&statusFirst,WUNTRACED | WCONTINUED)) < 0 ){
perror("First waitpid error:\n");
}else{
if (WIFEXITED(statusFirst)) {
printf("First exited, status=%d\n", WEXITSTATUS(statusFirst));
} else if (WIFSIGNALED(statusFirst)) {
printf("First killed by signal %d\n", WTERMSIG(statusFirst));
} else if (WIFSTOPPED(statusFirst)) {
printf("First stopped by signal %d\n", WSTOPSIG(statusFirst));
} else if (WIFCONTINUED(statusFirst)) {
printf("First continued\n");
}
}
if( (waitpid(childPidTwo,&statusSecond,WUNTRACED | WCONTINUED)) < 0 ){
perror("Second waitpid error:\n");
}
if (WIFEXITED(statusSecond)) {
printf("Second exited, status=%d\n", WEXITSTATUS(statusSecond));
} else if (WIFSIGNALED(statusSecond)) {
printf("Second killed by signal %d\n", WTERMSIG(statusSecond));
} else if (WIFSTOPPED(statusSecond)) {
printf("Second stopped by signal %d\n", WSTOPSIG(statusSecond));
} else if (WIFCONTINUED(statusSecond)) {
printf("Second continued\n");
}
exit(0);
return 0;
}
Maybe I have a wrong understanding of how pipe + fork + execvp work so let me tell you what I'm doing in my code:
I create an unnamed pipe - both childs use the same pipe
I'll create two childs by forking them
Since I execute my program like this: ./pipeline [FIRST SYSTEM CALL] | [SECOND SYSTEM CALL] or just to give you an example:./pipeline echo Hello | wc -m I close the reading site of the pipe
And then call execvp(argv[1], &argv[1])
And this is where the error happens (I guess):
I am never closing the writing side until the second child does because execvp will never return if it succeeds.
And I know that execvp will not close open file descriptors ( it can be closed by using a flag in fcntl as mentioned in What does the FD_CLOEXEC flag do? ).
Example
Let me give you an example.
echo Hello | wc -m
outputs the result
6
Because the system call wc (word count) counts the characters (-m) in a given String
That is correct because hello = 5 + 1 (which is \n or \0 I guess) and that makes 6.
Now, running my program gives the result
56
Or to get more information
echo hello | wc
outputs
1 (line) 1 (word) 6 (characters)
And ./pipeline echo hello | wc
outputs
3 (lines) 9 (word) 56 (characters)
I've searched for days but I can't figure it out.
Any ideas?
Thanks a lot!
Solved it myself.
Forgot to use dup2.
Just type the dup2 command after the close command and you will be fine.
Related
I'm trying to code a shell in C that supports multi-pipe process handling, depending on the amount of separate processes given by the user, and each separated by a "|" symbol. The shell forks the amount of processes into a child for each process.
Here's an example:
texto1.txt = "Isto é o Texto 1"
$ cat texto1.txt | grep -c Isto
result: 1
But I'm having trouble with communicating between children and finally to the parent process.
Here's my current code for the execpipe function, which executes the pipe process, being argv1 and argv2 the 2 separate processes given by user input: (Example: ls -l | wc -l)
int execpipe(char ** argv1, char ** argv2)
{
int fds[2];
pipe(fds);
int i;
pid_t p1, p2;
p1 = fork();
if (p1 == -1)
{ // error
char * error = strerror(errno);
printf("error fork!!\n");
return 1;
}
if (p1 == 0)
{ // child process
close(fds[0]);
dup2(fds[1], STDOUT_FILENO);
close(fds[1]);
if(execvp(argv1[0], argv1)<0){ // run command AFTER pipe character in userinput
char * error = strerror(errno);
printf("unknown command\n");
return 0;
}
}
else
{ // parent process
p2 = fork();
if(p2==0){
close(fds[1]);
dup2(fds[0], STDIN_FILENO);
close(fds[0]);
if(execvp(argv2[0], argv2)<0){ // run command AFTER pipe character in userinput
char * error = strerror(errno);
printf("unknown command\n");
return 0;
}
}else{ //Parent waits for both of it's children
wait(NULL);
wait(NULL);
}
}
}
The parent must close the pipe's write end to indicate the second process that no more data will come:
}
}else{ //Parent waits for both of it's children
close(fds[1]); // ++ add this line
wait(NULL);
wait(NULL);
}
Little note: instead of
char * error = strerror(errno);
printf("error fork!!\n");
why not
perror("fork");
I'm currently working on an assignment that is teaching us on how to implement pipes in my custom shell. Before I actually implement pipes on my shell and change my code, they want us to create two children, and run a command on each child while implementing a pipe:
Execute "ls -l" on child 1
Execute "tail -n 2" on child 2
Currently, my code looks like this:
int main (int argc, char * argv[]){
int debugMode=0;
int p[2];
int writeDup;
int readDup;
int status;
if (strcmp(argv[1],"-d")==0)
debugMode=1;
if (pipe(p)<0)
return 0;
int child1= fork();
if (child1 == 0)
{
if (debugMode == 1)
fprintf(stderr, "Child 1 is redirecting stdout to write end of pipe.\n");
fclose(stdout);
writeDup = dup(p[1]);
close(writeDup);
char *args[] = {"ls","-l",NULL};
if (execvp(args[0],args)<0){
if (debugMode ==1)
perror("ls -l failed ");
return 0;
}
}
else
{
if (debugMode == 1)
fprintf(stderr, "Parent process is waiting to close write end of pipe.\n");
while ((child1=waitpid(-1,&status,0))!=-1);
close(p[1]);
}
int child2 = fork();
if (child2 == 0)
{
fclose(stdin);
readDup = dup(p[0]);
close(readDup);
char *args[] = {"tail","-n","2",NULL};
if (execvp(args[0],args)<0){
if (debugMode ==1)
perror("tail -n 2 failed ");
return 0;
}
}
else{
if (debugMode == 1)
fprintf(stderr, "Parent process is closing read end of pipe.\n");
while ((child2=waitpid(-1,&status,0))!=-1);
close(p[0]);
}
if (debugMode == 1 && child1 != 0 && child2 !=0)
fprintf(stderr, "Waiting for child processes to terminate.\n");
while ((child1=waitpid(-1,&status,0))!=-1 && (child2=waitpid(-1,&status,0))!=-1 );
return 0;
}
However, while executing, I receive several errors:
ls: write error : bad file descriptor
tail: cannot fstat 'standard input': Bad file descriptor
tail: -: bad file descriptor
They requested us to close the standard inputs & outputs, so by doing so I assume that the program should default into reading/writing into the pipe. I'm continuing to try to find a solution, I would appreciate any help or direction!
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'm trying to solve this problem but I cannot.
Here is a short description:
We have a parent with two child processes (child_a, child_b) and N strings.
Current data: data[i] (0...N)
Parent starts and waiting for signal from child_b. Child_a is waiting.
Child_b sends a signal to parent and waiting for data.
Parent write data[i] into pipe and waiting
Child_b reads data[i] from pipe and printf(). Then waiting for Child_a
Child_a generates a random number (between 1-5) and write into pipe.
Child_b reads rand from pipe and sends to Parent.
Parent write "data[i] - rand" into a file.
And start from the begining with next data...
Here is my code:
FILE *fp2;
fp2 = fopen("bill.dat" , "a");
pid_t child_a, child_b;
int pipefd_a[2], pipefd_b[2];
char msg[100];
char sleep_time[10];
int stat;
signal(SIGUSR1, handler);
signal(SIGUSR2, handler);
if(pipe(pipefd_a) == -1 || pipe(pipefd_b) == -1){
perror("Error\n");
exit(EXIT_FAILURE);
}
int i;
for(i = 0; i < n; i++){
child_a = fork();
if(child_a < 0){
perror("Error\n");
}
if(child_a == 0){
sleep(1);
printf("Child_a-----\n");
srand(time(NULL));
int r = rand()%5+1;
char rand[2];
sprintf(rand, "%d", r);
printf("Child_a rand: %s\n", rand);
write(pipefd_b[1], rand, strlen(rand)+1);
printf("Child_a end-----\n");
exit(0);
}
else{
child_b = fork();
if(child_b == 0){
printf("Child_b sends a signal to parent\n");
kill(getppid(), SIGUSR1);
close(pipefd_a[1]);
read(pipefd_a[0], msg, sizeof(msg));
close(pipefd_a[0]);
printf("Child_b reads from pipe (from parent): %s\n", msg);
kill(child_a, SIGUSR2);
sleep(2);
read(pipefd_b[0], sleep_time, 10);
printf("Child_b reads from pipe (from child_a): %s\n", sleep_time);
fflush(NULL);
write(pipefd_b[1], sleep_time, sizeof(sleep_time));
close(pipefd_b[1]);
printf("Child_b end-----\n");
exit(0);
}
printf("============== %d ============== \n", i);
printf("Parent waiting for signal...\n");
pause();
printf("Signal received\n");
printf("Parent write into pipe\n");
close(pipefd_a[0]);
write(pipefd_a[1], data[i].address, 100);
kill(child_b, SIGUSR2);
waitpid(child_b, &stat, 0);
read(pipefd_b[0], msg, sizeof(msg));
fprintf(fp2, "%s - %s\n", data[i].address, msg);
printf("Parent writes into file: %s\n", msg);
}
}
fclose(fp2);
and my output (n = 2):
data1 - 1
data1 - 1
data1 - 1
data2 - 3
There are always 2^n lines in the file.
The assignment requires two child processes to achieve this and I suspect that the problem is with the "fork()" within the loop, but don't understand what I'm doing wrong.
The first problem is that you put your process creation in for loop.
That is why you get 2n lines. If you want to read some data form file N times you don't make N processes.
Second thing you need 3 pipes for your work. Pipe is unidirectional meaning one process can only write into pipe and the other can only read from it. One end is for writing and the other end is for reading, so you must close unused descriptors!
first pipe is used when parent writes data[i] into pipe and child_b reads
second pipe is used when child_a writes a random number into pipe and child_b reads
third pipe is used when child_b writes into pipe and parent performs reading
If you could write the whole program it would be easier form me to understand it and to help you.
This is skeleton how i would try to make this work
int pipe_parent_to_childB[2], pipe_childB_to_parent[2], pipe_childA_to_childB;
if(pipe(pipe_parent_to_childB) == -1 || pipe(pipe_childB_to_parent) == -1 ||pipe(pipe_childA_to_childB) == -1)
{
perror("Error\n");
exit(EXIT_FAILURE);
}
//close read end because parent will write to pipe
close(pipe_parent_to_childB[0]);
switch(fork()) //create child b
{
case -1:
//error
case 0:
//now you are in child_b
close(pipe_parent_to_childB[1]);
close(pipe_childA_to_childB[1]);
//perform some action
default:
break;
}
}
switch(fork()) //crete child a
{
case -1:
//error
case 0:
//now you are in child_a
close(pipe_childA_to_childB[0]);
//perform some action
default:
break;
}
//here you are in parent process again. Send signals, wait for signals and write to pipe here
//from parent you send some data through pipe to process child_b N times
//after this you close write end of the pipe descriptor.
I'm currently having a problem with the third process because it wont work every time when I run the program. And suggestions with the exit() part because is printing multiple child process! Any suggestions?
I would really APPRECIATE it a lot!
main(){
pid_t son;
int i;
for (i=0; i<3; i++){
switch (i){
case 0:
son = fork();
if (son<0){
fprintf(stderr, "Fork failed!");
//exit(-1);
}else if (son == 0){
execlp("/bin/cat", "cat", "wctrial.txt", NULL);
}else{
wait(NULL);
printf("Child process completed!");
//exit(0);
}
case 1:
son = fork();
if (son<0){
fprintf(stderr, "Fork failed!");
//exit(-1);
}else if (son == 0){
execlp("/bin/mkdir", "mkdir", "mydirectory", NULL);
}else{
wait(NULL);
printf("Child process completed!");
//exit(0);
}
case 2:
son = fork();
if (son<0){
fprintf(stderr, "Fork failed!");
//exit(-1);
}else if (son == 0){
execlp("/bin/wc","wc","wctrial.txt", NULL);
}else{
wait(NULL);
printf("Child process completed!");
//exit(0);
}
}
}
At least I don't see the break at the end of the each case.
In the case of 0 the program will run through all of your cases.
Actually break is the problem that if case 1 execute then 2,3 also will.(but this is not problem that wc not working)
Why wc is not working ?
Because of path of wc command!
In your system path for wc may not is: "/bin/wc"
Search tha path of wc command in your system like:
:~$ whereis wc
wc: /usr/bin/wc
and change
execlp("/bin/wc","wc","wctrial.txt", NULL);
^
as
execlp("/usr/bin/wc","wc","wctrial.txt", NULL);
^
// actually not exactly this but one that appears in your system.
Give it a try!!
Below are my suggestion ,
1st) suggestion would be the clean-up of child process once it is
done, as below,
}else if (son == 0){
execlp("/bin/mkdir", "mkdir", "mydirectory", NULL);
_exit(0);
}
2nd) do break after each switch statement
3rd) and also validate the path of executable by using "whereis"
command before feeding into execlp routine.