Program that read file and send it to parent process with pipe - c

I need to write a program that create pipe send filename from command line to child process. In child read that file and send it back using pipe. Parent process should print the file. if error occur in child process error must be send to parent process.
here is my code, it print some junk along file file (and also it disable scrolling in terminal emulator when I run it).
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
void main(int argc, char *argv[]) {
int pipefd[2];
char buff[100];
int childpid;
int size;
FILE *file;
if (argc != 2) {
printf("usage:\n%s <filename>\n", argv[0]);
exit(1);
}
if (pipe(pipefd) < 0) {
perror("can't open pipe\n");
}
if ((childpid = fork()) == 0) {
sleep(1);
size = read(pipefd[0], buff, sizeof(buff));
file = fopen(buff, "r");
if (file == NULL) {
write(pipefd[1], "Can't open file", 15);
exit(1);
}
while (!feof(file)) {
if (fgets(buff, sizeof(buff), file) == NULL) {
write(pipefd[1], "Error reading file", 18);
} else {
write(pipefd[1], buff, sizeof(buff));
}
}
} else if (childpid > 0) {
size = strlen(argv[1]);
if (write(pipefd[1], argv[1], size) != size) {
perror("Error writing to pipe\n");
}
wait(NULL);
while ((size = read(pipefd[0], buff, sizeof(buff))) > 0) {
write(1, buff, size);
}
}
exit(0);
}

Your program works as intended after quite a few changes. Lets list out what all changes are required and why-
I) Both in the child and parent, close the respective pipes as soon as you are done with them. From man page of read(3),
If some process has the pipe open for writing and O_NONBLOCK is clear,
read() shall block the calling thread until some data is written or
the pipe is closed by all processes that had the pipe open for
writing.
So do something like this in your code everywhere where the job pipes is over,
size = read(pipefd[0], buff, sizeof(buff));
close(pipefd[0]);
write(pipefd[1], buff, strlen(buff));
close(pipefd[1]);
if (write(pipefd[1], argv[1], size) != size) {
perror("Error writing to pipe\n");
}
close(pipefd[1]);
while ((size = read(pipefd[0], buff, sizeof(buff))) > 0)
{
write(1, buff, size);
}
close(pipefd[0]);
You hadn't closed the write end of of the pipe in the child and your parent was blocking in the read
II) You are using something like while(fgets(...)) in a loop to read data from file. This will bomb when there are newlines in the file and fgets returns multiple times, overwriting the buffer everytime during the process
I always use simple fgetc and feof combination to read from file. So, change your file reading mechanism to something like
unsigned count=0;
while (!feof(file) && count < sizeof(buff))
buff[count++]=fgetc(file);
if (feof(file))
buff[--count]=0;
else
buff[sizeof(buff)-1]=0;
III) While writing the file data from the child, you should use strlen(as we have already made sure buffer is null terminated, see above ) and not sizeof as the buffer may not be full at all and you will end up writing junk. So, change
write(pipefd[1], buff, sizeof(buff));
to
write(pipefd[1], buff, strlen(buff));
IV) Follow a safe exit from the child and parent after their job is done. Something like
close(pipefd[1]);
_exit(EXIT_SUCCESS); // in child
and
close(pipefd[0]);
exit(EXIT_SUCCESS); // in parent
PS: I've changed the file reading logic, so your compiler error is gone now and do follow the advice given by n.m.

You cannot write sizeof(buf) meaningful bytes if fgets returned less than that. The rest will be filled with junk.
Moreover, mixing string-oriented fgets with binary read/write is a bad style. Use read or fread to read the file. They return number of bytes read, use this number as an argument to write.

This code does not compile:
while (fgets(buff, sizeof(buff), file) != NULL) {
write(pipefd[1], "Error reading file", 18);
} else {
write(pipefd[1], buff, sizeof(buff));
}
You can't have an else clause there.

Related

