1 use help::Help; 2 use regex::{Captures, Regex}; 3 use std::io::Read; 4 use std::os::unix::ffi::OsStrExt; 5 use std::{ 6 format, 7 fs::{self, File, OpenOptions}, 8 io::Write, 9 path::Path, 10 print, println, 11 string::String, 12 vec::Vec, 13 }; 14 15 use crate::shell::Shell; 16 use crate::Env; 17 use crate::ROOT_PATH; 18 19 mod help; 20 21 enum CommandType { 22 InternalCommand(BuildInCmd), 23 ExternalCommand(String), 24 } 25 26 pub struct Command { 27 args: Vec<String>, 28 cmd_type: CommandType, 29 } 30 31 #[derive(Debug, PartialEq, Eq, Clone)] 32 pub enum CommandError { 33 CommandNotFound(String), 34 InvalidArgument(String), 35 WrongArgumentCount(usize), 36 EnvironmentVariableNotFound(String), 37 PathNotFound(String), 38 FileNotFound(String), 39 DirectoryNotFound(String), 40 NotDirectory(String), 41 NotFile(String), 42 } 43 44 impl CommandError { 45 pub fn handle(e: CommandError) { 46 match e { 47 CommandError::CommandNotFound(command) => { 48 println!("cannot find command: {}", command) 49 } 50 CommandError::InvalidArgument(argument) => { 51 println!("invalid argument: {}", argument) 52 } 53 CommandError::WrongArgumentCount(count) => { 54 println!("argument count incorrect: {}", count) 55 } 56 CommandError::EnvironmentVariableNotFound(env) => { 57 println!("environment variable not found: {}", env); 58 } 59 CommandError::PathNotFound(path) => { 60 println!("cannot found file or dirctory: {}", path) 61 } 62 CommandError::FileNotFound(path) => { 63 println!("cannot found file: {}", path) 64 } 65 CommandError::DirectoryNotFound(path) => { 66 println!("cannot found dirctory: {}", path) 67 } 68 CommandError::NotDirectory(path) => { 69 println!("path is not a dirctory: {}", path) 70 } 71 CommandError::NotFile(path) => { 72 println!("path is not a file: {}", path) 73 } 74 }; 75 } 76 } 77 78 impl Command { 79 fn new(name: String, args: Vec<String>) -> Result<Command, CommandError> { 80 for BuildInCmd(cmd) in BuildInCmd::BUILD_IN_CMD { 81 if name == *cmd { 82 return Ok(Command { 83 args, 84 cmd_type: CommandType::InternalCommand(BuildInCmd(cmd)), 85 }); 86 } 87 } 88 89 return Ok(Command { 90 args, 91 cmd_type: CommandType::ExternalCommand(name), 92 }); 93 } 94 95 fn from_string(str: String) -> Result<Command, CommandError> { 96 let regex: Regex = Regex::new(r#"([^\s'"]|("[^"]*"|'[^']*'))+"#).unwrap(); 97 let hay = str.clone(); 98 let mut iter = regex 99 .captures_iter(hay.as_str()) 100 .map(|c| String::from(c.get(0).unwrap().as_str())); 101 // let mut iter = str.split_ascii_whitespace(); 102 let name = iter.next().unwrap(); 103 let re: Regex = Regex::new(r"\$[\w_]+").unwrap(); 104 let replacement = |caps: &Captures| -> String { 105 match Env::get(&String::from(&caps[0][1..])) { 106 Some(value) => value, 107 None => String::from(&caps[0]), 108 } 109 }; 110 let mut args: Vec<String> = Vec::new(); 111 for arg in iter.collect::<Vec<String>>().iter() { 112 let arg = re.replace_all(arg.as_str(), &replacement).to_string(); 113 match re.captures(arg.as_str()) { 114 Some(caps) => { 115 return Err(CommandError::EnvironmentVariableNotFound(String::from( 116 caps.get(0).unwrap().as_str(), 117 ))) 118 } 119 None => args.push(arg), 120 } 121 } 122 Command::new(name, args) 123 } 124 125 pub fn from_strings(str: String) -> Vec<Command> { 126 str.split(';') 127 .filter_map(|s| match Command::from_string(String::from(s)) { 128 Ok(s) => Some(s), 129 Err(e) => { 130 CommandError::handle(e); 131 None 132 } 133 }) 134 .collect::<Vec<Command>>() 135 } 136 } 137 138 pub struct BuildInCmd(pub &'static str); 139 140 impl BuildInCmd { 141 pub const BUILD_IN_CMD: &[BuildInCmd] = &[ 142 BuildInCmd("cd"), 143 BuildInCmd("ls"), 144 BuildInCmd("cat"), 145 BuildInCmd("touch"), 146 BuildInCmd("mkdir"), 147 BuildInCmd("rm"), 148 BuildInCmd("rmdir"), 149 BuildInCmd("pwd"), 150 BuildInCmd("cp"), 151 BuildInCmd("exec"), 152 BuildInCmd("echo"), 153 BuildInCmd("reboot"), 154 BuildInCmd("free"), 155 BuildInCmd("kill"), 156 BuildInCmd("help"), 157 BuildInCmd("export"), 158 BuildInCmd("env"), 159 BuildInCmd("compgen"), 160 BuildInCmd("complete"), 161 ]; 162 } 163 164 impl Shell { 165 pub fn exec_internal_command( 166 &mut self, 167 cmd: &str, 168 args: &Vec<String>, 169 ) -> Result<(), CommandError> { 170 match cmd { 171 "cd" => self.shell_cmd_cd(args), 172 "ls" => self.shell_cmd_ls(args), 173 "cat" => self.shell_cmd_cat(args), 174 "touch" => self.shell_cmd_touch(args), 175 "mkdir" => self.shell_cmd_mkdir(args), 176 "rm" => self.shell_cmd_rm(args), 177 "rmdir" => self.shell_cmd_rmdir(args), 178 "pwd" => self.shell_cmd_pwd(args), 179 "cp" => self.shell_cmd_cp(args), 180 "exec" => self.shell_cmd_exec(args), 181 "echo" => self.shell_cmd_echo(args), 182 "reboot" => self.shell_cmd_reboot(args), 183 "free" => self.shell_cmd_free(args), 184 "kill" => self.shell_cmd_kill(args), 185 "help" => self.shell_cmd_help(args), 186 "export" => self.shell_cmd_export(args), 187 "env" => self.shell_cmd_env(args), 188 "compgen" => self.shell_cmd_compgen(args), 189 "complete" => self.shell_cmd_complete(args), 190 191 _ => Err(CommandError::CommandNotFound(String::from(cmd))), 192 } 193 } 194 195 pub fn exec_external_command(&mut self, path: String, args: &Vec<String>) { 196 let mut full_args = args.clone(); 197 full_args.insert(0, path.clone()); 198 self.shell_cmd_exec(&full_args).unwrap_or_else(|e| { 199 let err = match e { 200 CommandError::FileNotFound(_) => CommandError::CommandNotFound(path.clone()), 201 _ => e, 202 }; 203 CommandError::handle(err); 204 }) 205 } 206 207 pub fn exec_command(&mut self, command: &Command) { 208 match &command.cmd_type { 209 CommandType::ExternalCommand(path) => { 210 self.exec_external_command(path.to_string(), &command.args); 211 } 212 213 CommandType::InternalCommand(BuildInCmd(cmd)) => { 214 match self.exec_internal_command(cmd, &command.args) { 215 Ok(_) => {} 216 Err(e) => CommandError::handle(e), 217 } 218 if command.args.contains(&String::from("--help")) { 219 Help::shell_help(cmd); 220 } 221 } 222 } 223 } 224 225 fn shell_cmd_cd(&mut self, args: &Vec<String>) -> Result<(), CommandError> { 226 if args.len() == 0 { 227 self.set_current_dir(&String::from(ROOT_PATH)); 228 return Ok(()); 229 } 230 if args.len() == 1 { 231 let mut path = args.get(0).unwrap().clone(); 232 match self.is_dir(&path) { 233 Ok(str) => path = str, 234 Err(e) => return Err(e), 235 } 236 self.set_current_dir(&path); 237 return Ok(()); 238 } 239 return Err(CommandError::WrongArgumentCount(args.len())); 240 } 241 242 fn shell_cmd_ls(&self, args: &Vec<String>) -> Result<(), CommandError> { 243 let mut path = String::new(); 244 if args.len() == 0 { 245 path = self.current_dir(); 246 } 247 if args.len() == 1 { 248 path = args.get(0).unwrap().clone(); 249 match self.is_dir(&path) { 250 Ok(str) => path = str, 251 Err(e) => return Err(e), 252 } 253 } 254 255 if path.is_empty() { 256 return Err(CommandError::WrongArgumentCount(args.len())); 257 } 258 259 let dir: fs::ReadDir; 260 match fs::read_dir(Path::new(&path)) { 261 Ok(readdir) => dir = readdir, 262 Err(_) => return Err(CommandError::InvalidArgument(path)), 263 } 264 for entry in dir { 265 let entry = entry.unwrap(); 266 if entry.file_type().unwrap().is_dir() { 267 crate::shell::Printer::print_color( 268 entry.file_name().as_bytes(), 269 0x000088ff, 270 0x00000000, 271 ); 272 print!(" "); 273 } else { 274 print!("{} ", entry.file_name().into_string().unwrap()); 275 } 276 } 277 println!(); 278 return Ok(()); 279 } 280 281 fn shell_cmd_cat(&self, args: &Vec<String>) -> Result<(), CommandError> { 282 if args.len() > 0 { 283 let mut path = args.get(0).unwrap().clone(); 284 let mut buf: Vec<u8> = Vec::new(); 285 286 match self.is_file(&path) { 287 Ok(str) => path = str, 288 Err(e) => return Err(e), 289 } 290 291 File::open(path).unwrap().read_to_end(&mut buf).unwrap(); 292 if args.len() == 1 { 293 println!("{}", String::from_utf8(buf.clone()).unwrap()); 294 } 295 296 if args.len() == 3 { 297 let mut target_path = args.get(2).unwrap().clone(); 298 match self.is_file(&target_path) { 299 Ok(str) => target_path = str, 300 Err(e) => return Err(e), 301 } 302 303 if args[1] == ">" { 304 match OpenOptions::new().write(true).open(target_path) { 305 Ok(mut file) => { 306 file.write_all(&buf).unwrap(); 307 } 308 Err(e) => print!("{e}"), 309 } 310 } else if args[1] == ">>" { 311 match OpenOptions::new().append(true).open(target_path) { 312 Ok(mut file) => { 313 file.write_all(&buf).unwrap(); 314 } 315 Err(e) => print!("{e}"), 316 } 317 } 318 } 319 return Ok(()); 320 } 321 return Err(CommandError::WrongArgumentCount(args.len())); 322 } 323 324 fn shell_cmd_touch(&self, args: &Vec<String>) -> Result<(), CommandError> { 325 if args.len() == 1 { 326 let mut path = args.get(0).unwrap().clone(); 327 match self.is_file(&path) { 328 Ok(str) => path = str, 329 Err(e) => return Err(e), 330 } 331 File::open(path).unwrap(); 332 return Ok(()); 333 } 334 return Err(CommandError::WrongArgumentCount(args.len())); 335 } 336 337 fn shell_cmd_mkdir(&self, args: &Vec<String>) -> Result<(), CommandError> { 338 if args.len() == 1 { 339 let path = args.get(0).unwrap(); 340 match fs::create_dir_all(path) { 341 Ok(_) => {} 342 Err(e) => { 343 print!("{e}") 344 } 345 } 346 return Ok(()); 347 } else { 348 return Err(CommandError::WrongArgumentCount(args.len())); 349 } 350 } 351 352 fn shell_cmd_rm(&self, args: &Vec<String>) -> Result<(), CommandError> { 353 if args.len() == 1 { 354 let mut path = args.get(0).unwrap().clone(); 355 // match fs::remove_file(path) { 356 // Ok(_) => {} 357 // Err(e) => { 358 // print!("{e}") 359 // } 360 // } 361 362 match self.is_file(&path) { 363 Ok(str) => path = str, 364 Err(e) => return Err(e), 365 } 366 367 let path_cstr = std::ffi::CString::new(path.clone()).unwrap(); 368 unsafe { 369 libc::syscall(libc::SYS_unlinkat, 0, path_cstr.as_ptr(), 0, 0, 0, 0); 370 } 371 return Ok(()); 372 } 373 return Err(CommandError::WrongArgumentCount(args.len())); 374 } 375 376 fn shell_cmd_rmdir(&self, args: &Vec<String>) -> Result<(), CommandError> { 377 if args.len() == 1 { 378 let mut path = args.get(0).unwrap().clone(); 379 match self.is_dir(&path) { 380 Ok(str) => path = str, 381 Err(e) => return Err(e), 382 } 383 384 let path_cstr = std::ffi::CString::new(path).unwrap(); 385 unsafe { libc::unlinkat(0, path_cstr.as_ptr(), libc::AT_REMOVEDIR) }; 386 return Ok(()); 387 } 388 return Err(CommandError::WrongArgumentCount(args.len())); 389 } 390 391 fn shell_cmd_pwd(&self, args: &Vec<String>) -> Result<(), CommandError> { 392 if args.len() == 0 { 393 println!("{}", self.current_dir()); 394 return Ok(()); 395 } 396 return Err(CommandError::WrongArgumentCount(args.len())); 397 } 398 399 fn shell_cmd_cp(&self, args: &Vec<String>) -> Result<(), CommandError> { 400 if args.len() == 2 { 401 let mut src_path = args.get(0).unwrap().clone(); 402 let mut target_path = args.get(1).unwrap().clone(); 403 404 match self.is_file(&src_path) { 405 Ok(str) => src_path = str, 406 Err(e) => return Err(e), 407 } 408 409 match self.is_file_or_dir(&target_path) { 410 Ok(str) => target_path = str, 411 Err(e) => { 412 let prefix = &target_path[..target_path.rfind('/').unwrap_or(0)]; 413 if !Path::new(prefix).is_dir() { 414 return Err(e); 415 } 416 } 417 } 418 419 if Path::new(&src_path).is_dir() { 420 let name = &src_path[src_path.rfind('/').unwrap_or(0)..]; 421 target_path = format!("{}/{}", target_path, name); 422 } 423 424 let mut src_file = File::open(&src_path).unwrap(); 425 let mut target_file = File::create(target_path).unwrap(); 426 let mut buf: Vec<u8> = Vec::new(); 427 src_file.read_to_end(&mut buf).unwrap(); 428 target_file.write_all(&buf).unwrap(); 429 return Ok(()); 430 } 431 return Err(CommandError::WrongArgumentCount(args.len())); 432 } 433 434 pub fn shell_cmd_exec(&self, args: &Vec<String>) -> Result<(), CommandError> { 435 if args.len() <= 0 { 436 return Err(CommandError::WrongArgumentCount(args.len())); 437 } 438 let path = args.get(0).unwrap(); 439 let mut real_path = String::new(); 440 if !path.starts_with('/') && !path.starts_with('.') { 441 let mut prefix_collection = Env::path(); 442 prefix_collection.insert(0, self.current_dir()); 443 for prefix in prefix_collection { 444 real_path = format!("{}/{}", prefix, path); 445 if Path::new(&real_path).is_file() { 446 break; 447 } 448 } 449 } 450 451 match self.is_file(&real_path) { 452 Ok(str) => real_path = str, 453 Err(e) => return Err(e), 454 } 455 456 let pid: libc::pid_t = unsafe { 457 libc::syscall(libc::SYS_fork, 0, 0, 0, 0, 0, 0) 458 .try_into() 459 .unwrap() 460 }; 461 462 let mut retval = 0; 463 if pid == 0 { 464 let path_cstr = std::ffi::CString::new(real_path).unwrap(); 465 let mut argv: *const *const i8 = std::ptr::null(); 466 if args.len() > 1 { 467 let args_cstr = args 468 .iter() 469 .skip(1) 470 .map(|str| std::ffi::CString::new(str.as_str()).unwrap()) 471 .collect::<Vec<std::ffi::CString>>(); 472 let mut args_ptr = args_cstr 473 .iter() 474 .map(|c_str| c_str.as_ptr()) 475 .collect::<Vec<*const i8>>(); 476 args_ptr.push(std::ptr::null()); 477 argv = args_ptr.as_ptr(); 478 } 479 unsafe { 480 libc::execv(path_cstr.as_ptr(), argv); 481 } 482 } else { 483 if args.last().unwrap() != &"&" { 484 unsafe { libc::waitpid(pid, &mut retval as *mut i32, 0) }; 485 } else { 486 println!("[1] {}", pid); 487 } 488 } 489 return Ok(()); 490 } 491 492 fn shell_cmd_echo(&self, args: &Vec<String>) -> Result<(), CommandError> { 493 if args.len() > 0 { 494 let str = args.get(0).unwrap(); 495 if args.len() == 1 { 496 println!("{str}"); 497 } 498 499 if args.len() == 3 { 500 let mut target_path = args.get(2).unwrap().clone(); 501 match self.is_file(&target_path) { 502 Ok(str) => target_path = str, 503 Err(e) => return Err(e), 504 } 505 506 if args[1] == ">" { 507 match OpenOptions::new().write(true).open(target_path) { 508 Ok(mut file) => { 509 file.write_all(str.as_bytes()).unwrap(); 510 } 511 Err(e) => print!("{e}"), 512 } 513 } else if args[1] == ">>" { 514 match OpenOptions::new().append(true).open(target_path) { 515 Ok(mut file) => { 516 file.write_all(str.as_bytes()).unwrap(); 517 } 518 Err(e) => print!("{e}"), 519 } 520 } 521 } 522 return Ok(()); 523 } 524 return Err(CommandError::WrongArgumentCount(args.len())); 525 } 526 527 fn shell_cmd_reboot(&self, args: &Vec<String>) -> Result<(), CommandError> { 528 if args.len() == 0 { 529 unsafe { libc::syscall(libc::SYS_reboot, 0, 0, 0, 0, 0, 0) }; 530 return Ok(()); 531 } else { 532 return Err(CommandError::WrongArgumentCount(args.len())); 533 } 534 } 535 536 fn shell_cmd_free(&self, args: &Vec<String>) -> Result<(), CommandError> { 537 if args.len() == 1 && args.get(0).unwrap() != "-m" { 538 return Err(CommandError::InvalidArgument( 539 args.get(0).unwrap().to_string(), 540 )); 541 } 542 543 struct Mstat { 544 total: u64, // 计算机的总内存数量大小 545 used: u64, // 已使用的内存大小 546 free: u64, // 空闲物理页所占的内存大小 547 shared: u64, // 共享的内存大小 548 cache_used: u64, // 位于slab缓冲区中的已使用的内存大小 549 cache_free: u64, // 位于slab缓冲区中的空闲的内存大小 550 available: u64, // 系统总空闲内存大小(包括kmalloc缓冲区) 551 } 552 553 let mut mst = Mstat { 554 total: 0, 555 used: 0, 556 free: 0, 557 shared: 0, 558 cache_used: 0, 559 cache_free: 0, 560 available: 0, 561 }; 562 563 let mut info_file = File::open("/proc/meminfo").unwrap(); 564 let mut buf: Vec<u8> = Vec::new(); 565 info_file.read_to_end(&mut buf).unwrap(); 566 let str = String::from_utf8(buf).unwrap(); 567 let info = str 568 .split(&['\n', '\t', ' ']) 569 .filter_map(|str| str.parse::<u64>().ok()) 570 .collect::<Vec<u64>>(); 571 mst.total = *info.get(0).unwrap(); 572 mst.free = *info.get(1).unwrap(); 573 mst.used = mst.total - mst.free; 574 575 print!("\ttotal\t\tused\t\tfree\t\tshared\t\tcache_used\tcache_free\tavailable\n"); 576 print!("Mem:\t"); 577 578 if args.len() == 0 { 579 print!( 580 "{}\t\t{}\t\t{}\t\t{}\t\t{}\t\t{}\t\t{}\n", 581 mst.total, 582 mst.used, 583 mst.free, 584 mst.shared, 585 mst.cache_used, 586 mst.cache_free, 587 mst.available 588 ); 589 } else { 590 print!( 591 "{}\t\t{}\t\t{}\t\t{}\t\t{}\t\t{}\n", 592 mst.total >> 10, 593 mst.used >> 10, 594 mst.free >> 10, 595 mst.shared >> 10, 596 mst.cache_used >> 10, 597 mst.available >> 10 598 ); 599 } 600 Ok(()) 601 } 602 603 fn shell_cmd_kill(&self, args: &Vec<String>) -> Result<(), CommandError> { 604 if args.len() == 1 { 605 let pid: i32; 606 match args.get(0).unwrap().parse::<i32>() { 607 Ok(x) => pid = x, 608 Err(_) => { 609 return Err(CommandError::InvalidArgument( 610 args.get(0).unwrap().to_string(), 611 )) 612 } 613 } 614 unsafe { 615 libc::kill(pid, libc::SIGKILL); 616 } 617 return Ok(()); 618 } else { 619 return Err(CommandError::WrongArgumentCount(args.len())); 620 } 621 } 622 623 fn shell_cmd_help(&self, args: &Vec<String>) -> Result<(), CommandError> { 624 if args.len() == 0 { 625 for BuildInCmd(cmd) in BuildInCmd::BUILD_IN_CMD { 626 Help::shell_help(cmd) 627 } 628 return Ok(()); 629 } 630 return Err(CommandError::WrongArgumentCount(args.len())); 631 } 632 633 fn shell_cmd_export(&self, args: &Vec<String>) -> Result<(), CommandError> { 634 if args.len() == 1 { 635 let pair = args.get(0).unwrap().split('=').collect::<Vec<&str>>(); 636 637 if pair.len() == 2 && !pair.contains(&"") { 638 let name = pair.get(0).unwrap().to_string(); 639 let value = pair.get(1).unwrap().to_string(); 640 Env::insert(name, value); 641 return Ok(()); 642 } else { 643 return Err(CommandError::InvalidArgument(args.get(0).unwrap().clone())); 644 } 645 } 646 return Err(CommandError::WrongArgumentCount(args.len())); 647 } 648 649 fn shell_cmd_env(&self, args: &Vec<String>) -> Result<(), CommandError> { 650 if args.len() == 0 { 651 let mut file = File::open("/etc/profile").unwrap(); 652 let mut buf: Vec<u8> = Vec::new(); 653 file.read_to_end(&mut buf).unwrap(); 654 println!("{}", String::from_utf8(buf).unwrap()); 655 return Ok(()); 656 } else { 657 return Err(CommandError::InvalidArgument(args.get(0).unwrap().clone())); 658 } 659 } 660 661 fn shell_cmd_compgen(&self, _args: &Vec<String>) -> Result<(), CommandError> { 662 Ok(()) 663 } 664 665 fn shell_cmd_complete(&self, _args: &Vec<String>) -> Result<(), CommandError> { 666 Ok(()) 667 } 668 669 fn path_format(&self, path: &String) -> Result<String, CommandError> { 670 let mut abs_path = path.clone(); 671 if !path.starts_with('/') { 672 abs_path = format!("{}/{}", self.current_dir(), path); 673 } 674 if let Ok(path) = Path::new(&abs_path).canonicalize() { 675 let mut fmt_path = path.to_str().unwrap().to_string(); 676 let replacement = |_caps: ®ex::Captures| -> String { String::from("/") }; 677 let re = regex::Regex::new(r"\/{2,}").unwrap(); 678 fmt_path = re.replace_all(fmt_path.as_str(), replacement).to_string(); 679 return Ok(fmt_path); 680 } else { 681 return Err(CommandError::PathNotFound(path.clone())); 682 } 683 } 684 685 fn is_file(&self, path_str: &String) -> Result<String, CommandError> { 686 match self.path_format(path_str) { 687 Ok(path_str) => { 688 let path = Path::new(&path_str); 689 if !path.is_file() { 690 return Err(CommandError::NotFile(path_str.clone())); 691 }; 692 Ok(path_str) 693 } 694 Err(_) => Err(CommandError::FileNotFound(path_str.clone())), 695 } 696 } 697 698 fn is_dir(&self, path_str: &String) -> Result<String, CommandError> { 699 match self.path_format(path_str) { 700 Ok(path_str) => { 701 let path = Path::new(&path_str); 702 if !path.is_dir() { 703 return Err(CommandError::NotDirectory(path_str.clone())); 704 }; 705 Ok(path_str) 706 } 707 Err(_) => Err(CommandError::DirectoryNotFound(path_str.clone())), 708 } 709 } 710 711 fn is_file_or_dir(&self, path_str: &String) -> Result<String, CommandError> { 712 match self.path_format(path_str) { 713 Ok(path_str) => Ok(path_str), 714 Err(_) => Err(CommandError::PathNotFound(path_str.clone())), 715 } 716 } 717 } 718