1 use crate::error::parse_error::{ParseError, ParseErrorType}; 2 use crate::parse::parse_util::UnitParseUtil; 3 use crate::unit::{UnitState, UnitType}; 4 use hashbrown::HashMap; 5 use lazy_static::lazy_static; 6 use std::format; 7 use std::string::String; 8 use std::string::ToString; 9 use std::vec::Vec; 10 11 use super::listener::Command; 12 13 #[allow(dead_code)] 14 #[derive(Debug, Clone)] 15 pub enum CommandOperation { 16 ListUnits, 17 ListSockets, 18 ListTimers, 19 Start, 20 Restart, 21 Stop, 22 Reload, 23 TryRestart, 24 ReloadOrRestart, 25 ReloadOrTryRestart, 26 Isolate, 27 Kill, 28 IsActive, 29 IsFailed, 30 Status, 31 Show, 32 Cat, 33 SetProperty, 34 Help, 35 ResetFailed, 36 ListDependencies, 37 ListUnitFiles, 38 Enable, 39 Disable, 40 Reenable, 41 Preset, 42 PresetAll, 43 IsEnabled, 44 Mask, 45 UnMask, 46 Link, 47 AddWants, 48 AddRequires, 49 Edit, 50 GetDefault, 51 SetDefault, 52 ListMachines, 53 ListJobs, 54 Cancel, 55 Snapshot, 56 Delete, 57 ShowEnvironment, 58 SetEnvironment, 59 UnsetEnvironment, 60 ImportEnvironment, 61 DeamonReload, 62 DeamonReexec, 63 IsSystemRunning, 64 Default, 65 Rescue, 66 Emergency, 67 Halt, 68 Poweroff, 69 Reboot, 70 Kexec, 71 Exit, 72 SwitchRoot, 73 Suspend, 74 Hibernate, 75 HybridSleep, 76 UnSupported, 77 None, 78 } 79 80 #[allow(dead_code)] 81 #[derive(Debug, Clone)] 82 pub enum Pattern { 83 Help, 84 Version, 85 System, 86 Host(String), 87 Machine(String), 88 Type(UnitType), 89 State(UnitState), 90 Property(String), 91 All, 92 Full, 93 Recursive, 94 Reverse, 95 JobMode(String), 96 ShowTypes, 97 IgnoreInhibitors, 98 KillWho(String), 99 Signal(String), 100 Now, 101 Quiet, 102 NoBlock, 103 NoWall, 104 NoReload, 105 NoLegend, 106 NoPaper, 107 NoAskPassword, 108 Global, 109 Runtime, 110 Force, 111 PresetMode, 112 Root(String), 113 Lines(i32), 114 Output(String), 115 Plain, 116 None, 117 } 118 119 impl ToString for Pattern { 120 fn to_string(&self) -> String { 121 match self { 122 Pattern::Help => "help".to_string(), 123 Pattern::Version => "version".to_string(), 124 Pattern::System => "system".to_string(), 125 Pattern::Host(s) => format!("{}={}", "host".to_string(), s), 126 Pattern::Machine(s) => format!("{}={}", "machine".to_string(), s), 127 Pattern::Type(t) => format!("{}={:?}", "type".to_string(), t), 128 Pattern::State(s) => format!("{}={:?}", "state".to_string(), s), 129 Pattern::Property(s) => format!("{}={}", "property".to_string(), s), 130 Pattern::All => "all".to_string(), 131 Pattern::Full => "full".to_string(), 132 Pattern::Recursive => "recursive".to_string(), 133 Pattern::Reverse => "reverse".to_string(), 134 Pattern::JobMode(s) => format!("{}={}", "job-mode".to_string(), s), 135 Pattern::ShowTypes => "show-types".to_string(), 136 Pattern::IgnoreInhibitors => "ignore-inhibitors".to_string(), 137 Pattern::KillWho(s) => format!("{}={}", "kill-who".to_string(), s), 138 Pattern::Signal(s) => format!("{}={}", "signal".to_string(), s), 139 Pattern::Now => "now".to_string(), 140 Pattern::Quiet => "quiet".to_string(), 141 Pattern::NoBlock => "no-block".to_string(), 142 Pattern::NoWall => "no-wall".to_string(), 143 Pattern::NoReload => "no-reload".to_string(), 144 Pattern::NoLegend => "no-legend".to_string(), 145 Pattern::NoPaper => "no-paper".to_string(), 146 Pattern::NoAskPassword => "no-ask-password".to_string(), 147 Pattern::Global => "global".to_string(), 148 Pattern::Runtime => "runtime".to_string(), 149 Pattern::Force => "force".to_string(), 150 Pattern::PresetMode => "preset-mode".to_string(), 151 Pattern::Root(s) => format!("{}={}", "root".to_string(), s), 152 Pattern::Lines(i) => format!("{}={}", "lines".to_string(), i), 153 Pattern::Output(s) => format!("{}={}", "output".to_string(), s), 154 Pattern::Plain => "plain".to_string(), 155 Pattern::None => "none".to_string(), 156 } 157 } 158 } 159 160 lazy_static! { 161 pub static ref CTL_COMMAND: HashMap<&'static str, CommandOperation> = { 162 let mut map = HashMap::new(); 163 map.insert("list-units", CommandOperation::ListUnits); 164 map.insert("list-sockets", CommandOperation::UnSupported); 165 map.insert("list-timers", CommandOperation::UnSupported); 166 map.insert("start", CommandOperation::Start); 167 map.insert("stop", CommandOperation::Stop); 168 map.insert("reload", CommandOperation::UnSupported); 169 map.insert("restart", CommandOperation::Restart); 170 map.insert("try-restart", CommandOperation::TryRestart); 171 map.insert("reload-or-restart", CommandOperation::ReloadOrRestart); 172 map.insert( 173 "reload-or-try-restart", 174 CommandOperation::ReloadOrTryRestart, 175 ); 176 map.insert("isolate", CommandOperation::Isolate); 177 map.insert("kill", CommandOperation::Kill); 178 map.insert("is-active", CommandOperation::IsActive); 179 map.insert("is-failed", CommandOperation::IsFailed); 180 map.insert("status", CommandOperation::Status); 181 map.insert("show", CommandOperation::Show); 182 map.insert("cat", CommandOperation::Cat); 183 map.insert("set-property", CommandOperation::SetProperty); 184 map.insert("help", CommandOperation::Help); 185 map.insert("reset-failed", CommandOperation::ResetFailed); 186 map.insert("list-dependencies", CommandOperation::ListDependencies); 187 map.insert("list-unit-files", CommandOperation::ListUnitFiles); 188 map.insert("enable", CommandOperation::Enable); 189 map.insert("disable", CommandOperation::Disable); 190 map.insert("reenable", CommandOperation::Reenable); 191 map.insert("preset", CommandOperation::Preset); 192 map.insert("preset-all", CommandOperation::PresetAll); 193 map.insert("is-enabled", CommandOperation::IsEnabled); 194 map.insert("mask", CommandOperation::Mask); 195 map.insert("unmask", CommandOperation::UnMask); 196 map.insert("link", CommandOperation::Link); 197 map.insert("add-wants", CommandOperation::AddWants); 198 map.insert("add-requires", CommandOperation::AddRequires); 199 map.insert("edit", CommandOperation::Edit); 200 map.insert("get-default", CommandOperation::GetDefault); 201 map.insert("set-default", CommandOperation::SetDefault); 202 map.insert("list-machines", CommandOperation::ListMachines); 203 map.insert("list-jobs", CommandOperation::ListJobs); 204 map.insert("cancel", CommandOperation::Cancel); 205 map.insert("snapshot", CommandOperation::Snapshot); 206 map.insert("delete", CommandOperation::Delete); 207 map.insert("show-environment", CommandOperation::ShowEnvironment); 208 map.insert("set-environment", CommandOperation::SetEnvironment); 209 map.insert("unset-environment", CommandOperation::UnsetEnvironment); 210 map.insert("import-environment", CommandOperation::ImportEnvironment); 211 map.insert("daemon-reload", CommandOperation::DeamonReload); 212 map.insert("daemon-reexec", CommandOperation::DeamonReexec); 213 map.insert("is-system-running", CommandOperation::IsSystemRunning); 214 map.insert("default", CommandOperation::Default); 215 map.insert("rescue", CommandOperation::Rescue); 216 map.insert("emergency", CommandOperation::Emergency); 217 map.insert("halt", CommandOperation::Halt); 218 map.insert("poweroff", CommandOperation::Poweroff); 219 map.insert("reboot", CommandOperation::Reboot); 220 map.insert("kexec", CommandOperation::Kexec); 221 map.insert("exit", CommandOperation::Exit); 222 map.insert("switch-root", CommandOperation::SwitchRoot); 223 map.insert("suspend", CommandOperation::Suspend); 224 map.insert("hibernate", CommandOperation::Hibernate); 225 map.insert("hybrid-sleep", CommandOperation::HybridSleep); 226 map 227 }; 228 pub static ref CTL_PATTERN: HashMap<&'static str, Pattern> = { 229 let mut map = HashMap::new(); 230 map.insert("help", Pattern::Help); 231 map.insert("version", Pattern::Version); 232 map.insert("system", Pattern::System); 233 map.insert("host", Pattern::Host(String::new())); 234 map.insert("machine", Pattern::Machine(String::new())); 235 map.insert("type", Pattern::Type(UnitType::Unknown)); 236 map.insert("state", Pattern::State(UnitState::Active)); 237 map.insert("property", Pattern::Property(String::new())); 238 map.insert("all", Pattern::All); 239 map.insert("full", Pattern::Full); 240 map.insert("recursive", Pattern::Recursive); 241 map.insert("reverse", Pattern::Reverse); 242 map.insert("job-mode", Pattern::JobMode(String::new())); 243 map.insert("show-types", Pattern::ShowTypes); 244 map.insert("ignore-inhibitors", Pattern::IgnoreInhibitors); 245 map.insert("kill-who", Pattern::KillWho(String::new())); 246 map.insert("signal", Pattern::Signal(String::new())); 247 map.insert("now", Pattern::Now); 248 map.insert("quiet", Pattern::Quiet); 249 map.insert("no-block", Pattern::NoBlock); 250 map.insert("no-wall", Pattern::NoWall); 251 map.insert("no-reload", Pattern::NoReload); 252 map.insert("no-legend", Pattern::NoLegend); 253 map.insert("no-paper", Pattern::NoPaper); 254 map.insert("no-ask-password", Pattern::NoAskPassword); 255 map.insert("global", Pattern::Global); 256 map.insert("runtime", Pattern::Runtime); 257 map.insert("force", Pattern::Force); 258 map.insert("preset-mode", Pattern::PresetMode); 259 map.insert("root", Pattern::Root(String::new())); 260 map.insert("lines", Pattern::Lines(-1)); 261 map.insert("output", Pattern::Output(String::new())); 262 map.insert("plain", Pattern::Plain); 263 map 264 }; 265 } 266 267 pub struct CtlParser; 268 269 impl CtlParser { 270 pub fn parse_ctl(s: &str) -> Result<Command, ParseError> { 271 let mut words = s.split_whitespace().collect::<Vec<&str>>(); 272 let mut ctl = Command::default(); 273 if let Some(op) = CTL_COMMAND.get(words.remove(0)) { 274 ctl.operation = op.clone(); 275 } 276 277 let mut opt: Option<Vec<String>>; 278 match ctl.operation { 279 CommandOperation::Start 280 | CommandOperation::Restart 281 | CommandOperation::Stop 282 | CommandOperation::TryRestart 283 | CommandOperation::Reload 284 | CommandOperation::AddRequires 285 | CommandOperation::AddWants 286 | CommandOperation::Kill 287 | CommandOperation::ListDependencies 288 | CommandOperation::Enable 289 | CommandOperation::Disable 290 | CommandOperation::Reenable 291 | CommandOperation::Preset 292 | CommandOperation::IsEnabled 293 | CommandOperation::Mask 294 | CommandOperation::UnMask 295 | CommandOperation::Link 296 | CommandOperation::Edit 297 | CommandOperation::SetDefault 298 | CommandOperation::SetEnvironment 299 | CommandOperation::UnsetEnvironment 300 | CommandOperation::ImportEnvironment => { 301 opt = Some(Vec::new()); 302 } 303 _ => { 304 opt = None; 305 } 306 } 307 308 for word in words { 309 if word.starts_with("--") { 310 ctl.patterns.push(Self::parse_pattern(&word[2..])?); 311 } else { 312 if let Some(ref mut v) = opt { 313 v.push(word.to_string()); 314 } else { 315 return Err(ParseError::new(ParseErrorType::EINVAL, s.to_string(), 0)); 316 } 317 } 318 } 319 320 ctl.args = opt; 321 322 Ok(ctl) 323 } 324 325 fn parse_pattern(s: &str) -> Result<Pattern, ParseError> { 326 let pattern: Pattern; 327 if s.contains("=") { 328 let words = s.split("=").collect::<Vec<&str>>(); 329 if words.len() > 2 { 330 return Err(ParseError::new(ParseErrorType::EINVAL, s.to_string(), 0)); 331 } 332 333 let option = CTL_PATTERN.get(words[0]); 334 if let Some(p) = option { 335 pattern = match *p { 336 Pattern::Host(_) => Pattern::Host(words[1].to_string()), 337 Pattern::Machine(_) => Pattern::Machine(words[1].to_string()), 338 Pattern::Type(_) => Pattern::Type(UnitParseUtil::parse_type(words[1])), 339 Pattern::State(_) => { 340 todo!() 341 } 342 Pattern::JobMode(_) => Pattern::JobMode(words[1].to_string()), 343 Pattern::KillWho(_) => Pattern::KillWho(words[1].to_string()), 344 Pattern::Lines(_) => match words[1].parse::<i32>() { 345 Ok(val) => Pattern::Lines(val), 346 Err(_) => { 347 return Err(ParseError::new(ParseErrorType::EINVAL, s.to_string(), 0)); 348 } 349 }, 350 Pattern::Output(_) => Pattern::Output(words[1].to_string()), 351 Pattern::Property(_) => Pattern::Property(words[1].to_string()), 352 Pattern::Root(_) => Pattern::Root(words[1].to_string()), 353 Pattern::Signal(_) => Pattern::Signal(words[1].to_string()), 354 _ => { 355 return Err(ParseError::new(ParseErrorType::EINVAL, s.to_string(), 0)); 356 } 357 }; 358 } else { 359 return Err(ParseError::new(ParseErrorType::EINVAL, s.to_string(), 0)); 360 } 361 } else { 362 pattern = match CTL_PATTERN.get(s) { 363 Some(val) => val.clone(), 364 None => { 365 return Err(ParseError::new(ParseErrorType::EINVAL, s.to_string(), 0)); 366 } 367 }; 368 } 369 return Ok(pattern); 370 } 371 } 372