@@ -11,7 +11,7 @@ use a3s_observer::{
1111 KubeResolver , LogExporter , Provider , ServiceClassifier , SniClassifier ,
1212} ;
1313use a3s_observer_common:: {
14- ConnectEvent , DnsEvent , ExecEvent , FileEvent , LlmEvent , SslEvent , TlsEvent ,
14+ ConnectEvent , DnsEvent , ExecEvent , ExitEvent , FileEvent , LlmEvent , SslEvent , TlsEvent ,
1515} ;
1616use anyhow:: Context as _;
1717use aya:: {
@@ -57,6 +57,7 @@ async fn main() -> anyhow::Result<()> {
5757 let files = std:: env:: var_os ( "A3S_OBSERVER_FILES" ) . is_some ( ) ;
5858 let mut probes = vec ! [
5959 ( "exec" , "sys_enter_execve" ) ,
60+ ( "proc_exit" , "sys_enter_exit_group" ) ,
6061 ( "tls_write" , "sys_enter_write" ) ,
6162 ( "tls_sendto" , "sys_enter_sendto" ) ,
6263 ( "connect" , "sys_enter_connect" ) ,
@@ -129,6 +130,10 @@ async fn main() -> anyhow::Result<()> {
129130 // closes (the in-kernel LlmEvent) to build the metric-bearing LlmCall.
130131 let mut llm_meta: HashMap < u64 , ( Option < String > , Option < Provider > , IpAddr ) > = HashMap :: new ( ) ;
131132 let mut exec_ring = RingBuf :: try_from ( ebpf. take_map ( "EVENTS" ) . context ( "`EVENTS` missing" ) ?) ?;
133+ let mut exit_ring = RingBuf :: try_from (
134+ ebpf. take_map ( "EXIT_EVENTS" )
135+ . context ( "`EXIT_EVENTS` missing" ) ?,
136+ ) ?;
132137 let mut tls_ring = RingBuf :: try_from (
133138 ebpf. take_map ( "TLS_EVENTS" )
134139 . context ( "`TLS_EVENTS` missing" ) ?,
@@ -194,6 +199,7 @@ async fn main() -> anyhow::Result<()> {
194199 . unwrap_or( 0 ) ;
195200 tracing:: info!(
196201 exec = stats. exec,
202+ exit = stats. exit,
197203 egress = stats. egress,
198204 dns = stats. dns,
199205 file = stats. file,
@@ -222,6 +228,18 @@ async fn main() -> anyhow::Result<()> {
222228 } ) ;
223229 }
224230 }
231+ while let Some ( item) = exit_ring. next( ) {
232+ if let Some ( ev) = read_pod:: <ExitEvent >( & item) {
233+ emit( exporter. as_ref( ) , & mut stats, EnrichedEvent {
234+ identity: identity_for( & resolver, ev. pid, & ev. comm) ,
235+ provider: None ,
236+ event: AgentEvent :: ProcessExit {
237+ pid: ev. pid,
238+ exit_code: ev. exit_code,
239+ } ,
240+ } ) ;
241+ }
242+ }
225243 // Drain connect BEFORE tls so a same-poll ClientHello finds its peer.
226244 while let Some ( item) = connect_ring. next( ) {
227245 if let Some ( ev) = read_pod:: <ConnectEvent >( & item) {
@@ -347,6 +365,7 @@ async fn main() -> anyhow::Result<()> {
347365 }
348366 tracing:: info!(
349367 exec = stats. exec,
368+ exit = stats. exit,
350369 egress = stats. egress,
351370 dns = stats. dns,
352371 file = stats. file,
@@ -432,6 +451,7 @@ fn identity_for(r: &impl IdentityResolver, pid: u32, comm: &[u8; 16]) -> Identit
432451#[ derive( Default ) ]
433452struct Stats {
434453 exec : u64 ,
454+ exit : u64 ,
435455 egress : u64 ,
436456 dns : u64 ,
437457 file : u64 ,
@@ -443,6 +463,7 @@ struct Stats {
443463fn emit ( exporter : & dyn Exporter , stats : & mut Stats , ev : EnrichedEvent ) {
444464 match & ev. event {
445465 AgentEvent :: ToolExec { .. } => stats. exec += 1 ,
466+ AgentEvent :: ProcessExit { .. } => stats. exit += 1 ,
446467 AgentEvent :: Egress { .. } => stats. egress += 1 ,
447468 AgentEvent :: Dns { .. } => stats. dns += 1 ,
448469 AgentEvent :: FileAccess { .. } => stats. file += 1 ,
0 commit comments