Read several times from pipe - c

I have two processes, one father and the other is the son, the son needs to access a text file, read it and send it to the father through a pipe, so he can print it. I don't know the size of the file so I guess the best way to do it is to send one line of the file at the time, but I dont know how to make the father known when to keep reading from pipe and when to stop.
Here's a sample of what I'm thinking:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <pwd.h>
#include <grp.h>
#include <dirent.h>
void main(int argc,char *argv[]){
int pipes[2];
pipe(pipes);
pid_t son;
son = fork();
if(son ==0){
FILE *file;
char buff[1020];
if ((file = fopen("path","r")) == NULL) printf("Error %s\n",strerror(errno));
close(pipes[0]);
while (fgets(buff,1020,file) != NULL){
write(pipes[1],buff,strlen(buff) +1);
}}
else{
char *bufftemp = malloc(sizeof(char)*1024);
close(pipes[1]);
while (read(pipes[0],bufftemp,1024) != 0 ){
printf("FATHER %s",bufftemp );
}
free(bufftemp);
printf("\n");
}
exit(0);
}
If I print the buff from the son, it prints each line, but the father seems to not read every time and it just reads the first line and thats it.

Related

Named pipe (FIFO) halts execution when read and write are called

Trying to create a process ring using named pipes for an assignment, and whenever I call read/write to those files it pauses the execution at that point. I've tried everything I could find for hours now, and have no idea why this is happening.
More context: Process ring with named pipes, passing a token between n processes, from a process i to i+1 in each loop.
Any help would be very much appreciated, and thank you for taking the time!
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <string.h>
#include <math.h>
#include <errno.h>
#define MAX 50
int main(int argc, char** argv) {
// File descriptors for pipes i and i+1 and token
int fd1, fd2, token = 0;
pid_t pid;
// Pipes path array
char* fifos[2][MAX] = { "pipe1to2" , "pipe2to1" }
...
// Create pipes
for(int i =0 ; i < 2 ; i++){
char* fileToCreate = fifos[i];
if ((mkfifo(fileToCreate,S_IRWXU)) != 0) {
if(errno == 17){ // If a file with the same name exists, this overwrites it
unlink(fileToCreate);
mkfifo(fileToCreate,S_IRWXU);
}else{
printf("Unable to create a fifo; errno=%d\n",errno);
exit(1);
}
}
}
while(true){
char* file = fifos[itr];
fd1 = open(file,O_WRONLY);
if(fd1 == -1){
printf("Open error\n");
return 1;
}
if(write(fd1,token,sizeof(int)) == -1){
printf("Write error");
return 2;
}
close(fd1);
...
}
}

Communicating through pipes and sending messages

