xref: /DragonReach/src/systemctl/ctl_parser/mod.rs (revision dfd3fd9812f3584f9392934d1254e24d17661b2d)
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