Usage of pipes and dup2 in C - c

I have this code
int main() {
int fd[2];
pipe(fd);
dup2(fd[1], 1); close(fd[1]);
printf("I wanna print this.");
char * buf = malloc(100);
read(fd[0], buf, 50);
fprintf(stderr, ">>>%s<<<\n", buf);
close(fd[0]);
}
Expected output : print >>>I wanna print this.<<< on stderr
How can I make this work?

The main problem is that the line you printed got buffered, so it didn't actually get sent to the pipe. Either do fflush(stdout); somewhere between your printf and read to flush the buffer, or disable buffering of stdout with setbuf or setvbuf at the very beginning of your program.
A secondary problem is that you're mixing up counted and null-terminated strings, as Jonathan Leffler alluded to in the comments.

This code produces the desired answer. It uses the suggestions I made in my comment.
/* SO 7493-6183 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{
int fd[2];
pipe(fd);
dup2(fd[1], 1);
close(fd[1]);
printf("I wanna print this.");
fflush(stdout);
char *buf = malloc(50);
int n = read(fd[0], buf, 50);
fprintf(stderr, ">>>%.*s<<<\n", n, buf);
close(fd[0]);
free(buf);
return 0;
}
Output:
>>>I wanna print this.<<<
Alternatively, you could use:
buf[n] = '\0';
fprintf(stderr, ">>>%s<<<\n", buf);
This null-terminates the string before printing it. The key parts of the exercise are fflush(stdout) and capturing the length from read() and using that to limit the output of the fprintf() statement.

Related

modify text in child process to use in parent

I am trying to create a simple pipe/fork function, so that child process modifies the value of text, then it is printed by the parent.
I have checked a similar question on Modify variable in child process, but I am unable to print the text variable in the parent.
int main()
{
pid_t childp;
char text[100];
pipe(text);
childp = fork();
if (childp ==0){
strncpy(text, "Hello world", 100); // child running
}
else{
printf("%s\n", text); // parent prints "Hello world"
}
return 1;
}
Any help is appreciated (I am very new to C language)
look into this website :
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main(void)
{
int fd[2], nbytes;
pid_t childpid;
char string[] = "Hello, world!\n";
char readbuffer[80];
pipe(fd);
if((childpid = fork()) == -1)
{
perror("fork");
exit(1);
}
if(childpid == 0)
{
/* Child process closes up input side of pipe */
close(fd[0]);
/* Send "string" through the output side of pipe */
write(fd[1], string, (strlen(string)+1));
exit(0);
}
else
{
/* Parent process closes up output side of pipe */
close(fd[1]);
/* Read in a string from the pipe */
nbytes = read(fd[0], readbuffer, sizeof(readbuffer));
printf("Received string: %s", readbuffer);
}
return(0);
}
It explain how to properly use C pipes. Take a look to this too.
Add this for error handling :
if (pipe(fd) == -1) {
fprintf(stderr, "Pipe Failed");
return 1;
}
The closest to your code working example I can come up with.
int main()
{
pid_t childp;
char text[100];
int fd[2];
pipe(fd);
childp = fork();
if (childp ==0){
FILE *f=fdopen(fd[1], "w"); // Write into this "file" what you want the other end to read.
fprintf(f, "Hello world\n");
}
else{
FILE *f=fdopen(fd[0], "r"); // We can read from this "file"
fgets(text, 100, f); // Read one line of text from the "file" up to 100 bytes
printf("read <%s> from by new child\n", text)
}
return 1;
}
Note again that this is not a shared buffer. So you need both end to agree on a "protocol". Because everything that is written by one end must be read by the other (otherwise the "write" instruction will be blocked), and everything that is read by one end, must be writter by the other (otherwise the "read" instruction will be blocked).
So, either you use a fixed size message, for example. If you choose 100, you need to write 100 bytes exactly at one end (fill with 0 if needed), and read 100 bytes exactly at the other.
Or you find some protocol so that the reading end knows exactly when to stop reading.
I choose the latter (because it is the closest to your code). By using fgets to read, that stop to read at each newline, and fprintf a message ended by a newline at the writing end.

how to make read() in thread block on a pipe's file descriptor?

