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);
}
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:
In
main(), you need toclose(out)after you dup it. You're doing that after you callexe(). Butexe()never returns, because it callsexecve(). So move that call before the call toexe().