Merge branch 'init_rework9' into 'main'

Some preparations for parallel service spawning

See merge request redox-os/base!218
This commit is contained in:
Jeremy Soller
2026-04-10 18:54:17 -06:00
28 changed files with 97 additions and 75 deletions
Generated
+3 -1
View File
@@ -1085,8 +1085,10 @@ name = "init"
version = "0.1.0"
dependencies = [
"config",
"daemon",
"libc",
"libredox",
"plain",
"redox_syscall 0.7.3",
"serde",
"serde_json",
"toml",
+2 -49
View File
@@ -55,6 +55,8 @@ impl Daemon {
}
/// Executes `Command` as a child process.
// FIXME remove once the service spawning of hwd and pcid-spawner is moved to init
#[deprecated]
pub fn spawn(mut cmd: Command) {
let (mut read_pipe, write_pipe) = io::pipe().unwrap();
@@ -124,53 +126,4 @@ impl SchemeDaemon {
let cap_fd = socket.create_this_scheme_fd(0, cap_id, 0, 0)?;
self.ready_with_fd(Fd::new(cap_fd))
}
/// Executes `Command` as a child process registering it to the scheme namespace.
pub fn spawn(mut cmd: Command, scheme_name: &str) {
let (read_pipe, write_pipe) = io::pipe().unwrap();
unsafe { pass_fd(&mut cmd, "INIT_NOTIFY", write_pipe.into()) };
if let Err(err) = cmd.spawn() {
eprintln!("daemon: failed to execute {cmd:?}: {err}");
return;
}
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::<usize>(),
)
};
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");
}
}
+1
View File
@@ -13,6 +13,7 @@ impl Backend for AcpiBackend {
// Spawn acpid
//TODO: pass rxsdt data to acpid?
#[allow(deprecated, reason = "we can't yet move this to init")]
daemon::Daemon::spawn(Command::new("acpid"));
Ok(Self { rxsdt })
+1
View File
@@ -37,6 +37,7 @@ fn daemon(daemon: daemon::Daemon) -> ! {
//TODO: launch pcid based on backend information?
// Must launch after acpid but before probe calls /scheme/acpi/symbols
#[allow(deprecated, reason = "we can't yet move this to init")]
daemon::Daemon::spawn(process::Command::new("pcid"));
daemon.ready();
+1
View File
@@ -92,6 +92,7 @@ fn main() -> Result<()> {
let channel_fd = handle.into_inner_fd();
command.env("PCID_CLIENT_CHANNEL", channel_fd.to_string());
#[allow(deprecated, reason = "we can't yet move this to init")]
daemon::Daemon::spawn(command);
syscall::close(channel_fd as usize).unwrap();
}
+7
View File
@@ -0,0 +1,7 @@
[unit]
description = "Logger"
default_dependencies = false
[service]
cmd = "logd"
type = { scheme = "log" }
@@ -2,6 +2,7 @@
description = "Services that relibc needs to function"
default_dependencies = false
requires_weak = [
"00_logd.service",
"00_nulld.service",
"00_randd.service",
"00_rtcd.service",
+3 -1
View File
@@ -6,13 +6,15 @@ edition = "2024"
license = "MIT"
[dependencies]
libc.workspace = true
libredox.workspace = true
plain.workspace = true
redox_syscall.workspace = true
serde.workspace = true
serde_json.workspace = true
toml.workspace = true
config = { path = "../config" }
daemon = { path = "../daemon" }
[lints]
workspace = true
+8 -8
View File
@@ -137,20 +137,20 @@ fn main() {
Path::new("/scheme/initfs/etc"),
);
// Start logd first such that we can pass /scheme/log as stdio to all other services
scheduler
.schedule_start_and_report_errors(&mut unit_store, UnitId("00_logd.service".to_owned()));
scheduler.step(&mut unit_store, &mut init_config);
if let Err(err) = switch_stdio("/scheme/log") {
eprintln!("init: failed to switch stdio to '/scheme/log': {err}");
}
let runtime_target = UnitId("00_runtime.target".to_owned());
scheduler.schedule_start_and_report_errors(&mut unit_store, runtime_target.clone());
unit_store.set_runtime_target(runtime_target);
scheduler
.schedule_start_and_report_errors(&mut unit_store, UnitId("90_initfs.target".to_owned()));
let mut command = std::process::Command::new("logd");
command.env_clear().envs(&init_config.envs);
daemon::SchemeDaemon::spawn(command, "log");
if let Err(err) = switch_stdio("/scheme/log") {
eprintln!("init: failed to switch stdio to '/scheme/log': {err}");
}
scheduler.step(&mut unit_store, &mut init_config);
switch_root(
+70 -16
View File
@@ -1,7 +1,10 @@
use std::collections::{BTreeMap, BTreeSet};
use std::env;
use std::ffi::OsString;
use std::io::Read;
use std::os::fd::{AsRawFd, OwnedFd};
use std::os::unix::process::CommandExt;
use std::process::Command;
use std::{env, io};
use serde::Deserialize;
@@ -43,21 +46,61 @@ impl Service {
}
command.envs(base_envs).envs(&self.envs);
match &self.type_ {
ServiceType::Notify => {
daemon::Daemon::spawn(command);
let (mut read_pipe, write_pipe) = io::pipe().unwrap();
unsafe { pass_fd(&mut command, "INIT_NOTIFY", write_pipe.into()) };
let mut child = match command.spawn() {
Ok(child) => child,
Err(err) => {
eprintln!("init: failed to execute {:?}: {}", command, err);
return;
}
};
match &self.type_ {
ServiceType::Notify => match read_pipe.read_exact(&mut [0]) {
Ok(()) => {}
Err(err) if err.kind() == io::ErrorKind::UnexpectedEof => {
eprintln!("init: {command:?} exited without notifying readiness");
}
Err(err) => {
eprintln!("init: failed to wait for {command:?}: {err}");
}
},
ServiceType::Scheme(scheme) => {
daemon::SchemeDaemon::spawn(command, &scheme);
let mut new_fd = usize::MAX;
loop {
match syscall::call_ro(
read_pipe.as_raw_fd() as usize,
unsafe { plain::as_mut_bytes(&mut new_fd) },
syscall::CallFlags::FD | syscall::CallFlags::FD_UPPER,
&[],
) {
Err(syscall::Error {
errno: syscall::EINTR,
}) => continue,
Ok(0) => {
eprintln!("init: {command:?} exited without notifying readiness");
return;
}
Ok(1) => break,
Ok(n) => {
eprintln!("init: incorrect amount of fds {n} returned");
return;
}
Err(err) => {
eprintln!("init: failed to wait for {command:?}: {err}");
return;
}
}
}
let current_namespace_fd = libredox::call::getns().expect("TODO");
libredox::call::register_scheme_to_ns(current_namespace_fd, scheme, new_fd)
.expect("TODO");
}
ServiceType::Oneshot => {
let mut child = match command.spawn() {
Ok(child) => child,
Err(err) => {
eprintln!("init: failed to execute {:?}: {}", command, err);
return;
}
};
drop(read_pipe);
match child.wait() {
Ok(exit_status) => {
if !exit_status.success() {
@@ -69,10 +112,21 @@ impl Service {
}
}
}
ServiceType::OneshotAsync => match command.spawn() {
Ok(_child) => {}
Err(err) => eprintln!("init: failed to execute '{:?}': {}", command, err),
},
ServiceType::OneshotAsync => {}
}
}
}
unsafe fn pass_fd(cmd: &mut Command, env: &str, fd: OwnedFd) {
cmd.env(env, format!("{}", fd.as_raw_fd()));
unsafe {
cmd.pre_exec(move || {
// Pass notify pipe to child
if libc::fcntl(fd.as_raw_fd(), libc::F_SETFD, 0) == -1 {
Err(io::Error::last_os_error())
} else {
Ok(())
}
});
}
}