Hi guys,
I took it upon myself to learn the mysteries of linux.

I'm trying to write a shell that can handle more than one pipe.

Here's the code:

typedef struct command_link{
	char *command;
	char *argv[MAX_ARGS];
	int argc;
	struct command_link* next;
	int isPiped;
}command_t;

int pipedCommand(command_t *command)
{
	int fd[2];
	int status;
	int isFirstPipe = 1;
	int pid = -1;
	
	status = pipe(fd);
	if (status < 0)
	{
		printf("\npipe error");
		return -1;
	}
	
	while (1)
	{
		
		pid = fork();
		
		if (pid < 0)	/*handle fork error*/
		{
			printf("\nfork error");
			return -1;
		}
		else if (pid == 0)	/*handle son code*/
		{
			//while there are more commands close stdout and redirect output to pipe
			if (command->next)
				dup2(fd[1], 1);	
			
			//close stdin and redirect input from pipe
			if (!isFirstPipe)
				dup2(fd[0], 0);	
				
			//keep pipe opened while there are commands
			if (!command->next)
			{
				close(fd[0]);
				close(fd[1]);
			}
			
			execv(command->command, command->argv);
			
			//if errors exist execv wouldn't have been invoked
			printf("\nexecv error");
			return -1;
		}
		else		/*handle father code*/
		{
			isFirstPipe = 0;
			
			//check for more commands and connect them to the opened pipe
			if (command->next) 
			{
				command = command->next;
				continue;
			}
			
			//close pipe and wait for the last son to die - then finish
			close(fd[0]);
			close(fd[1]);
			
			while(wait(&status) != pid);
			break;
						
		}
	}
				
}

This works okay for one pipe.
When I use two pipes the shell hangs and ignores my inputs.

I'm guessing a leaky pipe somewhere. But I can't see the flaw in my logic:

The "father" process keeps forking out children while there are still commands in the linked list. Every child process gets the fathers file descriptors (where i/o is always stdin/stdout). The child process replaces the file i/o descriptors of the father process with the pipe file descriptors according to their place in the link :
head : in - stdin, out - fd[1]
middle link: in - fd[0], out - fd[1]
tail: in - fd[0], out - stdout

Where is the error?

Thank you

Recommended Answers

All 5 Replies

Here's a quick example of a parent receiving from a child which receives from a child...It should be 'mostly' correct.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

enum PIPES {READ, WRITE};

int main(int argc, char**argv)
{
	int hpipe[2];
	pipe(hpipe);

	if (fork())
	{
		/*parent*/
		char ch;
		close(hpipe[WRITE]);
		dup2(hpipe[READ], 0);
		close(hpipe[READ]);
		while ((ch = fgetc(stdin)) != EOF)
		{
			fprintf(stdout, "parent rec->%c\n", ch);
		}

	}
	else
	{
		/*child*/
		char ch;
		int hpipe2[2];
		pipe(hpipe2);

		close(hpipe[READ]);
		dup2(hpipe[WRITE], 1);
		close(hpipe[WRITE]);

		if (fork())
		{
		close(hpipe2[WRITE]);
		dup2(hpipe2[READ], 0);
		close(hpipe2[READ]);

		while ((write(1, (char*)&ch, read(0, (char*)&ch, sizeof(char)))))
		{
			fprintf(stderr, "middle child rec->%c\n", ch);
		}
		}
		else
		{
			/*child2*/
			close(hpipe2[READ]);
			dup2(hpipe2[WRITE], 1);
			close(hpipe2[WRITE]);
			fputs("this is from way down the line", stdout);
		}
	}
	exit(EXIT_SUCCESS);
}

So you're saying that the sons do not share the same pipe with the father.
And that the fork isn't preformed by the father for all sons.

Each son acts as if his a father and forks off a child and a pipe.

If I were to implement this with more than two pipes I would have to use recursion!!!!!!!!!!!!!!!

Am I understanding correctly?

Actually if your trying to minic a shell this example is better.

example of usage ./testit ps wc

where ps = current processes
and wc = word count


testit.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

enum PIPES {READ, WRITE};

int main(int argc, char**argv)
{
	int hpipe[2];
	pipe(hpipe);

	if (fork())
	{
		close(hpipe[READ]);
		dup2(hpipe[WRITE], 1);
		close(hpipe[WRITE]);
		execlp(argv[1], argv[1], NULL);
	}
	else
	{
		close(hpipe[WRITE]);
		dup2(hpipe[READ], 0);
		close(hpipe[READ]);
		execlp(argv[2], argv[2], NULL);
	}

	exit(EXIT_SUCCESS);
}

Okay, figured it out.
The main thing I missed was the purpose of the pipe. If a pipe is to transfer data from one process to another it's best to keep one end open in one process and the other end open in the second process.
In the code I'd posted my mistake was that I closed both pipe ends in the process.
As for the process and pipe creation I think that there is more than one way to do it. I used the father process to create all the processes and all the pipes:

int pipedCommand(command_t *command)
{
	int fd[10][2];
	int status;
	int isFirstPipe = 1;
	int pid[10] = {-1};
	int count = 0;
	int i;
		
	while (1)
	{
		status = pipe(fd[count]);
		if (status < 0)
		{
			printf("\npipe error");
			return -1;
		}
		
		
		pid[count] = fork();
		
		if (pid[count] < 0)
		{
			printf("\nError in fork");
			return -1;
		}
		else if (pid[count]) /*father code*/
		{
			if (command->next) 
			{
				command = command->next;
				count++;
				continue;
			}
			
			//close all opened pipes
			for (i = 0; i <= count; i++)
			{
				close(fd[i][FD_READ]);
				close(fd[i][FD_WRITE]);
			}
			
			if(!isConcurrency)
			{
				for (i = 0; i <= count; i++)
					waitpid(pid[i], NULL, 0);
			}
			
			return 0;			
		}
		else	/*son code*/
		{
			if (count == 0)
				close(fd[0][FD_READ]);
			else
				dup2(fd[count-1][FD_READ], 0);
			
			if (!command->next)
				close(fd[count][FD_WRITE]);
			else
				dup2(fd[count][FD_WRITE],1);
							
				
			//close all other opened pipes
			for (i = 0; i <= count; i++)
			{
				close(fd[i][FD_READ]);
				close(fd[i][FD_WRITE]);
			}
			
			execv(command->command, command->argv);
			
			//if execv failed
			printf("%s: failed to execute", command->argv[0]);
			return -1;
		}
		
	
					
	}
	
				
}

what is the variable isConcurrency

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.