Skip to main content

starnix_kernel_runner/
serve_protocols.rs

1// Copyright 2022 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::Container;
6use anyhow::{Context as _, Error};
7use fidl::endpoints::{RequestStream, ServerEnd};
8use fidl_fuchsia_component_runner as frunner;
9use fidl_fuchsia_element as felement;
10use fidl_fuchsia_io as fio;
11use fidl_fuchsia_memory_attribution as fattribution;
12use fidl_fuchsia_posix as fposix;
13use fidl_fuchsia_starnix_binder as fbinder;
14use fidl_fuchsia_starnix_container as fstarcontainer;
15use fuchsia_async::{
16    DurationExt, {self as fasync},
17};
18use fuchsia_rcu::RcuReadScope;
19use futures::channel::oneshot;
20use futures::{
21    AsyncReadExt, AsyncWriteExt, Future, FutureExt, StreamExt, TryFutureExt, TryStreamExt, pin_mut,
22    select,
23};
24use starnix_core::execution::{create_init_child_process, execute_task_with_prerun_result};
25use starnix_core::fs::fuchsia::create_fuchsia_pipe;
26use starnix_core::task::dynamic_thread_spawner::SpawnRequestBuilder;
27use starnix_core::task::{CurrentTask, ExitStatus, Kernel, ProcessEntryRef};
28use starnix_core::vfs::buffers::{VecInputBuffer, VecOutputBuffer};
29use starnix_core::vfs::file_server::serve_file_at;
30use starnix_core::vfs::socket::VsockSocket;
31use starnix_core::vfs::{FdFlags, FileHandle};
32use starnix_logging::{log_error, log_warn};
33use starnix_modules::{Framebuffer, create_main_and_replica};
34use starnix_task_command::TaskCommand;
35use starnix_uapi::auth::Credentials;
36use starnix_uapi::errors::Errno;
37use starnix_uapi::open_flags::OpenFlags;
38use starnix_uapi::signals::UncheckedSignal;
39use starnix_uapi::{errno, error, uapi};
40use std::ffi::CString;
41
42use super::start_component;
43
44pub fn expose_root(
45    system_task: &CurrentTask,
46    server_end: ServerEnd<fio::DirectoryMarker>,
47) -> Result<(), Error> {
48    let root_file = system_task.open_file("/".into(), OpenFlags::RDONLY)?;
49    serve_file_at(server_end.into_channel().into(), system_task, &root_file, Credentials::root())?;
50    Ok(())
51}
52
53pub async fn serve_component_runner(
54    request_stream: frunner::ComponentRunnerRequestStream,
55    system_task: &CurrentTask,
56) -> Result<(), Error> {
57    request_stream
58        .try_for_each_concurrent(None, |event| async {
59            match event {
60                frunner::ComponentRunnerRequest::Start { start_info, controller, .. } => {
61                    if let Err(e) = start_component(start_info, controller, system_task).await {
62                        log_error!("failed to start component: {:?}", e);
63                    }
64                }
65                frunner::ComponentRunnerRequest::_UnknownMethod { ordinal, .. } => {
66                    log_warn!("Unknown ComponentRunner request: {ordinal}");
67                }
68            }
69            Ok(())
70        })
71        .await
72        .map_err(Error::from)
73}
74
75fn to_winsize(window_size: Option<fstarcontainer::ConsoleWindowSize>) -> uapi::winsize {
76    window_size
77        .map(|window_size| uapi::winsize {
78            ws_row: window_size.rows,
79            ws_col: window_size.cols,
80            ws_xpixel: window_size.x_pixels,
81            ws_ypixel: window_size.y_pixels,
82        })
83        .unwrap_or(uapi::winsize::default())
84}
85
86async fn spawn_console(
87    kernel: &Kernel,
88    payload: fstarcontainer::ControllerSpawnConsoleRequest,
89) -> Result<Result<u8, fstarcontainer::SpawnConsoleError>, Error> {
90    if let (Some(console_in), Some(console_out), Some(binary_path)) =
91        (payload.console_in, payload.console_out, payload.binary_path)
92    {
93        let binary_path = CString::new(binary_path)?;
94        let argv = payload
95            .argv
96            .unwrap_or(vec![])
97            .into_iter()
98            .map(CString::new)
99            .collect::<Result<Vec<_>, _>>()?;
100        let environ = payload
101            .environ
102            .unwrap_or(vec![])
103            .into_iter()
104            .map(CString::new)
105            .collect::<Result<Vec<_>, _>>()?;
106        let window_size = to_winsize(payload.window_size);
107        let current_task = create_init_child_process(
108            &kernel.weak_self.upgrade().expect("Kernel must still be alive"),
109            TaskCommand::new(binary_path.as_bytes()),
110            Credentials::with_ids(0, 0),
111            None,
112        )?;
113        let (sender, receiver) = oneshot::channel();
114        let pty = execute_task_with_prerun_result(
115            current_task,
116            move |current_task| {
117                let executable = current_task.open_file_for_exec(
118                    starnix_core::vfs::FdNumber::AT_FDCWD,
119                    binary_path.as_bytes().into(),
120                    OpenFlags::empty(),
121                )?;
122                current_task.exec(executable, binary_path, argv, environ)?;
123                let (pty, pts) = create_main_and_replica(&current_task, window_size)?;
124                let fd_flags = FdFlags::empty();
125                assert_eq!(0, current_task.add_file(pts.clone(), fd_flags)?.raw());
126                assert_eq!(1, current_task.add_file(pts.clone(), fd_flags)?.raw());
127                assert_eq!(2, current_task.add_file(pts, fd_flags)?.raw());
128                Ok(pty)
129            },
130            move |result| {
131                let _ = match result {
132                    Ok(ExitStatus::Exit(exit_code)) => sender.send(Ok(exit_code)),
133                    _ => sender.send(Err(fstarcontainer::SpawnConsoleError::Canceled)),
134                };
135            },
136        )?;
137        let _ = forward_to_pty(kernel, console_in, console_out, pty).map_err(|e| {
138            log_error!("failed to forward to terminal {:?}", e);
139        });
140
141        Ok(receiver.await?)
142    } else {
143        Ok(Err(fstarcontainer::SpawnConsoleError::InvalidArgs))
144    }
145}
146
147pub async fn serve_container_controller(
148    request_stream: fstarcontainer::ControllerRequestStream,
149    system_task: &CurrentTask,
150) -> Result<(), Error> {
151    request_stream
152        .map_err(Error::from)
153        .try_for_each_concurrent(None, |event| async {
154            match event {
155                fstarcontainer::ControllerRequest::VsockConnect {
156                    payload:
157                        fstarcontainer::ControllerVsockConnectRequest { port, bridge_socket, .. },
158                    ..
159                } => {
160                    let Some(port) = port else {
161                        log_warn!("vsock connection missing port");
162                        return Ok(());
163                    };
164                    let Some(bridge_socket) = bridge_socket else {
165                        log_warn!("vsock connection missing bridge_socket");
166                        return Ok(());
167                    };
168                    connect_to_vsock(port, bridge_socket, system_task).await.unwrap_or_else(|e| {
169                        log_error!("failed to connect to vsock {:?}", e);
170                    });
171                }
172                fstarcontainer::ControllerRequest::SpawnConsole { payload, responder } => {
173                    responder.send(spawn_console(system_task.kernel(), payload).await?)?;
174                }
175                fstarcontainer::ControllerRequest::GetVmoReferences { payload, responder } => {
176                    if let Some(koid) = payload.koid {
177                        let mut results = vec![];
178                        for thread_group in
179                            system_task.kernel().pids.get_thread_groups(&RcuReadScope::new())
180                        {
181                            if let Ok(leader) = thread_group.leader.get_task() {
182                                if let Ok(files) = leader.files() {
183                                    let fds = files.get_all_fds();
184                                    for fd in fds {
185                                        if let Ok(file) = files.get(fd) {
186                                            if let Ok(memory) = file.get_memory(
187                                                system_task,
188                                                None,
189                                                starnix_core::mm::ProtectionFlags::READ,
190                                            ) {
191                                                let memory_koid = memory
192                                                    .info()
193                                                    .expect("Failed to get memory info")
194                                                    .koid;
195                                                if memory_koid.raw_koid() == koid {
196                                                    let process_name = thread_group
197                                                        .process
198                                                        .get_name()
199                                                        .unwrap_or_default();
200                                                    results.push(fstarcontainer::VmoReference {
201                                                        process_name: Some(
202                                                            process_name.to_string(),
203                                                        ),
204                                                        pid: Some(leader.get_pid() as u64),
205                                                        fd: Some(fd.raw()),
206                                                        koid: Some(koid),
207                                                        ..Default::default()
208                                                    });
209                                                }
210                                            }
211                                        }
212                                    }
213                                }
214                            }
215                        }
216                        let _ =
217                            responder.send(&fstarcontainer::ControllerGetVmoReferencesResponse {
218                                references: Some(results),
219                                ..Default::default()
220                            });
221                    }
222                }
223                fstarcontainer::ControllerRequest::GetJobHandle { responder } => {
224                    let _result = responder.send(fstarcontainer::ControllerGetJobHandleResponse {
225                        job: Some(
226                            fuchsia_runtime::job_default()
227                                .duplicate_handle(zx::Rights::SAME_RIGHTS)
228                                .expect("Failed to dup handle"),
229                        ),
230                        ..Default::default()
231                    });
232                }
233                fstarcontainer::ControllerRequest::SendSignal {
234                    payload:
235                        fstarcontainer::ControllerSendSignalRequest {
236                            pid: Some(pid),
237                            signal: Some(signal),
238                            ..
239                        },
240                    responder,
241                } => {
242                    let pids = &system_task.kernel().pids;
243                    if let Some(ProcessEntryRef::Process(target_thread_group)) =
244                        pids.get(pid).ok().and_then(|p| p.get_process())
245                    {
246                        #[allow(
247                            clippy::undocumented_unsafe_blocks,
248                            reason = "Force documented unsafe blocks in Starnix"
249                        )]
250                        match unsafe {
251                            target_thread_group.send_signal_unchecked_debug(
252                                system_task,
253                                UncheckedSignal::new(signal),
254                            )
255                        } {
256                            Ok(_) => {
257                                let _result = responder.send(Ok(()));
258                            }
259                            Err(_) => {
260                                let _result =
261                                    responder.send(Err(fstarcontainer::SignalError::InvalidSignal));
262                            }
263                        };
264                    } else {
265                        let _result =
266                            responder.send(Err(fstarcontainer::SignalError::InvalidTarget));
267                    };
268                }
269                // The request did not contain both a signal and a target pid.
270                fstarcontainer::ControllerRequest::SendSignal { responder, .. } => {
271                    log_error!("malformed SendSignal request");
272                    let _result = responder.send(Err(fstarcontainer::SignalError::InvalidTarget));
273                }
274                fstarcontainer::ControllerRequest::SetSyscallLogFilter { payload, responder } => {
275                    if let Some(process_name) = payload.process_name {
276                        system_task.kernel().add_syscall_log_filter(&process_name);
277                        let _ = responder.send(Ok(()));
278                    } else {
279                        let _ = responder.send(Err(
280                            fstarcontainer::SetSyscallLogFilterError::MissingProcessName,
281                        ));
282                    }
283                }
284                fstarcontainer::ControllerRequest::ClearSyscallLogFilters { responder } => {
285                    system_task.kernel().clear_syscall_log_filters();
286                    let _ = responder.send();
287                }
288                fstarcontainer::ControllerRequest::_UnknownMethod { .. } => (),
289            }
290            Ok(())
291        })
292        .await
293}
294
295async fn connect_to_vsock(
296    port: u32,
297    bridge_socket: fidl::Socket,
298    system_task: &CurrentTask,
299) -> Result<(), Error> {
300    let socket = loop {
301        if let Ok(socket) = system_task.kernel().default_abstract_vsock_namespace.lookup(&port) {
302            break socket;
303        };
304        fasync::Timer::new(fasync::MonotonicDuration::from_millis(100).after_now()).await;
305    };
306
307    let pipe =
308        create_fuchsia_pipe(system_task, bridge_socket, OpenFlags::RDWR | OpenFlags::NONBLOCK)?;
309    socket.downcast_socket::<VsockSocket>().unwrap().remote_connection(
310        &socket,
311        system_task,
312        pipe,
313    )?;
314
315    Ok(())
316}
317
318fn forward_to_pty(
319    kernel: &Kernel,
320    console_in: fidl::Socket,
321    console_out: fidl::Socket,
322    pty: FileHandle,
323) -> Result<(), Error> {
324    // Matches fuchsia.io.Transfer capacity, somewhat arbitrarily.
325    const BUFFER_CAPACITY: usize = 8192;
326
327    let mut rx = fuchsia_async::Socket::from_socket(console_in);
328    let mut tx = fuchsia_async::Socket::from_socket(console_out);
329    let pty_sink = pty.clone();
330    let closure = async move |current_task: &CurrentTask| {
331        let _result: Result<(), Error> = (async || {
332            let mut buffer = vec![0u8; BUFFER_CAPACITY];
333            loop {
334                let bytes = rx.read(&mut buffer[..]).await?;
335                if bytes == 0 {
336                    return Ok(());
337                }
338                pty_sink.write(current_task, &mut VecInputBuffer::new(&buffer[..bytes]))?;
339            }
340        })()
341        .await;
342    };
343    let req = SpawnRequestBuilder::new()
344        .with_debug_name("forward-to-pty-in")
345        .with_async_closure(closure)
346        .build();
347    kernel.kthreads.spawner().spawn_from_request(req);
348
349    let pty_source = pty;
350    let closure = move |current_task: &CurrentTask| {
351        let _result: Result<(), Error> =
352            fasync::LocalExecutor::default().run_singlethreaded(async {
353                let mut buffer = VecOutputBuffer::new(BUFFER_CAPACITY);
354                loop {
355                    buffer.reset();
356                    let bytes = pty_source.read(current_task, &mut buffer)?;
357                    if bytes == 0 {
358                        return Ok(());
359                    }
360                    tx.write_all(buffer.data()).await?;
361                }
362            });
363    };
364    let req = SpawnRequestBuilder::new()
365        .with_debug_name("forward-to-pty-out")
366        .with_sync_closure(closure)
367        .build();
368    kernel.kthreads.spawner().spawn_from_request(req);
369
370    Ok(())
371}
372
373pub async fn serve_container_info(
374    request_stream: fstarcontainer::InfoRequestStream,
375) -> Result<(), Error> {
376    request_stream
377        .map_err(Error::from)
378        .try_for_each_concurrent(None, |event| async {
379            match event {
380                fstarcontainer::InfoRequest::GetJobKoid { responder } => {
381                    let koid = fuchsia_runtime::job_default()
382                        .koid()
383                        .unwrap_or(zx::Koid::from_raw(zx::sys::ZX_KOID_INVALID));
384                    responder.send(&fstarcontainer::InfoGetJobKoidResponse {
385                        koid: Some(koid.raw_koid()),
386                        ..Default::default()
387                    })?;
388                }
389                fstarcontainer::InfoRequest::_UnknownMethod { ordinal, .. } => {
390                    starnix_logging::log_warn!("Unknown InfoRequest method: {}", ordinal);
391                }
392            }
393            Ok(())
394        })
395        .await
396}
397
398pub async fn serve_graphical_presenter(
399    mut request_stream: felement::GraphicalPresenterRequestStream,
400    kernel: &Kernel,
401) -> Result<(), Error> {
402    while let Some(request) = request_stream.next().await {
403        match request.context("reading graphical presenter request")? {
404            felement::GraphicalPresenterRequest::PresentView {
405                view_spec,
406                annotation_controller: _,
407                view_controller_request: _,
408                responder,
409            } => match view_spec.viewport_creation_token {
410                Some(token) => {
411                    let fb = Framebuffer::get(kernel).context("getting framebuffer from kernel")?;
412                    fb.present_view(token);
413                    let _ = responder.send(Ok(()));
414                }
415                None => {
416                    let _ = responder.send(Err(felement::PresentViewError::InvalidArgs));
417                }
418            },
419        }
420    }
421    Ok(())
422}
423
424/// Serves the memory attribution provider for the Kernel ELF component.
425pub fn serve_memory_attribution_provider_elfkernel(
426    mut request_stream: fattribution::ProviderRequestStream,
427    container: &Container,
428) -> impl Future<Output = Result<(), Error>> {
429    let observer = container.new_memory_attribution_observer(request_stream.control_handle());
430    async move {
431        while let Some(event) = request_stream.try_next().await? {
432            match event {
433                fattribution::ProviderRequest::Get { responder } => {
434                    observer.next(responder);
435                }
436                fattribution::ProviderRequest::_UnknownMethod {
437                    ordinal, control_handle, ..
438                } => {
439                    log_error!("Invalid request to AttributionProvider: {ordinal}");
440                    control_handle.shutdown_with_epitaph(zx::Status::INVALID_ARGS);
441                }
442            }
443        }
444        Ok(())
445    }
446}
447
448/// Serves the memory attribution provider for the Container component.
449pub fn serve_memory_attribution_provider_container(
450    mut request_stream: fattribution::ProviderRequestStream,
451    kernel: &Kernel,
452) -> impl Future<Output = ()> + use<> {
453    let observer = kernel.new_memory_attribution_observer(request_stream.control_handle());
454    async move {
455        while let Some(event) = request_stream
456            .try_next()
457            .await
458            .inspect_err(|err| {
459                log_warn!("Error while serving container memory attribution: {:?}", err)
460            })
461            .ok()
462            .flatten()
463        {
464            match event {
465                fattribution::ProviderRequest::Get { responder } => {
466                    observer.next(responder);
467                }
468                fattribution::ProviderRequest::_UnknownMethod {
469                    ordinal, control_handle, ..
470                } => {
471                    log_error!("Invalid request to AttributionProvider: {ordinal}");
472                    control_handle.shutdown_with_epitaph(zx::Status::INVALID_ARGS);
473                }
474            }
475        }
476    }
477}
478
479async fn select_first<O>(f1: impl Future<Output = O>, f2: impl Future<Output = O>) -> O {
480    let f1 = f1.fuse();
481    let f2 = f2.fuse();
482    pin_mut!(f1, f2);
483    select! {
484        f1 = f1 => f1,
485        f2 = f2 => f2,
486    }
487}
488
489/// Serve the LutexController protocol.
490pub async fn serve_lutex_controller(
491    request_stream: fbinder::LutexControllerRequestStream,
492    current_task: &CurrentTask,
493) -> Result<(), Error> {
494    let kernel = current_task.kernel();
495    request_stream
496        .map_err(Error::from)
497        .try_for_each_concurrent(None, |event| async move {
498            match event {
499                fbinder::LutexControllerRequest::WaitBitset { payload, responder } => {
500                    let deadline_and_receiver = (|| {
501                        let vmo = payload.vmo.ok_or_else(|| errno!(EINVAL))?;
502                        let offset = payload.offset.ok_or_else(|| errno!(EINVAL))?;
503                        let value = payload.value.ok_or_else(|| errno!(EINVAL))?;
504                        let mask = payload.mask.unwrap_or(u32::MAX);
505                        let deadline = payload.deadline.map(zx::MonotonicInstant::from_nanos);
506                        kernel
507                            .shared_futexes
508                            .external_wait(vmo.into(), offset, value, mask)
509                            .map(|(event, receiver)| (deadline, event, receiver))
510                    })();
511                    let result = match deadline_and_receiver {
512                        Ok((deadline, event, receiver)) => {
513                            // We construct a specific `wait_fut` to explicitly bind the lifecycle
514                            // of the `event` to the duration of this wait operation. If the FIDL
515                            // client disconnects (or the future is otherwise dropped/cancelled),
516                            // the `event` is immediately dropped. This zeros the strong reference
517                            // count on the `InterruptibleEvent`, signaling to the `FutexTable`
518                            // that this external waiter is now stale and should be garbage
519                            // collected on its next cleanup pass.
520                            let wait_fut = async move {
521                                let _event = event;
522                                let receiver = receiver.map_err(|_| errno!(EINTR));
523                                if let Some(deadline) = deadline {
524                                    let timer =
525                                        fasync::Timer::new(deadline).map(|_| error!(ETIMEDOUT));
526                                    select_first(timer, receiver).await
527                                } else {
528                                    receiver.await
529                                }
530                            };
531                            wait_fut.await
532                        }
533                        Err(e) => Err(e),
534                    };
535                    let result = result.map_err(|e: Errno| {
536                        fposix::Errno::from_primitive(e.code.error_code() as i32)
537                            .unwrap_or(fposix::Errno::Einval)
538                    });
539                    responder
540                        .send(result)
541                        .context("Unable to send LutexControllerRequest::WaitBitset response")?;
542                }
543                fbinder::LutexControllerRequest::WakeBitset { payload, responder } => {
544                    let result = (|| {
545                        let vmo = payload.vmo.ok_or_else(|| errno!(EINVAL))?;
546                        let offset = payload.offset.ok_or_else(|| errno!(EINVAL))?;
547                        let count = payload.count.ok_or_else(|| errno!(EINVAL))?;
548                        let mask = payload.mask.unwrap_or(u32::MAX);
549                        kernel.shared_futexes.external_wake(
550                            vmo.into(),
551                            offset,
552                            count as usize,
553                            mask,
554                        )
555                    })();
556                    let result = result
557                        .map(|count| fbinder::WakeResponse {
558                            count: Some(count as u64),
559                            ..fbinder::WakeResponse::default()
560                        })
561                        .map_err(|e: Errno| {
562                            fposix::Errno::from_primitive(e.code.error_code() as i32)
563                                .unwrap_or(fposix::Errno::Einval)
564                        });
565                    responder
566                        .send(result)
567                        .context("Unable to send LutexControllerRequest::WakeBitset response")?;
568                }
569                fbinder::LutexControllerRequest::CmpRequeue { payload, responder } => {
570                    let result = (|| {
571                        let first_vmo = payload.first_vmo.ok_or_else(|| errno!(EINVAL))?;
572                        let first_offset = payload.first_offset.ok_or_else(|| errno!(EINVAL))?;
573                        let second_vmo = payload.second_vmo;
574                        let second_offset = payload.second_offset.ok_or_else(|| errno!(EINVAL))?;
575                        let wake_count = payload.wake_count.ok_or_else(|| errno!(EINVAL))?;
576                        let requeue_count = payload.requeue_count.ok_or_else(|| errno!(EINVAL))?;
577                        let cmp_val = payload.cmp_val;
578                        kernel.shared_futexes.external_requeue(
579                            first_vmo.into(),
580                            first_offset,
581                            second_vmo.map(Into::into),
582                            second_offset,
583                            wake_count as usize,
584                            requeue_count as usize,
585                            cmp_val,
586                        )
587                    })();
588                    let result = result
589                        .map(|count| fbinder::CmpRequeueResponse {
590                            count: Some(count as u64),
591                            ..fbinder::CmpRequeueResponse::default()
592                        })
593                        .map_err(|e: Errno| {
594                            fposix::Errno::from_primitive(e.code.error_code() as i32)
595                                .unwrap_or(fposix::Errno::Einval)
596                        });
597                    responder
598                        .send(result)
599                        .context("Unable to send LutexControllerRequest::Requeue response")?;
600                }
601                fbinder::LutexControllerRequest::_UnknownMethod { ordinal, .. } => {
602                    log_warn!("Unknown LutexController ordinal: {}", ordinal);
603                }
604            }
605            Ok(())
606        })
607        .await
608        .context("failed fbinder::LutexController request")
609}
610
611#[cfg(test)]
612mod tests {
613    use super::*;
614    use assert_matches::assert_matches;
615    use fidl_fuchsia_posix as fposix;
616    use starnix_core::testing::*;
617    use zx;
618
619    #[fuchsia::test]
620    async fn lutex_controller_test() {
621        spawn_kernel_and_run(async |current_task| {
622            let (sender, receiver) = oneshot::channel::<()>();
623            current_task.kernel.kthreads.spawn_future(
624                {
625                    let kernel = current_task.kernel.clone();
626                    move || async move {
627                        let (lutex_controller, stream) = fidl::endpoints::create_proxy_and_stream::<
628                            fbinder::LutexControllerMarker,
629                        >();
630
631                        // Spawn the server
632                        let server_fut =
633                            serve_lutex_controller(stream, kernel.kthreads.system_task());
634
635                        let client_fut = async move {
636                            const VMO_SIZE: usize = 4 * 1024;
637                            let vmo = zx::Vmo::create(VMO_SIZE as u64).expect("Vmo::create");
638                            // Wait on an incorrect value.
639                            let wait = lutex_controller
640                                .wait_bitset(fbinder::WaitBitsetRequest {
641                                    vmo: Some(
642                                        vmo.duplicate_handle(zx::Rights::SAME_RIGHTS)
643                                            .expect("duplicate vmo"),
644                                    ),
645                                    offset: Some(0),
646                                    value: Some(1),
647                                    ..Default::default()
648                                })
649                                .await
650                                .expect("got_answer");
651                            assert_matches!(wait, Err(fposix::Errno::Eagain));
652
653                            // Wait with a timeout
654                            let wait = lutex_controller
655                                .wait_bitset(fbinder::WaitBitsetRequest {
656                                    vmo: Some(
657                                        vmo.duplicate_handle(zx::Rights::SAME_RIGHTS)
658                                            .expect("duplicate vmo"),
659                                    ),
660                                    offset: Some(0),
661                                    value: Some(0),
662                                    deadline: Some(0),
663                                    ..Default::default()
664                                })
665                                .await
666                                .expect("got_answer");
667                            assert_matches!(wait, Err(fposix::Errno::Etimedout));
668
669                            let mut wait = Box::pin(
670                                lutex_controller.wait_bitset(fbinder::WaitBitsetRequest {
671                                    vmo: Some(
672                                        vmo.duplicate_handle(zx::Rights::SAME_RIGHTS)
673                                            .expect("duplicate vmo"),
674                                    ),
675                                    offset: Some(0),
676                                    value: Some(0),
677                                    deadline: None,
678                                    ..Default::default()
679                                }),
680                            );
681                            // The wait is correct, the future should stay pending until a wake.
682                            assert!(futures::poll!(&mut wait).is_pending());
683
684                            let waken_up: fbinder::WakeResponse = lutex_controller
685                                .wake_bitset(fbinder::WakeBitsetRequest {
686                                    vmo: Some(
687                                        vmo.duplicate_handle(zx::Rights::SAME_RIGHTS)
688                                            .expect("duplicate vmo"),
689                                    ),
690                                    offset: Some(0),
691                                    count: Some(1),
692                                    mask: None,
693                                    ..Default::default()
694                                })
695                                .await
696                                .expect("wake_answer")
697                                .expect("wake_response");
698                            assert_eq!(waken_up.count, Some(1));
699
700                            // The wait should now return.
701                            assert!(wait.await.expect("await_answer").is_ok());
702
703                            // test cmp_requeue
704                            {
705                                let vmo2 = zx::Vmo::create(VMO_SIZE as u64).expect("Vmo::create");
706                                let mut wait1 = Box::pin(
707                                    lutex_controller.wait_bitset(fbinder::WaitBitsetRequest {
708                                        vmo: Some(
709                                            vmo.duplicate_handle(zx::Rights::SAME_RIGHTS)
710                                                .expect("duplicate vmo"),
711                                        ),
712                                        offset: Some(4),
713                                        value: Some(0),
714                                        deadline: None,
715                                        ..Default::default()
716                                    }),
717                                );
718                                let mut wait2 = Box::pin(
719                                    lutex_controller.wait_bitset(fbinder::WaitBitsetRequest {
720                                        vmo: Some(
721                                            vmo.duplicate_handle(zx::Rights::SAME_RIGHTS)
722                                                .expect("duplicate vmo"),
723                                        ),
724                                        offset: Some(4),
725                                        value: Some(0),
726                                        deadline: None,
727                                        ..Default::default()
728                                    }),
729                                );
730                                let cmp_requeue_response = lutex_controller
731                                    .cmp_requeue(fbinder::CmpRequeueRequest {
732                                        first_vmo: Some(
733                                            vmo.duplicate_handle(zx::Rights::SAME_RIGHTS)
734                                                .expect("duplicate vmo"),
735                                        ),
736                                        first_offset: Some(4),
737                                        second_vmo: Some(
738                                            vmo2.duplicate_handle(zx::Rights::SAME_RIGHTS)
739                                                .expect("duplicate vmo"),
740                                        ),
741                                        second_offset: Some(8),
742                                        wake_count: Some(1),
743                                        requeue_count: Some(1),
744                                        cmp_val: Some(0xBAD),
745                                        ..Default::default()
746                                    })
747                                    .await
748                                    .expect("got_answer")
749                                    .unwrap_err();
750                                // 0xBAD is not 0x0, so EAGAIN expected
751                                assert_matches!(cmp_requeue_response, fposix::Errno::Eagain);
752                                let cmp_requeue_response2: fbinder::CmpRequeueResponse =
753                                    lutex_controller
754                                        .cmp_requeue(fbinder::CmpRequeueRequest {
755                                            first_vmo: Some(
756                                                vmo.duplicate_handle(zx::Rights::SAME_RIGHTS)
757                                                    .expect("duplicate vmo"),
758                                            ),
759                                            first_offset: Some(4),
760                                            second_vmo: Some(
761                                                vmo2.duplicate_handle(zx::Rights::SAME_RIGHTS)
762                                                    .expect("duplicate vmo"),
763                                            ),
764                                            second_offset: Some(8),
765                                            wake_count: Some(1),
766                                            requeue_count: Some(1),
767                                            cmp_val: Some(0x0),
768                                            ..Default::default()
769                                        })
770                                        .await
771                                        .expect("got_answer")
772                                        .expect("got_response");
773                                // 1 woken, 1 re-queued
774                                assert_matches!(cmp_requeue_response2.count, Some(2));
775                                // give wait1 and wait2 a chance to complete; we don't rely on this
776                                // being long enough however
777                                fasync::Timer::new(
778                                    fasync::MonotonicDuration::from_millis(100).after_now(),
779                                )
780                                .await;
781                                let mut still_pending_count: u32 = 0;
782                                // Up to one of wait1 and wait2 can be completed by this point, so at
783                                // least one must still be pending.
784                                let wait1_poll = futures::poll!(&mut wait1);
785                                if wait1_poll.is_pending() {
786                                    still_pending_count += 1;
787                                }
788                                let wait2_poll = futures::poll!(&mut wait2);
789                                if wait2_poll.is_pending() {
790                                    still_pending_count += 1;
791                                }
792                                assert!(still_pending_count >= 1);
793                                // at this point exactly one of wait1 or wait2 will remain pending until
794                                // we wake it via vmo2 - we don't know which of wait1 or wait2 we're
795                                // waking here (which is fine)
796                                let vmo2_wake_response: fbinder::WakeResponse = lutex_controller
797                                    .wake_bitset(fbinder::WakeBitsetRequest {
798                                        vmo: Some(
799                                            vmo2.duplicate_handle(zx::Rights::SAME_RIGHTS)
800                                                .expect("duplicate vmo"),
801                                        ),
802                                        offset: Some(8),
803                                        count: Some(1),
804                                        mask: None,
805                                        ..Default::default()
806                                    })
807                                    .await
808                                    .expect("wake_answer")
809                                    .expect("wake_response");
810                                assert_eq!(vmo2_wake_response.count, Some(1));
811                                // Now we know that both wait1 and wait2 are unblocked, so we can
812                                // wait on both of them.
813                                if wait1_poll.is_pending() {
814                                    assert!(wait1.await.expect("await_answer").is_ok());
815                                }
816                                if wait2_poll.is_pending() {
817                                    assert!(wait2.await.expect("await_answer").is_ok());
818                                }
819                            }
820                        };
821
822                        let (server_res, _) = futures::join!(server_fut, client_fut);
823                        server_res.expect("server failed");
824                        let _ = sender.send(());
825                    }
826                },
827                "lutex_controller_test",
828            );
829            receiver.await.expect("test failed");
830        })
831        .await;
832    }
833
834    #[fuchsia::test]
835    async fn container_info_test() {
836        let (info_proxy, stream) =
837            fidl::endpoints::create_proxy_and_stream::<fstarcontainer::InfoMarker>();
838
839        let server_fut = serve_container_info(stream);
840        let client_fut = async move {
841            let response = info_proxy.get_job_koid().await.expect("get_job_koid failed");
842            let expected_koid = fuchsia_runtime::job_default()
843                .koid()
844                .unwrap_or(zx::Koid::from_raw(zx::sys::ZX_KOID_INVALID));
845            assert_eq!(response.koid, Some(expected_koid.raw_koid()));
846            assert_ne!(response.koid, Some(zx::sys::ZX_KOID_INVALID));
847            drop(info_proxy);
848        };
849
850        let (server_res, ()) = futures::join!(server_fut, client_fut);
851        server_res.expect("server failed");
852    }
853}