Live data from Hacker News

Fixing Ctrl+C in Rust terminal apps: Child process management

fiveonefour.com

21–30 of 62 posts

Re: Fixing Ctrl+C in Rust terminal apps: Child process management

#21

Are you saying that after the main process has exited, child processes can still run and write to stdout/stderr?

Yes, this is easy to test too:

    #include 
    #include 
    #include 
    int main() {
        pid_t pid = fork();
        if (pid == 0) {
            sleep(1);
            printf("after parent died!\n");
            return 0;
        }
        return 0;
    }
You'll see the message printed out 1 second after the process ends.

Re: Fixing Ctrl+C in Rust terminal apps: Child process management

#22
The graceful shutdown stuff is good, but always piping the output of child processes is not necessarily the right thing to do. Some processes need stdin (what if it's a shell?) and some processes will be checking if stdout is a tty. What you should do (and Rust doesn't make this easy) is allocate a new pty for your child processes if your own stdout is a tty. Some programs default to this (eg: ssh), others make it configurable (eg: docker).

You're also missing the standard techniques for managing and reaping your children, which I don't see mentioned. You don't need to maintain a registry of child processes for example, at least on Linux there are a few things you can do for this without any global state (PR_SET_PDEATHSIG, PR_SET_CHILD_SUBREAPER, PID namespaces). On MacOS you can write a routine to reap children like a linux init() process would. The registry approach is also fragile: what if library code spawns children?

Also, if the terminal is in raw mode then you'll never get ctrl+C. This is really about signal handling. You also can't gracefully shutdown if you get a SIGKILL, which is why PR_SET_PDEATHSIG and PID namespaces are very nice - they guarantee descendants get killed.

Re: Fixing Ctrl+C in Rust terminal apps: Child process management

#23

Are you saying that after the main process has exited, child processes can still run and write to stdout/stderr?

Child processes are created using, generally, 2 syscalls: fork, then exec. When you fork, all file descriptors the main process has open are copied, and are now open in two places. Then, when the child calls exec (to transform itself into the target program), all file descriptors stay open in the new process (unless a specific fd is explicitly configured otherwise, FD_CLOEXEC).

Standard output are just file descriptors with the number 0, 1, and 2, and you can use the dup2 syscall to assign those numbers to some pipes that you originally created before you fork. Now the standard output of your child process is going to those pipes in your parent process. Or you can close those file descriptors, which will prevent the child process from reading/writing them at all. Or you can do nothing, and the copied file descriptors from the parent still apply.

Conceptually, you think of "spawning a child" as something that is in some kind of container (the parent process), but the underlying mechanics are not like this at all, and processes don't actually exist in a "tree", they just happen to keep a record of their "parent process ID" so the OS knows who to notify when the process dies.

Re: Fixing Ctrl+C in Rust terminal apps: Child process management

#25
post #22

The graceful shutdown stuff is good, but always piping the output of child processes is not necessarily the right thing to do. Some processes need stdin (what if it's a shell?) and some processes will be checking if stdout is a tty. What you should do (and Rust doesn't make this easy) is allocate a new pty for your child processes if your own stdout is a tty. Some programs default to this (eg: ssh), others make it co…

>Also, if the terminal is in raw mode then you'll never get ctrl+C.

The process/thread/task won't receive SIGINT, true. But I believe it will see the character ETX (ASCII 3). Programs that use raw mode input need to do their own keystroke processing.

Re: Fixing Ctrl+C in Rust terminal apps: Child process management

#26
post #22

The graceful shutdown stuff is good, but always piping the output of child processes is not necessarily the right thing to do. Some processes need stdin (what if it's a shell?) and some processes will be checking if stdout is a tty. What you should do (and Rust doesn't make this easy) is allocate a new pty for your child processes if your own stdout is a tty. Some programs default to this (eg: ssh), others make it co…

Don't use PR_SET_PDEATHSIG. That way lies pain.

Re: Fixing Ctrl+C in Rust terminal apps: Child process management

#28
post #24

I hate to be the guy, but I could barely see the code snippets. Is contrast an issue for anyone else? Reader mode improves thins slightly but at the cost of code being unhighlighted and wrapping like crazy.

LGTM on a MBP + Brave browser.

Re: Fixing Ctrl+C in Rust terminal apps: Child process management

#29

Are you saying that after the main process has exited, child processes can still run and write to stdout/stderr?

Child processes are created using, generally, 2 syscalls: fork, then exec. When you fork, all file descriptors the main process has open are copied, and are now open in two places. Then, when the child calls exec (to transform itself into the target program), all file descriptors stay open in the new process (unless a specific fd is explicitly configured otherwise, FD_CLOEXEC). Standard output are just file descripto…

fork() when followed by exec*() is generally inefficient. That's why vfork(), clone(), and clone3() exist. There's no point in duplicating (even CoW) the entire kernel side and libc internal state of a process if it's going to be replaced with exec*() by a new, unrelated process.

Re: Fixing Ctrl+C in Rust terminal apps: Child process management

#30
post #24

I hate to be the guy, but I could barely see the code snippets. Is contrast an issue for anyone else? Reader mode improves thins slightly but at the cost of code being unhighlighted and wrapping like crazy.

Yes, the contrast of the code examples is not great. Grey on grey, light pastels and orange does not combine into an easy-to-read color palette for me.
Post reply on HN