pipe function returning random characters

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
int main(void){
//Variables, p[2] for each end of the pipe. nbytes to read pipe return value SUCCESS or FAILURE. pid_t to hold pid of fork process.
// buffer to hold response from the child process.
int p[2], nbytes;
pid_t childpid;
char string[] = "Hello, World!\n";
char buffer[80];
//Declaration of pipe
pipe(p);
//Error handling.
if(((childpid = fork()) == -1) || (pipe(p) == -1))
{
perror("fork");
exit(1);
}
//Child process sends message to paprent.
if(childpid == 0)
{
/* Child process closes up input side of pipe */
close(p[0]);
/* Send "string" through the output side of pipe */
write(p[1], string, (strlen(string)+1));
exit(0);
}
else
{
/* Parent process closes up output side of pipe */
close(p[1]);
/* Read in a string from the pipe */
nbytes = read(p[0], buffer, sizeof(buffer));
printf("Received string: %s", buffer);
}
return(0);
}
Output > Received string: #�=zJ
The point of the exercise is to have a child process send a message through a pipe to the parent process and the parent returns the result. This exact code worked the first time I ran it, but then when I tried to run it a second time it started to return seemingly random characters each time. I tried to copy my buffer to another variable but then it was empty. Is the pipe actually not function the way I think it is? What am I doing wrong?
You first create a pipe with pipe(p); and then you create another with ... || (pipe(p) == -1)) Is that deliberate?
2nd Pipe was causing an issue.
You have:
pipe(p);
//Error handling.
if(((childpid = fork()) == -1) || (pipe(p) == -1))
{
perror("fork");
exit(1);
}
This creates two pipes — one in the line pipe(p); and the second in the condition if(((childpid = fork()) == -1) || (pipe(p) == -1)). This is wasteful at best. Moreover, the second pipe is after the fork(), so the parent and child processes don't access the same pipe any more — you overwrote the one created before the fork() which they do share. Test the result of pipe() before calling fork() and remove the extra condition in the if test:
if (pipe(p) != 0)
{
perror("pipe");
exit(1);
}
if ((childpid = fork()) < 0)
{
perror("fork");
exit(1);
}
Get used to testing for errors and writing appropriate code to handle them. It will be a major part of your life as a C programmer.
Later on in the code, you have:
{
/* Parent process closes up output side of pipe */
close(p[1]);
/* Read in a string from the pipe */
nbytes = read(p[0], buffer, sizeof(buffer));
printf("Received string: %s", buffer);
}
You need to heed the value of nbytes. Since it is an int, you could use:
printf("Received %d bytes: [%.*s]\n", nbytes, nbytes, buffer);
This limits the output to what was read, and reports 0 if that's what it gets. I suppose you should also check for -1 in nbytes before using it in the printf() statement:
if (nbytes < 0)
{
fprintf(stderr, "failed to read from pipe descriptor %d\n", p[0]);
// Or perror("read");
// Should you exit here with a non-zero status?
}
else
printf("Received %d bytes: [%.*s]\n", nbytes, nbytes, buffer);
Note: errors are reported on stderr; perror() does that automatically.
The problem is that you create two pipes when you really only need to check the first for errors:
// Declaration of pipe
if(pipe(p) == -1) { // check for error here
perror("pipe");
exit(1);
}
// Error handling.
if((childpid = fork()) == -1) { // and don't create another pipe here
perror("fork");
exit(1);
}
You should also check the return values from write and read. They may not write or read the full string in one go.

Why does program hang on child to parent communication?

