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.
Related
I'm trying to understand what is behind this behaviour in my parent process.
Basically, I create a child process and connect its stdout to my pipe. The parent process continuously reads from the pipe and does some stuff.
I noticed that when inserting the while loop in the parent the stdout seems to be lost, nothing appears on the terminal etc I thought that the output of stdout would somehow go to the pipe (maybe an issue with dup2) but that doesn't seem to be the issue. If I don't continuously fflush(stdout) in the parent process, whatever I'm trying to get to the terminal just won't show. Without a while loop in the parent it works fine, but I'm really not sure why it's happening or if the rest of my implementation is problematic somehow.
Nothing past the read system call seems to be going to the stdout in the parent process. Assuming the output of inotifywait in the pipe is small enough ( 30 > bytes ), what exactly is wrong with this program?
What I expect to happen is the stdout of inotifywait to go to the pipe, then for the parent to read the message, run strtok and print the file name (which only appears in stdout when I fflush)
Running the program with inotify installed and creating any file in the current directory of the program should be enough. Removing the while loop does print the created file's name (as expected).
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <errno.h>
int main(void) {
char b[100];
int pipefd;
if (mkfifo("fifo", 0666) == -1) {
if (errno != EEXIST) {
perror("mkfifo");
exit(EXIT_FAILURE);
}
}
pid_t pid = fork();
if (pid < 0) {
perror("fork");
exit(1);
}
if ((pipefd = open("fifo", O_RDWR)) < 0) {
perror("open pipe");
exit(EXIT_FAILURE);
}
if (pid == 0) {
dup2(pipefd, 1);
const char* dir = ".";
const char* args[] = {"inotifywait", dir, "-m", "-e",
"create", "-e", "moved_to", NULL};
execvp("inotifywait", (char**)args);
perror("inotifywait");
} else {
while (1) {
fflush(stdout); // the output only appears in stdout with this here
if (read(pipefd, b, 30) < 0) {
perror("problem # read");
exit(1);
}
char filename[30];
printf("anything");
sscanf(b, "./ CREATE %s", filename);
printf("%s", filename);
}
}
}
The streams used by the C standard library are designed in such a way that they are normally buffered (except for the standard error stream stderr).
The standard output stream is normally line buffered, unless the output device is not an interactive device, in which case it is normally fully buffered. Therefore, in your case, it is probably line buffered.
This means that the buffer will only be flushed
when it is full,
when an \n character is encountered,
when the stream is closed (e.g. during normal program termination),
when reading input from an unbuffered or line-buffered stream (in certain situations), or
when you explicitly call fflush.
This explains why you are not seeing the output, because none of the above are happening in your infinite loop (when you don't call fflush). Although you are reading input, you are not doing this from a C standard library FILE * stream. Instead, you are bypassing the C runtime library (e.g. glibc) by using the read system call directly (i.e. you are using a file descriptor instead of a stream).
The simplest solution to your problem would probably be to replace the line
printf("%s", filename);
with:
printf("%s\n", filename);
If stdout is line-buffered (which should be the case if it is connected to a terminal), then the input should automatically be flushed after every line and an explicit call to fflush should no longer be necessary.
I am currently trying to understand combincation of dup2 and C pipes, but not even the simplest program seems to work. Already when reading example codes I am pretty confused on when they close ends of the pipe and where the output should be printed.
Sometimes the write end is closed, even though one line later output should be generated which should go into the pipe. In other examples, the unused end is closed (which makes more sense to me).
Then, I do not understand when dup2 should be executed. I guess it should become before the output I want to redirect, but I have the feeling I also saw that differently today.
So in the end I came up with this little test with printf and fflush in each line, where nothing gets redirected through the pipe. Why's that? What am I doing wrong?
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int out_pipe[2];
char *output[101];
if (pipe(out_pipe) != 0) {
perror("pipe()");
exit(1);
}
printf("Hello");
fflush(stdout);
dup2(out_pipe[1], STDOUT_FILENO);
printf("Hello");
fflush(stdout);
close(out_pipe[1]);
printf("Hello");
fflush(stdout);
read(out_pipe[0], output, 100);
close(out_pipe[0]);
printf("PIPE: %s", output);
fflush(stdout);
return 0;
}
End your printf() messages with newlines; the fflush() is still a good idea as you're about to change where standard output goes, though it's not usually necessary if the standard output of the program is going to a terminal. If the standard output was going to a file and the fflush() was not in place, then you'd get three copies of "Hello\n" written to the pipe.
When you change standard output to the pipe, your message is indeed written to the pipe.
When you close the write file descriptor, you don't run into any issues. You then write a second Hello to the pipe. You need this fflush() to ensure that the standard I/O package has actually written its buffered data to the pipe.
You then read from the pipe into the output buffer. You should check how many bytes you read since the string is not going to be null terminated. You should get 10 bytes read (when you don't have any newlines in the messages).
You then write to the pipe again with the PIPE: prefix.
To fix, write messages to standard error.
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int out_pipe[2];
char output[101];
if (pipe(out_pipe) != 0) {
perror("pipe()");
exit(1);
}
printf("Hello\n");
fflush(stdout);
dup2(out_pipe[1], STDOUT_FILENO);
printf("Hello\n");
fflush(stdout);
close(out_pipe[1]);
printf("Hello\n");
fflush(stdout);
int n = read(out_pipe[0], output, sizeof(output));
close(out_pipe[0]);
fprintf(stderr, "PIPE: %.*s\n", n, output);
return 0;
}
Note that I changed the definition of output from an array of char * to a simple array of char. With the changes, I got the output:
$ ./pipe3
Hello
PIPE: Hello
Hello
$
That's because I included newlines in the messages written to the pipe, as well as in the format string that ends up on standard error.
Is there a possibility to "reenable" stdout?
Yes; simply preserve a copy of the original file descriptor for standard output before using dup2(), and then reinstate the copy once you've done with the pipe.
I've removed the two leading fflush() calls, and the sample output demonstrates the difference between terminal and file output:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int out_pipe[2];
char output[101];
int old_stdout;
if (pipe(out_pipe) != 0) {
perror("pipe()");
exit(1);
}
printf("Hello\n");
old_stdout = dup(STDOUT_FILENO);
dup2(out_pipe[1], STDOUT_FILENO);
printf("Hello\n");
close(out_pipe[1]);
printf("Hello\n");
fflush(stdout);
int n = read(out_pipe[0], output, sizeof(output));
close(out_pipe[0]);
dup2(old_stdout, STDOUT_FILENO);
printf("PIPE: %d <<%.*s>>\n", n, n, output);
return 0;
}
Sample outputs:
$ ./pipe3Hello
PIPE: 12 <<Hello
Hello
>>
$./pipe3 > output
'pipe3' is up to date.
$ cat output
PIPE: 18 <<Hello
Hello
Hello
>>
$
If you remove the remaining fflush(), the program hangs. There is nothing in the pipe (because standard I/O hasn't flushed its buffer because it isn't full and the output isn't a terminal any more), but the pipe is open for writing, so the kernel considers that input could appear on it — if only the program that has the pipe open for writing wasn't waiting on the read end of the pipe for the input to appear. The program has deadlocked on itself.
I'm experimenting with this dup2 command in linux. I've written a code as follows:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main()
{
int pipe1_ends[2];
int pipe2_ends[2];
char string[] = "this \n is \n not \n sorted";
char buffer[100];
pid_t pid;
pipe(pipe1_ends);
pipe(pipe2_ends);
pid = fork();
if(pid > 0) { /* parent */
close(pipe1_ends[0]);
close(pipe2_ends[1]);
write(pipe1_ends[1],string,strlen(string));
read(pipe2_ends[0], buffer, 100);
printf("%s",buffer);
return 0;
}
if(pid == 0) { /* child */
close(pipe1_ends[1]);
close(pipe2_ends[0]);
dup2(pipe1_ends[0], 0);
dup2(pipe2_ends[1],1);
char *args[2];
args[0] = "/usr/bin/sort";
args[1] = NULL;
execv("/usr/bin/sort",args);
}
return 0;
}
I expect this program to behave as follows:
It should fork a child and replace its image with sort process. And since the stdin and stdout are replaced with dup2 command, I expect sort to read input from the pipe and write the output into the other pipe which is printed by the parent. But the sort program doesn't seem to be reading any input. If no commandline argument is given, sort reads it from the stdin right? Can someone help me with this problem, please.
Many thanks!
Hm. What's happening is that you aren't finishing your write: after sending data to the child process, you have to tell it you're done writing, either by closing pipe1_ends[1] or calling shutdown(2) on it. You should also call write/read in a loop, since it's quite likely in the general case that read at least won't give you all the results in one go. Obviously the full code checks all return values, doesn't it?
One final thing: Your printf is badly broken. It can only accept null-terminated strings, and the result returned by read will not be null-terminated (it's a buffer-with-length, the other common way of knowing where the end is). You want:
int n = read(pipe2_ends[0], buffer, 99);
if (n < 0) { perror("read"); exit(1); }
buffer[n] = 0;
printf("%s",buffer);
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 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.