Don't start authfs until fd_server is ready
We pass a pipe fd to fd_server which it closes when it is ready to
accept connections. pvm_exec waits for this before triggering
compilation, which will cause authfs in the VM to connect back to
fd_server.
Also improved a bunch of error handling/error logging code, since I
managed to hit a lot of failure cases.
Fix: 201764092
Bug: 186126194
Test: atest ComposTestCase
Change-Id: I99ea5ba4fb390d47485aef3cbaaccedd046ae9fe
diff --git a/authfs/fd_server/src/main.rs b/authfs/fd_server/src/main.rs
index 12f013c..32ffc0a 100644
--- a/authfs/fd_server/src/main.rs
+++ b/authfs/fd_server/src/main.rs
@@ -32,6 +32,7 @@
use std::convert::TryInto;
use std::fs::File;
use std::io;
+use std::os::raw;
use std::os::unix::fs::FileExt;
use std::os::unix::io::{AsRawFd, FromRawFd};
@@ -292,7 +293,12 @@
Ok((fd, FdConfig::ReadWrite(file)))
}
-fn parse_args() -> Result<BTreeMap<i32, FdConfig>> {
+struct Args {
+ fd_pool: BTreeMap<i32, FdConfig>,
+ ready_fd: Option<File>,
+}
+
+fn parse_args() -> Result<Args> {
#[rustfmt::skip]
let matches = clap::App::new("fd_server")
.arg(clap::Arg::with_name("ro-fds")
@@ -303,6 +309,9 @@
.long("rw-fds")
.multiple(true)
.number_of_values(1))
+ .arg(clap::Arg::with_name("ready-fd")
+ .long("ready-fd")
+ .takes_value(true))
.get_matches();
let mut fd_pool = BTreeMap::new();
@@ -318,8 +327,13 @@
fd_pool.insert(fd, config);
}
}
-
- Ok(fd_pool)
+ let ready_fd = if let Some(arg) = matches.value_of("ready-fd") {
+ let fd = arg.parse::<i32>()?;
+ Some(fd_to_file(fd)?)
+ } else {
+ None
+ };
+ Ok(Args { fd_pool, ready_fd })
}
fn main() -> Result<()> {
@@ -327,16 +341,22 @@
android_logger::Config::default().with_tag("fd_server").with_min_level(log::Level::Debug),
);
- let fd_pool = parse_args()?;
+ let args = parse_args()?;
- let mut service = FdService::new_binder(fd_pool).as_binder();
+ let mut service = FdService::new_binder(args.fd_pool).as_binder();
+ let mut ready_notifier = ReadyNotifier(args.ready_fd);
+
debug!("fd_server is starting as a rpc service.");
// SAFETY: Service ownership is transferring to the server and won't be valid afterward.
// Plus the binder objects are threadsafe.
+ // RunRpcServerCallback does not retain a reference to ready_callback, and only ever
+ // calls it with the param we provide during the lifetime of ready_notifier.
let retval = unsafe {
- binder_rpc_unstable_bindgen::RunRpcServer(
+ binder_rpc_unstable_bindgen::RunRpcServerCallback(
service.as_native_mut() as *mut binder_rpc_unstable_bindgen::AIBinder,
RPC_SERVICE_PORT,
+ Some(ReadyNotifier::ready_callback),
+ ready_notifier.as_void_ptr(),
)
};
if retval {
@@ -346,3 +366,25 @@
bail!("Premature termination of RPC server");
}
}
+
+struct ReadyNotifier(Option<File>);
+
+impl ReadyNotifier {
+ fn notify(&mut self) {
+ debug!("fd_server is ready");
+ // Close the ready-fd if we were given one to signal our readiness.
+ drop(self.0.take());
+ }
+
+ fn as_void_ptr(&mut self) -> *mut raw::c_void {
+ self as *mut _ as *mut raw::c_void
+ }
+
+ unsafe extern "C" fn ready_callback(param: *mut raw::c_void) {
+ // SAFETY: This is only ever called by RunRpcServerCallback, within the lifetime of the
+ // ReadyNotifier, with param taking the value returned by as_void_ptr (so a properly aligned
+ // non-null pointer to an initialized instance).
+ let ready_notifier = param as *mut Self;
+ ready_notifier.as_mut().unwrap().notify()
+ }
+}