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.
Related
I'm having the following code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
if (argc != 2) {
fprintf(stderr, "Usage: %s <file>\n", argv[0]);
exit(-1);
}
int pfd[2]; // pipe
int pfd2[2]; // pipe2
pid_t pid; // child
if (pipe(pfd) < 0) {
fprintf(stderr, "error.\n");
exit(-1);
}
if (pipe(pfd2) < 0) {
fprintf(stderr, "error.\n");
exit(-1);
}
if ((pid = fork()) < 0) {
fprintf(stderr, "error.\n");
exit(-1);
}
// child process
if (pid == 0) {
close(pfd[1]);
int v[100] = {0};
int k = 0;
int r;
char buf[128];
while ((r = read(pfd[0], buf, sizeof(buf)))) {
buf[r] = '\0';
// some processing here vor the 'v' variable
}
close(pfd[0]); // close the reading end 1st pipe
dup2(pfd2[1], 1); // redirect
close(pfd2[1]);
execlp("bash", "bash", "script.sh", v, NULL);
exit(-1);
} else {
// parent code
close(pfd2[1]);
dup2(pfd2[0], 0);
char buf[16];
int r = 0;
while ((r = read(pfd2[0], buf, sizeof(buf)))) {
buf[r] = '\0';
// read here from second pipe
}
close(pfd2[0]); // close reading end from 2pnd pipe
close(pfd[0]); // close reading end from 1st pipe
dup2(pfd[1], 1); // redirect
execlp("cat", "cat", argv[1], NULL); // exec 'cat' on the given arg
exit(-1);
}
return 0;
}
That respect the following flow: the parent process executes the 'cat' command on the given arg file, then pass it to the child -> the child does some processing and then stores values in the 'v' variable -> then executes the 'script.sh' script with the values taken from the 'v' variable and then passes the output to the parent which will print to stdout the output based on the result from the child.
I'm unsure where should the code for the 2nd pipe reading should go, I'm pretty sure that's the problem right now.
Can anyone take a look at it and point where the problem is? Thanks!
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;
}
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 trying to make a pipe system for my shell, but it's not working as intended.
void pipes (char *listaCommand[], int end, char **argv)
{
int cont = end;
for (cont;listaCommand[cont]; cont++)
{
if (listaCommand[cont] != NULL)
{
if (!strcmp(listaCommand[cont],"|")){
int pid2, status;
int pipefd[2], ret;
listaCommand[cont] = NULL;
ret = pipe (pipefd);
if (ret < 0) fatal();
/* Now fork. */
pid2 = fork ();
if (pid2 <0) fatal ();
if (pid2 > 0)
{
printf ("P: waiting for child\n");
wait (&status);
close(STDIN_FILENO);
dup(pipefd[0]);
close(pipefd[0]);
close(pipefd[1]);
/*execvp (auxCommand[0], auxCommand);*/
pipes(listaCommand, cont+1, argv);
/*break;*/
}
else
{
close (STDOUT_FILENO);
dup (pipefd[1]);
close (pipefd[1]);
close (pipefd[0]);
}
}
}
}
if (end >= 3)
{
printf("%s \n", listaCommand[end-1]);
}
execvp (listaCommand[end], listaCommand);
printf ("%s: command not found.\n", listaCommand[end]); /* Exec failed. */
exit(EXIT_FAILURE);
}
If I use commands like ls | sort, it works, but if ls has any argument, it doesnt work, because for some reason, listaCommand[cont] where its == "|" is not NULL, so I just get
ls: option -- 'a' invalid.
listaCommand have
[0] = "ls"
[1] = "-al"
[2] = "|"
[3] = "sort"
You don't need to pass the end argument, instead increment the pointer to your command array. You are passing the initial array to the execvp call so it tries to execute ls multiple times. Further, you need a break statement after setting the listaCommand[cont] to NULL because after the iteration cont is incremented. Also I think you need to protect the execvp call so that the parent does not call it after the processing is done.
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#define fatal() exit(1)
void pipes (char *listaCommand[], char **argv)
{
printf("pipes %s\n", listaCommand[0]);
int cont = 0;
for (;listaCommand[cont]; cont++) {
if (listaCommand[cont][0] == '|'){
int pid2, status;
int pipefd[2], ret;
listaCommand[cont] = NULL;
ret = pipe (pipefd);
if (ret < 0) fatal();
/* Now fork. */
pid2 = fork ();
if (pid2 <0) fatal ();
if (pid2 > 0)
{
printf ("P: waiting for child\n");
wait (&status);
close(STDIN_FILENO);
dup(pipefd[0]);
close(pipefd[0]);
close(pipefd[1]);
/*execvp (auxCommand[0], auxCommand);*/
pipes(listaCommand + cont + 1, argv);
/*break;*/
}
else
{
close (STDOUT_FILENO);
dup (pipefd[1]);
close (pipefd[1]);
close (pipefd[0]);
break;
}
}
}
if (listaCommand[0]) {
execvp (listaCommand[0], listaCommand);
printf ("%s: command not found.\n", listaCommand[0]); /* Exec failed. */
exit(EXIT_FAILURE);
}
}
int main() {
char *args[] = { "ls", "-al", "|", "sort", "|" , "tr", "[a-z]", "[A-Z]", 0 };
pipes(args, 0);
return 0;
}
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(...);
}
}