aboutsummaryrefslogtreecommitdiff
path: root/src/detect/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/detect/mod.rs')
-rw-r--r--src/detect/mod.rs48
1 files changed, 48 insertions, 0 deletions
diff --git a/src/detect/mod.rs b/src/detect/mod.rs
index 408ee669..7d7759d2 100644
--- a/src/detect/mod.rs
+++ b/src/detect/mod.rs
@@ -366,6 +366,11 @@ pub fn foreground_process_group_id(child_pid: u32) -> Option<u32> {
crate::platform::foreground_process_group_id(child_pid)
}
+/// True when the pane's own shell is at its prompt with nothing running in it.
+pub fn pane_shell_is_idle(child_pid: u32) -> bool {
+ crate::platform::available_pane_shell(child_pid).is_some()
+}
+
fn normalized_process_name(process: &crate::platform::ForegroundProcess) -> String {
let effective = process.argv0.as_deref().unwrap_or(&process.name);
let lower_effective = effective.to_lowercase();
@@ -1928,6 +1933,49 @@ mod tests {
#[cfg(target_os = "linux")]
#[test]
+ fn pane_shell_is_idle_tracks_foreground_command() {
+ use portable_pty::CommandBuilder;
+ use std::io::Write;
+
+ fn wait_for(expected: bool, pid: u32) -> bool {
+ let deadline = std::time::Instant::now() + std::time::Duration::from_secs(5);
+ while std::time::Instant::now() < deadline {
+ if pane_shell_is_idle(pid) == expected {
+ return true;
+ }
+ std::thread::sleep(std::time::Duration::from_millis(20));
+ }
+ false
+ }
+
+ let pair = open_test_pty();
+ let mut cmd = CommandBuilder::new("sh");
+ cmd.arg("-i");
+ cmd.env("ENV", "/dev/null");
+ let mut child = pair.slave.spawn_command(cmd).expect("failed to spawn");
+ let pid = child.process_id().expect("no pid");
+ let mut reader = pair.master.try_clone_reader().expect("no reader");
+ std::thread::spawn(move || {
+ let mut buf = [0u8; 1024];
+ while matches!(std::io::Read::read(&mut reader, &mut buf), Ok(n) if n > 0) {}
+ });
+ let mut writer = pair.master.take_writer().expect("no writer");
+
+ assert!(wait_for(true, pid), "interactive shell should start idle");
+ writer.write_all(b"sleep 999\n").unwrap();
+ assert!(wait_for(false, pid), "shell running a command is not idle");
+ writer.write_all(&[3]).unwrap();
+ assert!(
+ wait_for(true, pid),
+ "shell should be idle after the command ends"
+ );
+
+ child.kill().ok();
+ child.wait().ok();
+ }
+
+ #[cfg(target_os = "linux")]
+ #[test]
fn proc_stat_parsing_handles_spaces_in_comm() {
// Verify our /proc/pid/stat parser correctly extracts fields
// even when (comm) could contain spaces.