I can barely understand the man page for pipe, so I kinda need help understanding how to take a piped input in an external executable.
I have 2 programs: main.o & log.o
I written main.o to fork. Here is what it is doing:
Parent fork will pipe data to the child
Child fork will exec log.o
I need the child fork for main to pipe to STDIN of log.o
log.o simply takes STDIN & logs with time stamp to a file.
My code is composed of some code from various StackOverflow pages I dont remember & the man page for pipe:
printf("\n> ");
while(fgets(input, MAXINPUTLINE, stdin)){
char buf;
int fd[2], num, status;
if(pipe(fd)){
perror("Pipe broke, dood");
return 111;
}
switch(fork()){
case -1:
perror("Fork is sad fais");
return 111;
case 0: // Child
close(fd[1]); // Close unused write end
while (read(fd[0], &buf, 1) > 0) write(STDOUT_FILENO, &buf, 1);
write(STDOUT_FILENO, "\n", 1);
close(fd[0]);
execlp("./log", "log", "log.txt", 0); // This is where I am confused
_exit(EXIT_FAILURE);
default: // Parent
data=stuff_happens_here();
close(fd[0]); // Close unused read end
write(fd[1], data, strlen(data));
close(fd[1]); // Reader will see EOF
wait(NULL); // Wait for child
}
printf("\n> ");
}
I suppose this is what you're going to do:
1. main fork, parent pass message to child via pipe.
2. child receive message from pipe, redirect message to STDIN, execute log.
3. log receive message from STDIN, do something.
the key to do this is dup2 to redirect file descriptor, from pipe to STDIN.
This is the modified simple version:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
int main(int argc, char *argv[]) {
int fd[2];
char buf[] = "HELLO WORLD!";
if(pipe(fd)){
perror("pipe");
return -1;
}
switch(fork()){
case -1:
perror("fork");
return -1;
case 0:
// child
close(fd[1]);
dup2(fd[0], STDIN_FILENO);
close(fd[0]);
execl("./log", NULL);
default:
// parent
close(fd[0]);
write(fd[1], buf, sizeof(buf));
close(fd[1]);
wait(NULL);
}
printf("END~\n");
return 0;
}
I can suggest a simpler approach. There's a function called popen(). It works very similar to the system() function except you can read or write to/from the child stdin/stdout.
Example:
int main(int argc, char* argv[])
{
FILE* fChild = popen("logApp.exe", "wb"); // the logger app is another application
if (NULL == fChild) return -1;
fprintf(fChild, "Hello world!\n");
pclose(fChild);
}
Write "man popen" in your console for a full description.
You could use dup2
See Mapping UNIX pipe descriptors to stdin and stdout in C
Related
There are already multiple answers to this question but none of them have been able to help me solve my problem. I am trying to understand IPC using an anonymous pipe in C.
From my understanding of pipes, they are a one way communication channel with one read end and one write end.
Assuming we have two c files one named parent.c and the other child.c. What I am trying to achieve is to be able to create 5 or more child processes. After this the parent and the child should communicate with the child processes through standard input and standard output, but since I want to be able to print what the parent receives from the child I'll instead tie the pipes to standard error output using dup2.
In summary
1. Run a parent program which spins up 5 or more child processes and runs them.
2. The child process waits for an input from the parent using scanf.
3. The parent sends a message to the child process.
4. The child process receives the message and sends a reply to the parent and exits.
5. The parent process prints the received message and prints it then exits.
parent.c
// Parentc
#include <stdio.h>
#include <stdlib.h>
#include <uinstd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc, const char *argv[]){
// File descriptors for the pipes
int read_pipe[2]; // From child to parent
int write_pipe[2]; // From parent to child
pid_t process_id;
int exit_status;
// Try to fork 5 child processes
for(int i = 0; i < 5; i++){
if(pipe(write_pipe) == -1 || pipe(read_pipe) == -1){
perror("Pipe");
exit(1);
}
// Spin a child process
process_id = fork();
if(process_id == -1){
perror("Fork");
exit(1);
} else if(processId == 0) {
// The child process
// I don't know what to do here, The idea is to close the
// unneeded end of the pipes and wait for input from the parent
// process
// Start the ./child
execl("./child", "");
} else {
// The parent process
char recieved_data[1024];
// Send data to child since stderr is duplicated in the pipe
// It sends the pid of the child
fprintf(stderr, "Test data to %d ", process_id);
// Wait to recieve data from child
// Don't know how to do that
// Print the recieved data
printf("Parent recieved: \"%s\"\n", recieved_data);
wait(&exit_status); // Will wait till all children exit before exiting
}
}
return 0;
}
The child.c is a simple program as shown below
child.c
#include <stdio.h>
int main(int argc, const char *argv[]){
char data_buffer[1024];
// Wait for input from parent
scanf("%s", data_buffer);
// Send data back to parent
printf("Child process: %s", data_buffer);
return 0;
}
Expected output
$ ./parent
parent recived: "Child process: Test data to 12345"
parent recived: "Child process: Test data to 12346"
parent recived: "Child process: Test data to 12347"
parent recived: "Child process: Test data to 12348"
parent recived: "Child process: Test data to 12349"
Where 12345, 12346....12349 is the process id of the child process
Here you have a code i did, and i will use to explain to you:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
int main() {
char buff[1024];
int aux, i, count;
int fds[2], fdss[2];
pipe(fds); //Here we initialize the file descriptors
pipe(fdss);
mode_t fd_mode = S_IRWXU;
for (i = 0; i < 3; i++) {
aux = fork();
if (aux == 0)
break;
}
switch (i) {
case 0:
printf("Write something:\n");
scanf("%s[^\n]", buff);
i = 0;
count = 0;
while(buff[i] != '\0') {
count++;
i++;
}
dup2(fds[1], 1);
close(fds[1]);
close(fds[0]);
close(fdss[0]);
close(fdss[1]);
write (1, buff, sizeof(buff));
break;
case 1:
dup2(fds[0], 0);
dup2(fdss[1], 1);
close(fds[0]);
close(fds[1]);
close(fdss[0]);
close(fdss[1]);
//
if (execl("/bin/grep", "grep", "example", NULL) == -1) {
printf("Error\n");
exit (1);
}
break;
case 2:
aux = open("result.txt", O_RDWR | O_CREAT , S_IRWXU);
dup2(fdss[0], 0);
dup2(aux, 1);
close(fds[0]);
close(fds[1]);
close(fdss[0]);
close(fdss[1]);
close(aux);
if (execl("/usr/bin/wc", "wc", "-l", NULL) == -1) {
printf("Error \n");
exit (1);
}
}
close(fds[0]);
close(fds[1]);
close(fdss[0]);
close(fdss[1]);
for (i = 0; i < 3; i++) wait(NULL);
return 0;
}
Ok, let's start:
We create and initialize pipes with pipe()
Then we write our code and before execl() we change the file descriptors, in order to pass the text we will write in the console, through processes and finally write in a file called result.txt the result of the "grep example" command applied to the text we have written.
The function dup2(new_descriptor, old_descriptor) is copying the new descriptor into the old descriptor and closes the old descriptor. For example:
Before dup2(fds[1], 1) we have:
0 STDIN
1 STDOUT
2 STDERR
After dup2(fds[1], 1) we have:
0 STDIN
1 fds[1]
2 STDERR
NOTE: If you don't want to use 1, yo can simply write STDOUT_FILENO
So now we are able to write through processes and in my example to a file too
In an attempt to better understand how pipes work in C, I decided to create a simple program. It is supposed to do the following: Firstly, I fork the program. The parent then reads from the standard input and writes everything into a pipe until EOF is reached. The child then reads from that pipe and writes the content back into another pipe, which is then supposed to be read by the parent process and written into the standard output.
Yes, the program isn't very "useful", but I'm just trying to familiarize myself with pipes and how to use them. This is my code:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
int main(int argc, char **argv) {
char buf;
int pipe_one[2];
int pipe_two[2];
pid_t child;
if(pipe(pipe_one) == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}
if(pipe(pipe_two) == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}
child = fork();
switch (child) {
case -1:
fprintf(stderr, "Error while forking.\n");
break;
case 0:
// child
// close unnecessary ends
close(pipe_one[1]);
close(pipe_two[0]);
// read input from parent and write it into pipe
while(read(pipe_one[0], &buf, 1) > 0) {
write(pipe_two[1], &buf, 1);
}
write(pipe_two[1], "\n", 1);
close(pipe_one[0]);
close(pipe_two[1]);
break;
default:
// parent
// close unnecessary ends
close(pipe_one[0]);
close(pipe_two[1]);
// read from standard input and write it into pipe
while(read(STDIN_FILENO, &buf, 1) > 0) {
write(pipe_one[1], &buf, 1);
}
write(pipe_one[1], "\n", 1);
close(pipe_one[1]);
// wait for child process to finish
wait(NULL);
// read from pipe that child wrote into
while(read(pipe_two[0], &buf, 1) > 0) {
write(STDOUT_FILENO, &buf, 1);
}
write(STDOUT_FILENO, "\n", 1);
close(pipe_two[0]);
break;
}
exit(EXIT_SUCCESS);
}
Expected behavior: In the beginning, the program reads user input until EOF is reached and then it outputs everything again into the standard output.
Actual behavior: The program reads the whole input, but once EOF is reached it just terminates (succesfully) without writing anything into the standard output. What am I doing wrong? I'd be happy if someone could look over it and help me out.
You close pipes for your parent in your child.
while(read(pipe_one[0], &buf, 1) > 0) {
write(pipe_two[1], &buf, 1);
}
write(pipe_two[1], "\n", 1);
close(pipe_one[0]); // Here you close pipes
close(pipe_two[1]); // for your parent
So the parent can't receive anything. Just remove those two lines and it will work.
I'm trying to create pipes in the shell to redirect standard streams and I am stuck now.
When I try to run this code:
int fd[2];
pid_t pid;
pipe(fd);
pid = fork();
if (pid == 0)
{
// child process
// redirect standard input and output
dup2(fd[1], STDOUT_FILENO);
dup2(fd[0], STDIN_FILENO);
// close them (they are now redirected)
close(fd[0]);
close(fd[1]);
char *input_argv[] = {"/bin/ls", "/bin/ls", ">", "out.txt", NULL};
execv(input_argv[0], input_argv);
}
else if (pid > 0)
{
// parent process
waitpid(pid, NULL,0);
}
I got this error messages:
/bin/ls: cannot access >: No such file or directory
/bin/ls: cannot access out.txt: No such file or directory
I have no idea what they mean, what cause them and how to fix them.
What am I doing wrong?
All in all, the code doesn't make any sense. I think the best answer one can give here is to explain the most problematic parts:
// redirect standard input and output
dup2(fd[1], STDOUT_FILENO);
dup2(fd[0], STDIN_FILENO);
fd[0] is the reading end, fd[1] the writing end of a pipe. Whatever you write to fd[1] is available for read on fd[0]. So, this is just "short-circuiting" your stdio streams, something not usefull at all.
What you want to do with pipes normally is have one pipe per direction of communication between parent and child process (e.g. child should read from parent: dup2() the reading end to STDIN_FILENO in the child and write to the writing end from the parent).
char *input_argv[] = {"/bin/ls", "/bin/ls", ">", "out.txt", NULL};
Now this doesn't make sense either. A > tells a shell to open a file for writing and exec() the child with a redirected STDOUT_FILENO already in place. It's certainly not an argument understood by ls here. You don't have a shell, you just exec() ls directly.
If your original intention was to mimic what the shell would do when given
ls > out.txt
you should just open the file out.txt for writing and in the child code dup2() the file descriptor of your opened file to STDOUT_FILENO before exec()ing ls. There's no need for a pipe in this scenario.
edit in case you want to understand what a shell does internally for ls > out.txt:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <unistd.h>
int main(void)
{
/* open file for writing */
int outfd = open("/tmp/out.txt", O_CREAT|O_WRONLY, 00666);
if (outfd < 0)
{
perror("open()");
return EXIT_FAILURE;
}
/* fork child */
int pid = fork();
if (pid < 0)
{
perror("fork()");
return EXIT_FAILURE;
}
if (pid == 0)
{
/* in the child, redirect stdout to our file */
if (dup2(outfd, STDOUT_FILENO) < 0)
{
perror("dup2()");
return EXIT_FAILURE;
}
close(outfd);
/* then execute 'ls' */
execlp("ls", "ls", 0);
/* only reached when execlp() fails: */
perror("execlp()");
return EXIT_FAILURE;
}
/* we don't need the output file in the parent process: */
close(outfd);
/* wait for child to complete */
int childrc;
waitpid(pid, &childrc, 0);
/* return exit code of child process */
return childrc;
}
Of course, the code of the actual shell looks different (doesn't have names hardcoded, uses execv* family of functions because it doesn't know the number of arguments in advance, and so on.)
I am trying to send two messages "hello World" and "Goodbye" from parent to a child using a pipe. The child must print the messages when recieves them.
My problem is how to send the second message. I compile and run the program but it only prints the first message. Any sugestions?
Here's my code:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 50
void main(){
int fd[2], n, status;
char buf[MAX];
pid_t pid;
char str1[]="Hello World!\n";
char str2[]="Goodbye\n";
pipe(fd);
if((pid=fork())<0){
abort();
}
else if(pid>0){// parent code goes here
close (fd[0]); // close read channel of parent
/*Send "hello world" through the pipe*/
write(fd[1],str1,(strlen(str1))); // write to the pipe
wait(&status);
close(fd[0]);
write(fd[1],str2,(strlen(str2)));
}
else{ // child code goes here
close(fd[1]); // close write channel of child
n=read(fd[0], buf, sizeof(buf)); // reads from the pipe
write(STDOUT_FILENO, buf, n);
exit(0);
}
}
In the parent, just write the two messages and then close the write end of the pipe:
close(fd[0]); // close read channel of pipe in parent
write (fd[1], str1, strlen(str1)); // write "hello world"
write (fd[1], str2, strlen(str2)); // write "goodbye"
close(fd[1]); // Tell child that we're done writing
wait(&status); // Wait for child to read everything and exit
In the child, you should read in a loop until you get EOF, indicated by read() returning 0:
close(fd[1]); // close write channel of pipe in child
while ((n = read(fd[0], buf, sizeof(buf)) > 0) { // Read until it returns 0 (EOF) or -1 (error)
write(STDOUT_FILENO, buf, n);
}
if (n < 0) { // -1 = error
perror("read from pipe");
}
I'm trying to run a program with a specific standard input. I succeed by using a file descriptor of a file where there is what I want to put in the stdin, but I fail to write directly on the stdin :
$cat input.test
echo Hello
$
Code C :
int main(int argc, char **argv)
{
int fd = 0;
fd = open("input.test", O_CREAT);
close(STDIN_FILENO);
dup2(fd, STDIN_FILENO);
char *const args[] = { "bash", NULL };
execvp("bash", args);
}
That works :
$./a.out
Hello
$
But if I try to write directly on the STDIN using pipe the program displays nothing and keeps running :
int main(int argc, char **argv)
{
int fds[2];
pipe(fds);
close(STDIN_FILENO);
dup2(fds[1], STDIN_FILENO);
write(fds[1], "echo Hello;", 11); // Résults are identics with fds[0]
char *const args[] = { "bash", NULL };
execvp("bash", args);
}
Thanks for your help
Cordially,
Bastien.
EDIT Problem solved:
Thanks for your answers, here the code which works :
int main(void)
{
int fd[2];
pid_t pid;
if (pipe(fd) < 0)
return EXIT_FAILURE;
if ((pid = fork()) < 0)
return EXIT_FAILURE;
else if (pid != 0) { /* father */
close(fd[1]);
dup2(fd[0], STDIN_FILENO);
execlp("bash", "bash", (char *)0);
} else { /* son */
close(fd[0]);
write(fd[1], "echo hello\n", 11);
}
return EXIT_SUCCESS;
}
You need to dup the read side of the pipe to stdin, not the write side. (And write to the write side, obviously.)
#include <unistd.h>
#include <string.h>
#include <stdio.h>
int main(int argc, char **argv)
{
int fds[2];
char cmd[] = "echo hello\nexit\n";
pipe(fds);
close(STDIN_FILENO);
dup2(fds[0], STDIN_FILENO);
write(fds[1], cmd, strlen(cmd));
char *const args[] = { "bash", NULL };
execvp("bash", args);
return 0;
}
Make sure you check the return values of all those functions though, you'll never manage to debug your code if you don't.
execv and friends replace the current running program with the specified one; they do not return - execution continues at the start of new program instead.
So what you normally do is fork and, in one of the forks, call execv. You then read and write through the pipe from your program continuing in the other fork. There are usually popen functions to do this in most languages; sadly in POSIX the popen() is strictly read or write and not bidirectional.
Luckily, I've made, tested and published a popen3 function. This gives you back three file descriptors - one for stdin to the process, and two for stdout and stderr. You can then use write() on the stdin.
When you call pipe, fd[ 0 ] is open for reading, and fd[ 1 ] is open for writing. You should be dup'ing stdin on the read side ( fd[ 0 ]) and writing to the write side( fd[ 1 ]). Check the return value of write: it is probably -1.
But there is a larger issue. You never close either side of the pipe. bash may block on a read and never do anything until the write side of the pipe is closed. You should close both sides of the pipe after you dup and write. (Or set FD_CLOEXEC).
Also note that doing it the way you do, you're dependent on pipe buffer size. If you write too much, write will be blocked as there's no reader. Do do it reliably, you should fork(), do exec in the child and write to the pipe in the parent. This way the pipe will have a reader and you will be able to write as much data as you want into it.