blob: 4ce0d35ba1bf6b0f6a6f6662e39a019a13cb9eb1 [file] [log] [blame]
Ted Bauer4dbf58a2024-02-08 18:46:52 +00001/*
2 * Copyright (C) 2024 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17//! `aflags` is a device binary to read and write aconfig flags.
18
Ted Bauera98448f2024-03-06 14:01:19 -050019use anyhow::{anyhow, ensure, Result};
Ted Bauer4dbf58a2024-02-08 18:46:52 +000020use clap::Parser;
21
22mod device_config_source;
23use device_config_source::DeviceConfigSource;
24
Ted Bauer6d4db662024-03-06 18:08:32 -050025mod aconfig_storage_source;
26use aconfig_storage_source::AconfigStorageSource;
27
Ted Bauer206d44a2024-03-15 17:04:06 +000028mod load_protos;
29
Ted Bauer46d758b2024-03-12 19:28:58 +000030#[derive(Clone, PartialEq, Debug)]
Ted Bauer4dbf58a2024-02-08 18:46:52 +000031enum FlagPermission {
32 ReadOnly,
33 ReadWrite,
34}
35
36impl ToString for FlagPermission {
37 fn to_string(&self) -> String {
38 match &self {
39 Self::ReadOnly => "read-only".into(),
40 Self::ReadWrite => "read-write".into(),
41 }
42 }
43}
44
Ted Bauer46d758b2024-03-12 19:28:58 +000045#[derive(Clone, Debug)]
Ted Bauer4dbf58a2024-02-08 18:46:52 +000046enum ValuePickedFrom {
47 Default,
48 Server,
49}
50
51impl ToString for ValuePickedFrom {
52 fn to_string(&self) -> String {
53 match &self {
54 Self::Default => "default".into(),
55 Self::Server => "server".into(),
56 }
57 }
58}
59
Mårten Kongstadf0c594d2024-03-08 10:20:32 +010060#[derive(Clone, Copy, PartialEq, Eq, Debug)]
61enum FlagValue {
62 Enabled,
63 Disabled,
64}
65
66impl TryFrom<&str> for FlagValue {
67 type Error = anyhow::Error;
68
69 fn try_from(value: &str) -> std::result::Result<Self, Self::Error> {
70 match value {
71 "true" | "enabled" => Ok(Self::Enabled),
72 "false" | "disabled" => Ok(Self::Disabled),
73 _ => Err(anyhow!("cannot convert string '{}' to FlagValue", value)),
74 }
75 }
76}
77
78impl ToString for FlagValue {
79 fn to_string(&self) -> String {
80 match &self {
81 Self::Enabled => "enabled".into(),
82 Self::Disabled => "disabled".into(),
83 }
84 }
85}
86
Ted Bauer46d758b2024-03-12 19:28:58 +000087#[derive(Clone, Debug)]
Ted Bauer4dbf58a2024-02-08 18:46:52 +000088struct Flag {
89 namespace: String,
90 name: String,
91 package: String,
92 container: String,
Mårten Kongstadf0c594d2024-03-08 10:20:32 +010093 value: FlagValue,
Ted Bauer46d758b2024-03-12 19:28:58 +000094 staged_value: Option<FlagValue>,
Ted Bauer4dbf58a2024-02-08 18:46:52 +000095 permission: FlagPermission,
96 value_picked_from: ValuePickedFrom,
97}
98
Ted Bauer84883bd2024-03-04 22:45:29 +000099impl Flag {
100 fn qualified_name(&self) -> String {
101 format!("{}.{}", self.package, self.name)
102 }
Ted Bauer46d758b2024-03-12 19:28:58 +0000103
104 fn display_staged_value(&self) -> String {
105 match self.staged_value {
106 Some(v) => format!("(->{})", v.to_string()),
107 None => "-".to_string(),
108 }
109 }
Ted Bauer84883bd2024-03-04 22:45:29 +0000110}
111
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000112trait FlagSource {
113 fn list_flags() -> Result<Vec<Flag>>;
Ted Bauer84883bd2024-03-04 22:45:29 +0000114 fn override_flag(namespace: &str, qualified_name: &str, value: &str) -> Result<()>;
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000115}
116
Ted Bauer6d4db662024-03-06 18:08:32 -0500117enum FlagSourceType {
118 DeviceConfig,
119 AconfigStorage,
120}
121
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000122const ABOUT_TEXT: &str = "Tool for reading and writing flags.
123
124Rows in the table from the `list` command follow this format:
125
126 package flag_name value provenance permission container
127
128 * `package`: package set for this flag in its .aconfig definition.
129 * `flag_name`: flag name, also set in definition.
130 * `value`: the value read from the flag.
Ted Bauer46d758b2024-03-12 19:28:58 +0000131 * `staged_value`: the value on next boot:
132 + `-`: same as current value
133 + `(->enabled) flipped to enabled on boot.
134 + `(->disabled) flipped to disabled on boot.
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000135 * `provenance`: one of:
136 + `default`: the flag value comes from its build-time default.
137 + `server`: the flag value comes from a server override.
138 * `permission`: read-write or read-only.
139 * `container`: the container for the flag, configured in its definition.
140";
141
142#[derive(Parser, Debug)]
143#[clap(long_about=ABOUT_TEXT)]
144struct Cli {
145 #[clap(subcommand)]
146 command: Command,
147}
148
149#[derive(Parser, Debug)]
150enum Command {
151 /// List all aconfig flags on this device.
Ted Bauer6d4db662024-03-06 18:08:32 -0500152 List {
153 /// Read from the new flag storage.
154 #[clap(long)]
155 use_new_storage: bool,
156 },
Ted Bauer84883bd2024-03-04 22:45:29 +0000157
158 /// Enable an aconfig flag on this device, on the next boot.
159 Enable {
160 /// <package>.<flag_name>
161 qualified_name: String,
162 },
163
164 /// Disable an aconfig flag on this device, on the next boot.
165 Disable {
166 /// <package>.<flag_name>
167 qualified_name: String,
168 },
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000169}
170
171struct PaddingInfo {
Mårten Kongstadd408e962024-03-07 13:56:30 +0100172 longest_flag_col: usize,
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000173 longest_val_col: usize,
Ted Bauer46d758b2024-03-12 19:28:58 +0000174 longest_staged_val_col: usize,
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000175 longest_value_picked_from_col: usize,
176 longest_permission_col: usize,
177}
178
179fn format_flag_row(flag: &Flag, info: &PaddingInfo) -> String {
Mårten Kongstadd408e962024-03-07 13:56:30 +0100180 let full_name = flag.qualified_name();
181 let p0 = info.longest_flag_col + 1;
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000182
Mårten Kongstadf0c594d2024-03-08 10:20:32 +0100183 let val = flag.value.to_string();
Mårten Kongstadd408e962024-03-07 13:56:30 +0100184 let p1 = info.longest_val_col + 1;
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000185
Ted Bauer46d758b2024-03-12 19:28:58 +0000186 let staged_val = flag.display_staged_value();
187 let p2 = info.longest_staged_val_col + 1;
188
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000189 let value_picked_from = flag.value_picked_from.to_string();
Ted Bauer46d758b2024-03-12 19:28:58 +0000190 let p3 = info.longest_value_picked_from_col + 1;
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000191
192 let perm = flag.permission.to_string();
Ted Bauer46d758b2024-03-12 19:28:58 +0000193 let p4 = info.longest_permission_col + 1;
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000194
195 let container = &flag.container;
196
Ted Bauer46d758b2024-03-12 19:28:58 +0000197 format!(
198 "{full_name:p0$}{val:p1$}{staged_val:p2$}{value_picked_from:p3$}{perm:p4$}{container}\n"
199 )
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000200}
201
Ted Bauer84883bd2024-03-04 22:45:29 +0000202fn set_flag(qualified_name: &str, value: &str) -> Result<()> {
Ted Bauera98448f2024-03-06 14:01:19 -0500203 ensure!(nix::unistd::Uid::current().is_root(), "must be root to mutate flags");
204
Ted Bauer84883bd2024-03-04 22:45:29 +0000205 let flags_binding = DeviceConfigSource::list_flags()?;
206 let flag = flags_binding.iter().find(|f| f.qualified_name() == qualified_name).ok_or(
207 anyhow!("no aconfig flag '{qualified_name}'. Does the flag have an .aconfig definition?"),
208 )?;
209
Ted Bauera98448f2024-03-06 14:01:19 -0500210 ensure!(flag.permission == FlagPermission::ReadWrite,
211 format!("could not write flag '{qualified_name}', it is read-only for the current release configuration."));
Ted Bauer84883bd2024-03-04 22:45:29 +0000212
213 DeviceConfigSource::override_flag(&flag.namespace, qualified_name, value)?;
214
215 Ok(())
216}
217
Ted Bauer6d4db662024-03-06 18:08:32 -0500218fn list(source_type: FlagSourceType) -> Result<String> {
219 let flags = match source_type {
220 FlagSourceType::DeviceConfig => DeviceConfigSource::list_flags()?,
221 FlagSourceType::AconfigStorage => AconfigStorageSource::list_flags()?,
222 };
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000223 let padding_info = PaddingInfo {
Mårten Kongstadd408e962024-03-07 13:56:30 +0100224 longest_flag_col: flags.iter().map(|f| f.qualified_name().len()).max().unwrap_or(0),
Mårten Kongstadf0c594d2024-03-08 10:20:32 +0100225 longest_val_col: flags.iter().map(|f| f.value.to_string().len()).max().unwrap_or(0),
Ted Bauer46d758b2024-03-12 19:28:58 +0000226 longest_staged_val_col: flags
227 .iter()
228 .map(|f| f.display_staged_value().len())
229 .max()
230 .unwrap_or(0),
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000231 longest_value_picked_from_col: flags
232 .iter()
233 .map(|f| f.value_picked_from.to_string().len())
234 .max()
235 .unwrap_or(0),
236 longest_permission_col: flags
237 .iter()
238 .map(|f| f.permission.to_string().len())
239 .max()
240 .unwrap_or(0),
241 };
242
243 let mut result = String::from("");
244 for flag in flags {
245 let row = format_flag_row(&flag, &padding_info);
246 result.push_str(&row);
247 }
248 Ok(result)
249}
250
251fn main() {
252 let cli = Cli::parse();
253 let output = match cli.command {
Ted Bauer6d4db662024-03-06 18:08:32 -0500254 Command::List { use_new_storage: true } => list(FlagSourceType::AconfigStorage).map(Some),
255 Command::List { use_new_storage: false } => list(FlagSourceType::DeviceConfig).map(Some),
Ted Bauer84883bd2024-03-04 22:45:29 +0000256 Command::Enable { qualified_name } => set_flag(&qualified_name, "true").map(|_| None),
257 Command::Disable { qualified_name } => set_flag(&qualified_name, "false").map(|_| None),
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000258 };
259 match output {
Ted Bauer84883bd2024-03-04 22:45:29 +0000260 Ok(Some(text)) => println!("{text}"),
261 Ok(None) => (),
262 Err(message) => println!("Error: {message}"),
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000263 }
264}