blob: a726cc0369239d3f95b319a0626eff729d4aebc5 [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
Chris Wailese6bb2e92024-05-09 15:14:22 -070036impl std::fmt::Display for FlagPermission {
37 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Ted Baueraeb96092024-05-15 09:23:43 -040038 write!(
39 f,
40 "{}",
41 match &self {
42 Self::ReadOnly => "read-only",
43 Self::ReadWrite => "read-write",
44 }
45 )
Ted Bauer4dbf58a2024-02-08 18:46:52 +000046 }
47}
48
Ted Bauer46d758b2024-03-12 19:28:58 +000049#[derive(Clone, Debug)]
Ted Bauer4dbf58a2024-02-08 18:46:52 +000050enum ValuePickedFrom {
51 Default,
52 Server,
Ted Bauer05fea282024-07-19 13:11:42 +000053 Local,
Ted Bauer4dbf58a2024-02-08 18:46:52 +000054}
55
Chris Wailese6bb2e92024-05-09 15:14:22 -070056impl std::fmt::Display for ValuePickedFrom {
57 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Ted Baueraeb96092024-05-15 09:23:43 -040058 write!(
59 f,
60 "{}",
61 match &self {
62 Self::Default => "default",
63 Self::Server => "server",
Ted Bauer05fea282024-07-19 13:11:42 +000064 Self::Local => "local",
Ted Baueraeb96092024-05-15 09:23:43 -040065 }
66 )
Ted Bauer4dbf58a2024-02-08 18:46:52 +000067 }
68}
69
Mårten Kongstadf0c594d2024-03-08 10:20:32 +010070#[derive(Clone, Copy, PartialEq, Eq, Debug)]
71enum FlagValue {
72 Enabled,
73 Disabled,
74}
75
76impl TryFrom<&str> for FlagValue {
77 type Error = anyhow::Error;
78
79 fn try_from(value: &str) -> std::result::Result<Self, Self::Error> {
80 match value {
81 "true" | "enabled" => Ok(Self::Enabled),
82 "false" | "disabled" => Ok(Self::Disabled),
83 _ => Err(anyhow!("cannot convert string '{}' to FlagValue", value)),
84 }
85 }
86}
87
Chris Wailese6bb2e92024-05-09 15:14:22 -070088impl std::fmt::Display for FlagValue {
89 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Ted Baueraeb96092024-05-15 09:23:43 -040090 write!(
91 f,
92 "{}",
93 match &self {
94 Self::Enabled => "enabled",
95 Self::Disabled => "disabled",
96 }
97 )
Mårten Kongstadf0c594d2024-03-08 10:20:32 +010098 }
99}
100
Ted Bauer46d758b2024-03-12 19:28:58 +0000101#[derive(Clone, Debug)]
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000102struct Flag {
103 namespace: String,
104 name: String,
105 package: String,
106 container: String,
Mårten Kongstadf0c594d2024-03-08 10:20:32 +0100107 value: FlagValue,
Ted Bauer46d758b2024-03-12 19:28:58 +0000108 staged_value: Option<FlagValue>,
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000109 permission: FlagPermission,
110 value_picked_from: ValuePickedFrom,
111}
112
Ted Bauer84883bd2024-03-04 22:45:29 +0000113impl Flag {
114 fn qualified_name(&self) -> String {
115 format!("{}.{}", self.package, self.name)
116 }
Ted Bauer46d758b2024-03-12 19:28:58 +0000117
118 fn display_staged_value(&self) -> String {
119 match self.staged_value {
Chris Wailese6bb2e92024-05-09 15:14:22 -0700120 Some(v) => format!("(->{})", v),
Ted Bauer46d758b2024-03-12 19:28:58 +0000121 None => "-".to_string(),
122 }
123 }
Ted Bauer84883bd2024-03-04 22:45:29 +0000124}
125
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000126trait FlagSource {
127 fn list_flags() -> Result<Vec<Flag>>;
Ted Bauer84883bd2024-03-04 22:45:29 +0000128 fn override_flag(namespace: &str, qualified_name: &str, value: &str) -> Result<()>;
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000129}
130
Ted Bauer6d4db662024-03-06 18:08:32 -0500131enum FlagSourceType {
132 DeviceConfig,
133 AconfigStorage,
134}
135
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000136const ABOUT_TEXT: &str = "Tool for reading and writing flags.
137
138Rows in the table from the `list` command follow this format:
139
140 package flag_name value provenance permission container
141
142 * `package`: package set for this flag in its .aconfig definition.
143 * `flag_name`: flag name, also set in definition.
144 * `value`: the value read from the flag.
Ted Bauer46d758b2024-03-12 19:28:58 +0000145 * `staged_value`: the value on next boot:
146 + `-`: same as current value
147 + `(->enabled) flipped to enabled on boot.
148 + `(->disabled) flipped to disabled on boot.
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000149 * `provenance`: one of:
150 + `default`: the flag value comes from its build-time default.
151 + `server`: the flag value comes from a server override.
152 * `permission`: read-write or read-only.
153 * `container`: the container for the flag, configured in its definition.
154";
155
156#[derive(Parser, Debug)]
157#[clap(long_about=ABOUT_TEXT)]
158struct Cli {
159 #[clap(subcommand)]
160 command: Command,
161}
162
163#[derive(Parser, Debug)]
164enum Command {
165 /// List all aconfig flags on this device.
Ted Bauer6d4db662024-03-06 18:08:32 -0500166 List {
Ted Baueraeb96092024-05-15 09:23:43 -0400167 /// Optionally filter by container name.
168 #[clap(short = 'c', long = "container")]
169 container: Option<String>,
Ted Bauer6d4db662024-03-06 18:08:32 -0500170 },
Ted Bauer84883bd2024-03-04 22:45:29 +0000171
172 /// Enable an aconfig flag on this device, on the next boot.
173 Enable {
174 /// <package>.<flag_name>
175 qualified_name: String,
176 },
177
178 /// Disable an aconfig flag on this device, on the next boot.
179 Disable {
180 /// <package>.<flag_name>
181 qualified_name: String,
182 },
Ted Bauerb1edaae2024-09-11 19:07:44 +0000183
184 /// Display which flag storage backs aconfig flags.
185 WhichBacking,
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000186}
187
188struct PaddingInfo {
Mårten Kongstadd408e962024-03-07 13:56:30 +0100189 longest_flag_col: usize,
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000190 longest_val_col: usize,
Ted Bauer46d758b2024-03-12 19:28:58 +0000191 longest_staged_val_col: usize,
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000192 longest_value_picked_from_col: usize,
193 longest_permission_col: usize,
194}
195
Ted Baueraeb96092024-05-15 09:23:43 -0400196struct Filter {
197 container: Option<String>,
198}
199
200impl Filter {
201 fn apply(&self, flags: &[Flag]) -> Vec<Flag> {
202 flags
203 .iter()
204 .filter(|flag| match &self.container {
205 Some(c) => flag.container == *c,
206 None => true,
207 })
208 .cloned()
209 .collect()
210 }
211}
212
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000213fn format_flag_row(flag: &Flag, info: &PaddingInfo) -> String {
Mårten Kongstadd408e962024-03-07 13:56:30 +0100214 let full_name = flag.qualified_name();
215 let p0 = info.longest_flag_col + 1;
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000216
Mårten Kongstadf0c594d2024-03-08 10:20:32 +0100217 let val = flag.value.to_string();
Mårten Kongstadd408e962024-03-07 13:56:30 +0100218 let p1 = info.longest_val_col + 1;
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000219
Ted Bauer46d758b2024-03-12 19:28:58 +0000220 let staged_val = flag.display_staged_value();
221 let p2 = info.longest_staged_val_col + 1;
222
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000223 let value_picked_from = flag.value_picked_from.to_string();
Ted Bauer46d758b2024-03-12 19:28:58 +0000224 let p3 = info.longest_value_picked_from_col + 1;
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000225
226 let perm = flag.permission.to_string();
Ted Bauer46d758b2024-03-12 19:28:58 +0000227 let p4 = info.longest_permission_col + 1;
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000228
229 let container = &flag.container;
230
Ted Bauer46d758b2024-03-12 19:28:58 +0000231 format!(
232 "{full_name:p0$}{val:p1$}{staged_val:p2$}{value_picked_from:p3$}{perm:p4$}{container}\n"
233 )
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000234}
235
Ted Bauer84883bd2024-03-04 22:45:29 +0000236fn set_flag(qualified_name: &str, value: &str) -> Result<()> {
237 let flags_binding = DeviceConfigSource::list_flags()?;
238 let flag = flags_binding.iter().find(|f| f.qualified_name() == qualified_name).ok_or(
239 anyhow!("no aconfig flag '{qualified_name}'. Does the flag have an .aconfig definition?"),
240 )?;
241
Ted Bauera98448f2024-03-06 14:01:19 -0500242 ensure!(flag.permission == FlagPermission::ReadWrite,
243 format!("could not write flag '{qualified_name}', it is read-only for the current release configuration."));
Ted Bauer84883bd2024-03-04 22:45:29 +0000244
245 DeviceConfigSource::override_flag(&flag.namespace, qualified_name, value)?;
246
247 Ok(())
248}
249
Ted Baueraeb96092024-05-15 09:23:43 -0400250fn list(source_type: FlagSourceType, container: Option<String>) -> Result<String> {
251 let flags_unfiltered = match source_type {
Ted Bauer6d4db662024-03-06 18:08:32 -0500252 FlagSourceType::DeviceConfig => DeviceConfigSource::list_flags()?,
253 FlagSourceType::AconfigStorage => AconfigStorageSource::list_flags()?,
254 };
Ted Baueraeb96092024-05-15 09:23:43 -0400255 let flags = (Filter { container }).apply(&flags_unfiltered);
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000256 let padding_info = PaddingInfo {
Mårten Kongstadd408e962024-03-07 13:56:30 +0100257 longest_flag_col: flags.iter().map(|f| f.qualified_name().len()).max().unwrap_or(0),
Mårten Kongstadf0c594d2024-03-08 10:20:32 +0100258 longest_val_col: flags.iter().map(|f| f.value.to_string().len()).max().unwrap_or(0),
Ted Bauer46d758b2024-03-12 19:28:58 +0000259 longest_staged_val_col: flags
260 .iter()
261 .map(|f| f.display_staged_value().len())
262 .max()
263 .unwrap_or(0),
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000264 longest_value_picked_from_col: flags
265 .iter()
266 .map(|f| f.value_picked_from.to_string().len())
267 .max()
268 .unwrap_or(0),
269 longest_permission_col: flags
270 .iter()
271 .map(|f| f.permission.to_string().len())
272 .max()
273 .unwrap_or(0),
274 };
275
276 let mut result = String::from("");
277 for flag in flags {
278 let row = format_flag_row(&flag, &padding_info);
279 result.push_str(&row);
280 }
281 Ok(result)
282}
283
Ted Bauerb1edaae2024-09-11 19:07:44 +0000284fn display_which_backing() -> String {
285 if aconfig_flags::auto_generated::enable_only_new_storage() {
286 "aconfig_storage".to_string()
287 } else {
288 "device_config".to_string()
289 }
290}
291
Ted Bauer5223a702024-06-17 14:48:55 +0000292fn main() -> Result<()> {
293 ensure!(nix::unistd::Uid::current().is_root(), "must be root");
294
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000295 let cli = Cli::parse();
296 let output = match cli.command {
Ted Bauerb1edaae2024-09-11 19:07:44 +0000297 Command::List { container } => {
298 if aconfig_flags::auto_generated::enable_only_new_storage() {
299 list(FlagSourceType::AconfigStorage, container)
300 .map_err(|err| anyhow!("storage may not be enabled: {err}"))
301 .map(Some)
302 } else {
303 list(FlagSourceType::DeviceConfig, container).map(Some)
304 }
Ted Baueraeb96092024-05-15 09:23:43 -0400305 }
Ted Bauer84883bd2024-03-04 22:45:29 +0000306 Command::Enable { qualified_name } => set_flag(&qualified_name, "true").map(|_| None),
307 Command::Disable { qualified_name } => set_flag(&qualified_name, "false").map(|_| None),
Ted Bauerb1edaae2024-09-11 19:07:44 +0000308 Command::WhichBacking => Ok(Some(display_which_backing())),
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000309 };
310 match output {
Ted Bauer84883bd2024-03-04 22:45:29 +0000311 Ok(Some(text)) => println!("{text}"),
312 Ok(None) => (),
313 Err(message) => println!("Error: {message}"),
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000314 }
Ted Bauer5223a702024-06-17 14:48:55 +0000315
316 Ok(())
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000317}
Ted Baueraeb96092024-05-15 09:23:43 -0400318
319#[cfg(test)]
320mod tests {
321 use super::*;
322
323 #[test]
324 fn test_filter_container() {
325 let flags = vec![
326 Flag {
327 namespace: "namespace".to_string(),
328 name: "test1".to_string(),
329 package: "package".to_string(),
330 value: FlagValue::Disabled,
331 staged_value: None,
332 permission: FlagPermission::ReadWrite,
333 value_picked_from: ValuePickedFrom::Default,
334 container: "system".to_string(),
335 },
336 Flag {
337 namespace: "namespace".to_string(),
338 name: "test2".to_string(),
339 package: "package".to_string(),
340 value: FlagValue::Disabled,
341 staged_value: None,
342 permission: FlagPermission::ReadWrite,
343 value_picked_from: ValuePickedFrom::Default,
344 container: "not_system".to_string(),
345 },
346 Flag {
347 namespace: "namespace".to_string(),
348 name: "test3".to_string(),
349 package: "package".to_string(),
350 value: FlagValue::Disabled,
351 staged_value: None,
352 permission: FlagPermission::ReadWrite,
353 value_picked_from: ValuePickedFrom::Default,
354 container: "system".to_string(),
355 },
356 ];
357
358 assert_eq!((Filter { container: Some("system".to_string()) }).apply(&flags).len(), 2);
359 }
360
361 #[test]
362 fn test_filter_no_container() {
363 let flags = vec![
364 Flag {
365 namespace: "namespace".to_string(),
366 name: "test1".to_string(),
367 package: "package".to_string(),
368 value: FlagValue::Disabled,
369 staged_value: None,
370 permission: FlagPermission::ReadWrite,
371 value_picked_from: ValuePickedFrom::Default,
372 container: "system".to_string(),
373 },
374 Flag {
375 namespace: "namespace".to_string(),
376 name: "test2".to_string(),
377 package: "package".to_string(),
378 value: FlagValue::Disabled,
379 staged_value: None,
380 permission: FlagPermission::ReadWrite,
381 value_picked_from: ValuePickedFrom::Default,
382 container: "not_system".to_string(),
383 },
384 Flag {
385 namespace: "namespace".to_string(),
386 name: "test3".to_string(),
387 package: "package".to_string(),
388 value: FlagValue::Disabled,
389 staged_value: None,
390 permission: FlagPermission::ReadWrite,
391 value_picked_from: ValuePickedFrom::Default,
392 container: "system".to_string(),
393 },
394 ];
395
396 assert_eq!((Filter { container: None }).apply(&flags).len(), 3);
397 }
398}