From a7a0c8bf2c9f828c6b29ac7f842ab60acf78d314 Mon Sep 17 00:00:00 2001 From: Mark Poliakov Date: Sun, 28 Nov 2021 11:46:55 +0200 Subject: [PATCH] feature: login program --- Cargo.lock | 1 + Makefile | 4 +- fs/vfs/src/ioctx.rs | 22 +++++- fs/vfs/src/node.rs | 10 ++- kernel/src/fs/devfs.rs | 9 ++- kernel/src/fs/mod.rs | 12 +++ kernel/src/init.rs | 4 +- kernel/src/proc/io.rs | 53 ++++++++++++- kernel/src/proc/process.rs | 4 + kernel/src/syscall/mod.rs | 73 +++++++++++++++++- libsys/src/abi.rs | 8 ++ libsys/src/calls.rs | 68 ++++++++++++++++- libsys/src/stat.rs | 67 ++++++++++++++++ user/Cargo.toml | 5 ++ user/src/init/main.rs | 13 +++- user/src/login/main.rs | 151 +++++++++++++++++++++++++++++++++++++ user/src/shell/main.rs | 41 ++++++++-- 17 files changed, 522 insertions(+), 23 deletions(-) create mode 100644 user/src/login/main.rs diff --git a/Cargo.lock b/Cargo.lock index ef237f1..486e6d3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -263,6 +263,7 @@ name = "user" version = "0.1.0" dependencies = [ "lazy_static", + "libsys", "libusr", ] diff --git a/Makefile b/Makefile index b73183a..ba8bb46 100644 --- a/Makefile +++ b/Makefile @@ -92,11 +92,13 @@ initrd: --target=../etc/$(ARCH)-osdev5.json \ -Z build-std=core,alloc,compiler_builtins \ $(CARGO_COMMON_OPTS) - mkdir -p $(O)/rootfs/bin + mkdir -p $(O)/rootfs/bin $(O)/rootfs/sbin $(O)/rootfs/dev + touch $(O)/rootfs/dev/.do_no_remove cp target/$(ARCH)-osdev5/$(PROFILE)/init $(O)/rootfs/init cp target/$(ARCH)-osdev5/$(PROFILE)/shell $(O)/rootfs/bin cp target/$(ARCH)-osdev5/$(PROFILE)/fuzzy $(O)/rootfs/bin cp target/$(ARCH)-osdev5/$(PROFILE)/ls $(O)/rootfs/bin + cp target/$(ARCH)-osdev5/$(PROFILE)/login $(O)/rootfs/sbin cd $(O)/rootfs && tar cf ../initrd.img `find -type f -printf "%P\n"` ifeq ($(MACH),orangepi3) $(MKIMAGE) \ diff --git a/fs/vfs/src/ioctx.rs b/fs/vfs/src/ioctx.rs index 3dc3e25..c4d498d 100644 --- a/fs/vfs/src/ioctx.rs +++ b/fs/vfs/src/ioctx.rs @@ -1,8 +1,8 @@ use crate::{FileRef, VnodeKind, VnodeRef}; use libsys::{ error::Errno, - stat::{OpenFlags, FileMode}, path::{path_component_left, path_component_right}, + stat::{FileMode, GroupId, OpenFlags, UserId}, }; /// I/O context structure @@ -10,13 +10,17 @@ use libsys::{ pub struct Ioctx { root: VnodeRef, cwd: VnodeRef, + pub uid: UserId, + pub gid: GroupId, } impl Ioctx { /// Creates a new I/O context with given root node - pub fn new(root: VnodeRef) -> Self { + pub fn new(root: VnodeRef, uid: UserId, gid: GroupId) -> Self { Self { cwd: root.clone(), + uid, + gid, root, } } @@ -41,6 +45,11 @@ impl Ioctx { } } + while let Some(target) = at.target() { + assert!(at.kind() == VnodeKind::Directory); + at = target; + } + if element.is_empty() && rest.is_empty() { return Ok(at); } @@ -113,6 +122,15 @@ impl Ioctx { node.open(opts) } + + pub fn chdir(&mut self, path: &str) -> Result<(), Errno> { + let node = self.find(None, path, true)?; + if !node.is_directory() { + return Err(Errno::NotADirectory); + } + self.cwd = node; + Ok(()) + } } #[cfg(test)] diff --git a/fs/vfs/src/node.rs b/fs/vfs/src/node.rs index 62ae21b..816add0 100644 --- a/fs/vfs/src/node.rs +++ b/fs/vfs/src/node.rs @@ -105,6 +105,7 @@ impl Vnode { pub const SEEKABLE: u32 = 1 << 0; pub const CACHE_READDIR: u32 = 1 << 1; + pub const CACHE_STAT: u32 = 1 << 2; /// Constructs a new [Vnode], wrapping it in [Rc]. The resulting node /// then needs to have [Vnode::set_data()] called on it to be usable. @@ -452,7 +453,14 @@ impl Vnode { /// Reports file status pub fn stat(self: &VnodeRef) -> Result { - if let Some(ref mut data) = *self.data() { + if self.flags & Self::CACHE_STAT != 0 { + let props = self.props(); + Ok(Stat { + blksize: 0, + size: 0, + mode: props.mode + }) + } else if let Some(ref mut data) = *self.data() { data.stat(self.clone()) } else { Err(Errno::NotImplemented) diff --git a/kernel/src/fs/devfs.rs b/kernel/src/fs/devfs.rs index 6a65421..3cb59c7 100644 --- a/kernel/src/fs/devfs.rs +++ b/kernel/src/fs/devfs.rs @@ -2,7 +2,7 @@ use crate::util::InitOnce; use alloc::boxed::Box; use core::sync::atomic::{AtomicUsize, Ordering}; -use libsys::error::Errno; +use libsys::{stat::FileMode, error::Errno}; use vfs::{CharDevice, CharDeviceWrapper, Vnode, VnodeKind, VnodeRef}; /// Possible character device kinds @@ -16,7 +16,9 @@ static DEVFS_ROOT: InitOnce = InitOnce::new(); /// Initializes devfs pub fn init() { - DEVFS_ROOT.init(Vnode::new("", VnodeKind::Directory, 0)); + let node = Vnode::new("", VnodeKind::Directory, Vnode::CACHE_READDIR | Vnode::CACHE_STAT); + node.props_mut().mode = FileMode::default_dir(); + DEVFS_ROOT.init(node); } /// Returns devfs root node reference @@ -27,7 +29,8 @@ pub fn root() -> &'static VnodeRef { fn _add_char_device(dev: &'static dyn CharDevice, name: &str) -> Result<(), Errno> { infoln!("Add char device: {}", name); - let node = Vnode::new(name, VnodeKind::Char, 0); + let node = Vnode::new(name, VnodeKind::Char, Vnode::CACHE_STAT); + node.props_mut().mode = FileMode::from_bits(0o600).unwrap() | FileMode::S_IFCHR; node.set_data(Box::new(CharDeviceWrapper::new(dev))); DEVFS_ROOT.get().attach(node); diff --git a/kernel/src/fs/mod.rs b/kernel/src/fs/mod.rs index 2773a42..299a08d 100644 --- a/kernel/src/fs/mod.rs +++ b/kernel/src/fs/mod.rs @@ -3,6 +3,8 @@ use crate::mem::{ self, phys::{self, PageUsage}, }; +use libsys::{error::Errno, stat::MountOptions}; +use vfs::VnodeRef; use memfs::BlockAllocator; pub mod devfs; @@ -25,3 +27,13 @@ unsafe impl BlockAllocator for MemfsBlockAlloc { phys::free_page(phys).unwrap(); } } + +pub fn create_filesystem(options: &MountOptions) -> Result { + let fs_name = options.fs.unwrap(); + + if fs_name == "devfs" { + Ok(devfs::root().clone()) + } else { + todo!(); + } +} diff --git a/kernel/src/init.rs b/kernel/src/init.rs index 2c876c0..db7789f 100644 --- a/kernel/src/init.rs +++ b/kernel/src/init.rs @@ -4,7 +4,7 @@ use crate::config::{ConfigKey, CONFIG}; use crate::fs::{devfs, MemfsBlockAlloc}; use crate::mem; use crate::proc::{elf, Process}; -use libsys::stat::{FileDescriptor, OpenFlags}; +use libsys::stat::{FileDescriptor, OpenFlags, UserId, GroupId}; use memfs::Ramfs; use vfs::{Filesystem, Ioctx}; @@ -29,7 +29,7 @@ pub extern "C" fn init_fn(_arg: usize) -> ! { unsafe { Ramfs::open(initrd_start as *mut u8, initrd_size, MemfsBlockAlloc {}).unwrap() }; let root = fs.root().unwrap(); - let ioctx = Ioctx::new(root); + let ioctx = Ioctx::new(root, UserId::root(), GroupId::root()); let node = ioctx.find(None, "/init", true).unwrap(); let file = node.open(OpenFlags::O_RDONLY | OpenFlags::O_EXEC).unwrap(); diff --git a/kernel/src/proc/io.rs b/kernel/src/proc/io.rs index c42f60f..5e4ee77 100644 --- a/kernel/src/proc/io.rs +++ b/kernel/src/proc/io.rs @@ -1,6 +1,6 @@ //! Process file descriptors and I/O context use alloc::collections::BTreeMap; -use libsys::{error::Errno, stat::FileDescriptor}; +use libsys::{error::Errno, stat::{FileDescriptor, UserId, GroupId}}; use vfs::{FileRef, Ioctx, VnodeRef, VnodeKind}; /// Process I/O context. Contains file tables, root/cwd info etc. @@ -31,6 +31,57 @@ impl ProcessIo { self.ctty.clone() } + #[inline(always)] + pub fn uid(&self) -> UserId { + self.ioctx.as_ref().unwrap().uid + } + + #[inline(always)] + pub fn gid(&self) -> GroupId { + self.ioctx.as_ref().unwrap().gid + } + + #[inline(always)] + pub fn set_uid(&mut self, uid: UserId) -> Result<(), Errno> { + let old_uid = self.uid(); + if old_uid == uid { + Ok(()) + } else if !old_uid.is_root() { + Err(Errno::PermissionDenied) + } else { + self.ioctx.as_mut().unwrap().uid = uid; + Ok(()) + } + } + + #[inline(always)] + pub fn set_gid(&mut self, gid: GroupId) -> Result<(), Errno> { + let old_gid = self.gid(); + if old_gid == gid { + Ok(()) + } else if !old_gid.is_root() { + Err(Errno::PermissionDenied) + } else { + self.ioctx.as_mut().unwrap().gid = gid; + Ok(()) + } + } + + pub fn duplicate_file(&mut self, src: FileDescriptor, dst: Option) -> Result { + let file_ref = self.file(src)?; + if let Some(dst) = dst { + let idx = u32::from(dst); + if self.files.get(&idx).is_some() { + return Err(Errno::AlreadyExists); + } + + self.files.insert(idx, file_ref); + Ok(dst) + } else { + self.place_file(file_ref) + } + } + /// Returns [File] struct referred to by file descriptor `idx` pub fn file(&mut self, fd: FileDescriptor) -> Result { self.files.get(&u32::from(fd)).cloned().ok_or(Errno::InvalidFile) diff --git a/kernel/src/proc/process.rs b/kernel/src/proc/process.rs index 154d20d..e437b4e 100644 --- a/kernel/src/proc/process.rs +++ b/kernel/src/proc/process.rs @@ -82,6 +82,10 @@ impl Process { self.inner.lock().pgid = pgid; } + pub fn set_sid(&self, sid: Pid) { + self.inner.lock().sid = sid; + } + #[inline] pub fn current() -> ProcessRef { Thread::current().owner().unwrap() diff --git a/kernel/src/syscall/mod.rs b/kernel/src/syscall/mod.rs index 762d909..7da0597 100644 --- a/kernel/src/syscall/mod.rs +++ b/kernel/src/syscall/mod.rs @@ -3,6 +3,7 @@ use crate::arch::{machine, platform::exception::ExceptionFrame}; use crate::debug::Level; use crate::dev::timer::TimestampSource; +use crate::fs::create_filesystem; use crate::proc::{self, elf, wait, Process, ProcessIo, Thread}; use core::mem::size_of; use core::ops::DerefMut; @@ -15,7 +16,8 @@ use libsys::{ proc::{ExitCode, Pid}, signal::{Signal, SignalDestination}, stat::{ - AccessMode, DirectoryEntry, FdSet, FileDescriptor, FileMode, OpenFlags, Stat, AT_EMPTY_PATH, + AccessMode, DirectoryEntry, FdSet, FileDescriptor, FileMode, GroupId, MountOptions, + OpenFlags, Stat, UserId, AT_EMPTY_PATH, }, traits::{Read, Write}, }; @@ -107,7 +109,8 @@ pub fn syscall(num: SystemCall, args: &[usize]) -> Result { let proc = Process::current(); let mut io = proc.io.lock(); - let stat = find_at_node(&mut io, at_fd, filename, flags & AT_EMPTY_PATH != 0)?.stat()?; + let stat = + find_at_node(&mut io, at_fd, filename, flags & AT_EMPTY_PATH != 0)?.stat()?; *buf = stat; Ok(0) } @@ -153,6 +156,48 @@ pub fn syscall(num: SystemCall, args: &[usize]) -> Result { io.file(fd)?.borrow_mut().readdir(buf) } + SystemCall::GetUserId => { + let proc = Process::current(); + let uid = proc.io.lock().uid(); + Ok(u32::from(uid) as usize) + } + SystemCall::GetGroupId => { + let proc = Process::current(); + let gid = proc.io.lock().gid(); + Ok(u32::from(gid) as usize) + } + SystemCall::DuplicateFd => { + let src = FileDescriptor::from(args[0] as u32); + let dst = FileDescriptor::from_i32(args[1] as i32)?; + + let proc = Process::current(); + let mut io = proc.io.lock(); + + let res = io.duplicate_file(src, dst)?; + + Ok(u32::from(res) as usize) + } + SystemCall::SetUserId => { + let uid = UserId::from(args[0] as u32); + let proc = Process::current(); + proc.io.lock().set_uid(uid)?; + Ok(0) + } + SystemCall::SetGroupId => { + let gid = GroupId::from(args[0] as u32); + let proc = Process::current(); + proc.io.lock().set_gid(gid)?; + Ok(0) + } + SystemCall::SetCurrentDirectory => { + let path = arg::str_ref(args[0], args[1])?; + let proc = Process::current(); + proc.io.lock().ioctx().chdir(path)?; + Ok(0) + } + SystemCall::GetCurrentDirectory => { + todo!() + } // Process SystemCall::Clone => { @@ -294,7 +339,9 @@ pub fn syscall(num: SystemCall, args: &[usize]) -> Result { todo!(); } - todo!(); + let id = proc.id(); + proc.set_sid(id); + Ok(id.value() as usize) } SystemCall::SetPgid => { let pid = args[0] as u32; @@ -317,10 +364,28 @@ pub fn syscall(num: SystemCall, args: &[usize]) -> Result { let time = machine::local_timer().timestamp()?; Ok(time.as_nanos() as usize) } + SystemCall::Mount => { + let target = arg::str_ref(args[0], args[1])?; + let options = arg::struct_ref::(args[2])?; + + let proc = Process::current(); + let mut io = proc.io.lock(); + + debugln!("mount(target={:?}, options={:#x?})", target, options); + + let target_node = io.ioctx().find(None, target, true)?; + let root = create_filesystem(options)?; + + target_node.mount(root)?; + + Ok(0) + } // Debugging SystemCall::DebugTrace => { - let level = TraceLevel::from_repr(args[0]).map(Level::from).ok_or(Errno::InvalidArgument)?; + let level = TraceLevel::from_repr(args[0]) + .map(Level::from) + .ok_or(Errno::InvalidArgument)?; let buf = arg::str_ref(args[1], args[2])?; let thread = Thread::current(); let proc = thread.owner().unwrap(); diff --git a/libsys/src/abi.rs b/libsys/src/abi.rs index 7fb3eb4..6d5c5dc 100644 --- a/libsys/src/abi.rs +++ b/libsys/src/abi.rs @@ -13,6 +13,13 @@ pub enum SystemCall { Select = 7, Access = 8, ReadDirectory = 9, + GetUserId = 10, + GetGroupId = 11, + DuplicateFd = 12, + SetUserId = 13, + SetGroupId = 14, + SetCurrentDirectory = 15, + GetCurrentDirectory = 16, // Process manipulation Fork = 32, Clone = 33, @@ -34,6 +41,7 @@ pub enum SystemCall { SetPgid = 49, // System GetCpuTime = 64, + Mount = 65, // Debugging DebugTrace = 128 } diff --git a/libsys/src/calls.rs b/libsys/src/calls.rs index d78abb6..d93b0b2 100644 --- a/libsys/src/calls.rs +++ b/libsys/src/calls.rs @@ -1,11 +1,14 @@ use crate::abi::SystemCall; use crate::{ - error::Errno, debug::TraceLevel, + error::Errno, ioctl::IoctlCmd, proc::{ExitCode, Pid}, signal::{Signal, SignalDestination}, - stat::{AccessMode, DirectoryEntry, FdSet, FileDescriptor, FileMode, OpenFlags, Stat}, + stat::{ + AccessMode, DirectoryEntry, FdSet, FileDescriptor, FileMode, GroupId, MountOptions, + OpenFlags, Stat, UserId, + }, }; use core::time::Duration; @@ -387,3 +390,64 @@ pub fn sys_readdir(fd: FileDescriptor, buf: &mut [DirectoryEntry]) -> Result UserId { + UserId::from(unsafe { syscall!(SystemCall::GetUserId) as u32 }) +} + +#[inline(always)] +pub fn sys_getgid() -> GroupId { + GroupId::from(unsafe { syscall!(SystemCall::GetGroupId) as u32 }) +} + +#[inline(always)] +pub fn sys_setsid() -> Result { + Errno::from_syscall(unsafe { syscall!(SystemCall::SetSid) }) + .map(|e| unsafe { Pid::from_raw(e as u32) }) +} + +#[inline(always)] +pub fn sys_mount(target: &str, options: &MountOptions) -> Result<(), Errno> { + Errno::from_syscall_unit(unsafe { + syscall!( + SystemCall::Mount, + argp!(target.as_ptr()), + argn!(target.len()), + argp!(options as *const _) + ) + }) +} + +#[inline(always)] +pub fn sys_dup(src: FileDescriptor, dst: Option) -> Result { + Errno::from_syscall(unsafe { + syscall!( + SystemCall::DuplicateFd, + argn!(u32::from(src)), + argn!(FileDescriptor::into_i32(dst)) + ) + }) + .map(|e| FileDescriptor::from(e as u32)) +} + +#[inline(always)] +pub fn sys_setuid(uid: UserId) -> Result<(), Errno> { + Errno::from_syscall_unit(unsafe { syscall!(SystemCall::SetUserId, u32::from(uid) as usize) }) +} + +#[inline(always)] +pub fn sys_setgid(gid: GroupId) -> Result<(), Errno> { + Errno::from_syscall_unit(unsafe { syscall!(SystemCall::SetGroupId, u32::from(gid) as usize) }) +} + +#[inline(always)] +pub fn sys_chdir(path: &str) -> Result<(), Errno> { + Errno::from_syscall_unit(unsafe { + syscall!( + SystemCall::SetCurrentDirectory, + argp!(path.as_ptr()), + argn!(path.len()) + ) + }) +} diff --git a/libsys/src/stat.rs b/libsys/src/stat.rs index 7ba0ecb..188f46b 100644 --- a/libsys/src/stat.rs +++ b/libsys/src/stat.rs @@ -1,3 +1,4 @@ +// TODO split up this file use crate::error::Errno; use core::fmt; @@ -15,6 +16,7 @@ bitflags! { const O_EXEC = 1 << 5; const O_CLOEXEC = 1 << 6; const O_DIRECTORY = 1 << 7; + const O_CTTY = 1 << 8; } } @@ -23,6 +25,7 @@ bitflags! { const FILE_TYPE = 0xF << 12; const S_IFREG = 0x8 << 12; const S_IFDIR = 0x4 << 12; + const S_IFCHR = 0x2 << 12; const USER_READ = 1 << 8; const USER_WRITE = 1 << 7; @@ -45,6 +48,69 @@ bitflags! { } } +#[derive(Clone, Debug)] +pub struct MountOptions<'a> { + pub device: Option<&'a str>, + pub fs: Option<&'a str>, + // TODO flags etc. +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] +#[repr(transparent)] +pub struct UserId(u32); + +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] +#[repr(transparent)] +pub struct GroupId(u32); + +impl UserId { + pub const fn root() -> Self { + Self(0) + } + + pub const fn is_root(self) -> bool { + self.0 == 0 + } +} + +impl From for UserId { + #[inline(always)] + fn from(v: u32) -> Self { + Self(v) + } +} + +impl From for u32 { + #[inline(always)] + fn from(v: UserId) -> u32 { + v.0 + } +} + +impl GroupId { + pub const fn root() -> Self { + Self(0) + } + + pub const fn is_root(self) -> bool { + self.0 == 0 + } +} + +impl From for GroupId { + #[inline(always)] + fn from(v: u32) -> Self { + Self(v) + } +} + +impl From for u32 { + #[inline(always)] + fn from(v: GroupId) -> u32 { + v.0 + } +} + #[derive(Clone, Default)] pub struct FdSet { bits: [u64; 2], @@ -191,6 +257,7 @@ impl fmt::Display for FileMode { "{}{}{}{}{}{}{}{}{}{}", // File type match *self & Self::FILE_TYPE { + Self::S_IFCHR => 'c', Self::S_IFDIR => 'd', Self::S_IFREG => '-', _ => '?' diff --git a/user/Cargo.toml b/user/Cargo.toml index 91af3a7..cb483a7 100644 --- a/user/Cargo.toml +++ b/user/Cargo.toml @@ -21,6 +21,11 @@ path = "src/fuzzy/main.rs" name = "ls" path = "src/ls/main.rs" +[[bin]] +name = "login" +path = "src/login/main.rs" + [dependencies] libusr = { path = "../libusr" } +libsys = { path = "../libsys" } lazy_static = { version = "*", features = ["spin_no_std"] } diff --git a/user/src/init/main.rs b/user/src/init/main.rs index 2161d0f..f75daca 100644 --- a/user/src/init/main.rs +++ b/user/src/init/main.rs @@ -5,8 +5,19 @@ #[macro_use] extern crate libusr; +use libusr::sys::{stat::MountOptions, sys_execve, sys_fork, sys_mount, sys_waitpid}; + #[no_mangle] fn main() -> i32 { + sys_mount( + "/dev", + &MountOptions { + device: None, + fs: Some("devfs"), + }, + ) + .expect("Failed to mount devfs"); + let pid = unsafe { libusr::sys::sys_fork().unwrap() }; if let Some(pid) = pid { @@ -20,7 +31,7 @@ fn main() -> i32 { } } } else { - libusr::sys::sys_execve("/bin/shell", &["/bin/shell"]).unwrap(); + libusr::sys::sys_execve("/sbin/login", &["/sbin/login", "/dev/ttyS0"]).unwrap(); loop {} } } diff --git a/user/src/login/main.rs b/user/src/login/main.rs new file mode 100644 index 0000000..63d172e --- /dev/null +++ b/user/src/login/main.rs @@ -0,0 +1,151 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate libusr; +#[macro_use] +extern crate alloc; + +use libsys::{ + calls::{ + sys_close, sys_dup, sys_fork, sys_getgid, sys_getpgid, sys_getuid, sys_ioctl, sys_openat, + sys_read, sys_setgid, sys_setpgid, sys_setsid, sys_setuid, sys_waitpid, sys_execve + }, + error::Errno, + ioctl::IoctlCmd, + proc::Pid, + stat::{FileDescriptor, FileMode, GroupId, OpenFlags, UserId}, + termios::{Termios, TermiosLflag}, +}; +use libusr::{env, io}; + +struct HiddenInput { + fd: FileDescriptor, + termios: Termios, +} + +impl HiddenInput { + fn open(fd: FileDescriptor) -> Result { + use core::mem::{size_of, MaybeUninit}; + let mut termios: MaybeUninit = MaybeUninit::uninit(); + sys_ioctl( + fd, + IoctlCmd::TtyGetAttributes, + termios.as_mut_ptr() as usize, + size_of::(), + )?; + let termios = unsafe { termios.assume_init() }; + + let mut new_termios = termios.clone(); + new_termios.lflag &= !(TermiosLflag::ECHO | TermiosLflag::ECHOK | TermiosLflag::ECHOE); + sys_ioctl( + fd, + IoctlCmd::TtySetAttributes, + &new_termios as *const _ as usize, + size_of::(), + )?; + + Ok(Self { fd, termios }) + } + + fn readline<'a>(&mut self, buf: &'a mut [u8]) -> Result<&'a str, Errno> { + readline(self.fd, buf) + } +} + +impl Drop for HiddenInput { + fn drop(&mut self) { + use core::mem::size_of; + sys_ioctl( + self.fd, + IoctlCmd::TtySetAttributes, + &self.termios as *const _ as usize, + size_of::(), + ) + .ok(); + } +} + +fn readline(fd: FileDescriptor, buf: &mut [u8]) -> Result<&str, Errno> { + let len = sys_read(fd, buf)?; + + if len == 0 { + Ok("") + } else { + Ok(core::str::from_utf8(&buf[..len - 1]).unwrap()) + } +} + +fn login_as(uid: UserId, gid: GroupId, shell: &str) -> Result<(), Errno> { + if let Some(pid) = unsafe { sys_fork() }? { + let mut status = 0; + sys_waitpid(pid, &mut status).ok(); + let pgid = sys_getpgid(unsafe { Pid::from_raw(0) }).unwrap(); + io::tcsetpgrp(FileDescriptor::STDIN, pgid).unwrap(); + Ok(()) + } else { + sys_setuid(uid).expect("setuid failed"); + sys_setgid(gid).expect("setgid failed"); + let pgid = sys_setpgid(unsafe { Pid::from_raw(0) }, unsafe { Pid::from_raw(0) }).unwrap(); + io::tcsetpgrp(FileDescriptor::STDIN, pgid).unwrap(); + sys_execve(shell, &[shell]).expect("execve() failed"); + panic!(); + } +} + +// TODO baud rate and misc port settings +#[no_mangle] +fn main() -> i32 { + if !sys_getuid().is_root() { + panic!("This program must be run as root"); + } + + let args = env::args(); + if args.len() != 2 { + panic!("Usage: {} TTY", args[0]); + } + + sys_setsid().expect("setsid() failed"); + + // Close controlling terminal + // NOTE this will invalidate rust-side Stdin, Stdout, Stderr + // until replacement is re-opened using the specified TTY + sys_close(FileDescriptor::STDERR).unwrap(); + sys_close(FileDescriptor::STDOUT).unwrap(); + sys_close(FileDescriptor::STDIN).unwrap(); + + sys_openat( + None, + args[1], + FileMode::default_reg(), + OpenFlags::O_RDONLY | OpenFlags::O_CTTY, + ) + .expect("Failed to open stdin"); + sys_openat( + None, + args[1], + FileMode::default_reg(), + OpenFlags::O_WRONLY | OpenFlags::O_CTTY, + ) + .expect("Failed to open stdout"); + sys_dup(FileDescriptor::STDOUT, Some(FileDescriptor::STDERR)).expect("Failed to open stderr"); + + let mut user_buf = [0; 128]; + let mut password_buf = [0; 128]; + loop { + print!("login: "); + let username = readline(FileDescriptor::STDIN, &mut user_buf).expect("Login read failed"); + print!("password: "); + let password = { + let mut input = HiddenInput::open(FileDescriptor::STDIN).unwrap(); + input.readline(&mut password_buf) + } + .expect("Password read failed"); + + if username == "root" && password == "toor" { + login_as(UserId::from(0), GroupId::from(0), "/bin/shell"); + } + } + + 0 +} diff --git a/user/src/shell/main.rs b/user/src/shell/main.rs index 5ecfa12..18d3e15 100644 --- a/user/src/shell/main.rs +++ b/user/src/shell/main.rs @@ -9,11 +9,34 @@ use alloc::{borrow::ToOwned, vec::Vec}; use libusr::io::{self, Read}; use libusr::signal::{self, SignalHandler}; use libusr::sys::{ - proc::Pid, sys_execve, sys_setpgid, sys_exit, sys_fork, sys_getpgid, sys_waitpid, Errno, ExitCode, - sys_faccessat, AccessMode, - FileDescriptor, Signal, + proc::Pid, sys_chdir, sys_execve, sys_exit, sys_faccessat, sys_fork, sys_getpgid, sys_setpgid, + sys_waitpid, AccessMode, Errno, ExitCode, FileDescriptor, Signal, }; +struct Builtin { + func: fn(&[&str]) -> ExitCode, + name: &'static str, +} + +fn cmd_cd(args: &[&str]) -> ExitCode { + if args.len() != 2 { + eprintln!("Usage: cd DIR"); + ExitCode::from(-1) + } else { + if let Err(err) = sys_chdir(args[1]) { + eprintln!("{}: {:?}", args[1], err); + ExitCode::from(-1) + } else { + ExitCode::from(0) + } + } +} + +static BUILTINS: [Builtin; 1] = [Builtin { + name: "cd", + func: cmd_cd, +}]; + fn readline<'a, F: Read>(f: &mut F, bytes: &'a mut [u8]) -> Result, io::Error> { let size = f.read(bytes)?; Ok(if size == 0 { @@ -32,6 +55,12 @@ fn execute(line: &str) -> Result { let args: Vec<&str> = line.split(' ').collect(); let cmd = args[0]; + for item in BUILTINS.iter() { + if item.name == cmd { + return Ok((item.func)(&args)); + } + } + let filename = "/bin/".to_owned() + cmd; sys_faccessat(None, &filename, AccessMode::X_OK, 0)?; @@ -73,12 +102,12 @@ fn main() -> i32 { if let Err(e) = execute(line) { eprintln!("{}: {:?}", line.split(' ').next().unwrap(), e); } - }, + } Err(_) => { println!("Interrupt!"); continue; - }, - _ => panic!() + } + _ => panic!(), } } 0