I'm actually working on project and we're trying to recreate a bash terminal in C. I've got some problems about handling pipes in my code. As you know when you write "cat | ls", it first shows ls result then standard input is opened but only for one line. The problem is the way I'm handling pipes, in first it shows the ls result as excepted but after the standard input is reading until I make CTRL-D. I want to make it end after the first line, like bash does. I think I have a problem with my pipes but I tried to remake things in other ways and it's always the same problem.
My code :
void make_command(t_list *cmds, char **env)
{
char *path;
int exec;
path = get_path((char *)cmds->content[0], env);
if (!path)
return ;
exec = execve(path, (char **)cmds->content, env);
if (exec < 0)
exit_error();
}
void make_pipe(t_list *cmds, char **env, t_listpids **pids, int *fd_old)
{
pid_t pid;
int tube[2];
while (cmds)
{
if (pipe(tube) == -1)
exit_error();
pid = fork();
if (pid == 0)
{
if (cmds->previous) //only if not first command
{
dup2((*fd_old), STDIN_FILENO);
close(*fd_old);
}
if (cmds->next) //only if not last command
{
dup2(tube[1], STDOUT_FILENO);
close(tube[1]);
}
make_command(cmds, env); //execute command function with execve
close(tube[0]);
close(tube[1]);
exit(EXIT_SUCCESS);
}
else
{
add_pids(pid, pids); //add pid to chained list of pids (used to waitpid them after)
*fd_old = tube[0];
close(tube[1]);
cmds = cmds->next;
while (cmds && ft_strncmp((char *)cmds->content[0], "|", 1) == 0)
cmds = cmds->next;
}
}
}
As you know when you write "cat | ls", it first shows ls result then standard input is opened but only for one line.
That's a confusing characterization of what happens with that largely nonsensical pipeline. Here's a better one: ls
runs to completion without waiting for any input, meanwhile cat
waits for input on its standard input, and terminates after reading one line, without echoing that line to the terminal.
Important distinctions between the two:
the ls
command does not run first in the sense of forcing cat
to wait for it. It simply does not itself have to wait for any user input, therefore its output is displayed immediately.
no new standard input is "opened". The cat
command inherits its standard input from the shell, and reads from that file until it terminates. It receives the input instead of the shell by virtue of being in the foreground.
Additionally, that cat
terminates after reading one line is incidental. It might read more if more were queued up for it to read immediately, or perhaps if the ls
ran slowly for some reason. When it does terminate, it's because it tries to write to a pipe that has no readers. That causes a SIGPIPE
to be delivered to it, which causes it to terminate.
And that is all relevant, because it points towards the issue with the (mis)behavior you describe. That in your case, the cat
keeps reading input until you type a Ctrl-D
tells me right away that either its standard output is not connected to a pipe at all, or else that the pipe to which it is connected is still open for reading somewhere.
And knowing what to look for, we can satisfy ourselves that yes, the parent process fails to close the read end of every one of the pipes it creates. It preserves them, one at a time, in *fd_old
, so as to be able to redirect the next child's standard input, but after providing for that redirection, it leaks its copy of the open file descriptor.
It only needs to hold that FD open until the next fork()
, so perhaps what you want is to close it in the else
block, something like this:
else
{
if (cmds->previous) //only if not first command
{
close(*fd_old);
}
add_pids(pid, pids); //add pid to chained list of pids (used to waitpid them after)
*fd_old = tube[0];
close(tube[1]);
cmds = cmds->next;
while (cmds && ft_strncmp((char *)cmds->content[0], "|", 1) == 0)
cmds = cmds->next;
}
You may also need to close the last one after the loop terminates. I would ordinarily think so, but the fact that you are storing it indirectly, via a pointer, leaves me uncertain as to what exactly you're trying to accomplish.