Skip to content

Commit c6532de

Browse files
jasonwhitefacebook-github-bot
authored andcommitted
Open source experimental in-guest interception
Differential Revision: D41099658 fbshipit-source-id: 52b414e27abd3c0c3a6367dea225724f4220793b
1 parent 0a023a3 commit c6532de

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+6643
-0
lines changed

experimental/nostd-print/Cargo.toml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# @generated by autocargo
2+
3+
[package]
4+
name = "nostd-print"
5+
version = "0.1.0"
6+
authors = ["Meta Platforms"]
7+
edition = "2021"
8+
license = "BSD-2-Clause"
9+
10+
[dependencies]
11+
syscalls = { version = "0.6.7", features = ["serde"] }

experimental/nostd-print/src/lib.rs

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
//! Provides helpers for printing formatted messages to stdout/stderr without
10+
//! relying on std.
11+
12+
use core::fmt;
13+
use core::fmt::Write;
14+
15+
use syscalls::syscall3;
16+
use syscalls::Errno;
17+
use syscalls::Sysno;
18+
19+
#[inline(always)]
20+
fn sys_write(fd: i32, buf: &[u8]) -> Result<usize, Errno> {
21+
unsafe { syscall3(Sysno::write, fd as usize, buf.as_ptr() as usize, buf.len()) }
22+
}
23+
24+
fn sys_write_all(fd: i32, mut buf: &[u8]) -> Result<(), Errno> {
25+
while !buf.is_empty() {
26+
match sys_write(fd, buf) {
27+
Ok(n) => buf = &buf[n..],
28+
Err(Errno::EINTR) => continue,
29+
Err(errno) => return Err(errno),
30+
}
31+
}
32+
Ok(())
33+
}
34+
35+
struct Stdio<const N: usize = 4096> {
36+
fd: i32,
37+
buf: [u8; N],
38+
len: usize,
39+
}
40+
41+
impl<const N: usize> Stdio<N> {
42+
pub fn new(fd: i32) -> Self {
43+
Self {
44+
fd,
45+
buf: [0; N],
46+
len: 0,
47+
}
48+
}
49+
50+
pub fn write_all(&mut self, mut buf: &[u8]) -> Result<(), Errno> {
51+
while !buf.is_empty() {
52+
if self.buf[self.len..].is_empty() {
53+
self.flush()?;
54+
}
55+
let remaining = &mut self.buf[self.len..];
56+
let count = remaining.len().min(buf.len());
57+
remaining[0..count].copy_from_slice(&buf[0..count]);
58+
self.len += count;
59+
buf = &buf[count..];
60+
}
61+
62+
Ok(())
63+
}
64+
65+
/// Flushes the buffered writes to the file descriptor.
66+
pub fn flush(&mut self) -> Result<(), Errno> {
67+
sys_write_all(self.fd, &self.buf[0..self.len])?;
68+
self.len = 0;
69+
Ok(())
70+
}
71+
}
72+
73+
impl<const N: usize> Drop for Stdio<N> {
74+
fn drop(&mut self) {
75+
let _ = self.flush();
76+
}
77+
}
78+
79+
impl fmt::Write for Stdio {
80+
fn write_str(&mut self, s: &str) -> fmt::Result {
81+
self.write_all(s.as_bytes()).map_err(|_| fmt::Error)
82+
}
83+
}
84+
85+
fn _inner_print(fd: i32, args: fmt::Arguments<'_>, newline: bool) -> fmt::Result {
86+
let mut f = Stdio::new(fd);
87+
f.write_fmt(args)?;
88+
89+
if newline {
90+
f.write_str("\n")?;
91+
}
92+
93+
Ok(())
94+
}
95+
96+
#[doc(hidden)]
97+
pub fn _print(fd: i32, args: fmt::Arguments<'_>, newline: bool) {
98+
// Ignore the error.
99+
let _ = _inner_print(fd, args, newline);
100+
}
101+
102+
#[macro_export]
103+
macro_rules! print {
104+
($($arg:tt)*) => ($crate::_print(1, ::core::format_args!($($arg)*), false));
105+
}
106+
107+
#[macro_export]
108+
macro_rules! eprint {
109+
($($arg:tt)*) => ($crate::_print(2, ::core::format_args!($($arg)*), false));
110+
}
111+
112+
#[macro_export]
113+
macro_rules! println {
114+
() => ($crate::print!("\n"));
115+
($($arg:tt)*) => ({
116+
// Purposefully avoiding format_args_nl because it requires a nightly
117+
// feature.
118+
$crate::_print(1, ::core::format_args!($($arg)*), true);
119+
})
120+
}
121+
122+
#[macro_export]
123+
macro_rules! eprintln {
124+
() => ($crate::eprint!("\n"));
125+
($($arg:tt)*) => ({
126+
// Purposefully avoiding format_args_nl because it requires a nightly
127+
// feature.
128+
$crate::_print(2, ::core::format_args!($($arg)*), true);
129+
})
130+
}

