String copy using pipes - c

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.

Related

Loop doesn't finish when reading from pipe

I'm following the free book Operating Systems: Three Easy Pieces by Arpaci-Dusseau, and despite being very new to C programming (constructive criticism is welcomed), I tried my luck on coding problem 8 from chapter 5:
Write a program that creates two children, and connects the standard
output of one to the standard input of the other, using the pipe()
system call.
Here is my attempt (some error-checking is removed for brevity):
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <sys/wait.h>
int main(int argc, char *argv[]) {
int pipefd[2];
char buf;
pipe(pipefd);
int rc1 = fork();
if (rc1 == 0) {
// Child 1, reading from argv, writing to pipe
close(pipefd[0]);
write(pipefd[1], argv[1], strlen(argv[1]));
close(pipefd[1]);
printf("Child 1 done.\n");
} else {
// Parent
wait(NULL);
int rc2 = fork();
if (rc2 == 0) {
// Child 2, reading from pipe, writing to stdout
printf("Entering child 2\n");
close(pipefd[1]);
while (read(pipefd[0], &buf, 1) > 0) {
write(STDOUT_FILENO, &buf, 1);
}
write(STDOUT_FILENO, "\n", 1);
printf("Wrote to stdout!\n");
close(pipefd[0]);
printf("Child 2 done.\n");
} else {
// Still parent
wait(NULL);
printf("Parent finished running.\n");
}
}
}
which generates the following output, and hangs:
$ ./myprogram "Hello world"
Child 1 done.
Entering child 2
Hello world█
where █ is the shell cursor, i.e. the while loop hasn't exited to reach the writing of the newline character thereafter.
I did, however, get this to work by replacing char buf; with char buf[1024]; and the lines
while (read(pipefd[0], &buf, 1) > 0) {
write(STDOUT_FILENO, &buf, 1);
}
with
int n = read(pipefd[0], &buf, 1024);
write(STDOUT_FILENO, &buf, n);
So, now it works. But I don't understand why the previous version did not work. That reading loop was practically identical to the one used in the example at the bottom of the pipe(2) man-pages, which I have verified to be working correctly for me. But why doesn't it work in my own little program?
Possible duplicate questions:
(1) Solution is to close unused ends of pipe. I believe I have done that correctly.
(2) Solution is to break on return codes <= 0, not on < 0. I believe I have done that correctly too.
pipefd[1]) never gets closed in the original parent process. So read(pipefd[0], &buf, 1) will hang in the second child process.
This version
int n = read(pipefd[0], &buf, 1024);
write(STDOUT_FILENO, &buf, n);
doesn't hang because there's no loop. The second child process reads the data written by
write(pipefd[1], argv[1], strlen(argv[1]));
and then continues onward, never checking pipefd[0] again. So it doesn't matter if read(pipefd[0],...) would hang.

Usage of pipes and dup2 in 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.

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 work with two pipes while sending data between child and parent process?

