diff --git a/Cargo.lock b/Cargo.lock index da3013f8a6..eadb0808a4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -517,6 +517,9 @@ name = "daemon" version = "0.0.0" dependencies = [ "libc", + "libredox", + "redox-scheme", + "redox_syscall 0.7.1", ] [[package]] diff --git a/daemon/Cargo.toml b/daemon/Cargo.toml index 3dba6f6671..d4cf2cdc86 100644 --- a/daemon/Cargo.toml +++ b/daemon/Cargo.toml @@ -5,3 +5,6 @@ edition = "2024" [dependencies] libc.workspace = true +libredox.workspace = true +redox-scheme.workspace = true +redox_syscall.workspace = true diff --git a/daemon/src/lib.rs b/daemon/src/lib.rs index 8967469d93..eb18ee6c4d 100644 --- a/daemon/src/lib.rs +++ b/daemon/src/lib.rs @@ -1,19 +1,32 @@ #![feature(never_type)] use std::io::{self, PipeWriter, Read, Write}; -use std::os::fd::{AsRawFd, FromRawFd}; +use std::os::fd::{AsRawFd, FromRawFd, RawFd}; use std::process::Command; +use libredox::Fd; +use redox_scheme::Socket; +use redox_scheme::scheme::{SchemeAsync, SchemeSync}; + +unsafe fn get_fd(var: &str) -> RawFd { + let fd: RawFd = std::env::var(var).unwrap().parse().unwrap(); + if unsafe { libc::fcntl(fd, libc::F_SETFD, libc::FD_CLOEXEC) } == -1 { + panic!( + "daemon: failed to set CLOEXEC flag for {var} fd: {}", + io::Error::last_os_error() + ); + } + fd +} + #[must_use = "Daemon::ready must be called"] pub struct Daemon { write_pipe: PipeWriter, } impl Daemon { - pub fn new !>(f: F) -> ! { - let write_pipe = unsafe { - io::PipeWriter::from_raw_fd(std::env::var("INIT_NOTIFY").unwrap().parse().unwrap()) - }; + pub fn new(f: impl FnOnce(Daemon) -> !) -> ! { + let write_pipe = unsafe { io::PipeWriter::from_raw_fd(get_fd("INIT_NOTIFY")) }; f(Daemon { write_pipe }) } @@ -25,10 +38,10 @@ impl Daemon { pub fn spawn(mut cmd: Command) { let (mut read_pipe, write_pipe) = io::pipe().unwrap(); - // Pass pipe to child + // Pass notify pipe to child if unsafe { libc::fcntl(write_pipe.as_raw_fd(), libc::F_SETFD, 0) } == -1 { eprintln!( - "daemon: failed to unset CLOEXEC flag for pipe: {}", + "daemon: failed to unset CLOEXEC flag for notify pipe: {}", io::Error::last_os_error() ); return; @@ -57,3 +70,103 @@ impl Daemon { } } } + +#[must_use = "SchemeDaemon::ready must be called"] +pub struct SchemeDaemon { + write_pipe: PipeWriter, +} + +impl SchemeDaemon { + pub fn new(f: impl FnOnce(SchemeDaemon) -> !) -> ! { + let write_pipe = unsafe { io::PipeWriter::from_raw_fd(get_fd("INIT_NOTIFY")) }; + + f(SchemeDaemon { write_pipe }) + } + + pub fn ready_with_fd(self, cap_fd: Fd) -> syscall::Result<()> { + syscall::call_wo( + self.write_pipe.as_raw_fd() as usize, + &cap_fd.into_raw().to_ne_bytes(), + syscall::CallFlags::FD, + &[], + )?; + Ok(()) + } + + pub fn ready_sync_scheme( + self, + socket: &Socket, + scheme: &mut S, + ) -> syscall::Result<()> { + let cap_id = scheme.scheme_root()?; + let cap_fd = socket.create_this_scheme_fd(0, cap_id, 0, 0)?; + self.ready_with_fd(Fd::new(cap_fd)) + } + + pub fn ready_async_scheme( + self, + socket: &Socket, + scheme: &mut S, + ) -> syscall::Result<()> { + let cap_id = scheme.scheme_root()?; + let cap_fd = socket.create_this_scheme_fd(0, cap_id, 0, 0)?; + self.ready_with_fd(Fd::new(cap_fd)) + } + + pub fn spawn(mut cmd: Command, scheme_name: &str) { + let (read_pipe, write_pipe) = io::pipe().unwrap(); + + // Pass notify pipe to child + if unsafe { libc::fcntl(write_pipe.as_raw_fd(), libc::F_SETFD, 0) } == -1 { + eprintln!( + "daemon: failed to unset CLOEXEC flag for notify pipe: {}", + io::Error::last_os_error() + ); + return; + } + cmd.env("INIT_NOTIFY", format!("{}", write_pipe.as_raw_fd())); + + if let Err(err) = cmd.spawn() { + eprintln!("daemon: failed to execute {cmd:?}: {err}"); + return; + } + drop(write_pipe); + + let mut new_fd = usize::MAX; + let fd_bytes = unsafe { + core::slice::from_raw_parts_mut( + core::slice::from_mut(&mut new_fd).as_mut_ptr() as *mut u8, + core::mem::size_of::(), + ) + }; + loop { + match syscall::call_ro( + read_pipe.as_raw_fd() as usize, + fd_bytes, + syscall::CallFlags::FD | syscall::CallFlags::FD_UPPER, + &[], + ) { + Err(syscall::Error { + errno: syscall::EINTR, + }) => continue, + Ok(0) => { + eprintln!("daemon: {cmd:?} exited without notifying readiness"); + return; + } + Ok(1) => break, + Ok(n) => { + eprintln!("daemon: incorrect amount of fds {n} returned"); + return; + } + Err(err) => { + eprintln!("daemon: failed to wait for {cmd:?}: {err}"); + return; + } + } + } + + let current_namespace_fd = libredox::call::getns().expect("TODO"); + libredox::call::register_scheme_to_ns(current_namespace_fd, scheme_name, new_fd) + .expect("TODO"); + } +} diff --git a/drivers/graphics/fbbootlogd/src/main.rs b/drivers/graphics/fbbootlogd/src/main.rs index cf16e22a8d..78bf3e9cfa 100644 --- a/drivers/graphics/fbbootlogd/src/main.rs +++ b/drivers/graphics/fbbootlogd/src/main.rs @@ -7,23 +7,22 @@ //! In the future fbbootlogd may also pull from logd as opposed to have logd push logs to it. And it //! it could display a boot splash like plymouth instead of a boot log when booting in quiet mode. -use std::io::Write; use std::os::fd::AsRawFd; use event::EventQueue; use inputd::ConsumerHandleEvent; use libredox::errno::EAGAIN; use orbclient::Event; -use redox_scheme::{scheme::register_sync_scheme, RequestKind, SignalBehavior, Socket}; +use redox_scheme::{RequestKind, SignalBehavior, Socket}; use crate::scheme::FbbootlogScheme; mod scheme; fn main() { - daemon::Daemon::new(daemon); + daemon::SchemeDaemon::new(daemon); } -fn daemon(daemon: daemon::Daemon) -> ! { +fn daemon(daemon: daemon::SchemeDaemon) -> ! { let event_queue = EventQueue::new().expect("fbbootlogd: failed to create event queue"); event::user_data! { @@ -58,7 +57,7 @@ fn daemon(daemon: daemon::Daemon) -> ! { .create_this_scheme_fd(0, 0, 0, 0) .expect("fbbootlogd: failed to create log fd"); // Add ourself as log sink - let mut log_file = libredox::Fd::open( + let log_file = libredox::Fd::open( "/scheme/log/add_sink", libredox::flag::O_WRONLY | libredox::flag::O_CLOEXEC, 0, @@ -69,15 +68,12 @@ fn daemon(daemon: daemon::Daemon) -> ! { .expect("fbbootlogd: failed to send log fd to log scheme."); } - register_sync_scheme(&socket, "fbbootlog", &mut scheme) - .expect("fbbootlog: failed to register scheme to namespace"); + let _ = daemon.ready_sync_scheme(&socket, &mut scheme); // This is not possible for now as fbbootlogd needs to open new displays at runtime for graphics // driver handoff. In the future inputd may directly pass a handle to the display instead. //libredox::call::setrens(0, 0).expect("fbbootlogd: failed to enter null namespace"); - daemon.ready(); - for event in event_queue { match event.expect("fbbootlogd: failed to get event").user_data { Source::Scheme => { diff --git a/drivers/graphics/fbcond/src/main.rs b/drivers/graphics/fbcond/src/main.rs index 3d74e8b31b..fde9044242 100644 --- a/drivers/graphics/fbcond/src/main.rs +++ b/drivers/graphics/fbcond/src/main.rs @@ -3,7 +3,7 @@ use inputd::ConsumerHandleEvent; use libredox::errno::{EAGAIN, EINTR}; use orbclient::Event; use redox_scheme::{ - scheme::{register_sync_scheme, Op, SchemeResponse, SchemeSync}, + scheme::{Op, SchemeResponse, SchemeSync}, CallerCtx, RequestKind, Response, SignalBehavior, Socket, }; use std::env; @@ -16,9 +16,9 @@ mod scheme; mod text; fn main() { - daemon::Daemon::new(daemon); + daemon::SchemeDaemon::new(daemon); } -fn daemon(daemon: daemon::Daemon) -> ! { +fn daemon(daemon: daemon::SchemeDaemon) -> ! { let vt_ids = env::args() .skip(1) .map(|arg| arg.parse().expect("invalid vt number")) @@ -46,15 +46,12 @@ fn daemon(daemon: daemon::Daemon) -> ! { let mut scheme = FbconScheme::new(&vt_ids, &mut event_queue); - register_sync_scheme(&socket, "fbcon", &mut scheme) - .expect("fbcond: failed to register scheme to namespace"); + let _ = daemon.ready_sync_scheme(&socket, &mut scheme); // This is not possible for now as fbcond needs to open new displays at runtime for graphics // driver handoff. In the future inputd may directly pass a handle to the display instead. // libredox::call::setrens(0, 0).expect("fbcond: failed to enter null namespace"); - daemon.ready(); - let mut blocked = Vec::new(); // Handle all events that could have happened before registering with the event queue. @@ -139,7 +136,7 @@ fn handle_event( RequestKind::Cancellation(cancellation_request) => { if let Some(i) = blocked .iter() - .position(|(op, caller)| caller.id == cancellation_request.id) + .position(|(_op, caller)| caller.id == cancellation_request.id) { let (blocked_req, _) = blocked.remove(i); let resp = Response::err(EINTR, blocked_req); diff --git a/drivers/inputd/src/main.rs b/drivers/inputd/src/main.rs index ea3e13bbc2..4629d5e064 100644 --- a/drivers/inputd/src/main.rs +++ b/drivers/inputd/src/main.rs @@ -20,7 +20,7 @@ use std::sync::atomic::{AtomicUsize, Ordering}; use inputd::{ControlEvent, VtEvent, VtEventKind}; use libredox::errno::ESTALE; -use redox_scheme::scheme::{register_sync_scheme, SchemeSync}; +use redox_scheme::scheme::SchemeSync; use redox_scheme::{CallerCtx, OpenResult, RequestKind, Response, SignalBehavior, Socket}; use orbclient::{Event, EventOption}; @@ -552,13 +552,12 @@ impl SchemeSync for InputScheme { } } -fn deamon(deamon: daemon::Daemon) -> anyhow::Result<()> { +fn deamon(daemon: daemon::SchemeDaemon) -> anyhow::Result<()> { // Create the ":input" scheme. let socket_file = Socket::create()?; let mut scheme = InputScheme::new(); - register_sync_scheme(&socket_file, "input", &mut scheme)?; - deamon.ready(); + let _ = daemon.ready_sync_scheme(&socket_file, &mut scheme); loop { scheme.has_new_events = false; @@ -632,7 +631,7 @@ fn deamon(deamon: daemon::Daemon) -> anyhow::Result<()> { } } -fn daemon_runner(daemon: daemon::Daemon) -> ! { +fn daemon_runner(daemon: daemon::SchemeDaemon) -> ! { deamon(daemon).unwrap(); unreachable!(); } @@ -697,6 +696,6 @@ fn main() { common::file_level(), ); - daemon::Daemon::new(daemon_runner); + daemon::SchemeDaemon::new(daemon_runner); } } diff --git a/init.d/00_runtime b/init.d/00_runtime index c8fd9e1b54..58973a9c23 100644 --- a/init.d/00_runtime +++ b/init.d/00_runtime @@ -4,6 +4,6 @@ export PATH /scheme/initfs/bin export LD_LIBRARY_PATH /scheme/initfs/lib export RUST_BACKTRACE 1 rtcd -notify nulld -notify zerod -notify randd +scheme null nulld +scheme zero zerod +scheme rand randd diff --git a/init.d/10_logging b/init.d/10_logging index 63a23fc339..bc1528d194 100644 --- a/init.d/10_logging +++ b/init.d/10_logging @@ -1,3 +1,3 @@ -notify logd +scheme log logd stdio /scheme/log -notify ramfs logging +scheme logging ramfs logging diff --git a/init.d/20_graphics b/init.d/20_graphics index a286364179..414b0b4d97 100644 --- a/init.d/20_graphics +++ b/init.d/20_graphics @@ -1,8 +1,8 @@ -notify inputd +scheme input inputd notify vesad unset FRAMEBUFFER_ADDR FRAMEBUFFER_VIRT FRAMEBUFFER_WIDTH FRAMEBUFFER_HEIGHT FRAMEBUFFER_STRIDE #TODO: unset FRAMEBUFFER1 and beyond? -notify fbbootlogd +scheme fbbootlog fbbootlogd # Activate framebuffer log VT, which disables kernel graphical debug inputd -A 1 -notify fbcond 2 +scheme fbcon fbcond 2 diff --git a/init/src/main.rs b/init/src/main.rs index 3bb439aaa7..9ff4cbacb3 100644 --- a/init/src/main.rs +++ b/init/src/main.rs @@ -124,6 +124,19 @@ fn run_command(cmd: Command, config: &InitConfig) { daemon::Daemon::spawn(command); } + Command::Scheme(scheme, cmd, args) => { + if config.skip_cmd.contains(&cmd) { + eprintln!("init: skipping '{} {}'", cmd, args.join(" ")); + return; + } + + let mut command = process::Command::new(&cmd); + for arg in args { + command.arg(arg); + } + + daemon::SchemeDaemon::spawn(command, &scheme); + } Command::Regular(cmd, args) => { let mut command = process::Command::new(cmd.clone()); for arg in args { diff --git a/init/src/script.rs b/init/src/script.rs index 06ae2bf42c..50d8ae2e5f 100644 --- a/init/src/script.rs +++ b/init/src/script.rs @@ -37,6 +37,7 @@ pub enum Command { // Service Nowait(String, Vec), Notify(String, Vec), + Scheme(String, String, Vec), Regular(String, Vec), // Modify env @@ -110,6 +111,16 @@ impl Command { Ok(Command::Notify(cmd, args.collect())) } + "scheme" => { + let Some(scheme) = args.next() else { + return Err("init: failed to run scheme: no argument".to_owned()); + }; + let Some(cmd) = args.next() else { + return Err("init: failed to run scheme: missing command".to_owned()); + }; + + Ok(Command::Scheme(scheme, cmd, args.collect())) + } _ => Ok(Command::Regular(cmd, args.collect())), } } diff --git a/logd/src/main.rs b/logd/src/main.rs index 3f35617e3c..43c81e2707 100644 --- a/logd/src/main.rs +++ b/logd/src/main.rs @@ -1,25 +1,19 @@ -use redox_scheme::{ - scheme::{register_sync_scheme, SchemeSync}, - RequestKind, Response, SignalBehavior, Socket, -}; +use redox_scheme::{scheme::SchemeSync, RequestKind, Response, SignalBehavior, Socket}; use std::process; use crate::scheme::LogScheme; mod scheme; -fn daemon(daemon: daemon::Daemon) -> ! { +fn daemon(daemon: daemon::SchemeDaemon) -> ! { let socket = Socket::create().expect("logd: failed to create log scheme"); let mut scheme = LogScheme::new(&socket); - register_sync_scheme(&socket, "log", &mut scheme) - .expect("logd: failed to register scheme to namespace"); + let _ = daemon.ready_sync_scheme(&socket, &mut scheme); libredox::call::setrens(0, 0).expect("logd: failed to enter null namespace"); - daemon.ready(); - while let Some(request) = socket .next_request(SignalBehavior::Restart) .expect("logd: failed to read events from log scheme") @@ -50,5 +44,5 @@ fn daemon(daemon: daemon::Daemon) -> ! { } fn main() { - daemon::Daemon::new(daemon); + daemon::SchemeDaemon::new(daemon); } diff --git a/logd/src/scheme.rs b/logd/src/scheme.rs index 28ef562ddc..b723733f49 100644 --- a/logd/src/scheme.rs +++ b/logd/src/scheme.rs @@ -3,7 +3,6 @@ use std::fs::{File, OpenOptions}; use std::io::{Read, Write}; use std::mem; use std::os::fd::{FromRawFd, RawFd}; -use std::path::PathBuf; use std::sync::mpsc::{self, Sender}; use redox_scheme::scheme::SchemeSync; diff --git a/ramfs/src/main.rs b/ramfs/src/main.rs index b675c6f4b6..1f10421948 100644 --- a/ramfs/src/main.rs +++ b/ramfs/src/main.rs @@ -8,19 +8,18 @@ use redox_scheme::{RequestKind, SignalBehavior}; use self::scheme::Scheme; fn main() { - daemon::Daemon::new(daemon); + daemon::SchemeDaemon::new(daemon); } -fn daemon(daemon: daemon::Daemon) -> ! { +fn daemon(daemon: daemon::SchemeDaemon) -> ! { let scheme_name = env::args().nth(1).expect("Usage:\n\tramfs SCHEME_NAME"); let socket = redox_scheme::Socket::create().expect("ramfs: failed to create socket"); let mut scheme = Scheme::new(scheme_name.clone()).expect("ramfs: failed to initialize scheme"); - redox_scheme::scheme::register_sync_scheme(&socket, &scheme_name, &mut scheme) - .expect("ramfs: failed to register to namespace"); - daemon.ready(); + let _ = daemon.ready_sync_scheme(&socket, &mut scheme); + libredox::call::setrens(0, 0).expect("ramfs: failed to enter null namespace"); loop { diff --git a/randd/src/main.rs b/randd/src/main.rs index 0bc50a5173..c5fda18122 100644 --- a/randd/src/main.rs +++ b/randd/src/main.rs @@ -14,13 +14,12 @@ pub const MODE_READ: u16 = 0o4; use raw_cpuid::CpuId; use redox_scheme::{ - scheme::{register_sync_scheme, SchemeSync}, - CallerCtx, OpenResult, RequestKind, Response, SignalBehavior, Socket, + scheme::SchemeSync, CallerCtx, OpenResult, RequestKind, SignalBehavior, Socket, }; use syscall::data::Stat; use syscall::flag::{EventFlags, O_CREAT, O_EXCL, O_RDONLY, O_RDWR, O_WRONLY}; use syscall::schemev2::NewFdFlags; -use syscall::{Error, Result, EACCES, EBADF, EEXIST, ENOENT, EOPNOTSUPP, EPERM, MODE_CHR}; +use syscall::{Error, Result, EACCES, EBADF, EEXIST, ENOENT, EPERM, MODE_CHR}; // Create an RNG Seed to create initial seed from the rdrand intel instruction use rand_core::SeedableRng; @@ -465,15 +464,12 @@ impl SchemeSync for RandScheme { } } -fn daemon(daemon: daemon::Daemon) -> ! { +fn daemon(daemon: daemon::SchemeDaemon) -> ! { let socket = Socket::create().expect("randd: failed to create rand scheme"); let mut scheme = RandScheme::new(); - register_sync_scheme(&socket, "rand", &mut scheme) - .expect("randd: failed to register scheme to namespace"); - - daemon.ready(); + let _ = daemon.ready_sync_scheme(&socket, &mut scheme); libredox::call::setrens(0, 0).expect("randd: failed to enter null namespace"); @@ -497,5 +493,5 @@ fn daemon(daemon: daemon::Daemon) -> ! { } fn main() { - daemon::Daemon::new(daemon); + daemon::SchemeDaemon::new(daemon); } diff --git a/zerod/src/main.rs b/zerod/src/main.rs index 6dc8b75931..bde902d892 100644 --- a/zerod/src/main.rs +++ b/zerod/src/main.rs @@ -1,4 +1,4 @@ -use redox_scheme::{scheme::register_sync_scheme, RequestKind, SignalBehavior, Socket}; +use redox_scheme::{RequestKind, SignalBehavior, Socket}; use scheme::ZeroScheme; @@ -10,27 +10,20 @@ enum Ty { } fn main() { - daemon::Daemon::new(daemon); + daemon::SchemeDaemon::new(daemon); } -fn daemon(daemon: daemon::Daemon) -> ! { +fn daemon(daemon: daemon::SchemeDaemon) -> ! { let ty = match &*std::env::args().next().unwrap() { "nulld" => Ty::Null, "zerod" => Ty::Zero, _ => panic!("needs to be called as either nulld or zerod"), }; - let name = match ty { - Ty::Null => "null", - Ty::Zero => "zero", - }; let socket = Socket::create().expect("zerod: failed to create zero scheme"); let mut zero_scheme = ZeroScheme(ty); - register_sync_scheme(&socket, name, &mut zero_scheme) - .expect("zerod: failed to register scheme to namespace"); - - daemon.ready(); + let _ = daemon.ready_sync_scheme(&socket, &mut zero_scheme); libredox::call::setrens(0, 0).expect("zerod: failed to enter null namespace");