#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>
#define UNUSED(x) (void)(x)
void execute_pipeline(
char *commands[],
char **commands_args[],
int number_commands )
{
int pipe_fd[number_commands - 1][2];
// Create pipes in a for loop
for (int i = 0; i < number_commands - 1; i++)
{
if (pipe(pipe_fd[i]) == -1)
{
perror("pipe");
exit(EXIT_FAILURE);
}
}
// Parent forks children in a for loop
for (int i = 0; i < number_commands; i++)
{
pid_t pid = fork();
int status;
if (pid == 0) // Child process
{
// If not the first command, redirect stdin to read end of previous pipe
if (i > 0)
{
if (dup2(pipe_fd[i-1][0], STDIN_FILENO) == -1)
{
perror("dup2");
exit(EXIT_FAILURE);
}
}
// If not the last command, redirect stdout to write end of pipe
if (i < number_commands - 1)
{
if (dup2(pipe_fd[i][1], STDOUT_FILENO) == -1)
{
perror("dup2");
exit(EXIT_FAILURE);
}
}
// Close all pipe ends
for (int j = 0; j < number_commands - 1; j++)
{
close(pipe_fd[j][0]);
close(pipe_fd[j][1]);
}
// Execute the command with arguments
execvp(commands[i], commands_args[i]);
perror("execvp");
exit(EXIT_FAILURE);
}
else if (pid < 0) {
// error in fork
perror("fork");
exit(EXIT_FAILURE);
}
else
{
// parent process
if (i > 0) {
close(pipe_fd[i-1][0]); // close read end of previous pipe
close(pipe_fd[i-1][1]); // close write end of previous pipe
}
if (i == number_commands - 1)
{
// last command, wait for all children to finish
for (int j = 0; j < number_commands; j++)
{
wait(&status);
}
}
}
}
// Close all pipe ends
for (int i = 0; i < number_commands - 1; i++)
{
close(pipe_fd[i][0]);
close(pipe_fd[i][1]);
}
}
int main()
{
// List of available commands on a command line
// You can add to it if you like
char *commands[] =
{
"ls",
"grep",
"sort",
"uniq",
"head",
"tail",
"wc",
"ps",
"who",
"find",
"xargs",
"awk",
"cut",
NULL};
// Select commands from commands[]
char *pipeline_commands[] = {commands[0], commands[1]};
// Set arguments
char *cmd1_args[] = {commands[0], NULL};
char *cmd2_args[] = {commands[1], "c", NULL};
// Pack arguments into an array
char **pipeline_args[] = {cmd1_args, cmd2_args};
int number_commands = sizeof(pipeline_commands) / sizeof(pipeline_commands[0]);
printf("Executing the following pipeline:\n");
for (int i = 0; i < number_commands; i++)
{
printf("%s", pipeline_commands[i]);
for (int j = 1; pipeline_args[i][j]!= NULL; j++)
{
printf(" %s", pipeline_args[i][j]);
}
if (i != number_commands - 1)
{
printf(" | ");
}
}
printf("\n");
execute_pipeline( pipeline_commands, pipeline_args, number_commands );
return 0;
}