blob: 810f2e31e6847c3b6f50d80b8b8526287d291a4e [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,
53}
54
Chris Wailese6bb2e92024-05-09 15:14:22 -070055impl std::fmt::Display for ValuePickedFrom {
56 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Ted Baueraeb96092024-05-15 09:23:43 -040057 write!(
58 f,
59 "{}",
60 match &self {
61 Self::Default => "default",
62 Self::Server => "server",
63 }
64 )
Ted Bauer4dbf58a2024-02-08 18:46:52 +000065 }
66}
67
Mårten Kongstadf0c594d2024-03-08 10:20:32 +010068#[derive(Clone, Copy, PartialEq, Eq, Debug)]
69enum FlagValue {
70 Enabled,
71 Disabled,
72}
73
74impl TryFrom<&str> for FlagValue {
75 type Error = anyhow::Error;
76
77 fn try_from(value: &str) -> std::result::Result<Self, Self::Error> {
78 match value {
79 "true" | "enabled" => Ok(Self::Enabled),
80 "false" | "disabled" => Ok(Self::Disabled),
81 _ => Err(anyhow!("cannot convert string '{}' to FlagValue", value)),
82 }
83 }
84}
85
Chris Wailese6bb2e92024-05-09 15:14:22 -070086impl std::fmt::Display for FlagValue {
87 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Ted Baueraeb96092024-05-15 09:23:43 -040088 write!(
89 f,
90 "{}",
91 match &self {
92 Self::Enabled => "enabled",
93 Self::Disabled => "disabled",
94 }
95 )
Mårten Kongstadf0c594d2024-03-08 10:20:32 +010096 }
97}
98
Ted Bauer46d758b2024-03-12 19:28:58 +000099#[derive(Clone, Debug)]
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000100struct Flag {
101 namespace: String,
102 name: String,
103 package: String,
104 container: String,
Mårten Kongstadf0c594d2024-03-08 10:20:32 +0100105 value: FlagValue,
Ted Bauer46d758b2024-03-12 19:28:58 +0000106 staged_value: Option<FlagValue>,
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000107 permission: FlagPermission,
108 value_picked_from: ValuePickedFrom,
109}
110
Ted Bauer84883bd2024-03-04 22:45:29 +0000111impl Flag {
112 fn qualified_name(&self) -> String {
113 format!("{}.{}", self.package, self.name)
114 }
Ted Bauer46d758b2024-03-12 19:28:58 +0000115
116 fn display_staged_value(&self) -> String {
117 match self.staged_value {
Chris Wailese6bb2e92024-05-09 15:14:22 -0700118 Some(v) => format!("(->{})", v),
Ted Bauer46d758b2024-03-12 19:28:58 +0000119 None => "-".to_string(),
120 }
121 }
Ted Bauer84883bd2024-03-04 22:45:29 +0000122}
123
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000124trait FlagSource {
125 fn list_flags() -> Result<Vec<Flag>>;
Ted Bauer84883bd2024-03-04 22:45:29 +0000126 fn override_flag(namespace: &str, qualified_name: &str, value: &str) -> Result<()>;
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000127}
128
Ted Bauer6d4db662024-03-06 18:08:32 -0500129enum FlagSourceType {
130 DeviceConfig,
131 AconfigStorage,
132}
133
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000134const ABOUT_TEXT: &str = "Tool for reading and writing flags.
135
136Rows in the table from the `list` command follow this format:
137
138 package flag_name value provenance permission container
139
140 * `package`: package set for this flag in its .aconfig definition.
141 * `flag_name`: flag name, also set in definition.
142 * `value`: the value read from the flag.
Ted Bauer46d758b2024-03-12 19:28:58 +0000143 * `staged_value`: the value on next boot:
144 + `-`: same as current value
145 + `(->enabled) flipped to enabled on boot.
146 + `(->disabled) flipped to disabled on boot.
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000147 * `provenance`: one of:
148 + `default`: the flag value comes from its build-time default.
149 + `server`: the flag value comes from a server override.
150 * `permission`: read-write or read-only.
151 * `container`: the container for the flag, configured in its definition.
152";
153
154#[derive(Parser, Debug)]
155#[clap(long_about=ABOUT_TEXT)]
156struct Cli {
157 #[clap(subcommand)]
158 command: Command,
159}
160
161#[derive(Parser, Debug)]
162enum Command {
163 /// List all aconfig flags on this device.
Ted Bauer6d4db662024-03-06 18:08:32 -0500164 List {
165 /// Read from the new flag storage.
166 #[clap(long)]
167 use_new_storage: bool,
Ted Baueraeb96092024-05-15 09:23:43 -0400168
169 /// Optionally filter by container name.
170 #[clap(short = 'c', long = "container")]
171 container: Option<String>,
Ted Bauer6d4db662024-03-06 18:08:32 -0500172 },
Ted Bauer84883bd2024-03-04 22:45:29 +0000173
174 /// Enable an aconfig flag on this device, on the next boot.
175 Enable {
176 /// <package>.<flag_name>
177 qualified_name: String,
178 },
179
180 /// Disable an aconfig flag on this device, on the next boot.
181 Disable {
182 /// <package>.<flag_name>
183 qualified_name: String,
184 },
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000185}
186
187struct PaddingInfo {
Mårten Kongstadd408e962024-03-07 13:56:30 +0100188 longest_flag_col: usize,
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000189 longest_val_col: usize,
Ted Bauer46d758b2024-03-12 19:28:58 +0000190 longest_staged_val_col: usize,
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000191 longest_value_picked_from_col: usize,
192 longest_permission_col: usize,
193}
194
Ted Baueraeb96092024-05-15 09:23:43 -0400195struct Filter {
196 container: Option<String>,
197}
198
199impl Filter {
200 fn apply(&self, flags: &[Flag]) -> Vec<Flag> {
201 flags
202 .iter()
203 .filter(|flag| match &self.container {
204 Some(c) => flag.container == *c,
205 None => true,
206 })
207 .cloned()
208 .collect()
209 }
210}
211
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000212fn format_flag_row(flag: &Flag, info: &PaddingInfo) -> String {
Mårten Kongstadd408e962024-03-07 13:56:30 +0100213 let full_name = flag.qualified_name();
214 let p0 = info.longest_flag_col + 1;
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000215
Mårten Kongstadf0c594d2024-03-08 10:20:32 +0100216 let val = flag.value.to_string();
Mårten Kongstadd408e962024-03-07 13:56:30 +0100217 let p1 = info.longest_val_col + 1;
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000218
Ted Bauer46d758b2024-03-12 19:28:58 +0000219 let staged_val = flag.display_staged_value();
220 let p2 = info.longest_staged_val_col + 1;
221
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000222 let value_picked_from = flag.value_picked_from.to_string();
Ted Bauer46d758b2024-03-12 19:28:58 +0000223 let p3 = info.longest_value_picked_from_col + 1;
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000224
225 let perm = flag.permission.to_string();
Ted Bauer46d758b2024-03-12 19:28:58 +0000226 let p4 = info.longest_permission_col + 1;
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000227
228 let container = &flag.container;
229
Ted Bauer46d758b2024-03-12 19:28:58 +0000230 format!(
231 "{full_name:p0$}{val:p1$}{staged_val:p2$}{value_picked_from:p3$}{perm:p4$}{container}\n"
232 )
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000233}
234
Ted Bauer84883bd2024-03-04 22:45:29 +0000235fn set_flag(qualified_name: &str, value: &str) -> Result<()> {
236 let flags_binding = DeviceConfigSource::list_flags()?;
237 let flag = flags_binding.iter().find(|f| f.qualified_name() == qualified_name).ok_or(
238 anyhow!("no aconfig flag '{qualified_name}'. Does the flag have an .aconfig definition?"),
239 )?;
240
Ted Bauera98448f2024-03-06 14:01:19 -0500241 ensure!(flag.permission == FlagPermission::ReadWrite,
242 format!("could not write flag '{qualified_name}', it is read-only for the current release configuration."));
Ted Bauer84883bd2024-03-04 22:45:29 +0000243
244 DeviceConfigSource::override_flag(&flag.namespace, qualified_name, value)?;
245
246 Ok(())
247}
248
Ted Baueraeb96092024-05-15 09:23:43 -0400249fn list(source_type: FlagSourceType, container: Option<String>) -> Result<String> {
250 let flags_unfiltered = match source_type {
Ted Bauer6d4db662024-03-06 18:08:32 -0500251 FlagSourceType::DeviceConfig => DeviceConfigSource::list_flags()?,
252 FlagSourceType::AconfigStorage => AconfigStorageSource::list_flags()?,
253 };
Ted Baueraeb96092024-05-15 09:23:43 -0400254 let flags = (Filter { container }).apply(&flags_unfiltered);
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000255 let padding_info = PaddingInfo {
Mårten Kongstadd408e962024-03-07 13:56:30 +0100256 longest_flag_col: flags.iter().map(|f| f.qualified_name().len()).max().unwrap_or(0),
Mårten Kongstadf0c594d2024-03-08 10:20:32 +0100257 longest_val_col: flags.iter().map(|f| f.value.to_string().len()).max().unwrap_or(0),
Ted Bauer46d758b2024-03-12 19:28:58 +0000258 longest_staged_val_col: flags
259 .iter()
260 .map(|f| f.display_staged_value().len())
261 .max()
262 .unwrap_or(0),
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000263 longest_value_picked_from_col: flags
264 .iter()
265 .map(|f| f.value_picked_from.to_string().len())
266 .max()
267 .unwrap_or(0),
268 longest_permission_col: flags
269 .iter()
270 .map(|f| f.permission.to_string().len())
271 .max()
272 .unwrap_or(0),
273 };
274
275 let mut result = String::from("");
276 for flag in flags {
277 let row = format_flag_row(&flag, &padding_info);
278 result.push_str(&row);
279 }
280 Ok(result)
281}
282
Ted Bauer5223a702024-06-17 14:48:55 +0000283fn main() -> Result<()> {
284 ensure!(nix::unistd::Uid::current().is_root(), "must be root");
285
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000286 let cli = Cli::parse();
287 let output = match cli.command {
Ted Baueraeb96092024-05-15 09:23:43 -0400288 Command::List { use_new_storage: true, container } => {
289 list(FlagSourceType::AconfigStorage, container).map(Some)
290 }
291 Command::List { use_new_storage: false, container } => {
292 list(FlagSourceType::DeviceConfig, container).map(Some)
293 }
Ted Bauer84883bd2024-03-04 22:45:29 +0000294 Command::Enable { qualified_name } => set_flag(&qualified_name, "true").map(|_| None),
295 Command::Disable { qualified_name } => set_flag(&qualified_name, "false").map(|_| None),
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000296 };
297 match output {
Ted Bauer84883bd2024-03-04 22:45:29 +0000298 Ok(Some(text)) => println!("{text}"),
299 Ok(None) => (),
300 Err(message) => println!("Error: {message}"),
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000301 }
Ted Bauer5223a702024-06-17 14:48:55 +0000302
303 Ok(())
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000304}
Ted Baueraeb96092024-05-15 09:23:43 -0400305
306#[cfg(test)]
307mod tests {
308 use super::*;
309
310 #[test]
311 fn test_filter_container() {
312 let flags = vec![
313 Flag {
314 namespace: "namespace".to_string(),
315 name: "test1".to_string(),
316 package: "package".to_string(),
317 value: FlagValue::Disabled,
318 staged_value: None,
319 permission: FlagPermission::ReadWrite,
320 value_picked_from: ValuePickedFrom::Default,
321 container: "system".to_string(),
322 },
323 Flag {
324 namespace: "namespace".to_string(),
325 name: "test2".to_string(),
326 package: "package".to_string(),
327 value: FlagValue::Disabled,
328 staged_value: None,
329 permission: FlagPermission::ReadWrite,
330 value_picked_from: ValuePickedFrom::Default,
331 container: "not_system".to_string(),
332 },
333 Flag {
334 namespace: "namespace".to_string(),
335 name: "test3".to_string(),
336 package: "package".to_string(),
337 value: FlagValue::Disabled,
338 staged_value: None,
339 permission: FlagPermission::ReadWrite,
340 value_picked_from: ValuePickedFrom::Default,
341 container: "system".to_string(),
342 },
343 ];
344
345 assert_eq!((Filter { container: Some("system".to_string()) }).apply(&flags).len(), 2);
346 }
347
348 #[test]
349 fn test_filter_no_container() {
350 let flags = vec![
351 Flag {
352 namespace: "namespace".to_string(),
353 name: "test1".to_string(),
354 package: "package".to_string(),
355 value: FlagValue::Disabled,
356 staged_value: None,
357 permission: FlagPermission::ReadWrite,
358 value_picked_from: ValuePickedFrom::Default,
359 container: "system".to_string(),
360 },
361 Flag {
362 namespace: "namespace".to_string(),
363 name: "test2".to_string(),
364 package: "package".to_string(),
365 value: FlagValue::Disabled,
366 staged_value: None,
367 permission: FlagPermission::ReadWrite,
368 value_picked_from: ValuePickedFrom::Default,
369 container: "not_system".to_string(),
370 },
371 Flag {
372 namespace: "namespace".to_string(),
373 name: "test3".to_string(),
374 package: "package".to_string(),
375 value: FlagValue::Disabled,
376 staged_value: None,
377 permission: FlagPermission::ReadWrite,
378 value_picked_from: ValuePickedFrom::Default,
379 container: "system".to_string(),
380 },
381 ];
382
383 assert_eq!((Filter { container: None }).apply(&flags).len(), 3);
384 }
385}