-
Notifications
You must be signed in to change notification settings - Fork 20
Introduction to proot‐rs workflow
This part deals with command line arguments, we use the library clap
to parse the arguments, the code is in proot-rs/cli.rs
Before starting the first subroutine, we need to complete the initialization of the filesystem. This mainly consists of checking if the path exists and regularizing the path.
Before entering the event loop, proot-rs starts the init process. Specifically, the proot-rs master process first spawns a new process by fork()
, and then calls execvp()
according to the file path and argument list specified in the command line arguments. At the same time, the parent process creates the first Tracee
instance based on the pid
of the child process and the FileSystem
initialized before, and registers it in the tracee list of the current survivor.
Before calling execvp()
, the child process first uses ptrace(PTRACE_TRACEME)
to let the parent process trace itself.
Then it sends a SIGSTOP
signal to itself, which is used to synchronize with the proot-rs tracer process. The latter needs to call ptrace(PTRACE_SETOPTIONS)
at this time, to set the ptrace option.
After waiting for the main process to acknowledge this signal, the child process continues to execute execvp()
, switching to execute the user-specified program.
The event loop part is the main part of the proot-rs master process, the proot-rs processes use waitpid(-1, &status, __WALL)
in a loop to wait for events from all tracees, which basically fall into the following categories.
-
Exited
: tracee exits actively, which generally means that the process calledexit()
. -
Signaled
: tracee is killed by a signal -
Stopped
: tracee is suspended because a signal is about to be delivered to tracee, this is calledsignal-delivery-stop
-
PtraceEvent
: tracee is paused because aPTRACE_EVENT_*
event has occurred, this is calledPTRACE_EVENT stops
. -
PtraceSyscall
: tracee was paused because it issued a system call, which is calledsyscall-stop
-
Continued
: tracee resumes from suspended state because it received theSIGCONT
signal. proot-rs does not need to pay attention to this event -
StillAlive
: proot-rs does not need to pay attention to this event
For some long-ago reason, the documentation on waitpid()
and ptrace()
is rather obscure and developers are prone to make mistakes in these areas.
Here is a sheet that describes the meaning of the status
value of the waitpid()
function: https://docs.google.com/spreadsheets/d/1JAm2fOYctoluSsMfNP8Dbt7HytdfEtRPm6vtGs8Kgr8/edit# gid=0
Normally, proot-rs
will exit after all tracees have exited, which means that proot-rs
may sometimes wait for a daemon to finish, even if the init process has long since finished. This is necessary because if the proot-rs
master process exits before the tracee, it can cause subsequent system calls to go untranslated. In the original version of proot
a --kill-on-exit
option was used to force the killing of the rest of the child processes after the init process exits. However, so far proot-rs does not implement this option.
The proot-rs master process will record the exit code of the init process and use it as its own exit code when it exits. However, if it detects that the init process was killed by a signal rather than exiting normally, then the exit code will be the value of that signal plus 128. This is consistent with the behavior of Bash: https://tldp.org/LDP/abs/html/exitcodes.html
Sometimes it is also possible that a bug in proot-rs itself causes an abnormal exit, in which case the exit code is 1 and an error log will be printed.
Note that for whatever reason the main proot-rs process terminates before the tracee process, all tracees are killed to prevent accidental escape of the tracee. This is done with the option PTRACE_O_EXITKILL
.