I am trying to understand why my program hangs. The Parent sends input froma
file it reads to the child program, and the child program will send the result of its computation back to it's parent. However, I have trouble sending the message back through a second pipe. The parent seems to hang when reading from the pipe.
From the other posts, I have read it seems to indicate that the parent should wait for the child to finish by using wait or waitpid (which in my case both of them does not resolve my issue).
I have notice by adding print statement that neither the PARENT or the CHILD finishes.. Could someone please explain to me why this is happening?
Why does this not work?
int main(int argc,char** argv) {
char buffer[1];
int i;
int fd1[2]; int fd2[2];
pipe(fd1); pipe(fd2);
pid_t pid;
// FIRST PROCESS.
// -------------------
pid = fork();
if(pid == 0) {
int cnt;
dup2(fd1[0], STDIN_FILENO);
dup2(fd2[1], STDOUT_FILENO);
for (i = 0; i < 2; i++) {
close(fd1[i]);
close(fd2[i]);
}
while(read(STDIN_FILENO, buffer, sizeof(buffer)) > 0) {
fprintf(stderr, "( %s )", buffer);
cnt = cnt + *buffer - 48;
}
write(STDOUT_FILENO, &cnt, sizeof(cnt));
exit(0);
}
// PARENT.
// ------------------------
int file = open(argv[1], O_RDONLY);
// READ THE FILE.
while(read(file, buffer, 1) > 0) {
if (48 <= *buffer && *buffer <= 57) {
// PIPE TO CHILD.
write(fd1[1], buffer, 1);
}
}
// WAIT FOR CHILD TO FINISH SENDING BACK.
// int status = 0;
// waitpid(pid, &status, 0);
// THIS BLOCK DOESN'T RESOLVE ANYTHING. IT HANGS AT WAIT OR WAITPID.
// **** THIS IS THE PART WHERE IT DOESN'T WORK.
while(read(fd2[0], buffer, 1) > 0) {
fprintf(stderr, "RESULT : %s", buffer);
}
// CLOSING PIPES
for (i = 0; i < 2; i++) {
close(fd1[i]);
close(fd2[i]);
}
close(file);
exit(0);
}
You aren't closing enough file descriptors in the parent soon enough.
Rule of thumb: If you
dup2()
one end of a pipe to standard input or standard output, close both of the
original file descriptors returned by
pipe()
as soon as possible.
In particular, you should close them before using any of the
exec*()
family of functions.
The rule also applies if you duplicate the descriptors with either
dup()
or
fcntl()
with F_DUPFD
Now, your child process is following the RoT perfectly. But the corollary for parent processes is that they need to close the unused ends of the pipe, and they must close the write end of a pipe that they use to signal EOF to the reading end of that pipe. This is where your code fails.
Arguably, before reading the file, the parent process should close the read end of the pipe it uses to write to the child, and it should close the write end of the pipe it uses to read from the child.
Then, after reading the whole of the file, it should close the write end of the pipe to the child, before going into the 'read from child' loop. That loop never terminates because the parent still has the write end of the pipe open, so there's a process that could (but won't) write to the pipe.
Also, since the child writes the bytes of an integer onto a pipe, the parent should read the bytes of an integer. Using char buffer[1]; with a %s format is pointless; you need a null terminator for the string, and a single char buffer can't hold both a null byte and any data.
Along with various other improvements ('0' instead of 48, for example), you might end up with:
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char** argv)
{
if (argc != 2) {
fprintf(stderr, "Usage: %s filename\n", argv[0]);
exit(EXIT_FAILURE);
}
int fd1[2];
int fd2[2];
char buffer[1];
pipe(fd1);
pipe(fd2);
pid_t pid = fork();
if (pid == 0) {
int cnt = 0;
dup2(fd1[0], STDIN_FILENO);
dup2(fd2[1], STDOUT_FILENO);
for (int i = 0; i < 2; i++) {
close(fd1[i]);
close(fd2[i]);
}
while (read(STDIN_FILENO, buffer, sizeof(buffer)) > 0) {
fprintf(stderr, "(%c)", buffer[0]); // Changed
cnt = cnt + buffer[0] - '0';
}
putc('\n', stderr); // Aesthetics
write(STDOUT_FILENO, &cnt, sizeof(cnt));
exit(0);
}
int file = open(argv[1], O_RDONLY);
if (file < 0) {
fprintf(stderr, "failed to open file '%s' for reading\n", argv[1]);
exit(EXIT_FAILURE);
}
close(fd1[0]); // Added
close(fd2[1]); // Added
while (read(file, buffer, sizeof(buffer)) > 0) {
if ('0' <= buffer[0] && buffer[0] <= '9') {
write(fd1[1], buffer, sizeof(buffer));
}
}
close(file); // Moved
close(fd1[1]); // Added
// Rewritten
int result;
while (read(fd2[0], &result, sizeof(result)) == sizeof(result)) {
fprintf(stderr, "RESULT : %d\n", result);
}
close(fd2[0]); // Added
// Close loop removed
return 0;
}
If that is stored in file pipe71.c and compiled, I get the following outputs when it is run:
$ ./pipe71 pipe71.c
(2)(0)(1)(2)(2)(2)(1)(1)(2)(0)(0)(2)(1)(0)(2)(2)(1)(0)(2)(1)(2)(0)(0)(0)(0)(0)(1)(0)(1)(1)(0)(2)(1)(0)(0)(0)(0)(9)(1)(1)(1)(1)(2)(0)(2)(0)(0)
RESULT : 49
$ ./pipe71 pipe71
(0)(0)(8)(0)(0)(2)(2)(0)(8)(1)(1)(5)(1)(1)(1)(1)(5)(1)(1)(1)(8)(5)(1)(9)(8)(5)(1)(1)(0)(4)(4)(4)(6)(0)(2)(8)(0)(0)(0)(2)(7)(1)(3)(8)(3)(0)(4)(3)(0)(4)(9)(0)(0)(0)(0)(7)(1)(9)(8)(1)(3)(0)
RESULT : 178
$

