How to implement piping in your own Linux shell? - c

I am writing my own simple shell for Linux in C. I am unable to implement piping without quitting the entire shell. I believe that I need to implement one more fork() to achieve this, but I am not sure where to do this.
My program has a main():
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(void){
char input[256];
char* temp = NULL;
char* args[9];
char* cmd1[5];
char* cmd2[5];
int i, j, pipe_pos;
int fd[2];
while(1){
// set all args to NULL
for (i = 0; i < 9; i++) {
args[i] = NULL;
}
for (i = 0; i < 5; i++) {
cmd1[i] = NULL;
cmd2[i] = NULL;
}
// print message to terminal
printf("My Shell (v2.0):> ");
// get input from user
fgets(input,255,stdin);
// test if input = 'quit'
if (!strncmp(input,"quit",4)) {
// exit program if input = quit
exit(0);
}
else { // continue running program
// initialise counter
i = 0;
// tokenize first word
temp = strtok(input, " \n");
// receive arguments from user input
while ((temp != NULL) && (i < 9)) {
args[i] = temp;
temp = strtok(NULL, " \n");
i++;
}
// find position of "|" (pipe)
j = i - 1;
pipe_pos = 8;
for (i = 0; i < j; i++) {
if (strcmp(args[i],"|") == 0) {
pipe_pos = i;
break;
}
}
// copy 1st command with arguments to new array
for (i = 0; i < pipe_pos; i++) {
cmd1[i] = args[i];
}
// copy 2nd command with arguments to new array
j = 0;
if (pipe_pos != 8) {
for (i = pipe_pos+1; i < 9; i++) {
// copy if array is not full
if (j < 5){
cmd2[j] = args[i];
j++;
} // end if (j < 5)
} // end for
} // end if (pipe_pos != 8) {
// run if program needs piping
if (pipe_pos != 8) {
pipeProcesses(cmd1, cmd2);
}
else {
runProcess(cmd1);
}
} // end else
}// end while
return 0;
}// end main()
A pipingProcesses() function:
void pipeProcesses(char ** cmd1, char ** cmd2){
int fd[2];
pipe(fd);
if (!fork()) {
// close STD_OUT
close(1);
// make STD_OUT same as fd[1]
dup(fd[1]);
// we don't need this
close(fd[0]);
runProcess(cmd1);
exit(0);
} else {
// close STD_IN
close(0);
// make STD_IN same as fd[0]
dup(fd[0]);
// we don't need this
close(fd[1]);
execvp(cmd2[0], cmd2);
}
}
And runProcess() function:
void runProcess(char ** cmd){
int error;
// initialise error flag
error = 0;
if (fork() == 0) {
// child process
error = execvp(cmd[0], cmd);
// print error: unknown command
if (error == -1) {
printf("ERROR: UNKNOWN COMMAND (%s)\n", cmd[0]);
exit(0);
}
}
else {
// parent process
waitpid(0,NULL,0);
}
}
Everything works as should, except that I want the shell to continue running after I have piped two commands. How can I fix this?

You call execvp(cmd2[0], cmd2); in the flow of the main program which replaces your program with cmd2.
You should fork() for that command as well.

Related

Implementing a pipe functionality in shell. In C

