Stuck in a while loop after forking - c

Im trying to solve this exercise:
"make a program that receives as argument 2 other programs (ex:"flow ls wc"). It should run both programs, using the output of the 1st program as the input of the 2nd and measure the amount of data sent. Print that amount every second on your stdout."
I have this:
int fluxo = 0;
void fa(){
printf("\nFlux: %d\n", fluxo);
fluxo = 0;
alarm(1);
}
int main(int argc,char **argv){
int pd[2];
pid_t p;
pipe(pd);
char *buf;
signal(SIGALRM,fa);
alarm(1);
if( (p=fork())==0 ){
close(pd[0]);
dup2(pd[1],1);
close(pd[1]);
execlp(argv[1],argv[1],NULL);
exit(EXIT_FAILURE);
}
else{
wait(NULL);
while(read(pd[0],&buf,1)==1)
fluxo++;
close(pd[1]);
dup2(pd[0],0);
close(pd[0]);
execlp(argv[2],argv[2],NULL);
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
I'm using that while loop to measure the amount of data. However i'm getting stuck on that while even after the execlp in the child process sent it's data, and therefore it's not executing the 2nd program.. It's stuck the while loop even after reading all of the data in the pipe.. Why?

First of all, you are declaring char* buf, but your pointer isn't allocated. I think you meant to use char buf (since you are using &buf in the read).
Your while loop isn't stucked, the call to read is. In fact, your parent process still has pd[1] descriptor opened when you are calling readfunction. So when your buffer is empty, it will wait.
You should replace this :
else{
wait(NULL);
while(read(pd[0],&buf,1)==1)
fluxo++;
close(pd[1]);
dup2(pd[0],0);
close(pd[0]);
execlp(argv[2],argv[2],NULL);
exit(EXIT_FAILURE);
}
by this :
else{
close(pd[1]);
wait(NULL);
while(read(pd[0],&buf,1)==1)
fluxo++;
dup2(pd[0],0);
close(pd[0]);
execlp(argv[2],argv[2],NULL);
exit(EXIT_FAILURE);
}
EDIT:
Note:
Reading byte per byte is not efficient, you will get better performance with a larger buffer. For example:
char buf[1024]
and your loop :
for(;;){
ssize_t rd = read(pd[0], buf, 1024);
if(rd==-1){
perror("Reading");
exit(EXIT_FAILURE);
}
if(rd==0) break;
fluxo+=rd;
}

Related

modify text in child process to use in parent

I am trying to create a simple pipe/fork function, so that child process modifies the value of text, then it is printed by the parent.
I have checked a similar question on Modify variable in child process, but I am unable to print the text variable in the parent.
int main()
{
pid_t childp;
char text[100];
pipe(text);
childp = fork();
if (childp ==0){
strncpy(text, "Hello world", 100); // child running
}
else{
printf("%s\n", text); // parent prints "Hello world"
}
return 1;
}
Any help is appreciated (I am very new to C language)
look into this website :
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main(void)
{
int fd[2], nbytes;
pid_t childpid;
char string[] = "Hello, world!\n";
char readbuffer[80];
pipe(fd);
if((childpid = fork()) == -1)
{
perror("fork");
exit(1);
}
if(childpid == 0)
{
/* Child process closes up input side of pipe */
close(fd[0]);
/* Send "string" through the output side of pipe */
write(fd[1], string, (strlen(string)+1));
exit(0);
}
else
{
/* Parent process closes up output side of pipe */
close(fd[1]);
/* Read in a string from the pipe */
nbytes = read(fd[0], readbuffer, sizeof(readbuffer));
printf("Received string: %s", readbuffer);
}
return(0);
}
It explain how to properly use C pipes. Take a look to this too.
Add this for error handling :
if (pipe(fd) == -1) {
fprintf(stderr, "Pipe Failed");
return 1;
}
The closest to your code working example I can come up with.
int main()
{
pid_t childp;
char text[100];
int fd[2];
pipe(fd);
childp = fork();
if (childp ==0){
FILE *f=fdopen(fd[1], "w"); // Write into this "file" what you want the other end to read.
fprintf(f, "Hello world\n");
}
else{
FILE *f=fdopen(fd[0], "r"); // We can read from this "file"
fgets(text, 100, f); // Read one line of text from the "file" up to 100 bytes
printf("read <%s> from by new child\n", text)
}
return 1;
}
Note again that this is not a shared buffer. So you need both end to agree on a "protocol". Because everything that is written by one end must be read by the other (otherwise the "write" instruction will be blocked), and everything that is read by one end, must be writter by the other (otherwise the "read" instruction will be blocked).
So, either you use a fixed size message, for example. If you choose 100, you need to write 100 bytes exactly at one end (fill with 0 if needed), and read 100 bytes exactly at the other.
Or you find some protocol so that the reading end knows exactly when to stop reading.
I choose the latter (because it is the closest to your code). By using fgets to read, that stop to read at each newline, and fprintf a message ended by a newline at the writing end.

c - execl`d program doesn't give promt back

EDIT: THE QUESTION IS ANSWERED IN COMMENTS
So, i'm studying pipes. Long story short, i have two programs:
first program creates a pipe and two forks: first fork closes read descriptor and writes some stuff to write one (then closes it), second fork closes write one, dup2 on read side of pipe to standard input (end closes read side itself) and execl second program, giving a size of a text the first fork writes as an argument; the parent closes both pipe sides and waitpids for child that wasexecld (second one).
second program just reads from its standard input (pipe side) the stuff and writes it out to standard output, then closes pipe side just in case.
In such a setup everything works as I intended, but when I delete waitpid in first program (or just wait for the first child that writes instead of the second one), the second one behaves weirdly - it executes till the end, passing through all the IO (that is, the printf before exit got executed), and then doesn't give me the prompt back. That is, the terminal looks like the program awaits for an input from standard input. If i execute the first program without execl, then everything works fine, If I execute just the second one with one argument, then it waits only until input is provided to standard input (as it should as it is not a part of a pipe in this case).
As i know, when parent terminates, the child is "inherited" by init and got waited. But even if it wasn't, that is, even if it remained as a zombie, then it still would be weird - why i cannot get my prompt back until i wait explicitly?
The code is below (of the setup that works correctly):
First program
/* headers */
int main(void)
{
int fildes[2];
pid_t p1, p2;
int status;
char mess[] = "written from execved program!\n";
int buf = strlen(mess);
if(pipe(fildes) == -1) {
perror("pipe in main");
exit(EXIT_FAILURE);
}
p1 = fork();
if(p1 == -1) {
perror("fork p1 in main");
exit(EXIT_FAILURE);
}
else if (p1 == 0) {
printf("Child 1!\n");
close(fildes[0]);
write(fildes[1], mess, buf);
close(fildes[1]);
printf("Before exit in child 1!\n");
exit(EXIT_SUCCESS);
}
p2 = fork();
if(p2 == -1) {
perror("fork p2 in main");
exit(EXIT_FAILURE);
}
else if (p2 == 0) {
printf("Child 2!\n");
dup2(fildes[0], 0);
close(fildes[0]);
close(fildes[1]);
char s_buf[30];
sprintf(s_buf, "%d", buf);
execl("./pipe2slave", "pipe2slave", s_buf, (char *) 0);
perror("execl have returned");
exit(EXIT_FAILURE);
}
close(fildes[0]);
close(fildes[1]);
/*
below if I wait for, say, p1, or don't wait it all,
the weird behavior described in my question happens
*/
if(waitpid(p2, &status, 0) == -1) {
perror("waitpid in main");
exit(EXIT_FAILURE);
}
if(WIFEXITED(status))
printf("pipe2slave exit status is %d\n", WEXITSTATUS(status));
printf("End of main in pipe2!\n");
exit(EXIT_SUCCESS);
}
Second program
/* headers */
int main(int argc, char **argv)
{
if (argc != 2) {
perror("pipe2slave - not enough args");
exit(EXIT_FAILURE);
}
printf("program name is %s\n", argv[0]);
int buf = atoi(argv[1]);
printf("%d\n", buf);
char mess_in[buf];
read(0, mess_in, buf);
write(1, mess_in, buf);
fsync(1);
close(0);
printf("end of slave!\n");
exit(EXIT_SUCCESS);
}
Thank you in advance!

How to work with two pipes while sending data between child and parent process?

I am learning about system calls, fork and pipe. I am creating a C program in which the parent process sends a character array to the child process and child process capitalizes the first 4 characters of the array and sends it back. The array is sent properly from parent to child, child makes the conversion and even writes to the second pipe properly, but parent process is not able to read the new array from the pipe 2.
I've tried closing the unnecessary descriptors as well, but that didn't work. I read somewhere that parent process might be finishing before there is something to read from the pipe, for which I tried wait function(but I might have done this wrong. I am not sure.)
I tried checking the size of values sent and received by the processes,
Parent writes (8)
Child reads (8)
Child writes (8)
Parent reads (1)
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
int main(int argc, char *argv[]){
int pipe1[2];
int pipe2[2];
char str[8], str1[8];
pid_t pid;
if(pipe(pipe1) < 0){
perror("Pipe 1 not created\n");
exit(EXIT_FAILURE);
}
if(pipe(pipe2) < 0){
perror("Pipe 2 not created\n");
exit(EXIT_FAILURE);
}
pid = fork();
if (pid == 0){
close(pipe1[1]);
close(pipe2[0]);
printf("\nChild Process");
ssize_t rd_stat_child = read(pipe1[0], str, 8*sizeof(char));
if(rd_stat_child > 0){
printf("rc%zd\n", rd_stat_child);
for(int i = 0; i < 4; i++){
str[i] = ((char)(((int)str[i])-32));
}
printf("\nFinal str in child: %s\n", str);
ssize_t wr_stat_child = write(pipe2[1], str, 8*sizeof(char));
printf("wc%zd\n", wr_stat_child);
if(wr_stat_child != sizeof(str)){
perror("Sending to parent failed");
exit(EXIT_FAILURE);
}
}else{
perror("Child failed to read");
exit(EXIT_FAILURE);
}
}else if (pid > 0){
close(pipe1[0]);
close(pipe2[1]);
printf("\nParent Process");
printf("\nEnter a 8 character string: ");
scanf("%s", str);
if(sizeof(str)/(8*sizeof(char)) != 1){
perror("Size of string greater than 8\n");
exit(EXIT_FAILURE);
}else{
ssize_t wr_stat_parent = write(pipe1[1], str, 8*sizeof(char));
printf("wp%zd\n", wr_stat_parent);
if(wr_stat_parent != sizeof(str)){
perror("Parent failed writing.\n");
exit(EXIT_FAILURE);
}
ssize_t rd_stat_parent = read(pipe2[0], str, 8*sizeof(char));
close(pipe2[0]);
if(rd_stat_parent <= sizeof(str)){
printf("rp%zd\n", rd_stat_parent);
printf("\nParent Recieved\n %s", str);
}else{
perror("Parent error while reading\n");
exit(EXIT_FAILURE);
}
}
}
return 0;
}
Expected Output
Parent Process
(input) >> lkybzqgv
Child Process
(process) >> LKYBzqgv
Parent Process
(output) >> LKYBzqgv
Actual Output
Parent Process
(input) >> lkybzqgv
Child Process
(process) >> LKYBzqgv
Parent Process
(output) >> kybzqgv
Your string-handling is broken. You need an array of length 9 to hold a string of length 8. (Remember that strings in c are zero-terminated). DO NOT WRITE scanf("%s", str); TO READ STRINGS !! That is just as bad as using gets(). It allows you to overflow the buffer (Which actually happens in your case). Read strings like this:
scanf("%8s", str);
This will read at most 8 (non-whitespace) characters and store them together with the zero-termination in str. (remember again that str must be large enough for 8 charecters + 1 termination character)
Then to check the length of a string, use strlen(), do not use sizeof(). sizeof may only tell the size of the array holding the string, or the pointer pointing to the string. Remember that the array holding the string must be at least 1 character larger than the string, but is allowed to be larger than that. And the size of the array is fixed at creation. It doesn't change size depending on what you put in it.
Oh, and by the way. You don't send/receive the termination character, so you have to set it yourself manually after you have called read():
read(pipe1[0], str, 8);
str[8] = 0;
There may be other problems with your code, but unless you fix the string-issues, you have undefined behavior, and everything else doesn't really matter.

Using bc through pipes and fork

I'm trying to get the answer of a char expression using bc through pipes.
I would like first to write the expression in the pipe1, which the bc will read and write the answer in pipe2. For this I am changing the input and output. This does work if I do not use a char[] and just put the expression in the write :
write(pipe1[1], "20*5\n", sizeof("20*5\n")-1) != sizeof("20*5\n")-1)
But if I declare a tab, I keep getting the error :
(standard_in) 2: illegal character: ^#
Sometimes it is 1 instead of 2
What I am doing wrong? If someone could explain me, thank you.
Code :
#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);
}
char* expression = "20*5\n";
if (resultat_fork != 0)
{
//printf("I am the parent\n");
close(pipe1[0]);
close(pipe2[1]);
if (write(pipe1[1], expression, sizeof(expression)) != sizeof(expression))
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;
}
^# is the nul character i.e. '\0'. This would suggest you are overrunning the end of the string when you write it to bc. The problem is here:
sizeof(expression)
expression is not an array, it is a char pointer that points to the first character of the string "20*5\n" and, if you are on a 64 bit machine, its size is 8. To get the length of the string to send, use strlen(expression) instead.
Another thing you need to do, not related to your problem is, in the parent process, after you've read the answer, wait for the child process to finish. Otherwise, you'll be left with a zombie.
This is wrong
if (write(pipe1[1], expression, sizeof(expression)) != sizeof(expression))
You are using sizeof operator with expression which is a char*, if your system is 32-bit, it will result in 4, which is the size of any pointer variable in your code.
You need to use strlen(expression) instead of sizeof.
As pointed out in the answer as well, you need to wait for child process to finish its execution and terminate, if the parent process has more than one child processes. Ideally, you should also check the return value of wait, which provides you more context on how the child process was terminated.

Sending multiple strings using pipes to child process

I have a task in Linux and I can't get it work.
I have a program that receives a text file as parameter. It then creates a child process using fork() and sends to the child process, line by line the content of the text file received as parameter. The child process needs to count the lines and return to the parent process the number of lines received.
This is what I have until now, but somewhat the child process does not receive all the lines. For my test I used a text file with 9 lines. The parent sent 9 lines as strings but the child process received only 2 or 3 of them.
What am I doing wrong?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
char string[80];
char readbuffer[80];
int pid, p[2];
FILE *fp;
int i=0;
if(argc != 2)
{
printf("Syntax: %s [file_name]\n", argv[0]);
return 0;
}
fp = fopen(argv[1], "r");
if(!fp)
{
printf("Error: File '%s' does not exist.\n", argv[1]);
return 0;
}
if(pipe(p) == -1)
{
printf("Error: Creating pipe failed.\n");
exit(0);
}
// creates the child process
if((pid=fork()) == -1)
{
printf("Error: Child process could not be created.\n");
exit(0);
}
/* Main process */
if (pid)
{
// close the read
close(p[0]);
while(fgets(string,sizeof(string),fp) != NULL)
{
write(p[1], string, (strlen(string)+1));
printf("%s\n",string);
}
// close the write
close(p[1]);
wait(0);
}
// child process
else
{
// close the write
close(p[1]);
while(read(p[0],readbuffer, sizeof(readbuffer)) != 0)
{
printf("Received string: %s\n", readbuffer);
}
// close the read
close(p[0]);
}
fclose(fp);
}
A pipe is a unidirectional interprocess communication channel. You have to create 2 pipes, one to speak to the child process, the other to read data back.
Remember to close the unused side of the pipe on both processes.
You are sending the null terminator to the other process:
write(p[1], string, (strlen(string)+1));
That makes the result confusing because when you print what you've received, you only see up to the null terminator.
If you do this instead:
write(p[1], string, strlen(string));
you should get what you expect.
You're not counting the number of lines, you're counting the number of times read(2) returns.
When using pipes, read(2) will pull as much data as possible from the pipe: min(pipe_available, space_available). It doesn't care for newlines, 0 bytes etc. Simple tricks to make it work:
Use a loop to walk readbuffer and look for \n
Use fdopen + fgets (I have a feeling this is probably flawed)
look into manpage of pipe ( man 2 pipe ), the program you're trying to write is as an example there, compare it with yours :)

Resources