include /usr/share/dpkg/architecture.mk
PACKAGE=proxmox-backup-restore-image
+PACKAGE_DBG=proxmox-backup-restore-image-debug
BUILDDIR=${PACKAGE}-${DEB_VERSION_UPSTREAM_REVISION}
DEB=${PACKAGE}_${DEB_VERSION}_${DEB_BUILD_ARCH}.deb
DSC=${PACKAGE}_${DEB_VERSION}.dsc
+DEB_DBG=${PACKAGE_DBG}_${DEB_VERSION}_${DEB_BUILD_ARCH}.deb
+DSC_DBG=${PACKAGE_DBG}_${DEB_VERSION}.dsc
all: deb
deb: ${DEB}
${DEB}: ${BUILDDIR}
cd ${BUILDDIR}; dpkg-buildpackage -b -us -uc
- lintian ${DEB}
+ lintian ${DEB} ${DEB_DBG}
+${DEB_DBG}: ${DEB}
.PHONY: dsc
dsc: ${DSC}
${DSC}: ${BUILDDIR}
cd ${BUILDDIR}; dpkg-buildpackage -S -us -uc -d
- lintian ${DSC}
+ lintian ${DSC} ${DSC_DBG}
+${DSC_DBG}: ${DSC}
.PHONY: dinstall
dinstall: deb
- dpkg -i ${DEB}
+ dpkg -i ${DEB} ${DEB_DBG}
.PHONY: upload
upload: ${DEB}
- tar cf - ${DEB} | ssh -X repoman@repo.proxmox.com upload --product pbs,pve --dist buster
+ tar cf - ${DEB} ${DEB_DBG} | ssh -X repoman@repo.proxmox.com upload --product pbs,pve --dist buster
.PHONY: clean
clean:
Section: admin
Priority: optional
Maintainer: Proxmox Support Team <support@proxmox.com>
-Build-Depends: asciidoc-base,
+Build-Depends: apt-rdepends,
+ asciidoc-base,
automake,
bc,
bison,
Preconfigured images used as base for single file restore of Proxmox Backup
Server snapshots. Not really useful on their own, so best used together with
the proxmox-backup-file-restore package, which provide the actual tools.
+
+Package: proxmox-backup-restore-image-debug
+Architecture: amd64
+Depends: proxmox-backup-restore-image
+Description: Debug initramfs image for Proxmox Backup single-file restore.
+ Not required for production use, only useful for manual inspection of file
+ restore VMs. Includes busybox and gdb.
--- /dev/null
+build/initramfs/initramfs-debug.img /usr/lib/x86_64-linux-gnu/proxmox-backup/file-restore/
--- /dev/null
+missing-depends-on-sensible-utils
+uses-dpkg-database-directly
--- /dev/null
+activate-noawait proxmox-backup-restore-image-update
KERNEL_IMG=${BUILDDIR}/bzImage
INITRAMFS_IMG=${INITRAMFS_BUILDDIR}/initramfs.img
+INITRAMFS_IMG_DBG=${INITRAMFS_BUILDDIR}/initramfs-debug.img
CONFIG=config-base
RUST_SRC=$(wildcard ${SHIM_DIR}/**/*.rs) ${SHIM_DIR}/Cargo.toml
-all: ${KERNEL_IMG} ${INITRAMFS_IMG}
+all: ${KERNEL_IMG} ${INITRAMFS_IMG_DBG}
${BUILDDIR}.prepared: ${CONFIG}
rm -rf ${BUILDDIR}
cd ${SHIM_DIR}; cargo build --release
sh build_initramfs.sh
+${INITRAMFS_IMG_DBG}: ${INITRAMFS_IMG}
+
.PHONY: test-run
test-run: ${KERNEL_IMG} ${INITRAMFS_IMG}
# note: this will always fail since /proxmox-restore-daemon is not
BUILDDIR="build/initramfs"
INIT="../../init-shim-rs/target/release/init-shim-rs"
-PKGS=" \
- libc6:amd64 \
- libgcc1:amd64 \
- libstdc++6:amd64 \
- libssl1.1:amd64 \
- libattr1:amd64 \
- libacl1:amd64
-"
-
echo "Using build dir: $BUILDDIR"
rm -rf "$BUILDDIR"
mkdir -p "$BUILDDIR"
cd "$BUILDDIR"
mkdir "$ROOT"
-# add necessary packages to initramfs
-for pkg in $PKGS; do
- apt-get download "$pkg"
- dpkg-deb -x ./*.deb "$ROOT"
+# adds necessary packages to initramfs build root folder
+add_pkgs() {
+ DEPS=""
+ for pkg in $1; do
+ LOCAL_DEPS=$(apt-rdepends -f Depends -s Depends "$pkg" | grep -v '^ ')
+ DEPS="$DEPS $LOCAL_DEPS"
+ done
+ # debconf and gcc are unnecessary
+ DEPS=$(echo "$DEPS" |\
+ sed -E 's/debconf(-2\.0)?//' |\
+ sed -E 's/gcc-.{1,2}-base//')
+ apt-get download $DEPS
+ for deb in ./*.deb; do
+ dpkg-deb -x "$deb" "$ROOT"
+ done
rm ./*.deb
-done
+}
-rm -rf ${ROOT:?}/usr/share # contains only docs and debian stuff
+make_cpio() {
+ fakeroot -- sh -c "
+ cd '$ROOT';
+ find . -print0 | cpio --null -oV --format=newc -F ../$1
+ "
+}
cp $INIT "$ROOT/init"
chmod a+x "$ROOT/init" # just to be sure
# tell daemon it's running in the correct environment
touch "$ROOT/restore-vm-marker"
-fakeroot -- sh -c "
- cd '$ROOT';
- find . -print0 | cpio --null -oV --format=newc -F ../initramfs.img
+add_pkgs "
+ libstdc++6:amd64 \
+ libssl1.1:amd64 \
+ libacl1:amd64 \
+"
+rm -rf ${ROOT:?}/usr/share # contains only docs and debian stuff
+make_cpio "initramfs.img"
+
+# add debug helpers for debug initramfs, packages from above are included too
+add_pkgs "
+ util-linux:amd64 \
+ busybox-static:amd64 \
+ gdb:amd64 \
"
+# leave /usr/share here, it contains necessary stuff for gdb
+make_cpio "initramfs-debug.img"
-use anyhow::Error;
+use anyhow::{bail, Error};
use std::ffi::CStr;
use std::fs;
+use std::path::PathBuf;
+use std::process::Command;
const URANDOM_MAJ: u64 = 1;
const URANDOM_MIN: u64 = 9;
do_mknod("/dev/urandom", URANDOM_MAJ, URANDOM_MIN)
});
+ if let Err(err) = run_agetty() {
+ // not fatal
+ println!("[init-shim] debug: agetty start failed: {}", err);
+ }
+
let uptime = read_uptime();
println!("[init-shim] reached daemon start after {:.2}s", uptime);
do_run("/proxmox-restore-daemon");
}
+fn run_agetty() -> Result<(), Error> {
+ use nix::unistd::{fork, ForkResult};
+
+ if !PathBuf::from("/sbin/agetty").exists() {
+ bail!("/sbin/agetty not found, probably not running debug mode and safe to ignore");
+ }
+
+ if !PathBuf::from("/sys/class/tty/ttyS1/device/driver/serial8250").exists() {
+ bail!("ttyS1 device does not exist or is not a 8250");
+ }
+
+ let dev = fs::read_to_string("/sys/class/tty/ttyS1/dev")?;
+ let (tty_maj, tty_min) = dev.trim().split_at(dev.find(':').unwrap_or(1));
+ do_mknod("/dev/ttyS1", tty_maj.parse()?, tty_min[1..].parse()?)?;
+
+ match unsafe { fork() } {
+ Ok(ForkResult::Parent { .. }) => {}
+ Ok(ForkResult::Child) => loop {
+ // continue to restart agetty if it exits, this runs in a forked process
+ println!("[init-shim] Spawning new agetty");
+ let res = Command::new("/sbin/agetty")
+ .args(&["-a", "root", "-l", "/bin/busybox", "-o", "sh", "115200", "ttyS1"])
+ .spawn()
+ .unwrap()
+ .wait()
+ .unwrap();
+ println!("[init-shim] agetty exited: {}", res.code().unwrap_or(-1));
+ },
+ Err(err) => println!("fork failed: {}", err),
+ }
+
+ Ok(())
+}
+
fn do_mount(target: &str, fstype: &str) -> Result<(), Error> {
use nix::mount::{mount, MsFlags};
- fs::create_dir(target)?;
+ fs::create_dir_all(target)?;
let none_type: Option<&CStr> = None;
mount(
none_type,
fn do_run(cmd: &str) -> ! {
use std::io::ErrorKind;
- use std::process::Command;
let spawn_res = Command::new(cmd).env("RUST_BACKTRACE", "1").spawn();