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