In the provided code below, the redirection methods work, however i've been having trouble with getting the pipe function to work.
This is how I'm viewing the flow of the code (at least for the pipe) and correct me if I'm wrong: When the array containing the users command (argv[]) is being traversed and checked for pipe declarations ("|"), for however many ("|") it detects, it will do that number of processes (processes++). It will then clear out the user command for future use, and go through the checks to continue the program.
I will also say I only have processes declared and iterated once, and I wasn't sure if I return it or implement it somewhere else.
Here is what I have so far:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <fcntl.h>
int main()
{
char *path, //shell system path indicator
*argv[20], //user command
buf[80], //will pass user command to argv
n, //user input
*p; //points to buffer
int m, // represents size
status, // checks if ant conditions violate the while loop
inword,
check,
continu;
while(1) //an infinite loop to keep the shell running
{
inword = 0; //set as false
p = buf; // p represents buffer
m = 0; //size of argv array
continu = 0; //set as false
printf("\nshhh> "); //display of shell prompt
while ((n = getchar()) != '\n' || continu) //while user input is not end of line OR
{
if (n == ' ') // if input is a space
{
if (inword) // if 0
{
inword = 0; // remains 0
*p++ = 0; //increment to next position in buffer[80]
}
}
else if (n == '\n') //if user input == end of line
continu = 0; // continue is 0 which breaks the while loop
else if (n == '\\' && !inword) // if user input == '\\' and inword is not 0
continu = 1; // continu is changed to 1 and you stay in while loop
else
{
if (!inword)
{
inword = 1;
argv[m++] = p; //next iteration of user command contains buffer
*p++ = n; //next slot in buffer contains user input
}
else
*p++ = n; //next slot in buffer contains user input
}
}
*p++ = 0; // next slot in buffer is 0
argv[m] = 0; //clear user command
if (strcmp(argv[0],"exit") == 0) //if user command is exit
exit (0); //end program
int processes = 0,
gate1,
gate2,
pipe_end[2],
pipe_id;
if (fork() == 0)// passing command to array
{
for(int iteratr = 0; argv[iteratr] != 0; iteratr++)
{
if(strcmp("|", argv[iteratr]) == 0)// pipe configuration
{
processes++;
argv[iteratr] = 0; //empty array for next user command
pipe(pipe_end);
if (pipe(pipe_end)==-1) // Check for pipe functionality
{
perror("Pipe Failed");
return 1;
}
pipe_id = fork();
if (pipe_id < 0) //Check for fork functionality
{
printf("Fork failed");
return 1;
}
else if(pipe_id == 0)//Child
{
close(1); //close stdout
dup(pipe_end[1]); //copy stdout
close(pipe_end[0]); //close read in
close(pipe_end[1]); //close write out
execvp(argv[0], argv); //execute "pre" read in
}
else //Parent
{
close(0); //close stdin
dup(pipe_end[0]); //copy stdin
close(pipe_end[0]); //close read in
close(pipe_end[1]); //close write out
execvp(argv[0], argv); //execute "sort" read in
}
}
if(strcmp("<", argv[iteratr]) == 0)// Input redirect
{
gate1 = open("input.txt", O_RDONLY);
close(0);
dup(gate1);
close(gate1);
argv[iteratr] = 0;
execvp(argv[0], argv);
}
if(strcmp(">", argv[iteratr]) == 0) // Output redirect
{
gate2 = creat("output.txt", 0750);
close(1);
dup(gate2);
close(gate2);
argv[iteratr] = 0;
execvp(argv[0], argv);
}
}
execvp(argv[0], argv);
printf(" didn't exec \n ");
check = wait(&status);
}
wait(&status); //check
}
return 0;
}

execvp() not working in my shell

