blsctl 0.2.2

Manages BLS entries and kernel cmdline options
Documentation
// cshim.rs
//
// Copyright 2019 Alberto Ruiz <aruiz@gnome.org>
//
// This file is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
//
// This file 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
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this program.  If not, see <http://www.gnu.org/licenses/>.
//
// SPDX-License-Identifier: LGPL-2.1-or-later

use crate::bls::{BLSEntry, KeyValue};
use crate::cmdline::{CmdlineHandler, CmdlineParam};
use std::ffi::{CStr, CString};
use std::os::raw::{c_char};

#[repr(C)]
pub struct CTraitReference {
  data: *mut std::os::raw::c_void,
  vtable: *mut std::os::raw::c_void
}

fn c_array_to_vector(c_array: *const *const c_char) -> Result<Vec<String>, &'static str> {
  /* Find null pointer to assess end of array */
  let start: *mut usize = c_array as *mut usize;
  let mut end: isize = 0;
  let mut boxed_address = unsafe { std::ptr::read (start.offset(end))};
  while boxed_address != 0 {
    boxed_address = unsafe { std::ptr::read (start.offset(end))};
    end += 1;
  }
  end -= 1;

  let ptr_slice = unsafe { std::slice::from_raw_parts(c_array, end as usize) };
  let vector: Vec<_> = ptr_slice.iter()
    .map(|cstr| { unsafe { CStr::from_ptr(*cstr) }})
    .map(|cstr| { cstr.to_str() })
    .filter_map(|cstr| { match cstr {
      Err(_) => None,
      Ok(elements) => Some(elements)
    }})
    .map(|element| { String::from(element) })
    .collect();

  if vector.len() != end as usize {
    return Err("could not convert pointer to vector");
  }

  Ok(vector)
}

fn generic_cmdline_handler(env: &mut dyn CmdlineHandler, params: *const *const c_char, op: &dyn Fn(&mut dyn CmdlineHandler, &[String]) -> std::io::Result<()>) -> c_char {
  let params = match c_array_to_vector(params) {
    Ok(params) => params,
    Err(_) => { return 0; }
  };

  match op(env, &params[..]) {
    Ok(_) =>   1,
    Err(_) =>  0
  }
}

fn generic_cmdline_get (env: &mut dyn CmdlineHandler, param: *const c_char) -> *mut CmdlineParam {
  let param = match unsafe { CStr::from_ptr(param) }.to_str() {
    Ok(param) => param,
    Err(_) => { return std::ptr::null_mut(); }
  };

  let cmdline = env.cmdline_get(&String::from(param));

  match cmdline {
    Ok(cmdline) => {
      let cmdline = Box::new(cmdline);
      Box::into_raw(cmdline)
    }
    Err(_) => std::ptr::null_mut()
  }
}

#[allow(dead_code)]
#[no_mangle]
pub extern "C" fn bls_cmdline_param_foreach (cmdline: *mut CmdlineParam, lambda: extern "C" fn (val: *const c_char, user_data: *mut std::ffi::c_void), user_data: *mut std::ffi::c_void) {
    let cmdline = unsafe{ Box::from_raw(cmdline) };
    for param_value in cmdline.iter() {
      match param_value {
        Some(param_value) => {
          let c_param_value = CString::new(param_value.as_str()).unwrap();

          lambda (c_param_value.as_ptr(), user_data);
        }
        None => {
          lambda (std::ptr::null(), user_data);
        }
      }
    };
    Box::into_raw(cmdline);
}


#[allow(dead_code)]
#[no_mangle]
pub extern "C" fn bls_cmdline_param_get_size (cmdline: *mut CmdlineParam) -> usize {
  let cmdline = unsafe{ Box::from_raw(cmdline) };
  let size = cmdline.len();
  Box::into_raw(cmdline);
  size
}

#[allow(dead_code)]
#[no_mangle]
pub extern "C" fn bls_cmdline_param_destroy(cmdline: *mut CmdlineParam) {
  unsafe{ Box::from_raw(cmdline) };
}

/* BLS Entry */

#[allow(dead_code)]
#[no_mangle]
pub extern "C" fn bls_entry_new(entry: *const c_char) -> *mut BLSEntry {
  let entry = match unsafe { CStr::from_ptr(entry) }.to_str() {
    Ok(entry_name) => String::from(entry_name),
    Err(_) => { return std::ptr::null_mut(); }
  };

  if let Ok(entry) = BLSEntry::new(&entry) {
    Box::into_raw(Box::new(entry))
  } else {
    std::ptr::null_mut()
  }
}

#[allow(dead_code)]
#[no_mangle]
pub extern "C" fn bls_entry_destroy(entry: *mut BLSEntry) {
  unsafe { Box::from_raw(entry) };
}

