- 📌 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
.