I am trying to make a tiny shell. My problem is that when I call execvp() - I get errors.
For example, when I type in ls -l it returns ls: invalid option -- '
Can someone, please, help me understand why I am getting this error? For my code, the function command split gets the user input, and splits them up into separate commands. Separate commands are seperated by ; character.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#define MAX_CHARACTERS 512
#define HISTORY_SIZE 10
int commandSplit(char *c, char *a[], int t[]) {
int count = 0;
int total = 0;
char *temp[MAX_CHARACTERS];
char *readCommands = strtok(c, ";");
while(readCommands != NULL) {
printf("Reading full command: %s\n", readCommands);
temp[count] = readCommands;
count++;
readCommands = strtok(NULL, ";");
}
printf("Done reading full commands\n");
for(int i = 0; i < count; i++) {
char *read = strtok(temp[i], " ");
int track = 0;
while(read != NULL) {
printf("Reading individual command: %s\n", read);
a[total] = read;
track++;
total++;
read = strtok(NULL, " ");
}
t[i] = track;
}
return count;
}
int main() {
int exitProgram = 0;
char *args[MAX_CHARACTERS];
while(!exitProgram) {
char *commands = (char *)(malloc(MAX_CHARACTERS*sizeof(char)));
int tracker[MAX_CHARACTERS];
int numOfCommands = 0;
printf("tinyshell> ");
fgets(commands, MAX_CHARACTERS, stdin);
if(strlen(commands) == 0) continue;
numOfCommands = commandSplit(commands, args, tracker);
printf("There are %i commands!\n", numOfCommands);
if(strcmp(args[0], "exit") == 0) {
printf("Exiting\n");
exitProgram = 1;
continue;
}
int l = 0;
for(int i = 0; i < numOfCommands; i++) {
int status;
char *holder[tracker[i]+1];
for(int j = 0; j < tracker[i]; j++) {
holder[j] = args[l];
printf("Assiging holder:%s\n", holder[j]);
l++;
}
holder[tracker[i]] = NULL;
printf("What is holder? \n");
for(int o = 0; o < tracker[i]; o++) printf("%s", holder[o]);
pid_t p = fork();
pid_t waiting;
if(p == 0) {
printf("I am in child process\n");
execvp(holder[0], holder);
fprintf(stderr, "Child process could not execvp!\n");
exit(1);
}
else {
if(p < 0) {
fprintf(stderr, "Fork FAILED!\n");
}
else {
waiting = wait(&status);
printf("Child %d, status %d\n", waiting, status);
}
}
for(int i = 0; i < numOfCommands; i++) {
args[i] = NULL;
}
}
}
return 0;
}
Your problem is that fgets() also reads the newline character. As a result, the last argument of execvp() arguments array contains a newline, causing ls complain about an unrecognized argument: what you acctually pass to ls is -l\n; what you need to pass is just -l without the newline.
Try adding this code after the fgets call to trim the input buffer:
int len;
len = strlen(commands);
if (len > 0 && commands[len-1] == '\n') {
commands[len-1] = '\0';
}

Implementing unlimited piping in shell using C

