redirection using dup2 command - c

I know this is a simple exercise but I'm having troubles with it.
ps | sed 1d | wc -l
I am trying to generate an output of the above terminal command using c code for my school exercise. I have written a code that is redirecting all the inputs and outputs to the required destination but I am not getting any output on the console as I haven't redirected the final output in any file so it must be displayed on console.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/file.h>
#define READ 0
#define WRITE 1
int main()
{
//fd fd1
//ps | sed 1d | wc -l
pid_t pid;
pid_t pid1;
pid_t pid2;
int fd[2];
int fd1[2];
pipe(fd);
pipe(fd1);
pid=fork();
if(pid==0)
{ //redirecting into sed 1d
close(fd[READ]);
dup2(fd[WRITE],1);
close(fd[WRITE]);
execlp("ps","ps",NULL);
}
else
{
pid1=fork();
if(pid1==0)
{ //redirecting into wc -l
close(fd[WRITE]);
dup2(fd[READ],0);
close(fd[READ]);
close(fd1[READ]);
dup2(fd1[WRITE],1);
close(fd1[WRITE]);
execlp("sed","sed","1d",NULL);
}
else
{
pid2=fork();
if(pid2==0)
{ //must display on console
close(fd1[WRITE]);
dup2(fd1[READ],0);
close(fd1[READ]);
execlp("wc","wc","-l",NULL);
}
else
{
close(fd[READ]);
close(fd[WRITE]);
close(fd1[READ]);
close(fd1[WRITE]);
wait(NULL);
}
}
}
}
What can be the problem?

The ps process has both ends of the fd1 pipe open, which it shouldn't. Also, the wc -l process has both ends of the fd pipe open, which it shouldn't. Fix both of those things and your program will stop hanging. Also, wait(NULL) only waits for one child process to terminate, not all of them, so you're risking the terminal prompt reappearing before your program is actually done.

Related

How to make wc accept a pipe file to take input from instead of stdin?

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;
}

What is the output that the program trying to achieve

I know about the fork(), dup2 calls but I cannot infer the output of the program.
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <unistd.h>
int main (int argc, char *argv[]){
pid_t pid1, pid2;
int fds[2];
char *argv1[] = { "ls", "-l","usr/bin", NULL};
char *argv2[] = {"more", NULL};
pipe(fds);
pid1=fork();
if(!pid1){
close(fds[0]);
dup2(fds[1], STDOUT_FILENO);
close(fds[1]);
execvp(argv1[0],argv1);
}
pid2=fork();
if(!pid2){
close(fds[1]);
dup2(fds[0], STDOUT_FILENO);
close(fds[0]);
execvp(argv1[0],argv2);
}
close(fds[0]);
close(fds[1]);
waitpid(pid2,NULL,0);
return EXIT_SUCCESS;
}
Also what would happen if i experiment and mistakenly press pid1 instead of pid2 at the waitpid call in the end.
Nominally, the program is running the shell pipeline
ls -l usr/bin | more
In practice, the connections to more are mishandled (using copy-and-paste is dangerous if you don't make all the necessary changes). It connects the read end of the pipe to the standard output of more, which is simply broken. It also runs ls a second time (passing argv1[0] to execvp() instead of argv2[0]) but tells ls that its name is more. It also doesn't believe execvp() can fail — but it can.
With those minimal fixes in place (the program doesn't include <stdio.h> so there is no error reporting), you get something like:
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
int main(void)
{
pid_t pid1, pid2;
int fds[2];
char *argv1[] = { "ls", "-l", "usr/bin", NULL };
char *argv2[] = { "more", NULL };
pipe(fds);
pid1 = fork();
if (pid1 == 0)
{
close(fds[0]);
dup2(fds[1], STDOUT_FILENO);
close(fds[1]);
execvp(argv1[0], argv1);
exit(EXIT_FAILURE);
}
pid2 = fork();
if (pid2 == 0)
{
close(fds[1]);
dup2(fds[0], STDIN_FILENO);
close(fds[0]);
execvp(argv2[0], argv2);
exit(EXIT_FAILURE);
}
close(fds[0]);
close(fds[1]);
waitpid(pid2, NULL, 0);
return EXIT_SUCCESS;
}
I called the program pipe31 (created from pipe31.c) and got the sample output:
$ pipe31
ls: usr/bin: No such file or directory
$ mkdir -p usr/bin
$ random -n 15 1000 9999 > usr/bin/polyglot
$ pipe31
total 8
-rw-r--r-- 1 jonathanleffler staff 75 Dec 8 22:07 polyglot
$
When invoked as shown, the local random program creates 15 random numbers between 1000 and 9999, each on its own line — corresponding to 75 bytes in the file. The outputs were piped via more, but it is hard to spot that in a web browser.
As to your proposed experimentation — experiment away — you won't cause harm. What you see will depend on the size of the directory you're working in, and the size of your terminal window. However, if the output from ls -l is big enough (but not too big), then you'd get a shell prompt in the middle of your ls listing, and you'd have more showing data and waiting to read a newline. There could potentially be a competition between the shell and more for the following inputs which could get interesting. That assumes you fix the problems identified in the rest of the answer before experimenting.

linux terminal command with a pipe in c code

I'm trying to execute the Linux command "ls -l | tail -n 2" with a simple pipe in a c code.
I added your tips and now this works but the output isn't exactly as it should be. It prints the output in a single line instead of two and waits for a user input to close.
here is the new code:
#include "stdio.h"
#include "unistd.h"
#include "stdlib.h"
#include "sys/wait.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
void main()
{
char line[100];
pid_t pid;
int fd[2];
int status;
char* ls_arguments[] = {"ls", "-l", NULL};
char* tail_arguments[] = {"tail", "-n", "2", NULL};
pipe(fd);
pid = fork();
if(pid == 0)//ls client
{
close(1);
dup(fd[1]);
close(fd[0]);
execvp("ls", ls_arguments);
}
pid = fork();
if(pid == 0)//tail client
{
close(0);
close(fd[1]);
dup(fd[0]);
execvp("tail", tail_arguments);
}
wait(pid, 0, WNOHANG);
close(fd[0]);
close(fd[1]);
}
this should run the "ls -l" command and output to the pipe and the next "tail" client would get it as input and run the "tail -n 2" command and print out the final output but the terminal prints nothing. Any help?
First of all, there is not such wait function, here is what the man says:
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);
pid_t waitpid(pid_t pid, int *status, int options);
I think you meant to use waitpid.
Then, you child process doesn't finish because the pipe is still opened somewhere: in the parent. Indeed you should first close the descriptors and then wait for your childs process. I would write:
close(fd[0]);
close(fd[1]);
wait(NULL); // Wait for the first child to finish
wait(NULL); // Wait fot the second one
return 0;
}
Instead of:
wait(pid, 0, WNOHANG);
close(fd[0]);
close(fd[1]);
}

