use libc::{c_char, dev_t, getuid, ino_t};
use haiku_sys::{B_MIME_TYPE_LENGTH, B_FILE_NAME_LENGTH, port_id, team_id, thread_id, status_t};
use haiku_sys::errors::{B_ERROR, B_OK};
use std::{mem, ptr};
use std::result;
use std::str::{Utf8Error, from_utf8};
use ::app::message::Message;
use ::app::messenger::Messenger;
use ::kernel::helpers;
use ::kernel::ports::Port;
use ::kernel::teams::Team;
use ::support::{ErrorKind, Flattenable, HaikuError, Result};
use ::storage::sys::entry_ref;
pub(crate) struct LaunchRoster {
messenger: Messenger
}
impl LaunchRoster {
fn init() -> LaunchRoster {
let port = Port::find("system:launch_daemon").expect("Cannot find the launch daemon");
let roster_messenger = Messenger::from_port(&port).expect("Cannot connect to the launch daemon");
LaunchRoster { messenger: roster_messenger }
}
pub(crate) fn get_data(&self, signature: &str) -> Result<Message> {
let constant: u32 = haiku_constant!('l','n','d','a');
let mut message = Message::new(constant);
message.add_data("name", &String::from(signature)).unwrap();
let uid = unsafe { getuid() };
message.add_data("user", &(uid as i32)).unwrap();
let response = self.messenger.send_and_wait_for_reply(message, None)?;
Ok(response)
}
}
pub(crate) enum ApplicationRegistrationStatus {
Registered(AppInfo),
PreRegistered(AppInfo),
NotRegistered
}
pub(crate) enum ApplicationRegistrationResult {
Registered,
PreRegistered(i32),
OtherInstance(team_id, i32)
}
pub struct Roster {
messenger: Messenger
}
impl Roster {
pub fn get_app_list(&self) -> Option<Vec<Team>> {
let request = Message::new(haiku_constant!('r','g','a','l'));
let response = self.messenger.send_and_wait_for_reply(request, None);
if response.is_err() {
return None;
}
let response = response.unwrap();
if response.what() == haiku_constant!('r','g','s','u') {
let count = match response.get_info("teams") {
Some(info) => info.1,
None => return None
};
let mut result: Vec<Team> = Vec::with_capacity(count);
for index in 0..count {
let team = response.find_data::<i32>("teams", index).unwrap();
result.push(Team::from(team).unwrap());
}
return Some(result);
}
return None;
}
pub fn get_running_app_info(&self, team: &Team) -> Option<AppInfo> {
let mut request = Message::new(haiku_constant!('r','g','a','i'));
request.add_data("team", &team.get_team_id()).unwrap();
let response = self.messenger.send_and_wait_for_reply(request, None);
if response.is_err() {
println!("Response.is err");
return None;
}
let response = response.unwrap();
if response.what() == haiku_constant!('r','g','s','u') {
let flat_app_info = response.find_data::<FlatAppInfo>("app_info", 0).unwrap();
return Some(flat_app_info.to_app_info());
}
return None;
}
pub fn get_app_info(&self, signature: &str) -> Option<AppInfo> {
let mut request = Message::new(haiku_constant!('r','g','a','i'));
request.add_data("signature", &String::from(signature)).unwrap();
let response = self.messenger.send_and_wait_for_reply(request, None);
if response.is_err() {
return None;
}
let response = response.unwrap();
if response.what() == haiku_constant!('r','g','s','u') {
let flat_app_info = response.find_data::<FlatAppInfo>("app_info", 0).unwrap();
return Some(flat_app_info.to_app_info());
}
return None;
}
pub(crate) fn add_application(&self, signature: &String, entry: &entry_ref,
flags: u32, team: team_id, thread: thread_id, port: port_id,
full_registration: bool) -> Result<ApplicationRegistrationResult>
{
let mut request = Message::new(haiku_constant!('r','g','a','a'));
request.add_data("signature", signature).unwrap();
request.add_data("ref", entry).unwrap();
request.add_data("flags", &flags).unwrap();
request.add_data("team", &team).unwrap();
request.add_data("thread", &thread).unwrap();
request.add_data("port", &port).unwrap();
request.add_data("full_registration", &full_registration).unwrap();
let response = self.messenger.send_and_wait_for_reply(request, None)?;
if response.what() == B_REG_SUCCESS {
if !full_registration && team < 0 {
let token: i32 = match response.find_data("token", 0) {
Ok(token) => token,
Err(_) => return Err(HaikuError::new(ErrorKind::InvalidData, "No token for preregistration by Registrar"))
};
Ok(ApplicationRegistrationResult::PreRegistered(token))
} else {
Ok(ApplicationRegistrationResult::Registered)
}
} else {
let token: Result<i32> = response.find_data("token", 0);
let team: Result<team_id> = response.find_data("team", 0);
if token.is_ok() && team.is_ok() {
Ok(ApplicationRegistrationResult::OtherInstance(team.unwrap(), token.unwrap()))
} else {
Err(HaikuError::new(ErrorKind::InvalidData, "Invalid registration response by Registrar"))
}
}
}
pub(crate) fn is_application_registered(&self, entry: &entry_ref,
team: team_id, token: u32) -> Result<ApplicationRegistrationStatus>
{
let mut request = Message::new(haiku_constant!('r','g','i','p'));
request.add_data("ref", entry).unwrap();
request.add_data("team", &team).unwrap();
request.add_data("token", &(token as i32)).unwrap();
let response = self.messenger.send_and_wait_for_reply(request, None)?;
if response.what() == B_REG_SUCCESS {
let registered: bool = response.find_data("registered", 0).unwrap_or(false);
let pre_registered: bool = response.find_data("pre-registered", 0).unwrap_or(false);
let app_info: Option<AppInfo> = match response.find_data::<FlatAppInfo>("app_info", 0) {
Ok(info) => Some(info.to_app_info()),
Err(_) => None
};
if (pre_registered || registered) && app_info.is_none() {
Err(HaikuError::new(ErrorKind::InvalidData, "The Registrar returned an invalid response"))
} else if pre_registered {
Ok(ApplicationRegistrationStatus::PreRegistered(app_info.unwrap()))
} else if registered {
Ok(ApplicationRegistrationStatus::Registered(app_info.unwrap()))
} else {
Ok(ApplicationRegistrationStatus::NotRegistered)
}
} else {
let errno: i32 = response.find_data("error", 0).unwrap_or(-1);
Err(HaikuError::new(ErrorKind::InvalidData, format!("The Registrar returned an error on request: {}", errno)))
}
}
pub(crate) fn remove_application(&self, team: team_id) -> Result<()> {
let mut request = Message::new(haiku_constant!('r','g','r','a'));
request.add_data("team", &team).unwrap();
let response = self.messenger.send_and_wait_for_reply(request, None)?;
if response.what() == B_REG_SUCCESS {
Ok(())
} else {
let error: status_t = response.find_data("error", 0).unwrap_or(B_ERROR);
Err(HaikuError::from_raw_os_error(error))
}
}
}
const B_REG_APP_INFO_TYPE: u32 = haiku_constant!('r','g','a','i');
const B_REG_SUCCESS: u32 = haiku_constant!('r','g','s','u');
#[repr(packed)]
struct FlatAppInfo {
pub thread: thread_id,
pub team: team_id,
pub port: port_id,
pub flags: u32,
pub ref_device: dev_t,
pub ref_directory: ino_t,
signature: [u8; B_MIME_TYPE_LENGTH],
ref_name: [c_char; B_FILE_NAME_LENGTH + 1]
}
impl FlatAppInfo {
fn to_app_info(&self) -> AppInfo {
let signature = match FlatAppInfo::str_from_array_with_or_without_nul(&self.signature) {
Ok(value) => String::from(value),
Err(_) => String::new()
};
let path = match helpers::get_path_for_entry_ref(self.ref_device, self.ref_directory, self.ref_name.as_ptr()) {
Ok(value) => String::from(value),
Err(_) => String::new()
};
AppInfo {
thread: self.thread,
team: self.team,
port: self.port,
flags: self.flags,
path: path,
signature: signature
}
}
fn str_from_array_with_or_without_nul(buf: &[u8]) -> result::Result<&str, Utf8Error> {
let len = buf.iter()
.enumerate()
.find(|&(_, &byte)| byte == 0)
.map_or_else(|| buf.len(), |(len, _)| len);
from_utf8(&buf[..len])
}
}
impl Flattenable<FlatAppInfo> for FlatAppInfo {
fn type_code() -> u32 {
B_REG_APP_INFO_TYPE
}
fn is_fixed_size() -> bool {
true
}
fn flattened_size(&self) -> usize {
mem::size_of::<FlatAppInfo>()
}
fn flatten(&self) -> Vec<u8> {
unimplemented!();
}
fn unflatten(buffer: &[u8]) -> Result<FlatAppInfo> {
if mem::size_of::<FlatAppInfo>() != buffer.len() {
return Err(HaikuError::new(ErrorKind::InvalidData, "the buffer is smaller than the flattened app info struct"));
}
let app_info: FlatAppInfo = unsafe { ptr::read(buffer.as_ptr() as *const _) };
Ok(app_info)
}
}
const B_MULTIPLE_LAUNCH: u32 = 0x1;
const B_EXCLUSIVE_LAUNCH: u32 = 0x2;
const B_BACKGROUND_APP: u32 = 0x4;
const B_ARGV_ONLY: u32 = 0x8;
pub struct AppInfo {
pub thread: thread_id,
pub team: team_id,
pub port: port_id,
pub flags: u32,
pub path: String,
pub signature: String,
}
pub enum LaunchType {
SingleLaunch,
MultipleLaunch,
ExclusiveLaunch
}
impl AppInfo {
pub fn launch_type(&self) -> LaunchType {
if self.flags & B_MULTIPLE_LAUNCH != 0 {
LaunchType::MultipleLaunch
} else if self.flags & B_EXCLUSIVE_LAUNCH != 0 {
LaunchType::ExclusiveLaunch
} else {
LaunchType::SingleLaunch
}
}
pub fn is_background(&self) -> bool {
self.flags & B_BACKGROUND_APP != 0
}
pub fn is_argv_only(&self) -> bool {
self.flags & B_ARGV_ONLY != 0
}
}
lazy_static! {
pub(crate) static ref LAUNCH_ROSTER: LaunchRoster = {
LaunchRoster::init()
};
}
lazy_static! {
pub static ref ROSTER: Roster = {
let roster_data = LAUNCH_ROSTER.get_data("application/x-vnd.haiku-registrar").expect("Cannot connect to the launch_daemon to get info about the registrar!");
if roster_data.what() != (B_OK as u32) {
panic!("Cannot connect to the registrar");
}
let port: port_id = roster_data.find_data("port", 0).expect("Cannot find port info for registrar");
Roster{ messenger: Messenger::from_port_id(port).unwrap() }
};
}
#[test]
fn test_roster_get_app_list() {
let app_list = ROSTER.get_app_list().unwrap();
assert!(app_list.len() != 0);
}