Skip to content
scanner.rs 7.07 KiB
Newer Older
Luker's avatar
Luker committed
/*
 * Copyright (c) 2021, Luca Fulchir <luker@fenrirproject.org>
 *
 * This file is part of dfim.
 *
 * dfim is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * dfim is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with dfim.  If not, see <https://www.gnu.org/licenses/>.
 */

use super::DeviceMsg;
use ::std::sync::{mpsc, Arc, Condvar, Mutex};

pub async fn scanner(
    logger: Arc<::slog::Logger>,
    cfg: Arc<dfim::config::Config>,
    udev: super::UdevSend,
    tx: mpsc::Sender<DeviceMsg>,
    cvar: Arc<Condvar>,
) -> Result<(), ::std::io::Error> {
    // First setup the udev monitors, then enumerate all devices one, then just
    // wait for udev events
Luker's avatar
Luker committed
    let mut mon_vec = Vec::new();
    for dev in &cfg.devices {
        let mut mon_builder = ::udev::MonitorBuilder::new().unwrap();
        match &dev.subsystem {
            None => {}
            Some(ss) => match &dev.devtype {
                None => {
                    mon_builder = match mon_builder.match_subsystem(ss.to_str())
                    {
                        Ok(new_mon_builder) => new_mon_builder,
                        Err(e) => {
                            ::slog::error!(
                                logger,
                                "Can't monitor subsystem \"{}\"",
                                ss
                            );
                            return Err(e);
                        }
                    };
                }
                Some(dt) => {
                    mon_builder = match mon_builder
                        .match_subsystem_devtype(ss.to_str(), dt.to_str())
                    {
                        Ok(new_mon_builder) => new_mon_builder,
                        Err(e) => {
                            ::slog::error!(
                                logger,
                                "Can't monitor subsystem \"{}\" with devtype \
                                 \"{}\"",
                                ss,
                                dt
                            );
                            return Err(e);
                        }
                    };
                }
            },
        }
        for tag in &dev.tags {
            mon_builder = match mon_builder.match_tag(&tag.id) {
                Ok(new_mon_builder) => new_mon_builder,
                Err(e) => {
                    ::slog::error!(
                        logger,
                        "Can't filter with tag \"{}\"",
                        &tag.id
                    );
                    return Err(e);
                }
            }
        }
        let mon_sock = match mon_builder.listen() {
            Ok(sock) => sock,
            Err(e) => {
                ::slog::error!(logger, "Can't create socket to monitor dev");
                return Err(e);
            }
        };
        mon_vec.push(mon_sock);
    }
Luker's avatar
Luker committed

    // enumerate all devices
Luker's avatar
Luker committed
    let udev_ctx = udev.udev;
Luker's avatar
Luker committed
    let mut devices = ::udev::Enumerator::with_udev(udev_ctx.clone())?;
    devices.match_is_initialized().unwrap();

    let dev_list = devices.scan_devices()?;

    for dev in dev_list {
Luker's avatar
Luker committed
        match filter_devices(cfg.clone(), dev) {
            Ok(dev) => {
                match tx.send(DeviceMsg { handle: dev }) {
                    Ok(()) => {}
                    Err(_) => {
                        // the receiver has been closed
                        // it's pointless to do any more work
                        return Err(::std::io::Error::new(
                            ::std::io::ErrorKind::ConnectionReset,
                            "work queue receiver has been closed",
                        ));
                    }
                }
                cvar.notify_one();
            }
            Err(()) => {}
        }
Luker's avatar
Luker committed

fn filter_devices(
    cfg: Arc<dfim::config::Config>,
    dev: ::udev::Device,
) -> Result<::udev::Device, ()> {
    for cfg_dev in &cfg.devices {
        match &cfg_dev.subsystem {
            None => {}
            Some(s) => {
                let os_s: ::std::ffi::OsString = s.to_str().into();
                if dev.subsystem() != Some(os_s.as_os_str()) {
                    return Err(());
                }
            }
        }
        match &cfg_dev.devtype {
            None => {}
            Some(dt) => {
                let os_dt: ::std::ffi::OsString = dt.to_str().into();
                if dev.devtype() != Some(os_dt.as_os_str()) {
                    return Err(());
                }
            }
        }
        match &cfg_dev.path_regex {
            None => {}
            Some(regex) => {
                let path = match dev.devpath().to_str() {
                    Some(path) => path,
                    None => return Err(()),
                };
                if !regex.is_match(path) {
                    return Err(());
                }
            }
        }
        for attr in &cfg_dev.attributes {
            let os_name: ::std::ffi::OsString = attr.name.clone().into();
            match dev.attribute_value(os_name) {
                Some(val) => {
                    let os_value: ::std::ffi::OsString =
                        attr.value.clone().into();
                    if val != os_value {
                        return Err(());
                    }
                }
                None => {
                    return Err(());
                }
            }
        }
        for attr in &cfg_dev.exclude_attributes {
            let os_name: ::std::ffi::OsString = attr.name.clone().into();
            match dev.attribute_value(os_name) {
                Some(val) => {
                    let os_value: ::std::ffi::OsString =
                        attr.value.clone().into();
                    if val == os_value {
                        return Err(());
                    }
                }
                None => {}
            }
        }
        match &cfg_dev.sysname {
            None => {}
            Some(sn) => {
                let os_sn: ::std::ffi::OsString = sn.as_str().into();
                if dev.sysname() != os_sn.as_os_str() {
                    return Err(());
                }
            }
        }
        for prop in &cfg_dev.properties {
            let os_name: ::std::ffi::OsString = prop.name.clone().into();
            match dev.property_value(os_name) {
                Some(val) => {
                    let os_value: ::std::ffi::OsString =
                        prop.value.clone().into();
                    if val != os_value {
                        return Err(());
                    }
                }
                None => {
                    return Err(());
                }
            }
        }
        // TODO: TAGS
    }

    Ok(dev)
}