#[allow(dead_code)]
#[no_mangle]
pub extern "C" fn bls_entry_get (entry: *mut BLSEntry, key: *const c_char) -> *mut KeyValue {
  let entry = unsafe { Box::from_raw(entry) };
  let key = match unsafe { CStr::from_ptr(key) }.to_str() {
    Ok(key) => key,
    _ => { return std::ptr::null_mut(); }
  };

 match entry.get(key) {
   Ok(keyvalue) => {
     let keyvalue = Box::new(keyvalue);
     Box::into_raw(keyvalue)
    },
    Err(_) => std::ptr::null_mut()
  }
}

#[allow(dead_code)]
#[no_mangle]
pub extern "C" fn bls_entry_set (entry: *mut BLSEntry, key: *const c_char,
                                 value: *const *const c_char) -> c_char {
  let entry = unsafe { Box::from_raw(entry) };
  let key = match unsafe { CStr::from_ptr(key) }.to_str() {
    Ok(key) => key,
    _ => { return 0; }
  };

 let value = match c_array_to_vector(value) {
    Ok(value) => value,
    Err(_) => { return 0; }
  };

  let ret = match entry.set(key, &value) {
    Ok(_) => 1,
    _ => 0
  };
  Box::into_raw(entry);
  ret
}

#[allow(dead_code)]
#[no_mangle]
pub extern "C" fn bls_entry_remove (entry: *mut BLSEntry, key: *const c_char) -> c_char {
  let entry = unsafe { Box::from_raw(entry) };
  let key = match unsafe { CStr::from_ptr(key) }.to_str() {
    Ok(key) => key,
    _ => { return 0; }
  };

  let ret = match entry.remove(key) {
    Ok(_) => 1,
    _ => 0
  };
  Box::into_raw(entry);
  ret
}

/* BLSEntryList */

#[allow(dead_code)]
#[no_mangle]
pub extern "C" fn bls_entry_list () -> *mut Vec<String> {
  match BLSEntry::get_bls_entries() {
    Ok(entries) => {
      Box::into_raw(Box::new(entries))
    },
    Err(_) => std::ptr::null_mut()
  }
}

#[allow(dead_code)]
#[no_mangle]
pub extern "C" fn bls_entry_list_destroy(list: *mut Vec<String>) {
  let _ = unsafe { Box::from_raw(list) };
}

/* Cmdline related methods */

#[allow(dead_code)]
#[no_mangle]
pub extern "C" fn bls_entry_to_cmdline (entry: *mut BLSEntry) -> CTraitReference {
  let mut entry = unsafe { Box::from_raw(entry) };
  let ret: CTraitReference = {
    let handler: &mut dyn CmdlineHandler = &mut *entry;
    unsafe { std::mem::transmute::<&mut dyn CmdlineHandler,CTraitReference> (handler) }
  };
  Box::into_raw(entry);
  ret
}

#[allow(dead_code)]
#[no_mangle]
pub extern "C" fn bls_entry_list_to_cmdline (list: *mut Vec<String>) -> CTraitReference {
  let mut list = unsafe { Box::from_raw(list) };
  let ret: CTraitReference = {
    let handler: &mut dyn CmdlineHandler = &mut *list;
    unsafe { std::mem::transmute::<&mut dyn CmdlineHandler,CTraitReference> (handler) }
  };
  Box::into_raw(list);
  ret
}

#[allow(dead_code)]
#[no_mangle]
pub extern "C" fn bls_cmdline_get (handler: CTraitReference, param: *mut c_char) -> *mut CmdlineParam {
  let handler = unsafe { std::mem::transmute::<CTraitReference, &mut dyn CmdlineHandler> (handler) };
  generic_cmdline_get (handler, param)
}

#[allow(dead_code)]
#[no_mangle]
pub extern "C" fn bls_cmdline_set (handler: CTraitReference, params: *const *const c_char) -> c_char {
  let handler = unsafe { std::mem::transmute::<CTraitReference, &mut dyn CmdlineHandler> (handler) };
  generic_cmdline_handler(handler, params, &|e, p| { e.cmdline_set(p) })

}

#[allow(dead_code)]
#[no_mangle]
pub extern "C" fn bls_cmdline_add (handler: CTraitReference, params: *const *const c_char) -> c_char {
  let handler = unsafe { std::mem::transmute::<CTraitReference, &mut dyn CmdlineHandler> (handler) };
  generic_cmdline_handler(handler, params, &|e, p| { e.cmdline_add(p) })
}

#[allow(dead_code)]
#[no_mangle]
pub extern "C" fn bls_cmdline_remove (handler: CTraitReference, params: *const *const c_char) -> c_char {
  let handler = unsafe { std::mem::transmute::<CTraitReference, &mut dyn CmdlineHandler> (handler) };
  generic_cmdline_handler(handler, params, &|e, p| { e.cmdline_remove(p) })
}

#[allow(dead_code)]
#[no_mangle]
pub extern "C" fn bls_cmdline_clear (handler: CTraitReference, params: *const *const c_char) -> c_char {
  let handler = unsafe { std::mem::transmute::<CTraitReference, &mut dyn CmdlineHandler> (handler) };
  generic_cmdline_handler(handler, params, &|e, p| { e.cmdline_clear(p) })
}