diff --git a/osdpctl/Cargo.toml b/osdpctl/Cargo.toml index d48e197..cf03075 100644 --- a/osdpctl/Cargo.toml +++ b/osdpctl/Cargo.toml @@ -20,7 +20,7 @@ clap = "4.4.7" configparser = "3.0.2" daemonize = "0.5.0" dirs = "5.0.1" -libosdp = { version = "0.1.8" } +libosdp = { path = "../libosdp" } log = "0.4.20" log4rs = "1.2.0" nix = { version = "0.28.0", features = ["signal"] } diff --git a/osdpctl/src/config.rs b/osdpctl/src/config.rs index 30c1298..9c61f18 100644 --- a/osdpctl/src/config.rs +++ b/osdpctl/src/config.rs @@ -6,10 +6,8 @@ use anyhow::bail; use anyhow::Context; use configparser::ini::Ini; -use libosdp::{ - channel::{OsdpChannel, UnixChannel}, - OsdpFlag, PdCapability, PdId, PdInfo, -}; +use libosdp::PdInfoBuilder; +use libosdp::{OsdpFlag, PdCapability, PdId, PdInfo}; use rand::Rng; use std::{ fmt::Write, @@ -17,6 +15,8 @@ use std::{ str::FromStr, }; +use crate::unix_channel::UnixChannel; + type Result = anyhow::Result; fn vec_to_array(v: Vec) -> [T; N] { @@ -142,16 +142,17 @@ impl CpConfig { bail!("Only unix channel is supported for now") } let path = runtime_dir.join(format!("{}/{}.sock", d.name, parts[1]).as_str()); - let stream = + let channel = UnixChannel::connect(&path).context("Unable to connect to PD channel")?; - Ok(PdInfo::for_cp( - &self.name, - d.address, - 9600, - d.flags, - OsdpChannel::new::(Box::new(stream)), - d.key_store.load()?, - )) + let pd_info = PdInfoBuilder::new() + .name(&self.name)? + .address(d.address)? + .baud_rate(115200)? + .flag(d.flags) + .channel(Box::new(channel)) + .secure_channel_key(d.key_store.load()?) + .build(); + Ok(pd_info) }) .collect() } @@ -239,17 +240,18 @@ impl PdConfig { bail!("Only unix channel is supported for now") } let path = self.runtime_dir.join(format!("{}.sock", parts[1]).as_str()); - let stream = UnixChannel::new(&path)?; - Ok(PdInfo::for_pd( - &self.name, - self.address, - 9600, - self.flags, - self.pd_id.clone(), - self.pd_cap.clone(), - OsdpChannel::new::(Box::new(stream)), - self.key_store.load()?, - )) + let channel = UnixChannel::new(&path)?; + let pd_info = PdInfoBuilder::new() + .name(&self.name)? + .address(self.address)? + .baud_rate(115200)? + .flag(self.flags) + .capabilities(&self.pd_cap) + .id(&self.pd_id) + .channel(Box::new(channel)) + .secure_channel_key(self.key_store.load()?) + .build(); + Ok(pd_info) } } diff --git a/osdpctl/src/main.rs b/osdpctl/src/main.rs index 4be1aab..df618c1 100644 --- a/osdpctl/src/main.rs +++ b/osdpctl/src/main.rs @@ -7,6 +7,7 @@ mod config; mod cp; mod daemonize; mod pd; +mod unix_channel; use anyhow::{bail, Context}; use clap::{arg, Command}; diff --git a/osdpctl/src/unix_channel.rs b/osdpctl/src/unix_channel.rs new file mode 100644 index 0000000..5983702 --- /dev/null +++ b/osdpctl/src/unix_channel.rs @@ -0,0 +1,89 @@ +// +// Copyright (c) 2023-2024 Siddharth Chandrasekaran +// +// SPDX-License-Identifier: Apache-2.0 + +//! OSDP unix channel + +use core::time::Duration; +use std::{ + collections::hash_map::DefaultHasher, + hash::{Hash, Hasher}, + io::{Read, Write}, + os::unix::net::{UnixListener, UnixStream}, + path::{Path, PathBuf}, + str::FromStr, + thread, +}; + +use libosdp::ChannelError; + +type Result = std::result::Result; + +/// A reference OSDP channel implementation for unix domain socket. +#[derive(Debug)] +pub struct UnixChannel { + id: i32, + stream: UnixStream, +} + +pub fn str_to_channel_id(key: &str) -> i32 { + let mut hasher = DefaultHasher::new(); + key.hash(&mut hasher); + let mut id: u64 = hasher.finish(); + id = (id >> 32) ^ id & 0xffffffff; + id as i32 +} + +impl UnixChannel { + /// Connect to a channel identified by `name`. + pub fn connect(path: &Path) -> Result { + let id = 0; + let stream = UnixStream::connect(&path)?; + Ok(Self { id, stream }) + } + + /// Listen on a channel identified by `name`. + pub fn new(path: &Path) -> Result { + let id = str_to_channel_id(path.as_os_str().try_into().unwrap()); + if path.exists() { + std::fs::remove_file(&path)?; + } + let listener = UnixListener::bind(&path)?; + println!("Waiting for connection to unix::{}", path.display()); + let (stream, _) = listener.accept()?; + Ok(Self { id, stream }) + } + + /// Create a bi-directional channel pair. Returns Result<(server, client)> + pub fn _pair(name: &str) -> Result<(Self, Self)> { + let path = PathBuf::from_str(format!("/tmp/osdp-{name}.sock").as_str())?; + let path_clone = path.clone(); + let h = thread::spawn(move || { + let path = path_clone; + UnixChannel::new(&path) + }); + thread::sleep(Duration::from_secs(1)); + let client = UnixChannel::connect(&path)?; + let server = h.join().unwrap()?; + Ok((server, client)) + } +} + +impl libosdp::Channel for UnixChannel { + fn get_id(&self) -> i32 { + self.id + } + + fn read(&mut self, buf: &mut [u8]) -> std::prelude::v1::Result { + self.stream.read(buf).map_err(ChannelError::from) + } + + fn write(&mut self, buf: &[u8]) -> std::prelude::v1::Result { + self.stream.write(buf).map_err(ChannelError::from) + } + + fn flush(&mut self) -> std::prelude::v1::Result<(), libosdp::ChannelError> { + self.stream.flush().map_err(ChannelError::from) + } +}