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(c) if c == '=' => { 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 #[allow(dead_code)] 110 mod helpers; 111 112 #[cfg(test)] 113 mod tests { 114 115 use super::*; 116 use crate::cstr; 117 check(raw: &CStr, expected: Result<&[(&str, Option<&str>)], ()>)118 fn check(raw: &CStr, expected: Result<&[(&str, Option<&str>)], ()>) { 119 let actual = BootArgsIterator::new(raw); 120 assert_eq!(actual.is_err(), expected.is_err(), "Unexpected result with {raw:?}"); 121 if actual.is_err() { 122 return; 123 } 124 let mut actual = actual.unwrap(); 125 126 for (name, value) in expected.unwrap() { 127 let actual = actual.next(); 128 assert!(actual.is_some(), "Expected ({}, {:?}) from {raw:?}", name, value); 129 let actual = actual.unwrap(); 130 assert_eq!(name, &actual.name(), "Unexpected name from {raw:?}"); 131 assert_eq!(value, &actual.value(), "Unexpected value from {raw:?}"); 132 } 133 let remaining = actual.next(); 134 assert!( 135 remaining.is_none(), 136 "Unexpected extra item from {raw:?}. Got ({}, {:?})", 137 remaining.as_ref().unwrap().name(), 138 remaining.as_ref().unwrap().value() 139 ); 140 } 141 142 #[test] empty()143 fn empty() { 144 check(cstr!(""), Ok(&[])); 145 check(cstr!(" "), Ok(&[])); 146 check(cstr!(" \n "), Ok(&[])); 147 } 148 149 #[test] single()150 fn single() { 151 check(cstr!("foo"), Ok(&[("foo", None)])); 152 check(cstr!(" foo"), Ok(&[("foo", None)])); 153 check(cstr!("foo "), Ok(&[("foo", None)])); 154 check(cstr!(" foo "), Ok(&[("foo", None)])); 155 } 156 157 #[test] single_with_value()158 fn single_with_value() { 159 check(cstr!("foo=bar"), Ok(&[("foo", Some("=bar"))])); 160 check(cstr!(" foo=bar"), Ok(&[("foo", Some("=bar"))])); 161 check(cstr!("foo=bar "), Ok(&[("foo", Some("=bar"))])); 162 check(cstr!(" foo=bar "), Ok(&[("foo", Some("=bar"))])); 163 164 check(cstr!("foo="), Ok(&[("foo", Some("="))])); 165 check(cstr!(" foo="), Ok(&[("foo", Some("="))])); 166 check(cstr!("foo= "), Ok(&[("foo", Some("="))])); 167 check(cstr!(" foo= "), Ok(&[("foo", Some("="))])); 168 } 169 170 #[test] single_with_quote()171 fn single_with_quote() { 172 check(cstr!("foo=hello\" \"world"), Ok(&[("foo", Some("=hello\" \"world"))])); 173 } 174 175 #[test] invalid_encoding()176 fn invalid_encoding() { 177 check(CStr::from_bytes_with_nul(&[255, 255, 255, 0]).unwrap(), Err(())); 178 } 179 180 #[test] multiple()181 fn multiple() { 182 check( 183 cstr!(" a=b c=d e= f g "), 184 Ok(&[("a", Some("=b")), ("c", Some("=d")), ("e", Some("=")), ("f", None), ("g", None)]), 185 ); 186 check( 187 cstr!(" a=b \n c=d e= f g"), 188 Ok(&[("a", Some("=b")), ("c", Some("=d")), ("e", Some("=")), ("f", None), ("g", None)]), 189 ); 190 } 191 192 #[test] incomplete_quote()193 fn incomplete_quote() { 194 check( 195 cstr!("foo=incomplete\" quote bar=y"), 196 Ok(&[("foo", Some("=incomplete\" quote bar=y"))]), 197 ); 198 } 199 200 #[test] complex()201 fn complex() { 202 check(cstr!(" a a1= b=c d=e,f,g x=\"value with quote\" y=val\"ue with \"multiple\" quo\"te "), Ok(&[ 203 ("a", None), 204 ("a1", Some("=")), 205 ("b", Some("=c")), 206 ("d", Some("=e,f,g")), 207 ("x", Some("=\"value with quote\"")), 208 ("y", Some("=val\"ue with \"multiple\" quo\"te")), 209 ])); 210 } 211 } 212