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