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 let mut args = args.clone(); 436 if args.len() <= 0 { 437 return Err(CommandError::WrongArgumentCount(args.len())); 438 } 439 let path = args.get(0).unwrap(); 440 let mut real_path = String::new(); 441 if !path.contains('/') { 442 let mut dir_collection = Env::path(); 443 dir_collection.insert(0, self.current_dir()); 444 for dir in dir_collection { 445 real_path = format!("{}/{}", dir, path); 446 if Path::new(&real_path).is_file() { 447 break; 448 } 449 } 450 if real_path.is_empty() { 451 return Err(CommandError::FileNotFound(path.clone())); 452 } 453 } else { 454 match self.is_file(path) { 455 Ok(path) => real_path = path, 456 Err(e) => return Err(e), 457 } 458 } 459 460 let pid: libc::pid_t = unsafe { 461 libc::syscall(libc::SYS_fork, 0, 0, 0, 0, 0, 0) 462 .try_into() 463 .unwrap() 464 }; 465 466 let name = &real_path[real_path.rfind('/').unwrap_or(0)..]; 467 *args.get_mut(0).unwrap() = name.to_string(); 468 let mut retval = 0; 469 if pid == 0 { 470 let path_cstr = std::ffi::CString::new(real_path).unwrap(); 471 let args_cstr = args 472 .iter() 473 .map(|str| std::ffi::CString::new(str.as_str()).unwrap()) 474 .collect::<Vec<std::ffi::CString>>(); 475 let mut args_ptr = args_cstr 476 .iter() 477 .map(|c_str| c_str.as_ptr()) 478 .collect::<Vec<*const i8>>(); 479 args_ptr.push(std::ptr::null()); 480 let argv = args_ptr.as_ptr(); 481 482 unsafe { 483 libc::execv(path_cstr.as_ptr(), argv); 484 } 485 } else { 486 if args.last().unwrap() != &"&" { 487 unsafe { libc::waitpid(pid, &mut retval as *mut i32, 0) }; 488 } else { 489 println!("[1] {}", pid); 490 } 491 } 492 return Ok(()); 493 } 494 495 fn shell_cmd_echo(&self, args: &Vec<String>) -> Result<(), CommandError> { 496 if args.len() > 0 { 497 let str = args.get(0).unwrap(); 498 if args.len() == 1 { 499 println!("{str}"); 500 } 501 502 if args.len() == 3 { 503 let mut target_path = args.get(2).unwrap().clone(); 504 match self.is_file(&target_path) { 505 Ok(str) => target_path = str, 506 Err(e) => return Err(e), 507 } 508 509 if args[1] == ">" { 510 match OpenOptions::new().write(true).open(target_path) { 511 Ok(mut file) => { 512 file.write_all(str.as_bytes()).unwrap(); 513 } 514 Err(e) => print!("{e}"), 515 } 516 } else if args[1] == ">>" { 517 match OpenOptions::new().append(true).open(target_path) { 518 Ok(mut file) => { 519 file.write_all(str.as_bytes()).unwrap(); 520 } 521 Err(e) => print!("{e}"), 522 } 523 } 524 } 525 return Ok(()); 526 } 527 return Err(CommandError::WrongArgumentCount(args.len())); 528 } 529 530 fn shell_cmd_reboot(&self, args: &Vec<String>) -> Result<(), CommandError> { 531 if args.len() == 0 { 532 unsafe { libc::syscall(libc::SYS_reboot, 0, 0, 0, 0, 0, 0) }; 533 return Ok(()); 534 } else { 535 return Err(CommandError::WrongArgumentCount(args.len())); 536 } 537 } 538 539 fn shell_cmd_free(&self, args: &Vec<String>) -> Result<(), CommandError> { 540 if args.len() == 1 && args.get(0).unwrap() != "-m" { 541 return Err(CommandError::InvalidArgument( 542 args.get(0).unwrap().to_string(), 543 )); 544 } 545 546 struct Mstat { 547 total: u64, // 计算机的总内存数量大小 548 used: u64, // 已使用的内存大小 549 free: u64, // 空闲物理页所占的内存大小 550 shared: u64, // 共享的内存大小 551 cache_used: u64, // 位于slab缓冲区中的已使用的内存大小 552 cache_free: u64, // 位于slab缓冲区中的空闲的内存大小 553 available: u64, // 系统总空闲内存大小(包括kmalloc缓冲区) 554 } 555 556 let mut mst = Mstat { 557 total: 0, 558 used: 0, 559 free: 0, 560 shared: 0, 561 cache_used: 0, 562 cache_free: 0, 563 available: 0, 564 }; 565 566 let mut info_file = File::open("/proc/meminfo").unwrap(); 567 let mut buf: Vec<u8> = Vec::new(); 568 info_file.read_to_end(&mut buf).unwrap(); 569 let str = String::from_utf8(buf).unwrap(); 570 let info = str 571 .split(&['\n', '\t', ' ']) 572 .filter_map(|str| str.parse::<u64>().ok()) 573 .collect::<Vec<u64>>(); 574 mst.total = *info.get(0).unwrap(); 575 mst.free = *info.get(1).unwrap(); 576 mst.used = mst.total - mst.free; 577 578 print!("\ttotal\t\tused\t\tfree\t\tshared\t\tcache_used\tcache_free\tavailable\n"); 579 print!("Mem:\t"); 580 581 if args.len() == 0 { 582 print!( 583 "{}\t\t{}\t\t{}\t\t{}\t\t{}\t\t{}\t\t{}\n", 584 mst.total, 585 mst.used, 586 mst.free, 587 mst.shared, 588 mst.cache_used, 589 mst.cache_free, 590 mst.available 591 ); 592 } else { 593 print!( 594 "{}\t\t{}\t\t{}\t\t{}\t\t{}\t\t{}\n", 595 mst.total >> 10, 596 mst.used >> 10, 597 mst.free >> 10, 598 mst.shared >> 10, 599 mst.cache_used >> 10, 600 mst.available >> 10 601 ); 602 } 603 Ok(()) 604 } 605 606 fn shell_cmd_kill(&self, args: &Vec<String>) -> Result<(), CommandError> { 607 if args.len() == 1 { 608 let pid: i32; 609 match args.get(0).unwrap().parse::<i32>() { 610 Ok(x) => pid = x, 611 Err(_) => { 612 return Err(CommandError::InvalidArgument( 613 args.get(0).unwrap().to_string(), 614 )) 615 } 616 } 617 unsafe { 618 libc::kill(pid, libc::SIGKILL); 619 } 620 return Ok(()); 621 } else { 622 return Err(CommandError::WrongArgumentCount(args.len())); 623 } 624 } 625 626 fn shell_cmd_help(&self, args: &Vec<String>) -> Result<(), CommandError> { 627 if args.len() == 0 { 628 for BuildInCmd(cmd) in BuildInCmd::BUILD_IN_CMD { 629 Help::shell_help(cmd) 630 } 631 return Ok(()); 632 } 633 return Err(CommandError::WrongArgumentCount(args.len())); 634 } 635 636 fn shell_cmd_export(&self, args: &Vec<String>) -> Result<(), CommandError> { 637 if args.len() == 1 { 638 let pair = args.get(0).unwrap().split('=').collect::<Vec<&str>>(); 639 640 if pair.len() == 2 && !pair.contains(&"") { 641 let name = pair.get(0).unwrap().to_string(); 642 let value = pair.get(1).unwrap().to_string(); 643 Env::insert(name, value); 644 return Ok(()); 645 } else { 646 return Err(CommandError::InvalidArgument(args.get(0).unwrap().clone())); 647 } 648 } 649 return Err(CommandError::WrongArgumentCount(args.len())); 650 } 651 652 fn shell_cmd_env(&self, args: &Vec<String>) -> Result<(), CommandError> { 653 if args.len() == 0 { 654 let mut file = File::open("/etc/profile").unwrap(); 655 let mut buf: Vec<u8> = Vec::new(); 656 file.read_to_end(&mut buf).unwrap(); 657 println!("{}", String::from_utf8(buf).unwrap()); 658 return Ok(()); 659 } else { 660 return Err(CommandError::InvalidArgument(args.get(0).unwrap().clone())); 661 } 662 } 663 664 fn shell_cmd_compgen(&self, _args: &Vec<String>) -> Result<(), CommandError> { 665 Ok(()) 666 } 667 668 fn shell_cmd_complete(&self, _args: &Vec<String>) -> Result<(), CommandError> { 669 Ok(()) 670 } 671 672 fn path_format(&self, path: &String) -> Result<String, CommandError> { 673 let mut abs_path = path.clone(); 674 if !path.starts_with('/') { 675 abs_path = format!("{}/{}", self.current_dir(), path); 676 } 677 if let Ok(path) = Path::new(&abs_path).canonicalize() { 678 let mut fmt_path = path.to_str().unwrap().to_string(); 679 let replacement = |_caps: ®ex::Captures| -> String { String::from("/") }; 680 let re = regex::Regex::new(r"\/{2,}").unwrap(); 681 fmt_path = re.replace_all(fmt_path.as_str(), replacement).to_string(); 682 return Ok(fmt_path); 683 } else { 684 return Err(CommandError::PathNotFound(path.clone())); 685 } 686 } 687 688 fn is_file(&self, path_str: &String) -> Result<String, CommandError> { 689 match self.path_format(path_str) { 690 Ok(path_str) => { 691 let path = Path::new(&path_str); 692 if !path.is_file() { 693 return Err(CommandError::NotFile(path_str.clone())); 694 }; 695 Ok(path_str) 696 } 697 Err(_) => Err(CommandError::FileNotFound(path_str.clone())), 698 } 699 } 700 701 fn is_dir(&self, path_str: &String) -> Result<String, CommandError> { 702 match self.path_format(path_str) { 703 Ok(path_str) => { 704 let path = Path::new(&path_str); 705 if !path.is_dir() { 706 return Err(CommandError::NotDirectory(path_str.clone())); 707 }; 708 Ok(path_str) 709 } 710 Err(_) => Err(CommandError::DirectoryNotFound(path_str.clone())), 711 } 712 } 713 714 fn is_file_or_dir(&self, path_str: &String) -> Result<String, CommandError> { 715 match self.path_format(path_str) { 716 Ok(path_str) => Ok(path_str), 717 Err(_) => Err(CommandError::PathNotFound(path_str.clone())), 718 } 719 } 720 } 721