@@ -9,13 +9,26 @@ use std::process::{Command, ExitCode};
99use std:: sync:: Arc ;
1010use std:: { env, thread} ;
1111
12+ /// The service is responsible for collecting the events from the supervised processes.
13+ ///
14+ /// The service is implemented as TCP server that listens on a random port on the loopback
15+ /// interface. The address of the service can be obtained by the `address` method.
16+ ///
17+ /// The service is started in a separate thread to dispatch the events to the consumer.
18+ /// The consumer is a function that receives the events from the service and processes them.
19+ /// It also runs in a separate thread. The reason for having two threads is to avoid blocking
20+ /// the main thread of the application and decouple the collection from the processing.
1221pub ( super ) struct InterceptService {
1322 collector : Arc < EventCollectorOnTcp > ,
1423 network_thread : Option < thread:: JoinHandle < ( ) > > ,
1524 output_thread : Option < thread:: JoinHandle < ( ) > > ,
1625}
1726
1827impl InterceptService {
28+ /// Creates a new intercept service.
29+ ///
30+ /// The `consumer` is a function that receives the events and processes them.
31+ /// The function is executed in a separate thread.
1932 pub fn new < F > ( consumer : F ) -> anyhow:: Result < Self >
2033 where
2134 F : FnOnce ( Receiver < Envelope > ) -> anyhow:: Result < ( ) > ,
@@ -27,27 +40,33 @@ impl InterceptService {
2740
2841 let collector_in_thread = collector_arc. clone ( ) ;
2942 let collector_thread = thread:: spawn ( move || {
43+ // TODO: log failures
3044 collector_in_thread. collect ( sender) . unwrap ( ) ;
3145 } ) ;
3246 let receiver_in_thread = receiver. clone ( ) ;
3347 let output_thread = thread:: spawn ( move || {
48+ // TODO: log failures
3449 consumer ( receiver_in_thread) . unwrap ( ) ;
3550 } ) ;
3651
52+ // TODO: log the address of the service
3753 Ok ( InterceptService {
3854 collector : collector_arc,
3955 network_thread : Some ( collector_thread) ,
4056 output_thread : Some ( output_thread) ,
4157 } )
4258 }
4359
60+ /// Returns the address of the service.
4461 pub fn address ( & self ) -> String {
4562 self . collector . address ( )
4663 }
4764}
4865
4966impl Drop for InterceptService {
67+ /// Shuts down the service.
5068 fn drop ( & mut self ) {
69+ // TODO: log the shutdown of the service and any errors
5170 self . collector . stop ( ) . expect ( "Failed to stop the collector" ) ;
5271 if let Some ( thread) = self . network_thread . take ( ) {
5372 thread. join ( ) . expect ( "Failed to join the collector thread" ) ;
@@ -58,6 +77,17 @@ impl Drop for InterceptService {
5877 }
5978}
6079
80+ /// The environment for the intercept mode.
81+ ///
82+ /// Running the build command requires a specific environment. The environment we
83+ /// need for intercepting the child processes is different for each intercept mode.
84+ ///
85+ /// The `Wrapper` mode requires a temporary directory with the executables that will
86+ /// be used to intercept the child processes. The executables are hard linked to the
87+ /// temporary directory.
88+ ///
89+ /// The `Preload` mode requires the path to the preload library that will be used to
90+ /// intercept the child processes.
6191pub ( super ) enum InterceptEnvironment {
6292 Wrapper {
6393 bin_dir : tempfile:: TempDir ,
@@ -70,6 +100,11 @@ pub(super) enum InterceptEnvironment {
70100}
71101
72102impl InterceptEnvironment {
103+ /// Creates a new intercept environment.
104+ ///
105+ /// The `config` is the intercept configuration that specifies the mode and the
106+ /// required parameters for the mode. The `address` is the address of the intercept
107+ /// service that will be used to collect the events.
73108 pub fn new ( config : & config:: Intercept , address : String ) -> anyhow:: Result < Self > {
74109 let result = match config {
75110 config:: Intercept :: Wrapper {
@@ -92,24 +127,38 @@ impl InterceptEnvironment {
92127 Ok ( result)
93128 }
94129
130+ /// Executes the build command in the intercept environment.
131+ ///
132+ /// The method is blocking and waits for the build command to finish.
133+ /// The method returns the exit code of the build command. Result failure
134+ /// indicates that the build command failed to start.
95135 pub fn execute_build_command ( self , input : args:: BuildCommand ) -> anyhow:: Result < ExitCode > {
136+ // TODO: record the execution of the build command
137+
96138 let environment = self . environment ( ) ;
97139 let mut child = Command :: new ( input. arguments [ 0 ] . clone ( ) )
98- . args ( input. arguments )
140+ . args ( input. arguments [ 1 .. ] . iter ( ) )
99141 . envs ( environment)
100142 . spawn ( ) ?;
101143
144+ // TODO: forward signals to the child process
102145 let result = child. wait ( ) ?;
103146
104- if result. success ( ) {
105- Ok ( ExitCode :: SUCCESS )
106- } else {
107- result
108- . code ( )
109- . map_or ( Ok ( ExitCode :: FAILURE ) , |code| Ok ( ExitCode :: from ( code as u8 ) ) )
110- }
147+ // The exit code is not always available. When the process is killed by a signal,
148+ // the exit code is not available. In this case, we return the `FAILURE` exit code.
149+ let exit_code = result
150+ . code ( )
151+ . map ( |code| ExitCode :: from ( code as u8 ) )
152+ . unwrap_or ( ExitCode :: FAILURE ) ;
153+
154+ Ok ( exit_code)
111155 }
112156
157+ /// Returns the environment variables for the intercept environment.
158+ ///
159+ /// The environment variables are different for each intercept mode.
160+ /// It does not change the original environment variables, but creates
161+ /// the environment variables that are required for the intercept mode.
113162 fn environment ( & self ) -> Vec < ( String , String ) > {
114163 match self {
115164 InterceptEnvironment :: Wrapper {
@@ -118,7 +167,7 @@ impl InterceptEnvironment {
118167 let path_original = env:: var ( "PATH" ) . unwrap_or_else ( |_| String :: new ( ) ) ;
119168 let path_updated = InterceptEnvironment :: insert_to_path (
120169 & path_original,
121- Self :: to_string ( bin_dir. path ( ) ) ,
170+ Self :: path_to_string ( bin_dir. path ( ) ) ,
122171 ) ;
123172 vec ! [
124173 ( "PATH" . to_string( ) , path_updated) ,
@@ -127,8 +176,10 @@ impl InterceptEnvironment {
127176 }
128177 InterceptEnvironment :: Preload { path, address, .. } => {
129178 let path_original = env:: var ( KEY_PRELOAD_PATH ) . unwrap_or_else ( |_| String :: new ( ) ) ;
130- let path_updated =
131- InterceptEnvironment :: insert_to_path ( & path_original, Self :: to_string ( path) ) ;
179+ let path_updated = InterceptEnvironment :: insert_to_path (
180+ & path_original,
181+ Self :: path_to_string ( path) ,
182+ ) ;
132183 vec ! [
133184 ( KEY_PRELOAD_PATH . to_string( ) , path_updated) ,
134185 ( KEY_DESTINATION . to_string( ) , address. clone( ) ) ,
@@ -137,13 +188,16 @@ impl InterceptEnvironment {
137188 }
138189 }
139190
191+ /// Manipulate a `PATH` like environment value by inserting the `first` path into
192+ /// the original value. It removes the `first` path if it already exists in the
193+ /// original value. And it inserts the `first` path at the beginning of the value.
140194 fn insert_to_path ( original : & str , first : String ) -> String {
141195 let mut paths: Vec < _ > = original. split ( ':' ) . filter ( |it| it != & first) . collect ( ) ;
142196 paths. insert ( 0 , first. as_str ( ) ) ;
143197 paths. join ( ":" )
144198 }
145199
146- fn to_string ( path : & Path ) -> String {
200+ fn path_to_string ( path : & Path ) -> String {
147201 path. to_str ( ) . unwrap_or ( "" ) . to_string ( )
148202 }
149203}
0 commit comments