main.rs 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. //! init is the first process started by the kernel.
  2. //!
  3. //! This implementation creates the minimal mounts required to run BPF programs, runs all binaries
  4. //! in /bin, prints a final message ("init: success|failure"), and powers off the machine.
  5. use anyhow::Context as _;
  6. #[derive(Debug)]
  7. struct Errors(Vec<anyhow::Error>);
  8. impl std::fmt::Display for Errors {
  9. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  10. let Self(errors) = self;
  11. for (i, error) in errors.iter().enumerate() {
  12. if i != 0 {
  13. writeln!(f)?;
  14. }
  15. write!(f, "{:?}", error)?;
  16. }
  17. Ok(())
  18. }
  19. }
  20. impl std::error::Error for Errors {}
  21. fn run() -> anyhow::Result<()> {
  22. const RXRXRX: nix::sys::stat::Mode = nix::sys::stat::Mode::empty()
  23. .union(nix::sys::stat::Mode::S_IRUSR)
  24. .union(nix::sys::stat::Mode::S_IXUSR)
  25. .union(nix::sys::stat::Mode::S_IRGRP)
  26. .union(nix::sys::stat::Mode::S_IXGRP)
  27. .union(nix::sys::stat::Mode::S_IROTH)
  28. .union(nix::sys::stat::Mode::S_IXOTH);
  29. struct Mount {
  30. source: &'static str,
  31. target: &'static str,
  32. fstype: &'static str,
  33. flags: nix::mount::MsFlags,
  34. data: Option<&'static str>,
  35. target_mode: Option<nix::sys::stat::Mode>,
  36. }
  37. for Mount {
  38. source,
  39. target,
  40. fstype,
  41. flags,
  42. data,
  43. target_mode,
  44. } in [
  45. Mount {
  46. source: "proc",
  47. target: "/proc",
  48. fstype: "proc",
  49. flags: nix::mount::MsFlags::empty(),
  50. data: None,
  51. target_mode: Some(RXRXRX),
  52. },
  53. Mount {
  54. source: "sysfs",
  55. target: "/sys",
  56. fstype: "sysfs",
  57. flags: nix::mount::MsFlags::empty(),
  58. data: None,
  59. target_mode: Some(RXRXRX),
  60. },
  61. Mount {
  62. source: "debugfs",
  63. target: "/sys/kernel/debug",
  64. fstype: "debugfs",
  65. flags: nix::mount::MsFlags::empty(),
  66. data: None,
  67. target_mode: None,
  68. },
  69. Mount {
  70. source: "bpffs",
  71. target: "/sys/fs/bpf",
  72. fstype: "bpf",
  73. flags: nix::mount::MsFlags::empty(),
  74. data: None,
  75. target_mode: None,
  76. },
  77. ] {
  78. match target_mode {
  79. None => {
  80. // Must exist.
  81. let nix::sys::stat::FileStat { st_mode, .. } = nix::sys::stat::stat(target)
  82. .with_context(|| format!("stat({target}) failed"))?;
  83. let s_flag = nix::sys::stat::SFlag::from_bits_truncate(st_mode);
  84. if !s_flag.contains(nix::sys::stat::SFlag::S_IFDIR) {
  85. anyhow::bail!("{target} is not a directory");
  86. }
  87. }
  88. Some(target_mode) => {
  89. // Must not exist.
  90. nix::unistd::mkdir(target, target_mode)
  91. .with_context(|| format!("mkdir({target}) failed"))?;
  92. }
  93. }
  94. nix::mount::mount(Some(source), target, Some(fstype), flags, data).with_context(|| {
  95. format!("mount({source}, {target}, {fstype}, {flags:?}, {data:?}) failed")
  96. })?;
  97. }
  98. // By contract we run everything in /bin and assume they're rust test binaries.
  99. //
  100. // If the user requested command line arguments, they're named init.arg={}.
  101. // Read kernel parameters from /proc/cmdline. They're space separated on a single line.
  102. let cmdline = std::fs::read_to_string("/proc/cmdline")
  103. .with_context(|| "read_to_string(/proc/cmdline) failed")?;
  104. let args = cmdline
  105. .split_whitespace()
  106. .filter_map(|parameter| {
  107. parameter
  108. .strip_prefix("init.arg=")
  109. .map(std::ffi::OsString::from)
  110. })
  111. .collect::<Vec<_>>();
  112. // Iterate files in /bin.
  113. let read_dir = std::fs::read_dir("/bin").context("read_dir(/bin) failed")?;
  114. let errors = read_dir
  115. .map(|entry| {
  116. let entry = entry.context("read_dir(/bin) failed")?;
  117. let path = entry.path();
  118. let status = std::process::Command::new(&path)
  119. .args(&args)
  120. .env("RUST_LOG", "debug")
  121. .status()
  122. .with_context(|| format!("failed to execute {}", path.display()))?;
  123. if status.code() == Some(0) {
  124. Ok(())
  125. } else {
  126. Err(anyhow::anyhow!("{} failed: {status:?}", path.display()))
  127. }
  128. })
  129. .filter_map(|result| {
  130. // TODO(https://github.com/rust-lang/rust-clippy/issues/14112): Remove this allowance
  131. // when the lint behaves more sensibly.
  132. #[expect(clippy::manual_ok_err)]
  133. match result {
  134. Ok(()) => None,
  135. Err(err) => Some(err),
  136. }
  137. })
  138. .collect::<Vec<_>>();
  139. if errors.is_empty() {
  140. Ok(())
  141. } else {
  142. Err(Errors(errors).into())
  143. }
  144. }
  145. fn main() {
  146. match run() {
  147. Ok(()) => {
  148. println!("init: success");
  149. }
  150. Err(err) => {
  151. println!("{err:?}");
  152. println!("init: failure");
  153. }
  154. }
  155. let how = nix::sys::reboot::RebootMode::RB_POWER_OFF;
  156. let _: std::convert::Infallible = nix::sys::reboot::reboot(how)
  157. .unwrap_or_else(|err| panic!("reboot({how:?}) failed: {err:?}"));
  158. }