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