Hi I am trying to recreate a shell and I am having two major problems:
1. After executing one single command it finishes the program
2. The pipeline doesn´t work
Here is the part of the code that deals with the pipes, redirections ...
int pfd[2];
if (pipe(pfd) < 0) exit(-1);
for (int i = 0; i < cmd.pipes; i++) {
pid_t pid;
pid = fork();
int fd;
if (pid < 0) exit(-1);
else if (pid == 0) {
close(pfd[0]);
dup2(pfd[1], STDOUT_FILENO);
close(pfd[1]);
if (cmd.filev[0] != NULL && i == 0) {
fd = open(cmd.filev[0], O_RDONLY, 0);
dup2(fd, STDIN_FILENO);
close(fd);
}
if (cmd.filev[2] != NULL) {
fd = creat(cmd.filev[2], 0644);
dup2(fd, STDERR_FILENO);
close(fd);
}
if (execvp(cmd.argv[i][0], cmd.argv[i]) < 0)
levenshtein(cmd.argv[i][0], commands);
} else if (pid > 0) {
if (cmd.bg > 0) wait(NULL);
close(pfd[1]);
dup2(pfd[0], STDIN_FILENO);
close(pfd[0]);
if (cmd.filev[1] != NULL && i == (cmd.pipes - 1)) {
fd = creat(cmd.filev[1], 0644);
dup2(fd, STDOUT_FILENO);
close(fd);
}
if (cmd.filev[2] != NULL) {
fd = creat(cmd.filev[2], 0644);
dup2(fd, STDERR_FILENO);
close(fd);
}
if (execvp(cmd.argv[i][0], cmd.argv[i]) < 0)
levenshtein(cmd.argv[i][0], commands);
}
}
PD:
Levenshtein is a function to deal when there is a misspell by the user
This needs a bit of refactoring ...
If there are N pipeline stages, we need N-1 separate pipe calls. The code only has one.
Each pipeline stage can have its own/private stderr diversion (e.g.):
cmd0 | cmd1 2>cmd1_stderr | cmd2 2>cmd2_stderr | cmd3
But, the code assumes all stderr will be the same.
And, it assumes that it has to open stderr [at all]. The child should only open stderr if we have something like above. Otherwise, each child should inherit the parent's stderr [and do nothing].
The parent process is doing things that only the child should do (e.g.): change stdin/stdout/stderr and execute the command. It should mostly just facilitate the pipe.
The parent is doing a wait in the creation/fork loop [for each child]. This causes the parent to block at each step.
The parent should only do the wait in a second loop and wait for all children at once.
Because your struct definition for cmd was not posted, I had to guess a bit as to intent. But, I think you should have an array of the structs, one for each command, rather than putting all arguments for all commands in a single struct.
I've created two versions of the code. One with annotations for the bugs. A second that is cleaned up and restructured. Both are untested but should give you some ideas.
Here's the annotated version:
// NOTE/BUG: each command in the pipeline can have its own/private
// stderr diversion
#if 0
struct cmd {
int pipes;
char *filev[3];
char **argv[MAXCMD][MAXARG];
};
#else
struct cmd {
char *stderr;
char *argv[100];
};
#endif
// NOTE/BUG: we need separate filev [and argv] for each pipeline stage
// so we need an _array_ of structs
int cmdcnt;
struct cmd cmdlist[30];
void
pipeline(void)
{
int pfd[2];
// NOTE/BUG: this only creates a single pipe -- we need N-1 pipes
#if 0
if (pipe(pfd) < 0)
exit(-1);
#endif
// NOTE/BUG: if child does _not_ have a private stderr it should just
// use the parent's stderr _unchanged_
for (int i = 0; i < cmdcnt; i++) {
pid_t pid;
// NOTE/BUG: here is the correct place to create the pipe
pid = fork();
int fd;
if (pid < 0)
exit(-1);
char **argv = cmd->argv;
char **filev = cmd->filev;
// child process
if (pid == 0) {
close(pfd[0]);
dup2(pfd[1], STDOUT_FILENO);
close(pfd[1]);
// NOTE/BUG: this does _not_ connect the input of cmd[N] to cmd[N-1]
#if 0
if (filev[0] != NULL && i == 0) {
fd = open(filev[0], O_RDONLY, 0);
dup2(fd, STDIN_FILENO);
close(fd);
}
#endif
if (filev[2] != NULL) {
fd = creat(filev[2], 0644);
dup2(fd, STDERR_FILENO);
close(fd);
}
if (execvp(cmd->argv[i][0], cmd->argv[i]) < 0)
levenshtein(cmd->argv[i][0], commands);
}
// parent process
if (pid > 0) {
// NOTE/BUG: parent should _not_ wait in the middle of the creation
// loop
if (cmd->bg > 0)
wait(NULL);
// NOTE/BUG: _parent_ should _not_ change its stdin/stderr/stdout
close(pfd[1]);
dup2(pfd[0], STDIN_FILENO);
close(pfd[0]);
if (filev[1] != NULL && i == (cmd->pipes - 1)) {
fd = creat(filev[1], 0644);
dup2(fd, STDOUT_FILENO);
close(fd);
}
if (filev[2] != NULL) {
fd = creat(filev[2], 0644);
dup2(fd, STDERR_FILENO);
close(fd);
}
// NOTE/BUG: _parent_ should _not_ execute the command
if (execvp(cmd->argv[i][0], cmd->argv[i]) < 0)
levenshtein(cmd->argv[i][0], commands);
}
}
}
Here's the refactored version:
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
struct cmd {
char *stderr;
char *argv[100];
};
char *filev[3];
#define CLOSEME(_fd) \
do { \
if (_fd < 0) \
break; \
close(_fd); \
_fd = -1; \
} while (0)
int run_in_background;
int cmdcnt;
struct cmd cmdlist[30];
int
pipeline(void)
{
int pfd[2] = { -1, -1 };
struct cmd *cmd;
char *file;
int oldfd;
pid_t pid;
pid_t lastpid = -1;
// open the common stderr
int errfd = -1;
if (filev[2] != NULL)
errfd = open(filev[2],O_APPEND | O_CREAT);
// start all commands
for (int i = 0; i < cmdcnt; i++) {
int iam_first = (i == 0);
int iam_last = (i == (cmdcnt - 1));
cmd = &cmdlist[i];
// get previous stage pipe descriptor
oldfd = pfd[0];
// create the pipe to the next stage
if (! iam_last) {
if (pipe(pfd) < 0)
exit(1);
}
pid = fork();
lastpid = pid;
int fd;
if (pid < 0)
exit(-1);
char **argv = cmd->argv;
// parent process
if (pid > 0) {
CLOSEME(pfd[1]);
continue;
}
// child process ...
// open stdin for _first_ command
fd = -1;
if (iam_first) {
file = filev[0];
if (file != NULL) {
fd = open(file, O_RDONLY, 0);
}
}
// connect stdin to previous stage pipe
else {
fd = oldfd;
oldfd = -1;
}
// connect stdin to correct source
if (fd >= 0) {
dup2(fd, STDIN_FILENO);
close(fd);
}
CLOSEME(oldfd);
// connect to stderr
file = cmd->stderr;
if (file != NULL) {
fd = creat(file, 0644);
dup2(fd, STDERR_FILENO);
close(fd);
}
else {
if (errfd >= 0)
dup2(errfd, STDERR_FILENO);
}
CLOSEME(errfd);
// connect stdout
// NOTE: does _not_ handle ">> outf" [only does "> outf"]
fd = -1;
if (iam_last) {
file = filev[1];
if (file != NULL) {
fd = open(file, O_WRONLY | O_CREAT, 0644);
dup2(fd, STDOUT_FILENO);
close(fd);
}
}
// execute the command
execvp(argv[0], argv);
exit(9);
}
CLOSEME(errfd);
int status;
int last_status = 0;
// parent waits for all pipeline stages to complete
if (! run_in_background) {
while (1) {
pid = wait(&status);
if (pid <= 0)
break;
if (pid == lastpid) {
last_status = status;
break;
}
}
}
return last_status;
}
Related
The following function successfully executes any command that doesn't contain pipes, so don't worry about the weird functions. These work. The problem I am having is that whenever I execute any command like the following:
cat file.txt | grep string
the command is successfully executed, but it remains idle, so somehow it gets stuck and no other command can execute. why is this happening?. I think it has something to do with the way I use pipe, dup and fork, so try to approach the problem from these functions. I know you may be arguing that this code doesn't work for other commands with pipes, but I just want to get this particular example to work and to do so I just redirect STDIN to the open file in the first iteration.
int myshell_execute(struct processNode* list, int a)
{
struct processNode* myList = list; // next node to be handled
int pipefd[2];
int in=0;
if (pipe(pipefd) == -1) {
perror("pipe");
myshell_exit(-1);
}
while(myList != NULL)
{
char* program = myList->program; // Get the program to be executed
char ** program_args = myList->program_arguments; // get the programs and arguments to be executed
char ** redirection_string = myList->redirection; //get the part of the command that contains redirection
int *status;
int* stdout;
int stdout_num = 1;
stdout = &stdout_num;
int fileDescriptor;
pid_t pid;
if(strcmp(program,"cd") == 0)
{
return myshell_cd(program_args);
}
else if (strcmp(program,"exit") == 0)
{
return myshell_exit(0);
}
pid = fork();
if(pid == 0)
{
if(in == 1)
{
close(pipefd[1]);
dup2(pipefd[0],0);
close(pipefd[0]);
}
if(sizeOfLine(redirection_string) != 0)
{
redirectionHandler(redirection_string,stdout); // This works. This just handles redirection properly
}
if(*stdout == 1 && myList->next !=NULL)
{
close(pipefd[0]);
dup2(pipefd[1],STDOUT_FILENO); // with this
close(pipefd[1]);
}
if(execvp(program,program_args) !=-1)
{
perror("myshell:");
myshell_exit(-1);
}
else{
myshell_exit(0);
}
}
else if (pid <0)
{
perror("myshell: ");
myshell_exit(-1);
}
else
{
wait(status);
}
in = 1;
myList = myList->next;
}
}
new solution:
int helper_execute(int in, int out, char* program, char ** program_args, char *redirection)
{
pid_t pid;
if ((pid = fork ()) == 0)
{
if (in != 0)
{
dup2 (in, 0);
close (in);
}
if (out != 1)
{
dup2 (out, 1);
close (out);
}
redirectionHandler(redirection);
return execvp (program, program_args);
}
return pid;
}
int myshell_execute(struct processNode* list, int a)
{
int i;
pid_t pid;
int in, fd [2];
struct processNode*new_list = list;
char ** newProgram = new_list->program_arguments;
char ** redirection = new_list->redirection;
char * program = new_list->program;
/* The first process should get its input from the original file descriptor 0. */
in = 0;
/* Note the loop bound, we spawn here all, but the last stage of the pipeline. */
while(new_list->next != NULL)
{
pipe (fd);
/* f [1] is the write end of the pipe, we carry `in` from the prev iteration. */
helper_execute (in, fd [1],program,newProgram,redirection);
/* No need for the write end of the pipe, the child will write here. */
close (fd [1]);
/* Keep the read end of the pipe, the next child will read from there. */
in = fd [0];
new_list = new_list->next;
}
/* Last stage of the pipeline - set stdin be the read end of the previous pipe
and output to the original file descriptor 1. */
if (in != 0)
dup2 (in, 0);
/* Execute the last stage with the current process. */
char* lastProgram = new_list->program;
char ** lastRedirection = new_list->redirection;
char * lastPrArguments = new_list->program_arguments;
redirectionHandler(redirection);
return execvp (lastProgram, lastPrArguments);
}
int main() {
int i=0;
char **input;
struct processNode* list;
int tracker = 0;
while ((input = getline()) != EOF) {
list = create_list(input);
myshell_execute(list,0);
}
return 0;
}
The only problem with this solution is that as soon as one command is executed, the main immediately detects the end of the file, so it exits the shell.
This is because
your parent process also holds the pipe open,
just after forking you call wait. If your first process (cat) fills its output pipe and there is not any process yet available to consume the read end of the pipe then the process stalls forever.
It would look like this:
#define MAX_PIPE_LEN 256
int myshell_execute(struct processNode* list, int a)
{
/* fd0 are the input and output descriptor for this command. fd1
are the input and output descriptor for the next command in the
pipeline. */
int fd0[2] = { STDIN_FILENO, STDOUT_FILENO },
fd1[2] = { -1, -1 };
pid_t pids[MAX_PIPE_LEN] = { 0 };
int pipe_len;
struct processNode* myList; // next node to be handled
int status;
int failed = 0;
for (pipe_len = 0, myList = list;
pipe_len < MAX_PIPE_LEN && myList != NULL;
pipe_len++, myList = myList->next) {
char* program = myList->program; // Get the program to be executed
char ** program_args = myList->program_arguments; // get the programs and arguments to be executed
char ** redirection_string = myList->redirection; //get the part of the command that contains redirection
if(strcmp(program,"cd") == 0) {
return myshell_cd(program_args);
}
else if (strcmp(program,"exit") == 0) {
return myshell_exit(0);
}
if (myList->next != NULL) {
/* The output of this command is piped into the next one */
int pipefd[2];
if (pipe(pipefd) == -1) {
perror("pipe failed");
failed = 1;
break;
}
fd1[0] = pipefd[0];
fd1[1] = fd0[1];
fd0[1] = pipefd[1];
}
pids[pipe_len] = fork();
if (pids[pipe_len] < 0) {
perror("error: fork failed");
failed = 1;
break;
}
if (pids[pipe_len] == 0) {
if (fd0[0] != STDIN_FILENO) {
if (dup2(fd0[0], STDIN_FILENO) == -1) {
perror("error: dup2 input failed");
abort();
}
close(fd0[0]);
}
if (fd0[1] != STDOUT_FILENO) {
if (dup2(fd0[1], STDOUT_FILENO) == -1) {
perror("error: dup2 outut failed");
abort();
}
close(fd0[1]);
}
if (fd1[0] >= 0) {
close(fd1[0]);
}
if (fd1[1] >= 0) {
close(fd1[1]);
}
if(sizeOfLine(redirection_string) != 0) {
redirectionHandler(redirection_string,stdout); // This works. This just handles redirection properly
}
execvp(program, program_args);
perror("error: execvp failed");
abort();
}
if (fd0[0] != STDIN_FILENO) {
close(fd0[0]);
}
if (fd1[1] != STDOUT_FILENO) {
close(fd0[1]);
}
fd0[0] = fd1[0];
fd0[1] = fd1[1];
fd1[0] = fd1[1] = -1;
}
if (myList->next) {
fprintf(stderr, "ERROR: MAX_PIPE_LEN (%d) is too small\n",
MAX_PIPE_LEN);
}
if (fd0[0] >= 0 && fd0[0] != STDIN_FILENO) {
close(fd0[0]);
}
if (fd0[1] >= 0 && fd0[1] != STDOUT_FILENO) {
close(fd0[1]);
}
if (fd1[0] >= 0) {
close(fd1[0]);
}
if (fd1[1] >= 0 && fd1[1] != STDOUT_FILENO) {
close(fd1[1]);
}
/* Now wait for the commands to finish */
int i;
for (i = 0; i < pipe_len; i++) {
if (waitpid(pids[pipe_len - 1], &status, 0) == -1) {
perror("error: waitpid failed");
failed = 1;
}
}
if (failed)
status = -1;
myshell_exit(status);
}
I am trying to implement the GNU popen library but having issues with dup2. I want to duplicate the child pipe to both STDOUT_FILENO and STDERR_FILENO.
I am little confuse here with the dup2, because it says on the man page that dup2 closes the old descriptor, how how can use duplicate pipe onto STDERR_FILENO after doing this dup2(pipe_list[child_fd], child_fd). Because from what i do understand upon a call to dup2 the oldfp will be closed and this dup2(pipe_list[child_fd], STDERR_FILENO) will not work because pipe_list[child_fd] was already closed. Should i use dup and after that close fd or there is a way to achieve this with dup2?
#define tpsr(a,b) (*type == 'r' ? (b) : (a))
#define tpsw(a,b) (*type == 'w' ? (b) : (a))
static FILE *fp_stream = NULL;
static pid_t popen_pid = -1;
static const char * const shell_path = "/bin/sh";
FILE *mypopen(const char *command, const char *type) {
int pipe_list[2];
int parent_fd, child_fd;
if (type == NULL || type[1] != 0) {
errno = EINVAL;
return NULL;
}
if (type[0] != 'w' && type[0] != 'r') {
errno = EINVAL;
return NULL;
}
if (pipe(pipe_list) == -1) {
return NULL; //errno will be set
}
child_fd = tpsr(STDIN_FILENO,STDOUT_FILENO);
parent_fd = tpsw(STDIN_FILENO,STDOUT_FILENO);
/*The above (tpsr and tpsw) are the same as this
if type[0] == 'r'
child_fd = STDOUT_FILENO; //1 child will be doing the writing
parent_fd = STDIN_FILENO; //0 parent read
if type[0] == 'w'
child_fd = STDIN_FILENO; //0 child doing the reading
parent_fd = STDOUT_FILENO;//1 parent do the writing
}*/
if ((popen_pid = fork()) == -1) {
close(pipe_list[0]);
close(pipe_list[1]);
return NULL;
}
if (popen_pid == 0) {
// we got a child here
if (pipe_list[child_fd] != child_fd) {
if (dup2(pipe_list[child_fd], child_fd) == -1) {
(void) close(pipe_list[child_fd]);
_exit(EXIT_FAILURE);
}
//is this the right way after the oldfd is closed by dup2
if (child_fd == STDOUT_FILENO) {
if (dup2(pipe_list[child_fd], STDERR_FILENO) == -1){
(void) close(pipe_list[child_fd]);
_exit(EXIT_FAILURE);
}
}
(void) close(pipe_list[child_fd]);
}
(void) pipe_list[parent_fd];
(void) execl(shell_path, "sh", "-c", command, (char *) NULL);
_exit(EXIT_FAILURE); //exit(127) required by man page
} else {
(void) close(pipe_list[child_fd]);
if ((fp_stream = fdopen(pipe_list[parent_fd], type)) == NULL) {
(void) close(pipe_list[parent_fd]);
return NULL;
}
}
return fp_stream;
}
When you call dup2(fd1, fd2):
If fd1 is not an open file descriptor, the function will return -1 and set errno to EBADF. fd2 is not closed.
If fd2 is outside the allowed range, or if it is not open but the process already has the maximum number of open file descriptors (RLIMIT_NOFILE), the function will return -1 and set errno to EBADF.
If any other problem occurs, the function will return -1 with errno set to some error code. It is not specified whether fd2 is untouched or closed in this case.
The rest of the cases below assume the operation is successful.
If fd1 == fd2, the function will return fd1 (which is the same as d2).
If fd2 is not an open descriptor, then fd1 is duplicated to it. The two descriptors then refer to the same file description (the stuff like file position that the kernel keeps).
If fd2 is an open file descriptor, it gets closed when fd1 is duplicated to it. The closing is silent, meaning that any error due to the closing is silently ignored.
The main point is, that only fd2 may be closed, and only if fd2 != fd1 and if fd2 was previously open. In no case is fd1 closed.
I cannot really follow your logic with regards to your code. In particular, using the parent_fd and client_fd as indexes to pipe_list[] seems suspect to me. Furthermore, the function should return both a file handle, as well as the child process PID.
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
typedef struct {
FILE *child_pipe;
pid_t child_pid;
int exit_status;
} cmd;
/* open() analog, except moves the descriptor to the
number specified. You cannot use O_CREAT flag.
Returns 0 if success, -1 if an error occurred,
with reason in errno. */
static int open_fd(const int fd, const char *path, const int flags)
{
int tempfd;
if (fd == -1 || !path || !*path || (flags & O_CREAT)) {
errno = EINVAL;
return -1;
}
tempfd = open(path, flags);
if (tempfd == -1)
return -1;
if (tempfd != fd) {
if (dup2(tempfd, fd) == -1) {
const int failure = errno;
close(tempfd);
errno = failure;
return -1;
}
if (close(tempfd)) {
const int failure = errno;
close(fd);
errno = failure;
return -1;
}
}
return 0;
}
/* pipe[] analog, except ensures neither endpoint
matches STDIN_FILENO, STDOUT_FILENO, or STDERR_FILENO.
*/
static int create_pipe(int fd[2])
{
/* I like to initialize return parameters.. */
fd[0] = -1;
fd[1] = -1;
if (pipe(fd) == -1)
return -1;
else {
const int close_stdin = (fd[0] == STDIN_FILENO) || (fd[1] == STDIN_FILENO);
const int close_stdout = (fd[0] == STDOUT_FILENO) || (fd[1] == STDOUT_FILENO);
const int close_stderr = (fd[0] == STDERR_FILENO) || (fd[1] == STDERR_FILENO);
int failure = 0;
do {
while (fd[0] == STDIN_FILENO ||
fd[0] == STDOUT_FILENO ||
fd[0] == STDERR_FILENO) {
fd[0] = dup(fd[0]);
if (fd[0] == -1) {
failure = -1;
break;
}
}
if (failure)
break;
while (fd[1] == STDIN_FILENO ||
fd[1] == STDOUT_FILENO ||
fd[1] == STDERR_FILENO) {
fd[1] = dup(fd[1]);
if (fd[1] == -1) {
failure = -1;
break;
}
}
if (failure)
break;
if (close_stdin)
close(STDIN_FILENO);
if (close_stdout)
close(STDOUT_FILENO);
if (close_stderr)
close(STDERR_FILENO);
return 0;
} while (0);
/* Whoops, failed: cleanup time. */
failure = errno;
if (fd[0] != STDIN_FILENO &&
fd[0] != STDOUT_FILENO &&
fd[0] != STDERR_FILENO)
close(fd[0]);
if (fd[1] != STDIN_FILENO &&
fd[1] != STDOUT_FILENO &&
fd[1] != STDERR_FILENO)
close(fd[1]);
if (close_stdin)
close(STDIN_FILENO);
if (close_stdout)
close(STDOUT_FILENO);
if (close_stderr)
close(STDERR_FILENO);
errno = failure;
return -1;
}
}
#define CMD_PASS 0
#define CMD_READ 1
#define CMD_DISCARD 2
int cmd_read(cmd *pipe,
const char *path,
char *args[],
const int stdout_mode,
const int stderr_mode)
{
int pipefd[2], controlfd[2], cause;
FILE *handle;
pid_t child, p;
/* If pipe is not NULL, initialize it. */
if (pipe) {
pipe->child_pipe = NULL;
pipe->child_pid = 0;
pipe->exit_status = 0;
}
/* Verify the parameters make sense. */
if (!path || !args || !pipe ||
stdout_mode < 0 || stdout_mode > 2 ||
stderr_mode < 0 || stderr_mode > 2) {
errno = EINVAL;
return -1;
}
/* Do we need the pipe? */
if (stdout_mode == CMD_READ || stderr_mode == CMD_READ) {
if (create_pipe(pipefd) == -1)
return -1;
} else {
pipefd[0] = -1;
pipefd[1] = -1;
}
/* We use a control pipe to detect exec errors. */
if (create_pipe(controlfd) == -1) {
cause = errno;
if (pipefd[0] != -1)
close(pipefd[0]);
if (pipefd[1] != -1)
close(pipefd[1]);
errno = cause;
return -1;
}
/* Parent reads from the control pipe,
and the child writes to it, but only
if exec fails. We mark the write end
close-on-exec, so the parent notices
if the exec is successful. */
fcntl(controlfd[1], F_SETFD, O_CLOEXEC);
/* Fork the child process. */
child = fork();
if (child == (pid_t)-1) {
cause = errno;
close(controlfd[0]);
close(controlfd[1]);
if (pipefd[0] != -1)
close(pipefd[0]);
if (pipefd[1] != -1)
close(pipefd[1]);
errno = cause;
return -1;
}
if (!child) {
/* This is the child process. */
close(controlfd[0]);
if (pipefd[0] != -1)
close(pipefd[0]);
cause = 0;
do {
if (stdout_mode == CMD_READ) {
if (dup2(pipefd[1], STDOUT_FILENO) == -1) {
cause = -1;
break;
}
} else
if (stdout_mode == CMD_DISCARD) {
if (open_fd(STDOUT_FILENO, "/dev/null", O_WRONLY) == -1) {
cause = -1;
break;
}
}
if (cause)
break;
if (stderr_mode == CMD_READ) {
if (dup2(pipefd[1], STDERR_FILENO) == -1) {
cause = -1;
break;
}
} else
if (stderr_mode == CMD_DISCARD) {
if (open_fd(STDERR_FILENO, "/dev/null", O_WRONLY) == -1) {
cause = -1;
break;
}
}
if (cause)
break;
if (pipefd[1] != -1)
close(pipefd[1]);
if (path[0] == '/')
execv(path, (char *const *)args);
else
execvp(path, (char *const *)args);
/* Failed. */
} while (0);
/* Tell parent, why. */
cause = errno;
/* To silence the warn_unused_result warning: */
if (write(controlfd[1], &cause, sizeof cause))
;
close(controlfd[1]);
exit(127);
}
/* Parent process. */
close(controlfd[1]);
if (pipefd[1] != -1)
close(pipefd[1]);
do {
ssize_t n;
/* Read from the pipe to see if exec failed. */
n = read(controlfd[0], &cause, sizeof cause);
if (n == (ssize_t)sizeof cause)
break;
if (n != 0) {
cause = EIO;
kill(child, SIGKILL);
break;
}
close(controlfd[0]);
if (pipefd[0] != -1) {
handle = fdopen(pipefd[0], "r");
if (!handle) {
cause = errno;
kill(child, SIGKILL);
break;
}
} else
handle = NULL;
/* Success! */
pipe->child_pipe = handle;
pipe->child_pid = child;
return 0;
} while (0);
/* Failed; reason is in cause. */
if (pipefd[0] != -1)
close(pipefd[0]);
/* Reap child. */
while (1) {
p = waitpid(child, NULL, 0);
if ((p == child) || (p == (pid_t)-1 && errno != EINTR))
break;
}
errno = cause;
return -1;
}
int cmd_wait(cmd *pipe)
{
pid_t p;
if (!pipe || pipe->child_pid == -1)
return errno = EINVAL;
while (1) {
p = waitpid(pipe->child_pid, &(pipe->exit_status), 0);
if (p == pipe->child_pid) {
if (pipe->child_pipe)
fclose(pipe->child_pipe);
pipe->child_pipe = NULL;
pipe->child_pid = 0;
return 0;
} else
if (p != -1) {
if (pipe->child_pipe)
fclose(pipe->child_pipe);
pipe->child_pipe = NULL;
pipe->child_pid = 0;
errno = EIO;
return -1;
} else
if (errno != EINTR) {
const int cause = errno;
if (pipe->child_pipe)
fclose(pipe->child_pipe);
pipe->child_pipe = NULL;
pipe->child_pid = 0;
errno = cause;
return -1;
}
}
}
int main(int argc, char *argv[])
{
off_t total = 0;
char *line = NULL;
size_t size = 0;
ssize_t len;
int stdout_mode, stderr_mode;
cmd run;
if (argc < 3 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
printf("\n");
printf("Usage:\n");
printf(" %s [ -h | --help ]\n", argv[0]);
printf(" %s MODE COMMAND [ ARGS ... ]\n", argv[0]);
printf("Where MODE may contain:\n");
printf(" o to retain output,\n");
printf(" O to discard/hide output,\n");
printf(" e to retain errors, and\n");
printf(" E to discard/hide errors.\n");
printf("All other characters are ignored.\n");
printf("If there is no 'o' or 'O' in MODE, then output\n");
printf("is passed through to standard output; similarly,\n");
printf("if there is no 'e' or 'E' in MODE, then errors\n");
printf("are passed through to standard error.\n");
printf("\n");
return EXIT_SUCCESS;
}
if (strchr(argv[1], 'o'))
stdout_mode = CMD_READ;
else
if (strchr(argv[1], 'O'))
stdout_mode = CMD_DISCARD;
else
stdout_mode = CMD_PASS;
if (strchr(argv[1], 'e'))
stderr_mode = CMD_READ;
else
if (strchr(argv[1], 'E'))
stderr_mode = CMD_DISCARD;
else
stderr_mode = CMD_PASS;
if (cmd_read(&run, argv[2], argv + 2, stdout_mode, stderr_mode) == -1) {
fprintf(stderr, "%s: %s.\n", argv[2], strerror(errno));
return EXIT_FAILURE;
}
if (run.child_pipe) {
while (1) {
len = getline(&line, &size, run.child_pipe);
if (len == -1)
break;
total += (off_t)len;
#ifdef PRINT_PIPE_CONTENTS
if (len > 0)
fwrite(line, (size_t)len, 1, stdout);
#endif
}
if (ferror(run.child_pipe) || !feof(run.child_pipe)) {
fprintf(stderr, "%s: Error reading from pipe.\n", argv[2]);
return EXIT_FAILURE;
}
}
if (cmd_wait(&run) == -1) {
fprintf(stderr, "%s: Lost child process: %s.\n", argv[2], strerror(errno));
return EXIT_FAILURE;
}
fprintf(stderr, "Read %llu bytes from child process.\n", (unsigned long long)total);
if (WIFEXITED(run.exit_status))
fprintf(stderr, "Child exited with status %d.\n", WEXITSTATUS(run.exit_status));
else
if (WIFSIGNALED(run.exit_status))
fprintf(stderr, "Child terminated due to signal %d.\n", WTERMSIG(run.exit_status));
else
fprintf(stderr, "Child lost.\n");
return EXIT_SUCCESS;
}
If you save the above as pipe_example.c, you can compile it using e.g.
gcc -Wall -O2 pipe_example.c -o pipe_ex
and test it by running e.g.
./pipe_ex oe sh -c 'printf "OUT\n"; printf "ERR\n" >&2; exit 5'
./pipe_ex Oe sh -c 'printf "OUT\n"; printf "ERR\n" >&2;'
./pipe_ex -E sh -c 'printf "OUT\n"; printf "ERR\n" >&2; kill -TERM $$'
The above code is, as you can obviously see, quite complicated -- and overall, it is just a popen() analog, but with fewer features.
However, it does everything quite carefully (except perhaps passing the reason the exec failed from the child to the parent -- there I am simply assuming the write succeeds). As such, it might be useful as an example of how many details (and at least one way of dealing with such details) there are in practical multiprocess programs. And, of course, how useful standard POSIX.1 popen() is.
A bit more detailed example with stdout and stderr at the same time.
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
int main(int argc, char *argv[]) {
int fdout[2]; /* pipe fd array for the stdout */
int fderr[2]; /* pipe fd array for the stderr */
pid_t pid; /* process id */
char buf[4096]; /* stdout and stderr buffer */
int status; /* place to store child process status */
if (pipe(fdout)==-1) /* first pipe for the stdout */
perror("pipe out"); /* exit with error if something wrong */
if (pipe(fderr)==-1) /* second pipe for the stderr */
perror("pipe err");
/*
* On success, the PID of the child process is returned in the
* parent, and 0 is returned in the child. On failure, -1 is
* returned in the parent, no child process is created, and errno is
* set to indicate the error.
*/
if ((pid = fork()) == -1) /* fork current process and store pid */
perror("fork error");
if(pid == 0) { /* if pid == 0 than this is a child process */
dup2 (fdout[1], STDOUT_FILENO); /* send stdout to the pipe fdout */
dup2 (fderr[1], STDERR_FILENO); /* send stderr to the pipe fderr */
/* close both sides of the pipe in the child */
close(fdout[0]);
close(fdout[1]);
/* close both sides of the pipe in the child */
close(fderr[0]);
close(fderr[1]);
/* execvp is called in the child ps
* argv[1] is a ptr to the first arg
* &argv[1] is a ptr to a ptr to the first arg
*/
execvp(argv[1], &argv[1]);
/* this part is never reached if execvp success */
return errno;
} else { /* parent */
while(wait(&status) > 0){} /* wait for the child processes to finish */
/* close write ends of the pipes */
close(fdout[1]);
close(fderr[1]);
/* if child process finished with errorprint error
* and return error code from parent process
*/
if (WIFEXITED(status) && WEXITSTATUS(status)){
printf("%s\n", strerror(WEXITSTATUS(status)));
return WEXITSTATUS(status);
}
ssize_t size;
size = read(fdout[0], buf, sizeof(buf));
printf("OUT: \"%.*s\"\n", (int)size, buf);
size = read(fderr[0], buf, sizeof(buf));
printf("ERR: \"%.*s\"\n", (int)size, buf);
/* close read ends of the pipes */
close(fdout[0]);
close(fderr[0]);
}
return 0;
}
compile and test:
ruslan#localhost:~/exec$ gcc main.c -o a && ./a /bin/ls
OUT: "a
main.c
"
ERR: ""
ruslan#localhost:~/exec$ gcc main.c -o a && ./a /bin/ls ---sdfadsfsdf
OUT: ""
ERR: "/bin/ls: unrecognized option '---sdfadsfsdf'
Try '/bin/ls --help' for more information.
"
ruslan#localhost:~/exec$
This is my first question so I apologize if I'm omitting anything important. So I've been working on an assignment that handles piping via forking. My code is pretty messy, littered with printf statements so I see what's going on.
I've looked around online and I think I get the idea of how to handle piping, but the problem I'm having is that my code skips dup2() on any file descriptor except inFD and outFD.
Here's the code for my function. Also, from what I understand, my teacher made a macro called CHK which checks for errors. If there is an error (such as dup2 returning -1), it'll terminate with a print to stderr.
My includes, global variables and myhandler() for signal
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include <fcntl.h>
#include <strings.h>
#include <math.h>
#include <signal.h>
// Function calls
void parse(char *w, char **ptrArray, char *inArray, char *outArray, int *pipeArray);
int flagHandler(char **ptrArray, char *inArray, char *outArray);
int pipeHandler(char **ptrArray, char *inArray, char *outArray, int *pipeArray);
// Global Variables
const int STORAGE = 254;
const int MAXITEM = 100;
int inFD; // file descriptor for <
int outFD; // file descriptor for >
int complete = 0; // for sighandler
int readDes = 0;
int writeDes = 1;
int numPipes = 0;
int status;
int forCounter = 0;
int fildes[4];
int pipeIndex = 0;
// MetaChar flags
int lessthanSign = 0; // < flag
int greaterthanSign = 0; // > flag
int firstChildFlag = 0;
int lastChildFlag = 0;
void myhandler(int signum)
{
complete = 1;
}
My main function
int main()
{
char s[STORAGE]; // array of words
char *newargv[MAXITEM];
char inArray[STORAGE]; // for <
char outArray[STORAGE]; // for >
int firstCheck;
int pidBackground; // holds value from fork(), used for background calls
struct stat st; // for stat(), checks if file exists
// dynamic array based on numPipes
// first child doesn't use this array, as it uses newargv[0] and newargv
// only the middle children and last child use this array, hence 10
int *pipeArray = malloc(10 * sizeof(int));
int numLoops = 0;
int i = 0;
signal(SIGTERM, myhandler);
for(;;)
{
// Reset flags here
lessthanSign = 0;
greaterthanSign = 0;
pipeSign = 0;
firstChildFlag = 0;
lastChildFlag = 0;
pipeIndex = 0;
parse(s, newargv, inArray, outArray, pipeArray);
pipeHandler(newargv, inArray, outArray, pipeArray);
wait(NULL);
fflush(NULL);
} // end for
printf("Entering killpg; numLoops = %d\n", numLoops);
killpg(getpid(), SIGTERM);
printf("p2 terminated.\n");
exit(0);
} // end main
Main calls parse which fills in newargv[]. It also fills in inArray[] and outArray[] with the string immediately after a < and > respectively. When detecting a pipe sign, it puts a null on newargv[], as well as putting a value in pipeArray[] for indexing the executable's name in newargv. I omitted the parse() and flagHandler() calls to keep it minimal.
My parseHandler() function
int pipeHandler(char **ptrArray, char *inArray, char *outArray, int *pipeArray)
{
pid_t firstChild;
pid_t firstChildBackground;
pid_t middleChild;
pid_t lastChild;
pid_t lastChildBackground;
int i = 0; // plain integer for for loops
printf("Initializing pipes\n");
//pipe(fildes);
//pipe(fildes + 2);
for (i = 0; i < (2*numPipes); i+=2)
{
printf("pipe initializing; i is %d\n", i);
if (pipe(fildes + i) < 0)
{
perror("pipe initialization failed");
exit(EXIT_FAILURE);
}
}
fflush(stdout);
if ((firstChild = fork()) < 0)
{
perror("First child's fork failed!");
exit(EXIT_FAILURE);
}
printf("firstChild pid = %d\n", getpid());
if (firstChild == 0)
{
if (firstChildFlag == 1)
{
printf("inFD = open...\n");
inFD = open(inArray, O_RDONLY);
printf("Doing dup2 inFD\n");
if (dup2(inFD, STDIN_FILENO) < 0)
{
perror("First child's < dup2 failed");
exit(EXIT_FAILURE);
}
}
printf("doing dup2 fildes[writeDes]\n");
if (dup2(fildes[writeDes], STDOUT_FILENO) < 0)
{
perror("First child's dup2 failed");
exit(EXIT_FAILURE);
}
printf("*****doing dup2 fildes[writeDes] was a success!\n");
for (i = 0; i < 4; i++)
{
if (close(fildes[i]) < 0)
{
perror("close failed");
exit(EXIT_FAILURE);
}
}
if (firstChildFlag == 1)
{
lessthanSign = 0;
firstChildFlag = 0;
if (close(inFD) < 0)
{
perror("close inFD failed");
exit(EXIT_FAILURE);
}
}
writeDes += 2;
printf("About to execvp first child\n");
if (execvp(ptrArray[0], ptrArray) < 0)
{
perror("execvp failed");
exit(EXIT_FAILURE);
}
}
else
{
fflush(stdout);
if ((middleChild = fork() < 0))
{
perror("Middle child's fork failed");
exit(EXIT_FAILURE);
}
printf("middleChild pid = %d\n", getpid());
if (middleChild == 0)
{
if (dup2(fildes[readDes], STDIN_FILENO) < 0)
{
perror("Middle child's dup2 on reading failed");
exit(EXIT_FAILURE);
}
if (dup2(fildes[writeDes], STDOUT_FILENO) < 0)
{
perror("Middle child's dup2 on writing failed");
exit(EXIT_FAILURE);
}
for (i = 0; i < 4; i++)
{
if (close(fildes[i]) < 0)
{
perror("close failed");
exit(EXIT_FAILURE);
}
}
readDes += 2;
writeDes += 2;
if (execvp(ptrArray[pipeArray[0]], ptrArray + pipeArray[0]) < 0)
{
perror("Middle child's execvp failed");
exit(EXIT_FAILURE);
}
}
else
{
fflush(stdout);
if ((lastChild = fork() < 0))
{
perror("Last child's fork failed");
exit(EXIT_FAILURE);
}
printf("lastChild pid = %d\n", getpid());
if (lastChild == 0)
{
if (dup2(fildes[readDes], STDOUT_FILENO) < 0)
{
perror("Last child's dup2 on reading failed");
exit(EXIT_FAILURE);
}
if (lastChildFlag == 1)
{
outFD = open(outArray, O_CREAT | O_RDWR, 0400 | 0200);
if (dup2(outFD, STDOUT_FILENO) < 0)
{
perror("Last child's > dup2 failed");
exit(EXIT_FAILURE);
}
}
for (i = 0; i < 4; i++)
{
if (close(fildes[i]) < 0)
{
perror("close failed");
exit(EXIT_FAILURE);
}
}
if (lastChildFlag == 1)
{
greaterthanSign = 0;
lastChildFlag = 0;
if (close(outFD) < 0)
{
perror("close on outFD failed");
exit(EXIT_FAILURE);
}
}
printf("Execvp last child\n");
if (execvp(ptrArray[pipeArray[1]], ptrArray + pipeArray[1]) < 0)
{
perror("Last child's execvp failed");
exit(EXIT_FAILURE);
}
printf("Last child execvp finished\n");
}
}
}
// Only the parent gets here
printf("Only the parent should be here\n");
printf("My pid is %d\n", getpid());
for (i = 0; i < 4; i++)
{
if (close(fildes[i]) < 0)
{
perror("close failed");
exit(EXIT_FAILURE);
}
}
for (;;)
{
pid_t pid;
if (pid = wait(NULL) < 0)
{
perror("wait failed");
exit(EXIT_FAILURE);
}
if (pid == lastChild)
{
printf("Parent is waiting for lastChild\n");
break;
}
}
printf("Parent finished waiting. Returning...\n");
return 0;
}
I did pipe(fildes) before any fork, so that all children and a parent have their copy. Therefore, I must close all file descriptors in each child (after dup2 but before execvp) and the parent. The parent will then wait until it gets the pid of lastChild.
With a lot of printf statements, I have found that no child does the dup2() command (except for dup2(inFD...) and dup2(outFD...) when the flags are appropriate). There is also no error printed.
I printed out my (char) newargv[] and my (int) pipeArray[] and they contain the correct values. It seems to be just the dup2 problem, and I have absolutely no idea what's going wrong with it.
I made a simple text file called test2 containing
ls | sort | cat someString
Where someString is just a file with some text. With all the print statements in the pipeHandler() function my output is:
EDIT: I fixed a couple typos I had. I forgot to lace an extra set of parenthesis on 3 ifs, if ((firstChild = fork()0 < 0)
I now have an infinite loop as the parent is waiting for the lastChild's pid. Here's the output:
Initializing pipes
numpipes = 2
pipe initializing; i is 0
pipe initializing; i is 2
firstChild pid = 20521
firstChild pid = 20522
doing dup2 fildes[writeDes]
middleChild pid = 20521
middleChild pid = 20523
lastChild pid = 20521
Only the parent should be here
My pid is 20521
lastChild pid = 20524
<infinite loop>
I'm still clueless though as to what's going on or what's potentially stopping the child.
#MarkPlotnick you're right! It's not that dup2 isn't executing or anything. Because I did dup2(fildes[1], STDOUT_FILENO), all print statements will be piped.
I fixed the typo mentioned as well. I tried my teacher's test file
< input1 cat|>your.outputc tr a-z A-Z | tr \ q
Which should result with a file called your.outputc. It does, and the contents are input1 with the effects of tr. However, I also have the printf statements at the top of this file.
I assumed the dup2 wasn't working because no printf statement followed, unlike it did in dup2(inFD, STDIN_FILENO), but that's probably because it was STDIN.
I'm writing a simple shell and I'd like to change my program to add the possibility of multiple pipe commands like "echo foo | cat | cat | cat | cat | wc". I have written for two commands but for multiple i can't.
Here is the source code of my program:
if (pid == 0) // in the child process
{
for (i = 0; i < command; i++) // for each cmd
{
if (argv[i][0] == '|')
{
j = i;
}
}
if (j > 0)
{
if (pipe(p))
{
fprintf(stderr, "pipe");
exit(1);
}
argv[j] = NULL;
if (fork() == 0) // child
{
j = -1;
close(p[0]);
dup2(p[1],1);
close(p[1]);
}
// parent
close(p[1]);
dup2(p[0], 0);
close(p[0]);
}
for (i = 0; dirs[i] != 0; i++)
{
snprintf(pathname, sizeof(pathname), "%s/%s", dirs[i], argv[j+1]);
execv(pathname, &argv[j+1]);
}
}
else
{
while (wait(0) != pid) // parent: wait child
}
Thank you in advance for help.
I come with an example of what you are trying to do. I use constants as commands, I leave the command line parsing to you.
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>
static char *my_command0[] = {"cat", "stackoverflow.c", NULL};
static char *my_command1[] = {"grep", "return", NULL};
static char *my_command2[] = {"sed", "s/^ *//g", NULL};
static char **const my_commands[] = {
my_command0,
my_command1,
my_command2,
NULL
};
int create_sub_process(char *const command[], int input_stream)
{
int pipefd[2] = {-1, -1};
pid_t fk;
if (pipe(pipefd) < 0)
{
perror("pipe");
close(input_stream);
return -1;
}
if ((fk = fork()) < 0)
{
perror("fork");
close(pipefd[0]);
close(pipefd[1]);
close(input_stream);
return -1;
}
if (fk == 0)
{
close(pipefd[0]);
close(0);
dup(input_stream);
close(input_stream);
close(1);
dup(pipefd[1]);
close(pipefd[1]);
execvp(command[0], command);
perror("execvp");
exit(1);
}
close(input_stream);
close(pipefd[1]);
return pipefd[0];
}
int main()
{
int fd = dup(0);
for (int i = 0; my_commands[i] != NULL; i++)
{
fd = create_sub_process(my_commands[i], fd); // replace my_commands[i] by whatever you need to execute - check: man execvp
if (fd < 0)
{
exit(1);
}
}
// Also adapt the following lines to whatever you want to do with last child results
close(0);
dup(fd);
close(fd);
execlp("cat", "cat", (char *)NULL);
perror("execlp");
return 1;
}
create_sub_process() creates a pipe and creates a sub process to execute given command, taking inputs from given input stream and sending output to the stream it returns to parent.
I'm trying to implement a very small shell of my own. I have to be able to handle pipes, like
ls -l | wc -l
but only for two programs at a time. Right now, I have this:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#define BUFFER_SIZE 256
#define NO_PARAMS 32
void split_string(char **params, char *string){
char *arg;
int i;
arg = strtok(string, " ");
params[0] = arg;
i = 1;
while(arg != NULL){
arg = strtok(NULL, " ");
params[i] = arg;
i++;
}
}
int main(int argc, char **argv){
char string[BUFFER_SIZE];
char *prog1, *prog2;
int i, err;
int fd[2];
pid_t pid1, pid2;
size_t buffer = BUFFER_SIZE;
char *params1[NO_PARAMS], *params2[NO_PARAMS];
int pipe_exists = 0;
memset(string,0,buffer);
while(1){
/*Read command*/
fgets(string, BUFFER_SIZE-1, stdin);
if(string == NULL){
perror("Error reading input:\n");
exit(1);
}
/*replace linefeed character with end of line character*/
for(i=0;i<BUFFER_SIZE;i++){
if(string[i] == 10){
string[i] = 0;
}
}
/*check if command is "exit"*/
if(strcmp(string,"exit") == 0){
return 0;
}
/*split command into different program calls*/
prog1 = strtok(string, "|");
prog2 = strtok(NULL,"\0");
if(prog2 != NULL){
pipe_exists = 1;
printf("PIPE!\n");
err = pipe(fd);
if(err<0){
perror("Error creating pipe:\n");
exit(1);
}
}
/*split string into arguments*/
split_string(params1, prog1);
if(pipe_exists){
split_string(params2, prog2);
}
/*fork child process*/
pid1 = fork();
if(pid1==0){ /*child 1*/
if(pipe_exists){
close(fd[0]); /*close read-end*/
err = dup2(fd[1], 1);
if(err<0){
perror("Error with dup in child 1!\n");
exit(1);
}
}
execvp(params1[0],params1);
perror("Error calling exec()!\n");
exit(1);
}else{ /*parent*/
if(pipe_exists){
pid2 = fork();
if(pid2==0){ /*child 2*/
close(fd[1]); /*close pipe write-end*/
err = dup2(fd[0], 0);
if(err<0){
perror("Error with dup in child 2!\n");
exit(1);
}
execvp(params2[0],params2);
perror("Error calling exec()!\n");
exit(1);
}else{ /*parent with 2 children*/
waitpid(pid1,0,0);
waitpid(pid2,0,0);
}
}else{ /*parent with 1 child*/
waitpid(pid1,0,0);
}
}
}
}
Right now, it'll handle single commands fine, but when I input something like the command above, nothing happens!
Thanks!
Oh! I've already figured it out. I had to close the pipe in the parent program as well :)
To start with, you should loop as long as you find the pipe character. Then you need to create a pipe for each "piping".
Real shells usually forks and exec itself for each command in the pipeline. This is so it should be able to handle internal commands.
There are 3 main parts in a command with pipes.
The begining, that takes stdin and pipes its output something |
The middle, optionnal or repeated at will with two pipes | something |
The end, that outputs to stdout | something
Then use three functions, one for each of those:
#define PIPE_INPUT 0
#define PIPE_OUTPUT 1
execute_pipe_start(t_cmdlist *commands)
{
int pid;
int fd[2];
if (!commands)
return;
if (commands->next)
{
if (pipe(fd) < 0)
{
perror("pipe failed");
exit(1);
}
pid = fork();
if (!pid)
{
close(fd[PIPE_INPUT]);
if (dup2(fd[PIPE_OUTPUT, 1) < 0)
{
perror("dup2 failed");
exit(1);
}
parse_and_exec_cmd(commands->cmd);
}
else
{
waitpid(...); //what you put here is a bit tricky because
//some shells like tcsh will execute all
//commands at the same time (try cat | cat | cat | cat)
}
if (commands->next->next != null) //If you have 2 commands in line there is a middle
execute_pipe_middle(commands->next, fd);
else // no middle
execute_pipe_end(commands->next, fd);
}
else
parse_and_exec_cmd(commands->cmd);
}
execute_pipe_middle(t_cmdlist *commands, int fd_before[2])
{
int pid;
int fd_after[2];
if (pipe(fd_after) < 0)
{
perror("pipe failed");
exit(1);
}
pid = fork();
if (!pid)
{
close(fd_before[PIPE_OUTPUT]);
close(fd_after[PIPE_INPUT]);
if (dup2(fd_after[PIPE_OUTPUT, 1) < 0)
{
perror("dup2 failed");
exit(1);
}
if (dup2(fd_before[PIPE_INPUT, 0) < 0)
{
perror("dup2 failed");
exit(1);
}
parse_and_exec_cmd(commands->cmd);
}
else
waitpid(...);
if (commands->next->next != null) //More than two following commands : a middle again
execute_pipe_middle(commands->next, fd_after);
else // No more repetition
execute_pipe_end(commands->next, fd_after);
}
execute_pipe_end(t_cmdlist *commands, int fd_before[2])
{
int pid;
if (!commands)
return;
if (commands->next)
{
pid = fork();
if (!pid)
{
close(fd_before[PIPE_OUTPUT]);
if (dup2(fd_before[PIPE_INPUT, 0) < 0)
{
perror("dup2 failed");
exit(1);
}
parse_and_exec_cmd(commands->cmd);
}
else
waitpid(...);
}
}