Let init handle daemonization
This way init can track the lifecycle of services in the future. It may also help with parallel booting in the future.
This commit is contained in:
Generated
+2
@@ -1109,6 +1109,7 @@ dependencies = [
|
||||
name = "init"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"daemon",
|
||||
"libredox",
|
||||
]
|
||||
|
||||
@@ -1490,6 +1491,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"common",
|
||||
"daemon",
|
||||
"log",
|
||||
"pcid",
|
||||
"pico-args",
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
hwd
|
||||
notify hwd
|
||||
pcid-spawner /etc/pcid/initfs.toml
|
||||
|
||||
@@ -1 +1 @@
|
||||
bcm2835-sdhcid
|
||||
notify bcm2835-sdhcid
|
||||
|
||||
+41
-28
@@ -1,46 +1,59 @@
|
||||
#![feature(never_type)]
|
||||
|
||||
use std::io::{self, PipeWriter, Read, Write};
|
||||
use std::os::fd::{AsRawFd, FromRawFd};
|
||||
use std::process::Command;
|
||||
|
||||
#[must_use = "Daemon::ready must be called"]
|
||||
pub struct Daemon {
|
||||
write_pipe: PipeWriter,
|
||||
}
|
||||
|
||||
fn errno() -> io::Error {
|
||||
io::Error::last_os_error()
|
||||
}
|
||||
|
||||
impl Daemon {
|
||||
pub fn new<F: FnOnce(Daemon) -> !>(f: F) -> ! {
|
||||
let (mut read_pipe, write_pipe) = std::io::pipe().unwrap();
|
||||
let write_pipe = unsafe {
|
||||
io::PipeWriter::from_raw_fd(std::env::var("INIT_NOTIFY").unwrap().parse().unwrap())
|
||||
};
|
||||
|
||||
match unsafe { libc::fork() } {
|
||||
0 => {
|
||||
drop(read_pipe);
|
||||
|
||||
f(Daemon { write_pipe })
|
||||
}
|
||||
-1 => return Err(errno()).unwrap(),
|
||||
_pid => {
|
||||
drop(write_pipe);
|
||||
|
||||
let mut data = [0];
|
||||
|
||||
match read_pipe.read_exact(&mut data) {
|
||||
Ok(()) => {}
|
||||
Err(err) if err.kind() == io::ErrorKind::UnexpectedEof => {
|
||||
unsafe { libc::_exit(101) };
|
||||
}
|
||||
Err(err) => return Err(err).unwrap(),
|
||||
};
|
||||
|
||||
unsafe { libc::_exit(data[0].into()) };
|
||||
}
|
||||
}
|
||||
f(Daemon { write_pipe })
|
||||
}
|
||||
|
||||
pub fn ready(mut self) {
|
||||
self.write_pipe.write_all(&[0]).unwrap();
|
||||
}
|
||||
|
||||
pub fn spawn(mut cmd: Command) {
|
||||
let (mut read_pipe, write_pipe) = io::pipe().unwrap();
|
||||
|
||||
// Pass 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: {}",
|
||||
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 data = [0];
|
||||
match read_pipe.read_exact(&mut data) {
|
||||
Ok(()) => {
|
||||
if data[0] != 0 {
|
||||
eprintln!("daemon: {cmd:?} failed with {}", data[0]);
|
||||
}
|
||||
}
|
||||
Err(err) if err.kind() == io::ErrorKind::UnexpectedEof => {
|
||||
eprintln!("daemon: {cmd:?} exited without notifying readiness");
|
||||
}
|
||||
Err(err) => {
|
||||
eprintln!("daemon: failed to wait for {cmd:?}: {err}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ impl Backend for AcpiBackend {
|
||||
|
||||
// Spawn acpid
|
||||
//TODO: pass rxsdt data to acpid?
|
||||
Command::new("acpid").spawn()?.wait()?;
|
||||
daemon::Daemon::spawn(Command::new("acpid"));
|
||||
|
||||
Ok(Self { rxsdt })
|
||||
}
|
||||
|
||||
+1
-15
@@ -37,21 +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
|
||||
match process::Command::new("pcid").spawn() {
|
||||
Ok(mut child) => match child.wait() {
|
||||
Ok(status) => {
|
||||
if !status.success() {
|
||||
log::error!("pcid exited with status {}", status);
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
log::error!("failed to wait for pcid: {}", err);
|
||||
}
|
||||
},
|
||||
Err(err) => {
|
||||
log::error!("failed to spawn pcid: {}", err);
|
||||
}
|
||||
}
|
||||
daemon::Daemon::spawn(process::Command::new("pcid"));
|
||||
|
||||
daemon.ready();
|
||||
|
||||
|
||||
@@ -14,4 +14,5 @@ serde = { version = "1", features = ["derive"] }
|
||||
toml = "0.5"
|
||||
|
||||
common = { path = "../common" }
|
||||
daemon = { path = "../../daemon" }
|
||||
pcid = { path = "../pcid" }
|
||||
|
||||
@@ -88,13 +88,7 @@ fn main() -> Result<()> {
|
||||
let channel_fd = handle.into_inner_fd();
|
||||
command.env("PCID_CLIENT_CHANNEL", channel_fd.to_string());
|
||||
|
||||
match command.status() {
|
||||
Ok(status) if !status.success() => {
|
||||
log::error!("pcid-spawner: driver {command:?} failed with {status}")
|
||||
}
|
||||
Ok(_) => {}
|
||||
Err(err) => log::error!("pcid-spawner: failed to execute {command:?}: {err}"),
|
||||
}
|
||||
daemon::Daemon::spawn(command);
|
||||
syscall::close(channel_fd as usize).unwrap();
|
||||
}
|
||||
|
||||
|
||||
@@ -3,26 +3,26 @@
|
||||
export PATH /scheme/initfs/bin
|
||||
export RUST_BACKTRACE 1
|
||||
rtcd
|
||||
nulld
|
||||
zerod
|
||||
randd
|
||||
notify nulld
|
||||
notify zerod
|
||||
notify randd
|
||||
|
||||
# Logging
|
||||
logd
|
||||
notify logd
|
||||
stdio /scheme/log
|
||||
ramfs logging
|
||||
notify ramfs logging
|
||||
|
||||
# Graphics infrastructure
|
||||
inputd
|
||||
vesad
|
||||
notify inputd
|
||||
notify vesad
|
||||
unset FRAMEBUFFER_ADDR FRAMEBUFFER_VIRT FRAMEBUFFER_WIDTH FRAMEBUFFER_HEIGHT FRAMEBUFFER_STRIDE
|
||||
#TODO: unset FRAMEBUFFER1 and beyond?
|
||||
fbbootlogd
|
||||
fbcond 2
|
||||
notify fbbootlogd
|
||||
notify fbcond 2
|
||||
|
||||
# Live disk
|
||||
# Note: Needs to start before drivers to ensure it gets priority when redoxfs searches for disks
|
||||
lived
|
||||
notify lived
|
||||
|
||||
# Drivers
|
||||
run /scheme/initfs/etc/init_drivers.rc
|
||||
|
||||
@@ -6,3 +6,5 @@ license = "MIT"
|
||||
|
||||
[dependencies]
|
||||
libredox.workspace = true
|
||||
|
||||
daemon = { path = "../daemon" }
|
||||
|
||||
+29
-7
@@ -20,12 +20,12 @@ fn switch_stdio(stdio: &str) -> Result<()> {
|
||||
}
|
||||
|
||||
struct InitConfig {
|
||||
pub log_debug: bool,
|
||||
pub skip_cmd: Vec<String>,
|
||||
log_debug: bool,
|
||||
skip_cmd: Vec<String>,
|
||||
}
|
||||
|
||||
impl InitConfig {
|
||||
pub fn new() -> Self {
|
||||
fn new() -> Self {
|
||||
let log_level = env::var("INIT_LOG_LEVEL").unwrap_or("INFO".into());
|
||||
let log_debug = matches!(log_level.as_str(), "DEBUG" | "TRACE");
|
||||
let skip_cmd: Vec<String> = match env::var("INIT_SKIP") {
|
||||
@@ -40,7 +40,7 @@ impl InitConfig {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(file: &Path, config: &InitConfig) -> Result<()> {
|
||||
fn run(file: &Path, config: &InitConfig) -> Result<()> {
|
||||
for line_raw in fs::read_to_string(file)?.lines() {
|
||||
let line = line_raw.trim();
|
||||
if line.is_empty() || line.starts_with('#') {
|
||||
@@ -167,6 +167,11 @@ fn run_command(line: &str, config: &InitConfig) {
|
||||
eprintln!("init: failed to run nowait: no argument");
|
||||
return;
|
||||
};
|
||||
if config.skip_cmd.contains(&cmd) {
|
||||
eprintln!("init: skipping '{}'", line);
|
||||
return;
|
||||
}
|
||||
|
||||
let mut command = Command::new(cmd);
|
||||
|
||||
for arg in args {
|
||||
@@ -178,17 +183,34 @@ fn run_command(line: &str, config: &InitConfig) {
|
||||
Err(err) => eprintln!("init: failed to execute '{}': {}", line, err),
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let mut command = Command::new(cmd.clone());
|
||||
"notify" => {
|
||||
let Some(cmd) = args.next() else {
|
||||
eprintln!("init: failed to run nowait: no argument");
|
||||
return;
|
||||
};
|
||||
if config.skip_cmd.contains(&cmd) {
|
||||
eprintln!("init: skipping '{}'", line);
|
||||
return;
|
||||
}
|
||||
|
||||
let mut command = Command::new(&cmd);
|
||||
for arg in args {
|
||||
command.arg(arg);
|
||||
}
|
||||
|
||||
daemon::Daemon::spawn(command);
|
||||
}
|
||||
_ => {
|
||||
if config.skip_cmd.contains(&cmd) {
|
||||
eprintln!("init: skipping '{}'", line);
|
||||
return;
|
||||
}
|
||||
|
||||
let mut command = Command::new(cmd.clone());
|
||||
for arg in args {
|
||||
command.arg(arg);
|
||||
}
|
||||
|
||||
let mut child = match command.spawn() {
|
||||
Ok(child) => child,
|
||||
Err(err) => {
|
||||
@@ -211,7 +233,7 @@ fn run_command(line: &str, config: &InitConfig) {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
fn main() {
|
||||
let init_path = Path::new("/scheme/initfs/etc/init.rc");
|
||||
let init_config = InitConfig::new();
|
||||
if let Err(err) = run(&init_path, &init_config) {
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
ps2d us
|
||||
hwd
|
||||
notify ps2d us
|
||||
notify hwd
|
||||
pcid-spawner /scheme/initfs/etc/pcid/initfs.toml
|
||||
|
||||
Reference in New Issue
Block a user