use std::fmt::Debug; use std::os::unix::io::AsFd; use std::os::unix::io::BorrowedFd; use std::path::Path; use std::path::PathBuf; use std::ptr::NonNull; use crate::util; use crate::util::validate_bpf_ret; use crate::AsRawLibbpf; use crate::ErrorExt as _; use crate::Program; use crate::Result; /// Represents an attached [`Program`]. /// /// This struct is used to model ownership. The underlying program will be detached /// when this object is dropped if nothing else is holding a reference count. #[derive(Debug)] pub struct Link { ptr: NonNull, } impl Link { /// Create a new [`Link`] from a [`libbpf_sys::bpf_link`]. /// /// # Safety /// /// `ptr` must point to a correctly initialized [`libbpf_sys::bpf_link`]. pub(crate) unsafe fn new(ptr: NonNull) -> Self { Link { ptr } } /// Create link from BPF FS file. pub fn open>(path: P) -> Result { let path_c = util::path_to_cstring(path)?; let path_ptr = path_c.as_ptr(); let ptr = unsafe { libbpf_sys::bpf_link__open(path_ptr) }; let ptr = validate_bpf_ret(ptr).context("failed to open link")?; let slf = unsafe { Self::new(ptr) }; Ok(slf) } /// Takes ownership from pointer. /// /// # Safety /// /// It is not safe to manipulate `ptr` after this operation. pub unsafe fn from_ptr(ptr: NonNull) -> Self { unsafe { Self::new(ptr) } } /// Replace the underlying prog with `prog`. pub fn update_prog(&mut self, prog: &Program<'_>) -> Result<()> { let ret = unsafe { libbpf_sys::bpf_link__update_program(self.ptr.as_ptr(), prog.ptr.as_ptr()) }; util::parse_ret(ret) } /// Release "ownership" of underlying BPF resource (typically, a BPF program /// attached to some BPF hook, e.g., tracepoint, kprobe, etc). Disconnected /// links, when destructed through bpf_link__destroy() call won't attempt to /// detach/unregistered that BPF resource. This is useful in situations where, /// say, attached BPF program has to outlive userspace program that attached it /// in the system. Depending on type of BPF program, though, there might be /// additional steps (like pinning BPF program in BPF FS) necessary to ensure /// exit of userspace program doesn't trigger automatic detachment and clean up /// inside the kernel. pub fn disconnect(&mut self) { unsafe { libbpf_sys::bpf_link__disconnect(self.ptr.as_ptr()) } } /// [Pin](https://facebookmicrosites.github.io/bpf/blog/2018/08/31/object-lifetime.html#bpffs) /// this link to bpffs. pub fn pin>(&mut self, path: P) -> Result<()> { let path_c = util::path_to_cstring(path)?; let path_ptr = path_c.as_ptr(); let ret = unsafe { libbpf_sys::bpf_link__pin(self.ptr.as_ptr(), path_ptr) }; util::parse_ret(ret) } /// [Unpin](https://facebookmicrosites.github.io/bpf/blog/2018/08/31/object-lifetime.html#bpffs) /// from bpffs pub fn unpin(&mut self) -> Result<()> { let ret = unsafe { libbpf_sys::bpf_link__unpin(self.ptr.as_ptr()) }; util::parse_ret(ret) } /// Returns path to BPF FS file or `None` if not pinned. pub fn pin_path(&self) -> Option { let path_ptr = unsafe { libbpf_sys::bpf_link__pin_path(self.ptr.as_ptr()) }; if path_ptr.is_null() { return None; } let path = match util::c_ptr_to_string(path_ptr) { Ok(p) => p, Err(_) => return None, }; Some(PathBuf::from(path.as_str())) } /// Detach the link. pub fn detach(&self) -> Result<()> { let ret = unsafe { libbpf_sys::bpf_link__detach(self.ptr.as_ptr()) }; util::parse_ret(ret) } } impl AsRawLibbpf for Link { type LibbpfType = libbpf_sys::bpf_link; /// Retrieve the underlying [`libbpf_sys::bpf_link`]. fn as_libbpf_object(&self) -> NonNull { self.ptr } } // SAFETY: `bpf_link` objects can safely be sent to a different thread. unsafe impl Send for Link {} impl AsFd for Link { #[inline] fn as_fd(&self) -> BorrowedFd<'_> { let fd = unsafe { libbpf_sys::bpf_link__fd(self.ptr.as_ptr()) }; // SAFETY: `bpf_link__fd` always returns a valid fd and the underlying // libbpf object is not destroyed until the object is dropped, // which means the fd remains valid as well. unsafe { BorrowedFd::borrow_raw(fd) } } } impl Drop for Link { fn drop(&mut self) { let _ = unsafe { libbpf_sys::bpf_link__destroy(self.ptr.as_ptr()) }; } }