#![allow(non_upper_case_globals)] #![allow(non_camel_case_types)] #![allow(non_snake_case)] #[derive(Debug)] pub enum SUPPORTED_FSV_VERSIONS { V1_40_14, // 1.40.14 V1_40_13, // 1.40.13 } impl SUPPORTED_FSV_VERSIONS { fn as_str(&self) -> &'static str { match self { Self::V1_40_14 => "1.40.14", Self::V1_40_13 => "1.40.13", } } } pub mod BINDINGS { include!(concat!(env!("OUT_DIR"), "/bindings_1.40.13.rs")); } // We need to keep the this use statement to get `ssv_function` macro working well use BINDINGS::*; /// Macro to generate a function that implements a call to an external function in BINDINGS macro_rules! ssv_function { ($binding:ty, $func_name:ident, {$($arg_name:ident: $arg_type:ty),*}) => { /// # Safety /// This function is unsafe because it calls an external function through FFI. /// The caller must ensure that the provided arguments are valid and that the /// external function is safe to call. pub unsafe fn $func_name(&self, $($arg_name: $arg_type),*) -> Result { let func_struct: libloading::Symbol<'_, $binding> = unsafe { self.library.get(stringify!($binding).as_bytes())? }; let func = match *func_struct { Some(func) => func, None => return Err(Error::SymbolMissing(stringify!($binding))), }; Ok(func($($arg_name),*)) } }; } #[derive(thiserror::Error, Debug)] pub enum Error { #[error(transparent)] LibLoading(#[from] libloading::Error), #[error("Symbol missing: {0}")] SymbolMissing(&'static str), } /// Wrapper around the SESAM-VITALE library /// This struct is responsible for loading the library and providing an interface to call its functions. /// The library is loaded at creation and kept in memory until the struct is dropped. #[derive(Debug)] pub struct SSVLibrary { version: SUPPORTED_FSV_VERSIONS, library: libloading::Library, } fn get_library_name() -> &'static str { // TODO : Use libloading::library_filename to get platform-specific filename ? "libssvlux64.so" } fn get_library_root_path() -> &'static str { "/opt/santesocial/fsv" } fn get_sesam_ini_root_path() -> &'static str { "/etc/opt/santesocial/fsv" } impl SSVLibrary { pub fn new(version: SUPPORTED_FSV_VERSIONS) -> Result { let library_path = Self::get_library_path(&version); let library = unsafe { libloading::Library::new(library_path)? }; Ok(SSVLibrary { version, library }) } pub fn library(&self) -> &libloading::Library { &self.library } pub fn sesam_ini_path(&self) -> String { let root_path = get_sesam_ini_root_path(); let version = self.version.as_str(); format!("{root_path}/{version}/conf/sesam.ini") } pub fn get_library_path(version: &SUPPORTED_FSV_VERSIONS) -> String { let root_path = get_library_root_path(); let library_name = get_library_name(); let version = version.as_str(); format!("{root_path}/{version}/lib/{library_name}") } ssv_function!(SSV_InitLIB2, ssv_init_lib2, { pcFichierSesam: *const i8 }); ssv_function!(SSV_LireConfig, ssv_lire_config, { pZDataOut: *mut *mut libc::c_void, psTailleDataOut: *mut usize }); ssv_function!(SSV_LireCartePS, ssv_lire_carte_ps, { NomRessourcePS: *const i8, NomRessourceLecteur: *const i8, CodePorteurPS: *const i8, pZDataOut: *mut *mut libc::c_void, pTailleZone: *mut usize }); } #[cfg(test)] mod test { use std::{ffi::CString, ptr}; use super::*; #[test] fn test_initlib2() { let ssv_library = SSVLibrary::new(SUPPORTED_FSV_VERSIONS::V1_40_13).expect("SSVLibrary::new failed"); let sesam_ini_str = CString::new(ssv_library.sesam_ini_path()).expect("CString::new failed"); let result = unsafe { ssv_library.ssv_init_lib2(sesam_ini_str.as_ptr()) }.unwrap(); assert_eq!(result, 0); } #[test] fn test_lire_config_and_carte_ps() { let ssv_library = SSVLibrary::new(SUPPORTED_FSV_VERSIONS::V1_40_13).expect("SSVLibrary::new failed"); let sesam_ini_str = CString::new(ssv_library.sesam_ini_path()).expect("CString::new failed"); let result = unsafe { ssv_library.ssv_init_lib2(sesam_ini_str.as_ptr()) }.unwrap(); assert_eq!(result, 0); let mut buffer_ptr: *mut libc::c_void = ptr::null_mut(); let mut size: libc::size_t = 0; let result = unsafe { ssv_library.ssv_lire_config(&mut buffer_ptr, &mut size) }.unwrap(); assert_eq!(result, 0); let nom_ressource_ps = CString::new("Gemalto PC Twin Reader (645D94C3) 00 00").expect("CString::new failed"); let nom_ressource_lecteur = CString::new("Gemalto PC Twin Reader (645D94C3) 00 00").expect("CString::new failed"); let code_porteur_ps = CString::new("1234").expect("CString::new failed"); let mut buffer_ptr: *mut libc::c_void = ptr::null_mut(); let mut size: libc::size_t = 0; let result = unsafe { ssv_library.ssv_lire_carte_ps( nom_ressource_ps.as_ptr(), nom_ressource_lecteur.as_ptr(), code_porteur_ps.as_ptr(), &mut buffer_ptr, &mut size, ) } .unwrap(); assert_eq!(result, 0); } }