I am trying to read from a file, write it to a pipe, and in a child process read from the pipe and write it to a new file. The program is passed two parameters: the name of the input file, and the name of the file to be copied to. This is a homework project, but I have spent hours online and have found only ways of making it more confusing. We were given two assignments, this and matrix multiplication with threads. I got the matrix multiplication with no problems, but this one, which should be fairly easy, I am having so much trouble with. I get the first word of the file that I am copying, but then a whole bunch of garble.
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
if(argc < 3) {
printf("Not enough arguments: FileCopy input.txt copy.txt\n");
exit(0);
}
char buffer[200];
pid_t pid;
int fds[2];
pipe(fds);
pid = fork();
if (pid == 0) { /* The child process */
//wait(NULL);
write(1, "hi i am in child\n", 17);
int copy = open(argv[2], O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR | S_IXUSR | S_IRGRP);
FILE* stream;
close(fds[1]);
stream = fdopen(fds[0], "r");
while (fgets(buffer, sizeof(buffer), stream) != NULL) {
//printf("%s\n", buffer);
write(copy, buffer, 200);
//printf("kjlkjljljlkj\n");
//puts(buffer);
}
close(copy);
close(fds[0]);
exit(0);
}
else {
write(1, "hi i am in parent\n", 18);
FILE* input = fopen(argv[1], "r");
FILE* stream;
close(fds[0]);
stream = fdopen(fds[1], "w");
/*while (fscanf(input, "%s", buffer) != EOF) {
//printf("%s\n", buffer);
fprintf(stream, "%s\n", buffer);
fflush(stream);
//printf("howdy doody\n");
}*/
fgets(buffer, sizeof(buffer), input);
printf("%s", buffer);
fprintf(stream, "%s", buffer);
fflush(stream);
close(fds[1]);
fclose(input);
wait(NULL);
exit(0);
}
return 0;
}
Am I doing the reads and writes wrong?
Am I doing the reads and writes wrong?
Yes.
In the child, you are mixing string-oriented buffered I/O (fgets()) with block-oriented binary I/O. (That is, write().) Either approach will work, but it would be normal practice to pick one or the other.
If you mix them, you have to consider more aspects of the problem. For example, in the child, you are reading just one line from the pipe but then you write the entire buffer to the file. This is the source of the garbage characters you are probably seeing in the file.
In the parent, you are sending only a single line with no loop. And after that, you close the underlying file descriptor before you fclose() the buffered I/O system. This means when fclose tries to flush the buffer, the now-closed descriptor will not work to write any remaining data.
You can either use write()/read()/close(), which are the Posix-specified kernel-level operations, or you can use fdopen/puts/gets/fclose which are the ISO C - specified standard I/O library operations. Now, there is one way of mixing them that will work. If you use stdio in the parent, you could still use read/write in the child, but then you would be making kernel calls for each line, which would not usually be an ideal practice.
You should generally read/write pipes only using the read/write-calls.
You should close the according ends of the pipe for child (read-only) and parent (write-only).
Afterwards, write from the parent into the pipe using write()-systemcall. And in the child read using read()-systemcall.
Look here for a good explanation.
Related
Similar questions has already been asked, but their solutions aren't helping me much
Program that read file and send it to parent process with pipe
Read/writing on a pipe, accomplishing file copying in C
I'm trying to read from a file test.txt ( which contains a single line of text), write it to a pipe, from where a child process will read from the pipe and write the contents to another file.
/* Read the contents of a file and display it using pipe */
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
void main()
{
char buffer[100];
char childbuff[100];
int fd[2], des, bytes, target;
pipe(fd);
if(fork()) {
/* parent process closes the downstream */
close(fd[0]);
/* reads the file */
des = open("test.txt", O_RDONLY);
bytes = read(des, buffer, sizeof(buffer));
/* puts data in pipe */
write(fd[1], buffer, bytes);
} else {
/* Child process closes the upstream */
close(fd[1]);
/* reads from the pipe */
read(fd[0], childbuff, sizeof(childbuff));
close(fd[0]);
/* output the received string */
printf("\nReceived string is -- %s", childbuff);
target = open("copy.txt", O_CREAT, 00777);
write(target, childbuff, (strlen(childbuff)-1));
}
}
Problem is printf() prints the string on terminal, a file named copy.txt also gets created, but nothing is getting copied to it (it seems there is a problem with the write() function )
however, if I change
write(target, childbuff, (strlen(childbuff)-1));
to
write(1, childbuff, (strlen(childbuff)-1));
string is simply getting written on my terminal.
So what possibly am I doing wrong while writing to file?
You also need O_WRONLY to write to the file:
target = open("copy.txt", O_CREAT |O_WRONLY, 00777);
Note that you can't use strlen() or %s to print it as a C-string. read(2) doesn't return a NUL terminated string.
Instead get the number of bytes read from read() and use it in write():
ssize_t num_bytes = read(fd[0], childbuff, sizeof(childbuff));
write(target, childbuff, num_bytes);
You should check the return of all system calls for failure.
i have the following code
it prints to the screen: haha
to the file :
haha
hello
Father finished
if i remove line 6 and 7 , I get different results
why?
int main()
{
// creates a new file having full read/write permissions
int fd = open("myfile", O_RDWR|O_CREAT, 0666);
write(fd, "haha\n", 5);
close(fd); // line 6
fd = open("myfile", O_RDWR); // line 7
close(0);
close(1);
dup(fd);
dup(fd);
if (fork() == 0)
{
char s[100];
dup(fd);
scanf("%s", s);
printf("hello\n");
write(2, s, strlen(s));
return 0;
}
wait(NULL);
printf("Father finished\n");
close(fd);
return 0;
}
Try to comment out the scanf(), recompile and rerun. The scanf() trying to read beyond EOF might be doing something in the stdio library internal buffers that is causing this issue in printf() buffer not being flushed at the time of process _exit. Just a guess...
A file descriptor has only one position which is used both for writing and reading. When you write to the file in line 4 the position is advanced past what was just written, so the descriptor's position is at the end of the file. Calling close and open has the effect of resetting the position to the beginning of file (among other things).
You could replace the calls to close and open with lseek(fd, 0, SEEK_SET) to have the same effect, without closing and reopening the file.
Also, you should not mix the stdio functions scanf, printf and low level functions such as write. The results of the program will be unpredictable because of buffering in the stdio functions.
I have some troubles with a library function.
I have to write some C code that uses a library function which prints on the screen its internal steps.
I am not interested to its return value, but only to printed steps.
So, I think I have to read from standard output and to copy read strings in a buffer.
I already tried fscanf and dup2 but I can't read from standard output. Please, could anyone help me?
An expanded version of the previous answer, without using files, and capturing stdout in a pipe, instead:
#include <stdio.h>
#include <unistd.h>
main()
{
int stdout_bk; //is fd for stdout backup
printf("this is before redirection\n");
stdout_bk = dup(fileno(stdout));
int pipefd[2];
pipe2(pipefd, 0); // O_NONBLOCK);
// What used to be stdout will now go to the pipe.
dup2(pipefd[1], fileno(stdout));
printf("this is printed much later!\n");
fflush(stdout);//flushall();
write(pipefd[1], "good-bye", 9); // null-terminated string!
close(pipefd[1]);
dup2(stdout_bk, fileno(stdout));//restore
printf("this is now\n");
char buf[101];
read(pipefd[0], buf, 100);
printf("got this from the pipe >>>%s<<<\n", buf);
}
Generates the following output:
this is before redirection
this is now
got this from the pipe >>>this is printed much later!
good-bye<<<
You should be able to open a pipe, dup the write end into stdout and then read from the read-end of the pipe, something like the following, with error checking:
int fds[2];
pipe(fds);
dup2(fds[1], stdout);
read(fds[0], buf, buf_sz);
FILE *fp;
int stdout_bk;//is fd for stdout backup
stdout_bk = dup(fileno(stdout));
fp=fopen("temp.txt","w");//file out, after read from file
dup2(fileno(fp), fileno(stdout));
/* ... */
fflush(stdout);//flushall();
fclose(fp);
dup2(stdout_bk, fileno(stdout));//restore
I'm assuming you meant the standard input. Another possible function is gets, use man gets to understand how it works (pretty simple). Please show your code and explain where you failed for a better answer.
I'm writing a little program, and here is what it should do.
In the main process I have to create a new one and that one should execute another program which only does a printf("text"). I want to redirect the pipe write end on stdout and the main process should read from its pipe read and and print it on stdout. I wrote the code but again and again I get a segmentation fault when the parent process tries to read from the pipe.
#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
void write_to(FILE *f){
char buf[50];
fprintf(f,"KOMA");
}
int main(){
int cpPipe[2];
int child1_fd;
int child2_fd;
if(pipe(cpPipe) == -1){
fprintf(stderr,"ERROR PIPE creation");
exit(1);
}else{printf("pipe couldn't be created\n");}
child1_fd = fork();
if(child1_fd < 0){
fprintf(stderr, " CHILD creation error");
exit(1);
}
if(child1_fd == 0){
printf("*CHILD*\n");
char program[] = "./Damn";
int dupK;
printf("stdout %d \n", STDOUT_FILENO);
printf("stdin %d \n", STDIN_FILENO);
printf("pipe1 %d \n", cpPipe[1]);
printf("pipe0 %d \n", cpPipe[0]);
// closing pipe write
close(cpPipe[0]);
close(1);
dup(cpPipe[1]);
printf("and");
close(cpPipe[1]);
exit(0);
}else{
printf("*Parent*\n");
char *p;
char *buf;
FILE *pipe_read;
close(cpPipe[1]);
pipe_read = fdopen(cpPipe[0],"r");
while((buf = fgets(p,30,pipe_read)) != NULL){
printf("buf %s \n", buf);
}
wait();
printf("Child is done\n");
fclose(pipe_read);
exit(0);
}
}
Do I have to close the pipe write end when I redirect stdout to it?
Uhm,... the reason for your segmentation fault is here:
buf = fgets(p,30,pipe_read);
p is a pointer to essentially nowhere of importance. It's content is whatever is in the stack at the time of execution, you never initialize it. You need it to point to a chunk of memory you can use! Assign the return of a malloc() call to it, or declare it as char p[LEN].
Edit: you are also reopening already open file descriptors. Check the documentation on fgets and pipe, I think you are confused as to how they work.
Now, that said, the flow of your function is kinda confusing. Try working on clarifying it! Remember, code is meant to express intentions, ideas of functionality. Try using pencil and paper to organize your program, and then write it as actual code :).
Cheers!
Do I have to close the pipe write end when I redirect stdout to it?
In general, yes, because while there is a process with the write end of the pipe open, the processes reading the pipe will not get EOF and will hang. It is also tidy to close file descriptors you aren't going to use, of course.
Your code also says "pipe could not be created" in the success path.
Can I make an anonymous stream in c? I don't want to create a new file on the file system, just have a stream that one function can fwrite to while the other can fread from it. Not c++, c.
Maybe You're looking for pipes.
Forward Your STDOUT to the pipe.
Then the other application would read from the pipe.
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#define RDR 0
#define WTR 1
char ** parseargs(char *string);
int main(void){
char mode = 'r';
char prog[50] = "/bin/ps --version";
char **argv;
int p[2];
pid_t pid;
FILE *readpipe;
int pipein, pipeout;
char buf;
/* create the pipe */
if(pipe(p) != 0){
fprintf(stderr, "error: could not open pipe\n");
}
pipein = p[RDR];
pipeout = p[WTR];
if((pid = fork()) == (pid_t) 0){
close(pipein);
dup2(pipeout, 1);
close(pipeout);
if(execv(argv[0], argv) == -1){
fprintf(stderr, "error: failed to execute %s\n", argv[0]);
}
_exit(1);
}
close(pipeout);
readpipe = fdopen(pipein, &mode);
while(!feof(readpipe)){
if(1 == fread(&buf, sizeof(char), 1, readpipe)){
fprintf(stdout, "%c", buf);
}
}
return 0;
}
Yes, tmpfile() is one way to do it. However, I believe tmpfile() is frowned upon these days due to security concerns.
So, you should use mkstemp in POSIX or tmpfile_s in Windows instead of tmpfile().
These will all still create files in the filesystem, though. They're temporary in that they "go away" when the program exits.
Another option, which doesn't create a physical file is mmap().
Oops, just found it... maybe. tmpfile() returns a tmeporary FILE *
Is that the right way to do it?
If you're on Unix (or a similar OS), you want to read Beej's Guide to Unix Interprocess Communication (it's a good read no matter what your OS is).
Check it out at Beej's Guides.
In a rapid glance there I noticed a few things you could probably use with more or less work (and with the optional creation of a file/resource):
Pipes
FIFOs
Message Queues
Shared Memory Segments
Memory Mapped Files
Unix Sockets