I am learning about system calls, fork and pipe. I am creating a C program in which the parent process sends a character array to the child process and child process capitalizes the first 4 characters of the array and sends it back. The array is sent properly from parent to child, child makes the conversion and even writes to the second pipe properly, but parent process is not able to read the new array from the pipe 2.
I've tried closing the unnecessary descriptors as well, but that didn't work. I read somewhere that parent process might be finishing before there is something to read from the pipe, for which I tried wait function(but I might have done this wrong. I am not sure.)
I tried checking the size of values sent and received by the processes,
Parent writes (8)
Child reads (8)
Child writes (8)
Parent reads (1)
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
int main(int argc, char *argv[]){
int pipe1[2];
int pipe2[2];
char str[8], str1[8];
pid_t pid;
if(pipe(pipe1) < 0){
perror("Pipe 1 not created\n");
exit(EXIT_FAILURE);
}
if(pipe(pipe2) < 0){
perror("Pipe 2 not created\n");
exit(EXIT_FAILURE);
}
pid = fork();
if (pid == 0){
close(pipe1[1]);
close(pipe2[0]);
printf("\nChild Process");
ssize_t rd_stat_child = read(pipe1[0], str, 8*sizeof(char));
if(rd_stat_child > 0){
printf("rc%zd\n", rd_stat_child);
for(int i = 0; i < 4; i++){
str[i] = ((char)(((int)str[i])-32));
}
printf("\nFinal str in child: %s\n", str);
ssize_t wr_stat_child = write(pipe2[1], str, 8*sizeof(char));
printf("wc%zd\n", wr_stat_child);
if(wr_stat_child != sizeof(str)){
perror("Sending to parent failed");
exit(EXIT_FAILURE);
}
}else{
perror("Child failed to read");
exit(EXIT_FAILURE);
}
}else if (pid > 0){
close(pipe1[0]);
close(pipe2[1]);
printf("\nParent Process");
printf("\nEnter a 8 character string: ");
scanf("%s", str);
if(sizeof(str)/(8*sizeof(char)) != 1){
perror("Size of string greater than 8\n");
exit(EXIT_FAILURE);
}else{
ssize_t wr_stat_parent = write(pipe1[1], str, 8*sizeof(char));
printf("wp%zd\n", wr_stat_parent);
if(wr_stat_parent != sizeof(str)){
perror("Parent failed writing.\n");
exit(EXIT_FAILURE);
}
ssize_t rd_stat_parent = read(pipe2[0], str, 8*sizeof(char));
close(pipe2[0]);
if(rd_stat_parent <= sizeof(str)){
printf("rp%zd\n", rd_stat_parent);
printf("\nParent Recieved\n %s", str);
}else{
perror("Parent error while reading\n");
exit(EXIT_FAILURE);
}
}
}
return 0;
}
Expected Output
Parent Process
(input) >> lkybzqgv
Child Process
(process) >> LKYBzqgv
Parent Process
(output) >> LKYBzqgv
Actual Output
Parent Process
(input) >> lkybzqgv
Child Process
(process) >> LKYBzqgv
Parent Process
(output) >> kybzqgv
Your string-handling is broken. You need an array of length 9 to hold a string of length 8. (Remember that strings in c are zero-terminated). DO NOT WRITE scanf("%s", str); TO READ STRINGS !! That is just as bad as using gets(). It allows you to overflow the buffer (Which actually happens in your case). Read strings like this:
scanf("%8s", str);
This will read at most 8 (non-whitespace) characters and store them together with the zero-termination in str. (remember again that str must be large enough for 8 charecters + 1 termination character)
Then to check the length of a string, use strlen(), do not use sizeof(). sizeof may only tell the size of the array holding the string, or the pointer pointing to the string. Remember that the array holding the string must be at least 1 character larger than the string, but is allowed to be larger than that. And the size of the array is fixed at creation. It doesn't change size depending on what you put in it.
Oh, and by the way. You don't send/receive the termination character, so you have to set it yourself manually after you have called read():
read(pipe1[0], str, 8);
str[8] = 0;
There may be other problems with your code, but unless you fix the string-issues, you have undefined behavior, and everything else doesn't really matter.

Capture stdout to a string and output it back to stdout in C