I am having trouble communicating with the child process. I am trying to make quick.c simply get an input from stdin and send it to sand.c to capitialise it and send it back to the parent and then print it to stdout. Right now the program asks for an input twice instead of only asking once.
this is quick.c
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
int fds[2];
int test[2];
int pid;
pid_t child_a;
char buffer[50], buff[50];
if(pipe(fds)) {
perror("Pipe:");
exit(0);
}
if(pipe(test)) {
perror("Pipe:");
exit(0);
}
child_a = fork();
if (child_a == 0) {
//Child
FILE *f = fdopen(fds[0], "r");
FILE *e = fdopen(test[1], "w");
close(fds[1]);
close(test[0]);
//dup2(fds[0],0); causes infinite loop
dup2(test[1],1);
execlp("./sand", "sand", NULL);
fclose(e);
fclose(f);
} else {
// Parent
// Wrap the pipes
FILE *f = fdopen(fds[1], "w");
FILE *e = fdopen(test[0], "r");
close(fds[0]);
close(test[1]);
fgets(buffer,50, stdin);
fprintf(f,"%s",buffer);
while(fgets(buff, 50, e)) {
printf("Parent receive %s", buff);
}
fflush(stdout);
fclose(f);
fclose(e);
wait(NULL);
}
return 0;
}
This method is sand.c
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
int main(int argc, char *argv[]) {
char buffer[50];
int i = 0;
fgets(buffer,50,stdin);
char chr;
// Loop
while (buffer[i]) {
buffer[i] = toupper(buffer[i]);
i++;
}
fprintf(stdout,"%s",buffer);
return 0;
}
On running the code in my machine, the commented dup2 line does not loop to infinity. That may be because pipe programs run differently on different machines. However, the program terminates after taking input. Here are the things that are wrong with your code:
You aren't waiting for the child to write data to test pipe before printing in the parent. You must put the wait statement after taking input.
You've used file pointers for handling pipes. Pipes are accessed with file descriptors and cause unexpected results when handled with file pointers. Instead of fgets and fprintf, use read and write methods to work with file descriptors.
Error in the execlp command which I've commented.
There are errors regarding buffers, I've commented them in the code where they occur.
This is quick.c
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
int fds[2];
int test[2];
int pid;
pid_t child_a;
char buffer[50], buff[50];
if(pipe(fds)) {
perror("Pipe:");
exit(0);
}
if(pipe(test)) {
perror("Pipe:");
exit(0);
}
child_a = fork();
if (child_a == 0) {
//Child
//CHANGED: No need to open file pointers here. Pipes are already open and accessed by file descriptor instead of file pointer. File pointers create trouble when used with pipes. 0 is file descriptor of stdin, 1 for stdout.
close(fds[1]);
close(test[0]);
dup2(fds[0],0);
dup2(test[1],1);
//CHANGED: There was an error with the command you wrote.
//That's because ./sand arg will look for a 'sand' directory which doesn't exist
//This line will throw warnings because execlp requires needs a command as the second argument, but in this case the filename is the command.
//NOTE: before running quick.c, compile sand.c as sand.out and not a.out
execlp("./sand.out",NULL);
printf("Exec Error\n"); //this will only execute if execlp didn't run. Always have this line in your code to know what's happening.
}
else
{
// Parent
// Wrap the pipes
//Got rid of the file pointers
close(fds[0]);
close(test[1]);
//CHANGE: fgets is only used with file pointers. While handling pipes, we work with file descriptors, with which read and write methods are used
int n = read(0,buffer,50); //If this is new to you, I strongly recommend reading manual pages for read and write, but for right now
// The signature should be enough to understand - read/write(int file_descriptor, char *buffer, int number_of_bytes)
write(fds[1],buffer,n);
//MOST IMPORTANT: You need to wait for child after this point. Because test pipe doesn't have data yet which will be received by child.
wait(NULL);
//CHANGE: printf statements do not work well with buffere, because buffers are not terminated with null
//%s specifier will always look for a null or print garbage
//If you still want to use printf, look into $man bzero
while((n = read(test[0],buff, 50))>0)
{
write(1,buff,n);
}
fflush(stdout);
}
return 0;
}
This is sand.c
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
int main(int argc, char *argv[])
{
char buffer[50];
int i = 0;
int n = read(0,buffer,sizeof(buffer)); //changed fgets to read, to get number of bytes read.
char chr;
// Loop
// we have number of bytes. So change while to for
for (i=0;i<n;i++)
{
buffer[i] = toupper(buffer[i]);
}
write(1,buffer,n); //Changed fprintf to write to get rid of %s problem.
//Again, to fill remaining places of buffer with null, look up bzero.
//The reason I haven't done that is to not confuse you with so many changed methods.
return 0;
}
Let me know, if the solution also helps you find the source of the infinity loop.

fprintf returning null after fork() in c

I am trying to open a file with fopen, fork the current process and make the child process write something on the file; when the child process exits, the parents process should read the file's content, but it reads "null", even if the file has been correctly written. No errors are reported.
Here is my code:
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
int main(int argc, char *argv[]){
FILE * sharedFile;
char *fileContent;
if((sharedFile = fopen("testFile.txt","w+")) == NULL){
fprintf(stderr,"Error while opening the file: %s",strerror(errno));
return 0;
}
pid_t pid = fork();
if(pid == 0){
//Child
fprintf(sharedFile,"%s",argv[1]);
exit(0);
}
else{
//Parent
wait(NULL);
if(fscanf(sharedFile,"%s",fileContent) < 0){
fprintf(stderr,"Error while reading file: %s",strerror(errno));
return 0;
}
fprintf(stdout,"File content: %s",fileContent); //Outputs "File content: (null)"
fclose(sharedFile);
}
}
Oddly, if I open again the file in the parent's code after the fork, the output is correct.
What could be the problem?
fileContent has not been allocated any space. Perhaps do this
char fileContent[101];
...
if (fscanf(sharedFile,"%100s",fileContent) != 1) // Prevent buffer overflows. Should return one when successful.

Share a file descriptor between parent and child after fork and exec

