blob: 766807acefdd6e4532715fea691b9338c9342653 [file] [log] [blame]
use crate::load_protos;
use crate::{Flag, FlagSource};
use crate::{FlagPermission, FlagValue, ValuePickedFrom};
use aconfigd_protos::{
ProtoFlagQueryReturnMessage, ProtoListStorageMessage, ProtoListStorageMessageMsg,
ProtoStorageRequestMessage, ProtoStorageRequestMessageMsg, ProtoStorageRequestMessages,
ProtoStorageReturnMessage, ProtoStorageReturnMessageMsg, ProtoStorageReturnMessages,
};
use anyhow::anyhow;
use anyhow::Result;
use protobuf::Message;
use protobuf::SpecialFields;
use std::collections::HashMap;
use std::io::{Read, Write};
use std::net::Shutdown;
use std::os::unix::net::UnixStream;
pub struct AconfigStorageSource {}
static ACONFIGD_SYSTEM_SOCKET_NAME: &str = "/dev/socket/aconfigd_system";
static ACONFIGD_MAINLINE_SOCKET_NAME: &str = "/dev/socket/aconfigd_mainline";
enum AconfigdSocket {
System,
Mainline,
}
impl AconfigdSocket {
pub fn name(&self) -> &str {
match self {
AconfigdSocket::System => ACONFIGD_SYSTEM_SOCKET_NAME,
AconfigdSocket::Mainline => ACONFIGD_MAINLINE_SOCKET_NAME,
}
}
}
fn load_flag_to_container() -> Result<HashMap<String, String>> {
Ok(load_protos::load()?.into_iter().map(|p| (p.qualified_name(), p.container)).collect())
}
fn convert(msg: ProtoFlagQueryReturnMessage, containers: &HashMap<String, String>) -> Result<Flag> {
let (value, value_picked_from) = match (
&msg.boot_flag_value,
msg.default_flag_value,
msg.local_flag_value,
msg.has_local_override,
) {
(_, _, Some(local), Some(has_local)) if has_local => {
(FlagValue::try_from(local.as_str())?, ValuePickedFrom::Local)
}
(Some(boot), Some(default), _, _) => {
let value = FlagValue::try_from(boot.as_str())?;
if *boot == default {
(value, ValuePickedFrom::Default)
} else {
(value, ValuePickedFrom::Server)
}
}
_ => return Err(anyhow!("missing override")),
};
let staged_value = match (msg.boot_flag_value, msg.server_flag_value, msg.has_server_override) {
(Some(boot), Some(server), _) if boot == server => None,
(Some(boot), Some(server), Some(has_server)) if boot != server && has_server => {
Some(FlagValue::try_from(server.as_str())?)
}
_ => None,
};
let permission = match msg.is_readwrite {
Some(is_readwrite) => {
if is_readwrite {
FlagPermission::ReadWrite
} else {
FlagPermission::ReadOnly
}
}
None => return Err(anyhow!("missing permission")),
};
let name = msg.flag_name.ok_or(anyhow!("missing flag name"))?;
let package = msg.package_name.ok_or(anyhow!("missing package name"))?;
let qualified_name = format!("{package}.{name}");
Ok(Flag {
name,
package,
value,
permission,
value_picked_from,
staged_value,
container: containers
.get(&qualified_name)
.cloned()
.unwrap_or_else(|| "<no container>".to_string())
.to_string(),
// TODO: remove once DeviceConfig is not in the CLI.
namespace: "-".to_string(),
})
}
fn read_from_socket(socket: AconfigdSocket) -> Result<Vec<ProtoFlagQueryReturnMessage>> {
let messages = ProtoStorageRequestMessages {
msgs: vec![ProtoStorageRequestMessage {
msg: Some(ProtoStorageRequestMessageMsg::ListStorageMessage(ProtoListStorageMessage {
msg: Some(ProtoListStorageMessageMsg::All(true)),
special_fields: SpecialFields::new(),
})),
special_fields: SpecialFields::new(),
}],
special_fields: SpecialFields::new(),
};
let mut socket = UnixStream::connect(socket.name())?;
let message_buffer = messages.write_to_bytes()?;
let mut message_length_buffer: [u8; 4] = [0; 4];
let message_size = &message_buffer.len();
message_length_buffer[0] = (message_size >> 24) as u8;
message_length_buffer[1] = (message_size >> 16) as u8;
message_length_buffer[2] = (message_size >> 8) as u8;
message_length_buffer[3] = *message_size as u8;
socket.write_all(&message_length_buffer)?;
socket.write_all(&message_buffer)?;
socket.shutdown(Shutdown::Write)?;
let mut response_length_buffer: [u8; 4] = [0; 4];
socket.read_exact(&mut response_length_buffer)?;
let response_length = u32::from_be_bytes(response_length_buffer) as usize;
let mut response_buffer = vec![0; response_length];
socket.read_exact(&mut response_buffer)?;
let response: ProtoStorageReturnMessages =
protobuf::Message::parse_from_bytes(&response_buffer)?;
match response.msgs.as_slice() {
[ProtoStorageReturnMessage {
msg: Some(ProtoStorageReturnMessageMsg::ListStorageMessage(list_storage_message)),
..
}] => Ok(list_storage_message.flags.clone()),
_ => Err(anyhow!("unexpected response from aconfigd")),
}
}
impl FlagSource for AconfigStorageSource {
fn list_flags() -> Result<Vec<Flag>> {
let containers = load_flag_to_container()?;
let system_messages = read_from_socket(AconfigdSocket::System);
let mainline_messages = read_from_socket(AconfigdSocket::Mainline);
let mut all_messages = vec![];
if let Ok(system_messages) = system_messages {
all_messages.extend_from_slice(&system_messages);
}
if let Ok(mainline_messages) = mainline_messages {
all_messages.extend_from_slice(&mainline_messages);
}
all_messages
.into_iter()
.map(|query_message| convert(query_message.clone(), &containers))
.collect()
}
fn override_flag(_namespace: &str, _qualified_name: &str, _value: &str) -> Result<()> {
todo!()
}
}