xref: /DragonReach/src/parse/parse_util/mod.rs (revision b58bd5a17ff5d5ea5385adc176e215cd2b9a485c)
1 use crate::{
2     contants::{AF_INET, AF_INET6, IPV4_MIN_MTU, IPV6_MIN_MTU, PRIO_MAX, PRIO_MIN},
3     error::{ParseError, ParseErrorType},
4     task::cmdtask::CmdTask,
5     unit::{Unit, Url},
6     FileDescriptor,
7 };
8 
9 #[cfg(target_os = "dragonos")]
10 use drstd as std;
11 
12 use std::{
13     format, fs, path::Path, print, println, rc::Rc, string::String, string::ToString, vec, vec::Vec, os::unix::prelude::PermissionsExt,
14 };
15 
16 use std::os::unix::fs::MetadataExt;
17 
18 use super::{
19     parse_service::ServiceParser, parse_target::TargetParser, BASE_IEC, BASE_SI, SEC_UNIT_TABLE,
20 };
21 
22 #[derive(PartialEq)]
23 pub enum SizeBase {
24     IEC,
25     Si,
26 }
27 
28 pub struct UnitParseUtil;
29 
30 impl UnitParseUtil {
31     /// @brief 解析布尔值
32     ///
33     /// 将传入的字符串解析为布尔值
34     /// "yes","y","1","true","t","on"均可表示true
35     /// "no","n","0","false","f","off"均可表示false
36     ///
37     /// @param s 需解析的字符串
38     ///
39     /// @return 解析成功则返回Ok(解析后的值),否则返回Err
40     pub fn parse_boolean(s: &str) -> Result<bool, ParseError> {
41         let t_table: Vec<&str> = vec!["yes", "y", "1", "true", "t", "on"];
42         let f_table: Vec<&str> = vec!["no", "n", "0", "false", "f", "off"];
43 
44         if t_table.contains(&s) {
45             return Ok(true);
46         } else if f_table.contains(&s) {
47             return Ok(false);
48         }
49 
50         return Err(ParseError::new(ParseErrorType::EINVAL, String::new(), 0));
51     }
52 
53     /// @brief 解析pid
54     ///
55     /// 将传入的字符串解析为pid
56     ///
57     /// @param s 需解析的字符串
58     ///
59     /// @return 解析成功则返回Ok(解析后的值),否则返回Err
60     pub fn parse_pid(s: &str) -> Result<i32, ParseError> {
61         let s = s.trim();
62         //先使用u64变换
63         let pid_ul = match s.parse::<u64>() {
64             Ok(val) => val,
65             Err(_) => {
66                 return Err(ParseError::new(ParseErrorType::EINVAL, String::new(), 0));
67             }
68         };
69         let pid: i32 = pid_ul as i32;
70 
71         if (pid as u64) != pid_ul {
72             //如果在从pid_t转换为u64之后与之前不等,则说明发生了截断,返回错误
73             return Err(ParseError::new(ParseErrorType::EINVAL, String::new(), 0));
74         }
75 
76         if pid < 0 {
77             //pid小于0不合法
78             return Err(ParseError::new(ParseErrorType::EINVAL, String::new(), 0));
79         }
80 
81         return Ok(pid);
82     }
83 
84     /// @brief 解析pid
85     ///
86     /// 将传入的字符串解析为mode_t
87     ///
88     /// @param s 需解析的字符串
89     ///
90     /// @return 解析成功则返回Ok(解析后的值),否则返回Err
91     pub fn parse_mode(s: &str) -> Result<u32, ParseError> {
92         let s = s.trim();
93         let m = match u32::from_str_radix(s, 8) {
94             Ok(val) => val,
95             Err(_) => {
96                 return Err(ParseError::new(ParseErrorType::EINVAL, String::new(), 0));
97             }
98         };
99 
100         //如果模式大于权限的最大值则为非法权限,返回错误
101         if m > 0o7777 {
102             return Err(ParseError::new(ParseErrorType::EINVAL, String::new(), 0));
103         }
104 
105         return Ok(m);
106     }
107 
108     /// @brief 解析网络接口索引
109     ///
110     /// 将传入的字符串解析为网络接口索引具体值
111     ///
112     /// @param s 需解析的字符串
113     ///
114     /// @return 解析成功则返回Ok(解析后的值),否则返回Err
115     pub fn parse_ifindex(s: &str) -> Result<i32, ParseError> {
116         let s = s.trim();
117         let ret: i32 = match s.parse::<i32>() {
118             Ok(val) => val,
119             Err(_) => {
120                 return Err(ParseError::new(ParseErrorType::EINVAL, String::new(), 0));
121             }
122         };
123 
124         if ret <= 0 {
125             return Err(ParseError::new(ParseErrorType::EINVAL, String::new(), 0));
126         }
127 
128         return Ok(ret);
129     }
130 
131     /// @brief 解析最大传输单元(MTU)
132     ///
133     /// 将传入的字符串解析为具体值
134     ///
135     /// @param s 需解析的字符串
136     ///
137     /// @param family 网络地址族
138     ///
139     /// @return 解析成功则返回Ok(解析后的值),否则返回Err
140     pub fn parse_mtu(s: &str, family: i32) -> Result<u32, ParseError> {
141         let s = s.trim();
142         let mtu = match s.parse::<u64>() {
143             Ok(val) => val,
144             Err(_) => {
145                 //针对非法字符出错时
146                 return Err(ParseError::new(ParseErrorType::EINVAL, String::new(), 0));
147             }
148         };
149 
150         //针对数据溢出时的报错
151         if mtu > u32::MAX as u64 {
152             return Err(ParseError::new(ParseErrorType::EINVAL, String::new(), 0));
153         }
154 
155         let mtu: u32 = mtu as u32;
156 
157         let mut min_mtu: u32 = 0;
158         //判断mtu是否合法
159         if family == AF_INET6 {
160             min_mtu = IPV6_MIN_MTU;
161         } else if family == AF_INET {
162             min_mtu = IPV4_MIN_MTU;
163         } else {
164             return Err(ParseError::new(ParseErrorType::EINVAL, String::new(), 0));
165         }
166 
167         return Ok(mtu);
168     }
169 
170     /// @brief 解析Size
171     ///
172     /// 将传入的字符串解析为具体的字节数
173     /// 可支持IEC二进制后缀,也可支持SI十进制后缀
174     ///
175     /// @param s 需解析的字符串
176     ///
177     /// @param base 设置为IEC二进制后缀或者SI十进制后缀
178     ///
179     /// @return 解析成功则返回Ok(解析后的值),否则返回Err
180     pub fn parse_size(s: &str, base: SizeBase) -> Result<u64, ParseError> {
181         let s = s.trim();
182         //将s分解为数字和后缀部分
183         let (number_str, suffix) = match s.find(|c: char| !c.is_digit(10) && c != '.') {
184             Some(mid) => s.split_at(mid),
185             None => (s, ""),
186         };
187 
188         //获得数字部分的整数和小数部分
189         let (integer, fraction) = match number_str.find(".") {
190             Some(mid) => {
191                 let (integer, fraction) = number_str.split_at(mid);
192                 let integer = integer.parse::<u64>().unwrap();
193                 let fraction = match fraction[1..].parse::<u64>() {
194                     Ok(val) => val,
195                     Err(_) => {
196                         return Err(ParseError::new(ParseErrorType::EINVAL, String::new(), 0));
197                     }
198                 };
199                 (integer, fraction)
200             }
201             None => (number_str.parse::<u64>().unwrap(), 0),
202         };
203 
204         //从表中查找到后缀所对应的字节倍数
205         let mut factor: u64 = 0;
206         if base == SizeBase::IEC {
207             factor = match BASE_IEC.get(suffix) {
208                 Some(val) => *val,
209                 None => {
210                     return Err(ParseError::new(ParseErrorType::EINVAL, String::new(), 0));
211                 }
212             }
213         } else if base == SizeBase::Si {
214             factor = match BASE_SI.get(suffix) {
215                 Some(val) => *val,
216                 None => {
217                     return Err(ParseError::new(ParseErrorType::EINVAL, String::new(), 0));
218                 }
219             }
220         }
221 
222         Ok(integer * factor + (fraction * factor) / (10u64.pow(fraction.to_string().len() as u32)))
223     }
224 
225     /// @brief 解析扇区大小
226     ///
227     /// 将传入的字符串解析为具体的扇区大小
228     /// 若扇区大小小于512或者大于4096,将会返回错误,若扇区大小不为2的幂,返回错误。
229     ///
230     /// @param s 需解析的字符串
231     ///
232     /// @return 解析成功则返回Ok(解析后的值),否则返回Err
233     pub fn parse_sector_size(s: &str) -> Result<u64, ParseError> {
234         let s = s.trim();
235         let size: u64 = match s.parse::<u64>() {
236             Ok(val) => val,
237             Err(_) => {
238                 return Err(ParseError::new(ParseErrorType::EINVAL, String::new(), 0));
239             }
240         };
241 
242         if size < 512 || size > 4096 {
243             return Err(ParseError::new(ParseErrorType::EINVAL, String::new(), 0));
244         }
245 
246         //判断是否为2的幂,如果不是则报错
247         if (size & (size - 1)) != 0 {
248             return Err(ParseError::new(ParseErrorType::EINVAL, String::new(), 0));
249         }
250 
251         return Ok(size);
252     }
253 
254     /// @brief 解析范围
255     ///
256     /// 将传入的字符串解析为具体的范围
257     ///
258     /// @param s 需解析的字符串
259     ///
260     /// @return 解析成功则返回Ok(解析后的值),否则返回Err
261     pub fn parse_range(s: &str) -> Result<(u32, u32), ParseError> {
262         let mid = match s.find('-') {
263             Some(val) => val,
264             None => {
265                 //如果字符串中没有'-'符号,则表示一个值,所以范围两端都为该值
266                 let s = s.trim();
267                 let ret = match s.parse::<u32>() {
268                     Ok(val) => val,
269                     Err(_) => {
270                         return Err(ParseError::new(ParseErrorType::EINVAL, String::new(), 0));
271                     }
272                 };
273                 return Ok((ret, ret));
274             }
275         };
276 
277         //若字符串中存在'-',则分别解析为u32,解析失败则报错
278         let (l, r) = s.split_at(mid);
279 
280         let l = l.trim();
281         let l = match l.parse::<u32>() {
282             Ok(val) => val,
283             Err(_) => {
284                 return Err(ParseError::new(ParseErrorType::EINVAL, String::new(), 0));
285             }
286         };
287         let r = r.trim();
288         let r = match r.parse::<u32>() {
289             Ok(val) => val,
290             Err(_) => {
291                 return Err(ParseError::new(ParseErrorType::EINVAL, String::new(), 0));
292             }
293         };
294 
295         return Ok((l, r));
296     }
297 
298     /// @brief 解析文件描述符
299     ///
300     /// 将传入的字符串解析为文件描述符fd
301     ///
302     /// @param s 需解析的字符串
303     ///
304     /// @return 解析成功则返回Ok(解析后的值),否则返回Err
305     pub fn parse_fd(s: &str) -> Result<FileDescriptor, ParseError> {
306         let s = s.trim();
307         let fd = match s.parse::<i32>() {
308             Ok(val) => val,
309             Err(_) => {
310                 return Err(ParseError::new(ParseErrorType::EINVAL, String::new(), 0));
311             }
312         };
313 
314         if fd < 0 {
315             return Err(ParseError::new(ParseErrorType::EBADF, String::new(), 0));
316         }
317 
318         return Ok(FileDescriptor(fd as usize));
319     }
320 
321     /// @brief 解析nice
322     ///
323     /// 将传入的字符串解析为nice
324     ///
325     /// @param s 需解析的字符串
326     ///
327     /// @return 解析成功则返回Ok(解析后的值),否则返回Err
328     pub fn parse_nice(s: &str) -> Result<i8, ParseError> {
329         let s = s.trim();
330         let nice = match s.parse::<i8>() {
331             Ok(val) => val,
332             Err(_) => {
333                 return Err(ParseError::new(ParseErrorType::EINVAL, String::new(), 0));
334             }
335         };
336 
337         if nice > PRIO_MAX || nice < PRIO_MIN {
338             return Err(ParseError::new(ParseErrorType::ERANGE, String::new(), 0));
339         }
340 
341         return Ok(nice);
342     }
343 
344     /// @brief 解析端口号
345     ///
346     /// 将传入的字符串解析为端口号
347     ///
348     /// @param s 需解析的字符串
349     ///
350     /// @return 解析成功则返回Ok(解析后的值),否则返回Err
351     pub fn parse_ip_port(s: &str) -> Result<u16, ParseError> {
352         let s = s.trim();
353         let port = match s.parse::<u16>() {
354             Ok(val) => val,
355             Err(_) => {
356                 return Err(ParseError::new(ParseErrorType::EINVAL, String::new(), 0));
357             }
358         };
359 
360         if port == 0 {
361             return Err(ParseError::new(ParseErrorType::EINVAL, String::new(), 0));
362         }
363 
364         return Ok(port);
365     }
366 
367     /// @brief 解析端口范围
368     ///
369     /// 将传入的字符串解析为端口范围
370     ///
371     /// @param s 需解析的字符串
372     ///
373     /// @return 解析成功则返回Ok((u16,u16)),否则返回Err
374     pub fn parse_ip_port_range(s: &str) -> Result<(u16, u16), ParseError> {
375         let (l, h) = Self::parse_range(s)?;
376 
377         let l = l as u16;
378         let h = h as u16;
379         if l <= 0 || l >= 65535 || h <= 0 || h >= 65535 {
380             return Err(ParseError::new(ParseErrorType::EINVAL, String::new(), 0));
381         }
382 
383         return Ok((l, h));
384     }
385 
386     /// @brief 解析OOM(Out-of-Memory)分数调整值
387     ///
388     /// 将传入的字符串解析为OOM(Out-of-Memory)分数调整值
389     ///
390     /// @param s 需解析的字符串
391     ///
392     /// @return 解析成功则返回Ok(u32),否则返回Err
393     pub fn parse_ip_prefix_length(s: &str) -> Result<u32, ParseError> {
394         let len = match s.parse::<u32>() {
395             Ok(val) => val,
396             Err(_) => {
397                 return Err(ParseError::new(ParseErrorType::EINVAL, String::new(), 0));
398             }
399         };
400 
401         if len > 128 {
402             return Err(ParseError::new(ParseErrorType::ERANGE, String::new(), 0));
403         }
404 
405         return Ok(len);
406     }
407 
408     /// @brief 目前为简单的分割字符串,并未提供严谨的Url解析
409     ///
410     /// 将传入的字符串解析为Url结构体的Vec,若Url非法则返回错误
411     ///
412     /// @param s 需解析的字符串
413     ///
414     /// @return 解析成功则返回Ok(Url),否则返回Err
415     pub fn parse_url(s: &str) -> Result<Vec<Url>, ParseError> {
416         let mut url = Url::default();
417         let url_strs = s.split_whitespace().collect::<Vec<&str>>();
418         let mut urls = Vec::new();
419         for s in url_strs {
420             urls.push(Url {
421                 url_string: String::from(s),
422             })
423         }
424         return Ok(urls);
425     }
426 
427     /// @brief 将对应的str解析为对应Unit
428     ///
429     /// 将传入的字符串解析为Unit,解析失败返回错误
430     ///
431     /// @param path 需解析的文件
432     ///
433     /// @return 解析成功则返回Ok(Rc<dyn Unit>),否则返回Err
434     pub fn parse_unit<T: Unit>(path: &str) -> Result<Rc<T>, ParseError> {
435         return T::from_path(path);
436     }
437 
438     /// @brief 将对应的str解析为Rc<dyn Unit>
439     ///
440     /// 将传入的字符串解析为Rc<dyn Unit>,解析失败返回错误
441     ///
442     /// @param path 需解析的文件
443     ///
444     /// @return 解析成功则返回Ok(Rc<dyn Unit>),否则返回Err
445     pub fn parse_unit_no_type(path: &str) -> Result<Rc<dyn Unit>, ParseError> {
446         let idx = match path.rfind('.') {
447             Some(val) => val,
448             None => {
449                 return Err(ParseError::new(ParseErrorType::EFILE, path.to_string(), 0));
450             }
451         };
452 
453         if idx == path.len() - 1 {
454             //处理非法文件xxxx. 类型
455             return Err(ParseError::new(ParseErrorType::EFILE, path.to_string(), 0));
456         }
457 
458         let suffix = &path[idx + 1..];
459 
460         //通过文件后缀分发给不同类型的Unit解析器解析
461         let unit: Rc<dyn Unit> = match suffix {
462             //TODO: 目前为递归,后续应考虑从DragonReach管理的Unit表中寻找是否有该Unit,并且通过记录消除递归
463             "service" => ServiceParser::parse(path)?,
464             "target" => TargetParser::parse(path)?,
465             _ => {
466                 return Err(ParseError::new(ParseErrorType::EFILE, path.to_string(), 0));
467             }
468         };
469 
470         return Ok(unit);
471     }
472 
473     /// @brief 将对应的str解析为对应CmdTask
474     ///
475     /// 将传入的字符串解析为CmdTask组,解析失败返回错误
476     ///
477     /// @param path 需解析的文件
478     ///
479     /// @return 解析成功则返回Ok(Vec<CmdTask>>),否则返回Err
480     pub fn parse_cmd_task(s: &str) -> Result<Vec<CmdTask>, ParseError> {
481         //分拆成单词Vec
482         let cmds = s.split_whitespace().collect::<Vec<&str>>();
483         let mut tasks = Vec::new();
484         let mut i = 0;
485         while i < cmds.len() {
486             let mut cmd_task = CmdTask {
487                 path: String::new(),
488                 cmd: String::new(),
489                 ignore: false,
490             };
491             //匹配到这里时,这个单词肯定是路径,若路径以-开头则设置ignore
492             cmd_task.ignore = cmds[i].starts_with('-');
493 
494             //获取到一个CmdTask的路径部分
495             let mut path = "";
496             if cmd_task.ignore {
497                 path = &cmds[i][1..];
498             } else {
499                 path = &cmds[i];
500             }
501 
502             //得到的非绝对路径则不符合语法要求,报错
503             if !UnitParseUtil::is_valid_exec_path(path) {
504                 return Err(ParseError::new(ParseErrorType::EINVAL, String::new(), 0));
505             }
506 
507             cmd_task.path = String::from(path);
508 
509             //i += 1,继续匹配下一个单词
510             i += 1;
511             let mut cmd_str = String::new();
512             while i < cmds.len() && !UnitParseUtil::is_valid_exec_path(cmds[i]) {
513                 //命令可能会有多个单词,将多个命令整理成一个
514                 let cmd = cmds[i];
515                 cmd_str = format!("{} {}", cmd_str, cmd);
516                 i += 1;
517             }
518             cmd_task.cmd = cmd_str;
519             tasks.push(cmd_task);
520             //经过while到这里之后,cmds[i]对应的单词一点是路径,i不需要加一
521         }
522         return Ok(tasks);
523     }
524 
525     /// @brief 判断是否为绝对路径,以及指向是否为可执行文件或者sh脚本
526     ///
527     /// 目前该方法仅判断是否为绝对路径
528     ///
529     /// @param path 路径
530     ///
531     /// @return 解析成功则返回true,否则返回false
532     pub fn is_valid_exec_path(path: &str) -> bool {
533         if !path.starts_with("/"){
534             return false;
535         }
536         return true;
537 
538         //TODO: 后续应判断该文件是否为合法文件
539         //let path = Path::new(path);
540         //return Self::is_executable_file(path) || Self::is_shell_script(path);
541     }
542 
543     pub fn is_valid_file(path: &str) -> bool {
544         if !path.starts_with("/"){
545             return false;
546         }
547 
548         let path = Path::new(path);
549         if let Ok(matadata) = fs::metadata(path) {
550             return matadata.is_file();
551         }
552 
553         return false;
554     }
555 
556     fn is_executable_file(path: &Path) -> bool {
557         if let Ok(metadata) = fs::metadata(path) {
558             // 检查文件类型是否是普通文件并且具有可执行权限
559             if metadata.is_file(){
560                 let permissions = metadata.permissions().mode();
561                 return permissions & 0o111 != 0;
562             }
563         }
564         false
565     }
566 
567     fn is_shell_script(path: &Path) -> bool {
568         if let Some(extension) = path.extension() {
569             if extension == "sh" {
570                 return true;
571             }
572         }
573         false
574     }
575 
576     /// @brief 将对应的str解析为us(微秒)
577     ///
578     /// 将传入的字符串解析为秒数,解析失败返回错误
579     ///
580     /// @param path 需解析的文件
581     ///
582     /// @return 解析成功则返回Ok(u64),否则返回Err
583     pub fn parse_sec(s: &str) -> Result<u64, ParseError> {
584         //下列参数分别记录整数部分,小数部分以及单位
585         let mut integer: u64 = 0;
586         let mut frac: u64 = 0;
587         let mut unit: &str = "";
588 
589         match s.find('.') {
590             Some(idx) => {
591                 //解析整数部分
592                 integer = match s[..idx].parse::<u64>() {
593                     Ok(val) => val,
594                     Err(_) => {
595                         return Err(ParseError::new(ParseErrorType::EINVAL, String::new(), 0))
596                     }
597                 };
598                 //获得小数+单位的字符串
599                 let frac_and_unit = &s[(idx + 1)..];
600                 match frac_and_unit.find(|c: char| !c.is_digit(10)) {
601                     Some(val) => {
602                         //匹配小数部分
603                         frac = match frac_and_unit[..val].parse::<u64>() {
604                             Ok(val) => val,
605                             Err(_) => {
606                                 return Err(ParseError::new(
607                                     ParseErrorType::EINVAL,
608                                     String::new(),
609                                     0,
610                                 ))
611                             }
612                         };
613                         //单位部分
614                         unit = &frac_and_unit[val..];
615                     }
616                     None => {
617                         //没有单位的情况,直接匹配小数
618                         frac = match frac_and_unit.parse::<u64>() {
619                             Ok(val) => val,
620                             Err(_) => {
621                                 return Err(ParseError::new(
622                                     ParseErrorType::EINVAL,
623                                     String::new(),
624                                     0,
625                                 ))
626                             }
627                         };
628                         unit = "";
629                     }
630                 };
631             }
632             None => {
633                 //没有小数点则直接匹配整数部分和单位部分
634                 match s.find(|c: char| !c.is_digit(10)) {
635                     Some(idx) => {
636                         integer = match s[..idx].parse::<u64>() {
637                             Ok(val) => val,
638                             Err(_) => {
639                                 return Err(ParseError::new(
640                                     ParseErrorType::EINVAL,
641                                     String::new(),
642                                     0,
643                                 ))
644                             }
645                         };
646                         unit = &s[idx..];
647                     }
648                     None => {
649                         integer = match s.parse::<u64>() {
650                             Ok(val) => val,
651                             Err(_) => {
652                                 return Err(ParseError::new(
653                                     ParseErrorType::EINVAL,
654                                     String::new(),
655                                     0,
656                                 ))
657                             }
658                         };
659                         unit = "";
660                     }
661                 };
662             }
663         };
664 
665         //从时间单位转换表中获取到单位转换为ns的倍数
666         let factor = match SEC_UNIT_TABLE.get(unit) {
667             Some(val) => val,
668             None => {
669                 return Err(ParseError::new(ParseErrorType::EINVAL, String::new(), 0));
670             }
671         };
672 
673         //计算ns
674         return Ok(integer * factor + (frac * factor) / (10u64.pow(frac.to_string().len() as u32)));
675     }
676     /// @brief 判断对应路径是否为目录
677     ///
678     /// @param path 路径
679     ///
680     /// @return true/false
681     pub fn is_dir(path: &str) -> bool {
682         if let Ok(metadata) = fs::metadata(path) {
683             if metadata.is_dir() {
684                 return true;
685             }
686             return false;
687         }
688         return false;
689     }
690 }
691