Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
39 changes: 37 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -67,6 +89,19 @@ Attach to a running process:
pinchy -p <PID> -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.
Expand Down
64 changes: 59 additions & 5 deletions pinchy-client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,49 @@ pub async fn trace_child(command: Vec<OsString>, syscalls: Vec<i64>) -> (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);
}
}

Expand Down Expand Up @@ -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.");
Expand Down Expand Up @@ -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.");
Expand Down
2 changes: 2 additions & 0 deletions pinchy-common/src/syscalls/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,5 +48,7 @@ macro_rules! declare_syscalls {
}
}
pub const ALL_SYSCALLS: &[i64] = &[$($name),*];
pub const SYSCALL_ALIASES: &[(&str, i64)] =
&[$( $( ($alias, $target) ),* )?];
};
}
2 changes: 2 additions & 0 deletions pinchy/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 = []
Expand Down
Loading
Loading