I'm trying to implement a C shell that allows for unlimited unidirectional pipes using the character '>'
So it can handle ls -A > tail > grep '.zip'
I understand that pipes are supposed to talk between processes, but I thought I came up with an idea that could use one pipe and multiple children.
This is what I have so far
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
/*#include <wait.h>*/
char *args[1000][1000];//array of arguments
int args_count = 0;//count of the arguments in the array
int runCommand(char **arguments, int *fd, int pipeHasSomeData, int baseCase) {
pid_t pid;
int x = 0;
int status;
pid = fork();
if(pid != 0) {
waitpid(pid, &status, 0);
if(baseCase) {
if(WIFEXITED(status))
{
if(WEXITSTATUS(status) == 0)
{
/*it worked*/
} else if(WEXITSTATUS(status) == 255) {
printf("The program %s does not exist \n", arguments[0]);
} else {
printf("ERROR: Error code: %d", WEXITSTATUS(status));
}
}
else
{
printf("There was a problem that is not normal");
}
printf("\n \n");
}
return 1;
} else {
if(pipeHasSomeData == 1) {// read from the pipe
dup2(fd[0], 0);//read from pipe
}
if(baseCase == 0) {// not the base case
dup2(fd[1], 1);//write to pipe
} else {
close(fd[1]);//close write
}
exit(execvp(arguments[0], arguments));
return 0;
}
}
int execute_commands(char *arguments[1000][1000], int pd[2] = NULL) {
int current_count = args_count;
int iterator = 0;
int fd[2];
int useAPipeInCommand = 0;
pipe(fd);
while(iterator <= args_count) {//go through and execute all the commands
if(current_count == 0) {//base case
return runCommand(arguments[iterator], fd, useAPipeInCommand, 1);
} else {
runCommand(arguments[iterator], fd, useAPipeInCommand, 0);
useAPipeInCommand = 1;
}
iterator++;
current_count--;
}//end while
return 1;
}
int main () {
int i = 0;
char text[1024]; /* the input line */
char *tok2;
while (1) { /* repeat until done .... */
fflush(stdin);
fflush(stdout);
printf("Shell -> "); /* display a prompt */
*text = 0;
fgets(text, sizeof text, stdin); /* read in the command line */
fflush(stdout);
printf("\n");
char * tok = strtok(text, " \n\t");
if (strcmp(tok, "exit") == 0) { /* is it an "exit"? */
return 0; /* exit if it is */
}
if (strcmp(tok, " ") == 0) { /* is it an "exit"? */
continue; /* exit if it is */
}
tok2 = tok;
memset(args, 0, sizeof(args[0][0]) * 1000 * 1000);//clear the arguments array
args_count = 0;
int count = 0;
while(tok2 != NULL) {
if(strcmp(tok2, ">") != 0) {
args[args_count][count] = tok2;
count++;
tok2 = strtok(NULL, " \n\t");
} else {//pipe was found, up the argument counter and set count to 0
args[args_count][count] = NULL;
args_count++;
count = 0;
tok2 = strtok(NULL, " \n\t");
}
}
args[args_count][count] = NULL;
execute_commands(args);
}//end while
return 0;
}
It is running the single base case no problem but the shell freezes when I do a pipe. Any ideas on the issue?
Correct answer from Comments by #beau-bouchard and #rici:
Pipes have a (small) finite buffer; you cannot write more than a little bit to the pipe without blocking unless the other end of the pipe is being read.
For a correct implementation, check out "multiple pipes in C" Coding multiple pipe in C
--UPDATE:
Here is my final working code for anyone that is having a similar issue:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <wait.h>
int READ = 0;
int WRITE = 1;
char *args[1000][1000];//array of arguments
int args_count = 0;//count of the arguments in the array
int execute_commands(char *arguments[1000][1000]) {
int pd[2];
int iterator = 0;
int fd[2];
int f_in = 0;
while(iterator <= args_count) {//go through and execute all the commands
pid_t pid;
int status;
pipe(fd);
pid = fork();
if(pid != 0) {
waitpid(pid, &status, 0);//wait for child to exit
close(fd[WRITE]);//close the writing end
if(WIFEXITED(status))
{
if(WEXITSTATUS(status) == 0)
{
/*it worked*/
} else if(WEXITSTATUS(status) == 255) {
printf("The program %s does not exist \n", arguments[iterator][0]);
} else {
printf("ERROR: Error code: %d", WEXITSTATUS(status));
}
}
else
{
printf("There was a problem that is not normal %d", status);
}
f_in = fd[READ];//set the pipe to the in
if(iterator == args_count) {
printf("\n \n");
}
//return 1;
} else {
dup2(f_in, 0);
if(iterator != args_count) {//its not the main value
dup2(fd[WRITE], 1);//write to pipe
}
close(fd[READ]);
exit(execvp(arguments[iterator][0], arguments[iterator]));
return 0;
}
iterator++;
}//end while
return 1;
}
int main () {
int i = 0;
char text[1024]; /* the input line */
char *tok2;
while (1) { /* repeat until done .... */
fflush(stdin);
fflush(stdout);
printf("Shell -> "); /* display a prompt */
*text = 0;
fgets(text, sizeof text, stdin); /* read in the command line */
fflush(stdout);
printf("\n");
char * tok = strtok(text, " \n\t");
if (strcmp(tok, "exit") == 0) { /* is it an "exit"? */
return 0; /* exit if it is */
}
if (strcmp(tok, " ") == 0) { /* is it an "exit"? */
continue; /* exit if it is */
}
tok2 = tok;
memset(args, 0, sizeof(args[0][0]) * 1000 * 1000);//clear the arguments array
args_count = 0;
int count = 0;
while(tok2 != NULL) {
if(strcmp(tok2, ">") != 0) {
args[args_count][count] = tok2;
count++;
tok2 = strtok(NULL, " \n\t");
} else {//pipe was found, up the argument counter and set count to 0
args[args_count][count] = NULL;
args_count++;
count = 0;
tok2 = strtok(NULL, " \n\t");
}
}
args[args_count][count] = NULL;
execute_commands(args);
}//end while
return 0;
}

