#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
/* Read characters from the pipe and echo them to stdout. */
void read_from_pipe (int file)
{
FILE *stream;
int c;
stream = fdopen (file, "r");
while ((c = fgetc (stream)) != EOF)
putchar (c);
fclose (stream);
}
/* Write some random text to the pipe. */
void write_to_pipe (int file)
{
FILE *stream;
stream = fdopen (file, "w");
fprintf (stream, "Hello from Parent!\n");
fclose (stream);
}
int main (void)
{
int pid;
int mypipe[2];
/* Create the pipe. */
if (pipe (mypipe))
{
printf("Pipe failed.\n");
return EXIT_FAILURE;
}
/* Create the child process. */
pid = fork ();
if (pid == 0)
{
/* This is the child process.
Close other end first. */
close (mypipe[1]);
read_from_pipe (mypipe[0]);
return EXIT_SUCCESS;
}
else if (pid < 0)
{
/* The fork failed. */
printf ("Fork failed.\n");
return EXIT_FAILURE;
}
else
{
/* This is the parent process.
Close other end first. */
close (mypipe[0]);
write_to_pipe (mypipe[1]);
return EXIT_SUCCESS;
}
}
I want to write simple program to do this task:
Parent process creates a pipe using the pipe() system call.
Parent process creates a child process.
Parent process sends a message "Hello from Parent" to the child.
Child process prints the message to the screen.
This code creates File stream. I can't understand why. Without file stream can I do this thing..?
This answer may be helpful for a future Googler.
I guess this is what you are looking for.
Here is what I wrote:
#include <stdio.h>
#include <unistd.h>
int main(){
int p, f;
int rw_setup[2];
char message[20];
p = pipe(rw_setup);
if(p < 0){
printf("An error occured. Could not create the pipe.");
_exit(1);
}
f = fork();
if(f > 0){
write(rw_setup[1], "Hello from Parent", 18);
}
else if(f == 0){
read(rw_setup[0],message,18);
printf("%s %d\n", message, r_return);
}
else{
printf("Could not create the child process");
}
return 0;
}
To create child process we use fork(). fork() returns :
<0 fail to create child (new) process
=0 for child process
>0 i.e process ID of the child process to the parent process. When >0 parent process will execute.
pipe() is used for passing information from one process to another. pipe() is unidirectional therefore, for two-way communication between processes, two pipes can be set up, one for each direction.
You can read more details here.
You can also use File descriptors.
Basically, when you call pipe(), the operating system prepares two file descriptors and writes their "names" to the argument (mypipe in this case).
You can use these file descriptors without opening any file streams by using the standard operating system calls: write(), read(), close(), etc.
Related
I create a function exec_in_child which takes the command arguments, pipe file descriptors (fds), read_flag and write_flag as input. When write_flag is set to 1, the child process should duplicate stdout to fds[1], and then execute the command. When read_flag is set to 1, the child should duplicate the stdin to fds[0] and the execute the command.
Do I have to close one end of the pipe when I'm reading/writing to
the other end?
The code below doesn't work. I'm trying to execute /bin/ls inside a child process, write the stdout to the pipe, and then read
it off in the parent process and print it. I'm not able to read in
the parent process.
Can I read and write to the pipe inside the same process without closing other? This situation arises when I want to child to read
from pipe, execute, and then write to the pipe.
#include <stdio.h> /* printf */
#include <stdlib.h>
#include <string.h> /* strlen, strcpy */
int exec_in_child(char *arguments[], const int temp[], int , int);
int main()
{
ssize_t bytes_read;
char *curr_dir = (char *)malloc(500);
int pipefd[2];
if (pipe(pipefd) == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}
char *arguments[] = {"/bin/pwd",0};
exec_in_child(arguments, pipefd, 0, 1);
bytes_read = read(pipefd[0], curr_dir, strlen(curr_dir));
printf("%s = %d\n", "bytes read from pipe" ,(int)bytes_read);
printf("%s: %s\n","character read from the pipe",curr_dir);
return 0;
}
int exec_in_child(char * arguments[], const int fds[], int read_flag, int write_flag) {
pid_t pid;
pid = fork();
if (pid < 0) {
perror("Error: Fork Failed");
}
else if (pid == 0){ /*inside the child process */
if (read_flag == 1) {
dup2(fds[0], 0);
perror("Dup2 stdin");
}
if (write_flag == 1) {
dup2(fds[1], 1);
perror("Dup2 stdout");
}
execv(arguments[0], arguments);
perror("Error in child");
exit(1);
} /* if (pid == 0) */
else {
while(pid != wait(0));
} /* if(pid < 0) */
return 0;
}
I get this result:
hmwk1-skk2142(test) > ./a.out
Dup2 stdout: Success
bytes read from pipe = 0
character read from the pipe:
To answer your questions:
1) You do not need to close either end of the pipe in order to use the other end. However, you generally want to close any end(s) of the pipe you're not using. The biggest reason to do this is that the pipe will only close when all open write file descriptors are closed.
2) Your code isn't working because you're using strlen() improperly. This function calculates the length of a string by searching for the null (0) character. When you malloc() the storage for curr_dir you have no guarantee of what resides there (though it will usually be zeroed, as in this case).
Thus, your call strlen(curr_dir) returns zero, and the read() system call thinks you want to read up to zero bytes of data. Change your read call to the following:
bytes_read = read(pipefd[0], curr_dir, 500);
and your code will work perfectly.
3) You can read and write to any pipe you've got a valid file descriptor to. A single process can absolutely read and write the same pipe.
I am trying to write from one process to the another using two separate pipes. In the following manner:
child1 writes to parent (using pipe1)
parent writes to child2 (using pipe2)
I have no problems writing to the parent, but when I try to relay the data to child2, the file descriptor appears to be NULL and I'm not sure why. For clarity purposes, I tried to emboldened the areas that I am having problems with. I also removed a lot of the error handling.
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main (void)
{
pid_t pid;
pid_t pid1;
int mypipe[2];
int mypipe1[2];
int file;
char buf[100];
FILE *stream;
FILE *stream2;
FILE *rm;
ssize_t numbersread;
if (pipe (mypipe))
{
fprintf (stderr, "Pipe failed.\n");
return EXIT_FAILURE;
}
if (pipe (mypipe1))
{
fprintf (stderr, "Pipe2 failed.\n");
return EXIT_FAILURE;
}
/* CREATE THE FIRST CHILD HERE. */
pid = fork ();
if (pid == (pid_t) 0)
{
rm = fopen("Readme.txt","r");
//10 BYTES AT A TIME
close(mypipe[0]);
for(k=0;k<=10;k++)
{
transmitor(mypipe[1],rm); // GO READ FILE AND THEN WRITE ON PIPE
}
fclose(rm);
return EXIT_SUCCESS;
}
// BACK TO THE PARENT PROCESS
else
{
/*OBJECTIVES:
1. READ THE FILE FROM THE PIPE
2. WRITE THE FILE ONTO A SECOND PIPE
3.SEND IT TO THE RECEIVER
*/
FILE *file1;
ssize_t numbersread1;
file1 = fdopen(mypipe[0],"r");
close (mypipe[1]);
close(mypipe1[0]);
stream2 = fdopen(mypipe1[1],"w");
while(!feof(file1)){
numbersread1 = fread(buf, 1, (sizeof buf),file1);
printf("%zd\n", numbersread1);
**fwrite(buf,1,numbersread1,stream2);**
buf[numbersread1] = 0;
}
printf("%s\n","finished parent");
fclose(file1);// FINISHED READING
fclose(stream2);
** /* CREATE THE SECOND CHILD HERE #2. */
/*OBJECTIVES:
1. READ DATA FROM PIPE
2. WRITE DATA TO FILE*/
pid1 = fork ();
sleep(2);
if (pid1 == (pid_t) 0)
{
/* This is the child process.
Close read end first. */
FILE *stream3;
stream3 = fdopen(mypipe1[0],"r");
close (mypipe1[1]);
if(stream3==NULL)
{
printf("%s","NULL Stream3 Variable");
}
else
{
while (!feof(stream3)) {
printf("\r\nIN WHILE\r\n");
numbersread = fread(buf, 1, (sizeof buf),stream3);
printf("%zd\n", numbersread);
buf[numbersread] = 0;
}
fclose(stream3);
}**
printf("%s","FINISHED RECEIVER");
return EXIT_SUCCESS;
}
return EXIT_SUCCESS;
}// THIS CLOSES THE FIRST ENTRANCE TO THE PARENT PROCESS WHERE WE ARE WRITING TO THE FIRST RECEIVER
}// THIS IS THE END OF THE MAIN FUNCTION
You close(2) file descriptors you use later, for example this bit of your code:
file1 = fdopen(mypipe[0],"r");
close (mypipe[1]);
close(mypipe1[0]);
you close mypipe1[0]. Further down you do:
FILE *stream3;
stream3 = fdopen(mypipe1[0],"r");
close (mypipe1[1]);
therefore stream3 will be NULL.
I would also strongly recommend to name the variables a bit more what they do. For example mypipe could be c1_to_parent and mypipe1 could be parent_to_c2. That would make your code a lot more readable.
I have a task in Linux and I can't get it work.
I have a program that receives a text file as parameter. It then creates a child process using fork() and sends to the child process, line by line the content of the text file received as parameter. The child process needs to count the lines and return to the parent process the number of lines received.
This is what I have until now, but somewhat the child process does not receive all the lines. For my test I used a text file with 9 lines. The parent sent 9 lines as strings but the child process received only 2 or 3 of them.
What am I doing wrong?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
char string[80];
char readbuffer[80];
int pid, p[2];
FILE *fp;
int i=0;
if(argc != 2)
{
printf("Syntax: %s [file_name]\n", argv[0]);
return 0;
}
fp = fopen(argv[1], "r");
if(!fp)
{
printf("Error: File '%s' does not exist.\n", argv[1]);
return 0;
}
if(pipe(p) == -1)
{
printf("Error: Creating pipe failed.\n");
exit(0);
}
// creates the child process
if((pid=fork()) == -1)
{
printf("Error: Child process could not be created.\n");
exit(0);
}
/* Main process */
if (pid)
{
// close the read
close(p[0]);
while(fgets(string,sizeof(string),fp) != NULL)
{
write(p[1], string, (strlen(string)+1));
printf("%s\n",string);
}
// close the write
close(p[1]);
wait(0);
}
// child process
else
{
// close the write
close(p[1]);
while(read(p[0],readbuffer, sizeof(readbuffer)) != 0)
{
printf("Received string: %s\n", readbuffer);
}
// close the read
close(p[0]);
}
fclose(fp);
}
A pipe is a unidirectional interprocess communication channel. You have to create 2 pipes, one to speak to the child process, the other to read data back.
Remember to close the unused side of the pipe on both processes.
You are sending the null terminator to the other process:
write(p[1], string, (strlen(string)+1));
That makes the result confusing because when you print what you've received, you only see up to the null terminator.
If you do this instead:
write(p[1], string, strlen(string));
you should get what you expect.
You're not counting the number of lines, you're counting the number of times read(2) returns.
When using pipes, read(2) will pull as much data as possible from the pipe: min(pipe_available, space_available). It doesn't care for newlines, 0 bytes etc. Simple tricks to make it work:
Use a loop to walk readbuffer and look for \n
Use fdopen + fgets (I have a feeling this is probably flawed)
look into manpage of pipe ( man 2 pipe ), the program you're trying to write is as an example there, compare it with yours :)
Program runs once and it both throws data to the pipe and gets it out in the same condition that should be mutually exclusive (in if and else).
What I don't get here?
How does that work?
I have no experience with this kind of programming.
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
Read characters from the pipe and echo them to stdout.
void
read_from_pipe (int file)
{
FILE *stream;
int c;
stream = fdopen (file, "r");
while ((c = fgetc (stream)) != EOF)
putchar (c);
fclose (stream);
}
Write some random text to the pipe.
void
write_to_pipe (int file)
{
FILE *stream;
stream = fdopen (file, "w");
fprintf (stream, "hello, world!\n");
fprintf (stream, "goodbye, world!\n");
fclose (stream);
}
int
main (void)
{
pid_t pid;
int mypipe[2];
/* Create the pipe. */
if (pipe (mypipe))
{
fprintf (stderr, "Pipe failed.\n");
return EXIT_FAILURE;
}
/* Create the child process. */
pid = fork ();
if (pid == (pid_t) 0)
{
/* This is the child process.
Close other end first. */
close (mypipe[1]);
read_from_pipe (mypipe[0]);
return EXIT_SUCCESS;
}
else if (pid < (pid_t) 0)
{
/* The fork failed. */
fprintf (stderr, "Fork failed.\n");
return EXIT_FAILURE;
}
else
{
/* This is the parent process.
Close other end first. */
close (mypipe[0]);
write_to_pipe (mypipe[1]);
return EXIT_SUCCESS;
}
}
Right after the line that says:
pid = fork();
You no longer have one program, but two distinct ones (when fork succeeds).
It's not the same program that runs both. In the child process, fork returns 0, but in the parent it returns the child PID.
The parent runs one branch of the if/else construct, the child runs another. (And a third is run, in the parent only, if fork fails.)
I am confused about how popen() redirects stdin, stdout and stderr of the child process in unix. The man page on popen() is not very clear in this regard. The call
FILE *p = popen("/usr/bin/foo", "w");
forks a child process and executes a shell with arguments "-c", "/usr/bin/foo", and redirects stdin of this shell (which is redirected stdin of foo), stdout to p. But what happens with stderr? What is the general principle behind it?
I noticed that, if I open a file in foo (using fopen, socket, accept etc.), and the parent process has no stdout, it gets assigned the next available file number, which is 1 and so on. This delivers unexpected results from calls like fprintf(stderr, ...).
It can be avoided by writing
FILE *p = popen("/usr/bin/foo 2>/dev/null", "w");
in the parent program, but are their better ways?
popen(3) is just a library function, which relies on fork(2) and pipe(2) to do the real work.
However pipe(2) can only create unidirectional pipes. To send the child process input, and also capture the output, you need to open two pipes.
If you want to capture the stderr too, that's possible, but then you'll need three pipes, and a select loop to arbitrate reads between the stdout and stderr streams.
There's an example here for the two-pipe version.
simple idea: why not add "2>&1" to the command string to force the bash to redirect stderr to stdout (OK, writing to stdin still is not possible but at least we get stderr and stdout into our C program).
The return value from popen() is a normal standard I/O stream in all
respects save that it must be closed with pclose() rather than
fclose(3). Writing to such a stream writes to the standard input of
the command; the command's standard output is the same as that of the
process that called popen(), unless this is altered by the command
itself. Conversely, reading from a "popened" stream reads the
command's standard output, and the command's standard input is the
same as that of the process that called popen().
From its manpage, so it allows you to read the commands standard output or write into its standard input. It doesn't say anything about stderr. Thus that is not redirected.
If you provide "w", you will send your stuff to the stdin of the shell that is executed. Thus, doing
FILE * file = popen("/bin/cat", "w");
fwrite("hello", 5, file);
pclose(file);
Will make the shell execute /bin/cat, and pass it the string "hello" as its standard input stream. If you want to redirect, for example stderr to the file "foo" do this first, before you execute the code above:
FILE * error_file = fopen("foo", "w+");
if(error_file) {
dup2(fileno(error_file), 2);
fclose(error_file);
}
It will open the file, and duplicate its file-descriptor to 2, closing the original file descriptor afterwards.
Now, if you have your stdout closed in your parent, then if the child calls open it will get 1, since that's (if stdin is already opened) the next free file-descriptor. Only solution i see is to just use dup2 and duplicate something into that in the parent, like the above code. Note that if the child opens stdout, it will not make stdout open in the parent too. It stays closed there.
Check out popenRWE by Bart Trojanowski. Clean way to do all 3 pipes.
if you just want to get STDERR, try this:
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <malloc.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
/*
* Pointer to array allocated at run-time.
*/
static pid_t *childpid = NULL;
/*
* From our open_max(), {Prog openmax}.
*/
static int maxfd;
FILE *
mypopen(const char *cmdstring, const char *type)
{
int i;
int pfd[2];
pid_t pid;
FILE *fp;
/* only allow "r" "e" or "w" */
if ((type[0] != 'r' && type[0] != 'w' && type[0] != 'e') || type[1] != 0) {
errno = EINVAL; /* required by POSIX */
return(NULL);
}
if (childpid == NULL) { /* first time through */
/* allocate zeroed out array for child pids */
maxfd = 256;
if ((childpid = calloc(maxfd, sizeof(pid_t))) == NULL)
return(NULL);
}
if (pipe(pfd) < 0)
return(NULL); /* errno set by pipe() */
if ((pid = fork()) < 0) {
return(NULL); /* errno set by fork() */
} else if (pid == 0) { /* child */
if (*type == 'e') {
close(pfd[0]);
if (pfd[1] != STDERR_FILENO) {
dup2(pfd[1], STDERR_FILENO);
close(pfd[1]);
}
} else if (*type == 'r') {
close(pfd[0]);
if (pfd[1] != STDOUT_FILENO) {
dup2(pfd[1], STDOUT_FILENO);
close(pfd[1]);
}
} else {
close(pfd[1]);
if (pfd[0] != STDIN_FILENO) {
dup2(pfd[0], STDIN_FILENO);
close(pfd[0]);
}
}
/* close all descriptors in childpid[] */
for (i = 0; i < maxfd; i++)
if (childpid[i] > 0)
close(i);
execl("/bin/sh", "sh", "-c", cmdstring, (char *)0);
_exit(127);
}
/* parent continues... */
if (*type == 'e') {
close(pfd[1]);
if ((fp = fdopen(pfd[0], "r")) == NULL)
return(NULL);
} else if (*type == 'r') {
close(pfd[1]);
if ((fp = fdopen(pfd[0], type)) == NULL)
return(NULL);
} else {
close(pfd[0]);
if ((fp = fdopen(pfd[1], type)) == NULL)
return(NULL);
}
childpid[fileno(fp)] = pid; /* remember child pid for this fd */
return(fp);
}
int
mypclose(FILE *fp)
{
int fd, stat;
pid_t pid;
if (childpid == NULL) {
errno = EINVAL;
return(-1); /* popen() has never been called */
}
fd = fileno(fp);
if ((pid = childpid[fd]) == 0) {
errno = EINVAL;
return(-1); /* fp wasn't opened by popen() */
}
childpid[fd] = 0;
if (fclose(fp) == EOF)
return(-1);
while (waitpid(pid, &stat, 0) < 0)
if (errno != EINTR)
return(-1); /* error other than EINTR from waitpid() */
return(stat); /* return child's termination status */
}
int shellcmd(char *cmd){
FILE *fp;
char buf[1024];
fp = mypopen(cmd,"e");
if (fp==NULL) return -1;
while(fgets(buf,1024,fp)!=NULL)
{
printf("shellcmd:%s", buf);
}
pclose(fp);
return 0;
}
int main()
{
shellcmd("ls kangear");
}
and you will get this:
shellcmd:ls: cannot access kangear: No such file or directory