blob: 808ffa0da8b9744dd9788f2797c29e4fb9e7d8f7 [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 Bauer46d758b2024-03-12 19:28:58 +000025#[derive(Clone, PartialEq, Debug)]
Ted Bauer4dbf58a2024-02-08 18:46:52 +000026enum FlagPermission {
27 ReadOnly,
28 ReadWrite,
29}
30
31impl ToString for FlagPermission {
32 fn to_string(&self) -> String {
33 match &self {
34 Self::ReadOnly => "read-only".into(),
35 Self::ReadWrite => "read-write".into(),
36 }
37 }
38}
39
Ted Bauer46d758b2024-03-12 19:28:58 +000040#[derive(Clone, Debug)]
Ted Bauer4dbf58a2024-02-08 18:46:52 +000041enum ValuePickedFrom {
42 Default,
43 Server,
44}
45
46impl ToString for ValuePickedFrom {
47 fn to_string(&self) -> String {
48 match &self {
49 Self::Default => "default".into(),
50 Self::Server => "server".into(),
51 }
52 }
53}
54
Mårten Kongstadf0c594d2024-03-08 10:20:32 +010055#[derive(Clone, Copy, PartialEq, Eq, Debug)]
56enum FlagValue {
57 Enabled,
58 Disabled,
59}
60
61impl TryFrom<&str> for FlagValue {
62 type Error = anyhow::Error;
63
64 fn try_from(value: &str) -> std::result::Result<Self, Self::Error> {
65 match value {
66 "true" | "enabled" => Ok(Self::Enabled),
67 "false" | "disabled" => Ok(Self::Disabled),
68 _ => Err(anyhow!("cannot convert string '{}' to FlagValue", value)),
69 }
70 }
71}
72
73impl ToString for FlagValue {
74 fn to_string(&self) -> String {
75 match &self {
76 Self::Enabled => "enabled".into(),
77 Self::Disabled => "disabled".into(),
78 }
79 }
80}
81
Ted Bauer46d758b2024-03-12 19:28:58 +000082#[derive(Clone, Debug)]
Ted Bauer4dbf58a2024-02-08 18:46:52 +000083struct Flag {
84 namespace: String,
85 name: String,
86 package: String,
87 container: String,
Mårten Kongstadf0c594d2024-03-08 10:20:32 +010088 value: FlagValue,
Ted Bauer46d758b2024-03-12 19:28:58 +000089 staged_value: Option<FlagValue>,
Ted Bauer4dbf58a2024-02-08 18:46:52 +000090 permission: FlagPermission,
91 value_picked_from: ValuePickedFrom,
92}
93
Ted Bauer84883bd2024-03-04 22:45:29 +000094impl Flag {
95 fn qualified_name(&self) -> String {
96 format!("{}.{}", self.package, self.name)
97 }
Ted Bauer46d758b2024-03-12 19:28:58 +000098
99 fn display_staged_value(&self) -> String {
100 match self.staged_value {
101 Some(v) => format!("(->{})", v.to_string()),
102 None => "-".to_string(),
103 }
104 }
Ted Bauer84883bd2024-03-04 22:45:29 +0000105}
106
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000107trait FlagSource {
108 fn list_flags() -> Result<Vec<Flag>>;
Ted Bauer84883bd2024-03-04 22:45:29 +0000109 fn override_flag(namespace: &str, qualified_name: &str, value: &str) -> Result<()>;
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000110}
111
112const ABOUT_TEXT: &str = "Tool for reading and writing flags.
113
114Rows in the table from the `list` command follow this format:
115
116 package flag_name value provenance permission container
117
118 * `package`: package set for this flag in its .aconfig definition.
119 * `flag_name`: flag name, also set in definition.
120 * `value`: the value read from the flag.
Ted Bauer46d758b2024-03-12 19:28:58 +0000121 * `staged_value`: the value on next boot:
122 + `-`: same as current value
123 + `(->enabled) flipped to enabled on boot.
124 + `(->disabled) flipped to disabled on boot.
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000125 * `provenance`: one of:
126 + `default`: the flag value comes from its build-time default.
127 + `server`: the flag value comes from a server override.
128 * `permission`: read-write or read-only.
129 * `container`: the container for the flag, configured in its definition.
130";
131
132#[derive(Parser, Debug)]
133#[clap(long_about=ABOUT_TEXT)]
134struct Cli {
135 #[clap(subcommand)]
136 command: Command,
137}
138
139#[derive(Parser, Debug)]
140enum Command {
141 /// List all aconfig flags on this device.
142 List,
Ted Bauer84883bd2024-03-04 22:45:29 +0000143
144 /// Enable an aconfig flag on this device, on the next boot.
145 Enable {
146 /// <package>.<flag_name>
147 qualified_name: String,
148 },
149
150 /// Disable an aconfig flag on this device, on the next boot.
151 Disable {
152 /// <package>.<flag_name>
153 qualified_name: String,
154 },
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000155}
156
157struct PaddingInfo {
Mårten Kongstadd408e962024-03-07 13:56:30 +0100158 longest_flag_col: usize,
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000159 longest_val_col: usize,
Ted Bauer46d758b2024-03-12 19:28:58 +0000160 longest_staged_val_col: usize,
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000161 longest_value_picked_from_col: usize,
162 longest_permission_col: usize,
163}
164
165fn format_flag_row(flag: &Flag, info: &PaddingInfo) -> String {
Mårten Kongstadd408e962024-03-07 13:56:30 +0100166 let full_name = flag.qualified_name();
167 let p0 = info.longest_flag_col + 1;
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000168
Mårten Kongstadf0c594d2024-03-08 10:20:32 +0100169 let val = flag.value.to_string();
Mårten Kongstadd408e962024-03-07 13:56:30 +0100170 let p1 = info.longest_val_col + 1;
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000171
Ted Bauer46d758b2024-03-12 19:28:58 +0000172 let staged_val = flag.display_staged_value();
173 let p2 = info.longest_staged_val_col + 1;
174
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000175 let value_picked_from = flag.value_picked_from.to_string();
Ted Bauer46d758b2024-03-12 19:28:58 +0000176 let p3 = info.longest_value_picked_from_col + 1;
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000177
178 let perm = flag.permission.to_string();
Ted Bauer46d758b2024-03-12 19:28:58 +0000179 let p4 = info.longest_permission_col + 1;
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000180
181 let container = &flag.container;
182
Ted Bauer46d758b2024-03-12 19:28:58 +0000183 format!(
184 "{full_name:p0$}{val:p1$}{staged_val:p2$}{value_picked_from:p3$}{perm:p4$}{container}\n"
185 )
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000186}
187
Ted Bauer84883bd2024-03-04 22:45:29 +0000188fn set_flag(qualified_name: &str, value: &str) -> Result<()> {
Ted Bauera98448f2024-03-06 14:01:19 -0500189 ensure!(nix::unistd::Uid::current().is_root(), "must be root to mutate flags");
190
Ted Bauer84883bd2024-03-04 22:45:29 +0000191 let flags_binding = DeviceConfigSource::list_flags()?;
192 let flag = flags_binding.iter().find(|f| f.qualified_name() == qualified_name).ok_or(
193 anyhow!("no aconfig flag '{qualified_name}'. Does the flag have an .aconfig definition?"),
194 )?;
195
Ted Bauera98448f2024-03-06 14:01:19 -0500196 ensure!(flag.permission == FlagPermission::ReadWrite,
197 format!("could not write flag '{qualified_name}', it is read-only for the current release configuration."));
Ted Bauer84883bd2024-03-04 22:45:29 +0000198
199 DeviceConfigSource::override_flag(&flag.namespace, qualified_name, value)?;
200
201 Ok(())
202}
203
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000204fn list() -> Result<String> {
205 let flags = DeviceConfigSource::list_flags()?;
206 let padding_info = PaddingInfo {
Mårten Kongstadd408e962024-03-07 13:56:30 +0100207 longest_flag_col: flags.iter().map(|f| f.qualified_name().len()).max().unwrap_or(0),
Mårten Kongstadf0c594d2024-03-08 10:20:32 +0100208 longest_val_col: flags.iter().map(|f| f.value.to_string().len()).max().unwrap_or(0),
Ted Bauer46d758b2024-03-12 19:28:58 +0000209 longest_staged_val_col: flags
210 .iter()
211 .map(|f| f.display_staged_value().len())
212 .max()
213 .unwrap_or(0),
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000214 longest_value_picked_from_col: flags
215 .iter()
216 .map(|f| f.value_picked_from.to_string().len())
217 .max()
218 .unwrap_or(0),
219 longest_permission_col: flags
220 .iter()
221 .map(|f| f.permission.to_string().len())
222 .max()
223 .unwrap_or(0),
224 };
225
226 let mut result = String::from("");
227 for flag in flags {
228 let row = format_flag_row(&flag, &padding_info);
229 result.push_str(&row);
230 }
231 Ok(result)
232}
233
234fn main() {
235 let cli = Cli::parse();
236 let output = match cli.command {
Ted Bauer84883bd2024-03-04 22:45:29 +0000237 Command::List => list().map(Some),
238 Command::Enable { qualified_name } => set_flag(&qualified_name, "true").map(|_| None),
239 Command::Disable { qualified_name } => set_flag(&qualified_name, "false").map(|_| None),
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000240 };
241 match output {
Ted Bauer84883bd2024-03-04 22:45:29 +0000242 Ok(Some(text)) => println!("{text}"),
243 Ok(None) => (),
244 Err(message) => println!("Error: {message}"),
Ted Bauer4dbf58a2024-02-08 18:46:52 +0000245 }
246}