Unable to read from a pipe after something is written to it by a child process

I create a function exec_in_child which takes the command arguments, pipe file descriptors (fds), read_flag and write_flag as input. When write_flag is set to 1, the child process should duplicate stdout to fds[1], and then execute the command. When read_flag is set to 1, the child should duplicate the stdin to fds[0] and the execute the command.
Do I have to close one end of the pipe when I'm reading/writing to
the other end?
The code below doesn't work. I'm trying to execute /bin/ls inside a child process, write the stdout to the pipe, and then read
it off in the parent process and print it. I'm not able to read in
the parent process.
Can I read and write to the pipe inside the same process without closing other? This situation arises when I want to child to read
from pipe, execute, and then write to the pipe.
#include <stdio.h> /* printf */
#include <stdlib.h>
#include <string.h> /* strlen, strcpy */
int exec_in_child(char *arguments[], const int temp[], int , int);
int main()
{
ssize_t bytes_read;
char *curr_dir = (char *)malloc(500);
int pipefd[2];
if (pipe(pipefd) == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}
char *arguments[] = {"/bin/pwd",0};
exec_in_child(arguments, pipefd, 0, 1);
bytes_read = read(pipefd[0], curr_dir, strlen(curr_dir));
printf("%s = %d\n", "bytes read from pipe" ,(int)bytes_read);
printf("%s: %s\n","character read from the pipe",curr_dir);
return 0;
}
int exec_in_child(char * arguments[], const int fds[], int read_flag, int write_flag) {
pid_t pid;
pid = fork();
if (pid < 0) {
perror("Error: Fork Failed");
}
else if (pid == 0){ /*inside the child process */
if (read_flag == 1) {
dup2(fds[0], 0);
perror("Dup2 stdin");
}
if (write_flag == 1) {
dup2(fds[1], 1);
perror("Dup2 stdout");
}
execv(arguments[0], arguments);
perror("Error in child");
exit(1);
} /* if (pid == 0) */
else {
while(pid != wait(0));
} /* if(pid < 0) */
return 0;
}
I get this result:
hmwk1-skk2142(test) > ./a.out
Dup2 stdout: Success
bytes read from pipe = 0
character read from the pipe:
To answer your questions:
1) You do not need to close either end of the pipe in order to use the other end. However, you generally want to close any end(s) of the pipe you're not using. The biggest reason to do this is that the pipe will only close when all open write file descriptors are closed.
2) Your code isn't working because you're using strlen() improperly. This function calculates the length of a string by searching for the null (0) character. When you malloc() the storage for curr_dir you have no guarantee of what resides there (though it will usually be zeroed, as in this case).
Thus, your call strlen(curr_dir) returns zero, and the read() system call thinks you want to read up to zero bytes of data. Change your read call to the following:
bytes_read = read(pipefd[0], curr_dir, 500);
and your code will work perfectly.
3) You can read and write to any pipe you've got a valid file descriptor to. A single process can absolutely read and write the same pipe.