Learning Pipes and Processes

I'm trying to get a better understanding of pipes and processes. I want to implement multiple chained pipes like cat test.txt | sort | uniq -c. I started my code with the cat test.txt, but it isn't working. It compiles, but when I provide a file name in the command line, for example, ./hwk ./test.txt. Nothing returns. Can someone take a look and give me some hints? I want to use loops because I want to be able to add more pipes. I know there's a lot of issues in my code, so I hope someone can give me some guidance on this topic. Thanks.
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
#define SIZE 1024
int main (int argc, char **argv)
{
int num_pipe = 1;
int commands = num_pipe + 1; //number of commands is one more than the number of pipes
int fds[num_pipe * 2];
int status;
pid_t pid;
char *str_ptr;
//Pass Command
char *arrayOfCommands[] = {"cat", NULL};
//Setting up pipes
int i;
for (i = 0; i < num_pipe; i++){
if(pipe(fds + i * 2) == -1) {
perror("Error creating pipes");
exit(1);
}
}
int j = 0;
for (i = 0; i < commands - 1; ++i) {
pid = fork();
if (pid == 0) {
if (i < commands) {
if (dup2(fds[j+1], 1) < 0) {
perror("dup2 error");
exit(EXIT_FAILURE);
}
}
if (j != 0) {
if(dup2(fds[j-2], 0) < 0) {
perror("dup2 error");
exit(EXIT_FAILURE);
}
}
for (i = 0; i < 2*num_pipe; i++) {
close(fds[i]);
}
if (execvp(arrayOfCommands[0], arrayOfCommands) < 0) {
perror("Array error");
exit(EXIT_FAILURE);
}
}
else if (pid < 0){
perror("Error");
exit(EXIT_FAILURE);
}
j += 2;
}
for (i = 0; i < 2 * num_pipe; i++){
close(fds[i]);
}
for (i = 0; i < num_pipe + 1; i++) {
wait(&status);
}
return 0;
}
I called this mainly minor adaptation of your program p3.c, compiling it to produce p3. Since there's only one command (cat) being invoked, I juggled things so that it will work correctly. When run as ./p3 p3.c, it prints out the content of the source code.
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
static void err_exit(const char *str);
int main (int argc, char **argv)
{
int num_pipe = 0; // Just cat - no pipes
int commands = num_pipe + 1; // Number of commands is one more than the number of pipes
int fds[num_pipe * 2 + 1]; // Avoid size 0 array
char *arrayOfCommands[3] = { "cat", NULL, NULL};
if (argc != 2)
err_exit("Missing filename argument");
arrayOfCommands[1] = argv[1];
for (int i = 0; i < num_pipe; i++)
{
if (pipe(fds + i * 2) == -1)
err_exit("Error creating pipes");
}
int j = 0;
for (int i = 0; i < commands; ++i)
{
pid_t pid = fork();
if (pid == 0)
{
printf("%d: %s %s\n", (int)getpid(), arrayOfCommands[0], arrayOfCommands[1]);
fflush(stdout);
if (i < commands-1 && dup2(fds[j+1], 1) < 0)
err_exit("dup2 error");
if (j != 0 && dup2(fds[j-2], 0) < 0)
err_exit("dup2 error");
for (i = 0; i < 2*num_pipe; i++)
close(fds[i]);
execvp(arrayOfCommands[0], arrayOfCommands);
err_exit("Array error");
}
else if (pid < 0)
err_exit("Error");
j += 2;
}
for (int i = 0; i < 2 * num_pipe; i++)
close(fds[i]);
for (int i = 0; i < num_pipe + 1; i++)
{
int status;
pid_t pid = wait(&status);
printf("PID %d exited 0x%.4X\n", (int)pid, status);
}
return 0;
}
static void err_exit(const char *str)
{
perror(str);
exit(EXIT_FAILURE);
}
Check that works for you. Then you'll need to work out how you're going to create a second command. Your arrayOfCommands isn't going to help directly. You'll need another array of strings in some shape or form.
An extension to run cat file | rev. The changes are really quite minor. I created a_cat to handle the cat command, a_rev for the rev command, and a_cmds as the array of commands. It was also necessary to fix a loop on i to a loop on k.
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/wait.h>
static void err_exit(const char *str);
int main (int argc, char **argv)
{
int num_pipe = 1;
int commands = num_pipe + 1; //number of commands is one more than the number of pipes
int fds[num_pipe * 2 + 1]; // Avoid size 0 array
char *a_cat[3] = { "cat", NULL, NULL};
char *a_rev[2] = { "rev", NULL};
char **a_cmds[] = { a_cat, a_rev };
if (argc != 2)
err_exit("Missing filename argument");
a_cat[1] = argv[1];
for (int i = 0; i < num_pipe; i++)
{
if (pipe(fds + i * 2) == -1)
err_exit("Error creating pipes");
}
int j = 0;
for (int i = 0; i < commands; ++i)
{
pid_t pid = fork();
if (pid == 0)
{
printf("%d: %s\n", (int)getpid(), a_cmds[i][0]);
fflush(stdout);
if (i < commands-1 && dup2(fds[j+1], 1) < 0)
err_exit("dup2 error");
if (j != 0 && dup2(fds[j-2], 0) < 0)
err_exit("dup2 error");
for (int k = 0; k < 2*num_pipe; k++)
close(fds[k]);
execvp(a_cmds[i][0], a_cmds[i]);
err_exit("Array error");
}
else if (pid < 0)
err_exit("Error");
j += 2;
}
for (int i = 0; i < 2 * num_pipe; i++)
close(fds[i]);
for (int i = 0; i < num_pipe + 1; i++)
{
int status;
pid_t pid = wait(&status);
printf("PID %d exited 0x%.4X\n", (int)pid, status);
}
return 0;
}
static void err_exit(const char *str)
{
perror(str);
exit(EXIT_FAILURE);
}
You aren't passing your program's command-line arguments through to the "cat" child process. You initialize arrayOfCommands like so -> char *arrayOfCommands[] = {"cat", NULL}; <- then you pass it as-is to the execvp() function as the second argument.
Okay your first problem is that in the line:
execvp(arrayOfCommands[0], arrayOfCommands);
you are using arrayOfCommands but I am not sure how you're populating arrayOfCommands for the case where the text file is not being displayed. I mean are you setting arrayOfCommands like the following earlier in the code:
char *arrayOfCommands[] = {"cat", "./test.txt", NULL};
If I understand you correctly your program is called hwk and for whatever reason you think ./hwk ./test.txt should be parsed but that means you should be parsing argv.
Okay now that that's out of the way let's look at the bigger problem of how you are setting things up.
So when a shell parses out pipes it does there's quite a bit going on. Consider the following:
foo fooparam1 fooparam2 | bar barparam1 | baz bazparam1 bazparam2
The shell uses recursion to solve the problem:
foo fooparam1 fooparam2 | ( bar barparam1 | baz bazparam1 bazparam2 )
So it would look SOMETHING like:
spawn_sub_pipes(const char *str) {
char *cmd = strtok(str, "|");
char *rest = strtok(NULL, "|");
int fds[2];
pipe(fds[]);
int pid = fork();
if ( pid < 0 ) {
perror("pipe error");
exit(-1);
}
if ( pid ) { /* parent is the writer */
close(fds[0]); /* close reading pipe */
dup2(fds[1], 1); /* we attach stdout to the pipe */
}
if ( pid == 0 ) {
close(fds[1]);
dup2(fds[0], 0); /* attach the pipe to stdin */
if ( rest ) { /* fork next children */
spawn_sub_pipes(rest);
}
execvpe(cmd);
}
}
IMPORTANT NOTE
I have just written the above code out without testing it. Get the idea from it but don't use it verbatim.

