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