C language is used. I have a function that writes to stdout.
I would like to capture that output, modify it a bit (replacing some strings). And than output it again to the stdout. So I want to start with:
char huge_string_buf[MASSIVE_SIZE];
freopen("NUL", "a", stdout); -OR- freopen("/dev/null", "a", stdout);
setbuf(stdout, huge_string_buffer);
/* modify huge_string_buffer */
The question is now, how do I output the huge_string_buffer back to the original stdout?
One idea is to mimic the functionality of the standard Unix utility tee, but to do so entirely within your program, without relying on outside redirection.
So I've written a simple function, mytee(), which seems to work. It uses shmget(), pipe(), fork(), and dup2():
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/shm.h>
static char *mytee(int size) {
int shmid = shmget(IPC_PRIVATE, size + 1, 0660 | IPC_CREAT);
int pipe_fds[2];
pipe(pipe_fds);
switch (fork()) {
case -1: // = error
perror("fork");
exit(EXIT_FAILURE);
case 0: { // = child
char *out = shmat(shmid, 0, 0), c;
int i = 0;
out[0] = 0;
dup2(pipe_fds[0], 0); // redirect pipe to child's stdin
setvbuf(stdout, 0, _IONBF, 0);
while (read(0, &c, 1) == 1 && i < size) {
printf("<%c>", c); // pass parent's stdout to real stdout,
out[i++] = c; // and then buffer in mycapture buffer
out[i] = 0; // (the extra <> are just for clarity)
}
_exit(EXIT_SUCCESS);
}
default: // = parent
dup2(pipe_fds[1], 1); // replace stdout with output to child
setvbuf(stdout, 0, _IONBF, 0);
return shmat(shmid, 0, 0); // return the child's capture buffer
}
}
My test program is:
int main(void) {
char *mycapture = mytee(100); // capture first 100 bytes
printf("Hello World"); // sample test string
sleep(1);
fprintf(stderr, "\nCaptured: <%s>\n", mycapture);
return 0;
}
The output is:
<H><e><l><l><o>< ><W><o><r><l><d>
Captured: <Hello World>
To use this in your application, in mytee() you'll need to replace the test statement printf("<%c>", c) with just write(1, &c, 1). And you may need to handle signals in the call to read. And after each of the two dup2()'s, you may want to add:
close(pipe_fds[0]);
close(pipe_fds[1]);
For a reference on this sort of stuff, see for example the excellent and short 27-year-old 220-page O'Reilly book Using C on the Unix System by Dave Curry.
The Unix way to do this is really to just write a little program that does the input processing you need, and then pipe the output of that other program to it on the command line.
If you insist on keeping it all in your C program, what I'd do instead is rewrite that function to have it send its output to a given char buffer (preferably returning the buffer's char *), so that it can be sent to stdout or processed as the client desires.
For example, the old way:
void usage () {
printf ("usage: frob sourcefile [-options]\n");
}
...and the new way:
char * usage(char * buffer) {
strcpy (buffer, "usage: frob sourcefile [-options]\n");
return buffer;
}
I really don't like tricky games with file descriptors. Can't you modify the function so that it returns its data some other way than by writing to stdout?
If you don't have access to the source code, and you can't do that, then I would suggest breaking out the code that writes to stdout into a small separate program, and run that as another process. It is easy and clean to redirect output from a process (maybe through a named pipe), and then you will have no problem with outputting to stdout from the process that receives the data.
Also, depending on the sort of editing you wish to do, you might be better off using a high-level language like Python to edit the data.
char huge_string_buf[MASSIVE_SIZE];
FILE stdout_ori=fdopen(stdout,"a");
freopen("NUL", "a", stdout); -OR- freopen("/dev/null", "a", stdout);
setbuf(stdout, huge_string_buffer);
/* modify huge_string_buffer */
//Write to stdout_fdopen
If you are on a unix system, you can use pipes (you don't need to use fork). I'll try to thoroughly comment the code below so it doesn't look like I am doing any "magic."
#include <stdio.h>
#include <unistd.h>
int main()
{
// Flush stdout first if you've previously printed something
fflush(stdout);
// Save stdout so it can be restored later
int temp_stdout;
temp_stdout = dup(fileno(stdout));
// Redirect stdout to a new pipe
int pipes[2];
pipe(pipes);
dup2(pipes[1], fileno(stdout));
// Do whatever here. stdout will be captured.
func_that_prints_something();
// Terminate captured output with a zero
write(pipes[1], "", 1);
// Restore stdout
fflush(stdout);
dup2(temp_stdout, fileno(stdout));
// Print the captured output
while (1)
{
char c;
read(pipes[0], &c, 1);
if (c == 0)
break;
putc(c, stdout);
}
// Alternatively, because the output is zero terminated, you could
// print using printf. You just need to make sure you read enough
// from the captured output
const int buffer_size = 1024;
char buffer[buffer_size];
read(pipes[0], buffer, buffer_size);
printf(buffer);
return 0;
}

Resources