C: redirecting stdin, stdout to pipes

I'm in the process of properly understanding pipes and FDs and I'm trying to program the following thing:
The program basically compresses stuff like gzip does with the options -cf.
The basic idea is:
I create two pipes in the parent process, then I fork it twice so that I'll have two children. In the first child, I redirect the first pipe's read end to stdin, and the second pipe's write end to stdout. Then I exec gzip with the -cf options so that it'll write to stdout (now the writing end of pipe2)
In the second child, I read from pipe2 and either output it directly or save it to a file.
The problem is, however, that no data arrives at the second child and I'm not really sure why. Here's the code:
int main(int argc, char **argv) {
char *file;
int out = 0;
if(argc == 2) {
file = argv[1];
out = 1;
} else if (argc > 2) {
exit(EXIT_FAILURE);
}
int c1pipe[2];
int c2pipe[2];
pipe(c1pipe);
pipe(c2pipe);
int f;
for(int i = 0; i < 2; i++) {
switch(f = fork()) {
case 0: //child
if(i == 0) { //first loop iteration, child 1
close(c1pipe[1]);
dup2(c1pipe[0], fileno(stdin));
close(c1pipe[0]);
close(c2pipe[0]);
dup2(c2pipe[1], fileno(stdout));
close(c2pipe[1]);
execlp("gzip", "gzip", "-cf", (char *) NULL);
} else if (i == 1) { //second loop iteration, child2
close(c1pipe[0]);
close(c1pipe[1]);
close(c2pipe[1]);
FILE *read = fdopen(c2pipe[0], "r");
char buffer[1024];
if(out == 0) { //output to stdout
while(fgets(buffer, 1024, read) != NULL) {
fprintf(stdout, "%s", buffer);
fflush(stdout);
}
} else { //write to specified file
FILE *writeto = fopen(file, "w");
while(fread(buffer, sizeof(char), strlen(buffer)+1, read) > 0) {
fwrite(buffer, sizeof(char), strlen(buffer)+1, writeto);
fflush(writeto);
}
fclose(writeto);
}
close(c2pipe[0]);
fclose(read);
}
break;
case -1: //err
//not implemented
break;
default: //parent
if(i == 0) {
close(c2pipe[0]);
close(c2pipe[1]);
close(c1pipe[0]);
FILE *writer;
writer = fdopen(c1pipe[1], "w");
char buffer[1024];
while(fgets(buffer, sizeof buffer, stdin) != NULL) {
fwrite(buffer, sizeof (char), strlen(buffer)+1, writer);
}
close(c1pipe[1]);
fclose(writer);
}
break;
}
}
return 0;
}
Please excuse the missing error handling as I wanted to create a quick-and-dirty version.
Any help is appreciated.
In the parent process, you are closing both ends of c2pipe before you have forked the second child.
You'd probably have figured this out already if you had put any error handling in on any of the read/write calls. In fact, if you checked for an error on the dup2 calls and then looked at errno, you probably would have found that it was EBADF (bad file descriptor).
Another issue is that your parent process exits before it knows that both child processes have finished. This means that the child processes will receive a signal and will themselves be terminated. The parent needs to call one of the variants of wait() to make sure both children have gone.

File pointer goes to end after read by child

