1use crate::Container;
6use anyhow::{Context as _, Error};
7use fidl::endpoints::{ControlHandle, RequestStream, ServerEnd};
8use fuchsia_async::{
9 DurationExt, {self as fasync},
10};
11use futures::channel::oneshot;
12use futures::{
13 AsyncReadExt, AsyncWriteExt, Future, FutureExt, StreamExt, TryFutureExt, TryStreamExt, pin_mut,
14 select,
15};
16use starnix_core::execution::{create_init_child_process, execute_task_with_prerun_result};
17use starnix_core::fs::devpts::create_main_and_replica;
18use starnix_core::fs::fuchsia::create_fuchsia_pipe;
19use starnix_core::task::dynamic_thread_spawner::SpawnRequestBuilder;
20use starnix_core::task::{CurrentTask, ExitStatus, Kernel, LockedAndTask, ProcessEntryRef};
21use starnix_core::vfs::buffers::{VecInputBuffer, VecOutputBuffer};
22use starnix_core::vfs::file_server::serve_file_at;
23use starnix_core::vfs::socket::VsockSocket;
24use starnix_core::vfs::{FdFlags, FileHandle};
25use starnix_logging::{log_error, log_warn};
26use starnix_modules_framebuffer::Framebuffer;
27use starnix_sync::{Locked, Unlocked};
28use starnix_task_command::TaskCommand;
29use starnix_uapi::auth::Credentials;
30use starnix_uapi::errors::Errno;
31use starnix_uapi::open_flags::OpenFlags;
32use starnix_uapi::signals::UncheckedSignal;
33use starnix_uapi::{errno, error, uapi};
34use std::ffi::CString;
35use std::ops::DerefMut;
36use {
37 fidl_fuchsia_component_runner as frunner, fidl_fuchsia_element as felement,
38 fidl_fuchsia_io as fio, fidl_fuchsia_memory_attribution as fattribution,
39 fidl_fuchsia_posix as fposix, fidl_fuchsia_starnix_binder as fbinder,
40 fidl_fuchsia_starnix_container as fstarcontainer,
41};
42
43use super::start_component;
44
45pub fn expose_root(
46 locked: &mut Locked<Unlocked>,
47 system_task: &CurrentTask,
48 server_end: ServerEnd<fio::DirectoryMarker>,
49) -> Result<(), Error> {
50 let root_file = system_task.open_file(locked, "/".into(), OpenFlags::RDONLY)?;
51 serve_file_at(server_end.into_channel().into(), system_task, &root_file, Credentials::root())?;
52 Ok(())
53}
54
55pub async fn serve_component_runner(
56 request_stream: frunner::ComponentRunnerRequestStream,
57 system_task: &CurrentTask,
58) -> Result<(), Error> {
59 request_stream
60 .try_for_each_concurrent(None, |event| async {
61 match event {
62 frunner::ComponentRunnerRequest::Start { start_info, controller, .. } => {
63 if let Err(e) = start_component(start_info, controller, system_task).await {
64 log_error!("failed to start component: {:?}", e);
65 }
66 }
67 frunner::ComponentRunnerRequest::_UnknownMethod { ordinal, .. } => {
68 log_warn!("Unknown ComponentRunner request: {ordinal}");
69 }
70 }
71 Ok(())
72 })
73 .await
74 .map_err(Error::from)
75}
76
77fn to_winsize(window_size: Option<fstarcontainer::ConsoleWindowSize>) -> uapi::winsize {
78 window_size
79 .map(|window_size| uapi::winsize {
80 ws_row: window_size.rows,
81 ws_col: window_size.cols,
82 ws_xpixel: window_size.x_pixels,
83 ws_ypixel: window_size.y_pixels,
84 })
85 .unwrap_or(uapi::winsize::default())
86}
87
88async fn spawn_console(
89 kernel: &Kernel,
90 payload: fstarcontainer::ControllerSpawnConsoleRequest,
91) -> Result<Result<u8, fstarcontainer::SpawnConsoleError>, Error> {
92 if let (Some(console_in), Some(console_out), Some(binary_path)) =
93 (payload.console_in, payload.console_out, payload.binary_path)
94 {
95 let binary_path = CString::new(binary_path)?;
96 let argv = payload
97 .argv
98 .unwrap_or(vec![])
99 .into_iter()
100 .map(CString::new)
101 .collect::<Result<Vec<_>, _>>()?;
102 let environ = payload
103 .environ
104 .unwrap_or(vec![])
105 .into_iter()
106 .map(CString::new)
107 .collect::<Result<Vec<_>, _>>()?;
108 let window_size = to_winsize(payload.window_size);
109 let current_task = create_init_child_process(
110 kernel.kthreads.unlocked_for_async().deref_mut(),
111 &kernel.weak_self.upgrade().expect("Kernel must still be alive"),
112 TaskCommand::new(binary_path.as_bytes()),
113 Credentials::with_ids(0, 0),
114 None,
115 )?;
116 let (sender, receiver) = oneshot::channel();
117 let pty = execute_task_with_prerun_result(
118 kernel.kthreads.unlocked_for_async().deref_mut(),
119 current_task,
120 move |locked, current_task| {
121 let executable = current_task.open_file(
122 locked,
123 binary_path.as_bytes().into(),
124 OpenFlags::RDONLY,
125 )?;
126 current_task.exec(locked, executable, binary_path, argv, environ)?;
127 let (pty, pts) = create_main_and_replica(locked, ¤t_task, window_size)?;
128 let fd_flags = FdFlags::empty();
129 assert_eq!(0, current_task.add_file(locked, pts.clone(), fd_flags)?.raw());
130 assert_eq!(1, current_task.add_file(locked, pts.clone(), fd_flags)?.raw());
131 assert_eq!(2, current_task.add_file(locked, pts, fd_flags)?.raw());
132 Ok(pty)
133 },
134 move |result| {
135 let _ = match result {
136 Ok(ExitStatus::Exit(exit_code)) => sender.send(Ok(exit_code)),
137 _ => sender.send(Err(fstarcontainer::SpawnConsoleError::Canceled)),
138 };
139 },
140 None,
141 )?;
142 let _ = forward_to_pty(kernel, console_in, console_out, pty).map_err(|e| {
143 log_error!("failed to forward to terminal {:?}", e);
144 });
145
146 Ok(receiver.await?)
147 } else {
148 Ok(Err(fstarcontainer::SpawnConsoleError::InvalidArgs))
149 }
150}
151
152pub async fn serve_container_controller(
153 request_stream: fstarcontainer::ControllerRequestStream,
154 system_task: &CurrentTask,
155) -> Result<(), Error> {
156 request_stream
157 .map_err(Error::from)
158 .try_for_each_concurrent(None, |event| async {
159 match event {
160 fstarcontainer::ControllerRequest::VsockConnect {
161 payload:
162 fstarcontainer::ControllerVsockConnectRequest { port, bridge_socket, .. },
163 ..
164 } => {
165 let Some(port) = port else {
166 log_warn!("vsock connection missing port");
167 return Ok(());
168 };
169 let Some(bridge_socket) = bridge_socket else {
170 log_warn!("vsock connection missing bridge_socket");
171 return Ok(());
172 };
173 connect_to_vsock(port, bridge_socket, system_task).await.unwrap_or_else(|e| {
174 log_error!("failed to connect to vsock {:?}", e);
175 });
176 }
177 fstarcontainer::ControllerRequest::SpawnConsole { payload, responder } => {
178 responder.send(spawn_console(system_task.kernel(), payload).await?)?;
179 }
180 fstarcontainer::ControllerRequest::GetVmoReferences { payload, responder } => {
181 if let Some(koid) = payload.koid {
182 let thread_groups = system_task
183 .kernel()
184 .pids
185 .read()
186 .get_thread_groups()
187 .collect::<Vec<_>>();
188 let mut results = vec![];
189 for thread_group in thread_groups {
190 if let Some(leader) =
191 system_task.get_task(thread_group.leader).upgrade()
192 {
193 let fds = leader.files.get_all_fds();
194 for fd in fds {
195 if let Ok(file) = leader.files.get(fd) {
196 if let Ok(memory) = file.get_memory(
197 system_task
198 .kernel()
199 .kthreads
200 .unlocked_for_async()
201 .deref_mut(),
202 system_task,
203 None,
204 starnix_core::mm::ProtectionFlags::READ,
205 ) {
206 let memory_koid = memory
207 .info()
208 .expect("Failed to get memory info")
209 .koid;
210 if memory_koid.raw_koid() == koid {
211 let process_name = thread_group
212 .process
213 .get_name()
214 .unwrap_or_default();
215 results.push(fstarcontainer::VmoReference {
216 process_name: Some(process_name.to_string()),
217 pid: Some(leader.get_pid() as u64),
218 fd: Some(fd.raw()),
219 koid: Some(koid),
220 ..Default::default()
221 });
222 }
223 }
224 }
225 }
226 }
227 }
228 let _ =
229 responder.send(&fstarcontainer::ControllerGetVmoReferencesResponse {
230 references: Some(results),
231 ..Default::default()
232 });
233 }
234 }
235 fstarcontainer::ControllerRequest::GetJobHandle { responder } => {
236 let _result = responder.send(fstarcontainer::ControllerGetJobHandleResponse {
237 job: Some(
238 fuchsia_runtime::job_default()
239 .duplicate(zx::Rights::SAME_RIGHTS)
240 .expect("Failed to dup handle"),
241 ),
242 ..Default::default()
243 });
244 }
245 fstarcontainer::ControllerRequest::SendSignal {
246 payload:
247 fstarcontainer::ControllerSendSignalRequest {
248 pid: Some(pid),
249 signal: Some(signal),
250 ..
251 },
252 responder,
253 } => {
254 let pids = system_task.kernel().pids.read();
255 if let Some(ProcessEntryRef::Process(target_thread_group)) =
256 pids.get_process(pid)
257 {
258 #[allow(
259 clippy::undocumented_unsafe_blocks,
260 reason = "Force documented unsafe blocks in Starnix"
261 )]
262 match unsafe {
263 target_thread_group.send_signal_unchecked_debug(
264 system_task,
265 UncheckedSignal::new(signal),
266 )
267 } {
268 Ok(_) => {
269 let _result = responder.send(Ok(()));
270 }
271 Err(_) => {
272 let _result =
273 responder.send(Err(fstarcontainer::SignalError::InvalidSignal));
274 }
275 };
276 } else {
277 let _result =
278 responder.send(Err(fstarcontainer::SignalError::InvalidTarget));
279 };
280 }
281 fstarcontainer::ControllerRequest::SendSignal { responder, .. } => {
283 log_error!("malformed SendSignal request");
284 let _result = responder.send(Err(fstarcontainer::SignalError::InvalidTarget));
285 }
286 fstarcontainer::ControllerRequest::SetSyscallLogFilter { payload, responder } => {
287 if let Some(process_name) = payload.process_name {
288 system_task.kernel().add_syscall_log_filter(&process_name);
289 let _ = responder.send(Ok(()));
290 } else {
291 let _ = responder.send(Err(
292 fstarcontainer::SetSyscallLogFilterError::MissingProcessName,
293 ));
294 }
295 }
296 fstarcontainer::ControllerRequest::ClearSyscallLogFilters { responder } => {
297 system_task.kernel().clear_syscall_log_filters();
298 let _ = responder.send();
299 }
300 fstarcontainer::ControllerRequest::_UnknownMethod { .. } => (),
301 }
302 Ok(())
303 })
304 .await
305}
306
307async fn connect_to_vsock(
308 port: u32,
309 bridge_socket: fidl::Socket,
310 system_task: &CurrentTask,
311) -> Result<(), Error> {
312 let socket = loop {
313 if let Ok(socket) = system_task.kernel().default_abstract_vsock_namespace.lookup(&port) {
314 break socket;
315 };
316 fasync::Timer::new(fasync::MonotonicDuration::from_millis(100).after_now()).await;
317 };
318
319 let pipe = create_fuchsia_pipe(
320 system_task.kernel().kthreads.unlocked_for_async().deref_mut(),
321 system_task,
322 bridge_socket,
323 OpenFlags::RDWR | OpenFlags::NONBLOCK,
324 )?;
325 socket.downcast_socket::<VsockSocket>().unwrap().remote_connection(
326 system_task.kernel().kthreads.unlocked_for_async().deref_mut(),
327 &socket,
328 system_task,
329 pipe,
330 )?;
331
332 Ok(())
333}
334
335fn forward_to_pty(
336 kernel: &Kernel,
337 console_in: fidl::Socket,
338 console_out: fidl::Socket,
339 pty: FileHandle,
340) -> Result<(), Error> {
341 const BUFFER_CAPACITY: usize = 8192;
343
344 let mut rx = fuchsia_async::Socket::from_socket(console_in);
345 let mut tx = fuchsia_async::Socket::from_socket(console_out);
346 let pty_sink = pty.clone();
347 let closure = async move |locked_and_task: LockedAndTask<'_>| {
348 let _result: Result<(), Error> = (async || {
349 let mut buffer = vec![0u8; BUFFER_CAPACITY];
350 loop {
351 let bytes = rx.read(&mut buffer[..]).await?;
352 if bytes == 0 {
353 return Ok(());
354 }
355 pty_sink.write(
356 &mut locked_and_task.unlocked(),
357 locked_and_task.current_task(),
358 &mut VecInputBuffer::new(&buffer[..bytes]),
359 )?;
360 }
361 })()
362 .await;
363 };
364 let req = SpawnRequestBuilder::new()
365 .with_debug_name("forward-to-pty-in")
366 .with_async_closure(closure)
367 .build();
368 kernel.kthreads.spawner().spawn_from_request(req);
369
370 let pty_source = pty;
371 let closure = move |locked: &mut Locked<Unlocked>, current_task: &CurrentTask| {
372 let _result: Result<(), Error> =
373 fasync::LocalExecutor::default().run_singlethreaded(async {
374 let mut buffer = VecOutputBuffer::new(BUFFER_CAPACITY);
375 loop {
376 buffer.reset();
377 let bytes = pty_source.read(locked, current_task, &mut buffer)?;
378 if bytes == 0 {
379 return Ok(());
380 }
381 tx.write_all(buffer.data()).await?;
382 }
383 });
384 };
385 let req = SpawnRequestBuilder::new()
386 .with_debug_name("forward-to-pty-out")
387 .with_sync_closure(closure)
388 .build();
389 kernel.kthreads.spawner().spawn_from_request(req);
390
391 Ok(())
392}
393
394pub async fn serve_graphical_presenter(
395 mut request_stream: felement::GraphicalPresenterRequestStream,
396 kernel: &Kernel,
397) -> Result<(), Error> {
398 while let Some(request) = request_stream.next().await {
399 match request.context("reading graphical presenter request")? {
400 felement::GraphicalPresenterRequest::PresentView {
401 view_spec,
402 annotation_controller: _,
403 view_controller_request: _,
404 responder,
405 } => match view_spec.viewport_creation_token {
406 Some(token) => {
407 let fb = Framebuffer::get(kernel).context("getting framebuffer from kernel")?;
408 fb.present_view(token);
409 let _ = responder.send(Ok(()));
410 }
411 None => {
412 let _ = responder.send(Err(felement::PresentViewError::InvalidArgs));
413 }
414 },
415 }
416 }
417 Ok(())
418}
419
420pub fn serve_memory_attribution_provider_elfkernel(
422 mut request_stream: fattribution::ProviderRequestStream,
423 container: &Container,
424) -> impl Future<Output = Result<(), Error>> {
425 let observer = container.new_memory_attribution_observer(request_stream.control_handle());
426 async move {
427 while let Some(event) = request_stream.try_next().await? {
428 match event {
429 fattribution::ProviderRequest::Get { responder } => {
430 observer.next(responder);
431 }
432 fattribution::ProviderRequest::_UnknownMethod {
433 ordinal, control_handle, ..
434 } => {
435 log_error!("Invalid request to AttributionProvider: {ordinal}");
436 control_handle.shutdown_with_epitaph(zx::Status::INVALID_ARGS);
437 }
438 }
439 }
440 Ok(())
441 }
442}
443
444pub fn serve_memory_attribution_provider_container(
446 mut request_stream: fattribution::ProviderRequestStream,
447 kernel: &Kernel,
448) -> impl Future<Output = ()> + use<> {
449 let observer = kernel.new_memory_attribution_observer(request_stream.control_handle());
450 async move {
451 while let Some(event) = request_stream
452 .try_next()
453 .await
454 .inspect_err(|err| {
455 log_warn!("Error while serving container memory attribution: {:?}", err)
456 })
457 .ok()
458 .flatten()
459 {
460 match event {
461 fattribution::ProviderRequest::Get { responder } => {
462 observer.next(responder);
463 }
464 fattribution::ProviderRequest::_UnknownMethod {
465 ordinal, control_handle, ..
466 } => {
467 log_error!("Invalid request to AttributionProvider: {ordinal}");
468 control_handle.shutdown_with_epitaph(zx::Status::INVALID_ARGS);
469 }
470 }
471 }
472 }
473}
474
475async fn select_first<O>(f1: impl Future<Output = O>, f2: impl Future<Output = O>) -> O {
476 let f1 = f1.fuse();
477 let f2 = f2.fuse();
478 pin_mut!(f1, f2);
479 select! {
480 f1 = f1 => f1,
481 f2 = f2 => f2,
482 }
483}
484
485pub async fn serve_lutex_controller(
487 request_stream: fbinder::LutexControllerRequestStream,
488 current_task: &CurrentTask,
489) -> Result<(), Error> {
490 let kernel = current_task.kernel();
491 request_stream
492 .map_err(Error::from)
493 .try_for_each_concurrent(None, |event| async move {
494 match event {
495 fbinder::LutexControllerRequest::WaitBitset { payload, responder } => {
496 let deadline_and_receiver = (|| {
497 let mut unlocked = kernel.kthreads.unlocked_for_async();
498 let vmo = payload.vmo.ok_or_else(|| errno!(EINVAL))?;
499 let offset = payload.offset.ok_or_else(|| errno!(EINVAL))?;
500 let value = payload.value.ok_or_else(|| errno!(EINVAL))?;
501 let mask = payload.mask.unwrap_or(u32::MAX);
502 let deadline = payload.deadline.map(zx::MonotonicInstant::from_nanos);
503 kernel
504 .shared_futexes
505 .external_wait(&mut unlocked, vmo.into(), offset, value, mask)
506 .map(|(event, receiver)| (deadline, event, receiver))
507 })();
508 let result = match deadline_and_receiver {
509 Ok((deadline, event, receiver)) => {
510 let wait_fut = async move {
518 let _event = event;
519 let receiver = receiver.map_err(|_| errno!(EINTR));
520 if let Some(deadline) = deadline {
521 let timer =
522 fasync::Timer::new(deadline).map(|_| error!(ETIMEDOUT));
523 select_first(timer, receiver).await
524 } else {
525 receiver.await
526 }
527 };
528 wait_fut.await
529 }
530 Err(e) => Err(e),
531 };
532 let result = result.map_err(|e: Errno| {
533 fposix::Errno::from_primitive(e.code.error_code() as i32)
534 .unwrap_or(fposix::Errno::Einval)
535 });
536 responder
537 .send(result)
538 .context("Unable to send LutexControllerRequest::WaitBitset response")?;
539 }
540 fbinder::LutexControllerRequest::WakeBitset { payload, responder } => {
541 let result = (|| {
542 let mut unlocked = kernel.kthreads.unlocked_for_async();
543 let vmo = payload.vmo.ok_or_else(|| errno!(EINVAL))?;
544 let offset = payload.offset.ok_or_else(|| errno!(EINVAL))?;
545 let count = payload.count.ok_or_else(|| errno!(EINVAL))?;
546 let mask = payload.mask.unwrap_or(u32::MAX);
547 kernel.shared_futexes.external_wake(
548 &mut unlocked,
549 vmo.into(),
550 offset,
551 count as usize,
552 mask,
553 )
554 })();
555 let result = result
556 .map(|count| fbinder::WakeResponse {
557 count: Some(count as u64),
558 ..fbinder::WakeResponse::default()
559 })
560 .map_err(|e: Errno| {
561 fposix::Errno::from_primitive(e.code.error_code() as i32)
562 .unwrap_or(fposix::Errno::Einval)
563 });
564 responder
565 .send(result)
566 .context("Unable to send LutexControllerRequest::WakeBitset response")?;
567 }
568 fbinder::LutexControllerRequest::_UnknownMethod { ordinal, .. } => {
569 log_warn!("Unknown LutexController ordinal: {}", ordinal);
570 }
571 }
572 Ok(())
573 })
574 .await
575 .context("failed fbinder::LutexController request")
576}
577
578#[cfg(test)]
579mod tests {
580 use super::*;
581 use assert_matches::assert_matches;
582 use fidl::HandleBased;
583 use starnix_core::testing::*;
584 use {fidl_fuchsia_posix as fposix, zx};
585
586 #[fuchsia::test]
587 async fn lutex_controller_test() {
588 spawn_kernel_and_run(async |mut _locked, current_task| {
589 let (sender, receiver) = oneshot::channel::<()>();
590 current_task.kernel.kthreads.spawn_future(
591 {
592 let kernel = current_task.kernel.clone();
593 move || async move {
594 let (lutex_controller, stream) = fidl::endpoints::create_proxy_and_stream::<
595 fbinder::LutexControllerMarker,
596 >();
597
598 let server_fut =
600 serve_lutex_controller(stream, kernel.kthreads.system_task());
601
602 let client_fut = async move {
603 const VMO_SIZE: usize = 4 * 1024;
604 let vmo = zx::Vmo::create(VMO_SIZE as u64).expect("Vmo::create");
605 let wait = lutex_controller
607 .wait_bitset(fbinder::WaitBitsetRequest {
608 vmo: Some(
609 vmo.duplicate_handle(zx::Rights::SAME_RIGHTS)
610 .expect("duplicate vmo"),
611 ),
612 offset: Some(0),
613 value: Some(1),
614 ..Default::default()
615 })
616 .await
617 .expect("got_answer");
618 assert_matches!(wait, Err(fposix::Errno::Eagain));
619
620 let wait = lutex_controller
622 .wait_bitset(fbinder::WaitBitsetRequest {
623 vmo: Some(
624 vmo.duplicate_handle(zx::Rights::SAME_RIGHTS)
625 .expect("duplicate vmo"),
626 ),
627 offset: Some(0),
628 value: Some(0),
629 deadline: Some(0),
630 ..Default::default()
631 })
632 .await
633 .expect("got_answer");
634 assert_matches!(wait, Err(fposix::Errno::Etimedout));
635
636 let mut wait = Box::pin(
637 lutex_controller.wait_bitset(fbinder::WaitBitsetRequest {
638 vmo: Some(
639 vmo.duplicate_handle(zx::Rights::SAME_RIGHTS)
640 .expect("duplicate vmo"),
641 ),
642 offset: Some(0),
643 value: Some(0),
644 deadline: None,
645 ..Default::default()
646 }),
647 );
648 assert!(futures::poll!(&mut wait).is_pending());
650
651 let waken_up: fbinder::WakeResponse = lutex_controller
652 .wake_bitset(fbinder::WakeBitsetRequest {
653 vmo: Some(
654 vmo.duplicate_handle(zx::Rights::SAME_RIGHTS)
655 .expect("duplicate vmo"),
656 ),
657 offset: Some(0),
658 count: Some(1),
659 mask: None,
660 ..Default::default()
661 })
662 .await
663 .expect("wake_answer")
664 .expect("wake_response");
665 assert_eq!(waken_up.count, Some(1));
666
667 assert!(wait.await.expect("await_answer").is_ok());
669 };
670
671 let (server_res, _) = futures::join!(server_fut, client_fut);
672 server_res.expect("server failed");
673 let _ = sender.send(());
674 }
675 },
676 "lutex_controller_test",
677 );
678 receiver.await.expect("test failed");
679 })
680 .await;
681 }
682}