Initial commit
This commit is contained in:
commit
3246be12e1
|
@ -0,0 +1 @@
|
||||||
|
/target
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,22 @@
|
||||||
|
[package]
|
||||||
|
name = "syndicate-pty-driver"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
lazy_static = "1.4.0"
|
||||||
|
libc = "0.2.150"
|
||||||
|
preserves-schema = "4.992.0"
|
||||||
|
pty = "0.2.2"
|
||||||
|
|
||||||
|
# syndicate = { path = "../syndicate-rs/syndicate", version = "0.30"}
|
||||||
|
syndicate = "0.30"
|
||||||
|
|
||||||
|
syndicate-macros = "0.25"
|
||||||
|
|
||||||
|
tokio = { version = "1.34.0", features = ["io-std", "fs"] }
|
||||||
|
tracing = "0.1.40"
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
preserves-schema = "4.992.0"
|
||||||
|
syndicate-schema-plugin = "0.2.0"
|
|
@ -0,0 +1,15 @@
|
||||||
|
use preserves_schema::compiler::*;
|
||||||
|
|
||||||
|
fn main() -> std::io::Result<()> {
|
||||||
|
let buildroot = std::path::PathBuf::from(std::env::var_os("OUT_DIR").unwrap());
|
||||||
|
|
||||||
|
let mut gen_dir = buildroot.clone();
|
||||||
|
gen_dir.push("src/schemas");
|
||||||
|
|
||||||
|
let mut c = CompilerConfig::new(gen_dir, "crate::schemas".to_owned());
|
||||||
|
c.plugins.push(Box::new(syndicate_schema_plugin::PatternPlugin));
|
||||||
|
|
||||||
|
let inputs = expand_inputs(&vec!["protocols/schema-bundle.bin".to_owned()])?;
|
||||||
|
c.load_schemas_and_bundles(&inputs, &vec![])?;
|
||||||
|
compile(&c)
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
all: schema-bundle.bin
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f schema-bundle.bin
|
||||||
|
|
||||||
|
schema-bundle.bin: schemas/*.prs
|
||||||
|
preserves-schemac schemas > $@.tmp
|
||||||
|
mv $@.tmp $@
|
|
@ -0,0 +1,4 @@
|
||||||
|
´³bundle·µ³pty„´³schema·³version°³definitions·³Chunk´³atom³
|
||||||
|
ByteString„³PtyInput´³rec´³lit³ pty-input„´³tupleµ´³named³id³any„´³named³data´³refµ„³Chunk„„„„„³ PtyOutput´³rec´³lit³
|
||||||
|
pty-output„´³tupleµ´³named³id³any„´³named³data´³refµ„³Chunk„„„„„³
|
||||||
|
PtySession´³rec´³lit³pty-session„´³tupleµ´³named³id³any„´³named³commandLine´³refµ„³CommandLine„„„„„³CommandLine´³tuplePrefixµ´³named³command³any„„´³named³args´³seqof³any„„„³PtySessionRunning´³rec´³lit³pty-session-running„´³tupleµ´³named³id³any„„„„„³embeddedType€„„„„
|
|
@ -0,0 +1,14 @@
|
||||||
|
version 1 .
|
||||||
|
|
||||||
|
PtySession = <pty-session @id any @commandLine CommandLine> .
|
||||||
|
CommandLine = [@command any @args any ...] .
|
||||||
|
|
||||||
|
PtySessionRunning = <pty-session-running @id any> .
|
||||||
|
|
||||||
|
# To the subprocess
|
||||||
|
PtyInput = <pty-input @id any @data Chunk> .
|
||||||
|
|
||||||
|
# From the subprocess
|
||||||
|
PtyOutput = <pty-output @id any @data Chunk> .
|
||||||
|
|
||||||
|
Chunk = bytes .
|
|
@ -0,0 +1 @@
|
||||||
|
nightly
|
|
@ -0,0 +1,9 @@
|
||||||
|
<require-service <daemon pty>>
|
||||||
|
<daemon pty {
|
||||||
|
argv: "cargo run"
|
||||||
|
protocol: application/syndicate
|
||||||
|
env: { RUST_LOG: "syndicate::dataspace=trace,syndicate_pty_driver=debug,info" }
|
||||||
|
}>
|
||||||
|
? <service-object <daemon pty> ?cap> [
|
||||||
|
$cap += <pty-session 1 [bash -i]>
|
||||||
|
]
|
|
@ -0,0 +1,136 @@
|
||||||
|
use std::io::Write;
|
||||||
|
use std::os::unix::io::AsRawFd;
|
||||||
|
use std::os::unix::io::FromRawFd;
|
||||||
|
use std::os::unix::prelude::CommandExt;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use syndicate::actor::Account;
|
||||||
|
use syndicate::actor::Activation;
|
||||||
|
use syndicate::actor::Actor;
|
||||||
|
use syndicate::actor::ActorError;
|
||||||
|
use syndicate::actor::ActorResult;
|
||||||
|
use syndicate::actor::AnyValue;
|
||||||
|
use syndicate::actor::Cap;
|
||||||
|
use syndicate::actor::LinkedTaskTermination;
|
||||||
|
use syndicate::dataspace::Dataspace;
|
||||||
|
use syndicate::enclose;
|
||||||
|
use syndicate::relay;
|
||||||
|
use syndicate::value::NestedValue;
|
||||||
|
|
||||||
|
use syndicate::value::NoEmbeddedDomainCodec;
|
||||||
|
use syndicate::value::TextWriter;
|
||||||
|
use syndicate_macros::during;
|
||||||
|
use syndicate_macros::on_message;
|
||||||
|
|
||||||
|
use tokio::io::AsyncReadExt;
|
||||||
|
|
||||||
|
mod schemas {
|
||||||
|
include!(concat!(env!("OUT_DIR"), "/src/schemas/mod.rs"));
|
||||||
|
}
|
||||||
|
|
||||||
|
preserves_schema::define_language!(language(): Language<AnyValue> {
|
||||||
|
syndicate: syndicate::schemas::Language,
|
||||||
|
main: crate::schemas::Language,
|
||||||
|
});
|
||||||
|
|
||||||
|
use schemas::pty::{PtySession, PtySessionRunning, Chunk, PtyOutput};
|
||||||
|
|
||||||
|
fn stringify(v: &AnyValue) -> Result<String, ActorError> {
|
||||||
|
Ok(TextWriter::encode(&mut NoEmbeddedDomainCodec, v)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pty_session(t: &mut Activation, local_ds: Arc<Cap>, s: PtySession<AnyValue>) -> ActorResult {
|
||||||
|
let mut pieces: Vec<String> = Vec::new();
|
||||||
|
for piece_value in s.command_line.args.into_iter() {
|
||||||
|
pieces.push(stringify(&piece_value)?);
|
||||||
|
}
|
||||||
|
let mut cmd = std::process::Command::new(stringify(&s.command_line.command)?);
|
||||||
|
cmd.args(pieces);
|
||||||
|
tracing::info!(?cmd);
|
||||||
|
|
||||||
|
let fork = pty::fork::Fork::from_ptmx()?;
|
||||||
|
match &fork {
|
||||||
|
pty::prelude::Fork::Parent(child_pid, parent) => {
|
||||||
|
tracing::info!(?child_pid);
|
||||||
|
let mut parent_w: std::fs::File = unsafe { FromRawFd::from_raw_fd(libc::dup(parent.as_raw_fd())) };
|
||||||
|
let mut parent_r: tokio::fs::File = unsafe { FromRawFd::from_raw_fd(libc::dup(parent.as_raw_fd())) };
|
||||||
|
let child_pid = *child_pid;
|
||||||
|
let facet = t.facet.clone();
|
||||||
|
|
||||||
|
local_ds.assert(t, language(), &PtySessionRunning {
|
||||||
|
id: s.id.clone(),
|
||||||
|
});
|
||||||
|
|
||||||
|
on_message!(t, local_ds, language(), <pty-input #(&s.id) $body: Chunk>, |_t| {
|
||||||
|
tracing::trace!(?body, "sending to child");
|
||||||
|
parent_w.write_all(&body.0[..])?;
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
|
||||||
|
t.linked_task(Some(AnyValue::symbol("waiter")), async move {
|
||||||
|
let mut status: i32 = 0;
|
||||||
|
loop {
|
||||||
|
match unsafe { libc::waitpid(child_pid, &mut status, libc::WNOHANG) } {
|
||||||
|
0 => tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await,
|
||||||
|
-1 => Err(std::io::Error::last_os_error())?,
|
||||||
|
_ => break,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tracing::info!(?status, "child exited");
|
||||||
|
Ok(LinkedTaskTermination::Normal)
|
||||||
|
});
|
||||||
|
|
||||||
|
t.linked_task(Some(AnyValue::symbol("reader")), async move {
|
||||||
|
let read_account = Account::new(Some(AnyValue::symbol("reader_account")), None);
|
||||||
|
let mut buf = [0; 1024];
|
||||||
|
loop {
|
||||||
|
let n = parent_r.read(&mut buf[..]).await?;
|
||||||
|
tracing::trace!(?n, buf=?&buf[0..n], "receiving from child");
|
||||||
|
if n == 0 || !facet.activate(&read_account, None, |t| {
|
||||||
|
local_ds.message(t, language(), &PtyOutput {
|
||||||
|
id: s.id.clone(),
|
||||||
|
data: Chunk(buf[0..n].to_vec()),
|
||||||
|
});
|
||||||
|
Ok(())
|
||||||
|
}) {
|
||||||
|
unsafe {
|
||||||
|
libc::kill(child_pid, libc::SIGTERM);
|
||||||
|
}
|
||||||
|
return Ok(LinkedTaskTermination::Normal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
pty::prelude::Fork::Child(_child) => {
|
||||||
|
Err(cmd.exec())?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> ActorResult {
|
||||||
|
syndicate::convenient_logging()?;
|
||||||
|
|
||||||
|
Actor::top(None, move |t| {
|
||||||
|
let local_ds = Cap::new(&t.create(Dataspace::new(Some(AnyValue::symbol("pty")))));
|
||||||
|
|
||||||
|
relay::TunnelRelay::run(t,
|
||||||
|
relay::Input::Bytes(Box::pin(tokio::io::stdin())),
|
||||||
|
relay::Output::Bytes(Box::pin(tokio::io::stdout())),
|
||||||
|
Some(local_ds.clone()),
|
||||||
|
None,
|
||||||
|
false);
|
||||||
|
|
||||||
|
during!(t, local_ds, language(), $s: PtySession::<AnyValue>, |t: &mut Activation| {
|
||||||
|
t.spawn_link(Some(s.id.clone()), enclose!((local_ds) |t| {
|
||||||
|
pty_session(t, local_ds, s)
|
||||||
|
}));
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}).await??;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
Loading…
Reference in New Issue