Pipe implementation in C
I just finished a shell interpreter but I think my channel implementation is wrong.
It works, the main things seem to ls | cat -e
work, but I'm afraid of the possibilities segmentation fault
if the file descriptor is more than 60 k. I also found an infinite loop when I make a cat of a file longer than 60 kb. For example, if do cat foo | cat -e
, where foo is a long file, an endless loop occurs.
Or another example, when I do cat /dev/urandom | cat -e
, it doesn't show me any display, so it executes first cat /dev/urandom
and then cat -e
.
This is my code:
int son(int *fd_in, int p[2], t_list *cmd, char **env)
{
(void)env;
dup2(*fd_in, 0);
if (cmd->act != ENDACT && cmd->act != LEFT && cmd->act != DLEFT)
dup2(p[1], 1);
close(p[0]);
execve(cmd->av[0], cmd->av, NULL);
return (-1);
}
t_list *execute_pipe(t_list *cmd, int *fd_in)
{
int p[2];
pid_t pid;
*fd_in = 0;
while (cmd->act != -1)
{
pipe(p);
if ((pid = fork()) == -1)
return (NULL);
else if (pid == 0)
son(fd_in, p, cmd, NULL);
else
{
wait(NULL);
close(p[1]);
*fd_in = p[0];
if (cmd->act != PIPE)
return (cmd);
cmd = cmd->next;
}
}
return (cmd);
}
source to share
Part of the idea behind a shell pipeline is that the involved processes run concurrently (or can do). The code you provided actively prevents this from wait()
happening for each child process before starting the next one. Among other things, there is a risk of filling the buffer (OS-level) before anything is ready to drain it. It will stall or, with luck, generate an error.
At a high level, the procedure should look like this:
- [shell] Let
C
initially be the command for the first channel segment and setfd0
asSTDIN_FILENO
- [shell] . Prepare an output file descriptor:
- If there are any follow-up commands, create
pipe()
and setfd1
as the end of this channel's recording; - otherwise set
fd1
asSTDOUT_FILENO
- If there are any follow-up commands, create
- [shell]
fork()
child element to run the commandC
. In that:- [child] if
fd0
different fromSTDIN_FILENO
, thendup2()
fd0
onSTDIN_FILENO
and closefd0
- [child] if
fd1
different fromSTDOUT_FILENO
, thendup2()
fd1
onSTDOUT_FILENO
and closefd1
- [child] exec command
C
- [child] if
- [shell] if
fd0
different fromSTDIN_FILENO
, then closefd0
- [shell] if
fd1
different fromSTDOUT_FILENO
, then closefd1
- [shell] If there are more commands in the pipe, then
- set
C
will be the next command - set
fd0
as read pipe end from step (2) above - go to step 2 (prepare an output file descriptor)
- set
- [shell] (At this point, all processes in the pipeline have been started.)
wait()
orwaitpid()
for all child processes
Note that this works equally well for a pipeline containing any positive number of instructions, including 1.
source to share