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