@@ -14,10 +14,100 @@ pub use std::sync::Arc;
1414use sysdefs:: constants:: lind_platform_const:: MAX_CAGEID ;
1515use sysdefs:: data:: fs_struct:: SigactionStruct ;
1616
17+ /// Represents how a cage terminated, mirroring the two primary POSIX
18+ /// process termination modes.
19+ ///
20+ /// A process may either:
21+ /// - exit normally via `exit()` with an exit code, or
22+ /// - be terminated by a signal.
23+ ///
24+ /// This enum stores the termination information in a structured form
25+ /// before it is encoded into the traditional POSIX wait status returned
26+ /// by `waitpid`.
27+ ///
28+ /// TODO: Currently, Lind-Wasm only supports normal exit and signal
29+ /// termination. Job-control states such as `Stopped` and `Continued`
30+ /// are not yet implemented.
31+ #[ derive( Debug , Clone , Copy ) ]
32+ pub enum ExitStatus {
33+ /// Process exited normally with the given exit code.
34+ /// The exit code will later be truncated to 8 bits when encoded
35+ /// into a POSIX wait status.
36+ Exited ( i32 ) ,
37+ /// Process was terminated by a signal.
38+ /// The boolean indicates whether a core dump occurred.
39+ Signaled ( i32 , bool ) , // (signal, core_dump)
40+ }
41+
42+ /// A zombie child process.
43+ ///
44+ /// A zombie represents a child cage that has already terminated but whose
45+ /// termination status has not yet been collected by the parent via
46+ /// `waitpid` or a related wait syscall.
47+ ///
48+ /// The runtime stores the cage identifier together with the termination
49+ /// status so the parent can later retrieve it.
1750#[ derive( Debug , Clone , Copy ) ]
1851pub struct Zombie {
1952 pub cageid : u64 ,
20- pub exit_code : i32 ,
53+ pub exit_code : ExitStatus ,
54+ }
55+
56+ /// Encode a structured `ExitStatus` into the traditional POSIX
57+ /// `waitpid` status integer.
58+ ///
59+ /// The encoding follows the standard Unix wait status layout:
60+ ///
61+ /// Normal exit:
62+ /// status = (exit_code & 0xff) << 8
63+ ///
64+ /// Signal termination:
65+ /// bits 0–6 : signal number
66+ /// bit 7 : core dump flag
67+ ///
68+ /// Exit codes are truncated to 8 bits to match POSIX semantics.
69+ /// This ensures that `WIFEXITED`, `WEXITSTATUS`, and related libc
70+ /// macros behave correctly.
71+ pub fn encode_wait_status ( st : ExitStatus ) -> i32 {
72+ match st {
73+ ExitStatus :: Exited ( code) => ( ( code & 0xff ) << 8 ) ,
74+ ExitStatus :: Signaled ( sig, core) => {
75+ let mut s = sig & 0x7f ;
76+ if core {
77+ s |= 0x80 ;
78+ } // core dump flag in traditional encoding
79+ s
80+ }
81+ }
82+ }
83+
84+ /// Record the final termination status of a cage.
85+ ///
86+ /// This function stores the exit status that will later be reported to the
87+ /// parent when the cage becomes a zombie (e.g., via `waitpid`). The status
88+ /// may represent either a normal exit (`Exited`) or signal-based termination
89+ /// (`Signaled`).
90+ ///
91+ /// The recorded status is later consumed when inserting a `Zombie` entry
92+ /// into the parent's zombie list.
93+ ///
94+ /// This function is currently used on signal-based termination to record
95+ /// the signal number.
96+ ///
97+ /// # Panics
98+ ///
99+ /// Panics if the specified cage does not exist in the cage table.
100+ pub fn cage_record_exit_status ( cageid : u64 , status : ExitStatus ) {
101+ let cage = get_cage ( cageid) . unwrap_or_else ( || {
102+ panic ! (
103+ "Attempted to record exit status for non-existent cage ID {}" ,
104+ cageid
105+ )
106+ } ) ;
107+ let mut final_status = cage. final_exit_status . write ( ) ;
108+ if final_status. is_none ( ) {
109+ * final_status = Some ( status) ;
110+ }
21111}
22112
23113#[ derive( Debug ) ]
@@ -73,6 +163,28 @@ pub struct Cage {
73163 pub child_num : AtomicU64 ,
74164 // vmmap represents the virtual memory mapping for this cage. More details on `memory::vmmap`
75165 pub vmmap : RwLock < Vmmap > ,
166+ // final_exit_status stores the terminal status of the cage once a
167+ // termination condition has been determined.
168+ //
169+ // This field is used as a temporary cache for the cage's final exit
170+ // status (either `Exited(code)` or `Signaled(signo, core_dump)`).
171+ // The status is recorded when the cage enters a terminal state
172+ // (e.g., exit syscall or signal-triggered termination), but before
173+ // the cage is fully cleaned up.
174+ //
175+ // The recorded value is later consumed when inserting a `Zombie`
176+ // entry into the parent cage's `zombies` list, which is what the
177+ // parent observes through `wait()` / `waitpid()`.
178+ //
179+ // This field cannot be replaced by the `exit_code` stored in
180+ // `Zombie`. A `Zombie` object only exists in the parent's zombie
181+ // list and is created during the final cleanup phase of the exiting
182+ // cage. However, the cage's termination reason may need to be
183+ // determined earlier (for example during signal handling), before
184+ // the zombie entry is created. Therefore, the cage must temporarily
185+ // store its final termination status until the zombie entry is
186+ // generated.
187+ pub final_exit_status : RwLock < Option < ExitStatus > > ,
76188}
77189
78190/// We achieve an O(1) complexity for our cage map implementation through the following three approaches:
@@ -225,6 +337,7 @@ mod tests {
225337 zombies : RwLock :: new ( vec ! [ ] ) ,
226338 child_num : AtomicU64 :: new ( 0 ) ,
227339 vmmap : RwLock :: new ( crate :: memory:: vmmap:: Vmmap :: new ( ) ) ,
340+ final_exit_status : RwLock :: new ( None ) ,
228341 } ;
229342
230343 add_cage ( 2 , test_cage) ;
0 commit comments