socketcan-alt 0.4.0

Rust binding for SocketCAN
Documentation
use super::Socket;
use crate::{sys, Cmsg, DataFrame, FdDataFrame, Frame, Id, Timestamping};
use rand::Rng;
use spin::RwLock;
use std::env;
use std::ffi::CString;
use std::io::ErrorKind;
use std::io::Result;
use std::os::unix::ffi::OsStrExt;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::thread;
use std::time::Duration;

pub(crate) static LOCK: RwLock<()> = RwLock::new(());

pub(crate) fn ifname() -> CString {
    let ifname = env::var_os("IFNAME").expect("IFNAME environment variable is not set");
    CString::new(ifname.as_bytes()).unwrap()
}

macro_rules! lock {
    (shared) => {
        let _lock = LOCK.read();
    };
    (exclusive) => {
        let _lock = LOCK.write();
    };
}

fn timeout<F, T>(f: F) -> Option<T>
where
    F: 'static + Send + FnOnce() -> T,
    T: 'static + Send,
{
    struct Context(Arc<AtomicBool>);
    impl Drop for Context {
        fn drop(&mut self) {
            self.0.store(true, Ordering::Relaxed);
        }
    }

    let is_done = Arc::new(AtomicBool::new(false));
    let cxt = Context(is_done.clone());
    let handle = thread::spawn(move || {
        let _cxt = cxt;
        f()
    });

    thread::sleep(Duration::from_millis(100));
    if is_done.load(Ordering::Relaxed) {
        handle.join().ok()
    } else {
        None
    }
}

fn recv(socket: Socket, query: Option<Frame>) -> Option<Result<()>> {
    timeout(move || loop {
        let frame = socket.recv()?;
        if query.as_ref().map(|query| &frame == query).unwrap_or(true) {
            return Ok(());
        }
    })
}

fn recv_msg(socket: Socket, query: Option<Frame>) -> Option<Result<Option<libc::timespec>>> {
    timeout(move || {
        let mut cmsg_buf = vec![0; Cmsg::space()];
        loop {
            let (frame, cmsgs) = socket.recv_msg(&mut cmsg_buf)?;
            if query.as_ref().map(|query| &frame == query).unwrap_or(true) {
                let timestamp = cmsgs.into_iter().flatten().find_map(|cmsg| match cmsg {
                    Cmsg::Timestamping(ts) => Some(ts[0]),
                    _ => None,
                });
                return Ok(timestamp);
            }
        }
    })
}

pub(crate) fn random_data_standard() -> Frame {
    let mut rng = rand::thread_rng();
    let id = Id::Standard(rng.gen_range(0..sys::CAN_SFF_MASK));
    let data = (0..rng.gen_range(0..sys::CAN_MAX_DLEN))
        .map(|_| rng.gen())
        .collect::<Vec<_>>();
    Frame::Data(DataFrame::new(id, &data))
}

pub(crate) fn random_fd_data_standard() -> Frame {
    let mut rng = rand::thread_rng();
    let id = Id::Standard(rng.gen_range(0..sys::CAN_SFF_MASK));
    let data = (0..rng.gen_range(0..sys::CANFD_MAX_DLEN))
        .map(|_| rng.gen())
        .collect::<Vec<_>>();
    Frame::FdData(FdDataFrame::new(id, false, false, &data))
}

#[test]
#[ignore]
fn test_bind() {
    Socket::bind(ifname()).unwrap();
}

#[test]
fn test_bind_no_device() {
    let ifname = CString::new("NO DEVICE").unwrap();
    assert!(Socket::bind(ifname).is_err());
}

#[test]
#[ignore]
fn test_default_nonblocking_off() {
    lock!(exclusive);
    let socket = Socket::bind(ifname()).unwrap();

    assert!(recv(socket, None).is_none());
}

#[test]
#[ignore]
fn test_set_nonblocking_on() {
    lock!(exclusive);
    let socket = Socket::bind(ifname()).unwrap();
    socket.set_nonblocking(true).unwrap();

    assert_eq!(
        recv(socket, None).unwrap().unwrap_err().kind(),
        ErrorKind::WouldBlock
    );
}

#[test]
#[ignore]
fn test_default_timestamping_off() {
    lock!(shared);
    let socket_tx = Socket::bind(ifname()).unwrap();
    let socket_rx = Socket::bind(ifname()).unwrap();

    let frame = random_data_standard();
    socket_tx.send(&frame).unwrap();
    assert!(recv_msg(socket_rx, Some(frame)).unwrap().unwrap().is_none());
}

#[test]
#[ignore]
fn test_set_timestamping_on() {
    lock!(shared);
    let socket_tx = Socket::bind(ifname()).unwrap();
    let socket_rx0 = Socket::bind(ifname()).unwrap();
    let socket_rx1 = Socket::bind(ifname()).unwrap();
    socket_rx0
        .set_timestamping(Timestamping::RX_SOFTWARE | Timestamping::SOFTWARE)
        .unwrap();
    socket_rx1
        .set_timestamping(Timestamping::RX_SOFTWARE | Timestamping::SOFTWARE)
        .unwrap();

    let frame = random_data_standard();
    socket_tx.send(&frame).unwrap();
    let timestamp0 = recv_msg(socket_rx0, Some(frame)).unwrap().unwrap().unwrap();
    thread::sleep(Duration::from_millis(100));
    let timestamp1 = recv_msg(socket_rx1, Some(frame)).unwrap().unwrap().unwrap();
    assert!(timestamp0.tv_sec != 0);
    assert!(timestamp1.tv_sec != 0);
    assert_eq!(
        (timestamp0.tv_sec, timestamp0.tv_nsec),
        (timestamp1.tv_sec, timestamp1.tv_nsec)
    );
}

#[test]
#[ignore]
fn test_default_loopback_on() {
    lock!(shared);
    let socket_tx = Socket::bind(ifname()).unwrap();
    let socket_rx = Socket::bind(ifname()).unwrap();

    let frame = random_data_standard();
    socket_tx.send(&frame).unwrap();
    recv(socket_rx, Some(frame)).unwrap().unwrap();
}

#[test]
#[ignore]
fn test_default_recv_own_msgs_off() {
    lock!(shared);
    let socket = Socket::bind(ifname()).unwrap();

    let frame = random_data_standard();
    socket.send(&frame).unwrap();
    assert!(recv(socket, Some(frame)).is_none());
}

#[test]
#[ignore]
fn test_set_recv_own_msgs_on() {
    lock!(shared);
    let socket = Socket::bind(ifname()).unwrap();
    socket.set_recv_own_msgs(true).unwrap();

    let frame = random_data_standard();
    socket.send(&frame).unwrap();
    recv(socket, Some(frame)).unwrap().unwrap();
}

#[test]
#[ignore]
fn test_default_fd_frames_off() {
    lock!(shared);
    let socket = Socket::bind(ifname()).unwrap();

    let frame = random_fd_data_standard();
    assert_eq!(
        socket.send(&frame).unwrap_err().kind(),
        ErrorKind::InvalidInput
    );
}

#[test]
#[ignore]
fn test_set_fd_frames_on() {
    lock!(shared);
    let socket_tx = Socket::bind(ifname()).unwrap();
    let socket_rx = Socket::bind(ifname()).unwrap();
    socket_tx.set_fd_frames(true).unwrap();
    socket_rx.set_fd_frames(true).unwrap();

    let frame = random_fd_data_standard();
    socket_tx.send(&frame).unwrap();
    recv(socket_rx, Some(frame)).unwrap().unwrap();
}