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.)
Related
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;
}
I'm making a C program where it basically reads in a line from the user, interprets it and then tries to execute the command with execve. I'm also forking the execve to a child process if '&' is in the input.
Now I wish to hide any terminal output which comes from the execve command when it's running in the child process.
Is there any relatively easy way to do this?
You can hide the output by redirecting stdout and stderr to /dev/null after forking but before execve(). The idea is to open /dev/null, then make stdout and stderr duplicates of the obtained file descriptor with dup2() (which will also close the originals first). It's almost the same as redirecting to a pipe.
An example (incomplete program, and skipping most error checking):
#include <unistd.h>
#include <fcntl.h>
...
int pid = fork();
if (pid == -1) {
/* fork error */
exit(1);
} else if (pid == 0) {
/* child process */
/* open /dev/null for writing */
int fd = open("/dev/null", O_WRONLY);
dup2(fd, 1); /* make stdout a copy of fd (> /dev/null) */
dup2(fd, 2); /* ...and same with stderr */
close(fd); /* close fd */
/* stdout and stderr now write to /dev/null */
/* ready to call exec */
execve(cmd, args, env);
exit(1);
} else {
/* parent process */
...
I've written simple example, maybe it will help you.
First, try to call it without | echo $1 > /dev/null - it should print files. When you add it, output is empty.
#include <stdio.h>
#include <unistd.h>
int main()
{
int ret;
char *cmd[] = { "ls", "-l", (char *)0 };
char *env[] = {(char *)0 };
ret = execve ("/bin/ls | echo $1 > /dev/null", cmd, env);
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.
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
I'm creating a small program which contains three processes; a source process, a filter process and a sink process. The stdout of the source process is redirected to the stdin of the filter process, and the filter process' stdout is redirected to the sink process' stdin.
My problem is that no output is printed to stdout from the sink process. Can any of you see the problem in the following tiny snippet of code?
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char** argv)
{
// Pipes
// pipe1 is from source to filter, pipe2 is from filter to sink
int pipe1[2], pipe2[2];
// Create pipes
if (pipe(pipe1) < 0 || pipe(pipe2) < 0)
{
perror("Creating pipes failed!");
}
if (fork() == 0)
{
close(1);
dup(pipe1[1]);
close(pipe1[0]);
close(pipe2[0]);
close(pipe2[1]);
execlp("ls", "ls", NULL);
exit(0);
}
else
{
if (fork() == 0)
{
close(0);
dup(pipe1[0]);
close(pipe1[1]);
close(1);
dup(pipe2[1]);
close(pipe2[0]);
execlp("sort", "sort", NULL);
exit(0);
}
else
{
if (fork() == 0)
{
close(0);
dup(pipe2[0]);
execlp("more", "more", NULL);
exit(0);
}
}
}
wait(NULL);
printf("Done.\n");
return 0;
}
BR
Jacob
I think problem may be, wait will only wait for one process. And when the parent exits after first child returns, I suspect more command also decides to terminate, because it may get SIGHUP (speculation, not sure).
But, check for errors from on all system calls! Also for wait calls which succeeded, print why the child exited (was it signal or normal exit, and if it was normal exit, what was exit code).
Also note, perror does not exit, it only prints.
It is kind of pointless trying to see why some code fails, if it does not have error handling in it...
Some easy way to do pipes for your scenario:
char cmd[MAX_LEN];
sprintf(cmd, "%s | %s | %s", app1, app2, app3); //app123 holds app name + args
system(cmd);
if you want to capture the output of the last app, use popen:
FILE pPipe = popen(cmd, "rt"); /* same access flag as fopen()*/
while (NULL != fget(buf, buf_len, pPipe)) {
// do something with the read line in 'buf'
}