Add armed plugin-originated file watches across VFS mutations
Register core.watch before its armed notification, coalesce later changes without losing post-notification writes, and clean up on caller death. Observe mounted layers and open file handles, including descriptor/stream writes, truncation, both rename paths and deletion. Track cancelled mutations through process idle and retain visible paths across renames. Document the protocol, expose native core discovery, and exercise watches through SDK callbacks and raw WASI filesystem operations. Card: SAU-359
This commit is contained in:
@@ -26,6 +26,11 @@ impl Call {
|
||||
self
|
||||
}
|
||||
/// Receive progress notifications before the result arrives.
|
||||
///
|
||||
/// For `core.watch`, await this call in a background task for the process's
|
||||
/// lifetime. Scan only after `armed`, and rescan on `changed`; events can
|
||||
/// coalesce. The call has no cancellation API. Do not use endpoint connect
|
||||
/// retries to wait for file publication.
|
||||
pub fn on_notify(mut self, f: impl FnMut(&str, Value) + 'static) -> Self {
|
||||
self.notify = Some(Box::new(f));
|
||||
self
|
||||
|
||||
@@ -2,6 +2,12 @@
|
||||
use sausage_plugin::serde_json::{Value, json};
|
||||
use sausage_plugin::{Ctx, Result, call, lock, rpc, schema};
|
||||
struct Probe;
|
||||
/// A virtual path to observe.
|
||||
#[schema]
|
||||
struct WatchPath {
|
||||
/// Directory or file, not necessarily present yet.
|
||||
path: String,
|
||||
}
|
||||
/// The values to add.
|
||||
#[schema]
|
||||
struct Add {
|
||||
@@ -30,6 +36,33 @@ struct Echo {
|
||||
}
|
||||
#[rpc(grace_seconds = 12,endpoints(control=Control))]
|
||||
impl Probe {
|
||||
/// Scan only after the watch is armed; keep the call open until process death.
|
||||
async fn watch(&self, p: WatchPath) -> Result<()> {
|
||||
let path = p.path.clone();
|
||||
call("core.watch", json!({"path":p.path}))
|
||||
.on_notify(move |name, _| {
|
||||
if name == "armed" && path == "/self" {
|
||||
std::fs::write("/self/owned", "visible via proc").unwrap();
|
||||
}
|
||||
let mut entries: Vec<_> = std::fs::read_dir(&path)
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.filter_map(std::result::Result::ok)
|
||||
.map(|e| e.file_name().into_string().unwrap())
|
||||
.collect();
|
||||
entries.sort();
|
||||
let text = std::fs::read_to_string(format!("{path}/before")).ok();
|
||||
Ctx::default()
|
||||
.notify(
|
||||
name,
|
||||
json!({"pid":sausage_plugin::pid(), "entries":entries, "text":text}),
|
||||
)
|
||||
.unwrap();
|
||||
})
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Publish a control endpoint until EOF.
|
||||
async fn serve(&self) -> Result<()> {
|
||||
sausage_plugin::listen("/endpoint", std::rc::Rc::new(Control)).await
|
||||
|
||||
Reference in New Issue
Block a user