use crate::api::state::{c, convert_runtime_information_to_c, rust};
use crate::api::{state, to_c_string};
use crate::cli::check_ockam_executable;
use crate::state::AppState;
use ockam_api::cli_state::CliState;
use ockam_api::cloud::email_address::EmailAddress;
use ockam_core::Address;
use std::ffi::c_char;
use std::pin::Pin;
use tracing::{error, info};
static mut APPLICATION_STATE: Option<Pin<Box<AppState>>> = None;
const ERROR_NOT_INITIALIZED: &str =
"initialize_application must be called before any other function";
#[no_mangle]
#[allow(static_mut_refs)]
extern "C" fn initialize_application(
application_state_callback: unsafe extern "C" fn(
state: super::state::c::ApplicationState,
) -> (),
notification_callback: unsafe extern "C" fn(
notification: super::notification::c::Notification,
) -> (),
) -> bool {
unsafe {
if APPLICATION_STATE.is_some() {
panic!("initialize_application must be called only once")
}
}
for &key in &[
"OCKAM_CONTROLLER_ADDR",
"OCKAM_CONTROLLER_IDENTITY_ID",
"OCKAM_HOME",
] {
match std::env::var(key) {
Ok(value) => info!("{key} has been set: {value}"),
Err(_) => info!("{key} is not set"),
}
}
let result = AppState::new(
super::state::rust::ApplicationStateCallback::new(application_state_callback),
super::notification::rust::NotificationCallback::new(notification_callback),
);
let app_state = match result {
Ok(app_state) => app_state,
Err(err) => {
eprintln!("Failed to load the local Ockam configuration: {err}");
return false;
}
};
app_state.setup_logging_tracing();
#[cfg(target_os = "macos")]
crate::cli::add_homebrew_to_path();
if let Err(err) = check_ockam_executable() {
error!(?err, "Couldn't find the ockam executable");
app_state.notify(super::notification::rust::Notification {
kind: super::notification::rust::Kind::Error,
title: "Couldn't find the ockam executable".to_string(),
message: "Please install ockam and make sure it is in your PATH".to_string(),
});
std::thread::sleep(std::time::Duration::from_secs(1));
std::process::exit(1);
}
unsafe {
APPLICATION_STATE.replace(Box::pin(app_state));
}
let app_state = unsafe { APPLICATION_STATE.as_ref() }.expect(ERROR_NOT_INITIALIZED);
app_state.context().runtime().spawn(async move {
app_state.publish_state().await;
app_state.load_model_state().await;
});
true
}
#[no_mangle]
#[allow(static_mut_refs)]
extern "C" fn accept_invitation(id: *const c_char) {
let id = unsafe { std::ffi::CStr::from_ptr(id).to_str().unwrap().to_string() };
let app_state = unsafe { APPLICATION_STATE.as_ref() }.expect(ERROR_NOT_INITIALIZED);
app_state.context().runtime().spawn(async {
let result = app_state.accept_invitation(id).await;
if let Err(err) = result {
error!(?err, "Couldn't accept the invitation");
}
});
}
#[no_mangle]
#[allow(static_mut_refs)]
extern "C" fn ignore_invitation(id: *const c_char) {
let id = unsafe { std::ffi::CStr::from_ptr(id).to_str().unwrap().to_string() };
let app_state = unsafe { APPLICATION_STATE.as_ref() }.expect(ERROR_NOT_INITIALIZED);
app_state.context().runtime().spawn(async {
let result = app_state.ignore_invitation(id).await;
if let Err(err) = result {
error!(?err, "Couldn't accept the invitation");
}
});
}
#[no_mangle]
#[allow(static_mut_refs)]
extern "C" fn shutdown_application() {
let app_state = unsafe { APPLICATION_STATE.as_ref() };
if let Some(app_state) = app_state {
app_state.shutdown();
} else {
std::process::exit(0);
}
}
#[no_mangle]
#[allow(static_mut_refs)]
extern "C" fn share_local_service(name: *const c_char, emails: *const c_char) -> *const c_char {
let worker_addr = unsafe { std::ffi::CStr::from_ptr(name).to_str().unwrap().to_string() };
let worker_addr: Address = worker_addr.into();
let emails: Vec<String> = unsafe {
std::ffi::CStr::from_ptr(emails)
.to_str()
.unwrap()
.to_string()
}
.split(';')
.map(|s| s.to_string())
.filter(|s| !s.is_empty())
.collect();
let app_state = unsafe { APPLICATION_STATE.as_ref() }.expect(ERROR_NOT_INITIALIZED);
let result = app_state.context().runtime().block_on(async {
let mut result = Ok(());
for email in emails {
match EmailAddress::parse(&email) {
Ok(email_address) => {
result = app_state
.create_service_invitation_by_alias(
&app_state.context(),
email_address,
&worker_addr,
)
.await;
}
Err(e) => {
error!("the email address {email} is not a valid email address");
result = Err(e.to_string())
}
}
app_state.publish_state().await;
if result.is_err() {
break;
}
}
result
});
match result {
Ok(_) => std::ptr::null(),
Err(err) => to_c_string(err.to_string()),
}
}
#[no_mangle]
#[allow(static_mut_refs)]
extern "C" fn enable_accepted_service(invitation_id: *const c_char) {
let invitation_id = unsafe {
std::ffi::CStr::from_ptr(invitation_id)
.to_str()
.unwrap()
.to_string()
};
let app_state = unsafe { APPLICATION_STATE.as_ref() }.expect(ERROR_NOT_INITIALIZED);
app_state.context().runtime().spawn(async move {
let result = app_state.enable_tcp_inlet(&invitation_id).await;
if let Err(err) = result {
error!(?err, "Couldn't enable the service");
}
});
}
#[no_mangle]
#[allow(static_mut_refs)]
extern "C" fn disable_accepted_service(invitation_id: *const c_char) {
let invitation_id = unsafe {
std::ffi::CStr::from_ptr(invitation_id)
.to_str()
.unwrap()
.to_string()
};
let app_state = unsafe { APPLICATION_STATE.as_ref() }.expect(ERROR_NOT_INITIALIZED);
app_state.context().runtime().spawn(async move {
let result = app_state.disable_tcp_inlet(&invitation_id).await;
if let Err(err) = result {
error!(?err, "Couldn't disable the service");
}
});
}
#[no_mangle]
#[allow(static_mut_refs)]
extern "C" fn delete_local_service(worker_addr: *const c_char) {
let worker_addr = unsafe {
std::ffi::CStr::from_ptr(worker_addr)
.to_str()
.unwrap()
.to_string()
};
let app_state = unsafe { APPLICATION_STATE.as_ref() }.expect(ERROR_NOT_INITIALIZED);
app_state.context().runtime().spawn(async {
let result = app_state.tcp_outlet_delete(worker_addr.into()).await;
if let Err(err) = result {
error!(?err, "Couldn't delete the local service");
}
});
}
#[no_mangle]
#[allow(static_mut_refs)]
extern "C" fn create_local_service(
worker_addr: *const c_char,
address: *const c_char,
) -> *const c_char {
let worker_addr = unsafe {
std::ffi::CStr::from_ptr(worker_addr)
.to_str()
.unwrap()
.to_string()
};
let socket_addr = unsafe {
std::ffi::CStr::from_ptr(address)
.to_str()
.unwrap()
.to_string()
};
let app_state = unsafe { APPLICATION_STATE.as_ref() }.expect(ERROR_NOT_INITIALIZED);
let result = app_state.context().runtime().block_on(async {
let result = app_state.tcp_outlet_create(worker_addr, socket_addr).await;
app_state.publish_state().await;
result
});
match result {
Ok(_) => std::ptr::null(),
Err(err) => to_c_string(format!("{}", err)),
}
}
#[no_mangle]
#[allow(static_mut_refs)]
extern "C" fn reset_application_state() {
let app_state = unsafe { APPLICATION_STATE.as_ref() };
match app_state {
Some(app_state) => {
app_state.context().runtime().block_on(async move {
let result = app_state.reset().await;
if let Err(err) = result {
error!(?err, "Cannot reset the application state");
}
});
}
None => {
CliState::backup_and_reset().expect(
"Failed to initialize CliState. Try to manually remove the '~/.ockam' directory",
);
}
}
}
#[no_mangle]
#[allow(static_mut_refs)]
extern "C" fn enroll_user() {
let app_state = unsafe { APPLICATION_STATE.as_ref() }.expect(ERROR_NOT_INITIALIZED);
app_state
.context()
.runtime()
.spawn(async move { app_state.enroll_user().await });
}
#[no_mangle]
#[allow(static_mut_refs)]
extern "C" fn application_state_snapshot() -> super::state::c::ApplicationState {
let app_state = unsafe { APPLICATION_STATE.as_ref() }.expect(ERROR_NOT_INITIALIZED);
let public_rust_state = app_state
.context()
.runtime()
.block_on(async { app_state.snapshot().await })
.expect("Cannot retrieve application state");
super::state::convert_application_state_to_c(public_rust_state)
}
#[no_mangle]
extern "C" fn runtime_information() -> c::RuntimeInformation {
let home = std::env::var("OCKAM_HOME").ok();
let controller_addr = std::env::var("OCKAM_CONTROLLER_ADDR").ok();
let controller_identity = std::env::var("OCKAM_CONTROLLER_IDENTITY_ID").ok();
let info = rust::RuntimeInformation {
version: env!("CARGO_PKG_VERSION").to_string(),
commit: env!("GIT_HASH").to_string(),
home,
controller_addr,
controller_identity,
};
convert_runtime_information_to_c(info)
}
#[no_mangle]
extern "C" fn free_runtime_information(information: c::RuntimeInformation) {
state::free_runtime_information_free(information);
}