1

I have some issue in my program.

I have a school homework where I have to reproduce some features of bash The homework is almost done however I have an issue with built_in command and pipes indeed I have a broken pipe error whenever a built_in command is the last pipeline like such : ls | echo (this line will produce a broken pipe error) where as this one echo | ls will work just fine. after some research I found out what was the problem, I have this error because for built_in command (meaning that I don't use execve to call them) I don't fork them into a child process while the other command are forked, so my guess is that the built_command close the read_end of the pipe way too fast while ls is still writing into the pipe. I handled this error with forking a process too for the built_in commands. However I was wondering if they were a solution without forking the built_in commands as this would use unnecessary resources.

void    execute_routine(t_data *data, t_cmd *cmd)
{
    pid_t   pid_ret;

    if (data -> s_pipes && !ft_strcmp(cmd -> prev_stop, "|")
        && cmd -> prev_cmd -> p_close)
        close_fd(data, "bash", &data -> s_pipes -> s_pipes[1]);
    if (!built_in(data, cmd, 0))
    {
        pid_ret = fork();
        if (pid_ret < 0)
            print_err_and_exit(data, NULL, "bash", 1);
        if (pid_ret == 0)
            forking(cmd);
        cmd -> pid = pid_ret;
    }
    handle_pipes(data, cmd);
}

Here above, is the function that will execute each command get by the help of readline, you can see that if the command is a built_in we won't fork it and it will be handled right into that function for the case of echo here is the function :

int echo(t_data *data, t_cmd *cmd)
{
    int fd;

    data -> status = 1;
    if (open_check_files_built_in(cmd, cmd -> tab))
        return (1);
    fd = where_to_write(data, cmd);
    if (ft_tab_len(cmd -> args) == 1)
    {
        if (write_to_fd(data, "\n", fd) < 0)
            return (print_err_built_in("bash", 1));
        data -> status = 0;
        return (1);
    }
    if (write_args_(data, cmd, fd))
        return (1);
    if (cmd -> last_in && cmd -> last_in -> type == IN)
        close_fd_built_in(&cmd -> last_in -> fd);
    if (cmd -> last_out)
        close_fd_built_in(&cmd -> last_out -> fd);
    data -> status = 0;
    return (1);
}

for echo I only look for out redirection and writing into the returned fd. When I am done with the function I the pipe in that function.

void    handle_pipes(t_data *data, t_cmd *cmd)
{
    if (data -> s_pipes && data -> prev_pipes == -1
        && !ft_strcmp(cmd -> prev_stop, "|"))
        close_fd(data, "bash", &data -> s_pipes -> read_end -> s_pipes[0]);
    close_fd(data, "bash error", &data -> prev_pipes);
    if (data -> inited)
    {
        data -> prev_pipes = data -> pipes[0];
        close_fd(data, "bash pipes close", &data -> pipes[1]);
    }
    data -> inited = 0;
}

After that I wait all of my process with the function below

   int i;

   i = -1;
   while (cmds[++i])
   {
        if (cmds[i]-> pid && waitpid(
                cmds[i]-> pid, &data -> status, 0) < 0 && errno != ECHILD)
            print_err_and_exit(data, NULL, "Error with waitpid", 1);
    }

Like I said earlier the only differences between built_in command and the others is that built_in commands are not forked and directly handled in the parent process while the others are forked and handled in the child process. Although I have the broken pipe error signal the pipeline still output the expected result, for example ls | echo bonjour would still output bonjour to STDOUT but the error SIGPIPE would be sent to waitpid by the process handling the ls command, while echo bonjour | ls will also work fine and return a status code of 0 and not 13.

dieri
  • 23
  • 6
  • 1
    No. No school asks you to "reproduce bash". It's common to be asked to write _a POSIX-flavored shell_ as an academic exercise (not often even a POSIX-_compliant_ shell, which is a lot more work), but bash is a _specific_ huge shell with decades of development history and a mismash of features adopted from different sources. – Charles Duffy Feb 07 '23 at 16:31
  • Please [edit] your question and create a [mre]. Both `ls` and `echo` will not read from STDIN and might close it. The behavior may depend on timing, the amount of data written to STDOUT by the first program and differences in handling of SIGPIPE. By default, the exit code of a pipe is the exit code of the last command. When you use `ls|echo` in a POSIX compatible shell, you will *not* notice that `ls` exits with a non-zero exit code since the shell ignores this. (In `bash` you can change this with `set -o pipefail`) – Bodo Feb 07 '23 at 16:59
  • Possibly edit your post to change _"I have to reproduce bash..."_ to _"I have to emulate a `bash` like feature..."_ – ryyker Feb 07 '23 at 16:59
  • @Bodo Thanks for your answer, since bash does the same I assume my problem is not a problem anymore ! – dieri Feb 08 '23 at 02:18

1 Answers1

1

The issue is that you misunderstand the nature of "echo" vs the nature of "ls", and again the nature of "pipes"

"ls" is a potentially multi-line function. It generates a stream.

A "pipe" is a mechanism to create an inter-process communication between one process creating a stream, and another process using that stream as input.

Here is where you failed to understand the nature of "echo": echo is NOT a multi-line (a.k.a. stream) -oriented tool! It is a single line tool geared to a "single" parameter string. That string could be multi-line, but that is passed to echo which only interprets it as a single parameter, without caring what the contents are.

Even the man page makes no reference to echo accepting input from stdin! In other words, it doesn't even open the pipe stream.

Hope that clarifies what you perceive as a mystery regarding the "ls | echo" pipeline.

Eric Marceau
  • 1,601
  • 1
  • 8
  • 11
  • Indeed. There is no mystery - this is exactly how standard utilities behave. `ls /bin | echo` on an Ubuntu 20.04 system produces the exact same output as `echo` alone - one empty line. And running `strace ls /bin | echo` does product this output: `397755 --- SIGPIPE {si_signo=SIGPIPE, si_code=SI_USER, si_pid=397755, si_uid=1000} --- 397755 +++ killed by SIGPIPE +++` Exactly the same as the OP is seeing - it's just that Ubuntu's standard `ls` utility doesn't emit any `SIGPIPE` error message. – Andrew Henle Feb 08 '23 at 03:06