diff --git a/kernel/driver/fs/ext2/src/data.rs b/kernel/driver/fs/ext2/src/data.rs index d4e39128..6edd9be1 100644 --- a/kernel/driver/fs/ext2/src/data.rs +++ b/kernel/driver/fs/ext2/src/data.rs @@ -5,6 +5,7 @@ use libk::vfs::Metadata; use yggdrasil_abi::{ bitflags, io::{FileMode, FileType, GroupId, UserId}, + time::SystemTime, }; use crate::Ext2Fs; @@ -253,8 +254,9 @@ impl Inode { inode: Some(ino), block_count: self.blocks(fs) as _, block_size: fs.block_size as _, - ctime: self.ctime as _, - mtime: self.mtime as _, + ctime: SystemTime::from_seconds(self.ctime as u64), + mtime: SystemTime::from_seconds(self.mtime as u64), + atime: SystemTime::from_seconds(self.atime as u64), } } } diff --git a/kernel/driver/fs/ext2/src/inode/cache.rs b/kernel/driver/fs/ext2/src/inode/cache.rs index 6a048a32..90ba736d 100644 --- a/kernel/driver/fs/ext2/src/inode/cache.rs +++ b/kernel/driver/fs/ext2/src/inode/cache.rs @@ -176,9 +176,9 @@ impl InodeAccess { .map_err(|_| Error::InvalidArgument)?; self.map_mut(|inode| { - inode.mtime = metadata.mtime as _; - inode.atime = metadata.mtime as _; - inode.ctime = metadata.ctime as _; + inode.mtime = metadata.mtime.seconds() as u32; + inode.atime = metadata.mtime.seconds() as u32; + inode.ctime = metadata.ctime.seconds() as u32; inode.mode.update_permissions(metadata.mode); inode.uid = uid; diff --git a/kernel/driver/fs/fat32/src/directory.rs b/kernel/driver/fs/fat32/src/directory.rs index 55cf475f..cd255584 100644 --- a/kernel/driver/fs/fat32/src/directory.rs +++ b/kernel/driver/fs/fat32/src/directory.rs @@ -15,6 +15,7 @@ use libk_util::{ }; use yggdrasil_abi::{ io::{DirectoryEntry, FileMode, GroupId, UserId}, + time::SystemTime, util::FixedString, }; @@ -254,8 +255,10 @@ impl DirectoryNode { FileMode::default_file() }, inode: Some(cluster.0), - ctime: 0, - mtime: 0, + // TODO + ctime: SystemTime::ZERO, + mtime: SystemTime::ZERO, + atime: SystemTime::ZERO, block_count: (size.div_ceil(self.fs.layout.bytes_per_sector as u32)) as u64, block_size: self.fs.layout.bytes_per_sector as u64, }; diff --git a/kernel/driver/fs/fat32/src/lib.rs b/kernel/driver/fs/fat32/src/lib.rs index 506657d2..fb60a060 100644 --- a/kernel/driver/fs/fat32/src/lib.rs +++ b/kernel/driver/fs/fat32/src/lib.rs @@ -10,7 +10,10 @@ use libk::{ vfs::{Filesystem, FilesystemMountOption, Metadata, Node, NodeFlags, NodeRef}, }; use libk_util::get_le_u32; -use yggdrasil_abi::io::{FileMode, GroupId, UserId}; +use yggdrasil_abi::{ + io::{FileMode, GroupId, UserId}, + time::SystemTime, +}; extern crate alloc; @@ -64,8 +67,10 @@ impl Fat32Fs { uid: UserId::root(), gid: GroupId::root(), mode: FileMode::default_dir(), - ctime: 0, - mtime: 0, + // TODO + ctime: SystemTime::ZERO, + mtime: SystemTime::ZERO, + atime: SystemTime::ZERO, inode: Some(fs.layout.root_directory_cluster.0), block_size: fs.layout.bytes_per_sector as u64, block_count: 0, diff --git a/kernel/driver/fs/memfs/src/file.rs b/kernel/driver/fs/memfs/src/file.rs index 2e9798f7..d80f3f22 100644 --- a/kernel/driver/fs/memfs/src/file.rs +++ b/kernel/driver/fs/memfs/src/file.rs @@ -2,13 +2,14 @@ use alloc::sync::Arc; use core::any::Any; use libk::vfs::{CommonImpl, InstanceData, Metadata, Node, NodeFlags, NodeRef, RegularImpl}; -use libk_util::sync::IrqSafeSpinlock; +use libk_util::sync::{spin_rwlock::IrqSafeRwLock, IrqSafeSpinlock}; use yggdrasil_abi::{error::Error, io::OpenOptions}; use crate::{block::BlockAllocator, bvec::BVec, MemoryFilesystem}; pub(crate) struct FileNode { pub(crate) data: IrqSafeSpinlock>, + pub(crate) metadata: IrqSafeRwLock, } impl FileNode { @@ -16,9 +17,10 @@ impl FileNode { Node::regular( Self { data: IrqSafeSpinlock::new(BVec::new()), + metadata: IrqSafeRwLock::new(metadata), }, - NodeFlags::IN_MEMORY_PROPS, - Some(metadata), + NodeFlags::empty(), + None, Some(fs), ) } @@ -32,6 +34,15 @@ impl CommonImpl for FileNode { fn size(&self, _node: &NodeRef) -> Result { Ok(self.data.lock().size() as u64) } + + fn metadata(&self, _node: &NodeRef) -> Result { + Ok(*self.metadata.read()) + } + + fn set_metadata(&self, _node: &NodeRef, metadata: &Metadata) -> Result<(), Error> { + *self.metadata.write() = *metadata; + Ok(()) + } } impl RegularImpl for FileNode { @@ -55,6 +66,7 @@ impl RegularImpl for FileNode { pos: u64, buf: &mut [u8], ) -> Result { + self.metadata.write().set_atime_now(); self.data.lock().read(pos, buf) } @@ -65,10 +77,12 @@ impl RegularImpl for FileNode { pos: u64, buf: &[u8], ) -> Result { + self.metadata.write().set_mtime_now(); self.data.lock().write(pos, buf) } fn truncate(&self, _node: &NodeRef, new_size: u64) -> Result<(), Error> { + self.metadata.write().set_mtime_now(); self.data.lock().truncate(new_size) } diff --git a/kernel/libk/src/task/process.rs b/kernel/libk/src/task/process.rs index 6af0ce4b..7538fe2c 100644 --- a/kernel/libk/src/task/process.rs +++ b/kernel/libk/src/task/process.rs @@ -20,7 +20,7 @@ use libk_mm::{phys::GlobalPhysicalAllocator, process::ProcessAddressSpace}; use libk_util::{ event::{BoolEvent, OneTimeEvent}, sync::{ - spin_rwlock::{IrqSafeRwLock, IrqSafeRwLockWriteGuard}, + spin_rwlock::{IrqSafeRwLock, IrqSafeRwLockReadGuard, IrqSafeRwLockWriteGuard}, IrqSafeSpinlock, }, }; @@ -443,7 +443,7 @@ impl Process { if let Some(status) = child.get_exit_status() { log::debug!("Child {id} exited with status {status:?} (didn't block)"); - // TODO remove child + self.inner.write().remove_child(id); return Ok(status); } @@ -455,7 +455,7 @@ impl Process { loop { if let Some(status) = child.get_exit_status() { log::debug!("Child {id} exited with status {status:?}", ); - // TODO remove child + self.inner.write().remove_child(id); break status; } @@ -464,13 +464,23 @@ impl Process { } } + fn poll_any_child_exit(&self) -> Option<(Arc, IrqSafeRwLockWriteGuard)> { + let read = self.inner.read(); + if let Some(child) = read.any_exited_child() { + let write = IrqSafeRwLockReadGuard::upgrade(read); + Some((child, write)) + } else { + None + } + } + pub fn wait_for_any_child(&self, flags: WaitFlags) -> Result<(ProcessId, ExitCode), Error> { - if let Some(child) = self.inner.read().any_exited_child() { + if let Some((child, mut guard)) = self.poll_any_child_exit() { let id = child.id; // unwrap ok: ProcessInner tells the child already exited let status = child.get_exit_status().unwrap(); - log::debug!("Child {id} exited with status {status:?} (didn't block)"); - // TODO remove child + log::info!("Child {id} exited with status {status:?} (didn't block)"); + guard.remove_child(id); return Ok((id, status)); } @@ -480,12 +490,12 @@ impl Process { block! { loop { - if let Some(child) = self.inner.read().any_exited_child() { + if let Some((child, mut guard)) = self.poll_any_child_exit() { let id = child.id; // unwrap ok: ProcessInner tells the child already exited let status = child.get_exit_status().unwrap(); - log::debug!("Child {id} exited with status {status:?}", ); - // TODO remove child + log::info!("Child {id} exited with status {status:?}", ); + guard.remove_child(id); break (id, status); } @@ -705,6 +715,10 @@ impl ProcessInner { .find(|child| child.has_exited()) } + pub fn remove_child(&mut self, id: ProcessId) -> bool { + self.children.remove(&id).is_some() + } + pub fn remove_thread(&mut self, id: ThreadId) -> bool { let n = self.threads.len(); self.threads.retain(|t| t.id != id); diff --git a/kernel/libk/src/vfs/node/mod.rs b/kernel/libk/src/vfs/node/mod.rs index 0eceec03..7b1bf061 100644 --- a/kernel/libk/src/vfs/node/mod.rs +++ b/kernel/libk/src/vfs/node/mod.rs @@ -14,8 +14,9 @@ use traits::HardlinkImpl; use yggdrasil_abi::{ bitflags, error::Error, - io::{FileMode, FileType, GroupId, UserId}, + io::{FileMetadataUpdate, FileMetadataUpdateMode, FileMode, FileType, GroupId, UserId}, path::Path, + time::SystemTime, }; mod access; @@ -126,10 +127,12 @@ pub struct Metadata { pub block_size: u64, /// Size of the node (without metadata) in units of `block_size` pub block_count: u64, - /// Creation time (in seconds) - pub ctime: u64, - /// Modification time (in seconds) - pub mtime: u64, + /// Creation time + pub ctime: SystemTime, + /// Modification time + pub mtime: SystemTime, + /// Last access time + pub atime: SystemTime, } struct PropertyCache { @@ -147,14 +150,17 @@ pub struct Node { } impl Metadata { + pub const MODE_MASK: FileMode = FileMode::new(0o777); + pub fn now(uid: UserId, gid: GroupId, mode: FileMode, ino: u32) -> Metadata { - let now = real_time().seconds(); + let now = real_time(); Metadata { mode, uid, gid, ctime: now, mtime: now, + atime: now, inode: Some(ino), block_size: 4096, block_count: 0, @@ -164,6 +170,33 @@ impl Metadata { pub fn now_root(mode: FileMode, ino: u32) -> Metadata { Self::now(UserId::root(), GroupId::root(), mode, ino) } + + pub fn set_atime_now(&mut self) { + self.atime = real_time(); + } + + pub fn set_mtime_now(&mut self) { + let t = real_time(); + log::info!("set_mtime_now = {t:?}"); + self.mtime = t; + self.atime = t; + } + + pub fn update(&mut self, update: &FileMetadataUpdate) { + match update { + FileMetadataUpdate::Times(_) => todo!(), + FileMetadataUpdate::Permissions(mode) => match *mode { + FileMetadataUpdateMode::Set(mode) => { + self.mode &= !Self::MODE_MASK; + self.mode |= mode & Self::MODE_MASK; + } + FileMetadataUpdateMode::Modify { set, clear } => { + self.mode &= !(clear & Self::MODE_MASK); + self.mode |= set & Self::MODE_MASK; + } + }, + } + } } impl Node { diff --git a/kernel/libk/src/vfs/node/ops.rs b/kernel/libk/src/vfs/node/ops.rs index 238de115..208af5cf 100644 --- a/kernel/libk/src/vfs/node/ops.rs +++ b/kernel/libk/src/vfs/node/ops.rs @@ -3,10 +3,7 @@ use core::mem::MaybeUninit; use libk_util::ext::OptionExt; use yggdrasil_abi::{ error::Error, - io::{ - DirectoryEntry, FileMetadataUpdate, FileMetadataUpdateMode, FileMode, GroupId, OpenOptions, - UserId, - }, + io::{DirectoryEntry, FileMetadataUpdate, FileMode, GroupId, OpenOptions, UserId}, }; use crate::vfs::{ @@ -228,70 +225,74 @@ impl Node { return Err(Error::InvalidOperation); } - let mut cache = self.props.lock(); let common = self.data_as_common(); + if self.flags.contains(NodeFlags::IN_MEMORY_PROPS) { + let mut cache = self.props.lock(); - let metadata = cache - .metadata - .get_or_try_insert_with(|| common.metadata(self))?; + let metadata = cache + .metadata + .get_or_try_insert_with(|| common.metadata(self))?; - // let mut metadata = self.metadata()?; + if let Some(uid) = uid { + metadata.uid = uid; + } + if let Some(gid) = gid { + metadata.gid = gid; + } + if let Some(mode) = mode { + metadata.mode = mode; + } - if let Some(uid) = uid { - metadata.uid = uid; - } - if let Some(gid) = gid { - metadata.gid = gid; - } - if let Some(mode) = mode { - metadata.mode = mode; - } - - if !self.flags.contains(NodeFlags::IN_MEMORY_PROPS) { + Ok(()) + } else { // Update permissions in the real node - common.set_metadata(self, metadata)?; + let mut metadata = common.metadata(self)?; + if let Some(uid) = uid { + metadata.uid = uid; + } + if let Some(gid) = gid { + metadata.gid = gid; + } + if let Some(mode) = mode { + metadata.mode &= !Metadata::MODE_MASK; + metadata.mode |= mode & Metadata::MODE_MASK; + } + common.set_metadata(self, &metadata) } - - Ok(()) } /// Returns the "metadata" of the file: uid, gid, access mode pub fn metadata(self: &NodeRef) -> Result { - let mut cache = self.props.lock(); + if self.flags.contains(NodeFlags::IN_MEMORY_PROPS) { + let mut cache = self.props.lock(); - let metadata = cache - .metadata - .get_or_try_insert_with(|| self.data_as_common().metadata(self))?; + let metadata = cache + .metadata + .get_or_try_insert_with(|| self.data_as_common().metadata(self))?; - Ok(*metadata) + Ok(*metadata) + } else { + self.data_as_common().metadata(self) + } } pub fn update_metadata(self: &NodeRef, update: &FileMetadataUpdate) -> Result<(), Error> { - let FileMetadataUpdate::Permissions(mode) = update else { - return Err(Error::NotImplemented); - }; - - let mut cache = self.props.lock(); let common = self.data_as_common(); - let metadata = cache - .metadata - .get_or_try_insert_with(|| common.metadata(self))?; + if self.flags.contains(NodeFlags::IN_MEMORY_PROPS) { + let mut cache = self.props.lock(); - match *mode { - FileMetadataUpdateMode::Set(value) => metadata.mode |= value, - FileMetadataUpdateMode::Modify { set, clear } => { - metadata.mode &= !clear; - metadata.mode |= set; - } - } - - if !self.flags.contains(NodeFlags::IN_MEMORY_PROPS) { + let metadata = cache + .metadata + .get_or_try_insert_with(|| common.metadata(self))?; + metadata.update(update); + Ok(()) + } else { + let mut metadata = common.metadata(self)?; + metadata.update(update); // Update permissions in the real node - common.set_metadata(self, metadata)?; + common.set_metadata(self, &metadata) } - - Ok(()) } // TODO clarify directory size diff --git a/kernel/src/syscall/imp/sys_io.rs b/kernel/src/syscall/imp/sys_io.rs index 67b8de32..36983d16 100644 --- a/kernel/src/syscall/imp/sys_io.rs +++ b/kernel/src/syscall/imp/sys_io.rs @@ -273,7 +273,7 @@ pub(crate) fn get_metadata( let metadata = node.metadata()?; let size = node.size()?; - buffer.write(FileAttr { + let metadata = FileAttr { size, ty: node.ty(), mode: metadata.mode, @@ -284,9 +284,9 @@ pub(crate) fn get_metadata( block_size: metadata.block_size, ctime: metadata.ctime, mtime: metadata.mtime, - // TODO atime? atime: metadata.mtime, - }); + }; + buffer.write(metadata); Ok(()) }) diff --git a/lib/abi/def/io.abi b/lib/abi/def/io.abi index 20a5e058..baaeffe7 100644 --- a/lib/abi/def/io.abi +++ b/lib/abi/def/io.abi @@ -103,11 +103,11 @@ struct FileAttr { pub gid: GroupId, /// Creation time - pub ctime: u64, + pub ctime: SystemTime, /// Last modification time - pub mtime: u64, + pub mtime: SystemTime, /// Last access time - pub atime: u64, + pub atime: SystemTime, } /// Raw directory entry representation diff --git a/lib/abi/src/lib.rs b/lib/abi/src/lib.rs index 310aaada..7f42ab6b 100644 --- a/lib/abi/src/lib.rs +++ b/lib/abi/src/lib.rs @@ -26,7 +26,7 @@ pub use abi_serde; mod generated { #![allow(missing_docs)] - use crate::{arch::SavedFrame, process::SpawnOption, util::FixedString}; + use crate::{arch::SavedFrame, process::SpawnOption, time::SystemTime, util::FixedString}; include!(concat!(env!("OUT_DIR"), "/generated_types.rs")); } diff --git a/lib/runtime/src/process/signal.rs b/lib/runtime/src/process/signal.rs index 07f3ce4d..442d5931 100644 --- a/lib/runtime/src/process/signal.rs +++ b/lib/runtime/src/process/signal.rs @@ -60,17 +60,21 @@ fn terminate_by_signal(signal: Signal) -> ! { } /// Updates the handler for a particular signal. Returns the old handler used for that signal. -/// -/// # Safety -/// -/// Marked as unsafe due to being thread-unsafe. Will be lifted once I port RwLock into the runtime -/// crate. -pub unsafe fn set_handler(signal: Signal, handler: SignalHandler) -> SignalHandler { +pub fn set_handler(signal: Signal, handler: SignalHandler) -> SignalHandler { let mut table = TABLE.write(); let entry = &mut table[signal.into_raw() as usize]; core::mem::replace(entry, handler) } +/// Updates all signal handlers to point to a single handler. Intended to be used with custom +/// signal "routers". +pub fn set_all_handlers(handler: SignalHandler) { + let mut table = TABLE.write(); + for entry in table.iter_mut() { + *entry = handler; + } +} + /// Sets the stack that will be used to handle signals **on this thread**. /// /// # Safety diff --git a/ports/c-tests/compile.sh b/ports/c-tests/compile.sh new file mode 100755 index 00000000..2a68d889 --- /dev/null +++ b/ports/c-tests/compile.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +mkdir -p $3 +clang -target $Y_TRIPLE --sysroot $Y_SYSROOT -fPIC -o $3/c-test $1/test.c diff --git a/ports/c-tests/install.sh b/ports/c-tests/install.sh new file mode 100755 index 00000000..c60dafcb --- /dev/null +++ b/ports/c-tests/install.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +mkdir -p $Y_SYSROOT/bin +install -m0755 $3/c-test $Y_SYSROOT/bin/c-test diff --git a/ports/c-tests/port.toml b/ports/c-tests/port.toml index 3a645baa..46077fa3 100644 --- a/ports/c-tests/port.toml +++ b/ports/c-tests/port.toml @@ -2,4 +2,4 @@ description = "Test C/C++ programs" version = "0.0.1" [dependencies] -runtime = ["meta-libc", "libc++"] +runtime = ["meta-libc", "compiler-rt"] diff --git a/ports/c-tests/test.c b/ports/c-tests/test.c new file mode 100644 index 00000000..de224c00 --- /dev/null +++ b/ports/c-tests/test.c @@ -0,0 +1,15 @@ +#include +#include +#include +#include + +int main(int argc, char **argv) { + struct stat s; + if (stat("/etc/test.c", &s) != 0) { + return 1; + } + + printf("t = %lds, %ldns\n", s.st_mtim.tv_sec, s.st_mtim.tv_nsec); + + return 0; +} diff --git a/ports/compiler-rt/install.sh b/ports/compiler-rt/install.sh index 8bbab6c7..d4c1b3b3 100755 --- a/ports/compiler-rt/install.sh +++ b/ports/compiler-rt/install.sh @@ -2,5 +2,11 @@ build_dir=$3 +RT_LIB_DIR=$Y_SYSROOT/lib/clang/19/lib/$Y_TRIPLE + cd $build_dir cmake --build . -t install -j >/dev/null + +mkdir -p $RT_LIB_DIR +cd $RT_LIB_DIR +ln -sf ../../../../yggdrasil/libclang_rt.builtins-$Y_ARCH.a libclang_rt.builtins.a diff --git a/ports/llvm/install.sh b/ports/llvm/install.sh index 85383255..40ea2a33 100755 --- a/ports/llvm/install.sh +++ b/ports/llvm/install.sh @@ -5,3 +5,7 @@ set -e build_dir=$3 cd $build_dir cmake --build . -t install -j >/dev/null + +ln -sf clang $Y_SYSROOT/bin/cc +ln -sf clang++ $Y_SYSROOT/bin/cxx +ln -sf lld $Y_SYSROOT/bin/ld diff --git a/userspace/.gitignore b/userspace/.gitignore index ac9b50a3..97af3d94 100644 --- a/userspace/.gitignore +++ b/userspace/.gitignore @@ -1,2 +1,3 @@ /target /dynload-program/target +/etc/rc.d/*.ext diff --git a/userspace/dyn-loader/src/env.rs b/userspace/dyn-loader/src/env.rs index 8fb2d9f0..d67e53c1 100644 --- a/userspace/dyn-loader/src/env.rs +++ b/userspace/dyn-loader/src/env.rs @@ -79,14 +79,19 @@ pub fn build_argument(args: &[String], auxv: &[AuxValue]) -> Result -#include - -int main(int argc, char **argv) { - printf("Argument count: %d\n", argc); - for (int i = 0; i < argc; ++i) { - printf("Argument %d is %s\n", i, argv[i]); - } - return EXIT_SUCCESS; -} diff --git a/userspace/etc/test.cpp b/userspace/etc/test.cpp deleted file mode 100644 index 383118a4..00000000 --- a/userspace/etc/test.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include - -int main(int argc, char **argv) { - std::cout << "Test!!!" << std::endl; - return 0; -} diff --git a/userspace/etc/test.md b/userspace/etc/test.md deleted file mode 100644 index fd0bcf13..00000000 --- a/userspace/etc/test.md +++ /dev/null @@ -1,36 +0,0 @@ -1. List item one. - - List item one continued with a second paragraph followed by an - Indented block. - - $ ls *.sh - $ mv *.sh ~/tmp - - List item continued with a third paragraph. - -2. List item two continued with an open block. - - This paragraph is part of the preceding list item. - - 1. This list is nested and does not require explicit item continuation. - - This paragraph is part of the preceding list item. - - 2. List item b. - - This paragraph belongs to item two of the outer list. - -> This is a blockquote ->> This is a quote of a quote ->> * This is an item of quote-quote list ->> ->> This is a continuation of quote-quote-list ->> * This is another item of a quote-quote list - -Another paragraph - -* Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean eget congue risus. Aenean facilisis quis augue ac accumsan. Praesent felis odio, sagittis ut pulvinar a, pharetra non ante. Cras accumsan varius auctor. Suspendisse pharetra mauris eget eros congue, ut scelerisque enim pharetra. Quisque pellentesque ante quis porttitor condimentum. Nullam nisi purus, interdum a dui vitae, hendrerit eleifend leo. Integer tempus neque ut neque faucibus vulputate. Ut orci tellus, interdum et sagittis eu, interdum ut ex. Donec ac consectetur sem. Aenean eget mauris rutrum, condimentum nisi nec, tempor nulla. Nullam ullamcorper nibh vel ligula pellentesque blandit. Curabitur suscipit placerat gravida. Nam id consectetur urna. Morbi viverra lorem vel nulla varius, at placerat nulla posuere. Donec in bibendum ex, ut tincidunt sapien. -* In ut quam tellus. Nunc ac sem vestibulum, sollicitudin ligula id, facilisis tortor. Pellentesque quam ex, ornare id diam ac, sagittis volutpat quam. Etiam faucibus, eros non tristique venenatis, odio risus interdum dolor, porttitor volutpat nulla erat in ex. Nullam venenatis leo justo. Integer ullamcorper auctor orci, non pulvinar nisi volutpat molestie. Phasellus tristique, leo id convallis cursus, diam dolor pretium dui, nec suscipit nisl ligula sit amet magna. Interdum et malesuada fames ac ante ipsum primis in faucibus. Maecenas sit amet nibh vel mauris sagittis semper quis efficitur mauris. Aenean iaculis, lectus sit amet placerat scelerisque, dui libero maximus orci, at convallis justo urna eget quam. Aenean luctus felis tristique enim suscipit, non porta eros gravida. Aliquam erat volutpat. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum eleifend iaculis fringilla. -* Fusce eleifend mauris vel urna pretium, non suscipit massa accumsan. Phasellus pretium ultricies accumsan. Suspendisse accumsan bibendum erat, sit amet eleifend ipsum maximus in. Curabitur eleifend, ipsum ut sollicitudin varius, felis lectus elementum nibh, eget bibendum mi ex eget lacus. Nam erat sapien, sodales nec bibendum cursus, accumsan eget magna. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Ut quis aliquam est. Nunc et erat lacus. Proin consequat eleifend fringilla. Phasellus eget nulla orci. Proin fermentum mi eu nisi posuere blandit. Interdum et malesuada fames ac ante ipsum primis in faucibus. -* In eget facilisis nisi. Donec purus dolor, fringilla nec efficitur nec, elementum id metus. Aenean a scelerisque augue. Nullam tempor porttitor eros tempus imperdiet. Sed euismod felis sed neque venenatis, quis lobortis odio sagittis. Phasellus tristique auctor massa eget vulputate. Etiam nulla tellus, congue ut euismod a, posuere quis sapien. Morbi pellentesque orci sit amet commodo interdum. Nunc eleifend, velit consectetur tempor dictum, sem turpis rutrum erat, eget vehicula justo felis id erat. Morbi condimentum pulvinar sem, sit amet molestie enim suscipit quis. Aliquam convallis ante lectus, at lacinia ex gravida semper. Morbi vel metus aliquam, vulputate neque ac, sodales arcu. Ut quis bibendum sem. -* Vivamus elementum augue eget ligula laoreet, et feugiat turpis efficitur. Mauris eleifend lacus id felis condimentum, vitae volutpat tellus volutpat. Vestibulum justo diam, bibendum sit amet neque quis, commodo luctus nunc. Donec luctus, libero vel viverra venenatis, nisl libero cursus massa, dapibus tempus libero erat facilisis lacus. Donec vitae finibus metus, porta tempor velit. Proin velit odio, facilisis sit amet elementum at, laoreet at tellus. Phasellus convallis, neque sit amet imperdiet hendrerit, nisi quam laoreet odio, molestie venenatis nisl ligula in erat. diff --git a/userspace/etc/test.sh b/userspace/etc/test.sh deleted file mode 100755 index 67d00063..00000000 --- a/userspace/etc/test.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/sh - -/sbin/mount -t ext2 /dev/vb0p1 /mnt -/mnt/bin/clang -cc1as -filetype obj -main-file-name test.S -target-cpu x86-64 -mrelocation-model pic -triple x86_64-unknown-yggdrasil -o /test.o /etc/test.S -/mnt/bin/ld.lld -nostdlib -o /test /test.o -/bin/ls -lh / - diff --git a/userspace/lib/ygglibc/src/headers/fcntl/mod.rs b/userspace/lib/ygglibc/src/headers/fcntl/mod.rs index f83831e9..49b8bc3f 100644 --- a/userspace/lib/ygglibc/src/headers/fcntl/mod.rs +++ b/userspace/lib/ygglibc/src/headers/fcntl/mod.rs @@ -147,7 +147,13 @@ unsafe extern "C" fn fcntl(fd: c_int, cmd: c_int, _args: ...) -> CIntCountResult // TODO kernel support for fcntl let _file = RawFile::e_try_from(fd)?; - todo!("fcntl({}, {}, ...)", fd, cmd); + match cmd { + F_GETFD => CIntCountResult::success(0), + F_SETFD => CIntCountResult::success(0), + _ => { + todo!("fcntl({}, {}, ...)", fd, cmd); + } + } } unsafe fn vopenat(atfd: c_int, pathname: *const c_char, opts: c_int, mut ap: VaList) -> CFdResult { diff --git a/userspace/lib/ygglibc/src/headers/locale/mod.rs b/userspace/lib/ygglibc/src/headers/locale/mod.rs index 0039d8bf..c98021ba 100644 --- a/userspace/lib/ygglibc/src/headers/locale/mod.rs +++ b/userspace/lib/ygglibc/src/headers/locale/mod.rs @@ -94,7 +94,7 @@ unsafe extern "C" fn newlocale( #[no_mangle] unsafe extern "C" fn setlocale(_category: c_int, _locale: *const c_char) -> *mut c_char { // TODO - todo!() + c"C".as_ptr().cast_mut() } #[no_mangle] diff --git a/userspace/lib/ygglibc/src/headers/pthread/thread.rs b/userspace/lib/ygglibc/src/headers/pthread/thread.rs index 09f9e807..23822713 100644 --- a/userspace/lib/ygglibc/src/headers/pthread/thread.rs +++ b/userspace/lib/ygglibc/src/headers/pthread/thread.rs @@ -43,7 +43,8 @@ unsafe extern "C" fn pthread_create( #[no_mangle] unsafe extern "C" fn pthread_detach(_thread: pthread_t) -> c_int { - todo!() + // TODO + 0 } #[no_mangle] diff --git a/userspace/lib/ygglibc/src/headers/signal/mod.rs b/userspace/lib/ygglibc/src/headers/signal/mod.rs index 241a451a..4af1017c 100644 --- a/userspace/lib/ygglibc/src/headers/signal/mod.rs +++ b/userspace/lib/ygglibc/src/headers/signal/mod.rs @@ -1,12 +1,12 @@ use core::{ - ffi::{c_char, c_int, c_long, c_void}, + ffi::{c_char, c_int, c_long, c_void, CStr}, ptr::NonNull, }; use yggdrasil_rt::process::{signal as rt, Signal}; use crate::{ - error::{self, CIntZeroResult, CResult, EResult, TryFromExt}, + error::{self, CIntZeroResult, CResult, EResult}, headers::errno::Errno, signal, util::PointerExt, @@ -17,7 +17,7 @@ use super::{ sys_types::{pid_t, uid_t}, }; -pub type sig_handler_t = unsafe extern "C" fn(SigNumber); +pub type sig_handler_t = unsafe extern "C" fn(c_int); pub type sigset_t = u64; @@ -72,45 +72,41 @@ pub union sigval { // SIG_DFL, SIG_ERR, SIG_HOLD, SIG_IGN are in extern "C" { - fn __sig_terminate(_: SigNumber); - fn __sig_ignore(_: SigNumber); + fn __sig_terminate(_: c_int); + fn __sig_ignore(_: c_int); } -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[non_exhaustive] -#[repr(C)] -pub enum SigNumber { - SIGHUP = 1, - SIGINT = 2, - SIGQUIT = 3, - SIGILL = 4, - SIGTRAP = 5, - SIGABRT = 6, - SIGBUS = 7, - SIGFPE = 8, - SIGKILL = 9, - SIGUSR1 = 10, - SIGSEGV = 11, - SIGUSR2 = 12, - SIGPIPE = 13, - SIGALRM = 14, - SIGTERM = 15, - SIGCHLD = 17, - SIGCONT = 18, - SIGSTOP = 19, - SIGTSTP = 20, - SIGTTIN = 21, - SIGTTOU = 22, - SIGURG = 23, - SIGXCPU = 24, - SIGXFSZ = 25, - SIGVTALRM = 26, - SIGPROF = 27, - SIGWINCH = 28, - SIGPOLL = 29, - SIGPWR = 30, - SIGSYS = 31, -} +pub const SIGHUP: c_int = 1; +pub const SIGINT: c_int = 2; +pub const SIGQUIT: c_int = 3; +pub const SIGILL: c_int = 4; +pub const SIGTRAP: c_int = 5; +pub const SIGABRT: c_int = 6; +pub const SIGBUS: c_int = 7; +pub const SIGFPE: c_int = 8; +pub const SIGKILL: c_int = 9; +pub const SIGUSR1: c_int = 10; +pub const SIGSEGV: c_int = 11; +pub const SIGUSR2: c_int = 12; +pub const SIGPIPE: c_int = 13; +pub const SIGALRM: c_int = 14; +pub const SIGTERM: c_int = 15; +pub const SIGCHLD: c_int = 17; +pub const SIGCONT: c_int = 18; +pub const SIGSTOP: c_int = 19; +pub const SIGTSTP: c_int = 20; +pub const SIGTTIN: c_int = 21; +pub const SIGTTOU: c_int = 22; +pub const SIGURG: c_int = 23; +pub const SIGXCPU: c_int = 24; +pub const SIGXFSZ: c_int = 25; +pub const SIGVTALRM: c_int = 26; +pub const SIGPROF: c_int = 27; +pub const SIGWINCH: c_int = 28; +pub const SIGPOLL: c_int = 29; +pub const SIGPWR: c_int = 30; +pub const SIGSYS: c_int = 31; +pub const SIGNAL_MAX: c_int = SIGSYS + 1; pub const SIGEV_NONE: c_int = 0; pub const SIGEV_SIGNAL: c_int = 1; @@ -134,32 +130,64 @@ pub const SS_DISABLE: c_int = 1 << 9; pub const MINSIGSTKSZ: usize = 2 * 4096; pub const SIGSTKSZ: usize = 8 * 4096; -impl TryFromExt for Signal { - fn e_try_from(value: SigNumber) -> EResult { - match value { - SigNumber::SIGABRT => EResult::Ok(Signal::Aborted), - SigNumber::SIGSEGV => EResult::Ok(Signal::MemoryAccessViolation), - SigNumber::SIGINT => EResult::Ok(Signal::Interrupted), - SigNumber::SIGILL => EResult::Ok(Signal::InvalidInstruction), - SigNumber::SIGKILL => EResult::Ok(Signal::Killed), - SigNumber::SIGTERM => EResult::Ok(Signal::Terminated), - _ => EResult::Err(Errno::EINVAL), - } +pub fn int_to_signum(value: c_int) -> EResult { + match value { + SIGABRT => EResult::Ok(Signal::Aborted), + SIGSEGV => EResult::Ok(Signal::MemoryAccessViolation), + SIGINT => EResult::Ok(Signal::Interrupted), + SIGILL => EResult::Ok(Signal::InvalidInstruction), + SIGKILL => EResult::Ok(Signal::Killed), + SIGTERM => EResult::Ok(Signal::Terminated), + _ => EResult::Err(Errno::EINVAL), } } -impl From for SigNumber { - fn from(value: Signal) -> Self { - match value { - Signal::Aborted => Self::SIGABRT, - Signal::MemoryAccessViolation => Self::SIGSEGV, - Signal::Interrupted => Self::SIGINT, - Signal::InvalidInstruction => Self::SIGILL, - Signal::Killed => Self::SIGKILL, - Signal::Terminated => Self::SIGTERM, - // Never issued/handled - Signal::Debug => unreachable!(), - } +pub fn signum_to_int(value: Signal) -> c_int { + match value { + Signal::Aborted => SIGABRT, + Signal::MemoryAccessViolation => SIGSEGV, + Signal::Interrupted => SIGINT, + Signal::InvalidInstruction => SIGILL, + Signal::Killed => SIGKILL, + Signal::Terminated => SIGTERM, + // Never issued/handled + Signal::Debug => unreachable!(), + } +} + +pub fn signal_string(value: c_int) -> Option<&'static CStr> { + match value { + SIGHUP => Some(c"Hangup"), + SIGINT => Some(c"Interrupted"), + SIGQUIT => Some(c"Quit"), + SIGILL => Some(c"Illegal instruction"), + SIGTRAP => Some(c"Trap/breakpoint"), + SIGABRT => Some(c"Aborted"), + SIGBUS => Some(c"Bus error"), + SIGFPE => Some(c"Floating-point exception"), + SIGKILL => Some(c"Killed"), + SIGUSR1 => Some(c"User signal 1"), + SIGSEGV => Some(c"Segmentation fault"), + SIGUSR2 => Some(c"User signal 2"), + SIGPIPE => Some(c"Broken pipe"), + SIGALRM => Some(c"Timer alarm"), + SIGTERM => Some(c"Terminated"), + SIGCHLD => Some(c"Child quit"), + SIGCONT => Some(c"Continued"), + SIGSTOP => Some(c"Stopped"), + SIGTSTP => Some(c"Terminal stopped"), + SIGTTIN => Some(c"Background process terminal input"), + SIGTTOU => Some(c"Background process terminal output"), + SIGURG => Some(c"Urgent"), + SIGXCPU => Some(c"CPU time limit exceeded"), + SIGXFSZ => Some(c"File size limit exceeded"), + SIGVTALRM => Some(c"Virtual alarm"), + SIGPROF => Some(c"Profiling timer expired"), + SIGWINCH => Some(c"Window size changed"), + SIGPOLL => Some(c"Pollable event"), + SIGPWR => Some(c"Power failure"), + SIGSYS => Some(c"Bad system call"), + _ => None, } } @@ -205,7 +233,7 @@ unsafe extern "C" fn raise(_signum: c_int) -> c_int { #[no_mangle] unsafe extern "C" fn sigaction( - signum: SigNumber, + signum: c_int, _new: *const sigaction, _old: *mut sigaction, ) -> CIntZeroResult { @@ -214,8 +242,13 @@ unsafe extern "C" fn sigaction( } #[no_mangle] -unsafe extern "C" fn sigaddset(_mask: *mut sigset_t, _signum: c_int) -> c_int { - todo!() +unsafe extern "C" fn sigaddset(mask: *mut sigset_t, signum: c_int) -> CIntZeroResult { + if signum > 63 || signum <= 0 { + error::errno = Errno::EINVAL; + return CIntZeroResult::ERROR; + } + *mask |= 1 << signum; + CIntZeroResult::SUCCESS } #[no_mangle] @@ -277,7 +310,7 @@ unsafe extern "C" fn sigismember(_mask: *const sigset_t, _signum: c_int) -> c_in } #[no_mangle] -unsafe extern "C" fn signal(handler: sig_handler_t, signum: SigNumber) -> sig_handler_t { +unsafe extern "C" fn signal(signum: c_int, handler: sig_handler_t) -> sig_handler_t { // Validate handler let address = handler as usize; // NULL or SIG_ERR diff --git a/userspace/lib/ygglibc/src/headers/spawn/mod.rs b/userspace/lib/ygglibc/src/headers/spawn/mod.rs index 672e16f6..42c2474a 100644 --- a/userspace/lib/ygglibc/src/headers/spawn/mod.rs +++ b/userspace/lib/ygglibc/src/headers/spawn/mod.rs @@ -37,20 +37,24 @@ unsafe extern "C" fn posix_spawn( _file_actions: *const posix_spawn_file_actions_t, _attrp: *const posix_spawnattr_t, argv: *const *mut c_char, - _envp: *const *mut c_char, + envp: *const *mut c_char, ) -> CIntZeroResult { let path = path.ensure_str(); let argv = NullTerminatedArrayIter::new(argv); + let envp = NullTerminatedArrayIter::new(envp); let args = argv .map(|arg| arg.cast_const().ensure_str()) .collect::>(); + let envs = envp + .map(|env| env.cast_const().ensure_str()) + .collect::>(); - log::info!("posix_spawn({path:?}, {args:?})"); + log::info!("posix_spawn({path:?}, {args:?}, {envs:?})"); let options = SpawnOptions { program: path, arguments: &args, - environment: &[], + environment: &envs, directory: None, optional: &[ SpawnOption::CopyFile { @@ -72,6 +76,7 @@ unsafe extern "C" fn posix_spawn( if let Some(pid) = pid.as_mut() { *pid = id.bits() as i32; } + log::info!(" -> {id:?}"); CIntZeroResult::SUCCESS } @@ -120,19 +125,23 @@ unsafe extern "C" fn posix_spawn_file_actions_addopen( unsafe extern "C" fn posix_spawn_file_actions_destroy( _file_actions: *mut posix_spawn_file_actions_t, ) -> c_int { - todo!() + log::warn!("TODO: posix_spawn_file_actions_destroy"); + 0 } #[no_mangle] unsafe extern "C" fn posix_spawn_file_actions_init( _file_actions: *mut posix_spawn_file_actions_t, ) -> c_int { - todo!() + log::warn!("TODO: posix_spawn_file_actions_init"); + 0 + // todo!() } #[no_mangle] unsafe extern "C" fn posix_spawnattr_destroy(_attrp: *mut posix_spawnattr_t) -> c_int { - todo!() + log::warn!("TODO: posix_spawnattr_destroy"); + 0 } #[no_mangle] @@ -186,7 +195,8 @@ unsafe extern "C" fn posix_spawnattr_getsigmask( #[no_mangle] unsafe extern "C" fn posix_spawnattr_init(_attrp: *mut posix_spawnattr_t) -> c_int { - todo!() + log::warn!("TODO: posix_spawnattr_init"); + 0 } #[no_mangle] @@ -194,7 +204,8 @@ unsafe extern "C" fn posix_spawnattr_setflags( _attrp: *mut posix_spawnattr_t, _flags: c_short, ) -> c_int { - todo!() + log::warn!("TODO: posix_spawnattr_setflags"); + 0 } #[no_mangle] @@ -234,5 +245,7 @@ unsafe extern "C" fn posix_spawnattr_setsigmask( _attrp: *mut posix_spawnattr_t, _sigset: *const c_void, ) -> c_int { - todo!() + log::warn!("TODO: posix_spawnattr_setsigmask"); + 0 + // todo!() } diff --git a/userspace/lib/ygglibc/src/headers/string/str.rs b/userspace/lib/ygglibc/src/headers/string/str.rs index 8725e8cc..2e124197 100644 --- a/userspace/lib/ygglibc/src/headers/string/str.rs +++ b/userspace/lib/ygglibc/src/headers/string/str.rs @@ -7,7 +7,7 @@ use core::{ use crate::{ allocator, error::CPtrResult, - headers::{errno::Errno, locale::locale_t}, + headers::{errno::Errno, locale::locale_t, signal::signal_string}, }; use super::mem::{memcpy, mempcpy, memset}; @@ -69,7 +69,15 @@ unsafe extern "C" fn strcmp(a: *const c_char, b: *const c_char) -> c_int { #[no_mangle] pub unsafe extern "C" fn strcpy(dst: *mut c_char, src: *const c_char) -> *mut c_char { - stpcpy(dst, src); + let mut i = 0; + loop { + let c = src.add(i).read(); + dst.add(i).write(c); + if c == 0 { + break; + } + i += 1; + } dst } @@ -246,8 +254,11 @@ unsafe extern "C" fn strrchr(a: *const c_char, c: c_int) -> *mut c_char { } #[no_mangle] -unsafe extern "C" fn strsignal(_signum: c_int) -> *mut c_char { - todo!() +unsafe extern "C" fn strsignal(signum: c_int) -> *mut c_char { + match signal_string(signum) { + Some(name) => name.as_ptr().cast_mut(), + None => null_mut(), + } } #[no_mangle] diff --git a/userspace/lib/ygglibc/src/headers/sys_stat/mod.rs b/userspace/lib/ygglibc/src/headers/sys_stat/mod.rs index ab9749e4..885b197d 100644 --- a/userspace/lib/ygglibc/src/headers/sys_stat/mod.rs +++ b/userspace/lib/ygglibc/src/headers/sys_stat/mod.rs @@ -13,7 +13,7 @@ use crate::{ use super::{ fcntl::{AT_FDCWD, AT_SYMLINK_NOFOLLOW}, - sys_time::__ygg_timespec_t, + sys_time::{__ygg_timespec_t, timespec}, sys_types::{blkcnt_t, blksize_t, dev_t, gid_t, ino_t, mode_t, nlink_t, off_t, uid_t}, }; @@ -77,10 +77,13 @@ impl From for stat { let st_size: off_t = value.size.try_into().unwrap(); let st_uid = u32::from(value.uid).try_into().unwrap(); let st_gid = u32::from(value.gid).try_into().unwrap(); - // TODO let st_blksize = value.block_size as _; let st_blocks = st_size.div_ceil(st_blksize as _).try_into().unwrap(); let st_ino = value.inode.unwrap_or(0) as u64; + let st_ctim = timespec::from(value.ctime); + let st_mtim = timespec::from(value.mtime); + let st_atim = timespec::from(value.atime); + log::info!("{st_mtim:?}, {:?}", value.mtime); Self { st_mode, @@ -90,7 +93,12 @@ impl From for stat { st_blksize, st_blocks, st_ino, - ..Default::default() + st_mtim, + st_atim, + st_ctim, + st_nlink: 1, + st_dev: 0, + st_rdev: 0, } } } diff --git a/userspace/lib/ygglibc/src/headers/sys_time/mod.rs b/userspace/lib/ygglibc/src/headers/sys_time/mod.rs index 978d91a8..4c3f73fe 100644 --- a/userspace/lib/ygglibc/src/headers/sys_time/mod.rs +++ b/userspace/lib/ygglibc/src/headers/sys_time/mod.rs @@ -4,6 +4,7 @@ use core::{ }; use yggdrasil_abi::time::MICROSECONDS_IN_SECOND; +use yggdrasil_rt::time::SystemTime; use super::sys_types::{suseconds_t, time_t}; @@ -97,6 +98,15 @@ impl From<__ygg_timespec_t> for Duration { } } +impl From for __ygg_timespec_t { + fn from(value: SystemTime) -> Self { + Self { + tv_sec: time_t(value.seconds() as i64), + tv_nsec: value.subsec_nanos() as i64, + } + } +} + impl From for __ygg_timespec_t { fn from(value: Duration) -> Self { Self { diff --git a/userspace/lib/ygglibc/src/headers/sys_wait/mod.rs b/userspace/lib/ygglibc/src/headers/sys_wait/mod.rs index 671ea471..12589360 100644 --- a/userspace/lib/ygglibc/src/headers/sys_wait/mod.rs +++ b/userspace/lib/ygglibc/src/headers/sys_wait/mod.rs @@ -39,7 +39,9 @@ fn wait_inner(what: ProcessWait, nonblocking: bool) -> EResult { if nonblocking { flags |= WaitFlags::NON_BLOCKING; } + log::info!("wait_inner({what:?})"); let pid = unsafe { syscall::wait_process(&what, &mut status, flags) }.e_map_err(Errno::from)?; + log::info!(" -> {pid:?}"); EResult::Ok(WaitResult { pid, status }) } @@ -68,8 +70,20 @@ fn encode_exit_status(code: ExitCode) -> c_int { } #[no_mangle] -unsafe extern "C" fn wait(_status: *mut c_int) -> pid_t { - todo!() +unsafe extern "C" fn wait(status: *mut c_int) -> pid_t { + let result = wait_inner(ProcessWait::AnyChild, false); + match result { + EResult::Ok(res) => { + if let Some(status) = status.as_mut() { + *status = encode_exit_status(res.status); + } + res.pid.bits() as pid_t + } + EResult::Err(error) => { + error::errno = error; + -1 as pid_t + } + } } // TODO siginfo_t diff --git a/userspace/lib/ygglibc/src/headers/unistd/fs.rs b/userspace/lib/ygglibc/src/headers/unistd/fs.rs index ee4b6304..4264d90c 100644 --- a/userspace/lib/ygglibc/src/headers/unistd/fs.rs +++ b/userspace/lib/ygglibc/src/headers/unistd/fs.rs @@ -1,13 +1,17 @@ -use core::ffi::{c_char, c_int, c_long}; +use core::{ + ffi::{c_char, c_int, c_long}, + ptr::{null_mut, NonNull}, +}; use crate::{ - error::{CIntZeroResult, TryFromExt}, + error::{self, CIntZeroResult, CPtrResult, EResult, TryFromExt}, headers::{ + errno::Errno, fcntl::{faccessat, AT_FDCWD}, sys_types::{gid_t, off_t, uid_t}, }, - io::{raw::RawFile, FromRawFd}, - util::PointerExt, + io::{self, raw::RawFile, FromRawFd}, + util::{PointerExt, PointerStrExt}, }; pub const _PC_PATH_MAX: c_int = 0; @@ -18,8 +22,10 @@ unsafe extern "C" fn access(path: *const c_char, mode: c_int) -> CIntZeroResult } #[no_mangle] -unsafe extern "C" fn chdir(path: *const c_char) -> c_int { - todo!() +unsafe extern "C" fn chdir(path: *const c_char) -> CIntZeroResult { + let path = path.ensure_str(); + io::set_current_directory(None, path)?; + CIntZeroResult::SUCCESS } #[no_mangle] @@ -56,17 +62,21 @@ unsafe extern "C" fn ftruncate(fd: c_int, size: off_t) -> CIntZeroResult { } #[no_mangle] -unsafe extern "C" fn getcwd(buf: *mut c_char, len: usize) -> *mut c_char { - let buffer = buf.ensure_slice_mut(len); - // TODO - if buffer.len() < 2 { - return core::ptr::null_mut(); - } +unsafe extern "C" fn getcwd(buf: *mut c_char, len: usize) -> CPtrResult { + let result = io::get_current_directory(|s| { + if buf.is_null() { + todo!(); + } + if len < s.len() + 1 { + return EResult::Err(Errno::ERANGE); + } + let buffer = buf.cast::().ensure_slice_mut(len); + buffer[..s.len()].copy_from_slice(s.as_bytes()); + buffer[s.len()] = 0; + EResult::Ok(NonNull::new_unchecked(buf)) + })?; - buffer[0] = b'/' as _; - buffer[1] = 0; - - buffer.as_mut_ptr() + CPtrResult::success(result) } #[no_mangle] @@ -126,7 +136,9 @@ unsafe extern "C" fn truncate(path: *const c_char, size: off_t) -> c_int { } #[no_mangle] unsafe extern "C" fn unlink(path: *const c_char) -> c_int { - todo!() + let path = path.ensure_str(); + log::warn!("TODO: unlink({path:?})"); + 0 } #[no_mangle] unsafe extern "C" fn unlinkat(atfd: c_int, path: *const c_char, flags: c_int) -> c_int { diff --git a/userspace/lib/ygglibc/src/headers/unistd/util.rs b/userspace/lib/ygglibc/src/headers/unistd/util.rs index 27f3b35f..1c60bd78 100644 --- a/userspace/lib/ygglibc/src/headers/unistd/util.rs +++ b/userspace/lib/ygglibc/src/headers/unistd/util.rs @@ -1,4 +1,11 @@ -use core::ffi::{c_char, c_int, c_long, c_uint, c_void}; +use core::ffi::{c_char, c_int, c_long, c_uint, c_void, CStr}; + +use crate::{ + env::get_env, + error::{self, EResult}, + headers::errno::Errno, + util::PointerExt, +}; #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] #[non_exhaustive] @@ -12,6 +19,8 @@ pub enum Sysconf { _SC_ARG_MAX, } +pub const _CS_PATH: c_int = 1; + #[no_mangle] unsafe extern "C" fn alarm(value: c_uint) -> c_uint { todo!() @@ -19,7 +28,33 @@ unsafe extern "C" fn alarm(value: c_uint) -> c_uint { #[no_mangle] unsafe extern "C" fn confstr(name: c_int, buf: *mut c_char, size: usize) -> usize { - todo!() + match name { + _CS_PATH => { + let path = match get_env("PATH".as_bytes()) { + EResult::Ok(value) => value, + EResult::Err(err) => { + error::errno = Errno::EINVAL; + return 0; + } + }; + let Some(path) = path else { + return 0; + }; + let cstr = CStr::from_ptr(path.as_ptr()); + if !buf.is_null() && size > 0 { + let buf = buf.cast::().ensure_slice_mut(size); + let bytes = cstr.to_bytes(); + let len = core::cmp::min(buf.len() - 1, bytes.len()); + buf[..len].copy_from_slice(&bytes[..len]); + buf[len] = 0; + } + cstr.to_bytes().len() + } + _ => { + error::errno = Errno::EINVAL; + 0 + } + } } #[no_mangle] diff --git a/userspace/lib/ygglibc/src/io/mod.rs b/userspace/lib/ygglibc/src/io/mod.rs index f393f178..5a4f02f5 100644 --- a/userspace/lib/ygglibc/src/io/mod.rs +++ b/userspace/lib/ygglibc/src/io/mod.rs @@ -136,6 +136,18 @@ pub fn create_directory(at: Option, path: &str, mode: FileMode) -> EResul EResult::Ok(()) } +pub fn set_current_directory(at: Option, path: &str) -> EResult<()> { + // TODO + assert!(at.is_none()); + yggdrasil_rt::io::set_current_directory(path)?; + EResult::Ok(()) +} + +pub fn get_current_directory EResult>(mapper: F) -> EResult { + let result = yggdrasil_rt::io::current_directory(mapper)??; + EResult::Ok(result) +} + pub fn realpath>(path: P) -> EResult { // Cases: // * /a/b/c -> /a/b/c diff --git a/userspace/lib/ygglibc/src/signal.rs b/userspace/lib/ygglibc/src/signal.rs index 8bb7ca80..e0de7f53 100644 --- a/userspace/lib/ygglibc/src/signal.rs +++ b/userspace/lib/ygglibc/src/signal.rs @@ -1,29 +1,54 @@ -use yggdrasil_rt::process::{ - signal::{self, SignalHandler}, - Signal, +use core::ffi::c_int; + +use yggdrasil_rt::{ + process::{ + signal::{self, SignalHandler}, + ExitCode, Signal, + }, + sync::rwlock::RwLock, }; use crate::{ - error::{EResult, TryFromExt}, - headers::signal::{sig_handler_t, SigNumber}, + error::EResult, + headers::{ + errno::Errno, + signal::{sig_handler_t, signum_to_int, SIGNAL_MAX}, + }, }; const SIGNAL_STACK_SIZE: usize = 4096 * 8; -// These are just stubs for addresses, which get converted into Rust handlers +static SIGNAL_TABLE: RwLock<[Option; SIGNAL_MAX as usize]> = + RwLock::new([None; SIGNAL_MAX as usize]); #[no_mangle] -unsafe extern "C" fn __sig_ignore(_signum: SigNumber) { +unsafe extern "C" fn __sig_ignore(_signum: c_int) { unreachable!() } #[no_mangle] -unsafe extern "C" fn __sig_terminate(_signum: SigNumber) { +unsafe extern "C" fn __sig_terminate(_signum: c_int) { unreachable!() } -pub unsafe fn set(sig: SigNumber, handler: sig_handler_t) -> EResult { - let signal = Signal::e_try_from(sig)?; +fn signal_handler(signal: Signal) { + let signum = signum_to_int(signal); + if let Some(entry) = SIGNAL_TABLE.read()[signum as usize] { + unsafe { entry(signum) }; + } else { + let pid = unsafe { yggdrasil_rt::sys::get_pid() }.into_raw(); + log::error!("{pid}: terminated by signal {signal:?}"); + unsafe { yggdrasil_rt::sys::exit_process(ExitCode::BySignal(Ok(signal))) }; + } +} + +pub unsafe fn set(sig: c_int, handler: sig_handler_t) -> EResult { + if sig < 0 || sig >= SIGNAL_MAX { + return EResult::Err(Errno::EINVAL); + } + + let mut table = SIGNAL_TABLE.write(); + // let signal = int_to_signum(sig)?; let address = handler as usize; let handler = match handler { // Transform special cases into Rust signal handlers instead @@ -32,14 +57,11 @@ pub unsafe fn set(sig: SigNumber, handler: sig_handler_t) -> EResult core::mem::transmute(handler), }; - let old = signal::set_handler(signal, SignalHandler::C(handler)); + let old = table[sig as usize].replace(handler); + let old = match old { - // Transform Rust special cases into C "handlers" - SignalHandler::Ignore => __sig_ignore, - SignalHandler::Terminate => __sig_terminate, - // libc doesn't set Rust signal handlers, return terminate just in case - SignalHandler::Rust(_) => __sig_terminate, - SignalHandler::C(handler) => core::mem::transmute(handler), + Some(old) => core::mem::transmute(old), + None => __sig_terminate as sig_handler_t, }; EResult::Ok(old) } @@ -52,4 +74,6 @@ pub fn init(main: bool) { } else { signal::setup_signal_stack(SIGNAL_STACK_SIZE).expect("Couldn't setup thread signal stack"); } + + signal::set_all_handlers(SignalHandler::Rust(signal_handler)); } diff --git a/userspace/sysutils/Cargo.toml b/userspace/sysutils/Cargo.toml index 29ca6fc1..47701782 100644 --- a/userspace/sysutils/Cargo.toml +++ b/userspace/sysutils/Cargo.toml @@ -58,6 +58,10 @@ name = "kmod" path = "src/kmod.rs" # /bin +[[bin]] +name = "echo" +path = "src/echo.rs" + [[bin]] name = "ls" path = "src/ls.rs" diff --git a/userspace/sysutils/src/echo.rs b/userspace/sysutils/src/echo.rs new file mode 100644 index 00000000..c268e4db --- /dev/null +++ b/userspace/sysutils/src/echo.rs @@ -0,0 +1,10 @@ +fn main() { + let args = std::env::args().skip(1); + for (i, arg) in args.enumerate() { + if i != 0 { + print!(" "); + } + print!("{arg}"); + } + println!(); +} diff --git a/userspace/sysutils/src/ls.rs b/userspace/sysutils/src/ls.rs index 86ecad39..23267496 100644 --- a/userspace/sysutils/src/ls.rs +++ b/userspace/sysutils/src/ls.rs @@ -250,7 +250,8 @@ impl Entry { #[cfg(any(target_os = "yggdrasil", rust_analyzer))] impl Entry { pub fn is_device(&self) -> bool { - self.ty.map_or(false, |d| d.is_block_device() || d.is_char_device()) + self.ty + .map_or(false, |d| d.is_block_device() || d.is_char_device()) } pub fn is_executable(&self) -> bool { @@ -285,6 +286,7 @@ impl MetadataImpl for Metadata { } fn convert_file_time(time: SystemTime) -> chrono::DateTime { + log::info!("time = {time:?}"); let timestamp = time.duration_since(SystemTime::UNIX_EPOCH).unwrap(); chrono::DateTime::from_timestamp(timestamp.as_secs() as _, 0).unwrap() } @@ -550,6 +552,7 @@ fn run(opts: &Args) -> Vec> { } pub fn main() -> ExitCode { + logsink::setup_logging(false); let mut args = Args::parse(); if !stdout().is_terminal() { diff --git a/userspace/sysutils/src/mount.rs b/userspace/sysutils/src/mount.rs index bf8690aa..89898a5a 100644 --- a/userspace/sysutils/src/mount.rs +++ b/userspace/sysutils/src/mount.rs @@ -1,13 +1,26 @@ #![feature(rustc_private, yggdrasil_os)] +use runtime::{abi as yggdrasil_abi, rt as yggdrasil_rt}; use std::{ - os::yggdrasil::io::device::{mount_raw, MountOptions}, + io, process::ExitCode, + time::{Duration, Instant}, }; +use yggdrasil_abi::io::MountOptions; use clap::Parser; +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("{0}")] + Io(#[from] io::Error), + #[error("Timed out")] + TimedOut, +} + #[derive(Parser, Debug)] struct Args { + #[arg(short, help = "Wait for the device to become present in N seconds")] + wait: Option, #[arg(short)] ty: Option, #[arg(short)] @@ -16,6 +29,36 @@ struct Args { target: Option, } +fn try_mount(options: &MountOptions) -> io::Result<()> { + unsafe { yggdrasil_rt::sys::mount(options) }?; + Ok(()) +} + +fn run(args: &Args, options: &MountOptions) -> Result<(), Error> { + let start = Instant::now(); + let timeout = args.wait.map(Duration::from_secs); + + loop { + match try_mount(options) { + Ok(()) => return Ok(()), + Err(e) if e.kind() == io::ErrorKind::NotFound => { + if let Some(timeout) = timeout { + if start.elapsed() >= timeout { + return Err(Error::TimedOut); + } else { + std::thread::sleep(Duration::from_millis(100)); + } + } else { + return Err(e.into()); + } + } + Err(e) => { + return Err(e.into()); + } + } + } +} + fn main() -> ExitCode { let args = Args::parse(); @@ -30,18 +73,14 @@ fn main() -> ExitCode { let options = options.as_deref(); // Permissions are not yet implemented, lol - let result = unsafe { - let options = MountOptions { - source, - filesystem, - options, - target, - }; - - mount_raw(&options) + let options = MountOptions { + source, + filesystem, + options, + target, }; - match result { + match run(&args, &options) { Ok(()) => ExitCode::SUCCESS, Err(err) => { eprintln!("mount: {:?}", err); diff --git a/userspace/tools/shell/src/main.rs b/userspace/tools/shell/src/main.rs index 856bc2f5..288d4928 100644 --- a/userspace/tools/shell/src/main.rs +++ b/userspace/tools/shell/src/main.rs @@ -30,10 +30,14 @@ pub mod syntax; pub enum Error { #[error("{0}")] Io(#[from] io::Error), + #[error("Invalid usage")] + InvalidUsage, } #[derive(Debug, Parser)] pub struct ShellArgs { + #[arg(short)] + command: Option, #[arg(short)] login: bool, script: Option, @@ -67,6 +71,38 @@ impl ShellInput { } } +fn run_single(env: &Env, command: &str) -> Outcome { + let line = command.trim(); + + let command = match parse_interactive(line) { + Ok(c) => c, + Err(e) => { + eprintln!("Syntax error: {e}"); + return Outcome::err(); + } + }; + let command = match command.expand(env) { + Ok(c) => c, + Err(e) => { + eprintln!("{e}"); + return Outcome::err(); + } + }; + let (outcome, exit) = match exec::eval(command) { + Ok(res) => res, + Err(error) => { + eprintln!("{error}"); + return Outcome::err(); + } + }; + + if let Some(exit) = exit { + exit.exit_process(); + } + + outcome +} + fn run(mut input: ShellInput, env: &Env) -> Result<(), Error> { let mut line = String::new(); loop { @@ -140,16 +176,27 @@ fn find_script>(arg: &P) -> &Path { } fn run_wrapper(args: ShellArgs, env: &mut Env) -> Result<(), Error> { - match args.script { - Some(script) => { + let shell_name = std::env::args().next().unwrap(); + match (args.command, args.script) { + (Some(_), Some(_)) => { + eprintln!("{shell_name}: cannot mix '-c' and regular arguments"); + Err(Error::InvalidUsage) + } + (Some(command), None) => match run_single(env, &command) { + Outcome::ExitShell(_) => unreachable!(), + Outcome::Process(status) if status.success() => ExitCode::SUCCESS.exit_process(), + Outcome::Process(status) if let Some(code) = status.code() => std::process::exit(code), + Outcome::Process(_) => todo!(), + Outcome::Builtin(code) => code.exit_process(), + }, + (None, Some(script)) => { let script_path = find_script(&script); let script_path_str = script_path.to_str().unwrap(); env.put_var("0", script_path_str.into()); let script = BufReader::new(File::open(script_path)?); run(ShellInput::File(script), env) } - None => { - let shell_name = std::env::args().next().unwrap(); + (None, None) => { env.put_var("0", shell_name.into()); run(ShellInput::Interactive, env) } diff --git a/xtask/src/build/userspace.rs b/xtask/src/build/userspace.rs index 00d648e5..f8b1a0c1 100644 --- a/xtask/src/build/userspace.rs +++ b/xtask/src/build/userspace.rs @@ -29,6 +29,7 @@ const PROGRAMS: &[(&str, &str)] = &[ // shell ("shell", "bin/sh"), // sysutils + ("echo", "bin/echo"), ("mount", "sbin/mount"), ("login", "sbin/login"), ("strace", "bin/strace"), @@ -150,8 +151,6 @@ fn build_rootfs, D: AsRef>( } } - // TODO this is a temporary hack - fs::create_dir_all(rootfs_dir.join("lib"))?; // TODO other architectures util::copy_file( env.workspace_root.join(format!( @@ -162,13 +161,6 @@ fn build_rootfs, D: AsRef>( rootfs_dir.join("dynload-program"), )?; - let libstd_so = env.workspace_root.join(format!( - "toolchain/build/host/stage1-std/{}-unknown-yggdrasil/release/libstd.so", - env.arch.name() - )); - - util::copy_file(libstd_so, rootfs_dir.join("lib/libstd.so"))?; - log::info!("Installing extras"); for (src, dst) in install_extra { util::copy_file(src, rootfs_dir.join(dst))?;