chore: commit durable overlay state (configs, patches, recipe symlinks)
Pre-existing work from other sessions committed as durable state: - local/config/drivers.d/ (8 driver configs) - local/config/firmware-fallbacks.d/ (3 firmware configs) - local/patches/base/, kernel/, relibc/ (new patch carriers) - recipes/system/ symlinks (driver-params, acmd, ecmd, usbaudiod) pkgar build artifacts and cache intentionally excluded.
This commit is contained in:
@@ -1,20 +1,23 @@
|
||||
# P2-daemon-ready-graceful.patch
|
||||
#
|
||||
# Replace unwrap() in Daemon::ready() with graceful error handling.
|
||||
# When hwd spawns pcid fire-and-forget (dropping the pipe's read end
|
||||
# before pcid signals readiness), the unwrap() causes a BrokenPipe panic
|
||||
# that kills pcid and cascades into total boot failure.
|
||||
# When hwd or pcid-spawner spawns a daemon fire-and-forget (dropping the
|
||||
# pipe's read end before the child signals readiness), the unwrap() causes
|
||||
# a BrokenPipe panic that kills the child and cascades into boot failures.
|
||||
#
|
||||
# Also adds the log crate to daemon's dependencies for the debug message.
|
||||
#
|
||||
diff --git a/daemon/Cargo.toml b/daemon/Cargo.toml
|
||||
--- a/daemon/Cargo.toml
|
||||
+++ b/daemon/Cargo.toml
|
||||
@@ -7,6 +7,7 @@ edition = "2024"
|
||||
[dependencies]
|
||||
libc.workspace = true
|
||||
libredox.workspace = true
|
||||
+log.workspace = true
|
||||
redox-scheme.workspace = true
|
||||
redox_syscall.workspace = true
|
||||
# The write may fail because init already closed the read end (service
|
||||
# timeout, duplicate start, or oneshot completion). In all cases the
|
||||
# daemon should continue running rather than panic.
|
||||
|
||||
diff --git a/daemon/src/lib.rs b/daemon/src/lib.rs
|
||||
--- a/daemon/src/lib.rs
|
||||
+++ b/daemon/src/lib.rs
|
||||
@@ -51,7 +51,7 @@ impl Daemon {
|
||||
|
||||
/// Notify the process that the daemon is ready to accept requests.
|
||||
pub fn ready(mut self) {
|
||||
- self.write_pipe.write_all(&[0]).unwrap();
|
||||
+ let _ = self.write_pipe.write_all(&[0]);
|
||||
}
|
||||
|
||||
/// Executes `Command` as a child process.
|
||||
|
||||
@@ -0,0 +1,479 @@
|
||||
diff --git a/drivers/pcid/src/scheme.rs b/drivers/pcid/src/scheme.rs
|
||||
index bb9f39a3..b6f8711e 100644
|
||||
--- a/drivers/pcid/src/scheme.rs
|
||||
+++ b/drivers/pcid/src/scheme.rs
|
||||
@@ -1,28 +1,100 @@
|
||||
-use std::collections::{BTreeMap, VecDeque};
|
||||
+use std::collections::{BTreeMap, HashMap, VecDeque};
|
||||
+use std::fmt::Write;
|
||||
|
||||
use pci_types::{ConfigRegionAccess, PciAddress};
|
||||
use redox_scheme::scheme::SchemeSync;
|
||||
use redox_scheme::{CallerCtx, OpenResult};
|
||||
use scheme_utils::HandleMap;
|
||||
use syscall::dirent::{DirEntry, DirentBuf, DirentKind};
|
||||
-use syscall::error::{Error, Result, EACCES, EBADF, EINVAL, EIO, EISDIR, ENOENT, ENOTDIR};
|
||||
+use syscall::error::{
|
||||
+ Error, Result, EACCES, EALREADY, EBADF, EINVAL, EIO, EISDIR, ENOENT, ENOTDIR, EROFS,
|
||||
+};
|
||||
use syscall::flag::{MODE_CHR, MODE_DIR, O_DIRECTORY, O_STAT};
|
||||
use syscall::schemev2::NewFdFlags;
|
||||
use syscall::ENOLCK;
|
||||
|
||||
use crate::cfg_access::Pcie;
|
||||
|
||||
+const PCIE_EXTENDED_CAPABILITY_AER: u16 = 0x0001;
|
||||
+
|
||||
+#[derive(Clone, Copy)]
|
||||
+enum AerRegisterName {
|
||||
+ UncorStatus,
|
||||
+ UncorMask,
|
||||
+ UncorSeverity,
|
||||
+ CorStatus,
|
||||
+ CorMask,
|
||||
+ Cap,
|
||||
+ HeaderLog,
|
||||
+}
|
||||
+
|
||||
+impl AerRegisterName {
|
||||
+ fn from_path(path: &str) -> Option<Self> {
|
||||
+ Some(match path {
|
||||
+ "uncor_status" => Self::UncorStatus,
|
||||
+ "uncor_mask" => Self::UncorMask,
|
||||
+ "uncor_severity" => Self::UncorSeverity,
|
||||
+ "cor_status" => Self::CorStatus,
|
||||
+ "cor_mask" => Self::CorMask,
|
||||
+ "cap" => Self::Cap,
|
||||
+ "header_log" => Self::HeaderLog,
|
||||
+ _ => return None,
|
||||
+ })
|
||||
+ }
|
||||
+
|
||||
+ const fn offset(self) -> u16 {
|
||||
+ match self {
|
||||
+ Self::UncorStatus => 0x00,
|
||||
+ Self::UncorMask => 0x04,
|
||||
+ Self::UncorSeverity => 0x08,
|
||||
+ Self::CorStatus => 0x0C,
|
||||
+ Self::CorMask => 0x10,
|
||||
+ Self::Cap => 0x14,
|
||||
+ Self::HeaderLog => 0x18,
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ const fn len(self) -> usize {
|
||||
+ match self {
|
||||
+ Self::HeaderLog => 16,
|
||||
+ _ => 4,
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
pub struct PciScheme {
|
||||
handles: HandleMap<HandleWrapper>,
|
||||
pub pcie: Pcie,
|
||||
pub tree: BTreeMap<PciAddress, crate::Func>,
|
||||
+ /// Maps device address string (e.g. "0000:00:14.0") to owning PID
|
||||
+ binds: HashMap<String, u32>,
|
||||
}
|
||||
enum Handle {
|
||||
- TopLevel { entries: Vec<String> },
|
||||
+ TopLevel {
|
||||
+ entries: Vec<String>,
|
||||
+ },
|
||||
Access,
|
||||
- Device,
|
||||
- Channel { addr: PciAddress, st: ChannelState },
|
||||
+ Device {
|
||||
+ addr: PciAddress,
|
||||
+ },
|
||||
+ Channel {
|
||||
+ addr: PciAddress,
|
||||
+ st: ChannelState,
|
||||
+ },
|
||||
SchemeRoot,
|
||||
+ /// Represents an open handle to a device's bind endpoint
|
||||
+ Bind {
|
||||
+ addr: PciAddress,
|
||||
+ },
|
||||
+ AerDir,
|
||||
+ Aer {
|
||||
+ addr: PciAddress,
|
||||
+ register: AerRegisterName,
|
||||
+ },
|
||||
+ /// Uevent surface for hotplug consumers. Opening uevent returns an object
|
||||
+ /// from which device add/remove events can be read. Since pcid currently
|
||||
+ /// only scans at startup, this surface is ready for hotplug polling consumers.
|
||||
+ Uevent,
|
||||
}
|
||||
struct HandleWrapper {
|
||||
inner: Handle,
|
||||
@@ -30,14 +102,23 @@ struct HandleWrapper {
|
||||
}
|
||||
impl Handle {
|
||||
fn is_file(&self) -> bool {
|
||||
- matches!(self, Self::Access | Self::Channel { .. })
|
||||
+ matches!(
|
||||
+ self,
|
||||
+ Self::Access
|
||||
+ | Self::Channel { .. }
|
||||
+ | Self::Bind { .. }
|
||||
+ | Self::Aer { .. }
|
||||
+ | Self::Uevent
|
||||
+ )
|
||||
}
|
||||
fn is_dir(&self) -> bool {
|
||||
!self.is_file()
|
||||
}
|
||||
- // TODO: capability rather than root
|
||||
fn requires_root(&self) -> bool {
|
||||
- matches!(self, Self::Access | Self::Channel { .. })
|
||||
+ matches!(
|
||||
+ self,
|
||||
+ Self::Access | Self::Channel { .. } | Self::Bind { .. }
|
||||
+ )
|
||||
}
|
||||
fn is_scheme_root(&self) -> bool {
|
||||
matches!(self, Self::SchemeRoot)
|
||||
@@ -49,7 +130,17 @@ enum ChannelState {
|
||||
AwaitingResponseRead(VecDeque<u8>),
|
||||
}
|
||||
|
||||
-const DEVICE_CONTENTS: &[&str] = &["channel"];
|
||||
+const DEVICE_CONTENTS: &[&str] = &["channel", "bind"];
|
||||
+const DEVICE_AER_CONTENTS: &[&str] = &["channel", "bind", "aer"];
|
||||
+const AER_CONTENTS: &[&str] = &[
|
||||
+ "uncor_status",
|
||||
+ "uncor_mask",
|
||||
+ "uncor_severity",
|
||||
+ "cor_status",
|
||||
+ "cor_mask",
|
||||
+ "cap",
|
||||
+ "header_log",
|
||||
+];
|
||||
|
||||
impl PciScheme {
|
||||
pub fn access(&mut self) -> usize {
|
||||
@@ -88,22 +179,25 @@ impl SchemeSync for PciScheme {
|
||||
let path = path.trim_matches('/');
|
||||
|
||||
let handle = if path.is_empty() {
|
||||
- Handle::TopLevel {
|
||||
- entries: self
|
||||
- .tree
|
||||
- .iter()
|
||||
- // FIXME remove replacement of : once the old scheme format is no longer supported.
|
||||
- .map(|(addr, _)| format!("{}", addr).replace(':', "--"))
|
||||
- .collect::<Vec<_>>(),
|
||||
- }
|
||||
+ let mut entries: Vec<String> = self
|
||||
+ .tree
|
||||
+ .iter()
|
||||
+ // FIXME remove replacement of : once the old scheme format is no longer supported.
|
||||
+ .map(|(addr, _)| format!("{}", addr).replace(':', "--"))
|
||||
+ .collect();
|
||||
+ entries.push(String::from("uevent"));
|
||||
+ entries.push(String::from("access"));
|
||||
+ Handle::TopLevel { entries }
|
||||
} else if path == "access" {
|
||||
Handle::Access
|
||||
+ } else if path == "uevent" {
|
||||
+ Handle::Uevent
|
||||
} else {
|
||||
let idx = path.find('/').unwrap_or(path.len());
|
||||
let (addr_str, after) = path.split_at(idx);
|
||||
let addr = parse_pci_addr(addr_str).ok_or(Error::new(ENOENT))?;
|
||||
|
||||
- self.parse_after_pci_addr(addr, after)?
|
||||
+ self.parse_after_pci_addr(addr, after, ctx)?
|
||||
};
|
||||
|
||||
let stat = flags & O_STAT != 0;
|
||||
@@ -131,8 +225,14 @@ impl SchemeSync for PciScheme {
|
||||
|
||||
let (len, mode) = match handle.inner {
|
||||
Handle::TopLevel { ref entries } => (entries.len(), MODE_DIR | 0o755),
|
||||
- Handle::Device => (DEVICE_CONTENTS.len(), MODE_DIR | 0o755),
|
||||
- Handle::Access | Handle::Channel { .. } => (0, MODE_CHR | 0o600),
|
||||
+ Handle::Device { addr } => (
|
||||
+ Self::device_entries(&self.pcie, addr).len(),
|
||||
+ MODE_DIR | 0o755,
|
||||
+ ),
|
||||
+ Handle::AerDir => (AER_CONTENTS.len(), MODE_DIR | 0o755),
|
||||
+ Handle::Aer { register, .. } => (register.len(), MODE_CHR | 0o444),
|
||||
+ Handle::Access | Handle::Channel { .. } | Handle::Bind { .. } => (0, MODE_CHR | 0o600),
|
||||
+ Handle::Uevent => (0, MODE_CHR | 0o644),
|
||||
Handle::SchemeRoot => return Err(Error::new(EBADF)),
|
||||
};
|
||||
stat.st_size = len as u64;
|
||||
@@ -143,7 +243,7 @@ impl SchemeSync for PciScheme {
|
||||
&mut self,
|
||||
id: usize,
|
||||
buf: &mut [u8],
|
||||
- _offset: u64,
|
||||
+ offset: u64,
|
||||
_fcntl_flags: u32,
|
||||
_ctx: &CallerCtx,
|
||||
) -> Result<usize> {
|
||||
@@ -155,12 +255,45 @@ impl SchemeSync for PciScheme {
|
||||
|
||||
match handle.inner {
|
||||
Handle::TopLevel { .. } => Err(Error::new(EISDIR)),
|
||||
- Handle::Device => Err(Error::new(EISDIR)),
|
||||
+ Handle::Device { .. } | Handle::AerDir => Err(Error::new(EISDIR)),
|
||||
Handle::Channel {
|
||||
addr: _,
|
||||
ref mut st,
|
||||
} => Self::read_channel(st, buf),
|
||||
- Handle::SchemeRoot => Err(Error::new(EBADF)),
|
||||
+ Handle::Aer { addr, register } => {
|
||||
+ Self::read_aer_register(&self.pcie, addr, register, buf, offset)
|
||||
+ }
|
||||
+ Handle::Uevent => {
|
||||
+ // Uevent surface for hotplug polling consumers.
|
||||
+ // pcid currently only scans at startup, so return the current
|
||||
+ // device tree as "add" events. Consumers can poll and re-read
|
||||
+ // to check for new events.
|
||||
+ let mut o = String::new();
|
||||
+ for (a, f) in &self.tree {
|
||||
+ let _ = write!(
|
||||
+ o,
|
||||
+ "add device {:02x}:{:02x}.{:x}.{:x} vendor=0x{:04x} device=0x{:04x} class=0x{:02x}.{:02x}\n",
|
||||
+ a.segment(),
|
||||
+ a.bus(),
|
||||
+ a.device(),
|
||||
+ a.function(),
|
||||
+ f.inner.full_device_id.vendor_id,
|
||||
+ f.inner.full_device_id.device_id,
|
||||
+ f.inner.full_device_id.class,
|
||||
+ f.inner.full_device_id.subclass
|
||||
+ );
|
||||
+ }
|
||||
+ let b = o.as_bytes();
|
||||
+ let s = offset as usize;
|
||||
+ if s < b.len() {
|
||||
+ let n = (b.len() - s).min(buf.len());
|
||||
+ buf[..n].copy_from_slice(&b[s..s + n]);
|
||||
+ Ok(n)
|
||||
+ } else {
|
||||
+ Ok(0)
|
||||
+ }
|
||||
+ }
|
||||
+ Handle::SchemeRoot | Handle::Bind { .. } => Err(Error::new(EBADF)),
|
||||
_ => Err(Error::new(EBADF)),
|
||||
}
|
||||
}
|
||||
@@ -192,8 +325,15 @@ impl SchemeSync for PciScheme {
|
||||
}
|
||||
return Ok(buf);
|
||||
}
|
||||
- Handle::Device => DEVICE_CONTENTS,
|
||||
- Handle::Access | Handle::Channel { .. } => return Err(Error::new(ENOTDIR)),
|
||||
+ Handle::Device { addr } => Self::device_entries(&self.pcie, addr),
|
||||
+ Handle::AerDir => AER_CONTENTS,
|
||||
+ Handle::Access
|
||||
+ | Handle::Channel { .. }
|
||||
+ | Handle::Bind { .. }
|
||||
+ | Handle::Aer { .. }
|
||||
+ | Handle::Uevent => {
|
||||
+ return Err(Error::new(ENOTDIR));
|
||||
+ }
|
||||
Handle::SchemeRoot => return Err(Error::new(EBADF)),
|
||||
};
|
||||
|
||||
@@ -226,6 +366,7 @@ impl SchemeSync for PciScheme {
|
||||
Handle::Channel { addr, ref mut st } => {
|
||||
Self::write_channel(&self.pcie, &mut self.tree, addr, st, buf)
|
||||
}
|
||||
+ Handle::Aer { .. } => Err(Error::new(EROFS)),
|
||||
|
||||
_ => Err(Error::new(EBADF)),
|
||||
}
|
||||
@@ -316,6 +457,16 @@ impl SchemeSync for PciScheme {
|
||||
func.enabled = false;
|
||||
}
|
||||
}
|
||||
+ Some(HandleWrapper {
|
||||
+ inner: Handle::Bind { addr },
|
||||
+ ..
|
||||
+ }) => {
|
||||
+ let addr_str = format!("{}", addr);
|
||||
+ if let Some(&owner_pid) = self.binds.get(&addr_str) {
|
||||
+ log::info!("pcid: device {} unbound by pid {}", addr_str, owner_pid);
|
||||
+ }
|
||||
+ self.binds.remove(&addr_str);
|
||||
+ }
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@@ -327,36 +478,154 @@ impl PciScheme {
|
||||
handles: HandleMap::new(),
|
||||
pcie,
|
||||
tree: BTreeMap::new(),
|
||||
+ binds: HashMap::new(),
|
||||
+ }
|
||||
+ }
|
||||
+ fn device_entries(pcie: &Pcie, addr: PciAddress) -> &'static [&'static str] {
|
||||
+ if Self::find_pcie_extended_capability(pcie, addr, PCIE_EXTENDED_CAPABILITY_AER).is_some() {
|
||||
+ DEVICE_AER_CONTENTS
|
||||
+ } else {
|
||||
+ DEVICE_CONTENTS
|
||||
}
|
||||
}
|
||||
- fn parse_after_pci_addr(&mut self, addr: PciAddress, after: &str) -> Result<Handle> {
|
||||
+ fn find_pcie_extended_capability(
|
||||
+ pcie: &Pcie,
|
||||
+ addr: PciAddress,
|
||||
+ capability_id: u16,
|
||||
+ ) -> Option<u16> {
|
||||
+ if !pcie.has_extended_config(addr) {
|
||||
+ return None;
|
||||
+ }
|
||||
+
|
||||
+ let mut offset = 0x100_u16;
|
||||
+
|
||||
+ while offset <= 0xFFC {
|
||||
+ let header = unsafe { pcie.read(addr, offset) };
|
||||
+ if header == 0 || header == u32::MAX {
|
||||
+ return None;
|
||||
+ }
|
||||
+
|
||||
+ if (header & 0xFFFF) as u16 == capability_id {
|
||||
+ return Some(offset);
|
||||
+ }
|
||||
+
|
||||
+ let next = ((header >> 20) & 0xFFF) as u16;
|
||||
+ if next < 0x100 || next <= offset || next > 0xFFC || next % 4 != 0 {
|
||||
+ return None;
|
||||
+ }
|
||||
+ offset = next;
|
||||
+ }
|
||||
+
|
||||
+ None
|
||||
+ }
|
||||
+ fn read_file_bytes(data: &[u8], buf: &mut [u8], offset: u64) -> Result<usize> {
|
||||
+ let Ok(offset) = usize::try_from(offset) else {
|
||||
+ return Ok(0);
|
||||
+ };
|
||||
+ if offset >= data.len() {
|
||||
+ return Ok(0);
|
||||
+ }
|
||||
+
|
||||
+ let count = std::cmp::min(buf.len(), data.len() - offset);
|
||||
+ buf[..count].copy_from_slice(&data[offset..offset + count]);
|
||||
+ Ok(count)
|
||||
+ }
|
||||
+ fn read_aer_register(
|
||||
+ pcie: &Pcie,
|
||||
+ addr: PciAddress,
|
||||
+ register: AerRegisterName,
|
||||
+ buf: &mut [u8],
|
||||
+ offset: u64,
|
||||
+ ) -> Result<usize> {
|
||||
+ let Some(aer_base) =
|
||||
+ Self::find_pcie_extended_capability(pcie, addr, PCIE_EXTENDED_CAPABILITY_AER)
|
||||
+ else {
|
||||
+ return Err(Error::new(ENOENT));
|
||||
+ };
|
||||
+
|
||||
+ let mut data = [0_u8; 16];
|
||||
+ for (index, chunk) in data[..register.len()].chunks_exact_mut(4).enumerate() {
|
||||
+ let index = u16::try_from(index).map_err(|_| Error::new(EIO))?;
|
||||
+ let value = unsafe { pcie.read(addr, aer_base + register.offset() + index * 4) };
|
||||
+ chunk.copy_from_slice(&value.to_le_bytes());
|
||||
+ }
|
||||
+
|
||||
+ Self::read_file_bytes(&data[..register.len()], buf, offset)
|
||||
+ }
|
||||
+ fn parse_after_pci_addr(
|
||||
+ &mut self,
|
||||
+ addr: PciAddress,
|
||||
+ after: &str,
|
||||
+ ctx: &CallerCtx,
|
||||
+ ) -> Result<Handle> {
|
||||
if after.chars().next().map_or(false, |c| c != '/') {
|
||||
return Err(Error::new(ENOENT));
|
||||
}
|
||||
let func = self.tree.get_mut(&addr).ok_or(Error::new(ENOENT))?;
|
||||
|
||||
Ok(if after.is_empty() {
|
||||
- Handle::Device
|
||||
+ Handle::Device { addr }
|
||||
} else {
|
||||
let path = &after[1..];
|
||||
|
||||
- match path {
|
||||
- "channel" => {
|
||||
- if func.enabled {
|
||||
- return Err(Error::new(ENOLCK));
|
||||
+ if path == "aer" {
|
||||
+ if Self::find_pcie_extended_capability(
|
||||
+ &self.pcie,
|
||||
+ addr,
|
||||
+ PCIE_EXTENDED_CAPABILITY_AER,
|
||||
+ )
|
||||
+ .is_none()
|
||||
+ {
|
||||
+ return Err(Error::new(ENOENT));
|
||||
+ }
|
||||
+ Handle::AerDir
|
||||
+ } else if let Some(register_name) = path.strip_prefix("aer/") {
|
||||
+ let register =
|
||||
+ AerRegisterName::from_path(register_name).ok_or(Error::new(ENOENT))?;
|
||||
+ if Self::find_pcie_extended_capability(
|
||||
+ &self.pcie,
|
||||
+ addr,
|
||||
+ PCIE_EXTENDED_CAPABILITY_AER,
|
||||
+ )
|
||||
+ .is_none()
|
||||
+ {
|
||||
+ return Err(Error::new(ENOENT));
|
||||
+ }
|
||||
+ Handle::Aer { addr, register }
|
||||
+ } else {
|
||||
+ match path {
|
||||
+ "channel" => {
|
||||
+ if func.enabled {
|
||||
+ return Err(Error::new(ENOLCK));
|
||||
+ }
|
||||
+ func.inner.legacy_interrupt_line = crate::enable_function(
|
||||
+ &self.pcie,
|
||||
+ &mut func.endpoint_header,
|
||||
+ &mut func.capabilities,
|
||||
+ );
|
||||
+ func.enabled = true;
|
||||
+ Handle::Channel {
|
||||
+ addr,
|
||||
+ st: ChannelState::AwaitingData,
|
||||
+ }
|
||||
}
|
||||
- func.inner.legacy_interrupt_line = crate::enable_function(
|
||||
- &self.pcie,
|
||||
- &mut func.endpoint_header,
|
||||
- &mut func.capabilities,
|
||||
- );
|
||||
- func.enabled = true;
|
||||
- Handle::Channel {
|
||||
- addr,
|
||||
- st: ChannelState::AwaitingData,
|
||||
+ "bind" => {
|
||||
+ let addr_str = format!("{}", addr);
|
||||
+ if let Some(&owner_pid) = self.binds.get(&addr_str) {
|
||||
+ log::info!(
|
||||
+ "pcid: device {} already bound by pid {}",
|
||||
+ addr_str,
|
||||
+ owner_pid
|
||||
+ );
|
||||
+ return Err(Error::new(EALREADY));
|
||||
+ }
|
||||
+ let caller_pid = u32::try_from(ctx.pid).map_err(|_| Error::new(EINVAL))?;
|
||||
+ self.binds.insert(addr_str.clone(), caller_pid);
|
||||
+ log::info!("pcid: device {} bound by pid {}", addr_str, caller_pid);
|
||||
+ Handle::Bind { addr }
|
||||
}
|
||||
+ _ => return Err(Error::new(ENOENT)),
|
||||
}
|
||||
- _ => return Err(Error::new(ENOENT)),
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,685 @@
|
||||
diff --git a/drivers/acpid/src/main.rs b/drivers/acpid/src/main.rs
|
||||
index 059254b3..a3f5f996 100644
|
||||
--- a/drivers/acpid/src/main.rs
|
||||
+++ b/drivers/acpid/src/main.rs
|
||||
@@ -3,6 +3,7 @@ use std::fs::File;
|
||||
use std::mem;
|
||||
use std::ops::ControlFlow;
|
||||
use std::os::unix::io::AsRawFd;
|
||||
+use std::process;
|
||||
use std::sync::Arc;
|
||||
|
||||
use ::acpi::aml::op_region::{RegionHandler, RegionSpace};
|
||||
@@ -17,6 +18,58 @@ mod ec;
|
||||
|
||||
mod scheme;
|
||||
|
||||
+fn parse_physaddrs(sdt: &self::acpi::Sdt) -> Vec<u64> {
|
||||
+ match &sdt.signature {
|
||||
+ b"RSDT" => {
|
||||
+ let chunks = sdt.data().chunks_exact(mem::size_of::<u32>());
|
||||
+ if !chunks.remainder().is_empty() {
|
||||
+ eprintln!(
|
||||
+ "acpid: malformed RSDT length {}: expected 4-byte entries",
|
||||
+ sdt.data().len()
|
||||
+ );
|
||||
+ process::exit(1);
|
||||
+ }
|
||||
+
|
||||
+ chunks
|
||||
+ .map(|chunk| match <[u8; mem::size_of::<u32>()]>::try_from(chunk) {
|
||||
+ Ok(bytes) => u32::from_le_bytes(bytes) as u64,
|
||||
+ Err(_) => {
|
||||
+ eprintln!("acpid: failed to decode RSDT physical address entry");
|
||||
+ process::exit(1);
|
||||
+ }
|
||||
+ })
|
||||
+ .collect()
|
||||
+ }
|
||||
+ b"XSDT" => {
|
||||
+ let chunks = sdt.data().chunks_exact(mem::size_of::<u64>());
|
||||
+ if !chunks.remainder().is_empty() {
|
||||
+ eprintln!(
|
||||
+ "acpid: malformed XSDT length {}: expected 8-byte entries",
|
||||
+ sdt.data().len()
|
||||
+ );
|
||||
+ process::exit(1);
|
||||
+ }
|
||||
+
|
||||
+ chunks
|
||||
+ .map(|chunk| match <[u8; mem::size_of::<u64>()]>::try_from(chunk) {
|
||||
+ Ok(bytes) => u64::from_le_bytes(bytes),
|
||||
+ Err(_) => {
|
||||
+ eprintln!("acpid: failed to decode XSDT physical address entry");
|
||||
+ process::exit(1);
|
||||
+ }
|
||||
+ })
|
||||
+ .collect()
|
||||
+ }
|
||||
+ signature => {
|
||||
+ eprintln!(
|
||||
+ "acpid: expected [RX]SDT from kernel, got {:?}",
|
||||
+ String::from_utf8_lossy(signature)
|
||||
+ );
|
||||
+ process::exit(1);
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
fn daemon(daemon: daemon::Daemon) -> ! {
|
||||
common::setup_logging(
|
||||
"misc",
|
||||
@@ -29,7 +82,10 @@ fn daemon(daemon: daemon::Daemon) -> ! {
|
||||
log::info!("acpid start");
|
||||
|
||||
let rxsdt_raw_data: Arc<[u8]> = std::fs::read("/scheme/kernel.acpi/rxsdt")
|
||||
- .expect("acpid: failed to read `/scheme/kernel.acpi/rxsdt`")
|
||||
+ .unwrap_or_else(|err| {
|
||||
+ eprintln!("acpid: failed to read `/scheme/kernel.acpi/rxsdt`: {err}");
|
||||
+ process::exit(1);
|
||||
+ })
|
||||
.into();
|
||||
|
||||
if rxsdt_raw_data.is_empty() {
|
||||
@@ -38,84 +94,84 @@ fn daemon(daemon: daemon::Daemon) -> ! {
|
||||
std::process::exit(0);
|
||||
}
|
||||
|
||||
- let sdt = self::acpi::Sdt::new(rxsdt_raw_data).expect("acpid: failed to parse [RX]SDT");
|
||||
-
|
||||
- let mut thirty_two_bit;
|
||||
- let mut sixty_four_bit;
|
||||
-
|
||||
- let physaddrs_iter = match &sdt.signature {
|
||||
- b"RSDT" => {
|
||||
- thirty_two_bit = sdt
|
||||
- .data()
|
||||
- .chunks(mem::size_of::<u32>())
|
||||
- // TODO: With const generics, the compiler has some way of doing this for static sizes.
|
||||
- .map(|chunk| <[u8; mem::size_of::<u32>()]>::try_from(chunk).unwrap())
|
||||
- .map(|chunk| u32::from_le_bytes(chunk))
|
||||
- .map(u64::from);
|
||||
-
|
||||
- &mut thirty_two_bit as &mut dyn Iterator<Item = u64>
|
||||
- }
|
||||
- b"XSDT" => {
|
||||
- sixty_four_bit = sdt
|
||||
- .data()
|
||||
- .chunks(mem::size_of::<u64>())
|
||||
- .map(|chunk| <[u8; mem::size_of::<u64>()]>::try_from(chunk).unwrap())
|
||||
- .map(|chunk| u64::from_le_bytes(chunk));
|
||||
-
|
||||
- &mut sixty_four_bit as &mut dyn Iterator<Item = u64>
|
||||
- }
|
||||
- _ => panic!("acpid: expected [RX]SDT from kernel to be either of those"),
|
||||
- };
|
||||
+ let sdt = self::acpi::Sdt::new(rxsdt_raw_data).unwrap_or_else(|err| {
|
||||
+ eprintln!("acpid: failed to parse [RX]SDT: {err}");
|
||||
+ process::exit(1);
|
||||
+ });
|
||||
+ let physaddrs = parse_physaddrs(&sdt);
|
||||
|
||||
let region_handlers: Vec<(RegionSpace, Box<dyn RegionHandler + 'static>)> = vec![
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
(RegionSpace::EmbeddedControl, Box::new(ec::Ec::new())),
|
||||
];
|
||||
- let acpi_context = self::acpi::AcpiContext::init(physaddrs_iter, region_handlers);
|
||||
+ let acpi_context = self::acpi::AcpiContext::init(physaddrs.into_iter(), region_handlers);
|
||||
|
||||
// TODO: I/O permission bitmap?
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
- common::acquire_port_io_rights().expect("acpid: failed to set I/O privilege level to Ring 3");
|
||||
+ common::acquire_port_io_rights().unwrap_or_else(|err| {
|
||||
+ eprintln!("acpid: failed to set I/O privilege level to Ring 3: {err}");
|
||||
+ process::exit(1);
|
||||
+ });
|
||||
|
||||
let shutdown_pipe = File::open("/scheme/kernel.acpi/kstop")
|
||||
- .expect("acpid: failed to open `/scheme/kernel.acpi/kstop`");
|
||||
-
|
||||
- let mut event_queue = RawEventQueue::new().expect("acpid: failed to create event queue");
|
||||
- let socket = Socket::nonblock().expect("acpid: failed to create disk scheme");
|
||||
+ .unwrap_or_else(|err| {
|
||||
+ eprintln!("acpid: failed to open `/scheme/kernel.acpi/kstop`: {err}");
|
||||
+ process::exit(1);
|
||||
+ });
|
||||
+
|
||||
+ let mut event_queue = RawEventQueue::new().unwrap_or_else(|err| {
|
||||
+ eprintln!("acpid: failed to create event queue: {err}");
|
||||
+ process::exit(1);
|
||||
+ });
|
||||
+ let socket = Socket::nonblock().unwrap_or_else(|err| {
|
||||
+ eprintln!("acpid: failed to create disk scheme: {err}");
|
||||
+ process::exit(1);
|
||||
+ });
|
||||
|
||||
let mut scheme = self::scheme::AcpiScheme::new(&acpi_context, &socket);
|
||||
let mut handler = Blocking::new(&socket, 16);
|
||||
|
||||
event_queue
|
||||
.subscribe(shutdown_pipe.as_raw_fd() as usize, 0, EventFlags::READ)
|
||||
- .expect("acpid: failed to register shutdown pipe for event queue");
|
||||
+ .unwrap_or_else(|err| {
|
||||
+ eprintln!("acpid: failed to register shutdown pipe for event queue: {err}");
|
||||
+ process::exit(1);
|
||||
+ });
|
||||
event_queue
|
||||
.subscribe(socket.inner().raw(), 1, EventFlags::READ)
|
||||
- .expect("acpid: failed to register scheme socket for event queue");
|
||||
+ .unwrap_or_else(|err| {
|
||||
+ eprintln!("acpid: failed to register scheme socket for event queue: {err}");
|
||||
+ process::exit(1);
|
||||
+ });
|
||||
|
||||
register_sync_scheme(&socket, "acpi", &mut scheme)
|
||||
- .expect("acpid: failed to register acpi scheme to namespace");
|
||||
+ .unwrap_or_else(|err| {
|
||||
+ eprintln!("acpid: failed to register acpi scheme to namespace: {err}");
|
||||
+ process::exit(1);
|
||||
+ });
|
||||
|
||||
daemon.ready();
|
||||
|
||||
- libredox::call::setrens(0, 0).expect("acpid: failed to enter null namespace");
|
||||
+ libredox::call::setrens(0, 0).unwrap_or_else(|err| {
|
||||
+ eprintln!("acpid: failed to enter null namespace: {err}");
|
||||
+ process::exit(1);
|
||||
+ });
|
||||
|
||||
let mut mounted = true;
|
||||
while mounted {
|
||||
- let Some(event) = event_queue
|
||||
- .next()
|
||||
- .transpose()
|
||||
- .expect("acpid: failed to read event file")
|
||||
- else {
|
||||
+ let Some(event) = event_queue.next().transpose().unwrap_or_else(|err| {
|
||||
+ eprintln!("acpid: failed to read event file: {err}");
|
||||
+ process::exit(1);
|
||||
+ }) else {
|
||||
break;
|
||||
};
|
||||
|
||||
if event.fd == socket.inner().raw() {
|
||||
loop {
|
||||
- match handler
|
||||
- .process_requests_nonblocking(&mut scheme)
|
||||
- .expect("acpid: failed to process requests")
|
||||
- {
|
||||
+ match handler.process_requests_nonblocking(&mut scheme).unwrap_or_else(|err| {
|
||||
+ eprintln!("acpid: failed to process requests: {err}");
|
||||
+ process::exit(1);
|
||||
+ }) {
|
||||
ControlFlow::Continue(()) => {}
|
||||
ControlFlow::Break(()) => break,
|
||||
}
|
||||
@@ -134,7 +190,8 @@ fn daemon(daemon: daemon::Daemon) -> ! {
|
||||
|
||||
acpi_context.set_global_s_state(5);
|
||||
|
||||
- unreachable!("System should have shut down before this is entered");
|
||||
+ eprintln!("acpid: system did not shut down after requesting S5");
|
||||
+ process::exit(1);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
diff --git a/drivers/pcid/src/main.rs b/drivers/pcid/src/main.rs
|
||||
index 61cd9a78..18ee18ab 100644
|
||||
--- a/drivers/pcid/src/main.rs
|
||||
+++ b/drivers/pcid/src/main.rs
|
||||
@@ -3,6 +3,7 @@
|
||||
#![feature(non_exhaustive_omitted_patterns_lint)]
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
+use std::process;
|
||||
|
||||
use log::{debug, info, trace, warn};
|
||||
use pci_types::capability::PciCapability;
|
||||
@@ -42,7 +43,16 @@ fn handle_parsed_header(
|
||||
continue;
|
||||
}
|
||||
match endpoint_header.bar(i, pcie) {
|
||||
- Some(TyBar::Io { port }) => bars[i as usize] = PciBar::Port(port.try_into().unwrap()),
|
||||
+ Some(TyBar::Io { port }) => match port.try_into() {
|
||||
+ Ok(port) => bars[i as usize] = PciBar::Port(port),
|
||||
+ Err(_) => {
|
||||
+ warn!(
|
||||
+ "pcid: skipping invalid I/O BAR port {port:#x} on {}",
|
||||
+ endpoint_header.header().address()
|
||||
+ );
|
||||
+ bars[i as usize] = PciBar::None;
|
||||
+ }
|
||||
+ },
|
||||
Some(TyBar::Memory32 {
|
||||
address,
|
||||
size,
|
||||
@@ -251,7 +261,10 @@ fn daemon(daemon: daemon::Daemon) -> ! {
|
||||
info!("PCI SG-BS:DV.F VEND:DEVI CL.SC.IN.RV");
|
||||
|
||||
let mut scheme = scheme::PciScheme::new(pcie);
|
||||
- let socket = redox_scheme::Socket::create().expect("failed to open pci scheme socket");
|
||||
+ let socket = redox_scheme::Socket::create().unwrap_or_else(|err| {
|
||||
+ eprintln!("pcid: failed to open pci scheme socket: {err}");
|
||||
+ process::exit(1);
|
||||
+ });
|
||||
let handler = Blocking::new(&socket, 16);
|
||||
|
||||
{
|
||||
@@ -259,17 +272,27 @@ fn daemon(daemon: daemon::Daemon) -> ! {
|
||||
Ok(register_pci) => {
|
||||
let access_id = scheme.access();
|
||||
|
||||
- let access_fd = socket
|
||||
- .create_this_scheme_fd(0, access_id, syscall::O_RDWR, 0)
|
||||
- .expect("failed to issue this resource");
|
||||
- let access_bytes = access_fd.to_ne_bytes();
|
||||
- let _ = register_pci
|
||||
- .call_wo(
|
||||
- &access_bytes,
|
||||
- syscall::CallFlags::WRITE | syscall::CallFlags::FD,
|
||||
- &[],
|
||||
- )
|
||||
- .expect("failed to send pci_fd to acpid");
|
||||
+ match socket.create_this_scheme_fd(0, access_id, syscall::O_RDWR, 0) {
|
||||
+ Ok(access_fd) => {
|
||||
+ let access_bytes = access_fd.to_ne_bytes();
|
||||
+ if let Err(err) = register_pci.call_wo(
|
||||
+ &access_bytes,
|
||||
+ syscall::CallFlags::WRITE | syscall::CallFlags::FD,
|
||||
+ &[],
|
||||
+ ) {
|
||||
+ warn!(
|
||||
+ "pcid: failed to send pci_fd to acpid (error: {}). Running without ACPI integration.",
|
||||
+ err
|
||||
+ );
|
||||
+ }
|
||||
+ }
|
||||
+ Err(err) => {
|
||||
+ warn!(
|
||||
+ "pcid: failed to issue acpid registration resource (error: {}). Running without ACPI integration.",
|
||||
+ err
|
||||
+ );
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
Err(err) => {
|
||||
if err.errno() == libredox::errno::ENODEV {
|
||||
@@ -305,13 +328,20 @@ fn daemon(daemon: daemon::Daemon) -> ! {
|
||||
debug!("Enumeration complete, now starting pci scheme");
|
||||
|
||||
register_sync_scheme(&socket, "pci", &mut scheme)
|
||||
- .expect("failed to register pci scheme to namespace");
|
||||
+ .unwrap_or_else(|err| {
|
||||
+ eprintln!("pcid: failed to register pci scheme to namespace: {err}");
|
||||
+ process::exit(1);
|
||||
+ });
|
||||
|
||||
let _ = daemon.ready();
|
||||
|
||||
- handler
|
||||
- .process_requests_blocking(scheme)
|
||||
- .expect("pcid: failed to process requests");
|
||||
+ match handler.process_requests_blocking(scheme) {
|
||||
+ Ok(never) => match never {},
|
||||
+ Err(err) => {
|
||||
+ eprintln!("pcid: failed to process requests: {err}");
|
||||
+ process::exit(1);
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
|
||||
fn scan_device(
|
||||
@@ -323,6 +353,7 @@ fn scan_device(
|
||||
) {
|
||||
for func_num in 0..8 {
|
||||
let header = TyPciHeader::new(PciAddress::new(0, bus_num, dev_num, func_num));
|
||||
+ let header_address = header.address();
|
||||
|
||||
let (vendor_id, device_id) = header.id(pcie);
|
||||
if vendor_id == 0xffff && device_id == 0xffff {
|
||||
@@ -344,21 +375,40 @@ fn scan_device(
|
||||
revision,
|
||||
};
|
||||
|
||||
- info!("PCI {} {}", header.address(), full_device_id.display());
|
||||
+ info!("PCI {} {}", header_address, full_device_id.display());
|
||||
|
||||
let has_multiple_functions = header.has_multiple_functions(pcie);
|
||||
|
||||
match header.header_type(pcie) {
|
||||
HeaderType::Endpoint => {
|
||||
+ let endpoint_header = match EndpointHeader::from_header(header, pcie) {
|
||||
+ Some(endpoint_header) => endpoint_header,
|
||||
+ None => {
|
||||
+ warn!(
|
||||
+ "pcid: failed to parse endpoint header for {}",
|
||||
+ header_address,
|
||||
+ );
|
||||
+ continue;
|
||||
+ }
|
||||
+ };
|
||||
handle_parsed_header(
|
||||
pcie,
|
||||
tree,
|
||||
- EndpointHeader::from_header(header, pcie).unwrap(),
|
||||
+ endpoint_header,
|
||||
full_device_id,
|
||||
);
|
||||
}
|
||||
HeaderType::PciPciBridge => {
|
||||
- let bridge_header = PciPciBridgeHeader::from_header(header, pcie).unwrap();
|
||||
+ let bridge_header = match PciPciBridgeHeader::from_header(header, pcie) {
|
||||
+ Some(bridge_header) => bridge_header,
|
||||
+ None => {
|
||||
+ warn!(
|
||||
+ "pcid: failed to parse bridge header for {}",
|
||||
+ header_address,
|
||||
+ );
|
||||
+ continue;
|
||||
+ }
|
||||
+ };
|
||||
bus_nums.push(bridge_header.secondary_bus_number(pcie));
|
||||
}
|
||||
ty => {
|
||||
diff --git a/init/src/main.rs b/init/src/main.rs
|
||||
index 5682cf44..cd270a6e 100644
|
||||
--- a/init/src/main.rs
|
||||
+++ b/init/src/main.rs
|
||||
@@ -1,6 +1,7 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::ffi::OsString;
|
||||
use std::path::Path;
|
||||
+use std::time::Duration;
|
||||
use std::{env, fs, io};
|
||||
|
||||
use libredox::flag::{O_RDONLY, O_WRONLY};
|
||||
@@ -166,19 +167,36 @@ fn main() {
|
||||
}
|
||||
};
|
||||
for entry in entries {
|
||||
+ let Some(unit_name) = entry.file_name().and_then(|name| name.to_str()) else {
|
||||
+ eprintln!(
|
||||
+ "init: skipping config entry with invalid filename: {}",
|
||||
+ entry.display()
|
||||
+ );
|
||||
+ continue;
|
||||
+ };
|
||||
scheduler.schedule_start_and_report_errors(
|
||||
&mut unit_store,
|
||||
- UnitId(entry.file_name().unwrap().to_str().unwrap().to_owned()),
|
||||
+ UnitId(unit_name.to_owned()),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
scheduler.step(&mut unit_store, &mut init_config);
|
||||
|
||||
- libredox::call::setrens(0, 0).expect("init: failed to enter null namespace");
|
||||
+ if let Err(err) = libredox::call::setrens(0, 0) {
|
||||
+ eprintln!("init: failed to enter null namespace: {err}");
|
||||
+ std::process::exit(1);
|
||||
+ }
|
||||
|
||||
loop {
|
||||
let mut status = 0;
|
||||
- libredox::call::waitpid(0, &mut status, 0).unwrap();
|
||||
+ match libredox::call::waitpid(0, &mut status, 0) {
|
||||
+ Ok(_) => {}
|
||||
+ Err(err) if err.errno() == libredox::errno::EINTR => continue,
|
||||
+ Err(err) => {
|
||||
+ eprintln!("init: waitpid failed: {err}");
|
||||
+ std::thread::sleep(Duration::from_millis(100));
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
}
|
||||
diff --git a/init/src/scheduler.rs b/init/src/scheduler.rs
|
||||
index d42a4e57..3b8d10b0 100644
|
||||
--- a/init/src/scheduler.rs
|
||||
+++ b/init/src/scheduler.rs
|
||||
@@ -43,7 +43,10 @@ impl Scheduler {
|
||||
) {
|
||||
let loaded_units = unit_store.load_units(unit_id.clone(), errors);
|
||||
for unit_id in loaded_units {
|
||||
- if !unit_store.unit(&unit_id).conditions_met() {
|
||||
+ if unit_store
|
||||
+ .try_unit(&unit_id)
|
||||
+ .is_ok_and(|unit| !unit.conditions_met())
|
||||
+ {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -62,7 +65,10 @@ impl Scheduler {
|
||||
|
||||
match job.kind {
|
||||
JobKind::Start => {
|
||||
- let unit = unit_store.unit_mut(&job.unit);
|
||||
+ let Ok(unit) = unit_store.try_unit_mut(&job.unit) else {
|
||||
+ eprintln!("init: unit {} not found in store, skipping", job.unit.0);
|
||||
+ continue 'a;
|
||||
+ };
|
||||
|
||||
for dep in &unit.info.requires_weak {
|
||||
for pending_job in &self.pending {
|
||||
diff --git a/init/src/service.rs b/init/src/service.rs
|
||||
index ed0023e9..827ae275 100644
|
||||
--- a/init/src/service.rs
|
||||
+++ b/init/src/service.rs
|
||||
@@ -3,13 +3,24 @@ 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::process::{Child, Command};
|
||||
use std::{env, io};
|
||||
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::script::subst_env;
|
||||
|
||||
+fn terminate_child(child: &mut Child, command: &str) {
|
||||
+ if let Err(err) = child.kill() {
|
||||
+ if err.kind() != io::ErrorKind::InvalidInput {
|
||||
+ eprintln!("init: failed to terminate {command}: {err}");
|
||||
+ }
|
||||
+ }
|
||||
+ if let Err(err) = child.wait() {
|
||||
+ eprintln!("init: failed to reap {command}: {err}");
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Service {
|
||||
@@ -37,7 +48,8 @@ pub enum ServiceType {
|
||||
impl Service {
|
||||
pub fn spawn(&self, base_envs: &BTreeMap<String, OsString>) {
|
||||
let mut command = Command::new(&self.cmd);
|
||||
- command.args(self.args.iter().map(|arg| subst_env(arg)));
|
||||
+ let resolved_args: Vec<String> = self.args.iter().map(|arg| subst_env(arg)).collect();
|
||||
+ command.args(&resolved_args);
|
||||
command.env_clear();
|
||||
for env in &self.inherit_envs {
|
||||
if let Some(value) = env::var_os(env) {
|
||||
@@ -45,14 +57,25 @@ impl Service {
|
||||
}
|
||||
}
|
||||
command.envs(base_envs).envs(&self.envs);
|
||||
+ let command_display = if resolved_args.is_empty() {
|
||||
+ self.cmd.clone()
|
||||
+ } else {
|
||||
+ format!("{} {}", self.cmd, resolved_args.join(" "))
|
||||
+ };
|
||||
|
||||
- let (mut read_pipe, write_pipe) = io::pipe().unwrap();
|
||||
+ let (mut read_pipe, write_pipe) = match io::pipe().map_err(|err| {
|
||||
+ eprintln!("init: failed to create readiness pipe for {command_display}: {err}");
|
||||
+ err
|
||||
+ }) {
|
||||
+ Ok(pair) => pair,
|
||||
+ Err(_) => return,
|
||||
+ };
|
||||
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);
|
||||
+ eprintln!("init: failed to execute {command_display}: {err}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
@@ -61,10 +84,10 @@ impl Service {
|
||||
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");
|
||||
+ eprintln!("init: {command_display} exited without notifying readiness");
|
||||
}
|
||||
Err(err) => {
|
||||
- eprintln!("init: failed to wait for {command:?}: {err}");
|
||||
+ eprintln!("init: failed to wait for {command_display}: {err}");
|
||||
}
|
||||
},
|
||||
ServiceType::Scheme(scheme) => {
|
||||
@@ -80,7 +103,7 @@ impl Service {
|
||||
errno: syscall::EINTR,
|
||||
}) => continue,
|
||||
Ok(0) => {
|
||||
- eprintln!("init: {command:?} exited without notifying readiness");
|
||||
+ eprintln!("init: {command_display} exited without notifying readiness");
|
||||
return;
|
||||
}
|
||||
Ok(1) => break,
|
||||
@@ -89,26 +112,40 @@ impl Service {
|
||||
return;
|
||||
}
|
||||
Err(err) => {
|
||||
- eprintln!("init: failed to wait for {command:?}: {err}");
|
||||
+ eprintln!("init: failed to wait for {command_display}: {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");
|
||||
+ let current_namespace_fd = match libredox::call::getns() {
|
||||
+ Ok(fd) => fd,
|
||||
+ Err(err) => {
|
||||
+ eprintln!("init: failed to get current namespace for {command_display}: {err}");
|
||||
+ terminate_child(&mut child, &command_display);
|
||||
+ return;
|
||||
+ }
|
||||
+ };
|
||||
+ if let Err(err) =
|
||||
+ libredox::call::register_scheme_to_ns(current_namespace_fd, scheme, new_fd)
|
||||
+ {
|
||||
+ eprintln!(
|
||||
+ "init: failed to register scheme {scheme:?} for {command_display}: {err}"
|
||||
+ );
|
||||
+ terminate_child(&mut child, &command_display);
|
||||
+ return;
|
||||
+ }
|
||||
}
|
||||
ServiceType::Oneshot => {
|
||||
drop(read_pipe);
|
||||
match child.wait() {
|
||||
Ok(exit_status) => {
|
||||
if !exit_status.success() {
|
||||
- eprintln!("init: {command:?} failed with {exit_status}");
|
||||
+ eprintln!("init: {command_display} failed with {exit_status}");
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
- eprintln!("init: failed to wait for {:?}: {}", command, err)
|
||||
+ eprintln!("init: failed to wait for {command_display}: {err}")
|
||||
}
|
||||
}
|
||||
}
|
||||
diff --git a/init/src/unit.rs b/init/src/unit.rs
|
||||
index 98053cb2..bd998394 100644
|
||||
--- a/init/src/unit.rs
|
||||
+++ b/init/src/unit.rs
|
||||
@@ -23,8 +23,14 @@ impl UnitStore {
|
||||
}
|
||||
|
||||
pub fn set_runtime_target(&mut self, unit_id: UnitId) {
|
||||
- assert!(self.runtime_target.is_none());
|
||||
- assert!(self.units.contains_key(&unit_id));
|
||||
+ if self.runtime_target.is_some() {
|
||||
+ eprintln!("init: runtime target already set, ignoring {}", unit_id.0);
|
||||
+ return;
|
||||
+ }
|
||||
+ if !self.units.contains_key(&unit_id) {
|
||||
+ eprintln!("init: runtime target {} not found in unit store", unit_id.0);
|
||||
+ return;
|
||||
+ }
|
||||
self.runtime_target = Some(unit_id);
|
||||
}
|
||||
|
||||
@@ -85,8 +91,15 @@ impl UnitStore {
|
||||
let unit = self.load_single_unit(unit_id, errors);
|
||||
if let Some(unit) = unit {
|
||||
loaded_units.push(unit.clone());
|
||||
- for dep in &self.unit(&unit).info.requires_weak {
|
||||
- pending_units.push(dep.clone());
|
||||
+ match self.try_unit(&unit) {
|
||||
+ Ok(unit) => {
|
||||
+ for dep in &unit.info.requires_weak {
|
||||
+ pending_units.push(dep.clone());
|
||||
+ }
|
||||
+ }
|
||||
+ Err(err) => {
|
||||
+ errors.push(err);
|
||||
+ }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -94,12 +107,34 @@ impl UnitStore {
|
||||
loaded_units
|
||||
}
|
||||
|
||||
+ pub fn try_unit(&self, unit: &UnitId) -> Result<&Unit, String> {
|
||||
+ self.units
|
||||
+ .get(unit)
|
||||
+ .ok_or_else(|| format!("unit {} not found in store", unit.0))
|
||||
+ }
|
||||
+
|
||||
+ // Keep the legacy infallible accessors for compatibility while scheduler/load paths
|
||||
+ // use the fallible helpers to avoid panicking on missing units.
|
||||
+ #[allow(dead_code)]
|
||||
pub fn unit(&self, unit: &UnitId) -> &Unit {
|
||||
- self.units.get(unit).unwrap()
|
||||
+ self.try_unit(unit).unwrap_or_else(|err| {
|
||||
+ eprintln!("init: {err}");
|
||||
+ std::process::exit(1);
|
||||
+ })
|
||||
+ }
|
||||
+
|
||||
+ pub fn try_unit_mut(&mut self, unit: &UnitId) -> Result<&mut Unit, String> {
|
||||
+ self.units
|
||||
+ .get_mut(unit)
|
||||
+ .ok_or_else(|| format!("unit {} not found in store", unit.0))
|
||||
}
|
||||
|
||||
+ #[allow(dead_code)]
|
||||
pub fn unit_mut(&mut self, unit: &UnitId) -> &mut Unit {
|
||||
- self.units.get_mut(unit).unwrap()
|
||||
+ self.try_unit_mut(unit).unwrap_or_else(|err| {
|
||||
+ eprintln!("init: {err}");
|
||||
+ std::process::exit(1);
|
||||
+ })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,7 +215,7 @@ impl Unit {
|
||||
) -> io::Result<Self> {
|
||||
let config = fs::read_to_string(config_path)?;
|
||||
|
||||
- let Some(ext) = config_path.extension().map(|ext| ext.to_str().unwrap()) else {
|
||||
+ let Some(ext) = config_path.extension().and_then(|ext| ext.to_str()) else {
|
||||
let script = Script::from_str(&config, errors)?;
|
||||
return Ok(Unit {
|
||||
id,
|
||||
Reference in New Issue
Block a user