I'm experimenting on how to communicate between a thread and the main function in C
There is a behavior that I don't understand in the following code :
#include <pthread.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
void* output(void* pipe1);
int main(int argc, const char *argv[])
{
pthread_t tid0;
int pipe1[2];
char buffer[200];
// Creating the pipe
pipe(pipe1);
// Creating the thread and passing the pipe as argument
pthread_create(&tid0, NULL, output, &pipe1);
// Input from user
scanf("%s", buffer);
// Writing to the pipe
write(pipe1[1], buffer, strlen(buffer));
return 0;
}
void* output(void* pipe1) {
char buffer[200];
// Reading the pipe and print the buffer
read(((int*)pipe1)[0], buffer, strlen(buffer));
printf("thread say: %s\n", buffer);
pthread_exit(NULL);
}
Why the read function doesn't block on the pipe's file descriptor ?
Maybe I should close the end of the pipe but since they share the same memory space, the error "bad file descriptor" is returned when I will call read or write.
Maybe you can guide me to other methods if pipe is really a bad solution (with an example it will be amazing ! :) )
Many thanks !
EDIT: SOLUTION
Many thank for your answer here is the code that have the expected behavior
#include <pthread.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
void* output(void* pipe1);
int main(int argc, const char *argv[])
{
pthread_t tid0;
int pipe1[2];
char buffer[200];
// Creating the pipe
pipe(pipe1);
// Creating the thread and passing the pipe as argument
pthread_create(&tid0, NULL, output, &pipe1);
// Input from user
scanf("%s", buffer);
// Writing to the pipe
if (write(pipe1[1], buffer, strlen(buffer)) < 0) {
perror("write");
exit(1);
}
// join so the main "wait" for the thread
pthread_join(tid0, NULL);
return 0;
}
void* output(void* pipe1) {
char buffer[200];
int nread;
// Reading the pipe and print the buffer
nread = read(((int*)pipe1)[0], buffer, sizeof buffer - 1);
if (nread < 0) {
fprintf(stderr, "ERROR\n");
perror("read");
exit(1);
}
buffer[nread] = '\0';
fprintf(stderr, "thread say: %s\n", buffer);
pthread_exit(NULL);
}
char buffer[200];
read(((int*)pipe1)[0], buffer, strlen(buffer));
You are calling strlen on an uninitialized buffer. This is allowed to crash your program. Instead, you got lucky, and all it did was tell read to read zero bytes, so read returned without doing anything.
What you actually want is
ssize_t nread = read(((int *)pipe1)[0], buffer, sizeof buffer - 1);
if (nread < 0) {
perror("read");
return 0;
}
buffer[nread] = '\0';
What read wants to be told is how much space you are giving it to read into, not the length of any string that may or may not already be in that space. That's sizeof buffer, minus one so we always have space to add a string terminator.
It's correct to use strlen when writing, because you only want to write the actual string, not any junk that might be beyond the end of the string; but then write doesn't write the string terminator to the pipe, so read doesn't read one, so you have to add it by hand. And, of course, always check for errors.
Also, keep in mind that the threads run simultaneously. Even after fixing this bug, the write may already have happened by the time the reader-thread calls read, and if it hasn't, it probably will happen very soon. If you want to observe the reader-thread actually blocking in read you need to delay before calling write.

C - Pipe input from one program to another program

