- π Project Overview
- π‘ Getting Started
- π» Program Usage
- π Core Logic and Implementation
- β Error Handling
- π§ Memory Management
- π§ Tester - Mandatory Part
Pipex
is a project aimed at understanding and implementing the piping mechanism in UNIX. The goal is to create a program that mimics the behavior of the shell in handling input/output redirection between commands. By completing this project, you'll deepen your understanding of UNIX system calls like:
pipe()
π οΈfork()
πΆdup2()
πexecve()
π
- Replicate the functionality of this shell command:
< infile cmd1 | cmd2 > outfile
- How it works:
- Takes two commands.
- Reads input from a file.
- Pipes the output of the first command into the second command.
- Writes the final output to another file.
- System: Unix-based system (e.g., Linux, macOS) π₯οΈ
- Compiler: GCC or any standard C compiler βοΈ
- Knowledge: Familiarity with system calls like
fork()
,pipe()
,dup2()
,execve()
π§ - Makefile: A Makefile that compiles with
-Wall
,-Werror
,-Wextra
flags, without relinking unnecessary files. π
You'll use these functions:
open()
,close()
,read()
,write()
πmalloc()
,free()
πΎperror()
,strerror()
β οΈ access()
,dup()
,dup2()
πexecve()
,exit()
,fork()
,pipe()
,unlink()
π οΈwait()
,waitpid()
β³
pipex/
βββ main.c # Entry point of the program
βββ error/ # Error handling functionality
β βββ error_pipex.c # Handles errors specific to pipex
βββ setup/ # Setup and initialization
β βββ pipex_setup.c # Sets up the pipex environment (files, pipes)
βββ free_memory/ # Memory management
β βββ free_pipex.c # Frees allocated memory in pipex
βββ command/ # Command execution and validation
β βββ command_execution.c # Executes the parsed commands
β βββ command_validation.c # Validates commands by checking PATH and permissions
βββ includes/ # Header files
β βββ pipex.h # Contains function prototypes and struct definitions
βββ Makefile # Makefile to compile the project
Explanation:
- Main file (
main.c
): The entry point for your Pipex program. - Error handling (
error/
): Manages any errors that occur in the program. - Setup (
setup/
): Handles setting up pipes, redirections, and the environment. - Memory management (
free_memory/
): Contains functions to free dynamically allocated memory. - Command execution (
command/
): Functions related to command parsing, validation, and execution. - Includes (
includes/
): Stores header files likepipex.h
. - Makefile: Automates the compilation process.
Use the following format to run the pipex
program:
./pipex infile "cmd1" "cmd2" outfile
infile
: Input file πcmd1
: First command πcmd2
: Second command π, takescmd1
output as input.outfile
: Output file where results will be stored πΎ
./pipex infile "ls -l" "wc -l" outfile
- Input:
infile
π - Command 1:
ls -l
(lists files) π - Command 2:
wc -l
(counts lines) π’ - Output: Written to
outfile
πΎ
This section explains the files and commands you need to provide when running the program.
-
Input file (
infile
) π:
A file containing the input data. The content of this file will be passed to the first command (cmd1
).
Example: A file with a list of file names or text content. -
Commands π₯οΈ:
Two commands must be passed as arguments:cmd1
: The first command reads frominfile
.cmd2
: The second command processes the output ofcmd1
.
Example commands:ls -l
,grep
,wc -l
.
-
Output file (
outfile
) πΎ:
The final output ofcmd2
will be written here. If it doesnβt exist, it will be created. If it exists, it will be overwritten.
Example: A text file that stores the result of the command chain.
./pipex infile "cat" "wc -l" outfile
infile
: A file that contains some text.- Command 1:
cat
reads the content ofinfile
. - Command 2:
wc -l
counts the number of lines. outfile
: The result (line count) will be saved here.
This section describes what the output will look like after the program is executed.
-
Standard Output π»:
No output will be printed to the terminal. The results are saved directly tooutfile
(unless debugging). -
Output File Content π:
After execution,outfile
will contain the result of the second command (cmd2
).- Example: If using
wc -l
, the output will be the line count ofinfile
.
- Example: If using
-
Error Handling
β οΈ :
If an error occurs (e.g., missing files, permission errors, invalid commands), an error message will be displayed, and the program will exit without modifyingoutfile
.
./pipex infile "cat" "wc -l" outfile
If the infile
contains:
Hello World
How are you?
I'm learning Pipex.
The outfile
will contain:
3
This output represents the number of lines in infile
.
To better understand the core logic, please refer to the visual diagram below, which outlines the entire process:
-
Setting up the
PATH
Environment π οΈ
The program retrieves and parses thePATH
variable to locate executables. -
Command Parsing and Validation π
Each command is validated and located, ensuring it's executable. -
Pipe Creation and Redirection π
Pipes are created to link the output of the first command to the input of the second. -
Forking Child Processes πΆ
Two child processes are created to run the commands concurrently. -
Executing Commands π
Commands are executed usingexecve()
, which replaces the current process with the new one. -
Closing File Descriptors πͺ
Properly closes all file descriptors to prevent resource leaks.
-
Common Errors:
- Invalid Command: Prints "Error: Command not found" and exits β.
- Permission Denied: Handles permissions issues when reading or writing files π.
- Broken Pipe: If the first command fails, the program handles the failure gracefully
β οΈ .
-
Systematic Error Messages:
- Custom error messages are shown for incorrect arguments, permission errors, or failed system calls π’.
-
Dynamic Memory Allocation
All dynamically allocated memory (e.g., for command arguments) must be freed after use to avoid memory leaks πΎ. -
Avoiding Memory Leaks
Use tools likevalgrind
to check for memory leaks and ensure all allocated memory is freed properly before the program exits π¨.
- Command:
valgrind ./pipex infile "cat" outfile
- Expected Behavior: The program should print an error message (e.g., "Error: Invalid number of arguments") and exit.
- Reason: The mandatory part requires exactly 4 arguments (infile, command1, command2, outfile). Any deviation should trigger an error.
- Command:
valgrind ./pipex nonexistentfile "cat" "wc -l" outfile
- Expected Behavior: The program should print an error like
Error: Could not open input file
and exit. - Reason: If
infile
does not exist, the program should handle this error gracefully, informing the user that the input file cannot be opened.
- Expected Behavior: The program should print an error like
- Command:
chmod -r infile && valgrind ./pipex infile "cat" "wc -l" outfile
- Expected Behavior: The program should print an error like
Error: Permission denied for input file
and exit. - Reason: If the program cannot read the input file due to permission issues, it should handle this error gracefully.
- Expected Behavior: The program should print an error like
- Command:
chmod -w outfile && valgrind ./pipex infile "cat" "wc -l" outfile
- Expected Behavior: The program should print an error like
Error: Permission denied for output file
and exit. - Reason: If the program cannot write to the output file, it should handle this error gracefully.
- Expected Behavior: The program should print an error like
- Command:
valgrind ./pipex infile "invalidcommand" "wc -l" outfile
- Expected Behavior: The program should print an error like
Error: Command not found
and exit. - Reason: If
execve()
cannot find or execute the command, the program should catch the error and inform the user that the command is invalid.
- Expected Behavior: The program should print an error like
- Command:
chmod -x /bin/cat && valgrind ./pipex infile "/bin/cat" "wc -l" outfile
- Expected Behavior: The program should print an error like
Error: Permission denied for command /bin/cat
and exit. - Reason: If a command exists but lacks execution permissions, the program should handle this and exit with a meaningful error message.
- Expected Behavior: The program should print an error like
- Command:
valgrind ./pipex emptyfile "cat" "wc -l" outfile
- Expected Behavior: The program should write
0
tooutfile
since the input file is empty. - Reason: An empty input file is a valid case, but it can lead to a
wc -l
output of0
. Ensure the program handles empty files correctly and outputs the expected result.
- Expected Behavior: The program should write
- Command:
valgrind ./pipex infile "invalidcommand" "wc -l" outfile
- Expected Behavior: The program should handle this gracefully without crashing or leaving file descriptors open.
- Reason: If the first command fails, thereβs no output to pipe to the second command. The program should handle the broken pipe case and avoid hanging.
- Command:
./pipex infile "cat" "wc -l" /invalid/path/to/outfile
- Expected Behavior: The program should print an error like
Error: Could not open output file
and exit. - Reason: If the output file path is invalid or points to a directory that doesnβt exist, the program should handle this error.
- Expected Behavior: The program should print an error like
- Command: Test a scenario where too many file descriptors are open, preventing the program from opening more pipes or files.
- Expected Behavior: The program should print an error like
Error: Too many open files
and exit gracefully. - Reason: In cases where the system runs out of available file descriptors, the program should handle this failure scenario gracefully.
- Expected Behavior: The program should print an error like
- Command: Modify the
PATH
environment variable to an invalid path, then run:PATH=/invalid/path valgrind ./pipex infile "cat" "wc -l" outfile
- Expected Behavior: The program should print an error like
Error: Command not found
for bothcat
andwc -l
. - Reason: If the
PATH
environment variable is invalid, commands likecat
andwc
wonβt be found, and the program should handle this case gracefully.
- Expected Behavior: The program should print an error like
- Command: Create a read-only directory and try writing an output file to it:
mkdir readonlydir && chmod -w readonlydir && valgrind ./pipex infile "cat" "wc -l" readonlydir/outputfile
- Expected Behavior: The program should print an error like
Error: Permission denied for output file
and exit. - Reason: If the output file cannot be created due to directory permissions, the program should fail gracefully.
- Expected Behavior: The program should print an error like
- Command:
valgrind ./pipex infile "cat" "'wc -l'" outfile
- Expected Behavior: The program should print an error like
Error: Invalid syntax or command not found
and exit. - Reason: If quotes are not handled correctly in commands, this can lead to the command being passed incorrectly to
execve()
. The program should identify and handle incorrect syntax.
- Expected Behavior: The program should print an error like
- Command:
valgrind ./pipex infile "cat" "wc -w" outfile
- Explanation:
cat
reads the contents ofinfile
, andwc -w
counts the words, writing the result tooutfile
.
- Command:
valgrind ./pipex infile "cat" "wc -l" outfile
- Explanation:
cat
reads the contents ofinfile
, andwc -l
counts the number of lines.
- Command:
valgrind ./pipex infile "cat" "wc -c" outfile
- Explanation:
cat
reads the contents ofinfile
, andwc -c
counts the number of characters (including whitespace and newlines).
- Command:
valgrind ./pipex infile "cat" "sort" outfile
- Explanation:
cat
outputs the content ofinfile
, andsort
sorts the lines alphabetically.
- Command:
valgrind ./pipex infile "cat" "rev" outfile
- Explanation:
cat
readsinfile
, andrev
reverses each line of text before writing tooutfile
.
- Command:
valgrind ./pipex infile "cat" "grep 'pattern'" outfile
- Explanation:
cat
reads the contents ofinfile
, andgrep 'pattern'
filters lines containing the pattern "pattern".
- Command:
valgrind ./pipex infile "cat" "grep -v '^$'" outfile
- Explanation:
cat
reads the file, andgrep -v '^$'
removes empty lines from the output.
- Command:
valgrind ./pipex infile "grep 'pattern'" "wc -l" outfile
- Explanation:
grep 'pattern'
searches for lines containing the word "pattern", andwc -l
counts how many lines matched the pattern.
- Command:
valgrind ./pipex infile "grep 'txt'" "wc -w" outfile
- Explanation:
grep 'txt'
searches for lines containing the word "txt", andwc -w
counts how many words match.
- Command:
valgrind ./pipex infile "grep -v '^$'" "wc -l" outfile
- Explanation:
grep -v '^$'
filters out empty lines, andwc -l
counts the number of non-empty lines.
- Command:
valgrind ./pipex infile "cat" "tr 'a-z' 'A-Z'" outfile
- Explanation:
cat
reads the contents ofinfile
, andtr 'a-z' 'A-Z'
converts all lowercase letters to uppercase.
- Command:
valgrind ./pipex infile "cat" "tr 'A-Z' 'a-z'" outfile
- Explanation:
cat
readsinfile
, andtr 'A-Z' 'a-z'
converts uppercase letters to lowercase.
- Command:
valgrind ./pipex infile "head -n 5" "cat" outfile
- Explanation:
head -n 5
reads the first 5 lines frominfile
, andcat
writes it tooutfile
.
- Command:
valgrind ./pipex infile "tail -n 5" "cat" outfile
- Explanation:
tail -n 5
reads the last 5 lines frominfile
, andcat
outputs them.
- Command:
valgrind ./pipex infile "ls -l" "grep 'txt'" outfile
- Explanation:
ls -l
lists files in the directory, andgrep 'txt'
filters files that contain "txt" in their names.
- Command:
valgrind ./pipex infile "cat" "uniq" outfile
- Explanation:
cat
reads the content ofinfile
, anduniq
filters out duplicate consecutive lines.
- Command:
valgrind ./pipex infile "sort" "uniq" outfile
- Explanation:
sort
sorts the lines alphabetically, anduniq
removes duplicate lines.
- Command:
valgrind ./pipex infile "sed 's/old/new/g'" "cat" outfile
- Explanation:
sed 's/old/new/g'
replaces all occurrences of "old" with "new" in the file.
- Command:
./pipex infile "grep -o 'word'" "wc -l" outfile
- Explanation:
grep -o 'word'
prints each occurrence of the word "word", andwc -l
counts how many times it appeared.
- Command:
valgrind ./pipex infile "cat -n" "cat" outfile
- Explanation:
cat -n
displays each line with its line number, andcat
writes it tooutfile
.