Search code examples
cshellpipecatexecve

Why pipes not working for cat | cat | ls?


I really need help and any suggestions will be very welcomed. Me and a mate have coded a mini version of the Shell and I did parsing and executing parts. All is working properly except for some cases that probably are hiding some logic that I can't catch and I'm actually stuck. In particular when launching cat | cat | ls cmd the ls output is ok but the cat behavior is not what expected. Instead of waiting for the stdin (not having any argument) the prompt is suddenly returned. That probably means that there's an issue with pipes not closing at the right place, but I'm not able to find where exactly. Here below you will find the executing part. If anyone please could help with kindness and a noncompetitive attitude will be very appreciated. Here is the github repo for any further details GitHub Repo

void    executing_commands(t_minish *data)
{
    t_cmd   *cmd;
    int process_status;

    process_status = 0;
    cmd = data->cmds;
    if (!cmd->full_cmd)
    return ;
    creating_pipes(data);
    while (cmd && cmd->full_cmd)
    cmd = creating_child(&cmd, data);
    closing_all_fd(data);
    cmd = data->cmds;
    while (cmd)
    {
        waitpid(data->child, &process_status, 0);
        if (data->cmds && !check_parent_builtin(&cmd))
            g_status = WEXITSTATUS(process_status);
        cmd = cmd->next;
    }
}


t_cmd   *creating_child(t_cmd **cmd, t_minish *data)
{
    int pid;

    if (check_parent_builtin(cmd))
        executing_builtin(data, cmd);
    else
    {
        pid = fork();
        if (ft_memcmp((*cmd)->full_cmd[0], "minishell", 10))
            set_signals(EXEC);
        data->child = pid;
        if (pid == -1)
        {
            closing_all_fd(data);
            error_manager(2, data, NULL);
        }
        else if (pid == 0)
            child_process(data, cmd);
    }
    return ((*cmd)->next);
}


void    child_process(t_minish *data, t_cmd **cmd)
{
    switching_input_output(data, cmd);
    closing_all_fd(data);
    if (check_child_builtin(cmd))
        executing_builtin(data, cmd);
    else
        launching_command(data, cmd);
}

void    switching_input_output(t_minish *data, t_cmd **cmd)
{
    if ((*cmd)->output > 1)
    {
        if (dup2((*cmd)->output, STDOUT_FILENO) < 0)
            error_manager(6, data, cmd);
        close((*cmd)->output);
    }
    if ((*cmd)->input)
    {
        if (dup2((*cmd)->input, STDIN_FILENO) < 0)
            error_manager(6, data, cmd);
        close((*cmd)->input);
    }
    if ((*cmd)->file_in)
    {
        if (dup2((*cmd)->file_in, STDIN_FILENO) < 0)
            error_manager(6, data, cmd);
        close((*cmd)->file_in);
    }
    if ((*cmd)->file_out)
    {
        if (dup2((*cmd)->file_out, STDOUT_FILENO) < 0)
            error_manager(6, data, cmd);
        close((*cmd)->file_out);
    }
}

void    launching_command(t_minish *data, t_cmd **cmd)
{
    if (execve((*cmd)->full_path, (*cmd)->full_cmd, data->env_table) == -1)
        error_manager(3, data, cmd);
}

void    closing_all_fd(t_minish *data)
{
    t_cmd   *cmd;

    cmd = data->cmds;
    while (cmd)
    {
        if (cmd->input)
            close(cmd->input);
        if (cmd->output > 1)
            close(cmd->output);
        cmd = cmd->next;
    }
}

void    closing_fd_if_redirections(t_minish *data)
{
    t_cmd   *cmd;

    cmd = data->cmds;
    while (cmd)
    {
        if (cmd->file_in && cmd->input)
            close(cmd->input);
        cmd = cmd->next;
    }
}


void    creating_pipes(t_minish *data)
{
    int     fd[2];
    t_cmd   *cmd;

    cmd = data->cmds;
    while (cmd->next != NULL)
    {
        if (pipe(fd) == -1)
            error_manager(1, data, NULL);
        cmd->output = fd[1];
        cmd->next->input = fd[0];
        if (!cmd->next->next)
            cmd->next->last = 1;
        cmd = cmd->next;
    }
    closing_fd_if_redirections(data);
}

I tried not to create all the pipes needed all in a sudden in order to better control closing in parent and child processes with just a pipe for each loop but still not working


Solution

  • … the prompt is suddenly returned. That probably means that there's an issue with pipes not closing at the right place …

    Your probability estimation went wrong. The error is that the shell does not wait for all the commands in the pipeline, because, though you rightly try to wait for each command in the while (cmd) loop in executing_commands, there is only one storage location data->child for the whole pipeline, so a child PID is overwritten by the following, and at the end the shell can only wait for the last command.
    Of course the remedy is to store child per cmd rather than per data.