]> git.proxmox.com Git - proxmox-backup-restore-image.git/blame - src/init-shim-rs/src/main.rs
add debug initramfs as seperate package
[proxmox-backup-restore-image.git] / src / init-shim-rs / src / main.rs
CommitLineData
2a1ef81f 1use anyhow::{bail, Error};
3b259e7a
SR
2use std::ffi::CStr;
3use std::fs;
2a1ef81f
SR
4use std::path::PathBuf;
5use std::process::Command;
3b259e7a
SR
6
7const URANDOM_MAJ: u64 = 1;
8const URANDOM_MIN: u64 = 9;
9
10/// Set up a somewhat normal linux userspace environment before starting the restore daemon, and
11/// provide error messages to the user if doing so fails.
12///
13/// This is supposed to run as /init in an initramfs image.
14fn main() {
15 println!("[init-shim] beginning user space setup");
16
17 // /dev is mounted automatically
18 wrap_err("mount /sys", || do_mount("/sys", "sysfs"));
19 wrap_err("mount /proc", || do_mount("/proc", "proc"));
20
21 // make device nodes required by daemon
22 wrap_err("mknod /dev/urandom", || {
23 do_mknod("/dev/urandom", URANDOM_MAJ, URANDOM_MIN)
24 });
25
2a1ef81f
SR
26 if let Err(err) = run_agetty() {
27 // not fatal
28 println!("[init-shim] debug: agetty start failed: {}", err);
29 }
30
3b259e7a
SR
31 let uptime = read_uptime();
32 println!("[init-shim] reached daemon start after {:.2}s", uptime);
33
34 do_run("/proxmox-restore-daemon");
35}
36
2a1ef81f
SR
37fn run_agetty() -> Result<(), Error> {
38 use nix::unistd::{fork, ForkResult};
39
40 if !PathBuf::from("/sbin/agetty").exists() {
41 bail!("/sbin/agetty not found, probably not running debug mode and safe to ignore");
42 }
43
44 if !PathBuf::from("/sys/class/tty/ttyS1/device/driver/serial8250").exists() {
45 bail!("ttyS1 device does not exist or is not a 8250");
46 }
47
48 let dev = fs::read_to_string("/sys/class/tty/ttyS1/dev")?;
49 let (tty_maj, tty_min) = dev.trim().split_at(dev.find(':').unwrap_or(1));
50 do_mknod("/dev/ttyS1", tty_maj.parse()?, tty_min[1..].parse()?)?;
51
52 match unsafe { fork() } {
53 Ok(ForkResult::Parent { .. }) => {}
54 Ok(ForkResult::Child) => loop {
55 // continue to restart agetty if it exits, this runs in a forked process
56 println!("[init-shim] Spawning new agetty");
57 let res = Command::new("/sbin/agetty")
58 .args(&["-a", "root", "-l", "/bin/busybox", "-o", "sh", "115200", "ttyS1"])
59 .spawn()
60 .unwrap()
61 .wait()
62 .unwrap();
63 println!("[init-shim] agetty exited: {}", res.code().unwrap_or(-1));
64 },
65 Err(err) => println!("fork failed: {}", err),
66 }
67
68 Ok(())
69}
70
3b259e7a
SR
71fn do_mount(target: &str, fstype: &str) -> Result<(), Error> {
72 use nix::mount::{mount, MsFlags};
2a1ef81f 73 fs::create_dir_all(target)?;
3b259e7a
SR
74 let none_type: Option<&CStr> = None;
75 mount(
76 none_type,
77 target,
78 Some(fstype),
79 MsFlags::MS_NOSUID | MsFlags::MS_NOEXEC,
80 none_type,
81 )?;
82 Ok(())
83}
84
85fn do_mknod(path: &str, maj: u64, min: u64) -> Result<(), Error> {
86 use nix::sys::stat;
87 let dev = stat::makedev(maj, min);
88 stat::mknod(path, stat::SFlag::S_IFCHR, stat::Mode::S_IRWXU, dev)?;
89 Ok(())
90}
91
92fn read_uptime() -> f32 {
93 let uptime = wrap_err("read /proc/uptime", || {
94 fs::read_to_string("/proc/uptime").map_err(|e| e.into())
95 });
96 // this can never fail on a sane kernel, so just unwrap
97 uptime
98 .split_ascii_whitespace()
99 .next()
100 .unwrap()
101 .parse()
102 .unwrap()
103}
104
105fn do_run(cmd: &str) -> ! {
106 use std::io::ErrorKind;
3b259e7a
SR
107
108 let spawn_res = Command::new(cmd).env("RUST_BACKTRACE", "1").spawn();
109
110 match spawn_res {
111 Ok(mut child) => {
112 let res = wrap_err("wait failed", || child.wait().map_err(|e| e.into()));
113 error(&format!(
114 "child process {} (pid={} exitcode={}) exited unexpectedly, check log for more info",
115 cmd,
116 child.id(),
117 res.code().unwrap_or(-1),
118 ));
119 }
120 Err(err) if err.kind() == ErrorKind::NotFound => {
121 error(&format!(
122 concat!(
123 "{} missing from image.\n",
124 "This initramfs should only be run with proxmox-file-restore!"
125 ),
126 cmd
127 ));
128 }
129 Err(err) => {
130 error(&format!(
131 "unexpected error during start of {}: {}",
132 cmd, err
133 ));
134 }
135 }
136}
137
138fn wrap_err<R, F: FnOnce() -> Result<R, Error>>(op: &str, f: F) -> R {
139 match f() {
140 Ok(r) => r,
141 Err(e) => error(&format!("operation '{}' failed: {}", op, e)),
142 }
143}
144
145fn error(msg: &str) -> ! {
146 use nix::sys::reboot;
147
148 println!("\n--------");
149 println!("ERROR: Init shim failed\n");
150 println!("{}", msg);
151 println!("--------\n");
152
153 // in case a fatal error occurs we shut down the VM, there's no sense in continuing and this
154 // will certainly alert whoever started us up in the first place
155 let err = reboot::reboot(reboot::RebootMode::RB_POWER_OFF).unwrap_err();
156 println!("'reboot' syscall failed: {} - cannot continue", err);
157
158 // in case 'reboot' fails just loop forever
159 loop {
160 std::thread::sleep(std::time::Duration::from_secs(600));
161 }
162}