1. Can you think of why we use `fork/execvp` instead of just calling `execvp` directly? What value do you think the `fork` provides?
> **Answer**:
> `fork()` creates a new child process, which allows the shell to remain running while executing a new command.
> If we called `execvp()` directly in the shell process, the shell itself would be replaced by the new program, preventing it from executing further commands.
> `fork()` make sure that:
> - The shell can continue to accept user input after running a command.
> - Parallel execution is possible when multiple processes need to run.
> - Process management (e.g., monitoring and handling child processes) is possible.
2. What happens if the fork() system call fails? How does your implementation handle this scenario?
> **Answer**:
> If `fork()` fails, it returns `-1`, indicating that the system could not create a new process.
> This can happen due to too many processes running.
> In my implementation, I handle this by:
> - Printing an error message using `perror("fork")`.
> - Returning an error code `ERR_EXEC_CMD` from `exect_cmd()` to indicate failure.
3. How does execvp() find the command to execute? What system environment variable plays a role in this process?
> **Answer**:
> `execvp()` searches for the specified command in the directories listed in the `PATH` environment variable.
> The `PATH` variable contains a colon-separated list of directories where executables are located.
> If the command is found, it execute the command.
> If the command is not found, `execvp()` returns `-1` and sets `errno` to `ENOENT` (No such file or directory).
4. What is the purpose of calling wait() in the parent process after forking? What would happen if we didn’t call it?
> **Answer**:
> `wait()` makes the parent process (shell) wait for the child process to complete before continuing.
> If the program didn’t call `wait()`, the child process would still exist in the process table even though it has finished execution and the shell would continue executing commands without knowing the child's exit status.
> Using `waitpid()` allows the shell to correctly track child processes and retrieve their exit statuses.
5. In the referenced demo code we used WEXITSTATUS(). What information does this provide, and why is it important?
> **Answer**:
> `WEXITSTATUS(status)` extracts the exit code from a terminated child process which tells us how the command executed:
> - `0` means success.
> - Nonzero values indicate errors or failures.
> Tracking the exit status is important for:
> - Handling command failures properly.
> - Debugging easier.
6. Describe how your implementation of build_cmd_buff() handles quoted arguments. Why is this necessary?
> **Answer**:
> Our `build_cmd_buff()` implementation ensures **quoted arguments are treated as a single argument** by:
> - Iterating through the command input character by character.
> - Detecting quoted sections and preserving spaces inside them.
> - Everything inside the quotes is treated as one argument.
> This is necessary because:
> - Standard tokenization removes extra spaces, which would break quoted strings.
> - Without this, `echo "hello world"` would be parsed as two arguments instead of one.
> - Many shell commands rely on proper argument grouping for correct execution.
7. What changes did you make to your parsing logic compared to the previous assignment? Were there any unexpected challenges in refactoring your old code?
> **Answer**:
> Changes made:
> - Removed pipe handling: The previous assignment’s `build_cmd_list()` function split commands based on the `|` character, treating each command separately. Since this assignment only handles single commands without pipes, the program simplified the parsing to focus on argument extraction.
> - The old version of `build_cmd_list()` used `strtok()`, which split arguments on spaces indiscriminately.
> - The new implementation properly detects quoted arguments, ensuring spaces inside quotes are preserved.
> - Stores the last command’s exit status and allows users to retrieve it using `rc`.
> **Challenges faced:**
> - Handling spaces properly: The original code relied on `strtok()`, which doesn’t correctly handle quoted arguments. I had to implement a manual character-by-character parsing approach.
8. For this quesiton, you need to do some research on Linux signals. You can use [this google search](https://www.google.com/search?q=Linux+signals+overview+site%3Aman7.org+OR+site%3Alinux.die.net+OR+site%3Atldp.org&oq=Linux+signals+overview+site%3Aman7.org+OR+site%3Alinux.die.net+OR+site%3Atldp.org&gs_lcrp=EgZjaHJvbWUyBggAEEUYOdIBBzc2MGowajeoAgCwAgA&sourceid=chrome&ie=UTF-8) to get started.
- What is the purpose of signals in a Linux system, and how do they differ from other forms of interprocess communication (IPC)?
> **Answer**:
> Signals notify processes of events asynchronously (e.g., termination, interruption).
> Unlike other mechanisms like shared memory or pipes, signals:
> - Do not carry data, they simply notify a process to take action.
> - Can be sent by the kernel (e.g., `SIGKILL` when a process exceeds memory limits).
> - Require minimal system resources.
- Find and describe three commonly used signals (e.g., SIGKILL, SIGTERM, SIGINT). What are their typical use cases?
> **Answer**:
> - `SIGKILL` immediately terminates a process.
> - Cannot be caught or ignored.
> - Used for forceful termination (`kill -9 PID`).
>
> - `SIGTERM` gracefully requests process termination.
> - Can be caught to allow cleanup before exit.
> - Default signal for `kill PID`.
>
> - `SIGINT` is sent when a user presses `Ctrl+C`.
> - Default behavior is to terminate the process.
> - Can be caught to prevent accidental shutdown.
- What happens when a process receives SIGSTOP? Can it be caught or ignored like SIGINT? Why or why not?
> **Answer**:
> `SIGSTOP` pauses a process but does not terminate it.