Im trying to replicate the multiple pipes execution of bash, and Im getting no output from terminal. First I create the number of pipes that my program will need depending on the number of commands, then in a while loop I launch each process executing each command in the child proccess and working with the correct FDs of pipes, in case it is first command, I dont read from any pipe, on the other hand, in case it is last command, I dont redirect the output to any pipe. I printed each fd to check if my program is doing well and it looks everything okay but as I said I dont get any output of the last commands.
Here is a snippet of my code about how I deal with the process and the pipes, if you need something more just let me know.
int i;
int *pipes;
i = 0;
pipes = ft_calloc(sizeof(int), gdata->n_pipes * 2);
while (i < gdata->n_pipes)
{
pipe(pipes + (i * 2));
i++;
}
int cc = 0; //command count
int r = 0;
int m;
pid_t pid;
while (gdata->cmds[r]) //double pointer array that contains the commands to execute ex: [["ls -l"], ["grep i"], ["wc -l"]]
{
pid = fork();
if (pid < 0)
{
perror("Fork: ");
exit(EXIT_FAILURE);
}
if (pid == 0)
{
if (r > 0)
{
if (dup2(pipes[(cc - 1) * 2], STDIN_FILENO) < 0)
{
perror("dup");
exit(EXIT_FAILURE);
}
}
if (r < gdata->commands - 1)
{
if (dup2(pipes[cc * 2 + 1], STDOUT_FILENO) < 0)
{
perror("dup");
exit(EXIT_FAILURE);
}
}
int k = 0;
while (k < gdata->n_pipes * 2)
{
close(pipes[k]);
k++;
}
handle_path(gdata->cmds[r], gdata->envp); // function that calls execve to execute commands
}
waitpid(pid, &m, 0);
r++;
cc++;
}
int y = 0;
while (y < gdata->n_pipes * 2)
{
close(pipes[y]);
y++;
}
Do you have any idea what I'm doing wrong, Thanks for the help!
I'd like to launch the command wc -c on every argument. I used pipes to communicate between the processors.
Example: ./test cat data.txt : grep left : grep And.
this should be launched on the terminal and execute wc -c on every command and save it in a file, and the child continue its thread.
this is my where I'm stuck: (without error handling for simpler code)
int out = open("counter.txt", O_RDWR|O_CREAT|O_APPEND, 0777);
for (int j = 0; j < numPipes + 1; j++){
pid = fork();
if(pid == 0) { // child
//if not first command
if(j != 0)
dup2(pipes[(j-1) * 2], 0);
//if not last command
if(j != numPipes)
dup2(pipes[j*2 + 1], 1);
//int out = open("count.txt", O_RDWR|O_CREAT|O_APPEND, 0777);
//dup2(1, out);
for(int i = 0; i < 2*numPipes; i++){
close(pipes[i]);
}
if( execvp(argv[indice_debut[j]], argv + indice_debut[j]) < 0 ){
perror(argv[indice_debut[j]]);
return 1;
}
} else if(pid < 0){
perror("error");
return 1;
}
}
//parent
//!!! is this part supposed to be in parent or child ?
//char* args[] = {"wc",argv[indice_debut[numPipes+1]],NULL};
//execvp("wc","-c",args);
//dup2(out, pipes[3]);
...
What can i do so far ? I'm stuck
I'm making my own UNIX shell and I'm trying to implement support for (multiple) pipe commands. As you can see below, I'm creating the necessary file descriptors for each pipe and then creating a child process for each command using fork(). Inside the loop, I'm using dup2 in order to copy the appropriate file descriptors inherited by the parent and redirect my input/output. However, when I test it using echo tst | cw -w , the Failed to write output for the next command.. block is triggered. I don't understand what's causing this issue here. I've created the pipes with their file descriptors, I've added the appropriate checks for the first and last commands, I'm closing the inherited pipes before executing the command, etc.
Please help me pinpoint where my logic is faulty in the below code:
void execPipedCommands(char *input, char **commands)
{
int numOfPipes = countCharOccurences(input, '|');
int fds[2 * numOfPipes]; // two file descriptors per pipe needed for interprocess communication
int i;
// initialize all pipes and store their respective fds in the appropriate place in the array
for (i = 0; i < 2 * numOfPipes; i++)
{
if (pipe(fds + 2 * i) == -1)
{
printf("Failed to create file descriptors for pipe commands!\n");
exit(EXIT_FAILURE);
}
}
for (i = 0; i < numOfPipes+1; i++)
{
if (commands[i] == NULL)
break;
pid_t cpid;
char *args[MAX_NUM_OF_COMMAND_WORDS] = {
NULL,
};
parseCommandWords(commands[i], args);
cpid = fork(); // start a child process
if (cpid == -1)
{
printf("Failed to fork..\n");
exit(EXIT_FAILURE);
}
if (cpid == 0)
{ // child process is executing
if (i != 0)
{ // if this is not the first command in the chain
// duplicate the file descriptor to read from the previous command's output
if (dup2(fds[(i - 1) * 2], STDIN_FILENO) < 0)
{
printf("Failed to read input from previous command..\n");
exit(EXIT_FAILURE);
}
}
// if this is not the last command in the chain
if (i != numOfPipes+1 && commands[i + 1] != NULL)
{
// duplicate write file descriptor in order to output to the next command
if (dup2(fds[(i * 2 + 1)], STDOUT_FILENO) < 0)
{
printf("Failed to write output for the next command..\n");
exit(EXIT_FAILURE);
}
}
// close the pipes
int j;
for (j = 0; j < numOfPipes * 2; j++)
{ // close all copies of the file descriptors
close(fds[j]);
}
// execute command
if (execvp(commands[i], args) < 0)
{
printf("Failed to execute given command..");
return;
}
}
}
// parent closes all original file descriptors
for (i = 0; i < numOfPipes * 2; i++)
{
close(fds[i]);
}
// parent waits for all child processes to finish
for (i = 0; i < numOfPipes + 1; i++)
wait(0);
}
The input argument is simply the line that is read from the command line and the commands argument contains all the commands in the line read. So for the example I gave earlier, it would contain:
commands[0] = echo tst
commands[1] = cw -w
I am creating a custom shell and currently working on getting multiple piping to work. Eg, ls -al | wc -l returns the number of all file directories in current directory. I am closely following the "solution code" at this link.
Here is my implementation, with very similar command input structure:
// every command is a
// struct command
// with their arguments attached
struct command
{
char **cmd; // argument list for execvp(eg: {"ls", "-al"})
int numArgs; // number of total arguments in current line
};
// my implementation of multiple pipes based on link supplied with slight modifications
void runPipedCommands(struct command *commands)
{
// this code just counds the number of pipes in commands
int numPipes = 0;
for (int i = 0; i < commands->numArgs; i++)
{
if (!strcmp(commands[i].cmd[0], "|"))
numPipes++;
}
printf("number of pipes: %d\n", numPipes);
int status;
int i = 0;
pid_t pid;
int pipefds[2 * numPipes];
// create the pipes
for (i = 0; i < (numPipes); i++)
{
if (pipe(pipefds + i * 2) < 0)
{
perror("couldn't pipe");
exit(EXIT_FAILURE);
}
}
int j = 0;
int c = 0;
while (commands[c].cmd)
{
// skip the pipe operators
if (!strcmp(commands[c].cmd[0], "|"))
c++;
pid = fork();
if (pid == 0)
{
printf("cmd to execute: %s\n", commands[c].cmd[0]);
//if not last command
if (commands[c + 1].cmd[0])
{
// everyone even fd gets this, set stdout to write
// end of pipe
if (dup2(pipefds[j + 1], STDOUT_FILENO) < 0)
{
perror("dup2");
exit(EXIT_FAILURE);
}
}
else
{
exit(EXIT_SUCCESS);
}
//if not first command&& j!= 2*numPipes
if (j != 0)
{
// every odd fd gets this, set stdin to
// read end of pipe
if (dup2(pipefds[j - 2], STDIN_FILENO) < 0)
{
perror(" dup2");
exit(EXIT_FAILURE);
}
}
// close dup2ed fds
for (i = 0; i < 2 * numPipes; i++)
{
close(pipefds[i]);
}
// execvp
if (execvp(commands[c].cmd[0], commands[c].cmd) < 0)
{
perror(commands[c].cmd[0]);
exit(EXIT_FAILURE);
}
}
else if (pid < 0)
{
perror("error");
exit(EXIT_FAILURE);
}
c++;
j += 2;
}
/**Parent closes the pipes and wait for children*/
for (i = 0; i < 2 * numPipes; i++)
{
close(pipefds[i]);
}
for (i = 0; i < numPipes + 1; i++)
wait(&status);
}
// my main function with my test input:
int main(int argc, char *argv[])
{
// my parseW function simply returns a list of struct commands
struct command *results = parseW("ls -al | wc -l");
runPipedCommands(results);
return 0;
}
The above code with parseW omitted results in a terminal output of:
Everything looks correct in the execution of the pipes, and I've gone over the file descriptors (there are only 2 to account for in my specific test case). I'm not sure what I'm doing wrong.
Without posting parseW(), the content of a commands[] array is unknown.
Within runPipedCommands() while loop, there are 3 areas that need revisions:
Provided a commands[x].cmd[] may privately contain a pipe, there's nothing to execute
and no need to fork() and exit; instead add a continue :
// skip the pipe operators
if (!strcmp(commands[c].cmd[0], "|")) {
c++;
continue;
}
After a fork(), the child process tries to test the next element of
commands[], assumes that member .cmd is non-NULL and deferences
with .cmd[0]; provided the last element of commands[] contains a NULL .cmd,
the child would crash here:
if (commands[c + 1].cmd[0])
instead, update the test without the dereference:
if (commands[c + 1].cmd)
Related with the added continue above, when this updated .cmd test is false, there's no need to exit and prevent
the child from reaching the execvp(); delete this block:
else {
exit(EXIT_SUCCESS);
}
Also: when the parent calls wait(), the status can be checked with
macros and indicate whether the child terminated normally, see a
wait manual
I'm trying to implement multiple pipes in my shell in C. I found a tutorial on this website and the function I made is based on this example. Here's the function
void executePipes(cmdLine* command, char* userInput) {
int numPipes = 2 * countPipes(userInput);
int status;
int i = 0, j = 0;
int pipefds[numPipes];
for(i = 0; i < (numPipes); i += 2)
pipe(pipefds + i);
while(command != NULL) {
if(fork() == 0){
if(j != 0){
dup2(pipefds[j - 2], 0);
}
if(command->next != NULL){
dup2(pipefds[j + 1], 1);
}
for(i = 0; i < (numPipes); i++){
close(pipefds[i]);
}
if( execvp(*command->arguments, command->arguments) < 0 ){
perror(*command->arguments);
exit(EXIT_FAILURE);
}
}
else{
if(command != NULL)
command = command->next;
j += 2;
for(i = 0; i < (numPipes ); i++){
close(pipefds[i]);
}
while(waitpid(0,0,0) < 0);
}
}
}
After executing it and typing a command like for example ls | grep bin, the shell just hangs there and doesn't output any result. I made sure I closed all pipes. But it just hangs there. I thought that it was the waitpid that's was the problem. I removed the waitpid and after executing I get no results. What did I do wrong? Thanks.
Added code:
void runPipedCommands(cmdLine* command, char* userInput) {
int numPipes = countPipes(userInput);
int status;
int i = 0, j = 0;
pid_t pid;
int pipefds[2*numPipes];
for(i = 0; i < 2*(numPipes); i++){
if(pipe(pipefds + i*2) < 0) {
perror("pipe");
exit(EXIT_FAILURE);
}
}
while(command) {
pid = fork();
if(pid == 0) {
//if not first command
if(j != 0){
if(dup2(pipefds[(j-1) * 2], 0) < 0){
perror(" dup2");///j-2 0 j+1 1
exit(EXIT_FAILURE);
//printf("j != 0 dup(pipefd[%d], 0])\n", j-2);
}
//if not last command
if(command->next){
if(dup2(pipefds[j * 2 + 1], 1) < 0){
perror("dup2");
exit(EXIT_FAILURE);
}
}
for(i = 0; i < 2*numPipes; i++){
close(pipefds[i]);
}
if( execvp(*command->arguments, command->arguments) < 0 ){
perror(*command->arguments);
exit(EXIT_FAILURE);
}
} else if(pid < 0){
perror("error");
exit(EXIT_FAILURE);
}
command = command->next;
j++;
}
for(i = 0; i < 2 * numPipes; i++){
close(pipefds[i]);
puts("closed pipe in parent");
}
while(waitpid(0,0,0) <= 0);
}
}
I believe the issue here is that your waiting and closing inside the same loop that's creating children. On the first iteration, the child will exec (which will destroy the child program, overwriting it with your first command) and then the parent closes all of its file descriptors and waits for the child to finish before it iterates on to creating the next child. At that point, since the parent has closed all of its pipes, any further children will have nothing to write to or read from. Since you are not checking for the success of your dup2 calls, this is going un-noticed.
If you want to keep the same loop structure, you'll need to make sure the parent only closes the file descriptors that have already been used, but leaves those that haven't alone. Then, after all children have been created, your parent can wait.
EDIT: I mixed up the parent/child in my answer, but the reasoning still holds: the process that goes on to fork again closes all of its copies of the pipes, so any process after the first fork will not have valid file descriptors to read to/write from.
pseudo code, using an array of pipes created up-front:
/* parent creates all needed pipes at the start */
for( i = 0; i < num-pipes; i++ ){
if( pipe(pipefds + i*2) < 0 ){
perror and exit
}
}
commandc = 0
while( command ){
pid = fork()
if( pid == 0 ){
/* child gets input from the previous command,
if it's not the first command */
if( not first command ){
if( dup2(pipefds[(commandc-1)*2], 0) < ){
perror and exit
}
}
/* child outputs to next command, if it's not
the last command */
if( not last command ){
if( dup2(pipefds[commandc*2+1], 1) < 0 ){
perror and exit
}
}
close all pipe-fds
execvp
perror and exit
} else if( pid < 0 ){
perror and exit
}
cmd = cmd->next
commandc++
}
/* parent closes all of its copies at the end */
for( i = 0; i < 2 * num-pipes; i++ ){
close( pipefds[i] );
}
In this code, the original parent process creates a child for each command and therefore survives the entire ordeal. The children check to see if they should get their input from the previous command and if they should send their output to the next command. Then they close all of their copies of the pipe file descriptors and then exec. The parent doesn't do anything but fork until it's created a child for each command. It then closes all of its copies of the descriptors and can go on to wait.
Creating all of the pipes you need first, and then managing them in the loop, is tricky and requires some array arithmetic. The goal, though, looks like this:
cmd0 cmd1 cmd2 cmd3 cmd4
pipe0 pipe1 pipe2 pipe3
[0,1] [2,3] [4,5] [6,7]
Realizing that, at any given time, you only need two sets of pipes (the pipe to the previous command and the pipe to the next command) will simplify your code and make it a little more robust. Ephemient gives pseudo-code for this here. His code is cleaner, because the parent and child do not have to do unnecessary looping to close un-needed file descriptors and because the parent can easily close its copies of the file descriptors immediately after the fork.
As a side note: you should always check the return values of pipe, dup2, fork, and exec.
EDIT 2: typo in pseudo code. OP: num-pipes would be the number of pipes. E.g., "ls | grep foo | sort -r" would have 2 pipes.
Here's the correct functioning code
void runPipedCommands(cmdLine* command, char* userInput) {
int numPipes = countPipes(userInput);
int status;
int i = 0;
pid_t pid;
int pipefds[2*numPipes];
for(i = 0; i < (numPipes); i++){
if(pipe(pipefds + i*2) < 0) {
perror("couldn't pipe");
exit(EXIT_FAILURE);
}
}
int j = 0;
while(command) {
pid = fork();
if(pid == 0) {
//if not last command
if(command->next){
if(dup2(pipefds[j + 1], 1) < 0){
perror("dup2");
exit(EXIT_FAILURE);
}
}
//if not first command&& j!= 2*numPipes
if(j != 0 ){
if(dup2(pipefds[j-2], 0) < 0){
perror(" dup2");///j-2 0 j+1 1
exit(EXIT_FAILURE);
}
}
for(i = 0; i < 2*numPipes; i++){
close(pipefds[i]);
}
if( execvp(*command->arguments, command->arguments) < 0 ){
perror(*command->arguments);
exit(EXIT_FAILURE);
}
} else if(pid < 0){
perror("error");
exit(EXIT_FAILURE);
}
command = command->next;
j+=2;
}
/**Parent closes the pipes and wait for children*/
for(i = 0; i < 2 * numPipes; i++){
close(pipefds[i]);
}
for(i = 0; i < numPipes + 1; i++)
wait(&status);
}
The (shortened) relevant code is:
if(fork() == 0){
// do child stuff here
....
}
else{
// do parent stuff here
if(command != NULL)
command = command->next;
j += 2;
for(i = 0; i < (numPipes ); i++){
close(pipefds[i]);
}
while(waitpid(0,0,0) < 0);
}
Which means the parent (controlling) process does this:
fork
close all pipes
wait for child process
next loop / child
But it should be something like this:
fork
fork
fork
close all pipes (everything should have been duped now)
wait for childs
You only need two pipes alternating like below:
typedef int io[2];
extern int I; //piped command current index
extern int pipe_count; //count of '|'
#define CURRENT 0
#define PREVIOUS 1
#define READ 0
#define WRITE 1
#define is_last_command (I == pipe_count)
bool connect(io pipes[2])
{
if (pipe_count)
{
if (is_last_command || I != 0)
dup2(pipes[PREVIOUS][READ], STDIN_FILENO);
if (I == 0 || !is_last_command)
dup2(pipes[CURRENT][WRITE], STDOUT_FILENO);
}
return (true);
}
void close_(io pipes[2])
{
if (pipe_count)
{
if (is_last_command || I != 0)
close(pipes[PREVIOUS][READ]);
if (I == 0 || !is_last_command)
close(pipes[CURRENT][WRITE]);
}
}
void alternate(int **pipes)
{
int *pipe_current;
pipe_current = pipes[CURRENT];
pipes[CURRENT] = pipes[PREVIOUS];
pipes[PREVIOUS] = pipe_current;
}
Example usage:
#define ERROR -1
#define CHILD 0
void execute(char **command)
{
static io pipes[2];
if (pipe_count && pipe(pipes[CURRENT]) == ERROR)
exit_error("pipe");
if (fork()==CHILD && connect(pipes))
{
execvp(command[0], command);
_exit(EXIT_FAILURE);
}
while (wait(NULL) >= 0);
close_(pipes);
alternate((int **)pipes);
}
static void run(char ***commands)
{
for (I = 0; commands[I]; I++)
if (*commands[I])
execute(commands[I]);
}
I'll leave a link to a full working code for someone who needs it.
Building upon the idea of using a maximum of two pipes at a given time mentioned by Christopher Neylan, I put together pseudocode for n-pipes. args is an array of character pointers of size 'args_size' which is a global variable.
// MULTIPLE PIPES
// Test case: char *args[] = {"ls", "-l", "|", "head", "|", "tail", "-4",
0};// "|", "grep", "Txt", 0};
enum fileEnd{READ, WRITE};
void multiple pipes( char** args){
pid_t cpid;
// declare pipes
int pipeA[2]
int pipeB[2]
// I have done getNumberofpipes
int numPipes = getNumberOfPipes;
int command_num = numPipes+1;
// holds sub array of args
// which is a statement to execute
// for example: cmd = {"ls", "-l", NULL}
char** cmd
// iterate over args
for(i = 0; i < args_size; i++){
//
// strip subarray from main array
// cmd 1 | cmd 2 | cmd3 => cmd
// cmd = {"ls", "-l", NULL}
//Open/reopen one pipe
//if i is even open pipeB
if(i % 2) pipe(pipeB);
//if i is odd open pipeA
else pipe(pipeA);
switch(cpid = fork(){
case -1: error forking
case 0: // child process
childprocess(i);
default: // parent process
parentprocess(i, cpid);
}
}
}
// parent pipes must be closed in parent
void parentprocess(int i, pid_t cpid){
// if first command
if(i == 0)
close(pipeB[WRITE]);
// if last command close WRITE
else if (i == numPipes){
// if i is even close pipeB[WRITE]
// if i is odd close pipeA[WRITE]
}
// otherwise if in middle close READ and WRITE
// for appropriate pipes
// if i is even
close(pipeA[READ])
close(pipeB[WRITE])
// if i is odd
close(pipeB[READ])
close(pipeA[WRITE])
}
int returnvalue, status;
waitpid(cpid, returnvalue, status);
}
void childprocess(int i){
// if in first command
if(i == 0)
dup2(pipeB[WRITE], STDOUT_FILENO);
//if in last command change stdin for
// the necessary pipe. Don't touch stdout -
// stdout goes to shell
else if( numPipes == i){
// if i is even
dup2(pipeB[READ], STDIN_FILENO)
//if i is odd
dup2(pipeA[READ], STDIN_FILENO);
}
// otherwise, we are in middle command where
// both pipes are used.
else{
// if i is even
dup2(pipeA[READ], STDIN_FILENO)
dupe(pipeB[WRITE], STDOUT_FILENO)
// if i is odd
dup2(pipeB[READ], STDIN_FILENO)
dup2(pipeA[WRITE], STDOUT_FILENO)
}
// execute command for this iteration
// check for errors!!
// The exec() functions only return if an error has occurred. The return value is -1, and errno is set to indicate the error.
if(exec(cmd, cmd) < 0)
printf("Oh dear, something went wrong with read()! %s\n", strerror(errno));
}
}
Basically what you wanna do is a recursive function where the child executes the first command and the parent executes the second one if no other commands are left or calls the function again.