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