Skip to main content

aya_friday/programs/
iter.rs

1//! Iterators.
2use std::{
3    fs::File,
4    os::fd::{AsFd, BorrowedFd},
5};
6
7use aya_obj::{
8    btf::{Btf, BtfKind},
9    generated::{
10        bpf_attach_type::BPF_TRACE_ITER, bpf_link_type::BPF_LINK_TYPE_ITER,
11        bpf_prog_type::BPF_PROG_TYPE_TRACING,
12    },
13};
14
15use crate::{
16    programs::{
17        FdLink, LinkError, PerfLinkIdInner, PerfLinkInner, ProgramData, ProgramError, ProgramType,
18        define_link_wrapper, impl_try_from_fdlink, impl_try_into_fdlink,
19        load_program_with_attach_type,
20    },
21    sys::{LinkTarget, SyscallError, bpf_create_iter, bpf_link_create},
22};
23
24/// A BPF iterator which allows to dump data from the kernel-space into the
25/// user-space.
26///
27/// It can be seen as an alternative to `/proc` filesystem as it offers more
28/// flexibility about what information should be retrieved and how it should be
29/// formatted.
30///
31/// # Minimum kernel version
32///
33/// The minimum kernel version required to use this feature is 5.8.
34///
35/// # Example
36///
37/// ```no_run
38/// use std::io::{BufRead, BufReader};
39/// use aya::{programs::{Iter, ProgramError}, BtfError, Btf, Ebpf};
40/// # let mut ebpf = Ebpf::load_file("ebpf_programs.o")?;
41///
42/// let btf = Btf::from_sys_fs()?;
43/// let program: &mut Iter = ebpf.program_mut("iter_prog").unwrap().try_into()?;
44/// program.load("task", &btf)?;
45///
46/// let link_id = program.attach()?;
47/// let link = program.take_link(link_id)?;
48/// let file = link.into_file()?;
49/// let reader = BufReader::new(file);
50///
51/// let mut lines = reader.lines();
52/// for line in lines {
53///     let line = line?;
54///     println!("{line}");
55/// }
56/// # Ok::<(), Box<dyn std::error::Error>>(())
57/// ```
58#[derive(Debug)]
59pub struct Iter {
60    pub(crate) data: ProgramData<IterLink>,
61}
62
63impl Iter {
64    /// The type of the program according to the kernel.
65    pub const PROGRAM_TYPE: ProgramType = ProgramType::Tracing;
66
67    /// Loads the program inside the kernel.
68    pub fn load(&mut self, iter_type: &str, btf: &Btf) -> Result<(), ProgramError> {
69        let Self { data } = self;
70        let type_name = format!("bpf_iter_{iter_type}");
71        data.attach_btf_id = Some(btf.id_by_type_name_kind(type_name.as_str(), BtfKind::Func)?);
72        load_program_with_attach_type(BPF_PROG_TYPE_TRACING, BPF_TRACE_ITER, data)
73    }
74
75    /// Attaches the program.
76    ///
77    /// The returned value can be used to detach, see [`Self::detach`].
78    pub fn attach(&mut self) -> Result<IterLinkId, ProgramError> {
79        let prog_fd = self.fd()?;
80        let prog_fd = prog_fd.as_fd();
81        let link_fd = bpf_link_create(prog_fd, LinkTarget::Iter, BPF_TRACE_ITER, 0, None).map_err(
82            |io_error| SyscallError {
83                call: "bpf_link_create",
84                io_error,
85            },
86        )?;
87
88        self.data
89            .links
90            .insert(IterLink::new(PerfLinkInner::Fd(FdLink::new(link_fd))))
91    }
92}
93
94/// An iterator descriptor.
95#[derive(Debug)]
96pub struct IterFd {
97    fd: crate::MockableFd,
98}
99
100impl AsFd for IterFd {
101    fn as_fd(&self) -> BorrowedFd<'_> {
102        let Self { fd } = self;
103        fd.as_fd()
104    }
105}
106
107impl_try_into_fdlink!(IterLink, PerfLinkInner);
108impl_try_from_fdlink!(IterLink, PerfLinkInner, BPF_LINK_TYPE_ITER);
109
110define_link_wrapper!(IterLink, IterLinkId, PerfLinkInner, PerfLinkIdInner, Iter);
111
112impl IterLink {
113    /// Converts [`IterLink`] into a [`File`] that can be used to retrieve the
114    /// outputs of the iterator program.
115    pub fn into_file(self) -> Result<File, LinkError> {
116        if let PerfLinkInner::Fd(fd) = self.into_inner() {
117            let fd = bpf_create_iter(fd.fd.as_fd()).map_err(|io_error| {
118                LinkError::SyscallError(SyscallError {
119                    call: "bpf_iter_create",
120                    io_error,
121                })
122            })?;
123            Ok(fd.into_inner().into())
124        } else {
125            Err(LinkError::InvalidLink)
126        }
127    }
128}