So I implemented a program that takes an input file, two command strings and an output file to mimick the behaviour of running :
<input cmd1 -option | cmd2 -option > output
that's called like this :
./pipe input "cmd1 -opt" "cmd2 -opt" output
and I did without using the wait
system call since while there are open file descriptors on the pipe I opened, the exec'd commands will wait for one another. that is the pipe takes care of coordination IIUC.
But I feel like I am doing it wrong since it seems that people I using wait()
out of convention. Is it reasonnable to think it is not necessary in my case since I only need the return value of the second command and the pipe ensures communication or am I missing something ? What else am I doing wrong in terms of code structure or style ?
Here is my code :
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include "pipex.h"
static const char g_cmd_not_found[] = {
"command not found: "
};
static const char g_empty_string[] = {
"The name of the input or output file cannot be an empty string\n"
};
static int open_or_die(char *filename, int flags, mode_t mode)
{
int fd;
fd = open(filename, flags, mode);
if (fd == -1)
{
if (*filename == '\0')
write(STDERR_FILENO, g_empty_string, sizeof(g_empty_string));
else
{
write(STDERR_FILENO, "pipex: ", sizeof("pipex: "));
perror(filename);
}
exit(EXIT_FAILURE);
}
return (fd);
}
static void pipe_or_die(int *pipe_fds)
{
int r;
r = pipe(pipe_fds);
if (r == -1)
{
write(STDERR_FILENO, "pipex: ", sizeof("pipex: "));
perror("pipe");
exit(EXIT_FAILURE);
}
}
static void file_is_ok_or_die(char **cmdv, char **pathvar_entries)
{
if (access(cmdv[0], X_OK) == -1)
{
write(STDERR_FILENO, "pipex: ", sizeof("pipex: "));
if (cmdv[0][0] != '/')
{
write(STDERR_FILENO, g_cmd_not_found, sizeof(g_cmd_not_found));
ft_puts_stderr(cmdv[0]);
}
else
perror(cmdv[0]);
free_null_terminated_array_of_arrays(cmdv);
free_null_terminated_array_of_arrays(pathvar_entries);
if (errno == ENOENT)
exit(127);
else if (errno == EACCES)
exit(126);
else
exit(EXIT_FAILURE);
}
}
void execute_pipeline(char *cmd_str, int read_from, int write_to, char **env)
{
char **pathvar_entries;
char **cmdv;
redirect_fd_to_fd(0, read_from);
redirect_fd_to_fd(1, write_to);
pathvar_entries = ft_split(get_path_var(env), ':');
cmdv = ft_split(cmd_str, ' ');
if (!pathvar_entries || !cmdv)
{
write(STDERR_FILENO, "pipex: ", sizeof("pipex: "));
perror("malloc");
free_null_terminated_array_of_arrays(cmdv);
free_null_terminated_array_of_arrays(pathvar_entries);
exit(EXIT_FAILURE);
}
cmdv[0] = get_command_path(cmdv, get_pwd_var(env), pathvar_entries);
file_is_ok_or_die(cmdv, pathvar_entries);
execve(cmdv[0], cmdv, env);
free_null_terminated_array_of_arrays(cmdv);
free_null_terminated_array_of_arrays(pathvar_entries);
}
int main(int ac, char **av, char **envp)
{
int pipefd[2];
int child_pid;
int infile_fd;
int outfile_fd;
if (ac != 5)
print_usage_exit();
pipe_or_die(pipefd);
child_pid = fork();
if (child_pid == -1)
perror("fork");
else if (child_pid == 0)
{
infile_fd = open_or_die(av[1], O_RDONLY, 0000);
close(pipefd[0]);
execute_pipeline(av[2], infile_fd, pipefd[1], envp);
}
else
{
outfile_fd = open_or_die(av[ac - 1], O_WRONLY | O_CREAT | O_TRUNC, 0666);
close(pipefd[1]);
execute_pipeline(av[3], pipefd[0], outfile_fd, envp);
}
return (EXIT_SUCCESS);
}
```
"pipex.h"
? It seems to be essential for this program, but I don't know what I need to install to compile this. \$\endgroup\$redirect_fd_to_fd()
that plain olddup2()
doesn't do, for instance. And I'd like to know whetherft_split()
is any better than passing the command string tosh -c
, for a much simplerexec()
setup. \$\endgroup\$execve
. Program usage is like./pipex infile "cmd1 -op1 -op2" "cm2 -op1 -op2" outfile
which is why I have to use a split, to split the command string.redirect_fd_to_fd
doesn't to much thandup2
but for callingclose
before dup2. \$\endgroup\$