diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 16890a12..1c4ca535 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -122,6 +122,7 @@ Integration tests and helpers live in: | `@MAYBEITEM_@` | `([^ "]+ )?` | | `@MAYBETRUNCATED@` | `( ... (truncated))?` | | `@GROUPLIST@` | `[0-9, ]*` | +| `@ERRNO@` | `-[0-9]+ \([A-Z0-9]+: [^)]*\)` | | `@ANY@` | `.+` | ## Canonical References diff --git a/README.md b/README.md index e396cc5d..751e8ba9 100644 --- a/README.md +++ b/README.md @@ -15,15 +15,37 @@ IMPORTANT: note that we depend on a specific version of nightly to avoid breakag ## Build & Run -Use `cargo build`, `cargo check`, etc. as normal. Run your program with: +Use `cargo build`, `cargo check`, etc. as normal. Run the daemon with: ```shell -cargo run --release --config 'target."cfg(all())".runner="sudo -E"' +cargo run --release --bin pinchyd --config 'target."cfg(all())".runner="sudo -E"' +``` + +and the client (no root needed) with: + +```shell +cargo run --release --bin pinchy -- ls /tmp ``` Cargo build scripts are used to automatically build the eBPF correctly and include it in the program. +## Installing the daemon + +`pinchyd` must run as root and registers the `org.pinchy.Service` name on the +system D-Bus. The repository ships the pieces you need: + +- `org.pinchy.Service.conf` → `/etc/dbus-1/system.d/` (bus policy: lets + pinchyd own the name and any user talk to it) +- `org.pinchy.Service.service` → `/usr/share/dbus-1/system-services/` + (bus activation: starts pinchyd on demand) +- `pinchy.service` → `/usr/lib/systemd/system/` (systemd unit) + +With those installed (and `pinchyd` in the path used by the service files), +running `pinchy ls /tmp` will start the daemon automatically via bus +activation, and it exits on its own after being idle. Alternatively manage it +explicitly with `sudo systemctl start pinchy`. + ## UML Efficiency Benchmark You can run the UML efficiency benchmark with any traced command: @@ -67,6 +89,19 @@ Attach to a running process: pinchy -p -e open,close ``` +List the syscall names supported by this build: +```shell +pinchy --list-syscalls +``` + +Other knobs: + +- `--format one-line|multi-line`: trace line formatting (default `one-line`) +- `PINCHY_STDOUT_FLUSH_BYTES` / `PINCHY_LOW_LATENCY_FLUSH`: client output + flush tuning (defaults: flush per event on a TTY, on threshold or idle + otherwise) +- `PINCHY_RINGBUF_SIZE`: daemon ring buffer size in bytes (default 80 MiB) + ### Syscall Aliases Pinchy supports common syscall aliases that users might be familiar with from `libc` or other tools. diff --git a/pinchy-client/src/lib.rs b/pinchy-client/src/lib.rs index e0a312c7..06ba7d2e 100644 --- a/pinchy-client/src/lib.rs +++ b/pinchy-client/src/lib.rs @@ -50,8 +50,49 @@ pub async fn trace_child(command: Vec, syscalls: Vec) -> (i32, Ow unsafe { // Wait for a signal before we exec libc::raise(libc::SIGSTOP); - let result = libc::execvp(command[0].as_ptr(), argv.as_ptr()); - std::process::exit(result); + libc::execvp(command[0].as_ptr(), argv.as_ptr()); + + // Only reached when exec failed. We forked off a multithreaded + // process, so only async-signal-safe calls are allowed here: + // write(2) straight to stderr and _exit(2), no allocation or + // locking. Use the shell convention for the exit code: 126 for + // permission problems, 127 for command not found. + let errno = *libc::__errno_location(); + + let write_all = |bytes: &[u8]| { + libc::write( + libc::STDERR_FILENO, + bytes.as_ptr() as *const libc::c_void, + bytes.len(), + ); + }; + + write_all(b"pinchy: cannot execute '"); + write_all(command[0].as_bytes()); + write_all(b"' (errno "); + + let mut digits = [0u8; 12]; + let mut i = digits.len(); + let mut n = errno as u32; + + loop { + i -= 1; + digits[i] = b'0' + (n % 10) as u8; + n /= 10; + + if n == 0 { + break; + } + } + + write_all(&digits[i..]); + write_all(b")\n"); + + let code = match errno { + libc::EACCES | libc::EPERM => 126, + _ => 127, + }; + libc::_exit(code); } } @@ -129,11 +170,20 @@ fn handle_dbus_error(error: ZBusError) -> ! { // Service-related errors ZBusError::MethodError(error_name, description, _) => match error_name.as_str() { - "org.freedesktop.DBus.Error.ServiceUnknown" => { + "org.freedesktop.DBus.Error.ServiceUnknown" + | "org.freedesktop.DBus.Error.NameHasNoOwner" => { eprintln!("Pinchy service is not running."); - eprintln!("Please start the pinchyd daemon first."); + eprintln!("Start it with: sudo systemctl start pinchy (or run pinchyd as root)."); std::process::exit(3); } + "org.freedesktop.DBus.Error.UnknownObject" => { + if let Some(desc) = description { + eprintln!("{desc}"); + } else { + eprintln!("The process does not exist (it may have exited)."); + } + std::process::exit(9); + } "org.freedesktop.DBus.Error.AccessDenied" | "org.freedesktop.DBus.Error.AuthFailed" => { eprintln!("Permission denied: You don't have access to trace this process."); eprintln!("Make sure you own the process or run with appropriate privileges."); @@ -172,9 +222,13 @@ fn handle_dbus_error(error: ZBusError) -> ! { ZBusError::FDO(fdo_error) => match *fdo_error { fdo::Error::ServiceUnknown(_) => { eprintln!("Pinchy service is not running."); - eprintln!("Please start the pinchyd daemon first."); + eprintln!("Start it with: sudo systemctl start pinchy (or run pinchyd as root)."); std::process::exit(3); } + fdo::Error::UnknownObject(ref msg) => { + eprintln!("{msg}"); + std::process::exit(9); + } fdo::Error::AccessDenied(_) | fdo::Error::AuthFailed(_) => { eprintln!("Permission denied: You don't have access to trace this process."); eprintln!("Make sure you own the process or run with appropriate privileges."); diff --git a/pinchy-common/src/syscalls/mod.rs b/pinchy-common/src/syscalls/mod.rs index cb4d07c2..72dce049 100644 --- a/pinchy-common/src/syscalls/mod.rs +++ b/pinchy-common/src/syscalls/mod.rs @@ -48,5 +48,7 @@ macro_rules! declare_syscalls { } } pub const ALL_SYSCALLS: &[i64] = &[$($name),*]; + pub const SYSCALL_ALIASES: &[(&str, i64)] = + &[$( $( ($alias, $target) ),* )?]; }; } diff --git a/pinchy/Cargo.toml b/pinchy/Cargo.toml index ebcc363d..654235ce 100644 --- a/pinchy/Cargo.toml +++ b/pinchy/Cargo.toml @@ -3,6 +3,8 @@ name = "pinchy" version = "0.1.0" edition = "2021" license = "MIT OR Apache-2.0" +description = "eBPF-based syscall tracer, a low-overhead strace alternative" +default-run = "pinchy" [features] default = [] diff --git a/pinchy/src/client.rs b/pinchy/src/client.rs index b197fd52..a6ed9408 100644 --- a/pinchy/src/client.rs +++ b/pinchy/src/client.rs @@ -8,7 +8,7 @@ use anyhow::Result; use clap::{CommandFactory as _, Parser}; use pinchy_common::{ compact_payload_size, max_compact_payload_size, - syscalls::{syscall_nr_from_name, ALL_SYSCALLS}, + syscalls::{syscall_name_from_nr, syscall_nr_from_name, ALL_SYSCALLS, SYSCALL_ALIASES}, WireEventHeader, WIRE_VERSION, }; use tokio::io::{AsyncReadExt as _, AsyncWriteExt as _}; @@ -32,18 +32,84 @@ trait Pinchy { const DEFAULT_STDOUT_FLUSH_BYTES: usize = 1; const DEFAULT_NON_TTY_FLUSH_BYTES: usize = 65536; +fn levenshtein(a: &str, b: &str) -> usize { + let a: Vec = a.chars().collect(); + let b: Vec = b.chars().collect(); + let mut row: Vec = (0..=b.len()).collect(); + + for (i, ca) in a.iter().enumerate() { + let mut prev = row[0]; + row[0] = i + 1; + + for (j, cb) in b.iter().enumerate() { + let cost = if ca == cb { prev } else { prev + 1 }; + prev = row[j + 1]; + row[j + 1] = cost.min(prev + 1).min(row[j] + 1); + } + } + + row[b.len()] +} + +fn closest_syscall_name(name: &str) -> Option<&'static str> { + ALL_SYSCALLS + .iter() + .filter_map(|&nr| syscall_name_from_nr(nr)) + .chain(SYSCALL_ALIASES.iter().map(|&(alias, _)| alias)) + .map(|candidate| (levenshtein(name, candidate), candidate)) + .min() + .filter(|&(distance, _)| distance <= 2) + .map(|(_, candidate)| candidate) +} + fn parse_syscall_names(names: &[String]) -> Result, String> { let mut out = Vec::new(); for name in names { + // Accept strace's -e trace=name,name qualifier syntax. + let name = name.strip_prefix("trace=").unwrap_or(name); + match syscall_nr_from_name(name) { Some(nr) if ALL_SYSCALLS.contains(&nr) => out.push(nr), Some(_) => return Err(format!("Syscall '{name}' is not supported by this build")), - None => return Err(format!("Unknown syscall name: {name}")), + None => { + let mut msg = format!("Unknown syscall name: {name}."); + + if let Some(suggestion) = closest_syscall_name(name) { + msg.push_str(&format!(" Did you mean '{suggestion}'?")); + } + + msg.push_str(" Run 'pinchy --list-syscalls' to see supported names."); + return Err(msg); + } } } Ok(out) } +fn list_syscalls() { + use std::io::Write as _; + + let mut names: Vec<&'static str> = ALL_SYSCALLS + .iter() + .filter_map(|&nr| syscall_name_from_nr(nr)) + .collect(); + names.sort_unstable(); + + let mut out = std::io::stdout().lock(); + for name in names { + if let Err(e) = writeln!(out, "{name}") { + // Tolerate a closed pipe (e.g. `pinchy --list-syscalls | head`), + // but surface real write failures. + if e.kind() == std::io::ErrorKind::BrokenPipe { + return; + } + + eprintln!("error writing syscall list: {e}"); + std::process::exit(1); + } + } +} + fn parse_stdout_flush_bytes() -> usize { std::env::var("PINCHY_STDOUT_FLUSH_BYTES") .ok() @@ -52,12 +118,12 @@ fn parse_stdout_flush_bytes() -> usize { .unwrap_or(DEFAULT_STDOUT_FLUSH_BYTES) } -fn low_latency_flush_enabled() -> bool { +fn low_latency_flush_enabled(sink_is_terminal: bool) -> bool { if let Ok(value) = std::env::var("PINCHY_LOW_LATENCY_FLUSH") { return value == "1" || value.eq_ignore_ascii_case("true"); } - std::io::stdout().is_terminal() + sink_is_terminal } #[derive(Parser, Debug)] @@ -67,10 +133,18 @@ struct Args { #[arg(short = 'e', long = "event", value_delimiter = ',', action = clap::ArgAction::Append)] syscalls: Vec, - // Formatting style, `one-line` or `multi-line` + /// Formatting style for trace output #[arg(long = "format", value_enum, default_value_t = FormattingStyle::default())] style: FormattingStyle, + /// List the syscall names supported by this build and exit + #[arg(long = "list-syscalls")] + list_syscalls: bool, + + /// Write trace output to FILE instead of stderr + #[arg(short = 'o', long = "output")] + output: Option, + /// PID to trace #[arg(short = 'p', long = "pid", action = clap::ArgAction::Set, conflicts_with = "command")] pid: Option, @@ -85,6 +159,12 @@ async fn main() -> Result<()> { env_logger::init(); let args = Args::parse(); + + if args.list_syscalls { + list_syscalls(); + return Ok(()); + } + let syscalls = if !args.syscalls.is_empty() { match parse_syscall_names(&args.syscalls) { Ok(v) => v, @@ -104,33 +184,56 @@ async fn main() -> Result<()> { // Read everything there is to read, the server will close the write end // of the pipe - relay_trace(fd, style).await?; + relay_to_sink(fd, style, args.output).await?; pinchy_client::cleanup_and_quit(pid); } else if let Some(pid) = args.pid { let fd = pinchy_client::attach(pid, syscalls).await; - relay_trace(fd, style).await?; + relay_to_sink(fd, style, args.output).await?; } else { - // Print clap's usage message and exit - Args::command().print_help().expect("Failed to print usage"); - println!(); + // Print clap's usage message to stderr and exit + eprintln!("{}", Args::command().render_help()); std::process::exit(2); }; Ok(()) } -async fn relay_trace(fd: OwnedFd, formatting_style: FormattingStyle) -> Result<()> { +// Trace output goes to stderr by default (like strace), keeping the traced +// program's stdout clean, or to a file with -o. +async fn relay_to_sink( + fd: OwnedFd, + style: FormattingStyle, + output: Option, +) -> Result<()> { + match output { + Some(path) => { + let file = tokio::fs::File::create(&path).await?; + relay_trace(fd, style, file, false).await + } + None => { + let is_terminal = std::io::stderr().is_terminal(); + relay_trace(fd, style, tokio::io::stderr(), is_terminal).await + } + } +} + +async fn relay_trace( + fd: OwnedFd, + formatting_style: FormattingStyle, + sink: W, + sink_is_terminal: bool, +) -> Result<()> { let mut reader = tokio::io::BufReader::with_capacity( 64 * 1024, tokio::fs::File::from(std::fs::File::from(fd)), ); - let mut stdout = tokio::io::BufWriter::new(tokio::io::stdout()); + let mut sink = tokio::io::BufWriter::new(sink); let mut header_buf = [0u8; std::mem::size_of::()]; let mut payload = Vec::new(); let max_payload_size = max_compact_payload_size(); - let low_latency_flush = low_latency_flush_enabled(); + let low_latency_flush = low_latency_flush_enabled(sink_is_terminal); let flush_bytes = if low_latency_flush { parse_stdout_flush_bytes() } else { @@ -190,7 +293,7 @@ async fn relay_trace(fd: OwnedFd, formatting_style: FormattingStyle) -> Result<( events::handle_event(&header, &payload, formatter).await?; - stdout.write_all(&output).await?; + sink.write_all(&output).await?; pending_flush_bytes += output.len(); // Flush on threshold, but also whenever we have caught up @@ -198,7 +301,7 @@ async fn relay_trace(fd: OwnedFd, formatting_style: FormattingStyle) -> Result<( // would block); otherwise output lags behind a slow tracee // by up to the threshold. if pending_flush_bytes >= flush_bytes || reader.buffer().is_empty() { - stdout.flush().await?; + sink.flush().await?; pending_flush_bytes = 0; } } @@ -206,7 +309,7 @@ async fn relay_trace(fd: OwnedFd, formatting_style: FormattingStyle) -> Result<( Err(e) => break Err(e.into()), } }; - stdout.flush().await?; + sink.flush().await?; read_result } diff --git a/pinchy/src/format_helpers.rs b/pinchy/src/format_helpers.rs index 7c623187..d86be7af 100644 --- a/pinchy/src/format_helpers.rs +++ b/pinchy/src/format_helpers.rs @@ -2251,7 +2251,7 @@ pub fn format_return_value(syscall_nr: i64, return_value: i64) -> std::borrow::C // Handle the common error case first if return_value == -1 { - return std::borrow::Cow::Borrowed("-1 (error)"); + return format_error_return(return_value); } match syscall_nr { @@ -2285,7 +2285,7 @@ pub fn format_return_value(syscall_nr: i64, return_value: i64) -> std::borrow::C if return_value >= 0 { std::borrow::Cow::Owned(format!("{return_value} (fd)")) } else { - std::borrow::Cow::Owned(format!("{return_value} (error)")) + format_error_return(return_value) } } @@ -2301,7 +2301,7 @@ pub fn format_return_value(syscall_nr: i64, return_value: i64) -> std::borrow::C if return_value >= 0 { std::borrow::Cow::Owned(format!("{} (fd)", return_value)) } else { - std::borrow::Cow::Owned(format!("{} (error)", return_value)) + format_error_return(return_value) } } @@ -2331,7 +2331,7 @@ pub fn format_return_value(syscall_nr: i64, return_value: i64) -> std::borrow::C if return_value >= 0 { std::borrow::Cow::Owned(format!("{return_value} (bytes)")) } else { - std::borrow::Cow::Owned(format!("{return_value} (error)")) + format_error_return(return_value) } } @@ -2340,7 +2340,7 @@ pub fn format_return_value(syscall_nr: i64, return_value: i64) -> std::borrow::C if return_value >= 0 { std::borrow::Cow::Owned(format!("{} (bytes)", return_value)) } else { - std::borrow::Cow::Owned(format!("{} (error)", return_value)) + format_error_return(return_value) } } @@ -2349,7 +2349,7 @@ pub fn format_return_value(syscall_nr: i64, return_value: i64) -> std::borrow::C if return_value >= 0 { std::borrow::Cow::Owned(format!("{return_value} (messages)")) } else { - std::borrow::Cow::Owned(format!("{return_value} (error)")) + format_error_return(return_value) } } @@ -2358,21 +2358,21 @@ pub fn format_return_value(syscall_nr: i64, return_value: i64) -> std::borrow::C if return_value >= 0 { std::borrow::Cow::Owned(format!("{return_value} (requests)")) } else { - std::borrow::Cow::Owned(format!("{return_value} (error)")) + format_error_return(return_value) } } syscalls::SYS_io_getevents | syscalls::SYS_io_pgetevents => { if return_value >= 0 { std::borrow::Cow::Owned(format!("{return_value} (events)")) } else { - std::borrow::Cow::Owned(format!("{return_value} (error)")) + format_error_return(return_value) } } syscalls::SYS_io_setup | syscalls::SYS_io_destroy | syscalls::SYS_io_cancel => { if return_value == 0 { std::borrow::Cow::Borrowed("0 (success)") } else { - std::borrow::Cow::Owned(format!("{return_value} (error)")) + format_error_return(return_value) } } @@ -2380,7 +2380,7 @@ pub fn format_return_value(syscall_nr: i64, return_value: i64) -> std::borrow::C if return_value >= 0 { std::borrow::Cow::Owned(format!("{return_value} (submitted)")) } else { - std::borrow::Cow::Owned(format!("{return_value} (error)")) + format_error_return(return_value) } } @@ -2389,7 +2389,7 @@ pub fn format_return_value(syscall_nr: i64, return_value: i64) -> std::borrow::C if return_value >= 0 { std::borrow::Cow::Owned(format!("{} (bytes)", return_value)) } else { - std::borrow::Cow::Owned(format!("{} (error)", return_value)) + format_error_return(return_value) } } @@ -2505,7 +2505,7 @@ pub fn format_return_value(syscall_nr: i64, return_value: i64) -> std::borrow::C | syscalls::SYS_kexec_load | syscalls::SYS_nfsservctl => match return_value { 0 => std::borrow::Cow::Borrowed("0 (success)"), - _ => std::borrow::Cow::Owned(format!("{return_value} (error)")), + _ => format_error_return(return_value), }, #[cfg(target_arch = "x86_64")] @@ -2517,7 +2517,7 @@ pub fn format_return_value(syscall_nr: i64, return_value: i64) -> std::borrow::C | syscalls::SYS_get_thread_area | syscalls::SYS_kexec_file_load => match return_value { 0 => std::borrow::Cow::Borrowed("0 (success)"), - _ => std::borrow::Cow::Owned(format!("{return_value} (error)")), + _ => format_error_return(return_value), }, #[cfg(target_arch = "x86_64")] @@ -2525,7 +2525,7 @@ pub fn format_return_value(syscall_nr: i64, return_value: i64) -> std::borrow::C if return_value >= 0 { std::borrow::Cow::Owned(format!("{return_value} (bytes)")) } else { - std::borrow::Cow::Owned(format!("{return_value} (error)")) + format_error_return(return_value) } } @@ -2548,7 +2548,7 @@ pub fn format_return_value(syscall_nr: i64, return_value: i64) -> std::borrow::C if return_value >= 0 { std::borrow::Cow::Owned(format!("{return_value} (success)")) } else { - std::borrow::Cow::Owned(format!("{return_value} (error)")) + format_error_return(return_value) } } @@ -2557,21 +2557,21 @@ pub fn format_return_value(syscall_nr: i64, return_value: i64) -> std::borrow::C 1 => std::borrow::Cow::Borrowed("1 (less than)"), 2 => std::borrow::Cow::Borrowed("2 (greater than)"), 3 => std::borrow::Cow::Borrowed("3 (not equal)"), - _ => std::borrow::Cow::Owned(format!("{return_value} (error)")), + _ => format_error_return(return_value), }, syscalls::SYS_getgroups => { if return_value >= 0 { std::borrow::Cow::Owned(format!("{return_value} (groups)")) } else { - std::borrow::Cow::Owned(format!("{return_value} (error)")) + format_error_return(return_value) } } #[cfg(target_arch = "x86_64")] syscalls::SYS_pause => match return_value { 0 => std::borrow::Cow::Borrowed("0 (success)"), - _ => std::borrow::Cow::Owned(format!("{return_value} (error)")), + _ => format_error_return(return_value), }, #[cfg(target_arch = "x86_64")] @@ -2592,21 +2592,21 @@ pub fn format_return_value(syscall_nr: i64, return_value: i64) -> std::borrow::C | syscalls::SYS_utimes | syscalls::SYS_futimesat => match return_value { 0 => std::borrow::Cow::Borrowed("0 (success)"), - _ => std::borrow::Cow::Owned(format!("{return_value} (error)")), + _ => format_error_return(return_value), }, // Count returning syscalls - special handling for poll/select family syscalls::SYS_pselect6 => match return_value { 0 => std::borrow::Cow::Borrowed("0 (timeout)"), n if n > 0 => std::borrow::Cow::Owned(format!("{n} (ready)")), - _ => std::borrow::Cow::Owned(format!("{return_value} (error)")), + _ => format_error_return(return_value), }, #[cfg(target_arch = "x86_64")] syscalls::SYS_poll | syscalls::SYS_select => match return_value { 0 => std::borrow::Cow::Borrowed("0 (timeout)"), n if n > 0 => std::borrow::Cow::Owned(format!("{} (ready)", n)), - _ => std::borrow::Cow::Owned(format!("{} (error)", return_value)), + _ => format_error_return(return_value), }, // ppoll gets special handling with detailed ready state - handled in events.rs @@ -2615,7 +2615,7 @@ pub fn format_return_value(syscall_nr: i64, return_value: i64) -> std::borrow::C // The events.rs handler will override this with the extra parameter match return_value { 0 => std::borrow::Cow::Borrowed("0 (timeout)"), - n if n < 0 => std::borrow::Cow::Owned(format!("{return_value} (error)")), + n if n < 0 => format_error_return(return_value), _ => std::borrow::Cow::Owned(format!("{return_value} (ready)")), } } @@ -2625,7 +2625,7 @@ pub fn format_return_value(syscall_nr: i64, return_value: i64) -> std::borrow::C syscalls::SYS_fork | syscalls::SYS_vfork => match return_value { 0 => std::borrow::Cow::Borrowed("0 (child)"), v if v > 0 => std::borrow::Cow::Owned(format!("{v} (child pid)")), - _ => std::borrow::Cow::Owned(format!("{return_value} (error)")), + _ => format_error_return(return_value), }, // PID returning syscalls @@ -2640,7 +2640,7 @@ pub fn format_return_value(syscall_nr: i64, return_value: i64) -> std::borrow::C if return_value >= 0 { std::borrow::Cow::Owned(format!("{return_value} (pid)")) } else { - std::borrow::Cow::Owned(format!("{return_value} (error)")) + format_error_return(return_value) } } @@ -2649,7 +2649,7 @@ pub fn format_return_value(syscall_nr: i64, return_value: i64) -> std::borrow::C if return_value >= 0 { std::borrow::Cow::Owned(format!("{return_value} (pid)")) } else { - std::borrow::Cow::Owned(format!("{return_value} (error)")) + format_error_return(return_value) } } @@ -2661,7 +2661,7 @@ pub fn format_return_value(syscall_nr: i64, return_value: i64) -> std::borrow::C if return_value >= 0 { std::borrow::Cow::Owned(format!("{return_value} (id)")) } else { - std::borrow::Cow::Owned(format!("{return_value} (error)")) + format_error_return(return_value) } } @@ -2669,7 +2669,7 @@ pub fn format_return_value(syscall_nr: i64, return_value: i64) -> std::borrow::C // -4095..0 range, anything else is a valid address syscalls::SYS_mmap | syscalls::SYS_mremap => { if (-4095..0).contains(&return_value) { - std::borrow::Cow::Owned(format!("{return_value} (error)")) + format_error_return(return_value) } else { std::borrow::Cow::Owned(format!("0x{return_value:x} (addr)")) } @@ -2677,7 +2677,7 @@ pub fn format_return_value(syscall_nr: i64, return_value: i64) -> std::borrow::C syscalls::SYS_shmat => { if (-4095..0).contains(&return_value) { - std::borrow::Cow::Owned(format!("{return_value} (error)")) + format_error_return(return_value) } else { std::borrow::Cow::Owned(format!("0x{return_value:x}")) } @@ -2691,7 +2691,7 @@ pub fn format_return_value(syscall_nr: i64, return_value: i64) -> std::borrow::C if return_value >= 0 { std::borrow::Cow::Owned(format!("{return_value} (pages not migrated)")) } else { - std::borrow::Cow::Owned(format!("{return_value} (error)")) + format_error_return(return_value) } } @@ -2701,7 +2701,7 @@ pub fn format_return_value(syscall_nr: i64, return_value: i64) -> std::borrow::C } else if return_value > 0 { std::borrow::Cow::Owned(format!("{return_value} (pages not migrated)")) } else { - std::borrow::Cow::Owned(format!("{return_value} (error)")) + format_error_return(return_value) } } @@ -2720,13 +2720,13 @@ pub fn format_return_value(syscall_nr: i64, return_value: i64) -> std::borrow::C | syscalls::SYS_nanosleep | syscalls::SYS_clock_nanosleep => match return_value { 0 => std::borrow::Cow::Borrowed("0 (success)"), - _ => std::borrow::Cow::Owned(format!("{return_value} (error)")), + _ => format_error_return(return_value), }, // Time adjustment syscalls - return clock state syscalls::SYS_adjtimex | syscalls::SYS_clock_adjtime => { if return_value < 0 { - std::borrow::Cow::Owned(format!("{return_value} (error)")) + format_error_return(return_value) } else { std::borrow::Cow::Owned(format!( "{} ({})", @@ -2761,60 +2761,60 @@ pub fn format_return_value(syscall_nr: i64, return_value: i64) -> std::borrow::C | syscalls::SYS_exit | syscalls::SYS_exit_group => match return_value { 0 => std::borrow::Cow::Borrowed("0 (success)"), - _ => std::borrow::Cow::Owned(format!("{return_value} (error)")), + _ => format_error_return(return_value), }, syscalls::SYS_shmget => match return_value { - n if n < 0 => std::borrow::Cow::Owned(format!("{return_value} (error)")), + n if n < 0 => format_error_return(return_value), _ => std::borrow::Cow::Owned(format!("{return_value} (shmid)")), }, syscalls::SYS_msgget => match return_value { - n if n < 0 => std::borrow::Cow::Owned(format!("{return_value} (error)")), + n if n < 0 => format_error_return(return_value), _ => std::borrow::Cow::Owned(format!("{return_value} (msqid)")), }, syscalls::SYS_semget => match return_value { - n if n < 0 => std::borrow::Cow::Owned(format!("{return_value} (error)")), + n if n < 0 => format_error_return(return_value), _ => std::borrow::Cow::Owned(format!("{return_value} (semid)")), }, syscalls::SYS_inotify_add_watch => match return_value { - n if n < 0 => std::borrow::Cow::Owned(format!("{return_value} (error)")), + n if n < 0 => format_error_return(return_value), _ => std::borrow::Cow::Owned(format!("{return_value} (wd)")), }, syscalls::SYS_timer_create => match return_value { 0 => std::borrow::Cow::Borrowed("0 (success)"), - _ => std::borrow::Cow::Owned(format!("{return_value} (error)")), + _ => format_error_return(return_value), }, syscalls::SYS_timer_getoverrun => match return_value { - -1 => std::borrow::Cow::Borrowed("-1 (error)"), + -1 => format_error_return(return_value), n if n >= 0 => std::borrow::Cow::Owned(format!("{n} (overruns)")), - _ => std::borrow::Cow::Owned(format!("{return_value} (error)")), + _ => format_error_return(return_value), }, syscalls::SYS_timer_gettime | syscalls::SYS_timer_settime => match return_value { 0 => std::borrow::Cow::Borrowed("0 (success)"), - _ => std::borrow::Cow::Owned(format!("{return_value} (error)")), + _ => format_error_return(return_value), }, // Special syscalls with unique return value semantics syscalls::SYS_membarrier => match return_value { - n if n < 0 => std::borrow::Cow::Owned(format!("{return_value} (error)")), + n if n < 0 => format_error_return(return_value), 0 => std::borrow::Cow::Borrowed("0 (success)"), _ => std::borrow::Cow::Owned(format!("{return_value} (bitmask)")), }, syscalls::SYS_pkey_alloc => match return_value { - n if n < 0 => std::borrow::Cow::Owned(format!("{return_value} (error)")), + n if n < 0 => format_error_return(return_value), _ => std::borrow::Cow::Owned(format!("{return_value} (pkey)")), }, // Signal-related syscalls with special return semantics syscalls::SYS_rt_sigtimedwait => match return_value { - n if n < 0 => std::borrow::Cow::Owned(format!("{return_value} (error)")), + n if n < 0 => format_error_return(return_value), _ => std::borrow::Cow::Owned(format!("{return_value} (signal)")), }, @@ -2822,13 +2822,13 @@ pub fn format_return_value(syscall_nr: i64, return_value: i64) -> std::borrow::C syscalls::SYS_syslog => match return_value { 0 => std::borrow::Cow::Borrowed("0 (success)"), n if n > 0 => std::borrow::Cow::Owned(format!("{n}")), - _ => std::borrow::Cow::Owned(format!("{return_value} (error)")), + _ => format_error_return(return_value), }, // Default case - just show the raw value with error indication if negative _ => { if return_value < 0 { - std::borrow::Cow::Owned(format!("{return_value} (error)")) + format_error_return(return_value) } else if return_value == 0 { std::borrow::Cow::Owned(format!("{return_value} (success)")) } else { @@ -6456,3 +6456,157 @@ pub fn format_arch_prctl_code(code: i32) -> Cow<'static, str> { _ => Cow::Owned(format!("0x{code:x}")), } } + +pub fn errno_info(errno: i32) -> Option<(&'static str, &'static str)> { + let info = match errno { + libc::EPERM => ("EPERM", "Operation not permitted"), + libc::ENOENT => ("ENOENT", "No such file or directory"), + libc::ESRCH => ("ESRCH", "No such process"), + libc::EINTR => ("EINTR", "Interrupted system call"), + libc::EIO => ("EIO", "Input/output error"), + libc::ENXIO => ("ENXIO", "No such device or address"), + libc::E2BIG => ("E2BIG", "Argument list too long"), + libc::ENOEXEC => ("ENOEXEC", "Exec format error"), + libc::EBADF => ("EBADF", "Bad file descriptor"), + libc::ECHILD => ("ECHILD", "No child processes"), + libc::EAGAIN => ("EAGAIN", "Resource temporarily unavailable"), + libc::ENOMEM => ("ENOMEM", "Cannot allocate memory"), + libc::EACCES => ("EACCES", "Permission denied"), + libc::EFAULT => ("EFAULT", "Bad address"), + libc::ENOTBLK => ("ENOTBLK", "Block device required"), + libc::EBUSY => ("EBUSY", "Device or resource busy"), + libc::EEXIST => ("EEXIST", "File exists"), + libc::EXDEV => ("EXDEV", "Invalid cross-device link"), + libc::ENODEV => ("ENODEV", "No such device"), + libc::ENOTDIR => ("ENOTDIR", "Not a directory"), + libc::EISDIR => ("EISDIR", "Is a directory"), + libc::EINVAL => ("EINVAL", "Invalid argument"), + libc::ENFILE => ("ENFILE", "Too many open files in system"), + libc::EMFILE => ("EMFILE", "Too many open files"), + libc::ENOTTY => ("ENOTTY", "Inappropriate ioctl for device"), + libc::ETXTBSY => ("ETXTBSY", "Text file busy"), + libc::EFBIG => ("EFBIG", "File too large"), + libc::ENOSPC => ("ENOSPC", "No space left on device"), + libc::ESPIPE => ("ESPIPE", "Illegal seek"), + libc::EROFS => ("EROFS", "Read-only file system"), + libc::EMLINK => ("EMLINK", "Too many links"), + libc::EPIPE => ("EPIPE", "Broken pipe"), + libc::EDOM => ("EDOM", "Numerical argument out of domain"), + libc::ERANGE => ("ERANGE", "Numerical result out of range"), + libc::EDEADLK => ("EDEADLK", "Resource deadlock avoided"), + libc::ENAMETOOLONG => ("ENAMETOOLONG", "File name too long"), + libc::ENOLCK => ("ENOLCK", "No locks available"), + libc::ENOSYS => ("ENOSYS", "Function not implemented"), + libc::ENOTEMPTY => ("ENOTEMPTY", "Directory not empty"), + libc::ELOOP => ("ELOOP", "Too many levels of symbolic links"), + libc::ENOMSG => ("ENOMSG", "No message of desired type"), + libc::EIDRM => ("EIDRM", "Identifier removed"), + libc::ECHRNG => ("ECHRNG", "Channel number out of range"), + libc::EL2NSYNC => ("EL2NSYNC", "Level 2 not synchronized"), + libc::EL3HLT => ("EL3HLT", "Level 3 halted"), + libc::EL3RST => ("EL3RST", "Level 3 reset"), + libc::ELNRNG => ("ELNRNG", "Link number out of range"), + libc::EUNATCH => ("EUNATCH", "Protocol driver not attached"), + libc::ENOCSI => ("ENOCSI", "No CSI structure available"), + libc::EL2HLT => ("EL2HLT", "Level 2 halted"), + libc::EBADE => ("EBADE", "Invalid exchange"), + libc::EBADR => ("EBADR", "Invalid request descriptor"), + libc::EXFULL => ("EXFULL", "Exchange full"), + libc::ENOANO => ("ENOANO", "No anode"), + libc::EBADRQC => ("EBADRQC", "Invalid request code"), + libc::EBADSLT => ("EBADSLT", "Invalid slot"), + libc::EBFONT => ("EBFONT", "Bad font file format"), + libc::ENOSTR => ("ENOSTR", "Device not a stream"), + libc::ENODATA => ("ENODATA", "No data available"), + libc::ETIME => ("ETIME", "Timer expired"), + libc::ENOSR => ("ENOSR", "Out of streams resources"), + libc::ENONET => ("ENONET", "Machine is not on the network"), + libc::ENOPKG => ("ENOPKG", "Package not installed"), + libc::EREMOTE => ("EREMOTE", "Object is remote"), + libc::ENOLINK => ("ENOLINK", "Link has been severed"), + libc::EADV => ("EADV", "Advertise error"), + libc::ESRMNT => ("ESRMNT", "Srmount error"), + libc::ECOMM => ("ECOMM", "Communication error on send"), + libc::EPROTO => ("EPROTO", "Protocol error"), + libc::EMULTIHOP => ("EMULTIHOP", "Multihop attempted"), + libc::EDOTDOT => ("EDOTDOT", "RFS specific error"), + libc::EBADMSG => ("EBADMSG", "Bad message"), + libc::EOVERFLOW => ("EOVERFLOW", "Value too large for defined data type"), + libc::ENOTUNIQ => ("ENOTUNIQ", "Name not unique on network"), + libc::EBADFD => ("EBADFD", "File descriptor in bad state"), + libc::EREMCHG => ("EREMCHG", "Remote address changed"), + libc::ELIBACC => ("ELIBACC", "Can not access a needed shared library"), + libc::ELIBBAD => ("ELIBBAD", "Accessing a corrupted shared library"), + libc::ELIBSCN => ("ELIBSCN", ".lib section in a.out corrupted"), + libc::ELIBMAX => ("ELIBMAX", "Attempting to link in too many shared libraries"), + libc::ELIBEXEC => ("ELIBEXEC", "Cannot exec a shared library directly"), + libc::EILSEQ => ( + "EILSEQ", + "Invalid or incomplete multibyte or wide character", + ), + libc::ERESTART => ("ERESTART", "Interrupted system call should be restarted"), + libc::ESTRPIPE => ("ESTRPIPE", "Streams pipe error"), + libc::EUSERS => ("EUSERS", "Too many users"), + libc::ENOTSOCK => ("ENOTSOCK", "Socket operation on non-socket"), + libc::EDESTADDRREQ => ("EDESTADDRREQ", "Destination address required"), + libc::EMSGSIZE => ("EMSGSIZE", "Message too long"), + libc::EPROTOTYPE => ("EPROTOTYPE", "Protocol wrong type for socket"), + libc::ENOPROTOOPT => ("ENOPROTOOPT", "Protocol not available"), + libc::EPROTONOSUPPORT => ("EPROTONOSUPPORT", "Protocol not supported"), + libc::ESOCKTNOSUPPORT => ("ESOCKTNOSUPPORT", "Socket type not supported"), + libc::EOPNOTSUPP => ("EOPNOTSUPP", "Operation not supported"), + libc::EPFNOSUPPORT => ("EPFNOSUPPORT", "Protocol family not supported"), + libc::EAFNOSUPPORT => ("EAFNOSUPPORT", "Address family not supported by protocol"), + libc::EADDRINUSE => ("EADDRINUSE", "Address already in use"), + libc::EADDRNOTAVAIL => ("EADDRNOTAVAIL", "Cannot assign requested address"), + libc::ENETDOWN => ("ENETDOWN", "Network is down"), + libc::ENETUNREACH => ("ENETUNREACH", "Network is unreachable"), + libc::ENETRESET => ("ENETRESET", "Network dropped connection on reset"), + libc::ECONNABORTED => ("ECONNABORTED", "Software caused connection abort"), + libc::ECONNRESET => ("ECONNRESET", "Connection reset by peer"), + libc::ENOBUFS => ("ENOBUFS", "No buffer space available"), + libc::EISCONN => ("EISCONN", "Transport endpoint is already connected"), + libc::ENOTCONN => ("ENOTCONN", "Transport endpoint is not connected"), + libc::ESHUTDOWN => ("ESHUTDOWN", "Cannot send after transport endpoint shutdown"), + libc::ETOOMANYREFS => ("ETOOMANYREFS", "Too many references: cannot splice"), + libc::ETIMEDOUT => ("ETIMEDOUT", "Connection timed out"), + libc::ECONNREFUSED => ("ECONNREFUSED", "Connection refused"), + libc::EHOSTDOWN => ("EHOSTDOWN", "Host is down"), + libc::EHOSTUNREACH => ("EHOSTUNREACH", "No route to host"), + libc::EALREADY => ("EALREADY", "Operation already in progress"), + libc::EINPROGRESS => ("EINPROGRESS", "Operation now in progress"), + libc::ESTALE => ("ESTALE", "Stale file handle"), + libc::EUCLEAN => ("EUCLEAN", "Structure needs cleaning"), + libc::ENOTNAM => ("ENOTNAM", "Not a XENIX named type file"), + libc::ENAVAIL => ("ENAVAIL", "No XENIX semaphores available"), + libc::EISNAM => ("EISNAM", "Is a named type file"), + libc::EREMOTEIO => ("EREMOTEIO", "Remote I/O error"), + libc::EDQUOT => ("EDQUOT", "Disk quota exceeded"), + libc::ENOMEDIUM => ("ENOMEDIUM", "No medium found"), + libc::EMEDIUMTYPE => ("EMEDIUMTYPE", "Wrong medium type"), + libc::ECANCELED => ("ECANCELED", "Operation canceled"), + libc::ENOKEY => ("ENOKEY", "Required key not available"), + libc::EKEYEXPIRED => ("EKEYEXPIRED", "Key has expired"), + libc::EKEYREVOKED => ("EKEYREVOKED", "Key has been revoked"), + libc::EKEYREJECTED => ("EKEYREJECTED", "Key was rejected by service"), + libc::EOWNERDEAD => ("EOWNERDEAD", "Owner died"), + libc::ENOTRECOVERABLE => ("ENOTRECOVERABLE", "State not recoverable"), + libc::ERFKILL => ("ERFKILL", "Operation not possible due to RF-kill"), + libc::EHWPOISON => ("EHWPOISON", "Memory page has hardware error"), + _ => return None, + }; + + Some(info) +} + +// Raw sys_exit return values use the -errno convention; decode them so the +// user does not have to. +pub fn format_error_return(return_value: i64) -> Cow<'static, str> { + if (-4095..0).contains(&return_value) { + if let Some((name, description)) = errno_info(-return_value as i32) { + return Cow::Owned(format!("{return_value} ({name}: {description})")); + } + } + + Cow::Owned(format!("{return_value} (error)")) +} diff --git a/pinchy/src/server.rs b/pinchy/src/server.rs index 89a824b0..41fdd1dc 100644 --- a/pinchy/src/server.rs +++ b/pinchy/src/server.rs @@ -188,7 +188,7 @@ fn spawn_auto_quit_task(dispatch: SharedEventDispatch, idle_since: Arc anyhow::Result<()> { Ok(()) } +// pinchyd takes no arguments; this still gives us --help/--version and a +// proper error for anything else. +#[derive(clap::Parser, Debug)] +#[command(author, version, about)] +struct Args {} + #[tokio::main] async fn main() -> anyhow::Result<()> { + let _args: Args = clap::Parser::parse(); + env_logger::init(); // Bump the memlock rlimit. This is needed for older kernels that don't use the @@ -273,7 +281,10 @@ async fn main() -> anyhow::Result<()> { loader.set_max_entries("EVENTS", size); } - let mut ebpf = loader.load(ebpf_bytes)?; + let mut ebpf = loader.load(ebpf_bytes).context( + "failed to load eBPF programs; pinchyd must run as root \ + (or with CAP_BPF, CAP_PERFMON and CAP_SYS_ADMIN)", + )?; if let Err(e) = aya_log::EbpfLogger::init(&mut ebpf) { // This can happen if you remove all log statements from your eBPF program. warn!("failed to initialize eBPF logger: {e}"); @@ -312,7 +323,7 @@ async fn main() -> anyhow::Result<()> { load_tailcalls(&mut ebpf)?; - println!("Loaded eBPF programs in {:?}", now.elapsed()); + debug!("Loaded eBPF programs in {:?}", now.elapsed()); // Keeps track of how long since we handled an event; used to decide when to // automatically quit. @@ -334,7 +345,7 @@ async fn main() -> anyhow::Result<()> { conn.object_server().at("/org/pinchy/Service", dbus).await?; conn.request_name("org.pinchy.Service").await?; - println!("Pinchy D-Bus service started on {bus_type} bus"); + debug!("Pinchy D-Bus service started on {bus_type} bus"); // Drop privileges. At this point we have created maps, loaded programs, opened // event buffers and obtained our well-known D-Bus name, so we can diminish and @@ -342,14 +353,21 @@ async fn main() -> anyhow::Result<()> { drop_privileges()?; let ctrl_c = signal::ctrl_c(); + let mut sigterm = signal::unix::signal(signal::unix::SignalKind::terminate())?; - println!("Waiting for Ctrl-C..."); + // Deliberate readiness marker: the UML test runner (and anything else + // that needs to know the service is up) waits for this exact line. + println!("Pinchy daemon ready"); tokio::select! { result = ctrl_c => { - eprintln!("Ctrl-C received..."); + debug!("SIGINT received..."); conn.close().await?; result?; }, + _ = sigterm.recv() => { + debug!("SIGTERM received..."); + conn.close().await?; + }, }; println!("Exiting..."); diff --git a/pinchy/src/tests/filesystem.rs b/pinchy/src/tests/filesystem.rs index 42cd0a1b..04e287a0 100644 --- a/pinchy/src/tests/filesystem.rs +++ b/pinchy/src/tests/filesystem.rs @@ -136,7 +136,7 @@ syscall_test!( make_compact_test_data(SYS_fstat, 33, -1, &data) }, - "33 fstat(fd: 5, struct stat: { mode: 0o644 (rw-r--r--), ino: 9876543, dev: 0, nlink: 0, uid: 1000, gid: 1000, size: 12345, blksize: 4096, blocks: 24, atime: 0, mtime: 0, ctime: 0 }) = -1 (error)\n" + "33 fstat(fd: 5, struct stat: { mode: 0o644 (rw-r--r--), ino: 9876543, dev: 0, nlink: 0, uid: 1000, gid: 1000, size: 12345, blksize: 4096, blocks: 24, atime: 0, mtime: 0, ctime: 0 }) = -1 (EPERM: Operation not permitted)\n" ); syscall_test!( @@ -255,7 +255,7 @@ syscall_test!( make_compact_test_data(SYS_faccessat, 1001, -1, &data) }, - "1001 faccessat(dirfd: 3, pathname: \"/etc/hosts\", mode: F_OK) = -1 (error)\n" + "1001 faccessat(dirfd: 3, pathname: \"/etc/hosts\", mode: F_OK) = -1 (EPERM: Operation not permitted)\n" ); syscall_test!( @@ -291,7 +291,7 @@ syscall_test!( make_compact_test_data(syscalls::SYS_faccessat2, 1003, -1, &data) }, - "1003 faccessat2(dirfd: 3, pathname: \"/etc/hosts\", mode: F_OK, flags: AT_SYMLINK_NOFOLLOW (0x100)) = -1 (error)\n" + "1003 faccessat2(dirfd: 3, pathname: \"/etc/hosts\", mode: F_OK, flags: AT_SYMLINK_NOFOLLOW (0x100)) = -1 (EPERM: Operation not permitted)\n" ); syscall_test!( @@ -338,7 +338,7 @@ syscall_test!( make_compact_test_data(SYS_newfstatat, 42, -1, &data) }, - "42 newfstatat(dirfd: AT_FDCWD, pathname: \"test_file.txt\", struct stat: , flags: AT_SYMLINK_NOFOLLOW (0x100)) = -1 (error)\n" + "42 newfstatat(dirfd: AT_FDCWD, pathname: \"test_file.txt\", struct stat: , flags: AT_SYMLINK_NOFOLLOW (0x100)) = -1 (EPERM: Operation not permitted)\n" ); syscall_test!( @@ -733,7 +733,7 @@ syscall_test!( make_compact_test_data(SYS_getcwd, 55, -1, &data) }, - "55 getcwd(buf: 0x7ffe12345000, size: 4096) = -1 (error)\n" + "55 getcwd(buf: 0x7ffe12345000, size: 4096) = -1 (EPERM: Operation not permitted)\n" ); syscall_test!( @@ -765,7 +765,7 @@ syscall_test!( make_compact_test_data(SYS_chdir, 66, -1, &data) }, - "66 chdir(path: \"/home/user/newdir\") = -1 (error)\n" + "66 chdir(path: \"/home/user/newdir\") = -1 (EPERM: Operation not permitted)\n" ); syscall_test!( @@ -819,7 +819,7 @@ syscall_test!( make_compact_test_data(SYS_mkdirat, 77, -1, &data) }, - "77 mkdirat(dirfd: 5, pathname: \"/home/user/newdir\", mode: 0o700 (rwx------)) = -1 (error)\n" + "77 mkdirat(dirfd: 5, pathname: \"/home/user/newdir\", mode: 0o700 (rwx------)) = -1 (EPERM: Operation not permitted)\n" ); syscall_test!( @@ -865,7 +865,7 @@ syscall_test!( make_compact_test_data(SYS_ftruncate, 125, -1, &data) }, - "125 ftruncate(fd: 3, length: 4096) = -1 (error)\n" + "125 ftruncate(fd: 3, length: 4096) = -1 (EPERM: Operation not permitted)\n" ); syscall_test!( @@ -885,7 +885,7 @@ syscall_test!( make_compact_test_data(SYS_fchmod, 126, -1, &data) }, - "126 fchmod(fd: 3, mode: 0o644 (rw-r--r--)) = -1 (error)\n" + "126 fchmod(fd: 3, mode: 0o644 (rw-r--r--)) = -1 (EPERM: Operation not permitted)\n" ); syscall_test!( @@ -1144,7 +1144,7 @@ syscall_test!( crate::tests::make_compact_test_data(pinchy_common::syscalls::SYS_unlink, 301, -1, &data) }, - "301 unlink(pathname: \"/tmp/nonexistent\") = -1 (error)\n" + "301 unlink(pathname: \"/tmp/nonexistent\") = -1 (EPERM: Operation not permitted)\n" ); syscall_test!( @@ -1180,7 +1180,7 @@ syscall_test!( make_compact_test_data(pinchy_common::syscalls::SYS_unlinkat, 501, -1, &data) }, - "501 unlinkat(dirfd: AT_FDCWD, pathname: \"/tmp/nonexistent\", flags: 0) = -1 (error)\n" + "501 unlinkat(dirfd: AT_FDCWD, pathname: \"/tmp/nonexistent\", flags: 0) = -1 (EPERM: Operation not permitted)\n" ); #[cfg(target_arch = "x86_64")] @@ -1216,7 +1216,7 @@ syscall_test!( crate::tests::make_compact_test_data(pinchy_common::syscalls::SYS_symlink, 601, -1, &data) }, - "601 symlink(target: \"/target\", linkpath: \"/link\") = -1 (error)\n" + "601 symlink(target: \"/target\", linkpath: \"/link\") = -1 (EPERM: Operation not permitted)\n" ); syscall_test!( @@ -1258,7 +1258,7 @@ syscall_test!( make_compact_test_data(pinchy_common::syscalls::SYS_symlinkat, 701, -1, &data) }, - "701 symlinkat(target: \"/target\", newdirfd: AT_FDCWD, linkpath: \"/link\") = -1 (error)\n" + "701 symlinkat(target: \"/target\", newdirfd: AT_FDCWD, linkpath: \"/link\") = -1 (EPERM: Operation not permitted)\n" ); syscall_test!( @@ -1286,7 +1286,7 @@ syscall_test!( crate::tests::make_compact_test_data(SYS_acct, 901, -1, &data) }, - "901 acct(filename: \"/var/log/account\") = -1 (error)\n" + "901 acct(filename: \"/var/log/account\") = -1 (EPERM: Operation not permitted)\n" ); syscall_test!( @@ -1420,7 +1420,7 @@ syscall_test!( crate::tests::make_compact_test_data(syscalls::SYS_mknodat, 203, -1, &data) }, - "203 mknodat(dirfd: AT_FDCWD, pathname: \"/tmp/testfile\", mode: 0o644 (rw-r--r--) (S_IFREG), dev: 0) = -1 (error)\n" + "203 mknodat(dirfd: AT_FDCWD, pathname: \"/tmp/testfile\", mode: 0o644 (rw-r--r--) (S_IFREG), dev: 0) = -1 (EPERM: Operation not permitted)\n" ); // Mount management syscalls tests @@ -1468,7 +1468,7 @@ syscall_test!( &data, ) }, - "1001 pivot_root(new_root: \"/mnt/root\", put_old: \"/mnt/old\") = -1 (error)\n" + "1001 pivot_root(new_root: \"/mnt/root\", put_old: \"/mnt/old\") = -1 (EPERM: Operation not permitted)\n" ); syscall_test!( @@ -1498,7 +1498,7 @@ syscall_test!( crate::tests::make_compact_test_data(pinchy_common::syscalls::SYS_chroot, 1003, -1, &data) }, - "1003 chroot(path: \"/bad/path\") = -1 (error)\n" + "1003 chroot(path: \"/bad/path\") = -1 (EPERM: Operation not permitted)\n" ); syscall_test!( @@ -1715,7 +1715,7 @@ syscall_test!( crate::tests::make_compact_test_data(pinchy_common::syscalls::SYS_move_mount, 1012, -1, &data) }, - "1012 move_mount(from_dfd: 5, from_pathname: \"/mnt/source\", to_dfd: 7, to_pathname: \"/mnt/target\", flags: 0x0) = -1 (error)\n" + "1012 move_mount(from_dfd: 5, from_pathname: \"/mnt/source\", to_dfd: 7, to_pathname: \"/mnt/target\", flags: 0x0) = -1 (EPERM: Operation not permitted)\n" ); syscall_test!( @@ -1766,7 +1766,7 @@ syscall_test!( crate::tests::make_compact_test_data(pinchy_common::syscalls::SYS_swapon, 1015, -1, &data) }, - "1015 swapon(pathname: \"/tmp/badfile\", flags: 0x10000 (SWAP_FLAG_DISCARD)) = -1 (error)\n" + "1015 swapon(pathname: \"/tmp/badfile\", flags: 0x10000 (SWAP_FLAG_DISCARD)) = -1 (EPERM: Operation not permitted)\n" ); syscall_test!( @@ -1796,7 +1796,7 @@ syscall_test!( crate::tests::make_compact_test_data(pinchy_common::syscalls::SYS_swapoff, 1017, -1, &data) }, - "1017 swapoff(pathname: \"/dev/sda2\") = -1 (error)\n" + "1017 swapoff(pathname: \"/dev/sda2\") = -1 (EPERM: Operation not permitted)\n" ); syscall_test!( @@ -1844,7 +1844,7 @@ syscall_test!( make_compact_test_data(SYS_statfs, 44, -1, &data) }, - "44 statfs(pathname: \"/mnt/data\", buf: ) = -1 (error)\n" + "44 statfs(pathname: \"/mnt/data\", buf: ) = -1 (EPERM: Operation not permitted)\n" ); syscall_test!( @@ -2067,7 +2067,7 @@ syscall_test!( crate::tests::make_compact_test_data(SYS_fallocate, 999, -1, &data) }, - "999 fallocate(fd: 3, mode: 0x10 (FALLOC_FL_ZERO_RANGE), offset: 100, size: 500) = -1 (error)\n" + "999 fallocate(fd: 3, mode: 0x10 (FALLOC_FL_ZERO_RANGE), offset: 100, size: 500) = -1 (EPERM: Operation not permitted)\n" ); #[cfg(target_arch = "x86_64")] @@ -2103,7 +2103,7 @@ syscall_test!( crate::tests::make_compact_test_data(SYS_link, 801, -1, &data) }, - "801 link(oldpath: \"/nonexistent\", newpath: \"/link\") = -1 (error)\n" + "801 link(oldpath: \"/nonexistent\", newpath: \"/link\") = -1 (EPERM: Operation not permitted)\n" ); syscall_test!( @@ -2172,7 +2172,7 @@ syscall_test!( make_compact_test_data(SYS_linkat, 901, -1, &data) }, - "901 linkat(olddirfd: AT_FDCWD, oldpath: \"nonexistent\", newdirfd: AT_FDCWD, newpath: \"link\", flags: 0) = -1 (error)\n" + "901 linkat(olddirfd: AT_FDCWD, oldpath: \"nonexistent\", newdirfd: AT_FDCWD, newpath: \"link\", flags: 0) = -1 (EPERM: Operation not permitted)\n" ); // Use fanotify constants directly from libc @@ -2213,7 +2213,7 @@ syscall_test!( crate::tests::make_compact_test_data(SYS_fanotify_init, 9002, -1, &data) }, - "9002 fanotify_init(flags: 0, event_f_flags: 0x0 (O_RDONLY)) = -1 (error)\n" + "9002 fanotify_init(flags: 0, event_f_flags: 0x0 (O_RDONLY)) = -1 (EPERM: Operation not permitted)\n" ); syscall_test!( @@ -2274,7 +2274,7 @@ syscall_test!( crate::tests::make_compact_test_data(SYS_fanotify_mark, 9102, -9, &data) }, - "9102 fanotify_mark(fanotify_fd: 999, flags: 0x1 (INODE|ADD), mask: 0x1 (ACCESS), dirfd: AT_FDCWD, pathname: (null)) = -9 (error)\n" + "9102 fanotify_mark(fanotify_fd: 999, flags: 0x1 (INODE|ADD), mask: 0x1 (ACCESS), dirfd: AT_FDCWD, pathname: (null)) = -9 (EBADF: Bad file descriptor)\n" ); syscall_test!( @@ -2335,7 +2335,7 @@ syscall_test!( crate::tests::make_compact_test_data(SYS_name_to_handle_at, 9202, -2, &data) }, - "9202 name_to_handle_at(dirfd: AT_FDCWD, pathname: \"/nonexistent\", handle: 0x0, mount_id: 0x0, flags: 0) = -2 (error)\n" + "9202 name_to_handle_at(dirfd: AT_FDCWD, pathname: \"/nonexistent\", handle: 0x0, mount_id: 0x0, flags: 0) = -2 (ENOENT: No such file or directory)\n" ); syscall_test!( @@ -2377,7 +2377,7 @@ syscall_test!( crate::tests::make_compact_test_data(SYS_open_by_handle_at, 9302, -1, &data) }, - "9302 open_by_handle_at(mount_fd: 999, handle: 0xbadbadbad, flags: 0x0 (O_RDONLY)) = -1 (error)\n" + "9302 open_by_handle_at(mount_fd: 999, handle: 0xbadbadbad, flags: 0x0 (O_RDONLY)) = -1 (EPERM: Operation not permitted)\n" ); syscall_test!( @@ -2434,7 +2434,7 @@ syscall_test!( crate::tests::make_compact_test_data(SYS_copy_file_range, 9402, -1, &data) }, - "9402 copy_file_range(fd_in: 999, off_in: NULL, fd_out: 998, off_out: NULL, len: 0, flags: 0) = -1 (error)\n" + "9402 copy_file_range(fd_in: 999, off_in: NULL, fd_out: 998, off_out: NULL, len: 0, flags: 0) = -1 (EPERM: Operation not permitted)\n" ); syscall_test!( @@ -2481,7 +2481,7 @@ syscall_test!( crate::tests::make_compact_test_data(SYS_sync_file_range, 9502, -9, &data) }, - "9502 sync_file_range(fd: 999, offset: 0, nbytes: 0, flags: 0) = -9 (error)\n" + "9502 sync_file_range(fd: 999, offset: 0, nbytes: 0, flags: 0) = -9 (EBADF: Bad file descriptor)\n" ); syscall_test!( @@ -2501,7 +2501,7 @@ syscall_test!( crate::tests::make_compact_test_data(SYS_syncfs, 9601, -9, &data) }, - "9601 syncfs(fd: 999) = -9 (error)\n" + "9601 syncfs(fd: 999) = -9 (EBADF: Bad file descriptor)\n" ); syscall_test!( @@ -2584,7 +2584,7 @@ syscall_test!( crate::tests::make_compact_test_data(SYS_utimensat, 9702, -2, &data) }, - "9702 utimensat(dirfd: AT_FDCWD, pathname: \"/nonexistent\", times: [UTIME_NOW, UTIME_OMIT], flags: 0) = -2 (error)\n" + "9702 utimensat(dirfd: AT_FDCWD, pathname: \"/nonexistent\", times: [UTIME_NOW, UTIME_OMIT], flags: 0) = -2 (ENOENT: No such file or directory)\n" ); syscall_test!( @@ -2656,7 +2656,7 @@ syscall_test!( crate::tests::make_compact_test_data(SYS_quotactl, 10004, -1, &data) }, - "10004 quotactl(op: 0x800005 (QCMD(Q_GETINFO, USRQUOTA)), special: \"/dev/sdc1\", id: 500, addr: 0x7fff00003000) = -1 (error)\n" + "10004 quotactl(op: 0x800005 (QCMD(Q_GETINFO, USRQUOTA)), special: \"/dev/sdc1\", id: 500, addr: 0x7fff00003000) = -1 (EPERM: Operation not permitted)\n" ); syscall_test!( @@ -2750,7 +2750,7 @@ syscall_test!( crate::tests::make_compact_test_data(SYS_lookup_dcookie, 123, -1, &data) }, - "123 lookup_dcookie(cookie: 1311768467463790320, buffer: \"\", size: 64) = -1 (error)\n" + "123 lookup_dcookie(cookie: 1311768467463790320, buffer: \"\", size: 64) = -1 (EPERM: Operation not permitted)\n" ); syscall_test!( @@ -2842,7 +2842,7 @@ syscall_test!( crate::tests::make_compact_test_data(SYS_access, 124, -1, &data) }, - "124 access(pathname: \"/root/.s\" ... (truncated), mode: X_OK) = -1 (error)\n" + "124 access(pathname: \"/root/.s\" ... (truncated), mode: X_OK) = -1 (EPERM: Operation not permitted)\n" ); #[cfg(target_arch = "x86_64")] @@ -2928,7 +2928,7 @@ syscall_test!( crate::tests::make_compact_test_data(SYS_readlink, 129, -1, &data) }, - "129 readlink(pathname: \"/tmp/not\" ... (truncated), buf: \"\", bufsiz: 64) = -1 (error)\n" + "129 readlink(pathname: \"/tmp/not\" ... (truncated), buf: \"\", bufsiz: 64) = -1 (EPERM: Operation not permitted)\n" ); #[cfg(target_arch = "x86_64")] diff --git a/pinchy/src/tests/ipc.rs b/pinchy/src/tests/ipc.rs index 932c39b0..b5b20eda 100644 --- a/pinchy/src/tests/ipc.rs +++ b/pinchy/src/tests/ipc.rs @@ -30,7 +30,7 @@ syscall_test!( crate::tests::make_compact_test_data(pinchy_common::syscalls::SYS_shmat, 2222, -1, &data) }, - "2222 shmat(shmid: 456, shmaddr: 0x1000, shmflg: 0x0) = -1 (error)\n" + "2222 shmat(shmid: 456, shmaddr: 0x1000, shmflg: 0x0) = -1 (EPERM: Operation not permitted)\n" ); syscall_test!( @@ -54,7 +54,7 @@ syscall_test!( crate::tests::make_compact_test_data(pinchy_common::syscalls::SYS_shmdt, 4444, -1, &data) }, - "4444 shmdt(shmaddr: 0xdeadbeef) = -1 (error)\n" + "4444 shmdt(shmaddr: 0xdeadbeef) = -1 (EPERM: Operation not permitted)\n" ); syscall_test!( @@ -82,7 +82,7 @@ syscall_test!( crate::tests::make_compact_test_data(pinchy_common::syscalls::SYS_shmget, 6666, -1, &data) }, - "6666 shmget(key: 0xbeef, size: 8192, shmflg: 0x0) = -1 (error)\n" + "6666 shmget(key: 0xbeef, size: 8192, shmflg: 0x0) = -1 (EPERM: Operation not permitted)\n" ); syscall_test!( @@ -130,7 +130,7 @@ syscall_test!( crate::tests::make_compact_test_data(pinchy_common::syscalls::SYS_shmctl, 8888, -1, &data) }, - "8888 shmctl(shmid: 99, cmd: IPC_RMID, buf: NULL) = -1 (error)\n" + "8888 shmctl(shmid: 99, cmd: IPC_RMID, buf: NULL) = -1 (EPERM: Operation not permitted)\n" ); syscall_test!( @@ -223,7 +223,7 @@ syscall_test!( crate::tests::make_compact_test_data(pinchy_common::syscalls::SYS_msgctl, 10505, -1, &data) }, - "10505 msgctl(msqid: 666, cmd: IPC_RMID, buf: NULL) = -1 (error)\n" + "10505 msgctl(msqid: 666, cmd: IPC_RMID, buf: NULL) = -1 (EPERM: Operation not permitted)\n" ); syscall_test!( @@ -251,7 +251,7 @@ syscall_test!( crate::tests::make_compact_test_data(pinchy_common::syscalls::SYS_semget, 20002, -1, &data) }, - "20002 semget(key: 0xbeef, nsems: 2, semflg: 0x0) = -1 (error)\n" + "20002 semget(key: 0xbeef, nsems: 2, semflg: 0x0) = -1 (EPERM: Operation not permitted)\n" ); syscall_test!( @@ -279,7 +279,7 @@ syscall_test!( crate::tests::make_compact_test_data(pinchy_common::syscalls::SYS_semop, 20004, -1, &data) }, - "20004 semop(semid: 654, sops: 0x7fff3000, nsops: 1) = -1 (error)\n" + "20004 semop(semid: 654, sops: 0x7fff3000, nsops: 1) = -1 (EPERM: Operation not permitted)\n" ); syscall_test!( @@ -363,7 +363,7 @@ syscall_test!( crate::tests::make_compact_test_data(pinchy_common::syscalls::SYS_semctl, 20008, -1, &data) }, - "20008 semctl(semid: 88, semnum: 2, op: SETVAL, val: 0) = -1 (error)\n" + "20008 semctl(semid: 88, semnum: 2, op: SETVAL, val: 0) = -1 (EPERM: Operation not permitted)\n" ); #[cfg(target_arch = "x86_64")] @@ -405,7 +405,7 @@ syscall_test!( crate::tests::make_compact_test_data(pinchy_common::syscalls::SYS_eventfd, 7777, -1, &data) }, - "7777 eventfd(initval: 100, flags: 0xffffffff (EFD_CLOEXEC|EFD_NONBLOCK|UNKNOWN)) = -1 (error)\n" + "7777 eventfd(initval: 100, flags: 0xffffffff (EFD_CLOEXEC|EFD_NONBLOCK|UNKNOWN)) = -1 (EPERM: Operation not permitted)\n" ); syscall_test!( @@ -431,7 +431,7 @@ syscall_test!( crate::tests::make_compact_test_data(pinchy_common::syscalls::SYS_eventfd2, 9999, -1, &data) }, - "9999 eventfd2(initval: 42, flags: 0xf423f (EFD_CLOEXEC|UNKNOWN)) = -1 (error)\n" + "9999 eventfd2(initval: 42, flags: 0xf423f (EFD_CLOEXEC|UNKNOWN)) = -1 (EPERM: Operation not permitted)\n" ); syscall_test!( @@ -484,7 +484,7 @@ syscall_test!( crate::tests::make_compact_test_data(pinchy_common::syscalls::SYS_mq_open, 5678, -1, &data) }, - "5678 mq_open(name: 0x7fff9abc, flags: 0x801 (O_WRONLY|O_NONBLOCK), mode: 0o600 (rw-------), attr: NULL) = -1 (error)\n" + "5678 mq_open(name: 0x7fff9abc, flags: 0x801 (O_WRONLY|O_NONBLOCK), mode: 0o600 (rw-------), attr: NULL) = -1 (EPERM: Operation not permitted)\n" ); syscall_test!( @@ -509,7 +509,7 @@ syscall_test!( &data, ) }, - "3456 mq_unlink(name: 0x7fff3456) = -1 (error)\n" + "3456 mq_unlink(name: 0x7fff3456) = -1 (EPERM: Operation not permitted)\n" ); syscall_test!( @@ -541,7 +541,7 @@ syscall_test!( crate::tests::make_compact_test_data(pinchy_common::syscalls::SYS_mq_timedsend, 5678, -1, &data) }, - "5678 mq_timedsend(mqdes: 4, msg_ptr: 0x7fff6789, msg_len: 2048, msg_prio: 10, abs_timeout: 0x0) = -1 (error)\n" + "5678 mq_timedsend(mqdes: 4, msg_ptr: 0x7fff6789, msg_len: 2048, msg_prio: 10, abs_timeout: 0x0) = -1 (EPERM: Operation not permitted)\n" ); syscall_test!( @@ -573,7 +573,7 @@ syscall_test!( crate::tests::make_compact_test_data(pinchy_common::syscalls::SYS_mq_timedreceive, 7890, -1, &data) }, - "7890 mq_timedreceive(mqdes: 5, msg_ptr: 0x7fff9abc, msg_len: 4096, msg_prio: 1, abs_timeout: 0x0) = -1 (error)\n" + "7890 mq_timedreceive(mqdes: 5, msg_ptr: 0x7fff9abc, msg_len: 4096, msg_prio: 1, abs_timeout: 0x0) = -1 (EPERM: Operation not permitted)\n" ); syscall_test!( @@ -601,7 +601,7 @@ syscall_test!( &data, ) }, - "9012 mq_notify(mqdes: 4, sevp: 0x0) = -1 (error)\n" + "9012 mq_notify(mqdes: 4, sevp: 0x0) = -1 (EPERM: Operation not permitted)\n" ); syscall_test!( @@ -648,7 +648,7 @@ syscall_test!( &data, ) }, - "2468 mq_getsetattr(mqdes: 99, newattr: NULL, oldattr: NULL) = -1 (error)\n" + "2468 mq_getsetattr(mqdes: 99, newattr: NULL, oldattr: NULL) = -1 (EPERM: Operation not permitted)\n" ); syscall_test!( diff --git a/pinchy/src/tests/memory.rs b/pinchy/src/tests/memory.rs index d640d3e4..880696c2 100644 --- a/pinchy/src/tests/memory.rs +++ b/pinchy/src/tests/memory.rs @@ -57,7 +57,7 @@ syscall_test!( crate::tests::make_compact_test_data(SYS_mmap, 66, -1, &data) }, - "66 mmap(addr: 0x7f0000000000, length: 8192, prot: 0x4 (PROT_EXEC), flags: 0x1 (MAP_SHARED), fd: 5, offset: 0x1000) = -1 (error)\n" + "66 mmap(addr: 0x7f0000000000, length: 8192, prot: 0x4 (PROT_EXEC), flags: 0x1 (MAP_SHARED), fd: 5, offset: 0x1000) = -1 (EPERM: Operation not permitted)\n" ); syscall_test!( @@ -87,7 +87,7 @@ syscall_test!( crate::tests::make_compact_test_data(SYS_munmap, 123, -1, &data) }, - "123 munmap(addr: 0xffff8a9c2000, length: 57344) = -1 (error)\n" + "123 munmap(addr: 0xffff8a9c2000, length: 57344) = -1 (EPERM: Operation not permitted)\n" ); syscall_test!( @@ -120,7 +120,7 @@ syscall_test!( crate::tests::make_compact_test_data(SYS_mprotect, 77, -22, &data) }, - "77 mprotect(addr: 0x1000, length: 4096, prot: 0x2 (PROT_WRITE)) = -22 (error)\n" + "77 mprotect(addr: 0x1000, length: 4096, prot: 0x2 (PROT_WRITE)) = -22 (EINVAL: Invalid argument)\n" ); syscall_test!( @@ -178,7 +178,7 @@ syscall_test!( crate::tests::make_compact_test_data(SYS_madvise, 456, -1, &data) }, - "456 madvise(addr: 0x0, length: 4096, advice: MADV_WILLNEED (3)) = -1 (error)\n" + "456 madvise(addr: 0x0, length: 4096, advice: MADV_WILLNEED (3)) = -1 (EPERM: Operation not permitted)\n" ); syscall_test!( @@ -262,7 +262,7 @@ syscall_test!( crate::tests::make_compact_test_data(SYS_process_madvise, 456, -1, &data) }, - "456 process_madvise(pidfd: 9, iov: [ iovec { base: 0x7f9876543000, len: 8192 } ], iovcnt: 1, advice: MADV_WILLNEED (3), flags: 0) = -1 (error)\n" + "456 process_madvise(pidfd: 9, iov: [ iovec { base: 0x7f9876543000, len: 8192 } ], iovcnt: 1, advice: MADV_WILLNEED (3), flags: 0) = -1 (EPERM: Operation not permitted)\n" ); syscall_test!( @@ -346,7 +346,7 @@ syscall_test!( crate::tests::make_compact_test_data(SYS_munlockall, 456, -1, &data) }, - "456 munlockall() = -1 (error)\n" + "456 munlockall() = -1 (EPERM: Operation not permitted)\n" ); syscall_test!( @@ -723,7 +723,7 @@ syscall_test!( crate::tests::make_compact_test_data(SYS_process_vm_readv, 999, -1, &data) }, - "999 process_vm_readv(pid: 123, local_iov: [ iovec { base: 0x7f1234567000, len: 32, buf: \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\" } ], liovcnt: 1, remote_iov: [ iovec { base: 0x0, len: 32 } ], riovcnt: 1, flags: 0) = -1 (error)\n" + "999 process_vm_readv(pid: 123, local_iov: [ iovec { base: 0x7f1234567000, len: 32, buf: \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\" } ], liovcnt: 1, remote_iov: [ iovec { base: 0x0, len: 32 } ], riovcnt: 1, flags: 0) = -1 (EPERM: Operation not permitted)\n" ); syscall_test!( diff --git a/pinchy/src/tests/network.rs b/pinchy/src/tests/network.rs index 474a5114..3d86dea5 100644 --- a/pinchy/src/tests/network.rs +++ b/pinchy/src/tests/network.rs @@ -148,7 +148,7 @@ syscall_test!( crate::tests::make_compact_test_data(SYS_recvmsg, 555, -1, &data) }, format!( - "555 recvmsg(sockfd: 3, msg: {{ name: NULL, iov: NULL, iovlen: 0, control: NULL, flags: 0 }}, flags: 0x2 (MSG_PEEK)) = -1 (error)\n" + "555 recvmsg(sockfd: 3, msg: {{ name: NULL, iov: NULL, iovlen: 0, control: NULL, flags: 0 }}, flags: 0x2 (MSG_PEEK)) = -1 (EPERM: Operation not permitted)\n" ) ); @@ -255,7 +255,7 @@ syscall_test!( crate::tests::make_compact_test_data(SYS_accept4, 555, -1, &data) }, - "555 accept4(sockfd: 8, addr: NULL, addrlen: 0, flags: 0x800 (SOCK_NONBLOCK)) = -1 (error)\n" + "555 accept4(sockfd: 8, addr: NULL, addrlen: 0, flags: 0x800 (SOCK_NONBLOCK)) = -1 (EPERM: Operation not permitted)\n" ); syscall_test!( @@ -647,7 +647,7 @@ syscall_test!( crate::tests::make_compact_test_data(SYS_recvfrom, 9999, -1, &data) }, - "9999 recvfrom(sockfd: 4, buf: NULL, size: 256, flags: 0x40 (MSG_DONTWAIT), src_addr: NULL, addrlen: 0) = -1 (error)\n" + "9999 recvfrom(sockfd: 4, buf: NULL, size: 256, flags: 0x40 (MSG_DONTWAIT), src_addr: NULL, addrlen: 0) = -1 (EPERM: Operation not permitted)\n" ); syscall_test!( @@ -722,7 +722,7 @@ syscall_test!( crate::tests::make_compact_test_data(SYS_sendto, 9999, -1, &data) }, - "9999 sendto(sockfd: 4, buf: NULL, size: 256, flags: 0x40 (MSG_DONTWAIT), dest_addr: NULL, addrlen: 0) = -1 (error)\n" + "9999 sendto(sockfd: 4, buf: NULL, size: 256, flags: 0x40 (MSG_DONTWAIT), dest_addr: NULL, addrlen: 0) = -1 (EPERM: Operation not permitted)\n" ); syscall_test!( @@ -826,7 +826,7 @@ syscall_test!( crate::tests::make_compact_test_data(SYS_connect, 5678, -1, &data) }, - "5678 connect(sockfd: 5, addr: { family: AF_INET, addr: 192.168.1.100:443 }, addrlen: 16) = -1 (error)\n" + "5678 connect(sockfd: 5, addr: { family: AF_INET, addr: 192.168.1.100:443 }, addrlen: 16) = -1 (EPERM: Operation not permitted)\n" ); syscall_test!( @@ -868,7 +868,7 @@ syscall_test!( crate::tests::make_compact_test_data(SYS_socket, 9999, -1, &data) }, - "9999 socket(domain: AF_INET6, type: SOCK_RAW, protocol: 1) = -1 (error)\n" + "9999 socket(domain: AF_INET6, type: SOCK_RAW, protocol: 1) = -1 (EPERM: Operation not permitted)\n" ); syscall_test!( @@ -894,7 +894,7 @@ syscall_test!( crate::tests::make_compact_test_data(SYS_listen, 5678, -1, &data) }, - "5678 listen(sockfd: 7, backlog: 50) = -1 (error)\n" + "5678 listen(sockfd: 7, backlog: 50) = -1 (EPERM: Operation not permitted)\n" ); syscall_test!( @@ -933,7 +933,7 @@ syscall_test!( crate::tests::make_compact_test_data(SYS_shutdown, 9999, -1, &data) }, - "9999 shutdown(sockfd: 10, how: SHUT_WR) = -1 (error)\n" + "9999 shutdown(sockfd: 10, how: SHUT_WR) = -1 (EPERM: Operation not permitted)\n" ); syscall_test!( @@ -978,7 +978,7 @@ syscall_test!( crate::tests::make_compact_test_data(SYS_socketpair, 9999, -1, &data) }, - "9999 socketpair(domain: AF_INET, type: SOCK_STREAM, protocol: 0, sv: [?, ?]) = -1 (error)\n" + "9999 socketpair(domain: AF_INET, type: SOCK_STREAM, protocol: 0, sv: [?, ?]) = -1 (EPERM: Operation not permitted)\n" ); syscall_test!( @@ -1058,7 +1058,7 @@ syscall_test!( crate::tests::make_compact_test_data(SYS_getsockname, 9999, -1, &data) }, - "9999 getsockname(sockfd: 8, addr: NULL, addrlen: 0) = -1 (error)\n" + "9999 getsockname(sockfd: 8, addr: NULL, addrlen: 0) = -1 (EPERM: Operation not permitted)\n" ); syscall_test!( @@ -1144,7 +1144,7 @@ syscall_test!( crate::tests::make_compact_test_data(SYS_getpeername, 8888, -1, &data) }, - "8888 getpeername(sockfd: 11, addr: NULL, addrlen: 0) = -1 (error)\n" + "8888 getpeername(sockfd: 11, addr: NULL, addrlen: 0) = -1 (EPERM: Operation not permitted)\n" ); syscall_test!( @@ -1245,7 +1245,7 @@ syscall_test!( crate::tests::make_compact_test_data(SYS_getsockopt, 5678, -1, &data) }, - "5678 getsockopt(sockfd: 9, level: SOL_SOCKET, optname: SO_TYPE, optval: NULL, optlen: 0) = -1 (error)\n" + "5678 getsockopt(sockfd: 9, level: SOL_SOCKET, optname: SO_TYPE, optval: NULL, optlen: 0) = -1 (EPERM: Operation not permitted)\n" ); syscall_test!( diff --git a/pinchy/src/tests/process.rs b/pinchy/src/tests/process.rs index ff7d149a..41cd7b6b 100644 --- a/pinchy/src/tests/process.rs +++ b/pinchy/src/tests/process.rs @@ -292,7 +292,7 @@ syscall_test!( crate::tests::make_compact_test_data(SYS_prlimit64, 9876, -1, &data) }, - "9876 prlimit64(pid: 5678, resource: RLIMIT_AS, new_limit: { rlim_cur: 4294967296, rlim_max: 8589934592 }, old_limit: NULL) = -1 (error)\n" + "9876 prlimit64(pid: 5678, resource: RLIMIT_AS, new_limit: { rlim_cur: 4294967296, rlim_max: 8589934592 }, old_limit: NULL) = -1 (EPERM: Operation not permitted)\n" ); syscall_test!( @@ -435,7 +435,7 @@ syscall_test!( crate::tests::make_compact_test_data(SYS_waitid, 6001, -1, &data) }, - "6001 waitid(idtype: P_PID, id: 9999, infop: NULL, options: WEXITED) = -1 (error)\n" + "6001 waitid(idtype: P_PID, id: 9999, infop: NULL, options: WEXITED) = -1 (EPERM: Operation not permitted)\n" ); syscall_test!( @@ -514,7 +514,7 @@ syscall_test!( crate::tests::make_compact_test_data(SYS_getrusage, 5001, -22, &data) }, - "5001 getrusage(who: UNKNOWN, rusage: NULL) = -22 (error)\n" + "5001 getrusage(who: UNKNOWN, rusage: NULL) = -22 (EINVAL: Invalid argument)\n" ); syscall_test!( @@ -790,7 +790,7 @@ syscall_test!( crate::tests::make_compact_test_data(SYS_pause, 1001, -4, &data) }, - "1001 pause() = -4 (error)\n" + "1001 pause() = -4 (EINTR: Interrupted system call)\n" ); syscall_test!( @@ -993,7 +993,7 @@ syscall_test!( crate::tests::make_compact_test_data(SYS_fork, 1000, -1, &data) }, - "1000 fork() = -1 (error)\n" + "1000 fork() = -1 (EPERM: Operation not permitted)\n" ); #[cfg(target_arch = "x86_64")] @@ -1026,5 +1026,5 @@ syscall_test!( crate::tests::make_compact_test_data(SYS_vfork, 2000, -1, &data) }, - "2000 vfork() = -1 (error)\n" + "2000 vfork() = -1 (EPERM: Operation not permitted)\n" ); diff --git a/pinchy/src/tests/return_values.rs b/pinchy/src/tests/return_values.rs index 3e205fae..8c78d878 100644 --- a/pinchy/src/tests/return_values.rs +++ b/pinchy/src/tests/return_values.rs @@ -12,15 +12,15 @@ fn test_error_return_values() { // All syscalls should show -1 as error assert_eq!( format_return_value(syscalls::SYS_openat, -1).as_ref(), - "-1 (error)" + "-1 (EPERM: Operation not permitted)" ); assert_eq!( format_return_value(syscalls::SYS_read, -1).as_ref(), - "-1 (error)" + "-1 (EPERM: Operation not permitted)" ); assert_eq!( format_return_value(syscalls::SYS_close, -1).as_ref(), - "-1 (error)" + "-1 (EPERM: Operation not permitted)" ); } @@ -90,11 +90,11 @@ fn test_boolean_like_syscalls() { // Non-zero is error assert_eq!( format_return_value(syscalls::SYS_close, -2).as_ref(), - "-2 (error)" + "-2 (ENOENT: No such file or directory)" ); assert_eq!( format_return_value(syscalls::SYS_bind, -1).as_ref(), - "-1 (error)" + "-1 (EPERM: Operation not permitted)" ); } @@ -159,7 +159,7 @@ fn test_default_syscall_handling() { assert_eq!(format_return_value(unknown_syscall, 42).as_ref(), "42"); assert_eq!( format_return_value(unknown_syscall, -5).as_ref(), - "-5 (error)" + "-5 (EIO: Input/output error)" ); } @@ -192,7 +192,7 @@ fn test_adjtimex_return_values() { ); assert_eq!( format_return_value(syscalls::SYS_adjtimex, -22).as_ref(), - "-22 (error)" + "-22 (EINVAL: Invalid argument)" ); } @@ -213,7 +213,7 @@ fn test_clock_adjtime_return_values() { ); assert_eq!( format_return_value(syscalls::SYS_clock_adjtime, -95).as_ref(), - "-95 (error)" + "-95 (EOPNOTSUPP: Operation not supported)" ); } @@ -226,7 +226,7 @@ fn test_deprecated_syscall_return_values() { ); assert_eq!( format_return_value(syscalls::SYS_tuxcall, -38).as_ref(), - "-38 (error)" + "-38 (ENOSYS: Function not implemented)" ); assert_eq!( format_return_value(syscalls::SYS_sysfs, 2).as_ref(), @@ -240,47 +240,47 @@ fn test_raw_errno_return_values() { // error for these syscalls assert_eq!( format_return_value(syscalls::SYS_ppoll, -(libc::EINTR as i64)).as_ref(), - "-4 (error)" + "-4 (EINTR: Interrupted system call)" ); assert_eq!( format_return_value(syscalls::SYS_mmap, -(libc::ENOMEM as i64)).as_ref(), - "-12 (error)" + "-12 (ENOMEM: Cannot allocate memory)" ); assert_eq!( format_return_value(syscalls::SYS_mremap, -(libc::EFAULT as i64)).as_ref(), - "-14 (error)" + "-14 (EFAULT: Bad address)" ); assert_eq!( format_return_value(syscalls::SYS_shmat, -(libc::EACCES as i64)).as_ref(), - "-13 (error)" + "-13 (EACCES: Permission denied)" ); assert_eq!( format_return_value(syscalls::SYS_shmget, -(libc::ENOENT as i64)).as_ref(), - "-2 (error)" + "-2 (ENOENT: No such file or directory)" ); assert_eq!( format_return_value(syscalls::SYS_msgget, -(libc::ENOENT as i64)).as_ref(), - "-2 (error)" + "-2 (ENOENT: No such file or directory)" ); assert_eq!( format_return_value(syscalls::SYS_semget, -(libc::ENOENT as i64)).as_ref(), - "-2 (error)" + "-2 (ENOENT: No such file or directory)" ); assert_eq!( format_return_value(syscalls::SYS_inotify_add_watch, -(libc::ENOSPC as i64)).as_ref(), - "-28 (error)" + "-28 (ENOSPC: No space left on device)" ); assert_eq!( format_return_value(syscalls::SYS_membarrier, -(libc::EINVAL as i64)).as_ref(), - "-22 (error)" + "-22 (EINVAL: Invalid argument)" ); assert_eq!( format_return_value(syscalls::SYS_pkey_alloc, -(libc::ENOSPC as i64)).as_ref(), - "-28 (error)" + "-28 (ENOSPC: No space left on device)" ); assert_eq!( format_return_value(syscalls::SYS_rt_sigtimedwait, -(libc::EAGAIN as i64)).as_ref(), - "-11 (error)" + "-11 (EAGAIN: Resource temporarily unavailable)" ); // brk returns the new break, never an errno diff --git a/pinchy/src/tests/scheduling.rs b/pinchy/src/tests/scheduling.rs index 4fe02509..5f47bdbf 100644 --- a/pinchy/src/tests/scheduling.rs +++ b/pinchy/src/tests/scheduling.rs @@ -73,7 +73,7 @@ syscall_test!( crate::tests::make_compact_test_data(SYS_rseq, 1234, -22, &data) }, - "1234 rseq(rseq: NULL, rseq_len: 32, flags: 0, signature: 0xabcdef12) = -22 (error)\n" + "1234 rseq(rseq: NULL, rseq_len: 32, flags: 0, signature: 0xabcdef12) = -22 (EINVAL: Invalid argument)\n" ); syscall_test!( @@ -260,7 +260,7 @@ syscall_test!( &data, ) }, - "9999 sched_setscheduler(pid: 1234, policy: SCHED_OTHER, param: NULL) = -22 (error)\n" + "9999 sched_setscheduler(pid: 1234, policy: SCHED_OTHER, param: NULL) = -22 (EINVAL: Invalid argument)\n" ); syscall_test!( diff --git a/pinchy/src/tests/security.rs b/pinchy/src/tests/security.rs index a2178ff8..08c68f6d 100644 --- a/pinchy/src/tests/security.rs +++ b/pinchy/src/tests/security.rs @@ -50,7 +50,7 @@ syscall_test!( crate::tests::make_compact_test_data(SYS_ptrace, 5003, -1, &data) }, - "5003 ptrace(request: PTRACE_CONT, pid: 9999, addr: 0x0, sig: UNKNOWN(0)) = -1 (error)\n" + "5003 ptrace(request: PTRACE_CONT, pid: 9999, addr: 0x0, sig: UNKNOWN(0)) = -1 (EPERM: Operation not permitted)\n" ); syscall_test!( @@ -105,7 +105,7 @@ syscall_test!( crate::tests::make_compact_test_data(SYS_seccomp, 6003, -1, &data) }, - "6003 seccomp(operation: SECCOMP_GET_ACTION_AVAIL, flags: 0, action: NULL) = -1 (error)\n" + "6003 seccomp(operation: SECCOMP_GET_ACTION_AVAIL, flags: 0, action: NULL) = -1 (EPERM: Operation not permitted)\n" ); syscall_test!( @@ -123,7 +123,7 @@ syscall_test!( crate::tests::make_compact_test_data(SYS_seccomp, 6007, -14, &data) }, - "6007 seccomp(operation: SECCOMP_GET_ACTION_AVAIL, flags: 0, action: 0xdeadbeef) = -14 (error)\n" + "6007 seccomp(operation: SECCOMP_GET_ACTION_AVAIL, flags: 0, action: 0xdeadbeef) = -14 (EFAULT: Bad address)\n" ); syscall_test!( diff --git a/pinchy/src/tests/signal.rs b/pinchy/src/tests/signal.rs index 3fee761b..251985f9 100644 --- a/pinchy/src/tests/signal.rs +++ b/pinchy/src/tests/signal.rs @@ -223,7 +223,7 @@ syscall_test!( crate::tests::make_compact_test_data(pinchy_common::syscalls::SYS_tgkill, 1111, -3, &data) }, - "1111 tgkill(tgid: 9999, pid: 8888, sig: SIGTERM) = -3 (error)\n" + "1111 tgkill(tgid: 9999, pid: 8888, sig: SIGTERM) = -3 (ESRCH: No such process)\n" ); syscall_test!( @@ -322,7 +322,7 @@ syscall_test!( crate::tests::make_compact_test_data(SYS_rt_sigsuspend, 3691, -1, &data) }, - "3691 rt_sigsuspend(mask: 0x7fff87654321, sigsetsize: 8) = -1 (error)\n" + "3691 rt_sigsuspend(mask: 0x7fff87654321, sigsetsize: 8) = -1 (EPERM: Operation not permitted)\n" ); syscall_test!( @@ -337,7 +337,7 @@ syscall_test!( crate::tests::make_compact_test_data(SYS_rt_sigsuspend, 4815, -1, &data) }, - "4815 rt_sigsuspend(mask: NULL, sigsetsize: 8) = -1 (error)\n" + "4815 rt_sigsuspend(mask: NULL, sigsetsize: 8) = -1 (EPERM: Operation not permitted)\n" ); syscall_test!( diff --git a/pinchy/src/tests/sync.rs b/pinchy/src/tests/sync.rs index e8707807..57ef96fd 100644 --- a/pinchy/src/tests/sync.rs +++ b/pinchy/src/tests/sync.rs @@ -54,7 +54,7 @@ syscall_test!( crate::tests::make_compact_test_data(SYS_set_robust_list, 1234, -22, &data) }, - "1234 set_robust_list(head: 0x7f1234560000, len: 0) = -22 (error)\n" + "1234 set_robust_list(head: 0x7f1234560000, len: 0) = -22 (EINVAL: Invalid argument)\n" ); syscall_test!( @@ -127,5 +127,5 @@ syscall_test!( crate::tests::make_compact_test_data(SYS_get_robust_list, 123, -22, &data) }, - "123 get_robust_list(pid: 1234, head: (content unavailable), len: (content unavailable)) = -22 (error)\n" + "123 get_robust_list(pid: 1234, head: (content unavailable), len: (content unavailable)) = -22 (EINVAL: Invalid argument)\n" ); diff --git a/pinchy/src/tests/system.rs b/pinchy/src/tests/system.rs index ebb36637..e53cce74 100644 --- a/pinchy/src/tests/system.rs +++ b/pinchy/src/tests/system.rs @@ -91,7 +91,7 @@ syscall_test!( crate::tests::make_compact_test_data(SYS_reboot, 42, -22, &data) }, - "42 reboot(magic1: 0xfee1dead (LINUX_REBOOT_MAGIC1), magic2: 0x28121969 (LINUX_REBOOT_MAGIC2), cmd: LINUX_REBOOT_CMD_HALT (-839974621), arg: 0x0) = -22 (error)\n" + "42 reboot(magic1: 0xfee1dead (LINUX_REBOOT_MAGIC1), magic2: 0x28121969 (LINUX_REBOOT_MAGIC2), cmd: LINUX_REBOOT_CMD_HALT (-839974621), arg: 0x0) = -22 (EINVAL: Invalid argument)\n" ); syscall_test!( @@ -175,7 +175,7 @@ syscall_test!( crate::tests::make_compact_test_data(SYS_getrandom, 555, -11, &data) }, - "555 getrandom(buf: 0x7f5678901000, buflen: 32, flags: 0x3 (GRND_NONBLOCK|GRND_RANDOM)) = -11 (error)\n" + "555 getrandom(buf: 0x7f5678901000, buflen: 32, flags: 0x3 (GRND_NONBLOCK|GRND_RANDOM)) = -11 (EAGAIN: Resource temporarily unavailable)\n" ); syscall_test!( @@ -499,7 +499,7 @@ syscall_test!( crate::tests::make_compact_test_data(SYS_nanosleep, 5678, -4, &data) }, - "5678 nanosleep(req: { secs: 10, nanos: 0 }, rem: { secs: 7, nanos: 250000000 }) = -4 (error)\n" + "5678 nanosleep(req: { secs: 10, nanos: 0 }, rem: { secs: 7, nanos: 250000000 }) = -4 (EINTR: Interrupted system call)\n" ); syscall_test!( @@ -565,7 +565,7 @@ syscall_test!( crate::tests::make_compact_test_data(SYS_clock_nanosleep, 5678, -4, &data) }, - "5678 clock_nanosleep(clockid: CLOCK_MONOTONIC, flags: 0, req: { secs: 10, nanos: 0 }, rem: { secs: 7, nanos: 250000000 }) = -4 (error)\n" + "5678 clock_nanosleep(clockid: CLOCK_MONOTONIC, flags: 0, req: { secs: 10, nanos: 0 }, rem: { secs: 7, nanos: 250000000 }) = -4 (EINTR: Interrupted system call)\n" ); syscall_test!( @@ -729,7 +729,7 @@ syscall_test!( crate::tests::make_compact_test_data(pinchy_common::syscalls::SYS_setrlimit, 101, -1, &data) }, - "101 setrlimit(resource: RLIMIT_NOFILE, limit: NULL) = -1 (error)\n" + "101 setrlimit(resource: RLIMIT_NOFILE, limit: NULL) = -1 (EPERM: Operation not permitted)\n" ); syscall_test!( @@ -763,7 +763,7 @@ syscall_test!( crate::tests::make_compact_test_data(pinchy_common::syscalls::SYS_getrlimit, 201, -1, &data) }, - "201 getrlimit(resource: RLIMIT_STACK, limit: (content unavailable)) = -1 (error)\n" + "201 getrlimit(resource: RLIMIT_STACK, limit: (content unavailable)) = -1 (EPERM: Operation not permitted)\n" ); syscall_test!( @@ -777,7 +777,7 @@ syscall_test!( crate::tests::make_compact_test_data(pinchy_common::syscalls::SYS_getrlimit, 202, -1, &data) }, - "202 getrlimit(resource: RLIMIT_STACK, limit: NULL) = -1 (error)\n" + "202 getrlimit(resource: RLIMIT_STACK, limit: NULL) = -1 (EPERM: Operation not permitted)\n" ); syscall_test!( @@ -813,7 +813,7 @@ syscall_test!( crate::tests::make_compact_test_data(SYS_init_module, 1001, -17, &data) }, - "1001 init_module(module_image: 0x7f8000002000, len: 32768, param_values: \"\") = -17 (error)\n" + "1001 init_module(module_image: 0x7f8000002000, len: 32768, param_values: \"\") = -17 (EEXIST: File exists)\n" ); syscall_test!( @@ -867,7 +867,7 @@ syscall_test!( crate::tests::make_compact_test_data(SYS_finit_module, 2002, -2, &data) }, - "2002 finit_module(fd: -1, param_values: \"\", flags: 0) = -2 (error)\n" + "2002 finit_module(fd: -1, param_values: \"\", flags: 0) = -2 (ENOENT: No such file or directory)\n" ); syscall_test!( @@ -913,7 +913,7 @@ syscall_test!( crate::tests::make_compact_test_data(SYS_delete_module, 3002, -2, &data) }, - "3002 delete_module(name: \"nonexistent_module\", flags: 0) = -2 (error)\n" + "3002 delete_module(name: \"nonexistent_module\", flags: 0) = -2 (ENOENT: No such file or directory)\n" ); syscall_test!( @@ -941,7 +941,7 @@ syscall_test!( crate::tests::make_compact_test_data(SYS_sethostname, 4001, -22, &data) }, - "4001 sethostname(name: \"verylonghostname\", len: 16) = -22 (error)\n" + "4001 sethostname(name: \"verylonghostname\", len: 16) = -22 (EINVAL: Invalid argument)\n" ); syscall_test!( @@ -969,7 +969,7 @@ syscall_test!( crate::tests::make_compact_test_data(SYS_setdomainname, 5001, -1, &data) }, - "5001 setdomainname(name: \"veryverylongdomainname.example.org\", len: 34) = -1 (error)\n" + "5001 setdomainname(name: \"veryverylongdomainname.example.org\", len: 34) = -1 (EPERM: Operation not permitted)\n" ); syscall_test!( @@ -1063,7 +1063,7 @@ syscall_test!( crate::tests::make_compact_test_data(SYS_landlock_add_rule, 6004, -9, &data) }, - "6004 landlock_add_rule(ruleset_fd: 999, rule_type: LANDLOCK_RULE_PATH_BENEATH, parent_fd: 0, allowed_access: 0, flags: 0) = -9 (error)\n" + "6004 landlock_add_rule(ruleset_fd: 999, rule_type: LANDLOCK_RULE_PATH_BENEATH, parent_fd: 0, allowed_access: 0, flags: 0) = -9 (EBADF: Bad file descriptor)\n" ); syscall_test!( @@ -1089,7 +1089,7 @@ syscall_test!( crate::tests::make_compact_test_data(SYS_landlock_restrict_self, 6006, -22, &data) }, - "6006 landlock_restrict_self(ruleset_fd: -1, flags: 0) = -22 (error)\n" + "6006 landlock_restrict_self(ruleset_fd: -1, flags: 0) = -22 (EINVAL: Invalid argument)\n" ); syscall_test!( parse_add_key_user_type, @@ -1169,7 +1169,7 @@ syscall_test!( crate::tests::make_compact_test_data(SYS_add_key, 7002, -22, &data) }, - "7002 add_key(type: \"unknown\", description: \"\", payload: (empty), keyring: KEY_SPEC_THREAD_KEYRING) = -22 (error)\n" + "7002 add_key(type: \"unknown\", description: \"\", payload: (empty), keyring: KEY_SPEC_THREAD_KEYRING) = -22 (EINVAL: Invalid argument)\n" ); syscall_test!( @@ -1351,7 +1351,7 @@ syscall_test!( crate::tests::make_compact_test_data(SYS_perf_event_open, 8002, -22, &data) }, - "8002 perf_event_open(attr: { type: PERF_TYPE_HARDWARE, size: 0, config: 0x0, sample_period: 0 }, pid: 0, cpu: -1, group_fd: -1, flags: 0) = -22 (error)\n" + "8002 perf_event_open(attr: { type: PERF_TYPE_HARDWARE, size: 0, config: 0x0, sample_period: 0 }, pid: 0, cpu: -1, group_fd: -1, flags: 0) = -22 (EINVAL: Invalid argument)\n" ); syscall_test!( @@ -1418,7 +1418,7 @@ syscall_test!( crate::tests::make_compact_test_data(SYS_bpf, 8102, -1, &data) }, - "8102 bpf(cmd: BPF_PROG_LOAD, size: 0) = -1 (error)\n" + "8102 bpf(cmd: BPF_PROG_LOAD, size: 0) = -1 (EPERM: Operation not permitted)\n" ); syscall_test!( @@ -1460,7 +1460,7 @@ syscall_test!( crate::tests::make_compact_test_data(SYS_syslog, 9102, -1, &data) }, - "9102 syslog(type: SYSLOG_ACTION_READ, bufp: 0x0, size: 0) = -1 (error)\n" + "9102 syslog(type: SYSLOG_ACTION_READ, bufp: 0x0, size: 0) = -1 (EPERM: Operation not permitted)\n" ); syscall_test!( @@ -1635,7 +1635,7 @@ syscall_test!( crate::tests::make_compact_test_data(pinchy_common::syscalls::SYS_tuxcall, 100, -1, &data) }, - "100 tuxcall(0x1234, 0x5678) = -1 (error)\n" + "100 tuxcall(0x1234, 0x5678) = -1 (EPERM: Operation not permitted)\n" ); #[cfg(target_arch = "x86_64")] @@ -1650,7 +1650,7 @@ syscall_test!( crate::tests::make_compact_test_data(pinchy_common::syscalls::SYS_ustat, 100, -38, &data) }, - "100 ustat(0x1234, 0x0, 0x5678) = -38 (error)\n" + "100 ustat(0x1234, 0x0, 0x5678) = -38 (ENOSYS: Function not implemented)\n" ); #[cfg(target_arch = "x86_64")] diff --git a/pinchy/src/tests/time.rs b/pinchy/src/tests/time.rs index dd8435ae..1f0cff81 100644 --- a/pinchy/src/tests/time.rs +++ b/pinchy/src/tests/time.rs @@ -415,7 +415,7 @@ syscall_test!( crate::tests::make_compact_test_data(SYS_getitimer, 1234, -1, &data) }, - "1234 getitimer(which: 999, curr_value: { it_interval: { tv_sec: 0, tv_usec: 0 }, it_value: { tv_sec: 0, tv_usec: 0 } }) = -1 (error)\n" + "1234 getitimer(which: 999, curr_value: { it_interval: { tv_sec: 0, tv_usec: 0 }, it_value: { tv_sec: 0, tv_usec: 0 } }) = -1 (EPERM: Operation not permitted)\n" ); syscall_test!( @@ -514,7 +514,7 @@ syscall_test!( crate::tests::make_compact_test_data(SYS_setitimer, 1234, -1, &data) }, - "1234 setitimer(which: -1, new_value: { it_interval: { tv_sec: 0, tv_usec: 0 }, it_value: { tv_sec: 0, tv_usec: 0 } }, old_value: { it_interval: { tv_sec: 0, tv_usec: 0 }, it_value: { tv_sec: 0, tv_usec: 0 } }) = -1 (error)\n" + "1234 setitimer(which: -1, new_value: { it_interval: { tv_sec: 0, tv_usec: 0 }, it_value: { tv_sec: 0, tv_usec: 0 } }, old_value: { it_interval: { tv_sec: 0, tv_usec: 0 }, it_value: { tv_sec: 0, tv_usec: 0 } }) = -1 (EPERM: Operation not permitted)\n" ); syscall_test!( diff --git a/pinchy/tests/integration.rs b/pinchy/tests/integration.rs index 47b63a0d..7f54e17d 100644 --- a/pinchy/tests/integration.rs +++ b/pinchy/tests/integration.rs @@ -40,7 +40,7 @@ fn memory_policy_syscalls() { let expected_output = escaped_regex(indoc! {r#" @PID@ set_mempolicy(mode: MPOL_DEFAULT, nodemask: NULL, maxnode: 0) = 0 (success) @PID@ mbind(addr: @ADDR@, len: 4096, mode: MPOL_PREFERRED, nodemask: [0], maxnode: 1, flags: 0) = @NUMBER@ (success) - @PID@ get_mempolicy(mode: NULL, nodemask: NULL, maxnode: 64, addr: @ADDR@, flags: 0x1 (MPOL_F_NODE)) = -@NUMBER@ (error) + @PID@ get_mempolicy(mode: NULL, nodemask: NULL, maxnode: 64, addr: @ADDR@, flags: 0x1 (MPOL_F_NODE)) = @ERRNO@ @PID@ mincore(addr: @ADDR@, length: 8192, vec: [0,0]) = 0 (success) @PID@ migrate_pages(pid: @NUMBER@, maxnode: 64, old_nodes: [0], new_nodes: [0]) = 0 (pages not migrated) @PID@ move_pages(pid: @NUMBER@, count: 1, pages: [@ADDR@], nodes: [0], status: [-@NUMBER@], flags: 0) = 0 (success) @@ -53,7 +53,7 @@ fn memory_policy_syscalls() { // std::io::stderr().write_all(&output.stdout).unwrap(); Assert::new(output) .success() - .stdout(predicate::str::is_match(&expected_output).unwrap()); + .stderr(predicate::str::is_match(&expected_output).unwrap()); // Server output - has to be at the end, since we kill the server for waiting. let output = pinchy.wait(); @@ -108,7 +108,7 @@ fn epoll_syscalls() { let output = handle.join().unwrap(); Assert::new(output) .success() - .stdout(predicate::str::is_match(&expected_output).unwrap()); + .stderr(predicate::str::is_match(&expected_output).unwrap()); let output = pinchy.wait(); Assert::new(output) @@ -167,7 +167,7 @@ fn pinchy_reads() { @PID@ read(fd: 3, buf: "", count: 1024) = 0 (bytes) @PID@ openat2(dfd: AT_FDCWD, pathname: "pinchy/tests/GPLv2", how: { flags: 0x0 (O_RDONLY), mode: 0, resolve: 0xc (RESOLVE_BENEATH|RESOLVE_NO_SYMLINKS) }, size: 24) = @NUMBER@ (fd) @PID@ openat2(dfd: AT_FDCWD, pathname: "pinchy/tests/GPLv2", how: { flags: 0x0 (O_RDONLY), mode: 0, resolve: 0 }, size: 24) = @NUMBER@ (fd) - @PID@ openat2(dfd: AT_FDCWD, pathname: "pinchy/tests/non-existent-file", how: { flags: 0x0 (O_RDONLY), mode: 0, resolve: 0x4 (RESOLVE_NO_SYMLINKS) }, size: 24) = -2 (error) + @PID@ openat2(dfd: AT_FDCWD, pathname: "pinchy/tests/non-existent-file", how: { flags: 0x0 (O_RDONLY), mode: 0, resolve: 0x4 (RESOLVE_NO_SYMLINKS) }, size: 24) = -2 (ENOENT: No such file or directory) "#}); let output = handle.join().unwrap(); @@ -175,7 +175,7 @@ fn pinchy_reads() { // stderr().write_all(&output.stdout).unwrap(); Assert::new(output) .success() - .stdout(predicate::str::is_match(&expected_output).unwrap()); + .stderr(predicate::str::is_match(&expected_output).unwrap()); // Server output - has to be at the end, since we kill the server for waiting. let output = pinchy.wait(); @@ -208,13 +208,13 @@ fn filesystem_syscalls() { @PID@ newfstatat(dirfd: AT_FDCWD, pathname: "pinchy/tests/GPLv2", struct stat: { mode: 0o@NUMBER@ (@MODE@), ino: @NUMBER@, dev: @NUMBER@, nlink: @NUMBER@, uid: @NUMBER@, gid: @NUMBER@, size: 18092, blksize: @NUMBER@, blocks: @NUMBER@, atime: @NUMBER@, mtime: @NUMBER@, ctime: @NUMBER@ }, flags: 0) = 0 (success) @PID@ faccessat(dirfd: AT_FDCWD, pathname: "pinchy/tests/GPLv2", mode: R_OK) = 0 (success) @PID@ faccessat2(dirfd: AT_FDCWD, pathname: "pinchy/tests/GPLv2", mode: R_OK, flags: 0) = 0 (success) - @PID@ faccessat2(dirfd: AT_FDCWD, pathname: "pinchy/tests/non-existent-file", mode: R_OK, flags: 0) = -2 (error) + @PID@ faccessat2(dirfd: AT_FDCWD, pathname: "pinchy/tests/non-existent-file", mode: R_OK, flags: 0) = -2 (ENOENT: No such file or directory) "#}); let output = handle.join().unwrap(); Assert::new(output) .success() - .stdout(predicate::str::is_match(&expected_output).unwrap()); + .stderr(predicate::str::is_match(&expected_output).unwrap()); let output = pinchy.wait(); Assert::new(output) @@ -232,14 +232,14 @@ fn statfs_syscalls() { // Expected output - we test both success and error cases let expected_output = escaped_regex(indoc! {r#" @PID@ statfs(pathname: "pinchy/tests/GPLv2", buf: { type: @ALPHANUM@ (0x@HEXNUMBER@), block_size: @NUMBER@, blocks: @NUMBER@, blocks_free: @NUMBER@, blocks_available: @NUMBER@, files: @NUMBER@, files_free: @NUMBER@, fsid: [@SIGNEDNUMBER@, @SIGNEDNUMBER@], name_max: @NUMBER@, fragment_size: @NUMBER@, mount_flags: 0x@HEXNUMBER@ (@ALPHANUM@) }) = 0 (success) - @PID@ statfs(pathname: "/non/existent/path", buf: ) = -2 (error) + @PID@ statfs(pathname: "/non/existent/path", buf: ) = -2 (ENOENT: No such file or directory) @PID@ fstatfs(fd: @NUMBER@, buf: { type: @ALPHANUM@ (0x@HEXNUMBER@), block_size: @NUMBER@, blocks: @NUMBER@, blocks_free: @NUMBER@, blocks_available: @NUMBER@, files: @NUMBER@, files_free: @NUMBER@, fsid: [@SIGNEDNUMBER@, @SIGNEDNUMBER@], name_max: @NUMBER@, fragment_size: @NUMBER@, mount_flags: 0x@HEXNUMBER@ (@ALPHANUM@) }) = 0 (success) "#}); let output = handle.join().unwrap(); Assert::new(output) .success() - .stdout(predicate::str::is_match(&expected_output).unwrap()); + .stderr(predicate::str::is_match(&expected_output).unwrap()); let output = pinchy.wait(); Assert::new(output) @@ -265,7 +265,7 @@ fn rt_sig() { let output = handle.join().unwrap(); Assert::new(output) .success() - .stdout(predicate::str::is_match(&expected_output).unwrap()); + .stderr(predicate::str::is_match(&expected_output).unwrap()); // Server output - has to be at the end, since we kill the server for waiting. let output = pinchy.wait(); @@ -291,7 +291,7 @@ fn rt_sigaction_realtime() { let output = handle.join().unwrap(); Assert::new(output) .success() - .stdout(predicate::str::is_match(&expected_output).unwrap()); + .stderr(predicate::str::is_match(&expected_output).unwrap()); // Server output - has to be at the end, since we kill the server for waiting. let output = pinchy.wait(); @@ -317,7 +317,7 @@ fn rt_sigaction_standard() { let output = handle.join().unwrap(); Assert::new(output) .success() - .stdout(predicate::str::is_match(&expected_output).unwrap()); + .stderr(predicate::str::is_match(&expected_output).unwrap()); // Server output - has to be at the end, since we kill the server for waiting. let output = pinchy.wait(); @@ -345,7 +345,7 @@ fn fcntl_syscalls() { let output = handle.join().unwrap(); Assert::new(output) .success() - .stdout(predicate::str::is_match(&expected_output).unwrap()); + .stderr(predicate::str::is_match(&expected_output).unwrap()); // Server output - has to be at the end, since we kill the server for waiting. let output = pinchy.wait(); @@ -383,7 +383,7 @@ fn pipe_operations_syscalls() { // std::io::stderr().write_all(&output.stdout).unwrap(); Assert::new(output) .success() - .stdout(predicate::str::is_match(&expected_output).unwrap()); + .stderr(predicate::str::is_match(&expected_output).unwrap()); // Server output - has to be at the end, since we kill the server for waiting. let output = pinchy.wait(); @@ -405,13 +405,13 @@ fn io_uring_syscalls() { let expected_output = escaped_regex(indoc! {r#" @PID@ io_uring_setup(entries: 8, params_ptr: @ADDR@, params: { sq_entries: 8, cq_entries: 16, flags: 0, sq_thread_cpu: 0, sq_thread_idle: 0, features: 0x@HEXNUMBER@ (IORING_FEAT_SINGLE_MMAP|IORING_FEAT_NODROP|IORING_FEAT_SUBMIT_STABLE|IORING_FEAT_RW_CUR_POS|IORING_FEAT_CUR_PERSONALITY|IORING_FEAT_FAST_POLL|IORING_FEAT_POLL_32BITS|IORING_FEAT_SQPOLL_NONFIXED|IORING_FEAT_EXT_ARG|IORING_FEAT_NATIVE_WORKERS|IORING_FEAT_RSRC_TAGS|IORING_FEAT_CQE_SKIP|IORING_FEAT_LINKED_FILE|IORING_FEAT_REG_REG_RING|IORING_FEAT_RECVSEND_BUNDLE|IORING_FEAT_MIN_TIMEOUT|IORING_FEAT_RW_ATTR|IORING_FEAT_NO_IOWAIT), wq_fd: 0 }) = @NUMBER@ (fd) @PID@ io_uring_enter(fd: @NUMBER@, to_submit: 0, min_complete: 0, flags: 0x5 (IORING_ENTER_GETEVENTS|IORING_ENTER_SQ_WAIT), sig: 0x0, sigsz: 0) = @NUMBER@ (submitted) - @PID@ io_uring_register(fd: @NUMBER@, opcode: IORING_REGISTER_PROBE, arg: @ADDR@, nr_args: 4) = -@NUMBER@ (error) + @PID@ io_uring_register(fd: @NUMBER@, opcode: IORING_REGISTER_PROBE, arg: @ADDR@, nr_args: 4) = @ERRNO@ "#}); let output = handle.join().unwrap(); Assert::new(output) .success() - .stdout(predicate::str::is_match(expected_output).unwrap()); + .stderr(predicate::str::is_match(expected_output).unwrap()); let output = pinchy.wait(); Assert::new(output) @@ -459,7 +459,7 @@ fn io_multiplexing_syscalls() { // std::io::stderr().write_all(&output.stdout).unwrap(); Assert::new(output) .success() - .stdout(predicate::str::is_match(&expected_output).unwrap()); + .stderr(predicate::str::is_match(&expected_output).unwrap()); // Server output - has to be at the end, since we kill the server for waiting. let output = pinchy.wait(); @@ -482,6 +482,7 @@ fn escaped_regex(expected_output: &str) -> String { escaped = escaped.replace("@MAYBEITEM_@", "([^ \"]+ )?"); escaped = escaped.replace("@MAYBETRUNCATED@", r"( ... \(truncated\))?"); escaped = escaped.replace("@GROUPLIST@", r"[0-9, ]*"); + escaped = escaped.replace("@ERRNO@", r"-[0-9]+ \([A-Z0-9]+: [^)]*\)"); escaped = escaped.replace("@ANY@", ".+"); escaped } @@ -499,7 +500,7 @@ fn fchdir_syscall() { let output = handle.join().unwrap(); Assert::new(output) .success() - .stdout(predicate::str::is_match(&expected_output).unwrap()); + .stderr(predicate::str::is_match(&expected_output).unwrap()); let output = pinchy.wait(); Assert::new(output) @@ -529,7 +530,7 @@ fn filesystem_sync_syscalls() { let output = handle.join().unwrap(); Assert::new(output) .success() - .stdout(predicate::str::is_match(&expected_output).unwrap()); + .stderr(predicate::str::is_match(&expected_output).unwrap()); let output = pinchy.wait(); Assert::new(output) @@ -559,7 +560,7 @@ fn network_syscalls() { // std::io::stderr().write_all(&output.stdout).unwrap(); Assert::new(output) .success() - .stdout(predicate::str::is_match(&expected_output).unwrap()); + .stderr(predicate::str::is_match(&expected_output).unwrap()); // Server output - has to be at the end, since we kill the server for waiting. let output = pinchy.wait(); @@ -600,7 +601,7 @@ fn socket_introspection_syscalls() { let output = handle.join().unwrap(); Assert::new(output) .success() - .stdout(predicate::str::is_match(&expected_output).unwrap()); + .stderr(predicate::str::is_match(&expected_output).unwrap()); let output = pinchy.wait(); Assert::new(output) @@ -628,7 +629,7 @@ fn recvfrom_syscall() { // std::io::stderr().write_all(&output.stdout).unwrap(); Assert::new(output) .success() - .stdout(predicate::str::is_match(&expected_output).unwrap()); + .stderr(predicate::str::is_match(&expected_output).unwrap()); // Server output - has to be at the end, since we kill the server for waiting. let output = pinchy.wait(); @@ -668,7 +669,7 @@ fn identity_syscalls() { // std::io::stderr().write_all(&output.stdout).unwrap(); Assert::new(output) .success() - .stdout(predicate::str::is_match(&expected_output).unwrap()); + .stderr(predicate::str::is_match(&expected_output).unwrap()); // Server output - has to be at the end, since we kill the server for waiting. let output = pinchy.wait(); @@ -697,7 +698,7 @@ fn mmap_syscalls() { @PID@ munmap(addr: @ADDR@, length: 4096) = 0 (success) @PID@ munmap(addr: 0x12345000, length: 4096) = 0 (success) @PID@ munmap(addr: 0x0, length: 4096) = 0 (success) - @PID@ munmap(addr: 0x1000, length: 0) = -22 (error) + @PID@ munmap(addr: 0x1000, length: 0) = -22 (EINVAL: Invalid argument) "#}); let output = handle.join().unwrap(); @@ -707,7 +708,7 @@ fn mmap_syscalls() { // std::io::stderr().write_all(&output.stdout).unwrap(); Assert::new(output) .success() - .stdout(predicate::str::is_match(&expected_output).unwrap()); + .stderr(predicate::str::is_match(&expected_output).unwrap()); // Server output - has to be at the end, since we kill the server for waiting. let output = pinchy.wait(); @@ -741,7 +742,7 @@ fn memfd_create_syscall() { // std::io::stderr().write_all(&output.stdout).unwrap(); Assert::new(output) .success() - .stdout(predicate::str::is_match(&expected_output).unwrap()); + .stderr(predicate::str::is_match(&expected_output).unwrap()); // Server output - has to be at the end, since we kill the server for waiting. let output = pinchy.wait(); @@ -762,7 +763,7 @@ fn madvise_syscall() { @PID@ madvise(addr: @ADDR@, length: 4096, advice: MADV_WILLNEED (3)) = 0 (success) @PID@ madvise(addr: @ADDR@, length: 4096, advice: MADV_DONTNEED (4)) = 0 (success) @PID@ madvise(addr: @ADDR@, length: 4096, advice: MADV_NORMAL (0)) = 0 (success) - @PID@ madvise(addr: 0x0, length: 4096, advice: MADV_WILLNEED (3)) = -12 (error) + @PID@ madvise(addr: 0x0, length: 4096, advice: MADV_WILLNEED (3)) = -12 (ENOMEM: Cannot allocate memory) "#}); let output = handle.join().unwrap(); @@ -772,7 +773,7 @@ fn madvise_syscall() { // std::io::stderr().write_all(&output.stdout).unwrap(); Assert::new(output) .success() - .stdout(predicate::str::is_match(&expected_output).unwrap()); + .stderr(predicate::str::is_match(&expected_output).unwrap()); // Server output - has to be at the end, since we kill the server for waiting. let output = pinchy.wait(); @@ -801,7 +802,7 @@ fn mlock_syscalls() { // std::io::stderr().write_all(&output.stdout).unwrap(); Assert::new(output) .success() - .stdout(predicate::str::is_match(&expected_output).unwrap()); + .stderr(predicate::str::is_match(&expected_output).unwrap()); // Server output - has to be at the end, since we kill the server for waiting. let output = pinchy.wait(); @@ -852,7 +853,7 @@ fn file_descriptor_syscalls() { // std::io::stderr().write_all(&output.stdout).unwrap(); Assert::new(output) .success() - .stdout(predicate::str::is_match(&expected_output).unwrap()); + .stderr(predicate::str::is_match(&expected_output).unwrap()); // Server output - has to be at the end, since we kill the server for waiting. let output = pinchy.wait(); @@ -887,7 +888,7 @@ fn session_process_syscalls() { // std::io::stderr().write_all(&output.stdout).unwrap(); Assert::new(output) .success() - .stdout(predicate::str::is_match(&expected_output).unwrap()); + .stderr(predicate::str::is_match(&expected_output).unwrap()); // Server output - has to be at the end, since we kill the server for waiting. let output = pinchy.wait(); @@ -931,7 +932,7 @@ fn uid_gid_syscalls() { // std::io::stderr().write_all(&output.stdout).unwrap(); Assert::new(output) .success() - .stdout(predicate::str::is_match(&expected_output).unwrap()); + .stderr(predicate::str::is_match(&expected_output).unwrap()); // Server output - has to be at the end, since we kill the server for waiting. let output = pinchy.wait(); @@ -961,7 +962,7 @@ fn system_operations() { // std::io::stderr().write_all(&output.stdout).unwrap(); Assert::new(output) .success() - .stdout(predicate::str::is_match(&expected_output).unwrap()); + .stderr(predicate::str::is_match(&expected_output).unwrap()); // Server output - has to be at the end, since we kill the server for waiting. let output = pinchy.wait(); @@ -990,7 +991,7 @@ fn uname_sysinfo_syscalls() { // std::io::stderr().write_all(&output.stdout).unwrap(); Assert::new(output) .success() - .stdout(predicate::str::is_match(&expected_output).unwrap()); + .stderr(predicate::str::is_match(&expected_output).unwrap()); // Server output - has to be at the end, since we kill the server for waiting. let output = pinchy.wait(); @@ -1027,7 +1028,7 @@ fn prctl_syscalls() { // std::io::stderr().write_all(&output.stdout).unwrap(); Assert::new(output) .success() - .stdout(predicate::str::is_match(&expected_output).unwrap()); + .stderr(predicate::str::is_match(&expected_output).unwrap()); // Server output - has to be at the end, since we kill the server for waiting. let output = pinchy.wait(); @@ -1056,7 +1057,7 @@ fn ioprio_syscalls() { // std::io::stderr().write_all(&output.stdout).unwrap(); Assert::new(output) .success() - .stdout(predicate::str::is_match(&expected_output).unwrap()); + .stderr(predicate::str::is_match(&expected_output).unwrap()); // Server output - has to be at the end, since we kill the server for waiting. let output = pinchy.wait(); @@ -1089,7 +1090,7 @@ fn scheduler_syscalls() { // std::io::stderr().write_all(&output.stdout).unwrap(); Assert::new(output) .success() - .stdout(predicate::str::is_match(&expected_output).unwrap()); + .stderr(predicate::str::is_match(&expected_output).unwrap()); // Server output - has to be at the end, since we kill the server for waiting. let output = pinchy.wait(); @@ -1123,7 +1124,7 @@ fn pread_pwrite_syscalls() { // std::io::stderr().write_all(&output.stdout).unwrap(); Assert::new(output) .success() - .stdout(predicate::str::is_match(&expected_output).unwrap()); + .stderr(predicate::str::is_match(&expected_output).unwrap()); // Server output - has to be at the end, since we kill the server for waiting. let output = pinchy.wait(); @@ -1152,7 +1153,7 @@ fn readv_writev_syscalls() { // std::io::stderr().write_all(&output.stdout).unwrap(); Assert::new(output) .success() - .stdout(predicate::str::is_match(&expected_output).unwrap()); + .stderr(predicate::str::is_match(&expected_output).unwrap()); // Server output - has to be at the end, since we kill the server for waiting. let output = pinchy.wait(); @@ -1195,7 +1196,7 @@ fn socket_lifecycle_syscalls() { // std::io::stderr().write_all(&output.stdout).unwrap(); Assert::new(output) .success() - .stdout(predicate::str::is_match(&expected_output).unwrap()); + .stderr(predicate::str::is_match(&expected_output).unwrap()); // Server output - has to be at the end, since we kill the server for waiting. let output = pinchy.wait(); @@ -1224,7 +1225,7 @@ fn accept_syscall() { // std::io::stderr().write_all(&output.stdout).unwrap(); Assert::new(output) .success() - .stdout(predicate::str::is_match(&expected_output).unwrap()); + .stderr(predicate::str::is_match(&expected_output).unwrap()); // Server output - has to be at the end, since we kill the server for waiting. let output = pinchy.wait(); @@ -1250,7 +1251,7 @@ fn pselect6_syscall() { let output = handle.join().unwrap(); Assert::new(output) .success() - .stdout(predicate::str::is_match(&expected_output).unwrap()); + .stderr(predicate::str::is_match(&expected_output).unwrap()); // Server output - has to be at the end, since we kill the server for waiting. let output = pinchy.wait(); @@ -1302,7 +1303,7 @@ fn eventfd_syscalls() { // std::io::stderr().write_all(&output.stdout).unwrap(); Assert::new(output) .success() - .stdout(predicate::str::is_match(&expected_output).unwrap()); + .stderr(predicate::str::is_match(&expected_output).unwrap()); // Server output - has to be at the end, since we kill the server for waiting. let output = pinchy.wait(); @@ -1320,7 +1321,7 @@ fn execveat_syscall() { // Expected output - we should see execveat calls with different arguments and flags let expected_output = escaped_regex(indoc! {r#" - @PID@ execveat(dirfd: @NUMBER@, pathname: "this-does-not-exist-for-sure", argv: [this-doe, arg1, arg2], envp: [@ALPHANUM@, @ALPHANUM@], flags: 0) = -2 (error) + @PID@ execveat(dirfd: @NUMBER@, pathname: "this-does-not-exist-for-sure", argv: [this-doe, arg1, arg2], envp: [@ALPHANUM@, @ALPHANUM@], flags: 0) = -2 (ENOENT: No such file or directory) "#}); let output = handle.join().unwrap(); @@ -1330,7 +1331,7 @@ fn execveat_syscall() { // std::io::stderr().write_all(&output.stdout).unwrap(); Assert::new(output) .success() - .stdout(predicate::str::is_match(&expected_output).unwrap()); + .stderr(predicate::str::is_match(&expected_output).unwrap()); // Server output - has to be at the end, since we kill the server for waiting. let output = pinchy.wait(); @@ -1366,7 +1367,7 @@ fn xattr_syscalls() { @PID@ listxattr(pathname: "/tmp/xattr_test_file", list: [ @MAYBEITEM_@@ALPHANUM@, @ALPHANUM@ ], size: 256) = @NUMBER@ @PID@ flistxattr(fd: @NUMBER@, list: [ @MAYBEITEM_@@ALPHANUM@, @ALPHANUM@ ], size: 256) = @NUMBER@ @PID@ getxattr(pathname: "/tmp/xattr_test_file", name: "user.test_attr1", value: "@ALPHANUM@", size: 0) = 12 - @PID@ getxattr(pathname: "/tmp/xattr_test_file", name: "user.nonexistent", value: @ADDR@, size: 64) = -61 (error) + @PID@ getxattr(pathname: "/tmp/xattr_test_file", name: "user.nonexistent", value: @ADDR@, size: 64) = -61 (ENODATA: No data available) "#}); let output = handle.join().unwrap(); @@ -1376,7 +1377,7 @@ fn xattr_syscalls() { // std::io::stderr().write_all(&output.stdout).unwrap(); Assert::new(output) .success() - .stdout(predicate::str::is_match(&expected_output).unwrap()); + .stderr(predicate::str::is_match(&expected_output).unwrap()); // Server output - has to be at the end, since we kill the server for waiting. let output = pinchy.wait(); @@ -1425,7 +1426,7 @@ fn sysv_ipc_syscalls() { // std::io::stderr().write_all(&output.stdout).unwrap(); Assert::new(output) .success() - .stdout(predicate::str::is_match(&expected_output).unwrap()); + .stderr(predicate::str::is_match(&expected_output).unwrap()); // Server output - has to be at the end, since we kill the server for waiting. let output = pinchy.wait(); @@ -1461,7 +1462,7 @@ fn socketpair_sendmmsg_syscalls() { // std::io::stderr().write_all(&output.stdout).unwrap(); Assert::new(output) .success() - .stdout(predicate::str::is_match(&expected_output).unwrap()); + .stderr(predicate::str::is_match(&expected_output).unwrap()); // Server output - has to be at the end, since we kill the server for waiting. let output = pinchy.wait(); @@ -1499,7 +1500,7 @@ fn timer_test() { let output = handle.join().unwrap(); Assert::new(output) .success() - .stdout(predicate::str::is_match(&expected_output).unwrap()); + .stderr(predicate::str::is_match(&expected_output).unwrap()); let output = pinchy.wait(); Assert::new(output) @@ -1536,7 +1537,7 @@ fn timerfd_test() { let output = handle.join().unwrap(); Assert::new(output) .success() - .stdout(predicate::str::is_match(&expected_output).unwrap()); + .stderr(predicate::str::is_match(&expected_output).unwrap()); let output = pinchy.wait(); Assert::new(output) @@ -1554,7 +1555,7 @@ fn ioctl_syscalls() { // Expected output - we should see ioctl calls with different requests let expected_output = escaped_regex(indoc! {r#" @PID@ ioctl(fd: 3, request: (0x541b) tty::FIONREAD, arg: @ADDR@) = 0 (success) - @PID@ ioctl(fd: 3, request: (0xdeadbeef) other::unknown, arg: @ADDR@) = -25 (error) + @PID@ ioctl(fd: 3, request: (0xdeadbeef) other::unknown, arg: @ADDR@) = -25 (ENOTTY: Inappropriate ioctl for device) @PID@ ioctl(fd: 3, request: (0x5451) tty::FIOCLEX, arg: 0x0) = 0 (success) "#}); @@ -1565,7 +1566,7 @@ fn ioctl_syscalls() { // std::io::stderr().write_all(&output.stdout).unwrap(); Assert::new(output) .success() - .stdout(predicate::str::is_match(&expected_output).unwrap()); + .stderr(predicate::str::is_match(&expected_output).unwrap()); // Server output - has to be at the end, since we kill the server for waiting. let output = pinchy.wait(); @@ -1600,8 +1601,8 @@ fn filesystem_links_syscalls() { @PID@ readlinkat(dirfd: AT_FDCWD, pathname: "/tmp/filesystem_links_symlink", buf: "/tmp/filesystem_links_target", bufsiz: 256) = 28 @PID@ linkat(olddirfd: AT_FDCWD, oldpath: "/tmp/filesystem_links_target", newdirfd: AT_FDCWD, newpath: "/tmp/filesystem_links_hardlink", flags: 0) = 0 (success) @PID@ link(oldpath: "/tmp/filesystem_links_target", newpath: "/tmp/filesystem_links_link2") = 0 (success) - @PID@ readlinkat(dirfd: AT_FDCWD, pathname: "/tmp/filesystem_links_nonexistent", buf: , bufsiz: 256) = -2 (error) - @PID@ linkat(olddirfd: AT_FDCWD, oldpath: "/tmp/filesystem_links_nonexisten"@MAYBETRUNCATED@, newdirfd: AT_FDCWD, newpath: "/tmp/filesystem_links_error_link"@MAYBETRUNCATED@, flags: 0) = -2 (error) + @PID@ readlinkat(dirfd: AT_FDCWD, pathname: "/tmp/filesystem_links_nonexistent", buf: , bufsiz: 256) = -2 (ENOENT: No such file or directory) + @PID@ linkat(olddirfd: AT_FDCWD, oldpath: "/tmp/filesystem_links_nonexisten"@MAYBETRUNCATED@, newdirfd: AT_FDCWD, newpath: "/tmp/filesystem_links_error_link"@MAYBETRUNCATED@, flags: 0) = -2 (ENOENT: No such file or directory) "#}); #[cfg(target_arch = "aarch64")] @@ -1609,8 +1610,8 @@ fn filesystem_links_syscalls() { @PID@ symlinkat(target: "/tmp/filesystem_links_target", newdirfd: AT_FDCWD, linkpath: "/tmp/filesystem_links_symlink") = 0 (success) @PID@ readlinkat(dirfd: AT_FDCWD, pathname: "/tmp/filesystem_links_symlink", buf: "/tmp/filesystem_links_target", bufsiz: 256) = 28 @PID@ linkat(olddirfd: AT_FDCWD, oldpath: "/tmp/filesystem_links_target", newdirfd: AT_FDCWD, newpath: "/tmp/filesystem_links_hardlink", flags: 0) = 0 (success) - @PID@ readlinkat(dirfd: AT_FDCWD, pathname: "/tmp/filesystem_links_nonexistent", buf: , bufsiz: 256) = -2 (error) - @PID@ linkat(olddirfd: AT_FDCWD, oldpath: "/tmp/filesystem_links_nonexisten"@MAYBETRUNCATED@, newdirfd: AT_FDCWD, newpath: "/tmp/filesystem_links_error_link"@MAYBETRUNCATED@, flags: 0) = -2 (error) + @PID@ readlinkat(dirfd: AT_FDCWD, pathname: "/tmp/filesystem_links_nonexistent", buf: , bufsiz: 256) = -2 (ENOENT: No such file or directory) + @PID@ linkat(olddirfd: AT_FDCWD, oldpath: "/tmp/filesystem_links_nonexisten"@MAYBETRUNCATED@, newdirfd: AT_FDCWD, newpath: "/tmp/filesystem_links_error_link"@MAYBETRUNCATED@, flags: 0) = -2 (ENOENT: No such file or directory) "#}); let output = handle.join().unwrap(); @@ -1620,7 +1621,7 @@ fn filesystem_links_syscalls() { // std::io::stderr().write_all(&output.stdout).unwrap(); Assert::new(output) .success() - .stdout(predicate::str::is_match(&expected_output).unwrap()); + .stderr(predicate::str::is_match(&expected_output).unwrap()); // Server output - has to be at the end, since we kill the server for waiting. let output = pinchy.wait(); @@ -1653,8 +1654,8 @@ fn aio_syscalls() { @PID@ io_setup(nr_events: 128, ctx_idp: @ADDR@) = 0 (success) @PID@ io_submit(ctx_id: @ADDR@, nr: 1, iocbpp: @ADDR@, iocbs: [ iocb { data: @ADDR@, key: @NUMBER@, rw_flags: @NUMBER@, lio_opcode: IOCB_CMD_PREAD, reqprio: @NUMBER@, fildes: @NUMBER@, buf: @ADDR@, nbytes: 64, offset: 0, flags: @NUMBER@ } ]) = 1 (requests) @PID@ io_getevents(ctx_id: @ADDR@, min_nr: 1, nr: 2, events: @ADDR@, timeout: { secs: 1, nanos: 0 }, events_returned: [ event { data: @ADDR@, obj: @ADDR@, res: @SIGNEDNUMBER@, res2: @SIGNEDNUMBER@ } ]) = 1 (events) - @PID@ io_pgetevents(ctx_id: @ADDR@, min_nr: 0, nr: 2, events: @ADDR@, timeout: { secs: 1, nanos: 0 }, usig: { sigmask: @ADDR@, sigsetsize: @NUMBER@, sigset: [] }) = -@NUMBER@ (error) - @PID@ io_cancel(ctx_id: @ADDR@, iocb: @ADDR@, result: @ADDR@) = -@NUMBER@ (error) + @PID@ io_pgetevents(ctx_id: @ADDR@, min_nr: 0, nr: 2, events: @ADDR@, timeout: { secs: 1, nanos: 0 }, usig: { sigmask: @ADDR@, sigsetsize: @NUMBER@, sigset: [] }) = @ERRNO@ + @PID@ io_cancel(ctx_id: @ADDR@, iocb: @ADDR@, result: @ADDR@) = @ERRNO@ @PID@ io_destroy(ctx_id: @ADDR@) = 0 (success) "#}); @@ -1665,7 +1666,7 @@ fn aio_syscalls() { // std::io::stderr().write_all(&output.stdout).unwrap(); Assert::new(output) .success() - .stdout(predicate::str::is_match(&expected_output).unwrap()); + .stderr(predicate::str::is_match(&expected_output).unwrap()); // Server output - has to be at the end, since we kill the server for waiting. let output = pinchy.wait(); @@ -1697,10 +1698,10 @@ fn landlock_syscalls() { // 5. landlock_restrict_self let expected_output = escaped_regex(indoc! {r#" @PID@ landlock_create_ruleset(attr: 0x0, size: 0, flags: 0x1 (LANDLOCK_CREATE_RULESET_VERSION)) = @NUMBER@ (fd) - @PID@ landlock_create_ruleset(attr: @ADDR@, size: @NUMBER@, flags: 0) = -@NUMBER@ (error) - @PID@ landlock_add_rule(ruleset_fd: -1, rule_type: LANDLOCK_RULE_PATH_BENEATH, parent_fd: 4, allowed_access: 0x3f (EXECUTE|WRITE_FILE|READ_FILE|READ_DIR|REMOVE_DIR|REMOVE_FILE), flags: 0) = -@NUMBER@ (error) - @PID@ landlock_add_rule(ruleset_fd: -1, rule_type: LANDLOCK_RULE_NET_PORT, port: 8080, access_rights: 0x3 (BIND_TCP|CONNECT_TCP), flags: 0) = -@NUMBER@ (error) - @PID@ landlock_restrict_self(ruleset_fd: -1, flags: 0) = -@NUMBER@ (error) + @PID@ landlock_create_ruleset(attr: @ADDR@, size: @NUMBER@, flags: 0) = @ERRNO@ + @PID@ landlock_add_rule(ruleset_fd: -1, rule_type: LANDLOCK_RULE_PATH_BENEATH, parent_fd: 4, allowed_access: 0x3f (EXECUTE|WRITE_FILE|READ_FILE|READ_DIR|REMOVE_DIR|REMOVE_FILE), flags: 0) = @ERRNO@ + @PID@ landlock_add_rule(ruleset_fd: -1, rule_type: LANDLOCK_RULE_NET_PORT, port: 8080, access_rights: 0x3 (BIND_TCP|CONNECT_TCP), flags: 0) = @ERRNO@ + @PID@ landlock_restrict_self(ruleset_fd: -1, flags: 0) = @ERRNO@ "#}); let output = handle.join().unwrap(); @@ -1710,7 +1711,7 @@ fn landlock_syscalls() { // std::io::stderr().write_all(&output.stdout).unwrap(); Assert::new(output) .success() - .stdout(predicate::str::is_match(&expected_output).unwrap()); + .stderr(predicate::str::is_match(&expected_output).unwrap()); // Server output - has to be at the end, since we kill the server for waiting. let output = pinchy.wait(); @@ -1754,7 +1755,7 @@ fn posix_mq_syscalls() { @PID@ mq_notify(mqdes: @NUMBER@, sevp: @ADDR@) = 0 (success) @PID@ mq_notify(mqdes: @NUMBER@, sevp: 0x0) = 0 (success) @PID@ mq_unlink(name: @ADDR@) = 0 (success) - @PID@ mq_open(name: @ADDR@, flags: 0x0 (O_RDONLY), mode: 0, attr: NULL) = -@NUMBER@ (error) + @PID@ mq_open(name: @ADDR@, flags: 0x0 (O_RDONLY), mode: 0, attr: NULL) = @ERRNO@ "#}); let output = handle.join().unwrap(); @@ -1764,7 +1765,7 @@ fn posix_mq_syscalls() { // std::io::stderr().write_all(&output.stdout).unwrap(); Assert::new(output) .success() - .stdout(predicate::str::is_match(&expected_output).unwrap()); + .stderr(predicate::str::is_match(&expected_output).unwrap()); // Server output - has to be at the end, since we kill the server for waiting. let output = pinchy.wait(); @@ -1805,7 +1806,7 @@ fn key_management_syscalls() { // std::io::stderr().write_all(&output.stdout).unwrap(); Assert::new(output) .success() - .stdout(predicate::str::is_match(&expected_output).unwrap()); + .stderr(predicate::str::is_match(&expected_output).unwrap()); // Server output - has to be at the end, since we kill the server for waiting. let output = pinchy.wait(); @@ -1825,13 +1826,13 @@ fn perf_event_syscalls() { let expected_output = escaped_regex(indoc! {r#" @PID@ perf_event_open(attr: { type: PERF_TYPE_SOFTWARE, size: @NUMBER@, config: 0x0, sample_period: 0 }, pid: 0, cpu: -1, group_fd: -1, flags: 0) = @NUMBER@ (fd) @PID@ perf_event_open(attr: { type: PERF_TYPE_SOFTWARE, size: @NUMBER@, config: 0x0, sample_period: 0 }, pid: 0, cpu: -1, group_fd: -1, flags: 0x8 (FD_CLOEXEC)) = @NUMBER@ (fd) - @PID@ perf_event_open(attr: { type: PERF_TYPE_HARDWARE, size: 0, config: 0x0, sample_period: 0 }, pid: 0, cpu: -1, group_fd: -1, flags: 0) = -@NUMBER@ (error) + @PID@ perf_event_open(attr: { type: PERF_TYPE_HARDWARE, size: 0, config: 0x0, sample_period: 0 }, pid: 0, cpu: -1, group_fd: -1, flags: 0) = @ERRNO@ "#}); let output = handle.join().unwrap(); Assert::new(output) .success() - .stdout(predicate::str::is_match(&expected_output).unwrap()); + .stderr(predicate::str::is_match(&expected_output).unwrap()); let output = pinchy.wait(); Assert::new(output) @@ -1857,7 +1858,7 @@ fn bpf_syscalls() { let output = handle.join().unwrap(); Assert::new(output) .success() - .stdout(predicate::str::is_match(&expected_output).unwrap()); + .stderr(predicate::str::is_match(&expected_output).unwrap()); let output = pinchy.wait(); Assert::new(output) @@ -1880,13 +1881,13 @@ fn fanotify_syscalls() { let expected_output = escaped_regex(indoc! {r#" @PID@ fanotify_init(flags: @ANY@, event_f_flags: @ANY@) = @NUMBER@ (fd) @PID@ fanotify_mark(fanotify_fd: @NUMBER@, flags: @ANY@, mask: @ANY@, dirfd: AT_FDCWD, pathname: "/tmp") = 0 (success) - @PID@ fanotify_init(flags: @ANY@, event_f_flags: @ANY@) = -@NUMBER@ (error) + @PID@ fanotify_init(flags: @ANY@, event_f_flags: @ANY@) = @ERRNO@ "#}); let output = handle.join().unwrap(); Assert::new(output) .success() - .stdout(predicate::str::is_match(&expected_output).unwrap()); + .stderr(predicate::str::is_match(&expected_output).unwrap()); let output = pinchy.wait(); Assert::new(output) @@ -1921,7 +1922,7 @@ fn file_handles_syscalls() { @PID@ utimensat(dirfd: AT_FDCWD, pathname: "/tmp/pinchy_file_handles_test.txt", times: @ANY@, flags: 0) = 0 (success) @PID@ utimensat(dirfd: AT_FDCWD, pathname: "/tmp/pinchy_file_handles_test.txt", times: NULL, flags: 0) = 0 (success) @PID@ name_to_handle_at(dirfd: AT_FDCWD, pathname: "/tmp/pinchy_file_handles_test.txt", handle: @ADDR@, mount_id: @ADDR@, flags: 0) = 0 (success) - @PID@ open_by_handle_at(mount_fd: AT_FDCWD, handle: @ADDR@, flags: @ANY@) = @SIGNEDNUMBER@ (error) + @PID@ open_by_handle_at(mount_fd: AT_FDCWD, handle: @ADDR@, flags: @ANY@) = @ERRNO@ "#}); let output = handle.join().unwrap(); @@ -1931,7 +1932,7 @@ fn file_handles_syscalls() { // std::io::stderr().write_all(&output.stdout).unwrap(); Assert::new(output) .success() - .stdout(predicate::str::is_match(&expected_output).unwrap()); + .stderr(predicate::str::is_match(&expected_output).unwrap()); let output = pinchy.wait(); Assert::new(output) @@ -1955,7 +1956,7 @@ fn itimer_test() { let output = handle.join().unwrap(); Assert::new(output) .success() - .stdout(predicate::str::is_match(&expected_output).unwrap()); + .stderr(predicate::str::is_match(&expected_output).unwrap()); let output = pinchy.wait(); Assert::new(output) @@ -1982,7 +1983,7 @@ fn syslog_test() { let output = handle.join().unwrap(); Assert::new(output) .success() - .stdout(predicate::str::is_match(&expected_output).unwrap()); + .stderr(predicate::str::is_match(&expected_output).unwrap()); let output = pinchy.wait(); Assert::new(output) @@ -1998,14 +1999,14 @@ fn ptrace_test() { let expected_output = escaped_regex(indoc! {r#" @PID@ ptrace(request: PTRACE_TRACEME, pid: 0, addr: @ADDR@, data: @ADDR@) = 0 (success) - @PID@ ptrace(request: PTRACE_PEEKTEXT, pid: @NUMBER@, addr: @ADDR@, data: @ADDR@) = @SIGNEDNUMBER@ (error) (data ptr) - @PID@ ptrace(request: PTRACE_CONT, pid: 1, addr: @ADDR@, sig: @ALPHANUM@) = @SIGNEDNUMBER@ (error) + @PID@ ptrace(request: PTRACE_PEEKTEXT, pid: @NUMBER@, addr: @ADDR@, data: @ADDR@) = @ERRNO@ (data ptr) + @PID@ ptrace(request: PTRACE_CONT, pid: 1, addr: @ADDR@, sig: @ALPHANUM@) = @ERRNO@ "#}); let output = handle.join().unwrap(); Assert::new(output) .success() - .stdout(predicate::str::is_match(&expected_output).unwrap()); + .stderr(predicate::str::is_match(&expected_output).unwrap()); let output = pinchy.wait(); Assert::new(output) @@ -2022,13 +2023,13 @@ fn seccomp_test() { let expected_output = escaped_regex(indoc! {r#" @PID@ seccomp(operation: SECCOMP_GET_ACTION_AVAIL, flags: 0, action: SECCOMP_RET_KILL_THREAD) = 0 (success) @PID@ seccomp(operation: SECCOMP_GET_NOTIF_SIZES, flags: 0, sizes: {notif: @NUMBER@, resp: @NUMBER@, data: @NUMBER@}) = 0 (success) - @PID@ seccomp(operation: 255, flags: 0, args: NULL) = @SIGNEDNUMBER@ (error) + @PID@ seccomp(operation: 255, flags: 0, args: NULL) = @ERRNO@ "#}); let output = handle.join().unwrap(); Assert::new(output) .success() - .stdout(predicate::str::is_match(&expected_output).unwrap()); + .stderr(predicate::str::is_match(&expected_output).unwrap()); let output = pinchy.wait(); Assert::new(output) @@ -2043,15 +2044,15 @@ fn quotactl_test() { let handle = run_workload(&pinchy, &["quotactl"], "quotactl_test"); let expected_output = escaped_regex(indoc! {r#" - @PID@ quotactl(op: 0x800001 (QCMD(Q_SYNC, USRQUOTA)), special: "", id: 0, addr: @ADDR@) = @SIGNEDNUMBER@ (error) - @PID@ quotactl(op: 0x800004 (QCMD(Q_GETFMT, USRQUOTA)), special: "/", id: 0, addr: @ADDR@) = @SIGNEDNUMBER@ (error) - @PID@ quotactl(op: 0x800007 (QCMD(Q_GETQUOTA, USRQUOTA)), special: "/", id: 1000, addr: @ADDR@) = @SIGNEDNUMBER@ (error) + @PID@ quotactl(op: 0x800001 (QCMD(Q_SYNC, USRQUOTA)), special: "", id: 0, addr: @ADDR@) = @ERRNO@ + @PID@ quotactl(op: 0x800004 (QCMD(Q_GETFMT, USRQUOTA)), special: "/", id: 0, addr: @ADDR@) = @ERRNO@ + @PID@ quotactl(op: 0x800007 (QCMD(Q_GETQUOTA, USRQUOTA)), special: "/", id: 1000, addr: @ADDR@) = @ERRNO@ "#}); let output = handle.join().unwrap(); Assert::new(output) .success() - .stdout(predicate::str::is_match(&expected_output).unwrap()); + .stderr(predicate::str::is_match(&expected_output).unwrap()); let output = pinchy.wait(); Assert::new(output) @@ -2079,7 +2080,7 @@ fn process_identity_test() { let output = handle.join().unwrap(); Assert::new(output) .success() - .stdout(predicate::str::is_match(&expected_output).unwrap()); + .stderr(predicate::str::is_match(&expected_output).unwrap()); let output = pinchy.wait(); Assert::new(output) @@ -2129,7 +2130,6 @@ fn auto_quit_after_client() { let output = pinchy.wait(); Assert::new(output) .success() - .stdout(predicate::str::contains("Currently serving: 0")) .stdout(predicate::str::contains( "Pinchy has been idle for a while, shutting down", )) @@ -2226,7 +2226,7 @@ fn signal_alias_sigprocmask() { let output = handle.join().unwrap(); Assert::new(output) .success() - .stdout(predicate::str::is_match(&expected_output).unwrap()); + .stderr(predicate::str::is_match(&expected_output).unwrap()); let output = pinchy.wait(); Assert::new(output) @@ -2251,7 +2251,7 @@ fn signal_alias_sigaction() { let output = handle.join().unwrap(); Assert::new(output) .success() - .stdout(predicate::str::is_match(&expected_output).unwrap()); + .stderr(predicate::str::is_match(&expected_output).unwrap()); let output = pinchy.wait(); Assert::new(output) @@ -2279,7 +2279,7 @@ fn aarch64_alias_open() { let output = handle.join().unwrap(); Assert::new(output) .success() - .stdout(predicate::str::is_match(&expected_output).unwrap()); + .stderr(predicate::str::is_match(&expected_output).unwrap()); let output = pinchy.wait(); Assert::new(output) @@ -2304,7 +2304,7 @@ fn aarch64_alias_stat() { let output = handle.join().unwrap(); Assert::new(output) .success() - .stdout(predicate::str::is_match(&expected_output).unwrap()); + .stderr(predicate::str::is_match(&expected_output).unwrap()); let output = pinchy.wait(); Assert::new(output) @@ -2330,7 +2330,7 @@ fn mixed_aliases_and_canonical() { let output = handle.join().unwrap(); Assert::new(output) .success() - .stdout(predicate::str::is_match(&expected_output).unwrap()); + .stderr(predicate::str::is_match(&expected_output).unwrap()); let output = pinchy.wait(); Assert::new(output) diff --git a/uml-kernel/uml-test-runner.sh b/uml-kernel/uml-test-runner.sh index 72c6be83..690e6c72 100755 --- a/uml-kernel/uml-test-runner.sh +++ b/uml-kernel/uml-test-runner.sh @@ -170,12 +170,18 @@ PINCHYD_OUT="$OUTDIR/pinchyd.out" wait_for_pinchyd() { for i in $(seq 1 60); do - if grep -q "Waiting for Ctrl-C..." "$PINCHYD_OUT" 2>/dev/null; then + if grep -q "Pinchy daemon ready" "$PINCHYD_OUT" 2>/dev/null; then return 0 fi if ! kill -0 $PINCHYD_PID 2>/dev/null; then - echo "pinchyd exited prematurely" >"$OUTDIR/pinchy.stderr" + # Include the daemon's output so startup failures are diagnosable + # from the test report. + { + echo "pinchyd exited prematurely" + echo "--- pinchyd output ---" + cat "$PINCHYD_OUT" 2>/dev/null + } >"$OUTDIR/pinchy.stderr" echo "1" >"$OUTDIR/pinchyd.exit" echo "1" >"$OUTDIR/pinchy.exit" touch "$OUTDIR/done" @@ -456,8 +462,8 @@ run_latency_probe() { first_print_seen=0 while kill -0 "$WORKLOAD_PID" 2>/dev/null; do - if [ "$first_print_seen" -eq 0 ] && [ -f "$OUTDIR/pinchy.stdout" ]; then - if grep -q 'pathname: "/etc/passwd"' "$OUTDIR/pinchy.stdout"; then + if [ "$first_print_seen" -eq 0 ] && [ -f "$OUTDIR/pinchy.stderr" ]; then + if grep -q 'pathname: "/etc/passwd"' "$OUTDIR/pinchy.stderr"; then date +%s%N >"$OUTDIR/latency_first_print_ns" first_print_seen=1 fi @@ -470,8 +476,8 @@ run_latency_probe() { date +%s%N >"$OUTDIR/latency_workload_exit_ns" - if [ "$first_print_seen" -eq 0 ] && [ -f "$OUTDIR/pinchy.stdout" ]; then - if grep -q 'pathname: "/etc/passwd"' "$OUTDIR/pinchy.stdout"; then + if [ "$first_print_seen" -eq 0 ] && [ -f "$OUTDIR/pinchy.stderr" ]; then + if grep -q 'pathname: "/etc/passwd"' "$OUTDIR/pinchy.stderr"; then date +%s%N >"$OUTDIR/latency_first_print_ns" first_print_seen=1 fi