@@ -10,6 +10,7 @@ use crate::ffi::{
1010} ;
1111use anyhow:: { anyhow, format_err, Context , Result } ;
1212use cap_std:: fs:: Dir ;
13+ use cap_std:: io_lifetimes:: { IntoSocketlike , OwnedFd , OwnedSocketlike } ;
1314use cap_std_ext:: dirext:: CapStdExtDirExt ;
1415use cap_std_ext:: { cap_std, rustix} ;
1516use fn_error_context:: context;
@@ -18,16 +19,29 @@ use once_cell::sync::Lazy;
1819use ostree_ext:: { gio, glib, ostree} ;
1920use rustix:: fd:: { BorrowedFd , FromRawFd } ;
2021use rustix:: fs:: MetadataExt ;
22+ use serde:: { Deserialize , Serialize } ;
2123use std:: collections:: { BTreeMap , BTreeSet } ;
22- use std:: io:: { Read , Write } ;
24+ use std:: io:: Read ;
2325use std:: os:: unix:: fs:: PermissionsExt ;
2426use std:: path:: Path ;
2527use std:: sync:: Mutex ;
26- use tokio:: net:: { UnixListener , UnixStream } ;
27- use tokio:: sync:: oneshot:: { Receiver , Sender } ;
28+ use tokio:: sync:: oneshot:: Sender ;
2829
2930const RPM_OSTREED_COMMIT_VERIFICATION_CACHE : & str = "rpm-ostree/gpgcheck-cache" ;
3031
32+ // Messages sent across the socket
33+ #[ derive( Debug , Serialize , Deserialize ) ]
34+ pub ( crate ) enum SocketMessage {
35+ ClientHello { selfid : String } ,
36+ ServerOk ,
37+ ServerError { msg : String } ,
38+ }
39+
40+ impl SocketMessage {
41+ // Maximum size of a message.
42+ pub ( crate ) const BUFSIZE : usize = 8192 ;
43+ }
44+
3145/// Validate basic assumptions on daemon startup.
3246pub ( crate ) fn daemon_sanitycheck_environment ( sysroot : & crate :: FFIOstreeSysroot ) -> CxxResult < ( ) > {
3347 let sysroot = & sysroot. glib_reborrow ( ) ;
@@ -135,57 +149,83 @@ fn deployment_populate_variant_origin(
135149 Ok ( ( ) )
136150}
137151
138- async fn send_ok_result_to_client ( _client : UnixStream ) {
139- // On success we close the stream without writing anything,
140- // which acknowledges successful startup to the client.
141- // In the future we may actually implement a protocol here, so this
142- // is stubbed out as a full async fn in preparation for that.
152+ pub ( crate ) fn write_message ( conn : & OwnedFd , message : SocketMessage ) -> Result < ( ) > {
153+ let sendbuf = serde_json:: to_vec ( & message) ?;
154+ rustix:: net:: send ( conn, & sendbuf, rustix:: net:: SendFlags :: empty ( ) ) ?;
155+ Ok ( ( ) )
156+ }
157+
158+ pub ( crate ) fn recv_message ( conn : & OwnedFd ) -> Result < SocketMessage > {
159+ let mut buf = [ 0u8 ; SocketMessage :: BUFSIZE ] ;
160+ let n = rustix:: net:: recv ( & conn, & mut buf, rustix:: net:: RecvFlags :: empty ( ) ) ?;
161+ if n == SocketMessage :: BUFSIZE {
162+ anyhow:: bail!( "Buffer filled to {n} bytes when reading" ) ;
163+ }
164+ assert ! ( n < SocketMessage :: BUFSIZE ) ;
165+ let buf = & buf[ 0 ..n] ;
166+ let msg: SocketMessage =
167+ serde_json:: from_slice ( buf) . context ( "Parsing client message" ) ?;
168+ Ok ( msg)
169+ }
170+
171+ fn client_hello ( client : OwnedSocketlike , e : anyhow:: Result < ( ) > ) -> Result < ( ) > {
172+ let msg = recv_message ( & client) ?;
173+ let reply = match msg {
174+ SocketMessage :: ClientHello { selfid } => {
175+ let myid = crate :: core:: self_id ( ) ?;
176+ if selfid != myid {
177+ // For now, make this not an error
178+ tracing:: warn!( "Client reported id: {selfid} different from mine: {myid}" ) ;
179+ }
180+ match e {
181+ Ok ( ( ) ) => SocketMessage :: ServerOk ,
182+ Err ( e) => SocketMessage :: ServerError {
183+ msg : format ! ( "{e}" ) ,
184+ } ,
185+ }
186+ }
187+ o => SocketMessage :: ServerError {
188+ msg : format ! ( "Unexpected message: {o:?}" ) ,
189+ } ,
190+ } ;
191+ write_message ( & client, reply) . context ( "Writing client reply" ) ?;
143192 tracing:: debug!( "Acknowleged client" ) ;
193+ Ok ( ( ) )
144194}
145195
146196static SHUTDOWN_SIGNAL : Lazy < Mutex < Option < Sender < ( ) > > > > = Lazy :: new ( || Mutex :: new ( None ) ) ;
147197
148- async fn process_clients_with_ok ( listener : UnixListener , mut cancel : Receiver < ( ) > ) {
198+ fn run_acknowledgement_worker ( listener : OwnedSocketlike ) {
149199 tracing:: debug!( "Processing clients..." ) ;
150200 loop {
151- tokio:: select! {
152- _ = & mut cancel => {
153- tracing:: debug!( "Got cancellation event" ) ;
154- return
201+ let sock = match rustix:: net:: accept ( & listener) {
202+ Ok ( s) => s,
203+ Err ( e) => {
204+ tracing:: warn!( "Failed to accept client: {e}" ) ;
205+ continue ;
155206 }
156- r = listener. accept( ) => {
157- match r {
158- Ok ( ( stream, _addr) ) => {
159- send_ok_result_to_client( stream) . await ;
160- } ,
161- Err ( e) => {
162- tracing:: debug!( "failed to accept client: {e}" )
163- }
164- }
207+ } ;
208+ std:: thread:: spawn ( move || {
209+ if let Err ( e) = client_hello ( sock. into_socketlike ( ) , Ok ( ( ) ) ) {
210+ tracing:: warn!( "error acknowledging client: {e}" ) ;
165211 }
166- }
212+ } ) ;
167213 }
168214}
169215
170216/// Ensure all asynchronous tasks in this Rust half of the daemon code are stopped.
171217/// Called from C++.
172218pub ( crate ) fn daemon_terminate ( ) {
173- let chan = ( * SHUTDOWN_SIGNAL ) . lock ( ) . unwrap ( ) . take ( ) . unwrap ( ) ;
174- let _ = chan. send ( ( ) ) ;
219+ if let Some ( chan) = ( * SHUTDOWN_SIGNAL ) . lock ( ) . unwrap ( ) . take ( ) {
220+ let _ = chan. send ( ( ) ) ;
221+ }
175222}
176223
177- fn process_one_client ( listener : std:: os:: unix:: net:: UnixListener , e : anyhow:: Error ) -> Result < ( ) > {
178- let mut incoming = match listener. incoming ( ) . next ( ) {
179- Some ( r) => r?,
180- None => {
181- anyhow:: bail!( "Expected to find client socket from activation" ) ;
182- }
183- } ;
184-
185- let buf = format ! ( "{e}" ) ;
186- incoming. write_all ( buf. as_bytes ( ) ) ?;
187-
188- todo ! ( )
224+ fn process_one_client ( listener : OwnedSocketlike , e : anyhow:: Error ) -> Result < ( ) > {
225+ let incoming = rustix:: net:: accept ( & listener) ?;
226+ client_hello ( incoming. into_socketlike ( ) , Err ( e) ) ?;
227+ // Now that we've acknowledged one client, exit the process with an error
228+ Ok ( ( ) )
189229}
190230
191231/// Perform initialization steps required by systemd service activation.
@@ -195,7 +235,6 @@ fn process_one_client(listener: std::os::unix::net::UnixListener, e: anyhow::Err
195235pub ( crate ) fn daemon_main ( debug : bool ) -> Result < ( ) > {
196236 let handle = tokio:: runtime:: Handle :: current ( ) ;
197237 let _tokio_guard = handle. enter ( ) ;
198- use std:: os:: unix:: net:: UnixListener as StdUnixListener ;
199238 if !systemd:: daemon:: booted ( ) ? {
200239 return Err ( anyhow ! ( "not running as a systemd service" ) ) ;
201240 }
@@ -204,54 +243,65 @@ pub(crate) fn daemon_main(debug: bool) -> Result<()> {
204243 tracing:: debug!( "Initialization result: {init_res:?}" ) ;
205244
206245 let mut fds = systemd:: daemon:: listen_fds ( false ) ?. iter ( ) ;
207- let listener = match fds. next ( ) {
246+ let ( listener, init_res ) = match fds. next ( ) {
208247 None => {
209248 // If started directly via `systemctl start` or DBus activation, we
210- // directly propagate the error back to our exit code.
249+ // directly propagate the error back to our exit code without even bothering
250+ // with a socket.
211251 init_res?;
212252 tracing:: debug!( "Initializing directly (not socket activated)" ) ;
213- cfg ! ( feature = "client-socket" )
214- . then ( || StdUnixListener :: bind ( "/run/rpm-ostree/client.sock" ) )
253+ let listener = cfg ! ( feature = "client-socket" )
254+ . then ( || {
255+ let socket = rustix:: net:: socket (
256+ rustix:: net:: AddressFamily :: UNIX ,
257+ rustix:: net:: SocketType :: SEQPACKET ,
258+ rustix:: net:: Protocol :: from_raw ( 0 ) ,
259+ ) ?;
260+ let addr = crate :: client:: sockaddr ( ) ?;
261+ rustix:: net:: bind_unix ( & socket, & addr) ?;
262+ Ok :: < _ , anyhow:: Error > ( socket. into_socketlike ( ) )
263+ } )
215264 . transpose ( )
216- . context ( "Binding to socket" ) ?
265+ . context ( "Binding to socket" ) ?;
266+ ( listener, Ok ( ( ) ) )
217267 }
218268 Some ( fd) => {
219269 if fds. next ( ) . is_some ( ) {
220270 return Err ( anyhow ! ( "Expected exactly 1 fd from systemd activation" ) ) ;
221271 }
222272 tracing:: debug!( "Initializing from socket activation; fd={fd}" ) ;
223- let listener = unsafe { StdUnixListener :: from_raw_fd ( fd) } ;
224-
225- match init_res {
226- Ok ( _) => Some ( listener) ,
227- Err ( e) => {
228- let err_copy = anyhow ! ( "{e}" ) ;
229- tracing:: debug!( "Reporting initialization error: {e}" ) ;
230- match process_one_client ( listener, err_copy) {
231- Ok ( ( ) ) => {
232- tracing:: debug!( "Acknowleged initial client" ) ;
233- }
234- Err ( e) => {
235- tracing:: debug!( "Caught error while processing client {e}" ) ;
236- }
237- }
238- return Err ( e) ;
239- }
240- }
273+ let listener = unsafe { OwnedFd :: from_raw_fd ( fd) } . into_socketlike ( ) ;
274+ // In the socket case, we will process the initialization error later.
275+ ( Some ( listener) , init_res)
241276 }
242277 } ;
243278
244279 if let Some ( listener) = listener {
245- let ( shutdown_send, shutdown_recv) = tokio:: sync:: oneshot:: channel ( ) ;
246- ( * SHUTDOWN_SIGNAL ) . lock ( ) . unwrap ( ) . replace ( shutdown_send) ;
247-
248- let listener = UnixListener :: from_std ( listener) ?;
249-
250280 // On success, we spawn a helper task that just responds with
251281 // sucess to clients that connect via the socket. In the future,
252282 // perhaps we'll expose an API here.
253283 tracing:: debug!( "Spawning acknowledgement task" ) ;
254- tokio:: task:: spawn ( async { process_clients_with_ok ( listener, shutdown_recv) . await } ) ;
284+ match init_res {
285+ Ok ( ( ) ) => {
286+ std:: thread:: spawn ( move || run_acknowledgement_worker ( listener) ) ;
287+ }
288+ Err ( e) => {
289+ let err_copy = anyhow:: format_err!( "{e}" ) ;
290+ let r = std:: thread:: spawn ( move || {
291+ if let Err ( suberr) = process_one_client ( listener, err_copy) {
292+ tracing:: warn!( "Failed to respond to client: {suberr}" )
293+ }
294+ } ) ;
295+ // Block until we've written the reply to the client;
296+ if let Err ( e) = r. join ( ) {
297+ tracing:: warn!( "Failed to join response thread: {e:?}" ) ;
298+ }
299+ // And finally propagate out the error
300+ return Err ( e) ;
301+ }
302+ } ;
303+ } else {
304+ init_res?;
255305 }
256306
257307 tracing:: debug!( "Entering daemon mainloop" ) ;
0 commit comments