Closing pipe, dup2, file descriptors in C? - c

I'm running a program that does piping.
The command I want to run is ls | cat.
int cmd(char** w, int* pipe, int action){
... some code up here
...
int fd;
if(child_pid == 0) {
if (pipe != 0) {
if (action == 0){
fd = dup2(pipe[0], STDIN_FILENO);
close(pipe[0]);
close(pipe[1]);
//close(fd);
}
else if (action == 1){
fd = dup2(pipe[1], STDOUT_FILENO);
close(pipe[0]);
close(pipe[1]);
//close(fd);
}
}
execvp(w[0], w);
printf("Unknown command\n");
exit(0);
}
... some code down here
When I run the code, the command ls | cat runs fine except that the cat doesn't end(i.e. the pipe doesn't close and just waits there doing nothing). I think it's because I didn't close a stream or something, but I'm not familiar enough with C/IO to know for sure. Am I doing this right?
the code that runs this function is like
int fd[2];
int p = pipe(fd);
cmd(w, fd, 1);
cmd(w, fd, 0);
edit: ur right, fatalerror, i mistyped on the arguement
thxs, looks like i just needed to close pipe[1] in the parent

The parent process also needs to close both ends of the pipe after the two cmd calls.

Related

how make cat and grep work in the first and the second pipe in c writing like heredoc in bash <<

I am working to make a shell like bash, but i have trouble solving heredoc << so i made a test code as simple as possible for this question.
void pipeline()
{
int i = 0;
int fd[2];
pid_t pid;
int fdd = 0;
while (i < 2)
{
pipe(fd);
pid = fork();
if (pid == 0)
{
//dup2(fd[1],1); if i dup in the first pipe cat dont finalize
if (i == 0)
dup2(fd[0],0);
write(fd[1], "hello\nhow\nare\nyou\n", 17);
close(fd[0]);
close(fd[1]);
dup2(fdd, 0);
if (i == 0)
execlp("cat", "cat", NULL);
else
execlp("grep", "grep", "you" , NULL);
perror("error");
exit(1);
}
else
{
close(fd[1]);
fdd = fd[0];
wait(NULL);
i++;
}
}
}
int main(int *argc, char **argv, char **env)
{
pipeline();
}
I know that cat and grep need an EOF to run; what I'm doing is writing in stdin and running cat, but my question is: how do I save stdout for grep without duping stdout on the first pipe?
If I dup on dup2(fd[1],1) cat does not work in the first pipe, could someone help me out to make this code work? And make it as similar to bash heredoc as well if possible.
how do I save stdout for grep without duping stdout on the first pipe?
I'd rearrange the creation of the child processes from rightmost to leftmost - then grep is created first and can output to the initial output descriptor. A necessary change is to run all child processes before waiting on one as well as before writing, so that there's no deadlock even if the pipe buffer wouldn't suffice for the heredoc.
void pipeline()
{
int i = 2; // create children from last to first
int fd[2];
pid_t pid;
int fdd = 1; // output of last child is STDOUT
while (i--)
{
pipe(fd);
pid = fork();
if (pid == 0)
{
dup2(fdd, 1); // child's output
dup2(fd[0], 0);
close(fd[0]);
close(fd[1]);
if (i == 0)
execlp("cat", "cat", "-A", NULL);
else
execlp("grep", "grep", "you" , NULL);
perror("error");
exit(1);
}
if (fdd != 1) close(fdd); // close if a pipe write end
fdd = fd[1]; // preceding child's output is pipe write end
close(fd[0]);
}
write(fd[1], "hello\nhow\nare\nyou\n", 17);
close(fd[1]); // signal EOF to child
while (wait(NULL) > 0) ; // wait for all children
}

execl() does not seem to read from stdin