Problem with piping commands in C

I'm trying to create a simple shell in C for Unix. I've been able to do all the parsing of commands and execution, but I'm having a problem with piping. I think the problem is that I'm not hooking into the correct pipe for the input of the second command.
For example, if I type "ls | wc", it will pause after the "wc" command, which I think is because its waiting for input. I think the problem is when I use dup2(reading[i],0), and its not hooking into the correct pipe.
I know this is a bit of a broad question, but if there are any pointers I could get, I would appreciate it. Here is the code that creates new processes and tries to pipe them.
int fileds[2];
int reading[num_cmds];
int writing[num_cmds];
int p;
for(p=0; p < num_cmds; p++)
{
reading[p] = -1;
writing[p] = -1;
}
int j;
for(j=0; j < num_cmds-1; j++) //Create pipes for commands
{
int fileds[2];
pipe(fileds);
reading[j+1] = fileds[0];
writing[j] = fileds[1];
}
int i = 0;
for(i = 0; i < num_cmds;i++)
{
cmd_args = parse_cmd(cmds[i],output_file,input_file,&run_bg); //Get command and args
pid_t childpid;
int status;
childpid=fork();
if (childpid >= 0)
{
if (childpid == 0)
{
if(writing[i] != -1)
{
dup2(writing[i],1);
close(writing[i]);
}
if(reading[i] != -1)
{
dup2(reading[i],0);
close(reading[i]);
}
int h;
for(h = 0; h < num_cmds; h++)
{
close(writing[h]);
close(reading[h]);
}
if(execvp(cmd_args[0],cmd_args) == -1)
{
perror("Problem with command");
exit(0);
}
}
else
{
wait(&status);
int m;
for(m = 0; m < num_cmds; m++)
{
if( writing[m] != -1) close(writing[m]);
if( reading[m] != -1) close(reading[m]);
}
}
}
else
{
perror("fork");
continue;
}
input_file[0] = 0;
output_file[0] = 0;
run_bg = 0;
}
}
UPDATE: I was able to figure it out, thanks to Richard. It was a combination of closing the file descriptors in the wrong order and not closing some at all. Here's the working code.
int fileds[2];
int reading[num_cmds];
int writing[num_cmds];
int p;
for(p=0; p < num_cmds; p++)
{
reading[p] = -1;
writing[p] = -1;
}
int j;
for(j=0; j < num_cmds-1; j++)
{
int fileds[2];
pipe(fileds);
reading[j+1] = fileds[0];
writing[j] = fileds[1];
}
int i = 0;
for(i = 0; i < num_cmds;i++)
{
cmd_args = parse_cmd(cmds[i],output_file,input_file,&run_bg);
pid_t childpid;
int status;
childpid=fork();
if (childpid >= 0)
{
if (childpid == 0)
{
if(writing[i] != -1)
{
close(1);
dup2(writing[i],1);
}
if(reading[i] != -1)
{
close(0);
dup2(reading[i],0);
}
if(execvp(cmd_args[0],cmd_args) == -1)
{
perror("Problem with command");
exit(0);
}
}
else
{
wait(&status);
close(writing[i]);
if(i > 0)
{
close(reading[i]);
}
}
}
else
{
perror("fork");
}
input_file[0] = 0;
output_file[0] = 0;
run_bg = 0;
}
I think your problem may be that you wait for each process inside the loop and then close all the file descriptors. This makes the file descriptors invalid for the next call to dup2() and results in stdin for the next process staying unchanged.
Just a guess, I haven't run the code.
When I type "ls | wc" wc does as expected and prints the number of words output by the ls command. Remember that when you are piping commands using "|" you don't need to create pipes in your application. The first command needs to output to stdout and the second command needs to read that output from standard in.

Resources