summaryrefslogtreecommitdiffstats
path: root/meta-egvirt/recipes-extended/vhost-device-can/files/vhost-device-can-0.1.0/src/main.rs
blob: 97386884e1c534402983791f4981e6e32f14c985 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
// VIRTIO CAN Emulation via vhost-user
//
// Copyright 2023-2024 VIRTUAL OPEN SYSTEMS SAS. All Rights Reserved.
//          Timos Ampelikiotis <t.ampelikiotis@virtualopensystems.com>
//
// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause

mod backend;
mod can;
mod vhu_can;
mod virtio_can;

use clap::Parser;
use log::{error, info};
use socketcan::{CanSocket, Socket};
use std::convert::TryFrom;
use std::path::PathBuf;
use std::process::exit;

pub(crate) type Result<T> = std::result::Result<T, Error>;
use crate::backend::{start_backend, Error, VuCanConfig};

#[derive(Parser, Debug)]
#[clap(author, version, about, long_about = None)]
struct CanArgs {
    /// Location of vhost-user Unix domain socket. This is suffixed by 0,1,2..socket_count-1.
    #[clap(short, long, value_name = "SOCKET")]
    socket_path: PathBuf,

    /// A can device name to be used for reading (ex. vcan, can0, can1, ... etc.)
    #[clap(short = 'd', long)]
    can_devices: String,

    /// Number of guests (sockets) to connect to.
    #[clap(short = 'c', long, default_value_t = 1)]
    socket_count: u32,
}

fn check_can_devices(can_devices: &[String]) -> Result<()> {
    for can_dev in can_devices {
        if CanSocket::open(can_dev).is_err() {
            info!("There is no interface with the following name {}", can_dev);
            return Err(Error::CouldNotFindCANDevs);
        }
    }
    Ok(())
}

fn parse_can_devices(input: &CanArgs) -> Result<Vec<String>> {
    let can_devices_vec: Vec<&str> = input.can_devices.split_whitespace().collect();
    let can_devices: Vec<_> = can_devices_vec.iter().map(|x| x.to_string()).collect();

    if (can_devices.len() as u32) != input.socket_count {
        info!(
            "Number of CAN/FD devices ({}) not equal with socket count {}",
            input.can_devices, input.socket_count
        );
        return Err(Error::SocketCountInvalid(
            input.socket_count.try_into().unwrap(),
        ));
    }

    match check_can_devices(&can_devices) {
        Ok(_) => Ok(can_devices),
        Err(_) => Err(Error::CouldNotFindCANDevs),
    }
}

impl TryFrom<CanArgs> for VuCanConfig {
    type Error = Error;

    fn try_from(args: CanArgs) -> Result<Self> {
        if args.socket_count == 0 {
            return Err(Self::Error::SocketCountInvalid(0));
        }

        let can_devices = match parse_can_devices(&args) {
            Ok(can_devs) => can_devs,
            Err(e) => return Err(e),
        };

        Ok(VuCanConfig {
            socket_path: args.socket_path,
            socket_count: args.socket_count,
            can_devices,
        })
    }
}

fn main() {
    env_logger::init();
    if let Err(e) = VuCanConfig::try_from(CanArgs::parse()).and_then(start_backend) {
        error!("{e}");
        exit(1);
    }
}