Redirection Save-Restore pattern
Example: redirecting STDOUT (target = 1) to an opened file (fd = 3):
⠀
saved_fd = dup(target)
dup2(fd, target)
close(fd)
dup2(saved_fd, target)
close(saved_fd)
⠀
If dup() or dup2() fail, the function returns -1.
If setup_redirection() fails, the caller closes the original fd, thus preventing leaks.
Pipe file descriptors
⠀
pipe(pipe_fd)
fork() left: close(pipe_fd[0]), dup2(pipe_fd[1], STDOUT), close(pipe_fd[1])
fork() right: close(pipe_fd[1]), dup2(pipe_fd[0], STDIN), close(pipe_fd[0])
parent: close(pipe_fd[0]), close(pipe_fd[1])
⠀
Each process closes the ends that it won't use.
The parent closes both ends after forking both children.
Heredoc pipe lifecycle
- handle_heredoc() calls pipe(pipe_fd)
- Lines are written to pipe_fd[1]
- pipe_fd[1] is closed after reading completes
- pipe_fd[0] is returned and stored in
redir->heredoc_fd
- During execution, handle_redir() consumes it by passing it to setup_redirection() which dup2's it into STDIN and closes it
- On CTRL C, both pipe ends are closed and restore_stdin() reopens /dev/tty on fd 0
- On AST cleanup, free_ast() closes any strictly positive heredoc_fd that was not consumed
Heredoc CTRL C (fd 0 restore)
heredoc_sigint_handler() closes STDIN_FILENO to break readline().
After pipe fds are closed, restore_stdin() opens /dev/tty.
If open() returns 0 (fd 0 was the lowest available) then STDIN is already correctly pointing to the tty, so no dup2/close needed.
The condition if (tty_fd != STDIN_FILENO) prevents the scenario of the destructive close(0).