[pbs-devel] [PATCH proxmox-backup-restore-image 9/9] add debug initramfs as seperate package

Stefan Reiter s.reiter at proxmox.com
Thu May 6 17:26:24 CEST 2021


"proxmox-backup-restore-image-debug", containing only the debug
initramfs, so depends on the base "proxmox-backup-restore-image" for the
kernel.

Adapt the init-shim to start an agetty on ttyS1, which the host
can use to connect to a root shell for debugging, and use
create_dir_all, since some debug packages seem to create /sys and /proc
as empty dirs already.

The build_initramfs.sh script is modified to include dependency
resolution via apt-rdepends, so debug packages like agetty (util-linux),
busybox and gdb can easily be added. This now builds both the regular
and the debug binary at once, to avoid downloading shared packages
twice.

Signed-off-by: Stefan Reiter <s.reiter at proxmox.com>
---

The updated build_initramfs script should also make further additions to the
initrd easier, e.g. adding lvm and zfs tooling if necessary.

 Makefile                                      | 13 +++--
 debian/control                                | 10 +++-
 ...proxmox-backup-restore-image-debug.install |  1 +
 ...ckup-restore-image-debug.lintian-overrides |  2 +
 ...roxmox-backup-restore-image-debug.triggers |  1 +
 src/Makefile                                  |  5 +-
 src/build_initramfs.sh                        | 55 +++++++++++++------
 src/init-shim-rs/src/main.rs                  | 46 +++++++++++++++-
 8 files changed, 106 insertions(+), 27 deletions(-)
 create mode 100644 debian/proxmox-backup-restore-image-debug.install
 create mode 100644 debian/proxmox-backup-restore-image-debug.lintian-overrides
 create mode 100644 debian/proxmox-backup-restore-image-debug.triggers

diff --git a/Makefile b/Makefile
index d11ac3e..92aa791 100644
--- a/Makefile
+++ b/Makefile
@@ -2,11 +2,14 @@ include /usr/share/dpkg/pkg-info.mk
 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
 
@@ -32,21 +35,23 @@ ${BUILDDIR}: submodules.prepared
 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 at repo.proxmox.com upload --product pbs,pve --dist buster
+	tar cf - ${DEB} ${DEB_DBG} | ssh -X repoman at repo.proxmox.com upload --product pbs,pve --dist buster
 
 .PHONY: clean
 clean:
diff --git a/debian/control b/debian/control
index 5b50392..b87c903 100644
--- a/debian/control
+++ b/debian/control
@@ -2,7 +2,8 @@ Source: proxmox-backup-restore-image
 Section: admin
 Priority: optional
 Maintainer: Proxmox Support Team <support at proxmox.com>
-Build-Depends: asciidoc-base,
+Build-Depends: apt-rdepends,
+               asciidoc-base,
                automake,
                bc,
                bison,
@@ -34,3 +35,10 @@ Description: Kernel/initramfs images for Proxmox Backup single-file restore.
  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.
diff --git a/debian/proxmox-backup-restore-image-debug.install b/debian/proxmox-backup-restore-image-debug.install
new file mode 100644
index 0000000..02ffb49
--- /dev/null
+++ b/debian/proxmox-backup-restore-image-debug.install
@@ -0,0 +1 @@
+build/initramfs/initramfs-debug.img /usr/lib/x86_64-linux-gnu/proxmox-backup/file-restore/
diff --git a/debian/proxmox-backup-restore-image-debug.lintian-overrides b/debian/proxmox-backup-restore-image-debug.lintian-overrides
new file mode 100644
index 0000000..ad59aad
--- /dev/null
+++ b/debian/proxmox-backup-restore-image-debug.lintian-overrides
@@ -0,0 +1,2 @@
+missing-depends-on-sensible-utils
+uses-dpkg-database-directly
diff --git a/debian/proxmox-backup-restore-image-debug.triggers b/debian/proxmox-backup-restore-image-debug.triggers
new file mode 100644
index 0000000..948d898
--- /dev/null
+++ b/debian/proxmox-backup-restore-image-debug.triggers
@@ -0,0 +1 @@
+activate-noawait proxmox-backup-restore-image-update
diff --git a/src/Makefile b/src/Makefile
index 37f385f..a398ea1 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -9,12 +9,13 @@ SHIM_DIR=init-shim-rs
 
 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}
@@ -54,6 +55,8 @@ ${INITRAMFS_IMG}: ${BUILDDIR}.prepared ${RUST_SRC} build_initramfs.sh
 	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
diff --git a/src/build_initramfs.sh b/src/build_initramfs.sh
index 4efa29b..c4ee95c 100755
--- a/src/build_initramfs.sh
+++ b/src/build_initramfs.sh
@@ -6,29 +6,36 @@ ROOT="root"
 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
@@ -36,7 +43,19 @@ 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"
diff --git a/src/init-shim-rs/src/main.rs b/src/init-shim-rs/src/main.rs
index 89aff7b..641218f 100644
--- a/src/init-shim-rs/src/main.rs
+++ b/src/init-shim-rs/src/main.rs
@@ -1,6 +1,8 @@
-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;
@@ -21,15 +23,54 @@ fn main() {
         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,
@@ -63,7 +104,6 @@ fn read_uptime() -> f32 {
 
 fn do_run(cmd: &str) -> ! {
     use std::io::ErrorKind;
-    use std::process::Command;
 
     let spawn_res = Command::new(cmd).env("RUST_BACKTRACE", "1").spawn();
 
-- 
2.20.1






More information about the pbs-devel mailing list