I have two processes on Linux, A & B.
I want to share the file descriptor from process A with process B, now I just serialize it to a char* and pass it to the execl parameters, but that doesn't work.
A.c looks like this:
union descriptor{
char c[sizeof(int)];
int i;
} fd;
pid_t pid;
fd.i = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
// Perform other socket functions
pid = fork();
if(pid == 0){
// Read data from socket
if(execl("./B", fd.c, NULL) < 0){
exit(EXIT_FAILURE);
}else(
exit(EXIT_SUCCESS);
}
}else if(pid < 0){
exit(EXIT_FAILURE);
}else{
waitpid(pid, NULL, 0);
}
B.c looks like this:
union descriptor{
char c[sizeof(int)];
int i;
} fd;
memcpy(&fd.c[0], argv[0], sizeof(int));
write(fd.i, "TEST", 4);
close(fd.i);
But this doesn't work, and I don't really understand why not. How can I make this work? And if it works, is it the best solution to share a file descriptor between a parent and a child after a fork and a exec?
Update
The problem is unrelated to the question I asked, it is caused by a wrong way of passing an integer as pointed out by #OliCharlesworth. Please close this question.
File descriptors are always passed between a parent and child process
When you fork a process, the file descriptors that are open in the parent(at the time of fork()) are implicitly passed on to the child. There is no need to send them explicitly.
For example:
The pseudo-code looks as follows:
In process A:
fd = open_socket_or_file;
char str_fd[3];
str_fd[0]=fd;
str_fd[1]=fd;
str_fd[2]=0;
if(fork()==0)
{
execl("./B",str_fd,NULL);
}
In the child process B you can do:
int fd = argv[1][0];
/* now do whatever you want with the fd...*/
EDIT:
In case the processes are different, you need to pass the file descriptor explicitly. This is generally done using UNIX-Domain Sockets(If you are using Linux Flavors). For code related to this, you can see this answer
Yes that is true that file descriptors remain open even after fork or exec or fork and exec.You only need to know the value of fd in the new process image that was replaced using exec else put that fd on the one which is already known to that process(ex:0,1,2). So you can do this in two ways:
Placing the fd on either one of standard file descriptors using dup2(note:as far as i know you will be unable to reset that standard file descriptor for which it was actually known for)
Passing the fd as string argument for one of 6 exec functions does the job
Therefore I suggest you to use second method in case if you want standard fds remain
These are the two methods of implementation:
P1.c(using argument passing)
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
void main()
{
printf("Hello this is process 1\n");
int fd=open("./foo",O_RDONLY);
char buf[255];
//int n=read(fd,buf,255);
int h=fork();
if(h==0)
{
char *fname="./p2";
char *arg[3];
char targ[10];
sprintf(targ,"%d",fd);
arg[0]=fname;
arg[1]=targ;
arg[2]=NULL;
execvp(fname,arg);
}
else
{
printf("This is from p1 process\n");
//write(1,buf,strlen(buf));
//do some process with p1
printf("This is end of p1 process\n");
}
}
P1.c(using dup2 with 0)
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
void main()
{
printf("Hello this is process 1\n");
int fd=open("./foo",O_RDONLY);
int h=fork();
if(h==0)
{
dup2(fd,0);//note we will be loosing standard input in p2
execvp(fname,NULL);
}
else
{
printf("This is from p1 process\n");
//write(1,buf,strlen(buf));
//do some process with p1
printf("This is end of p1 process\n");
}
}
P2.c
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
int main(int argc,char *argv[])
{
int fd=atoi(argv[1]); //here fd=0 in case dup2 in process ps1.c
char buf[1024];
int n=read(fd,buf,1024);
buf[n]='\0';
printf("This is from p2\n");
write(1,buf,strlen(buf));
}

gzip with execlp in fork process

I have a problem i don't know how to solve in my code. I have to compress with gzip several arguments received form the command-line.
But i have to introduce in the command line the route and not the file. The sample i have prepared is working well but i'm indicating the name of the file and this is not correct.
Can you help me how to indicate the route and not the file? in the execlp. The route is argv[] but i don't know exactly how to build the sentence.
The code is:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/time.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
int p,pid[p];
int fills;
int ret=0;
char msg[100];
int status;
char filename[30];
if (argc>1)
fills=atoi(argv[p]);
if (argc==1)
{
printf("Error");
exit(1);
}
// Creem N parĂ metres
for(p=1; p<argc; p++)
{
pid[p] = fork();
if (pid[p]<0)
error("Error");
if (pid[p]==0)
{
memset(filename,0,sizeof(filename));
snprintf(filename,30,"ex1a.c");
ret=execlp("gzip", "gzip", "-9", "-f", filename, NULL);
if (ret < 0)
{
exit(EXIT_FAILURE);
}
printf("Process %d created process %d compressed file %s \n",getppid(),getpid(),argv[p]);
exit(p);
}
else
wait(status);
}
exit(EXIT_SUCCESS);
}

Resources