Parent has opened a file to read, I fork two children to read from file and write on different files.
child 1 reads the first line, and child 2,reads nothing. When I do an ftell, it reaches the end.
Can anyone please explain this behaviour?
f[0] = fopen("input", "r");
for ( i = 1; i <= 2; i++ ){
if ((pid = fork()) != 0){
waitpid(pid);
}
else
{
snprintf ( buffer, 10, "output%d", i );
printf("opening file %s \n",buffer);
f[i] = fopen( buffer, "w");
fgets(buff2, 10, f[0]);
fprintf(f[i], "%s", buff2);
fclose(f[i]);
_exit(0);
}
}
fclose(f[0]);
Your problem is buffering. stdio reads files on fully buffered mode by default, which means a call to fgets(3) will actually read a huge block of characters from the file, buffer everything, and then return the first line, while leaving the rest in the buffer, in the perspective of being called again in the future (remember that stdio strives for minimizing the number of read(2) and write(2) syscalls). Note that stdio buffering is a user-space thing; all the kernel sees is a single process reading a huge block on that file, and so the cursor is updated accordingly.
Common block sizes are 4096 and 8192; your input file is probably smaller than that and so the first process that calls fgets(3) ends up reading the whole file, leaving the cursor in the end. Buffering is tricky.
What can you do? One solution I can think of is to disable buffering (since this is an input stream we're talking about, we can't use line buffered mode, because line buffering is meaningless for input streams). So if you disable buffering on the input stream before forking, everything will work. This is done with setvbuf(3).
Here's a working example:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
static FILE *f[3];
static char buffer[128];
static char buff2[128];
int main(void) {
pid_t pid;
int i;
if ((f[0] = fopen("input", "r")) == NULL) {
perror("Error opening input file");
exit(EXIT_FAILURE);
}
if (setvbuf(f[0], NULL, _IONBF, 0) < 0) {
perror("setvbuf(3) failed");
exit(EXIT_FAILURE);
}
for (i = 1; i <= 2; i++) {
if ((pid = fork()) < 0) {
perror("fork(2) failed");
exit(EXIT_FAILURE);
}
if (pid != 0) {
if (waitpid(pid, NULL, 0) < 0) {
perror("waitpid(2) failed");
exit(EXIT_FAILURE);
}
} else {
snprintf(buffer, sizeof(buffer), "output%d", i);
printf("opening file %s\n", buffer);
if ((f[i] = fopen(buffer, "w")) == NULL) {
perror("fopen(2) failed");
exit(EXIT_FAILURE);
}
errno = 0;
if (fgets(buff2, sizeof(buff2), f[0]) == NULL) {
if (errno != 0) {
perror("fgets(3) error");
exit(EXIT_FAILURE);
}
}
fprintf(f[i], "%s", buff2);
fclose(f[i]);
exit(EXIT_SUCCESS);
}
}
fclose(f[0]);
return 0;
}
Note that this may incur a significant performance hit. Your code will be making a lot more syscalls, and it might be too expensive for huge files, but it doesn't seem to be a problem since apparently you're dealing with relatively small input files.
Here's an extract of my fork() man page:
The child process has its own copy of the parent's descriptors. These descriptors reference the same underlying objects, so that, for instance, file pointers in file objects are shared between the child and the parent, so that an lseek(2) on a descriptor in the child process can affect a subsequent read or write by the parent. This descriptor copying is also used by the shell to establish standard input and output for newly created processes as well as to set up pipes.
So your behaviour is completely normal. If you want your child to have its own file descriptor, it should open its own file.
For example, you could do the following:
for ( i = 1; i <= 2; i++ )
{
if ((pid = fork()) != 0)
{
waitpid(pid);
}
else
{
f[0] = fopen("input", "r"); // New
snprintf ( buffer, 10, "output%d", i );
printf("opening file %s \n",buffer);
f[i] = fopen( buffer, "w");
fgets(buff2, 10, f[0]);
fprintf(f[i], "%s", buff2);
fclose(f[i]);
fclose(f[0]); //New
_exit(0);
}
}
Also, you should check for errors (almost all the functions in your else could fail with error).

Resources