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);
Related
I wrote this piece of code to show the basic working of how I would like to send some data (Strings) from the parent process to the child process. But I seem to have some problems. (I removed all error checking to make the code more readable)
When I run this piece of code I expect to see the two test strings to be displayed on the terminal, but I only see the first one.
When I uncomment the first “sleep(1)”, then I see both strings displayed.
But when I uncomment only the second “sleep(1)”, then I again only see the first string.
I suspect this problem has something to do with synchronization. That the strings get written to fast and the fifo write end closes before everything is read by the child process. That’s why we see the correct output when we introduce a sleep between the two write() commands.
But what I don’t understand is that we still get a faulty output when we only introduce a sleep after both write commands. Why can’t the child read both strings even if they are both written before it can read one?
How can I solve this problem? Do I need some synchronization code, and if so how should I implement this. Because I won’t write a “sleep(1)” after every write command.
And is the solution also viable for multiple processes that want to write to the same fifo? (but with still only one process that reads from the fifo)
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main(int argc, char const *argv[]) {
mkfifo("test_fifo", 0666);
pid_t pid = fork();
if (pid == 0) {
int fd = open("test_fifo", O_RDONLY);
char data[400];
int rc;
do {
rc = read(fd, data, 400);
if (rc > 0) printf("Received data: %s\n", data);
} while(rc > 0);
}
else {
int fd = open("test_fifo", O_WRONLY);
char * string1 = "This is the first test string";
write(fd, string1, strlen(string1) + 1);
//sleep(1);
char * string2 = "This is the second test string";
write(fd, string2, strlen(string2) + 1);
//sleep(1);
close(fd);
wait(NULL);
}
return 0;
}
You are receiving both strings at the same time at the first call to read. Because %s prints up until a zero byte, the second string is just not displayed. The poor mans synchronization with sleep(1) allows child to "catch" the messages in two distinct read call.
read returns the count of bytes read. Use that number. Change the parent code to:
ssize_t rc;
do {
rc = read(fd, data, 400);
if (rc > 0) {
printf("Received data: ");
for (size_t i = 0; i < rc; ++i) {
if (data[i] == '\0') {
printf("\\x00");
continue;
}
printf("%c", data[i]);
}
printf("\n");
}
} while(rc >= 0);
and it shows on my pc:
Received data: This is the first test string\x00This is the second test string\x00
Why can’t the child read both strings even if they are both written before it can read one?
Well, the problem is not in reading, it's how you are displaying the data you read. (Still, reading could be improved, one should handle that pesky EAGAIN errno code).
How can I solve this problem?
If you want 1:1 relationship between read/write use a constant size packets or generally you have to know in advance how many bytes you want to read. Bytes written are "concatenated" together and lose structure. Or use pipe(3p) on which messages with size smaller then PIPE_BUF are guaranteed to be atomic. Or you could use POSIX message queue mq_receive/mq_send.
Or write a proper "deserializer", something that will buffer data and keep internal state and notify higher level only when a whole "message" was received, ie. detect when a zero byte was received in the stream of bytes and restore structure the the stream of bytes.
I have a sample program that outputs a line of text every second. In the test program below, this program writes some text to stdout then waits 1 second and repeats 20 times.
I have another program which uses popen (_popen on Windows) to open a pipe for reading from the program. I then use fgets to read data. The problem I have is that the fgets blocks until the program terminates. Then I get all the output, all 20 lines, in one go. I want to get the output a line at a time, then ok for fgets to block until next line ready. The reason is I plan to use this on a program that will be constantly running, outputting text, e.g. like the use of tail.
If I run this code example on a program that outputs some text all in one go and exits then it works fine.
Why does fgets block? The test program does print some text immediately, so why doesn't fgets read this first line of text immediately?
Here is the code:
#include <stdio.h>
#include <windows.h>
void execute(const char* cmd) {
char buffer[128] = { 0 };
FILE* pipe = _popen(cmd, "r");
if (!pipe) {
printf("popen() failed!\n");
return;
}
while (!feof(pipe)) {
if (fgets(buffer, 128, pipe) != nullptr)
printf("%s", buffer);
}
int rc = _pclose(pipe);
if (rc != EXIT_SUCCESS) { // return code not 0
printf("pclose exit failure: %d\n", rc);
}
}
int main(int argc, char* argv[]) {
if (argc != 2) {
printf("Usage: pipe_test.exe <program>\n");
exit(1);
}
execute(argv[1]);
}
The program run, helloworld.exe:
#include <stdio.h>
#include <windows.h>
int main() {
for (int i = 0; i < 20; i++) {
printf("Hello World %d\n", i);
Sleep(1000);
}
}
Why does fgets block?
Because it's waiting for the children to output something.
The test program does print some text immediately, so why doesn't fgets read this first line of text immediately?
It actually does not print text immediately. The problem here, as #Barmar notices, is that writing to a pipe is buffered (and not line buffered) by the C standard library implementation. This buffering happens in your child program (helloworld), not in your parent program (pipe_test).
From your parent program, you have no control over what the children spawned through popen() will do, therefore if the child output is buffered like in this case, the only thing you can do (without modifying the child's code) is to wait until the buffer is flushed to the pipe.
In order to get the output sooner, you would have to modify the children's code to manually call fflush() or use setvbuf() to disable buffering:
int main() {
setvbuf(stdout, NULL, _IONBF, 0); // Disable buffering on stdout.
for (int i = 0; i < 20; i++) {
printf("Hello World %d\n", i);
Sleep(1000);
}
}
There's really not much else you can do.
I need to create program with 3 processes:
The first process should repeatedly read /dev/urandom and send 15 chars each cycle to the second process via a pipe.
The second process should convert received data to hex and send the result to the third process via a fifo.
The third process should print the received data.
This is what I wrote so far. Communication using the pipe is working fine, however there is some problem with the fifo - when I change n to a larger number such as 100000 or 1000000, the program doesn't start. When it's smaller, say 500 or 1000, the program works. What could be the reason behind that?
This is how I run it:
cat /dev/urandom | ./a.out
And here is the code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#define FIFO "my_fifo"
int main(void) {
int pdesk[2];
char buf[15];
char buffer[15];
char hex[30];
char f[30];
int len;
int n;
n = 100;
umask(0);
mkfifo(FIFO, 0666);
pipe(pdesk);
if (fork() == 0) {
for (int i = 0; i < n; i++) {
read(STDIN_FILENO, buffer, 15);
write(pdesk[1], buffer, 15);
}
close(pdesk[1]);
} else {
sleep(1);
int fp;
for(int i = 0; i < n; i++) {
read(pdesk[0], buf, 15);
for(int a = 0, b = 0; b < 30; ++a, b+= 2)
sprintf(hex + b, "%02x", buf[a] & 0xff);
fp = open(FIFO, O_WRONLY);
write(fp, hex, 30);
close(fp);
usleep(10000);
}
close(pdesk[0]);
}
if (fork() == 0) {
sleep(2);
int fp;
for (int i = 0; i < n; i++) {
fp = open(FIFO, O_RDONLY);
read(fp, f, 30);
printf("Odczytano: %s\n", f);
close(fp);
usleep(10000);
}
}
}
If I understand your code correct, it will do the following:
With the first fork you start a child that reads from stdin and writes to the pipe.
Your parent process reads from the pipe and writes to the FIFO.
When your parent process has finished its loop it calls the second fork to create another child which will read from the FIFO and print the data.
When the loop count is too large you will reach the buffer limit of the FIFO and the parent will block because no process is reading from the FIFO. When the process is blocked in writing to the FIFO it will never create the child that is expected to read from the FIFO.
I think the main problem is that you should create the second child before starting the loop that reads the data from the pipe and writes to the FIFO.
Some additional remarks:
With cat /dev/urandom | ./a.out your program does not read /dev/urandom directly. It reads from a pipe which might behave differently.
You should always check the return value of read. It will tell you how many bytes it has read which may be less than you asked it to read. If you want to have exactly 15 characters you might have to read several times if you get less than 15 characters.
The same applies to write.
Thank you very much. When the process that displays data is above other child processes, it finally works.
With cat /dev/urandom | ./a.out your program does not read /dev/urandom directly. It reads from a pipe which might behave differently.
How could I change it?
The programs also needs to read files the same way it reads from /dev/urandom, for example:
cat file.txt | ./a.out
I took your advice and started to check the value of read and now it doesn't go behind the range of file. The problem is I don't know how to check which parameter was called (and hence I can't check the length of file) - if it was file.txt, /dev/urandom, none or anything else. I tried with
int main(char argc, char* argv[])
but argv is always ./a.out, no matter what I call. Is there any way to check that?
I have a program A that takes two arguments from stdin and exits with a unique code depending on the arguments. I am writing a program B that calls program A using fork and exec and let program B print out the code program A exits with. For some reason, program A doesn't seem to be getting the data I piped through to it in the child process of fork. I'm not sure if I'm piping the correct data to the child process.
Could someone help me please? Thanks!
Here is my code:
int program_B(void) {
char var_a[256];
char var_b[256];
int fd[2];
// Read from stdin
char *sendarray[2];
sendarray[0] = var_a;
sendarray[1] = var_b;
if(fgets(var_a, MAXLINE, stdin) == NULL) {
perror("fgets");
exit(1);
}
if(fgets(var_b, MAXLINE, stdin) == NULL) {
perror("fgets");
exit(1);
}
if (pipe(fd) == -1) {
perror("pipe");
exit(1);
}
int pid = fork();
// Child process -- error seems to be here.
if (pid == 0) {
close(fd[1]);
dup2(fd[0], fileno(stdin));
close(fd[0]);
execl("program_A", NULL);
perror("exec");
exit(1);
} else {
close(fd[0]);
write(fd[1], sendarray, 2*sizeof(char*));
close (fd[1]);
int status;
if (wait(&status) != -1) {
if (WIFEXITED(status)) {
printf("%d\n", WEXITSTATUS(status));
} else {
perror("wait");
exit(1);
}
}
}
return 0;
}
You are piping the wrong data to the child process.
I am assuming var_a and var_b are the strings you want to send to program A. They are both of type array of chars, which in C is the same thing as pointer to char (Actually there is a small difference between pointers and arrays but this is irrelevant for this problem). So they are actually just pointers to the first byte of each argument. sendarray, however is an array of char-pointers which is the same thing as a pointer to char-pointer. Keep this in mind for a second.
When calling write() the 2nd parameter tells it where the data is in memory. By passing sendarray, write thinks this sendarray points the data you want to write although it actually points to yet another pointer. So what happens is that the pointer values of var_a and var_b (which is what sendarray points to), are written to the pipe.
So you have to pass var_a and var_b to write(), since those are pointers to the actual data you want to send. Also you have to know how long (how many bytes) this data is. If var_a and var_b point to null-terminated strings, you can use strlen() to determine their length.
One last thing: I don't know how exactly your program A obtains 2 arguments from a continuous byte stream like stdin, but assuming it reads it line by line, you obviously have to send a new-line character from program B, as well.
So putting it all together your write statements should look something like this:
write(fd[1], var_a, strlen(var_a));
write(fd[1], "\n", 1);
write(fd[1], var_b, strlen(var_b));
Of course, if any of the assumptions I made is wrong, you have to adopt this code appropriately.
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.