execve grep process never exit

The following code simulate the pipe and grep operation by forking process and using execve system call. The output seems fine, however, the grep process seems never exit (still running in the back) until the whole process ends. What's the problem? It is abnormal since using grep in shell always exit.
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <fcntl.h>
int main(int argc, char *argv[], char *env[]) {
char ch[32];
while (1) {
scanf("%s", ch);
int pd[2];
if (pipe(pd) == -1) {
perror("Pipe failed");
exit(1);
}
int childPid, pid;
if ((childPid = fork()) < 0) {
perror("fork failed\n");
exit(1);
} else {
// parent process
if (childPid) {
int status;
wait(&status);
// print exit code of child process
printf("Exit code %d\n", status);
}
// child process, execute command
else {
// fork a child
if ((pid = fork()) < 0) {
perror("fork failed\n");
exit(1);
}
if (pid) { // parent as pipe WRITER
close(pd[0]);
close(1);
// replace input with pipe
dup(pd[1]);
char* cmds[] = { "/bin/cat", "aa", 0 };
execve(cmds[0], cmds, env);
exit(0);
} else { // child as pipe READER
close(pd[1]);
close(0); // close its READ end
dup(pd[0]);
char* cmds[] = { "/bin/grep", "rw", 0 };
execve(cmds[0], cmds, env);
exit(0); // never been here
}
}
}
}
return 0;
}
Here is the output I monitor the process before and after running this program once.
hengstar#ubuntu:~$ ps -ef | grep "grep"
hengstar 58073 58038 0 01:43 pts/26 00:00:00 grep --color=auto grep
hengstar#ubuntu:~$ ps -ef | grep "grep"
hengstar 58075 1886 0 01:43 pts/11 00:00:00 /bin/grep drw
hengstar 58077 58038 0 01:43 pts/26 00:00:00 grep --color=auto grep
The exec family of functions never returns. Their purpose is to load a new program to replace the current program running in the process.
If an exec function returns, it means there was an error.
I laughted so hard I myself when I found this! Did you noticed that while(1) above? Well, everything is okay, except for that! Without it, your program works as you describe it to.
BTW: It happens that if a process's parent dies, the child becomes adopted by init, a.k.a (pid_t)1. That was happening with your code with while(1).

Using dup2 for piping

How do I use dup2 to perform the following command?
ls -al | grep alpha | more
A Little example with the first two commands. You need to create a pipe with the pipe() function that will go between ls and grep and other pipe between grep and more. What dup2 does is copy a file descriptor into another. Pipe works by connecting the input in fd[0] to the output of fd[1]. You should read the man pages of pipe and dup2. I may try and simplify the example later if you have some other doubts.
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#define READ_END 0
#define WRITE_END 1
int
main(int argc, char* argv[])
{
pid_t pid;
int fd[2];
pipe(fd);
pid = fork();
if(pid==0)
{
printf("i'm the child used for ls \n");
dup2(fd[WRITE_END], STDOUT_FILENO);
close(fd[WRITE_END]);
execlp("ls", "ls", "-al", NULL);
}
else
{
pid=fork();
if(pid==0)
{
printf("i'm in the second child, which will be used to run grep\n");
dup2(fd[READ_END], STDIN_FILENO);
close(fd[READ_END]);
execlp("grep", "grep", "alpha",NULL);
}
}
return 0;
}
You would use pipe(2,3p) as well. Create the pipe, fork, duplicate the appropriate end of the pipe onto FD 0 or FD 1 of the child, then exec.

Resources