1 // Copyright 2023, The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 //! Routines for parsing bootargs
16 
17 #[cfg(not(test))]
18 use alloc::format;
19 #[cfg(not(test))]
20 use alloc::string::String;
21 use core::ffi::CStr;
22 
23 /// A single boot argument ex: "panic", "init=", or "foo=1,2,3".
24 pub struct BootArg<'a> {
25     arg: &'a str,
26     equal_sign: Option<usize>,
27 }
28 
29 impl AsRef<str> for BootArg<'_> {
as_ref(&self) -> &str30     fn as_ref(&self) -> &str {
31         self.arg
32     }
33 }
34 
35 impl BootArg<'_> {
36     /// Name of the boot argument
name(&self) -> &str37     pub fn name(&self) -> &str {
38         if let Some(n) = self.equal_sign {
39             &self.arg[..n]
40         } else {
41             self.arg
42         }
43     }
44 
45     /// Optional value of the boot aragument. This includes the '=' prefix.
value(&self) -> Option<&str>46     pub fn value(&self) -> Option<&str> {
47         Some(&self.arg[self.equal_sign?..])
48     }
49 }
50 
51 /// Iterator that iteratos over bootargs
52 pub struct BootArgsIterator<'a> {
53     arg: &'a str,
54 }
55 
56 impl<'a> BootArgsIterator<'a> {
57     /// Creates a new iterator from the raw boot args. The input has to be encoded in ASCII
new(bootargs: &'a CStr) -> Result<Self, String>58     pub fn new(bootargs: &'a CStr) -> Result<Self, String> {
59         let arg = bootargs.to_str().map_err(|e| format!("{e}"))?;
60         if !arg.is_ascii() {
61             return Err(format!("{arg:?} is not ASCII"));
62         }
63 
64         Ok(Self { arg })
65     }
66 
67     // Finds the end of a value in the given string `s`, and returns the index of the end. A value
68     // can have spaces if quoted. The quote character can't be escaped.
find_value_end(s: &str) -> usize69     fn find_value_end(s: &str) -> usize {
70         let mut in_quote = false;
71         for (i, c) in s.char_indices() {
72             if c == '"' {
73                 in_quote = !in_quote;
74             } else if c.is_whitespace() && !in_quote {
75                 return i;
76             }
77         }
78         s.len()
79     }
80 }
81 
82 impl<'a> Iterator for BootArgsIterator<'a> {
83     type Item = BootArg<'a>;
84 
next(&mut self) -> Option<Self::Item>85     fn next(&mut self) -> Option<Self::Item> {
86         // Skip spaces to find the start of a name. If there's nothing left, that's the end of the
87         // iterator.
88         let arg = self.arg.trim_start();
89         self.arg = arg; // advance before returning
90         if arg.is_empty() {
91             return None;
92         }
93         // Name ends with either whitespace or =. If it ends with =, the value comes immediately
94         // after.
95         let name_end = arg.find(|c: char| c.is_whitespace() || c == '=').unwrap_or(arg.len());
96         let (arg, equal_sign) = match arg.chars().nth(name_end) {
97             Some('=') => {
98                 let value_end = name_end + Self::find_value_end(&arg[name_end..]);
99                 (&arg[..value_end], Some(name_end))
100             }
101             _ => (&arg[..name_end], None),
102         };
103         self.arg = &self.arg[arg.len()..]; // advance before returning
104         Some(BootArg { arg, equal_sign })
105     }
106 }
107 
108 #[cfg(test)]
109 mod tests {
110     use super::*;
111     use cstr::cstr;
112 
check(raw: &CStr, expected: Result<&[(&str, Option<&str>)], ()>)113     fn check(raw: &CStr, expected: Result<&[(&str, Option<&str>)], ()>) {
114         let actual = BootArgsIterator::new(raw);
115         assert_eq!(actual.is_err(), expected.is_err(), "Unexpected result with {raw:?}");
116         if actual.is_err() {
117             return;
118         }
119         let mut actual = actual.unwrap();
120 
121         for (name, value) in expected.unwrap() {
122             let actual = actual.next();
123             assert!(actual.is_some(), "Expected ({}, {:?}) from {raw:?}", name, value);
124             let actual = actual.unwrap();
125             assert_eq!(name, &actual.name(), "Unexpected name from {raw:?}");
126             assert_eq!(value, &actual.value(), "Unexpected value from {raw:?}");
127         }
128         let remaining = actual.next();
129         assert!(
130             remaining.is_none(),
131             "Unexpected extra item from {raw:?}. Got ({}, {:?})",
132             remaining.as_ref().unwrap().name(),
133             remaining.as_ref().unwrap().value()
134         );
135     }
136 
137     #[test]
empty()138     fn empty() {
139         check(cstr!(""), Ok(&[]));
140         check(cstr!("    "), Ok(&[]));
141         check(cstr!("  \n  "), Ok(&[]));
142     }
143 
144     #[test]
single()145     fn single() {
146         check(cstr!("foo"), Ok(&[("foo", None)]));
147         check(cstr!("   foo"), Ok(&[("foo", None)]));
148         check(cstr!("foo   "), Ok(&[("foo", None)]));
149         check(cstr!("   foo   "), Ok(&[("foo", None)]));
150     }
151 
152     #[test]
single_with_value()153     fn single_with_value() {
154         check(cstr!("foo=bar"), Ok(&[("foo", Some("=bar"))]));
155         check(cstr!("   foo=bar"), Ok(&[("foo", Some("=bar"))]));
156         check(cstr!("foo=bar   "), Ok(&[("foo", Some("=bar"))]));
157         check(cstr!("   foo=bar   "), Ok(&[("foo", Some("=bar"))]));
158 
159         check(cstr!("foo="), Ok(&[("foo", Some("="))]));
160         check(cstr!("   foo="), Ok(&[("foo", Some("="))]));
161         check(cstr!("foo=   "), Ok(&[("foo", Some("="))]));
162         check(cstr!("   foo=   "), Ok(&[("foo", Some("="))]));
163     }
164 
165     #[test]
single_with_quote()166     fn single_with_quote() {
167         check(cstr!("foo=hello\" \"world"), Ok(&[("foo", Some("=hello\" \"world"))]));
168     }
169 
170     #[test]
invalid_encoding()171     fn invalid_encoding() {
172         check(CStr::from_bytes_with_nul(&[255, 255, 255, 0]).unwrap(), Err(()));
173     }
174 
175     #[test]
multiple()176     fn multiple() {
177         check(
178             cstr!(" a=b   c=d   e=  f g  "),
179             Ok(&[("a", Some("=b")), ("c", Some("=d")), ("e", Some("=")), ("f", None), ("g", None)]),
180         );
181         check(
182             cstr!("   a=b  \n c=d      e=  f g"),
183             Ok(&[("a", Some("=b")), ("c", Some("=d")), ("e", Some("=")), ("f", None), ("g", None)]),
184         );
185     }
186 
187     #[test]
incomplete_quote()188     fn incomplete_quote() {
189         check(
190             cstr!("foo=incomplete\" quote bar=y"),
191             Ok(&[("foo", Some("=incomplete\" quote bar=y"))]),
192         );
193     }
194 
195     #[test]
complex()196     fn complex() {
197         check(cstr!("  a  a1=  b=c d=e,f,g x=\"value with quote\" y=val\"ue with \"multiple\" quo\"te  "), Ok(&[
198             ("a", None),
199             ("a1", Some("=")),
200             ("b", Some("=c")),
201             ("d", Some("=e,f,g")),
202             ("x", Some("=\"value with quote\"")),
203             ("y", Some("=val\"ue with \"multiple\" quo\"te")),
204         ]));
205     }
206 }
207