use crate::error::InvalidMessage;
use crate::netlink::{deserialize_attrs, deserialize_u32, NetlinkAttr};
use crate::parse::{check_attr_type, drop_attr, drop_attrs, parse_aggr_pid};
use crate::raw::{
TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK, TASKSTATS_CMD_ATTR_REGISTER_CPUMASK,
TASKSTATS_TYPE_AGGR_PID, TASKSTATS_TYPE_AGGR_TGID, TASKSTATS_TYPE_TGID,
};
use crate::taskstats::Taskstats;
use crate::{Error, TaskstatsConnection};
use libc::pid_t;
use log::{debug, warn};
use std::ffi::CStr;
pub struct TaskstatsListener {
conn: Option<TaskstatsConnection>,
cpu_mask: Box<CStr>,
}
impl TaskstatsListener {
pub fn register(mut conn: TaskstatsConnection, cpu_mask: Box<CStr>) -> Result<Self, Error> {
do_register(&mut conn, &cpu_mask)?;
Ok(Self {
conn: Some(conn),
cpu_mask,
})
}
pub fn deregister(mut self) -> Result<TaskstatsConnection, Error> {
let mut conn = self.conn.take().unwrap();
do_deregister(&mut conn, &self.cpu_mask)?;
Ok(conn)
}
pub fn get_next(&mut self) -> Result<(pid_t, Taskstats), Error> {
let conn = self.conn.as_mut().unwrap();
let mut attrs = conn.recv()?;
let (ty, value) = attrs.next_or(Error::UnexpectedEmptyResult)?;
check_attr_type(ty, TASKSTATS_TYPE_AGGR_PID)?;
let result = parse_aggr_pid(value)?;
let next_attr = attrs.next().transpose()?;
if let Some((ty, value)) = next_attr {
match ty {
TASKSTATS_TYPE_AGGR_TGID => {
let mut attrs = deserialize_attrs(value);
let (ty, value) = attrs.next_or(InvalidMessage::EmptyAggrTgid)?;
check_attr_type(ty, TASKSTATS_TYPE_TGID)?;
let tgid = deserialize_u32(value)?;
debug!("dropping aggr tgid stats for {}", tgid);
}
_ => drop_attr((ty, value)),
}
}
drop_attrs(attrs)?;
Ok(result)
}
}
impl Drop for TaskstatsListener {
fn drop(&mut self) {
if let Some(conn) = self.conn.as_mut() {
match do_deregister(conn, &self.cpu_mask) {
Ok(()) => {}
Err(e) => warn!("failed to deregister on {:?}: {}", self.cpu_mask, e),
}
}
}
}
fn do_register(conn: &mut TaskstatsConnection, cpu_mask: &CStr) -> Result<(), Error> {
conn.send(NetlinkAttr {
ty: TASKSTATS_CMD_ATTR_REGISTER_CPUMASK,
payload: cpu_mask,
})
}
fn do_deregister(conn: &mut TaskstatsConnection, cpu_mask: &CStr) -> Result<(), Error> {
conn.send(NetlinkAttr {
ty: TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK,
payload: cpu_mask,
})
}