I'm supposed to create two programs (main and aux), where main forks a child to execute aux. The parent takes input from the user, until blank line '\n', and the child executes aux, which is supposed to print the input back out. I'm able to get it to work in main with the commented code instead of execlp(), but cannot get execlp(aux) to work correctly. Any help is appreciated.
"main.c"
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
int main() {
int fd[2], i;
char line[100], buffer[100];
pipe(fd);
pid_t pid = fork();
if (pid < 0) {
printf("Fork Failed\n");
exit(-1);
}
else if (pid > 0) {
close(fd[0]);
while(fgets(line, sizeof(line), stdin) && line[0] != '\n') {
write(fd[1], line, sizeof(line));
}
close(fd[1]);
}
else {
close(fd[1]);
dup2(fd[0], STDIN_FILENO);
//while(read(fd[0], buffer, sizeof(buffer)))
// printf("> %s", buffer);
execlp("./aux", "aux", (char *)0);
}
return 0;
}
"aux.c"
#include <stdio.h>
#include <stdlib.h>
int main() {
char data[100];
while(fgets(data, sizeof(data), stdin))
printf(">%s\n", data);
return 0;
}
sample input/output
this
>this
is a test
>
> test
only prints larger text with random \n
>
>ts larger text with random \n
Your call to write(2) is wrong (you always write 100 bytes even for shorter line-s):
write(fd[1], line, sizeof(line)); // WRONG
should probably be using strlen(3)
size_t ll = strlen(line);
ssize_t wc = write(fd[1], line, ll);
if (wc != ll)
fprintf(stderr, "write was wrong (only %d, wanted %d) - %s\n",
(int) wc, (int) ll, strerror(errno));
Since you want to write only the filled bytes of the line buffer, not always 100 bytes each time (some of them not being initialized).
In your case sizeof(data) is 100 since you declared char data[100];
Please read carefully the documentation of every used function (and also ALP or some other book on Unix/POSIX/Linux programming). The documentation of strerror(3) and of errno(3) tells that you need to add:
#include <string.h>
#include <errno.h>
Actually, if you want to use read(2) and write(2) directly (without stdio(3)) you should prefer using larger buffers (e.g. 4Kbytes each at least for efficiency) and you need to manage partial read-s and write-s and do your buffering by yourself.
BTW, compile with all warnings and debug info: gcc -Wall -Wextra -g and learn to use the gdb debugger and strace(1) (and valgrind). In general, be scared of undefined behavior (however, at a first glance, your program don't seem to have UB).
Notice that execlp(3) could fail. Consider adding some call to perror(3) after it.

Simple 30 char buffer PIPE prints non printable characters, no idea why

I used a simple fork() to simulate client / server then a very simple pipe to send / receive a char buffer of max 30 length, but it ends up printing non printable characters (small "?" and a box with 4 ones and zeroes) AFTER the desired word.
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <string.h>
int main () {
int pipefd[2];
int cpid;
char buf[31];
if (pipe(pipefd) == -1) {
perror("pipe");
exit(EXIT_FAILURE)
}
cpid = fork();
if (cpid == -1) P
perror("cpid");
exit(EXIT_FAILURE);
}
if (cpid == 0) { // child reads from pipe
close (pipefd[1]); // close unused write end
read (pipefd[0], &buf, 30); // if I use 30 instead of strlen(buf) it prints Server transmit: Server receives. It does not wait for write. Makes no sense
printf ("Server receives: %s", buf);
close (pipefd[0])l
exit (EXIT_SUCCESS);
}
else { // parent writes to pipe
close (pipefd[0]); // closing unused read end;
char buf2[30];
printf("Server transmits: ");
scanf ("%s", buf2);
write (pipefd[1], buf2, strlen(buf2));
close(pipefd[1]);
wait(NULL);
exit(EXIT_SUCCESS);
}
return 0;
}
Also if I write more than one word it forgets about the second. In c++ I used getline (cin, string) but that's not an option here.
Also used read (pipefd[0], &buf, sizeof(buf));, now it prints in the correct order (no idea why strlen did not work) but I still get non printable characters at the end.
When you write (pipefd[1], buf2, strlen(buf2)); You neglect to put the '\0' in the stream. Change that to:
write (pipefd[1], buf2, strlen(buf2)+1);
And your string will now contain the null terminator, preventing the garbage at the end.
Using read (pipefd[0], &buf, strlen(buf)) did not work because buf is uninitialized. strlen is a simple function which looks for the terminating null at the end on the string, stopping when it's found. Unlike the length functions of C++ vectors, C functions have no way of accessing memory metadata. (sizeofis an operator)

String copy using pipes

i have written the following code to copy a string "hello world" to another char array using fork and pipes instead of using standard library functions or standard i/o streams. The program is compiling successfully but i am not getting any output. Even, the printf's output are not being shown.
# include <string.h>
# include <unistd.h>
# include <stdio.h>
char string[] = "hello world";
int main()
{
int count, i;
int toPar[2], toChild[2];
char buf[256];
pipe(toPar);
pipe(toChild);
if (fork() == 0)
{
printf("\n--- child process ---");
close(0);
dup(toChild[0]);
close(1);
dup(toPar[1]);
close(toPar[1]);
close(toChild[0]);
close(toPar[0]);
close(toChild[1]);
for (;;)
{
if ((count = read(0, buf, sizeof(buf))) == 0)
break;
printf("\nChild buf: %s", buf);
write(1, buf, count);
}
}
printf("\n--- parent process ---");
close(1);
dup(toChild[1]);
close(0);
dup(toPar[0]);
close(toPar[1]);
close(toChild[0]);
close(toPar[0]);
close(toChild[1]);
for (i = 0; i < 15; i++)
{
write(1, string, strlen(string));
printf("\nParent buf: %s", buf);
read(0, buf, sizeof(buf));
}
return 0;
}
Your printfs are writing to stdout - but in both the parent and child, you've redirected file descriptor 1 to a pipe, so that's where the printf output will go.
Instead of printf(...), use fprintf(stderr, ...) - then you'll be able to see the output, since stderr is still pointing to your terminal.
Note that you have a couple of bugs:
the child should call _exit(0) when it is done, otherwise it will drop into the parent code;
the write should use strlen(string) + 1, so that it writes the nul terminator.
Try adding a "\n", like printf("\nParent buf: %s\n", buf);
I'd guess that those pipes are doing blocking IO, so read will simply not return unless the pipe is closed by the other process. That, and printf doing buffered IO, prevents you from getting any output.

Resources