Enes Özmert
Enes Özmert

Reputation: 1

C , dup2 and exec() pipe "|" operator

I have a problem in execve-pipe process. I split a command with pipeline and sent to function one by one . I made fork , dup2 and close functions . Whole command executed but output of last command sending to terminal and readline function. For this reason readline runs the last output. For example I sent ls | wc, output: 8. error: 8 is not a command. segfault.

while (++i < nproc)
{
    rdl->main_str = ft_strdup(rdl->pipe_str[i]);
    rdl->len = ft_strlen(rdl->pipe_str[i]);
    parser(rdl);
    command(rdl); // => run a pipe_exec function
    token_clear(&rdl->token);
    free(rdl->main_str);
    printf("*****************\n");
}
while (nproc-- > 0)
    waitpid(-1, 0, 0);
#define READ 0
#define WRITE 1

static void ft_fatality(void)
{
    ft_putstr_fd("error: fatal\n", 2);
    exit(1);
}

// static void  ft_exec_error(char *str)
// {
//  ft_putstr_fd("error: cannot execute ", 2);
//  ft_putstr_fd(str, 2);
//  ft_putstr_fd("\n", 2);
//  exit(1);
// }

static void ft_openpipes(int fd[2])
{
    if (close(fd[READ]) == -1)
        ft_fatality();
    if (dup2(fd[WRITE], STDOUT_FILENO) == -1)
        ft_fatality();
    if (close(fd[WRITE]) == -1)
        ft_fatality();
}

static void ft_closepipes(int fd[2])
{
    if (dup2(fd[READ], STDIN_FILENO) == -1)
        ft_fatality();
    if (close(fd[READ]) == -1)
        ft_fatality();
    if (close(fd[WRITE]) == -1)
        ft_fatality();
}

int pipe_exec(t_command command)
{
    printf("pipe_exec\n");
    printf("pipe_exec command count %d\n", command.count);
    int i;
    int j;
    int fd[2];
    int type_size;
    int size;
    int result;
    char *arg;
    char *path;
    char **type;
    pid_t pid;

    i = -1;
    j = 1;
    result = 0;
    size = token_size(command.tokens);
    type_size = 0;
    arg = ft_strdup("");
    if (pipe(fd) == -1)
        ft_fatality();
    while (++i < size)
    {
        if (command.tokens->type_id == 12)
            type_size++;
        get_next_token(&command.tokens);
    }
    i = -1;
    path = command_find_path(command.keyword);
    type = (char **)malloc(sizeof(char *) * ((type_size + 1) + 2));
    type[0] = ft_strdup(path);
    while (++i < size)
    {
        if (command.tokens->type_id == 13 || command.tokens->type_id == 7)
        {
            arg = ft_strjoin(arg, command.tokens->context);
            printf("arg %s\n", arg);
        }
        if (command.tokens->type_id == 12 || size - 1 == command.tokens->id)
        {
            type[j++] = ft_strdup(arg);
            arg = ft_strdup("");
        }
        get_next_token(&command.tokens);
    }
    type[j] = NULL;
    j = -1;
    while (type[++j])
    {
        printf("type : %s\n", type[j]);
    }
    pid = fork();
    // signal(SIGINT, proc_signal_handler);
    if (pid < 0)
        return (-1);
    if (pid == 0)
    {
        ft_openpipes(fd);
        result = execve(path, type, g_env.env);
    }
    else
        ft_closepipes(fd);
    if (result == -1)
        return (1);
    waitpid(pid, 0, 0);
    command.fd[0] = fd[0];
    command.fd[1] = fd[1];
    free(arg);
    ft_free_dbl_str(type);
    free(path);
    return (0);
}

bash % ./minishell
->ls | wc -l
-------------------
ls      token->type->context keyword                    token->type->id 0               token->t_flag 0
|       token->type->context pipe                       token->type->id 6               token->t_flag 6
wc      token->type->context keyword                    token->type->id 0               token->t_flag 0
l       token->type->context arg                        token->type->id 13              token->t_flag -1
-------------------
-------------------
ls      token->type->context keyword                    token->type->id 0               token->t_flag 0
-------------------
pipe_exec
pipe_exec command count 1
type : /bin/ls
*****************
-------------------
wc      token->type->context keyword                    token->type->id 0               token->t_flag 0
-       token->type->context option                     token->type->id 7               token->t_flag 5
l       token->type->context arg                        token->type->id 13              token->t_flag -1
-------------------
pipe_exec
pipe_exec command count 2
arg -
arg -l
type : /usr/bin/wc
type : -l
*****************
->       8
-------------------
8       token->type->context string                     token->type->id 12              token->t_flag -1
        token->type->context string                     token->type->id 12              token->t_flag -1
-------------------
**bash: 8: command not found
->zsh: segmentation fault  ./minishell**

Upvotes: 0

Views: 118

Answers (1)

John Bollinger
John Bollinger

Reputation: 181339

Your ft_closepipes() function dupes the read end of the pipe onto STDIN_FILENO. You execute that in the parent process, which causes exactly the effect you describe. The parent's standard input is redirected to the read end of the (current) pipe.

That happens to work out ok for the processes in the pipeline (but see below), because they each inherit their standard input from their parent, and you start them in order from left to right. But it leaves the shell itself consuming the output of the last process as its own input.

And that brings up the other point: your ft_openpipes() function redirects the caller's standard output to the specified pipe, but you don't want to do that for the last process in the pipeline. It was a bit fortuitous to combine that error with the other, however, because it made very clear what the nature of the problem is.

For the parent, one alternative would be to dupe the standard input FD before setting up the pipeline, to preserve it, then dupe it back afterward. That would be a relatively easy retrofit, but I think it's poor form. Although you would need more of a rework to accomplish it, better would be to avoid ever redirecting the parent's file descriptors at all.

As for the segfault, that's probably a result of the child process returning to the caller of pipe_exec() in the event that its execve() call returns. It ought instead to terminate, just as would (eventually) happen if it had successfully started the the requested program. Personally, I would go with something like this:

            // ...
            result = execve(path, type, g_env.env);
            assert(result == -1);
            perror(path);
            _exit(EXIT_FAILURE);

Upvotes: 0

Related Questions