xref: /DragonReach/src/systemctl/ctl_parser/mod.rs (revision 2069cc0dc0984a2981454b00316ba607f88ac512) !
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 {
to_string(&self) -> String116     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::ListTimers);
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     /// # parse_ctl - 解析控制命令
267     ///
268     /// 该函数用于解析一个字符串形式的控制命令,并将其转换为`Command`
269     ///
270     /// ## 参数
271     ///
272     /// - s: &str,需要解析的控制命令字符串。
273     ///
274     /// ## 返回值
275     ///
276     /// - Ok(Command): 解析成功的控制命令。
277     /// - Err(ParseError): 解析失败时产生的错误。
278     ///
279     /// ## 错误处理
280     ///
281     /// 当输入的字符串不符合预期格式时,函数会返回一个`ParseError`错误。
parse_ctl(s: &str) -> Result<Command, ParseError>282     pub fn parse_ctl(s: &str) -> Result<Command, ParseError> {
283         let mut words = s.split_whitespace().collect::<Vec<&str>>();
284         let mut ctl = Command::default();
285         if let Some(op) = CTL_COMMAND.get(words.remove(0)) {
286             ctl.operation = op.clone();
287         }
288 
289         let mut opt: Option<Vec<String>>;
290         match ctl.operation {
291             CommandOperation::Start
292             | CommandOperation::Restart
293             | CommandOperation::Stop
294             | CommandOperation::TryRestart
295             | CommandOperation::Reload
296             | CommandOperation::AddRequires
297             | CommandOperation::AddWants
298             | CommandOperation::Kill
299             | CommandOperation::ListDependencies
300             | CommandOperation::Enable
301             | CommandOperation::Disable
302             | CommandOperation::Reenable
303             | CommandOperation::Preset
304             | CommandOperation::IsEnabled
305             | CommandOperation::Mask
306             | CommandOperation::UnMask
307             | CommandOperation::Link
308             | CommandOperation::Edit
309             | CommandOperation::SetDefault
310             | CommandOperation::SetEnvironment
311             | CommandOperation::UnsetEnvironment
312             | CommandOperation::ImportEnvironment => {
313                 opt = Some(Vec::new());
314             }
315             _ => {
316                 opt = None;
317             }
318         }
319 
320         for word in words {
321             if word.starts_with("--") {
322                 ctl.patterns.push(Self::parse_pattern(&word[2..])?);
323             } else {
324                 if let Some(ref mut v) = opt {
325                     v.push(word.to_string());
326                 } else {
327                     return Err(ParseError::new(ParseErrorType::EINVAL, s.to_string(), 0));
328                 }
329             }
330         }
331 
332         ctl.args = opt;
333 
334         Ok(ctl)
335     }
336 
parse_pattern(s: &str) -> Result<Pattern, ParseError>337     fn parse_pattern(s: &str) -> Result<Pattern, ParseError> {
338         let pattern: Pattern;
339         if s.contains("=") {
340             let words = s.split("=").collect::<Vec<&str>>();
341             if words.len() > 2 {
342                 return Err(ParseError::new(ParseErrorType::EINVAL, s.to_string(), 0));
343             }
344 
345             let option = CTL_PATTERN.get(words[0]);
346             if let Some(p) = option {
347                 pattern = match *p {
348                     Pattern::Host(_) => Pattern::Host(words[1].to_string()),
349                     Pattern::Machine(_) => Pattern::Machine(words[1].to_string()),
350                     Pattern::Type(_) => Pattern::Type(UnitParseUtil::parse_type(words[1])),
351                     Pattern::State(_) => {
352                         todo!()
353                     }
354                     Pattern::JobMode(_) => Pattern::JobMode(words[1].to_string()),
355                     Pattern::KillWho(_) => Pattern::KillWho(words[1].to_string()),
356                     Pattern::Lines(_) => match words[1].parse::<i32>() {
357                         Ok(val) => Pattern::Lines(val),
358                         Err(_) => {
359                             return Err(ParseError::new(ParseErrorType::EINVAL, s.to_string(), 0));
360                         }
361                     },
362                     Pattern::Output(_) => Pattern::Output(words[1].to_string()),
363                     Pattern::Property(_) => Pattern::Property(words[1].to_string()),
364                     Pattern::Root(_) => Pattern::Root(words[1].to_string()),
365                     Pattern::Signal(_) => Pattern::Signal(words[1].to_string()),
366                     _ => {
367                         return Err(ParseError::new(ParseErrorType::EINVAL, s.to_string(), 0));
368                     }
369                 };
370             } else {
371                 return Err(ParseError::new(ParseErrorType::EINVAL, s.to_string(), 0));
372             }
373         } else {
374             pattern = match CTL_PATTERN.get(s) {
375                 Some(val) => val.clone(),
376                 None => {
377                     return Err(ParseError::new(ParseErrorType::EINVAL, s.to_string(), 0));
378                 }
379             };
380         }
381         return Ok(pattern);
382     }
383 }
384