diff --git a/bindings/rust/Cargo.toml b/bindings/rust/Cargo.toml new file mode 100644 index 00000000..15f82993 --- /dev/null +++ b/bindings/rust/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "swupdate-bindings" +version = "0.1.0" +edition = "2021" + +[dependencies] +lazy_static = "1.4.0" +libc = "0.2.147" +once_cell = "1.18.0" diff --git a/bindings/rust/lib.rs b/bindings/rust/lib.rs new file mode 100644 index 00000000..bfdbd02b --- /dev/null +++ b/bindings/rust/lib.rs @@ -0,0 +1,139 @@ +use lazy_static::lazy_static; +use once_cell::sync::OnceCell; +use std::ffi::{CStr, CString}; +use std::io::{Error, ErrorKind}; +use std::os::raw::{c_char, c_int}; +use std::sync::{Condvar, Mutex}; + +mod types; +use types::{ + ipc_message::IpcMessage, run_type::RunType, source_type::SourceType, + swupdate_request::SwupdateRequest, +}; + +type WriteData = unsafe extern "C" fn(*mut *mut c_char, *mut c_int) -> c_int; +type GetStatus = unsafe extern "C" fn(*mut IpcMessage) -> c_int; +type Terminated = unsafe extern "C" fn(c_int) -> c_int; +type Callback = fn(String); + +lazy_static! { + static ref MY_MUTEX: Mutex = Mutex::new(false); + static ref CV_END: Condvar = Condvar::new(); +} + +static mut FD: c_int = 0; +static mut BUF: [u8; 256] = [0u8; 256]; +static mut CB: OnceCell = OnceCell::new(); + +#[link(name = "swupdate")] +extern "C" { + fn swupdate_async_start( + wr_func: WriteData, + status_func: GetStatus, + end_func: Terminated, + req: *mut SwupdateRequest, + size: isize, + ) -> c_int; + + fn swupdate_prepare_req(req: *mut SwupdateRequest); +} + +extern "C" fn readimage(p: *mut *mut c_char, size: *mut c_int) -> c_int { + unsafe { + let ret = libc::read(FD, BUF.as_mut_ptr() as *mut std::ffi::c_void, BUF.len()); + + *p = BUF.as_mut_ptr() as *mut c_char; + *size = ret as c_int; + + ret as c_int + } +} + +extern "C" fn printstatus(msg: *mut IpcMessage) -> c_int { + unsafe { + CB.get().expect("Callback not initialized")(format!( + "Status: {:?} message: {:?}\n", + (*msg).data.status.current, + CStr::from_ptr((*msg).data.status.desc.as_ptr()) + )); + }; + 0 +} + +extern "C" fn end(_status: c_int) -> c_int { + let mut guard = MY_MUTEX.lock().unwrap(); + *guard = true; + CV_END.notify_one(); + + 0 +} + +fn open_file(filename: &str) -> c_int { + let c_filename = CString::new(filename).expect("CString::new failed"); + unsafe { + let fd = libc::open(c_filename.as_ptr(), libc::O_RDONLY); + if fd < 0 { + eprintln!("Unable to open {}", filename); + return libc::EXIT_FAILURE; + } + FD = fd; + } + libc::EXIT_SUCCESS +} + +fn close_file() { + unsafe { + if FD >= 0 { + libc::close(FD); + } + } +} + +pub fn send_file(filename: &str, dry_run: bool, cb: Callback) -> Result<(), Error> { + open_file(filename); + + let dry_run_val = if dry_run { + RunType::Dryrun + } else { + RunType::Default + }; + + let mut req = SwupdateRequest { + apiversion: 0, + source: SourceType::Local, + dry_run: dry_run_val, + len: 0, + info: [0; 512], + software_set: [0; 256], + running_mode: [0; 256], + disable_store_swu: false, + }; + + unsafe { swupdate_prepare_req(&mut req) }; + + let mut guard = MY_MUTEX.lock().unwrap(); + + unsafe { + let size = std::mem::size_of_val(&req).try_into().unwrap(); + CB.get_or_init(|| cb); + let rc = swupdate_async_start(readimage, printstatus, end, &mut req, size); + if rc < 0 { + close_file(); + return Err(Error::new( + ErrorKind::Other, + format!( + "Software update failed to start with return code: {:?}", + libc::EXIT_FAILURE + ), + )); + } + } + + while !*guard { + guard = CV_END.wait(guard).unwrap(); + } + + close_file(); + + Ok(()) +} diff --git a/bindings/rust/types/ipc_message.rs b/bindings/rust/types/ipc_message.rs new file mode 100644 index 00000000..8575fcd7 --- /dev/null +++ b/bindings/rust/types/ipc_message.rs @@ -0,0 +1,87 @@ +use std::os::raw::{c_char, c_int, c_uint}; + +use crate::types::source_type::SourceType; +use crate::SwupdateRequest; + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct Status { + pub current: c_int, + last_result: c_int, + error: c_int, + pub desc: [c_char; 2048], +} + +#[repr(C)] +#[derive(Clone, Copy)] +struct Notify { + status: c_int, + error: c_int, + level: c_int, + msg: [c_char; 2048], +} + +#[repr(C)] +#[derive(Clone, Copy)] +struct InstMsg { + req: SwupdateRequest, + len: c_uint, + buf: [c_char; 2048], +} + +#[repr(C)] +#[derive(Clone, Copy)] +struct ProcMsg { + source: SourceType, + cmd: c_int, + timeout: c_int, + len: c_uint, + buf: [c_char; 2048], +} + +#[repr(C)] +#[derive(Clone, Copy)] +struct AESKeyMsg { + key_ascii: [c_char; 65], + ivt_ascii: [c_char; 33], +} + +#[repr(C)] +#[derive(Clone, Copy)] +struct Versions { + minimum_version: [c_char; 256], + maximum_version: [c_char; 256], + current_version: [c_char; 256], +} + +#[repr(C)] +#[derive(Clone, Copy)] +struct Revisions { + boardname: [c_char; 256], + revision: [c_char; 256], +} + +#[repr(C)] +pub union MsgData { + msg: [c_char; 128], + pub status: Status, + notify: Notify, + inst_msg: InstMsg, + proc_msg: ProcMsg, + aes_key_msg: AESKeyMsg, + versions: Versions, + revisions: Revisions, +} + +impl Default for MsgData { + fn default() -> Self { + MsgData { msg: [0; 128] } + } +} + +#[repr(C)] +pub struct IpcMessage { + magic: c_int, + type_: c_int, + pub data: MsgData, +} diff --git a/bindings/rust/types/mod.rs b/bindings/rust/types/mod.rs new file mode 100644 index 00000000..53039213 --- /dev/null +++ b/bindings/rust/types/mod.rs @@ -0,0 +1,4 @@ +pub mod ipc_message; +pub mod run_type; +pub mod source_type; +pub mod swupdate_request; diff --git a/bindings/rust/types/run_type.rs b/bindings/rust/types/run_type.rs new file mode 100644 index 00000000..34f80d42 --- /dev/null +++ b/bindings/rust/types/run_type.rs @@ -0,0 +1,7 @@ +#[repr(C)] +#[derive(Clone, Copy)] +pub enum RunType { + Default, + Dryrun, + _Install, +} diff --git a/bindings/rust/types/source_type.rs b/bindings/rust/types/source_type.rs new file mode 100644 index 00000000..a6ea78c3 --- /dev/null +++ b/bindings/rust/types/source_type.rs @@ -0,0 +1,10 @@ +#[repr(C)] +#[derive(Clone, Copy)] +pub enum SourceType { + _Unknown, + _Webserver, + _Suricatta, + _Downloader, + Local, + _ChunksDownloader, +} diff --git a/bindings/rust/types/swupdate_request.rs b/bindings/rust/types/swupdate_request.rs new file mode 100644 index 00000000..385f96d5 --- /dev/null +++ b/bindings/rust/types/swupdate_request.rs @@ -0,0 +1,16 @@ +use std::os::raw::c_char; + +use crate::types::{run_type::RunType, source_type::SourceType}; + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct SwupdateRequest { + pub apiversion: u32, + pub source: SourceType, + pub dry_run: RunType, + pub len: usize, + pub info: [c_char; 512], + pub software_set: [c_char; 256], + pub running_mode: [c_char; 256], + pub disable_store_swu: bool, +}