I'm trying to reproduce this command in c language:
ls | wc > output.txt
So, to do that, I wrote the following program:
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
int main()
{
pid_t lsFork, wcFork;
int tube[2];
pipe(tube);
lsFork = fork();
if(lsFork == 0) // ls command
{
close(tube[0]);
dup2(tube[1], STDOUT_FILENO);
close(tube[1]);
if(execl("/usr/bin/ls", "ls", NULL) == -1)
perror("Cannot execute ls");
}
else
{
wcFork = fork();
if(wcFork == 0) // wc command
{
sleep(1);
int file = open("output.txt", O_WRONLY | O_CREAT);
if(file == -1)
perror("Cannot open output.txt");
close(tube[1]);
dup2(tube[0], STDIN_FILENO);
close(tube[0]);
dup2(file, STDOUT_FILENO);
close(file);
/*char buffer[BUFSIZ];
read(STDIN_FILENO, buffer, BUFSIZ);
write(STDOUT_FILENO, buffer, BUFSIZ);*/
if(execl("/usr/bin/wc", "wc", NULL) == -1)
perror("Cannot execute wc");
close(STDOUT_FILENO);
}
else // parent
{
int status;
waitpid(lsFork, &status, 0);
waitpid(wcFork, &status, 0);
}
}
return EXIT_SUCCESS;
}
But, the program does not exit. According to htop, the wc command is blocking the program. To understand this behaviour, I wrote a piece of code (the lines commented before execl()) and I don't understand what this works and not execl(). Am I forgetting something when calling this function?
The parent process still has the pipe open, so wc is waiting around in case the parent decides to write stuff (which wc would need to count).
Close both ends of the pipe in the parent too:
else // parent
{
int status;
close(tube[0]); // <---
close(tube[1]); // <---
waitpid(lsFork, &status, 0);
waitpid(wcFork, &status, 0);
}
Don't complicate things when you can do it easily..
Try the simpler code below & see if you can understand anything or not.
int main(){
int tube[2];
int fID;
pipe(tube);
if (fork() == 0){
// this is the child process
close(tube[0]); // reading end of the pipe
dup2(tube[1], 1); // stdout ---> pipe writing end
execlp("ls", "ls", NULL);
}else{
if (fork() == 0){
//umask(0022);
fID = open("sample.txt", O_WRONLY | O_CREAT, 0644);
close(tube[1]); // writing end of the pipe
dup2(tube[0], 0); // stdin ----> pipe reading end
dup2(fID, 1);
execlp("wc", "wc", NULL);
}
}
return 0;
}
Note If the purpose of the code is to solely implement the above mentioned piping, then you don't need to implement any waiting mechanisms. The OS will auto-kill all the zombie child, if any. Moreover execlp("wc", "wc", NULL); will auto block the program to end. Hence it will not exit early
You'll need to close the write end of the pipe in the parent too.

dup return error (c programming in linux)

