Can we close file descriptors 3 and 4? I'm having trouble with it

127 Views Asked by At

I am facing challenges with managing file descriptors in my C program, specifically in the context of my Pipex project. Upon program exit, Valgrind reports that 5 file descriptors are open, with 3 being standard descriptors. In particular, file descriptor 5 was opened with the pipe function in the pipe.c file at line 29, called from the here_doc function at address 0x109622 in my program.

I have included a snippet from the Valgrind report below for reference: valgrind 1 valgrind 2

code : pipex.c

#include "../inc/pipex.h"

char    *find_path(char *cmd, char **ev)
{
    char    **allpaths;
    char    *exe;
    int     i;

    i = 0;
    while (ev[i])
    {
        if (ft_strnstr(ev[i], "PATH=", 5) != NULL)
            break ;
        i++;
    }
    allpaths = ft_split((ev[i] + 5), ':');
    i = 0;
    while (allpaths[i])
    {
        exe = build_executable_path(cmd, allpaths[i]);
        if (exe != NULL)
        {
            ft_clear_tab(allpaths);
            return (exe);
        }
        i++;
    }
    ft_clear_tab(allpaths);
    return (NULL);
}

void    exe(char **ev, char *av)
{
    char    **cmd;
    char    *cmd_exe;

    cmd = ft_split(av, ' ');
    cmd_exe = find_path(cmd[0], ev);
    if (cmd_exe == NULL)
    {
        perror("pipex: command not found");
        ft_putendl_fd(cmd[0], 2);
        ft_clear_tab(cmd);
        exit(EXIT_FAILURE);
    }
    if (execve(cmd_exe, cmd, ev) == -1)
    {
        perror("pipex: execution error");
        ft_putendl_fd(cmd[0], 2);
        ft_clear_tab(cmd);
        free(cmd_exe);
        exit(EXIT_FAILURE);
    }
}

void    do_pipe(char **av, char **ev)
{
    pid_t   pid;
    int     fd[2];

    if (pipe(fd) == -1)
        error_arg();
    pid = fork();
    if (pid == 0)
    {
        close(fd[0]);
        dup2(fd[1], STDOUT_FILENO);
        exe(ev, av[0]);
    }
    else
    {
        close(fd[1]);
        dup2(fd[0], STDIN_FILENO);
        waitpid(pid, NULL, 0);
    }
}

void here_doc(char *limiter, int ac)
{
    int fd[2];
    pid_t pid;
    char *line;

    if (ac < 6)
        error_arg();

    if (pipe(fd) == -1 || (pid = fork()) == -1)
    {
        perror("Pipe or Fork");
        exit(EXIT_FAILURE);
    }

    if (pid == 0)
    {
        close(fd[0]);

        while (gnl(&line) > 0)
        {
            if (ft_strncmp(line, limiter, ft_strlen(limiter)) == 0 || line[0] == '\0')
                exit(EXIT_SUCCESS);
            write(fd[1], line, ft_strlen(line));
        }
        free(line);
        close(fd[1]);
        exit(EXIT_SUCCESS);
    }
    else
    {
        close(fd[1]);
        dup2(fd[0], STDIN_FILENO);
        close(fd[0]);
        wait(NULL);
    }
}
int main(int ac, char **av, char **ev) 
{
    int in;
    int out;
    int i;

    if (ac < 5)
        error_arg();
    if (ft_strncmp(av[1], "here_doc", 8) == 0) 
    {
        i = 3;
        out = open(av[ac - 1], O_WRONLY | O_CREAT | O_TRUNC, 0777);
        if (out == -1)
            exit(EXIT_FAILURE);
        here_doc(av[2], ac);
    } 
    else
    {
        i = 2;
        out = open(av[ac - 1], O_WRONLY | O_CREAT | O_TRUNC, 0777);
        in = open(av[1], O_RDONLY, 0777);
        if (out == -1 || in == -1)
            exit(EXIT_FAILURE);
        dup2(in, 0);
    }
    while (i < ac - 2)
        do_pipe(av + i++, ev);
    dup2(out, 1);
    exe(ev, av[ac - 2]);
    close(in);
    close(out);
    return 0;
}

utils.c

#include "../inc/pipex.h"
#define BUFFER_SIZE 100000

void    error_arg(void)
{
    write(2, "\033[1;31m!! we need more than 5 arguments :c !!\n\033[0m\n", 49);
    write(2, "\033[1;32mExample: ./Here_doc cmd1 cmd2 outfile\033[0m\n", 50);
    write(2,
        "\033[1;32mExample 2: ./pipex infile cmd1 cmd2 cmd3 ... outfile\033[0m\n",
        65);
    exit(EXIT_FAILURE);
}

#include <unistd.h>
#include <stdlib.h>

int gnl(char **line)
{
    char *buffer;
    int i = 0;
    int r;
    char c;

    buffer = (char *)malloc(10000);
    if (!buffer)
        return (-1);
    while ((r = read(0, &c, 1)) > 0 && c != '\n' && c != '\0')
        buffer[i++] = c;

    buffer[i] = '\0'; 

    *line = buffer;

    if (r > 0 || i > 0)
        return (1); 
    else
        return (r);
}

void    ft_clear_tab(char **tab)
{
    size_t  i;

    i = 0;
    while (tab[i])
    {
        free(tab[i]);
        i++;
    }
    free(tab);
}

char    *build_executable_path(char *cmd, char *path)
{
    char    *part_path;
    char    *exe;

    part_path = ft_strjoin(path, "/");
    exe = ft_strjoin(part_path, cmd);
    free(part_path);
    if (access(exe, F_OK | X_OK) == 0)
        return (exe);
    free(exe);
    return (NULL);
}

1

There are 1 best solutions below

7
Barmar On BEST ANSWER

After you dup one of the pipe FDs to stdin or stdout, you no longer need the original pipe FD to be open. So the code that redirects should look like:

    if (pid == 0)
    {
        close(fd[0]);
        dup2(fd[1], STDOUT_FILENO);
        close(fd[1]);
        exe(ev, av[0]);
    }
    else
    {
        close(fd[1]);
        dup2(fd[0], STDIN_FILENO);
        close(fd[0]);
        waitpid(pid, NULL, 0);
    }

In main(), you need to close(out) after you dup it. You're doing that after you call exe(). But exe() never returns, because it calls execve(). So move that call before the call to exe().

    dup2(out, STDOUT_FILENO);
    close(in);
    exe(ev, av[ac - 2]);