Files
Easytier/easytier-contrib/easytier-ffi/src/instance_api.rs
T
KKRainbow 9d965cae64 Add FFI JNI JSON RPC bridge (#2326)
* Add FFI JNI JSON RPC bridge
* Add FFI instance list API
2026-06-07 17:48:58 +08:00

367 lines
10 KiB
Rust

use std::ffi::{CString, c_char, c_int};
use easytier::common::config::{ConfigFileControl, ConfigLoader as _, TomlConfigLoader};
use crate::{
config_server::{
in_config_server_callback, remove_config_server_tracked_instance_ids,
wait_for_config_server_delivery,
},
data_plane::remove_data_plane_handles_by_instance_ids,
error::set_error_msg,
state::{
INSTANCE_MANAGER, INSTANCE_MUTATION_LOCK, INSTANCE_NAME_ID_MAP, instance_name_exists,
lock_remote_instance_mutation,
},
types::KeyValuePair,
};
/// # Safety
/// Set the tun fd
pub(crate) unsafe fn set_tun_fd(inst_name: *const c_char, fd: c_int) -> c_int {
let inst_name = unsafe {
assert!(!inst_name.is_null());
std::ffi::CStr::from_ptr(inst_name)
.to_string_lossy()
.into_owned()
};
if !INSTANCE_NAME_ID_MAP.contains_key(&inst_name) {
return -1;
}
let inst_id = *INSTANCE_NAME_ID_MAP
.get(&inst_name)
.as_ref()
.unwrap()
.value();
match INSTANCE_MANAGER.set_tun_fd(&inst_id, fd) {
Ok(_) => 0,
Err(_) => -1,
}
}
/// # Safety
/// Parse the config
pub(crate) unsafe fn parse_config(cfg_str: *const std::ffi::c_char) -> std::ffi::c_int {
let cfg_str = unsafe {
assert!(!cfg_str.is_null());
std::ffi::CStr::from_ptr(cfg_str)
.to_string_lossy()
.into_owned()
};
if let Err(e) = TomlConfigLoader::new_from_str(&cfg_str) {
set_error_msg(&format!("failed to parse config: {:?}", e));
return -1;
}
0
}
/// # Safety
/// Run the network instance
pub(crate) unsafe fn run_network_instance(cfg_str: *const std::ffi::c_char) -> std::ffi::c_int {
if in_config_server_callback() {
set_error_msg("cannot run network instance from config server callback");
return -1;
}
let cfg_str = unsafe {
assert!(!cfg_str.is_null());
std::ffi::CStr::from_ptr(cfg_str)
.to_string_lossy()
.into_owned()
};
let cfg = match TomlConfigLoader::new_from_str(&cfg_str) {
Ok(cfg) => cfg,
Err(e) => {
set_error_msg(&format!("failed to parse config: {}", e));
return -1;
}
};
let inst_name = cfg.get_inst_name();
wait_for_config_server_delivery();
let _remote_mutation_guard = lock_remote_instance_mutation();
let _mutation_guard = match INSTANCE_MUTATION_LOCK.lock() {
Ok(guard) => guard,
Err(err) => {
set_error_msg(&format!("failed to lock instance mutation: {}", err));
return -1;
}
};
if instance_name_exists(&inst_name) {
set_error_msg("instance already exists");
return -1;
}
let instance_id =
match INSTANCE_MANAGER.run_network_instance(cfg, false, ConfigFileControl::STATIC_CONFIG) {
Ok(id) => id,
Err(e) => {
set_error_msg(&format!("failed to start instance: {}", e));
return -1;
}
};
INSTANCE_NAME_ID_MAP.insert(inst_name, instance_id);
0
}
unsafe fn parse_instance_names(
inst_names: *const *const c_char,
length: usize,
) -> Option<Vec<String>> {
if length == 0 {
return Some(Vec::new());
}
if inst_names.is_null() {
set_error_msg("inst_names is null");
return None;
}
let names = unsafe { std::slice::from_raw_parts(inst_names, length) };
let mut parsed = Vec::with_capacity(length);
for (index, &name) in names.iter().enumerate() {
if name.is_null() {
set_error_msg(&format!("inst_names[{}] is null", index));
return None;
}
parsed.push(
unsafe { std::ffi::CStr::from_ptr(name) }
.to_string_lossy()
.into_owned(),
);
}
Some(parsed)
}
/// # Safety
/// Retain the network instance
pub(crate) unsafe fn retain_network_instance(
inst_names: *const *const std::ffi::c_char,
length: usize,
) -> std::ffi::c_int {
if in_config_server_callback() {
set_error_msg("cannot retain network instances from config server callback");
return -1;
}
wait_for_config_server_delivery();
let _remote_mutation_guard = lock_remote_instance_mutation();
let _mutation_guard = match INSTANCE_MUTATION_LOCK.lock() {
Ok(guard) => guard,
Err(err) => {
set_error_msg(&format!("failed to lock instance mutation: {}", err));
return -1;
}
};
if length == 0 {
let removed_ids = INSTANCE_MANAGER.list_network_instance_ids();
if let Err(e) = INSTANCE_MANAGER.delete_network_instance(removed_ids.clone()) {
set_error_msg(&format!("failed to delete instances: {}", e));
return -1;
}
remove_config_server_tracked_instance_ids(&removed_ids);
remove_data_plane_handles_by_instance_ids(&removed_ids);
INSTANCE_NAME_ID_MAP.clear();
return 0;
}
let Some(inst_names) = (unsafe { parse_instance_names(inst_names, length) }) else {
return -1;
};
let removed_ids = INSTANCE_MANAGER
.list_network_instance_ids()
.into_iter()
.filter(|id| {
INSTANCE_MANAGER
.get_instance_name(id)
.is_none_or(|name| !inst_names.contains(&name))
})
.collect::<Vec<_>>();
if let Err(e) = INSTANCE_MANAGER.delete_network_instance(removed_ids.clone()) {
set_error_msg(&format!("failed to delete instances: {}", e));
return -1;
}
remove_config_server_tracked_instance_ids(&removed_ids);
remove_data_plane_handles_by_instance_ids(&removed_ids);
INSTANCE_NAME_ID_MAP.retain(|k, _| inst_names.contains(k));
0
}
/// # Safety
/// Delete named network instances.
pub(crate) unsafe fn delete_network_instance(
inst_names: *const *const std::ffi::c_char,
length: usize,
) -> std::ffi::c_int {
if in_config_server_callback() {
set_error_msg("cannot delete network instances from config server callback");
return -1;
}
wait_for_config_server_delivery();
let _remote_mutation_guard = lock_remote_instance_mutation();
let _mutation_guard = match INSTANCE_MUTATION_LOCK.lock() {
Ok(guard) => guard,
Err(err) => {
set_error_msg(&format!("failed to lock instance mutation: {}", err));
return -1;
}
};
if length == 0 {
return 0;
}
let Some(inst_names) = (unsafe { parse_instance_names(inst_names, length) }) else {
return -1;
};
let removed_ids = inst_names
.iter()
.filter_map(|name| INSTANCE_NAME_ID_MAP.get(name).map(|id| *id.value()))
.collect::<Vec<_>>();
if let Err(e) = INSTANCE_MANAGER.delete_network_instance(removed_ids.clone()) {
set_error_msg(&format!("failed to delete instances: {}", e));
return -1;
}
remove_config_server_tracked_instance_ids(&removed_ids);
remove_data_plane_handles_by_instance_ids(&removed_ids);
for name in inst_names {
INSTANCE_NAME_ID_MAP.remove(&name);
}
0
}
/// # Safety
/// Collect the network infos
pub(crate) unsafe fn collect_network_infos(
infos: *mut KeyValuePair,
max_length: usize,
) -> std::ffi::c_int {
if in_config_server_callback() {
set_error_msg("cannot collect network infos from config server callback");
return -1;
}
if max_length == 0 {
return 0;
}
let infos = unsafe {
assert!(!infos.is_null());
std::slice::from_raw_parts_mut(infos, max_length)
};
let collected_infos = match INSTANCE_MANAGER.collect_network_infos_sync() {
Ok(infos) => infos,
Err(e) => {
set_error_msg(&format!("failed to collect network infos: {}", e));
return -1;
}
};
let mut index = 0;
for (instance_id, value) in collected_infos.iter() {
if index >= max_length {
break;
}
let Some(key) = INSTANCE_MANAGER.get_instance_name(instance_id) else {
continue;
};
// convert value to json string
let value = match serde_json::to_string(&value) {
Ok(value) => value,
Err(e) => {
set_error_msg(&format!("failed to serialize instance info: {}", e));
return -1;
}
};
infos[index] = KeyValuePair {
key: std::ffi::CString::new(key).unwrap().into_raw(),
value: std::ffi::CString::new(value).unwrap().into_raw(),
};
index += 1;
}
index as std::ffi::c_int
}
/// # Safety
/// List the instance names and IDs known by the FFI instance manager.
pub(crate) unsafe fn list_instance(infos: *mut KeyValuePair, max_length: usize) -> std::ffi::c_int {
if in_config_server_callback() {
set_error_msg("cannot list instances from config server callback");
return -1;
}
if max_length == 0 {
return 0;
}
if infos.is_null() {
set_error_msg("infos is null");
return -1;
}
let infos = unsafe { std::slice::from_raw_parts_mut(infos, max_length) };
let mut instances = INSTANCE_MANAGER
.list_network_instance_ids()
.into_iter()
.filter_map(|id| {
INSTANCE_MANAGER
.get_instance_name(&id)
.map(|name| (name, id))
})
.collect::<Vec<_>>();
instances.sort_by(|(left_name, left_id), (right_name, right_id)| {
left_name
.cmp(right_name)
.then_with(|| left_id.to_string().cmp(&right_id.to_string()))
});
let encoded_instances = match instances
.into_iter()
.take(max_length)
.map(|(name, id)| {
let key = CString::new(name)
.map_err(|err| format!("failed to encode instance name: {}", err))?;
let value = CString::new(id.to_string())
.map_err(|err| format!("failed to encode instance id: {}", err))?;
Ok((key, value))
})
.collect::<Result<Vec<_>, String>>()
{
Ok(value) => value,
Err(err) => {
set_error_msg(&err);
return -1;
}
};
let count = encoded_instances.len();
for (index, (key, value)) in encoded_instances.into_iter().enumerate() {
infos[index] = KeyValuePair {
key: key.into_raw(),
value: value.into_raw(),
};
}
count as std::ffi::c_int
}