I'm trying to create a simple program which simulates the "ls -l | tail -n 2" call in terminal. I'm using "fork" and "execvp" for that purpose.
Well, here is the code:
int main(int argc, char *argv[])
{
int pipefd[2];
pid_t child1;
pid_t child2;
char* com1[] = {"ls", "-l",NULL};
char* com2[] = {"tail", "-n","2",NULL};
if (!(child1 = fork()))
{
close(STDOUT);
dup(pipefd[1]);
close(pipefd[1]);
execvp (com1[0], com1);
_exit(EXIT_SUCCESS);
}
else
{
close(pipefd[1]);
if (!(child2 = fork()))
{
close(STDIN);
dup(pipefd[0]); /* dupWR now holds the lowest fd available, meaning STDOUT's */
perror("dup 2");
close(pipefd[0]); /* reader will see EOF */
execvp (com2[0], com2);
_exit(EXIT_SUCCESS);
}
else
{
close(pipefd[0]);
waitpid(child2,0,0);
}
waitpid(child1,0,0);
}
return 0;
}
I get these errors:
dup 2: Bad file descriptor
tail: cannot fstat `standard input': Bad file descriptor
tail: -: Bad file descriptor
It seems to me that there is a problem in synchronization. In fact, if I declare:
com2[] = {"ls", "-l",NULL}; It works fine (I mean as in normal shell). Moreover, I found that the second "dup" in the second "fork" returns error. Why is that? I don't know where is the problem with this code. Please help!
Edit:
I added this code (forgot to create pipes):
if (pipe(pipefd) == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}
Thanks, Useless!
close(STDOUT);
dup(pipefd[1]);
close(pipefd[1]);
Since dup returns the new file descriptor, and you don't use the return value, you're discarding it.
Did you want to replace stdout instead, like so?
dup2(pipefd[1], STDOUT_FILENO);
If so, pipefd[] should really be initialized first. Did you mean to call pipe somewhere?

How do I use two pipes in Unix C?

I have a homework to do that says the following:
Write a program in C that creates a child who will also create a child, make a pipe between the three processes, the fist process(father) will connect the second(child) and the child will connect with the third (child of the child). Our program should display the total number of system users who use bash as default shell. The result of the program should be identical to the "cat / etc / passwd | grep" / bin / bash $ "| wc-l"
I am confused with the first child and the method that we close the first pipe and open the second in the same time. If you reply me with the right code I 'll undestand it right once.
Thank you.
Here is what I 've wrote so far:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
main()
{
int pid, pid2;
int fd[2];
int fd2[2];
char *arg[3];
char *arg2[3];
char *arg3[3];
if (pipe(fd) == -1)
{
perror("pipe");
exit(1);
}
pid = fork();
if (pid == -1)
{
perror("fork");
exit(2);
}
if (pid == 0)
{
if (pipe(fd2) == -1)
{
perror("pipe");
exit(11);
}
pid2=fork();
if(pid2 == -1)
{
perror("fork 2");
exit(22);
}
if (pid2 == 0)
{
//i am child 2 (child of the child)
close (fd2[1]);
dup2 (fd2[0],0);
close (fd2[0]);
arg3[0] = "wc";
arg3[1] = "-l";
arg3[2] = NULL;
execvp("wc", arg3);
perror("execvp second child");
}
else
{
//i am child 1
close (fd[1]);
dup2(fd[0],0);
close (fd[0]);
close (fd2[0]);
dup2(fd2[1],1);
close (fd2[1]);
arg2[0] = "grep";
arg2[1] = "/bin/bash$";
arg2[2] = NULL;
execvp("grep", arg2);
perror("execvp first child");
}
}
else
{
//i 'm the father
close (fd[0]);
dup2(fd[1],1);
close (fd[1]);
arg[0] = "cat";
arg[1] = "/etc/passwd";
arg[2] = NULL;
execvp("cat", arg);
perror("execvp father");
}
}
Your program very nearly works. What's missing is
//i am child 2 (child of the child)
close (fd[1]);
close (fd[0]);
The pipe you called fd is for communicating between 'cat' and 'grep'. What's happening in your current code is that cat dumps the file and exits, closing its output. Grep reads all of that and waits for the EOF on its input. Since "child 2" still has the input side of the pipe open (it inherited it via fork), grep waits forever. If run your program and then type ps you should see a grep and a wc hanging around waiting to finish.
The other thing you would normally do when constructing a pipeline like this is arrange it so that the final task (in this case wc) is the one that the shell is waiting for. As written, when your program is run from the shell it will appear to finish when cat finishes, and the output of wc will print as if from a background task. If you arrange the pipe so that wc is under "i am child 1" then the shell will be waiting for wc instead.
Alternatively you could fork all of the three processes off and "child 1" would invoke wait() to wait for all of them before exiting. That waiting process would be like your own tiny shell.

How to catch the ouput from a execl command

I'm using the execl function to run a Linux process from C. When I do, for example:
int cmd_quem() {
int result;
result = fork();
if(result < 0) {
exit(-1);
}
if (result == 0) {
execl("/usr/bin/who", "who", NULL);
sleep(4); //checking if father is being polite
exit(1);
}
else {
// father's time
wait();
}
return 0;
}
I get on the console the result of doing "who" on the terminal. What I'd like to know is if there is any function to "catch" the output result from a command. What I mean is, if there is anyway to catch this:
feuplive tty5 2009-11-21 18:20
Which is one of the lines resulting from the who command.
To do this, you need to open a pipe. You then replace the child's stdout with the writing end of the pipe, and read from the reading end of the pipe in the parent. Like this modified version of your code:
int cmd_quem(void) {
int result;
int pipefd[2];
FILE *cmd_output;
char buf[1024];
int status;
result = pipe(pipefd);
if (result < 0) {
perror("pipe");
exit(-1);
}
result = fork();
if(result < 0) {
exit(-1);
}
if (result == 0) {
dup2(pipefd[1], STDOUT_FILENO); /* Duplicate writing end to stdout */
close(pipefd[0]);
close(pipefd[1]);
execl("/usr/bin/who", "who", NULL);
_exit(1);
}
/* Parent process */
close(pipefd[1]); /* Close writing end of pipe */
cmd_output = fdopen(pipefd[0], "r");
if (fgets(buf, sizeof buf, cmd_output)) {
printf("Data from who command: %s\n", buf);
} else {
printf("No data received.\n");
}
wait(&status);
printf("Child exit status = %d\n", status);
return 0;
}
First, execl does not return unless there's a problem like the executable is not found. That sleep(4) is probably never executed.
As for redirecting and getting the output, check out the Unix Programming FAQ. Look for spawn_background_command.
The exec() family of functions creates a new process image from a regular, executable file. This file is either an executable object file, or an interpreter script. There is no return from a successful call to an exec() function, because the calling process is functionally replaced by the new process.
So any code after exec() is never executed unless it is failed.
If you want to capture output of a shell command you need popen.

Resources