I am trying to figure out piping in C by playing around with it. I want to write a program that takes the output from the shell command 'cat', saves it as a string, then prints that string. The command should look like this:
cat foo.txt | ./my_prog
I am having issues sending the output from the cat command to my_prog. Here is what I have tried so far.
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc, char *argv[])
{
int pipe_1[2];
pid_t pid = -1;
char catString[200];
catString [199] = '\0';
// dup stdout to pipe_1
if( dup2(STDOUT_FILENO, pipe_1[1]) == -1 ){
perror("Could not create pipe 1");
exit(-1);
}
// fork a new process
pid = fork();
switch(pid){
case -1:
perror("Fork 1 failed");
exit(-1);
case 0: // child
// close stdin and write stdout to the string
close(pipe_1[0]);
write(pipe_1[1], catString, 200);
break;
default: // parent
// wait for child process to finish, close stdout, then print the string
wait(NULL);
close(pipe_1[1]);
printf("Parent recieved %s\n", catString);
break;
}
return 0;
}
This doesn't print anything and gives me the output:
Parent recieved
On a side note, am I using the wait() function correctly? I wanted to make sure the child is done writing to catString before the parent process executed.
The shell will send the output of cat foo.txt to stdin of your program. You don't have to do anything with "pipes" inside your program, just accept the input in the way the shell delivers it.
Related
I'm using the following code to create a child process. The idea is to filter (more generally speaking execute an arbitrary function on) stdin and stdout of the child.
For one shots this example should work, but for an interactive shell there are things missing to work. The question is basically: Is this the right way to achieve what I want? What am I missing? (e.g. I tried to catch all signals (execept STOP and KILL) and relay them to the child, but it didnt really help much)
I'm eager to understand the issue so I'd be glad for any help and insight on that matter
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
/* since pipes are unidirectional, we need two pipes.
one for data to flow from parent's stdout to child's
stdin and the other for child's stdout to flow to
parent's stdin */
#define NUM_PIPES 2
#define PARENT_WRITE_PIPE 0
#define PARENT_READ_PIPE 1
int pipes[NUM_PIPES][2];
/* always in a pipe[], pipe[0] is for read and
pipe[1] is for write */
#define READ_FD 0
#define WRITE_FD 1
#define PARENT_FROM_CHILD_FD ( pipes[PARENT_READ_PIPE][READ_FD] )
#define PARENT_TO_CHILD_FD ( pipes[PARENT_WRITE_PIPE][WRITE_FD] )
#define CHILD_FROM_PARENT_FD ( pipes[PARENT_WRITE_PIPE][READ_FD] )
#define CHILD_TO_PARENT_FD ( pipes[PARENT_READ_PIPE][WRITE_FD] )
pid_t pid;
void filterStream( char* buffer, int count){
printf("filter\n");
}
int main () {
// pipes for parent to write and read
pipe(pipes[PARENT_READ_PIPE]);
pipe(pipes[PARENT_WRITE_PIPE]);
if ((pid = fork()) < 0) {
exit(1);
}
if (pid==0){
/*child*/
char *argv[]={ "sh","-i",0};
dup2(CHILD_FROM_PARENT_FD, STDIN_FILENO);
dup2(CHILD_TO_PARENT_FD, STDOUT_FILENO);
//edit
close(CHILD_FROM_PARENT_FD);
close(CHILD_TO_PARENT_FD);
close(PARENT_FROM_CHILD_FD);
close(PARENT_TO_CHILD_FD);
execvp(argv[0], argv);
} else {
/*parent*/
char buffer[8192];
int count;
int buflen=sizeof(buffer)-1;
/* close fds not required by parent */
close(CHILD_FROM_PARENT_FD);
close(CHILD_TO_PARENT_FD);
while(1){
// read stdin
while((count = read(STDIN_FILENO, buffer, buflen))>0){
filterStream(buffer,count);
buffer[count] = 0;
write(PARENT_TO_CHILD_FD,buffer,count);
if (count<buflen){
break;
}
}
if (count<0){
printf("IOERR: %d\n",errno);
}
// Read from child’s stdout
while((count = read(PARENT_FROM_CHILD_FD, buffer, buflen))>0){
filterStream(buffer,count);
buffer[count] = 0;
write(STDOUT_FILENO,buffer,count);
if (count<buflen){
break;
}
}
if (count<0){
printf("IOERR2: %d\n",errno);
}
}
}
}
Problem 1:
After the first command no further commands are executed: Is this just a redirection problem from parent to child or is it deeper?
./a.out
sh-5.1$ touch test
filter
touch test
sh-5.1$ exit
# hangs
^C
sh-5.1$ exit
# hangs...
Problem 2: on some commands the shell just backgrounds: Who backgrounds it exactly?
./a.out
sh-5.1$ whoami
filter
whoami
filter
currentuser
[1] + 238242 suspended (tty input) ./a.out
I am unsure where the error is in my code. I intended on piping the output of one child process to a parent process and then piping the output of that to the find child. The program compiles but I receive the error : awk: read error (Is a directory), the intent of the program is to read the file size of the desired directory using the ls -al function along with the awk function. These values are then piped to the ./adder program which sums the values together. The output should be the addition of the file sizes.
The code:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#define INPUT 1
#define OUTPUT 0
#define STDIN 0
#define STDOUT 1
int main(void){
int awk2adderPFD[2];
pid_t pid , pid2;
pid = fork();
pipe(awk2adderPFD);
if( pid == 0) { //must be child
close(awk2adderPFD[OUTPUT]); //close read end of pipe
dup2(awk2adderPFD[INPUT], STDOUT); //fd1 points to write end
close(awk2adderPFD[INPUT]); //close pfd[1] as no longer used
execlp("./adder","./adder", (char *)0) ;
}
else {
int ls2awkPFD[2];
pipe(ls2awkPFD);
pid2 = fork();
if( pid2 == 0) { //must be child
close(ls2awkPFD[OUTPUT]); //close read end of pipe
dup2(ls2awkPFD[INPUT], STDOUT); //fd1 points to write end
close(ls2awkPFD[INPUT]); //close pfd[1] as no longer used
execlp("ls", "ls","-al", (char *) 0);
}
else { //parent process
close(ls2awkPFD[INPUT]); //close write end of pipe
dup2(ls2awkPFD[OUTPUT], STDIN); //pfd0 points to read end
close(ls2awkPFD[OUTPUT]); //close pfd[0] of parent
execlp("awk", "awk", "-F:", "{ print $5 }", "/home/adeyemo/assignment2.1", (char *) 0);
}
}
}
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
This is a homework problem. The task is to replicate the command: ls | wc -l in a C program using execlp, fork, and pipes.
My Approach
I think the problem can be solved this way:
Create a pipe file: pipe.txt
Create a child process using fork()
Map the stdout of the child process to pipe.txt
Execute ls using execlp
This puts the output of ls into pipe.txt
Inside of parent process
Map the stdin of the parent process to pipe.txt
Execute wc -l using execlp without giving any further arguments so it reads from stdin instead
Since the stdout of this parent process is still the terminal itself, so it should print out the number of lines on the terminal
My Code
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/wait.h>
int main() {
int pipefds[2];
int returnstatus;
int pid;
char argArr[30] = {'\n'};
returnstatus = pipe(pipefds);
if (returnstatus == -1) {
printf("Unable to create pipe\n");
return 1;
}
int file_desc = open("pipe.txt", O_RDWR | O_APPEND | O_CREAT);
pid = fork();
if (pid == 0) {
int copy_desc = dup2(file_desc, 1);
execlp("ls", "ls", NULL);
} else {
int copy_desc = dup2(file_desc, 0);
close(copy_desc);
execlp("wc", "wc", "-l", NULL);
}
return 0;
}
Actual Output
main.cpp blabla.cpp main pipe.txt
>
Problems
Two things that are wrong with this:
Since I set the stdout of the child to be the pipe.txt file, why does it still output on the terminal? NOTE: It does put the output in the pipe.txt file too. But why does it display on the terminal too?
It starts waiting for the user to provide the input? Shouldn't it get the input from pipe file instead of the user?
Expected Output
5
*if there are 5 files in the current directory
Tried Solutions
Using just the pipe: (Got a bad file descriptor error)
int main() {
int pipefds[2];
int returnstatus;
int pid;
returnstatus = pipe(pipefds);
if (returnstatus == -1) {
printf("Unable to create pipe\n");
return 1;
}
pid = fork();
if (pid == 0) {
dup2(pipefds[0], 1);
close(pipefds[1]);
execlp("ls", "ls", NULL);
} else {
dup2(pipefds[1], 0);
close(pipefds[0]);
execlp("wc", "wc", "-l", NULL);
}
return 0;
}
Thanks for the helpful comments.
The problem in the code is that I am not using pipes at all. I was doing all my work with a file that I created. So that was the basic problem.
Here's the new code:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/wait.h>
int main() {
// Step1. Create pipe file descriptors: pipefd[0] for reading from pipe, pipefd[1] for writing to the pipe
int pipefds[2];
// Helping variables
int returnstatus;
int pid;
// Step2. Create a pipe with the file descriptors
returnstatus = pipe(pipefds);
// Check if pipe was successfully created
if (returnstatus == -1) {
printf("Unable to create pipe\n");
return 1;
}
// Step3. Fork to create a child process
pid = fork();
if (pid == 0) {
// Inside the child process
// Step4. Duplicate the file descriptor of the write end of the pipe and set it equal to the stdout of the process
dup2(pipefds[1], 1);
// Step5. Close both ends of the pipe
close(pipefds[0]);
close(pipefds[1]);
// Step6. Execute the LS command. It ouputs to stdout which we set equal to the pipe in Step4.
// So essentially, we send all output of ls to our pipe
returnstatus = execlp("ls", "ls", NULL);
// Error checking the execlp command
if (returnstatus == -1){
perror("Error executing execlp: ");
}
} else {
// Inside the parent process
// Step7. Duplicate the file descriptor the READ end of the pipe and set it equal to the stdin of the process
dup2(pipefds[0], 0);
// Step8. Close the both ends of the pipe
close(pipefds[0]);
close(pipefds[1]);
// Step9. Execute the WC command. It takes the file as an argument usually but if no file is given, it will take
// stdin as input. Since the stdin is the pipe, therefore it will read all the data from the pipe.
// The output of the wc command is stdout which is the terminal for this process so we will get the number of
// files/directories in the current directory as an output on the terminal
returnstatus = execlp("wc", "wc", "-l", NULL);
// Error checking the execlp command
if (returnstatus == -1){
perror("Error executing execlp: ");
}
}
return 0;
}
So I am trying to use pipes to cat a file and to sed into a file called newfile.txt Currently the cat command works, using execvp, however it's outputing onto the command display. And then the program goes into an infinite loop when it goes to the sed command.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
char *myargv2[]={"sed", "-e" "s/color/colour/g", NULL};
char *myargv1[]={"cat", "colorfile.txt", NULL};
main()
{
int f_des[2];
int fd[2];
int pipe(int filedes[2]);
int file = open("newfile.txt",O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
if (file < 0)
return 1;
// create a pipe
// Open a pipe and report error if it fails
if (pipe(f_des)==-1)
{
perror("Pipe");
exit(2);
}
//fork the process
// Use switch for fork, because parent doesn't need child's pid.
switch (fork())
{
case -1: // Error
perror("Fork");
exit(2);
case 0: // Child
printf("HERE1\n");
//child will call dup2 to hook standard output to one end of the pipe. Then, execute the cat command using execvp
dup2(fd[1], fileno(stdout));
execvp(myargv1[0], myargv1);
close(fd[1]);
close(fd[0]);
perror(myargv1[0]);
close(fd[1]);
close(fd[0]);
printf("HERE12\n");
exit(3);
default: // Parent
{
printf("HERE13\n");
//parent will call dup2 to hook standard input to the other end of the pipe. Then, execute the sed command using execvp
dup2(fd[0], fileno(stdin));
execvp(myargv2[0], myargv2);
perror(myargv2[0]);
close(fd[1]);
close(fd[0]);
printf("HERE14\n");
//parent will also call dup2 to hook standard output to the file called newfile.txt
if(dup2(file,0 < 0))
return 1;
}
exit(4);
}
return 0;
}
Obviously I'm struggling here. Can anyone point out what I'm doing wrong and/or point me to a good source of information on how to do this?
Thanks!
One primary problem is that you can't make up your mind whether to use f_des or fd for the pipe file descriptors. You have:
int f_des[2];
int fd[2];
int pipe(int filedes[2]);
…
if (pipe(f_des) == -1)
{
perror("Pipe");
exit(2);
}
The declaration of pipe() is not a good idea; that's what the system headers do. But the serious problem is that you create the pipe in f_des and thereafter work with fd.
The other problem is that you don't close the pipe file descriptors accurately. You also have a fair amount of superfluous code. This code works correctly:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
static char *myargv2[]={"sed", "-e" "s/color/colour/g", NULL};
static char *myargv1[]={"cat", "colorfile.txt", NULL};
int main(void)
{
int fd[2];
int pipe(int filedes[2]);
int file = open("newfile.txt",O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
if (file < 0)
return 1;
if (pipe(fd)==-1)
{
perror("Pipe");
exit(2);
}
switch (fork())
{
case -1: // Error
perror("Fork");
exit(2);
case 0: // Child
printf("HERE1\n");
dup2(fd[1], fileno(stdout));
close(fd[0]); // Important (in general)
close(fd[1]); // Important (in general)
execvp(myargv1[0], myargv1);
perror(myargv1[0]);
printf("HERE12\n");
exit(3);
default: // Parent
printf("HERE13\n");
dup2(fd[0], fileno(stdin));
close(fd[0]); // Crucial
close(fd[1]); // Important (in general)
execvp(myargv2[0], myargv2);
perror(myargv2[0]);
exit(4);
}
return 0;
}
A simple rule of thumb is:
If you dup() or dup2() one end of a pipe to standard input or standard output, you should close both of the raw pipe file descriptors.
Given input file colorfile.txt containing:
this is the color of danger
coloration is not important
end of file is.
The program's output is:
HERE13
HERE1
this is the colour of danger
colouration is not important
end of file is.
Interestingly, if the output of the program is piped to another program, the debugging information isn't printed. That's a consequence of default buffering.