I have to write a program that will perform the same operation that du | sort | head in the command line would do, but I'm stuck, and my program is not working. The output right now is 112 . and the program doesn't terminate. Please help, I don't know what to do!
int main(void) {
int fd[2];
int fd1[2];
int pid;
if (pipe(fd) == -1) {
perror("Pipe");
exit(1);
}
switch (fork()) {
case -1:
perror("Fork");
exit(2);
case 0:
dup2(fd[1], STDOUT_FILENO);
close(fd[0]);
close(fd[1]);
execl("/usr/bin/du", "du", (char *) 0);
exit(3);
}
if (pipe(fd1) == -1) {
perror("Pipe");
exit(1);
}
switch (fork()) {
case -1:
perror("Fork");
exit(2);
case 0:
dup2(fd[0], STDIN_FILENO);
dup2(fd1[1], STDOUT_FILENO);
close(fd[0]);
close(fd[1]);
close(fd1[0]);
close(fd1[1]);
execl("/usr/bin/sort", "sort", (char *) 0);
exit(3);
}
close(fd[0]);
close(fd[1]);
switch (fork()) {
case -1:
perror("Fork");
exit(2);
case 0:
dup2(fd1[0], STDIN_FILENO);
close(fd1[0]);
close(fd1[1]);
execl("/usr/bin/head", "head", (char *) 0);
exit(3);
}
}
Let head be your parent process, sort — its child process, and du — the child of sort, or the grandchild of head.
You need two pipes, thus, two arrays — fd and fd1. Let the fd pipe connect sort with head, and fd1 — du with sort.
You will need one big switch statement, which will determine whether you currently are in the parent process (head, pipe(fd) is not 0) or the child (sort, pipe(fd) is 0). If you are in sort, you need to create the fd1 pipe and run the grandchild process du. Now, since you again have two processes (three in total), you need to set a pipe according to your location — whether you are in the grandchild or the child process. You can use a similar switch statement as you did for pipe fd. The trick here is to set the input and output for fd1 pipe correctly.
Your code must do something like this:
int main(void) {
int fd[2]; // sort <===> head
int fd1[2]; // du <===> sort
pipe(fd);
switch (fork()) {
case 0: // Are we in sort?
pipe(fd1); // If yes, let's make a new pipe!
switch (fork()) {
case 0: // Are we in du?
dup2(fd1[1], STDOUT_FILENO);
close(fd1[0]);
close(fd1[1]);
execl("/usr/bin/du", "du", (whatever directory), NULL);
exit(1);
default:
/* If not in du, we're in sort! in the middle!
Let's set up both input and output properly.
We have to deal with both pipes */
dup2(fd1[0], STDIN_FILENO);
dup2(fd[1], STDOUT_FILENO);
close(fd1[0]);
close(fd1[1]);
execl("/usr/bin/sort", "sort (flags if needed)", (char *) 0);
exit(2);
}
exit(3);
default: // If we're not in sort, we're in head
dup2(fd[0], STDIN_FILENO);
close(fd[0]);
close(fd[1]);
execl("/usr/bin/head", "head (flags if needed)", (char *) 0);
exit(4);
}
}
Related
I'm trying to simulate a unix shell in a C program and it's still in the beginning and working for at most two pipes. I have a vector of commands (char *com[3][3]), which were separated considering the character "|", but my question is how to proceed to more pipes in a for loop? In the follow the current implementation, I'm trying to execute 3 commands separeted by pipes:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/types.h>
int main(int argc, char **argv){
//Vector with positions of pipes found, position 0 reserved for the total amount of commands.
char* com[3][3] = { { "/bin/ls", "-la", 0 },
{ "/bin/grep", ".", 0}, { "/usr/bin/wc", "-l", 0 }};
//EXECUTE COMMANDS
pid_t fork1, fork2, fork3;
int fd1[2], fd2[2];
if(pipe(fd1) < 0){
perror("pipe1");
}
if(pipe(fd2) < 0){
perror("pipe2");
}
//COMMAND 1
fork1 = fork();
if(fork1 == 0){
dup2(fd1[1], STDOUT_FILENO);
close(fd1[0]);
close(fd2[0]);
close(fd2[1]);
execvp(com[0][0], com[0]);
perror("execvp 1");
exit(EXIT_FAILURE);
}
//COMMAND 2
fork2 = fork();
if(fork2 == 0){
dup2(fd1[0], STDIN_FILENO);
dup2(fd2[1], STDOUT_FILENO);
close(fd1[1]);
close(fd2[0]);
execvp(com[1][0], com[1]);
perror("execvp 2");
exit(EXIT_FAILURE);
}
//COMMAND 3
fork3 = fork();
if(fork3 == 0){
dup2(fd2[0], STDIN_FILENO);
close(fd2[1]);
close(fd1[0]);
close(fd1[1]);
execvp(com[2][0], com[2]);
perror("execvp 3");
exit(EXIT_FAILURE);
}
close(fd1[0]);
close(fd1[1]);
close(fd2[0]);
close(fd2[1]);
waitpid(-1, NULL, 0);
waitpid(-1, NULL, 0);
waitpid(-1, NULL, 0);
return 0;
}
How do I make to com[n][3], in a for loop?
"To iterate is human, to recurse is divine" -- Anon.
I'd attack this with a recursive approach. This is one of those very rare occasions when being a Three Star programmer is almost justified. ;)
This is completely untested, but should get you pointed in the correct direction.
// You'll need to rearrange your command strings into this three dimensional array
// of pointers, but by doing so you allow an arbitrary number of commands, each with
// an arbitrary number of arguments.
int executePipe(char ***commands, int inputfd)
{
// commands is NULL terminated
if (commands[1] == NULL)
{
// If we get here there's no further commands to execute, so run the
// current one, and send its result back.
pid_t pid;
int status;
if ((pid = fork()) == 0)
{
// Set up stdin for this process. Leave stdout alone so output goes to the
// terminal. If you want '>' / '>>' redirection to work, you'd do that here
if (inputfd != -1)
{
dup2(inputfd, STDIN_FILENO);
close(inputfd);
}
execvp(commands[0][0], commands[0]);
perror("execvp");
exit(EXIT_FAILURE);
}
else if (pid < 0)
{
perror("fork");
exit(EXIT_FAILURE);
}
waitpid(pid, &status, 0);
return status;
}
else
{
// Somewhat similar to the above, except we also redirect stdout for the
// next process in the chain
int fds[2];
if (pipe(fds) != 0)
{
perror("pipe");
exit(EXIT_FAILURE);
}
pid_t pid;
int status;
if ((pid = fork()) == 0)
{
// Redirect stdin if needed
if (inputfd != -1)
{
dup2(inputfd, STDIN_FILENO);
close(inputfd);
}
dup2(fds[1], STDOUT_FILENO);
close(fds[1]);
execvp(commands[0][0], commands[0]);
perror("execvp");
exit(EXIT_FAILURE);
}
else if (pid < 0)
{
perror("fork");
exit(EXIT_FAILURE);
}
// This is where we handle piped commands. We've just executed
// commands[0], and we know there's another command in the chain.
// We have everything needed to execute that next command, so call
// ourselves recursively to do the heavy lifting.
status = executePipe(++commands, fds[0]);
// As written, this returns the exit status of the very last command
// in the chain. If you pass &status as the second parameter here
// to waitpid, you'll get the exit status of the first command.
// It is left as an exercise to the reader to figure how to get the
// the complete list of exit statuses
waitpid(pid, NULL, 0);
return status;
}
}
To use this, call it initially with the commands array set up as described, and inputfd initially -1.
If you want to handle < type redirection, you probably want to check for inputfd == -1 at the very top, do redirection if requested and replace inputfd with the appropriate value before entering the remainder of the body.
I need to implement such IPC-schema:
runtime data -> filter1 -> filter2 -> output. (same as data | filter1 | filter2).
I can pass data to first filter, but to second I can not (maybe because in first child stdout fd is not closed). How to properly implement such schema?
P.S. filter1 and filter2 just read from stdin and write to stdout.
My code:
int main() {
int fd1[2];
pipe(fd1);
pid_t pid1;
if ((pid1 = fork()) > 0) {
char data[] = "Hello world!";
close(fd1[0]);
write(fd1[1], data, sizeof(data));
close(fd1[1]);
wait(NULL);
exit(EXIT_SUCCESS);
} else if (pid1 == 0) {
int fd2[2];
pipe(fd2);
pid_t pid2;
dup2(fd1[0], STDIN_FILENO);
dup2(fd2[1], STDOUT_FILENO);
close(fd1[0]); close(fd1[1]);
close(fd2[0]); close(fd2[1]);
if ((pid2 = fork()) > 0) {
execl("./upcase", "upcase", NULL);
perror("execl");
exit(EXIT_FAILURE);
} else if (pid2 == 0) {
close(fd1[0]); close(fd1[1]);
dup2(fd2[0], STDIN_FILENO);
close(fd2[0]); close(fd2[1]);
execl("./reverse", "reverse", NULL);
perror("execl");
exit(EXIT_FAILURE);
} else {
perror("pid2");
exit(EXIT_FAILURE);
}
} else {
perror("pid1");
exit(EXIT_FAILURE);
}
}
You are closing the pipes too early. Typically, you close fd2[0] before you use it in dup2. And as you redirect FILENO_STDOUT before the second fork, the second filter has no longer access to the original stdout.
Following code works:
int main() {
int fd1[2];
pipe(fd1);
pid_t pid1;
if ((pid1 = fork()) > 0) {
char data[] = "Hello world!";
close(fd1[0]); // OK, will no longer be used
write(fd1[1], data, sizeof(data));
close(fd1[1]);
wait(NULL);
exit(EXIT_SUCCESS);
} else if (pid1 == 0) {
int fd2[2];
pipe(fd2);
pid_t pid2;
close(fd1[1]); // OK, no used from here
if ((pid2 = fork()) > 0) {
dup2(fd1[0], STDIN_FILENO); // redirections for filter1
dup2(fd2[1], STDOUT_FILENO);
close(fd1[0]); // close everything except stdin and stdout
close(fd2[0]); close(fd2[1]);
execl("./upcase", "upcase", NULL);
perror("execl upcase");
exit(EXIT_FAILURE);
} else if (pid2 == 0) {
close(fd1[0]); // not used here
dup2(fd2[0], STDIN_FILENO); // redirection for filter2
close(fd2[0]); close(fd2[1]); // close all what remains
execl("./reverse", "reverse", NULL);
perror("execl reverse");
exit(EXIT_FAILURE);
} else {
...
I'm writing a program that just chains together specifically 3 programs, etc "ls | sort | wc".
I looked through the other posts I found on google about multiple pipes but I still can't figure out what I've been doing wrong. Been stuck on this for a while. I believe my program is logically correct, but for some reason it doesn't run. What am I missing?
pipe(pipe1);
pipe(pipe2);
pid = fork();
if(pid > 0){
close(pipe2[1]);
dup2(pipe2[0], 0);
execlp(argv[3], argv[3], NULL);
}
else if(pid == 0){
pid2 = fork();
if(pid2 == 0){
close(pipe1[0]);
dup2(pipe1[1], 1);
execlp(argv[1], argv[1], NULL);
}
else if(pid2 > 0){
close(pipe1[1]);
dup2(pipe1[0], 0);
close(pipe2[0]);
dup2(pipe2[1], 1);
execlp(argv[2], argv[2], NULL);
}
}
You need to fork three times each time in the parent cases, and execute your commands only inside the child cases. Take a look here
Adjusted to your case:
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
int main(int argc, char **argv) {
int pid;
int pipe1[2];
int pipe2[2];
// create pipe1
if (pipe(pipe1) == -1) {
perror("bad pipe1");
exit(1);
}
// fork (ps aux)
if ((pid = fork()) == -1) {
perror("bad fork1");
exit(1);
}
else if (pid == 0) {
// stdin --> ps --> pipe1
// input from stdin (already done), output to pipe1
dup2(pipe1[1], 1);
// close fds
close(pipe1[0]);
close(pipe1[1]);
execlp(argv[1], argv[1], NULL);
// exec didn't work, exit
perror("bad exec ps");
_exit(1);
}
// parent
// create pipe2
if (pipe(pipe2) == -1) {
perror("bad pipe2");
exit(1);
}
// fork (grep root)
if ((pid = fork()) == -1) {
perror("bad fork2");
exit(1);
}
else if (pid == 0) {
// pipe1 --> grep --> pipe2
// input from pipe1
dup2(pipe1[0], 0);
// output to pipe2
dup2(pipe2[1], 1);
// close fds
close(pipe1[0]);
close(pipe1[1]);
close(pipe2[0]);
close(pipe2[1]);
execlp(argv[2], argv[2], NULL);
// exec didn't work, exit
perror("bad exec grep root");
_exit(1);
}
// parent
// close unused fds
close(pipe1[0]);
close(pipe1[1]);
// fork (grep sbin)
if ((pid = fork()) == -1) {
perror("bad fork3");
exit(1);
}
else if (pid == 0) {
// pipe2 --> grep --> stdout
// input from pipe2
dup2(pipe2[0], 0);
// output to stdout (already done). Close fds
close(pipe2[0]);
close(pipe2[1]);
execlp(argv[3], argv[3], NULL);
// exec didn't work, exit
perror("bad exec grep sbin");
_exit(1);
}
// parent
return 0;
}
I find another solution, the parent process treats the first command, write its output in the first pipe and create a child which will treat the second command and write its outputin the second pipe, meanwhile this child will create a sub-child which will treat the third command.
the command is : ls -l | grep "sthg" `| wc -l
#include<sys/types.h>
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include <fcntl.h>
int main(){
int p[2],p1[2];
int x,y,z;
pipe(p);
x= fork();
if (x>0){//père or parent
close(p[0]);
dup2(p[1],1);
execlp("ls","ls","-l",NULL);
close(p[1]);
}
else{ // fils du père or child of parent
pipe(p1);
z=fork();
if(z>0){ //fils du père or child of parent
close(p[1]);
close(p1[0]);
dup2(p[0],0);
dup2(p1[1],1);
execlp("grep","grep","sthg",NULL);
close(p[0]);
close(p1[1]);
}
else{ //fils du fils or sub-child
close(p[0]);
close(p[1]);
close(p1[1]);
dup2(p1[0],0);
execlp("wc","wc","-l",NULL);
close(p1[0]);
}
}
return 0;
}
I'm trying to make program on C, which execute console shell command
cat log.txt| awk '{ print $7 }' | head -10
but the third command won't work with 2 present.
Here's what i done
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
int fd[2], status;
pipe(fd);
pid_t pid1 = fork();
if (!pid1)
{
dup2(fd[1], 1);
close(fd[0]);
close(fd[1]);
char* command[3] = {"/bin/cat", "log.txt", 0};
execvp(command[0], command);
exit(EXIT_FAILURE);
}
else if (pid1 == -1)
{
fprintf(stderr, "Can't fork, exiting...\n");
exit(EXIT_FAILURE);
}
pid_t pid2 = fork();
if (!pid2) {
dup2(fd[0], 0);
close(fd[0]);
close(fd[1]);
char* command[3] = {"awk", "{ print $7 }", 0};
execvp(command[0], command);
exit(EXIT_FAILURE);
} else if (pid2 == -1) {
fprintf(stderr, "Can't fork, exiting...\n");
exit(EXIT_FAILURE);
}
pid_t pid3 = fork();
if (!pid3) {
dup2(fd[0], 0);
close(fd[0]);
close(fd[1]);
char* command[3] = {"head", "-10", 0};
execvp(command[0], command);
exit(EXIT_FAILURE);
} else if (pid3 == -1) {
fprintf(stderr, "Can't fork, exiting...\n");
exit(EXIT_FAILURE);
}
close(fd[0]);
close(fd[1]);
waitpid(pid1, NULL, 0);
waitpid(pid2, NULL, 0);
waitpid(pid3, &status, 0);
exit(status);
return 0;
}
pid3 can't execute. I tried to make dup2(fd[1], 1) in pid3, but thats doesn't work. What should be on pid3 to make it's work and how to make more than 3 commands using dup2?
You have created one pipe. One pipe has two ends. Two ends are enough for two processes. If you have three processes all in a single pipeline, you need two pipes. The process in the middle holds on two pipes and two other processes hold on the remaining ends.
Look at this picture:
cat | awk | head
See two pipe symbols? They are the two pipes you need.
You will have to set up two pipes-- one to connect cat to awk and one to connect awk to head.
Also, don't close file descriptors that you actually need (such as fd[0] in your first fork!)
I am implementing a shell in C. This is the function i use for piping. When i put "ls | a" in the code (i.e. pipe a valid command with invalid one),It doesnt exit the child process like it should. How do i make it go back to main function?
same thing happens when i do ps | ls or ps | pwd etc. but ls | ps works the same as in bash. i know ls | ps or ps | ls dont make sense but atleast they should give same output as bash.
void exec3(char **args, char **args2){
int fd[2];
pid_t pid,pid1;
int status;
pipe(fd);
int e=0;
if ((pid = fork()) < 0) {
printf("*** ERROR: forking child process failed\n");
exit(1);
}
else if ((pid1 = fork()) < 0) {
printf("*** ERROR: forking child process failed\n");
exit(1);
}
else if (pid == 0 && pid1!=0){
printf("in 1\n");
close(1);
dup(fd[1]);
close(fd[0]);
close(fd[1]);
if(execvp(args[0],args)<0){
printf("**error in exec");
close(fd[0]);
close(fd[1]);
exit(1);
}
//printf("exiting 1\n");
exit(0);
}
else if (pid1 == 0 && pid!=0) {
printf("in 2\n");
close(0);
dup(fd[0]);
close(fd[1]);
close(fd[0]);
if((e=execvp(args2[0],args2))<0){
printf("**error in exec2 ");
close(fd[0]);
close(fd[1]);
exit(1);
}
exit(0);
}
else {
close(fd[0]);
close(fd[1]);
fflush(stdout) ;
while (wait(&status) != pid);
while (wait(&status) != pid1);
}
}
You are close to the solution. Look at how popen() is implemented, that is what you are trying to do.