diff --git a/Cargo.lock b/Cargo.lock index cf84011a2..bb50f08b7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -106,6 +106,7 @@ dependencies = [ "libc", "log", "nix", + "notify", "once_cell", "parking_lot", "regex", @@ -605,6 +606,18 @@ dependencies = [ "libc", ] +[[package]] +name = "filetime" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cbc844cecaee9d4443931972e1289c8ff485cb4cc2767cb03ca139ed6885153" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "windows-sys 0.48.0", +] + [[package]] name = "fnv" version = "1.0.7" @@ -620,6 +633,15 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fsevent-sys" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2" +dependencies = [ + "libc", +] + [[package]] name = "futf" version = "0.1.5" @@ -990,6 +1012,26 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "inotify" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff" +dependencies = [ + "bitflags", + "inotify-sys", + "libc", +] + +[[package]] +name = "inotify-sys" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" +dependencies = [ + "libc", +] + [[package]] name = "io-lifetimes" version = "1.0.10" @@ -1056,6 +1098,26 @@ dependencies = [ "rayon", ] +[[package]] +name = "kqueue" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c8fc60ba15bf51257aa9807a48a61013db043fcf3a78cb0d916e8e396dcad98" +dependencies = [ + "kqueue-sys", + "libc", +] + +[[package]] +name = "kqueue-sys" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8367585489f01bc55dd27404dcf56b95e6da061a256a666ab23be9ba96a2e587" +dependencies = [ + "bitflags", + "libc", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -1248,6 +1310,24 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "notify" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d9ba6c734de18ca27c8cef5cd7058aa4ac9f63596131e4c7e41e579319032a2" +dependencies = [ + "bitflags", + "crossbeam-channel", + "filetime", + "fsevent-sys", + "inotify", + "kqueue", + "libc", + "mio", + "walkdir", + "windows-sys 0.45.0", +] + [[package]] name = "num-integer" version = "0.1.45" diff --git a/crates/ark/Cargo.toml b/crates/ark/Cargo.toml index c53c2bafa..609b223e8 100644 --- a/crates/ark/Cargo.toml +++ b/crates/ark/Cargo.toml @@ -28,6 +28,7 @@ libR-sys = "0.5.0" libc = "0.2" log = "0.4.17" nix = { version = "0.26.2", features = ["signal"] } +notify = "6.0.0" once_cell = "1.17.1" parking_lot = "0.12.1" regex = "1.7.1" diff --git a/crates/ark/src/main.rs b/crates/ark/src/main.rs index 42039a8c9..f3a288243 100644 --- a/crates/ark/src/main.rs +++ b/crates/ark/src/main.rs @@ -22,8 +22,10 @@ use ark::shell::Shell; use ark::version::detect_r; use bus::Bus; use crossbeam::channel::bounded; +use crossbeam::channel::unbounded; use log::*; use nix::sys::signal::*; +use notify::Watcher; use stdext::unwrap; fn start_kernel(connection_file: ConnectionFile, capture_streams: bool) { @@ -203,6 +205,8 @@ fn main() { let mut connection_file: Option = None; let mut log_file: Option = None; + let mut startup_notifier_file: Option = None; + let mut startup_delay: Option = None; let mut has_action = false; let mut capture_streams = true; @@ -241,6 +245,31 @@ fn main() { break; } }, + "--startup-notifier-file" => { + if let Some(file) = argv.next() { + startup_notifier_file = Some(file); + } else { + eprintln!( + "A notification file must be specified with the --startup-notifier-file argument." + ); + break; + } + }, + "--startup-delay" => { + if let Some(delay_arg) = argv.next() { + if let Ok(delay) = delay_arg.parse::() { + startup_delay = Some(std::time::Duration::from_millis(delay)); + } else { + eprintln!("Can't parse delay in milliseconds"); + break; + } + } else { + eprintln!( + "A delay in milliseconds must be specified with the --startup-delay argument." + ); + break; + } + }, other => { eprintln!("Argument '{}' unknown", other); break; @@ -248,6 +277,47 @@ fn main() { } } + // Initialize the logger. + logger::initialize(log_file.as_deref()); + + if let Some(file) = startup_notifier_file { + let path = std::path::Path::new(&file); + let (tx, rx) = unbounded(); + + if let Err(err) = (|| -> anyhow::Result<()> { + let config = notify::Config::default() + .with_poll_interval(std::time::Duration::from_millis(2)) + .with_compare_contents(false); + + let mut watcher = notify::RecommendedWatcher::new(tx, config).unwrap(); + watcher.watch(path, notify::RecursiveMode::NonRecursive)?; + + loop { + let ev = rx.recv()?; + match ev.unwrap().kind { + notify::event::EventKind::Modify(_) => { + break; + }, + notify::event::EventKind::Remove(_) => { + break; + }, + _ => { + continue; + }, + } + } + + watcher.unwatch(path)?; + Ok(()) + })() { + eprintln!("Problem with the delay file: {:?}", err); + } + } + + if let Some(delay) = startup_delay { + std::thread::sleep(delay); + } + // If the user didn't specify an action, print the usage instructions and // exit if !has_action { @@ -255,9 +325,6 @@ fn main() { return; } - // Initialize the logger. - logger::initialize(log_file.as_deref()); - // Register segfault handler to get a backtrace. Should be after // initialising `log!`. Note that R will not override this handler // because we set `R_SignalHandlers` to 0 before startup.