6 Final program - Aryalexa/42-pipex GitHub Wiki
Now that we know how processes work, how to create pipes, and to redirect inputs and outputs... We can create a program that replicates the behavior of doing < infile cmd1 | cmd2 > outfile
in bash. Or even < infile cmd1 | ... | cmdn > outfile
.
Our program pipex
will receive al least 4 arguments where the first one will be the input file it will read from, and the last one will be the output file that will contain the final outputs.
./pipex infile cmd1 cmd2 cmd3 outfile
This is what our program will do step by step:
// check arguments received
check_args(argc, argv);
// fetch the files and the commands
char *infile = get_infile(argc, argv);
char *outfile = get_outfile(argc, argv);
char **cmds = get_commands(argc, argv);
// open the input file - handle possible errors (existence, permissions, ...)
int fdin = open(infile, ...)
dup2(fdin, 0); // set read from fdin by default
// execute the first command: cmds[0]
my_pipex(cmds[0], env);
// while there are more commands, execute the command i: cmds[i], except the last one
...
// for the last execution we want to link 1 to the output file before the execution.
// this execution reads from the last pipe and writes to the output file.
int fdout = open(outfile, ...) // create it if it does not exist already
dup2(fdout, 1);
my_exec(cmds[i], env);
my_pipex(cmd, env)
executes a program using a pipe. The command will read from an already set up input, writes in a new pipe, and sets the stdin_filno (0) as the reading part of the pipe for the next command to use.
// the execute should be done in a child process
// we need to create a pipe so the output of the command is redirected to the pipe,
// and the parent can have access to it
// the parent should set the read from the pipe by default, so the next execution reads from it.
int pfd[2];
if (pipe(pfd) == -1) // pipe error
exit();
int pid = fork();
if (pid == -1) // fork error
exit();
if (pid == 0) {
close(pfd[0]); // close reading end from pipe
dup2(pfd[1], 1); // set write on pipe
close(pfd[1]);
my_exec(cmd, env);
} else {
close(pfd[1]);
dup2(pfd[0], 0); // set read from pipe
close(pfd[0]);
wait(NULL); // wait for child to terminate, so it is finished before more commands
}
my_exec
should use execve
so we need to do some prepping
char **cmd_args = split(cmd, " "); // split the command into the name and arguments
char *cmd_path = get_path(cmd_args[0], env); // using the env received from main.
// get_path uses the "PATH=path1:path2:..." variable in env.
// It searches the path where the command is (path1/cmd? path2/cmd? ...) and returns it.
cmd_args[0] = cmd_path; // save the path as the first argument instead of only the name
execve(cmd_path, cmd_args, env);
perror(); // exec error
All this is just a way to understand what is happening and there is room for code optimization and error handling, of course. :)