experimental/reverie-host/Cargo.toml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# @generated by autocargo
2+
3+
[package]
4+
name = "reverie-host"
5+
version = "0.1.0"
6+
authors = ["Meta Platforms"]
7+
edition = "2021"
8+
license = "BSD-2-Clause"
9+
10+
[dependencies]
11+
anyhow = "1.0.65"
12+
dirs = "2.0"
13+
reverie-process = { version = "0.1.0", path = "../../reverie-process" }
14+
reverie-rpc = { version = "0.1.0", path = "../reverie-rpc" }
15+
serde = { version = "1.0.136", features = ["derive", "rc"] }
16+
tempfile = "3.3"
17+
tokio = { version = "1.21.2", features = ["full", "test-util", "tracing"] }
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
use core::marker::Unpin;
10+
use std::io;
11+
12+
use serde::Deserialize;
13+
use serde::Serialize;
14+
use tokio::io::AsyncRead;
15+
use tokio::io::AsyncReadExt;
16+
use tokio::io::AsyncWrite;
17+
use tokio::io::AsyncWriteExt;
18+
19+
pub async fn read<'a, T, S>(stream: &mut S, buf: &'a mut Vec<u8>) -> io::Result<T>
20+
where
21+
T: Deserialize<'a>,
22+
S: AsyncRead + Unpin,
23+
{
24+
let len = stream.read_u32().await? as usize;
25+
26+
buf.resize(len, 0);
27+
28+
stream.read_exact(buf).await?;
29+
30+
reverie_rpc::decode_frame(buf)
31+
}
32+
33+
pub async fn write<T, S>(stream: &mut S, buf: &mut Vec<u8>, item: T) -> io::Result<()>
34+
where
35+
T: Serialize,
36+
S: AsyncWrite + Unpin,
37+
{
38+
buf.clear();
39+
40+
reverie_rpc::encode(&item, buf)?;
41+
42+
stream.write_all(buf).await
43+
}

experimental/reverie-host/src/lib.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
//! This crate does two main things:
10+
//! * Handles launching the root child process.
11+
//! * Provides an interface for managing global state for in-guest backends.
12+
13+
mod codec;
14+
mod server;
15+
mod tracer;
16+
17+
pub use server::*;
18+
pub use tracer::*;
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
use std::path::Path;
10+
use std::path::PathBuf;
11+
12+
use anyhow::Result;
13+
use reverie_rpc::Service;
14+
use tempfile::NamedTempFile;
15+
use tokio::net::UnixListener;
16+
17+
use super::codec;
18+
19+
/// A global state server.
20+
pub struct Server {
21+
socket: NamedTempFile<UnixListener>,
22+
}
23+
24+
impl Server {
25+
/// Creates the server, but does not yet listen for incoming connections.
26+
pub fn new() -> Result<Self> {
27+
let sock_dir = dirs::runtime_dir().unwrap_or_else(|| PathBuf::from("/tmp"));
28+
29+
let prefix = format!("reverie-{}-", std::process::id());
30+
let socket = tempfile::Builder::new()
31+
.prefix(&prefix)
32+
.suffix(".sock")
33+
.make_in(sock_dir, |path| UnixListener::bind(path))?;
34+
35+
Ok(Self { socket })
36+
}
37+
38+
/// Returns the path to the socket.
39+
pub fn sock_path(&self) -> &Path {
40+
self.socket.path()
41+
}
42+
43+
/// Accepts new socket connections and processes them.
44+
pub async fn serve<S>(&self, service: S) -> !
45+
where
46+
S: Service + Clone + Send + Sync + 'static,
47+
{
48+
loop {
49+
match self.socket.as_file().accept().await {
50+
Ok((mut stream, _addr)) => {
51+
let service = service.clone();
52+
53+
tokio::spawn(async move {
54+
let mut reader_buf = Vec::with_capacity(1024);
55+
let mut writer_buf = Vec::with_capacity(1024);
56+
57+
while let Ok(request) = codec::read(&mut stream, &mut reader_buf).await {
58+
if let Some(response) = service.call(request).await {
59+
// Only send back a response if this request has
60+
// an associated response. This lets us have
61+
// "send-only" messages, which are useful for
62+
// accumulating state.
63+
codec::write(&mut stream, &mut writer_buf, response)
64+
.await
65+
.unwrap();
66+
}
67+
}
68+
});
69+
}
70+
Err(e) => {
71+
eprintln!("connection failed: {}", e);
72+
}
73+
}
74+
}
75+
}
76+
}

0 commit comments

Comments
 (0)